LLVM 24.0.0git
X86WinEHUnwindV3.cpp
Go to the documentation of this file.
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
23#include "X86.h"
24#include "X86Subtarget.h"
25#include "llvm/ADT/Statistic.h"
32#include "llvm/IR/LLVMContext.h"
33#include "llvm/IR/Module.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.
60 "x86-wineh-unwindv3-instr-avg-size", cl::Hidden,
62 "Average size of an instruction. This value is used in determining "
63 "split points for chained unwinder info"),
64 cl::init(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.
72 for (MachineBasicBlock &MBB : MF) {
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.
117};
118
119class X86WinEHUnwindV3 : public MachineFunctionPass {
120public:
121 static char ID;
122
123 X86WinEHUnwindV3() : MachineFunctionPass(ID) {
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,
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
151 return new X86WinEHUnwindV3();
152}
153
154FuncletInfo X86WinEHUnwindV3::analyzeFunclet(MachineFunction &MF,
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.
172 unsigned MaxPadding = A.value() - 1;
173 if (unsigned MaxBytes = MBB.getMaxBytesForAlignment())
174 MaxPadding = std::min(MaxPadding, 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({&MI, ApproxBytePos});
208 break;
209 case X86::SEH_EndEpilogue:
210 InEpilog = false;
211 Info.MaxEpilogOpCount =
212 std::max(Info.MaxEpilogOpCount, 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) {
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(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;
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(DiagnosticInfoResourceLimit(
266 F, "number of unwind v3 prolog operations required",
267 Info.PrologOpCount, MaxV3PrologOps, DS_Error, DK_ResourceLimit));
268 Ctx.diagnose(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(DiagnosticInfoResourceLimit(
278 F, "number of unwind v3 epilog operations required",
279 Info.MaxEpilogOpCount, MaxV3EpilogOps, DS_Error, DK_ResourceLimit));
280 Ctx.diagnose(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(*MBB, MBB->begin(), Epilog.BeginEpilog->getDebugLoc(),
304 TII->get(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 >=
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 >=
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}
MachineBasicBlock & MBB
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition MD5.cpp:54
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
static cl::opt< RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode > Mode("regalloc-enable-advisor", cl::Hidden, cl::init(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Default), cl::desc("Enable regalloc advisor mode"), cl::values(clEnumValN(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Default, "default", "Default"), clEnumValN(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Release, "release", "precompiled"), clEnumValN(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Development, "development", "for training")))
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
Definition Statistic.h:171
#define LLVM_DEBUG(...)
Definition Debug.h:119
static constexpr unsigned MaxV3PrologOps
V3 limits from the format specification.
static constexpr unsigned MaxV3Epilogs
static constexpr unsigned EpilogDistanceThreshold
static constexpr unsigned MaxV3EpilogOps
static cl::opt< unsigned > ApproxBytesPerInstr("x86-wineh-unwindv3-instr-avg-size", cl::Hidden, cl::desc("Average size of an instruction. This value is used in determining " "split points for chained unwinder info"), cl::init(7))
Approximate byte distance between an epilog and its fragment tail beyond which the funclet is split i...
static void suppressWinCFI(MachineFunction &MF)
After reporting a recoverable error for MF, erase all SEH pseudo- instructions and clear the WinCFI f...
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:353
Module * getParent()
Get the module that this global value is contained inside of...
LLVM_ABI void diagnose(const DiagnosticInfo &DI)
Report a message to the currently installed diagnostic handler.
unsigned getMaxBytesForAlignment() const
Return the maximum amount of padding allowed for aligning the basic block.
bool isEHFuncletEntry() const
Returns true if this is the entry block of an EH funclet.
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
Align getAlignment() const
Return alignment of the basic block.
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
StringRef getName() const
getName - Return the name of the corresponding LLVM function.
Function & getFunction()
Return the LLVM function that this machine code represents.
BasicBlockListType::iterator iterator
Representation of each machine instruction.
WinX64EHUnwindMode getWinX64EHUnwindMode() const
Get how unwind information should be generated for x64 Windows.
Definition Module.cpp:970
static LLVM_ABI PassRegistry * getPassRegistry()
getPassRegistry - Access the global registry object, which is automatically initialized at applicatio...
virtual const TargetInstrInfo * getInstrInfo() const
Changed
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
initializer< Ty > init(const Ty &Val)
This is an optimization pass for GlobalISel generic memory operations.
FunctionPass * createX86WinEHUnwindV3Pass()
Capacity check and sub-fragment splitting for Win x64 Unwind V3.
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:633
@ DK_ResourceLimit
WinX64EHUnwindMode
Definition CodeGen.h:167
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
void initializeX86WinEHUnwindV3Pass(PassRegistry &)
constexpr uint64_t value() const
This is a hole in the type system and should not be abused.
Definition Alignment.h:77