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