[modules] Improve performance when there is a local declaration of an entity

before the first imported declaration.

We don't need to track all formerly-canonical declarations of an entity; it's sufficient to track those ones for which no other formerly-canonical declaration was imported into the same module. We call those ones "key declarations", and use them as our starting points for collecting redeclarations and performing namespace lookups.

llvm-svn: 241999
This commit is contained in:
Richard Smith
2015-07-12 23:43:21 +00:00
parent a2275910a7
commit 5fc18a9a1f
6 changed files with 207 additions and 149 deletions

View File

@@ -254,6 +254,8 @@ void ASTDeclWriter::VisitDecl(Decl *D) {
//
// This happens when we instantiate a class with a friend declaration or a
// function with a local extern declaration, for instance.
//
// FIXME: Can we handle this in AddedVisibleDecl instead?
if (D->isOutOfLine()) {
auto *DC = D->getDeclContext();
while (auto *NS = dyn_cast<NamespaceDecl>(DC->getRedeclContext())) {
@@ -1005,42 +1007,6 @@ void ASTDeclWriter::VisitNamespaceDecl(NamespaceDecl *D) {
Writer.AddDeclRef(D->getAnonymousNamespace(), Record);
Code = serialization::DECL_NAMESPACE;
if (Writer.hasChain() && !D->isOriginalNamespace() &&
D->getOriginalNamespace()->isFromASTFile()) {
NamespaceDecl *NS = D->getOriginalNamespace();
Writer.UpdatedDeclContexts.insert(NS);
// Make sure all visible decls are written. They will be recorded later. We
// do this using a side data structure so we can sort the names into
// a deterministic order.
StoredDeclsMap *Map = NS->buildLookup();
SmallVector<std::pair<DeclarationName, DeclContext::lookup_result>, 16>
LookupResults;
if (Map) {
LookupResults.reserve(Map->size());
for (auto &Entry : *Map)
LookupResults.push_back(
std::make_pair(Entry.first, Entry.second.getLookupResult()));
}
std::sort(LookupResults.begin(), LookupResults.end(), llvm::less_first());
for (auto &NameAndResult : LookupResults) {
DeclarationName Name = NameAndResult.first;
DeclContext::lookup_result Result = NameAndResult.second;
if (Name.getNameKind() == DeclarationName::CXXConstructorName ||
Name.getNameKind() == DeclarationName::CXXConversionFunctionName) {
// We have to work around a name lookup bug here where negative lookup
// results for these names get cached in namespace lookup tables.
assert(Result.empty() && "Cannot have a constructor or conversion "
"function name in a namespace!");
continue;
}
for (NamedDecl *ND : Result)
Writer.GetDeclRef(ND);
}
}
if (Writer.hasChain() && D->isAnonymousNamespace() &&
D == D->getMostRecentDecl()) {
// This is a most recent reopening of the anonymous namespace. If its parent
@@ -1512,6 +1478,17 @@ void ASTDeclWriter::VisitDeclContext(DeclContext *DC, uint64_t LexicalOffset,
Record.push_back(VisibleOffset);
}
/// Determine whether D is the first declaration in its redeclaration chain that
/// is not from an AST file.
template <typename T>
static bool isFirstLocalDecl(Redeclarable<T> *D) {
assert(D && !static_cast<T*>(D)->isFromASTFile());
do
D = D->getPreviousDecl();
while (D && static_cast<T*>(D)->isFromASTFile());
return !D;
}
template <typename T>
void ASTDeclWriter::VisitRedeclarable(Redeclarable<T> *D) {
T *First = D->getFirstDecl();
@@ -1520,41 +1497,30 @@ void ASTDeclWriter::VisitRedeclarable(Redeclarable<T> *D) {
assert(isRedeclarableDeclKind(static_cast<T *>(D)->getKind()) &&
"Not considered redeclarable?");
// There is more than one declaration of this entity, so we will need to
// write a redeclaration chain.
Writer.AddDeclRef(First, Record);
Writer.Redeclarations.insert(First);
auto *Previous = D->getPreviousDecl();
// In a modules build, we can have imported declarations after a local
// canonical declaration. If this is the first local declaration, emit
// a list of all such imported declarations so that we can ensure they
// are loaded before we are. This allows us to rebuild the redecl chain
// in the right order on reload (all declarations imported by a module
// should be before all declarations provided by that module).
bool EmitImportedMergedCanonicalDecls = false;
// In a modules build, emit a list of all imported key declarations
// (excluding First, if it was imported), so that we can be sure that all
// redeclarations visible to this module are before D in the redecl chain.
unsigned I = Record.size();
Record.push_back(0);
if (Context.getLangOpts().Modules && Writer.Chain) {
auto *PreviousLocal = Previous;
while (PreviousLocal && PreviousLocal->isFromASTFile())
PreviousLocal = PreviousLocal->getPreviousDecl();
if (!PreviousLocal)
EmitImportedMergedCanonicalDecls = true;
if (isFirstLocalDecl(D)) {
Writer.Chain->forEachImportedKeyDecl(First, [&](const Decl *D) {
if (D != First)
Writer.AddDeclRef(D, Record);
});
Record[I] = Record.size() - I - 1;
// Write a redeclaration chain, attached to the first key decl.
Writer.Redeclarations.push_back(Writer.Chain->getKeyDeclaration(First));
}
} else if (D == First || D->getPreviousDecl()->isFromASTFile()) {
assert(isFirstLocalDecl(D) && "imported decl after local decl");
// Write a redeclaration chain attached to the first decl.
Writer.Redeclarations.push_back(First);
}
if (EmitImportedMergedCanonicalDecls) {
llvm::SmallMapVector<ModuleFile*, Decl*, 16> FirstInModule;
for (auto *Redecl = MostRecent; Redecl;
Redecl = Redecl->getPreviousDecl())
if (Redecl->isFromASTFile())
FirstInModule[Writer.Chain->getOwningModuleFile(Redecl)] = Redecl;
// FIXME: If FirstInModule has entries for modules A and B, and B imports
// A (directly or indirectly), we don't need to write the entry for A.
Record.push_back(FirstInModule.size());
for (auto I = FirstInModule.rbegin(), E = FirstInModule.rend();
I != E; ++I)
Writer.AddDeclRef(I->second, Record);
} else
Record.push_back(0);
// Make sure that we serialize both the previous and the most-recent
// declarations, which (transitively) ensures that all declarations in the
@@ -1562,7 +1528,7 @@ void ASTDeclWriter::VisitRedeclarable(Redeclarable<T> *D) {
//
// FIXME: This is not correct; when we reach an imported declaration we
// won't emit its previous declaration.
(void)Writer.GetDeclRef(Previous);
(void)Writer.GetDeclRef(D->getPreviousDecl());
(void)Writer.GetDeclRef(MostRecent);
} else {
// We use the sentinel value 0 to indicate an only declaration.