1 //===-- AArch64WinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "AArch64WinCOFFStreamer.h" 10 #include "llvm/MC/MCAsmBackend.h" 11 #include "llvm/MC/MCCodeEmitter.h" 12 #include "llvm/MC/MCObjectWriter.h" 13 #include "llvm/MC/MCWin64EH.h" 14 #include "llvm/MC/MCWinCOFFStreamer.h" 15 16 using namespace llvm; 17 18 namespace { 19 20 class AArch64WinCOFFStreamer : public MCWinCOFFStreamer { 21 Win64EH::ARM64UnwindEmitter EHStreamer; 22 23 public: 24 AArch64WinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB, 25 std::unique_ptr<MCCodeEmitter> CE, 26 std::unique_ptr<MCObjectWriter> OW) 27 : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {} 28 29 void EmitWinEHHandlerData(SMLoc Loc) override; 30 void EmitWindowsUnwindTables() override; 31 void finishImpl() override; 32 }; 33 34 void AArch64WinCOFFStreamer::EmitWinEHHandlerData(SMLoc Loc) { 35 MCStreamer::EmitWinEHHandlerData(Loc); 36 37 // We have to emit the unwind info now, because this directive 38 // actually switches to the .xdata section! 39 EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo()); 40 } 41 42 void AArch64WinCOFFStreamer::EmitWindowsUnwindTables() { 43 if (!getNumWinFrameInfos()) 44 return; 45 EHStreamer.Emit(*this); 46 } 47 48 void AArch64WinCOFFStreamer::finishImpl() { 49 emitFrames(nullptr); 50 EmitWindowsUnwindTables(); 51 52 MCWinCOFFStreamer::finishImpl(); 53 } 54 } // end anonymous namespace 55 56 namespace llvm { 57 58 // Helper function to common out unwind code setup for those codes that can 59 // belong to both prolog and epilog. 60 // There are three types of Windows ARM64 SEH codes. They can 61 // 1) take no operands: SEH_Nop, SEH_PrologEnd, SEH_EpilogStart, SEH_EpilogEnd 62 // 2) take an offset: SEH_StackAlloc, SEH_SaveFPLR, SEH_SaveFPLR_X 63 // 3) take a register and an offset/size: all others 64 void AArch64TargetWinCOFFStreamer::EmitARM64WinUnwindCode(unsigned UnwindCode, 65 int Reg, 66 int Offset) { 67 auto &S = getStreamer(); 68 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 69 if (!CurFrame) 70 return; 71 MCSymbol *Label = S.emitCFILabel(); 72 auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset); 73 if (InEpilogCFI) 74 CurFrame->EpilogMap[CurrentEpilog].push_back(Inst); 75 else 76 CurFrame->Instructions.push_back(Inst); 77 } 78 79 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAllocStack(unsigned Size) { 80 unsigned Op = Win64EH::UOP_AllocSmall; 81 if (Size >= 16384) 82 Op = Win64EH::UOP_AllocLarge; 83 else if (Size >= 512) 84 Op = Win64EH::UOP_AllocMedium; 85 EmitARM64WinUnwindCode(Op, -1, Size); 86 } 87 88 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLR(int Offset) { 89 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset); 90 } 91 92 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLRX(int Offset) { 93 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset); 94 } 95 96 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveReg(unsigned Reg, 97 int Offset) { 98 assert(Offset >= 0 && Offset <= 504 && 99 "Offset for save reg should be >= 0 && <= 504"); 100 EmitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset); 101 } 102 103 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegX(unsigned Reg, 104 int Offset) { 105 EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset); 106 } 107 108 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegP(unsigned Reg, 109 int Offset) { 110 EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset); 111 } 112 113 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegPX(unsigned Reg, 114 int Offset) { 115 EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset); 116 } 117 118 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFReg(unsigned Reg, 119 int Offset) { 120 assert(Offset >= 0 && Offset <= 504 && 121 "Offset for save reg should be >= 0 && <= 504"); 122 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset); 123 } 124 125 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegX(unsigned Reg, 126 int Offset) { 127 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset); 128 } 129 130 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegP(unsigned Reg, 131 int Offset) { 132 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset); 133 } 134 135 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegPX(unsigned Reg, 136 int Offset) { 137 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset); 138 } 139 140 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISetFP() { 141 EmitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0); 142 } 143 144 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAddFP(unsigned Offset) { 145 assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040"); 146 EmitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset); 147 } 148 149 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFINop() { 150 EmitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0); 151 } 152 153 // The functions below handle opcodes that can end up in either a prolog or 154 // an epilog, but not both. 155 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIPrologEnd() { 156 auto &S = getStreamer(); 157 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 158 if (!CurFrame) 159 return; 160 161 MCSymbol *Label = S.emitCFILabel(); 162 CurFrame->PrologEnd = Label; 163 WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); 164 auto it = CurFrame->Instructions.begin(); 165 CurFrame->Instructions.insert(it, Inst); 166 } 167 168 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogStart() { 169 auto &S = getStreamer(); 170 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 171 if (!CurFrame) 172 return; 173 174 InEpilogCFI = true; 175 CurrentEpilog = S.emitCFILabel(); 176 } 177 178 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogEnd() { 179 auto &S = getStreamer(); 180 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 181 if (!CurFrame) 182 return; 183 184 InEpilogCFI = false; 185 MCSymbol *Label = S.emitCFILabel(); 186 WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); 187 CurFrame->EpilogMap[CurrentEpilog].push_back(Inst); 188 CurrentEpilog = nullptr; 189 } 190 191 MCWinCOFFStreamer *createAArch64WinCOFFStreamer( 192 MCContext &Context, std::unique_ptr<MCAsmBackend> MAB, 193 std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter, 194 bool RelaxAll, bool IncrementalLinkerCompatible) { 195 auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB), 196 std::move(Emitter), std::move(OW)); 197 S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible); 198 return S; 199 } 200 201 } // end llvm namespace 202