Repository navigation
Conversation
HIP_ARCH was never used: enable_language(HIP) sets CMAKE_HIP_ARCHITECTURES to the GPU of the build machine before archs.cmake reads it, and hip::device compiled the C++ sources for GPU_TARGETS, also detected. HIP_ARCH now seeds CMAKE_HIP_ARCHITECTURES (an explicit value still wins), GPU_TARGETS follows it, and the default is gfx1200;gfx1201 (Navi 44 and Navi 48 can't load each other's code objects). attention_wave.h adds ATTN_WAVE_SIZE, attnShflXor and attnRequireWave32 for porting the attention DPPs to HIP. ROCm's clang no longer exposes the wave size at compile time, so a wave64 build traps at run time instead of failing the build, unless the compiler still defines __AMDGCN_WAVEFRONT_SIZE__. Nothing uses them yet; utest_attention_wave tests them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014d2rht2WMSopuodhtXYZzx
utest_flash_attention gets testFp8KV (fp8 e4m3 per-token K/V through makeFp8KVRead, oracle on the dequantized values, like testInt8KV). New utest_flash_decode covers executeFlashDecode for every loadRow path (fp32, raw bf16, int8 and fp8 caches, auto and forced splits with empty ones, two calls on one workspace) and new utest_flash_attention_mma covers executeFlashAttentionMma with raw bf16 Q/K/V, including the split-KV forward. The oracle and the Ptr1D buffer helpers move to attention_test_common.h. Writing them found three bugs in flash_decode.h, fixed here: - readElem called attnToF32 without low_precision::, so executeFlashDecode didn't compile with any generic prologue (fp32 reads, fused chains); - the packed int8/fp8 loads only handle 2 or 4 bytes per lane: HEAD_DIM 32 made a misaligned 4-byte load per lane, 256 shifted a 32-bit value by 32+; - the bf16 pair loads need an even count per lane: HEAD_DIM 32 loaded misaligned pairs and wrote past out[]. Those widths now fall back to the per-element read; 64 and 128 are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014d2rht2WMSopuodhtXYZzx
softmax.h, flash_attention.h and flash_decode.h now build their DPPs for HIP too (flash_attention_mma.h stays CUDA-only). The entry points take Stream_<defaultParArch>& (the same type as before under nvcc) and launch on getHIPStream() under HIP. Wave-size 32s and shuffles go through attention_wave.h; tile sizes are unchanged. FP8 is OCP e4m3 on both backends: attn_fp8_e4m3 is __hip_fp8_e4m3 on HIP (not the FNUZ type of gfx942) and __nv_fp8_e4m3 on CUDA, decoded by one attnFp8ToF32 used by Fp8TokenDequantRead and flash decode. attnFp8IsOcpE4M3() checks the format on known bytes. bf16 is attn_bf16. Bf16AttentionRead and the isRawBf16Read/isFp8KVRead/isInt8KVRead traits move from flash_attention_mma.h to flash_attention.h, so decode doesn't need the CUDA-only header. low_precision::attnToF32/attnFromF32 become host and device: the host KV packing helpers call them, which clang rejects for a __device__ function. The attention tests use the Ptr helpers instead of raw cudaMalloc and the new __ONLY_GPU__ marker (cmake/tests/discover_tests.cmake: no _cpp target, _cu and _hip ones), so they also run as _hip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014d2rht2WMSopuodhtXYZzx
…rst configure Follows 1cf881b: the comment in hip_init.cmake and AGENTS.md still said HIP_ARCH defaulted to gfx1200;gfx1201 and was re-applied on reconfigure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014d2rht2WMSopuodhtXYZzx
flash_attention_wmma.h adds FlashAttentionWmmaDPP, the HIP counterpart of FlashAttentionMmaDPP, on the gfx12 wave32 WMMA 16x16x16 instructions. Each wave owns 16 queries and computes S^T = K Q^T and O^T += V^T P^T: the accumulator layout of S^T is the B layout of P^T, so P feeds the second WMMA from registers and the online softmax is lane-local. K/V tiles are staged through the prologue IOps (16-byte loads for raw bf16, 8-byte loads for the int8/fp8 caches), one tile ahead for bf16. An Fp8TokenDequantRead K selects the FP8 QK^T path (e4m3 K bytes, Q quantized per query in-kernel). executeFlashAttentionAuto picks it on gfx12 and FlashAttentionDPP elsewhere. utest_flash_attention_wmma covers bf16/fp32/int8/fp8 prologues, causal and ragged shapes, both tile configurations, the epilogue and the dispatcher; the new __ONLY_HIP__ marker keeps only its _hip target. spikes/rdna4 has the WMMA layout/throughput spike and the kernel benchmark (not built by CMake). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014d2rht2WMSopuodhtXYZzx
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Device dispatch, FP8 indexing, compilation guards and numerical validation still need correction.
6 open issues
Use stored K height for FP8 scale-plane stride · New Cache device capabilities per device, not per template · New Guard unsupported MMA kernels at compile time · New Guard FP8 code when FP8 support is unavailable · New Reject NaN differences in oracle error reduction · New Reject non-finite values in epilogue and Auto comparisons · New
What changed in this PR
Adds RDNA4 WMMA FlashAttention to FKL, alongside the stacked HIP attention port and supporting tests.
Changes:
- Adds bf16 and FP8 WMMA kernels with automatic backend selection.
- Ports scalar attention and decode to HIP and fixes decode loads.
- Expands validation, benchmarks, HIP architecture configuration and test discovery.
| File | Description |
|---|---|
| utests/algorithm/attention/utest_softmax.h | Makes softmax tests GPU-portable. |
| utests/algorithm/attention/utest_flash_decode.h | Adds decode and packed-load coverage. |
| utests/algorithm/attention/utest_flash_attention.h | Adds HIP execution and FP8 checks. |
| utests/algorithm/attention/utest_flash_attention_wmma.h | Tests WMMA precision, epilogues and dispatch. |
| utests/algorithm/attention/utest_flash_attention_mma.h | Adds CUDA MMA validation. |
| utests/algorithm/attention/utest_attention_wave.h | Tests wave-local exchanges. |
| utests/algorithm/attention/attention_test_common.h | Shares oracle and buffer helpers. |
| spikes/rdna4/wmma_gfx12.hip | Validates WMMA mapping and throughput. |
| spikes/rdna4/fa_wmma_bench.hip | Benchmarks WMMA against scalar attention. |
| include/fused_kernel/algorithms/attention/softmax.h | Adds HIP launches and host conversions. |
| include/fused_kernel/algorithms/attention/flash_decode.h | Adds HIP support and load fallbacks. |
| include/fused_kernel/algorithms/attention/flash_attention.h | Shares low-precision readers and enables HIP. |
| include/fused_kernel/algorithms/attention/flash_attention_wmma.h | Implements WMMA attention and dispatch. |
| include/fused_kernel/algorithms/attention/flash_attention_mma.h | Relocates shared readers and traits. |
| include/fused_kernel/algorithms/attention/attention_wave.h | Adds portable wave32 helpers. |
| cmake/tests/discover_tests.cmake | Supports GPU-only and HIP-only markers. |
| cmake/libs/hip/hip.cmake | Aligns HIP target architectures. |
| cmake/libs/hip/hip_init.cmake | Applies architecture overrides before initialization. |
| cmake/libs/hip/archs.cmake | Uses resolved HIP architectures. |
| AGENTS.md | Documents HIP configuration and attention support. |
Review effort: Balanced
| } | ||
| if constexpr (FP8_QK) { | ||
| for (int r = threadIdx.x; r < BLOCK_KV; r += THREADS) { | ||
| kScaleTile[r] = kv0 + r < p.seq_k ? p.k.params.scales[(long)bh * p.seq_k + kv0 + r] : 0.f; |
| Stream_<ParArch::GPU_AMD>& stream, const float scaleOverride = -1.f, | ||
| const EpilogueIOp& epilogue = {}) { | ||
| using DPP = FlashAttentionWmmaDPP<OT, HEAD_DIM, QIOp, KIOp, VIOp, EpilogueIOp, BLOCK_KV, NUM_WAVES>; | ||
| static const bool hasWmma = attnDeviceHasWmmaGfx12(); |
Comment on lines
+73
to
+76
| if (prop.major < 8) { | ||
| std::cout << "SKIPPED (FlashAttentionMmaDPP needs sm_80, this GPU is sm_" << prop.major << prop.minor << ")" | ||
| << std::endl; | ||
| return 0; |
Comment on lines
+106
to
+107
| quantizeKVCacheFp8Host(hk.data(), k8.data(), kSc.data(), (int)nTok, HEAD_DIM); | ||
| quantizeKVCacheFp8Host(hv.data(), v8.data(), vSc.data(), (int)nTok, HEAD_DIM); |
| inline double maxAbsErr(const std::vector<T>& got, const std::vector<double>& ref) { | ||
| double maxErr = 0.0; | ||
| for (size_t i = 0; i < ref.size(); ++i) { | ||
| maxErr = std::max(maxErr, std::abs(static_cast<double>(got[i]) - ref[i])); |
Comment on lines
+170
to
+171
| epiErr = std::max(epiErr, std::abs((double)g2[i] - ((double)g1[i] * 2.0 + 0.5))); | ||
| autoErr = std::max(autoErr, std::abs((double)g3[i] - (double)g1[i])); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Phase 4 of the RDNA4 port: FlashAttention on gfx12 WMMA, bf16 and FP8 QK^T. Tested and benchmarked on the RX 9060 (gfx1200, ROCm 10.1).
Stacked on #394, #395 and #396: this branch contains their commits. The phase 4 change is the last commit,
7f1d2ea.1. Spike (
spikes/rdna4/wmma_gfx12.hip, not built by CMake)Builtins:
__builtin_amdgcn_wmma_f32_16x16x16_bf16_w32_gfx12(A/B: 8 × bf16 asshort8, C/D:float8) and__builtin_amdgcn_wmma_f32_16x16x16_fp8_fp8_w32_gfx12(A/B: 8 e4m3 bytes asint2), the operand types rocWMMA uses for gfx1200/1201.Lane mapping, validated against a CPU matmul (bf16 max error 2.4e-7, fp8 exact). Lane
lholds:l%16, k =8*(l/16) .. +7;l%16, the same k split;l%16, rows8*(l/16) .. +7.This differs from gfx11 and from NVIDIA's m16n8k16.
Raw WMMA throughput, all waves busy, median of 20 runs: bf16 ≈ 40 TFLOPS, fp8 ≈ 82 TFLOPS. The number of independent accumulator chains (1 to 16) makes little difference. HIP reports 14 "CUs" (WGPs); rocminfo shows 28 CUs at 2.4 GHz.
2. Kernel (
attention/flash_attention_wmma.h, HIP only)FlashAttentionWmmaDPP, the counterpart ofFlashAttentionMmaDPP: FA-2 forward with online softmax in fp32 registers.xor 16exchange each.FlashAttentionDPP: one 16-byte load for raw bf16, an 8-byte load plus dequantization for int8/fp8 caches, and an element-wise read for any other prologue or fused chain. Q goes through its IOp once per block. The epilogue chain runs in registers before the single write.__launch_bounds__removed the spills (up to 142 VGPRs) the 192-VGPR default cap caused at d128.Fp8TokenDequantReadK selects it, mirroring the CUDA FP8_QK design. The K tile holds the raw e4m3 cache bytes. Q is quantized per query in-kernel (scale = max|q| / 448), and the scores are rescaled by kScale·qScale. V and P stay bf16.executeFlashAttentionWmma<HEAD_DIM, BLOCK_KV, NUM_WAVES>(q, k, v, o, bh, seqQ, seqK, causal, stream, scale, epilogue)throws on non-gfx12 devices.executeFlashAttentionAuto<HEAD_DIM>(...)picks the WMMA kernel on gfx12 andFlashAttentionDPPelsewhere. Note that the WMMA path rounds Q, K, V and P to bf16, while the scalar kernel stays fp32 throughout.Tests (
utest_flash_attention_wmma.h, new__ONLY_HIP__marker:_hiponly)On non-gfx12 GPUs the test prints
SKIPPED. It has 17 cases against the double-precision oracle, with HEAD_DIM 32/64/128, dense/causal, ragged and cross-attention shapes, and both tile configs. Max errors on the RX 9060:WMMA_BF16_TOL1e-2WMMA_BF16_TOL1e-2WMMA_BF16_TOL1e-2WMMA_FP8_TOL3e-2executeFlashAttentionAutovsexecuteFlashAttentionWmmaThe tolerances are documented in the test. For bf16: P has 8 significant bits and outputs are convex combinations of V in [-1, 1], so the error stays below 2⁻⁸ = 3.9e-3, plus input rounding. For fp8, Q has 3 mantissa bits.
Mutation check: reading the wrong half of the Vᵀ fragment fails 12/14 cases.
Throughput, kernel vs scalar
FlashAttentionDPP(TFLOPS = 4·bh·s²·d, halved for causal; bf16 inputs for both; median of 20;spikes/rdna4/fa_wmma_bench.hip)Verified on this machine
utest_flash_attention_wmma_hip. Same unique warnings._cpptests pass with the same list. The new header is__HIPCC__-only, so no CUDA-visible code changed apart from the test-discovery cmake.🤖 Generated with Claude Code
https://claude.ai/code/session_014d2rht2WMSopuodhtXYZzx