From 23184b26934c5a3ba833fb3d7a12adcafb6fad8e Mon Sep 17 00:00:00 2001 From: Chuanqi Xu Date: Thu, 4 Jan 2024 16:19:05 +0800 Subject: [PATCH] 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 23a279de96ab1..212ae7db30faa 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 293d6495d164e..10726b440de51 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; + using DeclContextVisibleUpdates = SmallVector; /// Updates to the visible declarations of declaration contexts that /// haven't been loaded yet. llvm::DenseMap PendingVisibleUpdates; + using SpecializationsUpdate = SmallVector; + llvm::DenseMap + PendingSpecializationsUpdates; + /// The set of C++ or Objective-C classes that have forward /// declarations that have not yet been linked to their definitions. llvm::SmallPtrSet 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 09806b8759076..0d39f8ace8784 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>; + SpecializationUpdateMap SpecializationsUpdates; + using FirstLatestDeclMap = llvm::DenseMap; /// 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 &Specs, + llvm::SmallVectorImpl &LookupTable); uint64_t WriteSpecializationsLookupTable( const NamedDecl *D, llvm::SmallVectorImpl &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 296642e3674a4..9d190c0506244 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 bcdd2dfc491d8..5583fce2e4b5f 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(Blob.data()); diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index e3ffcee6988b3..cbc21be234f1d 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 &); + void UpdateDecl( + Decl *D, + SmallVectorImpl &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 PendingLazySpecializationIDs; + SmallVector 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(D) || - isa(D)) && + assert((PendingLazyPartialSpecializationIDs.empty() || + isa(D)) && "Must not have pending specializations"); if (auto *CTD = dyn_cast(D)) - ASTDeclReader::AddLazySpecializations(CTD, PendingLazySpecializationIDs); - else if (auto *FTD = dyn_cast(D)) - ASTDeclReader::AddLazySpecializations(FTD, PendingLazySpecializationIDs); + ASTDeclReader::AddLazySpecializations(CTD, + PendingLazyPartialSpecializationIDs); else if (auto *VTD = dyn_cast(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 &PendingLazySpecializationIDs) { + llvm::SmallVectorImpl + &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 ed58e71b01c09..733dab8dfb9c0 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 &Specializations) { + llvm::SmallVectorImpl &Specializations, + llvm::SmallVectorImpl &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 &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(); + 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 db3800025f8d2..9be609700aeed 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(Specialization)) + Writer.DeclUpdates[Template].push_back(ASTWriter::DeclUpdate( + UPD_CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION, Specialization)); + else + Writer.SpecializationsUpdates[cast(Template)].push_back( + cast(Specialization)); } }; } diff --git a/clang/test/Modules/cxx-templates.cpp b/clang/test/Modules/cxx-templates.cpp index b7d5741e69af6..2d285c10ceec5 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 03f3ff3f78655..5a174f2335a4b 100644 --- a/clang/unittests/Serialization/LoadSpecLazily.cpp +++ b/clang/unittests/Serialization/LoadSpecLazily.cpp @@ -156,4 +156,38 @@ A a; "test.cpp")); } +TEST_F(LoadSpecLazilyTest, ChainedTest) { + GenerateModuleInterface("M", R"cpp( +export module M; +export template +class A {}; + )cpp"); + + GenerateModuleInterface("N", R"cpp( +export module N; +export import M; +export class ShouldNotBeLoaded {}; + +export class Temp { + A AS; +}; + )cpp"); + + const char *test_file_contents = R"cpp( +import N; +A a; + )cpp"; + std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str(); + EXPECT_TRUE(runToolOnCodeWithArgs( + std::make_unique("ShouldNotBeLoaded"), + test_file_contents, + { + "-std=c++20", + DepArg.c_str(), + "-I", + TestDir.c_str(), + }, + "test.cpp")); +} + } // namespace