diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -264,6 +264,9 @@ static TemplateArgumentList *CreateCopy(ASTContext &Context, ArrayRef Args); + /// \brief Create hash for the given arguments. + static unsigned ComputeODRHash(ArrayRef Args); + /// Construct a new, temporary template argument list on the stack. /// /// The template argument list does not own the template arguments @@ -779,6 +782,26 @@ } void anchor() override; + struct LazySpecializationInfo { + uint32_t DeclID = ~0U; + unsigned ODRHash = ~0U; + bool IsPartial = false; + LazySpecializationInfo(uint32_t ID, unsigned Hash = ~0U, + bool Partial = false) + : DeclID(ID), ODRHash(Hash), IsPartial(Partial) { } + 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!"); + assert((DeclID != Other.DeclID || IsPartial == Other.IsPartial) && + "Both must be the same kinds!"); + return DeclID == Other.DeclID; + } + }; + protected: template struct SpecEntryTraits { using DeclType = EntryType; @@ -819,7 +842,13 @@ return SpecIterator(isEnd ? Specs.end() : Specs.begin()); } - void loadLazySpecializationsImpl() const; + void loadLazySpecializationsImpl(bool OnlyPartial = false) const; + + ///\returns true if any lazy specialization was loaded. + void loadLazySpecializationsImpl(llvm::ArrayRef Args, + TemplateParameterList *TPL = nullptr) const; + + Decl *loadLazySpecializationImpl(unsigned &Hash) const; template typename SpecEntryTraits::DeclType* @@ -846,7 +875,7 @@ /// /// The first value in the array is the number of specializations/partial /// specializations that follow. - uint32_t *LazySpecializations = nullptr; + LazySpecializationInfo *LazySpecializations = nullptr; /// The set of "injected" template arguments used within this /// template. @@ -2320,7 +2349,7 @@ friend class TemplateDeclInstantiator; /// Load any lazily-loaded specializations from the external source. - void LoadLazySpecializations() const; + void LoadLazySpecializations(bool OnlyPartial = false) const; /// Get the underlying class declarations of the template. CXXRecordDecl *getTemplatedDecl() const { @@ -3154,7 +3183,7 @@ friend class ASTDeclWriter; /// Load any lazily-loaded specializations from the external source. - void LoadLazySpecializations() const; + void LoadLazySpecializations(bool OnlyPartial = false) const; /// Get the underlying variable declarations of the template. VarDecl *getTemplatedDecl() const { diff --git a/clang/include/clang/AST/ExternalASTSource.h b/clang/include/clang/AST/ExternalASTSource.h --- a/clang/include/clang/AST/ExternalASTSource.h +++ b/clang/include/clang/AST/ExternalASTSource.h @@ -100,6 +100,7 @@ /// /// The default implementation of this method is a no-op. virtual Decl *GetExternalDecl(uint32_t ID); + virtual Decl *GetLazyTemplateDecl(unsigned Hash); /// Resolve a selector ID into a selector. /// diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -702,6 +702,10 @@ /// Record code for an unterminated \#pragma clang assume_nonnull begin /// recorded in a preamble. PP_ASSUME_NONNULL_LOC = 67, + + /// Record for ODRHash vs DeclIDs + ODRHASH_VS_DECL_IDS_ODR = 68, + ODRHASH_VS_DECL_IDS_DECLS = 69, }; /// Record types used within a source manager block. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -354,7 +354,7 @@ /// which will provide access to the contents of the AST files. /// /// The AST reader provides lazy de-serialization of declarations, as -/// required when traversing the AST. Only those AST nodes that are +/// required when traversing the AST. Only those AST nodes thresettingat are /// actually required will be de-serialized. class ASTReader : public ExternalPreprocessorSource, @@ -583,6 +583,15 @@ /// Map from a FileID to the file-level declarations that it contains. llvm::DenseMap FileDeclIDs; + + /// Map from an ODRHash of template arguments to the DeclIDs + llvm::DenseMap ODRHashDeclIDs; + + /// Vectors to store ODRHashes of template specialization args + llvm::SmallVector ODRHashes; + + /// Vectors to store DeclIDs of template specialization args + llvm::SmallVector ODRHashesDeclIDsVec; /// An array of lexical contents of a declaration context, as a sequence of /// Decl::Kind, DeclID pairs. @@ -1903,6 +1912,8 @@ Decl *GetDecl(serialization::DeclID ID); Decl *GetExternalDecl(uint32_t ID) override; + Decl *GetLazyTemplateDecl(unsigned Hash) override; + /// Resolve a declaration ID into a declaration. Return 0 if it's not /// been loaded yet. Decl *GetExistingDecl(serialization::DeclID ID); diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -224,6 +224,9 @@ /// that it contains. FileDeclIDsTy FileDeclIDs; + /// Map to store Template Args ODRHash vs the declIDs + llvm::DenseMap ODRHashDeclIDs; + void associateDeclWithFile(const Decl *D, serialization::DeclID); /// The first ID number we can use for our own types. @@ -523,6 +526,7 @@ uint64_t WriteDeclContextVisibleBlock(ASTContext &Context, DeclContext *DC); void WriteTypeDeclOffsets(); void WriteFileDeclIDsMap(); + void WriteODRHashDeclIDsMap(); void WriteComments(); void WriteSelectors(Sema &SemaRef); void WriteReferencedSelectorsPool(Sema &SemaRef); diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -12,6 +12,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/ASTContext.h" +#include "clang/Serialization/ASTReader.h" #include "clang/AST/ASTMutationListener.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclarationName.h" @@ -20,6 +21,8 @@ #include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" +#include "clang/AST/ODRHash.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/LLVM.h" @@ -331,16 +334,40 @@ return Common; } -void RedeclarableTemplateDecl::loadLazySpecializationsImpl() const { +void RedeclarableTemplateDecl::loadLazySpecializationsImpl( + bool OnlyPartial/*=false*/) const { // 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; - CommonBasePtr->LazySpecializations = nullptr; - for (uint32_t I = 0, N = *Specs++; I != N; ++I) - (void)Context.getExternalSource()->GetExternalDecl(Specs[I]); + if (auto *Specs = CommonBasePtr->LazySpecializations) { + if (!OnlyPartial) + CommonBasePtr->LazySpecializations = nullptr; + for (uint32_t I = 0, N = Specs[0].DeclID; I != N; ++I) { + // Skip over already loaded specializations. + if (!Specs[I+1].ODRHash) + continue; + if (!OnlyPartial || Specs[I+1].IsPartial) + (void)loadLazySpecializationImpl(Specs[I+1].ODRHash); + } + } +} + +Decl * +RedeclarableTemplateDecl::loadLazySpecializationImpl(unsigned &Hash) const { + return getASTContext().getExternalSource()->GetLazyTemplateDecl(Hash); +} + +void RedeclarableTemplateDecl::loadLazySpecializationsImpl( + ArrayRef Args, TemplateParameterList *TPL) const { + CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr(); + if (auto *Specs = CommonBasePtr->LazySpecializations) { + unsigned Hash = TemplateArgumentList::ComputeODRHash(Args); + if (Specs->ODRHash && Specs->ODRHash == Hash) { + (void)loadLazySpecializationImpl(Hash); + Specs->IsPartial = 0; + Specs->ODRHash = 0; + Specs->DeclID = 0; + } } } @@ -351,6 +378,8 @@ ProfileArguments&&... ProfileArgs) { using SETraits = SpecEntryTraits; + loadLazySpecializationsImpl(std::forward(ProfileArgs)...); + llvm::FoldingSetNodeID ID; EntryType::Profile(ID, std::forward(ProfileArgs)..., getASTContext()); @@ -366,10 +395,10 @@ if (InsertPos) { #ifndef NDEBUG + auto Args = SETraits::getTemplateArgs(Entry); + loadLazySpecializationsImpl(Args); void *CorrectInsertPos; - assert(!findSpecializationImpl(Specializations, - CorrectInsertPos, - SETraits::getTemplateArgs(Entry)) && + assert(!findSpecializationImpl(Specializations, CorrectInsertPos, Args) && InsertPos == CorrectInsertPos && "given incorrect InsertPos for specialization"); #endif @@ -431,7 +460,7 @@ } void FunctionTemplateDecl::LoadLazySpecializations() const { - loadLazySpecializationsImpl(); + loadLazySpecializationsImpl(/*OnlyPartial=*/false); } llvm::FoldingSetVector & @@ -443,12 +472,14 @@ FunctionDecl * FunctionTemplateDecl::findSpecialization(ArrayRef Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), InsertPos, Args); + auto *Common = getCommonPtr(); + return findSpecializationImpl(Common->Specializations, InsertPos, Args); } void FunctionTemplateDecl::addSpecialization( FunctionTemplateSpecializationInfo *Info, void *InsertPos) { - addSpecializationImpl(getSpecializations(), Info, + auto *Common = getCommonPtr(); + addSpecializationImpl(Common->Specializations, Info, InsertPos); } @@ -508,8 +539,9 @@ DeclarationName(), nullptr, nullptr); } -void ClassTemplateDecl::LoadLazySpecializations() const { - loadLazySpecializationsImpl(); +void ClassTemplateDecl::LoadLazySpecializations( + bool OnlyPartial/*=false*/) const { + loadLazySpecializationsImpl(OnlyPartial); } llvm::FoldingSetVector & @@ -520,7 +552,7 @@ llvm::FoldingSetVector & ClassTemplateDecl::getPartialSpecializations() const { - LoadLazySpecializations(); + LoadLazySpecializations(/*PartialOnly = */ true); return getCommonPtr()->PartialSpecializations; } @@ -534,12 +566,15 @@ ClassTemplateSpecializationDecl * ClassTemplateDecl::findSpecialization(ArrayRef Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), InsertPos, Args); + auto *Common = getCommonPtr(); + return findSpecializationImpl(Common->Specializations, InsertPos, Args); } void ClassTemplateDecl::AddSpecialization(ClassTemplateSpecializationDecl *D, void *InsertPos) { - addSpecializationImpl(getSpecializations(), D, InsertPos); + auto *Common = getCommonPtr(); + addSpecializationImpl(Common->Specializations, D, + InsertPos); } ClassTemplatePartialSpecializationDecl * @@ -886,6 +921,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, TemplateSpecializationKind TSK, const TemplateArgumentList *TemplateArgs, @@ -1241,8 +1284,9 @@ DeclarationName(), nullptr, nullptr); } -void VarTemplateDecl::LoadLazySpecializations() const { - loadLazySpecializationsImpl(); +void VarTemplateDecl::LoadLazySpecializations( + bool OnlyPartial/*=false*/) const { + loadLazySpecializationsImpl(OnlyPartial); } llvm::FoldingSetVector & @@ -1253,7 +1297,7 @@ llvm::FoldingSetVector & VarTemplateDecl::getPartialSpecializations() const { - LoadLazySpecializations(); + LoadLazySpecializations(/*PartialOnly = */ true); return getCommonPtr()->PartialSpecializations; } @@ -1267,12 +1311,14 @@ VarTemplateSpecializationDecl * VarTemplateDecl::findSpecialization(ArrayRef Args, void *&InsertPos) { - return findSpecializationImpl(getSpecializations(), InsertPos, Args); + auto *Common = getCommonPtr(); + return findSpecializationImpl(Common->Specializations, InsertPos, Args); } void VarTemplateDecl::AddSpecialization(VarTemplateSpecializationDecl *D, void *InsertPos) { - addSpecializationImpl(getSpecializations(), D, InsertPos); + auto *Common = getCommonPtr(); + addSpecializationImpl(Common->Specializations, D, InsertPos); } VarTemplatePartialSpecializationDecl * diff --git a/clang/lib/AST/ExternalASTSource.cpp b/clang/lib/AST/ExternalASTSource.cpp --- a/clang/lib/AST/ExternalASTSource.cpp +++ b/clang/lib/AST/ExternalASTSource.cpp @@ -72,6 +72,8 @@ return nullptr; } +Decl *ExternalASTSource::GetLazyTemplateDecl(unsigned Hash) { return nullptr; } + Selector ExternalASTSource::GetExternalSelector(uint32_t ID) { return Selector(); } diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -139,6 +139,8 @@ } void ODRHash::AddTemplateName(TemplateName Name) { + if (auto *TD = Name.getAsTemplateDecl()) + AddDecl(TD); auto Kind = Name.getKind(); ID.AddInteger(Kind); @@ -161,7 +163,6 @@ void ODRHash::AddTemplateArgument(TemplateArgument TA) { const auto Kind = TA.getKind(); ID.AddInteger(Kind); - switch (Kind) { case TemplateArgument::Null: llvm_unreachable("Expected valid TemplateArgument"); @@ -172,12 +173,13 @@ AddDecl(TA.getAsDecl()); break; case TemplateArgument::NullPtr: - ID.AddPointer(nullptr); + AddQualType(TA.getNullPtrType()); break; case TemplateArgument::Integral: { // There are integrals (e.g.: _BitInt(128)) that cannot be represented as // any builtin integral type, so we use the hash of APSInt instead. TA.getAsIntegral().Profile(ID); + // AddQualType(TA.getIntegralType()); // Maybe? break; } case TemplateArgument::Template: @@ -802,6 +804,21 @@ for (const TemplateArgument &TA : List.asArray()) AddTemplateArgument(TA); } + + // If this was a specialization we should take into account its template + // arguments. This helps to reduce collisions coming when visiting template + // specialization types (eg. when processing type template arguments). + 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)) + if (FD->getTemplateSpecializationArgs()) + Args = FD->getTemplateSpecializationArgs()->asArray(); + + for (auto &TA : Args) + AddTemplateArgument(TA); } namespace { diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3889,10 +3889,38 @@ for (unsigned I = 0, N = Record.size(); I != N; ++I) DeclsToCheckForDeferredDiags.insert(getGlobalDeclID(F, Record[I])); break; + + case ODRHASH_VS_DECL_IDS_ODR: + for (unsigned I = 0, N = Record.size(); I != N; ++I) + ODRHashes.push_back(Record[I]); + if (ODRHashesDeclIDsVec.size()) { + assert(ODRHashesDeclIDsVec.size() == ODRHashes.size() && + "The sizes of ODRHash vector and declIDs vector should be the " + "same"); + for (std::size_t i = 0; i < ODRHashes.size(); i++) + ODRHashDeclIDs[ODRHashes[i]] = ODRHashesDeclIDsVec[i]; + } + break; + + case ODRHASH_VS_DECL_IDS_DECLS: + for (unsigned I = 0, N = Record.size(); I != N; ++I) + ODRHashesDeclIDsVec.push_back(getGlobalDeclID(F, Record[I])); + if (ODRHashes.size()) { + assert(ODRHashesDeclIDsVec.size() == ODRHashes.size() && + "The sizes of ODRHash vector and declIDs vector should be the " + "same"); + for (std::size_t i = 0; i < ODRHashes.size(); i++) + ODRHashDeclIDs[ODRHashes[i]] = ODRHashesDeclIDsVec[i]; + } + break; } } } +Decl *ASTReader::GetLazyTemplateDecl(unsigned Hash){ + return GetDecl(ODRHashDeclIDs[Hash]); +} + void ASTReader::ReadModuleOffsetMap(ModuleFile &F) const { assert(!F.ModuleOffsetMap.empty() && "no module offset map to read"); @@ -7398,14 +7426,23 @@ } } - 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 ** diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -85,9 +85,9 @@ ASTReader::RecordLocation Loc; const DeclID ThisDeclID; const SourceLocation ThisDeclLoc; - using RecordData = ASTReader::RecordData; - + using LazySpecializationInfo + = RedeclarableTemplateDecl::LazySpecializationInfo; TypeID DeferredTypeID = 0; unsigned AnonymousDeclNumber = 0; GlobalDeclID NamedDeclForTagDecl = 0; @@ -133,9 +133,16 @@ return Record.readString(); } - void readDeclIDList(SmallVectorImpl &IDs) { + LazySpecializationInfo ReadLazySpecializationInfo() { + DeclID ID = readDeclID(); + unsigned Hash = Record.readInt(); + bool IsPartial = Record.readInt(); + return LazySpecializationInfo(ID, Hash, IsPartial); + } + + void readDeclIDList(SmallVectorImpl &IDs) { for (unsigned I = 0, Size = Record.readInt(); I != Size; ++I) - IDs.push_back(readDeclID()); + IDs.push_back(ReadLazySpecializationInfo()); } Decl *readDecl() { @@ -259,7 +266,7 @@ template static void AddLazySpecializations(T *D, - SmallVectorImpl& IDs) { + SmallVectorImpl& IDs) { if (IDs.empty()) return; @@ -269,12 +276,11 @@ auto *&LazySpecializations = D->getCommonPtr()->LazySpecializations; if (auto &Old = LazySpecializations) { - IDs.insert(IDs.end(), Old + 1, Old + 1 + Old[0]); - llvm::sort(IDs); + 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); @@ -312,7 +318,7 @@ void ReadFunctionDefinition(FunctionDecl *FD); void Visit(Decl *D); - void UpdateDecl(Decl *D, SmallVectorImpl &); + void UpdateDecl(Decl *D, llvm::SmallVectorImpl&); static void setNextObjCCategory(ObjCCategoryDecl *Cat, ObjCCategoryDecl *Next) { @@ -2382,7 +2388,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); } @@ -2410,7 +2416,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); } @@ -2520,7 +2526,7 @@ if (ThisDeclID == Redecl.getFirstID()) { // This FunctionTemplateDecl owns a CommonPtr; read it. - SmallVector SpecIDs; + SmallVector SpecIDs; readDeclIDList(SpecIDs); ASTDeclReader::AddLazySpecializations(D, SpecIDs); } @@ -4127,7 +4133,9 @@ ProcessingUpdatesRAIIObj ProcessingUpdates(*this); DeclUpdateOffsetsMap::iterator UpdI = DeclUpdateOffsets.find(ID); - SmallVector PendingLazySpecializationIDs; + using LazySpecializationInfo + = RedeclarableTemplateDecl::LazySpecializationInfo; + llvm::SmallVector PendingLazySpecializationIDs; if (UpdI != DeclUpdateOffsets.end()) { auto UpdateOffsets = std::move(UpdI->second); @@ -4397,7 +4405,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: { @@ -4410,7 +4418,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: { diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -868,6 +868,8 @@ RECORD(DECLS_TO_CHECK_FOR_DEFERRED_DIAGS); RECORD(PP_INCLUDED_FILES); RECORD(PP_ASSUME_NONNULL_LOC); + RECORD(ODRHASH_VS_DECL_IDS_ODR); + RECORD(ODRHASH_VS_DECL_IDS_DECLS); // SourceManager Block. BLOCK(SOURCE_MANAGER_BLOCK); @@ -3193,6 +3195,40 @@ Stream.EmitRecordWithBlob(AbbrevCode, Record, bytes(FileGroupedDeclIDs)); } +void ASTWriter::WriteODRHashDeclIDsMap() { + using namespace llvm; + + // Join the vectors of DeclIDs from all files. + SmallVector ODRHashes; + SmallVector ODRHashesDeclIDs; + for (auto &ODRHashDeclEntry : ODRHashDeclIDs) { + ODRHashes.push_back(ODRHashDeclEntry.second); + ODRHashesDeclIDs.push_back(ODRHashDeclEntry.first); + } + + { + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(ODRHASH_VS_DECL_IDS_ODR)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + RecordData::value_type Record[] = {ODRHASH_VS_DECL_IDS_ODR, + ODRHashes.size()}; + Stream.EmitRecordWithBlob(AbbrevCode, Record, bytes(ODRHashes)); + } + + { + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(ODRHASH_VS_DECL_IDS_DECLS)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + RecordData::value_type Record[] = {ODRHASH_VS_DECL_IDS_DECLS, + ODRHashesDeclIDs.size()}; + Stream.EmitRecordWithBlob(AbbrevCode, Record, bytes(ODRHashesDeclIDs)); + } +} + void ASTWriter::WriteComments() { Stream.EnterSubblock(COMMENTS_BLOCK_ID, 3); auto _ = llvm::make_scope_exit([this] { Stream.ExitBlock(); }); @@ -5041,6 +5077,7 @@ WriteTypeDeclOffsets(); if (!DeclUpdatesOffsetsRecord.empty()) Stream.EmitRecord(DECL_UPDATE_OFFSETS, DeclUpdatesOffsetsRecord); + WriteODRHashDeclIDsMap(); WriteFileDeclIDsMap(); WriteSourceManagerBlock(Context.getSourceManager(), PP); WriteComments(); @@ -5221,12 +5258,31 @@ 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)); + ODRHashDeclIDs[TemplateArgumentList::ComputeODRHash(Args)] = + DeclIDs[Spec]; + bool IsPartialSpecialization + = isa(Spec) || + isa(Spec); + Record.push_back(IsPartialSpecialization); + break; + } case UPD_CXX_ADDED_FUNCTION_DEFINITION: case UPD_CXX_ADDED_VAR_DEFINITION: break; diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -177,11 +177,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()) @@ -189,10 +189,49 @@ 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)); + bool IsPartialSpecialization + = isa(D) || + isa(D); + Record.push_back(IsPartialSpecialization); + } + } + /// Get the specialization decl from an entry in the specialization list. template typename RedeclarableTemplateDecl::SpecEntryTraits::DeclType * @@ -205,7 +244,9 @@ decltype(T::PartialSpecializations) &getPartialSpecializations(T *Common) { return Common->PartialSpecializations; } - ArrayRef getPartialSpecializations(FunctionTemplateDecl::Common *) { + + MutableArrayRef + getPartialSpecializations(FunctionTemplateDecl::Common *) { return std::nullopt; } @@ -222,9 +263,11 @@ assert(!Common->LazySpecializations); } - ArrayRef LazySpecializations; + using LazySpecializationInfo + = RedeclarableTemplateDecl::LazySpecializationInfo; + ArrayRef LazySpecializations; if (auto *LS = Common->LazySpecializations) - LazySpecializations = llvm::ArrayRef(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(); @@ -240,12 +283,20 @@ for (auto *D : Specs) { assert(D->isCanonicalDecl() && "non-canonical decl in set"); - AddFirstDeclFromEachModule(D, /*IncludeLocal*/true); + AddFirstSpecializationDeclFromEachModule(D, /*IncludeLocal*/true); + } + for (auto &SpecInfo : LazySpecializations) { + Record.push_back(SpecInfo.DeclID); + Record.push_back(SpecInfo.ODRHash); + Record.push_back(SpecInfo.IsPartial); } - 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 together. + assert ((Record.size() - I - 1) % 3 == 0 + && "Must be divisible by LazySpecializationInfo count!"); + Record[I] = (Record.size() - I - 1) / 3; } /// Ensure that this template specialization is associated with the specified