1 //===-- AArch64WinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "AArch64WinCOFFStreamer.h"
11 #include "llvm/MC/MCAsmBackend.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
17 using namespace llvm;
18
19 namespace {
20
21 class AArch64WinCOFFStreamer : public MCWinCOFFStreamer {
22 Win64EH::ARM64UnwindEmitter EHStreamer;
23
24 public:
AArch64WinCOFFStreamer(MCContext & C,std::unique_ptr<MCAsmBackend> AB,std::unique_ptr<MCCodeEmitter> CE,std::unique_ptr<MCObjectWriter> OW)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 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 }
42
EmitWindowsUnwindTables()43 void AArch64WinCOFFStreamer::EmitWindowsUnwindTables() {
44 if (!getNumWinFrameInfos())
45 return;
46 EHStreamer.Emit(*this);
47 }
48
FinishImpl()49 void AArch64WinCOFFStreamer::FinishImpl() {
50 EmitFrames(nullptr);
51 EmitWindowsUnwindTables();
52
53 MCWinCOFFStreamer::FinishImpl();
54 }
55 } // end anonymous namespace
56
57 namespace llvm {
58
59 // Helper function to common out unwind code setup for those codes that can
60 // belong to both prolog and epilog.
61 // There are three types of Windows ARM64 SEH codes. They can
62 // 1) take no operands: SEH_Nop, SEH_PrologEnd, SEH_EpilogStart, SEH_EpilogEnd
63 // 2) take an offset: SEH_StackAlloc, SEH_SaveFPLR, SEH_SaveFPLR_X
64 // 3) take a register and an offset/size: all others
EmitARM64WinUnwindCode(unsigned UnwindCode,int Reg,int Offset)65 void AArch64TargetWinCOFFStreamer::EmitARM64WinUnwindCode(unsigned UnwindCode,
66 int Reg,
67 int Offset) {
68 auto &S = getStreamer();
69 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
70 if (!CurFrame)
71 return;
72 MCSymbol *Label = S.EmitCFILabel();
73 auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset);
74 if (InEpilogCFI)
75 CurFrame->EpilogMap[CurrentEpilog].push_back(Inst);
76 else
77 CurFrame->Instructions.push_back(Inst);
78 }
79
EmitARM64WinCFIAllocStack(unsigned Size)80 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAllocStack(unsigned Size) {
81 unsigned Op = Win64EH::UOP_AllocSmall;
82 if (Size >= 16384)
83 Op = Win64EH::UOP_AllocLarge;
84 else if (Size >= 512)
85 Op = Win64EH::UOP_AllocMedium;
86 EmitARM64WinUnwindCode(Op, -1, Size);
87 }
88
EmitARM64WinCFISaveFPLR(int Offset)89 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLR(int Offset) {
90 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset);
91 }
92
EmitARM64WinCFISaveFPLRX(int Offset)93 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLRX(int Offset) {
94 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset);
95 }
96
EmitARM64WinCFISaveReg(unsigned Reg,int Offset)97 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveReg(unsigned Reg,
98 int Offset) {
99 assert(Offset >= 0 && Offset <= 504 &&
100 "Offset for save reg should be >= 0 && <= 504");
101 EmitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset);
102 }
103
EmitARM64WinCFISaveRegX(unsigned Reg,int Offset)104 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegX(unsigned Reg,
105 int Offset) {
106 EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset);
107 }
108
EmitARM64WinCFISaveRegP(unsigned Reg,int Offset)109 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegP(unsigned Reg,
110 int Offset) {
111 EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset);
112 }
113
EmitARM64WinCFISaveRegPX(unsigned Reg,int Offset)114 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegPX(unsigned Reg,
115 int Offset) {
116 EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset);
117 }
118
EmitARM64WinCFISaveFReg(unsigned Reg,int Offset)119 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFReg(unsigned Reg,
120 int Offset) {
121 assert(Offset >= 0 && Offset <= 504 &&
122 "Offset for save reg should be >= 0 && <= 504");
123 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset);
124 }
125
EmitARM64WinCFISaveFRegX(unsigned Reg,int Offset)126 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegX(unsigned Reg,
127 int Offset) {
128 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset);
129 }
130
EmitARM64WinCFISaveFRegP(unsigned Reg,int Offset)131 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegP(unsigned Reg,
132 int Offset) {
133 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset);
134 }
135
EmitARM64WinCFISaveFRegPX(unsigned Reg,int Offset)136 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegPX(unsigned Reg,
137 int Offset) {
138 EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset);
139 }
140
EmitARM64WinCFISetFP()141 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISetFP() {
142 EmitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0);
143 }
144
EmitARM64WinCFIAddFP(unsigned Offset)145 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAddFP(unsigned Offset) {
146 assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040");
147 EmitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset);
148 }
149
EmitARM64WinCFINop()150 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFINop() {
151 EmitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0);
152 }
153
154 // The functions below handle opcodes that can end up in either a prolog or
155 // an epilog, but not both.
EmitARM64WinCFIPrologEnd()156 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIPrologEnd() {
157 auto &S = getStreamer();
158 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
159 if (!CurFrame)
160 return;
161
162 MCSymbol *Label = S.EmitCFILabel();
163 CurFrame->PrologEnd = Label;
164 WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0);
165 auto it = CurFrame->Instructions.begin();
166 CurFrame->Instructions.insert(it, Inst);
167 }
168
EmitARM64WinCFIEpilogStart()169 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogStart() {
170 auto &S = getStreamer();
171 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
172 if (!CurFrame)
173 return;
174
175 InEpilogCFI = true;
176 CurrentEpilog = S.EmitCFILabel();
177 }
178
EmitARM64WinCFIEpilogEnd()179 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogEnd() {
180 auto &S = getStreamer();
181 WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
182 if (!CurFrame)
183 return;
184
185 InEpilogCFI = false;
186 MCSymbol *Label = S.EmitCFILabel();
187 WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0);
188 CurFrame->EpilogMap[CurrentEpilog].push_back(Inst);
189 CurrentEpilog = nullptr;
190 }
191
createAArch64WinCOFFStreamer(MCContext & Context,std::unique_ptr<MCAsmBackend> MAB,std::unique_ptr<MCObjectWriter> OW,std::unique_ptr<MCCodeEmitter> Emitter,bool RelaxAll,bool IncrementalLinkerCompatible)192 MCWinCOFFStreamer *createAArch64WinCOFFStreamer(
193 MCContext &Context, std::unique_ptr<MCAsmBackend> MAB,
194 std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter,
195 bool RelaxAll, bool IncrementalLinkerCompatible) {
196 auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB),
197 std::move(Emitter), std::move(OW));
198 S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
199 return S;
200 }
201
202 } // end llvm namespace
203