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