Index: include/clang/AST/DeclTemplate.h =================================================================== --- include/clang/AST/DeclTemplate.h +++ include/clang/AST/DeclTemplate.h @@ -235,6 +235,9 @@ static TemplateArgumentList *CreateCopy(ASTContext &Context, ArrayRef Args); + /// \brief Create hash for the given arguments. + static unsigned ComputeODRHash(ArrayRef Args); + /// \brief Construct a new, temporary template argument list on the stack. /// /// The template argument list does not own the template arguments @@ -750,6 +753,22 @@ return getMostRecentDecl(); } + struct LazySpecializationInfo { + uint32_t DeclID = ~0U; + unsigned ODRHash = ~0U; + LazySpecializationInfo(uint32_t ID, unsigned Hash = ~0U) + : DeclID(ID), ODRHash(Hash) { } + LazySpecializationInfo() { } + bool operator< (const LazySpecializationInfo &Other) const { + return DeclID < Other.DeclID; + } + bool operator== (const LazySpecializationInfo &Other) const { + assert((DeclID != Other.DeclID || ODRHash == Other.ODRHash) && + "Hashes differ!"); + return DeclID == Other.DeclID; + } + }; + protected: template struct SpecEntryTraits { using DeclType = EntryType; @@ -791,6 +810,8 @@ } void loadLazySpecializationsImpl() const; + void loadLazySpecializationsImpl(llvm::ArrayRef Args) const; + Decl *loadLazySpecializationImpl(uint32_t ID) const; template typename SpecEntryTraits::DeclType* findSpecializationImpl(llvm::FoldingSetVector &Specs, @@ -816,7 +837,7 @@ /// /// The first value in the array is the number of specializations/partial /// specializations that follow. - uint32_t *LazySpecializations = nullptr; + LazySpecializationInfo *LazySpecializations = nullptr; }; /// \brief Pointer to the common data shared by all declarations of this Index: lib/AST/DeclTemplate.cpp =================================================================== --- lib/AST/DeclTemplate.cpp +++ lib/AST/DeclTemplate.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Expr.h" +#include "clang/AST/ODRHash.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" @@ -186,15 +187,29 @@ // Grab the most recent declaration to ensure we've loaded any lazy // redeclarations of this template. CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr(); - if (CommonBasePtr->LazySpecializations) { - ASTContext &Context = getASTContext(); - uint32_t *Specs = CommonBasePtr->LazySpecializations; + if (auto *Specs = CommonBasePtr->LazySpecializations) { CommonBasePtr->LazySpecializations = nullptr; - for (uint32_t I = 0, N = *Specs++; I != N; ++I) - (void)Context.getExternalSource()->GetExternalDecl(Specs[I]); + for (uint32_t I = 0, N = Specs[0].DeclID; I != N; ++I) + (void)loadLazySpecializationImpl(Specs[I+1].DeclID); } } +Decl *RedeclarableTemplateDecl::loadLazySpecializationImpl(uint32_t ID) const { + return getASTContext().getExternalSource()->GetExternalDecl(ID); +} + +void +RedeclarableTemplateDecl::loadLazySpecializationsImpl(ArrayRef + Args) const { + CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr(); + if (auto *Specs = CommonBasePtr->LazySpecializations) { + unsigned Hash = TemplateArgumentList::ComputeODRHash(Args); + for (uint32_t I = 0, N = Specs[0].DeclID; I != N; ++I) + if (Specs[I+1].ODRHash == Hash) + (void)loadLazySpecializationImpl(Specs[I+1].DeclID); + } + } + template typename RedeclarableTemplateDecl::SpecEntryTraits::DeclType * RedeclarableTemplateDecl::findSpecializationImpl( @@ -202,6 +217,8 @@ void *&InsertPos) { using SETraits = SpecEntryTraits; + loadLazySpecializationsImpl(Args); + llvm::FoldingSetNodeID ID; EntryType::Profile(ID, Args, getASTContext()); EntryType *Entry = Specs.FindNodeOrInsertPos(ID, InsertPos); @@ -276,12 +293,14 @@ FunctionDecl * FunctionTemplateDecl::findSpecialization(ArrayRef Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), Args, InsertPos); + auto *Common = getCommonPtr(); + return findSpecializationImpl(Common->Specializations, Args, InsertPos); } void FunctionTemplateDecl::addSpecialization( FunctionTemplateSpecializationInfo *Info, void *InsertPos) { - addSpecializationImpl(getSpecializations(), Info, + auto *Common = getCommonPtr(); + addSpecializationImpl(Common->Specializations, Info, InsertPos); } @@ -357,12 +376,15 @@ ClassTemplateSpecializationDecl * ClassTemplateDecl::findSpecialization(ArrayRef Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), Args, InsertPos); + auto *Common = getCommonPtr(); + return findSpecializationImpl(Common->Specializations, Args, InsertPos); } void ClassTemplateDecl::AddSpecialization(ClassTemplateSpecializationDecl *D, void *InsertPos) { - addSpecializationImpl(getSpecializations(), D, InsertPos); + auto *Common = getCommonPtr(); + addSpecializationImpl(Common->Specializations, D, + InsertPos); } ClassTemplatePartialSpecializationDecl * @@ -653,6 +675,14 @@ return new (Mem) TemplateArgumentList(Args); } +unsigned TemplateArgumentList::ComputeODRHash(ArrayRef Args) { + ODRHash Hasher; + for (TemplateArgument TA : Args) + Hasher.AddTemplateArgument(TA); + + return Hasher.CalculateHash(); +} + FunctionTemplateSpecializationInfo * FunctionTemplateSpecializationInfo::Create(ASTContext &C, FunctionDecl *FD, FunctionTemplateDecl *Template, @@ -958,12 +988,14 @@ VarTemplateSpecializationDecl * VarTemplateDecl::findSpecialization(ArrayRef Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), Args, InsertPos); + auto *Common = getCommonPtr(); + return findSpecializationImpl(Common->Specializations, Args, InsertPos); } void VarTemplateDecl::AddSpecialization(VarTemplateSpecializationDecl *D, void *InsertPos) { - addSpecializationImpl(getSpecializations(), D, InsertPos); + auto *Common = getCommonPtr(); + addSpecializationImpl(Common->Specializations, D, InsertPos); } VarTemplatePartialSpecializationDecl * Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -7001,14 +7001,25 @@ } } - if (auto *CTSD = dyn_cast(D)) - CTSD->getSpecializedTemplate()->LoadLazySpecializations(); - if (auto *VTSD = dyn_cast(D)) - VTSD->getSpecializedTemplate()->LoadLazySpecializations(); - if (auto *FD = dyn_cast(D)) { - if (auto *Template = FD->getPrimaryTemplate()) - Template->LoadLazySpecializations(); + RedeclarableTemplateDecl *Template = nullptr; + ArrayRef Args; + if (auto *CTSD = dyn_cast(D)) { + Template = CTSD->getSpecializedTemplate(); + Args = CTSD->getTemplateArgs().asArray(); } + else if (auto *VTSD = dyn_cast(D)) { + Template = VTSD->getSpecializedTemplate(); + Args = VTSD->getTemplateArgs().asArray(); + } + else if (auto *FD = dyn_cast(D)) { + if (auto *Tmplt = FD->getPrimaryTemplate()) { + Template = Tmplt; + Args = FD->getTemplateSpecializationArgs()->asArray(); + } + } + + if (Template) + Template->loadLazySpecializationsImpl(Args); } CXXCtorInitializer ** Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -40,6 +40,8 @@ const DeclID ThisDeclID; const SourceLocation ThisDeclLoc; typedef ASTReader::RecordData RecordData; + using LazySpecializationInfo + = RedeclarableTemplateDecl::LazySpecializationInfo; TypeID TypeIDForTypeDecl; unsigned AnonymousDeclNumber; GlobalDeclID NamedDeclForTagDecl; @@ -85,9 +87,15 @@ return Record.readString(); } - void ReadDeclIDList(SmallVectorImpl &IDs) { + LazySpecializationInfo ReadLazySpecializationInfo() { + DeclID ID = ReadDeclID(); + unsigned Hash = Record.readInt(); + return LazySpecializationInfo(ID, Hash); + } + + void ReadDeclIDList(SmallVectorImpl &IDs) { for (unsigned I = 0, Size = Record.readInt(); I != Size; ++I) - IDs.push_back(ReadDeclID()); + IDs.push_back(ReadLazySpecializationInfo()); } Decl *ReadDecl() { @@ -221,7 +229,7 @@ template static void AddLazySpecializations(T *D, - SmallVectorImpl& IDs) { + SmallVectorImpl& IDs) { if (IDs.empty()) return; @@ -231,12 +239,11 @@ auto *&LazySpecializations = D->getCommonPtr()->LazySpecializations; if (auto &Old = LazySpecializations) { - IDs.insert(IDs.end(), Old + 1, Old + 1 + Old[0]); + IDs.insert(IDs.end(), Old + 1, Old + 1 + Old[0].DeclID); std::sort(IDs.begin(), IDs.end()); IDs.erase(std::unique(IDs.begin(), IDs.end()), IDs.end()); } - - auto *Result = new (C) serialization::DeclID[1 + IDs.size()]; + auto *Result = new (C) LazySpecializationInfo[1 + IDs.size()]; *Result = IDs.size(); std::copy(IDs.begin(), IDs.end(), Result + 1); @@ -271,7 +278,7 @@ void ReadFunctionDefinition(FunctionDecl *FD); void Visit(Decl *D); - void UpdateDecl(Decl *D, llvm::SmallVectorImpl&); + void UpdateDecl(Decl *D, llvm::SmallVectorImpl&); static void setNextObjCCategory(ObjCCategoryDecl *Cat, ObjCCategoryDecl *Next) { @@ -2015,7 +2022,7 @@ if (ThisDeclID == Redecl.getFirstID()) { // This ClassTemplateDecl owns a CommonPtr; read it to keep track of all of // the specializations. - SmallVector SpecIDs; + SmallVector SpecIDs; ReadDeclIDList(SpecIDs); ASTDeclReader::AddLazySpecializations(D, SpecIDs); } @@ -2042,7 +2049,7 @@ if (ThisDeclID == Redecl.getFirstID()) { // This VarTemplateDecl owns a CommonPtr; read it to keep track of all of // the specializations. - SmallVector SpecIDs; + SmallVector SpecIDs; ReadDeclIDList(SpecIDs); ASTDeclReader::AddLazySpecializations(D, SpecIDs); } @@ -2148,7 +2155,7 @@ if (ThisDeclID == Redecl.getFirstID()) { // This FunctionTemplateDecl owns a CommonPtr; read it. - SmallVector SpecIDs; + SmallVector SpecIDs; ReadDeclIDList(SpecIDs); ASTDeclReader::AddLazySpecializations(D, SpecIDs); } @@ -3703,7 +3710,9 @@ ProcessingUpdatesRAIIObj ProcessingUpdates(*this); DeclUpdateOffsetsMap::iterator UpdI = DeclUpdateOffsets.find(ID); - llvm::SmallVector PendingLazySpecializationIDs; + using LazySpecializationInfo + = RedeclarableTemplateDecl::LazySpecializationInfo; + llvm::SmallVector PendingLazySpecializationIDs; if (UpdI != DeclUpdateOffsets.end()) { auto UpdateOffsets = std::move(UpdI->second); @@ -3949,7 +3958,7 @@ } void ASTDeclReader::UpdateDecl(Decl *D, - llvm::SmallVectorImpl &PendingLazySpecializationIDs) { + SmallVectorImpl &PendingLazySpecializationIDs) { while (Record.getIdx() < Record.size()) { switch ((DeclUpdateKind)Record.readInt()) { case UPD_CXX_ADDED_IMPLICIT_MEMBER: { @@ -3966,7 +3975,7 @@ case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION: // It will be added to the template's lazy specialization set. - PendingLazySpecializationIDs.push_back(ReadDeclID()); + PendingLazySpecializationIDs.push_back(ReadLazySpecializationInfo()); break; case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE: { Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -5126,12 +5126,25 @@ switch (Kind) { case UPD_CXX_ADDED_IMPLICIT_MEMBER: - case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION: case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE: assert(Update.getDecl() && "no decl to add?"); Record.push_back(GetDeclRef(Update.getDecl())); break; - + case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION: { + const Decl *Spec = Update.getDecl(); + assert(Spec && "no decl to add?"); + Record.push_back(GetDeclRef(Spec)); + ArrayRef Args; + if (auto *CTSD = dyn_cast(Spec)) + Args = CTSD->getTemplateArgs().asArray(); + else if (auto *VTSD = dyn_cast(Spec)) + Args = VTSD->getTemplateArgs().asArray(); + else if (auto *FD = dyn_cast(Spec)) + Args = FD->getTemplateSpecializationArgs()->asArray(); + assert(Args.size()); + Record.push_back(TemplateArgumentList::ComputeODRHash(Args)); + break; + } case UPD_CXX_ADDED_FUNCTION_DEFINITION: break; Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -162,11 +162,11 @@ Record.AddSourceLocation(typeParams->getRAngleLoc()); } - /// 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 Firsts; + /// Collect the first declaration from each module file that provides a + /// declaration of D. + void CollectFirstDeclFromEachModule(const Decl *D, bool IncludeLocal, + llvm::MapVector &Firsts) { + // FIXME: We can skip entries that we know are implied by others. for (const Decl *R = D->getMostRecentDecl(); R; R = R->getPreviousDecl()) { if (R->isFromASTFile()) @@ -174,10 +174,45 @@ else if (IncludeLocal) Firsts[nullptr] = R; } + } + + /// 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 Firsts; + CollectFirstDeclFromEachModule(D, IncludeLocal, Firsts); + for (const auto &F : Firsts) Record.AddDeclRef(F.second); } + /// Add to the record the first template specialization from each module + /// file that provides a declaration of D. We store the DeclId and an + /// ODRHash of the template arguments of D which should provide enough + /// information to load D only if the template instantiator needs it. + void AddFirstSpecializationDeclFromEachModule(const Decl *D, + bool IncludeLocal) { + assert(isa(D) || + isa(D) || isa(D) && + "Must not be called with other decls"); + llvm::MapVector Firsts; + CollectFirstDeclFromEachModule(D, IncludeLocal, Firsts); + + for (const auto &F : Firsts) { + Record.AddDeclRef(F.second); + ArrayRef Args; + if (auto *CTSD = dyn_cast(D)) + Args = CTSD->getTemplateArgs().asArray(); + else if (auto *VTSD = dyn_cast(D)) + Args = VTSD->getTemplateArgs().asArray(); + else if (auto *FD = dyn_cast(D)) + Args = FD->getTemplateSpecializationArgs()->asArray(); + assert(Args.size()); + Record.push_back(TemplateArgumentList::ComputeODRHash(Args)); + } + } + /// Get the specialization decl from an entry in the specialization list. template typename RedeclarableTemplateDecl::SpecEntryTraits::DeclType * @@ -190,7 +225,8 @@ decltype(T::PartialSpecializations) &getPartialSpecializations(T *Common) { return Common->PartialSpecializations; } - ArrayRef getPartialSpecializations(FunctionTemplateDecl::Common *) { + MutableArrayRef + getPartialSpecializations(FunctionTemplateDecl::Common *) { return None; } @@ -207,9 +243,11 @@ assert(!Common->LazySpecializations); } - ArrayRef LazySpecializations; + using LazySpecializationInfo + = RedeclarableTemplateDecl::LazySpecializationInfo; + ArrayRef LazySpecializations; if (auto *LS = Common->LazySpecializations) - LazySpecializations = llvm::makeArrayRef(LS + 1, LS[0]); + LazySpecializations = llvm::makeArrayRef(LS + 1, LS[0].DeclID); // Add a slot to the record for the number of specializations. unsigned I = Record.size(); @@ -225,12 +263,18 @@ for (auto *D : Specs) { assert(D->isCanonicalDecl() && "non-canonical decl in set"); - AddFirstDeclFromEachModule(D, /*IncludeLocal*/true); + AddFirstSpecializationDeclFromEachModule(D, /*IncludeLocal*/true); + } + for (auto &IDHashPair : LazySpecializations) { + Record.push_back(IDHashPair.DeclID); + Record.push_back(IDHashPair.ODRHash); } - Record.append(LazySpecializations.begin(), LazySpecializations.end()); - // Update the size entry we added earlier. - Record[I] = Record.size() - I - 1; + // Update the size entry we added earlier. We linerized the + // LazySpecializationInfo members and we need to adjust the size as we + // will read them always both + assert ((Record.size() - I - 1) % 2 == 0 && "Must be even"); + Record[I] = (Record.size() - I - 1) / 2; } /// Ensure that this template specialization is associated with the specified