Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions python/cudnn/frost/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -506,8 +506,9 @@ expressible, with one discipline separating them:
- The box is pure data: per-axis fields on `Capabilities`. Covers most of the
surface; adding an engine is writing a row, not logic.
- A notch is a rule in `mismatch()` gated by a conjunction flag on the row
(e.g. `bottom_right_padded_seq_q: bool`). The matcher encodes the SHAPE of the
interaction once; each engine's row supplies the VERDICT. When a future
(e.g. `padded_stats: bool` — padding mask + generate_stats needs the
per-batch LSE trim). The matcher encodes the SHAPE of the interaction once;
each engine's row supplies the VERDICT. When a future
kernel supports the conjunction, flip its flag -- never edit the matcher.
This is what keeps interaction checks from regressing into a per-engine
if-ladder: shared code may know about kinds of interactions, never about
Expand Down
21 changes: 0 additions & 21 deletions python/cudnn/sdpa/fwd/api_dsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -847,15 +847,6 @@ def check_support(self) -> bool:
self.causal_bottom_right and not self.is_causal,
"SM100 DSL SDPA: causal_bottom_right requires is_causal=True",
)
# Backstop for the engines.bottom_right_padded_seq_q gate: with dense
# per-batch Q lengths the kernel's BR diagonal (anchored at the global
# S_q) is wrong for any batch with seq_len_q[b] < S_q.
self._value_error_if(
self.causal_bottom_right and self.seq_q_lens_present,
"SM100 DSL SDPA: causal_bottom_right with per-batch seq_len_q is not "
"supported (kernel anchors the BR diagonal at the global S_q, not "
"seq_len_q[b])",
)
if self.thd:
self.seq_kv_lens_present = True
self._not_implemented_error_if(
Expand Down Expand Up @@ -884,18 +875,6 @@ def check_support(self) -> bool:
self.seq_q_lens_present and self._fp8,
"seq_q_lens_present (dense padded-Q LSE trim) is not plumbed for the FP8/MXFP8 kernels",
)
# Dense BR + per-batch Q lengths: the kernels anchor the bottom-right
# diagonal with the GLOBAL S_q (compute_kv_loop_bounds: causal_diag =
# seq_kv_len - seqlen_q with the scalar S_q), but cuDNN semantics
# anchor it at the per-batch (seq_len_q[b], seq_len_kv[b]) corner —
# batches with seq_len_q[b] < S_q get over-masked. KV-only padding is
# exact (actual Q length == S_q), so only this combination is gated.
self._value_error_if(
self.causal_bottom_right and self.seq_q_lens_present,
"SM100 DSL SDPA: causal_bottom_right with per-batch seq_len_q (dense "
"padded-Q trim) is not supported — the kernel anchors the BR diagonal "
"at the global S_q, not seq_len_q[b]/seq_len_kv[b]",
)
# KV-tail correctness: the kernel zero-fills the last KV tile via TMA
# OOB but only *masks* those columns on the padded / causal paths. A
# ragged S_kv is safe when a padding mask carries the real lengths, or
Expand Down
15 changes: 0 additions & 15 deletions python/cudnn/sdpa/fwd/engines.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,6 @@ class Capabilities:

causal: bool = False
bottom_right: bool = False
# Kernel gap (pre-existing): for a DENSE padded graph the BR diagonal is
# computed as seq_len_kv[b] - GLOBAL S_q, but cuDNN semantics for a dense
# padded graph carrying per-batch seq_len_q anchor it at
# (seq_len_q[b], seq_len_kv[b]) — any batch with seq_len_q[b] < S_q gets a
# wrongly shifted diagonal (extra zeroed rows at the top). KV-only padding
# (no seq_len_q tensor) is unaffected: there the actual Q length IS S_q.
bottom_right_padded_seq_q: bool = False
swa: bool = False
padded: bool = False
sink: bool = False
Expand Down Expand Up @@ -372,11 +365,6 @@ def mismatch(capabilities: Capabilities, facts: "ga.SdpaGraphFacts", knobs: Opti
return "bottom-right alignment requires a causal upper bound (plain or right-widened)"
if not capabilities.bottom_right:
return "graph uses bottom-right causal, which this kernel does not support"
if facts.padded and not facts.thd and facts.seq_q_t is not None and not capabilities.bottom_right_padded_seq_q:
return (
"bottom-right causal with a dense padding mask carrying per-batch seq_len_q is not "
"supported (kernel anchors the BR diagonal at the global S_q, not seq_len_q[b])"
)
if facts.padded and facts.wants_stats and not facts.thd and not capabilities.padded_stats:
return "padding mask with generate_stats is not supported yet (per-batch seq_len_q LSE trim not plumbed)"

Expand Down Expand Up @@ -578,7 +566,6 @@ def _sm80_spec() -> EngineSpec:
right_band_widening=True,
causal=True,
bottom_right=True,
bottom_right_padded_seq_q=True,
swa=True,
padded=True,
sink=True,
Expand Down Expand Up @@ -673,7 +660,6 @@ def _sm120_spec() -> EngineSpec:
dtypes=frozenset({cudnn.data_type.HALF, cudnn.data_type.BFLOAT16}),
causal=True,
bottom_right=True,
bottom_right_padded_seq_q=True,
swa=True,
right_band_widening=True,
padded=True,
Expand Down Expand Up @@ -1014,7 +1000,6 @@ def _sm120_fp8_spec() -> EngineSpec:
is_fp8=True,
causal=True,
bottom_right=True,
bottom_right_padded_seq_q=True,
swa=True,
right_band_widening=True,
padded=True,
Expand Down
27 changes: 17 additions & 10 deletions python/cudnn/sdpa/fwd/kernels/_common_sm100.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,20 +702,27 @@ def _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, scalar_seqlen_kv):
return scalar_seqlen_kv

@cute.jit
def _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, scalar_seqlen_q, n_batch):
"""Per-sequence Q length for the bottom-right causal diagonal.

THD anchors the BR diagonal at the per-sequence corner
(seq_len_q[b], seq_len_kv[b]): the actual Q length is the cu_seqlen_q
difference from the packed [kv_lens | cu_q | cu_kv] metadata buffer
(same layout _thd_decode / _thd_tma_offsets read). Dense graphs (and
every non-BR mask, where seqlen_q only feeds the unused diagonal)
keep the scalar S_q, so this folds out unless THD_VARLEN and
CAUSAL_BOTTOM_RIGHT are both set."""
def _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, scalar_seqlen_q, n_batch, seq_q_lens_tensor=None):
"""Per-batch Q length for the bottom-right causal diagonal.

Bottom-right anchors the diagonal at the per-batch corner
(seq_len_q[b], seq_len_kv[b]). THD reads the actual Q length as the
cu_seqlen_q difference from the packed [kv_lens | cu_q | cu_kv] metadata
buffer (same layout _thd_decode / _thd_tma_offsets read); dense padded
graphs carrying per-batch Q lengths read the SEPARATE (B,)-int32
``seq_q_lens_tensor`` (cuDNN SEQLEN_Q style), clamped to [0, S_q].
KV-only padding (and every non-BR mask, where seqlen_q only feeds the
unused diagonal) keeps the scalar S_q, so both reads fold out unless
CAUSAL_BOTTOM_RIGHT is set together with THD_VARLEN or
SEQ_Q_LENS_PRESENT (mutually exclusive by _validate_params).
"""
if cutlass.const_expr(int(getattr(CFG, "THD_VARLEN", 0)) == 1 and int(CFG.BOTTOM_RIGHT) == 1):
cu = cutlass.make_array_view(seq_kv_lens_tensor)
q0 = n_batch
return cutlass.Int32(cu[q0 + batch_idx + cutlass.Int32(1)]) - cutlass.Int32(cu[q0 + batch_idx])
if cutlass.const_expr(int(getattr(CFG, "SEQ_Q_LENS_PRESENT", 0)) == 1 and int(CFG.BOTTOM_RIGHT) == 1):
arr = cutlass.make_array_view(seq_q_lens_tensor)
return cute.math.max(cutlass.Int32(0), cute.math.min(cutlass.Int32(arr[batch_idx]), scalar_seqlen_q))
return scalar_seqlen_q

_thd_on = int(getattr(CFG, "THD_VARLEN", 0))
Expand Down
37 changes: 21 additions & 16 deletions python/cudnn/sdpa/fwd/kernels/prefill_d128_f16_sm100.py
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@ def _tmaldg_warp_group(
kv_left, kv_right = _nomask_range_split(seqlen_kv, split_idx)
else:
eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds_init = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)
kv_left = bounds_init.left
kv_right = bounds_init.right
Expand Down Expand Up @@ -834,7 +834,7 @@ def _tmaldg_warp_group(
kv_left, kv_right = _nomask_range_split(seqlen_kv, split_idx)
elif cutlass.const_expr(CFG.MASK_FLAGS != 0):
eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds_next = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)
kv_left = bounds_next.left
kv_right = bounds_next.right
Expand Down Expand Up @@ -1103,7 +1103,7 @@ def _mma_warp_group(
kv_left, kv_right = _nomask_range_split(seqlen_kv, split_idx)
else:
eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds_init = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)
kv_left = bounds_init.left
kv_right = bounds_init.right
Expand Down Expand Up @@ -1311,7 +1311,7 @@ def _mma_warp_group(
kv_left, kv_right = _nomask_range_split(seqlen_kv, split_idx)
else:
eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds_next = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)
kv_left = bounds_next.left
kv_right = bounds_next.right
Expand Down Expand Up @@ -1593,7 +1593,7 @@ def _softmax_warp_group(

eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)

eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)

softmax_wg_base_const = CFG.SOFTMAX_WG0_BASE if sub_tile_id == 0 else CFG.SOFTMAX_WG1_BASE
Expand Down Expand Up @@ -1725,7 +1725,7 @@ def _softmax_warp_group(
is_valid_tile = nxt_v & cutlass.Int32(1)
sched_state = advance(sched_state, CFG.SCHEDULER_STAGES)
eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)


Expand Down Expand Up @@ -1797,7 +1797,7 @@ def _correction_warp_group(

eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)

eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)

while is_valid_tile > cutlass.Int32(0):
Expand Down Expand Up @@ -1962,15 +1962,20 @@ def _correction_warp_group(
sO_sub_base = sO[qs].base

for chunk_idx in cutlass.range_constexpr(N_CHUNKS_O):
o_addr = tmem_base_epi + cutlass.Int32(tmem_O_off + chunk_idx * O_CHUNK)
o_chunk = nvvm.tcgen05_ld(
"32x32b",
nvvm.make_tmem_ptr(o_addr, cutlass.Float32),
num=O_CHUNK,
o_fp16 = cutlass.Vector.from_elements(
tuple(STORAGE_DTYPE(0.0) for _ in range(O_CHUNK)),
STORAGE_DTYPE,
)
nvvm.tcgen05_wait(kind=nvvm.Tcgen05Wait.LOAD)
o_scaled = o_chunk * inv_sum
o_fp16 = o_scaled.to(STORAGE_DTYPE)
if cutlass.const_expr(not MAY_BE_EMPTY) or (bounds.right > bounds.left):
o_addr = tmem_base_epi + cutlass.Int32(tmem_O_off + chunk_idx * O_CHUNK)
o_chunk = nvvm.tcgen05_ld(
"32x32b",
nvvm.make_tmem_ptr(o_addr, cutlass.Float32),
num=O_CHUNK,
)
nvvm.tcgen05_wait(kind=nvvm.Tcgen05Wait.LOAD)
o_scaled = o_chunk * inv_sum
o_fp16 = o_scaled.to(STORAGE_DTYPE)

col_offset_const = (chunk_idx * O_CHUNK) % D_BLOCK_SIZE
block_idx_const = (chunk_idx * O_CHUNK) // D_BLOCK_SIZE
Expand Down Expand Up @@ -2015,7 +2020,7 @@ def _correction_warp_group(
is_valid_tile = nxt_v & cutlass.Int32(1)
sched_state = advance(sched_state, CFG.SCHEDULER_STAGES)
eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)

# End-of-warp tmem_dealloc: under cga2 each corr lane ALSO DSMEM-arrives
Expand Down
16 changes: 8 additions & 8 deletions python/cudnn/sdpa/fwd/kernels/prefill_d192_d128_f16_sm100.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ def _tmaldg_warp_group(
kv_left, kv_right = _nomask_range_split(seqlen_kv, split_idx)
else:
eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds_init = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)
kv_left = bounds_init.left
kv_right = bounds_init.right
Expand Down Expand Up @@ -886,7 +886,7 @@ def _tmaldg_warp_group(
kv_left, kv_right = _nomask_range_split(seqlen_kv, split_idx)
elif cutlass.const_expr(CFG.MASK_FLAGS != 0):
eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds_next = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)
kv_left = bounds_next.left
kv_right = bounds_next.right
Expand Down Expand Up @@ -1167,7 +1167,7 @@ def _mma_warp_group(
kv_left, kv_right = _nomask_range_split(seqlen_kv, split_idx)
else:
eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds_init = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)
kv_left = bounds_init.left
kv_right = bounds_init.right
Expand Down Expand Up @@ -1374,7 +1374,7 @@ def _mma_warp_group(
kv_left, kv_right = _nomask_range_split(seqlen_kv, split_idx)
else:
eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds_next = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)
kv_left = bounds_next.left
kv_right = bounds_next.right
Expand Down Expand Up @@ -1681,7 +1681,7 @@ def _softmax_warp_group(

eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)

eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)

softmax_wg_base_const = CFG.SOFTMAX_WG0_BASE if sub_tile_id == 0 else CFG.SOFTMAX_WG1_BASE
Expand Down Expand Up @@ -1816,7 +1816,7 @@ def _softmax_warp_group(
is_valid_tile = nxt_v & cutlass.Int32(1)
sched_state = advance(sched_state, CFG.SCHEDULER_STAGES)
eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)


Expand Down Expand Up @@ -1888,7 +1888,7 @@ def _correction_warp_group(

eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)

eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)

while is_valid_tile > cutlass.Int32(0):
Expand Down Expand Up @@ -2115,7 +2115,7 @@ def _correction_warp_group(
is_valid_tile = nxt_v & cutlass.Int32(1)
sched_state = advance(sched_state, CFG.SCHEDULER_STAGES)
eff_seqlen_kv = _resolve_seqlen_kv(seq_kv_lens_tensor, batch_idx, seqlen_kv)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch)
eff_seqlen_q = _resolve_seqlen_q(seq_kv_lens_tensor, batch_idx, seqlen_q, n_batch, seq_q_lens_tensor)
bounds = _bounds_for_tile_split(q_super_idx, eff_seqlen_q, eff_seqlen_kv, cta_in_pair, seq_q_lens_tensor, batch_idx, split_idx)

# End-of-warp tmem_dealloc: under cga2 each corr lane ALSO DSMEM-arrives
Expand Down
Loading