xref: /netbsd-src/external/apache2/llvm/dist/llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp (revision 82d56013d7b633d116a93943de88e08335357a7c)
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,
70                                                           int Offset) {
71   auto &S = getStreamer();
72   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
73   if (!CurFrame)
74     return;
75   MCSymbol *Label = S.emitCFILabel();
76   auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset);
77   if (InEpilogCFI)
78     CurFrame->EpilogMap[CurrentEpilog].push_back(Inst);
79   else
80     CurFrame->Instructions.push_back(Inst);
81 }
82 
EmitARM64WinCFIAllocStack(unsigned Size)83 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAllocStack(unsigned Size) {
84   unsigned Op = Win64EH::UOP_AllocSmall;
85   if (Size >= 16384)
86     Op = Win64EH::UOP_AllocLarge;
87   else if (Size >= 512)
88     Op = Win64EH::UOP_AllocMedium;
89   EmitARM64WinUnwindCode(Op, -1, Size);
90 }
91 
EmitARM64WinCFISaveR19R20X(int Offset)92 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveR19R20X(int Offset) {
93   EmitARM64WinUnwindCode(Win64EH::UOP_SaveR19R20X, -1, Offset);
94 }
95 
EmitARM64WinCFISaveFPLR(int Offset)96 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLR(int Offset) {
97   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset);
98 }
99 
EmitARM64WinCFISaveFPLRX(int Offset)100 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLRX(int Offset) {
101   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset);
102 }
103 
EmitARM64WinCFISaveReg(unsigned Reg,int Offset)104 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveReg(unsigned Reg,
105                                                           int Offset) {
106   assert(Offset >= 0 && Offset <= 504 &&
107         "Offset for save reg should be >= 0 && <= 504");
108   EmitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset);
109 }
110 
EmitARM64WinCFISaveRegX(unsigned Reg,int Offset)111 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegX(unsigned Reg,
112                                                            int Offset) {
113   EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset);
114 }
115 
EmitARM64WinCFISaveRegP(unsigned Reg,int Offset)116 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegP(unsigned Reg,
117                                                            int Offset) {
118   EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset);
119 }
120 
EmitARM64WinCFISaveRegPX(unsigned Reg,int Offset)121 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegPX(unsigned Reg,
122                                                             int Offset) {
123   EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset);
124 }
125 
EmitARM64WinCFISaveLRPair(unsigned Reg,int Offset)126 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveLRPair(unsigned Reg,
127                                                              int Offset) {
128   EmitARM64WinUnwindCode(Win64EH::UOP_SaveLRPair, Reg, Offset);
129 }
130 
EmitARM64WinCFISaveFReg(unsigned Reg,int Offset)131 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFReg(unsigned Reg,
132                                                            int Offset) {
133   assert(Offset >= 0 && Offset <= 504 &&
134         "Offset for save reg should be >= 0 && <= 504");
135   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset);
136 }
137 
EmitARM64WinCFISaveFRegX(unsigned Reg,int Offset)138 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegX(unsigned Reg,
139                                                             int Offset) {
140   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset);
141 }
142 
EmitARM64WinCFISaveFRegP(unsigned Reg,int Offset)143 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegP(unsigned Reg,
144                                                             int Offset) {
145   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset);
146 }
147 
EmitARM64WinCFISaveFRegPX(unsigned Reg,int Offset)148 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegPX(unsigned Reg,
149                                                              int Offset) {
150   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset);
151 }
152 
EmitARM64WinCFISetFP()153 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISetFP() {
154   EmitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0);
155 }
156 
EmitARM64WinCFIAddFP(unsigned Offset)157 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAddFP(unsigned Offset) {
158   assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040");
159   EmitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset);
160 }
161 
EmitARM64WinCFINop()162 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFINop() {
163   EmitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0);
164 }
165 
EmitARM64WinCFISaveNext()166 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveNext() {
167   EmitARM64WinUnwindCode(Win64EH::UOP_SaveNext, -1, 0);
168 }
169 
170 // The functions below handle opcodes that can end up in either a prolog or
171 // an epilog, but not both.
EmitARM64WinCFIPrologEnd()172 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIPrologEnd() {
173   auto &S = getStreamer();
174   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
175   if (!CurFrame)
176     return;
177 
178   MCSymbol *Label = S.emitCFILabel();
179   CurFrame->PrologEnd = Label;
180   WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0);
181   auto it = CurFrame->Instructions.begin();
182   CurFrame->Instructions.insert(it, Inst);
183 }
184 
EmitARM64WinCFIEpilogStart()185 void 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 
EmitARM64WinCFIEpilogEnd()195 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogEnd() {
196   auto &S = getStreamer();
197   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
198   if (!CurFrame)
199     return;
200 
201   InEpilogCFI = false;
202   MCSymbol *Label = S.emitCFILabel();
203   WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0);
204   CurFrame->EpilogMap[CurrentEpilog].push_back(Inst);
205   CurrentEpilog = nullptr;
206 }
207 
EmitARM64WinCFITrapFrame()208 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFITrapFrame() {
209   EmitARM64WinUnwindCode(Win64EH::UOP_TrapFrame, -1, 0);
210 }
211 
EmitARM64WinCFIMachineFrame()212 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIMachineFrame() {
213   EmitARM64WinUnwindCode(Win64EH::UOP_PushMachFrame, -1, 0);
214 }
215 
EmitARM64WinCFIContext()216 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIContext() {
217   EmitARM64WinUnwindCode(Win64EH::UOP_Context, -1, 0);
218 }
219 
EmitARM64WinCFIClearUnwoundToCall()220 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIClearUnwoundToCall() {
221   EmitARM64WinUnwindCode(Win64EH::UOP_ClearUnwoundToCall, -1, 0);
222 }
223 
createAArch64WinCOFFStreamer(MCContext & Context,std::unique_ptr<MCAsmBackend> MAB,std::unique_ptr<MCObjectWriter> OW,std::unique_ptr<MCCodeEmitter> Emitter,bool RelaxAll,bool IncrementalLinkerCompatible)224 MCWinCOFFStreamer *llvm::createAArch64WinCOFFStreamer(
225     MCContext &Context, std::unique_ptr<MCAsmBackend> MAB,
226     std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter,
227     bool RelaxAll, bool IncrementalLinkerCompatible) {
228   auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB),
229                                        std::move(Emitter), std::move(OW));
230   S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
231   return S;
232 }
233