summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuneyoung Lee <aqjune@gmail.com>2020-09-08 11:41:19 +0900
committerJuneyoung Lee <aqjune@gmail.com>2020-09-09 20:00:26 +0900
commit25ce1e0497259711836f949005297125e92a6e93 (patch)
treec290681fcf97397eadcb0ff5613149ce40ac0691
parent455cce3e216ba3cac0844b4ee9cf85791c1ac046 (diff)
downloadllvm-25ce1e0497259711836f949005297125e92a6e93.tar.gz
[ValueTracking] Add UndefOrPoison/Poison-only version of relevant functions
This patch adds isGuaranteedNotToBePoison and programUndefinedIfUndefOrPoison. isGuaranteedNotToBePoison will be used at D75808. The latter function is used at isGuaranteedNotToBePoison. Reviewed By: nikic Differential Revision: https://reviews.llvm.org/D84242
-rw-r--r--llvm/include/llvm/Analysis/ValueTracking.h24
-rw-r--r--llvm/lib/Analysis/ScalarEvolution.cpp2
-rw-r--r--llvm/lib/Analysis/ValueTracking.cpp107
-rw-r--r--llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp2
-rw-r--r--llvm/lib/Transforms/Scalar/IndVarSimplify.cpp2
-rw-r--r--llvm/unittests/Analysis/ValueTrackingTest.cpp48
6 files changed, 146 insertions, 39 deletions
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index f9a27a8ec4b0..8ddbcbf4d643 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -584,25 +584,27 @@ constexpr unsigned MaxAnalysisRecursionDepth = 6;
/// if, for all i, r is evaluated to poison or op raises UB if vi = poison.
/// To filter out operands that raise UB on poison, you can use
/// getGuaranteedNonPoisonOp.
- bool propagatesPoison(const Instruction *I);
+ bool propagatesPoison(const Operator *I);
/// Insert operands of I into Ops such that I will trigger undefined behavior
/// if I is executed and that operand has a poison value.
void getGuaranteedNonPoisonOps(const Instruction *I,
SmallPtrSetImpl<const Value *> &Ops);
- /// Return true if the given instruction must trigger undefined behavior.
+ /// Return true if the given instruction must trigger undefined behavior
/// when I is executed with any operands which appear in KnownPoison holding
/// a poison value at the point of execution.
bool mustTriggerUB(const Instruction *I,
const SmallSet<const Value *, 16>& KnownPoison);
- /// Return true if this function can prove that if PoisonI is executed
- /// and yields a poison value, then that will trigger undefined behavior.
+ /// Return true if this function can prove that if Inst is executed
+ /// and yields a poison value or undef bits, then that will trigger
+ /// undefined behavior.
///
/// Note that this currently only considers the basic block that is
- /// the parent of I.
- bool programUndefinedIfPoison(const Instruction *PoisonI);
+ /// the parent of Inst.
+ bool programUndefinedIfUndefOrPoison(const Instruction *Inst);
+ bool programUndefinedIfPoison(const Instruction *Inst);
/// canCreateUndefOrPoison returns true if Op can create undef or poison from
/// non-undef & non-poison operands.
@@ -618,9 +620,9 @@ constexpr unsigned MaxAnalysisRecursionDepth = 6;
bool canCreateUndefOrPoison(const Operator *Op);
bool canCreatePoison(const Operator *Op);
- /// Return true if this function can prove that V is never undef value
- /// or poison value. If V is an aggregate value or vector, check whether all
- /// elements (except padding) are not undef or poison.
+ /// Return true if this function can prove that V does not have undef bits
+ /// and is never poison. If V is an aggregate value or vector, check whether
+ /// all elements (except padding) are not undef or poison.
/// Note that this is different from canCreateUndefOrPoison because the
/// function assumes Op's operands are not poison/undef.
///
@@ -631,6 +633,10 @@ constexpr unsigned MaxAnalysisRecursionDepth = 6;
const Instruction *CtxI = nullptr,
const DominatorTree *DT = nullptr,
unsigned Depth = 0);
+ bool isGuaranteedNotToBePoison(const Value *V,
+ const Instruction *CtxI = nullptr,
+ const DominatorTree *DT = nullptr,
+ unsigned Depth = 0);
/// Specific patterns of select instructions we can match.
enum SelectPatternFlavor {
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 11d92bc816e9..649e8d3733a9 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -5912,7 +5912,7 @@ bool ScalarEvolution::isAddRecNeverPoison(const Instruction *I, const Loop *L) {
const Instruction *Poison = PoisonStack.pop_back_val();
for (auto *PoisonUser : Poison->users()) {
- if (propagatesPoison(cast<Instruction>(PoisonUser))) {
+ if (propagatesPoison(cast<Operator>(PoisonUser))) {
if (Pushed.insert(cast<Instruction>(PoisonUser)).second)
PoisonStack.push_back(cast<Instruction>(PoisonUser));
} else if (auto *BI = dyn_cast<BranchInst>(PoisonUser)) {
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 5eb66e96e1d8..469257d91071 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -4860,10 +4860,13 @@ bool llvm::canCreatePoison(const Operator *Op) {
return ::canCreateUndefOrPoison(Op, /*PoisonOnly=*/true);
}
-bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V,
- const Instruction *CtxI,
- const DominatorTree *DT,
- unsigned Depth) {
+static bool programUndefinedIfUndefOrPoison(const Instruction *Inst,
+ bool PoisonOnly);
+
+static bool isGuaranteedNotToBeUndefOrPoison(const Value *V,
+ const Instruction *CtxI,
+ const DominatorTree *DT,
+ unsigned Depth, bool PoisonOnly) {
if (Depth >= MaxAnalysisRecursionDepth)
return false;
@@ -4874,14 +4877,15 @@ bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V,
if (auto *C = dyn_cast<Constant>(V)) {
if (isa<UndefValue>(C))
- return false;
+ return PoisonOnly;
if (isa<ConstantInt>(C) || isa<GlobalVariable>(C) || isa<ConstantFP>(V) ||
isa<ConstantPointerNull>(C) || isa<Function>(C))
return true;
if (C->getType()->isVectorTy() && !isa<ConstantExpr>(C))
- return !C->containsConstantExpression() && !C->containsUndefElement();
+ return (PoisonOnly || !C->containsUndefElement()) &&
+ !C->containsConstantExpression();
}
// Strip cast operations from a pointer value.
@@ -4898,7 +4902,7 @@ bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V,
return true;
auto OpCheck = [&](const Value *V) {
- return isGuaranteedNotToBeUndefOrPoison(V, CtxI, DT, Depth + 1);
+ return isGuaranteedNotToBeUndefOrPoison(V, CtxI, DT, Depth + 1, PoisonOnly);
};
if (auto *Opr = dyn_cast<Operator>(V)) {
@@ -4917,9 +4921,7 @@ bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V,
}
if (auto *I = dyn_cast<Instruction>(V)) {
- if (programUndefinedIfPoison(I) && I->getType()->isIntegerTy(1))
- // Note: once we have an agreement that poison is a value-wise concept,
- // we can remove the isIntegerTy(1) constraint.
+ if (programUndefinedIfUndefOrPoison(I, PoisonOnly))
return true;
}
@@ -4941,12 +4943,24 @@ bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V,
while (Dominator) {
auto *TI = Dominator->getBlock()->getTerminator();
+ Value *Cond = nullptr;
if (auto BI = dyn_cast<BranchInst>(TI)) {
- if (BI->isConditional() && BI->getCondition() == V)
- return true;
+ if (BI->isConditional())
+ Cond = BI->getCondition();
} else if (auto SI = dyn_cast<SwitchInst>(TI)) {
- if (SI->getCondition() == V)
+ Cond = SI->getCondition();
+ }
+
+ if (Cond) {
+ if (Cond == V)
return true;
+ else if (PoisonOnly && isa<Operator>(Cond)) {
+ // For poison, we can analyze further
+ auto *Opr = cast<Operator>(Cond);
+ if (propagatesPoison(Opr) &&
+ any_of(Opr->operand_values(), [&](Value *Op) { return Op == V; }))
+ return true;
+ }
}
Dominator = Dominator->getIDom();
@@ -4955,6 +4969,18 @@ bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V,
return false;
}
+bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V,
+ const Instruction *CtxI,
+ const DominatorTree *DT,
+ unsigned Depth) {
+ return ::isGuaranteedNotToBeUndefOrPoison(V, CtxI, DT, Depth, false);
+}
+
+bool llvm::isGuaranteedNotToBePoison(const Value *V, const Instruction *CtxI,
+ const DominatorTree *DT, unsigned Depth) {
+ return ::isGuaranteedNotToBeUndefOrPoison(V, CtxI, DT, Depth, true);
+}
+
OverflowResult llvm::computeOverflowForSignedAdd(const AddOperator *Add,
const DataLayout &DL,
AssumptionCache *AC,
@@ -5048,7 +5074,7 @@ bool llvm::isGuaranteedToExecuteForEveryIteration(const Instruction *I,
llvm_unreachable("Instruction not contained in its own parent basic block.");
}
-bool llvm::propagatesPoison(const Instruction *I) {
+bool llvm::propagatesPoison(const Operator *I) {
switch (I->getOpcode()) {
case Instruction::Freeze:
case Instruction::Select:
@@ -5124,30 +5150,51 @@ bool llvm::mustTriggerUB(const Instruction *I,
return false;
}
-
-bool llvm::programUndefinedIfPoison(const Instruction *PoisonI) {
- // We currently only look for uses of poison values within the same basic
+static bool programUndefinedIfUndefOrPoison(const Instruction *Inst,
+ bool PoisonOnly) {
+ // We currently only look for uses of values within the same basic
// block, as that makes it easier to guarantee that the uses will be
- // executed given that PoisonI is executed.
+ // executed given that Inst is executed.
//
// FIXME: Expand this to consider uses beyond the same basic block. To do
// this, look out for the distinction between post-dominance and strong
// post-dominance.
- const BasicBlock *BB = PoisonI->getParent();
+ const BasicBlock *BB = Inst->getParent();
+
+ BasicBlock::const_iterator Begin = Inst->getIterator(), End = BB->end();
+
+ if (!PoisonOnly) {
+ // Be conservative & just check whether a value is passed to a noundef
+ // argument.
+ // Instructions that raise UB with a poison operand are well-defined
+ // or have unclear semantics when the input is partially undef.
+ // For example, 'udiv x, (undef | 1)' isn't UB.
+
+ for (auto &I : make_range(Begin, End)) {
+ if (const auto *CB = dyn_cast<CallBase>(&I)) {
+ for (unsigned i = 0; i < CB->arg_size(); ++i) {
+ if (CB->paramHasAttr(i, Attribute::NoUndef) &&
+ CB->getArgOperand(i) == Inst)
+ return true;
+ }
+ }
+ if (!isGuaranteedToTransferExecutionToSuccessor(&I))
+ break;
+ }
+ return false;
+ }
- // Set of instructions that we have proved will yield poison if PoisonI
+ // Set of instructions that we have proved will yield poison if Inst
// does.
SmallSet<const Value *, 16> YieldsPoison;
SmallSet<const BasicBlock *, 4> Visited;
- YieldsPoison.insert(PoisonI);
- Visited.insert(PoisonI->getParent());
-
- BasicBlock::const_iterator Begin = PoisonI->getIterator(), End = BB->end();
+ YieldsPoison.insert(Inst);
+ Visited.insert(Inst->getParent());
unsigned Iter = 0;
while (Iter++ < MaxAnalysisRecursionDepth) {
for (auto &I : make_range(Begin, End)) {
- if (&I != PoisonI) {
+ if (&I != Inst) {
if (mustTriggerUB(&I, YieldsPoison))
return true;
if (!isGuaranteedToTransferExecutionToSuccessor(&I))
@@ -5158,7 +5205,7 @@ bool llvm::programUndefinedIfPoison(const Instruction *PoisonI) {
if (YieldsPoison.count(&I)) {
for (const User *User : I.users()) {
const Instruction *UserI = cast<Instruction>(User);
- if (propagatesPoison(UserI))
+ if (propagatesPoison(cast<Operator>(UserI)))
YieldsPoison.insert(User);
}
}
@@ -5178,6 +5225,14 @@ bool llvm::programUndefinedIfPoison(const Instruction *PoisonI) {
return false;
}
+bool llvm::programUndefinedIfUndefOrPoison(const Instruction *Inst) {
+ return ::programUndefinedIfUndefOrPoison(Inst, false);
+}
+
+bool llvm::programUndefinedIfPoison(const Instruction *Inst) {
+ return ::programUndefinedIfUndefOrPoison(Inst, true);
+}
+
static bool isKnownNonNaN(const Value *V, FastMathFlags FMF) {
if (FMF.noNaNs())
return true;
diff --git a/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp b/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp
index 6f785687b504..fc5267261851 100644
--- a/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp
+++ b/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp
@@ -295,7 +295,7 @@ static bool rewrite(Function &F) {
}
SmallVector<Value*, 4> Checks;
- if (propagatesPoison(&I))
+ if (propagatesPoison(cast<Operator>(&I)))
for (Value *V : I.operands())
Checks.push_back(getPoisonFor(ValToPoison, V));
diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
index 20b85626dced..f5a74b86ae9d 100644
--- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
+++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
@@ -1824,7 +1824,7 @@ static bool mustExecuteUBIfPoisonOnPathTo(Instruction *Root,
// If we can't analyze propagation through this instruction, just skip it
// and transitive users. Safe as false is a conservative result.
- if (!propagatesPoison(I) && I != Root)
+ if (!propagatesPoison(cast<Operator>(I)) && I != Root)
continue;
if (KnownPoison.insert(I).second)
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 3df5dc1fb82d..09faad448459 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -10,6 +10,7 @@
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/ConstantRange.h"
+#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instructions.h"
@@ -716,12 +717,57 @@ TEST(ValueTracking, propagatesPoison) {
for (auto &I : BB) {
if (isa<ReturnInst>(&I))
break;
- EXPECT_EQ(propagatesPoison(&I), Data[Index].first)
+ EXPECT_EQ(propagatesPoison(cast<Operator>(&I)), Data[Index].first)
<< "Incorrect answer at instruction " << Index << " = " << I;
Index++;
}
}
+TEST_F(ValueTrackingTest, programUndefinedIfPoison) {
+ parseAssembly("declare i32 @any_num()"
+ "define void @test(i32 %mask) {\n"
+ " %A = call i32 @any_num()\n"
+ " %B = or i32 %A, %mask\n"
+ " udiv i32 1, %B"
+ " ret void\n"
+ "}\n");
+ // If %A was poison, udiv raises UB regardless of %mask's value
+ EXPECT_EQ(programUndefinedIfPoison(A), true);
+}
+
+TEST_F(ValueTrackingTest, programUndefinedIfUndefOrPoison) {
+ parseAssembly("declare i32 @any_num()"
+ "define void @test(i32 %mask) {\n"
+ " %A = call i32 @any_num()\n"
+ " %B = or i32 %A, %mask\n"
+ " udiv i32 1, %B"
+ " ret void\n"
+ "}\n");
+ // If %A was undef and %mask was 1, udiv does not raise UB
+ EXPECT_EQ(programUndefinedIfUndefOrPoison(A), false);
+}
+
+TEST_F(ValueTrackingTest, isGuaranteedNotToBePoison_exploitBranchCond) {
+ parseAssembly("declare i1 @any_bool()"
+ "define void @test(i1 %y) {\n"
+ " %A = call i1 @any_bool()\n"
+ " %cond = and i1 %A, %y\n"
+ " br i1 %cond, label %BB1, label %BB2\n"
+ "BB1:\n"
+ " ret void\n"
+ "BB2:\n"
+ " ret void\n"
+ "}\n");
+ DominatorTree DT(*F);
+ for (auto &BB : *F) {
+ if (&BB == &F->getEntryBlock())
+ continue;
+
+ EXPECT_EQ(isGuaranteedNotToBePoison(A, BB.getTerminator(), &DT), true)
+ << "isGuaranteedNotToBePoison does not hold at " << *BB.getTerminator();
+ }
+}
+
TEST(ValueTracking, canCreatePoisonOrUndef) {
std::string AsmHead =
"declare i32 @g(i32)\n"