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