From: Richard Smith Date: Fri, 30 Aug 2013 04:46:40 +0000 (+0000) Subject: Don't eagerly load all conversion operators when loading a class declaration X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c2d775714f79af977672e4f1dbc16ee9e02d1dea;p=clang Don't eagerly load all conversion operators when loading a class declaration from a PCH/module. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@189646 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTUnresolvedSet.h b/include/clang/AST/ASTUnresolvedSet.h index f9f4306bfa..e8be67006c 100644 --- a/include/clang/AST/ASTUnresolvedSet.h +++ b/include/clang/AST/ASTUnresolvedSet.h @@ -22,12 +22,21 @@ namespace clang { /// \brief An UnresolvedSet-like class which uses the ASTContext's allocator. class ASTUnresolvedSet { - typedef ASTVector DeclsTy; + struct DeclsTy : ASTVector { + DeclsTy() {} + DeclsTy(ASTContext &C, unsigned N) : ASTVector(C, N) {} + + bool isLazy() const { return getTag(); } + void setLazy(bool Lazy) { setTag(Lazy); } + }; + DeclsTy Decls; ASTUnresolvedSet(const ASTUnresolvedSet &) LLVM_DELETED_FUNCTION; void operator=(const ASTUnresolvedSet &) LLVM_DELETED_FUNCTION; + friend class LazyASTUnresolvedSet; + public: ASTUnresolvedSet() {} ASTUnresolvedSet(ASTContext &C, unsigned N) : Decls(C, N) {} @@ -48,7 +57,7 @@ public: /// Replaces the given declaration with the new one, once. /// /// \return true if the set changed - bool replace(const NamedDecl* Old, NamedDecl *New, AccessSpecifier AS) { + bool replace(const NamedDecl *Old, NamedDecl *New, AccessSpecifier AS) { for (DeclsTy::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) { if (I->getDecl() == Old) { I->set(New, AS); @@ -76,7 +85,29 @@ public: DeclAccessPair &operator[](unsigned I) { return Decls[I]; } const DeclAccessPair &operator[](unsigned I) const { return Decls[I]; } }; - + +/// \brief An UnresolvedSet-like class that might not have been loaded from the +/// external AST source yet. +class LazyASTUnresolvedSet { + mutable ASTUnresolvedSet Impl; + + void getFromExternalSource(ASTContext &C) const; + +public: + ASTUnresolvedSet &get(ASTContext &C) const { + if (Impl.Decls.isLazy()) + getFromExternalSource(C); + return Impl; + } + + void reserve(ASTContext &C, unsigned N) { Impl.reserve(C, N); } + void addLazyDecl(ASTContext &C, uintptr_t ID, AccessSpecifier AS) { + assert(Impl.empty() || Impl.Decls.isLazy()); + Impl.Decls.setLazy(true); + Impl.addDecl(C, reinterpret_cast(ID << 2), AS); + } +}; + } // namespace clang #endif diff --git a/include/clang/AST/ASTVector.h b/include/clang/AST/ASTVector.h index 71a687869a..6db918eaa6 100644 --- a/include/clang/AST/ASTVector.h +++ b/include/clang/AST/ASTVector.h @@ -55,16 +55,24 @@ namespace clang { template class ASTVector { - T *Begin, *End, *Capacity; +private: + T *Begin, *End; + llvm::PointerIntPair Capacity; void setEnd(T *P) { this->End = P; } +protected: + // Make a tag bit available to users of this class. + // FIXME: This is a horrible hack. + bool getTag() const { return Capacity.getInt(); } + void setTag(bool B) { Capacity.setInt(B); } + public: // Default ctor - Initialize to empty. - ASTVector() : Begin(NULL), End(NULL), Capacity(NULL) { } + ASTVector() : Begin(0), End(0), Capacity(0, false) {} ASTVector(const ASTContext &C, unsigned N) - : Begin(NULL), End(NULL), Capacity(NULL) { + : Begin(0), End(0), Capacity(0, false) { reserve(C, N); } @@ -156,7 +164,7 @@ public: } void push_back(const_reference Elt, const ASTContext &C) { - if (End < Capacity) { + if (End < this->capacity_ptr()) { Retry: new (End) T(Elt); ++End; @@ -167,13 +175,13 @@ public: } void reserve(const ASTContext &C, unsigned N) { - if (unsigned(Capacity-Begin) < N) + if (unsigned(this->capacity_ptr()-Begin) < N) grow(C, N); } /// capacity - Return the total number of elements in the currently allocated /// buffer. - size_t capacity() const { return Capacity - Begin; } + size_t capacity() const { return this->capacity_ptr() - Begin; } /// append - Add the specified range to the end of the SmallVector. /// @@ -220,7 +228,7 @@ public: return this->end()-1; } - if (this->End < this->Capacity) { + if (this->End < this->capacity_ptr()) { Retry: new (this->end()) T(this->back()); this->setEnd(this->end()+1); @@ -365,13 +373,16 @@ private: } protected: - iterator capacity_ptr() { return (iterator)this->Capacity; } + const_iterator capacity_ptr() const { + return (iterator) Capacity.getPointer(); + } + iterator capacity_ptr() { return (iterator)Capacity.getPointer(); } }; // Define this out-of-line to dissuade the C++ compiler from inlining it. template void ASTVector::grow(const ASTContext &C, size_t MinSize) { - size_t CurCapacity = Capacity-Begin; + size_t CurCapacity = this->capacity(); size_t CurSize = size(); size_t NewCapacity = 2*CurCapacity; if (NewCapacity < MinSize) @@ -394,7 +405,7 @@ void ASTVector::grow(const ASTContext &C, size_t MinSize) { // ASTContext never frees any memory. Begin = NewElts; End = NewElts+CurSize; - Capacity = Begin+NewCapacity; + Capacity.setPointer(Begin+NewCapacity); } } // end: clang namespace diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index f96032c61b..f8f2e41fe6 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -473,14 +473,14 @@ class CXXRecordDecl : public RecordDecl { /// inherited conversion functions). /// /// Each of the entries in this overload set is a CXXConversionDecl. - ASTUnresolvedSet Conversions; + LazyASTUnresolvedSet Conversions; /// \brief The conversion functions of this C++ class and all those /// inherited conversion functions that are visible in this class. /// /// Each of the entries in this overload set is a CXXConversionDecl or a /// FunctionTemplateDecl. - ASTUnresolvedSet VisibleConversions; + LazyASTUnresolvedSet VisibleConversions; /// \brief The declaration which defines this record. CXXRecordDecl *Definition; @@ -1014,10 +1014,10 @@ public: typedef UnresolvedSetIterator conversion_iterator; conversion_iterator conversion_begin() const { - return data().Conversions.begin(); + return data().Conversions.get(getASTContext()).begin(); } conversion_iterator conversion_end() const { - return data().Conversions.end(); + return data().Conversions.get(getASTContext()).end(); } /// Removes a conversion function from this class. The conversion diff --git a/include/clang/AST/UnresolvedSet.h b/include/clang/AST/UnresolvedSet.h index c33474906a..759af2537f 100644 --- a/include/clang/AST/UnresolvedSet.h +++ b/include/clang/AST/UnresolvedSet.h @@ -51,6 +51,7 @@ public: typedef std::iterator_traits::iterator_category iterator_category; NamedDecl *getDecl() const { return ir->getDecl(); } + void setDecl(NamedDecl *ND) const { return ir->setDecl(ND); } AccessSpecifier getAccess() const { return ir->getAccess(); } void setAccess(AccessSpecifier AS) { ir->setAccess(AS); } DeclAccessPair getPair() const { return *ir; } diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index ef4d8860b1..0354e824dd 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -88,7 +88,7 @@ class TypeLocReader; struct HeaderFileInfo; class VersionTuple; class TargetOptions; -class ASTUnresolvedSet; +class LazyASTUnresolvedSet; /// \brief Abstract interface for callback invocations by the ASTReader. /// @@ -1745,7 +1745,7 @@ public: unsigned &Idx); /// \brief Read a UnresolvedSet structure. - void ReadUnresolvedSet(ModuleFile &F, ASTUnresolvedSet &Set, + void ReadUnresolvedSet(ModuleFile &F, LazyASTUnresolvedSet &Set, const RecordData &Record, unsigned &Idx); /// \brief Read a C++ base specifier. diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index ccd8220a68..4d963cf4b3 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -35,6 +35,17 @@ AccessSpecDecl *AccessSpecDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (Mem) AccessSpecDecl(EmptyShell()); } +void LazyASTUnresolvedSet::getFromExternalSource(ASTContext &C) const { + ExternalASTSource *Source = C.getExternalSource(); + assert(Impl.Decls.isLazy() && "getFromExternalSource for non-lazy set"); + assert(Source && "getFromExternalSource with no external source"); + + for (ASTUnresolvedSet::iterator I = Impl.begin(); I != Impl.end(); ++I) + I.setDecl(cast(Source->GetExternalDecl( + reinterpret_cast(I.getDecl()) >> 2))); + Impl.Decls.setLazy(false); +} + CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) : UserDeclaredConstructor(false), UserDeclaredSpecialMembers(0), Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false), @@ -552,18 +563,16 @@ void CXXRecordDecl::addedMember(Decl *D) { if (Conversion->getPrimaryTemplate()) { // We don't record specializations. - } else if (FunTmpl) { - if (FunTmpl->getPreviousDecl()) - data().Conversions.replace(FunTmpl->getPreviousDecl(), - FunTmpl, AS); - else - data().Conversions.addDecl(getASTContext(), FunTmpl, AS); } else { - if (Conversion->getPreviousDecl()) - data().Conversions.replace(Conversion->getPreviousDecl(), - Conversion, AS); + ASTContext &Ctx = getASTContext(); + ASTUnresolvedSet &Conversions = data().Conversions.get(Ctx); + NamedDecl *Primary = + FunTmpl ? cast(FunTmpl) : cast(Conversion); + if (Primary->getPreviousDecl()) + Conversions.replace(cast(Primary->getPreviousDecl()), + Primary, AS); else - data().Conversions.addDecl(getASTContext(), Conversion, AS); + Conversions.addDecl(Ctx, Primary, AS); } } @@ -880,10 +889,13 @@ void CXXRecordDecl::addedMember(Decl *D) { } // Handle using declarations of conversion functions. - if (UsingShadowDecl *Shadow = dyn_cast(D)) + if (UsingShadowDecl *Shadow = dyn_cast(D)) { if (Shadow->getDeclName().getNameKind() - == DeclarationName::CXXConversionFunctionName) - data().Conversions.addDecl(getASTContext(), Shadow, Shadow->getAccess()); + == DeclarationName::CXXConversionFunctionName) { + ASTContext &Ctx = getASTContext(); + data().Conversions.get(Ctx).addDecl(Ctx, Shadow, Shadow->getAccess()); + } + } } void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) { @@ -1083,16 +1095,21 @@ static void CollectVisibleConversions(ASTContext &Context, /// in current class; including conversion function templates. std::pair CXXRecordDecl::getVisibleConversionFunctions() { - // If root class, all conversions are visible. - if (bases_begin() == bases_end()) - return std::make_pair(data().Conversions.begin(), data().Conversions.end()); - // If visible conversion list is already evaluated, return it. - if (!data().ComputedVisibleConversions) { - CollectVisibleConversions(getASTContext(), this, data().VisibleConversions); - data().ComputedVisibleConversions = true; + ASTContext &Ctx = getASTContext(); + + ASTUnresolvedSet *Set; + if (bases_begin() == bases_end()) { + // If root class, all conversions are visible. + Set = &data().Conversions.get(Ctx); + } else { + Set = &data().VisibleConversions.get(Ctx); + // If visible conversion list is not evaluated, evaluate it. + if (!data().ComputedVisibleConversions) { + CollectVisibleConversions(Ctx, this, *Set); + data().ComputedVisibleConversions = true; + } } - return std::make_pair(data().VisibleConversions.begin(), - data().VisibleConversions.end()); + return std::make_pair(Set->begin(), Set->end()); } void CXXRecordDecl::removeConversion(const NamedDecl *ConvDecl) { @@ -1107,7 +1124,7 @@ void CXXRecordDecl::removeConversion(const NamedDecl *ConvDecl) { // with sufficiently large numbers of directly-declared conversions // that asymptotic behavior matters. - ASTUnresolvedSet &Convs = data().Conversions; + ASTUnresolvedSet &Convs = data().Conversions.get(getASTContext()); for (unsigned I = 0, E = Convs.size(); I != E; ++I) { if (Convs[I].getDecl() == ConvDecl) { Convs.erase(I); @@ -1233,8 +1250,7 @@ void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) { } // Set access bits correctly on the directly-declared conversions. - for (UnresolvedSetIterator I = data().Conversions.begin(), - E = data().Conversions.end(); + for (conversion_iterator I = conversion_begin(), E = conversion_end(); I != E; ++I) I.setAccess((*I)->getAccess()); } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 6a2db82c43..5b55390730 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -6972,14 +6972,14 @@ ReadTemplateArgumentList(SmallVectorImpl &TemplArgs, } /// \brief Read a UnresolvedSet structure. -void ASTReader::ReadUnresolvedSet(ModuleFile &F, ASTUnresolvedSet &Set, +void ASTReader::ReadUnresolvedSet(ModuleFile &F, LazyASTUnresolvedSet &Set, const RecordData &Record, unsigned &Idx) { unsigned NumDecls = Record[Idx++]; Set.reserve(Context, NumDecls); while (NumDecls--) { - NamedDecl *D = ReadDeclAs(F, Record, Idx); + DeclID ID = ReadDeclID(F, Record, Idx); AccessSpecifier AS = (AccessSpecifier)Record[Idx++]; - Set.addDecl(Context, D, AS); + Set.addLazyDecl(Context, ID, AS); } } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 7f1bd9b882..f8e8b5d7ad 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -5117,8 +5117,8 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec AddCXXBaseSpecifiersRef(Data.getVBases(), Data.getVBases() + Data.NumVBases, Record); - AddUnresolvedSet(Data.Conversions, Record); - AddUnresolvedSet(Data.VisibleConversions, Record); + AddUnresolvedSet(Data.Conversions.get(*Context), Record); + AddUnresolvedSet(Data.VisibleConversions.get(*Context), Record); // Data.Definition is the owning decl, no need to write it. AddDeclRef(D->getFirstFriend(), Record); diff --git a/test/PCH/check-deserializations.cpp b/test/PCH/check-deserializations.cpp index 66eb5b480b..e4dafb7f54 100644 --- a/test/PCH/check-deserializations.cpp +++ b/test/PCH/check-deserializations.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -emit-pch -o %t.1 %s -// RUN: %clang_cc1 -error-on-deserialized-decl S1_keyfunc -include-pch %t.1 -emit-pch -o %t.2 %s -// RUN: %clang_cc1 -error-on-deserialized-decl S1_method -include-pch %t.2 -emit-llvm-only %s +// RUN: %clang_cc1 -error-on-deserialized-decl S1_keyfunc -error-on-deserialized-decl S3 -include-pch %t.1 -emit-pch -o %t.2 %s +// RUN: %clang_cc1 -error-on-deserialized-decl S1_method -error-on-deserialized-decl S3 -include-pch %t.2 -emit-llvm-only %s #ifndef HEADER1 #define HEADER1 @@ -11,17 +11,24 @@ struct S1 { virtual void S1_keyfunc(); }; +struct S3 {}; + +struct S2 { + operator S3(); +}; + #elif !defined(HEADER2) #define HEADER2 // Chained PCH. -S1 *p; +S1 *s1; +S2 *s2; #else // Using the headers. -void test(S1*) { +void test(S1*, S2*) { } #endif