Skip to content

Commit c7d66b8

Browse files
Teach swift to compute cache key for compiler outputs
Teach swift how to serialize its input into CAS to create a cache key for compiler outputs. To compute the cache key for the output, it first needs to compute a base-key for the compiler invocation. The base key is computed from: swift compiler version and the command-line arguments for the invocation. Each compiler output from swift will gets its own key. The key for the output is computed from: the base key for the compiler invocation + the primary input for the output + the output type.
1 parent b9f9eeb commit c7d66b8

File tree

13 files changed

+226
-7
lines changed

13 files changed

+226
-7
lines changed

include/swift/Basic/FileTypes.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,5 +98,8 @@ TYPE("pch", PCH, "pch", "")
9898
TYPE("none", Nothing, "", "")
9999

100100
TYPE("abi-baseline-json", SwiftABIDescriptor, "abi.json", "")
101+
TYPE("fixit", SwiftFixIt, "", "")
102+
TYPE("module-semantic-info", ModuleSemanticInfo, "", "")
103+
TYPE("cached-diagnostics", CachedDiagnostics, "", "")
101104

102105
#undef TYPE

include/swift/Basic/SupplementaryOutputPaths.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef SWIFT_FRONTEND_SUPPLEMENTARYOUTPUTPATHS_H
1414
#define SWIFT_FRONTEND_SUPPLEMENTARYOUTPUTPATHS_H
1515

16+
#include "swift/Basic/FileTypes.h"
1617
#include "swift/Basic/LLVM.h"
1718
#include "llvm/IR/Function.h"
1819

@@ -201,6 +202,48 @@ struct SupplementaryOutputPaths {
201202
fn(ModuleSemanticInfoOutputPath);
202203
}
203204

205+
void forEachSetOutputAndType(
206+
llvm::function_ref<void(const std::string &, file_types::ID)> fn) const {
207+
if (!ClangHeaderOutputPath.empty())
208+
fn(ClangHeaderOutputPath, file_types::ID::TY_ClangHeader);
209+
if (!ModuleOutputPath.empty())
210+
fn(ModuleOutputPath, file_types::ID::TY_SwiftModuleFile);
211+
if (!ModuleSourceInfoOutputPath.empty())
212+
fn(ModuleSourceInfoOutputPath, file_types::ID::TY_SwiftSourceInfoFile);
213+
if (!ModuleDocOutputPath.empty())
214+
fn(ModuleDocOutputPath, file_types::ID::TY_SwiftModuleDocFile);
215+
if (!DependenciesFilePath.empty())
216+
fn(DependenciesFilePath, file_types::ID::TY_Dependencies);
217+
if (!ReferenceDependenciesFilePath.empty())
218+
fn(ReferenceDependenciesFilePath, file_types::ID::TY_SwiftDeps);
219+
if (!SerializedDiagnosticsPath.empty())
220+
fn(SerializedDiagnosticsPath, file_types::ID::TY_SerializedDiagnostics);
221+
if (!FixItsOutputPath.empty())
222+
fn(FixItsOutputPath, file_types::ID::TY_SwiftFixIt);
223+
if (!LoadedModuleTracePath.empty())
224+
fn(LoadedModuleTracePath, file_types::ID::TY_ModuleTrace);
225+
if (!TBDPath.empty())
226+
fn(TBDPath, file_types::ID::TY_TBD);
227+
if (!ModuleInterfaceOutputPath.empty())
228+
fn(ModuleInterfaceOutputPath,
229+
file_types::ID::TY_SwiftModuleInterfaceFile);
230+
if (!PrivateModuleInterfaceOutputPath.empty())
231+
fn(PrivateModuleInterfaceOutputPath,
232+
file_types::ID::TY_PrivateSwiftModuleInterfaceFile);
233+
if (!ModuleSummaryOutputPath.empty())
234+
fn(ModuleSummaryOutputPath, file_types::ID::TY_SwiftModuleSummaryFile);
235+
if (!ABIDescriptorOutputPath.empty())
236+
fn(ABIDescriptorOutputPath, file_types::ID::TY_SwiftABIDescriptor);
237+
if (!ConstValuesOutputPath.empty())
238+
fn(ConstValuesOutputPath, file_types::ID::TY_ConstValues);
239+
if (!YAMLOptRecordPath.empty())
240+
fn(YAMLOptRecordPath, file_types::ID::TY_YAMLOptRecord);
241+
if (!BitstreamOptRecordPath.empty())
242+
fn(BitstreamOptRecordPath, file_types::ID::TY_BitstreamOptRecord);
243+
if (!ModuleSemanticInfoOutputPath.empty())
244+
fn(ModuleSemanticInfoOutputPath, file_types::ID::TY_ModuleSemanticInfo);
245+
}
246+
204247
bool empty() const {
205248
return ClangHeaderOutputPath.empty() && ModuleOutputPath.empty() &&
206249
ModuleDocOutputPath.empty() && DependenciesFilePath.empty() &&
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===--- CompileJobCacheKey.h - compile cache key methods -------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file contains declarations of utility methods for creating cache keys
14+
// for compilation jobs.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef SWIFT_COMPILEJOBCACHEKEY_H
19+
#define SWIFT_COMPILEJOBCACHEKEY_H
20+
21+
#include "swift/AST/DiagnosticEngine.h"
22+
#include "swift/Basic/FileTypes.h"
23+
#include "llvm/ADT/ArrayRef.h"
24+
#include "llvm/CAS/ObjectStore.h"
25+
#include "llvm/Support/Error.h"
26+
27+
namespace swift {
28+
29+
/// Compute CompileJobBaseKey from swift-frontend command-line arguments.
30+
/// CompileJobBaseKey represents the core inputs and arguments, and is used as a
31+
/// base to compute keys for each compiler outputs.
32+
// TODO: switch to create key from CompilerInvocation after we can canonicalize
33+
// arguments.
34+
llvm::Expected<llvm::cas::ObjectRef>
35+
createCompileJobBaseCacheKey(llvm::cas::ObjectStore &CAS,
36+
ArrayRef<const char *> Args);
37+
38+
/// 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.
41+
llvm::Expected<llvm::cas::ObjectRef>
42+
createCompileJobCacheKeyForOutput(llvm::cas::ObjectStore &CAS,
43+
llvm::cas::ObjectRef BaseKey,
44+
StringRef ProducingInput,
45+
file_types::ID OutputType);
46+
}
47+
48+
#endif

include/swift/Frontend/Frontend.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ class CompilerInstance {
455455
/// the file buffer provided by CAS needs to outlive the SourceMgr.
456456
std::shared_ptr<llvm::cas::ObjectStore> CAS;
457457
std::shared_ptr<llvm::cas::ActionCache> ResultCache;
458+
Optional<llvm::cas::ObjectRef> CompileJobBaseKey;
458459

459460
SourceManager SourceMgr;
460461
DiagnosticEngine Diagnostics{SourceMgr};
@@ -544,6 +545,9 @@ class CompilerInstance {
544545
std::shared_ptr<llvm::cas::ObjectStore> getSharedCASInstance() const {
545546
return CAS;
546547
}
548+
Optional<llvm::cas::ObjectRef> getCompilerBaseKey() const {
549+
return CompileJobBaseKey;
550+
}
547551

548552
ASTContext &getASTContext() { return *Context; }
549553
const ASTContext &getASTContext() const { return *Context; }
@@ -646,7 +650,8 @@ class CompilerInstance {
646650
}
647651

648652
/// Returns true if there was an error during setup.
649-
bool setup(const CompilerInvocation &Invocation, std::string &Error);
653+
bool setup(const CompilerInvocation &Invocation, std::string &Error,
654+
ArrayRef<const char *> Args = {});
650655

651656
const CompilerInvocation &getInvocation() const { return Invocation; }
652657

@@ -667,7 +672,7 @@ class CompilerInstance {
667672
bool setUpASTContextIfNeeded();
668673
void setupStatsReporter();
669674
void setupDependencyTrackerIfNeeded();
670-
bool setupCASIfNeeded();
675+
bool setupCASIfNeeded(ArrayRef<const char *> Args);
671676
void setupOutputBackend();
672677

673678
/// \return false if successful, true on error.

include/swift/Frontend/FrontendInputsAndOutputs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ class FrontendInputsAndOutputs {
3939
llvm::StringMap<unsigned> PrimaryInputsByName;
4040
std::vector<unsigned> PrimaryInputsInOrder;
4141

42+
/// The file type for main output files. Assuming all inputs produce the
43+
/// same kind of output.
44+
file_types::ID PrincipalOutputType = file_types::ID::TY_INVALID;
45+
4246
/// In Single-threaded WMO mode, all inputs are used
4347
/// both for importing and compiling.
4448
bool IsSingleThreadedWMO = false;
@@ -190,13 +194,17 @@ class FrontendInputsAndOutputs {
190194
ArrayRef<std::string> outputFiles,
191195
ArrayRef<SupplementaryOutputPaths> supplementaryOutputs,
192196
ArrayRef<std::string> outputFilesForIndexUnits = None);
197+
void setPrincipalOutputType(file_types::ID type) {
198+
PrincipalOutputType = type;
199+
}
193200

194201
public:
195202
unsigned countOfInputsProducingMainOutputs() const;
196203

197204
bool hasInputsProducingMainOutputs() const {
198205
return countOfInputsProducingMainOutputs() != 0;
199206
}
207+
file_types::ID getPrincipalOutputType() const { return PrincipalOutputType; }
200208

201209
const InputFile &firstInputProducingOutput() const;
202210
const InputFile &lastInputProducingOutput() const;

lib/Basic/FileTypes.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ bool file_types::isTextual(ID Id) {
109109
case file_types::TY_IndexData:
110110
case file_types::TY_BitstreamOptRecord:
111111
case file_types::TY_IndexUnitOutputPath:
112+
case file_types::TY_SwiftFixIt:
113+
case file_types::TY_ModuleSemanticInfo:
114+
case file_types::TY_CachedDiagnostics:
112115
return false;
113116
case file_types::TY_INVALID:
114117
llvm_unreachable("Invalid type ID.");
@@ -162,6 +165,9 @@ bool file_types::isAfterLLVM(ID Id) {
162165
case file_types::TY_IndexUnitOutputPath:
163166
case file_types::TY_SwiftABIDescriptor:
164167
case file_types::TY_ConstValues:
168+
case file_types::TY_SwiftFixIt:
169+
case file_types::TY_ModuleSemanticInfo:
170+
case file_types::TY_CachedDiagnostics:
165171
return false;
166172
case file_types::TY_INVALID:
167173
llvm_unreachable("Invalid type ID.");
@@ -215,6 +221,9 @@ bool file_types::isPartOfSwiftCompilation(ID Id) {
215221
case file_types::TY_IndexUnitOutputPath:
216222
case file_types::TY_SwiftABIDescriptor:
217223
case file_types::TY_ConstValues:
224+
case file_types::TY_SwiftFixIt:
225+
case file_types::TY_ModuleSemanticInfo:
226+
case file_types::TY_CachedDiagnostics:
218227
return false;
219228
case file_types::TY_INVALID:
220229
llvm_unreachable("Invalid type ID.");

lib/Driver/Driver.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,6 +2084,9 @@ void Driver::buildActions(SmallVectorImpl<const Action *> &TopLevelActions,
20842084
case file_types::TY_JSONFeatures:
20852085
case file_types::TY_SwiftABIDescriptor:
20862086
case file_types::TY_ConstValues:
2087+
case file_types::TY_SwiftFixIt:
2088+
case file_types::TY_ModuleSemanticInfo:
2089+
case file_types::TY_CachedDiagnostics:
20872090
// We could in theory handle assembly or LLVM input, but let's not.
20882091
// FIXME: What about LTO?
20892092
Diags.diagnose(SourceLoc(), diag::error_unexpected_input_file,

lib/Driver/ToolChains.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,9 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const {
753753
case file_types::TY_IndexUnitOutputPath:
754754
case file_types::TY_SwiftABIDescriptor:
755755
case file_types::TY_ConstValues:
756+
case file_types::TY_SwiftFixIt:
757+
case file_types::TY_ModuleSemanticInfo:
758+
case file_types::TY_CachedDiagnostics:
756759
llvm_unreachable("Output type can never be primary output.");
757760
case file_types::TY_INVALID:
758761
llvm_unreachable("Invalid type ID");
@@ -1014,6 +1017,9 @@ ToolChain::constructInvocation(const BackendJobAction &job,
10141017
case file_types::TY_IndexUnitOutputPath:
10151018
case file_types::TY_SwiftABIDescriptor:
10161019
case file_types::TY_ConstValues:
1020+
case file_types::TY_SwiftFixIt:
1021+
case file_types::TY_ModuleSemanticInfo:
1022+
case file_types::TY_CachedDiagnostics:
10171023
llvm_unreachable("Output type can never be primary output.");
10181024
case file_types::TY_INVALID:
10191025
llvm_unreachable("Invalid type ID");

lib/Frontend/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ add_swift_host_library(swiftFrontend STATIC
33
ArgsToFrontendInputsConverter.cpp
44
ArgsToFrontendOptionsConverter.cpp
55
ArgsToFrontendOutputsConverter.cpp
6+
CompileJobCacheKey.cpp
67
CompilerInvocation.cpp
78
DependencyVerifier.cpp
89
DiagnosticVerifier.cpp

lib/Frontend/CompileJobCacheKey.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//===--- CompileJobCacheKey.cpp - compile cache key methods ---------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file contains utility methods for creating compile job cache keys.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include <swift/Frontend/CompileJobCacheKey.h>
18+
#include <swift/Basic/Version.h>
19+
#include <llvm/ADT/SmallString.h>
20+
#include "llvm/ADT/STLExtras.h"
21+
#include "llvm/CAS/HierarchicalTreeBuilder.h"
22+
#include "llvm/CAS/ObjectStore.h"
23+
24+
using namespace swift;
25+
26+
// TODO: Rewrite this into CASNodeSchema.
27+
llvm::Expected<llvm::cas::ObjectRef> swift::createCompileJobBaseCacheKey(
28+
llvm::cas::ObjectStore &CAS, ArrayRef<const char *> Args) {
29+
SmallString<256> CommandLine;
30+
31+
// TODO: Improve this list.
32+
static const std::vector<std::string> removeArgAndNext = {
33+
"-o", "-supplementary-output-file-map", "-serialize-diagnostics-path",
34+
"-num-threads", "-cas-path"};
35+
36+
// Don't count the `-frontend` in the first location since only frontend
37+
// invocation can have a cache key.
38+
if (Args.size() > 1 && StringRef(Args.front()) == "-frontend")
39+
Args = Args.drop_front();
40+
41+
bool SkipNext = false;
42+
for (StringRef Arg : Args) {
43+
if (SkipNext) {
44+
SkipNext = false;
45+
continue;
46+
}
47+
if (llvm::is_contained(removeArgAndNext, Arg)) {
48+
SkipNext = true;
49+
continue;
50+
}
51+
CommandLine.append(Arg);
52+
CommandLine.push_back(0);
53+
}
54+
55+
llvm::cas::HierarchicalTreeBuilder Builder;
56+
auto CMD = CAS.storeFromString(None, CommandLine);
57+
if (!CMD)
58+
return CMD.takeError();
59+
Builder.push(*CMD, llvm::cas::TreeEntry::Regular, "command-line");
60+
61+
// FIXME: The version is maybe insufficient...
62+
auto Version = CAS.storeFromString(None, version::getSwiftFullVersion());
63+
if (!Version)
64+
return Version.takeError();
65+
Builder.push(*Version, llvm::cas::TreeEntry::Regular, "version");
66+
67+
if (auto Out = Builder.create(CAS))
68+
return Out->getRef();
69+
else
70+
return Out.takeError();
71+
}
72+
73+
llvm::Expected<llvm::cas::ObjectRef> swift::createCompileJobCacheKeyForOutput(
74+
llvm::cas::ObjectStore &CAS, llvm::cas::ObjectRef BaseKey,
75+
StringRef ProducingInput, file_types::ID OutputType) {
76+
SmallString<256> OutputInfo;
77+
78+
// Encode the OutputType as first byte, then append the input file path.
79+
OutputInfo.emplace_back((char)OutputType);
80+
OutputInfo.append(ProducingInput);
81+
82+
return CAS.storeFromString({BaseKey}, OutputInfo);
83+
}

lib/Frontend/Frontend.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "swift/Basic/FileTypes.h"
2727
#include "swift/Basic/SourceManager.h"
2828
#include "swift/Basic/Statistic.h"
29+
#include "swift/Frontend/CompileJobCacheKey.h"
2930
#include "swift/Frontend/ModuleInterfaceLoader.h"
3031
#include "swift/Parse/Lexer.h"
3132
#include "swift/SIL/SILModule.h"
@@ -398,7 +399,7 @@ void CompilerInstance::setupDependencyTrackerIfNeeded() {
398399
DepTracker->addDependency(path, /*isSystem=*/false);
399400
}
400401

401-
bool CompilerInstance::setupCASIfNeeded() {
402+
bool CompilerInstance::setupCASIfNeeded(ArrayRef<const char *> Args) {
402403
const auto &Opts = getInvocation().getFrontendOptions();
403404
if (!Opts.EnableCAS)
404405
return false;
@@ -429,6 +430,13 @@ bool CompilerInstance::setupCASIfNeeded() {
429430
}
430431
}
431432

433+
auto BaseKey = createCompileJobBaseCacheKey(*CAS, Args);
434+
if (!BaseKey) {
435+
Diagnostics.diagnose(SourceLoc(), diag::error_cas,
436+
toString(BaseKey.takeError()));
437+
return true;
438+
}
439+
CompileJobBaseKey = *BaseKey;
432440
return false;
433441
}
434442

@@ -452,10 +460,10 @@ void CompilerInstance::setupOutputBackend() {
452460
}
453461

454462
bool CompilerInstance::setup(const CompilerInvocation &Invoke,
455-
std::string &Error) {
463+
std::string &Error, ArrayRef<const char *> Args) {
456464
Invocation = Invoke;
457465

458-
if (setupCASIfNeeded()) {
466+
if (setupCASIfNeeded(Args)) {
459467
Error = "Setting up CAS failed";
460468
return true;
461469
}

lib/Frontend/FrontendInputsAndOutputs.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ FrontendInputsAndOutputs::FrontendInputsAndOutputs(
3939
addInput(input);
4040
IsSingleThreadedWMO = other.IsSingleThreadedWMO;
4141
ShouldRecoverMissingInputs = other.ShouldRecoverMissingInputs;
42+
OutputType = other.OutputType;
4243
}
4344

4445
FrontendInputsAndOutputs &FrontendInputsAndOutputs::
@@ -48,6 +49,7 @@ operator=(const FrontendInputsAndOutputs &other) {
4849
addInput(input);
4950
IsSingleThreadedWMO = other.IsSingleThreadedWMO;
5051
ShouldRecoverMissingInputs = other.ShouldRecoverMissingInputs;
52+
OutputType = other.OutputType;
5153
return *this;
5254
}
5355

0 commit comments

Comments
 (0)