1//===-- X86WinEHUnwindV3.cpp - Win x64 Unwind v3 ----------------*- 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 capacity-checking and sub-fragment splitting pass for
10/// Unwind v3 information. V3 can encode any prolog/epilog pattern, so this
11/// pass does not validate epilog structure; it only needs to:
12/// 1. Count prolog/epilog operations and epilogs.
13/// 2. Check V3 capacity limits (<=31 prolog/epilog ops, <=7 epilogs).
14/// 3. Insert sub-fragment split points if limits are exceeded.
15///
16/// The unwind version is normally module-wide. When only an individual function
17/// needs V3 (see requireWinX64UnwindV3()), this pass stamps each of its frames
18/// -- the entry block and every funclet -- with a per-function
19/// .seh_unwindversion 3, leaving the rest of the module on its default version.
20///
21/// See https://learn.microsoft.com/en-us/cpp/build/x64-unwind-information-v3
22///
23//===----------------------------------------------------------------------===//
24
25#include "X86.h"
26#include "X86Subtarget.h"
27#include "llvm/ADT/Statistic.h"
28#include "llvm/CodeGen/MachineBasicBlock.h"
29#include "llvm/CodeGen/MachineFunctionPass.h"
30#include "llvm/CodeGen/MachineInstrBuilder.h"
31#include "llvm/CodeGen/TargetInstrInfo.h"
32#include "llvm/CodeGen/TargetSubtargetInfo.h"
33#include "llvm/IR/DiagnosticInfo.h"
34#include "llvm/IR/LLVMContext.h"
35#include "llvm/IR/Module.h"
36#include "llvm/Support/CommandLine.h"
37#include "llvm/Support/Debug.h"
38
39using namespace llvm;
40
41#define DEBUG_TYPE "x86-wineh-unwindv3"
42
43STATISTIC(FunctionsProcessed,
44 "Number of functions processed by Unwind v3 pass");
45STATISTIC(SubFragmentSplits,
46 "Number of sub-fragment splits inserted for Unwind v3");
47
48/// V3 limits from the format specification.
49static constexpr unsigned MaxV3PrologOps = 31;
50static constexpr unsigned MaxV3Epilogs = 7;
51static constexpr unsigned MaxV3EpilogOps = 31;
52static constexpr unsigned EpilogDistanceThreshold = 32767;
53
54/// Approximate byte distance between an epilog and its fragment tail beyond
55/// which the funclet is split into a new chained sub-fragment. The V3
56/// EpilogOffset field is a signed 16-bit byte offset measured from the
57/// fragment tail, so each fragment must span less than 32 KiB of code. The
58/// exact byte offsets aren't known until MC layout, so (like the V2 pass) an
59/// approximate byte count is used as a proxy — instructions are charged
60/// ApproxBytesPerInstr each and alignment padding is added.
61static cl::opt<unsigned> ApproxBytesPerInstr(
62 "x86-wineh-unwindv3-instr-avg-size", cl::Hidden,
63 cl::desc(
64 "Average size of an instruction. This value is used in determining "
65 "split points for chained unwinder info"),
66 cl::init(Val: 7));
67
68/// After reporting a recoverable error for `MF`, erase all SEH pseudo-
69/// instructions and clear the WinCFI flag so the AsmPrinter doesn't try to
70/// emit (potentially malformed) unwind information. The LLVMContext
71/// diagnostic recorded by the caller will prevent the object file from
72/// actually being written.
73static void suppressWinCFI(MachineFunction &MF) {
74 for (MachineBasicBlock &MBB : MF) {
75 for (MachineInstr &MI : llvm::make_early_inc_range(Range&: MBB)) {
76 switch (MI.getOpcode()) {
77 case X86::SEH_PushReg:
78 case X86::SEH_Push2Regs:
79 case X86::SEH_SaveReg:
80 case X86::SEH_SaveXMM:
81 case X86::SEH_StackAlloc:
82 case X86::SEH_StackAlign:
83 case X86::SEH_SetFrame:
84 case X86::SEH_PushFrame:
85 case X86::SEH_EndPrologue:
86 case X86::SEH_BeginEpilogue:
87 case X86::SEH_EndEpilogue:
88 case X86::SEH_SplitChained:
89 case X86::SEH_SplitChainedAtEndOfBlock:
90 MI.eraseFromParent();
91 break;
92 default:
93 break;
94 }
95 }
96 }
97 MF.setHasWinCFI(false);
98}
99
100namespace {
101
102/// A V3 epilog and the approximate byte position where it begins, used
103/// as a candidate sub-fragment split point.
104struct EpilogSplitPoint {
105 MachineInstr *BeginEpilog;
106 unsigned ApproxBytePos;
107};
108
109/// Per-funclet analysis results.
110struct FuncletInfo {
111 unsigned PrologOpCount = 0;
112 unsigned MaxEpilogOpCount = 0;
113 /// Approximate byte position at the end of the funclet, used as the
114 /// initial fragment tail reference for size-based splitting.
115 unsigned EndBytePos = 0;
116 /// SEH_BeginEpilogue instructions (with approximate positions), used as
117 /// candidate insertion points for sub-fragment splitting.
118 SmallVector<EpilogSplitPoint, 8> Epilogs;
119};
120
121class X86WinEHUnwindV3 : public MachineFunctionPass {
122public:
123 static char ID;
124
125 X86WinEHUnwindV3() : MachineFunctionPass(ID) {
126 initializeX86WinEHUnwindV3Pass(*PassRegistry::getPassRegistry());
127 }
128
129 StringRef getPassName() const override { return "WinEH Unwind V3"; }
130
131 bool runOnMachineFunction(MachineFunction &MF) override;
132
133private:
134 /// Analyze one funclet (or the main function body) starting at Iter.
135 /// Advances Iter past the analyzed region, stopping at the next funclet
136 /// entry or the end of the function. ApproxBytePos is a running estimate of
137 /// the byte position across the whole function, used to estimate the byte
138 /// distance between epilogs and their fragment tail.
139 static FuncletInfo analyzeFunclet(MachineFunction &MF,
140 MachineFunction::iterator &Iter,
141 unsigned &ApproxBytePos);
142};
143
144} // end anonymous namespace
145
146char X86WinEHUnwindV3::ID = 0;
147
148INITIALIZE_PASS(X86WinEHUnwindV3, "x86-wineh-unwindv3",
149 "Capacity check and sub-fragment splitting for Win64 Unwind v3",
150 false, false)
151
152FunctionPass *llvm::createX86WinEHUnwindV3Pass() {
153 return new X86WinEHUnwindV3();
154}
155
156FuncletInfo X86WinEHUnwindV3::analyzeFunclet(MachineFunction &MF,
157 MachineFunction::iterator &Iter,
158 unsigned &ApproxBytePos) {
159 FuncletInfo Info;
160 bool InEpilog = false;
161 bool SeenProlog = false;
162 unsigned CurrentEpilogOpCount = 0;
163
164 for (; Iter != MF.end(); ++Iter) {
165 MachineBasicBlock &MBB = *Iter;
166
167 // If we've already been processing a funclet's prolog/body and encounter
168 // another funclet entry, stop - that funclet gets its own analysis.
169 if (MBB.isEHFuncletEntry() && SeenProlog)
170 break;
171
172 // Account for worst-case scenario of padding inserted to align this block.
173 Align A = MBB.getAlignment();
174 unsigned MaxPadding = A.value() - 1;
175 if (unsigned MaxBytes = MBB.getMaxBytesForAlignment())
176 MaxPadding = std::min(a: MaxPadding, b: MaxBytes);
177 ApproxBytePos += MaxPadding;
178
179 for (MachineInstr &MI : MBB) {
180 // Approximate the emitted byte size, mirroring the V2 pass. This
181 // estimates how far each epilog sits from its fragment tail; the exact
182 // byte offsets aren't available until MC layout, so each real
183 // instruction is charged ApproxBytesPerInstr bytes.
184 if (!MI.isPseudo() && !MI.isMetaInstruction())
185 ApproxBytePos += ApproxBytesPerInstr;
186
187 switch (MI.getOpcode()) {
188 case X86::SEH_PushReg:
189 case X86::SEH_Push2Regs:
190 case X86::SEH_StackAlloc:
191 case X86::SEH_SetFrame:
192 case X86::SEH_SaveReg:
193 case X86::SEH_SaveXMM:
194 case X86::SEH_PushFrame:
195 if (InEpilog)
196 CurrentEpilogOpCount++;
197 else
198 Info.PrologOpCount++;
199 break;
200 case X86::SEH_EndPrologue:
201 SeenProlog = true;
202 break;
203 case X86::SEH_BeginEpilogue:
204 InEpilog = true;
205 CurrentEpilogOpCount = 0;
206 LLVM_DEBUG(dbgs() << " epilog " << Info.Epilogs.size()
207 << " begins at approx byte position " << ApproxBytePos
208 << "\n");
209 Info.Epilogs.push_back(Elt: {.BeginEpilog: &MI, .ApproxBytePos: ApproxBytePos});
210 break;
211 case X86::SEH_EndEpilogue:
212 InEpilog = false;
213 Info.MaxEpilogOpCount =
214 std::max(a: Info.MaxEpilogOpCount, b: CurrentEpilogOpCount);
215 break;
216 default:
217 break;
218 }
219 }
220 }
221
222 Info.EndBytePos = ApproxBytePos;
223 LLVM_DEBUG(dbgs() << " funclet has " << Info.Epilogs.size()
224 << " epilog(s); ends at approx byte position "
225 << ApproxBytePos << "\n");
226 return Info;
227}
228
229bool X86WinEHUnwindV3::runOnMachineFunction(MachineFunction &MF) {
230 Function &F = MF.getFunction();
231 LLVMContext &Ctx = F.getContext();
232
233 if (!requireWinX64UnwindV3(MF))
234 return false;
235
236 // Emit a per-function .seh_unwindversion 3 only when V3 is enabled for this
237 // function alone: in module-wide V3 the AsmPrinter emits it once, so stamping
238 // here would duplicate it. The gate also requires WinCFI -- without a
239 // .seh_proc there is nothing to version, and a lone SEH pseudo would trip an
240 // AsmPrinter assertion. The marker is per .seh_proc, hence stamped on each
241 // funclet in the loop below.
242 bool PerFunctionV3 =
243 MF.hasWinCFI() && MF.getFunction().getParent()->getWinX64EHUnwindMode() !=
244 WinX64EHUnwindMode::V3;
245
246 bool Changed = false;
247 unsigned ApproxBytePos = 0;
248 MachineFunction::iterator Iter = MF.begin();
249
250 LLVM_DEBUG(dbgs() << "X86WinEHUnwindV3: processing " << MF.getName() << "\n");
251
252 // Process each funclet (and the main function body) independently.
253 // Each funclet gets its own UNWIND_INFO, so V3 limits apply per funclet.
254 while (Iter != MF.end()) {
255 // Iter points at the first block of a frame -- the entry frame on the
256 // first iteration, an EH funclet on later ones. Each frame is its own
257 // .seh_proc, so stamp the version on each here before analyzeFunclet
258 // advances past it.
259 if (PerFunctionV3) {
260 const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
261 MachineBasicBlock &FuncletEntry = *Iter;
262 BuildMI(BB&: FuncletEntry, I: FuncletEntry.begin(),
263 MIMD: FuncletEntry.findDebugLoc(MBBI: FuncletEntry.begin()),
264 MCID: TII->get(Opcode: X86::SEH_UnwindVersion))
265 .addImm(Val: 3)
266 .setMIFlag(MachineInstr::FrameSetup);
267 Changed = true;
268 }
269
270 FuncletInfo Info = analyzeFunclet(MF, Iter, ApproxBytePos);
271
272 if (Info.PrologOpCount > MaxV3PrologOps) {
273 Ctx.diagnose(DI: DiagnosticInfoResourceLimit(
274 F, "number of unwind v3 prolog operations required",
275 Info.PrologOpCount, MaxV3PrologOps, DS_Error, DK_ResourceLimit));
276 Ctx.diagnose(DI: DiagnosticInfoGenericWithLoc(
277 "sub-fragment splitting for prolog overflow is not yet implemented",
278 F, F.getSubprogram(), DS_Note));
279 // Stripping the SEH pseudos modifies the function, so report a change.
280 suppressWinCFI(MF);
281 return true;
282 }
283
284 if (Info.MaxEpilogOpCount > MaxV3EpilogOps) {
285 Ctx.diagnose(DI: DiagnosticInfoResourceLimit(
286 F, "number of unwind v3 epilog operations required",
287 Info.MaxEpilogOpCount, MaxV3EpilogOps, DS_Error, DK_ResourceLimit));
288 Ctx.diagnose(DI: DiagnosticInfoGenericWithLoc(
289 "sub-fragment splitting for epilog overflow is not yet implemented",
290 F, F.getSubprogram(), DS_Note));
291 // Stripping the SEH pseudos modifies the function, so report a change.
292 suppressWinCFI(MF);
293 return true;
294 }
295
296 // Split the funclet into chained sub-fragments so that each fragment's
297 // UNWIND_INFO stays within the V3 capacity limits: at most 7 epilogs per
298 // fragment, and each adjacent-epilog gap (plus the gap from the last epilog
299 // to the fragment tail) small enough that the corresponding signed-16-bit
300 // EpilogOffset delta fits.
301 //
302 // A SEH_SplitChainedAtEndOfBlock inserted at the start of an epilog's
303 // block makes the AsmPrinter emit the actual .seh_splitchained at the
304 // *end* of that block, so the epilog becomes the last epilog of the
305 // earlier fragment, immediately followed by the new chained fragment. A
306 // long tail after the last epilog is pushed into its own epilog-free
307 // chained fragment.
308 const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
309 auto SplitAfter = [&](const EpilogSplitPoint &Epilog) {
310 MachineBasicBlock *MBB = Epilog.BeginEpilog->getParent();
311 BuildMI(BB&: *MBB, I: MBB->begin(), MIMD: Epilog.BeginEpilog->getDebugLoc(),
312 MCID: TII->get(Opcode: X86::SEH_SplitChainedAtEndOfBlock));
313 SubFragmentSplits++;
314 Changed = true;
315 };
316
317 unsigned EpilogsInFragment = 0;
318 const EpilogSplitPoint *LastEpilog = nullptr;
319 [[maybe_unused]] unsigned LastEpilogIdx = 0;
320 for (unsigned Idx = 0; Idx < Info.Epilogs.size(); ++Idx) {
321 const EpilogSplitPoint &Epilog = Info.Epilogs[Idx];
322 // If adding this epilog would exceed a fragment limit or is too far, end
323 // the current fragment after the previous epilog and start a new one.
324 if (EpilogsInFragment > 0) {
325 bool ExceedsEpilogCount = EpilogsInFragment >= MaxV3Epilogs;
326 bool ExceedsDistance =
327 Epilog.ApproxBytePos - LastEpilog->ApproxBytePos >=
328 EpilogDistanceThreshold;
329 if (ExceedsEpilogCount || ExceedsDistance) {
330 LLVM_DEBUG({
331 dbgs() << " splitting after epilog " << LastEpilogIdx
332 << " because adding epilog " << Idx << " would exceed the ";
333 if (ExceedsEpilogCount)
334 dbgs() << "7-epilog-per-fragment limit\n";
335 else
336 dbgs() << "epilog distance threshold (gap from previous epilog "
337 "at "
338 << LastEpilog->ApproxBytePos << " to epilog at "
339 << Epilog.ApproxBytePos << ")\n";
340 });
341 SplitAfter(*LastEpilog);
342 EpilogsInFragment = 0;
343 }
344 }
345 EpilogsInFragment++;
346 LastEpilog = &Epilog;
347 LastEpilogIdx = Idx;
348 }
349
350 // If the last epilog is too far from the funclet end, split after it so the
351 // trailing code becomes its own epilog-free chained fragment.
352 if (LastEpilog && Info.EndBytePos - LastEpilog->ApproxBytePos >=
353 EpilogDistanceThreshold) {
354 LLVM_DEBUG(dbgs() << " splitting after last epilog " << LastEpilogIdx
355 << " to isolate the trailing tail (gap from epilog at "
356 << LastEpilog->ApproxBytePos << " to funclet end "
357 << Info.EndBytePos << ")\n");
358 SplitAfter(*LastEpilog);
359 }
360 }
361
362 if (Changed)
363 FunctionsProcessed++;
364
365 return Changed;
366}
367