mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-10-19 02:52:53 +02:00
[ValueTracking] Let analyses assume a value cannot be partially poison
Summary: This is RFC for fixes in poison-related functions of ValueTracking. These functions assume that a value can be poison bitwisely, but the semantics of bitwise poison is not clear at the moment. Allowing a value to have bitwise poison adds complexity to reasoning about correctness of optimizations. This patch makes the analysis functions simply assume that a value is either fully poison or not, which has been used to understand the correctness of a few previous optimizations. The bitwise poison semantics seems to be only used by these functions as well. In terms of implementation, using value-wise poison concept makes existing functions do more precise analysis, which is what this patch contains. Reviewers: spatel, lebedev.ri, jdoerfert, reames, nikic, nlopes, regehr Reviewed By: nikic Subscribers: fhahn, hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D78503
This commit is contained in:
parent
aede5a0e78
commit
1146a2123b
@ -563,34 +563,26 @@ class Value;
|
|||||||
const Loop *L);
|
const Loop *L);
|
||||||
|
|
||||||
/// Return true if this function can prove that I is guaranteed to yield
|
/// Return true if this function can prove that I is guaranteed to yield
|
||||||
/// full-poison (all bits poison) if at least one of its operands are
|
/// poison if at least one of its operands is poison.
|
||||||
/// full-poison (all bits poison).
|
bool propagatesPoison(const Instruction *I);
|
||||||
///
|
|
||||||
/// The exact rules for how poison propagates through instructions have
|
|
||||||
/// not been settled as of 2015-07-10, so this function is conservative
|
|
||||||
/// and only considers poison to be propagated in uncontroversial
|
|
||||||
/// cases. There is no attempt to track values that may be only partially
|
|
||||||
/// poison.
|
|
||||||
bool propagatesFullPoison(const Instruction *I);
|
|
||||||
|
|
||||||
/// Return either nullptr or an operand of I such that I will trigger
|
/// Return either nullptr or an operand of I such that I will trigger
|
||||||
/// undefined behavior if I is executed and that operand has a full-poison
|
/// undefined behavior if I is executed and that operand has a poison
|
||||||
/// value (all bits poison).
|
/// value.
|
||||||
const Value *getGuaranteedNonFullPoisonOp(const Instruction *I);
|
const Value *getGuaranteedNonPoisonOp(const Instruction *I);
|
||||||
|
|
||||||
/// 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
|
/// when I is executed with any operands which appear in KnownPoison holding
|
||||||
/// a full-poison value at the point of execution.
|
/// a poison value at the point of execution.
|
||||||
bool mustTriggerUB(const Instruction *I,
|
bool mustTriggerUB(const Instruction *I,
|
||||||
const SmallSet<const Value *, 16>& KnownPoison);
|
const SmallSet<const Value *, 16>& KnownPoison);
|
||||||
|
|
||||||
/// Return true if this function can prove that if PoisonI is executed
|
/// Return true if this function can prove that if PoisonI is executed
|
||||||
/// and yields a full-poison value (all bits poison), then that will
|
/// and yields a poison value, then that will trigger undefined behavior.
|
||||||
/// trigger undefined behavior.
|
|
||||||
///
|
///
|
||||||
/// Note that this currently only considers the basic block that is
|
/// Note that this currently only considers the basic block that is
|
||||||
/// the parent of I.
|
/// the parent of I.
|
||||||
bool programUndefinedIfFullPoison(const Instruction *PoisonI);
|
bool programUndefinedIfPoison(const Instruction *PoisonI);
|
||||||
|
|
||||||
/// Return true if I can create poison from non-poison operands.
|
/// Return true if I can create poison from non-poison operands.
|
||||||
/// For vectors, canCreatePoison returns true if there is potential poison in
|
/// For vectors, canCreatePoison returns true if there is potential poison in
|
||||||
|
@ -6072,7 +6072,7 @@ bool ScalarEvolution::isSCEVExprNeverPoison(const Instruction *I) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Only proceed if we can prove that I does not yield poison.
|
// Only proceed if we can prove that I does not yield poison.
|
||||||
if (!programUndefinedIfFullPoison(I))
|
if (!programUndefinedIfPoison(I))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// At this point we know that if I is executed, then it does not wrap
|
// At this point we know that if I is executed, then it does not wrap
|
||||||
@ -6152,7 +6152,7 @@ bool ScalarEvolution::isAddRecNeverPoison(const Instruction *I, const Loop *L) {
|
|||||||
SmallVector<const Instruction *, 8> PoisonStack;
|
SmallVector<const Instruction *, 8> PoisonStack;
|
||||||
|
|
||||||
// We start by assuming \c I, the post-inc add recurrence, is poison. Only
|
// We start by assuming \c I, the post-inc add recurrence, is poison. Only
|
||||||
// things that are known to be fully poison under that assumption go on the
|
// things that are known to be poison under that assumption go on the
|
||||||
// PoisonStack.
|
// PoisonStack.
|
||||||
Pushed.insert(I);
|
Pushed.insert(I);
|
||||||
PoisonStack.push_back(I);
|
PoisonStack.push_back(I);
|
||||||
@ -6162,7 +6162,7 @@ bool ScalarEvolution::isAddRecNeverPoison(const Instruction *I, const Loop *L) {
|
|||||||
const Instruction *Poison = PoisonStack.pop_back_val();
|
const Instruction *Poison = PoisonStack.pop_back_val();
|
||||||
|
|
||||||
for (auto *PoisonUser : Poison->users()) {
|
for (auto *PoisonUser : Poison->users()) {
|
||||||
if (propagatesFullPoison(cast<Instruction>(PoisonUser))) {
|
if (propagatesPoison(cast<Instruction>(PoisonUser))) {
|
||||||
if (Pushed.insert(cast<Instruction>(PoisonUser)).second)
|
if (Pushed.insert(cast<Instruction>(PoisonUser)).second)
|
||||||
PoisonStack.push_back(cast<Instruction>(PoisonUser));
|
PoisonStack.push_back(cast<Instruction>(PoisonUser));
|
||||||
} else if (auto *BI = dyn_cast<BranchInst>(PoisonUser)) {
|
} else if (auto *BI = dyn_cast<BranchInst>(PoisonUser)) {
|
||||||
|
@ -4720,7 +4720,7 @@ bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (auto I = dyn_cast<Instruction>(V)) {
|
if (auto I = dyn_cast<Instruction>(V)) {
|
||||||
if (programUndefinedIfFullPoison(I) && I->getType()->isIntegerTy(1))
|
if (programUndefinedIfPoison(I) && I->getType()->isIntegerTy(1))
|
||||||
// Note: once we have an agreement that poison is a value-wise concept,
|
// Note: once we have an agreement that poison is a value-wise concept,
|
||||||
// we can remove the isIntegerTy(1) constraint.
|
// we can remove the isIntegerTy(1) constraint.
|
||||||
return true;
|
return true;
|
||||||
@ -4846,7 +4846,7 @@ bool llvm::isGuaranteedToExecuteForEveryIteration(const Instruction *I,
|
|||||||
llvm_unreachable("Instruction not contained in its own parent basic block.");
|
llvm_unreachable("Instruction not contained in its own parent basic block.");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool llvm::propagatesFullPoison(const Instruction *I) {
|
bool llvm::propagatesPoison(const Instruction *I) {
|
||||||
// TODO: This should include all instructions apart from phis, selects and
|
// TODO: This should include all instructions apart from phis, selects and
|
||||||
// call-like instructions.
|
// call-like instructions.
|
||||||
switch (I->getOpcode()) {
|
switch (I->getOpcode()) {
|
||||||
@ -4880,7 +4880,7 @@ bool llvm::propagatesFullPoison(const Instruction *I) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Value *llvm::getGuaranteedNonFullPoisonOp(const Instruction *I) {
|
const Value *llvm::getGuaranteedNonPoisonOp(const Instruction *I) {
|
||||||
switch (I->getOpcode()) {
|
switch (I->getOpcode()) {
|
||||||
case Instruction::Store:
|
case Instruction::Store:
|
||||||
return cast<StoreInst>(I)->getPointerOperand();
|
return cast<StoreInst>(I)->getPointerOperand();
|
||||||
@ -4918,12 +4918,12 @@ const Value *llvm::getGuaranteedNonFullPoisonOp(const Instruction *I) {
|
|||||||
|
|
||||||
bool llvm::mustTriggerUB(const Instruction *I,
|
bool llvm::mustTriggerUB(const Instruction *I,
|
||||||
const SmallSet<const Value *, 16>& KnownPoison) {
|
const SmallSet<const Value *, 16>& KnownPoison) {
|
||||||
auto *NotPoison = getGuaranteedNonFullPoisonOp(I);
|
auto *NotPoison = getGuaranteedNonPoisonOp(I);
|
||||||
return (NotPoison && KnownPoison.count(NotPoison));
|
return (NotPoison && KnownPoison.count(NotPoison));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool llvm::programUndefinedIfFullPoison(const Instruction *PoisonI) {
|
bool llvm::programUndefinedIfPoison(const Instruction *PoisonI) {
|
||||||
// We currently only look for uses of poison values within the same basic
|
// We currently only look for uses of poison values within the same basic
|
||||||
// block, as that makes it easier to guarantee that the uses will be
|
// block, as that makes it easier to guarantee that the uses will be
|
||||||
// executed given that PoisonI is executed.
|
// executed given that PoisonI is executed.
|
||||||
@ -4956,7 +4956,7 @@ bool llvm::programUndefinedIfFullPoison(const Instruction *PoisonI) {
|
|||||||
if (YieldsPoison.count(&I)) {
|
if (YieldsPoison.count(&I)) {
|
||||||
for (const User *User : I.users()) {
|
for (const User *User : I.users()) {
|
||||||
const Instruction *UserI = cast<Instruction>(User);
|
const Instruction *UserI = cast<Instruction>(User);
|
||||||
if (propagatesFullPoison(UserI))
|
if (propagatesPoison(UserI))
|
||||||
YieldsPoison.insert(User);
|
YieldsPoison.insert(User);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,7 +283,7 @@ static bool rewrite(Function &F) {
|
|||||||
|
|
||||||
// Note: There are many more sources of documented UB, but this pass only
|
// Note: There are many more sources of documented UB, but this pass only
|
||||||
// attempts to find UB triggered by propagation of poison.
|
// attempts to find UB triggered by propagation of poison.
|
||||||
if (Value *Op = const_cast<Value*>(getGuaranteedNonFullPoisonOp(&I)))
|
if (Value *Op = const_cast<Value*>(getGuaranteedNonPoisonOp(&I)))
|
||||||
CreateAssertNot(B, getPoisonFor(ValToPoison, Op));
|
CreateAssertNot(B, getPoisonFor(ValToPoison, Op));
|
||||||
|
|
||||||
if (LocalCheck)
|
if (LocalCheck)
|
||||||
@ -294,7 +294,7 @@ static bool rewrite(Function &F) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SmallVector<Value*, 4> Checks;
|
SmallVector<Value*, 4> Checks;
|
||||||
if (propagatesFullPoison(&I))
|
if (propagatesPoison(&I))
|
||||||
for (Value *V : I.operands())
|
for (Value *V : I.operands())
|
||||||
Checks.push_back(getPoisonFor(ValToPoison, V));
|
Checks.push_back(getPoisonFor(ValToPoison, V));
|
||||||
|
|
||||||
@ -344,7 +344,7 @@ PreservedAnalyses PoisonCheckingPass::run(Function &F,
|
|||||||
- shufflevector - It would seem reasonable for an out of bounds mask element
|
- shufflevector - It would seem reasonable for an out of bounds mask element
|
||||||
to produce poison, but the LangRef does not state.
|
to produce poison, but the LangRef does not state.
|
||||||
- and/or - It would seem reasonable for poison to propagate from both
|
- and/or - It would seem reasonable for poison to propagate from both
|
||||||
arguments, but LangRef doesn't state and propagatesFullPoison doesn't
|
arguments, but LangRef doesn't state and propagatesPoison doesn't
|
||||||
include these two.
|
include these two.
|
||||||
- all binary ops w/vector operands - The likely interpretation would be that
|
- all binary ops w/vector operands - The likely interpretation would be that
|
||||||
any element overflowing should produce poison for the entire result, but
|
any element overflowing should produce poison for the entire result, but
|
||||||
|
@ -1813,7 +1813,7 @@ static bool mustExecuteUBIfPoisonOnPathTo(Instruction *Root,
|
|||||||
|
|
||||||
// If we can't analyze propagation through this instruction, just skip it
|
// If we can't analyze propagation through this instruction, just skip it
|
||||||
// and transitive users. Safe as false is a conservative result.
|
// and transitive users. Safe as false is a conservative result.
|
||||||
if (!propagatesFullPoison(I) && I != Root)
|
if (!propagatesPoison(I) && I != Root)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (KnownPoison.insert(I).second)
|
if (KnownPoison.insert(I).second)
|
||||||
|
@ -1214,13 +1214,13 @@ bool SeparateConstOffsetFromGEP::reuniteExts(Instruction *I) {
|
|||||||
|
|
||||||
// Add I to DominatingExprs if it's an add/sub that can't sign overflow.
|
// Add I to DominatingExprs if it's an add/sub that can't sign overflow.
|
||||||
if (match(I, m_NSWAdd(m_Value(LHS), m_Value(RHS)))) {
|
if (match(I, m_NSWAdd(m_Value(LHS), m_Value(RHS)))) {
|
||||||
if (programUndefinedIfFullPoison(I)) {
|
if (programUndefinedIfPoison(I)) {
|
||||||
const SCEV *Key =
|
const SCEV *Key =
|
||||||
SE->getAddExpr(SE->getUnknown(LHS), SE->getUnknown(RHS));
|
SE->getAddExpr(SE->getUnknown(LHS), SE->getUnknown(RHS));
|
||||||
DominatingAdds[Key].push_back(I);
|
DominatingAdds[Key].push_back(I);
|
||||||
}
|
}
|
||||||
} else if (match(I, m_NSWSub(m_Value(LHS), m_Value(RHS)))) {
|
} else if (match(I, m_NSWSub(m_Value(LHS), m_Value(RHS)))) {
|
||||||
if (programUndefinedIfFullPoison(I)) {
|
if (programUndefinedIfPoison(I)) {
|
||||||
const SCEV *Key =
|
const SCEV *Key =
|
||||||
SE->getAddExpr(SE->getUnknown(LHS), SE->getUnknown(RHS));
|
SE->getAddExpr(SE->getUnknown(LHS), SE->getUnknown(RHS));
|
||||||
DominatingSubs[Key].push_back(I);
|
DominatingSubs[Key].push_back(I);
|
||||||
|
@ -205,7 +205,7 @@ exit:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Demonstrate why we need a Visited set in llvm::programUndefinedIfFullPoison.
|
; Demonstrate why we need a Visited set in llvm::programUndefinedIfPoison.
|
||||||
define void @test-add-not-header5(float* %input, i32 %offset) {
|
define void @test-add-not-header5(float* %input, i32 %offset) {
|
||||||
; CHECK-LABEL: @test-add-not-header5
|
; CHECK-LABEL: @test-add-not-header5
|
||||||
entry:
|
entry:
|
||||||
|
Loading…
Reference in New Issue
Block a user