[llvm] [GlobalISel] Undef debug uses of erased instructions (PR #228906)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Oct 4 08:19:08 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-risc-v
@llvm/pr-subscribers-backend-aarch64
Author: Aochang Liu (HelloWorldU)
<details>
<summary>Changes</summary>
Erasing an instruction could leave a `DBG_VALUE` referring to its def: salvaging failures were ignored, and defs erased as dead through eraseInstrs were not salvaged at all. On RV32 the dangling s64 asserts in RegBankSelect with -g; other targets keep the dangling reference silently.
Salvage before every erase and undef what cannot be salvaged, skipping defs a combine replaced with a vreg defined elsewhere. The updated tests only change dangling references to `$noreg`.
Fixes #<!-- -->228874
Assisted-by: Claude Opus 5.5
---
Full diff: https://github.com/llvm/llvm-project/pull/228906.diff
7 Files Affected:
- (modified) llvm/lib/CodeGen/GlobalISel/Utils.cpp (+7)
- (modified) llvm/test/CodeGen/AArch64/GlobalISel/salvage-debug-info-dead.mir (+1-1)
- (modified) llvm/test/CodeGen/AArch64/GlobalISel/select-dbg-value.mir (+1-1)
- (modified) llvm/test/CodeGen/AArch64/GlobalISel/uaddo-8-16-bits.mir (+1-1)
- (added) llvm/test/CodeGen/RISCV/GlobalISel/legalizer-dbg-value-undef.mir (+81)
- (modified) llvm/test/DebugInfo/AArch64/debug-reg-bank.ll (+1-1)
- (modified) llvm/test/DebugInfo/X86/debug-reg-bank.ll (+1-1)
``````````diff
diff --git a/llvm/lib/CodeGen/GlobalISel/Utils.cpp b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
index 9a369bbdced1a..364a70ad8598a 100644
--- a/llvm/lib/CodeGen/GlobalISel/Utils.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
@@ -1647,6 +1647,7 @@ void llvm::saveUsesAndErase(MachineInstr &MI, MachineRegisterInfo &MRI,
DeadInstChain.insert(MRI.getVRegDef(Op.getReg()));
}
LLVM_DEBUG(dbgs() << MI << "Is dead; erasing.\n");
+ salvageDebugInfo(MRI, MI);
DeadInstChain.remove(&MI);
MI.eraseFromParent();
if (LocObserver)
@@ -1676,6 +1677,9 @@ void llvm::eraseInstr(MachineInstr &MI, MachineRegisterInfo &MRI,
void llvm::salvageDebugInfo(const MachineRegisterInfo &MRI, MachineInstr &MI) {
for (auto &Def : MI.defs()) {
assert(Def.isReg() && "Must be a reg");
+ // A combine may have replaced Def with a vreg that is defined elsewhere.
+ if (!MRI.hasOneDef(Def.getReg()))
+ continue;
SmallVector<MachineOperand *, 16> DbgUsers;
for (auto &MOUse : MRI.use_operands(Def.getReg())) {
@@ -1689,6 +1693,9 @@ void llvm::salvageDebugInfo(const MachineRegisterInfo &MRI, MachineInstr &MI) {
if (!DbgUsers.empty()) {
salvageDebugInfoForDbgValue(MRI, MI, DbgUsers);
}
+
+ // MI is about to be erased, so drop the debug uses that were not salvaged.
+ MRI.markUsesInDebugValueAsUndef(Def.getReg());
}
}
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/salvage-debug-info-dead.mir b/llvm/test/CodeGen/AArch64/GlobalISel/salvage-debug-info-dead.mir
index 6fd24f0af770e..8c6d383b264a0 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/salvage-debug-info-dead.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/salvage-debug-info-dead.mir
@@ -52,7 +52,7 @@ body: |
; CHECK-NEXT: DBG_VALUE $x0, $noreg, {{.*}}, !DIExpression(DW_OP_LLVM_convert, 64, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_LLVM_convert, 16, DW_ATE_unsigned, DW_OP_LLVM_convert, 16, DW_ATE_unsigned, DW_OP_LLVM_convert, 8, DW_ATE_unsigned, DW_OP_stack_value), debug-location !DILocation(line: 8, column: 7, scope: !6)
; CHECK: bb.1:
; CHECK-NEXT: DBG_VALUE $x0, $noreg, {{.*}}, !DIExpression(DW_OP_LLVM_convert, 64, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_stack_value), debug-location !DILocation(line: 9, column: 7, scope: !6)
- ; CHECK-NEXT: DBG_VALUE %4:_(i32), 0, {{.*}}, !DIExpression(), debug-location !DILocation(line: 9, column: 7, scope: !6)
+ ; CHECK-NEXT: DBG_VALUE $noreg, 0, {{.*}}, !DIExpression(), debug-location !DILocation(line: 9, column: 7, scope: !6)
bb.0:
liveins: $x0
%0:_(i64) = COPY $x0
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/select-dbg-value.mir b/llvm/test/CodeGen/AArch64/GlobalISel/select-dbg-value.mir
index b28ebbb90a79c..c455539c5b1b5 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/select-dbg-value.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/select-dbg-value.mir
@@ -65,7 +65,7 @@ body: |
; CHECK-LABEL: name: test_dbg_value_dead
; CHECK: liveins: $w0
; CHECK-NEXT: {{ $}}
- ; CHECK-NEXT: DBG_VALUE %1:gpr64, $noreg, !7, !DIExpression(), debug-location !9
+ ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, !7, !DIExpression(), debug-location !9
%0:gpr(i32) = COPY $w0
%1:gpr(i64) = G_ZEXT %0:gpr(i32)
DBG_VALUE %1(i64), $noreg, !7, !DIExpression(), debug-location !9
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/uaddo-8-16-bits.mir b/llvm/test/CodeGen/AArch64/GlobalISel/uaddo-8-16-bits.mir
index 67f1a85b89844..14041db257ca6 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/uaddo-8-16-bits.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/uaddo-8-16-bits.mir
@@ -310,7 +310,7 @@ body: |
; CHECK-NEXT: [[AND:%[0-9]+]]:_(i32) = G_AND [[ADD]], [[C]]
; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[AND]](i32), [[C1]]
- ; CHECK-NEXT: DBG_VALUE %6:_(i16)
+ ; CHECK-NEXT: DBG_VALUE $noreg
; CHECK-NEXT: G_BRCOND [[ICMP]](i1), %bb.2
; CHECK-NEXT: G_BR %bb.1
; CHECK-NEXT: {{ $}}
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-dbg-value-undef.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-dbg-value-undef.mir
new file mode 100644
index 0000000000000..472f36ca55e4e
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-dbg-value-undef.mir
@@ -0,0 +1,81 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=riscv32 -run-pass=legalizer %s -o - | FileCheck %s
+
+# A DBG_VALUE must not keep referring to a vreg whose def the legalizer
+# erases, both when salvaging fails and when the def dies as an artifact. A
+# DBG_VALUE of an unmerge result that a combine forwarded must be kept.
+
+--- |
+ define void @dead_merge() !dbg !3 {
+ ret void
+ }
+
+ define i64 @narrowed_xor() !dbg !8 {
+ ret i64 0
+ }
+
+ !llvm.dbg.cu = !{!0}
+ !llvm.module.flags = !{!2}
+
+ !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, emissionKind: FullDebug)
+ !1 = !DIFile(filename: "t.c", directory: "/")
+ !2 = !{i32 2, !"Debug Info Version", i32 3}
+ !3 = distinct !DISubprogram(name: "dead_merge", scope: !1, file: !1, type: !4, spFlags: DISPFlagDefinition, unit: !0)
+ !4 = !DISubroutineType(types: !{})
+ !5 = !DILocalVariable(name: "a", scope: !3, file: !1, type: !6)
+ !6 = !DIBasicType(name: "long long", size: 64, encoding: DW_ATE_signed)
+ !7 = !DILocation(line: 1, scope: !3)
+ !8 = distinct !DISubprogram(name: "narrowed_xor", scope: !1, file: !1, type: !4, spFlags: DISPFlagDefinition, unit: !0)
+ !9 = !DILocalVariable(name: "n", scope: !8, file: !1, type: !6)
+ !10 = !DILocation(line: 2, scope: !8)
+...
+---
+name: dead_merge
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $x10, $x11
+
+ ; CHECK-LABEL: name: dead_merge
+ ; CHECK: liveins: $x10, $x11
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, !7, !DIExpression(), debug-location !DILocation(line: 1, scope: !3)
+ ; CHECK-NEXT: PseudoRET
+ %0:_(s32) = COPY $x10
+ %1:_(s32) = COPY $x11
+ %2:_(s64) = G_MERGE_VALUES %0(s32), %1(s32)
+ DBG_VALUE %2(s64), $noreg, !5, !DIExpression(), debug-location !7
+ PseudoRET
+...
+---
+name: narrowed_xor
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $x10, $x11
+
+ ; CHECK-LABEL: name: narrowed_xor
+ ; CHECK: liveins: $x10, $x11
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 -1
+ ; CHECK-NEXT: [[XOR:%[0-9]+]]:_(s32) = G_XOR [[COPY]], [[C]]
+ ; CHECK-NEXT: [[XOR1:%[0-9]+]]:_(s32) = G_XOR [[COPY1]], [[C]]
+ ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, !7, !DIExpression(), debug-location !DILocation(line: 2, scope: !6)
+ ; CHECK-NEXT: DBG_VALUE [[XOR]](s32), $noreg, !7, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !DILocation(line: 2, scope: !6)
+ ; CHECK-NEXT: $x10 = COPY [[XOR]](s32)
+ ; CHECK-NEXT: $x11 = COPY [[XOR1]](s32)
+ ; CHECK-NEXT: PseudoRET implicit $x10, implicit $x11
+ %0:_(s32) = COPY $x10
+ %1:_(s32) = COPY $x11
+ %2:_(s64) = G_MERGE_VALUES %0(s32), %1(s32)
+ %3:_(s64) = G_CONSTANT i64 -1
+ %4:_(s64) = G_XOR %2, %3
+ DBG_VALUE %4(s64), $noreg, !9, !DIExpression(), debug-location !10
+ %5:_(s32), %6:_(s32) = G_UNMERGE_VALUES %4(s64)
+ DBG_VALUE %5(s32), $noreg, !9, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !10
+ $x10 = COPY %5(s32)
+ $x11 = COPY %6(s32)
+ PseudoRET implicit $x10, implicit $x11
+...
diff --git a/llvm/test/DebugInfo/AArch64/debug-reg-bank.ll b/llvm/test/DebugInfo/AArch64/debug-reg-bank.ll
index 19c33b34f4a39..ce6194175d00c 100644
--- a/llvm/test/DebugInfo/AArch64/debug-reg-bank.ll
+++ b/llvm/test/DebugInfo/AArch64/debug-reg-bank.ll
@@ -6,7 +6,7 @@ define void @foo(i32 %n) !dbg !7 {
; CHECK: bb.1.entry:
; CHECK-NEXT: liveins: $w0
; CHECK-NEXT: {{ $}}
- ; CHECK-NEXT: DBG_VALUE %2:gpr32, $noreg, !12, !DIExpression(), debug-location !19
+ ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, !12, !DIExpression(), debug-location !19
; CHECK-NEXT: RET_ReallyLR debug-location !20
entry:
%m = mul i32 %n, 13
diff --git a/llvm/test/DebugInfo/X86/debug-reg-bank.ll b/llvm/test/DebugInfo/X86/debug-reg-bank.ll
index 8d3bd95720a6b..d9eb5bceea260 100644
--- a/llvm/test/DebugInfo/X86/debug-reg-bank.ll
+++ b/llvm/test/DebugInfo/X86/debug-reg-bank.ll
@@ -6,7 +6,7 @@ define void @foo(i32 %n) !dbg !7 {
; CHECK: bb.1.entry:
; CHECK-NEXT: liveins: $edi
; CHECK-NEXT: {{ $}}
- ; CHECK-NEXT: DBG_VALUE %2:gr32, $noreg, !12, !DIExpression(), debug-location !19
+ ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, !12, !DIExpression(), debug-location !19
; CHECK-NEXT: RET 0, debug-location !20
entry:
%m = mul i32 %n, 13
``````````
</details>
https://github.com/llvm/llvm-project/pull/228906
More information about the llvm-commits
mailing list