[analyzer] Cache the "concrete offset base" for regions with symbolic offsets.

This makes it faster to access and invalidate bindings with symbolic offsets
by only computing this information once.

No intended functionality change.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161635 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
index 2018bd2..1281cfb 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
@@ -52,11 +52,19 @@
   int64_t Offset;
 
 public:
+  enum { Symbolic = INT64_MAX };
+
   RegionOffset() : R(0) {}
   RegionOffset(const MemRegion *r, int64_t off) : R(r), Offset(off) {}
 
   const MemRegion *getRegion() const { return R; }
-  int64_t getOffset() const { return Offset; }
+
+  bool hasSymbolicOffset() const { return Offset == Symbolic; }
+
+  int64_t getOffset() const {
+    assert(!hasSymbolicOffset());
+    return Offset;
+  }
 
   bool isValid() const { return R; }
 };
diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp
index b2e7055..58abc9a 100644
--- a/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -1038,12 +1038,14 @@
 
 RegionOffset MemRegion::getAsOffset() const {
   const MemRegion *R = this;
+  const MemRegion *SymbolicOffsetBase = 0;
   int64_t Offset = 0;
 
   while (1) {
     switch (R->getKind()) {
     default:
-      return RegionOffset();
+      return RegionOffset(R, RegionOffset::Symbolic);
+
     case SymbolicRegionKind:
     case AllocaRegionKind:
     case CompoundLiteralRegionKind:
@@ -1053,6 +1055,7 @@
     case ObjCIvarRegionKind:
     case CXXTempObjectRegionKind:
       goto Finish;
+
     case CXXBaseObjectRegionKind: {
       const CXXBaseObjectRegion *BOR = cast<CXXBaseObjectRegion>(R);
       R = BOR->getSuperRegion();
@@ -1070,8 +1073,14 @@
       const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl();
       if (!Child) {
         // We cannot compute the offset of the base class.
-        return RegionOffset();
+        SymbolicOffsetBase = R;
       }
+
+      // Don't bother calculating precise offsets if we already have a
+      // symbolic offset somewhere in the chain.
+      if (SymbolicOffsetBase)
+        continue;
+
       const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child);
 
       CharUnits BaseOffset;
@@ -1087,29 +1096,46 @@
     }
     case ElementRegionKind: {
       const ElementRegion *ER = cast<ElementRegion>(R);
-      QualType EleTy = ER->getValueType();
+      R = ER->getSuperRegion();
 
-      if (!IsCompleteType(getContext(), EleTy))
-        return RegionOffset();
+      QualType EleTy = ER->getValueType();
+      if (!IsCompleteType(getContext(), EleTy)) {
+        // We cannot compute the offset of the base class.
+        SymbolicOffsetBase = R;
+        continue;
+      }
 
       SVal Index = ER->getIndex();
       if (const nonloc::ConcreteInt *CI=dyn_cast<nonloc::ConcreteInt>(&Index)) {
+        // Don't bother calculating precise offsets if we already have a
+        // symbolic offset somewhere in the chain. 
+        if (SymbolicOffsetBase)
+          continue;
+
         int64_t i = CI->getValue().getSExtValue();
         // This type size is in bits.
         Offset += i * getContext().getTypeSize(EleTy);
       } else {
         // We cannot compute offset for non-concrete index.
-        return RegionOffset();
+        SymbolicOffsetBase = R;
       }
-      R = ER->getSuperRegion();
       break;
     }
     case FieldRegionKind: {
       const FieldRegion *FR = cast<FieldRegion>(R);
+      R = FR->getSuperRegion();
+
       const RecordDecl *RD = FR->getDecl()->getParent();
-      if (!RD->isCompleteDefinition())
+      if (!RD->isCompleteDefinition()) {
         // We cannot compute offset for incomplete type.
-        return RegionOffset();
+        SymbolicOffsetBase = R;
+      }
+
+      // Don't bother calculating precise offsets if we already have a
+      // symbolic offset somewhere in the chain.
+      if (SymbolicOffsetBase)
+        continue;
+
       // Get the field number.
       unsigned idx = 0;
       for (RecordDecl::field_iterator FI = RD->field_begin(), 
@@ -1120,13 +1146,14 @@
       const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
       // This is offset in bits.
       Offset += Layout.getFieldOffset(idx);
-      R = FR->getSuperRegion();
       break;
     }
     }
   }
 
  Finish:
+  if (SymbolicOffsetBase)
+    return RegionOffset(SymbolicOffsetBase, RegionOffset::Symbolic);
   return RegionOffset(R, Offset);
 }
 
diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp
index 90bd0f3..1ba46ea 100644
--- a/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -40,33 +40,42 @@
 namespace {
 class BindingKey {
 public:
-  enum Kind { Direct = 0x0, Default = 0x1 };
+  enum Kind { Default = 0x0, Direct = 0x1 };
 private:
-  enum { SYMBOLIC = UINT64_MAX };
+  enum { Symbolic = 0x2 };
 
-  llvm::PointerIntPair<const MemRegion *, 1, Kind> P;
-  uint64_t Offset;
+  llvm::PointerIntPair<const MemRegion *, 2> P;
+  uint64_t Data;
 
-  explicit BindingKey(const MemRegion *r, Kind k)
-    : P(r, k), Offset(SYMBOLIC) {}
+  explicit BindingKey(const MemRegion *r, const MemRegion *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");
+  }
   explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k)
-    : P(r, k), Offset(offset) {}
+    : P(r, k), Data(offset) {
+    assert(r && "Must have known regions.");
+    assert(getOffset() == offset && "Failed to store offset");
+  }
 public:
 
-  bool isDirect() const { return P.getInt() == Direct; }
-  bool hasSymbolicOffset() const { return Offset == SYMBOLIC; }
+  bool isDirect() const { return P.getInt() & Direct; }
+  bool hasSymbolicOffset() const { return P.getInt() & Symbolic; }
 
   const MemRegion *getRegion() const { return P.getPointer(); }
   uint64_t getOffset() const {
     assert(!hasSymbolicOffset());
-    return Offset;
+    return Data;
   }
 
-  const MemRegion *getConcreteOffsetRegion() const;
+  const MemRegion *getConcreteOffsetRegion() const {
+    assert(hasSymbolicOffset());
+    return reinterpret_cast<const MemRegion *>(static_cast<uintptr_t>(Data));
+  }
 
   void Profile(llvm::FoldingSetNodeID& ID) const {
     ID.AddPointer(P.getOpaqueValue());
-    ID.AddInteger(Offset);
+    ID.AddInteger(Data);
   }
 
   static BindingKey Make(const MemRegion *R, Kind k);
@@ -76,12 +85,12 @@
       return true;
     if (P.getOpaqueValue() > X.P.getOpaqueValue())
       return false;
-    return Offset < X.Offset;
+    return Data < X.Data;
   }
 
   bool operator==(const BindingKey &X) const {
     return P.getOpaqueValue() == X.P.getOpaqueValue() &&
-           Offset == X.Offset;
+           Data == X.Data;
   }
 
   bool isValid() const {
@@ -92,27 +101,10 @@
 
 BindingKey BindingKey::Make(const MemRegion *R, Kind k) {
   const RegionOffset &RO = R->getAsOffset();
-  if (RO.isValid())
-    return BindingKey(RO.getRegion(), RO.getOffset(), k);
+  if (RO.hasSymbolicOffset())
+    return BindingKey(R, RO.getRegion(), k);
 
-  return BindingKey(R, k);
-}
-
-const MemRegion *BindingKey::getConcreteOffsetRegion() const {
-  const MemRegion *R = getRegion();
-  if (!hasSymbolicOffset())
-    return R;
-
-  RegionOffset RO;
-  do {
-    const SubRegion *SR = dyn_cast<SubRegion>(R);
-    if (!SR)
-      break;
-    R = SR->getSuperRegion();
-    RO = R->getAsOffset();
-  } while (!RO.isValid());
-
-  return R;
+  return BindingKey(RO.getRegion(), RO.getOffset(), k);
 }
 
 namespace llvm {
@@ -561,7 +553,6 @@
   // by changing the data structure used for RegionBindings.
 
   BindingKey SRKey = BindingKey::Make(R, BindingKey::Default);
-  assert(SRKey.isValid());
   if (SRKey.hasSymbolicOffset()) {
     const SubRegion *Base = cast<SubRegion>(SRKey.getConcreteOffsetRegion());
     B = removeSubRegionBindings(B, Base);