LLVM 23.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. Unlike the V2 pass, V3 does not need to validate
11/// epilog structure (V3 can encode any prolog/epilog pattern). This pass
12/// only needs to:
13/// 1. Count prolog/epilog operations and epilogs.
14/// 2. Check V3 capacity limits (<=31 prolog/epilog ops, <=7 epilogs).
15/// 3. Insert sub-fragment split points if limits are exceeded.
16///
17/// The unwind version is set module-wide, not per-function.
18///
19/// See https://learn.microsoft.com/en-us/cpp/build/x64-unwind-information-v3
20///
21//===----------------------------------------------------------------------===//
22
24#include "X86.h"
25#include "X86Subtarget.h"
26#include "llvm/ADT/Statistic.h"
33#include "llvm/IR/LLVMContext.h"
34#include "llvm/IR/Module.h"
35
36using namespace llvm;
37
38#define DEBUG_TYPE "x86-wineh-unwindv3"
39
40STATISTIC(FunctionsProcessed,
41 "Number of functions processed by Unwind v3 pass");
42STATISTIC(SubFragmentSplits,
43 "Number of sub-fragment splits inserted for Unwind v3");
44
45/// V3 limits from the format specification.
46static constexpr unsigned MaxV3PrologOps = 31;
47static constexpr unsigned MaxV3Epilogs = 7;
48static constexpr unsigned MaxV3EpilogOps = 31;
49
50/// After reporting a recoverable error for `MF`, erase all SEH pseudo-
51/// instructions and clear the WinCFI flag so the AsmPrinter doesn't try to
52/// emit (potentially malformed) unwind information. The LLVMContext
53/// diagnostic recorded by the caller will prevent the object file from
54/// actually being written.
56 for (MachineBasicBlock &MBB : MF) {
58 switch (MI.getOpcode()) {
59 case X86::SEH_PushReg:
60 case X86::SEH_Push2Regs:
61 case X86::SEH_SaveReg:
62 case X86::SEH_SaveXMM:
63 case X86::SEH_StackAlloc:
64 case X86::SEH_StackAlign:
65 case X86::SEH_SetFrame:
66 case X86::SEH_PushFrame:
67 case X86::SEH_EndPrologue:
68 case X86::SEH_BeginEpilogue:
69 case X86::SEH_EndEpilogue:
70 case X86::SEH_SplitChained:
71 case X86::SEH_SplitChainedAtEndOfBlock:
72 MI.eraseFromParent();
73 break;
74 default:
75 break;
76 }
77 }
78 }
79 MF.setHasWinCFI(false);
80}
81
82namespace {
83
84/// Per-funclet analysis results.
85struct FuncletInfo {
86 unsigned PrologOpCount = 0;
87 unsigned EpilogCount = 0;
88 unsigned MaxEpilogOpCount = 0;
89 /// SEH_BeginEpilogue instructions, used as insertion points for splitting.
90 SmallVector<MachineInstr *, 8> EpilogBegins;
91};
92
93class X86WinEHUnwindV3 : public MachineFunctionPass {
94public:
95 static char ID;
96
97 X86WinEHUnwindV3() : MachineFunctionPass(ID) {
99 }
100
101 StringRef getPassName() const override { return "WinEH Unwind V3"; }
102
103 bool runOnMachineFunction(MachineFunction &MF) override;
104
105private:
106 /// Analyze one funclet (or the main function body) starting at Iter.
107 /// Advances Iter past the analyzed region, stopping at the next funclet
108 /// entry or the end of the function.
109 static FuncletInfo analyzeFunclet(MachineFunction &MF,
111};
112
113} // end anonymous namespace
114
115char X86WinEHUnwindV3::ID = 0;
116
117INITIALIZE_PASS(X86WinEHUnwindV3, "x86-wineh-unwindv3",
118 "Capacity check and sub-fragment splitting for Win64 Unwind v3",
119 false, false)
120
122 return new X86WinEHUnwindV3();
123}
124
125FuncletInfo X86WinEHUnwindV3::analyzeFunclet(MachineFunction &MF,
127 FuncletInfo Info;
128 bool InEpilog = false;
129 bool SeenProlog = false;
130 unsigned CurrentEpilogOpCount = 0;
131
132 for (; Iter != MF.end(); ++Iter) {
133 MachineBasicBlock &MBB = *Iter;
134
135 // If we've already been processing a funclet's prolog/body and encounter
136 // another funclet entry, stop - that funclet gets its own analysis.
137 if (MBB.isEHFuncletEntry() && SeenProlog)
138 break;
139
140 for (MachineInstr &MI : MBB) {
141 switch (MI.getOpcode()) {
142 case X86::SEH_PushReg:
143 case X86::SEH_Push2Regs:
144 case X86::SEH_StackAlloc:
145 case X86::SEH_SetFrame:
146 case X86::SEH_SaveReg:
147 case X86::SEH_SaveXMM:
148 case X86::SEH_PushFrame:
149 if (InEpilog)
150 CurrentEpilogOpCount++;
151 else
152 Info.PrologOpCount++;
153 break;
154 case X86::SEH_EndPrologue:
155 SeenProlog = true;
156 break;
157 case X86::SEH_BeginEpilogue:
158 InEpilog = true;
159 CurrentEpilogOpCount = 0;
160 Info.EpilogCount++;
161 Info.EpilogBegins.push_back(&MI);
162 break;
163 case X86::SEH_EndEpilogue:
164 InEpilog = false;
165 Info.MaxEpilogOpCount =
166 std::max(Info.MaxEpilogOpCount, CurrentEpilogOpCount);
167 break;
168 default:
169 break;
170 }
171 }
172 }
173
174 return Info;
175}
176
177bool X86WinEHUnwindV3::runOnMachineFunction(MachineFunction &MF) {
180
181 Function &F = MF.getFunction();
182 LLVMContext &Ctx = F.getContext();
183
184 // EGPR (R16-R31) requires V3 unwind info because V1/V2 cannot encode
185 // registers beyond R15. Only enforce this for functions that actually
186 // emit SEH unwind info — `nounwind` functions and targets that don't
187 // require unwind tables (e.g. cross-compilation host defaults) can use
188 // EGPR with any unwind mode since no SEH metadata is generated.
189 if (Mode != WinX64EHUnwindMode::V3) {
190 if (!F.needsUnwindTableEntry())
191 return false;
192 const auto &STI = MF.getSubtarget<X86Subtarget>();
193 if (STI.hasEGPR()) {
194 Ctx.diagnose(DiagnosticInfoUnsupported(
195 F, "EGPR (R16-R31) requires V3 unwind info on Windows x64"));
196 // Stripping the SEH pseudos modifies the function, so report a change.
197 suppressWinCFI(MF);
198 return true;
199 }
200 return false;
201 }
202
203 bool Changed = false;
205
206 // Process each funclet (and the main function body) independently.
207 // Each funclet gets its own UNWIND_INFO, so V3 limits apply per funclet.
208 while (Iter != MF.end()) {
209 FuncletInfo Info = analyzeFunclet(MF, Iter);
210
211 if (Info.PrologOpCount > MaxV3PrologOps) {
212 Ctx.diagnose(DiagnosticInfoResourceLimit(
213 F, "number of unwind v3 prolog operations required",
214 Info.PrologOpCount, MaxV3PrologOps, DS_Error, DK_ResourceLimit));
215 Ctx.diagnose(DiagnosticInfoGenericWithLoc(
216 "sub-fragment splitting for prolog overflow is not yet implemented",
217 F, F.getSubprogram(), DS_Note));
218 // Stripping the SEH pseudos modifies the function, so report a change.
219 suppressWinCFI(MF);
220 return true;
221 }
222
223 if (Info.MaxEpilogOpCount > MaxV3EpilogOps) {
224 Ctx.diagnose(DiagnosticInfoResourceLimit(
225 F, "number of unwind v3 epilog operations required",
226 Info.MaxEpilogOpCount, MaxV3EpilogOps, DS_Error, DK_ResourceLimit));
227 Ctx.diagnose(DiagnosticInfoGenericWithLoc(
228 "sub-fragment splitting for epilog overflow is not yet implemented",
229 F, F.getSubprogram(), DS_Note));
230 // Stripping the SEH pseudos modifies the function, so report a change.
231 suppressWinCFI(MF);
232 return true;
233 }
234
235 if (Info.EpilogCount > MaxV3Epilogs) {
236 const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
237 unsigned Count = 0;
238 for (MachineInstr *BeginEpilog : Info.EpilogBegins) {
239 Count++;
240 if (Count > MaxV3Epilogs) {
241 MachineBasicBlock *MBB = BeginEpilog->getParent();
242 BuildMI(*MBB, BeginEpilog, BeginEpilog->getDebugLoc(),
243 TII->get(X86::SEH_SplitChained));
244 BuildMI(*MBB, BeginEpilog, BeginEpilog->getDebugLoc(),
245 TII->get(X86::SEH_EndPrologue));
246 SubFragmentSplits++;
247 Count = 1;
248 }
249 }
250 Changed = true;
251 }
252 }
253
254 if (Changed)
255 FunctionsProcessed++;
256
257 return Changed;
258}
MachineBasicBlock & MBB
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
static constexpr unsigned MaxV3PrologOps
V3 limits from the format specification.
static constexpr unsigned MaxV3Epilogs
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:354
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.
bool isEHFuncletEntry() const
Returns true if this is the entry block of an EH funclet.
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.
Function & getFunction()
Return the LLVM function that this machine code represents.
BasicBlockListType::iterator iterator
Representation of each machine instruction.
const MachineBasicBlock * getParent() const
const DebugLoc & getDebugLoc() const
Returns the debug location id of this MachineInstr.
WinX64EHUnwindMode getWinX64EHUnwindMode() const
Get how unwind information should be generated for x64 Windows.
Definition Module.cpp:960
static LLVM_ABI PassRegistry * getPassRegistry()
getPassRegistry - Access the global registry object, which is automatically initialized at applicatio...
virtual const TargetInstrInfo * getInstrInfo() const
Changed
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
FunctionAddr VTableAddr Count
Definition InstrProf.h:139
void initializeX86WinEHUnwindV3Pass(PassRegistry &)