1pub const GDN_KERNELS: &str = r#"
33// Split one f32 activation into two int8 words: a ~= s*(qh + ql/127). Lives here because it is the first
34// device code emitted, and both the GDN gate norm and the projection producers need it.
35__device__ __forceinline__ void lgx_q8hl(float t, signed char* __restrict__ qh,
36 signed char* __restrict__ ql, long i){
37 int h = __float2int_rn(t);
38 h = h > 127 ? 127 : (h < -127 ? -127 : h);
39 int l = __float2int_rn((t - (float)h) * 127.0f);
40 qh[i] = (signed char)h;
41 ql[i] = (signed char)l;
42}
43
44__device__ __forceinline__ float gdn_warp_sum(float v){
45 for(int off=16; off>0; off>>=1) v += __shfl_down_sync(0xffffffffu, v, off);
46 return v;
47}
48// Three block-wide reductions sharing one traversal of the memory row.
49__device__ __forceinline__ void gdn_block_sum3(float a, float b, float c, float* oa, float* ob, float* oc){
50 __shared__ float sa[32], sb[32], sc[32];
51 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = (blockDim.x + 31) >> 5;
52 a = gdn_warp_sum(a); b = gdn_warp_sum(b); c = gdn_warp_sum(c);
53 if(lane == 0){ sa[warp] = a; sb[warp] = b; sc[warp] = c; }
54 __syncthreads();
55 if(threadIdx.x == 0){
56 float ta = 0.f, tb = 0.f, tc = 0.f;
57 for(int i = 0; i < nw; ++i){ ta += sa[i]; tb += sb[i]; tc += sc[i]; }
58 *oa = ta; *ob = tb; *oc = tc;
59 }
60 __syncthreads();
61}
62
63// grid = (HV, D), block = D threads. Thread b owns column b of row a of head h's memory.
64// ONE WARP PER STATE ROW. The state is D x D per value head; row `a` needs S[a]·k, S[a]·q and k·q, which is
65// three reductions over D. Giving the row to a warp makes all three warp shuffles over registers the lanes
66// already hold — no __syncthreads, and the row is re-used from those same registers for the write-back, so
67// the D x D memory is read once and written once. The block-per-row form this replaces launched D blocks per
68// head (295k blocks per token across 48 layers) and paid three block-wide barriers to reduce one float each.
69// `sreg` is sized for D <= 256; the loops are lane-strided so any D, including D < 32, is handled.
70__global__ void gdn_step(const float* __restrict__ q, const float* __restrict__ k, const float* __restrict__ v,
71 const float* __restrict__ bet, const float* __restrict__ alp,
72 float* __restrict__ S, float* __restrict__ o, int D, int NK){
73 const unsigned m = 0xffffffffu;
74 int h = blockIdx.x, lane = threadIdx.x & 31, warp = threadIdx.x >> 5;
75 int a = blockIdx.y * (blockDim.x >> 5) + warp;
76 if(a >= D) return;
77 // The key head this value head reads. llama.cpp broadcasts k/q from NK heads to NV with
78 // `ggml_repeat_4d`, and ggml_repeat TILES (index modulo) rather than repeat-interleaving — so the
79 // pairing is h % NK ([0,1,..,15,0,1,..,15,..]), NOT h / (NV/NK) ([0,0,0,1,1,1,..]).
80 int g = h % NK;
81 const float* kk = k + (long)g * D;
82 const float* qq = q + (long)g * D;
83 long base = (long)h * D * D + (long)a * D;
84
85 float sreg[8];
86 float u_ = 0.f, w_ = 0.f, kq_ = 0.f;
87 int n = 0;
88 for(int b = lane; b < D; b += 32){
89 float sv = S[base + b], kb = kk[b], qb = qq[b];
90 sreg[n++] = sv; u_ += sv * kb; w_ += sv * qb; kq_ += kb * qb;
91 }
92 for(int sh = 16; sh > 0; sh >>= 1){
93 u_ += __shfl_down_sync(m, u_, sh); w_ += __shfl_down_sync(m, w_, sh); kq_ += __shfl_down_sync(m, kq_, sh);
94 }
95 u_ = __shfl_sync(m, u_, 0); w_ = __shfl_sync(m, w_, 0); kq_ = __shfl_sync(m, kq_, 0);
96
97 float alpha = alp[h], beta = bet[h];
98 float coef = beta * (v[(long)h * D + a] - alpha * u_);
99 // The 1/sqrt(D) output scale llama.cpp applies (`attn_col * scale`). It does NOT cancel under the
100 // following RMSNorm: the recurrence output is tiny (RMS ~1e-2), so `eps` is comparable to the scaled
101 // variance and `s*x/sqrt(s^2*m + eps) != x/sqrt(m + eps)`. Dropping it scaled the whole layer by ~1.25x.
102 if(lane == 0) o[(long)h * D + a] = (alpha * w_ + coef * kq_) * rsqrtf((float)D);
103 n = 0;
104 for(int b = lane; b < D; b += 32) S[base + b] = alpha * sreg[n++] + coef * kk[b];
105}
106
107// grid = (HV), block = D. RMSNorm the head and scale by the shared weight, THEN gate by silu(z) — the order
108// llama.cpp's `build_norm_gated` uses for this architecture. (HF's Qwen3-Next gates first; Qwen3.5 does not,
109// and the checkpoint follows Qwen3.5.)
110__global__ void gdn_gate_norm(const float* __restrict__ z, const float* __restrict__ gn,
111 const float* __restrict__ o, float* __restrict__ out, int D, float eps){
112 int h = blockIdx.x, a = threadIdx.x;
113 long i = (long)h * D + a;
114 float zz = z[i];
115 float val = o[i];
116 __shared__ float ss[32];
117 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = (blockDim.x + 31) >> 5;
118 float p = gdn_warp_sum(val * val);
119 if(lane == 0) ss[warp] = p;
120 __syncthreads();
121 __shared__ float den;
122 if(threadIdx.x == 0){ float t = 0.f; for(int j = 0; j < nw; ++j) t += ss[j]; den = sqrtf(t / (float)D + eps); }
123 __syncthreads();
124 out[i] = (gn[a] * val / den) * (zz / (1.0f + __expf(-zz)));
125}
126
127// The same epilogue with the ACTIVATION QUANTIZATION fused in. The o-projection that follows consumes int8,
128// and `blockDim == D` with `D % 32 == 0` means one warp owns exactly one 32-element quantization group — so
129// the group amax is a warp shuffle over registers the block already holds, not a second pass over global
130// memory. Saves a launch and a full round-trip of the value vector per GDN layer.
131__global__ void gdn_gate_norm_q(const float* __restrict__ z, const float* __restrict__ gn,
132 const float* __restrict__ o, float* __restrict__ out,
133 signed char* __restrict__ xq, float* __restrict__ xs, int D, float eps){
134 int h = blockIdx.x, a = threadIdx.x;
135 long i = (long)h * D + a;
136 float zz = z[i];
137 float val = o[i];
138 __shared__ float ss[32];
139 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = (blockDim.x + 31) >> 5;
140 float p = gdn_warp_sum(val * val);
141 if(lane == 0) ss[warp] = p;
142 __syncthreads();
143 __shared__ float den;
144 if(threadIdx.x == 0){ float t = 0.f; for(int j = 0; j < nw; ++j) t += ss[j]; den = sqrtf(t / (float)D + eps); }
145 __syncthreads();
146 float y = (gn[a] * val / den) * (zz / (1.0f + __expf(-zz)));
147 out[i] = y;
148 float am = fabsf(y);
149 for(int sh = 16; sh > 0; sh >>= 1) am = fmaxf(am, __shfl_down_sync(0xffffffffu, am, sh));
150 am = __shfl_sync(0xffffffffu, am, 0);
151 float sc = am > 0.f ? am / 127.0f : 1.0f;
152 if(lane == 0) xs[i >> 5] = sc;
153 xq[i] = (signed char)__float2int_rn(y / sc);
154}
155
156// The gate norm with the two-word activation split, for the lane that keeps the activation nearly exact.
157__global__ void gdn_gate_norm_hl(const float* __restrict__ z, const float* __restrict__ gn,
158 const float* __restrict__ o, float* __restrict__ out,
159 signed char* __restrict__ xq, signed char* __restrict__ xl,
160 float* __restrict__ xs, int D, float eps){
161 int h = blockIdx.x, a = threadIdx.x;
162 long i = (long)h * D + a;
163 float zz = z[i];
164 float val = o[i];
165 __shared__ float ss[32];
166 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = (blockDim.x + 31) >> 5;
167 float p = gdn_warp_sum(val * val);
168 if(lane == 0) ss[warp] = p;
169 __syncthreads();
170 __shared__ float den;
171 if(threadIdx.x == 0){ float t = 0.f; for(int j = 0; j < nw; ++j) t += ss[j]; den = sqrtf(t / (float)D + eps); }
172 __syncthreads();
173 float y = (gn[a] * val / den) * (zz / (1.0f + __expf(-zz)));
174 out[i] = y;
175 float am = fabsf(y);
176 for(int sh = 16; sh > 0; sh >>= 1) am = fmaxf(am, __shfl_down_sync(0xffffffffu, am, sh));
177 am = __shfl_sync(0xffffffffu, am, 0);
178 float sc = am > 0.f ? am / 127.0f : 1.0f;
179 if(lane == 0) xs[i >> 5] = sc;
180 lgx_q8hl(y / sc, xq, xl, i);
181}
182"#;
183
184pub const QWEN38_KERNELS: &str = r#"
189// Depthwise causal conv over C channels with a `T`-deep ring, silu FUSED into the epilogue. The ring is the
190// layer's carried state: shift up one, append this token, reduce. Depthwise means no cross-channel mixing, so
191// one thread owns a channel end to end and the whole thing is a single pass.
192__global__ void gk_conv_silu(const float* __restrict__ x, const float* __restrict__ wc,
193 float* __restrict__ cs, float* __restrict__ y, int C, int T){
194 int c = blockIdx.x * blockDim.x + threadIdx.x; if(c >= C) return;
195 for(int t = 0; t < T - 1; ++t) cs[(long)t * C + c] = cs[(long)(t + 1) * C + c];
196 cs[(long)(T - 1) * C + c] = x[c];
197 float acc = 0.f;
198 for(int t = 0; t < T; ++t) acc += cs[(long)t * C + c] * wc[(long)t * C + c];
199 y[c] = acc / (1.0f + __expf(-acc)); // silu, fused
200}
201
202// The conv with BOTH epilogues fused: silu, then — for the leading `nl2` head-blocks, which hold q and k —
203// the per-head L2 normalize GDN applies before the delta update. Launched one block per head-sized chunk
204// (`blockDim == D`, `grid == C / D`), so the normalize is a reduction over registers the block already owns.
205// The v region (blocks >= nl2) takes the silu and stops. One launch where there were two, and q/k never
206// leave the SM between the two steps.
207__global__ void gk_conv_silu_l2(const float* __restrict__ x, const float* __restrict__ wc,
208 float* __restrict__ cs, float* __restrict__ y, int C, int T,
209 int nl2, float eps){
210 int c = blockIdx.x * blockDim.x + threadIdx.x; if(c >= C) return;
211 for(int t = 0; t < T - 1; ++t) cs[(long)t * C + c] = cs[(long)(t + 1) * C + c];
212 cs[(long)(T - 1) * C + c] = x[c];
213 float acc = 0.f;
214 for(int t = 0; t < T; ++t) acc += cs[(long)t * C + c] * wc[(long)t * C + c];
215 float v = acc / (1.0f + __expf(-acc)); // silu, fused
216 if(blockIdx.x >= nl2){ y[c] = v; return; } // the v region needs no normalize
217 __shared__ float ss[32];
218 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = (blockDim.x + 31) >> 5;
219 float p = gdn_warp_sum(v * v);
220 if(lane == 0) ss[warp] = p;
221 __syncthreads();
222 __shared__ float den;
223 if(threadIdx.x == 0){ float t = 0.f; for(int j = 0; j < nw; ++j) t += ss[j]; den = sqrtf(t + eps); }
224 __syncthreads();
225 y[c] = v / den;
226}
227
228// Per-head L2 normalize in place — GDN normalizes q and k before the delta update. grid = heads, block = D.
229__global__ void gk_l2norm_heads(float* __restrict__ x, int D, float eps){
230 int h = blockIdx.x, i = threadIdx.x; long o = (long)h * D + i;
231 float v = x[o];
232 __shared__ float ss[32];
233 int lane = threadIdx.x & 31, warp = threadIdx.x >> 5, nw = (blockDim.x + 31) >> 5;
234 float p = gdn_warp_sum(v * v);
235 if(lane == 0) ss[warp] = p;
236 __syncthreads();
237 __shared__ float den;
238 if(threadIdx.x == 0){ float t = 0.f; for(int j = 0; j < nw; ++j) t += ss[j]; den = sqrtf(t + eps); }
239 __syncthreads();
240 x[o] = v / den;
241}
242
243// Both GDN gates in one launch: write strength and forget rate, one value per value head.
244// beta = sigmoid(br)
245// decay = (1 + e^(ar + dt))^rate
246// The paper writes decay as exp(−exp(A_log)·softplus(ar+dt)), but the GGUF conversion ALREADY stores
247// −exp(A_log) in `ssm_a` (verified: all 48 values negative, |v| < 0.08), so `rate` is used directly —
248// exponentiating it again turns a ^(−0.04) long memory into a ^(−0.96) one that forgets almost every token.
249// softplus is ln(1+e^u), so the logarithm cancels against the outer exponential: one fewer transcendental.
250__global__ void gk_gdn_gates(const float* __restrict__ br, const float* __restrict__ ar,
251 const float* __restrict__ dt, const float* __restrict__ alog,
252 float* __restrict__ beta, float* __restrict__ decay, int NV){
253 int i = blockIdx.x * blockDim.x + threadIdx.x; if(i >= NV) return;
254 beta[i] = 1.0f / (1.0f + __expf(-br[i]));
255 decay[i] = powf(1.0f + __expf(ar[i] + dt[i]), alog[i]);
256}
257
258// Per-head RMSNorm + PARTIAL RoPE. Qwen3.8 rotates `rot` of each head's `hd` dims (64 of 256) and passes the
259// rest through, and the inverse frequencies are computed over `rot`, not `hd` — using `hd` there would detune
260// every rotation while still producing plausible-looking output.
261__global__ void gk_qk_norm_rope_part(float* __restrict__ base, const int* __restrict__ d_pos, int stride,
262 const float* __restrict__ nw, int n_heads, int hd, int rot,
263 float rbase, float eps){
264 int head = blockIdx.x; if(head >= n_heads) return;
265 float* h = base + (long)(*d_pos) * stride + (long)head * hd;
266 __shared__ float sh[128];
267 float s = 0.f; for(int i = threadIdx.x; i < hd; i += blockDim.x) s += h[i] * h[i];
268 sh[threadIdx.x] = s; __syncthreads();
269 for(int k = blockDim.x / 2; k > 0; k >>= 1){ if(threadIdx.x < k) sh[threadIdx.x] += sh[threadIdx.x + k]; __syncthreads(); }
270 float sc = rsqrtf(sh[0] / hd + eps); __syncthreads();
271 for(int i = threadIdx.x; i < hd; i += blockDim.x) h[i] = h[i] * sc * nw[i];
272 __syncthreads();
273 int half = rot / 2, pos = *d_pos;
274 for(int i = threadIdx.x; i < half; i += blockDim.x){
275 float f = powf(rbase, -2.0f * (float)i / (float)rot); // over the ROTARY width
276 float a = pos * f, c = cosf(a), si = sinf(a);
277 float x0 = h[i], x1 = h[i + half];
278 h[i] = x0 * c - x1 * si; h[i + half] = x0 * si + x1 * c;
279 }
280 // dims [rot, hd) are carried through untouched — that is what "partial" means.
281}
282
283// Gated attention's output gate: ctx *= sigmoid(g). The checkpoint packs g into the Q projection, so this is
284// the only extra work a gated attention layer does over an ordinary one.
285__global__ void gk_gate_mul(float* __restrict__ ctx, const float* __restrict__ g, int n){
286 int i = blockIdx.x * blockDim.x + threadIdx.x;
287 if(i < n) ctx[i] *= 1.0f / (1.0f + __expf(-g[i]));
288}
289"#;
290
291#[allow(clippy::too_many_arguments)]
296pub fn emit_gated_delta_forward(
297 q: &[f64],
298 k: &[f64],
299 v: &[f64],
300 z: &[f64],
301 beta: &[f64],
302 alpha: &[f64],
303 s0: &[f64],
304 gn: &[f64],
305 hv: usize,
306 hk: usize,
307 d: usize,
308) -> String {
309 let arr = |name: &str, xs: &[f64]| {
310 let body = xs
311 .iter()
312 .map(|x| format!("{:.17e}f", x))
313 .collect::<Vec<_>>()
314 .join(",");
315 format!("static const float {name}[] = {{{body}}};\n")
316 };
317 format!(
318 "#include <cstdio>\n#include <cmath>\n#include <cuda_runtime.h>\n{kern}\n\
319 {aq}{ak}{av}{az}{ab}{aa}{as_}{ag}\n\
320 int main(){{\n\
321 \x20 const int D={d}, HV={hv}, NK={hk};\n\
322 \x20 float *dq,*dk,*dv,*dz,*db,*da,*dS,*dgn,*dO,*dOut;\n\
323 \x20 cudaMalloc(&dq,sizeof(hq)); cudaMemcpy(dq,hq,sizeof(hq),cudaMemcpyHostToDevice);\n\
324 \x20 cudaMalloc(&dk,sizeof(hk_)); cudaMemcpy(dk,hk_,sizeof(hk_),cudaMemcpyHostToDevice);\n\
325 \x20 cudaMalloc(&dv,sizeof(hv_)); cudaMemcpy(dv,hv_,sizeof(hv_),cudaMemcpyHostToDevice);\n\
326 \x20 cudaMalloc(&dz,sizeof(hz)); cudaMemcpy(dz,hz,sizeof(hz),cudaMemcpyHostToDevice);\n\
327 \x20 cudaMalloc(&db,sizeof(hb)); cudaMemcpy(db,hb,sizeof(hb),cudaMemcpyHostToDevice);\n\
328 \x20 cudaMalloc(&da,sizeof(ha)); cudaMemcpy(da,ha,sizeof(ha),cudaMemcpyHostToDevice);\n\
329 \x20 cudaMalloc(&dS,sizeof(hs)); cudaMemcpy(dS,hs,sizeof(hs),cudaMemcpyHostToDevice);\n\
330 \x20 cudaMalloc(&dgn,sizeof(hg)); cudaMemcpy(dgn,hg,sizeof(hg),cudaMemcpyHostToDevice);\n\
331 \x20 cudaMalloc(&dO,(size_t)HV*D*4); cudaMalloc(&dOut,(size_t)HV*D*4);\n\
332 \x20 gdn_step<<<dim3(HV,(D+3)/4),128>>>(dq,dk,dv,db,da,dS,dO,D,NK);\n\
333 \x20 gdn_gate_norm<<<HV,D>>>(dz,dgn,dO,dOut,D,1e-6f);\n\
334 \x20 cudaDeviceSynchronize();\n\
335 \x20 float* out=(float*)malloc((size_t)HV*D*4); cudaMemcpy(out,dOut,(size_t)HV*D*4,cudaMemcpyDeviceToHost);\n\
336 \x20 float* st=(float*)malloc(sizeof(hs)); cudaMemcpy(st,dS,sizeof(hs),cudaMemcpyDeviceToHost);\n\
337 \x20 for(int i=0;i<HV*D;++i) printf(\"%.9g\\n\", out[i]);\n\
338 \x20 for(size_t i=0;i<sizeof(hs)/4;++i) printf(\"%.9g\\n\", st[i]);\n\
339 \x20 return 0;\n}}\n",
340 kern = GDN_KERNELS,
341 aq = arr("hq", q), ak = arr("hk_", k), av = arr("hv_", v), az = arr("hz", z),
342 ab = arr("hb", beta), aa = arr("ha", alpha), as_ = arr("hs", s0), ag = arr("hg", gn),
343 d = d, hv = hv, hk = hk,
344 )
345}