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