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/MCAssembler.h" 12 #include "llvm/MC/MCCodeEmitter.h" 13 #include "llvm/MC/MCObjectWriter.h" 14 #include "llvm/MC/MCWin64EH.h" 15 #include "llvm/MC/MCWinCOFFStreamer.h" 16 17 using namespace llvm; 18 19 namespace { 20 21 class AArch64WinCOFFStreamer : public MCWinCOFFStreamer { 22 Win64EH::ARM64UnwindEmitter EHStreamer; 23 24 public: 25 AArch64WinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB, 26 std::unique_ptr<MCCodeEmitter> CE, 27 std::unique_ptr<MCObjectWriter> OW) 28 : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {} 29 30 void emitWinEHHandlerData(SMLoc Loc) override; 31 void emitWindowsUnwindTables() override; 32 void emitWindowsUnwindTables(WinEH::FrameInfo *Frame) override; 33 void finishImpl() override; 34 }; 35 36 void AArch64WinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) { 37 MCStreamer::emitWinEHHandlerData(Loc); 38 39 // We have to emit the unwind info now, because this directive 40 // actually switches to the .xdata section! 41 EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(), 42 /* HandlerData = */ true); 43 } 44 45 void AArch64WinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) { 46 EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false); 47 } 48 49 void AArch64WinCOFFStreamer::emitWindowsUnwindTables() { 50 if (!getNumWinFrameInfos()) 51 return; 52 EHStreamer.Emit(*this); 53 } 54 55 void AArch64WinCOFFStreamer::finishImpl() { 56 emitFrames(nullptr); 57 emitWindowsUnwindTables(); 58 59 MCWinCOFFStreamer::finishImpl(); 60 } 61 } // end anonymous namespace 62 63 // Helper function to common out unwind code setup for those codes that can 64 // belong to both prolog and epilog. 65 // There are three types of Windows ARM64 SEH codes. They can 66 // 1) take no operands: SEH_Nop, SEH_PrologEnd, SEH_EpilogStart, SEH_EpilogEnd 67 // 2) take an offset: SEH_StackAlloc, SEH_SaveFPLR, SEH_SaveFPLR_X 68 // 3) take a register and an offset/size: all others 69 void AArch64TargetWinCOFFStreamer::emitARM64WinUnwindCode(unsigned UnwindCode, 70 int Reg, int Offset) { 71 auto &S = getStreamer(); 72 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 73 if (!CurFrame) 74 return; 75 auto Inst = WinEH::Instruction(UnwindCode, /*Label=*/nullptr, Reg, Offset); 76 if (InEpilogCFI) 77 CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst); 78 else 79 CurFrame->Instructions.push_back(Inst); 80 } 81 82 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIAllocStack(unsigned Size) { 83 unsigned Op = Win64EH::UOP_AllocSmall; 84 if (Size >= 16384) 85 Op = Win64EH::UOP_AllocLarge; 86 else if (Size >= 512) 87 Op = Win64EH::UOP_AllocMedium; 88 emitARM64WinUnwindCode(Op, -1, Size); 89 } 90 91 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveR19R20X(int Offset) { 92 emitARM64WinUnwindCode(Win64EH::UOP_SaveR19R20X, -1, Offset); 93 } 94 95 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFPLR(int Offset) { 96 emitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset); 97 } 98 99 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFPLRX(int Offset) { 100 emitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset); 101 } 102 103 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveReg(unsigned Reg, 104 int Offset) { 105 assert(Offset >= 0 && Offset <= 504 && 106 "Offset for save reg should be >= 0 && <= 504"); 107 emitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset); 108 } 109 110 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegX(unsigned Reg, 111 int Offset) { 112 emitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset); 113 } 114 115 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegP(unsigned Reg, 116 int Offset) { 117 emitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset); 118 } 119 120 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegPX(unsigned Reg, 121 int Offset) { 122 emitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset); 123 } 124 125 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveLRPair(unsigned Reg, 126 int Offset) { 127 emitARM64WinUnwindCode(Win64EH::UOP_SaveLRPair, Reg, Offset); 128 } 129 130 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFReg(unsigned Reg, 131 int Offset) { 132 assert(Offset >= 0 && Offset <= 504 && 133 "Offset for save reg should be >= 0 && <= 504"); 134 emitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset); 135 } 136 137 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegX(unsigned Reg, 138 int Offset) { 139 emitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset); 140 } 141 142 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegP(unsigned Reg, 143 int Offset) { 144 emitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset); 145 } 146 147 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegPX(unsigned Reg, 148 int Offset) { 149 emitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset); 150 } 151 152 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISetFP() { 153 emitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0); 154 } 155 156 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIAddFP(unsigned Offset) { 157 assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040"); 158 emitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset); 159 } 160 161 void AArch64TargetWinCOFFStreamer::emitARM64WinCFINop() { 162 emitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0); 163 } 164 165 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveNext() { 166 emitARM64WinUnwindCode(Win64EH::UOP_SaveNext, -1, 0); 167 } 168 169 // The functions below handle opcodes that can end up in either a prolog or 170 // an epilog, but not both. 171 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIPrologEnd() { 172 auto &S = getStreamer(); 173 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 174 if (!CurFrame) 175 return; 176 177 MCSymbol *Label = S.emitCFILabel(); 178 CurFrame->PrologEnd = Label; 179 WinEH::Instruction Inst = 180 WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0); 181 auto it = CurFrame->Instructions.begin(); 182 CurFrame->Instructions.insert(it, Inst); 183 } 184 185 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIEpilogStart() { 186 auto &S = getStreamer(); 187 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 188 if (!CurFrame) 189 return; 190 191 InEpilogCFI = true; 192 CurrentEpilog = S.emitCFILabel(); 193 } 194 195 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIEpilogEnd() { 196 auto &S = getStreamer(); 197 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 198 if (!CurFrame) 199 return; 200 201 InEpilogCFI = false; 202 WinEH::Instruction Inst = 203 WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0); 204 CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst); 205 CurrentEpilog = nullptr; 206 } 207 208 void AArch64TargetWinCOFFStreamer::emitARM64WinCFITrapFrame() { 209 emitARM64WinUnwindCode(Win64EH::UOP_TrapFrame, -1, 0); 210 } 211 212 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIMachineFrame() { 213 emitARM64WinUnwindCode(Win64EH::UOP_PushMachFrame, -1, 0); 214 } 215 216 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIContext() { 217 emitARM64WinUnwindCode(Win64EH::UOP_Context, -1, 0); 218 } 219 220 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIClearUnwoundToCall() { 221 emitARM64WinUnwindCode(Win64EH::UOP_ClearUnwoundToCall, -1, 0); 222 } 223 224 MCWinCOFFStreamer *llvm::createAArch64WinCOFFStreamer( 225 MCContext &Context, std::unique_ptr<MCAsmBackend> MAB, 226 std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter, 227 bool RelaxAll, bool IncrementalLinkerCompatible) { 228 auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB), 229 std::move(Emitter), std::move(OW)); 230 S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible); 231 return S; 232 } 233