Skip to content

[clang][deps] Skip slow UNHASHED_CONTROL_BLOCK records #69975

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 6 commits into from
Nov 2, 2023
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
8 changes: 8 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2947,6 +2947,14 @@ def fno_modules_validate_textual_header_includes :
MarshallingInfoNegativeFlag<LangOpts<"ModulesValidateTextualHeaderIncludes">>,
HelpText<"Do not enforce -fmodules-decluse and private header restrictions for textual headers. "
"This flag will be removed in a future Clang release.">;
defm modules_skip_diagnostic_options : BoolFOption<"modules-skip-diagnostic-options",
HeaderSearchOpts<"ModulesSkipDiagnosticOptions">, DefaultFalse,
PosFlag<SetTrue, [], [], "Disable writing diagnostic options">,
NegFlag<SetFalse>, BothFlags<[], [CC1Option]>>;
defm modules_skip_header_search_paths : BoolFOption<"modules-skip-header-search-paths",
HeaderSearchOpts<"ModulesSkipHeaderSearchPaths">, DefaultFalse,
PosFlag<SetTrue, [], [], "Disable writing header search paths">,
NegFlag<SetFalse>, BothFlags<[], [CC1Option]>>;

def fincremental_extensions :
Flag<["-"], "fincremental-extensions">,
Expand Down
12 changes: 11 additions & 1 deletion clang/include/clang/Lex/HeaderSearchOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,14 @@ class HeaderSearchOptions {

unsigned ModulesValidateDiagnosticOptions : 1;

/// Whether to entirely skip writing diagnostic options.
/// Primarily used to speed up deserialization during dependency scanning.
unsigned ModulesSkipDiagnosticOptions : 1;

/// Whether to entirely skip writing header search paths.
/// Primarily used to speed up deserialization during dependency scanning.
unsigned ModulesSkipHeaderSearchPaths : 1;

unsigned ModulesHashContent : 1;

/// Whether we should include all things that could impact the module in the
Expand All @@ -238,7 +246,9 @@ class HeaderSearchOptions {
ModulesValidateSystemHeaders(false),
ValidateASTInputFilesContent(false),
ForceCheckCXX20ModulesInputFiles(false), UseDebugInfo(false),
ModulesValidateDiagnosticOptions(true), ModulesHashContent(false),
ModulesValidateDiagnosticOptions(true),
ModulesSkipDiagnosticOptions(false),
ModulesSkipHeaderSearchPaths(false), ModulesHashContent(false),
ModulesStrictContextHash(false) {}

/// AddPath - Add the \p Path path to the specified \p Group list.
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,21 @@ namespace {
return false;
}

bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
bool Complain) override {
Out.indent(2) << "Header search paths:\n";
Out.indent(4) << "User entries:\n";
for (const auto &Entry : HSOpts.UserEntries)
Out.indent(6) << Entry.Path << "\n";
Out.indent(4) << "System header prefixes:\n";
for (const auto &Prefix : HSOpts.SystemHeaderPrefixes)
Out.indent(6) << Prefix.Prefix << "\n";
Out.indent(4) << "VFS overlay files:\n";
for (const auto &Overlay : HSOpts.VFSOverlayFiles)
Out.indent(6) << Overlay << "\n";
return false;
}

bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts,
bool ReadMacros, bool Complain,
std::string &SuggestedPredefines) override {
Expand Down
70 changes: 36 additions & 34 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1212,52 +1212,54 @@ void ASTWriter::writeUnhashedControlBlock(Preprocessor &PP,
Record.clear();
}

const auto &HSOpts = PP.getHeaderSearchInfo().getHeaderSearchOpts();

// Diagnostic options.
const auto &Diags = Context.getDiagnostics();
const DiagnosticOptions &DiagOpts = Diags.getDiagnosticOptions();
if (!HSOpts.ModulesSkipDiagnosticOptions) {
#define DIAGOPT(Name, Bits, Default) Record.push_back(DiagOpts.Name);
#define ENUM_DIAGOPT(Name, Type, Bits, Default) \
Record.push_back(static_cast<unsigned>(DiagOpts.get##Name()));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

clang-format unfortunately does not indent this line.

#include "clang/Basic/DiagnosticOptions.def"
Record.push_back(DiagOpts.Warnings.size());
for (unsigned I = 0, N = DiagOpts.Warnings.size(); I != N; ++I)
AddString(DiagOpts.Warnings[I], Record);
Record.push_back(DiagOpts.Remarks.size());
for (unsigned I = 0, N = DiagOpts.Remarks.size(); I != N; ++I)
AddString(DiagOpts.Remarks[I], Record);
// Note: we don't serialize the log or serialization file names, because they
// are generally transient files and will almost always be overridden.
Stream.EmitRecord(DIAGNOSTIC_OPTIONS, Record);
Record.clear();
Record.push_back(DiagOpts.Warnings.size());
for (unsigned I = 0, N = DiagOpts.Warnings.size(); I != N; ++I)
AddString(DiagOpts.Warnings[I], Record);
Record.push_back(DiagOpts.Remarks.size());
for (unsigned I = 0, N = DiagOpts.Remarks.size(); I != N; ++I)
AddString(DiagOpts.Remarks[I], Record);
// Note: we don't serialize the log or serialization file names, because
// they are generally transient files and will almost always be overridden.
Stream.EmitRecord(DIAGNOSTIC_OPTIONS, Record);
Record.clear();
}

// Header search paths.
Record.clear();
const HeaderSearchOptions &HSOpts =
PP.getHeaderSearchInfo().getHeaderSearchOpts();

// Include entries.
Record.push_back(HSOpts.UserEntries.size());
for (unsigned I = 0, N = HSOpts.UserEntries.size(); I != N; ++I) {
const HeaderSearchOptions::Entry &Entry = HSOpts.UserEntries[I];
AddString(Entry.Path, Record);
Record.push_back(static_cast<unsigned>(Entry.Group));
Record.push_back(Entry.IsFramework);
Record.push_back(Entry.IgnoreSysRoot);
}
if (!HSOpts.ModulesSkipHeaderSearchPaths) {
// Include entries.
Record.push_back(HSOpts.UserEntries.size());
for (unsigned I = 0, N = HSOpts.UserEntries.size(); I != N; ++I) {
const HeaderSearchOptions::Entry &Entry = HSOpts.UserEntries[I];
AddString(Entry.Path, Record);
Record.push_back(static_cast<unsigned>(Entry.Group));
Record.push_back(Entry.IsFramework);
Record.push_back(Entry.IgnoreSysRoot);
}

// System header prefixes.
Record.push_back(HSOpts.SystemHeaderPrefixes.size());
for (unsigned I = 0, N = HSOpts.SystemHeaderPrefixes.size(); I != N; ++I) {
AddString(HSOpts.SystemHeaderPrefixes[I].Prefix, Record);
Record.push_back(HSOpts.SystemHeaderPrefixes[I].IsSystemHeader);
}
// System header prefixes.
Record.push_back(HSOpts.SystemHeaderPrefixes.size());
for (unsigned I = 0, N = HSOpts.SystemHeaderPrefixes.size(); I != N; ++I) {
AddString(HSOpts.SystemHeaderPrefixes[I].Prefix, Record);
Record.push_back(HSOpts.SystemHeaderPrefixes[I].IsSystemHeader);
}

// VFS overlay files.
Record.push_back(HSOpts.VFSOverlayFiles.size());
for (StringRef VFSOverlayFile : HSOpts.VFSOverlayFiles)
AddString(VFSOverlayFile, Record);
// VFS overlay files.
Record.push_back(HSOpts.VFSOverlayFiles.size());
for (StringRef VFSOverlayFile : HSOpts.VFSOverlayFiles)
AddString(VFSOverlayFile, Record);

Stream.EmitRecord(HEADER_SEARCH_PATHS, Record);
Stream.EmitRecord(HEADER_SEARCH_PATHS, Record);
}

// Write out the diagnostic/pragma mappings.
WritePragmaDiagnosticMappings(Diags, /* isModule = */ WritingModule);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ class DependencyScanningAction : public tooling::ToolAction {
// TODO: Implement diagnostic bucketing to reduce the impact of strict
// context hashing.
ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;
ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;

// Avoid some checks and module map parsing when loading PCM files.
ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
Expand Down
41 changes: 41 additions & 0 deletions clang/test/Modules/diagnostic-options-mismatch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// RUN: rm -rf %t && mkdir -p %t
// RUN: split-file %s %t

//--- module.modulemap
module Mod { header "mod.h" }
//--- mod.h
//--- tu.c
#include "mod.h"

// Without any extra compiler flags, mismatched diagnostic options trigger recompilation of modules.
//
// RUN: %clang_cc1 -fmodules -fmodule-map-file=%t/module.modulemap -fmodules-cache-path=%t/cache1 -fdisable-module-hash \
// RUN: -fsyntax-only %t/tu.c -Wnon-modular-include-in-module
// RUN: %clang_cc1 -fmodules -fmodule-map-file=%t/module.modulemap -fmodules-cache-path=%t/cache1 -fdisable-module-hash \
// RUN: -fsyntax-only %t/tu.c -Werror=non-modular-include-in-module -Rmodule-build 2>&1 \
// RUN: | FileCheck %s --check-prefix=DID-REBUILD
// DID-REBUILD: remark: building module 'Mod'

// When skipping serialization of diagnostic options, mismatches cannot be detected, old PCM file gets reused.
//
// RUN: %clang_cc1 -fmodules -fmodule-map-file=%t/module.modulemap -fmodules-cache-path=%t/cache2 -fdisable-module-hash \
// RUN: -fsyntax-only %t/tu.c -fmodules-skip-diagnostic-options -Wnon-modular-include-in-module
// RUN: %clang_cc1 -fmodules -fmodule-map-file=%t/module.modulemap -fmodules-cache-path=%t/cache2 -fdisable-module-hash \
// RUN: -fsyntax-only %t/tu.c -fmodules-skip-diagnostic-options -Werror=non-modular-include-in-module -Rmodule-build 2>&1 \
// RUN: | FileCheck %s --check-prefix=DID-REUSE --allow-empty
// DID-REUSE-NOT: remark: building module 'Mod'
//
// RUN: %clang_cc1 -module-file-info %t/cache2/Mod.pcm | FileCheck %s --check-prefix=NO-DIAG-OPTS
// NO-DIAG-OPTS-NOT: Diagnostic flags:

// When disabling validation of diagnostic options, mismatches are not checked for, old PCM file gets reused.
//
// RUN: %clang_cc1 -fmodules -fmodule-map-file=%t/module.modulemap -fmodules-cache-path=%t/cache3 -fdisable-module-hash \
// RUN: -fsyntax-only %t/tu.c -fmodules-disable-diagnostic-validation -Wnon-modular-include-in-module
// RUN: %clang_cc1 -fmodules -fmodule-map-file=%t/module.modulemap -fmodules-cache-path=%t/cache3 -fdisable-module-hash \
// RUN: -fsyntax-only %t/tu.c -fmodules-disable-diagnostic-validation -Werror=non-modular-include-in-module -Rmodule-build 2>&1 \
// RUN: | FileCheck %s --check-prefix=DID-REUSE --allow-empty
//
// RUN: %clang_cc1 -module-file-info %t/cache3/Mod.pcm | FileCheck %s --check-prefix=OLD-DIAG-OPTS
// OLD-DIAG-OPTS: Diagnostic flags:
// OLD-DIAG-OPTS-NEXT: -Wnon-modular-include-in-module
36 changes: 36 additions & 0 deletions clang/test/Modules/header-search-paths-mismatch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// RUN: rm -rf %t && mkdir -p %t
// RUN: split-file %s %t

//--- module.modulemap
module Mod { header "mod.h" }
//--- mod.h
//--- tu.c
#include "mod.h"

//--- one/foo.h
//--- two/foo.h

// By default, mismatched header search paths are ignored, old PCM file gets reused.
//
// RUN: %clang_cc1 -fmodules -fmodule-map-file=%t/module.modulemap -fmodules-cache-path=%t/cache1 -fdisable-module-hash \
// RUN: -fsyntax-only %t/tu.c -I %t/one
// RUN: %clang_cc1 -fmodules -fmodule-map-file=%t/module.modulemap -fmodules-cache-path=%t/cache1 -fdisable-module-hash \
// RUN: -fsyntax-only %t/tu.c -I %t/two -Rmodule-build 2>&1 \
// RUN: | FileCheck %s --allow-empty --check-prefix=DID-REUSE
// DID-REUSE-NOT: remark: building module 'Mod'
//
// RUN: %clang_cc1 -module-file-info %t/cache1/Mod.pcm | FileCheck %s --check-prefix=HS-PATHS
// HS-PATHS: Header search paths:
// HS-PATHS-NEXT: User entries:
// HS-PATHS-NEXT: one

// When skipping serialization of header search paths, mismatches cannot be detected, old PCM file gets reused.
//
// RUN: %clang_cc1 -fmodules -fmodule-map-file=%t/module.modulemap -fmodules-cache-path=%t/cache2 -fdisable-module-hash \
// RUN: -fsyntax-only %t/tu.c -fmodules-skip-header-search-paths -I %t/one
// RUN: %clang_cc1 -fmodules -fmodule-map-file=%t/module.modulemap -fmodules-cache-path=%t/cache2 -fdisable-module-hash \
// RUN: -fsyntax-only %t/tu.c -fmodules-skip-header-search-paths -I %t/two -Rmodule-build 2>&1 \
// RUN: | FileCheck %s --check-prefix=DID-REUSE --allow-empty
//
// RUN: %clang_cc1 -module-file-info %t/cache2/Mod.pcm | FileCheck %s --check-prefix=NO-HS-PATHS
// NO-HS-PATHS-NOT: Header search paths: