Skip to content

Commit c7f22bf

Browse files
Qualcomm AI Engine Direct - Support Platform Abstraction Layer (PAL) (pytorch#20050)
### Summary This PR introduces **Platform Abstraction Layer (PAL)** support to enable consistent, platform-independent behavior, including Linux and Windows. ### Scope The goal of this change is to encapsulate platform-specific logic behind a unified interface, improving portability, maintainability, and extensibility of the codebase. ### Supported Platforms - Linux - Windows ### Currently Supported Functionality The initial PAL implementation focuses on the following capabilities: - **Dynamic loading** Provides a unified interface for loading shared libraries at runtime across supported platforms. - **Shared library name construction** Abstracts platform-specific shared library naming conventions (e.g., .so, .dll) to ensure correct library resolution on each platform. ### Future Work - Additional PAL features if required. --------- Co-authored-by: chenweng <chenweng@qti.qualcomm.com>
1 parent 6ed7e4b commit c7f22bf

17 files changed

Lines changed: 397 additions & 45 deletions

backends/qualcomm/CMakeLists.txt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ endif()
119119
include_directories(
120120
BEFORE ${_common_include_directories} ${QNN_SDK_ROOT}/include/QNN
121121
${EXECUTORCH_SOURCE_DIR}/runtime/core/portable_type/c10
122+
${QNN_EXECUTORCH_ROOT_DIR}/runtime/pal/include
122123
)
123124

124125
set(_qnn_schema__srcs backends/qualcomm/serialization/qc_compiler_spec.fbs)
@@ -175,6 +176,7 @@ add_library(qnn_logger STATIC)
175176
add_library(qnn_manager STATIC)
176177
add_library(qnn_mem_manager STATIC)
177178
add_library(qnn_op_package_manager STATIC)
179+
add_library(qnn_pal STATIC)
178180
add_library(qnn_profiler STATIC)
179181
add_library(qnn_schema INTERFACE ${_qnn_schema__outputs})
180182
add_library(qnn_sys_function_interface INTERFACE)
@@ -188,11 +190,12 @@ add_library(wrappers STATIC)
188190
target_link_libraries(wrappers PRIVATE qnn_executorch_logging)
189191
target_link_libraries(
190192
qnn_implementation PRIVATE qnn_function_interface qnn_executorch_logging
191-
${CMAKE_DL_LIBS}
193+
qnn_pal ${CMAKE_DL_LIBS}
192194
)
193195
target_link_libraries(
194-
qnn_sys_implementation PRIVATE qnn_sys_function_interface
195-
qnn_executorch_logging ${CMAKE_DL_LIBS}
196+
qnn_sys_implementation
197+
PRIVATE qnn_sys_function_interface qnn_executorch_logging qnn_pal
198+
${CMAKE_DL_LIBS}
196199
)
197200
target_link_libraries(qnn_executorch_logging PRIVATE qnn_schema)
198201
target_link_libraries(qnn_profiler PRIVATE qnn_executorch_logging)
@@ -243,6 +246,7 @@ target_link_libraries(
243246
qnn_context
244247
qnn_graph
245248
qnn_mem_manager
249+
qnn_pal
246250
)
247251

248252
target_link_libraries(
@@ -297,7 +301,7 @@ else()
297301
)
298302
endif()
299303
target_link_libraries(
300-
shared_buffer PRIVATE qnn_executorch_logging ${CMAKE_DL_LIBS}
304+
shared_buffer PRIVATE qnn_executorch_logging qnn_pal ${CMAKE_DL_LIBS}
301305
)
302306
#
303307
# add linker option

backends/qualcomm/runtime/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,16 @@ target_sources(
4747
shared_buffer PRIVATE ${CMAKE_CURRENT_LIST_DIR}/SharedBuffer.h
4848
${CMAKE_CURRENT_LIST_DIR}/SharedBuffer.cpp
4949
)
50+
51+
# qnn_pal
52+
if(WIN32)
53+
target_sources(
54+
qnn_pal PRIVATE ${CMAKE_CURRENT_LIST_DIR}/pal/src/windows/DynamicLoading.cpp
55+
${CMAKE_CURRENT_LIST_DIR}/pal/src/windows/Path.cpp
56+
)
57+
else()
58+
target_sources(
59+
qnn_pal PRIVATE ${CMAKE_CURRENT_LIST_DIR}/pal/src/linux/DynamicLoading.cpp
60+
${CMAKE_CURRENT_LIST_DIR}/pal/src/linux/Path.cpp
61+
)
62+
endif()

backends/qualcomm/runtime/SharedBuffer.cpp

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
* This source code is licensed under the BSD-style license found in the
66
* LICENSE file in the root directory of this source tree.
77
*/
8-
#include <dlfcn.h>
98
#include <executorch/backends/qualcomm/runtime/Logging.h>
109
#include <executorch/backends/qualcomm/runtime/SharedBuffer.h>
1110

11+
#include <pal/DynamicLoading.h>
12+
1213
// Refer to the QNN HTP Shared Buffer Tutorial
1314
// in Qualcomm® AI Engine Direct document
1415
constexpr uint8_t RPCMEM_HEAP_ID_SYSTEM = 25;
@@ -81,10 +82,10 @@ SharedBuffer& SharedBuffer::GetSharedBufferManager() {
8182
std::lock_guard<std::mutex> lk(init_mutex_);
8283
static SharedBuffer shared_buffer_manager;
8384
if (!shared_buffer_manager.GetInitialize()) {
84-
#if defined(__aarch64__)
85+
#if defined(__ANDROID__)
8586
Error status = shared_buffer_manager.Load();
8687
#else
87-
// For x86_64 platform
88+
// libcdsprpc.so is an Android vendor library; skip on all other platforms.
8889
Error status = Error::Ok;
8990
#endif
9091
if (status == Error::Ok) {
@@ -95,7 +96,7 @@ SharedBuffer& SharedBuffer::GetSharedBufferManager() {
9596
}
9697

9798
SharedBuffer::~SharedBuffer() {
98-
#if defined(__aarch64__)
99+
#if defined(__ANDROID__)
99100
if (initialize_) {
100101
SharedBuffer::GetSharedBufferManager().UnLoad();
101102
}
@@ -163,23 +164,27 @@ bool SharedBuffer::IsAllocated(void* buf) {
163164
Error SharedBuffer::Load() {
164165
// On Android, 32-bit and 64-bit libcdsprpc.so can be found at /vendor/lib/
165166
// and /vendor/lib64/ respectively.
166-
lib_cdsp_rpc_ = dlopen("libcdsprpc.so", RTLD_NOW | RTLD_LOCAL);
167+
lib_cdsp_rpc_ = pal::dynamic_loading::DlOpen(
168+
"libcdsprpc.so",
169+
pal::dynamic_loading::DL_NOW | pal::dynamic_loading::DL_LOCAL);
167170
if (lib_cdsp_rpc_ == nullptr) {
168171
QNN_EXECUTORCH_LOG_ERROR(
169-
"Unable to load shared buffer. dlerror(): %s", dlerror());
172+
"Unable to load shared buffer. dlerror(): %s",
173+
pal::dynamic_loading::DlError());
170174
return Error::Internal;
171175
}
172176
rpc_mem_alloc_ = reinterpret_cast<RpcMemAllocFn_t>( // NOLINT
173-
dlsym(lib_cdsp_rpc_, "rpcmem_alloc"));
177+
pal::dynamic_loading::DlSym(lib_cdsp_rpc_, "rpcmem_alloc"));
174178
rpc_mem_free_ = reinterpret_cast<RpcMemFreeFn_t>( // NOLINT
175-
dlsym(lib_cdsp_rpc_, "rpcmem_free"));
179+
pal::dynamic_loading::DlSym(lib_cdsp_rpc_, "rpcmem_free"));
176180
rpc_mem_to_fd_ = reinterpret_cast<RpcMemToFdFn_t>( // NOLINT
177-
dlsym(lib_cdsp_rpc_, "rpcmem_to_fd"));
181+
pal::dynamic_loading::DlSym(lib_cdsp_rpc_, "rpcmem_to_fd"));
178182
if (nullptr == rpc_mem_alloc_ || nullptr == rpc_mem_free_ ||
179183
nullptr == rpc_mem_to_fd_) {
180184
QNN_EXECUTORCH_LOG_ERROR(
181-
"Unable to access symbols in shared buffer. dlerror(): %s", dlerror());
182-
dlclose(lib_cdsp_rpc_);
185+
"Unable to access symbols in shared buffer. dlerror(): %s",
186+
pal::dynamic_loading::DlError());
187+
pal::dynamic_loading::DlClose(lib_cdsp_rpc_);
183188
return Error::Internal;
184189
}
185190
return Error::Ok;
@@ -199,9 +204,10 @@ void SharedBuffer::AddCusomMemTensorAddr(void* tensor_addr, void* custom_mem) {
199204
};
200205

201206
Error SharedBuffer::UnLoad() {
202-
if (dlclose(lib_cdsp_rpc_) != 0) {
207+
if (pal::dynamic_loading::DlClose(lib_cdsp_rpc_) != 0) {
203208
QNN_EXECUTORCH_LOG_ERROR(
204-
"Unable to close shared buffer. dlerror(): %s", dlerror());
209+
"Unable to close shared buffer. dlerror(): %s",
210+
pal::dynamic_loading::DlError());
205211
return Error::Internal;
206212
};
207213
return Error::Ok;

backends/qualcomm/runtime/backends/QnnBackendUnifiedRegistry.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <executorch/backends/qualcomm/runtime/backends/QnnSysImplementation.h>
1616
#include <executorch/runtime/core/error.h>
1717

18+
#include <pal/Path.h>
19+
1820
#include <memory>
1921
#include <mutex>
2022
#include <string>
@@ -79,12 +81,16 @@ class QnnBackendUnifiedRegistry {
7981
// For macro, refer to executorch/backends/qualcomm/CMakeLists.txt
8082
static constexpr const char* htp_library_name_ = HEXAGON_LIB;
8183
#else
82-
static constexpr const char* htp_library_name_ = "libQnnHtp.so";
84+
static inline const std::string htp_library_name_ =
85+
pal::path::GetLibraryName("QnnHtp");
8386
#endif
84-
static constexpr const char* gpu_library_name_ = "libQnnGpu.so";
85-
static constexpr const char* dsp_library_name_ = "libQnnDsp.so";
87+
static inline const std::string gpu_library_name_ =
88+
pal::path::GetLibraryName("QnnGpu");
89+
static inline const std::string dsp_library_name_ =
90+
pal::path::GetLibraryName("QnnDsp");
8691
// Lpai library name is same for both traditional build and hexagon build.
87-
static constexpr const char* lpai_library_name_ = "libQnnLpai.so";
92+
static inline const std::string lpai_library_name_ =
93+
pal::path::GetLibraryName("QnnLpai");
8894

8995
std::unique_ptr<const QnnSaver_Config_t*[]> GetImplementationConfig(
9096
const QnnExecuTorchOptions* options) {

backends/qualcomm/runtime/backends/QnnDlcManager.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <executorch/backends/qualcomm/runtime/backends/QnnBackendUnifiedRegistry.h>
1515
#include <executorch/backends/qualcomm/runtime/backends/ir/IrContext.h>
1616

17+
#include <pal/Path.h>
18+
1719
namespace executorch {
1820
namespace backends {
1921
namespace qnn {
@@ -105,7 +107,8 @@ class QnnDlcManager {
105107
}
106108

107109
private:
108-
static constexpr const char* library_name_ = "libQnnIr.so";
110+
static inline const std::string library_name_ =
111+
pal::path::GetLibraryName("QnnIr");
109112

110113
const QnnExecuTorchContextBinary& qnn_context_blob_;
111114
const QnnExecuTorchOptions* options_;

backends/qualcomm/runtime/backends/QnnImplementation.cpp

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct DlCloser {
2222
int operator()(void* handle) {
2323
if (handle == nullptr)
2424
return 0;
25-
return dlclose(handle);
25+
return pal::dynamic_loading::DlClose(handle);
2626
}
2727
};
2828

@@ -59,22 +59,26 @@ const QnnInterface_t* QnnImplementation::StartBackend(
5959

6060
#ifdef __hexagon__
6161
FARF(RUNTIME_HIGH, "Opening lib_path %s", lib_path.c_str());
62-
std::unique_ptr<void, DlCloser> lib_handle(
63-
dlopen(lib_path.c_str(), RTLD_NOW | RTLD_GLOBAL));
62+
std::unique_ptr<void, DlCloser> lib_handle(pal::dynamic_loading::DlOpen(
63+
lib_path.c_str(),
64+
pal::dynamic_loading::DL_NOW | pal::dynamic_loading::DL_GLOBAL));
6465
FARF(RUNTIME_HIGH, "Done loading lib_path %s", lib_path.c_str());
6566
#else
6667
// If the library is already loaded, return the handle.
67-
std::unique_ptr<void, DlCloser> lib_handle(
68-
dlopen(lib_path.c_str(), RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL));
68+
std::unique_ptr<void, DlCloser> lib_handle(pal::dynamic_loading::DlOpen(
69+
lib_path.c_str(),
70+
pal::dynamic_loading::DL_NOW | pal::dynamic_loading::DL_NOLOAD |
71+
pal::dynamic_loading::DL_GLOBAL));
6972
if (!lib_handle) {
70-
lib_handle = std::unique_ptr<void, DlCloser>(
71-
dlopen(lib_path.c_str(), RTLD_NOW | RTLD_GLOBAL));
73+
lib_handle = std::unique_ptr<void, DlCloser>(pal::dynamic_loading::DlOpen(
74+
lib_path.c_str(),
75+
pal::dynamic_loading::DL_NOW | pal::dynamic_loading::DL_GLOBAL));
7276
}
7377
if (lib_handle == nullptr) {
7478
QNN_EXECUTORCH_LOG_ERROR(
7579
"Cannot Open QNN library %s, with error: %s",
7680
lib_path.c_str(),
77-
dlerror());
81+
pal::dynamic_loading::DlError());
7882
return nullptr;
7983
}
8084
#endif
@@ -87,7 +91,7 @@ const QnnInterface_t* QnnImplementation::StartBackend(
8791
QNN_EXECUTORCH_LOG_ERROR(
8892
"QnnImplementation::Load Cannot load symbol "
8993
"QnnInterface_getProviders : %s",
90-
dlerror());
94+
pal::dynamic_loading::DlError());
9195
return nullptr;
9296
}
9397

@@ -131,12 +135,12 @@ Error QnnImplementation::Unload() {
131135
return Error::Ok;
132136
}
133137

134-
int dlclose_error = dlclose(lib_handle_);
138+
int dlclose_error = pal::dynamic_loading::DlClose(lib_handle_);
135139
if (dlclose_error != 0) {
136140
QNN_EXECUTORCH_LOG_ERROR(
137141
"Fail to close QNN backend %s with error %s",
138142
lib_path_.c_str(),
139-
dlerror());
143+
pal::dynamic_loading::DlError());
140144
return Error::Internal;
141145
}
142146
lib_handle_ = nullptr;

backends/qualcomm/runtime/backends/QnnImplementation.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@
1010
#include <executorch/backends/qualcomm/runtime/Logging.h>
1111
#include <executorch/backends/qualcomm/runtime/backends/QnnFunctionInterface.h>
1212

13-
#include <dlfcn.h>
13+
#include <pal/DynamicLoading.h>
14+
1415
#include <string>
1516
namespace executorch {
1617
namespace backends {
1718
namespace qnn {
1819

1920
template <typename Fn>
2021
Fn loadQnnFunction(void* handle, const char* function_name) {
21-
return reinterpret_cast<Fn>(dlsym(handle, function_name)); // NOLINT
22+
return reinterpret_cast<Fn>( // NOLINT
23+
pal::dynamic_loading::DlSym(handle, function_name));
2224
}
2325

2426
class QnnImplementation {

backends/qualcomm/runtime/backends/QnnSysImplementation.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
#include <dlfcn.h>
109
#include <executorch/backends/qualcomm/runtime/backends/QnnSysImplementation.h>
10+
#include <pal/DynamicLoading.h>
1111
namespace executorch {
1212
namespace backends {
1313
namespace qnn {
@@ -16,24 +16,27 @@ using executorch::runtime::Error;
1616

1717
Error QnnSystemImplementation::Load() {
1818
Qnn_ErrorHandle_t error = QNN_SUCCESS;
19-
void* lib_handle_ = dlopen(lib_path_.c_str(), RTLD_NOW | RTLD_LOCAL);
19+
lib_handle_ = pal::dynamic_loading::DlOpen(
20+
lib_path_.c_str(),
21+
pal::dynamic_loading::DL_NOW | pal::dynamic_loading::DL_LOCAL);
2022
if (lib_handle_ == nullptr) {
2123
QNN_EXECUTORCH_LOG_ERROR(
2224
"Cannot Open QNN library %s, with error: %s",
2325
lib_path_.c_str(),
24-
dlerror());
26+
pal::dynamic_loading::DlError());
2527
return Error::Internal;
2628
}
2729

2830
auto* get_providers =
2931
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3032
reinterpret_cast<QnnSystemInterfaceGetProvidersFn*>(
31-
dlsym(lib_handle_, "QnnSystemInterface_getProviders"));
33+
pal::dynamic_loading::DlSym(
34+
lib_handle_, "QnnSystemInterface_getProviders"));
3235
if (get_providers == nullptr) {
3336
QNN_EXECUTORCH_LOG_ERROR(
3437
"QnnSystemImplementation::Load Cannot load symbol "
3538
"QnnSystemInterface_getProviders : %s",
36-
dlerror());
39+
pal::dynamic_loading::DlError());
3740
return Error::Internal;
3841
}
3942

@@ -65,10 +68,11 @@ Error QnnSystemImplementation::Unload() {
6568
if (lib_handle_ == nullptr)
6669
return Error::Ok;
6770

68-
int dlclose_error = dlclose(lib_handle_);
71+
int dlclose_error = pal::dynamic_loading::DlClose(lib_handle_);
6972
if (dlclose_error != 0) {
7073
QNN_EXECUTORCH_LOG_WARN(
71-
"Failed to close QnnSystem library with error %s", dlerror());
74+
"Failed to close QnnSystem library with error %s",
75+
pal::dynamic_loading::DlError());
7276
return Error::Internal;
7377
}
7478

backends/qualcomm/runtime/backends/ir/target/QnnDlcManager.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88
#include <executorch/backends/qualcomm/runtime/backends/QnnDlcManager.h>
9+
#ifndef _WIN32
910
#include <fcntl.h>
1011
#include <sys/mman.h>
1112
#include <sys/stat.h>
1213
#include <unistd.h>
14+
#endif
1315
#include <fstream>
1416

1517
namespace executorch {

0 commit comments

Comments
 (0)