Skip to content

fix(cute/fp4): promote layout_DS extents to int64 to stop batch-stride int32 overflow - #387

Merged
DefTruth merged 1 commit into
vipshop:mainfrom
lewisword:fix/fp4-delta-s-tma-int32-overflow
Oct 22, 2026
Merged

DefTruth merged 1 commit into
vipshop:mainfrom
lewisword:fix/fp4-delta-s-tma-int32-overflow

Conversation

@lewisword

Copy link
Copy Markdown
Collaborator

Summary

The NVFP4 (CUDABackend(enable_fp4=True)) sm_120 persist-D path aborts with a CUTLASS TMA assertion for long attention sequences. The delta_s gmem layout in the fp4 launchers is built with int32 extents; the batch-mode block stride Nkv_pad * (Nq_pad/kBr) * Nh overflows INT32_MAX once the sequence exceeds ~70k tokens (at Nh=56, D=128), wraps negative, and trips the TMA descriptor assertion.

Fixes #386

Error

cute/atom/copy_traits_sm90_tma.hpp:983: ... Assertion
`(gmem_prob_stride[4]) < (uint64_t(1) << 40)' failed.
Aborted (core dumped)

Root cause

csrc/cuffpa/launch/cute_fp4_persist_d.cuh (same pattern in the split-D and split-D m4n2 launchers):

auto layout_DS =
    tile_to_shape(SmemLayoutAtomDS{}, make_shape(Nq_pad, Nkv_pad, Nh, Nb),
                  Step<_2, _1, _3, _4>{});

tile_to_shape -> blocked_product -> make_ordered_layout accumulates the block strides as products of these int32 values. The batch-mode stride is Nkv_pad * (Nq_pad/kBr) * Nh, which scales as N^2 and crosses INT32_MAX at roughly 70k attention tokens (B=1, H=56, D=128). The wrapped negative stride is reinterpreted as uint64 inside make_tma_copy_desc and aborts on the assertion.

Note the true stride (2.9e9 elements for an 81k-token sequence, ~11.6 GB in bytes) is far below the 2^40-byte TMA hardware limit - this is purely an int32 accumulation overflow, not a genuine TMA limit.

Minimal reproduction

import os
os.environ["FFPA_CUDA_ALLOW_SMALL_D"] = "1"
import torch
from ffpa_attn import CUDABackend, ffpa_attn_func

backend = CUDABackend(forward=True, backward=False, tensor_layout="NHD",
                      enable_fp4=True, fp4_smooth_v=False, fp4_hadamard=False)
# B=1, S=77000, H=56, D=128 (long-sequence video DiT self-attention)
q = torch.randn(1, 77000, 56, 128, dtype=torch.bfloat16, device="cuda")
k = torch.randn_like(q); v = torch.randn_like(q)
with torch.no_grad():
    ffpa_attn_func(q, k, v, forward_backend=backend)   # aborts

S=42000 succeeds; S=77000 aborts deterministically.

Fix

Promote the four dynamic extents to int64_t in make_shape in all three fp4 launchers (cute_fp4_persist_d.cuh, cute_fp4_split_d.cuh, cute_fp4_split_d_m4n2.cuh). The TMA atom's static type layout is unchanged, so no kernel-side changes are required.

Verification (RTX PRO 5000, sm_120f, torch 2.13.0+cu129, CUDA 12.9)

  • The minimal repro above aborts before the change and passes after it.
  • Numerics unchanged vs SDPA (cos ~0.982, the inherent NVFP4 quantization error), identical between short (42k) and long (77k) sequences.
  • End-to-end: a 362-frame (15s) 768x1344 video-DiT generation (MiniMax-H3, ~117k tokens) that previously aborted at the first denoise step now completes.

…e int32 overflow

The fp4 persist-D/split-D/m4n2 launchers build the delta_s TMA gmem
layout with all-int32 extents:

  auto layout_DS =
      tile_to_shape(SmemLayoutAtomDS{}, make_shape(Nq_pad, Nkv_pad, Nh, Nb),
                    Step<_2, _1, _3, _4>{});

tile_to_shape -> blocked_product -> make_ordered_layout accumulates the
block strides as products of these int32 values, so the batch-mode
stride is Nkv_pad * (Nq_pad/kBr) * Nh. That product scales as N^2 and
crosses INT32_MAX at roughly 70k attention tokens (B=1, H=56, D=128):
the wrapped negative stride is reinterpreted as uint64 inside
make_tma_copy_desc and aborts on the CUTLASS TMA assertion

  copy_traits_sm90_tma.hpp:983:
  Assertion `(gmem_prob_stride[4]) < (uint64_t(1) << 40)' failed.

The true stride (e.g. 2.9e9 elements for an 81k-token sequence, ~11.6
GB in bytes) is far below the 2^40-byte TMA hardware limit, so this is
purely an int32 accumulation overflow, not a TMA limit.

Promote the four dynamic extents to int64_t in all three launchers.
The TMA atom's static type layout is unchanged, so no kernel-side
changes are needed.

Verified on an RTX PRO 5000 (sm_120f): the minimal repro (B=1, H=56,
D=128, NHD, N=77000) aborts before and passes after the change, with
numerics unchanged vs SDPA (cos ~0.982, the inherent NVFP4
quantization error); an end-to-end 362-frame (15s) 768x1344 video-DiT
generation that previously aborted now completes.

Fixes vipshop#386

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Approval recommended

The narrowly scoped casts address overflow before stride accumulation consistently across all three launchers, with no unresolved findings.

No open issues 🎉

What changed in this PR

Fixes #386 by preventing 32-bit stride overflow in the CUDA backend’s NVFP4 delta_s layouts, which caused TMA descriptor assertions for long attention sequences.

Changes:

  • Cast all four dynamic layout extents to int64_t before stride accumulation in all three FP4 launchers.
  • Document the overflow cause while preserving layout geometry and kernel interfaces.
File Description
csrc/​cuffpa/​launch/​cute_fp4_split_d.cuh Widens split-D layout extents to prevent stride overflow.
csrc/​cuffpa/​launch/​cute_fp4_split_d_m4n2.cuh Applies the same protection to the M4N2 layout.
csrc/​cuffpa/​launch/​cute_fp4_persist_d.cuh Widens persist-D layout extents and explains the assertion’s cause.

Review effort: Balanced

@DefTruth DefTruth left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM~ Thanks for this fix!

@DefTruth
DefTruth merged commit fcd6ec5 into vipshop:main Oct 22, 2026
2 checks passed
DefTruth added a commit that referenced this pull request Oct 22, 2026
…lass)

Kernel-side companions to the fp4 launcher fix in #387: tail-tile O
direct stores (sm_120 fp16/fp8/fp4 persist/split/m4n2), sm_80 fp16 and
sm_89 fp8 Q/K/V + O plane bases, fp8 per-thread Q quantize row, fp4
quantize pre-kernel strides, and the native family (sm_80/sm_120 split_d
bases, split_kv decode kv_base/o_row_base, prefill helper offsets).
Value-neutral on non-overflowing shapes.

Verified on RTX PRO 5000 sm_120f (full-family build): pre-fix 11/14
overflow-trigger cases fail (illegal access / tail-row corruption);
post-fix 24/24 pass; 13-case normal-shape regression bitwise-identical;
perf within noise (+/-0.5%).
DefTruth added a commit that referenced this pull request Oct 22, 2026
…lass) (#388)

Kernel-side companions to the fp4 launcher fix in #387: tail-tile O
direct stores (sm_120 fp16/fp8/fp4 persist/split/m4n2), sm_80 fp16 and
sm_89 fp8 Q/K/V + O plane bases, fp8 per-thread Q quantize row, fp4
quantize pre-kernel strides, and the native family (sm_80/sm_120 split_d
bases, split_kv decode kv_base/o_row_base, prefill helper offsets).
Value-neutral on non-overflowing shapes.

Verified on RTX PRO 5000 sm_120f (full-family build): pre-fix 11/14
overflow-trigger cases fail (illegal access / tail-row corruption);
post-fix 24/24 pass; 13-case normal-shape regression bitwise-identical;
perf within noise (+/-0.5%).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FP4] CUTLASS TMA assertion failure (gmem_prob_stride[4] < 2^40) for long sequences: int32 overflow in persist-D layout_DS batch stride

4 participants