Make the deserialization of C++ base class specifiers lazy, improving
the performance of C++ PCH and reducing stack depth in the reader.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@117732 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp
index 0e70053..88ede82 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -36,7 +36,7 @@
HasTrivialDestructor(true), ComputedVisibleConversions(false),
DeclaredDefaultConstructor(false), DeclaredCopyConstructor(false),
DeclaredCopyAssignment(false), DeclaredDestructor(false),
- Bases(0), NumBases(0), VBases(0), NumVBases(0),
+ NumBases(0), NumVBases(0), Bases(), VBases(),
Definition(D), FirstFriend(0) {
}
@@ -77,8 +77,8 @@
// no base classes [...].
data().Aggregate = false;
- if (data().Bases)
- C.Deallocate(data().Bases);
+ if (!data().Bases.isOffset() && data().NumBases > 0)
+ C.Deallocate(data().getBases());
// The set of seen virtual base types.
llvm::SmallPtrSet<CanQualType, 8> SeenVBaseTypes;
@@ -89,7 +89,7 @@
data().Bases = new(C) CXXBaseSpecifier [NumBases];
data().NumBases = NumBases;
for (unsigned i = 0; i < NumBases; ++i) {
- data().Bases[i] = *Bases[i];
+ data().getBases()[i] = *Bases[i];
// Keep track of inherited vbases for this base class.
const CXXBaseSpecifier *Base = Bases[i];
QualType BaseType = Base->getType();
@@ -193,7 +193,7 @@
CXXRecordDecl *VBaseClassDecl = cast<CXXRecordDecl>(
VBaseTypeInfo->getType()->getAs<RecordType>()->getDecl());
- data().VBases[I] =
+ data().getVBases()[I] =
CXXBaseSpecifier(VBaseClassDecl->getSourceRange(), true,
VBaseClassDecl->getTagKind() == TTK_Class,
VBases[I]->getAccessSpecifier(), VBaseTypeInfo);
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index 6497b78..aa76765 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -2069,6 +2069,17 @@
std::make_pair(&F, Record[I+1]);
break;
}
+
+ case CXX_BASE_SPECIFIER_OFFSETS: {
+ if (F.LocalNumCXXBaseSpecifiers != 0) {
+ Error("duplicate CXX_BASE_SPECIFIER_OFFSETS record in AST file");
+ return Failure;
+ }
+
+ F.LocalNumCXXBaseSpecifiers = Record[0];
+ F.CXXBaseSpecifiersOffsets = (const uint32_t *)BlobStart;
+ break;
+ }
}
First = false;
}
@@ -3207,6 +3218,14 @@
return I->second;
}
+unsigned ASTReader::getTotalNumCXXBaseSpecifiers() const {
+ unsigned Result = 0;
+ for (unsigned I = 0, N = Chain.size(); I != N; ++I)
+ Result += Chain[I]->LocalNumCXXBaseSpecifiers;
+
+ return Result;
+}
+
TemplateArgumentLocInfo
ASTReader::GetTemplateArgumentLocInfo(PerFileData &F,
TemplateArgument::ArgKind Kind,
@@ -3249,6 +3268,63 @@
return GetDecl(ID);
}
+uint64_t
+ASTReader::GetCXXBaseSpecifiersOffset(serialization::CXXBaseSpecifiersID ID) {
+ if (ID == 0)
+ return 0;
+
+ --ID;
+ uint64_t Offset = 0;
+ for (unsigned I = 0, N = Chain.size(); I != N; ++I) {
+ if (ID < Chain[I]->LocalNumCXXBaseSpecifiers)
+ return Offset + Chain[I]->CXXBaseSpecifiersOffsets[ID];
+
+ ID -= Chain[I]->LocalNumCXXBaseSpecifiers;
+ Offset += Chain[I]->SizeInBits;
+ }
+
+ assert(false && "CXXBaseSpecifiers not found");
+ return 0;
+}
+
+CXXBaseSpecifier *ASTReader::GetExternalCXXBaseSpecifiers(uint64_t Offset) {
+ // Figure out which AST file contains this offset.
+ PerFileData *F = 0;
+ for (unsigned I = 0, N = Chain.size(); I != N; ++I) {
+ if (Offset < Chain[I]->SizeInBits) {
+ F = Chain[I];
+ break;
+ }
+
+ Offset -= Chain[I]->SizeInBits;
+ }
+
+ if (!F) {
+ Error("Malformed AST file: C++ base specifiers at impossible offset");
+ return 0;
+ }
+
+ llvm::BitstreamCursor &Cursor = F->DeclsCursor;
+ SavedStreamPosition SavedPosition(Cursor);
+ Cursor.JumpToBit(Offset);
+ ReadingKindTracker ReadingKind(Read_Decl, *this);
+ RecordData Record;
+ unsigned Code = Cursor.ReadCode();
+ unsigned RecCode = Cursor.ReadRecord(Code, Record);
+ if (RecCode != DECL_CXX_BASE_SPECIFIERS) {
+ Error("Malformed AST file: missing C++ base specifiers");
+ return 0;
+ }
+
+ unsigned Idx = 0;
+ unsigned NumBases = Record[Idx++];
+ void *Mem = Context->Allocate(sizeof(CXXBaseSpecifier) * NumBases);
+ CXXBaseSpecifier *Bases = new (Mem) CXXBaseSpecifier [NumBases];
+ for (unsigned I = 0; I != NumBases; ++I)
+ Bases[I] = ReadCXXBaseSpecifier(*F, Record, Idx);
+ return Bases;
+}
+
TranslationUnitDecl *ASTReader::GetTranslationUnitDecl() {
if (!DeclsLoaded[0]) {
ReadDeclRecord(0, 1);
@@ -4411,7 +4487,8 @@
IdentifierLookupTable(0), LocalNumMacroDefinitions(0),
MacroDefinitionOffsets(0), LocalNumSelectors(0), SelectorOffsets(0),
SelectorLookupTableData(0), SelectorLookupTable(0), LocalNumDecls(0),
- DeclOffsets(0), LocalNumTypes(0), TypeOffsets(0), StatCache(0),
+ DeclOffsets(0), LocalNumCXXBaseSpecifiers(0), CXXBaseSpecifiersOffsets(0),
+ LocalNumTypes(0), TypeOffsets(0), StatCache(0),
NumPreallocatedPreprocessingEntities(0), NextInSource(0)
{}
diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp
index d63dce9..c6cb8db 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -773,8 +773,6 @@
void ASTDeclReader::ReadCXXDefinitionData(
struct CXXRecordDecl::DefinitionData &Data,
const RecordData &Record, unsigned &Idx) {
- ASTContext &C = *Reader.getContext();
-
Data.UserDeclaredConstructor = Record[Idx++];
Data.UserDeclaredCopyConstructor = Record[Idx++];
Data.UserDeclaredCopyAssignment = Record[Idx++];
@@ -793,20 +791,13 @@
Data.DeclaredCopyConstructor = Record[Idx++];
Data.DeclaredCopyAssignment = Record[Idx++];
Data.DeclaredDestructor = Record[Idx++];
-
- // setBases() is unsuitable since it may try to iterate the bases of an
- // uninitialized base.
Data.NumBases = Record[Idx++];
- Data.Bases = new(C) CXXBaseSpecifier [Data.NumBases];
- for (unsigned i = 0; i != Data.NumBases; ++i)
- Data.Bases[i] = Reader.ReadCXXBaseSpecifier(F, Record, Idx);
-
- // FIXME: Make VBases lazily computed when needed to avoid storing them.
+ if (Data.NumBases)
+ Data.Bases = Reader.GetCXXBaseSpecifiersOffset(Record[Idx++]);
Data.NumVBases = Record[Idx++];
- Data.VBases = new(C) CXXBaseSpecifier [Data.NumVBases];
- for (unsigned i = 0; i != Data.NumVBases; ++i)
- Data.VBases[i] = Reader.ReadCXXBaseSpecifier(F, Record, Idx);
-
+ if (Data.NumVBases)
+ Data.VBases = Reader.GetCXXBaseSpecifiersOffset(Record[Idx++]);
+
Reader.ReadUnresolvedSet(Data.Conversions, Record, Idx);
Reader.ReadUnresolvedSet(Data.VisibleConversions, Record, Idx);
assert(Data.Definition && "Data.Definition should be already set!");
@@ -1508,6 +1499,9 @@
case DECL_BLOCK:
D = BlockDecl::Create(*Context, 0, SourceLocation());
break;
+ case DECL_CXX_BASE_SPECIFIERS:
+ Error("attempt to read a C++ base-specifier record as a declaration");
+ return 0;
}
assert(D && "Unknown declaration reading AST file");
diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp
index 114f920..1fec22b 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -1725,7 +1725,7 @@
// Create a blob abbreviation for the selector table offsets.
Abbrev = new BitCodeAbbrev();
Abbrev->Add(BitCodeAbbrevOp(SELECTOR_OFFSETS));
- Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // index
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // size
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
unsigned SelectorOffsetAbbrev = Stream.EmitAbbrev(Abbrev);
@@ -2230,7 +2230,9 @@
NextSelectorID(FirstSelectorID), FirstMacroID(1), NextMacroID(FirstMacroID),
CollectedStmts(&StmtsToEmit),
NumStatements(0), NumMacros(0), NumLexicalDeclContexts(0),
- NumVisibleDeclContexts(0) {
+ NumVisibleDeclContexts(0), FirstCXXBaseSpecifiersID(1),
+ NextCXXBaseSpecifiersID(1)
+{
}
void ASTWriter::WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls,
@@ -2401,6 +2403,26 @@
WriteTypeDeclOffsets();
+ // Write the C++ base-specifier set offsets.
+ if (!CXXBaseSpecifiersOffsets.empty()) {
+ // Create a blob abbreviation for the C++ base specifiers offsets.
+ using namespace llvm;
+
+ BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
+ Abbrev->Add(BitCodeAbbrevOp(CXX_BASE_SPECIFIER_OFFSETS));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // size
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
+ unsigned BaseSpecifierOffsetAbbrev = Stream.EmitAbbrev(Abbrev);
+
+ // Write the selector offsets table.
+ Record.clear();
+ Record.push_back(CXX_BASE_SPECIFIER_OFFSETS);
+ Record.push_back(CXXBaseSpecifiersOffsets.size());
+ Stream.EmitRecordWithBlob(BaseSpecifierOffsetAbbrev, Record,
+ (const char *)CXXBaseSpecifiersOffsets.data(),
+ CXXBaseSpecifiersOffsets.size() * sizeof(uint32_t));
+ }
+
// Write the record containing external, unnamed definitions.
if (!ExternalDefinitions.empty())
Stream.EmitRecord(EXTERNAL_DEFINITIONS, ExternalDefinitions);
@@ -2798,6 +2820,16 @@
AddDeclRef(Temp->getDestructor(), Record);
}
+void ASTWriter::AddCXXBaseSpecifiersRef(CXXBaseSpecifier const *Bases,
+ CXXBaseSpecifier const *BasesEnd,
+ RecordDataImpl &Record) {
+ assert(Bases != BasesEnd && "Empty base-specifier sets are not recorded");
+ CXXBaseSpecifiersToWrite.push_back(
+ QueuedCXXBaseSpecifiers(NextCXXBaseSpecifiersID,
+ Bases, BasesEnd));
+ Record.push_back(NextCXXBaseSpecifiersID++);
+}
+
void ASTWriter::AddTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind,
const TemplateArgumentLocInfo &Arg,
RecordDataImpl &Record) {
@@ -3155,6 +3187,32 @@
AddSourceRange(Base.getSourceRange(), Record);
}
+void ASTWriter::FlushCXXBaseSpecifiers() {
+ RecordData Record;
+ for (unsigned I = 0, N = CXXBaseSpecifiersToWrite.size(); I != N; ++I) {
+ Record.clear();
+
+ // Record the offset of this base-specifier set.
+ unsigned Index = CXXBaseSpecifiersToWrite[I].ID - FirstCXXBaseSpecifiersID;
+ if (Index == CXXBaseSpecifiersOffsets.size())
+ CXXBaseSpecifiersOffsets.push_back(Stream.GetCurrentBitNo());
+ else {
+ if (Index > CXXBaseSpecifiersOffsets.size())
+ CXXBaseSpecifiersOffsets.resize(Index + 1);
+ CXXBaseSpecifiersOffsets[Index] = Stream.GetCurrentBitNo();
+ }
+
+ const CXXBaseSpecifier *B = CXXBaseSpecifiersToWrite[I].Bases,
+ *BEnd = CXXBaseSpecifiersToWrite[I].BasesEnd;
+ Record.push_back(BEnd - B);
+ for (; B != BEnd; ++B)
+ AddCXXBaseSpecifier(*B, Record);
+ Stream.EmitRecord(serialization::DECL_CXX_BASE_SPECIFIERS, Record);
+ }
+
+ CXXBaseSpecifiersToWrite.clear();
+}
+
void ASTWriter::AddCXXBaseOrMemberInitializers(
const CXXBaseOrMemberInitializer * const *BaseOrMembers,
unsigned NumBaseOrMembers, RecordDataImpl &Record) {
@@ -3208,13 +3266,15 @@
Record.push_back(Data.DeclaredDestructor);
Record.push_back(Data.NumBases);
- for (unsigned i = 0; i != Data.NumBases; ++i)
- AddCXXBaseSpecifier(Data.Bases[i], Record);
-
+ if (Data.NumBases > 0)
+ AddCXXBaseSpecifiersRef(Data.getBases(), Data.getBases() + Data.NumBases,
+ Record);
+
// FIXME: Make VBases lazily computed when needed to avoid storing them.
Record.push_back(Data.NumVBases);
- for (unsigned i = 0; i != Data.NumVBases; ++i)
- AddCXXBaseSpecifier(Data.VBases[i], Record);
+ if (Data.NumVBases > 0)
+ AddCXXBaseSpecifiersRef(Data.getVBases(), Data.getVBases() + Data.NumVBases,
+ Record);
AddUnresolvedSet(Data.Conversions, Record);
AddUnresolvedSet(Data.VisibleConversions, Record);
@@ -3230,6 +3290,7 @@
FirstIdentID == NextIdentID &&
FirstSelectorID == NextSelectorID &&
FirstMacroID == NextMacroID &&
+ FirstCXXBaseSpecifiersID == NextCXXBaseSpecifiersID &&
"Setting chain after writing has started.");
Chain = Reader;
@@ -3238,11 +3299,13 @@
FirstIdentID += Chain->getTotalNumIdentifiers();
FirstSelectorID += Chain->getTotalNumSelectors();
FirstMacroID += Chain->getTotalNumMacroDefinitions();
+ FirstCXXBaseSpecifiersID += Chain->getTotalNumCXXBaseSpecifiers();
NextDeclID = FirstDeclID;
NextTypeID = FirstTypeID;
NextIdentID = FirstIdentID;
NextSelectorID = FirstSelectorID;
NextMacroID = FirstMacroID;
+ NextCXXBaseSpecifiersID = FirstCXXBaseSpecifiersID;
}
void ASTWriter::IdentifierRead(IdentID ID, IdentifierInfo *II) {
diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp
index 37c7765..7d80f31 100644
--- a/lib/Serialization/ASTWriterDecl.cpp
+++ b/lib/Serialization/ASTWriterDecl.cpp
@@ -1182,6 +1182,12 @@
// Flush any expressions that were written as part of this declaration.
FlushStmts();
+
+ // Flush C++ base specifiers, if there are any.
+ FlushCXXBaseSpecifiers();
+
+ // Flush any expressions that were written as part of the base specifiers.
+ FlushStmts();
// Note "external" declarations so that we can add them to a record in the
// AST file later.