[llvm] [BranchFolder] Prevent nested salvage in empty BB chains with pseudo probes (PR #206092)
Jinjie Huang via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 29 06:04:47 PDT 2026
https://github.com/Jinjie-Huang updated https://github.com/llvm/llvm-project/pull/206092
>From 5daf830970757c9b802edaa86e1bfb5ae4282275 Mon Sep 17 00:00:00 2001
From: huangjinjie <huangjinjie at bytedance.com>
Date: Fri, 26 Jun 2026 22:10:21 +0800
Subject: [PATCH 1/3] Prevent nested salvage in empty BB chains with pseudo
probes
---
llvm/lib/CodeGen/BranchFolding.cpp | 13 +++
...ranch-folder-pseudoprobe-debug-salvage.mir | 108 ++++++++++++++++++
2 files changed, 121 insertions(+)
create mode 100644 llvm/test/CodeGen/MIR/X86/branch-folder-pseudoprobe-debug-salvage.mir
diff --git a/llvm/lib/CodeGen/BranchFolding.cpp b/llvm/lib/CodeGen/BranchFolding.cpp
index 55f2dd430d6cb..275e600a24d03 100644
--- a/llvm/lib/CodeGen/BranchFolding.cpp
+++ b/llvm/lib/CodeGen/BranchFolding.cpp
@@ -1283,6 +1283,12 @@ static bool IsBranchOnlyBlock(MachineBasicBlock *MBB) {
return I->isBranch();
}
+static bool HasPseudoProbe(const MachineBasicBlock &MBB) {
+ return llvm::any_of(MBB, [](const MachineInstr &MI) {
+ return MI.isPseudoProbe();
+ });
+}
+
/// IsBetterFallthrough - Return true if it would be clearly better to
/// fall-through to MBB1 than to fall through into MBB2. This has to return
/// a strict ordering, returning true for both (MBB1,MBB2) and (MBB2,MBB1) will
@@ -1347,6 +1353,13 @@ static void salvageDebugInfoFromEmptyBlock(const TargetInstrInfo *TII,
for (MachineBasicBlock *SuccBB : MBB.successors())
if (SuccBB->pred_size() == 1)
copyDebugInfoToSuccessor(TII, MBB, *SuccBB);
+
+ // Prevent O(N^2) getFirstTerminator() rescans in long chains of empty blocks
+ // (a pattern common with llvm.pseudoprobe) by not salvaging them into
+ // predecessor debug tails.
+ if (HasPseudoProbe(MBB))
+ return;
+
// If this MBB is the only successor of a predecessor it is legal to copy the
// DBG_VALUE instructions to the end of the predecessor (just before the
// terminators, assuming that the terminator isn't affecting the DBG_VALUE).
diff --git a/llvm/test/CodeGen/MIR/X86/branch-folder-pseudoprobe-debug-salvage.mir b/llvm/test/CodeGen/MIR/X86/branch-folder-pseudoprobe-debug-salvage.mir
new file mode 100644
index 0000000000000..ab6163e41f26a
--- /dev/null
+++ b/llvm/test/CodeGen/MIR/X86/branch-folder-pseudoprobe-debug-salvage.mir
@@ -0,0 +1,108 @@
+# RUN: llc -o - %s -mtriple=x86_64-- -run-pass=branch-folder | FileCheck %s
+
+# BranchFolder removes empty blocks and salvages their DBG_VALUEs. Empty
+# pseudo-probe blocks can appear in very long chains. Salvaging their debug
+# info into predecessors may repeatedly append DBG_VALUEs before the same
+# predecessor terminator and cause quadratic getFirstTerminator() rescans.
+# Check that pseudo-probe empty blocks are still salvaged to successors, but not
+# to predecessors, while ordinary debug-only empty blocks keep the predecessor
+# salvage behavior.
+
+--- |
+ define void @probe_pred_skip() !dbg !4 {
+ ret void
+ }
+
+ define void @debug_pred_keep() !dbg !15 {
+ ret void
+ }
+
+ !llvm.dbg.cu = !{!0}
+ !llvm.module.flags = !{!3}
+
+ !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, isOptimized: true, emissionKind: FullDebug)
+ !1 = !DIFile(filename: "branch-folder-pseudoprobe-debug-salvage.c", directory: "/tmp")
+ !2 = !{}
+ !3 = !{i32 2, !"Debug Info Version", i32 3}
+ !4 = distinct !DISubprogram(name: "probe_pred_skip", scope: !1, file: !1, line: 1, type: !5, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
+ !5 = !DISubroutineType(types: !6)
+ !6 = !{null}
+ !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+ !8 = !{!9}
+ !9 = !DILocalVariable(name: "probe_var", scope: !4, file: !1, line: 2, type: !7)
+ !10 = !DILocation(line: 2, column: 3, scope: !4)
+ !15 = distinct !DISubprogram(name: "debug_pred_keep", scope: !1, file: !1, line: 10, type: !5, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !16)
+ !16 = !{!17}
+ !17 = !DILocalVariable(name: "debug_var", scope: !15, file: !1, line: 11, type: !7)
+ !18 = !DILocation(line: 11, column: 3, scope: !15)
+
+...
+---
+name: probe_pred_skip
+tracksRegLiveness: false
+body: |
+ ; The empty pseudo-probe block bb.2 is removed. Its successor has another
+ ; predecessor, so successor salvage is not legal. Check that the DBG_VALUE is
+ ; not copied into bb.1 either.
+ ; CHECK-LABEL: name: probe_pred_skip
+ ; CHECK: bb.1:
+ ; CHECK: $eax = MOV32ri 42
+ ; CHECK-NOT: DBG_VALUE 1, $noreg
+ ; CHECK: RET 0
+ bb.0:
+ successors: %bb.1, %bb.3
+ TEST32rr $eax, $eax, implicit-def $eflags
+ JCC_1 %bb.3, 4, implicit $eflags
+ JMP_1 %bb.1
+
+ bb.1:
+ successors: %bb.2
+ $eax = MOV32ri 42
+ JMP_1 %bb.2
+
+ bb.2:
+ successors: %bb.4
+ PSEUDO_PROBE 1, 1, 0, 0, debug-location !10
+ DBG_VALUE 1, $noreg, !9, !DIExpression(), debug-location !10
+
+ bb.4:
+ RET 0
+
+ bb.3:
+ successors: %bb.4
+ JMP_1 %bb.4
+
+...
+---
+name: debug_pred_keep
+tracksRegLiveness: false
+body: |
+ ; Ordinary debug-only empty blocks retain predecessor salvage.
+ ; CHECK-LABEL: name: debug_pred_keep
+ ; CHECK: bb.1:
+ ; CHECK: $eax = MOV32ri 42
+ ; CHECK-NEXT: DBG_VALUE 2, $noreg
+ ; CHECK: RET 0
+ bb.0:
+ successors: %bb.1, %bb.3
+ TEST32rr $eax, $eax, implicit-def $eflags
+ JCC_1 %bb.3, 4, implicit $eflags
+ JMP_1 %bb.1
+
+ bb.1:
+ successors: %bb.2
+ $eax = MOV32ri 42
+ JMP_1 %bb.2
+
+ bb.2:
+ successors: %bb.4
+ DBG_VALUE 2, $noreg, !17, !DIExpression(), debug-location !18
+
+ bb.4:
+ RET 0
+
+ bb.3:
+ successors: %bb.4
+ JMP_1 %bb.4
+
+...
>From f79cc5513c1ddb38e6ebcd92e52a160176536873 Mon Sep 17 00:00:00 2001
From: huangjinjie <huangjinjie at bytedance.com>
Date: Fri, 26 Jun 2026 23:59:06 +0800
Subject: [PATCH 2/3] clang fromat
---
llvm/lib/CodeGen/BranchFolding.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/CodeGen/BranchFolding.cpp b/llvm/lib/CodeGen/BranchFolding.cpp
index 275e600a24d03..22806763e941d 100644
--- a/llvm/lib/CodeGen/BranchFolding.cpp
+++ b/llvm/lib/CodeGen/BranchFolding.cpp
@@ -1284,9 +1284,8 @@ static bool IsBranchOnlyBlock(MachineBasicBlock *MBB) {
}
static bool HasPseudoProbe(const MachineBasicBlock &MBB) {
- return llvm::any_of(MBB, [](const MachineInstr &MI) {
- return MI.isPseudoProbe();
- });
+ return llvm::any_of(
+ MBB, [](const MachineInstr &MI) { return MI.isPseudoProbe(); });
}
/// IsBetterFallthrough - Return true if it would be clearly better to
>From 02ab10bd5226700538d8d380d9fff52218e5a0a8 Mon Sep 17 00:00:00 2001
From: huangjinjie <huangjinjie at bytedance.com>
Date: Mon, 29 Jun 2026 21:04:14 +0800
Subject: [PATCH 3/3] move test to CodeGen/X86
---
.../{MIR => }/X86/branch-folder-pseudoprobe-debug-salvage.mir | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename llvm/test/CodeGen/{MIR => }/X86/branch-folder-pseudoprobe-debug-salvage.mir (100%)
diff --git a/llvm/test/CodeGen/MIR/X86/branch-folder-pseudoprobe-debug-salvage.mir b/llvm/test/CodeGen/X86/branch-folder-pseudoprobe-debug-salvage.mir
similarity index 100%
rename from llvm/test/CodeGen/MIR/X86/branch-folder-pseudoprobe-debug-salvage.mir
rename to llvm/test/CodeGen/X86/branch-folder-pseudoprobe-debug-salvage.mir
More information about the llvm-commits
mailing list