diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h --- a/llvm/include/llvm/Analysis/ValueTracking.h +++ b/llvm/include/llvm/Analysis/ValueTracking.h @@ -601,6 +601,9 @@ /// canCreatePoison returns false. bool canCreatePoison(const Instruction *I); + /// Return true if V is poison given that ValAssumedPoison is already poison. + bool impliesPoison(const Value *ValAssumedPoison, const Value *V); + /// Return true if this function can prove that V is never undef value /// or poison value. // diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -4677,6 +4677,65 @@ } } +bool llvm::impliesPoison(const Value *ValAssumedPoison, const Value *V) { + if (isGuaranteedNotToBeUndefOrPoison(ValAssumedPoison)) + // The assumption is false, so we can say anything + return true; + + // Construct a set of values which are known to be poison from + // ValAssumedPoison. + SmallSet PoisonValues; + PoisonValues.insert(ValAssumedPoison); + const Instruction *PoisonI = dyn_cast(ValAssumedPoison); + unsigned Depth = 0; + + while (PoisonI && Depth < MaxDepth) { + // We'd like to know whether an operand of PoisonI is also poison. + if (canCreatePoison(PoisonI)) + // PoisonI can be a poison-generating instruction, so don't look further + break; + + const Value *NextVal = nullptr; + bool MoreThanOneCandidate = false; + for (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(); + } + } + + if (NextVal == nullptr) { + // All operands are non-poison, so PoisonI cannot be poison! + return true; + } else if (MoreThanOneCandidate) + break; + + Depth++; + PoisonValues.insert(NextVal); + PoisonI = dyn_cast(NextVal); + } + + if (PoisonValues.count(V) != 0) + // V should be poison. + return true; + + // Let's look one level further, by seeing its arguments if I was an + // instruction. + auto *I = dyn_cast(V); + if (I && propagatesFullPoison(I)) { + for (auto &Op : I->operands()) + if (PoisonValues.count(Op.get())) + return true; + } + + return false; +} + bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V, const Instruction *CtxI, const DominatorTree *DT) { diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp --- a/llvm/unittests/Analysis/ValueTrackingTest.cpp +++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp @@ -44,11 +44,13 @@ if (!F) return; - A = nullptr; + A = B = nullptr; for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { if (I->hasName()) { if (I->getName() == "A") A = &*I; + else if (I->getName() == "B") + B = &*I; } } ASSERT_TRUE(A) << "@test must have an instruction %A"; @@ -57,6 +59,7 @@ LLVMContext Context; std::unique_ptr M; Instruction *A = nullptr; + Instruction *B = nullptr; }; class MatchSelectPatternTest : public ValueTrackingTest { @@ -667,6 +670,59 @@ EXPECT_EQ(ComputeNumSignBits(A, M->getDataLayout()), 1u); } +TEST_F(ValueTrackingTest, impliesPoisonTest_Identity) { + parseAssembly("define void @test(i32 %x, i32 %y) {\n" + " %A = add i32 %x, %y\n" + " ret void\n" + "}"); + EXPECT_EQ(impliesPoison(A, A), 1u); +} + +TEST_F(ValueTrackingTest, impliesPoisonTest_ICmp) { + parseAssembly("define void @test(i32 %x) {\n" + " %B = icmp eq i32 %x, 0\n" + " %A = icmp eq i32 %x, 1\n" + " ret void\n" + "}"); + EXPECT_EQ(impliesPoison(B, A), 1u); +} + +TEST_F(ValueTrackingTest, impliesPoisonTest_ICmpUnknown) { + parseAssembly("define void @test(i32 %x, i32 %y) {\n" + " %B = icmp eq i32 %x, %y\n" + " %A = icmp eq i32 %x, 1\n" + " ret void\n" + "}"); + EXPECT_EQ(impliesPoison(B, A), 0u); +} + +TEST_F(ValueTrackingTest, impliesPoisonTest_AddNswOkay) { + parseAssembly("define void @test(i32 %x) {\n" + " %B = add nsw i32 %x, 1\n" + " %A = add i32 %B, 1\n" + " ret void\n" + "}"); + EXPECT_EQ(impliesPoison(B, A), 1u); +} + +TEST_F(ValueTrackingTest, impliesPoisonTest_AddNswOkay2) { + parseAssembly("define void @test(i32 %x) {\n" + " %B = add i32 %x, 1\n" + " %A = add nsw i32 %B, 1\n" + " ret void\n" + "}"); + EXPECT_EQ(impliesPoison(B, A), 1u); +} + +TEST_F(ValueTrackingTest, impliesPoisonTest_AddNsw) { + parseAssembly("define void @test(i32 %x) {\n" + " %B = add nsw i32 %x, 1\n" + " %A = add i32 %x, 1\n" + " ret void\n" + "}"); + EXPECT_EQ(impliesPoison(B, A), 0u); +} + TEST(ValueTracking, canCreatePoison) { std::string AsmHead = "declare i32 @g(i32)\n"