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