xref: /freebsd/contrib/llvm-project/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1*700637cbSDimitry Andric //===-- X86WinEHUnwindV2.cpp - Win x64 Unwind v2 ----------------*- C++ -*-===//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
8*700637cbSDimitry Andric ///
9*700637cbSDimitry Andric /// Implements the analysis required to detect if a function can use Unwind v2
10*700637cbSDimitry Andric /// information, and emits the neccesary pseudo instructions used by MC to
11*700637cbSDimitry Andric /// generate the unwind info.
12*700637cbSDimitry Andric ///
13*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
14*700637cbSDimitry Andric 
15*700637cbSDimitry Andric #include "MCTargetDesc/X86BaseInfo.h"
16*700637cbSDimitry Andric #include "X86.h"
17*700637cbSDimitry Andric #include "llvm/ADT/Statistic.h"
18*700637cbSDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h"
19*700637cbSDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
20*700637cbSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
21*700637cbSDimitry Andric #include "llvm/CodeGen/TargetInstrInfo.h"
22*700637cbSDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
23*700637cbSDimitry Andric #include "llvm/IR/DiagnosticInfo.h"
24*700637cbSDimitry Andric #include "llvm/IR/Module.h"
25*700637cbSDimitry Andric 
26*700637cbSDimitry Andric using namespace llvm;
27*700637cbSDimitry Andric 
28*700637cbSDimitry Andric #define DEBUG_TYPE "x86-wineh-unwindv2"
29*700637cbSDimitry Andric 
30*700637cbSDimitry Andric STATISTIC(MeetsUnwindV2Criteria,
31*700637cbSDimitry Andric           "Number of functions that meet Unwind v2 criteria");
32*700637cbSDimitry Andric STATISTIC(FailsUnwindV2Criteria,
33*700637cbSDimitry Andric           "Number of functions that fail Unwind v2 criteria");
34*700637cbSDimitry Andric 
35*700637cbSDimitry Andric static cl::opt<unsigned> MaximumUnwindCodes(
36*700637cbSDimitry Andric     "x86-wineh-unwindv2-max-unwind-codes", cl::Hidden,
37*700637cbSDimitry Andric     cl::desc("Maximum number of unwind codes permitted in each unwind info."),
38*700637cbSDimitry Andric     cl::init(UINT8_MAX));
39*700637cbSDimitry Andric 
40*700637cbSDimitry Andric static cl::opt<unsigned>
41*700637cbSDimitry Andric     ForceMode("x86-wineh-unwindv2-force-mode", cl::Hidden,
42*700637cbSDimitry Andric               cl::desc("Overwrites the Unwind v2 mode for testing purposes."));
43*700637cbSDimitry Andric 
44*700637cbSDimitry Andric namespace {
45*700637cbSDimitry Andric 
46*700637cbSDimitry Andric class X86WinEHUnwindV2 : public MachineFunctionPass {
47*700637cbSDimitry Andric public:
48*700637cbSDimitry Andric   static char ID;
49*700637cbSDimitry Andric 
X86WinEHUnwindV2()50*700637cbSDimitry Andric   X86WinEHUnwindV2() : MachineFunctionPass(ID) {
51*700637cbSDimitry Andric     initializeX86WinEHUnwindV2Pass(*PassRegistry::getPassRegistry());
52*700637cbSDimitry Andric   }
53*700637cbSDimitry Andric 
getPassName() const54*700637cbSDimitry Andric   StringRef getPassName() const override { return "WinEH Unwind V2"; }
55*700637cbSDimitry Andric 
56*700637cbSDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
57*700637cbSDimitry Andric 
58*700637cbSDimitry Andric private:
59*700637cbSDimitry Andric   /// Rejects the current function due to an internal error within LLVM.
60*700637cbSDimitry Andric   static bool rejectCurrentFunctionInternalError(const MachineFunction &MF,
61*700637cbSDimitry Andric                                                  WinX64EHUnwindV2Mode Mode,
62*700637cbSDimitry Andric                                                  StringRef Reason);
63*700637cbSDimitry Andric };
64*700637cbSDimitry Andric 
65*700637cbSDimitry Andric enum class FunctionState {
66*700637cbSDimitry Andric   InProlog,
67*700637cbSDimitry Andric   HasProlog,
68*700637cbSDimitry Andric   InEpilog,
69*700637cbSDimitry Andric   FinishedEpilog,
70*700637cbSDimitry Andric };
71*700637cbSDimitry Andric 
72*700637cbSDimitry Andric } // end anonymous namespace
73*700637cbSDimitry Andric 
74*700637cbSDimitry Andric char X86WinEHUnwindV2::ID = 0;
75*700637cbSDimitry Andric 
76*700637cbSDimitry Andric INITIALIZE_PASS(X86WinEHUnwindV2, "x86-wineh-unwindv2",
77*700637cbSDimitry Andric                 "Analyze and emit instructions for Win64 Unwind v2", false,
78*700637cbSDimitry Andric                 false)
79*700637cbSDimitry Andric 
createX86WinEHUnwindV2Pass()80*700637cbSDimitry Andric FunctionPass *llvm::createX86WinEHUnwindV2Pass() {
81*700637cbSDimitry Andric   return new X86WinEHUnwindV2();
82*700637cbSDimitry Andric }
83*700637cbSDimitry Andric 
findDebugLoc(const MachineBasicBlock & MBB)84*700637cbSDimitry Andric DebugLoc findDebugLoc(const MachineBasicBlock &MBB) {
85*700637cbSDimitry Andric   for (const MachineInstr &MI : MBB)
86*700637cbSDimitry Andric     if (MI.getDebugLoc())
87*700637cbSDimitry Andric       return MI.getDebugLoc();
88*700637cbSDimitry Andric 
89*700637cbSDimitry Andric   return DebugLoc::getUnknown();
90*700637cbSDimitry Andric }
91*700637cbSDimitry Andric 
runOnMachineFunction(MachineFunction & MF)92*700637cbSDimitry Andric bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) {
93*700637cbSDimitry Andric   WinX64EHUnwindV2Mode Mode =
94*700637cbSDimitry Andric       ForceMode.getNumOccurrences()
95*700637cbSDimitry Andric           ? static_cast<WinX64EHUnwindV2Mode>(ForceMode.getValue())
96*700637cbSDimitry Andric           : MF.getFunction().getParent()->getWinX64EHUnwindV2Mode();
97*700637cbSDimitry Andric 
98*700637cbSDimitry Andric   if (Mode == WinX64EHUnwindV2Mode::Disabled)
99*700637cbSDimitry Andric     return false;
100*700637cbSDimitry Andric 
101*700637cbSDimitry Andric   // Current state of processing the function. We'll assume that all functions
102*700637cbSDimitry Andric   // start with a prolog.
103*700637cbSDimitry Andric   FunctionState State = FunctionState::InProlog;
104*700637cbSDimitry Andric 
105*700637cbSDimitry Andric   // Prolog information.
106*700637cbSDimitry Andric   SmallVector<int64_t> PushedRegs;
107*700637cbSDimitry Andric   bool HasStackAlloc = false;
108*700637cbSDimitry Andric   unsigned ApproximatePrologCodeCount = 0;
109*700637cbSDimitry Andric 
110*700637cbSDimitry Andric   // Requested changes.
111*700637cbSDimitry Andric   SmallVector<MachineInstr *> UnwindV2StartLocations;
112*700637cbSDimitry Andric 
113*700637cbSDimitry Andric   for (MachineBasicBlock &MBB : MF) {
114*700637cbSDimitry Andric     // Current epilog information. We assume that epilogs cannot cross basic
115*700637cbSDimitry Andric     // block boundaries.
116*700637cbSDimitry Andric     unsigned PoppedRegCount = 0;
117*700637cbSDimitry Andric     bool HasStackDealloc = false;
118*700637cbSDimitry Andric     MachineInstr *UnwindV2StartLocation = nullptr;
119*700637cbSDimitry Andric 
120*700637cbSDimitry Andric     for (MachineInstr &MI : MBB) {
121*700637cbSDimitry Andric       switch (MI.getOpcode()) {
122*700637cbSDimitry Andric       //
123*700637cbSDimitry Andric       // Prolog handling.
124*700637cbSDimitry Andric       //
125*700637cbSDimitry Andric       case X86::SEH_PushReg:
126*700637cbSDimitry Andric         if (State != FunctionState::InProlog)
127*700637cbSDimitry Andric           llvm_unreachable("SEH_PushReg outside of prolog");
128*700637cbSDimitry Andric         ApproximatePrologCodeCount++;
129*700637cbSDimitry Andric         PushedRegs.push_back(MI.getOperand(0).getImm());
130*700637cbSDimitry Andric         break;
131*700637cbSDimitry Andric 
132*700637cbSDimitry Andric       case X86::SEH_StackAlloc:
133*700637cbSDimitry Andric       case X86::SEH_SetFrame:
134*700637cbSDimitry Andric         if (State != FunctionState::InProlog)
135*700637cbSDimitry Andric           llvm_unreachable("SEH_StackAlloc or SEH_SetFrame outside of prolog");
136*700637cbSDimitry Andric         // Assume a large alloc...
137*700637cbSDimitry Andric         ApproximatePrologCodeCount +=
138*700637cbSDimitry Andric             (MI.getOpcode() == X86::SEH_StackAlloc) ? 3 : 1;
139*700637cbSDimitry Andric         HasStackAlloc = true;
140*700637cbSDimitry Andric         break;
141*700637cbSDimitry Andric 
142*700637cbSDimitry Andric       case X86::SEH_SaveReg:
143*700637cbSDimitry Andric       case X86::SEH_SaveXMM:
144*700637cbSDimitry Andric         if (State != FunctionState::InProlog)
145*700637cbSDimitry Andric           llvm_unreachable("SEH_SaveXMM or SEH_SaveReg outside of prolog");
146*700637cbSDimitry Andric         // Assume a big reg...
147*700637cbSDimitry Andric         ApproximatePrologCodeCount += 3;
148*700637cbSDimitry Andric         break;
149*700637cbSDimitry Andric 
150*700637cbSDimitry Andric       case X86::SEH_PushFrame:
151*700637cbSDimitry Andric         if (State != FunctionState::InProlog)
152*700637cbSDimitry Andric           llvm_unreachable("SEH_PushFrame outside of prolog");
153*700637cbSDimitry Andric         ApproximatePrologCodeCount++;
154*700637cbSDimitry Andric         break;
155*700637cbSDimitry Andric 
156*700637cbSDimitry Andric       case X86::SEH_EndPrologue:
157*700637cbSDimitry Andric         if (State != FunctionState::InProlog)
158*700637cbSDimitry Andric           llvm_unreachable("SEH_EndPrologue outside of prolog");
159*700637cbSDimitry Andric         State = FunctionState::HasProlog;
160*700637cbSDimitry Andric         break;
161*700637cbSDimitry Andric 
162*700637cbSDimitry Andric       //
163*700637cbSDimitry Andric       // Epilog handling.
164*700637cbSDimitry Andric       //
165*700637cbSDimitry Andric       case X86::SEH_BeginEpilogue:
166*700637cbSDimitry Andric         if (State != FunctionState::HasProlog)
167*700637cbSDimitry Andric           llvm_unreachable("SEH_BeginEpilogue in prolog or another epilog");
168*700637cbSDimitry Andric         State = FunctionState::InEpilog;
169*700637cbSDimitry Andric         break;
170*700637cbSDimitry Andric 
171*700637cbSDimitry Andric       case X86::SEH_EndEpilogue:
172*700637cbSDimitry Andric         if (State != FunctionState::InEpilog)
173*700637cbSDimitry Andric           llvm_unreachable("SEH_EndEpilogue outside of epilog");
174*700637cbSDimitry Andric         if (HasStackAlloc != HasStackDealloc)
175*700637cbSDimitry Andric           return rejectCurrentFunctionInternalError(
176*700637cbSDimitry Andric               MF, Mode,
177*700637cbSDimitry Andric               "The prolog made a stack allocation, "
178*700637cbSDimitry Andric               "but the epilog did not deallocate it");
179*700637cbSDimitry Andric         if (PoppedRegCount != PushedRegs.size())
180*700637cbSDimitry Andric           return rejectCurrentFunctionInternalError(
181*700637cbSDimitry Andric               MF, Mode,
182*700637cbSDimitry Andric               "The prolog pushed more registers than "
183*700637cbSDimitry Andric               "the epilog popped");
184*700637cbSDimitry Andric 
185*700637cbSDimitry Andric         // If we didn't find the start location, then use the end of the
186*700637cbSDimitry Andric         // epilog.
187*700637cbSDimitry Andric         if (!UnwindV2StartLocation)
188*700637cbSDimitry Andric           UnwindV2StartLocation = &MI;
189*700637cbSDimitry Andric         UnwindV2StartLocations.push_back(UnwindV2StartLocation);
190*700637cbSDimitry Andric         State = FunctionState::FinishedEpilog;
191*700637cbSDimitry Andric         break;
192*700637cbSDimitry Andric 
193*700637cbSDimitry Andric       case X86::MOV64rr:
194*700637cbSDimitry Andric       case X86::ADD64ri32:
195*700637cbSDimitry Andric         if (State == FunctionState::InEpilog) {
196*700637cbSDimitry Andric           // If the prolog contains a stack allocation, then the first
197*700637cbSDimitry Andric           // instruction in the epilog must be to adjust the stack pointer.
198*700637cbSDimitry Andric           if (!HasStackAlloc)
199*700637cbSDimitry Andric             return rejectCurrentFunctionInternalError(
200*700637cbSDimitry Andric                 MF, Mode,
201*700637cbSDimitry Andric                 "The epilog is deallocating a stack "
202*700637cbSDimitry Andric                 "allocation, but the prolog did "
203*700637cbSDimitry Andric                 "not allocate one");
204*700637cbSDimitry Andric           if (HasStackDealloc)
205*700637cbSDimitry Andric             return rejectCurrentFunctionInternalError(
206*700637cbSDimitry Andric                 MF, Mode,
207*700637cbSDimitry Andric                 "The epilog is deallocating the stack "
208*700637cbSDimitry Andric                 "allocation more than once");
209*700637cbSDimitry Andric           if (PoppedRegCount > 0)
210*700637cbSDimitry Andric             llvm_unreachable(
211*700637cbSDimitry Andric                 "Should have raised an error: either popping before "
212*700637cbSDimitry Andric                 "deallocating or deallocating without an allocation");
213*700637cbSDimitry Andric 
214*700637cbSDimitry Andric           HasStackDealloc = true;
215*700637cbSDimitry Andric         } else if (State == FunctionState::FinishedEpilog)
216*700637cbSDimitry Andric           return rejectCurrentFunctionInternalError(
217*700637cbSDimitry Andric               MF, Mode, "Unexpected mov or add instruction after the epilog");
218*700637cbSDimitry Andric         break;
219*700637cbSDimitry Andric 
220*700637cbSDimitry Andric       case X86::POP64r:
221*700637cbSDimitry Andric         if (State == FunctionState::InEpilog) {
222*700637cbSDimitry Andric           // After the stack pointer has been adjusted, the epilog must
223*700637cbSDimitry Andric           // POP each register in reverse order of the PUSHes in the prolog.
224*700637cbSDimitry Andric           PoppedRegCount++;
225*700637cbSDimitry Andric           if (HasStackAlloc != HasStackDealloc)
226*700637cbSDimitry Andric             return rejectCurrentFunctionInternalError(
227*700637cbSDimitry Andric                 MF, Mode,
228*700637cbSDimitry Andric                 "Cannot pop registers before the stack "
229*700637cbSDimitry Andric                 "allocation has been deallocated");
230*700637cbSDimitry Andric           if (PoppedRegCount > PushedRegs.size())
231*700637cbSDimitry Andric             return rejectCurrentFunctionInternalError(
232*700637cbSDimitry Andric                 MF, Mode,
233*700637cbSDimitry Andric                 "The epilog is popping more registers than the prolog pushed");
234*700637cbSDimitry Andric           if (PushedRegs[PushedRegs.size() - PoppedRegCount] !=
235*700637cbSDimitry Andric               MI.getOperand(0).getReg())
236*700637cbSDimitry Andric             return rejectCurrentFunctionInternalError(
237*700637cbSDimitry Andric                 MF, Mode,
238*700637cbSDimitry Andric                 "The epilog is popping a registers in "
239*700637cbSDimitry Andric                 "a different order than the "
240*700637cbSDimitry Andric                 "prolog pushed them");
241*700637cbSDimitry Andric 
242*700637cbSDimitry Andric           // Unwind v2 records the size of the epilog not from where we place
243*700637cbSDimitry Andric           // SEH_BeginEpilogue (as that contains the instruction to adjust the
244*700637cbSDimitry Andric           // stack pointer) but from the first POP instruction (if there is
245*700637cbSDimitry Andric           // one).
246*700637cbSDimitry Andric           if (!UnwindV2StartLocation) {
247*700637cbSDimitry Andric             assert(PoppedRegCount == 1);
248*700637cbSDimitry Andric             UnwindV2StartLocation = &MI;
249*700637cbSDimitry Andric           }
250*700637cbSDimitry Andric         } else if (State == FunctionState::FinishedEpilog)
251*700637cbSDimitry Andric           // Unexpected instruction after the epilog.
252*700637cbSDimitry Andric           return rejectCurrentFunctionInternalError(
253*700637cbSDimitry Andric               MF, Mode, "Registers are being popped after the epilog");
254*700637cbSDimitry Andric         break;
255*700637cbSDimitry Andric 
256*700637cbSDimitry Andric       default:
257*700637cbSDimitry Andric         if (MI.isTerminator()) {
258*700637cbSDimitry Andric           if (State == FunctionState::FinishedEpilog)
259*700637cbSDimitry Andric             // Found the terminator after the epilog, we're now ready for
260*700637cbSDimitry Andric             // another epilog.
261*700637cbSDimitry Andric             State = FunctionState::HasProlog;
262*700637cbSDimitry Andric           else if (State == FunctionState::InEpilog)
263*700637cbSDimitry Andric             llvm_unreachable("Terminator in the middle of the epilog");
264*700637cbSDimitry Andric         } else if (!MI.isDebugOrPseudoInstr()) {
265*700637cbSDimitry Andric           if ((State == FunctionState::FinishedEpilog) ||
266*700637cbSDimitry Andric               (State == FunctionState::InEpilog))
267*700637cbSDimitry Andric             // Unknown instruction in or after the epilog.
268*700637cbSDimitry Andric             return rejectCurrentFunctionInternalError(
269*700637cbSDimitry Andric                 MF, Mode, "Unexpected instruction in or after the epilog");
270*700637cbSDimitry Andric         }
271*700637cbSDimitry Andric       }
272*700637cbSDimitry Andric     }
273*700637cbSDimitry Andric   }
274*700637cbSDimitry Andric 
275*700637cbSDimitry Andric   if (UnwindV2StartLocations.empty()) {
276*700637cbSDimitry Andric     assert(State == FunctionState::InProlog &&
277*700637cbSDimitry Andric            "If there are no epilogs, then there should be no prolog");
278*700637cbSDimitry Andric     return false;
279*700637cbSDimitry Andric   }
280*700637cbSDimitry Andric 
281*700637cbSDimitry Andric   MachineBasicBlock &FirstMBB = MF.front();
282*700637cbSDimitry Andric   // Assume +1 for the "header" UOP_Epilog that contains the epilog size, and
283*700637cbSDimitry Andric   // that we won't be able to use the "last epilog at the end of function"
284*700637cbSDimitry Andric   // optimization.
285*700637cbSDimitry Andric   if (ApproximatePrologCodeCount + UnwindV2StartLocations.size() + 1 >
286*700637cbSDimitry Andric       static_cast<unsigned>(MaximumUnwindCodes)) {
287*700637cbSDimitry Andric     if (Mode == WinX64EHUnwindV2Mode::Required)
288*700637cbSDimitry Andric       MF.getFunction().getContext().diagnose(DiagnosticInfoGenericWithLoc(
289*700637cbSDimitry Andric           "Windows x64 Unwind v2 is required, but the function '" +
290*700637cbSDimitry Andric               MF.getName() +
291*700637cbSDimitry Andric               "' has too many unwind codes. Try splitting the function or "
292*700637cbSDimitry Andric               "reducing the number of places where it exits early with a tail "
293*700637cbSDimitry Andric               "call.",
294*700637cbSDimitry Andric           MF.getFunction(), findDebugLoc(FirstMBB)));
295*700637cbSDimitry Andric 
296*700637cbSDimitry Andric     FailsUnwindV2Criteria++;
297*700637cbSDimitry Andric     return false;
298*700637cbSDimitry Andric   }
299*700637cbSDimitry Andric 
300*700637cbSDimitry Andric   MeetsUnwindV2Criteria++;
301*700637cbSDimitry Andric 
302*700637cbSDimitry Andric   // Emit the pseudo instruction that marks the start of each epilog.
303*700637cbSDimitry Andric   const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
304*700637cbSDimitry Andric   for (MachineInstr *MI : UnwindV2StartLocations) {
305*700637cbSDimitry Andric     BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
306*700637cbSDimitry Andric             TII->get(X86::SEH_UnwindV2Start));
307*700637cbSDimitry Andric   }
308*700637cbSDimitry Andric   // Note that the function is using Unwind v2.
309*700637cbSDimitry Andric   BuildMI(FirstMBB, FirstMBB.front(), findDebugLoc(FirstMBB),
310*700637cbSDimitry Andric           TII->get(X86::SEH_UnwindVersion))
311*700637cbSDimitry Andric       .addImm(2);
312*700637cbSDimitry Andric 
313*700637cbSDimitry Andric   return true;
314*700637cbSDimitry Andric }
315*700637cbSDimitry Andric 
rejectCurrentFunctionInternalError(const MachineFunction & MF,WinX64EHUnwindV2Mode Mode,StringRef Reason)316*700637cbSDimitry Andric bool X86WinEHUnwindV2::rejectCurrentFunctionInternalError(
317*700637cbSDimitry Andric     const MachineFunction &MF, WinX64EHUnwindV2Mode Mode, StringRef Reason) {
318*700637cbSDimitry Andric   if (Mode == WinX64EHUnwindV2Mode::Required)
319*700637cbSDimitry Andric     reportFatalInternalError("Windows x64 Unwind v2 is required, but LLVM has "
320*700637cbSDimitry Andric                              "generated incompatible code in function '" +
321*700637cbSDimitry Andric                              MF.getName() + "': " + Reason);
322*700637cbSDimitry Andric 
323*700637cbSDimitry Andric   FailsUnwindV2Criteria++;
324*700637cbSDimitry Andric   return false;
325*700637cbSDimitry Andric }
326