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

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 24 12:51:20 PDT 2026


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

>From 96d87fab7082db7996f2045de8bb1355a2571f16 Mon Sep 17 00:00:00 2001
From: Shubh <shubh at shubhxho.com>
Date: Tue, 25 Aug 2026 00:45:49 +0530
Subject: [PATCH] [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)



More information about the llvm-commits mailing list