Skip to content

Commit 23184b2

Browse files
committed
Load Specialization Updates Lazily
1 parent 50fd47f commit 23184b2

File tree

10 files changed

+152
-34
lines changed

10 files changed

+152
-34
lines changed

clang/include/clang/Serialization/ASTBitCodes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,8 @@ enum ASTRecordTypes {
695695
/// Record code for an unterminated \#pragma clang assume_nonnull begin
696696
/// recorded in a preamble.
697697
PP_ASSUME_NONNULL_LOC = 67,
698+
699+
UPDATE_SPECIALIZATION = 68,
698700
};
699701

700702
/// Record types used within a source manager block.

clang/include/clang/Serialization/ASTReader.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -610,18 +610,22 @@ class ASTReader
610610
// Updates for visible decls can occur for other contexts than just the
611611
// TU, and when we read those update records, the actual context may not
612612
// be available yet, so have this pending map using the ID as a key. It
613-
// will be realized when the context is actually loaded.
614-
struct PendingVisibleUpdate {
613+
// will be realized when the data is actually loaded.
614+
struct UpdateData {
615615
ModuleFile *Mod;
616616
const unsigned char *Data;
617617
};
618-
using DeclContextVisibleUpdates = SmallVector<PendingVisibleUpdate, 1>;
618+
using DeclContextVisibleUpdates = SmallVector<UpdateData, 1>;
619619

620620
/// Updates to the visible declarations of declaration contexts that
621621
/// haven't been loaded yet.
622622
llvm::DenseMap<serialization::DeclID, DeclContextVisibleUpdates>
623623
PendingVisibleUpdates;
624624

625+
using SpecializationsUpdate = SmallVector<UpdateData, 1>;
626+
llvm::DenseMap<serialization::DeclID, SpecializationsUpdate>
627+
PendingSpecializationsUpdates;
628+
625629
/// The set of C++ or Objective-C classes that have forward
626630
/// declarations that have not yet been linked to their definitions.
627631
llvm::SmallPtrSet<Decl *, 4> PendingDefinitions;
@@ -650,6 +654,8 @@ class ASTReader
650654

651655
bool ReadSpecializations(ModuleFile &M, llvm::BitstreamCursor &Cursor,
652656
uint64_t Offset, Decl *D);
657+
void AddSpecializations(const Decl *D, const unsigned char *Data,
658+
ModuleFile &M);
653659

654660
/// A vector containing identifiers that have already been
655661
/// loaded.

clang/include/clang/Serialization/ASTWriter.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,10 @@ class ASTWriter : public ASTDeserializationListener,
385385
/// record containing modifications to them.
386386
DeclUpdateMap DeclUpdates;
387387

388+
using SpecializationUpdateMap =
389+
llvm::MapVector<const NamedDecl *, SmallVector<const NamedDecl *>>;
390+
SpecializationUpdateMap SpecializationsUpdates;
391+
388392
using FirstLatestDeclMap = llvm::DenseMap<Decl *, Decl *>;
389393

390394
/// Map of first declarations from a chained PCH that point to the
@@ -527,6 +531,9 @@ class ASTWriter : public ASTDeserializationListener,
527531
bool isLookupResultExternal(StoredDeclsList &Result, DeclContext *DC);
528532
bool isLookupResultEntirelyExternal(StoredDeclsList &Result, DeclContext *DC);
529533

534+
void GenerateSpecializationsLookupTable(
535+
const NamedDecl *D, llvm::SmallVectorImpl<const NamedDecl *> &Specs,
536+
llvm::SmallVectorImpl<char> &LookupTable);
530537
uint64_t WriteSpecializationsLookupTable(
531538
const NamedDecl *D,
532539
llvm::SmallVectorImpl<const NamedDecl *> &Specializations);
@@ -542,6 +549,7 @@ class ASTWriter : public ASTDeserializationListener,
542549
void WriteReferencedSelectorsPool(Sema &SemaRef);
543550
void WriteIdentifierTable(Preprocessor &PP, IdentifierResolver &IdResolver,
544551
bool IsModule);
552+
void WriteSpecializationsUpdates();
545553
void WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord);
546554
void WriteDeclContextVisibleUpdate(const DeclContext *DC);
547555
void WriteFPPragmaOptions(const FPOptionsOverride &Opts);

clang/lib/Serialization/ASTCommon.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ namespace serialization {
2323

2424
enum DeclUpdateKind {
2525
UPD_CXX_ADDED_IMPLICIT_MEMBER,
26-
UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION,
26+
UPD_CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION,
2727
UPD_CXX_ADDED_ANONYMOUS_NAMESPACE,
2828
UPD_CXX_ADDED_FUNCTION_DEFINITION,
2929
UPD_CXX_ADDED_VAR_DEFINITION,

clang/lib/Serialization/ASTReader.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,13 @@ bool ASTReader::ReadVisibleDeclContextStorage(ModuleFile &M,
13441344
return false;
13451345
}
13461346

1347+
void ASTReader::AddSpecializations(const Decl *D, const unsigned char *Data,
1348+
ModuleFile &M) {
1349+
D = D->getCanonicalDecl();
1350+
SpecializationsLookups[D].Table.add(
1351+
&M, Data, reader::SpecializationsLookupTrait(*this, M));
1352+
}
1353+
13471354
bool ASTReader::ReadSpecializations(ModuleFile &M, BitstreamCursor &Cursor,
13481355
uint64_t Offset, Decl *D) {
13491356
assert(Offset != 0);
@@ -1375,10 +1382,7 @@ bool ASTReader::ReadSpecializations(ModuleFile &M, BitstreamCursor &Cursor,
13751382
}
13761383

13771384
auto *Data = (const unsigned char *)Blob.data();
1378-
D = D->getCanonicalDecl();
1379-
SpecializationsLookups[D].Table.add(
1380-
&M, Data, reader::SpecializationsLookupTrait(*this, M));
1381-
1385+
AddSpecializations(D, Data, M);
13821386
return false;
13831387
}
13841388

@@ -3481,6 +3485,20 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
34813485
break;
34823486
}
34833487

3488+
case UPDATE_SPECIALIZATION: {
3489+
unsigned Idx = 0;
3490+
serialization::DeclID ID = ReadDeclID(F, Record, Idx);
3491+
auto *Data = (const unsigned char *)Blob.data();
3492+
PendingSpecializationsUpdates[ID].push_back(
3493+
PendingVisibleUpdate{&F, Data});
3494+
// If we've already loaded the decl, perform the updates when we finish
3495+
// loading this block.
3496+
if (Decl *D = GetExistingDecl(ID))
3497+
PendingUpdateRecords.push_back(
3498+
PendingUpdateRecord(ID, D, /*JustLoaded=*/false));
3499+
break;
3500+
}
3501+
34843502
case IDENTIFIER_TABLE:
34853503
F.IdentifierTableData =
34863504
reinterpret_cast<const unsigned char *>(Blob.data());

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,9 @@ namespace clang {
321321
void ReadFunctionDefinition(FunctionDecl *FD);
322322
void Visit(Decl *D);
323323

324-
void UpdateDecl(Decl *D, SmallVectorImpl<serialization::DeclID> &);
324+
void UpdateDecl(
325+
Decl *D,
326+
SmallVectorImpl<serialization::DeclID> &UpdatedPartialSpecializations);
325327

326328
static void setNextObjCCategory(ObjCCategoryDecl *Cat,
327329
ObjCCategoryDecl *Next) {
@@ -4194,7 +4196,7 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
41944196
ProcessingUpdatesRAIIObj ProcessingUpdates(*this);
41954197
DeclUpdateOffsetsMap::iterator UpdI = DeclUpdateOffsets.find(ID);
41964198

4197-
SmallVector<serialization::DeclID, 8> PendingLazySpecializationIDs;
4199+
SmallVector<serialization::DeclID, 8> PendingLazyPartialSpecializationIDs;
41984200

41994201
if (UpdI != DeclUpdateOffsets.end()) {
42004202
auto UpdateOffsets = std::move(UpdI->second);
@@ -4233,7 +4235,7 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
42334235

42344236
ASTDeclReader Reader(*this, Record, RecordLocation(F, Offset), ID,
42354237
SourceLocation());
4236-
Reader.UpdateDecl(D, PendingLazySpecializationIDs);
4238+
Reader.UpdateDecl(D, PendingLazyPartialSpecializationIDs);
42374239

42384240
// We might have made this declaration interesting. If so, remember that
42394241
// we need to hand it off to the consumer.
@@ -4246,16 +4248,16 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
42464248
}
42474249
}
42484250
// Add the lazy specializations to the template.
4249-
assert((PendingLazySpecializationIDs.empty() || isa<ClassTemplateDecl>(D) ||
4250-
isa<FunctionTemplateDecl, VarTemplateDecl>(D)) &&
4251+
assert((PendingLazyPartialSpecializationIDs.empty() ||
4252+
isa<ClassTemplateDecl, VarTemplateDecl>(D)) &&
42514253
"Must not have pending specializations");
42524254
if (auto *CTD = dyn_cast<ClassTemplateDecl>(D))
4253-
ASTDeclReader::AddLazySpecializations(CTD, PendingLazySpecializationIDs);
4254-
else if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
4255-
ASTDeclReader::AddLazySpecializations(FTD, PendingLazySpecializationIDs);
4255+
ASTDeclReader::AddLazySpecializations(CTD,
4256+
PendingLazyPartialSpecializationIDs);
42564257
else if (auto *VTD = dyn_cast<VarTemplateDecl>(D))
4257-
ASTDeclReader::AddLazySpecializations(VTD, PendingLazySpecializationIDs);
4258-
PendingLazySpecializationIDs.clear();
4258+
ASTDeclReader::AddLazySpecializations(VTD,
4259+
PendingLazyPartialSpecializationIDs);
4260+
PendingLazyPartialSpecializationIDs.clear();
42594261

42604262
// Load the pending visible updates for this decl context, if it has any.
42614263
auto I = PendingVisibleUpdates.find(ID);
@@ -4270,6 +4272,16 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
42704272
reader::ASTDeclContextNameLookupTrait(*this, *Update.Mod));
42714273
DC->setHasExternalVisibleStorage(true);
42724274
}
4275+
4276+
// Load the pending specializations update for this decl, if it has any.
4277+
if (auto I = PendingSpecializationsUpdates.find(ID);
4278+
I != PendingSpecializationsUpdates.end()) {
4279+
auto SpecializationUpdates = std::move(I->second);
4280+
PendingSpecializationsUpdates.erase(I);
4281+
4282+
for (const auto &Update : SpecializationUpdates)
4283+
AddSpecializations(D, Update.Data, *Update.Mod);
4284+
}
42734285
}
42744286

42754287
void ASTReader::loadPendingDeclChain(Decl *FirstLocal, uint64_t LocalOffset) {
@@ -4464,7 +4476,8 @@ static void forAllLaterRedecls(DeclT *D, Fn F) {
44644476
}
44654477

44664478
void ASTDeclReader::UpdateDecl(Decl *D,
4467-
llvm::SmallVectorImpl<serialization::DeclID> &PendingLazySpecializationIDs) {
4479+
llvm::SmallVectorImpl<serialization::DeclID>
4480+
&PendingLazyPartialSpecializationIDs) {
44684481
while (Record.getIdx() < Record.size()) {
44694482
switch ((DeclUpdateKind)Record.readInt()) {
44704483
case UPD_CXX_ADDED_IMPLICIT_MEMBER: {
@@ -4475,9 +4488,9 @@ void ASTDeclReader::UpdateDecl(Decl *D,
44754488
break;
44764489
}
44774490

4478-
case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION:
4479-
// It will be added to the template's lazy specialization set.
4480-
PendingLazySpecializationIDs.push_back(readDeclID());
4491+
case UPD_CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION:
4492+
// It will be added to the template's lazy partial specialization set.
4493+
PendingLazyPartialSpecializationIDs.push_back(readDeclID());
44814494
break;
44824495

44834496
case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE: {

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4023,9 +4023,10 @@ unsigned CalculateODRHashForSpecs(const Decl *Spec) {
40234023
}
40244024
} // namespace
40254025

4026-
uint64_t ASTWriter::WriteSpecializationsLookupTable(
4026+
void ASTWriter::GenerateSpecializationsLookupTable(
40274027
const NamedDecl *D,
4028-
llvm::SmallVectorImpl<const NamedDecl *> &Specializations) {
4028+
llvm::SmallVectorImpl<const NamedDecl *> &Specializations,
4029+
llvm::SmallVectorImpl<char> &LookupTable) {
40294030
assert(D->isFirstDecl());
40304031

40314032
// Create the on-disk hash table representation.
@@ -4053,13 +4054,19 @@ uint64_t ASTWriter::WriteSpecializationsLookupTable(
40534054
for (auto Iter : SpecializationMaps)
40544055
Generator.insert(Iter.first, Trait.getData(Iter.second), Trait);
40554056

4056-
uint64_t Offset = Stream.GetCurrentBitNo();
4057-
40584057
auto *Lookups =
40594058
Chain ? Chain->getLoadedSpecializationsLookupTables(D) : nullptr;
4060-
llvm::SmallString<4096> LookupTable;
40614059
Generator.emit(LookupTable, Trait, Lookups ? &Lookups->Table : nullptr);
4060+
}
4061+
4062+
uint64_t ASTWriter::WriteSpecializationsLookupTable(
4063+
const NamedDecl *D,
4064+
llvm::SmallVectorImpl<const NamedDecl *> &Specializations) {
4065+
4066+
llvm::SmallString<4096> LookupTable;
4067+
GenerateSpecializationsLookupTable(D, Specializations, LookupTable);
40624068

4069+
uint64_t Offset = Stream.GetCurrentBitNo();
40634070
RecordData::value_type Record[] = {DECL_SPECIALIZATIONS};
40644071
Stream.EmitRecordWithBlob(DeclSpecializationsAbbrev, Record, LookupTable);
40654072

@@ -5239,6 +5246,10 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
52395246
WriteTypeDeclOffsets();
52405247
if (!DeclUpdatesOffsetsRecord.empty())
52415248
Stream.EmitRecord(DECL_UPDATE_OFFSETS, DeclUpdatesOffsetsRecord);
5249+
5250+
if (!SpecializationsUpdates.empty())
5251+
WriteSpecializationsUpdates();
5252+
52425253
WriteFileDeclIDsMap();
52435254
WriteSourceManagerBlock(Context.getSourceManager(), PP);
52445255
WriteComments();
@@ -5391,6 +5402,26 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
53915402
return backpatchSignature();
53925403
}
53935404

5405+
void ASTWriter::WriteSpecializationsUpdates() {
5406+
auto Abv = std::make_shared<llvm::BitCodeAbbrev>();
5407+
Abv->Add(llvm::BitCodeAbbrevOp(UPDATE_SPECIALIZATION));
5408+
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6));
5409+
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
5410+
auto UpdateSpecializationAbbrev = Stream.EmitAbbrev(std::move(Abv));
5411+
5412+
for (auto &SpecializationUpdate : SpecializationsUpdates) {
5413+
const NamedDecl *D = SpecializationUpdate.first;
5414+
5415+
llvm::SmallString<4096> LookupTable;
5416+
GenerateSpecializationsLookupTable(D, SpecializationUpdate.second,
5417+
LookupTable);
5418+
5419+
// Write the lookup table
5420+
RecordData::value_type Record[] = {UPDATE_SPECIALIZATION, getDeclID(D)};
5421+
Stream.EmitRecordWithBlob(UpdateSpecializationAbbrev, Record, LookupTable);
5422+
}
5423+
}
5424+
53945425
void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
53955426
if (DeclUpdates.empty())
53965427
return;
@@ -5419,7 +5450,7 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
54195450

54205451
switch (Kind) {
54215452
case UPD_CXX_ADDED_IMPLICIT_MEMBER:
5422-
case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION:
5453+
case UPD_CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION:
54235454
case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE:
54245455
assert(Update.getDecl() && "no decl to add?");
54255456
Record.push_back(GetDeclRef(Update.getDecl()));

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,13 @@ namespace clang {
271271
if (Writer.getFirstLocalDecl(Specialization) != Specialization)
272272
return;
273273

274-
Writer.DeclUpdates[Template].push_back(ASTWriter::DeclUpdate(
275-
UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION, Specialization));
274+
if (isa<ClassTemplatePartialSpecializationDecl,
275+
VarTemplatePartialSpecializationDecl>(Specialization))
276+
Writer.DeclUpdates[Template].push_back(ASTWriter::DeclUpdate(
277+
UPD_CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION, Specialization));
278+
else
279+
Writer.SpecializationsUpdates[cast<NamedDecl>(Template)].push_back(
280+
cast<NamedDecl>(Specialization));
276281
}
277282
};
278283
}

clang/test/Modules/cxx-templates.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ namespace Std {
251251

252252
// CHECK-DUMP: ClassTemplateDecl {{.*}} <{{.*[/\\]}}cxx-templates-common.h:1:1, {{.*}}> col:{{.*}} in cxx_templates_common SomeTemplate
253253
// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
254-
// CHECK-DUMP-NEXT: TemplateArgument type 'char[2]'
254+
// CHECK-DUMP-NEXT: TemplateArgument type 'char[1]'
255255
// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
256256
// CHECK-DUMP-NEXT: DefinitionData
257257
// CHECK-DUMP-NEXT: DefaultConstructor
@@ -260,9 +260,9 @@ namespace Std {
260260
// CHECK-DUMP-NEXT: CopyAssignment
261261
// CHECK-DUMP-NEXT: MoveAssignment
262262
// CHECK-DUMP-NEXT: Destructor
263-
// CHECK-DUMP-NEXT: TemplateArgument type 'char[2]'
264-
// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
265263
// CHECK-DUMP-NEXT: TemplateArgument type 'char[1]'
264+
// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
265+
// CHECK-DUMP-NEXT: TemplateArgument type 'char[2]'
266266
// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
267267
// CHECK-DUMP-NEXT: DefinitionData
268268
// CHECK-DUMP-NEXT: DefaultConstructor
@@ -271,4 +271,5 @@ namespace Std {
271271
// CHECK-DUMP-NEXT: CopyAssignment
272272
// CHECK-DUMP-NEXT: MoveAssignment
273273
// CHECK-DUMP-NEXT: Destructor
274-
// CHECK-DUMP-NEXT: TemplateArgument type 'char[1]'
274+
// CHECK-DUMP-NEXT: TemplateArgument type 'char[2]'
275+

clang/unittests/Serialization/LoadSpecLazily.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,38 @@ A<int> a;
156156
"test.cpp"));
157157
}
158158

159+
TEST_F(LoadSpecLazilyTest, ChainedTest) {
160+
GenerateModuleInterface("M", R"cpp(
161+
export module M;
162+
export template <class T>
163+
class A {};
164+
)cpp");
165+
166+
GenerateModuleInterface("N", R"cpp(
167+
export module N;
168+
export import M;
169+
export class ShouldNotBeLoaded {};
170+
171+
export class Temp {
172+
A<ShouldNotBeLoaded> AS;
173+
};
174+
)cpp");
175+
176+
const char *test_file_contents = R"cpp(
177+
import N;
178+
A<int> a;
179+
)cpp";
180+
std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
181+
EXPECT_TRUE(runToolOnCodeWithArgs(
182+
std::make_unique<CheckLoadSpecLazilyAction>("ShouldNotBeLoaded"),
183+
test_file_contents,
184+
{
185+
"-std=c++20",
186+
DepArg.c_str(),
187+
"-I",
188+
TestDir.c_str(),
189+
},
190+
"test.cpp"));
191+
}
192+
159193
} // namespace

0 commit comments

Comments
 (0)