1//===-- X86WinEHUnwindV2.cpp - Win x64 Unwind v2 ----------------*- 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/// Implements the analysis required to detect if a function can use Unwind v2
10/// information, and emits the neccesary pseudo instructions used by MC to
11/// generate the unwind info.
12///
13//===----------------------------------------------------------------------===//
14
15#include "MCTargetDesc/X86BaseInfo.h"
16#include "X86.h"
17#include "X86Subtarget.h"
18#include "llvm/ADT/Statistic.h"
19#include "llvm/CodeGen/MachineBasicBlock.h"
20#include "llvm/CodeGen/MachineFunctionPass.h"
21#include "llvm/CodeGen/MachineInstrBuilder.h"
22#include "llvm/CodeGen/TargetFrameLowering.h"
23#include "llvm/CodeGen/TargetInstrInfo.h"
24#include "llvm/CodeGen/TargetSubtargetInfo.h"
25#include "llvm/IR/DiagnosticInfo.h"
26#include "llvm/IR/Module.h"
27
28using namespace llvm;
29
30#define DEBUG_TYPE "x86-wineh-unwindv2"
31
32STATISTIC(MeetsUnwindV2Criteria,
33 "Number of functions that meet Unwind v2 criteria");
34STATISTIC(FailsUnwindV2Criteria,
35 "Number of functions that fail Unwind v2 criteria");
36
37namespace {
38
39struct EpilogInfo {
40 MachineInstr *UnwindV2StartLocation;
41 unsigned ApproximateInstructionPosition;
42};
43
44struct FrameInfo {
45 unsigned ApproximatePrologCodeCount;
46 unsigned ApproximateInstructionCount;
47 SmallVector<EpilogInfo> EpilogInfos;
48};
49
50class X86WinEHUnwindV2Legacy : public MachineFunctionPass {
51public:
52 static char ID;
53
54 X86WinEHUnwindV2Legacy() : MachineFunctionPass(ID) {
55 initializeX86WinEHUnwindV2LegacyPass(*PassRegistry::getPassRegistry());
56 }
57
58 StringRef getPassName() const override { return "WinEH Unwind V2"; }
59
60 bool runOnMachineFunction(MachineFunction &MF) override;
61};
62
63/// Rejects the current function due to an internal error within LLVM.
64std::nullopt_t rejectCurrentFunctionInternalError(const MachineFunction &MF,
65 WinX64EHUnwindMode Mode,
66 StringRef Reason) {
67 if (Mode == WinX64EHUnwindMode::V2Required)
68 reportFatalInternalError(reason: "Windows x64 Unwind v2 is required, but LLVM has "
69 "generated incompatible code in function '" +
70 MF.getName() + "': " + Reason);
71
72 FailsUnwindV2Criteria++;
73 return std::nullopt;
74}
75
76enum class FunctionState {
77 InProlog,
78 HasProlog,
79 InEpilog,
80 FinishedEpilog,
81};
82
83} // end anonymous namespace
84
85char X86WinEHUnwindV2Legacy::ID = 0;
86
87INITIALIZE_PASS(X86WinEHUnwindV2Legacy, "x86-wineh-unwindv2",
88 "Analyze and emit instructions for Win64 Unwind v2", false,
89 false)
90
91FunctionPass *llvm::createX86WinEHUnwindV2LegacyPass() {
92 return new X86WinEHUnwindV2Legacy();
93}
94
95DebugLoc findDebugLoc(const MachineBasicBlock &MBB) {
96 for (const MachineInstr &MI : MBB)
97 if (MI.getDebugLoc())
98 return MI.getDebugLoc();
99
100 return DebugLoc::getUnknown();
101}
102
103// Continues running the analysis on the given function or funclet.
104std::optional<FrameInfo>
105runAnalysisOnFuncOrFunclet(MachineFunction &MF, MachineFunction::iterator &Iter,
106 WinX64EHUnwindMode Mode) {
107 const TargetFrameLowering &TFL = *MF.getSubtarget().getFrameLowering();
108
109 // Current state of processing the function. We'll assume that all functions
110 // start with a prolog.
111 FunctionState State = FunctionState::InProlog;
112
113 // Prolog information.
114 SmallVector<int64_t> PushedRegs;
115 bool HasStackAlloc = false;
116 bool HasSetFrame = false;
117 unsigned ApproximatePrologCodeCount = 0;
118
119 SmallVector<EpilogInfo> EpilogInfos;
120
121 // Unwind v2 requires that the epilog is no more than 4Kb away from the last
122 // instruction that the current unwind info covers. If we believe that we are
123 // going over that limit then we need to split the unwind info. Ideally we'd
124 // do this at the point where we actually know how far away we are from the
125 // last instruction, but that's not possible here and splitting unwind infos
126 // in MC would be difficult. However, the cost of splitting an unwind info is
127 // fairly cheap (in the other of bytes in the xdata section), so we can
128 // instead use a heuristic based on the number of MachineInstrs to decide when
129 // to split unwind infos, and allow users to tune the threshold if needed.
130 // This is not a perfect solution, but 1) it is cheap to calculate, 2) allows
131 // the common case for small functions or large functions with multiple
132 // returns at the end to have a single unwind info, and 3) allows unwind v2 to
133 // be used in large functions (that would otherwise be rejected) for a small
134 // binary size cost.
135 unsigned ApproximateInstructionCount = 0;
136
137 for (; Iter != MF.end(); ++Iter) {
138 MachineBasicBlock &MBB = *Iter;
139
140 // If we're already been processing a function, then come across a funclet
141 // then break since the funclet will get a fresh frame info.
142 if (MBB.isEHFuncletEntry() && State != FunctionState::InProlog)
143 break;
144
145 // Current epilog information. We assume that epilogs cannot cross basic
146 // block boundaries.
147 unsigned PoppedRegCount = 0;
148 bool HasStackDealloc = false;
149 bool HasSetFrameBack = false;
150 MachineInstr *UnwindV2StartLocation = nullptr;
151
152 for (MachineInstr &MI : MBB) {
153 // This is an *approximation* of the number of instructions that will be
154 // emitted. It is not the actual number of instructions, but that doesn't
155 // matter: see the comment at the declaration of
156 // ApproximateInstructionCount.
157 if (!MI.isPseudo() && !MI.isMetaInstruction())
158 ApproximateInstructionCount++;
159
160 switch (MI.getOpcode()) {
161 //
162 // Prolog handling.
163 //
164 case X86::SEH_PushReg:
165 if (State != FunctionState::InProlog)
166 llvm_unreachable("SEH_PushReg outside of prolog");
167 ApproximatePrologCodeCount++;
168 PushedRegs.push_back(Elt: MI.getOperand(i: 0).getImm());
169 break;
170
171 case X86::SEH_StackAlloc:
172 if (State != FunctionState::InProlog)
173 llvm_unreachable("SEH_StackAlloc outside of prolog");
174 // Assume a large alloc...
175 ApproximatePrologCodeCount += 3;
176 HasStackAlloc = true;
177 break;
178
179 case X86::SEH_SetFrame:
180 if (State != FunctionState::InProlog)
181 llvm_unreachable("SEH_SetFrame outside of prolog");
182 ApproximatePrologCodeCount++;
183 HasSetFrame = true;
184 break;
185
186 case X86::SEH_SaveReg:
187 case X86::SEH_SaveXMM:
188 if (State != FunctionState::InProlog)
189 llvm_unreachable("SEH_SaveXMM or SEH_SaveReg outside of prolog");
190 // Assume a big reg...
191 ApproximatePrologCodeCount += 3;
192 break;
193
194 case X86::SEH_PushFrame:
195 if (State != FunctionState::InProlog)
196 llvm_unreachable("SEH_PushFrame outside of prolog");
197 ApproximatePrologCodeCount++;
198 break;
199
200 case X86::SEH_EndPrologue:
201 if (State != FunctionState::InProlog)
202 llvm_unreachable("SEH_EndPrologue outside of prolog");
203 State = FunctionState::HasProlog;
204 break;
205
206 //
207 // Epilog handling.
208 //
209 case X86::SEH_BeginEpilogue:
210 if (State != FunctionState::HasProlog)
211 llvm_unreachable("SEH_BeginEpilogue in prolog or another epilog");
212 State = FunctionState::InEpilog;
213 break;
214
215 case X86::SEH_EndEpilogue:
216 if (State != FunctionState::InEpilog)
217 llvm_unreachable("SEH_EndEpilogue outside of epilog");
218 if (HasStackAlloc != HasStackDealloc)
219 return rejectCurrentFunctionInternalError(
220 MF, Mode,
221 Reason: "The prolog made a stack allocation, "
222 "but the epilog did not deallocate it");
223 if (PoppedRegCount != PushedRegs.size())
224 return rejectCurrentFunctionInternalError(
225 MF, Mode,
226 Reason: "The prolog pushed more registers than "
227 "the epilog popped");
228
229 // If we didn't find the start location, then use the end of the
230 // epilog.
231 if (!UnwindV2StartLocation)
232 UnwindV2StartLocation = &MI;
233 EpilogInfos.push_back(
234 Elt: {.UnwindV2StartLocation: UnwindV2StartLocation, .ApproximateInstructionPosition: ApproximateInstructionCount});
235 State = FunctionState::FinishedEpilog;
236 break;
237
238 case X86::MOV64rr:
239 if (State == FunctionState::InEpilog) {
240 // If the prolog contains a stack allocation, then the first
241 // instruction in the epilog must be to adjust the stack pointer.
242 if (!HasSetFrame)
243 return rejectCurrentFunctionInternalError(
244 MF, Mode,
245 Reason: "The epilog is setting frame back, but prolog did not set it");
246 if (PoppedRegCount > 0)
247 return rejectCurrentFunctionInternalError(
248 MF, Mode,
249 Reason: "The epilog is setting the frame back after popping "
250 "registers");
251 if (HasStackDealloc)
252 return rejectCurrentFunctionInternalError(
253 MF, Mode,
254 Reason: "Cannot set the frame back after the stack "
255 "allocation has been deallocated");
256 HasSetFrameBack = true;
257 } else if (State == FunctionState::FinishedEpilog)
258 return rejectCurrentFunctionInternalError(
259 MF, Mode, Reason: "Unexpected mov instruction after the epilog");
260 break;
261
262 case X86::LEA64r:
263 case X86::ADD64ri32:
264 if (State == FunctionState::InEpilog) {
265 // If the prolog contains a stack allocation, then the first
266 // instruction in the epilog must be to adjust the stack pointer.
267 if (!HasStackAlloc)
268 return rejectCurrentFunctionInternalError(
269 MF, Mode,
270 Reason: "The epilog is deallocating a stack "
271 "allocation, but the prolog did "
272 "not allocate one");
273 if (PoppedRegCount > 0)
274 return rejectCurrentFunctionInternalError(
275 MF, Mode,
276 Reason: "The epilog is deallocating a stack allocation after popping "
277 "registers");
278
279 HasStackDealloc = true;
280 } else if (State == FunctionState::FinishedEpilog)
281 return rejectCurrentFunctionInternalError(
282 MF, Mode, Reason: "Unexpected lea or add instruction after the epilog");
283 break;
284
285 case X86::POP64r:
286 if (State == FunctionState::InEpilog) {
287 Register Reg = MI.getOperand(i: 0).getReg();
288 if (HasStackAlloc && (PoppedRegCount == 0) &&
289 !llvm::is_contained(Range&: PushedRegs, Element: Reg)) {
290 // If this is a pop that doesn't correspond to the set of pushed
291 // registers, then assume it was used to adjust the stack pointer.
292 HasStackDealloc = true;
293 } else {
294 // Special case: no explicit stack dealloc is required if SetFrame
295 // was used and the function has a frame pointer.
296 if (PoppedRegCount == 0 && HasStackAlloc && !HasStackDealloc &&
297 HasSetFrameBack && TFL.hasFP(MF))
298 HasStackDealloc = true;
299
300 // After the stack pointer has been adjusted, the epilog must
301 // POP each register in reverse order of the PUSHes in the prolog.
302 PoppedRegCount++;
303 if (HasStackAlloc != HasStackDealloc)
304 return rejectCurrentFunctionInternalError(
305 MF, Mode,
306 Reason: "Cannot pop registers before the stack "
307 "allocation has been deallocated");
308 if (PoppedRegCount > PushedRegs.size())
309 return rejectCurrentFunctionInternalError(
310 MF, Mode,
311 Reason: "The epilog is popping more registers than the prolog "
312 "pushed");
313 if (PushedRegs[PushedRegs.size() - PoppedRegCount] != Reg.id())
314 return rejectCurrentFunctionInternalError(
315 MF, Mode,
316 Reason: "The epilog is popping a registers in "
317 "a different order than the "
318 "prolog pushed them");
319
320 // Unwind v2 records the size of the epilog not from where we place
321 // SEH_BeginEpilogue (as that contains the instruction to adjust the
322 // stack pointer) but from the first POP instruction (if there is
323 // one).
324 if (!UnwindV2StartLocation) {
325 assert(PoppedRegCount == 1);
326 UnwindV2StartLocation = &MI;
327 }
328 }
329 } else if (State == FunctionState::FinishedEpilog)
330 // Unexpected instruction after the epilog.
331 return rejectCurrentFunctionInternalError(
332 MF, Mode, Reason: "Registers are being popped after the epilog");
333 break;
334
335 default:
336 if (MI.isTerminator()) {
337 if (State == FunctionState::FinishedEpilog)
338 // Found the terminator after the epilog, we're now ready for
339 // another epilog.
340 State = FunctionState::HasProlog;
341 else if (State == FunctionState::InEpilog)
342 llvm_unreachable("Terminator in the middle of the epilog");
343 } else if (!MI.isDebugOrPseudoInstr()) {
344 if ((State == FunctionState::FinishedEpilog) ||
345 (State == FunctionState::InEpilog))
346 // Unknown instruction in or after the epilog.
347 return rejectCurrentFunctionInternalError(
348 MF, Mode, Reason: "Unexpected instruction in or after the epilog");
349 }
350 }
351 }
352 }
353
354 return FrameInfo{.ApproximatePrologCodeCount: ApproximatePrologCodeCount, .ApproximateInstructionCount: ApproximateInstructionCount,
355 .EpilogInfos: EpilogInfos};
356}
357
358bool runX86WinEHUnwindV2(MachineFunction &MF) {
359 const X86Options &CLOpts = MF.getSubtarget<X86Subtarget>().getCLOpts();
360 std::optional<unsigned> ForceMode = CLOpts.wineh_unwindv2_force_mode;
361 WinX64EHUnwindMode Mode =
362 ForceMode ? static_cast<WinX64EHUnwindMode>(*ForceMode)
363 : MF.getFunction().getParent()->getWinX64EHUnwindMode();
364
365 // Only act on V2 modes; V1 = disabled, V3 handled by the V3 pass.
366 if (Mode != WinX64EHUnwindMode::V2BestEffort &&
367 Mode != WinX64EHUnwindMode::V2Required)
368 return false;
369
370 // A function that requires V3 (see requireWinX64UnwindV3()) is emitted as V3
371 // instead; skip it here so this pass does not stamp V2 pseudos that conflict
372 // with the V3 layout.
373 if (requireWinX64UnwindV3(MF))
374 return false;
375
376 // Requested changes.
377 SmallVector<FrameInfo> FrameInfos;
378 MachineFunction::iterator Iter = MF.begin();
379 while (Iter != MF.end()) {
380 auto FI = runAnalysisOnFuncOrFunclet(MF, Iter, Mode);
381 if (!FI)
382 return false;
383 if (!FI->EpilogInfos.empty())
384 FrameInfos.push_back(Elt: std::move(*FI));
385 }
386
387 if (FrameInfos.empty())
388 return false;
389
390 MeetsUnwindV2Criteria++;
391
392 const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
393 for (auto &FI : FrameInfos) {
394 // Walk the list of epilogs backwards and add new SEH pseudo instructions:
395 // * SEH_UnwindV2Start at the start of each epilog.
396 // * If the current instruction is too far away from where the last unwind
397 // info ended OR there are too many unwind codes in the info, then add
398 // SEH_SplitChainedAtEndOfBlock to finish the current info.
399 unsigned LastUnwindInfoEndPosition = FI.ApproximateInstructionCount;
400 unsigned UnwindCodeCount = FI.ApproximatePrologCodeCount + 1;
401 for (auto &Info : llvm::reverse(C&: FI.EpilogInfos)) {
402 MachineBasicBlock &MBB = *Info.UnwindV2StartLocation->getParent();
403 const DebugLoc &DL = Info.UnwindV2StartLocation->getDebugLoc();
404 BuildMI(BB&: MBB, I: Info.UnwindV2StartLocation, MIMD: DL,
405 MCID: TII->get(Opcode: X86::SEH_UnwindV2Start))
406 .setMIFlag(MachineInstr::FrameDestroy);
407
408 if ((LastUnwindInfoEndPosition - Info.ApproximateInstructionPosition >=
409 CLOpts.wineh_unwindv2_instruction_count_threshold) ||
410 (UnwindCodeCount >= CLOpts.wineh_unwindv2_unwind_codes_threshold)) {
411 BuildMI(BB&: MBB, I: MBB.begin(), MIMD: DL,
412 MCID: TII->get(Opcode: X86::SEH_SplitChainedAtEndOfBlock))
413 .setMIFlag(MachineInstr::FrameDestroy);
414 LastUnwindInfoEndPosition = Info.ApproximateInstructionPosition;
415 // Doesn't reset to 0, as the prolog unwind codes are now in this info.
416 UnwindCodeCount = FI.ApproximatePrologCodeCount + 1;
417 }
418
419 UnwindCodeCount++;
420 }
421 }
422
423 // Note that the function is using Unwind v2.
424 MachineBasicBlock &FirstMBB = MF.front();
425 BuildMI(BB&: FirstMBB, I&: FirstMBB.front(), MIMD: findDebugLoc(MBB: FirstMBB),
426 MCID: TII->get(Opcode: X86::SEH_UnwindVersion))
427 .addImm(Val: 2)
428 .setMIFlag(MachineInstr::FrameSetup);
429
430 return true;
431}
432
433PreservedAnalyses
434X86WinEHUnwindV2Pass::run(MachineFunction &MF,
435 MachineFunctionAnalysisManager &MFAM) {
436 const bool Modified = runX86WinEHUnwindV2(MF);
437 return Modified ? getMachineFunctionPassPreservedAnalyses()
438 .preserveSet<CFGAnalyses>()
439 : PreservedAnalyses::all();
440}
441
442bool X86WinEHUnwindV2Legacy::runOnMachineFunction(MachineFunction &MF) {
443 return runX86WinEHUnwindV2(MF);
444}
445