Skip to content

Commit

Permalink
[ValueLattice] Add new state for undef constants.
Browse files Browse the repository at this point in the history
This patch adds a new undef lattice state, which is used to represent
UndefValue constants or instructions producing undef.

The main difference to the unknown state is that merging undef values
with constants (or single element constant ranges) produces  the
constant/constant range, assuming all uses of the merge result will be
replaced by the found constant.

Contrary, merging non-single element ranges with undef needs to go to
overdefined. Using unknown for UndefValues currently causes mis-compiles
in CVP/LVI (PR44949) and will become problematic once we use
ValueLatticeElement for SCCP.

Reviewers: efriedma, reames, davide, nikic

Reviewed By: efriedma

Differential Revision: https://reviews.llvm.org/D75120
  • Loading branch information
fhahn committed Mar 14, 2020
1 parent b236b4c commit 4878aa3
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 58 deletions.
76 changes: 56 additions & 20 deletions llvm/include/llvm/Analysis/ValueLattice.h
Expand Up @@ -29,7 +29,12 @@ class ValueLatticeElement {
/// producing instruction is dead. Caution: We use this as the starting
/// state in our local meet rules. In this usage, it's taken to mean
/// "nothing known yet".
undefined,
unknown,

/// This Value is an UndefValue constant or produces undef. Undefined values
/// can be merged with constants (or single element constant ranges),
/// assuming all uses of the result will be replaced.
undef,

/// This Value has a specific constant value. (For constant integers,
/// constantrange is used instead. Integer typed constantexprs can appear
Expand Down Expand Up @@ -60,14 +65,15 @@ class ValueLatticeElement {

public:
// Const and Range are initialized on-demand.
ValueLatticeElement() : Tag(undefined) {}
ValueLatticeElement() : Tag(unknown) {}

/// Custom destructor to ensure Range is properly destroyed, when the object
/// is deallocated.
~ValueLatticeElement() {
switch (Tag) {
case overdefined:
case undefined:
case unknown:
case undef:
case constant:
case notconstant:
break;
Expand All @@ -79,7 +85,7 @@ class ValueLatticeElement {

/// Custom copy constructor, to ensure Range gets initialized when
/// copying a constant range lattice element.
ValueLatticeElement(const ValueLatticeElement &Other) : Tag(undefined) {
ValueLatticeElement(const ValueLatticeElement &Other) : Tag(unknown) {
*this = Other;
}

Expand Down Expand Up @@ -109,7 +115,8 @@ class ValueLatticeElement {
ConstVal = Other.ConstVal;
break;
case overdefined:
case undefined:
case unknown:
case undef:
break;
}
Tag = Other.Tag;
Expand All @@ -118,14 +125,16 @@ class ValueLatticeElement {

static ValueLatticeElement get(Constant *C) {
ValueLatticeElement Res;
if (!isa<UndefValue>(C))
if (isa<UndefValue>(C))
Res.markUndef();
else
Res.markConstant(C);
return Res;
}
static ValueLatticeElement getNot(Constant *C) {
ValueLatticeElement Res;
if (!isa<UndefValue>(C))
Res.markNotConstant(C);
assert(!isa<UndefValue>(C) && "!= undef is not supported");
Res.markNotConstant(C);
return Res;
}
static ValueLatticeElement getRange(ConstantRange CR) {
Expand All @@ -139,8 +148,9 @@ class ValueLatticeElement {
return Res;
}

bool isUndefined() const { return Tag == undefined; }
bool isUnknown() const { return Tag == undefined; }
bool isUndef() const { return Tag == undef; }
bool isUnknown() const { return Tag == unknown; }
bool isUnknownOrUndef() const { return Tag == unknown || Tag == undef; }
bool isConstant() const { return Tag == constant; }
bool isNotConstant() const { return Tag == notconstant; }
bool isConstantRange() const { return Tag == constantrange; }
Expand Down Expand Up @@ -182,9 +192,18 @@ class ValueLatticeElement {
return true;
}

bool markUndef() {
if (isUndef())
return false;

assert(isUnknown());
Tag = undef;
return true;
}

bool markConstant(Constant *V) {
if (isa<UndefValue>(V))
return false;
return markUndef();

if (isConstant()) {
assert(getConstant() == V && "Marking constant with different value");
Expand All @@ -194,7 +213,7 @@ class ValueLatticeElement {
if (ConstantInt *CI = dyn_cast<ConstantInt>(V))
return markConstantRange(ConstantRange(CI->getValue()));

assert(isUndefined());
assert(isUnknown() || isUndef());
Tag = constant;
ConstVal = V;
return true;
Expand All @@ -214,7 +233,7 @@ class ValueLatticeElement {
return false;
}

assert(isUndefined());
assert(isUnknown());
Tag = notconstant;
ConstVal = V;
return true;
Expand All @@ -223,7 +242,7 @@ class ValueLatticeElement {
/// Mark the object as constant range with \p NewR. If the object is already a
/// constant range, nothing changes if the existing range is equal to \p
/// NewR. Otherwise \p NewR must be a superset of the existing range or the
/// object must be undefined.
/// object must be undef.
bool markConstantRange(ConstantRange NewR) {
if (isConstantRange()) {
if (getConstantRange() == NewR)
Expand All @@ -238,7 +257,7 @@ class ValueLatticeElement {
return true;
}

assert(isUndefined());
assert(isUnknown() || isUndef());
if (NewR.isEmptySet())
return markOverdefined();

Expand All @@ -250,21 +269,35 @@ class ValueLatticeElement {
/// Updates this object to approximate both this object and RHS. Returns
/// true if this object has been changed.
bool mergeIn(const ValueLatticeElement &RHS, const DataLayout &DL) {
if (RHS.isUndefined() || isOverdefined())
if (RHS.isUnknown() || isOverdefined())
return false;
if (RHS.isOverdefined()) {
markOverdefined();
return true;
}

if (isUndefined()) {
if (isUndef()) {
assert(!RHS.isUnknown());
if (RHS.isUndef())
return false;
if (RHS.isConstant())
return markConstant(RHS.getConstant());
if (RHS.isConstantRange() && RHS.getConstantRange().isSingleElement())
return markConstantRange(RHS.getConstantRange());
return markOverdefined();
}

if (isUnknown()) {
assert(!RHS.isUnknown() && "Unknow RHS should be handled earlier");
*this = RHS;
return !RHS.isUndefined();
return true;
}

if (isConstant()) {
if (RHS.isConstant() && getConstant() == RHS.getConstant())
return false;
if (RHS.isUndef())
return false;
markOverdefined();
return true;
}
Expand All @@ -277,6 +310,9 @@ class ValueLatticeElement {
}

assert(isConstantRange() && "New ValueLattice type?");
if (RHS.isUndef() && getConstantRange().isSingleElement())
return false;

if (!RHS.isConstantRange()) {
// We can get here if we've encountered a constantexpr of integer type
// and merge it with a constantrange.
Expand All @@ -292,12 +328,12 @@ class ValueLatticeElement {
return markConstantRange(std::move(NewR));
}

/// Compares this symbolic value with Other using Pred and returns either
// Compares this symbolic value with Other using Pred and returns either
/// true, false or undef constants, or nullptr if the comparison cannot be
/// evaluated.
Constant *getCompare(CmpInst::Predicate Pred, Type *Ty,
const ValueLatticeElement &Other) const {
if (isUndefined() || Other.isUndefined())
if (isUnknownOrUndef() || Other.isUnknownOrUndef())
return UndefValue::get(Ty);

if (isConstant() && Other.isConstant())
Expand Down
12 changes: 6 additions & 6 deletions llvm/lib/Analysis/LazyValueInfo.cpp
Expand Up @@ -96,9 +96,9 @@ static ValueLatticeElement intersect(const ValueLatticeElement &A,
const ValueLatticeElement &B) {
// Undefined is the strongest state. It means the value is known to be along
// an unreachable path.
if (A.isUndefined())
if (A.isUnknown())
return A;
if (B.isUndefined())
if (B.isUnknown())
return B;

// If we gave up for one, but got a useable fact from the other, use it.
Expand Down Expand Up @@ -1203,7 +1203,7 @@ static ValueLatticeElement getValueFromICmpCondition(Value *Val, ICmpInst *ICI,
// false SETNE.
if (isTrueDest == (Predicate == ICmpInst::ICMP_EQ))
return ValueLatticeElement::get(cast<Constant>(RHS));
else
else if (!isa<UndefValue>(RHS))
return ValueLatticeElement::getNot(cast<Constant>(RHS));
}
}
Expand Down Expand Up @@ -1722,7 +1722,7 @@ ConstantRange LazyValueInfo::getConstantRange(Value *V, BasicBlock *BB,
const DataLayout &DL = BB->getModule()->getDataLayout();
ValueLatticeElement Result =
getImpl(PImpl, AC, &DL, DT).getValueInBlock(V, BB, CxtI);
if (Result.isUndefined())
if (Result.isUnknown())
return ConstantRange::getEmpty(Width);
if (Result.isConstantRange())
return Result.getConstantRange();
Expand Down Expand Up @@ -1761,7 +1761,7 @@ ConstantRange LazyValueInfo::getConstantRangeOnEdge(Value *V,
ValueLatticeElement Result =
getImpl(PImpl, AC, &DL, DT).getValueOnEdge(V, FromBB, ToBB, CxtI);

if (Result.isUndefined())
if (Result.isUnknown())
return ConstantRange::getEmpty(Width);
if (Result.isConstantRange())
return Result.getConstantRange();
Expand Down Expand Up @@ -1991,7 +1991,7 @@ void LazyValueInfoAnnotatedWriter::emitBasicBlockStartAnnot(
for (auto &Arg : F->args()) {
ValueLatticeElement Result = LVIImpl->getValueInBlock(
const_cast<Argument *>(&Arg), const_cast<BasicBlock *>(BB));
if (Result.isUndefined())
if (Result.isUnknown())
continue;
OS << "; LatticeVal for: '" << Arg << "' is: " << Result << "\n";
}
Expand Down
6 changes: 4 additions & 2 deletions llvm/lib/Analysis/ValueLattice.cpp
Expand Up @@ -10,8 +10,10 @@

namespace llvm {
raw_ostream &operator<<(raw_ostream &OS, const ValueLatticeElement &Val) {
if (Val.isUndefined())
return OS << "undefined";
if (Val.isUnknown())
return OS << "unknown";
if (Val.isUndef())
return OS << "undef";
if (Val.isOverdefined())
return OS << "overdefined";

Expand Down

0 comments on commit 4878aa3

Please sign in to comment.