diff options
Diffstat (limited to 'llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp')
-rw-r--r-- | llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp index e6f649164a29..cdd7f6fb715a 100644 --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp @@ -8,30 +8,59 @@ #include "ARMMCTargetDesc.h" #include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" #include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCWin64EH.h" #include "llvm/MC/MCWinCOFFStreamer.h" using namespace llvm; namespace { class ARMWinCOFFStreamer : public MCWinCOFFStreamer { + Win64EH::ARMUnwindEmitter EHStreamer; + public: ARMWinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB, std::unique_ptr<MCCodeEmitter> CE, std::unique_ptr<MCObjectWriter> OW) : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {} + void emitWinEHHandlerData(SMLoc Loc) override; + void emitWindowsUnwindTables() override; + void emitWindowsUnwindTables(WinEH::FrameInfo *Frame) override; + void emitThumbFunc(MCSymbol *Symbol) override; void finishImpl() override; }; +void ARMWinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) { + MCStreamer::emitWinEHHandlerData(Loc); + + // We have to emit the unwind info now, because this directive + // actually switches to the .xdata section! + EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(), + /* HandlerData = */ true); +} + +void ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) { + EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false); +} + +void ARMWinCOFFStreamer::emitWindowsUnwindTables() { + if (!getNumWinFrameInfos()) + return; + EHStreamer.Emit(*this); +} + void ARMWinCOFFStreamer::emitThumbFunc(MCSymbol *Symbol) { getAssembler().setIsThumbFunc(Symbol); } void ARMWinCOFFStreamer::finishImpl() { emitFrames(nullptr); + emitWindowsUnwindTables(); MCWinCOFFStreamer::finishImpl(); } @@ -48,3 +77,201 @@ MCStreamer *llvm::createARMWinCOFFStreamer( return S; } +namespace { +class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer { +private: + // True if we are processing SEH directives in an epilogue. + bool InEpilogCFI = false; + + // Symbol of the current epilog for which we are processing SEH directives. + MCSymbol *CurrentEpilog = nullptr; + +public: + ARMTargetWinCOFFStreamer(llvm::MCStreamer &S) : ARMTargetStreamer(S) {} + + // The unwind codes on ARM Windows are documented at + // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling + void emitARMWinCFIAllocStack(unsigned Size, bool Wide) override; + void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) override; + void emitARMWinCFISaveSP(unsigned Reg) override; + void emitARMWinCFISaveFRegs(unsigned First, unsigned Last) override; + void emitARMWinCFISaveLR(unsigned Offset) override; + void emitARMWinCFIPrologEnd(bool Fragment) override; + void emitARMWinCFINop(bool Wide) override; + void emitARMWinCFIEpilogStart(unsigned Condition) override; + void emitARMWinCFIEpilogEnd() override; + void emitARMWinCFICustom(unsigned Opcode) override; + +private: + void emitARMWinUnwindCode(unsigned UnwindCode, int Reg, int Offset); +}; + +// Helper function to common out unwind code setup for those codes that can +// belong to both prolog and epilog. +void ARMTargetWinCOFFStreamer::emitARMWinUnwindCode(unsigned UnwindCode, + int Reg, int Offset) { + auto &S = getStreamer(); + WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); + if (!CurFrame) + return; + MCSymbol *Label = S.emitCFILabel(); + auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset); + if (InEpilogCFI) + CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst); + else + CurFrame->Instructions.push_back(Inst); +} + +void ARMTargetWinCOFFStreamer::emitARMWinCFIAllocStack(unsigned Size, + bool Wide) { + unsigned Op = Win64EH::UOP_AllocSmall; + if (!Wide) { + if (Size / 4 > 0xffff) + Op = Win64EH::UOP_AllocHuge; + else if (Size / 4 > 0x7f) + Op = Win64EH::UOP_AllocLarge; + } else { + Op = Win64EH::UOP_WideAllocMedium; + if (Size / 4 > 0xffff) + Op = Win64EH::UOP_WideAllocHuge; + else if (Size / 4 > 0x3ff) + Op = Win64EH::UOP_WideAllocLarge; + } + emitARMWinUnwindCode(Op, -1, Size); +} + +void ARMTargetWinCOFFStreamer::emitARMWinCFISaveRegMask(unsigned Mask, + bool Wide) { + assert(Mask != 0); + int Lr = (Mask & 0x4000) ? 1 : 0; + Mask &= ~0x4000; + if (Wide) + assert((Mask & ~0x1fff) == 0); + else + assert((Mask & ~0x00ff) == 0); + if (Mask && ((Mask + (1 << 4)) & Mask) == 0) { + if (Wide && (Mask & 0x1000) == 0 && (Mask & 0xff) == 0xf0) { + // One continuous range from r4 to r8-r11 + for (int I = 11; I >= 8; I--) { + if (Mask & (1 << I)) { + emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegsR4R11LR, I, Lr); + return; + } + } + // If it actually was from r4 to r4-r7, continue below. + } else if (!Wide) { + // One continuous range from r4 to r4-r7 + for (int I = 7; I >= 4; I--) { + if (Mask & (1 << I)) { + emitARMWinUnwindCode(Win64EH::UOP_SaveRegsR4R7LR, I, Lr); + return; + } + } + llvm_unreachable("logic error"); + } + } + Mask |= Lr << 14; + if (Wide) + emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegMask, Mask, 0); + else + emitARMWinUnwindCode(Win64EH::UOP_SaveRegMask, Mask, 0); +} + +void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) { + emitARMWinUnwindCode(Win64EH::UOP_SaveSP, Reg, 0); +} + +void ARMTargetWinCOFFStreamer::emitARMWinCFISaveFRegs(unsigned First, + unsigned Last) { + assert(First <= Last); + assert(First >= 16 || Last < 16); + assert(First <= 31 && Last <= 31); + if (First == 8) + emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD8D15, Last, 0); + else if (First <= 15) + emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD0D15, First, Last); + else + emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD16D31, First, Last); +} + +void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) { + emitARMWinUnwindCode(Win64EH::UOP_SaveLR, 0, Offset); +} + +void ARMTargetWinCOFFStreamer::emitARMWinCFINop(bool Wide) { + if (Wide) + emitARMWinUnwindCode(Win64EH::UOP_WideNop, -1, 0); + else + emitARMWinUnwindCode(Win64EH::UOP_Nop, -1, 0); +} + +void ARMTargetWinCOFFStreamer::emitARMWinCFIPrologEnd(bool Fragment) { + auto &S = getStreamer(); + WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); + if (!CurFrame) + return; + + MCSymbol *Label = S.emitCFILabel(); + CurFrame->PrologEnd = Label; + WinEH::Instruction Inst = + WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0); + auto it = CurFrame->Instructions.begin(); + CurFrame->Instructions.insert(it, Inst); + CurFrame->Fragment = Fragment; +} + +void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogStart(unsigned Condition) { + auto &S = getStreamer(); + WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); + if (!CurFrame) + return; + + InEpilogCFI = true; + CurrentEpilog = S.emitCFILabel(); + CurFrame->EpilogMap[CurrentEpilog].Condition = Condition; +} + +void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogEnd() { + auto &S = getStreamer(); + WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); + if (!CurFrame) + return; + + if (!CurrentEpilog) { + S.getContext().reportError(SMLoc(), "Stray .seh_endepilogue in " + + CurFrame->Function->getName()); + return; + } + + std::vector<WinEH::Instruction> &Epilog = + CurFrame->EpilogMap[CurrentEpilog].Instructions; + + unsigned UnwindCode = Win64EH::UOP_End; + if (!Epilog.empty()) { + WinEH::Instruction EndInstr = Epilog.back(); + if (EndInstr.Operation == Win64EH::UOP_Nop) { + UnwindCode = Win64EH::UOP_EndNop; + Epilog.pop_back(); + } else if (EndInstr.Operation == Win64EH::UOP_WideNop) { + UnwindCode = Win64EH::UOP_WideEndNop; + Epilog.pop_back(); + } + } + + InEpilogCFI = false; + WinEH::Instruction Inst = WinEH::Instruction(UnwindCode, nullptr, -1, 0); + CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst); + MCSymbol *Label = S.emitCFILabel(); + CurFrame->EpilogMap[CurrentEpilog].End = Label; + CurrentEpilog = nullptr; +} + +void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) { + emitARMWinUnwindCode(Win64EH::UOP_Custom, 0, Opcode); +} + +} // end anonymous namespace + +MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) { + return new ARMTargetWinCOFFStreamer(S); +} |