llvm-project/clang/lib/Analysis/RangeConstraintManager.cpp
Ted Kremenek de8e7447b6 Remove references to 'Checker' and 'GRTransferFuncs' from
GRStateManager.  Having these references was an abstraction violation,
as they really should only be known about GRExprEngine.

This change required adding a new 'ProcessAssume' callback in
GRSubEngine.  GRExprEngine implements this callback by calling
'EvalAssume' on all registered Checker objects as well as the
registered GRTransferFunc object.

llvm-svn: 92549
2010-01-05 00:15:18 +00:00

360 lines
12 KiB
C++

//== RangeConstraintManager.cpp - Manage range constraints.------*- C++ -*--==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines RangeConstraintManager, a class that tracks simple
// equality and inequality constraints on symbolic values of GRState.
//
//===----------------------------------------------------------------------===//
#include "SimpleConstraintManager.h"
#include "clang/Analysis/PathSensitive/GRState.h"
#include "clang/Analysis/PathSensitive/GRStateTrait.h"
#include "clang/Analysis/PathSensitive/GRTransferFuncs.h"
#include "clang/Analysis/ManagerRegistry.h"
#include "llvm/Support/Debug.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableSet.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
namespace { class ConstraintRange {}; }
static int ConstraintRangeIndex = 0;
/// A Range represents the closed range [from, to]. The caller must
/// guarantee that from <= to. Note that Range is immutable, so as not
/// to subvert RangeSet's immutability.
namespace {
class Range : public std::pair<const llvm::APSInt*,
const llvm::APSInt*> {
public:
Range(const llvm::APSInt &from, const llvm::APSInt &to)
: std::pair<const llvm::APSInt*, const llvm::APSInt*>(&from, &to) {
assert(from <= to);
}
bool Includes(const llvm::APSInt &v) const {
return *first <= v && v <= *second;
}
const llvm::APSInt &From() const {
return *first;
}
const llvm::APSInt &To() const {
return *second;
}
const llvm::APSInt *getConcreteValue() const {
return &From() == &To() ? &From() : NULL;
}
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddPointer(&From());
ID.AddPointer(&To());
}
};
class RangeTrait : public llvm::ImutContainerInfo<Range> {
public:
// When comparing if one Range is less than another, we should compare
// the actual APSInt values instead of their pointers. This keeps the order
// consistent (instead of comparing by pointer values) and can potentially
// be used to speed up some of the operations in RangeSet.
static inline bool isLess(key_type_ref lhs, key_type_ref rhs) {
return *lhs.first < *rhs.first || (!(*rhs.first < *lhs.first) &&
*lhs.second < *rhs.second);
}
};
/// RangeSet contains a set of ranges. If the set is empty, then
/// there the value of a symbol is overly constrained and there are no
/// possible values for that symbol.
class RangeSet {
typedef llvm::ImmutableSet<Range, RangeTrait> PrimRangeSet;
PrimRangeSet ranges; // no need to make const, since it is an
// ImmutableSet - this allows default operator=
// to work.
public:
typedef PrimRangeSet::Factory Factory;
typedef PrimRangeSet::iterator iterator;
RangeSet(PrimRangeSet RS) : ranges(RS) {}
RangeSet(Factory& F) : ranges(F.GetEmptySet()) {}
iterator begin() const { return ranges.begin(); }
iterator end() const { return ranges.end(); }
bool isEmpty() const { return ranges.isEmpty(); }
/// Construct a new RangeSet representing '{ [from, to] }'.
RangeSet(Factory &F, const llvm::APSInt &from, const llvm::APSInt &to)
: ranges(F.Add(F.GetEmptySet(), Range(from, to))) {}
/// Profile - Generates a hash profile of this RangeSet for use
/// by FoldingSet.
void Profile(llvm::FoldingSetNodeID &ID) const { ranges.Profile(ID); }
/// getConcreteValue - If a symbol is contrained to equal a specific integer
/// constant then this method returns that value. Otherwise, it returns
/// NULL.
const llvm::APSInt* getConcreteValue() const {
return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : 0;
}
/// AddEQ - Create a new RangeSet with the additional constraint that the
/// value be equal to V.
RangeSet AddEQ(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) {
// Search for a range that includes 'V'. If so, return a new RangeSet
// representing { [V, V] }.
for (PrimRangeSet::iterator i = begin(), e = end(); i!=e; ++i)
if (i->Includes(V))
return RangeSet(F, V, V);
return RangeSet(F);
}
/// AddNE - Create a new RangeSet with the additional constraint that the
/// value be not be equal to V.
RangeSet AddNE(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) {
PrimRangeSet newRanges = ranges;
// FIXME: We can perhaps enhance ImmutableSet to do this search for us
// in log(N) time using the sorted property of the internal AVL tree.
for (iterator i = begin(), e = end(); i != e; ++i) {
if (i->Includes(V)) {
// Remove the old range.
newRanges = F.Remove(newRanges, *i);
// Split the old range into possibly one or two ranges.
if (V != i->From())
newRanges = F.Add(newRanges, Range(i->From(), BV.Sub1(V)));
if (V != i->To())
newRanges = F.Add(newRanges, Range(BV.Add1(V), i->To()));
// All of the ranges are non-overlapping, so we can stop.
break;
}
}
return newRanges;
}
/// AddNE - Create a new RangeSet with the additional constraint that the
/// value be less than V.
RangeSet AddLT(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) {
PrimRangeSet newRanges = F.GetEmptySet();
for (iterator i = begin(), e = end() ; i != e ; ++i) {
if (i->Includes(V) && i->From() < V)
newRanges = F.Add(newRanges, Range(i->From(), BV.Sub1(V)));
else if (i->To() < V)
newRanges = F.Add(newRanges, *i);
}
return newRanges;
}
RangeSet AddLE(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) {
PrimRangeSet newRanges = F.GetEmptySet();
for (iterator i = begin(), e = end(); i != e; ++i) {
// Strictly we should test for includes *V + 1, but no harm is
// done by this formulation
if (i->Includes(V))
newRanges = F.Add(newRanges, Range(i->From(), V));
else if (i->To() <= V)
newRanges = F.Add(newRanges, *i);
}
return newRanges;
}
RangeSet AddGT(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) {
PrimRangeSet newRanges = F.GetEmptySet();
for (PrimRangeSet::iterator i = begin(), e = end(); i != e; ++i) {
if (i->Includes(V) && i->To() > V)
newRanges = F.Add(newRanges, Range(BV.Add1(V), i->To()));
else if (i->From() > V)
newRanges = F.Add(newRanges, *i);
}
return newRanges;
}
RangeSet AddGE(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) {
PrimRangeSet newRanges = F.GetEmptySet();
for (PrimRangeSet::iterator i = begin(), e = end(); i != e; ++i) {
// Strictly we should test for includes *V - 1, but no harm is
// done by this formulation
if (i->Includes(V))
newRanges = F.Add(newRanges, Range(V, i->To()));
else if (i->From() >= V)
newRanges = F.Add(newRanges, *i);
}
return newRanges;
}
void print(llvm::raw_ostream &os) const {
bool isFirst = true;
os << "{ ";
for (iterator i = begin(), e = end(); i != e; ++i) {
if (isFirst)
isFirst = false;
else
os << ", ";
os << '[' << i->From().toString(10) << ", " << i->To().toString(10)
<< ']';
}
os << " }";
}
bool operator==(const RangeSet &other) const {
return ranges == other.ranges;
}
};
} // end anonymous namespace
typedef llvm::ImmutableMap<SymbolRef,RangeSet> ConstraintRangeTy;
namespace clang {
template<>
struct GRStateTrait<ConstraintRange>
: public GRStatePartialTrait<ConstraintRangeTy> {
static inline void* GDMIndex() { return &ConstraintRangeIndex; }
};
}
namespace {
class RangeConstraintManager : public SimpleConstraintManager{
RangeSet GetRange(const GRState *state, SymbolRef sym);
public:
RangeConstraintManager(GRSubEngine &subengine)
: SimpleConstraintManager(subengine) {}
const GRState* AssumeSymNE(const GRState* St, SymbolRef sym,
const llvm::APSInt& V);
const GRState* AssumeSymEQ(const GRState* St, SymbolRef sym,
const llvm::APSInt& V);
const GRState* AssumeSymLT(const GRState* St, SymbolRef sym,
const llvm::APSInt& V);
const GRState* AssumeSymGT(const GRState* St, SymbolRef sym,
const llvm::APSInt& V);
const GRState* AssumeSymGE(const GRState* St, SymbolRef sym,
const llvm::APSInt& V);
const GRState* AssumeSymLE(const GRState* St, SymbolRef sym,
const llvm::APSInt& V);
const llvm::APSInt* getSymVal(const GRState* St, SymbolRef sym) const;
// FIXME: Refactor into SimpleConstraintManager?
bool isEqual(const GRState* St, SymbolRef sym, const llvm::APSInt& V) const {
const llvm::APSInt *i = getSymVal(St, sym);
return i ? *i == V : false;
}
const GRState* RemoveDeadBindings(const GRState* St, SymbolReaper& SymReaper);
void print(const GRState* St, llvm::raw_ostream& Out,
const char* nl, const char *sep);
private:
RangeSet::Factory F;
};
} // end anonymous namespace
ConstraintManager* clang::CreateRangeConstraintManager(GRStateManager&,
GRSubEngine &subeng) {
return new RangeConstraintManager(subeng);
}
const llvm::APSInt* RangeConstraintManager::getSymVal(const GRState* St,
SymbolRef sym) const {
const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(sym);
return T ? T->getConcreteValue() : NULL;
}
/// Scan all symbols referenced by the constraints. If the symbol is not alive
/// as marked in LSymbols, mark it as dead in DSymbols.
const GRState*
RangeConstraintManager::RemoveDeadBindings(const GRState* state,
SymbolReaper& SymReaper) {
ConstraintRangeTy CR = state->get<ConstraintRange>();
ConstraintRangeTy::Factory& CRFactory = state->get_context<ConstraintRange>();
for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) {
SymbolRef sym = I.getKey();
if (SymReaper.maybeDead(sym))
CR = CRFactory.Remove(CR, sym);
}
return state->set<ConstraintRange>(CR);
}
//===------------------------------------------------------------------------===
// AssumeSymX methods: public interface for RangeConstraintManager.
//===------------------------------------------------------------------------===/
RangeSet
RangeConstraintManager::GetRange(const GRState *state, SymbolRef sym) {
if (ConstraintRangeTy::data_type* V = state->get<ConstraintRange>(sym))
return *V;
// Lazily generate a new RangeSet representing all possible values for the
// given symbol type.
QualType T = state->getSymbolManager().getType(sym);
BasicValueFactory& BV = state->getBasicVals();
return RangeSet(F, BV.getMinValue(T), BV.getMaxValue(T));
}
//===------------------------------------------------------------------------===
// AssumeSymX methods: public interface for RangeConstraintManager.
//===------------------------------------------------------------------------===/
#define AssumeX(OP)\
const GRState*\
RangeConstraintManager::AssumeSym ## OP(const GRState* state, SymbolRef sym,\
const llvm::APSInt& V){\
const RangeSet& R = GetRange(state, sym).Add##OP(state->getBasicVals(), F, V);\
return !R.isEmpty() ? state->set<ConstraintRange>(sym, R) : NULL;\
}
AssumeX(EQ)
AssumeX(NE)
AssumeX(LT)
AssumeX(GT)
AssumeX(LE)
AssumeX(GE)
//===------------------------------------------------------------------------===
// Pretty-printing.
//===------------------------------------------------------------------------===/
void RangeConstraintManager::print(const GRState* St, llvm::raw_ostream& Out,
const char* nl, const char *sep) {
ConstraintRangeTy Ranges = St->get<ConstraintRange>();
if (Ranges.isEmpty())
return;
Out << nl << sep << "ranges of symbol values:";
for (ConstraintRangeTy::iterator I=Ranges.begin(), E=Ranges.end(); I!=E; ++I){
Out << nl << ' ' << I.getKey() << " : ";
I.getData().print(Out);
}
}