https://github.com/vgvassilev updated https://github.com/llvm/llvm-project/pull/76774
>From 50fd47f2bfda527807f8cc5e46425050246868aa Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <yedeng...@linux.alibaba.com> Date: Wed, 3 Jan 2024 11:33:17 +0800 Subject: [PATCH 1/2] Load Specializations Lazily --- clang/include/clang/AST/DeclTemplate.h | 35 ++-- clang/include/clang/AST/ExternalASTSource.h | 6 + clang/include/clang/AST/ODRHash.h | 3 + .../clang/Sema/MultiplexExternalSemaSource.h | 6 + .../include/clang/Serialization/ASTBitCodes.h | 3 + clang/include/clang/Serialization/ASTReader.h | 28 +++ clang/include/clang/Serialization/ASTWriter.h | 6 + clang/lib/AST/DeclTemplate.cpp | 67 +++++--- clang/lib/AST/ExternalASTSource.cpp | 5 + clang/lib/AST/ODRHash.cpp | 2 + .../lib/Sema/MultiplexExternalSemaSource.cpp | 6 + clang/lib/Serialization/ASTReader.cpp | 125 +++++++++++++- clang/lib/Serialization/ASTReaderDecl.cpp | 35 +++- clang/lib/Serialization/ASTReaderInternals.h | 80 +++++++++ clang/lib/Serialization/ASTWriter.cpp | 144 +++++++++++++++- clang/lib/Serialization/ASTWriterDecl.cpp | 78 ++++++--- clang/test/Modules/odr_hash.cpp | 4 +- clang/unittests/Serialization/CMakeLists.txt | 1 + .../Serialization/LoadSpecLazily.cpp | 159 ++++++++++++++++++ 19 files changed, 728 insertions(+), 65 deletions(-) create mode 100644 clang/unittests/Serialization/LoadSpecLazily.cpp diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 832ad2de6b08a8..4699dd17bc182c 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -525,8 +525,11 @@ class FunctionTemplateSpecializationInfo final return Function.getInt(); } + void loadExternalRedecls(); + public: friend TrailingObjects; + friend class ASTReader; static FunctionTemplateSpecializationInfo * Create(ASTContext &C, FunctionDecl *FD, FunctionTemplateDecl *Template, @@ -789,13 +792,15 @@ class RedeclarableTemplateDecl : public TemplateDecl, return SpecIterator<EntryType>(isEnd ? Specs.end() : Specs.begin()); } - void loadLazySpecializationsImpl() const; + void loadExternalSpecializations() const; template <class EntryType, typename ...ProfileArguments> typename SpecEntryTraits<EntryType>::DeclType* findSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs, void *&InsertPos, ProfileArguments &&...ProfileArgs); + void loadLazySpecializationsWithArgs(ArrayRef<TemplateArgument> TemplateArgs); + template <class Derived, class EntryType> void addSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs, EntryType *Entry, void *InsertPos); @@ -814,9 +819,13 @@ class RedeclarableTemplateDecl : public TemplateDecl, /// If non-null, points to an array of specializations (including /// partial specializations) known only by their external declaration IDs. /// + /// These specializations needs to be loaded at once in + /// loadExternalSpecializations to complete the redecl chain or be preparing + /// for template resolution. + /// /// The first value in the array is the number of specializations/partial /// specializations that follow. - uint32_t *LazySpecializations = nullptr; + uint32_t *ExternalSpecializations = nullptr; /// The set of "injected" template arguments used within this /// template. @@ -850,6 +859,8 @@ class RedeclarableTemplateDecl : public TemplateDecl, friend class ASTDeclWriter; friend class ASTReader; template <class decl_type> friend class RedeclarableTemplate; + friend class ClassTemplateSpecializationDecl; + friend class VarTemplateSpecializationDecl; /// Retrieves the canonical declaration of this template. RedeclarableTemplateDecl *getCanonicalDecl() override { @@ -977,6 +988,7 @@ SpecEntryTraits<FunctionTemplateSpecializationInfo> { class FunctionTemplateDecl : public RedeclarableTemplateDecl { protected: friend class FunctionDecl; + friend class FunctionTemplateSpecializationInfo; /// Data that is common to all of the declarations of a given /// function template. @@ -1012,13 +1024,13 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl { void addSpecialization(FunctionTemplateSpecializationInfo* Info, void *InsertPos); + /// Load any lazily-loaded specializations from the external source. + void LoadLazySpecializations() const; + public: friend class ASTDeclReader; friend class ASTDeclWriter; - /// Load any lazily-loaded specializations from the external source. - void LoadLazySpecializations() const; - /// Get the underlying function declaration of the template. FunctionDecl *getTemplatedDecl() const { return static_cast<FunctionDecl *>(TemplatedDecl); @@ -1839,6 +1851,8 @@ class ClassTemplateSpecializationDecl LLVM_PREFERRED_TYPE(TemplateSpecializationKind) unsigned SpecializationKind : 3; + void loadExternalRedecls(); + protected: ClassTemplateSpecializationDecl(ASTContext &Context, Kind DK, TagKind TK, DeclContext *DC, SourceLocation StartLoc, @@ -1852,6 +1866,7 @@ class ClassTemplateSpecializationDecl public: friend class ASTDeclReader; friend class ASTDeclWriter; + friend class ASTReader; static ClassTemplateSpecializationDecl * Create(ASTContext &Context, TagKind TK, DeclContext *DC, @@ -2285,9 +2300,7 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl { friend class ASTDeclReader; friend class ASTDeclWriter; friend class TemplateDeclInstantiator; - - /// Load any lazily-loaded specializations from the external source. - void LoadLazySpecializations() const; + friend class ClassTemplateSpecializationDecl; /// Get the underlying class declarations of the template. CXXRecordDecl *getTemplatedDecl() const { @@ -2651,6 +2664,8 @@ class VarTemplateSpecializationDecl : public VarDecl, LLVM_PREFERRED_TYPE(bool) unsigned IsCompleteDefinition : 1; + void loadExternalRedecls(); + protected: VarTemplateSpecializationDecl(Kind DK, ASTContext &Context, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, @@ -2664,6 +2679,7 @@ class VarTemplateSpecializationDecl : public VarDecl, public: friend class ASTDeclReader; friend class ASTDeclWriter; + friend class ASTReader; friend class VarDecl; static VarTemplateSpecializationDecl * @@ -3057,8 +3073,7 @@ class VarTemplateDecl : public RedeclarableTemplateDecl { friend class ASTDeclReader; friend class ASTDeclWriter; - /// Load any lazily-loaded specializations from the external source. - void LoadLazySpecializations() const; + friend class VarTemplatePartialSpecializationDecl; /// 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 index 8e573965b0a336..75ee6f827080d4 100644 --- a/clang/include/clang/AST/ExternalASTSource.h +++ b/clang/include/clang/AST/ExternalASTSource.h @@ -150,6 +150,12 @@ class ExternalASTSource : public RefCountedBase<ExternalASTSource> { virtual bool FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name); + /// Load all the external specialzations for the Decl and the corresponding + /// template arguments. + virtual void + LoadExternalSpecializations(const Decl *D, + ArrayRef<TemplateArgument> TemplateArgs); + /// Ensures that the table of all visible declarations inside this /// context is up to date. /// diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h index cedf644520fc32..ddd1bb0f095e75 100644 --- a/clang/include/clang/AST/ODRHash.h +++ b/clang/include/clang/AST/ODRHash.h @@ -101,6 +101,9 @@ class ODRHash { // Save booleans until the end to lower the size of data to process. void AddBoolean(bool value); + // Add intergers to ID. + void AddInteger(unsigned Value); + static bool isSubDeclToBeProcessed(const Decl *D, const DeclContext *Parent); private: diff --git a/clang/include/clang/Sema/MultiplexExternalSemaSource.h b/clang/include/clang/Sema/MultiplexExternalSemaSource.h index 2bf91cb5212c5e..9345e96e8cb8c3 100644 --- a/clang/include/clang/Sema/MultiplexExternalSemaSource.h +++ b/clang/include/clang/Sema/MultiplexExternalSemaSource.h @@ -97,6 +97,12 @@ class MultiplexExternalSemaSource : public ExternalSemaSource { bool FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) override; + /// Load all the external specialzations for the Decl and the corresponding + /// template args. + virtual void + LoadExternalSpecializations(const Decl *D, + ArrayRef<TemplateArgument> TemplateArgs) override; + /// Ensures that the table of all visible declarations inside this /// context is up to date. void completeVisibleDeclsMap(const DeclContext *DC) override; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index fdd64f2abbe937..23a279de96ab15 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1523,6 +1523,9 @@ enum DeclCode { /// An ImplicitConceptSpecializationDecl record. DECL_IMPLICIT_CONCEPT_SPECIALIZATION, + // A decls specilization record. + DECL_SPECIALIZATIONS, + DECL_LAST = DECL_IMPLICIT_CONCEPT_SPECIALIZATION }; diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 21d791f5cd89a2..293d6495d164ef 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -340,6 +340,9 @@ class ASTIdentifierLookupTrait; /// The on-disk hash table(s) used for DeclContext name lookup. struct DeclContextLookupTable; +/// The on-disk hash table(s) used for specialization decls. +struct SpecializationsLookupTable; + } // namespace reader } // namespace serialization @@ -599,6 +602,11 @@ class ASTReader llvm::DenseMap<const DeclContext *, serialization::reader::DeclContextLookupTable> Lookups; + /// Map from decls to specialized decls. + llvm::DenseMap<const Decl *, + serialization::reader::SpecializationsLookupTable> + SpecializationsLookups; + // Updates for visible decls can occur for other contexts than just the // TU, and when we read those update records, the actual context may not // be available yet, so have this pending map using the ID as a key. It @@ -640,6 +648,9 @@ class ASTReader llvm::BitstreamCursor &Cursor, uint64_t Offset, serialization::DeclID ID); + bool ReadSpecializations(ModuleFile &M, llvm::BitstreamCursor &Cursor, + uint64_t Offset, Decl *D); + /// A vector containing identifiers that have already been /// loaded. /// @@ -1343,6 +1354,11 @@ class ASTReader const serialization::reader::DeclContextLookupTable * getLoadedLookupTables(DeclContext *Primary) const; + /// Get the loaded specializations lookup tables for \p D, + /// if any. + serialization::reader::SpecializationsLookupTable * + getLoadedSpecializationsLookupTables(const Decl *D); + private: struct ImportedModule { ModuleFile *Mod; @@ -1982,6 +1998,10 @@ class ASTReader bool FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) override; + void + LoadExternalSpecializations(const Decl *D, + ArrayRef<TemplateArgument> TemplateArgs) override; + /// Read all of the declarations lexically stored in a /// declaration context. /// @@ -2404,6 +2424,14 @@ class ASTReader bool isProcessingUpdateRecords() { return ProcessingUpdateRecords; } }; +/// Get a stable hash for template arguments across compiler invovations. +/// This is used for loading corresponding specializations lazily according +/// to the template arguments. +/// Given it is fine to load additional specializations, we're tolerant to +/// map different template arguments to the same hash value as long as the +/// semantically same template arguments get the same hash value. +unsigned GetTemplateArgsStableHash(ArrayRef<TemplateArgument> TemplateArgs); + /// A simple helper class to unpack an integer to bits and consuming /// the bits in order. class BitsUnpacker { diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index de69f99003d827..09806b87590766 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -527,6 +527,10 @@ class ASTWriter : public ASTDeserializationListener, bool isLookupResultExternal(StoredDeclsList &Result, DeclContext *DC); bool isLookupResultEntirelyExternal(StoredDeclsList &Result, DeclContext *DC); + uint64_t WriteSpecializationsLookupTable( + const NamedDecl *D, + llvm::SmallVectorImpl<const NamedDecl *> &Specializations); + void GenerateNameLookupTable(const DeclContext *DC, llvm::SmallVectorImpl<char> &LookupTable); uint64_t WriteDeclContextLexicalBlock(ASTContext &Context, DeclContext *DC); @@ -564,6 +568,8 @@ class ASTWriter : public ASTDeserializationListener, unsigned DeclEnumAbbrev = 0; unsigned DeclObjCIvarAbbrev = 0; unsigned DeclCXXMethodAbbrev = 0; + unsigned DeclSpecializationsAbbrev = 0; + unsigned DeclDependentNonTemplateCXXMethodAbbrev = 0; unsigned DeclTemplateCXXMethodAbbrev = 0; unsigned DeclMemberSpecializedCXXMethodAbbrev = 0; diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 7d7556e670f951..16b396a5e785d7 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -331,14 +331,14 @@ RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() c return Common; } -void RedeclarableTemplateDecl::loadLazySpecializationsImpl() const { +void RedeclarableTemplateDecl::loadExternalSpecializations() const { // Grab the most recent declaration to ensure we've loaded any lazy // redeclarations of this template. CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr(); - if (CommonBasePtr->LazySpecializations) { + if (CommonBasePtr->ExternalSpecializations) { ASTContext &Context = getASTContext(); - uint32_t *Specs = CommonBasePtr->LazySpecializations; - CommonBasePtr->LazySpecializations = nullptr; + uint32_t *Specs = CommonBasePtr->ExternalSpecializations; + CommonBasePtr->ExternalSpecializations = nullptr; for (uint32_t I = 0, N = *Specs++; I != N; ++I) (void)Context.getExternalSource()->GetExternalDecl(Specs[I]); } @@ -358,6 +358,16 @@ RedeclarableTemplateDecl::findSpecializationImpl( return Entry ? SETraits::getDecl(Entry)->getMostRecentDecl() : nullptr; } +void RedeclarableTemplateDecl::loadLazySpecializationsWithArgs( + ArrayRef<TemplateArgument> TemplateArgs) { + auto *ExternalSource = getASTContext().getExternalSource(); + if (!ExternalSource) + return; + + ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(), + TemplateArgs); +} + template<class Derived, class EntryType> void RedeclarableTemplateDecl::addSpecializationImpl( llvm::FoldingSetVector<EntryType> &Specializations, EntryType *Entry, @@ -430,24 +440,23 @@ FunctionTemplateDecl::newCommon(ASTContext &C) const { return CommonPtr; } -void FunctionTemplateDecl::LoadLazySpecializations() const { - loadLazySpecializationsImpl(); -} - llvm::FoldingSetVector<FunctionTemplateSpecializationInfo> & FunctionTemplateDecl::getSpecializations() const { - LoadLazySpecializations(); + loadExternalSpecializations(); return getCommonPtr()->Specializations; } FunctionDecl * FunctionTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args, void *&InsertPos) { + loadLazySpecializationsWithArgs(Args); return findSpecializationImpl(getSpecializations(), InsertPos, Args); } void FunctionTemplateDecl::addSpecialization( FunctionTemplateSpecializationInfo *Info, void *InsertPos) { + using SETraits = SpecEntryTraits<FunctionTemplateSpecializationInfo>; + loadLazySpecializationsWithArgs(SETraits::getTemplateArgs(Info)); addSpecializationImpl<FunctionTemplateDecl>(getSpecializations(), Info, InsertPos); } @@ -508,19 +517,15 @@ ClassTemplateDecl *ClassTemplateDecl::CreateDeserialized(ASTContext &C, DeclarationName(), nullptr, nullptr); } -void ClassTemplateDecl::LoadLazySpecializations() const { - loadLazySpecializationsImpl(); -} - llvm::FoldingSetVector<ClassTemplateSpecializationDecl> & ClassTemplateDecl::getSpecializations() const { - LoadLazySpecializations(); + loadExternalSpecializations(); return getCommonPtr()->Specializations; } llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> & ClassTemplateDecl::getPartialSpecializations() const { - LoadLazySpecializations(); + loadExternalSpecializations(); return getCommonPtr()->PartialSpecializations; } @@ -534,11 +539,14 @@ ClassTemplateDecl::newCommon(ASTContext &C) const { ClassTemplateSpecializationDecl * ClassTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args, void *&InsertPos) { + loadLazySpecializationsWithArgs(Args); return findSpecializationImpl(getSpecializations(), InsertPos, Args); } void ClassTemplateDecl::AddSpecialization(ClassTemplateSpecializationDecl *D, void *InsertPos) { + using SETraits = SpecEntryTraits<ClassTemplateSpecializationDecl>; + loadLazySpecializationsWithArgs(SETraits::getTemplateArgs(D)); addSpecializationImpl<ClassTemplateDecl>(getSpecializations(), D, InsertPos); } @@ -546,6 +554,7 @@ ClassTemplatePartialSpecializationDecl * ClassTemplateDecl::findPartialSpecialization( ArrayRef<TemplateArgument> Args, TemplateParameterList *TPL, void *&InsertPos) { + loadLazySpecializationsWithArgs(Args); return findSpecializationImpl(getPartialSpecializations(), InsertPos, Args, TPL); } @@ -900,6 +909,11 @@ FunctionTemplateSpecializationInfo *FunctionTemplateSpecializationInfo::Create( FD, Template, TSK, TemplateArgs, ArgsAsWritten, POI, MSInfo); } +void FunctionTemplateSpecializationInfo::loadExternalRedecls() { + getTemplate()->loadExternalSpecializations(); + getTemplate()->loadLazySpecializationsWithArgs(TemplateArguments->asArray()); +} + //===----------------------------------------------------------------------===// // ClassTemplateSpecializationDecl Implementation //===----------------------------------------------------------------------===// @@ -1024,6 +1038,12 @@ ClassTemplateSpecializationDecl::getSourceRange() const { } } +void ClassTemplateSpecializationDecl::loadExternalRedecls() { + getSpecializedTemplate()->loadExternalSpecializations(); + getSpecializedTemplate()->loadLazySpecializationsWithArgs( + getTemplateArgs().asArray()); +} + //===----------------------------------------------------------------------===// // ConceptDecl Implementation //===----------------------------------------------------------------------===// @@ -1226,19 +1246,15 @@ VarTemplateDecl *VarTemplateDecl::CreateDeserialized(ASTContext &C, DeclarationName(), nullptr, nullptr); } -void VarTemplateDecl::LoadLazySpecializations() const { - loadLazySpecializationsImpl(); -} - llvm::FoldingSetVector<VarTemplateSpecializationDecl> & VarTemplateDecl::getSpecializations() const { - LoadLazySpecializations(); + loadExternalSpecializations(); return getCommonPtr()->Specializations; } llvm::FoldingSetVector<VarTemplatePartialSpecializationDecl> & VarTemplateDecl::getPartialSpecializations() const { - LoadLazySpecializations(); + loadExternalSpecializations(); return getCommonPtr()->PartialSpecializations; } @@ -1252,17 +1268,21 @@ VarTemplateDecl::newCommon(ASTContext &C) const { VarTemplateSpecializationDecl * VarTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args, void *&InsertPos) { + loadLazySpecializationsWithArgs(Args); return findSpecializationImpl(getSpecializations(), InsertPos, Args); } void VarTemplateDecl::AddSpecialization(VarTemplateSpecializationDecl *D, void *InsertPos) { + using SETraits = SpecEntryTraits<VarTemplateSpecializationDecl>; + loadLazySpecializationsWithArgs(SETraits::getTemplateArgs(D)); addSpecializationImpl<VarTemplateDecl>(getSpecializations(), D, InsertPos); } VarTemplatePartialSpecializationDecl * VarTemplateDecl::findPartialSpecialization(ArrayRef<TemplateArgument> Args, TemplateParameterList *TPL, void *&InsertPos) { + loadLazySpecializationsWithArgs(Args); return findSpecializationImpl(getPartialSpecializations(), InsertPos, Args, TPL); } @@ -1393,6 +1413,11 @@ SourceRange VarTemplateSpecializationDecl::getSourceRange() const { return VarDecl::getSourceRange(); } +void VarTemplateSpecializationDecl::loadExternalRedecls() { + getSpecializedTemplate()->loadExternalSpecializations(); + getSpecializedTemplate()->loadLazySpecializationsWithArgs( + getTemplateArgs().asArray()); +} //===----------------------------------------------------------------------===// // VarTemplatePartialSpecializationDecl Implementation diff --git a/clang/lib/AST/ExternalASTSource.cpp b/clang/lib/AST/ExternalASTSource.cpp index 090ef02aa4224d..bba73fcbd8fa23 100644 --- a/clang/lib/AST/ExternalASTSource.cpp +++ b/clang/lib/AST/ExternalASTSource.cpp @@ -100,6 +100,11 @@ ExternalASTSource::FindExternalVisibleDeclsByName(const DeclContext *DC, return false; } +void ExternalASTSource::LoadExternalSpecializations( + const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) { + return; +} + void ExternalASTSource::completeVisibleDeclsMap(const DeclContext *DC) {} void ExternalASTSource::FindExternalLexicalDecls( diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index aea1a93ae1fa82..ace24eb4d29d85 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -1249,3 +1249,5 @@ void ODRHash::AddQualType(QualType T) { void ODRHash::AddBoolean(bool Value) { Bools.push_back(Value); } + +void ODRHash::AddInteger(unsigned Value) { ID.AddInteger(Value); } diff --git a/clang/lib/Sema/MultiplexExternalSemaSource.cpp b/clang/lib/Sema/MultiplexExternalSemaSource.cpp index 058e22cb2b814e..b845383675ab36 100644 --- a/clang/lib/Sema/MultiplexExternalSemaSource.cpp +++ b/clang/lib/Sema/MultiplexExternalSemaSource.cpp @@ -115,6 +115,12 @@ FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) { return AnyDeclsFound; } +void MultiplexExternalSemaSource::LoadExternalSpecializations( + const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) { + for (size_t i = 0; i < Sources.size(); ++i) + Sources[i]->LoadExternalSpecializations(D, TemplateArgs); +} + void MultiplexExternalSemaSource::completeVisibleDeclsMap(const DeclContext *DC){ for(size_t i = 0; i < Sources.size(); ++i) Sources[i]->completeVisibleDeclsMap(DC); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 9effd333daccdb..bcdd2dfc491d8f 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -1223,6 +1223,38 @@ void ASTDeclContextNameLookupTrait::ReadDataInto(internal_key_type, } } +ModuleFile *SpecializationsLookupTrait::ReadFileRef(const unsigned char *&d) { + using namespace llvm::support; + + uint32_t ModuleFileID = + endian::readNext<uint32_t, llvm::endianness::little, unaligned>(d); + return Reader.getLocalModuleFile(F, ModuleFileID); +} + +SpecializationsLookupTrait::internal_key_type +SpecializationsLookupTrait::ReadKey(const unsigned char *d, unsigned) { + using namespace llvm::support; + return endian::readNext<uint32_t, llvm::endianness::little, unaligned>(d); +} + +std::pair<unsigned, unsigned> +SpecializationsLookupTrait::ReadKeyDataLength(const unsigned char *&d) { + return readULEBKeyDataLength(d); +} + +void SpecializationsLookupTrait::ReadDataInto(internal_key_type, + const unsigned char *d, + unsigned DataLen, + data_type_builder &Val) { + using namespace llvm::support; + + for (unsigned NumDecls = DataLen / 4; NumDecls; --NumDecls) { + uint32_t LocalID = + endian::readNext<uint32_t, llvm::endianness::little, unaligned>(d); + Val.insert(Reader.getGlobalDeclID(F, LocalID)); + } +} + bool ASTReader::ReadLexicalDeclContextStorage(ModuleFile &M, BitstreamCursor &Cursor, uint64_t Offset, @@ -1312,6 +1344,44 @@ bool ASTReader::ReadVisibleDeclContextStorage(ModuleFile &M, return false; } +bool ASTReader::ReadSpecializations(ModuleFile &M, BitstreamCursor &Cursor, + uint64_t Offset, Decl *D) { + assert(Offset != 0); + + SavedStreamPosition SavedPosition(Cursor); + if (llvm::Error Err = Cursor.JumpToBit(Offset)) { + Error(std::move(Err)); + return true; + } + + RecordData Record; + StringRef Blob; + Expected<unsigned> MaybeCode = Cursor.ReadCode(); + if (!MaybeCode) { + Error(MaybeCode.takeError()); + return true; + } + unsigned Code = MaybeCode.get(); + + Expected<unsigned> MaybeRecCode = Cursor.readRecord(Code, Record, &Blob); + if (!MaybeRecCode) { + Error(MaybeRecCode.takeError()); + return true; + } + unsigned RecCode = MaybeRecCode.get(); + if (RecCode != DECL_SPECIALIZATIONS) { + Error("Expected decl specs block"); + return true; + } + + auto *Data = (const unsigned char *)Blob.data(); + D = D->getCanonicalDecl(); + SpecializationsLookups[D].Table.add( + &M, Data, reader::SpecializationsLookupTrait(*this, M)); + + return false; +} + void ASTReader::Error(StringRef Msg) const { Error(diag::err_fe_pch_malformed, Msg); if (PP.getLangOpts().Modules && !Diags.isDiagnosticInFlight() && @@ -7523,13 +7593,13 @@ void ASTReader::CompleteRedeclChain(const Decl *D) { } if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) - CTSD->getSpecializedTemplate()->LoadLazySpecializations(); + const_cast<ClassTemplateSpecializationDecl *>(CTSD)->loadExternalRedecls(); if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D)) - VTSD->getSpecializedTemplate()->LoadLazySpecializations(); - if (auto *FD = dyn_cast<FunctionDecl>(D)) { - if (auto *Template = FD->getPrimaryTemplate()) - Template->LoadLazySpecializations(); - } + const_cast<VarTemplateSpecializationDecl *>(VTSD)->loadExternalRedecls(); + if (auto *FD = dyn_cast<FunctionDecl>(D)) + if (auto *FDInfo = FD->getTemplateSpecializationInfo()) + const_cast<FunctionTemplateSpecializationInfo *>(FDInfo) + ->loadExternalRedecls(); } CXXCtorInitializer ** @@ -7958,6 +8028,42 @@ ASTReader::FindExternalVisibleDeclsByName(const DeclContext *DC, return !Decls.empty(); } +unsigned +clang::GetTemplateArgsStableHash(ArrayRef<TemplateArgument> TemplateArgs) { + // FIXME: ODR hashing may not be the best mechanism to hash the template + // arguments. ODR hashing is (or perhaps, should be) about determining whether + // two things are spelled the same way and have the same meaning (as required + // by the C++ ODR), whereas what we want here is whether they have the same + // meaning regardless of spelling. Maybe we can get away with reusing ODR + // hashing anyway, on the basis that any canonical, non-dependent template + // argument should have the same (invented) spelling in every translation + // unit, but it is not sure that's true in all cases. There may still be cases + // where the canonical type includes some aspect of "whatever we saw first", + // in which case the ODR hash can differ across translation units for + // non-dependent, canonical template arguments that are spelled differently + // but have the same meaning. But it is not easy to raise examples. + ODRHash Hasher; + Hasher.AddInteger(TemplateArgs.size()); + for (const TemplateArgument &TemplateArg : TemplateArgs) + Hasher.AddTemplateArgument(TemplateArg); + return Hasher.CalculateHash(); +} + +void ASTReader::LoadExternalSpecializations( + const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) { + assert(D); + + auto It = SpecializationsLookups.find(D); + if (It == SpecializationsLookups.end()) + return; + + auto HashValue = GetTemplateArgsStableHash(TemplateArgs); + + Deserializing LookupResults(this); + for (DeclID ID : It->second.Table.find(HashValue)) + GetDecl(ID); +} + void ASTReader::completeVisibleDeclsMap(const DeclContext *DC) { if (!DC->hasExternalVisibleStorage()) return; @@ -7987,6 +8093,13 @@ ASTReader::getLoadedLookupTables(DeclContext *Primary) const { return I == Lookups.end() ? nullptr : &I->second; } +serialization::reader::SpecializationsLookupTable * +ASTReader::getLoadedSpecializationsLookupTables(const Decl *D) { + assert(D->isCanonicalDecl()); + auto I = SpecializationsLookups.find(D); + return I == SpecializationsLookups.end() ? nullptr : &I->second; +} + /// Under non-PCH compilation the consumer receives the objc methods /// before receiving the implementation, and codegen depends on this. /// We simulate this by deserializing and passing to consumer the methods of the diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 547eb77930b4ee..e3ffcee6988b3f 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -274,9 +274,10 @@ namespace clang { // FIXME: We should avoid this pattern of getting the ASTContext. ASTContext &C = D->getASTContext(); - auto *&LazySpecializations = D->getCommonPtr()->LazySpecializations; + auto *&ExternalSpecializations = + D->getCommonPtr()->ExternalSpecializations; - if (auto &Old = LazySpecializations) { + if (auto &Old = ExternalSpecializations) { IDs.insert(IDs.end(), Old + 1, Old + 1 + Old[0]); llvm::sort(IDs); IDs.erase(std::unique(IDs.begin(), IDs.end()), IDs.end()); @@ -286,7 +287,7 @@ namespace clang { *Result = IDs.size(); std::copy(IDs.begin(), IDs.end(), Result + 1); - LazySpecializations = Result; + ExternalSpecializations = Result; } template <typename DeclT> @@ -426,6 +427,9 @@ namespace clang { std::pair<uint64_t, uint64_t> VisitDeclContext(DeclContext *DC); + void ReadSpecializations(ModuleFile &M, Decl *D, + llvm::BitstreamCursor &Cursor); + template<typename T> RedeclarableResult VisitRedeclarable(Redeclarable<T> *D); @@ -2431,10 +2435,14 @@ void ASTDeclReader::VisitClassTemplateDecl(ClassTemplateDecl *D) { mergeRedeclarableTemplate(D, Redecl); if (ThisDeclID == Redecl.getFirstID()) { - // This ClassTemplateDecl owns a CommonPtr; read it to keep track of all of - // the specializations. + // This ClassTemplateDecl owns a CommonPtr; read it to keep track of all + // of the specializations. SmallVector<serialization::DeclID, 32> SpecIDs; readDeclIDList(SpecIDs); + + if (Record.readInt()) + ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor); + ASTDeclReader::AddLazySpecializations(D, SpecIDs); } @@ -2463,6 +2471,10 @@ void ASTDeclReader::VisitVarTemplateDecl(VarTemplateDecl *D) { // the specializations. SmallVector<serialization::DeclID, 32> SpecIDs; readDeclIDList(SpecIDs); + + if (Record.readInt()) + ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor); + ASTDeclReader::AddLazySpecializations(D, SpecIDs); } } @@ -2566,6 +2578,9 @@ void ASTDeclReader::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { SmallVector<serialization::DeclID, 32> SpecIDs; readDeclIDList(SpecIDs); ASTDeclReader::AddLazySpecializations(D, SpecIDs); + + if (Record.readInt()) + ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor); } } @@ -2755,6 +2770,14 @@ ASTDeclReader::VisitDeclContext(DeclContext *DC) { return std::make_pair(LexicalOffset, VisibleOffset); } +void ASTDeclReader::ReadSpecializations(ModuleFile &M, Decl *D, + llvm::BitstreamCursor &DeclsCursor) { + uint64_t Offset = ReadLocalOffset(); + bool Failed = Reader.ReadSpecializations(M, DeclsCursor, Offset, D); + (void)Failed; + assert(!Failed); +} + template <typename T> ASTDeclReader::RedeclarableResult ASTDeclReader::VisitRedeclarable(Redeclarable<T> *D) { @@ -3800,6 +3823,7 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { switch ((DeclCode)MaybeDeclCode.get()) { case DECL_CONTEXT_LEXICAL: case DECL_CONTEXT_VISIBLE: + case DECL_SPECIALIZATIONS: llvm_unreachable("Record cannot be de-serialized with readDeclRecord"); case DECL_TYPEDEF: D = TypedefDecl::CreateDeserialized(Context, ID); @@ -4112,6 +4136,7 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { ReadVisibleDeclContextStorage(*Loc.F, DeclsCursor, Offsets.second, ID)) return nullptr; } + assert(Record.getIdx() == Record.size()); // Load any relevant update records. diff --git a/clang/lib/Serialization/ASTReaderInternals.h b/clang/lib/Serialization/ASTReaderInternals.h index 25a46ddabcb707..9ac3c139815b08 100644 --- a/clang/lib/Serialization/ASTReaderInternals.h +++ b/clang/lib/Serialization/ASTReaderInternals.h @@ -119,6 +119,86 @@ struct DeclContextLookupTable { MultiOnDiskHashTable<ASTDeclContextNameLookupTrait> Table; }; +/// Class that performs lookup to specialized decls. +class SpecializationsLookupTrait { + ASTReader &Reader; + ModuleFile &F; + +public: + // Maximum number of lookup tables we allow before condensing the tables. + static const int MaxTables = 4; + + /// The lookup result is a list of global declaration IDs. + using data_type = SmallVector<DeclID, 4>; + + struct data_type_builder { + data_type &Data; + llvm::DenseSet<DeclID> Found; + + data_type_builder(data_type &D) : Data(D) {} + + void insert(DeclID ID) { + // Just use a linear scan unless we have more than a few IDs. + if (Found.empty() && !Data.empty()) { + if (Data.size() <= 4) { + for (auto I : Found) + if (I == ID) + return; + Data.push_back(ID); + return; + } + + // Switch to tracking found IDs in the set. + Found.insert(Data.begin(), Data.end()); + } + + if (Found.insert(ID).second) + Data.push_back(ID); + } + }; + using hash_value_type = unsigned; + using offset_type = unsigned; + using file_type = ModuleFile *; + + using external_key_type = unsigned; + using internal_key_type = unsigned; + + explicit SpecializationsLookupTrait(ASTReader &Reader, ModuleFile &F) + : Reader(Reader), F(F) {} + + static bool EqualKey(const internal_key_type &a, const internal_key_type &b) { + return a == b; + } + + static hash_value_type ComputeHash(const internal_key_type &Key) { + return Key; + } + + static internal_key_type GetInternalKey(const external_key_type &Name) { + return Name; + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const unsigned char *&d); + + internal_key_type ReadKey(const unsigned char *d, unsigned); + + void ReadDataInto(internal_key_type, const unsigned char *d, unsigned DataLen, + data_type_builder &Val); + + static void MergeDataInto(const data_type &From, data_type_builder &To) { + To.Data.reserve(To.Data.size() + From.size()); + for (DeclID ID : From) + To.insert(ID); + } + + file_type ReadFileRef(const unsigned char *&d); +}; + +struct SpecializationsLookupTable { + MultiOnDiskHashTable<SpecializationsLookupTrait> Table; +}; + /// Base class for the trait describing the on-disk hash table for the /// identifiers in an AST file. /// diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 78939bfd533ffa..ed58e71b01c09a 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -29,6 +29,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/LambdaCapture.h" #include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/ODRHash.h" #include "clang/AST/OpenMPClause.h" #include "clang/AST/RawCommentList.h" #include "clang/AST/TemplateName.h" @@ -3924,6 +3925,147 @@ class ASTDeclContextNameLookupTrait { } // namespace +namespace { +class SpecializationsLookupTrait { + ASTWriter &Writer; + llvm::SmallVector<DeclID, 64> DeclIDs; + +public: + using key_type = unsigned; + using key_type_ref = key_type; + + /// A start and end index into DeclIDs, representing a sequence of decls. + using data_type = std::pair<unsigned, unsigned>; + using data_type_ref = const data_type &; + + using hash_value_type = unsigned; + using offset_type = unsigned; + + explicit SpecializationsLookupTrait(ASTWriter &Writer) : Writer(Writer) {} + + template <typename Col> data_type getData(Col &&C) { + unsigned Start = DeclIDs.size(); + for (auto *D : C) + DeclIDs.push_back(Writer.GetDeclRef(getDeclForLocalLookup( + Writer.getLangOpts(), const_cast<NamedDecl *>(D)))); + return std::make_pair(Start, DeclIDs.size()); + } + + data_type + ImportData(const reader::SpecializationsLookupTrait::data_type &FromReader) { + unsigned Start = DeclIDs.size(); + for (auto ID : FromReader) + DeclIDs.push_back(ID); + return std::make_pair(Start, DeclIDs.size()); + } + + static bool EqualKey(key_type_ref a, key_type_ref b) { return a == b; } + + hash_value_type ComputeHash(key_type Name) { return Name; } + + void EmitFileRef(raw_ostream &Out, ModuleFile *F) const { + assert(Writer.hasChain() && + "have reference to loaded module file but no chain?"); + + using namespace llvm::support; + endian::write<uint32_t>(Out, Writer.getChain()->getModuleFileID(F), + llvm::endianness::little); + } + + std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &Out, + key_type HashValue, + data_type_ref Lookup) { + // 4 bytes for each slot. + unsigned KeyLen = 4; + unsigned DataLen = 4 * (Lookup.second - Lookup.first); + + return emitULEBKeyDataLength(KeyLen, DataLen, Out); + } + + void EmitKey(raw_ostream &Out, key_type HashValue, unsigned) { + using namespace llvm::support; + + endian::Writer LE(Out, llvm::endianness::little); + LE.write<uint32_t>(HashValue); + } + + void EmitData(raw_ostream &Out, key_type_ref, data_type Lookup, + unsigned DataLen) { + using namespace llvm::support; + + endian::Writer LE(Out, llvm::endianness::little); + uint64_t Start = Out.tell(); + (void)Start; + for (unsigned I = Lookup.first, N = Lookup.second; I != N; ++I) + LE.write<uint32_t>(DeclIDs[I]); + assert(Out.tell() - Start == DataLen && "Data length is wrong"); + } +}; + +unsigned CalculateODRHashForSpecs(const Decl *Spec) { + assert(!isa<ClassTemplatePartialSpecializationDecl>(Spec) && + !isa<VarTemplatePartialSpecializationDecl>(Spec) && + "We shouldn't see partial specializations here."); + + if (auto *FD = dyn_cast<FunctionDecl>(Spec)) { + auto *FDInfo = FD->getTemplateSpecializationInfo(); + assert(FDInfo); + return GetTemplateArgsStableHash(FDInfo->TemplateArguments->asArray()); + } + + if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(Spec)) + return GetTemplateArgsStableHash(CTSD->getTemplateArgs().asArray()); + + if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(Spec)) + return GetTemplateArgsStableHash(VTSD->getTemplateArgs().asArray()); + + llvm_unreachable("Unimaged specialization kind?"); +} +} // namespace + +uint64_t ASTWriter::WriteSpecializationsLookupTable( + const NamedDecl *D, + llvm::SmallVectorImpl<const NamedDecl *> &Specializations) { + assert(D->isFirstDecl()); + + // Create the on-disk hash table representation. + MultiOnDiskHashTableGenerator<reader::SpecializationsLookupTrait, + SpecializationsLookupTrait> + Generator; + SpecializationsLookupTrait Trait(*this); + + llvm::DenseMap<unsigned, llvm::SmallVector<const NamedDecl *, 4>> + SpecializationMaps; + + for (auto *Specialization : Specializations) { + unsigned HashedValue = CalculateODRHashForSpecs(Specialization); + + auto Iter = SpecializationMaps.find(HashedValue); + if (Iter == SpecializationMaps.end()) + Iter = SpecializationMaps + .try_emplace(HashedValue, + llvm::SmallVector<const NamedDecl *, 4>()) + .first; + + Iter->second.push_back(Specialization); + } + + for (auto Iter : SpecializationMaps) + Generator.insert(Iter.first, Trait.getData(Iter.second), Trait); + + uint64_t Offset = Stream.GetCurrentBitNo(); + + auto *Lookups = + Chain ? Chain->getLoadedSpecializationsLookupTables(D) : nullptr; + llvm::SmallString<4096> LookupTable; + Generator.emit(LookupTable, Trait, Lookups ? &Lookups->Table : nullptr); + + RecordData::value_type Record[] = {DECL_SPECIALIZATIONS}; + Stream.EmitRecordWithBlob(DeclSpecializationsAbbrev, Record, LookupTable); + + return Offset; +} + bool ASTWriter::isLookupResultExternal(StoredDeclsList &Result, DeclContext *DC) { return Result.hasExternalDecls() && @@ -5074,7 +5216,7 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, // Keep writing types, declarations, and declaration update records // until we've emitted all of them. - Stream.EnterSubblock(DECLTYPES_BLOCK_ID, /*bits for abbreviations*/5); + Stream.EnterSubblock(DECLTYPES_BLOCK_ID, /*bits for abbreviations*/ 6); DeclTypesBlockStartOffset = Stream.GetCurrentBitNo(); WriteTypeAbbrevs(); WriteDeclAbbrevs(); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 9e3299f0491848..db3800025f8d27 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -207,22 +207,26 @@ namespace clang { return std::nullopt; } - template<typename DeclTy> - void AddTemplateSpecializations(DeclTy *D) { + template <typename DeclTy> + void AddTemplateSpecializations( + DeclTy *D, llvm::SmallVectorImpl<const NamedDecl *> &OptionalSpecs) { auto *Common = D->getCommonPtr(); // If we have any lazy specializations, and the external AST source is // our chained AST reader, we can just write out the DeclIDs. Otherwise, // we need to resolve them to actual declarations. if (Writer.Chain != Writer.Context->getExternalSource() && - Common->LazySpecializations) { - D->LoadLazySpecializations(); - assert(!Common->LazySpecializations); + Common->ExternalSpecializations) { + D->loadExternalSpecializations(); + assert(!Common->ExternalSpecializations); } - ArrayRef<DeclID> LazySpecializations; - if (auto *LS = Common->LazySpecializations) - LazySpecializations = llvm::ArrayRef(LS + 1, LS[0]); + for (auto &Entry : Common->Specializations) + OptionalSpecs.push_back(getSpecializationDecl(Entry)); + + ArrayRef<DeclID> ExternalSpecializations; + if (auto *LS = Common->ExternalSpecializations) + ExternalSpecializations = llvm::ArrayRef(LS + 1, LS[0]); // Add a slot to the record for the number of specializations. unsigned I = Record.size(); @@ -230,17 +234,20 @@ namespace clang { // AddFirstDeclFromEachModule might trigger deserialization, invalidating // *Specializations iterators. - llvm::SmallVector<const Decl*, 16> Specs; - for (auto &Entry : Common->Specializations) - Specs.push_back(getSpecializationDecl(Entry)); + // + // We need to load all the partial specializations at once if the template + // required. Since we can't know if a partial specializations will be + // needed before resolving a request to instantiate the template. + llvm::SmallVector<const Decl *, 16> PartialSpecs; for (auto &Entry : getPartialSpecializations(Common)) - Specs.push_back(getSpecializationDecl(Entry)); + PartialSpecs.push_back(getSpecializationDecl(Entry)); - for (auto *D : Specs) { + for (auto *D : PartialSpecs) { assert(D->isCanonicalDecl() && "non-canonical decl in set"); AddFirstDeclFromEachModule(D, /*IncludeLocal*/true); } - Record.append(LazySpecializations.begin(), LazySpecializations.end()); + Record.append(ExternalSpecializations.begin(), + ExternalSpecializations.end()); // Update the size entry we added earlier. Record[I] = Record.size() - I - 1; @@ -1670,8 +1677,16 @@ void ASTDeclWriter::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { void ASTDeclWriter::VisitClassTemplateDecl(ClassTemplateDecl *D) { VisitRedeclarableTemplateDecl(D); - if (D->isFirstDecl()) - AddTemplateSpecializations(D); + if (D->isFirstDecl()) { + llvm::SmallVector<const NamedDecl *, 16> OptionalSpecs; + AddTemplateSpecializations(D, OptionalSpecs); + if (!OptionalSpecs.empty()) { + Record.push_back(1); + Record.AddOffset( + Writer.WriteSpecializationsLookupTable(D, OptionalSpecs)); + } else + Record.push_back(0); + } Code = serialization::DECL_CLASS_TEMPLATE; } @@ -1730,8 +1745,17 @@ void ASTDeclWriter::VisitClassTemplatePartialSpecializationDecl( void ASTDeclWriter::VisitVarTemplateDecl(VarTemplateDecl *D) { VisitRedeclarableTemplateDecl(D); - if (D->isFirstDecl()) - AddTemplateSpecializations(D); + if (D->isFirstDecl()) { + llvm::SmallVector<const NamedDecl *, 16> OptionalSpecs; + AddTemplateSpecializations(D, OptionalSpecs); + if (!OptionalSpecs.empty()) { + Record.push_back(1); + Record.AddOffset( + Writer.WriteSpecializationsLookupTable(D, OptionalSpecs)); + } else + Record.push_back(0); + } + Code = serialization::DECL_VAR_TEMPLATE; } @@ -1791,8 +1815,17 @@ void ASTDeclWriter::VisitVarTemplatePartialSpecializationDecl( void ASTDeclWriter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { VisitRedeclarableTemplateDecl(D); - if (D->isFirstDecl()) - AddTemplateSpecializations(D); + if (D->isFirstDecl()) { + llvm::SmallVector<const NamedDecl *, 16> OptionalSpecs; + AddTemplateSpecializations(D, OptionalSpecs); + if (!OptionalSpecs.empty()) { + Record.push_back(1); + Record.AddOffset( + Writer.WriteSpecializationsLookupTable(D, OptionalSpecs)); + } else + Record.push_back(0); + } + Code = serialization::DECL_FUNCTION_TEMPLATE; } @@ -2657,6 +2690,11 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(serialization::DECL_CONTEXT_VISIBLE)); Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); DeclContextVisibleLookupAbbrev = Stream.EmitAbbrev(std::move(Abv)); + + Abv = std::make_shared<BitCodeAbbrev>(); + Abv->Add(BitCodeAbbrevOp(serialization::DECL_SPECIALIZATIONS)); + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + DeclSpecializationsAbbrev = Stream.EmitAbbrev(std::move(Abv)); } /// isRequiredDecl - Check if this is a "required" Decl, which must be seen by diff --git a/clang/test/Modules/odr_hash.cpp b/clang/test/Modules/odr_hash.cpp index 220ef767df849a..4483cf21e5c623 100644 --- a/clang/test/Modules/odr_hash.cpp +++ b/clang/test/Modules/odr_hash.cpp @@ -2897,7 +2897,7 @@ struct S5 { }; #else S5 s5; -// expected-error@second.h:* {{'PointersAndReferences::S5::x' from module 'SecondModule' is not present in definition of 'PointersAndReferences::S5' in module 'FirstModule'}} +// expected-error@first.h:* {{'PointersAndReferences::S5::x' from module 'FirstModule' is not present in definition of 'PointersAndReferences::S5' in module 'SecondModule'}} // expected-note@first.h:* {{declaration of 'x' does not match}} #endif @@ -3834,7 +3834,7 @@ struct Valid { #else Invalid::L2<1>::L3<1> invalid; // expected-error@second.h:* {{'Types::InjectedClassName::Invalid::L2::L3::x' from module 'SecondModule' is not present in definition of 'L3<>' in module 'FirstModule'}} -// expected-note@first.h:* {{declaration of 'x' does not match}} +// expected-note@second.h:* {{declaration of 'x' does not match}} Valid::L2<1>::L3<1> valid; #endif } // namespace InjectedClassName diff --git a/clang/unittests/Serialization/CMakeLists.txt b/clang/unittests/Serialization/CMakeLists.txt index 10d7de970c643d..6276bbb6d0cb4b 100644 --- a/clang/unittests/Serialization/CMakeLists.txt +++ b/clang/unittests/Serialization/CMakeLists.txt @@ -8,6 +8,7 @@ set(LLVM_LINK_COMPONENTS add_clang_unittest(SerializationTests ForceCheckFileInputTest.cpp InMemoryModuleCacheTest.cpp + LoadSpecLazily.cpp ModuleCacheTest.cpp NoCommentsTest.cpp SourceLocationEncodingTest.cpp diff --git a/clang/unittests/Serialization/LoadSpecLazily.cpp b/clang/unittests/Serialization/LoadSpecLazily.cpp new file mode 100644 index 00000000000000..03f3ff3f786555 --- /dev/null +++ b/clang/unittests/Serialization/LoadSpecLazily.cpp @@ -0,0 +1,159 @@ +//== unittests/Serialization/LoadSpecLazily.cpp ----------------------========// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Serialization/ASTDeserializationListener.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; +using namespace clang::tooling; + +namespace { + +class LoadSpecLazilyTest : public ::testing::Test { + void SetUp() override { + ASSERT_FALSE( + sys::fs::createUniqueDirectory("load-spec-lazily-test", TestDir)); + } + + void TearDown() override { sys::fs::remove_directories(TestDir); } + +public: + SmallString<256> TestDir; + + void addFile(StringRef Path, StringRef Contents) { + ASSERT_FALSE(sys::path::is_absolute(Path)); + + SmallString<256> AbsPath(TestDir); + sys::path::append(AbsPath, Path); + + ASSERT_FALSE( + sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath))); + + std::error_code EC; + llvm::raw_fd_ostream OS(AbsPath, EC); + ASSERT_FALSE(EC); + OS << Contents; + } + + std::string GenerateModuleInterface(StringRef ModuleName, + StringRef Contents) { + std::string FileName = llvm::Twine(ModuleName + ".cppm").str(); + addFile(FileName, Contents); + + IntrusiveRefCntPtr<DiagnosticsEngine> Diags = + CompilerInstance::createDiagnostics(new DiagnosticOptions()); + CreateInvocationOptions CIOpts; + CIOpts.Diags = Diags; + CIOpts.VFS = llvm::vfs::createPhysicalFileSystem(); + + std::string CacheBMIPath = + llvm::Twine(TestDir + "/" + ModuleName + " .pcm").str(); + std::string PrebuiltModulePath = + "-fprebuilt-module-path=" + TestDir.str().str(); + const char *Args[] = {"clang++", + "-std=c++20", + "--precompile", + PrebuiltModulePath.c_str(), + "-working-directory", + TestDir.c_str(), + "-I", + TestDir.c_str(), + FileName.c_str(), + "-o", + CacheBMIPath.c_str()}; + std::shared_ptr<CompilerInvocation> Invocation = + createInvocation(Args, CIOpts); + EXPECT_TRUE(Invocation); + + CompilerInstance Instance; + Instance.setDiagnostics(Diags.get()); + Instance.setInvocation(Invocation); + GenerateModuleInterfaceAction Action; + EXPECT_TRUE(Instance.ExecuteAction(Action)); + EXPECT_FALSE(Diags->hasErrorOccurred()); + + return CacheBMIPath; + } +}; + +class DeclsReaderListener : public ASTDeserializationListener { +public: + void DeclRead(serialization::DeclID ID, const Decl *D) override { + auto *ND = dyn_cast<NamedDecl>(D); + if (!ND) + return; + + EXPECT_FALSE(ND->getName().contains(ForbiddenName)); + } + + DeclsReaderListener(StringRef ForbiddenName) : ForbiddenName(ForbiddenName) {} + + StringRef ForbiddenName; +}; + +class LoadSpecLazilyConsumer : public ASTConsumer { + DeclsReaderListener Listener; + +public: + LoadSpecLazilyConsumer(StringRef ForbiddenName) : Listener(ForbiddenName) {} + + ASTDeserializationListener *GetASTDeserializationListener() override { + return &Listener; + } +}; + +class CheckLoadSpecLazilyAction : public ASTFrontendAction { + StringRef ForbiddenName; + +public: + std::unique_ptr<ASTConsumer> + CreateASTConsumer(CompilerInstance &CI, StringRef /*Unused*/) override { + return std::make_unique<LoadSpecLazilyConsumer>(ForbiddenName); + } + + CheckLoadSpecLazilyAction(StringRef ForbiddenName) + : ForbiddenName(ForbiddenName) {} +}; + +TEST_F(LoadSpecLazilyTest, BasicTest) { + GenerateModuleInterface("M", R"cpp( +export module M; +export template <class T> +class A {}; + +export class ShouldNotBeLoaded {}; + +export class Temp { + A<ShouldNotBeLoaded> AS; +}; + )cpp"); + + const char *test_file_contents = R"cpp( +import M; +A<int> a; + )cpp"; + std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str(); + EXPECT_TRUE(runToolOnCodeWithArgs( + std::make_unique<CheckLoadSpecLazilyAction>("ShouldNotBeLoaded"), + test_file_contents, + { + "-std=c++20", + DepArg.c_str(), + "-I", + TestDir.c_str(), + }, + "test.cpp")); +} + +} // namespace >From fd2d753329e9c25bce6e4d012dc74c37eaf5ad07 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <yedeng...@linux.alibaba.com> Date: Thu, 4 Jan 2024 16:19:05 +0800 Subject: [PATCH 2/2] Load Specialization Updates Lazily --- .../include/clang/Serialization/ASTBitCodes.h | 2 + clang/include/clang/Serialization/ASTReader.h | 12 ++++-- clang/include/clang/Serialization/ASTWriter.h | 8 ++++ clang/lib/Serialization/ASTCommon.h | 2 +- clang/lib/Serialization/ASTReader.cpp | 26 +++++++++-- clang/lib/Serialization/ASTReaderDecl.cpp | 41 ++++++++++++------ clang/lib/Serialization/ASTWriter.cpp | 43 ++++++++++++++++--- clang/lib/Serialization/ASTWriterDecl.cpp | 9 +++- clang/test/Modules/cxx-templates.cpp | 9 ++-- .../Serialization/LoadSpecLazily.cpp | 34 +++++++++++++++ 10 files changed, 152 insertions(+), 34 deletions(-) diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 23a279de96ab15..212ae7db30faa0 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -695,6 +695,8 @@ enum ASTRecordTypes { /// Record code for an unterminated \#pragma clang assume_nonnull begin /// recorded in a preamble. PP_ASSUME_NONNULL_LOC = 67, + + UPDATE_SPECIALIZATION = 68, }; /// Record types used within a source manager block. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 293d6495d164ef..10726b440de515 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -610,18 +610,22 @@ class ASTReader // Updates for visible decls can occur for other contexts than just the // TU, and when we read those update records, the actual context may not // be available yet, so have this pending map using the ID as a key. It - // will be realized when the context is actually loaded. - struct PendingVisibleUpdate { + // will be realized when the data is actually loaded. + struct UpdateData { ModuleFile *Mod; const unsigned char *Data; }; - using DeclContextVisibleUpdates = SmallVector<PendingVisibleUpdate, 1>; + using DeclContextVisibleUpdates = SmallVector<UpdateData, 1>; /// Updates to the visible declarations of declaration contexts that /// haven't been loaded yet. llvm::DenseMap<serialization::DeclID, DeclContextVisibleUpdates> PendingVisibleUpdates; + using SpecializationsUpdate = SmallVector<UpdateData, 1>; + llvm::DenseMap<serialization::DeclID, SpecializationsUpdate> + PendingSpecializationsUpdates; + /// The set of C++ or Objective-C classes that have forward /// declarations that have not yet been linked to their definitions. llvm::SmallPtrSet<Decl *, 4> PendingDefinitions; @@ -650,6 +654,8 @@ class ASTReader bool ReadSpecializations(ModuleFile &M, llvm::BitstreamCursor &Cursor, uint64_t Offset, Decl *D); + void AddSpecializations(const Decl *D, const unsigned char *Data, + ModuleFile &M); /// A vector containing identifiers that have already been /// loaded. diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index 09806b87590766..0d39f8ace87843 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -385,6 +385,10 @@ class ASTWriter : public ASTDeserializationListener, /// record containing modifications to them. DeclUpdateMap DeclUpdates; + using SpecializationUpdateMap = + llvm::MapVector<const NamedDecl *, SmallVector<const NamedDecl *>>; + SpecializationUpdateMap SpecializationsUpdates; + using FirstLatestDeclMap = llvm::DenseMap<Decl *, Decl *>; /// Map of first declarations from a chained PCH that point to the @@ -527,6 +531,9 @@ class ASTWriter : public ASTDeserializationListener, bool isLookupResultExternal(StoredDeclsList &Result, DeclContext *DC); bool isLookupResultEntirelyExternal(StoredDeclsList &Result, DeclContext *DC); + void GenerateSpecializationsLookupTable( + const NamedDecl *D, llvm::SmallVectorImpl<const NamedDecl *> &Specs, + llvm::SmallVectorImpl<char> &LookupTable); uint64_t WriteSpecializationsLookupTable( const NamedDecl *D, llvm::SmallVectorImpl<const NamedDecl *> &Specializations); @@ -542,6 +549,7 @@ class ASTWriter : public ASTDeserializationListener, void WriteReferencedSelectorsPool(Sema &SemaRef); void WriteIdentifierTable(Preprocessor &PP, IdentifierResolver &IdResolver, bool IsModule); + void WriteSpecializationsUpdates(); void WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord); void WriteDeclContextVisibleUpdate(const DeclContext *DC); void WriteFPPragmaOptions(const FPOptionsOverride &Opts); diff --git a/clang/lib/Serialization/ASTCommon.h b/clang/lib/Serialization/ASTCommon.h index 296642e3674a49..9d190c05062444 100644 --- a/clang/lib/Serialization/ASTCommon.h +++ b/clang/lib/Serialization/ASTCommon.h @@ -23,7 +23,7 @@ namespace serialization { enum DeclUpdateKind { UPD_CXX_ADDED_IMPLICIT_MEMBER, - UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION, + UPD_CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION, UPD_CXX_ADDED_ANONYMOUS_NAMESPACE, UPD_CXX_ADDED_FUNCTION_DEFINITION, UPD_CXX_ADDED_VAR_DEFINITION, diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index bcdd2dfc491d8f..5583fce2e4b5f6 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -1344,6 +1344,13 @@ bool ASTReader::ReadVisibleDeclContextStorage(ModuleFile &M, return false; } +void ASTReader::AddSpecializations(const Decl *D, const unsigned char *Data, + ModuleFile &M) { + D = D->getCanonicalDecl(); + SpecializationsLookups[D].Table.add( + &M, Data, reader::SpecializationsLookupTrait(*this, M)); +} + bool ASTReader::ReadSpecializations(ModuleFile &M, BitstreamCursor &Cursor, uint64_t Offset, Decl *D) { assert(Offset != 0); @@ -1375,10 +1382,7 @@ bool ASTReader::ReadSpecializations(ModuleFile &M, BitstreamCursor &Cursor, } auto *Data = (const unsigned char *)Blob.data(); - D = D->getCanonicalDecl(); - SpecializationsLookups[D].Table.add( - &M, Data, reader::SpecializationsLookupTrait(*this, M)); - + AddSpecializations(D, Data, M); return false; } @@ -3481,6 +3485,20 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, break; } + case UPDATE_SPECIALIZATION: { + unsigned Idx = 0; + serialization::DeclID ID = ReadDeclID(F, Record, Idx); + auto *Data = (const unsigned char *)Blob.data(); + PendingSpecializationsUpdates[ID].push_back( + PendingVisibleUpdate{&F, Data}); + // If we've already loaded the decl, perform the updates when we finish + // loading this block. + if (Decl *D = GetExistingDecl(ID)) + PendingUpdateRecords.push_back( + PendingUpdateRecord(ID, D, /*JustLoaded=*/false)); + break; + } + case IDENTIFIER_TABLE: F.IdentifierTableData = reinterpret_cast<const unsigned char *>(Blob.data()); diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index e3ffcee6988b3f..cbc21be234f1df 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -321,7 +321,9 @@ namespace clang { void ReadFunctionDefinition(FunctionDecl *FD); void Visit(Decl *D); - void UpdateDecl(Decl *D, SmallVectorImpl<serialization::DeclID> &); + void UpdateDecl( + Decl *D, + SmallVectorImpl<serialization::DeclID> &UpdatedPartialSpecializations); static void setNextObjCCategory(ObjCCategoryDecl *Cat, ObjCCategoryDecl *Next) { @@ -4194,7 +4196,7 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { ProcessingUpdatesRAIIObj ProcessingUpdates(*this); DeclUpdateOffsetsMap::iterator UpdI = DeclUpdateOffsets.find(ID); - SmallVector<serialization::DeclID, 8> PendingLazySpecializationIDs; + SmallVector<serialization::DeclID, 8> PendingLazyPartialSpecializationIDs; if (UpdI != DeclUpdateOffsets.end()) { auto UpdateOffsets = std::move(UpdI->second); @@ -4233,7 +4235,7 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { ASTDeclReader Reader(*this, Record, RecordLocation(F, Offset), ID, SourceLocation()); - Reader.UpdateDecl(D, PendingLazySpecializationIDs); + Reader.UpdateDecl(D, PendingLazyPartialSpecializationIDs); // We might have made this declaration interesting. If so, remember that // we need to hand it off to the consumer. @@ -4246,16 +4248,16 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { } } // Add the lazy specializations to the template. - assert((PendingLazySpecializationIDs.empty() || isa<ClassTemplateDecl>(D) || - isa<FunctionTemplateDecl, VarTemplateDecl>(D)) && + assert((PendingLazyPartialSpecializationIDs.empty() || + isa<ClassTemplateDecl, VarTemplateDecl>(D)) && "Must not have pending specializations"); if (auto *CTD = dyn_cast<ClassTemplateDecl>(D)) - ASTDeclReader::AddLazySpecializations(CTD, PendingLazySpecializationIDs); - else if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D)) - ASTDeclReader::AddLazySpecializations(FTD, PendingLazySpecializationIDs); + ASTDeclReader::AddLazySpecializations(CTD, + PendingLazyPartialSpecializationIDs); else if (auto *VTD = dyn_cast<VarTemplateDecl>(D)) - ASTDeclReader::AddLazySpecializations(VTD, PendingLazySpecializationIDs); - PendingLazySpecializationIDs.clear(); + ASTDeclReader::AddLazySpecializations(VTD, + PendingLazyPartialSpecializationIDs); + PendingLazyPartialSpecializationIDs.clear(); // Load the pending visible updates for this decl context, if it has any. auto I = PendingVisibleUpdates.find(ID); @@ -4270,6 +4272,16 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { reader::ASTDeclContextNameLookupTrait(*this, *Update.Mod)); DC->setHasExternalVisibleStorage(true); } + + // Load the pending specializations update for this decl, if it has any. + if (auto I = PendingSpecializationsUpdates.find(ID); + I != PendingSpecializationsUpdates.end()) { + auto SpecializationUpdates = std::move(I->second); + PendingSpecializationsUpdates.erase(I); + + for (const auto &Update : SpecializationUpdates) + AddSpecializations(D, Update.Data, *Update.Mod); + } } void ASTReader::loadPendingDeclChain(Decl *FirstLocal, uint64_t LocalOffset) { @@ -4464,7 +4476,8 @@ static void forAllLaterRedecls(DeclT *D, Fn F) { } void ASTDeclReader::UpdateDecl(Decl *D, - llvm::SmallVectorImpl<serialization::DeclID> &PendingLazySpecializationIDs) { + llvm::SmallVectorImpl<serialization::DeclID> + &PendingLazyPartialSpecializationIDs) { while (Record.getIdx() < Record.size()) { switch ((DeclUpdateKind)Record.readInt()) { case UPD_CXX_ADDED_IMPLICIT_MEMBER: { @@ -4475,9 +4488,9 @@ void ASTDeclReader::UpdateDecl(Decl *D, break; } - case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION: - // It will be added to the template's lazy specialization set. - PendingLazySpecializationIDs.push_back(readDeclID()); + case UPD_CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION: + // It will be added to the template's lazy partial specialization set. + PendingLazyPartialSpecializationIDs.push_back(readDeclID()); break; case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE: { diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index ed58e71b01c09a..733dab8dfb9c09 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -4023,9 +4023,10 @@ unsigned CalculateODRHashForSpecs(const Decl *Spec) { } } // namespace -uint64_t ASTWriter::WriteSpecializationsLookupTable( +void ASTWriter::GenerateSpecializationsLookupTable( const NamedDecl *D, - llvm::SmallVectorImpl<const NamedDecl *> &Specializations) { + llvm::SmallVectorImpl<const NamedDecl *> &Specializations, + llvm::SmallVectorImpl<char> &LookupTable) { assert(D->isFirstDecl()); // Create the on-disk hash table representation. @@ -4053,13 +4054,19 @@ uint64_t ASTWriter::WriteSpecializationsLookupTable( for (auto Iter : SpecializationMaps) Generator.insert(Iter.first, Trait.getData(Iter.second), Trait); - uint64_t Offset = Stream.GetCurrentBitNo(); - auto *Lookups = Chain ? Chain->getLoadedSpecializationsLookupTables(D) : nullptr; - llvm::SmallString<4096> LookupTable; Generator.emit(LookupTable, Trait, Lookups ? &Lookups->Table : nullptr); +} + +uint64_t ASTWriter::WriteSpecializationsLookupTable( + const NamedDecl *D, + llvm::SmallVectorImpl<const NamedDecl *> &Specializations) { + + llvm::SmallString<4096> LookupTable; + GenerateSpecializationsLookupTable(D, Specializations, LookupTable); + uint64_t Offset = Stream.GetCurrentBitNo(); RecordData::value_type Record[] = {DECL_SPECIALIZATIONS}; Stream.EmitRecordWithBlob(DeclSpecializationsAbbrev, Record, LookupTable); @@ -5239,6 +5246,10 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, WriteTypeDeclOffsets(); if (!DeclUpdatesOffsetsRecord.empty()) Stream.EmitRecord(DECL_UPDATE_OFFSETS, DeclUpdatesOffsetsRecord); + + if (!SpecializationsUpdates.empty()) + WriteSpecializationsUpdates(); + WriteFileDeclIDsMap(); WriteSourceManagerBlock(Context.getSourceManager(), PP); WriteComments(); @@ -5391,6 +5402,26 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, return backpatchSignature(); } +void ASTWriter::WriteSpecializationsUpdates() { + auto Abv = std::make_shared<llvm::BitCodeAbbrev>(); + Abv->Add(llvm::BitCodeAbbrevOp(UPDATE_SPECIALIZATION)); + Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6)); + Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)); + auto UpdateSpecializationAbbrev = Stream.EmitAbbrev(std::move(Abv)); + + for (auto &SpecializationUpdate : SpecializationsUpdates) { + const NamedDecl *D = SpecializationUpdate.first; + + llvm::SmallString<4096> LookupTable; + GenerateSpecializationsLookupTable(D, SpecializationUpdate.second, + LookupTable); + + // Write the lookup table + RecordData::value_type Record[] = {UPDATE_SPECIALIZATION, getDeclID(D)}; + Stream.EmitRecordWithBlob(UpdateSpecializationAbbrev, Record, LookupTable); + } +} + void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) { if (DeclUpdates.empty()) return; @@ -5419,7 +5450,7 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) { switch (Kind) { case UPD_CXX_ADDED_IMPLICIT_MEMBER: - case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION: + case UPD_CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION: case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE: assert(Update.getDecl() && "no decl to add?"); Record.push_back(GetDeclRef(Update.getDecl())); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index db3800025f8d27..9be609700aeedb 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -271,8 +271,13 @@ namespace clang { if (Writer.getFirstLocalDecl(Specialization) != Specialization) return; - Writer.DeclUpdates[Template].push_back(ASTWriter::DeclUpdate( - UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION, Specialization)); + if (isa<ClassTemplatePartialSpecializationDecl, + VarTemplatePartialSpecializationDecl>(Specialization)) + Writer.DeclUpdates[Template].push_back(ASTWriter::DeclUpdate( + UPD_CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION, Specialization)); + else + Writer.SpecializationsUpdates[cast<NamedDecl>(Template)].push_back( + cast<NamedDecl>(Specialization)); } }; } diff --git a/clang/test/Modules/cxx-templates.cpp b/clang/test/Modules/cxx-templates.cpp index b7d5741e69af61..2d285c10ceec59 100644 --- a/clang/test/Modules/cxx-templates.cpp +++ b/clang/test/Modules/cxx-templates.cpp @@ -251,7 +251,7 @@ 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-NEXT: TemplateArgument type 'char[1]' // CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition // CHECK-DUMP-NEXT: DefinitionData // CHECK-DUMP-NEXT: DefaultConstructor @@ -260,9 +260,9 @@ namespace Std { // CHECK-DUMP-NEXT: CopyAssignment // CHECK-DUMP-NEXT: MoveAssignment // CHECK-DUMP-NEXT: Destructor -// CHECK-DUMP-NEXT: TemplateArgument type 'char[2]' -// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate // 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: DefinitionData // CHECK-DUMP-NEXT: DefaultConstructor @@ -271,4 +271,5 @@ namespace Std { // CHECK-DUMP-NEXT: CopyAssignment // CHECK-DUMP-NEXT: MoveAssignment // CHECK-DUMP-NEXT: Destructor -// CHECK-DUMP-NEXT: TemplateArgument type 'char[1]' +// CHECK-DUMP-NEXT: TemplateArgument type 'char[2]' + diff --git a/clang/unittests/Serialization/LoadSpecLazily.cpp b/clang/unittests/Serialization/LoadSpecLazily.cpp index 03f3ff3f786555..5a174f2335a4b9 100644 --- a/clang/unittests/Serialization/LoadSpecLazily.cpp +++ b/clang/unittests/Serialization/LoadSpecLazily.cpp @@ -156,4 +156,38 @@ A<int> a; "test.cpp")); } +TEST_F(LoadSpecLazilyTest, ChainedTest) { + GenerateModuleInterface("M", R"cpp( +export module M; +export template <class T> +class A {}; + )cpp"); + + GenerateModuleInterface("N", R"cpp( +export module N; +export import M; +export class ShouldNotBeLoaded {}; + +export class Temp { + A<ShouldNotBeLoaded> AS; +}; + )cpp"); + + const char *test_file_contents = R"cpp( +import N; +A<int> a; + )cpp"; + std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str(); + EXPECT_TRUE(runToolOnCodeWithArgs( + std::make_unique<CheckLoadSpecLazilyAction>("ShouldNotBeLoaded"), + test_file_contents, + { + "-std=c++20", + DepArg.c_str(), + "-I", + TestDir.c_str(), + }, + "test.cpp")); +} + } // namespace _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits