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 
EmitARM64WinCFISaveFPLR(int Offset)88 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLR(int Offset) {
89   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset);
90 }
91 
EmitARM64WinCFISaveFPLRX(int Offset)92 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLRX(int Offset) {
93   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset);
94 }
95 
EmitARM64WinCFISaveReg(unsigned Reg,int Offset)96 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveReg(unsigned Reg,
97                                                           int Offset) {
98   assert(Offset >= 0 && Offset <= 504 &&
99         "Offset for save reg should be >= 0 && <= 504");
100   EmitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset);
101 }
102 
EmitARM64WinCFISaveRegX(unsigned Reg,int Offset)103 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegX(unsigned Reg,
104                                                            int Offset) {
105   EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset);
106 }
107 
EmitARM64WinCFISaveRegP(unsigned Reg,int Offset)108 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegP(unsigned Reg,
109                                                            int Offset) {
110   EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset);
111 }
112 
EmitARM64WinCFISaveRegPX(unsigned Reg,int Offset)113 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegPX(unsigned Reg,
114                                                             int Offset) {
115   EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset);
116 }
117 
EmitARM64WinCFISaveFReg(unsigned Reg,int Offset)118 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFReg(unsigned Reg,
119                                                            int Offset) {
120   assert(Offset >= 0 && Offset <= 504 &&
121         "Offset for save reg should be >= 0 && <= 504");
122   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset);
123 }
124 
EmitARM64WinCFISaveFRegX(unsigned Reg,int Offset)125 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegX(unsigned Reg,
126                                                             int Offset) {
127   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset);
128 }
129 
EmitARM64WinCFISaveFRegP(unsigned Reg,int Offset)130 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegP(unsigned Reg,
131                                                             int Offset) {
132   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset);
133 }
134 
EmitARM64WinCFISaveFRegPX(unsigned Reg,int Offset)135 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegPX(unsigned Reg,
136                                                              int Offset) {
137   EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset);
138 }
139 
EmitARM64WinCFISetFP()140 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISetFP() {
141   EmitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0);
142 }
143 
EmitARM64WinCFIAddFP(unsigned Offset)144 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAddFP(unsigned Offset) {
145   assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040");
146   EmitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset);
147 }
148 
EmitARM64WinCFINop()149 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFINop() {
150   EmitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0);
151 }
152 
153 // The functions below handle opcodes that can end up in either a prolog or
154 // an epilog, but not both.
EmitARM64WinCFIPrologEnd()155 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIPrologEnd() {
156   auto &S = getStreamer();
157   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
158   if (!CurFrame)
159     return;
160 
161   MCSymbol *Label = S.emitCFILabel();
162   CurFrame->PrologEnd = Label;
163   WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0);
164   auto it = CurFrame->Instructions.begin();
165   CurFrame->Instructions.insert(it, Inst);
166 }
167 
EmitARM64WinCFIEpilogStart()168 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogStart() {
169   auto &S = getStreamer();
170   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
171   if (!CurFrame)
172     return;
173 
174   InEpilogCFI = true;
175   CurrentEpilog = S.emitCFILabel();
176 }
177 
EmitARM64WinCFIEpilogEnd()178 void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogEnd() {
179   auto &S = getStreamer();
180   WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
181   if (!CurFrame)
182     return;
183 
184   InEpilogCFI = false;
185   MCSymbol *Label = S.emitCFILabel();
186   WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0);
187   CurFrame->EpilogMap[CurrentEpilog].push_back(Inst);
188   CurrentEpilog = nullptr;
189 }
190 
createAArch64WinCOFFStreamer(MCContext & Context,std::unique_ptr<MCAsmBackend> MAB,std::unique_ptr<MCObjectWriter> OW,std::unique_ptr<MCCodeEmitter> Emitter,bool RelaxAll,bool IncrementalLinkerCompatible)191 MCWinCOFFStreamer *createAArch64WinCOFFStreamer(
192     MCContext &Context, std::unique_ptr<MCAsmBackend> MAB,
193     std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter,
194     bool RelaxAll, bool IncrementalLinkerCompatible) {
195   auto *S = new AArch64WinCOFFStreamer(Context, std::move(MAB),
196                                        std::move(Emitter), std::move(OW));
197   S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
198   return S;
199 }
200 
201 } // end llvm namespace
202