You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[NVIDIA][CuTe,Fwd,sm120] Implement Pack-GQA on SM120 (+ graceful SplitKV fallback) (#2656)
* [CuTe,Fwd,sm120] Fix use_tma_O crash on SM120 (issue #2649)
On SM120 (Blackwell GeForce / RTX PRO 6000 / DGX Spark) the forward kernel set
`use_tma_O = self.arch >= Arch.sm_90`, enabling the TMA-based O-store epilogue.
But SM120 does not build the TMA store atom (tma_atom_O is None), so any forward
call crashes in cpasync.tma_partition with:
AttributeError: 'NoneType' object has no attribute '_trait'
This makes the CuTe-DSL forward unusable on every SM120 GPU.
Restrict the TMA O-store to sm_90..sm_119, which is where the WGMMA-era epilogue
path is actually available:
self.use_tma_O = Arch.sm_90 <= self.arch < Arch.sm_120
SM120 falls back to the non-TMA register->gmem O store (already used for the
SM80 path), which is correct and what the CpAsync SM120 kernel expects.
Verified on RTX PRO 6000 Blackwell (sm_120, cc 12.0), torch 2.12.0+cu130,
nvidia-cutlass-dsl 4.5.2: forward now runs and matches PyTorch SDPA reference
for hdim 64/96/128, causal and non-causal (max abs err <= 8e-3 in bf16). Before
this fix every SM120 forward call raised the AttributeError above.
* [CuTe,Fwd,sm120] Implement Pack-GQA on SM120; graceful SplitKV fallback
Pack-GQA was only half-wired in the SM80/SM120 CpAsync forward: the epilogue
referenced PackGQA.store_O/store_LSE, but the Q-load and head-indexing used the
plain (unpacked) path. So pack_gqa=True crashed in pack_gqa.store_O (crd2idx on a
packed (h_idx, m_idx) coordinate against an unpacked mO layout).
This implements Pack-GQA end to end on SM120 (and SM80), mirroring the SM90 path:
- Reshape mQ/mO (head_idx=2) and mLSE (head_idx=1) via pack_gqa_layout so
qhead_per_kvhead folds into the seqlen mode ((qhead, seqlen)).
- Scheduler args use cute.size(mQ.shape[0]) (packed total rows) and seqlen_q_static
= mQ.shape[0][1] (logical seqlen), so causal/mask q_idx stay correct.
- Kernel head-indexing: when pack_gqa, num_head from the scheduler already indexes
the KV head (mQ/mK share nheads_kv); no division.
- Q-load: gather rows via PackGQA.load_Q (per-row (h_idx, m_idx) gmem pointers)
instead of the contiguous local_tile path.
SplitKV (num_splits>1) is an SM100-only feature (SM80/SM90 also assert it
unsupported); SM120 has no forward+combine path. Fall back to num_splits=1, which
is numerically correct, instead of crashing in _check_type on the fp32 partials.
Verified on RTX PRO 6000 Blackwell (sm_120): pack_gqa=True matches PyTorch SDPA
GQA/MQA reference (err <= 8.4e-3 bf16) AND is bit-identical to the unpacked path
(max |packed - unpacked| = 0.0) across MHA/GQA/MQA, causal/non-causal, hd 64/128,
seqlen 512-2048. num_splits=3 falls back and matches reference (err 6.8e-4).
Stacked on the SM120 use_tma_O fix (#2649).
* re-enable SM120 pack-gqa after rebase
* clean up SM120 pack-gqa split handling
* fix SM120 varlen pack-gqa offset
---------
Co-authored-by: drisspg <drisspguessous@gmail.com>
0 commit comments