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