From: Argyrios Kyrtzidis Date: Thu, 14 Oct 2010 20:14:34 +0000 (+0000) Subject: Allow deserialization of just the fields of a record, when we want to iterate over... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=eb5e9986e577b1e2bff3cca5973a2494fb593fbb;p=clang Allow deserialization of just the fields of a record, when we want to iterate over them, instead of deserializing the complete declaration context of the record. Iterating over the fields of a record is very common (e.g to determine the layout), unfortunately we needlessly deserialize every declaration that the declaration context of the record contains; this can be bad for large C++ classes that contain a lot of methods. Fix this by allow deserialization of just the fields when we want to iterate over them. Progress for rdar://7260160. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@116507 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 1e7b0cd0d4..43e82c91a6 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -2215,6 +2215,13 @@ class RecordDecl : public TagDecl { /// containing an object. bool HasObjectMember : 1; + /// \brief Whether the field declarations of this record have been loaded + /// from external storage. To avoid unnecessary deserialization of + /// methods/nested types we allow deserialization of just the fields + /// when needed. + mutable bool LoadedFieldsFromExternalStorage : 1; + friend void DeclContext::LoadLexicalDeclsFromExternalStorage() const; + protected: RecordDecl(Kind DK, TagKind TK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id, @@ -2293,11 +2300,10 @@ public: // data members, functions, constructors, destructors, etc. typedef specific_decl_iterator field_iterator; - field_iterator field_begin() const { - return field_iterator(decls_begin()); - } + field_iterator field_begin() const; + field_iterator field_end() const { - return field_iterator(decls_end()); + return field_iterator(decl_iterator()); } // field_empty - Whether there are any fields (non-static data @@ -2315,6 +2321,10 @@ public: static bool classofKind(Kind K) { return K >= firstRecord && K <= lastRecord; } + +private: + /// \brief Deserialize just the fields. + void LoadFieldsFromExternalStorage() const; }; class FileScopeAsmDecl : public Decl { diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index 1369c2b595..b45bfa89e8 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -698,6 +698,7 @@ class DeclContext { /// dependent context). mutable StoredDeclsMap *LookupPtr; +protected: /// FirstDecl - The first declaration stored within this declaration /// context. mutable Decl *FirstDecl; @@ -710,7 +711,12 @@ class DeclContext { friend class ExternalASTSource; -protected: + /// \brief Build up a chain of declarations. + /// + /// \returns the first/last pair of declarations. + static std::pair + BuildDeclChain(const llvm::SmallVectorImpl &Decls); + DeclContext(Decl::Kind K) : DeclKind(K), ExternalLexicalStorage(false), ExternalVisibleStorage(false), LookupPtr(0), FirstDecl(0), diff --git a/include/clang/AST/ExternalASTSource.h b/include/clang/AST/ExternalASTSource.h index a8ef0053a4..b35096e072 100644 --- a/include/clang/AST/ExternalASTSource.h +++ b/include/clang/AST/ExternalASTSource.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_AST_EXTERNAL_AST_SOURCE_H #define LLVM_CLANG_AST_EXTERNAL_AST_SOURCE_H +#include "clang/AST/DeclBase.h" #include #include @@ -110,11 +111,31 @@ public: virtual void MaterializeVisibleDecls(const DeclContext *DC) = 0; /// \brief Finds all declarations lexically contained within the given - /// DeclContext. + /// DeclContext, after applying an optional filter predicate. + /// + /// \param isKindWeWant a predicate function that returns true if the passed + /// declaration kind is one we are looking for. If NULL, all declarations + /// are returned. /// /// \return true if an error occurred virtual bool FindExternalLexicalDecls(const DeclContext *DC, - llvm::SmallVectorImpl &Result) = 0; + bool (*isKindWeWant)(Decl::Kind), + llvm::SmallVectorImpl &Result) = 0; + + /// \brief Finds all declarations lexically contained within the given + /// DeclContext. + /// + /// \return true if an error occurred + bool FindExternalLexicalDecls(const DeclContext *DC, + llvm::SmallVectorImpl &Result) { + return FindExternalLexicalDecls(DC, 0, Result); + } + + template + bool FindExternalLexicalDeclsBy(const DeclContext *DC, + llvm::SmallVectorImpl &Result) { + return FindExternalLexicalDecls(DC, DeclTy::classofKind, Result); + } /// \brief Notify ExternalASTSource that we started deserialization of /// a decl or type so until FinishedDeserializing is called there may be diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 6e9658bfff..a59b867159 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -54,6 +54,9 @@ namespace clang { /// reserved for the translation unit declaration. typedef uint32_t DeclID; + /// \brief a Decl::Kind/DeclID pair. + typedef std::pair KindDeclIDPair; + /// \brief An ID number that refers to a type in an AST file. /// /// The ID of a type is partitioned into two parts: the lower diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index c5dd186ceb..f275c76f0c 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -405,7 +405,7 @@ private: /// \brief Information about the contents of a DeclContext. struct DeclContextInfo { void *NameLookupTableData; // a ASTDeclContextNameLookupTable. - const serialization::DeclID *LexicalDecls; + const serialization::KindDeclIDPair *LexicalDecls; unsigned NumLexicalDecls; }; // In a full chain, there could be multiple updates to every decl context, @@ -928,6 +928,7 @@ public: /// \returns true if there was an error while reading the /// declarations for this declaration context. virtual bool FindExternalLexicalDecls(const DeclContext *DC, + bool (*isKindWeWant)(Decl::Kind), llvm::SmallVectorImpl &Decls); /// \brief Notify ASTReader that we started deserialization of diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index f455473059..e822009c58 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1651,6 +1651,7 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, DeclContext *DC, SourceLocation L, HasFlexibleArrayMember = false; AnonymousStructOrUnion = false; HasObjectMember = false; + LoadedFieldsFromExternalStorage = false; assert(classof(static_cast(this)) && "Invalid Kind!"); } @@ -1673,6 +1674,13 @@ bool RecordDecl::isInjectedClassName() const { cast(getDeclContext())->getDeclName() == getDeclName(); } +RecordDecl::field_iterator RecordDecl::field_begin() const { + if (hasExternalLexicalStorage() && !LoadedFieldsFromExternalStorage) + LoadFieldsFromExternalStorage(); + + return field_iterator(decl_iterator(FirstDecl)); +} + /// completeDefinition - Notes that the definition of this type is now /// complete. void RecordDecl::completeDefinition() { @@ -1691,6 +1699,31 @@ ValueDecl *RecordDecl::getAnonymousStructOrUnionObject() { return D; } +void RecordDecl::LoadFieldsFromExternalStorage() const { + ExternalASTSource *Source = getASTContext().getExternalSource(); + assert(hasExternalLexicalStorage() && Source && "No external storage?"); + + // Notify that we have a RecordDecl doing some initialization. + ExternalASTSource::Deserializing TheFields(Source); + + llvm::SmallVector Decls; + if (Source->FindExternalLexicalDeclsBy(this, Decls)) + return; + +#ifndef NDEBUG + // Check that all decls we got were FieldDecls. + for (unsigned i=0, e=Decls.size(); i != e; ++i) + assert(isa(Decls[i])); +#endif + + LoadedFieldsFromExternalStorage = true; + + if (Decls.empty()) + return; + + llvm::tie(FirstDecl, LastDecl) = BuildDeclChain(Decls); +} + //===----------------------------------------------------------------------===// // BlockDecl Implementation //===----------------------------------------------------------------------===// diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index ece9945390..d60a06749d 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -599,6 +599,24 @@ DeclContext *DeclContext::getNextContext() { } } +std::pair +DeclContext::BuildDeclChain(const llvm::SmallVectorImpl &Decls) { + // Build up a chain of declarations via the Decl::NextDeclInContext field. + Decl *FirstNewDecl = 0; + Decl *PrevDecl = 0; + for (unsigned I = 0, N = Decls.size(); I != N; ++I) { + Decl *D = Decls[I]; + if (PrevDecl) + PrevDecl->NextDeclInContext = D; + else + FirstNewDecl = D; + + PrevDecl = D; + } + + return std::make_pair(FirstNewDecl, PrevDecl); +} + /// \brief Load the declarations within this lexical storage from an /// external source. void @@ -619,26 +637,22 @@ DeclContext::LoadLexicalDeclsFromExternalStorage() const { if (Decls.empty()) return; - // Resolve all of the declaration IDs into declarations, building up - // a chain of declarations via the Decl::NextDeclInContext field. - Decl *FirstNewDecl = 0; - Decl *PrevDecl = 0; - for (unsigned I = 0, N = Decls.size(); I != N; ++I) { - Decl *D = Decls[I]; - if (PrevDecl) - PrevDecl->NextDeclInContext = D; - else - FirstNewDecl = D; - - PrevDecl = D; - } + // We may have already loaded just the fields of this record, in which case + // don't add the decls, just replace the FirstDecl/LastDecl chain. + if (const RecordDecl *RD = dyn_cast(this)) + if (RD->LoadedFieldsFromExternalStorage) { + llvm::tie(FirstDecl, LastDecl) = BuildDeclChain(Decls); + return; + } // Splice the newly-read declarations into the beginning of the list // of declarations. - PrevDecl->NextDeclInContext = FirstDecl; - FirstDecl = FirstNewDecl; + Decl *ExternalFirst, *ExternalLast; + llvm::tie(ExternalFirst, ExternalLast) = BuildDeclChain(Decls); + ExternalLast->NextDeclInContext = FirstDecl; + FirstDecl = ExternalFirst; if (!LastDecl) - LastDecl = PrevDecl; + LastDecl = ExternalLast; } DeclContext::lookup_result diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 9ee818367b..23f2eaac66 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -903,8 +903,8 @@ bool ASTReader::ReadDeclContextStorage(llvm::BitstreamCursor &Cursor, return true; } - Info.LexicalDecls = reinterpret_cast(Blob); - Info.NumLexicalDecls = BlobLen / sizeof(DeclID); + Info.LexicalDecls = reinterpret_cast(Blob); + Info.NumLexicalDecls = BlobLen / sizeof(KindDeclIDPair); } else { Info.LexicalDecls = 0; Info.NumLexicalDecls = 0; @@ -1778,8 +1778,8 @@ ASTReader::ReadASTBlock(PerFileData &F) { case TU_UPDATE_LEXICAL: { DeclContextInfo Info = { /* No visible information */ 0, - reinterpret_cast(BlobStart), - BlobLen / sizeof(DeclID) + reinterpret_cast(BlobStart), + BlobLen / sizeof(KindDeclIDPair) }; DeclContextOffsets[Context ? Context->getTranslationUnitDecl() : 0] .push_back(Info); @@ -3242,6 +3242,7 @@ Stmt *ASTReader::GetExternalDeclStmt(uint64_t Offset) { } bool ASTReader::FindExternalLexicalDecls(const DeclContext *DC, + bool (*isKindWeWant)(Decl::Kind), llvm::SmallVectorImpl &Decls) { assert(DC->hasExternalLexicalStorage() && "DeclContext has no lexical decls in storage"); @@ -3258,9 +3259,12 @@ bool ASTReader::FindExternalLexicalDecls(const DeclContext *DC, continue; // Load all of the declaration IDs - for (const DeclID *ID = I->LexicalDecls, *IDE = ID + I->NumLexicalDecls; - ID != IDE; ++ID) { - Decl *D = GetDecl(*ID); + for (const KindDeclIDPair *ID = I->LexicalDecls, + *IDE = ID + I->NumLexicalDecls; ID != IDE; ++ID) { + if (isKindWeWant && !isKindWeWant((Decl::Kind)ID->first)) + continue; + + Decl *D = GetDecl(ID->second); assert(D && "Null decl in lexical decls"); Decls.push_back(D); } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index eae47db44e..8dffe1a950 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -1474,14 +1474,15 @@ uint64_t ASTWriter::WriteDeclContextLexicalBlock(ASTContext &Context, uint64_t Offset = Stream.GetCurrentBitNo(); RecordData Record; Record.push_back(DECL_CONTEXT_LEXICAL); - llvm::SmallVector Decls; + llvm::SmallVector Decls; for (DeclContext::decl_iterator D = DC->decls_begin(), DEnd = DC->decls_end(); D != DEnd; ++D) - Decls.push_back(GetDeclRef(*D)); + Decls.push_back(std::make_pair((*D)->getKind(), GetDeclRef(*D))); ++NumLexicalDeclContexts; Stream.EmitRecordWithBlob(DeclContextLexicalAbbrev, Record, - reinterpret_cast(Decls.data()), Decls.size() * sizeof(DeclID)); + reinterpret_cast(Decls.data()), + Decls.size() * sizeof(KindDeclIDPair)); return Offset; } @@ -2471,12 +2472,12 @@ void ASTWriter::WriteASTChain(Sema &SemaRef, MemorizeStatCalls *StatCalls, // We don't start with the translation unit, but with its decls that // don't come from the chained PCH. const TranslationUnitDecl *TU = Context.getTranslationUnitDecl(); - llvm::SmallVector NewGlobalDecls; + llvm::SmallVector NewGlobalDecls; for (DeclContext::decl_iterator I = TU->noload_decls_begin(), E = TU->noload_decls_end(); I != E; ++I) { if ((*I)->getPCHLevel() == 0) - NewGlobalDecls.push_back(GetDeclRef(*I)); + NewGlobalDecls.push_back(std::make_pair((*I)->getKind(), GetDeclRef(*I))); else if ((*I)->isChangedSinceDeserialization()) (void)GetDeclRef(*I); // Make sure it's written, but don't record it. } @@ -2489,7 +2490,7 @@ void ASTWriter::WriteASTChain(Sema &SemaRef, MemorizeStatCalls *StatCalls, Record.push_back(TU_UPDATE_LEXICAL); Stream.EmitRecordWithBlob(TuUpdateLexicalAbbrev, Record, reinterpret_cast(NewGlobalDecls.data()), - NewGlobalDecls.size() * sizeof(DeclID)); + NewGlobalDecls.size() * sizeof(KindDeclIDPair)); // And in C++, a visible updates block for the TU. if (Context.getLangOptions().CPlusPlus) { Abv = new llvm::BitCodeAbbrev(); diff --git a/test/PCH/check-deserializations.cpp b/test/PCH/check-deserializations.cpp new file mode 100644 index 0000000000..ea0398470d --- /dev/null +++ b/test/PCH/check-deserializations.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -emit-pch -o %t %s +// RUN: %clang_cc1 -error-on-deserialized-decl S1_method -include-pch %t -emit-llvm-only %s + +#ifndef HEADER +#define HEADER +// Header. + +struct S1 { + void S1_method(); // This should not be deserialized. +}; + + +#else +// Using the header. + +void test(S1*) { +} + +#endif