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 (InEpilogCFI) |
77 | CurFrame->EpilogMap[CurrentEpilog].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 | auto &S = getStreamer(); |
187 | WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(Loc: SMLoc()); |
188 | if (!CurFrame) |
189 | return; |
190 | |
191 | InEpilogCFI = true; |
192 | CurrentEpilog = S.emitCFILabel(); |
193 | } |
194 | |
195 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIEpilogEnd() { |
196 | auto &S = getStreamer(); |
197 | WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(Loc: 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(x: Inst); |
205 | MCSymbol *Label = S.emitCFILabel(); |
206 | CurFrame->EpilogMap[CurrentEpilog].End = Label; |
207 | CurrentEpilog = nullptr; |
208 | } |
209 | |
210 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFITrapFrame() { |
211 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_TrapFrame, Reg: -1, Offset: 0); |
212 | } |
213 | |
214 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIMachineFrame() { |
215 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_PushMachFrame, Reg: -1, Offset: 0); |
216 | } |
217 | |
218 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIContext() { |
219 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_Context, Reg: -1, Offset: 0); |
220 | } |
221 | |
222 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIECContext() { |
223 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_ECContext, Reg: -1, Offset: 0); |
224 | } |
225 | |
226 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIClearUnwoundToCall() { |
227 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_ClearUnwoundToCall, Reg: -1, Offset: 0); |
228 | } |
229 | |
230 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFIPACSignLR() { |
231 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_PACSignLR, Reg: -1, Offset: 0); |
232 | } |
233 | |
234 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegI(unsigned Reg, |
235 | int Offset) { |
236 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegI, Reg, Offset); |
237 | } |
238 | |
239 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIP(unsigned Reg, |
240 | int Offset) { |
241 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegIP, Reg, Offset); |
242 | } |
243 | |
244 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegD(unsigned Reg, |
245 | int Offset) { |
246 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegD, Reg, Offset); |
247 | } |
248 | |
249 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDP(unsigned Reg, |
250 | int Offset) { |
251 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegDP, Reg, Offset); |
252 | } |
253 | |
254 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQ(unsigned Reg, |
255 | int Offset) { |
256 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegQ, Reg, Offset); |
257 | } |
258 | |
259 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQP(unsigned Reg, |
260 | int Offset) { |
261 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegQP, Reg, Offset); |
262 | } |
263 | |
264 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIX(unsigned Reg, |
265 | int Offset) { |
266 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegIX, Reg, Offset); |
267 | } |
268 | |
269 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIPX(unsigned Reg, |
270 | int Offset) { |
271 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegIPX, Reg, Offset); |
272 | } |
273 | |
274 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDX(unsigned Reg, |
275 | int Offset) { |
276 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegDX, Reg, Offset); |
277 | } |
278 | |
279 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDPX(unsigned Reg, |
280 | int Offset) { |
281 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegDPX, Reg, Offset); |
282 | } |
283 | |
284 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQX(unsigned Reg, |
285 | int Offset) { |
286 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegQX, Reg, Offset); |
287 | } |
288 | |
289 | void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQPX(unsigned Reg, |
290 | int Offset) { |
291 | emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegQPX, Reg, Offset); |
292 | } |
293 | |
294 | MCWinCOFFStreamer * |
295 | llvm::createAArch64WinCOFFStreamer(MCContext &Context, |
296 | std::unique_ptr<MCAsmBackend> MAB, |
297 | std::unique_ptr<MCObjectWriter> OW, |
298 | std::unique_ptr<MCCodeEmitter> Emitter) { |
299 | return new AArch64WinCOFFStreamer(Context, std::move(MAB), std::move(Emitter), |
300 | std::move(OW)); |
301 | } |
302 | |