Skip to content

Commit 7f482aa

Browse files
authored
[clang modules] Setting DebugCompilationDir when it is safe to ignore current working directory (llvm#128446)
This PR explicitly sets `DebugCompilationDir` to the system's root directory if it is safe to ignore the current working directory. This fixes a problem where a PCM file's embedded debug information can lead to compilation failure. The compiler may have decided it is indeed safe to ignore the current working directory. In this case, the PCM file's content is functionally correct regardless of the current working directory because no inputs use relative paths (see llvm#124786). However, a PCM may contain debug info. If debug info is requested, the compiler uses the current working directory value to set `DW_AT_comp_dir`. This may lead to the following situation: 1. Two different compilations need the same PCM file. 2. The PCM file is compiled assuming a working directory, which is embedded in the debug info, but otherwise has no effect. 3. The second compilation assumes a different working directory, and expects an identically-sized pcm file. However, it cannot find such a PCM, because the existing PCM file has been compiled assuming a different `DW_AT_comp_dir `, which is embedded in the debug info. This PR resets the `DebugCompilationDir` if it is functionally safe to ignore the working directory so the above situation is avoided, since all debug information will share the same working directory. rdar://145249881
1 parent 364b97f commit 7f482aa

File tree

3 files changed

+76
-8
lines changed

3 files changed

+76
-8
lines changed

clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ class ModuleDepCollector final : public DependencyCollector {
317317

318318
/// Compute the context hash for \p Deps, and create the mapping
319319
/// \c ModuleDepsByID[Deps.ID] = &Deps.
320-
void associateWithContextHash(const CowCompilerInvocation &CI,
320+
void associateWithContextHash(const CowCompilerInvocation &CI, bool IgnoreCWD,
321321
ModuleDeps &Deps);
322322
};
323323

clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,34 @@ static void optimizeDiagnosticOpts(DiagnosticOptions &Opts,
129129
Opts.Remarks.clear();
130130
}
131131

132+
static void optimizeCWD(CowCompilerInvocation &BuildInvocation, StringRef CWD) {
133+
BuildInvocation.getMutFileSystemOpts().WorkingDir.clear();
134+
if (BuildInvocation.getCodeGenOpts().DwarfVersion) {
135+
// It is necessary to explicitly set the DebugCompilationDir
136+
// to a common directory (e.g. root) if IgnoreCWD is true.
137+
// When IgnoreCWD is true, the module's content should not
138+
// depend on the current working directory. However, if dwarf
139+
// information is needed (when CGOpts.DwarfVersion is
140+
// non-zero), then CGOpts.DebugCompilationDir must be
141+
// populated, because otherwise the current working directory
142+
// will be automatically embedded in the dwarf information in
143+
// the pcm, contradicting the assumption that it is safe to
144+
// ignore the CWD. Thus in such cases,
145+
// CGOpts.DebugCompilationDir is explicitly set to a common
146+
// directory.
147+
// FIXME: It is still excessive to create a copy of
148+
// CodeGenOpts for each module. Since we do not modify the
149+
// CodeGenOpts otherwise per module, the following code
150+
// ends up generating identical CodeGenOpts for each module
151+
// with DebugCompilationDir pointing to the root directory.
152+
// We can optimize this away by creating a _single_ copy of
153+
// CodeGenOpts whose DebugCompilationDir points to the root
154+
// directory and reuse it across modules.
155+
BuildInvocation.getMutCodeGenOpts().DebugCompilationDir =
156+
llvm::sys::path::root_path(CWD);
157+
}
158+
}
159+
132160
static std::vector<std::string> splitString(std::string S, char Separator) {
133161
SmallVector<StringRef> Segments;
134162
StringRef(S).split(Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false);
@@ -489,11 +517,8 @@ static std::string getModuleContextHash(const ModuleDeps &MD,
489517
HashBuilder.add(getClangFullRepositoryVersion());
490518
HashBuilder.add(serialization::VERSION_MAJOR, serialization::VERSION_MINOR);
491519
llvm::ErrorOr<std::string> CWD = VFS.getCurrentWorkingDirectory();
492-
auto &FSOpts = const_cast<FileSystemOptions &>(CI.getFileSystemOpts());
493520
if (CWD && !IgnoreCWD)
494521
HashBuilder.add(*CWD);
495-
else
496-
FSOpts.WorkingDir.clear();
497522

498523
// Hash the BuildInvocation without any input files.
499524
SmallString<0> ArgVec;
@@ -524,9 +549,7 @@ static std::string getModuleContextHash(const ModuleDeps &MD,
524549
}
525550

526551
void ModuleDepCollector::associateWithContextHash(
527-
const CowCompilerInvocation &CI, ModuleDeps &Deps) {
528-
bool IgnoreCWD = any(OptimizeArgs & ScanningOptimizations::IgnoreCWD) &&
529-
isSafeToIgnoreCWD(CI);
552+
const CowCompilerInvocation &CI, bool IgnoreCWD, ModuleDeps &Deps) {
530553
Deps.ID.ContextHash =
531554
getModuleContextHash(Deps, CI, EagerLoadModules, IgnoreCWD,
532555
ScanInstance.getVirtualFileSystem());
@@ -726,6 +749,7 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
726749
MD.ModuleMapFileDeps.emplace_back(*ResolvedFilenameAsRequested);
727750
});
728751

752+
bool IgnoreCWD = false;
729753
CowCompilerInvocation CI =
730754
MDC.getInvocationAdjustedForModuleBuildWithoutOutputs(
731755
MD, [&](CowCompilerInvocation &BuildInvocation) {
@@ -735,13 +759,25 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
735759
*MDC.ScanInstance.getASTReader(), *MF,
736760
MDC.PrebuiltModuleVFSMap,
737761
MDC.OptimizeArgs);
762+
738763
if (any(MDC.OptimizeArgs & ScanningOptimizations::SystemWarnings))
739764
optimizeDiagnosticOpts(
740765
BuildInvocation.getMutDiagnosticOpts(),
741766
BuildInvocation.getFrontendOpts().IsSystemModule);
767+
768+
IgnoreCWD =
769+
any(MDC.OptimizeArgs & ScanningOptimizations::IgnoreCWD) &&
770+
isSafeToIgnoreCWD(BuildInvocation);
771+
if (IgnoreCWD) {
772+
llvm::ErrorOr<std::string> CWD =
773+
MDC.ScanInstance.getVirtualFileSystem()
774+
.getCurrentWorkingDirectory();
775+
if (CWD)
776+
optimizeCWD(BuildInvocation, *CWD);
777+
}
742778
});
743779

744-
MDC.associateWithContextHash(CI, MD);
780+
MDC.associateWithContextHash(CI, IgnoreCWD, MD);
745781

746782
// Finish the compiler invocation. Requires dependencies and the context hash.
747783
MDC.addOutputPaths(CI, MD);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// REQUIRES: shell
2+
3+
// RUN: rm -rf %t
4+
// RUN: split-file %s %t
5+
// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.in > %t/cdb.json
6+
// RUN: clang-scan-deps -compilation-database %t/cdb.json -format \
7+
// RUN: experimental-full > %t/result.json
8+
// RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck %s
9+
10+
//--- cdb.json.in
11+
[{
12+
"directory": "DIR",
13+
"command": "clang -c -g -gmodules DIR/tu.c -fmodules -fmodules-cache-path=DIR/cache -IDIR/include/ -fdebug-compilation-dir=DIR -o DIR/tu.o",
14+
"file": "DIR/tu.c"
15+
}]
16+
17+
//--- include/module.modulemap
18+
module mod {
19+
header "mod.h"
20+
}
21+
22+
//--- include/mod.h
23+
24+
//--- tu.c
25+
#include "mod.h"
26+
27+
// Check the -fdebug-compilation-dir used for the module is the root
28+
// directory when current working directory optimization is in effect.
29+
// CHECK: "modules": [
30+
// CHECK: "command-line": [
31+
// CHECK: "-fdebug-compilation-dir={{\/|.*:(\\)?}}",
32+
// CHECK: "translation-units": [

0 commit comments

Comments
 (0)