Repository navigation
feat(rocke): GDN chunkwise prefill for gfx950 - #12225
Merged
AviralGoelAMD merged 1 commit intoSep 28, 2026
Merged
AviralGoelAMD merged 1 commit into
AviralGoelAMD merged 1 commit into
Conversation
ISSUE ID : AICK-2229 Adds the GDN prefill path: the shared KDA chunkwise kernels run in a new gate_kind="gdn" mode, plus dispatch wiring, tests and docs. GDN prefill is not a separate kernel. The chunkwise algorithm is identical to KDA's; only the decay gate differs (unbounded -exp(A_log)*softplus(a+dt_bias) scalar per token-head, versus KDA's bounded per-channel sigmoid). gate_kind defaults to "kda", so every existing KDA spec emits byte-identical code: the gfx950 KDA golden gains two GDN cases and no existing case SHA moves. dispatch_gdn_prefill rejects algorithm="auto": prefill is chunk_prep then chunk_scan, and resolving auto to one half would dispatch half a computation and return successfully.
yraparti
reviewed
Sep 22, 2026
yraparti
approved these changes
Sep 22, 2026
cgmillette
reviewed
Sep 24, 2026
cgmillette
left a comment
Contributor
There was a problem hiding this comment.
PR #12225 — GDN chunkwise prefill (gfx950) · AICK-2229 · head 6d2aaa02 via Claude 4.7
| # | Loc | Finding | Status/Sev |
|---|---|---|---|
| N1 | prefill_gfx950.py support() |
blank/whitespace algorithm is a wildcard matching both candidates → priority silently picks chunk_scan = half the path (the auto footgun, reopened) |
🆕 MED |
| N2 | kda_chunkwise.py _parts/qk_off |
pow2→shift only holds for unsigned div; confirm b.div is udiv else pow2 divisor carries a sign-fixup, not a bare shift |
🆕 LOW |
| N3 | prefill_common.py prefill_request_errors |
request boundary accepts non-pow2 kv_group (48/16=3); only caught later by is_valid_spec |
🆕 LOW |
Judgement: Positive. Adds GDN prefill by extending the shared KDA emitter with gate_kind (no fork); gate math correct (-exp(A_log)·softplus, overflow-guarded); KDA byte-identity fingerprinted (371 keys, 0 moved); refuses auto/non-pow2 kv_group/unverified KDA+GQA. Disclosed: decay guard in launcher not dispatcher; check_byte_identity unrun.
Legend — ✅ approve · 🟡 partial/open · 🆕 mine (not re-filing R1) · MED/LOW sev.
AviralGoelAMD
merged commit Sep 28, 2026
20f207d
into
users/avirgoel/rocke/gdn-decode-upstream
332 of 349 checks passed
AviralGoelAMD
added a commit
that referenced
this pull request
Sep 28, 2026
ISSUE ID : AICK-2229 ## What this adds Adds GDN (Gated DeltaNet) chunkwise prefill for gfx950. Existing GDN support handles one-token decode; this change adds the two-stage path needed to process an arriving sequence while carrying its recurrent state across chunks. It does not change existing GDN decode selection or KDA behavior. ## What changed | Area / files | Crux of change | |---|---| | `library/kernels/gfx950/kda_chunkwise.py`, `library/builders/gfx950/kda/gdn_prefill.py` | Adds the GDN gate mode to the shared chunkwise KDA emitter and provides the host driver plus reference implementation. | | `library/dispatch/gdn/prefill_*.py` | Adds explicit `chunk_prep` and `chunk_scan` candidates; rejects `algorithm="auto"` because either half alone is incomplete. | | `library/tests/` | Adds dispatch, gate-range, golden, specification, and on-device numeric coverage. | | `platform/dsl_docs/instances/gdn.md` | Documents the prefill contract, its two-stage launch, and the GDN gate constraint. | - Rebased from [#12225](#12225), which merged into a feature branch rather than `develop`. ## Why it works GDN and KDA share the same chunk factorization, triangular solve, and state scan. The only algorithmic difference is the decay-gate expression, so `gate_kind="gdn"` changes that expression without duplicating the shared algebra. `gate_kind` defaults to KDA, and the golden tests check that existing KDA outputs remain unchanged. A prefill runs `chunk_prep` followed by `chunk_scan`. Dispatch requires the caller to select one stage explicitly, which prevents returning from a partial two-stage computation as if it were complete. ## How we validated | Gate | Result | |---|---| | Targeted library suite | 66 passed; 19 skipped only where the local environment lacks optional Torch or a gfx950 ROCm runtime. | | KDA/GDN golden IR | Passed for llvm20, llvm22, and llvm23. | | Pre-commit | Passed for `origin/develop...HEAD`. | | Dual-engine byte identity | 69 of 70 families green on this branch. The sole `gfx942_attention_tiled_3d` drift is also present on a clean `develop` baseline and is outside this diff. | | On-device GDN numeric lane | Not rerun after the transplant: this host does not provide a gfx950 ROCm-Torch environment. The original [#12225](#12225) validation record covers the source commit; rerun this lane on gfx950 before review. | ## Notes - No performance data is included in this public PR. - This PR changes only the prefill delta from #12225; its prerequisite GDN decode is already in `develop` via [#12172](#12172).
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.
ISSUE ID : AICK-2229
Adds GDN (Gated DeltaNet) chunkwise prefill for gfx950 — the many-tokens-at-once path that runs when a sequence first arrives — plus dispatch wiring, tests and docs. 19 files, +2214.
The one design decision
GDN prefill is not a new kernel. It is the shared KDA chunkwise pair run in a new gate mode.
The chunkwise algorithm is identical for both families — chunk factorization, triangular solve, state scan. Only the decay gate differs:
lower_bound * sigmoid(exp(A_log) * (g + dt_bias))-exp(A_log) * softplus(a + dt_bias)(token, head), broadcast acrossDK(0,1)Forking the emitter would have duplicated three pieces of real algebra to vary one expression. Instead
KdaChunkPrepSpecgainsgate_kind, defaulting to"kda".Because this touches a shipped family, the default path must not move
Every KDA spec emits byte-identical IR. The golden fixture gains the two new GDN cases and nothing else.
Two launches, and
autois refusedA GDN prefill is
chunk_prepthenchunk_scan. There is no fused single-kernel path, sodispatch_gdn_prefillrejectsalgorithm="auto"rather than resolving it to one half — dispatching half a computation and returning successfully is the failure mode worth preventing.What is in the diff
kernels/gfx950/kda_chunkwise.py—gate_kindfield, GDN gate lowering, validationbuilders/gfx950/kda/gdn_prefill.py— host driver + fp32 referencedispatch/gdn/{__init__,prefill_common,prefill_gfx950}.pygdn.mdprefill section,index.mdentry points, README inventory,SUPPORT_MATRIXsweep_prefill_value_splits.pyThe unbounded gate is why
test_gdn_prefill_decay_guard.pyexists: KDA's gate cannot leave(0,1), GDN's can, so the supported envelope is asserted rather than assumed.One unrelated change, declared
kda_chunkwise.py:286-293rewrites a comment about the triangular solve's cost: the old textsaid the scalar part "shrinks as the square of the block size", which is wrong -- there are
C/bblocks, so the total scalar work is~C*b/2, linear insolve_block. Comment only,zero codegen effect, and the dispatcher pins
solve_block=8so nothing here exercises it. Itis not GDN work; it is kept rather than reverted because leaving a known-wrong comment in a
shipped file costs the next reader more than the scope purity saves a reviewer.
Stack
PR 4 of 4: #12070 -> #12090 -> #12172 (decode) -> this, based on #12172. CI is red until
the first two merge; verification below ran on a tree carrying all of them.
Verification
Gates run on this branch at head
8edf4a84007, with the base branch as the baseline:platform/testsdeveloplibrary/tests*smci355-ccs-aus-n01-09, commit45399628b51)*
--continue-on-collection-errorsis required. At this head two GDN suites error atcollection and six decode tests fail, all of them
make_kernel_id/warp_shuffle_xor_quad— symbols that arrive with #12090 and #12070. Without that flag pytest aborts the session
and runs nothing.
check_byte_identity.pyis the third named gate and was NOT run: it needs the C++ engine,which is not built on this node.
Three caveats
Criterion 3 is PARTIAL. The decay guard works and is silicon-verified, but
launch_gdnisthe dev/test launcher -- no production launcher exists for prefill yet, and the decay lives in
tensors the request does not carry, so the check cannot move into the dispatcher. Whoever adds
one must call
reject_if_decay_out_of_range.KDA is undisturbed, measured not argued. Both trees fingerprinted on one GPU in one
session -- LLVM text, hsaco bytes, float results: 371 keys, 0 moved.
Past its limit the gate saturates, it does not fault. A bounded wrong answer, and the
output degrades while the carried state stays clean -- so a loop validating only its state
sees nothing. That is what the guard exists to stop.
Known limitation
Small-batch prefill is not yet optimized; that work is tracked as follow-up. Measured
comparisons live in the internal perf-data record, not here.