LLVM: lib/Target/X86/X86WinEHUnwindV2.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
25
26using namespace llvm;
27
28#define DEBUG_TYPE "x86-wineh-unwindv2"
29
31 "Number of functions that meet Unwind v2 criteria");
33 "Number of functions that fail Unwind v2 criteria");
34
36 "x86-wineh-unwindv2-max-unwind-codes", cl::Hidden,
37 cl::desc("Maximum number of unwind codes permitted in each unwind info."),
39
42 cl::desc("Overwrites the Unwind v2 mode for testing purposes."));
43
44namespace {
45
47public:
48 static char ID;
49
52 }
53
54 StringRef getPassName() const override { return "WinEH Unwind V2"; }
55
56 bool runOnMachineFunction(MachineFunction &MF) override;
57
58private:
59
60 static bool rejectCurrentFunctionInternalError(const MachineFunction &MF,
62 StringRef Reason);
63};
64
65enum class FunctionState {
66 InProlog,
67 HasProlog,
68 InEpilog,
69 FinishedEpilog,
70};
71
72}
73
74char X86WinEHUnwindV2::ID = 0;
75
77 "Analyze and emit instructions for Win64 Unwind v2", false,
78 false)
79
81 return new X86WinEHUnwindV2();
82}
83
86 if (MI.getDebugLoc())
87 return MI.getDebugLoc();
88
90}
91
92bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) {
97
98 if (Mode == WinX64EHUnwindV2Mode::Disabled)
99 return false;
100
101
102
103 FunctionState State = FunctionState::InProlog;
104
105
107 bool HasStackAlloc = false;
108 bool HasSetFrame = false;
109 unsigned ApproximatePrologCodeCount = 0;
110
111
113
114 for (MachineBasicBlock &MBB : MF) {
115
116
117 unsigned PoppedRegCount = 0;
118 bool HasStackDealloc = false;
119 MachineInstr *UnwindV2StartLocation = nullptr;
120
121 for (MachineInstr &MI : MBB) {
122 switch (MI.getOpcode()) {
123
124
125
126 case X86::SEH_PushReg:
127 if (State != FunctionState::InProlog)
129 ApproximatePrologCodeCount++;
130 PushedRegs.push_back(MI.getOperand(0).getImm());
131 break;
132
133 case X86::SEH_StackAlloc:
134 if (State != FunctionState::InProlog)
136
137 ApproximatePrologCodeCount += 3;
138 HasStackAlloc = true;
139 break;
140
141 case X86::SEH_SetFrame:
142 if (State != FunctionState::InProlog)
144 ApproximatePrologCodeCount++;
145 HasSetFrame = true;
146 break;
147
148 case X86::SEH_SaveReg:
149 case X86::SEH_SaveXMM:
150 if (State != FunctionState::InProlog)
151 llvm_unreachable("SEH_SaveXMM or SEH_SaveReg outside of prolog");
152
153 ApproximatePrologCodeCount += 3;
154 break;
155
156 case X86::SEH_PushFrame:
157 if (State != FunctionState::InProlog)
159 ApproximatePrologCodeCount++;
160 break;
161
162 case X86::SEH_EndPrologue:
163 if (State != FunctionState::InProlog)
165 State = FunctionState::HasProlog;
166 break;
167
168
169
170
171 case X86::SEH_BeginEpilogue:
172 if (State != FunctionState::HasProlog)
173 llvm_unreachable("SEH_BeginEpilogue in prolog or another epilog");
174 State = FunctionState::InEpilog;
175 break;
176
177 case X86::SEH_EndEpilogue:
178 if (State != FunctionState::InEpilog)
180 if (HasStackAlloc != HasStackDealloc)
181 return rejectCurrentFunctionInternalError(
183 "The prolog made a stack allocation, "
184 "but the epilog did not deallocate it");
185 if (PoppedRegCount != PushedRegs.size())
186 return rejectCurrentFunctionInternalError(
188 "The prolog pushed more registers than "
189 "the epilog popped");
190
191
192
193 if (!UnwindV2StartLocation)
194 UnwindV2StartLocation = &MI;
195 UnwindV2StartLocations.push_back(UnwindV2StartLocation);
196 State = FunctionState::FinishedEpilog;
197 break;
198
199 case X86::MOV64rr:
200 if (State == FunctionState::InEpilog) {
201
202
203 if (!HasSetFrame)
204 return rejectCurrentFunctionInternalError(
206 "The epilog is setting frame back, but prolog did not set it");
207 if (PoppedRegCount > 0)
208 return rejectCurrentFunctionInternalError(
210 "The epilog is setting the frame back after popping "
211 "registers");
212 if (HasStackDealloc)
213 return rejectCurrentFunctionInternalError(
215 "Cannot set the frame back after the stack "
216 "allocation has been deallocated");
217 } else if (State == FunctionState::FinishedEpilog)
218 return rejectCurrentFunctionInternalError(
219 MF, Mode, "Unexpected mov instruction after the epilog");
220 break;
221
222 case X86::LEA64r:
223 case X86::ADD64ri32:
224 if (State == FunctionState::InEpilog) {
225
226
227 if (!HasStackAlloc)
228 return rejectCurrentFunctionInternalError(
230 "The epilog is deallocating a stack "
231 "allocation, but the prolog did "
232 "not allocate one");
233 if (PoppedRegCount > 0)
234 return rejectCurrentFunctionInternalError(
236 "The epilog is deallocating a stack allocation after popping "
237 "registers");
238
239 HasStackDealloc = true;
240 } else if (State == FunctionState::FinishedEpilog)
241 return rejectCurrentFunctionInternalError(
242 MF, Mode, "Unexpected lea or add instruction after the epilog");
243 break;
244
245 case X86::POP64r:
246 if (State == FunctionState::InEpilog) {
248 if (HasStackAlloc && (PoppedRegCount == 0) &&
250
251
252 HasStackDealloc = true;
253 } else {
254
255
256 PoppedRegCount++;
257 if (HasStackAlloc != HasStackDealloc)
258 return rejectCurrentFunctionInternalError(
260 "Cannot pop registers before the stack "
261 "allocation has been deallocated");
262 if (PoppedRegCount > PushedRegs.size())
263 return rejectCurrentFunctionInternalError(
265 "The epilog is popping more registers than the prolog "
266 "pushed");
267 if (PushedRegs[PushedRegs.size() - PoppedRegCount] != Reg.id())
268 return rejectCurrentFunctionInternalError(
270 "The epilog is popping a registers in "
271 "a different order than the "
272 "prolog pushed them");
273
274
275
276
277
278 if (!UnwindV2StartLocation) {
279 assert(PoppedRegCount == 1);
280 UnwindV2StartLocation = &MI;
281 }
282 }
283 } else if (State == FunctionState::FinishedEpilog)
284
285 return rejectCurrentFunctionInternalError(
286 MF, Mode, "Registers are being popped after the epilog");
287 break;
288
289 default:
290 if (MI.isTerminator()) {
291 if (State == FunctionState::FinishedEpilog)
292
293
294 State = FunctionState::HasProlog;
295 else if (State == FunctionState::InEpilog)
297 } else if (.isDebugOrPseudoInstr()) {
298 if ((State == FunctionState::FinishedEpilog) ||
299 (State == FunctionState::InEpilog))
300
301 return rejectCurrentFunctionInternalError(
302 MF, Mode, "Unexpected instruction in or after the epilog");
303 }
304 }
305 }
306 }
307
308 if (UnwindV2StartLocations.empty())
309 return false;
310
311 MachineBasicBlock &FirstMBB = MF.front();
312
313
314
315 if (ApproximatePrologCodeCount + UnwindV2StartLocations.size() + 1 >
317 if (Mode == WinX64EHUnwindV2Mode::Required)
318 MF.getFunction().getContext().diagnose(DiagnosticInfoGenericWithLoc(
319 "Windows x64 Unwind v2 is required, but the function '" +
320 MF.getName() +
321 "' has too many unwind codes. Try splitting the function or "
322 "reducing the number of places where it exits early with a tail "
323 "call.",
325
326 FailsUnwindV2Criteria++;
327 return false;
328 }
329
330 MeetsUnwindV2Criteria++;
331
332
333 const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
334 for (MachineInstr *MI : UnwindV2StartLocations) {
336 TII->get(X86::SEH_UnwindV2Start));
337 }
338
340 TII->get(X86::SEH_UnwindVersion))
342
343 return true;
344}
345
346bool X86WinEHUnwindV2::rejectCurrentFunctionInternalError(
348 if (Mode == WinX64EHUnwindV2Mode::Required)
350 "generated incompatible code in function '" +
351 MF.getName() + "': " + Reason);
352
353 FailsUnwindV2Criteria++;
354 return false;
355}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
const TargetInstrInfo & TII
Module.h This file contains the declarations for the Module class.
Promote Memory to Register
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
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)
DebugLoc findDebugLoc(const MachineBasicBlock &MBB)
Definition X86WinEHUnwindV2.cpp:84
static cl::opt< unsigned > ForceMode("x86-wineh-unwindv2-force-mode", cl::Hidden, cl::desc("Overwrites the Unwind v2 mode for testing purposes."))
static cl::opt< unsigned > MaximumUnwindCodes("x86-wineh-unwindv2-max-unwind-codes", cl::Hidden, cl::desc("Maximum number of unwind codes permitted in each unwind info."), cl::init(UINT8_MAX))
static DebugLoc getUnknown()
FunctionPass class - This class is used to implement most global optimizations.
Module * getParent()
Get the module that this global value is contained inside of...
const MCInstrDesc & get(unsigned Opcode) const
Return the machine instruction descriptor that corresponds to the specified instruction opcode.
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
StringRef getName() const
getName - Return the name of the corresponding LLVM function.
Function & getFunction()
Return the LLVM function that this machine code represents.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
Representation of each machine instruction.
WinX64EHUnwindV2Mode getWinX64EHUnwindV2Mode() const
Get how unwind v2 (epilog) information should be generated for x64 Windows.
static LLVM_ABI PassRegistry * getPassRegistry()
getPassRegistry - Access the global registry object, which is automatically initialized at applicatio...
constexpr unsigned id() const
void push_back(const T &Elt)
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
initializer< Ty > init(const Ty &Val)
This is an optimization pass for GlobalISel generic memory operations.
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
LLVM_ABI void reportFatalInternalError(Error Err)
Report a fatal error that indicates a bug in LLVM.
void initializeX86WinEHUnwindV2Pass(PassRegistry &)
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
FunctionPass * createX86WinEHUnwindV2Pass()
// Analyzes and emits pseudos to support Win x64 Unwind V2.
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.