From 74c42b3e01b7a6e342b6b553743b2058c87c0708 Mon Sep 17 00:00:00 2001 From: "Agarwal, Udit" Date: Mon, 29 Sep 2025 20:50:56 +0200 Subject: [PATCH 1/6] [SYCL] Lookup versioned OpenCL adapter library as fallback --- sycl/include/sycl/detail/os_util.hpp | 24 +++++++++++++++--------- sycl/source/detail/os_util.cpp | 14 +++++++++++--- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/sycl/include/sycl/detail/os_util.hpp b/sycl/include/sycl/detail/os_util.hpp index 936d5be56cc4..57e2040deeef 100644 --- a/sycl/include/sycl/detail/os_util.hpp +++ b/sycl/include/sycl/detail/os_util.hpp @@ -106,27 +106,33 @@ void fileTreeWalk(const std::string Path, std::function Func, bool ignoreErrors = false); -void *dynLookup(const char *WinName, const char *LinName, const char *FunName); +void *dynLookup(const char *WinName, const char *LinName, + const char *LinuxFallbackLibName, const char *FunName); // Look up a function name that was dynamically linked // This is used by the runtime where it needs to manipulate native handles (e.g. // retaining OpenCL handles). On Windows, the symbol name is looked up in -// `WinName`. In Linux, it uses `LinName`. +// `WinName`. In Linux, it uses `LinName` or `LinuxFallbackLibName`. // // The library must already have been loaded (perhaps by UR), otherwise this // function throws a SYCL runtime exception. template fn *dynLookupFunction(const char *WinName, const char *LinName, - const char *FunName) { - return reinterpret_cast(dynLookup(WinName, LinName, FunName)); + const char *LinuxFallbackLibName, const char *FunName) { + return reinterpret_cast( + dynLookup(WinName, LinName, LinuxFallbackLibName, FunName)); } -// On Linux, the name of OpenCL that was used to link against may be either -// `OpenCL.so`, `OpenCL.so.1` or possibly anything else. -// `libur_adapter_opencl.so` is a more stable name, since it is hardcoded into -// the loader. + +// On Linux, first try to load from libur_adapter_opencl.so, then +// libur_adapter_opencl.so.0 if the first is not found. libur_adapter_opencl.so +// and libur_adapter_opencl.so.0 might be different libraries if they are not +// symlinked, which is the case with PyPi compiler distribution package. +// We can't load libur_adapter_opencl.so.0 always as the first choice because +// that would break SYCL unittests, which rely on mocking libur_adapter_opencl. #define __SYCL_OCL_CALL(FN, ...) \ (sycl::_V1::detail::dynLookupFunction( \ - "OpenCL", "libur_adapter_opencl.so", #FN)(__VA_ARGS__)) + "OpenCL", "libur_adapter_opencl.so", "libur_adapter_opencl.so.0", \ + #FN)(__VA_ARGS__)) } // namespace detail } // namespace _V1 diff --git a/sycl/source/detail/os_util.cpp b/sycl/source/detail/os_util.cpp index 437743c9af24..57bcf148a567 100644 --- a/sycl/source/detail/os_util.cpp +++ b/sycl/source/detail/os_util.cpp @@ -299,7 +299,9 @@ size_t getDirectorySize(const std::string &Path, bool ignoreErrors) { // The library must already have been loaded (perhaps by UR), otherwise this // function throws a SYCL runtime exception. void *dynLookup([[maybe_unused]] const char *WinName, - [[maybe_unused]] const char *LinName, const char *FunName) { + [[maybe_unused]] const char *LinName, + [[maybe_unused]] const char *LinuxFallbackLibName, + const char *FunName) { #ifdef __SYCL_RT_OS_WINDOWS auto handle = GetModuleHandleA(WinName); if (!handle) { @@ -310,8 +312,14 @@ void *dynLookup([[maybe_unused]] const char *WinName, #else auto handle = dlopen(LinName, RTLD_LAZY | RTLD_NOLOAD); if (!handle) { - throw sycl::exception(make_error_code(errc::runtime), - std::string(LinName) + " library is not loaded"); + + // Try to open fallback library if provided. + if (LinuxFallbackLibName) + handle = dlopen(LinuxFallbackLibName, RTLD_LAZY | RTLD_NOLOAD); + + if (!handle) + throw sycl::exception(make_error_code(errc::runtime), + std::string(LinName) + " library is not loaded"); } auto *retVal = dlsym(handle, FunName); dlclose(handle); From 22a38f5b464eed7cecb58932eefcc22ee3f5b1b7 Mon Sep 17 00:00:00 2001 From: "Agarwal, Udit" Date: Mon, 29 Sep 2025 23:06:47 +0200 Subject: [PATCH 2/6] Add comment to UR --- unified-runtime/source/loader/ur_manifests.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unified-runtime/source/loader/ur_manifests.hpp b/unified-runtime/source/loader/ur_manifests.hpp index c376ab26a4a3..47448bb041d3 100644 --- a/unified-runtime/source/loader/ur_manifests.hpp +++ b/unified-runtime/source/loader/ur_manifests.hpp @@ -31,6 +31,8 @@ struct ur_adapter_manifest { }; const std::vector ur_adapter_manifests = { + // NOTE: SYCL RT also loads OpenCL adapter for interop functionality. + // If OpenCL adapter version is changed, please update SYCL RT accordingly. {"opencl", MAKE_LIBRARY_NAME("ur_adapter_opencl", "0"), UR_BACKEND_OPENCL, From a79273c9f8909ab4a6dc4a38f056df6eac9bef32 Mon Sep 17 00:00:00 2001 From: "Agarwal, Udit" Date: Tue, 30 Sep 2025 06:09:07 +0200 Subject: [PATCH 3/6] Revert "Add comment to UR" This reverts commit 22a38f5b464eed7cecb58932eefcc22ee3f5b1b7. --- unified-runtime/source/loader/ur_manifests.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/unified-runtime/source/loader/ur_manifests.hpp b/unified-runtime/source/loader/ur_manifests.hpp index 47448bb041d3..c376ab26a4a3 100644 --- a/unified-runtime/source/loader/ur_manifests.hpp +++ b/unified-runtime/source/loader/ur_manifests.hpp @@ -31,8 +31,6 @@ struct ur_adapter_manifest { }; const std::vector ur_adapter_manifests = { - // NOTE: SYCL RT also loads OpenCL adapter for interop functionality. - // If OpenCL adapter version is changed, please update SYCL RT accordingly. {"opencl", MAKE_LIBRARY_NAME("ur_adapter_opencl", "0"), UR_BACKEND_OPENCL, From 15bb682760b6da3115d8dc8b60c457ee3cbac5bc Mon Sep 17 00:00:00 2001 From: "Agarwal, Udit" Date: Tue, 30 Sep 2025 07:49:23 +0200 Subject: [PATCH 4/6] Address feedback --- sycl/include/sycl/detail/os_util.hpp | 28 +++++++------- sycl/source/detail/os_util.cpp | 56 ++++++++++++++-------------- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/sycl/include/sycl/detail/os_util.hpp b/sycl/include/sycl/detail/os_util.hpp index 57e2040deeef..f4d727fa1db1 100644 --- a/sycl/include/sycl/detail/os_util.hpp +++ b/sycl/include/sycl/detail/os_util.hpp @@ -106,21 +106,18 @@ void fileTreeWalk(const std::string Path, std::function Func, bool ignoreErrors = false); -void *dynLookup(const char *WinName, const char *LinName, - const char *LinuxFallbackLibName, const char *FunName); - // Look up a function name that was dynamically linked -// This is used by the runtime where it needs to manipulate native handles (e.g. -// retaining OpenCL handles). On Windows, the symbol name is looked up in -// `WinName`. In Linux, it uses `LinName` or `LinuxFallbackLibName`. +// This is used by the runtime where it needs to manipulate native handles +// (e.g. retaining OpenCL handles). // // The library must already have been loaded (perhaps by UR), otherwise this // function throws a SYCL runtime exception. +void *dynLookup(const std::vector &LibNames, const char *FunName); + template -fn *dynLookupFunction(const char *WinName, const char *LinName, - const char *LinuxFallbackLibName, const char *FunName) { - return reinterpret_cast( - dynLookup(WinName, LinName, LinuxFallbackLibName, FunName)); +fn *dynLookupFunction(const std::vector LibNames, + const char *FunName) { + return reinterpret_cast(dynLookup(LibNames, FunName)); } // On Linux, first try to load from libur_adapter_opencl.so, then @@ -129,10 +126,15 @@ fn *dynLookupFunction(const char *WinName, const char *LinName, // symlinked, which is the case with PyPi compiler distribution package. // We can't load libur_adapter_opencl.so.0 always as the first choice because // that would break SYCL unittests, which rely on mocking libur_adapter_opencl. +#ifdef __SYCL_RT_OS_WINDOWS +#define OCLLibNames {"OpenCL"} +#else +#define OCLLibNames {"libur_adapter_opencl.so", "libur_adapter_opencl.so.0"} +#endif + #define __SYCL_OCL_CALL(FN, ...) \ - (sycl::_V1::detail::dynLookupFunction( \ - "OpenCL", "libur_adapter_opencl.so", "libur_adapter_opencl.so.0", \ - #FN)(__VA_ARGS__)) + (sycl::_V1::detail::dynLookupFunction(OCLLibNames, \ + #FN)(__VA_ARGS__)) } // namespace detail } // namespace _V1 diff --git a/sycl/source/detail/os_util.cpp b/sycl/source/detail/os_util.cpp index 57bcf148a567..08fba6cdfc3c 100644 --- a/sycl/source/detail/os_util.cpp +++ b/sycl/source/detail/os_util.cpp @@ -291,44 +291,44 @@ size_t getDirectorySize(const std::string &Path, bool ignoreErrors) { return DirSizeVar; } -// Look up a function name that was dynamically linked -// This is used by the runtime where it needs to manipulate native handles (e.g. -// retaining OpenCL handles). On Windows, the symbol name is looked up in -// `WinName`. In Linux, it uses `LinName`. +// Look up a function name from the given list of shared libraries. // -// The library must already have been loaded (perhaps by UR), otherwise this +// These library must already have been loaded (perhaps by UR), otherwise this // function throws a SYCL runtime exception. -void *dynLookup([[maybe_unused]] const char *WinName, - [[maybe_unused]] const char *LinName, - [[maybe_unused]] const char *LinuxFallbackLibName, +void *dynLookup(const std::vector &LibNames, const char *FunName) { #ifdef __SYCL_RT_OS_WINDOWS - auto handle = GetModuleHandleA(WinName); - if (!handle) { - throw sycl::exception(make_error_code(errc::runtime), - std::string(WinName) + " library is not loaded"); - } - auto *retVal = GetProcAddress(handle, FunName); + HMODULE handle = nullptr; + auto GetHandleF = [](const char *LibName) { + return GetModuleHandleA(LibName); + }; + auto GetProcF = [&]() { return GetProcAddress(handle, FunName); }; #else - auto handle = dlopen(LinName, RTLD_LAZY | RTLD_NOLOAD); - if (!handle) { + void *handle = nullptr; + auto GetHandleF = [](const char *LibName) { + return dlopen(LibName, RTLD_LAZY | RTLD_NOLOAD); + }; + auto GetProcF = [&]() { + auto *retVal = dlsym(handle, FunName); + dlclose(handle); + return retVal; + }; +#endif - // Try to open fallback library if provided. - if (LinuxFallbackLibName) - handle = dlopen(LinuxFallbackLibName, RTLD_LAZY | RTLD_NOLOAD); + // Iterate over the list of libraries and try to find one that is loaded. + auto LibNameIt = LibNames.begin(); + while (!handle && LibNameIt != LibNames.end()) + handle = GetHandleF(*(LibNameIt++)); + if (!handle) + throw sycl::exception(make_error_code(errc::runtime), + "Libraries could not be loaded"); - if (!handle) - throw sycl::exception(make_error_code(errc::runtime), - std::string(LinName) + " library is not loaded"); - } - auto *retVal = dlsym(handle, FunName); - dlclose(handle); -#endif - if (!retVal) { + // Look up the function in the loaded library. + auto *retVal = GetProcF(); + if (!retVal) throw sycl::exception(make_error_code(errc::runtime), "Symbol " + std::string(FunName) + " could not be found"); - } return reinterpret_cast(retVal); } From 2e72fc26de297df3e94b903ed331d45b12db25c3 Mon Sep 17 00:00:00 2001 From: "Agarwal, Udit" Date: Tue, 30 Sep 2025 08:56:22 +0200 Subject: [PATCH 5/6] Use C-style data ptr and size, instead of sycl::span --- sycl/include/sycl/detail/os_util.hpp | 17 ++++++++++------- sycl/source/detail/os_util.cpp | 8 ++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/sycl/include/sycl/detail/os_util.hpp b/sycl/include/sycl/detail/os_util.hpp index f4d727fa1db1..92e96dfb7a3f 100644 --- a/sycl/include/sycl/detail/os_util.hpp +++ b/sycl/include/sycl/detail/os_util.hpp @@ -112,12 +112,13 @@ void fileTreeWalk(const std::string Path, // // The library must already have been loaded (perhaps by UR), otherwise this // function throws a SYCL runtime exception. -void *dynLookup(const std::vector &LibNames, const char *FunName); +void *dynLookup(const char *const *LibNames, size_t LibNameSizes, + const char *FunName); template -fn *dynLookupFunction(const std::vector LibNames, +fn *dynLookupFunction(const char *const *LibNames, size_t LibNameSize, const char *FunName) { - return reinterpret_cast(dynLookup(LibNames, FunName)); + return reinterpret_cast(dynLookup(LibNames, LibNameSize, FunName)); } // On Linux, first try to load from libur_adapter_opencl.so, then @@ -127,14 +128,16 @@ fn *dynLookupFunction(const std::vector LibNames, // We can't load libur_adapter_opencl.so.0 always as the first choice because // that would break SYCL unittests, which rely on mocking libur_adapter_opencl. #ifdef __SYCL_RT_OS_WINDOWS -#define OCLLibNames {"OpenCL"} +constexpr std::array OCLLibNames = {"OpenCL"}; #else -#define OCLLibNames {"libur_adapter_opencl.so", "libur_adapter_opencl.so.0"} +constexpr std::array OCLLibNames = { + "libur_adapter_opencl.so", "libur_adapter_opencl.so.0"}; #endif #define __SYCL_OCL_CALL(FN, ...) \ - (sycl::_V1::detail::dynLookupFunction(OCLLibNames, \ - #FN)(__VA_ARGS__)) + (sycl::_V1::detail::dynLookupFunction( \ + sycl::detail::OCLLibNames.data(), sycl::detail::OCLLibNames.size(), \ + #FN)(__VA_ARGS__)) } // namespace detail } // namespace _V1 diff --git a/sycl/source/detail/os_util.cpp b/sycl/source/detail/os_util.cpp index 08fba6cdfc3c..30cef251e11a 100644 --- a/sycl/source/detail/os_util.cpp +++ b/sycl/source/detail/os_util.cpp @@ -295,7 +295,7 @@ size_t getDirectorySize(const std::string &Path, bool ignoreErrors) { // // These library must already have been loaded (perhaps by UR), otherwise this // function throws a SYCL runtime exception. -void *dynLookup(const std::vector &LibNames, +void *dynLookup(const char *const *LibNames, size_t LibNameSizes, const char *FunName) { #ifdef __SYCL_RT_OS_WINDOWS HMODULE handle = nullptr; @@ -316,9 +316,9 @@ void *dynLookup(const std::vector &LibNames, #endif // Iterate over the list of libraries and try to find one that is loaded. - auto LibNameIt = LibNames.begin(); - while (!handle && LibNameIt != LibNames.end()) - handle = GetHandleF(*(LibNameIt++)); + size_t LibNameIterator = 0; + while (!handle && LibNameIterator < LibNameSizes) + handle = GetHandleF(LibNames[LibNameIterator++]); if (!handle) throw sycl::exception(make_error_code(errc::runtime), "Libraries could not be loaded"); From 96ca413e34df14fed5d164f4baa64580913819bf Mon Sep 17 00:00:00 2001 From: "Agarwal, Udit" Date: Tue, 30 Sep 2025 09:14:30 +0200 Subject: [PATCH 6/6] Fix windows build --- sycl/include/sycl/detail/os_util.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/sycl/include/sycl/detail/os_util.hpp b/sycl/include/sycl/detail/os_util.hpp index 92e96dfb7a3f..6dc084573cc5 100644 --- a/sycl/include/sycl/detail/os_util.hpp +++ b/sycl/include/sycl/detail/os_util.hpp @@ -12,6 +12,7 @@ #include // for __SYCL_EXPORT +#include #include // for size_t #include #include // for string