From ef9ae8ea175d9dd3429f1c6dd54ade461f3368fc Mon Sep 17 00:00:00 2001 From: Alexey Sachkov Date: Tue, 24 Sep 2024 05:19:26 -0700 Subject: [PATCH] [SYCL][NFC] Detach library loading helpers from UR This is a by-product of #14145. Functions to dynamically load a library and query a symbol out of it should not be attached to UR, because they are used for other libraries as well. Moved them from `detail::ur` into `detail` namespace, outlined into a separate header and removed declarations from public SYCL headers. Fixes https://github.com/intel/llvm/issues/14923 --- sycl/include/sycl/detail/ur.hpp | 12 ---- sycl/source/CMakeLists.txt | 2 + sycl/source/detail/jit_compiler.cpp | 17 ++--- .../kernel_compiler_opencl.cpp | 12 ++-- sycl/source/detail/load_library.hpp | 36 ++++++++++ sycl/source/detail/load_library_posix.cpp | 41 +++++++++++ sycl/source/detail/load_library_win.cpp | 72 +++++++++++++++++++ .../online_compiler/online_compiler.cpp | 12 ++-- sycl/source/detail/posix_ur.cpp | 18 ----- sycl/source/detail/windows_ur.cpp | 48 ------------- 10 files changed, 172 insertions(+), 98 deletions(-) create mode 100644 sycl/source/detail/load_library.hpp create mode 100644 sycl/source/detail/load_library_posix.cpp create mode 100644 sycl/source/detail/load_library_win.cpp diff --git a/sycl/include/sycl/detail/ur.hpp b/sycl/include/sycl/detail/ur.hpp index fe797853212fc..35fe14717db02 100644 --- a/sycl/include/sycl/detail/ur.hpp +++ b/sycl/include/sycl/detail/ur.hpp @@ -111,18 +111,6 @@ template __SYCL_EXPORT void *getPluginOpaqueData(void *opaquedata_arg); namespace ur { -// Function to load a shared library -// Implementation is OS dependent -void *loadOsLibrary(const std::string &Library); - -// Function to unload a shared library -// Implementation is OS dependent (see posix-ur.cpp and windows-ur.cpp) -int unloadOsLibrary(void *Library); - -// Function to get Address of a symbol defined in the shared -// library, implementation is OS dependent. -void *getOsLibraryFuncAddress(void *Library, const std::string &FunctionName); - void *getURLoaderLibrary(); // Performs UR one-time initialization. diff --git a/sycl/source/CMakeLists.txt b/sycl/source/CMakeLists.txt index 5e7cdead48f25..0bbfe25f01d0b 100644 --- a/sycl/source/CMakeLists.txt +++ b/sycl/source/CMakeLists.txt @@ -305,6 +305,8 @@ set(SYCL_COMMON_SOURCES "virtual_mem.cpp" "$<$:detail/windows_ur.cpp>" "$<$,$>:detail/posix_ur.cpp>" + "$<$:detail/load_library_win.cpp>" + "$<$,$>:detail/load_library_posix.cpp>" ) set(SYCL_NON_PREVIEW_SOURCES "${SYCL_COMMON_SOURCES}" diff --git a/sycl/source/detail/jit_compiler.cpp b/sycl/source/detail/jit_compiler.cpp index 8abb9ddabfd7d..6e0ff406a01aa 100644 --- a/sycl/source/detail/jit_compiler.cpp +++ b/sycl/source/detail/jit_compiler.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -31,15 +32,15 @@ jit_compiler::jit_compiler() { auto checkJITLibrary = [this]() -> bool { static const std::string JITLibraryName = "libsycl-jit.so"; - void *LibraryPtr = sycl::detail::ur::loadOsLibrary(JITLibraryName); + void *LibraryPtr = sycl::detail::loadOsLibrary(JITLibraryName); if (LibraryPtr == nullptr) { printPerformanceWarning("Could not find JIT library " + JITLibraryName); return false; } this->AddToConfigHandle = reinterpret_cast( - sycl::detail::ur::getOsLibraryFuncAddress(LibraryPtr, - "addToJITConfiguration")); + sycl::detail::getOsLibraryFuncAddress(LibraryPtr, + "addToJITConfiguration")); if (!this->AddToConfigHandle) { printPerformanceWarning( "Cannot resolve JIT library function entry point"); @@ -47,8 +48,8 @@ jit_compiler::jit_compiler() { } this->ResetConfigHandle = reinterpret_cast( - sycl::detail::ur::getOsLibraryFuncAddress(LibraryPtr, - "resetJITConfiguration")); + sycl::detail::getOsLibraryFuncAddress(LibraryPtr, + "resetJITConfiguration")); if (!this->ResetConfigHandle) { printPerformanceWarning( "Cannot resolve JIT library function entry point"); @@ -56,7 +57,7 @@ jit_compiler::jit_compiler() { } this->FuseKernelsHandle = reinterpret_cast( - sycl::detail::ur::getOsLibraryFuncAddress(LibraryPtr, "fuseKernels")); + sycl::detail::getOsLibraryFuncAddress(LibraryPtr, "fuseKernels")); if (!this->FuseKernelsHandle) { printPerformanceWarning( "Cannot resolve JIT library function entry point"); @@ -65,8 +66,8 @@ jit_compiler::jit_compiler() { this->MaterializeSpecConstHandle = reinterpret_cast( - sycl::detail::ur::getOsLibraryFuncAddress( - LibraryPtr, "materializeSpecConstants")); + sycl::detail::getOsLibraryFuncAddress(LibraryPtr, + "materializeSpecConstants")); if (!this->MaterializeSpecConstHandle) { printPerformanceWarning( "Cannot resolve JIT library function entry point"); diff --git a/sycl/source/detail/kernel_compiler/kernel_compiler_opencl.cpp b/sycl/source/detail/kernel_compiler/kernel_compiler_opencl.cpp index 10a65d05dec1f..bb3bc6a26bd22 100644 --- a/sycl/source/detail/kernel_compiler/kernel_compiler_opencl.cpp +++ b/sycl/source/detail/kernel_compiler/kernel_compiler_opencl.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include // getOsLibraryFuncAddress +#include // getOsLibraryFuncAddress #include // make_error_code #include "kernel_compiler_opencl.hpp" @@ -27,7 +27,7 @@ namespace detail { // ensures the OclocLibrary has the right version, etc. void checkOclocLibrary(void *OclocLibrary) { void *OclocVersionHandle = - sycl::detail::ur::getOsLibraryFuncAddress(OclocLibrary, "oclocVersion"); + sycl::detail::getOsLibraryFuncAddress(OclocLibrary, "oclocVersion"); // The initial versions of ocloc library did not have the oclocVersion() // function. Those versions had the same API as the first version of ocloc // library having that oclocVersion() function. @@ -67,7 +67,7 @@ void *loadOclocLibrary() { #endif void *tempPtr = OclocLibrary; if (tempPtr == nullptr) { - tempPtr = sycl::detail::ur::loadOsLibrary(OclocLibraryName); + tempPtr = sycl::detail::loadOsLibrary(OclocLibraryName); if (tempPtr == nullptr) throw sycl::exception(make_error_code(errc::build), @@ -104,12 +104,12 @@ void SetupLibrary(voidPtr &oclocInvokeHandle, voidPtr &oclocFreeOutputHandle, loadOclocLibrary(); oclocInvokeHandle = - sycl::detail::ur::getOsLibraryFuncAddress(OclocLibrary, "oclocInvoke"); + sycl::detail::getOsLibraryFuncAddress(OclocLibrary, "oclocInvoke"); if (!oclocInvokeHandle) throw sycl::exception(the_errc, "Cannot load oclocInvoke() function"); - oclocFreeOutputHandle = sycl::detail::ur::getOsLibraryFuncAddress( - OclocLibrary, "oclocFreeOutput"); + oclocFreeOutputHandle = + sycl::detail::getOsLibraryFuncAddress(OclocLibrary, "oclocFreeOutput"); if (!oclocFreeOutputHandle) throw sycl::exception(the_errc, "Cannot load oclocFreeOutput() function"); } diff --git a/sycl/source/detail/load_library.hpp b/sycl/source/detail/load_library.hpp new file mode 100644 index 0000000000000..766c1b5617986 --- /dev/null +++ b/sycl/source/detail/load_library.hpp @@ -0,0 +1,36 @@ +//==-------------------------- load_library.hpp ----------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Collection of helper OS-agnostic functions to dynamically load libraries and +// query their symbols. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include + +namespace sycl { +inline namespace _V1 { +namespace detail { + +// Function to load a shared library +// Implementation is OS dependent +void *loadOsLibrary(const std::string &Library); + +// Function to unload a shared library +// Implementation is OS dependent (see posix-ur.cpp and windows-ur.cpp) +int unloadOsLibrary(void *Library); + +// Function to get Address of a symbol defined in the shared +// library, implementation is OS dependent. +void *getOsLibraryFuncAddress(void *Library, const std::string &FunctionName); + +} // namespace detail +} // namespace _V1 +} // namespace sycl diff --git a/sycl/source/detail/load_library_posix.cpp b/sycl/source/detail/load_library_posix.cpp new file mode 100644 index 0000000000000..3f2fd0b6b62be --- /dev/null +++ b/sycl/source/detail/load_library_posix.cpp @@ -0,0 +1,41 @@ +//==-------------------- load_library_posix.cpp ----------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include + +#include +#include + +namespace sycl { +inline namespace _V1 { +namespace detail { + +void *loadOsLibrary(const std::string &LibraryPath) { + // TODO: Check if the option RTLD_NOW is correct. Explore using + // RTLD_DEEPBIND option when there are multiple adapters. + void *so = dlopen(LibraryPath.c_str(), RTLD_NOW); + if (!so && ur::trace(ur::TraceLevel::TRACE_ALL)) { + char *Error = dlerror(); + std::cerr << "SYCL_UR_TRACE: dlopen(" << LibraryPath << ") failed with <" + << (Error ? Error : "unknown error") << ">" << std::endl; + } + return so; +} + +int unloadOsLibrary(void *Library) { return dlclose(Library); } + +void *getOsLibraryFuncAddress(void *Library, const std::string &FunctionName) { + return dlsym(Library, FunctionName.c_str()); +} + +} // namespace detail +} // namespace _V1 +} // namespace sycl diff --git a/sycl/source/detail/load_library_win.cpp b/sycl/source/detail/load_library_win.cpp new file mode 100644 index 0000000000000..3176a068854ea --- /dev/null +++ b/sycl/source/detail/load_library_win.cpp @@ -0,0 +1,72 @@ +//==-------------------- load_library_lin.cpp ------------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include + +#include +#include +#include +#include +#include + +namespace sycl { +inline namespace _V1 { +namespace detail { + +void *loadOsLibrary(const std::string &LibraryPath) { + // Tells the system to not display the critical-error-handler message box. + // Instead, the system sends the error to the calling process. + // This is crucial for graceful handling of shared libs that can't be + // loaded, e.g. due to missing native run-times. + + UINT SavedMode = SetErrorMode(SEM_FAILCRITICALERRORS); + // Exclude current directory from DLL search path + if (!SetDllDirectoryA("")) { + assert(false && "Failed to update DLL search path"); + } + + auto Result = (void *)LoadLibraryExA(LibraryPath.c_str(), NULL, NULL); + (void)SetErrorMode(SavedMode); + if (!SetDllDirectoryA(nullptr)) { + assert(false && "Failed to restore DLL search path"); + } + + return Result; +} + +int unloadOsLibrary(void *Library) { + return (int)FreeLibrary((HMODULE)Library); +} + +void *getOsLibraryFuncAddress(void *Library, const std::string &FunctionName) { + return reinterpret_cast( + GetProcAddress((HMODULE)Library, FunctionName.c_str())); +} + +static std::filesystem::path getCurrentDSODirPath() { + wchar_t Path[MAX_PATH]; + auto Handle = + getOSModuleHandle(reinterpret_cast(&getCurrentDSODirPath)); + DWORD Ret = GetModuleFileName( + reinterpret_cast(ExeModuleHandle == Handle ? 0 : Handle), Path, + MAX_PATH); + assert(Ret < MAX_PATH && "Path is longer than MAX_PATH?"); + assert(Ret > 0 && "GetModuleFileName failed"); + (void)Ret; + + BOOL RetCode = PathRemoveFileSpec(Path); + assert(RetCode && "PathRemoveFileSpec failed"); + (void)RetCode; + + return std::filesystem::path(Path); +} + +} // namespace detail +} // namespace _V1 +} // namespace sycl diff --git a/sycl/source/detail/online_compiler/online_compiler.cpp b/sycl/source/detail/online_compiler/online_compiler.cpp index 5d3c3a381607b..4b0644ca6fdf7 100644 --- a/sycl/source/detail/online_compiler/online_compiler.cpp +++ b/sycl/source/detail/online_compiler/online_compiler.cpp @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// +#include #include -#include #include #include @@ -94,12 +94,12 @@ compileToSPIRV(const std::string &Source, sycl::info::device_type DeviceType, #else static const std::string OclocLibraryName = "libocloc.so"; #endif - void *OclocLibrary = sycl::detail::ur::loadOsLibrary(OclocLibraryName); + void *OclocLibrary = sycl::detail::loadOsLibrary(OclocLibraryName); if (!OclocLibrary) throw online_compile_error("Cannot load ocloc library: " + OclocLibraryName); void *OclocVersionHandle = - sycl::detail::ur::getOsLibraryFuncAddress(OclocLibrary, "oclocVersion"); + sycl::detail::getOsLibraryFuncAddress(OclocLibrary, "oclocVersion"); // The initial versions of ocloc library did not have the oclocVersion() // function. Those versions had the same API as the first version of ocloc // library having that oclocVersion() function. @@ -126,11 +126,11 @@ compileToSPIRV(const std::string &Source, sycl::info::device_type DeviceType, ".N), where (N >= " + std::to_string(CurrentVersionMinor) + ")."); CompileToSPIRVHandle = - sycl::detail::ur::getOsLibraryFuncAddress(OclocLibrary, "oclocInvoke"); + sycl::detail::getOsLibraryFuncAddress(OclocLibrary, "oclocInvoke"); if (!CompileToSPIRVHandle) throw online_compile_error("Cannot load oclocInvoke() function"); - FreeSPIRVOutputsHandle = sycl::detail::ur::getOsLibraryFuncAddress( - OclocLibrary, "oclocFreeOutput"); + FreeSPIRVOutputsHandle = + sycl::detail::getOsLibraryFuncAddress(OclocLibrary, "oclocFreeOutput"); if (!FreeSPIRVOutputsHandle) throw online_compile_error("Cannot load oclocFreeOutput() function"); } diff --git a/sycl/source/detail/posix_ur.cpp b/sycl/source/detail/posix_ur.cpp index 18ffeed1cc60e..2418b668994f6 100644 --- a/sycl/source/detail/posix_ur.cpp +++ b/sycl/source/detail/posix_ur.cpp @@ -17,24 +17,6 @@ namespace sycl { inline namespace _V1 { namespace detail::ur { -void *loadOsLibrary(const std::string &LibraryPath) { - // TODO: Check if the option RTLD_NOW is correct. Explore using - // RTLD_DEEPBIND option when there are multiple adapters. - void *so = dlopen(LibraryPath.c_str(), RTLD_NOW); - if (!so && trace(TraceLevel::TRACE_ALL)) { - char *Error = dlerror(); - std::cerr << "SYCL_UR_TRACE: dlopen(" << LibraryPath << ") failed with <" - << (Error ? Error : "unknown error") << ">" << std::endl; - } - return so; -} - -int unloadOsLibrary(void *Library) { return dlclose(Library); } - -void *getOsLibraryFuncAddress(void *Library, const std::string &FunctionName) { - return dlsym(Library, FunctionName.c_str()); -} - void *getURLoaderLibrary() { return nullptr; } } // namespace detail::ur diff --git a/sycl/source/detail/windows_ur.cpp b/sycl/source/detail/windows_ur.cpp index 6f8d1f7ae6bdf..1768d00ed95b4 100644 --- a/sycl/source/detail/windows_ur.cpp +++ b/sycl/source/detail/windows_ur.cpp @@ -27,54 +27,6 @@ void *GetWinProcAddress(void *module, const char *funcName) { namespace ur { -void *loadOsLibrary(const std::string &LibraryPath) { - // Tells the system to not display the critical-error-handler message box. - // Instead, the system sends the error to the calling process. - // This is crucial for graceful handling of shared libs that can't be - // loaded, e.g. due to missing native run-times. - - UINT SavedMode = SetErrorMode(SEM_FAILCRITICALERRORS); - // Exclude current directory from DLL search path - if (!SetDllDirectoryA("")) { - assert(false && "Failed to update DLL search path"); - } - - auto Result = (void *)LoadLibraryExA(LibraryPath.c_str(), NULL, NULL); - (void)SetErrorMode(SavedMode); - if (!SetDllDirectoryA(nullptr)) { - assert(false && "Failed to restore DLL search path"); - } - - return Result; -} - -int unloadOsLibrary(void *Library) { - return (int)FreeLibrary((HMODULE)Library); -} - -void *getOsLibraryFuncAddress(void *Library, const std::string &FunctionName) { - return reinterpret_cast( - GetProcAddress((HMODULE)Library, FunctionName.c_str())); -} - -static std::filesystem::path getCurrentDSODirPath() { - wchar_t Path[MAX_PATH]; - auto Handle = - getOSModuleHandle(reinterpret_cast(&getCurrentDSODirPath)); - DWORD Ret = GetModuleFileName( - reinterpret_cast(ExeModuleHandle == Handle ? 0 : Handle), Path, - MAX_PATH); - assert(Ret < MAX_PATH && "Path is longer than MAX_PATH?"); - assert(Ret > 0 && "GetModuleFileName failed"); - (void)Ret; - - BOOL RetCode = PathRemoveFileSpec(Path); - assert(RetCode && "PathRemoveFileSpec failed"); - (void)RetCode; - - return std::filesystem::path(Path); -} - void *getURLoaderLibrary() { return getPreloadedURLib(); } } // namespace ur