[llvm-branch-commits] [llvm] 051ec9f - [ValueTracking] Strengthen impliesPoison reasoning
Nikita Popov via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Jan 19 09:09:02 PST 2021
Author: Nikita Popov
Date: 2021-01-19T18:04:23+01:00
New Revision: 051ec9f5f43a83e23bd3e20e512fc5ec44c19850
URL: https://github.com/llvm/llvm-project/commit/051ec9f5f43a83e23bd3e20e512fc5ec44c19850
DIFF: https://github.com/llvm/llvm-project/commit/051ec9f5f43a83e23bd3e20e512fc5ec44c19850.diff
LOG: [ValueTracking] Strengthen impliesPoison reasoning
Split impliesPoison into two recursive walks, one over V, the
other over ValAssumedPoison. This allows us to reason about poison
implications in a number of additional cases that are important
in practice. This is a generalized form of D94859, which handles
the cmp to cmp implication in particular.
Differential Revision: https://reviews.llvm.org/D94866
Added:
Modified:
llvm/lib/Analysis/ValueTracking.cpp
llvm/test/Transforms/InstCombine/select-and-or.ll
llvm/unittests/Analysis/ValueTrackingTest.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index a9cef91e7316..7f8f101d42af 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -4806,64 +4806,49 @@ bool llvm::canCreatePoison(const Operator *Op) {
return ::canCreateUndefOrPoison(Op, /*PoisonOnly=*/true);
}
-bool llvm::impliesPoison(const Value *ValAssumedPoison, const Value *V) {
- // Construct a set of values which are known to be poison from the knowledge
- // that ValAssumedPoison is poison.
- SmallPtrSet<const Value *, 4> PoisonValues;
- PoisonValues.insert(ValAssumedPoison);
- const Instruction *PoisonI = dyn_cast<Instruction>(ValAssumedPoison);
- unsigned Depth = 0;
- const unsigned MaxDepth = 2;
-
- while (PoisonI && Depth < MaxDepth) {
- // We'd like to know whether an operand of PoisonI is also poison.
- if (canCreatePoison(cast<Operator>(PoisonI)))
- // PoisonI can be a poison-generating instruction, so don't look further
- break;
-
- const Value *NextVal = nullptr;
- bool MoreThanOneCandidate = false;
- // See which operand can be poison
- for (const auto &Op : PoisonI->operands()) {
- if (!isGuaranteedNotToBeUndefOrPoison(Op.get())) {
- // Op can be poison.
- if (NextVal) {
- // There is more than one operand that can make PoisonI poison.
- MoreThanOneCandidate = true;
- break;
- }
- NextVal = Op.get();
- }
- }
+static bool directlyImpliesPoison(const Value *ValAssumedPoison,
+ const Value *V, unsigned Depth) {
+ if (ValAssumedPoison == V)
+ return true;
- if (NextVal == nullptr) {
- // All operands are non-poison, so PoisonI cannot be poison.
- // Since assumption is false, return true
- return true;
- } else if (MoreThanOneCandidate)
- break;
+ const unsigned MaxDepth = 2;
+ if (Depth >= MaxDepth)
+ return false;
- Depth++;
- PoisonValues.insert(NextVal);
- PoisonI = dyn_cast<Instruction>(NextVal);
+ const auto *I = dyn_cast<Instruction>(V);
+ if (I && propagatesPoison(cast<Operator>(I))) {
+ return any_of(I->operands(), [=](const Value *Op) {
+ return directlyImpliesPoison(ValAssumedPoison, Op, Depth + 1);
+ });
}
+ return false;
+}
- if (PoisonValues.contains(V))
+static bool impliesPoison(const Value *ValAssumedPoison, const Value *V,
+ unsigned Depth) {
+ if (isGuaranteedNotToBeUndefOrPoison(ValAssumedPoison))
return true;
- // Let's look one level further, by seeing its arguments if I was an
- // instruction.
- // This happens when I is e.g. 'icmp X, const' where X is in PoisonValues.
- const auto *I = dyn_cast<Instruction>(V);
- if (I && propagatesPoison(cast<Operator>(I))) {
- for (const auto &Op : I->operands())
- if (PoisonValues.count(Op.get()))
- return true;
- }
+ if (directlyImpliesPoison(ValAssumedPoison, V, /* Depth */ 0))
+ return true;
+ const unsigned MaxDepth = 2;
+ if (Depth >= MaxDepth)
+ return false;
+
+ const auto *I = dyn_cast<Instruction>(ValAssumedPoison);
+ if (I && !canCreatePoison(cast<Operator>(I))) {
+ return all_of(I->operands(), [=](const Value *Op) {
+ return impliesPoison(Op, V, Depth + 1);
+ });
+ }
return false;
}
+bool llvm::impliesPoison(const Value *ValAssumedPoison, const Value *V) {
+ return ::impliesPoison(ValAssumedPoison, V, /* Depth */ 0);
+}
+
static bool programUndefinedIfUndefOrPoison(const Value *V,
bool PoisonOnly);
diff --git a/llvm/test/Transforms/InstCombine/select-and-or.ll b/llvm/test/Transforms/InstCombine/select-and-or.ll
index 8681a7349ff9..f3de67649cb1 100644
--- a/llvm/test/Transforms/InstCombine/select-and-or.ll
+++ b/llvm/test/Transforms/InstCombine/select-and-or.ll
@@ -148,10 +148,9 @@ define i1 @logical_or_noundef_a(i1 noundef %a, i1 %b) {
}
; Noundef on false value allows conversion to or.
-; TODO: impliesPoison doesn't handle this yet.
define i1 @logical_or_noundef_b(i1 %a, i1 noundef %b) {
; CHECK-LABEL: @logical_or_noundef_b(
-; CHECK-NEXT: [[RES:%.*]] = select i1 [[A:%.*]], i1 true, i1 [[B:%.*]]
+; CHECK-NEXT: [[RES:%.*]] = or i1 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret i1 [[RES]]
;
%res = select i1 %a, i1 true, i1 %b
@@ -169,10 +168,9 @@ define i1 @logical_and_noundef_a(i1 noundef %a, i1 %b) {
}
; Noundef on false value allows conversion to and.
-; TODO: impliesPoison doesn't handle this yet.
define i1 @logical_and_noundef_b(i1 %a, i1 noundef %b) {
; CHECK-LABEL: @logical_and_noundef_b(
-; CHECK-NEXT: [[RES:%.*]] = select i1 [[A:%.*]], i1 [[B:%.*]], i1 false
+; CHECK-NEXT: [[RES:%.*]] = and i1 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret i1 [[RES]]
;
%res = select i1 %a, i1 %b, i1 false
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 4b3b33b42625..f3240e0b4ff4 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -748,6 +748,46 @@ TEST_F(ValueTrackingTest, impliesPoisonTest_AddNsw) {
EXPECT_FALSE(impliesPoison(A2, A));
}
+TEST_F(ValueTrackingTest, impliesPoisonTest_Cmp) {
+ parseAssembly("define void @test(i32 %x, i32 %y, i1 %c) {\n"
+ " %A2 = icmp eq i32 %x, %y\n"
+ " %A0 = icmp ult i32 %x, %y\n"
+ " %A = or i1 %A0, %c\n"
+ " ret void\n"
+ "}");
+ EXPECT_TRUE(impliesPoison(A2, A));
+}
+
+TEST_F(ValueTrackingTest, impliesPoisonTest_FCmpFMF) {
+ parseAssembly("define void @test(float %x, float %y, i1 %c) {\n"
+ " %A2 = fcmp nnan oeq float %x, %y\n"
+ " %A0 = fcmp olt float %x, %y\n"
+ " %A = or i1 %A0, %c\n"
+ " ret void\n"
+ "}");
+ EXPECT_FALSE(impliesPoison(A2, A));
+}
+
+TEST_F(ValueTrackingTest, impliesPoisonTest_AddSubSameOps) {
+ parseAssembly("define void @test(i32 %x, i32 %y, i1 %c) {\n"
+ " %A2 = add i32 %x, %y\n"
+ " %A = sub i32 %x, %y\n"
+ " ret void\n"
+ "}");
+ EXPECT_TRUE(impliesPoison(A2, A));
+}
+
+TEST_F(ValueTrackingTest, impliesPoisonTest_MaskCmp) {
+ parseAssembly("define void @test(i32 %x, i32 %y, i1 %c) {\n"
+ " %M2 = and i32 %x, 7\n"
+ " %A2 = icmp eq i32 %M2, 1\n"
+ " %M = and i32 %x, 15\n"
+ " %A = icmp eq i32 %M, 3\n"
+ " ret void\n"
+ "}");
+ EXPECT_TRUE(impliesPoison(A2, A));
+}
+
TEST_F(ValueTrackingTest, ComputeNumSignBits_Shuffle_Pointers) {
parseAssembly(
"define <2 x i32*> @test(<2 x i32*> %x) {\n"
More information about the llvm-branch-commits
mailing list