// Mamba2 fused SSM scan kernel. One workgroup per (head, dim, seq)// 64 threads. Each thread owns c_factor = d_state/64 state elements in
// private registers//
// References:
// ggml/src/ggml-cuda/ssm-scan.cu:117 ssm_scan_f32_group
// ggml/src/ggml-cpu/ops.cpp:9368 ggml_compute_forward_ssm_scan_f32
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#if defined(cl_qcom_reqd_sub_group_size)
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
#define REQD_SUBGROUP_SIZE_64 __attribute__((qcom_reqd_sub_group_size("half")))
#else
#define REQD_SUBGROUP_SIZE_64
#endif
inline float softplus_f32(float x) {
return (x <= 20.0f) ? log(1.0f + exp(x)) : x}
// d_state = 128 (most Mamba-2 models, e.g. mamba2-2.7B, Codestral-Mamba).
// WG = 64 threads, each holds 2 state elements (tid and tid+64).
REQD_SUBGROUP_SIZE_64
kernel void kernel_ssm_scan_f32_mamba2_d128(
global const char * src0_base, ulong src0_off,
global const char * src1_base, ulong src1_off,
global const char * src2_base, ulong src2_off,
global const char * src3_base, ulong src3_off,
global const char * src4_base, ulong src4_off,
global const char * src5_base, ulong src5_off,
global const char * src6_base, ulong src6_off,
global char * dst_base, ulong dst_off,
ulong s0_nb2, ulong s0_nb3,
ulong x_nb2, ulong x_nb3,
ulong dt_nb1, ulong dt_nb2,
ulong A_nb1,
ulong B_nb2, ulong B_nb3,
ulong C_nb2, ulong C_nb3,
ulong s_off_bytes,
int head_dim, int n_head, int n_group, int n_tokens
) {
const int d_state = 128
const int tid = (int) get_local_id(0) const int wg_x = (int) get_group_id(0) const int seq_id = (int) get_group_id(1)
const int head_id = wg_x / head_dim const int dim_id = wg_x - head_id * head_dim const int g = head_id / (n_head / n_group)
src0_base += src0_off src1_base += src1_off src2_base += src2_off src3_base += src3_off src4_base += src4_off src5_base += src5_off src6_base += src6_off dst_base += dst_off
const int seq_slot = ((global const int *) src6_base)[seq_id]
const ulong state_base_off = (ulong)seq_slot * s0_nb3 + (ulong)head_id * s0_nb2
+ (ulong)dim_id * d_state * sizeof(float) global const float * s0_warp = (global const float *)(src0_base + state_base_off) const ulong state_out_off = (ulong)seq_id * s0_nb3 + (ulong)head_id * s0_nb2
+ (ulong)dim_id * d_state * sizeof(float) global float * s_warp = (global float *)(dst_base + s_off_bytes + state_out_off)
global const char * x_seq = src1_base + (ulong)seq_id * x_nb3 global const char * dt_seq = src2_base + (ulong)seq_id * dt_nb2 global const char * B_seq = src4_base + (ulong)seq_id * B_nb3 + (ulong)g * d_state * sizeof(float) global const char * C_seq = src5_base + (ulong)seq_id * C_nb3 + (ulong)g * d_state * sizeof(float)
const ulong y_dim_total = (ulong)n_head * head_dim global float * y_seq = (global float *)dst_base
+ (ulong)seq_id * (ulong)n_tokens * y_dim_total
const float A_val = ((global const float *)src3_base)[(ulong)head_id * A_nb1 / sizeof(float)]
// c_factor = 2: each thread owns 2 state elements (tid and tid+64).
float state0 = s0_warp[tid] float state1 = s0_warp[tid + 64]
for (int t = 0 const float dt_h = ((global const float *)(dt_seq + (ulong)t * dt_nb1))[head_id] const float dt_softplus = softplus_f32(dt_h) const float dA = exp(dt_softplus * A_val) const float x_val = ((global const float *)(x_seq + (ulong)t * x_nb2))[(ulong)head_id * head_dim + dim_id] const float x_dt = x_val * dt_softplus
const float B0 = ((global const float *)(B_seq + (ulong)t * B_nb2))[tid] const float B1 = ((global const float *)(B_seq + (ulong)t * B_nb2))[tid + 64] const float C0 = ((global const float *)(C_seq + (ulong)t * C_nb2))[tid] const float C1 = ((global const float *)(C_seq + (ulong)t * C_nb2))[tid + 64]
state0 = state0 * dA + B0 * x_dt state1 = state1 * dA + B1 * x_dt const float partial = state0 * C0 + state1 * C1
const float sum = sub_group_reduce_add(partial) if (tid == 0) {
y_seq[(ulong)t * y_dim_total + (ulong)head_id * head_dim + dim_id] = sum }
}
s_warp[tid] = state0 s_warp[tid + 64] = state1}
// d_state = 256 (Falcon-H1). WG = 64 threads, each holds 4 state elements.
REQD_SUBGROUP_SIZE_64
kernel void kernel_ssm_scan_f32_mamba2_d256(
global const char * src0_base, ulong src0_off,
global const char * src1_base, ulong src1_off,
global const char * src2_base, ulong src2_off,
global const char * src3_base, ulong src3_off,
global const char * src4_base, ulong src4_off,
global const char * src5_base, ulong src5_off,
global const char * src6_base, ulong src6_off,
global char * dst_base, ulong dst_off,
ulong s0_nb2, ulong s0_nb3,
ulong x_nb2, ulong x_nb3,
ulong dt_nb1, ulong dt_nb2,
ulong A_nb1,
ulong B_nb2, ulong B_nb3,
ulong C_nb2, ulong C_nb3,
ulong s_off_bytes,
int head_dim, int n_head, int n_group, int n_tokens
) {
const int d_state = 256
const int tid = (int) get_local_id(0) const int wg_x = (int) get_group_id(0) const int seq_id = (int) get_group_id(1)
const int head_id = wg_x / head_dim const int dim_id = wg_x - head_id * head_dim const int g = head_id / (n_head / n_group)
src0_base += src0_off src1_base += src1_off src2_base += src2_off src3_base += src3_off src4_base += src4_off src5_base += src5_off src6_base += src6_off dst_base += dst_off
const int seq_slot = ((global const int *) src6_base)[seq_id]
const ulong state_base_off = (ulong)seq_slot * s0_nb3 + (ulong)head_id * s0_nb2
+ (ulong)dim_id * d_state * sizeof(float) global const float * s0_warp = (global const float *)(src0_base + state_base_off) const ulong state_out_off = (ulong)seq_id * s0_nb3 + (ulong)head_id * s0_nb2
+ (ulong)dim_id * d_state * sizeof(float) global float * s_warp = (global float *)(dst_base + s_off_bytes + state_out_off)
global const char * x_seq = src1_base + (ulong)seq_id * x_nb3 global const char * dt_seq = src2_base + (ulong)seq_id * dt_nb2 global const char * B_seq = src4_base + (ulong)seq_id * B_nb3 + (ulong)g * d_state * sizeof(float) global const char * C_seq = src5_base + (ulong)seq_id * C_nb3 + (ulong)g * d_state * sizeof(float)
const ulong y_dim_total = (ulong)n_head * head_dim global float * y_seq = (global float *)dst_base
+ (ulong)seq_id * (ulong)n_tokens * y_dim_total
const float A_val = ((global const float *)src3_base)[(ulong)head_id * A_nb1 / sizeof(float)]
// c_factor = 4: each thread owns 4 state elements.
float state0 = s0_warp[tid] float state1 = s0_warp[tid + 64] float state2 = s0_warp[tid + 128] float state3 = s0_warp[tid + 192]
for (int t = 0 const float dt_h = ((global const float *)(dt_seq + (ulong)t * dt_nb1))[head_id] const float dt_softplus = softplus_f32(dt_h) const float dA = exp(dt_softplus * A_val) const float x_val = ((global const float *)(x_seq + (ulong)t * x_nb2))[(ulong)head_id * head_dim + dim_id] const float x_dt = x_val * dt_softplus
global const float * B_t = (global const float *)(B_seq + (ulong)t * B_nb2) global const float * C_t = (global const float *)(C_seq + (ulong)t * C_nb2)
const float B0 = B_t[tid] const float B1 = B_t[tid + 64] const float B2 = B_t[tid + 128] const float B3 = B_t[tid + 192] const float C0 = C_t[tid] const float C1 = C_t[tid + 64] const float C2 = C_t[tid + 128] const float C3 = C_t[tid + 192]
state0 = state0 * dA + B0 * x_dt state1 = state1 * dA + B1 * x_dt state2 = state2 * dA + B2 * x_dt state3 = state3 * dA + B3 * x_dt const float partial = state0 * C0 + state1 * C1 + state2 * C2 + state3 * C3
const float sum = sub_group_reduce_add(partial) if (tid == 0) {
y_seq[(ulong)t * y_dim_total + (ulong)head_id * head_dim + dim_id] = sum }
}
s_warp[tid] = state0 s_warp[tid + 64] = state1 s_warp[tid + 128] = state2 s_warp[tid + 192] = state3}