spark_model/layers/moe/
init.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2
3//! MoeLayer::new constructor.
4
5use super::*;
6
7impl MoeLayer {
8    pub fn new(
9        weights: MoeWeights,
10        num_experts: usize,
11        gate_nvfp4: Option<QuantizedWeight>,
12        gpu: &dyn GpuBackend,
13        config: &atlas_core::config::ModelConfig,
14    ) -> Result<Self> {
15        Self::new_with_hash(weights, num_experts, gate_nvfp4, None, gpu, config)
16    }
17
18    /// Like [`MoeLayer::new`] but with an optional DeepSeek-V4 hash-routing
19    /// `tid2eid` table ([vocab_size, top_k] i64). `Some` marks this as a
20    /// hash-routed layer.
21    #[allow(clippy::too_many_arguments)]
22    pub fn new_with_hash(
23        weights: MoeWeights,
24        num_experts: usize,
25        gate_nvfp4: Option<QuantizedWeight>,
26        tid2eid_dev: Option<DevicePtr>,
27        gpu: &dyn GpuBackend,
28        config: &atlas_core::config::ModelConfig,
29    ) -> Result<Self> {
30        // Sanity-check the routing config: top-k that exceeds the
31        // expert count would index OOB in the topk kernel and produce
32        // silent NaN routing. Catch the misconfiguration at load time.
33        anyhow::ensure!(
34            config.num_experts_per_tok <= num_experts && num_experts > 0,
35            "MoE config invalid: num_experts_per_tok={} must be in 1..={}",
36            config.num_experts_per_tok,
37            num_experts,
38        );
39        // The check above bounds top-k by the expert count, which is the OOB
40        // the topk kernel can READ. It says nothing about the OOB the kernel
41        // can WRITE: the sigmoid routing kernels stage their top-K in a
42        // fixed-size shared array, and `top_k` was passed to them unbounded.
43        anyhow::ensure!(
44            config.num_experts_per_tok <= crate::layers::ops::MOE_TOPK_SIGMOID_MAX_TOP_K
45                && num_experts <= crate::layers::ops::MOE_TOPK_SIGMOID_MAX_EXPERTS,
46            "MoE config exceeds the routing kernels' fixed shared-memory bounds: \
47             num_experts_per_tok={} (max {}), num_experts={} (max {}). Raise \
48             MAX_TOP_K / MAX_EXPERTS in kernels/gb10/common/moe_topk_sigmoid.cu \
49             and their mirrors in layers::ops together.",
50            config.num_experts_per_tok,
51            crate::layers::ops::MOE_TOPK_SIGMOID_MAX_TOP_K,
52            num_experts,
53            crate::layers::ops::MOE_TOPK_SIGMOID_MAX_EXPERTS,
54        );
55        let gate_ptrs = build_ptr_table(&weights.experts, |e| &e.gate_proj, gpu)?;
56        let up_ptrs = build_ptr_table(&weights.experts, |e| &e.up_proj, gpu)?;
57        let down_ptrs = build_ptr_table(&weights.experts, |e| &e.down_proj, gpu)?;
58
59        // Extract the optional correction-bias device pointer before the
60        // struct literal below moves `weights`. `.map(|dw| dw.weight)` turns
61        // an `Option<DenseWeight>` into an `Option<DevicePtr>` for the
62        // `moe_topk_sigmoid` kernel's bias arg.
63        let weights_correction_bias: Option<DevicePtr> =
64            weights.correction_bias.map(|dw| dw.weight);
65
66        let _ = num_experts;
67        let rms_norm_k = gpu.kernel("norm", "rms_norm")?;
68        Ok(Self {
69            weights,
70            // Default: standard NVFP4 (FP8-E4M3 per-16 + f32 global). The
71            // DeepSeek-V4 native-MXFP4 loader overrides this to `Mxfp4E8m0`
72            // after construction (see deepseek_v4/assemble.rs).
73            experts_scale_kind: crate::weight_map::WeightQuantFormat::Nvfp4,
74            shared_experts_scale_kind: crate::weight_map::WeightQuantFormat::Nvfp4,
75            gate_nvfp4,
76            pre_expert_norm: None,
77            pre_expert_norm_k: rms_norm_k,
78            dense_gemv: gpu.kernel("gemv", "dense_gemv_bf16")?,
79            w4a16_gemv: gpu.kernel("w4a16_gemv", "w4a16_gemv")?,
80            w4a16_gemv_sw: super::super::try_kernel(gpu, "w4a16_gemv", "w4a16_gemv_sw"),
81            w4a16_gemm: gpu.kernel("w4a16", "w4a16_gemm")?,
82            dense_gemm: gpu.kernel("gemm", "dense_gemm_bf16")?,
83            dense_gemm_router: super::super::try_kernel(gpu, "gemm", "dense_gemm_bf16_router"),
84            dense_gemm_pipelined: super::super::try_kernel(
85                gpu,
86                "gemm",
87                "dense_gemm_bf16_pipelined",
88            ),
89            // FP32 gate path (ATLAS_FP32_GATE) — optional; KernelHandle(0) if the
90            // target's kernel set predates these symbols, dispatch then stays BF16.
91            dense_gemm_f32out: super::super::try_kernel(gpu, "gemm", "dense_gemm_bf16_f32out"),
92            dense_gemm_f32in: super::super::try_kernel(gpu, "gemm", "dense_gemm_f32in_f32out"),
93            moe_topk_f32: super::super::try_kernel(gpu, "moe_topk", "moe_topk_softmax_f32"),
94            moe_expert_gate_up_shared: gpu
95                .kernel("moe_shared_expert_fused", "moe_expert_gate_up_shared")?,
96            moe_expert_silu_down_shared: gpu
97                .kernel("moe_shared_expert_fused", "moe_expert_silu_down_shared")?,
98            moe_topk: gpu.kernel("moe_topk", "moe_topk_softmax")?,
99            moe_weighted_sum_blend: gpu.kernel("moe_expert_gemv", "moe_weighted_sum_blend")?,
100            residual_add: gpu.kernel("residual_add", "bf16_residual_add")?,
101            moe_topk_batched: gpu.kernel("moe_topk", "moe_topk_softmax_batched")?,
102            moe_expert_gate_up_shared_batch2: gpu
103                .kernel("moe_fused_batch2", "moe_expert_gate_up_shared_batch2")?,
104            moe_expert_silu_down_shared_batch2: gpu
105                .kernel("moe_fused_batch2", "moe_expert_silu_down_shared_batch2")?,
106            moe_weighted_sum_blend_batch2: gpu
107                .kernel("moe_fused_batch2", "moe_weighted_sum_blend_batch2")?,
108            w4a16_gemv_batch2: gpu.kernel("w4a16_gemv", "w4a16_gemv_batch2")?,
109            moe_expert_gate_up_shared_batch3: gpu
110                .kernel("moe_fused_batch3", "moe_expert_gate_up_shared_batch3")?,
111            moe_expert_silu_down_shared_batch3: gpu
112                .kernel("moe_fused_batch3", "moe_expert_silu_down_shared_batch3")?,
113            moe_weighted_sum_blend_batch3: gpu
114                .kernel("moe_fused_batch3", "moe_weighted_sum_blend_batch3")?,
115            w4a16_gemv_batch3: gpu.kernel("w4a16_gemv", "w4a16_gemv_batch3")?,
116            moe_expert_gate_up_shared_token_major: gpu
117                .kernel("moe_prefill", "moe_expert_gate_up_shared_prefill")?,
118            moe_expert_silu_down_shared_token_major: gpu
119                .kernel("moe_prefill", "moe_expert_silu_down_shared_prefill")?,
120            moe_weighted_sum_blend_token_major: gpu
121                .kernel("moe_prefill", "moe_weighted_sum_blend_prefill")?,
122            moe_decode_atomic_c4_silu_down_accum_k: super::super::try_kernel(
123                gpu,
124                "moe_decode_atomic_c4",
125                "moe_decode_atomic_c4_silu_down_accum",
126            ),
127            moe_decode_atomic_c4_finalize_k: super::super::try_kernel(
128                gpu,
129                "moe_decode_atomic_c4",
130                "moe_decode_atomic_c4_finalize",
131            ),
132            moe_sort_by_expert: gpu.kernel("moe", "moe_sort_by_expert")?,
133            moe_sorted_gate_up: gpu.kernel("moe_sorted", "moe_sorted_gate_up")?,
134            moe_sorted_silu_down: gpu.kernel("moe_sorted", "moe_sorted_silu_down")?,
135            moe_grouped_gemm: gpu.kernel("moe_w4a16", "moe_w4a16_grouped_gemm_ptrtable")?,
136            moe_grouped_gemm_k32: if std::env::var("ATLAS_MOE_GROUPED_K32").as_deref() == Ok("1") {
137                super::super::try_kernel(gpu, "moe_w4a16", "moe_w4a16_grouped_gemm_ptrtable_k32")
138            } else {
139                KernelHandle(0)
140            },
141            moe_grouped_gemm_m256: if std::env::var("ATLAS_MOE_GROUPED_M256").as_deref() == Ok("1")
142            {
143                super::super::try_kernel(gpu, "moe_w4a16", "moe_w4a16_grouped_gemm_ptrtable_m256")
144            } else {
145                KernelHandle(0)
146            },
147            moe_grouped_gemm_t: gpu.kernel("moe_w4a16", "moe_w4a16_grouped_gemm_ptrtable_t")?,
148            moe_grouped_gemm_t_k64: gpu
149                .kernel("moe_w4a16", "moe_w4a16_grouped_gemm_ptrtable_t_k64")?,
150            moe_fused_gate_up_t: gpu.kernel("moe_w4a16", "moe_w4a16_fused_gate_up_t")?,
151            moe_fused_gate_up_t_k64: gpu.kernel("moe_w4a16", "moe_w4a16_fused_gate_up_t_k64")?,
152            // ARM-2 Phase-K native-MXFP4 (E8M0) prefill variants — try_kernel:
153            // only the deepseek-v4-flash target's moe_w4a16 module ships them.
154            moe_grouped_gemm_e8m0: super::super::try_kernel(
155                gpu,
156                "moe_w4a16",
157                "moe_w4a16_grouped_gemm_ptrtable_e8m0",
158            ),
159            moe_grouped_gemm_t_e8m0: super::super::try_kernel(
160                gpu,
161                "moe_w4a16",
162                "moe_w4a16_grouped_gemm_ptrtable_t_e8m0",
163            ),
164            moe_grouped_gemm_t_k64_e8m0: super::super::try_kernel(
165                gpu,
166                "moe_w4a16",
167                "moe_w4a16_grouped_gemm_ptrtable_t_k64_e8m0",
168            ),
169            moe_fused_gate_up_t_e8m0: super::super::try_kernel(
170                gpu,
171                "moe_w4a16",
172                "moe_w4a16_fused_gate_up_t_e8m0",
173            ),
174            moe_fused_gate_up_t_k64_e8m0: super::super::try_kernel(
175                gpu,
176                "moe_w4a16",
177                "moe_w4a16_fused_gate_up_t_k64_e8m0",
178            ),
179            // M=128 variant only present in models where Block D #3 has
180            // been ported (currently minimax-m2-229b). Other models keep
181            // KernelHandle(0) and dispatch falls through to M=64.
182            moe_fused_gate_up_t_k64_m128: super::super::try_kernel(
183                gpu,
184                "moe_w4a16",
185                "moe_w4a16_fused_gate_up_t_k64_m128",
186            ),
187            // FUSED FP4 gate_up kernel (ATLAS_HOLO_MOE_GATEUP_FP4). try_kernel:
188            // KernelHandle(0) on images that didn't compile it; the FP4 dispatch
189            // checks this handle != 0 before firing.
190            moe_fused_gate_up_t_k64_fp4: super::super::try_kernel(
191                gpu,
192                "moe_w4a16",
193                "moe_w4a16_fused_gate_up_t_k64_fp4",
194            ),
195            moe_fp8_grouped_gemm_t: gpu.kernel("moe_w4a16", "moe_fp8_grouped_gemm_ptrtable_t")?,
196            // THE routed-expert FP8 prefill kernel: grid-compaction (persistent
197            // 96-CTA grid over a compacted work-list). Handle may be 0 on older
198            // images that don't ship it.
199            moe_fp8_grouped_gemm_k: super::super::try_kernel(
200                gpu,
201                "moe_fp8_grouped_gemm",
202                "moe_fp8_grouped_gemm",
203            ),
204            // Work-list builder (module "moe" = moe_permute.cu). Launched on the
205            // SAME stream as the grouped GEMM (read-after-write of total_tiles).
206            moe_build_tile_worklist_k: super::super::try_kernel(
207                gpu,
208                "moe",
209                "moe_build_tile_worklist",
210            ),
211            moe_w8a8_grouped_gemm_k: super::super::try_kernel(
212                gpu,
213                "moe_w8a8_grouped_gemm",
214                "moe_w8a8_grouped_gemm",
215            ),
216            // PM4-geometry W8A8 grouped GEMM (same module). Handle may be 0 on
217            // targets/images without it; dispatch falls back to the dense grid.
218            moe_w8a8_grouped_gemm_pm4_k: super::super::try_kernel(
219                gpu,
220                "moe_w8a8_grouped_gemm",
221                "moe_w8a8_grouped_gemm_pm4",
222            ),
223            per_token_group_quant_fp8_k: ops::Fp8ActQuant::resolve(gpu),
224            // Fused silu_mul + per-token-group quant. Same module as
225            // moe_silu_mul, so a model that shadows moe_silu_mul.cu without
226            // this entry point gets handle 0 → unfused fallback.
227            silu_mul_quant_fp8_k: super::super::try_kernel(
228                gpu,
229                "moe_silu_mul",
230                "silu_mul_quant_fp8",
231            ),
232            fp8_gemm_t_blockscaled_k: super::super::try_kernel(
233                gpu,
234                "fp8_gemm_t_blockscaled",
235                "fp8_gemm_t_blockscaled",
236            ),
237            moe_bf16_grouped_gemm_k: super::super::try_kernel(
238                gpu,
239                "moe_bf16_grouped_gemm",
240                "moe_bf16_grouped_gemm",
241            ),
242            moe_expert_gate_up_shared_bf16_k: super::super::try_kernel(
243                gpu,
244                "moe_shared_expert_fused_bf16",
245                "moe_expert_gate_up_shared_bf16",
246            ),
247            moe_expert_silu_down_shared_bf16_k: super::super::try_kernel(
248                gpu,
249                "moe_shared_expert_fused_bf16",
250                "moe_expert_silu_down_shared_bf16",
251            ),
252            moe_expert_gate_up_shared_bf16_batch2_k: super::super::try_kernel(
253                gpu,
254                "moe_shared_expert_fused_bf16_batch2",
255                "moe_expert_gate_up_shared_bf16_batch2",
256            ),
257            moe_expert_silu_down_shared_bf16_batch2_k: super::super::try_kernel(
258                gpu,
259                "moe_shared_expert_fused_bf16_batch2",
260                "moe_expert_silu_down_shared_bf16_batch2",
261            ),
262            w8a16_gemm_k: super::super::try_kernel(gpu, "w8a16_gemm", "w8a16_gemm"),
263            w8a16_gemm_pipelined_k: super::super::try_kernel(
264                gpu,
265                "w8a16_gemm_pipelined",
266                "w8a16_gemm_pipelined",
267            ),
268            moe_gate_topk_fused_k: super::super::try_kernel(
269                gpu,
270                "moe_gate_topk",
271                "moe_gate_topk_fused",
272            ),
273            w4a16_gemm_t: gpu.kernel("w4a16", "w4a16_gemm_t")?,
274            bf16_to_fp8_k: gpu.kernel("w4a16", "bf16_to_fp8")?,
275            fp8_gemm_k: gpu.kernel("w4a16", "fp8_gemm_t")?,
276            moe_silu_mul: gpu.kernel("moe_silu_mul", "moe_silu_mul")?,
277            moe_act_mul: gpu.kernel("moe_silu_mul", "moe_silu_mul")?, // default: SiLU
278            gelu_activation: false,
279            moe_unpermute_reduce: gpu.kernel("moe", "moe_unpermute_reduce_indexed")?,
280            moe_batched_blend: gpu.kernel("moe", "moe_batched_blend")?,
281            gate_ptrs,
282            up_ptrs,
283            down_ptrs,
284            gate_ptrs_t: None,
285            up_ptrs_t: None,
286            down_ptrs_t: None,
287            cutlass_grouped_host: None,
288            _cutlass_sfb_owned: Vec::new(),
289            down_t_scratch_packed: None,
290            down_t_scratch_scale: None,
291            moe_transpose_u8_batched_k: gpu
292                .kernel("moe_transpose_batched", "moe_transpose_u8_batched")?,
293            // ── Phase 8a transposed-layout decode kernels ──
294            // Module name = file stem (default convention in atlas-kernels).
295            moe_expert_gate_up_shared_t_k: gpu
296                .kernel("moe_shared_expert_fused_t", "moe_expert_gate_up_shared_t")?,
297            moe_expert_silu_down_shared_t_k: gpu
298                .kernel("moe_shared_expert_fused_t", "moe_expert_silu_down_shared_t")?,
299            // ARM-2 Phase-K dual-format decode variants (E8M0 routed / NVFP4
300            // shared). try_kernel — the entries are in the common .cu but load
301            // by name; 0 where a target doesn't compile that module.
302            moe_expert_gate_up_shared_t_e8m0_k: super::super::try_kernel(
303                gpu,
304                "moe_shared_expert_fused_t",
305                "moe_expert_gate_up_shared_t_e8m0",
306            ),
307            moe_expert_silu_down_shared_t_e8m0_k: super::super::try_kernel(
308                gpu,
309                "moe_shared_expert_fused_t",
310                "moe_expert_silu_down_shared_t_e8m0",
311            ),
312            // sqrtsoftplus kernels: lazy-loaded via try_kernel so models that
313            // don't register them (all except DeepSeek-V4) start fine.
314            moe_topk_sqrtsoftplus_k: super::super::try_kernel(
315                gpu,
316                "moe_topk_sqrt",
317                "moe_topk_sqrtsoftplus",
318            ),
319            moe_topk_sqrtsoftplus_batched_k: super::super::try_kernel(
320                gpu,
321                "moe_topk_sqrt",
322                "moe_topk_sqrtsoftplus_batched",
323            ),
324            // Hash routing (DeepSeek-V4 hash_moe layers): lazy-loaded so other
325            // models start fine. `tid2eid_dev` is the per-layer table (Some
326            // only for hash layers).
327            router_logits_n: (config.num_experts + config.zero_expert_num) as u32,
328            moe_topk_softmax_bias_k: super::super::try_kernel(
329                gpu,
330                "moe_topk_softmax_bias",
331                "moe_topk_softmax_bias",
332            ),
333            moe_topk_softmax_bias_batched_k: super::super::try_kernel(
334                gpu,
335                "moe_topk_softmax_bias",
336                "moe_topk_softmax_bias_batched",
337            ),
338            moe_zero_expert_add_k: super::super::try_kernel(
339                gpu,
340                "moe_topk_softmax_bias",
341                "moe_zero_expert_add",
342            ),
343            // 64 KB, unconditional: written by the softmax+bias router even
344            // with zero_expert_num == 0 (always zeros then).
345            zero_accum_dev: gpu.alloc(16384 * 4)?,
346            moe_hash_route_k: super::super::try_kernel(gpu, "moe_hash_route", "moe_hash_route"),
347            moe_hash_route_batched_k: super::super::try_kernel(
348                gpu,
349                "moe_hash_route",
350                "moe_hash_route_batched",
351            ),
352            tid2eid_dev,
353            moe_expert_gate_up_shared_batch2_t_k: gpu.kernel(
354                "moe_shared_expert_fused_batch2_t",
355                "moe_expert_gate_up_shared_batch2_t",
356            )?,
357            moe_expert_silu_down_shared_batch2_t_k: gpu.kernel(
358                "moe_shared_expert_fused_batch2_t",
359                "moe_expert_silu_down_shared_batch2_t",
360            )?,
361            moe_expert_gate_up_shared_batch3_t_k: gpu.kernel(
362                "moe_shared_expert_fused_batch3_t",
363                "moe_expert_gate_up_shared_batch3_t",
364            )?,
365            moe_expert_silu_down_shared_batch3_t_k: gpu.kernel(
366                "moe_shared_expert_fused_batch3_t",
367                "moe_expert_silu_down_shared_batch3_t",
368            )?,
369            moe_expert_gate_up_shared_fp8_t_k: gpu.kernel(
370                "moe_shared_expert_fused_fp8_t",
371                "moe_expert_gate_up_shared_fp8_t",
372            )?,
373            moe_expert_silu_down_shared_fp8_t_k: gpu.kernel(
374                "moe_shared_expert_fused_fp8_t",
375                "moe_expert_silu_down_shared_fp8_t",
376            )?,
377            moe_expert_gate_up_shared_fp8_batch2_t_k: gpu.kernel(
378                "moe_shared_expert_fused_fp8_batch2_t",
379                "moe_expert_gate_up_shared_fp8_batch2_t",
380            )?,
381            moe_expert_silu_down_shared_fp8_batch2_t_k: gpu.kernel(
382                "moe_shared_expert_fused_fp8_batch2_t",
383                "moe_expert_silu_down_shared_fp8_batch2_t",
384            )?,
385            moe_expert_gate_up_shared_fp8_batch3_t_k: gpu.kernel(
386                "moe_shared_expert_fused_fp8_batch3_t",
387                "moe_expert_gate_up_shared_fp8_batch3_t",
388            )?,
389            moe_expert_silu_down_shared_fp8_batch3_t_k: gpu.kernel(
390                "moe_shared_expert_fused_fp8_batch3_t",
391                "moe_expert_silu_down_shared_fp8_batch3_t",
392            )?,
393            unified_layout: std::env::var("ATLAS_UNIFIED_MOE_LAYOUT")
394                .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
395                .unwrap_or(false),
396            hybrid_layout: std::env::var("ATLAS_HYBRID_MOE_LAYOUT")
397                .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
398                .unwrap_or(false),
399            nvfp4_gate_up_m128: std::env::var("ATLAS_NVFP4_GATE_UP_M128")
400                .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
401                .unwrap_or(false),
402            // FP4 prefill MoE over the shared FAST_MOE=full [K/2,N] tables.
403            gateup_fp4: std::env::var("ATLAS_HOLO_MOE_GATEUP_FP4")
404                .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
405                .unwrap_or(false),
406            down_fp4: std::env::var("ATLAS_HOLO_MOE_DOWN_FP4")
407                .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
408                .unwrap_or(false),
409            shared_gate_t: None,
410            shared_up_t: None,
411            shared_down_t: None,
412            gate_fp8: None,
413            shared_gate_fp8: None,
414            shared_up_fp8: None,
415            shared_down_fp8: None,
416            prefill_stream: gpu.create_stream()?,
417            event_a: gpu.create_event()?,
418            event_b: gpu.create_event()?,
419            moe_expert_gate_up_shared_fp8: gpu.kernel(
420                "moe_shared_expert_fused_fp8",
421                "moe_expert_gate_up_shared_fp8",
422            )?,
423            moe_expert_silu_down_shared_fp8: gpu.kernel(
424                "moe_shared_expert_fused_fp8",
425                "moe_expert_silu_down_shared_fp8",
426            )?,
427            // FP8 batch2/3 kernels for MTP verify
428            moe_expert_gate_up_shared_fp8_batch2: gpu.kernel(
429                "moe_shared_expert_fused_fp8_batch2",
430                "moe_expert_gate_up_shared_fp8_batch2",
431            )?,
432            moe_expert_silu_down_shared_fp8_batch2: gpu.kernel(
433                "moe_shared_expert_fused_fp8_batch2",
434                "moe_expert_silu_down_shared_fp8_batch2",
435            )?,
436            moe_weighted_sum_blend_fp8_batch2: gpu.kernel(
437                "moe_shared_expert_fused_fp8_batch2",
438                "moe_weighted_sum_blend_fp8_batch2",
439            )?,
440            moe_expert_gate_up_shared_fp8_batch3: gpu.kernel(
441                "moe_shared_expert_fused_fp8_batch3",
442                "moe_expert_gate_up_shared_fp8_batch3",
443            )?,
444            moe_expert_silu_down_shared_fp8_batch3: gpu.kernel(
445                "moe_shared_expert_fused_fp8_batch3",
446                "moe_expert_silu_down_shared_fp8_batch3",
447            )?,
448            moe_weighted_sum_blend_fp8_batch3: gpu.kernel(
449                "moe_shared_expert_fused_fp8_batch3",
450                "moe_weighted_sum_blend_fp8_batch3",
451            )?,
452            fp8_gate_weight_ptrs: None,
453            fp8_up_weight_ptrs: None,
454            fp8_down_weight_ptrs: None,
455            bf16_gate_weight_ptrs: None,
456            bf16_up_weight_ptrs: None,
457            bf16_down_weight_ptrs: None,
458            bf16_shared_expert: None,
459            fp8_shared_expert: None,
460            moe_down_t_k64_fp4: super::super::try_kernel(
461                gpu,
462                "moe_w4a16",
463                "moe_w4a16_down_t_k64_fp4",
464            ),
465            moe_permute_tokens_k: super::super::try_kernel(gpu, "moe", "moe_permute_tokens"),
466            // Phase 2.7 Tier C — set by loader after construction (qwen35.rs).
467            is_dflash_capture_layer: false,
468            lora: None,
469            correction_bias_dev: weights_correction_bias,
470            // `moe_topk_sig` is only registered for sigmoid-gated MoE models
471            // (MiniMax-M2, Nemotron-Nano, Nemotron-Super). Softmax-gated MoEs
472            // (Qwen3.5, Qwen3-Next, Gemma-4, Mistral) never hit the sigmoid
473            // dispatch path, so a missing kernel is fine — fail at call time
474            // via the KernelHandle(0) check in ops::moe_topk_sigmoid rather
475            // than at MoeLayer::new(), which would otherwise block all
476            // softmax-MoE model startup (observed on Qwen3.5-35B-A3B-FP8 in
477            // alpha-2.43: "Module 'moe_topk_sig' not loaded" during model
478            // build).
479            moe_topk_sigmoid_k: super::super::try_kernel(gpu, "moe_topk_sig", "moe_topk_sigmoid"),
480            moe_topk_sigmoid_batched_k: super::super::try_kernel(
481                gpu,
482                "moe_topk_sig",
483                "moe_topk_sigmoid_batched",
484            ),
485        })
486    }
487}