[modules] Driver support for precompiling a collection of files as a single

action.

llvm-svn: 342305
This commit is contained in:
Richard Smith 2018-09-15 01:21:16 +00:00
parent d6509cf21d
commit cd35eff395
8 changed files with 183 additions and 37 deletions

View File

@ -296,6 +296,9 @@ def warn_drv_vectorize_needs_hvx : Warning<
"auto-vectorization requires HVX, use -mhvx to enable it">,
InGroup<OptionIgnored>;
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=<seconds since Epoch>' or '-fbuild-session-file=<file>'">;

View File

@ -59,6 +59,7 @@ public:
OffloadClass,
PreprocessJobClass,
PrecompileJobClass,
HeaderModulePrecompileJobClass,
AnalyzeJobClass,
MigrateJobClass,
CompileJobClass,
@ -398,14 +399,38 @@ 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 {
void anchor() override;

View File

@ -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<PrecompileJobAction>((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)

View File

@ -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<phases::ID, phases::MaxNumberOfPhases> 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<HeaderModulePrecompileJobAction>(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<HeaderModulePrecompileJobAction>(Input, OutputTy,
ModName);
return C.MakeAction<PrecompileJobAction>(Input, OutputTy);
}
case phases::Compile: {
@ -3448,7 +3480,7 @@ class ToolSelector final {
/// - Backend + Compile.
const Tool *
combineAssembleBackendCompile(ArrayRef<JobActionInfo> 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<JobActionInfo> 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<JobActionInfo> 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<PreprocessJobAction>(PJ))
return;
ActionList NewInputs;
for (Action *A : Inputs) {
auto *PJ = getPrevDependentAction({A}, PreprocessJobOffloadActions);
if (!PJ || !isa<PreprocessJobAction>(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<JobAction>(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<HeaderModulePrecompileJobAction>(JA))
BaseInput = ModuleJA->getModuleName();
// Append outputs of offload device jobs to the input list
if (!OffloadDependencesInputInfo.empty())
InputInfos.append(OffloadDependencesInputInfo.begin(),

View File

@ -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:

View File

@ -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<PrecompileJobAction>(JA) && JA.getType() == types::TY_ModuleFile;
bool IsHeaderModulePrecompile = isa<HeaderModulePrecompileJobAction>(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<InputInfo> 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()));
}
}

View File

@ -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;

View File

@ -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