//== GRState*h - Path-Sens. "State" for tracking valuues -----*- 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 SymbolRef, ExprBindKey, and GRState* // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_ANALYSIS_VALUESTATE_H #define LLVM_CLANG_ANALYSIS_VALUESTATE_H // FIXME: Reduce the number of includes. #include "clang/Analysis/PathSensitive/Environment.h" #include "clang/Analysis/PathSensitive/Store.h" #include "clang/Analysis/PathSensitive/ConstraintManager.h" #include "clang/Analysis/PathSensitive/ValueManager.h" #include "clang/Analysis/PathSensitive/GRCoreEngine.h" #include "clang/AST/Expr.h" #include "clang/AST/Decl.h" #include "clang/AST/ASTContext.h" #include "clang/Analysis/Analyses/LiveVariables.h" #include "llvm/Support/Casting.h" #include "llvm/Support/DataTypes.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Streams.h" #include namespace clang { class GRStateManager; class GRTransferFuncs; typedef ConstraintManager* (*ConstraintManagerCreator)(GRStateManager&); typedef StoreManager* (*StoreManagerCreator)(GRStateManager&); //===----------------------------------------------------------------------===// // GRStateTrait - Traits used by the Generic Data Map of a GRState. //===----------------------------------------------------------------------===// template struct GRStatePartialTrait; template struct GRStateTrait { typedef typename T::data_type data_type; static inline void* GDMIndex() { return &T::TagInt; } static inline void* MakeVoidPtr(data_type D) { return (void*) D; } static inline data_type MakeData(void* const* P) { return P ? (data_type) *P : (data_type) 0; } }; //===----------------------------------------------------------------------===// // GRState- An ImmutableMap type Stmt*/Decl*/Symbols to SVals. //===----------------------------------------------------------------------===// /// GRState - This class encapsulates the actual data values for /// for a "state" in our symbolic value tracking. It is intended to be /// used as a functional object; that is once it is created and made /// "persistent" in a FoldingSet its values will never change. class GRState : public llvm::FoldingSetNode { public: // Typedefs. typedef llvm::ImmutableSet IntSetTy; typedef llvm::ImmutableMap GenericDataMap; typedef GRStateManager ManagerTy; private: void operator=(const GRState& R) const; friend class GRStateManager; Environment Env; Store St; // FIXME: Make these private. public: GenericDataMap GDM; public: /// This ctor is used when creating the first GRState object. GRState(const Environment& env, Store st, GenericDataMap gdm) : Env(env), St(st), GDM(gdm) {} /// Copy ctor - We must explicitly define this or else the "Next" ptr /// in FoldingSetNode will also get copied. GRState(const GRState& RHS) : llvm::FoldingSetNode(), Env(RHS.Env), St(RHS.St), GDM(RHS.GDM) {} /// getEnvironment - Return the environment associated with this state. /// The environment is the mapping from expressions to values. const Environment& getEnvironment() const { return Env; } /// getStore - Return the store associated with this state. The store /// is a mapping from locations to values. Store getStore() const { return St; } /// getGDM - Return the generic data map associated with this state. GenericDataMap getGDM() const { return GDM; } /// Profile - Profile the contents of a GRState object for use /// in a FoldingSet. static void Profile(llvm::FoldingSetNodeID& ID, const GRState* V) { V->Env.Profile(ID); ID.AddPointer(V->St); V->GDM.Profile(ID); } /// Profile - Used to profile the contents of this object for inclusion /// in a FoldingSet. void Profile(llvm::FoldingSetNodeID& ID) const { Profile(ID, this); } SVal LookupExpr(Expr* E) const { return Env.LookupExpr(E); } // Iterators. typedef Environment::seb_iterator seb_iterator; seb_iterator seb_begin() const { return Env.seb_begin(); } seb_iterator seb_end() const { return Env.beb_end(); } typedef Environment::beb_iterator beb_iterator; beb_iterator beb_begin() const { return Env.beb_begin(); } beb_iterator beb_end() const { return Env.beb_end(); } // Trait based GDM dispatch. void* const* FindGDM(void* K) const; template typename GRStateTrait::data_type get() const { return GRStateTrait::MakeData(FindGDM(GRStateTrait::GDMIndex())); } template typename GRStateTrait::lookup_type get(typename GRStateTrait::key_type key) const { void* const* d = FindGDM(GRStateTrait::GDMIndex()); return GRStateTrait::Lookup(GRStateTrait::MakeData(d), key); } template bool contains(typename GRStateTrait::key_type key) const { void* const* d = FindGDM(GRStateTrait::GDMIndex()); return GRStateTrait::Contains(GRStateTrait::MakeData(d), key); } // State pretty-printing. class Printer { public: virtual ~Printer() {} virtual void Print(std::ostream& Out, const GRState* state, const char* nl, const char* sep) = 0; }; void print(std::ostream& Out, StoreManager& StoreMgr, ConstraintManager& ConstraintMgr, Printer **Beg = 0, Printer **End = 0, const char* nl = "\n", const char *sep = "") const; // Tags used for the Generic Data Map. struct NullDerefTag { static int TagInt; typedef const SVal* data_type; }; }; template<> struct GRTrait { static inline void* toPtr(GRState* St) { return (void*) St; } static inline GRState* toState(void* P) { return (GRState*) P; } static inline void Profile(llvm::FoldingSetNodeID& profile, GRState* St) { // At this point states have already been uniqued. Just // add the pointer. profile.AddPointer(St); } }; class GRStateSet { typedef llvm::SmallPtrSet ImplTy; ImplTy Impl; public: GRStateSet() {} inline void Add(const GRState* St) { Impl.insert(St); } typedef ImplTy::const_iterator iterator; inline unsigned size() const { return Impl.size(); } inline bool empty() const { return Impl.empty(); } inline iterator begin() const { return Impl.begin(); } inline iterator end() const { return Impl.end(); } class AutoPopulate { GRStateSet& S; unsigned StartSize; const GRState* St; public: AutoPopulate(GRStateSet& s, const GRState* st) : S(s), StartSize(S.size()), St(st) {} ~AutoPopulate() { if (StartSize == S.size()) S.Add(St); } }; }; //===----------------------------------------------------------------------===// // GRStateManager - Factory object for GRStates. //===----------------------------------------------------------------------===// class GRStateRef; class GRStateManager { friend class GRExprEngine; friend class GRStateRef; private: EnvironmentManager EnvMgr; llvm::OwningPtr StoreMgr; llvm::OwningPtr ConstraintMgr; GRState::IntSetTy::Factory ISetFactory; GRState::GenericDataMap::Factory GDMFactory; typedef llvm::DenseMap > GDMContextsTy; GDMContextsTy GDMContexts; /// Printers - A set of printer objects used for pretty-printing a GRState. /// GRStateManager owns these objects. std::vector Printers; /// StateSet - FoldingSet containing all the states created for analyzing /// a particular function. This is used to unique states. llvm::FoldingSet StateSet; /// ValueMgr - Object that manages the data for all created SVals. ValueManager ValueMgr; /// Alloc - A BumpPtrAllocator to allocate states. llvm::BumpPtrAllocator& Alloc; /// CurrentStmt - The block-level statement currently being visited. This /// is set by GRExprEngine. Stmt* CurrentStmt; /// cfg - The CFG for the analyzed function/method. CFG& cfg; /// codedecl - The Decl representing the function/method being analyzed. const Decl& codedecl; /// TF - Object that represents a bundle of transfer functions /// for manipulating and creating SVals. GRTransferFuncs* TF; /// Liveness - live-variables information of the ValueDecl* and block-level /// Expr* in the CFG. Used to get initial store and prune out dead state. LiveVariables& Liveness; private: Environment RemoveBlkExpr(const Environment& Env, Expr* E) { return EnvMgr.RemoveBlkExpr(Env, E); } // FIXME: Remove when we do lazy initializaton of variable bindings. // const GRState* BindVar(const GRState* St, VarDecl* D, SVal V) { // return SetSVal(St, getLoc(D), V); // } public: GRStateManager(ASTContext& Ctx, StoreManagerCreator CreateStoreManager, ConstraintManagerCreator CreateConstraintManager, llvm::BumpPtrAllocator& alloc, CFG& c, const Decl& cd, LiveVariables& L) : EnvMgr(alloc), ISetFactory(alloc), GDMFactory(alloc), ValueMgr(alloc, Ctx), Alloc(alloc), cfg(c), codedecl(cd), Liveness(L) { StoreMgr.reset((*CreateStoreManager)(*this)); ConstraintMgr.reset((*CreateConstraintManager)(*this)); } ~GRStateManager(); const GRState* getInitialState(); ASTContext &getContext() { return ValueMgr.getContext(); } const ASTContext &getContext() const { return ValueMgr.getContext(); } const Decl &getCodeDecl() { return codedecl; } GRTransferFuncs& getTransferFuncs() { return *TF; } BasicValueFactory &getBasicVals() { return ValueMgr.getBasicValueFactory(); } const BasicValueFactory& getBasicVals() const { return ValueMgr.getBasicValueFactory(); } SymbolManager &getSymbolManager() { return ValueMgr.getSymbolManager(); } const SymbolManager &getSymbolManager() const { return ValueMgr.getSymbolManager(); } ValueManager &getValueManager() { return ValueMgr; } const ValueManager &getValueManager() const { return ValueMgr; } LiveVariables& getLiveVariables() { return Liveness; } llvm::BumpPtrAllocator& getAllocator() { return Alloc; } MemRegionManager& getRegionManager() { return ValueMgr.getRegionManager(); } const MemRegionManager& getRegionManager() const { return ValueMgr.getRegionManager(); } StoreManager& getStoreManager() { return *StoreMgr; } ConstraintManager& getConstraintManager() { return *ConstraintMgr; } const GRState* BindDecl(const GRState* St, const VarDecl* VD, SVal IVal) { // Store manager should return a persistent state. return StoreMgr->BindDecl(St, VD, IVal); } const GRState* BindDeclWithNoInit(const GRState* St, const VarDecl* VD) { // Store manager should return a persistent state. return StoreMgr->BindDeclWithNoInit(St, VD); } /// BindCompoundLiteral - Return the state that has the bindings currently /// in 'state' plus the bindings for the CompoundLiteral. 'R' is the region /// for the compound literal and 'BegInit' and 'EndInit' represent an /// array of initializer values. const GRState* BindCompoundLiteral(const GRState* St, const CompoundLiteralExpr* CL, SVal V) { return StoreMgr->BindCompoundLiteral(St, CL, V); } const GRState* RemoveDeadBindings(const GRState* St, Stmt* Loc, SymbolReaper& SymReaper); const GRState* RemoveSubExprBindings(const GRState* St) { GRState NewSt = *St; NewSt.Env = EnvMgr.RemoveSubExprBindings(NewSt.Env); return getPersistentState(NewSt); } // Utility methods for getting regions. VarRegion* getRegion(const VarDecl* D) { return getRegionManager().getVarRegion(D); } const MemRegion* getSelfRegion(const GRState* state) { return StoreMgr->getSelfRegion(state->getStore()); } // Get the lvalue for a variable reference. SVal GetLValue(const GRState* St, const VarDecl* D) { return StoreMgr->getLValueVar(St, D); } // Get the lvalue for a StringLiteral. SVal GetLValue(const GRState* St, const StringLiteral* E) { return StoreMgr->getLValueString(St, E); } SVal GetLValue(const GRState* St, const CompoundLiteralExpr* CL) { return StoreMgr->getLValueCompoundLiteral(St, CL); } // Get the lvalue for an ivar reference. SVal GetLValue(const GRState* St, const ObjCIvarDecl* D, SVal Base) { return StoreMgr->getLValueIvar(St, D, Base); } // Get the lvalue for a field reference. SVal GetLValue(const GRState* St, SVal Base, const FieldDecl* D) { return StoreMgr->getLValueField(St, Base, D); } // Get the lvalue for an array index. SVal GetLValue(const GRState* St, QualType ElementType, SVal Base, SVal Idx) { return StoreMgr->getLValueElement(St, ElementType, Base, Idx); } // Methods that query & manipulate the Environment. SVal GetSVal(const GRState* St, Stmt* Ex) { return St->getEnvironment().GetSVal(Ex, getBasicVals()); } SVal GetSValAsScalarOrLoc(const GRState* state, const Stmt *S) { if (const Expr *Ex = dyn_cast(S)) { QualType T = Ex->getType(); if (Loc::IsLocType(T) || T->isIntegerType()) return GetSVal(state, S); } return UnknownVal(); } SVal GetSVal(const GRState* St, const Stmt* Ex) { return St->getEnvironment().GetSVal(const_cast(Ex), getBasicVals()); } SVal GetBlkExprSVal(const GRState* St, Stmt* Ex) { return St->getEnvironment().GetBlkExprSVal(Ex, getBasicVals()); } const GRState* BindExpr(const GRState* St, Stmt* Ex, SVal V, bool isBlkExpr, bool Invalidate) { const Environment& OldEnv = St->getEnvironment(); Environment NewEnv = EnvMgr.BindExpr(OldEnv, Ex, V, isBlkExpr, Invalidate); if (NewEnv == OldEnv) return St; GRState NewSt = *St; NewSt.Env = NewEnv; return getPersistentState(NewSt); } const GRState* BindExpr(const GRState* St, Stmt* Ex, SVal V, bool Invalidate = true) { bool isBlkExpr = false; if (Ex == CurrentStmt) { // FIXME: Should this just be an assertion? When would we want to set // the value of a block-level expression if it wasn't CurrentStmt? isBlkExpr = cfg.isBlkExpr(Ex); if (!isBlkExpr) return St; } return BindExpr(St, Ex, V, isBlkExpr, Invalidate); } SVal ArrayToPointer(Loc Array) { return StoreMgr->ArrayToPointer(Array); } // Methods that manipulate the GDM. const GRState* addGDM(const GRState* St, void* Key, void* Data); // Methods that query or create regions. bool hasStackStorage(const MemRegion* R) { return getRegionManager().hasStackStorage(R); } // Methods that query & manipulate the Store. void iterBindings(const GRState* state, StoreManager::BindingsHandler& F) { StoreMgr->iterBindings(state->getStore(), F); } SVal GetSVal(const GRState* state, Loc LV, QualType T = QualType()) { return StoreMgr->Retrieve(state, LV, T); } SVal GetSVal(const GRState* state, const MemRegion* R) { return StoreMgr->Retrieve(state, loc::MemRegionVal(R)); } SVal GetSValAsScalarOrLoc(const GRState* state, const MemRegion *R) { // We only want to do fetches from regions that we can actually bind // values. For example, SymbolicRegions of type 'id<...>' cannot // have direct bindings (but their can be bindings on their subregions). if (!R->isBoundable(getContext())) return UnknownVal(); if (const TypedRegion *TR = dyn_cast(R)) { QualType T = TR->getValueType(getContext()); if (Loc::IsLocType(T) || T->isIntegerType()) return GetSVal(state, R); } return UnknownVal(); } const GRState* BindLoc(const GRState* St, Loc LV, SVal V) { return StoreMgr->Bind(St, LV, V); } void Unbind(GRState& St, Loc LV) { St.St = StoreMgr->Remove(St.St, LV); } const GRState* Unbind(const GRState* St, Loc LV); const GRState* getPersistentState(GRState& Impl); // MakeStateWithStore - get a persistent state with the new store. const GRState* MakeStateWithStore(const GRState* St, Store store); bool isEqual(const GRState* state, Expr* Ex, const llvm::APSInt& V); bool isEqual(const GRState* state, Expr* Ex, uint64_t); //==---------------------------------------------------------------------==// // Generic Data Map methods. //==---------------------------------------------------------------------==// // // GRStateManager and GRState support a "generic data map" that allows // different clients of GRState objects to embed arbitrary data within a // GRState object. The generic data map is essentially an immutable map // from a "tag" (that acts as the "key" for a client) and opaque values. // Tags/keys and values are simply void* values. The typical way that clients // generate unique tags are by taking the address of a static variable. // Clients are responsible for ensuring that data values referred to by a // the data pointer are immutable (and thus are essentially purely functional // data). // // The templated methods below use the GRStateTrait class // to resolve keys into the GDM and to return data values to clients. // // Trait based GDM dispatch. template const GRState* set(const GRState* st, typename GRStateTrait::data_type D) { return addGDM(st, GRStateTrait::GDMIndex(), GRStateTrait::MakeVoidPtr(D)); } template const GRState* set(const GRState* st, typename GRStateTrait::key_type K, typename GRStateTrait::value_type V, typename GRStateTrait::context_type C) { return addGDM(st, GRStateTrait::GDMIndex(), GRStateTrait::MakeVoidPtr(GRStateTrait::Set(st->get(), K, V, C))); } template const GRState* add(const GRState* st, typename GRStateTrait::key_type K, typename GRStateTrait::context_type C) { return addGDM(st, GRStateTrait::GDMIndex(), GRStateTrait::MakeVoidPtr(GRStateTrait::Add(st->get(), K, C))); } template const GRState* remove(const GRState* st, typename GRStateTrait::key_type K, typename GRStateTrait::context_type C) { return addGDM(st, GRStateTrait::GDMIndex(), GRStateTrait::MakeVoidPtr(GRStateTrait::Remove(st->get(), K, C))); } void* FindGDMContext(void* index, void* (*CreateContext)(llvm::BumpPtrAllocator&), void (*DeleteContext)(void*)); template typename GRStateTrait::context_type get_context() { void* p = FindGDMContext(GRStateTrait::GDMIndex(), GRStateTrait::CreateContext, GRStateTrait::DeleteContext); return GRStateTrait::MakeContext(p); } //==---------------------------------------------------------------------==// // Constraints on values. //==---------------------------------------------------------------------==// // // Each GRState records constraints on symbolic values. These constraints // are managed using the ConstraintManager associated with a GRStateManager. // As constraints gradually accrue on symbolic values, added constraints // may conflict and indicate that a state is infeasible (as no real values // could satisfy all the constraints). This is the principal mechanism // for modeling path-sensitivity in GRExprEngine/GRState. // // Various "Assume" methods form the interface for adding constraints to // symbolic values. A call to "Assume" indicates an assumption being placed // on one or symbolic values. Assume methods take the following inputs: // // (1) A GRState object representing the current state. // // (2) The assumed constraint (which is specific to a given "Assume" method). // // (3) A binary value "Assumption" that indicates whether the constraint is // assumed to be true or false. // // The output of "Assume" are two values: // // (a) "isFeasible" is set to true or false to indicate whether or not // the assumption is feasible. // // (b) A new GRState object with the added constraints. // // FIXME: (a) should probably disappear since it is redundant with (b). // (i.e., (b) could just be set to NULL). // const GRState* Assume(const GRState* St, SVal Cond, bool Assumption, bool& isFeasible) { const GRState *state = ConstraintMgr->Assume(St, Cond, Assumption, isFeasible); assert(!isFeasible || state); return isFeasible ? state : NULL; } const GRState* AssumeInBound(const GRState* St, SVal Idx, SVal UpperBound, bool Assumption, bool& isFeasible) { const GRState *state = ConstraintMgr->AssumeInBound(St, Idx, UpperBound, Assumption, isFeasible); assert(!isFeasible || state); return isFeasible ? state : NULL; } const llvm::APSInt* getSymVal(const GRState* St, SymbolRef sym) { return ConstraintMgr->getSymVal(St, sym); } void EndPath(const GRState* St) { ConstraintMgr->EndPath(St); } bool scanReachableSymbols(SVal val, const GRState* state, SymbolVisitor& visitor); }; //===----------------------------------------------------------------------===// // GRStateRef - A "fat" reference to GRState that also bundles GRStateManager. //===----------------------------------------------------------------------===// class GRStateRef { const GRState* St; GRStateManager* Mgr; public: GRStateRef(const GRState* st, GRStateManager& mgr) : St(st), Mgr(&mgr) {} const GRState* getState() const { return St; } operator const GRState*() const { return St; } GRStateManager& getManager() const { return *Mgr; } SVal GetSVal(Expr* Ex) { return Mgr->GetSVal(St, Ex); } SVal GetBlkExprSVal(Expr* Ex) { return Mgr->GetBlkExprSVal(St, Ex); } SVal GetSValAsScalarOrLoc(const Expr *Ex) { return Mgr->GetSValAsScalarOrLoc(St, Ex); } SVal GetSVal(Loc LV, QualType T = QualType()) { return Mgr->GetSVal(St, LV, T); } SVal GetSVal(const MemRegion* R) { return Mgr->GetSVal(St, R); } SVal GetSValAsScalarOrLoc(const MemRegion *R) { return Mgr->GetSValAsScalarOrLoc(St, R); } GRStateRef BindExpr(Stmt* Ex, SVal V, bool isBlkExpr, bool Invalidate) { return GRStateRef(Mgr->BindExpr(St, Ex, V, isBlkExpr, Invalidate), *Mgr); } GRStateRef BindExpr(Stmt* Ex, SVal V, bool Invalidate = true) { return GRStateRef(Mgr->BindExpr(St, Ex, V, Invalidate), *Mgr); } GRStateRef BindDecl(const VarDecl* VD, SVal InitVal) { return GRStateRef(Mgr->BindDecl(St, VD, InitVal), *Mgr); } GRStateRef BindLoc(Loc LV, SVal V) { return GRStateRef(Mgr->BindLoc(St, LV, V), *Mgr); } GRStateRef BindLoc(SVal LV, SVal V) { if (!isa(LV)) return *this; return BindLoc(cast(LV), V); } GRStateRef Unbind(Loc LV) { return GRStateRef(Mgr->Unbind(St, LV), *Mgr); } // Trait based GDM dispatch. template typename GRStateTrait::data_type get() const { return St->get(); } template typename GRStateTrait::lookup_type get(typename GRStateTrait::key_type key) const { return St->get(key); } template GRStateRef set(typename GRStateTrait::data_type D) { return GRStateRef(Mgr->set(St, D), *Mgr); } template typename GRStateTrait::context_type get_context() { return Mgr->get_context(); } template GRStateRef set(typename GRStateTrait::key_type K, typename GRStateTrait::value_type E, typename GRStateTrait::context_type C) { return GRStateRef(Mgr->set(St, K, E, C), *Mgr); } template GRStateRef set(typename GRStateTrait::key_type K, typename GRStateTrait::value_type E) { return GRStateRef(Mgr->set(St, K, E, get_context()), *Mgr); } template GRStateRef add(typename GRStateTrait::key_type K) { return GRStateRef(Mgr->add(St, K, get_context()), *Mgr); } template GRStateRef remove(typename GRStateTrait::key_type K, typename GRStateTrait::context_type C) { return GRStateRef(Mgr->remove(St, K, C), *Mgr); } template GRStateRef remove(typename GRStateTrait::key_type K) { return GRStateRef(Mgr->remove(St, K, get_context()), *Mgr); } template bool contains(typename GRStateTrait::key_type key) const { return St->contains(key); } // Lvalue methods. SVal GetLValue(const VarDecl* VD) { return Mgr->GetLValue(St, VD); } GRStateRef Assume(SVal Cond, bool Assumption, bool& isFeasible) { return GRStateRef(Mgr->Assume(St, Cond, Assumption, isFeasible), *Mgr); } template CB scanReachableSymbols(SVal val) { CB cb(*this); Mgr->scanReachableSymbols(val, St, cb); return cb; } SymbolManager& getSymbolManager() { return Mgr->getSymbolManager(); } BasicValueFactory& getBasicVals() { return Mgr->getBasicVals(); } // Pretty-printing. void print(std::ostream& Out, const char* nl = "\n", const char *sep = "") const; void printStdErr() const; void printDOT(std::ostream& Out) const; }; } // end clang namespace #endif