spark_model/layers/ops/
ssm_gdn_b.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/// Fused 3-token GDN decode (K=3 speculative verification).
17///
18/// Processes exactly 3 tokens through GDN in a single kernel launch.
19/// Saves 2 intermediate H states (H_1, H_2) for rollback on draft rejection.
20/// 4 passes vs 6 for 3× sequential decode.
21///
22/// Kernel: `gated_delta_rule_chunk3(h_state, query, key, value, gate, beta,
23///          output, h_inter0, h_inter1, batch_size, num_k_heads,
24///          num_v_heads, k_dim, v_dim, qk_stride, v_stride, gb_stride)`
25/// Grid: (num_v_heads, batch, 1)  Block: (128, 1, 1)
26#[allow(clippy::too_many_arguments)]
27pub fn gdn_decode_chunk3(
28    gpu: &dyn GpuBackend,
29    kernel: KernelHandle,
30    h_state: DevicePtr,
31    query: DevicePtr,
32    key: DevicePtr,
33    value: DevicePtr,
34    gate: DevicePtr,
35    beta: DevicePtr,
36    output: DevicePtr,
37    h_state_inter0: DevicePtr,
38    h_state_inter1: DevicePtr,
39    batch_size: u32,
40    num_k_heads: u32,
41    num_v_heads: u32,
42    k_dim: u32,
43    v_dim: u32,
44    qk_stride: u32,
45    v_stride: u32,
46    gb_stride: u32,
47    stream: u64,
48) -> Result<()> {
49    KernelLaunch::new(gpu, kernel)
50        .grid([num_v_heads, batch_size, 1])
51        .block([128, 1, 1])
52        .arg_ptr(h_state)
53        .arg_ptr(query)
54        .arg_ptr(key)
55        .arg_ptr(value)
56        .arg_ptr(gate)
57        .arg_ptr(beta)
58        .arg_ptr(output)
59        .arg_ptr(h_state_inter0)
60        .arg_ptr(h_state_inter1)
61        .arg_u32(batch_size)
62        .arg_u32(num_k_heads)
63        .arg_u32(num_v_heads)
64        .arg_u32(k_dim)
65        .arg_u32(v_dim)
66        .arg_u32(qk_stride)
67        .arg_u32(v_stride)
68        .arg_u32(gb_stride)
69        .launch(stream)
70}
71
72/// WY-chunkwise 2-token GDN decode (2-pass algorithm).
73///
74/// Drop-in replacement for `gdn_decode_chunk2`. Computes both H^T @ k_t
75/// dot products in a single pass over H, then applies WY algebraic correction.
76/// 2 passes vs 3, reducing memory traffic by 33%.
77///
78/// Grid: (num_v_heads, batch, 1)  Block: (128, 1, 1)
79#[allow(clippy::too_many_arguments)]
80pub fn gdn_decode_wy2(
81    gpu: &dyn GpuBackend,
82    kernel: KernelHandle,
83    h_state: DevicePtr,
84    query: DevicePtr,
85    key: DevicePtr,
86    value: DevicePtr,
87    gate: DevicePtr,
88    beta: DevicePtr,
89    output: DevicePtr,
90    h_state_intermediate: DevicePtr,
91    batch_size: u32,
92    num_k_heads: u32,
93    num_v_heads: u32,
94    k_dim: u32,
95    v_dim: u32,
96    qk_stride: u32,
97    v_stride: u32,
98    gb_stride: u32,
99    // false = contiguous state bases indexed by (b*num_v_heads+vh);
100    // true  = device pointer TABLES, one entry per sequence. See
101    // `gdn_decode_wy4` for the full rationale — contiguous is only correct at
102    // batch_size==1 because the intermediate's pool stride is
103    // num_intermediates x h_state's.
104    state_is_table: bool,
105    stream: u64,
106) -> Result<()> {
107    // HARD guard, not debug_assert: this compiles out in release, which is
108    // exactly where the corruption would be silent.
109    anyhow::ensure!(
110        state_is_table || batch_size == 1,
111        "gdn_decode_wy2: contiguous state addressing is only valid at \
112         batch_size==1 (got {batch_size}) — the intermediate's pool stride is \
113         num_intermediates x h_state's, so sequence 1's Hi0 would land on \
114         sequence 0's Hi1. Stage pointer tables and pass state_is_table=true."
115    );
116    KernelLaunch::new(gpu, kernel)
117        .grid([num_v_heads, batch_size, 1])
118        .block([128, 1, 1])
119        .arg_ptr(h_state)
120        .arg_ptr(query)
121        .arg_ptr(key)
122        .arg_ptr(value)
123        .arg_ptr(gate)
124        .arg_ptr(beta)
125        .arg_ptr(output)
126        .arg_ptr(h_state_intermediate)
127        .arg_u32(batch_size)
128        .arg_u32(num_k_heads)
129        .arg_u32(num_v_heads)
130        .arg_u32(k_dim)
131        .arg_u32(v_dim)
132        .arg_u32(qk_stride)
133        .arg_u32(v_stride)
134        .arg_u32(gb_stride)
135        .arg_u32(u32::from(state_is_table))
136        .launch(stream)
137}
138
139/// WY-chunkwise 3-token GDN decode (2-pass algorithm).
140///
141/// Drop-in replacement for `gdn_decode_chunk3`. All 3 H^T @ k_t dot products
142/// computed in a single pass. 2 passes vs 4, reducing memory traffic by 50%.
143///
144/// Grid: (num_v_heads, batch, 1)  Block: (128, 1, 1)
145#[allow(clippy::too_many_arguments)]
146pub fn gdn_decode_wy3(
147    gpu: &dyn GpuBackend,
148    kernel: KernelHandle,
149    h_state: DevicePtr,
150    query: DevicePtr,
151    key: DevicePtr,
152    value: DevicePtr,
153    gate: DevicePtr,
154    beta: DevicePtr,
155    output: DevicePtr,
156    h_state_inter0: DevicePtr,
157    h_state_inter1: DevicePtr,
158    batch_size: u32,
159    num_k_heads: u32,
160    num_v_heads: u32,
161    k_dim: u32,
162    v_dim: u32,
163    qk_stride: u32,
164    v_stride: u32,
165    gb_stride: u32,
166    // false = contiguous state bases indexed by (b*num_v_heads+vh);
167    // true  = device pointer TABLES, one entry per sequence. See
168    // `gdn_decode_wy4` for the full rationale — contiguous is only correct at
169    // batch_size==1 because the intermediates' pool stride is
170    // num_intermediates x h_state's.
171    state_is_table: bool,
172    stream: u64,
173) -> Result<()> {
174    // HARD guard, not debug_assert: this compiles out in release, which is
175    // exactly where the corruption would be silent.
176    anyhow::ensure!(
177        state_is_table || batch_size == 1,
178        "gdn_decode_wy3: contiguous state addressing is only valid at \
179         batch_size==1 (got {batch_size}) — the intermediates' pool stride is \
180         num_intermediates x h_state's, so sequence 1's Hi0 would land on \
181         sequence 0's Hi1. Stage pointer tables and pass state_is_table=true."
182    );
183    KernelLaunch::new(gpu, kernel)
184        .grid([num_v_heads, batch_size, 1])
185        .block([128, 1, 1])
186        .arg_ptr(h_state)
187        .arg_ptr(query)
188        .arg_ptr(key)
189        .arg_ptr(value)
190        .arg_ptr(gate)
191        .arg_ptr(beta)
192        .arg_ptr(output)
193        .arg_ptr(h_state_inter0)
194        .arg_ptr(h_state_inter1)
195        .arg_u32(batch_size)
196        .arg_u32(num_k_heads)
197        .arg_u32(num_v_heads)
198        .arg_u32(k_dim)
199        .arg_u32(v_dim)
200        .arg_u32(qk_stride)
201        .arg_u32(v_stride)
202        .arg_u32(gb_stride)
203        .arg_u32(u32::from(state_is_table))
204        .launch(stream)
205}
206
207/// WY-chunkwise 4-token GDN decode (2-pass algorithm).
208///
209/// All 4 H^T @ k_t dot products computed in a single pass, then WY correction
210/// derives v_new values. Second pass applies all 4 state updates + outputs.
211/// 2 passes vs 5, reducing memory traffic by 60%.
212///
213/// Grid: (num_v_heads, batch, 1)  Block: (128, 1, 1)
214#[allow(clippy::too_many_arguments)]
215pub fn gdn_decode_wy4(
216    gpu: &dyn GpuBackend,
217    kernel: KernelHandle,
218    h_state: DevicePtr,
219    query: DevicePtr,
220    key: DevicePtr,
221    value: DevicePtr,
222    gate: DevicePtr,
223    beta: DevicePtr,
224    output: DevicePtr,
225    h_state_inter0: DevicePtr,
226    h_state_inter1: DevicePtr,
227    h_state_inter2: DevicePtr,
228    batch_size: u32,
229    num_k_heads: u32,
230    num_v_heads: u32,
231    k_dim: u32,
232    v_dim: u32,
233    qk_stride: u32,
234    v_stride: u32,
235    gb_stride: u32,
236    // false = contiguous state bases indexed by (b*num_v_heads+vh);
237    // true  = device pointer TABLES, one entry per sequence.
238    //
239    // Contiguous is only correct at batch_size==1: it assumes the intermediates
240    // share h_state's batch stride, but the pool's intermediate stride is
241    // num_intermediates x larger, so at n>1 sequence 1's Hi0 lands on sequence
242    // 0's Hi1 — silent cross-sequence rollback corruption. Pass true with staged
243    // tables for any batched verify. false is byte-identical to the old kernel.
244    state_is_table: bool,
245    stream: u64,
246) -> Result<()> {
247    // HARD guard, not debug_assert: this compiles out in release, which is
248    // exactly where the corruption would be silent.
249    anyhow::ensure!(
250        state_is_table || batch_size == 1,
251        "gdn_decode_wy4: contiguous state addressing is only valid at \
252         batch_size==1 (got {batch_size}) — the intermediates' pool stride is \
253         num_intermediates x h_state's, so sequence 1's Hi0 would land on \
254         sequence 0's Hi1. Stage pointer tables and pass state_is_table=true."
255    );
256    KernelLaunch::new(gpu, kernel)
257        .grid([num_v_heads, batch_size, 1])
258        .block([128, 1, 1])
259        .arg_ptr(h_state)
260        .arg_ptr(query)
261        .arg_ptr(key)
262        .arg_ptr(value)
263        .arg_ptr(gate)
264        .arg_ptr(beta)
265        .arg_ptr(output)
266        .arg_ptr(h_state_inter0)
267        .arg_ptr(h_state_inter1)
268        .arg_ptr(h_state_inter2)
269        .arg_u32(batch_size)
270        .arg_u32(num_k_heads)
271        .arg_u32(num_v_heads)
272        .arg_u32(k_dim)
273        .arg_u32(v_dim)
274        .arg_u32(qk_stride)
275        .arg_u32(v_stride)
276        .arg_u32(gb_stride)
277        .arg_u32(u32::from(state_is_table))
278        .launch(stream)
279}
280
281/// WY-Chunkwise Gated Delta Rule, pool-layout intermediates — K-generic
282/// launch shared by the K=17 DFlash verify (`gated_delta_rule_wy17`) and the
283/// chain-verify K∈{5..8} instantiations (`gated_delta_rule_wy5..wy8`, one
284/// templated source `gated_delta_rule_wyn.cu`). K is compile-time in the
285/// kernel; the caller selects it via the `kernel` handle. Computes K H·k dot
286/// products in 1 pass over H, applies WY algebraic correction over K tokens
287/// (K*(K-1)/2 inter-token k-dots), then applies K state updates in a second
288/// fused pass writing Hi_0..Hi_{K-2} + final H.
289///
290/// `h_state_inter_base` points to a contiguous pool of (K-1) intermediate
291/// H states per (layer, slot). Each Hi_t is at
292/// `h_state_inter_base + t * inter_stride_floats` (per (b, vh) sub-region).
293#[allow(clippy::too_many_arguments)]
294pub fn gdn_decode_wyn(
295    gpu: &dyn GpuBackend,
296    kernel: KernelHandle,
297    h_state: DevicePtr,
298    query: DevicePtr,
299    key: DevicePtr,
300    value: DevicePtr,
301    gate: DevicePtr,
302    beta: DevicePtr,
303    output: DevicePtr,
304    h_state_inter_base: DevicePtr,
305    inter_stride_floats: u32,
306    batch_size: u32,
307    num_k_heads: u32,
308    num_v_heads: u32,
309    k_dim: u32,
310    v_dim: u32,
311    qk_stride: u32,
312    v_stride: u32,
313    gb_stride: u32,
314    stream: u64,
315) -> Result<()> {
316    // `gdn_decode_wyn`'s kernel hardcodes the CONTIGUOUS state stride
317    // ((b*num_v_heads+vh)*hv) for BOTH h_state and the intermediates. That is
318    // wrong for the intermediates, whose pool stride is num_intermediates x
319    // larger — at batch_size>1 sequence 1's Hi0 lands on sequence 0's Hi1,
320    // silently corrupting cross-sequence rollback. Only wy4 has the
321    // `state_is_table` pointer-table form that sidesteps this. Refuse rather
322    // than corrupt; port the table form (see gated_delta_rule_wy4.cu) before
323    // enabling a batched verify at K<4.
324    anyhow::ensure!(
325        batch_size == 1,
326        "gdn_decode_wyn: contiguous state addressing is only valid at batch_size==1 \
327         (got {batch_size}); port the wy4 `state_is_table` pointer-table form first"
328    );
329    KernelLaunch::new(gpu, kernel)
330        .grid([num_v_heads, batch_size, 1])
331        .block([128, 1, 1])
332        .arg_ptr(h_state)
333        .arg_ptr(query)
334        .arg_ptr(key)
335        .arg_ptr(value)
336        .arg_ptr(gate)
337        .arg_ptr(beta)
338        .arg_ptr(output)
339        .arg_ptr(h_state_inter_base)
340        .arg_u32(inter_stride_floats)
341        .arg_u32(batch_size)
342        .arg_u32(num_k_heads)
343        .arg_u32(num_v_heads)
344        .arg_u32(k_dim)
345        .arg_u32(v_dim)
346        .arg_u32(qk_stride)
347        .arg_u32(v_stride)
348        .arg_u32(gb_stride)
349        .launch(stream)
350}
351
352/// Fused 2-token conv1d sliding window update + SiLU.
353///
354/// Each thread handles one channel independently. The 2-token dependency
355/// (token 1's window includes token 0's input) is resolved in registers.
356/// Saves intermediate conv_state (after token 0) for rollback.
357///
358/// Kernel: `causal_conv1d_update_chunk2(conv_state, input, weight, bias,
359///          output, conv_state_intermediate, batch, dim, d_conv)`
360/// Grid: (ceil(dim/256), batch, 1)  Block: (256, 1, 1)
361#[allow(clippy::too_many_arguments)]
362pub fn conv1d_update_chunk2(
363    gpu: &dyn GpuBackend,
364    kernel: KernelHandle,
365    conv_state: DevicePtr,
366    input: DevicePtr,
367    weight: &DenseWeight,
368    output: DevicePtr,
369    conv_state_intermediate: DevicePtr,
370    d_inner: u32,
371    d_conv: u32,
372    batch_size: u32,
373    stream: u64,
374) -> Result<()> {
375    let bias_ptr = DevicePtr::NULL;
376    KernelLaunch::new(gpu, kernel)
377        .grid([div_ceil(d_inner, 256), batch_size, 1])
378        .block([256, 1, 1])
379        .arg_ptr(conv_state)
380        .arg_ptr(input)
381        .arg_ptr(weight.weight)
382        .arg_ptr(bias_ptr)
383        .arg_ptr(output)
384        .arg_ptr(conv_state_intermediate)
385        .arg_u32(batch_size)
386        .arg_u32(d_inner)
387        .arg_u32(d_conv)
388        .launch(stream)
389}
390
391// ── Activations / Element-wise ─────────────────────────────────────