From 8baafcdac143d6cb0c92eb59b19f3cc9f5ca1c77 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu Date: Tue, 12 Mar 2024 17:26:49 +0800 Subject: [PATCH] [C++20] [Modules] Introduce -fgen-reduced-bmi --- clang/include/clang/CodeGen/CodeGenAction.h | 2 + clang/include/clang/Driver/Options.td | 6 +++ .../include/clang/Frontend/FrontendOptions.h | 9 +++- clang/lib/CodeGen/CodeGenAction.cpp | 19 +++++++ clang/lib/Driver/Driver.cpp | 12 ++++- clang/lib/Driver/ToolChains/Clang.cpp | 18 +++++++ clang/lib/Frontend/FrontendActions.cpp | 7 +++ .../test/Driver/module-fgen-reduced-bmi.cppm | 51 +++++++++++++++++++ clang/test/Modules/modules-reduced-bmi.cppm | 36 +++++++++++++ 9 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 clang/test/Driver/module-fgen-reduced-bmi.cppm create mode 100644 clang/test/Modules/modules-reduced-bmi.cppm diff --git a/clang/include/clang/CodeGen/CodeGenAction.h b/clang/include/clang/CodeGen/CodeGenAction.h index 7ad2988e589eb2..186dbb43f01ef7 100644 --- a/clang/include/clang/CodeGen/CodeGenAction.h +++ b/clang/include/clang/CodeGen/CodeGenAction.h @@ -57,6 +57,8 @@ class CodeGenAction : public ASTFrontendAction { bool loadLinkModules(CompilerInstance &CI); protected: + bool BeginSourceFileAction(CompilerInstance &CI) override; + /// Create a new code generation action. If the optional \p _VMContext /// parameter is supplied, the action uses it without taking ownership, /// otherwise it creates a fresh LLVM context and takes ownership. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index f5289fb00c895e..b4faae42a9ce5c 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3017,6 +3017,7 @@ defm prebuilt_implicit_modules : BoolFOption<"prebuilt-implicit-modules", def fmodule_output_EQ : Joined<["-"], "fmodule-output=">, Flags<[NoXarchOption]>, Visibility<[ClangOption, CC1Option]>, + MarshallingInfoString>, HelpText<"Save intermediate module file results when compiling a standard C++ module unit.">; def fmodule_output : Flag<["-"], "fmodule-output">, Flags<[NoXarchOption]>, Visibility<[ClangOption, CC1Option]>, @@ -3030,6 +3031,11 @@ defm skip_odr_check_in_gmf : BoolOption<"f", "skip-odr-check-in-gmf", "Perform ODR checks for decls in the global module fragment.">>, Group; +def modules_reduced_bmi : Flag<["-"], "fexperimental-modules-reduced-bmi">, + Group, Visibility<[ClangOption, CC1Option]>, + HelpText<"Generate the reduced BMI">, + MarshallingInfoFlag>; + def fmodules_prune_interval : Joined<["-"], "fmodules-prune-interval=">, Group, Visibility<[ClangOption, CC1Option]>, MetaVarName<"">, HelpText<"Specify the interval (in seconds) between attempts to prune the module cache">, diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index 8085dbcbf671a6..ddfd4f30d1b773 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -387,6 +387,10 @@ class FrontendOptions { LLVM_PREFERRED_TYPE(bool) unsigned ModulesShareFileManager : 1; + /// Whether to generate reduced BMI for C++20 named modules. + LLVM_PREFERRED_TYPE(bool) + unsigned GenReducedBMI : 1; + CodeCompleteOptions CodeCompleteOpts; /// Specifies the output format of the AST. @@ -553,6 +557,9 @@ class FrontendOptions { /// Path which stores the output files for -ftime-trace std::string TimeTracePath; + /// Output Path for module output file. + std::string ModuleOutputPath; + public: FrontendOptions() : DisableFree(false), RelocatablePCH(false), ShowHelp(false), @@ -565,7 +572,7 @@ class FrontendOptions { BuildingImplicitModuleUsesLock(true), ModulesEmbedAllFiles(false), IncludeTimestamps(true), UseTemporary(true), AllowPCMWithCompilerErrors(false), ModulesShareFileManager(true), - TimeTraceGranularity(500) {} + GenReducedBMI(false), TimeTraceGranularity(500) {} /// getInputKindForExtension - Return the appropriate input kind for a file /// extension. For example, "c" would return Language::C. diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp index bb9aaba025fa59..cd0d011e8c72b0 100644 --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -25,8 +25,11 @@ #include "clang/CodeGen/ModuleBuilder.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/MultiplexConsumer.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Serialization/ASTWriter.h" #include "llvm/ADT/Hashing.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h" @@ -1003,6 +1006,12 @@ CodeGenerator *CodeGenAction::getCodeGenerator() const { return BEConsumer->getCodeGenerator(); } +bool CodeGenAction::BeginSourceFileAction(CompilerInstance &CI) { + if (CI.getFrontendOpts().GenReducedBMI) + CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleInterface); + return true; +} + static std::unique_ptr GetOutputStream(CompilerInstance &CI, StringRef InFile, BackendAction Action) { switch (Action) { @@ -1061,6 +1070,16 @@ CodeGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { CI.getPreprocessor().addPPCallbacks(std::move(Callbacks)); } + if (CI.getFrontendOpts().GenReducedBMI && + !CI.getFrontendOpts().ModuleOutputPath.empty()) { + std::vector> Consumers{2}; + Consumers[0] = std::make_unique( + CI.getPreprocessor(), CI.getModuleCache(), + CI.getFrontendOpts().ModuleOutputPath); + Consumers[1] = std::move(Result); + return std::make_unique(std::move(Consumers)); + } + return std::move(Result); } diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 1a0f5f27eda2fc..b2be4f6cc74024 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -4749,6 +4749,14 @@ Action *Driver::ConstructPhaseAction( if (Args.hasArg(options::OPT_extract_api)) return C.MakeAction(Input, types::TY_API_INFO); + // With 'fexperimental-modules-reduced-bmi', we don't want to run the + // precompile phase unless the user specified '--precompile'. In the case + // the '--precompile' flag is enabled, we will try to emit the reduced BMI + // as a by product in GenerateModuleInterfaceAction. + if (Args.hasArg(options::OPT_modules_reduced_bmi) && + !Args.getLastArg(options::OPT__precompile)) + return Input; + types::ID OutputTy = getPrecompiledType(Input->getType()); assert(OutputTy != types::TY_INVALID && "Cannot precompile this input type!"); @@ -5903,8 +5911,10 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, // If we're emitting a module output with the specified option // `-fmodule-output`. if (!AtTopLevel && isa(JA) && - JA.getType() == types::TY_ModuleFile && SpecifiedModuleOutput) + JA.getType() == types::TY_ModuleFile && SpecifiedModuleOutput) { + assert(!C.getArgs().hasArg(options::OPT_modules_reduced_bmi)); return GetModuleOutputPath(C, JA, BaseInput); + } // Output to a temporary file? if ((!AtTopLevel && !isSaveTempsEnabled() && diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 7fd6ad6a47d46f..d73630baba9672 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4045,6 +4045,24 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D, // module fragment. CmdArgs.push_back("-fskip-odr-check-in-gmf"); + if (Args.hasArg(options::OPT_modules_reduced_bmi) && + (Input.getType() == driver::types::TY_CXXModule || + Input.getType() == driver::types::TY_PP_CXXModule)) { + CmdArgs.push_back("-fexperimental-modules-reduced-bmi"); + + if (Args.hasArg(options::OPT_fmodule_output_EQ)) + Args.AddLastArg(CmdArgs, options::OPT_fmodule_output_EQ); + else + CmdArgs.push_back(Args.MakeArgString( + "-fmodule-output=" + + getCXX20NamedModuleOutputPath(Args, Input.getBaseInput()))); + } + + // Noop if we see '-fexperimental-modules-reduced-bmi' with other translation + // units than module units. This is more user friendly to allow end uers to + // enable this feature without asking for help from build systems. + Args.ClaimAllArgs(options::OPT_modules_reduced_bmi); + // We need to include the case the input file is a module file here. // Since the default compilation model for C++ module interface unit will // create temporary module file and compile the temporary module file diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 0bc26b694cfc8d..2f077c972d6ed8 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -284,6 +284,13 @@ GenerateModuleInterfaceAction::CreateASTConsumer(CompilerInstance &CI, if (Consumers.empty()) return nullptr; + if (CI.getFrontendOpts().GenReducedBMI && + !CI.getFrontendOpts().ModuleOutputPath.empty()) { + Consumers.push_back(std::make_unique( + CI.getPreprocessor(), CI.getModuleCache(), + CI.getFrontendOpts().ModuleOutputPath)); + } + return std::make_unique(std::move(Consumers)); } diff --git a/clang/test/Driver/module-fgen-reduced-bmi.cppm b/clang/test/Driver/module-fgen-reduced-bmi.cppm new file mode 100644 index 00000000000000..1223189fb49b72 --- /dev/null +++ b/clang/test/Driver/module-fgen-reduced-bmi.cppm @@ -0,0 +1,51 @@ +// It is annoying to handle different slash direction +// in Windows and Linux. So we disable the test on Windows +// here. +// REQUIRES: !system-windows +// On AIX, the default output for `-c` may be `.s` instead of `.o`, +// which makes the test fail. So disable the test on AIX. +// UNSUPPORTED: system-aix +// +// RUN: rm -rf %t && split-file %s %t && cd %t +// +// RUN: %clang -std=c++20 Hello.cppm -fmodule-output=Hello.pcm \ +// RUN: -fexperimental-modules-reduced-bmi -c -o Hello.o -### 2>&1 | FileCheck Hello.cppm +// +// RUN: %clang -std=c++20 Hello.cppm \ +// RUN: -fexperimental-modules-reduced-bmi -c -o Hello.o -### 2>&1 | \ +// RUN: FileCheck Hello.cppm --check-prefix=CHECK-UNSPECIFIED +// +// RUN: %clang -std=c++20 Hello.cppm \ +// RUN: -fexperimental-modules-reduced-bmi -c -### 2>&1 | \ +// RUN: FileCheck Hello.cppm --check-prefix=CHECK-NO-O +// +// RUN: %clang -std=c++20 Hello.cppm \ +// RUN: -fexperimental-modules-reduced-bmi -c -o AnotherName.o -### 2>&1 | \ +// RUN: FileCheck Hello.cppm --check-prefix=CHECK-ANOTHER-NAME +// +// RUN: %clang -std=c++20 Hello.cppm --precompile -fexperimental-modules-reduced-bmi \ +// RUN: -o Hello.full.pcm -### 2>&1 | FileCheck Hello.cppm \ +// RUN: --check-prefix=CHECK-EMIT-MODULE-INTERFACE +// +// RUN: %clang -std=c++20 Hello.cc -fexperimental-modules-reduced-bmi -Wall -Werror \ +// RUN: -c -o Hello.o -### 2>&1 | FileCheck Hello.cc + +//--- Hello.cppm +export module Hello; + +// Test that we won't generate the emit-module-interface as 2 phase compilation model. +// CHECK-NOT: -emit-module-interface +// CHECK: "-fexperimental-modules-reduced-bmi" + +// CHECK-UNSPECIFIED: -fmodule-output=Hello.pcm + +// CHECK-NO-O: -fmodule-output=Hello.pcm +// CHECK-ANOTHER-NAME: -fmodule-output=AnotherName.pcm + +// With `-emit-module-interface` specified, we should still see the `-emit-module-interface` +// flag. +// CHECK-EMIT-MODULE-INTERFACE: -emit-module-interface + +//--- Hello.cc + +// CHECK-NOT: "-fexperimental-modules-reduced-bmi" diff --git a/clang/test/Modules/modules-reduced-bmi.cppm b/clang/test/Modules/modules-reduced-bmi.cppm new file mode 100644 index 00000000000000..9b84220ae03032 --- /dev/null +++ b/clang/test/Modules/modules-reduced-bmi.cppm @@ -0,0 +1,36 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-reduced-module-interface -o %t/a.reduced.pcm +// RUN: %clang_cc1 -std=c++20 %t/a.cppm -fexperimental-modules-reduced-bmi -fmodule-output=%t/a.pcm \ +// RUN: -S -emit-llvm -o %t/a.ll +// +// Test that the generated BMI from `-fexperimental-modules-reduced-bmi -fmodule-output=` is same with +// `-emit-reduced-module-interface`. +// RUN: diff %t/a.reduced.pcm %t/a.pcm +// +// Test that we can consume the produced BMI correctly. +// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fmodule-file=a=%t/a.pcm -fsyntax-only -verify +// +// RUN: rm -f %t/a.pcm +// RUN: %clang_cc1 -std=c++20 %t/a.cppm -fexperimental-modules-reduced-bmi -fmodule-output=%t/a.pcm \ +// RUN: -emit-module-interface -o %t/a.full.pcm +// RUN: diff %t/a.reduced.pcm %t/a.pcm +// RUN: not diff %t/a.pcm %t/a.full.pcm +// +// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fmodule-file=a=%t/a.pcm -fsyntax-only -verify +// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fmodule-file=a=%t/a.full.pcm -fsyntax-only -verify + +//--- a.cppm +export module a; +export int a() { + return 43; +} + +//--- b.cppm +// Test that we can consume the produced BMI correctly as a smocking test. +// expected-no-diagnostics +export module b; +import a; +export int b() { return a(); }