Skip to content

Commit 3113c62

Browse files
committed
AST: Fix performance regression in lookupDirect()
Every call to lookupDirect() was calling prepareLookupTable() followed by addLoadedExtensions(). While prepareLookupTable() only did its work once, addLoadedExtensions() would walk all currently-loaded members of all extensions every time. However, just merging it with prepareLookupTable() was not enough, because other places call prepareLookupTable(), which ends up loading extensions too early after this change. Instead, separate out the lazy allocation of the lookup table from initialization. getLookupTable() returns a potentially-uninitialized lookup table, and prepareLookupTable() now does what it did before as well as what was formerly in addLoadedExtensions(). With this new split, getLookupTable() can be used instead of prepareLookupTable() to avoid request cycles in a couple of places.
1 parent c2ca0ac commit 3113c62

File tree

2 files changed

+48
-33
lines changed

2 files changed

+48
-33
lines changed

include/swift/AST/Decl.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3565,10 +3565,6 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
35653565
/// Prepare to traverse the list of extensions.
35663566
void prepareExtensions();
35673567

3568-
/// Add loaded members from all extensions. Eagerly load any members that we
3569-
/// can't lazily load.
3570-
void addLoadedExtensions();
3571-
35723568
/// Retrieve the conformance loader (if any), and removing it in the
35733569
/// same operation. The caller is responsible for loading the
35743570
/// conformances.
@@ -3583,13 +3579,19 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
35833579
std::pair<LazyMemberLoader *, uint64_t> takeConformanceLoaderSlow();
35843580

35853581
/// A lookup table containing all of the members of this type and
3586-
/// its extensions.
3582+
/// its extensions, together with a bit indicating if the table
3583+
/// has been prepared.
35873584
///
35883585
/// The table itself is lazily constructed and updated when
35893586
/// lookupDirect() is called.
3590-
MemberLookupTable *LookupTable = nullptr;
3587+
llvm::PointerIntPair<MemberLookupTable *, 1, bool> LookupTable;
3588+
3589+
/// Get the lookup table, lazily constructing an empty table if
3590+
/// necessary.
3591+
MemberLookupTable *getLookupTable();
35913592

35923593
/// Prepare the lookup table to make it ready for lookups.
3594+
/// Does nothing when called more than once.
35933595
void prepareLookupTable();
35943596

35953597
/// Note that we have added a member into the iterable declaration context,

lib/AST/NameLookup.cpp

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,25 +1374,27 @@ void MemberLookupTable::updateLookupTable(NominalTypeDecl *nominal) {
13741374
}
13751375

13761376
void NominalTypeDecl::addedExtension(ExtensionDecl *ext) {
1377-
if (!LookupTable) return;
1377+
auto *table = LookupTable.getPointer();
1378+
1379+
if (!table) return;
13781380

13791381
if (ext->hasLazyMembers()) {
1380-
LookupTable->addMembers(ext->getCurrentMembersWithoutLoading());
1381-
LookupTable->clearLazilyCompleteCache();
1382+
table->addMembers(ext->getCurrentMembersWithoutLoading());
1383+
table->clearLazilyCompleteCache();
13821384
} else {
1383-
LookupTable->addMembers(ext->getMembers());
1385+
table->addMembers(ext->getMembers());
13841386
}
13851387
}
13861388

13871389
void NominalTypeDecl::addedMember(Decl *member) {
13881390
// If we have a lookup table, add the new member to it. If not, we'll pick up
13891391
// this member when we first create the table.
13901392
auto *vd = dyn_cast<ValueDecl>(member);
1391-
auto *lookup = LookupTable;
1392-
if (!vd || !lookup)
1393+
auto *table = LookupTable.getPointer();
1394+
if (!vd || !table)
13931395
return;
13941396

1395-
lookup->addMember(vd);
1397+
table->addMember(vd);
13961398
}
13971399

13981400
void ExtensionDecl::addedMember(Decl *member) {
@@ -1406,9 +1408,7 @@ void ExtensionDecl::addedMember(Decl *member) {
14061408
}
14071409

14081410
void NominalTypeDecl::addMemberToLookupTable(Decl *member) {
1409-
prepareLookupTable();
1410-
1411-
LookupTable->addMember(member);
1411+
getLookupTable()->addMember(member);
14121412
}
14131413

14141414
// For lack of anywhere more sensible to put it, here's a diagram of the pieces
@@ -1506,36 +1506,49 @@ populateLookupTableEntryFromExtensions(ASTContext &ctx,
15061506
}
15071507
}
15081508

1509+
MemberLookupTable *NominalTypeDecl::getLookupTable() {
1510+
if (!LookupTable.getPointer()) {
1511+
auto &ctx = getASTContext();
1512+
LookupTable.setPointer(new (ctx) MemberLookupTable(ctx));
1513+
}
1514+
1515+
return LookupTable.getPointer();
1516+
}
1517+
15091518
void NominalTypeDecl::prepareLookupTable() {
1510-
// If we have already allocated the lookup table, then there's nothing further
1519+
// If we have already prepared the lookup table, then there's nothing further
15111520
// to do.
1512-
if (LookupTable) {
1521+
if (LookupTable.getInt())
15131522
return;
1514-
}
15151523

1516-
// Otherwise start the first fill.
1517-
auto &ctx = getASTContext();
1518-
LookupTable = new (ctx) MemberLookupTable(ctx);
1524+
LookupTable.setInt(true);
15191525

1526+
auto *table = getLookupTable();
1527+
1528+
// Otherwise start the first fill.
15201529
if (hasLazyMembers()) {
15211530
assert(!hasUnparsedMembers());
1522-
LookupTable->addMembers(getCurrentMembersWithoutLoading());
1531+
table->addMembers(getCurrentMembersWithoutLoading());
15231532
} else {
1524-
LookupTable->addMembers(getMembers());
1533+
table->addMembers(getMembers());
15251534
}
1526-
}
15271535

1528-
void NominalTypeDecl::addLoadedExtensions() {
1536+
// Note: this calls prepareExtensions()
15291537
for (auto e : getExtensions()) {
15301538
// If we can lazy-load this extension, only take the members we've loaded
15311539
// so far.
1540+
//
1541+
// FIXME: This should be 'e->hasLazyMembers()' but that crashes` because
1542+
// some imported extensions don't have a Clang node, and only support
1543+
// LazyMemberLoader::loadAllMembers() and not
1544+
// LazyMemberLoader::loadNamedMembers().
15321545
if (e->wasDeserialized() || e->hasClangNode()) {
1533-
LookupTable->addMembers(e->getCurrentMembersWithoutLoading());
1546+
table->addMembers(e->getCurrentMembersWithoutLoading());
15341547
continue;
15351548
}
15361549

15371550
// Else, load all the members into the table.
1538-
LookupTable->addMembers(e->getMembers());
1551+
table->addMembers(e->getMembers());
15391552
}
15401553
}
15411554

@@ -1592,12 +1605,12 @@ DirectLookupRequest::evaluate(Evaluator &evaluator,
15921605

15931606
decl->prepareLookupTable();
15941607

1595-
// If we're allowed to load extensions, call addLoadedExtensions to ensure we
1596-
// properly invalidate the lazily-complete cache for any extensions brought in
1597-
// by modules loaded after-the-fact. This can happen with the LLDB REPL.
1598-
decl->addLoadedExtensions();
1608+
// Call prepareExtensions() to ensure we properly invalidate the
1609+
// lazily-complete cache for any extensions brought in by modules
1610+
// loaded after-the-fact. This can happen with the LLDB REPL.
1611+
decl->prepareExtensions();
15991612

1600-
auto &Table = *decl->LookupTable;
1613+
auto &Table = *decl->getLookupTable();
16011614
if (!useNamedLazyMemberLoading) {
16021615
// Make sure we have the complete list of members (in this nominal and in
16031616
// all extensions).

0 commit comments

Comments
 (0)