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 emitThumbFunc(MCSymbol *Symbol) override; |
35 | void finishImpl() override; |
36 | }; |
37 | |
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(Streamer&: *this, FI: getCurrentWinFrameInfo(), |
44 | /* HandlerData = */ true); |
45 | } |
46 | |
47 | void ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) { |
48 | EHStreamer.EmitUnwindInfo(Streamer&: *this, FI: Frame, /* HandlerData = */ false); |
49 | } |
50 | |
51 | void ARMWinCOFFStreamer::emitWindowsUnwindTables() { |
52 | if (!getNumWinFrameInfos()) |
53 | return; |
54 | EHStreamer.Emit(Streamer&: *this); |
55 | } |
56 | |
57 | void ARMWinCOFFStreamer::emitThumbFunc(MCSymbol *Symbol) { |
58 | getAssembler().setIsThumbFunc(Symbol); |
59 | } |
60 | |
61 | void ARMWinCOFFStreamer::finishImpl() { |
62 | emitFrames(MAB: nullptr); |
63 | emitWindowsUnwindTables(); |
64 | |
65 | MCWinCOFFStreamer::finishImpl(); |
66 | } |
67 | } |
68 | |
69 | MCStreamer * |
70 | llvm::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 | |
78 | namespace { |
79 | class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer { |
80 | private: |
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 | |
87 | public: |
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 | |
103 | private: |
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. |
109 | void 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 | |
123 | void 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 | |
141 | void 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 | |
178 | void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) { |
179 | emitARMWinUnwindCode(UnwindCode: Win64EH::UOP_SaveSP, Reg, Offset: 0); |
180 | } |
181 | |
182 | void 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 | |
195 | void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) { |
196 | emitARMWinUnwindCode(UnwindCode: Win64EH::UOP_SaveLR, Reg: 0, Offset); |
197 | } |
198 | |
199 | void 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 | |
206 | void 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 | |
221 | void 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 | |
232 | void 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 | |
267 | void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) { |
268 | emitARMWinUnwindCode(UnwindCode: Win64EH::UOP_Custom, Reg: 0, Offset: Opcode); |
269 | } |
270 | |
271 | } // end anonymous namespace |
272 | |
273 | MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) { |
274 | return new ARMTargetWinCOFFStreamer(S); |
275 | } |
276 | |