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:
AArch64WinCOFFStreamer(MCContext & C,std::unique_ptr<MCAsmBackend> AB,std::unique_ptr<MCCodeEmitter> CE,std::unique_ptr<MCObjectWriter> OW)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
EmitWinEHHandlerData(SMLoc Loc)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
EmitWindowsUnwindTables(WinEH::FrameInfo * Frame)44 void AArch64WinCOFFStreamer::EmitWindowsUnwindTables(WinEH::FrameInfo *Frame) {
45 EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false);
46 }
47
EmitWindowsUnwindTables()48 void AArch64WinCOFFStreamer::EmitWindowsUnwindTables() {
49 if (!getNumWinFrameInfos())
50 return;
51 EHStreamer.Emit(*this);
52 }
53
finishImpl()54 void AArch64WinCOFFStreamer::finishImpl() {
55 emitFrames(nullptr);
56 EmitWindowsUnwindTables();
57
58 MCWinCOFFStreamer::finishImpl();
59 }
60 } // end anonymous namespace
61
62 // Helper function to common out unwind code setup for those codes that can
63 // belong to both prolog and epilog.
64 // There are three types of Windows ARM64 SEH codes. They can
65 // 1) take no operands: SEH_Nop, SEH_PrologEnd, SEH_EpilogStart, SEH_EpilogEnd
66 // 2) take an offset: SEH_StackAlloc, SEH_SaveFPLR, SEH_SaveFPLR_X
67 // 3) take a register and an offset/size: all others
emitARM64WinUnwindCode(unsigned UnwindCode,int Reg,int Offset)68 void AArch64TargetWinCOFFStreamer::emitARM64WinUnwindCode(unsigned UnwindCode,
69 int Reg, int Offset) {
70 auto &S = getStreamer();
71 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
72 if (!CurFrame)
73 return;
74 MCSymbol *Label = S.emitCFILabel();
75 auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset);
76 if (InEpilogCFI)
77 CurFrame->EpilogMap[CurrentEpilog].push_back(Inst);
78 else
79 CurFrame->Instructions.push_back(Inst);
80 }
81
emitARM64WinCFIAllocStack(unsigned Size)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(Op, -1, Size);
89 }
90
emitARM64WinCFISaveR19R20X(int Offset)91 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveR19R20X(int Offset) {
92 emitARM64WinUnwindCode(Win64EH::UOP_SaveR19R20X, -1, Offset);
93 }
94
emitARM64WinCFISaveFPLR(int Offset)95 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFPLR(int Offset) {
96 emitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset);
97 }
98
emitARM64WinCFISaveFPLRX(int Offset)99 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFPLRX(int Offset) {
100 emitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset);
101 }
102
emitARM64WinCFISaveReg(unsigned Reg,int Offset)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(Win64EH::UOP_SaveReg, Reg, Offset);
108 }
109
emitARM64WinCFISaveRegX(unsigned Reg,int Offset)110 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegX(unsigned Reg,
111 int Offset) {
112 emitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset);
113 }
114
emitARM64WinCFISaveRegP(unsigned Reg,int Offset)115 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegP(unsigned Reg,
116 int Offset) {
117 emitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset);
118 }
119
emitARM64WinCFISaveRegPX(unsigned Reg,int Offset)120 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveRegPX(unsigned Reg,
121 int Offset) {
122 emitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset);
123 }
124
emitARM64WinCFISaveLRPair(unsigned Reg,int Offset)125 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveLRPair(unsigned Reg,
126 int Offset) {
127 emitARM64WinUnwindCode(Win64EH::UOP_SaveLRPair, Reg, Offset);
128 }
129
emitARM64WinCFISaveFReg(unsigned Reg,int Offset)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(Win64EH::UOP_SaveFReg, Reg, Offset);
135 }
136
emitARM64WinCFISaveFRegX(unsigned Reg,int Offset)137 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegX(unsigned Reg,
138 int Offset) {
139 emitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset);
140 }
141
emitARM64WinCFISaveFRegP(unsigned Reg,int Offset)142 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegP(unsigned Reg,
143 int Offset) {
144 emitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset);
145 }
146
emitARM64WinCFISaveFRegPX(unsigned Reg,int Offset)147 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveFRegPX(unsigned Reg,
148 int Offset) {
149 emitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset);
150 }
151
emitARM64WinCFISetFP()152 void AArch64TargetWinCOFFStreamer::emitARM64WinCFISetFP() {
153 emitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0);
154 }
155
emitARM64WinCFIAddFP(unsigned Offset)156 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIAddFP(unsigned Offset) {
157 assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040");
158 emitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset);
159 }
160
emitARM64WinCFINop()161 void AArch64TargetWinCOFFStreamer::emitARM64WinCFINop() {
162 emitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0);
163 }
164
emitARM64WinCFISaveNext()165 void 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.
emitARM64WinCFIPrologEnd()171 void 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 = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0);
180 auto it = CurFrame->Instructions.begin();
181 CurFrame->Instructions.insert(it, Inst);
182 }
183
emitARM64WinCFIEpilogStart()184 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIEpilogStart() {
185 auto &S = getStreamer();
186 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
187 if (!CurFrame)
188 return;
189
190 InEpilogCFI = true;
191 CurrentEpilog = S.emitCFILabel();
192 }
193
emitARM64WinCFIEpilogEnd()194 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIEpilogEnd() {
195 auto &S = getStreamer();
196 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
197 if (!CurFrame)
198 return;
199
200 InEpilogCFI = false;
201 MCSymbol *Label = S.emitCFILabel();
202 WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0);
203 CurFrame->EpilogMap[CurrentEpilog].push_back(Inst);
204 CurrentEpilog = nullptr;
205 }
206
emitARM64WinCFITrapFrame()207 void AArch64TargetWinCOFFStreamer::emitARM64WinCFITrapFrame() {
208 emitARM64WinUnwindCode(Win64EH::UOP_TrapFrame, -1, 0);
209 }
210
emitARM64WinCFIMachineFrame()211 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIMachineFrame() {
212 emitARM64WinUnwindCode(Win64EH::UOP_PushMachFrame, -1, 0);
213 }
214
emitARM64WinCFIContext()215 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIContext() {
216 emitARM64WinUnwindCode(Win64EH::UOP_Context, -1, 0);
217 }
218
emitARM64WinCFIClearUnwoundToCall()219 void AArch64TargetWinCOFFStreamer::emitARM64WinCFIClearUnwoundToCall() {
220 emitARM64WinUnwindCode(Win64EH::UOP_ClearUnwoundToCall, -1, 0);
221 }
222
createAArch64WinCOFFStreamer(MCContext & Context,std::unique_ptr<MCAsmBackend> MAB,std::unique_ptr<MCObjectWriter> OW,std::unique_ptr<MCCodeEmitter> Emitter,bool RelaxAll,bool IncrementalLinkerCompatible)223 MCWinCOFFStreamer *llvm::createAArch64WinCOFFStreamer(
224 MCContext &Context, std::unique_ptr<MCAsmBackend> MAB,
225 std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter,
226 bool RelaxAll, bool IncrementalLinkerCompatible) {
227 auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB),
228 std::move(Emitter), std::move(OW));
229 S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
230 return S;
231 }
232