Skip to content

Commit 060f3f0

Browse files
authored
[clang][deps] Make dependency directives getter thread-safe (llvm#136178)
This PR fixes two issues in one go: 1. The dependency directives getter (a `std::function`) was being stored in `PreprocessorOptions`. This goes against the principle where the options classes are supposed to be value-objects representing the `-cc1` command line arguments. This is fixed by moving the getter directly to `CompilerInstance` and propagating it explicitly. 2. The getter was capturing the `ScanInstance` VFS. That's fine in synchronous implicit module builds where the same VFS instance is used throughout, but breaks down once you try to build modules asynchronously (which forces the use of separate VFS instances). This is fixed by explicitly passing a `FileManager` into the getter and extracting the right instance of the scanning VFS out of it.
1 parent 1041d54 commit 060f3f0

File tree

9 files changed

+111
-50
lines changed

9 files changed

+111
-50
lines changed

clang/include/clang/Frontend/CompilerInstance.h

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "clang/Frontend/CompilerInvocation.h"
1717
#include "clang/Frontend/PCHContainerOperations.h"
1818
#include "clang/Frontend/Utils.h"
19+
#include "clang/Lex/DependencyDirectivesScanner.h"
1920
#include "clang/Lex/HeaderSearchOptions.h"
2021
#include "clang/Lex/ModuleLoader.h"
2122
#include "llvm/ADT/ArrayRef.h"
@@ -99,6 +100,9 @@ class CompilerInstance : public ModuleLoader {
99100
/// The cache of PCM files.
100101
IntrusiveRefCntPtr<ModuleCache> ModCache;
101102

103+
/// Functor for getting the dependency preprocessor directives of a file.
104+
std::unique_ptr<DependencyDirectivesGetter> GetDependencyDirectives;
105+
102106
/// The preprocessor.
103107
std::shared_ptr<Preprocessor> PP;
104108

@@ -697,6 +701,11 @@ class CompilerInstance : public ModuleLoader {
697701
/// and replace any existing one with it.
698702
void createPreprocessor(TranslationUnitKind TUKind);
699703

704+
void setDependencyDirectivesGetter(
705+
std::unique_ptr<DependencyDirectivesGetter> Getter) {
706+
GetDependencyDirectives = std::move(Getter);
707+
}
708+
700709
std::string getSpecificModuleCachePath(StringRef ModuleHash);
701710
std::string getSpecificModuleCachePath() {
702711
return getSpecificModuleCachePath(getInvocation().getModuleHash());

clang/include/clang/Lex/DependencyDirectivesScanner.h

+14
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "llvm/ADT/ArrayRef.h"
2222

2323
namespace clang {
24+
class FileManager;
2425

2526
namespace tok {
2627
enum TokenKind : unsigned short;
@@ -135,6 +136,19 @@ void printDependencyDirectivesAsSource(
135136
ArrayRef<dependency_directives_scan::Directive> Directives,
136137
llvm::raw_ostream &OS);
137138

139+
/// Functor that returns the dependency directives for a given file.
140+
class DependencyDirectivesGetter {
141+
public:
142+
/// Clone the getter for a new \c FileManager instance.
143+
virtual std::unique_ptr<DependencyDirectivesGetter>
144+
cloneFor(FileManager &FileMgr) = 0;
145+
146+
/// Get the dependency directives for the given file.
147+
virtual std::optional<ArrayRef<dependency_directives_scan::Directive>>
148+
operator()(FileEntryRef File) = 0;
149+
150+
virtual ~DependencyDirectivesGetter() = default;
151+
};
138152
} // end namespace clang
139153

140154
#endif // LLVM_CLANG_LEX_DEPENDENCYDIRECTIVESSCANNER_H

clang/include/clang/Lex/Preprocessor.h

+10
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ class Preprocessor {
140140
friend class VariadicMacroScopeGuard;
141141

142142
llvm::unique_function<void(const clang::Token &)> OnToken;
143+
/// Functor for getting the dependency preprocessor directives of a file.
144+
///
145+
/// These are directives derived from a special form of lexing where the
146+
/// source input is scanned for the preprocessor directives that might have an
147+
/// effect on the dependencies for a compilation unit.
148+
DependencyDirectivesGetter *GetDependencyDirectives = nullptr;
143149
const PreprocessorOptions &PPOpts;
144150
DiagnosticsEngine *Diags;
145151
const LangOptions &LangOpts;
@@ -1326,6 +1332,10 @@ class Preprocessor {
13261332
OnToken = std::move(F);
13271333
}
13281334

1335+
void setDependencyDirectivesGetter(DependencyDirectivesGetter &Get) {
1336+
GetDependencyDirectives = &Get;
1337+
}
1338+
13291339
void setPreprocessToken(bool Preprocess) { PreprocessToken = Preprocess; }
13301340

13311341
bool isMacroDefined(StringRef Id) {

clang/include/clang/Lex/PreprocessorOptions.h

-13
Original file line numberDiff line numberDiff line change
@@ -189,19 +189,6 @@ class PreprocessorOptions {
189189
/// with support for lifetime-qualified pointers.
190190
ObjCXXARCStandardLibraryKind ObjCXXARCStandardLibrary = ARCXX_nolib;
191191

192-
/// Function for getting the dependency preprocessor directives of a file.
193-
///
194-
/// These are directives derived from a special form of lexing where the
195-
/// source input is scanned for the preprocessor directives that might have an
196-
/// effect on the dependencies for a compilation unit.
197-
///
198-
/// Enables a client to cache the directives for a file and provide them
199-
/// across multiple compiler invocations.
200-
/// FIXME: Allow returning an error.
201-
std::function<std::optional<ArrayRef<dependency_directives_scan::Directive>>(
202-
FileEntryRef)>
203-
DependencyDirectivesForFile;
204-
205192
/// Set up preprocessor for RunAnalysis action.
206193
bool SetUpStaticAnalyzer = false;
207194

clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h

+10
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,16 @@ class DependencyScanningWorkerFilesystem
379379
/// false if not (i.e. this entry is not a file or its scan fails).
380380
bool ensureDirectiveTokensArePopulated(EntryRef Entry);
381381

382+
/// \returns The scanned preprocessor directive tokens of the file that are
383+
/// used to speed up preprocessing, if available.
384+
std::optional<ArrayRef<dependency_directives_scan::Directive>>
385+
getDirectiveTokens(const Twine &Path) {
386+
if (llvm::ErrorOr<EntryRef> Entry = getOrCreateFileSystemEntry(Path.str()))
387+
if (ensureDirectiveTokensArePopulated(*Entry))
388+
return Entry->getDirectiveTokens();
389+
return std::nullopt;
390+
}
391+
382392
/// Check whether \p Path exists. By default checks cached result of \c
383393
/// status(), and falls back on FS if unable to do so.
384394
bool exists(const Twine &Path) override;

clang/lib/Frontend/CompilerInstance.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,9 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) {
536536
/*ShowAllHeaders=*/true, /*OutputPath=*/"",
537537
/*ShowDepth=*/true, /*MSStyle=*/true);
538538
}
539+
540+
if (GetDependencyDirectives)
541+
PP->setDependencyDirectivesGetter(*GetDependencyDirectives);
539542
}
540543

541544
std::string CompilerInstance::getSpecificModuleCachePath(StringRef ModuleHash) {
@@ -1246,6 +1249,10 @@ std::unique_ptr<CompilerInstance> CompilerInstance::cloneForModuleCompileImpl(
12461249
// Make a copy for the new instance.
12471250
Instance.FailedModules = FailedModules;
12481251

1252+
if (GetDependencyDirectives)
1253+
Instance.GetDependencyDirectives =
1254+
GetDependencyDirectives->cloneFor(Instance.getFileManager());
1255+
12491256
// If we're collecting module dependencies, we need to share a collector
12501257
// between all of the module CompilerInstances. Other than that, we don't
12511258
// want to produce any dependency output from the module build.

clang/lib/Lex/PPLexerChange.cpp

+4-10
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,10 @@ bool Preprocessor::EnterSourceFile(FileID FID, ConstSearchDirIterator CurDir,
9292
}
9393

9494
Lexer *TheLexer = new Lexer(FID, *InputFile, *this, IsFirstIncludeOfFile);
95-
if (getPreprocessorOpts().DependencyDirectivesForFile &&
96-
FID != PredefinesFileID) {
97-
if (OptionalFileEntryRef File = SourceMgr.getFileEntryRefForID(FID)) {
98-
if (std::optional<ArrayRef<dependency_directives_scan::Directive>>
99-
DepDirectives =
100-
getPreprocessorOpts().DependencyDirectivesForFile(*File)) {
101-
TheLexer->DepDirectives = *DepDirectives;
102-
}
103-
}
104-
}
95+
if (GetDependencyDirectives && FID != PredefinesFileID)
96+
if (OptionalFileEntryRef File = SourceMgr.getFileEntryRefForID(FID))
97+
if (auto MaybeDepDirectives = (*GetDependencyDirectives)(*File))
98+
TheLexer->DepDirectives = *MaybeDepDirectives;
10599

106100
EnterSourceFileWithLexer(TheLexer, CurDir);
107101
return false;

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

+31-11
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,32 @@ static void canonicalizeDefines(PreprocessorOptions &PPOpts) {
349349
std::swap(PPOpts.Macros, NewMacros);
350350
}
351351

352+
class ScanningDependencyDirectivesGetter : public DependencyDirectivesGetter {
353+
DependencyScanningWorkerFilesystem *DepFS;
354+
355+
public:
356+
ScanningDependencyDirectivesGetter(FileManager &FileMgr) : DepFS(nullptr) {
357+
FileMgr.getVirtualFileSystem().visit([&](llvm::vfs::FileSystem &FS) {
358+
auto *DFS = llvm::dyn_cast<DependencyScanningWorkerFilesystem>(&FS);
359+
if (DFS) {
360+
assert(!DepFS && "Found multiple scanning VFSs");
361+
DepFS = DFS;
362+
}
363+
});
364+
assert(DepFS && "Did not find scanning VFS");
365+
}
366+
367+
std::unique_ptr<DependencyDirectivesGetter>
368+
cloneFor(FileManager &FileMgr) override {
369+
return std::make_unique<ScanningDependencyDirectivesGetter>(FileMgr);
370+
}
371+
372+
std::optional<ArrayRef<dependency_directives_scan::Directive>>
373+
operator()(FileEntryRef File) override {
374+
return DepFS->getDirectiveTokens(File.getName());
375+
}
376+
};
377+
352378
/// A clang tool that runs the preprocessor in a mode that's optimized for
353379
/// dependency scanning for the given compiler invocation.
354380
class DependencyScanningAction : public tooling::ToolAction {
@@ -416,6 +442,9 @@ class DependencyScanningAction : public tooling::ToolAction {
416442
ScanInstance.getInvocation(), ScanInstance.getDiagnostics(),
417443
DriverFileMgr->getVirtualFileSystemPtr());
418444

445+
// Create a new FileManager to match the invocation's FileSystemOptions.
446+
auto *FileMgr = ScanInstance.createFileManager(FS);
447+
419448
// Use the dependency scanning optimized file system if requested to do so.
420449
if (DepFS) {
421450
StringRef ModulesCachePath =
@@ -425,19 +454,10 @@ class DependencyScanningAction : public tooling::ToolAction {
425454
if (!ModulesCachePath.empty())
426455
DepFS->setBypassedPathPrefix(ModulesCachePath);
427456

428-
ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =
429-
[LocalDepFS = DepFS](FileEntryRef File)
430-
-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
431-
if (llvm::ErrorOr<EntryRef> Entry =
432-
LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
433-
if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
434-
return Entry->getDirectiveTokens();
435-
return std::nullopt;
436-
};
457+
ScanInstance.setDependencyDirectivesGetter(
458+
std::make_unique<ScanningDependencyDirectivesGetter>(*FileMgr));
437459
}
438460

439-
// Create a new FileManager to match the invocation's FileSystemOptions.
440-
auto *FileMgr = ScanInstance.createFileManager(FS);
441461
ScanInstance.createSourceManager(*FileMgr);
442462

443463
// Create a collection of stable directories derived from the ScanInstance

clang/unittests/Lex/PPDependencyDirectivesTest.cpp

+26-16
Original file line numberDiff line numberDiff line change
@@ -103,25 +103,33 @@ TEST_F(PPDependencyDirectivesTest, MacroGuard) {
103103
SmallVector<dependency_directives_scan::Token> Tokens;
104104
SmallVector<dependency_directives_scan::Directive> Directives;
105105
};
106-
SmallVector<std::unique_ptr<DepDirectives>> DepDirectivesObjects;
107-
108-
auto getDependencyDirectives = [&](FileEntryRef File)
109-
-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
110-
DepDirectivesObjects.push_back(std::make_unique<DepDirectives>());
111-
StringRef Input = (*FileMgr.getBufferForFile(File))->getBuffer();
112-
bool Err = scanSourceForDependencyDirectives(
113-
Input, DepDirectivesObjects.back()->Tokens,
114-
DepDirectivesObjects.back()->Directives);
115-
EXPECT_FALSE(Err);
116-
return llvm::ArrayRef(DepDirectivesObjects.back()->Directives);
117-
};
118106

119-
PreprocessorOptions PPOpts;
120-
PPOpts.DependencyDirectivesForFile = [&](FileEntryRef File)
121-
-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
122-
return getDependencyDirectives(File);
107+
class TestDependencyDirectivesGetter : public DependencyDirectivesGetter {
108+
FileManager &FileMgr;
109+
SmallVector<std::unique_ptr<DepDirectives>> DepDirectivesObjects;
110+
111+
public:
112+
TestDependencyDirectivesGetter(FileManager &FileMgr) : FileMgr(FileMgr) {}
113+
114+
std::unique_ptr<DependencyDirectivesGetter>
115+
cloneFor(FileManager &FileMgr) override {
116+
return std::make_unique<TestDependencyDirectivesGetter>(FileMgr);
117+
}
118+
119+
std::optional<ArrayRef<dependency_directives_scan::Directive>>
120+
operator()(FileEntryRef File) override {
121+
DepDirectivesObjects.push_back(std::make_unique<DepDirectives>());
122+
StringRef Input = (*FileMgr.getBufferForFile(File))->getBuffer();
123+
bool Err = scanSourceForDependencyDirectives(
124+
Input, DepDirectivesObjects.back()->Tokens,
125+
DepDirectivesObjects.back()->Directives);
126+
EXPECT_FALSE(Err);
127+
return DepDirectivesObjects.back()->Directives;
128+
}
123129
};
130+
TestDependencyDirectivesGetter GetDependencyDirectives(FileMgr);
124131

132+
PreprocessorOptions PPOpts;
125133
HeaderSearchOptions HSOpts;
126134
TrivialModuleLoader ModLoader;
127135
HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get());
@@ -130,6 +138,8 @@ TEST_F(PPDependencyDirectivesTest, MacroGuard) {
130138
/*OwnsHeaderSearch =*/false);
131139
PP.Initialize(*Target);
132140

141+
PP.setDependencyDirectivesGetter(GetDependencyDirectives);
142+
133143
SmallVector<StringRef> IncludedFiles;
134144
PP.addPPCallbacks(std::make_unique<IncludeCollector>(PP, IncludedFiles));
135145
PP.EnterMainSourceFile();

0 commit comments

Comments
 (0)