[Clang] Rework creating offloading toolchains (#125556)

Summary:
This patch reworks how we create offloading toolchains. Previously we
would handle this separately for all the different kinds. This patch
instead changes this to use the target triple and the offloading kind to
determine the proper toolchain. In the old case where the user only
passes `--offload-arch` we instead infer the triple from the passed
arguments. This is a pretty major overhaul but currently passes all the
clang tests with only minor changes to error messages.
This commit is contained in:
Joseph Huber 2025-07-21 18:36:39 -05:00 committed by GitHub
parent 354944d675
commit a7d93653a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 382 additions and 547 deletions

View file

@ -116,6 +116,8 @@ def err_drv_cuda_host_arch : Error<
"unsupported architecture '%0' for host compilation">;
def err_drv_mix_cuda_hip : Error<
"mixed CUDA and HIP compilation is not supported">;
def err_drv_mix_offload : Error<
"mixed %0 and %1 offloading compilation is not supported">;
def err_drv_bad_target_id : Error<
"invalid target ID '%0'; format is a processor name followed by an optional "
"colon-delimited list of features followed by an enable/disable sign (e.g., "

View file

@ -355,6 +355,9 @@ public:
phases::ID getFinalPhase(const llvm::opt::DerivedArgList &DAL,
llvm::opt::Arg **FinalPhaseArg = nullptr) const;
llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
executeProgram(llvm::ArrayRef<llvm::StringRef> Args) const;
private:
/// Certain options suppress the 'no input files' warning.
LLVM_PREFERRED_TYPE(bool)
@ -367,10 +370,6 @@ private:
/// stored in it, and will clean them up when torn down.
mutable llvm::StringMap<std::unique_ptr<ToolChain>> ToolChains;
/// The associated offloading architectures with each toolchain.
llvm::DenseMap<const ToolChain *, llvm::SmallVector<llvm::StringRef>>
OffloadArchs;
private:
/// TranslateInputArgs - Create a new derived argument list from the input
/// arguments, after applying the standard argument translations.
@ -537,8 +536,7 @@ public:
/// empty string.
llvm::SmallVector<StringRef>
getOffloadArchs(Compilation &C, const llvm::opt::DerivedArgList &Args,
Action::OffloadKind Kind, const ToolChain *TC,
bool SpecificToolchain = true) const;
Action::OffloadKind Kind, const ToolChain &TC) const;
/// Check that the file referenced by Value exists. If it doesn't,
/// issue a diagnostic and return false.

View file

@ -1156,7 +1156,7 @@ def offload_arch_EQ : CommaJoined<["--"], "offload-arch=">,
"If 'native' is used the compiler will detect locally installed architectures. "
"For HIP offloading, the device architecture can be followed by target ID features "
"delimited by a colon (e.g. gfx908:xnack+:sramecc-). May be specified more than once.">;
def no_offload_arch_EQ : Joined<["--"], "no-offload-arch=">,
def no_offload_arch_EQ : CommaJoined<["--"], "no-offload-arch=">,
Visibility<[ClangOption, FlangOption]>,
HelpText<"Remove CUDA/HIP offloading device architecture (e.g. sm_35, gfx906) from the list of devices to compile for. "
"'all' resets the list to its default value.">;
@ -1624,7 +1624,7 @@ defm auto_import : BoolFOption<"auto-import",
// In the future this option will be supported by other offloading
// languages and accept other values such as CPU/GPU architectures,
// offload kinds and target aliases.
def offload_EQ : CommaJoined<["--"], "offload=">, Flags<[NoXarchOption]>,
def offload_EQ : CommaJoined<["--"], "offload=">, Flags<[NoXarchOption]>, Alias<offload_targets_EQ>,
HelpText<"Specify comma-separated list of offloading target triples (CUDA and HIP only)">;
// C++ Coroutines

View file

@ -202,10 +202,6 @@ protected:
ToolChain(const Driver &D, const llvm::Triple &T,
const llvm::opt::ArgList &Args);
/// Executes the given \p Executable and returns the stdout.
llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
executeToolChainProgram(StringRef Executable) const;
void setTripleEnvironment(llvm::Triple::EnvironmentType Env);
virtual Tool *buildAssembler() const;

View file

@ -83,6 +83,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ExitCodes.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/Path.h"
@ -109,65 +110,6 @@ using namespace clang::driver;
using namespace clang;
using namespace llvm::opt;
static std::optional<llvm::Triple> getOffloadTargetTriple(const Driver &D,
const ArgList &Args) {
auto OffloadTargets = Args.getAllArgValues(options::OPT_offload_EQ);
// Offload compilation flow does not support multiple targets for now. We
// need the HIPActionBuilder (and possibly the CudaActionBuilder{,Base}too)
// to support multiple tool chains first.
switch (OffloadTargets.size()) {
default:
D.Diag(diag::err_drv_only_one_offload_target_supported);
return std::nullopt;
case 0:
D.Diag(diag::err_drv_invalid_or_unsupported_offload_target) << "";
return std::nullopt;
case 1:
break;
}
return llvm::Triple(OffloadTargets[0]);
}
static std::optional<llvm::Triple>
getNVIDIAOffloadTargetTriple(const Driver &D, const ArgList &Args,
const llvm::Triple &HostTriple) {
if (!Args.hasArg(options::OPT_offload_EQ)) {
return llvm::Triple(HostTriple.isArch64Bit() ? "nvptx64-nvidia-cuda"
: "nvptx-nvidia-cuda");
}
auto TT = getOffloadTargetTriple(D, Args);
if (TT && (TT->getArch() == llvm::Triple::spirv32 ||
TT->getArch() == llvm::Triple::spirv64)) {
if (Args.hasArg(options::OPT_emit_llvm))
return TT;
D.Diag(diag::err_drv_cuda_offload_only_emit_bc);
return std::nullopt;
}
D.Diag(diag::err_drv_invalid_or_unsupported_offload_target) << TT->str();
return std::nullopt;
}
static std::optional<llvm::Triple>
getHIPOffloadTargetTriple(const Driver &D, const ArgList &Args) {
if (!Args.hasArg(options::OPT_offload_EQ)) {
auto OffloadArchs = Args.getAllArgValues(options::OPT_offload_arch_EQ);
if (llvm::is_contained(OffloadArchs, "amdgcnspirv") &&
OffloadArchs.size() == 1)
return llvm::Triple("spirv64-amd-amdhsa");
return llvm::Triple("amdgcn-amd-amdhsa"); // Default HIP triple.
}
auto TT = getOffloadTargetTriple(D, Args);
if (!TT)
return std::nullopt;
if (TT->isAMDGCN() && TT->getVendor() == llvm::Triple::AMD &&
TT->getOS() == llvm::Triple::AMDHSA)
return TT;
if (TT->getArch() == llvm::Triple::spirv64)
return TT;
D.Diag(diag::err_drv_invalid_or_unsupported_offload_target) << TT->str();
return std::nullopt;
}
template <typename F> static bool usesInput(const ArgList &Args, F &&Fn) {
return llvm::any_of(Args, [&](Arg *A) {
return (A->getOption().matches(options::OPT_x) &&
@ -458,6 +400,44 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL,
return FinalPhase;
}
llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
Driver::executeProgram(llvm::ArrayRef<llvm::StringRef> Args) const {
llvm::SmallString<64> OutputFile;
llvm::sys::fs::createTemporaryFile("driver-program", "txt", OutputFile,
llvm::sys::fs::OF_Text);
llvm::FileRemover OutputRemover(OutputFile.c_str());
std::optional<llvm::StringRef> Redirects[] = {
{""},
OutputFile.str(),
{""},
};
std::string ErrorMessage;
int SecondsToWait = 60;
if (std::optional<std::string> Str =
llvm::sys::Process::GetEnv("CLANG_TOOLCHAIN_PROGRAM_TIMEOUT")) {
if (!llvm::to_integer(*Str, SecondsToWait))
return llvm::createStringError(std::error_code(),
"CLANG_TOOLCHAIN_PROGRAM_TIMEOUT expected "
"an integer, got '" +
*Str + "'");
SecondsToWait = std::max(SecondsToWait, 0); // infinite
}
StringRef Executable = Args[0];
if (llvm::sys::ExecuteAndWait(Executable, Args, {}, Redirects, SecondsToWait,
/*MemoryLimit=*/0, &ErrorMessage))
return llvm::createStringError(std::error_code(),
Executable + ": " + ErrorMessage);
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> OutputBuf =
llvm::MemoryBuffer::getFile(OutputFile.c_str());
if (!OutputBuf)
return llvm::createStringError(OutputBuf.getError(),
"Failed to read stdout of " + Executable +
": " + OutputBuf.getError().message());
return std::move(*OutputBuf);
}
static Arg *MakeInputArg(DerivedArgList &Args, const OptTable &Opts,
StringRef Value, bool Claim = true) {
Arg *A = new Arg(Opts.getOption(options::OPT_INPUT), Value,
@ -950,221 +930,264 @@ static bool addSYCLDefaultTriple(Compilation &C,
return true;
}
void Driver::CreateOffloadingDeviceToolChains(Compilation &C,
InputList &Inputs) {
// Handles `native` offload architectures by using the 'offload-arch' utility.
static llvm::SmallVector<std::string>
getSystemOffloadArchs(Compilation &C, Action::OffloadKind Kind) {
StringRef Program = C.getArgs().getLastArgValue(
options::OPT_offload_arch_tool_EQ, "offload-arch");
//
// CUDA/HIP
//
// We need to generate a CUDA/HIP toolchain if any of the inputs has a CUDA
// or HIP type. However, mixed CUDA/HIP compilation is not supported.
bool IsCuda =
llvm::any_of(Inputs, [](std::pair<types::ID, const llvm::opt::Arg *> &I) {
return types::isCuda(I.first);
});
bool IsHIP =
llvm::any_of(Inputs,
[](std::pair<types::ID, const llvm::opt::Arg *> &I) {
return types::isHIP(I.first);
}) ||
C.getInputArgs().hasArg(options::OPT_hip_link) ||
C.getInputArgs().hasArg(options::OPT_hipstdpar);
bool UseLLVMOffload = C.getInputArgs().hasArg(
options::OPT_foffload_via_llvm, options::OPT_fno_offload_via_llvm, false);
if (IsCuda && IsHIP) {
Diag(clang::diag::err_drv_mix_cuda_hip);
return;
}
if (IsCuda && !UseLLVMOffload) {
auto CudaTriple = getNVIDIAOffloadTargetTriple(
*this, C.getInputArgs(), C.getDefaultToolChain().getTriple());
if (!CudaTriple)
return;
SmallVector<std::string, 1> GPUArchs;
if (llvm::ErrorOr<std::string> Executable =
llvm::sys::findProgramByName(Program)) {
llvm::SmallVector<StringRef> Args{*Executable};
if (Kind == Action::OFK_HIP)
Args.push_back("--only=amdgpu");
else if (Kind == Action::OFK_Cuda)
Args.push_back("--only=nvptx");
auto StdoutOrErr = C.getDriver().executeProgram(Args);
auto &TC =
getOffloadToolChain(C.getInputArgs(), Action::OFK_Cuda, *CudaTriple,
C.getDefaultToolChain().getTriple());
// Emit a warning if the detected CUDA version is too new.
const CudaInstallationDetector &CudaInstallation =
static_cast<const toolchains::CudaToolChain &>(TC).CudaInstallation;
if (CudaInstallation.isValid())
CudaInstallation.WarnIfUnsupportedVersion();
C.addOffloadDeviceToolChain(&TC, Action::OFK_Cuda);
OffloadArchs[&TC] = getOffloadArchs(C, C.getArgs(), Action::OFK_Cuda, &TC,
/*SpecificToolchain=*/true);
} else if (IsHIP && !UseLLVMOffload) {
if (auto *OMPTargetArg =
C.getInputArgs().getLastArg(options::OPT_offload_targets_EQ)) {
Diag(clang::diag::err_drv_unsupported_opt_for_language_mode)
<< OMPTargetArg->getSpelling() << "HIP";
return;
if (!StdoutOrErr) {
C.getDriver().Diag(diag::err_drv_undetermined_gpu_arch)
<< Action::GetOffloadKindName(Kind) << StdoutOrErr.takeError()
<< "--offload-arch";
return GPUArchs;
} else if ((*StdoutOrErr)->getBuffer().empty()) {
C.getDriver().Diag(diag::err_drv_undetermined_gpu_arch)
<< Action::GetOffloadKindName(Kind) << "No GPU detected in the system"
<< "--offload-arch";
return GPUArchs;
}
auto HIPTriple = getHIPOffloadTargetTriple(*this, C.getInputArgs());
if (!HIPTriple)
return;
for (StringRef Arch : llvm::split((*StdoutOrErr)->getBuffer(), "\n"))
if (!Arch.empty())
GPUArchs.push_back(Arch.str());
} else {
C.getDriver().Diag(diag::err_drv_command_failure) << "offload-arch";
}
return GPUArchs;
}
auto &TC =
getOffloadToolChain(C.getInputArgs(), Action::OFK_HIP, *HIPTriple,
C.getDefaultToolChain().getTriple());
C.addOffloadDeviceToolChain(&TC, Action::OFK_HIP);
// TODO: Fix 'amdgcnspirv' handling with the new driver.
if (C.getInputArgs().hasFlag(options::OPT_offload_new_driver,
options::OPT_no_offload_new_driver, false))
OffloadArchs[&TC] = getOffloadArchs(C, C.getArgs(), Action::OFK_HIP, &TC,
/*SpecificToolchain=*/true);
// Attempts to infer the correct offloading toolchain triple by looking at the
// requested offloading kind and architectures.
static llvm::DenseSet<llvm::StringRef>
inferOffloadToolchains(Compilation &C, Action::OffloadKind Kind) {
std::set<std::string> Archs;
for (Arg *A : C.getInputArgs()) {
for (StringRef Arch : A->getValues()) {
if (A->getOption().matches(options::OPT_offload_arch_EQ)) {
if (Arch == "native") {
for (StringRef Str : getSystemOffloadArchs(C, Kind))
Archs.insert(Str.str());
} else {
Archs.insert(Arch.str());
}
} else if (A->getOption().matches(options::OPT_no_offload_arch_EQ)) {
if (Arch == "all")
Archs.clear();
else
Archs.erase(Arch.str());
}
}
}
if (IsCuda || IsHIP)
CUIDOpts = CUIDOptions(C.getArgs(), *this);
llvm::DenseSet<llvm::StringRef> Triples;
for (llvm::StringRef Arch : Archs) {
OffloadArch ID = StringToOffloadArch(Arch);
if (ID == OffloadArch::UNKNOWN)
ID = StringToOffloadArch(
getProcessorFromTargetID(llvm::Triple("amdgcn-amd-amdhsa"), Arch));
//
// OpenMP
//
// We need to generate an OpenMP toolchain if the user specified targets with
// the -fopenmp-targets option or used --offload-arch with OpenMP enabled.
if (Kind == Action::OFK_HIP && !IsAMDOffloadArch(ID)) {
C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch)
<< "HIP" << Arch;
return llvm::DenseSet<llvm::StringRef>();
}
if (Kind == Action::OFK_Cuda && !IsNVIDIAOffloadArch(ID)) {
C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch)
<< "CUDA" << Arch;
return llvm::DenseSet<llvm::StringRef>();
}
if (Kind == Action::OFK_OpenMP &&
(ID == OffloadArch::UNKNOWN || ID == OffloadArch::UNUSED)) {
C.getDriver().Diag(clang::diag::err_drv_failed_to_deduce_target_from_arch)
<< Arch;
return llvm::DenseSet<llvm::StringRef>();
}
if (ID == OffloadArch::UNKNOWN || ID == OffloadArch::UNUSED) {
C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch)
<< "offload" << Arch;
return llvm::DenseSet<llvm::StringRef>();
}
StringRef Triple;
if (ID == OffloadArch::AMDGCNSPIRV)
Triple = "spirv64-amd-amdhsa";
else if (IsNVIDIAOffloadArch(ID))
Triple = C.getDefaultToolChain().getTriple().isArch64Bit()
? "nvptx64-nvidia-cuda"
: "nvptx-nvidia-cuda";
else if (IsAMDOffloadArch(ID))
Triple = "amdgcn-amd-amdhsa";
else
continue;
// Make a new argument that dispatches this argument to the appropriate
// toolchain. This is required when we infer it and create potentially
// incompatible toolchains from the global option.
Option Opt = C.getDriver().getOpts().getOption(options::OPT_Xarch__);
unsigned Index = C.getArgs().getBaseArgs().MakeIndex("-Xarch_");
Arg *A = new Arg(Opt, C.getArgs().getArgString(Index), Index,
C.getArgs().MakeArgString(Triple.split("-").first),
C.getArgs().MakeArgString("--offload-arch=" + Arch));
C.getArgs().append(A);
Triples.insert(Triple);
}
// Infer the default target triple if no specific architectures are given.
if (Archs.empty() && Kind == Action::OFK_HIP)
Triples.insert("amdgcn-amd-amdhsa");
else if (Archs.empty() && Kind == Action::OFK_Cuda)
Triples.insert(C.getDefaultToolChain().getTriple().isArch64Bit()
? "nvptx64-nvidia-cuda"
: "nvptx-nvidia-cuda");
else if (Archs.empty() && Kind == Action::OFK_SYCL)
Triples.insert(C.getDefaultToolChain().getTriple().isArch64Bit()
? "spirv64-unknown-unknown"
: "spirv32-unknown-unknown");
// We need to dispatch these to the appropriate toolchain now.
C.getArgs().eraseArg(options::OPT_offload_arch_EQ);
C.getArgs().eraseArg(options::OPT_no_offload_arch_EQ);
return Triples;
}
void Driver::CreateOffloadingDeviceToolChains(Compilation &C,
InputList &Inputs) {
bool UseLLVMOffload = C.getInputArgs().hasArg(
options::OPT_foffload_via_llvm, options::OPT_fno_offload_via_llvm, false);
bool IsCuda =
llvm::any_of(Inputs,
[](std::pair<types::ID, const llvm::opt::Arg *> &I) {
return types::isCuda(I.first);
}) &&
!UseLLVMOffload;
bool IsHIP =
(llvm::any_of(Inputs,
[](std::pair<types::ID, const llvm::opt::Arg *> &I) {
return types::isHIP(I.first);
}) ||
C.getInputArgs().hasArg(options::OPT_hip_link) ||
C.getInputArgs().hasArg(options::OPT_hipstdpar)) &&
!UseLLVMOffload;
bool IsSYCL = C.getInputArgs().hasFlag(options::OPT_fsycl,
options::OPT_fno_sycl, false);
bool IsOpenMPOffloading =
((IsCuda || IsHIP) && UseLLVMOffload) ||
UseLLVMOffload ||
(C.getInputArgs().hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ,
options::OPT_fno_openmp, false) &&
(C.getInputArgs().hasArg(options::OPT_offload_targets_EQ) ||
C.getInputArgs().hasArg(options::OPT_offload_arch_EQ)));
if (IsOpenMPOffloading) {
// We expect that -fopenmp-targets is always used in conjunction with the
// option -fopenmp specifying a valid runtime with offloading support, i.e.
// libomp or libiomp.
OpenMPRuntimeKind RuntimeKind = getOpenMPRuntime(C.getInputArgs());
if (RuntimeKind != OMPRT_OMP && RuntimeKind != OMPRT_IOMP5) {
Diag(clang::diag::err_drv_expecting_fopenmp_with_fopenmp_targets);
return;
}
(C.getInputArgs().hasArg(options::OPT_offload_arch_EQ)) &&
!(IsCuda || IsHIP)));
// If the user specified -fopenmp-targets= we create a toolchain for each
// valid triple. Otherwise, if only --offload-arch= was specified we instead
// attempt to derive the appropriate toolchains from the arguments.
if (Arg *OpenMPTargets =
C.getInputArgs().getLastArg(options::OPT_offload_targets_EQ)) {
if (OpenMPTargets && !OpenMPTargets->getNumValues()) {
Diag(clang::diag::warn_drv_empty_joined_argument)
<< OpenMPTargets->getAsString(C.getInputArgs());
return;
}
llvm::DenseSet<Action::OffloadKind> Kinds;
const std::pair<bool, Action::OffloadKind> ActiveKinds[] = {
{IsCuda, Action::OFK_Cuda},
{IsHIP, Action::OFK_HIP},
{IsOpenMPOffloading, Action::OFK_OpenMP},
{IsSYCL, Action::OFK_SYCL}};
for (const auto &[Active, Kind] : ActiveKinds)
if (Active)
Kinds.insert(Kind);
// Make sure these show up in a deterministic order.
std::multiset<StringRef> OpenMPTriples;
for (StringRef T : OpenMPTargets->getValues())
OpenMPTriples.insert(T);
llvm::StringMap<StringRef> FoundNormalizedTriples;
for (StringRef T : OpenMPTriples) {
llvm::Triple TT(ToolChain::getOpenMPTriple(T));
std::string NormalizedName = TT.normalize();
// Make sure we don't have a duplicate triple.
auto [TripleIt, Inserted] =
FoundNormalizedTriples.try_emplace(NormalizedName, T);
if (!Inserted) {
Diag(clang::diag::warn_drv_omp_offload_target_duplicate)
<< T << TripleIt->second;
continue;
}
// If the specified target is invalid, emit a diagnostic.
if (TT.getArch() == llvm::Triple::UnknownArch) {
Diag(clang::diag::err_drv_invalid_omp_target) << T;
continue;
}
auto &TC = getOffloadToolChain(C.getInputArgs(), Action::OFK_OpenMP, TT,
C.getDefaultToolChain().getTriple());
C.addOffloadDeviceToolChain(&TC, Action::OFK_OpenMP);
OffloadArchs[&TC] =
getOffloadArchs(C, C.getArgs(), Action::OFK_OpenMP, &TC,
/*SpecificToolchain=*/true);
}
} else if (C.getInputArgs().hasArg(options::OPT_offload_arch_EQ) &&
((!IsHIP && !IsCuda) || UseLLVMOffload)) {
llvm::Triple AMDTriple("amdgcn-amd-amdhsa");
llvm::Triple NVPTXTriple("nvptx64-nvidia-cuda");
for (StringRef Arch :
C.getInputArgs().getAllArgValues(options::OPT_offload_arch_EQ)) {
bool IsNVPTX = IsNVIDIAOffloadArch(
StringToOffloadArch(getProcessorFromTargetID(NVPTXTriple, Arch)));
bool IsAMDGPU = IsAMDOffloadArch(
StringToOffloadArch(getProcessorFromTargetID(AMDTriple, Arch)));
if (!IsNVPTX && !IsAMDGPU && !Arch.empty() &&
!Arch.equals_insensitive("native")) {
Diag(clang::diag::err_drv_failed_to_deduce_target_from_arch) << Arch;
return;
}
}
// Attempt to deduce the offloading triple from the set of architectures.
// We can only correctly deduce NVPTX / AMDGPU triples currently.
for (const llvm::Triple &TT : {AMDTriple, NVPTXTriple}) {
auto &TC = getOffloadToolChain(C.getInputArgs(), Action::OFK_OpenMP, TT,
C.getDefaultToolChain().getTriple());
llvm::SmallVector<StringRef> Archs =
getOffloadArchs(C, C.getArgs(), Action::OFK_OpenMP, &TC,
/*SpecificToolchain=*/false);
if (!Archs.empty()) {
C.addOffloadDeviceToolChain(&TC, Action::OFK_OpenMP);
OffloadArchs[&TC] = Archs;
}
}
// If the set is empty then we failed to find a native architecture.
auto TCRange = C.getOffloadToolChains(Action::OFK_OpenMP);
if (TCRange.first == TCRange.second)
Diag(clang::diag::err_drv_failed_to_deduce_target_from_arch)
<< "native";
}
} else if (C.getInputArgs().hasArg(options::OPT_offload_targets_EQ)) {
Diag(clang::diag::err_drv_expecting_fopenmp_with_fopenmp_targets);
// We currently don't support any kind of mixed offloading.
if (Kinds.size() > 1) {
Diag(clang::diag::err_drv_mix_offload)
<< Action::GetOffloadKindName(*Kinds.begin()).upper()
<< Action::GetOffloadKindName(*(++Kinds.begin())).upper();
return;
}
// We need to generate a SYCL toolchain if the user specified -fsycl.
bool IsSYCL = C.getInputArgs().hasFlag(options::OPT_fsycl,
options::OPT_fno_sycl, false);
// Initialize the compilation identifier used for unique CUDA / HIP names.
if (IsCuda || IsHIP)
CUIDOpts = CUIDOptions(C.getArgs(), *this);
auto argSYCLIncompatible = [&](OptSpecifier OptId) {
if (!IsSYCL)
return;
if (Arg *IncompatArg = C.getInputArgs().getLastArg(OptId))
Diag(clang::diag::err_drv_argument_not_allowed_with)
<< IncompatArg->getSpelling() << "-fsycl";
};
// -static-libstdc++ is not compatible with -fsycl.
argSYCLIncompatible(options::OPT_static_libstdcxx);
// -ffreestanding cannot be used with -fsycl
argSYCLIncompatible(options::OPT_ffreestanding);
// Get the list of requested offloading toolchains. If they were not
// explicitly specified we will infer them based on the offloading language
// and requested architectures.
std::multiset<llvm::StringRef> Triples;
if (C.getInputArgs().hasArg(options::OPT_offload_targets_EQ)) {
std::vector<std::string> ArgValues =
C.getInputArgs().getAllArgValues(options::OPT_offload_targets_EQ);
for (llvm::StringRef Target : ArgValues)
Triples.insert(C.getInputArgs().MakeArgString(Target));
llvm::SmallVector<llvm::Triple, 4> UniqueSYCLTriplesVec;
if (IsSYCL) {
addSYCLDefaultTriple(C, UniqueSYCLTriplesVec);
// We'll need to use the SYCL and host triples as the key into
// getOffloadingDeviceToolChain, because the device toolchains we're
// going to create will depend on both.
const ToolChain *HostTC = C.getSingleOffloadToolChain<Action::OFK_Host>();
for (const auto &TT : UniqueSYCLTriplesVec) {
auto &TC = getOffloadToolChain(C.getInputArgs(), Action::OFK_SYCL, TT,
HostTC->getTriple());
C.addOffloadDeviceToolChain(&TC, Action::OFK_SYCL);
OffloadArchs[&TC] = getOffloadArchs(C, C.getArgs(), Action::OFK_SYCL, &TC,
/*SpecificToolchain=*/true);
if (ArgValues.empty())
Diag(clang::diag::warn_drv_empty_joined_argument)
<< C.getInputArgs()
.getLastArg(options::OPT_offload_targets_EQ)
->getAsString(C.getInputArgs());
} else if (Kinds.size() > 0) {
for (Action::OffloadKind Kind : Kinds) {
llvm::DenseSet<llvm::StringRef> Derived = inferOffloadToolchains(C, Kind);
Triples.insert(Derived.begin(), Derived.end());
}
}
//
// TODO: Add support for other offloading programming models here.
//
// Build an offloading toolchain for every requested target and kind.
llvm::StringMap<StringRef> FoundNormalizedTriples;
for (StringRef Target : Triples) {
// OpenMP offloading requires a compatible libomp.
if (Kinds.contains(Action::OFK_OpenMP)) {
OpenMPRuntimeKind RuntimeKind = getOpenMPRuntime(C.getInputArgs());
if (RuntimeKind != OMPRT_OMP && RuntimeKind != OMPRT_IOMP5) {
Diag(clang::diag::err_drv_expecting_fopenmp_with_fopenmp_targets);
return;
}
}
// Certain options are not allowed when combined with SYCL compilation.
if (Kinds.contains(Action::OFK_SYCL)) {
for (auto ID :
{options::OPT_static_libstdcxx, options::OPT_ffreestanding})
if (Arg *IncompatArg = C.getInputArgs().getLastArg(ID))
Diag(clang::diag::err_drv_argument_not_allowed_with)
<< IncompatArg->getSpelling() << "-fsycl";
}
// Create a device toolchain for every specified kind and triple.
for (Action::OffloadKind Kind : Kinds) {
llvm::Triple TT = Kind == Action::OFK_OpenMP
? ToolChain::getOpenMPTriple(Target)
: llvm::Triple(Target);
if (TT.getArch() == llvm::Triple::ArchType::UnknownArch) {
Diag(diag::err_drv_invalid_or_unsupported_offload_target) << TT.str();
continue;
}
std::string NormalizedName = TT.normalize();
auto [TripleIt, Inserted] =
FoundNormalizedTriples.try_emplace(NormalizedName, Target);
if (!Inserted) {
Diag(clang::diag::warn_drv_omp_offload_target_duplicate)
<< Target << TripleIt->second;
continue;
}
auto &TC = getOffloadToolChain(C.getInputArgs(), Kind, TT,
C.getDefaultToolChain().getTriple());
// Emit a warning if the detected CUDA version is too new.
if (Kind == Action::OFK_Cuda) {
auto &CudaInstallation =
static_cast<const toolchains::CudaToolChain &>(TC).CudaInstallation;
if (CudaInstallation.isValid())
CudaInstallation.WarnIfUnsupportedVersion();
}
C.addOffloadDeviceToolChain(&TC, Kind);
}
}
}
bool Driver::loadZOSCustomizationFile(llvm::cl::ExpansionContext &ExpCtx) {
@ -3306,9 +3329,6 @@ class OffloadingActionBuilder final {
// architecture. If we are in host-only mode we return 'success' so that
// the host uses the CUDA offload kind.
if (auto *IA = dyn_cast<InputAction>(HostAction)) {
assert(!GpuArchList.empty() &&
"We should have at least one GPU architecture.");
// If the host input is not CUDA or HIP, we don't need to bother about
// this input.
if (!(IA->getType() == types::TY_CUDA ||
@ -3408,10 +3428,6 @@ class OffloadingActionBuilder final {
CudaDeviceActions.clear();
}
/// Get canonicalized offload arch option. \returns empty StringRef if the
/// option is invalid.
virtual StringRef getCanonicalOffloadArch(StringRef Arch) = 0;
virtual std::optional<std::pair<llvm::StringRef, llvm::StringRef>>
getConflictOffloadArchCombination(const std::set<StringRef> &GpuArchs) = 0;
@ -3440,91 +3456,25 @@ class OffloadingActionBuilder final {
return true;
}
ToolChains.push_back(
AssociatedOffloadKind == Action::OFK_Cuda
? C.getSingleOffloadToolChain<Action::OFK_Cuda>()
: C.getSingleOffloadToolChain<Action::OFK_HIP>());
std::set<StringRef> GpuArchs;
for (Action::OffloadKind Kind : {Action::OFK_Cuda, Action::OFK_HIP}) {
for (auto &I : llvm::make_range(C.getOffloadToolChains(Kind))) {
ToolChains.push_back(I.second);
for (auto Arch :
C.getDriver().getOffloadArchs(C, C.getArgs(), Kind, *I.second))
GpuArchs.insert(Arch);
}
}
for (auto Arch : GpuArchs)
GpuArchList.push_back(Arch.data());
CompileHostOnly = C.getDriver().offloadHostOnly();
EmitLLVM = Args.getLastArg(options::OPT_emit_llvm);
EmitAsm = Args.getLastArg(options::OPT_S);
// --offload and --offload-arch options are mutually exclusive.
if (Args.hasArgNoClaim(options::OPT_offload_EQ) &&
Args.hasArgNoClaim(options::OPT_offload_arch_EQ,
options::OPT_no_offload_arch_EQ)) {
C.getDriver().Diag(diag::err_opt_not_valid_with_opt) << "--offload-arch"
<< "--offload";
}
// Collect all offload arch parameters, removing duplicates.
std::set<StringRef> GpuArchs;
bool Error = false;
const ToolChain &TC = *ToolChains.front();
for (Arg *A : C.getArgsForToolChain(&TC, /*BoundArch=*/"",
AssociatedOffloadKind)) {
if (!(A->getOption().matches(options::OPT_offload_arch_EQ) ||
A->getOption().matches(options::OPT_no_offload_arch_EQ)))
continue;
A->claim();
for (StringRef ArchStr : llvm::split(A->getValue(), ",")) {
if (A->getOption().matches(options::OPT_no_offload_arch_EQ) &&
ArchStr == "all") {
GpuArchs.clear();
} else if (ArchStr == "native") {
auto GPUsOrErr = ToolChains.front()->getSystemGPUArchs(Args);
if (!GPUsOrErr) {
TC.getDriver().Diag(diag::err_drv_undetermined_gpu_arch)
<< llvm::Triple::getArchTypeName(TC.getArch())
<< llvm::toString(GPUsOrErr.takeError()) << "--offload-arch";
continue;
}
for (auto GPU : *GPUsOrErr) {
GpuArchs.insert(Args.MakeArgString(GPU));
}
} else {
ArchStr = getCanonicalOffloadArch(ArchStr);
if (ArchStr.empty()) {
Error = true;
} else if (A->getOption().matches(options::OPT_offload_arch_EQ))
GpuArchs.insert(ArchStr);
else if (A->getOption().matches(options::OPT_no_offload_arch_EQ))
GpuArchs.erase(ArchStr);
else
llvm_unreachable("Unexpected option.");
}
}
}
auto &&ConflictingArchs = getConflictOffloadArchCombination(GpuArchs);
if (ConflictingArchs) {
C.getDriver().Diag(clang::diag::err_drv_bad_offload_arch_combo)
<< ConflictingArchs->first << ConflictingArchs->second;
C.setContainsError();
return true;
}
// Collect list of GPUs remaining in the set.
for (auto Arch : GpuArchs)
GpuArchList.push_back(Arch.data());
// Default to sm_20 which is the lowest common denominator for
// supported GPUs. sm_20 code should work correctly, if
// suboptimally, on all newer GPUs.
if (GpuArchList.empty()) {
if (ToolChains.front()->getTriple().isSPIRV()) {
if (ToolChains.front()->getTriple().getVendor() == llvm::Triple::AMD)
GpuArchList.push_back(OffloadArch::AMDGCNSPIRV);
else
GpuArchList.push_back(OffloadArch::Generic);
} else {
GpuArchList.push_back(DefaultOffloadArch);
}
}
return Error;
return false;
}
};
@ -3538,15 +3488,6 @@ class OffloadingActionBuilder final {
DefaultOffloadArch = OffloadArch::CudaDefault;
}
StringRef getCanonicalOffloadArch(StringRef ArchStr) override {
OffloadArch Arch = StringToOffloadArch(ArchStr);
if (Arch == OffloadArch::UNKNOWN || !IsNVIDIAOffloadArch(Arch)) {
C.getDriver().Diag(clang::diag::err_drv_cuda_bad_gpu_arch) << ArchStr;
return StringRef();
}
return OffloadArchToString(Arch);
}
std::optional<std::pair<llvm::StringRef, llvm::StringRef>>
getConflictOffloadArchCombination(
const std::set<StringRef> &GpuArchs) override {
@ -3705,24 +3646,6 @@ class OffloadingActionBuilder final {
bool canUseBundlerUnbundler() const override { return true; }
StringRef getCanonicalOffloadArch(StringRef IdStr) override {
llvm::StringMap<bool> Features;
// getHIPOffloadTargetTriple() is known to return valid value as it has
// been called successfully in the CreateOffloadingDeviceToolChains().
auto T =
(IdStr == "amdgcnspirv")
? llvm::Triple("spirv64-amd-amdhsa")
: *getHIPOffloadTargetTriple(C.getDriver(), C.getInputArgs());
auto ArchStr = parseTargetID(T, IdStr, &Features);
if (!ArchStr) {
C.getDriver().Diag(clang::diag::err_drv_bad_target_id) << IdStr;
C.setContainsError();
return StringRef();
}
auto CanId = getCanonicalTargetID(*ArchStr, Features);
return Args.MakeArgStringRef(CanId);
};
std::optional<std::pair<llvm::StringRef, llvm::StringRef>>
getConflictOffloadArchCombination(
const std::set<StringRef> &GpuArchs) override {
@ -4715,23 +4638,20 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
static StringRef getCanonicalArchString(Compilation &C,
const llvm::opt::DerivedArgList &Args,
StringRef ArchStr,
const llvm::Triple &Triple,
bool SpecificToolchain) {
const llvm::Triple &Triple) {
// Lookup the CUDA / HIP architecture string. Only report an error if we were
// expecting the triple to be only NVPTX / AMDGPU.
OffloadArch Arch =
StringToOffloadArch(getProcessorFromTargetID(Triple, ArchStr));
if (Triple.isNVPTX() &&
(Arch == OffloadArch::UNKNOWN || !IsNVIDIAOffloadArch(Arch))) {
if (SpecificToolchain)
C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch)
<< "CUDA" << ArchStr;
C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch)
<< "CUDA" << ArchStr;
return StringRef();
} else if (Triple.isAMDGPU() &&
(Arch == OffloadArch::UNKNOWN || !IsAMDOffloadArch(Arch))) {
if (SpecificToolchain)
C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch)
<< "HIP" << ArchStr;
C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch)
<< "HIP" << ArchStr;
return StringRef();
}
@ -4767,11 +4687,7 @@ getConflictOffloadArchCombination(const llvm::DenseSet<StringRef> &Archs,
llvm::SmallVector<StringRef>
Driver::getOffloadArchs(Compilation &C, const llvm::opt::DerivedArgList &Args,
Action::OffloadKind Kind, const ToolChain *TC,
bool SpecificToolchain) const {
if (!TC)
TC = &C.getDefaultToolChain();
Action::OffloadKind Kind, const ToolChain &TC) const {
// --offload and --offload-arch options are mutually exclusive.
if (Args.hasArgNoClaim(options::OPT_offload_EQ) &&
Args.hasArgNoClaim(options::OPT_offload_arch_EQ,
@ -4784,48 +4700,44 @@ Driver::getOffloadArchs(Compilation &C, const llvm::opt::DerivedArgList &Args,
}
llvm::DenseSet<StringRef> Archs;
for (auto *Arg : C.getArgsForToolChain(TC, /*BoundArch=*/"", Kind)) {
for (auto *Arg : C.getArgsForToolChain(&TC, /*BoundArch=*/"", Kind)) {
// Add or remove the seen architectures in order of appearance. If an
// invalid architecture is given we simply exit.
if (Arg->getOption().matches(options::OPT_offload_arch_EQ)) {
for (StringRef Arch : Arg->getValues()) {
if (Arch == "native" || Arch.empty()) {
auto GPUsOrErr = TC->getSystemGPUArchs(Args);
auto GPUsOrErr = TC.getSystemGPUArchs(Args);
if (!GPUsOrErr) {
if (!SpecificToolchain)
llvm::consumeError(GPUsOrErr.takeError());
else
TC->getDriver().Diag(diag::err_drv_undetermined_gpu_arch)
<< llvm::Triple::getArchTypeName(TC->getArch())
<< llvm::toString(GPUsOrErr.takeError()) << "--offload-arch";
TC.getDriver().Diag(diag::err_drv_undetermined_gpu_arch)
<< llvm::Triple::getArchTypeName(TC.getArch())
<< llvm::toString(GPUsOrErr.takeError()) << "--offload-arch";
continue;
}
for (auto ArchStr : *GPUsOrErr) {
StringRef CanonicalStr =
getCanonicalArchString(C, Args, Args.MakeArgString(ArchStr),
TC->getTriple(), SpecificToolchain);
StringRef CanonicalStr = getCanonicalArchString(
C, Args, Args.MakeArgString(ArchStr), TC.getTriple());
if (!CanonicalStr.empty())
Archs.insert(CanonicalStr);
else if (SpecificToolchain)
else
return llvm::SmallVector<StringRef>();
}
} else {
StringRef CanonicalStr = getCanonicalArchString(
C, Args, Arch, TC->getTriple(), SpecificToolchain);
StringRef CanonicalStr =
getCanonicalArchString(C, Args, Arch, TC.getTriple());
if (!CanonicalStr.empty())
Archs.insert(CanonicalStr);
else if (SpecificToolchain)
else
return llvm::SmallVector<StringRef>();
}
}
} else if (Arg->getOption().matches(options::OPT_no_offload_arch_EQ)) {
for (StringRef Arch : llvm::split(Arg->getValue(), ",")) {
for (StringRef Arch : Arg->getValues()) {
if (Arch == "all") {
Archs.clear();
} else {
StringRef ArchStr = getCanonicalArchString(
C, Args, Arch, TC->getTriple(), SpecificToolchain);
StringRef ArchStr =
getCanonicalArchString(C, Args, Arch, TC.getTriple());
Archs.erase(ArchStr);
}
}
@ -4833,28 +4745,30 @@ Driver::getOffloadArchs(Compilation &C, const llvm::opt::DerivedArgList &Args,
}
if (auto ConflictingArchs =
getConflictOffloadArchCombination(Archs, TC->getTriple()))
getConflictOffloadArchCombination(Archs, TC.getTriple()))
C.getDriver().Diag(clang::diag::err_drv_bad_offload_arch_combo)
<< ConflictingArchs->first << ConflictingArchs->second;
// Skip filling defaults if we're just querying what is availible.
if (SpecificToolchain && Archs.empty()) {
// Fill in the default architectures if not provided explicitly.
if (Archs.empty()) {
if (Kind == Action::OFK_Cuda) {
Archs.insert(OffloadArchToString(OffloadArch::CudaDefault));
} else if (Kind == Action::OFK_HIP) {
Archs.insert(OffloadArchToString(OffloadArch::HIPDefault));
Archs.insert(OffloadArchToString(TC.getTriple().isSPIRV()
? OffloadArch::Generic
: OffloadArch::HIPDefault));
} else if (Kind == Action::OFK_SYCL) {
Archs.insert(StringRef());
} else if (Kind == Action::OFK_OpenMP) {
// Accept legacy `-march` device arguments for OpenMP.
if (auto *Arg = C.getArgsForToolChain(TC, /*BoundArch=*/"", Kind)
if (auto *Arg = C.getArgsForToolChain(&TC, /*BoundArch=*/"", Kind)
.getLastArg(options::OPT_march_EQ)) {
Archs.insert(Arg->getValue());
} else {
auto ArchsOrErr = TC->getSystemGPUArchs(Args);
auto ArchsOrErr = TC.getSystemGPUArchs(Args);
if (!ArchsOrErr) {
TC->getDriver().Diag(diag::err_drv_undetermined_gpu_arch)
<< llvm::Triple::getArchTypeName(TC->getArch())
TC.getDriver().Diag(diag::err_drv_undetermined_gpu_arch)
<< llvm::Triple::getArchTypeName(TC.getArch())
<< llvm::toString(ArchsOrErr.takeError()) << "--offload-arch";
} else if (!ArchsOrErr->empty()) {
for (auto Arch : *ArchsOrErr)
@ -4934,7 +4848,7 @@ Action *Driver::BuildOffloadingActions(Compilation &C,
// Get the product of all bound architectures and toolchains.
SmallVector<std::pair<const ToolChain *, StringRef>> TCAndArchs;
for (const ToolChain *TC : ToolChains) {
for (StringRef Arch : OffloadArchs.lookup(TC)) {
for (StringRef Arch : getOffloadArchs(C, C.getArgs(), Kind, *TC)) {
TCAndArchs.push_back(std::make_pair(TC, Arch));
DeviceActions.push_back(
C.MakeAction<InputAction>(*InputArg, InputType, CUID));

View file

@ -104,44 +104,6 @@ ToolChain::ToolChain(const Driver &D, const llvm::Triple &T,
addIfExists(getFilePaths(), Path);
}
llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
ToolChain::executeToolChainProgram(StringRef Executable) const {
llvm::SmallString<64> OutputFile;
llvm::sys::fs::createTemporaryFile("toolchain-program", "txt", OutputFile,
llvm::sys::fs::OF_Text);
llvm::FileRemover OutputRemover(OutputFile.c_str());
std::optional<llvm::StringRef> Redirects[] = {
{""},
OutputFile.str(),
{""},
};
std::string ErrorMessage;
int SecondsToWait = 60;
if (std::optional<std::string> Str =
llvm::sys::Process::GetEnv("CLANG_TOOLCHAIN_PROGRAM_TIMEOUT")) {
if (!llvm::to_integer(*Str, SecondsToWait))
return llvm::createStringError(std::error_code(),
"CLANG_TOOLCHAIN_PROGRAM_TIMEOUT expected "
"an integer, got '" +
*Str + "'");
SecondsToWait = std::max(SecondsToWait, 0); // infinite
}
if (llvm::sys::ExecuteAndWait(Executable, {Executable}, {}, Redirects,
SecondsToWait,
/*MemoryLimit=*/0, &ErrorMessage))
return llvm::createStringError(std::error_code(),
Executable + ": " + ErrorMessage);
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> OutputBuf =
llvm::MemoryBuffer::getFile(OutputFile.c_str());
if (!OutputBuf)
return llvm::createStringError(OutputBuf.getError(),
"Failed to read stdout of " + Executable +
": " + OutputBuf.getError().message());
return std::move(*OutputBuf);
}
void ToolChain::setTripleEnvironment(llvm::Triple::EnvironmentType Env) {
Triple.setEnvironment(Env);
if (EffectiveTriple != llvm::Triple())

View file

@ -841,7 +841,7 @@ AMDGPUToolChain::getSystemGPUArchs(const ArgList &Args) const {
else
Program = GetProgramPath("amdgpu-arch");
auto StdoutOrErr = executeToolChainProgram(Program);
auto StdoutOrErr = getDriver().executeProgram({Program});
if (!StdoutOrErr)
return StdoutOrErr.takeError();

View file

@ -97,32 +97,15 @@ forAllAssociatedToolChains(Compilation &C, const JobAction &JA,
// Apply Work on all the offloading tool chains associated with the current
// action.
if (JA.isHostOffloading(Action::OFK_Cuda))
Work(*C.getSingleOffloadToolChain<Action::OFK_Cuda>());
else if (JA.isDeviceOffloading(Action::OFK_Cuda))
Work(*C.getSingleOffloadToolChain<Action::OFK_Host>());
else if (JA.isHostOffloading(Action::OFK_HIP))
Work(*C.getSingleOffloadToolChain<Action::OFK_HIP>());
else if (JA.isDeviceOffloading(Action::OFK_HIP))
Work(*C.getSingleOffloadToolChain<Action::OFK_Host>());
if (JA.isHostOffloading(Action::OFK_OpenMP)) {
auto TCs = C.getOffloadToolChains<Action::OFK_OpenMP>();
for (auto II = TCs.first, IE = TCs.second; II != IE; ++II)
Work(*II->second);
} else if (JA.isDeviceOffloading(Action::OFK_OpenMP))
Work(*C.getSingleOffloadToolChain<Action::OFK_Host>());
if (JA.isHostOffloading(Action::OFK_SYCL)) {
auto TCs = C.getOffloadToolChains<Action::OFK_SYCL>();
for (auto II = TCs.first, IE = TCs.second; II != IE; ++II)
Work(*II->second);
} else if (JA.isDeviceOffloading(Action::OFK_SYCL))
Work(*C.getSingleOffloadToolChain<Action::OFK_Host>());
//
// TODO: Add support for other offloading programming models here.
//
for (Action::OffloadKind Kind : {Action::OFK_Cuda, Action::OFK_OpenMP,
Action::OFK_HIP, Action::OFK_SYCL}) {
if (JA.isHostOffloading(Kind)) {
auto TCs = C.getOffloadToolChains(Kind);
for (auto II = TCs.first, IE = TCs.second; II != IE; ++II)
Work(*II->second);
} else if (JA.isDeviceOffloading(Kind))
Work(*C.getSingleOffloadToolChain<Action::OFK_Host>());
}
}
static bool
@ -4985,8 +4968,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
else {
// Host-side compilation.
NormalizedTriple =
(IsCuda ? C.getSingleOffloadToolChain<Action::OFK_Cuda>()
: C.getSingleOffloadToolChain<Action::OFK_HIP>())
(IsCuda ? C.getOffloadToolChains(Action::OFK_Cuda).first->second
: C.getOffloadToolChains(Action::OFK_HIP).first->second)
->getTriple()
.normalize();
if (IsCuda) {

View file

@ -815,7 +815,7 @@ NVPTXToolChain::getSystemGPUArchs(const ArgList &Args) const {
else
Program = GetProgramPath("nvptx-arch");
auto StdoutOrErr = executeToolChainProgram(Program);
auto StdoutOrErr = getDriver().executeProgram({Program});
if (!StdoutOrErr)
return StdoutOrErr.takeError();

View file

@ -14,14 +14,14 @@
// RUN: | FileCheck %s --check-prefix=NO-OUTPUT-ERROR
// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -nogpulib --offload-new-driver --offload-arch=native --amdgpu-arch-tool=%t/amdgpu_arch_fail -x hip %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=NO-OUTPUT-ERROR
// NO-OUTPUT-ERROR: error: cannot determine amdgcn architecture{{.*}}; consider passing it via '--offload-arch'
// NO-OUTPUT-ERROR: error: cannot determine hip architecture{{.*}}; consider passing it via '--offload-arch'
// case when amdgpu-arch does not return anything with successful execution
// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -nogpulib --offload-arch=native --amdgpu-arch-tool=%t/amdgpu_arch_empty -x hip %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=EMPTY-OUTPUT
// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -nogpulib --offload-new-driver --offload-arch=native --amdgpu-arch-tool=%t/amdgpu_arch_empty -x hip %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=EMPTY-OUTPUT
// EMPTY-OUTPUT: error: cannot determine amdgcn architecture: No AMD GPU detected in the system; consider passing it via '--offload-arch'
// EMPTY-OUTPUT: error: cannot determine hip architecture: No GPU detected in the system; consider passing it via '--offload-arch'
// case when amdgpu-arch returns a gfx906 GPU.
// RUN: %clang -### --target=x86_64-unknown-linux-gnu -nogpuinc -nogpulib --offload-arch=native --amdgpu-arch-tool=%t/amdgpu_arch_gfx906 -x hip %s 2>&1 \
@ -36,4 +36,4 @@
// RUN: --offload-arch=native --amdgpu-arch-tool=%t/amdgpu_arch_gfx906 \
// RUN: -x hip %s 2>&1 | \
// RUN: FileCheck %s --check-prefix=BAD-TIMEOUT
// BAD-TIMEOUT: clang: error: cannot determine amdgcn architecture: CLANG_TOOLCHAIN_PROGRAM_TIMEOUT expected an integer, got 'foo'; consider passing it via '--offload-arch'; environment variable CLANG_TOOLCHAIN_PROGRAM_TIMEOUT specifies the tool timeout (integer secs, <=0 is infinite)
// BAD-TIMEOUT: clang: error: cannot determine hip architecture: CLANG_TOOLCHAIN_PROGRAM_TIMEOUT expected an integer, got 'foo'; consider passing it via '--offload-arch'; environment variable CLANG_TOOLCHAIN_PROGRAM_TIMEOUT specifies the tool timeout (integer secs, <=0 is infinite)

View file

@ -324,8 +324,8 @@
// RUN: -ccc-print-phases --offload-arch=sm_999 -fgpu-rdc -c %s 2>&1 \
// RUN: | FileCheck -check-prefix=INVALID-ARCH %s
// INVALID-ARCH: error: unsupported CUDA gpu architecture: sm_999
// INVALID-ARCH-NEXT: 0: input, "[[INPUT:.+]]", cuda, (host-cuda)
// INVALID-ARCH-NEXT: 1: preprocessor, {0}, cuda-cpp-output, (host-cuda)
// INVALID-ARCH-NEXT: 2: compiler, {1}, ir, (host-cuda)
// INVALID-ARCH-NEXT: 3: backend, {2}, assembler, (host-cuda)
// INVALID-ARCH-NEXT: 4: assembler, {3}, object, (host-cuda)
// INVALID-ARCH: 0: input, "[[INPUT:.+]]", cuda
// INVALID-ARCH-NEXT: 1: preprocessor, {0}, cuda-cpp-output
// INVALID-ARCH-NEXT: 2: compiler, {1}, ir
// INVALID-ARCH-NEXT: 3: backend, {2}, assembler
// INVALID-ARCH-NEXT: 4: assembler, {3}, object

View file

@ -15,5 +15,5 @@
// RUN: --hip-link %S/Inputs/hip_multiple_inputs/a.cu 2>&1 \
// RUN: | FileCheck -check-prefix=MIX %s
// CHECK-NOT: error: mixed CUDA and HIP compilation is not supported
// MIX: error: mixed CUDA and HIP compilation is not supported
// CHECK-NOT: error: mixed CUDA and HIP offloading compilation is not supported
// MIX: error: mixed CUDA and HIP offloading compilation is not supported

View file

@ -4,7 +4,7 @@
// RUN: --rocm-path=%S/Inputs/rocm \
// RUN: %s 2>&1 | FileCheck -check-prefix=NOPLUS %s
// NOPLUS: error: invalid target ID 'gfx908xnack'
// NOPLUS: error: unsupported HIP gpu architecture: gfx908xnack
// RUN: not %clang -### --target=x86_64-linux-gnu \
// RUN: -x hip --offload-arch=gfx900 \
@ -22,7 +22,7 @@
// RUN: --rocm-path=%S/Inputs/rocm \
// RUN: %s 2>&1 | FileCheck -check-prefix=UNK %s
// UNK: error: invalid target ID 'gfx908:unknown+'
// UNK: error: unsupported HIP gpu architecture: gfx900+xnack
// RUN: not %clang -### --target=x86_64-linux-gnu \
// RUN: -x hip --offload-arch=gfx908 \
@ -31,7 +31,7 @@
// RUN: --rocm-path=%S/Inputs/rocm \
// RUN: %s 2>&1 | FileCheck -check-prefix=MIXED %s
// MIXED: error: invalid target ID 'gfx908:sramecc+:unknown+'
// MIXED: error: unsupported HIP gpu architecture: gfx900+xnack
// RUN: not %clang -### --target=x86_64-linux-gnu \
// RUN: -x hip --offload-arch=gfx908 \
@ -55,7 +55,7 @@
// RUN: --rocm-path=%S/Inputs/rocm \
// RUN: %s 2>&1 | FileCheck -check-prefix=NOCOLON %s
// NOCOLON: error: invalid target ID 'gfx900+xnack'
// NOCOLON: error: unsupported HIP gpu architecture: gfx900+xnack
// RUN: not %clang -### --target=x86_64-linux-gnu \
// RUN: -x hip --offload-arch=gfx908 \

View file

@ -115,11 +115,6 @@
// OMP-NOT: "-cc1"{{.*}} "-triple" "amdgcn-amd-amdhsa" {{.*}} "-fopenmp"
// OMP: "-cc1"{{.*}} "-triple" "x86_64-unknown-linux-gnu" {{.*}} "-fopenmp"
// RUN: not %clang --target=x86_64-unknown-linux-gnu -nogpuinc -nogpulib \
// RUN: --offload-arch=gfx906 -fopenmp=libomp -fopenmp-targets=amdgcn %s 2>&1 \
// RUN: | FileCheck -check-prefix=OMPTGT %s
// OMPTGT: unsupported option '--offload-targets=' for language mode 'HIP'
// Check -Xoffload-linker option is passed to lld.
// RUN: %clang -### --target=x86_64-unknown-linux-gnu -nogpuinc -nogpulib \

View file

@ -1,29 +1,7 @@
// UNSUPPORTED: system-windows
// RUN: not %clang -### -x hip --target=x86_64-linux-gnu --offload= \
// RUN: --hip-path=%S/Inputs/hipspv -nogpuinc -nogpulib %s \
// RUN: 2>&1 | FileCheck --check-prefix=INVALID-TARGET %s
// RUN: not %clang -### -x hip --target=x86_64-linux-gnu --offload=foo \
// RUN: --hip-path=%S/Inputs/hipspv -nogpuinc -nogpulib %s \
// RUN: 2>&1 | FileCheck --check-prefix=INVALID-TARGET %s
// INVALID-TARGET: error: invalid or unsupported offload target: '{{.*}}'
// In the future we should be able to specify multiple targets for HIP
// compilation but currently it is not supported.
//
// RUN: not %clang -### -x hip --target=x86_64-linux-gnu --offload=foo,bar \
// RUN: --hip-path=%S/Inputs/hipspv -nogpuinc -nogpulib %s \
// RUN: 2>&1 | FileCheck --check-prefix=TOO-MANY-TARGETS %s
// RUN: not %clang -### -x hip --target=x86_64-linux-gnu \
// RUN: --offload=foo --offload=bar \
// RUN: --hip-path=%S/Inputs/hipspv -nogpuinc -nogpulib %s \
// RUN: 2>&1 | FileCheck --check-prefix=TOO-MANY-TARGETS %s
// TOO-MANY-TARGETS: error: only one offload target is supported
// RUN: not %clang -### -x hip --target=x86_64-linux-gnu -nogpuinc -nogpulib \
// RUN: --offload=amdgcn-amd-amdhsa --offload-arch=gfx900 %s \
// RUN: 2>&1 | FileCheck --check-prefix=OFFLOAD-ARCH-MIX %s
// OFFLOAD-ARCH-MIX: error: option '--offload-arch' cannot be specified with '--offload'

View file

@ -16,14 +16,14 @@
// RUN: | FileCheck %s --check-prefix=NO-OUTPUT-ERROR
// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -nogpulib --offload-new-driver --offload-arch=native --nvptx-arch-tool=%t/nvptx_arch_fail -x cuda %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=NO-OUTPUT-ERROR
// NO-OUTPUT-ERROR: error: cannot determine nvptx64 architecture{{.*}}; consider passing it via '--offload-arch'
// NO-OUTPUT-ERROR: error: cannot determine cuda architecture{{.*}}; consider passing it via '--offload-arch'
// case when nvptx-arch does not return anything with successful execution
// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -nogpulib --offload-arch=native --nvptx-arch-tool=%t/nvptx_arch_empty -x cuda %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=EMPTY-OUTPUT
// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -nogpulib --offload-new-driver --offload-arch=native --nvptx-arch-tool=%t/nvptx_arch_empty -x cuda %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=EMPTY-OUTPUT
// EMPTY-OUTPUT: error: cannot determine nvptx64 architecture: No NVIDIA GPU detected in the system; consider passing it via '--offload-arch'
// EMPTY-OUTPUT: error: cannot determine cuda architecture: No GPU detected in the system; consider passing it via '--offload-arch'
// case when nvptx-arch does not return anything with successful execution
// RUN: %clang -### --target=x86_64-unknown-linux-gnu -nogpulib --offload-arch=native --nvptx-arch-tool=%t/nvptx_arch_sm_70 -x cuda --cuda-path=%S/Inputs/CUDA_102/usr/local/cuda %s 2>&1 \
@ -49,4 +49,4 @@
// RUN: --offload-arch=native --nvptx-arch-tool=%t/nvptx_arch_sm_70 \
// RUN: --cuda-path=%S/Inputs/CUDA_102/usr/local/cuda -x cuda %s 2>&1 | \
// RUN: FileCheck %s --check-prefix=BAD-TIMEOUT
// BAD-TIMEOUT: clang: error: cannot determine nvptx64 architecture: CLANG_TOOLCHAIN_PROGRAM_TIMEOUT expected an integer, got 'foo'; consider passing it via '--offload-arch'; environment variable CLANG_TOOLCHAIN_PROGRAM_TIMEOUT specifies the tool timeout (integer secs, <=0 is infinite)
// BAD-TIMEOUT: clang: error: cannot determine cuda architecture: CLANG_TOOLCHAIN_PROGRAM_TIMEOUT expected an integer, got 'foo'; consider passing it via '--offload-arch'; environment variable CLANG_TOOLCHAIN_PROGRAM_TIMEOUT specifies the tool timeout (integer secs, <=0 is infinite)

View file

@ -0,0 +1,22 @@
// RUN: %clang -### -fsycl --offload-targets=spirv64 -nogpuinc %s -ccc-print-bindings 2>&1 \
// RUN: | FileCheck %s -check-prefix=SYCL
// SYCL: "spirv64" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[SYCL_BC:.+]]"
// RUN: %clang -### --offload-targets=amdgcn-amd-amdhsa -nogpulib -nogpuinc -x hip %s -ccc-print-bindings 2>&1 \
// RUN: | FileCheck %s -check-prefix=HIP
// HIP: "amdgcn-amd-amdhsa" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[AMD_OBJ:.+]]"
// RUN: %clang -### --offload-targets=nvptx64-nvidia-cuda -nogpulib -nogpuinc -x cuda %s -ccc-print-bindings 2>&1 \
// RUN: | FileCheck %s -check-prefix=CUDA
// CUDA: "nvptx64-nvidia-cuda" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[NV_OBJ:.+]]"
// RUN: %clang -### --offload-targets=amdgcn-amd-amdhsa,nvptx64-nvidia-cuda -fopenmp \
// RUN: -Xarch_amdgcn --offload-arch=gfx90a -Xarch_nvptx64 --offload-arch=sm_89 \
// RUN: -nogpulib -nogpuinc %s -ccc-print-bindings 2>&1 \
// RUN: | FileCheck %s -check-prefix=OPENMP
// OPENMP: "amdgcn-amd-amdhsa" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[AMD_OBJ:.+]]"
// OPENMP: "nvptx64-nvidia-cuda" - "clang", inputs: ["[[INPUT]]"], output: "[[NV_OBJ:.+]]"
// RUN: %clang -### --offload-targets=spirv64-amd-amdhsa -nogpulib -nogpuinc -x hip %s -ccc-print-bindings 2>&1 \
// RUN: | FileCheck %s -check-prefix=HIPSPIRV
// HIPSPIRV: "spirv64-amd-amdhsa" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[AMD_OBJ:.+]]"

View file

@ -7,7 +7,7 @@
/// Check whether an invalid OpenMP target is specified:
// RUN: not %clang -### -fopenmp=libomp -fopenmp-targets=aaa-bbb-ccc-ddd %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-INVALID-TARGET %s
// CHK-INVALID-TARGET: error: OpenMP target is invalid: 'aaa-bbb-ccc-ddd'
// CHK-INVALID-TARGET: error: invalid or unsupported offload target: 'aaa-bbb-ccc-ddd'
/// ###########################################################################
@ -18,15 +18,6 @@
/// ###########################################################################
/// Check error for no -fopenmp option
// RUN: not %clang -### -fopenmp-targets=powerpc64le-ibm-linux-gnu %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-NO-FOPENMP %s
// RUN: not %clang -### -fopenmp=libgomp -fopenmp-targets=powerpc64le-ibm-linux-gnu %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-NO-FOPENMP %s
// CHK-NO-FOPENMP: error: '-fopenmp-targets' must be used in conjunction with a '-fopenmp' option compatible with offloading; e.g., '-fopenmp=libomp' or '-fopenmp=libiomp5'
/// ###########################################################################
/// Check warning for duplicate offloading targets.
// RUN: %clang -### -ccc-print-phases -fopenmp=libomp -fopenmp-targets=powerpc64le-ibm-linux-gnu,powerpc64le-ibm-linux-gnu %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-DUPLICATES %s

View file

@ -24,13 +24,7 @@
// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -nogpulib -fopenmp=libomp --offload-arch=native \
// RUN: --nvptx-arch-tool=%t/nvptx_arch_empty --amdgpu-arch-tool=%t/amdgpu_arch_empty %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=NO-OUTPUT-ERROR
// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -nogpulib -fopenmp=libomp --offload-arch= \
// RUN: --nvptx-arch-tool=%t/nvptx_arch_fail --amdgpu-arch-tool=%t/amdgpu_arch_fail %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=NO-OUTPUT-ERROR
// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -nogpulib -fopenmp=libomp --offload-arch= \
// RUN: --nvptx-arch-tool=%t/nvptx_arch_empty --amdgpu-arch-tool=%t/amdgpu_arch_empty %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=NO-OUTPUT-ERROR
// NO-OUTPUT-ERROR: error: failed to deduce triple for target architecture 'native'; specify the triple using '-fopenmp-targets' and '-Xopenmp-target' instead
// NO-OUTPUT-ERROR: error: cannot determine openmp architecture
// case when amdgpu-arch succeeds.
// RUN: %clang -### --target=x86_64-unknown-linux-gnu -nogpulib -fopenmp=libomp --offload-arch=native \