123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- //===-- ARMWinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #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();
- }
- }
- MCStreamer *llvm::createARMWinCOFFStreamer(
- MCContext &Context, std::unique_ptr<MCAsmBackend> &&MAB,
- std::unique_ptr<MCObjectWriter> &&OW,
- std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll,
- bool IncrementalLinkerCompatible) {
- auto *S = new ARMWinCOFFStreamer(Context, std::move(MAB), std::move(Emitter),
- std::move(OW));
- S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
- 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);
- }
|