[llvm] [UnwinderV3] Include padding in spliting calculations (PR #208458)
Evgenii Kudriashov via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 14 03:12:21 PDT 2026
https://github.com/e-kud updated https://github.com/llvm/llvm-project/pull/208458
>From 30db25272624b3fd3cdea5e2ca5700f23623e1a2 Mon Sep 17 00:00:00 2001
From: "Pirog, Mikolaj Maciej" <mikolaj.maciej.pirog at intel.com>
Date: Mon, 6 Jul 2026 11:50:32 +0200
Subject: [PATCH 1/3] Include padding in epliog spliting calculations
---
llvm/lib/Target/X86/X86WinEHUnwindV3.cpp | 92 ++++++++++---------
.../X86/win64-eh-unwindv3-split-large.ll | 4 +-
2 files changed, 51 insertions(+), 45 deletions(-)
diff --git a/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp b/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp
index 901510763936e..7e5bc46ece006 100644
--- a/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp
+++ b/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp
@@ -47,22 +47,20 @@ STATISTIC(SubFragmentSplits,
static constexpr unsigned MaxV3PrologOps = 31;
static constexpr unsigned MaxV3Epilogs = 7;
static constexpr unsigned MaxV3EpilogOps = 31;
-
-/// Maximum approximate instruction distance allowed between two adjacent
-/// epilogs, and between the last epilog and the funclet end, before the
-/// funclet is split into a new chained sub-fragment. V3 encodes each epilog's
-/// position as a signed 16-bit EpilogOffset: a delta from the previous epilog,
-/// with the tail-closest epilog encoded relative to the fragment end. The exact
-/// byte offsets aren't known until MC layout, so the approximate instruction
-/// count is used as a proxy, with margin for the average emitted instruction
-/// size.
-static cl::opt<unsigned> EpilogDistanceThreshold(
- "x86-wineh-unwindv3-epilog-distance-threshold", cl::Hidden,
- cl::desc(
- "Maximum approximate instruction distance between adjacent epilogs "
- "(or between the last epilog and the funclet end) before "
- "splitting into a new chained unwind info for Unwind v3."),
- cl::init(3000));
+static constexpr unsigned EpilogDistanceThreshold = 32767;
+
+/// Approximate byte distance between an epilog and its fragment tail beyond
+/// which the funclet is split into a new chained sub-fragment. The V3
+/// EpilogOffset field is a signed 16-bit byte offset measured from the
+/// fragment tail, so each fragment must span less than 32 KiB of code. The
+/// exact byte offsets aren't known until MC layout, so (like the V2 pass) an
+/// approximate byte count is used as a proxy — instructions are charged
+/// ApproxBytesPerInstr each and alignment padding is added.
+static cl::opt<unsigned> ApproxBytesPerInstr(
+ "x86-wineh-unwindv3-instr-avg-size", cl::Hidden,
+ cl::desc("Average size of an instruction. This value is used in determining "
+ "split points for chained unwinder info"),
+ cl::init(6));
/// After reporting a recoverable error for `MF`, erase all SEH pseudo-
/// instructions and clear the WinCFI flag so the AsmPrinter doesn't try to
@@ -98,20 +96,20 @@ static void suppressWinCFI(MachineFunction &MF) {
namespace {
-/// A V3 epilog and the approximate instruction position where it begins, used
+/// A V3 epilog and the approximate byte position where it begins, used
/// as a candidate sub-fragment split point.
struct EpilogSplitPoint {
MachineInstr *BeginEpilog;
- unsigned ApproxInstrPos;
+ unsigned ApproxBytePos;
};
/// Per-funclet analysis results.
struct FuncletInfo {
unsigned PrologOpCount = 0;
unsigned MaxEpilogOpCount = 0;
- /// Approximate instruction position at the end of the funclet, used as the
+ /// Approximate byte position at the end of the funclet, used as the
/// initial fragment tail reference for size-based splitting.
- unsigned EndInstrPos = 0;
+ unsigned EndBytePos = 0;
/// SEH_BeginEpilogue instructions (with approximate positions), used as
/// candidate insertion points for sub-fragment splitting.
SmallVector<EpilogSplitPoint, 8> Epilogs;
@@ -132,12 +130,12 @@ class X86WinEHUnwindV3 : public MachineFunctionPass {
private:
/// Analyze one funclet (or the main function body) starting at Iter.
/// Advances Iter past the analyzed region, stopping at the next funclet
- /// entry or the end of the function. ApproxInstrPos is a running count of
- /// emitted instructions across the whole function, used to estimate the
- /// byte distance between epilogs and their fragment tail.
+ /// entry or the end of the function. ApproxBytePos is a running estimate of
+ /// the byte position across the whole function, used to estimate the byte
+ /// distance between epilogs and their fragment tail.
static FuncletInfo analyzeFunclet(MachineFunction &MF,
MachineFunction::iterator &Iter,
- unsigned &ApproxInstrPos);
+ unsigned &ApproxBytePos);
};
} // end anonymous namespace
@@ -154,7 +152,7 @@ FunctionPass *llvm::createX86WinEHUnwindV3Pass() {
FuncletInfo X86WinEHUnwindV3::analyzeFunclet(MachineFunction &MF,
MachineFunction::iterator &Iter,
- unsigned &ApproxInstrPos) {
+ unsigned &ApproxBytePos) {
FuncletInfo Info;
bool InEpilog = false;
bool SeenProlog = false;
@@ -168,12 +166,20 @@ FuncletInfo X86WinEHUnwindV3::analyzeFunclet(MachineFunction &MF,
if (MBB.isEHFuncletEntry() && SeenProlog)
break;
+ // Account for worst-case scenario of padding inserted to align this block.
+ Align A = MBB.getAlignment();
+ unsigned MaxPadding = A.value() - 1;
+ if (unsigned MaxBytes = MBB.getMaxBytesForAlignment())
+ MaxPadding = std::min(MaxPadding, MaxBytes);
+ ApproxBytePos += MaxPadding;
+
for (MachineInstr &MI : MBB) {
- // Approximate the number of emitted instructions. This estimates how
- // far each epilog sits from its fragment tail; the exact byte offsets
- // aren't available until MC layout.
+ // Approximate the emitted byte size, mirroring the V2 pass. This
+ // estimates how far each epilog sits from its fragment tail; the exact
+ // byte offsets aren't available until MC layout, so each real
+ // instruction is charged ApproxBytesPerInstr bytes.
if (!MI.isPseudo() && !MI.isMetaInstruction())
- ApproxInstrPos++;
+ ApproxBytePos += ApproxBytesPerInstr;
switch (MI.getOpcode()) {
case X86::SEH_PushReg:
@@ -195,9 +201,9 @@ FuncletInfo X86WinEHUnwindV3::analyzeFunclet(MachineFunction &MF,
InEpilog = true;
CurrentEpilogOpCount = 0;
LLVM_DEBUG(dbgs() << " epilog " << Info.Epilogs.size()
- << " begins at approx instruction position "
- << ApproxInstrPos << "\n");
- Info.Epilogs.push_back({&MI, ApproxInstrPos});
+ << " begins at approx byte position "
+ << ApproxBytePos << "\n");
+ Info.Epilogs.push_back({&MI, ApproxBytePos});
break;
case X86::SEH_EndEpilogue:
InEpilog = false;
@@ -210,10 +216,10 @@ FuncletInfo X86WinEHUnwindV3::analyzeFunclet(MachineFunction &MF,
}
}
- Info.EndInstrPos = ApproxInstrPos;
+ Info.EndBytePos = ApproxBytePos;
LLVM_DEBUG(dbgs() << " funclet has " << Info.Epilogs.size()
- << " epilog(s); ends at approx instruction position "
- << ApproxInstrPos << "\n");
+ << " epilog(s); ends at approx byte position "
+ << ApproxBytePos << "\n");
return Info;
}
@@ -244,7 +250,7 @@ bool X86WinEHUnwindV3::runOnMachineFunction(MachineFunction &MF) {
}
bool Changed = false;
- unsigned ApproxInstrPos = 0;
+ unsigned ApproxBytePos = 0;
MachineFunction::iterator Iter = MF.begin();
LLVM_DEBUG(dbgs() << "X86WinEHUnwindV3: processing " << MF.getName() << "\n");
@@ -252,7 +258,7 @@ bool X86WinEHUnwindV3::runOnMachineFunction(MachineFunction &MF) {
// Process each funclet (and the main function body) independently.
// Each funclet gets its own UNWIND_INFO, so V3 limits apply per funclet.
while (Iter != MF.end()) {
- FuncletInfo Info = analyzeFunclet(MF, Iter, ApproxInstrPos);
+ FuncletInfo Info = analyzeFunclet(MF, Iter, ApproxBytePos);
if (Info.PrologOpCount > MaxV3PrologOps) {
Ctx.diagnose(DiagnosticInfoResourceLimit(
@@ -309,7 +315,7 @@ bool X86WinEHUnwindV3::runOnMachineFunction(MachineFunction &MF) {
if (EpilogsInFragment > 0) {
bool ExceedsEpilogCount = EpilogsInFragment >= MaxV3Epilogs;
bool ExceedsDistance =
- Epilog.ApproxInstrPos - LastEpilog->ApproxInstrPos >=
+ Epilog.ApproxBytePos - LastEpilog->ApproxBytePos >=
EpilogDistanceThreshold;
if (ExceedsEpilogCount || ExceedsDistance) {
LLVM_DEBUG({
@@ -320,8 +326,8 @@ bool X86WinEHUnwindV3::runOnMachineFunction(MachineFunction &MF) {
else
dbgs() << "epilog distance threshold (gap from previous epilog "
"at "
- << LastEpilog->ApproxInstrPos << " to epilog at "
- << Epilog.ApproxInstrPos << ")\n";
+ << LastEpilog->ApproxBytePos << " to epilog at "
+ << Epilog.ApproxBytePos << ")\n";
});
SplitAfter(*LastEpilog);
EpilogsInFragment = 0;
@@ -334,12 +340,12 @@ bool X86WinEHUnwindV3::runOnMachineFunction(MachineFunction &MF) {
// If the last epilog is too far from the funclet end, split after it so the
// trailing code becomes its own epilog-free chained fragment.
- if (LastEpilog && Info.EndInstrPos - LastEpilog->ApproxInstrPos >=
+ if (LastEpilog && Info.EndBytePos - LastEpilog->ApproxBytePos >=
EpilogDistanceThreshold) {
LLVM_DEBUG(dbgs() << " splitting after last epilog " << LastEpilogIdx
<< " to isolate the trailing tail (gap from epilog at "
- << LastEpilog->ApproxInstrPos << " to funclet end "
- << Info.EndInstrPos << ")\n");
+ << LastEpilog->ApproxBytePos << " to funclet end "
+ << Info.EndBytePos << ")\n");
SplitAfter(*LastEpilog);
}
}
diff --git a/llvm/test/CodeGen/X86/win64-eh-unwindv3-split-large.ll b/llvm/test/CodeGen/X86/win64-eh-unwindv3-split-large.ll
index ab8762fc72192..50b36fb9edc1d 100644
--- a/llvm/test/CodeGen/X86/win64-eh-unwindv3-split-large.ll
+++ b/llvm/test/CodeGen/X86/win64-eh-unwindv3-split-large.ll
@@ -1,7 +1,7 @@
; RUN: llc -mtriple=x86_64-unknown-windows-msvc \
-; RUN: -x86-wineh-unwindv3-epilog-distance-threshold=1 -o - %s | FileCheck %s
+; RUN: -x86-wineh-unwindv3-instr-avg-size=100000 -o - %s | FileCheck %s
; RUN: llc -mtriple=x86_64-unknown-windows-msvc \
-; RUN: -x86-wineh-unwindv3-epilog-distance-threshold=1 -filetype=obj %s -o - \
+; RUN: -x86-wineh-unwindv3-instr-avg-size=100000 -filetype=obj %s -o - \
; RUN: | llvm-readobj --unwind - | FileCheck %s --check-prefix=OBJ
; Test V3 *size-based* sub-fragment splitting (the "Unwind v2 style" heuristic).
>From 9e9c123b178b7513d9ba0118fe2d972668e8d5a3 Mon Sep 17 00:00:00 2001
From: "Pirog, Mikolaj Maciej" <mikolaj.maciej.pirog at intel.com>
Date: Thu, 9 Jul 2026 15:22:23 +0200
Subject: [PATCH 2/3] Bump instr size
---
llvm/lib/Target/X86/X86WinEHUnwindV3.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp b/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp
index 7e5bc46ece006..1baa88d56d8a5 100644
--- a/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp
+++ b/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp
@@ -60,7 +60,7 @@ static cl::opt<unsigned> ApproxBytesPerInstr(
"x86-wineh-unwindv3-instr-avg-size", cl::Hidden,
cl::desc("Average size of an instruction. This value is used in determining "
"split points for chained unwinder info"),
- cl::init(6));
+ cl::init(7));
/// After reporting a recoverable error for `MF`, erase all SEH pseudo-
/// instructions and clear the WinCFI flag so the AsmPrinter doesn't try to
>From 904166db1d1e32c91a7ce290f39faa7cd5c63cf4 Mon Sep 17 00:00:00 2001
From: "Pirog, Mikolaj Maciej" <mikolaj.maciej.pirog at intel.com>
Date: Thu, 9 Jul 2026 15:36:07 +0200
Subject: [PATCH 3/3] Formatting
---
llvm/lib/Target/X86/X86WinEHUnwindV3.cpp | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp b/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp
index 1baa88d56d8a5..162633e2fddf3 100644
--- a/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp
+++ b/llvm/lib/Target/X86/X86WinEHUnwindV3.cpp
@@ -58,8 +58,9 @@ static constexpr unsigned EpilogDistanceThreshold = 32767;
/// ApproxBytesPerInstr each and alignment padding is added.
static cl::opt<unsigned> ApproxBytesPerInstr(
"x86-wineh-unwindv3-instr-avg-size", cl::Hidden,
- cl::desc("Average size of an instruction. This value is used in determining "
- "split points for chained unwinder info"),
+ cl::desc(
+ "Average size of an instruction. This value is used in determining "
+ "split points for chained unwinder info"),
cl::init(7));
/// After reporting a recoverable error for `MF`, erase all SEH pseudo-
@@ -201,8 +202,8 @@ FuncletInfo X86WinEHUnwindV3::analyzeFunclet(MachineFunction &MF,
InEpilog = true;
CurrentEpilogOpCount = 0;
LLVM_DEBUG(dbgs() << " epilog " << Info.Epilogs.size()
- << " begins at approx byte position "
- << ApproxBytePos << "\n");
+ << " begins at approx byte position " << ApproxBytePos
+ << "\n");
Info.Epilogs.push_back({&MI, ApproxBytePos});
break;
case X86::SEH_EndEpilogue:
More information about the llvm-commits
mailing list