Skip to content

Commit

Permalink
[ValueLattice] Distinguish between constant ranges with/without undef.
Browse files Browse the repository at this point in the history
This patch updates ValueLattice to distinguish between ranges that are
guaranteed to not include undef and ranges that may include undef.

A constant range guaranteed to not contain undef can be used to simplify
instructions to arbitrary values. A constant range that may contain
undef can only be used to simplify to a constant. If the value can be
undef, it might take a value outside the range. For example, consider
the snipped below

define i32 @f(i32 %a, i1 %c) {
  br i1 %c, label %true, label %false
true:
  %a.255 = and i32 %a, 255
  br label %exit
false:
  br label %exit
exit:
  %p = phi i32 [ %a.255, %true ], [ undef, %false ]
  %f.1 = icmp eq i32 %p, 300
  call void @use(i1 %f.1)
  %res = and i32 %p, 255
  ret i32 %res
}

In the exit block, %p would be a constant range [0, 256) including undef as
%p could be undef. We can use the range information to replace %f.1 with
false because we remove the compare, effectively forcing the use of the
constant to be != 300. We cannot replace %res with %p however, because
if %a would be undef %cond may be true but the  second use might not be
< 256.

Currently LazyValueInfo uses the new behavior just when simplifying AND
instructions and does not distinguish between constant ranges with and
without undef otherwise. I think we should address the remaining issues
in LVI incrementally.

Reviewers: efriedma, reames, aqjune, jdoerfert, sstefan1

Reviewed By: efriedma

Differential Revision: https://reviews.llvm.org/D76931
  • Loading branch information
fhahn committed Mar 31, 2020
1 parent b4d0384 commit b375437
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 99 deletions.
4 changes: 3 additions & 1 deletion llvm/include/llvm/Analysis/LazyValueInfo.h
Expand Up @@ -85,7 +85,9 @@ class LazyValueInfo {
/// Return the ConstantRange constraint that is known to hold for the
/// specified value at the end of the specified block. This may only be called
/// on integer-typed Values.
ConstantRange getConstantRange(Value *V, BasicBlock *BB, Instruction *CxtI = nullptr);
ConstantRange getConstantRange(Value *V, BasicBlock *BB,
Instruction *CxtI = nullptr,
bool UndefAllowed = true);

/// Determine whether the specified value is known to be a
/// constant on the specified edge. Return null if not.
Expand Down
109 changes: 61 additions & 48 deletions llvm/include/llvm/Analysis/ValueLattice.h
Expand Up @@ -37,7 +37,7 @@ class ValueLatticeElement {
/// assuming all uses of the result will be replaced.
/// Transition allowed to the following states:
/// constant
/// singlecrfromundef
/// constantrange_including_undef
/// overdefined
undef,

Expand All @@ -59,20 +59,20 @@ class ValueLatticeElement {
/// The Value falls within this range. (Used only for integer typed values.)
/// Transition allowed to the following states:
/// constantrange (new range must be a superset of the existing range)
/// singlecrfromundef (range must stay a single element range)
/// constantrange_including_undef
/// overdefined
constantrange,

/// This Value contains a single element constant range that was merged with
/// an Undef value. Merging it with other constant ranges results in
/// overdefined, unless they match the single element constant range.
/// This Value falls within this range, but also may be undef.
/// Merging it with other constant ranges results in
/// constantrange_including_undef.
/// Transition allowed to the following states:
/// overdefined
singlecrfromundef,
constantrange_including_undef,

/// We can not precisely model the dynamic values this value might take.
/// No transitions are allowed after reaching overdefined.
overdefined
overdefined,
};

ValueLatticeElementTy Tag;
Expand All @@ -97,9 +97,9 @@ class ValueLatticeElement {
case unknown:
case undef:
case constant:
case singlecrfromundef:
case notconstant:
break;
case constantrange_including_undef:
case constantrange:
Range.~ConstantRange();
break;
Expand Down Expand Up @@ -128,7 +128,7 @@ class ValueLatticeElement {

switch (Other.Tag) {
case constantrange:
case singlecrfromundef:
case constantrange_including_undef:
if (!isConstantRange())
new (&Range) ConstantRange(Other.Range);
else
Expand Down Expand Up @@ -161,12 +161,13 @@ class ValueLatticeElement {
Res.markNotConstant(C);
return Res;
}
static ValueLatticeElement getRange(ConstantRange CR) {
static ValueLatticeElement getRange(ConstantRange CR,
bool MayIncludeUndef = false) {
if (CR.isFullSet())
return getOverdefined();

ValueLatticeElement Res;
Res.markConstantRange(std::move(CR));
Res.markConstantRange(std::move(CR), MayIncludeUndef);
return Res;
}
static ValueLatticeElement getOverdefined() {
Expand All @@ -179,10 +180,17 @@ class ValueLatticeElement {
bool isUnknown() const { return Tag == unknown; }
bool isUnknownOrUndef() const { return Tag == unknown || Tag == undef; }
bool isConstant() const { return Tag == constant; }
bool isSingleCRFromUndef() const { return Tag == singlecrfromundef; }
bool isNotConstant() const { return Tag == notconstant; }
bool isConstantRange() const {
return Tag == constantrange || Tag == singlecrfromundef;
bool isConstantRangeIncludingUndef() const {
return Tag == constantrange_including_undef;
}
/// Returns true if this value is a constant range. Use \p UndefAllowed to
/// exclude non-singleton constant ranges that may also be undef. Note that
/// this function also returns true if the range may include undef, but only
/// contains a single element. In that case, it can be replaced by a constant.
bool isConstantRange(bool UndefAllowed = true) const {
return Tag == constantrange || (Tag == constantrange_including_undef &&
(UndefAllowed || Range.isSingleElement()));
}
bool isOverdefined() const { return Tag == overdefined; }

Expand All @@ -196,8 +204,12 @@ class ValueLatticeElement {
return ConstVal;
}

const ConstantRange &getConstantRange() const {
assert(isConstantRange() &&
/// Returns the constant range for this value. Use \p UndefAllowed to exclude
/// non-singleton constant ranges that may also be undef. Note that this
/// function also returns a range if the range may include undef, but only
/// contains a single element. In that case, it can be replaced by a constant.
const ConstantRange &getConstantRange(bool UndefAllowed = true) const {
assert(isConstantRange(UndefAllowed) &&
"Cannot get the constant-range of a non-constant-range!");
return Range;
}
Expand Down Expand Up @@ -231,7 +243,7 @@ class ValueLatticeElement {
return true;
}

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

Expand All @@ -241,7 +253,7 @@ class ValueLatticeElement {
}

if (ConstantInt *CI = dyn_cast<ConstantInt>(V))
return markConstantRange(ConstantRange(CI->getValue()));
return markConstantRange(ConstantRange(CI->getValue()), MayIncludeUndef);

assert(isUnknown() || isUndef());
Tag = constant;
Expand Down Expand Up @@ -271,29 +283,38 @@ 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 undef.
bool markConstantRange(ConstantRange NewR) {
if (isConstantRange()) {
if (getConstantRange() == NewR)
return false;

assert(!isSingleCRFromUndef());
/// NewR and the tag. Otherwise \p NewR must be a superset of the existing
/// range or the object must be undef. The tag is set to
/// constant_range_including_undef if either the existing value or the new
/// range may include undef.
bool markConstantRange(ConstantRange NewR, bool MayIncludeUndef = false) {
if (NewR.isFullSet())
return markOverdefined();

ValueLatticeElementTy OldTag = Tag;
ValueLatticeElementTy NewTag =
(isUndef() || isConstantRangeIncludingUndef() || MayIncludeUndef)
? constantrange_including_undef
: constantrange;
if (isConstantRange()) {
if (NewR.isEmptySet())
return markOverdefined();

Tag = NewTag;
if (getConstantRange() == NewR)
return Tag != OldTag;

assert(NewR.contains(getConstantRange()) &&
"Existing range must be a subset of NewR");
Range = std::move(NewR);
return true;
}

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

Tag = isUnknown() ? constantrange : singlecrfromundef;
Tag = NewTag;
new (&Range) ConstantRange(std::move(NewR));
return true;
}
Expand All @@ -313,9 +334,10 @@ class ValueLatticeElement {
if (RHS.isUndef())
return false;
if (RHS.isConstant())
return markConstant(RHS.getConstant());
if (RHS.isConstantRange() && RHS.getConstantRange().isSingleElement())
return markConstantRange(RHS.getConstantRange());
return markConstant(RHS.getConstant(), /*MayIncludeUndef=*/true);
if (RHS.isConstantRange())
return markConstantRange(RHS.getConstantRange(true),
/*MayIncludeUndef=*/true);
return markOverdefined();
}

Expand All @@ -341,9 +363,12 @@ class ValueLatticeElement {
return true;
}

auto OldTag = Tag;
assert(isConstantRange() && "New ValueLattice type?");
if (RHS.isUndef() && getConstantRange().isSingleElement())
return false;
if (RHS.isUndef()) {
Tag = constantrange_including_undef;
return OldTag != Tag;
}

if (!RHS.isConstantRange()) {
// We can get here if we've encountered a constantexpr of integer type
Expand All @@ -353,21 +378,9 @@ class ValueLatticeElement {
}

ConstantRange NewR = getConstantRange().unionWith(RHS.getConstantRange());

if (isSingleCRFromUndef() || RHS.isSingleCRFromUndef()) {
if (NewR.isSingleElement()) {
assert(getConstantRange() == NewR);
return false;
}
markOverdefined();
return true;
}
if (NewR.isFullSet())
return markOverdefined();
else if (NewR == getConstantRange())
return false;
else
return markConstantRange(std::move(NewR));
return markConstantRange(
std::move(NewR),
/*MayIncludeUndef=*/RHS.isConstantRangeIncludingUndef());
}

// Compares this symbolic value with Other using Pred and returns either
Expand Down
29 changes: 19 additions & 10 deletions llvm/lib/Analysis/LazyValueInfo.cpp
Expand Up @@ -121,11 +121,13 @@ static ValueLatticeElement intersect(const ValueLatticeElement &A,

// Intersect two constant ranges
ConstantRange Range =
A.getConstantRange().intersectWith(B.getConstantRange());
A.getConstantRange().intersectWith(B.getConstantRange());
// Note: An empty range is implicitly converted to overdefined internally.
// TODO: We could instead use Undefined here since we've proven a conflict
// and thus know this path must be unreachable.
return ValueLatticeElement::getRange(std::move(Range));
return ValueLatticeElement::getRange(
std::move(Range), /*MayIncludeUndef=*/A.isConstantRangeIncludingUndef() |
B.isConstantRangeIncludingUndef());
}

//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -901,29 +903,35 @@ bool LazyValueInfoImpl::solveBlockValueSelect(ValueLatticeElement &BBLV,
return TrueCR.umax(FalseCR);
};
}();
BBLV = ValueLatticeElement::getRange(ResultCR);
BBLV = ValueLatticeElement::getRange(
ResultCR, TrueVal.isConstantRangeIncludingUndef() |
FalseVal.isConstantRangeIncludingUndef());
return true;
}

if (SPR.Flavor == SPF_ABS) {
if (LHS == SI->getTrueValue()) {
BBLV = ValueLatticeElement::getRange(TrueCR.abs());
BBLV = ValueLatticeElement::getRange(
TrueCR.abs(), TrueVal.isConstantRangeIncludingUndef());
return true;
}
if (LHS == SI->getFalseValue()) {
BBLV = ValueLatticeElement::getRange(FalseCR.abs());
BBLV = ValueLatticeElement::getRange(
FalseCR.abs(), FalseVal.isConstantRangeIncludingUndef());
return true;
}
}

if (SPR.Flavor == SPF_NABS) {
ConstantRange Zero(APInt::getNullValue(TrueCR.getBitWidth()));
if (LHS == SI->getTrueValue()) {
BBLV = ValueLatticeElement::getRange(Zero.sub(TrueCR.abs()));
BBLV = ValueLatticeElement::getRange(
Zero.sub(TrueCR.abs()), FalseVal.isConstantRangeIncludingUndef());
return true;
}
if (LHS == SI->getFalseValue()) {
BBLV = ValueLatticeElement::getRange(Zero.sub(FalseCR.abs()));
BBLV = ValueLatticeElement::getRange(
Zero.sub(FalseCR.abs()), FalseVal.isConstantRangeIncludingUndef());
return true;
}
}
Expand Down Expand Up @@ -1706,16 +1714,17 @@ Constant *LazyValueInfo::getConstant(Value *V, BasicBlock *BB,
}

ConstantRange LazyValueInfo::getConstantRange(Value *V, BasicBlock *BB,
Instruction *CxtI) {
Instruction *CxtI,
bool UndefAllowed) {
assert(V->getType()->isIntegerTy());
unsigned Width = V->getType()->getIntegerBitWidth();
const DataLayout &DL = BB->getModule()->getDataLayout();
ValueLatticeElement Result =
getImpl(PImpl, AC, &DL, DT).getValueInBlock(V, BB, CxtI);
if (Result.isUnknown())
return ConstantRange::getEmpty(Width);
if (Result.isConstantRange())
return Result.getConstantRange();
if (Result.isConstantRange(UndefAllowed))
return Result.getConstantRange(UndefAllowed);
// We represent ConstantInt constants as constant ranges but other kinds
// of integer constants, i.e. ConstantExpr will be tagged as constants
assert(!(Result.isConstant() && isa<ConstantInt>(Result.getConstant())) &&
Expand Down
8 changes: 4 additions & 4 deletions llvm/lib/Analysis/ValueLattice.cpp
Expand Up @@ -20,10 +20,10 @@ raw_ostream &operator<<(raw_ostream &OS, const ValueLatticeElement &Val) {
if (Val.isNotConstant())
return OS << "notconstant<" << *Val.getNotConstant() << ">";

if (Val.isSingleCRFromUndef())
return OS << "constantrange (from undef)<"
<< Val.getConstantRange().getLower() << ", "
<< Val.getConstantRange().getUpper() << ">";
if (Val.isConstantRangeIncludingUndef())
return OS << "constantrange incl. undef <"
<< Val.getConstantRange(true).getLower() << ", "
<< Val.getConstantRange(true).getUpper() << ">";

if (Val.isConstantRange())
return OS << "constantrange<" << Val.getConstantRange().getLower() << ", "
Expand Down
5 changes: 4 additions & 1 deletion llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
Expand Up @@ -790,7 +790,10 @@ static bool processAnd(BinaryOperator *BinOp, LazyValueInfo *LVI) {
if (!RHS || !RHS->getValue().isMask())
return false;

ConstantRange LRange = LVI->getConstantRange(LHS, BB, BinOp);
// We can only replace the AND with LHS based on range info if the range does
// not include undef.
ConstantRange LRange =
LVI->getConstantRange(LHS, BB, BinOp, /*UndefAllowed=*/false);
if (!LRange.getUnsignedMax().ule(RHS->getValue()))
return false;

Expand Down

0 comments on commit b375437

Please sign in to comment.