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