Skip to content

[clang][ScanDeps] cherry-pick VFS optimization fixes from upstream #8292

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 2 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/DiagnosticSerializationKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ def err_pch_diagopt_mismatch : Error<"%0 is currently enabled, but was not in "
"the PCH file">;
def err_pch_modulecache_mismatch : Error<"PCH was compiled with module cache "
"path '%0', but the path is currently '%1'">;
def err_pch_vfsoverlay_mismatch : Error<"PCH was compiled with different VFS overlay files than are currently in use">;
def warn_pch_vfsoverlay_mismatch : Warning<
"PCH was compiled with different VFS overlay files than are currently in use">,
InGroup<DiagGroup<"pch-vfs-diff">>;
def note_pch_vfsoverlay_files : Note<"%select{PCH|current translation unit}0 has the following VFS overlays:\n%1">;
def note_pch_vfsoverlay_empty : Note<"%select{PCH|current translation unit}0 has no VFS overlays">;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ enum class ScanningOptimizations {
Macros = 8,

DSS_LAST_BITMASK_ENUM(Macros),
Default = All
Default = All,
FullIncludeTreeIrrelevant = HeaderSearch | VFS,
};

#undef DSS_LAST_BITMASK_ENUM
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ struct ModuleDeps {
BuildInfo;
};

using PrebuiltModuleVFSMapT = llvm::StringMap<llvm::StringSet<>>;

class ModuleDepCollector;

/// Callback that records textual includes and direct modular includes/imports
Expand Down Expand Up @@ -226,6 +228,7 @@ class ModuleDepCollector final : public DependencyCollector {
CompilerInstance &ScanInstance, DependencyConsumer &C,
DependencyActionController &Controller,
CompilerInvocation OriginalCI,
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap,
ScanningOptimizations OptimizeArgs, bool EagerLoadModules,
bool IsStdModuleP1689Format);

Expand All @@ -245,6 +248,8 @@ class ModuleDepCollector final : public DependencyCollector {
DependencyConsumer &Consumer;
/// Callbacks for computing dependency information.
DependencyActionController &Controller;
/// Mapping from prebuilt AST files to their sorted list of VFS overlay files.
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
/// Path to the main source file.
std::string MainFile;
/// Hash identifying the compilation conditions of the current TU.
Expand Down
69 changes: 55 additions & 14 deletions clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
const HeaderSearchOptions &ExistingHSOpts,
DiagnosticsEngine *Diags,
const LangOptions &LangOpts) {
if (LangOpts.Modules) {
if (LangOpts.Modules && ExistingHSOpts.ModulesIncludeVFSUsage) {
if (HSOpts.VFSOverlayFiles != ExistingHSOpts.VFSOverlayFiles) {
if (Diags) {
Diags->Report(diag::err_pch_vfsoverlay_mismatch);
Diags->Report(diag::warn_pch_vfsoverlay_mismatch);
auto VFSNote = [&](int Type, ArrayRef<std::string> VFSOverlays) {
if (VFSOverlays.empty()) {
Diags->Report(diag::note_pch_vfsoverlay_empty) << Type;
Expand All @@ -92,7 +92,6 @@ static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
VFSNote(0, HSOpts.VFSOverlayFiles);
VFSNote(1, ExistingHSOpts.VFSOverlayFiles);
}
return true;
}
}
return false;
Expand All @@ -107,9 +106,11 @@ class PrebuiltModuleListener : public ASTReaderListener {
PrebuiltModuleListener(CompilerInstance &CI,
PrebuiltModuleFilesT &PrebuiltModuleFiles,
llvm::SmallVector<std::string> &NewModuleFiles,
PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
DiagnosticsEngine &Diags)
: CI(CI), PrebuiltModuleFiles(PrebuiltModuleFiles),
NewModuleFiles(NewModuleFiles), Diags(Diags) {}
NewModuleFiles(NewModuleFiles),
PrebuiltModuleVFSMap(PrebuiltModuleVFSMap), Diags(Diags) {}

bool needsImportVisitation() const override { return true; }

Expand All @@ -118,10 +119,19 @@ class PrebuiltModuleListener : public ASTReaderListener {
NewModuleFiles.push_back(Filename.str());
}

void visitModuleFile(StringRef Filename,
serialization::ModuleKind Kind) override {
CurrentFile = Filename;
}

bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
bool Complain) override {
return checkHeaderSearchPaths(
HSOpts, CI.getHeaderSearchOpts(), Complain ? &Diags : nullptr, CI.getLangOpts());
std::vector<std::string> VFSOverlayFiles = HSOpts.VFSOverlayFiles;
PrebuiltModuleVFSMap.insert(
{CurrentFile, llvm::StringSet<>(VFSOverlayFiles)});
return checkHeaderSearchPaths(HSOpts, CI.getHeaderSearchOpts(),
Complain ? &Diags : nullptr,
CI.getLangOpts());
}

bool readModuleCacheKey(StringRef ModuleName, StringRef Filename,
Expand All @@ -136,19 +146,25 @@ class PrebuiltModuleListener : public ASTReaderListener {
CompilerInstance &CI;
PrebuiltModuleFilesT &PrebuiltModuleFiles;
llvm::SmallVector<std::string> &NewModuleFiles;
PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap;
DiagnosticsEngine &Diags;
std::string CurrentFile;
};

/// Visit the given prebuilt module and collect all of the modules it
/// transitively imports and contributing input files.
static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
CompilerInstance &CI,
PrebuiltModuleFilesT &ModuleFiles,
PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
DiagnosticsEngine &Diags) {
// List of module files to be processed.
llvm::SmallVector<std::string> Worklist;
PrebuiltModuleListener Listener(CI, ModuleFiles, Worklist, Diags);
PrebuiltModuleListener Listener(CI, ModuleFiles, Worklist,
PrebuiltModuleVFSMap, Diags);

Listener.visitModuleFile(PrebuiltModuleFilename,
serialization::MK_ExplicitModule);
if (ASTReader::readASTFileControlBlock(
PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(),
CI.getPCHContainerReader(),
Expand All @@ -157,6 +173,7 @@ static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
return true;

while (!Worklist.empty()) {
Listener.visitModuleFile(Worklist.back(), serialization::MK_ExplicitModule);
if (ASTReader::readASTFileControlBlock(
Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
CI.getPCHContainerReader(),
Expand Down Expand Up @@ -193,8 +210,18 @@ static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
DiagOpts.ShowCarets = false;
// Don't write out diagnostic file.
DiagOpts.DiagnosticSerializationFile.clear();
// Don't emit warnings as errors (and all other warnings too).
DiagOpts.IgnoreWarnings = true;
// Don't emit warnings except for scanning specific warnings.
// TODO: It would be useful to add a more principled way to ignore all
// warnings that come from source code. The issue is that we need to
// ignore warnings that could be surpressed by
// `#pragma clang diagnostic`, while still allowing some scanning
// warnings for things we're not ready to turn into errors yet.
// See `test/ClangScanDeps/diagnostic-pragmas.c` for an example.
llvm::erase_if(DiagOpts.Warnings, [](StringRef Warning) {
return llvm::StringSwitch<bool>(Warning)
.Cases("pch-vfs-diff", "error=pch-vfs-diff", false)
.Default(true);
Comment on lines +222 to +223

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

llvm#82294 has an extra .StartsWith("no-error=", false) in here. Was there a reason for not including it? In my testing it seems to be necessary to avoid a test failure in ClangScanDeps/diagnostics.c, which checks for -no-error= being ignored.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahmishal: did something change about "please test llvm"? I am looking at the logs in https://ci.swift.org/view/Pull%20Requests/job/apple-llvm-project-pr-macos/ and only LLDB tests seem to run during those? I cannot find the LLVM or Clang tests (but the logs are 134MB, so I might be not looking in the right place).

});
}

// Clang implements -D and -U by splatting text into a predefines buffer. This
Expand Down Expand Up @@ -384,7 +411,13 @@ class DependencyScanningAction : public tooling::ToolAction {
CASOpts(CASOpts),
EmitDependencyFile(EmitDependencyFile),
DiagGenerationAsCompilation(DiagGenerationAsCompilation),
ModuleName(ModuleName), VerboseOS(VerboseOS) {}
ModuleName(ModuleName), VerboseOS(VerboseOS) {
// The FullIncludeTree output format completely subsumes header search and
// VFS optimizations due to how it works. Disable these optimizations so
// we're not doing unneeded work.
if (Format == ScanningOutputFormat::FullIncludeTree)
this->OptimizeArgs &= ~ScanningOptimizations::FullIncludeTreeIrrelevant;
}

bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
FileManager *FileMgr,
Expand Down Expand Up @@ -429,14 +462,19 @@ class DependencyScanningAction : public tooling::ToolAction {
if (VerboseOS)
ScanInstance.setVerboseOutputStream(*VerboseOS);

// Some DiagnosticConsumers require that finish() is called.
auto DiagConsumerFinisher =
llvm::make_scope_exit([DiagConsumer]() { DiagConsumer->finish(); });

ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath =
true;

ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
ScanInstance.getFrontendOpts().ModulesShareFileManager = false;
ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";
ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage = true;
ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage =
any(OptimizeArgs & ScanningOptimizations::VFS);

ScanInstance.setFileManager(FileMgr);
// Support for virtual file system overlays.
Expand All @@ -449,12 +487,13 @@ class DependencyScanningAction : public tooling::ToolAction {
// Store the list of prebuilt module files into header search options. This
// will prevent the implicit build to create duplicate modules and will
// force reuse of the existing prebuilt module files instead.
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
if (visitPrebuiltModule(
ScanInstance.getPreprocessorOpts().ImplicitPCHInclude,
ScanInstance,
ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
ScanInstance.getDiagnostics()))
PrebuiltModuleVFSMap, ScanInstance.getDiagnostics()))
return false;

// Use the dependency scanning optimized file system if requested to do so.
Expand Down Expand Up @@ -538,8 +577,8 @@ class DependencyScanningAction : public tooling::ToolAction {

MDC = std::make_shared<ModuleDepCollector>(
std::move(Opts), ScanInstance, Consumer, Controller,
OriginalInvocation, OptimizeArgs, EagerLoadModules,
Format == ScanningOutputFormat::P1689);
OriginalInvocation, std::move(PrebuiltModuleVFSMap), OptimizeArgs,
EagerLoadModules, Format == ScanningOutputFormat::P1689);
ScanInstance.addDependencyCollector(MDC);
ScanInstance.setGenModuleActionWrapper(
[&Controller = Controller](const FrontendOptions &Opts,
Expand Down Expand Up @@ -581,6 +620,8 @@ class DependencyScanningAction : public tooling::ToolAction {
if (ScanInstance.getDiagnostics().hasErrorOccurred())
return false;

// Each action is responsible for calling finish.
DiagConsumerFinisher.release();
if (!ScanInstance.ExecuteAction(*Action))
return false;

Expand Down
34 changes: 26 additions & 8 deletions clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ const std::vector<std::string> &ModuleDeps::getBuildArguments() {
return std::get<std::vector<std::string>>(BuildInfo);
}

static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts,
ASTReader &Reader,
const serialization::ModuleFile &MF,
ScanningOptimizations OptimizeArgs) {
static void
optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, ASTReader &Reader,
const serialization::ModuleFile &MF,
const PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
ScanningOptimizations OptimizeArgs) {
if (any(OptimizeArgs & ScanningOptimizations::HeaderSearch)) {
// Only preserve search paths that were used during the dependency scan.
std::vector<HeaderSearchOptions::Entry> Entries;
Expand Down Expand Up @@ -70,11 +71,25 @@ static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts,
llvm::DenseSet<const serialization::ModuleFile *> Visited;
std::function<void(const serialization::ModuleFile *)> VisitMF =
[&](const serialization::ModuleFile *MF) {
VFSUsage |= MF->VFSUsage;
Visited.insert(MF);
for (const serialization::ModuleFile *Import : MF->Imports)
if (!Visited.contains(Import))
VisitMF(Import);
if (MF->Kind == serialization::MK_ImplicitModule) {
VFSUsage |= MF->VFSUsage;
// We only need to recurse into implicit modules. Other module types
// will have the correct set of VFSs for anything they depend on.
for (const serialization::ModuleFile *Import : MF->Imports)
if (!Visited.contains(Import))
VisitMF(Import);
} else {
// This is not an implicitly built module, so it may have different
// VFS options. Fall back to a string comparison instead.
auto VFSMap = PrebuiltModuleVFSMap.find(MF->FileName);
if (VFSMap == PrebuiltModuleVFSMap.end())
return;
for (std::size_t I = 0, E = VFSOverlayFiles.size(); I != E; ++I) {
if (VFSMap->second.contains(VFSOverlayFiles[I]))
VFSUsage[I] = true;
}
}
};
VisitMF(&MF);

Expand Down Expand Up @@ -682,6 +697,7 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
ScanningOptimizations::VFS)))
optimizeHeaderSearchOpts(BuildInvocation.getMutHeaderSearchOpts(),
*MDC.ScanInstance.getASTReader(), *MF,
MDC.PrebuiltModuleVFSMap,
MDC.OptimizeArgs);
if (any(MDC.OptimizeArgs & ScanningOptimizations::SystemWarnings))
optimizeDiagnosticOpts(
Expand Down Expand Up @@ -805,9 +821,11 @@ ModuleDepCollector::ModuleDepCollector(
std::unique_ptr<DependencyOutputOptions> Opts,
CompilerInstance &ScanInstance, DependencyConsumer &C,
DependencyActionController &Controller, CompilerInvocation OriginalCI,
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap,
ScanningOptimizations OptimizeArgs, bool EagerLoadModules,
bool IsStdModuleP1689Format)
: ScanInstance(ScanInstance), Consumer(C), Controller(Controller),
PrebuiltModuleVFSMap(std::move(PrebuiltModuleVFSMap)),
Opts(std::move(Opts)),
CommonInvocation(
makeCommonInvocationForModuleBuild(std::move(OriginalCI))),
Expand Down
Loading