#ifdef MNN_SUPPORT_FP16 #pragma OPENCL EXTENSION cl_khr_fp16 : enable #endif // Kernel 1: Depthwise Conv1D + SiLU // Each work-item processes one (batch*channel, seq_pos) element. // Input: qkv [B, D, L], conv_state [B, D, conv_state_size], conv_weight [D, 1, K] // Output: conv_out [B, D, L] // conv_state is read but NOT updated here (updated by separate kernel). __kernel void linear_attn_conv_silu( __private const int global_dim0, __global const FLOAT* qkv, __global const FLOAT* conv_state, __global const FLOAT* conv_weight, __global FLOAT* conv_out, __private const int batch, __private const int conv_dim, __private const int seq_len, __private const int kernel_size, __private const int conv_state_size) { const int gid = get_global_id(0); if (gid >= global_dim0) return; const int L = seq_len; const int D = conv_dim; const int K = kernel_size; const int css = conv_state_size; // Decompose: gid -> (batch_chan, seq_pos) const int l = gid % L; const int bd = gid / L; const int b = bd / D; const int d = bd % D; // Compute valid convolution for position l // Padded input = [conv_state[b,d,:], qkv[b,d,:]] // padded[pos]: if pos < css -> conv_state[b*D*css + d*css + pos] // else -> qkv[b*D*L + d*L + (pos - css)] float sum = 0.0f; for (int k = 0; k < K; ++k) { int pos = l + k; float input_val; if (pos < css) { input_val = (float)conv_state[b * D * css + d * css + pos]; } else { input_val = (float)qkv[b * D * L + d * L + (pos - css)]; } sum += input_val * (float)conv_weight[d * K + k]; } // SiLU activation: x * sigmoid(x) float sigmoid_val = 1.0f / (1.0f + exp(-sum)); conv_out[b * D * L + d * L + l] = (FLOAT)(sum * sigmoid_val); } // Kernel 2: Update conv state with last (K-1) elements of padded input // new_state[i] = padded[L + i], where padded = cat(old_state[css], qkv[L]) // Must execute AFTER linear_attn_conv_silu (which reads conv_state). __kernel void linear_attn_conv_state_update( __private const int global_dim0, __global const FLOAT* qkv, __global FLOAT* conv_state, __private const int batch, __private const int conv_dim, __private const int seq_len, __private const int conv_state_size) { const int gid = get_global_id(0); if (gid >= global_dim0) return; const int L = seq_len; const int D = conv_dim; const int css = conv_state_size; const int i = gid % css; const int bd = gid / css; const int b = bd / D; const int d = bd % D; // new_state[i] = padded[L + i] // padded = cat(old_state[css], qkv[L]) // position (L + i) in padded: if (L+i) < css -> old_state, else -> qkv[(L+i) - css] int pos = L + i; FLOAT val; if (pos < css) { val = conv_state[b * D * css + d * css + pos]; } else { val = qkv[b * D * L + d * L + (pos - css)]; } // Safe: we write to index i, read from index (L+i) where L >= 1, so (L+i) > i always conv_state[b * D * css + d * css + i] = val; } // Kernel 3: Gated Delta Rule (Steps 2-5 fused) // Each work-item processes one (batch, head) pair across all timesteps. // Uses float32 exclusively for numerical stability of the recurrence. __kernel void linear_attn_gated_delta_rule( __global const FLOAT* conv_out, __global const FLOAT* gate, __global const FLOAT* beta_in, __global FLOAT* recurrent_state, __global FLOAT* attn_out, __private const int batch, __private const int conv_dim, __private const int seq_len, __private const int num_k_heads, __private const int num_v_heads, __private const int d_k, __private const int d_v, __private const int key_dim, __private const int val_dim, __private const int gqa_factor, __private const float q_scale) { const int dv4 = (d_v + 3) / 4; const int total = dv4 * num_v_heads * batch; const int gid = get_global_id(1); if (gid >= total) return; const int x = (gid % dv4) << 2; const int bh = gid / dv4; const int h = bh % num_v_heads; const int b = bh / num_v_heads; const int k_head = h / gqa_factor; // GQA: corresponding K-head __local float4 __sum[LOCAL_SIZE]; __local float4 rec_local[K_SIZE]; __local float key_local[K_SIZE]; const int lid = get_local_id(0); // State pointer: recurrent_state layout [B, H, d_k, d_v] const int state_offset = (b * num_v_heads + h) * K_SIZE * d_v + x; #ifdef DECODE_PHASE const int conv_base = b * conv_dim; float g_t = (float)(gate[b * num_v_heads + h]); float4 beta_t = (float4)(beta_in[b * num_v_heads + h]); float4 decay_val = (float4)(exp(g_t)); const int out_offset = b * num_v_heads * d_v + h * d_v; float4 s = (float4)(0); for (int i = lid; i < K_SIZE; i+=LOCAL_SIZE) { float4 rec = convert_float4(vload4(0, recurrent_state + state_offset + i * d_v))* decay_val; float key = (float)(conv_out[conv_base + key_dim + k_head * K_SIZE + i]); s += rec * (float4)(key); rec_local[i] = rec; key_local[i] = key; } __sum[lid] = s; barrier(CLK_LOCAL_MEM_FENCE); for(int i = LOCAL_SIZE/2; i > 0; i /= 2){ if (lid < i) __sum[lid] += __sum[lid + i]; barrier(CLK_LOCAL_MEM_FENCE); } s = __sum[0]; float4 v_data = convert_float4(vload4(0, conv_out + conv_base + 2 * key_dim + h * d_v + x)); float4 delta = beta_t * (v_data - s); s = (float4)(0); for (int i = lid; i < K_SIZE; i+=LOCAL_SIZE) { float4 recurrent_state_data = rec_local[i] + (float4)(key_local[i]) * delta; s += recurrent_state_data * (float4)(conv_out[conv_base + (k_head * K_SIZE + i)]) * (float4)(q_scale); vstore4(CONVERT_FLOAT4(recurrent_state_data), 0, recurrent_state + state_offset + i * d_v); } __sum[lid] = s; barrier(CLK_LOCAL_MEM_FENCE); for(int i = LOCAL_SIZE/2; i > 0; i /= 2){ if (lid < i) __sum[lid] += __sum[lid + i]; barrier(CLK_LOCAL_MEM_FENCE); } s = __sum[0]; if(lid == 0){ vstore4(CONVERT_FLOAT4(s), 0, attn_out + out_offset + x); } #else for (int i = lid; i < K_SIZE; i+=LOCAL_SIZE) { rec_local[i] = convert_float4(vload4(0, recurrent_state + state_offset + i * d_v)); } for (int t = 0; t < seq_len; ++t) { const int conv_base = b * seq_len * conv_dim; float g_t = (float)(gate[b * seq_len * num_v_heads + t * num_v_heads + h]); float4 beta_t = (float4)(beta_in[b * seq_len * num_v_heads + t * num_v_heads + h]); float4 decay_val = (float4)(exp(g_t)); const int out_offset = (b * seq_len + t) * num_v_heads * d_v + h * d_v; float4 s = (float4)(0); for (int i = lid; i < K_SIZE; i+=LOCAL_SIZE) { float4 rec = rec_local[i] * decay_val; float key = (float)(conv_out[conv_base + (key_dim + k_head * K_SIZE + i) * seq_len + t]); s += rec * (float4)(key); key_local[i] = key; } __sum[lid] = s; barrier(CLK_LOCAL_MEM_FENCE); for(int i = LOCAL_SIZE/2; i > 0; i /= 2){ if (lid < i) __sum[lid] += __sum[lid + i]; barrier(CLK_LOCAL_MEM_FENCE); } s = __sum[0]; float4 v_data; v_data.x = (float)(conv_out[conv_base + (2 * key_dim + h * d_v + x) * seq_len + t]); v_data.y = (float)(conv_out[conv_base + (2 * key_dim + h * d_v + x + 1) * seq_len + t]); v_data.z = (float)(conv_out[conv_base + (2 * key_dim + h * d_v + x + 2) * seq_len + t]); v_data.w = (float)(conv_out[conv_base + (2 * key_dim + h * d_v + x + 3) * seq_len + t]); float4 delta = beta_t * (v_data - s); s = (float4)(0); for (int i = lid; i < K_SIZE; i+=LOCAL_SIZE) { float4 recurrent_state_data = rec_local[i] * decay_val + (float4)(key_local[i]) * delta; s += recurrent_state_data * (float4)(conv_out[conv_base + (k_head * K_SIZE + i) * seq_len + t]) * (float4)(q_scale); rec_local[i] = recurrent_state_data; } __sum[lid] = s; barrier(CLK_LOCAL_MEM_FENCE); for(int i = LOCAL_SIZE/2; i > 0; i /= 2){ if (lid < i) __sum[lid] += __sum[lid + i]; barrier(CLK_LOCAL_MEM_FENCE); } s = __sum[0]; if(lid == 0){ vstore4(CONVERT_FLOAT4(s), 0, attn_out + out_offset + x); } } for (int i = lid; i < K_SIZE; i+=LOCAL_SIZE) { vstore4(CONVERT_FLOAT4(rec_local[i]), 0, recurrent_state + state_offset + i * d_v); } #endif } __kernel void l2_norm(__global const FLOAT* input, __global FLOAT* output, __private const int conv_dim, __private const int head_k_dim, __private const int gqa_factor, __private const int key_dim, __private const int seq_len) { #ifdef USE_VEC const int hl = get_global_id(1); const int bk = get_global_id(2); const int seq_len4 = (seq_len + 3) / 4; const int h = hl / seq_len4; const int sq = hl % seq_len4; const int b = bk / 2; const int k = bk % 2; const int lid = get_local_id(0); const int k_head = h / gqa_factor; // GQA: corresponding K-head const int input_offset = (b * conv_dim + k * key_dim + k_head * head_k_dim) * seq_len + (sq << 2); __local float4 __sum[K_SIZE]; float4 sum = 0.0f; for(int i = lid; i < K_SIZE; i += K_SIZE){ float4 in = convert_float4(vload4(0, input + input_offset + i * seq_len)); sum += in * in; } __sum[lid] = sum; barrier(CLK_LOCAL_MEM_FENCE); for(int i = K_SIZE/2; i > 0; i /= 2){ if (lid < i) __sum[lid] += __sum[lid + i]; barrier(CLK_LOCAL_MEM_FENCE); } sum = __sum[0]; int remain = seq_len - (sq << 2); float4 invNorm = (float4)1.0f / sqrt(sum + (float4)1e-6f); if(remain == 1){ for(int i = lid; i < K_SIZE; i += K_SIZE){ float4 out = convert_float4(vload4(0, input + input_offset + i * seq_len)) * invNorm; output[input_offset + i * seq_len] = (FLOAT)out.s0; } }else if(remain == 2){ for(int i = lid; i < K_SIZE; i += K_SIZE){ float4 out = convert_float4(vload4(0, input + input_offset + i * seq_len)) * invNorm; vstore2(CONVERT_FLOAT2(out.s01), 0, output + input_offset + i * seq_len); } }else if(remain == 3){ for(int i = lid; i < K_SIZE; i += K_SIZE){ float4 out = convert_float4(vload4(0, input + input_offset + i * seq_len)) * invNorm; vstore3(CONVERT_FLOAT3(out.s012), 0, output + input_offset + i * seq_len); } }else{ for(int i = lid; i < K_SIZE; i += K_SIZE){ float4 out = convert_float4(vload4(0, input + input_offset + i * seq_len)) * invNorm; vstore4(CONVERT_FLOAT4(out), 0, output + input_offset + i * seq_len); } } #else const int h = get_global_id(1); const int bk = get_global_id(2); const int b = bk / 2; const int k = bk % 2; const int lid = get_local_id(0); const int k_head = h / gqa_factor; // GQA: corresponding K-head const int input_offset = b * conv_dim + k * key_dim + k_head * head_k_dim; __local float __sum[K_SIZE]; float sum = 0.0f; for(int i = lid; i < K_SIZE; i += K_SIZE){ float in = (float)(input[input_offset + i]); sum += in * in; } __sum[lid] = sum; barrier(CLK_LOCAL_MEM_FENCE); for(int i = K_SIZE/2; i > 0; i /= 2){ if (lid < i) __sum[lid] += __sum[lid + i]; barrier(CLK_LOCAL_MEM_FENCE); } sum = __sum[0]; float invNorm = 1.0f / sqrt(sum + 1e-6f); for(int i = lid; i < K_SIZE; i += K_SIZE){ float out = (float)(input[input_offset + i]) * invNorm; output[input_offset + i] = (FLOAT)out; } #endif } #ifdef CHUNK_PREFILL // ======================== Chunked Prefill Kernels ======================== // Implements torch_chunk_gated_delta_rule from modeling_qwen3_5.py // Decomposes sequential recurrence into chunk-parallel operations. // CHUNK_SIZE must be defined at compile time (e.g. -DCHUNK_SIZE=64). // Kernel C1: Cumulative sum of gate values within each chunk // GWS: {num_v_heads, num_chunks, batch} __kernel void chunk_g_cumsum( __global const FLOAT* gate, // [B, L, H] __global float* g_cumsum, // [B, H, num_chunks, CHUNK_SIZE] (float32 for precision) __private const int num_v_heads, __private const int seq_len, __private const int num_chunks) { const int h = get_global_id(0); const int c = get_global_id(1); const int b = get_global_id(2); if (h >= num_v_heads || c >= num_chunks) return; const int H = num_v_heads; const int L = seq_len; const int C = CHUNK_SIZE; float cumsum = 0.0f; int out_base = ((b * H + h) * num_chunks + c) * C; for (int p = 0; p < C; ++p) { int l = c * C + p; float g_val = (l < L) ? (float)gate[b * L * H + l * H + h] : 0.0f; cumsum += g_val; g_cumsum[out_base + p] = cumsum; } } // Kernel C2: Build Neumann-corrected intra-chunk attention matrix // attn = I + neumann_correction(-(k_beta @ k^T) * decay_mask) // GWS: {CHUNK_SIZE, num_v_heads * num_chunks, batch}, LWS: {CHUNK_SIZE, 1, 1} __kernel void chunk_build_neumann_attn_step0( __global const FLOAT* conv_out, // [B, D, L] __global const FLOAT* beta_in, // [B, L, H] __global const float* g_cumsum, // [B, H, num_chunks, C] (float32) __global float* attn_matrix, // [B, H, num_chunks, C, C] (float32) __private const int batch, __private const int conv_dim, __private const int seq_len, __private const int num_v_heads, __private const int head_k_dim, __private const int key_dim, __private const int gqa_factor, __private const int num_chunks) { const int rc = get_global_id(0); const int hc = get_global_id(1); const int b = get_global_id(2); if (hc >= num_v_heads * num_chunks || b >= batch || rc >= CHUNK_SIZE * CHUNK_SIZE) return; const int col = rc % CHUNK_SIZE; const int row = rc / CHUNK_SIZE; const int h = hc / num_chunks; const int c = hc % num_chunks; const int C = CHUNK_SIZE; const int dk = head_k_dim; const int D = conv_dim; const int L = seq_len; const int H = num_v_heads; const int k_head = h / gqa_factor; const int g_base = ((b * H + h) * num_chunks + c) * C; const int attn_out_base = ((b * H + h) * num_chunks + c) * C * C; const int conv_base = b * D * L; // Phase 1: Compute strictly-lower-triangular attn // attn[i, j] = -(k[i]*beta[i]) dot k[j] * exp(g[i]-g[j]) float val = 0.0f; if (col < row) { const int l_i = c * C + row; const int l_j = c * C + col; if (l_i < L && l_j < L) { const float g_i = (float)g_cumsum[g_base + row]; const float beta_i = (float)beta_in[b * L * H + l_i * H + h]; const float g_j = (float)g_cumsum[g_base + col]; const float decay = exp(g_i - g_j); float dot = 0.0f; for (int d = 0; d < dk; ++d) { float k_i_d = (float)conv_out[conv_base + (key_dim + k_head * dk + d) * L + l_i]; float k_j_d = (float)conv_out[conv_base + (key_dim + k_head * dk + d) * L + l_j]; dot += k_i_d * k_j_d; } val = -(beta_i * dot) * decay; } } attn_matrix[attn_out_base + row * C + col] = val; } __kernel void chunk_build_neumann_attn_step1( __global float* attn_matrix, // [B, H, num_chunks, C, C] (float32) __private const int batch, __private const int conv_dim, __private const int seq_len, __private const int num_v_heads, __private const int head_k_dim, __private const int key_dim, __private const int gqa_factor, __private const int num_chunks) { const int col = get_global_id(0); const int hc = get_global_id(1); const int b = get_global_id(2); if (hc >= num_v_heads * num_chunks || b >= batch || col >= CHUNK_SIZE) return; const int lid = get_local_id(0); const int h = hc / num_chunks; const int c = hc % num_chunks; const int C = CHUNK_SIZE; const int dk = head_k_dim; const int D = conv_dim; const int L = seq_len; const int H = num_v_heads; const int k_head = h / gqa_factor; const int attn_out_base = ((b * H + h) * num_chunks + c) * C * C; // Phase 2: Neumann series correction (row by row) for (int r = 1; r < C; ++r) { float orig = attn_matrix[attn_out_base + r * C + lid]; float correction = 0.0f; if (lid < r) { for (int k = 0; k < r; ++k) { correction += attn_matrix[attn_out_base + r * C + k] * attn_matrix[attn_out_base + k * C + lid]; } } if (lid < r) { attn_matrix[attn_out_base + r * C + lid] = orig + correction; } barrier(CLK_LOCAL_MEM_FENCE); } // Phase 3: Add identity and write to global (float32) attn_matrix[attn_out_base + lid * C + lid] = attn_matrix[attn_out_base + lid * C + lid] + 1.0f; } // Kernel C3: v_corrected = attn_matrix @ (V * beta) // GWS: {UP_DIV(dv, 4), CHUNK_SIZE * num_chunks, B * H} __kernel void chunk_correct_v( __global const float* attn_matrix, // [B, H, num_chunks, C, C] (float32) __global const FLOAT* conv_out, // [B, D, L] __global const FLOAT* beta_in, // [B, L, H] __global const float* g_cumsum, // [B, H, num_chunks, C] (float32) __global float* v_corrected, // [B, H, num_chunks, C, dv] (float32) __global float* k_cumdecay, // [B, H, num_chunks, C, dk] (float32) __private const int global_dim0, __private const int global_dim1, __private const int global_dim2, __private const int conv_dim, __private const int seq_len, __private const int num_v_heads, __private const int head_k_dim, __private const int head_v_dim, __private const int key_dim, __private const int gqa_factor, __private const int num_chunks) { const int x = get_global_id(0); const int y = get_global_id(1); const int z = get_global_id(2); if (x >= global_dim0 || y >= global_dim1 || z >= global_dim2) return; const int j = x << 2; const int c = y / CHUNK_SIZE; const int row = y % CHUNK_SIZE; const int b = z / num_v_heads; const int h = z % num_v_heads; const int C = CHUNK_SIZE; const int dk = head_k_dim; const int dv = head_v_dim; const int D = conv_dim; const int L = seq_len; const int H = num_v_heads; const int k_head = h / gqa_factor; const int attn_base = ((b * H + h) * num_chunks + c) * C * C + row * C; const int g_base = ((b * H + h) * num_chunks + c) * C; const int conv_base = b * D * L; float4 result0 = (float4)(0.0f); float4 result1 = (float4)(0.0f); int l_p = c * C; for (int p = 0; p < C && l_p < L; ++p, ++l_p) { float a = attn_matrix[attn_base + p]; float beta_p = (float)beta_in[b * L * H + l_p * H + h]; float ab = a * beta_p; float g_p = g_cumsum[g_base + p]; float coeff = ab * exp(g_p); float4 v_p; v_p.x = (float)conv_out[conv_base + (2 * key_dim + h * dv + j) * L + l_p]; v_p.y = (float)conv_out[conv_base + (2 * key_dim + h * dv + j + 1) * L + l_p]; v_p.z = (float)conv_out[conv_base + (2 * key_dim + h * dv + j + 2) * L + l_p]; v_p.w = (float)conv_out[conv_base + (2 * key_dim + h * dv + j + 3) * L + l_p]; result0 += ab * v_p; float4 k_p; k_p.x = (float)conv_out[conv_base + (key_dim + k_head * dk + j) * L + l_p]; k_p.y = (float)conv_out[conv_base + (key_dim + k_head * dk + j + 1) * L + l_p]; k_p.z = (float)conv_out[conv_base + (key_dim + k_head * dk + j + 2) * L + l_p]; k_p.w = (float)conv_out[conv_base + (key_dim + k_head * dk + j + 3) * L + l_p]; result1 += coeff * k_p; } int out_idx = ((b * H + h) * num_chunks + c) * C * dv + row * dv + j; vstore4((float4)(result0), 0, v_corrected + out_idx); vstore4((float4)(result1), 0, k_cumdecay + out_idx); } // Kernel C5: Intra-chunk QK attention with decay (lower triangular including diagonal) // qk[i,j] = (q[i] dot k[j]) * q_scale * exp(g[i]-g[j]) for j <= i // GWS: {CHUNK_SIZE, CHUNK_SIZE * num_chunks, B * H} __kernel void chunk_qk_attn( __global const FLOAT* conv_out, // [B, D, L] __global const float* g_cumsum, // [B, H, num_chunks, C] (float32) __global float* qk_attn_out, // [B, H, num_chunks, C, C] (float32, reuses attn_matrix buf) __private const int global_dim0, __private const int global_dim1, __private const int global_dim2, __private const int conv_dim, __private const int seq_len, __private const int num_v_heads, __private const int head_k_dim, __private const int key_dim, __private const int gqa_factor, __private const int num_chunks, __private const float q_scale) { const int col = get_global_id(0); const int y = get_global_id(1); const int z = get_global_id(2); if (col >= global_dim0 || y >= global_dim1 || z >= global_dim2) return; const int c = y / CHUNK_SIZE; const int row = y % CHUNK_SIZE; const int b = z / num_v_heads; const int h = z % num_v_heads; const int C = CHUNK_SIZE; const int dk = head_k_dim; const int D = conv_dim; const int L = seq_len; const int H = num_v_heads; const int k_head = h / gqa_factor; float val = 0.0f; if (col <= row) { const int l_row = c * C + row; const int l_col = c * C + col; if (l_row < L && l_col < L) { const int g_base = ((b * H + h) * num_chunks + c) * C; const float decay = exp(g_cumsum[g_base + row] - g_cumsum[g_base + col]); const int conv_base = b * D * L; float dot = 0.0f; for (int d = 0; d < dk; ++d) { float q_d = (float)conv_out[conv_base + (k_head * dk + d) * L + l_row]; float k_d = (float)conv_out[conv_base + (key_dim + k_head * dk + d) * L + l_col]; dot += q_d * k_d; } val = dot * q_scale * decay; } } int out_idx = ((b * H + h) * num_chunks + c) * C * C + row * C + col; qk_attn_out[out_idx] = val; } // Kernel C6: v_new = v_corrected - k_cumdecay @ state (per chunk) // GWS: {UP_DIV(dv, 4), CHUNK_SIZE, B * H} __kernel void chunk_compute_vnew( __global const float* v_corrected, // [B, H, num_chunks, C, dv] (float32) __global const float* k_cumdecay, // [B, H, num_chunks, C, dk] (float32) __global const FLOAT* recurrent_state,// [B, H, dk, dv] __global float* v_new, // [B, H, C, dv] (float32) __private const int global_dim0, __private const int global_dim1, __private const int global_dim2, __private const int head_k_dim, __private const int head_v_dim, __private const int num_v_heads, __private const int num_chunks, __private const int chunk_idx) { const int x = get_global_id(0); const int p = get_global_id(1); const int z = get_global_id(2); if (x >= global_dim0 || p >= global_dim1 || z >= global_dim2) return; const int j = x << 2; const int b = z / num_v_heads; const int h = z % num_v_heads; const int C = CHUNK_SIZE; const int dk = head_k_dim; const int dv = head_v_dim; const int H = num_v_heads; const int c = chunk_idx; const int kc_base = ((b * H + h) * num_chunks + c) * C * dk + p * dk; const int state_base = (b * H + h) * dk * dv; float4 v_prime = (float4)(0.0f); for (int d = 0; d < dk; ++d) { float kc_d = k_cumdecay[kc_base + d]; float4 s = convert_float4(vload4(0, recurrent_state + state_base + d * dv + j)); v_prime += kc_d * s; } int vc_idx = ((b * H + h) * num_chunks + c) * C * dv + p * dv + j; float4 vc = vload4(0, v_corrected + vc_idx); float4 vn = vc - v_prime; int vn_idx = (b * H + h) * C * dv + p * dv + j; vstore4((float4)(vn), 0, v_new + vn_idx); } // Kernel C7: Compute output and update recurrent state (per chunk) // GWS: {dk * UP_DIV(dv, 4), H, batch} __kernel void chunk_output_state_update( __global const FLOAT* conv_out, // [B, D, L] __global const float* qk_attn_matrix, // [B, H, num_chunks, C, C] (float32) __global const float* v_new, // [B, H, C, dv] (float32) __global const float* g_cumsum, // [B, H, num_chunks, C] (float32) __global FLOAT* recurrent_state, // [B, H, dk, dv] __global FLOAT* attn_out, // [B, L, H*dv] __private const int global_dim0, __private const int global_dim1, __private const int global_dim2, __private const int conv_dim, __private const int seq_len, __private const int num_v_heads, __private const int head_k_dim, __private const int head_v_dim, __private const int key_dim, __private const int gqa_factor, __private const int num_chunks, __private const int chunk_idx, __private const float q_scale) { const int x = get_global_id(0); const int h = get_global_id(1); const int b = get_global_id(2); if (x >= global_dim0 || h >= global_dim1 || b >= global_dim2) return; const int dk = head_k_dim; const int dv = head_v_dim; const int dv4 = (dv + 3) / 4; const int j = (x % dv4) << 2; const int d = x / dv4; const int C = CHUNK_SIZE; const int D = conv_dim; const int L = seq_len; const int H = num_v_heads; const int c = chunk_idx; const int k_head = h / gqa_factor; const int state_base = (b * H + h) * dk * dv; const int g_base = ((b * H + h) * num_chunks + c) * C; const int qk_base = ((b * H + h) * num_chunks + c) * C * C; const int vn_base = (b * H + h) * C * dv; const int conv_base = b * D * L; int last_p = min(C, L - c * C); if (last_p <= 0) return; // Phase 2: Update recurrent state // state[d,j] = state[d,j]*exp(g_last) + sum_p k[p,d]*exp(g_last-g[p])*v_new[p,j] float g_last = g_cumsum[g_base + last_p - 1]; float decay_state = exp(g_last); float4 s = convert_float4(vload4(0, recurrent_state + state_base + d * dv + j)) * (float4)(decay_state); for (int p = 0; p < last_p; ++p) { int l_p = c * C + p; float g_p = g_cumsum[g_base + p]; float k_d = (float)conv_out[conv_base + (key_dim + k_head * dk + d) * L + l_p] * exp(g_last - g_p); float4 vn = vload4(0, v_new + vn_base + p * dv + j); s += k_d * vn; } vstore4(CONVERT_FLOAT4(s), 0, recurrent_state + state_base + d * dv + j); } __kernel void chunk_output( __global const FLOAT* conv_out, // [B, D, L] __global const float* qk_attn_matrix, // [B, H, num_chunks, C, C] (float32) __global const float* v_new, // [B, H, C, dv] (float32) __global const float* g_cumsum, // [B, H, num_chunks, C] (float32) __global const FLOAT* recurrent_state, // [B, H, dk, dv] __global FLOAT* attn_out, // [B, L, H*dv] __private const int global_dim0, __private const int global_dim1, __private const int global_dim2, __private const int conv_dim, __private const int seq_len, __private const int num_v_heads, __private const int dk, __private const int dv, __private const int key_dim, __private const int gqa_factor, __private const int num_chunks, __private const int chunk_idx, __private const float q_scale) { const int x = get_global_id(0); const int h = get_global_id(1); const int b = get_global_id(2); if (x >= global_dim0 || h >= global_dim1 || b >= global_dim2) return; const int dv4 = (dv + 3) / 4; const int j = (x % dv4) << 2; const int p = x / dv4; const int C = CHUNK_SIZE; const int D = conv_dim; const int L = seq_len; const int H = num_v_heads; const int c = chunk_idx; const int k_head = h / gqa_factor; const int state_base = (b * H + h) * dk * dv; const int g_base = ((b * H + h) * num_chunks + c) * C; const int qk_base = ((b * H + h) * num_chunks + c) * C * C; const int vn_base = (b * H + h) * C * dv; const int conv_base = b * D * L; int last_p = min(C, L - c * C); if (last_p <= 0 || p >= last_p) return; // Phase 1: Compute output for each position p int l_p = c * C + p; float g_p = g_cumsum[g_base + p]; float decay_q = exp(g_p) * q_scale; // Cross-chunk: q * scale * exp(g) @ state float4 inter = (float4)(0.0f); for (int d = 0; d < dk; d++) { float q_d = (float)conv_out[conv_base + (k_head * dk + d) * L + l_p] * decay_q; float4 s = convert_float4(vload4(0, recurrent_state + state_base + d * dv + j)); inter += q_d * s; } // Intra-chunk: qk_attn @ v_new float4 intra = (float4)(0.0f); for (int r = 0; r <= p; ++r) { float qk = qk_attn_matrix[qk_base + p * C + r]; float4 vn = vload4(0, v_new + vn_base + r * dv + j); intra += qk * vn; } const int out_offset = (b * L + l_p) * H * dv + h * dv + j; vstore4(CONVERT_FLOAT4(inter + intra), 0, attn_out + out_offset); } #endif // CHUNK_PREFILL