@@ -638,6 +638,145 @@ def _make_scaled_matmul2d_cuda_native_kernel(
638638 return tilelang .language .prim_func (_fp8_scaled_matmul2d_cuda_native_kernel_template )
639639
640640
641+ # Lever fp8-mma-tune: a CLOSURE-parameterized, PIPELINED native-fp8 prim builder
642+ # the autotune harness drives. It is a STRICT SUPERSET of
643+ # :func:`_make_scaled_matmul2d_cuda_native_kernel` whose params (BM,BN,BK,threads,
644+ # num_stages,enable_rasteration) are bound as Python closure locals (NOT
645+ # ``globals().update`` mutation of the module template), so the tuner can vary the
646+ # tile per call without racing the shared module globals. The KERNEL BODY is the
647+ # verified SM120 fp8-input e4m3 MMA: e4m3 shared operands feed ``T.gemm`` directly
648+ # (-> ``tl::mma_sync<kFloat8_e4m3,kFloat8_e4m3,kFloat32,16,8,32>``), fp32 ``C_frag``
649+ # accumulate, the ``sa*sb`` epilogue, and ``disable_tma=True`` on every copy
650+ # (sm_121 TMA tensormap mis-aligns -> "Invalid TMA descriptor arguments").
651+ #
652+ # DELTAS vs the (still-default) module template
653+ # (:func:`_fp8_scaled_matmul2d_cuda_native_kernel_template`) -- which stays
654+ # BYTE-IDENTICAL so the production default + the Metal route are untouched:
655+ # * the K loop is ``T.Pipelined(..., num_stages=num_stages)`` instead of
656+ # ``T.serial(...)`` -- so ``num_stages`` is now LIVE (the module template's
657+ # ``T.serial`` makes num_stages a NO-OP); num_stages=0 lowers to an
658+ # un-pipelined single-buffer loop (parity with the current default).
659+ # * ``threads`` parameterizes ``T.Kernel`` (the module template hard-binds
660+ # ``_FP8_MM_THREADS``).
661+ # * an OPTIONAL ``T.use_swizzle(panel_size=10, enable=enable_rasteration)``
662+ # L2 rasterization hint (off by default -> emission parity with the module
663+ # template when enable_rasteration=False).
664+ # RULE #1: this is the ONE tunable path; an illegal tile RAISES in the builder
665+ # (validated below, same gates as :func:`_native_fp8_tile_for`); a pipelining/
666+ # swizzle codegen failure RAISES at compile -- it never silently degrades to the
667+ # un-pipelined template or to bf16.
668+
669+
670+ def _make_scaled_matmul2d_cuda_native_tunable (
671+ * ,
672+ M : int ,
673+ N : int ,
674+ K : int ,
675+ block_M : int ,
676+ block_N : int ,
677+ block_K : int ,
678+ num_stages : int ,
679+ threads : int ,
680+ c_dtype : str = "float32" ,
681+ enable_rasteration : bool = False ,
682+ ) -> Any :
683+ """Build a PIPELINED, closure-parameterized native fp8-input e4m3 ``T.gemm`` prim.
684+
685+ All of ``block_M/block_N/block_K``, ``num_stages``, ``threads`` and
686+ ``enable_rasteration`` are required (the autotuner supplies every axis); they
687+ are captured as Python closure locals and validated against the fp8 m16n8k32
688+ MMA hard constraints (BK%32==0, K%BK==0, BM%16==0, BN%8==0, M%BM==0, N%BN==0)
689+ -- an illegal tile RAISES here (RULE #1: no silent re-tile / partial-tile).
690+
691+ The emitted body is identical math to the verified native template; only the
692+ K loop (``T.Pipelined``), the ``T.Kernel`` ``threads``, and the optional
693+ ``T.use_swizzle`` differ (see the module comment above). Returns a
694+ ``@T.prim_func`` ready for :func:`tilelang.compile` with the same
695+ ``tl.disable_tma_lower`` / ``tl.disable_warp_specialized`` pass_configs.
696+ """
697+ import tilelang # noqa: F401
698+ from tilelang import language as T
699+
700+ bm , bn , bk = int (block_M ), int (block_N ), int (block_K )
701+ thr , ns = int (threads ), int (num_stages )
702+ M_i , N_i , K_i = int (M ), int (N ), int (K )
703+ c_dt = str (c_dtype )
704+ rasterize = bool (enable_rasteration )
705+
706+ # Validate the tile against the e4m3 m16n8k32 MMA gates (RULE #1: RAISE, the
707+ # autotuner is expected to filter illegal configs out BEFORE calling this, so
708+ # reaching here with an illegal tile is a real bug, not a sweep miss).
709+ if bk % 32 != 0 or K_i % bk :
710+ raise ValueError (
711+ "fp8_scaled_matmul_path_c (cuda native tunable): illegal BK "
712+ f"block_K={ bk } for K={ K_i } ; the e4m3 m16n8k32 MMA requires "
713+ "block_K%32==0 and K%block_K==0 (RULE #1: K%32 is a hard floor)."
714+ )
715+ if bm % 16 or bn % 8 or M_i % bm or N_i % bn :
716+ raise ValueError (
717+ "fp8_scaled_matmul_path_c (cuda native tunable): illegal BM/BN "
718+ f"block_M={ bm } block_N={ bn } for M={ M_i } N={ N_i } ; require "
719+ "block_M%16==0, block_N%8==0, M%block_M==0, N%block_N==0 "
720+ "(RULE #1: no wrong partial-tile)."
721+ )
722+ if thr <= 0 or thr % 32 != 0 :
723+ raise ValueError (
724+ "fp8_scaled_matmul_path_c (cuda native tunable): illegal threads="
725+ f"{ thr } ; require a positive multiple of 32 (warp granularity)."
726+ )
727+ if ns < 0 :
728+ raise ValueError (
729+ "fp8_scaled_matmul_path_c (cuda native tunable): illegal "
730+ f"num_stages={ ns } ; require >= 0 (0 = un-pipelined single buffer)."
731+ )
732+
733+ @T .prim_func
734+ def _native_fp8_tunable (
735+ A_fp8 : T .Tensor ((M_i , K_i ), "float8_e4m3" ),
736+ A_scale : T .Tensor ((1 ,), "float32" ),
737+ B_fp8 : T .Tensor ((N_i , K_i ), "float8_e4m3" ),
738+ B_scale : T .Tensor ((1 ,), "float32" ),
739+ C : T .Tensor ((M_i , N_i ), c_dt ),
740+ ):
741+ with T .Kernel (
742+ T .ceildiv (N_i , bn ),
743+ T .ceildiv (M_i , bm ),
744+ threads = thr ,
745+ ) as (bx , by ):
746+ # NATIVE-fp8: shared operands stay e4m3 (no float16 decode buffers) so
747+ # T.gemm dispatches the SM120 fp8 MMA atom (verified kFloat8_e4m3).
748+ A_shared = T .alloc_shared ((bm , bk ), "float8_e4m3" , scope = "shared" )
749+ B_shared = T .alloc_shared ((bn , bk ), "float8_e4m3" , scope = "shared" )
750+ C_frag = T .alloc_fragment ((bm , bn ), c_dt )
751+ C_shared = T .alloc_shared ((bm , bn ), c_dt , scope = "shared" )
752+ T .clear (C_frag )
753+ # Optional L2 rasterization hint (off by default -> emission parity).
754+ T .use_swizzle (panel_size = 10 , enable = rasterize )
755+ # PIPELINED K loop: num_stages is LIVE here (the default module
756+ # template uses T.serial, which makes num_stages inert).
757+ for ko in T .Pipelined (T .ceildiv (K_i , bk ), num_stages = ns ):
758+ T .copy (
759+ A_fp8 [by * bm : by * bm + bm , ko * bk : ko * bk + bk ],
760+ A_shared ,
761+ disable_tma = True ,
762+ )
763+ T .copy (
764+ B_fp8 [bx * bn : bx * bn + bn , ko * bk : ko * bk + bk ],
765+ B_shared ,
766+ disable_tma = True ,
767+ )
768+ # transpose_B=True keeps the TN layout the SM120 fp8 MMA requires.
769+ T .gemm (A_shared , B_shared , C_frag , transpose_B = True )
770+ sa = A_scale [0 ]
771+ sb = B_scale [0 ]
772+ for i , j in T .Parallel (bm , bn ):
773+ C_frag [i , j ] = C_frag [i , j ] * sa * sb
774+ T .copy (C_frag , C_shared )
775+ T .copy (C_shared , C [by * bm , bx * bn ], disable_tma = True )
776+
777+ return _native_fp8_tunable
778+
779+
641780def _resolve_fp8_compile_target (target : Any ) -> Any :
642781 """Resolve the ``target`` kwarg threaded through the FP8 matmul builders.
643782
@@ -1256,3 +1395,53 @@ def fp8_scaled_matmul_path_c_cuda_native_prim(
12561395
12571396__all__ .append ("fp8_scaled_matmul_path_c_cuda_native_prim" )
12581397__all__ .append ("FP8_PATH_C_CUDA_NATIVE_ENV" )
1398+
1399+
1400+ def fp8_scaled_matmul_path_c_cuda_native_tunable_prim (
1401+ * ,
1402+ M : int ,
1403+ N : int ,
1404+ K : int ,
1405+ block_M : int ,
1406+ block_N : int ,
1407+ block_K : int ,
1408+ num_stages : int ,
1409+ threads : int ,
1410+ c_dtype : str = "float32" ,
1411+ enable_rasteration : bool = False ,
1412+ ) -> Any :
1413+ """Public alias: build the PIPELINED, closure-parameterized native fp8 prim.
1414+
1415+ Thin wrapper over :func:`_make_scaled_matmul2d_cuda_native_tunable` -- the
1416+ lever-fp8-mma-tune kernel the autotune harness (``scratch/fp8_mma_autotune.py``)
1417+ sweeps. Every tile axis (``block_M/block_N/block_K``, ``num_stages``,
1418+ ``threads``, ``enable_rasteration``) is required and validated; an illegal tile
1419+ RAISES (RULE #1). The kernel body is the verified SM120 fp8-input e4m3 MMA
1420+ (``SM120_16x8x32_TN`` -> ``mma.sync...kind::f8f6f4.m16n8k32...e4m3.e4m3.f32``),
1421+ fp32 accumulate, but with a LIVE ``T.Pipelined(num_stages)`` K loop and a
1422+ parameterized ``threads`` (the default
1423+ :func:`fp8_scaled_matmul_path_c_cuda_native_prim` uses ``T.serial`` so its
1424+ num_stages is inert). Compile via :func:`tilelang.compile` with
1425+ ``target=_as_cuda_target("cuda")`` and ``pass_configs={"tl.disable_tma_lower":
1426+ True, "tl.disable_warp_specialized": True}``.
1427+
1428+ VERIFY-GATE (RULE #1): the GB10 phase must confirm the generated CUDA still
1429+ emits ``tl::mma_sync<kFloat8_e4m3,kFloat8_e4m3,kFloat32,16,8,32>`` (NOT
1430+ kFloat16) after pipelining -- that proves the pipelined loop did not perturb
1431+ the SM120 fp8 MMA dispatch back to an fp16 path.
1432+ """
1433+ return _make_scaled_matmul2d_cuda_native_tunable (
1434+ M = M ,
1435+ N = N ,
1436+ K = K ,
1437+ block_M = block_M ,
1438+ block_N = block_N ,
1439+ block_K = block_K ,
1440+ num_stages = num_stages ,
1441+ threads = threads ,
1442+ c_dtype = c_dtype ,
1443+ enable_rasteration = enable_rasteration ,
1444+ )
1445+
1446+
1447+ __all__ .append ("fp8_scaled_matmul_path_c_cuda_native_tunable_prim" )
0 commit comments