[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