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"
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.
67 for (MachineBasicBlock &MBB : MF) {
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.
112};
113
114class X86WinEHUnwindV3 : public MachineFunctionPass {
115public:
116 static char ID;
117
118 X86WinEHUnwindV3() : MachineFunctionPass(ID) {
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,
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
146 return new X86WinEHUnwindV3();
147}
148
149FuncletInfo X86WinEHUnwindV3::analyzeFunclet(MachineFunction &MF,
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.
169 unsigned MaxPadding = A.value() - 1;
170 if (unsigned MaxBytes = MBB.getMaxBytesForAlignment())
171 MaxPadding = std::min(MaxPadding, 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({&MI, ApproxBytePos});
205 break;
206 case X86::SEH_EndEpilogue:
207 InEpilog = false;
208 Info.MaxEpilogOpCount =
209 std::max(Info.MaxEpilogOpCount, 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 =
239 WinX64EHUnwindMode::V3;
240
241 bool Changed = false;
242 unsigned ApproxBytePos = 0;
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(FuncletEntry, FuncletEntry.begin(),
258 FuncletEntry.findDebugLoc(FuncletEntry.begin()),
259 TII->get(X86::SEH_UnwindVersion))
260 .addImm(3)
262 Changed = true;
263 }
264
265 FuncletInfo Info = analyzeFunclet(MF, Iter, ApproxBytePos);
266
267 if (Info.PrologOpCount > MaxV3PrologOps) {
268 Ctx.diagnose(DiagnosticInfoResourceLimit(
269 F, "number of unwind v3 prolog operations required",
270 Info.PrologOpCount, MaxV3PrologOps, DS_Error, DK_ResourceLimit));
271 Ctx.diagnose(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(DiagnosticInfoResourceLimit(
281 F, "number of unwind v3 epilog operations required",
282 Info.MaxEpilogOpCount, MaxV3EpilogOps, DS_Error, DK_ResourceLimit));
283 Ctx.diagnose(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(*MBB, MBB->begin(), Epilog.BeginEpilog->getDebugLoc(),
307 TII->get(X86::SEH_SplitChainedAtEndOfBlock))
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 >=
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 >=
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}
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
Approximate byte distance between an epilog and its fragment tail beyond which the funclet is split i...
static constexpr unsigned MaxV3EpilogOps
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:1012
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.
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:649
@ 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:82