https://github.com/naveen-seth created https://github.com/llvm/llvm-project/pull/199289
With this, Standard library modules are always precompiled as the primary output of their `-cc1` invocation (instead of being produced as a byproduct of compiling the Standard library modules to object files). This also keeps Standard library module precompilation independent of the final phase specified on the command line, so importing them keep working under `-fsyntax-only` (and other command-line options that specify the final phase). This also makes the Standard library module precompilation independent of the `-o` flag, so that a command like `clang -std=c++23 -fmodules-driver main.cpp -o main` no longer redirects the Standard library module outputs to 'main', breaking the compilation. >From f4dc2ea4504345ba3f9a8695329fa161872017aa Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig <[email protected]> Date: Fri, 22 May 2026 23:05:50 +0200 Subject: [PATCH] [clang][modules-driver] Precompile std modules independently of -o and final phase Standard library modules are now always precompiled as the primary output of their -cc1 invocation (instead of being produced as a byproduct of compiling the Standard library modules to object files). This also keeps std module precompilation independent of the final phase specified on the command line, so imports keep working under -fsyntax-only (and other command-line options that select the final phase). This also makes the Standard library module precompilation independent of the -o flag, so a command like 'clang -std=c++23 -fmodules-driver main.cpp -o main' no longer redirects the Standard library module outputs to 'main', breaking the compilation. --- clang/include/clang/Driver/Types.def | 2 + clang/lib/Driver/Driver.cpp | 57 +++++++++++---- clang/lib/Driver/ModulesDriver.cpp | 21 +++++- clang/lib/Driver/ToolChains/Clang.cpp | 6 +- clang/lib/Driver/Types.cpp | 13 +++- .../InterpolatingCompilationDatabase.cpp | 2 + .../test/Driver/modules-driver-import-std.cpp | 71 +++++++++++++++++++ 7 files changed, 154 insertions(+), 18 deletions(-) diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def index 76944ec656917..5c325f5685c44 100644 --- a/clang/include/clang/Driver/Types.def +++ b/clang/include/clang/Driver/Types.def @@ -73,6 +73,8 @@ TYPE("objective-c++-header-cpp-output", PP_ObjCXXHeader, INVALID,"mii", phases TYPE("objective-c++-header", ObjCXXHeader, PP_ObjCXXHeader, "h", phases::Preprocess, phases::Precompile) TYPE("c++-module", CXXModule, PP_CXXModule, "cppm", phases::Preprocess, phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("c++-module-cpp-output", PP_CXXModule, INVALID, "iim", phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("c++-std-module", CXXStdModule, PP_CXXStdModule, "cppm", phases::Preprocess, phases::Precompile) +TYPE("c++-std-module-output", PP_CXXStdModule, INVALID, "iim", phases::Precompile) // Other languages. TYPE("ada", Ada, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 4a968a4ce5cc0..398f40920c15e 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -5275,6 +5275,13 @@ Action *Driver::ConstructPhaseAction( if (Args.hasArg(options::OPT_extract_api)) return C.MakeAction<ExtractAPIJobAction>(Input, types::TY_API_INFO); + // Standard library modules always precompile in -fmodules-driver mode, + // even when -fsyntax-only is specified. + if (Input->getType() == types::TY_CXXStdModule || + Input->getType() == types::TY_PP_CXXStdModule) + return C.MakeAction<PrecompileJobAction>( + Input, getPrecompiledType(Input->getType())); + // With 'fmodules-reduced-bmi', we don't want to run the // precompile phase unless the user specified '--precompile' or // '--precompile-reduced-bmi'. If '--precompile' is specified, we will try @@ -5471,6 +5478,14 @@ void Driver::BuildJobs(Compilation &C) const { A->getKind() == clang::driver::Action::BinaryTranslatorJobClass) continue; + if (isa<PrecompileJobAction>(A) && !A->getInputs().empty()) { + const Action *Input = A->getInputs().front(); + types::ID InputType = Input->getType(); + if (InputType == types::TY_CXXStdModule || + InputType == types::TY_PP_CXXStdModule) + continue; + } + if (A->getType() != types::TY_Nothing && !(A->getKind() == Action::IfsMergeJobClass || (A->getType() == clang::driver::types::TY_IFS_CPP && @@ -6472,6 +6487,34 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, std::string BoundArch = sanitizeTargetIDInFileName(OrigBoundArch); llvm::PrettyStackTraceString CrashInfo("Computing output path"); + + auto CreateTempOutputPath = [&](StringRef Prefix) { + const char *Suffix = + types::getTypeTempSuffix(JA.getType(), IsCLMode() || IsDXCMode()); + // The non-offloading toolchain on Darwin requires deterministic input + // file name for binaries to be deterministic, therefore it needs unique + // directory. + llvm::Triple Triple(C.getDriver().getTargetTriple()); + bool NeedUniqueDirectory = + (JA.getOffloadingDeviceKind() == Action::OFK_None || + JA.getOffloadingDeviceKind() == Action::OFK_Host) && + Triple.isOSDarwin(); + return CreateTempFile(C, Prefix, Suffix, MultipleArchs, BoundArch, + NeedUniqueDirectory); + }; + + const bool IsModulesDriver = C.getArgs().hasArg(options::OPT_fmodules_driver); + + // Standard library output in -fmodules-driver? + if (IsModulesDriver && isa<PrecompileJobAction>(JA) && + !JA.getInputs().empty() && + (JA.getInputs().front()->getType() == types::TY_CXXStdModule || + JA.getInputs().front()->getType() == types::TY_PP_CXXStdModule)) { + StringRef Filename = llvm::sys::path::filename(BaseInput); + StringRef Stem = llvm::sys::path::stem(Filename); + return CreateTempOutputPath(Stem); + } + // Output to a user requested destination? if (AtTopLevel && !isa<DsymutilJobAction>(JA) && !isa<VerifyJobAction>(JA)) { if (Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o)) @@ -6570,19 +6613,7 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, !C.getArgs().hasArg(options::OPT__SLASH_Fo)) || CCGenDiagnostics) { StringRef Name = llvm::sys::path::filename(BaseInput); - std::pair<StringRef, StringRef> Split = Name.split('.'); - const char *Suffix = - types::getTypeTempSuffix(JA.getType(), IsCLMode() || IsDXCMode()); - // The non-offloading toolchain on Darwin requires deterministic input - // file name for binaries to be deterministic, therefore it needs unique - // directory. - llvm::Triple Triple(C.getDriver().getTargetTriple()); - bool NeedUniqueDirectory = - (JA.getOffloadingDeviceKind() == Action::OFK_None || - JA.getOffloadingDeviceKind() == Action::OFK_Host) && - Triple.isOSDarwin(); - return CreateTempFile(C, Split.first, Suffix, MultipleArchs, BoundArch, - NeedUniqueDirectory); + return CreateTempOutputPath(Name.split('.').first); } SmallString<128> BasePath(BaseInput); diff --git a/clang/lib/Driver/ModulesDriver.cpp b/clang/lib/Driver/ModulesDriver.cpp index 3f7a9f346accd..a09f69d6aad4e 100644 --- a/clang/lib/Driver/ModulesDriver.cpp +++ b/clang/lib/Driver/ModulesDriver.cpp @@ -146,7 +146,7 @@ void driver::modules::buildStdModuleManifestInputs( for (const auto &Entry : ManifestEntries) { auto *InputArg = makeInputArg(Args, Opts, Args.MakeArgString(Entry.SourcePath)); - Inputs.emplace_back(types::TY_CXXModule, InputArg); + Inputs.emplace_back(types::TY_CXXStdModule, InputArg); } } @@ -1213,7 +1213,8 @@ static bool validateScannedJobInputKinds( const auto &MainInput = Job.getInputInfos().front(); const bool DefinesNamedModule = !InputDeps.ModuleName.empty(); - if (DefinesNamedModule && MainInput.getType() != types::TY_CXXModule) { + if (DefinesNamedModule && MainInput.getType() != types::TY_CXXModule && + MainInput.getType() != types::TY_CXXStdModule) { Diags.Report(diag::err_module_defined_outside_of_module_source) << InputDeps.ModuleName << MainInput.getFilename(); return false; @@ -1597,6 +1598,22 @@ static void fixupNamedModuleCommandLines(Compilation &C, llvm::CastTo<NamedModuleJobNode>); for (NamedModuleJobNode *Node : NamedModuleNodes) { + const auto &Job = *Node->Job; + + // For Standard library modules, the driver already creates the module + // output as a temp file, so we can use that path directly. + const bool IsStdModule = + Job.getInputInfos().front().getType() == types::TY_CXXStdModule; + if (IsStdModule) { + const auto &OutputFilenames = Job.getOutputFilenames(); + if (OutputFilenames.empty()) + continue; + + StringRef ModuleOutputPath = Job.getOutputFilenames().front(); + propagateModuleFileMappingArg(C, *Node, ModuleOutputPath); + continue; + } + const StringRef ModuleName = Node->InputDeps.ModuleName; const auto ModuleOutputPath = createModuleOutputPath(C, ModuleName); C.addTempFile(C.getArgs().MakeArgString(ModuleOutputPath)); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 2b415e60d5331..bf467b8fb0fab 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -349,6 +349,7 @@ static void addDashXForInput(const ArgList &Args, const InputInfo &Input, const char *ClangType; switch (Input.getType()) { case types::TY_CXXModule: + case types::TY_CXXStdModule: ClangType = "c++"; break; case types::TY_PP_CXXModule: @@ -5270,7 +5271,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (JA.getType() == types::TY_Nothing) CmdArgs.push_back("-fsyntax-only"); else if (JA.getType() == types::TY_ModuleFile) { - if (Args.hasArg(options::OPT__precompile_reduced_bmi)) + if (Args.hasArg(options::OPT__precompile_reduced_bmi) || + ((Input.getType() == types::TY_CXXStdModule || + Input.getType() == types::TY_PP_CXXStdModule) && + !Args.hasArg(options::OPT_fno_modules_reduced_bmi))) CmdArgs.push_back("-emit-reduced-module-interface"); else CmdArgs.push_back("-emit-module-interface"); diff --git a/clang/lib/Driver/Types.cpp b/clang/lib/Driver/Types.cpp index 08866e89ea447..69b6028bfbb13 100644 --- a/clang/lib/Driver/Types.cpp +++ b/clang/lib/Driver/Types.cpp @@ -59,7 +59,8 @@ types::ID types::getPreprocessedType(ID Id) { } static bool isPreprocessedModuleType(ID Id) { - return Id == TY_CXXModule || Id == TY_PP_CXXModule; + return Id == TY_CXXModule || Id == TY_PP_CXXModule || Id == TY_CXXStdModule || + Id == TY_PP_CXXStdModule; } static bool isPreprocessedHeaderUnitType(ID Id) { @@ -148,7 +149,10 @@ bool types::isAcceptedByClang(ID Id) { case TY_CXXHUHeader: case TY_PP_CXXHeaderUnit: case TY_ObjCXXHeader: case TY_PP_ObjCXXHeader: - case TY_CXXModule: case TY_PP_CXXModule: + case TY_CXXModule: + case TY_PP_CXXModule: + case TY_CXXStdModule: + case TY_PP_CXXStdModule: case TY_AST: case TY_ModuleFile: case TY_PCH: case TY_LLVM_IR: case TY_LLVM_BC: case TY_API_INFO: @@ -209,6 +213,8 @@ bool types::isDerivedFromC(ID Id) { case TY_ObjCXXHeader: case TY_CXXModule: case TY_PP_CXXModule: + case TY_CXXStdModule: + case TY_PP_CXXStdModule: return true; } } @@ -253,6 +259,8 @@ bool types::isCXX(ID Id) { case TY_ObjCXXHeader: case TY_PP_ObjCXXHeader: case TY_CXXModule: case TY_PP_CXXModule: + case TY_CXXStdModule: + case TY_PP_CXXStdModule: case TY_ModuleFile: case TY_PP_CLCXX: case TY_CUDA: case TY_PP_CUDA: case TY_CUDA_DEVICE: @@ -435,6 +443,7 @@ ID types::lookupHeaderTypeForSourceType(ID Id) { return types::TY_CHeader; case types::TY_CXX: case types::TY_CXXModule: + case types::TY_CXXStdModule: return types::TY_CXXHeader; case types::TY_ObjC: return types::TY_ObjCHeader; diff --git a/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp b/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp index 08ee02f639a4f..36d0775a80708 100644 --- a/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp +++ b/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp @@ -113,6 +113,8 @@ static types::ID foldType(types::ID Lang) { case types::TY_CXXHeader: case types::TY_CXXModule: case types::TY_PP_CXXModule: + case types::TY_CXXStdModule: + case types::TY_PP_CXXStdModule: return types::TY_CXX; case types::TY_ObjCXX: case types::TY_ObjCXXHeader: diff --git a/clang/test/Driver/modules-driver-import-std.cpp b/clang/test/Driver/modules-driver-import-std.cpp index 3a51cab0406b2..0ce2a5d36af50 100644 --- a/clang/test/Driver/modules-driver-import-std.cpp +++ b/clang/test/Driver/modules-driver-import-std.cpp @@ -57,9 +57,80 @@ int main() {} // RUN: -L%t/Inputs/usr/lib/x86_64-linux-gnu \ // RUN: --target=x86_64-linux-gnu \ // RUN: -c %t/main.cpp 2>&1 \ +// RUN: --verbose \ // RUN: | sed 's:\\\\\?:/:g' \ // RUN: | FileCheck -DPREFIX=%/t %s +// Skip past diagnostics omitted during the dependency scan. +// CHECK: clang: remark: printing module dependency graph [-Rmodules-driver] + +// Checks that the standard library modules are primary outputs. +// TODO: Test the same for -fno-modules-reduced-bmi when supported by +// -fmodules-driver. +// CHECK: -o {{.*std-[^ ]*\.pcm}} +// CHECK-SAME: -emit-reduced-module-interface +// CHECK-SAME: -main-file-name std.cppm +// CHECK: -o {{.*std\.compat-[^ ]*\.pcm}} +// CHECK-SAME: -emit-reduced-module-interface +// CHECK-SAME: -main-file-name std.compat.cppm + +// Checks that the object file is generated for the main TU. +// CHECK: -emit-obj +// CHECK-SAME: -main-file-name main.cpp + +// Checks that the standard library modules are successfully imported. // CHECK: [[PREFIX]]/main.cpp:1:1: remark: importing module 'std.compat' from // CHECK: [[PREFIX]]/main.cpp:1:1: remark: importing module 'std' into 'std.compat' from // CHECK: [[PREFIX]]/main.cpp:2:1: remark: importing module 'std' from + +// Checks that standard library modules are still precompiled with -emit-llvm. +// RUN: %clang -std=c++23 -stdlib=libc++ \ +// RUN: -fmodules-driver -Rmodules-driver -Rmodule-import \ +// RUN: -stdlib=libc++ \ +// RUN: -resource-dir=%t/FakeSysroot/usr/lib/x86_64-linux-gnu \ +// RUN: --sysroot=%t/FakeSysroot \ +// RUN: -L%t/Inputs/usr/lib/x86_64-linux-gnu \ +// RUN: --target=x86_64-linux-gnu \ +// RUN: %t/main.cpp -S -emit-llvm -o out.ll \ +// RUN: --verbose 2>&1 \ +// RUN: | sed 's:\\\\\?:/:g' \ +// RUN: | FileCheck -DPREFIX=%/t --check-prefix=CHECK-EMIT-LLVM %s + +// Skip past diagnostics omitted during the dependency scan. +// CHECK-EMIT-LLVM: clang: remark: printing module dependency graph [-Rmodules-driver] + +// CHECK-EMIT-LLVM: -o {{.*std-[^ ]*\.pcm}} +// CHECK-EMIT-LLVM-SAME: -emit-reduced-module-interface +// CHECK-EMIT-LLVM-SAME: -main-file-name std.cppm +// CHECK-EMIT-LLVM: -o {{.*std\.compat-[^ ]*\.pcm}} +// CHECK-EMIT-LLVM-SAME: -emit-reduced-module-interface +// CHECK-EMIT-LLVM-SAME: -main-file-name std.compat.cppm +// CHECK-EMIT-LLVM: -emit-llvm +// CHECK-EMIT-LLVM-SAME: -main-file-name main.cpp + +// Checks that standard library modules are still precompiled with -fsyntax-only. +// RUN: %clang -std=c++23 -stdlib=libc++ \ +// RUN: -fmodules-driver -Rmodules-driver -Rmodule-import \ +// RUN: -stdlib=libc++ \ +// RUN: -resource-dir=%t/FakeSysroot/usr/lib/x86_64-linux-gnu \ +// RUN: --sysroot=%t/FakeSysroot \ +// RUN: -L%t/Inputs/usr/lib/x86_64-linux-gnu \ +// RUN: --target=x86_64-linux-gnu \ +// RUN: -fsyntax-only \ +// RUN: %t/main.cpp \ +// RUN: --verbose 2>&1 \ +// RUN: | sed 's:\\\\\?:/:g' \ +// RUN: | FileCheck -DPREFIX=%/t --check-prefix=CHECK-SYNTAX-ONLY %s + +// Skip past diagnostics omitted during the dependency scan. +// CHECK-SYNTAX-ONLY: clang: remark: printing module dependency graph [-Rmodules-driver] + +// CHECK-SYNTAX-ONLY: -o {{.*std-[^ ]*\.pcm}} +// CHECK-SYNTAX-ONLY-SAME: -emit-reduced-module-interface +// CHECK-SYNTAX-ONLY-SAME: -main-file-name std.cppm +// CHECK-SYNTAX-ONLY: -o {{.*std\.compat-[^ ]*\.pcm}} +// CHECK-SYNTAX-ONLY-SAME: -emit-reduced-module-interface +// CHECK-SYNTAX-ONLY-SAME: -main-file-name std.compat.cppm + +// CHECK-SYNTAX-ONLY: -cc1 {{.*}} -Rmodules-driver +// CHECK-SYNTAX-ONLY-NOT: {{ -o }} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
