From: Richard Smith Date: Sat, 15 Sep 2018 01:21:16 +0000 (+0000) Subject: [modules] Driver support for precompiling a collection of files as a single X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=80e197b92b65c2a3cf6293a53e955e83963bc065;p=clang [modules] Driver support for precompiling a collection of files as a single action. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@342305 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td index 262861b6a2..c0606e4770 100644 --- a/include/clang/Basic/DiagnosticDriverKinds.td +++ b/include/clang/Basic/DiagnosticDriverKinds.td @@ -296,6 +296,9 @@ def warn_drv_vectorize_needs_hvx : Warning< "auto-vectorization requires HVX, use -mhvx to enable it">, InGroup; +def err_drv_module_header_wrong_kind : Error< + "header file '%0' input type '%1' does not match type of prior input " + "in module compilation; use '-x %2' to override">; def err_drv_modules_validate_once_requires_timestamp : Error< "option '-fmodules-validate-once-per-build-session' requires " "'-fbuild-session-timestamp=' or '-fbuild-session-file='">; diff --git a/include/clang/Driver/Action.h b/include/clang/Driver/Action.h index 723fbbed35..f4aaa6c544 100644 --- a/include/clang/Driver/Action.h +++ b/include/clang/Driver/Action.h @@ -59,6 +59,7 @@ public: OffloadClass, PreprocessJobClass, PrecompileJobClass, + HeaderModulePrecompileJobClass, AnalyzeJobClass, MigrateJobClass, CompileJobClass, @@ -398,12 +399,36 @@ public: class PrecompileJobAction : public JobAction { void anchor() override; +protected: + PrecompileJobAction(ActionClass Kind, Action *Input, types::ID OutputType); + public: PrecompileJobAction(Action *Input, types::ID OutputType); static bool classof(const Action *A) { - return A->getKind() == PrecompileJobClass; + return A->getKind() == PrecompileJobClass || + A->getKind() == HeaderModulePrecompileJobClass; + } +}; + +class HeaderModulePrecompileJobAction : public PrecompileJobAction { + void anchor() override; + + const char *ModuleName; + +public: + HeaderModulePrecompileJobAction(Action *Input, types::ID OutputType, + const char *ModuleName); + + static bool classof(const Action *A) { + return A->getKind() == HeaderModulePrecompileJobClass; } + + void addModuleHeaderInput(Action *Input) { + getInputs().push_back(Input); + } + + const char *getModuleName() const { return ModuleName; } }; class AnalyzeJobAction : public JobAction { diff --git a/lib/Driver/Action.cpp b/lib/Driver/Action.cpp index f909043875..d4c7040a23 100644 --- a/lib/Driver/Action.cpp +++ b/lib/Driver/Action.cpp @@ -26,6 +26,7 @@ const char *Action::getClassName(ActionClass AC) { return "offload"; case PreprocessJobClass: return "preprocessor"; case PrecompileJobClass: return "precompiler"; + case HeaderModulePrecompileJobClass: return "header-module-precompiler"; case AnalyzeJobClass: return "analyzer"; case MigrateJobClass: return "migrator"; case CompileJobClass: return "compiler"; @@ -319,6 +320,19 @@ void PrecompileJobAction::anchor() {} PrecompileJobAction::PrecompileJobAction(Action *Input, types::ID OutputType) : JobAction(PrecompileJobClass, Input, OutputType) {} +PrecompileJobAction::PrecompileJobAction(ActionClass Kind, Action *Input, + types::ID OutputType) + : JobAction(Kind, Input, OutputType) { + assert(isa((Action*)this) && "invalid action kind"); +} + +void HeaderModulePrecompileJobAction::anchor() {} + +HeaderModulePrecompileJobAction::HeaderModulePrecompileJobAction( + Action *Input, types::ID OutputType, const char *ModuleName) + : PrecompileJobAction(HeaderModulePrecompileJobClass, Input, OutputType), + ModuleName(ModuleName) {} + void AnalyzeJobAction::anchor() {} AnalyzeJobAction::AnalyzeJobAction(Action *Input, types::ID OutputType) diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 908ffa3bd1..81369f995d 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -3010,6 +3010,7 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, OffloadingActionBuilder OffloadBuilder(C, Args, Inputs); // Construct the actions to perform. + HeaderModulePrecompileJobAction *HeaderModuleAction = nullptr; ActionList LinkerInputs; llvm::SmallVector PL; @@ -3106,13 +3107,29 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, break; } + // Each precompiled header file after a module file action is a module + // header of that same module file, rather than being compiled to a + // separate PCH. + if (Phase == phases::Precompile && HeaderModuleAction && + getPrecompiledType(InputType) == types::TY_PCH) { + HeaderModuleAction->addModuleHeaderInput(Current); + Current = nullptr; + break; + } + + // FIXME: Should we include any prior module file outputs as inputs of + // later actions in the same command line? + // Otherwise construct the appropriate action. - auto *NewCurrent = ConstructPhaseAction(C, Args, Phase, Current); + Action *NewCurrent = ConstructPhaseAction(C, Args, Phase, Current); // We didn't create a new action, so we will just move to the next phase. if (NewCurrent == Current) continue; + if (auto *HMA = dyn_cast(NewCurrent)) + HeaderModuleAction = HMA; + Current = NewCurrent; // Use the current host action in any of the offloading actions, if @@ -3192,10 +3209,25 @@ Action *Driver::ConstructPhaseAction( types::ID OutputTy = getPrecompiledType(Input->getType()); assert(OutputTy != types::TY_INVALID && "Cannot precompile this input type!"); + + // If we're given a module name, precompile header file inputs as a + // module, not as a precompiled header. + const char *ModName = nullptr; + if (OutputTy == types::TY_PCH) { + if (Arg *A = Args.getLastArg(options::OPT_fmodule_name_EQ)) + ModName = A->getValue(); + if (ModName) + OutputTy = types::TY_ModuleFile; + } + if (Args.hasArg(options::OPT_fsyntax_only)) { // Syntax checks should not emit a PCH file OutputTy = types::TY_Nothing; } + + if (ModName) + return C.MakeAction(Input, OutputTy, + ModName); return C.MakeAction(Input, OutputTy); } case phases::Compile: { @@ -3448,7 +3480,7 @@ class ToolSelector final { /// - Backend + Compile. const Tool * combineAssembleBackendCompile(ArrayRef ActionInfo, - const ActionList *&Inputs, + ActionList &Inputs, ActionList &CollapsedOffloadAction) { if (ActionInfo.size() < 3 || !canCollapseAssembleAction()) return nullptr; @@ -3474,13 +3506,13 @@ class ToolSelector final { if (!T->hasIntegratedAssembler()) return nullptr; - Inputs = &CJ->getInputs(); + Inputs = CJ->getInputs(); AppendCollapsedOffloadAction(CollapsedOffloadAction, ActionInfo, /*NumElements=*/3); return T; } const Tool *combineAssembleBackend(ArrayRef ActionInfo, - const ActionList *&Inputs, + ActionList &Inputs, ActionList &CollapsedOffloadAction) { if (ActionInfo.size() < 2 || !canCollapseAssembleAction()) return nullptr; @@ -3507,13 +3539,13 @@ class ToolSelector final { if (!T->hasIntegratedAssembler()) return nullptr; - Inputs = &BJ->getInputs(); + Inputs = BJ->getInputs(); AppendCollapsedOffloadAction(CollapsedOffloadAction, ActionInfo, /*NumElements=*/2); return T; } const Tool *combineBackendCompile(ArrayRef ActionInfo, - const ActionList *&Inputs, + ActionList &Inputs, ActionList &CollapsedOffloadAction) { if (ActionInfo.size() < 2) return nullptr; @@ -3545,7 +3577,7 @@ class ToolSelector final { if (T->canEmitIR() && ((SaveTemps && !InputIsBitcode) || EmbedBitcode)) return nullptr; - Inputs = &CJ->getInputs(); + Inputs = CJ->getInputs(); AppendCollapsedOffloadAction(CollapsedOffloadAction, ActionInfo, /*NumElements=*/2); return T; @@ -3555,22 +3587,28 @@ class ToolSelector final { /// preprocessor action, and the current input is indeed a preprocessor /// action. If combining results in the collapse of offloading actions, those /// are appended to \a CollapsedOffloadAction. - void combineWithPreprocessor(const Tool *T, const ActionList *&Inputs, + void combineWithPreprocessor(const Tool *T, ActionList &Inputs, ActionList &CollapsedOffloadAction) { if (!T || !canCollapsePreprocessorAction() || !T->hasIntegratedCPP()) return; // Attempt to get a preprocessor action dependence. ActionList PreprocessJobOffloadActions; - auto *PJ = getPrevDependentAction(*Inputs, PreprocessJobOffloadActions); - if (!PJ || !isa(PJ)) - return; + ActionList NewInputs; + for (Action *A : Inputs) { + auto *PJ = getPrevDependentAction({A}, PreprocessJobOffloadActions); + if (!PJ || !isa(PJ)) { + NewInputs.push_back(A); + continue; + } - // This is legal to combine. Append any offload action we found and set the - // current inputs to preprocessor inputs. - CollapsedOffloadAction.append(PreprocessJobOffloadActions.begin(), - PreprocessJobOffloadActions.end()); - Inputs = &PJ->getInputs(); + // This is legal to combine. Append any offload action we found and add the + // current input to preprocessor inputs. + CollapsedOffloadAction.append(PreprocessJobOffloadActions.begin(), + PreprocessJobOffloadActions.end()); + NewInputs.append(PJ->input_begin(), PJ->input_end()); + } + Inputs = NewInputs; } public: @@ -3588,7 +3626,7 @@ public: /// connected to collapsed actions are updated accordingly. The latter enables /// the caller of the selector to process them afterwards instead of just /// dropping them. If no suitable tool is found, null will be returned. - const Tool *getTool(const ActionList *&Inputs, + const Tool *getTool(ActionList &Inputs, ActionList &CollapsedOffloadAction) { // // Get the largest chain of actions that we could combine. @@ -3624,7 +3662,7 @@ public: if (!T) T = combineBackendCompile(ActionChain, Inputs, CollapsedOffloadAction); if (!T) { - Inputs = &BaseAction->getInputs(); + Inputs = BaseAction->getInputs(); T = TC.SelectTool(*BaseAction); } @@ -3769,7 +3807,7 @@ InputInfo Driver::BuildJobsForActionNoCache( } - const ActionList *Inputs = &A->getInputs(); + ActionList Inputs = A->getInputs(); const JobAction *JA = cast(A); ActionList CollapsedOffloadActions; @@ -3795,7 +3833,7 @@ InputInfo Driver::BuildJobsForActionNoCache( // Only use pipes when there is exactly one input. InputInfoList InputInfos; - for (const Action *Input : *Inputs) { + for (const Action *Input : Inputs) { // Treat dsymutil and verify sub-jobs as being at the top-level too, they // shouldn't get temporary output names. // FIXME: Clean this up. @@ -3814,6 +3852,10 @@ InputInfo Driver::BuildJobsForActionNoCache( if (JA->getType() == types::TY_dSYM) BaseInput = InputInfos[0].getFilename(); + // ... and in header module compilations, which use the module name. + if (auto *ModuleJA = dyn_cast(JA)) + BaseInput = ModuleJA->getModuleName(); + // Append outputs of offload device jobs to the input list if (!OffloadDependencesInputInfo.empty()) InputInfos.append(OffloadDependencesInputInfo.begin(), diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index d5004df593..8933e905fc 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -302,6 +302,7 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const { case Action::CompileJobClass: case Action::PrecompileJobClass: + case Action::HeaderModulePrecompileJobClass: case Action::PreprocessJobClass: case Action::AnalyzeJobClass: case Action::MigrateJobClass: diff --git a/lib/Driver/ToolChains/Clang.cpp b/lib/Driver/ToolChains/Clang.cpp index 201fd67834..0126287114 100644 --- a/lib/Driver/ToolChains/Clang.cpp +++ b/lib/Driver/ToolChains/Clang.cpp @@ -3144,17 +3144,54 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Check number of inputs for sanity. We need at least one input. assert(Inputs.size() >= 1 && "Must have at least one input."); - const InputInfo &Input = Inputs[0]; // CUDA/HIP compilation may have multiple inputs (source file + results of // device-side compilations). OpenMP device jobs also take the host IR as a - // second input. All other jobs are expected to have exactly one - // input. + // second input. Module precompilation accepts a list of header files to + // include as part of the module. All other jobs are expected to have exactly + // one input. bool IsCuda = JA.isOffloading(Action::OFK_Cuda); bool IsHIP = JA.isOffloading(Action::OFK_HIP); bool IsOpenMPDevice = JA.isDeviceOffloading(Action::OFK_OpenMP); - assert((IsCuda || IsHIP || (IsOpenMPDevice && Inputs.size() == 2) || - Inputs.size() == 1) && - "Unable to handle multiple inputs."); + bool IsModulePrecompile = + isa(JA) && JA.getType() == types::TY_ModuleFile; + bool IsHeaderModulePrecompile = isa(JA); + + // A header module compilation doesn't have a main input file, so invent a + // fake one as a placeholder. + // FIXME: Pick the language based on the header file language. + const char *ModuleName = [&]{ + auto *ModuleNameArg = Args.getLastArg(options::OPT_fmodule_name_EQ); + return ModuleNameArg ? ModuleNameArg->getValue() : ""; + }(); + InputInfo HeaderModuleInput(types::TY_CXXModule, ModuleName, ModuleName); + + const InputInfo &Input = + IsHeaderModulePrecompile ? HeaderModuleInput : Inputs[0]; + + InputInfoList ModuleHeaderInputs; + const InputInfo *CudaDeviceInput = nullptr; + const InputInfo *OpenMPDeviceInput = nullptr; + for (const InputInfo &I : Inputs) { + if (&I == &Input) { + // This is the primary input. + } else if (IsModulePrecompile && + types::getPrecompiledType(I.getType()) == types::TY_PCH) { + types::ID Expected = + types::lookupHeaderTypeForSourceType(Inputs[0].getType()); + if (I.getType() != Expected) { + D.Diag(diag::err_drv_module_header_wrong_kind) + << I.getFilename() << types::getTypeName(I.getType()) + << types::getTypeName(Expected); + } + ModuleHeaderInputs.push_back(I); + } else if ((IsCuda || IsHIP) && !CudaDeviceInput) { + CudaDeviceInput = &I; + } else if (IsOpenMPDevice && !OpenMPDeviceInput) { + OpenMPDeviceInput = &I; + } else { + llvm_unreachable("unexpectedly given multiple inputs"); + } + } const llvm::Triple *AuxTriple = IsCuda ? getToolChain().getAuxTriple() : nullptr; @@ -3267,7 +3304,9 @@ 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) - CmdArgs.push_back("-emit-module-interface"); + CmdArgs.push_back(IsHeaderModulePrecompile + ? "-emit-header-module" + : "-emit-module-interface"); else if (UsePCH) CmdArgs.push_back("-emit-pch"); else @@ -4729,10 +4768,18 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, addDashXForInput(Args, Input, CmdArgs); - if (Input.isFilename()) - CmdArgs.push_back(Input.getFilename()); - else - Input.getInputArg().renderAsInput(Args, CmdArgs); + ArrayRef FrontendInputs = Input; + if (IsHeaderModulePrecompile) + FrontendInputs = ModuleHeaderInputs; + else if (Input.isNothing()) + FrontendInputs = {}; + + for (const InputInfo &Input : FrontendInputs) { + if (Input.isFilename()) + CmdArgs.push_back(Input.getFilename()); + else + Input.getInputArg().renderAsInput(Args, CmdArgs); + } Args.AddAllArgs(CmdArgs, options::OPT_undef); @@ -4765,10 +4812,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (IsCuda) { // Host-side cuda compilation receives all device-side outputs in a single // fatbin as Inputs[1]. Include the binary with -fcuda-include-gpubinary. - if (Inputs.size() > 1) { - assert(Inputs.size() == 2 && "More than one GPU binary!"); + if (CudaDeviceInput) { CmdArgs.push_back("-fcuda-include-gpubinary"); - CmdArgs.push_back(Inputs[1].getFilename()); + CmdArgs.push_back(CudaDeviceInput->getFilename()); } if (Args.hasFlag(options::OPT_fcuda_rdc, options::OPT_fno_cuda_rdc, false)) @@ -4785,9 +4831,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // only the relevant declarations are emitted. if (IsOpenMPDevice) { CmdArgs.push_back("-fopenmp-is-device"); - if (Inputs.size() == 2) { + if (OpenMPDeviceInput) { CmdArgs.push_back("-fopenmp-host-ir-file-path"); - CmdArgs.push_back(Args.MakeArgString(Inputs.back().getFilename())); + CmdArgs.push_back(Args.MakeArgString(OpenMPDeviceInput->getFilename())); } } diff --git a/lib/Driver/Types.cpp b/lib/Driver/Types.cpp index 45bb699cfb..9d2737bbc7 100644 --- a/lib/Driver/Types.cpp +++ b/lib/Driver/Types.cpp @@ -312,9 +312,11 @@ ID types::lookupHeaderTypeForSourceType(ID Id) { default: return Id; + // FIXME: Handle preprocessed input types. case types::TY_C: return types::TY_CHeader; case types::TY_CXX: + case types::TY_CXXModule: return types::TY_CXXHeader; case types::TY_ObjC: return types::TY_ObjCHeader; diff --git a/test/Driver/header-module.cpp b/test/Driver/header-module.cpp new file mode 100644 index 0000000000..2302c495f1 --- /dev/null +++ b/test/Driver/header-module.cpp @@ -0,0 +1,13 @@ +// Check compiling a header module to a .pcm file. +// +// RUN: %clang -fmodules-ts -fmodule-name=foobar -x c++-header --precompile %S/Inputs/header1.h %S/Inputs/header2.h %S/Inputs/header3.h -o %t.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE +// +// CHECK-PRECOMPILE: -cc1 {{.*}} -emit-header-module +// CHECK-PRECOMPILE-SAME: -fmodules-ts +// CHECK-PRECOMPILE-SAME: -fno-implicit-modules +// CHECK-PRECOMPILE-SAME: -fmodule-name=foobar +// CHECK-PRECOMPILE-SAME: -o {{.*}}.pcm +// CHECK-PRECOMPILE-SAME: -x c++ +// CHECK-PRECOMPILE-SAME: header1.h +// CHECK-PRECOMPILE-SAME: header2.h +// CHECK-PRECOMPILE-SAME: header3.h