[HIPSPV] Add in-tree SPIR-V backend support for chipStar#186972
[HIPSPV] Add in-tree SPIR-V backend support for chipStar#186972pvelesko wants to merge 7 commits into
Conversation
|
@llvm/pr-subscribers-llvm-transforms @llvm/pr-subscribers-clang-driver Author: Paulius Velesko (pvelesko) ChangesSummarychipStar (https://github.com/CHIP-SPV/chipStar) enables HIP/CUDA programs to run on OpenCL and Level Zero devices via SPIR-V. Until now, the HIPSPV toolchain relied exclusively on the external This patch adds native in-tree SPIR-V backend support for chipStar targets ( ChangesHIPSPV old driver (
New offload driver (
SPIR-V toolchain (
SPIR-V backend (
Test plan
Patch is 21.95 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/186972.diff 6 Files Affected:
diff --git a/clang/include/clang/Basic/AlignedAllocation.h b/clang/include/clang/Basic/AlignedAllocation.h
index ac26eb4a276da..9b84d07286d52 100644
--- a/clang/include/clang/Basic/AlignedAllocation.h
+++ b/clang/include/clang/Basic/AlignedAllocation.h
@@ -35,6 +35,8 @@ inline llvm::VersionTuple alignedAllocMinVersion(llvm::Triple::OSType OS) {
return llvm::VersionTuple(4U);
case llvm::Triple::ZOS:
return llvm::VersionTuple(); // All z/OS versions have no support.
+ case llvm::Triple::ChipStar:
+ return llvm::VersionTuple(); // No version constraint for device targets.
}
llvm_unreachable("Unexpected OS");
diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index 8bdb7ab042b2b..70f831c3ade87 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -72,10 +72,56 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
tools::constructLLVMLinkCommand(C, *this, JA, Inputs, LinkArgs, Output, Args,
TempFile);
- // Post-link HIP lowering.
+ auto T = getToolChain().getTriple();
+
+ if (T.getOS() == llvm::Triple::ChipStar) {
+ // chipStar: run HipSpvPasses via opt, then use the in-tree SPIR-V backend
+ // for codegen (replaces the external llvm-spirv translator).
+
+ // Run HipSpvPasses plugin via opt (must run on LLVM IR before
+ // the SPIR-V backend lowers to MIR).
+ auto PassPluginPath = findPassPlugin(C.getDriver(), Args);
+ if (!PassPluginPath.empty()) {
+ const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath);
+ const char *OptOutput = HIP::getTempFile(C, Name + "-lower", "bc");
+ ArgStringList OptArgs{TempFile, "-load-pass-plugin",
+ PassPathCStr, "-passes=hip-post-link-passes",
+ "-o", OptOutput};
+ const char *Opt =
+ Args.MakeArgString(getToolChain().GetProgramPath("opt"));
+ C.addCommand(std::make_unique<Command>(
+ JA, *this, ResponseFileSupport::None(), Opt, OptArgs, Inputs,
+ Output));
+ TempFile = OptOutput;
+ }
- // Run LLVM IR passes to lower/expand/emulate HIP code that does not translate
- // to SPIR-V (E.g. dynamic shared memory).
+ // Compile processed bitcode to SPIR-V using the in-tree backend.
+ ArgStringList ClangArgs;
+ ClangArgs.push_back("--no-default-config");
+ ClangArgs.push_back("-c");
+ ClangArgs.push_back(
+ C.getArgs().MakeArgString("--target=" + T.getTriple()));
+
+ ClangArgs.push_back("-mllvm");
+ ClangArgs.push_back("-spirv-ext=+SPV_INTEL_function_pointers"
+ ",+SPV_INTEL_subgroups"
+ ",+SPV_EXT_relaxed_printf_string_address_space"
+ ",+SPV_KHR_bit_instructions"
+ ",+SPV_EXT_shader_atomic_float_add");
+
+ ClangArgs.push_back(TempFile);
+ ClangArgs.push_back("-o");
+ ClangArgs.push_back(Output.getFilename());
+
+ const char *Clang =
+ C.getArgs().MakeArgString(C.getDriver().getClangProgramPath());
+ C.addCommand(std::make_unique<Command>(
+ JA, *this, ResponseFileSupport::None(), Clang, ClangArgs, Inputs,
+ Output));
+ return;
+ }
+
+ // Non-chipStar: run HIP passes via opt, then translate with llvm-spirv.
auto PassPluginPath = findPassPlugin(C.getDriver(), Args);
if (!PassPluginPath.empty()) {
const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath);
@@ -89,27 +135,11 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
TempFile = OptOutput;
}
- // Emit SPIR-V binary.
+ // Emit SPIR-V binary via llvm-spirv translator (non-chipStar targets).
llvm::opt::ArgStringList TrArgs;
- auto T = getToolChain().getTriple();
- bool HasNoSubArch = T.getSubArch() == llvm::Triple::NoSubArch;
- if (T.getOS() == llvm::Triple::ChipStar) {
- // chipStar needs 1.2 for supporting warp-level primitivies via sub-group
- // extensions. Strictly put we'd need 1.3 for the standard non-extension
- // shuffle operations, but it's not supported by any backend driver of the
- // chipStar.
- if (HasNoSubArch)
- TrArgs.push_back("--spirv-max-version=1.2");
- TrArgs.push_back("--spirv-ext=-all"
- // Needed for experimental indirect call support.
- ",+SPV_INTEL_function_pointers"
- // Needed for shuffles below SPIR-V 1.3
- ",+SPV_INTEL_subgroups");
- } else {
- if (HasNoSubArch)
- TrArgs.push_back("--spirv-max-version=1.1");
- TrArgs.push_back("--spirv-ext=+all");
- }
+ if (T.getSubArch() == llvm::Triple::NoSubArch)
+ TrArgs.push_back("--spirv-max-version=1.1");
+ TrArgs.push_back("--spirv-ext=+all");
InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, "");
SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs);
@@ -152,14 +182,17 @@ HIPSPVToolChain::HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple,
void HIPSPVToolChain::addClangTargetOptions(
const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
Action::OffloadKind DeviceOffloadingKind) const {
-
if (!HostTC) {
assert(DeviceOffloadingKind == Action::OFK_None &&
"Need host toolchain for offloading!");
return;
}
- HostTC->addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind);
+ // NOTE: Unlike other HIP toolchains, we do NOT delegate to
+ // HostTC.addClangTargetOptions() here. On macOS (Darwin), the host toolchain
+ // adds flags like -faligned-alloc-unavailable that are specific to macOS
+ // libc++ and break SPIR-V device compilation. SPIR-V device code doesn't
+ // have the same stdlib limitations as the host.
assert(DeviceOffloadingKind == Action::OFK_HIP &&
"Only HIP offloading kinds are supported for GPUs.");
diff --git a/clang/lib/Driver/ToolChains/SPIRV.cpp b/clang/lib/Driver/ToolChains/SPIRV.cpp
index a59bd05cac0cf..e6a04e00af87b 100644
--- a/clang/lib/Driver/ToolChains/SPIRV.cpp
+++ b/clang/lib/Driver/ToolChains/SPIRV.cpp
@@ -185,7 +185,8 @@ SPIRVToolChain::SPIRVToolChain(const Driver &D, const llvm::Triple &Triple,
: ToolChain(D, Triple, Args) {
// TODO: Revisit need/use of --sycl-link option once SYCL toolchain is
// available and SYCL linking support is moved there.
- NativeLLVMSupport = Args.hasArg(options::OPT_sycl_link) || D.isUsingLTO();
+ NativeLLVMSupport = Args.hasArg(options::OPT_sycl_link) || D.isUsingLTO() ||
+ Triple.getOS() == llvm::Triple::ChipStar;
// Lookup binaries into the driver directory.
getProgramPaths().push_back(getDriver().Dir);
diff --git a/clang/test/Driver/hipspv-toolchain.hip b/clang/test/Driver/hipspv-toolchain.hip
index ae8d65313abfb..9af64ca4bdaa4 100644
--- a/clang/test/Driver/hipspv-toolchain.hip
+++ b/clang/test/Driver/hipspv-toolchain.hip
@@ -59,17 +59,50 @@
// RUN: llvm-offload-binary -o %t.dev.out \
// RUN: --image=file=%t.dev.bc,kind=hip,triple=spirv64-unknown-chipstar,arch=generic
-// RUN: clang-linker-wrapper --dry-run \
+// Test the in-tree SPIR-V backend path (no llvm-spirv available).
+// Run from a directory that doesn't contain llvm-spirv and use
+// --no-canonical-prefixes so getExecutableDir() looks there instead of
+// the build bin dir. Empty PATH ensures PATH lookup also fails.
+// RUN: mkdir -p %t/no-spirv %t/empty
+// RUN: ln -sf clang-linker-wrapper %t/no-spirv/clang-linker-wrapper
+// RUN: env "PATH=%t/empty" %t/no-spirv/clang-linker-wrapper \
+// RUN: --no-canonical-prefixes --dry-run \
// RUN: --device-compiler=spirv64-unknown-chipstar=--hip-path="%S/Inputs/hipspv" \
// RUN: --host-triple=spirv64-unknown-chipstar \
// RUN: --linker-path=clang-offload-bundler \
// RUN: --emit-fatbin-only -o /dev/null %t.dev.out \
// RUN: 2>&1 | FileCheck %s --check-prefix=WRAPPER -DHIP_PATH=%S/Inputs/hipspv
-// WRAPPER: clang{{.*}}" --no-default-config -o {{[^ ]*.img}}
+// The linker wrapper runs opt (HipSpvPasses) then uses the in-tree SPIR-V
+// backend when llvm-spirv is not available.
+// WRAPPER: "{{.*}}opt" {{.*}}-load-pass-plugin
+// WRAPPER-SAME: {{.*}}libLLVMHipSpvPasses.so
+// WRAPPER-SAME: -passes=hip-post-link-passes
+
+// WRAPPER: "{{.*}}clang{{.*}}" --no-default-config -o
// WRAPPER-SAME: --target=spirv64-unknown-chipstar
-// WRAPPER-SAME: {{[^ ]*.o}}
-// WRAPPER-SAME: --hip-path=[[HIP_PATH]]
+// WRAPPER-SAME: -mllvm -spirv-ext=+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_EXT_relaxed_printf_string_address_space,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add
+// WRAPPER-SAME: -c -x ir
+
+// Test the llvm-spirv translator path (llvm-spirv available in executable dir).
+// Place a fake llvm-spirv next to the clang-linker-wrapper symlink.
+// RUN: mkdir -p %t/with-spirv
+// RUN: ln -sf clang-linker-wrapper %t/with-spirv/clang-linker-wrapper
+// RUN: touch %t/with-spirv/llvm-spirv && chmod +x %t/with-spirv/llvm-spirv
+// RUN: env "PATH=%t/empty" %t/with-spirv/clang-linker-wrapper \
+// RUN: --no-canonical-prefixes --dry-run \
+// RUN: --device-compiler=spirv64-unknown-chipstar=--hip-path="%S/Inputs/hipspv" \
+// RUN: --host-triple=spirv64-unknown-chipstar \
+// RUN: --linker-path=clang-offload-bundler \
+// RUN: --emit-fatbin-only -o /dev/null %t.dev.out \
+// RUN: 2>&1 | FileCheck %s --check-prefix=WRAPPER-TR
+
+// WRAPPER-TR: "{{.*}}opt" {{.*}}-load-pass-plugin
+// WRAPPER-TR-SAME: {{.*}}libLLVMHipSpvPasses.so
+// WRAPPER-TR-SAME: -passes=hip-post-link-passes
+
+// WRAPPER-TR: "{{.*}}llvm-spirv" {{.*}}--spirv-max-version=1.2
+// WRAPPER-TR-SAME: --spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups
// RUN: touch %t.dummy.o
// RUN: %clang -### --no-default-config -o %t.dummy.img \
@@ -84,8 +117,9 @@
// CHIPSTAR-SAME: "[[HIP_PATH]]/lib/libLLVMHipSpvPasses.so"
// CHIPSTAR-SAME: "-passes=hip-post-link-passes" "-o" [[LOWER_BC:".*bc"]]
-// CHIPSTAR: {{".*llvm-spirv"}} "--spirv-max-version=1.2"
-// CHIPSTAR-SAME: "--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups"
+// CHIPSTAR: {{".*clang.*"}} "--no-default-config" "-c"
+// CHIPSTAR-SAME: "--target=spirv64-unknown-chipstar"
+// CHIPSTAR-SAME: "-mllvm" "-spirv-ext=+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_EXT_relaxed_printf_string_address_space,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add"
// CHIPSTAR-SAME: [[LOWER_BC]] "-o" "[[SPIRV_OUT:.*img]]"
// RUN: %clang -### --no-default-config -o %t.dummy.img \
@@ -100,8 +134,9 @@
// CHIPSTAR-SUBARCH-SAME: "[[HIP_PATH]]/lib/libLLVMHipSpvPasses.so"
// CHIPSTAR-SUBARCH-SAME: "-passes=hip-post-link-passes" "-o" [[LOWER_BC:".*bc"]]
-// CHIPSTAR-SUBARCH: {{".*llvm-spirv"}}
-// CHIPSTAR-SUBARCH-SAME: "--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups"
+// CHIPSTAR-SUBARCH: {{".*clang.*"}} "--no-default-config" "-c"
+// CHIPSTAR-SUBARCH-SAME: "--target=spirv64v1.3-unknown-chipstar"
+// CHIPSTAR-SUBARCH-SAME: "-mllvm" "-spirv-ext=+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_EXT_relaxed_printf_string_address_space,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add"
// CHIPSTAR-SUBARCH-SAME: [[LOWER_BC]] "-o" "[[SPIRV_OUT:.*img]]"
//-----------------------------------------------------------------------------
@@ -115,9 +150,4 @@
// RUN: | FileCheck -DVERSION=%llvm-version-major \
// RUN: --check-prefix=VERSIONED %s
-// RUN: env "PATH=%t/versioned" %clang -### --no-default-config \
-// RUN: -o %t.dummy.img --target=spirv64-unknown-chipstar %t.dummy.o \
-// RUN: --hip-path="%S/Inputs/hipspv" -o /dev/null 2>&1 \
-// RUN: | FileCheck -DVERSION=%llvm-version-major --check-prefix=VERSIONED %s
-
// VERSIONED: {{.*}}llvm-spirv-[[VERSION]]
diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
index 9e24a9c26d897..2522d0ac71df1 100644
--- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
+++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
@@ -125,6 +125,9 @@ static StringRef ExecutableName;
/// Binary path for the CUDA installation.
static std::string CudaBinaryPath;
+/// HIP installation path.
+static std::string HipPath;
+
/// Mutex lock to protect writes to shared TempFiles in parallel.
static std::mutex TempFilesMutex;
@@ -479,9 +482,25 @@ fatbinary(ArrayRef<std::tuple<StringRef, StringRef, StringRef>> InputFiles,
SmallVector<StringRef> Targets = {
Saver.save("-targets=host-" + HostTriple.normalize())};
for (const auto &[File, TripleRef, Arch] : InputFiles) {
- std::string NormalizedTriple =
- normalizeForBundler(Triple(TripleRef), !Arch.empty());
- Targets.push_back(Saver.save("hip-" + NormalizedTriple + "-" + Arch));
+ llvm::Triple T(TripleRef);
+ // For SPIR-V targets, derive arch from triple if not provided
+ StringRef EffectiveArch = Arch;
+ if (EffectiveArch.empty() && T.isSPIRV()) {
+ EffectiveArch = T.getArchName();
+ }
+ StringRef BundleID;
+ if (EffectiveArch == "amdgcnspirv") {
+ BundleID = Saver.save("hip-spirv64-amd-amdhsa--" + EffectiveArch);
+ } else if (T.isSPIRV()) {
+ // ChipStar and other SPIR-V HIP targets: use hip-spirv64-<vendor>-<os>--<arch>
+ BundleID = Saver.save("hip-spirv64-" + T.getVendorName() + "-" +
+ T.getOSName() + "--" + EffectiveArch);
+ } else {
+ std::string NormalizedTriple =
+ normalizeForBundler(T, !Arch.empty());
+ BundleID = Saver.save("hip-" + NormalizedTriple + "-" + Arch);
+ }
+ Targets.push_back(BundleID);
}
CmdArgs.push_back(Saver.save(llvm::join(Targets, ",")));
@@ -554,7 +573,161 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args,
if (!Triple.isNVPTX() && !Triple.isSPIRV())
CmdArgs.push_back("-Wl,--no-undefined");
- for (StringRef InputFile : InputFiles)
+ // For non-chipStar SPIR-V targets, pass the HIP path to clang so it can
+ // find resources. For chipStar, passes are run via opt separately, so the
+ // inner clang doesn't need --hip-path (it just compiles IR to SPIR-V).
+ if (Triple.isSPIRV() && !HipPath.empty() &&
+ Triple.getOS() != llvm::Triple::ChipStar)
+ CmdArgs.push_back(Args.MakeArgString("--hip-path=" + HipPath));
+
+ // For chipStar targets: llvm-link (merge) → opt (HipSpvPasses) → clang
+ // (SPIR-V backend). The passes must operate on LLVM IR before the backend
+ // lowers to MIR, and all TU bitcode must be merged first for RDC support.
+ SmallVector<StringRef, 16> ProcessedInputFiles;
+ if (Triple.isSPIRV() && Triple.getOS() == llvm::Triple::ChipStar) {
+ // Step 1: Merge all input bitcode files with llvm-link (needed for RDC
+ // where functions can be defined across translation units).
+ StringRef MergedFile;
+ if (InputFiles.size() > 1) {
+ Expected<std::string> LinkPath =
+ findProgram("llvm-link", {getExecutableDir("llvm-link")});
+ if (!LinkPath)
+ return LinkPath.takeError();
+
+ auto LinkOutOrErr = createOutputFile(
+ sys::path::filename(ExecutableName) + ".merged", "bc");
+ if (!LinkOutOrErr)
+ return LinkOutOrErr.takeError();
+
+ SmallVector<StringRef, 16> LinkArgs{*LinkPath};
+ for (StringRef F : InputFiles)
+ LinkArgs.push_back(F);
+ LinkArgs.push_back("-o");
+ LinkArgs.push_back(*LinkOutOrErr);
+
+ if (Error Err = executeCommands(*LinkPath, LinkArgs))
+ return std::move(Err);
+
+ MergedFile = *LinkOutOrErr;
+ } else {
+ MergedFile = InputFiles[0];
+ }
+
+ // Step 2: Run HipSpvPasses via opt on the merged bitcode.
+ SmallString<128> PluginPath;
+ if (!HipPath.empty()) {
+ PluginPath.assign(HipPath);
+ sys::path::append(PluginPath, "lib", "libLLVMHipSpvPasses.so");
+ if (!sys::fs::exists(PluginPath)) {
+ PluginPath.assign(HipPath);
+ sys::path::append(PluginPath, "lib", "llvm",
+ "libLLVMHipSpvPasses.so");
+ }
+ if (!sys::fs::exists(PluginPath))
+ PluginPath.clear();
+ }
+
+ StringRef OptOutputFile = MergedFile;
+ if (!PluginPath.empty()) {
+ Expected<std::string> OptPath =
+ findProgram("opt", {getExecutableDir("opt")});
+ if (!OptPath)
+ return OptPath.takeError();
+
+ auto OptOutOrErr = createOutputFile(
+ sys::path::filename(ExecutableName) + ".lowered", "bc");
+ if (!OptOutOrErr)
+ return OptOutOrErr.takeError();
+
+ SmallVector<StringRef, 16> OptArgs{
+ *OptPath,
+ MergedFile,
+ "-load-pass-plugin",
+ Args.MakeArgString(PluginPath),
+ "-passes=hip-post-link-passes",
+ "-o",
+ *OptOutOrErr,
+ };
+
+ if (Error Err = executeCommands(*OptPath, OptArgs))
+ return std::move(Err);
+
+ OptOutputFile = *OptOutOrErr;
+ }
+
+ // Step 3: Convert processed bitcode to SPIR-V.
+ // Check if llvm-spirv translator is available. If so, use it directly;
+ // otherwise use the in-tree SPIR-V backend via clang.
+ // Use sys::findProgramByName() instead of findProgram() to avoid the
+ // dry-run fallback that always "finds" programs by returning their name.
+ bool UseLLVMSpirvTranslator = false;
+ std::string LLVMSpirvPathStr;
+ {
+ ErrorOr<std::string> LLVMSpirvPath =
+ sys::findProgramByName("llvm-spirv", {getExecutableDir("llvm-spirv")});
+ if (!LLVMSpirvPath)
+ LLVMSpirvPath = sys::findProgramByName("llvm-spirv");
+ if (LLVMSpirvPath) {
+ LLVMSpirvPathStr = *LLVMSpirvPath;
+ UseLLVMSpirvTranslator = true;
+ }
+ }
+ if (UseLLVMSpirvTranslator) {
+ // Use llvm-spirv translator: BC → SPIR-V binary directly.
+ auto SpirvOutOrErr = createOutputFile(
+ sys::path::filename(ExecutableName) + ".spirv", "spv");
+ if (!SpirvOutOrErr)
+ return SpirvOutOrErr.takeError();
+
+ // Derive SPIR-V max version from the triple's sub-arch.
+ // chipStar needs v1.2 for sub-group extensions by default.
+ std::string MaxVerArg;
+ if (Triple.getSubArch() == llvm::Triple::SPIRVSubArch_v13)
+ MaxVerArg = "--spirv-max-version=1.3";
+ else if (Triple.getSubArch() == llvm::Triple::SPIRVSubArch_v12 ||
+ Triple.getOS() == llvm::Triple::ChipStar)
+ MaxVerArg = "--spirv-max-version=1.2";
+ else
+ MaxVerArg = "--spirv-max-version=1.1";
+
+ SmallVector<StringRef, 16> TranslateArgs{
+ LLVMSpirvPathStr,
+ OptOutputFile,
+ Args.MakeArgString(MaxVerArg),
+ "--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups",
+ "-o",
+ *SpirvOutOrErr,
+ };
+
+ if (Error Err = executeCommands(LLVMSpirvPathStr, TranslateArgs))
+ return std::move(Err);
+
+ // The SPIR-V binary is the final output; skip the inner clang
+ // compilation by returning it directly as the linked image.
+ return *SpirvOutOrErr;
+ }
+
+ // No llvm-spirv available; use the in-tree SPIR-V backend via clang.
+ ProcessedInputFiles.push_back(OptOutputFile);
+ CmdArgs.push_back("-mllvm");
+ CmdArgs.push_back("-spirv-ext=+SPV_INTEL_function_pointers"
+ ",+SPV_INTEL_subgroups"
+ ",+SPV_EXT_relaxed_printf_string_address_space"
+ ",+SPV_KHR_bit_instructions"
+ ",+SPV_EXT_shader_atomic_float_add");
+ // The extracted bitcode files have a .o extension which causes the driver
+ // to treat them as pre-compiled objects, skipping the Backend compilation
+ // step. Force the input language to LLVM IR so the SPIR-V backend runs.
+ // Use -c to skip the link phase — the SPIR-V backend output is the final
+ // binary; hitting HIPSPV::Linker would re-run the full pipeline.
+ CmdArgs.push_back("-c");
+ CmdArgs.push_back("-x");
+ CmdArgs.push_back("ir");
+ } else {
+ ProcessedInputFiles.append(InputFiles.begin(), InputFiles.end());
+ }
+
+ for (StringRef InputFile : ProcessedInputFiles)
CmdArgs.push_back(InputFile);
// If this is CPU offloading we copy the input libraries.
@@ -613,8 +786,14 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args,
for (StringRef Arg : Args.getA...
[truncated]
|
|
@llvm/pr-subscribers-backend-spir-v Author: Paulius Velesko (pvelesko) ChangesSummarychipStar (https://github.com/CHIP-SPV/chipStar) enables HIP/CUDA programs to run on OpenCL and Level Zero devices via SPIR-V. Until now, the HIPSPV toolchain relied exclusively on the external This patch adds native in-tree SPIR-V backend support for chipStar targets ( ChangesHIPSPV old driver (
New offload driver (
SPIR-V toolchain (
SPIR-V backend (
Test plan
Patch is 21.95 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/186972.diff 6 Files Affected:
diff --git a/clang/include/clang/Basic/AlignedAllocation.h b/clang/include/clang/Basic/AlignedAllocation.h
index ac26eb4a276da..9b84d07286d52 100644
--- a/clang/include/clang/Basic/AlignedAllocation.h
+++ b/clang/include/clang/Basic/AlignedAllocation.h
@@ -35,6 +35,8 @@ inline llvm::VersionTuple alignedAllocMinVersion(llvm::Triple::OSType OS) {
return llvm::VersionTuple(4U);
case llvm::Triple::ZOS:
return llvm::VersionTuple(); // All z/OS versions have no support.
+ case llvm::Triple::ChipStar:
+ return llvm::VersionTuple(); // No version constraint for device targets.
}
llvm_unreachable("Unexpected OS");
diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index 8bdb7ab042b2b..70f831c3ade87 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -72,10 +72,56 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
tools::constructLLVMLinkCommand(C, *this, JA, Inputs, LinkArgs, Output, Args,
TempFile);
- // Post-link HIP lowering.
+ auto T = getToolChain().getTriple();
+
+ if (T.getOS() == llvm::Triple::ChipStar) {
+ // chipStar: run HipSpvPasses via opt, then use the in-tree SPIR-V backend
+ // for codegen (replaces the external llvm-spirv translator).
+
+ // Run HipSpvPasses plugin via opt (must run on LLVM IR before
+ // the SPIR-V backend lowers to MIR).
+ auto PassPluginPath = findPassPlugin(C.getDriver(), Args);
+ if (!PassPluginPath.empty()) {
+ const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath);
+ const char *OptOutput = HIP::getTempFile(C, Name + "-lower", "bc");
+ ArgStringList OptArgs{TempFile, "-load-pass-plugin",
+ PassPathCStr, "-passes=hip-post-link-passes",
+ "-o", OptOutput};
+ const char *Opt =
+ Args.MakeArgString(getToolChain().GetProgramPath("opt"));
+ C.addCommand(std::make_unique<Command>(
+ JA, *this, ResponseFileSupport::None(), Opt, OptArgs, Inputs,
+ Output));
+ TempFile = OptOutput;
+ }
- // Run LLVM IR passes to lower/expand/emulate HIP code that does not translate
- // to SPIR-V (E.g. dynamic shared memory).
+ // Compile processed bitcode to SPIR-V using the in-tree backend.
+ ArgStringList ClangArgs;
+ ClangArgs.push_back("--no-default-config");
+ ClangArgs.push_back("-c");
+ ClangArgs.push_back(
+ C.getArgs().MakeArgString("--target=" + T.getTriple()));
+
+ ClangArgs.push_back("-mllvm");
+ ClangArgs.push_back("-spirv-ext=+SPV_INTEL_function_pointers"
+ ",+SPV_INTEL_subgroups"
+ ",+SPV_EXT_relaxed_printf_string_address_space"
+ ",+SPV_KHR_bit_instructions"
+ ",+SPV_EXT_shader_atomic_float_add");
+
+ ClangArgs.push_back(TempFile);
+ ClangArgs.push_back("-o");
+ ClangArgs.push_back(Output.getFilename());
+
+ const char *Clang =
+ C.getArgs().MakeArgString(C.getDriver().getClangProgramPath());
+ C.addCommand(std::make_unique<Command>(
+ JA, *this, ResponseFileSupport::None(), Clang, ClangArgs, Inputs,
+ Output));
+ return;
+ }
+
+ // Non-chipStar: run HIP passes via opt, then translate with llvm-spirv.
auto PassPluginPath = findPassPlugin(C.getDriver(), Args);
if (!PassPluginPath.empty()) {
const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath);
@@ -89,27 +135,11 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
TempFile = OptOutput;
}
- // Emit SPIR-V binary.
+ // Emit SPIR-V binary via llvm-spirv translator (non-chipStar targets).
llvm::opt::ArgStringList TrArgs;
- auto T = getToolChain().getTriple();
- bool HasNoSubArch = T.getSubArch() == llvm::Triple::NoSubArch;
- if (T.getOS() == llvm::Triple::ChipStar) {
- // chipStar needs 1.2 for supporting warp-level primitivies via sub-group
- // extensions. Strictly put we'd need 1.3 for the standard non-extension
- // shuffle operations, but it's not supported by any backend driver of the
- // chipStar.
- if (HasNoSubArch)
- TrArgs.push_back("--spirv-max-version=1.2");
- TrArgs.push_back("--spirv-ext=-all"
- // Needed for experimental indirect call support.
- ",+SPV_INTEL_function_pointers"
- // Needed for shuffles below SPIR-V 1.3
- ",+SPV_INTEL_subgroups");
- } else {
- if (HasNoSubArch)
- TrArgs.push_back("--spirv-max-version=1.1");
- TrArgs.push_back("--spirv-ext=+all");
- }
+ if (T.getSubArch() == llvm::Triple::NoSubArch)
+ TrArgs.push_back("--spirv-max-version=1.1");
+ TrArgs.push_back("--spirv-ext=+all");
InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, "");
SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs);
@@ -152,14 +182,17 @@ HIPSPVToolChain::HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple,
void HIPSPVToolChain::addClangTargetOptions(
const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
Action::OffloadKind DeviceOffloadingKind) const {
-
if (!HostTC) {
assert(DeviceOffloadingKind == Action::OFK_None &&
"Need host toolchain for offloading!");
return;
}
- HostTC->addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind);
+ // NOTE: Unlike other HIP toolchains, we do NOT delegate to
+ // HostTC.addClangTargetOptions() here. On macOS (Darwin), the host toolchain
+ // adds flags like -faligned-alloc-unavailable that are specific to macOS
+ // libc++ and break SPIR-V device compilation. SPIR-V device code doesn't
+ // have the same stdlib limitations as the host.
assert(DeviceOffloadingKind == Action::OFK_HIP &&
"Only HIP offloading kinds are supported for GPUs.");
diff --git a/clang/lib/Driver/ToolChains/SPIRV.cpp b/clang/lib/Driver/ToolChains/SPIRV.cpp
index a59bd05cac0cf..e6a04e00af87b 100644
--- a/clang/lib/Driver/ToolChains/SPIRV.cpp
+++ b/clang/lib/Driver/ToolChains/SPIRV.cpp
@@ -185,7 +185,8 @@ SPIRVToolChain::SPIRVToolChain(const Driver &D, const llvm::Triple &Triple,
: ToolChain(D, Triple, Args) {
// TODO: Revisit need/use of --sycl-link option once SYCL toolchain is
// available and SYCL linking support is moved there.
- NativeLLVMSupport = Args.hasArg(options::OPT_sycl_link) || D.isUsingLTO();
+ NativeLLVMSupport = Args.hasArg(options::OPT_sycl_link) || D.isUsingLTO() ||
+ Triple.getOS() == llvm::Triple::ChipStar;
// Lookup binaries into the driver directory.
getProgramPaths().push_back(getDriver().Dir);
diff --git a/clang/test/Driver/hipspv-toolchain.hip b/clang/test/Driver/hipspv-toolchain.hip
index ae8d65313abfb..9af64ca4bdaa4 100644
--- a/clang/test/Driver/hipspv-toolchain.hip
+++ b/clang/test/Driver/hipspv-toolchain.hip
@@ -59,17 +59,50 @@
// RUN: llvm-offload-binary -o %t.dev.out \
// RUN: --image=file=%t.dev.bc,kind=hip,triple=spirv64-unknown-chipstar,arch=generic
-// RUN: clang-linker-wrapper --dry-run \
+// Test the in-tree SPIR-V backend path (no llvm-spirv available).
+// Run from a directory that doesn't contain llvm-spirv and use
+// --no-canonical-prefixes so getExecutableDir() looks there instead of
+// the build bin dir. Empty PATH ensures PATH lookup also fails.
+// RUN: mkdir -p %t/no-spirv %t/empty
+// RUN: ln -sf clang-linker-wrapper %t/no-spirv/clang-linker-wrapper
+// RUN: env "PATH=%t/empty" %t/no-spirv/clang-linker-wrapper \
+// RUN: --no-canonical-prefixes --dry-run \
// RUN: --device-compiler=spirv64-unknown-chipstar=--hip-path="%S/Inputs/hipspv" \
// RUN: --host-triple=spirv64-unknown-chipstar \
// RUN: --linker-path=clang-offload-bundler \
// RUN: --emit-fatbin-only -o /dev/null %t.dev.out \
// RUN: 2>&1 | FileCheck %s --check-prefix=WRAPPER -DHIP_PATH=%S/Inputs/hipspv
-// WRAPPER: clang{{.*}}" --no-default-config -o {{[^ ]*.img}}
+// The linker wrapper runs opt (HipSpvPasses) then uses the in-tree SPIR-V
+// backend when llvm-spirv is not available.
+// WRAPPER: "{{.*}}opt" {{.*}}-load-pass-plugin
+// WRAPPER-SAME: {{.*}}libLLVMHipSpvPasses.so
+// WRAPPER-SAME: -passes=hip-post-link-passes
+
+// WRAPPER: "{{.*}}clang{{.*}}" --no-default-config -o
// WRAPPER-SAME: --target=spirv64-unknown-chipstar
-// WRAPPER-SAME: {{[^ ]*.o}}
-// WRAPPER-SAME: --hip-path=[[HIP_PATH]]
+// WRAPPER-SAME: -mllvm -spirv-ext=+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_EXT_relaxed_printf_string_address_space,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add
+// WRAPPER-SAME: -c -x ir
+
+// Test the llvm-spirv translator path (llvm-spirv available in executable dir).
+// Place a fake llvm-spirv next to the clang-linker-wrapper symlink.
+// RUN: mkdir -p %t/with-spirv
+// RUN: ln -sf clang-linker-wrapper %t/with-spirv/clang-linker-wrapper
+// RUN: touch %t/with-spirv/llvm-spirv && chmod +x %t/with-spirv/llvm-spirv
+// RUN: env "PATH=%t/empty" %t/with-spirv/clang-linker-wrapper \
+// RUN: --no-canonical-prefixes --dry-run \
+// RUN: --device-compiler=spirv64-unknown-chipstar=--hip-path="%S/Inputs/hipspv" \
+// RUN: --host-triple=spirv64-unknown-chipstar \
+// RUN: --linker-path=clang-offload-bundler \
+// RUN: --emit-fatbin-only -o /dev/null %t.dev.out \
+// RUN: 2>&1 | FileCheck %s --check-prefix=WRAPPER-TR
+
+// WRAPPER-TR: "{{.*}}opt" {{.*}}-load-pass-plugin
+// WRAPPER-TR-SAME: {{.*}}libLLVMHipSpvPasses.so
+// WRAPPER-TR-SAME: -passes=hip-post-link-passes
+
+// WRAPPER-TR: "{{.*}}llvm-spirv" {{.*}}--spirv-max-version=1.2
+// WRAPPER-TR-SAME: --spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups
// RUN: touch %t.dummy.o
// RUN: %clang -### --no-default-config -o %t.dummy.img \
@@ -84,8 +117,9 @@
// CHIPSTAR-SAME: "[[HIP_PATH]]/lib/libLLVMHipSpvPasses.so"
// CHIPSTAR-SAME: "-passes=hip-post-link-passes" "-o" [[LOWER_BC:".*bc"]]
-// CHIPSTAR: {{".*llvm-spirv"}} "--spirv-max-version=1.2"
-// CHIPSTAR-SAME: "--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups"
+// CHIPSTAR: {{".*clang.*"}} "--no-default-config" "-c"
+// CHIPSTAR-SAME: "--target=spirv64-unknown-chipstar"
+// CHIPSTAR-SAME: "-mllvm" "-spirv-ext=+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_EXT_relaxed_printf_string_address_space,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add"
// CHIPSTAR-SAME: [[LOWER_BC]] "-o" "[[SPIRV_OUT:.*img]]"
// RUN: %clang -### --no-default-config -o %t.dummy.img \
@@ -100,8 +134,9 @@
// CHIPSTAR-SUBARCH-SAME: "[[HIP_PATH]]/lib/libLLVMHipSpvPasses.so"
// CHIPSTAR-SUBARCH-SAME: "-passes=hip-post-link-passes" "-o" [[LOWER_BC:".*bc"]]
-// CHIPSTAR-SUBARCH: {{".*llvm-spirv"}}
-// CHIPSTAR-SUBARCH-SAME: "--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups"
+// CHIPSTAR-SUBARCH: {{".*clang.*"}} "--no-default-config" "-c"
+// CHIPSTAR-SUBARCH-SAME: "--target=spirv64v1.3-unknown-chipstar"
+// CHIPSTAR-SUBARCH-SAME: "-mllvm" "-spirv-ext=+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_EXT_relaxed_printf_string_address_space,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add"
// CHIPSTAR-SUBARCH-SAME: [[LOWER_BC]] "-o" "[[SPIRV_OUT:.*img]]"
//-----------------------------------------------------------------------------
@@ -115,9 +150,4 @@
// RUN: | FileCheck -DVERSION=%llvm-version-major \
// RUN: --check-prefix=VERSIONED %s
-// RUN: env "PATH=%t/versioned" %clang -### --no-default-config \
-// RUN: -o %t.dummy.img --target=spirv64-unknown-chipstar %t.dummy.o \
-// RUN: --hip-path="%S/Inputs/hipspv" -o /dev/null 2>&1 \
-// RUN: | FileCheck -DVERSION=%llvm-version-major --check-prefix=VERSIONED %s
-
// VERSIONED: {{.*}}llvm-spirv-[[VERSION]]
diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
index 9e24a9c26d897..2522d0ac71df1 100644
--- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
+++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
@@ -125,6 +125,9 @@ static StringRef ExecutableName;
/// Binary path for the CUDA installation.
static std::string CudaBinaryPath;
+/// HIP installation path.
+static std::string HipPath;
+
/// Mutex lock to protect writes to shared TempFiles in parallel.
static std::mutex TempFilesMutex;
@@ -479,9 +482,25 @@ fatbinary(ArrayRef<std::tuple<StringRef, StringRef, StringRef>> InputFiles,
SmallVector<StringRef> Targets = {
Saver.save("-targets=host-" + HostTriple.normalize())};
for (const auto &[File, TripleRef, Arch] : InputFiles) {
- std::string NormalizedTriple =
- normalizeForBundler(Triple(TripleRef), !Arch.empty());
- Targets.push_back(Saver.save("hip-" + NormalizedTriple + "-" + Arch));
+ llvm::Triple T(TripleRef);
+ // For SPIR-V targets, derive arch from triple if not provided
+ StringRef EffectiveArch = Arch;
+ if (EffectiveArch.empty() && T.isSPIRV()) {
+ EffectiveArch = T.getArchName();
+ }
+ StringRef BundleID;
+ if (EffectiveArch == "amdgcnspirv") {
+ BundleID = Saver.save("hip-spirv64-amd-amdhsa--" + EffectiveArch);
+ } else if (T.isSPIRV()) {
+ // ChipStar and other SPIR-V HIP targets: use hip-spirv64-<vendor>-<os>--<arch>
+ BundleID = Saver.save("hip-spirv64-" + T.getVendorName() + "-" +
+ T.getOSName() + "--" + EffectiveArch);
+ } else {
+ std::string NormalizedTriple =
+ normalizeForBundler(T, !Arch.empty());
+ BundleID = Saver.save("hip-" + NormalizedTriple + "-" + Arch);
+ }
+ Targets.push_back(BundleID);
}
CmdArgs.push_back(Saver.save(llvm::join(Targets, ",")));
@@ -554,7 +573,161 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args,
if (!Triple.isNVPTX() && !Triple.isSPIRV())
CmdArgs.push_back("-Wl,--no-undefined");
- for (StringRef InputFile : InputFiles)
+ // For non-chipStar SPIR-V targets, pass the HIP path to clang so it can
+ // find resources. For chipStar, passes are run via opt separately, so the
+ // inner clang doesn't need --hip-path (it just compiles IR to SPIR-V).
+ if (Triple.isSPIRV() && !HipPath.empty() &&
+ Triple.getOS() != llvm::Triple::ChipStar)
+ CmdArgs.push_back(Args.MakeArgString("--hip-path=" + HipPath));
+
+ // For chipStar targets: llvm-link (merge) → opt (HipSpvPasses) → clang
+ // (SPIR-V backend). The passes must operate on LLVM IR before the backend
+ // lowers to MIR, and all TU bitcode must be merged first for RDC support.
+ SmallVector<StringRef, 16> ProcessedInputFiles;
+ if (Triple.isSPIRV() && Triple.getOS() == llvm::Triple::ChipStar) {
+ // Step 1: Merge all input bitcode files with llvm-link (needed for RDC
+ // where functions can be defined across translation units).
+ StringRef MergedFile;
+ if (InputFiles.size() > 1) {
+ Expected<std::string> LinkPath =
+ findProgram("llvm-link", {getExecutableDir("llvm-link")});
+ if (!LinkPath)
+ return LinkPath.takeError();
+
+ auto LinkOutOrErr = createOutputFile(
+ sys::path::filename(ExecutableName) + ".merged", "bc");
+ if (!LinkOutOrErr)
+ return LinkOutOrErr.takeError();
+
+ SmallVector<StringRef, 16> LinkArgs{*LinkPath};
+ for (StringRef F : InputFiles)
+ LinkArgs.push_back(F);
+ LinkArgs.push_back("-o");
+ LinkArgs.push_back(*LinkOutOrErr);
+
+ if (Error Err = executeCommands(*LinkPath, LinkArgs))
+ return std::move(Err);
+
+ MergedFile = *LinkOutOrErr;
+ } else {
+ MergedFile = InputFiles[0];
+ }
+
+ // Step 2: Run HipSpvPasses via opt on the merged bitcode.
+ SmallString<128> PluginPath;
+ if (!HipPath.empty()) {
+ PluginPath.assign(HipPath);
+ sys::path::append(PluginPath, "lib", "libLLVMHipSpvPasses.so");
+ if (!sys::fs::exists(PluginPath)) {
+ PluginPath.assign(HipPath);
+ sys::path::append(PluginPath, "lib", "llvm",
+ "libLLVMHipSpvPasses.so");
+ }
+ if (!sys::fs::exists(PluginPath))
+ PluginPath.clear();
+ }
+
+ StringRef OptOutputFile = MergedFile;
+ if (!PluginPath.empty()) {
+ Expected<std::string> OptPath =
+ findProgram("opt", {getExecutableDir("opt")});
+ if (!OptPath)
+ return OptPath.takeError();
+
+ auto OptOutOrErr = createOutputFile(
+ sys::path::filename(ExecutableName) + ".lowered", "bc");
+ if (!OptOutOrErr)
+ return OptOutOrErr.takeError();
+
+ SmallVector<StringRef, 16> OptArgs{
+ *OptPath,
+ MergedFile,
+ "-load-pass-plugin",
+ Args.MakeArgString(PluginPath),
+ "-passes=hip-post-link-passes",
+ "-o",
+ *OptOutOrErr,
+ };
+
+ if (Error Err = executeCommands(*OptPath, OptArgs))
+ return std::move(Err);
+
+ OptOutputFile = *OptOutOrErr;
+ }
+
+ // Step 3: Convert processed bitcode to SPIR-V.
+ // Check if llvm-spirv translator is available. If so, use it directly;
+ // otherwise use the in-tree SPIR-V backend via clang.
+ // Use sys::findProgramByName() instead of findProgram() to avoid the
+ // dry-run fallback that always "finds" programs by returning their name.
+ bool UseLLVMSpirvTranslator = false;
+ std::string LLVMSpirvPathStr;
+ {
+ ErrorOr<std::string> LLVMSpirvPath =
+ sys::findProgramByName("llvm-spirv", {getExecutableDir("llvm-spirv")});
+ if (!LLVMSpirvPath)
+ LLVMSpirvPath = sys::findProgramByName("llvm-spirv");
+ if (LLVMSpirvPath) {
+ LLVMSpirvPathStr = *LLVMSpirvPath;
+ UseLLVMSpirvTranslator = true;
+ }
+ }
+ if (UseLLVMSpirvTranslator) {
+ // Use llvm-spirv translator: BC → SPIR-V binary directly.
+ auto SpirvOutOrErr = createOutputFile(
+ sys::path::filename(ExecutableName) + ".spirv", "spv");
+ if (!SpirvOutOrErr)
+ return SpirvOutOrErr.takeError();
+
+ // Derive SPIR-V max version from the triple's sub-arch.
+ // chipStar needs v1.2 for sub-group extensions by default.
+ std::string MaxVerArg;
+ if (Triple.getSubArch() == llvm::Triple::SPIRVSubArch_v13)
+ MaxVerArg = "--spirv-max-version=1.3";
+ else if (Triple.getSubArch() == llvm::Triple::SPIRVSubArch_v12 ||
+ Triple.getOS() == llvm::Triple::ChipStar)
+ MaxVerArg = "--spirv-max-version=1.2";
+ else
+ MaxVerArg = "--spirv-max-version=1.1";
+
+ SmallVector<StringRef, 16> TranslateArgs{
+ LLVMSpirvPathStr,
+ OptOutputFile,
+ Args.MakeArgString(MaxVerArg),
+ "--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups",
+ "-o",
+ *SpirvOutOrErr,
+ };
+
+ if (Error Err = executeCommands(LLVMSpirvPathStr, TranslateArgs))
+ return std::move(Err);
+
+ // The SPIR-V binary is the final output; skip the inner clang
+ // compilation by returning it directly as the linked image.
+ return *SpirvOutOrErr;
+ }
+
+ // No llvm-spirv available; use the in-tree SPIR-V backend via clang.
+ ProcessedInputFiles.push_back(OptOutputFile);
+ CmdArgs.push_back("-mllvm");
+ CmdArgs.push_back("-spirv-ext=+SPV_INTEL_function_pointers"
+ ",+SPV_INTEL_subgroups"
+ ",+SPV_EXT_relaxed_printf_string_address_space"
+ ",+SPV_KHR_bit_instructions"
+ ",+SPV_EXT_shader_atomic_float_add");
+ // The extracted bitcode files have a .o extension which causes the driver
+ // to treat them as pre-compiled objects, skipping the Backend compilation
+ // step. Force the input language to LLVM IR so the SPIR-V backend runs.
+ // Use -c to skip the link phase — the SPIR-V backend output is the final
+ // binary; hitting HIPSPV::Linker would re-run the full pipeline.
+ CmdArgs.push_back("-c");
+ CmdArgs.push_back("-x");
+ CmdArgs.push_back("ir");
+ } else {
+ ProcessedInputFiles.append(InputFiles.begin(), InputFiles.end());
+ }
+
+ for (StringRef InputFile : ProcessedInputFiles)
CmdArgs.push_back(InputFile);
// If this is CPU offloading we copy the input libraries.
@@ -613,8 +786,14 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args,
for (StringRef Arg : Args.getA...
[truncated]
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
ed3a552 to
bbae359
Compare
|
The Test SPIR-V CI failure is pre-existing on main — confirmed by #186992 (no-op baseline PR on main that shows the same 5 pointer/SpecConstantOp test failures). |
bbae359 to
5b7d5d7
Compare
5b7d5d7 to
a4a5227
Compare
…rapper Address review feedback on llvm#186972: the chipStar SPIR-V pipeline (llvm-link -> opt(HipSpvPasses) -> SPIR-V backend) was duplicated inside ClangLinkerWrapper.cpp. The same pipeline already lives in HIPSPV::Linker::constructLinkAndEmitSpirvCommand and runs whenever an inner clang is invoked with --target=spirv64*-unknown-chipstar. Drop the wrapper-side duplication and let the inner clang's HIPSPV toolchain do the work: - Remove the global HipPath and its extraction in main(); --hip-path is already forwarded from --device-compiler= via the existing OPT_compiler_arg_EQ channel and reaches the inner clang automatically. - Remove the chipStar-specific llvm-link/opt/SPIR-V emission block from the linker-wrapper clang() helper. - Remove the unconditional --hip-path push for non-chipStar SPIR-V targets; no consumer of that branch existed. - Remove the chipStar --hip-path filter in the compiler_arg loop, restoring the simple forwarding loop. Tests: - hipspv-toolchain.hip: collapse the WRAPPER / WRAPPER-TR runs into a single WRAPPER run that verifies the wrapper invokes inner clang with --target, --hip-path, and the input objects. The opt + in-tree-backend pipeline is already covered by the CHIPSTAR / CHIPSTAR-SUBARCH driver runs that hit HIPSPV::Linker directly. - hipspv-link-static-library.hip: same simplification of SDL-NEW-WRAPPER. Net change: -203 lines in the wrapper, no behavioral regression. All clang/test/Driver lit tests pass (1250/1250 modulo the one pre-existing expected failure).
| // hip-spirv64-<vendor>-<os>--<arch> | ||
| BundleID = Saver.save("hip-spirv64-" + T.getVendorName() + "-" + | ||
| T.getOSName() + "--" + EffectiveArch); |
There was a problem hiding this comment.
I'm lost here - does this fix a breakage for chipStar? I have tested CHIP-SPV/chipStar#1219 locally on couple days old LLVM and I haven't seen issues. I'm wondering if it necessary to have this.
| // For SPIR-V targets, derive arch from triple if not provided | ||
| StringRef EffectiveArch = Arch; | ||
| if (EffectiveArch.empty() && T.isSPIRV()) { | ||
| EffectiveArch = T.getArchName(); |
There was a problem hiding this comment.
I think CPU/GPU model is expected here which is not same thing as what Triple::getArchName() returns. For SPIR-V I think we can set it to "generic" - that's the default model used elsewhere for SPIR-V targets.
There was a problem hiding this comment.
Also, is it a problem if the Arch is empty for non-chipStar SPIR-V targets?
| case llvm::Triple::ChipStar: | ||
| return llvm::VersionTuple(); // No version constraint for device targets. |
There was a problem hiding this comment.
IIUC, this function is related to availability of std::aligned_alloc(). I'm wondering what this has to do with HIP. Like does HIP language support std::aligned_alloc() to be called from device code?
| for (const auto &Input : Inputs) | ||
| if (Input.isFilename()) | ||
| LinkArgs.push_back(Input.getFilename()); |
There was a problem hiding this comment.
This change got introduced in #187655 so a rebase maybe needed before merge.
| const char *Clang = | ||
| C.getArgs().MakeArgString(C.getDriver().getClangProgramPath()); |
There was a problem hiding this comment.
Using clang driver for emitting SPIR-V (through SPIR-V BE) seems hazarous - it think it's better to use cc1 like HIPAMD does.
| // Run HipSpvPasses plugin via opt (must run on LLVM IR before | ||
| // the SPIR-V backend lowers to MIR). | ||
| auto PassPluginPath = findPassPlugin(C.getDriver(), Args); | ||
| if (!PassPluginPath.empty()) { | ||
| const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath); | ||
| const char *OptOutput = HIP::getTempFile(C, Name + "-lower", "bc"); | ||
| ArgStringList OptArgs{TempFile, "-load-pass-plugin", | ||
| PassPathCStr, "-passes=hip-post-link-passes", | ||
| "-o", OptOutput}; | ||
| const char *Opt = | ||
| Args.MakeArgString(getToolChain().GetProgramPath("opt")); | ||
| C.addCommand(std::make_unique<Command>(JA, *this, | ||
| ResponseFileSupport::None(), Opt, | ||
| OptArgs, Inputs, Output)); | ||
| TempFile = OptOutput; | ||
| } |
There was a problem hiding this comment.
This portion is a duplicate from the below - could you refactor the duplication away?
| @@ -91,7 +91,8 @@ SPIRVSubtarget::SPIRVSubtarget(const Triple &TT, const std::string &CPU, | |||
| if (TargetTriple.getOS() == Triple::Vulkan) | |||
| Env = Shader; | |||
| else if (TargetTriple.getOS() == Triple::OpenCL || | |||
| TargetTriple.getVendor() == Triple::AMD) | |||
| TargetTriple.getVendor() == Triple::AMD || | |||
| TargetTriple.getOS() == Triple::ChipStar) | |||
There was a problem hiding this comment.
This if fine for now. I just want to comment that I regret making the "ChipStar" an OS component but it doesn't matter unless/until chipStar adds a driver backend that requires Vulkan or other SPIR-V environment. It would have been better if it were a vendor component instead to differentiate the target environment - something like "spirv64-chipstar-vulkan".
There was a problem hiding this comment.
doesn't matter unless/until chipStar adds a driver backend that requires Vulkan
I have a branch that converts our generated SPIRV from OpenCL to Vulkan SPIRV. This is for simplifying integration with clvk - converting SPIRV to Vulkan SPIRV on our side allows us to bypass the clspv component of clvk.
Not sure if this is something that will ever get merged or how that would affect llvm if so.
29d34a7 to
23c5b7b
Compare
…rapper Address review feedback on llvm#186972: the chipStar SPIR-V pipeline (llvm-link -> opt(HipSpvPasses) -> SPIR-V backend) was duplicated inside ClangLinkerWrapper.cpp. The same pipeline already lives in HIPSPV::Linker::constructLinkAndEmitSpirvCommand and runs whenever an inner clang is invoked with --target=spirv64*-unknown-chipstar. Drop the wrapper-side duplication and let the inner clang's HIPSPV toolchain do the work: - Remove the global HipPath and its extraction in main(); --hip-path is already forwarded from --device-compiler= via the existing OPT_compiler_arg_EQ channel and reaches the inner clang automatically. - Remove the chipStar-specific llvm-link/opt/SPIR-V emission block from the linker-wrapper clang() helper. - Remove the unconditional --hip-path push for non-chipStar SPIR-V targets; no consumer of that branch existed. - Remove the chipStar --hip-path filter in the compiler_arg loop, restoring the simple forwarding loop. Tests: - hipspv-toolchain.hip: collapse the WRAPPER / WRAPPER-TR runs into a single WRAPPER run that verifies the wrapper invokes inner clang with --target, --hip-path, and the input objects. The opt + in-tree-backend pipeline is already covered by the CHIPSTAR / CHIPSTAR-SUBARCH driver runs that hit HIPSPV::Linker directly. - hipspv-link-static-library.hip: same simplification of SDL-NEW-WRAPPER. Net change: -203 lines in the wrapper, no behavioral regression. All clang/test/Driver lit tests pass (1250/1250 modulo the one pre-existing expected failure).
23c5b7b to
2829c67
Compare
chipStar (https://github.com/CHIP-SPV/chipStar) enables HIP/CUDA programs to run on OpenCL and Level Zero devices via SPIR-V. Until now, the HIPSPV toolchain relied exclusively on the external llvm-spirv translator for bitcode-to-SPIR-V conversion. This patch adds native in-tree SPIR-V backend support for chipStar targets (triple: spirv64*-unknown-chipstar), removing the hard dependency on llvm-spirv. Changes: HIPSPV old driver (HIPSPV.cpp): - chipStar targets now use opt (HipSpvPasses) + clang -c (SPIR-V backend) instead of opt + llvm-spirv translator - Non-chipStar HIPSPV targets continue using llvm-spirv unchanged - Remove HostTC->addClangTargetOptions() delegation to avoid macOS Darwin flags (-faligned-alloc-unavailable) breaking SPIR-V device compilation New offload driver (ClangLinkerWrapper.cpp): - Add chipStar SPIR-V pipeline: llvm-link -> opt (HipSpvPasses) -> clang -c --target=spirv64 with SPIR-V extensions - Extract --hip-path from --device-compiler= args for locating the HipSpvPasses plugin and device libraries - Fall back to llvm-spirv translator when available for non-chipStar SPIR-V targets SPIR-V toolchain (SPIRV.cpp): - Enable NativeLLVMSupport for chipStar triples so the toolchain does not require an external translator SPIR-V backend (SPIRVSubtarget.cpp): - Set Kernel environment for chipStar triples (needed for OpenCL kernel ABI) AlignedAllocation.h: - Add ChipStar case to avoid unhandled enum warning Tests: - Update hipspv-toolchain.hip driver test to verify the new in-tree backend pipeline for chipStar targets
…rapper Address review feedback on llvm#186972: the chipStar SPIR-V pipeline (llvm-link -> opt(HipSpvPasses) -> SPIR-V backend) was duplicated inside ClangLinkerWrapper.cpp. The same pipeline already lives in HIPSPV::Linker::constructLinkAndEmitSpirvCommand and runs whenever an inner clang is invoked with --target=spirv64*-unknown-chipstar. Drop the wrapper-side duplication and let the inner clang's HIPSPV toolchain do the work: - Remove the global HipPath and its extraction in main(); --hip-path is already forwarded from --device-compiler= via the existing OPT_compiler_arg_EQ channel and reaches the inner clang automatically. - Remove the chipStar-specific llvm-link/opt/SPIR-V emission block from the linker-wrapper clang() helper. - Remove the unconditional --hip-path push for non-chipStar SPIR-V targets; no consumer of that branch existed. - Remove the chipStar --hip-path filter in the compiler_arg loop, restoring the simple forwarding loop. Tests: - hipspv-toolchain.hip: collapse the WRAPPER / WRAPPER-TR runs into a single WRAPPER run that verifies the wrapper invokes inner clang with --target, --hip-path, and the input objects. The opt + in-tree-backend pipeline is already covered by the CHIPSTAR / CHIPSTAR-SUBARCH driver runs that hit HIPSPV::Linker directly. - hipspv-link-static-library.hip: same simplification of SDL-NEW-WRAPPER. Net change: -203 lines in the wrapper, no behavioral regression. All clang/test/Driver lit tests pass (1250/1250 modulo the one pre-existing expected failure).
When HIPSPV::Linker is reached via the new offload driver (the inner clang spawned by clang-linker-wrapper for chipStar SPIR-V emission), the InputInfoList passed to constructLinkAndEmitSpirvCommand may contain non-filename entries (Nothing or InputArg placeholders) alongside the real bitcode inputs. The pre-existing loop called getFilename() unconditionally on every Input, which trips the assert in debug builds and reads garbage from the InputInfo union in release builds. The garbage was then forwarded to llvm-link as an input filename, producing errors like: llvm-link: No such file or directory: '<random bytes>' clang: error: hipspv-link command failed with exit code 1 This bug existed prior to the in-tree SPIR-V backend changes but was latent because the old (--no-offload-new-driver) HIPSPV path always produced a filename-only InputInfoList. The new-driver delegation in "[HIPSPV] Delegate chipStar SPIR-V emission to inner clang in linker wrapper" makes the same code reachable via the new driver and surfaces the bug. Filter the loop on isFilename(), matching the canonical pattern used elsewhere in the driver (e.g. CommonArgs.cpp line 1014 in the LTO link helper). Verified by reproducing the failure with a single-file chipStar HIP compile against the new offload driver, applying the fix, and confirming the same compile succeeds end-to-end. clang/test/Driver lit tests still pass.
The chipstar OS branch in HIPSPV::Linker::constructLinkAndEmitSpirvCommand unconditionally invoked the in-tree SPIR-V backend via 'clang -mllvm -spirv-ext=...'. On LLVM builds without the SPIRV target (translator-only configurations) this aborts with "Unknown command line argument '-spirv-ext='" because the backend's options are never registered. Probe for an llvm-spirv binary first (toolchain dir, then PATH) and, when found, translate bitcode to SPIR-V via SPIRV::constructTranslateCommand with --spirv-max-version derived from the triple's subarch — matching the fallback already implemented in clang-linker-wrapper. The in-tree backend remains the path used when llvm-spirv is unavailable. Restores --offload-device-only -c to working state for chipStar builds that ship the external translator.
The SPIR-V VariadicABIInfo::ignoreFunction already skips OpenCL printf so
the backend can emit OpExtInst printf with inline arguments, but it only
matched the demangled spelling "printf(" (the C++/OpenCL mangled form).
An unmangled C printf declaration demangles to bare "printf" with no
argument list, so it slipped through and ExpandVariadics packed its
arguments into a vararg buffer. The backend then passed the buffer
pointer as printf's first variadic operand, so device printf printed
pointer values instead of the actual arguments.
Match the bare function name as well so OpenCL/HIP printf (emitted
unmangled) is left intact for the OpenCL.std printf lowering.
2829c67 to
c43f22b
Compare
constructLinkAndEmitSpirvCommand has two SPIR-V emission paths: the external llvm-spirv translator (used when the binary is found next to the toolchain) and the in-tree SPIR-V backend fallback. The fallback already allows SPV_EXT_relaxed_printf_string_address_space (and SPV_EXT_shader_atomic_float_add), but the external translator path did not. On LLVM 23 with the external translator present, any HIP module whose printf format string is not in the constant address space (e.g. device-side assert, dynamic %s lowering) failed translation with: Either SPV_EXT_relaxed_printf_string_address_space extension should be allowed to translate this module ... clang: error: hipspv-link command failed with exit code 18 Add the two extensions to the external translator ext list so both paths behave identically.
…ffload Darwin::ensureTargetInitialized() (the lazy target-init helper added for the HIP-SPIR-V offload toolchain) called setTarget() with the raw triple OS version from getOSVersion(). On macOS that returns the Darwin *kernel* version (e.g. 24.3.0) rather than the macOS product version (e.g. 15.3.0). When clang later builds the offload host job, AddDeploymentTarget() calls setTarget() again with the real macOS version. setTarget()'s reinit guard only short-circuits when the versions match; kernel 24.3.0 != macOS 15.3.0, so it falls through to assert(!TargetInitialized) and clang crashes: Assertion failed: (!TargetInitialized && "Target already initialized!"), function setTarget, file Darwin.h, line 453. This crashes every '-x hip --offload=spirv64* --target=arm64-apple-darwin' compile on macOS, independent of the offload driver (--no-offload-new-driver does not help). Convert MacOS kernel versions via getMacOSXVersion() so the lazy init sets the same version AddDeploymentTarget later uses, matching the behavior of chipStar's LLVM-22 macOS patch (0006-macos-hip-spirv-llvm22).
🐧 Linux x64 Test Results
Failed Tests(click on a test name to see its output) lldb-apilldb-api.python_api/run_locker/TestRunLocker.pyIf these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the |
Summary
chipStar (https://github.com/CHIP-SPV/chipStar) enables HIP/CUDA programs to run on OpenCL and Level Zero devices via SPIR-V. Until now, the HIPSPV toolchain relied exclusively on the external
llvm-spirvtranslator for bitcode-to-SPIR-V conversion.This patch adds native in-tree SPIR-V backend support for chipStar targets (
spirv64*-unknown-chipstar), removing the hard dependency onllvm-spirv.Changes
HIPSPV old driver (
HIPSPV.cpp):opt(HipSpvPasses) +clang -c(SPIR-V backend) instead ofopt+llvm-spirvllvm-spirvunchangedHostTC->addClangTargetOptions()delegation to avoid macOS Darwin flags (-faligned-alloc-unavailable) breaking SPIR-V device compilationNew offload driver (
ClangLinkerWrapper.cpp):llvm-link→opt(HipSpvPasses) →clang -c --target=spirv64with SPIR-V extensions--hip-pathfrom--device-compiler=args for locating the HipSpvPasses plugin and device librariesllvm-spirvtranslator when available for non-chipStar SPIR-V targetsSPIR-V toolchain (
SPIRV.cpp):NativeLLVMSupportfor chipStar triples so the toolchain does not require an external translatorSPIR-V backend (
SPIRVSubtarget.cpp):Kernelenvironment for chipStar triples (needed for OpenCL kernel ABI)AlignedAllocation.h:ChipStarcase to avoid unhandled enum warningTest plan
hipspv-toolchain.hipdriver test updated to verify the new in-tree backend pipelineExpandVariadicsregression in LLVM 23)