Files
DS4Server/metal/dsv4_misc.metal

6158 lines
238 KiB
Metal

struct ds4_metal_args_dsv4_topk_mask {
int64_t ne00;
int64_t ne01;
uint64_t nb00;
uint64_t nb01;
int64_t ne0;
int64_t ne1;
uint64_t nb0;
uint64_t nb1;
};
struct ds4_metal_args_dsv4_indexer_weighted_sum {
int64_t ne00;
int64_t ne01;
int64_t ne02;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
int64_t ne10;
int64_t ne11;
uint64_t nb10;
uint64_t nb11;
int64_t ne0;
int64_t ne1;
uint64_t nb0;
uint64_t nb1;
float scale;
};
struct ds4_metal_args_dsv4_softmax_pool {
int64_t ne00;
int64_t ne01;
int64_t ne02;
uint64_t nb00;
uint64_t nb01;
uint64_t nb02;
uint64_t nb10;
uint64_t nb11;
uint64_t nb12;
int64_t ne0;
int64_t ne1;
uint64_t nb0;
uint64_t nb1;
};
struct ds4_metal_args_dsv4_softmax_pool_ratio4_direct {
int64_t n_rows;
uint32_t head_dim;
uint32_t n_comp;
uint32_t replay;
uint32_t pad;
};
struct ds4_metal_args_dsv4_compressor_score_ape {
uint32_t width;
uint32_t ratio;
uint32_t pos0;
uint32_t n_tokens;
};
struct ds4_metal_args_dsv4_indexed_attention {
uint32_t n_tokens;
uint32_t n_head;
uint32_t n_raw;
uint32_t raw_cap;
uint32_t raw_start;
uint32_t n_comp;
uint32_t top_k;
uint32_t pos0;
uint32_t window;
uint32_t ratio;
uint32_t comp_kv_f16;
uint32_t pad0;
uint64_t q_token_stride;
uint64_t q_head_stride;
uint64_t raw_row_stride;
uint64_t comp_row_stride;
uint64_t topk_token_stride;
uint64_t dst_token_stride;
uint64_t dst_head_stride;
float scale;
};
struct ds4_metal_args_dsv4_indexer_scores_fused {
uint32_t n_comp;
uint32_t n_tokens;
uint32_t n_head;
uint32_t head_dim;
uint32_t pos0;
uint32_t ratio;
uint64_t q_token_stride;
uint64_t q_head_stride;
uint64_t weights_token_stride;
uint64_t index_row_stride;
uint64_t score_token_stride;
float scale;
};
struct ds4_metal_args_dsv4_router_select_one {
uint32_t has_bias;
uint32_t hash_mode;
uint32_t use_token_buffer;
uint32_t token;
uint32_t hash_rows;
};
struct ds4_metal_args_glm_router_select_one {
uint32_t n_expert;
uint32_t n_expert_used;
float expert_weight_scale;
uint32_t pad0;
};
struct ds4_metal_args_glm_kv_lora_rms_norm {
uint32_t n_tokens;
uint32_t kv_raw_dim;
uint32_t kv_lora_dim;
float eps;
};
struct ds4_metal_args_glm_k_b_project {
uint32_t n_tokens;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t n_head;
uint32_t row_bytes;
uint32_t weight_type;
uint32_t pad1;
uint32_t pad2;
};
struct ds4_metal_args_glm_build_kv_cache {
uint32_t pos0;
uint32_t n_tokens;
uint32_t cache_cap;
uint32_t n_head;
uint32_t kv_raw_dim;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t qk_rope;
uint32_t value_dim;
uint32_t n_ctx_orig;
uint32_t cache_f16;
uint32_t pad0;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
};
struct ds4_metal_args_glm_store_compact_kv {
uint32_t pos0;
uint32_t n_tokens;
uint32_t cache_cap;
uint32_t kv_raw_dim;
uint32_t kv_lora_dim;
uint32_t qk_rope;
uint32_t cache_f16;
uint32_t pad1;
};
struct ds4_metal_args_glm_qkv_norm_store_compact_kv {
uint32_t pos0;
uint32_t n_tokens;
uint32_t cache_cap;
uint32_t q_n;
uint32_t q_n4;
uint32_t kv_raw_dim;
uint32_t kv_lora_dim;
uint32_t kv_lora_n4;
uint32_t qk_rope;
uint32_t cache_f16;
float eps;
uint32_t pad0;
};
struct ds4_metal_args_glm_store_indexer_k {
uint32_t pos0;
uint32_t n_tokens;
uint32_t cache_cap;
uint32_t head_dim;
uint32_t rot_dim;
uint32_t n_ctx_orig;
uint32_t cache_f16;
uint32_t pad0;
float eps;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
float pad1;
};
struct ds4_metal_args_glm_attention_full {
uint32_t pos0;
uint32_t n_tokens;
uint32_t cache_len;
uint32_t cache_cap;
uint32_t n_head;
uint32_t qk_dim;
uint32_t value_dim;
uint32_t pad0;
uint32_t cache_f16;
uint32_t pad1;
uint32_t pad2;
float scale;
};
struct ds4_metal_args_glm_fill_selected_range {
uint32_t n_selected;
};
struct ds4_metal_args_glm_fill_selected_range_batch {
uint32_t n_tokens;
uint32_t pos0;
uint32_t n_selected;
uint32_t pad_row;
};
struct ds4_metal_args_glm_indexer_rope_tail {
uint32_t n_tokens;
uint32_t n_head;
uint32_t head_dim;
uint32_t rot_dim;
uint32_t rot_offset;
uint32_t pos0;
uint32_t n_ctx_orig;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
};
struct ds4_metal_args_glm_indexer_score_one {
uint32_t n_rows;
uint32_t n_head;
uint32_t head_dim;
uint32_t cache_f16;
float scale;
};
struct ds4_metal_args_glm_indexer_scores_batch {
uint32_t n_rows;
uint32_t n_tokens;
uint32_t n_head;
uint32_t head_dim;
uint32_t pos0;
uint32_t cache_f16;
uint64_t q_token_stride;
uint64_t q_head_stride;
uint64_t weights_token_stride;
uint64_t score_token_stride;
float scale;
};
struct ds4_metal_args_glm_qk_lowrank {
uint32_t n_head;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t qk_dim;
uint32_t row_bytes;
uint32_t weight_type;
uint32_t pad1;
uint32_t pad2;
};
struct ds4_metal_args_glm_qk_lowrank_batch {
uint32_t n_tokens;
uint32_t n_head;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t qk_dim;
uint32_t row_bytes;
uint32_t weight_type;
/* First head this dispatch computes: under tensor-parallel head split
* each rank covers a contiguous half of the heads; buffers and weights
* keep full-model layout and are indexed by absolute head. */
uint32_t head_base;
};
struct ds4_metal_args_glm_attention_indexed_decode {
uint32_t n_selected;
uint32_t cache_cap;
uint32_t cache_f16;
uint32_t n_head;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t qk_rope;
uint32_t value_dim;
uint32_t n_ctx_orig;
uint32_t value_row_bytes;
float scale;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
uint32_t value_type;
};
struct ds4_metal_args_glm_attention_indexed_decode_split {
uint32_t n_selected;
uint32_t cache_cap;
uint32_t cache_f16;
uint32_t n_head;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t qk_rope;
uint32_t value_dim;
uint32_t n_ctx_orig;
uint32_t value_row_bytes;
uint32_t block_rows;
uint32_t n_blocks;
float scale;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
uint32_t value_type;
};
struct ds4_metal_args_glm_attention_indexed_batch {
uint32_t n_tokens;
uint32_t n_selected;
uint32_t cache_cap;
uint32_t cache_f16;
uint32_t n_head;
uint32_t kv_lora_dim;
uint32_t qk_nope;
uint32_t qk_rope;
uint32_t value_dim;
uint32_t n_ctx_orig;
uint32_t value_row_bytes;
uint32_t value_type;
uint32_t pos0;
float scale;
float freq_base;
float freq_scale;
float ext_factor;
float attn_factor;
float beta_fast;
float beta_slow;
uint32_t head_base;
};
struct ds4_metal_args_dsv4_directional_steering_project {
uint32_t width;
uint32_t rows;
uint32_t layer;
uint32_t n_threads;
float scale;
};
// Optional directional steering projection.
//
// Each threadgroup owns one 4096-wide token row, computes
// dot(row, direction[layer]), then subtracts scale * direction * dot in-place.
// Positive scales remove a concept direction; negative scales amplify it. The
// kernel is not used unless a steering file and nonzero scale are provided.
kernel void kernel_dsv4_directional_steering_project_f32(
constant ds4_metal_args_dsv4_directional_steering_project & args,
device float *x,
device const float *directions,
threadgroup float *scratch [[threadgroup(0)]],
uint row [[threadgroup_position_in_grid]],
uint tid [[thread_position_in_threadgroup]]) {
if (row >= args.rows || args.width == 0) return;
device float *xr = x + (uint64_t)row * args.width;
device const float *dir = directions + (uint64_t)args.layer * args.width;
const uint nth = args.n_threads;
float sum = 0.0f;
for (uint i = tid; i < args.width; i += nth) {
sum += xr[i] * dir[i];
}
scratch[tid] = sum;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) scratch[tid] += scratch[tid + step];
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float coeff = args.scale * scratch[0];
for (uint i = tid; i < args.width; i += nth) {
xr[i] -= coeff * dir[i];
}
}
// Decode-only DS4 ratio-4 indexer score builder. One threadgroup owns one
// compressed row for the current token, stages that 128-wide row once, then
// walks the 64 indexer heads in four-head groups. This avoids materializing the
// intermediate [compressed rows x heads] score matrix used by the generic
// matvec + weighted-sum path.
kernel void kernel_dsv4_indexer_score_one_direct(
constant ds4_metal_args_dsv4_indexer_scores_fused & args,
device const char *q,
device const char *weights,
device const char *index_comp,
device char *scores,
threadgroup float *shared [[threadgroup(0)]],
uint row [[threadgroup_position_in_grid]],
ushort tid [[thread_index_in_threadgroup]],
ushort lane [[thread_index_in_simdgroup]],
ushort sg [[simdgroup_index_in_threadgroup]]) {
if (row >= args.n_comp || args.n_head != 64u || args.head_dim != 128u) {
return;
}
threadgroup float *ktg = shared; // [128]
threadgroup float *psum = ktg + 128u; // [4]
if (tid < 128u) {
device const float *krow = (device const float *)(index_comp +
(uint64_t)row * args.index_row_stride);
ktg[tid] = krow[tid];
}
float acc = 0.0f;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint head0 = 0; head0 < 64u; head0 += 4u) {
const uint head = head0 + (uint)sg;
device const float4 *q4 = (device const float4 *)(q +
(uint64_t)head * args.q_head_stride);
threadgroup const float4 *k4 = (threadgroup const float4 *)ktg;
float s = dot(q4[lane], k4[lane]);
s = simd_sum(s);
if (lane == 0) {
device const float *w = (device const float *)weights;
psum[sg] = max(s, 0.0f) * (w[head] * args.scale);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
if (tid == 0) {
acc += psum[0];
acc += psum[1];
acc += psum[2];
acc += psum[3];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (tid == 0) {
device float *dst = (device float *)scores;
dst[row] = acc;
}
}
// Decode router post-processing for one token. The selected expert ids are
// already known; this gathers their probabilities, normalizes by the selected
// sum, clamps the denominator like the reference path, and applies DS4's 1.5
// expert-weight scale in one tiny dispatch.
kernel void kernel_dsv4_router_weights_one(
device const char *probs,
device const char *selected,
device char *weights,
uint tid [[thread_position_in_grid]]) {
if (tid >= 6) return;
device const float *p = (device const float *)probs;
device const int *s = (device const int *)selected;
float sum = 0.0f;
for (uint i = 0; i < 6; i++) {
sum += p[s[i]];
}
sum = max(sum, 6.103515625e-5f);
device float *w = (device float *)weights;
w[tid] = p[s[tid]] / sum * 1.5f;
}
static inline float ds4_glm_router_sigmoid(float x) {
if (x >= 0.0f) {
const float e = exp(-x);
return 1.0f / (1.0f + e);
} else {
const float e = exp(x);
return e / (1.0f + e);
}
}
static inline bool ds4_glm_router_better(
threadgroup const float *scores,
int32_t a,
int32_t b) {
const float sa = scores[(uint)a];
const float sb = scores[(uint)b];
return sa > sb || (sa == sb && a < b);
}
static float glm_rope_yarn_ramp(const float low, const float high, const int i0) {
const float y = (i0 / 2 - low) / max(0.001f, high - low);
return 1.0f - min(1.0f, max(0.0f, y));
}
static void glm_rope_yarn(
float theta_extrap,
float freq_scale,
float corr_dims[2],
int i0,
float ext_factor,
float mscale,
thread float *cos_theta,
thread float *sin_theta) {
float theta_interp = freq_scale * theta_extrap;
float theta = theta_interp;
if (ext_factor != 0.0f) {
float ramp_mix = glm_rope_yarn_ramp(corr_dims[0], corr_dims[1], i0) * ext_factor;
theta = theta_interp * (1 - ramp_mix) + theta_extrap * ramp_mix;
mscale *= 1.0f + 0.1f * log(1.0f / freq_scale);
}
*cos_theta = cos(theta) * mscale;
*sin_theta = sin(theta) * mscale;
}
static float glm_rope_yarn_corr_factor(int n_dims, int n_ctx_orig, float n_rot, float base) {
return n_dims * log(n_ctx_orig / (n_rot * 2 * M_PI_F)) / (2 * log(base));
}
static void glm_rope_yarn_corr_dims(
int n_dims,
int n_ctx_orig,
float freq_base,
float beta_fast,
float beta_slow,
float dims[2]) {
dims[0] = max(0.0f,
floor(glm_rope_yarn_corr_factor(n_dims, n_ctx_orig, beta_fast, freq_base)));
dims[1] = min(n_dims - 1.0f,
ceil(glm_rope_yarn_corr_factor(n_dims, n_ctx_orig, beta_slow, freq_base)));
}
kernel void kernel_glm_kv_lora_rms_norm(
constant ds4_metal_args_glm_kv_lora_rms_norm & args,
device const char *src,
device const char *weight,
device char *dst,
threadgroup float *scratch [[threadgroup(0)]],
uint3 tgpig [[threadgroup_position_in_grid]],
ushort tid_u [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]]) {
const uint row = tgpig.x;
if (row >= args.n_tokens) return;
const uint tid = tid_u;
const uint nth = ntg_u.x;
device const float *x = (device const float *)(src + (uint64_t)row * args.kv_raw_dim * sizeof(float));
device const float *w = (device const float *)weight;
device float *out = (device float *)(dst + (uint64_t)row * args.kv_lora_dim * sizeof(float));
float ss = 0.0f;
for (uint i = tid; i < args.kv_lora_dim; i += nth) {
const float v = x[i];
ss += v * v;
}
scratch[tid] = ss;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) scratch[tid] += scratch[tid + step];
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float inv = rsqrt(scratch[0] / (float)args.kv_lora_dim + args.eps);
for (uint i = tid; i < args.kv_lora_dim; i += nth) {
out[i] = x[i] * inv * w[i];
}
}
static inline float glm_quant_weight_at(
uint weight_type,
device const char *row,
uint col);
kernel void kernel_glm_k_b_project_q8_0(
constant ds4_metal_args_glm_k_b_project & args,
device const char *weight,
device const char *kv_norm,
device char *dst,
threadgroup float *kv_scratch [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort tiisg [[thread_index_in_simdgroup]],
ushort sgitg [[simdgroup_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint token = tgpig.x;
const uint head = tgpig.y;
if (token >= args.n_tokens || head >= args.n_head) return;
const uint nth = (uint)ntg_u.x * (uint)ntg_u.y;
device const float *kv =
(device const float *)(kv_norm + (uint64_t)token * args.kv_lora_dim * sizeof(float));
device float *out =
(device float *)(dst +
((uint64_t)token * args.n_head + head) * args.qk_nope * sizeof(float));
for (uint j = tid; j < args.kv_lora_dim; j += nth) {
kv_scratch[j] = kv[j];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
const uint block = (uint)sgitg;
const uint q = (block << 5) + (uint)tiisg;
if (q < args.qk_nope) {
float acc = 0.0f;
for (uint j = 0; j < args.kv_lora_dim; j++) {
device const char *row =
weight + ((uint64_t)head * args.kv_lora_dim + j) * args.row_bytes;
acc += glm_quant_weight_at(args.weight_type, row, q) * kv_scratch[j];
}
out[q] = acc;
}
}
kernel void kernel_glm_store_compact_kv(
constant ds4_metal_args_glm_store_compact_kv & args,
device const char *kv_norm,
device const char *kv_raw,
device char *kv_lora_cache,
device char *k_rope_cache,
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint token = tgpig.x;
const uint part = tgpig.y;
if (token >= args.n_tokens || part > 1u) return;
const uint pos = args.pos0 + token;
if (pos >= args.cache_cap) return;
const uint nth = ntg_u.x;
if (part == 0) {
device const float *src =
(device const float *)(kv_norm +
(uint64_t)token * args.kv_lora_dim * sizeof(float));
if (args.cache_f16 != 0u) {
device half *dst =
(device half *)(kv_lora_cache +
(uint64_t)pos * args.kv_lora_dim * sizeof(half));
for (uint i = tid; i < args.kv_lora_dim; i += nth) {
dst[i] = (half)src[i];
}
} else {
device float *dst =
(device float *)(kv_lora_cache +
(uint64_t)pos * args.kv_lora_dim * sizeof(float));
for (uint i = tid; i < args.kv_lora_dim; i += nth) {
dst[i] = src[i];
}
}
} else {
device const float *src =
(device const float *)(kv_raw +
((uint64_t)token * args.kv_raw_dim + args.kv_lora_dim) * sizeof(float));
if (args.cache_f16 != 0u) {
device half *dst =
(device half *)(k_rope_cache +
(uint64_t)pos * args.qk_rope * sizeof(half));
for (uint i = tid; i < args.qk_rope; i += nth) {
dst[i] = (half)src[i];
}
} else {
device float *dst =
(device float *)(k_rope_cache +
(uint64_t)pos * args.qk_rope * sizeof(float));
for (uint i = tid; i < args.qk_rope; i += nth) {
dst[i] = src[i];
}
}
}
}
kernel void kernel_glm_qkv_norm_store_compact_kv(
constant ds4_metal_args_glm_qkv_norm_store_compact_kv & args,
device const char *q_src,
device const char *q_weight,
device char *q_dst,
device const char *kv_raw,
device const char *kv_weight,
device char *kv_lora_cache,
device char *k_rope_cache,
threadgroup float *shmem_f32 [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort sgitg [[simdgroup_index_in_threadgroup]],
ushort tiisg [[thread_index_in_simdgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint token = tgpig.x;
const uint part = tgpig.y;
if (token >= args.n_tokens || part > 2u) return;
const uint nth = ntg_u.x;
if (part == 2u) {
const uint pos = args.pos0 + token;
if (pos >= args.cache_cap) return;
device const float *src =
(device const float *)(kv_raw +
((uint64_t)token * args.kv_raw_dim + args.kv_lora_dim) * sizeof(float));
if (args.cache_f16 != 0u) {
device half *dst =
(device half *)(k_rope_cache +
(uint64_t)pos * args.qk_rope * sizeof(half));
for (uint i = tid; i < args.qk_rope; i += nth) {
dst[i] = (half)src[i];
}
} else {
device float *dst =
(device float *)(k_rope_cache +
(uint64_t)pos * args.qk_rope * sizeof(float));
for (uint i = tid; i < args.qk_rope; i += nth) {
dst[i] = src[i];
}
}
return;
}
if (sgitg == 0) {
shmem_f32[tiisg] = 0.0f;
}
const bool kv_task = part != 0u;
const uint n = kv_task ? args.kv_lora_dim : args.q_n;
const uint n4 = kv_task ? args.kv_lora_n4 : args.q_n4;
device const float4 *x =
kv_task
? (device const float4 *)(kv_raw +
(uint64_t)token * args.kv_raw_dim * sizeof(float))
: (device const float4 *)(q_src +
(uint64_t)token * args.q_n * sizeof(float));
device const float4 *w =
kv_task ? (device const float4 *)kv_weight
: (device const float4 *)q_weight;
float sumf = 0.0f;
for (uint i = tid; i < n4; i += nth) {
const float4 v = x[i];
sumf += dot(v, v);
}
sumf = simd_sum(sumf);
threadgroup_barrier(mem_flags::mem_threadgroup);
if (tiisg == 0) {
shmem_f32[sgitg] = sumf;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
sumf = shmem_f32[tiisg];
sumf = simd_sum(sumf);
#ifdef DS4_METAL_NORM_RSQRT_DISABLE
const float scale = 1.0f / sqrt(sumf / float(n) + args.eps);
#else
const float scale = rsqrt(sumf / float(n) + args.eps);
#endif
if (!kv_task) {
device float4 *y =
(device float4 *)(q_dst +
(uint64_t)token * args.q_n * sizeof(float));
for (uint i = tid; i < n4; i += nth) {
y[i] = (x[i] * scale) * w[i];
}
return;
}
const uint pos = args.pos0 + token;
if (pos >= args.cache_cap) return;
device const float *x1 =
(device const float *)(kv_raw +
(uint64_t)token * args.kv_raw_dim * sizeof(float));
device const float *w1 = (device const float *)kv_weight;
if (args.cache_f16 != 0u) {
device half *dst =
(device half *)(kv_lora_cache +
(uint64_t)pos * args.kv_lora_dim * sizeof(half));
for (uint i = tid; i < args.kv_lora_dim; i += nth) {
dst[i] = (half)((x1[i] * scale) * w1[i]);
}
} else {
device float *dst =
(device float *)(kv_lora_cache +
(uint64_t)pos * args.kv_lora_dim * sizeof(float));
for (uint i = tid; i < args.kv_lora_dim; i += nth) {
dst[i] = (x1[i] * scale) * w1[i];
}
}
}
kernel void kernel_glm_store_indexer_k(
constant ds4_metal_args_glm_store_indexer_k & args,
device const char *raw_k,
device const char *weight,
device const char *bias,
device char *indexer_key_cache,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint token = tgpig.x;
if (token >= args.n_tokens) return;
const uint pos = args.pos0 + token;
if (pos >= args.cache_cap) return;
const uint nth = ntg_u.x;
const uint head_dim = args.head_dim;
const uint rot_dim = args.rot_dim;
device const float *src =
(device const float *)(raw_k + (uint64_t)token * head_dim * sizeof(float));
device const float *w = (device const float *)weight;
device const float *b = (device const float *)bias;
float sum = 0.0f;
for (uint i = tid; i < head_dim; i += nth) {
sum += src[i];
}
scratch[tid] = sum;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) scratch[tid] += scratch[tid + step];
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float mean = scratch[0] / (float)head_dim;
float ss = 0.0f;
for (uint i = tid; i < head_dim; i += nth) {
const float d = src[i] - mean;
ss += d * d;
}
scratch[tid] = ss;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) scratch[tid] += scratch[tid + step];
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float inv = rsqrt(scratch[0] / (float)head_dim + args.eps);
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)rot_dim,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
const float theta_base = (float)pos;
const float inv_ndims = -1.0f / (float)rot_dim;
if (args.cache_f16 != 0u) {
device half *dst =
(device half *)(indexer_key_cache +
(uint64_t)pos * head_dim * sizeof(half));
for (uint i = tid; i < head_dim; i += nth) {
if (i < rot_dim) {
if ((i & 1u) != 0u) continue;
const uint rel_i0 = i;
#ifdef DS4_METAL_ROPE_EXP2_LOG2
const float theta = theta_base * exp2(inv_ndims * (float)rel_i0 * log2(args.freq_base));
#else
const float theta = theta_base * pow(args.freq_base, inv_ndims * (float)rel_i0);
#endif
float cos_theta;
float sin_theta;
glm_rope_yarn(theta,
args.freq_scale,
corr_dims,
(int)rel_i0,
args.ext_factor,
args.attn_factor,
&cos_theta,
&sin_theta);
const float x0 = (src[i] - mean) * inv * w[i] + b[i];
const uint j = i + 1u;
const float x1 = (src[j] - mean) * inv * w[j] + b[j];
dst[i] = (half)(x0 * cos_theta - x1 * sin_theta);
dst[j] = (half)(x0 * sin_theta + x1 * cos_theta);
} else if (i >= rot_dim) {
const float x = (src[i] - mean) * inv * w[i] + b[i];
dst[i] = (half)x;
}
}
} else {
device float *dst =
(device float *)(indexer_key_cache +
(uint64_t)pos * head_dim * sizeof(float));
for (uint i = tid; i < head_dim; i += nth) {
if (i < rot_dim) {
if ((i & 1u) != 0u) continue;
const uint rel_i0 = i;
#ifdef DS4_METAL_ROPE_EXP2_LOG2
const float theta = theta_base * exp2(inv_ndims * (float)rel_i0 * log2(args.freq_base));
#else
const float theta = theta_base * pow(args.freq_base, inv_ndims * (float)rel_i0);
#endif
float cos_theta;
float sin_theta;
glm_rope_yarn(theta,
args.freq_scale,
corr_dims,
(int)rel_i0,
args.ext_factor,
args.attn_factor,
&cos_theta,
&sin_theta);
const float x0 = (src[i] - mean) * inv * w[i] + b[i];
const uint j = i + 1u;
const float x1 = (src[j] - mean) * inv * w[j] + b[j];
dst[i] = x0 * cos_theta - x1 * sin_theta;
dst[j] = x0 * sin_theta + x1 * cos_theta;
} else if (i >= rot_dim) {
const float x = (src[i] - mean) * inv * w[i] + b[i];
dst[i] = x;
}
}
}
}
static inline void glm_dense_cache_store_f32_or_f16(
device char *base,
uint64_t index,
uint cache_f16,
float x) {
if (cache_f16 != 0u) {
((device half *)base)[index] = (half)x;
} else {
((device float *)base)[index] = x;
}
}
static inline float glm_dense_cache_load_f32_or_f16(
device const char *base,
uint64_t index,
uint cache_f16) {
if (cache_f16 != 0u) {
return (float)((device const half *)base)[index];
}
return ((device const float *)base)[index];
}
static inline float4 glm_dense_cache_load4_f32_or_f16(
device const char *base,
uint64_t index,
uint cache_f16) {
if (cache_f16 != 0u) {
device const half *h = (device const half *)base;
return float4((float)h[index + 0u],
(float)h[index + 1u],
(float)h[index + 2u],
(float)h[index + 3u]);
}
return ((device const float4 *)base)[index >> 2u];
}
kernel void kernel_glm_build_kv_cache(
constant ds4_metal_args_glm_build_kv_cache & args,
device const char *kv_raw,
device const char *k_nope,
device const char *value,
device char *key_cache,
device char *value_cache,
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint token = tgpig.x;
const uint head = tgpig.y;
if (token >= args.n_tokens || head >= args.n_head) return;
const uint nth = ntg_u.x;
const uint qk_dim = args.qk_nope + args.qk_rope;
const uint pos = args.pos0 + token;
device const float *raw =
(device const float *)(kv_raw + (uint64_t)token * args.kv_raw_dim * sizeof(float));
device const float *kn =
(device const float *)(k_nope +
((uint64_t)token * args.n_head + head) * args.qk_nope * sizeof(float));
device const float *val =
(device const float *)(value +
((uint64_t)token * args.n_head + head) * args.value_dim * sizeof(float));
const uint64_t kbase = ((uint64_t)pos * args.n_head + head) * qk_dim;
const uint64_t vbase = ((uint64_t)pos * args.n_head + head) * args.value_dim;
for (uint i = tid; i < args.qk_nope; i += nth) {
glm_dense_cache_store_f32_or_f16(key_cache, kbase + i, args.cache_f16, kn[i]);
}
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)args.qk_rope,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
const float theta_base = (float)pos;
const float inv_ndims = -1.0f / (float)args.qk_rope;
for (uint r = tid * 2u; r < args.qk_rope; r += nth * 2u) {
#ifdef DS4_METAL_ROPE_EXP2_LOG2
const float theta = theta_base * exp2(inv_ndims * (float)r * log2(args.freq_base));
#else
const float theta = theta_base * pow(args.freq_base, inv_ndims * (float)r);
#endif
float cos_theta;
float sin_theta;
glm_rope_yarn(theta,
args.freq_scale,
corr_dims,
(int)r,
args.ext_factor,
args.attn_factor,
&cos_theta,
&sin_theta);
const uint src0 = args.kv_lora_dim + r;
const float x0 = raw[src0];
const float x1 = raw[src0 + 1u];
const uint dst0 = args.qk_nope + r;
glm_dense_cache_store_f32_or_f16(key_cache,
kbase + dst0,
args.cache_f16,
x0 * cos_theta - x1 * sin_theta);
glm_dense_cache_store_f32_or_f16(key_cache,
kbase + dst0 + 1u,
args.cache_f16,
x0 * sin_theta + x1 * cos_theta);
}
for (uint i = tid; i < args.value_dim; i += nth) {
glm_dense_cache_store_f32_or_f16(value_cache, vbase + i, args.cache_f16, val[i]);
}
}
kernel void kernel_glm_build_kv_cache_decode_group4(
constant ds4_metal_args_glm_build_kv_cache & args,
device const char *kv_raw,
device const char *k_nope,
device const char *value,
device char *key_cache,
device char *value_cache,
uint tid [[thread_index_in_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint token = tgpig.x;
const uint group_head0 = tgpig.y * 4u;
if (token >= args.n_tokens || group_head0 >= args.n_head) return;
const uint qk_dim = args.qk_nope + args.qk_rope;
const uint pos = args.pos0 + token;
const uint lane = tid & 63u;
const uint slot = tid >> 6;
device const float *raw =
(device const float *)(kv_raw + (uint64_t)token * args.kv_raw_dim * sizeof(float));
const uint head = group_head0 + slot;
if (slot < 4u && head < args.n_head) {
device const float *kn =
(device const float *)(k_nope +
((uint64_t)token * args.n_head + head) * args.qk_nope * sizeof(float));
device const float *val =
(device const float *)(value +
((uint64_t)token * args.n_head + head) * args.value_dim * sizeof(float));
const uint64_t kbase = ((uint64_t)pos * args.n_head + head) * qk_dim;
const uint64_t vbase = ((uint64_t)pos * args.n_head + head) * args.value_dim;
for (uint i = lane; i < args.qk_nope; i += 64u) {
glm_dense_cache_store_f32_or_f16(key_cache, kbase + i, args.cache_f16, kn[i]);
}
for (uint i = lane; i < args.value_dim; i += 64u) {
glm_dense_cache_store_f32_or_f16(value_cache, vbase + i, args.cache_f16, val[i]);
}
}
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)args.qk_rope,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
const float theta_base = (float)pos;
const float inv_ndims = -1.0f / (float)args.qk_rope;
for (uint r = tid * 2u; r < args.qk_rope; r += 512u) {
#ifdef DS4_METAL_ROPE_EXP2_LOG2
const float theta = theta_base * exp2(inv_ndims * (float)r * log2(args.freq_base));
#else
const float theta = theta_base * pow(args.freq_base, inv_ndims * (float)r);
#endif
float cos_theta;
float sin_theta;
glm_rope_yarn(theta,
args.freq_scale,
corr_dims,
(int)r,
args.ext_factor,
args.attn_factor,
&cos_theta,
&sin_theta);
const uint src0 = args.kv_lora_dim + r;
const float x0 = raw[src0];
const float x1 = raw[src0 + 1u];
const uint dst0 = args.qk_nope + r;
const float y0 = x0 * cos_theta - x1 * sin_theta;
const float y1 = x0 * sin_theta + x1 * cos_theta;
for (uint h = group_head0; h < min(group_head0 + 4u, args.n_head); h++) {
const uint64_t kbase = ((uint64_t)pos * args.n_head + h) * qk_dim;
glm_dense_cache_store_f32_or_f16(key_cache, kbase + dst0, args.cache_f16, y0);
glm_dense_cache_store_f32_or_f16(key_cache, kbase + dst0 + 1u, args.cache_f16, y1);
}
}
}
kernel void kernel_glm_build_kv_cache_flash(
constant ds4_metal_args_glm_build_kv_cache & args,
device const char *kv_raw,
device const char *k_nope,
device const char *value,
device char *key_cache,
device char *value_cache,
device char *key_f16,
device char *value_f16,
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint token = tgpig.x;
const uint head = tgpig.y;
if (token >= args.n_tokens || head >= args.n_head) return;
const uint nth = ntg_u.x;
const uint qk_dim = args.qk_nope + args.qk_rope;
const uint pos = args.pos0 + token;
device const float *raw =
(device const float *)(kv_raw + (uint64_t)token * args.kv_raw_dim * sizeof(float));
device const float *kn =
(device const float *)(k_nope +
((uint64_t)token * args.n_head + head) * args.qk_nope * sizeof(float));
device const float *val =
(device const float *)(value +
((uint64_t)token * args.n_head + head) * args.value_dim * sizeof(float));
const uint64_t kbase = ((uint64_t)pos * args.n_head + head) * qk_dim;
const uint64_t vbase = ((uint64_t)pos * args.n_head + head) * args.value_dim;
device half *kdst_f16 =
(device half *)(key_f16 +
((uint64_t)head * args.n_tokens + token) * qk_dim * sizeof(half));
device half *vdst_f16 =
(device half *)(value_f16 +
((uint64_t)head * args.n_tokens + token) * args.value_dim * sizeof(half));
for (uint i = tid; i < args.qk_nope; i += nth) {
const float x = kn[i];
glm_dense_cache_store_f32_or_f16(key_cache, kbase + i, args.cache_f16, x);
kdst_f16[i] = (half)x;
}
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)args.qk_rope,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
const float theta_base = (float)pos;
const float inv_ndims = -1.0f / (float)args.qk_rope;
for (uint r = tid * 2u; r < args.qk_rope; r += nth * 2u) {
#ifdef DS4_METAL_ROPE_EXP2_LOG2
const float theta = theta_base * exp2(inv_ndims * (float)r * log2(args.freq_base));
#else
const float theta = theta_base * pow(args.freq_base, inv_ndims * (float)r);
#endif
float cos_theta;
float sin_theta;
glm_rope_yarn(theta,
args.freq_scale,
corr_dims,
(int)r,
args.ext_factor,
args.attn_factor,
&cos_theta,
&sin_theta);
const uint src0 = args.kv_lora_dim + r;
const float x0 = raw[src0];
const float x1 = raw[src0 + 1u];
const uint dst0 = args.qk_nope + r;
const float y0 = x0 * cos_theta - x1 * sin_theta;
const float y1 = x0 * sin_theta + x1 * cos_theta;
glm_dense_cache_store_f32_or_f16(key_cache, kbase + dst0, args.cache_f16, y0);
glm_dense_cache_store_f32_or_f16(key_cache, kbase + dst0 + 1u, args.cache_f16, y1);
kdst_f16[dst0] = (half)y0;
kdst_f16[dst0 + 1u] = (half)y1;
}
for (uint i = tid; i < args.value_dim; i += nth) {
const float x = val[i];
glm_dense_cache_store_f32_or_f16(value_cache, vbase + i, args.cache_f16, x);
vdst_f16[i] = (half)x;
}
}
kernel void kernel_glm_attention_full(
constant ds4_metal_args_glm_attention_full & args,
device const char *q,
device const char *key_cache,
device const char *value_cache,
device char *heads,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint token = tgpig.x;
const uint head = tgpig.y;
if (token >= args.n_tokens || head >= args.n_head) return;
const uint nth = ntg_u.x;
const uint qk4 = args.qk_dim / 4u;
const uint visible = min(args.cache_len, args.pos0 + token + 1u);
threadgroup float *red = scratch;
threadgroup float *scores = scratch + 256u;
device const float4 *q4 = (device const float4 *)(q +
((uint64_t)token * args.n_head + head) * args.qk_dim * sizeof(float));
if (args.pad0 == 2u) {
for (uint s = tid; s < visible; s += nth) {
const uint64_t kbase = ((uint64_t)s * args.n_head + head) * args.qk_dim;
float dotv = 0.0f;
for (uint i = 0; i < qk4; i++) {
dotv += dot(q4[i],
glm_dense_cache_load4_f32_or_f16(key_cache,
kbase + 4u * (uint64_t)i,
args.cache_f16));
}
scores[s] = dotv * args.scale;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
if (tid == 0u) {
float max_score = -INFINITY;
for (uint s = 0; s < visible; s++) {
max_score = max(max_score, scores[s]);
}
float sum = 0.0f;
for (uint s = 0; s < visible; s++) {
const float w = exp(scores[s] - max_score);
scores[s] = w;
sum += w;
}
red[0] = max(sum, 1.0e-20f);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
const float denom = red[0];
device float *out = (device float *)(heads +
((uint64_t)token * args.n_head + head) * args.value_dim * sizeof(float));
for (uint d = tid; d < args.value_dim; d += nth) {
float acc = 0.0f;
for (uint s = 0; s < visible; s++) {
const uint64_t vbase = ((uint64_t)s * args.n_head + head) * args.value_dim;
acc += scores[s] *
glm_dense_cache_load_f32_or_f16(value_cache,
vbase + d,
args.cache_f16);
}
out[d] = acc / denom;
}
return;
}
if (args.pad0 == 1u) {
if (tid == 0u) {
float max_score = -INFINITY;
for (uint s = 0; s < visible; s++) {
const uint64_t kbase = ((uint64_t)s * args.n_head + head) * args.qk_dim;
float dotv = 0.0f;
for (uint i = 0; i < qk4; i++) {
dotv += dot(q4[i],
glm_dense_cache_load4_f32_or_f16(key_cache,
kbase + 4u * (uint64_t)i,
args.cache_f16));
}
const float score = dotv * args.scale;
scores[s] = score;
max_score = max(max_score, score);
}
float sum = 0.0f;
for (uint s = 0; s < visible; s++) {
const float w = exp(scores[s] - max_score);
scores[s] = w;
sum += w;
}
red[0] = max(sum, 1.0e-20f);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
const float denom = red[0];
device float *out = (device float *)(heads +
((uint64_t)token * args.n_head + head) * args.value_dim * sizeof(float));
for (uint d = tid; d < args.value_dim; d += nth) {
float acc = 0.0f;
for (uint s = 0; s < visible; s++) {
const uint64_t vbase = ((uint64_t)s * args.n_head + head) * args.value_dim;
acc += scores[s] *
glm_dense_cache_load_f32_or_f16(value_cache,
vbase + d,
args.cache_f16);
}
out[d] = acc / denom;
}
return;
}
float local_max = -INFINITY;
for (uint s = tid; s < visible; s += nth) {
const uint64_t kbase = ((uint64_t)s * args.n_head + head) * args.qk_dim;
float dotv = 0.0f;
for (uint i = 0; i < qk4; i++) {
dotv += dot(q4[i],
glm_dense_cache_load4_f32_or_f16(key_cache,
kbase + 4u * (uint64_t)i,
args.cache_f16));
}
const float score = dotv * args.scale;
scores[s] = score;
local_max = max(local_max, score);
}
red[tid] = local_max;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) red[tid] = max(red[tid], red[tid + step]);
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float max_score = red[0];
float local_sum = 0.0f;
for (uint s = tid; s < visible; s += nth) {
const float w = exp(scores[s] - max_score);
scores[s] = w;
local_sum += w;
}
red[tid] = local_sum;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) red[tid] += red[tid + step];
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float denom = max(red[0], 1.0e-20f);
threadgroup_barrier(mem_flags::mem_threadgroup);
device float *out = (device float *)(heads +
((uint64_t)token * args.n_head + head) * args.value_dim * sizeof(float));
for (uint d = tid; d < args.value_dim; d += nth) {
float acc = 0.0f;
for (uint s = 0; s < visible; s++) {
const uint64_t vbase = ((uint64_t)s * args.n_head + head) * args.value_dim;
acc += scores[s] *
glm_dense_cache_load_f32_or_f16(value_cache,
vbase + d,
args.cache_f16);
}
out[d] = acc / denom;
}
}
kernel void kernel_glm_fill_selected_range(
constant ds4_metal_args_glm_fill_selected_range & args,
device uint32_t *selected,
uint gid [[thread_position_in_grid]]) {
if (gid < args.n_selected) selected[gid] = gid;
}
kernel void kernel_glm_fill_selected_range_batch(
constant ds4_metal_args_glm_fill_selected_range_batch & args,
device uint32_t *selected,
uint gid [[thread_position_in_grid]]) {
const uint total = args.n_tokens * args.n_selected;
if (gid >= total || args.n_selected == 0u) return;
const uint token = gid / args.n_selected;
const uint slot = gid - token * args.n_selected;
const uint visible = args.pos0 + token + 1u;
selected[gid] = slot < visible ? slot : args.pad_row;
}
kernel void kernel_glm_indexer_rope_tail_f32(
constant ds4_metal_args_glm_indexer_rope_tail & args,
device char *x,
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint head = tgpig.x;
const uint token = tgpig.y;
if (head >= args.n_head || token >= args.n_tokens) return;
if (args.rot_dim == 0u || args.rot_offset > args.head_dim ||
args.rot_dim > args.head_dim - args.rot_offset || (args.rot_dim & 1u) != 0u) return;
const uint nth = ntg_u.x;
const uint pos = args.pos0 + token;
device float *row =
(device float *)(x +
((uint64_t)token * args.n_head + head) * args.head_dim * sizeof(float));
row += args.rot_offset;
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)args.rot_dim,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
const float theta_base = (float)pos;
const float inv_ndims = -1.0f / (float)args.rot_dim;
for (uint i = tid * 2u; i < args.rot_dim; i += nth * 2u) {
const uint rel_i0 = i;
#ifdef DS4_METAL_ROPE_EXP2_LOG2
const float theta = theta_base * exp2(inv_ndims * (float)rel_i0 * log2(args.freq_base));
#else
const float theta = theta_base * pow(args.freq_base, inv_ndims * (float)rel_i0);
#endif
float cos_theta;
float sin_theta;
glm_rope_yarn(theta,
args.freq_scale,
corr_dims,
(int)rel_i0,
args.ext_factor,
args.attn_factor,
&cos_theta,
&sin_theta);
const uint j = i + 1u;
const float x0 = row[i];
const float x1 = row[j];
row[i] = x0 * cos_theta - x1 * sin_theta;
row[j] = x0 * sin_theta + x1 * cos_theta;
}
}
static inline float glm_cache_load_f32_or_f16(
device const char *base,
uint64_t index,
uint cache_f16) {
if (cache_f16 != 0u) {
return (float)((device const half *)base)[index];
}
return ((device const float *)base)[index];
}
static inline float glm_cache_load_f16_only(
device const char *base,
uint64_t index) {
return (float)((device const half *)base)[index];
}
static inline float2 glm_cache_load_rotated_rope_pair(
device const char *base,
uint64_t rope_base,
uint r,
uint row,
uint qk_rope,
uint cache_f16,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float corr0,
float corr1) {
const float theta_base = (float)row;
const float inv_ndims = -1.0f / (float)qk_rope;
#ifdef DS4_METAL_ROPE_EXP2_LOG2
const float theta = theta_base * exp2(inv_ndims * (float)r * log2(freq_base));
#else
const float theta = theta_base * pow(freq_base, inv_ndims * (float)r);
#endif
float corr_dims[2] = {corr0, corr1};
float cos_theta;
float sin_theta;
glm_rope_yarn(theta,
freq_scale,
corr_dims,
(int)r,
ext_factor,
attn_factor,
&cos_theta,
&sin_theta);
const float x0 = glm_cache_load_f32_or_f16(base, rope_base + r, cache_f16);
const float x1 = glm_cache_load_f32_or_f16(base, rope_base + r + 1u, cache_f16);
return float2(x0 * cos_theta - x1 * sin_theta,
x0 * sin_theta + x1 * cos_theta);
}
static inline float2 glm_cache_load_rotated_rope_pair_f16_only(
device const char *base,
uint64_t rope_base,
uint r,
uint row,
uint qk_rope,
float freq_base,
float freq_scale,
float ext_factor,
float attn_factor,
float corr0,
float corr1) {
const float theta_base = (float)row;
const float inv_ndims = -1.0f / (float)qk_rope;
#ifdef DS4_METAL_ROPE_EXP2_LOG2
const float theta = theta_base * exp2(inv_ndims * (float)r * log2(freq_base));
#else
const float theta = theta_base * pow(freq_base, inv_ndims * (float)r);
#endif
float corr_dims[2] = {corr0, corr1};
float cos_theta;
float sin_theta;
glm_rope_yarn(theta,
freq_scale,
corr_dims,
(int)r,
ext_factor,
attn_factor,
&cos_theta,
&sin_theta);
const float x0 = glm_cache_load_f16_only(base, rope_base + r);
const float x1 = glm_cache_load_f16_only(base, rope_base + r + 1u);
return float2(x0 * cos_theta - x1 * sin_theta,
x0 * sin_theta + x1 * cos_theta);
}
static inline float glm_q8_0_weight_at(
device const char *row,
uint col) {
const uint block = col >> 5;
const uint qi = col & 31u;
device const char *block_base = row + (uint64_t)block * 34u;
const float d = (float)(*((device const half *)block_base));
device const int8_t *qs = (device const int8_t *)(block_base + 2u);
return d * (float)qs[qi];
}
static inline float glm_q8_0_dot_row_tg_f32(
device const char *row,
threadgroup const float *x,
uint n_cols) {
float acc = 0.0f;
const uint n_blocks = (n_cols + 31u) >> 5;
for (uint block = 0; block < n_blocks; block++) {
device const char *block_base = row + (uint64_t)block * 34u;
const float d = (float)(*((device const half *)block_base));
device const int8_t *qs = (device const int8_t *)(block_base + 2u);
const uint base = block << 5;
const uint count = min(32u, n_cols - base);
for (uint qi = 0; qi < count; qi++) {
acc += d * (float)qs[qi] * x[base + qi];
}
}
return acc;
}
static inline float glm_q8_0_dot_row_tg_f32_512(
device const char *row,
threadgroup const float *x) {
float acc = 0.0f;
for (uint block = 0; block < 16u; block++) {
device const char *block_base = row + (uint64_t)block * 34u;
const float d = (float)(*((device const half *)block_base));
device const int8_t *qs = (device const int8_t *)(block_base + 2u);
const uint base = block << 5;
FOR_UNROLL (uint qi = 0; qi < 32u; qi++) {
acc += d * (float)qs[qi] * x[base + qi];
}
}
return acc;
}
static inline float glm_q8_0_dot_row_tg_f32_fast(
device const char *row,
threadgroup const float *x,
uint n_cols) {
if (n_cols == 512u) {
return glm_q8_0_dot_row_tg_f32_512(row, x);
}
return glm_q8_0_dot_row_tg_f32(row, x, n_cols);
}
static inline float glm_q8_0_dot_row_dev_f32(
device const char *row,
device const float *x,
uint n_cols) {
float acc = 0.0f;
const uint n_blocks = (n_cols + 31u) >> 5;
for (uint block = 0; block < n_blocks; block++) {
device const char *block_base = row + (uint64_t)block * 34u;
const float d = (float)(*((device const half *)block_base));
device const int8_t *qs = (device const int8_t *)(block_base + 2u);
const uint base = block << 5;
const uint count = min(32u, n_cols - base);
for (uint qi = 0; qi < count; qi++) {
acc += d * (float)qs[qi] * x[base + qi];
}
}
return acc;
}
#define DS4_METAL_GGUF_Q4_0 2u
#define DS4_METAL_GGUF_Q8_0 8u
#define DS4_METAL_GGUF_Q4_K 12u
static inline uchar2 glm_q4_K_scale_min(int j, int k, device const uchar *q) {
return j < 4 ? uchar2{uchar(q[j + 0 + k] & 63), uchar(q[j + 4 + k] & 63)}
: uchar2{uchar((q[j + 4 + k] & 0x0f) | ((q[j - 4 + k] & 0xc0) >> 2)),
uchar((q[j + 4 + k] >> 4) | ((q[j - 0 + k] & 0xc0) >> 2))};
}
static inline float glm_q4_0_weight_at(device const char *row, uint col) {
const uint block = col >> 5;
const uint qi = col & 31u;
device const char *block_base = row + (uint64_t)block * 18u;
const float d = (float)(*((device const half *)block_base));
device const uchar *qs = (device const uchar *)(block_base + 2u);
/* ggml Q4_0: elems 0..15 = low nibbles of qs[0..15], 16..31 = high. */
const uchar packed = qs[qi & 15u];
const uchar q = (qi < 16u) ? (packed & 0x0f) : (packed >> 4);
return d * ((float)q - 8.0f);
}
static inline float glm_q4_K_weight_at(device const char *row, uint col) {
const uint block = col >> 8u;
const uint idx = col & 255u;
device const char *block_base = row + (uint64_t)block * 144u;
const float d = (float)(*((device const half *)(block_base + 0u)));
const float dmin = (float)(*((device const half *)(block_base + 2u)));
device const uchar *scales = (device const uchar *)(block_base + 4u);
device const uchar *qs = (device const uchar *)(block_base + 16u);
const uint group = idx >> 5u;
const uint l = idx & 31u;
const uchar2 sm = glm_q4_K_scale_min((int)group, 0, scales);
const uint byte_off = (group >> 1u) * 32u + l;
const uint shift = (group & 1u) * 4u;
const uint q = ((uint)qs[byte_off] >> shift) & 0x0fu;
return d * (float)sm.x * (float)q - dmin * (float)sm.y;
}
static inline float glm_quant_weight_at(
uint weight_type,
device const char *row,
uint col) {
if (weight_type == DS4_METAL_GGUF_Q4_0) return glm_q4_0_weight_at(row, col);
if (weight_type == DS4_METAL_GGUF_Q4_K) return glm_q4_K_weight_at(row, col);
return glm_q8_0_weight_at(row, col);
}
static inline float glm_q4_0_dot_row_tg_f32(
device const char *row,
threadgroup const float *x,
uint n_cols) {
float acc = 0.0f;
for (uint col = 0; col < n_cols; col++) {
acc += glm_q4_0_weight_at(row, col) * x[col];
}
return acc;
}
static inline float glm_q4_K_dot_row_tg_f32(
device const char *row,
threadgroup const float *x,
uint n_cols) {
float acc = 0.0f;
for (uint col = 0; col < n_cols; col++) {
acc += glm_q4_K_weight_at(row, col) * x[col];
}
return acc;
}
static inline float glm_quant_dot_row_tg_f32(
uint weight_type,
device const char *row,
threadgroup const float *x,
uint n_cols) {
if (weight_type == DS4_METAL_GGUF_Q4_0) return glm_q4_0_dot_row_tg_f32(row, x, n_cols);
if (weight_type == DS4_METAL_GGUF_Q4_K) return glm_q4_K_dot_row_tg_f32(row, x, n_cols);
return glm_q8_0_dot_row_tg_f32_fast(row, x, n_cols);
}
/* Per-lane Q4_K row dot: lane l covers elements (g*32 + l) of every
* 32-group so the 144-byte superblocks are read with coalesced per-lane
* bytes; callers simd_sum the result. x lives in threadgroup memory. */
static inline float glm_q4_K_dot_row_lane_f32(
device const char *row,
threadgroup const float *x,
uint n_cols,
ushort lane) {
float acc = 0.0f;
const uint nblocks = n_cols >> 8u;
for (uint b = 0; b < nblocks; b++) {
device const char *block_base = row + (uint64_t)b * 144u;
const float d = (float)(*((device const half *)(block_base + 0u)));
const float dmin = (float)(*((device const half *)(block_base + 2u)));
device const uchar *scales = (device const uchar *)(block_base + 4u);
device const uchar *qs = (device const uchar *)(block_base + 16u);
threadgroup const float *xb = x + (b << 8u);
FOR_UNROLL (uint g = 0; g < 8u; g++) {
const uchar2 sm = glm_q4_K_scale_min((int)g, 0, scales);
const uint byte_off = (g >> 1u) * 32u + lane;
const uint shift = (g & 1u) * 4u;
const uint q = ((uint)qs[byte_off] >> shift) & 0x0fu;
const float xv = xb[(g << 5u) + lane];
acc += (d * (float)sm.x * (float)q - dmin * (float)sm.y) * xv;
}
}
return acc;
}
static inline float glm_quant_dot_row_dev_f32(
uint weight_type,
device const char *row,
device const float *x,
uint n_cols) {
if (weight_type == DS4_METAL_GGUF_Q8_0) return glm_q8_0_dot_row_dev_f32(row, x, n_cols);
float acc = 0.0f;
for (uint col = 0; col < n_cols; col++) {
acc += glm_quant_weight_at(weight_type, row, col) * x[col];
}
return acc;
}
kernel void kernel_glm_indexer_score_one(
constant ds4_metal_args_glm_indexer_score_one & args,
device const char *q,
device const float *weights,
device const char *indexer_key_cache,
device float *scores,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint row = tgpig.x;
if (row >= args.n_rows) return;
const uint nth = ntg_u.x;
float score = 0.0f;
for (uint h = 0; h < args.n_head; h++) {
float partial = 0.0f;
device const float *qh =
(device const float *)(q + (uint64_t)h * args.head_dim * sizeof(float));
for (uint d = tid; d < args.head_dim; d += nth) {
const float k = glm_cache_load_f32_or_f16(indexer_key_cache,
(uint64_t)row * args.head_dim + d,
args.cache_f16);
partial += qh[d] * k;
}
scratch[tid] = partial;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) scratch[tid] += scratch[tid + step];
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (tid == 0) {
score += max(scratch[0] * args.scale, 0.0f) * weights[h];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (tid == 0) scores[row] = score;
}
kernel void kernel_glm_indexer_score_one_direct(
constant ds4_metal_args_glm_indexer_score_one & args,
device const char *q,
device const float *weights,
device const char *indexer_key_cache,
device float *scores,
threadgroup float *shared [[threadgroup(0)]],
uint row [[threadgroup_position_in_grid]],
ushort tid [[thread_index_in_threadgroup]],
ushort lane [[thread_index_in_simdgroup]],
ushort sg [[simdgroup_index_in_threadgroup]]) {
if (row >= args.n_rows || args.n_head != 32u || args.head_dim != 128u) {
return;
}
threadgroup float *ktg = shared;
threadgroup float *psum = ktg + 128u;
if (tid < 128u) {
ktg[tid] = glm_cache_load_f32_or_f16(indexer_key_cache,
(uint64_t)row * 128u + tid,
args.cache_f16);
}
float acc = 0.0f;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint head0 = 0; head0 < 32u; head0 += 4u) {
const uint head = head0 + (uint)sg;
device const float4 *q4 = (device const float4 *)(q +
(uint64_t)head * 128u * sizeof(float));
threadgroup const float4 *k4 = (threadgroup const float4 *)ktg;
float s = dot(q4[lane], k4[lane]);
s = simd_sum(s);
if (lane == 0) {
psum[sg] = max(s * args.scale, 0.0f) * weights[head];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
if (tid == 0) {
acc += psum[0];
acc += psum[1];
acc += psum[2];
acc += psum[3];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (tid == 0) {
scores[row] = acc;
}
}
kernel void kernel_glm_indexer_scores_batch(
constant ds4_metal_args_glm_indexer_scores_batch & args,
device const char *q,
device const char *weights,
device const char *indexer_key_cache,
device char *scores,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint row = tgpig.x;
const uint token = tgpig.y;
if (row >= args.n_rows || token >= args.n_tokens) return;
device float *dst = (device float *)(scores +
(uint64_t)token * args.score_token_stride) + row;
const uint visible = min(args.pos0 + token + 1u, args.n_rows);
if (row >= visible) {
if (tid == 0) *dst = -INFINITY;
return;
}
const uint nth = ntg_u.x;
float score = 0.0f;
for (uint h = 0; h < args.n_head; h++) {
float partial = 0.0f;
device const float *qh = (device const float *)(q +
(uint64_t)token * args.q_token_stride +
(uint64_t)h * args.q_head_stride);
for (uint d = tid; d < args.head_dim; d += nth) {
const float k = glm_cache_load_f32_or_f16(indexer_key_cache,
(uint64_t)row * args.head_dim + d,
args.cache_f16);
partial += qh[d] * k;
}
scratch[tid] = partial;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) scratch[tid] += scratch[tid + step];
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (tid == 0) {
device const float *w = (device const float *)(weights +
(uint64_t)token * args.weights_token_stride);
score += max(scratch[0] * args.scale, 0.0f) * w[h];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (tid == 0) *dst = score;
}
kernel void kernel_glm_indexer_scores_tiled_f32(
constant ds4_metal_args_glm_indexer_scores_batch & args,
device const char *q,
device const char *weights,
device const char *indexer_key_cache,
device char *scores,
threadgroup float *shared [[threadgroup(0)]],
uint2 tgpig [[threadgroup_position_in_grid]],
ushort tid [[thread_index_in_threadgroup]],
ushort lane [[thread_index_in_simdgroup]],
ushort sg [[simdgroup_index_in_threadgroup]]) {
constexpr uint TM = 8;
constexpr uint TN = 32;
constexpr uint TS = 8;
constexpr uint D = 128;
const uint row_base = tgpig.x * TN;
const uint token_base = tgpig.y * TM;
threadgroup float *qtg = shared;
threadgroup float *ktg = qtg + TM*D;
threadgroup float *dot = ktg + TN*D;
const uint last_token = min(token_base + TM, args.n_tokens);
const uint max_visible = last_token > token_base ?
min(args.pos0 + last_token, args.n_rows) : 0u;
if (row_base >= max_visible) {
for (uint i = tid; i < TM*TN; i += 128) {
const uint tr = i / TN;
const uint rc = i - tr*TN;
const uint token = token_base + tr;
const uint row = row_base + rc;
if (token < args.n_tokens && row < args.n_rows) {
device float *dst = (device float *)(scores +
(uint64_t)token * args.score_token_stride) + row;
*dst = -INFINITY;
}
}
return;
}
for (uint i = tid; i < TN*D; i += 128) {
const uint rc = i / D;
const uint d = i - rc*D;
const uint row = row_base + rc;
float v = 0.0f;
if (row < args.n_rows) {
v = glm_cache_load_f32_or_f16(indexer_key_cache,
(uint64_t)row * args.head_dim + d,
args.cache_f16);
}
ktg[i] = v;
}
const uint cell0 = lane;
const uint cell1 = lane + 32u;
const uint token_row0 = cell0 >> 3;
const uint token_row1 = cell1 >> 3;
const uint sub0 = cell0 & 7u;
const uint sub1 = cell1 & 7u;
const uint col0 = (uint)sg * TS + sub0;
const uint col1 = (uint)sg * TS + sub1;
const uint token0 = token_base + token_row0;
const uint token1 = token_base + token_row1;
const uint row0 = row_base + col0;
const uint row1 = row_base + col1;
float acc0 = 0.0f;
float acc1 = 0.0f;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint head = 0; head < args.n_head; head++) {
for (uint i = tid; i < TM*D; i += 128) {
const uint tr = i / D;
const uint d = i - tr*D;
const uint token = token_base + tr;
float v = 0.0f;
if (token < args.n_tokens) {
device const float *qrow = (device const float *)(q +
(uint64_t)token * args.q_token_stride +
(uint64_t)head * args.q_head_stride);
v = qrow[d];
}
qtg[i] = v;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
simdgroup_float8x8 mdot = make_filled_simdgroup_matrix<float, 8>(0.0f);
for (uint db = 0; db < D/TS; db++) {
simdgroup_float8x8 mq;
simdgroup_float8x8 mk;
simdgroup_load(mq, qtg + db*TS, D, 0, false);
simdgroup_load(mk, ktg + ((uint)sg * TS) * D + db*TS, D, 0, true);
simdgroup_multiply_accumulate(mdot, mq, mk, mdot);
}
simdgroup_store(mdot, dot + (uint)sg * TS, TN, 0, false);
threadgroup_barrier(mem_flags::mem_threadgroup);
if (token0 < args.n_tokens && row0 < args.n_rows) {
device const float *w = (device const float *)(weights +
(uint64_t)token0 * args.weights_token_stride);
const float s = dot[token_row0*TN + col0];
acc0 += max(s * args.scale, 0.0f) * w[head];
}
if (token1 < args.n_tokens && row1 < args.n_rows) {
device const float *w = (device const float *)(weights +
(uint64_t)token1 * args.weights_token_stride);
const float s = dot[token_row1*TN + col1];
acc1 += max(s * args.scale, 0.0f) * w[head];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (token0 < args.n_tokens && row0 < args.n_rows) {
const uint visible = min(args.pos0 + token0 + 1u, args.n_rows);
device float *dst = (device float *)(scores +
(uint64_t)token0 * args.score_token_stride) + row0;
*dst = row0 < visible ? acc0 : -INFINITY;
}
if (token1 < args.n_tokens && row1 < args.n_rows) {
const uint visible = min(args.pos0 + token1 + 1u, args.n_rows);
device float *dst = (device float *)(scores +
(uint64_t)token1 * args.score_token_stride) + row1;
*dst = row1 < visible ? acc1 : -INFINITY;
}
}
kernel void kernel_glm_indexer_scores_tiled(
constant ds4_metal_args_glm_indexer_scores_batch & args,
device const char *q,
device const char *weights,
device const char *indexer_key_cache,
device char *scores,
threadgroup float *shared [[threadgroup(0)]],
uint2 tgpig [[threadgroup_position_in_grid]],
ushort tid [[thread_index_in_threadgroup]],
ushort lane [[thread_index_in_simdgroup]],
ushort sg [[simdgroup_index_in_threadgroup]]) {
constexpr uint TM = 8;
constexpr uint TN = 32;
constexpr uint TS = 8;
constexpr uint D = 128;
const uint row_base = tgpig.x * TN;
const uint token_base = tgpig.y * TM;
threadgroup half *qtg = (threadgroup half *)shared;
threadgroup half *ktg = qtg + TM*D;
threadgroup float *dot = (threadgroup float *)(ktg + TN*D);
const uint last_token = min(token_base + TM, args.n_tokens);
const uint max_visible = last_token > token_base ?
min(args.pos0 + last_token, args.n_rows) : 0u;
if (row_base >= max_visible) {
for (uint i = tid; i < TM*TN; i += 128) {
const uint tr = i / TN;
const uint rc = i - tr*TN;
const uint token = token_base + tr;
const uint row = row_base + rc;
if (token < args.n_tokens && row < args.n_rows) {
device float *dst = (device float *)(scores +
(uint64_t)token * args.score_token_stride) + row;
*dst = -INFINITY;
}
}
return;
}
for (uint i = tid; i < TN*D; i += 128) {
const uint rc = i / D;
const uint d = i - rc*D;
const uint row = row_base + rc;
half v = half(0.0f);
if (row < args.n_rows) {
v = half(glm_cache_load_f32_or_f16(indexer_key_cache,
(uint64_t)row * args.head_dim + d,
args.cache_f16));
}
ktg[i] = v;
}
const uint cell0 = lane;
const uint cell1 = lane + 32u;
const uint token_row0 = cell0 >> 3;
const uint token_row1 = cell1 >> 3;
const uint sub0 = cell0 & 7u;
const uint sub1 = cell1 & 7u;
const uint col0 = (uint)sg * TS + sub0;
const uint col1 = (uint)sg * TS + sub1;
const uint token0 = token_base + token_row0;
const uint token1 = token_base + token_row1;
const uint row0 = row_base + col0;
const uint row1 = row_base + col1;
float acc0 = 0.0f;
float acc1 = 0.0f;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint head = 0; head < args.n_head; head++) {
for (uint i = tid; i < TM*D; i += 128) {
const uint tr = i / D;
const uint d = i - tr*D;
const uint token = token_base + tr;
half v = half(0.0f);
if (token < args.n_tokens) {
device const float *qrow = (device const float *)(q +
(uint64_t)token * args.q_token_stride +
(uint64_t)head * args.q_head_stride);
v = half(qrow[d]);
}
qtg[i] = v;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
simdgroup_float8x8 mdot = make_filled_simdgroup_matrix<float, 8>(0.0f);
for (uint db = 0; db < D/TS; db++) {
simdgroup_half8x8 mq;
simdgroup_half8x8 mk;
simdgroup_load(mq, qtg + db*TS, D, 0, false);
simdgroup_load(mk, ktg + ((uint)sg * TS) * D + db*TS, D, 0, true);
simdgroup_multiply_accumulate(mdot, mq, mk, mdot);
}
simdgroup_store(mdot, dot + (uint)sg * TS, TN, 0, false);
threadgroup_barrier(mem_flags::mem_threadgroup);
if (token0 < args.n_tokens && row0 < args.n_rows) {
device const float *w = (device const float *)(weights +
(uint64_t)token0 * args.weights_token_stride);
const float s = dot[token_row0*TN + col0];
acc0 += max(s * args.scale, 0.0f) * w[head];
}
if (token1 < args.n_tokens && row1 < args.n_rows) {
device const float *w = (device const float *)(weights +
(uint64_t)token1 * args.weights_token_stride);
const float s = dot[token_row1*TN + col1];
acc1 += max(s * args.scale, 0.0f) * w[head];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (token0 < args.n_tokens && row0 < args.n_rows) {
const uint visible = min(args.pos0 + token0 + 1u, args.n_rows);
device float *dst = (device float *)(scores +
(uint64_t)token0 * args.score_token_stride) + row0;
*dst = row0 < visible ? acc0 : -INFINITY;
}
if (token1 < args.n_tokens && row1 < args.n_rows) {
const uint visible = min(args.pos0 + token1 + 1u, args.n_rows);
device float *dst = (device float *)(scores +
(uint64_t)token1 * args.score_token_stride) + row1;
*dst = row1 < visible ? acc1 : -INFINITY;
}
}
kernel void kernel_glm_qk_lowrank_q8_0(
constant ds4_metal_args_glm_qk_lowrank & args,
device const char *weight,
device const char *q,
device char *qk_low,
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint head = tgpig.x;
if (head >= args.n_head) return;
const uint nth = ntg_u.x;
device const float *qh =
(device const float *)(q + (uint64_t)head * args.qk_dim * sizeof(float));
device float *out =
(device float *)(qk_low + (uint64_t)head * args.kv_lora_dim * sizeof(float));
for (uint j = tid; j < args.kv_lora_dim; j += nth) {
device const char *row =
weight + ((uint64_t)head * args.kv_lora_dim + j) * args.row_bytes;
out[j] = glm_quant_dot_row_dev_f32(args.weight_type, row, qh, args.qk_nope);
}
}
kernel void kernel_glm_qk_lowrank_q8_0_glm52(
constant ds4_metal_args_glm_qk_lowrank & args,
device const char *weight,
device const char *q,
device char *qk_low,
threadgroup float *x [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
constexpr uint n_head = 64u;
constexpr uint kv_lora_dim = 512u;
constexpr uint qk_nope = 192u;
constexpr uint qk_dim = 256u;
constexpr uint row_bytes = 204u;
const uint head = tgpig.x;
if (head >= n_head ||
args.n_head != n_head ||
args.kv_lora_dim != kv_lora_dim ||
args.qk_nope != qk_nope ||
args.qk_dim != qk_dim ||
args.row_bytes != row_bytes ||
args.weight_type != DS4_METAL_GGUF_Q8_0) {
return;
}
const uint nth = ntg_u.x;
device const float *qh =
(device const float *)(q + (uint64_t)head * qk_dim * sizeof(float));
for (uint d = tid; d < qk_nope; d += nth) {
x[d] = qh[d];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
device float *out =
(device float *)(qk_low + (uint64_t)head * kv_lora_dim * sizeof(float));
for (uint j = tid; j < kv_lora_dim; j += nth) {
device const char *row =
weight + ((uint64_t)head * kv_lora_dim + j) * row_bytes;
float acc = 0.0f;
for (uint block = 0; block < 6u; block++) {
device const char *block_base = row + (uint64_t)block * 34u;
const float d = (float)(*((device const half *)block_base));
device const int8_t *qs = (device const int8_t *)(block_base + 2u);
const uint base = block << 5;
FOR_UNROLL (uint qi = 0; qi < 32u; qi++) {
const uint col = base + qi;
acc += d * (float)qs[qi] * x[col];
}
}
out[j] = acc;
}
}
// Coalesced GLM 5.2 decode qk-low: one simdgroup per pair of output rows,
// lanes split the 192-wide dot so the 204-byte Q8 rows are read with
// consecutive per-lane bytes. The thread-per-row variant above issues
// strided scalar byte loads from only 64 threadgroups and measures ~7.5x
// off the weight-bandwidth floor.
kernel void kernel_glm_qk_lowrank_q8_0_glm52_sg(
constant ds4_metal_args_glm_qk_lowrank & args,
device const char *weight,
device const char *q,
device char *qk_low,
uint3 tgpig [[threadgroup_position_in_grid]],
ushort3 ntg_u [[threads_per_threadgroup]],
ushort tiisg [[thread_index_in_simdgroup]],
ushort sgitg [[simdgroup_index_in_threadgroup]]) {
constexpr uint n_head = 64u;
constexpr uint kv_lora_dim = 512u;
constexpr uint qk_nope = 192u;
constexpr uint qk_dim = 256u;
constexpr uint NR = 2u;
const uint head = tgpig.x;
const uint wt = args.weight_type;
if (head >= n_head ||
args.n_head != n_head ||
args.kv_lora_dim != kv_lora_dim ||
args.qk_nope != qk_nope ||
args.qk_dim != qk_dim ||
!((wt == DS4_METAL_GGUF_Q8_0 && args.row_bytes == 204u) ||
(wt == DS4_METAL_GGUF_Q4_0 && args.row_bytes == 108u))) {
return;
}
const uint row_bytes = args.row_bytes;
const uint nsg = ntg_u.y;
const uint row0 = (tgpig.y * nsg + (uint)sgitg) * NR;
if (row0 >= kv_lora_dim) return;
device const float *qh =
(device const float *)(q + (uint64_t)head * qk_dim * sizeof(float));
float qv[6];
FOR_UNROLL (uint b = 0; b < 6u; b++) {
qv[b] = qh[(b << 5) + tiisg];
}
device float *out =
(device float *)(qk_low + (uint64_t)head * kv_lora_dim * sizeof(float));
for (uint r = 0; r < NR; r++) {
const uint j = row0 + r;
device const char *row =
weight + ((uint64_t)head * kv_lora_dim + j) * row_bytes;
float acc = 0.0f;
if (wt == DS4_METAL_GGUF_Q8_0) {
FOR_UNROLL (uint b = 0; b < 6u; b++) {
device const char *block_base = row + (uint64_t)b * 34u;
const float d = (float)(*((device const half *)block_base));
device const int8_t *qs = (device const int8_t *)(block_base + 2u);
acc += d * (float)qs[tiisg] * qv[b];
}
} else {
/* Q4_0: 18B blocks; elems 0..15 = low nibbles, 16..31 = high. */
FOR_UNROLL (uint b = 0; b < 6u; b++) {
device const char *block_base = row + (uint64_t)b * 18u;
const float d = (float)(*((device const half *)block_base));
device const uint8_t *qs = (device const uint8_t *)(block_base + 2u);
const uint byte = qs[tiisg & 15u];
const float v = (float)((tiisg < 16u) ? (byte & 0xFu) : (byte >> 4)) - 8.0f;
acc += d * v * qv[b];
}
}
const float sum = simd_sum(acc);
if (tiisg == 0) {
out[j] = sum;
}
}
}
kernel void kernel_glm_qk_lowrank_q8_0_batch(
constant ds4_metal_args_glm_qk_lowrank_batch & args,
device const char *weight,
device const char *q,
device char *qk_low,
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint head = tgpig.x + args.head_base;
const uint token = tgpig.y;
if (head >= args.n_head || token >= args.n_tokens) return;
const uint nth = ntg_u.x;
const uint qk_dim = args.qk_dim;
const uint64_t q_token_stride = (uint64_t)args.n_head * qk_dim * sizeof(float);
const uint64_t low_token_stride = (uint64_t)args.n_head * args.kv_lora_dim * sizeof(float);
device const float *qh =
(device const float *)(q +
(uint64_t)token * q_token_stride +
(uint64_t)head * qk_dim * sizeof(float));
device float *out =
(device float *)(qk_low +
(uint64_t)token * low_token_stride +
(uint64_t)head * args.kv_lora_dim * sizeof(float));
for (uint j = tid; j < args.kv_lora_dim; j += nth) {
device const char *row =
weight + ((uint64_t)head * args.kv_lora_dim + j) * args.row_bytes;
out[j] = glm_quant_dot_row_dev_f32(args.weight_type, row, qh, args.qk_nope);
}
}
kernel void kernel_glm_qk_lowrank_q8_0_batch_glm52_t4(
constant ds4_metal_args_glm_qk_lowrank_batch & args,
device const char *weight,
device const char *q,
device char *qk_low,
threadgroup float *x [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
constexpr uint n_head = 64u;
constexpr uint kv_lora_dim = 512u;
constexpr uint qk_nope = 192u;
constexpr uint qk_dim = 256u;
constexpr uint tile_tokens = 4u;
constexpr uint row_bytes = 204u;
const uint head = tgpig.x + args.head_base;
const uint token0 = tgpig.y * tile_tokens;
const uint nth = ntg_u.x;
const uint64_t q_token_stride = (uint64_t)n_head * qk_dim * sizeof(float);
const uint64_t low_token_stride = (uint64_t)n_head * kv_lora_dim * sizeof(float);
for (uint t = 0; t < tile_tokens; t++) {
const uint token = token0 + t;
threadgroup float *xt = x + t * qk_nope;
if (token < args.n_tokens) {
device const float *qh =
(device const float *)(q +
(uint64_t)token * q_token_stride +
(uint64_t)head * qk_dim * sizeof(float));
for (uint d = tid; d < qk_nope; d += nth) {
xt[d] = qh[d];
}
} else {
for (uint d = tid; d < qk_nope; d += nth) {
xt[d] = 0.0f;
}
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint j = tid; j < kv_lora_dim; j += nth) {
device const char *row =
weight + ((uint64_t)head * kv_lora_dim + j) * row_bytes;
float acc0 = 0.0f;
float acc1 = 0.0f;
float acc2 = 0.0f;
float acc3 = 0.0f;
for (uint block = 0; block < 6u; block++) {
device const char *block_base = row + (uint64_t)block * 34u;
const float d = (float)(*((device const half *)block_base));
device const int8_t *qs = (device const int8_t *)(block_base + 2u);
const uint base = block << 5;
FOR_UNROLL (uint qi = 0; qi < 32u; qi++) {
const uint col = base + qi;
const float wq = d * (float)qs[qi];
acc0 += wq * x[col];
acc1 += wq * x[qk_nope + col];
acc2 += wq * x[2u * qk_nope + col];
acc3 += wq * x[3u * qk_nope + col];
}
}
if (token0 < args.n_tokens) {
device float *out0 =
(device float *)(qk_low +
(uint64_t)token0 * low_token_stride +
(uint64_t)head * kv_lora_dim * sizeof(float));
out0[j] = acc0;
}
if (token0 + 1u < args.n_tokens) {
device float *out1 =
(device float *)(qk_low +
(uint64_t)(token0 + 1u) * low_token_stride +
(uint64_t)head * kv_lora_dim * sizeof(float));
out1[j] = acc1;
}
if (token0 + 2u < args.n_tokens) {
device float *out2 =
(device float *)(qk_low +
(uint64_t)(token0 + 2u) * low_token_stride +
(uint64_t)head * kv_lora_dim * sizeof(float));
out2[j] = acc2;
}
if (token0 + 3u < args.n_tokens) {
device float *out3 =
(device float *)(qk_low +
(uint64_t)(token0 + 3u) * low_token_stride +
(uint64_t)head * kv_lora_dim * sizeof(float));
out3[j] = acc3;
}
}
}
kernel void kernel_glm_value_project_q8_0(
constant ds4_metal_args_glm_qk_lowrank & args,
device const char *weight,
device const char *lora,
device char *heads,
threadgroup float *x [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint head = tgpig.x;
if (head >= args.n_head) return;
const uint nth = ntg_u.x;
device const float *src =
(device const float *)(lora + (uint64_t)head * args.kv_lora_dim * sizeof(float));
for (uint j = tid; j < args.kv_lora_dim; j += nth) {
x[j] = src[j];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
device float *out =
(device float *)(heads + (uint64_t)head * args.qk_dim * sizeof(float));
for (uint d = tid; d < args.qk_dim; d += nth) {
device const char *row =
weight + ((uint64_t)head * args.qk_dim + d) * args.row_bytes;
out[d] = glm_quant_dot_row_tg_f32(args.weight_type, row, x, args.kv_lora_dim);
}
}
kernel void kernel_glm_value_project_q8_0_batch_heads(
constant ds4_metal_args_glm_qk_lowrank_batch & args,
device const char *weight,
device const char *lora,
device char *heads,
threadgroup float *x [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint head = tgpig.x + args.head_base;
const uint token = tgpig.y;
if (head >= args.n_head || token >= args.n_tokens) return;
const uint nth = ntg_u.x;
const uint value_dim = args.qk_dim;
const uint64_t lora_token_stride =
(uint64_t)args.n_head * args.kv_lora_dim * sizeof(float);
const uint64_t heads_token_stride =
(uint64_t)args.n_head * value_dim * sizeof(float);
device const float *src =
(device const float *)(lora +
(uint64_t)token * lora_token_stride +
(uint64_t)head * args.kv_lora_dim * sizeof(float));
device float *out =
(device float *)(heads +
(uint64_t)token * heads_token_stride +
(uint64_t)head * value_dim * sizeof(float));
for (uint j = tid; j < args.kv_lora_dim; j += nth) {
x[j] = src[j];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint d = tid; d < value_dim; d += nth) {
device const char *row =
weight + ((uint64_t)head * value_dim + d) * args.row_bytes;
out[d] = glm_quant_dot_row_tg_f32(args.weight_type, row, x, args.kv_lora_dim);
}
}
kernel void kernel_glm_value_project_q8_0_batch_heads_mma(
constant ds4_metal_args_glm_qk_lowrank_batch & args,
device const char *weight,
device const char *lora,
device char *heads,
threadgroup char *shmem [[threadgroup(0)]],
uint3 tgpig [[threadgroup_position_in_grid]],
ushort tid [[thread_index_in_threadgroup]],
ushort sg [[simdgroup_index_in_threadgroup]]) {
constexpr uint NR0 = 64u;
constexpr uint NR1 = 32u;
constexpr uint NK = 32u;
constexpr uint NL0 = 2u;
constexpr uint NL1 = 4u;
const uint token0 = tgpig.x * NR1;
const uint value0 = tgpig.y * NR0;
const uint head = tgpig.z + args.head_base;
if (head >= args.n_head || token0 >= args.n_tokens || value0 >= args.qk_dim) {
return;
}
threadgroup half *sa = (threadgroup half *)shmem;
threadgroup half *sb = (threadgroup half *)(shmem + 4096u);
const uint nr0 = min(NR0, args.qk_dim - value0);
const uint nr1 = min(NR1, args.n_tokens - token0);
const uint lr0 = min((uint)tid / NL0, nr0 - 1u);
const uint lr1 = min((uint)tid / NL1, nr1 - 1u);
const uint il0 = (uint)tid & 1u;
const uint iy = 8u * ((uint)tid & (NL1 - 1u));
const uint64_t lora_token_stride =
(uint64_t)args.n_head * args.kv_lora_dim * sizeof(float);
const uint64_t heads_token_stride =
(uint64_t)args.n_head * args.qk_dim * sizeof(float);
const uint64_t head_lora_base =
(uint64_t)head * args.kv_lora_dim * sizeof(float);
const uint64_t head_out_base =
(uint64_t)head * args.qk_dim * sizeof(float);
simdgroup_half8x8 ma[4];
simdgroup_half8x8 mb[2];
simdgroup_float8x8 mc[8];
for (uint i = 0; i < 8u; i++) {
mc[i] = make_filled_simdgroup_matrix<float, 8>(0.0f);
}
for (uint loop_k = 0; loop_k < args.kv_lora_dim; loop_k += NK) {
const uint value = value0 + lr0;
const uint block = loop_k >> 5;
device const char *row =
weight + ((uint64_t)head * args.qk_dim + value) * args.row_bytes;
device const char *block_base = row + (uint64_t)block * 34u;
const float d = (float)(*((device const half *)block_base));
device const int8_t *qs = (device const int8_t *)(block_base + 2u);
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint i = 0; i < 16u; i++) {
const uint k = loop_k + 16u * il0 + i;
const uint sx = 2u * il0 + i / 8u;
const uint sy = ((uint)tid / NL0) / 8u;
const uint lx = ((uint)tid / NL0) & 7u;
const uint ly = i & 7u;
const uint ib = 8u * sx + sy;
const half v = (value < args.qk_dim && k < args.kv_lora_dim) ?
half(d * (float)qs[16u * il0 + i]) :
half(0.0f);
*(sa + 64u * ib + 8u * ly + lx) = v;
}
const uint token = token0 + lr1;
device const float *y =
(device const float *)(lora +
(uint64_t)token * lora_token_stride +
head_lora_base +
(uint64_t)loop_k * sizeof(float) +
(uint64_t)iy * sizeof(float));
for (uint i = 0; i < 8u; i++) {
const uint k = loop_k + iy + i;
const uint sx = ((uint)tid) & (NL1 - 1u);
const uint sy = ((uint)tid / NL1) / 8u;
const uint lx = i;
const uint ly = ((uint)tid / NL1) & 7u;
const uint ib = 4u * sx + sy;
const half v = (token < args.n_tokens && k < args.kv_lora_dim) ?
half(y[i]) :
half(0.0f);
*(sb + 64u * ib + 8u * ly + lx) = v;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
threadgroup const half *lsma = sa + 4u * 64u * ((uint)sg & 1u);
threadgroup const half *lsmb = sb + 2u * 64u * ((uint)sg >> 1);
for (uint ik = 0; ik < NK / 8u; ik++) {
simdgroup_barrier(mem_flags::mem_none);
for (uint i = 0; i < 4u; i++) {
simdgroup_load(ma[i], lsma + 64u * i, 8u, 0, false);
}
simdgroup_barrier(mem_flags::mem_none);
for (uint i = 0; i < 2u; i++) {
simdgroup_load(mb[i], lsmb + 64u * i, 8u, 0, false);
}
simdgroup_barrier(mem_flags::mem_none);
for (uint i = 0; i < 8u; i++) {
simdgroup_multiply_accumulate(mc[i], mb[i / 4u], ma[i & 3u], mc[i]);
}
lsma += 8u * 64u;
lsmb += 4u * 64u;
}
}
if (nr0 == NR0 && nr1 == NR1) {
device float *dst =
(device float *)(heads +
(uint64_t)(token0 + 16u * ((uint)sg >> 1)) * heads_token_stride +
head_out_base +
(uint64_t)(value0 + 32u * ((uint)sg & 1u)) * sizeof(float));
for (uint i = 0; i < 8u; i++) {
simdgroup_store(mc[i],
dst + 8u * (i & 3u) + 8u * (heads_token_stride / sizeof(float)) * (i / 4u),
heads_token_stride / sizeof(float),
0,
false);
}
} else {
threadgroup_barrier(mem_flags::mem_threadgroup);
threadgroup float *tmp = (threadgroup float *)shmem;
for (uint i = 0; i < 8u; i++) {
simdgroup_store(mc[i],
tmp + 32u * ((uint)sg & 1u) +
16u * ((uint)sg >> 1) * NR0 +
8u * (i & 3u) + 8u * NR0 * (i / 4u),
NR0,
0,
false);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
if (sg == 0) {
for (uint t = tid; t < nr1; t += 128u) {
device float *dst =
(device float *)(heads +
(uint64_t)(token0 + t) * heads_token_stride +
head_out_base +
(uint64_t)value0 * sizeof(float));
threadgroup const float *src = tmp + t * NR0;
for (uint v = 0; v < nr0; v++) {
dst[v] = src[v];
}
}
}
}
}
template <bool assume_valid_rows, bool assume_valid_heads>
kernel void kernel_glm_attention_indexed_decode_split_group8_partial_impl(
constant ds4_metal_args_glm_attention_indexed_decode_split & args,
device const char *q,
device const char *qk_low,
device const char *kv_lora_cache,
device const char *k_rope_cache,
device const uint32_t *selected,
device char *partial_lora,
device char *partial_ms,
threadgroup half4 *scratch [[threadgroup(0)]],
ushort tid_u [[thread_index_in_threadgroup]],
ushort lane_u [[thread_index_in_simdgroup]],
ushort sg_u [[simdgroup_index_in_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
constexpr uint group_heads = 8u;
constexpr uint stage_rows = 16u;
const uint tid = (uint)tid_u;
const uint lane = (uint)lane_u;
const uint head_in_group = (uint)sg_u;
const uint head = tgpig.x * group_heads + head_in_group;
const uint block = tgpig.y;
if (args.n_selected == 0u ||
args.cache_f16 == 0u ||
args.kv_lora_dim != 512u ||
args.qk_rope != 64u ||
args.block_rows == 0u ||
block >= args.n_blocks) {
return;
}
const bool valid_head = assume_valid_heads || head < args.n_head;
const uint safe_head = valid_head ? head : 0u;
const uint kv_vecs = args.kv_lora_dim >> 2;
const uint rope_vecs = args.qk_rope >> 2;
const uint qk_dim = args.qk_nope + args.qk_rope;
const uint block_start = block * args.block_rows;
const uint block_end = min(args.n_selected, block_start + args.block_rows);
threadgroup half4 *kv_shared = scratch;
threadgroup float4 *rope_shared =
(threadgroup float4 *)(kv_shared + stage_rows * kv_vecs);
device const float *qh =
(device const float *)(q + (uint64_t)safe_head * qk_dim * sizeof(float));
device const float4 *low4 =
(device const float4 *)(qk_low +
(uint64_t)safe_head * args.kv_lora_dim * sizeof(float));
float4 low0 = 0.0f;
float4 low1 = 0.0f;
float4 low2 = 0.0f;
float4 low3 = 0.0f;
float4 qrope = 0.0f;
if (valid_head) {
low0 = low4[lane + 0u];
low1 = low4[lane + 32u];
low2 = low4[lane + 64u];
low3 = low4[lane + 96u];
if (lane < rope_vecs) {
qrope = *((device const float4 *)(qh + args.qk_nope + lane * 4u));
}
}
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)args.qk_rope,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
float M = -FLT_MAX / 2.0f;
float S = 0.0f;
float4 o0 = 0.0f;
float4 o1 = 0.0f;
float4 o2 = 0.0f;
float4 o3 = 0.0f;
for (uint base = block_start; base < block_end; base += stage_rows) {
const uint rows = min(stage_rows, block_end - base);
for (uint off = tid; off < rows * kv_vecs; off += 256u) {
const uint rr = off / kv_vecs;
const uint vv = off - rr * kv_vecs;
const uint row = selected[base + rr];
const bool valid_row = assume_valid_rows || row < args.cache_cap;
if (valid_row) {
device const half4 *src =
(device const half4 *)((device const half *)kv_lora_cache +
(uint64_t)row * args.kv_lora_dim);
kv_shared[off] = src[vv];
} else {
kv_shared[off] = half4(half(0.0f));
}
}
for (uint off = tid; off < rows * rope_vecs; off += 256u) {
const uint rr = off / rope_vecs;
const uint vv = off - rr * rope_vecs;
const uint r = vv * 4u;
const uint row = selected[base + rr];
const bool valid_row = assume_valid_rows || row < args.cache_cap;
if (valid_row) {
const uint64_t rope_base = (uint64_t)row * args.qk_rope;
const float2 y0 =
glm_cache_load_rotated_rope_pair_f16_only(k_rope_cache,
rope_base,
r,
row,
args.qk_rope,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
const float2 y1 =
glm_cache_load_rotated_rope_pair_f16_only(k_rope_cache,
rope_base,
r + 2u,
row,
args.qk_rope,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
rope_shared[off] = float4(y0.x, y0.y, y1.x, y1.y);
} else {
rope_shared[off] = float4(0.0f);
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint rr = 0u; rr < rows; rr++) {
const uint row = selected[base + rr];
const bool valid_row = assume_valid_rows || row < args.cache_cap;
threadgroup const half4 *kv_row = kv_shared + rr * kv_vecs;
threadgroup const float4 *rope_row = rope_shared + rr * rope_vecs;
float partial = 0.0f;
if (valid_head && valid_row) {
partial += dot(low0, (float4)kv_row[lane + 0u]);
partial += dot(low1, (float4)kv_row[lane + 32u]);
partial += dot(low2, (float4)kv_row[lane + 64u]);
partial += dot(low3, (float4)kv_row[lane + 96u]);
if (lane < rope_vecs) {
partial += dot(qrope, rope_row[lane]);
}
}
const float sum = simd_sum(partial);
const float score =
(valid_head && valid_row) ? sum * args.scale : -FLT_MAX / 2.0f;
if (valid_head && valid_row) {
const float new_m = max(M, score);
const float old_scale = exp(M - new_m);
const float row_scale = exp(score - new_m);
o0 = o0 * old_scale + (float4)kv_row[lane + 0u] * row_scale;
o1 = o1 * old_scale + (float4)kv_row[lane + 32u] * row_scale;
o2 = o2 * old_scale + (float4)kv_row[lane + 64u] * row_scale;
o3 = o3 * old_scale + (float4)kv_row[lane + 96u] * row_scale;
S = S * old_scale + row_scale;
M = new_m;
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (valid_head) {
device float4 *out4 =
(device float4 *)(partial_lora +
((uint64_t)block * args.n_head + head) *
args.kv_lora_dim * sizeof(float));
out4[lane + 0u] = o0;
out4[lane + 32u] = o1;
out4[lane + 64u] = o2;
out4[lane + 96u] = o3;
if (lane == 0u) {
device float *ms =
(device float *)(partial_ms +
((uint64_t)block * args.n_head + head) * 2u * sizeof(float));
ms[0] = M;
ms[1] = S;
}
}
}
typedef decltype(kernel_glm_attention_indexed_decode_split_group8_partial_impl<false, false>)
glm_attention_indexed_decode_split_group8_partial_t;
template [[host_name("kernel_glm_attention_indexed_decode_split_group8_partial")]]
kernel glm_attention_indexed_decode_split_group8_partial_t
kernel_glm_attention_indexed_decode_split_group8_partial_impl<false, false>;
template [[host_name("kernel_glm_attention_indexed_decode_split_group8_partial_valid_fullheads")]]
kernel glm_attention_indexed_decode_split_group8_partial_t
kernel_glm_attention_indexed_decode_split_group8_partial_impl<true, true>;
template<uint FIXED_BLOCKS>
static void kernel_glm_attention_indexed_decode_split_group8_reduce_impl(
constant ds4_metal_args_glm_attention_indexed_decode_split & args,
device const char *partial_lora,
device const char *partial_ms,
device const char *value_weight,
device char *heads,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint head = tgpig.x;
const uint n_blocks = FIXED_BLOCKS != 0u ? FIXED_BLOCKS : args.n_blocks;
if (head >= args.n_head ||
args.n_selected == 0u ||
args.kv_lora_dim != 512u ||
n_blocks == 0u ||
n_blocks > 64u ||
(FIXED_BLOCKS != 0u && args.n_blocks != FIXED_BLOCKS)) {
return;
}
const uint nth = ntg_u.x;
threadgroup float *red = scratch;
threadgroup float *block_scale = scratch + 256u;
threadgroup float *lora_sum = scratch + 320u;
float local_m = -FLT_MAX / 2.0f;
if (tid < n_blocks) {
device const float *ms =
(device const float *)(partial_ms +
((uint64_t)tid * args.n_head + head) * 2u * sizeof(float));
local_m = ms[1] > 0.0f ? ms[0] : -FLT_MAX / 2.0f;
}
red[tid] = local_m;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) red[tid] = max(red[tid], red[tid + step]);
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float max_m = red[0];
float local_denom = 0.0f;
if (tid < n_blocks) {
device const float *ms =
(device const float *)(partial_ms +
((uint64_t)tid * args.n_head + head) * 2u * sizeof(float));
const float s = ms[1];
const float e = s > 0.0f ? exp(ms[0] - max_m) : 0.0f;
block_scale[tid] = e;
local_denom = s * e;
}
red[tid] = local_denom;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) red[tid] += red[tid + step];
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float denom = max(red[0], 1.0e-20f);
for (uint j = tid; j < args.kv_lora_dim; j += nth) {
float acc = 0.0f;
for (uint b = 0u; b < n_blocks; b++) {
device const float *src =
(device const float *)(partial_lora +
((uint64_t)b * args.n_head + head) *
args.kv_lora_dim * sizeof(float));
acc += src[j] * block_scale[b];
}
lora_sum[j] = acc / denom;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
device float *out =
(device float *)(heads + (uint64_t)head * args.value_dim * sizeof(float));
if (args.value_type == DS4_METAL_GGUF_Q4_K &&
(args.kv_lora_dim & 255u) == 0u) {
/* Lane-split Q4_K value project: one simdgroup per output row with
* coalesced per-lane superblock reads; the per-thread scalar
* fallback below walks the 144-byte rows one element at a time. */
const uint vp_sg = tid >> 5u;
const uint vp_lane = tid & 31u;
const uint vp_nsg = nth >> 5u;
for (uint d = vp_sg; d < args.value_dim; d += vp_nsg) {
device const char *row =
value_weight + ((uint64_t)head * args.value_dim + d) * args.value_row_bytes;
const float part = glm_q4_K_dot_row_lane_f32(row, lora_sum,
args.kv_lora_dim,
(ushort)vp_lane);
const float sum = simd_sum(part);
if (vp_lane == 0u) {
out[d] = sum;
}
}
} else {
for (uint d = tid; d < args.value_dim; d += nth) {
device const char *row =
value_weight + ((uint64_t)head * args.value_dim + d) * args.value_row_bytes;
out[d] = glm_quant_dot_row_tg_f32(args.value_type, row, lora_sum, args.kv_lora_dim);
}
}
}
kernel void kernel_glm_attention_indexed_decode_split_group8_reduce(
constant ds4_metal_args_glm_attention_indexed_decode_split & args,
device const char *partial_lora,
device const char *partial_ms,
device const char *value_weight,
device char *heads,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
kernel_glm_attention_indexed_decode_split_group8_reduce_impl<0>(
args, partial_lora, partial_ms, value_weight, heads, scratch,
tid, ntg_u, tgpig);
}
kernel void kernel_glm_attention_indexed_decode_split_group8_reduce16(
constant ds4_metal_args_glm_attention_indexed_decode_split & args,
device const char *partial_lora,
device const char *partial_ms,
device const char *value_weight,
device char *heads,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
kernel_glm_attention_indexed_decode_split_group8_reduce_impl<16>(
args, partial_lora, partial_ms, value_weight, heads, scratch,
tid, ntg_u, tgpig);
}
kernel void kernel_glm_attention_indexed_decode(
constant ds4_metal_args_glm_attention_indexed_decode & args,
device const char *q,
device const char *qk_low,
device const char *kv_lora_cache,
device const char *k_rope_cache,
device const char *value_weight,
device const uint32_t *selected,
device char *heads,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint head = tgpig.x;
if (head >= args.n_head || args.n_selected == 0u) return;
const uint nth = ntg_u.x;
const uint qk_dim = args.qk_nope + args.qk_rope;
threadgroup float *red = scratch;
threadgroup float *scores = scratch + 256u;
threadgroup float *lora_sum = scores + args.n_selected;
device const float *qh =
(device const float *)(q + (uint64_t)head * qk_dim * sizeof(float));
device const float *low =
(device const float *)(qk_low + (uint64_t)head * args.kv_lora_dim * sizeof(float));
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)args.qk_rope,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
if (args.cache_f16 != 0u) {
float local_max = -INFINITY;
for (uint s = tid; s < args.n_selected; s += nth) {
const uint row = selected[s];
float score = -INFINITY;
if (row < args.cache_cap) {
float dotv = 0.0f;
const uint64_t lora_base = (uint64_t)row * args.kv_lora_dim;
uint j = 0;
for (; j + 3u < args.kv_lora_dim; j += 4u) {
device const half4 *kv4 =
(device const half4 *)((device const half *)kv_lora_cache + lora_base + j);
device const float4 *low4 =
(device const float4 *)(low + j);
const float4 kv = (float4)(*kv4);
const float4 qv = *low4;
dotv += qv.x * kv.x + qv.y * kv.y +
qv.z * kv.z + qv.w * kv.w;
}
if (j < args.kv_lora_dim) {
for (; j < args.kv_lora_dim; j++) {
const float kv = glm_cache_load_f16_only(kv_lora_cache,
lora_base + j);
dotv += low[j] * kv;
}
}
const uint64_t rope_base = (uint64_t)row * args.qk_rope;
for (uint r = 0; r < args.qk_rope; r += 2u) {
const float2 y = glm_cache_load_rotated_rope_pair_f16_only(k_rope_cache,
rope_base,
r,
row,
args.qk_rope,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
dotv += qh[args.qk_nope + r] * y.x +
qh[args.qk_nope + r + 1u] * y.y;
}
score = dotv * args.scale;
}
scores[s] = score;
local_max = max(local_max, score);
}
red[tid] = local_max;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) red[tid] = max(red[tid], red[tid + step]);
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float max_score = red[0];
float local_sum = 0.0f;
for (uint s = tid; s < args.n_selected; s += nth) {
const float w = exp(scores[s] - max_score);
scores[s] = w;
local_sum += w;
}
red[tid] = local_sum;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) red[tid] += red[tid + step];
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float denom = max(red[0], 1.0e-20f);
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint j0 = tid * 2u; j0 < args.kv_lora_dim; j0 += nth * 2u) {
const uint j1 = j0 + 1u;
const bool use_j1 = j1 < args.kv_lora_dim;
float acc0 = 0.0f;
float acc1 = 0.0f;
for (uint s = 0; s < args.n_selected; s++) {
const uint row = selected[s];
if (row < args.cache_cap) {
const uint64_t row_base = (uint64_t)row * args.kv_lora_dim;
const float w = scores[s];
if (use_j1) {
device const half2 *kv2 =
(device const half2 *)((device const half *)kv_lora_cache + row_base + j0);
const float2 kv = (float2)(*kv2);
acc0 += w * kv.x;
acc1 += w * kv.y;
} else {
const float kv0 = glm_cache_load_f16_only(kv_lora_cache,
row_base + j0);
acc0 += w * kv0;
}
}
}
lora_sum[j0] = acc0 / denom;
if (use_j1) lora_sum[j1] = acc1 / denom;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
device float *out =
(device float *)(heads + (uint64_t)head * args.value_dim * sizeof(float));
for (uint d = tid; d < args.value_dim; d += nth) {
device const char *row =
value_weight + ((uint64_t)head * args.value_dim + d) * args.value_row_bytes;
out[d] = glm_quant_dot_row_tg_f32(args.value_type, row, lora_sum, args.kv_lora_dim);
}
return;
}
float local_max = -INFINITY;
for (uint s = tid; s < args.n_selected; s += nth) {
const uint row = selected[s];
float score = -INFINITY;
if (row < args.cache_cap) {
float dotv = 0.0f;
const uint64_t lora_base = (uint64_t)row * args.kv_lora_dim;
for (uint j = 0; j < args.kv_lora_dim; j++) {
const float kv = glm_cache_load_f32_or_f16(kv_lora_cache,
lora_base + j,
args.cache_f16);
dotv += low[j] * kv;
}
const uint64_t rope_base = (uint64_t)row * args.qk_rope;
for (uint r = 0; r < args.qk_rope; r += 2u) {
const float2 y = glm_cache_load_rotated_rope_pair(k_rope_cache,
rope_base,
r,
row,
args.qk_rope,
args.cache_f16,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
dotv += qh[args.qk_nope + r] * y.x +
qh[args.qk_nope + r + 1u] * y.y;
}
score = dotv * args.scale;
}
scores[s] = score;
local_max = max(local_max, score);
}
red[tid] = local_max;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) red[tid] = max(red[tid], red[tid + step]);
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float max_score = red[0];
float local_sum = 0.0f;
for (uint s = tid; s < args.n_selected; s += nth) {
const float w = exp(scores[s] - max_score);
scores[s] = w;
local_sum += w;
}
red[tid] = local_sum;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) red[tid] += red[tid + step];
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float denom = max(red[0], 1.0e-20f);
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint j0 = tid; j0 < args.kv_lora_dim; j0 += nth * 2u) {
const uint j1 = j0 + nth;
const bool use_j1 = j1 < args.kv_lora_dim;
float acc0 = 0.0f;
float acc1 = 0.0f;
for (uint s = 0; s < args.n_selected; s++) {
const uint row = selected[s];
if (row < args.cache_cap) {
const uint64_t row_base = (uint64_t)row * args.kv_lora_dim;
const float w = scores[s];
const float kv0 = glm_cache_load_f32_or_f16(kv_lora_cache,
row_base + j0,
args.cache_f16);
acc0 += w * kv0;
if (use_j1) {
const float kv1 = glm_cache_load_f32_or_f16(kv_lora_cache,
row_base + j1,
args.cache_f16);
acc1 += w * kv1;
}
}
}
lora_sum[j0] = acc0 / denom;
if (use_j1) lora_sum[j1] = acc1 / denom;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
device float *out =
(device float *)(heads + (uint64_t)head * args.value_dim * sizeof(float));
if (args.value_type == DS4_METAL_GGUF_Q4_K &&
(args.kv_lora_dim & 255u) == 0u) {
/* Lane-split Q4_K value project: one simdgroup per output row with
* coalesced per-lane superblock reads; the per-thread scalar
* fallback below walks the 144-byte rows one element at a time. */
const uint vp_sg = tid >> 5u;
const uint vp_lane = tid & 31u;
const uint vp_nsg = nth >> 5u;
for (uint d = vp_sg; d < args.value_dim; d += vp_nsg) {
device const char *row =
value_weight + ((uint64_t)head * args.value_dim + d) * args.value_row_bytes;
const float part = glm_q4_K_dot_row_lane_f32(row, lora_sum,
args.kv_lora_dim,
(ushort)vp_lane);
const float sum = simd_sum(part);
if (vp_lane == 0u) {
out[d] = sum;
}
}
} else {
for (uint d = tid; d < args.value_dim; d += nth) {
device const char *row =
value_weight + ((uint64_t)head * args.value_dim + d) * args.value_row_bytes;
out[d] = glm_quant_dot_row_tg_f32(args.value_type, row, lora_sum, args.kv_lora_dim);
}
}
}
kernel void kernel_glm_attention_indexed_batch(
constant ds4_metal_args_glm_attention_indexed_batch & args,
device const char *q,
device const char *qk_low,
device const char *kv_lora_cache,
device const char *k_rope_cache,
device const char *value_weight,
device const uint32_t *selected,
device char *heads,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint head = tgpig.x;
const uint token = tgpig.y;
if (head >= args.n_head || token >= args.n_tokens || args.n_selected == 0u) return;
const uint nth = ntg_u.x;
const uint qk_dim = args.qk_nope + args.qk_rope;
const uint64_t q_token_stride = (uint64_t)args.n_head * qk_dim * sizeof(float);
const uint64_t low_token_stride = (uint64_t)args.n_head * args.kv_lora_dim * sizeof(float);
const uint64_t heads_token_stride = (uint64_t)args.n_head * args.value_dim * sizeof(float);
threadgroup float *red = scratch;
threadgroup float *scores = scratch + 256u;
threadgroup float *lora_sum = scores + args.n_selected;
device const float *qh =
(device const float *)(q +
(uint64_t)token * q_token_stride +
(uint64_t)head * qk_dim * sizeof(float));
device const float *low =
(device const float *)(qk_low +
(uint64_t)token * low_token_stride +
(uint64_t)head * args.kv_lora_dim * sizeof(float));
device const uint32_t *token_selected =
selected + (uint64_t)token * args.n_selected;
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)args.qk_rope,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
float local_max = -INFINITY;
for (uint s = tid; s < args.n_selected; s += nth) {
const uint row = token_selected[s];
float score = -INFINITY;
if (row < args.cache_cap) {
float dotv = 0.0f;
const uint64_t lora_base = (uint64_t)row * args.kv_lora_dim;
for (uint j = 0; j < args.kv_lora_dim; j++) {
const float kv = glm_cache_load_f32_or_f16(kv_lora_cache,
lora_base + j,
args.cache_f16);
dotv += low[j] * kv;
}
const uint64_t rope_base = (uint64_t)row * args.qk_rope;
for (uint r = 0; r < args.qk_rope; r += 2u) {
const float2 y = glm_cache_load_rotated_rope_pair(k_rope_cache,
rope_base,
r,
row,
args.qk_rope,
args.cache_f16,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
dotv += qh[args.qk_nope + r] * y.x +
qh[args.qk_nope + r + 1u] * y.y;
}
score = dotv * args.scale;
}
scores[s] = score;
local_max = max(local_max, score);
}
red[tid] = local_max;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) red[tid] = max(red[tid], red[tid + step]);
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float max_score = red[0];
float local_sum = 0.0f;
for (uint s = tid; s < args.n_selected; s += nth) {
const float w = exp(scores[s] - max_score);
scores[s] = w;
local_sum += w;
}
red[tid] = local_sum;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) red[tid] += red[tid + step];
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float denom = max(red[0], 1.0e-20f);
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint j = tid; j < args.kv_lora_dim; j += nth) {
float acc = 0.0f;
for (uint s = 0; s < args.n_selected; s++) {
const uint row = token_selected[s];
if (row < args.cache_cap) {
const float kv = glm_cache_load_f32_or_f16(kv_lora_cache,
(uint64_t)row * args.kv_lora_dim + j,
args.cache_f16);
acc += scores[s] * kv;
}
}
lora_sum[j] = acc / denom;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
device float *out =
(device float *)(heads +
(uint64_t)token * heads_token_stride +
(uint64_t)head * args.value_dim * sizeof(float));
if (args.value_type == DS4_METAL_GGUF_Q4_K &&
(args.kv_lora_dim & 255u) == 0u) {
/* Lane-split Q4_K value project: one simdgroup per output row with
* coalesced per-lane superblock reads; the per-thread scalar
* fallback below walks the 144-byte rows one element at a time. */
const uint vp_sg = tid >> 5u;
const uint vp_lane = tid & 31u;
const uint vp_nsg = nth >> 5u;
for (uint d = vp_sg; d < args.value_dim; d += vp_nsg) {
device const char *row =
value_weight + ((uint64_t)head * args.value_dim + d) * args.value_row_bytes;
const float part = glm_q4_K_dot_row_lane_f32(row, lora_sum,
args.kv_lora_dim,
(ushort)vp_lane);
const float sum = simd_sum(part);
if (vp_lane == 0u) {
out[d] = sum;
}
}
} else {
for (uint d = tid; d < args.value_dim; d += nth) {
device const char *row =
value_weight + ((uint64_t)head * args.value_dim + d) * args.value_row_bytes;
out[d] = glm_quant_dot_row_tg_f32(args.value_type, row, lora_sum, args.kv_lora_dim);
}
}
}
kernel void kernel_glm_attention_indexed_batch_group2(
constant ds4_metal_args_glm_attention_indexed_batch & args,
device const char *q,
device const char *qk_low,
device const char *kv_lora_cache,
device const char *k_rope_cache,
device const char *value_weight,
device const uint32_t *selected,
device char *heads,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_index_in_threadgroup]],
ushort3 ntg_u [[threads_per_threadgroup]],
uint3 tgpig [[threadgroup_position_in_grid]]) {
const uint token = tgpig.y;
if (token >= args.n_tokens || args.n_selected == 0u) return;
const uint nth = ntg_u.x;
const uint head0 = tgpig.x * 2u;
const uint head1 = head0 + 1u;
const bool valid0 = head0 < args.n_head;
const bool valid1 = head1 < args.n_head;
if (!valid0 && !valid1) return;
const uint qk_dim = args.qk_nope + args.qk_rope;
const uint64_t q_token_stride = (uint64_t)args.n_head * qk_dim * sizeof(float);
const uint64_t low_token_stride = (uint64_t)args.n_head * args.kv_lora_dim * sizeof(float);
const uint64_t heads_token_stride = (uint64_t)args.n_head * args.value_dim * sizeof(float);
threadgroup float *red0 = scratch;
threadgroup float *red1 = red0 + 256u;
threadgroup float *scores0 = red1 + 256u;
threadgroup float *scores1 = scores0 + args.n_selected;
threadgroup float *lora0 = scores1 + args.n_selected;
threadgroup float *lora1 = lora0 + args.kv_lora_dim;
device const float *qh0 =
(device const float *)(q +
(uint64_t)token * q_token_stride +
(uint64_t)head0 * qk_dim * sizeof(float));
device const float *qh1 =
(device const float *)(q +
(uint64_t)token * q_token_stride +
(uint64_t)(valid1 ? head1 : head0) * qk_dim * sizeof(float));
device const float *low0 =
(device const float *)(qk_low +
(uint64_t)token * low_token_stride +
(uint64_t)head0 * args.kv_lora_dim * sizeof(float));
device const float *low1 =
(device const float *)(qk_low +
(uint64_t)token * low_token_stride +
(uint64_t)(valid1 ? head1 : head0) * args.kv_lora_dim * sizeof(float));
device const uint32_t *token_selected =
selected + (uint64_t)token * args.n_selected;
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)args.qk_rope,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
float local_max0 = -INFINITY;
float local_max1 = -INFINITY;
for (uint s = tid; s < args.n_selected; s += nth) {
const uint row = token_selected[s];
float score0 = -INFINITY;
float score1 = -INFINITY;
if (row < args.cache_cap) {
float dot0 = 0.0f;
float dot1 = 0.0f;
const uint64_t lora_base = (uint64_t)row * args.kv_lora_dim;
for (uint j = 0; j < args.kv_lora_dim; j++) {
const float kv = glm_cache_load_f32_or_f16(kv_lora_cache,
lora_base + j,
args.cache_f16);
dot0 += low0[j] * kv;
if (valid1) dot1 += low1[j] * kv;
}
const uint64_t rope_base = (uint64_t)row * args.qk_rope;
for (uint r = 0; r < args.qk_rope; r += 2u) {
const float2 y = glm_cache_load_rotated_rope_pair(k_rope_cache,
rope_base,
r,
row,
args.qk_rope,
args.cache_f16,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
dot0 += qh0[args.qk_nope + r] * y.x +
qh0[args.qk_nope + r + 1u] * y.y;
if (valid1) {
dot1 += qh1[args.qk_nope + r] * y.x +
qh1[args.qk_nope + r + 1u] * y.y;
}
}
score0 = dot0 * args.scale;
if (valid1) score1 = dot1 * args.scale;
}
scores0[s] = score0;
scores1[s] = score1;
local_max0 = max(local_max0, score0);
local_max1 = max(local_max1, score1);
}
red0[tid] = local_max0;
red1[tid] = local_max1;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) {
red0[tid] = max(red0[tid], red0[tid + step]);
red1[tid] = max(red1[tid], red1[tid + step]);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float max_score0 = red0[0];
const float max_score1 = red1[0];
float local_sum0 = 0.0f;
float local_sum1 = 0.0f;
for (uint s = tid; s < args.n_selected; s += nth) {
const float w0 = (max_score0 > -INFINITY) ? exp(scores0[s] - max_score0) : 0.0f;
const float w1 = (valid1 && max_score1 > -INFINITY) ? exp(scores1[s] - max_score1) : 0.0f;
scores0[s] = w0;
scores1[s] = w1;
local_sum0 += w0;
local_sum1 += w1;
}
red0[tid] = local_sum0;
red1[tid] = local_sum1;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint step = nth >> 1; step > 0; step >>= 1) {
if (tid < step) {
red0[tid] += red0[tid + step];
red1[tid] += red1[tid + step];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float denom0 = max(red0[0], 1.0e-20f);
const float denom1 = max(red1[0], 1.0e-20f);
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint j = tid; j < args.kv_lora_dim; j += nth) {
float acc0 = 0.0f;
float acc1 = 0.0f;
for (uint s = 0; s < args.n_selected; s++) {
const uint row = token_selected[s];
if (row < args.cache_cap) {
const float kv = glm_cache_load_f32_or_f16(kv_lora_cache,
(uint64_t)row * args.kv_lora_dim + j,
args.cache_f16);
acc0 += scores0[s] * kv;
if (valid1) acc1 += scores1[s] * kv;
}
}
lora0[j] = acc0 / denom0;
if (valid1) lora1[j] = acc1 / denom1;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint d = tid; d < args.value_dim; d += nth) {
device float *out0 =
(device float *)(heads +
(uint64_t)token * heads_token_stride +
(uint64_t)head0 * args.value_dim * sizeof(float));
device const char *row0 =
value_weight + ((uint64_t)head0 * args.value_dim + d) * args.value_row_bytes;
out0[d] = glm_quant_dot_row_tg_f32(args.value_type, row0, lora0, args.kv_lora_dim);
if (valid1) {
device float *out1 =
(device float *)(heads +
(uint64_t)token * heads_token_stride +
(uint64_t)head1 * args.value_dim * sizeof(float));
device const char *row1 =
value_weight + ((uint64_t)head1 * args.value_dim + d) * args.value_row_bytes;
out1[d] = glm_quant_dot_row_tg_f32(args.value_type, row1, lora1, args.kv_lora_dim);
}
}
}
template <bool assume_valid_rows, bool assume_valid_heads>
kernel void kernel_glm_attention_indexed_batch_lora_group8_vec_impl(
constant ds4_metal_args_glm_attention_indexed_batch & args,
device const char *q,
device const char *qk_low,
device const char *kv_lora_cache,
device const char *k_rope_cache,
device const uint32_t *selected,
device char *lora_out,
threadgroup half4 *scratch [[threadgroup(0)]],
uint3 tgpig [[threadgroup_position_in_grid]],
ushort tid_u [[thread_index_in_threadgroup]],
ushort lane_u [[thread_index_in_simdgroup]],
ushort sg_u [[simdgroup_index_in_threadgroup]]) {
constexpr uint group_heads = 8u;
constexpr uint stage_rows = 16u;
const uint token = tgpig.y;
const uint tid = (uint)tid_u;
const uint lane = (uint)lane_u;
const uint head_in_group = (uint)sg_u;
const uint head = tgpig.x * group_heads + head_in_group + args.head_base;
if (token >= args.n_tokens ||
args.n_selected == 0u ||
args.cache_f16 == 0u ||
args.kv_lora_dim != 512u ||
args.qk_rope != 64u) {
return;
}
const bool valid_head = assume_valid_heads || head < args.n_head;
const uint safe_head = valid_head ? head : 0u;
const uint kv_vecs = args.kv_lora_dim >> 2;
const uint rope_vecs = args.qk_rope >> 2;
const uint qk_dim = args.qk_nope + args.qk_rope;
const uint64_t q_token_stride = (uint64_t)args.n_head * qk_dim * sizeof(float);
const uint64_t low_token_stride =
(uint64_t)args.n_head * args.kv_lora_dim * sizeof(float);
threadgroup half4 *kv_shared = scratch;
threadgroup float4 *rope_shared =
(threadgroup float4 *)(kv_shared + stage_rows * kv_vecs);
device const float *qh =
(device const float *)(q +
(uint64_t)token * q_token_stride +
(uint64_t)safe_head * qk_dim * sizeof(float));
device const float4 *low4 =
(device const float4 *)(qk_low +
(uint64_t)token * low_token_stride +
(uint64_t)safe_head * args.kv_lora_dim * sizeof(float));
device const uint32_t *token_selected =
selected + (uint64_t)token * args.n_selected;
float4 low0 = 0.0f;
float4 low1 = 0.0f;
float4 low2 = 0.0f;
float4 low3 = 0.0f;
float4 qrope = 0.0f;
if (valid_head) {
low0 = low4[lane + 0u];
low1 = low4[lane + 32u];
low2 = low4[lane + 64u];
low3 = low4[lane + 96u];
if (lane < rope_vecs) {
qrope = *((device const float4 *)(qh + args.qk_nope + lane * 4u));
}
}
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)args.qk_rope,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
float M = -FLT_MAX / 2.0f;
float S = 0.0f;
float4 o0 = 0.0f;
float4 o1 = 0.0f;
float4 o2 = 0.0f;
float4 o3 = 0.0f;
for (uint base = 0u; base < args.n_selected; base += stage_rows) {
const uint rows = min(stage_rows, args.n_selected - base);
for (uint off = tid; off < rows * kv_vecs; off += 256u) {
const uint rr = off / kv_vecs;
const uint vv = off - rr * kv_vecs;
const uint row = token_selected[base + rr];
const bool valid_row = assume_valid_rows || row < args.cache_cap;
if (valid_row) {
device const half4 *src =
(device const half4 *)((device const half *)kv_lora_cache +
(uint64_t)row * args.kv_lora_dim);
kv_shared[off] = src[vv];
} else {
kv_shared[off] = half4(half(0.0f));
}
}
for (uint off = tid; off < rows * rope_vecs; off += 256u) {
const uint rr = off / rope_vecs;
const uint vv = off - rr * rope_vecs;
const uint r = vv * 4u;
const uint row = token_selected[base + rr];
const bool valid_row = assume_valid_rows || row < args.cache_cap;
if (valid_row) {
const uint64_t rope_base = (uint64_t)row * args.qk_rope;
const float2 y0 =
glm_cache_load_rotated_rope_pair_f16_only(k_rope_cache,
rope_base,
r,
row,
args.qk_rope,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
const float2 y1 =
glm_cache_load_rotated_rope_pair_f16_only(k_rope_cache,
rope_base,
r + 2u,
row,
args.qk_rope,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
rope_shared[off] = float4(y0.x, y0.y, y1.x, y1.y);
} else {
rope_shared[off] = float4(0.0f);
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint rr = 0u; rr < rows; rr++) {
const uint row = token_selected[base + rr];
const bool valid_row = assume_valid_rows || row < args.cache_cap;
threadgroup const half4 *kv_row = kv_shared + rr * kv_vecs;
threadgroup const float4 *rope_row = rope_shared + rr * rope_vecs;
float partial = 0.0f;
if (valid_head && valid_row) {
partial += dot(low0, (float4)kv_row[lane + 0u]);
partial += dot(low1, (float4)kv_row[lane + 32u]);
partial += dot(low2, (float4)kv_row[lane + 64u]);
partial += dot(low3, (float4)kv_row[lane + 96u]);
if (lane < rope_vecs) {
partial += dot(qrope, rope_row[lane]);
}
}
const float sum = simd_sum(partial);
const float score =
(valid_head && valid_row) ? sum * args.scale : -FLT_MAX / 2.0f;
if (valid_head && valid_row) {
const float new_m = max(M, score);
const float old_scale = exp(M - new_m);
const float row_scale = exp(score - new_m);
o0 = o0 * old_scale + (float4)kv_row[lane + 0u] * row_scale;
o1 = o1 * old_scale + (float4)kv_row[lane + 32u] * row_scale;
o2 = o2 * old_scale + (float4)kv_row[lane + 64u] * row_scale;
o3 = o3 * old_scale + (float4)kv_row[lane + 96u] * row_scale;
S = S * old_scale + row_scale;
M = new_m;
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (valid_head) {
const float inv_s = S > 0.0f ? 1.0f / S : 0.0f;
device float4 *out4 =
(device float4 *)(lora_out +
((uint64_t)token * args.n_head + head) *
args.kv_lora_dim * sizeof(float));
out4[lane + 0u] = o0 * inv_s;
out4[lane + 32u] = o1 * inv_s;
out4[lane + 64u] = o2 * inv_s;
out4[lane + 96u] = o3 * inv_s;
}
}
typedef decltype(kernel_glm_attention_indexed_batch_lora_group8_vec_impl<false, false>)
glm_attention_indexed_batch_lora_group8_vec_t;
template [[host_name("kernel_glm_attention_indexed_batch_lora_group8_vec")]]
kernel glm_attention_indexed_batch_lora_group8_vec_t
kernel_glm_attention_indexed_batch_lora_group8_vec_impl<false, false>;
template [[host_name("kernel_glm_attention_indexed_batch_lora_group8_vec_valid")]]
kernel glm_attention_indexed_batch_lora_group8_vec_t
kernel_glm_attention_indexed_batch_lora_group8_vec_impl<true, false>;
template [[host_name("kernel_glm_attention_indexed_batch_lora_group8_vec_valid_fullheads")]]
kernel glm_attention_indexed_batch_lora_group8_vec_t
kernel_glm_attention_indexed_batch_lora_group8_vec_impl<true, true>;
template <bool assume_valid_heads>
kernel void kernel_glm_attention_indexed_batch_lora_group8_vec_causal_impl(
constant ds4_metal_args_glm_attention_indexed_batch & args,
device const char *q,
device const char *qk_low,
device const char *kv_lora_cache,
device const char *k_rope_cache,
device char *lora_out,
threadgroup half4 *scratch [[threadgroup(0)]],
uint3 tgpig [[threadgroup_position_in_grid]],
ushort tid_u [[thread_index_in_threadgroup]],
ushort lane_u [[thread_index_in_simdgroup]],
ushort sg_u [[simdgroup_index_in_threadgroup]]) {
constexpr uint group_heads = 8u;
constexpr uint stage_rows = 16u;
const uint token = tgpig.y;
const uint tid = (uint)tid_u;
const uint lane = (uint)lane_u;
const uint head_in_group = (uint)sg_u;
const uint head = tgpig.x * group_heads + head_in_group + args.head_base;
if (token >= args.n_tokens ||
args.n_selected == 0u ||
args.cache_f16 == 0u ||
args.kv_lora_dim != 512u ||
args.qk_rope != 64u) {
return;
}
const uint visible = min(args.n_selected, args.pos0 + token + 1u);
if (visible == 0u) return;
const bool valid_head = assume_valid_heads || head < args.n_head;
const uint safe_head = valid_head ? head : 0u;
const uint kv_vecs = args.kv_lora_dim >> 2;
const uint rope_vecs = args.qk_rope >> 2;
const uint qk_dim = args.qk_nope + args.qk_rope;
const uint64_t q_token_stride = (uint64_t)args.n_head * qk_dim * sizeof(float);
const uint64_t low_token_stride =
(uint64_t)args.n_head * args.kv_lora_dim * sizeof(float);
threadgroup half4 *kv_shared = scratch;
threadgroup float4 *rope_shared =
(threadgroup float4 *)(kv_shared + stage_rows * kv_vecs);
device const float *qh =
(device const float *)(q +
(uint64_t)token * q_token_stride +
(uint64_t)safe_head * qk_dim * sizeof(float));
device const float4 *low4 =
(device const float4 *)(qk_low +
(uint64_t)token * low_token_stride +
(uint64_t)safe_head * args.kv_lora_dim * sizeof(float));
float4 low0 = 0.0f;
float4 low1 = 0.0f;
float4 low2 = 0.0f;
float4 low3 = 0.0f;
float4 qrope = 0.0f;
if (valid_head) {
low0 = low4[lane + 0u];
low1 = low4[lane + 32u];
low2 = low4[lane + 64u];
low3 = low4[lane + 96u];
if (lane < rope_vecs) {
qrope = *((device const float4 *)(qh + args.qk_nope + lane * 4u));
}
}
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)args.qk_rope,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
float M = -FLT_MAX / 2.0f;
float S = 0.0f;
float4 o0 = 0.0f;
float4 o1 = 0.0f;
float4 o2 = 0.0f;
float4 o3 = 0.0f;
for (uint base = 0u; base < visible; base += stage_rows) {
const uint rows = min(stage_rows, visible - base);
for (uint off = tid; off < rows * kv_vecs; off += 256u) {
const uint rr = off / kv_vecs;
const uint vv = off - rr * kv_vecs;
const uint row = base + rr;
device const half4 *src =
(device const half4 *)((device const half *)kv_lora_cache +
(uint64_t)row * args.kv_lora_dim);
kv_shared[off] = src[vv];
}
for (uint off = tid; off < rows * rope_vecs; off += 256u) {
const uint rr = off / rope_vecs;
const uint vv = off - rr * rope_vecs;
const uint r = vv * 4u;
const uint row = base + rr;
const uint64_t rope_base = (uint64_t)row * args.qk_rope;
const float2 y0 =
glm_cache_load_rotated_rope_pair_f16_only(k_rope_cache,
rope_base,
r,
row,
args.qk_rope,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
const float2 y1 =
glm_cache_load_rotated_rope_pair_f16_only(k_rope_cache,
rope_base,
r + 2u,
row,
args.qk_rope,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
rope_shared[off] = float4(y0.x, y0.y, y1.x, y1.y);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint rr = 0u; rr < rows; rr++) {
threadgroup const half4 *kv_row = kv_shared + rr * kv_vecs;
threadgroup const float4 *rope_row = rope_shared + rr * rope_vecs;
float partial = 0.0f;
if (valid_head) {
partial += dot(low0, (float4)kv_row[lane + 0u]);
partial += dot(low1, (float4)kv_row[lane + 32u]);
partial += dot(low2, (float4)kv_row[lane + 64u]);
partial += dot(low3, (float4)kv_row[lane + 96u]);
if (lane < rope_vecs) {
partial += dot(qrope, rope_row[lane]);
}
}
const float sum = simd_sum(partial);
const float score = valid_head ? sum * args.scale : -FLT_MAX / 2.0f;
if (valid_head) {
const float new_m = max(M, score);
const float old_scale = exp(M - new_m);
const float row_scale = exp(score - new_m);
o0 = o0 * old_scale + (float4)kv_row[lane + 0u] * row_scale;
o1 = o1 * old_scale + (float4)kv_row[lane + 32u] * row_scale;
o2 = o2 * old_scale + (float4)kv_row[lane + 64u] * row_scale;
o3 = o3 * old_scale + (float4)kv_row[lane + 96u] * row_scale;
S = S * old_scale + row_scale;
M = new_m;
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (valid_head) {
const float inv_s = S > 0.0f ? 1.0f / S : 0.0f;
device float4 *out4 =
(device float4 *)(lora_out +
((uint64_t)token * args.n_head + head) *
args.kv_lora_dim * sizeof(float));
out4[lane + 0u] = o0 * inv_s;
out4[lane + 32u] = o1 * inv_s;
out4[lane + 64u] = o2 * inv_s;
out4[lane + 96u] = o3 * inv_s;
}
}
typedef decltype(kernel_glm_attention_indexed_batch_lora_group8_vec_causal_impl<false>)
glm_attention_indexed_batch_lora_group8_vec_causal_t;
template [[host_name("kernel_glm_attention_indexed_batch_lora_group8_vec_causal")]]
kernel glm_attention_indexed_batch_lora_group8_vec_causal_t
kernel_glm_attention_indexed_batch_lora_group8_vec_causal_impl<false>;
template [[host_name("kernel_glm_attention_indexed_batch_lora_group8_vec_causal_fullheads")]]
kernel glm_attention_indexed_batch_lora_group8_vec_causal_t
kernel_glm_attention_indexed_batch_lora_group8_vec_causal_impl<true>;
kernel void kernel_glm_attention_indexed_batch_group8(
constant ds4_metal_args_glm_attention_indexed_batch & args,
device const char *q,
device const char *qk_low,
device const char *kv_lora_cache,
device const char *k_rope_cache,
device const char *value_weight,
device const uint32_t *selected,
device char *heads,
threadgroup float *scratch [[threadgroup(0)]],
uint3 tgpig [[threadgroup_position_in_grid]],
ushort tid_u [[thread_index_in_threadgroup]],
ushort lane_u [[thread_index_in_simdgroup]],
ushort sg_u [[simdgroup_index_in_threadgroup]]) {
const uint token = tgpig.y;
if (token >= args.n_tokens || args.n_selected == 0u) return;
constexpr uint group_heads = 8u;
constexpr uint stage_rows = 8u;
const uint tid = (uint)tid_u;
const uint lane = (uint)lane_u;
const uint head_in_group = (uint)sg_u;
const uint head = tgpig.x * group_heads + head_in_group + args.head_base;
const bool valid_head = head < args.n_head;
const uint safe_head = valid_head ? head : 0u;
const uint qk_dim = args.qk_nope + args.qk_rope;
const uint64_t q_token_stride = (uint64_t)args.n_head * qk_dim * sizeof(float);
const uint64_t low_token_stride = (uint64_t)args.n_head * args.kv_lora_dim * sizeof(float);
const uint64_t heads_token_stride = (uint64_t)args.n_head * args.value_dim * sizeof(float);
threadgroup half *kv_shared = (threadgroup half *)scratch;
threadgroup half *rope_shared = kv_shared + stage_rows * args.kv_lora_dim;
threadgroup float *lora_sums =
(threadgroup float *)(rope_shared + stage_rows * args.qk_rope);
threadgroup float *head_lora = lora_sums + head_in_group * args.kv_lora_dim;
device const float *qh =
(device const float *)(q +
(uint64_t)token * q_token_stride +
(uint64_t)safe_head * qk_dim * sizeof(float));
device const float *low =
(device const float *)(qk_low +
(uint64_t)token * low_token_stride +
(uint64_t)safe_head * args.kv_lora_dim * sizeof(float));
device const uint32_t *token_selected =
selected + (uint64_t)token * args.n_selected;
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)args.qk_rope,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
constexpr uint max_low_cache = 16u;
constexpr uint max_qrope_cache = 4u;
const bool use_low_cache = args.kv_lora_dim <= max_low_cache * 32u;
const bool use_qrope_cache = args.qk_rope <= max_qrope_cache * 32u;
half low_cache[max_low_cache];
half qrope_cache[max_qrope_cache];
for (uint k = 0u; k < max_low_cache; k++) {
const uint j = lane + k * 32u;
low_cache[k] = (valid_head && use_low_cache && j < args.kv_lora_dim) ?
(half)low[j] : (half)0.0f;
}
for (uint k = 0u; k < max_qrope_cache; k++) {
const uint r = lane + k * 32u;
qrope_cache[k] = (valid_head && use_qrope_cache && r < args.qk_rope) ?
(half)qh[args.qk_nope + r] : (half)0.0f;
}
for (uint j = lane; j < args.kv_lora_dim; j += 32u) {
head_lora[j] = 0.0f;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
float M = -INFINITY;
float S = 0.0f;
for (uint base = 0u; base < args.n_selected; base += stage_rows) {
const uint rows = min(stage_rows, args.n_selected - base);
const uint kv_count = rows * args.kv_lora_dim;
const uint rope_pairs = args.qk_rope >> 1;
const uint rope_count = rows * rope_pairs;
for (uint idx = tid; idx < kv_count; idx += 256u) {
const uint rr = idx / args.kv_lora_dim;
const uint j = idx - rr * args.kv_lora_dim;
const uint row = token_selected[base + rr];
kv_shared[idx] = (row < args.cache_cap)
? (half)glm_cache_load_f32_or_f16(kv_lora_cache,
(uint64_t)row * args.kv_lora_dim + j,
args.cache_f16)
: (half)0.0f;
}
for (uint idx = tid; idx < rope_count; idx += 256u) {
const uint rr = idx / rope_pairs;
const uint pair = idx - rr * rope_pairs;
const uint r = pair * 2u;
const uint row = token_selected[base + rr];
threadgroup half *rope_row = rope_shared + rr * args.qk_rope;
if (row < args.cache_cap) {
const float2 y = glm_cache_load_rotated_rope_pair(k_rope_cache,
(uint64_t)row * args.qk_rope,
r,
row,
args.qk_rope,
args.cache_f16,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
rope_row[r] = (half)y.x;
rope_row[r + 1u] = (half)y.y;
} else {
rope_row[r] = (half)0.0f;
rope_row[r + 1u] = (half)0.0f;
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint rr = 0u; rr < rows; rr++) {
const uint row = token_selected[base + rr];
const bool valid_row = row < args.cache_cap;
float partial = 0.0f;
if (valid_head && valid_row) {
threadgroup const half *kv_row = kv_shared + rr * args.kv_lora_dim;
threadgroup const half *rope_row = rope_shared + rr * args.qk_rope;
if (use_low_cache) {
for (uint k = 0u; k < max_low_cache; k++) {
const uint j = lane + k * 32u;
if (j < args.kv_lora_dim) {
partial += (float)(low_cache[k] * kv_row[j]);
}
}
} else {
for (uint j = lane; j < args.kv_lora_dim; j += 32u) {
partial += low[j] * (float)kv_row[j];
}
}
if (use_qrope_cache) {
for (uint k = 0u; k < max_qrope_cache; k++) {
const uint r = lane + k * 32u;
if (r < args.qk_rope) {
partial += (float)(qrope_cache[k] * rope_row[r]);
}
}
} else {
for (uint r = lane; r < args.qk_rope; r += 32u) {
partial += qh[args.qk_nope + r] * (float)rope_row[r];
}
}
}
const float sum = simd_sum(partial);
const float score = (valid_head && valid_row) ? sum * args.scale : -INFINITY;
if (valid_head && valid_row) {
threadgroup const half *kv_row = kv_shared + rr * args.kv_lora_dim;
const float old_m = M;
const float new_m = max(M, score);
const float old_scale = (old_m == -INFINITY) ? 0.0f : exp(old_m - new_m);
const float row_scale = exp(score - new_m);
S = S * old_scale + row_scale;
for (uint j = lane; j < args.kv_lora_dim; j += 32u) {
head_lora[j] = head_lora[j] * old_scale + row_scale * (float)kv_row[j];
}
M = new_m;
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float inv_s = (valid_head && S > 0.0f) ? 1.0f / S : 0.0f;
for (uint j = lane; j < args.kv_lora_dim; j += 32u) {
head_lora[j] *= inv_s;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
if (valid_head) {
if (args.value_type == 1u) {
const uint64_t offset =
(uint64_t)token *
((uint64_t)args.n_head * args.kv_lora_dim * sizeof(float)) +
(uint64_t)head * args.kv_lora_dim * sizeof(float);
device float *out =
(device float *)(heads + offset);
for (uint j = lane; j < args.kv_lora_dim; j += 32u) {
out[j] = head_lora[j];
}
return;
}
device float *out =
(device float *)(heads +
(uint64_t)token * heads_token_stride +
(uint64_t)head * args.value_dim * sizeof(float));
for (uint d = lane; d < args.value_dim; d += 32u) {
device const char *row =
value_weight + ((uint64_t)head * args.value_dim + d) * args.value_row_bytes;
out[d] = glm_quant_dot_row_tg_f32(args.value_type, row, head_lora, args.kv_lora_dim);
}
}
}
kernel void kernel_glm_attention_indexed_batch_q2_group4(
constant ds4_metal_args_glm_attention_indexed_batch & args,
device const char *q,
device const char *qk_low,
device const char *kv_lora_cache,
device const char *k_rope_cache,
device const char *value_weight,
device const uint32_t *selected,
device char *heads,
threadgroup uint *scratch [[threadgroup(0)]],
uint3 tgpig [[threadgroup_position_in_grid]],
ushort tid_u [[thread_index_in_threadgroup]],
ushort lane_u [[thread_index_in_simdgroup]],
ushort sg_u [[simdgroup_index_in_threadgroup]]) {
const uint token0 = tgpig.y * 2u;
if (token0 >= args.n_tokens || args.n_selected == 0u) return;
constexpr uint group_heads = 4u;
constexpr uint stage_rows = 4u;
constexpr uint group_threads = 128u;
const uint token1 = token0 + 1u;
const bool valid1 = token1 < args.n_tokens;
const uint tid = (uint)tid_u;
const uint lane = (uint)lane_u;
const uint head_in_group = (uint)sg_u;
const uint head = tgpig.x * group_heads + head_in_group + args.head_base;
const bool valid_head = head < args.n_head;
const uint safe_head = valid_head ? head : 0u;
const uint qk_dim = args.qk_nope + args.qk_rope;
const uint64_t q_token_stride = (uint64_t)args.n_head * qk_dim * sizeof(float);
const uint64_t low_token_stride = (uint64_t)args.n_head * args.kv_lora_dim * sizeof(float);
const uint64_t heads_token_stride = (uint64_t)args.n_head * args.value_dim * sizeof(float);
const uint bit_words = (args.cache_cap + 31u) >> 5;
threadgroup atomic_uint *member_bits = (threadgroup atomic_uint *)scratch;
threadgroup half *kv_shared = (threadgroup half *)(scratch + bit_words);
threadgroup half *rope_shared = kv_shared + stage_rows * args.kv_lora_dim;
threadgroup float *lora_sums =
(threadgroup float *)(rope_shared + stage_rows * args.qk_rope);
threadgroup float *head_lora0 = lora_sums + head_in_group * args.kv_lora_dim;
threadgroup float *head_lora1 =
lora_sums + (group_heads + head_in_group) * args.kv_lora_dim;
const uint safe_token1 = valid1 ? token1 : token0;
device const float *qh0 =
(device const float *)(q +
(uint64_t)token0 * q_token_stride +
(uint64_t)safe_head * qk_dim * sizeof(float));
device const float *qh1 =
(device const float *)(q +
(uint64_t)safe_token1 * q_token_stride +
(uint64_t)safe_head * qk_dim * sizeof(float));
device const float *low0 =
(device const float *)(qk_low +
(uint64_t)token0 * low_token_stride +
(uint64_t)safe_head * args.kv_lora_dim * sizeof(float));
device const float *low1 =
(device const float *)(qk_low +
(uint64_t)safe_token1 * low_token_stride +
(uint64_t)safe_head * args.kv_lora_dim * sizeof(float));
device const uint32_t *selected0 = selected + (uint64_t)token0 * args.n_selected;
device const uint32_t *selected1 = selected + (uint64_t)safe_token1 * args.n_selected;
float corr_dims[2] = {0.0f, 0.0f};
if (args.ext_factor != 0.0f) {
glm_rope_yarn_corr_dims((int)args.qk_rope,
(int)args.n_ctx_orig,
args.freq_base,
args.beta_fast,
args.beta_slow,
corr_dims);
}
constexpr uint max_low_cache = 16u;
constexpr uint max_qrope_cache = 4u;
const bool use_low_cache = args.kv_lora_dim <= max_low_cache * 32u;
const bool use_qrope_cache = args.qk_rope <= max_qrope_cache * 32u;
half low_cache0[max_low_cache];
half low_cache1[max_low_cache];
half qrope_cache0[max_qrope_cache];
half qrope_cache1[max_qrope_cache];
for (uint k = 0u; k < max_low_cache; k++) {
const uint j = lane + k * 32u;
low_cache0[k] = (valid_head && use_low_cache && j < args.kv_lora_dim) ?
(half)low0[j] : (half)0.0f;
low_cache1[k] = (valid_head && valid1 && use_low_cache && j < args.kv_lora_dim) ?
(half)low1[j] : (half)0.0f;
}
for (uint k = 0u; k < max_qrope_cache; k++) {
const uint r = lane + k * 32u;
qrope_cache0[k] = (valid_head && use_qrope_cache && r < args.qk_rope) ?
(half)qh0[args.qk_nope + r] : (half)0.0f;
qrope_cache1[k] = (valid_head && valid1 && use_qrope_cache && r < args.qk_rope) ?
(half)qh1[args.qk_nope + r] : (half)0.0f;
}
for (uint j = lane; j < args.kv_lora_dim; j += 32u) {
head_lora0[j] = 0.0f;
if (valid1) head_lora1[j] = 0.0f;
}
for (uint i = tid; i < bit_words; i += group_threads) {
atomic_store_explicit(member_bits + i, 0u, memory_order_relaxed);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint s = tid; s < args.n_selected; s += group_threads) {
const uint row = selected0[s];
if (row < args.cache_cap) {
const uint mask = 1u << (row & 31u);
atomic_fetch_or_explicit(member_bits + (row >> 5), mask, memory_order_relaxed);
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
float M0 = -INFINITY;
float S0 = 0.0f;
float M1 = -INFINITY;
float S1 = 0.0f;
if (valid1) {
for (uint base = 0u; base < args.n_selected; base += stage_rows) {
const uint rows = min(stage_rows, args.n_selected - base);
const uint kv_count = rows * args.kv_lora_dim;
const uint rope_pairs = args.qk_rope >> 1;
const uint rope_count = rows * rope_pairs;
for (uint idx = tid; idx < kv_count; idx += 256u) {
const uint rr = idx / args.kv_lora_dim;
const uint j = idx - rr * args.kv_lora_dim;
const uint row = selected1[base + rr];
kv_shared[idx] = (row < args.cache_cap)
? (half)glm_cache_load_f32_or_f16(kv_lora_cache,
(uint64_t)row * args.kv_lora_dim + j,
args.cache_f16)
: (half)0.0f;
}
for (uint idx = tid; idx < rope_count; idx += 256u) {
const uint rr = idx / rope_pairs;
const uint pair = idx - rr * rope_pairs;
const uint r = pair * 2u;
const uint row = selected1[base + rr];
threadgroup half *rope_row = rope_shared + rr * args.qk_rope;
if (row < args.cache_cap) {
const float2 y = glm_cache_load_rotated_rope_pair(k_rope_cache,
(uint64_t)row * args.qk_rope,
r,
row,
args.qk_rope,
args.cache_f16,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
rope_row[r] = (half)y.x;
rope_row[r + 1u] = (half)y.y;
} else {
rope_row[r] = (half)0.0f;
rope_row[r + 1u] = (half)0.0f;
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint rr = 0u; rr < rows; rr++) {
const uint row = selected1[base + rr];
const bool valid_row = row < args.cache_cap;
const bool in_token0 = valid_row &&
((atomic_load_explicit(member_bits + (row >> 5),
memory_order_relaxed) &
(1u << (row & 31u))) != 0u);
threadgroup const half *kv_row = kv_shared + rr * args.kv_lora_dim;
threadgroup const half *rope_row = rope_shared + rr * args.qk_rope;
float partial0 = 0.0f;
float partial1 = 0.0f;
if (valid_head && valid_row) {
if (use_low_cache) {
for (uint k = 0u; k < max_low_cache; k++) {
const uint j = lane + k * 32u;
if (j < args.kv_lora_dim) {
const half kv = kv_row[j];
if (in_token0) partial0 += (float)(low_cache0[k] * kv);
partial1 += (float)(low_cache1[k] * kv);
}
}
} else {
for (uint j = lane; j < args.kv_lora_dim; j += 32u) {
const float kv = (float)kv_row[j];
if (in_token0) partial0 += low0[j] * kv;
partial1 += low1[j] * kv;
}
}
if (use_qrope_cache) {
for (uint k = 0u; k < max_qrope_cache; k++) {
const uint r = lane + k * 32u;
if (r < args.qk_rope) {
const half kv = rope_row[r];
if (in_token0) partial0 += (float)(qrope_cache0[k] * kv);
partial1 += (float)(qrope_cache1[k] * kv);
}
}
} else {
for (uint r = lane; r < args.qk_rope; r += 32u) {
const float kv = (float)rope_row[r];
if (in_token0) partial0 += qh0[args.qk_nope + r] * kv;
partial1 += qh1[args.qk_nope + r] * kv;
}
}
}
const float sum0 = simd_sum(partial0);
const float sum1 = simd_sum(partial1);
const float score0 = (valid_head && in_token0) ? sum0 * args.scale : -INFINITY;
const float score1 = (valid_head && valid_row) ? sum1 * args.scale : -INFINITY;
if (valid_head && in_token0) {
const float new_m = max(M0, score0);
const float old_scale = (M0 == -INFINITY) ? 0.0f : exp(M0 - new_m);
const float row_scale = exp(score0 - new_m);
S0 = S0 * old_scale + row_scale;
for (uint j = lane; j < args.kv_lora_dim; j += 32u) {
head_lora0[j] = head_lora0[j] * old_scale + row_scale * (float)kv_row[j];
}
M0 = new_m;
}
if (valid_head && valid_row) {
const float new_m = max(M1, score1);
const float old_scale = (M1 == -INFINITY) ? 0.0f : exp(M1 - new_m);
const float row_scale = exp(score1 - new_m);
S1 = S1 * old_scale + row_scale;
for (uint j = lane; j < args.kv_lora_dim; j += 32u) {
head_lora1[j] = head_lora1[j] * old_scale + row_scale * (float)kv_row[j];
}
M1 = new_m;
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
for (uint i = tid; i < bit_words; i += group_threads) {
atomic_store_explicit(member_bits + i, 0u, memory_order_relaxed);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint s = tid; s < args.n_selected; s += group_threads) {
const uint row = selected1[s];
if (row < args.cache_cap) {
const uint mask = 1u << (row & 31u);
atomic_fetch_or_explicit(member_bits + (row >> 5), mask, memory_order_relaxed);
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
for (uint base = 0u; base < args.n_selected; base += stage_rows) {
const uint rows = min(stage_rows, args.n_selected - base);
const uint kv_count = rows * args.kv_lora_dim;
const uint rope_pairs = args.qk_rope >> 1;
const uint rope_count = rows * rope_pairs;
for (uint idx = tid; idx < kv_count; idx += 256u) {
const uint rr = idx / args.kv_lora_dim;
const uint j = idx - rr * args.kv_lora_dim;
const uint row = selected0[base + rr];
kv_shared[idx] = (row < args.cache_cap)
? (half)glm_cache_load_f32_or_f16(kv_lora_cache,
(uint64_t)row * args.kv_lora_dim + j,
args.cache_f16)
: (half)0.0f;
}
for (uint idx = tid; idx < rope_count; idx += 256u) {
const uint rr = idx / rope_pairs;
const uint pair = idx - rr * rope_pairs;
const uint r = pair * 2u;
const uint row = selected0[base + rr];
threadgroup half *rope_row = rope_shared + rr * args.qk_rope;
if (row < args.cache_cap) {
const float2 y = glm_cache_load_rotated_rope_pair(k_rope_cache,
(uint64_t)row * args.qk_rope,
r,
row,
args.qk_rope,
args.cache_f16,
args.freq_base,
args.freq_scale,
args.ext_factor,
args.attn_factor,
corr_dims[0],
corr_dims[1]);
rope_row[r] = (half)y.x;
rope_row[r + 1u] = (half)y.y;
} else {
rope_row[r] = (half)0.0f;
rope_row[r + 1u] = (half)0.0f;
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint rr = 0u; rr < rows; rr++) {
const uint row = selected0[base + rr];
const bool valid_row = row < args.cache_cap;
const bool in_token1 = valid1 && valid_row &&
((atomic_load_explicit(member_bits + (row >> 5),
memory_order_relaxed) &
(1u << (row & 31u))) != 0u);
const bool take0 = valid_row && !in_token1;
threadgroup const half *kv_row = kv_shared + rr * args.kv_lora_dim;
threadgroup const half *rope_row = rope_shared + rr * args.qk_rope;
float partial0 = 0.0f;
if (valid_head && take0) {
if (use_low_cache) {
for (uint k = 0u; k < max_low_cache; k++) {
const uint j = lane + k * 32u;
if (j < args.kv_lora_dim) {
partial0 += (float)(low_cache0[k] * kv_row[j]);
}
}
} else {
for (uint j = lane; j < args.kv_lora_dim; j += 32u) {
partial0 += low0[j] * (float)kv_row[j];
}
}
if (use_qrope_cache) {
for (uint k = 0u; k < max_qrope_cache; k++) {
const uint r = lane + k * 32u;
if (r < args.qk_rope) {
partial0 += (float)(qrope_cache0[k] * rope_row[r]);
}
}
} else {
for (uint r = lane; r < args.qk_rope; r += 32u) {
partial0 += qh0[args.qk_nope + r] * (float)rope_row[r];
}
}
}
const float sum0 = simd_sum(partial0);
const float score0 = (valid_head && take0) ? sum0 * args.scale : -INFINITY;
if (valid_head && take0) {
const float new_m = max(M0, score0);
const float old_scale = (M0 == -INFINITY) ? 0.0f : exp(M0 - new_m);
const float row_scale = exp(score0 - new_m);
S0 = S0 * old_scale + row_scale;
for (uint j = lane; j < args.kv_lora_dim; j += 32u) {
head_lora0[j] = head_lora0[j] * old_scale + row_scale * (float)kv_row[j];
}
M0 = new_m;
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
const float inv_s0 = (valid_head && S0 > 0.0f) ? 1.0f / S0 : 0.0f;
const float inv_s1 = (valid_head && valid1 && S1 > 0.0f) ? 1.0f / S1 : 0.0f;
for (uint j = lane; j < args.kv_lora_dim; j += 32u) {
head_lora0[j] *= inv_s0;
if (valid1) head_lora1[j] *= inv_s1;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
if (valid_head) {
device float *out0 =
(device float *)(heads +
(uint64_t)token0 * heads_token_stride +
(uint64_t)head * args.value_dim * sizeof(float));
device float *out1 =
(device float *)(heads +
(uint64_t)safe_token1 * heads_token_stride +
(uint64_t)head * args.value_dim * sizeof(float));
for (uint d = lane; d < args.value_dim; d += 32u) {
device const char *row =
value_weight + ((uint64_t)head * args.value_dim + d) * args.value_row_bytes;
out0[d] = glm_quant_dot_row_tg_f32(args.value_type, row, head_lora0, args.kv_lora_dim);
if (valid1) {
out1[d] = glm_quant_dot_row_tg_f32(args.value_type, row, head_lora1, args.kv_lora_dim);
}
}
}
}
// GLM-5.2 decode router for one token. Selection uses sigmoid(logit)+bias,
// while route weights are normalized from the unbiased sigmoid probabilities.
kernel void kernel_glm_router_select_one(
constant ds4_metal_args_glm_router_select_one & args,
device const float *logits,
device const float *bias,
device int32_t *selected,
device float *weights,
device float *probs,
threadgroup float *scratch [[threadgroup(0)]],
uint token [[threadgroup_position_in_grid]],
uint tid [[thread_position_in_threadgroup]]) {
threadgroup float *sel_scores = scratch;
threadgroup int32_t *idx = (threadgroup int32_t *)(scratch + 256);
device const float *token_logits = logits + (uint64_t)token * args.n_expert;
device int32_t *token_selected = selected + (uint64_t)token * args.n_expert_used;
device float *token_weights = weights + (uint64_t)token * args.n_expert_used;
device float *token_probs = probs + (uint64_t)token * args.n_expert;
const uint n_expert = min(args.n_expert, 256u);
const bool active = tid < n_expert;
const float p = active ? ds4_glm_router_sigmoid(token_logits[tid]) : 0.0f;
if (active) token_probs[tid] = p;
sel_scores[tid] = active ? p + bias[tid] : -INFINITY;
idx[tid] = (int32_t)tid;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint k = 2; k <= 256; k <<= 1) {
for (uint j = k >> 1; j > 0; j >>= 1) {
const uint other = tid ^ j;
if (other > tid) {
const int32_t a = idx[tid];
const int32_t b = idx[other];
const bool descending = (tid & k) == 0;
const bool swap = descending
? ds4_glm_router_better(sel_scores, b, a)
: ds4_glm_router_better(sel_scores, a, b);
if (swap) {
idx[tid] = b;
idx[other] = a;
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
}
const uint k_used = min(args.n_expert_used, n_expert);
if (tid < k_used) {
token_selected[tid] = idx[tid];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
if (tid < k_used) {
float sum = 0.0f;
for (uint i = 0; i < k_used; i++) {
sum += token_probs[(uint)token_selected[i]];
}
sum = max(sum, 6.103515625e-5f);
token_weights[tid] = token_probs[(uint)token_selected[tid]] / sum * args.expert_weight_scale;
}
}
// Batched Flash-router weight finalization after selection is already known.
// Six active lanes deliberately match kernel_sum_rows_f32_f32's reduction
// topology. The denominator and divided weights cross threadgroup storage
// boundaries so division cannot be reassociated with the final scale.
kernel void kernel_dsv4_router_weights_batch(
constant float &scale,
device const float *probs,
device const int32_t *selected,
device float *weights,
threadgroup volatile float *scratch [[threadgroup(0)]],
uint row [[threadgroup_position_in_grid]],
ushort tid [[thread_position_in_threadgroup]],
ushort sgitg [[simdgroup_index_in_threadgroup]],
ushort tiisg [[thread_index_in_simdgroup]]) {
if (tid >= 6) return;
threadgroup volatile float *sum_scratch = scratch;
threadgroup volatile float *denom_scratch = scratch + 32;
threadgroup volatile float *div_scratch = scratch + 33;
const uint out_index = row * 6u + (uint)tid;
const int32_t expert = selected[out_index];
const float p = probs[row * 256u + (uint)expert];
// Keep this sequence identical to kernel_sum_rows_f32_f32 for width 6.
if (sgitg == 0) {
sum_scratch[tiisg] = 0.0f;
}
float sumf = 0.0f;
sumf += p;
sumf = simd_sum(sumf);
threadgroup_barrier(mem_flags::mem_threadgroup);
if (tiisg == 0) {
sum_scratch[sgitg] = sumf;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
sumf = sum_scratch[tiisg];
sumf = simd_sum(sumf);
if (tid == 0) {
denom_scratch[0] = clamp(sumf, 6.103515625e-5f, INFINITY);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
div_scratch[tid] = p / denom_scratch[0];
threadgroup_barrier(mem_flags::mem_threadgroup);
weights[out_index] = div_scratch[tid] * scale;
}
// Decode router selection for one token after the existing
// sqrt(softplus(logit)) probability kernel has run. Bias affects only top-k
// selection. Route-weight normalization deliberately stays in the old one-token
// kernel: even tiny denominator-order changes here are amplified by 43 MoE
// layers, so this kernel only replaces the selection work.
kernel void kernel_dsv4_router_finalize_one(
constant ds4_metal_args_dsv4_router_select_one & args,
device const float *probs,
device const float *bias,
device const int32_t *hash,
device const int32_t *tokens,
device int32_t *selected,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_position_in_threadgroup]]) {
if (tid >= 256) return;
threadgroup float *sel_scores = scratch;
threadgroup int32_t *idx = (threadgroup int32_t *)(scratch + 256);
const float p = probs[tid];
sel_scores[tid] = args.has_bias ? p + bias[tid] : p;
idx[tid] = (int32_t)tid;
threadgroup_barrier(mem_flags::mem_threadgroup);
if (args.hash_mode) {
if (tid == 0) {
const uint token = args.use_token_buffer ? (uint)tokens[0] : args.token;
const uint row = min(token, args.hash_rows - 1u);
device const int32_t *src = hash + row * 6u;
for (uint i = 0; i < 6; i++) {
selected[i] = src[i];
}
}
} else {
for (uint k = 2; k <= 256; k <<= 1) {
for (uint j = k >> 1; j > 0; j >>= 1) {
const uint other = tid ^ j;
if (other > tid) {
if ((tid & k) == 0) {
if (sel_scores[(uint)idx[tid]] < sel_scores[(uint)idx[other]]) {
const int32_t tmp = idx[tid];
idx[tid] = idx[other];
idx[other] = tmp;
}
} else {
if (sel_scores[(uint)idx[tid]] > sel_scores[(uint)idx[other]]) {
const int32_t tmp = idx[tid];
idx[tid] = idx[other];
idx[other] = tmp;
}
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
}
if (tid < 6) {
selected[tid] = idx[tid];
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
// M3 decode specialization for the non-hash one-token router. Scores and ids
// stay in registers. Intra-SIMD bitonic stages use shuffle-xor; the six stages
// that cross 32-lane SIMD groups exchange through alternating threadgroup
// banks. The next bank's publish barrier proves every prior-bank read finished;
// by the time a bank is reused two cross stages later, no reader can remain.
kernel void kernel_dsv4_router_finalize_one_simd(
constant ds4_metal_args_dsv4_router_select_one & args,
device const float *probs,
device const float *bias,
device const int32_t *hash,
device const int32_t *tokens,
device int32_t *selected,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_position_in_threadgroup]]) {
if (tid >= 256 || args.hash_mode) return;
(void)hash;
(void)tokens;
threadgroup float *score0_tg = scratch;
threadgroup int32_t *idx0_tg =
(threadgroup int32_t *)(scratch + 256);
threadgroup float *score1_tg = scratch + 512;
threadgroup int32_t *idx1_tg =
(threadgroup int32_t *)(scratch + 768);
const float p = probs[tid];
float score = args.has_bias ? p + bias[tid] : p;
int32_t idx = (int32_t)tid;
uint cross_stage = 0;
for (uint k = 2; k <= 256; k <<= 1) {
for (uint j = k >> 1; j > 0; j >>= 1) {
float peer_score;
int32_t peer_idx;
bool take_peer;
const bool lower = (tid & j) == 0;
const bool descending = (tid & k) == 0;
if (j < 32) {
peer_score = simd_shuffle_xor(score, (ushort)j);
peer_idx = simd_shuffle_xor(idx, (ushort)j);
take_peer = descending
? (lower ? score < peer_score : score > peer_score)
: (lower ? score > peer_score : score < peer_score);
if (take_peer) {
score = peer_score;
idx = peer_idx;
}
} else {
threadgroup float *score_tg =
(cross_stage & 1u) != 0u ? score1_tg : score0_tg;
threadgroup int32_t *idx_tg =
(cross_stage & 1u) != 0u ? idx1_tg : idx0_tg;
score_tg[tid] = score;
idx_tg[tid] = idx;
threadgroup_barrier(mem_flags::mem_threadgroup);
const uint other = tid ^ j;
peer_score = score_tg[other];
peer_idx = idx_tg[other];
take_peer = descending
? (lower ? score < peer_score : score > peer_score)
: (lower ? score > peer_score : score < peer_score);
if (take_peer) {
score = peer_score;
idx = peer_idx;
}
cross_stage++;
}
}
}
if (tid < 6) {
selected[tid] = idx;
}
}
// M3 decode specialization that extends the register/TG SIMD selection above
// through the existing six-value serial weight normalization. The selected ids
// cross the same device-memory boundary as the standalone weight kernel;
// volatile TG stores pin its left-fold and scaled-reciprocal rounding points.
kernel void kernel_dsv4_router_finalize_weights_one_simd(
constant ds4_metal_args_dsv4_router_select_one & args,
device const float *probs,
device const float *bias,
device const int32_t *hash,
device const int32_t *tokens,
device int32_t *selected,
device float *weights,
threadgroup float *scratch [[threadgroup(0)]],
uint tid [[thread_position_in_threadgroup]]) {
if (tid >= 256 || args.hash_mode) return;
(void)hash;
(void)tokens;
threadgroup float *score0_tg = scratch;
threadgroup int32_t *idx0_tg =
(threadgroup int32_t *)(scratch + 256);
threadgroup float *score1_tg = scratch + 512;
threadgroup int32_t *idx1_tg =
(threadgroup int32_t *)(scratch + 768);
const float p = probs[tid];
float score = args.has_bias ? p + bias[tid] : p;
int32_t idx = (int32_t)tid;
uint cross_stage = 0;
for (uint k = 2; k <= 256; k <<= 1) {
for (uint j = k >> 1; j > 0; j >>= 1) {
float peer_score;
int32_t peer_idx;
bool take_peer;
const bool lower = (tid & j) == 0;
const bool descending = (tid & k) == 0;
if (j < 32) {
peer_score = simd_shuffle_xor(score, (ushort)j);
peer_idx = simd_shuffle_xor(idx, (ushort)j);
take_peer = descending
? (lower ? score < peer_score : score > peer_score)
: (lower ? score > peer_score : score < peer_score);
if (take_peer) {
score = peer_score;
idx = peer_idx;
}
} else {
threadgroup float *score_tg =
(cross_stage & 1u) != 0u ? score1_tg : score0_tg;
threadgroup int32_t *idx_tg =
(cross_stage & 1u) != 0u ? idx1_tg : idx0_tg;
score_tg[tid] = score;
idx_tg[tid] = idx;
threadgroup_barrier(mem_flags::mem_threadgroup);
const uint other = tid ^ j;
peer_score = score_tg[other];
peer_idx = idx_tg[other];
take_peer = descending
? (lower ? score < peer_score : score > peer_score)
: (lower ? score > peer_score : score < peer_score);
if (take_peer) {
score = peer_score;
idx = peer_idx;
}
cross_stage++;
}
}
}
if (tid < 6) {
selected[tid] = idx;
}
threadgroup_barrier(mem_flags::mem_device);
threadgroup volatile float *norm_scratch =
(threadgroup volatile float *)scratch;
if (tid == 0) {
device const int32_t *s = selected;
norm_scratch[0] = 0.0f;
for (uint i = 0; i < 6; i++) {
norm_scratch[0] = norm_scratch[0] + probs[s[i]];
}
norm_scratch[0] = max(norm_scratch[0], 6.103515625e-5f);
norm_scratch[1] = 1.5f / norm_scratch[0];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
if (tid < 6) {
device const int32_t *s = selected;
weights[tid] = probs[s[tid]] * norm_scratch[1];
}
}
// Fills the dense compressed-attention mask with -inf. The selected top-k rows
// are enabled by kernel_dsv4_topk_mask_scatter in a second ordered dispatch.
kernel void kernel_dsv4_topk_mask(
constant ds4_metal_args_dsv4_topk_mask & args,
device const char * topk,
device char * dst,
uint gid [[thread_position_in_grid]]) {
const int64_t n = args.ne0 * args.ne1;
if ((int64_t) gid >= n) {
return;
}
const int64_t ic = gid % args.ne0;
const int64_t it = gid / args.ne0;
(void)topk;
*((device float *) (dst + ic*args.nb0 + it*args.nb1)) = -INFINITY;
}
// Enables the selected compressed rows in the dense mask. This replaces the
// old O(n_comp * n_tokens * top_k) membership test with O(top_k * n_tokens)
// writes while preserving exactly the same 0/-inf mask consumed by attention.
kernel void kernel_dsv4_topk_mask_scatter(
constant ds4_metal_args_dsv4_topk_mask & args,
device const char * topk,
device char * dst,
uint gid [[thread_position_in_grid]]) {
const int64_t n = args.ne00 * args.ne01;
if ((int64_t) gid >= n) {
return;
}
const int64_t ik = gid % args.ne00;
const int64_t it = gid / args.ne00;
const int32_t idx = *((device const int32_t *) (topk + ik*args.nb00 + it*args.nb01));
if (idx >= 0 && (int64_t)idx < args.ne0) {
*((device float *) (dst + (int64_t)idx*args.nb0 + it*args.nb1)) = 0.0f;
}
}
// Sorts each token's selected compressed rows by row id. The indexer selects by
// score, but attention scans compressed K/V in cache order in the dense graph.
// Sorting preserves that order while still letting the indexed attention kernel
// touch only the selected rows.
kernel void kernel_dsv4_sort_i32_rows_asc(
constant ds4_metal_args_dsv4_topk_mask & args,
device const char * src,
device char * dst,
threadgroup int32_t * row_tmp [[threadgroup(0)]],
uint row [[threadgroup_position_in_grid]],
uint tid [[thread_position_in_threadgroup]],
uint n_threads [[threads_per_threadgroup]]) {
const uint top_k = (uint)args.ne00;
if (row >= (uint)args.ne01 || tid >= n_threads) {
return;
}
for (uint i = tid; i < top_k; i += n_threads) {
row_tmp[i] = *((device const int32_t *) (src + (uint64_t)i*args.nb00 + (uint64_t)row*args.nb01));
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint k = 2; k <= top_k; k <<= 1) {
for (uint j = k >> 1; j > 0; j >>= 1) {
for (uint i = tid; i < top_k; i += n_threads) {
const uint other = i ^ j;
if (other > i && other < top_k) {
const int32_t a = row_tmp[i];
const int32_t b = row_tmp[other];
const bool up = (i & k) == 0;
if ((up && a > b) || (!up && a < b)) {
row_tmp[i] = b;
row_tmp[other] = a;
}
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
}
for (uint i = tid; i < top_k; i += n_threads) {
*((device int32_t *) (dst + (uint64_t)i*args.nb00 + (uint64_t)row*args.nb01)) = row_tmp[i];
}
}
static inline void dsv4_attend_f32_row_as_f16(
device const char *kv,
uint64_t row_stride,
uint row,
half4 q0,
half4 q1,
half4 q2,
half4 q3,
float scale,
ushort lane,
thread float &M,
thread float &S,
thread float4 &o0,
thread float4 &o1,
thread float4 &o2,
thread float4 &o3) {
device const float4 *kv4 = (device const float4 *)(kv + (uint64_t)row * row_stride);
const half4 k0 = (half4)kv4[lane + 0];
const half4 k1 = (half4)kv4[lane + 32];
const half4 k2 = (half4)kv4[lane + 64];
const half4 k3 = (half4)kv4[lane + 96];
float score = dot((float4)q0, (float4)k0) +
dot((float4)q1, (float4)k1) +
dot((float4)q2, (float4)k2) +
dot((float4)q3, (float4)k3);
score = simd_sum(score) * scale;
const float old_m = M;
const float new_m = max(M, score);
const float old_scale = exp(old_m - new_m);
const float row_scale = exp(score - new_m);
S = S * old_scale + row_scale;
o0 *= old_scale;
o1 *= old_scale;
o2 *= old_scale;
o3 *= old_scale;
o0 += (float4)k0 * row_scale;
o1 += (float4)k1 * row_scale;
o2 += (float4)k2 * row_scale;
o3 += (float4)k3 * row_scale;
M = new_m;
}
static inline void dsv4_attend_shared_f32_row_as_f16(
threadgroup const float4 *kv4,
half4 q0,
half4 q1,
half4 q2,
half4 q3,
float scale,
ushort lane,
thread float &M,
thread float &S,
thread float4 &o0,
thread float4 &o1,
thread float4 &o2,
thread float4 &o3) {
const half4 k0 = (half4)kv4[lane + 0];
const half4 k1 = (half4)kv4[lane + 32];
const half4 k2 = (half4)kv4[lane + 64];
const half4 k3 = (half4)kv4[lane + 96];
float score = dot((float4)q0, (float4)k0) +
dot((float4)q1, (float4)k1) +
dot((float4)q2, (float4)k2) +
dot((float4)q3, (float4)k3);
score = simd_sum(score) * scale;
const float old_m = M;
const float new_m = max(M, score);
const float old_scale = exp(old_m - new_m);
const float row_scale = exp(score - new_m);
S = S * old_scale + row_scale;
o0 *= old_scale;
o1 *= old_scale;
o2 *= old_scale;
o3 *= old_scale;
o0 += (float4)k0 * row_scale;
o1 += (float4)k1 * row_scale;
o2 += (float4)k2 * row_scale;
o3 += (float4)k3 * row_scale;
M = new_m;
}
static inline void dsv4_attend_shared_f32_row_as_f16_at(
threadgroup const float4 *kv4,
uint row_in_tg,
half4 q0,
half4 q1,
half4 q2,
half4 q3,
float scale,
ushort lane,
thread float &M,
thread float &S,
thread float4 &o0,
thread float4 &o1,
thread float4 &o2,
thread float4 &o3) {
dsv4_attend_shared_f32_row_as_f16(kv4 + row_in_tg * 128u,
q0, q1, q2, q3,
scale,
lane,
M, S,
o0, o1, o2, o3);
}
static inline void dsv4_attend_shared_h4_row(
threadgroup const half4 *kv4,
half4 q0,
half4 q1,
half4 q2,
half4 q3,
float scale,
ushort lane,
thread float &M,
thread float &S,
thread float4 &o0,
thread float4 &o1,
thread float4 &o2,
thread float4 &o3) {
const half4 k0 = kv4[lane + 0];
const half4 k1 = kv4[lane + 32];
const half4 k2 = kv4[lane + 64];
const half4 k3 = kv4[lane + 96];
float score = dot((float4)q0, (float4)k0) +
dot((float4)q1, (float4)k1) +
dot((float4)q2, (float4)k2) +
dot((float4)q3, (float4)k3);
score = simd_sum(score) * scale;
const float old_m = M;
const float new_m = max(M, score);
const float old_scale = exp(old_m - new_m);
const float row_scale = exp(score - new_m);
S = S * old_scale + row_scale;
o0 *= old_scale;
o1 *= old_scale;
o2 *= old_scale;
o3 *= old_scale;
o0 += (float4)k0 * row_scale;
o1 += (float4)k1 * row_scale;
o2 += (float4)k2 * row_scale;
o3 += (float4)k3 * row_scale;
M = new_m;
}
static inline void dsv4_attend_shared_h4_row_at(
threadgroup const half4 *kv4,
uint row_in_tg,
half4 q0,
half4 q1,
half4 q2,
half4 q3,
float scale,
ushort lane,
thread float &M,
thread float &S,
thread float4 &o0,
thread float4 &o1,
thread float4 &o2,
thread float4 &o3) {
dsv4_attend_shared_h4_row(kv4 + row_in_tg * 128u,
q0, q1, q2, q3,
scale,
lane,
M, S,
o0, o1, o2, o3);
}
static inline half4 dsv4_load_cache_h4(
device const char *kv,
uint64_t row_stride,
uint row,
uint col,
bool f16_rows) {
device const char *base = kv + (uint64_t)row * row_stride;
if (f16_rows) {
return ((device const half4 *)base)[col];
}
return (half4)((device const float4 *)base)[col];
}
static inline void dsv4_attend_sink(
float score,
thread float &M,
thread float &S,
thread float4 &o0,
thread float4 &o1,
thread float4 &o2,
thread float4 &o3) {
const float old_m = M;
const float new_m = max(M, score);
const float old_scale = exp(old_m - new_m);
const float row_scale = exp(score - new_m);
S = S * old_scale + row_scale;
o0 *= old_scale;
o1 *= old_scale;
o2 *= old_scale;
o3 *= old_scale;
M = new_m;
}
// DS4 ratio-4 indexed mixed attention. It replaces the dense top-k mask path:
// the threadgroup covers one token and eight heads. Top-k rows and local raw
// rows are the same for all heads of a token, so K/V is staged once in
// threadgroup memory and reused by the eight simdgroups. It keeps the DS4 F16
// attention rounding by casting Q/K/V to half before the dot/value update.
kernel void kernel_dsv4_indexed_mixed_attention_heads8(
constant ds4_metal_args_dsv4_indexed_attention & args,
device const char *q,
device const char *raw_kv,
device const char *comp_kv,
device const char *topk,
device const char *sinks,
device char *dst,
threadgroup half4 *kv_shared [[threadgroup(0)]],
uint2 tgpig [[threadgroup_position_in_grid]],
ushort tid [[thread_index_in_threadgroup]],
ushort lane [[thread_index_in_simdgroup]],
ushort sg [[simdgroup_index_in_threadgroup]]) {
const uint token = tgpig.x;
const uint head = tgpig.y * 8u + (uint)sg;
if (token >= args.n_tokens || head >= args.n_head) {
return;
}
device const float4 *q4 = (device const float4 *)(q +
(uint64_t)token * args.q_token_stride +
(uint64_t)head * args.q_head_stride);
const half4 q0 = (half4)q4[lane + 0];
const half4 q1 = (half4)q4[lane + 32];
const half4 q2 = (half4)q4[lane + 64];
const half4 q3 = (half4)q4[lane + 96];
float M = -FLT_MAX/2.0f;
float S = 0.0f;
float4 o0 = 0.0f;
float4 o1 = 0.0f;
float4 o2 = 0.0f;
float4 o3 = 0.0f;
const uint qpos = args.pos0 + token;
const uint last_pos = args.pos0 + args.n_tokens - 1u;
const uint first_raw_pos = last_pos + 1u - args.n_raw;
const uint raw_last_pos = first_raw_pos + args.n_raw - 1u;
const uint window_first = (args.window != 0u && qpos + 1u > args.window) ?
qpos + 1u - args.window : 0u;
uint first = max(first_raw_pos, window_first);
uint last = min(qpos, raw_last_pos);
if (first <= last) {
for (uint pos = first; pos <= last; pos++) {
const uint logical = pos - first_raw_pos;
const uint row = (args.raw_start + logical) % args.raw_cap;
device const float4 *src = (device const float4 *)(raw_kv +
(uint64_t)row * args.raw_row_stride);
if (tid < 128) kv_shared[tid] = (half4)src[tid];
threadgroup_barrier(mem_flags::mem_threadgroup);
dsv4_attend_shared_h4_row(kv_shared,
q0, q1, q2, q3,
args.scale,
lane,
M, S,
o0, o1, o2, o3);
threadgroup_barrier(mem_flags::mem_threadgroup);
}
}
uint visible = (qpos + 1u) / args.ratio;
visible = min(visible, args.n_comp);
device const int32_t *row_topk = (device const int32_t *)(topk +
(uint64_t)token * args.topk_token_stride);
for (uint i = 0; i < args.top_k; i++) {
const int32_t idx = row_topk[i];
if (idx < 0) {
continue;
}
if ((uint)idx >= visible) {
break;
}
if (tid < 128) {
kv_shared[tid] = dsv4_load_cache_h4(comp_kv,
args.comp_row_stride,
(uint)idx,
tid,
args.comp_kv_f16 != 0u);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
dsv4_attend_shared_h4_row(kv_shared,
q0, q1, q2, q3,
args.scale,
lane,
M, S,
o0, o1, o2, o3);
threadgroup_barrier(mem_flags::mem_threadgroup);
}
dsv4_attend_sink(((device const float *)sinks)[head], M, S, o0, o1, o2, o3);
const float inv_s = S == 0.0f ? 0.0f : 1.0f/S;
device float4 *dst4 = (device float4 *)(dst +
(uint64_t)token * args.dst_token_stride +
(uint64_t)head * args.dst_head_stride);
dst4[lane + 0] = o0 * inv_s;
dst4[lane + 32] = o1 * inv_s;
dst4[lane + 64] = o2 * inv_s;
dst4[lane + 96] = o3 * inv_s;
}
// Decode specialization of kernel_dsv4_indexed_mixed_attention_heads8.
// Generation attends one token at a time, so the ratio-4 indexed path spends a
// visible amount of time repeatedly staging the same K/V row for the eight
// heads in a group. This variant stages sixteen selected rows at once and then
// consumes them sequentially, preserving the row order and online softmax math
// while cutting threadgroup barriers in the long top-k scan.
kernel void kernel_dsv4_indexed_mixed_attention_heads8_rb16(
constant ds4_metal_args_dsv4_indexed_attention & args,
device const char *q,
device const char *raw_kv,
device const char *comp_kv,
device const char *topk,
device const char *sinks,
device char *dst,
threadgroup half4 *kv_shared [[threadgroup(0)]],
uint2 tgpig [[threadgroup_position_in_grid]],
ushort tid [[thread_index_in_threadgroup]],
ushort lane [[thread_index_in_simdgroup]],
ushort sg [[simdgroup_index_in_threadgroup]]) {
const uint token = tgpig.x;
const uint head = tgpig.y * 8u + (uint)sg;
if (token >= args.n_tokens || head >= args.n_head) {
return;
}
device const float4 *q4 = (device const float4 *)(q +
(uint64_t)token * args.q_token_stride +
(uint64_t)head * args.q_head_stride);
const half4 q0 = (half4)q4[lane + 0];
const half4 q1 = (half4)q4[lane + 32];
const half4 q2 = (half4)q4[lane + 64];
const half4 q3 = (half4)q4[lane + 96];
float M = -FLT_MAX/2.0f;
float S = 0.0f;
float4 o0 = 0.0f;
float4 o1 = 0.0f;
float4 o2 = 0.0f;
float4 o3 = 0.0f;
const uint qpos = args.pos0 + token;
const uint last_pos = args.pos0 + args.n_tokens - 1u;
const uint first_raw_pos = last_pos + 1u - args.n_raw;
const uint raw_last_pos = first_raw_pos + args.n_raw - 1u;
const uint window_first = (args.window != 0u && qpos + 1u > args.window) ?
qpos + 1u - args.window : 0u;
uint first = max(first_raw_pos, window_first);
uint last = min(qpos, raw_last_pos);
if (first <= last) {
for (uint pos0 = first; pos0 <= last; pos0 += 16u) {
const uint n_rows = min(16u, last - pos0 + 1u);
for (uint off = (uint)tid; off < n_rows * 128u; off += 256u) {
const uint r = off >> 7;
const uint c = off & 127u;
const uint logical = pos0 + r - first_raw_pos;
const uint row = (args.raw_start + logical) % args.raw_cap;
device const float4 *src = (device const float4 *)(raw_kv +
(uint64_t)row * args.raw_row_stride);
kv_shared[off] = (half4)src[c];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint r = 0; r < n_rows; r++) {
dsv4_attend_shared_h4_row_at(kv_shared,
r,
q0, q1, q2, q3,
args.scale,
lane,
M, S,
o0, o1, o2, o3);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
}
uint visible = (qpos + 1u) / args.ratio;
visible = min(visible, args.n_comp);
device const int32_t *row_topk = (device const int32_t *)(topk +
(uint64_t)token * args.topk_token_stride);
bool stop = false;
for (uint i = 0; i < args.top_k && !stop; i += 16u) {
uint rows[16];
uint n_rows = 0;
for (uint j = 0; j < 16u && i + j < args.top_k; j++) {
const int32_t idx = row_topk[i + j];
if (idx < 0) {
continue;
}
if ((uint)idx >= visible) {
stop = true;
break;
}
rows[n_rows++] = (uint)idx;
}
if (n_rows == 0) {
continue;
}
for (uint off = (uint)tid; off < n_rows * 128u; off += 256u) {
const uint r = off >> 7;
const uint c = off & 127u;
kv_shared[off] = dsv4_load_cache_h4(comp_kv,
args.comp_row_stride,
rows[r],
c,
args.comp_kv_f16 != 0u);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint r = 0; r < n_rows; r++) {
dsv4_attend_shared_h4_row_at(kv_shared,
r,
q0, q1, q2, q3,
args.scale,
lane,
M, S,
o0, o1, o2, o3);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
dsv4_attend_sink(((device const float *)sinks)[head], M, S, o0, o1, o2, o3);
const float inv_s = S == 0.0f ? 0.0f : 1.0f/S;
device float4 *dst4 = (device float4 *)(dst +
(uint64_t)token * args.dst_token_stride +
(uint64_t)head * args.dst_head_stride);
dst4[lane + 0] = o0 * inv_s;
dst4[lane + 32] = o1 * inv_s;
dst4[lane + 64] = o2 * inv_s;
dst4[lane + 96] = o3 * inv_s;
}
static inline float dsv4_indexer_dot128_shared_q(
float4 c0,
float4 c1,
float4 c2,
float4 c3,
threadgroup const float4 *q4,
ushort lane) {
float sum = 0.0f;
if (lane < 8) {
const ushort ib = lane >> 1;
const ushort il = lane & 1;
const ushort base = ib*8 + il*4;
sum += dot(c0, q4[base + 0]);
sum += dot(c1, q4[base + 1]);
sum += dot(c2, q4[base + 2]);
sum += dot(c3, q4[base + 3]);
}
return simd_sum(sum);
}
// Tiled prefill score builder for the sparse-compressed attention indexer.
//
// The kernel covers an 8-token by 32-compressed-row rectangle: K is copied into
// threadgroup memory once, then reused for all 64 indexer heads, while simdgroup
// matrix multiply computes each 8x8 score subtile.
//
// It still writes the exact score matrix consumed by top-k:
//
// score[t,c] = sum_h relu(dot(Q[t,h], K[c])) * W[t,h] * scale
//
// Causal masking is applied on store so invisible compressed rows become -inf.
kernel void kernel_dsv4_indexer_scores_tiled_f32(
constant ds4_metal_args_dsv4_indexer_scores_fused & args,
device const char *q,
device const char *weights,
device const char *index_comp,
device char *scores,
threadgroup float *shared [[threadgroup(0)]],
uint2 tgpig [[threadgroup_position_in_grid]],
ushort tid [[thread_index_in_threadgroup]],
ushort lane [[thread_index_in_simdgroup]],
ushort sg [[simdgroup_index_in_threadgroup]]) {
constexpr uint TM = 8;
constexpr uint TN = 32;
constexpr uint TS = 8;
constexpr uint D = 128;
const uint c0 = tgpig.x * TN;
const uint t0 = tgpig.y * TM;
threadgroup float *qtg = shared; // [8][128]
threadgroup float *ktg = qtg + TM*D; // [32][128]
threadgroup float *dot = ktg + TN*D; // [8][32]
const uint last_token = min(t0 + TM, args.n_tokens);
const uint max_visible = last_token > t0 ?
min((args.pos0 + last_token) / args.ratio, args.n_comp) : 0u;
if (c0 >= max_visible) {
for (uint i = tid; i < TM*TN; i += 128) {
const uint r = i / TN;
const uint cc = i - r*TN;
const uint token = t0 + r;
const uint comp = c0 + cc;
if (token < args.n_tokens && comp < args.n_comp) {
device float *dst = (device float *)(scores +
(uint64_t)token * args.score_token_stride) + comp;
*dst = -INFINITY;
}
}
return;
}
for (uint i = tid; i < TN*D; i += 128) {
const uint cc = i / D;
const uint d = i - cc*D;
const uint comp = c0 + cc;
float v = 0.0f;
if (comp < args.n_comp) {
device const float *row = (device const float *)(index_comp +
(uint64_t)comp * args.index_row_stride);
v = row[d];
}
ktg[i] = v;
}
const uint cell0 = lane;
const uint cell1 = lane + 32u;
const uint row0 = cell0 >> 3;
const uint row1 = cell1 >> 3;
const uint sub0 = cell0 & 7u;
const uint sub1 = cell1 & 7u;
const uint col0 = (uint)sg * TS + sub0;
const uint col1 = (uint)sg * TS + sub1;
const uint token0 = t0 + row0;
const uint token1 = t0 + row1;
const uint comp0 = c0 + col0;
const uint comp1 = c0 + col1;
float acc0 = 0.0f;
float acc1 = 0.0f;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint head = 0; head < args.n_head; head++) {
for (uint i = tid; i < TM*D; i += 128) {
const uint r = i / D;
const uint d = i - r*D;
const uint token = t0 + r;
float v = 0.0f;
if (token < args.n_tokens) {
device const float *qrow = (device const float *)(q +
(uint64_t)token * args.q_token_stride +
(uint64_t)head * args.q_head_stride);
v = qrow[d];
}
qtg[i] = v;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
simdgroup_float8x8 mdot = make_filled_simdgroup_matrix<float, 8>(0.0f);
for (uint db = 0; db < D/TS; db++) {
simdgroup_float8x8 mq;
simdgroup_float8x8 mk;
simdgroup_load(mq, qtg + db*TS, D, 0, false);
simdgroup_load(mk, ktg + ((uint)sg * TS) * D + db*TS, D, 0, true);
simdgroup_multiply_accumulate(mdot, mq, mk, mdot);
}
simdgroup_store(mdot, dot + (uint)sg * TS, TN, 0, false);
threadgroup_barrier(mem_flags::mem_threadgroup);
if (token0 < args.n_tokens && comp0 < args.n_comp) {
device const float *w = (device const float *)(weights +
(uint64_t)token0 * args.weights_token_stride);
const float s = dot[row0*TN + col0];
acc0 += max(s, 0.0f) * (w[head] * args.scale);
}
if (token1 < args.n_tokens && comp1 < args.n_comp) {
device const float *w = (device const float *)(weights +
(uint64_t)token1 * args.weights_token_stride);
const float s = dot[row1*TN + col1];
acc1 += max(s, 0.0f) * (w[head] * args.scale);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (token0 < args.n_tokens && comp0 < args.n_comp) {
const uint visible = min((args.pos0 + token0 + 1u) / args.ratio, args.n_comp);
device float *dst = (device float *)(scores +
(uint64_t)token0 * args.score_token_stride) + comp0;
*dst = comp0 < visible ? acc0 : -INFINITY;
}
if (token1 < args.n_tokens && comp1 < args.n_comp) {
const uint visible = min((args.pos0 + token1 + 1u) / args.ratio, args.n_comp);
device float *dst = (device float *)(scores +
(uint64_t)token1 * args.score_token_stride) + comp1;
*dst = comp1 < visible ? acc1 : -INFINITY;
}
}
kernel void kernel_dsv4_indexer_scores_tiled(
constant ds4_metal_args_dsv4_indexer_scores_fused & args,
device const char *q,
device const char *weights,
device const char *index_comp,
device char *scores,
threadgroup float *shared [[threadgroup(0)]],
uint2 tgpig [[threadgroup_position_in_grid]],
ushort tid [[thread_index_in_threadgroup]],
ushort lane [[thread_index_in_simdgroup]],
ushort sg [[simdgroup_index_in_threadgroup]]) {
constexpr uint TM = 8;
constexpr uint TN = 32;
constexpr uint TS = 8;
constexpr uint D = 128;
const uint c0 = tgpig.x * TN;
const uint t0 = tgpig.y * TM;
// Q/K are staged as half but the dot accumulator and final score remain
// float. This is the one intentional precision tradeoff in the indexer:
// the indexer only ranks compressed rows for top-k selection, and long
// context profiling shows this score matrix dominates the prefill slope.
threadgroup half *qtg = (threadgroup half *)shared; // [8][128]
threadgroup half *ktg = qtg + TM*D; // [32][128]
threadgroup float *dot = (threadgroup float *)(ktg + TN*D); // [8][32]
const uint last_token = min(t0 + TM, args.n_tokens);
const uint max_visible = last_token > t0 ?
min((args.pos0 + last_token) / args.ratio, args.n_comp) : 0u;
if (c0 >= max_visible) {
for (uint i = tid; i < TM*TN; i += 128) {
const uint r = i / TN;
const uint cc = i - r*TN;
const uint token = t0 + r;
const uint comp = c0 + cc;
if (token < args.n_tokens && comp < args.n_comp) {
device float *dst = (device float *)(scores +
(uint64_t)token * args.score_token_stride) + comp;
*dst = -INFINITY;
}
}
return;
}
// Stage compressed index rows once. Edge columns are zeroed so the matrix
// loads below can stay regular; guarded stores discard them.
for (uint i = tid; i < TN*D; i += 128) {
const uint cc = i / D;
const uint d = i - cc*D;
const uint comp = c0 + cc;
half v = half(0.0f);
if (comp < args.n_comp) {
device const float *row = (device const float *)(index_comp +
(uint64_t)comp * args.index_row_stride);
v = half(row[d]);
}
ktg[i] = v;
}
const uint cell0 = lane;
const uint cell1 = lane + 32u;
const uint row0 = cell0 >> 3;
const uint row1 = cell1 >> 3;
const uint sub0 = cell0 & 7u;
const uint sub1 = cell1 & 7u;
const uint col0 = (uint)sg * TS + sub0;
const uint col1 = (uint)sg * TS + sub1;
const uint token0 = t0 + row0;
const uint token1 = t0 + row1;
const uint comp0 = c0 + col0;
const uint comp1 = c0 + col1;
float acc0 = 0.0f;
float acc1 = 0.0f;
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint head = 0; head < args.n_head; head++) {
// Stage Q for the eight-token tile. Each 8x8 matrix load below reads a
// contiguous depth block from this layout.
for (uint i = tid; i < TM*D; i += 128) {
const uint r = i / D;
const uint d = i - r*D;
const uint token = t0 + r;
half v = half(0.0f);
if (token < args.n_tokens) {
device const float *qrow = (device const float *)(q +
(uint64_t)token * args.q_token_stride +
(uint64_t)head * args.q_head_stride);
v = half(qrow[d]);
}
qtg[i] = v;
}
threadgroup_barrier(mem_flags::mem_threadgroup);
simdgroup_float8x8 mdot = make_filled_simdgroup_matrix<float, 8>(0.0f);
for (uint db = 0; db < D/TS; db++) {
simdgroup_half8x8 mq;
simdgroup_half8x8 mk;
simdgroup_load(mq, qtg + db*TS, D, 0, false);
simdgroup_load(mk, ktg + ((uint)sg * TS) * D + db*TS, D, 0, true);
simdgroup_multiply_accumulate(mdot, mq, mk, mdot);
}
simdgroup_store(mdot, dot + (uint)sg * TS, TN, 0, false);
threadgroup_barrier(mem_flags::mem_threadgroup);
if (token0 < args.n_tokens && comp0 < args.n_comp) {
device const float *w = (device const float *)(weights +
(uint64_t)token0 * args.weights_token_stride);
const float s = dot[row0*TN + col0];
acc0 += max(s, 0.0f) * (w[head] * args.scale);
}
if (token1 < args.n_tokens && comp1 < args.n_comp) {
device const float *w = (device const float *)(weights +
(uint64_t)token1 * args.weights_token_stride);
const float s = dot[row1*TN + col1];
acc1 += max(s, 0.0f) * (w[head] * args.scale);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
if (token0 < args.n_tokens && comp0 < args.n_comp) {
const uint visible = min((args.pos0 + token0 + 1u) / args.ratio, args.n_comp);
device float *dst = (device float *)(scores +
(uint64_t)token0 * args.score_token_stride) + comp0;
*dst = comp0 < visible ? acc0 : -INFINITY;
}
if (token1 < args.n_tokens && comp1 < args.n_comp) {
const uint visible = min((args.pos0 + token1 + 1u) / args.ratio, args.n_comp);
device float *dst = (device float *)(scores +
(uint64_t)token1 * args.score_token_stride) + comp1;
*dst = comp1 < visible ? acc1 : -INFINITY;
}
}
#ifdef DS4_METAL_HAS_TENSOR
// Retained full-512 prefill indexer score path. This is the part of sparse
// compressed attention that maps cleanly to TensorOps: a regular token by
// compressed-row dot tile. The kernel intentionally leaves top-k selection and
// indexed attention semantics unchanged; all 512 selected rows remain available
// to the later attention kernel.
//
// Each matmul processes a pair of heads (TQ = 2 x TM q rows): the per-element
// dot is still a 128-deep reduction in 32-wide k-steps, so scores are
// bit-identical to single-head tiles while the run count halves. The q tile
// is double-buffered, so the next k-step's stage overlaps the current
// cooperative matmul and each pair needs 5 barriers instead of 10. q and k
// staging use one float4/half4 per lane (each thread covers one row of 8/32
// consecutive elements), which is the same half(float) conversion per element
// as the scalar form.
kernel void kernel_dsv4_indexer_scores_nax(
constant ds4_metal_args_dsv4_indexer_scores_fused & args,
device const char *q,
device const char *weights,
device const char *index_comp,
device char *scores,
threadgroup half *shared [[threadgroup(0)]],
uint2 tgpig [[threadgroup_position_in_grid]],
ushort tid [[thread_index_in_threadgroup]]) {
constexpr int TM = 16;
constexpr int TQ = 32;
constexpr int TN = 32;
constexpr int NK = 32;
constexpr int D = 128;
constexpr int NUM_THREADS = 128;
// The 16-token x 32-row tile was the winning NAX shape in local sweeps. A
// wider 64-row compressed tile increased setup/cache pressure and was
// slower despite doing more work per dispatch.
const uint c0 = tgpig.x * TN;
const uint t0 = tgpig.y * TM;
threadgroup half *qtg = shared; // 2 x [TQ][NK]
threadgroup half *ktg = qtg + 2*TQ*NK; // [32][128]
threadgroup float *dot = (threadgroup float *)(ktg + TN*D); // [TQ][TN], column-major
const uint last_token = min(t0 + (uint)TM, args.n_tokens);
const uint max_visible = last_token > t0 ?
min((args.pos0 + last_token) / args.ratio, args.n_comp) : 0u;
if (c0 >= max_visible) {
for (uint i = tid; i < TM*TN; i += NUM_THREADS) {
const uint r = i / TN;
const uint cc = i - r*TN;
const uint token = t0 + r;
const uint comp = c0 + cc;
if (token < args.n_tokens && comp < args.n_comp) {
device float *dst = (device float *)(scores +
(uint64_t)token * args.score_token_stride) + comp;
*dst = -INFINITY;
}
}
return;
}
{
// One compressed row per 4 threads, 32 consecutive floats per thread.
const uint cc = tid / 4;
const uint comp = c0 + cc;
device const float *krow = nullptr;
if (comp < args.n_comp) {
krow = (device const float *)(index_comp +
(uint64_t)comp * args.index_row_stride);
}
const uint d0 = (tid % 4) * 32;
FOR_UNROLL (uint j = 0; j < 8; j++) {
const float4 kv = krow ? *(device const float4 *)(krow + d0 + 4*j)
: float4(0.0f);
*(threadgroup half4 *)(ktg + cc*D + d0 + 4*j) = half4(kv);
}
}
threadgroup_barrier(mem_flags::mem_threadgroup);
float acc[4];
#pragma unroll
for (uint j = 0; j < 4; j++) {
acc[j] = 0.0f;
}
auto tq0 = tensor(qtg, dextents<int32_t, 2>(NK, TQ));
auto tq1 = tensor(qtg + TQ*NK, dextents<int32_t, 2>(NK, TQ));
auto tk = tensor(ktg, dextents<int32_t, 2>(D, TN));
auto td = tensor(dot, dextents<int32_t, 2>(TQ, TN), array<int, 2>({1, TQ}));
matmul2d<
matmul2d_descriptor(TN, TQ, NK, false, true, false,
matmul2d_descriptor::mode::multiply_accumulate),
execution_simdgroups<4>> mm;
// One q row per 4 threads, 8 consecutive floats per thread. Row r covers
// head (r / TM) of the pair and token row (r % TM).
const uint q_r = tid / 4;
const uint q_k4 = (tid % 4) * 8;
const uint q_hl = q_r / TM;
const uint q_tr = q_r % TM;
const uint q_token = t0 + q_tr;
device const char *q_row_base = nullptr;
if (q_token < args.n_tokens) {
q_row_base = q + (uint64_t)q_token * args.q_token_stride;
}
auto stage_q = [&](const uint head0, const uint loop_k, threadgroup half *buf) {
const uint head = head0 + q_hl;
half4 v0 = half4(0.0f);
half4 v1 = half4(0.0f);
if (q_row_base && head < args.n_head) {
device const float4 *src4 = (device const float4 *)
(q_row_base + (uint64_t)head * args.q_head_stride +
(uint64_t)(loop_k + q_k4) * sizeof(float));
v0 = half4(src4[0]);
v1 = half4(src4[1]);
}
*(threadgroup half4 *)(buf + q_r*NK + q_k4) = v0;
*(threadgroup half4 *)(buf + q_r*NK + q_k4 + 4) = v1;
};
for (uint head0 = 0; head0 < args.n_head; head0 += 2) {
auto ct = mm.template get_destination_cooperative_tensor<decltype(tk), decltype(tq0), float>();
#pragma unroll
for (uint16_t i = 0; i < ct.get_capacity(); i++) {
if (ct.is_valid_element(i)) {
ct[i] = 0.0f;
}
}
stage_q(head0, 0, qtg);
threadgroup_barrier(mem_flags::mem_threadgroup);
uint qsel = 0;
FOR_UNROLL (uint i = 0; i < 4; i++) {
auto mk = tk.slice(i*NK, 0);
auto mq = (qsel ? tq1 : tq0).slice(0, 0);
mm.run(mk, mq, ct);
if (i < 3) {
qsel ^= 1u;
stage_q(head0, (i + 1)*NK, qsel ? qtg + TQ*NK : qtg);
threadgroup_barrier(mem_flags::mem_threadgroup);
}
}
ct.store(td);
threadgroup_barrier(mem_flags::mem_threadgroup);
#pragma unroll
for (uint j = 0; j < 4; j++) {
const uint linear = (uint)tid + j*NUM_THREADS;
if (linear < TM*TN) {
const uint r = linear / TN;
const uint cc = linear - r*TN;
const uint token = t0 + r;
if (token < args.n_tokens) {
device const float *w = (device const float *)(weights +
(uint64_t)token * args.weights_token_stride);
acc[j] += max(dot[cc*TQ + r], 0.0f) * (w[head0] * args.scale);
if (head0 + 1 < args.n_head) {
acc[j] += max(dot[cc*TQ + TM + r], 0.0f) * (w[head0 + 1] * args.scale);
}
}
}
}
// No barrier here: the next pair's q stage and these dot reads touch
// different buffers, and the next q-stage barrier separates the next
// ct.store from these reads.
}
#pragma unroll
for (uint j = 0; j < 4; j++) {
const uint linear = (uint)tid + j*NUM_THREADS;
if (linear >= TM*TN) {
continue;
}
const uint r = linear / TN;
const uint cc = linear - r*TN;
const uint token = t0 + r;
const uint comp = c0 + cc;
if (token < args.n_tokens && comp < args.n_comp) {
const uint visible = min((args.pos0 + token + 1u) / args.ratio, args.n_comp);
device float *dst = (device float *)(scores +
(uint64_t)token * args.score_token_stride) + comp;
*dst = comp < visible ? acc[j] : -INFINITY;
}
}
}
#endif
// Collapses per-head indexer scores into one score per compressed row using the
// learned head weights. Negative head scores are clipped exactly as DS4 expects.
kernel void kernel_dsv4_indexer_weighted_sum(
constant ds4_metal_args_dsv4_indexer_weighted_sum & args,
device const char * scores,
device const char * weights,
device char * dst,
uint gid [[thread_position_in_grid]]) {
const int64_t n = args.ne0 * args.ne1;
if ((int64_t) gid >= n) {
return;
}
const int64_t ic = gid % args.ne0;
const int64_t it = gid / args.ne0;
float acc = 0.0f;
for (int64_t ih = 0; ih < args.ne02; ++ih) {
const float s = *((device const float *) (scores + ic*args.nb00 + it*args.nb01 + ih*args.nb02));
const float w = *((device const float *) (weights + ih*args.nb10 + it*args.nb11));
acc += max(s, 0.0f) * (w * args.scale);
}
*((device float *) (dst + ic*args.nb0 + it*args.nb1)) = acc;
}
// Adds the periodic compressor APE directly to projected scores. The legacy
// path materializes one repeated APE segment per period and then performs this
// same single F32 add; these kernels remove only that intermediate copy graph.
kernel void kernel_dsv4_compressor_score_ape_f32(
constant ds4_metal_args_dsv4_compressor_score_ape & args,
device const float *score,
device const float *ape,
device float *dst,
uint gid [[thread_position_in_grid]]) {
const uint64_t total = (uint64_t)args.n_tokens * args.width;
if ((uint64_t)gid >= total) return;
const uint token = gid / args.width;
const uint col = gid - token*args.width;
const uint ape_row = (uint)(((uint64_t)args.pos0 + token) % args.ratio);
dst[gid] = score[gid] + ape[(uint64_t)ape_row*args.width + col];
}
kernel void kernel_dsv4_compressor_score_ape_f16(
constant ds4_metal_args_dsv4_compressor_score_ape & args,
device const float *score,
device const half *ape,
device float *dst,
uint gid [[thread_position_in_grid]]) {
const uint64_t total = (uint64_t)args.n_tokens * args.width;
if ((uint64_t)gid >= total) return;
const uint token = gid / args.width;
const uint col = gid - token*args.width;
const uint ape_row = (uint)(((uint64_t)args.pos0 + token) % args.ratio);
dst[gid] = score[gid] + float(ape[(uint64_t)ape_row*args.width + col]);
}
// Fused softmax-weighted pooling of compressed KV rows. It is used when several
// compressor rows are present; the one-row case deliberately follows the
// unfused softmax/mul/sum graph in Objective-C to keep identical reductions.
kernel void kernel_dsv4_softmax_pool(
constant ds4_metal_args_dsv4_softmax_pool & args,
device const char * kv,
device const char * score,
device char * dst,
uint gid [[thread_position_in_grid]]) {
const int64_t n = args.ne0 * args.ne1;
if ((int64_t) gid >= n) {
return;
}
const int64_t id = gid % args.ne0;
const int64_t ic = gid / args.ne0;
float max_s = -INFINITY;
for (int64_t ir = 0; ir < args.ne00; ++ir) {
const float s = *((device const float *) (score + ir*args.nb10 + id*args.nb11 + ic*args.nb12));
max_s = max(max_s, s);
}
float sum = 0.0f;
float acc = 0.0f;
for (int64_t ir = 0; ir < args.ne00; ++ir) {
const float s = *((device const float *) (score + ir*args.nb10 + id*args.nb11 + ic*args.nb12));
const float w = exp(s - max_s);
const float v = *((device const float *) (kv + ir*args.nb00 + id*args.nb01 + ic*args.nb02));
sum += w;
acc += v*w;
}
*((device float *) (dst + id*args.nb0 + ic*args.nb1)) = acc/sum;
}
// Tensor-parallel keep-alive: a few threadgroups of FMAs dispatched
// back-to-back on a side queue while TP decode runs. The per-layer gate
// stalls make the real workload look idle to the GPU power manager, which
// otherwise halves the clocks within a second (~2x decode regression);
// this holds them up for negligible bandwidth and a few watts.
kernel void kernel_dsv4_tp_keepalive(
device float * out,
constant uint & iters,
uint tid [[thread_position_in_grid]]) {
float a = out[tid];
const float b = 1.000001f;
for (uint i = 0; i < iters; i++) {
a = fma(a, b, 0.000001f);
a = fma(a, b, -0.000001f);
}
out[tid] = a;
}
// Tensor-parallel gate flag: publishes a sequence number to a slab slot the
// CPU service thread spin-reads, replacing the much slower shared-event
// signal for the GPU->CPU direction. Ordering against the partial-output
// kernels comes from the buffer hazard on the shared slab.
kernel void kernel_dsv4_tp_flag_set(
device atomic_uint & flag,
constant uint & value,
uint tid [[thread_position_in_grid]]) {
if (tid == 0) {
atomic_store_explicit(&flag, value, memory_order_relaxed);
}
}
// Ratio-4 compressor pooling without materializing the [n_comp, 8, head_dim]
// KV and score packs. The row mapping and both reduction loops deliberately
// match kernel_dsv4_softmax_pool so the arithmetic order is unchanged.
kernel void kernel_dsv4_softmax_pool_ratio4_direct(
constant ds4_metal_args_dsv4_softmax_pool_ratio4_direct & args,
device const float * kv,
device const float * score,
device const float * state_kv,
device const float * state_score,
device float * dst,
uint gid [[thread_position_in_grid]]) {
const uint64_t n = (uint64_t)args.head_dim * args.n_comp;
if ((uint64_t)gid >= n || args.head_dim == 0u) {
return;
}
const uint64_t id = gid % args.head_dim;
const uint64_t ic = gid / args.head_dim;
const uint64_t input_row_stride = 2ull * args.head_dim;
float max_s = -INFINITY;
float sum = 0.0f;
float acc = 0.0f;
if (ic != 0u) {
const int64_t token_base = (int64_t)ic * 4 - 4;
for (int64_t ir = 0; ir < args.n_rows; ++ir) {
const uint64_t token = (uint64_t)(token_base + ir);
const uint64_t src = token * input_row_stride +
((uint64_t)ir >> 2u) * args.head_dim + id;
const float s = score[src];
max_s = max(max_s, s);
}
for (int64_t ir = 0; ir < args.n_rows; ++ir) {
const uint64_t token = (uint64_t)(token_base + ir);
const uint64_t src = token * input_row_stride +
((uint64_t)ir >> 2u) * args.head_dim + id;
const float s = score[src];
const float w = exp(s - max_s);
const float v = kv[src];
sum += w;
acc += v*w;
}
} else {
for (int64_t ir = 0; ir < args.n_rows; ++ir) {
float s;
if (ir >= 4) {
const uint64_t src = (uint64_t)(ir - 4) * input_row_stride +
args.head_dim + id;
s = score[src];
} else if (args.replay != 0u) {
s = state_score[(uint64_t)ir * input_row_stride + id];
} else {
s = -INFINITY;
}
max_s = max(max_s, s);
}
for (int64_t ir = 0; ir < args.n_rows; ++ir) {
float s;
float v;
if (ir >= 4) {
const uint64_t src = (uint64_t)(ir - 4) * input_row_stride +
args.head_dim + id;
s = score[src];
v = kv[src];
} else if (args.replay != 0u) {
const uint64_t src = (uint64_t)ir * input_row_stride + id;
s = state_score[src];
v = state_kv[src];
} else {
s = -INFINITY;
v = 0.0f;
}
const float w = exp(s - max_s);
sum += w;
acc += v*w;
}
}
dst[ic * args.head_dim + id] = acc/sum;
}