[Caching] Encoding cache key for input file with index instead of path

Avoid path encoding difference (for example, real_path vs. path from
symlink) by eliminating the path from cache key. Cache key is now
encoded with the index of the input file from all the input files from
the command-line, reguardless if those inputs will produce output or
not. This is to ensure stable ordering even the batching is different.

Add a new cache computation API that is preferred for using input index
directly. Old API for cache key is deprecated but still updated to
fallback to real_path comparsion if needed.

As a result of swift scan API change, rename the feature in JSON file to
avoid version confusion between swift-driver and libSwiftScan.

rdar://119387650
This commit is contained in:
Steven Wu
2023-12-11 12:05:11 -08:00
parent 443cac20fb
commit 76bde39ee7
16 changed files with 246 additions and 87 deletions

View File

@@ -25,7 +25,7 @@
/// SWIFTSCAN_VERSION_MINOR should increase when there are API additions. /// SWIFTSCAN_VERSION_MINOR should increase when there are API additions.
/// SWIFTSCAN_VERSION_MAJOR is intended for "major" source/ABI breaking changes. /// SWIFTSCAN_VERSION_MAJOR is intended for "major" source/ABI breaking changes.
#define SWIFTSCAN_VERSION_MAJOR 0 #define SWIFTSCAN_VERSION_MAJOR 0
#define SWIFTSCAN_VERSION_MINOR 6 #define SWIFTSCAN_VERSION_MINOR 7
SWIFTSCAN_BEGIN_DECLS SWIFTSCAN_BEGIN_DECLS
@@ -505,10 +505,26 @@ SWIFTSCAN_PUBLIC void swiftscan_cas_dispose(swiftscan_cas_t cas);
/// swift input on the command-line by convention. Return \c CacheKey as string. /// swift input on the command-line by convention. Return \c CacheKey as string.
/// If error happens, the error message is returned via `error` parameter, and /// If error happens, the error message is returned via `error` parameter, and
/// caller needs to free the error message via `swiftscan_string_dispose`. /// caller needs to free the error message via `swiftscan_string_dispose`.
/// This API is DEPRECATED and in favor of using
/// `swiftscan_cache_compute_key_from_input_index`.
SWIFTSCAN_PUBLIC swiftscan_string_ref_t SWIFTSCAN_PUBLIC swiftscan_string_ref_t
swiftscan_cache_compute_key(swiftscan_cas_t cas, int argc, const char **argv, swiftscan_cache_compute_key(swiftscan_cas_t cas, int argc, const char **argv,
const char *input, swiftscan_string_ref_t *error); const char *input, swiftscan_string_ref_t *error);
/// Compute \c CacheKey for the outputs of a primary input file from a compiler
/// invocation with command-line \c argc and \c argv and the index for the
/// input. The index of the input is computed from the position of the input
/// file from all input files. When primary input file is not available for
/// compilation, e.g., using WMO, primary file is the first swift input on the
/// command-line by convention. Return \c CacheKey as string. If error happens,
/// the error message is returned via `error` parameter, and caller needs to
/// free the error message via `swiftscan_string_dispose`.
SWIFTSCAN_PUBLIC swiftscan_string_ref_t
swiftscan_cache_compute_key_from_input_index(swiftscan_cas_t cas, int argc,
const char **argv,
unsigned input_index,
swiftscan_string_ref_t *error);
/// Query the result of the compilation using the output cache key. \c globally /// Query the result of the compilation using the output cache key. \c globally
/// suggests if the lookup should check remote cache if such operation exists. /// suggests if the lookup should check remote cache if such operation exists.
/// Returns the cached compilation of the result if found, or nullptr if output /// Returns the cached compilation of the result if found, or nullptr if output

View File

@@ -33,8 +33,7 @@ protected:
llvm::Optional<llvm::vfs::OutputConfig> Config) override; llvm::Optional<llvm::vfs::OutputConfig> Config) override;
virtual llvm::Error storeImpl(llvm::StringRef Path, llvm::StringRef Bytes, virtual llvm::Error storeImpl(llvm::StringRef Path, llvm::StringRef Bytes,
llvm::StringRef CorrespondingInput, unsigned InputIndex, file_types::ID OutputKind);
file_types::ID OutputKind);
private: private:
file_types::ID getOutputFileType(llvm::StringRef Path) const; file_types::ID getOutputFileType(llvm::StringRef Path) const;
@@ -47,7 +46,7 @@ public:
FrontendOptions::ActionType Action); FrontendOptions::ActionType Action);
~SwiftCASOutputBackend(); ~SwiftCASOutputBackend();
llvm::Error storeCachedDiagnostics(llvm::StringRef InputFile, llvm::Error storeCachedDiagnostics(unsigned InputIndex,
llvm::StringRef Bytes); llvm::StringRef Bytes);
private: private:

View File

@@ -36,12 +36,13 @@ createCompileJobBaseCacheKey(llvm::cas::ObjectStore &CAS,
ArrayRef<const char *> Args); ArrayRef<const char *> Args);
/// Compute CompileJobKey for the compiler outputs. The key for the output /// Compute CompileJobKey for the compiler outputs. The key for the output
/// is computed from the base key for the compilation, the output kind and the /// is computed from the base key for the compilation and the input file index
/// input file path that is associated with this specific output. /// which is the index for the input among all the input files (not just the
/// output producing inputs).
llvm::Expected<llvm::cas::ObjectRef> llvm::Expected<llvm::cas::ObjectRef>
createCompileJobCacheKeyForOutput(llvm::cas::ObjectStore &CAS, createCompileJobCacheKeyForOutput(llvm::cas::ObjectStore &CAS,
llvm::cas::ObjectRef BaseKey, llvm::cas::ObjectRef BaseKey,
StringRef ProducingInput); unsigned InputIndex);
} // namespace swift } // namespace swift
#endif #endif

View File

@@ -159,6 +159,8 @@ public:
const InputFile &getFirstOutputProducingInput() const; const InputFile &getFirstOutputProducingInput() const;
unsigned getIndexOfFirstOutputProducingInput() const;
bool isInputPrimary(StringRef file) const; bool isInputPrimary(StringRef file) const;
unsigned numberOfPrimaryInputsEndingWith(StringRef extension) const; unsigned numberOfPrimaryInputsEndingWith(StringRef extension) const;

View File

@@ -179,7 +179,9 @@ updateModuleCacheKey(ModuleDependencyInfo &depInfo,
if (cache.getScanService().hasPathMapping()) if (cache.getScanService().hasPathMapping())
InputPath = cache.getScanService().remapPath(InputPath); InputPath = cache.getScanService().remapPath(InputPath);
auto key = createCompileJobCacheKeyForOutput(CAS, *base, InputPath); // Module compilation commands always have only one input and the input
// index is always 0.
auto key = createCompileJobCacheKeyForOutput(CAS, *base, /*InputIndex=*/0);
if (!key) if (!key)
return key.takeError(); return key.takeError();

View File

@@ -284,10 +284,10 @@ int SwiftCacheToolInvocation::printOutputKeys() {
std::vector<OutputEntry> OutputKeys; std::vector<OutputEntry> OutputKeys;
bool hasError = false; bool hasError = false;
auto addFromInputFile = [&](const InputFile &Input) { auto addFromInputFile = [&](const InputFile &Input, unsigned InputIndex) {
auto InputPath = Input.getFileName(); auto InputPath = Input.getFileName();
auto OutputKey = auto OutputKey =
createCompileJobCacheKeyForOutput(CAS, *BaseKey, InputPath); createCompileJobCacheKeyForOutput(CAS, *BaseKey, InputIndex);
if (!OutputKey) { if (!OutputKey) {
llvm::errs() << "cannot create cache key for " << InputPath << ": " llvm::errs() << "cannot create cache key for " << InputPath << ": "
<< toString(OutputKey.takeError()) << "\n"; << toString(OutputKey.takeError()) << "\n";
@@ -310,9 +310,10 @@ int SwiftCacheToolInvocation::printOutputKeys() {
Outputs.emplace_back(file_types::getTypeName(ID), File); Outputs.emplace_back(file_types::getTypeName(ID), File);
}); });
}; };
llvm::for_each( auto AllInputs =
Invocation.getFrontendOptions().InputsAndOutputs.getAllInputs(), Invocation.getFrontendOptions().InputsAndOutputs.getAllInputs();
addFromInputFile); for (unsigned Index = 0; Index < AllInputs.size(); ++Index)
addFromInputFile(AllInputs[Index], Index);
// Add diagnostics file. // Add diagnostics file.
if (!OutputKeys.empty()) if (!OutputKeys.empty())

View File

@@ -68,7 +68,7 @@ public:
auto ProducingInput = OutputToInputMap.find(ResolvedPath); auto ProducingInput = OutputToInputMap.find(ResolvedPath);
assert(ProducingInput != OutputToInputMap.end() && "Unknown output file"); assert(ProducingInput != OutputToInputMap.end() && "Unknown output file");
std::string InputFilename = ProducingInput->second.first.getFileName(); unsigned InputIndex = ProducingInput->second.first;
auto OutputType = ProducingInput->second.second; auto OutputType = ProducingInput->second.second;
// Uncached output kind. // Uncached output kind.
@@ -77,18 +77,18 @@ public:
return std::make_unique<SwiftCASOutputFile>( return std::make_unique<SwiftCASOutputFile>(
ResolvedPath, ResolvedPath,
[this, InputFilename, OutputType](StringRef Path, [this, InputIndex, OutputType](StringRef Path,
StringRef Bytes) -> Error { StringRef Bytes) -> Error {
return storeImpl(Path, Bytes, InputFilename, OutputType); return storeImpl(Path, Bytes, InputIndex, OutputType);
}); });
} }
void initBackend(const FrontendInputsAndOutputs &InputsAndOutputs); void initBackend(const FrontendInputsAndOutputs &InputsAndOutputs);
Error storeImpl(StringRef Path, StringRef Bytes, StringRef CorrespondingInput, Error storeImpl(StringRef Path, StringRef Bytes, unsigned InputIndex,
file_types::ID OutputKind); file_types::ID OutputKind);
Error finalizeCacheKeysFor(StringRef Input); Error finalizeCacheKeysFor(unsigned InputIndex);
private: private:
friend class SwiftCASOutputBackend; friend class SwiftCASOutputBackend;
@@ -98,8 +98,11 @@ private:
const FrontendInputsAndOutputs &InputsAndOutputs; const FrontendInputsAndOutputs &InputsAndOutputs;
FrontendOptions::ActionType Action; FrontendOptions::ActionType Action;
StringMap<std::pair<const InputFile &, file_types::ID>> OutputToInputMap; // Map from output path to the input index and output kind.
StringMap<DenseMap<file_types::ID, ObjectRef>> OutputRefs; StringMap<std::pair<unsigned, file_types::ID>> OutputToInputMap;
// A vector of output refs where the index is the input index.
SmallVector<DenseMap<file_types::ID, ObjectRef>> OutputRefs;
}; };
SwiftCASOutputBackend::SwiftCASOutputBackend( SwiftCASOutputBackend::SwiftCASOutputBackend(
@@ -127,14 +130,14 @@ file_types::ID SwiftCASOutputBackend::getOutputFileType(StringRef Path) const {
} }
Error SwiftCASOutputBackend::storeImpl(StringRef Path, StringRef Bytes, Error SwiftCASOutputBackend::storeImpl(StringRef Path, StringRef Bytes,
StringRef CorrespondingInput, unsigned InputIndex,
file_types::ID OutputKind) { file_types::ID OutputKind) {
return Impl.storeImpl(Path, Bytes, CorrespondingInput, OutputKind); return Impl.storeImpl(Path, Bytes, InputIndex, OutputKind);
} }
Error SwiftCASOutputBackend::storeCachedDiagnostics(StringRef InputFile, Error SwiftCASOutputBackend::storeCachedDiagnostics(unsigned InputIndex,
StringRef Bytes) { StringRef Bytes) {
return storeImpl("<cached-diagnostics>", Bytes, InputFile, return storeImpl("<cached-diagnostics>", Bytes, InputIndex,
file_types::ID::TY_CachedDiagnostics); file_types::ID::TY_CachedDiagnostics);
} }
@@ -145,28 +148,34 @@ void SwiftCASOutputBackend::Implementation::initBackend(
// input it actually comes from. Maybe the solution is just not to cache // input it actually comes from. Maybe the solution is just not to cache
// any commands write output to `-`. // any commands write output to `-`.
file_types::ID mainOutputType = InputsAndOutputs.getPrincipalOutputType(); file_types::ID mainOutputType = InputsAndOutputs.getPrincipalOutputType();
auto addInput = [&](const InputFile &Input) { auto addInput = [&](const InputFile &Input, unsigned Index) {
if (!Input.outputFilename().empty()) if (!Input.outputFilename().empty())
OutputToInputMap.insert( OutputToInputMap.insert(
{Input.outputFilename(), {Input, mainOutputType}}); {Input.outputFilename(), {Index, mainOutputType}});
Input.getPrimarySpecificPaths() Input.getPrimarySpecificPaths()
.SupplementaryOutputs.forEachSetOutputAndType( .SupplementaryOutputs.forEachSetOutputAndType(
[&](const std::string &Out, file_types::ID ID) { [&](const std::string &Out, file_types::ID ID) {
if (!file_types::isProducedFromDiagnostics(ID)) if (!file_types::isProducedFromDiagnostics(ID))
OutputToInputMap.insert({Out, {Input, ID}}); OutputToInputMap.insert({Out, {Index, ID}});
}); });
}; };
llvm::for_each(InputsAndOutputs.getAllInputs(), addInput);
for (unsigned idx = 0; idx < InputsAndOutputs.getAllInputs().size(); ++idx)
addInput(InputsAndOutputs.getAllInputs()[idx], idx);
// FIXME: Cached diagnostics is associated with the first output producing // FIXME: Cached diagnostics is associated with the first output producing
// input file. // input file.
OutputToInputMap.insert({"<cached-diagnostics>", OutputToInputMap.insert(
{InputsAndOutputs.getFirstOutputProducingInput(), {"<cached-diagnostics>",
file_types::TY_CachedDiagnostics}}); {InputsAndOutputs.getIndexOfFirstOutputProducingInput(),
file_types::TY_CachedDiagnostics}});
// Resize the output refs to hold all inputs.
OutputRefs.resize(InputsAndOutputs.getAllInputs().size());
} }
Error SwiftCASOutputBackend::Implementation::storeImpl( Error SwiftCASOutputBackend::Implementation::storeImpl(
StringRef Path, StringRef Bytes, StringRef CorrespondingInput, StringRef Path, StringRef Bytes, unsigned InputIndex,
file_types::ID OutputKind) { file_types::ID OutputKind) {
Optional<ObjectRef> BytesRef; Optional<ObjectRef> BytesRef;
if (Error E = CAS.storeFromString(None, Bytes).moveInto(BytesRef)) if (Error E = CAS.storeFromString(None, Bytes).moveInto(BytesRef))
@@ -174,28 +183,28 @@ Error SwiftCASOutputBackend::Implementation::storeImpl(
LLVM_DEBUG(llvm::dbgs() << "DEBUG: producing CAS output of type \'" LLVM_DEBUG(llvm::dbgs() << "DEBUG: producing CAS output of type \'"
<< file_types::getTypeName(OutputKind) << file_types::getTypeName(OutputKind)
<< "\' for input \'" << CorrespondingInput << "\': \'" << "\' for input \'" << InputIndex << "\': \'"
<< CAS.getID(*BytesRef).toString() << "\'\n";); << CAS.getID(*BytesRef).toString() << "\'\n";);
OutputRefs[CorrespondingInput].insert({OutputKind, *BytesRef}); OutputRefs[InputIndex].insert({OutputKind, *BytesRef});
return finalizeCacheKeysFor(CorrespondingInput); return finalizeCacheKeysFor(InputIndex);
} }
Error SwiftCASOutputBackend::Implementation::finalizeCacheKeysFor( Error SwiftCASOutputBackend::Implementation::finalizeCacheKeysFor(
StringRef Input) { unsigned InputIndex) {
auto Entry = OutputRefs.find(Input); auto ProducedOutputs = OutputRefs[InputIndex];
assert(Entry != OutputRefs.end() && "Unexpected input"); assert(!ProducedOutputs.empty() && "Expect outputs for this input");
// If not all outputs for the input are emitted, return. // If not all outputs for the input are emitted, return.
if (!llvm::all_of(OutputToInputMap, [&](auto &E) { if (!llvm::all_of(OutputToInputMap, [&](auto &E) {
return (E.second.first.getFileName() != Input || return (E.second.first != InputIndex ||
Entry->second.count(E.second.second)); ProducedOutputs.count(E.second.second));
})) }))
return Error::success(); return Error::success();
std::vector<std::pair<file_types::ID, ObjectRef>> OutputsForInput; std::vector<std::pair<file_types::ID, ObjectRef>> OutputsForInput;
llvm::for_each(Entry->second, [&OutputsForInput](auto E) { llvm::for_each(ProducedOutputs, [&OutputsForInput](auto E) {
OutputsForInput.emplace_back(E.first, E.second); OutputsForInput.emplace_back(E.first, E.second);
}); });
// Sort to a stable ordering for deterministic output cache object. // Sort to a stable ordering for deterministic output cache object.
@@ -237,14 +246,14 @@ Error SwiftCASOutputBackend::Implementation::finalizeCacheKeysFor(
return Err; return Err;
} }
auto CacheKey = createCompileJobCacheKeyForOutput(CAS, BaseKey, Input); auto CacheKey = createCompileJobCacheKeyForOutput(CAS, BaseKey, InputIndex);
if (!CacheKey) if (!CacheKey)
return CacheKey.takeError(); return CacheKey.takeError();
LLVM_DEBUG(llvm::dbgs() << "DEBUG: writing cache entry for input \'" << Input LLVM_DEBUG(llvm::dbgs() << "DEBUG: writing cache entry for input \'"
<< "\': \'" << CAS.getID(*CacheKey).toString() << InputIndex << "\': \'"
<< "\' => \'" << CAS.getID(*Result).toString() << CAS.getID(*CacheKey).toString() << "\' => \'"
<< "\'\n";); << CAS.getID(*Result).toString() << "\'\n";);
if (auto E = Cache.put(CAS.getID(*CacheKey), CAS.getID(*Result))) if (auto E = Cache.put(CAS.getID(*CacheKey), CAS.getID(*Result)))
return E; return E;

View File

@@ -777,8 +777,7 @@ CachingDiagnosticsProcessor::CachingDiagnosticsProcessor(
auto Err = Instance.getCASOutputBackend().storeCachedDiagnostics( auto Err = Instance.getCASOutputBackend().storeCachedDiagnostics(
Instance.getInvocation() Instance.getInvocation()
.getFrontendOptions() .getFrontendOptions()
.InputsAndOutputs.getFirstOutputProducingInput() .InputsAndOutputs.getIndexOfFirstOutputProducingInput(),
.getFileName(),
Content); Content);
if (Err) { if (Err) {

View File

@@ -145,10 +145,12 @@ bool replayCachedCompilerOutputs(
Optional<OutputEntry> DiagnosticsOutput; Optional<OutputEntry> DiagnosticsOutput;
auto replayOutputsForInputFile = [&](const std::string &InputPath, auto replayOutputsForInputFile = [&](const std::string &InputPath,
unsigned InputIndex,
const DenseMap<file_types::ID, const DenseMap<file_types::ID,
std::string> &Outputs) { std::string> &Outputs) {
auto lookupFailed = [&CanReplayAllOutput] { CanReplayAllOutput = false; }; auto lookupFailed = [&CanReplayAllOutput] { CanReplayAllOutput = false; };
auto OutputKey = createCompileJobCacheKeyForOutput(CAS, BaseKey, InputPath); auto OutputKey =
createCompileJobCacheKeyForOutput(CAS, BaseKey, InputIndex);
if (!OutputKey) { if (!OutputKey) {
Diag.diagnose(SourceLoc(), diag::error_cas, Diag.diagnose(SourceLoc(), diag::error_cas,
@@ -199,7 +201,8 @@ bool replayCachedCompilerOutputs(
} }
}; };
auto replayOutputFromInput = [&](const InputFile &Input) { auto replayOutputFromInput = [&](const InputFile &Input,
unsigned InputIndex) {
auto InputPath = Input.getFileName(); auto InputPath = Input.getFileName();
DenseMap<file_types::ID, std::string> Outputs; DenseMap<file_types::ID, std::string> Outputs;
if (!Input.outputFilename().empty()) if (!Input.outputFilename().empty())
@@ -225,27 +228,33 @@ bool replayCachedCompilerOutputs(
Outputs.try_emplace(file_types::ID::TY_CachedDiagnostics, Outputs.try_emplace(file_types::ID::TY_CachedDiagnostics,
"<cached-diagnostics>"); "<cached-diagnostics>");
return replayOutputsForInputFile(InputPath, Outputs); return replayOutputsForInputFile(InputPath, InputIndex, Outputs);
}; };
auto AllInputs = InputsAndOutputs.getAllInputs();
// If there are primary inputs, look up only the primary input files. // If there are primary inputs, look up only the primary input files.
// Otherwise, prepare to do cache lookup for all inputs. // Otherwise, prepare to do cache lookup for all inputs.
if (InputsAndOutputs.hasPrimaryInputs()) for (unsigned Index = 0; Index < AllInputs.size(); ++Index) {
InputsAndOutputs.forEachPrimaryInput([&](const InputFile &File) { const auto &Input = AllInputs[Index];
replayOutputFromInput(File); if (InputsAndOutputs.hasPrimaryInputs() && !Input.isPrimary())
return false; continue;
});
else replayOutputFromInput(Input, Index);
llvm::for_each(InputsAndOutputs.getAllInputs(), replayOutputFromInput); }
if (!CanReplayAllOutput)
return false;
// If there is not diagnostic output, this is a job that produces no output // If there is not diagnostic output, this is a job that produces no output
// and only diagnostics, like `typecheck-module-from-interface`, look up // and only diagnostics, like `typecheck-module-from-interface`, look up
// diagnostics from first file. // diagnostics from first file.
if (!DiagnosticsOutput) if (!DiagnosticsOutput)
replayOutputsForInputFile( replayOutputsForInputFile(
InputsAndOutputs.getFirstOutputProducingInput().getFileName(), "<cached-diagnostics>",
InputsAndOutputs.getIndexOfFirstOutputProducingInput(),
{{file_types::ID::TY_CachedDiagnostics, "<cached-diagnostics>"}}); {{file_types::ID::TY_CachedDiagnostics, "<cached-diagnostics>"}});
// Check again to make sure diagnostics is fetched successfully.
if (!CanReplayAllOutput) if (!CanReplayAllOutput)
return false; return false;

View File

@@ -23,6 +23,7 @@
#include "llvm/CAS/ObjectStore.h" #include "llvm/CAS/ObjectStore.h"
#include "llvm/Option/ArgList.h" #include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h" #include "llvm/Option/OptTable.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/Error.h" #include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/MemoryBuffer.h"
@@ -84,13 +85,16 @@ llvm::Expected<llvm::cas::ObjectRef> swift::createCompileJobBaseCacheKey(
return Out.takeError(); return Out.takeError();
} }
llvm::Expected<llvm::cas::ObjectRef> swift::createCompileJobCacheKeyForOutput( llvm::Expected<llvm::cas::ObjectRef>
llvm::cas::ObjectStore &CAS, llvm::cas::ObjectRef BaseKey, swift::createCompileJobCacheKeyForOutput(llvm::cas::ObjectStore &CAS,
StringRef ProducingInput) { llvm::cas::ObjectRef BaseKey,
SmallString<256> OutputInfo; unsigned InputIndex) {
std::string InputInfo;
llvm::raw_string_ostream OS(InputInfo);
// CacheKey is the producting input + the base key. // CacheKey is the index of the producting input + the base key.
OutputInfo.append(ProducingInput); // Encode the unsigned value as little endian in the field.
llvm::support::endian::write<uint32_t>(OS, InputIndex, llvm::support::little);
return CAS.storeFromString({BaseKey}, OutputInfo); return CAS.storeFromString({BaseKey}, OS.str());
} }

View File

@@ -159,6 +159,10 @@ FrontendInputsAndOutputs::getFirstOutputProducingInput() const {
return isWholeModule() ? firstInput() : firstPrimaryInput(); return isWholeModule() ? firstInput() : firstPrimaryInput();
} }
unsigned FrontendInputsAndOutputs::getIndexOfFirstOutputProducingInput() const {
return isWholeModule() ? 0 : PrimaryInputsInOrder[0];
}
bool FrontendInputsAndOutputs::isInputPrimary(StringRef file) const { bool FrontendInputsAndOutputs::isInputPrimary(StringRef file) const {
return primaryInputNamed(file) != nullptr; return primaryInputNamed(file) != nullptr;
} }

View File

@@ -40,7 +40,7 @@
"name": "ld-path-driver-option" "name": "ld-path-driver-option"
}, },
{ {
"name": "cache-compile-job" "name": "compilation-caching"
} }
] ]
} }

View File

@@ -4,6 +4,12 @@
// RUN: -emit-module -emit-module-path %t/Test.swiftmodule -c -emit-dependencies -module-name Test -o %t/test.o -cas-path %t/cas \ // RUN: -emit-module -emit-module-path %t/Test.swiftmodule -c -emit-dependencies -module-name Test -o %t/test.o -cas-path %t/cas \
// RUN: -allow-unstable-cache-key-for-testing > %t/key.casid // RUN: -allow-unstable-cache-key-for-testing > %t/key.casid
// RUN: %swift-scan-test -action compute_cache_key_from_index -cas-path %t/cas -input 0 -- %target-swift-frontend -cache-compile-job -Rcache-compile-job %s \
// RUN: -emit-module -emit-module-path %t/Test.swiftmodule -c -emit-dependencies -module-name Test -o %t/test.o -cas-path %t/cas \
// RUN: -allow-unstable-cache-key-for-testing > %t/key1.casid
// RUN: diff %t/key.casid %t/key1.casid
// RUN: not %swift-scan-test -action cache_query -id @%t/key.casid -cas-path %t/cas 2>&1 | %FileCheck %s --check-prefix=CHECK-QUERY-NOT-FOUND // RUN: not %swift-scan-test -action cache_query -id @%t/key.casid -cas-path %t/cas 2>&1 | %FileCheck %s --check-prefix=CHECK-QUERY-NOT-FOUND
// RUN: %target-swift-frontend -cache-compile-job -Rcache-compile-job %s -emit-module -emit-module-path %t/Test.swiftmodule -c -emit-dependencies \ // RUN: %target-swift-frontend -cache-compile-job -Rcache-compile-job %s -emit-module -emit-module-path %t/Test.swiftmodule -c -emit-dependencies \

View File

@@ -38,7 +38,9 @@
#include "llvm/CAS/CASReference.h" #include "llvm/CAS/CASReference.h"
#include "llvm/CAS/ObjectStore.h" #include "llvm/CAS/ObjectStore.h"
#include "llvm/Support/Allocator.h" #include "llvm/Support/Allocator.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h" #include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/PrefixMapper.h" #include "llvm/Support/PrefixMapper.h"
#include "llvm/Support/StringSaver.h" #include "llvm/Support/StringSaver.h"
#include "llvm/Support/VirtualOutputBackend.h" #include "llvm/Support/VirtualOutputBackend.h"
@@ -73,19 +75,19 @@ struct SwiftCachedCompilationHandle {
SwiftCachedCompilationHandle(llvm::cas::ObjectRef Key, SwiftCachedCompilationHandle(llvm::cas::ObjectRef Key,
llvm::cas::ObjectRef Output, llvm::cas::ObjectRef Output,
clang::cas::CompileJobCacheResult &&Result, clang::cas::CompileJobCacheResult &&Result,
llvm::StringRef Input, SwiftScanCAS &CAS) unsigned InputIndex, SwiftScanCAS &CAS)
: Key(Key), Output(Output), CorrespondingInput(Input), Result(Result), : Key(Key), Output(Output), InputIndex(InputIndex), Result(Result),
DB(CAS) {} DB(CAS) {}
SwiftCachedCompilationHandle(llvm::cas::ObjectRef Key, SwiftCachedCompilationHandle(llvm::cas::ObjectRef Key,
llvm::cas::ObjectRef Output, llvm::cas::ObjectRef Output,
swift::cas::CompileJobCacheResult &&Result, swift::cas::CompileJobCacheResult &&Result,
llvm::StringRef Input, SwiftScanCAS &CAS) unsigned InputIndex, SwiftScanCAS &CAS)
: Key(Key), Output(Output), CorrespondingInput(Input), Result(Result), : Key(Key), Output(Output), InputIndex(InputIndex), Result(Result),
DB(CAS) {} DB(CAS) {}
llvm::cas::ObjectRef Key; llvm::cas::ObjectRef Key;
llvm::cas::ObjectRef Output; llvm::cas::ObjectRef Output;
std::string CorrespondingInput; unsigned InputIndex;
std::variant<swift::cas::CompileJobCacheResult, std::variant<swift::cas::CompileJobCacheResult,
clang::cas::CompileJobCacheResult> clang::cas::CompileJobCacheResult>
Result; Result;
@@ -201,10 +203,67 @@ computeCacheKey(llvm::cas::ObjectStore &CAS, llvm::ArrayRef<const char *> Args,
if (!BaseKey) if (!BaseKey)
return BaseKey.takeError(); return BaseKey.takeError();
auto Key = swift::createCompileJobCacheKeyForOutput(CAS, *BaseKey, InputPath); // Parse the arguments to figure out the index for the input.
swift::CompilerInvocation Invocation;
swift::SourceManager SourceMgr;
swift::DiagnosticEngine Diags(SourceMgr);
llvm::SmallString<128> workingDirectory;
llvm::sys::fs::current_path(workingDirectory);
llvm::SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4>
configurationFileBuffers;
std::string MainExecutablePath = llvm::sys::fs::getMainExecutable(
"swift-frontend", (void *)swiftscan_cache_replay_compilation);
// Drop the `-frontend` option if it is passed.
if (llvm::StringRef(Args.front()) == "-frontend")
Args = Args.drop_front();
if (Invocation.parseArgs(Args, Diags, &configurationFileBuffers,
workingDirectory, MainExecutablePath))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Argument parsing failed");
auto computeKey = [&](unsigned Index) -> llvm::Expected<std::string> {
auto Key = swift::createCompileJobCacheKeyForOutput(CAS, *BaseKey, Index);
if (!Key)
return Key.takeError();
return CAS.getID(*Key).toString();
};
auto AllInputs =
Invocation.getFrontendOptions().InputsAndOutputs.getAllInputs();
// First pass, check for path equal.
for (unsigned Idx = 0; Idx < AllInputs.size(); ++Idx) {
if (AllInputs[Idx].getFileName() == InputPath)
return computeKey(Idx);
}
// If not found, slow second iteration with real_path.
llvm::SmallString<256> InputRealPath;
llvm::sys::fs::real_path(InputPath, InputRealPath, true);
for (unsigned Idx = 0; Idx < AllInputs.size(); ++Idx) {
llvm::SmallString<256> TestRealPath;
llvm::sys::fs::real_path(AllInputs[Idx].getFileName(), TestRealPath, true);
if (InputRealPath == TestRealPath)
return computeKey(Idx);
}
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"requested input not found from invocation");
}
static llvm::Expected<std::string>
computeCacheKeyFromIndex(llvm::cas::ObjectStore &CAS,
llvm::ArrayRef<const char *> Args,
unsigned InputIndex) {
auto BaseKey = swift::createCompileJobBaseCacheKey(CAS, Args);
if (!BaseKey)
return BaseKey.takeError();
auto Key =
swift::createCompileJobCacheKeyForOutput(CAS, *BaseKey, InputIndex);
if (!Key) if (!Key)
return Key.takeError(); return Key.takeError();
return CAS.getID(*Key).toString(); return CAS.getID(*Key).toString();
} }
@@ -225,6 +284,26 @@ swiftscan_cache_compute_key(swiftscan_cas_t cas, int argc, const char **argv,
return swift::c_string_utils::create_clone(ID->c_str()); return swift::c_string_utils::create_clone(ID->c_str());
} }
swiftscan_string_ref_t
swiftscan_cache_compute_key_from_input_index(swiftscan_cas_t cas, int argc,
const char **argv,
unsigned input_index,
swiftscan_string_ref_t *error) {
std::vector<const char *> Compilation;
for (int i = 0; i < argc; ++i)
Compilation.push_back(argv[i]);
auto ID =
computeCacheKeyFromIndex(unwrap(cas)->getCAS(), Compilation, input_index);
if (!ID) {
*error =
swift::c_string_utils::create_clone(toString(ID.takeError()).c_str());
return swift::c_string_utils::create_null();
}
*error = swift::c_string_utils::create_null();
return swift::c_string_utils::create_clone(ID->c_str());
}
// Create a non-owning string ref that is used in call backs. // Create a non-owning string ref that is used in call backs.
static swiftscan_string_ref_t createNonOwningString(llvm::StringRef Str) { static swiftscan_string_ref_t createNonOwningString(llvm::StringRef Str) {
if (Str.empty()) if (Str.empty())
@@ -259,6 +338,9 @@ createCachedCompilation(SwiftScanCAS &CAS, const llvm::cas::CASID &ID,
return KeyProxy.takeError(); return KeyProxy.takeError();
auto Input = KeyProxy->getData(); auto Input = KeyProxy->getData();
unsigned Index =
llvm::support::endian::read<uint32_t, llvm::support::little,
llvm::support::unaligned>(Input.data());
{ {
swift::cas::CompileJobResultSchema Schema(CAS.getCAS()); swift::cas::CompileJobResultSchema Schema(CAS.getCAS());
if (Schema.isRootNode(*Proxy)) { if (Schema.isRootNode(*Proxy)) {
@@ -266,7 +348,7 @@ createCachedCompilation(SwiftScanCAS &CAS, const llvm::cas::CASID &ID,
if (!Result) if (!Result)
return Result.takeError(); return Result.takeError();
return new SwiftCachedCompilationHandle(KeyProxy->getRef(), *Ref, return new SwiftCachedCompilationHandle(KeyProxy->getRef(), *Ref,
std::move(*Result), Input, CAS); std::move(*Result), Index, CAS);
} }
} }
{ {
@@ -276,7 +358,7 @@ createCachedCompilation(SwiftScanCAS &CAS, const llvm::cas::CASID &ID,
if (!Result) if (!Result)
return Result.takeError(); return Result.takeError();
return new SwiftCachedCompilationHandle(KeyProxy->getRef(), *Ref, return new SwiftCachedCompilationHandle(KeyProxy->getRef(), *Ref,
std::move(*Result), Input, CAS); std::move(*Result), Index, CAS);
} }
} }
return createUnsupportedSchemaError(); return createUnsupportedSchemaError();
@@ -759,13 +841,10 @@ static llvm::Error replayCompilation(SwiftScanReplayInstance &Instance,
auto &InputsAndOutputs = auto &InputsAndOutputs =
Instance.Invocation.getFrontendOptions().InputsAndOutputs; Instance.Invocation.getFrontendOptions().InputsAndOutputs;
auto AllInputs = InputsAndOutputs.getAllInputs(); auto AllInputs = InputsAndOutputs.getAllInputs();
auto Input = llvm::find_if(AllInputs, [&](const InputFile &Input) { if (Comp.InputIndex >= AllInputs.size())
return Input.getFileName() == Comp.CorrespondingInput;
});
if (Input == AllInputs.end())
return createStringError(inconvertibleErrorCode(), return createStringError(inconvertibleErrorCode(),
"InputFile \"" + Comp.CorrespondingInput + "InputFile index too large for compilation");
"\" is not part of the compilation"); const auto &Input = AllInputs[Comp.InputIndex];
// Setup DiagnosticsConsumers. // Setup DiagnosticsConsumers.
// FIXME: Reduce code duplication against `performFrontend()` and add support // FIXME: Reduce code duplication against `performFrontend()` and add support
@@ -793,10 +872,10 @@ static llvm::Error replayCompilation(SwiftScanReplayInstance &Instance,
// Collect the file that needs to write. // Collect the file that needs to write.
DenseMap<file_types::ID, std::string> Outputs; DenseMap<file_types::ID, std::string> Outputs;
if (!Input->outputFilename().empty()) if (!Input.outputFilename().empty())
Outputs.try_emplace(InputsAndOutputs.getPrincipalOutputType(), Outputs.try_emplace(InputsAndOutputs.getPrincipalOutputType(),
Input->outputFilename()); Input.outputFilename());
Input->getPrimarySpecificPaths().SupplementaryOutputs.forEachSetOutputAndType( Input.getPrimarySpecificPaths().SupplementaryOutputs.forEachSetOutputAndType(
[&](const std::string &File, file_types::ID ID) { [&](const std::string &File, file_types::ID ID) {
if (file_types::isProducedFromDiagnostics(ID)) if (file_types::isProducedFromDiagnostics(ID))
return; return;

View File

@@ -85,6 +85,7 @@ swiftscan_cas_options_set_plugin_path
swiftscan_cas_options_set_plugin_option swiftscan_cas_options_set_plugin_option
swiftscan_cas_store swiftscan_cas_store
swiftscan_cache_compute_key swiftscan_cache_compute_key
swiftscan_cache_compute_key_from_input_index
swiftscan_cache_query swiftscan_cache_query
swiftscan_cache_query_async swiftscan_cache_query_async
swiftscan_cached_compilation_get_num_outputs swiftscan_cached_compilation_get_num_outputs

View File

@@ -17,6 +17,7 @@
#include "swift-c/DependencyScan/DependencyScan.h" #include "swift-c/DependencyScan/DependencyScan.h"
#include "swift/Basic/Defer.h" #include "swift/Basic/Defer.h"
#include "swift/Basic/FileTypes.h" #include "swift/Basic/FileTypes.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Allocator.h" #include "llvm/Support/Allocator.h"
#include "llvm/Support/CommandLine.h" #include "llvm/Support/CommandLine.h"
#include "llvm/Support/StringSaver.h" #include "llvm/Support/StringSaver.h"
@@ -26,6 +27,7 @@ using namespace llvm;
namespace { namespace {
enum Actions { enum Actions {
compute_cache_key, compute_cache_key,
compute_cache_key_from_index,
cache_query, cache_query,
replay_result, replay_result,
}; };
@@ -35,11 +37,13 @@ llvm::cl::opt<std::string> CASPath("cas-path", llvm::cl::desc("<path>"),
llvm::cl::cat(Category)); llvm::cl::cat(Category));
llvm::cl::opt<std::string> CASID("id", llvm::cl::desc("<casid>"), llvm::cl::opt<std::string> CASID("id", llvm::cl::desc("<casid>"),
llvm::cl::cat(Category)); llvm::cl::cat(Category));
llvm::cl::opt<std::string> Input("input", llvm::cl::desc("<file>"), llvm::cl::opt<std::string> Input("input", llvm::cl::desc("<file|index>"),
llvm::cl::cat(Category)); llvm::cl::cat(Category));
llvm::cl::opt<Actions> llvm::cl::opt<Actions>
Action("action", llvm::cl::desc("<action>"), Action("action", llvm::cl::desc("<action>"),
llvm::cl::values(clEnumVal(compute_cache_key, "compute cache key"), llvm::cl::values(clEnumVal(compute_cache_key, "compute cache key"),
clEnumVal(compute_cache_key_from_index,
"compute cache key from index"),
clEnumVal(cache_query, "cache query"), clEnumVal(cache_query, "cache query"),
clEnumVal(replay_result, "replay result")), clEnumVal(replay_result, "replay result")),
llvm::cl::cat(Category)); llvm::cl::cat(Category));
@@ -78,6 +82,27 @@ static int action_compute_cache_key(swiftscan_cas_t cas, StringRef input,
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
static int
action_compute_cache_key_from_index(swiftscan_cas_t cas, StringRef index,
std::vector<const char *> &Args) {
unsigned inputIndex = 0;
if (!to_integer(index, inputIndex)) {
llvm::errs() << "-input is not a number for compute_cache_key_from_index\n";
return EXIT_FAILURE;
}
swiftscan_string_ref_t err_msg;
auto key = swiftscan_cache_compute_key_from_input_index(
cas, Args.size(), Args.data(), inputIndex, &err_msg);
if (key.length == 0)
return printError(err_msg);
llvm::outs() << toString(key) << "\n";
swiftscan_string_dispose(key);
return EXIT_SUCCESS;
}
static int print_cached_compilation(swiftscan_cached_compilation_t comp, static int print_cached_compilation(swiftscan_cached_compilation_t comp,
const char *key) { const char *key) {
auto numOutput = swiftscan_cached_compilation_get_num_outputs(comp); auto numOutput = swiftscan_cached_compilation_get_num_outputs(comp);
@@ -194,6 +219,8 @@ int main(int argc, char *argv[]) {
switch (Action) { switch (Action) {
case compute_cache_key: case compute_cache_key:
return action_compute_cache_key(cas, Input, Args); return action_compute_cache_key(cas, Input, Args);
case compute_cache_key_from_index:
return action_compute_cache_key_from_index(cas, Input, Args);
case cache_query: case cache_query:
return action_cache_query(cas, CASID.c_str()); return action_cache_query(cas, CASID.c_str());
case replay_result: case replay_result: