| //===-- AArch64WinCOFFStreamer.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 "AArch64WinCOFFStreamer.h" | |
| #include "llvm/MC/MCAsmBackend.h" | |
| #include "llvm/MC/MCCodeEmitter.h" | |
| #include "llvm/MC/MCObjectWriter.h" | |
| #include "llvm/MC/MCWin64EH.h" | |
| #include "llvm/MC/MCWinCOFFStreamer.h" | |
| using namespace llvm; | |
| namespace { | |
| class AArch64WinCOFFStreamer : public MCWinCOFFStreamer { | |
| Win64EH::ARM64UnwindEmitter EHStreamer; | |
| public: | |
| AArch64WinCOFFStreamer(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 FinishImpl() override; | |
| }; | |
| void AArch64WinCOFFStreamer::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()); | |
| } | |
| void AArch64WinCOFFStreamer::EmitWindowsUnwindTables() { | |
| if (!getNumWinFrameInfos()) | |
| return; | |
| EHStreamer.Emit(*this); | |
| } | |
| void AArch64WinCOFFStreamer::FinishImpl() { | |
| EmitFrames(nullptr); | |
| EmitWindowsUnwindTables(); | |
| MCWinCOFFStreamer::FinishImpl(); | |
| } | |
| } // end anonymous namespace | |
| namespace llvm { | |
| // Helper function to common out unwind code setup for those codes that can | |
| // belong to both prolog and epilog. | |
| // There are three types of Windows ARM64 SEH codes. They can | |
| // 1) take no operands: SEH_Nop, SEH_PrologEnd, SEH_EpilogStart, SEH_EpilogEnd | |
| // 2) take an offset: SEH_StackAlloc, SEH_SaveFPLR, SEH_SaveFPLR_X | |
| // 3) take a register and an offset/size: all others | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinUnwindCode(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].push_back(Inst); | |
| else | |
| CurFrame->Instructions.push_back(Inst); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAllocStack(unsigned Size) { | |
| unsigned Op = Win64EH::UOP_AllocSmall; | |
| if (Size >= 16384) | |
| Op = Win64EH::UOP_AllocLarge; | |
| else if (Size >= 512) | |
| Op = Win64EH::UOP_AllocMedium; | |
| EmitARM64WinUnwindCode(Op, -1, Size); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLR(int Offset) { | |
| EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLRX(int Offset) { | |
| EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveReg(unsigned Reg, | |
| int Offset) { | |
| assert(Offset >= 0 && Offset <= 504 && | |
| "Offset for save reg should be >= 0 && <= 504"); | |
| EmitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegX(unsigned Reg, | |
| int Offset) { | |
| EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegP(unsigned Reg, | |
| int Offset) { | |
| EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegPX(unsigned Reg, | |
| int Offset) { | |
| EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFReg(unsigned Reg, | |
| int Offset) { | |
| assert(Offset >= 0 && Offset <= 504 && | |
| "Offset for save reg should be >= 0 && <= 504"); | |
| EmitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegX(unsigned Reg, | |
| int Offset) { | |
| EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegP(unsigned Reg, | |
| int Offset) { | |
| EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegPX(unsigned Reg, | |
| int Offset) { | |
| EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISetFP() { | |
| EmitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAddFP(unsigned Offset) { | |
| assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040"); | |
| EmitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFINop() { | |
| EmitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0); | |
| } | |
| // The functions below handle opcodes that can end up in either a prolog or | |
| // an epilog, but not both. | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIPrologEnd() { | |
| 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, -1, 0); | |
| auto it = CurFrame->Instructions.begin(); | |
| CurFrame->Instructions.insert(it, Inst); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogStart() { | |
| auto &S = getStreamer(); | |
| WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); | |
| if (!CurFrame) | |
| return; | |
| InEpilogCFI = true; | |
| CurrentEpilog = S.EmitCFILabel(); | |
| } | |
| void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogEnd() { | |
| auto &S = getStreamer(); | |
| WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); | |
| if (!CurFrame) | |
| return; | |
| InEpilogCFI = false; | |
| MCSymbol *Label = S.EmitCFILabel(); | |
| WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); | |
| CurFrame->EpilogMap[CurrentEpilog].push_back(Inst); | |
| CurrentEpilog = nullptr; | |
| } | |
| MCWinCOFFStreamer *createAArch64WinCOFFStreamer( | |
| MCContext &Context, std::unique_ptr<MCAsmBackend> MAB, | |
| std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter, | |
| bool RelaxAll, bool IncrementalLinkerCompatible) { | |
| auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB), | |
| std::move(Emitter), std::move(OW)); | |
| S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible); | |
| return S; | |
| } | |
| } // end llvm namespace |