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: |
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 | |
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(Streamer&: *this, FI: getCurrentWinFrameInfo(), |
42 | /* HandlerData = */ true); |
43 | } |
44 | |
45 | void AArch64WinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) { |
46 | EHStreamer.EmitUnwindInfo(Streamer&: *this, FI: Frame, /* HandlerData = */ false); |
47 | } |
48 | |
49 | void AArch64WinCOFFStreamer::emitWindowsUnwindTables() { |
50 | if (!getNumWinFrameInfos()) |
51 | return; |
52 | EHStreamer.Emit(Streamer&: *this); |
53 | } |
54 | |
55 | void AArch64WinCOFFStreamer::finishImpl() { |
56 | emitFrames(MAB: 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 |
69 | void AArch64TargetWinCOFFStreamer::emitARM64WinUnwindCode(unsigned UnwindCode, |
70 | int Reg, int Offset) { |
71 | auto &S = getStreamer(); |
72 | WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(Loc: SMLoc()); |
73 | if (!CurFrame) |
74 | return; |
75 | auto Inst = WinEH::Instruction(UnwindCode, /*Label=*/nullptr, Reg, Offset); |
76 | if (S.isInEpilogCFI()) |
77 | S.getCurrentWinEpilog()->Instructions.push_back(x: Inst); |
78 | else |
79 | CurFrame->Instructions.push_back(x: Inst); |
80 | } |
81 | |
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(UnwindCode: Op, Reg: -1, Offset: Size); |
89 | } |
90 | |
91 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveR19R20X(int Offset) { |
92 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveR19R20X, Reg: -1, Offset); |
93 | } |
94 | |
95 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFPLR(int Offset) { |
96 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveFPLR, Reg: -1, Offset); |
97 | } |
98 | |
99 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFPLRX(int Offset) { |
100 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveFPLRX, Reg: -1, Offset); |
101 | } |
102 | |
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(UnwindCode: Win64EH::UOP_SaveReg, Reg, Offset); |
108 | } |
109 | |
110 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegX(unsigned Reg, |
111 | int Offset) { |
112 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveRegX, Reg, Offset); |
113 | } |
114 | |
115 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegP(unsigned Reg, |
116 | int Offset) { |
117 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveRegP, Reg, Offset); |
118 | } |
119 | |
120 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegPX(unsigned Reg, |
121 | int Offset) { |
122 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveRegPX, Reg, Offset); |
123 | } |
124 | |
125 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveLRPair(unsigned Reg, |
126 | int Offset) { |
127 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveLRPair, Reg, Offset); |
128 | } |
129 | |
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(UnwindCode: Win64EH::UOP_SaveFReg, Reg, Offset); |
135 | } |
136 | |
137 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegX(unsigned Reg, |
138 | int Offset) { |
139 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveFRegX, Reg, Offset); |
140 | } |
141 | |
142 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegP(unsigned Reg, |
143 | int Offset) { |
144 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveFRegP, Reg, Offset); |
145 | } |
146 | |
147 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegPX(unsigned Reg, |
148 | int Offset) { |
149 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveFRegPX, Reg, Offset); |
150 | } |
151 | |
152 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISetFP() { |
153 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SetFP, Reg: -1, Offset: 0); |
154 | } |
155 | |
156 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIAddFP(unsigned Offset) { |
157 | assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040" ); |
158 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_AddFP, Reg: -1, Offset); |
159 | } |
160 | |
161 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFINop() { |
162 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_Nop, Reg: -1, Offset: 0); |
163 | } |
164 | |
165 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveNext() { |
166 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveNext, Reg: -1, Offset: 0); |
167 | } |
168 | |
169 | // The functions below handle opcodes that can end up in either a prolog or |
170 | // an epilog, but not both. |
171 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIPrologEnd() { |
172 | auto &S = getStreamer(); |
173 | WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(Loc: 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(position: it, x: Inst); |
183 | } |
184 | |
185 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIEpilogStart() { |
186 | getStreamer().emitWinCFIBeginEpilogue(); |
187 | } |
188 | |
189 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIEpilogEnd() { |
190 | auto &S = getStreamer(); |
191 | WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(Loc: SMLoc()); |
192 | if (!CurFrame) |
193 | return; |
194 | |
195 | if (S.isInEpilogCFI()) { |
196 | WinEH::Instruction Inst = |
197 | WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0); |
198 | S.getCurrentWinEpilog()->Instructions.push_back(x: Inst); |
199 | } |
200 | S.emitWinCFIEndEpilogue(); |
201 | } |
202 | |
203 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFITrapFrame() { |
204 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_TrapFrame, Reg: -1, Offset: 0); |
205 | } |
206 | |
207 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIMachineFrame() { |
208 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_PushMachFrame, Reg: -1, Offset: 0); |
209 | } |
210 | |
211 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIContext() { |
212 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_Context, Reg: -1, Offset: 0); |
213 | } |
214 | |
215 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIECContext() { |
216 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_ECContext, Reg: -1, Offset: 0); |
217 | } |
218 | |
219 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIClearUnwoundToCall() { |
220 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_ClearUnwoundToCall, Reg: -1, Offset: 0); |
221 | } |
222 | |
223 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIPACSignLR() { |
224 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_PACSignLR, Reg: -1, Offset: 0); |
225 | } |
226 | |
227 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegI(unsigned Reg, |
228 | int Offset) { |
229 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegI, Reg, Offset); |
230 | } |
231 | |
232 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIP(unsigned Reg, |
233 | int Offset) { |
234 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegIP, Reg, Offset); |
235 | } |
236 | |
237 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegD(unsigned Reg, |
238 | int Offset) { |
239 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegD, Reg, Offset); |
240 | } |
241 | |
242 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDP(unsigned Reg, |
243 | int Offset) { |
244 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegDP, Reg, Offset); |
245 | } |
246 | |
247 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQ(unsigned Reg, |
248 | int Offset) { |
249 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegQ, Reg, Offset); |
250 | } |
251 | |
252 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQP(unsigned Reg, |
253 | int Offset) { |
254 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegQP, Reg, Offset); |
255 | } |
256 | |
257 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIX(unsigned Reg, |
258 | int Offset) { |
259 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegIX, Reg, Offset); |
260 | } |
261 | |
262 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIPX(unsigned Reg, |
263 | int Offset) { |
264 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegIPX, Reg, Offset); |
265 | } |
266 | |
267 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDX(unsigned Reg, |
268 | int Offset) { |
269 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegDX, Reg, Offset); |
270 | } |
271 | |
272 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDPX(unsigned Reg, |
273 | int Offset) { |
274 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegDPX, Reg, Offset); |
275 | } |
276 | |
277 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQX(unsigned Reg, |
278 | int Offset) { |
279 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegQX, Reg, Offset); |
280 | } |
281 | |
282 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQPX(unsigned Reg, |
283 | int Offset) { |
284 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegQPX, Reg, Offset); |
285 | } |
286 | |
287 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIAllocZ(int Offset) { |
288 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_AllocZ, Reg: 0, Offset); |
289 | } |
290 | |
291 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveZReg(unsigned Reg, |
292 | int Offset) { |
293 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveZReg, Reg, Offset); |
294 | } |
295 | |
296 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISavePReg(unsigned Reg, |
297 | int Offset) { |
298 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SavePReg, Reg, Offset); |
299 | } |
300 | |
301 | MCStreamer * |
302 | llvm::createAArch64WinCOFFStreamer(MCContext &Context, |
303 | std::unique_ptr<MCAsmBackend> &&MAB, |
304 | std::unique_ptr<MCObjectWriter> &&OW, |
305 | std::unique_ptr<MCCodeEmitter> &&Emitter) { |
306 | return new AArch64WinCOFFStreamer(Context, std::move(MAB), std::move(Emitter), |
307 | std::move(OW)); |
308 | } |
309 | |