1
0
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:
Juneyoung Lee 2020-04-23 08:08:16 +09:00
parent aede5a0e78
commit 1146a2123b
7 changed files with 24 additions and 32 deletions

View File

@ -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

View File

@ -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)) {

View File

@ -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);
} }
} }

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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: