[llvm] [CorrelatedValuePropagation] Drop stale debug records on context-only rewrites (PR #218498)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Oct 4 12:57:00 PDT 2026
https://github.com/shubhxho updated https://github.com/llvm/llvm-project/pull/218498
>From 2ecc72de9aeb57940b2ae04ad234628c6778be13 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/3] [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 fa0a6d5afeceefb0ba20354d09626751338b356e 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/3] [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) {
>From 6cc8267bc8901ca150a7126afaa9b809eda956a5 Mon Sep 17 00:00:00 2001
From: Shubh <shubh at shubhxho.com>
Date: Tue, 25 Aug 2026 01:34:21 +0530
Subject: [PATCH 3/3] [DebugInfo] Preserve valid locations through CFG
transforms
Merge distinct locations when FlattenCFG combines equivalent bodies, and avoid sinking values described by debug records in GVNSink. These changes prevent a debugger from attributing execution to the wrong branch or referring to a value before its definition.
Fixes #217606
Fixes #212399
---
llvm/lib/Transforms/Scalar/GVNSink.cpp | 2 +-
llvm/lib/Transforms/Utils/FlattenCFG.cpp | 14 ++++++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Scalar/GVNSink.cpp b/llvm/lib/Transforms/Scalar/GVNSink.cpp
index 67196ef9715f1..719a32aa9a363 100644
--- a/llvm/lib/Transforms/Scalar/GVNSink.cpp
+++ b/llvm/lib/Transforms/Scalar/GVNSink.cpp
@@ -536,7 +536,7 @@ class GVNSink {
bool shouldAvoidSinkingInstruction(Instruction *I) {
// These instructions may change or break semantics if moved.
if (isa<PHINode>(I) || I->isEHPad() || isa<AllocaInst>(I) ||
- I->getType()->isTokenTy())
+ I->getType()->isTokenTy() || I->isUsedByMetadata())
return true;
return false;
}
diff --git a/llvm/lib/Transforms/Utils/FlattenCFG.cpp b/llvm/lib/Transforms/Utils/FlattenCFG.cpp
index 2837fb57b44ac..bef8cdae869a6 100644
--- a/llvm/lib/Transforms/Utils/FlattenCFG.cpp
+++ b/llvm/lib/Transforms/Utils/FlattenCFG.cpp
@@ -428,6 +428,8 @@ bool FlattenCFGOpt::MergeIfRegion(BasicBlock *BB, IRBuilder<> &Builder) {
// Either then-path or else-path should be empty.
bool InvertCond2 = false;
+ BasicBlock *Body1;
+ BasicBlock *Body2;
BinaryOperator::BinaryOps CombineOp;
if (IfFalse1 == FirstEntryBlock) {
// The else-path is empty, so we must use "or" operation to combine the
@@ -443,6 +445,8 @@ bool FlattenCFGOpt::MergeIfRegion(BasicBlock *BB, IRBuilder<> &Builder) {
if (!CompareIfRegionBlock(IfTrue1, IfTrue2, SecondEntryBlock))
return false;
+ Body1 = IfTrue1;
+ Body2 = IfTrue2;
} else if (IfTrue1 == FirstEntryBlock) {
// The then-path is empty, so we must use "and" operation to combine the
// conditions.
@@ -457,6 +461,8 @@ bool FlattenCFGOpt::MergeIfRegion(BasicBlock *BB, IRBuilder<> &Builder) {
if (!CompareIfRegionBlock(IfFalse1, IfFalse2, SecondEntryBlock))
return false;
+ Body1 = IfFalse1;
+ Body2 = IfFalse2;
} else
return false;
@@ -472,6 +478,14 @@ bool FlattenCFGOpt::MergeIfRegion(BasicBlock *BB, IRBuilder<> &Builder) {
return false;
}
+ // The merged body represents either source path. Keep a common source
+ // location for each retained instruction instead of selecting one path's
+ // location unconditionally.
+ auto I1 = Body1->begin();
+ auto I2 = Body2->begin();
+ for (; I1 != Body1->end(); ++I1, ++I2)
+ I2->applyMergedLocation(I1->getDebugLoc(), I2->getDebugLoc());
+
// Merge \param SecondEntryBlock into \param FirstEntryBlock.
FirstEntryBlock->back().eraseFromParent();
FirstEntryBlock->splice(FirstEntryBlock->end(), SecondEntryBlock);
More information about the llvm-commits
mailing list