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