Files
TheRock/rocm-systems/emulation/rocjitsu/tests/dbi/hsa_dispatch_util.h
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

252 lines
9.1 KiB
C++

// Copyright (c) 2025-2026 Advanced Micro Devices, Inc.
// SPDX-License-Identifier: MIT
/// @file hsa_dispatch_util.h
/// @brief Shared HSA agent/pool discovery + vector_add dispatch scaffold for
/// the DBI smoke tests. Both hsa_dbi_nop_asm_test.cpp (inline-nop path)
/// and hsa_dbi_nop_probe_test.cpp (probe-call path) load a patched ELF
/// and dispatch it the same way; this header is the single copy of that
/// boilerplate.
///
/// Functions are `inline` so the header can be included in multiple TUs without
/// ODR violations. They require a live HSA runtime; callers gate on a real
/// gfx90a agent before invoking dispatch_vector_add.
#pragma once
#include "rocjitsu/base/rj_compiler.h"
RJ_DIAGNOSTIC_PUSH
RJ_DIAGNOSTIC_IGNORE_PEDANTIC
#include <hsa/hsa.h>
#include <hsa/hsa_ext_amd.h>
RJ_DIAGNOSTIC_POP
#include <cstdint>
#include <cstring>
#include <span>
#include <vector>
namespace rocjitsu::dbi_test {
// Find a GPU agent whose ISA name contains "gfx90a". Returns {handle=0} if
// no such agent is present.
inline hsa_agent_t find_gfx90a_agent() {
hsa_agent_t result{};
hsa_iterate_agents(
[](hsa_agent_t agent, void *data) -> hsa_status_t {
hsa_device_type_t type;
hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &type);
if (type != HSA_DEVICE_TYPE_GPU)
return HSA_STATUS_SUCCESS;
hsa_isa_t isa{};
hsa_agent_get_info(agent, HSA_AGENT_INFO_ISA, &isa);
char isa_name[128]{};
hsa_isa_get_info_alt(isa, HSA_ISA_INFO_NAME, isa_name);
if (std::strstr(isa_name, "gfx90a")) {
*static_cast<hsa_agent_t *>(data) = agent;
return HSA_STATUS_INFO_BREAK;
}
return HSA_STATUS_SUCCESS;
},
&result);
return result;
}
inline hsa_agent_t find_cpu_agent() {
hsa_agent_t result{};
hsa_iterate_agents(
[](hsa_agent_t agent, void *data) -> hsa_status_t {
hsa_device_type_t type;
hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &type);
if (type == HSA_DEVICE_TYPE_CPU) {
*static_cast<hsa_agent_t *>(data) = agent;
return HSA_STATUS_INFO_BREAK;
}
return HSA_STATUS_SUCCESS;
},
&result);
return result;
}
inline hsa_amd_memory_pool_t find_pool(hsa_agent_t agent, hsa_amd_segment_t segment,
bool host_accessible = false) {
struct Ctx {
hsa_amd_segment_t seg;
bool host_acc;
hsa_amd_memory_pool_t pool;
} ctx{segment, host_accessible, {}};
hsa_amd_agent_iterate_memory_pools(
agent,
[](hsa_amd_memory_pool_t pool, void *data) -> hsa_status_t {
auto *c = static_cast<Ctx *>(data);
hsa_amd_segment_t seg;
hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_SEGMENT, &seg);
if (seg != c->seg)
return HSA_STATUS_SUCCESS;
if (c->host_acc) {
bool acc = false;
hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_ACCESSIBLE_BY_ALL, &acc);
if (!acc)
return HSA_STATUS_SUCCESS;
}
c->pool = pool;
return HSA_STATUS_INFO_BREAK;
},
&ctx);
return ctx.pool;
}
// Dispatch the vector_add kernel from @p elf_bytes and return the output
// buffer. Returns an empty vector on any HSA failure (the caller asserts on
// size). Mirrors the dispatch scaffold in tests/dbt/hsa_translate_test.cpp.
inline std::vector<float> dispatch_vector_add(std::span<const uint8_t> elf_bytes, hsa_agent_t gpu,
hsa_agent_t cpu, const std::vector<float> &a_in,
const std::vector<float> &b_in, uint32_t n) {
constexpr size_t kKernArgsBytes = 256;
const size_t buf_size = n * sizeof(float);
hsa_code_object_reader_t reader{};
if (hsa_code_object_reader_create_from_memory(elf_bytes.data(), elf_bytes.size(), &reader) !=
HSA_STATUS_SUCCESS)
return {};
hsa_executable_t executable{};
if (hsa_executable_create_alt(HSA_PROFILE_FULL, HSA_DEFAULT_FLOAT_ROUNDING_MODE_DEFAULT, nullptr,
&executable) != HSA_STATUS_SUCCESS) {
hsa_code_object_reader_destroy(reader);
return {};
}
if (hsa_executable_load_agent_code_object(executable, gpu, reader, nullptr, nullptr) !=
HSA_STATUS_SUCCESS ||
hsa_executable_freeze(executable, nullptr) != HSA_STATUS_SUCCESS) {
hsa_executable_destroy(executable);
hsa_code_object_reader_destroy(reader);
return {};
}
hsa_executable_symbol_t symbol{};
uint64_t kernel_object = 0;
if (hsa_executable_get_symbol_by_name(executable, "vector_add.kd", &gpu, &symbol) !=
HSA_STATUS_SUCCESS ||
hsa_executable_symbol_get_info(symbol, HSA_EXECUTABLE_SYMBOL_INFO_KERNEL_OBJECT,
&kernel_object) != HSA_STATUS_SUCCESS ||
kernel_object == 0) {
hsa_executable_destroy(executable);
hsa_code_object_reader_destroy(reader);
return {};
}
// Pool + buffer + queue setup. Each step is checked so a failure on a
// flaky system returns an empty result cleanly rather than crashing on a
// null pointer or zero handle later. A single cleanup epilogue runs
// regardless of success.
auto gpu_pool = find_pool(gpu, HSA_AMD_SEGMENT_GLOBAL);
auto kernarg_pool = find_pool(cpu, HSA_AMD_SEGMENT_GLOBAL, /*host_accessible=*/true);
float *A_dev = nullptr, *B_dev = nullptr, *C_dev = nullptr;
void *kernarg = nullptr;
hsa_queue_t *queue = nullptr;
hsa_signal_t signal{};
std::vector<float> out;
bool ok = (gpu_pool.handle != 0) && (kernarg_pool.handle != 0);
if (ok)
ok = hsa_amd_memory_pool_allocate(gpu_pool, buf_size, 0, reinterpret_cast<void **>(&A_dev)) ==
HSA_STATUS_SUCCESS;
if (ok)
ok = hsa_amd_memory_pool_allocate(gpu_pool, buf_size, 0, reinterpret_cast<void **>(&B_dev)) ==
HSA_STATUS_SUCCESS;
if (ok)
ok = hsa_amd_memory_pool_allocate(gpu_pool, buf_size, 0, reinterpret_cast<void **>(&C_dev)) ==
HSA_STATUS_SUCCESS;
if (ok)
ok = hsa_amd_memory_pool_allocate(kernarg_pool, kKernArgsBytes, 0, &kernarg) ==
HSA_STATUS_SUCCESS;
if (ok) {
hsa_agent_t both[] = {cpu, gpu};
hsa_amd_agents_allow_access(2, both, nullptr, A_dev);
hsa_amd_agents_allow_access(2, both, nullptr, B_dev);
hsa_amd_agents_allow_access(2, both, nullptr, C_dev);
hsa_amd_agents_allow_access(2, both, nullptr, kernarg);
hsa_memory_copy(A_dev, a_in.data(), buf_size);
hsa_memory_copy(B_dev, b_in.data(), buf_size);
const std::vector<uint8_t> zero_bytes(buf_size, 0);
hsa_memory_copy(C_dev, zero_bytes.data(), buf_size);
std::memset(kernarg, 0, kKernArgsBytes);
struct __attribute__((packed)) KernArgs {
const float *A;
const float *B;
float *C;
uint32_t N;
};
auto *args = static_cast<KernArgs *>(kernarg);
args->A = A_dev;
args->B = B_dev;
args->C = C_dev;
args->N = n;
uint32_t queue_size = 0;
hsa_agent_get_info(gpu, HSA_AGENT_INFO_QUEUE_MAX_SIZE, &queue_size);
ok = hsa_queue_create(gpu, queue_size, HSA_QUEUE_TYPE_SINGLE, nullptr, nullptr, UINT32_MAX,
UINT32_MAX, &queue) == HSA_STATUS_SUCCESS &&
queue != nullptr;
}
if (ok)
ok = hsa_signal_create(1, 0, nullptr, &signal) == HSA_STATUS_SUCCESS;
if (ok) {
uint64_t write_idx = hsa_queue_add_write_index_relaxed(queue, 1);
auto *aql = static_cast<hsa_kernel_dispatch_packet_t *>(queue->base_address) +
(write_idx & (queue->size - 1));
std::memset(aql, 0, sizeof(*aql));
aql->setup = 1;
aql->workgroup_size_x = 64;
aql->workgroup_size_y = 1;
aql->workgroup_size_z = 1;
aql->grid_size_x = n;
aql->grid_size_y = 1;
aql->grid_size_z = 1;
aql->kernel_object = kernel_object;
aql->kernarg_address = kernarg;
aql->completion_signal = signal;
uint16_t header = HSA_PACKET_TYPE_KERNEL_DISPATCH << HSA_PACKET_HEADER_TYPE;
header |= 1 << HSA_PACKET_HEADER_BARRIER;
header |= HSA_FENCE_SCOPE_SYSTEM << HSA_PACKET_HEADER_ACQUIRE_FENCE_SCOPE;
header |= HSA_FENCE_SCOPE_SYSTEM << HSA_PACKET_HEADER_RELEASE_FENCE_SCOPE;
__atomic_store_n(reinterpret_cast<uint16_t *>(aql), header, __ATOMIC_RELEASE);
hsa_signal_store_relaxed(queue->doorbell_signal, write_idx);
hsa_signal_value_t val = hsa_signal_wait_scacquire(signal, HSA_SIGNAL_CONDITION_LT, 1,
5'000'000'000ULL, HSA_WAIT_STATE_BLOCKED);
if (val == 0) {
out.resize(n);
hsa_memory_copy(out.data(), C_dev, buf_size);
}
}
// Cleanup epilogue: each call is guarded so partial-initialization paths
// don't dereference null handles.
if (signal.handle != 0)
hsa_signal_destroy(signal);
if (queue != nullptr)
hsa_queue_destroy(queue);
if (kernarg != nullptr)
hsa_amd_memory_pool_free(kernarg);
if (A_dev != nullptr)
hsa_amd_memory_pool_free(A_dev);
if (B_dev != nullptr)
hsa_amd_memory_pool_free(B_dev);
if (C_dev != nullptr)
hsa_amd_memory_pool_free(C_dev);
hsa_executable_destroy(executable);
hsa_code_object_reader_destroy(reader);
return out;
}
} // namespace rocjitsu::dbi_test