1 //===-- AArch64WinCOFFStreamer.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 "AArch64WinCOFFStreamer.h"
10 #include "llvm/MC/MCAsmBackend.h"
11 #include "llvm/MC/MCAssembler.h"
12 #include "llvm/MC/MCCodeEmitter.h"
13 #include "llvm/MC/MCObjectWriter.h"
14 #include "llvm/MC/MCWin64EH.h"
15 #include "llvm/MC/MCWinCOFFStreamer.h"
16 
17 using namespace llvm;
18 
19 namespace {
20 
21 class AArch64WinCOFFStreamer : public MCWinCOFFStreamer {
22   Win64EH::ARM64UnwindEmitter EHStreamer;
23 
24 public:
AArch64WinCOFFStreamer(MCContext & C,std::unique_ptr<MCAsmBackend> AB,std::unique_ptr<MCCodeEmitter> CE,std::unique_ptr<MCObjectWriter> OW)25   AArch64WinCOFFStreamer(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   void finishImpl() override;
34 };
35 
emitWinEHHandlerData(SMLoc Loc)36 void AArch64WinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) {
37   MCStreamer::emitWinEHHandlerData(Loc);
38 
39   // We have to emit the unwind info now, because this directive
40   // actually switches to the .xdata section!
41   EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(),
42                             /* HandlerData = */ true);
43 }
44 
emitWindowsUnwindTables(WinEH::FrameInfo * Frame)45 void AArch64WinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) {
46   EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false);
47 }
48 
emitWindowsUnwindTables()49 void AArch64WinCOFFStreamer::emitWindowsUnwindTables() {
50   if (!getNumWinFrameInfos())
51     return;
52   EHStreamer.Emit(*this);
53 }
54 
finishImpl()55 void AArch64WinCOFFStreamer::finishImpl() {
56   emitFrames(nullptr);
57   emitWindowsUnwindTables();
58 
59   MCWinCOFFStreamer::finishImpl();
60 }
61 } // end anonymous namespace
62 
63 // Helper function to common out unwind code setup for those codes that can
64 // belong to both prolog and epilog.
65 // There are three types of Windows ARM64 SEH codes.  They can
66 // 1) take no operands: SEH_Nop, SEH_PrologEnd, SEH_EpilogStart, SEH_EpilogEnd
67 // 2) take an offset: SEH_StackAlloc, SEH_SaveFPLR, SEH_SaveFPLR_X
68 // 3) take a register and an offset/size: all others
emitARM64WinUnwindCode(unsigned UnwindCode,int Reg,int Offset)69 void AArch64TargetWinCOFFStreamer::emitARM64WinUnwindCode(unsigned UnwindCode,
70                                                           int Reg, int Offset) {
71   auto &S = getStreamer();
72   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
73   if (!CurFrame)
74     return;
75   auto Inst = WinEH::Instruction(UnwindCode, /*Label=*/nullptr, Reg, Offset);
76   if (InEpilogCFI)
77     CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
78   else
79     CurFrame->Instructions.push_back(Inst);
80 }
81 
emitARM64WinCFIAllocStack(unsigned Size)82 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIAllocStack(unsigned Size) {
83   unsigned Op = Win64EH::UOP_AllocSmall;
84   if (Size >= 16384)
85     Op = Win64EH::UOP_AllocLarge;
86   else if (Size >= 512)
87     Op = Win64EH::UOP_AllocMedium;
88   emitARM64WinUnwindCode(Op, -1, Size);
89 }
90 
emitARM64WinCFISaveR19R20X(int Offset)91 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveR19R20X(int Offset) {
92   emitARM64WinUnwindCode(Win64EH::UOP_SaveR19R20X, -1, Offset);
93 }
94 
emitARM64WinCFISaveFPLR(int Offset)95 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFPLR(int Offset) {
96   emitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset);
97 }
98 
emitARM64WinCFISaveFPLRX(int Offset)99 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFPLRX(int Offset) {
100   emitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset);
101 }
102 
emitARM64WinCFISaveReg(unsigned Reg,int Offset)103 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveReg(unsigned Reg,
104                                                           int Offset) {
105   assert(Offset >= 0 && Offset <= 504 &&
106         "Offset for save reg should be >= 0 && <= 504");
107   emitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset);
108 }
109 
emitARM64WinCFISaveRegX(unsigned Reg,int Offset)110 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegX(unsigned Reg,
111                                                            int Offset) {
112   emitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset);
113 }
114 
emitARM64WinCFISaveRegP(unsigned Reg,int Offset)115 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegP(unsigned Reg,
116                                                            int Offset) {
117   emitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset);
118 }
119 
emitARM64WinCFISaveRegPX(unsigned Reg,int Offset)120 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegPX(unsigned Reg,
121                                                             int Offset) {
122   emitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset);
123 }
124 
emitARM64WinCFISaveLRPair(unsigned Reg,int Offset)125 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveLRPair(unsigned Reg,
126                                                              int Offset) {
127   emitARM64WinUnwindCode(Win64EH::UOP_SaveLRPair, Reg, Offset);
128 }
129 
emitARM64WinCFISaveFReg(unsigned Reg,int Offset)130 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFReg(unsigned Reg,
131                                                            int Offset) {
132   assert(Offset >= 0 && Offset <= 504 &&
133         "Offset for save reg should be >= 0 && <= 504");
134   emitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset);
135 }
136 
emitARM64WinCFISaveFRegX(unsigned Reg,int Offset)137 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegX(unsigned Reg,
138                                                             int Offset) {
139   emitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset);
140 }
141 
emitARM64WinCFISaveFRegP(unsigned Reg,int Offset)142 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegP(unsigned Reg,
143                                                             int Offset) {
144   emitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset);
145 }
146 
emitARM64WinCFISaveFRegPX(unsigned Reg,int Offset)147 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegPX(unsigned Reg,
148                                                              int Offset) {
149   emitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset);
150 }
151 
emitARM64WinCFISetFP()152 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISetFP() {
153   emitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0);
154 }
155 
emitARM64WinCFIAddFP(unsigned Offset)156 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIAddFP(unsigned Offset) {
157   assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040");
158   emitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset);
159 }
160 
emitARM64WinCFINop()161 void AArch64TargetWinCOFFStreamer::emitARM64WinCFINop() {
162   emitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0);
163 }
164 
emitARM64WinCFISaveNext()165 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveNext() {
166   emitARM64WinUnwindCode(Win64EH::UOP_SaveNext, -1, 0);
167 }
168 
169 // The functions below handle opcodes that can end up in either a prolog or
170 // an epilog, but not both.
emitARM64WinCFIPrologEnd()171 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIPrologEnd() {
172   auto &S = getStreamer();
173   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
174   if (!CurFrame)
175     return;
176 
177   MCSymbol *Label = S.emitCFILabel();
178   CurFrame->PrologEnd = Label;
179   WinEH::Instruction Inst =
180       WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0);
181   auto it = CurFrame->Instructions.begin();
182   CurFrame->Instructions.insert(it, Inst);
183 }
184 
emitARM64WinCFIEpilogStart()185 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIEpilogStart() {
186   auto &S = getStreamer();
187   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
188   if (!CurFrame)
189     return;
190 
191   InEpilogCFI = true;
192   CurrentEpilog = S.emitCFILabel();
193 }
194 
emitARM64WinCFIEpilogEnd()195 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIEpilogEnd() {
196   auto &S = getStreamer();
197   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
198   if (!CurFrame)
199     return;
200 
201   InEpilogCFI = false;
202   WinEH::Instruction Inst =
203       WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0);
204   CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
205   MCSymbol *Label = S.emitCFILabel();
206   CurFrame->EpilogMap[CurrentEpilog].End = Label;
207   CurrentEpilog = nullptr;
208 }
209 
emitARM64WinCFITrapFrame()210 void AArch64TargetWinCOFFStreamer::emitARM64WinCFITrapFrame() {
211   emitARM64WinUnwindCode(Win64EH::UOP_TrapFrame, -1, 0);
212 }
213 
emitARM64WinCFIMachineFrame()214 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIMachineFrame() {
215   emitARM64WinUnwindCode(Win64EH::UOP_PushMachFrame, -1, 0);
216 }
217 
emitARM64WinCFIContext()218 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIContext() {
219   emitARM64WinUnwindCode(Win64EH::UOP_Context, -1, 0);
220 }
221 
emitARM64WinCFIECContext()222 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIECContext() {
223   emitARM64WinUnwindCode(Win64EH::UOP_ECContext, -1, 0);
224 }
225 
emitARM64WinCFIClearUnwoundToCall()226 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIClearUnwoundToCall() {
227   emitARM64WinUnwindCode(Win64EH::UOP_ClearUnwoundToCall, -1, 0);
228 }
229 
emitARM64WinCFIPACSignLR()230 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIPACSignLR() {
231   emitARM64WinUnwindCode(Win64EH::UOP_PACSignLR, -1, 0);
232 }
233 
emitARM64WinCFISaveAnyRegI(unsigned Reg,int Offset)234 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegI(unsigned Reg,
235                                                               int Offset) {
236   emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegI, Reg, Offset);
237 }
238 
emitARM64WinCFISaveAnyRegIP(unsigned Reg,int Offset)239 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIP(unsigned Reg,
240                                                                int Offset) {
241   emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegIP, Reg, Offset);
242 }
243 
emitARM64WinCFISaveAnyRegD(unsigned Reg,int Offset)244 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegD(unsigned Reg,
245                                                               int Offset) {
246   emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegD, Reg, Offset);
247 }
248 
emitARM64WinCFISaveAnyRegDP(unsigned Reg,int Offset)249 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDP(unsigned Reg,
250                                                                int Offset) {
251   emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegDP, Reg, Offset);
252 }
253 
emitARM64WinCFISaveAnyRegQ(unsigned Reg,int Offset)254 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQ(unsigned Reg,
255                                                               int Offset) {
256   emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegQ, Reg, Offset);
257 }
258 
emitARM64WinCFISaveAnyRegQP(unsigned Reg,int Offset)259 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQP(unsigned Reg,
260                                                                int Offset) {
261   emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegQP, Reg, Offset);
262 }
263 
emitARM64WinCFISaveAnyRegIX(unsigned Reg,int Offset)264 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIX(unsigned Reg,
265                                                                int Offset) {
266   emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegIX, Reg, Offset);
267 }
268 
emitARM64WinCFISaveAnyRegIPX(unsigned Reg,int Offset)269 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIPX(unsigned Reg,
270                                                                 int Offset) {
271   emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegIPX, Reg, Offset);
272 }
273 
emitARM64WinCFISaveAnyRegDX(unsigned Reg,int Offset)274 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDX(unsigned Reg,
275                                                                int Offset) {
276   emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegDX, Reg, Offset);
277 }
278 
emitARM64WinCFISaveAnyRegDPX(unsigned Reg,int Offset)279 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDPX(unsigned Reg,
280                                                                 int Offset) {
281   emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegDPX, Reg, Offset);
282 }
283 
emitARM64WinCFISaveAnyRegQX(unsigned Reg,int Offset)284 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQX(unsigned Reg,
285                                                                int Offset) {
286   emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegQX, Reg, Offset);
287 }
288 
emitARM64WinCFISaveAnyRegQPX(unsigned Reg,int Offset)289 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQPX(unsigned Reg,
290                                                                 int Offset) {
291   emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegQPX, Reg, Offset);
292 }
293 
createAArch64WinCOFFStreamer(MCContext & Context,std::unique_ptr<MCAsmBackend> MAB,std::unique_ptr<MCObjectWriter> OW,std::unique_ptr<MCCodeEmitter> Emitter,bool RelaxAll,bool IncrementalLinkerCompatible)294 MCWinCOFFStreamer *llvm::createAArch64WinCOFFStreamer(
295     MCContext &Context, std::unique_ptr<MCAsmBackend> MAB,
296     std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter,
297     bool RelaxAll, bool IncrementalLinkerCompatible) {
298   auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB),
299                                        std::move(Emitter), std::move(OW));
300   S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
301   return S;
302 }
303