[llvm] [CorrelatedValuePropagation] Drop stale debug records on context-only rewrites (PR #218498)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 24 13:01:26 PDT 2026


https://github.com/shubhxho updated https://github.com/llvm/llvm-project/pull/218498

>From eb300a92335c86d505073a0866c993f3a0ea232a Mon Sep 17 00:00:00 2001
From: Shubh <shubh at shubhxho.com>
Date: Tue, 25 Aug 2026 00:45:49 +0530
Subject: [PATCH 1/2] [CorrelatedValuePropagation] Drop stale debug records on
 context-only rewrites

CorrelatedValuePropagation can narrow signed division and remainder, or replace an arithmetic shift with a logical shift, from facts that only hold at the optimized program use. Transferring an attached debug record then lets the debugger describe the source variable with a value that is not generally equivalent. Drop these records before replacing the instruction so the variable is unavailable rather than wrong.

Fixes #218366

Fixes #218406
---
 .../Scalar/CorrelatedValuePropagation.cpp     |   9 ++
 .../debug-records.ll                          | 114 ++++++++++++++++++
 2 files changed, 123 insertions(+)
 create mode 100644 llvm/test/Transforms/CorrelatedValuePropagation/debug-records.ll

diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
index 14f7df24c0433..d400238c2eb7a 100644
--- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
+++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
@@ -814,6 +814,11 @@ static bool narrowSDivOrSRem(BinaryOperator *Instr, const ConstantRange &LCR,
     if (BinOp->getOpcode() == Instruction::SDiv)
       BinOp->setIsExact(Instr->isExact());
 
+  // This narrowing is only valid at the uses of the result. A debug record
+  // can describe the value outside that range, where the narrow computation
+  // does not have the source-level value. Do not transfer those records to the
+  // sign-extended replacement.
+  Instr->dropDbgRecords();
   Instr->replaceAllUsesWith(Sext);
   Instr->eraseFromParent();
   return true;
@@ -1111,6 +1116,10 @@ static bool processAShr(BinaryOperator *SDI, LazyValueInfo *LVI) {
   BO->takeName(SDI);
   BO->setDebugLoc(SDI->getDebugLoc());
   BO->setIsExact(SDI->isExact());
+  // The replacement is equivalent only where LVI proved the operand
+  // non-negative. Debug records may be observed outside that range, where an
+  // arithmetic and logical shift have different values.
+  SDI->dropDbgRecords();
   SDI->replaceAllUsesWith(BO);
   SDI->eraseFromParent();
 
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/debug-records.ll b/llvm/test/Transforms/CorrelatedValuePropagation/debug-records.ll
new file mode 100644
index 0000000000000..468f7e8505415
--- /dev/null
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/debug-records.ll
@@ -0,0 +1,114 @@
+; RUN: opt -passes=correlated-propagation -S < %s | FileCheck %s
+;
+; CorrelatedValuePropagation can use facts that hold at an instruction to
+; change the instruction's representation. Those facts do not make the new
+; representation a valid source-level value for an attached debug record.
+; Keep the optimized instruction, but make the affected source variable
+; unavailable instead of transferring a misleading record.
+
+declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+; The branch proves that %x is non-negative at the shift. This permits the
+; ashr-to-lshr rewrite. A logical shift is nevertheless not the source value
+; of `shifted` for all source-level observations.
+define i16 @ashr_debug_value(i16 %x) !dbg !10 {
+; CHECK-LABEL: define i16 @ashr_debug_value(
+; CHECK: [[SHIFT:%.*]] = lshr i16 %x, 1
+; CHECK-NOT: #dbg_value(i16 [[SHIFT]], ![[SHIFTED:[0-9]+]]
+; CHECK: ret i16 [[SHIFT]]
+entry:
+  %nonnegative = icmp sge i16 %x, 0, !dbg !16
+  br i1 %nonnegative, label %shift, label %exit, !dbg !16
+
+shift:
+  %shr = ashr i16 %x, 1, !dbg !17
+  call void @llvm.dbg.value(metadata i16 %shr, metadata !13,
+                             metadata !DIExpression()), !dbg !17
+  ret i16 %shr, !dbg !18
+
+exit:
+  ret i16 24, !dbg !19
+}
+
+; The range facts on this block permit the division to be narrowed from i16
+; to i8. The replacement computes an i8 quotient and sign-extends it, so its
+; debug record must not describe the original i16 division.
+define i16 @sdiv_debug_value(i16 %x) !dbg !20 {
+; CHECK-LABEL: define i16 @sdiv_debug_value(
+; CHECK: [[LHS:%.*]] = trunc i16 %x to i8
+; CHECK: [[DIV:%.*]] = sdiv i8 [[LHS]], 42
+; CHECK: [[SEXT:%.*]] = sext i8 [[DIV]] to i16
+; CHECK-NOT: #dbg_value(i16 [[SEXT]], ![[QUOTIENT:[0-9]+]]
+; CHECK: ret i16 [[SEXT]]
+entry:
+  %lower = icmp sgt i16 %x, -43, !dbg !24
+  %upper = icmp slt i16 %x, 43, !dbg !24
+  %in.range = and i1 %lower, %upper, !dbg !24
+  br i1 %in.range, label %narrow, label %exit, !dbg !24
+
+narrow:
+  %div = sdiv i16 %x, 42, !dbg !25
+  call void @llvm.dbg.value(metadata i16 %div, metadata !22,
+                             metadata !DIExpression()), !dbg !25
+  ret i16 %div, !dbg !26
+
+exit:
+  ret i16 24, !dbg !27
+}
+
+; Remainders have the same narrowing implementation as divisions. Test the
+; separate opcode because signed remainders have different source semantics
+; for negative operands.
+define i16 @srem_debug_value(i16 %x) !dbg !30 {
+; CHECK-LABEL: define i16 @srem_debug_value(
+; CHECK: [[LHS:%.*]] = trunc i16 %x to i8
+; CHECK: [[REM:%.*]] = srem i8 [[LHS]], 42
+; CHECK: [[SEXT:%.*]] = sext i8 [[REM]] to i16
+; CHECK-NOT: #dbg_value(i16 [[SEXT]], ![[REMAINDER:[0-9]+]]
+; CHECK: ret i16 [[SEXT]]
+entry:
+  %lower = icmp sgt i16 %x, -43, !dbg !34
+  %upper = icmp slt i16 %x, 43, !dbg !34
+  %in.range = and i1 %lower, %upper, !dbg !34
+  br i1 %in.range, label %narrow, label %exit, !dbg !34
+
+narrow:
+  %rem = srem i16 %x, 42, !dbg !35
+  call void @llvm.dbg.value(metadata i16 %rem, metadata !32,
+                             metadata !DIExpression()), !dbg !35
+  ret i16 %rem, !dbg !36
+
+exit:
+  ret i16 24, !dbg !37
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !2, globals: !2, splitDebugInlining: false)
+!1 = !DIFile(filename: "debug-records.c", directory: "/")
+!2 = !{}
+!3 = !DISubroutineType(types: !2)
+!4 = !DIBasicType(name: "i16", size: 16, encoding: DW_ATE_signed)
+!6 = !{i32 2, !"Debug Info Version", i32 3}
+
+!10 = distinct !DISubprogram(name: "ashr_debug_value", scope: !1, file: !1, line: 1, type: !3, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!13 = !DILocalVariable(name: "shifted", scope: !10, file: !1, line: 2, type: !4)
+!16 = !DILocation(line: 3, column: 7, scope: !10)
+!17 = !DILocation(line: 4, column: 17, scope: !10)
+!18 = !DILocation(line: 5, column: 3, scope: !10)
+!19 = !DILocation(line: 6, column: 3, scope: !10)
+
+!20 = distinct !DISubprogram(name: "sdiv_debug_value", scope: !1, file: !1, line: 10, type: !3, scopeLine: 10, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!22 = !DILocalVariable(name: "quotient", scope: !20, file: !1, line: 11, type: !4)
+!24 = !DILocation(line: 12, column: 7, scope: !20)
+!25 = !DILocation(line: 13, column: 18, scope: !20)
+!26 = !DILocation(line: 14, column: 3, scope: !20)
+!27 = !DILocation(line: 15, column: 3, scope: !20)
+
+!30 = distinct !DISubprogram(name: "srem_debug_value", scope: !1, file: !1, line: 20, type: !3, scopeLine: 20, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!32 = !DILocalVariable(name: "remainder", scope: !30, file: !1, line: 21, type: !4)
+!34 = !DILocation(line: 22, column: 7, scope: !30)
+!35 = !DILocation(line: 23, column: 18, scope: !30)
+!36 = !DILocation(line: 24, column: 3, scope: !30)
+!37 = !DILocation(line: 25, column: 3, scope: !30)

>From ebb5774225012dbe312d68b1c2e92cb4c3be9eeb Mon Sep 17 00:00:00 2001
From: Shubh <shubh at shubhxho.com>
Date: Tue, 25 Aug 2026 01:30:52 +0530
Subject: [PATCH 2/2] [DebugInfo] Preserve debug records in scalar transforms

Update debug records when InstCombine inverts an fcmp predicate, and salvage them before Float2Int deletes converted instructions. This avoids presenting inverted predicates or poison values as source variables.

Fixes #215183

Fixes #147123
---
 llvm/lib/Transforms/InstCombine/InstructionCombining.cpp | 3 +++
 llvm/lib/Transforms/Scalar/Float2Int.cpp                 | 8 +++++++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 6aa33b0821f10..7759db46fe080 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -4392,6 +4392,9 @@ Instruction *InstCombinerImpl::visitCondBrInst(CondBrInst &BI) {
     // Swap destinations and condition.
     auto *Cmp = cast<CmpInst>(Cond);
     Cmp->setPredicate(CmpInst::getInversePredicate(Pred));
+    // The comparison now has the inverse value. Keep attached debug records
+    // describing the original source predicate by adding a logical not.
+    freelyInvertAllUsersOf(Cmp, &BI);
     BI.swapSuccessors();
     if (BPI)
       BPI->swapSuccEdgesProbabilities(BI.getParent());
diff --git a/llvm/lib/Transforms/Scalar/Float2Int.cpp b/llvm/lib/Transforms/Scalar/Float2Int.cpp
index 37822cf05f144..abd3baf5f1a9e 100644
--- a/llvm/lib/Transforms/Scalar/Float2Int.cpp
+++ b/llvm/lib/Transforms/Scalar/Float2Int.cpp
@@ -478,8 +478,14 @@ Value *Float2IntPass::convert(Instruction *I, Type *ToTy) {
 
 // Perform dead code elimination on the instructions we just modified.
 void Float2IntPass::cleanup() {
-  for (auto &I : reverse(ConvertedInsts))
+  for (auto &I : reverse(ConvertedInsts)) {
+    // The replacement for a converted instruction is not necessarily a
+    // source-level equivalent value for every debug use of an operand. Let
+    // salvageDebugInfo preserve a valid expression or make the location
+    // unavailable before deleting the original instruction.
+    salvageDebugInfo(*I.first);
     I.first->eraseFromParent();
+  }
 }
 
 bool Float2IntPass::runImpl(Function &F, const DominatorTree &DT) {



More information about the llvm-commits mailing list