xref: /freebsd/contrib/llvm-project/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===-- ARMWinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "ARMMCTargetDesc.h"
100b57cec5SDimitry Andric #include "llvm/MC/MCAsmBackend.h"
1181ad6265SDimitry Andric #include "llvm/MC/MCAssembler.h"
120b57cec5SDimitry Andric #include "llvm/MC/MCCodeEmitter.h"
1381ad6265SDimitry Andric #include "llvm/MC/MCContext.h"
140b57cec5SDimitry Andric #include "llvm/MC/MCObjectWriter.h"
1581ad6265SDimitry Andric #include "llvm/MC/MCWin64EH.h"
160b57cec5SDimitry Andric #include "llvm/MC/MCWinCOFFStreamer.h"
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric using namespace llvm;
190b57cec5SDimitry Andric 
200b57cec5SDimitry Andric namespace {
210b57cec5SDimitry Andric class ARMWinCOFFStreamer : public MCWinCOFFStreamer {
2281ad6265SDimitry Andric   Win64EH::ARMUnwindEmitter EHStreamer;
2381ad6265SDimitry Andric 
240b57cec5SDimitry Andric public:
ARMWinCOFFStreamer(MCContext & C,std::unique_ptr<MCAsmBackend> AB,std::unique_ptr<MCCodeEmitter> CE,std::unique_ptr<MCObjectWriter> OW)250b57cec5SDimitry Andric   ARMWinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB,
260b57cec5SDimitry Andric                      std::unique_ptr<MCCodeEmitter> CE,
270b57cec5SDimitry Andric                      std::unique_ptr<MCObjectWriter> OW)
280b57cec5SDimitry Andric       : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {}
290b57cec5SDimitry Andric 
3081ad6265SDimitry Andric   void emitWinEHHandlerData(SMLoc Loc) override;
3181ad6265SDimitry Andric   void emitWindowsUnwindTables() override;
3281ad6265SDimitry Andric   void emitWindowsUnwindTables(WinEH::FrameInfo *Frame) override;
3381ad6265SDimitry Andric 
345ffd83dbSDimitry Andric   void emitThumbFunc(MCSymbol *Symbol) override;
355ffd83dbSDimitry Andric   void finishImpl() override;
360b57cec5SDimitry Andric };
370b57cec5SDimitry Andric 
emitWinEHHandlerData(SMLoc Loc)3881ad6265SDimitry Andric void ARMWinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) {
3981ad6265SDimitry Andric   MCStreamer::emitWinEHHandlerData(Loc);
4081ad6265SDimitry Andric 
4181ad6265SDimitry Andric   // We have to emit the unwind info now, because this directive
4281ad6265SDimitry Andric   // actually switches to the .xdata section!
4381ad6265SDimitry Andric   EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(),
4481ad6265SDimitry Andric                             /* HandlerData = */ true);
4581ad6265SDimitry Andric }
4681ad6265SDimitry Andric 
emitWindowsUnwindTables(WinEH::FrameInfo * Frame)4781ad6265SDimitry Andric void ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) {
4881ad6265SDimitry Andric   EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false);
4981ad6265SDimitry Andric }
5081ad6265SDimitry Andric 
emitWindowsUnwindTables()5181ad6265SDimitry Andric void ARMWinCOFFStreamer::emitWindowsUnwindTables() {
5281ad6265SDimitry Andric   if (!getNumWinFrameInfos())
5381ad6265SDimitry Andric     return;
5481ad6265SDimitry Andric   EHStreamer.Emit(*this);
5581ad6265SDimitry Andric }
5681ad6265SDimitry Andric 
emitThumbFunc(MCSymbol * Symbol)575ffd83dbSDimitry Andric void ARMWinCOFFStreamer::emitThumbFunc(MCSymbol *Symbol) {
580b57cec5SDimitry Andric   getAssembler().setIsThumbFunc(Symbol);
590b57cec5SDimitry Andric }
600b57cec5SDimitry Andric 
finishImpl()615ffd83dbSDimitry Andric void ARMWinCOFFStreamer::finishImpl() {
625ffd83dbSDimitry Andric   emitFrames(nullptr);
6381ad6265SDimitry Andric   emitWindowsUnwindTables();
640b57cec5SDimitry Andric 
655ffd83dbSDimitry Andric   MCWinCOFFStreamer::finishImpl();
660b57cec5SDimitry Andric }
670b57cec5SDimitry Andric }
680b57cec5SDimitry Andric 
69*0fca6ea1SDimitry Andric MCStreamer *
createARMWinCOFFStreamer(MCContext & Context,std::unique_ptr<MCAsmBackend> && MAB,std::unique_ptr<MCObjectWriter> && OW,std::unique_ptr<MCCodeEmitter> && Emitter)70*0fca6ea1SDimitry Andric llvm::createARMWinCOFFStreamer(MCContext &Context,
71*0fca6ea1SDimitry Andric                                std::unique_ptr<MCAsmBackend> &&MAB,
720b57cec5SDimitry Andric                                std::unique_ptr<MCObjectWriter> &&OW,
73*0fca6ea1SDimitry Andric                                std::unique_ptr<MCCodeEmitter> &&Emitter) {
74*0fca6ea1SDimitry Andric   return new ARMWinCOFFStreamer(Context, std::move(MAB), std::move(Emitter),
750b57cec5SDimitry Andric                                 std::move(OW));
760b57cec5SDimitry Andric }
770b57cec5SDimitry Andric 
7881ad6265SDimitry Andric namespace {
7981ad6265SDimitry Andric class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer {
8081ad6265SDimitry Andric private:
8181ad6265SDimitry Andric   // True if we are processing SEH directives in an epilogue.
8281ad6265SDimitry Andric   bool InEpilogCFI = false;
8381ad6265SDimitry Andric 
8481ad6265SDimitry Andric   // Symbol of the current epilog for which we are processing SEH directives.
8581ad6265SDimitry Andric   MCSymbol *CurrentEpilog = nullptr;
8681ad6265SDimitry Andric 
8781ad6265SDimitry Andric public:
ARMTargetWinCOFFStreamer(llvm::MCStreamer & S)8881ad6265SDimitry Andric   ARMTargetWinCOFFStreamer(llvm::MCStreamer &S) : ARMTargetStreamer(S) {}
8981ad6265SDimitry Andric 
9081ad6265SDimitry Andric   // The unwind codes on ARM Windows are documented at
9181ad6265SDimitry Andric   // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
9281ad6265SDimitry Andric   void emitARMWinCFIAllocStack(unsigned Size, bool Wide) override;
9381ad6265SDimitry Andric   void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) override;
9481ad6265SDimitry Andric   void emitARMWinCFISaveSP(unsigned Reg) override;
9581ad6265SDimitry Andric   void emitARMWinCFISaveFRegs(unsigned First, unsigned Last) override;
9681ad6265SDimitry Andric   void emitARMWinCFISaveLR(unsigned Offset) override;
9781ad6265SDimitry Andric   void emitARMWinCFIPrologEnd(bool Fragment) override;
9881ad6265SDimitry Andric   void emitARMWinCFINop(bool Wide) override;
9981ad6265SDimitry Andric   void emitARMWinCFIEpilogStart(unsigned Condition) override;
10081ad6265SDimitry Andric   void emitARMWinCFIEpilogEnd() override;
10181ad6265SDimitry Andric   void emitARMWinCFICustom(unsigned Opcode) override;
10281ad6265SDimitry Andric 
10381ad6265SDimitry Andric private:
10481ad6265SDimitry Andric   void emitARMWinUnwindCode(unsigned UnwindCode, int Reg, int Offset);
10581ad6265SDimitry Andric };
10681ad6265SDimitry Andric 
10781ad6265SDimitry Andric // Helper function to common out unwind code setup for those codes that can
10881ad6265SDimitry Andric // belong to both prolog and epilog.
emitARMWinUnwindCode(unsigned UnwindCode,int Reg,int Offset)10981ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinUnwindCode(unsigned UnwindCode,
11081ad6265SDimitry Andric                                                     int Reg, int Offset) {
11181ad6265SDimitry Andric   auto &S = getStreamer();
11281ad6265SDimitry Andric   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
11381ad6265SDimitry Andric   if (!CurFrame)
11481ad6265SDimitry Andric     return;
11581ad6265SDimitry Andric   MCSymbol *Label = S.emitCFILabel();
11681ad6265SDimitry Andric   auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset);
11781ad6265SDimitry Andric   if (InEpilogCFI)
11881ad6265SDimitry Andric     CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
11981ad6265SDimitry Andric   else
12081ad6265SDimitry Andric     CurFrame->Instructions.push_back(Inst);
12181ad6265SDimitry Andric }
12281ad6265SDimitry Andric 
emitARMWinCFIAllocStack(unsigned Size,bool Wide)12381ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFIAllocStack(unsigned Size,
12481ad6265SDimitry Andric                                                        bool Wide) {
12581ad6265SDimitry Andric   unsigned Op = Win64EH::UOP_AllocSmall;
12681ad6265SDimitry Andric   if (!Wide) {
12781ad6265SDimitry Andric     if (Size / 4 > 0xffff)
12881ad6265SDimitry Andric       Op = Win64EH::UOP_AllocHuge;
12981ad6265SDimitry Andric     else if (Size / 4 > 0x7f)
13081ad6265SDimitry Andric       Op = Win64EH::UOP_AllocLarge;
13181ad6265SDimitry Andric   } else {
13281ad6265SDimitry Andric     Op = Win64EH::UOP_WideAllocMedium;
13381ad6265SDimitry Andric     if (Size / 4 > 0xffff)
13481ad6265SDimitry Andric       Op = Win64EH::UOP_WideAllocHuge;
13581ad6265SDimitry Andric     else if (Size / 4 > 0x3ff)
13681ad6265SDimitry Andric       Op = Win64EH::UOP_WideAllocLarge;
13781ad6265SDimitry Andric   }
13881ad6265SDimitry Andric   emitARMWinUnwindCode(Op, -1, Size);
13981ad6265SDimitry Andric }
14081ad6265SDimitry Andric 
emitARMWinCFISaveRegMask(unsigned Mask,bool Wide)14181ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFISaveRegMask(unsigned Mask,
14281ad6265SDimitry Andric                                                         bool Wide) {
14381ad6265SDimitry Andric   assert(Mask != 0);
14481ad6265SDimitry Andric   int Lr = (Mask & 0x4000) ? 1 : 0;
14581ad6265SDimitry Andric   Mask &= ~0x4000;
14681ad6265SDimitry Andric   if (Wide)
14781ad6265SDimitry Andric     assert((Mask & ~0x1fff) == 0);
14881ad6265SDimitry Andric   else
14981ad6265SDimitry Andric     assert((Mask & ~0x00ff) == 0);
15081ad6265SDimitry Andric   if (Mask && ((Mask + (1 << 4)) & Mask) == 0) {
15181ad6265SDimitry Andric     if (Wide && (Mask & 0x1000) == 0 && (Mask & 0xff) == 0xf0) {
15281ad6265SDimitry Andric       // One continuous range from r4 to r8-r11
15381ad6265SDimitry Andric       for (int I = 11; I >= 8; I--) {
15481ad6265SDimitry Andric         if (Mask & (1 << I)) {
15581ad6265SDimitry Andric           emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegsR4R11LR, I, Lr);
15681ad6265SDimitry Andric           return;
15781ad6265SDimitry Andric         }
15881ad6265SDimitry Andric       }
15981ad6265SDimitry Andric       // If it actually was from r4 to r4-r7, continue below.
16081ad6265SDimitry Andric     } else if (!Wide) {
16181ad6265SDimitry Andric       // One continuous range from r4 to r4-r7
16281ad6265SDimitry Andric       for (int I = 7; I >= 4; I--) {
16381ad6265SDimitry Andric         if (Mask & (1 << I)) {
16481ad6265SDimitry Andric           emitARMWinUnwindCode(Win64EH::UOP_SaveRegsR4R7LR, I, Lr);
16581ad6265SDimitry Andric           return;
16681ad6265SDimitry Andric         }
16781ad6265SDimitry Andric       }
16881ad6265SDimitry Andric       llvm_unreachable("logic error");
16981ad6265SDimitry Andric     }
17081ad6265SDimitry Andric   }
17181ad6265SDimitry Andric   Mask |= Lr << 14;
17281ad6265SDimitry Andric   if (Wide)
17381ad6265SDimitry Andric     emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegMask, Mask, 0);
17481ad6265SDimitry Andric   else
17581ad6265SDimitry Andric     emitARMWinUnwindCode(Win64EH::UOP_SaveRegMask, Mask, 0);
17681ad6265SDimitry Andric }
17781ad6265SDimitry Andric 
emitARMWinCFISaveSP(unsigned Reg)17881ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) {
17981ad6265SDimitry Andric   emitARMWinUnwindCode(Win64EH::UOP_SaveSP, Reg, 0);
18081ad6265SDimitry Andric }
18181ad6265SDimitry Andric 
emitARMWinCFISaveFRegs(unsigned First,unsigned Last)18281ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFISaveFRegs(unsigned First,
18381ad6265SDimitry Andric                                                       unsigned Last) {
18481ad6265SDimitry Andric   assert(First <= Last);
18581ad6265SDimitry Andric   assert(First >= 16 || Last < 16);
18681ad6265SDimitry Andric   assert(First <= 31 && Last <= 31);
18781ad6265SDimitry Andric   if (First == 8)
18881ad6265SDimitry Andric     emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD8D15, Last, 0);
18981ad6265SDimitry Andric   else if (First <= 15)
19081ad6265SDimitry Andric     emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD0D15, First, Last);
19181ad6265SDimitry Andric   else
19281ad6265SDimitry Andric     emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD16D31, First, Last);
19381ad6265SDimitry Andric }
19481ad6265SDimitry Andric 
emitARMWinCFISaveLR(unsigned Offset)19581ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) {
19681ad6265SDimitry Andric   emitARMWinUnwindCode(Win64EH::UOP_SaveLR, 0, Offset);
19781ad6265SDimitry Andric }
19881ad6265SDimitry Andric 
emitARMWinCFINop(bool Wide)19981ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFINop(bool Wide) {
20081ad6265SDimitry Andric   if (Wide)
20181ad6265SDimitry Andric     emitARMWinUnwindCode(Win64EH::UOP_WideNop, -1, 0);
20281ad6265SDimitry Andric   else
20381ad6265SDimitry Andric     emitARMWinUnwindCode(Win64EH::UOP_Nop, -1, 0);
20481ad6265SDimitry Andric }
20581ad6265SDimitry Andric 
emitARMWinCFIPrologEnd(bool Fragment)20681ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFIPrologEnd(bool Fragment) {
20781ad6265SDimitry Andric   auto &S = getStreamer();
20881ad6265SDimitry Andric   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
20981ad6265SDimitry Andric   if (!CurFrame)
21081ad6265SDimitry Andric     return;
21181ad6265SDimitry Andric 
21281ad6265SDimitry Andric   MCSymbol *Label = S.emitCFILabel();
21381ad6265SDimitry Andric   CurFrame->PrologEnd = Label;
21481ad6265SDimitry Andric   WinEH::Instruction Inst =
21581ad6265SDimitry Andric       WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0);
21681ad6265SDimitry Andric   auto it = CurFrame->Instructions.begin();
21781ad6265SDimitry Andric   CurFrame->Instructions.insert(it, Inst);
21881ad6265SDimitry Andric   CurFrame->Fragment = Fragment;
21981ad6265SDimitry Andric }
22081ad6265SDimitry Andric 
emitARMWinCFIEpilogStart(unsigned Condition)22181ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogStart(unsigned Condition) {
22281ad6265SDimitry Andric   auto &S = getStreamer();
22381ad6265SDimitry Andric   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
22481ad6265SDimitry Andric   if (!CurFrame)
22581ad6265SDimitry Andric     return;
22681ad6265SDimitry Andric 
22781ad6265SDimitry Andric   InEpilogCFI = true;
22881ad6265SDimitry Andric   CurrentEpilog = S.emitCFILabel();
22981ad6265SDimitry Andric   CurFrame->EpilogMap[CurrentEpilog].Condition = Condition;
23081ad6265SDimitry Andric }
23181ad6265SDimitry Andric 
emitARMWinCFIEpilogEnd()23281ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogEnd() {
23381ad6265SDimitry Andric   auto &S = getStreamer();
23481ad6265SDimitry Andric   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
23581ad6265SDimitry Andric   if (!CurFrame)
23681ad6265SDimitry Andric     return;
23781ad6265SDimitry Andric 
23881ad6265SDimitry Andric   if (!CurrentEpilog) {
23981ad6265SDimitry Andric     S.getContext().reportError(SMLoc(), "Stray .seh_endepilogue in " +
24081ad6265SDimitry Andric                                             CurFrame->Function->getName());
24181ad6265SDimitry Andric     return;
24281ad6265SDimitry Andric   }
24381ad6265SDimitry Andric 
24481ad6265SDimitry Andric   std::vector<WinEH::Instruction> &Epilog =
24581ad6265SDimitry Andric       CurFrame->EpilogMap[CurrentEpilog].Instructions;
24681ad6265SDimitry Andric 
24781ad6265SDimitry Andric   unsigned UnwindCode = Win64EH::UOP_End;
24881ad6265SDimitry Andric   if (!Epilog.empty()) {
24981ad6265SDimitry Andric     WinEH::Instruction EndInstr = Epilog.back();
25081ad6265SDimitry Andric     if (EndInstr.Operation == Win64EH::UOP_Nop) {
25181ad6265SDimitry Andric       UnwindCode = Win64EH::UOP_EndNop;
25281ad6265SDimitry Andric       Epilog.pop_back();
25381ad6265SDimitry Andric     } else if (EndInstr.Operation == Win64EH::UOP_WideNop) {
25481ad6265SDimitry Andric       UnwindCode = Win64EH::UOP_WideEndNop;
25581ad6265SDimitry Andric       Epilog.pop_back();
25681ad6265SDimitry Andric     }
25781ad6265SDimitry Andric   }
25881ad6265SDimitry Andric 
25981ad6265SDimitry Andric   InEpilogCFI = false;
26081ad6265SDimitry Andric   WinEH::Instruction Inst = WinEH::Instruction(UnwindCode, nullptr, -1, 0);
26181ad6265SDimitry Andric   CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
26281ad6265SDimitry Andric   MCSymbol *Label = S.emitCFILabel();
26381ad6265SDimitry Andric   CurFrame->EpilogMap[CurrentEpilog].End = Label;
26481ad6265SDimitry Andric   CurrentEpilog = nullptr;
26581ad6265SDimitry Andric }
26681ad6265SDimitry Andric 
emitARMWinCFICustom(unsigned Opcode)26781ad6265SDimitry Andric void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) {
26881ad6265SDimitry Andric   emitARMWinUnwindCode(Win64EH::UOP_Custom, 0, Opcode);
26981ad6265SDimitry Andric }
27081ad6265SDimitry Andric 
27181ad6265SDimitry Andric } // end anonymous namespace
27281ad6265SDimitry Andric 
createARMObjectTargetWinCOFFStreamer(MCStreamer & S)27381ad6265SDimitry Andric MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) {
27481ad6265SDimitry Andric   return new ARMTargetWinCOFFStreamer(S);
27581ad6265SDimitry Andric }
276