diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index a9cef91e73169d..7f8f101d42af1a 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 PoisonValues; - PoisonValues.insert(ValAssumedPoison); - const Instruction *PoisonI = dyn_cast(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(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(NextVal); + const auto *I = dyn_cast(V); + if (I && propagatesPoison(cast(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(V); - if (I && propagatesPoison(cast(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(ValAssumedPoison); + if (I && !canCreatePoison(cast(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 8681a7349ff9a3..f3de67649cb1b1 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 4b3b33b426251e..f3240e0b4ff449 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"