Skip to content

Commit d341379

Browse files
committed
[clang][cas] Use module cache key as context hash
This improves canonicalization, reducing the number of cache misses when using compilation caching. For leaf modules this makes no difference since the extra compilation would be cached anyway, but when a module is imported the context hash is generally part of the path of the pcm, which then effects every downstream importer. This was previously causing spurious cache misses when changing options like -fmessage-length that are otherwise canonicalized away. rdar://105844077 (cherry picked from commit 2249fb1)
1 parent 71fa71b commit d341379

File tree

2 files changed

+83
-13
lines changed

2 files changed

+83
-13
lines changed

clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs(
9797
// differ between identical modules discovered from different translation
9898
// units.
9999
CI.getFrontendOpts().Inputs.clear();
100-
CI.getFrontendOpts().OutputFile.clear();
100+
// CAS: OutputFile cannot be empty when computing a cache key.
101+
CI.getFrontendOpts().OutputFile = "-";
101102
// FIXME: a build system may want to provide a new path.
102103
CI.getFrontendOpts().IndexUnitOutputPath.clear();
103104

@@ -117,7 +118,9 @@ ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs(
117118
CI.getDiagnosticOpts().DiagnosticSerializationFile = "-";
118119
if (!CI.getDependencyOutputOpts().OutputFile.empty())
119120
CI.getDependencyOutputOpts().OutputFile = "-";
120-
CI.getDependencyOutputOpts().Targets.clear();
121+
// CAS: -MT must be preserved for cache key.
122+
if (!CI.getDependencyOutputOpts().Targets.empty())
123+
CI.getDependencyOutputOpts().Targets = {"-"};
121124

122125
CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;
123126
CI.getLangOpts()->ModuleName = Deps.ID.ModuleName;
@@ -285,6 +288,22 @@ static std::string getModuleContextHash(const ModuleDeps &MD,
285288
HashBuilder;
286289
SmallString<32> Scratch;
287290

291+
auto FormatHash = [&](llvm::BLAKE3Result<16> Hash) {
292+
std::array<uint64_t, 2> Words;
293+
static_assert(sizeof(Hash) == sizeof(Words), "Hash must match Words");
294+
std::memcpy(Words.data(), Hash.data(), sizeof(Hash));
295+
return toString(llvm::APInt(sizeof(Words) * 8, Words), 36,
296+
/*Signed=*/false);
297+
};
298+
299+
if (MD.ModuleCacheKey) {
300+
// Cache keys have better canonicalization, so use them as the context hash.
301+
// This reduces the number of modules needed when compatible configurations
302+
// are used (e.g. change in -fmessage-length).
303+
HashBuilder.add(*MD.ModuleCacheKey);
304+
return FormatHash(HashBuilder.final());
305+
}
306+
288307
// Hash the compiler version and serialization version to ensure the module
289308
// will be readable.
290309
HashBuilder.add(getClangFullRepositoryVersion());
@@ -320,13 +339,30 @@ static std::string getModuleContextHash(const ModuleDeps &MD,
320339
}
321340

322341
HashBuilder.add(EagerLoadModules);
342+
return FormatHash(HashBuilder.final());
343+
}
323344

324-
llvm::BLAKE3Result<16> Hash = HashBuilder.final();
325-
std::array<uint64_t, 2> Words;
326-
static_assert(sizeof(Hash) == sizeof(Words), "Hash must match Words");
327-
std::memcpy(Words.data(), Hash.data(), sizeof(Hash));
328-
return toString(llvm::APInt(sizeof(Words) * 8, Words), 36, /*Signed=*/false);
345+
#ifndef NDEBUG
346+
static void checkCompileCacheKeyMatch(cas::ObjectStore &CAS,
347+
StringRef OldKeyStr, cas::CASID NewKey) {
348+
if (NewKey.toString() == OldKeyStr)
349+
return;
350+
351+
// Mismatched keys, report error.
352+
auto OldKey = CAS.parseID(OldKeyStr);
353+
if (!OldKey)
354+
llvm::report_fatal_error(OldKey.takeError());
355+
SmallString<256> Err;
356+
llvm::raw_svector_ostream OS(Err);
357+
OS << "Compile cache key for module changed; previously:";
358+
if (auto E = printCompileJobCacheKey(CAS, *OldKey, OS))
359+
OS << std::move(E);
360+
OS << "\nkey is now:";
361+
if (auto E = printCompileJobCacheKey(CAS, NewKey, OS))
362+
OS << std::move(E);
363+
llvm::report_fatal_error(OS.str());
329364
}
365+
#endif
330366

331367
void ModuleDepCollector::associateWithContextHash(const CompilerInvocation &CI,
332368
ModuleDeps &Deps) {
@@ -521,17 +557,27 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
521557
if (auto E = MDC.Controller.finalizeModuleInvocation(CI, MD))
522558
Diags.Report(diag::err_cas_depscan_failed) << std::move(E);
523559

560+
// Compute the cache key, if needed. Requires dependencies.
561+
if (MDC.ScanInstance.getFrontendOpts().CacheCompileJob) {
562+
auto &CAS = MDC.ScanInstance.getOrCreateObjectStore();
563+
if (auto Key = createCompileJobCacheKey(CAS, Diags, CI))
564+
MD.ModuleCacheKey = Key->toString();
565+
}
566+
524567
MDC.associateWithContextHash(CI, MD);
525568

526569
// Finish the compiler invocation. Requires dependencies and the context hash.
527570
MDC.addOutputPaths(CI, MD);
528571

529-
// Compute the cache key, if needed. Requires dependencies and outputs.
530-
if (MDC.ScanInstance.getFrontendOpts().CacheCompileJob) {
572+
#ifndef NDEBUG
573+
// Verify the key has not changed with final arguments.
574+
if (MD.ModuleCacheKey) {
531575
auto &CAS = MDC.ScanInstance.getOrCreateObjectStore();
532-
if (auto Key = createCompileJobCacheKey(CAS, Diags, CI))
533-
MD.ModuleCacheKey = Key->toString();
576+
auto Key = createCompileJobCacheKey(CAS, Diags, CI);
577+
assert(Key);
578+
checkCompileCacheKeyMatch(CAS, *MD.ModuleCacheKey, *Key);
534579
}
580+
#endif
535581

536582
MD.BuildArguments = CI.getCC1CommandLine();
537583

clang/test/ClangScanDeps/modules-cas-context-hash.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,38 @@
6363
//--- cdb1.json.template
6464
[{
6565
"directory": "DIR",
66-
"command": "clang -Xclang -fcas-plugin-path -Xclang /1 -Xclang -fcas-plugin-option -Xclang a=x -fsyntax-only DIR/tu.c -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache1",
66+
"arguments": [
67+
"clang",
68+
"-fsyntax-only",
69+
"DIR/tu.c",
70+
"-fmodules",
71+
"-fimplicit-module-maps",
72+
73+
"-Xclang", "-fcas-plugin-path", "-Xclang", "/1",
74+
"-Xclang", "-fcas-plugin-option", "-Xclang", "a=x",
75+
"-fmodules-cache-path=DIR/cache1",
76+
"-fmessage-length=1",
77+
"-fcolor-diagnostics",
78+
],
6779
"file": "DIR/tu.c"
6880
}]
6981

7082
//--- cdb2.json.template
7183
[{
7284
"directory": "DIR",
73-
"command": "clang -Xclang -fcas-plugin-path -Xclang /2 -Xclang -fcas-plugin-option -Xclang b=y -fsyntax-only DIR/tu.c -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache2",
85+
"arguments": [
86+
"clang",
87+
"-fsyntax-only",
88+
"DIR/tu.c",
89+
"-fmodules",
90+
"-fimplicit-module-maps",
91+
92+
"-Xclang", "-fcas-plugin-path", "-Xclang", "/2",
93+
"-Xclang", "-fcas-plugin-option", "-Xclang", "b=y",
94+
"-fmodules-cache-path=DIR/cache2",
95+
"-fmessage-length=2",
96+
"-fno-color-diagnostics",
97+
],
7498
"file": "DIR/tu.c"
7599
}]
76100

0 commit comments

Comments
 (0)