Skip to content

Commit 19cc6ff

Browse files
authored
Merge pull request swiftlang#28922 from CodaFi/neo-compl-cache
[NFC] One-Shot Name Lookup + Lazily-Complete Base Name Cache
2 parents c4c7851 + b1c39f1 commit 19cc6ff

File tree

5 files changed

+93
-83
lines changed

5 files changed

+93
-83
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3300,6 +3300,10 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
33003300
/// so that it can also be added to the lookup table (if needed).
33013301
void addedMember(Decl *member);
33023302

3303+
/// Note that we have added an extension into the nominal type,
3304+
/// so that its members can eventually be added to the lookup table.
3305+
void addedExtension(ExtensionDecl *ext);
3306+
33033307
/// A lookup table used to find the protocol conformances of
33043308
/// a given nominal type.
33053309
mutable ConformanceLookupTable *ConformanceTable = nullptr;

lib/AST/Decl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3664,12 +3664,16 @@ void NominalTypeDecl::addExtension(ExtensionDecl *extension) {
36643664
if (!FirstExtension) {
36653665
FirstExtension = extension;
36663666
LastExtension = extension;
3667+
3668+
addedExtension(extension);
36673669
return;
36683670
}
36693671

36703672
// Add to the end of the list.
36713673
LastExtension->NextExtension.setPointer(extension);
36723674
LastExtension = extension;
3675+
3676+
addedExtension(extension);
36733677
}
36743678

36753679
ArrayRef<VarDecl *> NominalTypeDecl::getStoredProperties() const {

lib/AST/NameLookup.cpp

Lines changed: 83 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,11 @@ class swift::MemberLookupTable {
880880
/// Lookup table mapping names to the set of declarations with that name.
881881
LookupTable Lookup;
882882

883+
/// The set of names of lazily-loaded members that the lookup table has a
884+
/// complete accounting of with respect to all known extensions of its
885+
/// parent nominal type.
886+
llvm::DenseSet<DeclBaseName> LazilyCompleteNames;
887+
883888
public:
884889
/// Create a new member lookup table.
885890
explicit MemberLookupTable(ASTContext &ctx);
@@ -893,6 +898,24 @@ class swift::MemberLookupTable {
893898
/// Add the given members to the lookup table.
894899
void addMembers(DeclRange members);
895900

901+
/// Returns \c true if the lookup table has a complete accounting of the
902+
/// given name.
903+
bool isLazilyComplete(DeclBaseName name) const {
904+
return LazilyCompleteNames.find(name) != LazilyCompleteNames.end();
905+
}
906+
907+
/// Mark a given lazily-loaded name as being complete.
908+
void markLazilyComplete(DeclBaseName name) {
909+
LazilyCompleteNames.insert(name);
910+
}
911+
912+
/// Clears the cache of lazily-complete names. This _must_ be called when
913+
/// new extensions with lazy members are added to the type, or direct lookup
914+
/// will return inconsistent or stale results.
915+
void clearLazilyCompleteCache() {
916+
LazilyCompleteNames.clear();
917+
}
918+
896919
/// Iterator into the lookup table.
897920
typedef LookupTable::iterator iterator;
898921

@@ -912,7 +935,11 @@ class swift::MemberLookupTable {
912935

913936
os << "Lookup:\n ";
914937
for (auto &pair : Lookup) {
915-
pair.getFirst().print(os) << ":\n ";
938+
pair.getFirst().print(os);
939+
if (isLazilyComplete(pair.getFirst().getBaseName())) {
940+
os << " (lazily complete)";
941+
}
942+
os << ":\n ";
916943
for (auto &decl : pair.getSecond()) {
917944
os << "- ";
918945
decl->dumpRef(os);
@@ -926,21 +953,6 @@ class swift::MemberLookupTable {
926953
dump(llvm::errs());
927954
}
928955

929-
// Mark all Decls in this table as not-resident in a table, drop
930-
// references to them. Should only be called when this was not fully-populated
931-
// from an IterableDeclContext.
932-
void clear() {
933-
// LastExtensionIncluded would only be non-null if this was populated from
934-
// an IterableDeclContext (though it might still be null in that case).
935-
assert(LastExtensionIncluded == nullptr);
936-
for (auto const &i : Lookup) {
937-
for (auto d : i.getSecond()) {
938-
d->setAlreadyInLookupTable(false);
939-
}
940-
}
941-
Lookup.clear();
942-
}
943-
944956
// Only allow allocation of member lookup tables using the allocator in
945957
// ASTContext or by doing a placement new.
946958
void *operator new(size_t Bytes, ASTContext &C,
@@ -1043,25 +1055,26 @@ void MemberLookupTable::updateLookupTable(NominalTypeDecl *nominal) {
10431055
}
10441056
}
10451057

1046-
void NominalTypeDecl::addedMember(Decl *member) {
1047-
auto *vd = dyn_cast<ValueDecl>(member);
1048-
if (!vd)
1049-
return;
1058+
void NominalTypeDecl::addedExtension(ExtensionDecl *ext) {
1059+
if (!LookupTable) return;
10501060

1061+
if (ext->hasLazyMembers()) {
1062+
LookupTable->addMembers(ext->getCurrentMembersWithoutLoading());
1063+
LookupTable->clearLazilyCompleteCache();
1064+
} else {
1065+
LookupTable->addMembers(ext->getMembers());
1066+
}
1067+
}
1068+
1069+
void NominalTypeDecl::addedMember(Decl *member) {
10511070
// If we have a lookup table, add the new member to it. If not, we'll pick up
10521071
// this member when we first create the table.
1053-
if (auto *lookup = LookupTable) {
1054-
if (hasLazyMembers()) {
1055-
// If we have lazy members, only add the new member to the lookup
1056-
// table if we already have another member with the same name.
1057-
// The presence of a lookup table entry indicates that the
1058-
// nominal as well as all extensions have already been searched.
1059-
if (lookup->find(vd->getBaseName()) == lookup->end())
1060-
return;
1061-
}
1072+
auto *vd = dyn_cast<ValueDecl>(member);
1073+
auto *lookup = LookupTable;
1074+
if (!vd || !lookup)
1075+
return;
10621076

1063-
lookup->addMember(vd);
1064-
}
1077+
lookup->addMember(vd);
10651078
}
10661079

10671080
void ExtensionDecl::addedMember(Decl *member) {
@@ -1094,8 +1107,8 @@ void ExtensionDecl::addedMember(Decl *member) {
10941107
// │ExtensionDecl *LastExtension ─┼───────┐│ │ └───┐
10951108
// │ │ ││ └──────────────────────┐│
10961109
// │MemberLookupTable *LookupTable├─┐ ││ ││
1097-
// │bool LookupTableComplete │ │ ││ ┌─────────────────┐ ││
1098-
// └──────────────────────────────┘ │ ││ │ExtensionDecl │ ││
1110+
// └──────────────────────────────┘ │ ││ ┌─────────────────┐ ││
1111+
// │ ││ │ExtensionDecl │ ││
10991112
// │ ││ │------------- │ ││
11001113
// ┌─────────────┘ │└────▶│ExtensionDecl │ ││
11011114
// │ │ │ *NextExtension ├──┐ ││
@@ -1156,34 +1169,30 @@ populateLookupTableEntryFromLazyIDCLoader(ASTContext &ctx,
11561169
}
11571170
}
11581171

1159-
static void populateLookupTableEntryFromCurrentMembers(
1160-
ASTContext &ctx, MemberLookupTable &LookupTable, DeclBaseName name,
1161-
IterableDeclContext *IDC) {
1162-
for (auto m : IDC->getMembers()) {
1163-
if (auto v = dyn_cast<ValueDecl>(m)) {
1164-
if (v->getBaseName() == name) {
1165-
LookupTable.addMember(m);
1166-
}
1167-
}
1168-
}
1169-
}
1170-
11711172
static void
11721173
populateLookupTableEntryFromExtensions(ASTContext &ctx,
11731174
MemberLookupTable &table,
11741175
NominalTypeDecl *nominal,
11751176
DeclBaseName name) {
1177+
assert(!table.isLazilyComplete(name) &&
1178+
"Should not be searching extensions for complete name!");
1179+
11761180
for (auto e : nominal->getExtensions()) {
1177-
// If we can retrieve the members of this extension without deserializing
1178-
// anything, do so now.
1179-
if (!e->wasDeserialized() && !e->hasClangNode()) {
1180-
populateLookupTableEntryFromCurrentMembers(ctx, table, name, e);
1181+
// If there's no lazy members to look at, all the members of this extension
1182+
// are present in the lookup table.
1183+
if (!e->hasLazyMembers()) {
11811184
continue;
11821185
}
11831186

1187+
assert(e->wasDeserialized() || e->hasClangNode() &&
1188+
"Extension without deserializable content has lazy members!");
11841189
assert(!e->hasUnparsedMembers());
1190+
1191+
// Try lazy loading. If that fails, then we fall back by loading the
1192+
// entire extension. FIXME: It's rather unfortunate that we fall off the
1193+
// happy path because the Clang Importer can't handle lazy import-as-member.
11851194
if (populateLookupTableEntryFromLazyIDCLoader(ctx, table, name, e)) {
1186-
populateLookupTableEntryFromCurrentMembers(ctx, table, name, e);
1195+
e->loadAllMembers();
11871196
}
11881197
}
11891198
}
@@ -1204,19 +1213,18 @@ void NominalTypeDecl::prepareLookupTable() {
12041213

12051214
// Lazy members: if the table needs population, populate the table _only
12061215
// from those members already in the IDC member list_ such as implicits or
1207-
// globals-as-members, then update table entries from the extensions that
1208-
// have the same names as any such initial-population members.
1216+
// globals-as-members.
12091217
LookupTable->addMembers(getCurrentMembersWithoutLoading());
1210-
1211-
llvm::SmallSet<DeclBaseName, 4> baseNamesPresent;
1212-
for (auto entry : *LookupTable) {
1213-
auto baseName = entry.getFirst().getBaseName();
1214-
if (!baseNamesPresent.insert(baseName).second)
1218+
for (auto e : getExtensions()) {
1219+
// If we can lazy-load this extension, only take the members we've loaded
1220+
// so far.
1221+
if (e->wasDeserialized() || e->hasClangNode()) {
1222+
LookupTable->addMembers(e->getCurrentMembersWithoutLoading());
12151223
continue;
1224+
}
12161225

1217-
populateLookupTableEntryFromExtensions(getASTContext(),
1218-
*LookupTable,
1219-
this, baseName);
1226+
// Else, load all the members into the table.
1227+
LookupTable->addMembers(e->getMembers());
12201228
}
12211229
} else {
12221230
LookupTable->addMembers(getMembers());
@@ -1276,8 +1284,9 @@ NominalTypeDecl::lookupDirect(DeclName name,
12761284
DeclName name) -> Optional<TinyPtrVector<ValueDecl *>> {
12771285
// Look for a declaration with this name.
12781286
auto known = table->find(name);
1279-
if (known == table->end())
1287+
if (known == table->end()) {
12801288
return None;
1289+
}
12811290

12821291
// We found something; return it.
12831292
return maybeFilterOutAttrImplements(known->second, name,
@@ -1297,29 +1306,22 @@ NominalTypeDecl::lookupDirect(DeclName name,
12971306

12981307
if (!useNamedLazyMemberLoading) {
12991308
updateLookupTable(LookupTable);
1309+
} else if (!LookupTable->isLazilyComplete(name.getBaseName())) {
1310+
// The lookup table believes it doesn't have a complete accounting of this
1311+
// name - either because we're never seen it before, or another extension
1312+
// was registered since the last time we searched. Ask the loaders to give
1313+
// us a hand.
1314+
auto &Table = *LookupTable;
1315+
DeclBaseName baseName(name.getBaseName());
1316+
if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table, baseName, this)) {
1317+
updateLookupTable(LookupTable);
1318+
} else {
1319+
populateLookupTableEntryFromExtensions(ctx, Table, this, baseName);
1320+
}
1321+
Table.markLazilyComplete(baseName);
13001322
}
13011323

13021324
// Look for a declaration with this name.
1303-
if (auto lookup = tryCacheLookup(LookupTable, name))
1304-
return lookup.getValue();
1305-
1306-
if (!useNamedLazyMemberLoading) {
1307-
return { };
1308-
}
1309-
1310-
// If we get here, we had a cache-miss and _are_ using
1311-
// NamedLazyMemberLoading. Try to populate a _single_ entry in the
1312-
// MemberLookupTable from both this nominal and all of its extensions, and
1313-
// retry.
1314-
auto &Table = *LookupTable;
1315-
if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table,
1316-
name.getBaseName(), this)) {
1317-
updateLookupTable(LookupTable);
1318-
} else {
1319-
populateLookupTableEntryFromExtensions(ctx, Table, this,
1320-
name.getBaseName());
1321-
}
1322-
13231325
return tryCacheLookup(LookupTable, name)
13241326
.getValueOr(TinyPtrVector<ValueDecl *>());
13251327
}

test/NameBinding/named_lazy_member_loading_objc_category.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// Check that named-lazy-member-loading reduces the number of Decls deserialized
99
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre %s
1010
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post %s
11-
// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -10' %t/stats-pre %t/stats-post
11+
// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -30' %t/stats-pre %t/stats-post
1212

1313
import NamedLazyMembers
1414

test/NameBinding/named_lazy_member_loading_objc_protocol.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// Check that named-lazy-member-loading reduces the number of Decls deserialized
99
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre %s
1010
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post %s
11-
// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -9' %t/stats-pre %t/stats-post
11+
// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -40' %t/stats-pre %t/stats-post
1212

1313
import NamedLazyMembers
1414

0 commit comments

Comments
 (0)