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 @@ -592,6 +592,19 @@ /// the parent of I. bool programUndefinedIfFullPoison(const Instruction *PoisonI); + /// Return true if pushing freeze into operands of I is okay. + /// For example, If I = `add x, y`, rewriting `freeze(add x, y)` into + /// `add (freeze x), (freeze y)` is valid, but it isn't okay if the add had + /// nsw flag because it can make the result poison. + bool canPushFreezeIntoOperands(const Instruction *I); + + /// Return true if V is poison given that ValAssumedPoison is already poison. + /// + /// If CtxI and DT are specified this method performs flow-sensitive analysis + bool isPoisonIf(const Value *V, const Value *ValAssumedPoison, + const Instruction *CtxI = nullptr, + const DominatorTree *DT = nullptr); + /// 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 @@ -4592,6 +4592,112 @@ return llvm::any_of(GuardingBranches, AllUsesGuardedByBranch); } +bool llvm::canPushFreezeIntoOperands(const Instruction *I) { + switch (I->getOpcode()) { + case Instruction::Add: + case Instruction::Sub: + case Instruction::Mul: + return !I->hasNoUnsignedWrap() && !I->hasNoSignedWrap(); + case Instruction::UDiv: + case Instruction::SDiv: + case Instruction::URem: + case Instruction::SRem: + return !I->isExact(); + case Instruction::And: + case Instruction::Or: + case Instruction::Xor: + case Instruction::ICmp: + return true; + case Instruction::FNeg: + case Instruction::FAdd: + case Instruction::FSub: + case Instruction::FMul: + case Instruction::FDiv: + case Instruction::FRem: + case Instruction::FCmp: { + auto FMF = I->getFastMathFlags(); + return !FMF.noNaNs() && !FMF.noInfs(); + } + case Instruction::Shl: + case Instruction::AShr: + case Instruction::LShr: + // For shift operations, it is poison if shiftwidth is larger than the + // bitwidth + return false; + case Instruction::PHI: + case Instruction::Select: + case Instruction::BitCast: + case Instruction::Freeze: + return true; + case Instruction::GetElementPtr: + return !dyn_cast(I)->isInBounds(); + default: + // Be conservative here. + return false; + } +} + +bool llvm::isPoisonIf(const Value *V, const Value *ValAssumedPoison, + const Instruction *CtxI, const DominatorTree *DT) { + if (isGuaranteedNotToBeUndefOrPoison(ValAssumedPoison, CtxI, DT)) + // 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 (!canPushFreezeIntoOperands(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(), CtxI, DT)) { + // 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,109 @@ EXPECT_EQ(ComputeNumSignBits(A, M->getDataLayout()), 1u); } +TEST_F(ValueTrackingTest, IsPoisonIfTest_Identity) { + parseAssembly( + "define void @test(i32 %x, i32 %y) {\n" + " %A = add i32 %x, %y\n" + " ret void\n" + "}"); + EXPECT_EQ(isPoisonIf(A, A), 1u); +} + +TEST_F(ValueTrackingTest, IsPoisonIfTest_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(isPoisonIf(A, B), 1u); +} + +TEST_F(ValueTrackingTest, IsPoisonIfTest_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(isPoisonIf(A, B), 0u); +} + +TEST_F(ValueTrackingTest, IsPoisonIfTest_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(isPoisonIf(A, B), 1u); +} + +TEST_F(ValueTrackingTest, IsPoisonIfTest_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(isPoisonIf(A, B), 1u); +} + +TEST_F(ValueTrackingTest, IsPoisonIfTest_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(isPoisonIf(A, B), 0u); +} + +TEST(ValueTracking, CanPushFreezeIntoOperands) { + StringRef Assembly = + "define void @f(i32 %x, i32 %y, float %fx, float %fy, i1 %cond) { " + " add i32 %x, %y\n" + " add nsw nuw i32 %x, %y\n" + " shl i32 %x, %y\n" + " udiv i32 %x, %y\n" + " udiv exact i32 %x, %y\n" + " fadd float %fx, %fy\n" + " fadd nnan float %fx, %fy\n" + " select i1 %cond, i32 %x, i32 %y\n" + " ret void " + "} "; + + LLVMContext Context; + SMDiagnostic Error; + auto M = parseAssemblyString(Assembly, Error, Context); + assert(M && "Bad assembly?"); + + auto *F = M->getFunction("f"); + assert(F && "Bad assembly?"); + + auto &BB = F->getEntryBlock(); + bool ExpectedAnswers[] = { + true, // add + false, // add nsw nuw + false, // shl + true, // udiv + false, // udiv exact + true, // fadd + false, // fadd nnan + true, // select + }; + + int Index = 0; + for (auto &I : BB) { + if (isa(&I)) + break; + EXPECT_EQ(canPushFreezeIntoOperands(&I), ExpectedAnswers[Index]) + << "Incorrect answer at instruction " << Index << " = " << I; + Index++; + } +} + TEST_F(ComputeKnownBitsTest, ComputeKnownBits) { parseAssembly( "define i32 @test(i32 %a, i32 %b) {\n"