Make the parent-map use significantly less memory.
authorManuel Klimek <klimek@google.com>
Wed, 21 May 2014 13:28:59 +0000 (13:28 +0000)
committerManuel Klimek <klimek@google.com>
Wed, 21 May 2014 13:28:59 +0000 (13:28 +0000)
On test files I ran this on, memory consumption overall went down from
2.5G to 2G, without performance regressions.
I also investigated making DynTypedNode by itself smaller (by pulling
out pointers for everything that doesn't fit in 8 bytes). This led to
another 200-300MB saved, but also introduced a significant regression in
performance due to the memory management overhead.

llvm-svn: 209297

clang/include/clang/AST/ASTContext.h
clang/lib/AST/ASTContext.cpp

index 229ac9f..1f97a4c 100644 (file)
@@ -424,7 +424,9 @@ public:
   typedef llvm::SmallVector<ast_type_traits::DynTypedNode, 1> ParentVector;
 
   /// \brief Maps from a node to its parents.
-  typedef llvm::DenseMap<const void *, ParentVector> ParentMap;
+  typedef llvm::DenseMap<const void *,
+                         llvm::PointerUnion<ast_type_traits::DynTypedNode *,
+                                            ParentVector *>> ParentMap;
 
   /// \brief Returns the parents of the given node.
   ///
@@ -2293,6 +2295,7 @@ private:
   friend class DeclContext;
   friend class DeclarationNameTable;
   void ReleaseDeclContextMaps();
+  void ReleaseParentMapEntries();
 
   std::unique_ptr<ParentMap> AllParents;
 
index 99547f8..2ea93ba 100644 (file)
@@ -755,6 +755,8 @@ ASTContext::ASTContext(LangOptions& LOpts, SourceManager &SM,
 }
 
 ASTContext::~ASTContext() {
+  ReleaseParentMapEntries();
+
   // Release the DenseMaps associated with DeclContext objects.
   // FIXME: Is this the ideal solution?
   ReleaseDeclContextMaps();
@@ -789,6 +791,18 @@ ASTContext::~ASTContext() {
   llvm::DeleteContainerSeconds(MangleNumberingContexts);
 }
 
+void ASTContext::ReleaseParentMapEntries() {
+  if (!AllParents) return;
+  for (const auto &Entry : *AllParents) {
+    if (Entry.second.is<ast_type_traits::DynTypedNode *>()) {
+      delete Entry.second.get<ast_type_traits::DynTypedNode *>();
+    } else {
+      assert(Entry.second.is<ParentVector *>());
+      delete Entry.second.get<ParentVector *>();
+    }
+  }
+}
+
 void ASTContext::AddDeallocation(void (*Callback)(void*), void *Data) {
   Deallocations[Callback].push_back(Data);
 }
@@ -8162,7 +8176,7 @@ namespace {
     bool TraverseNode(T *Node, bool(VisitorBase:: *traverse) (T *)) {
       if (!Node)
         return true;
-      if (ParentStack.size() > 0)
+      if (ParentStack.size() > 0) {
         // FIXME: Currently we add the same parent multiple times, for example
         // when we visit all subexpressions of template instantiations; this is
         // suboptimal, bug benign: the only way to visit those is with
@@ -8171,7 +8185,23 @@ namespace {
         // map. The main problem there is to implement hash functions /
         // comparison operators for all types that DynTypedNode supports that
         // do not have pointer identity.
-        (*Parents)[Node].push_back(ParentStack.back());
+        auto &NodeOrVector = (*Parents)[Node];
+        if (NodeOrVector.isNull()) {
+          NodeOrVector = new ast_type_traits::DynTypedNode(ParentStack.back());
+        } else if (NodeOrVector
+                       .template is<ast_type_traits::DynTypedNode *>()) {
+          auto *Node =
+              NodeOrVector.template get<ast_type_traits::DynTypedNode *>();
+          auto *Vector = new ASTContext::ParentVector(1, *Node);
+          Vector->push_back(ParentStack.back());
+          NodeOrVector = Vector;
+          delete Node;
+        } else {
+          assert(NodeOrVector.template is<ASTContext::ParentVector *>());
+          NodeOrVector.template get<ASTContext::ParentVector *>()->push_back(
+              ParentStack.back());
+        }
+      }
       ParentStack.push_back(ast_type_traits::DynTypedNode::create(*Node));
       bool Result = (this ->* traverse) (Node);
       ParentStack.pop_back();
@@ -8209,7 +8239,11 @@ ASTContext::getParents(const ast_type_traits::DynTypedNode &Node) {
   if (I == AllParents->end()) {
     return ParentVector();
   }
-  return I->second;
+  if (I->second.is<ast_type_traits::DynTypedNode *>()) {
+    return ParentVector(1, *I->second.get<ast_type_traits::DynTypedNode *>());
+  }
+  const auto &Parents = *I->second.get<ParentVector *>();
+  return ParentVector(Parents.begin(), Parents.end());
 }
 
 bool