Skip to content

Commit 30b6fdc

Browse files
authored
Merge pull request #12429 from graydon/named-lazy-member-loading
Named lazy member loading 1/N
2 parents 58c9b45 + cb1c852 commit 30b6fdc

File tree

16 files changed

+213
-4
lines changed

16 files changed

+213
-4
lines changed

include/swift/AST/LazyResolver.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,20 @@ class alignas(void*) LazyMemberLoader {
218218
public:
219219
virtual ~LazyMemberLoader() = default;
220220

221-
/// Populates the given vector with all member decls for \p D.
221+
/// Populates a given decl \p D with all of its members.
222222
///
223223
/// The implementation should add the members to D.
224224
virtual void
225225
loadAllMembers(Decl *D, uint64_t contextData) = 0;
226226

227+
/// Populates a vector with all members of \p D that have DeclName
228+
/// matching \p N.
229+
///
230+
/// Returns None if an error occurred \em or named member-lookup
231+
/// was otherwise unsupported in this implementation or Decl.
232+
virtual Optional<TinyPtrVector<ValueDecl *>>
233+
loadNamedMembers(const Decl *D, DeclName N, uint64_t contextData) = 0;
234+
227235
/// Populates the given vector with all conformances for \p D.
228236
///
229237
/// The implementation should \em not call setConformances on \p D.

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ namespace swift {
153153
/// \brief Enable the iterative type checker.
154154
bool IterativeTypeChecker = false;
155155

156+
/// \brief Enable named lazy member loading.
157+
bool NamedLazyMemberLoading = false;
158+
156159
/// Debug the generic signatures computed by the generic signature builder.
157160
bool DebugGenericSignatures = false;
158161

include/swift/Basic/Statistics.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ FRONTEND_STATISTIC(Sema, NumLazyIterableDeclContexts)
161161
/// Number of direct member-name lookups performed on nominal types.
162162
FRONTEND_STATISTIC(Sema, NominalTypeLookupDirectCount)
163163

164+
/// Number of member-name lookups that avoided loading all members.
165+
FRONTEND_STATISTIC(Sema, NamedLazyMemberLoadSuccessCount)
166+
167+
/// Number of member-name lookups that wound up loading all members.
168+
FRONTEND_STATISTIC(Sema, NamedLazyMemberLoadFailureCount)
169+
164170
/// Number of types deserialized.
165171
FRONTEND_STATISTIC(Sema, NumTypesDeserialized)
166172

include/swift/Option/FrontendOptions.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ def propagate_constraints : Flag<["-"], "propagate-constraints">,
156156
def iterative_type_checker : Flag<["-"], "iterative-type-checker">,
157157
HelpText<"Enable the iterative type checker">;
158158

159+
def enable_named_lazy_member_loading : Flag<["-"], "enable-named-lazy-member-loading">,
160+
HelpText<"Enable per-name lazy member loading">;
161+
159162
def debug_generic_signatures : Flag<["-"], "debug-generic-signatures">,
160163
HelpText<"Debug generic signatures">;
161164

include/swift/Serialization/ModuleFile.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,11 @@ class ModuleFile
700700
virtual void loadAllMembers(Decl *D,
701701
uint64_t contextData) override;
702702

703+
virtual
704+
Optional<TinyPtrVector<ValueDecl *>>
705+
loadNamedMembers(const Decl *D, DeclName N,
706+
uint64_t contextData) override;
707+
703708
virtual void
704709
loadAllConformances(const Decl *D, uint64_t contextData,
705710
SmallVectorImpl<ProtocolConformance*> &Conforms) override;

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3371,6 +3371,10 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement(
33713371
}
33723372
}
33733373

3374+
// Remaining logic is not relevant in ObjC protocol cases.
3375+
if (proto->isObjC())
3376+
return ConstraintResult::Resolved;
3377+
33743378
// Collect all of the inherited associated types and typealiases in the
33753379
// inherited protocols (recursively).
33763380
llvm::MapVector<DeclName, TinyPtrVector<TypeDecl *>> inheritedTypeDecls;

lib/AST/NameLookup.cpp

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
#include "swift/Basic/STLExtras.h"
3030
#include "llvm/ADT/DenseMap.h"
3131
#include "llvm/ADT/TinyPtrVector.h"
32+
#include "llvm/Support/Debug.h"
33+
#include "llvm/Support/raw_ostream.h"
34+
35+
#define DEBUG_TYPE "namelookup"
3236

3337
using namespace swift;
3438

@@ -1195,12 +1199,49 @@ TinyPtrVector<ValueDecl *> NominalTypeDecl::lookupDirect(
11951199
DeclName name,
11961200
bool ignoreNewExtensions) {
11971201
RecursiveSharedTimer::Guard guard;
1198-
if (auto s = getASTContext().Stats) {
1202+
ASTContext &ctx = getASTContext();
1203+
if (auto s = ctx.Stats) {
11991204
++s->getFrontendCounters().NominalTypeLookupDirectCount;
12001205
guard = s->getFrontendRecursiveSharedTimers()
12011206
.NominalTypeDecl__lookupDirect.getGuard();
12021207
}
12031208

1209+
DEBUG(llvm::dbgs() << getNameStr() << ".lookupDirect(" << name << ")"
1210+
<< ", lookupTable.getInt()=" << LookupTable.getInt()
1211+
<< ", hasLazyMembers()=" << hasLazyMembers()
1212+
<< "\n");
1213+
1214+
bool hasExtensionsToConsider = false;
1215+
if (!ignoreNewExtensions) {
1216+
auto E = getExtensions();
1217+
hasExtensionsToConsider = E.begin() != E.end();
1218+
}
1219+
1220+
if (!hasExtensionsToConsider &&
1221+
ctx.LangOpts.NamedLazyMemberLoading &&
1222+
!LookupTable.getInt() &&
1223+
hasLazyMembers()) {
1224+
// The lookup table (containing all loaded members) has not yet been built;
1225+
// we *might* be able to avoid loading all members, just focus on the ones
1226+
// matching the name we were asked for.
1227+
TinyPtrVector<ValueDecl *> results;
1228+
auto contextInfo =
1229+
ctx.getOrCreateLazyIterableContextData(this,
1230+
/*lazyLoader=*/nullptr);
1231+
if (auto results =
1232+
contextInfo->loader->loadNamedMembers(this, name,
1233+
contextInfo->memberData)) {
1234+
if (auto s = ctx.Stats) {
1235+
++s->getFrontendCounters().NamedLazyMemberLoadSuccessCount;
1236+
}
1237+
return *results;
1238+
} else {
1239+
if (auto s = ctx.Stats) {
1240+
++s->getFrontendCounters().NamedLazyMemberLoadFailureCount;
1241+
}
1242+
}
1243+
}
1244+
12041245
(void)getMembers();
12051246

12061247
// Make sure we have the complete list of members (in this nominal and in all

lib/ClangImporter/ClangImporter.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3274,6 +3274,64 @@ void ClangImporter::Implementation::lookupAllObjCMembers(
32743274
}
32753275
}
32763276

3277+
Optional<TinyPtrVector<ValueDecl *>>
3278+
ClangImporter::Implementation::loadNamedMembers(
3279+
const Decl *D, DeclName N, uint64_t contextData) {
3280+
3281+
auto *DC = cast<DeclContext>(D);
3282+
3283+
auto *CD = D->getClangDecl();
3284+
assert(CD && "loadNamedMembers on a Decl without a clangDecl");
3285+
3286+
// There are 3 cases:
3287+
//
3288+
// - The decl is from a bridging header, CMO is Some(nullptr)
3289+
// which denotes the __ObjC Swift module and its associated
3290+
// BridgingHeaderLookupTable.
3291+
//
3292+
// - The decl is from a clang module, CMO is Some(M) for non-null
3293+
// M and we can use the table for that module.
3294+
//
3295+
// - The decl is a forward declaration, CMO is None, which should
3296+
// never be the case if we got here (someone is asking for members).
3297+
//
3298+
// findLookupTable, below, handles the first two cases; we assert on the
3299+
// third.
3300+
3301+
auto CMO = getClangSubmoduleForDecl(CD);
3302+
assert(CMO && "loadNamedMembers on a forward-declared Decl");
3303+
3304+
auto table = findLookupTable(*CMO);
3305+
assert(table && "clang module without lookup table");
3306+
3307+
clang::ASTContext &clangCtx = getClangASTContext();
3308+
3309+
TinyPtrVector<ValueDecl *> Members;
3310+
if (auto *CPD = dyn_cast<clang::ObjCProtocolDecl>(CD)) {
3311+
for (auto entry : table->lookup(SerializedSwiftName(N.getBaseName()), CPD)) {
3312+
if (!entry.is<clang::NamedDecl *>()) continue;
3313+
auto member = entry.get<clang::NamedDecl *>();
3314+
if (!isVisibleClangEntry(clangCtx, member)) continue;
3315+
SmallVector<Decl*, 4> tmp;
3316+
insertMembersAndAlternates(member, tmp);
3317+
for (auto *TD : tmp) {
3318+
if (auto *V = dyn_cast<ValueDecl>(TD)) {
3319+
// Skip ValueDecls if they import into different DeclContexts
3320+
// or under different names than the one we asked about.
3321+
if (V->getDeclContext() == DC &&
3322+
V->getFullName().matchesRef(N)) {
3323+
Members.push_back(V);
3324+
}
3325+
}
3326+
}
3327+
}
3328+
return Members;
3329+
}
3330+
3331+
return None;
3332+
}
3333+
3334+
32773335
EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext(
32783336
const NominalTypeDecl *nominal) {
32793337
// If we have a Clang declaration, look at it to determine the

lib/ClangImporter/ImporterImpl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
11221122
virtual void
11231123
loadAllMembers(Decl *D, uint64_t unused) override;
11241124

1125+
virtual Optional<TinyPtrVector<ValueDecl *>>
1126+
loadNamedMembers(const Decl *D, DeclName N, uint64_t contextData) override;
1127+
11251128
private:
11261129
void
11271130
loadAllMembersOfObjcContainer(Decl *D,

lib/Frontend/CompilerInvocation.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
845845
Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints);
846846
Opts.EnableConstraintPropagation |= Args.hasArg(OPT_propagate_constraints);
847847
Opts.IterativeTypeChecker |= Args.hasArg(OPT_iterative_type_checker);
848+
Opts.NamedLazyMemberLoading |= Args.hasArg(OPT_enable_named_lazy_member_loading);
848849
Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures);
849850

850851
Opts.DebuggerSupport |= Args.hasArg(OPT_debugger_support);

lib/Sema/TypeCheckNameLookup.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,14 @@ namespace {
145145
// pull out the superclass instead, and use that below.
146146
if (foundInType->isExistentialType()) {
147147
auto layout = foundInType->getExistentialLayout();
148-
if (layout.superclass)
148+
if (layout.superclass) {
149149
conformingType = layout.superclass;
150+
} else {
151+
// Non-subclass existential: don't need to look for further
152+
// conformance or witness.
153+
addResult(found);
154+
return;
155+
}
150156
}
151157

152158
// Dig out the protocol conformance.

lib/Serialization/Deserialization.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4678,6 +4678,13 @@ void ModuleFile::loadAllMembers(Decl *container, uint64_t contextData) {
46784678
}
46794679
}
46804680

4681+
Optional<TinyPtrVector<ValueDecl *>>
4682+
ModuleFile::loadNamedMembers(const Decl *D, DeclName N,
4683+
uint64_t contextData) {
4684+
// Not presently supported.
4685+
return None;
4686+
}
4687+
46814688
void
46824689
ModuleFile::loadAllConformances(const Decl *D, uint64_t contextData,
46834690
SmallVectorImpl<ProtocolConformance*> &conformances) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@protocol Doer
4+
5+
- (void)doSomeWork;
6+
- (void)doSomeWorkWithSpeed:(int)s;
7+
- (void)doSomeWorkWithSpeed:(int)s thoroughness:(int)t
8+
NS_SWIFT_NAME(doVeryImportantWork(speed:thoroughness:));
9+
- (void)doSomeWorkWithSpeed:(int)s alacrity:(int)a
10+
NS_SWIFT_NAME(doSomeWorkWithSpeed(speed:levelOfAlacrity:));
11+
12+
// These we are generally trying to not-import, via laziness.
13+
- (void)goForWalk;
14+
- (void)takeNap;
15+
- (void)eatMeal;
16+
- (void)tidyHome;
17+
- (void)callFamily;
18+
- (void)singSong;
19+
- (void)readBook;
20+
- (void)attendLecture;
21+
- (void)writeLetter;
22+
23+
@end
24+
25+
26+
// Don't conform to the protocol; that loads all protocol members.
27+
@interface SimpleDoer
28+
29+
// These are names we're hoping don't interfere with Doer, above.
30+
+ (SimpleDoer*)Doer;
31+
+ (SimpleDoer*)DoerOfNoWork;
32+
33+
@end
34+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module NamedLazyMembers {
2+
header "NamedLazyMembers.h"
3+
export *
4+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// REQUIRES: objc_interop
2+
// REQUIRES: OS=macosx
3+
// RUN: rm -rf %t && mkdir -p %t/stats-pre && mkdir -p %t/stats-post
4+
//
5+
// Prime module cache
6+
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -typecheck %s
7+
//
8+
// Check that without lazy named member loading, we're importing too much.
9+
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -typecheck -stats-output-dir %t/stats-pre %s
10+
// RUN: %utils/process-stats-dir.py --evaluate 'NumTotalClangImportedEntities > 20' %t/stats-pre
11+
//
12+
// Check that with lazy named member loading, we're importing less.
13+
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -enable-named-lazy-member-loading -stats-output-dir %t/stats-post %s
14+
// RUN: %utils/process-stats-dir.py --evaluate 'NumTotalClangImportedEntities < 15' %t/stats-post
15+
16+
import NamedLazyMembers
17+
18+
public func foo(d: Doer) {
19+
d.doSomeWork()
20+
d.doSomeWork(withSpeed:10)
21+
d.doVeryImportantWork(speed:10, thoroughness:12)
22+
d.doSomeWorkWithSpeed(speed:10, levelOfAlacrity:12)
23+
24+
let _ = SimpleDoer()
25+
let _ = SimpleDoer.ofNoWork()
26+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
// See https://swift.org/LICENSE.txt for license information
66
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

8-
// RUN: not --crash %target-swift-frontend %s -emit-ir
8+
// RUN: not %target-swift-frontend %s -emit-ir
99
@objc protocol P{typealias e where f=a

0 commit comments

Comments
 (0)