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