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 namespace llvm { 63 64 // Helper function to common out unwind code setup for those codes that can 65 // belong to both prolog and epilog. 66 // There are three types of Windows ARM64 SEH codes. They can 67 // 1) take no operands: SEH_Nop, SEH_PrologEnd, SEH_EpilogStart, SEH_EpilogEnd 68 // 2) take an offset: SEH_StackAlloc, SEH_SaveFPLR, SEH_SaveFPLR_X 69 // 3) take a register and an offset/size: all others 70 void AArch64TargetWinCOFFStreamer::EmitARM64WinUnwindCode(unsigned UnwindCode, 71 int Reg, 72 int Offset) { 73 auto &S = getStreamer(); 74 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 75 if (!CurFrame) 76 return; 77 MCSymbol *Label = S.emitCFILabel(); 78 auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset); 79 if (InEpilogCFI) 80 CurFrame->EpilogMap[CurrentEpilog].push_back(Inst); 81 else 82 CurFrame->Instructions.push_back(Inst); 83 } 84 85 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAllocStack(unsigned Size) { 86 unsigned Op = Win64EH::UOP_AllocSmall; 87 if (Size >= 16384) 88 Op = Win64EH::UOP_AllocLarge; 89 else if (Size >= 512) 90 Op = Win64EH::UOP_AllocMedium; 91 EmitARM64WinUnwindCode(Op, -1, Size); 92 } 93 94 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveR19R20X(int Offset) { 95 EmitARM64WinUnwindCode(Win64EH::UOP_SaveR19R20X, -1, Offset); 96 } 97 98 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLR(int Offset) { 99 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset); 100 } 101 102 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLRX(int Offset) { 103 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset); 104 } 105 106 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveReg(unsigned Reg, 107 int Offset) { 108 assert(Offset >= 0 && Offset <= 504 && 109 "Offset for save reg should be >= 0 && <= 504"); 110 EmitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset); 111 } 112 113 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegX(unsigned Reg, 114 int Offset) { 115 EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset); 116 } 117 118 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegP(unsigned Reg, 119 int Offset) { 120 EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset); 121 } 122 123 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegPX(unsigned Reg, 124 int Offset) { 125 EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset); 126 } 127 128 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveLRPair(unsigned Reg, 129 int Offset) { 130 EmitARM64WinUnwindCode(Win64EH::UOP_SaveLRPair, Reg, Offset); 131 } 132 133 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFReg(unsigned Reg, 134 int Offset) { 135 assert(Offset >= 0 && Offset <= 504 && 136 "Offset for save reg should be >= 0 && <= 504"); 137 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset); 138 } 139 140 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegX(unsigned Reg, 141 int Offset) { 142 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset); 143 } 144 145 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegP(unsigned Reg, 146 int Offset) { 147 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset); 148 } 149 150 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegPX(unsigned Reg, 151 int Offset) { 152 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset); 153 } 154 155 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISetFP() { 156 EmitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0); 157 } 158 159 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAddFP(unsigned Offset) { 160 assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040"); 161 EmitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset); 162 } 163 164 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFINop() { 165 EmitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0); 166 } 167 168 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveNext() { 169 EmitARM64WinUnwindCode(Win64EH::UOP_SaveNext, -1, 0); 170 } 171 172 // The functions below handle opcodes that can end up in either a prolog or 173 // an epilog, but not both. 174 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIPrologEnd() { 175 auto &S = getStreamer(); 176 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 177 if (!CurFrame) 178 return; 179 180 MCSymbol *Label = S.emitCFILabel(); 181 CurFrame->PrologEnd = Label; 182 WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); 183 auto it = CurFrame->Instructions.begin(); 184 CurFrame->Instructions.insert(it, Inst); 185 } 186 187 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogStart() { 188 auto &S = getStreamer(); 189 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 190 if (!CurFrame) 191 return; 192 193 InEpilogCFI = true; 194 CurrentEpilog = S.emitCFILabel(); 195 } 196 197 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogEnd() { 198 auto &S = getStreamer(); 199 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 200 if (!CurFrame) 201 return; 202 203 InEpilogCFI = false; 204 MCSymbol *Label = S.emitCFILabel(); 205 WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); 206 CurFrame->EpilogMap[CurrentEpilog].push_back(Inst); 207 CurrentEpilog = nullptr; 208 } 209 210 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFITrapFrame() { 211 EmitARM64WinUnwindCode(Win64EH::UOP_TrapFrame, -1, 0); 212 } 213 214 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIMachineFrame() { 215 EmitARM64WinUnwindCode(Win64EH::UOP_PushMachFrame, -1, 0); 216 } 217 218 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIContext() { 219 EmitARM64WinUnwindCode(Win64EH::UOP_Context, -1, 0); 220 } 221 222 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIClearUnwoundToCall() { 223 EmitARM64WinUnwindCode(Win64EH::UOP_ClearUnwoundToCall, -1, 0); 224 } 225 226 MCWinCOFFStreamer *createAArch64WinCOFFStreamer( 227 MCContext &Context, std::unique_ptr<MCAsmBackend> MAB, 228 std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter, 229 bool RelaxAll, bool IncrementalLinkerCompatible) { 230 auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB), 231 std::move(Emitter), std::move(OW)); 232 S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible); 233 return S; 234 } 235 236 } // end llvm namespace 237