109467b48Spatrick //===-- ARMWinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick
909467b48Spatrick #include "ARMMCTargetDesc.h"
1009467b48Spatrick #include "llvm/MC/MCAsmBackend.h"
11*d415bd75Srobert #include "llvm/MC/MCAssembler.h"
1209467b48Spatrick #include "llvm/MC/MCCodeEmitter.h"
13*d415bd75Srobert #include "llvm/MC/MCContext.h"
1409467b48Spatrick #include "llvm/MC/MCObjectWriter.h"
15*d415bd75Srobert #include "llvm/MC/MCWin64EH.h"
1609467b48Spatrick #include "llvm/MC/MCWinCOFFStreamer.h"
1709467b48Spatrick
1809467b48Spatrick using namespace llvm;
1909467b48Spatrick
2009467b48Spatrick namespace {
2109467b48Spatrick class ARMWinCOFFStreamer : public MCWinCOFFStreamer {
22*d415bd75Srobert Win64EH::ARMUnwindEmitter EHStreamer;
23*d415bd75Srobert
2409467b48Spatrick public:
ARMWinCOFFStreamer(MCContext & C,std::unique_ptr<MCAsmBackend> AB,std::unique_ptr<MCCodeEmitter> CE,std::unique_ptr<MCObjectWriter> OW)2509467b48Spatrick ARMWinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB,
2609467b48Spatrick std::unique_ptr<MCCodeEmitter> CE,
2709467b48Spatrick std::unique_ptr<MCObjectWriter> OW)
2809467b48Spatrick : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {}
2909467b48Spatrick
30*d415bd75Srobert void emitWinEHHandlerData(SMLoc Loc) override;
31*d415bd75Srobert void emitWindowsUnwindTables() override;
32*d415bd75Srobert void emitWindowsUnwindTables(WinEH::FrameInfo *Frame) override;
33*d415bd75Srobert
34097a140dSpatrick void emitThumbFunc(MCSymbol *Symbol) override;
35097a140dSpatrick void finishImpl() override;
3609467b48Spatrick };
3709467b48Spatrick
emitWinEHHandlerData(SMLoc Loc)38*d415bd75Srobert void ARMWinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) {
39*d415bd75Srobert MCStreamer::emitWinEHHandlerData(Loc);
40*d415bd75Srobert
41*d415bd75Srobert // We have to emit the unwind info now, because this directive
42*d415bd75Srobert // actually switches to the .xdata section!
43*d415bd75Srobert EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(),
44*d415bd75Srobert /* HandlerData = */ true);
45*d415bd75Srobert }
46*d415bd75Srobert
emitWindowsUnwindTables(WinEH::FrameInfo * Frame)47*d415bd75Srobert void ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) {
48*d415bd75Srobert EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false);
49*d415bd75Srobert }
50*d415bd75Srobert
emitWindowsUnwindTables()51*d415bd75Srobert void ARMWinCOFFStreamer::emitWindowsUnwindTables() {
52*d415bd75Srobert if (!getNumWinFrameInfos())
53*d415bd75Srobert return;
54*d415bd75Srobert EHStreamer.Emit(*this);
55*d415bd75Srobert }
56*d415bd75Srobert
emitThumbFunc(MCSymbol * Symbol)57097a140dSpatrick void ARMWinCOFFStreamer::emitThumbFunc(MCSymbol *Symbol) {
5809467b48Spatrick getAssembler().setIsThumbFunc(Symbol);
5909467b48Spatrick }
6009467b48Spatrick
finishImpl()61097a140dSpatrick void ARMWinCOFFStreamer::finishImpl() {
62097a140dSpatrick emitFrames(nullptr);
63*d415bd75Srobert emitWindowsUnwindTables();
6409467b48Spatrick
65097a140dSpatrick MCWinCOFFStreamer::finishImpl();
6609467b48Spatrick }
6709467b48Spatrick }
6809467b48Spatrick
createARMWinCOFFStreamer(MCContext & Context,std::unique_ptr<MCAsmBackend> && MAB,std::unique_ptr<MCObjectWriter> && OW,std::unique_ptr<MCCodeEmitter> && Emitter,bool RelaxAll,bool IncrementalLinkerCompatible)6909467b48Spatrick MCStreamer *llvm::createARMWinCOFFStreamer(
7009467b48Spatrick MCContext &Context, std::unique_ptr<MCAsmBackend> &&MAB,
7109467b48Spatrick std::unique_ptr<MCObjectWriter> &&OW,
7209467b48Spatrick std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll,
7309467b48Spatrick bool IncrementalLinkerCompatible) {
7409467b48Spatrick auto *S = new ARMWinCOFFStreamer(Context, std::move(MAB), std::move(Emitter),
7509467b48Spatrick std::move(OW));
7609467b48Spatrick S->getAssembler().setIncrementalLinkerCompatible(IncrementalLinkerCompatible);
7709467b48Spatrick return S;
7809467b48Spatrick }
7909467b48Spatrick
80*d415bd75Srobert namespace {
81*d415bd75Srobert class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer {
82*d415bd75Srobert private:
83*d415bd75Srobert // True if we are processing SEH directives in an epilogue.
84*d415bd75Srobert bool InEpilogCFI = false;
85*d415bd75Srobert
86*d415bd75Srobert // Symbol of the current epilog for which we are processing SEH directives.
87*d415bd75Srobert MCSymbol *CurrentEpilog = nullptr;
88*d415bd75Srobert
89*d415bd75Srobert public:
ARMTargetWinCOFFStreamer(llvm::MCStreamer & S)90*d415bd75Srobert ARMTargetWinCOFFStreamer(llvm::MCStreamer &S) : ARMTargetStreamer(S) {}
91*d415bd75Srobert
92*d415bd75Srobert // The unwind codes on ARM Windows are documented at
93*d415bd75Srobert // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
94*d415bd75Srobert void emitARMWinCFIAllocStack(unsigned Size, bool Wide) override;
95*d415bd75Srobert void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) override;
96*d415bd75Srobert void emitARMWinCFISaveSP(unsigned Reg) override;
97*d415bd75Srobert void emitARMWinCFISaveFRegs(unsigned First, unsigned Last) override;
98*d415bd75Srobert void emitARMWinCFISaveLR(unsigned Offset) override;
99*d415bd75Srobert void emitARMWinCFIPrologEnd(bool Fragment) override;
100*d415bd75Srobert void emitARMWinCFINop(bool Wide) override;
101*d415bd75Srobert void emitARMWinCFIEpilogStart(unsigned Condition) override;
102*d415bd75Srobert void emitARMWinCFIEpilogEnd() override;
103*d415bd75Srobert void emitARMWinCFICustom(unsigned Opcode) override;
104*d415bd75Srobert
105*d415bd75Srobert private:
106*d415bd75Srobert void emitARMWinUnwindCode(unsigned UnwindCode, int Reg, int Offset);
107*d415bd75Srobert };
108*d415bd75Srobert
109*d415bd75Srobert // Helper function to common out unwind code setup for those codes that can
110*d415bd75Srobert // belong to both prolog and epilog.
emitARMWinUnwindCode(unsigned UnwindCode,int Reg,int Offset)111*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinUnwindCode(unsigned UnwindCode,
112*d415bd75Srobert int Reg, int Offset) {
113*d415bd75Srobert auto &S = getStreamer();
114*d415bd75Srobert WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
115*d415bd75Srobert if (!CurFrame)
116*d415bd75Srobert return;
117*d415bd75Srobert MCSymbol *Label = S.emitCFILabel();
118*d415bd75Srobert auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset);
119*d415bd75Srobert if (InEpilogCFI)
120*d415bd75Srobert CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
121*d415bd75Srobert else
122*d415bd75Srobert CurFrame->Instructions.push_back(Inst);
123*d415bd75Srobert }
124*d415bd75Srobert
emitARMWinCFIAllocStack(unsigned Size,bool Wide)125*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFIAllocStack(unsigned Size,
126*d415bd75Srobert bool Wide) {
127*d415bd75Srobert unsigned Op = Win64EH::UOP_AllocSmall;
128*d415bd75Srobert if (!Wide) {
129*d415bd75Srobert if (Size / 4 > 0xffff)
130*d415bd75Srobert Op = Win64EH::UOP_AllocHuge;
131*d415bd75Srobert else if (Size / 4 > 0x7f)
132*d415bd75Srobert Op = Win64EH::UOP_AllocLarge;
133*d415bd75Srobert } else {
134*d415bd75Srobert Op = Win64EH::UOP_WideAllocMedium;
135*d415bd75Srobert if (Size / 4 > 0xffff)
136*d415bd75Srobert Op = Win64EH::UOP_WideAllocHuge;
137*d415bd75Srobert else if (Size / 4 > 0x3ff)
138*d415bd75Srobert Op = Win64EH::UOP_WideAllocLarge;
139*d415bd75Srobert }
140*d415bd75Srobert emitARMWinUnwindCode(Op, -1, Size);
141*d415bd75Srobert }
142*d415bd75Srobert
emitARMWinCFISaveRegMask(unsigned Mask,bool Wide)143*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFISaveRegMask(unsigned Mask,
144*d415bd75Srobert bool Wide) {
145*d415bd75Srobert assert(Mask != 0);
146*d415bd75Srobert int Lr = (Mask & 0x4000) ? 1 : 0;
147*d415bd75Srobert Mask &= ~0x4000;
148*d415bd75Srobert if (Wide)
149*d415bd75Srobert assert((Mask & ~0x1fff) == 0);
150*d415bd75Srobert else
151*d415bd75Srobert assert((Mask & ~0x00ff) == 0);
152*d415bd75Srobert if (Mask && ((Mask + (1 << 4)) & Mask) == 0) {
153*d415bd75Srobert if (Wide && (Mask & 0x1000) == 0 && (Mask & 0xff) == 0xf0) {
154*d415bd75Srobert // One continuous range from r4 to r8-r11
155*d415bd75Srobert for (int I = 11; I >= 8; I--) {
156*d415bd75Srobert if (Mask & (1 << I)) {
157*d415bd75Srobert emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegsR4R11LR, I, Lr);
158*d415bd75Srobert return;
159*d415bd75Srobert }
160*d415bd75Srobert }
161*d415bd75Srobert // If it actually was from r4 to r4-r7, continue below.
162*d415bd75Srobert } else if (!Wide) {
163*d415bd75Srobert // One continuous range from r4 to r4-r7
164*d415bd75Srobert for (int I = 7; I >= 4; I--) {
165*d415bd75Srobert if (Mask & (1 << I)) {
166*d415bd75Srobert emitARMWinUnwindCode(Win64EH::UOP_SaveRegsR4R7LR, I, Lr);
167*d415bd75Srobert return;
168*d415bd75Srobert }
169*d415bd75Srobert }
170*d415bd75Srobert llvm_unreachable("logic error");
171*d415bd75Srobert }
172*d415bd75Srobert }
173*d415bd75Srobert Mask |= Lr << 14;
174*d415bd75Srobert if (Wide)
175*d415bd75Srobert emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegMask, Mask, 0);
176*d415bd75Srobert else
177*d415bd75Srobert emitARMWinUnwindCode(Win64EH::UOP_SaveRegMask, Mask, 0);
178*d415bd75Srobert }
179*d415bd75Srobert
emitARMWinCFISaveSP(unsigned Reg)180*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) {
181*d415bd75Srobert emitARMWinUnwindCode(Win64EH::UOP_SaveSP, Reg, 0);
182*d415bd75Srobert }
183*d415bd75Srobert
emitARMWinCFISaveFRegs(unsigned First,unsigned Last)184*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFISaveFRegs(unsigned First,
185*d415bd75Srobert unsigned Last) {
186*d415bd75Srobert assert(First <= Last);
187*d415bd75Srobert assert(First >= 16 || Last < 16);
188*d415bd75Srobert assert(First <= 31 && Last <= 31);
189*d415bd75Srobert if (First == 8)
190*d415bd75Srobert emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD8D15, Last, 0);
191*d415bd75Srobert else if (First <= 15)
192*d415bd75Srobert emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD0D15, First, Last);
193*d415bd75Srobert else
194*d415bd75Srobert emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD16D31, First, Last);
195*d415bd75Srobert }
196*d415bd75Srobert
emitARMWinCFISaveLR(unsigned Offset)197*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) {
198*d415bd75Srobert emitARMWinUnwindCode(Win64EH::UOP_SaveLR, 0, Offset);
199*d415bd75Srobert }
200*d415bd75Srobert
emitARMWinCFINop(bool Wide)201*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFINop(bool Wide) {
202*d415bd75Srobert if (Wide)
203*d415bd75Srobert emitARMWinUnwindCode(Win64EH::UOP_WideNop, -1, 0);
204*d415bd75Srobert else
205*d415bd75Srobert emitARMWinUnwindCode(Win64EH::UOP_Nop, -1, 0);
206*d415bd75Srobert }
207*d415bd75Srobert
emitARMWinCFIPrologEnd(bool Fragment)208*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFIPrologEnd(bool Fragment) {
209*d415bd75Srobert auto &S = getStreamer();
210*d415bd75Srobert WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
211*d415bd75Srobert if (!CurFrame)
212*d415bd75Srobert return;
213*d415bd75Srobert
214*d415bd75Srobert MCSymbol *Label = S.emitCFILabel();
215*d415bd75Srobert CurFrame->PrologEnd = Label;
216*d415bd75Srobert WinEH::Instruction Inst =
217*d415bd75Srobert WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0);
218*d415bd75Srobert auto it = CurFrame->Instructions.begin();
219*d415bd75Srobert CurFrame->Instructions.insert(it, Inst);
220*d415bd75Srobert CurFrame->Fragment = Fragment;
221*d415bd75Srobert }
222*d415bd75Srobert
emitARMWinCFIEpilogStart(unsigned Condition)223*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogStart(unsigned Condition) {
224*d415bd75Srobert auto &S = getStreamer();
225*d415bd75Srobert WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
226*d415bd75Srobert if (!CurFrame)
227*d415bd75Srobert return;
228*d415bd75Srobert
229*d415bd75Srobert InEpilogCFI = true;
230*d415bd75Srobert CurrentEpilog = S.emitCFILabel();
231*d415bd75Srobert CurFrame->EpilogMap[CurrentEpilog].Condition = Condition;
232*d415bd75Srobert }
233*d415bd75Srobert
emitARMWinCFIEpilogEnd()234*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogEnd() {
235*d415bd75Srobert auto &S = getStreamer();
236*d415bd75Srobert WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
237*d415bd75Srobert if (!CurFrame)
238*d415bd75Srobert return;
239*d415bd75Srobert
240*d415bd75Srobert if (!CurrentEpilog) {
241*d415bd75Srobert S.getContext().reportError(SMLoc(), "Stray .seh_endepilogue in " +
242*d415bd75Srobert CurFrame->Function->getName());
243*d415bd75Srobert return;
244*d415bd75Srobert }
245*d415bd75Srobert
246*d415bd75Srobert std::vector<WinEH::Instruction> &Epilog =
247*d415bd75Srobert CurFrame->EpilogMap[CurrentEpilog].Instructions;
248*d415bd75Srobert
249*d415bd75Srobert unsigned UnwindCode = Win64EH::UOP_End;
250*d415bd75Srobert if (!Epilog.empty()) {
251*d415bd75Srobert WinEH::Instruction EndInstr = Epilog.back();
252*d415bd75Srobert if (EndInstr.Operation == Win64EH::UOP_Nop) {
253*d415bd75Srobert UnwindCode = Win64EH::UOP_EndNop;
254*d415bd75Srobert Epilog.pop_back();
255*d415bd75Srobert } else if (EndInstr.Operation == Win64EH::UOP_WideNop) {
256*d415bd75Srobert UnwindCode = Win64EH::UOP_WideEndNop;
257*d415bd75Srobert Epilog.pop_back();
258*d415bd75Srobert }
259*d415bd75Srobert }
260*d415bd75Srobert
261*d415bd75Srobert InEpilogCFI = false;
262*d415bd75Srobert WinEH::Instruction Inst = WinEH::Instruction(UnwindCode, nullptr, -1, 0);
263*d415bd75Srobert CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
264*d415bd75Srobert MCSymbol *Label = S.emitCFILabel();
265*d415bd75Srobert CurFrame->EpilogMap[CurrentEpilog].End = Label;
266*d415bd75Srobert CurrentEpilog = nullptr;
267*d415bd75Srobert }
268*d415bd75Srobert
emitARMWinCFICustom(unsigned Opcode)269*d415bd75Srobert void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) {
270*d415bd75Srobert emitARMWinUnwindCode(Win64EH::UOP_Custom, 0, Opcode);
271*d415bd75Srobert }
272*d415bd75Srobert
273*d415bd75Srobert } // end anonymous namespace
274*d415bd75Srobert
createARMObjectTargetWinCOFFStreamer(MCStreamer & S)275*d415bd75Srobert MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) {
276*d415bd75Srobert return new ARMTargetWinCOFFStreamer(S);
277*d415bd75Srobert }
278