diff --git a/clang/docs/Modules.rst b/clang/docs/Modules.rst --- a/clang/docs/Modules.rst +++ b/clang/docs/Modules.rst @@ -225,6 +225,16 @@ ``-fprebuilt-module-path=`` Specify the path to the prebuilt modules. If specified, we will look for modules in this directory for a given top-level module name. We don't need a module map for loading prebuilt modules in this directory and the compiler will not try to rebuild these modules. This can be specified multiple times. +-cc1 Options +^^^^^^^^^^^^ + +``-fmodules-strict-context-hash`` + Enables hashing of all compiler options that could impact the semantics of a + module in an implicit build. This includes things such as header search paths + and diagnostics. Using this option may lead to an excessive number of modules + being built if the command line arguments are not homogeneous across your + build. + Module Semantics ================ diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -815,6 +815,9 @@ HelpText<"Disable the module hash">; def fmodules_hash_content : Flag<["-"], "fmodules-hash-content">, HelpText<"Enable hashing the content of a module file">; +def fmodules_strict_context_hash : Flag<["-"], "fmodules-strict-context-hash">, + HelpText<"Enable hashing of all compiler options that could impact the " + "semantics of a module in an implicit build">; def c_isystem : JoinedOrSeparate<["-"], "c-isystem">, MetaVarName<"">, HelpText<"Add directory to the C SYSTEM include search path">; def objc_isystem : JoinedOrSeparate<["-"], "objc-isystem">, diff --git a/clang/include/clang/Lex/HeaderSearchOptions.h b/clang/include/clang/Lex/HeaderSearchOptions.h --- a/clang/include/clang/Lex/HeaderSearchOptions.h +++ b/clang/include/clang/Lex/HeaderSearchOptions.h @@ -11,6 +11,7 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/CachedHashString.h" +#include "llvm/ADT/Hashing.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringRef.h" #include @@ -206,6 +207,13 @@ unsigned ModulesHashContent : 1; + /// Whether we should include all things that could impact the module in the + /// hash. + /// + /// This includes things like the full header search path, and enabled + /// diagnostics. + unsigned ModulesStrictContextHash : 1; + HeaderSearchOptions(StringRef _Sysroot = "/") : Sysroot(_Sysroot), ModuleFormat("raw"), DisableModuleHash(false), ImplicitModuleMaps(false), ModuleMapFileHomeIsCwd(false), @@ -214,7 +222,8 @@ ModulesValidateOncePerBuildSession(false), ModulesValidateSystemHeaders(false), ValidateASTInputFilesContent(false), UseDebugInfo(false), - ModulesValidateDiagnosticOptions(true), ModulesHashContent(false) {} + ModulesValidateDiagnosticOptions(true), ModulesHashContent(false), + ModulesStrictContextHash(false) {} /// AddPath - Add the \p Path path to the specified \p Group list. void AddPath(StringRef Path, frontend::IncludeDirGroup Group, @@ -238,6 +247,15 @@ } }; +inline llvm::hash_code hash_value(const HeaderSearchOptions::Entry &E) { + return llvm::hash_combine(E.Path, E.Group, E.IsFramework, E.IgnoreSysRoot); +} + +inline llvm::hash_code +hash_value(const HeaderSearchOptions::SystemHeaderPrefix &SHP) { + return llvm::hash_combine(SHP.Prefix, SHP.IsSystemHeader); +} + } // namespace clang #endif // LLVM_CLANG_LEX_HEADERSEARCHOPTIONS_H diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2067,6 +2067,7 @@ Opts.AddPrebuiltModulePath(A->getValue()); Opts.DisableModuleHash = Args.hasArg(OPT_fdisable_module_hash); Opts.ModulesHashContent = Args.hasArg(OPT_fmodules_hash_content); + Opts.ModulesStrictContextHash = Args.hasArg(OPT_fmodules_strict_context_hash); Opts.ModulesValidateDiagnosticOptions = !Args.hasArg(OPT_fmodules_disable_diagnostic_validation); Opts.ImplicitModuleMaps = Args.hasArg(OPT_fimplicit_module_maps); @@ -3546,6 +3547,7 @@ using llvm::hash_code; using llvm::hash_value; using llvm::hash_combine; + using llvm::hash_combine_range; // Start the signature with the compiler version. // FIXME: We'd rather use something more cryptographically sound than @@ -3600,6 +3602,24 @@ hsOpts.ModulesValidateDiagnosticOptions); code = hash_combine(code, hsOpts.ResourceDir); + if (hsOpts.ModulesStrictContextHash) { + hash_code SHPC = hash_combine_range(hsOpts.SystemHeaderPrefixes.begin(), + hsOpts.SystemHeaderPrefixes.end()); + hash_code UEC = hash_combine_range(hsOpts.UserEntries.begin(), + hsOpts.UserEntries.end()); + code = hash_combine(code, hsOpts.SystemHeaderPrefixes.size(), SHPC, + hsOpts.UserEntries.size(), UEC); + + const DiagnosticOptions &diagOpts = getDiagnosticOpts(); + #define DIAGOPT(Name, Bits, Default) \ + code = hash_combine(code, diagOpts.Name); + #define ENUM_DIAGOPT(Name, Type, Bits, Default) \ + code = hash_combine(code, diagOpts.get##Name()); + #include "clang/Basic/DiagnosticOptions.def" + #undef DIAGOPT + #undef ENUM_DIAGOPT + } + // Extend the signature with the user build path. code = hash_combine(code, hsOpts.ModuleUserBuildPath); diff --git a/clang/test/Modules/context-hash.c b/clang/test/Modules/context-hash.c new file mode 100644 --- /dev/null +++ b/clang/test/Modules/context-hash.c @@ -0,0 +1,33 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fsyntax-only -internal-isystem \ +// RUN: %S/Inputs/System/usr/include -fmodules -fimplicit-module-maps \ +// RUN: -fmodules-cache-path=%t %s -Rmodule-build 2> %t1 +// RUN: rm -rf %t +// RUN: %clang_cc1 -fsyntax-only -internal-isystem \ +// RUN: %S/Inputs/System/usr/include -internal-isystem %S -fmodules \ +// RUN: -fimplicit-module-maps -fmodules-cache-path=%t %s -Rmodule-build 2> \ +// RUN: %t2 +// RUN: rm -rf %t +// RUN: %clang_cc1 -fsyntax-only -internal-isystem \ +// RUN: %S/Inputs/System/usr/include -internal-isystem %S -fmodules \ +// RUN: -fimplicit-module-maps -fmodules-cache-path=%t %s \ +// RUN: -fmodules-strict-context-hash -Rmodule-build 2> %t3 +// RUN: rm -rf %t +// RUN: %clang_cc1 -fsyntax-only -Weverything -internal-isystem \ +// RUN: %S/Inputs/System/usr/include -fmodules -fmodules-strict-context-hash \ +// RUN: -fimplicit-module-maps -fmodules-cache-path=%t %s -Rmodule-build 2> \ +// RUN: %t4 +// RUN: echo %t | cat - %t1 %t2 %t3 %t4 | FileCheck %s + +// This test verifies that only strict hashing includes search paths and +// diagnostics in the module context hash. + +#include + +// CHECK: [[PREFIX:(.*[/\\])+[a-zA-Z0-9.-]+]] +// CHECK: building module 'cstd' as '[[PREFIX]]{{[/\\]}}[[CONTEXT_HASH:[A-Z0-9]+]]{{[/\\]}}cstd-[[AST_HASH:[A-Z0-9]+]].pcm' +// CHECK: building module 'cstd' as '{{.*[/\\]}}[[CONTEXT_HASH]]{{[/\\]}}cstd-[[AST_HASH]].pcm' +// CHECK-NOT: building module 'cstd' as '{{.*[/\\]}}[[CONTEXT_HASH]]{{[/\\]}} +// CHECK: cstd-[[AST_HASH]].pcm' +// CHECK-NOT: building module 'cstd' as '{{.*[/\\]}}[[CONTEXT_HASH]]{{[/\\]}} +// CHECK: cstd-[[AST_HASH]].pcm'