Skip to content

feat(rocke): GDN chunkwise prefill for gfx950 - #12225

Merged
AviralGoelAMD merged 1 commit into
users/avirgoel/rocke/gdn-decode-upstreamfrom
users/avirgoel/rocke/gdn-prefill
Sep 28, 2026
Merged

AviralGoelAMD merged 1 commit into
users/avirgoel/rocke/gdn-decode-upstreamfrom
users/avirgoel/rocke/gdn-prefill

Conversation

@AviralGoelAMD

@AviralGoelAMD AviralGoelAMD commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

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:

KDA GDN
gate lower_bound * sigmoid(exp(A_log) * (g + dt_bias)) -exp(A_log) * softplus(a + dt_bias)
shape per channel scalar per (token, head), broadcast across DK
range bounded in (0,1) unbounded

Forking the emitter would have duplicated three pieces of real algebra to vary one expression. Instead KdaChunkPrepSpec gains gate_kind, defaulting to "kda".

Because this touches a shipped family, the default path must not move

flavour existing cases added existing SHAs moved
llvm20 18 2 GDN cases 0
llvm22 18 2 GDN cases 0
llvm23 18 2 GDN cases 0

Every KDA spec emits byte-identical IR. The golden fixture gains the two new GDN cases and nothing else.

Two launches, and auto is refused

A GDN prefill is chunk_prep then chunk_scan. There is no fused single-kernel path, so dispatch_gdn_prefill rejects algorithm="auto" rather than resolving it to one half — dispatching half a computation and returning successfully is the failure mode worth preventing.

prep = dispatch_gdn_prefill(GdnPrefillRequest(..., algorithm="chunk_prep"))
scan = dispatch_gdn_prefill(GdnPrefillRequest(..., algorithm="chunk_scan"))

What is in the diff

area files
emitter kernels/gfx950/kda_chunkwise.py — gate_kind field, GDN gate lowering, validation
driver builders/gfx950/kda/gdn_prefill.py — host driver + fp32 reference
dispatch dispatch/gdn/{__init__,prefill_common,prefill_gfx950}.py
tests prefill wiring, decay guard, KDA chunkwise spec, KDA golden + fixture, on-device GDN numeric
docs gdn.md prefill section, index.md entry points, README inventory, SUPPORT_MATRIX
benchmark sweep_prefill_value_splits.py

The unbounded gate is why test_gdn_prefill_decay_guard.py exists: 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-293 rewrites a comment about the triangular solve's cost: the old text
said the scalar part "shrinks as the square of the block size", which is wrong -- there are
C/b blocks, so the total scalar work is ~C*b/2, linear in solve_block. Comment only,
zero codegen effect, and the dispatcher pins solve_block=8 so nothing here exercises it. It
is 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:

gate this PR base branch verdict
installed golden IR (llvm20/22/23) PASS PASS clean
platform/tests 3 failed / 865 passed 3 failed / 865 passed on develop pre-existing, identical
library/tests * 18 failed / 1276 passed 18 failed / 1268 passed 0 new failures, +8 passed
On-device gfx950 (Slurm 872, smci355-ccs-aus-n01-09, commit 45399628b51) 15 numeric + 18 wiring + 2 guard passed, 0 skipped — see caveats
KDA regression fingerprint 371 keys, 0 moved — LLVM text + hsaco bytes + float results — same machine code

* --continue-on-collection-errors is required. At this head two GDN suites error at
collection 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.py is 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_gdn is
the 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.

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.

@cgmillette cgmillette left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
AviralGoelAMD merged commit 20f207d into users/avirgoel/rocke/gdn-decode-upstream Sep 28, 2026
332 of 349 checks passed
@AviralGoelAMD
AviralGoelAMD deleted the users/avirgoel/rocke/gdn-prefill branch September 28, 2026 15:01
@AviralGoelAMD
AviralGoelAMD restored the users/avirgoel/rocke/gdn-prefill branch September 28, 2026 15:06
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).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants