Repository navigation
feat(rocke): async K loop, cshuffle, fp16 split-K and two-stage split-K for gfx1250 WMMA wgrad - #14832
Conversation
…MA wgrad The WMMA wgrad K loop was single-buffered with no overlap: load, barrier, WMMA, barrier. On gfx1250, async_dma now runs a one-barrier ping-pong over global_load_async_to_lds (CoalescedTileLoader.load_async_to_lds): - it reuses the synchronous K-outer loaders for the slot partition and keeps the K-outer row pad of 8, since the copy writes a per-lane LDS address (the gfx9 buffer_load_lds path keeps its pad of 0); - each phase waits s_wait_asynccnt 0 and takes one LDS barrier, then issues the next tile while the current tile's WMMAs run; - the copy has no buffer-resource range check, which the wgrad descriptors rely on for the reduction tail and the free-axis overhang, so the loader ANDs byte_offset < num_bytes into each chunk's valid predicate and zero-fills failing chunks with an LDS store; - it needs lds_k_outer and a 4-, 8- or 16-byte chunk on both operands. Admit epilogue="cshuffle" on WMMA wgrad. Both wgrad cshuffle epilogues (direct store and split-K atomic_store) read the WMMA fragment through CShuffleEpilogue.from_grid_op, the path the forward and dgrad WMMA epilogues already use, so a 16-bit split-K partial is written as genuine <2 x dtype> pairs instead of one zero-partner packed atomic per element. Like MFMA's, that LDS pairing needs an even store vector, which both engines' validators now require for WMMA cshuffle split-K (the default WMMA epilogue still ignores it). pipeline stays "mem", and unroll_k / prefetch_addr / chiplet_swizzle stay rejected on WMMA, as before. Both engines are mirrored (rocke_conv_emit_kloop_async_global_lds and rocke_coalesced_tile_loader_load_async_to_lds in C++). Parity configs 55-62 cover the async loop (fp32 dW split-K, partial N tiles, 2x2 warps), the C=3 async reject, and the cshuffle epilogue (direct fp16, split-K bf16, with async at C=24, the gfx1201 reject, and the odd store-vector reject). Config 53 (gfx1250 bf16 split-K with cshuffle) is now admitted by both engines. Two new representative-IR cases (async, cshuffle) are blessed; no existing hash changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The legacy llvm.amdgcn.global.atomic.fadd.v2f16 intrinsic is lowered as a fenced atomic: a global_wb / global_inv pair (buffer_wbl2 / buffer_inv on gfx950) and full counter drains around every single add, which serialised the split-K epilogues. Emit the generic agent-scope monotonic atomicrmw fadd <2 x half> with the device-local memory-model metadata, as the packed bf16 add already does; the backend selects the same global_atomic_pk_add_f16 without the fences. Mirrored in the C++ engine (lower_llvm/mem.cpp). The intrinsic declaration has no other user, so it is dropped from both engines' declaration tables and from the intrinsic arch-domain columns. The HIP lowering keeps __builtin_amdgcn_global_atomic_fadd_v2f16, which clang already emits unfenced. Every kernel with a packed fp16 atomic changes its IR: the fp16 split-K wgrad configs and the fp16 atomic dgrad epilogue configs. The parity golden and the representative-IR golden are re-blessed for exactly those keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Give gfx1250 fp16 dW its own wgrad split policy: fill to 1024 CTAs (2048 grouped) with K slices down to 512, up to degree 128. bf16 keeps its accuracy-bound policy and fp32 the general one. This reverses the "fp16 cap 1" of the gfx1250 split-K policy. That cap was a symptom of the fenced packed fp16 atomic: with the atomic lowered as a monotonic atomicrmw (previous commit), deeper fp16 splits pay off. The policy was fitted on a degree sweep of the cached split-K binaries, and no fp16 degree failed the 5e-2 verification there. Python only: the C++ engine has no split-policy counterpart (the degree is a kernarg resolved by the host). Update the policy tests and the gfx1250 GPU split-K resolver test, whose fp16 cases now run at the resolved degree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…k widths An async-DMA binary records no A / B load width (the copy picks its chunk), so the widest-vector filter of the from-cache sweep added None and aborted. Rank such an entry by the chunk widths recorded in its identity (async_chunk_a / async_chunk_b), as a synchronous kernel with the same load widths; fall back to 8 only when none is recorded. Ranking every async binary as 8 wide let it outrank, and so filter out, the widest synchronous kernels of a problem whose channel run caps the loads below 8. Benchmark / run side only; no kernel IR change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two-stage scratch-store epilogue decoded the accumulator only through the MFMA atom, so wave32 had no two-stage path: a 16-bit dW with an odd row (a 3-channel stem) could not be split at all, and bf16 split-K stopped at the packed atomic's accuracy cap. Decode the WMMA C fragment through op.c_layout() in the scratch epilogue, as the WMMA atomic epilogue does, and enable two-stage on wave32 targets whose catalog marks float global atomics as validated (gfx1250; the f32 scratch adds are global float atomics). The workspace reduction accumulates in fp32, so the split policy resolves its degree with the fp32 cap. From the cache on wave32, bf16 also runs the two-stage kernels by default (--two-stage auto), next to the packed-atomic ones. The MFMA path's IR is unchanged. Mirrored in the C++ engine (availability gate and the WMMA scratch epilogue). Parity configs 63-66: fp16 dW with an odd dW row (C=3), bf16 dW on 64x64x64 2x2 warps K-outer, fp16 dW over the async K loop with one scratch slab, and the gfx1151 reject. One new representative-IR case. CPU tests cover the availability predicate and the gfx1151 reject; a new gfx1250 GPU test runs two-stage on an odd-dW-row stem and a 2x2 stride-2 downsample, fp16 / bf16, plain, K-outer and async K loops. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… async variants On wave32 (WMMA) the implicit-GEMM wgrad cache keeps every kernel it built before: the synchronous default-epilogue kernels, unsplit and packed-atomic split-K, on the whole tile ladder. On top of that, on 19 (tile_m, tile_n, tile_k, warp_m, warp_n) geometries (CACHE_WGRAD_WMMA_GEOMETRIES), it adds: - the cshuffle epilogue, unsplit and packed-atomic split-K; - two-stage (f32 workspace, ws_replicas = 8) split-K on the default epilogue, which the two-stage availability on gfx1250 brings in; - the async_dma K loop (global_load_async_to_lds) next to the plain loop for every non-two-stage configuration, built once per geometry with A / B widths left to the copy and its chunk widths recorded in the identity (async_chunk_a / async_chunk_b). Each cover geometry was the fastest of at least one reference wgrad layer, and together they hold the fastest measured time of every layer. count_jobs follows the same rule (and now counts the async loop), and a CPU test pins it: the default grid is unchanged and the new variants stay on the cover. The gfx950 grid is unchanged. The gfx1250 GPU split-K resolver test takes its 2x2-warp tiles from the cover and only the synchronous packed-atomic binaries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
✅ All Checks Passed — Ready for Review
📖 Need help? See the Policy FAQ for details on every check and how to fix failures. |
|
🎉 All checks passed! This PR is ready for review. |
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical defects can cause out-of-bounds async copies near the 2 GiB boundary and fp16 accumulation errors exceeding the accuracy tolerance.
5 open issues
What changed in this PR
This PR extends rocKE’s AOT-cache-served gfx1250 WMMA weight-gradient kernels with asynchronous loading, cshuffle epilogues, and two-stage split-K, with corresponding C++ implementations.
Changes:
- Adds async global-to-LDS ping-pong loading and WMMA fragment handling for cshuffle and workspace epilogues.
- Updates packed fp16 atomic lowering, automatic split-K policies, cache generation, and benchmark selection.
- Expands validation, numerical tests, parity cases, IR goldens, and documentation.
Paths are relative to dnn-providers/hip-kernel-provider/rocke/.
| File | Description |
|---|---|
platform/tests/instances/rocke_ir_parity_harness.py |
Adds representative async, cshuffle, and two-stage cases. |
platform/tests/instances/differential/golden/llvm_gfx_all.json |
Updates differential IR hashes. |
platform/tests/golden/rocke_representative_ir_sha256/conv_wgrad.json |
Records new variants and fp16 atomic IR changes. |
platform/tests/golden/rocke_representative_ir_sha256/conv_wgrad_two_stage.json |
Adds gfx1250 workspace-epilogue goldens. |
platform/tests/dispatch/dispatch_tests/gemm/test_split_k_wgrad.py |
Tests the revised fp16 split policy. |
platform/python/rocke/instances/SUPPORT_MATRIX.md |
Documents expanded WMMA wgrad support. |
platform/python/rocke/helpers/split_k.py |
Introduces a gfx1250 fp16-specific policy. |
platform/python/rocke/helpers/loads.py |
Adds asynchronous global-to-LDS tile loading. |
platform/python/rocke/core/lower_llvm.py |
Lowers packed fp16 adds through atomicrmw. |
platform/python/rocke/core/ir.py |
Updates packed fp16 atomic documentation. |
platform/python/rocke/core/arch/data/intrinsic_arch_domain.llvm24.json |
Removes the unused legacy atomic intrinsic entry. |
platform/python/rocke/core/arch/data/intrinsic_arch_domain.llvm23.json |
Removes the unused legacy atomic intrinsic entry. |
platform/python/rocke/core/arch/data/intrinsic_arch_domain.llvm22.json |
Removes the unused legacy atomic intrinsic entry. |
platform/python/rocke/core/arch/data/intrinsic_arch_domain.llvm20.json |
Removes the unused legacy atomic intrinsic entry. |
platform/dsl_docs/instances/convolution.md |
Explains WMMA loading, padding, and epilogues. |
platform/cpp/instances/common/conv_implicit_gemm_wgrad.cpp |
Implements C++ WMMA validation and epilogue changes. |
platform/cpp/instances/common/conv_implicit_gemm_conv_load_phase.cpp |
Routes async copies through the new loader. |
platform/cpp/instances/common/conv_implicit_gemm_conv_kloop_drivers.cpp |
Adds the async ping-pong K-loop driver. |
platform/cpp/include/rocke/instance_conv_implicit_gemm_internal.h |
Declares async context fields and the driver. |
platform/cpp/include/rocke/helper_rocke.helpers.loads.h |
Declares the async tile-loading helper. |
platform/cpp/helpers/loads.cpp |
Implements async copies and zero-fill handling. |
platform/cpp/core/lower_llvm/mem.cpp |
Mirrors packed fp16 atomicrmw lowering. |
platform/cpp/core/lower_llvm/data.cpp |
Removes the legacy atomic declaration. |
library/tests/test_conv_wgrad_correctness.py |
Adds WMMA validation and numerical coverage. |
library/tests/test_conv_split_k.py |
Updates automatic fp16 split expectations. |
library/tests/test_conv_abi.py |
Checks cache geometry coverage and job counts. |
library/tests/parity/conv_implicit_gemm_wgrad_emit.py |
Adds Python emission and rejection cases. |
library/tests/parity/conv_implicit_gemm_wgrad_emit.c |
Adds matching C++ engine parity configurations. |
library/kernels/conv/aot_jobs.py |
Generates new variants on the WMMA geometry cover. |
library/kernels/common/README_conv_implicit_gemm_wgrad.md |
Documents new kernels and split-K behavior. |
library/kernels/common/conv_split_k.py |
Updates reduction eligibility and workspace degree selection. |
library/kernels/common/conv_implicit_gemm_wgrad.py |
Implements Python WMMA async and epilogue support. |
library/benchmarks/common/benchmark_implicit_gemm_conv.py |
Updates workspace selection and async-width ranking. |
Review effort: Balanced
…n the job count count_jobs counted the async_dma wgrad variants on every wave32 target, but _wgrad_jobs drops them on gfx1151 and gfx1201, which have no global_load_async_to_lds. The count then exceeded what the walk can yield. _wgrad_k_loops now takes the target and offers the async loop only where target.memory.has_async_global_lds holds; both the walk and the count use it. Test: count_jobs equals the wgrad walk on gfx1250, gfx1151 and gfx1201, and only gfx1250 yields async jobs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The from-cache sweep keeps only the widest vector copy of each kernel kind. An async-DMA wgrad binary records the chunk widths its copies use, while a synchronous binary records its width caps, which its loader can narrow at build time. On the gfx1250 cover geometry 32x64x32 (2x4 warps) the async loop records A=4 / B=8 and the synchronous copy A=8 / B=8, so with both in one kind every async kernel was dropped before it was benchmarked. The kind now includes async_dma, so each loop is pruned to its own widest copy. Benchmark / run side only; no kernel IR change. Test: on that geometry the async cshuffle kernel survives the pruning and the synchronous kernels are still cut to their widest copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…c's accuracy bound The gfx1250 fp16 wgrad split policy went up to degree 128. Each packed fp16 atomic add rounds the running sum in dW, so valid inputs can miss the 5e-2 tolerance at that degree: with one 1.0 partial followed by 127 partials of 2**-11 the fp16 sum stays at 1.0, a 5.8% error. The policy now caps fp16 at atomic_rounding_degree_cap(10) = 97, the same accuracy bound bf16 uses with 7 fraction bits. The cap binds only the packed-atomic reduction: the f32 workspace degree is already resolved as fp32 dW and keeps the general cap of 128. Explicit and overridden degrees are unchanged. No kernel IR or AOT job changes; an automatic fp16 dW degree on gfx1250 that would exceed 97 is now 97. Tests: the worst-case fp16 partials exceed the tolerance at 128 and stay within it at the automatic degree (the pointwise N=64, 32x32, C=K=16 shape on a 64x128x64 tile); the policy and resolver expectations move from 128 to 97, and the workspace reduction of the same kind of shape still resolves to 128. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…c LDS loader CoalescedTileLoader.load_async_to_lds makes the buffer-resource range check itself, since global_load_async_to_lds has none. Two fixes, in both engines: - The check compared the i32 byte offset with num_bytes signed. A padded reduction tail near 2 GiB can give the offset 0x80000000, which is negative as an i32 and so passed the check, issuing an out-of-bounds copy instead of a zero-fill. The compare is now unsigned (icmp ult), as the resource's is. - The C++ mirror zero-filled out-of-range chunks with an fp16 vector whatever the operands, while Python uses the loader's element type, so bf16-input async kernels diverged between the engines. The C++ loader carries no element type, so the zero now takes the destination LDS tile's (f16 or bf16; anything else is an error, as this port loads 16-bit elements). New parity config 67 (case 55 with bf16 operands) covers the bf16 zero-fill in both engines. The async wgrad goldens move with the compare: configs 55, 56, 60 and 65 and the new 67 in the differential golden (re-recording the family also adds the gfx1250 configs 49-51, which only the llvm24 section had recorded), and the async case in the representative conv_wgrad shard. Test: test_coalesced_load_async_to_lds.py checks the unsigned compare and the element type of the zero store for fp16 and bf16 tiles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max PR #14832 — async K loop, cshuffle, fp16 split-K and two-stage split-K for gfx1250 WMMA wgrad (jakpiase) — 🟡 First-round: minor fixes, CI running
Net-new: none. Utility: 🟢 Positive — gfx1250 wgrad gains async K loop, cshuffle, two-stage; fp16 atomics unfenced. Legend: 🔴 blocking · 🟠 should-fix · 🟡 minor nit · 🟢 info/good · ⚪ retracted First-round review: first pass by a local qwen3.6 model, verified by the orchestrator (Claude Opus 5.5). Checks: merge with develop, native build + ctest, byte identity (every family, 4 flavors), goldens, PR tests, compile + new AOT grid, fp16-atomic ISA, gfx1151 GPU run, Copilot items, CI at head. Flags whether a closer look is needed; not an approval or rejection. |
|
Thanks. The five Copilot items listed there are fixed at cc12d0f: the fp16 auto cap is 97 (6e49bda); the async check is unsigned and the zero-fill uses the tile type (cc12d0f); async pruning (4d5e200); count_jobs (d02b387). The earlier Multi-Arch failures came from a cancelled run that was superseded by a re-run on the same head. |
|
Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max PR #14832 — async K loop, cshuffle, fp16 split-K and two-stage split-K for gfx1250 WMMA wgrad (jakpiase, AICK-2880) — 🟡 Follow-up: fixes verified, CI running
Utility: 🟢 Positive — gfx1250 wgrad gains async K loop, cshuffle, two-stage; review fixes verified. Legend: 🔴 blocking · 🟠 should-fix · 🟡 minor nit · 🟢 info/good · ⚪ retracted Follow-up review: first pass by a local qwen3.6 model, verified by the orchestrator (Claude Opus 5.5). Checks: merge with develop, native build + ctest, byte identity (every family, 4 flavors), goldens, PR tests, compile, fp16 degree sweep, async pruning and job-count probes, async IR diff, CI at head. Flags whether a closer look is needed; not an approval or rejection. |
…ke/gfx1250-wgrad-wmma-implicit
|
Thanks for the follow-up. N1: fixed in the PR body: the gfx1250 fp16 policy now says the automatic degree is capped at 97 (the packed fp16 atomic's accuracy bound; explicit split_k and overrides stay limited to 128, and the f32 workspace keeps the fp32 cap), and the Test Plan lists parity configs 55-67, with 67 covering the bf16 async zero-fill. The body also now describes the unsigned async range check, the async/sync pruning split and the per-target count_jobs gate, and lists byte identity at llvm24. Develop is merged into the branch (a3467a8, no conflicts). After the merge: byte identity GREEN at llvm20/22/23/24, the goldens are OK, ctest -L host passes, and the CPU tests show no new failures. |
…ke/gfx1250-wgrad-wmma-implicit
CongMa13
left a comment
There was a problem hiding this comment.
One actionable issue is reported inline.
…ke/gfx1250-wgrad-wmma-implicit # Conflicts: # dnn-providers/hip-kernel-provider/rocke/platform/python/rocke/core/arch/data/intrinsic_arch_domain.llvm20.json # dnn-providers/hip-kernel-provider/rocke/platform/python/rocke/core/arch/data/intrinsic_arch_domain.llvm22.json # dnn-providers/hip-kernel-provider/rocke/platform/python/rocke/core/arch/data/intrinsic_arch_domain.llvm23.json # dnn-providers/hip-kernel-provider/rocke/platform/python/rocke/core/arch/data/intrinsic_arch_domain.llvm24.json
…loop in C++ Python is_valid_wgrad_spec rejects an explicit vector_size_a/b when async_dma is set, because the async K loop derives its chunk widths from the channel counts alone, and range- and divisibility-checks the caps otherwise. The C++ validator had none of these checks, so a C spec with a cap on the async path passed validation and the builder ignored it. Port the Python loop into rocke_implicit_gemm_conv_wgrad_is_valid_spec at the same point in the gate order, with the same reason strings: reject a cap under async_dma, then require 1 <= cap <= 8 (4 for fp32 A) and that the cap divides the operand's stride-1 channel run (kpg for A, cpg for B). Tests: rocke_wgrad_streamk_host pins the Python reasons for the async rejection (A and B), the range and divisibility rejections, and the specs both engines accept; parity config 68 (config 55 with vector_size_a=4) is a both-engines-reject case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the review. CongMa13 inline (C++ async chunk widths ignore vector_size_a/b): fixed in 480216e: the C++ wgrad validator now rejects an explicit vector_size_a / vector_size_b under async_dma with Python's reason, at the same point in the gate order, and also ports Python's range and divisibility checks on those caps, so both engines accept the same specs. Covered by rocke_wgrad_streamk_host (reasons checked against the Python text) and parity config 68 (config 55 with vector_size_a=4, which both engines reject). Develop is merged into the branch (8564ead). The only conflict was the four intrinsic_arch_domain.llvm*.json files: develop extended the global.atomic.fadd.v2f16 column, which this PR removes along with the intrinsic, so the column stays removed. After the merge and the fix: byte identity GREEN at llvm20/22/23/24, the goldens are OK, ctest -L host passes, and the CPU tests show no new failures. |
|
Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max PR #14832 — async K loop, cshuffle, fp16 split-K and two-stage split-K for gfx1250 WMMA wgrad (jakpiase, AICK-2880) — 🟡 Follow-up: fix verified, two minors open
Utility: 🟢 Positive — CongMa13's fix verified by mutation; body and C++ cap gaps found. Legend: 🔴 blocking · 🟠 should-fix · 🟡 minor nit · 🟢 info/good · ⚪ retracted Follow-up review: first pass by a local qwen3.6 model, verified by the orchestrator (Claude Opus 5.5). Checks: merge with develop, native build + ctest, byte identity (every family, 4 flavors), goldens, PR tests (source and installed), compile, two-engine validator sweep and mutation test, split-K resolver and AOT grid probes, arch-domain tests, CI logs at both heads, code quality. Flags whether a closer look is needed; not an approval or rejection. |
Python caps the synchronous free-axis load widths of the implicit-GEMM wgrad with vector_size_a / vector_size_b (_wgrad_free_axis_vec takes the smaller of the auto width and the cap). The C++ builder computed the auto width inline and ignored both fields, so a capped spec built different IR in the two engines, and the pybind binding dropped both fields, so rocke_engine neither built the capped kernel nor ran the cap checks the C++ validator already has. - wgrad_free_axis_vec takes the cap, as Python does, and the synchronous loader setup (M-outer and K-outer) uses it in place of its inline copy. The async availability check still passes no cap, as _wmma_async_vecs does; both validators reject a cap there. - conv_wgrad_build_spec reads vector_size_a and vector_size_b. Tests: parity configs 69-70 (config 55 on the synchronous K loop with caps 4/2, K-outer and M-outer); both fail against the previous C++ builder. A new gfx1250 K-outer harness case with the same caps. test_conv_wgrad_streamk checks through the binding that the capped kernels lower byte-identically and that the validator reasons agree on the async, range and divisibility rejects. Golden: new keys only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the follow-up. N2 (C++ admits sync A/B caps its builder ignores): fixed in 8ac37b6.
The existing configs' hashes are unchanged; the golden diff only adds the new keys. N1 (body): an explicit The branch merges cleanly with develop (0 commits behind). At 8ac37b6: byte identity GREEN at llvm20/22/23/24, the representative-IR and differential goldens are OK, ctest -L host passes (45 passed, 1 skipped), and the CPU wgrad tests show no new failures against the previous head. |
|
No conflicts in the current merge pool. Targeted for next merge wave. Please prioritize needed approval. |
|
Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max PR #14832 — async K loop, cshuffle, fp16 split-K and two-stage split-K for gfx1250 WMMA wgrad (jakpiase, AICK-2880) — 🟡 Follow-up: fixes verified, one test nit
Utility: 🟢 Positive — C++ cap fix verified by mutation and sweep; one test gap found. Legend: 🔴 blocking · 🟠 should-fix · 🟡 minor nit · 🟢 info/good · ⚪ retracted Follow-up review: first pass by a local qwen3.6 model, verified by the orchestrator (Claude Opus 5.5). Checks: merge with develop, native build + ctest, byte identity (every family, 4 flavors), goldens and their diffs, PR tests (source, installed, unforced llvm24), compile, builder and binding mutation tests, two-engine cap sweep, split-K resolver probe, CI at head, code quality. Flags whether a closer look is needed; not an approval or rejection. |
A cap wider than the auto free-axis width (cap 6 at kpg 12, auto 4) must leave the width at 4. The new binding test checks that both engines lower that spec to the uncapped kernel's IR, on the K-outer and M-outer loops. Also drop the _free_axis_vec alias of _wgrad_free_axis_vec in the builder and name _wgrad_free_axis_vec in the C++ comment that cited the alias. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ke/gfx1250-wgrad-wmma-implicit
|
Thanks for the follow-up. N3 (no test pins "the smaller of auto width and cap"): fixed in b02e4e2. Alias: the Develop is merged into the branch (838c729, no conflicts; it now includes #14835). At the new head:
|
|
Automated overlap notice (rocKE merge monitor) Hi @jakpiase, this PR and #14907 each replace develop's packed fp16 global atomic, the The two copies, here at
On every wgrad parity config the two forms emit the same IR: the differential hashes below carry over unchanged. Owner: #14907: the fix lands before the feature (cgmillette's decision), and its helper is already the one home for the packed bf16 and fp16 adds. Resolution: this PR drops its fp16 body and calls #14907's helper. Your removal of the old intrinsic's declarations ( Suggested order: #14907 first, then this PR, as recorded. Today's merge plan (develop Rebase, after #14907 lands (checked with
Resolved this way (on develop If this PR lands first (today's plan order), take #14907's text now in place of your fp16 body: its If you think this PR should own it instead, reply here and on #14907, and the reviews will follow the new decision. Automated overlap check: one owner per shared piece of code, coordinated on the PRs. Nothing else changed. |
|
Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max PR #14832 — async K loop, cshuffle, fp16 split-K and two-stage split-K for gfx1250 WMMA wgrad (jakpiase, AICK-2880) — 🟡 Follow-up: N3 fixed; rebase on 14842
Utility: 🟢 Positive — N3 pinned in both engines; 14842 conflict resolved and checked. Legend: 🔴 blocking · 🟠 should-fix · 🟡 minor nit · 🟢 info/good · ⚪ retracted Follow-up review: first pass by a local qwen3.6 model, verified by the orchestrator (Claude Opus 5.5). Checks: merge with develop (resolved), build + ctest, byte identity (every family, 4 flavors), goldens, differential golden, tests on the merge (both engines at llvm22 and llvm24, and without the engine), installed tests, compile, N3 mutants in both engines, conflict-resolution probe, CI at head, code quality. Flags whether a closer look is needed; not an approval or rejection. |
…ke/gfx1250-wgrad-wmma-implicit # Conflicts: # dnn-providers/hip-kernel-provider/rocke/platform/cpp/core/lower_llvm/mem.cpp # dnn-providers/hip-kernel-provider/rocke/platform/python/rocke/core/lower_llvm.py
aledudek
left a comment
There was a problem hiding this comment.
LGTM
From what is see the only problem left are merge conflicts, but they arise every second with our blazingly fast development pace
|
Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max PR #14832 — async K loop, cshuffle, fp16 split-K and two-stage split-K for gfx1250 WMMA wgrad (jakpiase, AICK-2880) — 🟡 Follow-up: N4 fixed; rebase after 14907
No net-new findings. The quality lane's points are statuses of the overlap notice, N3 and follow-up 4's code quality row. Utility: 🟢 Positive — 14842's guard kept in both engines; approved behaviour unchanged since Legend: 🔴 blocking · 🟠 should-fix · 🟡 minor nit · 🟢 info/good · ⚪ retracted Follow-up review: first pass by a local qwen3.6 model, verified by the orchestrator (Claude Opus 5.5). Checks: merge with develop, build + ctest, byte identity (every family, 4 flavors), goldens, differential golden, tests on the merge (llvm22 and llvm24), installed tests, compile, N4 probe, fp16 split-K IR, rebase trial with 14907, diff against the approved head, CI at head, code quality. Flags whether a closer look is needed; not an approval or rejection. |


Motivation
The implicit-GEMM wgrad on gfx1250 (wave32 WMMA) ran a single-buffered K loop with no overlap, wrote 16-bit split-K partials as one zero-partner packed atomic per element, lowered the packed fp16 atomic as a fenced atomic, never split fp16 automatically, and had no two-stage (f32 workspace) split-K at all.
This PR gives the gfx1250 WMMA wgrad an async-to-LDS K loop, the cshuffle epilogue, an unfenced packed fp16 atomic, an fp16 split policy, and two-stage split-K, and caches the new variants on a geometry cover, keeping every kernel the cache built before. Every kernel change is mirrored in the C++ engine with byte-identical IR.
gfx1250 conv is served from the AOT cache, as on gfx950. This PR is AOT-cache-served; there is no dispatcher wiring.
Technical Details
Async-to-LDS K loop on WMMA (
conv_implicit_gemm_wgrad.py,helpers/loads.py; C++conv_implicit_gemm_wgrad.cpp,conv_implicit_gemm_conv_kloop_drivers.cpp,conv_implicit_gemm_conv_load_phase.cpp,helpers/loads.cpp)async_dmaon gfx1250 is a one-barrier ping-pong overglobal_load_async_to_lds(CoalescedTileLoader.load_async_to_lds/rocke_conv_emit_kloop_async_global_lds).buffer_load_ldspath keeps its pad of 0.byte_offset < num_bytes(icmp ult, as the buffer resource checks it) into each chunk'svalidand zero-fills failing chunks with an LDS store of the LDS tile's element type (fp16 or bf16).lds_k_outerand a 4-, 8- or 16-byte chunk on both operands.vector_size_a/vector_size_bon it with the same reason. The C++ validator now also checks those caps' range and that they divide the channel run, as Python does._wgrad_free_axis_vecdoes, and therocke_enginebinding passes both fields.pipelinestaysmem.unroll_k,prefetch_addrandchiplet_swizzlestay rejected on WMMA, as before.cshuffle epilogue on WMMA
atomic_store) read the WMMA fragment throughCShuffleEpilogue.from_grid_op, the path the forward and dgrad WMMA epilogues already use. A 16-bit split-K partial is then written as real<2 x dtype>pairs.Packed fp16 atomic (
core/lower_llvm.py; C++core/lower_llvm/mem.cpp)global_atomic_add_pk_f16is now lowered asatomicrmw fadd <2 x half> syncscope("agent") monotonicwith the device-local memory-model metadata, as the packed bf16 add already is.llvm.amdgcn.global.atomic.fadd.v2f16intrinsic it replaces is compiled as a fenced atomic, with aglobal_wb/global_invpair and counter drains around every add. The new form selects the sameglobal_atomic_pk_add_f16with no fences.fp16 split policy (
helpers/split_k.py, Python only; the degree is a host-resolved kernarg with no C++ counterpart)atomic_rounding_degree_cap(10)). An override of the automatic degree (the resolver'soverride, which the benchmark'sROCKE_CONV_SPLIT_Kmaps onto) is still held to the 16-bit atomic limit of 128 on the packed atomic; a free reduction moves to the workspace above it instead. An explicitsplit_kis kept as given (only the gridDim.z budget bounds it).Two-stage split-K on WMMA
op.c_layout(), as the WMMA atomic epilogue does.wgrad_two_stage_availableis true on wave32 targets whose catalog marks float global atomics as validated (gfx1250).--two-stage auto).Benchmark run side (
benchmark_implicit_gemm_conv.py)async_chunk_a/async_chunk_b), not as 8 wide, and the widest-vector pruning ranks async and synchronous kernels apart, so the async kernels are not pruned by the wider synchronous ones.AOT (
kernels/conv/aot_jobs.py)CACHE_WGRAD_WMMA_GEOMETRIES) it adds:ws_replicas= 8);async_dmaloop next to the plain loop for every non-two-stage configuration, built once per geometry with its chunk widths recorded in the identity.global_load_async_to_lds.count_jobsfollows the same rule; CPU tests check that the default grid is unchanged, that the new variants stay on the cover, and that the count matches the job walk on gfx1250, gfx1151 and gfx1201.Docs:
README_conv_implicit_gemm_wgrad.md,dsl_docs/instances/convolution.md,SUPPORT_MATRIX.md.Test Plan
platform/tools/check_byte_identity.py) at llvm20, llvm22, llvm23 and llvm24, each with a fresh build root.conv_implicit_gemm_wgrad_emit.{py,c}cover:vector_size_a/vector_size_bcaps (4 / 2) on the synchronous K loop, K-outer and M-outer (configs 69-70);vector_size_aon the async loop (config 68).rocke_wgrad_streamk_host(ctest -L host): the C++ validator'svector_size_a/vector_size_breasons, checked against the Python text.test_conv_wgrad_streamk.py: through therocke_enginebinding, capped synchronous kernels lower byte-identically, a cap wider than the auto width (6 at kpg 12) keeps the auto width, and the cap rejects give Python's reasons.test_conv_wgrad_correctness.py: validator gates, two-stage availability, cshuffle store vector;test_conv_wgrad_prefetch_addr.py,test_conv_split_k.py, the split-policy dispatch tests (including the fp16 cap of 97 on worst-case partials);test_coalesced_load_async_to_lds.py: the async range check is unsigned, and the zero-fill follows the tile type;test_conv_abi.py: async kernels survive the widest-vector pruning, andcount_jobsmatches the walk per target;test_kernel_label.py,test_conv_aot_grid_limits.py, the arch-domain tests.TestWgradWmmaCShuffle;TestWgradTwoStageWmmaNumerics: an odd-dW-row stem and a 2x2 stride-2 downsample, fp16 / bf16, plain / K-outer / async;--compile-all --directions wgrad, then--run-from-cache --verifyon fp16 / bf16 split-K async, cshuffle and two-stage cases.Test Result
test_conv_wgrad_correctness.py,test_conv_split_k.py,test_conv_wgrad_prefetch_addr.py), compared with the same files on develop: no new failures. The two-stage cases that develop could not build on WMMA now pass, and every new WMMA case passes (cshuffle, cshuffle + async, two-stage plain / K-outer / async, the async odd-slice tail, the split-K resolver). The remaining failures are develop's MFMA-only cases, which fail the same way on develop on gfx1250.--run-from-cache --verifysmoke from the fresh cache, fp16 and bf16 (3x3, 2x2 stride 2, 1x1, a 3-output-channel layer): every verified kernel passes, covering cshuffle, the async loop, packed-atomic split-K and two-stage split-K.Submission Checklist
--verifysmoke.JIRA ID : AICK-2880