#ifndef LLVM_ANALYSIS_VALUELATTICE_H
#define LLVM_ANALYSIS_VALUELATTICE_H
#include "llvm/IR/Constants.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/Instructions.h"
namespace llvm {
class Constant;
class ValueLatticeElement {
enum ValueLatticeElementTy {
unknown,
undef,
constant,
notconstant,
constantrange,
constantrange_including_undef,
overdefined,
};
ValueLatticeElementTy Tag : 8;
unsigned NumRangeExtensions : 8;
union {
Constant *ConstVal;
ConstantRange Range;
};
void destroy() {
switch (Tag) {
case overdefined:
case unknown:
case undef:
case constant:
case notconstant:
break;
case constantrange_including_undef:
case constantrange:
Range.~ConstantRange();
break;
};
}
public:
struct MergeOptions {
bool MayIncludeUndef;
bool CheckWiden;
unsigned MaxWidenSteps;
MergeOptions() : MergeOptions(false, false) {}
MergeOptions(bool MayIncludeUndef, bool CheckWiden,
unsigned MaxWidenSteps = 1)
: MayIncludeUndef(MayIncludeUndef), CheckWiden(CheckWiden),
MaxWidenSteps(MaxWidenSteps) {}
MergeOptions &setMayIncludeUndef(bool V = true) {
MayIncludeUndef = V;
return *this;
}
MergeOptions &setCheckWiden(bool V = true) {
CheckWiden = V;
return *this;
}
MergeOptions &setMaxWidenSteps(unsigned Steps = 1) {
CheckWiden = true;
MaxWidenSteps = Steps;
return *this;
}
};
ValueLatticeElement() : Tag(unknown), NumRangeExtensions(0) {}
~ValueLatticeElement() { destroy(); }
ValueLatticeElement(const ValueLatticeElement &Other)
: Tag(Other.Tag), NumRangeExtensions(0) {
switch (Other.Tag) {
case constantrange:
case constantrange_including_undef:
new (&Range) ConstantRange(Other.Range);
NumRangeExtensions = Other.NumRangeExtensions;
break;
case constant:
case notconstant:
ConstVal = Other.ConstVal;
break;
case overdefined:
case unknown:
case undef:
break;
}
}
ValueLatticeElement(ValueLatticeElement &&Other)
: Tag(Other.Tag), NumRangeExtensions(0) {
switch (Other.Tag) {
case constantrange:
case constantrange_including_undef:
new (&Range) ConstantRange(std::move(Other.Range));
NumRangeExtensions = Other.NumRangeExtensions;
break;
case constant:
case notconstant:
ConstVal = Other.ConstVal;
break;
case overdefined:
case unknown:
case undef:
break;
}
Other.Tag = unknown;
}
ValueLatticeElement &operator=(const ValueLatticeElement &Other) {
destroy();
new (this) ValueLatticeElement(Other);
return *this;
}
ValueLatticeElement &operator=(ValueLatticeElement &&Other) {
destroy();
new (this) ValueLatticeElement(std::move(Other));
return *this;
}
static ValueLatticeElement get(Constant *C) {
ValueLatticeElement Res;
if (isa<UndefValue>(C))
Res.markUndef();
else
Res.markConstant(C);
return Res;
}
static ValueLatticeElement getNot(Constant *C) {
ValueLatticeElement Res;
assert(!isa<UndefValue>(C) && "!= undef is not supported");
Res.markNotConstant(C);
return Res;
}
static ValueLatticeElement getRange(ConstantRange CR,
bool MayIncludeUndef = false) {
if (CR.isFullSet())
return getOverdefined();
if (CR.isEmptySet()) {
ValueLatticeElement Res;
if (MayIncludeUndef)
Res.markUndef();
return Res;
}
ValueLatticeElement Res;
Res.markConstantRange(std::move(CR),
MergeOptions().setMayIncludeUndef(MayIncludeUndef));
return Res;
}
static ValueLatticeElement getOverdefined() {
ValueLatticeElement Res;
Res.markOverdefined();
return Res;
}
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 isConstantRangeIncludingUndef() const {
return Tag == constantrange_including_undef;
}
bool isConstantRange(bool UndefAllowed = true) const {
return Tag == constantrange || (Tag == constantrange_including_undef &&
(UndefAllowed || Range.isSingleElement()));
}
bool isOverdefined() const { return Tag == overdefined; }
Constant *getConstant() const {
assert(isConstant() && "Cannot get the constant of a non-constant!");
return ConstVal;
}
Constant *getNotConstant() const {
assert(isNotConstant() && "Cannot get the constant of a non-notconstant!");
return ConstVal;
}
const ConstantRange &getConstantRange(bool UndefAllowed = true) const {
assert(isConstantRange(UndefAllowed) &&
"Cannot get the constant-range of a non-constant-range!");
return Range;
}
Optional<APInt> asConstantInteger() const {
if (isConstant() && isa<ConstantInt>(getConstant())) {
return cast<ConstantInt>(getConstant())->getValue();
} else if (isConstantRange() && getConstantRange().isSingleElement()) {
return *getConstantRange().getSingleElement();
}
return None;
}
bool markOverdefined() {
if (isOverdefined())
return false;
destroy();
Tag = overdefined;
return true;
}
bool markUndef() {
if (isUndef())
return false;
assert(isUnknown());
Tag = undef;
return true;
}
bool markConstant(Constant *V, bool MayIncludeUndef = false) {
if (isa<UndefValue>(V))
return markUndef();
if (isConstant()) {
assert(getConstant() == V && "Marking constant with different value");
return false;
}
if (ConstantInt *CI = dyn_cast<ConstantInt>(V))
return markConstantRange(
ConstantRange(CI->getValue()),
MergeOptions().setMayIncludeUndef(MayIncludeUndef));
assert(isUnknown() || isUndef());
Tag = constant;
ConstVal = V;
return true;
}
bool markNotConstant(Constant *V) {
assert(V && "Marking constant with NULL");
if (ConstantInt *CI = dyn_cast<ConstantInt>(V))
return markConstantRange(
ConstantRange(CI->getValue() + 1, CI->getValue()));
if (isa<UndefValue>(V))
return false;
if (isNotConstant()) {
assert(getNotConstant() == V && "Marking !constant with different value");
return false;
}
assert(isUnknown());
Tag = notconstant;
ConstVal = V;
return true;
}
bool markConstantRange(ConstantRange NewR,
MergeOptions Opts = MergeOptions()) {
assert(!NewR.isEmptySet() && "should only be called for non-empty sets");
if (NewR.isFullSet())
return markOverdefined();
ValueLatticeElementTy OldTag = Tag;
ValueLatticeElementTy NewTag =
(isUndef() || isConstantRangeIncludingUndef() || Opts.MayIncludeUndef)
? constantrange_including_undef
: constantrange;
if (isConstantRange()) {
Tag = NewTag;
if (getConstantRange() == NewR)
return Tag != OldTag;
if (Opts.CheckWiden && ++NumRangeExtensions > Opts.MaxWidenSteps)
return markOverdefined();
assert(NewR.contains(getConstantRange()) &&
"Existing range must be a subset of NewR");
Range = std::move(NewR);
return true;
}
assert(isUnknown() || isUndef());
NumRangeExtensions = 0;
Tag = NewTag;
new (&Range) ConstantRange(std::move(NewR));
return true;
}
bool mergeIn(const ValueLatticeElement &RHS,
MergeOptions Opts = MergeOptions()) {
if (RHS.isUnknown() || isOverdefined())
return false;
if (RHS.isOverdefined()) {
markOverdefined();
return true;
}
if (isUndef()) {
assert(!RHS.isUnknown());
if (RHS.isUndef())
return false;
if (RHS.isConstant())
return markConstant(RHS.getConstant(), true);
if (RHS.isConstantRange())
return markConstantRange(RHS.getConstantRange(true),
Opts.setMayIncludeUndef());
return markOverdefined();
}
if (isUnknown()) {
assert(!RHS.isUnknown() && "Unknow RHS should be handled earlier");
*this = RHS;
return true;
}
if (isConstant()) {
if (RHS.isConstant() && getConstant() == RHS.getConstant())
return false;
if (RHS.isUndef())
return false;
markOverdefined();
return true;
}
if (isNotConstant()) {
if (RHS.isNotConstant() && getNotConstant() == RHS.getNotConstant())
return false;
markOverdefined();
return true;
}
auto OldTag = Tag;
assert(isConstantRange() && "New ValueLattice type?");
if (RHS.isUndef()) {
Tag = constantrange_including_undef;
return OldTag != Tag;
}
if (!RHS.isConstantRange()) {
markOverdefined();
return true;
}
ConstantRange NewR = getConstantRange().unionWith(RHS.getConstantRange());
return markConstantRange(
std::move(NewR),
Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
}
Constant *getCompare(CmpInst::Predicate Pred, Type *Ty,
const ValueLatticeElement &Other) const {
if (isUnknownOrUndef() || Other.isUnknownOrUndef())
return UndefValue::get(Ty);
if (isConstant() && Other.isConstant())
return ConstantExpr::getCompare(Pred, getConstant(), Other.getConstant());
if (ICmpInst::isEquality(Pred)) {
if ((isNotConstant() && Other.isConstant() &&
getNotConstant() == Other.getConstant()) ||
(isConstant() && Other.isNotConstant() &&
getConstant() == Other.getNotConstant()))
return Pred == ICmpInst::ICMP_NE
? ConstantInt::getTrue(Ty) : ConstantInt::getFalse(Ty);
}
if (!isConstantRange() || !Other.isConstantRange())
return nullptr;
const auto &CR = getConstantRange();
const auto &OtherCR = Other.getConstantRange();
if (CR.icmp(Pred, OtherCR))
return ConstantInt::getTrue(Ty);
if (CR.icmp(CmpInst::getInversePredicate(Pred), OtherCR))
return ConstantInt::getFalse(Ty);
return nullptr;
}
unsigned getNumRangeExtensions() const { return NumRangeExtensions; }
void setNumRangeExtensions(unsigned N) { NumRangeExtensions = N; }
};
static_assert(sizeof(ValueLatticeElement) <= 40,
"size of ValueLatticeElement changed unexpectedly");
raw_ostream &operator<<(raw_ostream &OS, const ValueLatticeElement &Val);
} #endif