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
17using namespace llvm;
18
19namespace {
20
21class AArch64WinCOFFStreamer : public MCWinCOFFStreamer {
22 Win64EH::ARM64UnwindEmitter EHStreamer;
23
24public:
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
36void 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
45void AArch64WinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) {
46 EHStreamer.EmitUnwindInfo(Streamer&: *this, FI: Frame, /* HandlerData = */ false);
47}
48
49void AArch64WinCOFFStreamer::emitWindowsUnwindTables() {
50 if (!getNumWinFrameInfos())
51 return;
52 EHStreamer.Emit(Streamer&: *this);
53}
54
55void 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
69void 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
82void 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
91void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveR19R20X(int Offset) {
92 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveR19R20X, Reg: -1, Offset);
93}
94
95void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFPLR(int Offset) {
96 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveFPLR, Reg: -1, Offset);
97}
98
99void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFPLRX(int Offset) {
100 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveFPLRX, Reg: -1, Offset);
101}
102
103void 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
110void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegX(unsigned Reg,
111 int Offset) {
112 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveRegX, Reg, Offset);
113}
114
115void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegP(unsigned Reg,
116 int Offset) {
117 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveRegP, Reg, Offset);
118}
119
120void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegPX(unsigned Reg,
121 int Offset) {
122 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveRegPX, Reg, Offset);
123}
124
125void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveLRPair(unsigned Reg,
126 int Offset) {
127 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveLRPair, Reg, Offset);
128}
129
130void 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
137void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegX(unsigned Reg,
138 int Offset) {
139 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveFRegX, Reg, Offset);
140}
141
142void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegP(unsigned Reg,
143 int Offset) {
144 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveFRegP, Reg, Offset);
145}
146
147void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegPX(unsigned Reg,
148 int Offset) {
149 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveFRegPX, Reg, Offset);
150}
151
152void AArch64TargetWinCOFFStreamer::emitARM64WinCFISetFP() {
153 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SetFP, Reg: -1, Offset: 0);
154}
155
156void 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
161void AArch64TargetWinCOFFStreamer::emitARM64WinCFINop() {
162 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_Nop, Reg: -1, Offset: 0);
163}
164
165void 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.
171void 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
185void AArch64TargetWinCOFFStreamer::emitARM64WinCFIEpilogStart() {
186 getStreamer().emitWinCFIBeginEpilogue();
187}
188
189void 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
203void AArch64TargetWinCOFFStreamer::emitARM64WinCFITrapFrame() {
204 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_TrapFrame, Reg: -1, Offset: 0);
205}
206
207void AArch64TargetWinCOFFStreamer::emitARM64WinCFIMachineFrame() {
208 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_PushMachFrame, Reg: -1, Offset: 0);
209}
210
211void AArch64TargetWinCOFFStreamer::emitARM64WinCFIContext() {
212 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_Context, Reg: -1, Offset: 0);
213}
214
215void AArch64TargetWinCOFFStreamer::emitARM64WinCFIECContext() {
216 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_ECContext, Reg: -1, Offset: 0);
217}
218
219void AArch64TargetWinCOFFStreamer::emitARM64WinCFIClearUnwoundToCall() {
220 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_ClearUnwoundToCall, Reg: -1, Offset: 0);
221}
222
223void AArch64TargetWinCOFFStreamer::emitARM64WinCFIPACSignLR() {
224 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_PACSignLR, Reg: -1, Offset: 0);
225}
226
227void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegI(unsigned Reg,
228 int Offset) {
229 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegI, Reg, Offset);
230}
231
232void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIP(unsigned Reg,
233 int Offset) {
234 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegIP, Reg, Offset);
235}
236
237void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegD(unsigned Reg,
238 int Offset) {
239 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegD, Reg, Offset);
240}
241
242void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDP(unsigned Reg,
243 int Offset) {
244 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegDP, Reg, Offset);
245}
246
247void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQ(unsigned Reg,
248 int Offset) {
249 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegQ, Reg, Offset);
250}
251
252void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQP(unsigned Reg,
253 int Offset) {
254 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegQP, Reg, Offset);
255}
256
257void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIX(unsigned Reg,
258 int Offset) {
259 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegIX, Reg, Offset);
260}
261
262void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIPX(unsigned Reg,
263 int Offset) {
264 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegIPX, Reg, Offset);
265}
266
267void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDX(unsigned Reg,
268 int Offset) {
269 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegDX, Reg, Offset);
270}
271
272void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDPX(unsigned Reg,
273 int Offset) {
274 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegDPX, Reg, Offset);
275}
276
277void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQX(unsigned Reg,
278 int Offset) {
279 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegQX, Reg, Offset);
280}
281
282void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQPX(unsigned Reg,
283 int Offset) {
284 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveAnyRegQPX, Reg, Offset);
285}
286
287void AArch64TargetWinCOFFStreamer::emitARM64WinCFIAllocZ(int Offset) {
288 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_AllocZ, Reg: 0, Offset);
289}
290
291void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveZReg(unsigned Reg,
292 int Offset) {
293 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SaveZReg, Reg, Offset);
294}
295
296void AArch64TargetWinCOFFStreamer::emitARM64WinCFISavePReg(unsigned Reg,
297 int Offset) {
298 emitARM64WinUnwindCode(UnwindCode: Win64EH::UOP_SavePReg, Reg, Offset);
299}
300
301MCStreamer *
302llvm::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