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 EmitWindowsUnwindTables(WinEH::FrameInfo *Frame) override; 32 void finishImpl() override; 33 }; 34 35 void AArch64WinCOFFStreamer::EmitWinEHHandlerData(SMLoc Loc) { 36 MCStreamer::EmitWinEHHandlerData(Loc); 37 38 // We have to emit the unwind info now, because this directive 39 // actually switches to the .xdata section! 40 EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(), 41 /* HandlerData = */ true); 42 } 43 44 void AArch64WinCOFFStreamer::EmitWindowsUnwindTables(WinEH::FrameInfo *Frame) { 45 EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false); 46 } 47 48 void AArch64WinCOFFStreamer::EmitWindowsUnwindTables() { 49 if (!getNumWinFrameInfos()) 50 return; 51 EHStreamer.Emit(*this); 52 } 53 54 void AArch64WinCOFFStreamer::finishImpl() { 55 emitFrames(nullptr); 56 EmitWindowsUnwindTables(); 57 58 MCWinCOFFStreamer::finishImpl(); 59 } 60 } // end anonymous namespace 61 62 // Helper function to common out unwind code setup for those codes that can 63 // belong to both prolog and epilog. 64 // There are three types of Windows ARM64 SEH codes. They can 65 // 1) take no operands: SEH_Nop, SEH_PrologEnd, SEH_EpilogStart, SEH_EpilogEnd 66 // 2) take an offset: SEH_StackAlloc, SEH_SaveFPLR, SEH_SaveFPLR_X 67 // 3) take a register and an offset/size: all others 68 void AArch64TargetWinCOFFStreamer::emitARM64WinUnwindCode(unsigned UnwindCode, 69 int Reg, int Offset) { 70 auto &S = getStreamer(); 71 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 72 if (!CurFrame) 73 return; 74 MCSymbol *Label = S.emitCFILabel(); 75 auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset); 76 if (InEpilogCFI) 77 CurFrame->EpilogMap[CurrentEpilog].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 = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); 180 auto it = CurFrame->Instructions.begin(); 181 CurFrame->Instructions.insert(it, Inst); 182 } 183 184 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIEpilogStart() { 185 auto &S = getStreamer(); 186 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 187 if (!CurFrame) 188 return; 189 190 InEpilogCFI = true; 191 CurrentEpilog = S.emitCFILabel(); 192 } 193 194 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIEpilogEnd() { 195 auto &S = getStreamer(); 196 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 197 if (!CurFrame) 198 return; 199 200 InEpilogCFI = false; 201 MCSymbol *Label = S.emitCFILabel(); 202 WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); 203 CurFrame->EpilogMap[CurrentEpilog].push_back(Inst); 204 CurrentEpilog = nullptr; 205 } 206 207 void AArch64TargetWinCOFFStreamer::emitARM64WinCFITrapFrame() { 208 emitARM64WinUnwindCode(Win64EH::UOP_TrapFrame, -1, 0); 209 } 210 211 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIMachineFrame() { 212 emitARM64WinUnwindCode(Win64EH::UOP_PushMachFrame, -1, 0); 213 } 214 215 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIContext() { 216 emitARM64WinUnwindCode(Win64EH::UOP_Context, -1, 0); 217 } 218 219 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIClearUnwoundToCall() { 220 emitARM64WinUnwindCode(Win64EH::UOP_ClearUnwoundToCall, -1, 0); 221 } 222 223 MCWinCOFFStreamer *llvm::createAArch64WinCOFFStreamer( 224 MCContext &Context, std::unique_ptr<MCAsmBackend> MAB, 225 std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter, 226 bool RelaxAll, bool IncrementalLinkerCompatible) { 227 auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB), 228 std::move(Emitter), std::move(OW)); 229 S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible); 230 return S; 231 } 232