[llvm] bbd61cb - Fix llvm bdce debug salvage (#220900)

via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 17 06:10:42 PDT 2026


Author: MrEven132
Date: 2026-09-17T14:10:36+01:00
New Revision: bbd61cb2e932bb7734cbf034dc50a6f5835e7d77

URL: https://github.com/llvm/llvm-project/commit/bbd61cb2e932bb7734cbf034dc50a6f5835e7d77
DIFF: https://github.com/llvm/llvm-project/commit/bbd61cb2e932bb7734cbf034dc50a6f5835e7d77.diff

LOG: Fix llvm bdce debug salvage (#220900)

BDCE simplifies constant-mask `and`, `or`, and `xor` instructions when
the changed bits are not demanded by program uses. It can similarly
replace a `sext` with a `zext` when none of the sign bits are demanded.
Both transformations used ordinary RAUW, which also redirected direct
debug uses to values with different meanings.

Replace only non-metadata uses when applying these optimizations. This
leaves direct debug users for BDCE's existing reverse-order salvage
pass, which reconstructs supported operations in a `DIExpression` and
makes unsupported locations unavailable.

Add regression tests covering salvage of `and`, `or`, and `xor`, the
unavailable fallback for an unrepresentable wide constant, and
preservation of signed-extension semantics when program uses receive a
`zext`.

Fixes #218890
Fixes #46640

Added: 
    llvm/test/Transforms/BDCE/dbg-binops-multiuse.ll
    llvm/test/Transforms/BDCE/dbg-sext-to-zext.ll

Modified: 
    llvm/lib/Transforms/Scalar/BDCE.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/BDCE.cpp b/llvm/lib/Transforms/Scalar/BDCE.cpp
index c907b9cd86c43..6697d6b083d5b 100644
--- a/llvm/lib/Transforms/Scalar/BDCE.cpp
+++ b/llvm/lib/Transforms/Scalar/BDCE.cpp
@@ -124,7 +124,9 @@ static bool bitTrackingDCE(Function &F, DemandedBits &DB) {
           Demanded.countl_zero() >= (DestBitSize - SrcBitSize)) {
         clearAssumptionsOfUsers(SE, DB);
         IRBuilder<> Builder(SE);
-        I.replaceAllUsesWith(
+        // Leave debug-info users pointing at the old instruction so they can
+        // be salvaged below.
+        I.replaceNonMetadataUsesWith(
             Builder.CreateZExt(SE->getOperand(0), DstTy, SE->getName()));
         Worklist.push_back(SE);
         Changed = true;
@@ -156,7 +158,9 @@ static bool bitTrackingDCE(Function &F, DemandedBits &DB) {
           // Avoid incorrect replacement of self-referential values.
           if (CanBeSimplified && BO != BO->getOperand(0)) {
             clearAssumptionsOfUsers(BO, DB);
-            BO->replaceAllUsesWith(BO->getOperand(0));
+            // Leave debug-info users pointing at the old instruction so they
+            // can be salvaged below.
+            BO->replaceNonMetadataUsesWith(BO->getOperand(0));
             Worklist.push_back(BO);
             ++NumSimplified;
             Changed = true;

diff  --git a/llvm/test/Transforms/BDCE/dbg-binops-multiuse.ll b/llvm/test/Transforms/BDCE/dbg-binops-multiuse.ll
new file mode 100644
index 0000000000000..bed5599c0399d
--- /dev/null
+++ b/llvm/test/Transforms/BDCE/dbg-binops-multiuse.ll
@@ -0,0 +1,69 @@
+; RUN: opt -S -passes=bdce < %s | FileCheck %s
+
+; Check that BDCE salvages debug uses when simplifying multi-use binary
+; operators. If the operation cannot be represented in a DIExpression, the
+; debug value must be killed instead of being replaced with the first operand.
+
+define void @test(i64 %a, i128 %wide) !dbg !5 {
+; CHECK-LABEL: define void @test(
+; CHECK-SAME: i64 [[A:%.*]], i128 [[WIDE:%.*]])
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    #dbg_value(i64 [[A]], [[OR_VAR:![0-9]+]], !DIExpression(DW_OP_constu, 3, DW_OP_or, DW_OP_stack_value)
+; CHECK-NEXT:    [[OR_USE:%.*]] = and i64 [[A]], 8
+; CHECK-NEXT:    #dbg_value(i64 [[A]], [[XOR_VAR:![0-9]+]], !DIExpression(DW_OP_constu, 3, DW_OP_xor, DW_OP_stack_value)
+; CHECK-NEXT:    [[XOR_USE:%.*]] = and i64 [[A]], 8
+; CHECK-NEXT:    #dbg_value(i64 [[A]], [[AND_VAR:![0-9]+]], !DIExpression(DW_OP_constu, 24, DW_OP_and, DW_OP_stack_value)
+; CHECK-NEXT:    [[AND_USE:%.*]] = and i64 [[A]], 8
+; CHECK-NEXT:    #dbg_value(i128 poison, [[WIDE_VAR:![0-9]+]], !DIExpression()
+; CHECK-NEXT:    [[WIDE_USE:%.*]] = and i128 [[WIDE]], 8
+; CHECK-NEXT:    call void @use(i64 [[OR_USE]])
+; CHECK-NEXT:    call void @use(i64 [[XOR_USE]])
+; CHECK-NEXT:    call void @use(i64 [[AND_USE]])
+; CHECK-NEXT:    call void @use_wide(i128 [[WIDE_USE]])
+; CHECK-NEXT:    ret void
+entry:
+  %or = or i64 %a, 3
+    #dbg_value(i64 %or, !9, !DIExpression(), !14)
+  %or.use = and i64 %or, 8
+
+  %xor = xor i64 %a, 3
+    #dbg_value(i64 %xor, !10, !DIExpression(), !14)
+  %xor.use = and i64 %xor, 8
+
+  %and = and i64 %a, 24
+    #dbg_value(i64 %and, !11, !DIExpression(), !14)
+  %and.use = and i64 %and, 8
+
+  %wide.or = or i128 %wide, 3
+    #dbg_value(i128 %wide.or, !12, !DIExpression(), !14)
+  %wide.use = and i128 %wide.or, 8
+
+  call void @use(i64 %or.use)
+  call void @use(i64 %xor.use)
+  call void @use(i64 %and.use)
+  call void @use_wide(i128 %wide.use)
+  ret void
+}
+
+declare void @use(i64)
+declare void @use_wide(i128)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "test.c", directory: "/")
+!2 = !{}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 2, !"Dwarf Version", i32 5}
+!5 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
+!6 = !DISubroutineType(types: !7)
+!7 = !{null, !13, !15}
+!8 = !{!9, !10, !11, !12}
+!9 = !DILocalVariable(name: "or", scope: !5, file: !1, line: 2, type: !13)
+!10 = !DILocalVariable(name: "xor", scope: !5, file: !1, line: 3, type: !13)
+!11 = !DILocalVariable(name: "and", scope: !5, file: !1, line: 4, type: !13)
+!12 = !DILocalVariable(name: "wide", scope: !5, file: !1, line: 5, type: !15)
+!13 = !DIBasicType(name: "uint64_t", size: 64, encoding: DW_ATE_unsigned)
+!14 = !DILocation(line: 2, column: 1, scope: !5)
+!15 = !DIBasicType(name: "uint128_t", size: 128, encoding: DW_ATE_unsigned)

diff  --git a/llvm/test/Transforms/BDCE/dbg-sext-to-zext.ll b/llvm/test/Transforms/BDCE/dbg-sext-to-zext.ll
new file mode 100644
index 0000000000000..911f42b102487
--- /dev/null
+++ b/llvm/test/Transforms/BDCE/dbg-sext-to-zext.ll
@@ -0,0 +1,39 @@
+; RUN: opt -S -passes=bdce < %s | FileCheck %s
+
+; Check that BDCE preserves the signed-extension semantics of direct debug uses
+; when replacing a sext with a zext because the sign bits are not demanded by
+; program uses.
+
+define i32 @test(i32 %a) !dbg !5 {
+; CHECK-LABEL: define i32 @test(
+; CHECK-SAME: i32 [[A:%.*]])
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i32 [[A]] to i64
+; CHECK-NEXT:    #dbg_value(i32 [[A]], [[VAR:![0-9]+]], !DIExpression(DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_LLVM_convert, 64, DW_ATE_signed, DW_OP_stack_value)
+; CHECK-NEXT:    [[OR:%.*]] = or i64 [[ZEXT]], 5
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[OR]] to i32
+; CHECK-NEXT:    ret i32 [[TRUNC]]
+entry:
+  %sext = sext i32 %a to i64
+    #dbg_value(i64 %sext, !9, !DIExpression(), !10)
+  %or = or i64 %sext, 5
+  %trunc = trunc i64 %or to i32
+  ret i32 %trunc
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "test.c", directory: "/")
+!2 = !{}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 2, !"Dwarf Version", i32 5}
+!5 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
+!6 = !DISubroutineType(types: !7)
+!7 = !{!11, !11}
+!8 = !{!9}
+!9 = !DILocalVariable(name: "extended", scope: !5, file: !1, line: 2, type: !12)
+!10 = !DILocation(line: 2, column: 1, scope: !5)
+!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!12 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed)


        


More information about the llvm-commits mailing list