diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Checker/RangeConstraintManager.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Checker/RangeConstraintManager.cpp | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/lib/Checker/RangeConstraintManager.cpp b/contrib/llvm/tools/clang/lib/Checker/RangeConstraintManager.cpp new file mode 100644 index 0000000..c904c33 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Checker/RangeConstraintManager.cpp @@ -0,0 +1,359 @@ +//== 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/Checker/PathSensitive/GRState.h" +#include "clang/Checker/PathSensitive/GRStateTrait.h" +#include "clang/Checker/PathSensitive/GRTransferFuncs.h" +#include "clang/Checker/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); + } +} |