Skip to content

Commit ad78812

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 ad78812

File tree

13 files changed

+1513
-239
lines changed

13 files changed

+1513
-239
lines changed

include/swift-c/DependencyScan/DependencyScan.h

Lines changed: 195 additions & 4 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,52 @@ 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+
461+
/// Enum types for output types for cache key computation.
462+
/// List should contain all the output file types, including supplemententary
463+
/// outputs, except diagnostics outputs, which are covered by cached diagnostic
464+
/// entry.
465+
typedef enum {
466+
SWIFTSCAN_OUTPUT_TYPE_OBJECT = 0,
467+
SWIFTSCAN_OUTPUT_TYPE_SWIFTMODULE = 1,
468+
SWIFTSCAN_OUTPUT_TYPE_SWIFTINTERFACE = 2,
469+
SWIFTSCAN_OUTPUT_TYPE_SWIFTPRIVATEINTERFACE = 3,
470+
SWIFTSCAN_OUTPUT_TYPE_CLANG_MODULE = 4,
471+
SWIFTSCAN_OUTPUT_TYPE_CLANG_PCH = 5,
472+
SWIFTSCAN_OUTPUT_TYPE_CLANG_HEADER = 6,
473+
SWIFTSCAN_OUTPUT_TYPE_SWIFT_SOURCE_INFO = 7,
474+
SWIFTSCAN_OUTPUT_TYPE_SWIFT_MODULE_DOC = 8,
475+
SWIFTSCAN_OUTPUT_TYPE_DEPENDENCIES = 9,
476+
SWIFTSCAN_OUTPUT_TYPE_SWIFT_DEPS = 10,
477+
SWIFTSCAN_OUTPUT_TYPE_MODULE_TRACE = 11,
478+
SWIFTSCAN_OUTPUT_TYPE_TBD = 12,
479+
SWIFTSCAN_OUTPUT_TYPE_SWIFT_MODULE_SUMMARY = 13,
480+
SWIFTSCAN_OUTPUT_TYPE_SWIFT_ABI_DESCRIPTOR = 14,
481+
SWIFTSCAN_OUTPUT_TYPE_SWIFT_API_DESCRIPTOR = 15,
482+
SWIFTSCAN_OUTPUT_TYPE_CONST_VALUE = 16,
483+
SWIFTSCAN_OUTPUT_TYPE_MODULE_SEMANTIC_INFO = 17,
484+
SWIFTSCAN_OUTPUT_TYPE_YAML_OPT_RECORD = 18,
485+
SWIFTSCAN_OUTPUT_TYPE_BITSTREAM_OPT_RECORD = 19,
486+
SWIFTSCAN_OUTPUT_TYPE_CACHED_DIAGNOSTICS = 20,
487+
SWIFTSCAN_OUTPUT_TYPE_LAST = SWIFTSCAN_OUTPUT_TYPE_CACHED_DIAGNOSTICS
488+
} swiftscan_output_kind_t;
489+
444490
/// Create a \c CASOptions for creating CAS inside scanner specified.
445491
SWIFTSCAN_PUBLIC swiftscan_cas_options_t swiftscan_cas_options_create(void);
446492

@@ -472,16 +518,16 @@ swiftscan_cas_options_set_option(swiftscan_cas_options_t options,
472518
SWIFTSCAN_PUBLIC swiftscan_cas_t swiftscan_cas_create_from_options(
473519
swiftscan_cas_options_t options, swiftscan_string_ref_t *error);
474520

475-
/// Dispose the \c cas instance.
476-
SWIFTSCAN_PUBLIC void swiftscan_cas_dispose(swiftscan_cas_t cas);
477-
478521
/// Store content into CAS. Return \c CASID as string. Return NULL on error.
479522
/// If error happens, the error message is returned via `error` parameter, and
480523
/// caller needs to free the error message via `swiftscan_string_dispose`.
481524
SWIFTSCAN_PUBLIC swiftscan_string_ref_t
482525
swiftscan_cas_store(swiftscan_cas_t cas, uint8_t *data, unsigned size,
483526
swiftscan_string_ref_t *error);
484527

528+
/// Dispose the \c cas instance.
529+
SWIFTSCAN_PUBLIC void swiftscan_cas_dispose(swiftscan_cas_t cas);
530+
485531
/// Compute \c CacheKey for the outputs of a primary input file from a compiler
486532
/// invocation with command-line \c argc and \c argv. When primary input file
487533
/// is not available for compilation, e.g., using WMO, primary file is the first
@@ -492,6 +538,151 @@ SWIFTSCAN_PUBLIC swiftscan_string_ref_t
492538
swiftscan_cache_compute_key(swiftscan_cas_t cas, int argc, const char **argv,
493539
const char *input, swiftscan_string_ref_t *error);
494540

541+
/// Query the result of the compilation using the output cache key. \c globally
542+
/// suggests if the lookup should check remote cache if such operation exists.
543+
/// Returns the cached compilation of the result if found, or nullptr if output
544+
/// is not found or an error occurs. When an error occurs, the error message is
545+
/// returned via \c error parameter and its caller needs to free the message
546+
/// using `swiftscan_string_dispose`. The returned cached compilation needs to
547+
/// be freed via `swiftscan_cached_compilation_dispose`.
548+
SWIFTSCAN_PUBLIC swiftscan_cached_compilation_t
549+
swiftscan_cache_query(swiftscan_cas_t cas, const char *key, bool globally,
550+
swiftscan_string_ref_t *error);
551+
552+
/// Async version of `swiftscan_cache_query` where result is returned via
553+
/// callback. Both cache_result enum and cached compilation will be provided to
554+
/// callback. \c ctx is an opaque value that passed to the callback and \c
555+
/// swiftscan_cache_cancellation_token_t will return an token that can be used
556+
/// to cancel the async operation. The token needs to be freed by caller using
557+
/// `swiftscan_cache_cancellation_token_dispose`. If no token is needed, nullptr
558+
/// can be passed and no token will be returned.
559+
SWIFTSCAN_PUBLIC void swiftscan_cache_query_async(
560+
swiftscan_cas_t cas, const char *key, bool globally, void *ctx,
561+
void (*callback)(void *ctx, swiftscan_cached_compilation_t,
562+
swiftscan_string_ref_t error),
563+
swiftscan_cache_cancellation_token_t *);
564+
565+
/// Query the number of outputs from a cached compilation.
566+
SWIFTSCAN_PUBLIC unsigned swiftscan_cached_compilation_get_num_outputs(
567+
swiftscan_cached_compilation_t);
568+
569+
/// Get the output kind for the given index in the cached compilation.
570+
SWIFTSCAN_PUBLIC swiftscan_output_kind_t
571+
swiftscan_cached_compilation_get_output_kind(swiftscan_cached_compilation_t,
572+
unsigned idx);
573+
574+
/// Get the cached output for the given index in the cached compilation.
575+
SWIFTSCAN_PUBLIC swiftscan_cached_output_t
576+
swiftscan_cached_compilation_get_output(swiftscan_cached_compilation_t,
577+
unsigned idx);
578+
579+
/// Check if the requested cached compilation is uncacheable. That means the
580+
/// compiler decides to skip caching its output even the compilation is
581+
/// successful.
582+
SWIFTSCAN_PUBLIC bool
583+
swiftscan_cached_compilation_is_uncacheable(swiftscan_cached_compilation_t);
584+
585+
/// Make the cache compilation available globally. \c callback will be called
586+
/// on completion.
587+
/// \c swiftscan_cache_cancellation_token_t will return an token that can be
588+
/// used to cancel the async operation. The token needs to be freed by caller
589+
/// using `swiftscan_cache_cancellation_token_dispose`. If no token is needed,
590+
/// nullptr can be passed and no token will be returned.
591+
SWIFTSCAN_PUBLIC void swiftscan_cached_compilation_make_global_async(
592+
swiftscan_cas_t cas, swiftscan_cached_compilation_t, void *ctx,
593+
void (*callback)(void *ctx, swiftscan_string_ref_t error),
594+
swiftscan_cache_cancellation_token_t *);
595+
596+
/// Dispose a cached compilation.
597+
SWIFTSCAN_PUBLIC
598+
void swiftscan_cached_compilation_dispose(swiftscan_cached_compilation_t);
599+
600+
/// Download and materialize the cached output if needed from a remote CAS.
601+
/// Return true if load is successful, else false if not found or error. If
602+
/// error, the error message is returned via \c error parameter and its caller
603+
/// needs to free the message using `swiftscan_string_dispose`.
604+
SWIFTSCAN_PUBLIC bool
605+
swiftscan_cached_output_load(swiftscan_cached_output_t,
606+
swiftscan_string_ref_t *error);
607+
608+
/// Async version of `swiftscan_cached_output_load` where result is
609+
/// returned via callback. \c ctx is an opaque value that passed to the callback
610+
/// and \c swiftscan_cache_cancellation_token_t will return an token that can be
611+
/// used to cancel the async operation. The token needs to be freed by caller
612+
/// using `swiftscan_cache_cancellation_token_dispose`. If no token is needed,
613+
/// nullptr can be passed and no token will be returned.
614+
SWIFTSCAN_PUBLIC void swiftscan_cached_output_load_async(
615+
swiftscan_cached_output_t, void *ctx,
616+
void (*callback)(void *ctx, bool success, swiftscan_string_ref_t error),
617+
swiftscan_cache_cancellation_token_t *);
618+
619+
/// Check if cached output is materialized locally and can be accessed
620+
/// without downloading.
621+
SWIFTSCAN_PUBLIC bool
622+
swiftscan_cached_output_is_materialized(swiftscan_cached_output_t);
623+
624+
/// Return the casid for the cached output as \c swiftscan_string_ref_t and the
625+
/// returned string needs to be freed using `swiftscan_string_dispose`. CASID
626+
/// can be requested before load/materializaing.
627+
SWIFTSCAN_PUBLIC swiftscan_string_ref_t
628+
swiftscan_cached_output_get_casid(swiftscan_cached_output_t);
629+
630+
/// Dispose a cached output.
631+
SWIFTSCAN_PUBLIC
632+
void swiftscan_cached_output_dispose(swiftscan_cached_output_t);
633+
634+
/// Cancel the async cache action that is associated with token.
635+
SWIFTSCAN_PUBLIC void
636+
swiftscan_cache_action_cancel(swiftscan_cache_cancellation_token_t);
637+
638+
/// Dispose the cancellation token.
639+
SWIFTSCAN_PUBLIC void swiftscan_cache_cancellation_token_dispose(
640+
swiftscan_cache_cancellation_token_t);
641+
642+
/// Create a swift cached compilation replay instance with its command-line
643+
/// invocation. Return nullptr when errors occurs and the error message is
644+
/// returned via \c error parameter and its caller needs to free the message
645+
/// using `swiftscan_string_dispose`.
646+
SWIFTSCAN_PUBLIC swiftscan_cache_replay_instance_t
647+
swiftscan_cache_replay_instance_create(int argc, const char **argv,
648+
swiftscan_string_ref_t *error);
649+
650+
/// Dispose swift cached compilation replay instance.
651+
SWIFTSCAN_PUBLIC void
652+
swiftscan_cache_replay_instance_dispose(swiftscan_cache_replay_instance_t);
653+
654+
/// Replay the cached compilation using cached compliation replay instance.
655+
/// Returns replay result or nullptr if output not found or error occurs. If
656+
/// error, the error message is returned via \c error parameter and its caller
657+
/// needs to free the message using `swiftscan_string_dispose`.
658+
SWIFTSCAN_PUBLIC swiftscan_cache_replay_result_t
659+
swiftscan_cache_replay_compilation(swiftscan_cache_replay_instance_t,
660+
swiftscan_cached_compilation_t,
661+
swiftscan_string_ref_t *error);
662+
663+
/// Get return code from replay which should matches the returnCode from the
664+
/// corresponding swift-frontend invocation. When the replay is sucessful, this
665+
/// should always be 0.
666+
SWIFTSCAN_PUBLIC
667+
int swiftscan_cache_replay_result_get_return_code(
668+
swiftscan_cache_replay_result_t);
669+
670+
/// Get stdout from cached replay result. The returning swiftscan_string_ref_t
671+
/// is owned by replay result and should not be disposed.
672+
SWIFTSCAN_PUBLIC
673+
swiftscan_string_ref_t
674+
swiftscan_cache_replay_result_get_stdout(swiftscan_cache_replay_result_t);
675+
676+
/// Get stderr from cached replay result. The returning swiftscan_string_ref_t
677+
/// is owned by replay result and should not be disposed.
678+
SWIFTSCAN_PUBLIC
679+
swiftscan_string_ref_t
680+
swiftscan_cache_replay_result_get_stderr(swiftscan_cache_replay_result_t);
681+
682+
/// Dispose a cached replay result.
683+
SWIFTSCAN_PUBLIC
684+
void swiftscan_cache_replay_result_dispose(swiftscan_cache_replay_result_t);
685+
495686
//===----------------------------------------------------------------------===//
496687

497688
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)