r245779 - [modules] Rearrange how redeclaration chains are loaded, to remove a walk over

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 21 18:47:18 PDT 2015


Author: rsmith
Date: Fri Aug 21 20:47:18 2015
New Revision: 245779

URL: http://llvm.org/viewvc/llvm-project?rev=245779&view=rev
Log:
[modules] Rearrange how redeclaration chains are loaded, to remove a walk over
all modules and reduce the number of declarations we load when loading a
redeclaration chain.

The new approach is:
 * when loading the first declaration of an entity within a module file, we
   first load all declarations of the entity that were imported into that
   module file, and then load all the other declarations of that entity from
   that module file and build a suitable decl chain from them
 * when loading any other declaration of an entity, we first load the first
   declaration from the same module file

As before, we complete redecl chains through name lookup where necessary.

To make this work, I also had to change the way that template specializations
are stored -- it no longer suffices to track only canonical specializations; we
now emit all "first local" declarations when emitting a list of specializations
for a template.

On one testcase with several thousand imported module files, this reduces the
total runtime by 72%.

Modified:
    cfe/trunk/include/clang/Serialization/ASTWriter.h
    cfe/trunk/lib/Serialization/ASTReader.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
    cfe/trunk/lib/Serialization/ASTWriter.cpp
    cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
    cfe/trunk/test/Modules/cxx-templates.cpp

Modified: cfe/trunk/include/clang/Serialization/ASTWriter.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTWriter.h?rev=245779&r1=245778&r2=245779&view=diff
==============================================================================
--- cfe/trunk/include/clang/Serialization/ASTWriter.h (original)
+++ cfe/trunk/include/clang/Serialization/ASTWriter.h Fri Aug 21 20:47:18 2015
@@ -405,6 +405,10 @@ private:
   /// \brief The set of declarations that may have redeclaration chains that
   /// need to be serialized.
   llvm::SmallVector<const Decl *, 16> Redeclarations;
+
+  /// \brief A cache of the first local declaration for "interesting"
+  /// redeclaration chains.
+  llvm::DenseMap<const Decl *, const Decl *> FirstLocalDeclCache;
                                       
   /// \brief Statements that we've encountered while serializing a
   /// declaration or type.
@@ -676,6 +680,10 @@ public:
                           const ASTTemplateArgumentListInfo *ASTTemplArgList,
                           RecordDataImpl &Record);
 
+  /// \brief Find the first local declaration of a given local redeclarable
+  /// decl.
+  const Decl *getFirstLocalDecl(const Decl *D);
+
   /// \brief Emit a reference to a declaration.
   void AddDeclRef(const Decl *D, RecordDataImpl &Record);
 
@@ -857,12 +865,6 @@ public:
   void CompletedTagDefinition(const TagDecl *D) override;
   void AddedVisibleDecl(const DeclContext *DC, const Decl *D) override;
   void AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D) override;
-  void AddedCXXTemplateSpecialization(const ClassTemplateDecl *TD,
-                             const ClassTemplateSpecializationDecl *D) override;
-  void AddedCXXTemplateSpecialization(const VarTemplateDecl *TD,
-                               const VarTemplateSpecializationDecl *D) override;
-  void AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD,
-                                      const FunctionDecl *D) override;
   void ResolvedExceptionSpec(const FunctionDecl *FD) override;
   void DeducedReturnType(const FunctionDecl *FD, QualType ReturnType) override;
   void ResolvedOperatorDelete(const CXXDestructorDecl *DD,

Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=245779&r1=245778&r2=245779&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReader.cpp Fri Aug 21 20:47:18 2015
@@ -8123,11 +8123,8 @@ void ASTReader::finishPendingActions() {
     PendingIncompleteDeclChains.clear();
 
     // Load pending declaration chains.
-    for (unsigned I = 0; I != PendingDeclChains.size(); ++I) {
-      PendingDeclChainsKnown.erase(PendingDeclChains[I]);
+    for (unsigned I = 0; I != PendingDeclChains.size(); ++I)
       loadPendingDeclChain(PendingDeclChains[I]);
-    }
-    assert(PendingDeclChainsKnown.empty());
     PendingDeclChains.clear();
 
     assert(RedeclsDeserialized.empty() && "some redecls not wired up");

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=245779&r1=245778&r2=245779&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Fri Aug 21 20:47:18 2015
@@ -147,12 +147,6 @@ namespace clang {
       }
 
       ~RedeclarableResult() {
-        if (FirstID && Owning &&
-            isRedeclarableDeclKind(LoadedDecl->getKind())) {
-          auto Canon = Reader.GetDecl(FirstID)->getCanonicalDecl();
-          if (Reader.PendingDeclChainsKnown.insert(Canon).second)
-            Reader.PendingDeclChains.push_back(Canon);
-        }
       }
 
       /// \brief Note that a RedeclarableDecl is not actually redeclarable.
@@ -2186,23 +2180,33 @@ ASTDeclReader::RedeclarableResult
 ASTDeclReader::VisitRedeclarable(Redeclarable<T> *D) {
   DeclID FirstDeclID = ReadDeclID(Record, Idx);
   Decl *MergeWith = nullptr;
+
   bool IsKeyDecl = ThisDeclID == FirstDeclID;
+  bool IsFirstLocalDecl = false;
 
   // 0 indicates that this declaration was the only declaration of its entity,
   // and is used for space optimization.
   if (FirstDeclID == 0) {
     FirstDeclID = ThisDeclID;
     IsKeyDecl = true;
+    IsFirstLocalDecl = true;
   } else if (unsigned N = Record[Idx++]) {
-    IsKeyDecl = false;
+    // This declaration was the first local declaration, but may have imported
+    // other declarations.
+    IsKeyDecl = N == 1;
+    IsFirstLocalDecl = true;
 
     // We have some declarations that must be before us in our redeclaration
     // chain. Read them now, and remember that we ought to merge with one of
     // them.
     // FIXME: Provide a known merge target to the second and subsequent such
     // declaration.
-    for (unsigned I = 0; I != N; ++I)
+    for (unsigned I = 0; I != N - 1; ++I)
       MergeWith = ReadDecl(Record, Idx/*, MergeWith*/);
+  } else {
+    // This declaration was not the first local declaration. Read the first
+    // local declaration now, to trigger the import of other redeclarations.
+    (void)ReadDecl(Record, Idx);
   }
 
   T *FirstDecl = cast_or_null<T>(Reader.GetDecl(FirstDeclID));
@@ -2214,13 +2218,21 @@ ASTDeclReader::VisitRedeclarable(Redecla
     D->RedeclLink = Redeclarable<T>::PreviousDeclLink(FirstDecl);
     D->First = FirstDecl->getCanonicalDecl();
   }    
-  
+
   // Note that this declaration has been deserialized.
-  Reader.RedeclsDeserialized.insert(static_cast<T *>(D));
-                             
+  T *DAsT = static_cast<T*>(D);
+  Reader.RedeclsDeserialized.insert(DAsT);
+
+  // Note that we need to load local redeclarations of this decl and build a
+  // decl chain for them. This must happen *after* we perform the preloading
+  // above; this ensures that the redeclaration chain is built in the correct
+  // order.
+  if (IsFirstLocalDecl)
+    Reader.PendingDeclChains.push_back(DAsT);
+
   // The result structure takes care to note that we need to load the 
   // other declaration chains for this ID.
-  return RedeclarableResult(Reader, FirstDeclID, static_cast<T *>(D), MergeWith,
+  return RedeclarableResult(Reader, FirstDeclID, DAsT, MergeWith,
                             IsKeyDecl);
 }
 
@@ -2330,11 +2342,8 @@ void ASTDeclReader::mergeRedeclarable(Re
           TemplatePatternID, Redecl.isKeyDecl());
 
     // If this declaration is a key declaration, make a note of that.
-    if (Redecl.isKeyDecl()) {
+    if (Redecl.isKeyDecl())
       Reader.KeyDecls[ExistingCanon].push_back(Redecl.getFirstID());
-      if (Reader.PendingDeclChainsKnown.insert(ExistingCanon).second)
-        Reader.PendingDeclChains.push_back(ExistingCanon);
-    }
   }
 }
 
@@ -3424,15 +3433,12 @@ namespace {
     ASTReader &Reader;
     SmallVectorImpl<DeclID> &SearchDecls;
     llvm::SmallPtrSetImpl<Decl *> &Deserialized;
-    GlobalDeclID CanonID;
     SmallVector<Decl *, 4> Chain;
 
   public:
     RedeclChainVisitor(ASTReader &Reader, SmallVectorImpl<DeclID> &SearchDecls,
-                       llvm::SmallPtrSetImpl<Decl *> &Deserialized,
-                       GlobalDeclID CanonID)
-      : Reader(Reader), SearchDecls(SearchDecls), Deserialized(Deserialized),
-        CanonID(CanonID) {
+                       llvm::SmallPtrSetImpl<Decl *> &Deserialized)
+      : Reader(Reader), SearchDecls(SearchDecls), Deserialized(Deserialized) {
       assert(std::is_sorted(SearchDecls.begin(), SearchDecls.end()));
     }
 
@@ -3518,10 +3524,10 @@ namespace {
           break;
       }
 
-      if (LocalSearchDeclID && LocalSearchDeclID != CanonID) {
+      assert(LocalSearchDeclID);
+      if (LocalSearchDeclID) {
         // If the search decl was from this module, add it to the chain.
         // Note, the chain is sorted from newest to oldest, so this goes last.
-        // We exclude the canonical declaration; it implicitly goes at the end.
         addToChain(Reader.GetDecl(LocalSearchDeclID));
       }
 
@@ -3531,26 +3537,14 @@ namespace {
   };
 }
 
-void ASTReader::loadPendingDeclChain(Decl *CanonDecl) {
-  // The decl might have been merged into something else after being added to
-  // our list. If it was, just skip it.
-  if (!CanonDecl->isCanonicalDecl())
-    return;
-
+void ASTReader::loadPendingDeclChain(Decl *FirstLocal) {
   // Determine the set of declaration IDs we'll be searching for.
-  SmallVector<DeclID, 16> SearchDecls;
-  GlobalDeclID CanonID = CanonDecl->getGlobalID();
-  if (CanonID)
-    SearchDecls.push_back(CanonDecl->getGlobalID()); // Always first.
-  KeyDeclsMap::iterator KeyPos = KeyDecls.find(CanonDecl);
-  if (KeyPos != KeyDecls.end())
-    SearchDecls.append(KeyPos->second.begin(), KeyPos->second.end());
-  llvm::array_pod_sort(SearchDecls.begin(), SearchDecls.end());
+  SmallVector<DeclID, 1> SearchDecls;
+  SearchDecls.push_back(FirstLocal->getGlobalID());
 
   // Build up the list of redeclarations.
-  RedeclChainVisitor Visitor(*this, SearchDecls, RedeclsDeserialized, CanonID);
-  ModuleMgr.visit(Visitor);
-  RedeclsDeserialized.erase(CanonDecl);
+  RedeclChainVisitor Visitor(*this, SearchDecls, RedeclsDeserialized);
+  Visitor(*getOwningModuleFile(FirstLocal));
 
   // Retrieve the chains.
   ArrayRef<Decl *> Chain = Visitor.getChain();
@@ -3561,11 +3555,14 @@ void ASTReader::loadPendingDeclChain(Dec
   //
   // FIXME: We have three different dispatches on decl kind here; maybe
   // we should instead generate one loop per kind and dispatch up-front?
+  Decl *CanonDecl = FirstLocal->getCanonicalDecl();
   Decl *MostRecent = ASTDeclReader::getMostRecentDecl(CanonDecl);
   if (!MostRecent)
     MostRecent = CanonDecl;
   for (unsigned I = 0, N = Chain.size(); I != N; ++I) {
     auto *D = Chain[N - I - 1];
+    if (D == CanonDecl)
+      continue;
     ASTDeclReader::attachPreviousDecl(*this, D, MostRecent, CanonDecl);
     MostRecent = D;
   }

Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=245779&r1=245778&r2=245779&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriter.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriter.cpp Fri Aug 21 20:47:18 2015
@@ -3799,41 +3799,39 @@ void ASTWriter::WriteRedeclarations() {
   SmallVector<serialization::LocalRedeclarationsInfo, 2> LocalRedeclsMap;
 
   for (unsigned I = 0, N = Redeclarations.size(); I != N; ++I) {
-    const Decl *Key = Redeclarations[I];
-    assert((Chain ? Chain->getKeyDeclaration(Key) == Key
-                  : Key->isFirstDecl()) &&
-           "not the key declaration");
+    const Decl *FirstLocal = Redeclarations[I];
+    assert(!FirstLocal->isFromASTFile() &&
+           (!FirstLocal->getPreviousDecl() ||
+            FirstLocal->getPreviousDecl()->isFromASTFile() ||
+            getDeclID(FirstLocal->getPreviousDecl()) < NUM_PREDEF_DECL_IDS) &&
+           "not the first local declaration");
+    assert(getDeclID(FirstLocal) >= NUM_PREDEF_DECL_IDS &&
+           "should not have predefined decl as first decl");
 
-    const Decl *First = Key->getCanonicalDecl();
-    const Decl *MostRecent = First->getMostRecentDecl();
-
-    assert((getDeclID(First) >= NUM_PREDEF_DECL_IDS || First == Key) &&
-           "should not have imported key decls for predefined decl");
-
-    // If we only have a single declaration, there is no point in storing
-    // a redeclaration chain.
-    if (First == MostRecent)
-      continue;
-    
     unsigned Offset = LocalRedeclChains.size();
     unsigned Size = 0;
     LocalRedeclChains.push_back(0); // Placeholder for the size.
 
     // Collect the set of local redeclarations of this declaration, from newest
     // to oldest.
-    for (const Decl *Prev = MostRecent; Prev;
-         Prev = Prev->getPreviousDecl()) { 
-      if (!Prev->isFromASTFile() && Prev != Key) {
+    for (const Decl *Prev = FirstLocal->getMostRecentDecl(); Prev != FirstLocal;
+         Prev = Prev->getPreviousDecl()) {
+      if (!Prev->isFromASTFile()) {
         AddDeclRef(Prev, LocalRedeclChains);
         ++Size;
       }
     }
 
+    // If we only have a single local declaration, there is no point in storing
+    // a redeclaration chain.
+    if (LocalRedeclChains.size() == 1)
+      continue;
+
     LocalRedeclChains[Offset] = Size;
 
     // Add the mapping from the first ID from the AST to the set of local
     // declarations.
-    LocalRedeclarationsInfo Info = { getDeclID(Key), Offset };
+    LocalRedeclarationsInfo Info = { getDeclID(FirstLocal), Offset };
     LocalRedeclsMap.push_back(Info);
 
     assert(N == Redeclarations.size() && 
@@ -4145,8 +4143,6 @@ void ASTWriter::WriteASTCore(Sema &SemaR
     if (D) {
       assert(D->isCanonicalDecl() && "predefined decl is not canonical");
       DeclIDs[D] = ID;
-      if (D->getMostRecentDecl() != D)
-        Redeclarations.push_back(D);
     }
   };
   RegisterPredefDecl(Context.getTranslationUnitDecl(),
@@ -5730,42 +5726,6 @@ void ASTWriter::AddedCXXImplicitMember(c
   DeclUpdates[RD].push_back(DeclUpdate(UPD_CXX_ADDED_IMPLICIT_MEMBER, D));
 }
 
-void ASTWriter::AddedCXXTemplateSpecialization(const ClassTemplateDecl *TD,
-                                     const ClassTemplateSpecializationDecl *D) {
-  // The specializations set is kept in the canonical template.
-  TD = TD->getCanonicalDecl();
-  if (!(!D->isFromASTFile() && TD->isFromASTFile()))
-    return; // Not a source specialization added to a template from PCH.
-
-  assert(!WritingAST && "Already writing the AST!");
-  DeclUpdates[TD].push_back(DeclUpdate(UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION,
-                                       D));
-}
-
-void ASTWriter::AddedCXXTemplateSpecialization(
-    const VarTemplateDecl *TD, const VarTemplateSpecializationDecl *D) {
-  // The specializations set is kept in the canonical template.
-  TD = TD->getCanonicalDecl();
-  if (!(!D->isFromASTFile() && TD->isFromASTFile()))
-    return; // Not a source specialization added to a template from PCH.
-
-  assert(!WritingAST && "Already writing the AST!");
-  DeclUpdates[TD].push_back(DeclUpdate(UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION,
-                                       D));
-}
-
-void ASTWriter::AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD,
-                                               const FunctionDecl *D) {
-  // The specializations set is kept in the canonical template.
-  TD = TD->getCanonicalDecl();
-  if (!(!D->isFromASTFile() && TD->isFromASTFile()))
-    return; // Not a source specialization added to a template from PCH.
-
-  assert(!WritingAST && "Already writing the AST!");
-  DeclUpdates[TD].push_back(DeclUpdate(UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION,
-                                       D));
-}
-
 void ASTWriter::ResolvedExceptionSpec(const FunctionDecl *FD) {
   assert(!DoneWritingDeclsAndTypes && "Already done writing updates!");
   if (!Chain) return;

Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=245779&r1=245778&r2=245779&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Fri Aug 21 20:47:18 2015
@@ -159,6 +159,19 @@ namespace clang {
       Writer.AddStmt(FD->getBody());
     }
 
+    /// Add to the record the first declaration from each module file that
+    /// provides a declaration of D. The intent is to provide a sufficient
+    /// set such that reloading this set will load all current redeclarations.
+    void AddFirstDeclFromEachModule(const Decl *D, bool IncludeLocal) {
+      llvm::MapVector<ModuleFile*, const Decl*> Firsts;
+      // FIXME: We can skip entries that we know are implied by others.
+      for (const Decl *R = D->getMostRecentDecl(); R; R = R->getPreviousDecl())
+        if (IncludeLocal || R->isFromASTFile())
+          Firsts[Writer.Chain->getOwningModuleFile(R)] = R;
+      for (const auto &F : Firsts)
+        Writer.AddDeclRef(F.second, Record);
+    }
+
     /// Get the specialization decl from an entry in the specialization list.
     template <typename EntryType>
     typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType *
@@ -194,20 +207,46 @@ namespace clang {
       if (auto *LS = Common->LazySpecializations)
         LazySpecializations = ArrayRef<DeclID>(LS + 1, LS + 1 + LS[0]);
 
-      Record.push_back(Specializations.size() +
-                       PartialSpecializations.size() +
-                       LazySpecializations.size());
+      // Add a slot to the record for the number of specializations.
+      unsigned I = Record.size();
+      Record.push_back(0);
+
       for (auto &Entry : Specializations) {
         auto *D = getSpecializationDecl(Entry);
         assert(D->isCanonicalDecl() && "non-canonical decl in set");
-        Writer.AddDeclRef(D, Record);
+        AddFirstDeclFromEachModule(D, /*IncludeLocal*/true);
       }
       for (auto &Entry : PartialSpecializations) {
         auto *D = getSpecializationDecl(Entry);
         assert(D->isCanonicalDecl() && "non-canonical decl in set");
-        Writer.AddDeclRef(D, Record);
+        AddFirstDeclFromEachModule(D, /*IncludeLocal*/true);
       }
       Record.append(LazySpecializations.begin(), LazySpecializations.end());
+
+      // Update the size entry we added earlier.
+      Record[I] = Record.size() - I - 1;
+    }
+
+    /// Ensure that this template specialization is associated with the specified
+    /// template on reload.
+    void RegisterTemplateSpecialization(const Decl *Template,
+                                        const Decl *Specialization) {
+      Template = Template->getCanonicalDecl();
+
+      // If the canonical template is local, we'll write out this specialization
+      // when we emit it.
+      // FIXME: We can do the same thing if there is any local declaration of
+      // the template, to avoid emitting an update record.
+      if (!Template->isFromASTFile())
+        return;
+
+      // We only need to associate the first local declaration of the
+      // specialization. The other declarations will get pulled in by it.
+      if (Writer.getFirstLocalDecl(Specialization) != Specialization)
+        return;
+
+      Writer.DeclUpdates[Template].push_back(ASTWriter::DeclUpdate(
+          UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION, Specialization));
     }
   };
 }
@@ -479,6 +518,9 @@ void ASTDeclWriter::VisitFunctionDecl(Fu
   case FunctionDecl::TK_FunctionTemplateSpecialization: {
     FunctionTemplateSpecializationInfo *
       FTSInfo = D->getTemplateSpecializationInfo();
+
+    RegisterTemplateSpecialization(FTSInfo->getTemplate(), D);
+
     Writer.AddDeclRef(FTSInfo->getTemplate(), Record);
     Record.push_back(FTSInfo->getTemplateSpecializationKind());
     
@@ -1249,6 +1291,8 @@ void ASTDeclWriter::VisitClassTemplateDe
 
 void ASTDeclWriter::VisitClassTemplateSpecializationDecl(
                                            ClassTemplateSpecializationDecl *D) {
+  RegisterTemplateSpecialization(D->getSpecializedTemplate(), D);
+
   VisitCXXRecordDecl(D);
 
   llvm::PointerUnion<ClassTemplateDecl *,
@@ -1308,6 +1352,8 @@ void ASTDeclWriter::VisitVarTemplateDecl
 
 void ASTDeclWriter::VisitVarTemplateSpecializationDecl(
     VarTemplateSpecializationDecl *D) {
+  RegisterTemplateSpecialization(D->getSpecializedTemplate(), D);
+
   VisitVarDecl(D);
 
   llvm::PointerUnion<VarTemplateDecl *, VarTemplatePartialSpecializationDecl *>
@@ -1478,48 +1524,61 @@ void ASTDeclWriter::VisitDeclContext(Dec
   Record.push_back(VisibleOffset);
 }
 
-/// Determine whether D is the first declaration in its redeclaration chain that
-/// is not from an AST file.
-template <typename T>
-static bool isFirstLocalDecl(Redeclarable<T> *D) {
-  assert(D && !static_cast<T*>(D)->isFromASTFile());
-  do
-    D = D->getPreviousDecl();
-  while (D && static_cast<T*>(D)->isFromASTFile());
-  return !D;
+/// \brief Is this a local declaration (that is, one that will be written to
+/// our AST file)? This is the case for declarations that are neither imported
+/// from another AST file nor predefined.
+static bool isLocalDecl(ASTWriter &W, const Decl *D) {
+  if (D->isFromASTFile())
+    return false;
+  return W.getDeclID(D) >= NUM_PREDEF_DECL_IDS;
+}
+
+const Decl *ASTWriter::getFirstLocalDecl(const Decl *D) {
+  assert(isLocalDecl(*this, D) && "expected a local declaration");
+
+  const Decl *Canon = D->getCanonicalDecl();
+  if (isLocalDecl(*this, Canon))
+    return Canon;
+
+  const Decl *&CacheEntry = FirstLocalDeclCache[Canon];
+  if (CacheEntry)
+    return CacheEntry;
+
+  for (const Decl *Redecl = D; Redecl; Redecl = Redecl->getPreviousDecl())
+    if (isLocalDecl(*this, Redecl))
+      D = Redecl;
+  return CacheEntry = D;
 }
 
 template <typename T>
 void ASTDeclWriter::VisitRedeclarable(Redeclarable<T> *D) {
   T *First = D->getFirstDecl();
   T *MostRecent = First->getMostRecentDecl();
+  T *DAsT = static_cast<T *>(D);
   if (MostRecent != First) {
-    assert(isRedeclarableDeclKind(static_cast<T *>(D)->getKind()) &&
+    assert(isRedeclarableDeclKind(DAsT->getKind()) &&
            "Not considered redeclarable?");
 
     Writer.AddDeclRef(First, Record);
 
-    // In a modules build, emit a list of all imported key declarations
-    // (excluding First, if it was imported), so that we can be sure that all
-    // redeclarations visible to this module are before D in the redecl chain.
-    unsigned I = Record.size();
-    Record.push_back(0);
-    if (Context.getLangOpts().Modules && Writer.Chain) {
-      if (isFirstLocalDecl(D)) {
-        Writer.Chain->forEachImportedKeyDecl(First, [&](const Decl *D) {
-          if (D != First)
-            Writer.AddDeclRef(D, Record);
-        });
-        Record[I] = Record.size() - I - 1;
-
-        // Write a redeclaration chain, attached to the first key decl.
-        Writer.Redeclarations.push_back(Writer.Chain->getKeyDeclaration(First));
-      }
-    } else if (D == First || D->getPreviousDecl()->isFromASTFile()) {
-      assert(isFirstLocalDecl(D) && "imported decl after local decl");
-
-      // Write a redeclaration chain attached to the first decl.
-      Writer.Redeclarations.push_back(First);
+    // Write out a list of local redeclarations of this declaration if it's the
+    // first local declaration in the chain.
+    const Decl *FirstLocal = Writer.getFirstLocalDecl(DAsT);
+    if (DAsT == FirstLocal) {
+      Writer.Redeclarations.push_back(DAsT);
+
+      // Emit a list of all imported first declarations so that we can be sure
+      // that all redeclarations visible to this module are before D in the
+      // redecl chain.
+      unsigned I = Record.size();
+      Record.push_back(0);
+      if (Writer.Chain)
+        AddFirstDeclFromEachModule(DAsT, /*IncludeLocal*/false);
+      // This is the number of imported first declarations + 1.
+      Record[I] = Record.size() - I;
+    } else {
+      Record.push_back(0);
+      Writer.AddDeclRef(FirstLocal, Record);
     }
 
     // Make sure that we serialize both the previous and the most-recent 

Modified: cfe/trunk/test/Modules/cxx-templates.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/cxx-templates.cpp?rev=245779&r1=245778&r2=245779&view=diff
==============================================================================
--- cfe/trunk/test/Modules/cxx-templates.cpp (original)
+++ cfe/trunk/test/Modules/cxx-templates.cpp Fri Aug 21 20:47:18 2015
@@ -187,10 +187,10 @@ namespace Std {
 
 // CHECK-DUMP:      ClassTemplateDecl {{.*}} <{{.*[/\\]}}cxx-templates-common.h:1:1, {{.*}}>  col:{{.*}} in cxx_templates_common SomeTemplate
 // CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
-// CHECK-DUMP-NEXT:     TemplateArgument type 'char [2]'
-// CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
-// CHECK-DUMP-NEXT:     TemplateArgument type 'char [2]'
-// CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
 // CHECK-DUMP-NEXT:     TemplateArgument type 'char [1]'
 // CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
 // CHECK-DUMP-NEXT:     TemplateArgument type 'char [1]'
+// CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
+// CHECK-DUMP-NEXT:     TemplateArgument type 'char [2]'
+// CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
+// CHECK-DUMP-NEXT:     TemplateArgument type 'char [2]'




More information about the cfe-commits mailing list