Skip to content

Commit 20e9049

Browse files
authoredDec 11, 2024··
[Serialization] Support loading template specializations lazily (#119333)
Reland #83237 --- (Original comments) Currently all the specializations of a template (including instantiation, specialization and partial specializations) will be loaded at once if we want to instantiate another instance for the template, or find instantiation for the template, or just want to complete the redecl chain. This means basically we need to load every specializations for the template once the template declaration got loaded. This is bad since when we load a specialization, we need to load all of its template arguments. Then we have to deserialize a lot of unnecessary declarations. For example, ``` // M.cppm export module M; export template <class T> class A {}; export class ShouldNotBeLoaded {}; export class Temp { A<ShouldNotBeLoaded> AS; }; // use.cpp import M; A<int> a; ``` We have a specialization ` A<ShouldNotBeLoaded>` in `M.cppm` and we instantiate the template `A` in `use.cpp`. Then we will deserialize `ShouldNotBeLoaded` surprisingly when compiling `use.cpp`. And this patch tries to avoid that. Given that the templates are heavily used in C++, this is a pain point for the performance. This patch adds MultiOnDiskHashTable for specializations in the ASTReader. Then we will only deserialize the specializations with the same template arguments. We made that by using ODRHash for the template arguments as the key of the hash table. To review this patch, I think `ASTReaderDecl::AddLazySpecializations` may be a good entry point.
1 parent c00a708 commit 20e9049

29 files changed

+1645
-180
lines changed
 

‎clang/include/clang/AST/DeclTemplate.h

+13-10
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,7 @@ class RedeclarableTemplateDecl : public TemplateDecl,
735735
}
736736

737737
void anchor() override;
738+
738739
protected:
739740
template <typename EntryType> struct SpecEntryTraits {
740741
using DeclType = EntryType;
@@ -775,13 +776,22 @@ class RedeclarableTemplateDecl : public TemplateDecl,
775776
return SpecIterator<EntryType>(isEnd ? Specs.end() : Specs.begin());
776777
}
777778

778-
void loadLazySpecializationsImpl() const;
779+
void loadLazySpecializationsImpl(bool OnlyPartial = false) const;
780+
781+
bool loadLazySpecializationsImpl(llvm::ArrayRef<TemplateArgument> Args,
782+
TemplateParameterList *TPL = nullptr) const;
779783

780784
template <class EntryType, typename ...ProfileArguments>
781785
typename SpecEntryTraits<EntryType>::DeclType*
782786
findSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs,
783787
void *&InsertPos, ProfileArguments &&...ProfileArgs);
784788

789+
template <class EntryType, typename... ProfileArguments>
790+
typename SpecEntryTraits<EntryType>::DeclType *
791+
findSpecializationLocally(llvm::FoldingSetVector<EntryType> &Specs,
792+
void *&InsertPos,
793+
ProfileArguments &&...ProfileArgs);
794+
785795
template <class Derived, class EntryType>
786796
void addSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs,
787797
EntryType *Entry, void *InsertPos);
@@ -796,13 +806,6 @@ class RedeclarableTemplateDecl : public TemplateDecl,
796806
/// was explicitly specialized.
797807
llvm::PointerIntPair<RedeclarableTemplateDecl *, 1, bool>
798808
InstantiatedFromMember;
799-
800-
/// If non-null, points to an array of specializations (including
801-
/// partial specializations) known only by their external declaration IDs.
802-
///
803-
/// The first value in the array is the number of specializations/partial
804-
/// specializations that follow.
805-
GlobalDeclID *LazySpecializations = nullptr;
806809
};
807810

808811
/// Pointer to the common data shared by all declarations of this
@@ -2283,7 +2286,7 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
22832286
friend class TemplateDeclInstantiator;
22842287

22852288
/// Load any lazily-loaded specializations from the external source.
2286-
void LoadLazySpecializations() const;
2289+
void LoadLazySpecializations(bool OnlyPartial = false) const;
22872290

22882291
/// Get the underlying class declarations of the template.
22892292
CXXRecordDecl *getTemplatedDecl() const {
@@ -3033,7 +3036,7 @@ class VarTemplateDecl : public RedeclarableTemplateDecl {
30333036
friend class ASTDeclWriter;
30343037

30353038
/// Load any lazily-loaded specializations from the external source.
3036-
void LoadLazySpecializations() const;
3039+
void LoadLazySpecializations(bool OnlyPartial = false) const;
30373040

30383041
/// Get the underlying variable declarations of the template.
30393042
VarDecl *getTemplatedDecl() const {

‎clang/include/clang/AST/ExternalASTSource.h

+15
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,21 @@ class ExternalASTSource : public RefCountedBase<ExternalASTSource> {
152152
virtual bool
153153
FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name);
154154

155+
/// Load all the external specializations for the Decl \param D if \param
156+
/// OnlyPartial is false. Otherwise, load all the external **partial**
157+
/// specializations for the \param D.
158+
///
159+
/// Return true if any new specializations get loaded. Return false otherwise.
160+
virtual bool LoadExternalSpecializations(const Decl *D, bool OnlyPartial);
161+
162+
/// Load all the specializations for the Decl \param D with the same template
163+
/// args specified by \param TemplateArgs.
164+
///
165+
/// Return true if any new specializations get loaded. Return false otherwise.
166+
virtual bool
167+
LoadExternalSpecializations(const Decl *D,
168+
ArrayRef<TemplateArgument> TemplateArgs);
169+
155170
/// Ensures that the table of all visible declarations inside this
156171
/// context is up to date.
157172
///

‎clang/include/clang/Sema/MultiplexExternalSemaSource.h

+6
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ class MultiplexExternalSemaSource : public ExternalSemaSource {
9797
bool FindExternalVisibleDeclsByName(const DeclContext *DC,
9898
DeclarationName Name) override;
9999

100+
bool LoadExternalSpecializations(const Decl *D, bool OnlyPartial) override;
101+
102+
bool
103+
LoadExternalSpecializations(const Decl *D,
104+
ArrayRef<TemplateArgument> TemplateArgs) override;
105+
100106
/// Ensures that the table of all visible declarations inside this
101107
/// context is up to date.
102108
void completeVisibleDeclsMap(const DeclContext *DC) override;

‎clang/include/clang/Serialization/ASTBitCodes.h

+13
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,13 @@ enum ASTRecordTypes {
733733
/// Record code for Sema's vector of functions/blocks with effects to
734734
/// be verified.
735735
DECLS_WITH_EFFECTS_TO_VERIFY = 72,
736+
737+
/// Record code for updated specialization
738+
UPDATE_SPECIALIZATION = 73,
739+
740+
CXX_ADDED_TEMPLATE_SPECIALIZATION = 74,
741+
742+
CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION = 75,
736743
};
737744

738745
/// Record types used within a source manager block.
@@ -1502,6 +1509,12 @@ enum DeclCode {
15021509
/// An ImplicitConceptSpecializationDecl record.
15031510
DECL_IMPLICIT_CONCEPT_SPECIALIZATION,
15041511

1512+
// A decls specilization record.
1513+
DECL_SPECIALIZATIONS,
1514+
1515+
// A decls specilization record.
1516+
DECL_PARTIAL_SPECIALIZATIONS,
1517+
15051518
DECL_LAST = DECL_IMPLICIT_CONCEPT_SPECIALIZATION
15061519
};
15071520

‎clang/include/clang/Serialization/ASTReader.h

+45-3
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,9 @@ class ASTIdentifierLookupTrait;
354354
/// The on-disk hash table(s) used for DeclContext name lookup.
355355
struct DeclContextLookupTable;
356356

357+
/// The on-disk hash table(s) used for specialization decls.
358+
struct LazySpecializationInfoLookupTable;
359+
357360
} // namespace reader
358361

359362
} // namespace serialization
@@ -632,20 +635,40 @@ class ASTReader
632635
llvm::DenseMap<const DeclContext *,
633636
serialization::reader::DeclContextLookupTable> Lookups;
634637

638+
using SpecLookupTableTy =
639+
llvm::DenseMap<const Decl *,
640+
serialization::reader::LazySpecializationInfoLookupTable>;
641+
/// Map from decls to specialized decls.
642+
SpecLookupTableTy SpecializationsLookups;
643+
/// Split partial specialization from specialization to speed up lookups.
644+
SpecLookupTableTy PartialSpecializationsLookups;
645+
646+
bool LoadExternalSpecializationsImpl(SpecLookupTableTy &SpecLookups,
647+
const Decl *D);
648+
bool LoadExternalSpecializationsImpl(SpecLookupTableTy &SpecLookups,
649+
const Decl *D,
650+
ArrayRef<TemplateArgument> TemplateArgs);
651+
635652
// Updates for visible decls can occur for other contexts than just the
636653
// TU, and when we read those update records, the actual context may not
637654
// be available yet, so have this pending map using the ID as a key. It
638-
// will be realized when the context is actually loaded.
639-
struct PendingVisibleUpdate {
655+
// will be realized when the data is actually loaded.
656+
struct UpdateData {
640657
ModuleFile *Mod;
641658
const unsigned char *Data;
642659
};
643-
using DeclContextVisibleUpdates = SmallVector<PendingVisibleUpdate, 1>;
660+
using DeclContextVisibleUpdates = SmallVector<UpdateData, 1>;
644661

645662
/// Updates to the visible declarations of declaration contexts that
646663
/// haven't been loaded yet.
647664
llvm::DenseMap<GlobalDeclID, DeclContextVisibleUpdates> PendingVisibleUpdates;
648665

666+
using SpecializationsUpdate = SmallVector<UpdateData, 1>;
667+
using SpecializationsUpdateMap =
668+
llvm::DenseMap<GlobalDeclID, SpecializationsUpdate>;
669+
SpecializationsUpdateMap PendingSpecializationsUpdates;
670+
SpecializationsUpdateMap PendingPartialSpecializationsUpdates;
671+
649672
/// The set of C++ or Objective-C classes that have forward
650673
/// declarations that have not yet been linked to their definitions.
651674
llvm::SmallPtrSet<Decl *, 4> PendingDefinitions;
@@ -678,6 +701,11 @@ class ASTReader
678701
llvm::BitstreamCursor &Cursor,
679702
uint64_t Offset, GlobalDeclID ID);
680703

704+
bool ReadSpecializations(ModuleFile &M, llvm::BitstreamCursor &Cursor,
705+
uint64_t Offset, Decl *D, bool IsPartial);
706+
void AddSpecializations(const Decl *D, const unsigned char *Data,
707+
ModuleFile &M, bool IsPartial);
708+
681709
/// A vector containing identifiers that have already been
682710
/// loaded.
683711
///
@@ -1419,6 +1447,14 @@ class ASTReader
14191447
const serialization::reader::DeclContextLookupTable *
14201448
getLoadedLookupTables(DeclContext *Primary) const;
14211449

1450+
/// Get the loaded specializations lookup tables for \p D,
1451+
/// if any.
1452+
serialization::reader::LazySpecializationInfoLookupTable *
1453+
getLoadedSpecializationsLookupTables(const Decl *D, bool IsPartial);
1454+
1455+
/// If we have any unloaded specialization for \p D
1456+
bool haveUnloadedSpecializations(const Decl *D) const;
1457+
14221458
private:
14231459
struct ImportedModule {
14241460
ModuleFile *Mod;
@@ -2076,6 +2112,12 @@ class ASTReader
20762112
unsigned BlockID,
20772113
uint64_t *StartOfBlockOffset = nullptr);
20782114

2115+
bool LoadExternalSpecializations(const Decl *D, bool OnlyPartial) override;
2116+
2117+
bool
2118+
LoadExternalSpecializations(const Decl *D,
2119+
ArrayRef<TemplateArgument> TemplateArgs) override;
2120+
20792121
/// Finds all the visible declarations with a given name.
20802122
/// The current implementation of this method just loads the entire
20812123
/// lookup table as unmaterialized references.

‎clang/include/clang/Serialization/ASTWriter.h

+17
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,13 @@ class ASTWriter : public ASTDeserializationListener,
423423
/// Only meaningful for reduced BMI.
424424
DeclUpdateMap DeclUpdatesFromGMF;
425425

426+
/// Mapping from decl templates and its new specialization in the
427+
/// current TU.
428+
using SpecializationUpdateMap =
429+
llvm::MapVector<const NamedDecl *, SmallVector<const Decl *>>;
430+
SpecializationUpdateMap SpecializationsUpdates;
431+
SpecializationUpdateMap PartialSpecializationsUpdates;
432+
426433
using FirstLatestDeclMap = llvm::DenseMap<Decl *, Decl *>;
427434

428435
/// Map of first declarations from a chained PCH that point to the
@@ -575,6 +582,12 @@ class ASTWriter : public ASTDeserializationListener,
575582

576583
bool isLookupResultExternal(StoredDeclsList &Result, DeclContext *DC);
577584

585+
void GenerateSpecializationInfoLookupTable(
586+
const NamedDecl *D, llvm::SmallVectorImpl<const Decl *> &Specializations,
587+
llvm::SmallVectorImpl<char> &LookupTable, bool IsPartial);
588+
uint64_t WriteSpecializationInfoLookupTable(
589+
const NamedDecl *D, llvm::SmallVectorImpl<const Decl *> &Specializations,
590+
bool IsPartial);
578591
void GenerateNameLookupTable(ASTContext &Context, const DeclContext *DC,
579592
llvm::SmallVectorImpl<char> &LookupTable);
580593
uint64_t WriteDeclContextLexicalBlock(ASTContext &Context,
@@ -590,6 +603,7 @@ class ASTWriter : public ASTDeserializationListener,
590603
void WriteDeclAndTypes(ASTContext &Context);
591604
void PrepareWritingSpecialDecls(Sema &SemaRef);
592605
void WriteSpecialDeclRecords(Sema &SemaRef);
606+
void WriteSpecializationsUpdates(bool IsPartial);
593607
void WriteDeclUpdatesBlocks(ASTContext &Context,
594608
RecordDataImpl &OffsetsRecord);
595609
void WriteDeclContextVisibleUpdate(ASTContext &Context,
@@ -619,6 +633,9 @@ class ASTWriter : public ASTDeserializationListener,
619633
unsigned DeclEnumAbbrev = 0;
620634
unsigned DeclObjCIvarAbbrev = 0;
621635
unsigned DeclCXXMethodAbbrev = 0;
636+
unsigned DeclSpecializationsAbbrev = 0;
637+
unsigned DeclPartialSpecializationsAbbrev = 0;
638+
622639
unsigned DeclDependentNonTemplateCXXMethodAbbrev = 0;
623640
unsigned DeclTemplateCXXMethodAbbrev = 0;
624641
unsigned DeclMemberSpecializedCXXMethodAbbrev = 0;

0 commit comments

Comments
 (0)
Please sign in to comment.