Skip to content

feat(rocke): free-stride SDPA forward and varlen host adapters - #13953

Merged
arai713 merged 7 commits into
developfrom
users/arai/sdpa_seqlens
Nov 2, 2026
Merged

arai713 merged 7 commits into
developfrom
users/arai/sdpa_seqlens

Conversation

@arai713

@arai713 arai713 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

JIRA ID : AICK-2518, AICK-2521, AICK-2618

Motivation

hipDNN needs attention forward to read Q/K/V from arbitrary memory layouts and to handle variable-length sequences. This PR lands the foundation for both on gfx942/gfx950.

Free strides (AICK-2618): the production forward kernel (fmha_mfma) only served the BSHD-contiguous case. It folded the batch axis in as batch_idx * seqlen_q rows, which is expressible only when the batch stride is a whole number of token strides. BHSD, general B/H/S strides and packed QKV could not be served without a host-side repack. This generalizes fmha_mfma so one kernel reads any layout.

Varlen (AICK-2518, AICK-2521): the host-side foundation for padding masks (ticket 10) and ragged / THD varlen lengths (ticket 12). The oracle can express padding, hipDNN's length tensors are validated and normalized into both the padded and the ragged forms the kernels consume, and the exact mask the kernel must emit is specified and cross-checked. The kernel-side masking lands in the free-stride path in a follow-up, together with the arbitrary-length buffer clipping it rides on (a separate ticket), so it becomes a mechanical drop-in.

Technical Details

Free-stride forward

  • Add an opt-in free_stride: bool = False field to FmhaMfmaSpec (defaulted, so existing descriptors / AOT packs hydrate to the current behavior).
  • When set, the kernel declares four per-tensor batch strides and rebases the Q/K/V/O base pointers to each CTA's batch slice (global_ptr_add(ptr, batch_idx * stride_*_batch * elem_bytes)). The shared body then addresses head and token through the runtime strides it already takes. Batch, head and token offsets are all runtime, so BSHD, BHSD, general strides and packed QKV all build the same kernel with nothing baked.
  • The default (free_stride=False) path is left verbatim and is byte-identical: the field defaults off, the ABI (15 params) and kernel name are unchanged, and only the free-stride path appends the four batch strides (19 params) and tags the name _fs.
  • The free-stride path is Python-only for now and is not added to the byte-identity parity fixtures.
  • New host helper _sdpa_fwd_strides.contiguous_strides single-sources the (batch, head, token) element strides the launcher passes for the bhsd/bshd orders. The earlier standalone free-stride scaffold (fmha_sdpa_fwd) is retired, now subsumed by fmha_mfma.

Varlen host adapters

  • dense_attention_reference gains optional seqlen_q / seqlen_kv. None keeps the exact unpadded arithmetic. When set, keys past seqlen_kv are masked, a row with no valid key yields O = 0, and padded query rows are zeroed.
  • _sdpa_seqlen.py is the host adapter for hipDNN SEQ_LEN tensors, covering both layouts.
    • sdpa_seqlens (padded) validates SEQ_LEN_Q/KV at the boundary (integer dtype, per-batch count, 0 <= len <= S_max) and returns the flat per-batch int32 arrays the free-stride kernel will read. The call signature is provisional until those kernargs freeze.
    • sdpa_cu_seqlens (ragged) validates the ragged per-sequence counts (integer dtype, exactly batch counts, non-negative) and returns (cu_seqlens_q, cu_seqlens_kv) as contiguous (B+1,) int32 exclusive prefix sums with a leading 0, whose last element is the packed token total. This is exactly the form fmha_varlen consumes, which recovers each span as cu[i+1] - cu[i]. The key side is named kv to match hipDNN's SEQ_LEN_KV. The kernel param is cu_seqlens_k.
    • The shared length validator is refactored so the S_max check is optional, since ragged packing has no per-sequence upper bound, with the negative-length check split into its own message. The padded path is behaviorally unchanged.
  • _sdpa_padding_mask.py is the host specification of the kernel-side mask, keep_key and row_is_valid (the epilogue's padded-row zeroing). The live IR emitter is deferred to the free-stride kernel follow-up, where it carries its byte-identity C++ mirror. This module is the host reference it must match.

Test Plan

  • CPU ABI contract (test_fmha_mfma_free_stride.py): default ABI unchanged (15 params, no batch strides), free_stride appends exactly the four batch strides (19), name tagging, builds for gfx942/gfx950 and GQA.
  • Host layout contract (test_sdpa_free_stride_layouts.py): every layout (BHSD/BSHD/padded/packed) recovers the same values and attention output via the kernel's scalar address formula, and contiguous_strides matches numpy.
  • On-GPU numeric parity (test_fmha_mfma_free_stride_numeric.py, CDNA-gated): fmha_mfma(free_stride=True) matches the numpy reference across four physical placements, contiguous BHSD, contiguous BSHD, a padded head dim and packed QKV, and all four agree bit-for-bit on one logical problem.
  • Oracle padding tests (test_numeric_references.py).
  • Length-adapter tests, padded and ragged (test_sdpa_seqlen.py).
  • Mask-spec tests including composition against the oracle (test_sdpa_padding_mask.py).
  • Default-path byte-identity: lowered LLVM IR compared before/after for gfx950 and gfx942, none and causal, plus the byte-identity gate at every flavor (tools/check_byte_identity.py).
  • Library layering guard (new kernel-layer files import downward only).

Test Result

  • Default-path LLVM IR is byte-identical to the pre-change kernel for gfx950 and gfx942 across the none and causal masks (the C++ twin and golden are untouched).
  • On-GPU numeric parity passes on gfx942 and gfx950, 11 cases: contiguous BHSD and BSHD, a padded head dim allocated at D+8 and sliced to D, and packed QKV where Q/K/V alias one buffer at offsets 0, HD and 2H*D. The four placements produce bit-identical output, confirming the per-batch pointer rebase and the strided addressing read and write every placement correctly.
  • The mask spec's keep_key / row_is_valid predicates reproduce dense_attention_reference's padded output exactly.
  • The ragged adapter output is cross-checked against fmha_varlen's documented cu_seqlens contract, i32 exclusive prefix sum.
  • CPU ABI, host-contract, length-adapter, mask-spec, oracle and layering suites pass with no GPU and no torch.

Submission Checklist

@arai713
arai713 requested review from a team as code owners October 19, 2026 19:41
@github-actions github-actions Bot added project: hip-kernel-provider rocke-lib: attention Touches rocKE library attention/FMHA kernels rocke: platform Touches rocKE platform labels Oct 19, 2026
@therock-pr-bot

therock-pr-bot Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

✅ All Checks Passed — Ready for Review

Check Status Details
📝 PR Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ✅ Pass —
🔎 pre-commit ✅ Pass —
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —
🤖 therock-pr-bot ✅ Pass —

🎉 All checks passed! This PR is ready for review.

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

@therock-pr-bot

therock-pr-bot Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

🎉 All checks passed! This PR is ready for review.

@amd-khushbu amd-khushbu 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.

Review findings

Went through this independently and cross-checked against _fmha_warp_body.py, RFC 0014, and the repo history.

Agree with @AnaghaRaoAMD's review on all 5 points raised

  • The fully-masked-row (len_kv==0) claim in _sdpa_padding_mask.py — verified against _fmha_warp_body.py's epilogue: there's no l==0/row_has_mass guard, so the row comes out as mean(V), not O=0.
  • sdpa_cu_seqlens deriving offsets via cumsum(seq_lens) instead of the spec's ragged_offset — verified against RFC 0014 §4.5; this misses the "packed with trailing padding" case.
  • The int32 narrowing in _validate_lengths happening before the range checks — we found this independently too, with the same repro.
  • The int32 overflow in the cu_seqlens prefix sum itself — reproduced.
  • The missing both-set-or-neither check on seqlen_q/seqlen_kv — confirmed it currently passes only by accident.

Two additional findings:

1. Duplicate padding-mask/oracle logic (MEDIUM)

references.py's dense_attention_reference grows its own padding-mask implementation in the same module that already imports rocke.numeric.sdpa_reference.sdpa_reference, which already supports seq_len_q/seq_len_kv (dense) and ragged q_offsets/kv_offsets (exactly the prefix-sum format sdpa_cu_seqlens produces). The two oracles' padding semantics are already documented as deliberately diverging, and this PR widens that split rather than reusing the more complete implementation. Related to Anagha's suggestion to build one keep mask up front instead of rebuilding predicates post-hoc — same root cause: the masking logic here isn't centralized.

2. Internal ticket IDs embedded in shipped docstrings (MEDIUM)

_sdpa_padding_mask.py ("ticket 10", "ticket 8", "ST4") and _sdpa_seqlen.py ("ticket 10", "ticket 12", "SH-01", "ST4"). Tracker references belong in the commit message / PR description, not in code that ships and outlives the ticket.


@cgmillette

Copy link
Copy Markdown
Contributor

Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max

PR #13953 — host-side SDPA sequence-length handling (padding + ragged) — 🟠 First-round: closer look — every reviewer item is still open (no commits since 10-19) and each one checks out

Prior item Status
AnaghaRaoAMD ×2, aosewski, amd-khushbu (10-20): a fully masked row (len_kv == 0) does not reach O = 0 in the kernel path the spec points at; the emulator zeroes it by hand, so the test cannot catch it 🔴 OPEN — confirmed: _fmha_warp_body.py masks with a finite -1e30 (:178) and divides by a bare rcp(l_final) (:296, :420), so the row comes out as mean(V) (read)
AnaghaRaoAMD, amd-khushbu: cu_seqlens from cumsum(SEQ_LEN) instead of hipDNN's ragged_offset (RFC 0014) 🔴 OPEN — RFC 0014 defines the layout by ragged_offset[B+1] (read)
AnaghaRaoAMD, amd-khushbu: int64 lengths narrowed to int32 before the range checks 🔴 OPEN — reproduced: 2**32 + 3 validates as 3 (ran)
AnaghaRaoAMD, amd-khushbu: int32 prefix-sum overflow on the ragged path 🔴 OPEN — reproduced: total -2147483645, no error (ran)
AnaghaRaoAMD, amd-khushbu: Q / KV lengths must be both set or both unset 🔴 OPEN — reproduced: a one-sided call fails only by accident, in the dtype check (ran)
amd-khushbu (CHANGES_REQUESTED): padding oracle duplicates sdpa_reference 🔴 OPEN — sdpa_reference already takes seq_len_q / seq_len_kv and q_offsets / kv_offsets (read)
amd-khushbu, aosewski: plan / tracker labels in shipped docstrings 🔴 OPEN — "ticket 8 / 10 / 12", "ST4", "SH-01" still at head (read)
AnaghaRaoAMD nits: build one keep mask; state the padded-row convention; land keep_key / row_is_valid with the shell's emitter 🔴 OPEN — no commits since (read)
Bots / CI 🟢 Policy bot, pre-commit, clang-tidy pass; TheRock CI hipkernelprovider green on Linux gfx94X and Windows gfx110X (both shards each)
Our checks 🟢 Clean merge with develop (74 behind); the PR's tests, the layering guard and the numeric-reference tests: 32 passed on the merge (ran)
# Sev file:line Finding
N1 🟡 library/tests/test_sdpa_seqlen.py Neither new library test file (test_sdpa_seqlen.py, test_sdpa_padding_mask.py) is in a CTest file list — library tests run only from the explicit lists in platform/CMakeLists.txt — so they never run in CI → add both to the attention CPU list.

Utility: 🟢 Positive in intent — useful host-side groundwork for SDPA padding and ragged inputs, but not mergeable until the reviewers' items land, the fully-masked-row spec and the ragged-offset contract above all.


Legend: 🔴 blocking · 🟠 should-fix · 🟡 minor nit · 🟢 info/good · ⚪ retracted

First-round review: a local qwen3.6 model made the independent first pass; the orchestrator (Claude Opus 5.5) verified it — reproduced each reviewer claim on the head, checked the kernel path and RFC they cite, ran the tests on a merge with develop, and checked CI enrollment — and owns this merge-worthiness call. It flags whether a closer look is needed; it is not an approval or a rejection.

@arai713
arai713 force-pushed the users/arai/sdpa_seqlens branch from 3105f3c to 3a46d21 Compare October 21, 2026 18:30
@CongMa13

Copy link
Copy Markdown
Contributor

I reviewed this PR again. The following review threads are still unresolved, or comments have no author reply:

If you have already fixed these, please reply on each comment to say so and resolve the threads. Please request my review again once all comments and threads are resolved.

@arai713
arai713 force-pushed the users/arai/sdpa_seqlens branch from 3a46d21 to 6fad3ac Compare October 26, 2026 17:53
@arai713

arai713 commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Review findings

Went through this independently and cross-checked against _fmha_warp_body.py, RFC 0014, and the repo history.

Agree with @AnaghaRaoAMD's review on all 5 points raised

  • The fully-masked-row (len_kv==0) claim in _sdpa_padding_mask.py — verified against _fmha_warp_body.py's epilogue: there's no l==0/row_has_mass guard, so the row comes out as mean(V), not O=0.
  • sdpa_cu_seqlens deriving offsets via cumsum(seq_lens) instead of the spec's ragged_offset — verified against RFC 0014 §4.5; this misses the "packed with trailing padding" case.
  • The int32 narrowing in _validate_lengths happening before the range checks — we found this independently too, with the same repro.
  • The int32 overflow in the cu_seqlens prefix sum itself — reproduced.
  • The missing both-set-or-neither check on seqlen_q/seqlen_kv — confirmed it currently passes only by accident.

Two additional findings:

1. Duplicate padding-mask/oracle logic (MEDIUM)

references.py's dense_attention_reference grows its own padding-mask implementation in the same module that already imports rocke.numeric.sdpa_reference.sdpa_reference, which already supports seq_len_q/seq_len_kv (dense) and ragged q_offsets/kv_offsets (exactly the prefix-sum format sdpa_cu_seqlens produces). The two oracles' padding semantics are already documented as deliberately diverging, and this PR widens that split rather than reusing the more complete implementation. Related to Anagha's suggestion to build one keep mask up front instead of rebuilding predicates post-hoc — same root cause: the masking logic here isn't centralized.

2. Internal ticket IDs embedded in shipped docstrings (MEDIUM)

_sdpa_padding_mask.py ("ticket 10", "ticket 8", "ST4") and _sdpa_seqlen.py ("ticket 10", "ticket 12", "SH-01", "ST4"). Tracker references belong in the commit message / PR description, not in code that ships and outlives the ticket.

All five shared points are fixed in the latest push: the empty-row spec now requires a row_has_mass guard and the test runs the real softmax, the adapter checks lengths as int64 and catches overflow, it enforces Q/KV both-set-or-neither, and the ragged adapter is scoped to packed-only with ragged_offset support deferred. The tracker labels are gone, and I put the oracle's masking behind a single keep mask, though moving it fully onto sdpa_reference is a bigger change I'd like to do as a follow-up.

@arai713

arai713 commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max

PR #13953 — host-side SDPA sequence-length handling (padding + ragged) — 🟠 First-round: closer look — every reviewer item is still open (no commits since 10-19) and each one checks out

Prior item Status
AnaghaRaoAMD ×2, aosewski, amd-khushbu (10-20): a fully masked row (len_kv == 0) does not reach O = 0 in the kernel path the spec points at; the emulator zeroes it by hand, so the test cannot catch it 🔴 OPEN — confirmed: _fmha_warp_body.py masks with a finite -1e30 (:178) and divides by a bare rcp(l_final) (:296, :420), so the row comes out as mean(V) (read)
AnaghaRaoAMD, amd-khushbu: cu_seqlens from cumsum(SEQ_LEN) instead of hipDNN's ragged_offset (RFC 0014) 🔴 OPEN — RFC 0014 defines the layout by ragged_offset[B+1] (read)
AnaghaRaoAMD, amd-khushbu: int64 lengths narrowed to int32 before the range checks 🔴 OPEN — reproduced: 2**32 + 3 validates as 3 (ran)
AnaghaRaoAMD, amd-khushbu: int32 prefix-sum overflow on the ragged path 🔴 OPEN — reproduced: total -2147483645, no error (ran)
AnaghaRaoAMD, amd-khushbu: Q / KV lengths must be both set or both unset 🔴 OPEN — reproduced: a one-sided call fails only by accident, in the dtype check (ran)
amd-khushbu (CHANGES_REQUESTED): padding oracle duplicates sdpa_reference 🔴 OPEN — sdpa_reference already takes seq_len_q / seq_len_kv and q_offsets / kv_offsets (read)
amd-khushbu, aosewski: plan / tracker labels in shipped docstrings 🔴 OPEN — "ticket 8 / 10 / 12", "ST4", "SH-01" still at head (read)
AnaghaRaoAMD nits: build one keep mask; state the padded-row convention; land keep_key / row_is_valid with the shell's emitter 🔴 OPEN — no commits since (read)
Bots / CI 🟢 Policy bot, pre-commit, clang-tidy pass; TheRock CI hipkernelprovider green on Linux gfx94X and Windows gfx110X (both shards each)
Our checks 🟢 Clean merge with develop (74 behind); the PR's tests, the layering guard and the numeric-reference tests: 32 passed on the merge (ran)

Sev file:line Finding

N1 🟡 library/tests/test_sdpa_seqlen.py Neither new library test file (test_sdpa_seqlen.py, test_sdpa_padding_mask.py) is in a CTest file list — library tests run only from the explicit lists in platform/CMakeLists.txt — so they never run in CI → add both to the attention CPU list.
Utility: 🟢 Positive in intent — useful host-side groundwork for SDPA padding and ragged inputs, but not mergeable until the reviewers' items land, the fully-masked-row spec and the ragged-offset contract above all.

Legend: 🔴 blocking · 🟠 should-fix · 🟡 minor nit · 🟢 info/good · ⚪ retracted

First-round review: a local qwen3.6 model made the independent first pass; the orchestrator (Claude Opus 5.5) verified it — reproduced each reviewer claim on the head, checked the kernel path and RFC they cite, ran the tests on a merge with develop, and checked CI enrollment — and owns this merge-worthiness call. It flags whether a closer look is needed; it is not an approval or a rejection.

Every open item in your table is handled in the latest push, rebased onto current develop, and both new test files are now in the attention CPU CTest list so they run in CI. The two narrowed items are the packed-only ragged scope and the sdpa_reference consolidation, both called out as follow-ups.

@arai713
arai713 requested a review from amd-khushbu October 26, 2026 17:54
@cgmillette

Copy link
Copy Markdown
Contributor

Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max

PR #13953 — host-side SDPA sequence-length handling (padding + ragged) — 🟢 Follow-up: no closer look needed — every reviewer item is fixed or explicitly deferred, and the tests now run in CI; amd-khushbu's changes-requested review still needs their sign-off on the deferred oracle consolidation

Prior item Status
AnaghaRaoAMD ×2, aosewski, amd-khushbu (10-20): a fully masked row (len_kv == 0) does not reach O = 0 in the kernel path 🟢 FIXED as a contract. _sdpa_padding_mask.py now requires the epilogue to select on row_has_mass and write O = 0 / LSE -inf. It also explains why the finite -1e30 sentinel otherwise yields mean(V) (read). The test emulator runs the real online-softmax recurrence, with len_kv == 0 (causal and not) in the oracle loop. Negative control: removing the emulator's guard fails 2 tests (ran). The kernel-side guard lands with the shell, which is not in the tree yet
AnaghaRaoAMD, amd-khushbu: cu_seqlens from cumsum(SEQ_LEN) instead of RFC 0014's ragged_offset 🟡 NARROWED. The ragged adapter is documented as fully packed only, and a caller that needs trailing padding must not use it. ragged_offset support is deferred (read)
AnaghaRaoAMD, amd-khushbu: int64 lengths narrowed to int32 before the range checks 🟢 FIXED: 2**32 + 3 is now rejected (ran)
AnaghaRaoAMD, amd-khushbu: int32 prefix-sum overflow 🟢 FIXED: the packed total is summed in int64 and rejected above int32 (ran)
AnaghaRaoAMD, amd-khushbu: Q / KV lengths both set or both unset 🟢 FIXED: both one-sided calls raise "must both be set or both unset" (ran)
amd-khushbu (CHANGES_REQUESTED, 10-20): the padding oracle duplicates sdpa_reference 🟡 DEFERRED by the author as a follow-up. amd-khushbu's review still stands, so this needs their sign-off (read)
amd-khushbu, aosewski: plan / tracker labels in shipped docstrings 🟢 FIXED: none are left in either module or the tests (ran)
AnaghaRaoAMD nits: one keep mask; the padded-row convention; keep_key / row_is_valid with the shell's emitter 🟢 The first two are FIXED. 🟡 The predicates stay as the host-side spec until the shell lands, as documented (read)
Ours N1 🟡 (10-21): neither new test file was in a CTest list 🟢 FIXED: both are in the attention CPU list. At head, both gfx94X shards ran them: test_sdpa_seqlen.py 18 passed and test_sdpa_padding_mask.py 5 passed (read)
CongMa13 (10-22): threads without an author reply 🟢 The author replied on every thread (read)
Bots / CI 🟢 At 6fad3ac7, the hipDNN superbuilds fail only on develop's break, which #14412 fixes: KeyError: 'kernel_source' and EveryHandlerReportsSupportsSaving… (read). The gfx94X tests pass, and the gfx110X sanity job is queued
Our checks 🟢 The merge with today's develop is clean. The PR's tests, the layering guard and the numeric-reference tests pass 38 on the merge (ran). rule_scan is clean (ran)

No net-new findings.

Utility: 🟢 Positive — host-side groundwork for SDPA padding and packed ragged inputs. The length validation is now overflow-safe, and the fully-masked-row contract is written down with a test that has teeth.


Legend: 🔴 blocking · 🟠 should-fix · 🟡 minor nit · 🟢 info/good · ⚪ retracted

Follow-up review: a local qwen3.6 model made the independent first pass. The orchestrator (Claude Opus 5.5) verified it. The checks were the first round's reproductions on the new head and the spec and emulator against the reviewers' items, with a negative control on the epilogue guard. They also covered the tests on a merge with today's develop and CI enrollment and failures from the logs. The orchestrator owns this merge-worthiness call. It flags whether a closer look is needed; it is not an approval or a rejection.

@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.

LGTM

@arai713
arai713 force-pushed the users/arai/sdpa_seqlens branch from 6fad3ac to c2b299d Compare October 27, 2026 17:52
@CongMa13

Copy link
Copy Markdown
Contributor

I reviewed this PR again. The following review threads are still unresolved, or comments have no author reply:

If you have already fixed these, please reply on each comment to say so and resolve the threads.

@arai713
arai713 force-pushed the users/arai/sdpa_seqlens branch from c2b299d to edfd555 Compare October 28, 2026 18:35
@arai713 arai713 changed the title feat(rocke): host-side SDPA sequence-length handling (padding + ragged) feat(rocke): free-stride SDPA forward and varlen host adapters Oct 28, 2026
@arai713
arai713 force-pushed the users/arai/sdpa_seqlens branch from edfd555 to 1310155 Compare October 28, 2026 19:33
@cgmillette

Copy link
Copy Markdown
Contributor

Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max

PR #13953 — free-stride SDPA forward and varlen host adapters (arai713, AICK-2518, AICK-2521, AICK-2618) — 🟢 Follow-up: no closer look needed — the four new free-stride commits leave the default kernel byte-identical and pass on gfx942 hardware in CI, but the merge stays blocked until AnaghaRaoAMD's five 10-28 threads are answered and resolved, and both approvals predate these commits

Prior item Status
AnaghaRaoAMD, aosewski, amd-khushbu (10-20) ×4: a fully masked row (len_kv == 0) must give O = 0; int64 lengths narrowed before the range checks; int32 prefix-sum overflow; Q / KV lengths both set or both unset 🟢 FIXED, unchanged since our 10-26 review. The three varlen commits are patch-identical to 6fad3ac7 (ran: git range-diff). The empty row is fixed as a contract: the spec requires the row_has_mass epilogue guard, and the kernel guard lands with the follow-up kernel-side masking. test_sdpa_seqlen.py and test_sdpa_padding_mask.py pass on the merge (ran) and on all four CI shards, with 18 and 5 passed (read)
AnaghaRaoAMD, amd-khushbu (10-20): cu_seqlens from cumsum(SEQ_LEN) instead of RFC 0014's ragged_offset 🟡 NARROWED / DEFERRED. The ragged adapter stays documented as fully packed only, because it "only takes lengths"; ragged_offset support is a follow-up. Acceptable while nothing consumes it (read)
amd-khushbu (10-20, CHANGES_REQUESTED): the padding oracle duplicates sdpa_reference 🟡 DEFERRED. The author calls it "a bigger change I'd like to do as a follow-up". amd-khushbu approved after that reply (10-27), which supersedes their changes request. That is the sign-off our 10-26 review was waiting for (ran: gh)
amd-khushbu, aosewski (10-20): plan / tracker labels in the varlen docstrings 🟢 FIXED c5418282: none are left in _sdpa_padding_mask.py or _sdpa_seqlen.py (ran: grep). The new free-stride files add "ticket 2" / "Step 1" (AnaghaRaoAMD, 10-28, below)
AnaghaRaoAMD (10-20) nits: one keep mask; the padded-row convention; land keep_key / row_is_valid with the shell's emitter 🟢 The first two are FIXED (read). 🟡 The third is DEFERRED: the predicates stay as the host-side spec, and the body moves the kernel-side masking to a follow-up on the free-stride path (read)
Ours N1 🟡 (10-21): neither varlen test file was in a CTest list 🟢 Still FIXED, and the three new files are enrolled too: the two CPU files in rocke_library_attention_cpu_pytest and the numeric file in rocke_library_gpu_pytest. All three ran in CI (read)
CongMa13 (10-22, 10-27): threads, and our 10-26 comment, without an author reply 🟢 ADDRESSED. The author answered every 10-20 thread. Our 10-26 comment had no open items, so it needs no reply (ran: gh)
cgmillette (10-26) and amd-khushbu (10-27) APPROVED at c2b299dd 🟡 Stale, still counts. c2b299dd is the rebased varlen-only head, so both approvals predate the four free-stride commits 326ba27e…13101557 (9 files, +689 / −13, a new kernel mode). develop does not dismiss stale approvals, and the two cover both code-owner teams: cgmillette for platform/, amd-khushbu for library/ (ran: gh)
AnaghaRaoAMD (10-28) ×2, GPU coverage: only contiguous BSHD / BHSD launch on the GPU, and the padded and packed cases never touch the kernel, so add padded-D and packed-QKV GPU cases; test_bshd_and_bhsd_agree adds no check 🟡 OPEN, valid ×2. The numeric file launches only bhsd / bshd (test_fmha_mfma_free_stride_numeric.py:29). The layout tests gather through numpy, and the agree test only re-asserts each layout's pass (:69). The support-matrix line, "any Q/K/V/O layout … numeric-verified on gfx942/gfx950" (SUPPORT_MATRIX.md:118), overstates this: only contiguous layouts are verified, and only on gfx942 in CI (read)
AnaghaRaoAMD (10-28) ×2, verify tool: backend="python" is justified by a "hipcc" path that compile_kernel does not have; bf16 maps to np.float16, but is_valid_spec rejects bf16 🟡 OPEN, valid ×2. Under ROCKE_CPP_STRICT=1, the default C++ backend lowers the free-stride kernel byte-identically to the Python lowerer (gfx942 none / causal, gfx950 GQA), so the override at sdpa_fwd_free_stride_verify.py:132 can go (ran: probe). bf16 is still rejected on the merge (fmha_mfma.py:154, which develop has not touched), so the mapping at :52 and :231 is dead code today (read)
AnaghaRaoAMD (10-28) nit: "ticket 2" / "Step 1" plan labels 🟡 OPEN, valid: still at sdpa_fwd_free_stride_verify.py:3, test_sdpa_free_stride_layouts.py:4 and :115 (read)
Bots / CI 🟢 At 13101557: 44 check runs passed, 51 skipped and none failed. The policy bot and the four required checks pass (pre-commit and the Multi-Arch, Component and Math CI summaries). Multi-Arch CI passed on its second attempt. The first attempt failed only on a pytest timeout in test_hip_device_properties_layout.py on both Windows gfx110X shards, a test this PR does not touch. All four Test hipkernelprovider shards pass (gfx94X Linux, gfx110X Windows). The new CPU tests pass (9 and 10). rocke_library_gpu_pytest ran test_fmha_mfma_free_stride_numeric.py on gfx942 hardware: 5 passed on both shards, with no torch gate. On gfx110X it skips as non-CDNA. PR CI has no gfx950 test job, so the gfx950 numerics are author-reported only. The hipDNN superbuilds pass (read). The PR is MERGEABLE but BLOCKED: develop requires every review thread to be resolved, and of 16 threads, the 11 from 10-20 are resolved while AnaghaRaoAMD's five from 10-28 are open with no author reply (ran: gh)
Our checks 🟢
• Merge: clean with develop f22bed43 (22 behind, 7 ahead). Among the PR's files, develop's two overlapping commits (#14415, #14394) touch only CMakeLists.txt and SUPPORT_MATRIX.md, and the PR's lane entries survive the auto-merge (ran).
• Interdiff since 6fad3ac7: a force-push. The three varlen commits are unchanged, and four new commits add the free-stride mode; the fmha_sdpa_fwd.py scaffold they add is deleted again within the PR (ran: git). The new branch keeps the default path's mask semantics (q_pos_base, causal offset 0), moves the batch offset into an i64 per-tensor pointer rebase, and is Python-only with no C++ twin, as the body states (read).
• Build: the native build and 33 ctests are OK (1 skipped) (ran).
• Byte identity: GREEN at llvm20 / 22 / 23 for fmha_mfma's 5 parity configs (0 bad), and the C++/Python attention parity tests pass (2 passed at each flavor). These cover the default path, which is unchanged; the free-stride kernel is not in the parity fixtures (ran).
• Goldens: the representative gate passes at llvm20 / 22 / 23 / 24. The differential golden (ll at all four flavors, and canonical) has no unblessed change and nothing missing (ran).
• Tests: the PR's 6 test files on the merge give 53 passed and 5 skipped; the skips are the GPU numeric file, with no GPU visible (ran).
• Compile (ROCm clang 24, -O3): the 5 default-path fmha_mfma configs compile with 0 failures, 0 newly rejected and no spills (ran). The free-stride kernel compiles and runs in CI's gfx942 job (read).
• Rules: the scan finds no tracker IDs, perf numbers or env reads in added lines, though it misses the plan labels above. None of the 7 commits or their messages has a perf number, and no links are added (ran)

Net-new: none.

Utility: 🟢 Positive — layout-general fmha_mfma plus varlen host adapters; default kernel unchanged, gfx942-verified, dispatch deferred.


Legend: 🔴 blocking · 🟠 should-fix · 🟡 minor nit · 🟢 info/good · ⚪ retracted

Follow-up review: a local qwen3.6 model made the independent first pass. The orchestrator (Claude Opus 5.5) verified it. The checks were an interdiff of the PR's own change since the head we reviewed (6fad3ac7): a range-diff of the force-push and a read of the four new free-stride commits. They also covered a merge with develop f22bed43: the native build and ctests, byte identity at llvm20 / 22 / 23 with the C++/Python parity tests, the representative and differential goldens, the PR's six test files, and a ROCm clang 24 compile of the family's parity configs. A strict-mode probe lowered the free-stride kernel through both lowerers. The threads, approvals, branch rules and the logs of both CI attempts at head were read. The orchestrator owns this merge-worthiness call. It flags whether a closer look is needed; it is not an approval or a rejection.

@arai713
arai713 force-pushed the users/arai/sdpa_seqlens branch from 1310155 to c3bc864 Compare October 29, 2026 17:54
@cgmillette

Copy link
Copy Markdown
Contributor

Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max

PR #13953 — free-stride SDPA forward and varlen host adapters (arai713, AICK-2518, AICK-2521, AICK-2618) — 🟢 Follow-up: no closer look needed — the code is unchanged by the rebase; the merge is still blocked on AnaghaRaoAMD's five 10-28 threads and a Windows CI re-run

Prior item Status
AnaghaRaoAMD, aosewski, amd-khushbu (10-20) ×4: a fully masked row (len_kv == 0) must give O = 0; int64 lengths narrowed before the range checks; int32 prefix-sum overflow; Q / KV lengths both set or both unset 🟢 FIXED 2ac0be99 (the rebased c5418282; only its CMake list hunk changed) (ran: git range-diff). The fixes: the row_has_mass guard (_sdpa_padding_mask.py:67), int64 range checks (_sdpa_seqlen.py:54), an int64 prefix sum that raises on int32 overflow (:96), and the pairing check (:36). test_sdpa_seqlen.py and test_sdpa_padding_mask.py pass on the merge (ran) and in gfx942 and gfx110X CI, with 18 and 5 passed (read)
AnaghaRaoAMD, amd-khushbu (10-20): cu_seqlens from cumsum(SEQ_LEN) instead of RFC 0014's ragged_offset 🟡 NARROWED / DEFERRED, unchanged. The adapter is scoped to fully packed layouts (_sdpa_seqlen.py:17) because it "only takes lengths"; ragged_offset support is a follow-up. Acceptable while nothing consumes it (read)
amd-khushbu (10-20, CHANGES_REQUESTED): the padding oracle duplicates sdpa_reference 🟡 DEFERRED, unchanged. The author's reason: "a bigger change I'd like to do as a follow-up". amd-khushbu's 10-27 approval supersedes the changes request (ran: gh)
amd-khushbu, aosewski (10-20): plan / tracker labels in the varlen docstrings 🟢 FIXED 2ac0be99: none are left in _sdpa_padding_mask.py or _sdpa_seqlen.py (ran: grep)
AnaghaRaoAMD (10-20) nits: one keep mask; the padded-row convention; land keep_key / row_is_valid with the shell's emitter 🟢 The first two are FIXED (references.py:57, :37) (read). 🟡 The third is DEFERRED to the free-stride masking follow-up (read)
Ours N1 🟡 (10-21): neither varlen test file was in a CTest list 🟢 Still FIXED. The rebase keeps all five entries: the four CPU files in rocke_library_attention_cpu_pytest and the numeric file in rocke_library_gpu_pytest. They sit next to develop's new #13889 entries and survive the merge (ran: git). All five ran in CI at this head (read)
CongMa13 (10-22, 10-27): threads, and our 10-26 comment, without an author reply 🟢 ADDRESSED. CongMa13 has posted nothing new since (ran: gh)
cgmillette (10-26) and amd-khushbu (10-27) APPROVED at c2b299dd 🟡 Stale, still counts. Both approvals predate the four free-stride commits, now 0f404c7e…c3bc8645 after the rebase. develop does not dismiss stale approvals or require a last-push approval. The two approvers cover both code-owner teams: cgmillette (rocke-platform) for platform/ and amd-khushbu (rocke-lib) for library/ (ran: gh)
AnaghaRaoAMD (10-28) ×2, GPU coverage: only contiguous BSHD / BHSD run on the GPU (add padded-D and packed-QKV cases); test_bshd_and_bhsd_agree adds no check 🟡 OPEN, valid ×2: unchanged, no reply, threads unresolved. The GPU tests still launch only bhsd / bshd (test_fmha_mfma_free_stride_numeric.py:29). The agree test asserts only ok_a and ok_b (:69). SUPPORT_MATRIX.md:121 still says "any Q/K/V/O layout … numeric-verified on gfx942/gfx950", but CI verifies only contiguous layouts, and only on gfx942 (read)
AnaghaRaoAMD (10-28) ×2, verify tool: backend="python" is justified by a "hipcc" path that compile_kernel lacks; bf16 maps to np.float16, but is_valid_spec rejects bf16 🟡 OPEN, valid ×2: unchanged, no reply. On this merge with ROCKE_CPP_STRICT=1, the default C++ backend lowers the free-stride kernel identically to the Python lowerer (gfx942 MHA, gfx942 causal GQA, gfx950 GQA). So the override at sdpa_fwd_free_stride_verify.py:132 can go (ran: probe). bf16 is still rejected (fmha_mfma.py:154), so the mapping at :52 and the --dtype choice at :231 are dead code (read)
AnaghaRaoAMD (10-28) nit: "ticket 2" / "Step 1" plan labels 🟡 OPEN, valid, unchanged: still at sdpa_fwd_free_stride_verify.py:3, test_sdpa_free_stride_layouts.py:4 and :115 (ran: grep)
Bots / CI 🟡 At c3bc8645: 42 check runs passed, 50 skipped and 3 failed. The failures are the required Multi-Arch CI Summary and both Windows gfx110X Test hipkernelprovider shards.
• Windows re-run: both shards failed in setup: choco install ninja got HTTP 429 (Too Many Requests) from the Chocolatey feed, so artifacts and tests were skipped.
• Windows first attempt: both shards failed on pytest timeouts in tests this PR does not touch: test_hip_device_properties_layout.py, which also times out on other PRs' gfx110X runs, and test_direct_kernel_cache.py::test_wave32_plans_launch_wave32_blocks[9].
• gfx94X: shard 1 hit one timeout in test_rocke_ci_static.py's golden SHA check (also seen on another PR) and passed on the re-run. Both shards now pass 49/49. rocke_library_gpu_pytest ran test_fmha_mfma_free_stride_numeric.py on gfx942, with 5 passed on each shard. The four CPU files pass (9, 10, 18 and 5), and so does test_numeric_references.py (11). On gfx110X the numeric file skips as non-CDNA, and there is still no gfx950 test job.
• Other checks: pre-commit, the Component and Math summaries, the policy bot and the hipDNN superbuilds pass.
• Merge state: MERGEABLE but BLOCKED by the red required check and by 5 of 16 threads unresolved → re-run the two Windows shards (read: logs; ran: gh)
Our checks 🟢
• Interdiff since 13101557: a rebase onto develop 239f40a0. 5 of the 7 commits are patch-identical. 2ac0be99 and c3bc8645 differ only in the platform/CMakeLists.txt conflict resolution, which keeps develop's #13889 list entries. 13 of the 15 files are byte-identical, and SUPPORT_MATRIX.md differs only by develop's rows (ran: git range-diff).
• Merge: clean with develop 2d1e830e (38 behind, 7 ahead). Of the PR's files, develop's newer commits touch only CMakeLists.txt and SUPPORT_MATRIX.md. They touch none of the attention kernel or numeric-reference files the free-stride path builds on (ran).
• Build: the native build and 33 ctests are OK (1 skipped) (ran).
• Byte identity: GREEN at llvm20 / 22 / 23 for fmha_mfma's 5 configs, and the C++/Python attention parity tests pass (2 passed at each flavor) (ran).
• Goldens: the representative gate passes. The differential golden has nothing missing, and its 180 unblessed configs are develop's own (ran).
• Tests: the PR's 6 test files on the merge give 53 passed and 5 skipped. The skips are the GPU numeric file, with no GPU visible (ran).
• Compile (ROCm clang 24, -O3): fmha_mfma's 5 configs compile with 0 failures, 0 newly rejected and no spills (ran).
• Rules: apart from the three plan labels above, the added lines and the 7 commit messages have no tracker IDs, PR numbers, links, perf numbers or env reads (ran)

Net-new: none.

Utility: 🟢 Positive — layout-general fmha_mfma plus varlen host adapters; default kernel unchanged, gfx942-verified, dispatch deferred.


Legend: 🔴 blocking · 🟠 should-fix · 🟡 minor nit · 🟢 info/good · ⚪ retracted

Follow-up review: a local qwen3.6 model made the independent first pass. The orchestrator (Claude Opus 5.5) verified it. The checks were a range-diff of the rebase since the head we reviewed (13101557) and a merge with develop 2d1e830e. On the merge they covered the native build and ctests, byte identity at llvm20 / 22 / 23 with the C++/Python parity tests, the representative and differential goldens, the PR's six test files, and a ROCm clang 24 compile of the family's parity configs. A strict-mode probe lowered the free-stride kernel through both lowerers. The threads, approvals and branch rules were read, along with the logs of both CI attempts at head. The orchestrator owns this merge-worthiness call. It flags whether a closer look is needed; it is not an approval or a rejection.

@cgmillette

Copy link
Copy Markdown
Contributor

Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max

PR #13953 — free-stride SDPA forward and varlen host adapters (arai713, AICK-2518) — 🟡 Follow-up: fixes in; rebase needed

Prior item Status
AnaghaRaoAMD, aosewski, amd-khushbu (10-20) ×5: masked row, int64 checks, overflow, pairing, labels 🟢 FIXED bd04b036
• unchanged by the rebase (ran: range-diff)
• _sdpa_seqlen.py:36, :54, :96; _sdpa_padding_mask.py:67; no plan labels left (read)
• seqlen + padding-mask tests 23 ✓ on the merge (ran)
AnaghaRaoAMD, amd-khushbu (10-20): cu_seqlens from cumsum, not ragged_offset 🟡 DEFERRED, unchanged
• scoped to fully packed layouts (_sdpa_seqlen.py:17); ragged_offset is a follow-up (read)
• acceptable while nothing consumes the adapter
amd-khushbu (10-20, CHANGES_REQUESTED): oracle duplicates sdpa_reference 🟡 DEFERRED, unchanged
• author: "a bigger change I'd like to do as a follow-up"
• acceptable; amd-khushbu approved on 10-27
AnaghaRaoAMD (10-20) nits ×3: one keep mask; padded-row docstring; emitter 🟡 DEFERRED (1 of 3)
• first two fixed: references.py:57, :43 (read)
• the keep_key / row_is_valid emitter waits for the free-stride masking follow-up
Our 10-21 N1 🟡: varlen tests in no CTest list 🟢 FIXED
• the union keeps all 5 PR entries and develop's new test_attention_unified_2d_lse_ir.py (ran: trial merge)
• all 5 ran in CI at c3bc8645; none yet at this head (read)
CongMa13 (10-22, 10-27): threads without an author reply 🟢 ADDRESSED
• author replied 10-26 and resolved all 11 threads from 10-20; nothing new since (ran: gh)
cgmillette (10-26), amd-khushbu (10-27) 🟢 APPROVED c2b299dd
• older head (the 3 varlen commits); still counts on develop
AnaghaRaoAMD (10-28) ×2: padded-D and packed-QKV GPU cases; agree test adds nothing 🟢 FIXED 6a76d246
• padded-D ×4 (free_stride_numeric.py:59), packed QKV ×2 (:81), bit-exact check of 4 placements (:120); SUPPORT_MATRIX.md:135 updated (read)
• author-reported 11/11 on gfx942 + gfx950; no CI run yet; 11 skip on this gfx1151 host (ran)
• all 5 threads from 10-28 open, no reply: arai713 to reply, AnaghaRaoAMD to resolve
AnaghaRaoAMD (10-28) ×2, verify tool: backend="python" override; dead bf16 mapping 🟢 FIXED 6a76d246
• default backend (free_stride_verify.py:184): C++ = Python lowering, 8 free-stride variants, strict mode; compiles for gfx942, gfx950 (ran: probe)
• f16 only: _NP_DTYPE (:60), --dtype (:299) (read)
AnaghaRaoAMD (10-28) nit: "ticket 2" / "Step 1" plan labels 🟢 FIXED 6a76d246
• none left in the PR's lines (read: grep)
Bots / CI 🟡 Not run (conflicts)
• 3 ✓: pull_request_target checks only
• no build, test, pre-commit or clang-tidy run: 1 file conflicts with develop
Our checks 🟡 Conflict; union merges clean
• range-diff since c3bc8645: only the GPU-test commit changed; CMake list conflicts with 7cd54ff9, union merges clean (ran)
• union merge: build + ctest 35 ✓ (1 skip); byte identity fmha_mfma GREEN ×3; representative + differential goldens OK (ran)
• the PR's 6 test files: 53 ✓ / 11 skip (CDNA-only), merge and head; rules 1–3 clean (ran)
# Sev file:line Finding
N1 🟡 PR body: Test Plan, Test Result The squash message is stale for the rewritten GPU test
• impact: develop's commit would say GPU parity covers only BSHD/BHSD, and repeats "## Test Result" with an older section
• fix: name the 4 placements and the bit-exact check; drop the second section

Utility: 🟢 Positive — layout-general fmha_mfma plus varlen host adapters; default kernel byte-identical.


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: range-diff since c3bc8645, trial merge with develop (CMake list union), native build + ctest, byte identity (llvm20/22/23), representative + differential goldens, the PR's 6 test files at head and on the merge, C++ lowering probe, threads and CI via gh. Flags whether a closer look is needed; not an approval or rejection.

arai713 and others added 7 commits October 31, 2026 03:28
…port

- dense_attention_reference gains optional seqlen_q / seqlen_kv. None keeps
  the exact unpadded arithmetic. When set, keys past seqlen_kv are masked,
  a row with no valid key yields O = 0, and padded query rows are zeroed.
- _sdpa_seqlen.py: host adapter validating hipDNN's per-sequence lengths at
  the boundary (integer dtype, per-batch count, 0 <= len <= S_max) and
  returning the flat per-batch int32 arrays the shell reads.
- _sdpa_padding_mask.py: host specification of the kernel-side mask --
  keep_key (the shell's extra_mask_predicate hook) and row_is_valid (the
  epilogue's padded-row zeroing), with the emitter form documented. The
  live IR emitter is deferred to the shell, where it carries its
  byte-identity C++ mirror. This module is the host reference it must match,
  cross-checked against dense_attention_reference.

Kernel wiring and GPU validation wait on the shell (ST4) and the arbitrary-length
buffer clipping (ticket 8).
Ticket 12 (varlen/THD forward) needs hipDNN's ragged per-sequence lengths
converted into the cumulative cu_seqlens the varlen kernels (fmha_varlen)
address with. Extend the shared SDPA length adapter with sdpa_cu_seqlens,
which validates the per-sequence counts and returns (B+1,) int32 exclusive
prefix sums (leading 0, last element = packed token total). The validator
is refactored to make the upper bound optional, since ragged tokens are
packed with no padding and therefore no per-sequence S_max.
Host numpy only, no kernel emission. Covers the host/adapter half of
ticket 12, the non-causal unified no-mask path and
arbitrary-length tails are separate work.
Correct the fully-masked-row spec to require an explicit row_has_mass epilogue
guard, and make the padding test run the real online-softmax recurrence with that guard plus
len_kv==0 and causal cases. Scope the ragged adapter to packed-only, validate
lengths on a widened int64 before narrowing, accumulate cu_seqlens in int64 with
an int32 overflow check, and enforce the Q/KV both-set-or-neither pairing. Build
one keep mask in the oracle, drop the internal tracker labels, and enroll both
tests in the attention CPU CTest list.
Add fmha_sdpa_fwd, the net-new MFMA-body layout shell (hipDNN
requirement 2), reusing mfma_attention_fwd_inner_body for the attention
math and adding only the addressing layer. This scaffold covers the
contiguous BHSD dense case: batch is threaded through the grid z-axis
plus the inner body's K/V offset hooks, with Q/O batch folded into the
tile base. BSHD, general strides, and packed QKV are declared on the
spec but gated off in is_valid_spec as Phase 3. CPU-only build/smoke
tests cover the validator gates, ABI signature, grid, and that a valid
spec lowers to a KernelDef.
Make fmha_sdpa_fwd read Q/K/V/O from any memory layout by rebasing each
tensor's base pointer to the CTA's batch slice (batch_idx * stride_*_batch)
and letting the shared body address head/token through its runtime strides.
Batch, head and token offsets are all runtime, so BSHD, BHSD, general B/H/S
strides and packed QKV build the same kernel, with nothing baked and the
contiguous Q/O fold removed.

Add _sdpa_fwd_strides.contiguous_strides as the launcher's single source of
truth for the (batch, head, token) element strides of the bhsd/bshd orders,
plus host tests: the layout contract and the updated validator/build smoke tests.
Add an opt-in free_stride mode that rebases Q/K/V/O base pointers per batch
and declares per-tensor batch strides, so one kernel reads any layout
(BSHD/BHSD/general/packed) instead of the BSHD-contiguous seqlen fold.The
free-stride path is Python-only for now. Retire the fmha_sdpa_fwd scaffold,
add a CPU ABI test and a GPU verify harness.
Extract the verify harness core into a reusable run_verify() and add a
CDNA-gated pytest (skips off gfx942/gfx950) that checks fmha_mfma(free_stride=
True) against the numpy reference, confirming layout invariance of the
per-batch pointer rebase.

Four placements over one logical problem: contiguous BSHD and BHSD, a padded
head dim ([B,H,S,D+8] sliced to D, so every stride carries the padding while
the head dim keeps unit stride), and packed QKV ([B,S,3,H,D], the three
operands aliasing one allocation at base offsets 0, H*D and 2*H*D). The
padded and packed cases fail if fed contiguous strides or zeroed base
offsets, so they test the kernel rather than numpy. All four agree
bit-for-bit, which the suite asserts directly.

The harness runs on the default backend rather than forcing the Python one,
and bf16 is dropped from the dtype table since is_valid_spec rejects it.

Verified: 11/11 on gfx942 and gfx950.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@arai713
arai713 force-pushed the users/arai/sdpa_seqlens branch from 6a76d24 to c9121ae Compare October 31, 2026 07:33
@cgmillette

Copy link
Copy Markdown
Contributor

Automated review: Model - qwen3.6:35b-a3b-coding-mtp-q4_K_M, effort: max

PR #13953 — free-stride SDPA forward and varlen host adapters (arai713, AICK-2518) — 🟡 Follow-up: clean rebase; CI running

Prior item Status
AnaghaRaoAMD, amd-khushbu (10-20) ×3: ragged_offset; oracle onto sdpa_reference; emitter 🟡 DEFERRED, unchanged
• author: packed-only scope (_sdpa_seqlen.py:16); emitter lands with the shell
• author: oracle move is "a bigger change… as a follow-up"
• acceptable: adapter and spec unused yet; amd-khushbu approved since
cgmillette (10-26), amd-khushbu (10-27) 🟢 APPROVED c2b299dd
• older head; still counts on develop
AnaghaRaoAMD (10-28) ×5: GPU placements, agree test, verify tool, labels 🟢 FIXED c9121ae9
• same code we checked at 6a76d246 (ran: blob compare)
• 11 GPU cases: first CI run on gfx94X now; gfx950 lane skipped
• 5 threads open, no reply: arai713 to reply, AnaghaRaoAMD to resolve
Our 10-31: CMake list conflict with develop; rebase needed 🟢 FIXED c9121ae9
• rebased on 7cd54ff9, 0 behind; tree = our 10-31 union merge (ran)
• only the CMake list changed; 10-20 fixes now 5a4094b1 (ran: range-diff)
• list: develop's test_attention_unified_2d_lse_ir.py + the PR's 4 CPU entries (ran)
Our 10-31 N1 🟡: squash message stale for the rewritten GPU test 🟡 OPEN
• body unchanged since our last review (ran: gh)
• still "BSHD and BHSD" only; ## Test Result twice
• arai713 to edit the body
Bots / CI 🟡 Running
• running: gfx94X hipkernelprovider ×2, hipdnn-integration-tests; Windows gfx110X math-libs
• 31 ✓ · 4 running · 0 ✗
• merge: clean (ran)
Our checks 🟢 Clean
• merge vs 7cd54ff9: clean, 0 behind (ran)
• build + ctest 34 ✓ / 1 skip; byte identity fmha_mfma GREEN ×3 (default path); goldens OK (ran)
• tests 53 ✓ / 11 skip (CDNA-only); compile 5 ✓, 0 spills; rules 1–3 clean (ran)

Net-new: none.

Utility: 🟢 Positive — layout-general fmha_mfma plus varlen host adapters; default kernel byte-identical.


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: range-diff and tree compare since 6a76d246, merge with develop, native build + ctest, byte identity (llvm20/22/23), representative + differential goldens, the PR's 6 test files, compile, threads and CI via gh. Flags whether a closer look is needed; not an approval or rejection.

@arai713
arai713 merged commit 4573b28 into develop Nov 2, 2026
165 of 167 checks passed
@arai713
arai713 deleted the users/arai/sdpa_seqlens branch November 2, 2026 18:45
ex-rzr added a commit that referenced this pull request Nov 3, 2026
…ke/dense-paged-kv

Conflicts with #13205 (gfx942 bf16 cfvst, K LDS double buffering, V register
prefetch) and the attention CPU test list (#13953).

All hunks keep both sides:
- gfx942 _async_load takes develop's kbuf slot offset and this branch's
  per-wave page lookup. The KDB/VPF pipeline requires persistent + cfvst, and
  paged K/V takes neither, so paged never reaches it.
- Name-injectivity table: lds_num_buffers is buildable on develop and the paged
  fields are buildable here, so neither stays in the unbuildable set; both new
  bases (persistent KDB/VPF, paged) are kept.
- The gfx942 numeric file keeps the paged class and develop's pipeline tests.
- CMake: develop's SDPA host tests plus the non-Windows gfx942 dense block.

No golden re-bless needed: the representative and differential goldens pass as
merged.
@assistant-librarian

Copy link
Copy Markdown
Contributor

TheRock Submodule Bump Activity

Newest first

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.

6 participants