[llvm] [JumpThreading] Infer constants from correlated PHIs (PR #190184)

via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 2 07:46:19 PDT 2026


https://github.com/Lane0218 updated https://github.com/llvm/llvm-project/pull/190184

>From 653cda8b52d71e911281826182810cc48ccb8b62 Mon Sep 17 00:00:00 2001
From: Lane0218 <laneljc at qq.com>
Date: Thu, 2 Apr 2026 22:34:13 +0800
Subject: [PATCH 1/2] [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  | 117 ++++++++++++++++++
 .../JumpThreading/phi-correlated-edge.ll      |  72 +++++++++++
 2 files changed, 189 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..9712fb71664de 100644
--- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
@@ -549,6 +549,120 @@ 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,
+                                  LazyValueInfo *LVI) {
+  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 +702,9 @@ 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, LVI);
       // 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
+}

>From 699c7e3e278bbc6aeb7823d86ed95507e8296849 Mon Sep 17 00:00:00 2001
From: Lane0218 <laneljc at qq.com>
Date: Thu, 2 Apr 2026 22:45:07 +0800
Subject: [PATCH 2/2] [JumpThreading] Clean up correlated PHI helper

Remove an unused LazyValueInfo parameter from the correlated PHI helper
and adjust formatting to match clang-format.
---
 llvm/lib/Transforms/Scalar/JumpThreading.cpp | 27 +++++++++-----------
 1 file changed, 12 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
index 9712fb71664de..986da22733da9 100644
--- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
@@ -591,14 +591,14 @@ getIncomingBlockConditionValue(Value *Cond, PHINode *GuardPN,
   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()})))
+  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;
 }
@@ -607,11 +607,9 @@ getIncomingBlockConditionValue(Value *Cond, PHINode *GuardPN,
 /// 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,
-                                  LazyValueInfo *LVI) {
+static Constant *getKnownConstantFromCorrelatedPHI(
+    Value *V, BasicBlock *FromBB, BasicBlock *ToBB,
+    ConstantPreference Preference) {
   auto *ValPN = dyn_cast<PHINode>(V);
   if (!ValPN)
     return nullptr;
@@ -703,8 +701,7 @@ bool JumpThreadingPass::computeValueKnownInPredecessorsImpl(
       // 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, LVI);
+        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"



More information about the llvm-commits mailing list