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