[llvm] [ValueTracking] Teach isGuaranteedNotToBePoison to reason about ZExt (PR #228493)

Sayan Sivakumaran via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 2 10:48:12 PDT 2026


https://github.com/sivakusayan updated https://github.com/llvm/llvm-project/pull/228493

>From 5278e8423ea8c4b63336e375cf97fafb8e845cce Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Fri, 2 Oct 2026 10:41:56 -0500
Subject: [PATCH 1/2] Precommit tests

---
 llvm/unittests/Analysis/ValueTrackingTest.cpp | 24 +++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 75cb0f52b15d5..8495c3878ec95 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -1198,6 +1198,30 @@ TEST_F(ValueTrackingTest, isGuaranteedNotToBePoison_phi) {
   }
 }
 
+TEST_F(ValueTrackingTest, isGuaranteedNotToBePoison_ZExt_NNeg) {
+  {
+    auto M = parseModule(R"(
+  declare void @llvm.assume(i1)
+
+  define i64 @test(i32 %X, i32 %Y) {
+    %res = icmp sge i32 %X, 0
+    call void @llvm.assume(i1 %res)
+
+    %ExtendX = zext nneg i32 %X to i64
+    %ExtendY = zext nneg i32 %Y to i64
+    ret i64 %ExtendY
+  })");
+    auto *F = M->getFunction("test");
+    auto *ExtendX = &findInstructionByName(F, "ExtendX");
+    auto *ExtendY = &findInstructionByName(F, "ExtendY");
+    AssumptionCache AC(*F);
+    SimplifyQuery SQ(M->getDataLayout(), /*DT=*/nullptr, &AC, /*CtxI=*/ExtendY);
+
+    EXPECT_TRUE(isGuaranteedNotToBePoison(ExtendX, SQ.AC, SQ.CtxI, SQ.DT));
+    EXPECT_FALSE(isGuaranteedNotToBePoison(ExtendY, SQ.AC, SQ.CtxI, SQ.DT));
+  }
+}
+
 TEST_F(ValueTrackingTest, isGuaranteedNotToBeUndefOrPoison) {
   parseAssembly("declare void @f(i32 noundef)"
                 "define void @test(i32 %x) {\n"

>From 63c7d6ba17f9fd6dc3fdb6ce73660f6d537d72eb Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Fri, 2 Oct 2026 11:13:17 -0500
Subject: [PATCH 2/2] [ValueTracking] Teach isGuaranteedNotToBePoison to reason
 about ZExt

---
 llvm/lib/Analysis/ValueTracking.cpp        | 97 +++++++++++++---------
 llvm/test/Transforms/InstCombine/freeze.ll |  3 +-
 2 files changed, 58 insertions(+), 42 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index e9c1b9fa95376..994cf887b9cf3 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -8151,6 +8151,60 @@ bool llvm::impliesPoison(const Value *ValAssumedPoison, const Value *V) {
 
 static bool programUndefinedIfUndefOrPoison(const Value *V, bool PoisonOnly);
 
+static bool isGuaranteedNotToBeUndefOrPoisonSpecialCases(
+    const Value *V, AssumptionCache *AC, const Instruction *CtxI,
+    const DominatorTree *DT, UndefPoisonKind Kind) {
+  // If we have `V = zext nneg <ty> %X`, where %X is provably non-negative, then
+  // V is never undef or poison.
+  Value *X;
+  if (match(V, m_NNegZExt(m_Value(X)))) {
+    return isKnownNonNegative(X, SimplifyQuery({}, DT, AC, CtxI));
+  }
+
+  // If V is used as a branch condition before reaching CtxI, V cannot be
+  // undef or poison.
+  //   br V, BB1, BB2
+  // BB1:
+  //   CtxI ; V cannot be undef or poison here
+  if (!DT)
+    return false;
+  auto *DNode = DT->getNode(CtxI->getParent());
+  if (!DNode)
+    // Unreachable block
+    return false;
+  auto *Dominator = DNode->getIDom();
+  // This check is purely for compile time reasons: we can skip the IDom walk
+  // if what we are checking for includes undef and the value is not an integer.
+  if (!includesUndef(Kind) || V->getType()->isIntegerTy())
+    while (Dominator) {
+      auto *TI = Dominator->getBlock()->getTerminatorOrNull();
+
+      Value *Cond = nullptr;
+      if (auto BI = dyn_cast_or_null<CondBrInst>(TI)) {
+        Cond = BI->getCondition();
+      } else if (auto SI = dyn_cast_or_null<SwitchInst>(TI)) {
+        Cond = SI->getCondition();
+      }
+
+      if (Cond) {
+        if (Cond == V)
+          return true;
+        else if (!includesUndef(Kind) && isa<Operator>(Cond)) {
+          // For poison, we can analyze further
+          auto *Opr = cast<Operator>(Cond);
+          if (any_of(Opr->operands(), [V](const Use &U) {
+                return V == U && propagatesPoison(U);
+              }))
+            return true;
+        }
+      }
+
+      Dominator = Dominator->getIDom();
+    }
+
+  return false;
+}
+
 static bool isGuaranteedNotToBeUndefOrPoison(
     const Value *V, AssumptionCache *AC, const Instruction *CtxI,
     const DominatorTree *DT, unsigned Depth, UndefPoisonKind Kind) {
@@ -8261,48 +8315,11 @@ static bool isGuaranteedNotToBeUndefOrPoison(
     return true;
 
   // CtxI may be null or a cloned instruction.
-  if (!CtxI || !CtxI->getParent() || !DT)
+  if (!CtxI || !CtxI->getParent())
     return false;
 
-  auto *DNode = DT->getNode(CtxI->getParent());
-  if (!DNode)
-    // Unreachable block
-    return false;
-
-  // If V is used as a branch condition before reaching CtxI, V cannot be
-  // undef or poison.
-  //   br V, BB1, BB2
-  // BB1:
-  //   CtxI ; V cannot be undef or poison here
-  auto *Dominator = DNode->getIDom();
-  // This check is purely for compile time reasons: we can skip the IDom walk
-  // if what we are checking for includes undef and the value is not an integer.
-  if (!includesUndef(Kind) || V->getType()->isIntegerTy())
-    while (Dominator) {
-      auto *TI = Dominator->getBlock()->getTerminatorOrNull();
-
-      Value *Cond = nullptr;
-      if (auto BI = dyn_cast_or_null<CondBrInst>(TI)) {
-        Cond = BI->getCondition();
-      } else if (auto SI = dyn_cast_or_null<SwitchInst>(TI)) {
-        Cond = SI->getCondition();
-      }
-
-      if (Cond) {
-        if (Cond == V)
-          return true;
-        else if (!includesUndef(Kind) && isa<Operator>(Cond)) {
-          // For poison, we can analyze further
-          auto *Opr = cast<Operator>(Cond);
-          if (any_of(Opr->operands(), [V](const Use &U) {
-                return V == U && propagatesPoison(U);
-              }))
-            return true;
-        }
-      }
-
-      Dominator = Dominator->getIDom();
-    }
+  if (isGuaranteedNotToBeUndefOrPoisonSpecialCases(V, AC, CtxI, DT, Kind))
+    return true;
 
   if (AC && getKnowledgeValidInContext(V, {Attribute::NoUndef}, *AC, CtxI, DT))
     return true;
diff --git a/llvm/test/Transforms/InstCombine/freeze.ll b/llvm/test/Transforms/InstCombine/freeze.ll
index 5938c31393258..ffa42292a0018 100644
--- a/llvm/test/Transforms/InstCombine/freeze.ll
+++ b/llvm/test/Transforms/InstCombine/freeze.ll
@@ -1794,8 +1794,7 @@ define i32 @pr171435_1(ptr noundef %arg) {
 ; CHECK:       [[BB_2]]:
 ; CHECK-NEXT:    br label %[[BB_3]]
 ; CHECK:       [[BB_3]]:
-; CHECK-NEXT:    [[PHI:%.*]] = phi i32 [ [[LOAD]], %[[BB_1]] ], [ 0, %[[BB_2]] ]
-; CHECK-NEXT:    [[PHI_FR:%.*]] = freeze i32 [[PHI]]
+; CHECK-NEXT:    [[PHI_FR:%.*]] = phi i32 [ [[LOAD]], %[[BB_1]] ], [ 0, %[[BB_2]] ]
 ; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[PHI_FR]], -8
 ; CHECK-NEXT:    store i32 [[ADD]], ptr [[GETELEMENTPTR]], align 4
 ; CHECK-NEXT:    ret i32 0



More information about the llvm-commits mailing list