Skip to content

Commit 034c15c

Browse files
[Caching] Add new CacheReplay APIs to libSwiftScan
Add new APIs libSwiftScan that can be used for cache query and cache replay. This enables swift-driver or build system to query the cache and replay the compilation results without invocation swift-frontend for better scheduling.
1 parent 30cfa3d commit 034c15c

File tree

13 files changed

+1435
-243
lines changed

13 files changed

+1435
-243
lines changed

include/swift-c/DependencyScan/DependencyScan.h

Lines changed: 162 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
/// SWIFTSCAN_VERSION_MINOR should increase when there are API additions.
2626
/// SWIFTSCAN_VERSION_MAJOR is intended for "major" source/ABI breaking changes.
2727
#define SWIFTSCAN_VERSION_MAJOR 0
28-
#define SWIFTSCAN_VERSION_MINOR 5
28+
#define SWIFTSCAN_VERSION_MINOR 6
2929

3030
SWIFTSCAN_BEGIN_DECLS
3131

@@ -441,6 +441,23 @@ typedef struct swiftscan_cas_options_s *swiftscan_cas_options_t;
441441
/// ActionCache.
442442
typedef struct swiftscan_cas_s *swiftscan_cas_t;
443443

444+
/// Opaque container for a cached compilation.
445+
typedef struct swiftscan_cached_compilation_s *swiftscan_cached_compilation_t;
446+
447+
/// Opaque container for a cached compilation output.
448+
typedef struct swiftscan_cached_output_s *swiftscan_cached_output_t;
449+
450+
/// Opaque type for a cache replay instance.
451+
typedef struct swiftscan_cache_replay_instance_s
452+
*swiftscan_cache_replay_instance_t;
453+
454+
/// Opaque container for a cached compilation replay result.
455+
typedef struct swiftscan_cache_replay_result_s *swiftscan_cache_replay_result_t;
456+
457+
/// Opaque type for a cancellation token for async cache operations.
458+
typedef struct swiftscan_cache_cancellation_token_s
459+
*swiftscan_cache_cancellation_token_t;
460+
444461
/// Create a \c CASOptions for creating CAS inside scanner specified.
445462
SWIFTSCAN_PUBLIC swiftscan_cas_options_t swiftscan_cas_options_create(void);
446463

@@ -462,26 +479,26 @@ swiftscan_cas_options_set_plugin_path(swiftscan_cas_options_t options,
462479
/// If error happens, the error message is returned via `error` parameter, and
463480
/// caller needs to free the error message via `swiftscan_string_dispose`.
464481
SWIFTSCAN_PUBLIC bool
465-
swiftscan_cas_options_set_option(swiftscan_cas_options_t options,
466-
const char *name, const char *value,
467-
swiftscan_string_ref_t *error);
482+
swiftscan_cas_options_set_plugin_option(swiftscan_cas_options_t options,
483+
const char *name, const char *value,
484+
swiftscan_string_ref_t *error);
468485

469486
/// Create a \c cas instance from plugin. Return NULL if error.
470487
/// If error happens, the error message is returned via `error` parameter, and
471488
/// caller needs to free the error message via `swiftscan_string_dispose`.
472489
SWIFTSCAN_PUBLIC swiftscan_cas_t swiftscan_cas_create_from_options(
473490
swiftscan_cas_options_t options, swiftscan_string_ref_t *error);
474491

475-
/// Dispose the \c cas instance.
476-
SWIFTSCAN_PUBLIC void swiftscan_cas_dispose(swiftscan_cas_t cas);
477-
478492
/// Store content into CAS. Return \c CASID as string. Return NULL on error.
479493
/// If error happens, the error message is returned via `error` parameter, and
480494
/// caller needs to free the error message via `swiftscan_string_dispose`.
481495
SWIFTSCAN_PUBLIC swiftscan_string_ref_t
482496
swiftscan_cas_store(swiftscan_cas_t cas, uint8_t *data, unsigned size,
483497
swiftscan_string_ref_t *error);
484498

499+
/// Dispose the \c cas instance.
500+
SWIFTSCAN_PUBLIC void swiftscan_cas_dispose(swiftscan_cas_t cas);
501+
485502
/// Compute \c CacheKey for the outputs of a primary input file from a compiler
486503
/// invocation with command-line \c argc and \c argv. When primary input file
487504
/// is not available for compilation, e.g., using WMO, primary file is the first
@@ -492,6 +509,144 @@ SWIFTSCAN_PUBLIC swiftscan_string_ref_t
492509
swiftscan_cache_compute_key(swiftscan_cas_t cas, int argc, const char **argv,
493510
const char *input, swiftscan_string_ref_t *error);
494511

512+
/// Query the result of the compilation using the output cache key. \c globally
513+
/// suggests if the lookup should check remote cache if such operation exists.
514+
/// Returns the cached compilation of the result if found, or nullptr if output
515+
/// is not found or an error occurs. When an error occurs, the error message is
516+
/// returned via \c error parameter and its caller needs to free the message
517+
/// using `swiftscan_string_dispose`. The returned cached compilation needs to
518+
/// be freed via `swiftscan_cached_compilation_dispose`.
519+
SWIFTSCAN_PUBLIC swiftscan_cached_compilation_t
520+
swiftscan_cache_query(swiftscan_cas_t cas, const char *key, bool globally,
521+
swiftscan_string_ref_t *error);
522+
523+
/// Async version of `swiftscan_cache_query` where result is returned via
524+
/// callback. Both cache_result enum and cached compilation will be provided to
525+
/// callback. \c ctx is an opaque value that passed to the callback and \c
526+
/// swiftscan_cache_cancellation_token_t will return an token that can be used
527+
/// to cancel the async operation. The token needs to be freed by caller using
528+
/// `swiftscan_cache_cancellation_token_dispose`. If no token is needed, nullptr
529+
/// can be passed and no token will be returned.
530+
SWIFTSCAN_PUBLIC void swiftscan_cache_query_async(
531+
swiftscan_cas_t cas, const char *key, bool globally, void *ctx,
532+
void (*callback)(void *ctx, swiftscan_cached_compilation_t,
533+
swiftscan_string_ref_t error),
534+
swiftscan_cache_cancellation_token_t *);
535+
536+
/// Query the number of outputs from a cached compilation.
537+
SWIFTSCAN_PUBLIC unsigned swiftscan_cached_compilation_get_num_outputs(
538+
swiftscan_cached_compilation_t);
539+
540+
/// Get the cached output for the given index in the cached compilation.
541+
SWIFTSCAN_PUBLIC swiftscan_cached_output_t
542+
swiftscan_cached_compilation_get_output(swiftscan_cached_compilation_t,
543+
unsigned idx);
544+
545+
/// Check if the requested cached compilation is uncacheable. That means the
546+
/// compiler decides to skip caching its output even the compilation is
547+
/// successful.
548+
SWIFTSCAN_PUBLIC bool
549+
swiftscan_cached_compilation_is_uncacheable(swiftscan_cached_compilation_t);
550+
551+
/// Make the cache compilation available globally. \c callback will be called
552+
/// on completion.
553+
/// \c swiftscan_cache_cancellation_token_t will return an token that can be
554+
/// used to cancel the async operation. The token needs to be freed by caller
555+
/// using `swiftscan_cache_cancellation_token_dispose`. If no token is needed,
556+
/// nullptr can be passed and no token will be returned.
557+
SWIFTSCAN_PUBLIC void swiftscan_cached_compilation_make_global_async(
558+
swiftscan_cached_compilation_t, void *ctx,
559+
void (*callback)(void *ctx, swiftscan_string_ref_t error),
560+
swiftscan_cache_cancellation_token_t *);
561+
562+
/// Dispose a cached compilation.
563+
SWIFTSCAN_PUBLIC
564+
void swiftscan_cached_compilation_dispose(swiftscan_cached_compilation_t);
565+
566+
/// Download and materialize the cached output if needed from a remote CAS.
567+
/// Return true if load is successful, else false if not found or error. If
568+
/// error, the error message is returned via \c error parameter and its caller
569+
/// needs to free the message using `swiftscan_string_dispose`.
570+
SWIFTSCAN_PUBLIC bool
571+
swiftscan_cached_output_load(swiftscan_cached_output_t,
572+
swiftscan_string_ref_t *error);
573+
574+
/// Async version of `swiftscan_cached_output_load` where result is
575+
/// returned via callback. \c ctx is an opaque value that passed to the callback
576+
/// and \c swiftscan_cache_cancellation_token_t will return an token that can be
577+
/// used to cancel the async operation. The token needs to be freed by caller
578+
/// using `swiftscan_cache_cancellation_token_dispose`. If no token is needed,
579+
/// nullptr can be passed and no token will be returned.
580+
SWIFTSCAN_PUBLIC void swiftscan_cached_output_load_async(
581+
swiftscan_cached_output_t, void *ctx,
582+
void (*callback)(void *ctx, bool success, swiftscan_string_ref_t error),
583+
swiftscan_cache_cancellation_token_t *);
584+
585+
/// Check if cached output is materialized locally and can be accessed
586+
/// without downloading.
587+
SWIFTSCAN_PUBLIC bool
588+
swiftscan_cached_output_is_materialized(swiftscan_cached_output_t);
589+
590+
/// Return the casid for the cached output as \c swiftscan_string_ref_t and the
591+
/// returned string needs to be freed using `swiftscan_string_dispose`. CASID
592+
/// can be requested before loading/materializing.
593+
SWIFTSCAN_PUBLIC swiftscan_string_ref_t
594+
swiftscan_cached_output_get_casid(swiftscan_cached_output_t);
595+
596+
/// Get the output name for cached compilation. The
597+
/// returned name needs to be freed by `swiftscan_string_dispose`.
598+
SWIFTSCAN_PUBLIC swiftscan_string_ref_t
599+
swiftscan_cached_output_get_name(swiftscan_cached_output_t);
600+
601+
/// Dispose a cached output.
602+
SWIFTSCAN_PUBLIC
603+
void swiftscan_cached_output_dispose(swiftscan_cached_output_t);
604+
605+
/// Cancel the async cache action that is associated with token.
606+
SWIFTSCAN_PUBLIC void
607+
swiftscan_cache_action_cancel(swiftscan_cache_cancellation_token_t);
608+
609+
/// Dispose the cancellation token.
610+
SWIFTSCAN_PUBLIC void swiftscan_cache_cancellation_token_dispose(
611+
swiftscan_cache_cancellation_token_t);
612+
613+
/// Create a swift cached compilation replay instance with its command-line
614+
/// invocation. Return nullptr when errors occurs and the error message is
615+
/// returned via \c error parameter and its caller needs to free the message
616+
/// using `swiftscan_string_dispose`.
617+
SWIFTSCAN_PUBLIC swiftscan_cache_replay_instance_t
618+
swiftscan_cache_replay_instance_create(int argc, const char **argv,
619+
swiftscan_string_ref_t *error);
620+
621+
/// Dispose swift cached compilation replay instance.
622+
SWIFTSCAN_PUBLIC void
623+
swiftscan_cache_replay_instance_dispose(swiftscan_cache_replay_instance_t);
624+
625+
/// Replay the cached compilation using cached compliation replay instance.
626+
/// Returns replay result or nullptr if output not found or error occurs. If
627+
/// error, the error message is returned via \c error parameter and its caller
628+
/// needs to free the message using `swiftscan_string_dispose`.
629+
SWIFTSCAN_PUBLIC swiftscan_cache_replay_result_t
630+
swiftscan_cache_replay_compilation(swiftscan_cache_replay_instance_t,
631+
swiftscan_cached_compilation_t,
632+
swiftscan_string_ref_t *error);
633+
634+
/// Get stdout from cached replay result. The returning swiftscan_string_ref_t
635+
/// is owned by replay result and should not be disposed.
636+
SWIFTSCAN_PUBLIC
637+
swiftscan_string_ref_t
638+
swiftscan_cache_replay_result_get_stdout(swiftscan_cache_replay_result_t);
639+
640+
/// Get stderr from cached replay result. The returning swiftscan_string_ref_t
641+
/// is owned by replay result and should not be disposed.
642+
SWIFTSCAN_PUBLIC
643+
swiftscan_string_ref_t
644+
swiftscan_cache_replay_result_get_stderr(swiftscan_cache_replay_result_t);
645+
646+
/// Dispose a cached replay result.
647+
SWIFTSCAN_PUBLIC
648+
void swiftscan_cache_replay_result_dispose(swiftscan_cache_replay_result_t);
649+
495650
//===----------------------------------------------------------------------===//
496651

497652
SWIFTSCAN_END_DECLS

include/swift/Frontend/CachingUtils.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,19 @@ std::vector<std::string> remapPathsFromCommandLine(
6363
namespace cas {
6464
class CachedResultLoader {
6565
public:
66-
CachedResultLoader(llvm::cas::ObjectStore &CAS, llvm::cas::ActionCache &Cache,
67-
llvm::cas::ObjectRef CacheKey)
68-
: CAS(CAS), Cache(Cache), CacheKey(CacheKey) {}
66+
CachedResultLoader(llvm::cas::ObjectStore &CAS,
67+
llvm::cas::ObjectRef OutputRef)
68+
: CAS(CAS), OutputRef(OutputRef) {}
6969

7070
using CallbackTy =
7171
llvm::function_ref<llvm::Error(file_types::ID, llvm::cas::ObjectRef)>;
72-
// Replay the cached result, return false if a cache miss happened.
73-
llvm::Expected<bool> replay(CallbackTy Callback);
72+
73+
/// Replay the cached result.
74+
llvm::Error replay(CallbackTy Callback);
7475

7576
private:
7677
llvm::cas::ObjectStore &CAS;
77-
llvm::cas::ActionCache &Cache;
78-
llvm::cas::ObjectRef CacheKey;
78+
llvm::cas::ObjectRef OutputRef;
7979
};
8080
} // end namespace cas
8181
} // end namespace swift

lib/DriverTool/swift_cache_tool_main.cpp

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -369,10 +369,21 @@ int SwiftCacheToolInvocation::validateOutputs() {
369369
report_fatal_error(DB.takeError());
370370

371371
auto &CAS = *DB->first;
372+
auto &Cache = *DB->second;
372373

373374
PrintingDiagnosticConsumer PDC;
374375
Instance.getDiags().addConsumer(PDC);
375376

377+
auto lookupFailed = [&](StringRef Key) {
378+
llvm::errs() << "failed to find output for cache key " << Key << "\n";
379+
return true;
380+
};
381+
auto lookupError = [&](llvm::Error Err, StringRef Key) {
382+
llvm::errs() << "failed to find output for cache key " << Key << ": "
383+
<< toString(std::move(Err)) << "\n";
384+
return true;
385+
};
386+
376387
auto validateCacheKeysFromFile = [&](const std::string &Path) {
377388
auto Keys = readOutputEntriesFromFile(Path);
378389
if (!Keys) {
@@ -391,28 +402,30 @@ int SwiftCacheToolInvocation::validateOutputs() {
391402
return true;
392403
}
393404
auto Ref = CAS.getReference(*ID);
394-
if (!Ref) {
395-
llvm::errs() << "failed to find output for cache key " << Key
396-
<< "\n";
397-
return true;
398-
}
399-
cas::CachedResultLoader Loader(*DB->first, *DB->second, *Ref);
400-
auto Result = Loader.replay(
405+
if (!Ref)
406+
return lookupFailed(*Key);
407+
auto KeyID = CAS.getID(*Ref);
408+
auto Lookup = Cache.get(KeyID);
409+
if (!Lookup)
410+
return lookupError(Lookup.takeError(), *Key);
411+
412+
if (!*Lookup)
413+
return lookupFailed(*Key);
414+
415+
auto OutputRef = CAS.getReference(**Lookup);
416+
if (!OutputRef)
417+
return lookupFailed(*Key);
418+
419+
cas::CachedResultLoader Loader(CAS, *OutputRef);
420+
if (auto Err = Loader.replay(
401421
[&](file_types::ID Kind, ObjectRef Ref) -> llvm::Error {
402422
auto Proxy = CAS.getProxy(Ref);
403423
if (!Proxy)
404424
return Proxy.takeError();
405425
return llvm::Error::success();
406-
});
407-
408-
if (!Result) {
426+
})) {
409427
llvm::errs() << "failed to find output for cache key " << *Key
410-
<< ": " << toString(Result.takeError()) << "\n";
411-
return true;
412-
}
413-
if (!*Result) {
414-
llvm::errs() << "failed to load output for cache key " << *Key
415-
<< "\n";
428+
<< ": " << toString(std::move(Err)) << "\n";
416429
return true;
417430
}
418431
continue;

0 commit comments

Comments
 (0)