[analyzer] Refactor RegionStore's sub-region bindings traversal.
authorJordan Rose <jordan_rose@apple.com>
Fri, 15 Feb 2013 00:32:03 +0000 (00:32 +0000)
committerJordan Rose <jordan_rose@apple.com>
Fri, 15 Feb 2013 00:32:03 +0000 (00:32 +0000)
This is going to be used in the next commit.
While I'm here, tighten up assumptions about symbolic offset
BindingKeys, and make offset calculation explicitly handle all
MemRegion kinds.

No functionality change.

llvm-svn: 175228

clang/lib/StaticAnalyzer/Core/MemRegion.cpp
clang/lib/StaticAnalyzer/Core/RegionStore.cpp

index 687c7c0..a0abe42 100644 (file)
@@ -1080,16 +1080,37 @@ RegionOffset MemRegion::getAsOffset() const {
 
   while (1) {
     switch (R->getKind()) {
-    default:
-      return RegionOffset(R, RegionOffset::Symbolic);
+    case GenericMemSpaceRegionKind:
+    case StackLocalsSpaceRegionKind:
+    case StackArgumentsSpaceRegionKind:
+    case HeapSpaceRegionKind:
+    case UnknownSpaceRegionKind:
+    case StaticGlobalSpaceRegionKind:
+    case GlobalInternalSpaceRegionKind:
+    case GlobalSystemSpaceRegionKind:
+    case GlobalImmutableSpaceRegionKind:
+      // Stores can bind directly to a region space to set a default value.
+      assert(Offset == 0 && !SymbolicOffsetBase);
+      goto Finish;
+
+    case FunctionTextRegionKind:
+    case BlockTextRegionKind:
+    case BlockDataRegionKind:
+      // These will never have bindings, but may end up having values requested
+      // if the user does some strange casting.
+      if (Offset != 0)
+        SymbolicOffsetBase = R;
+      goto Finish;
 
     case SymbolicRegionKind:
     case AllocaRegionKind:
     case CompoundLiteralRegionKind:
     case CXXThisRegionKind:
     case StringRegionKind:
+    case ObjCStringRegionKind:
     case VarRegionKind:
     case CXXTempObjectRegionKind:
+      // Usual base regions.
       goto Finish;
 
     case ObjCIvarRegionKind:
index 354b551..7b1d72b 100644 (file)
@@ -46,11 +46,15 @@ private:
   llvm::PointerIntPair<const MemRegion *, 2> P;
   uint64_t Data;
 
-  explicit BindingKey(const MemRegion *r, const MemRegion *Base, Kind k)
+  /// Create a key for a binding to region \p r, which has a symbolic offset
+  /// from region \p Base.
+  explicit BindingKey(const SubRegion *r, const SubRegion *Base, Kind k)
     : P(r, k | Symbolic), Data(reinterpret_cast<uintptr_t>(Base)) {
     assert(r && Base && "Must have known regions.");
     assert(getConcreteOffsetRegion() == Base && "Failed to store base region");
   }
+
+  /// Create a key for a binding at \p offset from base region \p r.
   explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k)
     : P(r, k), Data(offset) {
     assert(r && "Must have known regions.");
@@ -68,9 +72,9 @@ public:
     return Data;
   }
 
-  const MemRegion *getConcreteOffsetRegion() const {
+  const SubRegion *getConcreteOffsetRegion() const {
     assert(hasSymbolicOffset());
-    return reinterpret_cast<const MemRegion *>(static_cast<uintptr_t>(Data));
+    return reinterpret_cast<const SubRegion *>(static_cast<uintptr_t>(Data));
   }
 
   const MemRegion *getBaseRegion() const {
@@ -106,7 +110,7 @@ public:
 BindingKey BindingKey::Make(const MemRegion *R, Kind k) {
   const RegionOffset &RO = R->getAsOffset();
   if (RO.hasSymbolicOffset())
-    return BindingKey(R, RO.getRegion(), k);
+    return BindingKey(cast<SubRegion>(R), cast<SubRegion>(RO.getRegion()), k);
 
   return BindingKey(RO.getRegion(), RO.getOffset(), k);
 }
@@ -709,61 +713,45 @@ static bool isCompatibleWithFields(BindingKey K, const FieldVector &Fields) {
                       Fields.begin() - Delta);
 }
 
-RegionBindingsRef
-RegionStoreManager::removeSubRegionBindings(RegionBindingsConstRef B,
-                                            const SubRegion *R) {
-  BindingKey SRKey = BindingKey::Make(R, BindingKey::Default);
-  const MemRegion *ClusterHead = SRKey.getBaseRegion();
-  if (R == ClusterHead) {
-    // We can remove an entire cluster's bindings all in one go.
-    return B.remove(R);
-  }
-
+static void collectSubRegionKeys(SmallVectorImpl<BindingKey> &Keys,
+                                 SValBuilder &SVB,
+                                 const ClusterBindings &Cluster,
+                                 const SubRegion *Top, BindingKey TopKey) {
   FieldVector FieldsInSymbolicSubregions;
-  bool HasSymbolicOffset = SRKey.hasSymbolicOffset();
-  if (HasSymbolicOffset) {
-    getSymbolicOffsetFields(SRKey, FieldsInSymbolicSubregions);
-    R = cast<SubRegion>(SRKey.getConcreteOffsetRegion());
-    SRKey = BindingKey::Make(R, BindingKey::Default);
+  if (TopKey.hasSymbolicOffset()) {
+    getSymbolicOffsetFields(TopKey, FieldsInSymbolicSubregions);
+    Top = cast<SubRegion>(TopKey.getConcreteOffsetRegion());
+    TopKey = BindingKey::Make(Top, BindingKey::Default);
   }
 
   // This assumes the region being invalidated is char-aligned. This isn't
   // true for bitfields, but since bitfields have no subregions they shouldn't
   // be using this function anyway.
   uint64_t Length = UINT64_MAX;
-
-  SVal Extent = R->getExtent(svalBuilder);
+  SVal Extent = Top->getExtent(SVB);
   if (nonloc::ConcreteInt *ExtentCI = dyn_cast<nonloc::ConcreteInt>(&Extent)) {
     const llvm::APSInt &ExtentInt = ExtentCI->getValue();
     assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned());
     // Extents are in bytes but region offsets are in bits. Be careful!
-    Length = ExtentInt.getLimitedValue() * Ctx.getCharWidth();
+    Length = ExtentInt.getLimitedValue() * SVB.getContext().getCharWidth();
   }
 
-  const ClusterBindings *Cluster = B.lookup(ClusterHead);
-  if (!Cluster)
-    return B;
-
-  ClusterBindingsRef Result(*Cluster, CBFactory);
-
-  // It is safe to iterate over the bindings as they are being changed
-  // because they are in an ImmutableMap.
-  for (ClusterBindings::iterator I = Cluster->begin(), E = Cluster->end();
+  for (ClusterBindings::iterator I = Cluster.begin(), E = Cluster.end();
        I != E; ++I) {
     BindingKey NextKey = I.getKey();
-    if (NextKey.getRegion() == SRKey.getRegion()) {
+    if (NextKey.getRegion() == TopKey.getRegion()) {
       // FIXME: This doesn't catch the case where we're really invalidating a
       // region with a symbolic offset. Example:
       //      R: points[i].y
       //   Next: points[0].x
 
-      if (NextKey.getOffset() > SRKey.getOffset() &&
-          NextKey.getOffset() - SRKey.getOffset() < Length) {
+      if (NextKey.getOffset() > TopKey.getOffset() &&
+          NextKey.getOffset() - TopKey.getOffset() < Length) {
         // Case 1: The next binding is inside the region we're invalidating.
         // Remove it.
-        Result = Result.remove(NextKey);
+        Keys.push_back(NextKey);
 
-      } else if (NextKey.getOffset() == SRKey.getOffset()) {
+      } else if (NextKey.getOffset() == TopKey.getOffset()) {
         // Case 2: The next binding is at the same offset as the region we're
         // invalidating. In this case, we need to leave default bindings alone,
         // since they may be providing a default value for a regions beyond what
@@ -771,33 +759,60 @@ RegionStoreManager::removeSubRegionBindings(RegionBindingsConstRef B,
         // FIXME: This is probably incorrect; consider invalidating an outer
         // struct whose first field is bound to a LazyCompoundVal.
         if (NextKey.isDirect())
-          Result = Result.remove(NextKey);
+          Keys.push_back(NextKey);
       }
-      
+
     } else if (NextKey.hasSymbolicOffset()) {
       const MemRegion *Base = NextKey.getConcreteOffsetRegion();
-      if (R->isSubRegionOf(Base)) {
+      if (Top->isSubRegionOf(Base)) {
         // Case 3: The next key is symbolic and we just changed something within
         // its concrete region. We don't know if the binding is still valid, so
         // we'll be conservative and remove it.
         if (NextKey.isDirect())
           if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions))
-            Result = Result.remove(NextKey);
+            Keys.push_back(NextKey);
       } else if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Base)) {
         // Case 4: The next key is symbolic, but we changed a known
         // super-region. In this case the binding is certainly no longer valid.
-        if (R == Base || BaseSR->isSubRegionOf(R))
+        if (Top == Base || BaseSR->isSubRegionOf(Top))
           if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions))
-            Result = Result.remove(NextKey);
+            Keys.push_back(NextKey);
       }
     }
   }
+}
+
+RegionBindingsRef
+RegionStoreManager::removeSubRegionBindings(RegionBindingsConstRef B,
+                                            const SubRegion *Top) {
+  BindingKey TopKey = BindingKey::Make(Top, BindingKey::Default);
+  const MemRegion *ClusterHead = TopKey.getBaseRegion();
+  if (Top == ClusterHead) {
+    // We can remove an entire cluster's bindings all in one go.
+    return B.remove(Top);
+  }
+
+  const ClusterBindings *Cluster = B.lookup(ClusterHead);
+  if (!Cluster)
+    return B;
+
+  SmallVector<BindingKey, 32> Keys;
+  collectSubRegionKeys(Keys, svalBuilder, *Cluster, Top, TopKey);
+
+  ClusterBindingsRef Result(*Cluster, CBFactory);
+  for (SmallVectorImpl<BindingKey>::const_iterator I = Keys.begin(),
+                                                   E = Keys.end();
+       I != E; ++I)
+    Result = Result.remove(*I);
 
   // If we're invalidating a region with a symbolic offset, we need to make sure
   // we don't treat the base region as uninitialized anymore.
   // FIXME: This isn't very precise; see the example in the loop.
-  if (HasSymbolicOffset)
-    Result = Result.add(SRKey, UnknownVal());
+  if (TopKey.hasSymbolicOffset()) {
+    const SubRegion *Concrete = TopKey.getConcreteOffsetRegion();
+    Result = Result.add(BindingKey::Make(Concrete, BindingKey::Default),
+                        UnknownVal());
+  }
 
   if (Result.isEmpty())
     return B.remove(ClusterHead);