| 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 set module-wide, not per-function. |
| 17 | /// |
| 18 | /// See https://learn.microsoft.com/en-us/cpp/build/x64-unwind-information-v3 |
| 19 | /// |
| 20 | //===----------------------------------------------------------------------===// |
| 21 | |
| 22 | #include "MCTargetDesc/X86BaseInfo.h" |
| 23 | #include "X86.h" |
| 24 | #include "X86Subtarget.h" |
| 25 | #include "llvm/ADT/Statistic.h" |
| 26 | #include "llvm/CodeGen/MachineBasicBlock.h" |
| 27 | #include "llvm/CodeGen/MachineFunctionPass.h" |
| 28 | #include "llvm/CodeGen/MachineInstrBuilder.h" |
| 29 | #include "llvm/CodeGen/TargetInstrInfo.h" |
| 30 | #include "llvm/CodeGen/TargetSubtargetInfo.h" |
| 31 | #include "llvm/IR/DiagnosticInfo.h" |
| 32 | #include "llvm/IR/LLVMContext.h" |
| 33 | #include "llvm/IR/Module.h" |
| 34 | #include "llvm/Support/CommandLine.h" |
| 35 | #include "llvm/Support/Debug.h" |
| 36 | |
| 37 | using namespace llvm; |
| 38 | |
| 39 | #define DEBUG_TYPE "x86-wineh-unwindv3" |
| 40 | |
| 41 | STATISTIC(FunctionsProcessed, |
| 42 | "Number of functions processed by Unwind v3 pass" ); |
| 43 | STATISTIC(SubFragmentSplits, |
| 44 | "Number of sub-fragment splits inserted for Unwind v3" ); |
| 45 | |
| 46 | /// V3 limits from the format specification. |
| 47 | static constexpr unsigned MaxV3PrologOps = 31; |
| 48 | static constexpr unsigned MaxV3Epilogs = 7; |
| 49 | static constexpr unsigned MaxV3EpilogOps = 31; |
| 50 | |
| 51 | /// Maximum approximate instruction distance allowed between two adjacent |
| 52 | /// epilogs, and between the last epilog and the funclet end, before the |
| 53 | /// funclet is split into a new chained sub-fragment. V3 encodes each epilog's |
| 54 | /// position as a signed 16-bit EpilogOffset: a delta from the previous epilog, |
| 55 | /// with the tail-closest epilog encoded relative to the fragment end. The exact |
| 56 | /// byte offsets aren't known until MC layout, so the approximate instruction |
| 57 | /// count is used as a proxy, with margin for the average emitted instruction |
| 58 | /// size. |
| 59 | static cl::opt<unsigned> EpilogDistanceThreshold( |
| 60 | "x86-wineh-unwindv3-epilog-distance-threshold" , cl::Hidden, |
| 61 | cl::desc( |
| 62 | "Maximum approximate instruction distance between adjacent epilogs " |
| 63 | "(or between the last epilog and the funclet end) before " |
| 64 | "splitting into a new chained unwind info for Unwind v3." ), |
| 65 | cl::init(Val: 3000)); |
| 66 | |
| 67 | /// After reporting a recoverable error for `MF`, erase all SEH pseudo- |
| 68 | /// instructions and clear the WinCFI flag so the AsmPrinter doesn't try to |
| 69 | /// emit (potentially malformed) unwind information. The LLVMContext |
| 70 | /// diagnostic recorded by the caller will prevent the object file from |
| 71 | /// actually being written. |
| 72 | static void suppressWinCFI(MachineFunction &MF) { |
| 73 | for (MachineBasicBlock &MBB : MF) { |
| 74 | for (MachineInstr &MI : llvm::make_early_inc_range(Range&: MBB)) { |
| 75 | switch (MI.getOpcode()) { |
| 76 | case X86::SEH_PushReg: |
| 77 | case X86::SEH_Push2Regs: |
| 78 | case X86::SEH_SaveReg: |
| 79 | case X86::SEH_SaveXMM: |
| 80 | case X86::SEH_StackAlloc: |
| 81 | case X86::SEH_StackAlign: |
| 82 | case X86::SEH_SetFrame: |
| 83 | case X86::SEH_PushFrame: |
| 84 | case X86::SEH_EndPrologue: |
| 85 | case X86::SEH_BeginEpilogue: |
| 86 | case X86::SEH_EndEpilogue: |
| 87 | case X86::SEH_SplitChained: |
| 88 | case X86::SEH_SplitChainedAtEndOfBlock: |
| 89 | MI.eraseFromParent(); |
| 90 | break; |
| 91 | default: |
| 92 | break; |
| 93 | } |
| 94 | } |
| 95 | } |
| 96 | MF.setHasWinCFI(false); |
| 97 | } |
| 98 | |
| 99 | namespace { |
| 100 | |
| 101 | /// A V3 epilog and the approximate instruction position where it begins, used |
| 102 | /// as a candidate sub-fragment split point. |
| 103 | struct EpilogSplitPoint { |
| 104 | MachineInstr *BeginEpilog; |
| 105 | unsigned ApproxInstrPos; |
| 106 | }; |
| 107 | |
| 108 | /// Per-funclet analysis results. |
| 109 | struct FuncletInfo { |
| 110 | unsigned PrologOpCount = 0; |
| 111 | unsigned MaxEpilogOpCount = 0; |
| 112 | /// Approximate instruction position at the end of the funclet, used as the |
| 113 | /// initial fragment tail reference for size-based splitting. |
| 114 | unsigned EndInstrPos = 0; |
| 115 | /// SEH_BeginEpilogue instructions (with approximate positions), used as |
| 116 | /// candidate insertion points for sub-fragment splitting. |
| 117 | SmallVector<EpilogSplitPoint, 8> Epilogs; |
| 118 | }; |
| 119 | |
| 120 | class X86WinEHUnwindV3 : public MachineFunctionPass { |
| 121 | public: |
| 122 | static char ID; |
| 123 | |
| 124 | X86WinEHUnwindV3() : MachineFunctionPass(ID) { |
| 125 | initializeX86WinEHUnwindV3Pass(*PassRegistry::getPassRegistry()); |
| 126 | } |
| 127 | |
| 128 | StringRef getPassName() const override { return "WinEH Unwind V3" ; } |
| 129 | |
| 130 | bool runOnMachineFunction(MachineFunction &MF) override; |
| 131 | |
| 132 | private: |
| 133 | /// Analyze one funclet (or the main function body) starting at Iter. |
| 134 | /// Advances Iter past the analyzed region, stopping at the next funclet |
| 135 | /// entry or the end of the function. ApproxInstrPos is a running count of |
| 136 | /// emitted instructions across the whole function, used to estimate the |
| 137 | /// byte distance between epilogs and their fragment tail. |
| 138 | static FuncletInfo analyzeFunclet(MachineFunction &MF, |
| 139 | MachineFunction::iterator &Iter, |
| 140 | unsigned &ApproxInstrPos); |
| 141 | }; |
| 142 | |
| 143 | } // end anonymous namespace |
| 144 | |
| 145 | char X86WinEHUnwindV3::ID = 0; |
| 146 | |
| 147 | INITIALIZE_PASS(X86WinEHUnwindV3, "x86-wineh-unwindv3" , |
| 148 | "Capacity check and sub-fragment splitting for Win64 Unwind v3" , |
| 149 | false, false) |
| 150 | |
| 151 | FunctionPass *llvm::createX86WinEHUnwindV3Pass() { |
| 152 | return new X86WinEHUnwindV3(); |
| 153 | } |
| 154 | |
| 155 | FuncletInfo X86WinEHUnwindV3::analyzeFunclet(MachineFunction &MF, |
| 156 | MachineFunction::iterator &Iter, |
| 157 | unsigned &ApproxInstrPos) { |
| 158 | FuncletInfo Info; |
| 159 | bool InEpilog = false; |
| 160 | bool SeenProlog = false; |
| 161 | unsigned CurrentEpilogOpCount = 0; |
| 162 | |
| 163 | for (; Iter != MF.end(); ++Iter) { |
| 164 | MachineBasicBlock &MBB = *Iter; |
| 165 | |
| 166 | // If we've already been processing a funclet's prolog/body and encounter |
| 167 | // another funclet entry, stop - that funclet gets its own analysis. |
| 168 | if (MBB.isEHFuncletEntry() && SeenProlog) |
| 169 | break; |
| 170 | |
| 171 | for (MachineInstr &MI : MBB) { |
| 172 | // Approximate the number of emitted instructions. This estimates how |
| 173 | // far each epilog sits from its fragment tail; the exact byte offsets |
| 174 | // aren't available until MC layout. |
| 175 | if (!MI.isPseudo() && !MI.isMetaInstruction()) |
| 176 | ApproxInstrPos++; |
| 177 | |
| 178 | switch (MI.getOpcode()) { |
| 179 | case X86::SEH_PushReg: |
| 180 | case X86::SEH_Push2Regs: |
| 181 | case X86::SEH_StackAlloc: |
| 182 | case X86::SEH_SetFrame: |
| 183 | case X86::SEH_SaveReg: |
| 184 | case X86::SEH_SaveXMM: |
| 185 | case X86::SEH_PushFrame: |
| 186 | if (InEpilog) |
| 187 | CurrentEpilogOpCount++; |
| 188 | else |
| 189 | Info.PrologOpCount++; |
| 190 | break; |
| 191 | case X86::SEH_EndPrologue: |
| 192 | SeenProlog = true; |
| 193 | break; |
| 194 | case X86::SEH_BeginEpilogue: |
| 195 | InEpilog = true; |
| 196 | CurrentEpilogOpCount = 0; |
| 197 | LLVM_DEBUG(dbgs() << " epilog " << Info.Epilogs.size() |
| 198 | << " begins at approx instruction position " |
| 199 | << ApproxInstrPos << "\n" ); |
| 200 | Info.Epilogs.push_back(Elt: {.BeginEpilog: &MI, .ApproxInstrPos: ApproxInstrPos}); |
| 201 | break; |
| 202 | case X86::SEH_EndEpilogue: |
| 203 | InEpilog = false; |
| 204 | Info.MaxEpilogOpCount = |
| 205 | std::max(a: Info.MaxEpilogOpCount, b: CurrentEpilogOpCount); |
| 206 | break; |
| 207 | default: |
| 208 | break; |
| 209 | } |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | Info.EndInstrPos = ApproxInstrPos; |
| 214 | LLVM_DEBUG(dbgs() << " funclet has " << Info.Epilogs.size() |
| 215 | << " epilog(s); ends at approx instruction position " |
| 216 | << ApproxInstrPos << "\n" ); |
| 217 | return Info; |
| 218 | } |
| 219 | |
| 220 | bool X86WinEHUnwindV3::runOnMachineFunction(MachineFunction &MF) { |
| 221 | WinX64EHUnwindMode Mode = |
| 222 | MF.getFunction().getParent()->getWinX64EHUnwindMode(); |
| 223 | |
| 224 | Function &F = MF.getFunction(); |
| 225 | LLVMContext &Ctx = F.getContext(); |
| 226 | |
| 227 | // EGPR (R16-R31) requires V3 unwind info because V1/V2 cannot encode |
| 228 | // registers beyond R15. Only enforce this for functions that actually |
| 229 | // emit SEH unwind info — `nounwind` functions and targets that don't |
| 230 | // require unwind tables (e.g. cross-compilation host defaults) can use |
| 231 | // EGPR with any unwind mode since no SEH metadata is generated. |
| 232 | if (Mode != WinX64EHUnwindMode::V3) { |
| 233 | if (!F.needsUnwindTableEntry()) |
| 234 | return false; |
| 235 | const auto &STI = MF.getSubtarget<X86Subtarget>(); |
| 236 | if (STI.hasEGPR()) { |
| 237 | Ctx.diagnose(DI: DiagnosticInfoUnsupported( |
| 238 | F, "EGPR (R16-R31) requires V3 unwind info on Windows x64" )); |
| 239 | // Stripping the SEH pseudos modifies the function, so report a change. |
| 240 | suppressWinCFI(MF); |
| 241 | return true; |
| 242 | } |
| 243 | return false; |
| 244 | } |
| 245 | |
| 246 | bool Changed = false; |
| 247 | unsigned ApproxInstrPos = 0; |
| 248 | MachineFunction::iterator Iter = MF.begin(); |
| 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 | FuncletInfo Info = analyzeFunclet(MF, Iter, ApproxInstrPos); |
| 256 | |
| 257 | if (Info.PrologOpCount > MaxV3PrologOps) { |
| 258 | Ctx.diagnose(DI: DiagnosticInfoResourceLimit( |
| 259 | F, "number of unwind v3 prolog operations required" , |
| 260 | Info.PrologOpCount, MaxV3PrologOps, DS_Error, DK_ResourceLimit)); |
| 261 | Ctx.diagnose(DI: DiagnosticInfoGenericWithLoc( |
| 262 | "sub-fragment splitting for prolog overflow is not yet implemented" , |
| 263 | F, F.getSubprogram(), DS_Note)); |
| 264 | // Stripping the SEH pseudos modifies the function, so report a change. |
| 265 | suppressWinCFI(MF); |
| 266 | return true; |
| 267 | } |
| 268 | |
| 269 | if (Info.MaxEpilogOpCount > MaxV3EpilogOps) { |
| 270 | Ctx.diagnose(DI: DiagnosticInfoResourceLimit( |
| 271 | F, "number of unwind v3 epilog operations required" , |
| 272 | Info.MaxEpilogOpCount, MaxV3EpilogOps, DS_Error, DK_ResourceLimit)); |
| 273 | Ctx.diagnose(DI: DiagnosticInfoGenericWithLoc( |
| 274 | "sub-fragment splitting for epilog overflow is not yet implemented" , |
| 275 | F, F.getSubprogram(), DS_Note)); |
| 276 | // Stripping the SEH pseudos modifies the function, so report a change. |
| 277 | suppressWinCFI(MF); |
| 278 | return true; |
| 279 | } |
| 280 | |
| 281 | // Split the funclet into chained sub-fragments so that each fragment's |
| 282 | // UNWIND_INFO stays within the V3 capacity limits: at most 7 epilogs per |
| 283 | // fragment, and each adjacent-epilog gap (plus the gap from the last epilog |
| 284 | // to the fragment tail) small enough that the corresponding signed-16-bit |
| 285 | // EpilogOffset delta fits. |
| 286 | // |
| 287 | // A SEH_SplitChainedAtEndOfBlock inserted at the start of an epilog's |
| 288 | // block makes the AsmPrinter emit the actual .seh_splitchained at the |
| 289 | // *end* of that block, so the epilog becomes the last epilog of the |
| 290 | // earlier fragment, immediately followed by the new chained fragment. A |
| 291 | // long tail after the last epilog is pushed into its own epilog-free |
| 292 | // chained fragment. |
| 293 | const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); |
| 294 | auto SplitAfter = [&](const EpilogSplitPoint &Epilog) { |
| 295 | MachineBasicBlock *MBB = Epilog.BeginEpilog->getParent(); |
| 296 | BuildMI(BB&: *MBB, I: MBB->begin(), MIMD: Epilog.BeginEpilog->getDebugLoc(), |
| 297 | MCID: TII->get(Opcode: X86::SEH_SplitChainedAtEndOfBlock)); |
| 298 | SubFragmentSplits++; |
| 299 | Changed = true; |
| 300 | }; |
| 301 | |
| 302 | unsigned EpilogsInFragment = 0; |
| 303 | const EpilogSplitPoint *LastEpilog = nullptr; |
| 304 | [[maybe_unused]] unsigned LastEpilogIdx = 0; |
| 305 | for (unsigned Idx = 0; Idx < Info.Epilogs.size(); ++Idx) { |
| 306 | const EpilogSplitPoint &Epilog = Info.Epilogs[Idx]; |
| 307 | // If adding this epilog would exceed a fragment limit or is too far, end |
| 308 | // the current fragment after the previous epilog and start a new one. |
| 309 | if (EpilogsInFragment > 0) { |
| 310 | bool ExceedsEpilogCount = EpilogsInFragment >= MaxV3Epilogs; |
| 311 | bool ExceedsDistance = |
| 312 | Epilog.ApproxInstrPos - LastEpilog->ApproxInstrPos >= |
| 313 | EpilogDistanceThreshold; |
| 314 | if (ExceedsEpilogCount || ExceedsDistance) { |
| 315 | LLVM_DEBUG({ |
| 316 | dbgs() << " splitting after epilog " << LastEpilogIdx |
| 317 | << " because adding epilog " << Idx << " would exceed the " ; |
| 318 | if (ExceedsEpilogCount) |
| 319 | dbgs() << "7-epilog-per-fragment limit\n" ; |
| 320 | else |
| 321 | dbgs() << "epilog distance threshold (gap from previous epilog " |
| 322 | "at " |
| 323 | << LastEpilog->ApproxInstrPos << " to epilog at " |
| 324 | << Epilog.ApproxInstrPos << ")\n" ; |
| 325 | }); |
| 326 | SplitAfter(*LastEpilog); |
| 327 | EpilogsInFragment = 0; |
| 328 | } |
| 329 | } |
| 330 | EpilogsInFragment++; |
| 331 | LastEpilog = &Epilog; |
| 332 | LastEpilogIdx = Idx; |
| 333 | } |
| 334 | |
| 335 | // If the last epilog is too far from the funclet end, split after it so the |
| 336 | // trailing code becomes its own epilog-free chained fragment. |
| 337 | if (LastEpilog && Info.EndInstrPos - LastEpilog->ApproxInstrPos >= |
| 338 | EpilogDistanceThreshold) { |
| 339 | LLVM_DEBUG(dbgs() << " splitting after last epilog " << LastEpilogIdx |
| 340 | << " to isolate the trailing tail (gap from epilog at " |
| 341 | << LastEpilog->ApproxInstrPos << " to funclet end " |
| 342 | << Info.EndInstrPos << ")\n" ); |
| 343 | SplitAfter(*LastEpilog); |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | if (Changed) |
| 348 | FunctionsProcessed++; |
| 349 | |
| 350 | return Changed; |
| 351 | } |
| 352 | |