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