#include "clang/Basic/JsonSupport.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <iterator>
using namespace clang;
using namespace ento;
class OperatorRelationsTable {
static_assert(BO_LT < BO_GT && BO_GT < BO_LE && BO_LE < BO_GE &&
BO_GE < BO_EQ && BO_EQ < BO_NE,
"This class relies on operators order. Rework it otherwise.");
public:
enum TriStateKind {
False = 0,
True,
Unknown,
};
private:
static constexpr size_t CmpOpCount = BO_NE - BO_LT + 1;
const TriStateKind CmpOpTable[CmpOpCount][CmpOpCount + 1] = {
{True, False, Unknown, False, False, Unknown, True}, {False, True, False, Unknown, False, Unknown, True}, {True, False, True, Unknown, True, Unknown, False}, {False, True, Unknown, True, True, Unknown, False}, {False, False, Unknown, Unknown, True, False, True}, {True, True, Unknown, Unknown, False, True, False}, };
static size_t getIndexFromOp(BinaryOperatorKind OP) {
return static_cast<size_t>(OP - BO_LT);
}
public:
constexpr size_t getCmpOpCount() const { return CmpOpCount; }
static BinaryOperatorKind getOpFromIndex(size_t Index) {
return static_cast<BinaryOperatorKind>(Index + BO_LT);
}
TriStateKind getCmpOpState(BinaryOperatorKind CurrentOP,
BinaryOperatorKind QueriedOP) const {
return CmpOpTable[getIndexFromOp(CurrentOP)][getIndexFromOp(QueriedOP)];
}
TriStateKind getCmpOpStateForUnknownX2(BinaryOperatorKind CurrentOP) const {
return CmpOpTable[getIndexFromOp(CurrentOP)][CmpOpCount];
}
};
RangeSet::ContainerType RangeSet::Factory::EmptySet{};
RangeSet RangeSet::Factory::add(RangeSet LHS, RangeSet RHS) {
ContainerType Result;
Result.reserve(LHS.size() + RHS.size());
std::merge(LHS.begin(), LHS.end(), RHS.begin(), RHS.end(),
std::back_inserter(Result));
return makePersistent(std::move(Result));
}
RangeSet RangeSet::Factory::add(RangeSet Original, Range Element) {
ContainerType Result;
Result.reserve(Original.size() + 1);
const_iterator Lower = llvm::lower_bound(Original, Element);
Result.insert(Result.end(), Original.begin(), Lower);
Result.push_back(Element);
Result.insert(Result.end(), Lower, Original.end());
return makePersistent(std::move(Result));
}
RangeSet RangeSet::Factory::add(RangeSet Original, const llvm::APSInt &Point) {
return add(Original, Range(Point));
}
RangeSet RangeSet::Factory::unite(RangeSet LHS, RangeSet RHS) {
ContainerType Result = unite(*LHS.Impl, *RHS.Impl);
return makePersistent(std::move(Result));
}
RangeSet RangeSet::Factory::unite(RangeSet Original, Range R) {
ContainerType Result;
Result.push_back(R);
Result = unite(*Original.Impl, Result);
return makePersistent(std::move(Result));
}
RangeSet RangeSet::Factory::unite(RangeSet Original, llvm::APSInt Point) {
return unite(Original, Range(ValueFactory.getValue(Point)));
}
RangeSet RangeSet::Factory::unite(RangeSet Original, llvm::APSInt From,
llvm::APSInt To) {
return unite(Original,
Range(ValueFactory.getValue(From), ValueFactory.getValue(To)));
}
template <typename T>
void swapIterators(T &First, T &FirstEnd, T &Second, T &SecondEnd) {
std::swap(First, Second);
std::swap(FirstEnd, SecondEnd);
}
RangeSet::ContainerType RangeSet::Factory::unite(const ContainerType &LHS,
const ContainerType &RHS) {
if (LHS.empty())
return RHS;
if (RHS.empty())
return LHS;
using llvm::APSInt;
using iterator = ContainerType::const_iterator;
iterator First = LHS.begin();
iterator FirstEnd = LHS.end();
iterator Second = RHS.begin();
iterator SecondEnd = RHS.end();
APSIntType Ty = APSIntType(First->From());
const APSInt Min = Ty.getMinValue();
if (Min == First->From() && Min == Second->From()) {
if (First->To() > Second->To()) {
if (++Second == SecondEnd)
return LHS;
} else {
if (++First == FirstEnd)
return RHS;
}
}
const APSInt One = Ty.getValue(1);
ContainerType Result;
const auto AppendTheRest = [&Result](iterator I, iterator E) {
Result.append(I, E);
return Result;
};
while (true) {
if (First->From() > Second->From())
swapIterators(First, FirstEnd, Second, SecondEnd);
const llvm::APSInt &UnionStart = First->From();
while (true) {
while (First->To() >= Second->To()) {
if (++Second == SecondEnd) {
Result.emplace_back(UnionStart, First->To());
return AppendTheRest(++First, FirstEnd);
}
}
if (First->To() < Second->From() - One)
break;
if (++First == FirstEnd) {
Result.emplace_back(UnionStart, Second->To());
return AppendTheRest(++Second, SecondEnd);
}
swapIterators(First, FirstEnd, Second, SecondEnd);
}
Result.emplace_back(UnionStart, First->To());
if (++First == FirstEnd)
return AppendTheRest(Second, SecondEnd);
}
llvm_unreachable("Normally, we should not reach here");
}
RangeSet RangeSet::Factory::getRangeSet(Range From) {
ContainerType Result;
Result.push_back(From);
return makePersistent(std::move(Result));
}
RangeSet RangeSet::Factory::makePersistent(ContainerType &&From) {
llvm::FoldingSetNodeID ID;
void *InsertPos;
From.Profile(ID);
ContainerType *Result = Cache.FindNodeOrInsertPos(ID, InsertPos);
if (!Result) {
Result = construct(std::move(From));
Cache.InsertNode(Result, InsertPos);
}
return Result;
}
RangeSet::ContainerType *RangeSet::Factory::construct(ContainerType &&From) {
void *Buffer = Arena.Allocate();
return new (Buffer) ContainerType(std::move(From));
}
const llvm::APSInt &RangeSet::getMinValue() const {
assert(!isEmpty());
return begin()->From();
}
const llvm::APSInt &RangeSet::getMaxValue() const {
assert(!isEmpty());
return std::prev(end())->To();
}
bool clang::ento::RangeSet::isUnsigned() const {
assert(!isEmpty());
return begin()->From().isUnsigned();
}
uint32_t clang::ento::RangeSet::getBitWidth() const {
assert(!isEmpty());
return begin()->From().getBitWidth();
}
APSIntType clang::ento::RangeSet::getAPSIntType() const {
assert(!isEmpty());
return APSIntType(begin()->From());
}
bool RangeSet::containsImpl(llvm::APSInt &Point) const {
if (isEmpty() || !pin(Point))
return false;
Range Dummy(Point);
const_iterator It = llvm::upper_bound(*this, Dummy);
if (It == begin())
return false;
return std::prev(It)->Includes(Point);
}
bool RangeSet::pin(llvm::APSInt &Point) const {
APSIntType Type(getMinValue());
if (Type.testInRange(Point, true) != APSIntType::RTR_Within)
return false;
Type.apply(Point);
return true;
}
bool RangeSet::pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const {
APSIntType Type(getMinValue());
APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower, true);
APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper, true);
switch (LowerTest) {
case APSIntType::RTR_Below:
switch (UpperTest) {
case APSIntType::RTR_Below:
if (Lower <= Upper)
return false;
Lower = Type.getMinValue();
Upper = Type.getMaxValue();
break;
case APSIntType::RTR_Within:
Lower = Type.getMinValue();
Type.apply(Upper);
break;
case APSIntType::RTR_Above:
Lower = Type.getMinValue();
Upper = Type.getMaxValue();
break;
}
break;
case APSIntType::RTR_Within:
switch (UpperTest) {
case APSIntType::RTR_Below:
Type.apply(Lower);
Upper = Type.getMaxValue();
break;
case APSIntType::RTR_Within:
Type.apply(Lower);
Type.apply(Upper);
break;
case APSIntType::RTR_Above:
Type.apply(Lower);
Upper = Type.getMaxValue();
break;
}
break;
case APSIntType::RTR_Above:
switch (UpperTest) {
case APSIntType::RTR_Below:
return false;
case APSIntType::RTR_Within:
Lower = Type.getMinValue();
Type.apply(Upper);
break;
case APSIntType::RTR_Above:
if (Lower <= Upper)
return false;
Lower = Type.getMinValue();
Upper = Type.getMaxValue();
break;
}
break;
}
return true;
}
RangeSet RangeSet::Factory::intersect(RangeSet What, llvm::APSInt Lower,
llvm::APSInt Upper) {
if (What.isEmpty() || !What.pin(Lower, Upper))
return getEmptySet();
ContainerType DummyContainer;
if (Lower <= Upper) {
if (What.getMaxValue() < Lower || Upper < What.getMinValue())
return getEmptySet();
DummyContainer.push_back(
Range(ValueFactory.getValue(Lower), ValueFactory.getValue(Upper)));
} else {
if (What.getMaxValue() < Lower && Upper < What.getMinValue())
return getEmptySet();
DummyContainer.push_back(
Range(ValueFactory.getMinValue(Upper), ValueFactory.getValue(Upper)));
DummyContainer.push_back(
Range(ValueFactory.getValue(Lower), ValueFactory.getMaxValue(Lower)));
}
return intersect(*What.Impl, DummyContainer);
}
RangeSet RangeSet::Factory::intersect(const RangeSet::ContainerType &LHS,
const RangeSet::ContainerType &RHS) {
ContainerType Result;
Result.reserve(std::max(LHS.size(), RHS.size()));
const_iterator First = LHS.begin(), Second = RHS.begin(),
FirstEnd = LHS.end(), SecondEnd = RHS.end();
while (First != FirstEnd && Second != SecondEnd) {
if (Second->From() < First->From())
swapIterators(First, FirstEnd, Second, SecondEnd);
do {
if (Second->From() > First->To()) {
++First;
break;
}
const llvm::APSInt &IntersectionStart = Second->From();
if (Second->To() > First->To()) {
swapIterators(First, FirstEnd, Second, SecondEnd);
}
Result.push_back(Range(IntersectionStart, Second->To()));
++Second;
} while (Second != SecondEnd);
}
if (Result.empty())
return getEmptySet();
return makePersistent(std::move(Result));
}
RangeSet RangeSet::Factory::intersect(RangeSet LHS, RangeSet RHS) {
if (LHS.isEmpty() || RHS.isEmpty() || LHS.getMaxValue() < RHS.getMinValue() ||
RHS.getMaxValue() < LHS.getMinValue())
return getEmptySet();
return intersect(*LHS.Impl, *RHS.Impl);
}
RangeSet RangeSet::Factory::intersect(RangeSet LHS, llvm::APSInt Point) {
if (LHS.containsImpl(Point))
return getRangeSet(ValueFactory.getValue(Point));
return getEmptySet();
}
RangeSet RangeSet::Factory::negate(RangeSet What) {
if (What.isEmpty())
return getEmptySet();
const llvm::APSInt SampleValue = What.getMinValue();
const llvm::APSInt &MIN = ValueFactory.getMinValue(SampleValue);
const llvm::APSInt &MAX = ValueFactory.getMaxValue(SampleValue);
ContainerType Result;
Result.reserve(What.size() + (SampleValue == MIN));
const_iterator It = What.begin();
const_iterator End = What.end();
const llvm::APSInt &From = It->From();
const llvm::APSInt &To = It->To();
if (From == MIN) {
if (To == MAX) {
return What;
}
const_iterator Last = std::prev(End);
if (Last->To() == MAX) {
Result.emplace_back(MIN, ValueFactory.getValue(-Last->From()));
End = Last;
} else {
Result.emplace_back(MIN, MIN);
}
if (To != MIN) {
Result.emplace_back(ValueFactory.getValue(-To), MAX);
}
++It;
}
for (; It != End; ++It) {
const llvm::APSInt &NewFrom = ValueFactory.getValue(-It->To());
const llvm::APSInt &NewTo = ValueFactory.getValue(-It->From());
Result.emplace_back(NewFrom, NewTo);
}
llvm::sort(Result);
return makePersistent(std::move(Result));
}
RangeSet RangeSet::Factory::castTo(RangeSet What, APSIntType Ty) {
if (What.isEmpty() || What.getAPSIntType() == Ty)
return What;
const bool IsConversion = What.isUnsigned() != Ty.isUnsigned();
const bool IsTruncation = What.getBitWidth() > Ty.getBitWidth();
const bool IsPromotion = What.getBitWidth() < Ty.getBitWidth();
if (IsTruncation)
return makePersistent(truncateTo(What, Ty));
if (IsConversion && (!IsPromotion || !What.isUnsigned()))
return makePersistent(convertTo(What, Ty));
assert(IsPromotion && "Only promotion operation from unsigneds left.");
return makePersistent(promoteTo(What, Ty));
}
RangeSet RangeSet::Factory::castTo(RangeSet What, QualType T) {
assert(T->isIntegralOrEnumerationType() && "T shall be an integral type.");
return castTo(What, ValueFactory.getAPSIntType(T));
}
RangeSet::ContainerType RangeSet::Factory::truncateTo(RangeSet What,
APSIntType Ty) {
using llvm::APInt;
using llvm::APSInt;
ContainerType Result;
ContainerType Dummy;
uint64_t CastRangeSize = APInt::getMaxValue(Ty.getBitWidth()).getZExtValue();
for (const Range &R : What) {
APSInt FromInt = R.From();
APSInt ToInt = R.To();
uint64_t CurrentRangeSize = (ToInt - FromInt).getZExtValue();
Dummy.clear();
if (CurrentRangeSize >= CastRangeSize) {
Dummy.emplace_back(ValueFactory.getMinValue(Ty),
ValueFactory.getMaxValue(Ty));
Result = std::move(Dummy);
break;
}
Ty.apply(FromInt);
Ty.apply(ToInt);
const APSInt &PersistentFrom = ValueFactory.getValue(FromInt);
const APSInt &PersistentTo = ValueFactory.getValue(ToInt);
if (FromInt > ToInt) {
Dummy.emplace_back(ValueFactory.getMinValue(Ty), PersistentTo);
Dummy.emplace_back(PersistentFrom, ValueFactory.getMaxValue(Ty));
} else
Dummy.emplace_back(PersistentFrom, PersistentTo);
Result = unite(Result, Dummy);
}
return Result;
}
RangeSet::ContainerType RangeSet::Factory::convertTo(RangeSet What,
APSIntType Ty) {
using llvm::APInt;
using llvm::APSInt;
using Bounds = std::pair<const APSInt &, const APSInt &>;
ContainerType AscendArray;
ContainerType DescendArray;
auto CastRange = [Ty, &VF = ValueFactory](const Range &R) -> Bounds {
APSInt FromInt = R.From();
APSInt ToInt = R.To();
Ty.apply(FromInt);
Ty.apply(ToInt);
return {VF.getValue(FromInt), VF.getValue(ToInt)};
};
APSInt LastConvertedInt = Ty.getMinValue();
const auto *It = What.begin();
const auto *E = What.end();
while (It != E) {
Bounds NewBounds = CastRange(*(It++));
if (NewBounds.first < LastConvertedInt) {
DescendArray.emplace_back(NewBounds.first, NewBounds.second);
break;
}
if (NewBounds.first > NewBounds.second) {
DescendArray.emplace_back(ValueFactory.getMinValue(Ty), NewBounds.second);
AscendArray.emplace_back(NewBounds.first, ValueFactory.getMaxValue(Ty));
} else
AscendArray.emplace_back(NewBounds.first, NewBounds.second);
LastConvertedInt = NewBounds.first;
}
while (It != E) {
Bounds NewBounds = CastRange(*(It++));
DescendArray.emplace_back(NewBounds.first, NewBounds.second);
}
return unite(AscendArray, DescendArray);
}
RangeSet::ContainerType RangeSet::Factory::promoteTo(RangeSet What,
APSIntType Ty) {
ContainerType Result;
Result.reserve(What.size());
for (const Range &R : What) {
llvm::APSInt FromInt = R.From();
llvm::APSInt ToInt = R.To();
Ty.apply(FromInt);
Ty.apply(ToInt);
Result.emplace_back(ValueFactory.getValue(FromInt),
ValueFactory.getValue(ToInt));
}
return Result;
}
RangeSet RangeSet::Factory::deletePoint(RangeSet From,
const llvm::APSInt &Point) {
if (!From.contains(Point))
return From;
llvm::APSInt Upper = Point;
llvm::APSInt Lower = Point;
++Upper;
--Lower;
return intersect(From, Upper, Lower);
}
LLVM_DUMP_METHOD void Range::dump(raw_ostream &OS) const {
OS << '[' << toString(From(), 10) << ", " << toString(To(), 10) << ']';
}
LLVM_DUMP_METHOD void Range::dump() const { dump(llvm::errs()); }
LLVM_DUMP_METHOD void RangeSet::dump(raw_ostream &OS) const {
OS << "{ ";
llvm::interleaveComma(*this, OS, [&OS](const Range &R) { R.dump(OS); });
OS << " }";
}
LLVM_DUMP_METHOD void RangeSet::dump() const { dump(llvm::errs()); }
REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(SymbolSet, SymbolRef)
namespace {
class EquivalenceClass;
}
REGISTER_MAP_WITH_PROGRAMSTATE(ClassMap, SymbolRef, EquivalenceClass)
REGISTER_MAP_WITH_PROGRAMSTATE(ClassMembers, EquivalenceClass, SymbolSet)
REGISTER_MAP_WITH_PROGRAMSTATE(ConstraintRange, EquivalenceClass, RangeSet)
REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(ClassSet, EquivalenceClass)
REGISTER_MAP_WITH_PROGRAMSTATE(DisequalityMap, EquivalenceClass, ClassSet)
namespace {
class EquivalenceClass : public llvm::FoldingSetNode {
public:
LLVM_NODISCARD static inline EquivalenceClass find(ProgramStateRef State,
SymbolRef Sym);
LLVM_NODISCARD static inline ProgramStateRef merge(RangeSet::Factory &F,
ProgramStateRef State,
SymbolRef First,
SymbolRef Second);
LLVM_NODISCARD inline ProgramStateRef
merge(RangeSet::Factory &F, ProgramStateRef State, EquivalenceClass Other);
LLVM_NODISCARD inline SymbolSet getClassMembers(ProgramStateRef State) const;
LLVM_NODISCARD inline bool isTrivial(ProgramStateRef State) const;
LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State,
SymbolReaper &Reaper) const;
LLVM_NODISCARD static inline ProgramStateRef
markDisequal(RangeSet::Factory &F, ProgramStateRef State, SymbolRef First,
SymbolRef Second);
LLVM_NODISCARD static inline ProgramStateRef
markDisequal(RangeSet::Factory &F, ProgramStateRef State,
EquivalenceClass First, EquivalenceClass Second);
LLVM_NODISCARD inline ProgramStateRef
markDisequal(RangeSet::Factory &F, ProgramStateRef State,
EquivalenceClass Other) const;
LLVM_NODISCARD static inline ClassSet
getDisequalClasses(ProgramStateRef State, SymbolRef Sym);
LLVM_NODISCARD inline ClassSet
getDisequalClasses(ProgramStateRef State) const;
LLVM_NODISCARD inline ClassSet
getDisequalClasses(DisequalityMapTy Map, ClassSet::Factory &Factory) const;
LLVM_NODISCARD static inline Optional<bool> areEqual(ProgramStateRef State,
EquivalenceClass First,
EquivalenceClass Second);
LLVM_NODISCARD static inline Optional<bool>
areEqual(ProgramStateRef State, SymbolRef First, SymbolRef Second);
LLVM_NODISCARD ProgramStateRef removeMember(ProgramStateRef State,
const SymbolRef Old);
LLVM_NODISCARD static inline ProgramStateRef simplify(SValBuilder &SVB,
RangeSet::Factory &F,
ProgramStateRef State,
EquivalenceClass Class);
void dumpToStream(ProgramStateRef State, raw_ostream &os) const;
LLVM_DUMP_METHOD void dump(ProgramStateRef State) const {
dumpToStream(State, llvm::errs());
}
LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool
isClassDataConsistent(ProgramStateRef State);
LLVM_NODISCARD QualType getType() const {
return getRepresentativeSymbol()->getType();
}
EquivalenceClass() = delete;
EquivalenceClass(const EquivalenceClass &) = default;
EquivalenceClass &operator=(const EquivalenceClass &) = delete;
EquivalenceClass(EquivalenceClass &&) = default;
EquivalenceClass &operator=(EquivalenceClass &&) = delete;
bool operator==(const EquivalenceClass &Other) const {
return ID == Other.ID;
}
bool operator<(const EquivalenceClass &Other) const { return ID < Other.ID; }
bool operator!=(const EquivalenceClass &Other) const {
return !operator==(Other);
}
static void Profile(llvm::FoldingSetNodeID &ID, uintptr_t CID) {
ID.AddInteger(CID);
}
void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, this->ID); }
private:
EquivalenceClass(SymbolRef Sym)
: ID(reinterpret_cast<uintptr_t>(Sym)) {}
SymbolRef getRepresentativeSymbol() const {
return reinterpret_cast<SymbolRef>(ID);
}
static inline SymbolSet::Factory &getMembersFactory(ProgramStateRef State);
inline ProgramStateRef mergeImpl(RangeSet::Factory &F, ProgramStateRef State,
SymbolSet Members, EquivalenceClass Other,
SymbolSet OtherMembers);
static inline bool
addToDisequalityInfo(DisequalityMapTy &Info, ConstraintRangeTy &Constraints,
RangeSet::Factory &F, ProgramStateRef State,
EquivalenceClass First, EquivalenceClass Second);
uintptr_t ID;
};
LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED bool
areFeasible(ConstraintRangeTy Constraints) {
return llvm::none_of(
Constraints,
[](const std::pair<EquivalenceClass, RangeSet> &ClassConstraint) {
return ClassConstraint.second.isEmpty();
});
}
LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
EquivalenceClass Class) {
return State->get<ConstraintRange>(Class);
}
LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
SymbolRef Sym) {
return getConstraint(State, EquivalenceClass::find(State, Sym));
}
LLVM_NODISCARD ProgramStateRef setConstraint(ProgramStateRef State,
EquivalenceClass Class,
RangeSet Constraint) {
return State->set<ConstraintRange>(Class, Constraint);
}
LLVM_NODISCARD ProgramStateRef setConstraints(ProgramStateRef State,
ConstraintRangeTy Constraints) {
return State->set<ConstraintRange>(Constraints);
}
Optional<bool> meansEquality(const SymSymExpr *Sym) {
switch (Sym->getOpcode()) {
case BO_Sub:
return false;
case BO_EQ:
return true;
case BO_NE:
return false;
default:
return llvm::None;
}
}
template <class SecondTy, class... RestTy>
LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
SecondTy Second, RestTy... Tail);
template <class... RangeTy> struct IntersectionTraits;
template <class... TailTy> struct IntersectionTraits<RangeSet, TailTy...> {
using Type = RangeSet;
};
template <> struct IntersectionTraits<> {
using Type = Optional<RangeSet>;
};
template <class OptionalOrPointer, class... TailTy>
struct IntersectionTraits<OptionalOrPointer, TailTy...> {
using Type = typename IntersectionTraits<TailTy...>::Type;
};
template <class EndTy>
LLVM_NODISCARD inline EndTy intersect(RangeSet::Factory &F, EndTy End) {
return End;
}
LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED inline Optional<RangeSet>
intersect(RangeSet::Factory &F, const RangeSet *End) {
if (End) {
return *End;
}
return llvm::None;
}
template <class... RestTy>
LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
RangeSet Second, RestTy... Tail) {
return intersect(F, F.intersect(Head, Second), Tail...);
}
template <class SecondTy, class... RestTy>
LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
SecondTy Second, RestTy... Tail) {
if (Second) {
return intersect(F, Head, *Second, Tail...);
}
return intersect(F, Head, Tail...);
}
template <class HeadTy, class SecondTy, class... RestTy>
LLVM_NODISCARD inline
typename IntersectionTraits<HeadTy, SecondTy, RestTy...>::Type
intersect(RangeSet::Factory &F, HeadTy Head, SecondTy Second,
RestTy... Tail) {
if (Head) {
return intersect(F, *Head, Second, Tail...);
}
return intersect(F, Second, Tail...);
}
class SymbolicRangeInferrer
: public SymExprVisitor<SymbolicRangeInferrer, RangeSet> {
public:
template <class SourceType>
static RangeSet inferRange(RangeSet::Factory &F, ProgramStateRef State,
SourceType Origin) {
SymbolicRangeInferrer Inferrer(F, State);
return Inferrer.infer(Origin);
}
RangeSet VisitSymExpr(SymbolRef Sym) {
if (Optional<RangeSet> RS = getRangeForNegatedSym(Sym))
return *RS;
return infer(Sym->getType());
}
RangeSet VisitUnarySymExpr(const UnarySymExpr *USE) {
if (Optional<RangeSet> RS = getRangeForNegatedUnarySym(USE))
return *RS;
return infer(USE->getType());
}
RangeSet VisitSymIntExpr(const SymIntExpr *Sym) {
return VisitBinaryOperator(Sym);
}
RangeSet VisitIntSymExpr(const IntSymExpr *Sym) {
return VisitBinaryOperator(Sym);
}
RangeSet VisitSymSymExpr(const SymSymExpr *SSE) {
return intersect(
RangeFactory,
getRangeForNegatedSymSym(SSE),
getRangeForComparisonSymbol(SSE),
getRangeForEqualities(SSE),
VisitBinaryOperator(SSE));
}
private:
SymbolicRangeInferrer(RangeSet::Factory &F, ProgramStateRef S)
: ValueFactory(F.getValueFactory()), RangeFactory(F), State(S) {}
RangeSet inferAs(const llvm::APSInt &Val, QualType) {
return {RangeFactory, Val};
}
RangeSet inferAs(SymbolRef Sym, QualType DestType) {
QualType ActualType = Sym->getType();
if (ActualType->isIntegralOrEnumerationType() ||
Loc::isLocType(ActualType)) {
return infer(Sym);
}
return infer(DestType);
}
RangeSet infer(SymbolRef Sym) {
return intersect(RangeFactory,
getConstraint(State, Sym),
Visit(Sym));
}
RangeSet infer(EquivalenceClass Class) {
if (const RangeSet *AssociatedConstraint = getConstraint(State, Class))
return *AssociatedConstraint;
return infer(Class.getType());
}
RangeSet infer(QualType T) {
RangeSet Result(RangeFactory, ValueFactory.getMinValue(T),
ValueFactory.getMaxValue(T));
if (T->isReferenceType())
return assumeNonZero(Result, T);
return Result;
}
template <class BinarySymExprTy>
RangeSet VisitBinaryOperator(const BinarySymExprTy *Sym) {
QualType ResultType = Sym->getType();
return VisitBinaryOperator(inferAs(Sym->getLHS(), ResultType),
Sym->getOpcode(),
inferAs(Sym->getRHS(), ResultType), ResultType);
}
RangeSet VisitBinaryOperator(RangeSet LHS, BinaryOperator::Opcode Op,
RangeSet RHS, QualType T) {
switch (Op) {
case BO_Or:
return VisitBinaryOperator<BO_Or>(LHS, RHS, T);
case BO_And:
return VisitBinaryOperator<BO_And>(LHS, RHS, T);
case BO_Rem:
return VisitBinaryOperator<BO_Rem>(LHS, RHS, T);
default:
return infer(T);
}
}
static Range fillGaps(RangeSet Origin) {
assert(!Origin.isEmpty());
return {Origin.getMinValue(), Origin.getMaxValue()};
}
llvm::Optional<Range> convert(const Range &Origin, APSIntType To) {
if (To.testInRange(Origin.From(), false) != APSIntType::RTR_Within ||
To.testInRange(Origin.To(), false) != APSIntType::RTR_Within) {
return llvm::None;
}
return Range(ValueFactory.Convert(To, Origin.From()),
ValueFactory.Convert(To, Origin.To()));
}
template <BinaryOperator::Opcode Op>
RangeSet VisitBinaryOperator(RangeSet LHS, RangeSet RHS, QualType T) {
if (LHS.isEmpty() || RHS.isEmpty()) {
return RangeFactory.getEmptySet();
}
Range CoarseLHS = fillGaps(LHS);
Range CoarseRHS = fillGaps(RHS);
APSIntType ResultType = ValueFactory.getAPSIntType(T);
auto ConvertedCoarseLHS = convert(CoarseLHS, ResultType);
auto ConvertedCoarseRHS = convert(CoarseRHS, ResultType);
if (!ConvertedCoarseLHS || !ConvertedCoarseRHS) {
return infer(T);
}
return VisitBinaryOperator<Op>(*ConvertedCoarseLHS, *ConvertedCoarseRHS, T);
}
template <BinaryOperator::Opcode Op>
RangeSet VisitBinaryOperator(Range LHS, Range RHS, QualType T) {
return infer(T);
}
Range getSymmetricalRange(Range Origin, QualType T) {
APSIntType RangeType = ValueFactory.getAPSIntType(T);
if (RangeType.isUnsigned()) {
return Range(ValueFactory.getMinValue(RangeType), Origin.To());
}
if (Origin.From().isMinSignedValue()) {
return {ValueFactory.getMinValue(RangeType),
ValueFactory.getMaxValue(RangeType)};
}
llvm::APSInt AbsMax = std::max(-Origin.From(), Origin.To());
return {ValueFactory.getValue(-AbsMax), ValueFactory.getValue(AbsMax)};
}
RangeSet assumeNonZero(RangeSet Domain, QualType T) {
APSIntType IntType = ValueFactory.getAPSIntType(T);
return RangeFactory.deletePoint(Domain, IntType.getZeroValue());
}
template <typename ProduceNegatedSymFunc>
Optional<RangeSet> getRangeForNegatedExpr(ProduceNegatedSymFunc F,
QualType T) {
if (!T->isUnsignedIntegerOrEnumerationType() &&
!T->isSignedIntegerOrEnumerationType())
return llvm::None;
if (SymbolRef NegatedSym = F())
if (const RangeSet *NegatedRange = getConstraint(State, NegatedSym))
return RangeFactory.negate(*NegatedRange);
return llvm::None;
}
Optional<RangeSet> getRangeForNegatedUnarySym(const UnarySymExpr *USE) {
return getRangeForNegatedExpr(
[USE]() -> SymbolRef {
if (USE->getOpcode() == UO_Minus)
return USE->getOperand();
return nullptr;
},
USE->getType());
}
Optional<RangeSet> getRangeForNegatedSymSym(const SymSymExpr *SSE) {
return getRangeForNegatedExpr(
[SSE, State = this->State]() -> SymbolRef {
if (SSE->getOpcode() == BO_Sub)
return State->getSymbolManager().getSymSymExpr(
SSE->getRHS(), BO_Sub, SSE->getLHS(), SSE->getType());
return nullptr;
},
SSE->getType());
}
Optional<RangeSet> getRangeForNegatedSym(SymbolRef Sym) {
return getRangeForNegatedExpr(
[Sym, State = this->State]() {
return State->getSymbolManager().getUnarySymExpr(Sym, UO_Minus,
Sym->getType());
},
Sym->getType());
}
Optional<RangeSet> getRangeForComparisonSymbol(const SymSymExpr *SSE) {
const BinaryOperatorKind CurrentOP = SSE->getOpcode();
if (!BinaryOperator::isComparisonOp(CurrentOP) || (CurrentOP == BO_Cmp))
return llvm::None;
static const OperatorRelationsTable CmpOpTable{};
const SymExpr *LHS = SSE->getLHS();
const SymExpr *RHS = SSE->getRHS();
QualType T = SSE->getType();
SymbolManager &SymMgr = State->getSymbolManager();
BinaryOperatorKind LastQueriedOpToUnknown = CurrentOP;
for (size_t i = 0; i < CmpOpTable.getCmpOpCount(); ++i) {
BinaryOperatorKind QueriedOP = OperatorRelationsTable::getOpFromIndex(i);
const SymSymExpr *SymSym = SymMgr.getSymSymExpr(LHS, QueriedOP, RHS, T);
const RangeSet *QueriedRangeSet = getConstraint(State, SymSym);
if (!QueriedRangeSet) {
const BinaryOperatorKind ROP =
BinaryOperator::reverseComparisonOp(QueriedOP);
SymSym = SymMgr.getSymSymExpr(RHS, ROP, LHS, T);
QueriedRangeSet = getConstraint(State, SymSym);
}
if (!QueriedRangeSet || QueriedRangeSet->isEmpty())
continue;
const llvm::APSInt *ConcreteValue = QueriedRangeSet->getConcreteValue();
const bool isInFalseBranch =
ConcreteValue ? (*ConcreteValue == 0) : false;
if (isInFalseBranch)
QueriedOP = BinaryOperator::negateComparisonOp(QueriedOP);
OperatorRelationsTable::TriStateKind BranchState =
CmpOpTable.getCmpOpState(CurrentOP, QueriedOP);
if (BranchState == OperatorRelationsTable::Unknown) {
if (LastQueriedOpToUnknown != CurrentOP &&
LastQueriedOpToUnknown != QueriedOP) {
BranchState = CmpOpTable.getCmpOpStateForUnknownX2(CurrentOP);
} else {
LastQueriedOpToUnknown = QueriedOP;
continue;
}
}
return (BranchState == OperatorRelationsTable::True) ? getTrueRange(T)
: getFalseRange(T);
}
return llvm::None;
}
Optional<RangeSet> getRangeForEqualities(const SymSymExpr *Sym) {
Optional<bool> Equality = meansEquality(Sym);
if (!Equality)
return llvm::None;
if (Optional<bool> AreEqual =
EquivalenceClass::areEqual(State, Sym->getLHS(), Sym->getRHS())) {
if (*AreEqual == *Equality) {
return getTrueRange(Sym->getType());
}
return getFalseRange(Sym->getType());
}
return llvm::None;
}
RangeSet getTrueRange(QualType T) {
RangeSet TypeRange = infer(T);
return assumeNonZero(TypeRange, T);
}
RangeSet getFalseRange(QualType T) {
const llvm::APSInt &Zero = ValueFactory.getValue(0, T);
return RangeSet(RangeFactory, Zero);
}
BasicValueFactory &ValueFactory;
RangeSet::Factory &RangeFactory;
ProgramStateRef State;
};
template <>
RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Or>(Range LHS, Range RHS,
QualType T) {
APSIntType ResultType = ValueFactory.getAPSIntType(T);
llvm::APSInt Zero = ResultType.getZeroValue();
bool IsLHSPositiveOrZero = LHS.From() >= Zero;
bool IsRHSPositiveOrZero = RHS.From() >= Zero;
bool IsLHSNegative = LHS.To() < Zero;
bool IsRHSNegative = RHS.To() < Zero;
if ((IsLHSPositiveOrZero && IsRHSPositiveOrZero) ||
(IsLHSNegative && IsRHSNegative)) {
const llvm::APSInt &Min = std::max(LHS.From(), RHS.From());
const llvm::APSInt &Max = IsLHSNegative
? ValueFactory.getValue(--Zero)
: ValueFactory.getMaxValue(ResultType);
return {RangeFactory, ValueFactory.getValue(Min), Max};
}
if (IsLHSNegative || IsRHSNegative) {
return {RangeFactory, ValueFactory.getMinValue(ResultType),
ValueFactory.getValue(--Zero)};
}
RangeSet DefaultRange = infer(T);
if (!LHS.Includes(Zero) || !RHS.Includes(Zero)) {
return assumeNonZero(DefaultRange, T);
}
return DefaultRange;
}
template <>
RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_And>(Range LHS,
Range RHS,
QualType T) {
APSIntType ResultType = ValueFactory.getAPSIntType(T);
llvm::APSInt Zero = ResultType.getZeroValue();
bool IsLHSPositiveOrZero = LHS.From() >= Zero;
bool IsRHSPositiveOrZero = RHS.From() >= Zero;
bool IsLHSNegative = LHS.To() < Zero;
bool IsRHSNegative = RHS.To() < Zero;
if ((IsLHSPositiveOrZero && IsRHSPositiveOrZero) ||
(IsLHSNegative && IsRHSNegative)) {
const llvm::APSInt &Max = std::min(LHS.To(), RHS.To());
const llvm::APSInt &Min = IsLHSNegative
? ValueFactory.getMinValue(ResultType)
: ValueFactory.getValue(Zero);
return {RangeFactory, Min, Max};
}
if (IsLHSPositiveOrZero || IsRHSPositiveOrZero) {
const llvm::APSInt &Max = IsLHSPositiveOrZero ? LHS.To() : RHS.To();
return {RangeFactory, ValueFactory.getValue(Zero),
ValueFactory.getValue(Max)};
}
return infer(T);
}
template <>
RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Rem>(Range LHS,
Range RHS,
QualType T) {
llvm::APSInt Zero = ValueFactory.getAPSIntType(T).getZeroValue();
Range ConservativeRange = getSymmetricalRange(RHS, T);
llvm::APSInt Max = ConservativeRange.To();
llvm::APSInt Min = ConservativeRange.From();
if (Max == Zero) {
return RangeFactory.getEmptySet();
}
if (Min.isSigned()) {
++Min;
}
--Max;
bool IsLHSPositiveOrZero = LHS.From() >= Zero;
bool IsRHSPositiveOrZero = RHS.From() >= Zero;
if (IsLHSPositiveOrZero && IsRHSPositiveOrZero) {
Max = std::min(LHS.To(), Max);
Min = LHS.To() < RHS.From() ? LHS.From() : Zero;
}
return {RangeFactory, ValueFactory.getValue(Min), ValueFactory.getValue(Max)};
}
class RangeConstraintManager : public RangedConstraintManager {
public:
RangeConstraintManager(ExprEngine *EE, SValBuilder &SVB)
: RangedConstraintManager(EE, SVB), F(getBasicVals()) {}
bool haveEqualConstraints(ProgramStateRef S1,
ProgramStateRef S2) const override {
return S1->get<ConstraintRange>() == S2->get<ConstraintRange>() &&
S1->get<ClassMap>() == S2->get<ClassMap>();
}
bool canReasonAbout(SVal X) const override;
ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override;
const llvm::APSInt *getSymVal(ProgramStateRef State,
SymbolRef Sym) const override;
ProgramStateRef removeDeadBindings(ProgramStateRef State,
SymbolReaper &SymReaper) override;
void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n",
unsigned int Space = 0, bool IsDot = false) const override;
void printValue(raw_ostream &Out, ProgramStateRef State,
SymbolRef Sym) override;
void printConstraints(raw_ostream &Out, ProgramStateRef State,
const char *NL = "\n", unsigned int Space = 0,
bool IsDot = false) const;
void printEquivalenceClasses(raw_ostream &Out, ProgramStateRef State,
const char *NL = "\n", unsigned int Space = 0,
bool IsDot = false) const;
void printDisequalities(raw_ostream &Out, ProgramStateRef State,
const char *NL = "\n", unsigned int Space = 0,
bool IsDot = false) const;
ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) override;
ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) override;
ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) override;
ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) override;
ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) override;
ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) override;
ProgramStateRef assumeSymWithinInclusiveRange(
ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
ProgramStateRef assumeSymOutsideInclusiveRange(
ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
private:
RangeSet::Factory F;
RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
RangeSet getRange(ProgramStateRef State, EquivalenceClass Class);
ProgramStateRef setRange(ProgramStateRef State, SymbolRef Sym,
RangeSet Range);
ProgramStateRef setRange(ProgramStateRef State, EquivalenceClass Class,
RangeSet Range);
RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment);
RangeSet getSymGTRange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment);
RangeSet getSymLERange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment);
RangeSet getSymLERange(llvm::function_ref<RangeSet()> RS,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment);
RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment);
};
template <class Derived> class ConstraintAssignorBase {
public:
using Const = const llvm::APSInt &;
#define DISPATCH(CLASS) return assign##CLASS##Impl(cast<CLASS>(Sym), Constraint)
#define ASSIGN(CLASS, TO, SYM, CONSTRAINT) \
if (!static_cast<Derived *>(this)->assign##CLASS##To##TO(SYM, CONSTRAINT)) \
return false
void assign(SymbolRef Sym, RangeSet Constraint) {
assignImpl(Sym, Constraint);
}
bool assignImpl(SymbolRef Sym, RangeSet Constraint) {
switch (Sym->getKind()) {
#define SYMBOL(Id, Parent) \
case SymExpr::Id##Kind: \
DISPATCH(Id);
#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def"
}
llvm_unreachable("Unknown SymExpr kind!");
}
#define DEFAULT_ASSIGN(Id) \
bool assign##Id##To##RangeSet(const Id *Sym, RangeSet Constraint) { \
return true; \
} \
bool assign##Id##To##Range(const Id *Sym, Range Constraint) { return true; } \
bool assign##Id##To##Const(const Id *Sym, Const Constraint) { return true; }
#define CONSTRAINT_DISPATCH(Id) \
if (const llvm::APSInt *Const = Constraint.getConcreteValue()) { \
ASSIGN(Id, Const, Sym, *Const); \
} \
if (Constraint.size() == 1) { \
ASSIGN(Id, Range, Sym, *Constraint.begin()); \
} \
ASSIGN(Id, RangeSet, Sym, Constraint)
#define SYMBOL(Id, Parent) \
bool assign##Id##Impl(const Id *Sym, RangeSet Constraint) { \
CONSTRAINT_DISPATCH(Id); \
DISPATCH(Parent); \
} \
DEFAULT_ASSIGN(Id)
#define ABSTRACT_SYMBOL(Id, Parent) SYMBOL(Id, Parent)
#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def"
bool assignSymExprImpl(const SymExpr *Sym, RangeSet Constraint) {
CONSTRAINT_DISPATCH(SymExpr);
return true;
}
DEFAULT_ASSIGN(SymExpr);
#undef DISPATCH
#undef CONSTRAINT_DISPATCH
#undef DEFAULT_ASSIGN
#undef ASSIGN
};
class ConstraintAssignor : public ConstraintAssignorBase<ConstraintAssignor> {
public:
template <class ClassOrSymbol>
LLVM_NODISCARD static ProgramStateRef
assign(ProgramStateRef State, SValBuilder &Builder, RangeSet::Factory &F,
ClassOrSymbol CoS, RangeSet NewConstraint) {
if (!State || NewConstraint.isEmpty())
return nullptr;
ConstraintAssignor Assignor{State, Builder, F};
return Assignor.assign(CoS, NewConstraint);
}
template <typename SymT>
bool handleRemainderOp(const SymT *Sym, RangeSet Constraint) {
if (Sym->getOpcode() != BO_Rem)
return true;
if (!Constraint.containsZero()) {
SVal SymSVal = Builder.makeSymbolVal(Sym->getLHS());
if (auto NonLocSymSVal = SymSVal.getAs<nonloc::SymbolVal>()) {
State = State->assume(*NonLocSymSVal, true);
if (!State)
return false;
}
}
return true;
}
inline bool assignSymExprToConst(const SymExpr *Sym, Const Constraint);
inline bool assignSymIntExprToRangeSet(const SymIntExpr *Sym,
RangeSet Constraint) {
return handleRemainderOp(Sym, Constraint);
}
inline bool assignSymSymExprToRangeSet(const SymSymExpr *Sym,
RangeSet Constraint);
private:
ConstraintAssignor(ProgramStateRef State, SValBuilder &Builder,
RangeSet::Factory &F)
: State(State), Builder(Builder), RangeFactory(F) {}
using Base = ConstraintAssignorBase<ConstraintAssignor>;
LLVM_NODISCARD ProgramStateRef assign(SymbolRef Sym, RangeSet NewConstraint) {
State = assign(EquivalenceClass::find(State, Sym), NewConstraint);
if (!State)
return nullptr;
Base::assign(Sym, NewConstraint);
return State;
}
LLVM_NODISCARD ProgramStateRef assign(EquivalenceClass Class,
RangeSet NewConstraint) {
if (const llvm::APSInt *Point = NewConstraint.getConcreteValue()) {
ConstraintRangeTy Constraints = State->get<ConstraintRange>();
ConstraintRangeTy::Factory &CF = State->get_context<ConstraintRange>();
Constraints = CF.add(Constraints, Class, NewConstraint);
for (EquivalenceClass DisequalClass : Class.getDisequalClasses(State)) {
RangeSet UpdatedConstraint = SymbolicRangeInferrer::inferRange(
RangeFactory, State, DisequalClass);
UpdatedConstraint = RangeFactory.deletePoint(UpdatedConstraint, *Point);
if (UpdatedConstraint.isEmpty())
return nullptr;
Constraints = CF.add(Constraints, DisequalClass, UpdatedConstraint);
}
assert(areFeasible(Constraints) && "Constraint manager shouldn't produce "
"a state with infeasible constraints");
return setConstraints(State, Constraints);
}
return setConstraint(State, Class, NewConstraint);
}
ProgramStateRef trackDisequality(ProgramStateRef State, SymbolRef LHS,
SymbolRef RHS) {
return EquivalenceClass::markDisequal(RangeFactory, State, LHS, RHS);
}
ProgramStateRef trackEquality(ProgramStateRef State, SymbolRef LHS,
SymbolRef RHS) {
return EquivalenceClass::merge(RangeFactory, State, LHS, RHS);
}
LLVM_NODISCARD Optional<bool> interpreteAsBool(RangeSet Constraint) {
assert(!Constraint.isEmpty() && "Empty ranges shouldn't get here");
if (Constraint.getConcreteValue())
return !Constraint.getConcreteValue()->isZero();
if (!Constraint.containsZero())
return true;
return llvm::None;
}
ProgramStateRef State;
SValBuilder &Builder;
RangeSet::Factory &RangeFactory;
};
bool ConstraintAssignor::assignSymExprToConst(const SymExpr *Sym,
const llvm::APSInt &Constraint) {
llvm::SmallSet<EquivalenceClass, 4> SimplifiedClasses;
ClassMembersTy Members = State->get<ClassMembers>();
for (std::pair<EquivalenceClass, SymbolSet> ClassToSymbolSet : Members) {
EquivalenceClass Class = ClassToSymbolSet.first;
State = EquivalenceClass::simplify(Builder, RangeFactory, State, Class);
if (!State)
return false;
SimplifiedClasses.insert(Class);
}
ConstraintRangeTy Constraints = State->get<ConstraintRange>();
for (std::pair<EquivalenceClass, RangeSet> ClassConstraint : Constraints) {
EquivalenceClass Class = ClassConstraint.first;
if (SimplifiedClasses.count(Class)) continue;
State = EquivalenceClass::simplify(Builder, RangeFactory, State, Class);
if (!State)
return false;
}
DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>();
for (std::pair<EquivalenceClass, ClassSet> DisequalityEntry :
DisequalityInfo) {
EquivalenceClass Class = DisequalityEntry.first;
ClassSet DisequalClasses = DisequalityEntry.second;
State = EquivalenceClass::simplify(Builder, RangeFactory, State, Class);
if (!State)
return false;
}
return true;
}
bool ConstraintAssignor::assignSymSymExprToRangeSet(const SymSymExpr *Sym,
RangeSet Constraint) {
if (!handleRemainderOp(Sym, Constraint))
return false;
Optional<bool> ConstraintAsBool = interpreteAsBool(Constraint);
if (!ConstraintAsBool)
return true;
if (Optional<bool> Equality = meansEquality(Sym)) {
if (*Equality == *ConstraintAsBool) {
State = trackEquality(State, Sym->getLHS(), Sym->getRHS());
} else {
State = trackDisequality(State, Sym->getLHS(), Sym->getRHS());
}
if (!State)
return false;
}
return true;
}
}
std::unique_ptr<ConstraintManager>
ento::CreateRangeConstraintManager(ProgramStateManager &StMgr,
ExprEngine *Eng) {
return std::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder());
}
ConstraintMap ento::getConstraintMap(ProgramStateRef State) {
ConstraintMap::Factory &F = State->get_context<ConstraintMap>();
ConstraintMap Result = F.getEmptyMap();
ConstraintRangeTy Constraints = State->get<ConstraintRange>();
for (std::pair<EquivalenceClass, RangeSet> ClassConstraint : Constraints) {
EquivalenceClass Class = ClassConstraint.first;
SymbolSet ClassMembers = Class.getClassMembers(State);
assert(!ClassMembers.isEmpty() &&
"Class must always have at least one member!");
SymbolRef Representative = *ClassMembers.begin();
Result = F.add(Result, Representative, ClassConstraint.second);
}
return Result;
}
LLVM_DUMP_METHOD void EquivalenceClass::dumpToStream(ProgramStateRef State,
raw_ostream &os) const {
SymbolSet ClassMembers = getClassMembers(State);
for (const SymbolRef &MemberSym : ClassMembers) {
MemberSym->dump();
os << "\n";
}
}
inline EquivalenceClass EquivalenceClass::find(ProgramStateRef State,
SymbolRef Sym) {
assert(State && "State should not be null");
assert(Sym && "Symbol should not be null");
if (const EquivalenceClass *NontrivialClass = State->get<ClassMap>(Sym))
return *NontrivialClass;
return Sym;
}
inline ProgramStateRef EquivalenceClass::merge(RangeSet::Factory &F,
ProgramStateRef State,
SymbolRef First,
SymbolRef Second) {
EquivalenceClass FirstClass = find(State, First);
EquivalenceClass SecondClass = find(State, Second);
return FirstClass.merge(F, State, SecondClass);
}
inline ProgramStateRef EquivalenceClass::merge(RangeSet::Factory &F,
ProgramStateRef State,
EquivalenceClass Other) {
if (*this == Other)
return State;
if (getType() != Other.getType())
return State;
SymbolSet Members = getClassMembers(State);
SymbolSet OtherMembers = Other.getClassMembers(State);
if (Members.getHeight() >= OtherMembers.getHeight()) {
return mergeImpl(F, State, Members, Other, OtherMembers);
} else {
return Other.mergeImpl(F, State, OtherMembers, *this, Members);
}
}
inline ProgramStateRef
EquivalenceClass::mergeImpl(RangeSet::Factory &RangeFactory,
ProgramStateRef State, SymbolSet MyMembers,
EquivalenceClass Other, SymbolSet OtherMembers) {
ConstraintRangeTy Constraints = State->get<ConstraintRange>();
ConstraintRangeTy::Factory &CRF = State->get_context<ConstraintRange>();
if (Optional<RangeSet> NewClassConstraint =
intersect(RangeFactory, getConstraint(State, *this),
getConstraint(State, Other))) {
if (NewClassConstraint->isEmpty())
return nullptr;
Constraints = CRF.remove(Constraints, Other);
Constraints = CRF.add(Constraints, *this, *NewClassConstraint);
assert(areFeasible(Constraints) && "Constraint manager shouldn't produce "
"a state with infeasible constraints");
State = State->set<ConstraintRange>(Constraints);
}
ClassMapTy Classes = State->get<ClassMap>();
ClassMapTy::Factory &CMF = State->get_context<ClassMap>();
ClassMembersTy Members = State->get<ClassMembers>();
ClassMembersTy::Factory &MF = State->get_context<ClassMembers>();
DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>();
DisequalityMapTy::Factory &DF = State->get_context<DisequalityMap>();
ClassSet::Factory &CF = State->get_context<ClassSet>();
SymbolSet::Factory &F = getMembersFactory(State);
SymbolSet NewClassMembers = MyMembers;
for (SymbolRef Sym : OtherMembers) {
NewClassMembers = F.add(NewClassMembers, Sym);
Classes = CMF.add(Classes, Sym, *this);
}
Members = MF.remove(Members, Other);
Members = MF.add(Members, *this, NewClassMembers);
ClassSet DisequalToOther = Other.getDisequalClasses(DisequalityInfo, CF);
if (DisequalToOther.contains(*this))
return nullptr;
if (!DisequalToOther.isEmpty()) {
ClassSet DisequalToThis = getDisequalClasses(DisequalityInfo, CF);
DisequalityInfo = DF.remove(DisequalityInfo, Other);
for (EquivalenceClass DisequalClass : DisequalToOther) {
DisequalToThis = CF.add(DisequalToThis, DisequalClass);
ClassSet OriginalSetLinkedToOther =
*DisequalityInfo.lookup(DisequalClass);
ClassSet NewSet = CF.remove(OriginalSetLinkedToOther, Other);
NewSet = CF.add(NewSet, *this);
DisequalityInfo = DF.add(DisequalityInfo, DisequalClass, NewSet);
}
DisequalityInfo = DF.add(DisequalityInfo, *this, DisequalToThis);
State = State->set<DisequalityMap>(DisequalityInfo);
}
State = State->set<ClassMap>(Classes);
State = State->set<ClassMembers>(Members);
return State;
}
inline SymbolSet::Factory &
EquivalenceClass::getMembersFactory(ProgramStateRef State) {
return State->get_context<SymbolSet>();
}
SymbolSet EquivalenceClass::getClassMembers(ProgramStateRef State) const {
if (const SymbolSet *Members = State->get<ClassMembers>(*this))
return *Members;
SymbolSet::Factory &F = getMembersFactory(State);
return F.add(F.getEmptySet(), getRepresentativeSymbol());
}
bool EquivalenceClass::isTrivial(ProgramStateRef State) const {
return State->get<ClassMembers>(*this) == nullptr;
}
bool EquivalenceClass::isTriviallyDead(ProgramStateRef State,
SymbolReaper &Reaper) const {
return isTrivial(State) && Reaper.isDead(getRepresentativeSymbol());
}
inline ProgramStateRef EquivalenceClass::markDisequal(RangeSet::Factory &RF,
ProgramStateRef State,
SymbolRef First,
SymbolRef Second) {
return markDisequal(RF, State, find(State, First), find(State, Second));
}
inline ProgramStateRef EquivalenceClass::markDisequal(RangeSet::Factory &RF,
ProgramStateRef State,
EquivalenceClass First,
EquivalenceClass Second) {
return First.markDisequal(RF, State, Second);
}
inline ProgramStateRef
EquivalenceClass::markDisequal(RangeSet::Factory &RF, ProgramStateRef State,
EquivalenceClass Other) const {
if (*this == Other) {
return nullptr;
}
DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>();
ConstraintRangeTy Constraints = State->get<ConstraintRange>();
if (!addToDisequalityInfo(DisequalityInfo, Constraints, RF, State, *this,
Other) ||
!addToDisequalityInfo(DisequalityInfo, Constraints, RF, State, Other,
*this))
return nullptr;
assert(areFeasible(Constraints) && "Constraint manager shouldn't produce "
"a state with infeasible constraints");
State = State->set<DisequalityMap>(DisequalityInfo);
State = State->set<ConstraintRange>(Constraints);
return State;
}
inline bool EquivalenceClass::addToDisequalityInfo(
DisequalityMapTy &Info, ConstraintRangeTy &Constraints,
RangeSet::Factory &RF, ProgramStateRef State, EquivalenceClass First,
EquivalenceClass Second) {
DisequalityMapTy::Factory &F = State->get_context<DisequalityMap>();
ClassSet::Factory &CF = State->get_context<ClassSet>();
ConstraintRangeTy::Factory &CRF = State->get_context<ConstraintRange>();
const ClassSet *CurrentSet = Info.lookup(First);
ClassSet NewSet = CurrentSet ? *CurrentSet : CF.getEmptySet();
NewSet = CF.add(NewSet, Second);
Info = F.add(Info, First, NewSet);
if (const RangeSet *SecondConstraint = Constraints.lookup(Second))
if (const llvm::APSInt *Point = SecondConstraint->getConcreteValue()) {
RangeSet FirstConstraint = SymbolicRangeInferrer::inferRange(
RF, State, First.getRepresentativeSymbol());
FirstConstraint = RF.deletePoint(FirstConstraint, *Point);
if (FirstConstraint.isEmpty())
return false;
Constraints = CRF.add(Constraints, First, FirstConstraint);
}
return true;
}
inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
SymbolRef FirstSym,
SymbolRef SecondSym) {
return EquivalenceClass::areEqual(State, find(State, FirstSym),
find(State, SecondSym));
}
inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
EquivalenceClass First,
EquivalenceClass Second) {
if (First == Second)
return true;
ClassSet DisequalToFirst = First.getDisequalClasses(State);
if (DisequalToFirst.contains(Second))
return false;
return llvm::None;
}
LLVM_NODISCARD ProgramStateRef
EquivalenceClass::removeMember(ProgramStateRef State, const SymbolRef Old) {
SymbolSet ClsMembers = getClassMembers(State);
assert(ClsMembers.contains(Old));
SymbolSet::Factory &F = getMembersFactory(State);
ClassMembersTy::Factory &EMFactory = State->get_context<ClassMembers>();
ClsMembers = F.remove(ClsMembers, Old);
assert(!ClsMembers.isEmpty() &&
"Class should have had at least two members before member removal");
ClassMembersTy ClassMembersMap = State->get<ClassMembers>();
ClassMembersMap = EMFactory.add(ClassMembersMap, *this, ClsMembers);
State = State->set<ClassMembers>(ClassMembersMap);
ClassMapTy Classes = State->get<ClassMap>();
ClassMapTy::Factory &CMF = State->get_context<ClassMap>();
Classes = CMF.remove(Classes, Old);
State = State->set<ClassMap>(Classes);
return State;
}
LLVM_NODISCARD ProgramStateRef reAssume(ProgramStateRef State,
const RangeSet *Constraint,
SVal TheValue) {
if (!Constraint)
return State;
const auto DefinedVal = TheValue.castAs<DefinedSVal>();
if (Constraint->encodesFalseRange())
return State->assume(DefinedVal, false);
if (Constraint->encodesTrueRange()) {
State = State->assume(DefinedVal, true);
if (!State)
return nullptr;
}
return State->assumeInclusiveRange(DefinedVal, Constraint->getMinValue(),
Constraint->getMaxValue(), true);
}
LLVM_NODISCARD ProgramStateRef
EquivalenceClass::simplify(SValBuilder &SVB, RangeSet::Factory &F,
ProgramStateRef State, EquivalenceClass Class) {
SymbolSet ClassMembers = Class.getClassMembers(State);
for (const SymbolRef &MemberSym : ClassMembers) {
const SVal SimplifiedMemberVal = simplifyToSVal(State, MemberSym);
const SymbolRef SimplifiedMemberSym = SimplifiedMemberVal.getAsSymbol();
if (const auto CI = SimplifiedMemberVal.getAs<nonloc::ConcreteInt>()) {
const llvm::APSInt &SV = CI->getValue();
const RangeSet *ClassConstraint = getConstraint(State, Class);
if (ClassConstraint && !ClassConstraint->contains(SV))
return nullptr;
}
if (SimplifiedMemberSym && MemberSym != SimplifiedMemberSym) {
ProgramStateRef OldState = State;
State = merge(F, State, MemberSym, SimplifiedMemberSym);
if (!State)
return nullptr;
if (OldState == State)
continue;
assert(find(State, MemberSym) == find(State, SimplifiedMemberSym));
State = find(State, MemberSym).removeMember(State, MemberSym);
const RangeSet *ClassConstraint = getConstraint(State, Class);
State = reAssume(State, ClassConstraint, SimplifiedMemberVal);
if (!State)
return nullptr;
}
}
return State;
}
inline ClassSet EquivalenceClass::getDisequalClasses(ProgramStateRef State,
SymbolRef Sym) {
return find(State, Sym).getDisequalClasses(State);
}
inline ClassSet
EquivalenceClass::getDisequalClasses(ProgramStateRef State) const {
return getDisequalClasses(State->get<DisequalityMap>(),
State->get_context<ClassSet>());
}
inline ClassSet
EquivalenceClass::getDisequalClasses(DisequalityMapTy Map,
ClassSet::Factory &Factory) const {
if (const ClassSet *DisequalClasses = Map.lookup(*this))
return *DisequalClasses;
return Factory.getEmptySet();
}
bool EquivalenceClass::isClassDataConsistent(ProgramStateRef State) {
ClassMembersTy Members = State->get<ClassMembers>();
for (std::pair<EquivalenceClass, SymbolSet> ClassMembersPair : Members) {
for (SymbolRef Member : ClassMembersPair.second) {
if (find(State, Member) == ClassMembersPair.first) {
continue;
}
return false;
}
}
DisequalityMapTy Disequalities = State->get<DisequalityMap>();
for (std::pair<EquivalenceClass, ClassSet> DisequalityInfo : Disequalities) {
EquivalenceClass Class = DisequalityInfo.first;
ClassSet DisequalClasses = DisequalityInfo.second;
if (DisequalClasses.isEmpty())
return false;
for (EquivalenceClass DisequalClass : DisequalClasses) {
const ClassSet *DisequalToDisequalClasses =
Disequalities.lookup(DisequalClass);
if (!DisequalToDisequalClasses ||
!DisequalToDisequalClasses->contains(Class))
return false;
}
}
return true;
}
bool RangeConstraintManager::canReasonAbout(SVal X) const {
Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
if (SymVal && SymVal->isExpression()) {
const SymExpr *SE = SymVal->getSymbol();
if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) {
switch (SIE->getOpcode()) {
case BO_And:
case BO_Or:
case BO_Xor:
return false;
case BO_Mul:
case BO_Div:
case BO_Rem:
case BO_Shl:
case BO_Shr:
return false;
default:
return true;
}
}
if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) {
if (BinaryOperator::isEqualityOp(SSE->getOpcode()) ||
BinaryOperator::isRelationalOp(SSE->getOpcode())) {
if (Loc::isLocType(SSE->getLHS()->getType())) {
return Loc::isLocType(SSE->getRHS()->getType());
}
}
}
return false;
}
return true;
}
ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State,
SymbolRef Sym) {
const RangeSet *Ranges = getConstraint(State, Sym);
if (!Ranges)
return ConditionTruthVal();
if (const llvm::APSInt *Value = Ranges->getConcreteValue())
return *Value == 0;
BasicValueFactory &BV = getBasicVals();
APSIntType IntType = BV.getAPSIntType(Sym->getType());
llvm::APSInt Zero = IntType.getZeroValue();
if (!Ranges->contains(Zero))
return false;
return ConditionTruthVal();
}
const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St,
SymbolRef Sym) const {
const RangeSet *T = getConstraint(St, Sym);
return T ? T->getConcreteValue() : nullptr;
}
ProgramStateRef
RangeConstraintManager::removeDeadBindings(ProgramStateRef State,
SymbolReaper &SymReaper) {
ClassMembersTy ClassMembersMap = State->get<ClassMembers>();
ClassMembersTy NewClassMembersMap = ClassMembersMap;
ClassMembersTy::Factory &EMFactory = State->get_context<ClassMembers>();
SymbolSet::Factory &SetFactory = State->get_context<SymbolSet>();
ConstraintRangeTy Constraints = State->get<ConstraintRange>();
ConstraintRangeTy NewConstraints = Constraints;
ConstraintRangeTy::Factory &ConstraintFactory =
State->get_context<ConstraintRange>();
ClassMapTy Map = State->get<ClassMap>();
ClassMapTy NewMap = Map;
ClassMapTy::Factory &ClassFactory = State->get_context<ClassMap>();
DisequalityMapTy Disequalities = State->get<DisequalityMap>();
DisequalityMapTy::Factory &DisequalityFactory =
State->get_context<DisequalityMap>();
ClassSet::Factory &ClassSetFactory = State->get_context<ClassSet>();
bool ClassMapChanged = false;
bool MembersMapChanged = false;
bool ConstraintMapChanged = false;
bool DisequalitiesChanged = false;
auto removeDeadClass = [&](EquivalenceClass Class) {
Constraints = ConstraintFactory.remove(Constraints, Class);
ConstraintMapChanged = true;
ClassSet DisequalClasses =
Class.getDisequalClasses(Disequalities, ClassSetFactory);
if (!DisequalClasses.isEmpty()) {
for (EquivalenceClass DisequalClass : DisequalClasses) {
ClassSet DisequalToDisequalSet =
DisequalClass.getDisequalClasses(Disequalities, ClassSetFactory);
assert(!DisequalToDisequalSet.isEmpty());
ClassSet NewSet = ClassSetFactory.remove(DisequalToDisequalSet, Class);
if (NewSet.isEmpty()) {
Disequalities =
DisequalityFactory.remove(Disequalities, DisequalClass);
} else {
Disequalities =
DisequalityFactory.add(Disequalities, DisequalClass, NewSet);
}
}
Disequalities = DisequalityFactory.remove(Disequalities, Class);
DisequalitiesChanged = true;
}
};
for (std::pair<EquivalenceClass, RangeSet> ClassConstraintPair :
Constraints) {
EquivalenceClass Class = ClassConstraintPair.first;
if (Class.isTriviallyDead(State, SymReaper)) {
removeDeadClass(Class);
}
}
for (std::pair<SymbolRef, EquivalenceClass> SymbolClassPair : Map) {
SymbolRef Sym = SymbolClassPair.first;
if (SymReaper.isDead(Sym)) {
ClassMapChanged = true;
NewMap = ClassFactory.remove(NewMap, Sym);
}
}
for (std::pair<EquivalenceClass, SymbolSet> ClassMembersPair :
ClassMembersMap) {
EquivalenceClass Class = ClassMembersPair.first;
SymbolSet LiveMembers = ClassMembersPair.second;
bool MembersChanged = false;
for (SymbolRef Member : ClassMembersPair.second) {
if (SymReaper.isDead(Member)) {
MembersChanged = true;
LiveMembers = SetFactory.remove(LiveMembers, Member);
}
}
if (!MembersChanged)
continue;
MembersMapChanged = true;
if (LiveMembers.isEmpty()) {
NewClassMembersMap = EMFactory.remove(NewClassMembersMap, Class);
removeDeadClass(Class);
} else {
NewClassMembersMap =
EMFactory.add(NewClassMembersMap, Class, LiveMembers);
}
}
if (ClassMapChanged)
State = State->set<ClassMap>(NewMap);
if (MembersMapChanged)
State = State->set<ClassMembers>(NewClassMembersMap);
if (ConstraintMapChanged)
State = State->set<ConstraintRange>(Constraints);
if (DisequalitiesChanged)
State = State->set<DisequalityMap>(Disequalities);
assert(EquivalenceClass::isClassDataConsistent(State));
return State;
}
RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
SymbolRef Sym) {
return SymbolicRangeInferrer::inferRange(F, State, Sym);
}
ProgramStateRef RangeConstraintManager::setRange(ProgramStateRef State,
SymbolRef Sym,
RangeSet Range) {
return ConstraintAssignor::assign(State, getSValBuilder(), F, Sym, Range);
}
ProgramStateRef
RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
APSIntType AdjustmentType(Adjustment);
if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within)
return St;
llvm::APSInt Point = AdjustmentType.convert(Int) - Adjustment;
RangeSet New = getRange(St, Sym);
New = F.deletePoint(New, Point);
return setRange(St, Sym, New);
}
ProgramStateRef
RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
APSIntType AdjustmentType(Adjustment);
if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within)
return nullptr;
llvm::APSInt AdjInt = AdjustmentType.convert(Int) - Adjustment;
RangeSet New = getRange(St, Sym);
New = F.intersect(New, AdjInt);
return setRange(St, Sym, New);
}
RangeSet RangeConstraintManager::getSymLTRange(ProgramStateRef St,
SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
APSIntType AdjustmentType(Adjustment);
switch (AdjustmentType.testInRange(Int, true)) {
case APSIntType::RTR_Below:
return F.getEmptySet();
case APSIntType::RTR_Within:
break;
case APSIntType::RTR_Above:
return getRange(St, Sym);
}
llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
llvm::APSInt Min = AdjustmentType.getMinValue();
if (ComparisonVal == Min)
return F.getEmptySet();
llvm::APSInt Lower = Min - Adjustment;
llvm::APSInt Upper = ComparisonVal - Adjustment;
--Upper;
RangeSet Result = getRange(St, Sym);
return F.intersect(Result, Lower, Upper);
}
ProgramStateRef
RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
RangeSet New = getSymLTRange(St, Sym, Int, Adjustment);
return setRange(St, Sym, New);
}
RangeSet RangeConstraintManager::getSymGTRange(ProgramStateRef St,
SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
APSIntType AdjustmentType(Adjustment);
switch (AdjustmentType.testInRange(Int, true)) {
case APSIntType::RTR_Below:
return getRange(St, Sym);
case APSIntType::RTR_Within:
break;
case APSIntType::RTR_Above:
return F.getEmptySet();
}
llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
llvm::APSInt Max = AdjustmentType.getMaxValue();
if (ComparisonVal == Max)
return F.getEmptySet();
llvm::APSInt Lower = ComparisonVal - Adjustment;
llvm::APSInt Upper = Max - Adjustment;
++Lower;
RangeSet SymRange = getRange(St, Sym);
return F.intersect(SymRange, Lower, Upper);
}
ProgramStateRef
RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
RangeSet New = getSymGTRange(St, Sym, Int, Adjustment);
return setRange(St, Sym, New);
}
RangeSet RangeConstraintManager::getSymGERange(ProgramStateRef St,
SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
APSIntType AdjustmentType(Adjustment);
switch (AdjustmentType.testInRange(Int, true)) {
case APSIntType::RTR_Below:
return getRange(St, Sym);
case APSIntType::RTR_Within:
break;
case APSIntType::RTR_Above:
return F.getEmptySet();
}
llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
llvm::APSInt Min = AdjustmentType.getMinValue();
if (ComparisonVal == Min)
return getRange(St, Sym);
llvm::APSInt Max = AdjustmentType.getMaxValue();
llvm::APSInt Lower = ComparisonVal - Adjustment;
llvm::APSInt Upper = Max - Adjustment;
RangeSet SymRange = getRange(St, Sym);
return F.intersect(SymRange, Lower, Upper);
}
ProgramStateRef
RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
RangeSet New = getSymGERange(St, Sym, Int, Adjustment);
return setRange(St, Sym, New);
}
RangeSet
RangeConstraintManager::getSymLERange(llvm::function_ref<RangeSet()> RS,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
APSIntType AdjustmentType(Adjustment);
switch (AdjustmentType.testInRange(Int, true)) {
case APSIntType::RTR_Below:
return F.getEmptySet();
case APSIntType::RTR_Within:
break;
case APSIntType::RTR_Above:
return RS();
}
llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
llvm::APSInt Max = AdjustmentType.getMaxValue();
if (ComparisonVal == Max)
return RS();
llvm::APSInt Min = AdjustmentType.getMinValue();
llvm::APSInt Lower = Min - Adjustment;
llvm::APSInt Upper = ComparisonVal - Adjustment;
RangeSet Default = RS();
return F.intersect(Default, Lower, Upper);
}
RangeSet RangeConstraintManager::getSymLERange(ProgramStateRef St,
SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
return getSymLERange([&] { return getRange(St, Sym); }, Int, Adjustment);
}
ProgramStateRef
RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
RangeSet New = getSymLERange(St, Sym, Int, Adjustment);
return setRange(St, Sym, New);
}
ProgramStateRef RangeConstraintManager::assumeSymWithinInclusiveRange(
ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
const llvm::APSInt &To, const llvm::APSInt &Adjustment) {
RangeSet New = getSymGERange(State, Sym, From, Adjustment);
if (New.isEmpty())
return nullptr;
RangeSet Out = getSymLERange([&] { return New; }, To, Adjustment);
return setRange(State, Sym, Out);
}
ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange(
ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
const llvm::APSInt &To, const llvm::APSInt &Adjustment) {
RangeSet RangeLT = getSymLTRange(State, Sym, From, Adjustment);
RangeSet RangeGT = getSymGTRange(State, Sym, To, Adjustment);
RangeSet New(F.add(RangeLT, RangeGT));
return setRange(State, Sym, New);
}
void RangeConstraintManager::printJson(raw_ostream &Out, ProgramStateRef State,
const char *NL, unsigned int Space,
bool IsDot) const {
printConstraints(Out, State, NL, Space, IsDot);
printEquivalenceClasses(Out, State, NL, Space, IsDot);
printDisequalities(Out, State, NL, Space, IsDot);
}
void RangeConstraintManager::printValue(raw_ostream &Out, ProgramStateRef State,
SymbolRef Sym) {
const RangeSet RS = getRange(State, Sym);
Out << RS.getBitWidth() << (RS.isUnsigned() ? "u:" : "s:");
RS.dump(Out);
}
static std::string toString(const SymbolRef &Sym) {
std::string S;
llvm::raw_string_ostream O(S);
Sym->dumpToStream(O);
return O.str();
}
void RangeConstraintManager::printConstraints(raw_ostream &Out,
ProgramStateRef State,
const char *NL,
unsigned int Space,
bool IsDot) const {
ConstraintRangeTy Constraints = State->get<ConstraintRange>();
Indent(Out, Space, IsDot) << "\"constraints\": ";
if (Constraints.isEmpty()) {
Out << "null," << NL;
return;
}
std::map<std::string, RangeSet> OrderedConstraints;
for (std::pair<EquivalenceClass, RangeSet> P : Constraints) {
SymbolSet ClassMembers = P.first.getClassMembers(State);
for (const SymbolRef &ClassMember : ClassMembers) {
bool insertion_took_place;
std::tie(std::ignore, insertion_took_place) =
OrderedConstraints.insert({toString(ClassMember), P.second});
assert(insertion_took_place &&
"two symbols should not have the same dump");
}
}
++Space;
Out << '[' << NL;
bool First = true;
for (std::pair<std::string, RangeSet> P : OrderedConstraints) {
if (First) {
First = false;
} else {
Out << ',';
Out << NL;
}
Indent(Out, Space, IsDot)
<< "{ \"symbol\": \"" << P.first << "\", \"range\": \"";
P.second.dump(Out);
Out << "\" }";
}
Out << NL;
--Space;
Indent(Out, Space, IsDot) << "]," << NL;
}
static std::string toString(ProgramStateRef State, EquivalenceClass Class) {
SymbolSet ClassMembers = Class.getClassMembers(State);
llvm::SmallVector<SymbolRef, 8> ClassMembersSorted(ClassMembers.begin(),
ClassMembers.end());
llvm::sort(ClassMembersSorted,
[](const SymbolRef &LHS, const SymbolRef &RHS) {
return toString(LHS) < toString(RHS);
});
bool FirstMember = true;
std::string Str;
llvm::raw_string_ostream Out(Str);
Out << "[ ";
for (SymbolRef ClassMember : ClassMembersSorted) {
if (FirstMember)
FirstMember = false;
else
Out << ", ";
Out << "\"" << ClassMember << "\"";
}
Out << " ]";
return Out.str();
}
void RangeConstraintManager::printEquivalenceClasses(raw_ostream &Out,
ProgramStateRef State,
const char *NL,
unsigned int Space,
bool IsDot) const {
ClassMembersTy Members = State->get<ClassMembers>();
Indent(Out, Space, IsDot) << "\"equivalence_classes\": ";
if (Members.isEmpty()) {
Out << "null," << NL;
return;
}
std::set<std::string> MembersStr;
for (std::pair<EquivalenceClass, SymbolSet> ClassToSymbolSet : Members)
MembersStr.insert(toString(State, ClassToSymbolSet.first));
++Space;
Out << '[' << NL;
bool FirstClass = true;
for (const std::string &Str : MembersStr) {
if (FirstClass) {
FirstClass = false;
} else {
Out << ',';
Out << NL;
}
Indent(Out, Space, IsDot);
Out << Str;
}
Out << NL;
--Space;
Indent(Out, Space, IsDot) << "]," << NL;
}
void RangeConstraintManager::printDisequalities(raw_ostream &Out,
ProgramStateRef State,
const char *NL,
unsigned int Space,
bool IsDot) const {
DisequalityMapTy Disequalities = State->get<DisequalityMap>();
Indent(Out, Space, IsDot) << "\"disequality_info\": ";
if (Disequalities.isEmpty()) {
Out << "null," << NL;
return;
}
using EqClassesStrTy = std::set<std::string>;
using DisequalityInfoStrTy = std::map<std::string, EqClassesStrTy>;
DisequalityInfoStrTy DisequalityInfoStr;
for (std::pair<EquivalenceClass, ClassSet> ClassToDisEqSet : Disequalities) {
EquivalenceClass Class = ClassToDisEqSet.first;
ClassSet DisequalClasses = ClassToDisEqSet.second;
EqClassesStrTy MembersStr;
for (EquivalenceClass DisEqClass : DisequalClasses)
MembersStr.insert(toString(State, DisEqClass));
DisequalityInfoStr.insert({toString(State, Class), MembersStr});
}
++Space;
Out << '[' << NL;
bool FirstClass = true;
for (std::pair<std::string, EqClassesStrTy> ClassToDisEqSet :
DisequalityInfoStr) {
const std::string &Class = ClassToDisEqSet.first;
if (FirstClass) {
FirstClass = false;
} else {
Out << ',';
Out << NL;
}
Indent(Out, Space, IsDot) << "{" << NL;
unsigned int DisEqSpace = Space + 1;
Indent(Out, DisEqSpace, IsDot) << "\"class\": ";
Out << Class;
const EqClassesStrTy &DisequalClasses = ClassToDisEqSet.second;
if (!DisequalClasses.empty()) {
Out << "," << NL;
Indent(Out, DisEqSpace, IsDot) << "\"disequal_to\": [" << NL;
unsigned int DisEqClassSpace = DisEqSpace + 1;
Indent(Out, DisEqClassSpace, IsDot);
bool FirstDisEqClass = true;
for (const std::string &DisEqClass : DisequalClasses) {
if (FirstDisEqClass) {
FirstDisEqClass = false;
} else {
Out << ',' << NL;
Indent(Out, DisEqClassSpace, IsDot);
}
Out << DisEqClass;
}
Out << "]" << NL;
}
Indent(Out, Space, IsDot) << "}";
}
Out << NL;
--Space;
Indent(Out, Space, IsDot) << "]," << NL;
}