| //===- 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"; |
| } |
| |
| 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) {} |