diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index e24626913add7..d3fdc05407675 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3179,6 +3179,12 @@ def fmodule_map_file : Joined<["-"], "fmodule-map-file=">, MetaVarName<"">, HelpText<"Load this module map file">, MarshallingInfoStringVector>; +def flate_module_map_file : Joined<["-"], "flate-module-map-file=">, + Group, + Visibility<[CC1Option]>, + MetaVarName<"">, + HelpText<"Load this module map file after all modules">, + MarshallingInfoStringVector>; def fmodule_file : Joined<["-"], "fmodule-file=">, Group, Visibility<[ClangOption, CC1Option, CLOption]>, diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index a738c1f375768..cc45e68899c7e 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -536,6 +536,9 @@ class FrontendOptions { /// The list of module map files to load before processing the input. std::vector ModuleMapFiles; + /// \brief The list of module map files to load after ModuleFile. + std::vector LateModuleMapFiles; + /// The list of additional prebuilt module files to load before /// processing the input. std::vector ModuleFiles; diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp index b7c9967316f0b..14745584c6cca 100644 --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -919,14 +919,17 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, if (!BeginSourceFileAction(CI)) return false; - // If we were asked to load any module map files, do so now. - for (const auto &Filename : CI.getFrontendOpts().ModuleMapFiles) { + auto LoadModuleMap = [&](StringRef Filename) { if (auto File = CI.getFileManager().getOptionalFileRef(Filename)) CI.getPreprocessor().getHeaderSearchInfo().loadModuleMapFile( *File, /*IsSystem*/false); else CI.getDiagnostics().Report(diag::err_module_map_not_found) << Filename; - } + }; + // If we were asked to load any module map files, do so now. + for (StringRef Filename : CI.getFrontendOpts().ModuleMapFiles) + LoadModuleMap(Filename); + // Note that late module maps will be loaded after modules. // If compiling implementation of a module, load its module map file now. (void)CI.getPreprocessor().getCurrentModuleImplementation(); @@ -1038,6 +1041,11 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, diag::warn_eagerly_load_for_standard_cplusplus_modules); } + // Some module maps are loaded after modules to allow avoiding redundant + // processing if they were processed by modules. + for (StringRef Filename : CI.getFrontendOpts().LateModuleMapFiles) + LoadModuleMap(Filename); + // If there is a layout overrides file, attach an external AST source that // provides the layouts from that file. if (!CI.getFrontendOpts().OverrideRecordLayoutsFile.empty() && diff --git a/clang/lib/Frontend/Rewrite/FrontendActions.cpp b/clang/lib/Frontend/Rewrite/FrontendActions.cpp index cf5a9437e89e6..f6e7aea06535d 100644 --- a/clang/lib/Frontend/Rewrite/FrontendActions.cpp +++ b/clang/lib/Frontend/Rewrite/FrontendActions.cpp @@ -255,6 +255,7 @@ class RewriteIncludesAction::RewriteImportsListener : public ASTReaderListener { Filename, InputKind(Language::Unknown, InputKind::Precompiled)); Instance.getFrontendOpts().ModuleFiles.clear(); Instance.getFrontendOpts().ModuleMapFiles.clear(); + Instance.getFrontendOpts().LateModuleMapFiles.clear(); // Don't recursively rewrite imports. We handle them all at the top level. Instance.getPreprocessorOutputOpts().RewriteImports = false; diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index eed7eca2e7356..232420b58f600 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -3130,7 +3130,10 @@ bool ModuleMap::parseModuleMapFile(FileEntryRef File, bool IsSystem, if (ID.isInvalid()) { auto FileCharacter = IsSystem ? SrcMgr::C_System_ModuleMap : SrcMgr::C_User_ModuleMap; - ID = SourceMgr.createFileID(File, ExternModuleLoc, FileCharacter); + if (ExternModuleLoc.isValid()) + ID = SourceMgr.createFileID(File, ExternModuleLoc, FileCharacter); + else // avoid creating redundant FileIDs, they take sloc space in PCMs. + ID = SourceMgr.getOrCreateFileID(File, FileCharacter); } assert(Target && "Missing target information"); diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index e19f19b2528c1..a45a8e5267389 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -242,6 +242,8 @@ ModuleDepCollector::getInvocationAdjustedForModuleBuildWithoutOutputs( // Remove directly passed modulemap files. They will get added back if they // were actually used. CI.getMutFrontendOpts().ModuleMapFiles.clear(); + // Late module maps can only be readded into ModuleMapFiles. + CI.getMutFrontendOpts().LateModuleMapFiles.clear(); auto DepModuleMapFiles = collectModuleMapFiles(Deps.ClangModuleDeps); for (StringRef ModuleMapFile : Deps.ModuleMapFileDeps) { diff --git a/clang/test/Modules/late-module-maps.cpp b/clang/test/Modules/late-module-maps.cpp new file mode 100644 index 0000000000000..2ca4af3ae203a --- /dev/null +++ b/clang/test/Modules/late-module-maps.cpp @@ -0,0 +1,93 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +// We need to check we don't waste source location space for the same file +// (i.e. base.modulemap) when it's passed to multiple PCM file. +// +// *** First, try to use normal module map files. +// RUN: %clang_cc1 -fmodules -fno-implicit-modules \ +// RUN: -fmodule-map-file=%t/base.modulemap -fmodule-map-file=%t/a.modulemap \ +// RUN: -fmodule-name=a -xc++ -emit-module -o %t/a.pcm %t/a.modulemap + +// RUN: %clang_cc1 -fmodules -fno-implicit-modules \ +// RUN: -fmodule-map-file=%t/base.modulemap -fmodule-map-file=%t/a.modulemap -fmodule-map-file=%t/b.modulemap \ +// RUN: -fmodule-file=%t/a.pcm \ +// RUN: -fmodule-name=b -xc++ -emit-module -o %t/b.pcm %t/b.modulemap + +// RUN: %clang_cc1 -fmodules -fno-implicit-modules \ +// RUN: -fmodule-map-file=%t/base.modulemap -fmodule-map-file=%t/a.modulemap -fmodule-map-file=%t/b.modulemap \ +// RUN: -fmodule-file=%t/a.pcm -fmodule-file=%t/b.pcm \ +// RUN: -fsyntax-only -print-stats %t/use.cpp 2>&1 \ +// RUN: | FileCheck %t/use.cpp + +// *** Switch to -flate-module-map-file and check it produces less loaded SLO entries. +// RUN: rm %t/*.pcm + +// RUN: %clang_cc1 -fmodules -fno-implicit-modules \ +// RUN: -flate-module-map-file=%t/base.modulemap -flate-module-map-file=%t/a.modulemap \ +// RUN: -fmodule-name=a -xc++ -emit-module -o %t/a.pcm %t/a.modulemap + +// RUN: %clang_cc1 -fmodules -fno-implicit-modules \ +// RUN: -flate-module-map-file=%t/base.modulemap -flate-module-map-file=%t/a.modulemap -flate-module-map-file=%t/b.modulemap \ +// RUN: -fmodule-file=%t/a.pcm \ +// RUN: -fmodule-name=b -xc++ -emit-module -o %t/b.pcm %t/b.modulemap + +// RUN: %clang_cc1 -fmodules -fno-implicit-modules \ +// RUN: -flate-module-map-file=%t/base.modulemap -flate-module-map-file=%t/a.modulemap -flate-module-map-file=%t/b.modulemap \ +// RUN: -fmodule-file=%t/a.pcm -fmodule-file=%t/b.pcm \ +// RUN: -fsyntax-only -print-stats %t/use.cpp 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-LATE %t/use.cpp + +//--- use.cpp +// This is a very shaky check, it would be nice if it was more directly looking at which files were loaded. +// We load 2 SLocEntries less with flate-module-map-file, they correspond to savings from base.modulemap and a.modulemap +// reusing the FileID in a.pcm and b.pcm. +// We also have 3 less local SLocEntries, because we get to reuse FileIDs all module maps from PCMs. +// +// CHECK: Source Manager Stats: +// CHECK: 7 local SLocEntries +// CHECK: 13 loaded SLocEntries + +// CHECK-LATE: Source Manager Stats: +// CHECK-LATE: 4 local SLocEntries +// CHECK-LATE: 11 loaded SLocEntries +#include "a.h" +#include "b.h" +#include "assert.h" + +int main() { + return a() + b(); +} + +//--- base.modulemap +module "base" { + textual header "assert.h" +} + +//--- a.modulemap +module "a" { + header "a.h" + use "base" +} + +//--- b.modulemap +module "b" { + header "b.h" + use "a" + use "base" +} + + +//--- assert.h +#define ASSERT + +//--- a.h +#include "assert.h" +inline int a() { return 1; } + +//--- b.h +#include "a.h" +#include "assert.h" + +inline int b() { return a() + 1; } +