diff --git a/.gitignore b/.gitignore index bd3528a4c4b..ab64ecd7d73 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,13 @@ .hypothesis buck-out/ cmake-out/ -cmake-android-out/ -cmake-out-android/ -cmake-ios-out/ +cmake-*-out/ +cmake-out-*/ ethos-u-scratch/ executorch.egg-info pip-out/ __pycache__/ +.python-version # Any exported models and profiling outputs *.pte diff --git a/backends/qualcomm/.gitignore b/backends/qualcomm/.gitignore new file mode 100644 index 00000000000..b2ddb055dcb --- /dev/null +++ b/backends/qualcomm/.gitignore @@ -0,0 +1 @@ +*_generated.h diff --git a/backends/qualcomm/CMakeLists.txt b/backends/qualcomm/CMakeLists.txt index a8265df8c7b..e2a94ed9685 100644 --- a/backends/qualcomm/CMakeLists.txt +++ b/backends/qualcomm/CMakeLists.txt @@ -183,9 +183,11 @@ target_link_libraries( qnn_executorch_backend PRIVATE qnn_executorch_header qnn_schema qnn_manager executorch_no_prim_ops qcir_utils extension_tensor ) -set_target_properties( - qnn_executorch_backend PROPERTIES LINK_FLAGS "-Wl,-rpath='$ORIGIN'" -) +if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") + set_target_properties( + qnn_executorch_backend PROPERTIES LINK_FLAGS "-Wl,-rpath='$ORIGIN'" + ) +endif() target_link_libraries(utils PRIVATE qnn_executorch_logging) target_link_libraries( shared_buffer PRIVATE qnn_executorch_logging ${CMAKE_DL_LIBS} @@ -221,7 +223,7 @@ add_subdirectory( install(TARGETS qnn_executorch_backend DESTINATION lib) # QNN pybind -if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") +if(EXECUTORCH_BUILD_PYBIND) add_subdirectory( ${EXECUTORCH_SOURCE_DIR}/third-party/pybind11 ${CMAKE_CURRENT_BINARY_DIR}/pybind11 diff --git a/backends/qualcomm/aot/ir/qcir_utils.cpp b/backends/qualcomm/aot/ir/qcir_utils.cpp index 75446bb7330..50b35acb4c6 100755 --- a/backends/qualcomm/aot/ir/qcir_utils.cpp +++ b/backends/qualcomm/aot/ir/qcir_utils.cpp @@ -138,7 +138,7 @@ flatbuffers::Offset ToQuantizeParam( size_t len = param.axisScaleOffsetEncoding.numScaleOffsets; axis = param.axisScaleOffsetEncoding.axis; data.reserve(len); - for (uint i = 0; i < len; ++i) { + for (size_t i = 0; i < len; ++i) { data.emplace_back(qcir::ScaleOffset( param.axisScaleOffsetEncoding.scaleOffset[i].scale, param.axisScaleOffsetEncoding.scaleOffset[i].offset)); diff --git a/backends/qualcomm/aot/python/CMakeLists.txt b/backends/qualcomm/aot/python/CMakeLists.txt index 337cfae1776..f4ce70dc314 100644 --- a/backends/qualcomm/aot/python/CMakeLists.txt +++ b/backends/qualcomm/aot/python/CMakeLists.txt @@ -15,3 +15,6 @@ target_sources( PyQnnWrapperAdaptor PUBLIC ${CMAKE_CURRENT_LIST_DIR}/PyQnnWrapperAdaptor.cpp ${CMAKE_CURRENT_LIST_DIR}/PyQnnWrapperAdaptor.h ) + +target_compile_options(PyQnnManagerAdaptor PRIVATE -fexceptions) +target_compile_options(PyQnnWrapperAdaptor PRIVATE -fexceptions) diff --git a/backends/qualcomm/runtime/QnnManager.cpp b/backends/qualcomm/runtime/QnnManager.cpp index f4275f0ab3d..74abff4d8f7 100644 --- a/backends/qualcomm/runtime/QnnManager.cpp +++ b/backends/qualcomm/runtime/QnnManager.cpp @@ -528,24 +528,27 @@ Error QnnManager::Compile( } // namespace qnn } // namespace executor } // namespace torch -void* QnnExecuTorchAllocCustomMem(size_t bytes, size_t alignment) { + +#define EXPORT __attribute__((visibility("default"))) + +EXPORT void* QnnExecuTorchAllocCustomMem(size_t bytes, size_t alignment) { void* buffer_ptr = torch::executor::qnn::SharedBuffer::GetSharedBufferManager().AllocMem( bytes, alignment); return buffer_ptr; } -void QnnExecuTorchFreeCustomMem(void* buffer_ptr) { +EXPORT void QnnExecuTorchFreeCustomMem(void* buffer_ptr) { torch::executor::qnn::SharedBuffer::GetSharedBufferManager().FreeMem( buffer_ptr); } -void QnnExecuTorchAddCustomMemTensorAddr(void* tensor_addr, void* custom_mem) { +EXPORT void QnnExecuTorchAddCustomMemTensorAddr(void* tensor_addr, void* custom_mem) { torch::executor::qnn::SharedBuffer::GetSharedBufferManager() .AddCusomMemTensorAddr(tensor_addr, custom_mem); } -void QnnExecuTorchAddCustomMemTensorInfo(const CustomMemTensorInfo& info) { +EXPORT void QnnExecuTorchAddCustomMemTensorInfo(const CustomMemTensorInfo& info) { torch::executor::qnn::SharedBuffer::GetSharedBufferManager() .AddCusomMemTensorInfo(info); } diff --git a/backends/qualcomm/runtime/QnnManager.h b/backends/qualcomm/runtime/QnnManager.h index 3d1cc3863aa..7dfaadf2e6b 100644 --- a/backends/qualcomm/runtime/QnnManager.h +++ b/backends/qualcomm/runtime/QnnManager.h @@ -80,9 +80,15 @@ class QnnManager { private: Error LoadQnnLibrary(); +#ifdef _WIN32 + static constexpr const char* htp_library_name_ = "QnnHtp.dll"; + static constexpr const char* gpu_library_name_ = "QnnGpu.dll"; + static constexpr const char* dsp_library_name_ = "QnnDsp.dll"; +#else static constexpr const char* htp_library_name_ = "libQnnHtp.so"; static constexpr const char* gpu_library_name_ = "libQnnGpu.so"; static constexpr const char* dsp_library_name_ = "libQnnDsp.so"; +#endif QnnExecuTorchContextBinary qnn_context_blob_; std::unique_ptr backend_params_ptr_; diff --git a/backends/qualcomm/runtime/SharedBuffer.cpp b/backends/qualcomm/runtime/SharedBuffer.cpp index 2b2a729835c..fe273395468 100644 --- a/backends/qualcomm/runtime/SharedBuffer.cpp +++ b/backends/qualcomm/runtime/SharedBuffer.cpp @@ -5,7 +5,9 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ +#ifdef __ANDROID__ #include +#endif #include #include @@ -158,6 +160,10 @@ bool SharedBuffer::IsAllocated(void* buf) { } Error SharedBuffer::Load() { +#ifndef __ANDROID__ + QNN_EXECUTORCH_LOG_WARN("Shared buffer is not supported on this platform."); + return Error::Ok; +#else // On Android, 32-bit and 64-bit libcdsprpc.so can be found at /vendor/lib/ // and /vendor/lib64/ respectively. lib_cdsp_rpc_ = dlopen("libcdsprpc.so", RTLD_NOW | RTLD_LOCAL); @@ -180,6 +186,7 @@ Error SharedBuffer::Load() { return Error::Internal; } return Error::Ok; +#endif } void SharedBuffer::AddCusomMemTensorAddr(void* tensor_addr, void* custom_mem) { @@ -192,12 +199,16 @@ void SharedBuffer::AddCusomMemTensorInfo(const CustomMemTensorInfo& info) { } Error SharedBuffer::UnLoad() { +#ifndef __ANDROID__ + return Error::Ok; +#else if (dlclose(lib_cdsp_rpc_) != 0) { QNN_EXECUTORCH_LOG_ERROR( "Unable to close shared buffer. dlerror(): %s", dlerror()); return Error::Internal; }; return Error::Ok; +#endif } } // namespace qnn } // namespace executor diff --git a/backends/qualcomm/runtime/SharedBuffer.h b/backends/qualcomm/runtime/SharedBuffer.h index 9d01e67c8e2..74bb3e8a93d 100644 --- a/backends/qualcomm/runtime/SharedBuffer.h +++ b/backends/qualcomm/runtime/SharedBuffer.h @@ -82,7 +82,7 @@ class SharedBuffer final { // Pointer to the dlopen'd libcdsprpc.so shared library which contains // rpcmem_alloc, rpcmem_free, rpcmem_to_fd APIs - void* lib_cdsp_rpc_; + [[maybe_unused]] void* lib_cdsp_rpc_; // Function pointer to rpcmem_alloc RpcMemAllocFn_t rpc_mem_alloc_; // Function pointer to rpcmem_free diff --git a/backends/qualcomm/runtime/Utils.cpp b/backends/qualcomm/runtime/Utils.cpp index c049d3720ee..5fe46fe6155 100644 --- a/backends/qualcomm/runtime/Utils.cpp +++ b/backends/qualcomm/runtime/Utils.cpp @@ -7,7 +7,11 @@ */ #include #include +#ifdef _WIN32 +#include +#else #include +#endif namespace torch { namespace executor { namespace qnn { @@ -24,7 +28,11 @@ void CreateDirectory(const std::string& path) { return; } CreateDirectory(subdir); +#ifdef _WIN32 + int mkdir_err = _mkdir(subdir.c_str()); +#else int mkdir_err = mkdir(subdir.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); +#endif if (mkdir_err != 0 && errno != EEXIST) { std::string err_msg = "Failed to create " + subdir + " folder\n"; QNN_EXECUTORCH_LOG_ERROR(err_msg.c_str()); diff --git a/backends/qualcomm/runtime/backends/CMakeLists.txt b/backends/qualcomm/runtime/backends/CMakeLists.txt index ed61d7545a9..ef894cfbc45 100644 --- a/backends/qualcomm/runtime/backends/CMakeLists.txt +++ b/backends/qualcomm/runtime/backends/CMakeLists.txt @@ -44,8 +44,17 @@ target_sources( ) # qnn_device +set(BACKEND_ARCH ${CMAKE_SYSTEM_PROCESSOR}) +if(BACKEND_ARCH STREQUAL "arm64" OR BACKEND_ARCH STREQUAL "aarch64" OR + BACKEND_ARCH STREQUAL "arm64-v8a") + set(BACKEND_ARCH "aarch64") +elseif(BACKEND_ARCH STREQUAL "x86_64") + set(BACKEND_ARCH "x86_64") +else() + message(FATAL_ERROR "Unsupported architecture: ${BACKEND_ARCH}") +endif() set(HOST_ARCHITECTURE - ${CMAKE_CURRENT_LIST_DIR}/htpbackend/${CMAKE_SYSTEM_PROCESSOR} + ${CMAKE_CURRENT_LIST_DIR}/htpbackend/${BACKEND_ARCH} ) target_sources( diff --git a/backends/qualcomm/runtime/backends/QnnBackendCache.h b/backends/qualcomm/runtime/backends/QnnBackendCache.h index ad6d3d0bd7b..9cb77b4043c 100644 --- a/backends/qualcomm/runtime/backends/QnnBackendCache.h +++ b/backends/qualcomm/runtime/backends/QnnBackendCache.h @@ -58,7 +58,11 @@ class QnnBackendCache { QnnExecuTorchContextBinary qnn_context_blob_; QnnSystemContext_Handle_t sys_context_handle_{nullptr}; +#ifdef _WIN32 + QnnSystemImplementation qnn_sys_impl_{ "QnnSystem.dll" }; +#else QnnSystemImplementation qnn_sys_impl_{"libQnnSystem.so"}; +#endif std::string graph_name_; std::vector input_tensor_structs_; std::vector output_tensor_structs_; diff --git a/backends/qualcomm/runtime/backends/QnnBackendFactory.cpp b/backends/qualcomm/runtime/backends/QnnBackendFactory.cpp index 9fb292613a3..4648e06f98b 100644 --- a/backends/qualcomm/runtime/backends/QnnBackendFactory.cpp +++ b/backends/qualcomm/runtime/backends/QnnBackendFactory.cpp @@ -24,8 +24,12 @@ std::unique_ptr QnnBackendFactory::Create( const std::string skel_library_dir = htp_options->skel_library_dir()->str(); if (!skel_library_dir.empty()) { +#ifdef _WIN32 + _putenv_s("ADSP_LIBRARY_PATH", skel_library_dir.c_str()); +#else setenv( "ADSP_LIBRARY_PATH", skel_library_dir.c_str(), /*overwrite=*/1); +#endif } QNN_EXECUTORCH_LOG_INFO( "skel_library_dir: %s", skel_library_dir.c_str()); diff --git a/backends/qualcomm/runtime/backends/QnnFunctionInterface.h b/backends/qualcomm/runtime/backends/QnnFunctionInterface.h index 5ea187ffa52..c78ce7a781b 100644 --- a/backends/qualcomm/runtime/backends/QnnFunctionInterface.h +++ b/backends/qualcomm/runtime/backends/QnnFunctionInterface.h @@ -7,6 +7,11 @@ */ #pragma once +// Dummy fix `interface` definition in Windows +#if defined(__WIN32__) && defined(interface) +#undef interface +#endif + #include "QnnInterface.h" #include "Saver/QnnSaver.h" diff --git a/backends/qualcomm/runtime/backends/QnnImplementation.cpp b/backends/qualcomm/runtime/backends/QnnImplementation.cpp index 0f7d45b54b2..71bfc151e66 100644 --- a/backends/qualcomm/runtime/backends/QnnImplementation.cpp +++ b/backends/qualcomm/runtime/backends/QnnImplementation.cpp @@ -5,7 +5,11 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ +#ifdef _WIN32 +#include +#else #include +#endif #include #include "QnnInterface.h" @@ -14,7 +18,11 @@ namespace executor { namespace qnn { template Fn loadQnnFunction(void* handle, const char* function_name) { +#ifdef _WIN32 + return reinterpret_cast(GetProcAddress(reinterpret_cast(handle), function_name)); +#else return reinterpret_cast(dlsym(handle, function_name)); // NOLINT +#endif } Error QnnImplementation::InitBackend( @@ -54,13 +62,24 @@ Error QnnImplementation::StartBackend( const std::string& lib_path, const QnnSaver_Config_t** saver_config) { Qnn_ErrorHandle_t error = QNN_SUCCESS; +#ifdef _WIN32 + void* lib_handle = LoadLibrary(lib_path.c_str()); +#else void* lib_handle = dlopen(lib_path.c_str(), RTLD_NOW | RTLD_GLOBAL); +#endif if (lib_handle == nullptr) { +#ifdef _WIN32 + QNN_EXECUTORCH_LOG_ERROR( + "Cannot Open QNN library %s, with error: %d", + lib_path.c_str(), + GetLastError()); +#else QNN_EXECUTORCH_LOG_ERROR( "Cannot Open QNN library %s, with error: %s", lib_path.c_str(), dlerror()); +#endif return Error::Internal; } @@ -72,7 +91,11 @@ Error QnnImplementation::StartBackend( QNN_EXECUTORCH_LOG_ERROR( "QnnImplementation::Load Cannot load symbol " "QnnInterface_getProviders : %s", +#ifdef _WIN32 + GetLastError()); +#else dlerror()); +#endif return Error::Internal; } @@ -120,6 +143,14 @@ Error QnnImplementation::StartBackend( if (loaded_lib_handle_.count(backend_id) > 0) { QNN_EXECUTORCH_LOG_WARN("closing %pK...", loaded_lib_handle_[backend_id]); +#ifdef _WIN32 + if (FreeLibrary(reinterpret_cast(loaded_lib_handle_[backend_id])) == 0) { + QNN_EXECUTORCH_LOG_WARN( + "Sadly, fail to close %pK with error %d", + loaded_lib_handle_[backend_id], + GetLastError()); + } +#else int dlclose_error = dlclose(loaded_lib_handle_[backend_id]); if (dlclose_error != 0) { QNN_EXECUTORCH_LOG_WARN( @@ -127,6 +158,7 @@ Error QnnImplementation::StartBackend( loaded_lib_handle_[backend_id], dlerror()); } +#endif } loaded_lib_handle_[backend_id] = lib_handle; @@ -138,6 +170,15 @@ Error QnnImplementation::StartBackend( lib_path_to_backend_id_.erase(lib_path); loaded_backend_.erase(backend_id); +#ifdef _WIN32 + if (FreeLibrary(reinterpret_cast(loaded_lib_handle_[backend_id])) == 0) { + QNN_EXECUTORCH_LOG_WARN( + "fail to close %pK after backend-init " + "failure, with error %d", + loaded_lib_handle_[backend_id], + GetLastError()); + } +#else int dlclose_error = dlclose(loaded_lib_handle_[backend_id]); if (dlclose_error != 0) { QNN_EXECUTORCH_LOG_WARN( @@ -146,6 +187,7 @@ Error QnnImplementation::StartBackend( loaded_lib_handle_[backend_id], dlerror()); } +#endif loaded_lib_handle_.erase(backend_id); return be_init_st; @@ -160,12 +202,22 @@ Error QnnImplementation::TerminateAllBackends() { loaded_backend_.clear(); for (auto& it : loaded_lib_handle_) { +#ifdef _WIN32 + if (FreeLibrary(reinterpret_cast(it.second)) == 0) { + QNN_EXECUTORCH_LOG_ERROR( + "Fail to close QNN backend %d with error %d", + it.first, + GetLastError()); + ret_status = Error::Internal; + } +#else int dlclose_error = dlclose(it.second); if (dlclose_error != 0) { QNN_EXECUTORCH_LOG_ERROR( "Fail to close QNN backend %d with error %s", it.first, dlerror()); ret_status = Error::Internal; } +#endif } loaded_lib_handle_.clear(); lib_path_to_backend_id_.clear(); diff --git a/backends/qualcomm/runtime/backends/QnnSysImplementation.cpp b/backends/qualcomm/runtime/backends/QnnSysImplementation.cpp index 519dd867d4a..e471b54881c 100644 --- a/backends/qualcomm/runtime/backends/QnnSysImplementation.cpp +++ b/backends/qualcomm/runtime/backends/QnnSysImplementation.cpp @@ -6,7 +6,11 @@ * LICENSE file in the root directory of this source tree. */ +#ifdef _WIN32 +#include +#else #include +#endif #include namespace torch { namespace executor { @@ -14,6 +18,28 @@ namespace qnn { Error QnnSystemImplementation::Load() { Qnn_ErrorHandle_t error = QNN_SUCCESS; +#ifdef _WIN32 + HMODULE lib_handle_ = LoadLibrary(lib_path_.c_str()); + if (lib_handle_ == nullptr) { + QNN_EXECUTORCH_LOG_ERROR( + "Cannot Open QNN library %s, with error: %d", + lib_path_.c_str(), + GetLastError()); + return Error::Internal; + } + + auto* get_providers = + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast( + GetProcAddress(lib_handle_, "QnnSystemInterface_getProviders")); + if (get_providers == nullptr) { + QNN_EXECUTORCH_LOG_ERROR( + "QnnSystemImplementation::Load Cannot load symbol " + "QnnSystemInterface_getProviders : %d", + GetLastError()); + return Error::Internal; + } +#else void* lib_handle_ = dlopen(lib_path_.c_str(), RTLD_NOW | RTLD_LOCAL); if (lib_handle_ == nullptr) { QNN_EXECUTORCH_LOG_ERROR( @@ -34,6 +60,7 @@ Error QnnSystemImplementation::Load() { dlerror()); return Error::Internal; } +#endif std::uint32_t num_providers; const QnnSystemInterface_t** provider_list = nullptr; @@ -64,12 +91,20 @@ Error QnnSystemImplementation::Unload() { if (lib_handle_ == nullptr) return Error::Ok; +#ifdef _WIN32 + if (!FreeLibrary(reinterpret_cast(lib_handle_))) { + QNN_EXECUTORCH_LOG_WARN( + "Failed to close QnnSystem library with error %d", GetLastError()); + return Error::Internal; + } +#else int dlclose_error = dlclose(lib_handle_); if (dlclose_error != 0) { QNN_EXECUTORCH_LOG_WARN( "Failed to close QnnSystem library with error %s", dlerror()); return Error::Internal; } +#endif lib_handle_ = nullptr; diff --git a/build/Utils.cmake b/build/Utils.cmake index 3ea616d5900..620ba27114e 100644 --- a/build/Utils.cmake +++ b/build/Utils.cmake @@ -195,12 +195,34 @@ function(extract_sources sources_file) else() message(FATAL_ERROR "Unsupported ANDROID_ABI setting ${ANDROID_ABI}. Please add it here!") endif() + else() + if(NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "${CMAKE_HOST_SYSTEM_NAME}") + if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") + set(fake_host_arg "--fake-host=macos") + else() + string(TOLOWER "${CMAKE_SYSTEM_NAME}" lowercase_system_name) + set(fake_host_arg "--fake-host=${lowercase_system_name}") + endif() + endif() + if (NOT "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "${CMAKE_HOST_SYSTEM_PROCESSOR}") + if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") + set(fake_arch_arg "--fake-arch=x8664") + else() + set(fake_arch_arg "--fake-arch=aarch64") + endif() + endif() + if ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") + set(fake_arch_arg "--fake-arch=aarch64") + elseif("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64") + set(fake_arch_arg "--fake-arch=x8664") + endif() endif() + execute_process( COMMAND ${PYTHON_EXECUTABLE} ${executorch_root}/build/extract_sources.py --config=${executorch_root}/build/cmake_deps.toml --out=${sources_file} - --buck2=${BUCK2} ${target_platforms_arg} + --buck2=${BUCK2} ${target_platforms_arg} ${fake_host_arg} ${fake_arch_arg} OUTPUT_VARIABLE gen_srcs_output ERROR_VARIABLE gen_srcs_error RESULT_VARIABLE gen_srcs_exit_code diff --git a/build/executorch-config.cmake b/build/executorch-config.cmake index 18b6c7801b9..f78776b6ce9 100644 --- a/build/executorch-config.cmake +++ b/build/executorch-config.cmake @@ -12,6 +12,10 @@ cmake_minimum_required(VERSION 3.19) +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll;.a") +endif() + set(_root "${CMAKE_CURRENT_LIST_DIR}/../..") set(required_lib_list executorch executorch_no_prim_ops portable_kernels) foreach(lib ${required_lib_list}) @@ -62,6 +66,7 @@ set(lib_list quantized_kernels quantized_ops_lib quantized_ops_aot_lib + custom_ops ) foreach(lib ${lib_list}) # Name of the variable which stores result of the find_library search @@ -83,7 +88,11 @@ foreach(lib ${lib_list}) # keep all libs as static when CMAKE_TOOLCHAIN_IOS is used add_library(${lib} STATIC IMPORTED) endif() - set_target_properties(${lib} PROPERTIES IMPORTED_LOCATION "${${lib_var}}") + if ("${${lib_var}}" MATCHES ".dll$") + set_target_properties(${lib} PROPERTIES IMPORTED_LOCATION "${${lib_var}}" IMPORTED_IMPLIB "${${lib_var}}.a") + else() + set_target_properties(${lib} PROPERTIES IMPORTED_LOCATION "${${lib_var}}") + endif() target_include_directories(${lib} INTERFACE ${_root}) endif() endforeach() diff --git a/build/extract_sources.py b/build/extract_sources.py index 5004fe0c508..1ab62f5621d 100755 --- a/build/extract_sources.py +++ b/build/extract_sources.py @@ -183,6 +183,12 @@ def parse_args() -> argparse.Namespace: parser.add_argument( "--target-platforms", help="--target-platforms to pass to buck cquery, if any." ) + parser.add_argument( + "--fake-host", help="Fake host to pass to buck cquery, if any." + ) + parser.add_argument( + "--fake-arch", help="Fake architecture to pass to buck cquery, if any." + ) return parser.parse_args() @@ -213,6 +219,13 @@ def main(): if args.target_platforms: buck_args = ["--target-platforms"] buck_args.append(args.target_platforms) + if args.fake_host: + buck_args.append("--fake-host") + buck_args.append(args.fake_host) + if args.fake_arch: + buck_args.append("--fake-arch") + buck_args.append(args.fake_arch) + print(f"buck_args: {buck_args}") for name, target in graph.by_name.items(): target_to_srcs[name] = sorted(target.get_sources(graph, runner, buck_args)) diff --git a/examples/models/llama2/CMakeLists.txt b/examples/models/llama2/CMakeLists.txt index 7a9b69d65b1..3b5c4228483 100644 --- a/examples/models/llama2/CMakeLists.txt +++ b/examples/models/llama2/CMakeLists.txt @@ -116,7 +116,7 @@ endif() target_link_options_shared_lib(quantized_ops_lib) list(APPEND link_libraries quantized_kernels quantized_ops_lib) -if(EXECUTORCH_BUILD_KERNELS_CUSTOM) +if(EXECUTORCH_BUILD_KERNELS_CUSTOM OR TARGET custom_ops) target_link_options_shared_lib(custom_ops) list(APPEND link_libraries custom_ops) endif() diff --git a/examples/qualcomm/executor_runner/qnn_executor_runner.cpp b/examples/qualcomm/executor_runner/qnn_executor_runner.cpp index 7235e36681e..e6233860fb0 100644 --- a/examples/qualcomm/executor_runner/qnn_executor_runner.cpp +++ b/examples/qualcomm/executor_runner/qnn_executor_runner.cpp @@ -32,6 +32,7 @@ #include #include #include +#include static uint8_t method_allocator_pool[4 * 1024U * 1024U]; // 4 MB diff --git a/extension/data_loader/file_data_loader.cpp b/extension/data_loader/file_data_loader.cpp index 1d097cfd989..29f2b0dbde5 100644 --- a/extension/data_loader/file_data_loader.cpp +++ b/extension/data_loader/file_data_loader.cpp @@ -14,10 +14,14 @@ #include #include +#ifdef _WIN32 +#include +#else #include #include #include #include +#endif #include #include @@ -69,9 +73,15 @@ FileDataLoader::~FileDataLoader() { // file_name_ can be nullptr if this instance was moved from, but freeing a // null pointer is safe. std::free(const_cast(file_name_)); +#ifdef _WIN32 + if (fd_ != INVALID_HANDLE_VALUE) { + CloseHandle(fd_); + } +#else // fd_ can be -1 if this instance was moved from, but closing a negative fd is // safe (though it will return an error). ::close(fd_); +#endif } Result FileDataLoader::from( @@ -83,6 +93,34 @@ Result FileDataLoader::from( "Alignment %zu is not a power of 2", alignment); +#ifdef _WIN32 + HANDLE fd = CreateFile( + file_name, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (fd == INVALID_HANDLE_VALUE) { + ET_LOG( + Error, "Failed to open %s: %lu", file_name, GetLastError()); + return Error::AccessFailed; + } + + LARGE_INTEGER file_size_li; + if (!GetFileSizeEx(fd, &file_size_li)) { + ET_LOG( + Error, + "Could not get length of %s: %lu", + file_name, + GetLastError()); + CloseHandle(fd); + return Error::AccessFailed; + } + size_t file_size = static_cast(file_size_li.QuadPart); + +#else // Use open() instead of fopen() to avoid the layer of buffering that // fopen() does. We will be reading large portions of the file in one shot, // so buffering does not help. @@ -107,12 +145,17 @@ Result FileDataLoader::from( return Error::AccessFailed; } size_t file_size = st.st_size; +#endif // Copy the filename so we can print better debug messages if reads fail. const char* file_name_copy = ::strdup(file_name); if (file_name_copy == nullptr) { ET_LOG(Error, "strdup(%s) failed", file_name); +#ifdef _WIN32 + CloseHandle(fd); +#else ::close(fd); +#endif return Error::MemoryAllocationFailed; } @@ -139,7 +182,7 @@ Result FileDataLoader::load( ET_UNUSED const DataLoader::SegmentInfo& segment_info) const { ET_CHECK_OR_RETURN_ERROR( // Probably had its value moved to another instance. - fd_ >= 0, + IS_VALID_FD(fd_), InvalidState, "Uninitialized"); ET_CHECK_OR_RETURN_ERROR( @@ -213,7 +256,7 @@ Result FileDataLoader::load( Result FileDataLoader::size() const { ET_CHECK_OR_RETURN_ERROR( // Probably had its value moved to another instance. - fd_ >= 0, + IS_VALID_FD(fd_), InvalidState, "Uninitialized"); return file_size_; @@ -226,7 +269,7 @@ ET_NODISCARD Error FileDataLoader::load_into( void* buffer) const { ET_CHECK_OR_RETURN_ERROR( // Probably had its value moved to another instance. - fd_ >= 0, + IS_VALID_FD(fd_), InvalidState, "Uninitialized"); ET_CHECK_OR_RETURN_ERROR( @@ -244,6 +287,53 @@ ET_NODISCARD Error FileDataLoader::load_into( size_t needed = size; uint8_t* buf = reinterpret_cast(buffer); +#ifdef _WIN32 + + while (needed > 0) { + const auto chunk_size = std::min( + needed, static_cast(std::numeric_limits::max())); + LARGE_INTEGER move; + move.QuadPart = static_cast(offset); + if (!SetFilePointerEx(fd_, move, nullptr, FILE_BEGIN)) { + ET_LOG( + Error, + "Reading from %s: failed to set file pointer: %lx", + file_name_, + GetLastError()); + return Error::AccessFailed; + } + DWORD nread = 0; + if (!ReadFile(fd_, buf, static_cast(chunk_size), &nread, nullptr)) { + DWORD error_code = GetLastError(); + if (error_code == ERROR_IO_PENDING) { + continue; + } + ET_LOG( + Error, + "Reading from %s: failed to read %zu bytes at offset %lu: %lx", + file_name_, + chunk_size, + offset, + error_code); + return Error::AccessFailed; + } + + if (nread == 0) { + ET_LOG( + Error, + "Reading from %s: EOF encountered unexpectedly at offset %zu", + file_name_, + offset); + return Error::AccessFailed; + } + + needed -= nread; + buf += nread; + offset += nread; + } + +#else + // Make a duplicate fd if pread() is not available and we have to seek(). // Cannot use the standard dup() or fcntl() calls because the returned // duplicate will share the underlying file record and affect the original fd @@ -288,6 +378,9 @@ ET_NODISCARD Error FileDataLoader::load_into( if (!ET_HAVE_PREAD) { ::close(dup_fd); } + +#endif + return Error::Ok; } diff --git a/extension/data_loader/file_data_loader.h b/extension/data_loader/file_data_loader.h index 7cf2a92c4ad..c18e055eca2 100644 --- a/extension/data_loader/file_data_loader.h +++ b/extension/data_loader/file_data_loader.h @@ -8,6 +8,17 @@ #pragma once +#ifdef _WIN32 +#include +#define FD_TYPE HANDLE +#define INVALID_FD INVALID_HANDLE_VALUE +#define IS_VALID_FD(fd) (fd != INVALID_HANDLE_VALUE) +#else +#define FD_TYPE int +#define INVALID_FD -1 +#define IS_VALID_FD(fd) (fd >= 0) +#endif + #include #include @@ -59,7 +70,7 @@ class FileDataLoader final : public executorch::runtime::DataLoader { const_cast(rhs.file_name_) = nullptr; const_cast(rhs.file_size_) = 0; const_cast(rhs.alignment_) = 0; - const_cast(rhs.fd_) = -1; + const_cast(rhs.fd_) = INVALID_FD; } ~FileDataLoader() override; @@ -80,7 +91,7 @@ class FileDataLoader final : public executorch::runtime::DataLoader { private: FileDataLoader( - int fd, + FD_TYPE fd, size_t file_size, size_t alignment, const char* file_name) @@ -97,7 +108,7 @@ class FileDataLoader final : public executorch::runtime::DataLoader { const char* const file_name_; // Owned by the instance. const size_t file_size_; const size_t alignment_; - const int fd_; // Owned by the instance. + const FD_TYPE fd_; // Owned by the instance. }; } // namespace extension diff --git a/extension/data_loader/mmap_data_loader.cpp b/extension/data_loader/mmap_data_loader.cpp index ebe74f95266..660f5aa20e0 100644 --- a/extension/data_loader/mmap_data_loader.cpp +++ b/extension/data_loader/mmap_data_loader.cpp @@ -12,11 +12,16 @@ #include #include +#ifdef _WIN32 +#include +#include +#else #include #include #include #include #include +#endif #include #include @@ -57,19 +62,43 @@ Range get_overlapping_pages(uintptr_t offset, size_t size, size_t page_size) { } // namespace +#ifdef _WIN32 +const char* get_last_error_message() { + DWORD errorMessageID = GetLastError(); + if(errorMessageID == 0) { + return ""; //No error message has been recorded + } + LPSTR messageBuffer = nullptr; + size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); + return messageBuffer; +} +#endif + MmapDataLoader::~MmapDataLoader() { // file_name_ can be nullptr if this instance was moved from, but freeing a // null pointer is safe. std::free(const_cast(file_name_)); +#ifdef _WIN32 + if (mapping_handle_ != nullptr) { + CloseHandle(mapping_handle_); + } +#else // fd_ can be -1 if this instance was moved from, but closing a negative fd is // safe (though it will return an error). ::close(fd_); +#endif } Result MmapDataLoader::from( const char* file_name, MmapDataLoader::MlockConfig mlock_config) { // Cache the page size. +#ifdef _WIN32 + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + size_t page_size = std::max(system_info.dwPageSize, system_info.dwAllocationGranularity); +#else long page_size = sysconf(_SC_PAGESIZE); if (page_size < 0) { ET_LOG(Error, "Could not get page size: %s (%d)", ::strerror(errno), errno); @@ -79,7 +108,55 @@ Result MmapDataLoader::from( ET_LOG(Error, "Page size 0x%ld is not a power of 2", page_size); return Error::InvalidState; } +#endif + +#ifdef _WIN32 + HANDLE file_handle = CreateFileA( + file_name, + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + if (file_handle == INVALID_HANDLE_VALUE) { + ET_LOG( + Error, + "Failed to open %s: %s", + file_name, + get_last_error_message()); + return Error::AccessFailed; + } + + LARGE_INTEGER file_size_li; + if (!GetFileSizeEx(file_handle, &file_size_li)) { + ET_LOG( + Error, + "Could not get length of %s: %s", + file_name, + get_last_error_message()); + CloseHandle(file_handle); + return Error::AccessFailed; + } + size_t file_size = static_cast(file_size_li.QuadPart); + HANDLE mapping_handle = CreateFileMappingA( + file_handle, + nullptr, + PAGE_READONLY, + 0, + 0, + nullptr); + if (mapping_handle == nullptr) { + ET_LOG( + Error, + "Could not create file mapping for %s: %s", + file_name, + get_last_error_message()); + CloseHandle(file_handle); + return Error::AccessFailed; + } +#else // Use open() instead of fopen() because mmap() needs a file descriptor. int fd = ::open(file_name, O_RDONLY); if (fd < 0) { @@ -106,17 +183,28 @@ Result MmapDataLoader::from( return Error::AccessFailed; } size_t file_size = st.st_size; +#endif // Copy the filename so we can print better debug messages if reads fail. const char* file_name_copy = ::strdup(file_name); if (file_name_copy == nullptr) { ET_LOG(Error, "strdup(%s) failed", file_name); +#ifdef _WIN32 + CloseHandle(mapping_handle); + CloseHandle(file_handle); +#else ::close(fd); +#endif return Error::MemoryAllocationFailed; } return MmapDataLoader( +#ifdef _WIN32 + file_handle, + mapping_handle, +#else fd, +#endif file_size, file_name_copy, static_cast(page_size), @@ -130,10 +218,19 @@ namespace { * `context` is actually the OS page size as a uintptr_t. */ void MunmapSegment(void* context, void* data, size_t size) { - const uintptr_t page_size = reinterpret_cast(context); + const size_t page_size = reinterpret_cast(context); - Range range = - get_overlapping_pages(reinterpret_cast(data), size, page_size); + Range range = get_overlapping_pages(reinterpret_cast(data), size, page_size); +#ifdef _WIN32 + if (!UnmapViewOfFile(reinterpret_cast(range.start))) { + ET_LOG( + Error, + "UnmapViewOfFile(0x%zx, %zu) failed: %s", + range.start, + range.size, + get_last_error_message()); + } +#else int ret = ::munmap(reinterpret_cast(range.start), range.size); if (ret < 0) { // Let the user know that something went wrong, but there's nothing we can @@ -146,6 +243,7 @@ void MunmapSegment(void* context, void* data, size_t size) { ::strerror(errno), errno); } +#endif } } // namespace @@ -155,7 +253,11 @@ Result MmapDataLoader::load( ET_UNUSED const DataLoader::SegmentInfo& segment_info) const { ET_CHECK_OR_RETURN_ERROR( // Probably had its value moved to another instance. +#ifdef _WIN32 + file_handle_ != INVALID_HANDLE_VALUE, +#else fd_ >= 0, +#endif InvalidState, "Uninitialized"); ET_CHECK_OR_RETURN_ERROR( @@ -168,7 +270,11 @@ Result MmapDataLoader::load( file_size_); ET_CHECK_OR_RETURN_ERROR( // Recommended by a lint warning. +#ifdef _WIN32 + offset <= std::numeric_limits::max(), +#else offset <= std::numeric_limits::max(), +#endif InvalidArgument, "Offset %zu too large for off_t", offset); @@ -185,6 +291,26 @@ Result MmapDataLoader::load( // Map the pages read-only. MAP_PRIVATE vs. MAP_SHARED doesn't matter since // the data is read-only, but use PRIVATE just to further avoid accidentally // modifying the file. +#ifdef _WIN32 + if (range.start + range.size > file_size_) { + range.size = file_size_ - range.start; + } + + void* pages = MapViewOfFile( + mapping_handle_, + FILE_MAP_READ | FILE_MAP_COPY, + static_cast(range.start >> 32), + static_cast(range.start & 0xFFFFFFFF), + range.size); + ET_CHECK_OR_RETURN_ERROR( + pages != nullptr, + AccessFailed, + "Failed to map %s: MapViewOfFile(..., size=%zd, ..., offset=0x%zx): %s", + file_name_, + range.size, + range.start, + get_last_error_message()); +#else void* pages = ::mmap( nullptr, range.size, @@ -200,9 +326,36 @@ Result MmapDataLoader::load( range.size, fd_, range.start); +#endif if (mlock_config_ == MlockConfig::UseMlock || mlock_config_ == MlockConfig::UseMlockIgnoreErrors) { +#ifdef _WIN32 + if (!VirtualLock(pages, size)) { + if (mlock_config_ == MlockConfig::UseMlockIgnoreErrors) { + ET_LOG( + Debug, + "Ignoring VirtualLock error for file %s (off=0x%zd): " + "VirtualLock(%p, %zu) failed: %s", + file_name_, + offset, + pages, + size, + get_last_error_message()); + } else { + ET_LOG( + Error, + "File %s (off=0x%zd): VirtualLock(%p, %zu) failed: %s", + file_name_, + offset, + pages, + size, + get_last_error_message()); + UnmapViewOfFile(pages); + return Error::NotSupported; + } + } +#else int err = ::mlock(pages, size); if (err < 0) { if (mlock_config_ == MlockConfig::UseMlockIgnoreErrors) { @@ -231,6 +384,7 @@ Result MmapDataLoader::load( } } // No need to keep track of this. munmap() will unlock as a side effect. +#endif } // The requested data is at an offset into the mapped pages. @@ -251,7 +405,11 @@ Result MmapDataLoader::load( Result MmapDataLoader::size() const { ET_CHECK_OR_RETURN_ERROR( // Probably had its value moved to another instance. +#ifdef _WIN32 + file_handle_ != INVALID_HANDLE_VALUE, +#else fd_ >= 0, +#endif InvalidState, "Uninitialized"); return file_size_; diff --git a/extension/data_loader/mmap_data_loader.h b/extension/data_loader/mmap_data_loader.h index c55f81a490b..6311afd6a70 100644 --- a/extension/data_loader/mmap_data_loader.h +++ b/extension/data_loader/mmap_data_loader.h @@ -12,6 +12,12 @@ #include #include +#ifdef _WIN32 +#include +#else +#include +#endif + namespace executorch { namespace extension { @@ -76,12 +82,22 @@ class MmapDataLoader final : public executorch::runtime::DataLoader { : file_name_(rhs.file_name_), file_size_(rhs.file_size_), page_size_(rhs.page_size_), +#ifdef _WIN32 + file_handle_(rhs.file_handle_), + mapping_handle_(rhs.mapping_handle_), +#else fd_(rhs.fd_), +#endif mlock_config_(rhs.mlock_config_) { +#ifdef _WIN32 + const_cast(rhs.file_handle_) = INVALID_HANDLE_VALUE; + const_cast(rhs.mapping_handle_) = nullptr; +#else + const_cast(rhs.fd_) = -1; +#endif const_cast(rhs.file_name_) = nullptr; const_cast(rhs.file_size_) = 0; const_cast(rhs.page_size_) = 0; - const_cast(rhs.fd_) = -1; const_cast(rhs.mlock_config_) = MlockConfig::NoMlock; } @@ -97,7 +113,12 @@ class MmapDataLoader final : public executorch::runtime::DataLoader { private: MmapDataLoader( +#ifdef _WIN32 + HANDLE file_handle, + HANDLE mapping_handle, +#else int fd, +#endif size_t file_size, const char* file_name, size_t page_size, @@ -105,7 +126,12 @@ class MmapDataLoader final : public executorch::runtime::DataLoader { : file_name_(file_name), file_size_(file_size), page_size_(page_size), +#ifdef _WIN32 + file_handle_(file_handle), + mapping_handle_(mapping_handle), +#else fd_(fd), +#endif mlock_config_(mlock_config) {} // Not safely copyable. @@ -113,10 +139,15 @@ class MmapDataLoader final : public executorch::runtime::DataLoader { MmapDataLoader& operator=(const MmapDataLoader&) = delete; MmapDataLoader& operator=(MmapDataLoader&&) = delete; - const char* const file_name_; // String data is owned by the instance. + const char* file_name_; // String data is owned by the instance. const size_t file_size_; const size_t page_size_; +#ifdef _WIN32 + const HANDLE file_handle_; + const HANDLE mapping_handle_; +#else const int fd_; // Owned by the instance. +#endif const MlockConfig mlock_config_; }; diff --git a/extension/llm/runner/util.h b/extension/llm/runner/util.h index 2f1d084811e..69be6ebe1b0 100644 --- a/extension/llm/runner/util.h +++ b/extension/llm/runner/util.h @@ -13,6 +13,9 @@ #if defined(__linux__) || defined(__ANDROID__) || defined(__unix__) #include #endif +#ifdef _WIN32 +#include +#endif namespace executorch { namespace extension { @@ -43,8 +46,12 @@ void inline safe_printf(const char* piece) { long inline time_in_ms() { // return time in milliseconds, for benchmarking the model speed struct timespec time; +#ifdef _WIN32 + return GetTickCount(); +#else clock_gettime(CLOCK_REALTIME, &time); return time.tv_sec * 1000 + time.tv_nsec / 1000000; +#endif } // ---------------------------------------------------------------------------- diff --git a/runtime/backend/interface.cpp b/runtime/backend/interface.cpp index 84c0bb82d43..dd54ea82693 100644 --- a/runtime/backend/interface.cpp +++ b/runtime/backend/interface.cpp @@ -19,16 +19,20 @@ namespace { // The max number of backends that can be registered globally. constexpr size_t kMaxRegisteredBackends = 16; +#pragma data_seg(".SS_DLLMAIN") + // TODO(T128866626): Remove global static variables. We want to be able to run // multiple Executor instances and having a global registration isn't a viable // solution in the long term. /// Global table of registered backends. -Backend registered_backends[kMaxRegisteredBackends]; +Backend registered_backends[kMaxRegisteredBackends] ET_SHARED; /// The number of backends registered in the table. size_t num_registered_backends = 0; +#pragma data_seg() + } // namespace BackendInterface* get_backend_class(const char* name) { diff --git a/runtime/kernel/operator_registry.cpp b/runtime/kernel/operator_registry.cpp index 78aa0a51732..9ad3f48be2d 100644 --- a/runtime/kernel/operator_registry.cpp +++ b/runtime/kernel/operator_registry.cpp @@ -29,6 +29,8 @@ constexpr uint32_t kMaxKernelsPerOp = 8; constexpr uint32_t kMaxRegisteredKernels = kMaxOperators * kMaxKernelsPerOp; #endif +#pragma data_seg(".SS_DLLMAIN") + // Data that backs the kernel table. Since Kernel has a custom default // constructor (implicitly, because it contains KernelKey, which has a custom // ctor), some toolchains don't like having a global array of them: it would @@ -37,13 +39,15 @@ constexpr uint32_t kMaxRegisteredKernels = kMaxOperators * kMaxKernelsPerOp; // and point the table at it. // @lint-ignore CLANGTIDY facebook-hte-CArray alignas(sizeof(Kernel)) uint8_t - registered_kernels_data[kMaxRegisteredKernels * sizeof(Kernel)]; + registered_kernels_data[kMaxRegisteredKernels * sizeof(Kernel)] ET_SHARED; /// Global table of registered kernels. -Kernel* registered_kernels = reinterpret_cast(registered_kernels_data); +Kernel* registered_kernels ET_SHARED = reinterpret_cast(registered_kernels_data); /// The number of kernels registered in the table. -size_t num_registered_kernels = 0; +size_t num_registered_kernels ET_SHARED = 0; + +#pragma data_seg() // Registers the kernels, but may return an error. Error register_kernels_internal(const Span kernels) { diff --git a/runtime/platform/compiler.h b/runtime/platform/compiler.h index b6f7fc8642f..2120e585b69 100644 --- a/runtime/platform/compiler.h +++ b/runtime/platform/compiler.h @@ -162,6 +162,18 @@ using ssize_t = ptrdiff_t; #endif +// Shared variable section +#ifdef _WIN32 +#ifdef __MINGW32__ +#define ET_SHARED __attribute__((section(".shr"), shared)) +#else +#define ET_SHARED +#endif +#else +#define ET_SHARED +#endif + + // DEPRECATED: Use the non-underscore-prefixed versions instead. // TODO(T199005537): Remove these once all users have stopped using them. #define __ET_DEPRECATED ET_DEPRECATED diff --git a/runtime/platform/default/posix.cpp b/runtime/platform/default/posix.cpp index aba504f53e0..f808b717ab7 100644 --- a/runtime/platform/default/posix.cpp +++ b/runtime/platform/default/posix.cpp @@ -63,11 +63,15 @@ #endif // NDEBUG +#pragma data_seg(".SS_DLLMAIN") // Shared data segment for DLL main with MSVC + /// Start time of the system (used to zero the system timestamp). -static std::chrono::time_point systemStartTime; +static std::chrono::time_point systemStartTime ET_SHARED; /// Flag set to true if the PAL has been successfully initialized. -static bool initialized = false; +static bool initialized ET_SHARED = false; + +#pragma data_seg() /** * Initialize the platform abstraction layer. diff --git a/runtime/platform/system.h b/runtime/platform/system.h index c836e5ff222..56bd8432950 100644 --- a/runtime/platform/system.h +++ b/runtime/platform/system.h @@ -21,6 +21,9 @@ #if defined(ET_USE_LIBDL) #include #endif +#if defined(ET_USE_WINAPI) +#include +#endif static constexpr const char* DYNAMIC_LIBRARY_NOT_SUPPORTED = "NOT_SUPPORTED"; static constexpr const char* DYNAMIC_LIBRARY_NOT_FOUND = "NOT_FOUND"; @@ -41,6 +44,17 @@ inline const char* et_pal_get_shared_library_name(const void* addr) { } else { return DYNAMIC_LIBRARY_NOT_FOUND; } +#endif +#if defined(ET_USE_WINAPI) + HMODULE hModule = NULL; + if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(addr), &hModule)) { + char path[MAX_PATH]; + if (GetModuleFileNameA(hModule, path, sizeof(path))) { + return path; + } + } + return DYNAMIC_LIBRARY_NOT_FOUND; #endif return DYNAMIC_LIBRARY_NOT_SUPPORTED; } diff --git a/runtime/platform/targets.bzl b/runtime/platform/targets.bzl index 42bb851e2cf..6d3141a4219 100644 --- a/runtime/platform/targets.bzl +++ b/runtime/platform/targets.bzl @@ -108,6 +108,7 @@ def define_common_targets(): "DEFAULT": [], "ovr_config//os:linux": ["-DET_USE_LIBDL"], "ovr_config//os:macos": ["-DET_USE_LIBDL"], + "ovr_config//os:windows": ["-DET_USE_WINAPI"], }, ), visibility = [