Skip to content

Fix default-build for SM120/121 block-scaled GEMM: keep largest tile first - #3438

Open
krauqen wants to merge 1 commit into
NVIDIA:mainfrom
krauqen:sm121-default-build-fix
Open

Fix default-build for SM120/121 block-scaled GEMM: keep largest tile first#3438
krauqen wants to merge 1 commit into
NVIDIA:mainfrom
krauqen:sm121-default-build-fix

Conversation

@krauqen

@krauqen krauqen commented Aug 6, 2026

Copy link
Copy Markdown

Problem

A default CUTLASS library / profiler build for SM120-class GPUs — including SM121 (DGX Spark), using the flag set suggested in the Quick Start Guide —

cmake .. -DCUTLASS_NVCC_ARCHS=121a -DCUTLASS_ENABLE_TESTS=OFF -DCUTLASS_UNITY_BUILD_ENABLED=ON
make cutlass_profiler -j

fails with static asserts in every SM120 block-scaled (bstensorop) GEMM shard whose data types touch FP4/FP6 or block-scale-factor output. Representative failures (CUDA 13.0, sm_121a):

include/cutlass/gemm/collective/builders/sm90_common.inl(321): error: static assertion failed with
  "BLK_K0 must be a multiple of size<1>(GMMA::Layout_K_INTER_Atom<ElementType>{})"
  [cutlass3x_sm120_bstensorop_gemm_..._void_e5m2_128x8x128_..._cooperative_q]

include/cute/atom/copy_atom.hpp(206): error: static assertion failed with
  "TiledCopy uses too few vals for selected CopyAtom"
  [cutlass3x_sm120_bstensorop_gemm_ue8m0xe2m1_ue8m0xe2m3_f32_void_f32_128x8x128_..._cooperative_q]

include/cutlass/epilogue/fusion/sm120_visitor_store_tma_warpspecialized.hpp(72): error: static assertion failed with
  "EpilogueTileN should be divisible by SFVecSize"
  [cutlass3x_sm120_bstensorop_gemm_ue4m3xe2m1_ue4m3xe2m1_f32_f16_ue8m0xe2m1_128x8x128_..._epiVs16t]

include/cutlass/epilogue/collective/builders/sm120_builder.inl(158): error: static assertion failed with
  "CTA tile for FP6 ElementD must have a contiguous extent that is a multiple of 128."

The consequence is that no NVFP4/MXFP4/FP6 block-scaled GEMM is buildable through the library on SM120/SM121 without hand-maintained CUTLASS_LIBRARY_EXCLUDE_KERNELS patterns, even though the underlying kernels are fully functional at larger tiles.

Root cause

With an empty CUTLASS_LIBRARY_KERNELS, CreateGemmUniversal3xOperator prunes each family's tile list to tile_descriptions[0]:

# by default, only generate the largest tile and largest alignment
if manifest.kernel_filter == '':
    tile_descriptions = [tile_descriptions[0]]

This relies on tile lists being ordered largest-first. The SM120 block-scaled generators (GenerateSM120_TensorOp_mixed_8bits_UMMA_gemm_with_block_scaled, GenerateSM120_TensorOp_fp4_UMMA_gemm_with_block_scaled) list their tiles smallest-first, so the default set instantiates the family exclusively at 128x8x128 (cooperative) / 128x16x128 (pingpong), crossed with every output variant the generators emit. Several of those combinations cannot compile at small tile N:

  • narrow-byte ElementD (e5m2, e2m1) — no valid epilogue smem atom at N=8 (and N=16 pingpong for e2m1+SFD)
  • FP6 ElementD — requires a contiguous CTA extent that is a multiple of 128
  • block-scale-factor output — epilogue tile N (= min(CTA_N, 32)) must be divisible by SFVecSize
  • FP6 B operand — SM100_SU6_DU8x16_x4_LDSM_N needs per-MMA-atom N ≥ 16 (fails at N=8 cooperative and N=16 pingpong)

The same combinations compile at 128x128x128, which was tile_descriptions[0] before the small-N tiles were added.

Fix

Reorder tile_sizes_cooperative / tile_sizes_pingpong in the two SM120 block-scaled generator functions to largest-first, restoring the tile_descriptions[0] convention. The default set then selects 128x128x128, which compiles for every generated dtype/output combination. No tiles are removed: builds with a non-empty CUTLASS_LIBRARY_KERNELS still instantiate the full lists, including the small-N tiles for skinny-N problems.

Add a regression test, test/python/cutlass_library/test_sm120_blockscaled_default_tiles.py (pure Python, no GPU required):

  • default-set path (empty kernel filter) must select TileN ≥ 128 for both generators and both dense/grouped kinds — fails on current main (TileN=8 selected), passes with this change;
  • non-empty-filter path must still contain the N=8/16 tiles, guarding against fixing this by deletion.
PYTHONPATH=python python3 test/python/cutlass_library/test_sm120_blockscaled_default_tiles.py

Validation

Tested on NVIDIA DGX Spark (GB10, SM121) with CUDA 13.0.88.

  • Generator run for 121a, empty kernel filter, no excludes: 500 bstensorop kernels emitted, all 128x128x128 (previously: all 128x8x128/128x16x128, of which large subsets fail to compile).
  • Full default cutlass_profiler build for 121a with no CUTLASS_LIBRARY_EXCLUDE_KERNELS: completes with 0 errors (previously fails in every FP4/FP6-touching shard).
  • Runtime sweep: all 484 block-scaled kernels in the resulting profiler binary executed at m=n=k=1024, one process per kernel: 484/484 passed, including NVFP4×NVFP4 with FP4-requantization output (LinCombBlockScaleFactor) and grouped variants.
  • New regression test: passes with this change; fails on current main (4 failures, default set selects TileN=8).
  • git diff --check passes.

…p largest tile first

With an empty CUTLASS_LIBRARY_KERNELS, CreateGemmUniversal3xOperator prunes
each family's tile list to tile_descriptions[0] ("only generate the largest
tile"). The SM120 block-scaled generators list their tiles smallest-first,
so a default sm_120a/121a library build instantiates the family exclusively
at 128x8x128 (cooperative) / 128x16x128 (pingpong), crossed with output
variants that only compile at larger tile N (narrow-byte D epilogue smem
atom, FP6 ElementD contiguous-extent constraint, SFVecSize divisibility,
FP6-B operand copy atom). The build fails with static asserts before any
block-scaled kernel is usable.

Reorder the tile lists largest-first so the default set selects
128x128x128, which compiles for every generated dtype/output combination.
The small-N tiles remain available to non-empty CUTLASS_LIBRARY_KERNELS
builds. Add a regression test asserting the default-set tile selection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mtx93NPwq8NLy7pqUB7oMs
@krauqen
krauqen force-pushed the sm121-default-build-fix branch from ce7212f to a822a84 Compare August 6, 2026 17:50
@krauqen krauqen changed the title Fix default-build static asserts for SM120/121 block-scaled GEMM: keep largest tile first Fix default-build for SM120/121 block-scaled GEMM: keep largest tile first Aug 6, 2026
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.

1 participant