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: |
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 | |
37 | void 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 | |
46 | void ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) { |
47 | EHStreamer.EmitUnwindInfo(Streamer&: *this, FI: Frame, /* HandlerData = */ false); |
48 | } |
49 | |
50 | void ARMWinCOFFStreamer::emitWindowsUnwindTables() { |
51 | if (!getNumWinFrameInfos()) |
52 | return; |
53 | EHStreamer.Emit(Streamer&: *this); |
54 | } |
55 | |
56 | void ARMWinCOFFStreamer::finishImpl() { |
57 | emitFrames(MAB: nullptr); |
58 | emitWindowsUnwindTables(); |
59 | |
60 | MCWinCOFFStreamer::finishImpl(); |
61 | } |
62 | } |
63 | |
64 | MCStreamer * |
65 | llvm::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 | |
73 | namespace { |
74 | class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer { |
75 | public: |
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 | |
96 | private: |
97 | void emitARMWinUnwindCode(unsigned UnwindCode, int Reg, int Offset); |
98 | }; |
99 | |
100 | void 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. |
106 | void 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 | |
120 | void 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 | |
138 | void 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 | |
175 | void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) { |
176 | emitARMWinUnwindCode(UnwindCode: Win64EH::UOP_SaveSP, Reg, Offset: 0); |
177 | } |
178 | |
179 | void 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 | |
192 | void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) { |
193 | emitARMWinUnwindCode(UnwindCode: Win64EH::UOP_SaveLR, Reg: 0, Offset); |
194 | } |
195 | |
196 | void 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 | |
203 | void 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 | |
218 | void 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 | |
230 | void 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 | |
258 | void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) { |
259 | emitARMWinUnwindCode(UnwindCode: Win64EH::UOP_Custom, Reg: 0, Offset: Opcode); |
260 | } |
261 | |
262 | } // end anonymous namespace |
263 | |
264 | MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) { |
265 | return new ARMTargetWinCOFFStreamer(S); |
266 | } |
267 | |