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/MCCodeEmitter.h"
12 #include "llvm/MC/MCObjectWriter.h"
13 #include "llvm/MC/MCWin64EH.h"
14 #include "llvm/MC/MCWinCOFFStreamer.h"
15 
16 using namespace llvm;
17 
18 namespace {
19 
20 class AArch64WinCOFFStreamer : public MCWinCOFFStreamer {
21   Win64EH::ARM64UnwindEmitter EHStreamer;
22 
23 public:
24   AArch64WinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB,
25                          std::unique_ptr<MCCodeEmitter> CE,
26                          std::unique_ptr<MCObjectWriter> OW)
27       : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {}
28 
29   void EmitWinEHHandlerData(SMLoc Loc) override;
30   void EmitWindowsUnwindTables() override;
31   void EmitWindowsUnwindTables(WinEH::FrameInfo *Frame) override;
32   void finishImpl() override;
33 };
34 
35 void AArch64WinCOFFStreamer::EmitWinEHHandlerData(SMLoc Loc) {
36   MCStreamer::EmitWinEHHandlerData(Loc);
37 
38   // We have to emit the unwind info now, because this directive
39   // actually switches to the .xdata section!
40   EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(),
41                             /* HandlerData = */ true);
42 }
43 
44 void AArch64WinCOFFStreamer::EmitWindowsUnwindTables(WinEH::FrameInfo *Frame) {
45   EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false);
46 }
47 
48 void AArch64WinCOFFStreamer::EmitWindowsUnwindTables() {
49   if (!getNumWinFrameInfos())
50     return;
51   EHStreamer.Emit(*this);
52 }
53 
54 void AArch64WinCOFFStreamer::finishImpl() {
55   emitFrames(nullptr);
56   EmitWindowsUnwindTables();
57 
58   MCWinCOFFStreamer::finishImpl();
59 }
60 } // end anonymous namespace
61 
62 namespace llvm {
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
70 void AArch64TargetWinCOFFStreamer::EmitARM64WinUnwindCode(unsigned UnwindCode,
71                                                           int Reg,
72                                                           int Offset) {
73   auto &S = getStreamer();
74   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
75   if (!CurFrame)
76     return;
77   MCSymbol *Label = S.emitCFILabel();
78   auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset);
79   if (InEpilogCFI)
80     CurFrame->EpilogMap[CurrentEpilog].push_back(Inst);
81   else
82     CurFrame->Instructions.push_back(Inst);
83 }
84 
85 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAllocStack(unsigned Size) {
86   unsigned Op = Win64EH::UOP_AllocSmall;
87   if (Size >= 16384)
88     Op = Win64EH::UOP_AllocLarge;
89   else if (Size >= 512)
90     Op = Win64EH::UOP_AllocMedium;
91   EmitARM64WinUnwindCode(Op, -1, Size);
92 }
93 
94 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveR19R20X(int Offset) {
95   EmitARM64WinUnwindCode(Win64EH::UOP_SaveR19R20X, -1, Offset);
96 }
97 
98 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLR(int Offset) {
99   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset);
100 }
101 
102 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLRX(int Offset) {
103   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset);
104 }
105 
106 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveReg(unsigned Reg,
107                                                           int Offset) {
108   assert(Offset >= 0 && Offset <= 504 &&
109         "Offset for save reg should be >= 0 && <= 504");
110   EmitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset);
111 }
112 
113 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegX(unsigned Reg,
114                                                            int Offset) {
115   EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset);
116 }
117 
118 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegP(unsigned Reg,
119                                                            int Offset) {
120   EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset);
121 }
122 
123 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegPX(unsigned Reg,
124                                                             int Offset) {
125   EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset);
126 }
127 
128 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveLRPair(unsigned Reg,
129                                                              int Offset) {
130   EmitARM64WinUnwindCode(Win64EH::UOP_SaveLRPair, Reg, Offset);
131 }
132 
133 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFReg(unsigned Reg,
134                                                            int Offset) {
135   assert(Offset >= 0 && Offset <= 504 &&
136         "Offset for save reg should be >= 0 && <= 504");
137   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset);
138 }
139 
140 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegX(unsigned Reg,
141                                                             int Offset) {
142   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset);
143 }
144 
145 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegP(unsigned Reg,
146                                                             int Offset) {
147   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset);
148 }
149 
150 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegPX(unsigned Reg,
151                                                              int Offset) {
152   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset);
153 }
154 
155 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISetFP() {
156   EmitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0);
157 }
158 
159 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAddFP(unsigned Offset) {
160   assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040");
161   EmitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset);
162 }
163 
164 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFINop() {
165   EmitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0);
166 }
167 
168 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveNext() {
169   EmitARM64WinUnwindCode(Win64EH::UOP_SaveNext, -1, 0);
170 }
171 
172 // The functions below handle opcodes that can end up in either a prolog or
173 // an epilog, but not both.
174 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIPrologEnd() {
175   auto &S = getStreamer();
176   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
177   if (!CurFrame)
178     return;
179 
180   MCSymbol *Label = S.emitCFILabel();
181   CurFrame->PrologEnd = Label;
182   WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0);
183   auto it = CurFrame->Instructions.begin();
184   CurFrame->Instructions.insert(it, Inst);
185 }
186 
187 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogStart() {
188   auto &S = getStreamer();
189   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
190   if (!CurFrame)
191     return;
192 
193   InEpilogCFI = true;
194   CurrentEpilog = S.emitCFILabel();
195 }
196 
197 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogEnd() {
198   auto &S = getStreamer();
199   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
200   if (!CurFrame)
201     return;
202 
203   InEpilogCFI = false;
204   MCSymbol *Label = S.emitCFILabel();
205   WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0);
206   CurFrame->EpilogMap[CurrentEpilog].push_back(Inst);
207   CurrentEpilog = nullptr;
208 }
209 
210 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFITrapFrame() {
211   EmitARM64WinUnwindCode(Win64EH::UOP_TrapFrame, -1, 0);
212 }
213 
214 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIMachineFrame() {
215   EmitARM64WinUnwindCode(Win64EH::UOP_PushMachFrame, -1, 0);
216 }
217 
218 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIContext() {
219   EmitARM64WinUnwindCode(Win64EH::UOP_Context, -1, 0);
220 }
221 
222 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIClearUnwoundToCall() {
223   EmitARM64WinUnwindCode(Win64EH::UOP_ClearUnwoundToCall, -1, 0);
224 }
225 
226 MCWinCOFFStreamer *createAArch64WinCOFFStreamer(
227     MCContext &Context, std::unique_ptr<MCAsmBackend> MAB,
228     std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter,
229     bool RelaxAll, bool IncrementalLinkerCompatible) {
230   auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB),
231                                        std::move(Emitter), std::move(OW));
232   S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
233   return S;
234 }
235 
236 } // end llvm namespace
237