Skip to content

[SYCL][Driver] Deduplicate libspirv logic #19131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 4 additions & 41 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
#include "ToolChains/PS4CPU.h"
#include "ToolChains/RISCVToolchain.h"
#include "ToolChains/SPIRV.h"
#include "ToolChains/SYCL.h"
#include "ToolChains/SPIRVOpenMP.h"
#include "ToolChains/SYCL.h"
#include "ToolChains/Solaris.h"
Expand Down Expand Up @@ -5993,46 +5992,10 @@ class OffloadingActionBuilder final {
// For NVPTX and NativeCPU we need to also link libclc at the same stage
// that we link all of the unbundled SYCL libdevice objects together.
if (TC->getTriple().isNVPTX() || isNativeCPU) {
std::string LibSpirvFile;
if (Args.hasArg(options::OPT_fsycl_libspirv_path_EQ)) {
auto ProvidedPath =
Args.getLastArgValue(options::OPT_fsycl_libspirv_path_EQ).str();
if (llvm::sys::fs::exists(ProvidedPath))
LibSpirvFile = ProvidedPath;
} else {
SmallVector<StringRef, 2> LibraryPaths;

// Expected path w/out install.
SmallString<256> WithoutInstallPath(C.getDriver().ResourceDir);
llvm::sys::path::append(WithoutInstallPath, Twine("../../clc"));
LibraryPaths.emplace_back(WithoutInstallPath.c_str());

// Expected path w/ install.
SmallString<256> WithInstallPath(C.getDriver().ResourceDir);
llvm::sys::path::append(WithInstallPath, Twine("../../../share/clc"));
LibraryPaths.emplace_back(WithInstallPath.c_str());

// Select remangled libclc variant
StringRef LibSpirvTargetNamePref =
TC->getAuxTriple()->isOSWindows()
? "remangled-l32-signed_char.libspirv-"
: "remangled-l64-signed_char.libspirv-";

for (StringRef LibraryPath : LibraryPaths) {
SmallString<128> LibSpirvTargetFile(LibraryPath);
llvm::sys::path::append(LibSpirvTargetFile,
LibSpirvTargetNamePref +
TC->getTripleString() + ".bc");
if (llvm::sys::fs::exists(LibSpirvTargetFile) ||
Args.hasArg(options::OPT__HASH_HASH_HASH)) {
LibSpirvFile = std::string(LibSpirvTargetFile.str());
break;
}
}
}
if (!LibSpirvFile.empty()) {
Arg *LibClcInputArg = MakeInputArg(Args, C.getDriver().getOpts(),
Args.MakeArgString(LibSpirvFile));
if (const char *LibSpirvFile = SYCLInstallation.findLibspirvPath(
TC->getTriple(), Args, *TC->getAuxTriple())) {
Arg *LibClcInputArg =
MakeInputArg(Args, C.getDriver().getOpts(), LibSpirvFile);
auto *SYCLLibClcInputAction =
C.MakeAction<InputAction>(*LibClcInputArg, types::TY_LLVM_BC);
DeviceLinkObjects.push_back(SYCLLibClcInputAction);
Expand Down
58 changes: 3 additions & 55 deletions clang/lib/Driver/ToolChains/Cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -861,14 +861,6 @@ NVPTXToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,
return DAL;
}

// Select remangled libclc variant. 64-bit longs default, 32-bit longs on
// Windows
static const char *getLibSpirvTargetName(const ToolChain &HostTC) {
if (HostTC.getTriple().isOSWindows())
return "remangled-l32-signed_char.libspirv-nvptx64-nvidia-cuda.bc";
return "remangled-l64-signed_char.libspirv-nvptx64-nvidia-cuda.bc";
}

void NVPTXToolChain::addClangTargetOptions(
const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
Action::OffloadKind DeviceOffloadingKind) const {}
Expand Down Expand Up @@ -970,6 +962,9 @@ void CudaToolChain::addClangTargetOptions(
options::OPT_fno_sycl_id_queries_fit_in_int, false))
CC1Args.append(
{"-mllvm", "-nvvm-reflect-add=__CUDA_ID_QUERIES_FIT_IN_INT=1"});

SYCLInstallation.addLibspirvLinkArgs(getEffectiveTriple(), DriverArgs,
HostTC.getTriple(), CC1Args);
} else {
CC1Args.append({"-fcuda-is-device", "-mllvm",
"-enable-memcpyopt-without-libcalls",
Expand All @@ -988,53 +983,6 @@ void CudaToolChain::addClangTargetOptions(
CC1Args.append({"-std=c++17", "-fsycl-is-host"});
}

auto NoLibSpirv = DriverArgs.hasArg(options::OPT_fno_sycl_libspirv) ||
getDriver().offloadDeviceOnly();
if (DeviceOffloadingKind == Action::OFK_SYCL && !NoLibSpirv) {
std::string LibSpirvFile;

if (DriverArgs.hasArg(clang::driver::options::OPT_fsycl_libspirv_path_EQ)) {
auto ProvidedPath =
DriverArgs.getLastArgValue(clang::driver::options::OPT_fsycl_libspirv_path_EQ).str();
if (llvm::sys::fs::exists(ProvidedPath))
LibSpirvFile = ProvidedPath;
} else {
SmallVector<StringRef, 8> LibraryPaths;

// Expected path w/out install.
SmallString<256> WithoutInstallPath(getDriver().ResourceDir);
llvm::sys::path::append(WithoutInstallPath, Twine("../../clc"));
LibraryPaths.emplace_back(WithoutInstallPath.c_str());

// Expected path w/ install.
SmallString<256> WithInstallPath(getDriver().ResourceDir);
llvm::sys::path::append(WithInstallPath, Twine("../../../share/clc"));
LibraryPaths.emplace_back(WithInstallPath.c_str());

// Select remangled libclc variant
std::string LibSpirvTargetName = getLibSpirvTargetName(HostTC);

for (StringRef LibraryPath : LibraryPaths) {
SmallString<128> LibSpirvTargetFile(LibraryPath);
llvm::sys::path::append(LibSpirvTargetFile, LibSpirvTargetName);
if (llvm::sys::fs::exists(LibSpirvTargetFile) ||
DriverArgs.hasArg(options::OPT__HASH_HASH_HASH)) {
LibSpirvFile = std::string(LibSpirvTargetFile.str());
break;
}
}
}

if (LibSpirvFile.empty()) {
getDriver().Diag(diag::err_drv_no_sycl_libspirv)
<< getLibSpirvTargetName(HostTC);
return;
}

CC1Args.push_back("-mlink-builtin-bitcode");
CC1Args.push_back(DriverArgs.MakeArgString(LibSpirvFile));
}

if (DriverArgs.hasFlag(options::OPT_fcuda_short_ptr,
options::OPT_fno_cuda_short_ptr, false))
CC1Args.append({"-mllvm", "--nvptx-short-ptr"});
Expand Down
53 changes: 2 additions & 51 deletions clang/lib/Driver/ToolChains/HIPAMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,6 @@ HIPAMDToolChain::HIPAMDToolChain(const Driver &D, const llvm::Triple &Triple,
diagnoseUnsupportedSanitizers(Args);
}

static const char *getLibSpirvTargetName(const ToolChain &HostTC) {
return "remangled-l64-signed_char.libspirv-amdgcn-amd-amdhsa.bc";
}

void HIPAMDToolChain::addClangTargetOptions(
const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
Action::OffloadKind DeviceOffloadingKind) const {
Expand Down Expand Up @@ -274,53 +270,8 @@ void HIPAMDToolChain::addClangTargetOptions(

if (DeviceOffloadingKind == Action::OFK_SYCL) {
SYCLInstallation.addSYCLIncludeArgs(DriverArgs, CC1Args);
}

auto NoLibSpirv = DriverArgs.hasArg(options::OPT_fno_sycl_libspirv) ||
getDriver().offloadDeviceOnly();
if (DeviceOffloadingKind == Action::OFK_SYCL && !NoLibSpirv) {
std::string LibSpirvFile;

if (DriverArgs.hasArg(clang::driver::options::OPT_fsycl_libspirv_path_EQ)) {
auto ProvidedPath =
DriverArgs
.getLastArgValue(
clang::driver::options::OPT_fsycl_libspirv_path_EQ)
.str();
if (llvm::sys::fs::exists(ProvidedPath))
LibSpirvFile = ProvidedPath;
} else {
SmallVector<StringRef, 8> LibraryPaths;

// Expected path w/out install.
SmallString<256> WithoutInstallPath(getDriver().ResourceDir);
llvm::sys::path::append(WithoutInstallPath, Twine("../../clc"));
LibraryPaths.emplace_back(WithoutInstallPath.c_str());

// Expected path w/ install.
SmallString<256> WithInstallPath(getDriver().ResourceDir);
llvm::sys::path::append(WithInstallPath, Twine("../../../share/clc"));
LibraryPaths.emplace_back(WithInstallPath.c_str());

std::string LibSpirvTargetName = getLibSpirvTargetName(HostTC);
for (StringRef LibraryPath : LibraryPaths) {
SmallString<128> LibSpirvTargetFile(LibraryPath);
llvm::sys::path::append(LibSpirvTargetFile, LibSpirvTargetName);
if (llvm::sys::fs::exists(LibSpirvTargetFile)) {
LibSpirvFile = std::string(LibSpirvTargetFile.str());
break;
}
}
}

if (LibSpirvFile.empty()) {
getDriver().Diag(diag::err_drv_no_sycl_libspirv)
<< getLibSpirvTargetName(HostTC);
return;
}

CC1Args.push_back("-mlink-builtin-bitcode");
CC1Args.push_back(DriverArgs.MakeArgString(LibSpirvFile));
SYCLInstallation.addLibspirvLinkArgs(getEffectiveTriple(), DriverArgs,
HostTC.getTriple(), CC1Args);
}

for (auto BCFile : getDeviceLibs(DriverArgs, DeviceOffloadingKind)) {
Expand Down
76 changes: 76 additions & 0 deletions clang/lib/Driver/ToolChains/SYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,82 @@ SYCLInstallationDetector::SYCLInstallationDetector(
const llvm::opt::ArgList &Args)
: D(D) {}

static llvm::SmallString<64>
getLibSpirvBasename(const llvm::Triple &DeviceTriple,
const llvm::Triple &HostTriple) {
// Select remangled libclc variant.
// Decide long size based on host triple, because offloading targets are going
// to match that.
// All known windows environments except Cygwin use 32-bit long.
llvm::SmallString<64> Result(HostTriple.isOSWindows() &&
!HostTriple.isWindowsCygwinEnvironment()
? "remangled-l32-signed_char.libspirv-"
: "remangled-l64-signed_char.libspirv-");

Result.append(DeviceTriple.getTriple());
Result.append(".bc");

return Result;
}

const char *SYCLInstallationDetector::findLibspirvPath(
const llvm::Triple &DeviceTriple, const llvm::opt::ArgList &Args,
const llvm::Triple &HostTriple) const {

// If -fsycl-libspirv-path= is specified, try to use that path directly.
if (Arg *A = Args.getLastArg(options::OPT_fsycl_libspirv_path_EQ)) {
if (llvm::sys::fs::exists(A->getValue()))
return A->getValue();

return nullptr;
}

const SmallString<64> Basename =
getLibSpirvBasename(DeviceTriple, HostTriple);
auto searchAt = [&](StringRef Path, const Twine &a = "", const Twine &b = "",
const Twine &c = "", const Twine &d = "",
const Twine &e = "") -> const char * {
SmallString<128> LibraryPath(Path);
llvm::sys::path::append(LibraryPath, a, b, c, d);
llvm::sys::path::append(LibraryPath, e, Basename);

if (Args.hasArgNoClaim(options::OPT__HASH_HASH_HASH) ||
llvm::sys::fs::exists(LibraryPath))
return Args.MakeArgString(LibraryPath);

return nullptr;
};

// Otherwise, assume libclc is installed at the same prefix as clang
// Expected path w/out install.
if (const char *R = searchAt(D.ResourceDir, "..", "..", "clc"))
return R;

// Expected path w/ install.
if (const char *R = searchAt(D.ResourceDir, "..", "..", "..", "share", "clc"))
return R;

return nullptr;
}

void SYCLInstallationDetector::addLibspirvLinkArgs(
const llvm::Triple &DeviceTriple, const llvm::opt::ArgList &DriverArgs,
const llvm::Triple &HostTriple, llvm::opt::ArgStringList &CC1Args) const {
if (DriverArgs.hasArg(options::OPT_fno_sycl_libspirv) ||
D.offloadDeviceOnly())
return;

if (const char *LibSpirvFile =
findLibspirvPath(DeviceTriple, DriverArgs, HostTriple)) {
CC1Args.push_back("-mlink-builtin-bitcode");
CC1Args.push_back(LibSpirvFile);
return;
}

D.Diag(diag::err_drv_no_sycl_libspirv)
<< getLibSpirvBasename(DeviceTriple, HostTriple);
}

void SYCLInstallationDetector::getSYCLDeviceLibPath(
llvm::SmallVector<llvm::SmallString<128>, 4> &DeviceLibPaths) const {
for (const auto &IC : InstallationCandidates) {
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/Driver/ToolChains/SYCL.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ class SYCLInstallationDetector {
SYCLInstallationDetector(const Driver &D);
SYCLInstallationDetector(const Driver &D, const llvm::Triple &HostTriple,
const llvm::opt::ArgList &Args);

/// \brief Find and return the path to the libspirv library for the target
/// \return The path to the libspirv library if found, otherwise nullptr.
/// The lifetime of the returned string is managed by \p Args.
const char *findLibspirvPath(const llvm::Triple &DeviceTriple,
const llvm::opt::ArgList &Args,
const llvm::Triple &HostTriple) const;

void addLibspirvLinkArgs(const llvm::Triple &DeviceTriple,
const llvm::opt::ArgList &DriverArgs,
const llvm::Triple &HostTriple,
llvm::opt::ArgStringList &CC1Args) const;

void getSYCLDeviceLibPath(
llvm::SmallVector<llvm::SmallString<128>, 4> &DeviceLibPaths) const;
void addSYCLIncludeArgs(const llvm::opt::ArgList &DriverArgs,
Expand Down
14 changes: 8 additions & 6 deletions clang/test/Driver/sycl-libspirv-toolchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@

// RUN: %clang -### -fsycl -fsycl-targets=nvptx64-nvidia-cuda -nocudalib -target x86_64-unknown-windows-msvc %s 2>&1 \
// RUN: | FileCheck %s --check-prefixes=CHECK-WINDOWS
// RUN: %clang -### -fsycl -fsycl-targets=nvptx64-nvidia-cuda -nocudalib -target x86_64-unknown-windows-gnu %s 2>&1 \
// RUN: | FileCheck %s --check-prefixes=CHECK-WINDOWS
// CHECK-WINDOWS: "-cc1"{{.*}} "-fsycl-is-device"{{.*}} "-mlink-builtin-bitcode" "{{.*[\\/]}}remangled-l32-signed_char.libspirv-nvptx64-nvidia-cuda.bc"
//
// RUN: %clang -### -fsycl -fsycl-targets=nvptx64-nvidia-cuda -nocudalib -target x86_64-unknown-linux-gnu %s 2>&1 \
// RUN: | FileCheck %s --check-prefixes=CHECK-LINUX
// RUN: %clang -### -fsycl -fsycl-targets=nvptx64-nvidia-cuda -nocudalib -target x86_64-unknown-windows-cygnus %s 2>&1 \
// RUN: | FileCheck %s --check-prefixes=CHECK-LINUX
// CHECK-LINUX: "-cc1"{{.*}} "-fsycl-is-device"{{.*}} "-mlink-builtin-bitcode" "{{.*[\\/]}}remangled-l64-signed_char.libspirv-nvptx64-nvidia-cuda.bc"
//
// AMDGCN wrongly uses 32-bit longs on Windows
// RUN: %clang -### -resource-dir %{resource_dir} -fsycl -fsycl-targets=amdgcn-amd-amdhsa -Xsycl-target-backend --offload-arch=gfx908 -nogpulib -target x86_64-unknown-windows-msvc %s 2>&1 \
// RUN: | FileCheck %s --check-prefixes=CHECK-AMDGCN-WINDOWS
// CHECK-AMDGCN-WINDOWS: "-cc1"{{.*}} "-fsycl-is-device"{{.*}} "-mlink-builtin-bitcode" "{{.*[\\/]}}remangled-l64-signed_char.libspirv-amdgcn-amd-amdhsa.bc"
// CHECK-AMDGCN-WINDOWS: "-cc1"{{.*}} "-fsycl-is-device"{{.*}} "-mlink-builtin-bitcode" "{{.*[\\/]}}remangled-l32-signed_char.libspirv-amdgcn-amd-amdhsa.bc"
//
// RUN: %clang -### -fsycl -fsycl-device-only -fsycl-targets=nvptx64-nvidia-cuda -nocudalib %s 2>&1 \
// RUN: | FileCheck %s --check-prefixes=CHECK-DEVICE-ONLY
Expand All @@ -38,10 +41,9 @@
// RUN: | FileCheck %s -DDIR=%{nonexistent_dir} --check-prefixes=CHECK-HHH-NONEXISTENT
// CHECK-HHH-NONEXISTENT: "-cc1"{{.*}} "-fsycl-is-device"{{.*}} "-mlink-builtin-bitcode" "[[DIR]]{{.*[\\/]}}remangled-{{.*}}.libspirv-nvptx64-nvidia-cuda.bc"
//
// But not for AMDGCN :^)
// RUN: not %clang -### -resource-dir %{nonexistent_dir} -fsycl -fsycl-targets=amdgcn-amd-amdhsa -Xsycl-target-backend --offload-arch=gfx908 -nogpulib %s 2>&1 \
// RUN: | FileCheck %s -DDIR=%{nonexistent_dir} --check-prefixes=CHECK-AMDGCN-HHH-NONEXISTENT
// CHECK-AMDGCN-HHH-NONEXISTENT: error: cannot find 'remangled-{{.*}}.libspirv-amdgcn-amd-amdhsa.bc'; provide path to libspirv library via '-fsycl-libspirv-path', or pass '-fno-sycl-libspirv' to build without linking with libspirv
// RUN: %clang -### -resource-dir %{nonexistent_dir} -fsycl -fsycl-targets=amdgcn-amd-amdhsa -Xsycl-target-backend --offload-arch=gfx908 -nogpulib %s 2>&1 \
// RUN: | FileCheck %s -DDIR=%{nonexistent_dir} --check-prefixes=CHECK-AMDGCN-HHH-NONEXISTENT
// CHECK-AMDGCN-HHH-NONEXISTENT: "-cc1"{{.*}} "-fsycl-is-device"{{.*}} "-mlink-builtin-bitcode" "[[DIR]]{{.*[\\/]}}remangled-{{.*}}.libspirv-amdgcn-amd-amdhsa.bc"
//
// `-fdriver-only` has no such special handling, so it will not find the file
// RUN: not %clang -fdriver-only -resource-dir %{nonexistent_dir} -fsycl -fsycl-targets=nvptx64-nvidia-cuda -nocudalib %s 2>&1 \
Expand Down