Files
TheRock/rocm-systems/projects/rocprofiler-compute/sample/mega_kernel/main.cpp
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

829 lines
35 KiB
C++

// Copyright (c) Advanced Micro Devices, Inc.
// SPDX-License-Identifier: MIT
/**
* AMD GPU Mega Kernel Unit Test - Host Code
*
* This file contains the host-side code to run the GPU mega kernel
* and report test results for various AMD GPU architectures.
*
* Supported architectures:
* - gfx90a (MI250/CDNA2)
* - gfx942 (MI300/CDNA3)
* - gfx950 (MI350/CDNA4)
* - gfx1150 (Strix Point / RDNA 3.5)
* - gfx1151 (Strix Halo / RDNA 3.5)
* - gfx1152 (Krackan / RDNA 3.5)
* - gfx1153 (RDNA 3.5)
* - gfx1200 (RX 9060/RDNA4)
* - gfx1201 (RX 9070 XT/RDNA4)
*
* Build: make (default GPU_ARCH=gfx1151; override e.g. GPU_ARCH=gfx1201 make) or manual:
* hipcc -O2 -g -std=c++17 -Wall -Wextra -fPIC --offload-arch=gfx1151 -I. -o
* mega_kernel_test mega_kernel.hip main.cpp
*
*/
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <getopt.h>
#include <hip/hip_runtime.h>
#include <vector>
#include "atomic_global_buffers.h"
#include "mega_kernel_host_arch.hpp"
#include "mega_kernel_types.h"
// Default configuration
#define DEFAULT_BATCH_SIZE 1024
#define DEFAULT_BLOCK_SIZE 256
#define DEFAULT_NUM_ITERATIONS 10
#define MIN_BATCH_SIZE 64
#define MAX_BATCH_SIZE (1024 * 1024 * 16) // 16M elements max
#define MAX_NUM_ITERATIONS 10000
// Command-line options
struct TestConfig
{
int batch_size;
int block_size;
int num_iterations;
MfmaMode mfma_mode;
bool verbose;
bool help;
};
void
print_usage(const char* prog_name)
{
printf("Usage: %s [OPTIONS]\n\n", prog_name);
printf("AMD GPU Mega Kernel Unit Test\n\n");
printf("Options:\n");
printf(" -b, --batch-size <N> Set batch/problem size (default: %d)\n",
DEFAULT_BATCH_SIZE);
printf(" Valid range: %d to %d\n", MIN_BATCH_SIZE,
MAX_BATCH_SIZE);
printf(" -t, --block-size <N> Set thread block size (default: %d)\n",
DEFAULT_BLOCK_SIZE);
printf(" Must be power of 2, 64-256 (max 256 due to kernel "
"shared memory)\n");
printf(" -n, --num-iterations <N> Number of kernel iterations (default: %d)\n",
DEFAULT_NUM_ITERATIONS);
printf(" Valid range: 1 to %d\n", MAX_NUM_ITERATIONS);
printf(" -m, --mfma-mode <MODE> Set MFMA test mode (default: both)\n");
printf(" Modes: both, asm, builtin\n");
printf(" -v, --verbose Enable verbose output\n");
printf(" -h, --help Show this help message\n");
printf("\nExamples:\n");
printf(" %s # Run with default settings\n", prog_name);
printf(" %s -b 4096 # Run with 4096 elements\n", prog_name);
printf(" %s -b 65536 -t 512 # Run with 64K elements, 512 threads/block\n",
prog_name);
printf(" %s -n 100 # Run kernel 100 times\n", prog_name);
printf(" %s --mfma-mode asm # Test MFMA using inline assembly only\n",
prog_name);
printf(" %s -m builtin # Test MFMA using HIP builtins only\n", prog_name);
}
TestConfig
parse_args(int argc, char** argv)
{
TestConfig config;
config.batch_size = DEFAULT_BATCH_SIZE;
config.block_size = DEFAULT_BLOCK_SIZE;
config.num_iterations = DEFAULT_NUM_ITERATIONS;
config.mfma_mode = MFMA_MODE_BOTH;
config.verbose = false;
config.help = false;
static struct option long_options[] = { { "batch-size", required_argument, 0, 'b' },
{ "block-size", required_argument, 0, 't' },
{ "num-iterations", required_argument, 0,
'n' },
{ "mfma-mode", required_argument, 0, 'm' },
{ "verbose", no_argument, 0, 'v' },
{ "help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 } };
int opt;
int option_index = 0;
while((opt = getopt_long(argc, argv, "b:t:n:m:vh", long_options, &option_index)) !=
-1)
{
switch(opt)
{
case 'b':
config.batch_size = atoi(optarg);
if(config.batch_size < MIN_BATCH_SIZE)
{
fprintf(stderr, "Warning: batch_size too small, using minimum: %d\n",
MIN_BATCH_SIZE);
config.batch_size = MIN_BATCH_SIZE;
}
if(config.batch_size > MAX_BATCH_SIZE)
{
fprintf(stderr, "Warning: batch_size too large, using maximum: %d\n",
MAX_BATCH_SIZE);
config.batch_size = MAX_BATCH_SIZE;
}
break;
case 't':
config.block_size = atoi(optarg);
// Validate block size (must be power of 2, 64-256)
// NOTE: Max 256 due to fixed-size __shared__ arrays in kernel (e.g.,
// vmem_lds_test_buffer[256])
if(config.block_size < 64 || config.block_size > 256 ||
(config.block_size & (config.block_size - 1)) != 0)
{
fprintf(stderr,
"Warning: invalid block_size (must be 64-256, power of 2), "
"using default: %d\n",
DEFAULT_BLOCK_SIZE);
config.block_size = DEFAULT_BLOCK_SIZE;
}
break;
case 'n':
config.num_iterations = atoi(optarg);
if(config.num_iterations < 1)
{
fprintf(stderr,
"Warning: num_iterations too small, using minimum: 1\n");
config.num_iterations = 1;
}
if(config.num_iterations > MAX_NUM_ITERATIONS)
{
fprintf(stderr,
"Warning: num_iterations too large, using maximum: %d\n",
MAX_NUM_ITERATIONS);
config.num_iterations = MAX_NUM_ITERATIONS;
}
break;
case 'm':
if(strcmp(optarg, "asm") == 0)
{
config.mfma_mode = MFMA_MODE_ASM;
}
else if(strcmp(optarg, "builtin") == 0)
{
config.mfma_mode = MFMA_MODE_BUILTIN;
}
else if(strcmp(optarg, "both") == 0)
{
config.mfma_mode = MFMA_MODE_BOTH;
}
else
{
fprintf(stderr, "Warning: invalid mfma-mode '%s', using 'both'\n",
optarg);
config.mfma_mode = MFMA_MODE_BOTH;
}
break;
case 'v': config.verbose = true; break;
case 'h': config.help = true; break;
default: config.help = true; break;
}
}
return config;
}
// Macro for HIP error checking
#define HIP_CHECK(cmd) \
do \
{ \
hipError_t error = (cmd); \
if(error != hipSuccess) \
{ \
fprintf(stderr, "HIP Error: %s at %s:%d\n", hipGetErrorString(error), \
__FILE__, __LINE__); \
exit(EXIT_FAILURE); \
} \
} while(0)
void
print_separator()
{
printf("============================================================================="
"===\n");
}
void
print_test_result(const char* test_name, int passed, int expected = 1)
{
// Width of 48 to accommodate longest test name: "Async LDS Store
// (global_store_async_from_lds)"
if(passed < 0)
{
// Negative value indicates test was bypassed (not applicable to this arch)
printf(" %-48s : \033[33mBYPASSED\033[0m (N/A)\n", test_name);
}
else if(passed >= expected)
{
printf(" %-48s : \033[32mPASS\033[0m (%d/%d)\n", test_name, passed, expected);
}
else
{
printf(" %-48s : \033[31mFAIL\033[0m (%d/%d)\n", test_name, passed, expected);
}
}
// Global variable to store detected architecture for later use
static char g_arch_name[64] = "";
// 0=unknown, 1=gfx90a, 2=gfx942, 3=gfx950, 4=gfx1200, 5=gfx1201, 6=gfx115x
static int g_arch_type = 0;
void
print_device_info()
{
int device;
hipDeviceProp_t props;
HIP_CHECK(hipGetDevice(&device));
HIP_CHECK(hipGetDeviceProperties(&props, device));
mega_kernel_host::detect_architecture(props.gcnArchName, g_arch_name, sizeof(g_arch_name),
&g_arch_type);
print_separator();
printf("AMD GPU MEGA KERNEL UNIT TEST\n");
print_separator();
printf("Device Information:\n");
printf(" Name: %s\n", props.name);
printf(" GCN Architecture: %s\n", props.gcnArchName);
printf(" Compute Units: %d\n", props.multiProcessorCount);
printf(" Max Threads/Block: %d\n", props.maxThreadsPerBlock);
printf(" Warp Size: %d\n", props.warpSize);
printf(" Global Memory: %.2f GB\n",
props.totalGlobalMem / (1024.0 * 1024.0 * 1024.0));
printf(" Shared Memory/Block: %zu KB\n", props.sharedMemPerBlock / 1024);
printf(" Clock Rate: %d MHz\n", props.clockRate / 1000);
print_separator();
printf("Detected Platform: %s\n",
mega_kernel_host::arch_description(g_arch_type));
print_separator();
}
int
main(int argc, char** argv)
{
// Parse command-line arguments
TestConfig config = parse_args(argc, argv);
if(config.help)
{
print_usage(argv[0]);
return 0;
}
// Print device info
print_device_info();
// Configuration from command-line or defaults
const int BUFFER_SIZE = config.batch_size;
const int BLOCK_SIZE = config.block_size;
const int NUM_BLOCKS = (BUFFER_SIZE + BLOCK_SIZE - 1) / BLOCK_SIZE;
// Global atomic scratch: sizes and index layout — see atomic_global_buffers.h
const int GLOBAL_INT_SIZE = MEGA_KERNEL_GLOBAL_INT_ELEMENTS;
const int GLOBAL_FLOAT_SIZE = MEGA_KERNEL_GLOBAL_FLOAT_ELEMENTS;
const int GLOBAL_DOUBLE_SIZE = MEGA_KERNEL_GLOBAL_DOUBLE_ELEMENTS;
printf("Test Configuration:\n");
printf(" Batch/Buffer Size: %d elements\n", BUFFER_SIZE);
printf(" Block Size: %d threads\n", BLOCK_SIZE);
printf(" Number of Blocks: %d\n", NUM_BLOCKS);
printf(" Total Threads: %d\n", NUM_BLOCKS * BLOCK_SIZE);
const char* mfma_mode_str = (config.mfma_mode == MFMA_MODE_ASM) ? "asm"
: (config.mfma_mode == MFMA_MODE_BUILTIN) ? "builtin"
: "both";
printf(" MFMA Test Mode: %s\n", mfma_mode_str);
printf(" Num Iterations: %d\n", config.num_iterations);
if(config.verbose)
{
printf(" Verbose Mode: enabled\n");
}
print_separator();
// Allocate host memory
TestResults h_results;
memset(&h_results, 0, sizeof(TestResults));
float* h_input = (float*) malloc(BUFFER_SIZE * sizeof(float));
float* h_output = (float*) malloc(BUFFER_SIZE * sizeof(float));
// Initialize input buffer
for(int i = 0; i < BUFFER_SIZE; i++)
{
h_input[i] = (float) i;
h_output[i] = 0.0f;
}
// Allocate device memory
TestResults* d_results;
float* d_global_float;
double* d_global_double;
int* d_global_int;
float* d_input;
float* d_output;
float* d_async_lds_src;
float* d_async_lds_dst;
const int ASYNC_LDS_SIZE = 256; // Size for async LDS test buffers
HIP_CHECK(hipMalloc(&d_results, sizeof(TestResults)));
HIP_CHECK(hipMalloc(&d_global_float, GLOBAL_FLOAT_SIZE * sizeof(float)));
HIP_CHECK(hipMalloc(&d_global_double, GLOBAL_DOUBLE_SIZE * sizeof(double)));
HIP_CHECK(hipMalloc(&d_global_int, GLOBAL_INT_SIZE * sizeof(int)));
HIP_CHECK(hipMalloc(&d_input, BUFFER_SIZE * sizeof(float)));
HIP_CHECK(hipMalloc(&d_output, BUFFER_SIZE * sizeof(float)));
HIP_CHECK(hipMalloc(&d_async_lds_src, ASYNC_LDS_SIZE * sizeof(float)));
HIP_CHECK(hipMalloc(&d_async_lds_dst, ASYNC_LDS_SIZE * sizeof(float)));
// Host-prepared zeros for global atomic buffers (kernel CAS etc. expect initial 0).
std::vector<int> h_global_int(static_cast<size_t>(GLOBAL_INT_SIZE), 0);
std::vector<float> h_global_float(static_cast<size_t>(GLOBAL_FLOAT_SIZE), 0.0f);
std::vector<double> h_global_double(static_cast<size_t>(GLOBAL_DOUBLE_SIZE), 0.0);
// Initialize device memory
HIP_CHECK(hipMemset(d_results, 0, sizeof(TestResults)));
HIP_CHECK(hipMemcpy(d_global_int, h_global_int.data(),
h_global_int.size() * sizeof(int), hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy(d_global_float, h_global_float.data(),
h_global_float.size() * sizeof(float), hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy(d_global_double, h_global_double.data(),
h_global_double.size() * sizeof(double), hipMemcpyHostToDevice));
HIP_CHECK(
hipMemcpy(d_input, h_input, BUFFER_SIZE * sizeof(float), hipMemcpyHostToDevice));
HIP_CHECK(hipMemset(d_output, 0, BUFFER_SIZE * sizeof(float)));
HIP_CHECK(hipMemset(d_async_lds_src, 0, ASYNC_LDS_SIZE * sizeof(float)));
HIP_CHECK(hipMemset(d_async_lds_dst, 0, ASYNC_LDS_SIZE * sizeof(float)));
// Create texture and surface objects for Strix (gfx1150/1151/1152) to exercise
// INSTS_TEX_LOAD (tex1Dfetch) and INSTS_TEX_STORE (surf1Dwrite).
hipTextureObject_t tex_obj = 0;
hipSurfaceObject_t surf_obj = 0;
float* d_surf_buffer = nullptr;
if(g_arch_type == 6)
{
hipResourceDesc resDesc;
memset(&resDesc, 0, sizeof(resDesc));
resDesc.resType = hipResourceTypeLinear;
resDesc.res.linear.sizeInBytes = BUFFER_SIZE * sizeof(float);
resDesc.res.linear.desc =
hipCreateChannelDesc(32, 0, 0, 0, hipChannelFormatKindFloat);
resDesc.res.linear.devPtr = d_input;
hipTextureDesc texDesc;
memset(&texDesc, 0, sizeof(texDesc));
texDesc.normalizedCoords = 0;
texDesc.filterMode = hipFilterModePoint;
texDesc.addressMode[0] = hipAddressModeClamp;
HIP_CHECK(hipCreateTextureObject(&tex_obj, &resDesc, &texDesc, nullptr));
HIP_CHECK(hipMalloc(&d_surf_buffer, BUFFER_SIZE * sizeof(float)));
HIP_CHECK(hipMemset(d_surf_buffer, 0, BUFFER_SIZE * sizeof(float)));
resDesc.res.linear.devPtr = d_surf_buffer;
HIP_CHECK(hipCreateSurfaceObject(&surf_obj, &resDesc));
}
printf("Running GPU Mega Kernel for %s...\n",
mega_kernel_host::arch_description(g_arch_type));
if(config.num_iterations > 1)
{
printf(" Running %d iterations...\n", config.num_iterations);
}
print_separator();
// Create events for timing
hipEvent_t start, stop;
HIP_CHECK(hipEventCreate(&start));
HIP_CHECK(hipEventCreate(&stop));
// Compute dynamic shared memory size (must match kernel expectations)
// Layout in test_lds_operations:
// - lds_float: BLOCK_SIZE * sizeof(float)
// - lds_int: BLOCK_SIZE * sizeof(int)
// - lds_double: (BLOCK_SIZE / 2) * sizeof(double)
// - lds_atomic_counter: sizeof(int) for LDS atomic test
const size_t sharedMemBytes =
static_cast<size_t>(BLOCK_SIZE) * sizeof(float) + // lds_float
static_cast<size_t>(BLOCK_SIZE) * sizeof(int) + // lds_int
static_cast<size_t>(BLOCK_SIZE / 2) * sizeof(double) + // lds_double
sizeof(int); // lds_atomic_counter
// Record start time
HIP_CHECK(hipEventRecord(start, 0));
// Run kernel for the specified number of iterations
for(int iter = 0; iter < config.num_iterations; iter++)
{
// Reset device memory for each iteration (except first)
// NOTE: d_input must also be reset because test_vmem_operations modifies it
if(iter > 0)
{
HIP_CHECK(hipMemset(d_results, 0, sizeof(TestResults)));
HIP_CHECK(hipMemcpy(d_global_int, h_global_int.data(),
h_global_int.size() * sizeof(int),
hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy(d_global_float, h_global_float.data(),
h_global_float.size() * sizeof(float),
hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy(d_global_double, h_global_double.data(),
h_global_double.size() * sizeof(double),
hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy(d_input, h_input, BUFFER_SIZE * sizeof(float),
hipMemcpyHostToDevice));
HIP_CHECK(hipMemset(d_output, 0, BUFFER_SIZE * sizeof(float)));
}
// Launch kernel
hipLaunchKernelGGL(mega_kernel, dim3(NUM_BLOCKS), dim3(BLOCK_SIZE),
sharedMemBytes, 0, d_results, d_global_float, d_global_double,
d_global_int, d_input, d_output, d_async_lds_src,
d_async_lds_dst, BUFFER_SIZE, (int) config.mfma_mode, tex_obj,
surf_obj);
// Check for launch errors
HIP_CHECK(hipGetLastError());
}
// Wait for all iterations to complete
HIP_CHECK(hipDeviceSynchronize());
// Record stop time
HIP_CHECK(hipEventRecord(stop, 0));
HIP_CHECK(hipEventSynchronize(stop));
float total_elapsed_ms;
HIP_CHECK(hipEventElapsedTime(&total_elapsed_ms, start, stop));
float avg_elapsed_ms = total_elapsed_ms / config.num_iterations;
if(tex_obj != 0)
{
HIP_CHECK(hipDestroyTextureObject(tex_obj));
}
if(surf_obj != 0)
{
HIP_CHECK(hipDestroySurfaceObject(surf_obj));
}
if(d_surf_buffer != nullptr)
{
HIP_CHECK(hipFree(d_surf_buffer));
}
// Copy results back
HIP_CHECK(
hipMemcpy(&h_results, d_results, sizeof(TestResults), hipMemcpyDeviceToHost));
HIP_CHECK(hipMemcpy(h_output, d_output, BUFFER_SIZE * sizeof(float),
hipMemcpyDeviceToHost));
// Verify memory operations output
bool memory_verified = true;
for(int i = 0; i < BUFFER_SIZE && memory_verified; i++)
{
float expected = h_input[i] * 2.0f + 1.0f;
if(fabsf(h_output[i] - expected) > 0.001f)
{
memory_verified = false;
printf("Memory verification failed at index %d: expected %.3f, got %.3f\n", i,
expected, h_output[i]);
}
}
// Print results
printf("TEST RESULTS\n");
print_separator();
printf("\n[Category: Warp/Wave Operations]\n");
print_test_result("Warp Shuffle (shfl/shfl_xor/shfl_up/down)",
h_results.warp_shuffle_passed);
print_test_result("Warp Ballot (__ballot/__any/__all)", h_results.warp_ballot_passed);
print_test_result("Warp Permute (ds_bpermute/ds_permute)",
h_results.warp_permute_passed);
print_test_result("Warp Reduce (shuffle-based reduction)",
h_results.warp_reduce_passed);
printf("\n[Category: FP8/BF8 Conversions (gfx942+/RDNA4)]\n");
print_test_result("FP8 E4M3 Conversion", h_results.fp8_convert_passed);
print_test_result("BF8 E5M2 Conversion", h_results.bf8_convert_passed);
printf("\n[Category: FP16/BF16 Operations]\n");
print_test_result("FP16 Arithmetic", h_results.fp16_convert_passed);
print_test_result("BF16 Conversion", h_results.bf16_convert_passed);
printf("\n[Category: FP32/FP64 Arithmetic]\n");
print_test_result("FP32 Arithmetic (add/sub/mul/div/fma)",
h_results.fp32_arith_passed);
print_test_result("FP64 Arithmetic (add/sub/mul/div/fma)",
h_results.fp64_arith_passed);
printf("\n[Category: Integer Operations]\n");
print_test_result("INT32 Arithmetic & Bit Ops", h_results.int32_arith_passed);
print_test_result("INT64 Arithmetic", h_results.int64_arith_passed);
printf("\n[Category: Packed Operations]\n");
print_test_result("Packed FP16 (half2)", h_results.pk_f16_passed);
print_test_result("Packed BF16", h_results.pk_bf16_passed);
print_test_result("Packed Add Operations", h_results.pk_add_passed);
printf("\n[Category: Atomic Operations]\n");
print_test_result("Atomic Add FP32", h_results.atomic_add_f32_passed);
print_test_result("Atomic Add FP64 (HW atomic on CDNA)",
h_results.atomic_add_f64_passed);
print_test_result("Atomic Min FP32", h_results.atomic_min_f32_passed);
print_test_result("Atomic Max FP32", h_results.atomic_max_f32_passed);
print_test_result("Atomic Integer Operations", h_results.atomic_int_passed);
print_test_result("LDS Atomic Operations", h_results.lds_atomic_passed);
printf("\n[Category: Transcendental Functions]\n");
print_test_result("Sine (sinf)", h_results.trans_sin_passed);
print_test_result("Cosine (cosf)", h_results.trans_cos_passed);
print_test_result("Exponential (expf)", h_results.trans_exp_passed);
print_test_result("Logarithm (logf)", h_results.trans_log_passed);
print_test_result("Square Root (sqrtf)", h_results.trans_sqrt_passed);
print_test_result("Reciprocal (1/x)", h_results.trans_rcp_passed);
print_test_result("Reciprocal Square Root (rsqrtf)", h_results.trans_rsq_passed);
printf("\n[Category: Memory Operations]\n");
print_test_result("Global Memory Load", h_results.global_load_passed);
print_test_result("Global Memory Store", h_results.global_store_passed);
print_test_result("LDS Load", h_results.lds_load_passed);
print_test_result("LDS Store", h_results.lds_store_passed);
print_test_result("Memory Output Verification", memory_verified ? 1 : 0);
printf("\n[Category: DOT Product Operations]\n");
print_test_result("DOT4 (4-element INT8 dot)", h_results.dot4_passed);
print_test_result("DOT8 (8-element INT4 dot)", h_results.dot8_passed);
printf("\n[Category: MFMA Operations (gfx90a+/CDNA)]\n");
print_test_result("MFMA FP32 (v_mfma_f32_32x32x2f32)", h_results.mfma_f32_passed);
print_test_result("MFMA FP64 (v_mfma_f64_16x16x4f64)", h_results.mfma_f64_passed);
print_test_result("MFMA FP16 (v_mfma_f32_32x32x8f16)", h_results.mfma_f16_passed);
print_test_result("MFMA BF16 (v_mfma_f32_32x32x*bf16)", h_results.mfma_bf16_passed);
print_test_result("MFMA INT8 (v_mfma_i32_32x32x*i8)", h_results.mfma_i8_passed);
print_test_result("MFMA FP8 (v_mfma_f32_32x32x16_fp8)", h_results.mfma_f8_passed);
printf("\n[Category: Dual-Issue VALU (instruction patterns only)]\n");
print_test_result("Dual-Issue Patterns (not scheduler-verified)",
h_results.dual_issue_passed);
printf("\n[Category: Lane/Thread Information]\n");
print_test_result("Lane ID Builtin", h_results.lane_id_passed);
print_test_result("Wavefront Size", h_results.wavefront_passed);
printf("\n[Category: Async LDS Operations (gfx950+/RDNA4)]\n");
print_test_result("Async LDS Load (global_load_async_to_lds)",
h_results.async_lds_load_passed);
print_test_result("Async LDS Store (global_store_async_from_lds)",
h_results.async_lds_store_passed);
printf("\n[Category: WMMA Operations (RDNA4/gfx12 - Wave32)]\n");
print_test_result("WMMA FP16 → FP32 (wmma_f32_16x16x16_f16)",
h_results.wmma_f16_passed);
print_test_result("WMMA BF16 → FP32 (wmma_f32_16x16x16_bf16)",
h_results.wmma_bf16_passed);
print_test_result("WMMA INT8 → INT32 (wmma_i32_16x16x16_iu8)",
h_results.wmma_i8_passed);
printf("\n[Category: VMEM (Vector Memory) Operations - Inline ASM]\n");
print_test_result("Flat Load/Store (flat_load_b32/flat_store_b32)",
h_results.vmem_flat_passed);
print_test_result("Global Load/Store (global_load_b32/global_store_b32)",
h_results.vmem_global_passed);
print_test_result("Buffer Load/Store (buffer_load_dword)",
h_results.vmem_buffer_passed);
print_test_result("Scratch Memory (scratch_load/scratch_store)",
h_results.vmem_scratch_passed);
print_test_result("LDS Operations (ds_read_b32/ds_write_b32)",
h_results.vmem_lds_passed);
print_test_result("Texture Load (tex1Dfetch / INSTS_TEX_LOAD)",
h_results.vmem_tex_load_passed);
print_test_result("Texture Store (surf1Dwrite / INSTS_TEX_STORE)",
h_results.vmem_tex_store_passed);
printf("\n[Category: Atomic Operations - Inline ASM]\n");
print_test_result("Global INT Atomics (global_atomic_add/sub/min/max)",
h_results.atomic_global_int_passed);
print_test_result("Global FP32 Atomics (global_atomic_add_f32)",
h_results.atomic_global_f32_passed);
print_test_result("Global FP64 Atomics (global_atomic_add_f64/min/max)",
h_results.atomic_global_f64_passed);
print_test_result("Flat INT Atomics (flat_atomic_add)",
h_results.atomic_flat_int_passed);
print_test_result("DS INT Atomics (ds_add_u32/min_i32/max_u32)",
h_results.atomic_ds_int_passed);
print_test_result("DS FP32 Atomics (ds_add_f32/min_f32)",
h_results.atomic_ds_f32_passed);
print_test_result("DS FP64 Atomics (ds_add_f64)", h_results.atomic_ds_f64_passed);
print_test_result("Packed FP16 Atomics (global_atomic_pk_add_f16)",
h_results.atomic_pk_f16_passed);
print_test_result("Packed BF16 Atomics (global_atomic_pk_add_bf16)",
h_results.atomic_pk_bf16_passed);
print_test_result("CAS Atomics (global_atomic_cmpswap)", h_results.atomic_cas_passed);
print_separator();
// Summary - count passed, failed, and bypassed tests
int passed_categories = 0;
int bypassed_categories = 0;
int failed_categories = 0;
// Helper macro to count test results
// > 0 = passed, < 0 = bypassed, == 0 = failed
#define COUNT_TEST(result) \
if((result) > 0) \
passed_categories++; \
else if((result) < 0) \
bypassed_categories++; \
else \
failed_categories++;
COUNT_TEST(h_results.warp_shuffle_passed);
COUNT_TEST(h_results.warp_ballot_passed);
COUNT_TEST(h_results.warp_permute_passed);
COUNT_TEST(h_results.warp_reduce_passed);
COUNT_TEST(h_results.fp8_convert_passed);
COUNT_TEST(h_results.bf8_convert_passed);
COUNT_TEST(h_results.fp16_convert_passed);
COUNT_TEST(h_results.bf16_convert_passed);
COUNT_TEST(h_results.fp32_arith_passed);
COUNT_TEST(h_results.fp64_arith_passed);
COUNT_TEST(h_results.int32_arith_passed);
COUNT_TEST(h_results.int64_arith_passed);
COUNT_TEST(h_results.pk_f16_passed);
COUNT_TEST(h_results.pk_bf16_passed);
COUNT_TEST(h_results.pk_add_passed);
COUNT_TEST(h_results.atomic_add_f32_passed);
COUNT_TEST(h_results.atomic_add_f64_passed);
COUNT_TEST(h_results.atomic_min_f32_passed);
COUNT_TEST(h_results.atomic_max_f32_passed);
COUNT_TEST(h_results.atomic_int_passed);
COUNT_TEST(h_results.lds_atomic_passed);
COUNT_TEST(h_results.trans_sin_passed);
COUNT_TEST(h_results.trans_cos_passed);
COUNT_TEST(h_results.trans_exp_passed);
COUNT_TEST(h_results.trans_log_passed);
COUNT_TEST(h_results.trans_sqrt_passed);
COUNT_TEST(h_results.trans_rcp_passed);
COUNT_TEST(h_results.trans_rsq_passed);
COUNT_TEST(h_results.global_load_passed);
COUNT_TEST(h_results.global_store_passed);
COUNT_TEST(h_results.lds_load_passed);
COUNT_TEST(h_results.lds_store_passed);
COUNT_TEST(h_results.dot4_passed);
COUNT_TEST(h_results.dot8_passed);
COUNT_TEST(h_results.mfma_f32_passed);
COUNT_TEST(h_results.mfma_f64_passed);
COUNT_TEST(h_results.mfma_f16_passed);
COUNT_TEST(h_results.mfma_bf16_passed);
COUNT_TEST(h_results.mfma_i8_passed);
COUNT_TEST(h_results.mfma_f8_passed);
COUNT_TEST(h_results.dual_issue_passed);
COUNT_TEST(h_results.lane_id_passed);
COUNT_TEST(h_results.wavefront_passed);
COUNT_TEST(h_results.async_lds_load_passed);
COUNT_TEST(h_results.async_lds_store_passed);
COUNT_TEST(h_results.wmma_f16_passed);
COUNT_TEST(h_results.wmma_bf16_passed);
COUNT_TEST(h_results.wmma_i8_passed);
COUNT_TEST(h_results.vmem_flat_passed);
COUNT_TEST(h_results.vmem_global_passed);
COUNT_TEST(h_results.vmem_buffer_passed);
COUNT_TEST(h_results.vmem_scratch_passed);
COUNT_TEST(h_results.vmem_lds_passed);
COUNT_TEST(h_results.vmem_tex_load_passed);
COUNT_TEST(h_results.vmem_tex_store_passed);
COUNT_TEST(h_results.atomic_global_int_passed);
COUNT_TEST(h_results.atomic_global_f32_passed);
COUNT_TEST(h_results.atomic_global_f64_passed);
COUNT_TEST(h_results.atomic_flat_int_passed);
COUNT_TEST(h_results.atomic_ds_int_passed);
COUNT_TEST(h_results.atomic_ds_f32_passed);
COUNT_TEST(h_results.atomic_ds_f64_passed);
COUNT_TEST(h_results.atomic_pk_f16_passed);
COUNT_TEST(h_results.atomic_pk_bf16_passed);
COUNT_TEST(h_results.atomic_cas_passed);
if(memory_verified)
passed_categories++;
else
failed_categories++;
#undef COUNT_TEST
int total_tests =
passed_categories + failed_categories; // Exclude bypassed from total
printf("SUMMARY\n");
print_separator();
printf("Test Categories Passed: %d / %d\n", passed_categories, total_tests);
printf("Test Categories Failed: %d\n", failed_categories);
printf("Test Categories Bypassed: %d (N/A for this architecture)\n",
bypassed_categories);
printf("Kernel Iterations: %d\n", config.num_iterations);
printf("Total Execution Time: %.3f ms\n", total_elapsed_ms);
if(config.num_iterations > 1)
{
printf("Average Time/Iteration: %.3f ms\n", avg_elapsed_ms);
}
if(failed_categories == 0)
{
printf("\n\033[32m*** OVERALL: PASSED (%d/%d, %d bypassed) ***\033[0m\n",
passed_categories, total_tests, bypassed_categories);
}
else
{
printf(
"\n\033[31m*** OVERALL: FAILED (%d/%d, %d failed, %d bypassed) ***\033[0m\n",
passed_categories, total_tests, failed_categories, bypassed_categories);
}
print_separator();
// Print architecture-specific feature summary
printf("\n%s SPECIFIC FEATURES TESTED:\n",
mega_kernel_host::arch_description(g_arch_type));
switch(g_arch_type)
{
case 1: // MI250/CDNA2
printf(" - FP32/FP64/FP16/BF16 arithmetic\n");
printf(" - Hardware FP64 atomics (add/min/max)\n");
printf(" - DOT4/DOT8 integer dot products (v_dot4_u32_u8)\n");
printf(" - MFMA (Matrix Fused Multiply-Add) instructions\n");
printf(" - Wave64 operations\n");
printf(" - LDS operations (32KB per CU)\n");
break;
case 2: // MI300/CDNA3
printf(" - All MI250 features plus:\n");
printf(" - FP8/BF8 conversions (FNUZ format)\n");
printf(" - TF32 (TensorFloat-32) support\n");
printf(" - Sparse MFMA instructions\n");
printf(" - Same-wave dual-issue via VOPD/VOPD3 encoding\n");
printf(" - Two operations per instruction (X op + Y op)\n");
printf(" - Enhanced Wave64 operations\n");
break;
case 3: // MI350/CDNA4
printf(" - All MI300 features plus:\n");
printf(" - FP8/BF8 scaled conversions (OCP format)\n");
printf(" - FP6/FP4 conversions\n");
printf(" - Cross-wave dual-issue VALU (in addition to VOPD)\n");
printf(" - 2 instructions/cycle from different waves\n");
printf(" - Requires Wave32, single-cycle ops, different MACC units\n");
printf(" - Async LDS (global_load_async_to_lds, "
"global_store_async_from_lds)\n");
printf(" - Wave32 optimized operations\n");
break;
case 4: // RX 9060/RDNA4
case 5: // RX 9070 XT/RDNA4
printf(" - FP8/BF8 conversions (OCP format)\n");
printf(" - FP16/BF16 optimized arithmetic\n");
printf(" - WMMA (Wave Matrix Multiply Accumulate) instructions\n");
printf(" - 16x16x16 matrix operations on Wave32\n");
printf(" - FP16/BF16 → FP32, INT8 → INT32\n");
printf(" - 2x throughput vs RDNA3 (1024 FLOPS/clock for FP16/BF16)\n");
printf(" - Async LDS operations\n");
printf(" - Wave32 native execution\n");
printf(" - WGP (Workgroup Processor) architecture\n");
printf(" - Limited FP64 support (consumer GPU)\n");
break;
case 6: // Strix/Strix Halo (RDNA 3.5)
printf(" - RDNA 3.5 APU iGPU: Strix Point (gfx1150) / Strix Halo (gfx1151) / "
"Krackan (gfx1152)\n");
printf(" - Wave32 native, WMMA (FP16/BF16/INT8) 16x16x16\n");
printf(" - VOPD same-wave dual-issue VALU\n");
printf(" - No CDNA-style FP8/BF8 cvt builtins; scalar FP; s_singleuse_vdst\n");
printf(" - Async LDS, WGP architecture\n");
break;
default:
printf(" - Basic GPU operations\n");
printf(" - FP32/FP64 arithmetic\n");
printf(" - Integer operations\n");
printf(" - LDS operations\n");
break;
}
print_separator();
// Cleanup
HIP_CHECK(hipEventDestroy(start));
HIP_CHECK(hipEventDestroy(stop));
HIP_CHECK(hipFree(d_results));
HIP_CHECK(hipFree(d_global_float));
HIP_CHECK(hipFree(d_global_double));
HIP_CHECK(hipFree(d_global_int));
HIP_CHECK(hipFree(d_input));
HIP_CHECK(hipFree(d_output));
HIP_CHECK(hipFree(d_async_lds_src));
HIP_CHECK(hipFree(d_async_lds_dst));
free(h_input);
free(h_output);
return (failed_categories == 0) ? 0 : 1;
}