mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-05-16 02:46:31 +00:00

This patch added a distinct CUID for each input file, which is represented by InputAction. clang initially creates an InputAction for each input file for the host compilation. In CUDA/HIP action builder, each InputAction is given a CUID and cloned for each GPU arch, and the CUID is also cloned. In this way, we guarantee the corresponding device and host compilation for the same file shared the same CUID. On the other hand, different compilation units have different CUID. -fuse-cuid=random|hash|none is added to control the method to generate CUID. The default is hash. -cuid=X is also added to specify CUID explicitly, which overrides -fuse-cuid. Reviewed by: Artem Belevich Differential Revision: https://reviews.llvm.org/D95007
425 lines
14 KiB
C++
425 lines
14 KiB
C++
//===- 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, 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<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) {}
|