From 885417050bea4d76ba033d208773b44006a4df4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry=20Linjam=C3=A4ki?= Date: Tue, 11 Nov 2025 12:17:58 +0200 Subject: [PATCH 1/3] Lift HIPSPV onto the new offload driver --- clang/include/clang/Options/Options.td | 4 ++ clang/lib/Driver/Driver.cpp | 21 +++++--- clang/lib/Driver/ToolChains/Clang.cpp | 14 ++++-- clang/lib/Driver/ToolChains/HIPSPV.cpp | 48 +++++++++++++++---- clang/lib/Driver/ToolChains/HIPSPV.h | 7 ++- .../ClangLinkerWrapper.cpp | 29 ++++++++--- 6 files changed, 94 insertions(+), 29 deletions(-) diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 2f7434d8afe11..838da6c270233 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -999,6 +999,10 @@ def Xthinlto_distributor_EQ : CommaJoined<["-"], "Xthinlto-distributor=">, "multiple times or with comma-separated values.">, MetaVarName<"">, Group; +def Xoffload_compiler : JoinedAndSeparate<["-"], "Xoffload-compiler">, + Visibility<[ClangOption, FlangOption]>, + HelpText<"Pass to the offload compilers or the ones identified by -">, + MetaVarName<" ">, Group; def Xoffload_linker : JoinedAndSeparate<["-"], "Xoffload-linker">, Visibility<[ClangOption, FlangOption]>, HelpText<"Pass to the offload linkers or the ones identified by -">, diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 9fd64d4aac514..e9e2222e024a1 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -4999,13 +4999,17 @@ Action *Driver::BuildOffloadingActions(Compilation &C, // Compiling HIP in device-only non-RDC mode requires linking each action // individually. for (Action *&A : DeviceActions) { - // Special handling for the HIP SPIR-V toolchain because it doesn't use + // Special handling for the HIP SPIR-V toolchains because they don't use // the SPIR-V backend yet doesn't report the output as an object. - bool IsAMDGCNSPIRV = A->getOffloadingToolChain() && - A->getOffloadingToolChain()->getTriple().getOS() == - llvm::Triple::OSType::AMDHSA && - A->getOffloadingToolChain()->getTriple().isSPIRV(); - if ((A->getType() != types::TY_Object && !IsAMDGCNSPIRV && + auto OsName = A->getOffloadingToolChain() + ? A->getOffloadingToolChain()->getTriple().getOSName() + : StringRef(); + bool IsHIPSPRIV = A->getOffloadingToolChain() && + A->getOffloadingToolChain()->getTriple().isSPIRV() && + (A->getOffloadingToolChain()->getTriple().getOS() == + llvm::Triple::OSType::AMDHSA || + OsName == "hipspv" || OsName == "chipstar"); + if ((A->getType() != types::TY_Object && !IsHIPSPRIV && A->getType() != types::TY_LTO_BC) || HIPRelocatableObj || !HIPNoRDC || !offloadDeviceOnly()) continue; @@ -6990,6 +6994,11 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, break; case llvm::Triple::spirv32: case llvm::Triple::spirv64: + if (Target.getOSName() == "hipspv") { + TC = std::make_unique(*this, Target, + Args); + break; + } TC = std::make_unique(*this, Target, Args); break; case llvm::Triple::csky: diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 80389937ee218..2748f21e0bd75 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -9051,6 +9051,7 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, OPT_v, OPT_cuda_path_EQ, OPT_rocm_path_EQ, + OPT_hip_path_EQ, OPT_O_Group, OPT_g_Group, OPT_g_flags_Group, @@ -9178,18 +9179,21 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, Linker->ConstructJob(C, JA, Output, Inputs, Args, LinkingOutput); const auto &LinkCommand = C.getJobs().getJobs().back(); - // Forward -Xoffload-linker<-triple> arguments to the device link job. - for (Arg *A : Args.filtered(options::OPT_Xoffload_linker)) { + for (Arg *A : + Args.filtered(options::OPT_Xoffload_compiler, OPT_Xoffload_linker)) { StringRef Val = A->getValue(0); + bool IsLinkJob = A->getOption().getID() == OPT_Xoffload_linker; + auto WrapperOption = + IsLinkJob ? Twine("--device-linker=") : Twine("--device-compiler="); if (Val.empty()) - CmdArgs.push_back( - Args.MakeArgString(Twine("--device-linker=") + A->getValue(1))); + CmdArgs.push_back(Args.MakeArgString(WrapperOption + A->getValue(1))); else CmdArgs.push_back(Args.MakeArgString( - "--device-linker=" + + WrapperOption + ToolChain::getOpenMPTriple(Val.drop_front()).getTriple() + "=" + A->getValue(1))); } + Args.ClaimAllArgs(options::OPT_Xoffload_compiler); Args.ClaimAllArgs(options::OPT_Xoffload_linker); // Embed bitcode instead of an object in JIT mode. diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp b/clang/lib/Driver/ToolChains/HIPSPV.cpp index be0f49d8e1497..4df241508f39e 100644 --- a/clang/lib/Driver/ToolChains/HIPSPV.cpp +++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp @@ -119,7 +119,16 @@ void HIPSPV::Linker::ConstructJob(Compilation &C, const JobAction &JA, HIPSPVToolChain::HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple, const ToolChain &HostTC, const ArgList &Args) - : ToolChain(D, Triple, Args), HostTC(HostTC) { + : ToolChain(D, Triple, Args), HostTC(&HostTC) { + // Lookup binaries into the driver directory, this is used to + // discover the clang-offload-bundler executable. + getProgramPaths().push_back(getDriver().Dir); +} + +// Non-offloading toolchain. Primaly used by clang-offload-linker. +HIPSPVToolChain::HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple, + const ArgList &Args) + : ToolChain(D, Triple, Args), HostTC(nullptr) { // Lookup binaries into the driver directory, this is used to // discover the clang-offload-bundler executable. getProgramPaths().push_back(getDriver().Dir); @@ -128,7 +137,14 @@ HIPSPVToolChain::HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple, void HIPSPVToolChain::addClangTargetOptions( const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, Action::OffloadKind DeviceOffloadingKind) const { - HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind); + + if (!HostTC) { + assert(DeviceOffloadingKind == Action::OFK_None && + "Need host toolchain for offloading!"); + return; + } + + HostTC->addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind); assert(DeviceOffloadingKind == Action::OFK_HIP && "Only HIP offloading kinds are supported for GPUs."); @@ -159,27 +175,37 @@ Tool *HIPSPVToolChain::buildLinker() const { } void HIPSPVToolChain::addClangWarningOptions(ArgStringList &CC1Args) const { - HostTC.addClangWarningOptions(CC1Args); + if (HostTC) + HostTC->addClangWarningOptions(CC1Args); + ToolChain::addClangWarningOptions(CC1Args); } ToolChain::CXXStdlibType HIPSPVToolChain::GetCXXStdlibType(const ArgList &Args) const { - return HostTC.GetCXXStdlibType(Args); + if (HostTC) + return HostTC->GetCXXStdlibType(Args); + return ToolChain::GetCXXStdlibType(Args); } void HIPSPVToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const { - HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args); + if (HostTC) + HostTC->AddClangSystemIncludeArgs(DriverArgs, CC1Args); + ToolChain::AddClangSystemIncludeArgs(DriverArgs, CC1Args); } void HIPSPVToolChain::AddClangCXXStdlibIncludeArgs( const ArgList &Args, ArgStringList &CC1Args) const { - HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args); + if (HostTC) + HostTC->AddClangCXXStdlibIncludeArgs(Args, CC1Args); + ToolChain::AddClangCXXStdlibIncludeArgs(Args, CC1Args); } void HIPSPVToolChain::AddIAMCUIncludeArgs(const ArgList &Args, ArgStringList &CC1Args) const { - HostTC.AddIAMCUIncludeArgs(Args, CC1Args); + if (HostTC) + HostTC->AddIAMCUIncludeArgs(Args, CC1Args); + ToolChain::AddIAMCUIncludeArgs(Args, CC1Args); } void HIPSPVToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs, @@ -273,12 +299,16 @@ SanitizerMask HIPSPVToolChain::getSupportedSanitizers() const { // This behavior is necessary because the host and device toolchains // invocations often share the command line, so the device toolchain must // tolerate flags meant only for the host toolchain. - return HostTC.getSupportedSanitizers(); + if (HostTC) + return HostTC->getSupportedSanitizers(); + return ToolChain::getSupportedSanitizers(); } VersionTuple HIPSPVToolChain::computeMSVCVersion(const Driver *D, const ArgList &Args) const { - return HostTC.computeMSVCVersion(D, Args); + if (HostTC) + return HostTC->computeMSVCVersion(D, Args); + return ToolChain::computeMSVCVersion(D, Args); } void HIPSPVToolChain::adjustDebugInfoKind( diff --git a/clang/lib/Driver/ToolChains/HIPSPV.h b/clang/lib/Driver/ToolChains/HIPSPV.h index caf6924151446..068040ee4f491 100644 --- a/clang/lib/Driver/ToolChains/HIPSPV.h +++ b/clang/lib/Driver/ToolChains/HIPSPV.h @@ -47,9 +47,12 @@ class LLVM_LIBRARY_VISIBILITY HIPSPVToolChain final : public ToolChain { public: HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple, const ToolChain &HostTC, const llvm::opt::ArgList &Args); + HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple, + const llvm::opt::ArgList &Args); const llvm::Triple *getAuxTriple() const override { - return &HostTC.getTriple(); + assert(HostTC); + return &HostTC->getTriple(); } void @@ -90,7 +93,7 @@ class LLVM_LIBRARY_VISIBILITY HIPSPVToolChain final : public ToolChain { bool isPICDefaultForced() const override { return false; } bool SupportsProfiling() const override { return false; } - const ToolChain &HostTC; + const ToolChain *HostTC = nullptr; protected: Tool *buildLinker() const override; diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index bfeca17d2147e..0f4fb761c794e 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -407,8 +407,19 @@ fatbinary(ArrayRef> InputFiles, } // namespace nvptx namespace amdgcn { + +// Constructs a triple string for clang offload bundler. +// NOTE: copied from HIPUtility.cpp. +static std::string normalizeForBundler(const llvm::Triple &T, + bool HasTargetID) { + return HasTargetID ? (T.getArchName() + "-" + T.getVendorName() + "-" + + T.getOSName() + "-" + T.getEnvironmentName()) + .str() + : T.normalize(llvm::Triple::CanonicalForm::FOUR_IDENT); +} + Expected -fatbinary(ArrayRef> InputFiles, +fatbinary(ArrayRef> InputFiles, const ArgList &Args) { llvm::TimeTraceScope TimeScope("AMDGPU Fatbinary"); @@ -439,8 +450,11 @@ fatbinary(ArrayRef> InputFiles, Args.MakeArgString(Twine("-compression-level=") + Arg->getValue())); SmallVector Targets = {"-targets=host-x86_64-unknown-linux-gnu"}; - for (const auto &[File, Arch] : InputFiles) - Targets.push_back(Saver.save("hip-amdgcn-amd-amdhsa--" + Arch)); + for (const auto &[File, TripleRef, Arch] : InputFiles) { + std::string NormalizedTriple = + normalizeForBundler(Triple(TripleRef), !Arch.empty()); + Targets.push_back(Saver.save("hip-" + NormalizedTriple + "-" + Arch)); + } CmdArgs.push_back(Saver.save(llvm::join(Targets, ","))); #ifdef _WIN32 @@ -448,7 +462,7 @@ fatbinary(ArrayRef> InputFiles, #else CmdArgs.push_back("-input=/dev/null"); #endif - for (const auto &[File, Arch] : InputFiles) + for (const auto &[File, Triple, Arch] : InputFiles) CmdArgs.push_back(Saver.save("-input=" + File)); CmdArgs.push_back(Saver.save("-output=" + *TempFileOrErr)); @@ -812,10 +826,11 @@ bundleCuda(ArrayRef Images, const ArgList &Args) { Expected>> bundleHIP(ArrayRef Images, const ArgList &Args) { - SmallVector, 4> InputFiles; + SmallVector, 4> InputFiles; for (const OffloadingImage &Image : Images) - InputFiles.emplace_back(std::make_pair(Image.Image->getBufferIdentifier(), - Image.StringData.lookup("arch"))); + InputFiles.emplace_back(std::make_tuple(Image.Image->getBufferIdentifier(), + Image.StringData.lookup("triple"), + Image.StringData.lookup("arch"))); auto FileOrErr = amdgcn::fatbinary(InputFiles, Args); if (!FileOrErr) From 889e0f085fb8508aade9100f078a214c485c0b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry=20Linjam=C3=A4ki?= Date: Thu, 30 Nov 2023 14:42:23 +0200 Subject: [PATCH 2/3] HIPSPV: set allowed SPIR-V extensions for chipStar --- clang/lib/Driver/Driver.cpp | 3 ++- clang/lib/Driver/ToolChains/HIPSPV.cpp | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index e9e2222e024a1..71878c21bae7c 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -6994,7 +6994,8 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, break; case llvm::Triple::spirv32: case llvm::Triple::spirv64: - if (Target.getOSName() == "hipspv") { + if (Target.getOSName() == "hipspv" || + Target.getOSName() == "chipstar") { TC = std::make_unique(*this, Target, Args); break; diff --git a/clang/lib/Driver/ToolChains/HIPSPV.cpp b/clang/lib/Driver/ToolChains/HIPSPV.cpp index 4df241508f39e..6a074fe5ebd83 100644 --- a/clang/lib/Driver/ToolChains/HIPSPV.cpp +++ b/clang/lib/Driver/ToolChains/HIPSPV.cpp @@ -93,9 +93,21 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand( } // Emit SPIR-V binary. + llvm::opt::ArgStringList TrArgs; + if (getToolChain().getTriple().getOSName() == "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 target yet. + TrArgs = {"--spirv-max-version=1.2", + "--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 { + TrArgs = {"--spirv-max-version=1.1", "--spirv-ext=+all"}; + } - llvm::opt::ArgStringList TrArgs{"--spirv-max-version=1.1", - "--spirv-ext=+all"}; InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, ""); SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs); } From 33222660368fcaf5ad04937ee2b0f3281c36fc52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry=20Linjam=C3=A4ki?= Date: Thu, 20 Nov 2025 13:02:40 +0200 Subject: [PATCH 3/3] hipspv-toolchain.hip: test --offload-new-driver --- .../hipspv-spirv64-unknown-hipspv.bc | 0 clang/test/Driver/hipspv-toolchain.hip | 51 ++++++++++++------- 2 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 clang/test/Driver/Inputs/hipspv/lib/hip-device-lib/hipspv-spirv64-unknown-hipspv.bc diff --git a/clang/test/Driver/Inputs/hipspv/lib/hip-device-lib/hipspv-spirv64-unknown-hipspv.bc b/clang/test/Driver/Inputs/hipspv/lib/hip-device-lib/hipspv-spirv64-unknown-hipspv.bc new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/hipspv-toolchain.hip b/clang/test/Driver/hipspv-toolchain.hip index 3c175ebf433cc..76f767efbb457 100644 --- a/clang/test/Driver/hipspv-toolchain.hip +++ b/clang/test/Driver/hipspv-toolchain.hip @@ -2,37 +2,54 @@ // RUN: %clang -### -target x86_64-linux-gnu --offload=spirv64 \ // RUN: --no-offload-new-driver --hip-path=%S/Inputs/hipspv -nohipwrapperinc %s \ -// RUN: 2>&1 | FileCheck %s +// RUN: 2>&1 | FileCheck --check-prefixes=CHECK,OLD \ +// RUN: -DTRIPLE=spirv64 %s -// CHECK: [[CLANG:".*clang.*"]] "-cc1" "-triple" "spirv64" +// RUN: %clang -### -target x86_64-linux-gnu --offload=spirv64-unknown-hipspv \ +// RUN: --offload-new-driver --hip-path=%S/Inputs/hipspv -nohipwrapperinc %s \ +// RUN: 2>&1 | FileCheck --check-prefixes=CHECK,NEW \ +// RUN: -DTRIPLE=spirv64-unknown-hipspv -DHIP_PATH=%S/Inputs/hipspv %s + +// CHECK: [[CLANG:".*clang.*"]] "-cc1" "-triple" "[[TRIPLE]]" // CHECK-SAME: "-aux-triple" "{{.*}}" "-emit-llvm-bc" // CHECK-SAME: "-fcuda-is-device" // CHECK-SAME: "-fcuda-allow-variadic-functions" -// CHECK-SAME: "-mlink-builtin-bitcode" {{".*/hipspv/lib/hip-device-lib/hipspv-spirv64.bc"}} +// CHECK-SAME: "-mlink-builtin-bitcode" {{".*/hipspv/lib/hip-device-lib/hipspv-}}[[TRIPLE]].bc" // CHECK-SAME: "-isystem" {{".*/hipspv/include"}} // CHECK-SAME: "-fhip-new-launch-api" -// CHECK-SAME: "-o" [[DEV_BC:".*bc"]] +// CHECK-SAME: "-o" "[[OBJ_DEV:.*(o|bc)]]" // CHECK-SAME: "-x" "hip" -// CHECK: {{".*llvm-link"}} [[DEV_BC]] "-o" [[LINK_BC:".*bc"]] +// OLD: {{".*llvm-link"}} "[[OBJ_DEV]]" "-o" [[LINK_BC:".*bc"]] + +// NEW: {{".*llvm-offload-binary"}} "-o" "[[PACKAGE:.*.out]]" +// NEW-SAME: "--image=file=[[OBJ_DEV]],triple=[[TRIPLE]],arch=generic,kind=hip" -// CHECK: {{".*opt"}} [[LINK_BC]] "-load-pass-plugin" -// CHECK-SAME: {{".*/hipspv/lib/libLLVMHipSpvPasses.so"}} -// CHECK-SAME: "-passes=hip-post-link-passes" "-o" [[LOWER_BC:".*bc"]] +// OLD: {{".*opt"}} [[LINK_BC]] "-load-pass-plugin" +// OLD-SAME: {{".*/hipspv/lib/libLLVMHipSpvPasses.so"}} +// OLD-SAME: "-passes=hip-post-link-passes" "-o" [[LOWER_BC:".*bc"]] -// CHECK: {{".*llvm-spirv"}} "--spirv-max-version=1.1" "--spirv-ext=+all" -// CHECK-SAME: [[LOWER_BC]] "-o" "[[SPIRV_OUT:.*out]]" +// OLD: {{".*llvm-spirv"}} "--spirv-max-version=1.1" "--spirv-ext=+all" +// OLD-SAME: [[LOWER_BC]] "-o" "[[SPIRV_OUT:.*out]]" -// CHECK: {{".*clang-offload-bundler"}} "-type=o" "-bundle-align=4096" -// CHECK-SAME: "-targets=host-x86_64-unknown-linux-gnu,hip-spirv64----generic" -// CHECK-SAME: "-input={{.*}}" "-input=[[SPIRV_OUT]]" "-output=[[BUNDLE:.*hipfb]]" +// OLD: {{".*clang-offload-bundler"}} "-type=o" "-bundle-align=4096" +// OLD-SAME: "-targets=host-x86_64-unknown-linux-gnu,hip-spirv64----generic" +// OLD-SAME: "-input={{.*}}" "-input=[[SPIRV_OUT]]" "-output=[[BUNDLE:.*hipfb]]" -// CHECK: [[CLANG]] "-cc1" "-triple" {{".*"}} "-aux-triple" "spirv64" +// CHECK: [[CLANG]] "-cc1" "-triple" {{".*"}} "-aux-triple" "[[TRIPLE]]" // CHECK-SAME: "-emit-obj" -// CHECK-SAME: "-fcuda-include-gpubinary" "[[BUNDLE]]" -// CHECK-SAME: "-o" [[OBJ_HOST:".*o"]] "-x" "hip" +// OLD-SAME: "-fcuda-include-gpubinary" "[[BUNDLE]]" +// NEW-SAME: "-fembed-offload-object=[[PACKAGE]]" +// OLD-SAME: "-o" [[OBJ_HOST:".*o"]] "-x" "hip" +// NEW-SAME: "-o" [[OBJ_HOST_TMP:".*o"]] "-x" "hip" + +// NEW: {{".*clang-linker-wrapper"}} +// NEW-SAME: "--device-compiler=[[TRIPLE]]=--hip-path=[[HIP_PATH]]" +// NEW-SAME: {{.*}}"--host-triple=x86_64-unknown-linux-gnu" +// NEW-SAME: "--linker-path={{.*}}" "-o" [[OBJ_HOST:".*o"]] [[OBJ_HOST_TMP]] "-r" + +// OLD: {{".*ld.*"}} {{.*}}[[OBJ_HOST]] -// CHECK: {{".*ld.*"}} {{.*}}[[OBJ_HOST]] //----------------------------------------------------------------------------- // Check llvm-spirv- is used if it is found in PATH.