From 0895d1513772eca5a20c552976209fd7f58b993f Mon Sep 17 00:00:00 2001
From: Argyrios Kyrtzidis <akyrtzi@gmail.com>
Date: Sat, 12 Feb 2011 07:50:47 +0000
Subject: [PATCH] When reading the AST, delay loading of the redeclaration
 chain to avoid deeply nested calls. Temporarily set the first (canonical)
 declaration as the previous one, which is the one that matters, and mark the
 real previous DeclID to be loaded & attached later on.

Fixes rdar://8956193.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@125434 91177308-0d34-0410-b5e6-96231b3b80d8
---
 include/clang/Serialization/ASTReader.h |   7 ++
 lib/Serialization/ASTReader.cpp         |   7 ++
 lib/Serialization/ASTReaderDecl.cpp     |  58 +++++++++++--
 lib/Serialization/ASTWriterDecl.cpp     |  14 +++-
 test/Index/c-index-redecls.c            | 107 ++++++++++++++++++++++++
 5 files changed, 181 insertions(+), 12 deletions(-)
 create mode 100644 test/Index/c-index-redecls.c

diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h
index 23701248d00..015ccc9b3f8 100644
--- a/include/clang/Serialization/ASTReader.h
+++ b/include/clang/Serialization/ASTReader.h
@@ -721,6 +721,13 @@ private:
   /// Objective-C protocols.
   std::deque<Decl *> InterestingDecls;
 
+  /// \brief We delay loading of the previous declaration chain to avoid
+  /// deeply nested calls when there are many redeclarations.
+  std::deque<std::pair<Decl *, serialization::DeclID> > PendingPreviousDecls;
+
+  /// \brief Ready to load the previous declaration of the given Decl.
+  void loadAndAttachPreviousDecl(Decl *D, serialization::DeclID ID);
+
   /// \brief When reading a Stmt tree, Stmt operands are placed in this stack.
   llvm::SmallVector<Stmt *, 16> StmtStack;
 
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index d2167376ff2..5e3fa132e69 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -4815,6 +4815,13 @@ void ASTReader::FinishedDeserializing() {
       PendingIdentifierInfos.pop_front();
     }
 
+    // Ready to load previous declarations of Decls that were delayed.
+    while (!PendingPreviousDecls.empty()) {
+      loadAndAttachPreviousDecl(PendingPreviousDecls.front().first,
+                                PendingPreviousDecls.front().second);
+      PendingPreviousDecls.pop_front();
+    }
+
     // We are not in recursive loading, so it's safe to pass the "interesting"
     // decls to the consumer.
     if (Consumer)
diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp
index 56c33ca913d..279d081098f 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -75,6 +75,8 @@ namespace clang {
       : Reader(Reader), F(F), Cursor(Cursor), ThisDeclID(thisDeclID),
         Record(Record), Idx(Idx), TypeIDForTypeDecl(0) { }
 
+    static void attachPreviousDecl(Decl *D, Decl *previous);
+
     void Visit(Decl *D);
 
     void UpdateDecl(Decl *D, const RecordData &Record);
@@ -981,13 +983,22 @@ void ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
   // can be used while this is still initializing.
 
   assert(D->CommonOrPrev.isNull() && "getCommonPtr was called earlier on this");
-  RedeclarableTemplateDecl *PrevDecl =
-      cast_or_null<RedeclarableTemplateDecl>(Reader.GetDecl(Record[Idx++]));
-  assert((PrevDecl == 0 || PrevDecl->getKind() == D->getKind()) &&
-         "PrevDecl kind mismatch");
-  if (PrevDecl)
-    D->CommonOrPrev = PrevDecl;
-  if (PrevDecl == 0) {
+  DeclID PreviousDeclID = Record[Idx++];
+  DeclID FirstDeclID =  PreviousDeclID ? Record[Idx++] : 0;
+  // We delay loading of the redeclaration chain to avoid deeply nested calls.
+  // We temporarily set the first (canonical) declaration as the previous one
+  // which is the one that matters and mark the real previous DeclID to be
+  // loaded & attached later on.
+  RedeclarableTemplateDecl *FirstDecl =
+      cast_or_null<RedeclarableTemplateDecl>(Reader.GetDecl(FirstDeclID));
+  assert((FirstDecl == 0 || FirstDecl->getKind() == D->getKind()) &&
+         "FirstDecl kind mismatch");
+  if (FirstDecl) {
+    D->CommonOrPrev = FirstDecl;
+    // Mark the real previous DeclID to be loaded & attached later on.
+    if (PreviousDeclID != FirstDeclID)
+      Reader.PendingPreviousDecls.push_back(std::make_pair(D, PreviousDeclID));
+  } else {
     D->CommonOrPrev = D->newCommon(*Reader.getContext());
     if (RedeclarableTemplateDecl *RTD
           = cast_or_null<RedeclarableTemplateDecl>(Reader.GetDecl(Record[Idx++]))) {
@@ -1224,10 +1235,20 @@ void ASTDeclReader::VisitRedeclarable(Redeclarable<T> *D) {
                 " reading");
   case NoRedeclaration:
     break;
-  case PointsToPrevious:
+  case PointsToPrevious: {
+    DeclID PreviousDeclID = Record[Idx++];
+    DeclID FirstDeclID = Record[Idx++];
+    // We delay loading of the redeclaration chain to avoid deeply nested calls.
+    // We temporarily set the first (canonical) declaration as the previous one
+    // which is the one that matters and mark the real previous DeclID to be
+    // loaded & attached later on.
     D->RedeclLink = typename Redeclarable<T>::PreviousDeclLink(
-                                cast_or_null<T>(Reader.GetDecl(Record[Idx++])));
+                                cast_or_null<T>(Reader.GetDecl(FirstDeclID)));
+    if (PreviousDeclID != FirstDeclID)
+      Reader.PendingPreviousDecls.push_back(std::make_pair(static_cast<T*>(D),
+                                                           PreviousDeclID));
     break;
+  }
   case PointsToLatest:
     D->RedeclLink = typename Redeclarable<T>::LatestDeclLink(
                                 cast_or_null<T>(Reader.GetDecl(Record[Idx++])));
@@ -1327,6 +1348,25 @@ ASTReader::DeclCursorForIndex(unsigned Index, DeclID ID) {
   return RecordLocation(F, F->DeclOffsets[Index]);
 }
 
+void ASTDeclReader::attachPreviousDecl(Decl *D, Decl *previous) {
+  assert(D && previous);
+  if (TagDecl *TD = dyn_cast<TagDecl>(D)) {
+    TD->RedeclLink.setPointer(cast<TagDecl>(previous));
+  } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+    FD->RedeclLink.setPointer(cast<FunctionDecl>(previous));
+  } else if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
+    VD->RedeclLink.setPointer(cast<VarDecl>(previous));
+  } else {
+    RedeclarableTemplateDecl *TD = cast<RedeclarableTemplateDecl>(D);
+    TD->CommonOrPrev = cast<RedeclarableTemplateDecl>(previous);
+  }
+}
+
+void ASTReader::loadAndAttachPreviousDecl(Decl *D, serialization::DeclID ID) {
+  Decl *previous = GetDecl(ID);
+  ASTDeclReader::attachPreviousDecl(D, previous);
+}
+
 /// \brief Read the declaration at the given offset from the AST file.
 Decl *ASTReader::ReadDeclRecord(unsigned Index, DeclID ID) {
   RecordLocation Loc = DeclCursorForIndex(Index, ID);
diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp
index b0ed8bc59f8..17ee85ad279 100644
--- a/lib/Serialization/ASTWriterDecl.cpp
+++ b/lib/Serialization/ASTWriterDecl.cpp
@@ -862,6 +862,9 @@ void ASTDeclWriter::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
   // getCommonPtr() can be used while this is still initializing.
 
   Writer.AddDeclRef(D->getPreviousDeclaration(), Record);
+  if (D->getPreviousDeclaration())
+    Writer.AddDeclRef(D->getFirstDeclaration(), Record);
+
   if (D->getPreviousDeclaration() == 0) {
     // This TemplateDecl owns the CommonPtr; write it.
     assert(D->isCanonicalDecl());
@@ -1075,9 +1078,14 @@ void ASTDeclWriter::VisitRedeclarable(Redeclarable<T> *D) {
   if (D->RedeclLink.getNext() == D) {
     Record.push_back(NoRedeclaration);
   } else {
-    Record.push_back(D->RedeclLink.NextIsPrevious() ? PointsToPrevious
-                                                    : PointsToLatest);
-    Writer.AddDeclRef(D->RedeclLink.getPointer(), Record);
+    if (D->RedeclLink.NextIsPrevious()) {
+      Record.push_back(PointsToPrevious);
+      Writer.AddDeclRef(D->getPreviousDeclaration(), Record);
+      Writer.AddDeclRef(D->getFirstDeclaration(), Record);
+    } else {
+      Record.push_back(PointsToLatest);
+      Writer.AddDeclRef(D->RedeclLink.getPointer(), Record);
+    }
   }
 
   T *First = D->getFirstDeclaration();
diff --git a/test/Index/c-index-redecls.c b/test/Index/c-index-redecls.c
new file mode 100644
index 00000000000..0cf2f032a3c
--- /dev/null
+++ b/test/Index/c-index-redecls.c
@@ -0,0 +1,107 @@
+// RUN: %clang_cc1 -emit-pch -o %t.ast %s
+// RUN: c-index-test -test-load-tu %t.ast all
+
+// rdar://8956193 - We would blow the thread stack because of nested calls due
+//                  to redeclarations.
+
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
-- 
GitLab