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

>From 863f6e532f9cb1b1ca8d9752645f712554d6984f Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Tue, 17 Mar 2026 09:55:28 +0200
Subject: [PATCH 01/17] [HIPSPV] Add in-tree SPIR-V backend support for
 chipStar

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        |  75 +++++--
 clang/lib/Driver/ToolChains/HIPSPV.h          |   3 +-
 clang/lib/Driver/ToolChains/SPIRV.cpp         |   3 +-
 .../Driver/hipspv-link-static-library.hip     |   9 +-
 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 +-
 9 files changed, 308 insertions(+), 53 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 1e6fa1e4d54c6..df5232e3923fa 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -73,10 +73,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;
+    }
 
-  // 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().getDriverProgramPath());
+    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);
@@ -90,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");
 
   // Preserve debug info requested via -g into the emitted SPIR-V using the
   // NonSemantic.Shader.DebugInfo form. Downstream consumers such as Intel's 
IGC
diff --git a/clang/lib/Driver/ToolChains/HIPSPV.h 
b/clang/lib/Driver/ToolChains/HIPSPV.h
index 337c9c9993876..fb53d65b335f5 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.h
+++ b/clang/lib/Driver/ToolChains/HIPSPV.h
@@ -52,8 +52,7 @@ 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;
   }
 
   void
diff --git a/clang/lib/Driver/ToolChains/SPIRV.cpp 
b/clang/lib/Driver/ToolChains/SPIRV.cpp
index 5cc1eec74c1cc..c6a0b667971fb 100644
--- a/clang/lib/Driver/ToolChains/SPIRV.cpp
+++ b/clang/lib/Driver/ToolChains/SPIRV.cpp
@@ -201,7 +201,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) || isUsingLTO(Args);
+  NativeLLVMSupport = Args.hasArg(options::OPT_sycl_link) || isUsingLTO(Args) 
||
+                      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..dbcb5bd4e5cbb 100644
--- a/clang/test/Driver/hipspv-link-static-library.hip
+++ b/clang/test/Driver/hipspv-link-static-library.hip
@@ -48,9 +48,12 @@
 // SDL-NEW-SAME: "{{.*}}/tu0.o" "{{.*}}/libSDL2.a"
 // 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: "{{.*}}opt"
+// SDL-NEW-WRAPPER-SAME: -load-pass-plugin
+// SDL-NEW-WRAPPER-SAME: [[HIP_PATH]]/lib/libLLVMHipSpvPasses.so
+// SDL-NEW-WRAPPER: clang{{.*}}" --no-default-config
+// SDL-NEW-WRAPPER-SAME: --target=spirv64-unknown-chipstar
+// SDL-NEW-WRAPPER-SAME: -c -x ir
 
 // 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 3e11d647fd821..870ebbde92c85 100644
--- a/clang/test/Driver/hipspv-toolchain.hip
+++ b/clang/test/Driver/hipspv-toolchain.hip
@@ -60,17 +60,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 \
@@ -85,8 +118,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 \
@@ -101,8 +135,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]]"
 
 // Check unknown linker options are ignored - such as ones that are targeted at
@@ -123,11 +158,6 @@
 // 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 c2de6578773c7..e9a9afce25edd 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;
 
@@ -473,9 +476,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, ",")));
 
@@ -543,7 +562,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.
@@ -602,8 +774,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);
@@ -1529,6 +1707,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 be140640962d3..5899f60360a11 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
@@ -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)
     Env = Kernel;
   else
     Env = Unknown;

>From f9ce2add3d1b7534831ff0426ef481beb888c31c Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Wed, 8 Apr 2026 17:09:17 +0300
Subject: [PATCH 02/17] [HIPSPV] Delegate chipStar SPIR-V emission to inner
 clang in linker wrapper

Address review feedback on llvm/llvm-project#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).
---
 .../Driver/hipspv-link-static-library.hip     |   8 +-
 clang/test/Driver/hipspv-toolchain.hip        |  45 +----
 .../ClangLinkerWrapper.cpp                    | 176 +-----------------
 3 files changed, 13 insertions(+), 216 deletions(-)

diff --git a/clang/test/Driver/hipspv-link-static-library.hip 
b/clang/test/Driver/hipspv-link-static-library.hip
index dbcb5bd4e5cbb..a00f385b3288b 100644
--- a/clang/test/Driver/hipspv-link-static-library.hip
+++ b/clang/test/Driver/hipspv-link-static-library.hip
@@ -48,12 +48,10 @@
 // SDL-NEW-SAME: "{{.*}}/tu0.o" "{{.*}}/libSDL2.a"
 // DELETE-SDL-NEW: "{{.*}}llvm-link" "-o" "{{.*}}.bc" "{{.*}}.o" "{{.*}}.o"
 
-// SDL-NEW-WRAPPER: "{{.*}}opt"
-// SDL-NEW-WRAPPER-SAME: -load-pass-plugin
-// SDL-NEW-WRAPPER-SAME: [[HIP_PATH]]/lib/libLLVMHipSpvPasses.so
-// SDL-NEW-WRAPPER: clang{{.*}}" --no-default-config
+// SDL-NEW-WRAPPER: clang{{.*}}" --no-default-config -o {{[^ ]*.img}}
 // SDL-NEW-WRAPPER-SAME: --target=spirv64-unknown-chipstar
-// SDL-NEW-WRAPPER-SAME: -c -x ir
+// SDL-NEW-WRAPPER-SAME: {{[^ ]*.o}}
+// SDL-NEW-WRAPPER-SAME: --hip-path=[[HIP_PATH]]
 
 // SDL: "{{.*}}opt"
 // SDL-SAME: "-load-pass-plugin" {{".*/hipspv/lib/libLLVMHipSpvPasses.so"}}
diff --git a/clang/test/Driver/hipspv-toolchain.hip 
b/clang/test/Driver/hipspv-toolchain.hip
index 870ebbde92c85..75764979fa38f 100644
--- a/clang/test/Driver/hipspv-toolchain.hip
+++ b/clang/test/Driver/hipspv-toolchain.hip
@@ -60,50 +60,21 @@
 // RUN: llvm-offload-binary -o %t.dev.out \
 // RUN:   
--image=file=%t.dev.bc,kind=hip,triple=spirv64-unknown-chipstar,arch=generic
 
-// 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 \
+// 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 \
 // 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
 
-// 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: clang{{.*}}" --no-default-config -o {{[^ ]*.img}}
 // WRAPPER-SAME: --target=spirv64-unknown-chipstar
-// 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
+// WRAPPER-SAME: {{[^ ]*.o}}
+// WRAPPER-SAME: --hip-path=[[HIP_PATH]]
 
 // RUN: touch %t.dummy.o
 // RUN: %clang -### --no-default-config -o %t.dummy.img \
diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp 
b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
index e9a9afce25edd..bdf12acc2a9b9 100644
--- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
+++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
@@ -125,9 +125,6 @@ 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;
 
@@ -562,160 +559,7 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, 
const ArgList &Args,
   if (!Triple.isNVPTX() && !Triple.isSPIRV())
     CmdArgs.push_back("-Wl,--no-undefined");
 
-  // 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)
+  for (StringRef InputFile : InputFiles)
     CmdArgs.push_back(InputFile);
 
   // If this is CPU offloading we copy the input libraries.
@@ -774,14 +618,8 @@ 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 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;
+  for (StringRef Arg : Args.getAllArgValues(OPT_compiler_arg_EQ))
     CmdArgs.push_back(Args.MakeArgString(Arg));
-  }
 
   if (Error Err = executeCommands(*ClangPath, CmdArgs))
     return std::move(Err);
@@ -1707,16 +1545,6 @@ 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))

>From 74a537d25c0eac865f145342435fb177d7b91c49 Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Wed, 8 Apr 2026 17:40:18 +0300
Subject: [PATCH 03/17] [HIPSPV] Skip non-filename Inputs in
 constructLinkAndEmitSpirvCommand

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.
---
 clang/lib/Driver/ToolChains/HIPSPV.cpp | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp 
b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index df5232e3923fa..ca911c8982be2 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -59,7 +59,11 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
   // Link LLVM bitcode.
   ArgStringList LinkArgs{};
 
-  for (auto Input : Inputs)
+  // The new offload driver can pass non-filename InputInfo entries (e.g.
+  // Nothing/InputArg placeholders) alongside the real bitcode inputs. Calling
+  // getFilename() on those reads garbage from the union; only forward genuine
+  // filename inputs to llvm-link.
+  for (const auto &Input : Inputs)
     if (Input.isFilename())
       LinkArgs.push_back(Input.getFilename());
 

>From 7aa53861e5b1eade0982f01e237bef8c4cdfb0d4 Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Wed, 29 Apr 2026 22:04:30 +0300
Subject: [PATCH 04/17] [HIPSPV] Fall back to llvm-spirv when in-tree SPIR-V
 target is missing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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.
---
 clang/lib/Driver/ToolChains/HIPSPV.cpp | 41 ++++++++++++++++++++++++--
 1 file changed, 38 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp 
b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index ca911c8982be2..c8d03315bb604 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -15,6 +15,7 @@
 #include "clang/Options/Options.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
 
 using namespace clang::driver;
 using namespace clang::driver::toolchains;
@@ -80,8 +81,9 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
   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).
+    // chipStar: run HipSpvPasses via opt, then emit SPIR-V either via the
+    // external llvm-spirv translator (if available) or the in-tree SPIR-V
+    // backend (mirrors the fallback in clang-linker-wrapper).
 
     // Run HipSpvPasses plugin via opt (must run on LLVM IR before
     // the SPIR-V backend lowers to MIR).
@@ -100,7 +102,40 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
       TempFile = OptOutput;
     }
 
-    // Compile processed bitcode to SPIR-V using the in-tree backend.
+    // Prefer the external llvm-spirv translator when it is available next to
+    // the toolchain — required when LLVM is built without the in-tree SPIR-V
+    // target. Use findProgramByName (not GetProgramPath) so a missing binary
+    // is reported as not-found rather than being silently substituted.
+    std::string LLVMSpirvPath;
+    {
+      llvm::ErrorOr<std::string> P = llvm::sys::findProgramByName(
+          "llvm-spirv", {llvm::sys::path::parent_path(C.getDriver().Dir)});
+      if (!P)
+        P = llvm::sys::findProgramByName("llvm-spirv",
+                                         {llvm::StringRef(C.getDriver().Dir)});
+      if (!P)
+        P = llvm::sys::findProgramByName("llvm-spirv");
+      if (P)
+        LLVMSpirvPath = *P;
+    }
+
+    if (!LLVMSpirvPath.empty()) {
+      // External translator path: BC -> SPIR-V via llvm-spirv.
+      llvm::opt::ArgStringList TrArgs;
+      // Match clang-linker-wrapper's chipstar version selection.
+      if (T.getSubArch() == llvm::Triple::SPIRVSubArch_v13)
+        TrArgs.push_back("--spirv-max-version=1.3");
+      else
+        TrArgs.push_back("--spirv-max-version=1.2");
+      TrArgs.push_back(
+          
"--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups");
+
+      InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, "");
+      SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs);
+      return;
+    }
+
+    // Fallback: compile processed bitcode to SPIR-V using the in-tree backend.
     ArgStringList ClangArgs;
     ClangArgs.push_back("--no-default-config");
     ClangArgs.push_back("-c");

>From 56f1b8fd6e149734ef37a380ba66cc4cd584820c Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Sun, 14 Jun 2026 13:35:39 +0300
Subject: [PATCH 05/17] [HIPSPV] Allow relaxed_printf ext on external
 llvm-spirv path

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.
---
 clang/lib/Driver/ToolChains/HIPSPV.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp 
b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index c8d03315bb604..ecd9e1e828e57 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -127,8 +127,15 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
         TrArgs.push_back("--spirv-max-version=1.3");
       else
         TrArgs.push_back("--spirv-max-version=1.2");
+      // Keep this extension list in sync with the in-tree backend fallback
+      // below. SPV_EXT_relaxed_printf_string_address_space is required to
+      // translate modules whose printf format string is not in the constant
+      // address space (e.g. device-side assert / dynamic %s lowering); without
+      // it llvm-spirv aborts with exit code 18.
       TrArgs.push_back(
-          
"--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups");
+          "--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups"
+          ",+SPV_EXT_shader_atomic_float_add"
+          ",+SPV_EXT_relaxed_printf_string_address_space");
 
       InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, "");
       SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs);

>From 9a599575d43f5ae4b7e7c8bd02560a1a4e274d1e Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Tue, 30 Jun 2026 10:58:53 +0300
Subject: [PATCH 06/17] [HIPSPV] Address review feedback (NFC + test hardening)

- Factor the duplicated HipSpvPasses opt invocation into a single
  runHipSpvPasses() helper used by both the chipStar and non-chipStar
  paths.
- clang-linker-wrapper fatbinary(): default an empty SPIR-V arch to the
  canonical "generic" CPU model and route all targets through
  normalizeForBundler() instead of hand-built bundle IDs.
- Drop the dead llvm-spirv lookup in the install root (parent_path of
  the driver dir is never a program directory) and fix a non-ASCII
  comment character.
- Pin PATH= on the CHIPSTAR driver-test RUN lines so the in-tree backend
  path is exercised deterministically regardless of a host llvm-spirv.
---
 clang/lib/Driver/ToolChains/HIPSPV.cpp        | 59 +++++++++----------
 clang/test/Driver/hipspv-pass-plugin.hip      |  8 +--
 clang/test/Driver/hipspv-toolchain.hip        |  6 +-
 .../ClangLinkerWrapper.cpp                    | 26 +++-----
 4 files changed, 44 insertions(+), 55 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp 
b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index ecd9e1e828e57..933c01777a3b2 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -49,6 +49,28 @@ static std::string findPassPlugin(const Driver &D,
   return std::string();
 }
 
+// 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 {
@@ -87,32 +109,17 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
 
     // 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;
-    }
+    TempFile = runHipSpvPasses(C, JA, *this, getToolChain(), Inputs, Output,
+                               Args, Name, TempFile);
 
     // Prefer the external llvm-spirv translator when it is available next to
-    // the toolchain — required when LLVM is built without the in-tree SPIR-V
+    // the toolchain - required when LLVM is built without the in-tree SPIR-V
     // target. Use findProgramByName (not GetProgramPath) so a missing binary
     // is reported as not-found rather than being silently substituted.
     std::string LLVMSpirvPath;
     {
       llvm::ErrorOr<std::string> P = llvm::sys::findProgramByName(
-          "llvm-spirv", {llvm::sys::path::parent_path(C.getDriver().Dir)});
-      if (!P)
-        P = llvm::sys::findProgramByName("llvm-spirv",
-                                         {llvm::StringRef(C.getDriver().Dir)});
+          "llvm-spirv", {llvm::StringRef(C.getDriver().Dir)});
       if (!P)
         P = llvm::sys::findProgramByName("llvm-spirv");
       if (P)
@@ -168,18 +175,8 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
   }
 
   // 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);
-    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;
-  }
+  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;
diff --git a/clang/test/Driver/hipspv-pass-plugin.hip 
b/clang/test/Driver/hipspv-pass-plugin.hip
index 5cf891e4c6114..eb915ebc58617 100644
--- a/clang/test/Driver/hipspv-pass-plugin.hip
+++ b/clang/test/Driver/hipspv-pass-plugin.hip
@@ -20,19 +20,19 @@
 // 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=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=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=CHIPSTAR,FROM-OPTION-INVALID
 
-// 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 2>&1 | FileCheck %s --check-prefixes=CHIPSTAR,NO-PLUGIN
 
 // FROM-HIP-PATH: {{".*opt"}} {{".*.bc"}} "-load-pass-plugin"
diff --git a/clang/test/Driver/hipspv-toolchain.hip 
b/clang/test/Driver/hipspv-toolchain.hip
index 75764979fa38f..4e2f59a1b6473 100644
--- a/clang/test/Driver/hipspv-toolchain.hip
+++ b/clang/test/Driver/hipspv-toolchain.hip
@@ -77,7 +77,7 @@
 // WRAPPER-SAME: --hip-path=[[HIP_PATH]]
 
 // RUN: touch %t.dummy.o
-// RUN: %clang -### --no-default-config -o %t.dummy.img \
+// RUN: env "PATH=" %clang -### --no-default-config -o %t.dummy.img \
 // RUN:   --target=spirv64-unknown-chipstar %t.dummy.o \
 // RUN:   --hip-path="%S/Inputs/hipspv" \
 // RUN: 2>&1 | FileCheck %s --check-prefix=CHIPSTAR -DHIP_PATH=%S/Inputs/hipspv
@@ -94,7 +94,7 @@
 // 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 \
+// RUN: env "PATH=" %clang -### --no-default-config -o %t.dummy.img \
 // RUN:   --target=spirv64v1.3-unknown-chipstar \
 // RUN:   %t.dummy.o --hip-path="%S/Inputs/hipspv" \
 // RUN: 2>&1 | FileCheck %s --check-prefix=CHIPSTAR-SUBARCH 
-DHIP_PATH=%S/Inputs/hipspv
@@ -113,7 +113,7 @@
 
 // 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 \
+// RUN: env "PATH=" %clang -### --no-default-config -o %t.dummy.img \
 // RUN:   --target=spirv64-unknown-chipstar %t.dummy.o \
 // RUN:   --hip-path="%S/Inputs/hipspv" -Xlinker foobar \
 // RUN: 2>&1 | FileCheck %s --check-prefix=CHIPSTAR -DHIP_PATH=%S/Inputs/hipspv
diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp 
b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
index bdf12acc2a9b9..9910ceb1a339a 100644
--- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
+++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
@@ -474,24 +474,16 @@ fatbinary(ArrayRef<std::tuple<StringRef, StringRef, 
StringRef>> InputFiles,
       Saver.save("-targets=host-" + HostTriple.normalize())};
   for (const auto &[File, TripleRef, Arch] : InputFiles) {
     llvm::Triple T(TripleRef);
-    // For SPIR-V targets, derive arch from triple if not provided
+    // SPIR-V HIP targets (e.g. chipStar) can arrive without an arch, but the
+    // offload bundler requires a non-empty arch field; default to the
+    // canonical SPIR-V CPU model "generic".
     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);
+    if (EffectiveArch.empty() && T.isSPIRV())
+      EffectiveArch = "generic";
+    std::string NormalizedTriple =
+        normalizeForBundler(T, !EffectiveArch.empty());
+    Targets.push_back(
+        Saver.save("hip-" + NormalizedTriple + "-" + EffectiveArch));
   }
   CmdArgs.push_back(Saver.save(llvm::join(Targets, ",")));
 

>From 0c86231d3fcce412783b20d5565933a5cc3e88fb Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Wed, 1 Jul 2026 13:14:29 +0300
Subject: [PATCH 07/17] [HIPSPV] Emit SPIR-V via clang -cc1 instead of the
 clang driver

The in-tree SPIR-V backend fallback invoked the clang driver to compile
the lowered bitcode to SPIR-V. That re-runs config-file loading, toolchain
detection and argument translation over an input that is already
device-compiled and lowered - wasteful and fragile. Invoke clang -cc1
directly instead, mirroring how HIPAMD drives its SPIR-V backend emission.

Update the hipspv driver tests to match the cc1 command line.
---
 clang/lib/Driver/ToolChains/HIPSPV.cpp   | 55 ++++++++++++++----------
 clang/test/Driver/hipspv-pass-plugin.hip |  3 +-
 clang/test/Driver/hipspv-toolchain.hip   |  8 ++--
 3 files changed, 39 insertions(+), 27 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp 
b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index 933c01777a3b2..585fa2b0b7439 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -149,28 +149,39 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
       return;
     }
 
-    // Fallback: 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().getDriverProgramPath());
-    C.addCommand(std::make_unique<Command>(JA, *this,
-                                           ResponseFileSupport::None(), Clang,
-                                           ClangArgs, Inputs, Output));
+    // Fallback: 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 and the clang-linker-wrapper fallback.
+    Cc1Args.push_back("-mllvm");
+    Cc1Args.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");
+
+    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(), Clang, Cc1Args, Inputs, Output,
+        Drv.getPrependArg()));
     return;
   }
 
diff --git a/clang/test/Driver/hipspv-pass-plugin.hip 
b/clang/test/Driver/hipspv-pass-plugin.hip
index eb915ebc58617..d13894ed50c9b 100644
--- a/clang/test/Driver/hipspv-pass-plugin.hip
+++ b/clang/test/Driver/hipspv-pass-plugin.hip
@@ -43,4 +43,5 @@
 // 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"
+// CHIPSTAR: {{".*clang.*"}} "-cc1"
+// CHIPSTAR-SAME: "-emit-obj"
diff --git a/clang/test/Driver/hipspv-toolchain.hip 
b/clang/test/Driver/hipspv-toolchain.hip
index 4e2f59a1b6473..b05bdb1272cab 100644
--- a/clang/test/Driver/hipspv-toolchain.hip
+++ b/clang/test/Driver/hipspv-toolchain.hip
@@ -89,8 +89,8 @@
 // CHIPSTAR-SAME: "[[HIP_PATH]]/lib/libLLVMHipSpvPasses.so"
 // CHIPSTAR-SAME: "-passes=hip-post-link-passes" "-o" [[LOWER_BC:".*bc"]]
 
-//      CHIPSTAR: {{".*clang.*"}} "--no-default-config" "-c"
-// CHIPSTAR-SAME: "--target=spirv64-unknown-chipstar"
+//      CHIPSTAR: {{".*clang.*"}} "-cc1" "-triple" "spirv64-unknown-chipstar"
+// CHIPSTAR-SAME: "-emit-obj"
 // 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]]"
 
@@ -106,8 +106,8 @@
 // CHIPSTAR-SUBARCH-SAME: "[[HIP_PATH]]/lib/libLLVMHipSpvPasses.so"
 // CHIPSTAR-SUBARCH-SAME: "-passes=hip-post-link-passes" "-o" 
[[LOWER_BC:".*bc"]]
 
-//      CHIPSTAR-SUBARCH: {{".*clang.*"}} "--no-default-config" "-c"
-// CHIPSTAR-SUBARCH-SAME: "--target=spirv64v1.3-unknown-chipstar"
+//      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_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]]"
 

>From 2e8d09d4f9de160bd71b66cd9680801fc9fb907b Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Wed, 1 Jul 2026 13:26:20 +0300
Subject: [PATCH 08/17] [HIPSPV] Drop the AlignedAllocation ChipStar case

alignedAllocMinVersion() feeds only the C++17 aligned operator new
availability diagnostic, which fires solely when the host Darwin
toolchain injects -faligned-alloc-unavailable. That flag no longer leaks
into chipStar device compilation now that Darwin's addClangTargetOptions
skips host flags for device offload (#183991, in main), so this case is
unreachable in the chipStar backend path and does not belong in this PR.
---
 clang/include/clang/Basic/AlignedAllocation.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/clang/include/clang/Basic/AlignedAllocation.h 
b/clang/include/clang/Basic/AlignedAllocation.h
index 9b84d07286d52..ac26eb4a276da 100644
--- a/clang/include/clang/Basic/AlignedAllocation.h
+++ b/clang/include/clang/Basic/AlignedAllocation.h
@@ -35,8 +35,6 @@ 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");

>From 4e71ca82561f297a595ea983e60f767c9817d54d Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Wed, 1 Jul 2026 13:49:35 +0300
Subject: [PATCH 09/17] [HIPSPV] Align llvm-spirv and in-tree SPIR-V extension
 lists

The llvm-spirv translator path was missing SPV_KHR_bit_instructions that
the in-tree backend path enables. Enable the same extension set in the
same order on both paths so the "keep in sync" comment holds, and drop
the stale reference to the removed clang-linker-wrapper fallback.
---
 clang/lib/Driver/ToolChains/HIPSPV.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp 
b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index 585fa2b0b7439..fe627836bf258 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -141,8 +141,9 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
       // it llvm-spirv aborts with exit code 18.
       TrArgs.push_back(
           "--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups"
-          ",+SPV_EXT_shader_atomic_float_add"
-          ",+SPV_EXT_relaxed_printf_string_address_space");
+          ",+SPV_EXT_relaxed_printf_string_address_space"
+          ",+SPV_KHR_bit_instructions"
+          ",+SPV_EXT_shader_atomic_float_add");
 
       InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, "");
       SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs);
@@ -165,7 +166,7 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
     Cc1Args.push_back("-emit-obj");
 
     // SPIR-V extensions the chipStar runtime relies on. Keep in sync with the
-    // llvm-spirv translator path above and the clang-linker-wrapper fallback.
+    // llvm-spirv translator path above.
     Cc1Args.push_back("-mllvm");
     Cc1Args.push_back("-spirv-ext=+SPV_INTEL_function_pointers"
                       ",+SPV_INTEL_subgroups"

>From 71d121cc8aba0afcfce0baa923b9de8edb7fcedb Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Wed, 1 Jul 2026 14:16:57 +0300
Subject: [PATCH 10/17] [HIPSPV] Drop the ineffective SPIRVToolChain ChipStar
 clause

A spirv64-unknown-chipstar (HIP) target routes to HIPSPVToolChain, never
to SPIRVToolChain: the OS switch in getToolChain sends OS=ChipStar to
HIPSPVToolChain, and offloaded HIP to spirv goes there too. SPIRVToolChain
is only built for Vulkan/ShaderModel or the OS-default arch path (and its
SPIRVOpenMPToolChain subclass, only for OpenMP offload). So making
NativeLLVMSupport true for ChipStar here has no effect on the chipStar
flow - which drives its own llvm-link/opt/backend command and bypasses the
HasNativeLLVMSupport linker gate anyway. Drop the dead clause.
---
 clang/lib/Driver/ToolChains/SPIRV.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/SPIRV.cpp 
b/clang/lib/Driver/ToolChains/SPIRV.cpp
index c6a0b667971fb..5cc1eec74c1cc 100644
--- a/clang/lib/Driver/ToolChains/SPIRV.cpp
+++ b/clang/lib/Driver/ToolChains/SPIRV.cpp
@@ -201,8 +201,7 @@ 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) || isUsingLTO(Args) 
||
-                      Triple.getOS() == llvm::Triple::ChipStar;
+  NativeLLVMSupport = Args.hasArg(options::OPT_sycl_link) || isUsingLTO(Args);
 
   // Lookup binaries into the driver directory.
   getProgramPaths().push_back(getDriver().Dir);

>From 069d6eb9c712f888baf03cff340ac42d36444f9b Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Wed, 1 Jul 2026 14:47:57 +0300
Subject: [PATCH 11/17] [HIPSPV] Detect versioned llvm-spirv on the chipStar
 path

The chipStar branch decided translator-vs-in-tree-backend by probing only
the unversioned "llvm-spirv", but SPIRV::constructTranslateCommand runs
"llvm-spirv-<major>" in preference. So an install shipping only
llvm-spirv-<major> was mis-detected as "no translator" and silently fell
back to the in-tree backend. Probe the versioned name first, matching the
binary that actually runs, and restore the chipStar VERSIONED test that
covers it.
---
 clang/lib/Driver/ToolChains/HIPSPV.cpp | 29 ++++++++++++++++++--------
 clang/test/Driver/hipspv-toolchain.hip |  8 +++++++
 2 files changed, 28 insertions(+), 9 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp 
b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index fe627836bf258..132bbad2fb3fd 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/Config/llvm-config.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Program.h"
@@ -113,17 +114,27 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
                                Args, Name, TempFile);
 
     // Prefer the external llvm-spirv translator when it is available next to
-    // the toolchain - required when LLVM is built without the in-tree SPIR-V
-    // target. Use findProgramByName (not GetProgramPath) so a missing binary
-    // is reported as not-found rather than being silently substituted.
+    // the toolchain (or on PATH) - required when LLVM is built without the
+    // in-tree SPIR-V target. Mirror SPIRV::constructTranslateCommand, which
+    // prefers the versioned "llvm-spirv-<major>" over the plain name, so the
+    // translator-vs-backend choice matches the binary that will actually run.
+    // Use findProgramByName (not GetProgramPath) so a missing binary is
+    // reported as not-found rather than being silently substituted by its 
name.
     std::string LLVMSpirvPath;
     {
-      llvm::ErrorOr<std::string> P = llvm::sys::findProgramByName(
-          "llvm-spirv", {llvm::StringRef(C.getDriver().Dir)});
-      if (!P)
-        P = llvm::sys::findProgramByName("llvm-spirv");
-      if (P)
-        LLVMSpirvPath = *P;
+      StringRef DriverDir(C.getDriver().Dir);
+      std::string Versioned =
+          "llvm-spirv-" + std::to_string(LLVM_VERSION_MAJOR);
+      for (StringRef Name : {StringRef(Versioned), StringRef("llvm-spirv")}) {
+        llvm::ErrorOr<std::string> P =
+            llvm::sys::findProgramByName(Name, {DriverDir});
+        if (!P)
+          P = llvm::sys::findProgramByName(Name);
+        if (P) {
+          LLVMSpirvPath = *P;
+          break;
+        }
+      }
     }
 
     if (!LLVMSpirvPath.empty()) {
diff --git a/clang/test/Driver/hipspv-toolchain.hip 
b/clang/test/Driver/hipspv-toolchain.hip
index b05bdb1272cab..621ac93f7f81e 100644
--- a/clang/test/Driver/hipspv-toolchain.hip
+++ b/clang/test/Driver/hipspv-toolchain.hip
@@ -129,6 +129,14 @@
 // RUN:   | FileCheck -DVERSION=%llvm-version-major \
 // RUN:   --check-prefix=VERSIONED %s
 
+// The chipStar path must make the same choice: a versioned llvm-spirv on PATH
+// is preferred over the in-tree backend (detection mirrors the translator
+// lookup in SPIRV::constructTranslateCommand).
+// 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" 2>&1 \
+// RUN:   | FileCheck -DVERSION=%llvm-version-major --check-prefix=VERSIONED %s
+
 // VERSIONED: {{.*}}llvm-spirv-[[VERSION]]
 
 //-----------------------------------------------------------------------------

>From 83beedcf298c4b2e4cd24f58fd4f0b1adc5246ea Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Wed, 1 Jul 2026 16:08:34 +0300
Subject: [PATCH 12/17] [HIPSPV] Drop the llvm-link input-loop
 comment/const-ref churn

The isFilename() guard on the llvm-link input loop already landed upstream
in #187655; this branch only added an explanatory comment and an auto ->
const auto& tweak. Revert that hunk to keep the PR focused on the SPIR-V
backend work.
---
 clang/lib/Driver/ToolChains/HIPSPV.cpp | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp 
b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index 132bbad2fb3fd..5743e833f8d98 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -83,11 +83,7 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
   // Link LLVM bitcode.
   ArgStringList LinkArgs{};
 
-  // The new offload driver can pass non-filename InputInfo entries (e.g.
-  // Nothing/InputArg placeholders) alongside the real bitcode inputs. Calling
-  // getFilename() on those reads garbage from the union; only forward genuine
-  // filename inputs to llvm-link.
-  for (const auto &Input : Inputs)
+  for (auto Input : Inputs)
     if (Input.isFilename())
       LinkArgs.push_back(Input.getFilename());
 

>From e0ae5d1bcb3b555b0e4294450ad2410938f4c6af Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Sat, 18 Jul 2026 14:31:14 +0300
Subject: [PATCH 13/17] [HIPSPV] Only cap SPIR-V version when the triple has no
 subarch

---
 clang/lib/Driver/ToolChains/HIPSPV.cpp | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp 
b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index 5743e833f8d98..fb65e5a553c86 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -136,10 +136,7 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
     if (!LLVMSpirvPath.empty()) {
       // External translator path: BC -> SPIR-V via llvm-spirv.
       llvm::opt::ArgStringList TrArgs;
-      // Match clang-linker-wrapper's chipstar version selection.
-      if (T.getSubArch() == llvm::Triple::SPIRVSubArch_v13)
-        TrArgs.push_back("--spirv-max-version=1.3");
-      else
+      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. SPV_EXT_relaxed_printf_string_address_space is required to

>From b232e22b8e0853fd5a2bdcf371a0c7e8954540ef Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Sat, 18 Jul 2026 14:52:21 +0300
Subject: [PATCH 14/17] [HIPSPV] Format translator extension list one extension
 per line

---
 clang/lib/Driver/ToolChains/HIPSPV.cpp | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp 
b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index fb65e5a553c86..3ae0b1e7db650 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -143,11 +143,12 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
       // translate modules whose printf format string is not in the constant
       // address space (e.g. device-side assert / dynamic %s lowering); without
       // it llvm-spirv aborts with exit code 18.
-      TrArgs.push_back(
-          "--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups"
-          ",+SPV_EXT_relaxed_printf_string_address_space"
-          ",+SPV_KHR_bit_instructions"
-          ",+SPV_EXT_shader_atomic_float_add");
+      TrArgs.push_back("--spirv-ext=-all"
+                       ",+SPV_INTEL_function_pointers"
+                       ",+SPV_INTEL_subgroups"
+                       ",+SPV_EXT_relaxed_printf_string_address_space"
+                       ",+SPV_KHR_bit_instructions"
+                       ",+SPV_EXT_shader_atomic_float_add");
 
       InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, "");
       SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs);

>From d1ae0a5ef1729428b5abf1e0190e13dfa2467645 Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Sat, 18 Jul 2026 15:20:22 +0300
Subject: [PATCH 15/17] [HIPSPV] Drop redundant arch default in linker wrapper

The clang driver already defaults SPIR-V HIP offload arch to "generic"
(Driver::getOffloadArchs and the packager job in Clang.cpp), so
fatbinary() never sees an empty arch from driver-produced binaries.
---
 .../clang-linker-wrapper/ClangLinkerWrapper.cpp      | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp 
b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
index 9910ceb1a339a..c2de6578773c7 100644
--- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
+++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
@@ -473,17 +473,9 @@ fatbinary(ArrayRef<std::tuple<StringRef, StringRef, 
StringRef>> InputFiles,
   SmallVector<StringRef> Targets = {
       Saver.save("-targets=host-" + HostTriple.normalize())};
   for (const auto &[File, TripleRef, Arch] : InputFiles) {
-    llvm::Triple T(TripleRef);
-    // SPIR-V HIP targets (e.g. chipStar) can arrive without an arch, but the
-    // offload bundler requires a non-empty arch field; default to the
-    // canonical SPIR-V CPU model "generic".
-    StringRef EffectiveArch = Arch;
-    if (EffectiveArch.empty() && T.isSPIRV())
-      EffectiveArch = "generic";
     std::string NormalizedTriple =
-        normalizeForBundler(T, !EffectiveArch.empty());
-    Targets.push_back(
-        Saver.save("hip-" + NormalizedTriple + "-" + EffectiveArch));
+        normalizeForBundler(Triple(TripleRef), !Arch.empty());
+    Targets.push_back(Saver.save("hip-" + NormalizedTriple + "-" + Arch));
   }
   CmdArgs.push_back(Saver.save(llvm::join(Targets, ",")));
 

>From bf7d78bab1d1f3ed7175163fd2fa0c79f686ac62 Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Tue, 21 Jul 2026 15:14:14 +0300
Subject: [PATCH 16/17] [HIPSPV] Drop
 SPV_EXT_relaxed_printf_string_address_space

No tested OpenCL runtime accepts SPIR-V modules using this extension:
Intel compute-runtime rejects them at clBuildProgram, rusticl aborts,
and Mali returns CL_INVALID_VALUE from clCreateProgramWithIL. Allowing
it only defers the failure from compile time to load time. Non-constant
printf format strings (device-side assert) are instead handled in
chipStar's device library.
---
 clang/lib/Driver/ToolChains/HIPSPV.cpp | 7 +------
 clang/test/Driver/hipspv-toolchain.hip | 4 ++--
 2 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp 
b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index 3ae0b1e7db650..8281ffae45ce8 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -139,14 +139,10 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
       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. SPV_EXT_relaxed_printf_string_address_space is required to
-      // translate modules whose printf format string is not in the constant
-      // address space (e.g. device-side assert / dynamic %s lowering); without
-      // it llvm-spirv aborts with exit code 18.
+      // below.
       TrArgs.push_back("--spirv-ext=-all"
                        ",+SPV_INTEL_function_pointers"
                        ",+SPV_INTEL_subgroups"
-                       ",+SPV_EXT_relaxed_printf_string_address_space"
                        ",+SPV_KHR_bit_instructions"
                        ",+SPV_EXT_shader_atomic_float_add");
 
@@ -175,7 +171,6 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
     Cc1Args.push_back("-mllvm");
     Cc1Args.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");
 
diff --git a/clang/test/Driver/hipspv-toolchain.hip 
b/clang/test/Driver/hipspv-toolchain.hip
index 621ac93f7f81e..8d1a60d527dba 100644
--- a/clang/test/Driver/hipspv-toolchain.hip
+++ b/clang/test/Driver/hipspv-toolchain.hip
@@ -91,7 +91,7 @@
 
 //      CHIPSTAR: {{".*clang.*"}} "-cc1" "-triple" "spirv64-unknown-chipstar"
 // CHIPSTAR-SAME: "-emit-obj"
-// 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: "-mllvm" 
"-spirv-ext=+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add"
 // CHIPSTAR-SAME: [[LOWER_BC]] "-o" "[[SPIRV_OUT:.*img]]"
 
 // RUN: env "PATH=" %clang -### --no-default-config -o %t.dummy.img \
@@ -108,7 +108,7 @@
 
 //      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_EXT_relaxed_printf_string_address_space,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add"
+// CHIPSTAR-SUBARCH-SAME: "-mllvm" 
"-spirv-ext=+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add"
 // CHIPSTAR-SUBARCH-SAME: [[LOWER_BC]] "-o" "[[SPIRV_OUT:.*img]]"
 
 // Check unknown linker options are ignored - such as ones that are targeted at

>From 20768d5c3a7893a1ce763d494a1841396637cbe3 Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Wed, 22 Jul 2026 12:25:48 +0300
Subject: [PATCH 17/17] [HIPSPV] Select SPIR-V emitter with
 -f[no-]integrated-objemitter

Use the existing integrated-backend machinery instead of probing for
llvm-spirv on PATH: the in-tree SPIR-V backend is the default (when
built), and -fno-integrated-objemitter selects the external llvm-spirv
translator, located as in SPIRV::constructTranslateCommand. This keeps
a stray llvm-spirv in PATH from silently changing the emitter.
---
 clang/lib/Driver/ToolChains/HIPSPV.cpp | 58 ++++++++++++--------------
 clang/lib/Driver/ToolChains/HIPSPV.h   |  4 ++
 clang/test/Driver/hipspv-toolchain.hip | 35 +++++++++++-----
 3 files changed, 56 insertions(+), 41 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp 
b/clang/lib/Driver/ToolChains/HIPSPV.cpp
index 8281ffae45ce8..1e29fec12df14 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.cpp
+++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp
@@ -13,10 +13,9 @@
 #include "clang/Driver/Driver.h"
 #include "clang/Driver/InputInfo.h"
 #include "clang/Options/Options.h"
-#include "llvm/Config/llvm-config.h"
+#include "llvm/MC/TargetRegistry.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
-#include "llvm/Support/Program.h"
 
 using namespace clang::driver;
 using namespace clang::driver::toolchains;
@@ -100,40 +99,16 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
   auto T = getToolChain().getTriple();
 
   if (T.getOS() == llvm::Triple::ChipStar) {
-    // chipStar: run HipSpvPasses via opt, then emit SPIR-V either via the
-    // external llvm-spirv translator (if available) or the in-tree SPIR-V
-    // backend (mirrors the fallback in clang-linker-wrapper).
+    // 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);
 
-    // Prefer the external llvm-spirv translator when it is available next to
-    // the toolchain (or on PATH) - required when LLVM is built without the
-    // in-tree SPIR-V target. Mirror SPIRV::constructTranslateCommand, which
-    // prefers the versioned "llvm-spirv-<major>" over the plain name, so the
-    // translator-vs-backend choice matches the binary that will actually run.
-    // Use findProgramByName (not GetProgramPath) so a missing binary is
-    // reported as not-found rather than being silently substituted by its 
name.
-    std::string LLVMSpirvPath;
-    {
-      StringRef DriverDir(C.getDriver().Dir);
-      std::string Versioned =
-          "llvm-spirv-" + std::to_string(LLVM_VERSION_MAJOR);
-      for (StringRef Name : {StringRef(Versioned), StringRef("llvm-spirv")}) {
-        llvm::ErrorOr<std::string> P =
-            llvm::sys::findProgramByName(Name, {DriverDir});
-        if (!P)
-          P = llvm::sys::findProgramByName(Name);
-        if (P) {
-          LLVMSpirvPath = *P;
-          break;
-        }
-      }
-    }
-
-    if (!LLVMSpirvPath.empty()) {
+    if (!getToolChain().useIntegratedBackend()) {
       // External translator path: BC -> SPIR-V via llvm-spirv.
       llvm::opt::ArgStringList TrArgs;
       if (T.getSubArch() == llvm::Triple::NoSubArch)
@@ -146,12 +121,21 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
                        ",+SPV_KHR_bit_instructions"
                        ",+SPV_EXT_shader_atomic_float_add");
 
+      // Preserve debug info requested via -g (see the comment on the
+      // equivalent block in the non-chipStar path below).
+      if (const Arg *A = Args.getLastArg(options::OPT_g_Group);
+          A && !A->getOption().matches(options::OPT_g0)) {
+        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;
     }
 
-    // Fallback: compile the lowered bitcode to SPIR-V with the in-tree 
backend.
+    // 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,
@@ -259,6 +243,18 @@ 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 used when it is built.
+  std::string IgnoredError;
+  return llvm::TargetRegistry::lookupTarget(getTriple(), IgnoredError);
+}
+
+bool HIPSPVToolChain::IsIntegratedBackendDefault() const {
+  // Prefer the in-tree SPIR-V backend; fall back to the external llvm-spirv
+  // translator when the backend is not built.
+  return IsIntegratedBackendSupported();
+}
+
 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 fb53d65b335f5..176ef5ddde8b3 100644
--- a/clang/lib/Driver/ToolChains/HIPSPV.h
+++ b/clang/lib/Driver/ToolChains/HIPSPV.h
@@ -55,6 +55,10 @@ class LLVM_LIBRARY_VISIBILITY HIPSPVToolChain final : public 
ToolChain {
     return HostTC ? &HostTC->getTriple() : nullptr;
   }
 
+  bool IsIntegratedBackendDefault() const override;
+  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-toolchain.hip 
b/clang/test/Driver/hipspv-toolchain.hip
index 8d1a60d527dba..7ca217aabf545 100644
--- a/clang/test/Driver/hipspv-toolchain.hip
+++ b/clang/test/Driver/hipspv-toolchain.hip
@@ -76,8 +76,10 @@
 // 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: env "PATH=" %clang -### --no-default-config -o %t.dummy.img \
+// RUN: %clang -### --no-default-config -o %t.dummy.img \
 // RUN:   --target=spirv64-unknown-chipstar %t.dummy.o \
 // RUN:   --hip-path="%S/Inputs/hipspv" \
 // RUN: 2>&1 | FileCheck %s --check-prefix=CHIPSTAR -DHIP_PATH=%S/Inputs/hipspv
@@ -94,7 +96,7 @@
 // CHIPSTAR-SAME: "-mllvm" 
"-spirv-ext=+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add"
 // CHIPSTAR-SAME: [[LOWER_BC]] "-o" "[[SPIRV_OUT:.*img]]"
 
-// RUN: env "PATH=" %clang -### --no-default-config -o %t.dummy.img \
+// RUN: %clang -### --no-default-config -o %t.dummy.img \
 // RUN:   --target=spirv64v1.3-unknown-chipstar \
 // RUN:   %t.dummy.o --hip-path="%S/Inputs/hipspv" \
 // RUN: 2>&1 | FileCheck %s --check-prefix=CHIPSTAR-SUBARCH 
-DHIP_PATH=%S/Inputs/hipspv
@@ -111,9 +113,23 @@
 // CHIPSTAR-SUBARCH-SAME: "-mllvm" 
"-spirv-ext=+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add"
 // 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: [[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: env "PATH=" %clang -### --no-default-config -o %t.dummy.img \
+// RUN: %clang -### --no-default-config -o %t.dummy.img \
 // RUN:   --target=spirv64-unknown-chipstar %t.dummy.o \
 // RUN:   --hip-path="%S/Inputs/hipspv" -Xlinker foobar \
 // RUN: 2>&1 | FileCheck %s --check-prefix=CHIPSTAR -DHIP_PATH=%S/Inputs/hipspv
@@ -129,12 +145,11 @@
 // RUN:   | FileCheck -DVERSION=%llvm-version-major \
 // RUN:   --check-prefix=VERSIONED %s
 
-// The chipStar path must make the same choice: a versioned llvm-spirv on PATH
-// is preferred over the in-tree backend (detection mirrors the translator
-// lookup in SPIRV::constructTranslateCommand).
+// 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.img --target=spirv64-unknown-chipstar %t.dummy.o \
-// RUN:   --hip-path="%S/Inputs/hipspv" 2>&1 \
+// RUN:   --hip-path="%S/Inputs/hipspv" -fno-integrated-objemitter 2>&1 \
 // RUN:   | FileCheck -DVERSION=%llvm-version-major --check-prefix=VERSIONED %s
 
 // VERSIONED: {{.*}}llvm-spirv-[[VERSION]]
@@ -145,17 +160,17 @@
 // translator, and that no debug flags are emitted without -g.
 // RUN: %clang -### --no-default-config -g -o %t.dummy.img \
 // RUN:   --target=spirv64-unknown-chipstar %t.dummy.o \
-// RUN:   --hip-path="%S/Inputs/hipspv" \
+// RUN:   --hip-path="%S/Inputs/hipspv" -fno-integrated-objemitter \
 // RUN: 2>&1 | FileCheck %s --check-prefix=DEBUG
 
 //      DEBUG: {{".*llvm-spirv"}} "--spirv-max-version=1.2"
-// DEBUG-SAME: 
"--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups"
+// DEBUG-SAME: 
"--spirv-ext=-all,+SPV_INTEL_function_pointers,+SPV_INTEL_subgroups,+SPV_KHR_bit_instructions,+SPV_EXT_shader_atomic_float_add"
 // DEBUG-SAME: "--spirv-ext=+SPV_KHR_non_semantic_info,+SPV_INTEL_optnone"
 // DEBUG-SAME: "--spirv-debug-info-version=nonsemantic-shader-200"
 
 // RUN: %clang -### --no-default-config -o %t.dummy.img \
 // RUN:   --target=spirv64-unknown-chipstar %t.dummy.o \
-// RUN:   --hip-path="%S/Inputs/hipspv" \
+// RUN:   --hip-path="%S/Inputs/hipspv" -fno-integrated-objemitter \
 // RUN: 2>&1 | FileCheck %s --check-prefix=NODEBUG
 
 //      NODEBUG: {{".*llvm-spirv"}}

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

Reply via email to