Don't eagerly load all conversion operators when loading a class declaration

from a PCH/module.

llvm-svn: 189646
This commit is contained in:
Richard Smith 2013-08-30 04:46:40 +00:00
parent 8568e576b0
commit a4ba74c5f5
9 changed files with 119 additions and 53 deletions

View File

@ -22,12 +22,21 @@ namespace clang {
/// \brief An UnresolvedSet-like class which uses the ASTContext's allocator.
class ASTUnresolvedSet {
typedef ASTVector<DeclAccessPair> DeclsTy;
struct DeclsTy : ASTVector<DeclAccessPair> {
DeclsTy() {}
DeclsTy(ASTContext &C, unsigned N) : ASTVector<DeclAccessPair>(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<NamedDecl*>(ID << 2), AS);
}
};
} // namespace clang
#endif

View File

@ -55,16 +55,24 @@ namespace clang {
template<typename T>
class ASTVector {
T *Begin, *End, *Capacity;
private:
T *Begin, *End;
llvm::PointerIntPair<T*, 1, bool> 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 <typename T>
void ASTVector<T>::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<T>::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

View File

@ -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

View File

@ -51,6 +51,7 @@ public:
typedef std::iterator_traits<IteratorTy>::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; }

View File

@ -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.

View File

@ -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<NamedDecl>(Source->GetExternalDecl(
reinterpret_cast<uintptr_t>(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<NamedDecl>(FunTmpl) : cast<NamedDecl>(Conversion);
if (Primary->getPreviousDecl())
Conversions.replace(cast<NamedDecl>(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<UsingShadowDecl>(D))
if (UsingShadowDecl *Shadow = dyn_cast<UsingShadowDecl>(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::conversion_iterator,CXXRecordDecl::conversion_iterator>
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());
}

View File

@ -6972,14 +6972,14 @@ ReadTemplateArgumentList(SmallVectorImpl<TemplateArgument> &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<NamedDecl>(F, Record, Idx);
DeclID ID = ReadDeclID(F, Record, Idx);
AccessSpecifier AS = (AccessSpecifier)Record[Idx++];
Set.addDecl(Context, D, AS);
Set.addLazyDecl(Context, ID, AS);
}
}

View File

@ -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);

View File

@ -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