blob: 861cec8262b54eca4a106778e25f1bcde56b2ef2 [file]
// Copyright 2026 The IREE Authors
//
// Licensed 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 <cstring>
#include "iree/async/notification.h"
#include "iree/async/proactor.h"
#include "iree/async/proactor_platform.h"
#include "iree/hal/api.h"
#include "iree/testing/gtest.h"
#include "iree/testing/status_matchers.h"
namespace {
static iree_async_proactor_t* test_proactor() {
static iree_async_proactor_t* proactor = nullptr;
if (!proactor) {
IREE_CHECK_OK(iree_async_proactor_create_platform(
iree_async_proactor_options_default(), iree_allocator_system(),
&proactor));
atexit([] {
iree_async_proactor_release(proactor);
proactor = nullptr;
});
}
return proactor;
}
typedef struct iree_hal_needs_wait_test_pool_t {
iree_hal_resource_t resource;
// Notification used by the generic synchronous allocation helper.
iree_async_notification_t* notification = nullptr;
iree_async_single_frontier_t wait_frontier;
const iree_async_frontier_t* released_frontier = nullptr;
iree_hal_pool_reservation_t released_reservation = {0};
bool wrap_called = false;
} iree_hal_needs_wait_test_pool_t;
static void iree_hal_needs_wait_test_pool_destroy(iree_hal_pool_t* base_pool) {
auto* pool = (iree_hal_needs_wait_test_pool_t*)base_pool;
iree_async_notification_release(pool->notification);
delete pool;
}
static iree_status_t iree_hal_needs_wait_test_pool_acquire_reservation(
iree_hal_pool_t* base_pool, iree_device_size_t size,
iree_device_size_t alignment,
const iree_async_frontier_t* requester_frontier,
iree_hal_pool_reserve_flags_t flags,
iree_hal_pool_reservation_t* out_reservation,
iree_hal_pool_acquire_info_t* out_info,
iree_hal_pool_acquire_result_t* out_result) {
auto* pool = (iree_hal_needs_wait_test_pool_t*)base_pool;
(void)alignment;
(void)requester_frontier;
(void)flags;
iree_async_single_frontier_initialize(
&pool->wait_frontier, iree_async_axis_make_queue(1, 2, 3, 4), 42);
memset(out_reservation, 0, sizeof(*out_reservation));
out_reservation->offset = 128;
out_reservation->length = size;
out_reservation->block_handle = 0xB10Cu;
out_reservation->slab_index = 7;
memset(out_info, 0, sizeof(*out_info));
out_info->wait_frontier =
iree_async_single_frontier_as_const_frontier(&pool->wait_frontier);
*out_result = IREE_HAL_POOL_ACQUIRE_OK_NEEDS_WAIT;
return iree_ok_status();
}
static void iree_hal_needs_wait_test_pool_release_reservation(
iree_hal_pool_t* base_pool, const iree_hal_pool_reservation_t* reservation,
const iree_async_frontier_t* death_frontier) {
auto* pool = (iree_hal_needs_wait_test_pool_t*)base_pool;
pool->released_reservation = *reservation;
pool->released_frontier = death_frontier;
}
static iree_status_t iree_hal_needs_wait_test_pool_materialize_reservation(
iree_hal_pool_t* base_pool, iree_hal_buffer_params_t params,
const iree_hal_pool_reservation_t* reservation,
iree_hal_pool_materialize_flags_t flags, iree_hal_buffer_t** out_buffer) {
auto* pool = (iree_hal_needs_wait_test_pool_t*)base_pool;
(void)params;
(void)reservation;
(void)flags;
(void)out_buffer;
pool->wrap_called = true;
return iree_make_status(IREE_STATUS_INTERNAL,
"materialize_reservation must not be called");
}
static void iree_hal_needs_wait_test_pool_query_capabilities(
const iree_hal_pool_t* base_pool,
iree_hal_pool_capabilities_t* out_capabilities) {
(void)base_pool;
(void)out_capabilities;
}
static void iree_hal_needs_wait_test_pool_query_stats(
const iree_hal_pool_t* base_pool, iree_hal_pool_stats_t* out_stats) {
(void)base_pool;
(void)out_stats;
}
static iree_status_t iree_hal_needs_wait_test_pool_trim(
iree_hal_pool_t* base_pool) {
(void)base_pool;
return iree_ok_status();
}
static iree_async_notification_t* iree_hal_needs_wait_test_pool_notification(
iree_hal_pool_t* base_pool) {
auto* pool = (iree_hal_needs_wait_test_pool_t*)base_pool;
return pool->notification;
}
static const iree_hal_pool_vtable_t iree_hal_needs_wait_test_pool_vtable = {
iree_hal_needs_wait_test_pool_destroy,
iree_hal_needs_wait_test_pool_acquire_reservation,
iree_hal_needs_wait_test_pool_release_reservation,
iree_hal_needs_wait_test_pool_materialize_reservation,
iree_hal_needs_wait_test_pool_query_capabilities,
iree_hal_needs_wait_test_pool_query_stats,
iree_hal_needs_wait_test_pool_trim,
iree_hal_needs_wait_test_pool_notification,
};
static iree_hal_needs_wait_test_pool_t* CreateNeedsWaitTestPool() {
auto* pool = new iree_hal_needs_wait_test_pool_t;
iree_hal_resource_initialize(&iree_hal_needs_wait_test_pool_vtable,
&pool->resource);
IREE_CHECK_OK(iree_async_notification_create(
test_proactor(), IREE_ASYNC_NOTIFICATION_FLAG_NONE, &pool->notification));
iree_async_single_frontier_initialize(
&pool->wait_frontier, iree_async_axis_make_queue(1, 2, 3, 4), 42);
pool->released_frontier = nullptr;
memset(&pool->released_reservation, 0, sizeof(pool->released_reservation));
pool->wrap_called = false;
return pool;
}
typedef struct iree_hal_routing_test_pool_t {
// Base resource header for vtable dispatch and ref counting.
iree_hal_resource_t resource;
// Capabilities returned when registering this pool in a pool set.
iree_hal_pool_capabilities_t capabilities = {0};
} iree_hal_routing_test_pool_t;
static void iree_hal_routing_test_pool_destroy(iree_hal_pool_t* base_pool) {
delete (iree_hal_routing_test_pool_t*)base_pool;
}
static iree_status_t iree_hal_routing_test_pool_acquire_reservation(
iree_hal_pool_t* base_pool, iree_device_size_t size,
iree_device_size_t alignment,
const iree_async_frontier_t* requester_frontier,
iree_hal_pool_reserve_flags_t flags,
iree_hal_pool_reservation_t* out_reservation,
iree_hal_pool_acquire_info_t* out_info,
iree_hal_pool_acquire_result_t* out_result) {
(void)base_pool;
(void)size;
(void)alignment;
(void)requester_frontier;
(void)flags;
(void)out_reservation;
(void)out_info;
(void)out_result;
return iree_make_status(IREE_STATUS_INTERNAL,
"acquire_reservation must not be called");
}
static void iree_hal_routing_test_pool_release_reservation(
iree_hal_pool_t* base_pool, const iree_hal_pool_reservation_t* reservation,
const iree_async_frontier_t* death_frontier) {
(void)base_pool;
(void)reservation;
(void)death_frontier;
}
static iree_status_t iree_hal_routing_test_pool_materialize_reservation(
iree_hal_pool_t* base_pool, iree_hal_buffer_params_t params,
const iree_hal_pool_reservation_t* reservation,
iree_hal_pool_materialize_flags_t flags, iree_hal_buffer_t** out_buffer) {
(void)base_pool;
(void)params;
(void)reservation;
(void)flags;
(void)out_buffer;
return iree_make_status(IREE_STATUS_INTERNAL,
"materialize_reservation must not be called");
}
static void iree_hal_routing_test_pool_query_capabilities(
const iree_hal_pool_t* base_pool,
iree_hal_pool_capabilities_t* out_capabilities) {
auto* pool = (iree_hal_routing_test_pool_t*)base_pool;
*out_capabilities = pool->capabilities;
}
static void iree_hal_routing_test_pool_query_stats(
const iree_hal_pool_t* base_pool, iree_hal_pool_stats_t* out_stats) {
(void)base_pool;
memset(out_stats, 0, sizeof(*out_stats));
}
static iree_status_t iree_hal_routing_test_pool_trim(
iree_hal_pool_t* base_pool) {
(void)base_pool;
return iree_ok_status();
}
static iree_async_notification_t* iree_hal_routing_test_pool_notification(
iree_hal_pool_t* base_pool) {
(void)base_pool;
return NULL;
}
static const iree_hal_pool_vtable_t iree_hal_routing_test_pool_vtable = {
iree_hal_routing_test_pool_destroy,
iree_hal_routing_test_pool_acquire_reservation,
iree_hal_routing_test_pool_release_reservation,
iree_hal_routing_test_pool_materialize_reservation,
iree_hal_routing_test_pool_query_capabilities,
iree_hal_routing_test_pool_query_stats,
iree_hal_routing_test_pool_trim,
iree_hal_routing_test_pool_notification,
};
static iree_hal_routing_test_pool_t* CreateRoutingTestPool(
iree_device_size_t max_allocation_size) {
auto* pool = new iree_hal_routing_test_pool_t;
iree_hal_resource_initialize(&iree_hal_routing_test_pool_vtable,
&pool->resource);
pool->capabilities.memory_type = IREE_HAL_MEMORY_TYPE_HOST_LOCAL;
pool->capabilities.supported_usage = IREE_HAL_BUFFER_USAGE_TRANSFER;
pool->capabilities.min_allocation_size = 0;
pool->capabilities.max_allocation_size = max_allocation_size;
return pool;
}
TEST(PoolAllocateBufferTest,
DetectsUnexpectedNeedsWaitReservationAndPreservesFrontier) {
iree_hal_needs_wait_test_pool_t* concrete_pool = CreateNeedsWaitTestPool();
iree_hal_pool_t* pool = (iree_hal_pool_t*)concrete_pool;
iree_hal_buffer_params_t params = {0};
params.usage = IREE_HAL_BUFFER_USAGE_TRANSFER;
params.type = IREE_HAL_MEMORY_TYPE_HOST_LOCAL;
params.access = IREE_HAL_MEMORY_ACCESS_ALL;
iree_hal_buffer_t* buffer = NULL;
IREE_EXPECT_STATUS_IS(IREE_STATUS_INTERNAL,
iree_hal_pool_allocate_buffer(
pool, params, 4096,
iree_async_single_frontier_as_const_frontier(
&concrete_pool->wait_frontier),
iree_make_timeout_ms(0), &buffer));
EXPECT_EQ(buffer, nullptr);
EXPECT_FALSE(concrete_pool->wrap_called);
EXPECT_EQ(concrete_pool->released_reservation.offset, 128u);
EXPECT_EQ(concrete_pool->released_reservation.length, 4096u);
EXPECT_EQ(concrete_pool->released_reservation.block_handle, 0xB10Cu);
EXPECT_EQ(concrete_pool->released_reservation.slab_index, 7u);
EXPECT_EQ(concrete_pool->released_frontier,
iree_async_single_frontier_as_const_frontier(
&concrete_pool->wait_frontier));
iree_hal_pool_release(pool);
}
TEST(PoolSetTest, SelectsHighestPriorityCompatiblePoolBySize) {
iree_allocator_t host_allocator = iree_allocator_system();
iree_hal_pool_set_t pool_set;
IREE_ASSERT_OK(iree_hal_pool_set_initialize(/*initial_capacity=*/2,
host_allocator, &pool_set));
iree_hal_routing_test_pool_t* direct_pool = CreateRoutingTestPool(0);
iree_hal_routing_test_pool_t* tlsf_pool = CreateRoutingTestPool(1024);
IREE_ASSERT_OK(
iree_hal_pool_set_register(&pool_set, 0, (iree_hal_pool_t*)direct_pool));
IREE_ASSERT_OK(
iree_hal_pool_set_register(&pool_set, 10, (iree_hal_pool_t*)tlsf_pool));
iree_hal_buffer_params_t params = {0};
params.usage = IREE_HAL_BUFFER_USAGE_TRANSFER;
params.type = IREE_HAL_MEMORY_TYPE_HOST_LOCAL;
params.access = IREE_HAL_MEMORY_ACCESS_ALL;
EXPECT_EQ((iree_hal_pool_t*)tlsf_pool,
iree_hal_pool_set_select(&pool_set, params, 512));
EXPECT_EQ((iree_hal_pool_t*)direct_pool,
iree_hal_pool_set_select(&pool_set, params, 2048));
params.type = IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_OPTIMAL;
EXPECT_EQ((iree_hal_pool_t*)tlsf_pool,
iree_hal_pool_set_select(&pool_set, params, 512));
params.usage = IREE_HAL_BUFFER_USAGE_DISPATCH_STORAGE;
EXPECT_EQ(nullptr, iree_hal_pool_set_select(&pool_set, params, 512));
iree_hal_pool_set_deinitialize(&pool_set);
iree_hal_pool_release((iree_hal_pool_t*)tlsf_pool);
iree_hal_pool_release((iree_hal_pool_t*)direct_pool);
}
} // namespace