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 @@ -630,10 +630,11 @@ /// and returns true if it is guaranteed to be never undef or poison /// immediately before the CtxI. bool isGuaranteedNotToBeUndefOrPoison(const Value *V, + AssumptionCache *AC = nullptr, const Instruction *CtxI = nullptr, const DominatorTree *DT = nullptr, unsigned Depth = 0); - bool isGuaranteedNotToBePoison(const Value *V, + bool isGuaranteedNotToBePoison(const Value *V, AssumptionCache *AC = nullptr, const Instruction *CtxI = nullptr, const DominatorTree *DT = nullptr, unsigned Depth = 0); diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -4093,11 +4093,11 @@ // long as the other value isn't poison. // select ?, undef, X -> X if (Q.isUndefValue(TrueVal) && - isGuaranteedNotToBeUndefOrPoison(FalseVal, Q.CxtI, Q.DT)) + isGuaranteedNotToBeUndefOrPoison(FalseVal, Q.AC, Q.CxtI, Q.DT)) return FalseVal; // select ?, X, undef -> X if (Q.isUndefValue(FalseVal) && - isGuaranteedNotToBeUndefOrPoison(TrueVal, Q.CxtI, Q.DT)) + isGuaranteedNotToBeUndefOrPoison(TrueVal, Q.AC, Q.CxtI, Q.DT)) return TrueVal; // Deal with partial undef vector constants: select ?, VecC, VecC' --> VecC'' @@ -5627,7 +5627,7 @@ /// Given operands for a Freeze, see if we can fold the result. static Value *SimplifyFreezeInst(Value *Op0, const SimplifyQuery &Q) { // Use a utility function defined in ValueTracking. - if (llvm::isGuaranteedNotToBeUndefOrPoison(Op0, Q.CxtI, Q.DT)) + if (llvm::isGuaranteedNotToBeUndefOrPoison(Op0, Q.AC, Q.CxtI, Q.DT)) return Op0; // We have room for improvement. return nullptr; 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 @@ -1801,7 +1801,8 @@ } break; case Instruction::Freeze: - if (isGuaranteedNotToBePoison(I->getOperand(0), Q.CxtI, Q.DT, Depth + 1)) + if (isGuaranteedNotToBePoison(I->getOperand(0), Q.AC, Q.CxtI, Q.DT, + Depth + 1)) computeKnownBits(I->getOperand(0), Known, Depth + 1, Q); break; } @@ -2516,7 +2517,7 @@ else if (const FreezeInst *FI = dyn_cast(V)) { auto *Op = FI->getOperand(0); if (isKnownNonZero(Op, Depth, Q) && - isGuaranteedNotToBePoison(Op, Q.CxtI, Q.DT, Depth)) + isGuaranteedNotToBePoison(Op, Q.AC, Q.CxtI, Q.DT, Depth)) return true; } @@ -4816,6 +4817,7 @@ bool PoisonOnly); static bool isGuaranteedNotToBeUndefOrPoison(const Value *V, + AssumptionCache *AC, const Instruction *CtxI, const DominatorTree *DT, unsigned Depth, bool PoisonOnly) { @@ -4857,7 +4859,8 @@ return true; auto OpCheck = [&](const Value *V) { - return isGuaranteedNotToBeUndefOrPoison(V, CtxI, DT, Depth + 1, PoisonOnly); + return isGuaranteedNotToBeUndefOrPoison(V, AC, CtxI, DT, Depth + 1, + PoisonOnly); }; if (auto *Opr = dyn_cast(V)) { @@ -4876,8 +4879,8 @@ bool IsWellDefined = true; for (unsigned i = 0; i < Num; ++i) { auto *TI = PN->getIncomingBlock(i)->getTerminator(); - if (!isGuaranteedNotToBeUndefOrPoison(PN->getIncomingValue(i), TI, DT, - Depth + 1, PoisonOnly)) { + if (!isGuaranteedNotToBeUndefOrPoison(PN->getIncomingValue(i), AC, TI, + DT, Depth + 1, PoisonOnly)) { IsWellDefined = false; break; } @@ -4932,19 +4935,24 @@ Dominator = Dominator->getIDom(); } + SmallVector AttrKinds{Attribute::NoUndef}; + if (getKnowledgeValidInContext(V, AttrKinds, CtxI, DT, AC)) + return true; + return false; } -bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V, +bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V, AssumptionCache *AC, const Instruction *CtxI, const DominatorTree *DT, unsigned Depth) { - return ::isGuaranteedNotToBeUndefOrPoison(V, CtxI, DT, Depth, false); + return ::isGuaranteedNotToBeUndefOrPoison(V, AC, CtxI, DT, Depth, false); } -bool llvm::isGuaranteedNotToBePoison(const Value *V, const Instruction *CtxI, +bool llvm::isGuaranteedNotToBePoison(const Value *V, AssumptionCache *AC, + const Instruction *CtxI, const DominatorTree *DT, unsigned Depth) { - return ::isGuaranteedNotToBeUndefOrPoison(V, CtxI, DT, Depth, true); + return ::isGuaranteedNotToBeUndefOrPoison(V, AC, CtxI, DT, Depth, true); } OverflowResult llvm::computeOverflowForSignedAdd(const AddOperator *Add, diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -7897,10 +7897,13 @@ AANoUndef::StateType &State) { const Value *UseV = U->get(); const DominatorTree *DT = nullptr; - if (Function *F = getAnchorScope()) - DT = A.getInfoCache().getAnalysisResultForFunction( - *F); - State.setKnown(isGuaranteedNotToBeUndefOrPoison(UseV, I, DT)); + AssumptionCache *AC = nullptr; + InformationCache &InfoCache = A.getInfoCache(); + if (Function *F = getAnchorScope()) { + DT = InfoCache.getAnalysisResultForFunction(*F); + AC = InfoCache.getAnalysisResultForFunction(*F); + } + State.setKnown(isGuaranteedNotToBeUndefOrPoison(UseV, AC, I, DT)); bool TrackUse = false; // Track use for instructions which must produce undef or poison bits when // at least one operand contains such bits. diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -1184,11 +1184,13 @@ // X == Y ? X : Z with X == Y ? Y : Z, as that would lead to an infinite // replacement cycle. Value *CmpLHS = Cmp.getOperand(0), *CmpRHS = Cmp.getOperand(1); - if (TrueVal != CmpLHS && isGuaranteedNotToBeUndefOrPoison(CmpRHS, &Sel, &DT)) + if (TrueVal != CmpLHS && + isGuaranteedNotToBeUndefOrPoison(CmpRHS, SQ.AC, &Sel, &DT)) if (Value *V = SimplifyWithOpReplaced(TrueVal, CmpLHS, CmpRHS, SQ, /* AllowRefinement */ true)) return replaceOperand(Sel, Swapped ? 2 : 1, V); - if (TrueVal != CmpRHS && isGuaranteedNotToBeUndefOrPoison(CmpLHS, &Sel, &DT)) + if (TrueVal != CmpRHS && + isGuaranteedNotToBeUndefOrPoison(CmpLHS, SQ.AC, &Sel, &DT)) if (Value *V = SimplifyWithOpReplaced(TrueVal, CmpRHS, CmpLHS, SQ, /* AllowRefinement */ true)) return replaceOperand(Sel, Swapped ? 2 : 1, V); diff --git a/llvm/lib/Transforms/Scalar/DivRemPairs.cpp b/llvm/lib/Transforms/Scalar/DivRemPairs.cpp --- a/llvm/lib/Transforms/Scalar/DivRemPairs.cpp +++ b/llvm/lib/Transforms/Scalar/DivRemPairs.cpp @@ -315,14 +315,14 @@ // %rem = sub %x, %mul // %rem = undef - undef = undef // If X is not frozen, %rem becomes undef after transformation. // TODO: We need a undef-specific checking function in ValueTracking - if (!isGuaranteedNotToBeUndefOrPoison(X, DivInst, &DT)) { + if (!isGuaranteedNotToBeUndefOrPoison(X, nullptr, DivInst, &DT)) { auto *FrX = new FreezeInst(X, X->getName() + ".frozen", DivInst); DivInst->setOperand(0, FrX); Sub->setOperand(0, FrX); } // Same for Y. If X = 1 and Y = (undef | 1), %rem in src is either 1 or 0, // but %rem in tgt can be one of many integer values. - if (!isGuaranteedNotToBeUndefOrPoison(Y, DivInst, &DT)) { + if (!isGuaranteedNotToBeUndefOrPoison(Y, nullptr, DivInst, &DT)) { auto *FrY = new FreezeInst(Y, Y->getName() + ".frozen", DivInst); DivInst->setOperand(1, FrY); Mul->setOperand(1, FrY); diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp --- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp +++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp @@ -2861,7 +2861,8 @@ // Expand the select. Value *Cond = SI->getCondition(); if (InsertFreezeWhenUnfoldingSelect && - !isGuaranteedNotToBeUndefOrPoison(Cond, SI, &DTU->getDomTree())) + !isGuaranteedNotToBeUndefOrPoison(Cond, nullptr, SI, + &DTU->getDomTree())) Cond = new FreezeInst(Cond, "cond.fr", SI); Instruction *Term = SplitBlockAndInsertIfThen(Cond, SI, false); BasicBlock *SplitBB = SI->getParent(); diff --git a/llvm/lib/Transforms/Scalar/SCCP.cpp b/llvm/lib/Transforms/Scalar/SCCP.cpp --- a/llvm/lib/Transforms/Scalar/SCCP.cpp +++ b/llvm/lib/Transforms/Scalar/SCCP.cpp @@ -2105,7 +2105,7 @@ // poison nor undef. Poison will be outside any range and currently // values outside of the specified range cause immediate undefined // behavior. - if (!isGuaranteedNotToBeUndefOrPoison(CB, CB)) + if (!isGuaranteedNotToBeUndefOrPoison(CB, nullptr, CB)) continue; // Do not touch existing metadata for now. diff --git a/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp b/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp --- a/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp +++ b/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp @@ -52,6 +52,7 @@ bool isUsefullToPreserve(Attribute::AttrKind Kind) { switch (Kind) { case Attribute::NonNull: + case Attribute::NoUndef: case Attribute::Alignment: case Attribute::Dereferenceable: case Attribute::DereferenceableOrNull: diff --git a/llvm/lib/Transforms/Utils/CanonicalizeFreezeInLoops.cpp b/llvm/lib/Transforms/Utils/CanonicalizeFreezeInLoops.cpp --- a/llvm/lib/Transforms/Utils/CanonicalizeFreezeInLoops.cpp +++ b/llvm/lib/Transforms/Utils/CanonicalizeFreezeInLoops.cpp @@ -109,7 +109,7 @@ auto *ValueToFr = U.get(); assert(L->contains(UserI->getParent()) && "Should not process an instruction that isn't inside the loop"); - if (isGuaranteedNotToBeUndefOrPoison(ValueToFr, UserI, &DT)) + if (isGuaranteedNotToBeUndefOrPoison(ValueToFr, nullptr, UserI, &DT)) return; LLVM_DEBUG(dbgs() << "canonfr: inserting freeze:\n"); @@ -176,7 +176,7 @@ assert(StepI && "Step instruction should have been found"); // Drop flags from the step instruction. - if (!isGuaranteedNotToBeUndefOrPoison(StepI, StepI, &DT)) { + if (!isGuaranteedNotToBeUndefOrPoison(StepI, nullptr, StepI, &DT)) { LLVM_DEBUG(dbgs() << "canonfr: drop flags: " << *StepI << "\n"); StepI->dropPoisonGeneratingFlags(); SE.forgetValue(StepI); diff --git a/llvm/test/Transforms/Inline/arg-attr-propagation.ll b/llvm/test/Transforms/Inline/arg-attr-propagation.ll --- a/llvm/test/Transforms/Inline/arg-attr-propagation.ll +++ b/llvm/test/Transforms/Inline/arg-attr-propagation.ll @@ -7,31 +7,41 @@ define i32 @callee(i32* dereferenceable(32) %t1) { ; CHECK-LABEL: define {{[^@]+}}@callee -; CHECK-SAME: (i32* dereferenceable(32) [[T1:%.*]]) -; CHECK-NEXT: [[T2:%.*]] = load i32, i32* [[T1]] +; CHECK-SAME: (i32* dereferenceable(32) [[T1:%.*]]) { +; CHECK-NEXT: [[T2:%.*]] = load i32, i32* [[T1]], align 4 ; CHECK-NEXT: ret i32 [[T2]] ; %t2 = load i32, i32* %t1 ret i32 %t2 } +define i32 @callee2(i32* dereferenceable(32) %t1, i32 noundef %t2) { +; CHECK-LABEL: define {{[^@]+}}@callee2 +; CHECK-SAME: (i32* dereferenceable(32) [[T1:%.*]], i32 noundef [[T2:%.*]]) { +; CHECK-NEXT: [[V:%.*]] = load i32, i32* [[T1]], align 4 +; CHECK-NEXT: ret i32 [[V]] +; + %v = load i32, i32* %t1 + ret i32 %v +} + ; FIXME: All dereferenceability information is lost. ; The caller argument could be known nonnull and dereferenceable(32). -define i32 @caller1(i32* %t1) { +define i32 @caller1(i32* %t1, i32 %t2) { ; NO_ASSUME-LABEL: define {{[^@]+}}@caller1 -; NO_ASSUME-SAME: (i32* [[T1:%.*]]) -; NO_ASSUME-NEXT: [[T2_I:%.*]] = load i32, i32* [[T1]] -; NO_ASSUME-NEXT: ret i32 [[T2_I]] +; NO_ASSUME-SAME: (i32* [[T1:%.*]], i32 [[T2:%.*]]) { +; NO_ASSUME-NEXT: [[V_I:%.*]] = load i32, i32* [[T1]], align 4 +; NO_ASSUME-NEXT: ret i32 [[V_I]] ; ; USE_ASSUME-LABEL: define {{[^@]+}}@caller1 -; USE_ASSUME-SAME: (i32* [[T1:%.*]]) -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[T1]], i64 32) ] -; USE_ASSUME-NEXT: [[T2_I:%.*]] = load i32, i32* [[T1]] -; USE_ASSUME-NEXT: ret i32 [[T2_I]] +; USE_ASSUME-SAME: (i32* [[T1:%.*]], i32 [[T2:%.*]]) { +; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[T1]], i64 32), "noundef"(i32 [[T2]]) ] +; USE_ASSUME-NEXT: [[V_I:%.*]] = load i32, i32* [[T1]], align 4 +; USE_ASSUME-NEXT: ret i32 [[V_I]] ; - %t2 = tail call i32 @callee(i32* dereferenceable(32) %t1) - ret i32 %t2 + %v = tail call i32 @callee2(i32* dereferenceable(32) %t1, i32 noundef %t2) + ret i32 %v } ; The caller argument is nonnull, but that can be explicit. 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 @@ -25,10 +25,18 @@ namespace { -static Instruction &findInstructionByName(Function *F, StringRef Name) { +static Instruction *findInstructionByNameOrNull(Function *F, StringRef Name) { for (Instruction &I : instructions(F)) if (I.getName() == Name) - return I; + return &I; + + return nullptr; +} + +static Instruction &findInstructionByName(Function *F, StringRef Name) { + auto *I = findInstructionByNameOrNull(F, Name); + if (I) + return *I; llvm_unreachable("Expected value not found"); } @@ -56,14 +64,21 @@ if (!F) return; - A = &findInstructionByName(F, "A"); + A = findInstructionByNameOrNull(F, "A"); ASSERT_TRUE(A) << "@test must have an instruction %A"; + + CxtI = findInstructionByNameOrNull(F, "CxtI"); + CxtI2 = findInstructionByNameOrNull(F, "CxtI2"); + CxtI3 = findInstructionByNameOrNull(F, "CxtI3"); } LLVMContext Context; std::unique_ptr M; Function *F = nullptr; Instruction *A = nullptr; + + // Context instructions (optional) + Instruction *CxtI = nullptr, *CxtI2 = nullptr, *CxtI3 = nullptr; }; class MatchSelectPatternTest : public ValueTrackingTest { @@ -763,7 +778,8 @@ if (&BB == &F->getEntryBlock()) continue; - EXPECT_EQ(isGuaranteedNotToBePoison(A, BB.getTerminator(), &DT), true) + EXPECT_EQ(isGuaranteedNotToBePoison(A, nullptr, BB.getTerminator(), &DT), + true) << "isGuaranteedNotToBePoison does not hold at " << *BB.getTerminator(); } } @@ -786,7 +802,7 @@ DominatorTree DT(*F); for (auto &BB : *F) { if (BB.getName() == "LOOP") { - EXPECT_EQ(isGuaranteedNotToBePoison(A, A, &DT), true) + EXPECT_EQ(isGuaranteedNotToBePoison(A, nullptr, A, &DT), true) << "isGuaranteedNotToBePoison does not hold"; } } @@ -802,6 +818,33 @@ EXPECT_EQ(isGuaranteedNotToBeUndefOrPoison(A), true); } +TEST_F(ValueTrackingTest, isGuaranteedNotToBeUndefOrPoison_assume) { + parseAssembly("declare i1 @f_i1()\n" + "declare i32 @f_i32()\n" + "declare void @llvm.assume(i1)\n" + "define void @test() {\n" + " %A = call i32 @f_i32()\n" + " %cond = call i1 @f_i1()\n" + " %CxtI = add i32 0, 0\n" + " br i1 %cond, label %BB1, label %EXIT\n" + "BB1:\n" + " %CxtI2 = add i32 0, 0\n" + " %cond2 = call i1 @f_i1()\n" + " call void @llvm.assume(i1 true) [ \"noundef\"(i32 %A) ]\n" + " br i1 %cond2, label %BB2, label %EXIT\n" + "BB2:\n" + " %CxtI3 = add i32 0, 0\n" + " ret void\n" + "EXIT:\n" + " ret void\n" + "}"); + AssumptionCache AC(*F); + DominatorTree DT(*F); + EXPECT_FALSE(isGuaranteedNotToBeUndefOrPoison(A, &AC, CxtI, &DT)); + EXPECT_FALSE(isGuaranteedNotToBeUndefOrPoison(A, &AC, CxtI2, &DT)); + EXPECT_TRUE(isGuaranteedNotToBeUndefOrPoison(A, &AC, CxtI3, &DT)); +} + TEST(ValueTracking, canCreatePoisonOrUndef) { std::string AsmHead = "declare i32 @g(i32)\n"