GFX1033,
GFX1034,
GFX1035,
+ Generic, // A processor model named 'generic' if the target backend defines a
+ // public one.
LAST,
};
}
static inline bool IsAMDGpuArch(CudaArch A) {
- return A >= CudaArch::GFX600 && A < CudaArch::LAST;
+ // Generic processor model is for testing only.
+ return A >= CudaArch::GFX600 && A < CudaArch::Generic;
}
const char *CudaArchToString(CudaArch A);
def err_drv_ssp_missing_offset_argument : Error<
"'%0' is used without '-mstack-protector-guard-offset', and there is no default">;
+
+def err_drv_only_one_offload_target_supported_in : Error<
+ "Only one offload target is supported in %0.">;
+def err_drv_invalid_or_unsupported_offload_target : Error<
+ "Invalid or unsupported offload target: '%0'.">;
}
/// @}
+ /// Retrieves a ToolChain for a particular device \p Target triple
+ ///
+ /// \param[in] HostTC is the host ToolChain paired with the device
+ ///
+ /// \param[in] Action (e.g. OFK_Cuda/OFK_OpenMP/OFK_SYCL) is an Offloading
+ /// action that is optionally passed to a ToolChain (used by CUDA, to specify
+ /// if it's used in conjunction with OpenMP)
+ ///
+ /// Will cache ToolChains for the life of the driver object, and create them
+ /// on-demand.
+ const ToolChain &getOffloadingDeviceToolChain(
+ const llvm::opt::ArgList &Args, const llvm::Triple &Target,
+ const ToolChain &HostTC,
+ const Action::OffloadKind &TargetDeviceOffloadKind) const;
+
/// Get bitmasks for which option flags to include and exclude based on
/// the driver mode.
std::pair<unsigned, unsigned> getIncludeExcludeOptionFlagMasks(bool IsClCompatMode) const;
NegFlag<SetFalse, [CC1Option], "Disable generation of linker directives for automatic library linking">,
PosFlag<SetTrue>>;
+// 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]>,
+ HelpText<"Specify comma-separated list of offloading target triples"
+ " (HIP only)">;
+
// C++ Coroutines TS
defm coroutines_ts : BoolFOption<"coroutines-ts",
LangOpts<"Coroutines">, Default<cpp20.KeyPath>,
GFX(1033), // gfx1033
GFX(1034), // gfx1034
GFX(1035), // gfx1035
+ {CudaArch::Generic, "generic", ""},
// clang-format on
};
#undef SM
case CudaArch::GFX1033:
case CudaArch::GFX1034:
case CudaArch::GFX1035:
+ case CudaArch::Generic:
case CudaArch::LAST:
break;
case CudaArch::UNUSED:
void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override {
for (int i = static_cast<int>(CudaArch::SM_20);
- i < static_cast<int>(CudaArch::LAST); ++i)
+ i < static_cast<int>(CudaArch::Generic); ++i)
Values.emplace_back(CudaArchToString(static_cast<CudaArch>(i)));
}
case CudaArch::GFX1033:
case CudaArch::GFX1034:
case CudaArch::GFX1035:
+ case CudaArch::Generic:
case CudaArch::UNUSED:
case CudaArch::UNKNOWN:
break;
#include "ToolChains/Fuchsia.h"
#include "ToolChains/Gnu.h"
#include "ToolChains/HIPAMD.h"
+#include "ToolChains/HIPSPV.h"
#include "ToolChains/Haiku.h"
#include "ToolChains/Hexagon.h"
#include "ToolChains/Hurd.h"
using namespace clang;
using namespace llvm::opt;
-static llvm::Triple getHIPOffloadTargetTriple() {
- static const llvm::Triple T("amdgcn-amd-amdhsa");
+static llvm::Optional<llvm::Triple>
+getHIPOffloadTargetTriple(const Driver &D, const ArgList &Args) {
+ if (Args.hasArg(options::OPT_offload_EQ)) {
+ auto HIPOffloadTargets = Args.getAllArgValues(options::OPT_offload_EQ);
+
+ // HIP 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 (HIPOffloadTargets.size()) {
+ default:
+ D.Diag(diag::err_drv_only_one_offload_target_supported_in) << "HIP";
+ return llvm::None;
+ case 0:
+ D.Diag(diag::err_drv_invalid_or_unsupported_offload_target) << "";
+ return llvm::None;
+ case 1:
+ break;
+ }
+ llvm::Triple TT(HIPOffloadTargets[0]);
+ if (TT.getArch() == llvm::Triple::amdgcn &&
+ TT.getVendor() == llvm::Triple::AMD &&
+ TT.getOS() == llvm::Triple::AMDHSA)
+ return TT;
+ if (TT.getArch() == llvm::Triple::spirv64 &&
+ TT.getVendor() == llvm::Triple::UnknownVendor &&
+ TT.getOS() == llvm::Triple::UnknownOS)
+ return TT;
+ D.Diag(diag::err_drv_invalid_or_unsupported_offload_target)
+ << HIPOffloadTargets[0];
+ return llvm::None;
+ }
+
+ static const llvm::Triple T("amdgcn-amd-amdhsa"); // Default HIP triple.
return T;
}
return;
}
const ToolChain *HostTC = C.getSingleOffloadToolChain<Action::OFK_Host>();
- const llvm::Triple &HostTriple = HostTC->getTriple();
auto OFK = Action::OFK_HIP;
- llvm::Triple HIPTriple = getHIPOffloadTargetTriple();
- // Use the HIP and host triples as the key into the ToolChains map,
- // because the device toolchain we create depends on both.
- auto &HIPTC = ToolChains[HIPTriple.str() + "/" + HostTriple.str()];
- if (!HIPTC) {
- HIPTC = std::make_unique<toolchains::HIPAMDToolChain>(
- *this, HIPTriple, *HostTC, C.getInputArgs());
- }
- C.addOffloadDeviceToolChain(HIPTC.get(), OFK);
+ auto HIPTriple = getHIPOffloadTargetTriple(*this, C.getInputArgs());
+ if (!HIPTriple)
+ return;
+ auto *HIPTC = &getOffloadingDeviceToolChain(C.getInputArgs(), *HIPTriple,
+ *HostTC, OFK);
+ assert(HIPTC && "Could not create offloading device tool chain.");
+ C.addOffloadDeviceToolChain(HIPTC, OFK);
}
//
}
}
+ // --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 cuda_gpu_arch parameters, removing duplicates.
std::set<StringRef> GpuArchs;
bool Error = false;
// 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())
- GpuArchList.push_back(DefaultCudaArch);
+ if (GpuArchList.empty()) {
+ if (ToolChains.front()->getTriple().isSPIRV())
+ GpuArchList.push_back(CudaArch::Generic);
+ else
+ GpuArchList.push_back(DefaultCudaArch);
+ }
return Error;
}
StringRef getCanonicalOffloadArch(StringRef IdStr) override {
llvm::StringMap<bool> Features;
- auto ArchStr =
- parseTargetID(getHIPOffloadTargetTriple(), IdStr, &Features);
+ // getHIPOffloadTargetTriple() is known to return valid value as it has
+ // been called successfully in the CreateOffloadingDeviceToolChains().
+ auto ArchStr = parseTargetID(
+ *getHIPOffloadTargetTriple(C.getDriver(), C.getInputArgs()), IdStr,
+ &Features);
if (!ArchStr) {
C.getDriver().Diag(clang::diag::err_drv_bad_target_id) << IdStr;
C.setContainsError();
// When LTO is not enabled, we follow the conventional
// compiler phases, including backend and assemble phases.
ActionList AL;
- auto BackendAction = C.getDriver().ConstructPhaseAction(
- C, Args, phases::Backend, CudaDeviceActions[I],
- AssociatedOffloadKind);
+ Action *BackendAction = nullptr;
+ if (ToolChains.front()->getTriple().isSPIRV()) {
+ // Emit LLVM bitcode for SPIR-V targets. SPIR-V device tool chain
+ // (HIPSPVToolChain) runs post-link LLVM IR passes.
+ types::ID Output = Args.hasArg(options::OPT_S)
+ ? types::TY_LLVM_IR
+ : types::TY_LLVM_BC;
+ BackendAction =
+ C.MakeAction<BackendJobAction>(CudaDeviceActions[I], Output);
+ } else
+ BackendAction = C.getDriver().ConstructPhaseAction(
+ C, Args, phases::Backend, CudaDeviceActions[I],
+ AssociatedOffloadKind);
auto AssembleAction = C.getDriver().ConstructPhaseAction(
C, Args, phases::Assemble, BackendAction,
AssociatedOffloadKind);
return *TC;
}
+const ToolChain &Driver::getOffloadingDeviceToolChain(
+ const ArgList &Args, const llvm::Triple &Target, const ToolChain &HostTC,
+ const Action::OffloadKind &TargetDeviceOffloadKind) const {
+ // Use device / host triples as the key into the ToolChains map because the
+ // device ToolChain we create depends on both.
+ auto &TC = ToolChains[Target.str() + "/" + HostTC.getTriple().str()];
+ if (!TC) {
+ // Categorized by offload kind > arch rather than OS > arch like
+ // the normal getToolChain call, as it seems a reasonable way to categorize
+ // things.
+ switch (TargetDeviceOffloadKind) {
+ case Action::OFK_HIP: {
+ if (Target.getArch() == llvm::Triple::amdgcn &&
+ Target.getVendor() == llvm::Triple::AMD &&
+ Target.getOS() == llvm::Triple::AMDHSA)
+ TC = std::make_unique<toolchains::HIPAMDToolChain>(*this, Target,
+ HostTC, Args);
+ else if (Target.getArch() == llvm::Triple::spirv64 &&
+ Target.getVendor() == llvm::Triple::UnknownVendor &&
+ Target.getOS() == llvm::Triple::UnknownOS)
+ TC = std::make_unique<toolchains::HIPSPVToolChain>(*this, Target,
+ HostTC, Args);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return *TC;
+}
+
bool Driver::ShouldUseClangCompiler(const JobAction &JA) const {
// Say "no" if there is not exactly one input of a type clang understands.
if (JA.size() != 1 ||
--- /dev/null
+HIP_VERSION_MAJOR=3
+HIP_VERSION_MINOR=6
--- /dev/null
+// REQUIRES: clang-driver
+// REQUIRES: x86-registered-target
+// UNSUPPORTED: system-windows
+
+// RUN: %clang -### -target x86_64-linux-gnu --offload=spirv64 \
+// RUN: --hip-path=%S/Inputs/hipspv %s \
+// RUN: 2>&1 | FileCheck --check-prefixes=ALL,HIP-PATH %s
+
+// Test --hip-device-lib-path
+// RUN: %clang -### -target x86_64-linux-gnu --offload=spirv64 \
+// RUN: --hip-path=%S/Inputs/hipspv \
+// RUN: --hip-device-lib-path=%S/Inputs/hipspv-dev-lib %s \
+// RUN: 2>&1 | FileCheck --check-prefixes=ALL,HIP-DEV-LIB-PATH %s
+
+// Test --hip-device-lib w/ --hip-device-lib-path and HIP_DEVICE_LIB_PATH.
+// RUN: env HIP_DEVICE_LIB_PATH=%S/Inputs/hipspv-dev-lib/a \
+// RUN: %clang -### -target x86_64-linux-gnu --offload=spirv64 \
+// RUN: --hip-path=%S/Inputs/hipspv \
+// RUN: --hip-device-lib-path=%S/Inputs/hipspv-dev-lib/b \
+// RUN: --hip-device-lib=a.bc --hip-device-lib=b.bc %s \
+// RUN: 2>&1 | FileCheck --check-prefixes=ALL,HIP-DEV-LIB %s
+
+// ALL: {{"[^"]*clang[^"]*"}}
+// HIP-PATH: "-mlink-builtin-bitcode" {{".*/hipspv/lib/hip-device-lib/hipspv-spirv64.bc"}}
+// HIP-DEV-LIB-PATH-NOT: "-mlink-builtin-bitcode" {{".*/hipspv/lib/hip-device-lib/hipspv-spirv64.bc"}}
+// HIP-DEV-LIB-PATH: "-mlink-builtin-bitcode" {{".*/hipspv-dev-lib/hipspv-spirv64.bc"}}
+// HIP-DEV-LIB: "-mlink-builtin-bitcode" {{".*/hipspv-dev-lib/a/a.bc"}}
+// HIP-DEV-LIB-SAME: "-mlink-builtin-bitcode" {{".*/hipspv-dev-lib/b/b.bc"}}
--- /dev/null
+// REQUIRES: clang-driver
+// REQUIRES: x86-registered-target
+// UNSUPPORTED: system-windows
+
+// RUN: %clang -### -target x86_64-linux-gnu --offload=spirv64 \
+// RUN: --hip-path=%S/Inputs/hipspv -nogpuinc %s \
+// RUN: 2>&1 | FileCheck --check-prefixes=FROM-HIP-PATH %s
+
+// RUN: %clang -### -target x86_64-linux-gnu --offload=spirv64 \
+// RUN: -nogpuinc -nogpulib --hipspv-pass-plugin=%S/Inputs/pass-plugin.so %s \
+// RUN: 2>&1 | FileCheck --check-prefixes=FROM-OPTION %s
+
+// RUN: %clang -### -target x86_64-linux-gnu --offload=spirv64 \
+// RUN: -nogpuinc -nogpulib --hipspv-pass-plugin=foo.so %s \
+// RUN: 2>&1 | FileCheck --check-prefixes=FROM-OPTION-INVALID %s
+
+// RUN: %clang -### -target x86_64-linux-gnu --offload=spirv64 \
+// RUN: -nogpuinc -nogpulib %s \
+// RUN: 2>&1 | FileCheck --check-prefixes=NO-PLUGIN %s
+
+// FROM-HIP-PATH: {{".*opt"}} {{".*.bc"}} "-load-pass-plugin"
+// FROM-HIP-PATH-SAME: {{".*/Inputs/hipspv/lib/libLLVMHipSpvPasses.so"}}
+// FROM-OPTION: {{".*opt"}} {{".*.bc"}} "-load-pass-plugin"
+// FROM-OPTION-SAME: {{".*/Inputs/pass-plugin.so"}}
+// FROM-OPTION-INVALID: error: no such file or directory: 'foo.so'
+// NO-PLUGIN-NOT: {{".*opt"}} {{".*.bc"}} "-load-pass-plugin"
+// NO-PLUGIN-NOT: {{".*/Inputs/hipspv/lib/libLLVMHipSpvPasses.so"}}
--- /dev/null
+// REQUIRES: clang-driver
+// REQUIRES: x86-registered-target
+// UNSUPPORTED: system-windows
+
+// RUN: %clang -### -x hip -target x86_64-linux-gnu --offload=spirv64 \
+// RUN: -fgpu-rdc --hip-path=%S/Inputs/hipspv -nohipwrapperinc \
+// RUN: %S/Inputs/hip_multiple_inputs/a.cu \
+// RUN: %S/Inputs/hip_multiple_inputs/b.hip \
+// RUN: 2>&1 | FileCheck %s
+
+// Emit objects for host side path
+// CHECK: [[CLANG:".*clang.*"]] "-cc1" "-triple" "x86_64-unknown-linux-gnu"
+// CHECK-SAME: "-aux-triple" "spirv64"
+// CHECK-SAME: "-emit-obj"
+// CHECK-SAME: "-fgpu-rdc"
+// CHECK-SAME: {{.*}} "-o" [[A_OBJ_HOST:".*o"]] "-x" "hip"
+// CHECK-SAME: {{.*}} [[A_SRC:".*a.cu"]]
+
+// CHECK: [[CLANG]] "-cc1" "-triple" "x86_64-unknown-linux-gnu"
+// CHECK-SAME: "-aux-triple" "spirv64"
+// CHECK-SAME: "-emit-obj"
+// CHECK-SAME: "-fgpu-rdc"
+// CHECK-SAME: {{.*}} "-o" [[B_OBJ_HOST:".*o"]] "-x" "hip"
+// CHECK-SAME: {{.*}} [[B_SRC:".*b.hip"]]
+
+// Emit code (LLVM BC) for device side path.
+// CHECK: [[CLANG]] "-cc1" "-triple" "spirv64"
+// CHECK-SAME: "-aux-triple" "x86_64-unknown-linux-gnu"
+// CHECK-SAME: "-emit-llvm-bc"
+// CHECK-SAME: "-fcuda-is-device" "-fcuda-allow-variadic-functions"
+// CHECK-SAME: "-fvisibility" "hidden" "-fapply-global-visibility-to-externs"
+// CHECK-SAME: "-fgpu-rdc"
+// CHECK-SAME: {{.*}} "-o" [[A_BC1:".*bc"]] "-x" "hip"
+// CHECK-SAME: {{.*}} [[A_SRC]]
+
+// CHECK: [[CLANG]] "-cc1" "-triple" "spirv64"
+// CHECK-SAME: "-aux-triple" "x86_64-unknown-linux-gnu"
+// CHECK-SAME: "-emit-llvm-bc"
+// CHECK-SAME: "-fcuda-is-device" "-fcuda-allow-variadic-functions"
+// CHECK-SAME: "-fvisibility" "hidden" "-fapply-global-visibility-to-externs"
+// CHECK-SAME: "-fgpu-rdc"
+// CHECK-SAME: {{.*}} "-o" [[B_BC1:".*bc"]] "-x" "hip"
+// CHECK-SAME: {{.*}} [[B_SRC]]
+
+// Link device code, lower it with HIPSPV passes and emit SPIR-V binary.
+// CHECK: {{".*llvm-link.*"}} [[A_BC1]] [[B_BC1]] "-o" [[AB_LINK:".*bc"]]
+// CHECK: {{".*opt.*"}} [[AB_LINK]] "-load-pass-plugin"
+// CHECK-SAME: "{{.*}}/Inputs/hipspv/lib/libLLVMHipSpvPasses.so"
+// CHECK-SAME: "-o" [[AB_LOWER:".*bc"]]
+// CHECK: {{".*llvm-spirv"}} "--spirv-max-version=1.1" "--spirv-ext=+all"
+// CHECK-SAME: [[AB_LOWER]] "-o" "[[AB_SPIRV:.*out]]"
+
+// Construct fat binary object.
+// CHECK: [[BUNDLER:".*clang-offload-bundler"]] "-type=o" "-bundle-align=4096"
+// CHECK-SAME: "-targets={{.*}},hip-spirv64----generic"
+// CHECK-SAME: "-inputs=/dev/null,[[AB_SPIRV]]"
+// CHECK-SAME: "-outputs=[[AB_FATBIN:.*hipfb]]"
+// CHECK: {{".*llvm-mc.*"}} "-o" [[OBJBUNDLE:".*o"]] "{{.*}}.mcin"
+// CHECK-SAME: "--filetype=obj"
+
+// Output the executable
+// CHECK: {{".*ld.*"}} {{.*}}"-o" "a.out" {{.*}} [[A_OBJ_HOST]] [[B_OBJ_HOST]]
+// CHECK-SAME: [[OBJBUNDLE]]
--- /dev/null
+// REQUIRES: clang-driver
+// REQUIRES: x86-registered-target
+// UNSUPPORTED: system-windows
+
+// RUN: %clang -### -target x86_64-linux-gnu --offload=spirv64 \
+// RUN: --hip-path=%S/Inputs/hipspv -nohipwrapperinc %s \
+// RUN: 2>&1 | FileCheck %s
+
+// CHECK: [[CLANG:".*clang.*"]] "-cc1" "-triple" "spirv64"
+// CHECK-SAME: "-aux-triple" "{{.*}}" "-emit-llvm-bc"
+// CHECK-SAME: "-fcuda-is-device"
+// CHECK-SAME: "-fcuda-allow-variadic-functions"
+// CHECK-SAME: "-mlink-builtin-bitcode" {{".*/hipspv/lib/hip-device-lib/hipspv-spirv64.bc"}}
+// CHECK-SAME: "-isystem" {{".*/hipspv/include"}}
+// CHECK-SAME: "-fhip-new-launch-api"
+// CHECK-SAME: "-o" [[DEV_BC:".*bc"]]
+// CHECK-SAME: "-x" "hip"
+
+// CHECK: {{".*llvm-link"}} [[DEV_BC]] "-o" [[LINK_BC:".*bc"]]
+
+// CHECK: {{".*opt"}} [[LINK_BC]] "-load-pass-plugin"
+// CHECK-SAME: {{".*/hipspv/lib/libLLVMHipSpvPasses.so"}}
+// CHECK-SAME: "-passes=hip-post-link-passes" "-o" [[LOWER_BC:".*bc"]]
+
+// CHECK: {{".*llvm-spirv"}} "--spirv-max-version=1.1" "--spirv-ext=+all"
+// CHECK-SAME: [[LOWER_BC]] "-o" "[[SPIRV_OUT:.*out]]"
+
+// CHECK: {{".*clang-offload-bundler"}} "-type=o" "-bundle-align=4096"
+// CHECK-SAME: "-targets=host-x86_64-unknown-linux,hip-spirv64----generic"
+// CHECK-SAME: "-inputs={{.*}},[[SPIRV_OUT]]" "-outputs=[[BUNDLE:.*hipfb]]"
+
+// CHECK: [[CLANG]] "-cc1" "-triple" {{".*"}} "-aux-triple" "spirv64"
+// CHECK-SAME: "-emit-obj"
+// CHECK-SAME: "-fcuda-include-gpubinary" "[[BUNDLE]]"
+// CHECK-SAME: "-o" [[OBJ_HOST:".*o"]] "-x" "hip"
+
+// CHECK: {{".*ld.*"}} {{.*}}[[OBJ_HOST]]
--- /dev/null
+// REQUIRES: clang-driver
+// REQUIRES: x86-registered-target
+// UNSUPPORTED: system-windows
+
+// RUN: %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: %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: %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: %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 in HIP.
+
+// RUN: %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'