mirror of https://github.com/microsoft/clang.git
[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
This commit is contained in:
parent
84853833c2
commit
80e197b92b
|
@ -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>'">;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue