Skip to content

Commit 0a7929e

Browse files
committed
Refine Naive Dependency Collection Algorithm
Split off the notion of "recording" dependencies from the notion of "collecting" dependencies. This corrects an oversight in the previous design where dependency replay and recording were actually not "free" in WMO where we actually never track dependencies. This architecture also lays the groundwork for the removal of the referenced name trackers. The algorithm builds upon the infrastructure for dependency sources and sinks laid down during the cut over to request-based dependency tracking in #30723. The idea of the naive algorithm is this: For a chain of requests A -> B* -> C -> D* -> ... -> L where L is a lookup request and all starred requests are cached, once L writes into the dependency collector, the active stack is walked and at each cache-point the results of dependency collection are associated with the request itself (in this example, B* and D* have all the names L found associated with them). Subsequent evaluations of these cached requests (B* and D* et al) will then *replay* the previous lookup results from L into the active referenced name tracker. One complication is, suppose the evaluation of a cached request involves multiple downstream name lookups. More concretely, suppose we have the following request trace: A* -> B -> L | -> C -> L | -> D -> L | -> ... Then A* must see the union of the results of each L. If this reminds anyone of a union-find, that is no accident! A persistent union-find a la Conchon and Filliatre is probably in order to help bring down peak heap usage...
1 parent 115bd28 commit 0a7929e

14 files changed

+145
-108
lines changed

include/swift/AST/Evaluator.h

+7-7
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ class Evaluator {
225225
/// so all clients must cope with cycles.
226226
llvm::DenseMap<AnyRequest, std::vector<AnyRequest>> dependencies;
227227

228-
evaluator::DependencyCollector collector;
228+
evaluator::DependencyRecorder recorder;
229229

230230
/// Retrieve the request function for the given zone and request IDs.
231231
AbstractRequestFunction *getAbstractRequestFunction(uint8_t zoneID,
@@ -268,8 +268,8 @@ class Evaluator {
268268
typename std::enable_if<Request::isEverCached>::type * = nullptr>
269269
llvm::Expected<typename Request::OutputType>
270270
operator()(const Request &request) {
271-
evaluator::DependencyCollector::StackRAII<Request> incDeps{collector,
272-
request};
271+
evaluator::DependencyRecorder::StackRAII<Request> incDeps{recorder,
272+
request};
273273
// The request can be cached, but check a predicate to determine
274274
// whether this particular instance is cached. This allows more
275275
// fine-grained control over which instances get cache.
@@ -285,8 +285,8 @@ class Evaluator {
285285
typename std::enable_if<!Request::isEverCached>::type * = nullptr>
286286
llvm::Expected<typename Request::OutputType>
287287
operator()(const Request &request) {
288-
evaluator::DependencyCollector::StackRAII<Request> incDeps{collector,
289-
request};
288+
evaluator::DependencyRecorder::StackRAII<Request> incDeps{recorder,
289+
request};
290290
return getResultUncached(request);
291291
}
292292

@@ -436,15 +436,15 @@ class Evaluator {
436436
!Request::isDependencySink>::type * = nullptr>
437437
void reportEvaluatedResult(const Request &r,
438438
const typename Request::OutputType &o) {
439-
collector.replay(ActiveRequest(r));
439+
recorder.replay(ActiveRequest(r));
440440
}
441441

442442
// Report the result of evaluating a request that is a dependency sink.
443443
template <typename Request,
444444
typename std::enable_if<Request::isDependencySink>::type * = nullptr>
445445
void reportEvaluatedResult(const Request &r,
446446
const typename Request::OutputType &o) {
447-
return collector.record(activeRequests, [&r, &o](auto &c) {
447+
return recorder.record(activeRequests, [&r, &o](auto &c) {
448448
return r.writeDependencySink(c, o);
449449
});
450450
}

include/swift/AST/EvaluatorDependencies.h

+51-36
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,9 @@ inline DependencyScope getScopeForAccessLevel(AccessLevel l) {
107107
// of individual contexts.
108108
using DependencySource = llvm::PointerIntPair<SourceFile *, 1, DependencyScope>;
109109

110-
/// A \c DependencyCollector is an aggregator of named references discovered in a
111-
/// particular \c DependencyScope during the course of request evaluation.
112-
struct DependencyCollector {
113-
private:
114-
/// A stack of dependency sources in the order they were evaluated.
115-
llvm::SmallVector<evaluator::DependencySource, 8> dependencySources;
110+
struct DependencyRecorder;
116111

112+
struct DependencyCollector {
117113
struct Reference {
118114
public:
119115
enum class Kind {
@@ -178,36 +174,11 @@ struct DependencyCollector {
178174
};
179175
};
180176

181-
public:
182-
enum class Mode {
183-
// Enables the current "status quo" behavior of the dependency collector.
184-
//
185-
// By default, the dependency collector moves to register dependencies in
186-
// the referenced name trackers at the top of the active dependency stack.
187-
StatusQuo,
188-
// Enables an experimental mode to only register private dependencies.
189-
//
190-
// This mode restricts the dependency collector to ignore changes of
191-
// scope. This has practical effect of charging all unqualified lookups to
192-
// the primary file being acted upon instead of to the destination file.
193-
ExperimentalPrivateDependencies,
194-
};
195-
Mode mode;
196-
llvm::DenseMap<AnyRequest, llvm::DenseSet<Reference, Reference::Info>>
197-
requestReferences;
177+
DependencyRecorder &parent;
198178
llvm::DenseSet<Reference, Reference::Info> scratch;
199-
bool isRecording;
200-
201-
explicit DependencyCollector(Mode mode)
202-
: mode{mode}, requestReferences{}, scratch{}, isRecording{false} {};
203-
204-
private:
205-
void realizeOrRecord(const Reference &ref);
206179

207180
public:
208-
void replay(const swift::ActiveRequest &req);
209-
void record(const llvm::SetVector<swift::ActiveRequest> &stack,
210-
llvm::function_ref<void(DependencyCollector &)> rec);
181+
explicit DependencyCollector(DependencyRecorder &parent) : parent(parent) {}
211182

212183
public:
213184
/// Registers a named reference from the current dependency scope to a member
@@ -246,6 +217,50 @@ struct DependencyCollector {
246217
/// a name that is found by \c AnyObject lookup.
247218
void addDynamicLookupName(DeclBaseName name);
248219

220+
public:
221+
const DependencyRecorder &getRecorder() const { return parent; }
222+
bool empty() const { return scratch.empty(); }
223+
};
224+
225+
/// A \c DependencyCollector is an aggregator of named references discovered in a
226+
/// particular \c DependencyScope during the course of request evaluation.
227+
struct DependencyRecorder {
228+
friend DependencyCollector;
229+
private:
230+
/// A stack of dependency sources in the order they were evaluated.
231+
llvm::SmallVector<evaluator::DependencySource, 8> dependencySources;
232+
233+
public:
234+
enum class Mode {
235+
// Enables the current "status quo" behavior of the dependency collector.
236+
//
237+
// By default, the dependency collector moves to register dependencies in
238+
// the referenced name trackers at the top of the active dependency stack.
239+
StatusQuo,
240+
// Enables an experimental mode to only register private dependencies.
241+
//
242+
// This mode restricts the dependency collector to ignore changes of
243+
// scope. This has practical effect of charging all unqualified lookups to
244+
// the primary file being acted upon instead of to the destination file.
245+
ExperimentalPrivateDependencies,
246+
};
247+
Mode mode;
248+
llvm::DenseMap<AnyRequest, llvm::DenseSet<DependencyCollector::Reference,
249+
DependencyCollector::Reference::Info>>
250+
requestReferences;
251+
bool isRecording;
252+
253+
explicit DependencyRecorder(Mode mode)
254+
: mode{mode}, requestReferences{}, isRecording{false} {};
255+
256+
private:
257+
void realize(const DependencyCollector::Reference &ref);
258+
259+
public:
260+
void replay(const swift::ActiveRequest &req);
261+
void record(const llvm::SetVector<swift::ActiveRequest> &stack,
262+
llvm::function_ref<void(DependencyCollector &)> rec);
263+
249264
public:
250265
/// Returns the scope of the current active scope.
251266
///
@@ -275,14 +290,14 @@ struct DependencyCollector {
275290
/// dependency source stack. It is specialized to be zero-cost for
276291
/// requests that are not dependency sources.
277292
template <typename Request, typename = detail::void_t<>> struct StackRAII {
278-
StackRAII(DependencyCollector &DC, const Request &Req) {}
293+
StackRAII(DependencyRecorder &DR, const Request &Req) {}
279294
};
280295

281296
template <typename Request>
282297
struct StackRAII<Request,
283298
typename std::enable_if<Request::isDependencySource>::type> {
284-
NullablePtr<DependencyCollector> Coll;
285-
StackRAII(DependencyCollector &coll, const Request &Req) {
299+
NullablePtr<DependencyRecorder> Coll;
300+
StackRAII(DependencyRecorder &coll, const Request &Req) {
286301
auto Source = Req.readDependencySource(coll);
287302
// If there is no source to introduce, bail. This can occur if
288303
// a request originates in the context of a module.

include/swift/AST/IRGenRequests.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ class IRGenSourceFileRequest
200200
public:
201201
// Incremental dependencies.
202202
evaluator::DependencySource
203-
readDependencySource(const evaluator::DependencyCollector &) const;
203+
readDependencySource(const evaluator::DependencyRecorder &) const;
204204
};
205205

206206
class IRGenWholeModuleRequest

include/swift/AST/NameLookupRequests.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ class InheritedProtocolsRequest
189189
public:
190190
// Incremental dependencies
191191
evaluator::DependencySource
192-
readDependencySource(const evaluator::DependencyCollector &e) const;
192+
readDependencySource(const evaluator::DependencyRecorder &e) const;
193193
void writeDependencySink(evaluator::DependencyCollector &tracker,
194194
ArrayRef<ProtocolDecl *> result) const;
195195
};
@@ -330,7 +330,7 @@ class GetDestructorRequest
330330
public:
331331
// Incremental dependencies.
332332
evaluator::DependencySource
333-
readDependencySource(const evaluator::DependencyCollector &) const;
333+
readDependencySource(const evaluator::DependencyRecorder &) const;
334334
};
335335

336336
class GenericParamListRequest :
@@ -434,7 +434,7 @@ class UnqualifiedLookupRequest
434434
public:
435435
// Incremental dependencies
436436
evaluator::DependencySource
437-
readDependencySource(const evaluator::DependencyCollector &) const;
437+
readDependencySource(const evaluator::DependencyRecorder &) const;
438438
void writeDependencySink(evaluator::DependencyCollector &tracker,
439439
LookupResult res) const;
440440
};
@@ -506,7 +506,7 @@ class ModuleQualifiedLookupRequest
506506
public:
507507
// Incremental dependencies
508508
evaluator::DependencySource
509-
readDependencySource(const evaluator::DependencyCollector &) const;
509+
readDependencySource(const evaluator::DependencyRecorder &) const;
510510
void writeDependencySink(evaluator::DependencyCollector &tracker,
511511
QualifiedLookupResult lookupResult) const;
512512
};
@@ -533,7 +533,7 @@ class QualifiedLookupRequest
533533
public:
534534
// Incremental dependencies.
535535
evaluator::DependencySource
536-
readDependencySource(const evaluator::DependencyCollector &) const;
536+
readDependencySource(const evaluator::DependencyRecorder &) const;
537537
};
538538

539539
/// The input type for a direct lookup request.

include/swift/AST/ParseRequests.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class ParseSourceFileRequest
103103

104104
public:
105105
evaluator::DependencySource
106-
readDependencySource(const evaluator::DependencyCollector &) const;
106+
readDependencySource(const evaluator::DependencyRecorder &) const;
107107
};
108108

109109
void simple_display(llvm::raw_ostream &out,
@@ -125,7 +125,7 @@ class CodeCompletionSecondPassRequest
125125

126126
public:
127127
evaluator::DependencySource
128-
readDependencySource(const evaluator::DependencyCollector &) const;
128+
readDependencySource(const evaluator::DependencyRecorder &) const;
129129
};
130130

131131
/// The zone number for the parser.

include/swift/AST/SILGenRequests.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class SILGenerationRequest
104104
public:
105105
// Incremental dependencies.
106106
evaluator::DependencySource
107-
readDependencySource(const evaluator::DependencyCollector &) const;
107+
readDependencySource(const evaluator::DependencyRecorder &) const;
108108
};
109109

110110
/// Parses a .sil file into a SILModule.

include/swift/AST/SimpleRequest.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ SourceLoc extractNearestSourceLoc(const std::tuple<First, Rest...> &value) {
232232
/// the 3 caching kinds defined above.
233233
/// \code
234234
/// evaluator::DependencySource
235-
/// readDependencySource(const evaluator::DependencyCollector &) const;
235+
/// readDependencySource(const evaluator::DependencyRecorder &) const;
236236
/// \endcode
237237
///
238238
/// Requests that define dependency sinks should instead override

include/swift/AST/TypeCheckRequests.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class SuperclassTypeRequest
123123
public:
124124
// Incremental dependencies
125125
evaluator::DependencySource
126-
readDependencySource(const evaluator::DependencyCollector &e) const;
126+
readDependencySource(const evaluator::DependencyRecorder &e) const;
127127
void writeDependencySink(evaluator::DependencyCollector &tracker,
128128
Type t) const;
129129
};
@@ -893,7 +893,7 @@ class TypeCheckFunctionBodyUntilRequest :
893893
public:
894894
// Incremental dependencies.
895895
evaluator::DependencySource
896-
readDependencySource(const evaluator::DependencyCollector &) const;
896+
readDependencySource(const evaluator::DependencyRecorder &) const;
897897
};
898898

899899
/// Request to obtain a list of stored properties in a nominal type.
@@ -2034,7 +2034,7 @@ class TypeCheckSourceFileRequest
20342034
public:
20352035
// Incremental dependencies.
20362036
evaluator::DependencySource
2037-
readDependencySource(const evaluator::DependencyCollector &) const;
2037+
readDependencySource(const evaluator::DependencyRecorder &) const;
20382038
};
20392039

20402040
/// Computes whether the specified type or a super-class/super-protocol has the
@@ -2277,7 +2277,7 @@ class LookupAllConformancesInContextRequest
22772277
public:
22782278
// Incremental dependencies
22792279
evaluator::DependencySource
2280-
readDependencySource(const evaluator::DependencyCollector &eval) const;
2280+
readDependencySource(const evaluator::DependencyRecorder &eval) const;
22812281
void writeDependencySink(evaluator::DependencyCollector &tracker,
22822282
ProtocolConformanceLookupResult r) const;
22832283
};
@@ -2305,7 +2305,7 @@ class CheckRedeclarationRequest
23052305

23062306
public:
23072307
evaluator::DependencySource
2308-
readDependencySource(const evaluator::DependencyCollector &eval) const;
2308+
readDependencySource(const evaluator::DependencyRecorder &eval) const;
23092309
void writeDependencySink(evaluator::DependencyCollector &tracker,
23102310
evaluator::SideEffect) const;
23112311
};

0 commit comments

Comments
 (0)