https://github.com/pvelesko updated 
https://github.com/llvm/llvm-project/pull/186972

>From ed3a55289dba8fd305d826ae2dab4f3c5145b1f2 Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Tue, 17 Mar 2026 09:55:28 +0200
Subject: [PATCH] [HIPSPV] Add in-tree SPIR-V backend support for chipStar
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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
---
 clang/include/clang/Basic/AlignedAllocation.h |   2 +
 clang/lib/Driver/ToolChains/HIPSPV.cpp        |  82 +++++---
 clang/lib/Driver/ToolChains/SPIRV.cpp         |   3 +-
 .../Driver/hipspv-link-static-library.hip     |   2 +-
 clang/test/Driver/hipspv-pass-plugin.hip      |  12 +-
 clang/test/Driver/hipspv-toolchain.hip        |  56 +++--
 .../ClangLinkerWrapper.cpp                    | 198 +++++++++++++++++-
 llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp      |   3 +-
 8 files changed, 307 insertions(+), 51 deletions(-)

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..f8497198c906f 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -72,10 +72,55 @@ 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;
+    }
+
+    // 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;
+  }
 
-  // Run LLVM IR passes to lower/expand/emulate HIP code that does not 
translate
-  // to SPIR-V (E.g. dynamic shared memory).
+  // 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 +134,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 +181,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-link-static-library.hip 
b/clang/test/Driver/hipspv-link-static-library.hip
index eb114ada49020..390fcdad36189 100644
--- a/clang/test/Driver/hipspv-link-static-library.hip
+++ b/clang/test/Driver/hipspv-link-static-library.hip
@@ -49,8 +49,8 @@
 // DELETE-SDL-NEW: "{{.*}}llvm-link" "-o" "{{.*}}.bc" "{{.*}}.o" "{{.*}}.o"
 
 // SDL-NEW-WRAPPER: clang{{.*}}" --no-default-config -o {{[^ ]*.img}}
-// SDL-NEW-WRAPPER-SAME: {{[^ ]*.o}} {{[^ ]*.o}}
 // SDL-NEW-WRAPPER-SAME: --hip-path=[[HIP_PATH]]
+// SDL-NEW-WRAPPER: {{[^ ]*.o}} {{[^ ]*.o}}
 
 // SDL: "{{.*}}opt"
 // SDL-SAME: "-load-pass-plugin" {{".*/hipspv/lib/libLLVMHipSpvPasses.so"}}
diff --git a/clang/test/Driver/hipspv-pass-plugin.hip 
b/clang/test/Driver/hipspv-pass-plugin.hip
index 3a0979ad6df01..5cf891e4c6114 100644
--- a/clang/test/Driver/hipspv-pass-plugin.hip
+++ b/clang/test/Driver/hipspv-pass-plugin.hip
@@ -16,23 +16,24 @@
 // RUN: --no-offload-new-driver -nogpuinc -nogpulib %s \
 // RUN: 2>&1 | FileCheck --check-prefixes=ALL,NO-PLUGIN %s
 
-// Run commands for the new offload driver:
+// Run commands for the new offload driver (chipStar uses in-tree SPIR-V
+// backend instead of llvm-spirv):
 
 // RUN: touch %t.dummy.o
 // RUN: %clang -### --no-default-config -o /dev/null 
--target=spirv64-unknown-chipstar \
 // RUN:   %t.dummy.o --hip-path=%S/Inputs/hipspv \
-// RUN: 2>&1 | FileCheck %s --check-prefixes=ALL,FROM-HIP-PATH
+// RUN: 2>&1 | FileCheck %s --check-prefixes=CHIPSTAR,FROM-HIP-PATH
 
 // RUN: %clang -### --no-default-config -o /dev/null 
--target=spirv64-unknown-chipstar \
 // RUN:   %t.dummy.o --hipspv-pass-plugin=%S/Inputs/pass-plugin.so \
-// RUN: 2>&1 | FileCheck %s --check-prefixes=ALL,FROM-OPTION
+// RUN: 2>&1 | FileCheck %s --check-prefixes=CHIPSTAR,FROM-OPTION
 
 // RUN: not %clang -### --no-default-config -o /dev/null 
--target=spirv64-unknown-chipstar \
 // RUN:   %t.dummy.o --hipspv-pass-plugin=foo.so \
-// RUN: 2>&1 | FileCheck %s --check-prefixes=ALL,FROM-OPTION-INVALID
+// RUN: 2>&1 | FileCheck %s --check-prefixes=CHIPSTAR,FROM-OPTION-INVALID
 
 // RUN: %clang -### --no-default-config -o /dev/null 
--target=spirv64-unknown-chipstar \
-// RUN:   %t.dummy.o 2>&1 | FileCheck %s --check-prefixes=ALL,NO-PLUGIN
+// RUN:   %t.dummy.o 2>&1 | FileCheck %s --check-prefixes=CHIPSTAR,NO-PLUGIN
 
 // FROM-HIP-PATH: {{".*opt"}} {{".*.bc"}} "-load-pass-plugin"
 // FROM-HIP-PATH-SAME: {{".*/Inputs/hipspv/lib/libLLVMHipSpvPasses.so"}}
@@ -42,3 +43,4 @@
 // NO-PLUGIN-NOT: {{".*opt"}} {{".*.bc"}} "-load-pass-plugin"
 // NO-PLUGIN-NOT: {{".*/Inputs/hipspv/lib/libLLVMHipSpvPasses.so"}}
 // ALL: {{".*llvm-spirv[^ ]*"}}
+// CHIPSTAR: {{".*clang.*"}} "--no-default-config" "-c"
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..ff1aa3d7bce54 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,160 @@ 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 +785,14 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, 
const ArgList &Args,
 
   for (StringRef Arg : Args.getAllArgValues(OPT_linker_arg_EQ))
     CmdArgs.append({"-Xlinker", Args.MakeArgString(Arg)});
-  for (StringRef Arg : Args.getAllArgValues(OPT_compiler_arg_EQ))
+  for (StringRef Arg : Args.getAllArgValues(OPT_compiler_arg_EQ)) {
+    // For chipStar, --hip-path is already handled by opt step above;
+    // passing it to the inner clang (which just does IR→SPIR-V) is unused.
+    if (Triple.isSPIRV() && Triple.getOS() == llvm::Triple::ChipStar &&
+        Arg.starts_with("--hip-path="))
+      continue;
     CmdArgs.push_back(Args.MakeArgString(Arg));
+  }
 
   if (Error Err = executeCommands(*ClangPath, CmdArgs))
     return std::move(Err);
@@ -1358,6 +1536,16 @@ int main(int Argc, char **Argv) {
   CudaBinaryPath = Args.getLastArgValue(OPT_cuda_path_EQ).str();
   CanonicalPrefixes = !Args.hasArg(OPT_no_canonical_prefixes);
 
+  // Extract --hip-path= from --device-compiler= args, where the outer driver
+  // forwards it via the CompilerOptions forwarding mechanism.
+  for (StringRef Arg : Args.getAllArgValues(OPT_device_compiler_args_EQ)) {
+    auto [DevTriple, Value] = Arg.split('=');
+    if (Value.consume_front("--hip-path=")) {
+      HipPath = Value.str();
+      break;
+    }
+  }
+
   llvm::Triple Triple(
       Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
   if (Args.hasArg(OPT_o))
diff --git a/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp 
b/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
index d65d8ec53c6d0..20c8e8cca6748 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
@@ -88,7 +88,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)
     Env = Kernel;
   else
     Env = Unknown;

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to