[cfe-dev] Lazy bindings

Ted Kremenek kremenek at apple.com
Thu Dec 18 18:50:37 PST 2008


On Dec 17, 2008, at 4:22 AM, Zhongxing Xu wrote:

> Hi Ted,
>
> This is the patch for lazy binding.

I have some specific comments on the patch below.  Two main comments:

1) Aside from Bind() returning a GRState* instead of Store, let's keep  
BasicStoreManager the same.  It works just fine for what it was  
intended to do.  The reason I get scared about large patches is the  
huge functionality changes.  BasicStoreManager just keeps binding for  
the VarDecls that are referenced within a function.  I haven't  
encountered any scalability issues with it.  We don't need to use a  
killset.  By keeping it the same we preserve its stability.  New  
functionality should go into RegionStoreManager.  BasicStoreManager  
doesn't need to be lazy in the same way.

2) Rename GRStateManager::getState() to MakeStateWithStore() (or  
something similar).  It makes it clear what it is doing.   
'get'State()' is so short and ambiguous that it has no real meaning,  
and such an important function shouldn't be ambiguous.

> * failed C test cases are due to bugs in RemoveDeadBindings(),
> which removes constraints that is still alive. I plan to fix this
> in a later patch. This patch is already messy enough.

Let's temporarily disable those tests (if necessary) for region store  
and then fix them in later patches.  Since there won't be any  
fundamental changes to BasicStoreManager all tests should pass for it.

>
> * I wish you could help fix failed ObjC test cases, since I have
> no access to the Mac development platform. It'll take some time for me
> to learn basic things.

Sure thing.  Same as the above comment (disable tests for region store  
and fix in later patch).

> * default value of array and struct regions are not implemented
> yet, because the GDM of the same type of ImmutableMap<const
> Region*, SVal> is already used for region extents.

In a later patch we'll use the "tag class" approach I mentioned in my  
other email.

> * Now Bind() methods take and return GRState* because binding could
>   also alter GDM.

Looks great.  As I said before, have BasicStoreManager use the new  
interface (return a state) but don't change its functionality.

> * No variables are initialized except those declared with initial
>   values.

Okay!

Here are specific comments:

Index: include/clang/Analysis/PathSensitive/Store.h
===================================================================
--- include/clang/Analysis/PathSensitive/Store.h	(版本 61131)
+++ include/clang/Analysis/PathSensitive/Store.h	(工作副本)
@@ -49,16 +49,17 @@
    SVal GetRegionSVal(const GRState* state, const MemRegion* R) {
      return Retrieve(state, loc::MemRegionVal(R));
    }
-
-  virtual Store Bind(Store St, Loc LV, SVal V) = 0;
+
+  virtual const GRState* Bind(const GRState* St, Loc LV, SVal V) = 0;

Looks good.  Let's take the opportunity to add a comment about what  
this method does.


    /// BindCompoundLiteral - Return the store that has the bindings  
currently
    ///  in 'store' plus the bindings for the CompoundLiteral.  'R' is  
the region
    ///  for the compound literal and 'BegInit' and 'EndInit'  
represent an
    ///  array of initializer values.
-  virtual Store BindCompoundLiteral(Store store, const  
CompoundLiteralExpr* CL,
-                                    SVal V) = 0;
+  virtual const GRState* BindCompoundLiteral(const GRState* St,
+                                             const  
CompoundLiteralExpr* CL,
+                                             SVal V) = 0;

Looks great.

                       llvm::SmallVectorImpl<const MemRegion*>&  
RegionRoots,
                       LiveSymbolsTy& LSymbols, DeadSymbolsTy&  
DSymbols) = 0;

-  virtual Store BindDecl(Store store, const VarDecl* VD, SVal* InitVal,
-                         unsigned Count) = 0;
+  virtual const GRState* BindDecl(const GRState* St, const VarDecl* VD,
+                                  SVal InitVal) = 0;

Looks great.  A natural change of interface.


Index: include/clang/Analysis/PathSensitive/SymbolManager.h
===================================================================
--- include/clang/Analysis/PathSensitive/SymbolManager.h	(版本  
61131)
+++ include/clang/Analysis/PathSensitive/SymbolManager.h	(工作副 
本)
@@ -104,15 +104,15 @@
  };

  class SymbolDataParmVar : public SymbolData {
-  ParmVarDecl *VD;
+  const ParmVarDecl *VD;

  public:
-  SymbolDataParmVar(SymbolRef MySym, ParmVarDecl* vd)
+  SymbolDataParmVar(SymbolRef MySym, const ParmVarDecl* vd)
      : SymbolData(ParmKind, MySym), VD(vd) {}

-  ParmVarDecl* getDecl() const { return VD; }
+  const ParmVarDecl* getDecl() const { return VD; }

-  static void Profile(llvm::FoldingSetNodeID& profile, ParmVarDecl*  
VD) {
+  static void Profile(llvm::FoldingSetNodeID& profile, const  
ParmVarDecl* VD) {
      profile.AddInteger((unsigned) ParmKind);
      profile.AddPointer(VD);
    }
@@ -128,15 +128,15 @@
  };

Nice constification.


  class SymbolDataGlobalVar : public SymbolData {
-  VarDecl *VD;
+  const VarDecl *VD;

  public:
-  SymbolDataGlobalVar(SymbolRef MySym, VarDecl* vd) :
+  SymbolDataGlobalVar(SymbolRef MySym, const VarDecl* vd) :
      SymbolData(GlobalKind, MySym), VD(vd) {}

-  VarDecl* getDecl() const { return VD; }
+  const VarDecl* getDecl() const { return VD; }

-  static void Profile(llvm::FoldingSetNodeID& profile, VarDecl* VD) {
+  static void Profile(llvm::FoldingSetNodeID& profile, const VarDecl*  
VD) {
      profile.AddInteger((unsigned) GlobalKind);
      profile.AddPointer(VD);
    }

Again, nice constification.


@@ -275,8 +275,9 @@
      : SymbolCounter(0), BPAlloc(bpalloc) {}

    ~SymbolManager();
-
-  SymbolRef getSymbol(VarDecl* D);
+
+  SymbolRef getSymbol(const MemRegion* R);

Please add a comment about what getSymbol : MemRegion* -> SymbolRef  
does.

+  SymbolRef getSymbol(const VarDecl* D);
    SymbolRef getElementSymbol(const MemRegion* R, const llvm::APSInt*  
Idx);
    SymbolRef getFieldSymbol(const MemRegion* R, const FieldDecl* D);
    SymbolRef getConjuredSymbol(Stmt* E, QualType T, unsigned  
VisitCount);



Index: include/clang/Analysis/PathSensitive/GRState.h
===================================================================
--- include/clang/Analysis/PathSensitive/GRState.h	(版本 61131)
+++ include/clang/Analysis/PathSensitive/GRState.h	(工作副本)
@@ -335,8 +335,9 @@

    typedef StoreManager::DeadSymbolsTy DeadSymbolsTy;

-  const GRState* BindDecl(const GRState* St, const VarDecl* VD, SVal*  
IVal,
-                          unsigned Count);
+  const GRState* BindDecl(const GRState* St, const VarDecl* VD, SVal  
IVal) {
+    return StoreMgr->BindDecl(St, VD, IVal);
+  }

Okay.  Here the assumption is that StoreManager::BindDecl will return  
a persistent GRState.  Can you add a comment above the call to  
StoreMgr->BindDecl() that states that assumption?


    /// BindCompoundLiteral - Return the state that has the bindings  
currently
    ///  in 'state' plus the bindings for the CompoundLiteral.  'R' is  
the region
@@ -467,9 +468,9 @@
      return StoreMgr->GetRegionSVal(state, R);
    }

-  void BindLoc(GRState& St, Loc LV, SVal V) {
-    St.St = StoreMgr->Bind(St.St, LV, V);
-  }
+//   void BindLoc(GRState& St, Loc LV, SVal V) {
+//     St.St = StoreMgr->Bind(St.St, LV, V);
+//   }

Don't comment it out.  Just remove it if it is no longer used.   
Getting rid of garbage code is a good thing.


    const GRState* BindLoc(const GRState* St, Loc LV, SVal V);

@@ -480,6 +481,9 @@
    const GRState* Unbind(const GRState* St, Loc LV);

    const GRState* getPersistentState(GRState& Impl);
+
+  // getState - get a persistent state with the new store.
+  const GRState* getState(const GRState* St, Store store);

Let's use a different name.  I know you like to keep things succinct,  
but this is such an important method that we should make it unambiguous.

+
    template <typename T>
+  const GRState* add(const GRState* st,
+                     typename GRStateTrait<T>::key_type K,
+                     typename GRStateTrait<T>::context_type C) {
+    return addGDM(st, GRStateTrait<T>::GDMIndex(),
+        GRStateTrait<T>::MakeVoidPtr(GRStateTrait<T>::Add(st- 
 >get<T>(), K, C)));
+  }

Looks good.


-  GRStateRef BindDecl(const VarDecl* VD, SVal* InitVal, unsigned  
Count) {
-    return GRStateRef(Mgr->BindDecl(St, VD, InitVal, Count), *Mgr);
+  GRStateRef BindDecl(const VarDecl* VD, SVal InitVal) {
+    return GRStateRef(Mgr->BindDecl(St, VD, InitVal), *Mgr);
    }

Looks good.


    GRStateRef BindLoc(Loc LV, SVal V) {
-    GRState StImpl = *St;
-    Mgr->BindLoc(StImpl, LV, V);
-    return GRStateRef(Mgr->getPersistentState(StImpl), *Mgr);
+    St = Mgr->BindLoc(St, LV, V);
+    return GRStateRef(St, *Mgr);
    }

Looks good.  You can also make it more succinct:

   return GRStateRef(Mgr->BindLoc(St, LV, V), *Mgr);


-
+
+  // For ImmutableSet specialization.
    template<typename T>
+  GRStateRef add(typename GRStateTrait<T>::key_type K) {
+    return GRStateRef(Mgr->add<T>(St, K, get_context<T>()), *Mgr);
+  }

The comment does really go to the generality of the interface.  It  
really has to do with any GDM entry where the key type is the same as  
the data.  Right now that is ImmutableSet<.>, but it could be anything  
with a similar interface.

+
+  template<typename T>
    GRStateRef remove(typename GRStateTrait<T>::key_type K,
                      typename GRStateTrait<T>::context_type C) {
      return GRStateRef(Mgr->remove<T>(St, K, C), *Mgr);
@@ -657,7 +674,7 @@

    template<typename T>
    bool contains(typename GRStateTrait<T>::key_type key) const {
-    return St->contains(key);
+    return St->contains<T>(key);
    }

Okay.


    // Lvalue methods.
Index: include/clang/Analysis/PathSensitive/GRStateTrait.h
===================================================================
--- include/clang/Analysis/PathSensitive/GRStateTrait.h	(版本  
61131)
+++ include/clang/Analysis/PathSensitive/GRStateTrait.h	(工作副 
本)
@@ -83,7 +83,11 @@

      static inline void* MakeVoidPtr(data_type B) {
        return B.getRoot();
-    }
+    }
+
+    static data_type Add(data_type B, key_type K, context_type F) {
+      return F.Add(B, K);
+    }

Nice.


Index: include/clang/Analysis/PathSensitive/MemRegion.h
===================================================================
--- include/clang/Analysis/PathSensitive/MemRegion.h	(版本 61131)
+++ include/clang/Analysis/PathSensitive/MemRegion.h	(工作副本)
@@ -99,7 +99,7 @@
    const MemRegion* getSuperRegion() const {
      return superRegion;
    }
-
+
    static bool classof(const MemRegion* R) {
      return R->getKind() > SymbolicRegionKind;
    }
@@ -464,6 +464,9 @@
      assert(R);
      return R == globals;
    }
+
+  bool onStack(const MemRegion* R);
+  bool onHeap(const MemRegion* R);

Looks good.  Doxygen comments please!


    /// getAllocaRegion - Retrieve a region associated with a call to  
alloca().
    AllocaRegion* getAllocaRegion(const Expr* Ex, unsigned Cnt);
Index: include/clang/Analysis/PathSensitive/SVals.h
===================================================================
--- include/clang/Analysis/PathSensitive/SVals.h	(版本 61131)
+++ include/clang/Analysis/PathSensitive/SVals.h	(工作副本)
@@ -71,7 +71,8 @@
    inline bool operator!=(const SVal& R) const {
      return !(*this == R);
    }
-
+  static SVal MakeSymbolValue(SymbolManager& SymMgr, const MemRegion*  
R,
+                              QualType T);

What does this method do?  Does it create a symbol value that has the  
rvalue type of 'R' and ... ?  (a couple comments should suffice).   
What about arrays?  structs?  Basically, what is the interface of this  
function?


    static SVal GetSymbolValue(SymbolManager& SymMgr, VarDecl *D);
    static SVal getSymbolValue(SymbolManager& SymMgr, const MemRegion*  
R,
                               const llvm::APSInt* Idx, QualType T);
@@ -171,6 +172,8 @@

    // Utility methods to create NonLocs.

+  static NonLoc MakeVal(SymbolRef sym);
+

Looks good.

    static NonLoc MakeVal(BasicValueFactory& BasicVals, unsigned X,
                          bool isUnsigned);

@@ -212,6 +215,8 @@
    static Loc MakeVal(const MemRegion* R);

    static Loc MakeVal(AddrLabelExpr* E);
+
+  static Loc MakeVal(SymbolRef sym);

Looks good.


Index: lib/Analysis/GRState.cpp
===================================================================
--- lib/Analysis/GRState.cpp	(版本 61131)
+++ lib/Analysis/GRState.cpp	(工作副本)
@@ -60,48 +60,17 @@
  }

  const GRState* GRStateManager::BindLoc(const GRState* St, Loc LV,  
SVal V) {
-
-  Store OldStore = St->getStore();
-  Store NewStore = StoreMgr->Bind(OldStore, LV, V);
-
-  if (NewStore == OldStore)
-    return St;
-
-  GRState NewSt = *St;
-  NewSt.St = NewStore;
-  return getPersistentState(NewSt);
+  return StoreMgr->Bind(St, LV, V);
  }

Since it is a one liner, we can just inline this in GRState.h.


-const GRState* GRStateManager::BindDecl(const GRState* St, const  
VarDecl* VD,
-                                        SVal* InitVal, unsigned  
Count) {
-  Store OldStore = St->getStore();
-  Store NewStore = StoreMgr->BindDecl(OldStore, VD, InitVal, Count);
-
-  if (NewStore == OldStore)
-    return St;
-
-  GRState NewSt = *St;
-  NewSt.St = NewStore;
-  return getPersistentState(NewSt);
-}

Right, this is now in GRState.h (and a one liner).

-
  /// BindCompoundLiteral - Return the store that has the bindings  
currently
  ///  in 'store' 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*
-GRStateManager::BindCompoundLiteral(const GRState* state,
+GRStateManager::BindCompoundLiteral(const GRState* St,
                                      const CompoundLiteralExpr* CL,  
SVal ILV) {
-
-  Store oldStore = state->getStore();
-  Store newStore = StoreMgr->BindCompoundLiteral(oldStore, CL, ILV);
-
-  if (newStore == oldStore)
-    return state;
-
-  GRState newState = *state;
-  newState.St = newStore;
-  return getPersistentState(newState);
+  return StoreMgr->BindCompoundLiteral(St, CL, ILV);
  }

This too can probably just go into GRState.h (one liner).



+const GRState* GRStateManager::getState(const GRState* St, Store  
store) {
+  GRState NewSt = *St;
+  NewSt.St = store;
+  return getPersistentState(NewSt);
+}

Needs a better name.


Index: lib/Analysis/MemRegion.cpp
===================================================================
--- lib/Analysis/MemRegion.cpp	(版本 61131)
+++ lib/Analysis/MemRegion.cpp	(工作副本)
@@ -209,6 +209,20 @@
    return LazyAllocate(unknown);
  }

+bool MemRegionManager::onStack(const MemRegion* R) {
+  while (const SubRegion* SR = dyn_cast<SubRegion>(R))
+    R = SR->getSuperRegion();
+
+  return (R != 0) && (R == stack);
+}
+
+bool MemRegionManager::onHeap(const MemRegion* R) {
+  while (const SubRegion* SR = dyn_cast<SubRegion>(R))
+    R = SR->getSuperRegion();
+
+  return (R != 0) && (R == heap);
+}

Looks good.


Index: lib/Analysis/SVals.cpp
===================================================================
--- lib/Analysis/SVals.cpp	(版本 61131)
+++ lib/Analysis/SVals.cpp	(工作副本)
@@ -242,6 +242,11 @@
  // 
= 
= 
=---------------------------------------------------------------------- 
===//
  // Utility methods for constructing Non-Locs.
  // 
= 
= 
=---------------------------------------------------------------------- 
===//
+
+NonLoc NonLoc::MakeVal(SymbolRef sym) {
+  return nonloc::SymbolVal(sym);
+}
+

Looks good.


+SVal SVal::MakeSymbolValue(SymbolManager& SymMgr, const MemRegion* R,
+                           QualType T) {
+  if (Loc::IsLocType(T))
+    return Loc::MakeVal(SymMgr.getSymbol(R));
+  else
+    return NonLoc::MakeVal(SymMgr.getSymbol(R));
+}

Looks good, but it would be great to specify in SVals.h what this  
method does.

Index: lib/Analysis/BasicStore.cpp
===================================================================
--- lib/Analysis/BasicStore.cpp	(版本 61131)
+++ lib/Analysis/BasicStore.cpp	(工作副本)
@@ -13,6 +13,7 @@

High level comment: don't change this file except to conform to the  
new API for Bind().  We want to keep its functionality the same.  No  
killset or lazy fetching of values in 'Retrieve'.  By keeping it the  
same we have an anchor of stability to compare against RegionStore.   
It doesn't need to do lazy binding because it has no known scalability  
issues with its current approach and it is tested and is reliable.   
The goal is to get RegionStore to the point where it is more reliable  
and scalable than BasicStore and then we just switch over to using  
RegionStore by default.

Index: lib/Analysis/SymbolManager.cpp
===================================================================
--- lib/Analysis/SymbolManager.cpp	(版本 61131)
+++ lib/Analysis/SymbolManager.cpp	(工作副本)
@@ -13,6 +13,7 @@
  // 
= 
= 
=---------------------------------------------------------------------- 
===//

  #include "clang/Analysis/PathSensitive/SymbolManager.h"
+#include "clang/Analysis/PathSensitive/MemRegion.h"
  #include "llvm/Support/raw_ostream.h"

  using namespace clang;
@@ -21,14 +22,35 @@
    os << getNumber();
  }

-SymbolRef SymbolManager::getSymbol(VarDecl* D) {
+SymbolRef SymbolManager::getSymbol(const MemRegion* R) {
+  switch (R->getKind()) {
+  case MemRegion::VarRegionKind:
+    return getSymbol(cast<VarRegion>(R)->getDecl());
+
+  case MemRegion::ElementRegionKind: {
+    const ElementRegion* ER = cast<ElementRegion>(R);
+    const llvm::APSInt& Idx =
+      cast<nonloc::ConcreteInt>(ER->getIndex()).getValue();
+    return getElementSymbol(ER->getSuperRegion(), &Idx);
+  }

+  case MemRegion::FieldRegionKind: {
+    const FieldRegion* FR = cast<FieldRegion>(R);
+    return getFieldSymbol(FR->getSuperRegion(), FR->getDecl());
+  }
+  default:
+    assert(0 && "unprocessed region");
+  }
+}

At some point we won't need a FieldSymbol or an ElementSymbol, since  
all of that information should be in the region.

Index: lib/Analysis/RegionStore.cpp
===================================================================
--- lib/Analysis/RegionStore.cpp	(版本 61131)
+++ lib/Analysis/RegionStore.cpp	(工作副本)
@@ -62,6 +62,16 @@
    };
  }

+// Regions that have default value zero.
+// FIXME: redefinition!
+// typedef llvm::ImmutableMap<const MemRegion*, SVal>  
RegionDefaultValue;
+// static int RegionDefaultValueIndex = 0;
+// namespace clang {
+//   template<> struct GRStateTrait<RegionDefaultValue>
+//     : public GRStatePartialTrait<RegionDefaultValue> {
+//     static void* GDMIndex() { return &RegionDefaultValueIndex; }
+//   };
+// }

Gotcha.  This will be worked on in a future patch.


-  Store BindCompoundLiteral(Store store, const CompoundLiteralExpr*  
CL, SVal V);
+  const GRState* BindCompoundLiteral(const GRState* St,
+                                     const CompoundLiteralExpr* CL,  
SVal V);

Good.

-  Store Bind(Store St, Loc LV, SVal V);
+  const GRState* Bind(const GRState* St, Loc LV, SVal V);

Good.

-  Store getInitialStore();
+  Store getInitialStore() { return RBFactory.GetEmptyMap().getRoot(); }

Great.  Nice and simple!

-  Store BindDecl(Store store, const VarDecl* VD, SVal* InitVal,  
unsigned Count);
+  const GRState* BindDecl(const GRState* St, const VarDecl* VD, SVal  
InitVal);

Good.

-  Store InitializeArray(Store store, const TypedRegion* R, SVal Init);
-  Store BindArrayToVal(Store store, const TypedRegion* BaseR, SVal V);
-  Store BindArrayToSymVal(Store store, const TypedRegion* BaseR);
+  //Store InitializeArray(Store store, const TypedRegion* R, SVal  
Init);
+  // Store BindArrayToVal(Store store, const TypedRegion* BaseR, SVal  
V);
+  // Store BindArrayToSymVal(Store store, const TypedRegion* BaseR);

Are these going to reimplemented or removed?  If reimplemented, add a  
FIXME above the commented out prototypes.  If removed, just remove them.

-  Store InitializeStruct(Store store, const TypedRegion* R, SVal Init);
-  Store BindStructToVal(Store store, const TypedRegion* BaseR, SVal V);
-  Store BindStructToSymVal(Store store, const TypedRegion* BaseR);
+  //Store InitializeStruct(Store store, const TypedRegion* R, SVal  
Init);
+  //Store BindStructToVal(Store store, const TypedRegion* BaseR, SVal  
V);
+  //Store BindStructToSymVal(Store store, const TypedRegion* BaseR);

Same thing.

-  SVal RetrieveStruct(Store store, const TypedRegion* R);
+  SVal RetrieveStruct(const GRState* St, const TypedRegion* R);

Good.

-  Store BindStruct(Store store, const TypedRegion* R, SVal V);
+  const GRState* BindStruct(const GRState* St, const TypedRegion* R,  
SVal V);

Good.

-SVal RegionStoreManager::Retrieve(const GRState* state, Loc L,  
QualType T) {
+SVal RegionStoreManager::Retrieve(const GRState* St, Loc L, QualType  
T) {
    assert(!isa<UnknownVal>(L) && "location unknown");
    assert(!isa<UndefinedVal>(L) && "location undefined");
-  Store S = state->getStore();

-  switch (L.getSubKind()) {
-  case loc::MemRegionKind: {
-    const MemRegion* R = cast<loc::MemRegionVal>(L).getRegion();
-    assert(R && "bad region");
+  if (isa<loc::SymbolVal>(L))
+    return UnknownVal();

-    if (const TypedRegion* TR = dyn_cast<TypedRegion>(R))
-      if (TR->getRValueType(getContext())->isStructureType())
-        return RetrieveStruct(S, TR);
+  if (isa<loc::ConcreteInt>(L))
+    return UndefinedVal();

-    RegionBindingsTy B(static_cast<const  
RegionBindingsTy::TreeTy*>(S));
-    RegionBindingsTy::data_type* V = B.lookup(R);
-    return V ? *V : UnknownVal();
-  }
+  if (isa<loc::FuncVal>(L))
+    return L;

-  case loc::SymbolValKind:
-    return UnknownVal();
+  const MemRegion* R = cast<loc::MemRegionVal>(L).getRegion();
+  assert(R && "bad region");

-  case loc::ConcreteIntKind:
-    return UndefinedVal(); // As in BasicStoreManager.
+  if (const TypedRegion* TR = dyn_cast<TypedRegion>(R))
+    if (TR->getRValueType(getContext())->isStructureType())
+      return RetrieveStruct(St, TR);
+
+  RegionBindingsTy B = GetRegionBindings(St->getStore());
+  RegionBindingsTy::data_type* V = B.lookup(R);

-  case loc::FuncValKind:
-    return L;
+  // Check if the region has a binding.
+  if (V)
+    return *V;
+
+  // Check if the region is in killset.
+  GRStateRef state(St, StateMgr);
+  if (state.contains<RegionKills>(R))
+    return UnknownVal();

-  default:
-    assert(false && "Invalid Location");
-    return L;
-  }
+  // The location is not initialized.
+
+  // We treat parameters as symbolic values.
+  if (const VarRegion* VR = dyn_cast<VarRegion>(R))
+    if (isa<ParmVarDecl>(VR->getDecl()))
+      return SVal::MakeSymbolValue(getSymbolManager(), VR,
+                                   VR->getRValueType(getContext()));
+
+  if (MRMgr.onStack(R) || MRMgr.onHeap(R))
+    return UndefinedVal();
+  else
+    return SVal::MakeSymbolValue(getSymbolManager(), R,
+                             cast<TypedRegion>(R)- 
 >getRValueType(getContext()));
+
+  // FIXME: consider default values for elements and fields.
  }

It's obvious that much of the guts of RegionStore has been moved to  
this function.  Please add some comments within its implementation  
saying what it is doing (at a high-level).  For example, the pseudo- 
code you presented in an earlier email provides a nice executive  
summary.   From inspection this code looks like it is doing the right  
thing, but I wouldn't be surprised if there were some subtle corner  
cases.  Those can be fixed in a later patch.


-SVal RegionStoreManager::RetrieveStruct(Store store, const  
TypedRegion* R) {
+SVal RegionStoreManager::RetrieveStruct(const GRState* St,const  
TypedRegion* R){
+
+  Store store = St->getStore();
+  GRStateRef state(St, StateMgr);
+
    // FIXME: Verify we want getRValueType instead of getLValueType.
    QualType T = R->getRValueType(getContext());
    assert(T->isStructureType());
@@ -447,10 +478,21 @@
                                                 FieldEnd =  
Fields.rend();
         Field != FieldEnd; ++Field) {
      FieldRegion* FR = MRMgr.getFieldRegion(*Field, R);
-    RegionBindingsTy B(static_cast<const  
RegionBindingsTy::TreeTy*>(store));
+    RegionBindingsTy B = GetRegionBindings(store);
      RegionBindingsTy::data_type* data = B.lookup(FR);

-    SVal FieldValue = data ? *data : UnknownVal();
+    SVal FieldValue;
+    if (data)
+      FieldValue = *data;
+    else if (state.contains<RegionKills>(FR))
+      FieldValue = UnknownVal();
+    else {
+      if (MRMgr.onStack(FR) || MRMgr.onHeap(FR))
+        FieldValue = UndefinedVal();
+      else
+        FieldValue = SVal::MakeSymbolValue(getSymbolManager(), FR,
+                                           FR- 
 >getRValueType(getContext()));
+    }

Looks good to me.

-Store RegionStoreManager::Bind(Store store, Loc LV, SVal V) {
-  if (LV.getSubKind() == loc::SymbolValKind)
-    return store;
+const GRState* RegionStoreManager::Bind(const GRState* St, Loc L,  
SVal V) {
+  if (isa<loc::SymbolVal>(L))
+    return St;

Please add a comment for this check.  It's not obvious to me why  
Bind() aborts on this case.

-  assert(LV.getSubKind() == loc::MemRegionKind);
-
-  const MemRegion* R = cast<loc::MemRegionVal>(LV).getRegion();
-
+  const MemRegion* R = cast<loc::MemRegionVal>(L).getRegion();
    assert(R);

Is Bind() always passed a region?

+  // Check if the region is a struct region.
    if (const TypedRegion* TR = dyn_cast<TypedRegion>(R))
      // FIXME: Verify we want getRValueType().
      if (TR->getRValueType(getContext())->isStructureType())
-      return BindStruct(store, TR, V);
+      return BindStruct(St, TR, V);

+  Store store = St->getStore();
    RegionBindingsTy B = GetRegionBindings(store);
-  return V.isUnknown()
-         ? RBFactory.Remove(B, R).getRoot()
-         : RBFactory.Add(B, R, V).getRoot();
+
+  if (V.isUnknown()) {
+    // Remove the binding.
+    store = RBFactory.Remove(B, R).getRoot();
+
+    // Add the region to the killset.
+    GRStateRef state(St, StateMgr);
+    St = state.add<RegionKills>(R);
+  }
+  else
+    store = RBFactory.Add(B, R, V).getRoot();
+
+  return StateMgr.getState(St, store);
  }

It's probably just cleaner to create 'GRStateRef state' at the  
beginning of this method and use it everywhere rather just on one code  
path.


-Store RegionStoreManager::BindStruct(Store store, const TypedRegion*  
R, SVal V){
-  // Verify we want getRValueType.
-  QualType T = R->getRValueType(getContext());
-  assert(T->isStructureType());
+const GRState* RegionStoreManager::BindDecl(const GRState* St,
+                                            const VarDecl* VD, SVal  
InitVal) {
+  // All static variables are treated as symbolic values.
+  if (VD->hasGlobalStorage())
+    return St;

-  const RecordType* RT = cast<RecordType>(T.getTypePtr());
-  RecordDecl* RD = RT->getDecl();
+  // Process local variables.

-  if (!RD->isDefinition()) {
-    // This can only occur when a pointer of incomplete struct type  
is used as a
-    // function argument.
-    assert(V.isUnknown());
-    return store;
-  }
+  QualType T = VD->getType();
+
+  VarRegion* VR = MRMgr.getVarRegion(VD);
+
+  if (Loc::IsLocType(T) || T->isIntegerType())
+    return Bind(St, Loc::MakeVal(VR), InitVal);

-  RegionBindingsTy B = GetRegionBindings(store);
+  else if (T->isArrayType())
+    return BindArray(St, VR, InitVal);

-  if (isa<UnknownVal>(V))
-    return BindStructToVal(store, R, UnknownVal());
+  else if (T->isStructureType())
+    return BindStruct(St, VR, InitVal);

Good.


-
-Store RegionStoreManager::BindCompoundLiteral(Store store,
-                                              const  
CompoundLiteralExpr* CL,
-                                              SVal V) {
+// FIXME: this method should be merged into Bind().
+const GRState*
+RegionStoreManager::BindCompoundLiteral(const GRState* St,
+                                        const CompoundLiteralExpr*  
CL, SVal V) {
    CompoundLiteralRegion* R = MRMgr.getCompoundLiteralRegion(CL);
-  store = Bind(store, loc::MemRegionVal(R), V);
-  return store;
+  return Bind(St, loc::MemRegionVal(R), V);
  }


Good.

-Store RegionStoreManager::InitializeArray(Store store, const  
TypedRegion* R,
-                                          SVal Init) {
+const GRState* RegionStoreManager::BindArray(const GRState* St,
+                                             const TypedRegion* R,  
SVal Init) {

    // FIXME: Verify we should use getLValueType or getRValueType.
    QualType T = R->getRValueType(getContext());
    assert(T->isArrayType());

+  // When we are binding the whole array, it always has default value  
0.
+  GRStateRef state(St, StateMgr);
+  //  St = state.set<RegionDefaultValue>(R,  
NonLoc::MakeVal(getBasicVals(), 0,
+  //                                                        false));
+
+  Store store = St->getStore();
+
    ConstantArrayType* CAT = cast<ConstantArrayType>(T.getTypePtr());

    llvm::APSInt Size(CAT->getSize(), false);
+  llvm::APSInt i = getBasicVals().getZeroWithPtrWidth(false);

-  llvm::APSInt i = getBasicVals().getValue(0, Size.getBitWidth(),
-                                           Size.isUnsigned());
-
    // Check if the init expr is a StringLiteral.
    if (isa<loc::MemRegionVal>(Init)) {
      const MemRegion* InitR =  
cast<loc::MemRegionVal>(Init).getRegion();
@@ -803,21 +746,21 @@
      unsigned len = S->getByteLength();
      unsigned j = 0;

+    // Copy bytes from the string literal into the target array.  
Trailing bytes
+    // in the array that are not covered by the string literal are  
initialized
+    // to zero.
      for (; i < Size; ++i, ++j) {
+      if (j >= len)
+        break;
+
        SVal Idx = NonLoc::MakeVal(getBasicVals(), i);
        ElementRegion* ER = MRMgr.getElementRegion(Idx, R);

-      // Copy bytes from the string literal into the target array.  
Trailing
-      // bytes in the array that are not covered by the string  
literal are
-      // initialized to zero.
-      SVal V = (j < len)
-        ? NonLoc::MakeVal(getBasicVals(), str[j], sizeof(char)*8, true)
-        : NonLoc::MakeVal(getBasicVals(), 0, sizeof(char)*8, true);
-
-      store = Bind(store, loc::MemRegionVal(ER), V);
+      SVal V = NonLoc::MakeVal(getBasicVals(), str[j],  
sizeof(char)*8, true);
+      St = Bind(St, loc::MemRegionVal(ER), V);
      }
    nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();

-  for (; i < Size; ++i) {
+  for (; i < Size; ++i, ++VI) {
+    // The init list might be shorter than the array decl.
+    if (VI == VE)
+      break;
+
      SVal Idx = NonLoc::MakeVal(getBasicVals(), i);
      ElementRegion* ER = MRMgr.getElementRegion(Idx, R);
-
-    store = Bind(store, loc::MemRegionVal(ER), (VI!=VE) ? *VI :  
UndefinedVal());
-    // The init list might be shorter than the array decl.
-    if (VI != VE) ++VI;
-  }

-  return store;
-}

This all looks reasonable, at least for an initial patch.

-Store Region
+const GRState*
+RegionStoreManager::BindStruct(const GRState* St, const TypedRegion*  
R, SVal V){
    // FIXME: Verify that we should use getRValueType or getLValueType.
    QualType T = R->getRValueType(getContext());
    assert(T->isStructureType());
@@ -906,104 +795,37 @@
    RecordDecl* RD = RT->getDecl();
    assert(RD->isDefinition());

-  nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(Init);
+  nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V);
    nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
    RecordDecl::field_iterator FI = RD->field_begin(), FE = RD- 
 >field_end();

-  for (; FI != FE; ++FI) {
-    QualType FTy = (*FI)->getType();
-    FieldRegion* FR = MRMgr.getFieldRegion(*FI, R);
+  for (; FI != FE; ++FI, ++VI) {

-    if (Loc::IsLocType(FTy) || FTy->isIntegerType()) {
-      if (VI != VE) {
-        store = Bind(store, loc::MemRegionVal(FR), *VI);
-        ++VI;
-      } else
-        store = Bind(store, loc::MemRegionVal(FR), UndefinedVal());
-    }
-    else if (FTy->isArrayType()) {
-      if (VI != VE) {
-        store = InitializeArray(store, FR, *VI);
-        ++VI;
-      } else
-        store = BindArrayToVal(store, FR, UndefinedVal());
+    // There may be fewer values than fields only when we are  
initializing a
+    // struct decl. In this case, mark the region as having default  
value.
+    if (VI == VE) {
+      // GRStateRef state(St, StateMgr);
+    //St = state.set<RegionDefaultValue>(R,  
NonLoc::MakeVal(getBasicVals(), 0,
+      //                                                   false));
+      break;
      }
-    else if (FTy->isStructureType()) {
-      if (VI != VE) {
-        store = InitializeStruct(store, FR, *VI);
-        ++VI;
-      } else
-        store = BindStructToVal(store, FR, UndefinedVal());
-    }
-  }
-  return store;
-}

Looks reasonable.


-// Bind all fields of the struct to some value.
-Store RegionStoreManager::BindStructToVal(Store store, const  
TypedRegion* BaseR,
-                                          SVal V) {
-
-  // FIXME: Verify that we should use getLValueType or getRValueType.
-  QualType T = BaseR->getRValueType(getContext());
-  assert(T->isStructureType());
+    QualType FTy = (*FI)->getType();
+    FieldRegion* FR = MRMgr.getFieldRegion(*FI, R);

-  const RecordType* RT = cast<RecordType>(T.getTypePtr());
-  RecordDecl* RD = RT->getDecl();
-  assert(RD->isDefinition());
-
-  RecordDecl::field_iterator I = RD->field_begin(), E = RD- 
 >field_end();
-
-  for (; I != E; ++I) {
+    if (Loc::IsLocType(FTy) || FTy->isIntegerType())
+      St = Bind(St, Loc::MakeVal(FR), *VI);

-    QualType FTy = (*I)->getType();
-    FieldRegion* FR = MRMgr.getFieldRegion(*I, BaseR);
-
-    if (Loc::IsLocType(FTy) || FTy->isIntegerType()) {
-      store = Bind(store, loc::MemRegionVal(FR), V);
+    else if (FTy->isArrayType())
+      St = BindArray(St, FR, *VI);

-    } else if (FTy->isArrayType()) {
-      store = BindArrayToVal(store, FR, V);
-
-    } else if (FTy->isStructureType()) {
-      store = BindStructToVal(store, FR, V);
-    }
+    else if (FTy->isStructureType())
+      St = BindStruct(St, FR, *VI);
    }

-  return store;
+  return St;
  }

-Store RegionStoreManager::BindStructToSymVal(Store store,
-                                             const TypedRegion*  
BaseR) {
-
-  // FIXME: Verify that we should use getLValueType or getRValueType
-  QualType T = BaseR->getRValueType(getContext());
-  assert(T->isStructureType());
-
-  const RecordType* RT = cast<RecordType>(T.getTypePtr());
-  RecordDecl* RD = RT->getDecl();
-  assert(RD->isDefinition());
-
-  RecordDecl::field_iterator I = RD->field_begin(), E = RD- 
 >field_end();
-
-  for (; I != E; ++I) {
-    QualType FTy = (*I)->getType();
-    FieldRegion* FR = MRMgr.getFieldRegion(*I, BaseR);
-
-    if (Loc::IsLocType(FTy) || FTy->isIntegerType()) {
-      store = Bind(store, loc::MemRegionVal(FR),
-                   SVal::getSymbolValue(getSymbolManager(), BaseR,  
*I, FTy));
-    }
-    else if (FTy->isArrayType()) {
-      store = BindArrayToSymVal(store, FR);
-    }
-    else if (FTy->isStructureType()) {
-      store = BindStructToSymVal(store, FR);
-    }
-  }
-
-  return store;
-}

Looks good.

-
  const GRState* RegionStoreManager::AddRegionView(const GRState* St,
                                                   const MemRegion*  
View,
                                                   const MemRegion*  
Base) {
Index: lib/Analysis/GRExprEngine.cpp
===================================================================
--- lib/Analysis/GRExprEngine.cpp	(版本 61131)
+++ lib/Analysis/GRExprEngine.cpp	(工作副本)
@@ -1811,7 +1811,8 @@
    for (NodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
      const GRState* St = GetState(*I);
      unsigned Count = Builder->getCurrentBlockCount();
-
+
+    // Decls without InitExpr are not initialized explicitly.
      if (InitEx) {
        SVal InitVal = GetSVal(St, InitEx);
        QualType T = VD->getType();
@@ -1829,12 +1830,9 @@
          }
        }

-      St = StateMgr.BindDecl(St, VD, &InitVal, Count);
+      St = StateMgr.BindDecl(St, VD, InitVal);
      }
-    else
-      St = StateMgr.BindDecl(St, VD, 0, Count);

-
      // Check if 'VD' is a VLA and if so check if has a non-zero size.
      QualType T = getContext().getCanonicalType(VD->getType());
      if (VariableArrayType* VLA = dyn_cast<VariableArrayType>(T)) {


Looks good.


All in all there are so many changes here that it is a little hard for  
me to take it all in, but it looks like a good start.  My high level  
thoughts are to keep the basic functionality of GRExprEngine, GRState  
and BasicStoreManager the same in this patch (modulo some interface  
changes).  That way nothing breaks, and the main functionality change  
is in RegionStore.  Once the big changes to RegionStore are in we can  
iterate on its implementation.

Great work!





More information about the cfe-dev mailing list