@@ -209,6 +209,11 @@ def __init__(
209209 # despite the literal `is_sm103` name.
210210 is_sm103 = self .arch .is_family_of (Arch .sm_103f )
211211 self .is_sm103 = is_sm103
212+ # SM103 tcgen05.ld.red fuses the S TMEM load with a per-x32-tile max computed
213+ # in the TMEM controller, making row max nearly free. Only valid when the
214+ # loaded values are used unmodified, so score_mod (incl. softcap) disables it;
215+ # masked iterations additionally fall back to the software max (see softmax_step).
216+ self .use_ldred_rowmax = is_sm103 and self .score_mod is None
212217 # enable_ex2_emu is derived: True if tuning config has freq > 0, else fallback to default logic
213218 _default_enable_ex2_emu = (self .head_dim_padded <= 128 or (self .head_dim_padded == 192 and self .use_2cta_instrs and not self .is_causal and not self .is_local )) and not is_sm103
214219 self .enable_ex2_emu = _default_enable_ex2_emu
@@ -1920,9 +1925,12 @@ def softmax_loop(
19201925 )
19211926 tStP = cute .make_tensor (tSAcc .iterator + self .tmem_s_to_p_offset , tStP_layout )
19221927
1923- tmem_load_atom = cute .make_copy_atom (
1924- tcgen05 .copy .Ld32x32bOp (tcgen05 .copy .Repetition (32 )), self .qk_acc_dtype
1928+ tmem_load_op = (
1929+ tcgen05 .copy .LdRed32x32bOp (tcgen05 .copy .Repetition (32 ))
1930+ if const_expr (self .use_ldred_rowmax )
1931+ else tcgen05 .copy .Ld32x32bOp (tcgen05 .copy .Repetition (32 ))
19251932 )
1933+ tmem_load_atom = cute .make_copy_atom (tmem_load_op , self .qk_acc_dtype )
19261934 thr_tmem_load = tcgen05 .make_tmem_copy (tmem_load_atom , tSAcc ).get_slice (tidx )
19271935 tStS_t2r = thr_tmem_load .partition_S (tSAcc ) # (((32,32),1),1,4)
19281936
@@ -2169,6 +2177,7 @@ def softmax_loop(
21692177 else :
21702178 mma_si_consumer_phase , sm_stats_producer_phase , s0_s1_sequence_phase = softmax_step (
21712179 mma_si_consumer_phase , sm_stats_producer_phase , s0_s1_sequence_phase , n_block ,
2180+ mask_is_noop = True ,
21722181 )
21732182 # Separate iterations with local masking on the left
21742183 if const_expr (self .is_local and block_info .window_size_left is not None ):
@@ -2256,6 +2265,9 @@ def softmax_step(
22562265 head_divmod = None ,
22572266 mask_fn : Optional [Callable ] = None ,
22582267 is_first : bool = False ,
2268+ # True when this call site applies no masking (no causal/local edge, no
2269+ # mask_mod, no seqlen boundary) — gates use of the tcgen05.ld.red hardware max.
2270+ mask_is_noop : cutlass .Constexpr [bool ] = False ,
22592271 ) -> Tuple [cute .Int32 , cute .Int32 , cute .Int32 ]:
22602272 """Perform a single step of the softmax computation on a block of attention scores.
22612273
@@ -2284,7 +2296,16 @@ def softmax_step(
22842296 # Wait for Si
22852297 pipeline_s_p_o .consumer_wait_w_index_phase (stage , mma_si_consumer_phase )
22862298 tSrS_t2r = cute .make_rmem_tensor (thr_tmem_load .partition_D (tScS ).shape , self .qk_acc_dtype )
2287- cute .copy (thr_tmem_load , tStS_t2r , tSrS_t2r )
2299+ hw_row_max = Float32 (- Float32 .inf )
2300+ if const_expr (self .use_ldred_rowmax ):
2301+ # ld.red returns each x32 tile's max in an extra register alongside the
2302+ # 32 values; the reduction tensor collapses the V-mode to a single element.
2303+ tSrS_red = cute .make_rmem_tensor (((1 , 1 ), * tSrS_t2r .shape [1 :]), self .qk_acc_dtype )
2304+ cute .copy (thr_tmem_load , tStS_t2r , (tSrS_t2r , tSrS_red ))
2305+ for i in cutlass .range_constexpr (cute .size (tSrS_red .shape )):
2306+ hw_row_max = cute .arch .fmax (hw_row_max , tSrS_red [i ])
2307+ else :
2308+ cute .copy (thr_tmem_load , tStS_t2r , tSrS_t2r )
22882309 # tSrS_t2r = copy_utils.load_t2r(thr_tmem_load, tScS_shape, tStS_t2r)
22892310 if cutlass .const_expr (self .score_mod is not None ):
22902311 self .apply_score_mod (
@@ -2304,7 +2325,12 @@ def softmax_step(
23042325
23052326 if const_expr (mask_fn is not None ):
23062327 mask_fn (tSrS_t2r , n_block = n_block )
2307- row_max , acc_scale = softmax .update_row_max (tSrS_t2r .load (), is_first )
2328+ # The hardware max is only valid when masking left the values untouched;
2329+ # masked iterations reduce over the post-mask values in software.
2330+ if const_expr (self .use_ldred_rowmax and mask_is_noop ):
2331+ row_max , acc_scale = softmax .update_row_max_precomputed (hw_row_max , is_first )
2332+ else :
2333+ row_max , acc_scale = softmax .update_row_max (tSrS_t2r .load (), is_first )
23082334
23092335 if const_expr (not is_first ):
23102336 # tSrScale_r2t = cute.make_rmem_tensor(thr_tmem_store_scale.partition_S(tScScale).shape, Float32)
0 commit comments