//===- Action.cpp - Abstract compilation steps ----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/Driver/Action.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
#include <string>
using namespace clang;
using namespace driver;
using namespace llvm::opt;
Action::~Action() = default;
const char *Action::getClassName(ActionClass AC) {
switch (AC) {
case InputClass: return "input";
case BindArchClass: return "bind-arch";
case OffloadClass:
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";
case BackendJobClass: return "backend";
case AssembleJobClass: return "assembler";
case IfsMergeJobClass: return "interface-stub-merger";
case LinkJobClass: return "linker";
case LipoJobClass: return "lipo";
case DsymutilJobClass: return "dsymutil";
case VerifyDebugInfoJobClass: return "verify-debug-info";
case VerifyPCHJobClass: return "verify-pch";
case OffloadBundlingJobClass:
return "clang-offload-bundler";
case OffloadUnbundlingJobClass:
return "clang-offload-unbundler";
case OffloadWrapperJobClass:
return "clang-offload-wrapper";
case StaticLibJobClass:
return "static-lib-linker";
}
llvm_unreachable("invalid class");
}
void Action::propagateDeviceOffloadInfo(OffloadKind OKind, const char *OArch) {
// Offload action set its own kinds on their dependences.
if (Kind == OffloadClass)
return;
// Unbundling actions use the host kinds.
if (Kind == OffloadUnbundlingJobClass)
return;
assert((OffloadingDeviceKind == OKind || OffloadingDeviceKind == OFK_None) &&
"Setting device kind to a different device??");
assert(!ActiveOffloadKindMask && "Setting a device kind in a host action??");
OffloadingDeviceKind = OKind;
OffloadingArch = OArch;
for (auto *A : Inputs)
A->propagateDeviceOffloadInfo(OffloadingDeviceKind, OArch);
}
void Action::propagateHostOffloadInfo(unsigned OKinds, const char *OArch) {
// Offload action set its own kinds on their dependences.
if (Kind == OffloadClass)
return;
assert(OffloadingDeviceKind == OFK_None &&
"Setting a host kind in a device action.");
ActiveOffloadKindMask |= OKinds;
OffloadingArch = OArch;
for (auto *A : Inputs)
A->propagateHostOffloadInfo(ActiveOffloadKindMask, OArch);
}
void Action::propagateOffloadInfo(const Action *A) {
if (unsigned HK = A->getOffloadingHostActiveKinds())
propagateHostOffloadInfo(HK, A->getOffloadingArch());
else
propagateDeviceOffloadInfo(A->getOffloadingDeviceKind(),
A->getOffloadingArch());
}
std::string Action::getOffloadingKindPrefix() const {
switch (OffloadingDeviceKind) {
case OFK_None:
break;
case OFK_Host:
llvm_unreachable("Host kind is not an offloading device kind.");
break;
case OFK_Cuda:
return "device-cuda";
case OFK_OpenMP:
return "device-openmp";
case OFK_HIP:
return "device-hip";
// TODO: Add other programming models here.
}
if (!ActiveOffloadKindMask)
return {};
std::string Res("host");
assert(!((ActiveOffloadKindMask & OFK_Cuda) &&
(ActiveOffloadKindMask & OFK_HIP)) &&
"Cannot offload CUDA and HIP at the same time");
if (ActiveOffloadKindMask & OFK_Cuda)
Res += "-cuda";
if (ActiveOffloadKindMask & OFK_HIP)
Res += "-hip";
if (ActiveOffloadKindMask & OFK_OpenMP)
Res += "-openmp";
// TODO: Add other programming models here.
return Res;
}
/// Return a string that can be used as prefix in order to generate unique files
/// for each offloading kind.
std::string
Action::GetOffloadingFileNamePrefix(OffloadKind Kind,
StringRef NormalizedTriple,
bool CreatePrefixForHost) {
// Don't generate prefix for host actions unless required.
if (!CreatePrefixForHost && (Kind == OFK_None || Kind == OFK_Host))
return {};
std::string Res("-");
Res += GetOffloadKindName(Kind);
Res += "-";
Res += NormalizedTriple;
return Res;
}
/// Return a string with the offload kind name. If that is not defined, we
/// assume 'host'.
StringRef Action::GetOffloadKindName(OffloadKind Kind) {
switch (Kind) {
case OFK_None:
case OFK_Host:
return "host";
case OFK_Cuda:
return "cuda";
case OFK_OpenMP:
return "openmp";
case OFK_HIP:
return "hip";
// TODO: Add other programming models here.
}
llvm_unreachable("invalid offload kind");
}
void InputAction::anchor() {}
InputAction::InputAction(const Arg &_Input, types::ID _Type)
: Action(InputClass, _Type), Input(_Input) {}
void BindArchAction::anchor() {}
BindArchAction::BindArchAction(Action *Input, StringRef ArchName)
: Action(BindArchClass, Input), ArchName(ArchName) {}
void OffloadAction::anchor() {}
OffloadAction::OffloadAction(const HostDependence &HDep)
: Action(OffloadClass, HDep.getAction()), HostTC(HDep.getToolChain()) {
OffloadingArch = HDep.getBoundArch();
ActiveOffloadKindMask = HDep.getOffloadKinds();
HDep.getAction()->propagateHostOffloadInfo(HDep.getOffloadKinds(),
HDep.getBoundArch());
}
OffloadAction::OffloadAction(const DeviceDependences &DDeps, types::ID Ty)
: Action(OffloadClass, DDeps.getActions(), Ty),
DevToolChains(DDeps.getToolChains()) {
auto &OKinds = DDeps.getOffloadKinds();
auto &BArchs = DDeps.getBoundArchs();
// If all inputs agree on the same kind, use it also for this action.
if (llvm::all_of(OKinds, [&](OffloadKind K) { return K == OKinds.front(); }))
OffloadingDeviceKind = OKinds.front();
// If we have a single dependency, inherit the architecture from it.
if (OKinds.size() == 1)
OffloadingArch = BArchs.front();
// Propagate info to the dependencies.
for (unsigned i = 0, e = getInputs().size(); i != e; ++i)
getInputs()[i]->propagateDeviceOffloadInfo(OKinds[i], BArchs[i]);
}
OffloadAction::OffloadAction(const HostDependence &HDep,
const DeviceDependences &DDeps)
: Action(OffloadClass, HDep.getAction()), HostTC(HDep.getToolChain()),
DevToolChains(DDeps.getToolChains()) {
// We use the kinds of the host dependence for this action.
OffloadingArch = HDep.getBoundArch();
ActiveOffloadKindMask = HDep.getOffloadKinds();
HDep.getAction()->propagateHostOffloadInfo(HDep.getOffloadKinds(),
HDep.getBoundArch());
// Add device inputs and propagate info to the device actions. Do work only if
// we have dependencies.
for (unsigned i = 0, e = DDeps.getActions().size(); i != e; ++i)
if (auto *A = DDeps.getActions()[i]) {
getInputs().push_back(A);
A->propagateDeviceOffloadInfo(DDeps.getOffloadKinds()[i],
DDeps.getBoundArchs()[i]);
}
}
void OffloadAction::doOnHostDependence(const OffloadActionWorkTy &Work) const {
if (!HostTC)
return;
assert(!getInputs().empty() && "No dependencies for offload action??");
auto *A = getInputs().front();
Work(A, HostTC, A->getOffloadingArch());
}
void OffloadAction::doOnEachDeviceDependence(
const OffloadActionWorkTy &Work) const {
auto I = getInputs().begin();
auto E = getInputs().end();
if (I == E)
return;
// We expect to have the same number of input dependences and device tool
// chains, except if we also have a host dependence. In that case we have one
// more dependence than we have device tool chains.
assert(getInputs().size() == DevToolChains.size() + (HostTC ? 1 : 0) &&
"Sizes of action dependences and toolchains are not consistent!");
// Skip host action
if (HostTC)
++I;
auto TI = DevToolChains.begin();
for (; I != E; ++I, ++TI)
Work(*I, *TI, (*I)->getOffloadingArch());
}
void OffloadAction::doOnEachDependence(const OffloadActionWorkTy &Work) const {
doOnHostDependence(Work);
doOnEachDeviceDependence(Work);
}
void OffloadAction::doOnEachDependence(bool IsHostDependence,
const OffloadActionWorkTy &Work) const {
if (IsHostDependence)
doOnHostDependence(Work);
else
doOnEachDeviceDependence(Work);
}
bool OffloadAction::hasHostDependence() const { return HostTC != nullptr; }
Action *OffloadAction::getHostDependence() const {
assert(hasHostDependence() && "Host dependence does not exist!");
assert(!getInputs().empty() && "No dependencies for offload action??");
return HostTC ? getInputs().front() : nullptr;
}
bool OffloadAction::hasSingleDeviceDependence(
bool DoNotConsiderHostActions) const {
if (DoNotConsiderHostActions)
return getInputs().size() == (HostTC ? 2 : 1);
return !HostTC && getInputs().size() == 1;
}
Action *
OffloadAction::getSingleDeviceDependence(bool DoNotConsiderHostActions) const {
assert(hasSingleDeviceDependence(DoNotConsiderHostActions) &&
"Single device dependence does not exist!");
// The previous assert ensures the number of entries in getInputs() is
// consistent with what we are doing here.
return HostTC ? getInputs()[1] : getInputs().front();
}
void OffloadAction::DeviceDependences::add(Action &A, const ToolChain &TC,
const char *BoundArch,
OffloadKind OKind) {
DeviceActions.push_back(&A);
DeviceToolChains.push_back(&TC);
DeviceBoundArchs.push_back(BoundArch);
DeviceOffloadKinds.push_back(OKind);
}
OffloadAction::HostDependence::HostDependence(Action &A, const ToolChain &TC,
const char *BoundArch,
const DeviceDependences &DDeps)
: HostAction(A), HostToolChain(TC), HostBoundArch(BoundArch) {
for (auto K : DDeps.getOffloadKinds())
HostOffloadKinds |= K;
}
void JobAction::anchor() {}
JobAction::JobAction(ActionClass Kind, Action *Input, types::ID Type)
: Action(Kind, Input, Type) {}
JobAction::JobAction(ActionClass Kind, const ActionList &Inputs, types::ID Type)
: Action(Kind, Inputs, Type) {}
void PreprocessJobAction::anchor() {}
PreprocessJobAction::PreprocessJobAction(Action *Input, types::ID OutputType)
: JobAction(PreprocessJobClass, Input, OutputType) {}
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)
: JobAction(AnalyzeJobClass, Input, OutputType) {}
void MigrateJobAction::anchor() {}
MigrateJobAction::MigrateJobAction(Action *Input, types::ID OutputType)
: JobAction(MigrateJobClass, Input, OutputType) {}
void CompileJobAction::anchor() {}
CompileJobAction::CompileJobAction(Action *Input, types::ID OutputType)
: JobAction(CompileJobClass, Input, OutputType) {}
void BackendJobAction::anchor() {}
BackendJobAction::BackendJobAction(Action *Input, types::ID OutputType)
: JobAction(BackendJobClass, Input, OutputType) {}
void AssembleJobAction::anchor() {}
AssembleJobAction::AssembleJobAction(Action *Input, types::ID OutputType)
: JobAction(AssembleJobClass, Input, OutputType) {}
void IfsMergeJobAction::anchor() {}
IfsMergeJobAction::IfsMergeJobAction(ActionList &Inputs, types::ID Type)
: JobAction(IfsMergeJobClass, Inputs, Type) {}
void LinkJobAction::anchor() {}
LinkJobAction::LinkJobAction(ActionList &Inputs, types::ID Type)
: JobAction(LinkJobClass, Inputs, Type) {}
void LipoJobAction::anchor() {}
LipoJobAction::LipoJobAction(ActionList &Inputs, types::ID Type)
: JobAction(LipoJobClass, Inputs, Type) {}
void DsymutilJobAction::anchor() {}
DsymutilJobAction::DsymutilJobAction(ActionList &Inputs, types::ID Type)
: JobAction(DsymutilJobClass, Inputs, Type) {}
void VerifyJobAction::anchor() {}
VerifyJobAction::VerifyJobAction(ActionClass Kind, Action *Input,
types::ID Type)
: JobAction(Kind, Input, Type) {
assert((Kind == VerifyDebugInfoJobClass || Kind == VerifyPCHJobClass) &&
"ActionClass is not a valid VerifyJobAction");
}
void VerifyDebugInfoJobAction::anchor() {}
VerifyDebugInfoJobAction::VerifyDebugInfoJobAction(Action *Input,
types::ID Type)
: VerifyJobAction(VerifyDebugInfoJobClass, Input, Type) {}
void VerifyPCHJobAction::anchor() {}
VerifyPCHJobAction::VerifyPCHJobAction(Action *Input, types::ID Type)
: VerifyJobAction(VerifyPCHJobClass, Input, Type) {}
void OffloadBundlingJobAction::anchor() {}
OffloadBundlingJobAction::OffloadBundlingJobAction(ActionList &Inputs)
: JobAction(OffloadBundlingJobClass, Inputs, Inputs.back()->getType()) {}
void OffloadUnbundlingJobAction::anchor() {}
OffloadUnbundlingJobAction::OffloadUnbundlingJobAction(Action *Input)
: JobAction(OffloadUnbundlingJobClass, Input, Input->getType()) {}
void OffloadWrapperJobAction::anchor() {}
OffloadWrapperJobAction::OffloadWrapperJobAction(ActionList &Inputs,
types::ID Type)
: JobAction(OffloadWrapperJobClass, Inputs, Type) {}
void StaticLibJobAction::anchor() {}
StaticLibJobAction::StaticLibJobAction(ActionList &Inputs, types::ID Type)
: JobAction(StaticLibJobClass, Inputs, Type) {}