//===- 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 #include 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 LinkerWrapperJobClass: return "clang-linker-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, StringRef _Id) : Action(InputClass, _Type), Input(_Input), Id(_Id.str()) {} 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((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 LinkerWrapperJobAction::anchor() {} LinkerWrapperJobAction::LinkerWrapperJobAction(ActionList &Inputs, types::ID Type) : JobAction(LinkerWrapperJobClass, Inputs, Type) {} void StaticLibJobAction::anchor() {} StaticLibJobAction::StaticLibJobAction(ActionList &Inputs, types::ID Type) : JobAction(StaticLibJobClass, Inputs, Type) {}