Be lazier when loading KeyFunctions from PCH/modules. We don't need to load

these in eagerly if we're not actually processing a translation unit. The added
laziness here also avoids us loading in parts of a CXXRecordDecl earlier than an
upcoming class template specialization merging patch would like.

Ideally, we should mark the vtable as used when we see a definition for the key
function, rather than having a separate pass over dynamic classes at the end of
the TU. The existing approach is pretty bad for PCH/modules, since it forcibly
loads the declarations of all key functions in all imported modules, whether or
not those key functions are defined.

llvm-svn: 189627
This commit is contained in:
Richard Smith 2013-08-29 23:59:27 +00:00
parent b81766ca23
commit 676c404dec
4 changed files with 28 additions and 22 deletions

View File

@ -148,7 +148,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable TypeInfoMap MemoizedTypeInfo; mutable TypeInfoMap MemoizedTypeInfo;
/// \brief A cache mapping from CXXRecordDecls to key functions. /// \brief A cache mapping from CXXRecordDecls to key functions.
llvm::DenseMap<const CXXRecordDecl*, const CXXMethodDecl*> KeyFunctions; llvm::DenseMap<const CXXRecordDecl*, LazyDeclPtr> KeyFunctions;
/// \brief Mapping from ObjCContainers to their ObjCImplementations. /// \brief Mapping from ObjCContainers to their ObjCImplementations.
llvm::DenseMap<ObjCContainerDecl*, ObjCImplDecl*> ObjCImpls; llvm::DenseMap<ObjCContainerDecl*, ObjCImplDecl*> ObjCImpls;

View File

@ -2438,32 +2438,31 @@ const CXXMethodDecl *ASTContext::getCurrentKeyFunction(const CXXRecordDecl *RD)
assert(RD->getDefinition() && "Cannot get key function for forward decl!"); assert(RD->getDefinition() && "Cannot get key function for forward decl!");
RD = cast<CXXRecordDecl>(RD->getDefinition()); RD = cast<CXXRecordDecl>(RD->getDefinition());
const CXXMethodDecl *&entry = KeyFunctions[RD]; LazyDeclPtr &Entry = KeyFunctions[RD];
if (!entry) { if (!Entry)
entry = computeKeyFunction(*this, RD); Entry = const_cast<CXXMethodDecl*>(computeKeyFunction(*this, RD));
}
return entry; return cast_or_null<CXXMethodDecl>(Entry.get(getExternalSource()));
} }
void ASTContext::setNonKeyFunction(const CXXMethodDecl *method) { void ASTContext::setNonKeyFunction(const CXXMethodDecl *Method) {
assert(method == method->getFirstDeclaration() && assert(Method == Method->getFirstDeclaration() &&
"not working with method declaration from class definition"); "not working with method declaration from class definition");
// Look up the cache entry. Since we're working with the first // Look up the cache entry. Since we're working with the first
// declaration, its parent must be the class definition, which is // declaration, its parent must be the class definition, which is
// the correct key for the KeyFunctions hash. // the correct key for the KeyFunctions hash.
llvm::DenseMap<const CXXRecordDecl*, const CXXMethodDecl*>::iterator llvm::DenseMap<const CXXRecordDecl*, LazyDeclPtr>::iterator
i = KeyFunctions.find(method->getParent()); I = KeyFunctions.find(Method->getParent());
// If it's not cached, there's nothing to do. // If it's not cached, there's nothing to do.
if (i == KeyFunctions.end()) return; if (I == KeyFunctions.end()) return;
// If it is cached, check whether it's the target method, and if so, // If it is cached, check whether it's the target method, and if so,
// remove it from the cache. // remove it from the cache.
if (i->second == method) { if (I->second.get(getExternalSource()) == Method) {
// FIXME: remember that we did this for module / chained PCH state? // FIXME: remember that we did this for module / chained PCH state?
KeyFunctions.erase(i); KeyFunctions.erase(I);
} }
} }

View File

@ -1276,11 +1276,11 @@ ASTDeclReader::VisitCXXRecordDeclImpl(CXXRecordDecl *D) {
} }
} }
// Load the key function to avoid deserializing every method so we can // Lazily load the key function to avoid deserializing every method so we can
// compute it. // compute it.
if (D->IsCompleteDefinition) { if (D->IsCompleteDefinition) {
if (CXXMethodDecl *Key = ReadDeclAs<CXXMethodDecl>(Record, Idx)) if (DeclID KeyFn = ReadDeclID(Record, Idx))
C.KeyFunctions[D] = Key; C.KeyFunctions[D] = KeyFn;
} }
return Redecl; return Redecl;

View File

@ -1,18 +1,25 @@
// RUN: %clang_cc1 -emit-pch -o %t %s // RUN: %clang_cc1 -emit-pch -o %t.1 %s
// RUN: %clang_cc1 -error-on-deserialized-decl S1_method -include-pch %t -emit-llvm-only %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
#ifndef HEADER #ifndef HEADER1
#define HEADER #define HEADER1
// Header. // Header.
struct S1 { struct S1 {
void S1_method(); // This should not be deserialized. void S1_method();
virtual void S1_keyfunc(); virtual void S1_keyfunc();
}; };
#elif !defined(HEADER2)
#define HEADER2
// Chained PCH.
S1 *p;
#else #else
// Using the header.
// Using the headers.
void test(S1*) { void test(S1*) {
} }