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