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