109467b48Spatrick //===-- ARMWinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick 
909467b48Spatrick #include "ARMMCTargetDesc.h"
1009467b48Spatrick #include "llvm/MC/MCAsmBackend.h"
11*d415bd75Srobert #include "llvm/MC/MCAssembler.h"
1209467b48Spatrick #include "llvm/MC/MCCodeEmitter.h"
13*d415bd75Srobert #include "llvm/MC/MCContext.h"
1409467b48Spatrick #include "llvm/MC/MCObjectWriter.h"
15*d415bd75Srobert #include "llvm/MC/MCWin64EH.h"
1609467b48Spatrick #include "llvm/MC/MCWinCOFFStreamer.h"
1709467b48Spatrick 
1809467b48Spatrick using namespace llvm;
1909467b48Spatrick 
2009467b48Spatrick namespace {
2109467b48Spatrick class ARMWinCOFFStreamer : public MCWinCOFFStreamer {
22*d415bd75Srobert   Win64EH::ARMUnwindEmitter EHStreamer;
23*d415bd75Srobert 
2409467b48Spatrick public:
ARMWinCOFFStreamer(MCContext & C,std::unique_ptr<MCAsmBackend> AB,std::unique_ptr<MCCodeEmitter> CE,std::unique_ptr<MCObjectWriter> OW)2509467b48Spatrick   ARMWinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB,
2609467b48Spatrick                      std::unique_ptr<MCCodeEmitter> CE,
2709467b48Spatrick                      std::unique_ptr<MCObjectWriter> OW)
2809467b48Spatrick       : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {}
2909467b48Spatrick 
30*d415bd75Srobert   void emitWinEHHandlerData(SMLoc Loc) override;
31*d415bd75Srobert   void emitWindowsUnwindTables() override;
32*d415bd75Srobert   void emitWindowsUnwindTables(WinEH::FrameInfo *Frame) override;
33*d415bd75Srobert 
34097a140dSpatrick   void emitThumbFunc(MCSymbol *Symbol) override;
35097a140dSpatrick   void finishImpl() override;
3609467b48Spatrick };
3709467b48Spatrick 
emitWinEHHandlerData(SMLoc Loc)38*d415bd75Srobert void ARMWinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) {
39*d415bd75Srobert   MCStreamer::emitWinEHHandlerData(Loc);
40*d415bd75Srobert 
41*d415bd75Srobert   // We have to emit the unwind info now, because this directive
42*d415bd75Srobert   // actually switches to the .xdata section!
43*d415bd75Srobert   EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(),
44*d415bd75Srobert                             /* HandlerData = */ true);
45*d415bd75Srobert }
46*d415bd75Srobert 
emitWindowsUnwindTables(WinEH::FrameInfo * Frame)47*d415bd75Srobert void ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) {
48*d415bd75Srobert   EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false);
49*d415bd75Srobert }
50*d415bd75Srobert 
emitWindowsUnwindTables()51*d415bd75Srobert void ARMWinCOFFStreamer::emitWindowsUnwindTables() {
52*d415bd75Srobert   if (!getNumWinFrameInfos())
53*d415bd75Srobert     return;
54*d415bd75Srobert   EHStreamer.Emit(*this);
55*d415bd75Srobert }
56*d415bd75Srobert 
emitThumbFunc(MCSymbol * Symbol)57097a140dSpatrick void ARMWinCOFFStreamer::emitThumbFunc(MCSymbol *Symbol) {
5809467b48Spatrick   getAssembler().setIsThumbFunc(Symbol);
5909467b48Spatrick }
6009467b48Spatrick 
finishImpl()61097a140dSpatrick void ARMWinCOFFStreamer::finishImpl() {
62097a140dSpatrick   emitFrames(nullptr);
63*d415bd75Srobert   emitWindowsUnwindTables();
6409467b48Spatrick 
65097a140dSpatrick   MCWinCOFFStreamer::finishImpl();
6609467b48Spatrick }
6709467b48Spatrick }
6809467b48Spatrick 
createARMWinCOFFStreamer(MCContext & Context,std::unique_ptr<MCAsmBackend> && MAB,std::unique_ptr<MCObjectWriter> && OW,std::unique_ptr<MCCodeEmitter> && Emitter,bool RelaxAll,bool IncrementalLinkerCompatible)6909467b48Spatrick MCStreamer *llvm::createARMWinCOFFStreamer(
7009467b48Spatrick     MCContext &Context, std::unique_ptr<MCAsmBackend> &&MAB,
7109467b48Spatrick     std::unique_ptr<MCObjectWriter> &&OW,
7209467b48Spatrick     std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll,
7309467b48Spatrick     bool IncrementalLinkerCompatible) {
7409467b48Spatrick   auto *S = new ARMWinCOFFStreamer(Context, std::move(MAB), std::move(Emitter),
7509467b48Spatrick                                    std::move(OW));
7609467b48Spatrick   S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
7709467b48Spatrick   return S;
7809467b48Spatrick }
7909467b48Spatrick 
80*d415bd75Srobert namespace {
81*d415bd75Srobert class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer {
82*d415bd75Srobert private:
83*d415bd75Srobert   // True if we are processing SEH directives in an epilogue.
84*d415bd75Srobert   bool InEpilogCFI = false;
85*d415bd75Srobert 
86*d415bd75Srobert   // Symbol of the current epilog for which we are processing SEH directives.
87*d415bd75Srobert   MCSymbol *CurrentEpilog = nullptr;
88*d415bd75Srobert 
89*d415bd75Srobert public:
ARMTargetWinCOFFStreamer(llvm::MCStreamer & S)90*d415bd75Srobert   ARMTargetWinCOFFStreamer(llvm::MCStreamer &S) : ARMTargetStreamer(S) {}
91*d415bd75Srobert 
92*d415bd75Srobert   // The unwind codes on ARM Windows are documented at
93*d415bd75Srobert   // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
94*d415bd75Srobert   void emitARMWinCFIAllocStack(unsigned Size, bool Wide) override;
95*d415bd75Srobert   void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) override;
96*d415bd75Srobert   void emitARMWinCFISaveSP(unsigned Reg) override;
97*d415bd75Srobert   void emitARMWinCFISaveFRegs(unsigned First, unsigned Last) override;
98*d415bd75Srobert   void emitARMWinCFISaveLR(unsigned Offset) override;
99*d415bd75Srobert   void emitARMWinCFIPrologEnd(bool Fragment) override;
100*d415bd75Srobert   void emitARMWinCFINop(bool Wide) override;
101*d415bd75Srobert   void emitARMWinCFIEpilogStart(unsigned Condition) override;
102*d415bd75Srobert   void emitARMWinCFIEpilogEnd() override;
103*d415bd75Srobert   void emitARMWinCFICustom(unsigned Opcode) override;
104*d415bd75Srobert 
105*d415bd75Srobert private:
106*d415bd75Srobert   void emitARMWinUnwindCode(unsigned UnwindCode, int Reg, int Offset);
107*d415bd75Srobert };
108*d415bd75Srobert 
109*d415bd75Srobert // Helper function to common out unwind code setup for those codes that can
110*d415bd75Srobert // belong to both prolog and epilog.
emitARMWinUnwindCode(unsigned UnwindCode,int Reg,int Offset)111*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinUnwindCode(unsigned UnwindCode,
112*d415bd75Srobert                                                     int Reg, int Offset) {
113*d415bd75Srobert   auto &S = getStreamer();
114*d415bd75Srobert   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
115*d415bd75Srobert   if (!CurFrame)
116*d415bd75Srobert     return;
117*d415bd75Srobert   MCSymbol *Label = S.emitCFILabel();
118*d415bd75Srobert   auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset);
119*d415bd75Srobert   if (InEpilogCFI)
120*d415bd75Srobert     CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
121*d415bd75Srobert   else
122*d415bd75Srobert     CurFrame->Instructions.push_back(Inst);
123*d415bd75Srobert }
124*d415bd75Srobert 
emitARMWinCFIAllocStack(unsigned Size,bool Wide)125*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFIAllocStack(unsigned Size,
126*d415bd75Srobert                                                        bool Wide) {
127*d415bd75Srobert   unsigned Op = Win64EH::UOP_AllocSmall;
128*d415bd75Srobert   if (!Wide) {
129*d415bd75Srobert     if (Size / 4 > 0xffff)
130*d415bd75Srobert       Op = Win64EH::UOP_AllocHuge;
131*d415bd75Srobert     else if (Size / 4 > 0x7f)
132*d415bd75Srobert       Op = Win64EH::UOP_AllocLarge;
133*d415bd75Srobert   } else {
134*d415bd75Srobert     Op = Win64EH::UOP_WideAllocMedium;
135*d415bd75Srobert     if (Size / 4 > 0xffff)
136*d415bd75Srobert       Op = Win64EH::UOP_WideAllocHuge;
137*d415bd75Srobert     else if (Size / 4 > 0x3ff)
138*d415bd75Srobert       Op = Win64EH::UOP_WideAllocLarge;
139*d415bd75Srobert   }
140*d415bd75Srobert   emitARMWinUnwindCode(Op, -1, Size);
141*d415bd75Srobert }
142*d415bd75Srobert 
emitARMWinCFISaveRegMask(unsigned Mask,bool Wide)143*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFISaveRegMask(unsigned Mask,
144*d415bd75Srobert                                                         bool Wide) {
145*d415bd75Srobert   assert(Mask != 0);
146*d415bd75Srobert   int Lr = (Mask & 0x4000) ? 1 : 0;
147*d415bd75Srobert   Mask &= ~0x4000;
148*d415bd75Srobert   if (Wide)
149*d415bd75Srobert     assert((Mask & ~0x1fff) == 0);
150*d415bd75Srobert   else
151*d415bd75Srobert     assert((Mask & ~0x00ff) == 0);
152*d415bd75Srobert   if (Mask && ((Mask + (1 << 4)) & Mask) == 0) {
153*d415bd75Srobert     if (Wide && (Mask & 0x1000) == 0 && (Mask & 0xff) == 0xf0) {
154*d415bd75Srobert       // One continuous range from r4 to r8-r11
155*d415bd75Srobert       for (int I = 11; I >= 8; I--) {
156*d415bd75Srobert         if (Mask & (1 << I)) {
157*d415bd75Srobert           emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegsR4R11LR, I, Lr);
158*d415bd75Srobert           return;
159*d415bd75Srobert         }
160*d415bd75Srobert       }
161*d415bd75Srobert       // If it actually was from r4 to r4-r7, continue below.
162*d415bd75Srobert     } else if (!Wide) {
163*d415bd75Srobert       // One continuous range from r4 to r4-r7
164*d415bd75Srobert       for (int I = 7; I >= 4; I--) {
165*d415bd75Srobert         if (Mask & (1 << I)) {
166*d415bd75Srobert           emitARMWinUnwindCode(Win64EH::UOP_SaveRegsR4R7LR, I, Lr);
167*d415bd75Srobert           return;
168*d415bd75Srobert         }
169*d415bd75Srobert       }
170*d415bd75Srobert       llvm_unreachable("logic error");
171*d415bd75Srobert     }
172*d415bd75Srobert   }
173*d415bd75Srobert   Mask |= Lr << 14;
174*d415bd75Srobert   if (Wide)
175*d415bd75Srobert     emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegMask, Mask, 0);
176*d415bd75Srobert   else
177*d415bd75Srobert     emitARMWinUnwindCode(Win64EH::UOP_SaveRegMask, Mask, 0);
178*d415bd75Srobert }
179*d415bd75Srobert 
emitARMWinCFISaveSP(unsigned Reg)180*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) {
181*d415bd75Srobert   emitARMWinUnwindCode(Win64EH::UOP_SaveSP, Reg, 0);
182*d415bd75Srobert }
183*d415bd75Srobert 
emitARMWinCFISaveFRegs(unsigned First,unsigned Last)184*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFISaveFRegs(unsigned First,
185*d415bd75Srobert                                                       unsigned Last) {
186*d415bd75Srobert   assert(First <= Last);
187*d415bd75Srobert   assert(First >= 16 || Last < 16);
188*d415bd75Srobert   assert(First <= 31 && Last <= 31);
189*d415bd75Srobert   if (First == 8)
190*d415bd75Srobert     emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD8D15, Last, 0);
191*d415bd75Srobert   else if (First <= 15)
192*d415bd75Srobert     emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD0D15, First, Last);
193*d415bd75Srobert   else
194*d415bd75Srobert     emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD16D31, First, Last);
195*d415bd75Srobert }
196*d415bd75Srobert 
emitARMWinCFISaveLR(unsigned Offset)197*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) {
198*d415bd75Srobert   emitARMWinUnwindCode(Win64EH::UOP_SaveLR, 0, Offset);
199*d415bd75Srobert }
200*d415bd75Srobert 
emitARMWinCFINop(bool Wide)201*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFINop(bool Wide) {
202*d415bd75Srobert   if (Wide)
203*d415bd75Srobert     emitARMWinUnwindCode(Win64EH::UOP_WideNop, -1, 0);
204*d415bd75Srobert   else
205*d415bd75Srobert     emitARMWinUnwindCode(Win64EH::UOP_Nop, -1, 0);
206*d415bd75Srobert }
207*d415bd75Srobert 
emitARMWinCFIPrologEnd(bool Fragment)208*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFIPrologEnd(bool Fragment) {
209*d415bd75Srobert   auto &S = getStreamer();
210*d415bd75Srobert   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
211*d415bd75Srobert   if (!CurFrame)
212*d415bd75Srobert     return;
213*d415bd75Srobert 
214*d415bd75Srobert   MCSymbol *Label = S.emitCFILabel();
215*d415bd75Srobert   CurFrame->PrologEnd = Label;
216*d415bd75Srobert   WinEH::Instruction Inst =
217*d415bd75Srobert       WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0);
218*d415bd75Srobert   auto it = CurFrame->Instructions.begin();
219*d415bd75Srobert   CurFrame->Instructions.insert(it, Inst);
220*d415bd75Srobert   CurFrame->Fragment = Fragment;
221*d415bd75Srobert }
222*d415bd75Srobert 
emitARMWinCFIEpilogStart(unsigned Condition)223*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogStart(unsigned Condition) {
224*d415bd75Srobert   auto &S = getStreamer();
225*d415bd75Srobert   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
226*d415bd75Srobert   if (!CurFrame)
227*d415bd75Srobert     return;
228*d415bd75Srobert 
229*d415bd75Srobert   InEpilogCFI = true;
230*d415bd75Srobert   CurrentEpilog = S.emitCFILabel();
231*d415bd75Srobert   CurFrame->EpilogMap[CurrentEpilog].Condition = Condition;
232*d415bd75Srobert }
233*d415bd75Srobert 
emitARMWinCFIEpilogEnd()234*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogEnd() {
235*d415bd75Srobert   auto &S = getStreamer();
236*d415bd75Srobert   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
237*d415bd75Srobert   if (!CurFrame)
238*d415bd75Srobert     return;
239*d415bd75Srobert 
240*d415bd75Srobert   if (!CurrentEpilog) {
241*d415bd75Srobert     S.getContext().reportError(SMLoc(), "Stray .seh_endepilogue in " +
242*d415bd75Srobert                                             CurFrame->Function->getName());
243*d415bd75Srobert     return;
244*d415bd75Srobert   }
245*d415bd75Srobert 
246*d415bd75Srobert   std::vector<WinEH::Instruction> &Epilog =
247*d415bd75Srobert       CurFrame->EpilogMap[CurrentEpilog].Instructions;
248*d415bd75Srobert 
249*d415bd75Srobert   unsigned UnwindCode = Win64EH::UOP_End;
250*d415bd75Srobert   if (!Epilog.empty()) {
251*d415bd75Srobert     WinEH::Instruction EndInstr = Epilog.back();
252*d415bd75Srobert     if (EndInstr.Operation == Win64EH::UOP_Nop) {
253*d415bd75Srobert       UnwindCode = Win64EH::UOP_EndNop;
254*d415bd75Srobert       Epilog.pop_back();
255*d415bd75Srobert     } else if (EndInstr.Operation == Win64EH::UOP_WideNop) {
256*d415bd75Srobert       UnwindCode = Win64EH::UOP_WideEndNop;
257*d415bd75Srobert       Epilog.pop_back();
258*d415bd75Srobert     }
259*d415bd75Srobert   }
260*d415bd75Srobert 
261*d415bd75Srobert   InEpilogCFI = false;
262*d415bd75Srobert   WinEH::Instruction Inst = WinEH::Instruction(UnwindCode, nullptr, -1, 0);
263*d415bd75Srobert   CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
264*d415bd75Srobert   MCSymbol *Label = S.emitCFILabel();
265*d415bd75Srobert   CurFrame->EpilogMap[CurrentEpilog].End = Label;
266*d415bd75Srobert   CurrentEpilog = nullptr;
267*d415bd75Srobert }
268*d415bd75Srobert 
emitARMWinCFICustom(unsigned Opcode)269*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) {
270*d415bd75Srobert   emitARMWinUnwindCode(Win64EH::UOP_Custom, 0, Opcode);
271*d415bd75Srobert }
272*d415bd75Srobert 
273*d415bd75Srobert } // end anonymous namespace
274*d415bd75Srobert 
createARMObjectTargetWinCOFFStreamer(MCStreamer & S)275*d415bd75Srobert MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) {
276*d415bd75Srobert   return new ARMTargetWinCOFFStreamer(S);
277*d415bd75Srobert }
278