Skip to content

Commit 3599451

Browse files
committed
[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 (cherry picked from commit 7f482aa) Conflicts: clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
1 parent 73714d9 commit 3599451

File tree

3 files changed

+77
-8
lines changed

3 files changed

+77
-8
lines changed

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

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

330330
/// Compute the context hash for \p Deps, and create the mapping
331331
/// \c ModuleDepsByID[Deps.ID] = &Deps.
332-
void associateWithContextHash(const CowCompilerInvocation &CI,
332+
void associateWithContextHash(const CowCompilerInvocation &CI, bool IgnoreCWD,
333333
ModuleDeps &Deps);
334334
};
335335

clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,34 @@ static void optimizeDiagnosticOpts(DiagnosticOptions &Opts,
134134
Opts.Remarks.clear();
135135
}
136136

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

543568
// Save and restore options that should not affect the hash, e.g. the exact
544569
// contents of input files, or prefix mappings.
570+
auto &FSOpts = const_cast<FileSystemOptions &>(CI.getFileSystemOpts());
545571
auto &FEOpts = const_cast<FrontendOptions &>(CI.getFrontendOpts());
546572
auto &CASOpts = const_cast<CASOptions &>(CI.getCASOpts());
547573
llvm::SaveAndRestore RestoreCASFSRootID(FSOpts.CASFileSystemRootID, {});
@@ -594,9 +620,7 @@ static void checkCompileCacheKeyMatch(cas::ObjectStore &CAS,
594620
#endif
595621

596622
void ModuleDepCollector::associateWithContextHash(
597-
const CowCompilerInvocation &CI, ModuleDeps &Deps) {
598-
bool IgnoreCWD = any(OptimizeArgs & ScanningOptimizations::IgnoreCWD) &&
599-
isSafeToIgnoreCWD(CI);
623+
const CowCompilerInvocation &CI, bool IgnoreCWD, ModuleDeps &Deps) {
600624
Deps.ID.ContextHash =
601625
getModuleContextHash(Deps, CI, EagerLoadModules, IgnoreCWD,
602626
ScanInstance.getVirtualFileSystem());
@@ -810,6 +834,7 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
810834
}
811835
}
812836

837+
bool IgnoreCWD = false;
813838
CowCompilerInvocation CI =
814839
MDC.getInvocationAdjustedForModuleBuildWithoutOutputs(
815840
MD, [&](CowCompilerInvocation &BuildInvocation) {
@@ -819,10 +844,22 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
819844
*MDC.ScanInstance.getASTReader(), *MF,
820845
MDC.PrebuiltModuleVFSMap,
821846
MDC.OptimizeArgs);
847+
822848
if (any(MDC.OptimizeArgs & ScanningOptimizations::SystemWarnings))
823849
optimizeDiagnosticOpts(
824850
BuildInvocation.getMutDiagnosticOpts(),
825851
BuildInvocation.getFrontendOpts().IsSystemModule);
852+
853+
IgnoreCWD =
854+
any(MDC.OptimizeArgs & ScanningOptimizations::IgnoreCWD) &&
855+
isSafeToIgnoreCWD(BuildInvocation);
856+
if (IgnoreCWD) {
857+
llvm::ErrorOr<std::string> CWD =
858+
MDC.ScanInstance.getVirtualFileSystem()
859+
.getCurrentWorkingDirectory();
860+
if (CWD)
861+
optimizeCWD(BuildInvocation, *CWD);
862+
}
826863
});
827864

828865
auto &Diags = MDC.ScanInstance.getDiagnostics();
@@ -837,7 +874,7 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
837874
MD.ModuleCacheKey = Key->toString();
838875
}
839876

840-
MDC.associateWithContextHash(CI, MD);
877+
MDC.associateWithContextHash(CI, IgnoreCWD, MD);
841878

842879
// Finish the compiler invocation. Requires dependencies and the context hash.
843880
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)