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