spark_model/layers/ops/
fp8_moe.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2
3//! Auto-extracted from `ops.rs` during refactor wave 4a.
4
5#![allow(unused_imports)]
6
7use anyhow::Result;
8use spark_runtime::gpu::{DevicePtr, GpuBackend, KernelHandle};
9use spark_runtime::kernel_args::{KernelLaunch, div_ceil};
10
11use crate::layers::moe;
12use crate::weight_map::{DenseWeight, Fp8DenseWeight, Fp8Weight, QuantizedWeight};
13
14use super::*;
15
16/// Full-sequence causal depthwise conv1d + SiLU activation.
17///
18/// Kernel: `causal_conv1d_fwd(input, weight, bias, output, batch, dim, seq_len, d_conv)`
19/// Grid: (dim, batch, 1)  Block: (min(seq_len, 1024), 1, 1)
20///
21/// Input: [batch, dim, seq_len] BF16 (channel-first)
22/// Weight: [dim, d_conv] BF16
23/// Output: [batch, dim, seq_len] BF16
24#[allow(clippy::too_many_arguments)]
25pub fn conv1d_fwd(
26    gpu: &dyn GpuBackend,
27    kernel: KernelHandle,
28    input: DevicePtr,
29    weight: &DenseWeight,
30    output: DevicePtr,
31    batch: u32,
32    dim: u32,
33    seq_len: u32,
34    d_conv: u32,
35    stream: u64,
36) -> Result<()> {
37    let block_x = std::cmp::min(seq_len, 1024);
38    KernelLaunch::new(gpu, kernel)
39        .grid([dim, batch, 1])
40        .block([block_x, 1, 1])
41        .arg_ptr(input)
42        .arg_ptr(weight.weight)
43        .arg_ptr(DevicePtr::NULL) // bias (none for this model)
44        .arg_ptr(output)
45        .arg_u32(batch)
46        .arg_u32(dim)
47        .arg_u32(seq_len)
48        .arg_u32(d_conv)
49        .launch(stream)
50}
51
52/// BF16 concatenation: out[0..N] = a[0..N], out[N..2N] = b[0..N].
53///
54/// Kernel: `bf16_concat(a, b, out, N)`
55/// Grid: (ceil(N/256), 1, 1)  Block: (256, 1, 1)
56pub fn bf16_concat(
57    gpu: &dyn GpuBackend,
58    kernel: KernelHandle,
59    a: DevicePtr,
60    b: DevicePtr,
61    output: DevicePtr,
62    n: u32,
63    stream: u64,
64) -> Result<()> {
65    KernelLaunch::new(gpu, kernel)
66        .grid([div_ceil(n, 256), 1, 1])
67        .block([256, 1, 1])
68        .arg_ptr(a)
69        .arg_ptr(b)
70        .arg_ptr(output)
71        .arg_u32(n)
72        .launch(stream)
73}
74
75// ── FP8 MoE batch2/batch3 dispatch ──────────────────────────────────
76
77/// FP8 fused gate+up GEMV for batch=2 (MTP K=2 verify).
78/// Grid: (ceil(N/8), 2*(top_k+1), 2)  Block: (128, 1, 1)
79#[allow(clippy::too_many_arguments)]
80pub fn moe_expert_gate_up_shared_fp8_batch2(
81    gpu: &dyn GpuBackend,
82    kernel: KernelHandle,
83    input: DevicePtr,
84    gp_w: DevicePtr,
85    gp_s: DevicePtr,
86    gate_out: DevicePtr,
87    up_w: DevicePtr,
88    up_s: DevicePtr,
89    up_out: DevicePtr,
90    indices: DevicePtr,
91    sh_gate: &Fp8Weight,
92    sh_gate_out: DevicePtr,
93    sh_up: &Fp8Weight,
94    sh_up_out: DevicePtr,
95    n: u32,
96    k: u32,
97    top_k: u32,
98    stream: u64,
99) -> Result<()> {
100    KernelLaunch::new(gpu, kernel)
101        .grid([div_ceil(n, 8), 2 * (top_k + 1), 2])
102        .block([128, 1, 1])
103        .arg_ptr(input)
104        .arg_ptr(gp_w)
105        .arg_ptr(gp_s)
106        .arg_ptr(gate_out)
107        .arg_ptr(up_w)
108        .arg_ptr(up_s)
109        .arg_ptr(up_out)
110        .arg_ptr(indices)
111        .arg_ptr(sh_gate.weight)
112        .arg_ptr(sh_gate.row_scale)
113        .arg_ptr(sh_gate_out)
114        .arg_ptr(sh_up.weight)
115        .arg_ptr(sh_up.row_scale)
116        .arg_ptr(sh_up_out)
117        .arg_u32(n)
118        .arg_u32(k)
119        .arg_u32(top_k)
120        .launch(stream)
121}
122
123/// FP8 fused SiLU+down GEMV for batch=2.
124/// Grid: (ceil(N/8), 2*(top_k+1), 1)  Block: (128, 1, 1)
125#[allow(clippy::too_many_arguments)]
126pub fn moe_expert_silu_down_shared_fp8_batch2(
127    gpu: &dyn GpuBackend,
128    kernel: KernelHandle,
129    gate_out: DevicePtr,
130    up_out: DevicePtr,
131    dp_w: DevicePtr,
132    dp_s: DevicePtr,
133    output: DevicePtr,
134    indices: DevicePtr,
135    sh_gate_in: DevicePtr,
136    sh_up_in: DevicePtr,
137    sh_down: &Fp8Weight,
138    sh_down_out: DevicePtr,
139    n: u32,
140    k: u32,
141    top_k: u32,
142    stream: u64,
143) -> Result<()> {
144    KernelLaunch::new(gpu, kernel)
145        .grid([div_ceil(n, 8), 2 * (top_k + 1), 1])
146        .block([128, 1, 1])
147        .arg_ptr(gate_out)
148        .arg_ptr(up_out)
149        .arg_ptr(dp_w)
150        .arg_ptr(dp_s)
151        .arg_ptr(output)
152        .arg_ptr(indices)
153        .arg_ptr(sh_gate_in)
154        .arg_ptr(sh_up_in)
155        .arg_ptr(sh_down.weight)
156        .arg_ptr(sh_down.row_scale)
157        .arg_ptr(sh_down_out)
158        .arg_u32(n)
159        .arg_u32(k)
160        .arg_u32(top_k)
161        .launch(stream)
162}
163
164/// FP8 fused gate+up GEMV for batch=3 (MTP K=3 verify).
165/// Grid: (ceil(N/8), 3*(top_k+1), 2)  Block: (128, 1, 1)
166#[allow(clippy::too_many_arguments)]
167pub fn moe_expert_gate_up_shared_fp8_batch3(
168    gpu: &dyn GpuBackend,
169    kernel: KernelHandle,
170    input: DevicePtr,
171    gp_w: DevicePtr,
172    gp_s: DevicePtr,
173    gate_out: DevicePtr,
174    up_w: DevicePtr,
175    up_s: DevicePtr,
176    up_out: DevicePtr,
177    indices: DevicePtr,
178    sh_gate: &Fp8Weight,
179    sh_gate_out: DevicePtr,
180    sh_up: &Fp8Weight,
181    sh_up_out: DevicePtr,
182    n: u32,
183    k: u32,
184    top_k: u32,
185    stream: u64,
186) -> Result<()> {
187    KernelLaunch::new(gpu, kernel)
188        .grid([div_ceil(n, 8), 3 * (top_k + 1), 2])
189        .block([128, 1, 1])
190        .arg_ptr(input)
191        .arg_ptr(gp_w)
192        .arg_ptr(gp_s)
193        .arg_ptr(gate_out)
194        .arg_ptr(up_w)
195        .arg_ptr(up_s)
196        .arg_ptr(up_out)
197        .arg_ptr(indices)
198        .arg_ptr(sh_gate.weight)
199        .arg_ptr(sh_gate.row_scale)
200        .arg_ptr(sh_gate_out)
201        .arg_ptr(sh_up.weight)
202        .arg_ptr(sh_up.row_scale)
203        .arg_ptr(sh_up_out)
204        .arg_u32(n)
205        .arg_u32(k)
206        .arg_u32(top_k)
207        .launch(stream)
208}
209
210/// FP8 fused SiLU+down GEMV for batch=3.
211/// Grid: (ceil(N/8), 3*(top_k+1), 1)  Block: (128, 1, 1)
212#[allow(clippy::too_many_arguments)]
213pub fn moe_expert_silu_down_shared_fp8_batch3(
214    gpu: &dyn GpuBackend,
215    kernel: KernelHandle,
216    gate_out: DevicePtr,
217    up_out: DevicePtr,
218    dp_w: DevicePtr,
219    dp_s: DevicePtr,
220    output: DevicePtr,
221    indices: DevicePtr,
222    sh_gate_in: DevicePtr,
223    sh_up_in: DevicePtr,
224    sh_down: &Fp8Weight,
225    sh_down_out: DevicePtr,
226    n: u32,
227    k: u32,
228    top_k: u32,
229    stream: u64,
230) -> Result<()> {
231    KernelLaunch::new(gpu, kernel)
232        .grid([div_ceil(n, 8), 3 * (top_k + 1), 1])
233        .block([128, 1, 1])
234        .arg_ptr(gate_out)
235        .arg_ptr(up_out)
236        .arg_ptr(dp_w)
237        .arg_ptr(dp_s)
238        .arg_ptr(output)
239        .arg_ptr(indices)
240        .arg_ptr(sh_gate_in)
241        .arg_ptr(sh_up_in)
242        .arg_ptr(sh_down.weight)
243        .arg_ptr(sh_down.row_scale)
244        .arg_ptr(sh_down_out)
245        .arg_u32(n)
246        .arg_u32(k)
247        .arg_u32(top_k)
248        .launch(stream)
249}
250
251/// Batched per-expert uint8 transpose for MoE down_proj relayout.
252///
253/// Reads per-expert source pointers from `src_ptrs` and writes per-expert
254/// transposed `[cols, rows]` blocks via `dst_ptrs`. Both tables hold one
255/// device pointer per global expert; NULL entries (EP-remote experts)
256/// cause the kernel to exit early at block level.
257///
258/// Grid: (ceil(cols/32), ceil(rows/32), num_experts)  Block: (32, 8)
259#[allow(clippy::too_many_arguments)]
260/// Single-matrix uint8 transpose `[rows, cols] -> [cols, rows]` on GPU
261/// (`transpose_u8.cu`, 32x32 shared-memory tiles). Load-time replacement for
262/// the host byte-loop in `QuantizedWeight::transpose_for_gemm*` — the old
263/// path bounced every packed weight D2H -> O(N*K) host loop -> H2D
264/// (~13.6 GB through host at 27B cold load).
265pub fn transpose_u8(
266    gpu: &dyn GpuBackend,
267    kernel: KernelHandle,
268    src: DevicePtr,
269    dst: DevicePtr,
270    rows: u32,
271    cols: u32,
272    stream: u64,
273) -> Result<()> {
274    KernelLaunch::new(gpu, kernel)
275        .grid([div_ceil(cols, 32), div_ceil(rows, 32), 1])
276        .block([32, 8, 1])
277        .arg_ptr(src)
278        .arg_ptr(dst)
279        .arg_u32(rows)
280        .arg_u32(cols)
281        .launch(stream)
282}
283
284pub fn moe_transpose_u8_batched(
285    gpu: &dyn GpuBackend,
286    kernel: KernelHandle,
287    src_ptrs: DevicePtr,
288    dst_ptrs: DevicePtr,
289    rows: u32,
290    cols: u32,
291    num_experts: u32,
292    stream: u64,
293) -> Result<()> {
294    KernelLaunch::new(gpu, kernel)
295        .grid([div_ceil(cols, 32), div_ceil(rows, 32), num_experts])
296        .block([32, 8, 1])
297        .arg_ptr(src_ptrs)
298        .arg_ptr(dst_ptrs)
299        .arg_u32(rows)
300        .arg_u32(cols)
301        .launch(stream)
302}
303
304// ─────────────────────────────────────────────────────────────────────
305// Phase 8a — Transposed-layout decode MoE kernel bindings.
306//
307// Match the kernels in `kernels/gb10/common/moe_shared_expert_fused*_t.cu`.
308// Same arg order as the non-transposed counterparts; grid changes to
309// `ceil(N/128)` (each thread = one output position, lanes coalesced).
310// Pointer-table args point at TRANSPOSED weights `[K/2, N]` (NVFP4) or
311// `[K, N]` (FP8). Shared-expert direct-pointer args likewise. Callers
312// MUST pass the transposed buffers — there is no runtime layout flag.
313// ─────────────────────────────────────────────────────────────────────
314
315// Block size for the transposed-layout decode kernels. 32 = one warp per
316// block — more blocks per silu_down/gate_up call → higher SM occupancy on
317// GB10 (only 25 SMs, so larger blocks under-utilise). Each thread owns one
318// output regardless of block size.
319pub(super) const T_BLOCK: u32 = 32;
320
321/// NVFP4 fused gate+up GEMV (transposed weight). Single-token decode.
322#[allow(clippy::too_many_arguments)]
323pub fn moe_expert_gate_up_shared_t(
324    gpu: &dyn GpuBackend,
325    kernel: KernelHandle,
326    input: DevicePtr,
327    gate_packed_t_ptrs: DevicePtr,
328    gate_scale_t_ptrs: DevicePtr,
329    gate_scale2_vals: DevicePtr,
330    gate_out: DevicePtr,
331    up_packed_t_ptrs: DevicePtr,
332    up_scale_t_ptrs: DevicePtr,
333    up_scale2_vals: DevicePtr,
334    up_out: DevicePtr,
335    expert_indices: DevicePtr,
336    sh_gate_t: &QuantizedWeight,
337    sh_gate_out: DevicePtr,
338    sh_up_t: &QuantizedWeight,
339    sh_up_out: DevicePtr,
340    n: u32,
341    k: u32,
342    top_k: u32,
343    stream: u64,
344) -> Result<()> {
345    KernelLaunch::new(gpu, kernel)
346        .grid([div_ceil(n, T_BLOCK), top_k + 1, 2])
347        .block([T_BLOCK, 1, 1])
348        .arg_ptr(input)
349        .arg_ptr(gate_packed_t_ptrs)
350        .arg_ptr(gate_scale_t_ptrs)
351        .arg_ptr(gate_scale2_vals)
352        .arg_ptr(gate_out)
353        .arg_ptr(up_packed_t_ptrs)
354        .arg_ptr(up_scale_t_ptrs)
355        .arg_ptr(up_scale2_vals)
356        .arg_ptr(up_out)
357        .arg_ptr(expert_indices)
358        .arg_ptr(sh_gate_t.weight)
359        .arg_ptr(sh_gate_t.weight_scale)
360        .arg_f32(sh_gate_t.weight_scale_2)
361        .arg_ptr(sh_gate_out)
362        .arg_ptr(sh_up_t.weight)
363        .arg_ptr(sh_up_t.weight_scale)
364        .arg_f32(sh_up_t.weight_scale_2)
365        .arg_ptr(sh_up_out)
366        .arg_u32(n)
367        .arg_u32(k)
368        .arg_u32(top_k)
369        .launch(stream)
370}
371
372/// NVFP4 fused SiLU+down GEMV (transposed weight). Single-token decode.
373#[allow(clippy::too_many_arguments)]
374pub fn moe_expert_silu_down_shared_t(
375    gpu: &dyn GpuBackend,
376    kernel: KernelHandle,
377    gate_out: DevicePtr,
378    up_out: DevicePtr,
379    packed_t_ptrs: DevicePtr,
380    scale_t_ptrs: DevicePtr,
381    scale2_vals: DevicePtr,
382    output: DevicePtr,
383    expert_indices: DevicePtr,
384    sh_gate_in: DevicePtr,
385    sh_up_in: DevicePtr,
386    sh_down_t: &QuantizedWeight,
387    sh_down_out: DevicePtr,
388    n: u32,
389    k: u32,
390    top_k: u32,
391    stream: u64,
392) -> Result<()> {
393    let smem_bytes = (k as usize * std::mem::size_of::<f32>()) as u32;
394    KernelLaunch::new(gpu, kernel)
395        .grid([div_ceil(n, T_BLOCK), top_k + 1, 1])
396        .block([T_BLOCK, 1, 1])
397        .shared_mem(smem_bytes)
398        .arg_ptr(gate_out)
399        .arg_ptr(up_out)
400        .arg_ptr(packed_t_ptrs)
401        .arg_ptr(scale_t_ptrs)
402        .arg_ptr(scale2_vals)
403        .arg_ptr(output)
404        .arg_ptr(expert_indices)
405        .arg_ptr(sh_gate_in)
406        .arg_ptr(sh_up_in)
407        .arg_ptr(sh_down_t.weight)
408        .arg_ptr(sh_down_t.weight_scale)
409        .arg_f32(sh_down_t.weight_scale_2)
410        .arg_ptr(sh_down_out)
411        .arg_u32(n)
412        .arg_u32(k)
413        .arg_u32(top_k)
414        .launch(stream)
415}