Skip to content

release/23.x: Reland "[HIPSPV] Add in-tree SPIR-V backend support for chipStar" (#213052) - #214531

Open
yxsamliu wants to merge 1 commit into
llvm:release/23.xfrom
yxsamliu:amd/dev/yaxunl/PR-213052-llvm23-backport
Open

release/23.x: Reland "[HIPSPV] Add in-tree SPIR-V backend support for chipStar" (#213052)#214531
yxsamliu wants to merge 1 commit into
llvm:release/23.xfrom
yxsamliu:amd/dev/yaxunl/PR-213052-llvm23-backport

Conversation

@yxsamliu

@yxsamliu yxsamliu commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Backports #213052 to release/23.x.

The original change is authored by @pvelesko.

The automatic cherry-pick conflicted because LLVM 23 has an older HIPSPV
driver implementation. This adapts the reland implementation while keeping
the same behavior as #213052. It leaves out an unrelated debug-info change
that is not present on the release branch.

Original commit: 7ef0ca2

@dyung

dyung commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Is this a bugfix? Superficially it looks like a new feature.

@yxsamliu

yxsamliu commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

@dyung This backport was requested by @pvelesko in #213052 after the fix landed on main.

It is a mixed backport: the bug fix is for the HIPSPV X86-only build issue, but on release/23.x it also brings the chipStar in-tree SPIR-V backend change that the fix belongs to.

@pvelesko Could you please elaborate on why this needs to be backported to LLVM 23?

@pvelesko

Copy link
Copy Markdown
Contributor

chipStar is the downstream consumer of the HIPSPV toolchain, and it is the only one. Right now chipStar has to ship its own out-of-tree driver patches on every LLVM release to get HIP-on-SPIR-V working with the in-tree SPIR-V backend and the new offload driver.

@dyung

dyung commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

chipStar is the downstream consumer of the HIPSPV toolchain, and it is the only one. Right now chipStar has to ship its own out-of-tree driver patches on every LLVM release to get HIP-on-SPIR-V working with the in-tree SPIR-V backend and the new offload driver.

Would including this patch completely eliminate the need for you to ship any out-of-tree driver patches?

@pvelesko

pvelesko commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Would including this patch completely eliminate the need for you to ship any out-of-tree driver patches?

Essentially, yes. There is another patch for MacOS support but it's minor. This is the main one.

@dyung

dyung commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Would including this patch completely eliminate the need for you to ship any out-of-tree driver patches?

Essentially, yes. There is another patch for MacOS support but it's minor. This is the main one.

Thanks for explaining this. Normally we would not accept a patch like this because it isn't strictly a bug fix, but since it is still in the rc-phase I am going to accept it this time.

@dyung dyung moved this from Needs Triage to Needs Review in LLVM Release Status Aug 14, 2026
@dyung

dyung commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

@yxsamliu could you review this change for inclusion on the 23.x release branch when you have a moment?

Or if that doesn't work since you posted the PR, @pvelesko can you review and approve the change if you've tested it and it works for what you intend to use it for?

…vm#213052)

Relands llvm#206910 (reverted in llvm#213088) with the fix for the breakage.

IsIntegratedBackendDefault() was tied to whether the SPIR-V backend is
registered, but it also controls whether clang collapses the compile and
backend
jobs, so builds without the SPIR-V target split every HIPSPV RDC device
compile
and failed hipspv-toolchain-rdc.hip. The default is back to the base
class value
and the translator fallback is decided in
constructLinkAndEmitSpirvCommand.
Third commit adds a test not guarded by spirv-registered-target.

Validated in X86;SPIRV and X86 only builds: driver tests pass in both,
and
non-RDC plus RDC (new driver) flows run for real emit spirv-val clean
modules
via the backend and via llvm-spirv respectively.

(cherry picked from commit 7ef0ca2)
@dyung
dyung force-pushed the amd/dev/yaxunl/PR-213052-llvm23-backport branch from 9d6447f to fa3798f Compare August 14, 2026 09:50
@llvmorg-github-actions llvmorg-github-actions Bot added clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' backend:SPIR-V labels Aug 14, 2026
@llvmorg-github-actions

llvmorg-github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown

@llvm/pr-subscribers-clang-driver

@llvm/pr-subscribers-backend-spir-v

Author: Yaxun (Sam) Liu (yxsamliu)

Changes

Backports #213052 to release/23.x.

The automatic cherry-pick conflicted because LLVM 23 has an older HIPSPV
driver implementation. This adapts the reland to that implementation while
keeping the same behavior as #213052. It leaves out an unrelated debug-info
change that is not present on the release branch.

Tested with both configurations:

  • X86 and SPIR-V: all seven hipspv*.hip driver tests pass.
  • X86 only: hipspv-no-spirv-backend.hip passes.

Original commit: 7ef0ca2


Patch is 21.73 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/214531.diff

7 Files Affected:

  • (modified) clang/lib/Driver/ToolChains/HIPSPV.cpp (+123-34)
  • (modified) clang/lib/Driver/ToolChains/HIPSPV.h (+10-2)
  • (modified) clang/test/Driver/hipspv-link-static-library.hip (+2-1)
  • (added) clang/test/Driver/hipspv-no-spirv-backend.hip (+41)
  • (modified) clang/test/Driver/hipspv-pass-plugin.hip (+13-9)
  • (modified) clang/test/Driver/hipspv-toolchain.hip (+43-7)
  • (modified) llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp (+2-1)
diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index d6900c767d1f7..04d5a57a04b43 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -13,6 +13,7 @@
 #include "clang/Driver/Driver.h"
 #include "clang/Driver/InputInfo.h"
 #include "clang/Options/Options.h"
+#include "llvm/MC/TargetRegistry.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 
@@ -48,6 +49,34 @@ static std::string findPassPlugin(const Driver &D,
   return std::string();
 }
 
+// Is the in-tree SPIR-V backend built into this clang?
+static bool isSPIRVBackendAvailable(const llvm::Triple &T) {
+  std::string IgnoredError;
+  return llvm::TargetRegistry::lookupTarget(T, IgnoredError);
+}
+
+// Runs the HipSpvPasses plugin via `opt` on TempFile when the plugin is found.
+// Returns the lowered bitcode path, or TempFile unchanged if no plugin exists.
+static const char *runHipSpvPasses(Compilation &C, const JobAction &JA,
+                                   const Tool &Creator, const ToolChain &TC,
+                                   const InputInfoList &Inputs,
+                                   const InputInfo &Output,
+                                   const llvm::opt::ArgList &Args,
+                                   StringRef Name, const char *TempFile) {
+  auto PassPluginPath = findPassPlugin(C.getDriver(), Args);
+  if (PassPluginPath.empty())
+    return TempFile;
+  const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath);
+  const char *OptOutput = HIP::getTempFile(C, Name.str() + "-lower", "bc");
+  ArgStringList OptArgs{TempFile,     "-load-pass-plugin",
+                        PassPathCStr, "-passes=hip-post-link-passes",
+                        "-o",         OptOutput};
+  const char *Opt = Args.MakeArgString(TC.GetProgramPath("opt"));
+  C.addCommand(std::make_unique<Command>(
+      JA, Creator, ResponseFileSupport::None(), Opt, OptArgs, Inputs, Output));
+  return OptOutput;
+}
+
 void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
     Compilation &C, const JobAction &JA, const InputInfoList &Inputs,
     const InputInfo &Output, const llvm::opt::ArgList &Args) const {
@@ -73,45 +102,100 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
   tools::constructLLVMLinkCommand(C, *this, JA, Inputs, LinkArgs, Output, Args,
                                   TempFile);
 
-  // Post-link HIP lowering.
+  auto T = getToolChain().getTriple();
 
-  // Run LLVM IR passes to lower/expand/emulate HIP code that does not translate
-  // to SPIR-V (E.g. dynamic shared memory).
-  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"));
+  if (T.getOS() == llvm::Triple::ChipStar) {
+    // chipStar: run HipSpvPasses via opt, then emit SPIR-V with the in-tree
+    // SPIR-V backend by default, or with the external llvm-spirv translator
+    // when -fno-integrated-objemitter is given (or the backend is not built).
+
+    // Run HipSpvPasses plugin via opt (must run on LLVM IR before
+    // the SPIR-V backend lowers to MIR).
+    TempFile = runHipSpvPasses(C, JA, *this, getToolChain(), Inputs, Output,
+                               Args, Name, TempFile);
+
+    // Note that useIntegratedBackend() is consulted first so that an explicit
+    // -f(no-)integrated-objemitter still gets diagnosed against this toolchain.
+    if (!getToolChain().useIntegratedBackend() || !isSPIRVBackendAvailable(T)) {
+      // External translator path: BC -> SPIR-V via llvm-spirv.
+      llvm::opt::ArgStringList TrArgs;
+      if (T.getSubArch() == llvm::Triple::NoSubArch)
+        TrArgs.push_back("--spirv-max-version=1.2");
+      // Keep this extension list in sync with the in-tree backend fallback
+      // below.
+      TrArgs.push_back("--spirv-ext=-all"
+                       ",+SPV_INTEL_function_pointers"
+                       ",+SPV_INTEL_subgroups"
+                       ",+SPV_KHR_bit_instructions"
+                       ",+SPV_EXT_shader_atomic_float_add");
+
+      // Preserve debug info in the NonSemantic.Shader.DebugInfo form.
+      // These flags are passed unconditionally instead of gating on -g: in
+      // RDC-mode links this job runs in a clang invoked by
+      // clang-linker-wrapper where the original -g is not visible, but the
+      // debug info itself travels in the bitcode. SPV_KHR_non_semantic_info
+      // and the debug info version only take effect when the bitcode carries
+      // debug info. SPV_INTEL_optnone is not tied to debug info: clang emits
+      // optnone at -O0 even without -g, and the emitter needs the extension
+      // allowed to encode it.
+      TrArgs.push_back("--spirv-ext=+SPV_KHR_non_semantic_info"
+                       ",+SPV_INTEL_optnone");
+      TrArgs.push_back("--spirv-debug-info-version=nonsemantic-shader-200");
+
+      InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, "");
+      SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs);
+      return;
+    }
+
+    // Default: compile the lowered bitcode to SPIR-V with the in-tree backend.
+    // Invoke `clang -cc1` directly rather than the clang driver: the driver
+    // would re-run config-file loading, toolchain detection and argument
+    // translation over an input that is already device-compiled and lowered,
+    // which is both wasteful and fragile. This mirrors how HIPAMD drives its
+    // SPIR-V backend emission (see HIPAMD::constructLinkAndEmitSpirvCommand).
+    // Keep the default -O0 backend pipeline (i.e. no -disable-llvm-optzns) so
+    // the mandatory lowering passes still run, matching the previously
+    // validated driver `-c` behavior.
+    ArgStringList Cc1Args;
+    Cc1Args.push_back("-cc1");
+    Cc1Args.push_back("-triple");
+    Cc1Args.push_back(C.getArgs().MakeArgString(T.getTriple()));
+    Cc1Args.push_back("-emit-obj");
+
+    // SPIR-V extensions the chipStar runtime relies on. Keep in sync with the
+    // llvm-spirv translator path above. SPV_KHR_non_semantic_info and
+    // SPV_INTEL_optnone let the backend emit NonSemantic.Shader.DebugInfo and
+    // the OptNoneINTEL function control when the bitcode carries debug info /
+    // optnone attributes (the backend's debug handler is a no-op otherwise).
+    Cc1Args.push_back("-mllvm");
+    Cc1Args.push_back("-spirv-ext=+SPV_INTEL_function_pointers"
+                      ",+SPV_INTEL_subgroups"
+                      ",+SPV_KHR_bit_instructions"
+                      ",+SPV_EXT_shader_atomic_float_add"
+                      ",+SPV_KHR_non_semantic_info"
+                      ",+SPV_INTEL_optnone");
+
+    Cc1Args.push_back(TempFile);
+    Cc1Args.push_back("-o");
+    Cc1Args.push_back(Output.getFilename());
+
+    const Driver &Drv = C.getDriver();
+    const char *Clang = Drv.getDriverProgramPath();
     C.addCommand(std::make_unique<Command>(
-        JA, *this, ResponseFileSupport::None(), Opt, OptArgs, Inputs, Output));
-    TempFile = OptOutput;
+        JA, *this, ResponseFileSupport::None(), Clang, Cc1Args, Inputs, Output,
+        Drv.getPrependArg()));
+    return;
   }
 
-  // Emit SPIR-V binary.
-  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");
-  }
+  // Non-chipStar: run HIP passes via opt, then translate with llvm-spirv.
+  TempFile = runHipSpvPasses(C, JA, *this, getToolChain(), Inputs, Output, Args,
+                             Name, TempFile);
 
+  // Emit SPIR-V binary via llvm-spirv translator (non-chipStar targets).
+  llvm::opt::ArgStringList TrArgs;
+  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);
 }
@@ -150,6 +234,11 @@ HIPSPVToolChain::HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple,
   getProgramPaths().push_back(getDriver().Dir);
 }
 
+bool HIPSPVToolChain::IsIntegratedBackendSupported() const {
+  // The in-tree SPIR-V backend can only be requested when it is built.
+  return isSPIRVBackendAvailable(getTriple());
+}
+
 void HIPSPVToolChain::addClangTargetOptions(
     const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
     BoundArch BA, Action::OffloadKind DeviceOffloadingKind) const {
diff --git a/clang/lib/Driver/ToolChains/HIPSPV.h b/clang/lib/Driver/ToolChains/HIPSPV.h
index 337c9c9993876..e38a7135a60fd 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.h
+++ b/clang/lib/Driver/ToolChains/HIPSPV.h
@@ -52,10 +52,18 @@ class LLVM_LIBRARY_VISIBILITY HIPSPVToolChain final : public ToolChain {
                   const llvm::opt::ArgList &Args);
 
   const llvm::Triple *getAuxTriple() const override {
-    assert(HostTC);
-    return &HostTC->getTriple();
+    return HostTC ? &HostTC->getTriple() : nullptr;
   }
 
+  // Keep IsIntegratedBackendDefault() at the base class' "true": it also
+  // decides whether clang's compile and backend jobs are collapsed into a
+  // single -cc1 invocation, so making it depend on whether the SPIR-V backend
+  // was built would change the device compilation job layout of every HIPSPV
+  // compile. The fallback to the external llvm-spirv translator is decided in
+  // HIPSPV::Linker::constructLinkAndEmitSpirvCommand instead.
+  bool IsIntegratedBackendSupported() const override;
+  bool IsNonIntegratedBackendSupported() const override { return true; }
+
   void
   addClangTargetOptions(const llvm::opt::ArgList &DriverArgs,
                         llvm::opt::ArgStringList &CC1Args, BoundArch BA,
diff --git a/clang/test/Driver/hipspv-link-static-library.hip b/clang/test/Driver/hipspv-link-static-library.hip
index eb114ada49020..a00f385b3288b 100644
--- a/clang/test/Driver/hipspv-link-static-library.hip
+++ b/clang/test/Driver/hipspv-link-static-library.hip
@@ -49,7 +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: --target=spirv64-unknown-chipstar
+// SDL-NEW-WRAPPER-SAME: {{[^ ]*.o}}
 // SDL-NEW-WRAPPER-SAME: --hip-path=[[HIP_PATH]]
 
 // SDL: "{{.*}}opt"
diff --git a/clang/test/Driver/hipspv-no-spirv-backend.hip b/clang/test/Driver/hipspv-no-spirv-backend.hip
new file mode 100644
index 0000000000000..decab842c8296
--- /dev/null
+++ b/clang/test/Driver/hipspv-no-spirv-backend.hip
@@ -0,0 +1,41 @@
+// Coverage for HIPSPV behavior that must not depend on whether the in-tree
+// SPIR-V backend was built. Deliberately not guarded by the
+// spirv-registered-target feature, so builders configured without the SPIR-V
+// target (e.g. X86-only bots) exercise it too.
+
+// UNSUPPORTED: system-windows
+
+// The device compilation job layout is a property of the driver, not of the
+// registered targets: an RDC device compile stays a single -cc1 invocation
+// that emits the object directly, rather than being split into a separate
+// -emit-llvm-bc job plus a backend job.
+
+// RUN: %clang -### -x hip --target=x86_64-linux-gnu --offload=spirv64 \
+// RUN:   --no-offload-new-driver -fgpu-rdc --hip-path=%S/Inputs/hipspv \
+// RUN:   -nohipwrapperinc %S/Inputs/hip_multiple_inputs/a.cu \
+// RUN: 2>&1 | FileCheck --check-prefix=RDC-JOB %s
+
+// RDC-JOB: "-cc1" "-triple" "spirv64"
+// RDC-JOB-SAME: "-aux-triple" "x86_64-unknown-linux-gnu"
+// RDC-JOB-SAME: "-emit-obj"
+// RDC-JOB-SAME: "-fcuda-is-device"
+
+// -fno-integrated-objemitter selects the external llvm-spirv translator, and
+// it is accepted in either configuration.
+
+// RUN: touch %t.o
+// RUN: %clang -### --no-default-config -o %t.img \
+// RUN:   --target=spirv64-unknown-chipstar %t.o --hip-path=%S/Inputs/hipspv \
+// RUN:   -fno-integrated-objemitter \
+// RUN: 2>&1 | FileCheck --check-prefix=XTOR -DHIP_PATH=%S/Inputs/hipspv %s
+
+//      XTOR: {{".*llvm-link"}}
+// XTOR-SAME: "-o" [[LINK_BC:".*bc"]] "{{[^ ]*.o}}"
+
+//      XTOR: {{".*opt"}} [[LINK_BC]] "-load-pass-plugin"
+// XTOR-SAME: "[[HIP_PATH]]/lib/libLLVMHipSpvPasses.so"
+// XTOR-SAME: "-passes=hip-post-link-passes" "-o" [[LOWER_BC:".*bc"]]
+
+//      XTOR: {{".*llvm-spirv.*"}} "--spirv-max-version=1.2"
+// XTOR-SAME: [[LOWER_BC]] "-o" "{{.*img}}"
+// XTOR-NOT: "-cc1"
diff --git a/clang/test/Driver/hipspv-pass-plugin.hip b/clang/test/Driver/hipspv-pass-plugin.hip
index 3a0979ad6df01..ae6194b74b2bf 100644
--- a/clang/test/Driver/hipspv-pass-plugin.hip
+++ b/clang/test/Driver/hipspv-pass-plugin.hip
@@ -1,3 +1,4 @@
+// REQUIRES: spirv-registered-target
 // UNSUPPORTED: system-windows
 
 // RUN: %clang -### -target x86_64-linux-gnu --offload=spirv64 \
@@ -16,23 +17,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: env "PATH=" %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: env "PATH=" %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: not env "PATH=" %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: env "PATH=" %clang -### --no-default-config -o /dev/null --target=spirv64-unknown-chipstar \
+// 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 +44,5 @@
 // NO-PLUGIN-NOT: {{".*opt"}} {{".*.bc"}} "-load-pass-plugin"
 // NO-PLUGIN-NOT: {{".*/Inputs/hipspv/lib/libLLVMHipSpvPasses.so"}}
 // ALL: {{".*llvm-spirv[^ ]*"}}
+// CHIPSTAR: {{".*clang.*"}} "-cc1"
+// CHIPSTAR-SAME: "-emit-obj"
diff --git a/clang/test/Driver/hipspv-toolchain.hip b/clang/test/Driver/hipspv-toolchain.hip
index 64d5d22a11d39..3a262a2ce6002 100644
--- a/clang/test/Driver/hipspv-toolchain.hip
+++ b/clang/test/Driver/hipspv-toolchain.hip
@@ -60,6 +60,10 @@
 // RUN: llvm-offload-binary -o %t.dev.out \
 // RUN:   --image=file=%t.dev.bc,kind=hip,triple=spirv64-unknown-chipstar,arch=generic
 
+// The linker wrapper forwards --hip-path from --device-compiler= to the inner
+// clang invocation; the HIPSPV toolchain inside that clang then drives the
+// llvm-link / opt (HipSpvPasses) / SPIR-V backend pipeline (covered by the
+// CHIPSTAR run below).
 // RUN: clang-linker-wrapper --dry-run \
 // RUN:   --device-compiler=spirv64-unknown-chipstar=--hip-path="%S/Inputs/hipspv" \
 // RUN:   --host-triple=spirv64-unknown-chipstar \
@@ -72,6 +76,8 @@
 // WRAPPER-SAME: {{[^ ]*.o}}
 // WRAPPER-SAME: --hip-path=[[HIP_PATH]]
 
+// The in-tree SPIR-V backend is the default emitter; PATH content (e.g. a
+// stray llvm-spirv) must not affect the choice.
 // RUN: touch %t.dummy.o
 // RUN: %clang -### --no-default-config -o %t.dummy.img \
 // RUN:   --target=spirv64-unknown-chipstar %t.dummy.o \
@@ -85,8 +91,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.*"}} "-cc1" "-triple" "spirv64-unknown-chipstar"
+// CHIPSTAR-SAME: "-emit-obj"
+// CHIPSTAR-SAME: "-mllvm" "-spirv-ext=+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add,+SPV_KHR_non_semantic_info,+SPV_INTEL_optnone"
 // CHIPSTAR-SAME: [[LOWER_BC]] "-o" "[[SPIRV_OUT:.*img]]"
 
 // RUN: %clang -### --no-default-config -o %t.dummy.img \
@@ -101,10 +108,27 @@
 // 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.*"}} "-cc1" "-triple" "spirv64v1.3-unknown-chipstar"
+// CHIPSTAR-SUBARCH-SAME: "-emit-obj"
+// CHIPSTAR-SUBARCH-SAME: "-mllvm" "-spirv-ext=+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add,+SPV_KHR_non_semantic_info,+SPV_INTEL_optnone"
 // CHIPSTAR-SUBARCH-SAME: [[LOWER_BC]] "-o" "[[SPIRV_OUT:.*img]]"
 
+// -fno-integrated-objemitter selects the external llvm-spirv translator.
+// RUN: %clang -### --no-default-config -o %t.dummy.img \
+// RUN:   --target=spirv64-unknown-chipstar %t.dummy.o \
+// RUN:   --hip-path="%S/Inputs/hipspv" -fno-integrated-objemitter \
+// RUN: 2>&1 | FileCheck %s --check-prefix=CHIPSTAR-XTOR -DHIP_PATH=%S/Inputs/hipspv
+
+//      CHIPSTAR-XTOR: {{".*opt"}} {{".*bc"}} "-load-pass-plugin"
+// CHIPSTAR-XTOR-SAME: "[[HIP_PATH]]/lib/libLLVMHipSpvPasses.so"
+// CHIPSTAR-XTOR-SAME: "-passes=hip-post-link-passes" "-o" [[LOWER_BC:".*bc"]]
+
+//      CHIPSTAR-XTOR: {{".*llvm-spirv.*"}} "--spirv-max-version=1.2"
+// CHIPSTAR-XTOR-SAME: "--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add"
+// CHIPSTAR-XTOR-SAME: "--spirv-ext=+SPV_KHR_non_semantic_info,+SPV_INTEL_optnone"
+// CHIPSTAR-XTOR-SAME: "--spirv-debug-info-version=nonsemantic-shader-200"
+// CHIPSTAR-XTOR-SAME: [[LOWER_BC]] "-o" "{{.*img}}"
+
 // Check unknown linker options are ignored - such as ones that are targeted at
 // spirv-link. HIPSPV toolchain does linking via llvm-link.
 // RUN: %clang -### --no-default-config -o %t.dummy.img \
@@ -123,9 +147,21 @@
 // RUN:   | FileCheck -DVERSION=%llvm-version-major \
 // RUN:   --check-prefix=VERSIONED %s
 
+// With -fno-integrated-objemitter the chipStar path must pick the same
+// versioned translator (lookup shared with SPIRV::constructTranslateCommand).
 // RUN: env "PATH=%t/versioned" %clang -### --no-default-config \
-// RUN:  -o %t.dummy.im...
[truncated]

@yxsamliu

Copy link
Copy Markdown
Contributor Author

@dyung I am not the original author of the change; @pvelesko is. I only cherry-picked it to release/23.x and resolved the conflicts.

I checked the conflict resolutions again. The backport keeps the same intended behavior as the main-branch change: chipStar uses the in-tree SPIR-V backend by default, falls back to llvm-spirv when needed, and keeps the HIPSPV device compile job layout unchanged.

The only main-branch difference I left out is an unrelated HIPSPV debug-info change that is not on release/23.x.

@pvelesko

Copy link
Copy Markdown
Contributor

@dyung tested, LGTM

@yxsamliu

Copy link
Copy Markdown
Contributor Author

@dyung Thanks. CI is green now and @pvelesko tested it. Could you approve or let me know if anything else is needed before merging?

@pvelesko

Copy link
Copy Markdown
Contributor

ping @dyung

@dyung

dyung commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

@VyacheslavLevytskyy and @Keenuts, not sure if you are the best people to ping for this, but would you have any objections to this change going into the release/23.x branch?

@dyung

dyung commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

ping @VyacheslavLevytskyy and @Keenuts, any objections to cherry-picking this change to the 23.x release branch?

@dyung

dyung commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

@jhuber6 and @sarnex, is this change an area that one of you might be able to say whether we should accept this cherry-pick into the 23.x release branch?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:SPIR-V clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl'

Projects

Status: Needs Review

Development

Successfully merging this pull request may close these issues.

4 participants