arames created this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
arames edited the summary of this revision.
arames added reviewers: bruno, rsmith.
Herald added a subscriber: dexonsmith.

The behavior is controlled by the `-fprebuilt-implicit-modules` option, and
allows searching for implicit modules in the prebuilt module cache paths.

The current command-line options for prebuilt modules do not allow to easily
maintain and use multiple versions of modules. Both the producer and users of
prebuilt modules are required to know the relationships between compilation
options and module file paths. Using a particular version of a prebuilt module
requires passing a particular option on the command line (e.g.
`-fmodule-file=[<name>=]<file>` or `-fprebuilt-module-path=<directory>`).

However the compiler already knows how to distinguish and automatically locate
implicit modules. Hence this proposal to introduce the
`-fprebuilt-implicit-modules` option. When set, it enables searching for
implicit modules in the prebuilt module paths (specified via
`-fprebuilt-module-path`). To not modify existing behavior, this search takes
place after the standard search for prebuilt modules. If not

Here is a workflow illustrating how both the producer and consumer of prebuilt
modules would need to know what versions of prebuilt modules are available and
where they are located.

  clang -cc1 -x c modulemap -fmodules -emit-module -fmodule-name=foo 
-fmodules-cache-path=prebuilt_modules_v1 <config 1 options>
  clang -cc1 -x c modulemap -fmodules -emit-module -fmodule-name=foo 
-fmodules-cache-path=prebuilt_modules_v2 <config 2 options>
  clang -cc1 -x c modulemap -fmodules -emit-module -fmodule-name=foo 
-fmodules-cache-path=prebuilt_modules_v3 <config 3 options>
  
  clang -cc1 -x c use.c -fmodules fmodule-map-file=modulemap 
-fprebuilt-module-path=prebuilt_modules_v1 <config 1 options>
  clang -cc1 -x c use.c -fmodules fmodule-map-file=modulemap <non-prebuilt 
config options>

With prebuilt implicit modules, the producer can generate prebuilt modules as
usual, all in the same output directory. The same mechanisms as for implicit
modules take care of incorporating hashes in the path to distinguish between
module versions.

Note that we do not specify the output module filename, so `-o` implicit 
modules are generated in the cache path `prebuilt_modules`.

  clang -cc1 -x c modulemap -fmodules -emit-module -fmodule-name=foo 
-fmodules-cache-path=prebuilt_modules <config 1 options>
  clang -cc1 -x c modulemap -fmodules -emit-module -fmodule-name=foo 
-fmodules-cache-path=prebuilt_modules <config 2 options>
  clang -cc1 -x c modulemap -fmodules -emit-module -fmodule-name=foo 
-fmodules-cache-path=prebuilt_modules <config 3 options>

The user can now simply enable prebuilt implicit modules and point to the
prebuilt modules cache. No need to "parse" command-line options to decide
what prebuilt modules (paths) to use.

  clang -cc1 -x c use.c -fmodules fmodule-map-file=modulemap 
-fprebuilt-module-path=prebuilt_modules -fprebuilt-implicit-modules <config 1 
options>
  clang -cc1 -x c use.c -fmodules fmodule-map-file=modulemap 
-fprebuilt-module-path=prebuilt_modules -fprebuilt-implicit-modules 
<non-prebuilt config options>

This is for example particularly useful in a use-case where compilation is
expensive, and the configurations expected to be used are predictable, but not
controlled by the producer of prebuilt modules. Modules for the set of
predictable configurations can be prebuilt, and using them does not require
"parsing" the configuration (command-line options).


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D68997

Files:
  clang/docs/Modules.rst
  clang/include/clang/Driver/Options.td
  clang/include/clang/Lex/HeaderSearch.h
  clang/include/clang/Lex/HeaderSearchOptions.h
  clang/lib/Driver/ToolChains/Clang.cpp
  clang/lib/Frontend/CompilerInstance.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/lib/Lex/HeaderSearch.cpp
  clang/lib/Serialization/ASTReader.cpp
  clang/lib/Serialization/ASTWriter.cpp
  clang/test/Modules/Inputs/prebuilt-implicit-module/a.h
  clang/test/Modules/Inputs/prebuilt-implicit-module/module.modulemap
  clang/test/Modules/prebuilt-implicit-modules.m

Index: clang/test/Modules/prebuilt-implicit-modules.m
===================================================================
--- /dev/null
+++ clang/test/Modules/prebuilt-implicit-modules.m
@@ -0,0 +1,27 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -x objective-c -fmodules %S/Inputs/prebuilt-implicit-module/module.modulemap -emit-module -fmodule-name=module_a -fmodules-cache-path=%t
+// RUN: find %t -name "module_a*.pcm" | grep module_a
+//
+// Check we use a prebuilt module when available, and do not build an implicit module.
+// RUN: rm -rf %t1
+// RUN: mkdir -p %t1
+// RUN: %clang_cc1 -x objective-c %s -I%S/Inputs/prebuilt-implicit-module -fmodules -fmodule-map-file=%S/Inputs/prebuilt-implicit-module/module.modulemap -fprebuilt-implicit-modules -fprebuilt-module-path=%t -fmodules-cache-path=%t1
+// RUN: find %t1 -name "module_a*.pcm" | not grep module_e
+//
+// Check that we correctly fall back to implicit modules if the prebuilt implicit module is not found.
+// RUN: %clang_cc1 -x objective-c %s -I%S/Inputs/prebuilt-implicit-module -fmodules -fmodule-map-file=%S/Inputs/prebuilt-implicit-module/module.modulemap -fprebuilt-implicit-modules -fprebuilt-module-path=%t -fmodules-cache-path=%t1 -fno-signed-char
+// RUN: find %t1 -name "module_a*.pcm" | grep module_a
+
+// Check that non-implicit prebuilt modules are always preferred to prebuilt implicit modules.
+// RUN: rm -rf %t2
+// RUN: mkdir -p %t2
+// RUN: %clang_cc1 -x objective-c -fmodules %S/Inputs/prebuilt-implicit-module/module.modulemap -emit-module -fmodule-name=module_a -fmodules-cache-path=%t
+// RUN: %clang_cc1 -x objective-c -fmodules %S/Inputs/prebuilt-implicit-module/module.modulemap -emit-module -fmodule-name=module_a -o %t/module_a.pcm -fno-signed-char
+// RUN: not %clang_cc1 -x objective-c %s -I%S/Inputs/prebuilt-implicit-module -fmodules -fmodule-map-file=%S/Inputs/prebuilt-implicit-module/module.modulemap -fprebuilt-implicit-modules -fprebuilt-module-path=%t -fmodules-cache-path=%t2
+// RUN: find %t2 -name "module_a*.pcm" | not grep module_a
+
+// expected-no-diagnostics
+@import module_a;
+int test() {
+  return a;
+}
Index: clang/test/Modules/Inputs/prebuilt-implicit-module/module.modulemap
===================================================================
--- /dev/null
+++ clang/test/Modules/Inputs/prebuilt-implicit-module/module.modulemap
@@ -0,0 +1 @@
+module module_a { header "a.h" }
Index: clang/test/Modules/Inputs/prebuilt-implicit-module/a.h
===================================================================
--- /dev/null
+++ clang/test/Modules/Inputs/prebuilt-implicit-module/a.h
@@ -0,0 +1 @@
+const int a = 1;
Index: clang/lib/Serialization/ASTWriter.cpp
===================================================================
--- clang/lib/Serialization/ASTWriter.cpp
+++ clang/lib/Serialization/ASTWriter.cpp
@@ -1675,6 +1675,7 @@
   Record.push_back(HSOpts.DisableModuleHash);
   Record.push_back(HSOpts.ImplicitModuleMaps);
   Record.push_back(HSOpts.ModuleMapFileHomeIsCwd);
+  Record.push_back(HSOpts.EnablePrebuiltImplicitModules);
   Record.push_back(HSOpts.UseBuiltinIncludes);
   Record.push_back(HSOpts.UseStandardSystemIncludes);
   Record.push_back(HSOpts.UseStandardCXXIncludes);
Index: clang/lib/Serialization/ASTReader.cpp
===================================================================
--- clang/lib/Serialization/ASTReader.cpp
+++ clang/lib/Serialization/ASTReader.cpp
@@ -5726,6 +5726,7 @@
   HSOpts.DisableModuleHash = Record[Idx++];
   HSOpts.ImplicitModuleMaps = Record[Idx++];
   HSOpts.ModuleMapFileHomeIsCwd = Record[Idx++];
+  HSOpts.EnablePrebuiltImplicitModules = Record[Idx++];
   HSOpts.UseBuiltinIncludes = Record[Idx++];
   HSOpts.UseStandardSystemIncludes = Record[Idx++];
   HSOpts.UseStandardCXXIncludes = Record[Idx++];
Index: clang/lib/Lex/HeaderSearch.cpp
===================================================================
--- clang/lib/Lex/HeaderSearch.cpp
+++ clang/lib/Lex/HeaderSearch.cpp
@@ -164,14 +164,41 @@
   return {};
 }
 
+std::string HeaderSearch::getPrebuiltImplicitModuleFileName(Module *Module) {
+  const FileEntry *ModuleMap =
+      getModuleMap().getModuleMapFileForUniquing(Module);
+  StringRef ModuleName = Module->Name;
+  StringRef ModuleMapPath = ModuleMap->getName();
+  StringRef ModuleCacheHash = HSOpts->DisableModuleHash
+                                  ? ""
+                                  : llvm::sys::path::stem(getModuleCachePath());
+  for (const std::string &Dir : HSOpts->PrebuiltModulePaths) {
+    SmallString<256> CachePath(Dir);
+    llvm::sys::fs::make_absolute(CachePath);
+    llvm::sys::path::append(CachePath, ModuleCacheHash);
+    std::string FileName =
+        getCachedModuleFileNameImpl(ModuleName, ModuleMapPath, CachePath);
+    if (!FileName.empty() && getFileMgr().getFile(FileName))
+      return FileName;
+  }
+  return {};
+}
+
 std::string HeaderSearch::getCachedModuleFileName(StringRef ModuleName,
                                                   StringRef ModuleMapPath) {
+  return getCachedModuleFileNameImpl(ModuleName, ModuleMapPath,
+                                     getModuleCachePath());
+}
+
+std::string HeaderSearch::getCachedModuleFileNameImpl(StringRef ModuleName,
+                                                      StringRef ModuleMapPath,
+                                                      StringRef CachePath) {
   // If we don't have a module cache path or aren't supposed to use one, we
   // can't do anything.
-  if (getModuleCachePath().empty())
+  if (CachePath.empty())
     return {};
 
-  SmallString<256> Result(getModuleCachePath());
+  SmallString<256> Result(CachePath);
   llvm::sys::fs::make_absolute(Result);
 
   if (HSOpts->DisableModuleHash) {
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -2069,6 +2069,8 @@
       !Args.hasArg(OPT_fmodules_disable_diagnostic_validation);
   Opts.ImplicitModuleMaps = Args.hasArg(OPT_fimplicit_module_maps);
   Opts.ModuleMapFileHomeIsCwd = Args.hasArg(OPT_fmodule_map_file_home_is_cwd);
+  Opts.EnablePrebuiltImplicitModules =
+      Args.hasArg(OPT_fprebuilt_implicit_modules);
   Opts.ModuleCachePruneInterval =
       getLastArgIntValue(Args, OPT_fmodules_prune_interval, 7 * 24 * 60 * 60);
   Opts.ModuleCachePruneAfter =
Index: clang/lib/Frontend/CompilerInstance.cpp
===================================================================
--- clang/lib/Frontend/CompilerInstance.cpp
+++ clang/lib/Frontend/CompilerInstance.cpp
@@ -1673,6 +1673,9 @@
                                      !HSOpts.PrebuiltModulePaths.empty())) {
       ModuleFileName =
         PP->getHeaderSearchInfo().getPrebuiltModuleFileName(ModuleName);
+      if (HSOpts.EnablePrebuiltImplicitModules && ModuleFileName.empty())
+        ModuleFileName =
+            PP->getHeaderSearchInfo().getPrebuiltImplicitModuleFileName(Module);
       if (!ModuleFileName.empty())
         Source = PrebuiltModulePath;
     }
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -2777,6 +2777,8 @@
           std::string("-fprebuilt-module-path=") + A->getValue()));
       A->claim();
     }
+    if (Args.hasFlag(options::OPT_fprebuilt_implicit_modules, false))
+      CmdArgs.push_back("-fprebuilt-implicit-modules");
   }
 
   // -fmodule-name specifies the module that is currently being built (or
Index: clang/include/clang/Lex/HeaderSearchOptions.h
===================================================================
--- clang/include/clang/Lex/HeaderSearchOptions.h
+++ clang/include/clang/Lex/HeaderSearchOptions.h
@@ -141,6 +141,10 @@
   /// file.
   unsigned ModuleMapFileHomeIsCwd : 1;
 
+  /// Also search for prebuilt implicit modules in the prebuilt module cache
+  /// path.
+  unsigned EnablePrebuiltImplicitModules : 1;
+
   /// The interval (in seconds) between pruning operations.
   ///
   /// This operation is expensive, because it requires Clang to walk through
@@ -205,8 +209,9 @@
   HeaderSearchOptions(StringRef _Sysroot = "/")
       : Sysroot(_Sysroot), ModuleFormat("raw"), DisableModuleHash(false),
         ImplicitModuleMaps(false), ModuleMapFileHomeIsCwd(false),
-        UseBuiltinIncludes(true), UseStandardSystemIncludes(true),
-        UseStandardCXXIncludes(true), UseLibcxx(false), Verbose(false),
+        EnablePrebuiltImplicitModules(false), UseBuiltinIncludes(true),
+        UseStandardSystemIncludes(true), UseStandardCXXIncludes(true),
+        UseLibcxx(false), Verbose(false),
         ModulesValidateOncePerBuildSession(false),
         ModulesValidateSystemHeaders(false), UseDebugInfo(false),
         ModulesValidateDiagnosticOptions(true), ModulesHashContent(false) {}
Index: clang/include/clang/Lex/HeaderSearch.h
===================================================================
--- clang/include/clang/Lex/HeaderSearch.h
+++ clang/include/clang/Lex/HeaderSearch.h
@@ -505,6 +505,15 @@
   std::string getPrebuiltModuleFileName(StringRef ModuleName,
                                         bool FileMapOnly = false);
 
+  /// Retrieve the name of the prebuilt module file that should be used
+  /// to load the given module.
+  ///
+  /// \param Module The module whose module file name will be returned.
+  ///
+  /// \returns The name of the module file that corresponds to this module,
+  /// or an empty string if this module does not correspond to any module file.
+  std::string getPrebuiltImplicitModuleFileName(Module *Module);
+
   /// Retrieve the name of the (to-be-)cached module file that should
   /// be used to load a module with the given name.
   ///
@@ -601,6 +610,22 @@
   Module *lookupModule(StringRef ModuleName, StringRef SearchName,
                        bool AllowExtraModuleMapSearch = false);
 
+  /// Retrieve the name of the (to-be-)cached module file that should
+  /// be used to load a module with the given name.
+  ///
+  /// \param ModuleName The module whose module file name will be returned.
+  ///
+  /// \param ModuleMapPath A path that when combined with \c ModuleName
+  /// uniquely identifies this module. See Module::ModuleMap.
+  ///
+  /// \param CachePath A path to the module cache.
+  ///
+  /// \returns The name of the module file that corresponds to this module,
+  /// or an empty string if this module does not correspond to any module file.
+  std::string getCachedModuleFileNameImpl(StringRef ModuleName,
+                                          StringRef ModuleMapPath,
+                                          StringRef CachePath);
+
   /// Retrieve a module with the given name, which may be part of the
   /// given framework.
   ///
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -1341,6 +1341,9 @@
 def fprebuilt_module_path : Joined<["-"], "fprebuilt-module-path=">, Group<i_Group>,
   Flags<[DriverOption, CC1Option]>, MetaVarName<"<directory>">,
   HelpText<"Specify the prebuilt module path">;
+def fprebuilt_implicit_modules : Flag <["-"], "fprebuilt-implicit-modules">, Group<f_Group>,
+  Flags<[DriverOption, CC1Option]>,
+  HelpText<"Look up implicit modules in the prebuilt module path">;
 def fmodules_prune_interval : Joined<["-"], "fmodules-prune-interval=">, Group<i_Group>,
   Flags<[CC1Option]>, MetaVarName<"<seconds>">,
   HelpText<"Specify the interval (in seconds) between attempts to prune the module cache">;
Index: clang/docs/Modules.rst
===================================================================
--- clang/docs/Modules.rst
+++ clang/docs/Modules.rst
@@ -225,6 +225,100 @@
 ``-fprebuilt-module-path=<directory>``
   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.
 
+``-fprebuilt-implicit-modules``
+  Enable prebuilt implicit modules. If a prebuilt module is not found in the
+  prebuilt modules paths (specified via ``-fprebuilt-module-path``), we will
+  look for a matching implicit modules in the prebuilt modules paths.
+
+Using Prebuilt Modules
+----------------------
+
+Below are a few examples illustrating uses of prebuilt modules via the different options.
+
+First, let's set up files for our examples.
+
+.. code-block:: c
+
+  /* A.h */
+  // empty
+
+.. code-block:: c
+
+  /* B.h */
+  #include "A.h"
+
+.. code-block:: c
+
+  /* use.c */
+  #include "B.h"
+
+.. code-block:: c
+
+  /* module.modulemap */
+  module A {
+    header "A.h"
+  }
+  module B {
+    header "B.h"
+  }
+
+In examples below, the compilation of ``use.c`` can be done without ``-cc1``, but the commands used to prebuild the modules would need to be updated to take into account the default options passed to ``clang -cc1``. (See ``clang use.c -v``)
+
+First let us use an explicit mapping from modules to files.
+
+.. code-block:: sh
+
+  rm -rf prebuilt ; mkdir prebuilt
+  clang -cc1 -x c module.modulemap -fmodules -emit-module -fmodule-name=A -o prebuilt/A.pcm
+  clang -cc1 -x c module.modulemap -fmodules -emit-module -fmodule-name=B -o prebuilt/B.pcm -fmodule-file=A=prebuilt/A.pcm
+  clang -cc1 -x c use.c -fmodules -o use.o -fmodule-file=A=prebuilt/A.pcm -fmodule-file=B=prebuilt/B.pcm
+
+Instead of of specifying the mappings manually, it can be convenient to use the ``-fprebuilt-module-path`` option.
+
+.. code-block:: sh
+
+  rm -rf prebuilt ; mkdir prebuilt
+  clang -cc1 -x c module.modulemap -fmodules -emit-module -fmodule-name=A -o prebuilt/A.pcm
+  clang -cc1 -x c module.modulemap -fmodules -emit-module -fmodule-name=B -o prebuilt/B.pcm -fmodule-file=A=prebuilt/A.pcm
+  clang -cc1 -x c use.c -fmodules -o use.o -fprebuilt-module-path=prebuilt
+
+A trick to prebuilt required modules in one command is to generate implicit modules using the ``-fdisable-module-hash`` option.
+
+.. code-block:: sh
+
+  rm -rf prebuilt ; mkdir prebuilt
+  clang -cc1 -x c module.modulemap -fmodules -emit-module -fmodule-name=B -fmodules-cache-path=prebuilt -fdisable-module-hash
+  ls prebuilt/*.pcm
+  # prebuilt/A.pcm  prebuilt/B.pcm
+  clang -cc1 -x c use.c -fmodules -fprebuilt-module-path=prebuilt -o /use.o
+
+Maintaining multiple versions of prebuilt modules requires using a different mapping or pointing to a different prebuilt modules cache path. For example:
+
+.. code-block:: sh
+
+  rm -rf prebuilt_1 ; rm -rf prebuilt_2 ; mkdir prebuilt_1 ; mkdir prebuilt_2
+  clang -cc1 -x c module.modulemap -fmodules -emit-module -fmodule-name=B -fmodules-cache-path=prebuilt_1 -D_CONFIG_1
+  clang -cc1 -x c module.modulemap -fmodules -emit-module -fmodule-name=B -fmodules-cache-path=prebuilt_2 -D_CONFIG_2
+  clang -cc1 -x c use.c -o use.o -fmodules -fprebuilt-module-path=prebuilt_1 -DCONFIG_1
+  clang -cc1 -x c use.c -o use.o -fmodules -fprebuilt-module-path=prebuilt_2 -DCONFIG_2
+
+Instead, one can build implicit modules in a given cache path (using
+``-fmodules-cache-path``, and reuse them as prebuilt implicit modules by
+passing ``-fprebuilt-module-path`` and ``-fprebuilt-implicit-modules``.
+
+.. code-block:: sh
+
+  rm -rf prebuilt; mkdir prebuilt
+  clang -cc1 -x c module.modulemap -fmodules -emit-module -fmodule-name=B -fmodules-cache-path=prebuilt -D_CONFIG_1
+  clang -cc1 -x c module.modulemap -fmodules -emit-module -fmodule-name=B -fmodules-cache-path=prebuilt -D_CONFIG_2
+  find prebuilt -name "*.pcm"
+  # prebuilt/W7OPMD3I127H/A-285BFOGQN82QX.pcm
+  # prebuilt/W7OPMD3I127H/B-285BFOGQN82QX.pcm
+  # prebuilt/Y4JCOK22SBQC/A-285BFOGQN82QX.pcm
+  # prebuilt/Y4JCOK22SBQC/B-285BFOGQN82QX.pcm
+  clang -cc1 -x c use.c -o /use.o -fmodules -fmodules-cache-path=cache -fprebuilt-module-path=prebuilt -fprebuilt-implicit-modules -D_CONFIG_1
+  clang -cc1 -x c use.c -o /use.o -fmodules -fmodules-cache-path=cache -fprebuilt-module-path=prebuilt -fprebuilt-implicit-modules -D_CONFIG_2
+
 Module Semantics
 ================
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D68997: Allow sear... Alexandre Rames via Phabricator via cfe-commits

Reply via email to