8181 make_layers ,
8282 maybe_prefix ,
8383)
84+ from atom .quant_spec import should_skip_online_quant
8485from atom .utils import envs
8586from atom .utils .custom_register import direct_register_custom_op
8687
@@ -1949,13 +1950,23 @@ def __init__(
19491950 # qkv_a_proj + reduce + RMSNorm + quant fusion remains gated by
19501951 # use_triton_gemm() in forward(), because that path depends on Triton GEMM.
19511952 self .prefix = prefix
1952- self .quant_dtype = layer_quant_dtype
1953- self .qknorm_quant_type = layer_quant_type_value
1953+ # Online-aware scheme for the fused q/kv norm+quant feeding q_b_proj:
1954+ # use the online target (applied after __init__), else the static config.
1955+ # layer_quant_dtype/type stay static for the fp4x2 weight-loading branch.
1956+ eff_dtype , eff_type = layer_quant_dtype , layer_quant_type
1957+ if quant_config is not None and quant_config .online_quant :
1958+ online_cfg = quant_config .get_layer_quant_config (
1959+ f"{ prefix } .{ q_a_proj_name } " , use_online_quant = True
1960+ )
1961+ if not should_skip_online_quant (eff_type , eff_dtype , online_cfg ):
1962+ eff_dtype , eff_type = online_cfg .quant_dtype , online_cfg .quant_type
1963+ self .quant_dtype = eff_dtype
1964+ self .qknorm_quant_type = None if eff_type is None else eff_type .value
19541965 self .fuse_qknorm_quant = False
19551966 # always fuse qknorm
19561967 self .fuse_qknorm = ENABLE_DS_QKNORM_FUSION
19571968 if quant_config is not None and ENABLE_DS_QKNORM_QUANT_FUSION :
1958- if layer_quant_dtype in (dtypes .fp8 , dtypes .fp4x2 ):
1969+ if eff_dtype in (dtypes .fp8 , dtypes .fp4x2 ):
19591970 self .fuse_qknorm_quant = True
19601971
19611972 def forward (
@@ -1964,8 +1975,16 @@ def forward(
19641975 hidden_states : torch .Tensor ,
19651976 ) -> torch .Tensor :
19661977 hidden_states_scale = None
1978+ # When input_layernorm fused AR+RMSNorm+quant, hidden_states is a tuple.
1979+ # A 3-tuple (fp8, scale, bf16) additionally carries the unquantized bf16
1980+ # normed activation for the v32 indexer (see RMSNorm.fused_quant_emit_bf16);
1981+ # a 2-tuple (fp8, scale) is the plain fused-quant output.
1982+ indexer_hidden = None
19671983 if isinstance (hidden_states , tuple ):
1968- hidden_states , hidden_states_scale = hidden_states
1984+ if len (hidden_states ) == 3 :
1985+ hidden_states , hidden_states_scale , indexer_hidden = hidden_states
1986+ else :
1987+ hidden_states , hidden_states_scale = hidden_states
19691988
19701989 if self .q_lora_rank is not None :
19711990 if self .fuse_qknorm_quant and use_triton_gemm ():
@@ -2042,8 +2061,11 @@ def forward(
20422061 kv_c_normed = self .kv_a_layernorm (kv_c )
20432062 hidden_states_or_q_c_scale = None
20442063 if self .is_v32 and self .indexer is not None and not self .skip_topk :
2064+ # The indexer's wk/weights_proj GEMMs run in BF16. When input_layernorm
2065+ # fused the quant it emits a bf16 mirror (indexer_hidden); otherwise
2066+ # hidden_states is already the bf16 normed activation.
20452067 self .indexer (
2046- hidden_states ,
2068+ indexer_hidden if indexer_hidden is not None else hidden_states ,
20472069 hidden_states_or_q_c ,
20482070 hidden_states_or_q_c_scale ,
20492071 positions ,
@@ -2158,12 +2180,57 @@ def __init__(
21582180 reduce_results = not self .fuse_ar_input_norm ,
21592181 prefix = f"{ prefix } .mlp" ,
21602182 )
2183+ # Fuse activation quant into AR+RMSNorm when fused_qkv_a_proj is
2184+ # per-1x128/per-token FP8, so the GEMM consumes the (fp8, scale) directly.
2185+ # Use the online-quant target (get_layer_quant_config(..., online)), not
2186+ # the static params_dtype: online quant flips the qkv proj to fp8 only in
2187+ # process_weights_after_loading, which runs after this __init__.
2188+ qkv_proj = getattr (self .self_attn , "fused_qkv_a_proj" , None )
2189+ eff_quant_type = None if qkv_proj is None else qkv_proj .quant_type
2190+ eff_quant_dtype = None if qkv_proj is None else qkv_proj .params_dtype
2191+ if (
2192+ qkv_proj is not None
2193+ and quant_config is not None
2194+ and quant_config .online_quant
2195+ ):
2196+ online_cfg = quant_config .get_layer_quant_config (
2197+ qkv_proj .prefix , use_online_quant = True
2198+ )
2199+ if not should_skip_online_quant (
2200+ eff_quant_type , eff_quant_dtype , online_cfg
2201+ ):
2202+ eff_quant_type = online_cfg .quant_type
2203+ eff_quant_dtype = online_cfg .quant_dtype
2204+ input_norm_fused_quant = (
2205+ qkv_proj is not None
2206+ and eff_quant_dtype == dtypes .fp8
2207+ and eff_quant_type .value
2208+ in (QuantType .per_1x128 .value , QuantType .per_Token .value )
2209+ )
2210+ fused_allreduce = (
2211+ self .fuse_ar_input_norm and self .layer_idx > 0 and not is_mtp_block
2212+ )
2213+ # GLM-5.2 (v32) DSA: the indexer's wk/weights_proj GEMMs run in BF16 and
2214+ # consume the same normed activation, so the fused kernel must also emit
2215+ # the pre-quant bf16 mirror alongside the fp8 for fused_qkv_a_proj.
2216+ emit_bf16_for_indexer = (
2217+ getattr (self .self_attn , "is_v32" , False )
2218+ and getattr (self .self_attn , "indexer" , None ) is not None
2219+ )
21612220 self .input_layernorm = RMSNorm (
21622221 config .hidden_size ,
21632222 eps = config .rms_norm_eps ,
2164- fused_allreduce = self .fuse_ar_input_norm
2165- and self .layer_idx > 0
2166- and not is_mtp_block ,
2223+ fused_allreduce = fused_allreduce ,
2224+ fused_quant = fused_allreduce and input_norm_fused_quant ,
2225+ fused_quant_emit_bf16 = (
2226+ fused_allreduce and input_norm_fused_quant and emit_bf16_for_indexer
2227+ ),
2228+ quant_config = quant_config ,
2229+ prefix = (
2230+ f"{ prefix } .self_attn.fused_qkv_a_proj"
2231+ if qkv_proj is not None
2232+ else f"{ prefix } .self_attn.q_a_proj"
2233+ ),
21672234 )
21682235 self .post_attention_layernorm = RMSNorm (
21692236 config .hidden_size ,
0 commit comments