Skip to content

Commit be8f4cc

Browse files
authored
Merge pull request swiftlang#35206 from slavapestov/per-request-caches
Per-request caches and dependency maps
2 parents b1564a6 + 0bfa868 commit be8f4cc

File tree

8 files changed

+587
-121
lines changed

8 files changed

+587
-121
lines changed

include/swift/AST/Evaluator.h

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "swift/AST/AnyRequest.h"
2222
#include "swift/AST/EvaluatorDependencies.h"
23+
#include "swift/AST/RequestCache.h"
2324
#include "swift/Basic/AnyValue.h"
2425
#include "swift/Basic/Debug.h"
2526
#include "swift/Basic/LangOptions.h"
@@ -211,7 +212,7 @@ class Evaluator {
211212
llvm::SetVector<ActiveRequest> activeRequests;
212213

213214
/// A cache that stores the results of requests.
214-
llvm::DenseMap<AnyRequest, AnyValue> cache;
215+
evaluator::RequestCache cache;
215216

216217
/// Track the dependencies of each request.
217218
///
@@ -314,14 +315,15 @@ class Evaluator {
314315
typename std::enable_if<!Request::hasExternalCache>::type* = nullptr>
315316
void cacheOutput(const Request &request,
316317
typename Request::OutputType &&output) {
317-
cache.insert({AnyRequest(request), std::move(output)});
318+
cache.insert<Request>(request, std::move(output));
318319
}
319320

320321
/// Do not introduce new callers of this function.
321322
template<typename Request,
322323
typename std::enable_if<!Request::hasExternalCache>::type* = nullptr>
323324
void clearCachedOutput(const Request &request) {
324-
cache.erase(AnyRequest(request));
325+
cache.erase<Request>(request);
326+
recorder.clearRequest<Request>(request);
325327
}
326328

327329
/// Clear the cache stored within this evaluator.
@@ -372,21 +374,21 @@ class Evaluator {
372374
FrontendStatsTracer statsTracer = make_tracer(stats, request);
373375
if (stats) reportEvaluatedRequest(*stats, request);
374376

375-
recorder.beginRequest(activeReq);
377+
recorder.beginRequest<Request>();
376378

377-
auto &&r = getRequestFunction<Request>()(request, *this);
379+
auto &&result = getRequestFunction<Request>()(request, *this);
378380

379-
recorder.endRequest(activeReq);
381+
recorder.endRequest<Request>(request);
380382

381383
handleDependencySourceRequest<Request>(request);
382-
handleDependencySinkRequest<Request>(request, r);
384+
handleDependencySinkRequest<Request>(request, result);
383385

384386
// Make sure we remove this from the set of active requests once we're
385387
// done.
386388
assert(activeRequests.back() == activeReq);
387389
activeRequests.pop_back();
388390

389-
return std::move(r);
391+
return std::move(result);
390392
}
391393

392394
/// Get the result of a request, consulting an external cache
@@ -398,7 +400,7 @@ class Evaluator {
398400
getResultCached(const Request &request) {
399401
// If there is a cached result, return it.
400402
if (auto cached = request.getCachedResult()) {
401-
recorder.replayCachedRequest(ActiveRequest(request));
403+
recorder.replayCachedRequest(request);
402404
handleDependencySinkRequest<Request>(request, *cached);
403405
return *cached;
404406
}
@@ -424,12 +426,12 @@ class Evaluator {
424426
llvm::Expected<typename Request::OutputType>
425427
getResultCached(const Request &request) {
426428
// If we already have an entry for this request in the cache, return it.
427-
auto known = cache.find_as(request);
428-
if (known != cache.end()) {
429-
auto r = known->second.template castTo<typename Request::OutputType>();
430-
recorder.replayCachedRequest(ActiveRequest(request));
431-
handleDependencySinkRequest<Request>(request, r);
432-
return r;
429+
auto known = cache.find_as<Request>(request);
430+
if (known != cache.end<Request>()) {
431+
auto result = known->second;
432+
recorder.replayCachedRequest(request);
433+
handleDependencySinkRequest<Request>(request, result);
434+
return result;
433435
}
434436

435437
// Compute the result.
@@ -438,7 +440,7 @@ class Evaluator {
438440
return result;
439441

440442
// Cache the result.
441-
cache.insert({AnyRequest(request), *result});
443+
cache.insert<Request>(request, *result);
442444
return result;
443445
}
444446

@@ -465,7 +467,7 @@ class Evaluator {
465467
void handleDependencySourceRequest(const Request &r) {
466468
auto source = r.readDependencySource(recorder);
467469
if (!source.isNull() && source.get()->isPrimary()) {
468-
recorder.handleDependencySourceRequest(ActiveRequest(r), source.get());
470+
recorder.handleDependencySourceRequest(r, source.get());
469471
}
470472
}
471473

include/swift/AST/EvaluatorDependencies.h

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "swift/AST/AnyRequest.h"
2222
#include "swift/AST/DependencyCollector.h"
23+
#include "swift/AST/RequestCache.h"
2324
#include "swift/Basic/NullablePtr.h"
2425
#include "llvm/ADT/DenseMap.h"
2526
#include "llvm/ADT/DenseSet.h"
@@ -63,15 +64,14 @@ struct DependencyRecorder {
6364
/// downstream request as well. Note that uncached requests don't appear as
6465
/// keys in this map; their references are charged to the innermost cached
6566
/// active request.
66-
llvm::DenseMap<AnyRequest, std::vector<DependencyCollector::Reference>>
67-
requestReferences;
67+
RequestReferences requestReferences;
6868

6969
/// Stack of references from each cached active request. When evaluating a
7070
/// dependency sink request, we update the innermost set of references.
7171
/// Upon completion of a request, we union the completed request's references
7272
/// with the next innermost active request.
73-
std::vector<llvm::DenseSet<DependencyCollector::Reference,
74-
DependencyCollector::Reference::Info>>
73+
std::vector<llvm::SmallDenseSet<DependencyCollector::Reference, 2,
74+
DependencyCollector::Reference::Info>>
7575
activeRequestReferences;
7676

7777
#ifndef NDEBUG
@@ -83,22 +83,30 @@ struct DependencyRecorder {
8383

8484
public:
8585
/// Push a new empty set onto the activeRequestReferences stack.
86-
void beginRequest(const swift::ActiveRequest &req);
86+
template<typename Request>
87+
void beginRequest();
8788

8889
/// Pop the activeRequestReferences stack, and insert recorded references
8990
/// into the requestReferences map, as well as the next innermost entry in
9091
/// activeRequestReferences.
91-
void endRequest(const swift::ActiveRequest &req);
92+
template<typename Request>
93+
void endRequest(const Request &req);
9294

9395
/// When replaying a request whose value has already been cached, we need
9496
/// to update the innermost set in the activeRequestReferences stack.
95-
void replayCachedRequest(const swift::ActiveRequest &req);
97+
template<typename Request>
98+
void replayCachedRequest(const Request &req);
9699

97100
/// Upon completion of a dependency source request, we update the
98101
/// fileReferences map.
99-
void handleDependencySourceRequest(const swift::ActiveRequest &req,
102+
template<typename Request>
103+
void handleDependencySourceRequest(const Request &req,
100104
SourceFile *source);
101105

106+
/// Clear the recorded dependencies of a request, if any.
107+
template<typename Request>
108+
void clearRequest(const Request &req);
109+
102110
private:
103111
/// Add an entry to the innermost set on the activeRequestReferences stack.
104112
/// Called from the DependencyCollector.
@@ -120,6 +128,76 @@ struct DependencyRecorder {
120128
void enumerateReferencesInFile(const SourceFile *SF,
121129
ReferenceEnumerator f) const ;
122130
};
131+
132+
template<typename Request>
133+
void evaluator::DependencyRecorder::beginRequest() {
134+
if (!Request::isEverCached && !Request::isDependencySource)
135+
return;
136+
137+
activeRequestReferences.push_back({});
138+
}
139+
140+
template<typename Request>
141+
void evaluator::DependencyRecorder::endRequest(const Request &req) {
142+
if (!Request::isEverCached && !Request::isDependencySource)
143+
return;
144+
145+
// Grab all the dependencies we've recorded so far, and pop
146+
// the stack.
147+
auto recorded = std::move(activeRequestReferences.back());
148+
activeRequestReferences.pop_back();
149+
150+
// If we didn't record anything, there is nothing to do.
151+
if (recorded.empty())
152+
return;
153+
154+
// Convert the set of dependencies into a vector.
155+
std::vector<DependencyCollector::Reference>
156+
vec(recorded.begin(), recorded.end());
157+
158+
// The recorded dependencies bubble up to the parent request.
159+
if (!activeRequestReferences.empty()) {
160+
activeRequestReferences.back().insert(vec.begin(),
161+
vec.end());
162+
}
163+
164+
// Finally, record the dependencies so we can replay them
165+
// later when the request is re-evaluated.
166+
requestReferences.insert<Request>(std::move(req), std::move(vec));
167+
}
168+
169+
template<typename Request>
170+
void evaluator::DependencyRecorder::replayCachedRequest(const Request &req) {
171+
assert(req.isCached());
172+
173+
if (activeRequestReferences.empty())
174+
return;
175+
176+
auto found = requestReferences.find_as<Request>(req);
177+
if (found == requestReferences.end<Request>())
178+
return;
179+
180+
activeRequestReferences.back().insert(found->second.begin(),
181+
found->second.end());
182+
}
183+
184+
template<typename Request>
185+
void evaluator::DependencyRecorder::handleDependencySourceRequest(
186+
const Request &req,
187+
SourceFile *source) {
188+
auto found = requestReferences.find_as<Request>(req);
189+
if (found != requestReferences.end<Request>()) {
190+
fileReferences[source].insert(found->second.begin(),
191+
found->second.end());
192+
}
193+
}
194+
195+
template<typename Request>
196+
void evaluator::DependencyRecorder::clearRequest(
197+
const Request &req) {
198+
requestReferences.erase(req);
199+
}
200+
123201
} // end namespace evaluator
124202

125203
} // end namespace swift

0 commit comments

Comments
 (0)