Validate gfx1201 WMMA layout and compile first K kernel

This commit is contained in:
Codex committed 2026-09-29 19:50:50 +03:00
1 parent 64669c2c36
commit 6760294f60
10 files changed
+404 -10

No files matched your search

+32 -1
View File
@@ -21,7 +21,28 @@ add_custom_command(OUTPUT "${probe_object}"
-o "${probe_object}" -o "${probe_object}"
DEPENDS tools/windows/probe_kernel.hip VERBATIM) DEPENDS tools/windows/probe_kernel.hip VERBATIM)
add_custom_target(d4r_probe_code_object ALL DEPENDS "${probe_object}") add_custom_target(d4r_probe_code_object ALL DEPENDS "${probe_object}")
foreach(probe hip_gfx1201_probe cuda_driver_probe d3d12_hip_interop_probe hip_stream_lifecycle_probe ngx_cuda_init_probe) set(wmma_object "${CMAKE_CURRENT_BINARY_DIR}/wmma_gfx1201.hsaco")
add_custom_command(OUTPUT "${wmma_object}"
COMMAND "${D4R_HIP_CLANG}" -x hip --offload-arch=gfx1201
--offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64
-nogpuinc -nogpulib -O2 "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/wmma_gfx1201.hip"
-o "${wmma_object}"
DEPENDS tools/windows/wmma_gfx1201.hip kernels/common/wmma_backend.h VERBATIM)
add_custom_target(d4r_wmma_code_object ALL DEPENDS "${wmma_object}")
# Compile-only K port smoke test. This object is intentionally not installed as
# a native override until its transformer outputs pass the K reference/replay.
set(k_enc1_object "${CMAKE_CURRENT_BINARY_DIR}/dltss_pwin_enc1_layer_gfx1201.hsaco")
add_custom_command(OUTPUT "${k_enc1_object}"
COMMAND "${D4R_HIP_CLANG}" -x hip -std=c++17 --offload-arch=gfx1201
--offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64
-nogpuinc -nogpulib -O2 -DD4R_DEVICE_ONLY_MINIMAL
"${CMAKE_CURRENT_SOURCE_DIR}/kernels/k/dltss_pwin_enc1_layer.hip"
-o "${k_enc1_object}"
DEPENDS kernels/k/dltss_pwin_enc1_layer.hip kernels/k/pwin_layer.h
kernels/k/pwin_common.h kernels/common/wmma_backend.h
kernels/common/hip_device_minimal.h VERBATIM)
add_custom_target(d4r_k_gfx1201_compile ALL DEPENDS "${k_enc1_object}")
foreach(probe hip_gfx1201_probe gfx12_wmma_probe k_module_probe cuda_driver_probe d3d12_hip_interop_probe hip_stream_lifecycle_probe ngx_cuda_init_probe)
add_executable(d4r_${probe} tools/windows/${probe}.cpp) add_executable(d4r_${probe} tools/windows/${probe}.cpp)
target_compile_definitions(d4r_${probe} PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__) target_compile_definitions(d4r_${probe} PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__)
target_include_directories(d4r_${probe} PRIVATE "${D4R_HIP_ROOT}/include") target_include_directories(d4r_${probe} PRIVATE "${D4R_HIP_ROOT}/include")
@@ -72,13 +93,23 @@ endif()
install(TARGETS d4r_nvapi_compat RUNTIME DESTINATION nvapi-compat) install(TARGETS d4r_nvapi_compat RUNTIME DESTINATION nvapi-compat)
target_link_libraries(d4r_d3d12_hip_interop_probe PRIVATE d3d12 dxgi dxguid) target_link_libraries(d4r_d3d12_hip_interop_probe PRIVATE d3d12 dxgi dxguid)
add_dependencies(d4r_hip_gfx1201_probe d4r_probe_code_object) add_dependencies(d4r_hip_gfx1201_probe d4r_probe_code_object)
add_dependencies(d4r_gfx12_wmma_probe d4r_wmma_code_object)
add_dependencies(d4r_k_module_probe d4r_k_gfx1201_compile)
install(FILES "${probe_object}" DESTINATION bin) install(FILES "${probe_object}" DESTINATION bin)
install(FILES "${wmma_object}" DESTINATION bin)
install(FILES "${k_enc1_object}" DESTINATION experimental/k)
install(FILES scripts/windows/test-windows-rdna4.ps1 DESTINATION .) install(FILES scripts/windows/test-windows-rdna4.ps1 DESTINATION .)
install(FILES docs/windows-rdna4-port.md DESTINATION docs) install(FILES docs/windows-rdna4-port.md DESTINATION docs)
include(CTest) include(CTest)
add_test(NAME hip_gfx1201 COMMAND d4r_hip_gfx1201_probe add_test(NAME hip_gfx1201 COMMAND d4r_hip_gfx1201_probe
--hip-root "${D4R_HIP_ROOT}" --module "${probe_object}") --hip-root "${D4R_HIP_ROOT}" --module "${probe_object}")
set_tests_properties(hip_gfx1201 PROPERTIES TIMEOUT 90 LABELS "hardware;gfx1201") set_tests_properties(hip_gfx1201 PROPERTIES TIMEOUT 90 LABELS "hardware;gfx1201")
add_test(NAME gfx12_wmma COMMAND d4r_gfx12_wmma_probe
--hip-root "${D4R_HIP_ROOT}" --module "${wmma_object}" --iterations 16)
set_tests_properties(gfx12_wmma PROPERTIES TIMEOUT 90 LABELS "hardware;gfx1201;wmma")
add_test(NAME k_enc1_module COMMAND d4r_k_module_probe
--hip-root "${D4R_HIP_ROOT}" --module "${k_enc1_object}")
set_tests_properties(k_enc1_module PROPERTIES TIMEOUT 90 LABELS "hardware;gfx1201;k_compile")
add_test(NAME d3d12_hip_interop COMMAND d4r_d3d12_hip_interop_probe add_test(NAME d3d12_hip_interop COMMAND d4r_d3d12_hip_interop_probe
--hip-root "${D4R_HIP_ROOT}" --module "${probe_object}" --iterations 32) --hip-root "${D4R_HIP_ROOT}" --module "${probe_object}" --iterations 32)
set_tests_properties(d3d12_hip_interop PROPERTIES TIMEOUT 120 LABELS "hardware;interop") set_tests_properties(d3d12_hip_interop PROPERTIES TIMEOUT 120 LABELS "hardware;interop")
+3 -1
View File
@@ -7,7 +7,9 @@ d4r runs NVIDIA's official DLSS Super Resolution library (`nvngx_dlss.dll`) in W
It is an experiment. It has been tested on one GPU in a handful of games, it depends on unreleased patches to ZLUDA and vkd3d-proton. It is an experiment. It has been tested on one GPU in a handful of games, it depends on unreleased patches to ZLUDA and vkd3d-proton.
**Windows / RDNA4 development:** the `windows-rdna4` branch adds native Windows **Windows / RDNA4 development:** the `windows-rdna4` branch adds native Windows
gfx1201 HIP/ZLUDA diagnostics, validated D3D12/HIP interop and an NGX init probe. gfx1201 HIP/ZLUDA diagnostics, validated D3D12/HIP interop, an NGX init probe,
and a CPU-checked gfx12 WMMA backend. An experimental K `enc1` kernel compiles
and loads but has not passed transformer-output validation.
Build commands, hardware results and remaining Build commands, hardware results and remaining
porting gates are in [docs/windows-rdna4-port.md](docs/windows-rdna4-port.md). porting gates are in [docs/windows-rdna4-port.md](docs/windows-rdna4-port.md).
This branch does not yet provide a Windows DLSS runtime. This branch does not yet provide a Windows DLSS runtime.
+40 -6
View File
@@ -6,8 +6,9 @@ This is a work log for an implementation in progress, not a claim of DLSS suppor
## Milestone and gates ## Milestone and gates
Current milestone: M1/M2/M4 passed; NGX init + DLSS CUDA capability discovery Current milestone: M1/M2/M4/M5 passed; NGX init + DLSS CUDA capability discovery
passed with the user's DLLs. Next: validated gfx12 WMMA and native K kernels. passed with the user's DLLs. The first K `enc1` code object compiles and loads;
its prep/transformer have not yet been executed or checked against replay.
Do not integrate NGX until the integer PTX workload is stable on the real GPU. Do not integrate NGX until the integer PTX workload is stable on the real GPU.
| Gate | Status | | Gate | Status |
@@ -17,7 +18,7 @@ Do not integrate NGX until the integer PTX workload is stable on the real GPU.
| M2: Windows ZLUDA integer PTX, primary and created contexts | PASS, 32 iterations each + guard verification | | M2: Windows ZLUDA integer PTX, primary and created contexts | PASS, 32 iterations each + guard verification |
| M3: Windows NGX initialization and simple DLSS path | Init + SR capabilities PASS; Evaluate/transformer pending | | M3: Windows NGX initialization and simple DLSS path | Init + SR capabilities PASS; Evaluate/transformer pending |
| M4: D3D12 / HIP external memory and fence round trip | PASS with TheRock; stable 7.2 has mapped-view leak | | M4: D3D12 / HIP external memory and fence round trip | PASS with TheRock; stable 7.2 has mapped-view leak |
| M5: independently validated gfx12 WMMA backend | Pending | | M5: independently validated gfx12 WMMA backend | PASS: raw + legacy adapter, 16 cases, max abs/relative error 0 |
| M6: K layers, full transformer and image validation | Pending | | M6: K layers, full transformer and image validation | Pending |
| M7: M FP16-equivalent baseline and full transformer | Pending | | M7: M FP16-equivalent baseline and full transformer | Pending |
| M8: standalone Windows D3D12 NGX harness | Pending | | M8: standalone Windows D3D12 NGX harness | Pending |
@@ -118,6 +119,27 @@ or update all dependent loads/stores and intermediate fragments together.
Start with FP16 / FP32 accumulate. M native FP8 is a later measured optimization. Start with FP16 / FP32 accumulate. M native FP8 is a later measured optimization.
Compile success is not proof of lane layout or numerical correctness. Compile success is not proof of lane layout or numerical correctness.
`kernels/common/wmma_backend.h` now preserves the gfx11 call and maps d4r's
16-half operand / even-odd accumulator contract to gfx12's 8-half operand /
contiguous accumulator contract. The gfx12 mapping uses an unconditional
wave32 half-exchange for every source fragment element before selection.
Performing the exchange only on lanes selected by a group-dependent branch
returned channel 9 in place of channel 1 on this card: the opposite half of
the wave was inactive at that instruction. This was caught by the CPU reference.
`tools/windows/wmma_gfx1201.hip` tests raw gfx12 WMMA, the adapter, the
half-exchange and lane ID with nonzero accumulator and one/two K16 steps.
Both stable HIP 7.2 and TheRock 10.2 produced exact 16x16 outputs across 16
different inputs; max absolute and relative errors were both zero.
`kernels/k/pwin_common.h` now calls the tested adapter. The Windows device-only
compiler uses `kernels/common/hip_device_minimal.h`, exposing only public
Clang AMDGPU work-item/grid/barrier builtins and HIP-compatible qualifiers;
the ordinary Linux HIP include path remains. CMake builds `enc1` for gfx1201
into `experimental/k/` and verifies that HIP loads both prep and transformer
entry points. This is a **compile/load smoke test**, not K layer correctness or
DLSS execution. The code object must stay out of ZLUDA's native override path
until numerical K replay validation passes.
## External interop blockers ## External interop blockers
The installed HIP headers define D3D12Resource/D3D12Heap external memory and The installed HIP headers define D3D12Resource/D3D12Heap external memory and
@@ -223,13 +245,13 @@ This builds into `build/windows-rdna4-therock` and installs into
`dist/windows-rdna4-therock`. The stable profile stays available for reproducing `dist/windows-rdna4-therock`. The stable profile stays available for reproducing
the runtime bug. It is expected to return failure at the lifetime gate. the runtime bug. It is expected to return failure at the lifetime gate.
One command adds the local NGX initialization gate after the five hardware gates: One command adds the local NGX initialization gate after the seven hardware gates:
```powershell ```powershell
powershell -NoProfile -File scripts/windows/test-windows-rdna4.ps1 -RuntimeProfile therock -NgxCore "C:/Users/Administrator/d4r/_nvngx.dll" -DlssDll "C:/Users/Administrator/d4r/nvngx_dlss.dll" powershell -NoProfile -File scripts/windows/test-windows-rdna4.ps1 -RuntimeProfile therock -NgxCore "C:/Users/Administrator/d4r/_nvngx.dll" -DlssDll "C:/Users/Administrator/d4r/nvngx_dlss.dll"
``` ```
Success requires all six tests and `PASS NGX_INIT ... sr_available=1`; Success requires all eight tests and `PASS NGX_INIT ... sr_available=1`;
`transformer_executed=0` is explicit. On NGX failure, CUDA trace and NVAPI query `transformer_executed=0` is explicit. On NGX failure, CUDA trace and NVAPI query
logs are collected automatically. NVIDIA DLLs remain local test inputs. logs are collected automatically. NVIDIA DLLs remain local test inputs.
@@ -295,6 +317,18 @@ MSVC parameter roundtrip Width=640, checked parameter/context/NGX cleanup.
Local logs: `test-results/ngx-init-private-runtime.zip`. This is only the first Local logs: `test-results/ngx-init-private-runtime.zip`. This is only the first
part of M3; feature execution, K/M correctness and the Windows shim remain open. part of M3; feature execution, K/M correctness and the Windows shim remain open.
2026-09-29 M5: TheRock full diagnostic run including user-provided NGX DLLs:
8/8 gates PASS (`test-results/m5-k-module.zip`). CTest: 7/7 PASS. Stable HIP 7.2:
5/5 non-interop gates PASS (HIP, gfx12 WMMA, K module load, both CUDA contexts);
stable mapped-view lifetime bug still excludes its interop fast path.
WMMA raw and legacy-layout adapter each match the scalar reference exactly
over 16 changing input matrices, with both one-step and two-step accumulation.
The `enc1` K module is built and loaded on gfx1201, with functions resolved;
`transformer_executed=0` is explicit in the module probe. Neither K nor M
transformer correctness is claimed yet. The same K source also compiles for
`gfx1101` through the unchanged gfx11 builtin branch (compile-only regression;
no RDNA3 GPU is present in this Windows machine).
Audit found an upstream validation blocker: `swin_model.py` imports Audit found an upstream validation blocker: `swin_model.py` imports
`model_enc3`, which is absent from the repository. M reference validation needs `model_enc3`, which is absent from the repository. M reference validation needs
that dependency restored or an independently checked replacement. that dependency restored or an independently checked replacement.
@@ -304,4 +338,4 @@ launching the MinGW child compiler, so branch/commit/build require scoped tool
approval in this managed environment. HIP_PATH ends in a backslash; build script approval in this managed environment. HIP_PATH ends in a backslash; build script
normalizes it before PowerShell 5 argument quoting. Test runner uses .NET Process normalizes it before PowerShell 5 argument quoting. Test runner uses .NET Process
to retain reliable exit codes and concurrently drain both diagnostic streams. to retain reliable exit codes and concurrently drain both diagnostic streams.
No K/M/DLSS/OptiScaler support is claimed by these diagnostic milestones. No end-to-end K/M/DLSS/OptiScaler support is claimed by these diagnostic milestones.
+32
View File
@@ -0,0 +1,32 @@
#pragma once
// Device-only Windows HIP build shim. The diagnostic/kernel code objects are
// compiled with -nogpuinc/-nogpulib; no host HIP or C++ standard library is
// needed. Definitions below mirror public HIP qualifiers and LLVM AMDGPU
// work-item/grid builtins. The normal Linux HIP include path is untouched.
#define __device__ __attribute__((device))
#define __global__ __attribute__((global))
#define __shared__ __attribute__((shared))
#define __forceinline__ __attribute__((always_inline)) inline
#define __launch_bounds__(n) __attribute__((amdgpu_flat_work_group_size(1, n)))
struct d4r_device_dim3 { unsigned x, y, z; };
__device__ __forceinline__ d4r_device_dim3 d4r_thread_idx() {
return {__builtin_amdgcn_workitem_id_x(), __builtin_amdgcn_workitem_id_y(),
__builtin_amdgcn_workitem_id_z()};
}
__device__ __forceinline__ d4r_device_dim3 d4r_block_idx() {
return {__builtin_amdgcn_workgroup_id_x(), __builtin_amdgcn_workgroup_id_y(),
__builtin_amdgcn_workgroup_id_z()};
}
__device__ __forceinline__ d4r_device_dim3 d4r_grid_dim() {
return {__builtin_amdgcn_grid_size_x() / __builtin_amdgcn_workgroup_size_x(),
__builtin_amdgcn_grid_size_y() / __builtin_amdgcn_workgroup_size_y(),
__builtin_amdgcn_grid_size_z() / __builtin_amdgcn_workgroup_size_z()};
}
#define threadIdx d4r_thread_idx()
#define blockIdx d4r_block_idx()
#define gridDim d4r_grid_dim()
__device__ __attribute__((convergent, always_inline)) inline void __syncthreads() {
__builtin_amdgcn_s_barrier();
}
+57
View File
@@ -0,0 +1,57 @@
#pragma once
#include <stdint.h>
// d4r's gfx11 interface: each lane supplies 16 FP16 K operands. The 8 FP32
// outputs have logical row 2*i + lane/16 and logical column lane%16.
// gfx12 consumes only 8 FP16 operands per lane and emits 8 consecutive rows
// per lane group. Keep the old interface for K/M/texture call sites until their
// surrounding fragments are ported and validated individually.
using d4r_wmma_h16 = _Float16 __attribute__((ext_vector_type(16)));
using d4r_wmma_h8 = _Float16 __attribute__((ext_vector_type(8)));
using d4r_wmma_f8 = float __attribute__((ext_vector_type(8)));
__attribute__((device, always_inline)) static inline float d4r_wmma_other_half(float value)
{
const uint32_t bits = __builtin_bit_cast(uint32_t, value);
const uint32_t swapped = __builtin_amdgcn_permlanex16(
bits, bits, 0x76543210u, 0xfedcba98u, false, false);
return __builtin_bit_cast(float, swapped);
}
__attribute__((device, always_inline)) static inline d4r_wmma_f8
d4r_wmma_legacy_layout(d4r_wmma_h16 a, d4r_wmma_h16 b, d4r_wmma_f8 c)
{
#if defined(__gfx1200__) || defined(__gfx1201__)
const uint32_t group = __builtin_amdgcn_mbcnt_lo(~0u, 0u) >> 4;
d4r_wmma_h8 a12, b12;
d4r_wmma_f8 c12;
d4r_wmma_f8 remote_c;
#pragma unroll
for (int i = 0; i < 8; ++i)
remote_c[i] = d4r_wmma_other_half(c[i]);
#pragma unroll
for (int i = 0; i < 8; ++i) {
a12[i] = a[8u * group + i];
b12[i] = b[8u * group + i];
const uint32_t row = 8u * group + i;
const uint32_t old_index = row >> 1;
c12[i] = (row & 1u) == group ? c[old_index] : remote_c[old_index];
}
const d4r_wmma_f8 d12 = __builtin_amdgcn_wmma_f32_16x16x16_f16_w32_gfx12(a12, b12, c12);
d4r_wmma_f8 result, remote_d;
#pragma unroll
for (int i = 0; i < 8; ++i)
remote_d[i] = d4r_wmma_other_half(d12[i]);
#pragma unroll
for (int i = 0; i < 8; ++i) {
const uint32_t row = 2u * i + group;
const uint32_t new_index = row & 7u;
result[i] = (row >> 3) == group ? d12[new_index] : remote_d[new_index];
}
return result;
#elif defined(__gfx1100__) || defined(__gfx1101__) || defined(__gfx1102__) || defined(__gfx1103__)
return __builtin_amdgcn_wmma_f32_16x16x16_f16_w32(a, b, c);
#else
#error "d4r WMMA backend requires a validated gfx11 or gfx12 target"
#endif
}
+6 -1
View File
@@ -6,7 +6,11 @@
// and the activations as the B operand (lane l: token l & 15, 16 K values). The f32 result then has // and the activations as the B operand (lane l: token l & 15, 16 K values). The f32 result then has
// lane l = token l & 15 and VGPR i = output channel 2i + (l >> 4) of the 16-channel tile ("D^T"). // lane l = token l & 15 and VGPR i = output channel 2i + (l >> 4) of the 16-channel tile ("D^T").
#pragma once #pragma once
#ifdef D4R_DEVICE_ONLY_MINIMAL
#include "../common/hip_device_minimal.h"
#else
#include <hip/hip_runtime.h> #include <hip/hip_runtime.h>
#endif
#include <stdint.h> #include <stdint.h>
#pragma clang fp contract(off) #pragma clang fp contract(off)
@@ -17,6 +21,7 @@ typedef _Float16 h16 __attribute__((ext_vector_type(16)));
typedef float f8v __attribute__((ext_vector_type(8))); typedef float f8v __attribute__((ext_vector_type(8)));
typedef uint32_t u8v __attribute__((ext_vector_type(8))); typedef uint32_t u8v __attribute__((ext_vector_type(8)));
typedef uint32_t u4v __attribute__((ext_vector_type(4))); typedef uint32_t u4v __attribute__((ext_vector_type(4)));
#include "../common/wmma_backend.h"
__device__ __forceinline__ uint32_t lane_id() __device__ __forceinline__ uint32_t lane_id()
{ {
@@ -25,7 +30,7 @@ __device__ __forceinline__ uint32_t lane_id()
__device__ __forceinline__ f8v wmma(u8v a, u8v b, f8v c) __device__ __forceinline__ f8v wmma(u8v a, u8v b, f8v c)
{ {
return __builtin_amdgcn_wmma_f32_16x16x16_f16_w32(__builtin_bit_cast(h16, a), __builtin_bit_cast(h16, b), c); return d4r_wmma_legacy_layout(__builtin_bit_cast(h16, a), __builtin_bit_cast(h16, b), c);
} }
// one k16 step with the f16 accumulator of NVIDIA's f16 wmma (rounded after the step) // one k16 step with the f16 accumulator of NVIDIA's f16 wmma (rounded after the step)
+9 -1
View File
@@ -94,7 +94,9 @@ try {
$comgr = Join-Path $HipRoot 'bin/amd_comgr_3.dll' $comgr = Join-Path $HipRoot 'bin/amd_comgr_3.dll'
if (!(Test-Path -LiteralPath $comgr)) { $comgr = Join-Path $HipRoot 'bin/amd_comgr.dll' } if (!(Test-Path -LiteralPath $comgr)) { $comgr = Join-Path $HipRoot 'bin/amd_comgr.dll' }
$files = @((Join-Path $HipRoot 'bin/amdhip64_7.dll'), $comgr, $files = @((Join-Path $HipRoot 'bin/amdhip64_7.dll'), $comgr,
(Join-Path $ZludaRoot 'nvcuda.dll'), (Join-Path $PackageRoot 'bin/probe_gfx1201.hsaco')) (Join-Path $ZludaRoot 'nvcuda.dll'), (Join-Path $PackageRoot 'bin/probe_gfx1201.hsaco'),
(Join-Path $PackageRoot 'bin/wmma_gfx1201.hsaco'),
(Join-Path $PackageRoot 'experimental/k/dltss_pwin_enc1_layer_gfx1201.hsaco'))
if ($NgxCore -or $DlssDll) { if ($NgxCore -or $DlssDll) {
if (!$NgxCore -or !$DlssDll) { throw 'Supply both -NgxCore and -DlssDll absolute paths.' } if (!$NgxCore -or !$DlssDll) { throw 'Supply both -NgxCore and -DlssDll absolute paths.' }
if (![IO.Path]::IsPathRooted($NgxCore) -or ![IO.Path]::IsPathRooted($DlssDll)) { if (![IO.Path]::IsPathRooted($NgxCore) -or ![IO.Path]::IsPathRooted($DlssDll)) {
@@ -117,6 +119,12 @@ try {
$hipOk = Invoke-Probe 'hip' (Join-Path $bin 'd4r_hip_gfx1201_probe.exe') @( $hipOk = Invoke-Probe 'hip' (Join-Path $bin 'd4r_hip_gfx1201_probe.exe') @(
'--hip-root', $HipRoot, '--module', (Join-Path $bin 'probe_gfx1201.hsaco'), '--iterations', "$Iterations") '--hip-root', $HipRoot, '--module', (Join-Path $bin 'probe_gfx1201.hsaco'), '--iterations', "$Iterations")
if ($hipOk) { if ($hipOk) {
$wmmaOk = Invoke-Probe 'gfx12-wmma' (Join-Path $bin 'd4r_gfx12_wmma_probe.exe') @(
'--hip-root', $HipRoot, '--module', (Join-Path $bin 'wmma_gfx1201.hsaco'), '--iterations', "$Iterations")
if (!$wmmaOk) { throw 'gfx12 WMMA layout validation failed; refusing to mark K/M readiness.' }
$kModuleOk = Invoke-Probe 'k-enc1-module' (Join-Path $bin 'd4r_k_module_probe.exe') @(
'--hip-root', $HipRoot, '--module', (Join-Path $PackageRoot 'experimental/k/dltss_pwin_enc1_layer_gfx1201.hsaco'))
if (!$kModuleOk) { throw 'K enc1 module cannot load on gfx1201; transformer execution remains untested.' }
$cudaOk = $true $cudaOk = $true
foreach ($context in @('primary', 'created')) { foreach ($context in @('primary', 'created')) {
$ok = Invoke-Probe "cuda-$context" (Join-Path $bin 'd4r_cuda_driver_probe.exe') @( $ok = Invoke-Probe "cuda-$context" (Join-Path $bin 'd4r_cuda_driver_probe.exe') @(
+127
View File
@@ -0,0 +1,127 @@
#include "hip_api.h"
#include <array>
#include <cmath>
#include <limits>
namespace {
uint16_t half_integer(int value)
{
const unsigned n = static_cast<unsigned>(std::abs(value));
if (n > 3u) throw std::runtime_error("test half integer out of range");
const uint16_t bits[] = {0u, 0x3c00u, 0x4000u, 0x4200u};
return static_cast<uint16_t>(bits[n] | (value < 0 ? 0x8000u : 0u));
}
struct Allocation {
d4r::diag::HipApi& hip;
void* pointer = nullptr;
Allocation(d4r::diag::HipApi& api, size_t bytes) : hip(api) {
hip.check(hip.hipMalloc(&pointer, bytes), "hipMalloc(WMMA)");
}
~Allocation() { if (pointer) (void)hip.hipFree(pointer); }
};
}
int main(int argc, char** argv)
{
using namespace d4r::diag;
start();
try {
Args args(argc, argv);
if (args.module.empty()) throw std::runtime_error("--module is required");
HipApi hip(args.hip_root);
hipDeviceProp_t properties{};
hip.select_gfx1201(args.device, properties);
hipModule_t module = nullptr;
hip.check(hip.hipModuleLoad(&module, args.module.c_str()), "hipModuleLoad(WMMA)");
struct ModuleCleanup {
HipApi& hip; hipModule_t module;
~ModuleCleanup() { if (module) (void)hip.hipModuleUnload(module); }
} cleanup{hip, module};
hipFunction_t raw_kernel = nullptr, adapter_kernel = nullptr, swap_kernel = nullptr;
hip.check(hip.hipModuleGetFunction(&raw_kernel, module, "d4r_wmma_gfx1201"),
"hipModuleGetFunction(WMMA)");
hip.check(hip.hipModuleGetFunction(&adapter_kernel, module, "d4r_wmma_legacy_contract"),
"hipModuleGetFunction(WMMA adapter)");
hip.check(hip.hipModuleGetFunction(&swap_kernel, module, "d4r_wmma_swap_test"),
"hipModuleGetFunction(WMMA swap)");
constexpr size_t elements = 16u * 16u;
std::array<uint16_t, elements> a{}, b{};
std::array<float, elements> initial{}, output{};
Allocation da(hip, sizeof(a)), db(hip, sizeof(b));
Allocation dc(hip, sizeof(initial)), dout(hip, sizeof(output));
void* swap_out = dout.pointer;
void* swap_params[] = {&swap_out};
hip.check(hip.hipModuleLaunchKernel(swap_kernel, 1, 1, 1, 32, 1, 1, 0,
nullptr, swap_params, nullptr), "hipModuleLaunchKernel(WMMA swap)");
hip.check(hip.hipDeviceSynchronize(), "hipDeviceSynchronize(WMMA swap)");
hip.check(hip.hipMemcpy(output.data(), dout.pointer, sizeof(output), hipMemcpyDeviceToHost), "hipMemcpy(WMMA swap)");
for (unsigned lane = 0; lane < 32u; ++lane) {
if (output[lane] != static_cast<float>(lane ^ 16u)) {
std::fprintf(stderr, "FAIL WMMA swap lane=%u expected=%u actual=%g\n",
lane, lane ^ 16u, output[lane]);
return 5;
}
if (output[32u + lane] != static_cast<float>(lane)) {
std::fprintf(stderr, "FAIL WMMA lane-id lane=%u mbcnt=%g\n", lane, output[32u + lane]);
return 5;
}
}
float max_absolute_error = 0.0f;
float max_relative_error = 0.0f;
for (unsigned iteration = 0; iteration < args.iterations; ++iteration) {
const unsigned steps = 1u + (iteration & 1u);
for (unsigned row = 0; row < 16u; ++row) {
for (unsigned k = 0; k < 16u; ++k) {
a[row * 16u + k] = half_integer(static_cast<int>((row * 3u + k * 5u + iteration) % 7u) - 3);
b[row * 16u + k] = half_integer(static_cast<int>((row * 5u + k * 2u + iteration * 3u) % 7u) - 3);
initial[row * 16u + k] = static_cast<float>(static_cast<int>((row * 11u + k * 3u + iteration) % 17u) - 8) * 0.125f;
}
}
hip.check(hip.hipMemcpy(da.pointer, a.data(), sizeof(a), hipMemcpyHostToDevice), "hipMemcpy(A)");
hip.check(hip.hipMemcpy(db.pointer, b.data(), sizeof(b), hipMemcpyHostToDevice), "hipMemcpy(B)");
hip.check(hip.hipMemcpy(dc.pointer, initial.data(), sizeof(initial), hipMemcpyHostToDevice), "hipMemcpy(C)");
void* a_arg = da.pointer;
void* b_arg = db.pointer;
void* c_arg = dc.pointer;
void* output_arg = dout.pointer;
unsigned steps_arg = steps;
void* params[] = {&a_arg, &b_arg, &c_arg, &output_arg, &steps_arg};
for (unsigned mode = 0; mode < 2u; ++mode) {
output.fill(std::numeric_limits<float>::quiet_NaN());
hip.check(hip.hipMemcpy(dout.pointer, output.data(), sizeof(output), hipMemcpyHostToDevice), "hipMemcpy(output sentinel)");
hip.check(hip.hipModuleLaunchKernel(mode ? adapter_kernel : raw_kernel, 1, 1, 1, 32, 1, 1, 0, nullptr,
params, nullptr), mode ? "hipModuleLaunchKernel(WMMA adapter)" : "hipModuleLaunchKernel(WMMA raw)");
hip.check(hip.hipDeviceSynchronize(), "hipDeviceSynchronize(WMMA)");
hip.check(hip.hipMemcpy(output.data(), dout.pointer, sizeof(output), hipMemcpyDeviceToHost), "hipMemcpy(output)");
for (unsigned row = 0; row < 16u; ++row) {
for (unsigned col = 0; col < 16u; ++col) {
int dot = 0;
for (unsigned k = 0; k < 16u; ++k) {
const int av = static_cast<int>((row * 3u + k * 5u + iteration) % 7u) - 3;
const int bv = static_cast<int>((col * 5u + k * 2u + iteration * 3u) % 7u) - 3;
dot += av * bv;
}
const float expected = initial[row * 16u + col] + static_cast<float>(static_cast<int>(steps) * dot);
const float got = output[row * 16u + col];
const float absolute = std::fabs(got - expected);
const float relative = absolute / std::max(1.0f, std::fabs(expected));
if (!std::isfinite(got) || absolute > 0.0001f) {
std::fprintf(stderr, "FAIL WMMA mode=%s iteration=%u row=%u col=%u steps=%u expected=%.9g actual=%.9g abs=%.9g\n",
mode ? "adapter" : "raw", iteration, row, col, steps, expected, got, absolute);
return 5;
}
max_absolute_error = std::max(max_absolute_error, absolute);
max_relative_error = std::max(max_relative_error, relative);
}
}
}
}
std::printf("PASS GFX12_WMMA architecture=gfx1201 modes=raw,legacy_adapter iterations=%u max_abs=%.9g max_rel=%.9g\n",
args.iterations, max_absolute_error, max_relative_error);
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "FAIL GFX12_WMMA %s\n", error.what());
loaded_modules();
return 4;
}
}
+31
View File
@@ -0,0 +1,31 @@
#include "hip_api.h"
int main(int argc, char** argv)
{
using namespace d4r::diag;
start();
try {
Args args(argc, argv);
if (args.module.empty()) throw std::runtime_error("--module is required");
HipApi hip(args.hip_root);
hipDeviceProp_t properties{};
hip.select_gfx1201(args.device, properties);
hipModule_t module = nullptr;
hip.check(hip.hipModuleLoad(&module, args.module.c_str()), "hipModuleLoad(K enc1)");
struct Cleanup {
HipApi& hip; hipModule_t module;
~Cleanup() { if (module) (void)hip.hipModuleUnload(module); }
} cleanup{hip, module};
for (const char* name : {"dltss_pwin_enc1_layer_prep", "dltss_pwin_enc1_layer"}) {
hipFunction_t function = nullptr;
hip.check(hip.hipModuleGetFunction(&function, module, name), name);
if (!function) throw std::runtime_error(std::string("Null K function: ") + name);
}
std::printf("PASS K_MODULE architecture=gfx1201 functions=prep,transformer transformer_executed=0\n");
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "FAIL K_MODULE %s\n", error.what());
loaded_modules();
return 4;
}
}
+67
View File
@@ -0,0 +1,67 @@
// Independent gfx12 WMMA layout probe. This deliberately does not use d4r fragments.
// A and B are row-major [logical row/column][K]; the intrinsic receives B,A
// so its output is row=A, column=B, as in AMD's RDNA 4 WMMA guide.
using h8 = _Float16 __attribute__((ext_vector_type(8)));
using f8 = float __attribute__((ext_vector_type(8)));
#include "../../kernels/common/wmma_backend.h"
extern "C" __attribute__((global)) void d4r_wmma_swap_test(float* output)
{
const unsigned int lane = __builtin_amdgcn_workitem_id_x();
if (lane < 32u) {
output[lane] = d4r_wmma_other_half(static_cast<float>(lane));
output[32u + lane] = static_cast<float>(__builtin_amdgcn_mbcnt_lo(~0u, 0u));
}
}
extern "C" __attribute__((global)) void d4r_wmma_gfx1201(
const unsigned short* a_bits, const unsigned short* b_bits,
const float* initial_c, float* output, unsigned int steps)
{
const unsigned int lane = __builtin_amdgcn_workitem_id_x();
if (lane >= 32u) return;
const unsigned int row = lane & 15u;
const unsigned int group = lane >> 4;
const _Float16* a = reinterpret_cast<const _Float16*>(a_bits);
const _Float16* b = reinterpret_cast<const _Float16*>(b_bits);
h8 a_frag, b_frag;
f8 c_frag;
for (unsigned int i = 0; i < 8u; ++i) {
a_frag[i] = a[row * 16u + group * 8u + i];
b_frag[i] = b[row * 16u + group * 8u + i];
c_frag[i] = initial_c[row * 16u + group * 8u + i];
}
f8 result = __builtin_amdgcn_wmma_f32_16x16x16_f16_w32_gfx12(
b_frag, a_frag, c_frag);
if (steps == 2u)
result = __builtin_amdgcn_wmma_f32_16x16x16_f16_w32_gfx12(
b_frag, a_frag, result);
for (unsigned int i = 0; i < 8u; ++i)
output[row * 16u + group * 8u + i] = result[i];
}
// d4r's original gfx11 fragment contract, exercised independently of K/M.
extern "C" __attribute__((global)) void d4r_wmma_legacy_contract(
const unsigned short* a_bits, const unsigned short* b_bits,
const float* initial_c, float* output, unsigned int steps)
{
const unsigned int lane = __builtin_amdgcn_workitem_id_x();
if (lane >= 32u) return;
const unsigned int token = lane & 15u;
const unsigned int group = lane >> 4;
const _Float16* a = reinterpret_cast<const _Float16*>(a_bits);
const _Float16* b = reinterpret_cast<const _Float16*>(b_bits);
d4r_wmma_h16 a_frag, b_frag;
d4r_wmma_f8 c_frag;
for (unsigned int i = 0; i < 16u; ++i) {
a_frag[i] = a[token * 16u + i];
b_frag[i] = b[token * 16u + i];
}
for (unsigned int i = 0; i < 8u; ++i)
c_frag[i] = initial_c[(2u * i + group) * 16u + token];
d4r_wmma_f8 result = d4r_wmma_legacy_layout(a_frag, b_frag, c_frag);
if (steps == 2u)
result = d4r_wmma_legacy_layout(a_frag, b_frag, result);
for (unsigned int i = 0; i < 8u; ++i)
output[(2u * i + group) * 16u + token] = result[i];
}