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 
createARMWinCOFFStreamer(MCContext & Context,std::unique_ptr<MCAsmBackend> && MAB,std::unique_ptr<MCObjectWriter> && OW,std::unique_ptr<MCCodeEmitter> && Emitter,bool RelaxAll,bool IncrementalLinkerCompatible)69 MCStreamer *llvm::createARMWinCOFFStreamer(
70     MCContext &Context, std::unique_ptr<MCAsmBackend> &&MAB,
71     std::unique_ptr<MCObjectWriter> &&OW,
72     std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll,
73     bool IncrementalLinkerCompatible) {
74   auto *S = new ARMWinCOFFStreamer(Context, std::move(MAB), std::move(Emitter),
75                                    std::move(OW));
76   S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
77   return S;
78 }
79 
80 namespace {
81 class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer {
82 private:
83   // True if we are processing SEH directives in an epilogue.
84   bool InEpilogCFI = false;
85 
86   // Symbol of the current epilog for which we are processing SEH directives.
87   MCSymbol *CurrentEpilog = nullptr;
88 
89 public:
ARMTargetWinCOFFStreamer(llvm::MCStreamer & S)90   ARMTargetWinCOFFStreamer(llvm::MCStreamer &S) : ARMTargetStreamer(S) {}
91 
92   // The unwind codes on ARM Windows are documented at
93   // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
94   void emitARMWinCFIAllocStack(unsigned Size, bool Wide) override;
95   void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) override;
96   void emitARMWinCFISaveSP(unsigned Reg) override;
97   void emitARMWinCFISaveFRegs(unsigned First, unsigned Last) override;
98   void emitARMWinCFISaveLR(unsigned Offset) override;
99   void emitARMWinCFIPrologEnd(bool Fragment) override;
100   void emitARMWinCFINop(bool Wide) override;
101   void emitARMWinCFIEpilogStart(unsigned Condition) override;
102   void emitARMWinCFIEpilogEnd() override;
103   void emitARMWinCFICustom(unsigned Opcode) override;
104 
105 private:
106   void emitARMWinUnwindCode(unsigned UnwindCode, int Reg, int Offset);
107 };
108 
109 // Helper function to common out unwind code setup for those codes that can
110 // belong to both prolog and epilog.
emitARMWinUnwindCode(unsigned UnwindCode,int Reg,int Offset)111 void ARMTargetWinCOFFStreamer::emitARMWinUnwindCode(unsigned UnwindCode,
112                                                     int Reg, int Offset) {
113   auto &S = getStreamer();
114   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
115   if (!CurFrame)
116     return;
117   MCSymbol *Label = S.emitCFILabel();
118   auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset);
119   if (InEpilogCFI)
120     CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
121   else
122     CurFrame->Instructions.push_back(Inst);
123 }
124 
emitARMWinCFIAllocStack(unsigned Size,bool Wide)125 void ARMTargetWinCOFFStreamer::emitARMWinCFIAllocStack(unsigned Size,
126                                                        bool Wide) {
127   unsigned Op = Win64EH::UOP_AllocSmall;
128   if (!Wide) {
129     if (Size / 4 > 0xffff)
130       Op = Win64EH::UOP_AllocHuge;
131     else if (Size / 4 > 0x7f)
132       Op = Win64EH::UOP_AllocLarge;
133   } else {
134     Op = Win64EH::UOP_WideAllocMedium;
135     if (Size / 4 > 0xffff)
136       Op = Win64EH::UOP_WideAllocHuge;
137     else if (Size / 4 > 0x3ff)
138       Op = Win64EH::UOP_WideAllocLarge;
139   }
140   emitARMWinUnwindCode(Op, -1, Size);
141 }
142 
emitARMWinCFISaveRegMask(unsigned Mask,bool Wide)143 void ARMTargetWinCOFFStreamer::emitARMWinCFISaveRegMask(unsigned Mask,
144                                                         bool Wide) {
145   assert(Mask != 0);
146   int Lr = (Mask & 0x4000) ? 1 : 0;
147   Mask &= ~0x4000;
148   if (Wide)
149     assert((Mask & ~0x1fff) == 0);
150   else
151     assert((Mask & ~0x00ff) == 0);
152   if (Mask && ((Mask + (1 << 4)) & Mask) == 0) {
153     if (Wide && (Mask & 0x1000) == 0 && (Mask & 0xff) == 0xf0) {
154       // One continuous range from r4 to r8-r11
155       for (int I = 11; I >= 8; I--) {
156         if (Mask & (1 << I)) {
157           emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegsR4R11LR, I, Lr);
158           return;
159         }
160       }
161       // If it actually was from r4 to r4-r7, continue below.
162     } else if (!Wide) {
163       // One continuous range from r4 to r4-r7
164       for (int I = 7; I >= 4; I--) {
165         if (Mask & (1 << I)) {
166           emitARMWinUnwindCode(Win64EH::UOP_SaveRegsR4R7LR, I, Lr);
167           return;
168         }
169       }
170       llvm_unreachable("logic error");
171     }
172   }
173   Mask |= Lr << 14;
174   if (Wide)
175     emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegMask, Mask, 0);
176   else
177     emitARMWinUnwindCode(Win64EH::UOP_SaveRegMask, Mask, 0);
178 }
179 
emitARMWinCFISaveSP(unsigned Reg)180 void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) {
181   emitARMWinUnwindCode(Win64EH::UOP_SaveSP, Reg, 0);
182 }
183 
emitARMWinCFISaveFRegs(unsigned First,unsigned Last)184 void ARMTargetWinCOFFStreamer::emitARMWinCFISaveFRegs(unsigned First,
185                                                       unsigned Last) {
186   assert(First <= Last);
187   assert(First >= 16 || Last < 16);
188   assert(First <= 31 && Last <= 31);
189   if (First == 8)
190     emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD8D15, Last, 0);
191   else if (First <= 15)
192     emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD0D15, First, Last);
193   else
194     emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD16D31, First, Last);
195 }
196 
emitARMWinCFISaveLR(unsigned Offset)197 void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) {
198   emitARMWinUnwindCode(Win64EH::UOP_SaveLR, 0, Offset);
199 }
200 
emitARMWinCFINop(bool Wide)201 void ARMTargetWinCOFFStreamer::emitARMWinCFINop(bool Wide) {
202   if (Wide)
203     emitARMWinUnwindCode(Win64EH::UOP_WideNop, -1, 0);
204   else
205     emitARMWinUnwindCode(Win64EH::UOP_Nop, -1, 0);
206 }
207 
emitARMWinCFIPrologEnd(bool Fragment)208 void ARMTargetWinCOFFStreamer::emitARMWinCFIPrologEnd(bool Fragment) {
209   auto &S = getStreamer();
210   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
211   if (!CurFrame)
212     return;
213 
214   MCSymbol *Label = S.emitCFILabel();
215   CurFrame->PrologEnd = Label;
216   WinEH::Instruction Inst =
217       WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0);
218   auto it = CurFrame->Instructions.begin();
219   CurFrame->Instructions.insert(it, Inst);
220   CurFrame->Fragment = Fragment;
221 }
222 
emitARMWinCFIEpilogStart(unsigned Condition)223 void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogStart(unsigned Condition) {
224   auto &S = getStreamer();
225   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
226   if (!CurFrame)
227     return;
228 
229   InEpilogCFI = true;
230   CurrentEpilog = S.emitCFILabel();
231   CurFrame->EpilogMap[CurrentEpilog].Condition = Condition;
232 }
233 
emitARMWinCFIEpilogEnd()234 void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogEnd() {
235   auto &S = getStreamer();
236   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
237   if (!CurFrame)
238     return;
239 
240   if (!CurrentEpilog) {
241     S.getContext().reportError(SMLoc(), "Stray .seh_endepilogue in " +
242                                             CurFrame->Function->getName());
243     return;
244   }
245 
246   std::vector<WinEH::Instruction> &Epilog =
247       CurFrame->EpilogMap[CurrentEpilog].Instructions;
248 
249   unsigned UnwindCode = Win64EH::UOP_End;
250   if (!Epilog.empty()) {
251     WinEH::Instruction EndInstr = Epilog.back();
252     if (EndInstr.Operation == Win64EH::UOP_Nop) {
253       UnwindCode = Win64EH::UOP_EndNop;
254       Epilog.pop_back();
255     } else if (EndInstr.Operation == Win64EH::UOP_WideNop) {
256       UnwindCode = Win64EH::UOP_WideEndNop;
257       Epilog.pop_back();
258     }
259   }
260 
261   InEpilogCFI = false;
262   WinEH::Instruction Inst = WinEH::Instruction(UnwindCode, nullptr, -1, 0);
263   CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
264   MCSymbol *Label = S.emitCFILabel();
265   CurFrame->EpilogMap[CurrentEpilog].End = Label;
266   CurrentEpilog = nullptr;
267 }
268 
emitARMWinCFICustom(unsigned Opcode)269 void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) {
270   emitARMWinUnwindCode(Win64EH::UOP_Custom, 0, Opcode);
271 }
272 
273 } // end anonymous namespace
274 
createARMObjectTargetWinCOFFStreamer(MCStreamer & S)275 MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) {
276   return new ARMTargetWinCOFFStreamer(S);
277 }
278