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 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
26#include "X86.h"
27#include "X86Subtarget.h"
28#include "llvm/ADT/Statistic.h"
35#include "llvm/IR/LLVMContext.h"
36#include "llvm/IR/Module.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.
63 "x86-wineh-unwindv3-instr-avg-size", cl::Hidden,
65 "Average size of an instruction. This value is used in determining "
66 "split points for chained unwinder info"),
67 cl::init(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.
75 for (MachineBasicBlock &MBB : MF) {
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.
120};
121
122class X86WinEHUnwindV3 : public MachineFunctionPass {
123public:
124 static char ID;
125
126 X86WinEHUnwindV3() : MachineFunctionPass(ID) {
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,
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
154 return new X86WinEHUnwindV3();
155}
156
157FuncletInfo X86WinEHUnwindV3::analyzeFunclet(MachineFunction &MF,
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.
175 unsigned MaxPadding = A.value() - 1;
176 if (unsigned MaxBytes = MBB.getMaxBytesForAlignment())
177 MaxPadding = std::min(MaxPadding, 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({&MI, ApproxBytePos});
211 break;
212 case X86::SEH_EndEpilogue:
213 InEpilog = false;
214 Info.MaxEpilogOpCount =
215 std::max(Info.MaxEpilogOpCount, 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 =
245 WinX64EHUnwindMode::V3;
246
247 bool Changed = false;
248 unsigned ApproxBytePos = 0;
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(FuncletEntry, FuncletEntry.begin(),
264 FuncletEntry.findDebugLoc(FuncletEntry.begin()),
265 TII->get(X86::SEH_UnwindVersion))
266 .addImm(3)
268 Changed = true;
269 }
270
271 FuncletInfo Info = analyzeFunclet(MF, Iter, ApproxBytePos);
272
273 if (Info.PrologOpCount > MaxV3PrologOps) {
274 Ctx.diagnose(DiagnosticInfoResourceLimit(
275 F, "number of unwind v3 prolog operations required",
276 Info.PrologOpCount, MaxV3PrologOps, DS_Error, DK_ResourceLimit));
277 Ctx.diagnose(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(DiagnosticInfoResourceLimit(
287 F, "number of unwind v3 epilog operations required",
288 Info.MaxEpilogOpCount, MaxV3EpilogOps, DS_Error, DK_ResourceLimit));
289 Ctx.diagnose(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(*MBB, MBB->begin(), Epilog.BeginEpilog->getDebugLoc(),
313 TII->get(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 >=
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 >=
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}
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
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.
LLVM_ABI DebugLoc findDebugLoc(instr_iterator MBBI)
Find the next valid DebugLoc starting at MBBI, skipping any debug instructions.
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
const MachineInstrBuilder & setMIFlag(MachineInstr::MIFlag Flag) const
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
Representation of each machine instruction.
WinX64EHUnwindMode getWinX64EHUnwindMode() const
Get how unwind information should be generated for x64 Windows.
Definition Module.cpp:976
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
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
bool requireWinX64UnwindV3(const MachineFunction &MF)
Returns true when MF must use Windows x64 Unwind V3: the module is in V3 mode, or the function needs ...
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