Skip to content

Commit 76bde39

Browse files
[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
1 parent 443cac2 commit 76bde39

16 files changed

+246
-87
lines changed

include/swift-c/DependencyScan/DependencyScan.h

+17-1
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 6
28+
#define SWIFTSCAN_VERSION_MINOR 7
2929

3030
SWIFTSCAN_BEGIN_DECLS
3131

@@ -505,10 +505,26 @@ SWIFTSCAN_PUBLIC void swiftscan_cas_dispose(swiftscan_cas_t cas);
505505
/// swift input on the command-line by convention. Return \c CacheKey as string.
506506
/// If error happens, the error message is returned via `error` parameter, and
507507
/// caller needs to free the error message via `swiftscan_string_dispose`.
508+
/// This API is DEPRECATED and in favor of using
509+
/// `swiftscan_cache_compute_key_from_input_index`.
508510
SWIFTSCAN_PUBLIC swiftscan_string_ref_t
509511
swiftscan_cache_compute_key(swiftscan_cas_t cas, int argc, const char **argv,
510512
const char *input, swiftscan_string_ref_t *error);
511513

514+
/// Compute \c CacheKey for the outputs of a primary input file from a compiler
515+
/// invocation with command-line \c argc and \c argv and the index for the
516+
/// input. The index of the input is computed from the position of the input
517+
/// file from all input files. When primary input file is not available for
518+
/// compilation, e.g., using WMO, primary file is the first swift input on the
519+
/// command-line by convention. Return \c CacheKey as string. If error happens,
520+
/// the error message is returned via `error` parameter, and caller needs to
521+
/// free the error message via `swiftscan_string_dispose`.
522+
SWIFTSCAN_PUBLIC swiftscan_string_ref_t
523+
swiftscan_cache_compute_key_from_input_index(swiftscan_cas_t cas, int argc,
524+
const char **argv,
525+
unsigned input_index,
526+
swiftscan_string_ref_t *error);
527+
512528
/// Query the result of the compilation using the output cache key. \c globally
513529
/// suggests if the lookup should check remote cache if such operation exists.
514530
/// Returns the cached compilation of the result if found, or nullptr if output

include/swift/Frontend/CASOutputBackends.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ class SwiftCASOutputBackend : public llvm::vfs::OutputBackend {
3333
llvm::Optional<llvm::vfs::OutputConfig> Config) override;
3434

3535
virtual llvm::Error storeImpl(llvm::StringRef Path, llvm::StringRef Bytes,
36-
llvm::StringRef CorrespondingInput,
37-
file_types::ID OutputKind);
36+
unsigned InputIndex, file_types::ID OutputKind);
3837

3938
private:
4039
file_types::ID getOutputFileType(llvm::StringRef Path) const;
@@ -47,7 +46,7 @@ class SwiftCASOutputBackend : public llvm::vfs::OutputBackend {
4746
FrontendOptions::ActionType Action);
4847
~SwiftCASOutputBackend();
4948

50-
llvm::Error storeCachedDiagnostics(llvm::StringRef InputFile,
49+
llvm::Error storeCachedDiagnostics(unsigned InputIndex,
5150
llvm::StringRef Bytes);
5251

5352
private:

include/swift/Frontend/CompileJobCacheKey.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@ createCompileJobBaseCacheKey(llvm::cas::ObjectStore &CAS,
3636
ArrayRef<const char *> Args);
3737

3838
/// Compute CompileJobKey for the compiler outputs. The key for the output
39-
/// is computed from the base key for the compilation, the output kind and the
40-
/// input file path that is associated with this specific output.
39+
/// is computed from the base key for the compilation and the input file index
40+
/// which is the index for the input among all the input files (not just the
41+
/// output producing inputs).
4142
llvm::Expected<llvm::cas::ObjectRef>
4243
createCompileJobCacheKeyForOutput(llvm::cas::ObjectStore &CAS,
4344
llvm::cas::ObjectRef BaseKey,
44-
StringRef ProducingInput);
45+
unsigned InputIndex);
4546
} // namespace swift
4647

4748
#endif

include/swift/Frontend/FrontendInputsAndOutputs.h

+2
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ class FrontendInputsAndOutputs {
159159

160160
const InputFile &getFirstOutputProducingInput() const;
161161

162+
unsigned getIndexOfFirstOutputProducingInput() const;
163+
162164
bool isInputPrimary(StringRef file) const;
163165

164166
unsigned numberOfPrimaryInputsEndingWith(StringRef extension) const;

lib/DependencyScan/ScanDependencies.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,9 @@ updateModuleCacheKey(ModuleDependencyInfo &depInfo,
179179
if (cache.getScanService().hasPathMapping())
180180
InputPath = cache.getScanService().remapPath(InputPath);
181181

182-
auto key = createCompileJobCacheKeyForOutput(CAS, *base, InputPath);
182+
// Module compilation commands always have only one input and the input
183+
// index is always 0.
184+
auto key = createCompileJobCacheKeyForOutput(CAS, *base, /*InputIndex=*/0);
183185
if (!key)
184186
return key.takeError();
185187

lib/DriverTool/swift_cache_tool_main.cpp

+6-5
Original file line numberDiff line numberDiff line change
@@ -284,10 +284,10 @@ int SwiftCacheToolInvocation::printOutputKeys() {
284284

285285
std::vector<OutputEntry> OutputKeys;
286286
bool hasError = false;
287-
auto addFromInputFile = [&](const InputFile &Input) {
287+
auto addFromInputFile = [&](const InputFile &Input, unsigned InputIndex) {
288288
auto InputPath = Input.getFileName();
289289
auto OutputKey =
290-
createCompileJobCacheKeyForOutput(CAS, *BaseKey, InputPath);
290+
createCompileJobCacheKeyForOutput(CAS, *BaseKey, InputIndex);
291291
if (!OutputKey) {
292292
llvm::errs() << "cannot create cache key for " << InputPath << ": "
293293
<< toString(OutputKey.takeError()) << "\n";
@@ -310,9 +310,10 @@ int SwiftCacheToolInvocation::printOutputKeys() {
310310
Outputs.emplace_back(file_types::getTypeName(ID), File);
311311
});
312312
};
313-
llvm::for_each(
314-
Invocation.getFrontendOptions().InputsAndOutputs.getAllInputs(),
315-
addFromInputFile);
313+
auto AllInputs =
314+
Invocation.getFrontendOptions().InputsAndOutputs.getAllInputs();
315+
for (unsigned Index = 0; Index < AllInputs.size(); ++Index)
316+
addFromInputFile(AllInputs[Index], Index);
316317

317318
// Add diagnostics file.
318319
if (!OutputKeys.empty())

lib/Frontend/CASOutputBackends.cpp

+43-34
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class SwiftCASOutputBackend::Implementation {
6868
auto ProducingInput = OutputToInputMap.find(ResolvedPath);
6969
assert(ProducingInput != OutputToInputMap.end() && "Unknown output file");
7070

71-
std::string InputFilename = ProducingInput->second.first.getFileName();
71+
unsigned InputIndex = ProducingInput->second.first;
7272
auto OutputType = ProducingInput->second.second;
7373

7474
// Uncached output kind.
@@ -77,18 +77,18 @@ class SwiftCASOutputBackend::Implementation {
7777

7878
return std::make_unique<SwiftCASOutputFile>(
7979
ResolvedPath,
80-
[this, InputFilename, OutputType](StringRef Path,
81-
StringRef Bytes) -> Error {
82-
return storeImpl(Path, Bytes, InputFilename, OutputType);
80+
[this, InputIndex, OutputType](StringRef Path,
81+
StringRef Bytes) -> Error {
82+
return storeImpl(Path, Bytes, InputIndex, OutputType);
8383
});
8484
}
8585

8686
void initBackend(const FrontendInputsAndOutputs &InputsAndOutputs);
8787

88-
Error storeImpl(StringRef Path, StringRef Bytes, StringRef CorrespondingInput,
88+
Error storeImpl(StringRef Path, StringRef Bytes, unsigned InputIndex,
8989
file_types::ID OutputKind);
9090

91-
Error finalizeCacheKeysFor(StringRef Input);
91+
Error finalizeCacheKeysFor(unsigned InputIndex);
9292

9393
private:
9494
friend class SwiftCASOutputBackend;
@@ -98,8 +98,11 @@ class SwiftCASOutputBackend::Implementation {
9898
const FrontendInputsAndOutputs &InputsAndOutputs;
9999
FrontendOptions::ActionType Action;
100100

101-
StringMap<std::pair<const InputFile &, file_types::ID>> OutputToInputMap;
102-
StringMap<DenseMap<file_types::ID, ObjectRef>> OutputRefs;
101+
// Map from output path to the input index and output kind.
102+
StringMap<std::pair<unsigned, file_types::ID>> OutputToInputMap;
103+
104+
// A vector of output refs where the index is the input index.
105+
SmallVector<DenseMap<file_types::ID, ObjectRef>> OutputRefs;
103106
};
104107

105108
SwiftCASOutputBackend::SwiftCASOutputBackend(
@@ -127,14 +130,14 @@ file_types::ID SwiftCASOutputBackend::getOutputFileType(StringRef Path) const {
127130
}
128131

129132
Error SwiftCASOutputBackend::storeImpl(StringRef Path, StringRef Bytes,
130-
StringRef CorrespondingInput,
133+
unsigned InputIndex,
131134
file_types::ID OutputKind) {
132-
return Impl.storeImpl(Path, Bytes, CorrespondingInput, OutputKind);
135+
return Impl.storeImpl(Path, Bytes, InputIndex, OutputKind);
133136
}
134137

135-
Error SwiftCASOutputBackend::storeCachedDiagnostics(StringRef InputFile,
138+
Error SwiftCASOutputBackend::storeCachedDiagnostics(unsigned InputIndex,
136139
StringRef Bytes) {
137-
return storeImpl("<cached-diagnostics>", Bytes, InputFile,
140+
return storeImpl("<cached-diagnostics>", Bytes, InputIndex,
138141
file_types::ID::TY_CachedDiagnostics);
139142
}
140143

@@ -145,57 +148,63 @@ void SwiftCASOutputBackend::Implementation::initBackend(
145148
// input it actually comes from. Maybe the solution is just not to cache
146149
// any commands write output to `-`.
147150
file_types::ID mainOutputType = InputsAndOutputs.getPrincipalOutputType();
148-
auto addInput = [&](const InputFile &Input) {
151+
auto addInput = [&](const InputFile &Input, unsigned Index) {
149152
if (!Input.outputFilename().empty())
150153
OutputToInputMap.insert(
151-
{Input.outputFilename(), {Input, mainOutputType}});
154+
{Input.outputFilename(), {Index, mainOutputType}});
152155
Input.getPrimarySpecificPaths()
153156
.SupplementaryOutputs.forEachSetOutputAndType(
154157
[&](const std::string &Out, file_types::ID ID) {
155158
if (!file_types::isProducedFromDiagnostics(ID))
156-
OutputToInputMap.insert({Out, {Input, ID}});
159+
OutputToInputMap.insert({Out, {Index, ID}});
157160
});
158161
};
159-
llvm::for_each(InputsAndOutputs.getAllInputs(), addInput);
162+
163+
for (unsigned idx = 0; idx < InputsAndOutputs.getAllInputs().size(); ++idx)
164+
addInput(InputsAndOutputs.getAllInputs()[idx], idx);
160165

161166
// FIXME: Cached diagnostics is associated with the first output producing
162167
// input file.
163-
OutputToInputMap.insert({"<cached-diagnostics>",
164-
{InputsAndOutputs.getFirstOutputProducingInput(),
165-
file_types::TY_CachedDiagnostics}});
168+
OutputToInputMap.insert(
169+
{"<cached-diagnostics>",
170+
{InputsAndOutputs.getIndexOfFirstOutputProducingInput(),
171+
file_types::TY_CachedDiagnostics}});
172+
173+
// Resize the output refs to hold all inputs.
174+
OutputRefs.resize(InputsAndOutputs.getAllInputs().size());
166175
}
167176

168177
Error SwiftCASOutputBackend::Implementation::storeImpl(
169-
StringRef Path, StringRef Bytes, StringRef CorrespondingInput,
178+
StringRef Path, StringRef Bytes, unsigned InputIndex,
170179
file_types::ID OutputKind) {
171180
Optional<ObjectRef> BytesRef;
172181
if (Error E = CAS.storeFromString(None, Bytes).moveInto(BytesRef))
173182
return E;
174183

175184
LLVM_DEBUG(llvm::dbgs() << "DEBUG: producing CAS output of type \'"
176185
<< file_types::getTypeName(OutputKind)
177-
<< "\' for input \'" << CorrespondingInput << "\': \'"
186+
<< "\' for input \'" << InputIndex << "\': \'"
178187
<< CAS.getID(*BytesRef).toString() << "\'\n";);
179188

180-
OutputRefs[CorrespondingInput].insert({OutputKind, *BytesRef});
189+
OutputRefs[InputIndex].insert({OutputKind, *BytesRef});
181190

182-
return finalizeCacheKeysFor(CorrespondingInput);
191+
return finalizeCacheKeysFor(InputIndex);
183192
}
184193

185194
Error SwiftCASOutputBackend::Implementation::finalizeCacheKeysFor(
186-
StringRef Input) {
187-
auto Entry = OutputRefs.find(Input);
188-
assert(Entry != OutputRefs.end() && "Unexpected input");
195+
unsigned InputIndex) {
196+
auto ProducedOutputs = OutputRefs[InputIndex];
197+
assert(!ProducedOutputs.empty() && "Expect outputs for this input");
189198

190199
// If not all outputs for the input are emitted, return.
191200
if (!llvm::all_of(OutputToInputMap, [&](auto &E) {
192-
return (E.second.first.getFileName() != Input ||
193-
Entry->second.count(E.second.second));
201+
return (E.second.first != InputIndex ||
202+
ProducedOutputs.count(E.second.second));
194203
}))
195204
return Error::success();
196205

197206
std::vector<std::pair<file_types::ID, ObjectRef>> OutputsForInput;
198-
llvm::for_each(Entry->second, [&OutputsForInput](auto E) {
207+
llvm::for_each(ProducedOutputs, [&OutputsForInput](auto E) {
199208
OutputsForInput.emplace_back(E.first, E.second);
200209
});
201210
// Sort to a stable ordering for deterministic output cache object.
@@ -237,14 +246,14 @@ Error SwiftCASOutputBackend::Implementation::finalizeCacheKeysFor(
237246
return Err;
238247
}
239248

240-
auto CacheKey = createCompileJobCacheKeyForOutput(CAS, BaseKey, Input);
249+
auto CacheKey = createCompileJobCacheKeyForOutput(CAS, BaseKey, InputIndex);
241250
if (!CacheKey)
242251
return CacheKey.takeError();
243252

244-
LLVM_DEBUG(llvm::dbgs() << "DEBUG: writing cache entry for input \'" << Input
245-
<< "\': \'" << CAS.getID(*CacheKey).toString()
246-
<< "\' => \'" << CAS.getID(*Result).toString()
247-
<< "\'\n";);
253+
LLVM_DEBUG(llvm::dbgs() << "DEBUG: writing cache entry for input \'"
254+
<< InputIndex << "\': \'"
255+
<< CAS.getID(*CacheKey).toString() << "\' => \'"
256+
<< CAS.getID(*Result).toString() << "\'\n";);
248257

249258
if (auto E = Cache.put(CAS.getID(*CacheKey), CAS.getID(*Result)))
250259
return E;

lib/Frontend/CachedDiagnostics.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -777,8 +777,7 @@ CachingDiagnosticsProcessor::CachingDiagnosticsProcessor(
777777
auto Err = Instance.getCASOutputBackend().storeCachedDiagnostics(
778778
Instance.getInvocation()
779779
.getFrontendOptions()
780-
.InputsAndOutputs.getFirstOutputProducingInput()
781-
.getFileName(),
780+
.InputsAndOutputs.getIndexOfFirstOutputProducingInput(),
782781
Content);
783782

784783
if (Err) {

lib/Frontend/CachingUtils.cpp

+20-11
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,12 @@ bool replayCachedCompilerOutputs(
145145
Optional<OutputEntry> DiagnosticsOutput;
146146

147147
auto replayOutputsForInputFile = [&](const std::string &InputPath,
148+
unsigned InputIndex,
148149
const DenseMap<file_types::ID,
149150
std::string> &Outputs) {
150151
auto lookupFailed = [&CanReplayAllOutput] { CanReplayAllOutput = false; };
151-
auto OutputKey = createCompileJobCacheKeyForOutput(CAS, BaseKey, InputPath);
152+
auto OutputKey =
153+
createCompileJobCacheKeyForOutput(CAS, BaseKey, InputIndex);
152154

153155
if (!OutputKey) {
154156
Diag.diagnose(SourceLoc(), diag::error_cas,
@@ -199,7 +201,8 @@ bool replayCachedCompilerOutputs(
199201
}
200202
};
201203

202-
auto replayOutputFromInput = [&](const InputFile &Input) {
204+
auto replayOutputFromInput = [&](const InputFile &Input,
205+
unsigned InputIndex) {
203206
auto InputPath = Input.getFileName();
204207
DenseMap<file_types::ID, std::string> Outputs;
205208
if (!Input.outputFilename().empty())
@@ -225,27 +228,33 @@ bool replayCachedCompilerOutputs(
225228
Outputs.try_emplace(file_types::ID::TY_CachedDiagnostics,
226229
"<cached-diagnostics>");
227230

228-
return replayOutputsForInputFile(InputPath, Outputs);
231+
return replayOutputsForInputFile(InputPath, InputIndex, Outputs);
229232
};
230233

234+
auto AllInputs = InputsAndOutputs.getAllInputs();
231235
// If there are primary inputs, look up only the primary input files.
232236
// Otherwise, prepare to do cache lookup for all inputs.
233-
if (InputsAndOutputs.hasPrimaryInputs())
234-
InputsAndOutputs.forEachPrimaryInput([&](const InputFile &File) {
235-
replayOutputFromInput(File);
236-
return false;
237-
});
238-
else
239-
llvm::for_each(InputsAndOutputs.getAllInputs(), replayOutputFromInput);
237+
for (unsigned Index = 0; Index < AllInputs.size(); ++Index) {
238+
const auto &Input = AllInputs[Index];
239+
if (InputsAndOutputs.hasPrimaryInputs() && !Input.isPrimary())
240+
continue;
241+
242+
replayOutputFromInput(Input, Index);
243+
}
244+
245+
if (!CanReplayAllOutput)
246+
return false;
240247

241248
// If there is not diagnostic output, this is a job that produces no output
242249
// and only diagnostics, like `typecheck-module-from-interface`, look up
243250
// diagnostics from first file.
244251
if (!DiagnosticsOutput)
245252
replayOutputsForInputFile(
246-
InputsAndOutputs.getFirstOutputProducingInput().getFileName(),
253+
"<cached-diagnostics>",
254+
InputsAndOutputs.getIndexOfFirstOutputProducingInput(),
247255
{{file_types::ID::TY_CachedDiagnostics, "<cached-diagnostics>"}});
248256

257+
// Check again to make sure diagnostics is fetched successfully.
249258
if (!CanReplayAllOutput)
250259
return false;
251260

lib/Frontend/CompileJobCacheKey.cpp

+11-7
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "llvm/CAS/ObjectStore.h"
2424
#include "llvm/Option/ArgList.h"
2525
#include "llvm/Option/OptTable.h"
26+
#include "llvm/Support/EndianStream.h"
2627
#include "llvm/Support/Error.h"
2728
#include "llvm/Support/MemoryBuffer.h"
2829

@@ -84,13 +85,16 @@ llvm::Expected<llvm::cas::ObjectRef> swift::createCompileJobBaseCacheKey(
8485
return Out.takeError();
8586
}
8687

87-
llvm::Expected<llvm::cas::ObjectRef> swift::createCompileJobCacheKeyForOutput(
88-
llvm::cas::ObjectStore &CAS, llvm::cas::ObjectRef BaseKey,
89-
StringRef ProducingInput) {
90-
SmallString<256> OutputInfo;
88+
llvm::Expected<llvm::cas::ObjectRef>
89+
swift::createCompileJobCacheKeyForOutput(llvm::cas::ObjectStore &CAS,
90+
llvm::cas::ObjectRef BaseKey,
91+
unsigned InputIndex) {
92+
std::string InputInfo;
93+
llvm::raw_string_ostream OS(InputInfo);
9194

92-
// CacheKey is the producting input + the base key.
93-
OutputInfo.append(ProducingInput);
95+
// CacheKey is the index of the producting input + the base key.
96+
// Encode the unsigned value as little endian in the field.
97+
llvm::support::endian::write<uint32_t>(OS, InputIndex, llvm::support::little);
9498

95-
return CAS.storeFromString({BaseKey}, OutputInfo);
99+
return CAS.storeFromString({BaseKey}, OS.str());
96100
}

0 commit comments

Comments
 (0)