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(*this, getCurrentWinFrameInfo(),
42                            /* HandlerData = */ true);
43}
44
45void AArch64WinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) {
46  EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false);
47}
48
49void AArch64WinCOFFStreamer::emitWindowsUnwindTables() {
50  if (!getNumWinFrameInfos())
51    return;
52  EHStreamer.Emit(*this);
53}
54
55void 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
69void 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
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(Op, -1, Size);
89}
90
91void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveR19R20X(int Offset) {
92  emitARM64WinUnwindCode(Win64EH::UOP_SaveR19R20X, -1, Offset);
93}
94
95void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFPLR(int Offset) {
96  emitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset);
97}
98
99void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFPLRX(int Offset) {
100  emitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -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(Win64EH::UOP_SaveReg, Reg, Offset);
108}
109
110void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegX(unsigned Reg,
111                                                           int Offset) {
112  emitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset);
113}
114
115void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegP(unsigned Reg,
116                                                           int Offset) {
117  emitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset);
118}
119
120void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegPX(unsigned Reg,
121                                                            int Offset) {
122  emitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset);
123}
124
125void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveLRPair(unsigned Reg,
126                                                             int Offset) {
127  emitARM64WinUnwindCode(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(Win64EH::UOP_SaveFReg, Reg, Offset);
135}
136
137void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegX(unsigned Reg,
138                                                            int Offset) {
139  emitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset);
140}
141
142void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegP(unsigned Reg,
143                                                            int Offset) {
144  emitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset);
145}
146
147void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegPX(unsigned Reg,
148                                                             int Offset) {
149  emitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset);
150}
151
152void AArch64TargetWinCOFFStreamer::emitARM64WinCFISetFP() {
153  emitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0);
154}
155
156void AArch64TargetWinCOFFStreamer::emitARM64WinCFIAddFP(unsigned Offset) {
157  assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040");
158  emitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset);
159}
160
161void AArch64TargetWinCOFFStreamer::emitARM64WinCFINop() {
162  emitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0);
163}
164
165void 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.
171void 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
185void 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
195void 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
210void AArch64TargetWinCOFFStreamer::emitARM64WinCFITrapFrame() {
211  emitARM64WinUnwindCode(Win64EH::UOP_TrapFrame, -1, 0);
212}
213
214void AArch64TargetWinCOFFStreamer::emitARM64WinCFIMachineFrame() {
215  emitARM64WinUnwindCode(Win64EH::UOP_PushMachFrame, -1, 0);
216}
217
218void AArch64TargetWinCOFFStreamer::emitARM64WinCFIContext() {
219  emitARM64WinUnwindCode(Win64EH::UOP_Context, -1, 0);
220}
221
222void AArch64TargetWinCOFFStreamer::emitARM64WinCFIClearUnwoundToCall() {
223  emitARM64WinUnwindCode(Win64EH::UOP_ClearUnwoundToCall, -1, 0);
224}
225
226void AArch64TargetWinCOFFStreamer::emitARM64WinCFIPACSignLR() {
227  emitARM64WinUnwindCode(Win64EH::UOP_PACSignLR, -1, 0);
228}
229
230void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegI(unsigned Reg,
231                                                              int Offset) {
232  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegI, Reg, Offset);
233}
234
235void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIP(unsigned Reg,
236                                                               int Offset) {
237  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegIP, Reg, Offset);
238}
239
240void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegD(unsigned Reg,
241                                                              int Offset) {
242  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegD, Reg, Offset);
243}
244
245void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDP(unsigned Reg,
246                                                               int Offset) {
247  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegDP, Reg, Offset);
248}
249
250void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQ(unsigned Reg,
251                                                              int Offset) {
252  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegQ, Reg, Offset);
253}
254
255void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQP(unsigned Reg,
256                                                               int Offset) {
257  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegQP, Reg, Offset);
258}
259
260void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIX(unsigned Reg,
261                                                               int Offset) {
262  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegIX, Reg, Offset);
263}
264
265void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIPX(unsigned Reg,
266                                                                int Offset) {
267  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegIPX, Reg, Offset);
268}
269
270void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDX(unsigned Reg,
271                                                               int Offset) {
272  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegDX, Reg, Offset);
273}
274
275void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDPX(unsigned Reg,
276                                                                int Offset) {
277  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegDPX, Reg, Offset);
278}
279
280void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQX(unsigned Reg,
281                                                               int Offset) {
282  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegQX, Reg, Offset);
283}
284
285void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQPX(unsigned Reg,
286                                                                int Offset) {
287  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegQPX, Reg, Offset);
288}
289
290MCWinCOFFStreamer *llvm::createAArch64WinCOFFStreamer(
291    MCContext &Context, std::unique_ptr<MCAsmBackend> MAB,
292    std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter,
293    bool RelaxAll, bool IncrementalLinkerCompatible) {
294  auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB),
295                                       std::move(Emitter), std::move(OW));
296  S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
297  return S;
298}
299