Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion ggml/src/ggml-hrx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ if(HRX_SOURCE_DIR)
set(GGML_HRX_BUILD_DIR "${GGML_HRX_PREFIX}/src/ggml-hrx-deps-build")
set(GGML_HRX_LIB "${GGML_HRX_BUILD_DIR}/libhrx/src/libhrx/${CMAKE_SHARED_LIBRARY_PREFIX}hrx${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(GGML_LOOMC_LIB "${GGML_HRX_BUILD_DIR}/loom/binding/c/${CMAKE_SHARED_LIBRARY_PREFIX}loomc${CMAKE_SHARED_LIBRARY_SUFFIX}")
if(WIN32)
# On Windows, linking against a DLL requires its companion import
# library (.lib), which CMake exposes as IMPORTED_IMPLIB. Both files
# are produced side-by-side with the DLL by the nested build.
set(GGML_HRX_LIB_IMPLIB "${GGML_HRX_BUILD_DIR}/libhrx/src/libhrx/${CMAKE_IMPORT_LIBRARY_PREFIX}hrx${CMAKE_IMPORT_LIBRARY_SUFFIX}")
set(GGML_LOOMC_LIB_IMPLIB "${GGML_HRX_BUILD_DIR}/loom/binding/c/${CMAKE_IMPORT_LIBRARY_PREFIX}loomc${CMAKE_IMPORT_LIBRARY_SUFFIX}")
endif()
set(GGML_HRX_LOOM_LINK "${GGML_HRX_BUILD_DIR}/loom/src/loom/tools/loom-link/loom-link${CMAKE_EXECUTABLE_SUFFIX}")
set(GGML_HRX_LOOM_FORMAT "${GGML_HRX_BUILD_DIR}/loom/src/loom/tools/loom-format/loom-format${CMAKE_EXECUTABLE_SUFFIX}")
set(GGML_HRX_IREE_BENCHMARK_LOOM "${GGML_HRX_BUILD_DIR}/loom/src/loom/tools/iree-benchmark-loom/iree-benchmark-loom${CMAKE_EXECUTABLE_SUFFIX}")
Expand All @@ -34,33 +41,55 @@ if(HRX_SOURCE_DIR)
if(IREE_ROCM_PATH)
list(APPEND GGML_HRX_CMAKE_ARGS -DIREE_ROCM_PATH=${IREE_ROCM_PATH})
endif()
if(IREE_ROCM_DEPENDENCY_MODE)
list(APPEND GGML_HRX_CMAKE_ARGS -DIREE_ROCM_DEPENDENCY_MODE=${IREE_ROCM_DEPENDENCY_MODE})
endif()
if(FETCHCONTENT_BASE_DIR)
list(APPEND GGML_HRX_CMAKE_ARGS -DFETCHCONTENT_BASE_DIR=${FETCHCONTENT_BASE_DIR})
endif()

ExternalProject_Add(ggml-hrx-deps
SOURCE_DIR "${HRX_SOURCE_DIR}"
PREFIX "${GGML_HRX_PREFIX}"
BINARY_DIR "${GGML_HRX_BUILD_DIR}"
CMAKE_ARGS ${GGML_HRX_CMAKE_ARGS}
BUILD_ALWAYS TRUE
BUILD_COMMAND ${CMAKE_COMMAND} --build . --target hrx loomc_shared loom_tools_loom-link_loom-link loom_tools_loom-format_loom-format loom_tools_iree-benchmark-loom_iree-benchmark-loom --config ${CMAKE_BUILD_TYPE}
INSTALL_COMMAND ""
BUILD_BYPRODUCTS "${GGML_HRX_LIB}" "${GGML_LOOMC_LIB}" "${GGML_HRX_LOOM_LINK}" "${GGML_HRX_LOOM_FORMAT}" "${GGML_HRX_IREE_BENCHMARK_LOOM}"
BUILD_BYPRODUCTS "${GGML_HRX_LIB}" "${GGML_LOOMC_LIB}" "${GGML_HRX_LIB_IMPLIB}" "${GGML_LOOMC_LIB_IMPLIB}" "${GGML_HRX_LOOM_LINK}" "${GGML_HRX_LOOM_FORMAT}" "${GGML_HRX_IREE_BENCHMARK_LOOM}"
UPDATE_COMMAND ""
)

add_library(hrx::hrx SHARED IMPORTED GLOBAL)
set_target_properties(hrx::hrx PROPERTIES
IMPORTED_LOCATION "${GGML_HRX_LIB}"
INTERFACE_INCLUDE_DIRECTORIES "${HRX_SOURCE_DIR}/libhrx/include")
if(WIN32)
set_target_properties(hrx::hrx PROPERTIES IMPORTED_IMPLIB "${GGML_HRX_LIB_IMPLIB}")
endif()
add_dependencies(hrx::hrx ggml-hrx-deps)

add_library(loomc::loomc SHARED IMPORTED GLOBAL)
set_target_properties(loomc::loomc PROPERTIES
IMPORTED_LOCATION "${GGML_LOOMC_LIB}"
INTERFACE_INCLUDE_DIRECTORIES "${HRX_SOURCE_DIR}/loom/binding/c/include"
INTERFACE_COMPILE_DEFINITIONS LOOMC_USING_SHARED_LIBRARY)
if(WIN32)
set_target_properties(loomc::loomc PROPERTIES IMPORTED_IMPLIB "${GGML_LOOMC_LIB_IMPLIB}")
endif()
add_dependencies(loomc::loomc ggml-hrx-deps)

# hrx::hrx/loomc::loomc are IMPORTED targets pointing at the nested
# ExternalProject build, so `install(TARGETS ...)` can't be used for them
# (CMake doesn't support installing IMPORTED targets). Install the runtime
# libraries directly so `cmake --install` produces a working tree; on
# Linux/macOS this mirrors GGML_HRX_BUNDLE_RUNTIME_LIBS's minimum
# requirement but doesn't depend on that Linux-only opt-in feature.
if(WIN32)
install(FILES "${GGML_HRX_LIB}" "${GGML_LOOMC_LIB}" DESTINATION "${CMAKE_INSTALL_BINDIR}")
else()
install(FILES "${GGML_HRX_LIB}" "${GGML_LOOMC_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}")
endif()
else()
find_package(hrx CONFIG REQUIRED)
find_package(loomc CONFIG REQUIRED)
Expand Down Expand Up @@ -267,6 +296,29 @@ ggml_add_backend_library(ggml-hrx
target_link_libraries(ggml-hrx PRIVATE ggml-hrx-kernel-corpus hrx::hrx loomc::loomc)
target_include_directories(ggml-hrx PRIVATE . "${CMAKE_CURRENT_BINARY_DIR}" ../../../vendor)
target_compile_definitions(ggml-hrx PRIVATE GGML_USE_HRX)
if(WIN32)
# Unlike ELF/Mach-O shared libraries, Windows DLLs export no symbols by
# default. ggml-hrx's internal C++ API (Graph, DispatchScheduler,
# build_command_program(), etc.) is consumed directly -- not just through
# the standard GGML_BACKEND_API registration entrypoint -- by the
# ggml-hrx-compile-kernel/ggml-hrx-analyze-graph tools and the HRX test
# executables. Auto-export every symbol so linking against ggml-hrx.dll
# on Windows behaves like linking against libggml-hrx.so elsewhere.
set_target_properties(ggml-hrx PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)

# Windows has no RPATH equivalent: hrx.dll/loomc.dll must sit next to
# whichever .exe/.dll loads them. Every ggml-hrx binary (the backend
# itself, the CLI tools, and the tests) shares one output directory, so a
# single copy alongside ggml-hrx's own output makes them all runnable
# without requiring `cmake --install` first.
add_custom_command(TARGET ggml-hrx POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"$<TARGET_FILE:hrx::hrx>"
"$<TARGET_FILE:loomc::loomc>"
"$<TARGET_FILE_DIR:ggml-hrx>"
COMMENT "Copying hrx/loomc runtime DLLs next to ggml-hrx"
VERBATIM)
endif()

if (GGML_HRX_BUNDLE_RUNTIME_LIBS)
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/BundleRuntime.cmake")
Expand Down
6 changes: 4 additions & 2 deletions ggml/src/ggml-hrx/backend-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,12 @@ struct ggml_backend_hrx_context {
std::string name;
};

// Process-lifetime registry. Device and stream handles are not released here:
// this object is destroyed from a DLL static destructor, which on process exit
// runs after driver threads are already gone. Call hrx_gpu_shutdown() from
// live application code if the process will continue without HRX.
struct ggml_backend_hrx_reg_context {
bool initialized = false;
std::vector<std::unique_ptr<ggml_backend_hrx_device_context>> device_contexts;
std::vector<ggml_backend_device> devices;

~ggml_backend_hrx_reg_context();
};
21 changes: 0 additions & 21 deletions ggml/src/ggml-hrx/ggml-hrx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,27 +57,6 @@ static bool hrx_check(hrx_status_t status, const char * expression, const char *

#define HRX_CHECK(expression) hrx_check((expression), #expression, __FILE__, __LINE__)

} // namespace

ggml_backend_hrx_reg_context::~ggml_backend_hrx_reg_context() {
for (auto & context : device_contexts) {
if (context->buffer_stream != nullptr) {
hrx_stream_release(context->buffer_stream);
}
if (context->device != nullptr) {
hrx_device_release(context->device);
}
}
if (initialized) {
hrx_status_t status = hrx_gpu_shutdown();
if (!hrx_status_is_ok(status)) {
hrx_status_ignore(status);
}
}
}

namespace {

static std::optional<std::string> device_string_property(hrx_device_t device,
hrx_device_property_t property,
const char * property_name) {
Expand Down
8 changes: 6 additions & 2 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -341,20 +341,24 @@ if (TARGET ggml-hrx)
add_executable(test-hrx-buffer test-hrx-buffer.cpp)
target_link_libraries(test-hrx-buffer PRIVATE ggml-hrx ggml hrx::hrx)
target_include_directories(test-hrx-buffer PRIVATE ../ggml/include ../ggml/src ../ggml/src/ggml-hrx)
target_compile_features(test-hrx-buffer PRIVATE cxx_std_17)
add_test(NAME test-hrx-buffer COMMAND test-hrx-buffer)

add_executable(hrx-backend-test hrx-backend-test.cpp)
target_link_libraries(hrx-backend-test PRIVATE ggml-hrx ggml hrx::hrx loomc::loomc)
target_link_libraries(hrx-backend-test PRIVATE ggml-hrx ggml-hrx-kernel-corpus ggml hrx::hrx loomc::loomc)
target_include_directories(hrx-backend-test PRIVATE ../ggml/include ../ggml/src ../ggml/src/ggml-hrx)
target_compile_features(hrx-backend-test PRIVATE cxx_std_17)
add_test(NAME hrx-backend-test COMMAND hrx-backend-test)

add_executable(test-hrx-loom-jit test-hrx-loom-jit.cpp)
target_link_libraries(test-hrx-loom-jit PRIVATE ggml-hrx ggml-hrx-kernel-corpus hrx::hrx loomc::loomc)
target_include_directories(test-hrx-loom-jit PRIVATE ../ggml/include ../ggml/src ../ggml/src/ggml-hrx)
target_compile_features(test-hrx-loom-jit PRIVATE cxx_std_17)
add_test(NAME test-hrx-loom-jit COMMAND test-hrx-loom-jit)

add_executable(test-hrx-ops test-hrx-ops.cpp)
target_link_libraries(test-hrx-ops PRIVATE ggml-hrx ggml hrx::hrx loomc::loomc)
target_link_libraries(test-hrx-ops PRIVATE ggml-hrx ggml-hrx-kernel-corpus ggml hrx::hrx loomc::loomc)
target_include_directories(test-hrx-ops PRIVATE ../ggml/include ../ggml/src ../ggml/src/ggml-hrx)
target_compile_features(test-hrx-ops PRIVATE cxx_std_17)
add_test(NAME test-hrx-ops COMMAND test-hrx-ops)
endif()
17 changes: 17 additions & 0 deletions tests/hrx-backend-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@
#include <utility>
#include <vector>

#ifdef _WIN32
// MSVC's CRT doesn't provide the POSIX setenv()/unsetenv() functions used
// throughout this file. Shim them on top of _putenv_s() with matching
// semantics (setenv() with overwrite=0 only sets when unset; both return 0
// on success).
static int setenv(const char * name, const char * value, int overwrite) {
if (!overwrite && std::getenv(name) != nullptr) {
return 0;
}
return _putenv_s(name, value) == 0 ? 0 : -1;
}

static int unsetenv(const char * name) {
return _putenv_s(name, "") == 0 ? 0 : -1;
}
#endif // _WIN32

#define REQUIRE(condition) \
do { \
if (!(condition)) { \
Expand Down
17 changes: 17 additions & 0 deletions tests/test-hrx-ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@
#include <utility>
#include <vector>

#ifdef _WIN32
// MSVC's CRT doesn't provide the POSIX setenv()/unsetenv() functions used
// throughout this file. Shim them on top of _putenv_s() with matching
// semantics (setenv() with overwrite=0 only sets when unset; both return 0
// on success).
static int setenv(const char * name, const char * value, int overwrite) {
if (!overwrite && std::getenv(name) != nullptr) {
return 0;
}
return _putenv_s(name, value) == 0 ? 0 : -1;
}

static int unsetenv(const char * name) {
return _putenv_s(name, "") == 0 ? 0 : -1;
}
#endif // _WIN32

#define REQUIRE(condition) \
do { \
if (!(condition)) { \
Expand Down