From ccd561d90c098fe3d897c23d84280f0df958c3dc Mon Sep 17 00:00:00 2001 From: "Cai, Justin" Date: Thu, 9 Oct 2025 10:45:38 -0700 Subject: [PATCH] [SYCL][clang-linker-wrapper] Add -sycl-bc-device-libraries option for linking bitcode device libraries --- clang/lib/Driver/ToolChains/Clang.cpp | 91 +++++++++++-------- .../test/Driver/sycl-bc-device-libraries.cpp | 63 +++++++++++++ .../ClangLinkerWrapper.cpp | 29 +++--- .../clang-linker-wrapper/LinkerWrapperOpts.td | 8 +- 4 files changed, 133 insertions(+), 58 deletions(-) create mode 100644 clang/test/Driver/sycl-bc-device-libraries.cpp diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 90cad2bdbe6a5..35c4f9b8b33d8 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -11266,43 +11266,6 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, if (Args.hasArg(options::OPT_fsycl_link_EQ)) CmdArgs.push_back(Args.MakeArgString("--sycl-device-link")); - // -sycl-device-libraries= contains all of the SYCL - // device specific libraries that are needed. This generic list will be - // populated with device binaries for all target triples in the current - // compilation flow. - - // Create a comma separated list to pass along to the linker wrapper. - SmallString<256> LibList; - llvm::Triple TargetTriple; - auto ToolChainRange = C.getOffloadToolChains(); - for (auto &I : - llvm::make_range(ToolChainRange.first, ToolChainRange.second)) { - const ToolChain *TC = I.second; - // Note: For AMD targets, we do not pass any SYCL device libraries. - if (TC->getTriple().isSPIROrSPIRV() || TC->getTriple().isNVPTX()) { - TargetTriple = TC->getTriple(); - SmallVector SYCLDeviceLibs; - bool IsSPIR = TargetTriple.isSPIROrSPIRV(); - bool IsSpirvAOT = TargetTriple.isSPIRAOT(); - bool UseJitLink = - IsSPIR && - Args.hasFlag(options::OPT_fsycl_device_lib_jit_link, - options::OPT_fno_sycl_device_lib_jit_link, false); - bool UseAOTLink = IsSPIR && (IsSpirvAOT || !UseJitLink); - SYCLDeviceLibs = SYCL::getDeviceLibraries(C, TargetTriple, UseAOTLink); - for (const auto &AddLib : SYCLDeviceLibs) { - if (LibList.size() > 0) - LibList += ","; - LibList += AddLib; - } - } - } - // -sycl-device-libraries= provides a comma separate list of - // libraries to add to the device linking step. - if (LibList.size()) - CmdArgs.push_back( - Args.MakeArgString(Twine("-sycl-device-libraries=") + LibList)); - // -sycl-device-library-location= provides the location in which the // SYCL device libraries can be found. SmallString<128> DeviceLibDir(D.Dir); @@ -11327,6 +11290,60 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, break; } } + + // Create a comma separated list to pass along to the linker wrapper. + SmallString<256> LibList; + SmallString<256> BCLibList; + + auto appendToList = [](SmallString<256> &List, const Twine &Arg) { + if (List.size() > 0) + List += ","; + List += Arg.str(); + }; + + llvm::Triple TargetTriple; + auto ToolChainRange = C.getOffloadToolChains(); + for (auto &I : + llvm::make_range(ToolChainRange.first, ToolChainRange.second)) { + const ToolChain *TC = I.second; + TargetTriple = TC->getTriple(); + SmallVector SYCLDeviceLibs; + bool IsSPIR = TargetTriple.isSPIROrSPIRV(); + bool IsSpirvAOT = TargetTriple.isSPIRAOT(); + bool UseJitLink = + IsSPIR && + Args.hasFlag(options::OPT_fsycl_device_lib_jit_link, + options::OPT_fno_sycl_device_lib_jit_link, false); + bool UseAOTLink = IsSPIR && (IsSpirvAOT || !UseJitLink); + SYCLDeviceLibs = SYCL::getDeviceLibraries(C, TargetTriple, UseAOTLink); + for (const auto &AddLib : SYCLDeviceLibs) { + if (llvm::sys::path::extension(AddLib) == ".bc") { + SmallString<256> LibPath(DeviceLibDir); + llvm::sys::path::append(LibPath, AddLib); + appendToList(BCLibList, + StringRef(TC->getTriple().str()) + "=" + LibPath); + continue; + } + + appendToList(LibList, AddLib); + } + + if (TC->getTriple().isNVPTX()) + if (const char *LibSpirvFile = SYCLInstallation.findLibspirvPath( + TC->getTriple(), Args, *TC->getAuxTriple())) + appendToList(BCLibList, + StringRef(TC->getTriple().str()) + "=" + LibSpirvFile); + } + // -sycl-device-libraries= provides a comma separate list of + // libraries to add to the device linking step. + if (LibList.size()) + CmdArgs.push_back( + Args.MakeArgString(Twine("-sycl-device-libraries=") + LibList)); + + if (BCLibList.size()) + CmdArgs.push_back( + Args.MakeArgString(Twine("-sycl-bc-device-libraries=") + BCLibList)); + CmdArgs.push_back(Args.MakeArgString( Twine("-sycl-device-library-location=") + DeviceLibDir)); diff --git a/clang/test/Driver/sycl-bc-device-libraries.cpp b/clang/test/Driver/sycl-bc-device-libraries.cpp new file mode 100644 index 0000000000000..181749aa35e7d --- /dev/null +++ b/clang/test/Driver/sycl-bc-device-libraries.cpp @@ -0,0 +1,63 @@ +/// Test that SYCL bitcode device libraries are properly separated for NVIDIA and AMD targets + +/// Check devicelib and libspirv are linked for nvptx +// RUN: %clang -### -fsycl --offload-new-driver \ +// RUN: -fsycl-targets=nvptx64-nvidia-cuda \ +// RUN: --cuda-path=%S/Inputs/CUDA_102/usr/local/cuda \ +// RUN: %s 2>&1 | FileCheck -check-prefix=CHECK-NVPTX-BC %s + +// CHECK-NVPTX-BC: clang-linker-wrapper +// CHECK-NVPTX-BC-SAME: "-sycl-bc-device-libraries={{.*}}nvptx64-nvidia-cuda={{.*}}devicelib-nvptx64-nvidia-cuda.bc{{.*}}nvptx64-nvidia-cuda={{.*}}libspirv-nvptx64-nvidia-cuda.bc" + +/// Check devicelib is linked for amdgcn +// RUN: %clang -### -fsycl --offload-new-driver \ +// RUN: -fsycl-targets=amdgcn-amd-amdhsa \ +// RUN: -Xsycl-target-backend=amdgcn-amd-amdhsa --offload-arch=gfx900 \ +// RUN: --rocm-path=%S/Inputs/rocm \ +// RUN: %s 2>&1 | FileCheck -check-prefix=CHECK-AMD-BC %s + +// CHECK-AMD-BC: clang-linker-wrapper +// CHECK-AMD-BC-SAME: "-sycl-bc-device-libraries=amdgcn-amd-amdhsa={{.*}}devicelib-amdgcn-amd-amdhsa.bc" + +/// Check linking with multiple targets +// RUN: %clang -### -fsycl --offload-new-driver \ +// RUN: -fsycl-targets=amdgcn-amd-amdhsa,nvptx64-nvidia-cuda \ +// RUN: -Xsycl-target-backend=amdgcn-amd-amdhsa --offload-arch=gfx900 \ +// RUN: --cuda-path=%S/Inputs/CUDA_102/usr/local/cuda \ +// RUN: --rocm-path=%S/Inputs/rocm \ +// RUN: %s 2>&1 | FileCheck -check-prefix=CHECK-MULTI-TARGET %s + +// CHECK-MULTI-TARGET: clang-linker-wrapper +// CHECK-MULTI-TARGET-SAME: "-sycl-bc-device-libraries={{.*}}amdgcn-amd-amdhsa={{.*}}devicelib-amdgcn-amd-amdhsa.bc{{.*}}nvptx64-nvidia-cuda={{.*}}devicelib-nvptx64-nvidia-cuda.bc{{.*}}nvptx64-nvidia-cuda={{.*}}libspirv-nvptx64-nvidia-cuda.bc" + +/// Test -sycl-bc-device-libraries with nvptx dummy libraries +// RUN: %clang -cc1 %s -triple nvptx64-nvidia-cuda -emit-llvm-bc -o %t.nvptx.devicelib.bc +// RUN: %clang -cc1 %s -triple nvptx64-nvidia-cuda -emit-llvm-bc -o %t.nvptx.libspirv.bc +// RUN: %clang++ -fsycl -fsycl-targets=nvptx64-nvidia-cuda --offload-new-driver -c %s -o %t.nvptx.o -nocudalib +// RUN: clang-linker-wrapper -sycl-bc-device-libraries=nvptx64-nvidia-cuda=%t.nvptx.devicelib.bc,nvptx64-nvidia-cuda=%t.nvptx.libspirv.bc \ +// RUN: --host-triple=x86_64-unknown-linux-gnu --dry-run \ +// RUN: --linker-path=/usr/bin/ld %t.nvptx.o -o a.out 2>&1 | FileCheck -check-prefix=CHECK-WRAPPER-NVPTX %s + +// CHECK-WRAPPER-NVPTX: llvm-link{{.*}} {{.*}}.nvptx.devicelib.bc {{.*}}.nvptx.libspirv.bc + +/// Test -sycl-bc-device-libraries with amdgcn dummy library +// RUN: %clang -cc1 %s -triple amdgcn-amd-amdhsa -emit-llvm-bc -o %t.amd.devicelib.bc +// RUN: %clang++ -fsycl -fsycl-targets=amdgcn-amd-amdhsa -Xsycl-target-backend=amdgcn-amd-amdhsa --offload-arch=gfx900 --offload-new-driver -c %s -o %t.amd.o -nogpulib +// RUN: clang-linker-wrapper -sycl-bc-device-libraries=amdgcn-amd-amdhsa=%t.amd.devicelib.bc \ +// RUN: --host-triple=x86_64-unknown-linux-gnu --dry-run \ +// RUN: --linker-path=/usr/bin/ld %t.amd.o -o a.out 2>&1 | FileCheck -check-prefix=CHECK-WRAPPER-AMD %s + +// CHECK-WRAPPER-AMD: llvm-link{{.*}} {{.*}}.amd.devicelib.bc + +/// Test clang-linker-wrapper directly with multi-target bc libraries +// RUN: %clang++ -fsycl -fsycl-targets=amdgcn-amd-amdhsa,nvptx64-nvidia-cuda \ +// RUN: -Xsycl-target-backend=amdgcn-amd-amdhsa --offload-arch=gfx900 \ +// RUN: --offload-new-driver -c %s -o %t.multi.o -nocudalib -nogpulib +// RUN: clang-linker-wrapper -sycl-bc-device-libraries=amdgcn-amd-amdhsa=%t.amd.devicelib.bc,nvptx64-nvidia-cuda=%t.nvptx.devicelib.bc,nvptx64-nvidia-cuda=%t.nvptx.libspirv.bc \ +// RUN: --host-triple=x86_64-unknown-linux-gnu --dry-run \ +// RUN: --linker-path=/usr/bin/ld %t.multi.o -o a.out 2>&1 | FileCheck -check-prefix=CHECK-WRAPPER-MULTI %s + +// CHECK-WRAPPER-MULTI: llvm-link{{.*}} {{.*}}.amd.devicelib.bc +// CHECK-WRAPPER-MULTI: llvm-link{{.*}} {{.*}}.nvptx.devicelib.bc {{.*}}.nvptx.libspirv.bc + +int main() { return 0; } diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index 7efcecc358e7e..0f73aeba69786 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -1417,24 +1417,17 @@ static Expected linkDevice(ArrayRef InputFiles, << "Compatible SYCL device library binary not found\n"; } - // For NVPTX backend we need to also link libclc and CUDA libdevice. - if (Triple.isNVPTX()) { - if (Arg *A = Args.getLastArg(OPT_sycl_nvptx_device_lib_EQ)) { - if (A->getValues().size() == 0) - return createStringError( - inconvertibleErrorCode(), - "Number of device library files cannot be zero."); - for (StringRef Val : A->getValues()) { - SmallString<128> LibName(Val); - if (llvm::sys::fs::exists(LibName)) - ExtractedDeviceLibFiles.emplace_back(std::string(LibName)); - else - return createStringError( - inconvertibleErrorCode(), - std::string(LibName) + - " SYCL device library file for NVPTX is not found."); - } - } + for (StringRef Library : Args.getAllArgValues(OPT_sycl_bc_device_lib_EQ)) { + auto [LibraryTriple, LibraryPath] = Library.split('='); + if (llvm::Triple(LibraryTriple) != Triple) + continue; + + if (!llvm::sys::fs::exists(LibraryPath)) + return createStringError(inconvertibleErrorCode(), + "The specified device library " + LibraryPath + + " does not exist."); + + ExtractedDeviceLibFiles.emplace_back(LibraryPath.str()); } // Make sure that SYCL device library files are available. diff --git a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td index 1e7cd060278cb..7c3d8f7ce96fe 100644 --- a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td +++ b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td @@ -156,9 +156,11 @@ def sycl_device_lib_EQ : CommaJoined<["--", "-"], "sycl-device-libraries=">, def sycl_device_library_location_EQ : Joined<["--", "-"], "sycl-device-library-location=">, Flags<[WrapperOnlyOption]>, HelpText<"Location of SYCL device library files">; -def sycl_nvptx_device_lib_EQ : CommaJoined<["--", "-"], "sycl-nvptx-device-libraries=">, - Flags<[WrapperOnlyOption]>, - HelpText<"A comma separated list of nvptx-specific device libraries that are linked during the device link.">; +def sycl_bc_device_lib_EQ + : CommaJoined<["--", "-"], "sycl-bc-device-libraries=">, + Flags<[WrapperOnlyOption]>, + HelpText<"A comma separated list of bitcode device libraries that are " + "linked during SYCL device link.">; // Options for SYCL backends and linker options for shared libraries. def sycl_backend_compile_options_EQ :