Skip to content

Commit 2249fb1

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
1 parent faf71f2 commit 2249fb1

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
@@ -98,7 +98,8 @@ ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs(
9898
// differ between identical modules discovered from different translation
9999
// units.
100100
CI.getFrontendOpts().Inputs.clear();
101-
CI.getFrontendOpts().OutputFile.clear();
101+
// CAS: OutputFile cannot be empty when computing a cache key.
102+
CI.getFrontendOpts().OutputFile = "-";
102103
// FIXME: a build system may want to provide a new path.
103104
CI.getFrontendOpts().IndexUnitOutputPath.clear();
104105

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

123126
CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;
124127
CI.getFrontendOpts().ARCMTAction = FrontendOptions::ARCMT_None;
@@ -289,6 +292,22 @@ static std::string getModuleContextHash(const ModuleDeps &MD,
289292
HashBuilder;
290293
SmallString<32> Scratch;
291294

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

326345
HashBuilder.add(EagerLoadModules);
346+
return FormatHash(HashBuilder.final());
347+
}
327348

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

335371
void ModuleDepCollector::associateWithContextHash(const CompilerInvocation &CI,
336372
ModuleDeps &Deps) {
@@ -552,17 +588,27 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
552588
if (auto E = MDC.Controller.finalizeModuleInvocation(CI, MD))
553589
Diags.Report(diag::err_cas_depscan_failed) << std::move(E);
554590

591+
// Compute the cache key, if needed. Requires dependencies.
592+
if (MDC.ScanInstance.getFrontendOpts().CacheCompileJob) {
593+
auto &CAS = MDC.ScanInstance.getOrCreateObjectStore();
594+
if (auto Key = createCompileJobCacheKey(CAS, Diags, CI))
595+
MD.ModuleCacheKey = Key->toString();
596+
}
597+
555598
MDC.associateWithContextHash(CI, MD);
556599

557600
// Finish the compiler invocation. Requires dependencies and the context hash.
558601
MDC.addOutputPaths(CI, MD);
559602

560-
// Compute the cache key, if needed. Requires dependencies and outputs.
561-
if (MDC.ScanInstance.getFrontendOpts().CacheCompileJob) {
603+
#ifndef NDEBUG
604+
// Verify the key has not changed with final arguments.
605+
if (MD.ModuleCacheKey) {
562606
auto &CAS = MDC.ScanInstance.getOrCreateObjectStore();
563-
if (auto Key = createCompileJobCacheKey(CAS, Diags, CI))
564-
MD.ModuleCacheKey = Key->toString();
607+
auto Key = createCompileJobCacheKey(CAS, Diags, CI);
608+
assert(Key);
609+
checkCompileCacheKeyMatch(CAS, *MD.ModuleCacheKey, *Key);
565610
}
611+
#endif
566612

567613
MD.BuildArguments = CI.getCC1CommandLine();
568614

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)