Skip to content

[clang][deps] Make dependency directives getter thread-safe #136178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions clang/include/clang/Frontend/CompilerInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/PCHContainerOperations.h"
#include "clang/Frontend/Utils.h"
#include "clang/Lex/DependencyDirectivesScanner.h"
#include "clang/Lex/HeaderSearchOptions.h"
#include "clang/Lex/ModuleLoader.h"
#include "llvm/ADT/ArrayRef.h"
Expand Down Expand Up @@ -99,6 +100,9 @@ class CompilerInstance : public ModuleLoader {
/// The cache of PCM files.
IntrusiveRefCntPtr<ModuleCache> ModCache;

/// Functor for getting the dependency preprocessor directives of a file.
std::unique_ptr<DependencyDirectivesGetter> GetDependencyDirectives;

/// The preprocessor.
std::shared_ptr<Preprocessor> PP;

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

void setDependencyDirectivesGetter(
std::unique_ptr<DependencyDirectivesGetter> Getter) {
GetDependencyDirectives = std::move(Getter);
}

std::string getSpecificModuleCachePath(StringRef ModuleHash);
std::string getSpecificModuleCachePath() {
return getSpecificModuleCachePath(getInvocation().getModuleHash());
Expand Down
14 changes: 14 additions & 0 deletions clang/include/clang/Lex/DependencyDirectivesScanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "llvm/ADT/ArrayRef.h"

namespace clang {
class FileManager;

namespace tok {
enum TokenKind : unsigned short;
Expand Down Expand Up @@ -135,6 +136,19 @@ void printDependencyDirectivesAsSource(
ArrayRef<dependency_directives_scan::Directive> Directives,
llvm::raw_ostream &OS);

/// Functor that returns the dependency directives for a given file.
class DependencyDirectivesGetter {
public:
/// Clone the getter for a new \c FileManager instance.
virtual std::unique_ptr<DependencyDirectivesGetter>
cloneFor(FileManager &FileMgr) = 0;

/// Get the dependency directives for the given file.
virtual std::optional<ArrayRef<dependency_directives_scan::Directive>>
operator()(FileEntryRef File) = 0;

virtual ~DependencyDirectivesGetter() = default;
};
} // end namespace clang

#endif // LLVM_CLANG_LEX_DEPENDENCYDIRECTIVESSCANNER_H
10 changes: 10 additions & 0 deletions clang/include/clang/Lex/Preprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ class Preprocessor {
friend class VariadicMacroScopeGuard;

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

void setDependencyDirectivesGetter(DependencyDirectivesGetter &Get) {
GetDependencyDirectives = &Get;
}

void setPreprocessToken(bool Preprocess) { PreprocessToken = Preprocess; }

bool isMacroDefined(StringRef Id) {
Expand Down
13 changes: 0 additions & 13 deletions clang/include/clang/Lex/PreprocessorOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,19 +189,6 @@ class PreprocessorOptions {
/// with support for lifetime-qualified pointers.
ObjCXXARCStandardLibraryKind ObjCXXARCStandardLibrary = ARCXX_nolib;

/// Function for getting the dependency preprocessor directives of a file.
///
/// These are directives derived from a special form of lexing where the
/// source input is scanned for the preprocessor directives that might have an
/// effect on the dependencies for a compilation unit.
///
/// Enables a client to cache the directives for a file and provide them
/// across multiple compiler invocations.
/// FIXME: Allow returning an error.
std::function<std::optional<ArrayRef<dependency_directives_scan::Directive>>(
FileEntryRef)>
DependencyDirectivesForFile;

/// Set up preprocessor for RunAnalysis action.
bool SetUpStaticAnalyzer = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,16 @@ class DependencyScanningWorkerFilesystem
/// false if not (i.e. this entry is not a file or its scan fails).
bool ensureDirectiveTokensArePopulated(EntryRef Entry);

/// \returns The scanned preprocessor directive tokens of the file that are
/// used to speed up preprocessing, if available.
std::optional<ArrayRef<dependency_directives_scan::Directive>>
getDirectiveTokens(const Twine &Path) {
if (llvm::ErrorOr<EntryRef> Entry = getOrCreateFileSystemEntry(Path.str()))
if (ensureDirectiveTokensArePopulated(*Entry))
return Entry->getDirectiveTokens();
return std::nullopt;
}

/// Check whether \p Path exists. By default checks cached result of \c
/// status(), and falls back on FS if unable to do so.
bool exists(const Twine &Path) override;
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Frontend/CompilerInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,9 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) {
/*ShowAllHeaders=*/true, /*OutputPath=*/"",
/*ShowDepth=*/true, /*MSStyle=*/true);
}

if (GetDependencyDirectives)
PP->setDependencyDirectivesGetter(*GetDependencyDirectives);
}

std::string CompilerInstance::getSpecificModuleCachePath(StringRef ModuleHash) {
Expand Down Expand Up @@ -1237,6 +1240,10 @@ std::unique_ptr<CompilerInstance> CompilerInstance::cloneForModuleCompileImpl(
// Make a copy for the new instance.
Instance.FailedModules = FailedModules;

if (GetDependencyDirectives)
Instance.GetDependencyDirectives =
GetDependencyDirectives->cloneFor(Instance.getFileManager());

// If we're collecting module dependencies, we need to share a collector
// between all of the module CompilerInstances. Other than that, we don't
// want to produce any dependency output from the module build.
Expand Down
14 changes: 4 additions & 10 deletions clang/lib/Lex/PPLexerChange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,10 @@ bool Preprocessor::EnterSourceFile(FileID FID, ConstSearchDirIterator CurDir,
}

Lexer *TheLexer = new Lexer(FID, *InputFile, *this, IsFirstIncludeOfFile);
if (getPreprocessorOpts().DependencyDirectivesForFile &&
FID != PredefinesFileID) {
if (OptionalFileEntryRef File = SourceMgr.getFileEntryRefForID(FID)) {
if (std::optional<ArrayRef<dependency_directives_scan::Directive>>
DepDirectives =
getPreprocessorOpts().DependencyDirectivesForFile(*File)) {
TheLexer->DepDirectives = *DepDirectives;
}
}
}
if (GetDependencyDirectives && FID != PredefinesFileID)
if (OptionalFileEntryRef File = SourceMgr.getFileEntryRefForID(FID))
if (auto MaybeDepDirectives = (*GetDependencyDirectives)(*File))
TheLexer->DepDirectives = *MaybeDepDirectives;

EnterSourceFileWithLexer(TheLexer, CurDir);
return false;
Expand Down
42 changes: 31 additions & 11 deletions clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,32 @@ static void canonicalizeDefines(PreprocessorOptions &PPOpts) {
std::swap(PPOpts.Macros, NewMacros);
}

class ActualDependencyDirectivesGetter : public DependencyDirectivesGetter {
DependencyScanningWorkerFilesystem *DepFS;

public:
ActualDependencyDirectivesGetter(FileManager &FileMgr) : DepFS(nullptr) {
FileMgr.getVirtualFileSystem().visit([&](llvm::vfs::FileSystem &FS) {
auto *DFS = llvm::dyn_cast<DependencyScanningWorkerFilesystem>(&FS);
if (DFS) {
assert(!DepFS && "Found multiple scanning VFSs");
DepFS = DFS;
}
});
assert(DepFS && "Did not find scanning VFS");
}

std::unique_ptr<DependencyDirectivesGetter>
cloneFor(FileManager &FileMgr) override {
return std::make_unique<ActualDependencyDirectivesGetter>(FileMgr);
}

std::optional<ArrayRef<dependency_directives_scan::Directive>>
operator()(FileEntryRef File) override {
return DepFS->getDirectiveTokens(File.getName());
}
};

/// A clang tool that runs the preprocessor in a mode that's optimized for
/// dependency scanning for the given compiler invocation.
class DependencyScanningAction : public tooling::ToolAction {
Expand Down Expand Up @@ -424,6 +450,9 @@ class DependencyScanningAction : public tooling::ToolAction {
ScanInstance.getInvocation(), ScanInstance.getDiagnostics(),
DriverFileMgr->getVirtualFileSystemPtr());

// Create a new FileManager to match the invocation's FileSystemOptions.
auto *FileMgr = ScanInstance.createFileManager(FS);

// Use the dependency scanning optimized file system if requested to do so.
if (DepFS) {
StringRef ModulesCachePath =
Expand All @@ -433,19 +462,10 @@ class DependencyScanningAction : public tooling::ToolAction {
if (!ModulesCachePath.empty())
DepFS->setBypassedPathPrefix(ModulesCachePath);

ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =
[LocalDepFS = DepFS](FileEntryRef File)
-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
if (llvm::ErrorOr<EntryRef> Entry =
LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
return Entry->getDirectiveTokens();
return std::nullopt;
};
ScanInstance.setDependencyDirectivesGetter(
std::make_unique<ActualDependencyDirectivesGetter>(*FileMgr));
}

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

// Store a mapping of prebuilt module files and their properties like header
Expand Down
42 changes: 26 additions & 16 deletions clang/unittests/Lex/PPDependencyDirectivesTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,25 +103,33 @@ TEST_F(PPDependencyDirectivesTest, MacroGuard) {
SmallVector<dependency_directives_scan::Token> Tokens;
SmallVector<dependency_directives_scan::Directive> Directives;
};
SmallVector<std::unique_ptr<DepDirectives>> DepDirectivesObjects;

auto getDependencyDirectives = [&](FileEntryRef File)
-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
DepDirectivesObjects.push_back(std::make_unique<DepDirectives>());
StringRef Input = (*FileMgr.getBufferForFile(File))->getBuffer();
bool Err = scanSourceForDependencyDirectives(
Input, DepDirectivesObjects.back()->Tokens,
DepDirectivesObjects.back()->Directives);
EXPECT_FALSE(Err);
return llvm::ArrayRef(DepDirectivesObjects.back()->Directives);
};

PreprocessorOptions PPOpts;
PPOpts.DependencyDirectivesForFile = [&](FileEntryRef File)
-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
return getDependencyDirectives(File);
class TestDependencyDirectivesGetter : public DependencyDirectivesGetter {
FileManager &FileMgr;
SmallVector<std::unique_ptr<DepDirectives>> DepDirectivesObjects;

public:
TestDependencyDirectivesGetter(FileManager &FileMgr) : FileMgr(FileMgr) {}

std::unique_ptr<DependencyDirectivesGetter>
cloneFor(FileManager &FileMgr) override {
return std::make_unique<TestDependencyDirectivesGetter>(FileMgr);
}

std::optional<ArrayRef<dependency_directives_scan::Directive>>
operator()(FileEntryRef File) override {
DepDirectivesObjects.push_back(std::make_unique<DepDirectives>());
StringRef Input = (*FileMgr.getBufferForFile(File))->getBuffer();
bool Err = scanSourceForDependencyDirectives(
Input, DepDirectivesObjects.back()->Tokens,
DepDirectivesObjects.back()->Directives);
EXPECT_FALSE(Err);
return DepDirectivesObjects.back()->Directives;
}
};
TestDependencyDirectivesGetter GetDependencyDirectives(FileMgr);

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

PP.setDependencyDirectivesGetter(GetDependencyDirectives);

SmallVector<StringRef> IncludedFiles;
PP.addPPCallbacks(std::make_unique<IncludeCollector>(PP, IncludedFiles));
PP.EnterMainSourceFile();
Expand Down
Loading