[llvm] [JumpThreading] Infer constants from correlated PHIs (PR #190184)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 2 07:52:54 PDT 2026
https://github.com/Lane0218 updated https://github.com/llvm/llvm-project/pull/190184
>From 8d60280750aca908ab5b1128f3a24868033196f1 Mon Sep 17 00:00:00 2001
From: Lane0218 <laneljc at qq.com>
Date: Thu, 2 Apr 2026 22:52:16 +0800
Subject: [PATCH] [JumpThreading] Infer constants from correlated PHIs
Teach JumpThreading to infer a PHI value on a predecessor edge from
another PHI in the same block that controls the predecessor branch.
If the branch condition makes exactly one incoming block feasible, use
the corresponding incoming value of the queried PHI. This fixes the
missed fold in the reduced reproducer from #188452.
Add a regression test and a negative test.
---
llvm/lib/Transforms/Scalar/JumpThreading.cpp | 115 ++++++++++++++++++
.../JumpThreading/phi-correlated-edge.ll | 72 +++++++++++
2 files changed, 187 insertions(+)
create mode 100644 llvm/test/Transforms/JumpThreading/phi-correlated-edge.ll
diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
index 415136b612ac2..4849a80245a3f 100644
--- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
@@ -549,6 +549,119 @@ static Constant *getKnownConstant(Value *Val, ConstantPreference Preference) {
return dyn_cast<ConstantInt>(Val);
}
+static bool getUniqueIncomingValueForBlock(PHINode *PN, BasicBlock *IncomingBB,
+ Value *&IncomingValue) {
+ IncomingValue = nullptr;
+ bool SeenIncoming = false;
+ for (unsigned I = 0, E = PN->getNumIncomingValues(); I != E; ++I) {
+ if (PN->getIncomingBlock(I) != IncomingBB)
+ continue;
+
+ Value *CurrentValue = PN->getIncomingValue(I);
+ if (!SeenIncoming) {
+ IncomingValue = CurrentValue;
+ SeenIncoming = true;
+ continue;
+ }
+
+ if (IncomingValue != CurrentValue)
+ return false;
+ }
+
+ return SeenIncoming;
+}
+
+static std::optional<bool>
+getIncomingBlockConditionValue(Value *Cond, PHINode *GuardPN,
+ unsigned IncomingIdx) {
+ BasicBlock *PhiBB = GuardPN->getParent();
+ BasicBlock *IncomingBB = GuardPN->getIncomingBlock(IncomingIdx);
+
+ auto *Cmp = dyn_cast<ICmpInst>(Cond);
+ if (!Cmp)
+ return std::nullopt;
+
+ PHINode *CmpPN = dyn_cast<PHINode>(Cmp->getOperand(0));
+ bool GuardOnLHS = true;
+ if (!CmpPN || CmpPN->getParent() != PhiBB) {
+ CmpPN = dyn_cast<PHINode>(Cmp->getOperand(1));
+ GuardOnLHS = false;
+ }
+
+ if (CmpPN != GuardPN)
+ return std::nullopt;
+
+ Value *LHS = GuardOnLHS
+ ? GuardPN->getIncomingValue(IncomingIdx)
+ : Cmp->getOperand(0)->DoPHITranslation(PhiBB, IncomingBB);
+ Value *RHS = GuardOnLHS
+ ? Cmp->getOperand(1)->DoPHITranslation(PhiBB, IncomingBB)
+ : GuardPN->getIncomingValue(IncomingIdx);
+ if (auto *C = dyn_cast_or_null<ConstantInt>(simplifyCmpInst(
+ Cmp->getPredicate(), LHS, RHS, {PhiBB->getDataLayout()})))
+ return !C->isZero();
+ return std::nullopt;
+}
+
+/// Try to infer a PHI value on the edge FromBB -> ToBB by using another PHI in
+/// the same block that controls the branch in FromBB. If the branch condition
+/// makes exactly one incoming block feasible, use the corresponding incoming
+/// value of V.
+static Constant *
+getKnownConstantFromCorrelatedPHI(Value *V, BasicBlock *FromBB,
+ BasicBlock *ToBB,
+ ConstantPreference Preference) {
+ auto *ValPN = dyn_cast<PHINode>(V);
+ if (!ValPN)
+ return nullptr;
+
+ auto *BI = dyn_cast<CondBrInst>(FromBB->getTerminator());
+ if (!BI || BI->getSuccessor(0) == BI->getSuccessor(1))
+ return nullptr;
+
+ const bool IsTrueDest = BI->getSuccessor(0) == ToBB;
+ if (!IsTrueDest && BI->getSuccessor(1) != ToBB)
+ return nullptr;
+
+ auto *Cond = BI->getCondition();
+ auto *Cmp = dyn_cast<ICmpInst>(Cond);
+ if (!Cmp)
+ return nullptr;
+
+ PHINode *GuardPN = dyn_cast<PHINode>(Cmp->getOperand(0));
+ if (!GuardPN || GuardPN->getParent() != ValPN->getParent())
+ GuardPN = dyn_cast<PHINode>(Cmp->getOperand(1));
+ if (!GuardPN || GuardPN->getParent() != ValPN->getParent())
+ return nullptr;
+
+ SmallPtrSet<BasicBlock *, 4> SeenIncomingBlocks;
+ SmallVector<BasicBlock *, 2> CandidateIncomingBlocks;
+ for (unsigned I = 0, E = GuardPN->getNumIncomingValues(); I != E; ++I) {
+ BasicBlock *IncomingBB = GuardPN->getIncomingBlock(I);
+ if (!SeenIncomingBlocks.insert(IncomingBB).second)
+ return nullptr;
+
+ std::optional<bool> CondValue =
+ getIncomingBlockConditionValue(Cond, GuardPN, I);
+ if (CondValue && *CondValue != IsTrueDest)
+ continue;
+
+ CandidateIncomingBlocks.push_back(IncomingBB);
+ if (CandidateIncomingBlocks.size() > 1)
+ return nullptr;
+ }
+
+ if (CandidateIncomingBlocks.size() != 1)
+ return nullptr;
+
+ Value *IncomingValue = nullptr;
+ if (!getUniqueIncomingValueForBlock(ValPN, CandidateIncomingBlocks.front(),
+ IncomingValue))
+ return nullptr;
+
+ return getKnownConstant(IncomingValue, Preference);
+}
+
/// computeValueKnownInPredecessors - Given a basic block BB and a value V, see
/// if we can infer that the value is a known ConstantInt/BlockAddress or undef
/// in any of our predecessors. If so, return the known list of value and pred
@@ -588,6 +701,8 @@ bool JumpThreadingPass::computeValueKnownInPredecessorsImpl(
// If the value is known by LazyValueInfo to be a constant in a
// predecessor, use that information to try to thread this block.
Constant *PredCst = LVI->getConstantOnEdge(V, P, BB, CxtI);
+ if (!PredCst)
+ PredCst = getKnownConstantFromCorrelatedPHI(V, P, BB, Preference);
// If I is a non-local compare-with-constant instruction, use more-rich
// 'getPredicateOnEdge' method. This would be able to handle value
// inequalities better, for example if the compare is "X < 4" and "X < 3"
diff --git a/llvm/test/Transforms/JumpThreading/phi-correlated-edge.ll b/llvm/test/Transforms/JumpThreading/phi-correlated-edge.ll
new file mode 100644
index 0000000000000..b48616e177639
--- /dev/null
+++ b/llvm/test/Transforms/JumpThreading/phi-correlated-edge.ll
@@ -0,0 +1,72 @@
+; RUN: opt -S -passes=jump-threading,simplifycfg < %s | FileCheck %s
+
+declare ptr @g()
+declare i64 @h()
+declare void @side()
+
+define void @correlated_phi(i32 %n) {
+; CHECK-LABEL: define void @correlated_phi(
+entry:
+ %p = call ptr @g()
+ %isnull = icmp eq ptr %p, null
+ br label %preheader
+
+preheader:
+ %iv = phi i32 [ 0, %entry ], [ %n, %crit_edge ]
+ %flag = phi i1 [ %isnull, %entry ], [ true, %crit_edge ]
+ br label %loop
+
+issue_check:
+ br i1 %flag, label %ret, label %issue_dead
+
+crit_edge:
+; CHECK: crit_edge:
+; CHECK-NEXT: %exit = icmp eq i32 %iv, 1
+; CHECK-NEXT: br i1 %exit, label %ret, label %preheader
+ %exit = icmp eq i32 %iv, 1
+ br i1 %exit, label %issue_check, label %preheader
+
+loop:
+ %size = call i64 @h()
+ %empty = icmp eq i64 %size, 0
+ br i1 %empty, label %crit_edge, label %loop
+
+ret:
+ ret void
+
+issue_dead:
+ %extra = call i64 @h()
+ br label %ret
+}
+
+; CHECK-NOT: issue_dead:
+; CHECK-NOT: %flag = phi
+
+define void @correlated_phi_nonconstant(i1 %c, i1 %d) {
+; CHECK-LABEL: define void @correlated_phi_nonconstant(
+entry:
+ br label %preheader
+
+preheader:
+ %iv = phi i32 [ 0, %entry ], [ 1, %backedge ]
+ %flag = phi i1 [ %c, %entry ], [ %d, %backedge ]
+ br label %backedge
+
+backedge:
+ %exit = icmp eq i32 %iv, 1
+ br i1 %exit, label %neg_check, label %preheader
+
+neg_check:
+; CHECK: neg_check:
+; CHECK-NEXT: br i1 %flag, label %neg_ret, label %neg_dead
+ br i1 %flag, label %neg_ret, label %neg_dead
+
+neg_dead:
+; CHECK: neg_dead:
+; CHECK-NEXT: call void @side()
+ call void @side()
+ br label %neg_ret
+
+neg_ret:
+ ret void
+}
More information about the llvm-commits
mailing list