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