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