1 //===-- ARMWinCOFFStreamer.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 "ARMMCTargetDesc.h" 10 #include "Utils/ARMBaseInfo.h" 11 #include "llvm/MC/MCAsmBackend.h" 12 #include "llvm/MC/MCAssembler.h" 13 #include "llvm/MC/MCCodeEmitter.h" 14 #include "llvm/MC/MCContext.h" 15 #include "llvm/MC/MCObjectWriter.h" 16 #include "llvm/MC/MCWin64EH.h" 17 #include "llvm/MC/MCWinCOFFStreamer.h" 18 19 using namespace llvm; 20 21 namespace { 22 class ARMWinCOFFStreamer : public MCWinCOFFStreamer { 23 Win64EH::ARMUnwindEmitter EHStreamer; 24 25 public: 26 ARMWinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB, 27 std::unique_ptr<MCCodeEmitter> CE, 28 std::unique_ptr<MCObjectWriter> OW) 29 : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {} 30 31 void emitWinEHHandlerData(SMLoc Loc) override; 32 void emitWindowsUnwindTables() override; 33 void emitWindowsUnwindTables(WinEH::FrameInfo *Frame) override; 34 35 void emitThumbFunc(MCSymbol *Symbol) override; 36 void finishImpl() override; 37 }; 38 39 void ARMWinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) { 40 MCStreamer::emitWinEHHandlerData(Loc); 41 42 // We have to emit the unwind info now, because this directive 43 // actually switches to the .xdata section! 44 EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(), 45 /* HandlerData = */ true); 46 } 47 48 void ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) { 49 EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false); 50 } 51 52 void ARMWinCOFFStreamer::emitWindowsUnwindTables() { 53 if (!getNumWinFrameInfos()) 54 return; 55 EHStreamer.Emit(*this); 56 } 57 58 void ARMWinCOFFStreamer::emitThumbFunc(MCSymbol *Symbol) { 59 getAssembler().setIsThumbFunc(Symbol); 60 } 61 62 void ARMWinCOFFStreamer::finishImpl() { 63 emitFrames(nullptr); 64 emitWindowsUnwindTables(); 65 66 MCWinCOFFStreamer::finishImpl(); 67 } 68 } 69 70 MCStreamer *llvm::createARMWinCOFFStreamer( 71 MCContext &Context, std::unique_ptr<MCAsmBackend> &&MAB, 72 std::unique_ptr<MCObjectWriter> &&OW, 73 std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll, 74 bool IncrementalLinkerCompatible) { 75 auto *S = new ARMWinCOFFStreamer(Context, std::move(MAB), std::move(Emitter), 76 std::move(OW)); 77 S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible); 78 return S; 79 } 80 81 namespace { 82 class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer { 83 private: 84 // True if we are processing SEH directives in an epilogue. 85 bool InEpilogCFI = false; 86 87 // Symbol of the current epilog for which we are processing SEH directives. 88 MCSymbol *CurrentEpilog = nullptr; 89 90 public: 91 ARMTargetWinCOFFStreamer(llvm::MCStreamer &S) : ARMTargetStreamer(S) {} 92 93 // The unwind codes on ARM Windows are documented at 94 // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling 95 void emitARMWinCFIAllocStack(unsigned Size, bool Wide) override; 96 void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) override; 97 void emitARMWinCFISaveSP(unsigned Reg) override; 98 void emitARMWinCFISaveFRegs(unsigned First, unsigned Last) override; 99 void emitARMWinCFISaveLR(unsigned Offset) override; 100 void emitARMWinCFIPrologEnd(bool Fragment) override; 101 void emitARMWinCFINop(bool Wide) override; 102 void emitARMWinCFIEpilogStart(unsigned Condition) override; 103 void emitARMWinCFIEpilogEnd() override; 104 void emitARMWinCFICustom(unsigned Opcode) override; 105 106 private: 107 void emitARMWinUnwindCode(unsigned UnwindCode, int Reg, int Offset); 108 }; 109 110 // Helper function to common out unwind code setup for those codes that can 111 // belong to both prolog and epilog. 112 void ARMTargetWinCOFFStreamer::emitARMWinUnwindCode(unsigned UnwindCode, 113 int Reg, int Offset) { 114 auto &S = getStreamer(); 115 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 116 if (!CurFrame) 117 return; 118 MCSymbol *Label = S.emitCFILabel(); 119 auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset); 120 if (InEpilogCFI) 121 CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst); 122 else 123 CurFrame->Instructions.push_back(Inst); 124 } 125 126 void ARMTargetWinCOFFStreamer::emitARMWinCFIAllocStack(unsigned Size, 127 bool Wide) { 128 unsigned Op = Win64EH::UOP_AllocSmall; 129 if (!Wide) { 130 if (Size / 4 > 0xffff) 131 Op = Win64EH::UOP_AllocHuge; 132 else if (Size / 4 > 0x7f) 133 Op = Win64EH::UOP_AllocLarge; 134 } else { 135 Op = Win64EH::UOP_WideAllocMedium; 136 if (Size / 4 > 0xffff) 137 Op = Win64EH::UOP_WideAllocHuge; 138 else if (Size / 4 > 0x3ff) 139 Op = Win64EH::UOP_WideAllocLarge; 140 } 141 emitARMWinUnwindCode(Op, -1, Size); 142 } 143 144 void ARMTargetWinCOFFStreamer::emitARMWinCFISaveRegMask(unsigned Mask, 145 bool Wide) { 146 assert(Mask != 0); 147 int Lr = (Mask & 0x4000) ? 1 : 0; 148 Mask &= ~0x4000; 149 if (Wide) 150 assert((Mask & ~0x1fff) == 0); 151 else 152 assert((Mask & ~0x00ff) == 0); 153 if (Mask && ((Mask + (1 << 4)) & Mask) == 0) { 154 if (Wide && (Mask & 0x1000) == 0 && (Mask & 0xff) == 0xf0) { 155 // One continuous range from r4 to r8-r11 156 for (int I = 11; I >= 8; I--) { 157 if (Mask & (1 << I)) { 158 emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegsR4R11LR, I, Lr); 159 return; 160 } 161 } 162 // If it actually was from r4 to r4-r7, continue below. 163 } else if (!Wide) { 164 // One continuous range from r4 to r4-r7 165 for (int I = 7; I >= 4; I--) { 166 if (Mask & (1 << I)) { 167 emitARMWinUnwindCode(Win64EH::UOP_SaveRegsR4R7LR, I, Lr); 168 return; 169 } 170 } 171 llvm_unreachable("logic error"); 172 } 173 } 174 Mask |= Lr << 14; 175 if (Wide) 176 emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegMask, Mask, 0); 177 else 178 emitARMWinUnwindCode(Win64EH::UOP_SaveRegMask, Mask, 0); 179 } 180 181 void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) { 182 emitARMWinUnwindCode(Win64EH::UOP_SaveSP, Reg, 0); 183 } 184 185 void ARMTargetWinCOFFStreamer::emitARMWinCFISaveFRegs(unsigned First, 186 unsigned Last) { 187 assert(First <= Last); 188 assert(First >= 16 || Last < 16); 189 assert(First <= 31 && Last <= 31); 190 if (First == 8) 191 emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD8D15, Last, 0); 192 else if (First <= 15) 193 emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD0D15, First, Last); 194 else 195 emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD16D31, First, Last); 196 } 197 198 void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) { 199 emitARMWinUnwindCode(Win64EH::UOP_SaveLR, 0, Offset); 200 } 201 202 void ARMTargetWinCOFFStreamer::emitARMWinCFINop(bool Wide) { 203 if (Wide) 204 emitARMWinUnwindCode(Win64EH::UOP_WideNop, -1, 0); 205 else 206 emitARMWinUnwindCode(Win64EH::UOP_Nop, -1, 0); 207 } 208 209 void ARMTargetWinCOFFStreamer::emitARMWinCFIPrologEnd(bool Fragment) { 210 auto &S = getStreamer(); 211 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 212 if (!CurFrame) 213 return; 214 215 MCSymbol *Label = S.emitCFILabel(); 216 CurFrame->PrologEnd = Label; 217 WinEH::Instruction Inst = 218 WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0); 219 auto it = CurFrame->Instructions.begin(); 220 CurFrame->Instructions.insert(it, Inst); 221 CurFrame->Fragment = Fragment; 222 } 223 224 void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogStart(unsigned Condition) { 225 auto &S = getStreamer(); 226 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 227 if (!CurFrame) 228 return; 229 230 InEpilogCFI = true; 231 CurrentEpilog = S.emitCFILabel(); 232 CurFrame->EpilogMap[CurrentEpilog].Condition = Condition; 233 } 234 235 void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogEnd() { 236 auto &S = getStreamer(); 237 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); 238 if (!CurFrame) 239 return; 240 241 if (!CurrentEpilog) { 242 S.getContext().reportError(SMLoc(), "Stray .seh_endepilogue in " + 243 CurFrame->Function->getName()); 244 return; 245 } 246 247 std::vector<WinEH::Instruction> &Epilog = 248 CurFrame->EpilogMap[CurrentEpilog].Instructions; 249 250 unsigned UnwindCode = Win64EH::UOP_End; 251 if (!Epilog.empty()) { 252 WinEH::Instruction EndInstr = Epilog.back(); 253 if (EndInstr.Operation == Win64EH::UOP_Nop) { 254 UnwindCode = Win64EH::UOP_EndNop; 255 Epilog.pop_back(); 256 } else if (EndInstr.Operation == Win64EH::UOP_WideNop) { 257 UnwindCode = Win64EH::UOP_WideEndNop; 258 Epilog.pop_back(); 259 } 260 } 261 262 InEpilogCFI = false; 263 WinEH::Instruction Inst = WinEH::Instruction(UnwindCode, nullptr, -1, 0); 264 CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst); 265 MCSymbol *Label = S.emitCFILabel(); 266 CurFrame->EpilogMap[CurrentEpilog].End = Label; 267 CurrentEpilog = nullptr; 268 } 269 270 void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) { 271 emitARMWinUnwindCode(Win64EH::UOP_Custom, 0, Opcode); 272 } 273 274 } // end anonymous namespace 275 276 MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) { 277 return new ARMTargetWinCOFFStreamer(S); 278 } 279