// DS4 Metal routed-MoE matvec kernels. #ifndef QK_K #define QK_K 256 #endif #define N_R0_Q2_K 4 #define N_R0_GLM_Q2_PAIR2_K 1 #define N_R0_Q4_K 2 #define N_R0_Q8_K 2 #define N_R0_GLM_Q4_PAIR2_K 1 #define N_R0_GLM_Q4_PAIR_K 4 #define N_R0_Q5_PAIR_K 4 #define N_R0_Q5_K 4 #define N_R0_Q6_K 2 #define N_R0_IQ2_XXS 4 static constant uchar ds4_metal_kmask_iq2xs[8] = { 1, 2, 4, 8, 16, 32, 64, 128 }; static constant uchar ds4_metal_ksigns_iq2xs[128] = { 0, 129, 130, 3, 132, 5, 6, 135, 136, 9, 10, 139, 12, 141, 142, 15, 144, 17, 18, 147, 20, 149, 150, 23, 24, 153, 154, 27, 156, 29, 30, 159, 160, 33, 34, 163, 36, 165, 166, 39, 40, 169, 170, 43, 172, 45, 46, 175, 48, 177, 178, 51, 180, 53, 54, 183, 184, 57, 58, 187, 60, 189, 190, 63, 192, 65, 66, 195, 68, 197, 198, 71, 72, 201, 202, 75, 204, 77, 78, 207, 80, 209, 210, 83, 212, 85, 86, 215, 216, 89, 90, 219, 92, 221, 222, 95, 96, 225, 226, 99, 228, 101, 102, 231, 232, 105, 106, 235, 108, 237, 238, 111, 240, 113, 114, 243, 116, 245, 246, 119, 120, 249, 250, 123, 252, 125, 126, 255, }; static constant ulong ds4_metal_iq2xxs_grid[256] = { 0x0808080808080808, 0x080808080808082b, 0x0808080808081919, 0x0808080808082b08, 0x0808080808082b2b, 0x0808080808190819, 0x0808080808191908, 0x08080808082b0808, 0x08080808082b082b, 0x08080808082b2b08, 0x08080808082b2b2b, 0x0808080819080819, 0x0808080819081908, 0x0808080819190808, 0x0808080819192b08, 0x08080808192b0819, 0x08080808192b1908, 0x080808082b080808, 0x080808082b08082b, 0x080808082b082b2b, 0x080808082b2b082b, 0x0808081908080819, 0x0808081908081908, 0x0808081908190808, 0x0808081908191919, 0x0808081919080808, 0x080808192b081908, 0x080808192b192b08, 0x0808082b08080808, 0x0808082b0808082b, 0x0808082b082b082b, 0x0808082b2b08082b, 0x0808190808080819, 0x0808190808081908, 0x0808190808190808, 0x08081908082b0819, 0x08081908082b1908, 0x0808190819080808, 0x080819081908082b, 0x0808190819082b08, 0x08081908192b0808, 0x080819082b080819, 0x080819082b081908, 0x080819082b190808, 0x080819082b2b1908, 0x0808191908080808, 0x080819190808082b, 0x0808191908082b08, 0x08081919082b0808, 0x080819191908192b, 0x08081919192b2b19, 0x080819192b080808, 0x080819192b190819, 0x0808192b08082b19, 0x0808192b08190808, 0x0808192b19080808, 0x0808192b2b081908, 0x0808192b2b2b1908, 0x08082b0808080808, 0x08082b0808081919, 0x08082b0808082b08, 0x08082b0808191908, 0x08082b08082b2b08, 0x08082b0819080819, 0x08082b0819081908, 0x08082b0819190808, 0x08082b081919082b, 0x08082b082b082b08, 0x08082b1908081908, 0x08082b1919080808, 0x08082b2b0808082b, 0x08082b2b08191908, 0x0819080808080819, 0x0819080808081908, 0x0819080808190808, 0x08190808082b0819, 0x0819080819080808, 0x08190808192b0808, 0x081908082b081908, 0x081908082b190808, 0x081908082b191919, 0x0819081908080808, 0x0819081908082b08, 0x08190819082b0808, 0x0819081919190808, 0x0819081919192b2b, 0x081908192b080808, 0x0819082b082b1908, 0x0819082b19081919, 0x0819190808080808, 0x0819190808082b08, 0x08191908082b0808, 0x08191908082b1919, 0x0819190819082b19, 0x081919082b080808, 0x0819191908192b08, 0x08191919192b082b, 0x0819192b08080808, 0x0819192b0819192b, 0x08192b0808080819, 0x08192b0808081908, 0x08192b0808190808, 0x08192b0819080808, 0x08192b082b080819, 0x08192b1908080808, 0x08192b1908081919, 0x08192b192b2b0808, 0x08192b2b19190819, 0x082b080808080808, 0x082b08080808082b, 0x082b080808082b2b, 0x082b080819081908, 0x082b0808192b0819, 0x082b08082b080808, 0x082b08082b08082b, 0x082b0819082b2b19, 0x082b081919082b08, 0x082b082b08080808, 0x082b082b0808082b, 0x082b190808080819, 0x082b190808081908, 0x082b190808190808, 0x082b190819080808, 0x082b19081919192b, 0x082b191908080808, 0x082b191919080819, 0x082b1919192b1908, 0x082b192b2b190808, 0x082b2b0808082b08, 0x082b2b08082b0808, 0x082b2b082b191908, 0x082b2b2b19081908, 0x1908080808080819, 0x1908080808081908, 0x1908080808190808, 0x1908080808192b08, 0x19080808082b0819, 0x19080808082b1908, 0x1908080819080808, 0x1908080819082b08, 0x190808081919192b, 0x19080808192b0808, 0x190808082b080819, 0x190808082b081908, 0x190808082b190808, 0x1908081908080808, 0x19080819082b0808, 0x19080819192b0819, 0x190808192b080808, 0x190808192b081919, 0x1908082b08080819, 0x1908082b08190808, 0x1908082b19082b08, 0x1908082b1919192b, 0x1908082b192b2b08, 0x1908190808080808, 0x1908190808082b08, 0x19081908082b0808, 0x190819082b080808, 0x190819082b192b19, 0x190819190819082b, 0x19081919082b1908, 0x1908192b08080808, 0x19082b0808080819, 0x19082b0808081908, 0x19082b0808190808, 0x19082b0819080808, 0x19082b0819081919, 0x19082b1908080808, 0x19082b1919192b08, 0x19082b19192b0819, 0x19082b192b08082b, 0x19082b2b19081919, 0x19082b2b2b190808, 0x1919080808080808, 0x1919080808082b08, 0x1919080808190819, 0x1919080808192b19, 0x19190808082b0808, 0x191908082b080808, 0x191908082b082b08, 0x1919081908081908, 0x191908191908082b, 0x191908192b2b1908, 0x1919082b2b190819, 0x191919082b190808, 0x191919082b19082b, 0x1919191908082b2b, 0x1919192b08080819, 0x1919192b19191908, 0x19192b0808080808, 0x19192b0808190819, 0x19192b0808192b19, 0x19192b08192b1908, 0x19192b1919080808, 0x19192b2b08082b08, 0x192b080808081908, 0x192b080808190808, 0x192b080819080808, 0x192b0808192b2b08, 0x192b081908080808, 0x192b081919191919, 0x192b082b08192b08, 0x192b082b192b0808, 0x192b190808080808, 0x192b190808081919, 0x192b191908190808, 0x192b19190819082b, 0x192b19192b081908, 0x192b2b081908082b, 0x2b08080808080808, 0x2b0808080808082b, 0x2b08080808082b2b, 0x2b08080819080819, 0x2b0808082b08082b, 0x2b08081908081908, 0x2b08081908192b08, 0x2b08081919080808, 0x2b08082b08190819, 0x2b08190808080819, 0x2b08190808081908, 0x2b08190808190808, 0x2b08190808191919, 0x2b08190819080808, 0x2b081908192b0808, 0x2b08191908080808, 0x2b0819191908192b, 0x2b0819192b191908, 0x2b08192b08082b19, 0x2b08192b19080808, 0x2b08192b192b0808, 0x2b082b080808082b, 0x2b082b1908081908, 0x2b082b2b08190819, 0x2b19080808081908, 0x2b19080808190808, 0x2b190808082b1908, 0x2b19080819080808, 0x2b1908082b2b0819, 0x2b1908190819192b, 0x2b1908192b080808, 0x2b19082b19081919, 0x2b19190808080808, 0x2b191908082b082b, 0x2b19190819081908, 0x2b19191919190819, 0x2b192b082b080819, 0x2b192b19082b0808, 0x2b2b08080808082b, 0x2b2b080819190808, 0x2b2b08082b081919, 0x2b2b081908082b19, 0x2b2b082b08080808, 0x2b2b190808192b08, 0x2b2b2b0819190808, 0x2b2b2b1908081908, }; #define kmask_iq2xs ds4_metal_kmask_iq2xs #define ksigns_iq2xs ds4_metal_ksigns_iq2xs #define iq2xxs_grid ds4_metal_iq2xxs_grid struct block_q2_K { uchar scales[QK_K/16]; uchar qs[QK_K/4]; half d; half dmin; }; struct block_q4_K { half d; half dmin; uchar scales[12]; uchar qs[QK_K/2]; }; struct block_q5_K { half d; half dmin; uchar scales[12]; uchar qh[QK_K/8]; uchar qs[QK_K/2]; }; struct block_q6_K { uchar ql[QK_K/2]; uchar qh[QK_K/4]; char scales[QK_K/16]; half d; }; struct block_iq2_xxs { half d; ushort qs[QK_K/8]; }; struct ds4_metal_glm_routed_moe_args { uint32_t in_dim; uint32_t mid_dim; uint32_t out_dim; uint32_t n_total_expert; uint32_t n_expert_used; uint32_t n_tokens; uint32_t mid_token_stride; uint32_t down_type; /* Expert ownership under tensor parallelism: tp_world 0/1 = full * compute; otherwise each rank owns a contiguous expert range. */ int32_t tp_rank; int32_t tp_world; int32_t tp_expert_base; uint64_t gate_expert_bytes; uint64_t gate_row_bytes; uint64_t up_expert_bytes; uint64_t up_row_bytes; uint64_t down_expert_bytes; uint64_t down_row_bytes; }; static inline bool ds4_tp_owns_expert(int expert, int n_total, int tp_rank, int tp_world) { if (tp_world <= 1) return true; const int first = tp_rank * (n_total / tp_world); const int last = tp_rank + 1 == tp_world ? n_total : first + n_total / tp_world; return expert >= first && expert < last; } struct ds4_metal_dsv4_moe_swiglu_weight_args { uint32_t width; uint32_t rows; uint64_t gate_row_stride; uint64_t up_row_stride; uint64_t mid_row_stride; uint64_t weight_stride; uint32_t write_clamped; float clamp_value; }; struct ds4_metal_dsv4_moe_sum6_args { uint32_t width; uint32_t tokens; uint64_t src_token_stride; uint64_t dst_token_stride; }; // Routed-MoE activation for the selected experts: // clamp(gate), clamp(up), silu(gate) * up * route_weight. Normal inference // does not consume gate/up after this point, so the fast path avoids writing the // clamped intermediates back. A diagnostic env switch can restore those writes // when comparing the old multi-kernel intermediate tensors. kernel void kernel_dsv4_moe_swiglu_weight( constant ds4_metal_dsv4_moe_swiglu_weight_args &args, device char *gate, device char *up, device char *mid, device const char *weights, uint row [[threadgroup_position_in_grid]], uint tid [[thread_position_in_threadgroup]], uint ntg [[threads_per_threadgroup]]) { if (row >= args.rows) return; device float *gate_row = (device float *)(gate + (uint64_t)row * args.gate_row_stride); device float *up_row = (device float *)(up + (uint64_t)row * args.up_row_stride); device float *mid_row = (device float *)(mid + (uint64_t)row * args.mid_row_stride); device const float *w = (device const float *)(weights + (uint64_t)row * args.weight_stride); const float route_weight = w[0]; const float c = args.clamp_value; for (uint i = tid; i < args.width; i += ntg) { float g = gate_row[i]; float u = up_row[i]; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); if (args.write_clamped != 0) { gate_row[i] = g; up_row[i] = u; } } const float silu = g / (1.0f + exp(-g)); mid_row[i] = silu * u * route_weight; } } // Same routed-MoE activation as above, but stores the down-projection input in // half precision. The grouped matmul path converts F32 activations to half // before MMA anyway, so this cuts the large mid write/read traffic without // changing the effective matmul input precision. kernel void kernel_dsv4_moe_swiglu_weight_f16( constant ds4_metal_dsv4_moe_swiglu_weight_args &args, device char *gate, device char *up, device char *mid, device const char *weights, uint row [[threadgroup_position_in_grid]], uint tid [[thread_position_in_threadgroup]], uint ntg [[threads_per_threadgroup]]) { if (row >= args.rows) return; device float *gate_row = (device float *)(gate + (uint64_t)row * args.gate_row_stride); device float *up_row = (device float *)(up + (uint64_t)row * args.up_row_stride); device half *mid_row = (device half *)(mid + (uint64_t)row * args.mid_row_stride); device const float *w = (device const float *)(weights + (uint64_t)row * args.weight_stride); const float route_weight = w[0]; const float c = args.clamp_value; for (uint i = tid; i < args.width; i += ntg) { float g = gate_row[i]; float u = up_row[i]; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); if (args.write_clamped != 0) { gate_row[i] = g; up_row[i] = u; } } const float silu = g / (1.0f + exp(-g)); mid_row[i] = (half)(silu * u * route_weight); } } kernel void kernel_dsv4_moe_sum6_f32( constant ds4_metal_dsv4_moe_sum6_args &args, device const char *src, device char *dst, uint token[[threadgroup_position_in_grid]], uint tid[[thread_position_in_threadgroup]], uint ntg[[threads_per_threadgroup]]) { if (token >= args.tokens) return; device const float *s = (device const float *)(src + (uint64_t)token * args.src_token_stride); device float *d = (device float *)(dst + (uint64_t)token * args.dst_token_stride); for (uint col = tid; col < args.width; col += ntg) { float v = s[col]; v += s[args.width + col]; v += s[2u * args.width + col]; v += s[3u * args.width + col]; v += s[4u * args.width + col]; v += s[5u * args.width + col]; d[col] = v; } } kernel void kernel_dsv4_moe_sum8_f32( constant ds4_metal_dsv4_moe_sum6_args &args, device const char *src, device char *dst, uint token[[threadgroup_position_in_grid]], uint tid[[thread_position_in_threadgroup]], uint ntg[[threads_per_threadgroup]]) { if (token >= args.tokens) return; device const float *s = (device const float *)(src + (uint64_t)token * args.src_token_stride); device float *d = (device float *)(dst + (uint64_t)token * args.dst_token_stride); for (uint col = tid; col < args.width; col += ntg) { float v = s[col]; v += s[args.width + col]; v += s[2u * args.width + col]; v += s[3u * args.width + col]; v += s[4u * args.width + col]; v += s[5u * args.width + col]; v += s[6u * args.width + col]; v += s[7u * args.width + col]; d[col] = v; } } template void dequantize_q2_K(device const block_q2_K *xb, short il, thread type4x4 & reg) { const float d = xb->d; const float min = xb->dmin; device const uint8_t * q = (device const uint8_t *)xb->qs; float dl, ml; uint8_t sc = xb->scales[il]; q = q + 32*(il/8) + 16*(il&1); il = (il/2)%4; half coef = il>1 ? (il>2 ? 1/64.h : 1/16.h) : (il>0 ? 1/4.h : 1.h); uchar mask = il>1 ? (il>2 ? 192 : 48) : (il>0 ? 12 : 3); dl = d * (sc & 0xF) * coef, ml = min * (sc >> 4); for (int i = 0; i < 16; ++i) { reg[i/4][i%4] = dl * (q[i] & mask) - ml; } } static inline float ds4_glm_q2_K_value(device const block_q2_K *blocks, uint k) { const uint block = k / QK_K; const uint idx = k - block * QK_K; device const block_q2_K *xb = blocks + block; const uint group = idx / 16u; const uint l = idx - group * 16u; const uint q_base = 32u * (group / 8u) + 16u * (group & 1u); const uint shift = ((group / 2u) & 3u) * 2u; const uint q = ((uint)xb->qs[q_base + l] >> shift) & 0x03u; const uint sc = (uint)xb->scales[group]; return (float)xb->d * (float)(sc & 0x0fu) * (float)q - (float)xb->dmin * (float)(sc >> 4u); } static inline uchar2 get_scale_min_k4_just2(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] & 0xF) | ((q[j-4+k] & 0xc0) >> 2)), uchar((q[j+4+k] >> 4) | ((q[j-0+k] & 0xc0) >> 2))}; } static inline float ds4_glm_q4_K_value(device const block_q4_K *blocks, uint k) { const uint block = k / QK_K; const uint idx = k - block * QK_K; device const block_q4_K *xb = blocks + block; const uint group = idx / 32u; const uint l = idx - group * 32u; const uchar2 sm = get_scale_min_k4_just2((int)group, 0, xb->scales); const uint byte_off = (group >> 1u) * 32u + l; const uint shift = (group & 1u) * 4u; const uint q = (xb->qs[byte_off] >> shift) & 0x0Fu; return (float)xb->d * (float)sm.x * (float)q - (float)xb->dmin * (float)sm.y; } static inline float ds4_glm_q5_K_value(device const block_q5_K *blocks, uint k) { const uint block = k / QK_K; const uint idx = k - block * QK_K; device const block_q5_K *xb = blocks + block; const uint group = idx / 32u; const uint l = idx - group * 32u; const uchar2 sm = get_scale_min_k4_just2((int)group, 0, xb->scales); const uint ql_base = (group >> 1u) * 32u + l; const uint shift = (group & 1u) * 4u; uint q = (xb->qs[ql_base] >> shift) & 0x0Fu; q += (xb->qh[l] & (uchar)(1u << group)) ? 16u : 0u; return (float)xb->d * (float)sm.x * (float)q - (float)xb->dmin * (float)sm.y; } static inline float ds4_glm_q6_K_value(device const block_q6_K *blocks, uint k) { const uint block = k / QK_K; const uint idx = k - block * QK_K; device const block_q6_K *xb = blocks + block; const uint n128 = idx >> 7u; const uint r = idx & 127u; const uint l = r & 31u; const uint quarter = r >> 5u; const uint ql_base = n128 * 64u; const uint qh_base = n128 * 32u; const uint sc_base = n128 * 8u; uint q; int sc; if (quarter == 0u) { q = (xb->ql[ql_base + l] & 0x0Fu) | (((xb->qh[qh_base + l] >> 0u) & 3u) << 4u); sc = (int)xb->scales[sc_base + l / 16u + 0u]; } else if (quarter == 1u) { q = (xb->ql[ql_base + 32u + l] & 0x0Fu) | (((xb->qh[qh_base + l] >> 2u) & 3u) << 4u); sc = (int)xb->scales[sc_base + l / 16u + 2u]; } else if (quarter == 2u) { q = (xb->ql[ql_base + l] >> 4u) | (((xb->qh[qh_base + l] >> 4u) & 3u) << 4u); sc = (int)xb->scales[sc_base + l / 16u + 4u]; } else { q = (xb->ql[ql_base + 32u + l] >> 4u) | (((xb->qh[qh_base + l] >> 6u) & 3u) << 4u); sc = (int)xb->scales[sc_base + l / 16u + 6u]; } return (float)xb->d * (float)sc * (float)((int)q - 32); } kernel void kernel_glm_q4_K_pair_swiglu_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate, device const char *up, device const float *x, device const int32_t *selected, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], uint tid [[thread_index_in_threadgroup]]) { const uint ntg = 256u; const uint row = tgpig.x; const uint slot = tgpig.y; const uint token = tgpig.z; if (row >= args.mid_dim || slot >= args.n_expert_used || token >= args.n_tokens) return; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; const uint64_t mid_off = (uint64_t)token * args.mid_token_stride + (uint64_t)slot * args.mid_dim + row; const int expert = selected[selected_off]; if (expert < 0 || (uint)expert >= args.n_total_expert) { if (tid == 0u) mid[mid_off] = 0.0f; return; } device const block_q4_K *gate_row = (device const block_q4_K *)(gate + (uint64_t)(uint)expert * args.gate_expert_bytes + (uint64_t)row * args.gate_row_bytes); device const block_q4_K *up_row = (device const block_q4_K *)(up + (uint64_t)(uint)expert * args.up_expert_bytes + (uint64_t)row * args.up_row_bytes); float acc_gate = 0.0f; float acc_up = 0.0f; device const float *token_x = x + (uint64_t)token * args.in_dim; for (uint k = tid; k < args.in_dim; k += ntg) { const float xv = token_x[k]; acc_gate += ds4_glm_q4_K_value(gate_row, k) * xv; acc_up += ds4_glm_q4_K_value(up_row, k) * xv; } scratch[tid] = acc_gate; scratch[ntg + tid] = acc_up; threadgroup_barrier(mem_flags::mem_threadgroup); for (uint stride = ntg >> 1u; stride > 0u; stride >>= 1u) { if (tid < stride) { scratch[tid] += scratch[tid + stride]; scratch[ntg + tid] += scratch[ntg + tid + stride]; } threadgroup_barrier(mem_flags::mem_threadgroup); } if (tid == 0u) { const float g = scratch[0]; const float u = scratch[ntg]; const float sw = g / (1.0f + exp(-g)); mid[mid_off] = sw * u * weights[selected_off]; } } template static inline void glm_q2_K_pair_swiglu_simd_f32_impl( ds4_metal_glm_routed_moe_args args, device const char *gate, device const char *up, device const float *x, device const float *weights, device float *mid, threadgroup float *scratch, uint3 tgpig, uint slot, uint token, uint64_t selected_off, int expert, ushort tiisg, ushort sgitg) { const short NSG = 2; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0; if (row0 >= args.mid_dim || slot >= args.n_expert_used || token >= args.n_tokens) return; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride + (uint64_t)slot * args.mid_dim; if (expert < 0 || (uint)expert >= args.n_total_expert) { if (tiisg == 0u) { for (short row = 0; row < N_R0 && row0 + (uint)row < args.mid_dim; row++) { mid[mid_base + row0 + (uint)row] = 0.0f; } } return; } const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const short is = (8 * ir) / 16; const int nb = args.in_dim / QK_K; const uint64_t expert_gate = (uint64_t)(uint)expert * args.gate_expert_bytes; const uint64_t expert_up = (uint64_t)(uint)expert * args.up_expert_bytes; device const block_q2_K *xg = (device const block_q2_K *)(gate + expert_gate + (uint64_t)row0 * args.gate_row_bytes); device const block_q2_K *xu = (device const block_q2_K *)(up + expert_up + (uint64_t)row0 * args.up_row_bytes); device const float *y = x + (uint64_t)token * args.in_dim; device const float *y4 = y + ix * QK_K + 128 * iq + 8 * ir; float yl[32]; float sumg[N_R0] = {0.f}; float sumu[N_R0] = {0.f}; for (int ib = ix; ib < nb; ib += 4) { float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yl[i + 16] = y4[i + 64]; sumy[2] += yl[i + 16]; yl[i + 24] = y4[i + 96]; sumy[3] += yl[i + 24]; } device const uint8_t *scg = (device const uint8_t *)xg[ib].scales + 8 * iq + is; device const uint16_t *qsg = (device const uint16_t *)xg[ib].qs + 16 * iq + 4 * ir; device const half *dhg = &xg[ib].d; device const uint8_t *scu = (device const uint8_t *)xu[ib].scales + 8 * iq + is; device const uint16_t *qsu = (device const uint16_t *)xu[ib].qs + 16 * iq + 4 * ir; device const half *dhu = &xu[ib].d; for (short row = 0; row < N_R0 && row0 + (uint)row < args.mid_dim; row++) { float4 acc1g = {0.f, 0.f, 0.f, 0.f}; float4 acc2g = {0.f, 0.f, 0.f, 0.f}; float4 acc1u = {0.f, 0.f, 0.f, 0.f}; float4 acc2u = {0.f, 0.f, 0.f, 0.f}; for (int i = 0; i < 8; i += 2) { acc1g[0] += yl[i + 0] * (qsg[i / 2] & 0x0003); acc2g[0] += yl[i + 1] * (qsg[i / 2] & 0x0300); acc1g[1] += yl[i + 8] * (qsg[i / 2] & 0x000c); acc2g[1] += yl[i + 9] * (qsg[i / 2] & 0x0c00); acc1g[2] += yl[i + 16] * (qsg[i / 2] & 0x0030); acc2g[2] += yl[i + 17] * (qsg[i / 2] & 0x3000); acc1g[3] += yl[i + 24] * (qsg[i / 2] & 0x00c0); acc2g[3] += yl[i + 25] * (qsg[i / 2] & 0xc000); acc1u[0] += yl[i + 0] * (qsu[i / 2] & 0x0003); acc2u[0] += yl[i + 1] * (qsu[i / 2] & 0x0300); acc1u[1] += yl[i + 8] * (qsu[i / 2] & 0x000c); acc2u[1] += yl[i + 9] * (qsu[i / 2] & 0x0c00); acc1u[2] += yl[i + 16] * (qsu[i / 2] & 0x0030); acc2u[2] += yl[i + 17] * (qsu[i / 2] & 0x3000); acc1u[3] += yl[i + 24] * (qsu[i / 2] & 0x00c0); acc2u[3] += yl[i + 25] * (qsu[i / 2] & 0xc000); } const float dg = dhg[0]; const float mg = dhg[1] * 1.f / 16.f; sumg[row] += dg * ((acc1g[0] + 1.f / 256.f * acc2g[0]) * (scg[0] & 0xF) * 1.f / 1.f + (acc1g[1] + 1.f / 256.f * acc2g[1]) * (scg[2] & 0xF) * 1.f / 4.f + (acc1g[2] + 1.f / 256.f * acc2g[2]) * (scg[4] & 0xF) * 1.f / 16.f + (acc1g[3] + 1.f / 256.f * acc2g[3]) * (scg[6] & 0xF) * 1.f / 64.f) - mg * (sumy[0] * (scg[0] & 0xF0) + sumy[1] * (scg[2] & 0xF0) + sumy[2] * (scg[4] & 0xF0) + sumy[3] * (scg[6] & 0xF0)); const float du = dhu[0]; const float mu = dhu[1] * 1.f / 16.f; sumu[row] += du * ((acc1u[0] + 1.f / 256.f * acc2u[0]) * (scu[0] & 0xF) * 1.f / 1.f + (acc1u[1] + 1.f / 256.f * acc2u[1]) * (scu[2] & 0xF) * 1.f / 4.f + (acc1u[2] + 1.f / 256.f * acc2u[2]) * (scu[4] & 0xF) * 1.f / 16.f + (acc1u[3] + 1.f / 256.f * acc2u[3]) * (scu[6] & 0xF) * 1.f / 64.f) - mu * (sumy[0] * (scu[0] & 0xF0) + sumy[1] * (scu[2] & 0xF0) + sumy[2] * (scu[4] & 0xF0) + sumy[3] * (scu[6] & 0xF0)); qsg += args.gate_row_bytes / 2; scg += args.gate_row_bytes; dhg += args.gate_row_bytes / 2; qsu += args.up_row_bytes / 2; scu += args.up_row_bytes; dhu += args.up_row_bytes / 2; } y4 += 4 * QK_K; } for (short row = 0; row < N_R0 && row0 + (uint)row < args.mid_dim; row++) { const float g = simd_sum(sumg[row]); const float u = simd_sum(sumu[row]); if (tiisg == 0u) { const float sw = g / (1.0f + exp(-g)); mid[mid_base + row0 + (uint)row] = sw * u * weights[selected_off]; } } (void)scratch; } kernel void kernel_glm_q2_K_pair_swiglu_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate, device const char *up, device const float *x, device const int32_t *selected, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; const int expert = selected[selected_off]; if (!ds4_tp_owns_expert(expert, args.n_total_expert, args.tp_rank, args.tp_world)) return; glm_q2_K_pair_swiglu_simd_f32_impl( args, gate, up, x, weights, mid, scratch, tgpig, slot, token, selected_off, expert - args.tp_expert_base, tiisg, sgitg); } kernel void kernel_glm_q2_K_addr_pair_swiglu2_f32( constant ds4_metal_glm_routed_moe_args &args, device const uint64_t *gate_addrs, device const uint64_t *up_addrs, device const float *x, device const int32_t *selected, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; const int expert = selected[selected_off]; const short NSG = 2; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_GLM_Q2_PAIR2_K; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride + (uint64_t)slot * args.mid_dim; if (row0 >= args.mid_dim) return; if (expert < 0 || (uint)expert >= args.n_total_expert) { if (tiisg == 0u) { for (short row = 0; row < N_R0_GLM_Q2_PAIR2_K && row0 + (uint)row < args.mid_dim; row++) { mid[mid_base + row0 + (uint)row] = 0.0f; } } return; } const uint64_t gate_addr = gate_addrs[(uint)expert]; const uint64_t up_addr = up_addrs[(uint)expert]; if (gate_addr == 0 || up_addr == 0) { if (tiisg == 0u) { for (short row = 0; row < N_R0_GLM_Q2_PAIR2_K && row0 + (uint)row < args.mid_dim; row++) { mid[mid_base + row0 + (uint)row] = 0.0f; } } return; } ds4_metal_glm_routed_moe_args local = args; local.n_total_expert = 1; local.gate_expert_bytes = 0; local.up_expert_bytes = 0; glm_q2_K_pair_swiglu_simd_f32_impl( local, reinterpret_cast(gate_addr), reinterpret_cast(up_addr), x, weights, mid, scratch, tgpig, slot, token, selected_off, 0, tiisg, sgitg); } kernel void kernel_glm_q2_K_addr_pair_swiglu2_f32_masked( constant ds4_metal_glm_routed_moe_args &args, constant uint32_t &active_mask, device const uint64_t *gate_addrs, device const uint64_t *up_addrs, device const float *x, device const int32_t *selected, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; if ((active_mask & (1u << slot)) == 0u) return; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; const int expert = selected[selected_off]; const short NSG = 2; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_GLM_Q2_PAIR2_K; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride + (uint64_t)slot * args.mid_dim; if (row0 >= args.mid_dim) return; if (expert < 0 || (uint)expert >= args.n_total_expert) { if (tiisg == 0u) { for (short row = 0; row < N_R0_GLM_Q2_PAIR2_K && row0 + (uint)row < args.mid_dim; row++) { mid[mid_base + row0 + (uint)row] = 0.0f; } } return; } const uint64_t gate_addr = gate_addrs[(uint)expert]; const uint64_t up_addr = up_addrs[(uint)expert]; if (gate_addr == 0 || up_addr == 0) { if (tiisg == 0u) { for (short row = 0; row < N_R0_GLM_Q2_PAIR2_K && row0 + (uint)row < args.mid_dim; row++) { mid[mid_base + row0 + (uint)row] = 0.0f; } } return; } ds4_metal_glm_routed_moe_args local = args; local.n_total_expert = 1; local.gate_expert_bytes = 0; local.up_expert_bytes = 0; glm_q2_K_pair_swiglu_simd_f32_impl( local, reinterpret_cast(gate_addr), reinterpret_cast(up_addr), x, weights, mid, scratch, tgpig, slot, token, selected_off, 0, tiisg, sgitg); } template static inline void glm_q4_K_pair_swiglu_simd_f32_impl( ds4_metal_glm_routed_moe_args args, device const char *gate, device const char *up, device const float *x, device const float *weights, device float *mid, threadgroup float *scratch, uint3 tgpig, uint slot, uint token, uint64_t selected_off, int expert, ushort tiisg, ushort sgitg) { const short NSG = 2; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0; if (row0 >= args.mid_dim || slot >= args.n_expert_used || token >= args.n_tokens) return; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride + (uint64_t)slot * args.mid_dim; if (expert < 0 || (uint)expert >= args.n_total_expert) { if (tiisg == 0u) { for (short row = 0; row < N_R0 && row0 + (uint)row < args.mid_dim; row++) { mid[mid_base + row0 + (uint)row] = 0.0f; } } return; } const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const int nb = args.in_dim / QK_K; const uint64_t expert_gate = (uint64_t)(uint)expert * args.gate_expert_bytes; const uint64_t expert_up = (uint64_t)(uint)expert * args.up_expert_bytes; device const block_q4_K *xg = (device const block_q4_K *)(gate + expert_gate + (uint64_t)row0 * args.gate_row_bytes); device const block_q4_K *xu = (device const block_q4_K *)(up + expert_up + (uint64_t)row0 * args.up_row_bytes); device const float *y = x + (uint64_t)token * args.in_dim; device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; float sumg[N_R0] = {0.f}; float sumu[N_R0] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *scg = (device const uint16_t *)xg[ib].scales + iq; device const uint16_t *qg1 = (device const uint16_t *)xg[ib].qs + 16 * iq + 4 * ir; device const half *dhg = &xg[ib].d; device const uint16_t *scu = (device const uint16_t *)xu[ib].scales + iq; device const uint16_t *qu1 = (device const uint16_t *)xu[ib].qs + 16 * iq + 4 * ir; device const half *dhu = &xu[ib].d; for (short row = 0; row < N_R0 && row0 + (uint)row < args.mid_dim; row++) { sc16[0] = scg[0] & kmask1; sc16[1] = scg[2] & kmask1; sc16[2] = ((scg[4] >> 0) & kmask2) | ((scg[0] & kmask3) >> 2); sc16[3] = ((scg[4] >> 4) & kmask2) | ((scg[2] & kmask3) >> 2); device const uint16_t *qg2 = qg1 + 32; float4 acc1g = {0.f, 0.f, 0.f, 0.f}; float4 acc2g = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1g[0] += yl[2 * i + 0] * (qg1[i] & 0x000F); acc1g[1] += yl[2 * i + 1] * (qg1[i] & 0x0F00); acc1g[2] += yl[2 * i + 8] * (qg1[i] & 0x00F0); acc1g[3] += yl[2 * i + 9] * (qg1[i] & 0xF000); acc2g[0] += yh[2 * i + 0] * (qg2[i] & 0x000F); acc2g[1] += yh[2 * i + 1] * (qg2[i] & 0x0F00); acc2g[2] += yh[2 * i + 8] * (qg2[i] & 0x00F0); acc2g[3] += yh[2 * i + 9] * (qg2[i] & 0xF000); } sumg[row] += dhg[0] * ((acc1g[0] + 1.f / 256.f * acc1g[1]) * sc8[0] + (acc1g[2] + 1.f / 256.f * acc1g[3]) * sc8[1] * 1.f / 16.f + (acc2g[0] + 1.f / 256.f * acc2g[1]) * sc8[4] + (acc2g[2] + 1.f / 256.f * acc2g[3]) * sc8[5] * 1.f / 16.f) - dhg[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); sc16[0] = scu[0] & kmask1; sc16[1] = scu[2] & kmask1; sc16[2] = ((scu[4] >> 0) & kmask2) | ((scu[0] & kmask3) >> 2); sc16[3] = ((scu[4] >> 4) & kmask2) | ((scu[2] & kmask3) >> 2); device const uint16_t *qu2 = qu1 + 32; float4 acc1u = {0.f, 0.f, 0.f, 0.f}; float4 acc2u = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1u[0] += yl[2 * i + 0] * (qu1[i] & 0x000F); acc1u[1] += yl[2 * i + 1] * (qu1[i] & 0x0F00); acc1u[2] += yl[2 * i + 8] * (qu1[i] & 0x00F0); acc1u[3] += yl[2 * i + 9] * (qu1[i] & 0xF000); acc2u[0] += yh[2 * i + 0] * (qu2[i] & 0x000F); acc2u[1] += yh[2 * i + 1] * (qu2[i] & 0x0F00); acc2u[2] += yh[2 * i + 8] * (qu2[i] & 0x00F0); acc2u[3] += yh[2 * i + 9] * (qu2[i] & 0xF000); } sumu[row] += dhu[0] * ((acc1u[0] + 1.f / 256.f * acc1u[1]) * sc8[0] + (acc1u[2] + 1.f / 256.f * acc1u[3]) * sc8[1] * 1.f / 16.f + (acc2u[0] + 1.f / 256.f * acc2u[1]) * sc8[4] + (acc2u[2] + 1.f / 256.f * acc2u[3]) * sc8[5] * 1.f / 16.f) - dhu[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); qg1 += args.gate_row_bytes / 2; scg += args.gate_row_bytes / 2; dhg += args.gate_row_bytes / 2; qu1 += args.up_row_bytes / 2; scu += args.up_row_bytes / 2; dhu += args.up_row_bytes / 2; } y4 += 4 * QK_K; } for (int row = 0; row < N_R0 && row0 + (uint)row < args.mid_dim; ++row) { const float g = simd_sum(sumg[row]); const float u = simd_sum(sumu[row]); if (tiisg == 0u) { const float sw = g / (1.0f + exp(-g)); mid[mid_base + row0 + (uint)row] = sw * u * weights[selected_off]; } } (void)scratch; } kernel void kernel_glm_q4_K_pair_swiglu2_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate, device const char *up, device const float *x, device const int32_t *selected, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; const int expert = selected[selected_off]; glm_q4_K_pair_swiglu_simd_f32_impl( args, gate, up, x, weights, mid, scratch, tgpig, slot, token, selected_off, expert, tiisg, sgitg); } kernel void kernel_glm_q4_K_addr_pair_swiglu_f32( constant ds4_metal_glm_routed_moe_args &args, device const uint64_t *gate_addrs, device const uint64_t *up_addrs, device const float *x, device const int32_t *selected, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; const int expert = selected[selected_off]; const short NSG = 2; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_Q4_K; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride + (uint64_t)slot * args.mid_dim; if (row0 >= args.mid_dim) return; if (expert < 0 || (uint)expert >= args.n_total_expert) { if (tiisg == 0u) { for (short row = 0; row < N_R0_Q4_K && row0 + (uint)row < args.mid_dim; row++) { mid[mid_base + row0 + (uint)row] = 0.0f; } } return; } const uint64_t gate_addr = gate_addrs[(uint)expert]; const uint64_t up_addr = up_addrs[(uint)expert]; if (gate_addr == 0 || up_addr == 0) { if (tiisg == 0u) { for (short row = 0; row < N_R0_Q4_K && row0 + (uint)row < args.mid_dim; row++) { mid[mid_base + row0 + (uint)row] = 0.0f; } } return; } ds4_metal_glm_routed_moe_args local = args; local.n_total_expert = 1; local.gate_expert_bytes = 0; local.up_expert_bytes = 0; glm_q4_K_pair_swiglu_simd_f32_impl( local, reinterpret_cast(gate_addr), reinterpret_cast(up_addr), x, weights, mid, scratch, tgpig, slot, token, selected_off, 0, tiisg, sgitg); } kernel void kernel_glm_q4_K_addr_pair_swiglu_f32_masked( constant ds4_metal_glm_routed_moe_args &args, constant uint32_t &active_mask, device const uint64_t *gate_addrs, device const uint64_t *up_addrs, device const float *x, device const int32_t *selected, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; if ((active_mask & (1u << slot)) == 0u) return; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; const int expert = selected[selected_off]; const short NSG = 2; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_Q4_K; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride + (uint64_t)slot * args.mid_dim; if (row0 >= args.mid_dim) return; if (expert < 0 || (uint)expert >= args.n_total_expert) { if (tiisg == 0u) { for (short row = 0; row < N_R0_Q4_K && row0 + (uint)row < args.mid_dim; row++) { mid[mid_base + row0 + (uint)row] = 0.0f; } } return; } const uint64_t gate_addr = gate_addrs[(uint)expert]; const uint64_t up_addr = up_addrs[(uint)expert]; if (gate_addr == 0 || up_addr == 0) { if (tiisg == 0u) { for (short row = 0; row < N_R0_Q4_K && row0 + (uint)row < args.mid_dim; row++) { mid[mid_base + row0 + (uint)row] = 0.0f; } } return; } ds4_metal_glm_routed_moe_args local = args; local.n_total_expert = 1; local.gate_expert_bytes = 0; local.up_expert_bytes = 0; glm_q4_K_pair_swiglu_simd_f32_impl( local, reinterpret_cast(gate_addr), reinterpret_cast(up_addr), x, weights, mid, scratch, tgpig, slot, token, selected_off, 0, tiisg, sgitg); } kernel void kernel_glm_q4_K_slots6_pair_swiglu_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate0, device const char *gate1, device const char *gate2, device const char *gate3, device const char *gate4, device const char *gate5, device const char *up0, device const char *up1, device const char *up2, device const char *up3, device const char *up4, device const char *up5, device const float *x, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; device const char *gate_cur = gate0; device const char *up_cur = up0; switch (slot) { case 1: gate_cur = gate1; up_cur = up1; break; case 2: gate_cur = gate2; up_cur = up2; break; case 3: gate_cur = gate3; up_cur = up3; break; case 4: gate_cur = gate4; up_cur = up4; break; case 5: gate_cur = gate5; up_cur = up5; break; default: break; } const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; glm_q4_K_pair_swiglu_simd_f32_impl( args, gate_cur, up_cur, x, weights, mid, scratch, tgpig, slot, token, selected_off, 0, tiisg, sgitg); } kernel void kernel_glm_q4_K_slots8_pair_swiglu_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate0, device const char *gate1, device const char *gate2, device const char *gate3, device const char *gate4, device const char *gate5, device const char *gate6, device const char *gate7, device const char *up0, device const char *up1, device const char *up2, device const char *up3, device const char *up4, device const char *up5, device const char *up6, device const char *up7, device const float *x, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; device const char *gate_cur = gate0; device const char *up_cur = up0; switch (slot) { case 1: gate_cur = gate1; up_cur = up1; break; case 2: gate_cur = gate2; up_cur = up2; break; case 3: gate_cur = gate3; up_cur = up3; break; case 4: gate_cur = gate4; up_cur = up4; break; case 5: gate_cur = gate5; up_cur = up5; break; case 6: gate_cur = gate6; up_cur = up6; break; case 7: gate_cur = gate7; up_cur = up7; break; default: break; } const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; glm_q4_K_pair_swiglu_simd_f32_impl( args, gate_cur, up_cur, x, weights, mid, scratch, tgpig, slot, token, selected_off, 0, tiisg, sgitg); } kernel void kernel_glm_q4_K_slots6_pair_swiglu4_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate0, device const char *gate1, device const char *gate2, device const char *gate3, device const char *gate4, device const char *gate5, device const char *up0, device const char *up1, device const char *up2, device const char *up3, device const char *up4, device const char *up5, device const float *x, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; device const char *gate_cur = gate0; device const char *up_cur = up0; switch (slot) { case 1: gate_cur = gate1; up_cur = up1; break; case 2: gate_cur = gate2; up_cur = up2; break; case 3: gate_cur = gate3; up_cur = up3; break; case 4: gate_cur = gate4; up_cur = up4; break; case 5: gate_cur = gate5; up_cur = up5; break; default: break; } const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; glm_q4_K_pair_swiglu_simd_f32_impl( args, gate_cur, up_cur, x, weights, mid, scratch, tgpig, slot, token, selected_off, 0, tiisg, sgitg); } kernel void kernel_glm_q4_K_slots8_pair_swiglu4_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate0, device const char *gate1, device const char *gate2, device const char *gate3, device const char *gate4, device const char *gate5, device const char *gate6, device const char *gate7, device const char *up0, device const char *up1, device const char *up2, device const char *up3, device const char *up4, device const char *up5, device const char *up6, device const char *up7, device const float *x, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; device const char *gate_cur = gate0; device const char *up_cur = up0; switch (slot) { case 1: gate_cur = gate1; up_cur = up1; break; case 2: gate_cur = gate2; up_cur = up2; break; case 3: gate_cur = gate3; up_cur = up3; break; case 4: gate_cur = gate4; up_cur = up4; break; case 5: gate_cur = gate5; up_cur = up5; break; case 6: gate_cur = gate6; up_cur = up6; break; case 7: gate_cur = gate7; up_cur = up7; break; default: break; } const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; glm_q4_K_pair_swiglu_simd_f32_impl( args, gate_cur, up_cur, x, weights, mid, scratch, tgpig, slot, token, selected_off, 0, tiisg, sgitg); } kernel void kernel_glm_q4_K_pair_swiglu4_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate, device const char *up, device const float *x, device const int32_t *selected, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; const int expert = selected[selected_off]; glm_q4_K_pair_swiglu_simd_f32_impl( args, gate, up, x, weights, mid, scratch, tgpig, slot, token, selected_off, expert, tiisg, sgitg); } kernel void kernel_glm_q4_K_pair_swiglu2_mapped_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate, device const char *up, device const float *x, device const uint32_t *htpe, device const int32_t *hids, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint expert = tgpig.z; if (expert >= args.n_total_expert) return; const uint count = htpe[expert]; const uint map_base = tgpig.y * 32u; for (uint i = 0; i < 32u; i++) { const uint map_row = map_base + i; if (map_row >= count) break; const int id = hids[(uint64_t)expert * args.n_tokens + map_row]; if (id < 0) continue; const uint token = (uint)id / args.n_expert_used; const uint slot = (uint)id - token * args.n_expert_used; if (slot >= args.n_expert_used || token >= args.n_tokens) continue; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; glm_q4_K_pair_swiglu_simd_f32_impl( args, gate, up, x, weights, mid, scratch, tgpig, slot, token, selected_off, (int)expert, tiisg, sgitg); } } kernel void kernel_glm_q4_K_pair_swiglu2_mapped_row_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate, device const char *up, device const float *x, device const uint32_t *htpe, device const int32_t *hids, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint expert = tgpig.z; const uint map_row = tgpig.y; if (expert >= args.n_total_expert || map_row >= htpe[expert]) return; const int id = hids[(uint64_t)expert * args.n_tokens + map_row]; if (id < 0) return; const uint token = (uint)id / args.n_expert_used; const uint slot = (uint)id - token * args.n_expert_used; if (slot >= args.n_expert_used || token >= args.n_tokens) return; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; glm_q4_K_pair_swiglu_simd_f32_impl( args, gate, up, x, weights, mid, scratch, tgpig, slot, token, selected_off, (int)expert, tiisg, sgitg); } static inline void glm_q5_K_pair_swiglu_f32_impl( constant ds4_metal_glm_routed_moe_args &args, device const char *gate, device const char *up, device const float *x, device const float *weights, device float *mid, threadgroup float *scratch, uint3 tgpig, uint slot, uint token, uint64_t selected_off, int expert, ushort tiisg, ushort sgitg) { const short NSG = 2; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_Q5_PAIR_K; if (row0 >= args.mid_dim || slot >= args.n_expert_used || token >= args.n_tokens) return; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride + (uint64_t)slot * args.mid_dim; if (expert < 0 || (uint)expert >= args.n_total_expert) { if (tiisg == 0u) { for (short row = 0; row < N_R0_Q5_PAIR_K && row0 + (uint)row < args.mid_dim; row++) { mid[mid_base + row0 + (uint)row] = 0.0f; } } return; } const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const uint bit0 = 2u * (uint)iq; const uint bit1 = bit0 + 1u; const uint bit2 = bit0 + 4u; const uint bit3 = bit0 + 5u; const int nb = args.in_dim / QK_K; const uint64_t expert_gate = (uint64_t)(uint)expert * args.gate_expert_bytes; const uint64_t expert_up = (uint64_t)(uint)expert * args.up_expert_bytes; device const block_q5_K *gate_rows = (device const block_q5_K *)(gate + expert_gate + (uint64_t)row0 * args.gate_row_bytes); device const block_q5_K *up_rows = (device const block_q5_K *)(up + expert_up + (uint64_t)row0 * args.up_row_bytes); device const float *y = x + (uint64_t)token * args.in_dim; device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; float sumg[N_R0_Q5_PAIR_K] = {0.f}; float sumu[N_R0_Q5_PAIR_K] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *scg = (device const uint16_t *)gate_rows[ib].scales + iq; device const uint16_t *qg1 = (device const uint16_t *)gate_rows[ib].qs + 16 * iq + 4 * ir; device const uint16_t *qhg = (device const uint16_t *)gate_rows[ib].qh + 4 * ir; device const half *dhg = &gate_rows[ib].d; device const uint16_t *scu = (device const uint16_t *)up_rows[ib].scales + iq; device const uint16_t *qu1 = (device const uint16_t *)up_rows[ib].qs + 16 * iq + 4 * ir; device const uint16_t *qhu = (device const uint16_t *)up_rows[ib].qh + 4 * ir; device const half *dhu = &up_rows[ib].d; for (short row = 0; row < N_R0_Q5_PAIR_K && row0 + (uint)row < args.mid_dim; row++) { sc16[0] = scg[0] & kmask1; sc16[1] = scg[2] & kmask1; sc16[2] = ((scg[4] >> 0) & kmask2) | ((scg[0] & kmask3) >> 2); sc16[3] = ((scg[4] >> 4) & kmask2) | ((scg[2] & kmask3) >> 2); device const uint16_t *qg2 = qg1 + 32; float4 accg = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { const uint ql1 = (uint)qg1[i]; const uint ql2 = (uint)qg2[i]; const uint hb = (uint)qhg[i]; accg[0] += yl[2 * i + 0] * (float)((ql1 & 0x000Fu) + (((hb >> bit0) & 1u) << 4u)) + yl[2 * i + 1] * (float)(((ql1 >> 8u) & 0x000Fu) + (((hb >> (bit0 + 8u)) & 1u) << 4u)); accg[1] += yl[2 * i + 8] * (float)(((ql1 >> 4u) & 0x000Fu) + (((hb >> bit1) & 1u) << 4u)) + yl[2 * i + 9] * (float)(((ql1 >> 12u) & 0x000Fu) + (((hb >> (bit1 + 8u)) & 1u) << 4u)); accg[2] += yh[2 * i + 0] * (float)((ql2 & 0x000Fu) + (((hb >> bit2) & 1u) << 4u)) + yh[2 * i + 1] * (float)(((ql2 >> 8u) & 0x000Fu) + (((hb >> (bit2 + 8u)) & 1u) << 4u)); accg[3] += yh[2 * i + 8] * (float)(((ql2 >> 4u) & 0x000Fu) + (((hb >> bit3) & 1u) << 4u)) + yh[2 * i + 9] * (float)(((ql2 >> 12u) & 0x000Fu) + (((hb >> (bit3 + 8u)) & 1u) << 4u)); } sumg[row] += dhg[0] * (accg[0] * sc8[0] + accg[1] * sc8[1] + accg[2] * sc8[4] + accg[3] * sc8[5]) - dhg[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); sc16[0] = scu[0] & kmask1; sc16[1] = scu[2] & kmask1; sc16[2] = ((scu[4] >> 0) & kmask2) | ((scu[0] & kmask3) >> 2); sc16[3] = ((scu[4] >> 4) & kmask2) | ((scu[2] & kmask3) >> 2); device const uint16_t *qu2 = qu1 + 32; float4 accu = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { const uint ql1 = (uint)qu1[i]; const uint ql2 = (uint)qu2[i]; const uint hb = (uint)qhu[i]; accu[0] += yl[2 * i + 0] * (float)((ql1 & 0x000Fu) + (((hb >> bit0) & 1u) << 4u)) + yl[2 * i + 1] * (float)(((ql1 >> 8u) & 0x000Fu) + (((hb >> (bit0 + 8u)) & 1u) << 4u)); accu[1] += yl[2 * i + 8] * (float)(((ql1 >> 4u) & 0x000Fu) + (((hb >> bit1) & 1u) << 4u)) + yl[2 * i + 9] * (float)(((ql1 >> 12u) & 0x000Fu) + (((hb >> (bit1 + 8u)) & 1u) << 4u)); accu[2] += yh[2 * i + 0] * (float)((ql2 & 0x000Fu) + (((hb >> bit2) & 1u) << 4u)) + yh[2 * i + 1] * (float)(((ql2 >> 8u) & 0x000Fu) + (((hb >> (bit2 + 8u)) & 1u) << 4u)); accu[3] += yh[2 * i + 8] * (float)(((ql2 >> 4u) & 0x000Fu) + (((hb >> bit3) & 1u) << 4u)) + yh[2 * i + 9] * (float)(((ql2 >> 12u) & 0x000Fu) + (((hb >> (bit3 + 8u)) & 1u) << 4u)); } sumu[row] += dhu[0] * (accu[0] * sc8[0] + accu[1] * sc8[1] + accu[2] * sc8[4] + accu[3] * sc8[5]) - dhu[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); qg1 += args.gate_row_bytes / 2; qhg += args.gate_row_bytes / 2; scg += args.gate_row_bytes / 2; dhg += args.gate_row_bytes / 2; qu1 += args.up_row_bytes / 2; qhu += args.up_row_bytes / 2; scu += args.up_row_bytes / 2; dhu += args.up_row_bytes / 2; } y4 += 4 * QK_K; } for (short row = 0; row < N_R0_Q5_PAIR_K && row0 + (uint)row < args.mid_dim; row++) { const float g = simd_sum(sumg[row]); const float u = simd_sum(sumu[row]); if (tiisg == 0u) { const float sw = g / (1.0f + exp(-g)); mid[mid_base + row0 + (uint)row] = sw * u * weights[selected_off]; } } (void)scratch; } kernel void kernel_glm_q5_K_pair_swiglu_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate, device const char *up, device const float *x, device const int32_t *selected, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; const int expert = selected[selected_off]; glm_q5_K_pair_swiglu_f32_impl( args, gate, up, x, weights, mid, scratch, tgpig, slot, token, selected_off, expert, tiisg, sgitg); } kernel void kernel_glm_q5_K_slots6_pair_swiglu_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate0, device const char *gate1, device const char *gate2, device const char *gate3, device const char *gate4, device const char *gate5, device const char *up0, device const char *up1, device const char *up2, device const char *up3, device const char *up4, device const char *up5, device const float *x, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; device const char *gate_cur = gate0; device const char *up_cur = up0; switch (slot) { case 1: gate_cur = gate1; up_cur = up1; break; case 2: gate_cur = gate2; up_cur = up2; break; case 3: gate_cur = gate3; up_cur = up3; break; case 4: gate_cur = gate4; up_cur = up4; break; case 5: gate_cur = gate5; up_cur = up5; break; default: break; } const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; glm_q5_K_pair_swiglu_f32_impl( args, gate_cur, up_cur, x, weights, mid, scratch, tgpig, slot, token, selected_off, 0, tiisg, sgitg); } kernel void kernel_glm_q5_K_slots8_pair_swiglu_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate0, device const char *gate1, device const char *gate2, device const char *gate3, device const char *gate4, device const char *gate5, device const char *gate6, device const char *gate7, device const char *up0, device const char *up1, device const char *up2, device const char *up3, device const char *up4, device const char *up5, device const char *up6, device const char *up7, device const float *x, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint slot = tgpig.y; const uint token = tgpig.z; if (slot >= args.n_expert_used || token >= args.n_tokens) return; device const char *gate_cur = gate0; device const char *up_cur = up0; switch (slot) { case 1: gate_cur = gate1; up_cur = up1; break; case 2: gate_cur = gate2; up_cur = up2; break; case 3: gate_cur = gate3; up_cur = up3; break; case 4: gate_cur = gate4; up_cur = up4; break; case 5: gate_cur = gate5; up_cur = up5; break; case 6: gate_cur = gate6; up_cur = up6; break; case 7: gate_cur = gate7; up_cur = up7; break; default: break; } const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; glm_q5_K_pair_swiglu_f32_impl( args, gate_cur, up_cur, x, weights, mid, scratch, tgpig, slot, token, selected_off, 0, tiisg, sgitg); } kernel void kernel_glm_q5_K_pair_swiglu_mapped_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate, device const char *up, device const float *x, device const uint32_t *htpe, device const int32_t *hids, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint expert = tgpig.z; if (expert >= args.n_total_expert) return; const uint count = htpe[expert]; const uint map_base = tgpig.y * 32u; for (uint i = 0; i < 32u; i++) { const uint map_row = map_base + i; if (map_row >= count) break; const int id = hids[(uint64_t)expert * args.n_tokens + map_row]; if (id < 0) continue; const uint token = (uint)id / args.n_expert_used; const uint slot = (uint)id - token * args.n_expert_used; if (slot >= args.n_expert_used || token >= args.n_tokens) continue; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; glm_q5_K_pair_swiglu_f32_impl( args, gate, up, x, weights, mid, scratch, tgpig, slot, token, selected_off, (int)expert, tiisg, sgitg); } } kernel void kernel_glm_q5_K_pair_swiglu_mapped_row_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *gate, device const char *up, device const float *x, device const uint32_t *htpe, device const int32_t *hids, device const float *weights, device float *mid, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const uint expert = tgpig.z; const uint map_row = tgpig.y; if (expert >= args.n_total_expert || map_row >= htpe[expert]) return; const int id = hids[(uint64_t)expert * args.n_tokens + map_row]; if (id < 0) return; const uint token = (uint)id / args.n_expert_used; const uint slot = (uint)id - token * args.n_expert_used; if (slot >= args.n_expert_used || token >= args.n_tokens) return; const uint64_t selected_off = (uint64_t)token * args.n_expert_used + slot; glm_q5_K_pair_swiglu_f32_impl( args, gate, up, x, weights, mid, scratch, tgpig, slot, token, selected_off, (int)expert, tiisg, sgitg); } kernel void kernel_glm_q5_K_down_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *down, device const int32_t *selected, device const float *mid, device float *out, uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const short NSG = 2; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_Q5_K; const uint token = tgpig.y; if (row0 >= args.out_dim || token >= args.n_tokens) return; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const uint bit0 = 2u * (uint)iq; const uint bit1 = bit0 + 1u; const uint bit2 = bit0 + 4u; const uint bit3 = bit0 + 5u; const int nb = args.mid_dim / QK_K; float sumf[N_R0_Q5_K] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; const uint64_t selected_base = (uint64_t)token * args.n_expert_used; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride; for (uint slot = 0; slot < args.n_expert_used; slot++) { const int expert = selected[selected_base + slot]; if (expert < 0 || (uint)expert >= args.n_total_expert) continue; device const block_q5_K *x = (device const block_q5_K *)(down + (uint64_t)(uint)expert * args.down_expert_bytes + (uint64_t)row0 * args.down_row_bytes); device const float *y = mid + mid_base + (uint64_t)slot * args.mid_dim; device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const uint16_t *qh = (device const uint16_t *)x[ib].qh + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < N_R0_Q5_K && row0 + (uint)row < args.out_dim; row++) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { const uint ql1 = (uint)q1[i]; const uint ql2 = (uint)q2[i]; const uint hb = (uint)qh[i]; acc[0] += yl[2 * i + 0] * (float)((ql1 & 0x000Fu) + (((hb >> bit0) & 1u) << 4u)) + yl[2 * i + 1] * (float)(((ql1 >> 8u) & 0x000Fu) + (((hb >> (bit0 + 8u)) & 1u) << 4u)); acc[1] += yl[2 * i + 8] * (float)(((ql1 >> 4u) & 0x000Fu) + (((hb >> bit1) & 1u) << 4u)) + yl[2 * i + 9] * (float)(((ql1 >> 12u) & 0x000Fu) + (((hb >> (bit1 + 8u)) & 1u) << 4u)); acc[2] += yh[2 * i + 0] * (float)((ql2 & 0x000Fu) + (((hb >> bit2) & 1u) << 4u)) + yh[2 * i + 1] * (float)(((ql2 >> 8u) & 0x000Fu) + (((hb >> (bit2 + 8u)) & 1u) << 4u)); acc[3] += yh[2 * i + 8] * (float)(((ql2 >> 4u) & 0x000Fu) + (((hb >> bit3) & 1u) << 4u)) + yh[2 * i + 9] * (float)(((ql2 >> 12u) & 0x000Fu) + (((hb >> (bit3 + 8u)) & 1u) << 4u)); } sumf[row] += dh[0] * (acc[0] * sc8[0] + acc[1] * sc8[1] + acc[2] * sc8[4] + acc[3] * sc8[5]) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); q1 += args.down_row_bytes / 2; qh += args.down_row_bytes / 2; sc += args.down_row_bytes / 2; dh += args.down_row_bytes / 2; } y4 += 4 * QK_K; } } for (short row = 0; row < N_R0_Q5_K && row0 + (uint)row < args.out_dim; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0u) { out[(uint64_t)token * args.out_dim + row0 + (uint)row] = sum_all; } } } kernel void kernel_glm_q2_K_down_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *down, device const int32_t *selected, device const float *mid, device float *out, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const short NSG = 2; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_Q2_K; const uint token = tgpig.y; if (row0 >= args.out_dim || token >= args.n_tokens) return; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const short is = (8 * ir) / 16; const int nb = args.mid_dim / QK_K; float sumf[N_R0_Q2_K] = {0.f}; const uint64_t selected_base = (uint64_t)token * args.n_expert_used; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride; for (uint slot = 0; slot < args.n_expert_used; slot++) { const int expert = selected[selected_base + slot]; if (expert < 0 || (uint)expert >= args.n_total_expert) continue; if (!ds4_tp_owns_expert(expert, args.n_total_expert, args.tp_rank, args.tp_world)) continue; device const block_q2_K *x = (device const block_q2_K *)(down + (uint64_t)(uint)(expert - args.tp_expert_base) * args.down_expert_bytes + (uint64_t)row0 * args.down_row_bytes); device const float *y = mid + mid_base + (uint64_t)slot * args.mid_dim; device const float *y4 = y + ix * QK_K + 128 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[32]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yl[i + 16] = y4[i + 64]; sumy[2] += yl[i + 16]; yl[i + 24] = y4[i + 96]; sumy[3] += yl[i + 24]; } device const uint8_t *sc = (device const uint8_t *)x[ib].scales + 8 * iq + is; device const uint16_t *qs = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < N_R0_Q2_K && row0 + (uint)row < args.out_dim; row++) { float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; for (int i = 0; i < 8; i += 2) { acc1[0] += yl[i + 0] * (qs[i / 2] & 0x0003); acc2[0] += yl[i + 1] * (qs[i / 2] & 0x0300); acc1[1] += yl[i + 8] * (qs[i / 2] & 0x000c); acc2[1] += yl[i + 9] * (qs[i / 2] & 0x0c00); acc1[2] += yl[i + 16] * (qs[i / 2] & 0x0030); acc2[2] += yl[i + 17] * (qs[i / 2] & 0x3000); acc1[3] += yl[i + 24] * (qs[i / 2] & 0x00c0); acc2[3] += yl[i + 25] * (qs[i / 2] & 0xc000); } const float d = dh[0]; const float m = dh[1] * 1.f / 16.f; sumf[row] += d * ((acc1[0] + 1.f / 256.f * acc2[0]) * (sc[0] & 0xF) * 1.f / 1.f + (acc1[1] + 1.f / 256.f * acc2[1]) * (sc[2] & 0xF) * 1.f / 4.f + (acc1[2] + 1.f / 256.f * acc2[2]) * (sc[4] & 0xF) * 1.f / 16.f + (acc1[3] + 1.f / 256.f * acc2[3]) * (sc[6] & 0xF) * 1.f / 64.f) - m * (sumy[0] * (sc[0] & 0xF0) + sumy[1] * (sc[2] & 0xF0) + sumy[2] * (sc[4] & 0xF0) + sumy[3] * (sc[6] & 0xF0)); qs += args.down_row_bytes / 2; sc += args.down_row_bytes; dh += args.down_row_bytes / 2; } y4 += 4 * QK_K; } } for (short row = 0; row < N_R0_Q2_K && row0 + (uint)row < args.out_dim; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0u) { out[(uint64_t)token * args.out_dim + row0 + (uint)row] = sum_all; } } (void)scratch; } kernel void kernel_glm_q2_K_addr_down_f32( constant ds4_metal_glm_routed_moe_args &args, device const uint64_t *down_addrs, device const int32_t *selected, device const float *mid, device float *out, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const short NSG = 2; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_Q2_K; const uint token = tgpig.y; if (row0 >= args.out_dim || token >= args.n_tokens) return; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const short is = (8 * ir) / 16; const int nb = args.mid_dim / QK_K; float sumf[N_R0_Q2_K] = {0.f}; const uint64_t selected_base = (uint64_t)token * args.n_expert_used; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride; for (uint slot = 0; slot < args.n_expert_used; slot++) { const int expert = selected[selected_base + slot]; if (expert < 0 || (uint)expert >= args.n_total_expert) continue; const uint64_t down_addr = down_addrs[(uint)expert]; if (down_addr == 0) continue; device const block_q2_K *x = (device const block_q2_K *)(reinterpret_cast(down_addr) + (uint64_t)row0 * args.down_row_bytes); device const float *y = mid + mid_base + (uint64_t)slot * args.mid_dim; device const float *y4 = y + ix * QK_K + 128 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[32]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yl[i + 16] = y4[i + 64]; sumy[2] += yl[i + 16]; yl[i + 24] = y4[i + 96]; sumy[3] += yl[i + 24]; } device const uint8_t *sc = (device const uint8_t *)x[ib].scales + 8 * iq + is; device const uint16_t *qs = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < N_R0_Q2_K && row0 + (uint)row < args.out_dim; row++) { float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; for (int i = 0; i < 8; i += 2) { acc1[0] += yl[i + 0] * (qs[i / 2] & 0x0003); acc2[0] += yl[i + 1] * (qs[i / 2] & 0x0300); acc1[1] += yl[i + 8] * (qs[i / 2] & 0x000c); acc2[1] += yl[i + 9] * (qs[i / 2] & 0x0c00); acc1[2] += yl[i + 16] * (qs[i / 2] & 0x0030); acc2[2] += yl[i + 17] * (qs[i / 2] & 0x3000); acc1[3] += yl[i + 24] * (qs[i / 2] & 0x00c0); acc2[3] += yl[i + 25] * (qs[i / 2] & 0xc000); } const float d = dh[0]; const float m = dh[1] * 1.f / 16.f; sumf[row] += d * ((acc1[0] + 1.f / 256.f * acc2[0]) * (sc[0] & 0xF) * 1.f / 1.f + (acc1[1] + 1.f / 256.f * acc2[1]) * (sc[2] & 0xF) * 1.f / 4.f + (acc1[2] + 1.f / 256.f * acc2[2]) * (sc[4] & 0xF) * 1.f / 16.f + (acc1[3] + 1.f / 256.f * acc2[3]) * (sc[6] & 0xF) * 1.f / 64.f) - m * (sumy[0] * (sc[0] & 0xF0) + sumy[1] * (sc[2] & 0xF0) + sumy[2] * (sc[4] & 0xF0) + sumy[3] * (sc[6] & 0xF0)); qs += args.down_row_bytes / 2; sc += args.down_row_bytes; dh += args.down_row_bytes / 2; } y4 += 4 * QK_K; } } for (short row = 0; row < N_R0_Q2_K && row0 + (uint)row < args.out_dim; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0u) { out[(uint64_t)token * args.out_dim + row0 + (uint)row] = sum_all; } } (void)scratch; } kernel void kernel_glm_q4_K_down_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *down, device const int32_t *selected, device const float *mid, device float *out, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], uint tid [[thread_index_in_threadgroup]]) { const uint ntg = 256u; const uint row = tgpig.x; const uint token = tgpig.y; if (row >= args.out_dim || token >= args.n_tokens) return; float acc = 0.0f; const uint64_t selected_base = (uint64_t)token * args.n_expert_used; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride; for (uint slot = 0; slot < args.n_expert_used; slot++) { const int expert = selected[selected_base + slot]; if (expert < 0 || (uint)expert >= args.n_total_expert) continue; device const block_q4_K *down_row = (device const block_q4_K *)(down + (uint64_t)(uint)expert * args.down_expert_bytes + (uint64_t)row * args.down_row_bytes); device const float *slot_mid = mid + mid_base + (uint64_t)slot * args.mid_dim; for (uint k = tid; k < args.mid_dim; k += ntg) { acc += ds4_glm_q4_K_value(down_row, k) * slot_mid[k]; } } scratch[tid] = acc; threadgroup_barrier(mem_flags::mem_threadgroup); for (uint stride = ntg >> 1u; stride > 0u; stride >>= 1u) { if (tid < stride) scratch[tid] += scratch[tid + stride]; threadgroup_barrier(mem_flags::mem_threadgroup); } if (tid == 0u) { out[(uint64_t)token * args.out_dim + row] = scratch[0]; } } kernel void kernel_glm_q4_K_addr_down_f32( constant ds4_metal_glm_routed_moe_args &args, device const uint64_t *down_addrs, device const int32_t *selected, device const float *mid, device float *out, threadgroup float *scratch [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], uint tid [[thread_index_in_threadgroup]]) { const uint ntg = 256u; const uint row = tgpig.x; const uint token = tgpig.y; if (row >= args.out_dim || token >= args.n_tokens) return; float acc = 0.0f; const uint64_t selected_base = (uint64_t)token * args.n_expert_used; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride; for (uint slot = 0; slot < args.n_expert_used; slot++) { const int expert = selected[selected_base + slot]; if (expert < 0 || (uint)expert >= args.n_total_expert) continue; const uint64_t down_addr = down_addrs[(uint)expert]; if (down_addr == 0) continue; device const block_q4_K *down_row = (device const block_q4_K *)(reinterpret_cast(down_addr) + (uint64_t)row * args.down_row_bytes); device const float *slot_mid = mid + mid_base + (uint64_t)slot * args.mid_dim; for (uint k = tid; k < args.mid_dim; k += ntg) { acc += ds4_glm_q4_K_value(down_row, k) * slot_mid[k]; } } scratch[tid] = acc; threadgroup_barrier(mem_flags::mem_threadgroup); for (uint stride = ntg >> 1u; stride > 0u; stride >>= 1u) { if (tid < stride) scratch[tid] += scratch[tid + stride]; threadgroup_barrier(mem_flags::mem_threadgroup); } if (tid == 0u) { out[(uint64_t)token * args.out_dim + row] = scratch[0]; } } kernel void kernel_glm_q4_K_down_simd_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *down, device const int32_t *selected, device const float *mid, device float *out, uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const short NSG = 2; const short nr0 = N_R0_Q4_K; const int nb = args.mid_dim / QK_K; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * (uint)nr0; const uint token = tgpig.y; if (row0 >= args.out_dim || token >= args.n_tokens) return; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; float sumf[N_R0_Q4_K] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; const uint64_t selected_base = (uint64_t)token * args.n_expert_used; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride; for (uint slot = 0; slot < args.n_expert_used; slot++) { const int expert = selected[selected_base + slot]; if (expert < 0 || (uint)expert >= args.n_total_expert) continue; device const block_q4_K *x = (device const block_q4_K *)(down + (uint64_t)(uint)expert * args.down_expert_bytes + (uint64_t)row0 * args.down_row_bytes); device const float *y = mid + mid_base + (uint64_t)slot * args.mid_dim; device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0 && row0 + (uint)row < args.out_dim; row++) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1[0] += yl[2 * i + 0] * (q1[i] & 0x000F); acc1[1] += yl[2 * i + 1] * (q1[i] & 0x0F00); acc1[2] += yl[2 * i + 8] * (q1[i] & 0x00F0); acc1[3] += yl[2 * i + 9] * (q1[i] & 0xF000); acc2[0] += yh[2 * i + 0] * (q2[i] & 0x000F); acc2[1] += yh[2 * i + 1] * (q2[i] & 0x0F00); acc2[2] += yh[2 * i + 8] * (q2[i] & 0x00F0); acc2[3] += yh[2 * i + 9] * (q2[i] & 0xF000); } sumf[row] += dh[0] * ((acc1[0] + 1.f / 256.f * acc1[1]) * sc8[0] + (acc1[2] + 1.f / 256.f * acc1[3]) * sc8[1] * 1.f / 16.f + (acc2[0] + 1.f / 256.f * acc2[1]) * sc8[4] + (acc2[2] + 1.f / 256.f * acc2[3]) * sc8[5] * 1.f / 16.f) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); q1 += args.down_row_bytes / 2; sc += args.down_row_bytes / 2; dh += args.down_row_bytes / 2; } y4 += 4 * QK_K; } } for (short row = 0; row < nr0 && row0 + (uint)row < args.out_dim; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0u) { out[(uint64_t)token * args.out_dim + row0 + (uint)row] = sum_all; } } } kernel void kernel_glm_q4_K_addr_down_simd_f32( constant ds4_metal_glm_routed_moe_args &args, device const uint64_t *down_addrs, device const int32_t *selected, device const float *mid, device float *out, uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const short NSG = 2; const short nr0 = N_R0_Q4_K; const int nb = args.mid_dim / QK_K; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * (uint)nr0; const uint token = tgpig.y; if (row0 >= args.out_dim || token >= args.n_tokens) return; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; float sumf[N_R0_Q4_K] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; const uint64_t selected_base = (uint64_t)token * args.n_expert_used; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride; for (uint slot = 0; slot < args.n_expert_used; slot++) { const int expert = selected[selected_base + slot]; if (expert < 0 || (uint)expert >= args.n_total_expert) continue; const uint64_t down_addr = down_addrs[(uint)expert]; if (down_addr == 0) continue; device const block_q4_K *x = (device const block_q4_K *)(reinterpret_cast(down_addr) + (uint64_t)row0 * args.down_row_bytes); device const float *y = mid + mid_base + (uint64_t)slot * args.mid_dim; device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0 && row0 + (uint)row < args.out_dim; row++) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1[0] += yl[2 * i + 0] * (q1[i] & 0x000F); acc1[1] += yl[2 * i + 1] * (q1[i] & 0x0F00); acc1[2] += yl[2 * i + 8] * (q1[i] & 0x00F0); acc1[3] += yl[2 * i + 9] * (q1[i] & 0xF000); acc2[0] += yh[2 * i + 0] * (q2[i] & 0x000F); acc2[1] += yh[2 * i + 1] * (q2[i] & 0x0F00); acc2[2] += yh[2 * i + 8] * (q2[i] & 0x00F0); acc2[3] += yh[2 * i + 9] * (q2[i] & 0xF000); } sumf[row] += dh[0] * ((acc1[0] + 1.f / 256.f * acc1[1]) * sc8[0] + (acc1[2] + 1.f / 256.f * acc1[3]) * sc8[1] * 1.f / 16.f + (acc2[0] + 1.f / 256.f * acc2[1]) * sc8[4] + (acc2[2] + 1.f / 256.f * acc2[3]) * sc8[5] * 1.f / 16.f) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); q1 += args.down_row_bytes / 2; sc += args.down_row_bytes / 2; dh += args.down_row_bytes / 2; } y4 += 4 * QK_K; } } for (short row = 0; row < nr0 && row0 + (uint)row < args.out_dim; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0u) { out[(uint64_t)token * args.out_dim + row0 + (uint)row] = sum_all; } } } kernel void kernel_glm_q6_K_down_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *down, device const int32_t *selected, device const float *mid, device float *out, uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const short NSG = 2; constexpr uint kmask1 = 0x03u; constexpr uint kmask2 = 0x0Cu; constexpr uint kmask3 = 0x30u; constexpr uint kmask4 = 0xC0u; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_Q6_K; const uint token = tgpig.y; if (row0 >= args.out_dim || token >= args.n_tokens) return; const int nb = args.mid_dim / QK_K; float sumf[N_R0_Q6_K] = {0.f}; float yl[16]; const short tid = tiisg / 2; const short ix = tiisg % 2; const short ip = tid / 8; const short il = tid % 8; const short l0 = 4 * il; const short is = 8 * ip + l0 / 16; const short y_offset = 128 * ip + l0; const short q_offset_l = 64 * ip + l0; const short q_offset_h = 32 * ip + l0; const uint64_t selected_base = (uint64_t)token * args.n_expert_used; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride; for (uint slot = 0; slot < args.n_expert_used; slot++) { const int expert = selected[selected_base + slot]; if (expert < 0 || (uint)expert >= args.n_total_expert) continue; device const block_q6_K *x = (device const block_q6_K *)(down + (uint64_t)(uint)expert * args.down_expert_bytes + (uint64_t)row0 * args.down_row_bytes); device const float *yy = mid + mid_base + (uint64_t)slot * args.mid_dim; for (int ib = ix; ib < nb; ib += 2) { device const uchar *q1 = x[ib].ql + q_offset_l; device const uchar *q2 = q1 + 32; device const uchar *qh = x[ib].qh + q_offset_h; device const char *sc = x[ib].scales + is; device const half *dh = &x[ib].d; device const float *y = yy + (uint64_t)ib * QK_K + y_offset; for (short l = 0; l < 4; ++l) { yl[4 * l + 0] = y[l + 0]; yl[4 * l + 1] = y[l + 32]; yl[4 * l + 2] = y[l + 64]; yl[4 * l + 3] = y[l + 96]; } for (short row = 0; row < N_R0_Q6_K && row0 + (uint)row < args.out_dim; row++) { float4 sums = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short l = 0; l < 4; ++l) { const uint h = (uint)qh[l]; sums[0] += yl[4 * l + 0] * (float)((int)((q1[l] & 0x0Fu) | ((h & kmask1) << 4u)) - 32); sums[1] += yl[4 * l + 1] * (float)((int)((q2[l] & 0x0Fu) | ((h & kmask2) << 2u)) - 32); sums[2] += yl[4 * l + 2] * (float)((int)((q1[l] >> 4u) | (h & kmask3)) - 32); sums[3] += yl[4 * l + 3] * (float)((int)((q2[l] >> 4u) | ((h & kmask4) >> 2u)) - 32); } sumf[row] += dh[0] * (sums[0] * sc[0] + sums[1] * sc[2] + sums[2] * sc[4] + sums[3] * sc[6]); q1 += args.down_row_bytes; q2 += args.down_row_bytes; qh += args.down_row_bytes; sc += args.down_row_bytes; dh += args.down_row_bytes / 2; } } } for (short row = 0; row < N_R0_Q6_K && row0 + (uint)row < args.out_dim; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0u) { out[(uint64_t)token * args.out_dim + row0 + (uint)row] = sum_all; } } } kernel void kernel_glm_q5_K_slots6_down_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *down0, device const char *down1, device const char *down2, device const char *down3, device const char *down4, device const char *down5, device const float *mid, device float *out, uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const short NSG = 2; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_Q5_K; const uint token = tgpig.y; if (row0 >= args.out_dim || token >= args.n_tokens) return; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const uint bit0 = 2u * (uint)iq; const uint bit1 = bit0 + 1u; const uint bit2 = bit0 + 4u; const uint bit3 = bit0 + 5u; const int nb = args.mid_dim / QK_K; float sumf[N_R0_Q5_K] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride; for (uint slot = 0; slot < args.n_expert_used; slot++) { device const char *down_cur = down0; switch (slot) { case 1: down_cur = down1; break; case 2: down_cur = down2; break; case 3: down_cur = down3; break; case 4: down_cur = down4; break; case 5: down_cur = down5; break; default: break; } device const block_q5_K *x = (device const block_q5_K *)(down_cur + (uint64_t)row0 * args.down_row_bytes); device const float *y = mid + mid_base + (uint64_t)slot * args.mid_dim; device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const uint16_t *qh = (device const uint16_t *)x[ib].qh + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < N_R0_Q5_K && row0 + (uint)row < args.out_dim; row++) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { const uint ql1 = (uint)q1[i]; const uint ql2 = (uint)q2[i]; const uint hb = (uint)qh[i]; acc[0] += yl[2 * i + 0] * (float)((ql1 & 0x000Fu) + (((hb >> bit0) & 1u) << 4u)) + yl[2 * i + 1] * (float)(((ql1 >> 8u) & 0x000Fu) + (((hb >> (bit0 + 8u)) & 1u) << 4u)); acc[1] += yl[2 * i + 8] * (float)(((ql1 >> 4u) & 0x000Fu) + (((hb >> bit1) & 1u) << 4u)) + yl[2 * i + 9] * (float)(((ql1 >> 12u) & 0x000Fu) + (((hb >> (bit1 + 8u)) & 1u) << 4u)); acc[2] += yh[2 * i + 0] * (float)((ql2 & 0x000Fu) + (((hb >> bit2) & 1u) << 4u)) + yh[2 * i + 1] * (float)(((ql2 >> 8u) & 0x000Fu) + (((hb >> (bit2 + 8u)) & 1u) << 4u)); acc[3] += yh[2 * i + 8] * (float)(((ql2 >> 4u) & 0x000Fu) + (((hb >> bit3) & 1u) << 4u)) + yh[2 * i + 9] * (float)(((ql2 >> 12u) & 0x000Fu) + (((hb >> (bit3 + 8u)) & 1u) << 4u)); } sumf[row] += dh[0] * (acc[0] * sc8[0] + acc[1] * sc8[1] + acc[2] * sc8[4] + acc[3] * sc8[5]) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); q1 += args.down_row_bytes / 2; qh += args.down_row_bytes / 2; sc += args.down_row_bytes / 2; dh += args.down_row_bytes / 2; } y4 += 4 * QK_K; } } for (short row = 0; row < N_R0_Q5_K && row0 + (uint)row < args.out_dim; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0u) { out[(uint64_t)token * args.out_dim + row0 + (uint)row] = sum_all; } } } kernel void kernel_glm_q5_K_slots8_down_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *down0, device const char *down1, device const char *down2, device const char *down3, device const char *down4, device const char *down5, device const char *down6, device const char *down7, device const float *mid, device float *out, uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const short NSG = 2; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_Q5_K; const uint token = tgpig.y; if (row0 >= args.out_dim || token >= args.n_tokens) return; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const uint bit0 = 2u * (uint)iq; const uint bit1 = bit0 + 1u; const uint bit2 = bit0 + 4u; const uint bit3 = bit0 + 5u; const int nb = args.mid_dim / QK_K; float sumf[N_R0_Q5_K] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride; for (uint slot = 0; slot < args.n_expert_used; slot++) { device const char *down_cur = down0; switch (slot) { case 1: down_cur = down1; break; case 2: down_cur = down2; break; case 3: down_cur = down3; break; case 4: down_cur = down4; break; case 5: down_cur = down5; break; case 6: down_cur = down6; break; case 7: down_cur = down7; break; default: break; } device const block_q5_K *x = (device const block_q5_K *)(down_cur + (uint64_t)row0 * args.down_row_bytes); device const float *y = mid + mid_base + (uint64_t)slot * args.mid_dim; device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const uint16_t *qh = (device const uint16_t *)x[ib].qh + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < N_R0_Q5_K && row0 + (uint)row < args.out_dim; row++) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { const uint ql1 = (uint)q1[i]; const uint ql2 = (uint)q2[i]; const uint hb = (uint)qh[i]; acc[0] += yl[2 * i + 0] * (float)((ql1 & 0x000Fu) + (((hb >> bit0) & 1u) << 4u)) + yl[2 * i + 1] * (float)(((ql1 >> 8u) & 0x000Fu) + (((hb >> (bit0 + 8u)) & 1u) << 4u)); acc[1] += yl[2 * i + 8] * (float)(((ql1 >> 4u) & 0x000Fu) + (((hb >> bit1) & 1u) << 4u)) + yl[2 * i + 9] * (float)(((ql1 >> 12u) & 0x000Fu) + (((hb >> (bit1 + 8u)) & 1u) << 4u)); acc[2] += yh[2 * i + 0] * (float)((ql2 & 0x000Fu) + (((hb >> bit2) & 1u) << 4u)) + yh[2 * i + 1] * (float)(((ql2 >> 8u) & 0x000Fu) + (((hb >> (bit2 + 8u)) & 1u) << 4u)); acc[3] += yh[2 * i + 8] * (float)(((ql2 >> 4u) & 0x000Fu) + (((hb >> bit3) & 1u) << 4u)) + yh[2 * i + 9] * (float)(((ql2 >> 12u) & 0x000Fu) + (((hb >> (bit3 + 8u)) & 1u) << 4u)); } sumf[row] += dh[0] * (acc[0] * sc8[0] + acc[1] * sc8[1] + acc[2] * sc8[4] + acc[3] * sc8[5]) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); q1 += args.down_row_bytes / 2; qh += args.down_row_bytes / 2; sc += args.down_row_bytes / 2; dh += args.down_row_bytes / 2; } y4 += 4 * QK_K; } } for (short row = 0; row < N_R0_Q5_K && row0 + (uint)row < args.out_dim; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0u) { out[(uint64_t)token * args.out_dim + row0 + (uint)row] = sum_all; } } } kernel void kernel_glm_q6_K_slots6_down_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *down0, device const char *down1, device const char *down2, device const char *down3, device const char *down4, device const char *down5, device const float *mid, device float *out, uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const short NSG = 2; constexpr uint kmask1 = 0x03u; constexpr uint kmask2 = 0x0Cu; constexpr uint kmask3 = 0x30u; constexpr uint kmask4 = 0xC0u; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_Q6_K; const uint token = tgpig.y; if (row0 >= args.out_dim || token >= args.n_tokens) return; const int nb = args.mid_dim / QK_K; float sumf[N_R0_Q6_K] = {0.f}; float yl[16]; const short tid = tiisg / 2; const short ix = tiisg % 2; const short ip = tid / 8; const short il = tid % 8; const short l0 = 4 * il; const short is = 8 * ip + l0 / 16; const short y_offset = 128 * ip + l0; const short q_offset_l = 64 * ip + l0; const short q_offset_h = 32 * ip + l0; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride; for (uint slot = 0; slot < args.n_expert_used; slot++) { device const char *down_cur = down0; switch (slot) { case 1: down_cur = down1; break; case 2: down_cur = down2; break; case 3: down_cur = down3; break; case 4: down_cur = down4; break; case 5: down_cur = down5; break; default: break; } device const block_q6_K *x = (device const block_q6_K *)(down_cur + (uint64_t)row0 * args.down_row_bytes); device const float *yy = mid + mid_base + (uint64_t)slot * args.mid_dim; for (int ib = ix; ib < nb; ib += 2) { device const uchar *q1 = x[ib].ql + q_offset_l; device const uchar *q2 = q1 + 32; device const uchar *qh = x[ib].qh + q_offset_h; device const char *sc = x[ib].scales + is; device const half *dh = &x[ib].d; device const float *y = yy + (uint64_t)ib * QK_K + y_offset; for (short l = 0; l < 4; ++l) { yl[4 * l + 0] = y[l + 0]; yl[4 * l + 1] = y[l + 32]; yl[4 * l + 2] = y[l + 64]; yl[4 * l + 3] = y[l + 96]; } for (short row = 0; row < N_R0_Q6_K && row0 + (uint)row < args.out_dim; row++) { float4 sums = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short l = 0; l < 4; ++l) { const uint h = (uint)qh[l]; sums[0] += yl[4 * l + 0] * (float)((int)((q1[l] & 0x0Fu) | ((h & kmask1) << 4u)) - 32); sums[1] += yl[4 * l + 1] * (float)((int)((q2[l] & 0x0Fu) | ((h & kmask2) << 2u)) - 32); sums[2] += yl[4 * l + 2] * (float)((int)((q1[l] >> 4u) | (h & kmask3)) - 32); sums[3] += yl[4 * l + 3] * (float)((int)((q2[l] >> 4u) | ((h & kmask4) >> 2u)) - 32); } sumf[row] += dh[0] * (sums[0] * sc[0] + sums[1] * sc[2] + sums[2] * sc[4] + sums[3] * sc[6]); q1 += args.down_row_bytes; q2 += args.down_row_bytes; qh += args.down_row_bytes; sc += args.down_row_bytes; dh += args.down_row_bytes / 2; } } } for (short row = 0; row < N_R0_Q6_K && row0 + (uint)row < args.out_dim; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0u) { out[(uint64_t)token * args.out_dim + row0 + (uint)row] = sum_all; } } } kernel void kernel_glm_q6_K_slots8_down_f32( constant ds4_metal_glm_routed_moe_args &args, device const char *down0, device const char *down1, device const char *down2, device const char *down3, device const char *down4, device const char *down5, device const char *down6, device const char *down7, device const float *mid, device float *out, uint3 tgpig [[threadgroup_position_in_grid]], ushort tiisg [[thread_index_in_simdgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { const short NSG = 2; constexpr uint kmask1 = 0x03u; constexpr uint kmask2 = 0x0Cu; constexpr uint kmask3 = 0x30u; constexpr uint kmask4 = 0xC0u; const uint row0 = ((uint)tgpig.x * (uint)NSG + (uint)sgitg) * N_R0_Q6_K; const uint token = tgpig.y; if (row0 >= args.out_dim || token >= args.n_tokens) return; const int nb = args.mid_dim / QK_K; float sumf[N_R0_Q6_K] = {0.f}; float yl[16]; const short tid = tiisg / 2; const short ix = tiisg % 2; const short ip = tid / 8; const short il = tid % 8; const short l0 = 4 * il; const short is = 8 * ip + l0 / 16; const short y_offset = 128 * ip + l0; const short q_offset_l = 64 * ip + l0; const short q_offset_h = 32 * ip + l0; const uint64_t mid_base = (uint64_t)token * args.mid_token_stride; for (uint slot = 0; slot < args.n_expert_used; slot++) { device const char *down_cur = down0; switch (slot) { case 1: down_cur = down1; break; case 2: down_cur = down2; break; case 3: down_cur = down3; break; case 4: down_cur = down4; break; case 5: down_cur = down5; break; case 6: down_cur = down6; break; case 7: down_cur = down7; break; default: break; } device const block_q6_K *x = (device const block_q6_K *)(down_cur + (uint64_t)row0 * args.down_row_bytes); device const float *yy = mid + mid_base + (uint64_t)slot * args.mid_dim; for (int ib = ix; ib < nb; ib += 2) { device const uchar *q1 = x[ib].ql + q_offset_l; device const uchar *q2 = q1 + 32; device const uchar *qh = x[ib].qh + q_offset_h; device const char *sc = x[ib].scales + is; device const half *dh = &x[ib].d; device const float *y = yy + (uint64_t)ib * QK_K + y_offset; for (short l = 0; l < 4; ++l) { yl[4 * l + 0] = y[l + 0]; yl[4 * l + 1] = y[l + 32]; yl[4 * l + 2] = y[l + 64]; yl[4 * l + 3] = y[l + 96]; } for (short row = 0; row < N_R0_Q6_K && row0 + (uint)row < args.out_dim; row++) { float4 sums = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short l = 0; l < 4; ++l) { const uint h = (uint)qh[l]; sums[0] += yl[4 * l + 0] * (float)((int)((q1[l] & 0x0Fu) | ((h & kmask1) << 4u)) - 32); sums[1] += yl[4 * l + 1] * (float)((int)((q2[l] & 0x0Fu) | ((h & kmask2) << 2u)) - 32); sums[2] += yl[4 * l + 2] * (float)((int)((q1[l] >> 4u) | (h & kmask3)) - 32); sums[3] += yl[4 * l + 3] * (float)((int)((q2[l] >> 4u) | ((h & kmask4) >> 2u)) - 32); } sumf[row] += dh[0] * (sums[0] * sc[0] + sums[1] * sc[2] + sums[2] * sc[4] + sums[3] * sc[6]); q1 += args.down_row_bytes; q2 += args.down_row_bytes; qh += args.down_row_bytes; sc += args.down_row_bytes; dh += args.down_row_bytes / 2; } } } for (short row = 0; row < N_R0_Q6_K && row0 + (uint)row < args.out_dim; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0u) { out[(uint64_t)token * args.out_dim + row0 + (uint)row] = sum_all; } } } template void dequantize_q4_K(device const block_q4_K *xb, short il, thread type4x4 ®) { device const uchar *q = xb->qs; short is = (il / 4) * 2; q = q + (il / 4) * 32 + 16 * (il & 1); il = il & 3; const uchar2 sc = get_scale_min_k4_just2(is, il / 2, xb->scales); const float d = il < 2 ? (float)xb->d : (float)xb->d * (1.0f / 16.0f); const float min = (float)xb->dmin; const float dl = d * sc[0]; const float ml = min * sc[1]; const ushort mask = il < 2 ? 0x0F : 0xF0; for (int i = 0; i < 16; ++i) { reg[i / 4][i % 4] = dl * (q[i] & mask) - ml; } } template void dequantize_q5_K(device const block_q5_K *xb, short il, thread type4x4 ®) { const short group = il / 2; const short l0 = (il & 1) * 16; const uchar2 sm = get_scale_min_k4_just2(group, 0, xb->scales); const float dl = (float)xb->d * (float)sm.x; const float ml = (float)xb->dmin * (float)sm.y; device const uchar *q = xb->qs + (group / 2) * 32 + l0; device const uchar *qh = xb->qh + l0; const uint shift = (uint)(group & 1) * 4u; const uchar hmask = (uchar)(1u << (uint)group); for (int i = 0; i < 16; ++i) { uint v = ((uint)q[i] >> shift) & 0x0Fu; v += (qh[i] & hmask) ? 16u : 0u; reg[i / 4][i % 4] = dl * (float)v - ml; } } template void dequantize_q6_K(device const block_q6_K *xb, short il, thread type4x4 ®) { const short n128 = il / 8; const short il128 = il & 7; const short quarter = il128 / 2; const short l0 = (il128 & 1) * 16; const uint ql_base = (uint)n128 * 64u; const uint qh_base = (uint)n128 * 32u; const uint sc_base = (uint)n128 * 8u + (uint)quarter * 2u + (uint)(il128 & 1); const float d = (float)xb->d * (float)((int)xb->scales[sc_base]); for (int i = 0; i < 16; ++i) { const uint l = (uint)l0 + (uint)i; uint v; if (quarter == 0) { v = ((uint)xb->ql[ql_base + l] & 0x0Fu) | ((((uint)xb->qh[qh_base + l] >> 0u) & 3u) << 4u); } else if (quarter == 1) { v = ((uint)xb->ql[ql_base + 32u + l] & 0x0Fu) | ((((uint)xb->qh[qh_base + l] >> 2u) & 3u) << 4u); } else if (quarter == 2) { v = ((uint)xb->ql[ql_base + l] >> 4u) | ((((uint)xb->qh[qh_base + l] >> 4u) & 3u) << 4u); } else { v = ((uint)xb->ql[ql_base + 32u + l] >> 4u) | ((((uint)xb->qh[qh_base + l] >> 6u) & 3u) << 4u); } reg[i / 4][i % 4] = d * (float)((int)v - 32); } } template void dequantize_iq2_xxs(device const block_iq2_xxs * xb, short il, thread type4x4 & reg) { const float d = xb->d; const int ib32 = il/2; il = il%2; device const uint16_t * q2 = xb->qs + 4*ib32; const uint32_t aux32_g = q2[0] | (q2[1] << 16); const uint32_t aux32_s = q2[2] | (q2[3] << 16); thread const uint8_t * aux8 = (thread const uint8_t *)&aux32_g; const float dl = d * (0.5f + (aux32_s >> 28)) * 0.25f; constant uint8_t * grid = (constant uint8_t *)(iq2xxs_grid + aux8[2*il+0]); uint8_t signs = ksigns_iq2xs[(aux32_s >> 14*il) & 127]; for (int i = 0; i < 8; ++i) { reg[i/4][i%4] = dl * grid[i] * (signs & kmask_iq2xs[i] ? -1.f : 1.f); } grid = (constant uint8_t *)(iq2xxs_grid + aux8[2*il+1]); signs = ksigns_iq2xs[(aux32_s >> (14*il+7)) & 127]; for (int i = 0; i < 8; ++i) { reg[2+i/4][i%4] = dl * grid[i] * (signs & kmask_iq2xs[i] ? -1.f : 1.f); } } struct ds4_metal_args_mul_mv_id { int32_t nei0; int32_t nei1; uint64_t nbi1; int32_t ne00; int32_t ne01; int32_t ne02; uint64_t nb00; uint64_t nb01; uint64_t nb02; int32_t ne10; int32_t ne11; int32_t ne12; int32_t ne13; uint64_t nb10; uint64_t nb11; uint64_t nb12; int32_t ne0; int32_t ne1; uint64_t nb1; int32_t nr0; /* Tensor-parallel expert ownership: when tp_world > 1, routed-MoE * kernels skip experts outside this rank's contiguous id range so this * rank never touches their weights. Zero-initialized (tp_world 0) means * no split; only the routed id kernels read these. */ int32_t tp_rank; int32_t tp_world; /* When set, the sum6 kernels add this per-row vector (the shared-expert * partial) into their accumulation, folding the TP local-add. */ int32_t tp_addend; /* First expert id present at the bound blob base (TP mappings bind only * the owned range; kernels rebase before pointer math). */ int32_t tp_expert_base; }; struct ds4_metal_moe_expert_group_args { uint32_t expert_base; uint32_t expert_count; uint32_t accumulate; uint32_t pad0; }; struct ds4_metal_q4_gather_slots6_args { uint64_t expert_bytes; uint32_t group_size; uint32_t n_slots; }; struct ds4_metal_q4_expert_table { array experts [[id(0)]]; }; struct ds4_metal_expert_address_table { device const uint64_t *addrs; }; struct ds4_metal_stream_expert_validate_args { uint32_t n_total_expert; uint32_t n_expert; }; struct ds4_metal_stream_expert_split_args { uint32_t active_mask; uint32_t accumulate; }; struct ds4_metal_args_mul_mm_id_map0 { int32_t ne02; int32_t ne10; int32_t ne11; uint64_t nb11; uint64_t nb12; int32_t ne21; int32_t ne20; uint64_t nb21; }; struct ds4_metal_args_mul_mm_id { int32_t ne00; int32_t ne02; uint64_t nb01; uint64_t nb02; uint64_t nb03; int32_t ne11; uint64_t nb10; uint64_t nb11; uint64_t nb12; uint64_t nb13; int32_t ne20; int32_t ne21; int32_t ne0; int32_t ne1; int16_t r2; int16_t r3; /* Tensor-parallel expert ownership; tp_world<=1 disables the split. * tp_expert_base rebases expert ids when the bind covers only the * owned contiguous range of the expert blob. */ int32_t tp_rank; int32_t tp_world; int32_t tp_expert_base; }; template void kernel_mul_mv_q2_K_f32_impl( args_t args, device const char * src0, device const char * src1, device char * dst, threadgroup char * shmem, uint3 tgpig, ushort tiisg, ushort sgitg) { const short NSG = FC_mul_mv_nsg; const int nb = args.ne00/QK_K; const int r0 = tgpig.x; const int r1 = tgpig.y; const int im = tgpig.z; const int first_row = (r0 * NSG + sgitg) * nr0; const uint i12 = im%args.ne12; const uint i13 = im/args.ne12; const uint64_t offset0 = first_row*args.nb01 + (i12/args.r2)*args.nb02 + (i13/args.r3)*args.nb03; const uint64_t offset1 = r1*args.nb11 + (i12 )*args.nb12 + (i13 )*args.nb13; device const block_q2_K * x = (device const block_q2_K *) (src0 + offset0); device const float * y = (device const float *) (src1 + offset1); float yl[32]; float sumf[nr0]={0.f}; const short ix = tiisg/8; // 0...3 const short it = tiisg%8; // 0...7 const short iq = it/4; // 0 or 1 const short ir = it%4; // 0...3 const short is = (8*ir)/16;// 0 or 1 device const float * y4 = y + ix * QK_K + 128 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i+ 0] = y4[i+ 0]; sumy[0] += yl[i+ 0]; yl[i+ 8] = y4[i+32]; sumy[1] += yl[i+ 8]; yl[i+16] = y4[i+64]; sumy[2] += yl[i+16]; yl[i+24] = y4[i+96]; sumy[3] += yl[i+24]; } device const uint8_t * sc = (device const uint8_t *)x[ib].scales + 8*iq + is; device const uint16_t * qs = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half * dh = &x[ib].d; for (short row = 0; row < nr0; row++) { float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; for (int i = 0; i < 8; i += 2) { acc1[0] += yl[i+ 0] * (qs[i/2] & 0x0003); acc2[0] += yl[i+ 1] * (qs[i/2] & 0x0300); acc1[1] += yl[i+ 8] * (qs[i/2] & 0x000c); acc2[1] += yl[i+ 9] * (qs[i/2] & 0x0c00); acc1[2] += yl[i+16] * (qs[i/2] & 0x0030); acc2[2] += yl[i+17] * (qs[i/2] & 0x3000); acc1[3] += yl[i+24] * (qs[i/2] & 0x00c0); acc2[3] += yl[i+25] * (qs[i/2] & 0xc000); } float dall = dh[0]; float dmin = dh[1] * 1.f/16.f; sumf[row] += dall * ((acc1[0] + 1.f/256.f * acc2[0]) * (sc[0] & 0xF) * 1.f/ 1.f + (acc1[1] + 1.f/256.f * acc2[1]) * (sc[2] & 0xF) * 1.f/ 4.f + (acc1[2] + 1.f/256.f * acc2[2]) * (sc[4] & 0xF) * 1.f/16.f + (acc1[3] + 1.f/256.f * acc2[3]) * (sc[6] & 0xF) * 1.f/64.f) - dmin * (sumy[0] * (sc[0] & 0xF0) + sumy[1] * (sc[2] & 0xF0) + sumy[2] * (sc[4] & 0xF0) + sumy[3] * (sc[6] & 0xF0)); qs += args.nb01/2; sc += args.nb01; dh += args.nb01/2; } y4 += 4 * QK_K; } device float * dst_f32 = (device float *) dst + (uint64_t)im*args.ne0*args.ne1 + (uint64_t)r1*args.ne0; for (int row = 0; row < nr0 && first_row + row < args.ne0; ++row) { float sum_all = simd_sum(sumf[row]); if (tiisg == 0) { dst_f32[first_row + row] = sum_all; } } } template void kernel_mul_mv_q4_K_f32_impl( args_t args, device const char *src0, device const char *src1, device char *dst, threadgroup char *shmem, uint3 tgpig, ushort tiisg, ushort sgitg) { const short NSG = FC_mul_mv_nsg; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const int nb = args.ne00 / QK_K; const int r0 = tgpig.x; const int r1 = tgpig.y; const int im = tgpig.z; const int first_row = (r0 * NSG + sgitg) * nr0; const uint i12 = im % args.ne12; const uint i13 = im / args.ne12; const uint64_t offset0 = first_row * args.nb01 + (i12 / args.r2) * args.nb02 + (i13 / args.r3) * args.nb03; const uint64_t offset1 = r1 * args.nb11 + i12 * args.nb12 + i13 * args.nb13; device const block_q4_K *x = (device const block_q4_K *)(src0 + offset0); device const float *y = (device const float *)(src1 + offset1); float yl[16]; float yh[16]; float sumf[nr0] = {0.f}; device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int ib = ix; ib < nb; ib += 4) { float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0; row++) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1[0] += yl[2 * i + 0] * (q1[i] & 0x000F); acc1[1] += yl[2 * i + 1] * (q1[i] & 0x0F00); acc1[2] += yl[2 * i + 8] * (q1[i] & 0x00F0); acc1[3] += yl[2 * i + 9] * (q1[i] & 0xF000); acc2[0] += yh[2 * i + 0] * (q2[i] & 0x000F); acc2[1] += yh[2 * i + 1] * (q2[i] & 0x0F00); acc2[2] += yh[2 * i + 8] * (q2[i] & 0x00F0); acc2[3] += yh[2 * i + 9] * (q2[i] & 0xF000); } sumf[row] += dh[0] * ((acc1[0] + 1.f / 256.f * acc1[1]) * sc8[0] + (acc1[2] + 1.f / 256.f * acc1[3]) * sc8[1] * 1.f / 16.f + (acc2[0] + 1.f / 256.f * acc2[1]) * sc8[4] + (acc2[2] + 1.f / 256.f * acc2[3]) * sc8[5] * 1.f / 16.f) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); q1 += args.nb01 / 2; sc += args.nb01 / 2; dh += args.nb01 / 2; } y4 += 4 * QK_K; } device float *dst_f32 = (device float *)dst + (uint64_t)im * args.ne0 * args.ne1 + (uint64_t)r1 * args.ne0; for (int row = 0; row < nr0 && first_row + row < args.ne0; ++row) { float sum_all = simd_sum(sumf[row]); if (tiisg == 0) { dst_f32[first_row + row] = sum_all; } } (void)shmem; } template void kernel_mul_mv_q8_K_f32_impl( args_t args, device const char *src0, device const char *src1, device char *dst, threadgroup char *shmem, uint3 tgpig, ushort tiisg, ushort sgitg) { const short NSG = FC_mul_mv_nsg; const int nb = args.ne00 / QK_K; const int r0 = tgpig.x; const int r1 = tgpig.y; const int im = tgpig.z; const int first_row = (r0 * NSG + sgitg) * nr0; const uint i12 = im % args.ne12; const uint i13 = im / args.ne12; const uint64_t offset0 = first_row * args.nb01 + (i12 / args.r2) * args.nb02 + (i13 / args.r3) * args.nb03; const uint64_t offset1 = r1 * args.nb11 + i12 * args.nb12 + i13 * args.nb13; device const block_q8_K *x = (device const block_q8_K *)(src0 + offset0); device const float *y = (device const float *)(src1 + offset1); float sumf[nr0] = {0.f}; const short ix = tiisg / 8; const short il = tiisg % 8; const int ib0 = ix; device const float *yb = y + ib0 * QK_K + il * 32; for (int ib = ib0; ib < nb; ib += 4) { float yl[32]; for (short i = 0; i < 32; ++i) { yl[i] = yb[i]; } for (short row = 0; row < nr0; row++) { device const block_q8_K *xr = (device const block_q8_K *)((device const char *)x + (uint64_t)row * args.nb01); device const int8_t *qs = xr[ib].qs + il * 32; float sumq = 0.f; for (short i = 0; i < 32; ++i) { sumq += (float)qs[i] * yl[i]; } sumf[row] += sumq * xr[ib].d; } yb += 4 * QK_K; } device float *dst_f32 = (device float *)dst + (uint64_t)im * args.ne0 * args.ne1 + (uint64_t)r1 * args.ne0; for (int row = 0; row < nr0 && first_row + row < args.ne0; ++row) { float sum_all = simd_sum(sumf[row]); if (tiisg == 0) { dst_f32[first_row + row] = sum_all; } } (void)shmem; } template void kernel_mul_mv_iq2_xxs_f32_impl( args_t args, device const char * src0, device const char * src1, device char * dst, threadgroup char * shmem, uint3 tgpig, ushort tiisg, ushort sgitg) { const short NSG = FC_mul_mv_nsg; const int nb = args.ne00/QK_K; const int r0 = tgpig.x; const int r1 = tgpig.y; const int im = tgpig.z; const int first_row = (r0 * NSG + sgitg) * nr0; const uint i12 = im%args.ne12; const uint i13 = im/args.ne12; const uint64_t offset0 = first_row*args.nb01 + (i12/args.r2)*args.nb02 + (i13/args.r3)*args.nb03; const uint64_t offset1 = r1*args.nb11 + (i12 )*args.nb12 + (i13 )*args.nb13; device const block_iq2_xxs * x = (device const block_iq2_xxs *) (src0 + offset0); device const float * y = (device const float *) (src1 + offset1); float yl[32]; float sumf[nr0]={0.f}; const int nb32 = nb * (QK_K / 32); threadgroup uint64_t * svalues = (threadgroup uint64_t *)(shmem); threadgroup uint8_t * ssigns = (threadgroup uint8_t *)(svalues + 256); { int nval = 4; int pos = (32*sgitg + tiisg)*nval; for (int i = 0; i < nval; ++i) svalues[pos + i] = ds4_metal_iq2xxs_grid[pos + i]; nval = 2; pos = (32*sgitg + tiisg)*nval; for (int i = 0; i < nval; ++i) ssigns[pos+i] = ds4_metal_ksigns_iq2xs[pos+i]; threadgroup_barrier(mem_flags::mem_threadgroup); } const int ix = tiisg; device const float * y4 = y + 32 * ix; for (int ib32 = ix; ib32 < nb32; ib32 += 32) { for (short i = 0; i < 32; ++i) { yl[i] = y4[i]; } const int ibl = ib32 / (QK_K / 32); const int ib = ib32 % (QK_K / 32); device const block_iq2_xxs * xr = x + ibl; device const uint16_t * q2 = xr->qs + 4 * ib; device const half * dh = &xr->d; for (short row = 0; row < nr0; row++) { const float db = dh[0]; device const uint8_t * aux8 = (device const uint8_t *)q2; const uint32_t aux32 = q2[2] | (q2[3] << 16); const float d = db * (0.5f + (aux32 >> 28)); float sum = 0; for (short l = 0; l < 4; ++l) { const threadgroup uint8_t * grid = (const threadgroup uint8_t *)(svalues + aux8[l]); const uint8_t signs = ssigns[(aux32 >> 7*l) & 127]; for (short j = 0; j < 8; ++j) { sum += yl[8*l + j] * grid[j] * (signs & ds4_metal_kmask_iq2xs[j] ? -1.f : 1.f); } } sumf[row] += d * sum; dh += args.nb01/2; q2 += args.nb01/2; } y4 += 32 * 32; } device float * dst_f32 = (device float *) dst + (uint64_t)im*args.ne0*args.ne1 + (uint64_t)r1*args.ne0; for (int row = 0; row < nr0 && first_row + row < args.ne0; ++row) { float sum_all = simd_sum(sumf[row]); if (tiisg == 0) { dst_f32[first_row + row] = sum_all * 0.25f; } } } template void kernel_mul_mv_iq2_xxs_pair_f32_impl( ds4_metal_args_mul_mv args, device const char * src0_gate, device const char * src0_up, device const char * src1, device char * dst_gate, device char * dst_up, threadgroup char * shmem, uint3 tgpig, ushort tiisg, ushort sgitg) { const short NSG = FC_mul_mv_nsg; const int nb = args.ne00/QK_K; const int r0 = tgpig.x; const int r1 = tgpig.y; const int im = tgpig.z; const int first_row = (r0 * NSG + sgitg) * nr0; const uint i12 = im%args.ne12; const uint i13 = im/args.ne12; const uint64_t offset0 = first_row*args.nb01 + (i12/args.r2)*args.nb02 + (i13/args.r3)*args.nb03; const uint64_t offset1 = r1*args.nb11 + (i12 )*args.nb12 + (i13 )*args.nb13; device const block_iq2_xxs * xg = (device const block_iq2_xxs *) (src0_gate + offset0); device const block_iq2_xxs * xu = (device const block_iq2_xxs *) (src0_up + offset0); device const float * y = (device const float *) (src1 + offset1); float yl[32]; float sumg[nr0]={0.f}; float sumu[nr0]={0.f}; const int nb32 = nb * (QK_K / 32); threadgroup uint64_t * svalues = (threadgroup uint64_t *)(shmem); threadgroup uint8_t * ssigns = (threadgroup uint8_t *)(svalues + 256); { int nval = 4; int pos = (32*sgitg + tiisg)*nval; for (int i = 0; i < nval; ++i) svalues[pos + i] = ds4_metal_iq2xxs_grid[pos + i]; nval = 2; pos = (32*sgitg + tiisg)*nval; for (int i = 0; i < nval; ++i) ssigns[pos+i] = ds4_metal_ksigns_iq2xs[pos+i]; threadgroup_barrier(mem_flags::mem_threadgroup); } const int ix = tiisg; device const float * y4 = y + 32 * ix; for (int ib32 = ix; ib32 < nb32; ib32 += 32) { for (short i = 0; i < 32; ++i) { yl[i] = y4[i]; } const int ibl = ib32 / (QK_K / 32); const int ib = ib32 % (QK_K / 32); device const block_iq2_xxs * xgr = xg + ibl; device const block_iq2_xxs * xur = xu + ibl; device const uint16_t * qg = xgr->qs + 4 * ib; device const uint16_t * qu = xur->qs + 4 * ib; device const half * dhg = &xgr->d; device const half * dhu = &xur->d; for (short row = 0; row < nr0; row++) { device const uint8_t * aux8g = (device const uint8_t *)qg; device const uint8_t * aux8u = (device const uint8_t *)qu; const uint32_t aux32g = qg[2] | (qg[3] << 16); const uint32_t aux32u = qu[2] | (qu[3] << 16); const float dg = (float)dhg[0] * (0.5f + (aux32g >> 28)); const float du = (float)dhu[0] * (0.5f + (aux32u >> 28)); float sg = 0; float su = 0; for (short l = 0; l < 4; ++l) { const threadgroup uint8_t * gridg = (const threadgroup uint8_t *)(svalues + aux8g[l]); const threadgroup uint8_t * gridu = (const threadgroup uint8_t *)(svalues + aux8u[l]); const uint8_t signg = ssigns[(aux32g >> 7*l) & 127]; const uint8_t signu = ssigns[(aux32u >> 7*l) & 127]; for (short j = 0; j < 8; ++j) { const float v = yl[8*l + j]; sg += v * gridg[j] * (signg & ds4_metal_kmask_iq2xs[j] ? -1.f : 1.f); su += v * gridu[j] * (signu & ds4_metal_kmask_iq2xs[j] ? -1.f : 1.f); } } sumg[row] += dg * sg; sumu[row] += du * su; dhg += args.nb01/2; dhu += args.nb01/2; qg += args.nb01/2; qu += args.nb01/2; } y4 += 32 * 32; } device float * dst_gate_f32 = (device float *) dst_gate + (uint64_t)im*args.ne0*args.ne1 + (uint64_t)r1*args.ne0; device float * dst_up_f32 = (device float *) dst_up + (uint64_t)im*args.ne0*args.ne1 + (uint64_t)r1*args.ne0; for (int row = 0; row < nr0 && first_row + row < args.ne0; ++row) { const float sum_gate = simd_sum(sumg[row]); const float sum_up = simd_sum(sumu[row]); if (tiisg == 0) { dst_gate_f32[first_row + row] = sum_gate * 0.25f; dst_up_f32[first_row + row] = sum_up * 0.25f; } } } typedef void (kernel_mul_mv2_disp_t)( ds4_metal_args_mul_mv args, device const char * src0, device const char * src1, device char * dst, threadgroup char * shmem, uint3 tgpig, ushort tiisg, ushort sgitg); template void mmv_fn( ds4_metal_args_mul_mv args, device const char * src0, device const char * src1, device char * dst, threadgroup char * shmem, uint3 tgpig, ushort tiitg, ushort tiisg, ushort sgitg) { disp_fn(args, src0, src1, dst, shmem, tgpig, tiisg, sgitg); } typedef decltype(mmv_fn>) mul_mv_id_disp_fn_t; // Decode-time expert matvec. The ids tensor selects the routed expert for each // slot, then this wrapper invokes the quantized row kernel for Q8_0, Q2_K, or // IQ2_XXS weights without materializing per-expert dispatches on the CPU. template kernel void kernel_mul_mv_id( constant ds4_metal_args_mul_mv_id & args, device const char * src0s, device const char * src1, device char * dst, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { (void)tiitg; const int iid1 = tgpig.z/args.nei0; const int idx = tgpig.z%args.nei0; tgpig.z = 0; const int32_t i02 = ((device const int32_t *) (ids + iid1*args.nbi1))[idx]; const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; const int64_t i1 = idx; const int64_t i2 = i12; device char * dst_cur = dst + (i1*args.ne0 + i2*args.ne1*args.ne0)*sizeof(float); if (!ds4_tp_owns_expert(i02, args.ne02, args.tp_rank, args.tp_world)) { /* Unowned expert under the TP split: zero this threadgroup's output * rows so the downstream expert-sum stages stay unchanged. */ const short NSG = FC_mul_mv_nsg; const int row0 = (tgpig.x * NSG + sgitg) * args.nr0; device float *dst_f32 = (device float *)dst_cur; for (int r = 0; r < args.nr0 && row0 + r < args.ne0; r++) { if (tiisg == 0) dst_f32[row0 + r] = 0.0f; } return; } device const char * src0_cur = src0s + (int64_t)(i02 - args.tp_expert_base)*args.nb02; device const char * src1_cur = src1 + i11*args.nb11 + i12*args.nb12; ds4_metal_args_mul_mv args0 = { /*.ne00 =*/ args.ne00, /*.ne01 =*/ args.ne01, /*.ne02 =*/ 1, /*.nb00 =*/ args.nb00, /*.nb01 =*/ args.nb01, /*.nb02 =*/ args.nb02, /*.nb03 =*/ args.nb02, /*.ne10 =*/ args.ne10, /*.ne11 =*/ 1, /*.ne12 =*/ 1, /*.nb10 =*/ args.nb10, /*.nb11 =*/ args.nb11, /*.nb12 =*/ args.nb12, /*.nb13 =*/ args.nb12, /*.ne0 =*/ args.ne0, /*.ne1 =*/ 1, /*.nr0 =*/ args.nr0, /*.r2 =*/ 1, /*.r3 =*/ 1, }; disp_fn( args0, /* src0 */ src0_cur, /* src1 */ src1_cur, /* dst */ dst_cur, shmem, tgpig, tiitg, tiisg, sgitg); } typedef decltype(kernel_mul_mv_id>>) kernel_mul_mv_id_q_t; typedef decltype(kernel_mul_mv_id>>) kernel_mul_mv_id_q8_0_t; // Host-visible decode MoE matvec variants for the DS4 quant formats. template [[host_name("kernel_mul_mv_id_q8_0_f32")]] kernel kernel_mul_mv_id_q8_0_t kernel_mul_mv_id>>; template [[host_name("kernel_mul_mv_id_q2_K_f32")]] kernel kernel_mul_mv_id_q_t kernel_mul_mv_id>>; template [[host_name("kernel_mul_mv_id_q4_K_f32")]] kernel kernel_mul_mv_id_q_t kernel_mul_mv_id>>; template [[host_name("kernel_mul_mv_id_q8_K_f32")]] kernel kernel_mul_mv_id_q_t kernel_mul_mv_id>>; template [[host_name("kernel_mul_mv_id_iq2_xxs_f32")]] kernel kernel_mul_mv_id_q_t kernel_mul_mv_id>>; // Plain dense Q4_K matvec on the classic impl. The mul_mv_ext family used // by the generic dense path tops out around 220 GB/s on M5 for the GLM // DenseQ4 decode shapes; this impl streams the same rows at 530-650 GB/s. kernel void kernel_mul_mv_q4_K_dense_f32( constant ds4_metal_args_mul_mv & args, device const char * src0, device const char * src1, device char * dst, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { kernel_mul_mv_q4_K_f32_impl(args, src0, src1, dst, shmem, tgpig, tiisg, sgitg); } // DS4 attention output low projection, specialized for the fixed block // diagonal mapping used by the model: // // low[token, group, rank] = heads[token, group, :] * Woa[group, rank, :] // // The generic GGML-style id matvec supports arbitrary routed expert ids. Here // the id is always equal to the group number, so this wrapper keeps the exact // Q8_0 dot kernel but removes the id-buffer load and the CPU-side id table. kernel void kernel_dsv4_attn_out_low_q8_0_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src0s, device const char * src1, device char * dst, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const int iid1 = tgpig.z/args.nei0; const int idx = tgpig.z%args.nei0; tgpig.z = 0; const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device const char * src0_cur = src0s + idx*args.nb02; device const char * src1_cur = src1 + i11*args.nb11 + i12*args.nb12; device char * dst_cur = dst + (idx*args.ne0 + i12*args.ne1*args.ne0)*sizeof(float); ds4_metal_args_mul_mv args0 = { /*.ne00 =*/ args.ne00, /*.ne01 =*/ args.ne01, /*.ne02 =*/ 1, /*.nb00 =*/ args.nb00, /*.nb01 =*/ args.nb01, /*.nb02 =*/ args.nb02, /*.nb03 =*/ args.nb02, /*.ne10 =*/ args.ne10, /*.ne11 =*/ 1, /*.ne12 =*/ 1, /*.nb10 =*/ args.nb10, /*.nb11 =*/ args.nb11, /*.nb12 =*/ args.nb12, /*.nb13 =*/ args.nb12, /*.ne0 =*/ args.ne0, /*.ne1 =*/ 1, /*.nr0 =*/ args.nr0, /*.r2 =*/ 1, /*.r3 =*/ 1, }; kernel_mul_mv_q8_0_f32_impl( args0, src0_cur, src1_cur, dst_cur, shmem, tgpig, tiisg, sgitg); } kernel void kernel_dsv4_attn_out_low_q4_K_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src0s, device const char * src1, device char * dst, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { (void)tiitg; const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device const char * src0_cur = src0s + idx * args.nb02; device const char * src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char * dst_cur = dst + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); ds4_metal_args_mul_mv args0 = { /*.ne00 =*/ args.ne00, /*.ne01 =*/ args.ne01, /*.ne02 =*/ 1, /*.nb00 =*/ args.nb00, /*.nb01 =*/ args.nb01, /*.nb02 =*/ args.nb02, /*.nb03 =*/ args.nb02, /*.ne10 =*/ args.ne10, /*.ne11 =*/ 1, /*.ne12 =*/ 1, /*.nb10 =*/ args.nb10, /*.nb11 =*/ args.nb11, /*.nb12 =*/ args.nb12, /*.nb13 =*/ args.nb12, /*.ne0 =*/ args.ne0, /*.ne1 =*/ 1, /*.nr0 =*/ args.nr0, /*.r2 =*/ 1, /*.r3 =*/ 1, }; kernel_mul_mv_q4_K_f32_impl( args0, src0_cur, src1_cur, dst_cur, shmem, tgpig, tiisg, sgitg); } kernel void kernel_mul_mv_id_iq2_xxs_pair_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src0_gate, device const char * src0_up, device const char * src1, device char * dst_gate, device char * dst_up, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const int iid1 = tgpig.z/args.nei0; const int idx = tgpig.z%args.nei0; tgpig.z = 0; const int32_t i02 = ((device const int32_t *) (ids + iid1*args.nbi1))[idx]; const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device char * dst_gate_cur = dst_gate + (idx*args.ne0 + i12*args.ne1*args.ne0)*sizeof(float); device char * dst_up_cur = dst_up + (idx*args.ne0 + i12*args.ne1*args.ne0)*sizeof(float); if (!ds4_tp_owns_expert(i02, args.ne02, args.tp_rank, args.tp_world)) { /* Unowned expert: zero this threadgroup's gate/up rows so the * separate swiglu-weight stage yields zero mid rows. */ const short NSG_z = FC_mul_mv_nsg; const int row0_z = (tgpig.x * NSG_z + sgitg) * args.nr0; device float *zg = (device float *)dst_gate_cur; device float *zu = (device float *)dst_up_cur; for (int r = 0; r < args.nr0 && row0_z + r < args.ne0; r++) { if (tiisg == 0) { zg[row0_z + r] = 0.0f; zu[row0_z + r] = 0.0f; } } return; } device const char * src0_gate_cur = src0_gate + (int64_t)(i02 - args.tp_expert_base)*args.nb02; device const char * src0_up_cur = src0_up + (int64_t)(i02 - args.tp_expert_base)*args.nb02; device const char * src1_cur = src1 + i11*args.nb11 + i12*args.nb12; ds4_metal_args_mul_mv args0 = { args.ne00, args.ne01, 1, args.nb00, args.nb01, args.nb02, args.nb02, args.ne10, 1, 1, args.nb10, args.nb11, args.nb12, args.nb12, args.ne0, 1, args.nr0, 1, 1, }; (void)tiitg; kernel_mul_mv_iq2_xxs_pair_f32_impl( args0, src0_gate_cur, src0_up_cur, src1_cur, dst_gate_cur, dst_up_cur, shmem, tgpig, tiisg, sgitg); } // Decode-only routed expert gate/up projection fused with the DS4 activation: // // mid = silu(clamp(gate)) * clamp(up) * route_weight // // The quantized dot products are intentionally the same IQ2_XXS paired path as // `kernel_mul_mv_id_iq2_xxs_pair_f32`. The only extra work is done by lane 0 // after each exact reduced row has been produced. This removes the separate // routed activation dispatch and avoids rereading the gate/up rows before the // down projection. The host uses this only for the normal release path where // diagnostics do not request clamped gate/up intermediates. kernel void kernel_mul_mv_id_iq2_xxs_pair_swiglu_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_dsv4_moe_swiglu_weight_args & act, device const char * src0_gate, device const char * src0_up, device const char * src1, device char * dst_gate, device char * dst_up, device char * dst_mid, device const char * ids, device const char * weights, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const short NSG = FC_mul_mv_nsg; const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int32_t i02 = ((device const int32_t *) (ids + iid1 * args.nbi1))[idx]; if (!ds4_tp_owns_expert(i02, args.ne02, args.tp_rank, args.tp_world)) return; const int i02b = i02 - args.tp_expert_base; const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * N_R0_IQ2_XXS; const int nb32 = nb * (QK_K / 32); device const block_iq2_xxs *xg = (device const block_iq2_xxs *)(src0_gate + (int64_t)i02b * args.nb02 + (uint64_t)first_row * args.nb01); device const block_iq2_xxs *xu = (device const block_iq2_xxs *)(src0_up + (int64_t)i02b * args.nb02 + (uint64_t)first_row * args.nb01); device const float *y = (device const float *)(src1 + i11 * args.nb11 + i12 * args.nb12); float yl[32]; float sumg[N_R0_IQ2_XXS] = {0.f}; float sumu[N_R0_IQ2_XXS] = {0.f}; threadgroup uint64_t *svalues = (threadgroup uint64_t *)(shmem); threadgroup uint8_t *ssigns = (threadgroup uint8_t *)(svalues + 256); { int nval = 4; int pos = (32 * sgitg + tiisg) * nval; for (int i = 0; i < nval; ++i) svalues[pos + i] = ds4_metal_iq2xxs_grid[pos + i]; nval = 2; pos = (32 * sgitg + tiisg) * nval; for (int i = 0; i < nval; ++i) ssigns[pos + i] = ds4_metal_ksigns_iq2xs[pos + i]; threadgroup_barrier(mem_flags::mem_threadgroup); } const int ix = tiisg; device const float *y4 = y + 32 * ix; for (int ib32 = ix; ib32 < nb32; ib32 += 32) { for (short i = 0; i < 32; ++i) { yl[i] = y4[i]; } const int ibl = ib32 / (QK_K / 32); const int ib = ib32 % (QK_K / 32); device const block_iq2_xxs *xgr = xg + ibl; device const block_iq2_xxs *xur = xu + ibl; device const uint16_t *qg = xgr->qs + 4 * ib; device const uint16_t *qu = xur->qs + 4 * ib; device const half *dhg = &xgr->d; device const half *dhu = &xur->d; for (short row = 0; row < N_R0_IQ2_XXS; row++) { device const uint8_t *aux8g = (device const uint8_t *)qg; device const uint8_t *aux8u = (device const uint8_t *)qu; const uint32_t aux32g = qg[2] | (qg[3] << 16); const uint32_t aux32u = qu[2] | (qu[3] << 16); const float dg = (float)dhg[0] * (0.5f + (aux32g >> 28)); const float du = (float)dhu[0] * (0.5f + (aux32u >> 28)); float sg = 0; float su = 0; for (short l = 0; l < 4; ++l) { const threadgroup uint8_t *gridg = (const threadgroup uint8_t *)(svalues + aux8g[l]); const threadgroup uint8_t *gridu = (const threadgroup uint8_t *)(svalues + aux8u[l]); const uint8_t signg = ssigns[(aux32g >> 7 * l) & 127]; const uint8_t signu = ssigns[(aux32u >> 7 * l) & 127]; for (short j = 0; j < 8; ++j) { const float v = yl[8 * l + j]; sg += v * gridg[j] * (signg & ds4_metal_kmask_iq2xs[j] ? -1.f : 1.f); su += v * gridu[j] * (signu & ds4_metal_kmask_iq2xs[j] ? -1.f : 1.f); } } sumg[row] += dg * sg; sumu[row] += du * su; dhg += args.nb01 / 2; dhu += args.nb01 / 2; qg += args.nb01 / 2; qu += args.nb01 / 2; } y4 += 32 * 32; } device float *dst_gate_f32 = (device float *)dst_gate + (uint64_t)i12 * args.ne0 * args.ne1 + (uint64_t)i11 * args.ne0; device float *dst_up_f32 = (device float *)dst_up + (uint64_t)i12 * args.ne0 * args.ne1 + (uint64_t)i11 * args.ne0; const uint64_t pair_row = (uint64_t)i12 * (uint64_t)args.nei0 + (uint64_t)idx; device float *dst_mid_f32 = (device float *)(dst_mid + pair_row * act.mid_row_stride); device const float *route_w = (device const float *)(weights + pair_row * act.weight_stride); const float c = act.clamp_value; const float route_weight = route_w[0]; for (int row = 0; row < N_R0_IQ2_XXS && first_row + row < args.ne0; ++row) { const float sum_gate = simd_sum(sumg[row]); const float sum_up = simd_sum(sumu[row]); if (tiisg == 0) { const uint out_row = first_row + row; const float gate = sum_gate * 0.25f; const float up = sum_up * 0.25f; float g = gate; float u = up; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); } dst_gate_f32[out_row] = gate; dst_up_f32[out_row] = up; const float silu = g / (1.0f + exp(-g)); dst_mid_f32[out_row] = silu * u * route_weight; } } (void)tiitg; } kernel void kernel_mul_mv_slots6_iq2_xxs_pair_swiglu_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_dsv4_moe_swiglu_weight_args & act, device const char * src0_gate0, device const char * src0_gate1, device const char * src0_gate2, device const char * src0_gate3, device const char * src0_gate4, device const char * src0_gate5, device const char * src0_up0, device const char * src0_up1, device const char * src0_up2, device const char * src0_up3, device const char * src0_up4, device const char * src0_up5, device const char * src1, device char * dst_gate, device char * dst_up, device char * dst_mid, device const char * weights, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device const char *src0_gate_cur = src0_gate0; device const char *src0_up_cur = src0_up0; switch (idx) { case 1: src0_gate_cur = src0_gate1; src0_up_cur = src0_up1; break; case 2: src0_gate_cur = src0_gate2; src0_up_cur = src0_up2; break; case 3: src0_gate_cur = src0_gate3; src0_up_cur = src0_up3; break; case 4: src0_gate_cur = src0_gate4; src0_up_cur = src0_up4; break; case 5: src0_gate_cur = src0_gate5; src0_up_cur = src0_up5; break; default: break; } device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char *dst_gate_cur = dst_gate + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); device char *dst_up_cur = dst_up + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); ds4_metal_args_mul_mv args0 = { args.ne00, args.ne01, 1, args.nb00, args.nb01, args.nb02, args.nb02, args.ne10, 1, 1, args.nb10, args.nb11, args.nb12, args.nb12, args.ne0, 1, args.nr0, 1, 1, }; kernel_mul_mv_iq2_xxs_pair_f32_impl( args0, src0_gate_cur, src0_up_cur, src1_cur, dst_gate_cur, dst_up_cur, shmem, tgpig, tiisg, sgitg); const short NSG = FC_mul_mv_nsg; const int first_row = (tgpig.x * NSG + sgitg) * N_R0_IQ2_XXS; device float *gate_f32 = (device float *)dst_gate_cur; device float *up_f32 = (device float *)dst_up_cur; const uint64_t pair_row = (uint64_t)i12 * (uint64_t)args.nei0 + (uint64_t)idx; device float *mid_f32 = (device float *)(dst_mid + pair_row * act.mid_row_stride); device const float *route_w = (device const float *)(weights + pair_row * act.weight_stride); const float c = act.clamp_value; const float route_weight = route_w[0]; if (tiisg == 0) { for (int row = 0; row < N_R0_IQ2_XXS && first_row + row < args.ne0; ++row) { const uint out_row = first_row + row; float g = gate_f32[out_row]; float u = up_f32[out_row]; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); } const float silu = g / (1.0f + exp(-g)); mid_f32[out_row] = silu * u * route_weight; } } (void)tiitg; } kernel void kernel_mul_mv_addr_iq2_xxs_pair_swiglu_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_dsv4_moe_swiglu_weight_args & act, device const uint64_t * gate_addrs, device const uint64_t * up_addrs, device const char * src1, device char * dst_gate, device char * dst_up, device char * dst_mid, device const char * ids, device const char * weights, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int32_t i02 = ((device const int32_t *)(ids + iid1 * args.nbi1))[idx]; if (i02 < 0 || i02 >= args.ne02 || i02 >= 384) { return; } const uint64_t gate_addr = gate_addrs[(uint)i02]; const uint64_t up_addr = up_addrs[(uint)i02]; if (gate_addr == 0 || up_addr == 0) { return; } const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device const char *src0_gate_cur = reinterpret_cast(gate_addr); device const char *src0_up_cur = reinterpret_cast(up_addr); device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char *dst_gate_cur = dst_gate + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); device char *dst_up_cur = dst_up + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); ds4_metal_args_mul_mv args0 = { args.ne00, args.ne01, 1, args.nb00, args.nb01, args.nb02, args.nb02, args.ne10, 1, 1, args.nb10, args.nb11, args.nb12, args.nb12, args.ne0, 1, args.nr0, 1, 1, }; kernel_mul_mv_iq2_xxs_pair_f32_impl( args0, src0_gate_cur, src0_up_cur, src1_cur, dst_gate_cur, dst_up_cur, shmem, tgpig, tiisg, sgitg); const short NSG = FC_mul_mv_nsg; const int first_row = (tgpig.x * NSG + sgitg) * N_R0_IQ2_XXS; device float *gate_f32 = (device float *)dst_gate_cur; device float *up_f32 = (device float *)dst_up_cur; const uint64_t pair_row = (uint64_t)i12 * (uint64_t)args.nei0 + (uint64_t)idx; device float *mid_f32 = (device float *)(dst_mid + pair_row * act.mid_row_stride); device const float *route_w = (device const float *)(weights + pair_row * act.weight_stride); const float c = act.clamp_value; const float route_weight = route_w[0]; if (tiisg == 0) { for (int row = 0; row < N_R0_IQ2_XXS && first_row + row < args.ne0; ++row) { const uint out_row = first_row + row; float g = gate_f32[out_row]; float u = up_f32[out_row]; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); } const float silu = g / (1.0f + exp(-g)); mid_f32[out_row] = silu * u * route_weight; } } (void)tiitg; } kernel void kernel_mul_mv_addr_iq2_xxs_f32( constant ds4_metal_args_mul_mv_id & args, device const uint64_t * addrs, device const char * src1, device char * dst, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { (void)tiitg; const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int32_t i02 = ((device const int32_t *)(ids + iid1 * args.nbi1))[idx]; if (i02 < 0 || i02 >= args.ne02 || i02 >= 384) { return; } const uint64_t addr = addrs[(uint)i02]; if (addr == 0) { return; } const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device const char *src0_cur = reinterpret_cast(addr); device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char *dst_cur = dst + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); ds4_metal_args_mul_mv args0 = { args.ne00, args.ne01, 1, args.nb00, args.nb01, args.nb02, args.nb02, args.ne10, 1, 1, args.nb10, args.nb11, args.nb12, args.nb12, args.ne0, 1, args.nr0, 1, 1, }; kernel_mul_mv_iq2_xxs_f32_impl( args0, src0_cur, src1_cur, dst_cur, shmem, tgpig, tiisg, sgitg); } kernel void kernel_mul_mv_addr_iq2_xxs_pair_swiglu_masked_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_dsv4_moe_swiglu_weight_args & act, constant ds4_metal_stream_expert_split_args & split, device const uint64_t * gate_addrs, device const uint64_t * up_addrs, device const char * src1, device char * dst_gate, device char * dst_up, device char * dst_mid, device const char * ids, device const char * weights, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; if ((split.active_mask & (1u << (uint)idx)) == 0) { return; } tgpig.z = 0; const int32_t i02 = ((device const int32_t *)(ids + iid1 * args.nbi1))[idx]; if (i02 < 0 || i02 >= args.ne02 || i02 >= 384) { return; } const uint64_t gate_addr = gate_addrs[(uint)i02]; const uint64_t up_addr = up_addrs[(uint)i02]; if (gate_addr == 0 || up_addr == 0) { return; } const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device const char *src0_gate_cur = reinterpret_cast(gate_addr); device const char *src0_up_cur = reinterpret_cast(up_addr); device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char *dst_gate_cur = dst_gate + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); device char *dst_up_cur = dst_up + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); ds4_metal_args_mul_mv args0 = { args.ne00, args.ne01, 1, args.nb00, args.nb01, args.nb02, args.nb02, args.ne10, 1, 1, args.nb10, args.nb11, args.nb12, args.nb12, args.ne0, 1, args.nr0, 1, 1, }; kernel_mul_mv_iq2_xxs_pair_f32_impl( args0, src0_gate_cur, src0_up_cur, src1_cur, dst_gate_cur, dst_up_cur, shmem, tgpig, tiisg, sgitg); const short NSG = FC_mul_mv_nsg; const int first_row = (tgpig.x * NSG + sgitg) * N_R0_IQ2_XXS; device float *gate_f32 = (device float *)dst_gate_cur; device float *up_f32 = (device float *)dst_up_cur; const uint64_t pair_row = (uint64_t)i12 * (uint64_t)args.nei0 + (uint64_t)idx; device float *mid_f32 = (device float *)(dst_mid + pair_row * act.mid_row_stride); device const float *route_w = (device const float *)(weights + pair_row * act.weight_stride); const float c = act.clamp_value; const float route_weight = route_w[0]; if (tiisg == 0) { for (int row = 0; row < N_R0_IQ2_XXS && first_row + row < args.ne0; ++row) { const uint out_row = first_row + row; float g = gate_f32[out_row]; float u = up_f32[out_row]; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); } const float silu = g / (1.0f + exp(-g)); mid_f32[out_row] = silu * u * route_weight; } } (void)tiitg; } kernel void kernel_stream_expert_cache_validate( constant ds4_metal_stream_expert_validate_args & args, device const char * ids, device const uint64_t * gate_addrs, device const uint64_t * up_addrs, device const uint64_t * down_addrs, device uint32_t * status, uint tid [[thread_position_in_grid]]) { if (tid != 0) return; uint32_t miss_mask = 0; uint32_t invalid_mask = 0; const uint32_t n_expert = min(args.n_expert, (uint32_t)6); device const int32_t *selected = (device const int32_t *)ids; status[3] = n_expert; for (uint32_t i = 0; i < 6; i++) { const int32_t expert = i < n_expert ? selected[i] : -1; status[4 + i] = as_type(expert); if (i >= n_expert) continue; if (expert < 0 || (uint32_t)expert >= args.n_total_expert || (uint32_t)expert >= 384) { invalid_mask |= (1u << i); continue; } const uint32_t e = (uint32_t)expert; if (gate_addrs[e] == 0 || up_addrs[e] == 0 || down_addrs[e] == 0) { miss_mask |= (1u << i); } } status[0] = (miss_mask == 0 && invalid_mask == 0) ? 1u : 0u; status[1] = miss_mask; status[2] = invalid_mask; } kernel void kernel_mul_mv_id_q4_K_pair_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src0_gate, device const char * src0_up, device const char * src1, device char * dst_gate, device char * dst_up, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int32_t i02 = ((device const int32_t *)(ids + iid1 * args.nbi1))[idx]; const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device char *dst_gate_cur = dst_gate + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); device char *dst_up_cur = dst_up + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); if (!ds4_tp_owns_expert(i02, args.ne02, args.tp_rank, args.tp_world)) { const short NSG_z = FC_mul_mv_nsg; const int row0_z = (tgpig.x * NSG_z + sgitg) * args.nr0; device float *zg = (device float *)dst_gate_cur; device float *zu = (device float *)dst_up_cur; for (int r = 0; r < args.nr0 && row0_z + r < args.ne0; r++) { if (tiisg == 0) { zg[row0_z + r] = 0.0f; zu[row0_z + r] = 0.0f; } } return; } device const char *src0_gate_cur = src0_gate + (int64_t)(i02 - args.tp_expert_base) * args.nb02; device const char *src0_up_cur = src0_up + (int64_t)(i02 - args.tp_expert_base) * args.nb02; device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; ds4_metal_args_mul_mv args0 = { args.ne00, args.ne01, 1, args.nb00, args.nb01, args.nb02, args.nb02, args.ne10, 1, 1, args.nb10, args.nb11, args.nb12, args.nb12, args.ne0, 1, args.nr0, 1, 1, }; (void)tiitg; kernel_mul_mv_q4_K_f32_impl( args0, src0_gate_cur, src1_cur, dst_gate_cur, shmem, tgpig, tiisg, sgitg); kernel_mul_mv_q4_K_f32_impl( args0, src0_up_cur, src1_cur, dst_up_cur, shmem, tgpig, tiisg, sgitg); } // Same release-path fusion as the IQ2_XXS kernel above for the Q4_K expert // variant. The Q4 pair path reuses the existing exact matvec implementation // for gate and up, then the same lane that wrote each row derives the routed // SwiGLU input. This keeps Q4 behavior aligned with the Q2 optimization while // preserving the old pair projection arithmetic. kernel void kernel_mul_mv_id_q4_K_pair_swiglu_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_dsv4_moe_swiglu_weight_args & act, device const char * src0_gate, device const char * src0_up, device const char * src1, device char * dst_gate, device char * dst_up, device char * dst_mid, device const char * ids, device const char * weights, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int32_t i02 = ((device const int32_t *)(ids + iid1 * args.nbi1))[idx]; if (!ds4_tp_owns_expert(i02, args.ne02, args.tp_rank, args.tp_world)) return; const int i02b = i02 - args.tp_expert_base; const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device const char *src0_gate_cur = src0_gate + (int64_t)i02b * args.nb02; device const char *src0_up_cur = src0_up + (int64_t)i02b * args.nb02; device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char *dst_gate_cur = dst_gate + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); device char *dst_up_cur = dst_up + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); const short NSG = FC_mul_mv_nsg; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * N_R0_Q4_K; device float *gate_f32 = (device float *)dst_gate_cur; device float *up_f32 = (device float *)dst_up_cur; const uint64_t pair_row = (uint64_t)i12 * (uint64_t)args.nei0 + (uint64_t)idx; device float *mid_f32 = (device float *)(dst_mid + pair_row * act.mid_row_stride); device const float *route_w = (device const float *)(weights + pair_row * act.weight_stride); const float c = act.clamp_value; const float route_weight = route_w[0]; device const block_q4_K *xg = (device const block_q4_K *)(src0_gate_cur + (uint64_t)first_row * args.nb01); device const block_q4_K *xu = (device const block_q4_K *)(src0_up_cur + (uint64_t)first_row * args.nb01); device const float *y = (device const float *)src1_cur; device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; float sumg[N_R0_Q4_K] = {0.f}; float sumu[N_R0_Q4_K] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *scg = (device const uint16_t *)xg[ib].scales + iq; device const uint16_t *qg1 = (device const uint16_t *)xg[ib].qs + 16 * iq + 4 * ir; device const half *dhg = &xg[ib].d; device const uint16_t *scu = (device const uint16_t *)xu[ib].scales + iq; device const uint16_t *qu1 = (device const uint16_t *)xu[ib].qs + 16 * iq + 4 * ir; device const half *dhu = &xu[ib].d; for (short row = 0; row < N_R0_Q4_K; row++) { sc16[0] = scg[0] & kmask1; sc16[1] = scg[2] & kmask1; sc16[2] = ((scg[4] >> 0) & kmask2) | ((scg[0] & kmask3) >> 2); sc16[3] = ((scg[4] >> 4) & kmask2) | ((scg[2] & kmask3) >> 2); device const uint16_t *qg2 = qg1 + 32; float4 acc1g = {0.f, 0.f, 0.f, 0.f}; float4 acc2g = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1g[0] += yl[2 * i + 0] * (qg1[i] & 0x000F); acc1g[1] += yl[2 * i + 1] * (qg1[i] & 0x0F00); acc1g[2] += yl[2 * i + 8] * (qg1[i] & 0x00F0); acc1g[3] += yl[2 * i + 9] * (qg1[i] & 0xF000); acc2g[0] += yh[2 * i + 0] * (qg2[i] & 0x000F); acc2g[1] += yh[2 * i + 1] * (qg2[i] & 0x0F00); acc2g[2] += yh[2 * i + 8] * (qg2[i] & 0x00F0); acc2g[3] += yh[2 * i + 9] * (qg2[i] & 0xF000); } sumg[row] += dhg[0] * ((acc1g[0] + 1.f / 256.f * acc1g[1]) * sc8[0] + (acc1g[2] + 1.f / 256.f * acc1g[3]) * sc8[1] * 1.f / 16.f + (acc2g[0] + 1.f / 256.f * acc2g[1]) * sc8[4] + (acc2g[2] + 1.f / 256.f * acc2g[3]) * sc8[5] * 1.f / 16.f) - dhg[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); sc16[0] = scu[0] & kmask1; sc16[1] = scu[2] & kmask1; sc16[2] = ((scu[4] >> 0) & kmask2) | ((scu[0] & kmask3) >> 2); sc16[3] = ((scu[4] >> 4) & kmask2) | ((scu[2] & kmask3) >> 2); device const uint16_t *qu2 = qu1 + 32; float4 acc1u = {0.f, 0.f, 0.f, 0.f}; float4 acc2u = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1u[0] += yl[2 * i + 0] * (qu1[i] & 0x000F); acc1u[1] += yl[2 * i + 1] * (qu1[i] & 0x0F00); acc1u[2] += yl[2 * i + 8] * (qu1[i] & 0x00F0); acc1u[3] += yl[2 * i + 9] * (qu1[i] & 0xF000); acc2u[0] += yh[2 * i + 0] * (qu2[i] & 0x000F); acc2u[1] += yh[2 * i + 1] * (qu2[i] & 0x0F00); acc2u[2] += yh[2 * i + 8] * (qu2[i] & 0x00F0); acc2u[3] += yh[2 * i + 9] * (qu2[i] & 0xF000); } sumu[row] += dhu[0] * ((acc1u[0] + 1.f / 256.f * acc1u[1]) * sc8[0] + (acc1u[2] + 1.f / 256.f * acc1u[3]) * sc8[1] * 1.f / 16.f + (acc2u[0] + 1.f / 256.f * acc2u[1]) * sc8[4] + (acc2u[2] + 1.f / 256.f * acc2u[3]) * sc8[5] * 1.f / 16.f) - dhu[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); qg1 += args.nb01 / 2; scg += args.nb01 / 2; dhg += args.nb01 / 2; qu1 += args.nb01 / 2; scu += args.nb01 / 2; dhu += args.nb01 / 2; } y4 += 4 * QK_K; } for (int row = 0; row < N_R0_Q4_K && first_row + row < args.ne0; ++row) { const float gate = simd_sum(sumg[row]); const float up = simd_sum(sumu[row]); if (tiisg == 0) { const uint out_row = first_row + row; float g = gate; float u = up; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); } gate_f32[out_row] = gate; up_f32[out_row] = up; const float silu = g / (1.0f + exp(-g)); mid_f32[out_row] = silu * u * route_weight; } } (void)tiitg; } kernel void kernel_mul_mv_table_q4_K_pair_swiglu_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_dsv4_moe_swiglu_weight_args & act, device const ds4_metal_q4_expert_table & gate_table, device const ds4_metal_q4_expert_table & up_table, device const char * src1, device char * dst_gate, device char * dst_up, device char * dst_mid, device const char * ids, device const char * weights, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int32_t i02 = ((device const int32_t *)(ids + iid1 * args.nbi1))[idx]; if (i02 < 0 || i02 >= args.ne02 || i02 >= 384) { return; } const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device const char *src0_gate_cur = gate_table.experts[(uint)i02]; device const char *src0_up_cur = up_table.experts[(uint)i02]; device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char *dst_gate_cur = dst_gate + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); device char *dst_up_cur = dst_up + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); const short NSG = FC_mul_mv_nsg; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * N_R0_Q4_K; device float *gate_f32 = (device float *)dst_gate_cur; device float *up_f32 = (device float *)dst_up_cur; const uint64_t pair_row = (uint64_t)i12 * (uint64_t)args.nei0 + (uint64_t)idx; device float *mid_f32 = (device float *)(dst_mid + pair_row * act.mid_row_stride); device const float *route_w = (device const float *)(weights + pair_row * act.weight_stride); const float c = act.clamp_value; const float route_weight = route_w[0]; device const block_q4_K *xg = (device const block_q4_K *)(src0_gate_cur + (uint64_t)first_row * args.nb01); device const block_q4_K *xu = (device const block_q4_K *)(src0_up_cur + (uint64_t)first_row * args.nb01); device const float *y = (device const float *)src1_cur; device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; float sumg[N_R0_Q4_K] = {0.f}; float sumu[N_R0_Q4_K] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *scg = (device const uint16_t *)xg[ib].scales + iq; device const uint16_t *qg1 = (device const uint16_t *)xg[ib].qs + 16 * iq + 4 * ir; device const half *dhg = &xg[ib].d; device const uint16_t *scu = (device const uint16_t *)xu[ib].scales + iq; device const uint16_t *qu1 = (device const uint16_t *)xu[ib].qs + 16 * iq + 4 * ir; device const half *dhu = &xu[ib].d; for (short row = 0; row < N_R0_Q4_K; row++) { sc16[0] = scg[0] & kmask1; sc16[1] = scg[2] & kmask1; sc16[2] = ((scg[4] >> 0) & kmask2) | ((scg[0] & kmask3) >> 2); sc16[3] = ((scg[4] >> 4) & kmask2) | ((scg[2] & kmask3) >> 2); device const uint16_t *qg2 = qg1 + 32; float4 acc1g = {0.f, 0.f, 0.f, 0.f}; float4 acc2g = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1g[0] += yl[2 * i + 0] * (qg1[i] & 0x000F); acc1g[1] += yl[2 * i + 1] * (qg1[i] & 0x0F00); acc1g[2] += yl[2 * i + 8] * (qg1[i] & 0x00F0); acc1g[3] += yl[2 * i + 9] * (qg1[i] & 0xF000); acc2g[0] += yh[2 * i + 0] * (qg2[i] & 0x000F); acc2g[1] += yh[2 * i + 1] * (qg2[i] & 0x0F00); acc2g[2] += yh[2 * i + 8] * (qg2[i] & 0x00F0); acc2g[3] += yh[2 * i + 9] * (qg2[i] & 0xF000); } sumg[row] += dhg[0] * ((acc1g[0] + 1.f / 256.f * acc1g[1]) * sc8[0] + (acc1g[2] + 1.f / 256.f * acc1g[3]) * sc8[1] * 1.f / 16.f + (acc2g[0] + 1.f / 256.f * acc2g[1]) * sc8[4] + (acc2g[2] + 1.f / 256.f * acc2g[3]) * sc8[5] * 1.f / 16.f) - dhg[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); sc16[0] = scu[0] & kmask1; sc16[1] = scu[2] & kmask1; sc16[2] = ((scu[4] >> 0) & kmask2) | ((scu[0] & kmask3) >> 2); sc16[3] = ((scu[4] >> 4) & kmask2) | ((scu[2] & kmask3) >> 2); device const uint16_t *qu2 = qu1 + 32; float4 acc1u = {0.f, 0.f, 0.f, 0.f}; float4 acc2u = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1u[0] += yl[2 * i + 0] * (qu1[i] & 0x000F); acc1u[1] += yl[2 * i + 1] * (qu1[i] & 0x0F00); acc1u[2] += yl[2 * i + 8] * (qu1[i] & 0x00F0); acc1u[3] += yl[2 * i + 9] * (qu1[i] & 0xF000); acc2u[0] += yh[2 * i + 0] * (qu2[i] & 0x000F); acc2u[1] += yh[2 * i + 1] * (qu2[i] & 0x0F00); acc2u[2] += yh[2 * i + 8] * (qu2[i] & 0x00F0); acc2u[3] += yh[2 * i + 9] * (qu2[i] & 0xF000); } sumu[row] += dhu[0] * ((acc1u[0] + 1.f / 256.f * acc1u[1]) * sc8[0] + (acc1u[2] + 1.f / 256.f * acc1u[3]) * sc8[1] * 1.f / 16.f + (acc2u[0] + 1.f / 256.f * acc2u[1]) * sc8[4] + (acc2u[2] + 1.f / 256.f * acc2u[3]) * sc8[5] * 1.f / 16.f) - dhu[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); qg1 += args.nb01 / 2; scg += args.nb01 / 2; dhg += args.nb01 / 2; qu1 += args.nb01 / 2; scu += args.nb01 / 2; dhu += args.nb01 / 2; } y4 += 4 * QK_K; } for (int row = 0; row < N_R0_Q4_K && first_row + row < args.ne0; ++row) { const float gate = simd_sum(sumg[row]); const float up = simd_sum(sumu[row]); if (tiisg == 0) { const uint out_row = first_row + row; float g = gate; float u = up; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); } gate_f32[out_row] = gate; up_f32[out_row] = up; const float silu = g / (1.0f + exp(-g)); mid_f32[out_row] = silu * u * route_weight; } } (void)tiitg; } kernel void kernel_mul_mv_addr_q4_K_pair_swiglu_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_dsv4_moe_swiglu_weight_args & act, device const ulong * gate_addrs, device const ulong * up_addrs, device const char * src1, device char * dst_gate, device char * dst_up, device char * dst_mid, device const char * ids, device const char * weights, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int32_t i02 = ((device const int32_t *)(ids + iid1 * args.nbi1))[idx]; if (i02 < 0 || i02 >= args.ne02 || i02 >= 384) { return; } const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device const char *src0_gate_cur = reinterpret_cast(gate_addrs[(uint)i02]); device const char *src0_up_cur = reinterpret_cast(up_addrs[(uint)i02]); device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char *dst_gate_cur = dst_gate + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); device char *dst_up_cur = dst_up + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); ds4_metal_args_mul_mv args0 = { args.ne00, args.ne01, 1, args.nb00, args.nb01, args.nb02, args.nb02, args.ne10, 1, 1, args.nb10, args.nb11, args.nb12, args.nb12, args.ne0, 1, args.nr0, 1, 1, }; kernel_mul_mv_q4_K_f32_impl( args0, src0_gate_cur, src1_cur, dst_gate_cur, shmem, tgpig, tiisg, sgitg); kernel_mul_mv_q4_K_f32_impl( args0, src0_up_cur, src1_cur, dst_up_cur, shmem, tgpig, tiisg, sgitg); const short NSG = FC_mul_mv_nsg; const int first_row = (tgpig.x * NSG + sgitg) * N_R0_Q4_K; device float *gate_f32 = (device float *)dst_gate_cur; device float *up_f32 = (device float *)dst_up_cur; const uint64_t pair_row = (uint64_t)i12 * (uint64_t)args.nei0 + (uint64_t)idx; device float *mid_f32 = (device float *)(dst_mid + pair_row * act.mid_row_stride); device const float *route_w = (device const float *)(weights + pair_row * act.weight_stride); const float c = act.clamp_value; const float route_weight = route_w[0]; if (tiisg == 0) { for (int row = 0; row < N_R0_Q4_K && first_row + row < args.ne0; ++row) { const uint out_row = first_row + row; float g = gate_f32[out_row]; float u = up_f32[out_row]; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); } const float silu = g / (1.0f + exp(-g)); mid_f32[out_row] = silu * u * route_weight; } } (void)tiitg; } kernel void kernel_q4_gather_slots6( constant ds4_metal_q4_gather_slots6_args &args, device const char *src_group0, device const char *src_group1, device const char *src_group2, device const char *src_group3, device const char *src_group4, device const char *src_group5, device const int32_t *ids, device char *dst, uint3 tgpig [[threadgroup_position_in_grid]], uint tiitg [[thread_index_in_threadgroup]]) { const uint slot = tgpig.y; if (slot >= args.n_slots || args.group_size == 0) return; const int32_t expert = ids[slot]; if (expert < 0) return; const uint expert_u = (uint)expert; const uint group = expert_u / args.group_size; if (group >= 6) return; const uint local_expert = expert_u - group * args.group_size; device const char *src_group = src_group0; switch (group) { case 1: src_group = src_group1; break; case 2: src_group = src_group2; break; case 3: src_group = src_group3; break; case 4: src_group = src_group4; break; case 5: src_group = src_group5; break; default: break; } const uint64_t chunk = (uint64_t)tgpig.x * 256ul + (uint64_t)tiitg; const uint64_t n_chunks = args.expert_bytes >> 4; if (chunk >= n_chunks) return; device const uint4 *src = (device const uint4 *)(src_group + (uint64_t)local_expert * args.expert_bytes); device uint4 *out = (device uint4 *)(dst + (uint64_t)slot * args.expert_bytes); out[chunk] = src[chunk]; } kernel void kernel_mul_mv_slots6_q4_K_pair_swiglu_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_dsv4_moe_swiglu_weight_args & act, device const char * src0_gate0, device const char * src0_gate1, device const char * src0_gate2, device const char * src0_gate3, device const char * src0_gate4, device const char * src0_gate5, device const char * src0_up0, device const char * src0_up1, device const char * src0_up2, device const char * src0_up3, device const char * src0_up4, device const char * src0_up5, device const char * src1, device char * dst_gate, device char * dst_up, device char * dst_mid, device const char * weights, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device const char *src0_gate_cur = src0_gate0; device const char *src0_up_cur = src0_up0; switch (idx) { case 1: src0_gate_cur = src0_gate1; src0_up_cur = src0_up1; break; case 2: src0_gate_cur = src0_gate2; src0_up_cur = src0_up2; break; case 3: src0_gate_cur = src0_gate3; src0_up_cur = src0_up3; break; case 4: src0_gate_cur = src0_gate4; src0_up_cur = src0_up4; break; case 5: src0_gate_cur = src0_gate5; src0_up_cur = src0_up5; break; default: break; } device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char *dst_gate_cur = dst_gate + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); device char *dst_up_cur = dst_up + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); ds4_metal_args_mul_mv args0 = { args.ne00, args.ne01, 1, args.nb00, args.nb01, args.nb02, args.nb02, args.ne10, 1, 1, args.nb10, args.nb11, args.nb12, args.nb12, args.ne0, 1, args.nr0, 1, 1, }; kernel_mul_mv_q4_K_f32_impl( args0, src0_gate_cur, src1_cur, dst_gate_cur, shmem, tgpig, tiisg, sgitg); kernel_mul_mv_q4_K_f32_impl( args0, src0_up_cur, src1_cur, dst_up_cur, shmem, tgpig, tiisg, sgitg); const short NSG = FC_mul_mv_nsg; const int first_row = (tgpig.x * NSG + sgitg) * N_R0_Q4_K; device float *gate_f32 = (device float *)dst_gate_cur; device float *up_f32 = (device float *)dst_up_cur; const uint64_t pair_row = (uint64_t)i12 * (uint64_t)args.nei0 + (uint64_t)idx; device float *mid_f32 = (device float *)(dst_mid + pair_row * act.mid_row_stride); device const float *route_w = (device const float *)(weights + pair_row * act.weight_stride); const float c = act.clamp_value; const float route_weight = route_w[0]; if (tiisg == 0) { for (int row = 0; row < N_R0_Q4_K && first_row + row < args.ne0; ++row) { const uint out_row = first_row + row; float g = gate_f32[out_row]; float u = up_f32[out_row]; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); } const float silu = g / (1.0f + exp(-g)); mid_f32[out_row] = silu * u * route_weight; } } (void)tiitg; } static inline device const char *ds4_q4_group24_select( uint32_t group_id, device const char *src00, device const char *src01, device const char *src02, device const char *src03, device const char *src04, device const char *src05, device const char *src06, device const char *src07, device const char *src08, device const char *src09, device const char *src10, device const char *src11, device const char *src12, device const char *src13, device const char *src14, device const char *src15, device const char *src16, device const char *src17, device const char *src18, device const char *src19, device const char *src20, device const char *src21, device const char *src22, device const char *src23) { switch (group_id) { case 1: return src01; case 2: return src02; case 3: return src03; case 4: return src04; case 5: return src05; case 6: return src06; case 7: return src07; case 8: return src08; case 9: return src09; case 10: return src10; case 11: return src11; case 12: return src12; case 13: return src13; case 14: return src14; case 15: return src15; case 16: return src16; case 17: return src17; case 18: return src18; case 19: return src19; case 20: return src20; case 21: return src21; case 22: return src22; case 23: return src23; default: return src00; } } kernel void kernel_mul_mv_group6_q4_K_pair_swiglu_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_dsv4_moe_swiglu_weight_args & act, device const char * src0_gate0, device const char * src0_gate1, device const char * src0_gate2, device const char * src0_gate3, device const char * src0_gate4, device const char * src0_gate5, device const char * src0_up0, device const char * src0_up1, device const char * src0_up2, device const char * src0_up3, device const char * src0_up4, device const char * src0_up5, device const char * src1, device char * dst_gate, device char * dst_up, device char * dst_mid, device const char * ids, device const char * weights, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { constexpr uint32_t expert_group_size = 64; const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int32_t expert = ((device const int32_t *)(ids + iid1 * args.nbi1))[idx]; if (expert < 0) { return; } const uint32_t expert_u = (uint32_t)expert; const uint32_t group_id = expert_u / expert_group_size; if (group_id >= 6) { return; } const uint32_t expert_local = expert_u - group_id * expert_group_size; device const char *src0_gate_cur = src0_gate0; device const char *src0_up_cur = src0_up0; switch (group_id) { case 1: src0_gate_cur = src0_gate1; src0_up_cur = src0_up1; break; case 2: src0_gate_cur = src0_gate2; src0_up_cur = src0_up2; break; case 3: src0_gate_cur = src0_gate3; src0_up_cur = src0_up3; break; case 4: src0_gate_cur = src0_gate4; src0_up_cur = src0_up4; break; case 5: src0_gate_cur = src0_gate5; src0_up_cur = src0_up5; break; default: break; } const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; src0_gate_cur += (uint64_t)expert_local * args.nb02; src0_up_cur += (uint64_t)expert_local * args.nb02; device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char *dst_gate_cur = dst_gate + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); device char *dst_up_cur = dst_up + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); ds4_metal_args_mul_mv args0 = { args.ne00, args.ne01, 1, args.nb00, args.nb01, args.nb02, args.nb02, args.ne10, 1, 1, args.nb10, args.nb11, args.nb12, args.nb12, args.ne0, 1, args.nr0, 1, 1, }; kernel_mul_mv_q4_K_f32_impl( args0, src0_gate_cur, src1_cur, dst_gate_cur, shmem, tgpig, tiisg, sgitg); kernel_mul_mv_q4_K_f32_impl( args0, src0_up_cur, src1_cur, dst_up_cur, shmem, tgpig, tiisg, sgitg); const short NSG = FC_mul_mv_nsg; const int first_row = (tgpig.x * NSG + sgitg) * N_R0_Q4_K; device float *gate_f32 = (device float *)dst_gate_cur; device float *up_f32 = (device float *)dst_up_cur; const uint64_t pair_row = (uint64_t)i12 * (uint64_t)args.nei0 + (uint64_t)idx; device float *mid_f32 = (device float *)(dst_mid + pair_row * act.mid_row_stride); device const float *route_w = (device const float *)(weights + pair_row * act.weight_stride); const float c = act.clamp_value; const float route_weight = route_w[0]; if (tiisg == 0) { for (int row = 0; row < N_R0_Q4_K && first_row + row < args.ne0; ++row) { const uint out_row = first_row + row; float g = gate_f32[out_row]; float u = up_f32[out_row]; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); } const float silu = g / (1.0f + exp(-g)); mid_f32[out_row] = silu * u * route_weight; } } (void)tiitg; } kernel void kernel_mul_mv_group8_q4_K_pair_swiglu_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_dsv4_moe_swiglu_weight_args & act, device const char * src0_gate0, device const char * src0_gate1, device const char * src0_gate2, device const char * src0_gate3, device const char * src0_gate4, device const char * src0_gate5, device const char * src0_gate6, device const char * src0_gate7, device const char * src0_up0, device const char * src0_up1, device const char * src0_up2, device const char * src0_up3, device const char * src0_up4, device const char * src0_up5, device const char * src0_up6, device const char * src0_up7, device const char * src1, device char * dst_gate, device char * dst_up, device char * dst_mid, device const char * ids, device const char * weights, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { constexpr uint32_t expert_group_size = 48; const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int32_t expert = ((device const int32_t *)(ids + iid1 * args.nbi1))[idx]; if (expert < 0) { return; } const uint32_t expert_u = (uint32_t)expert; const uint32_t group_id = expert_u / expert_group_size; if (group_id >= 8) { return; } const uint32_t expert_local = expert_u - group_id * expert_group_size; device const char *src0_gate_cur = src0_gate0; device const char *src0_up_cur = src0_up0; switch (group_id) { case 1: src0_gate_cur = src0_gate1; src0_up_cur = src0_up1; break; case 2: src0_gate_cur = src0_gate2; src0_up_cur = src0_up2; break; case 3: src0_gate_cur = src0_gate3; src0_up_cur = src0_up3; break; case 4: src0_gate_cur = src0_gate4; src0_up_cur = src0_up4; break; case 5: src0_gate_cur = src0_gate5; src0_up_cur = src0_up5; break; case 6: src0_gate_cur = src0_gate6; src0_up_cur = src0_up6; break; case 7: src0_gate_cur = src0_gate7; src0_up_cur = src0_up7; break; default: break; } const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; src0_gate_cur += (uint64_t)expert_local * args.nb02; src0_up_cur += (uint64_t)expert_local * args.nb02; device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char *dst_gate_cur = dst_gate + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); device char *dst_up_cur = dst_up + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); ds4_metal_args_mul_mv args0 = { args.ne00, args.ne01, 1, args.nb00, args.nb01, args.nb02, args.nb02, args.ne10, 1, 1, args.nb10, args.nb11, args.nb12, args.nb12, args.ne0, 1, args.nr0, 1, 1, }; kernel_mul_mv_q4_K_f32_impl( args0, src0_gate_cur, src1_cur, dst_gate_cur, shmem, tgpig, tiisg, sgitg); kernel_mul_mv_q4_K_f32_impl( args0, src0_up_cur, src1_cur, dst_up_cur, shmem, tgpig, tiisg, sgitg); const short NSG = FC_mul_mv_nsg; const int first_row = (tgpig.x * NSG + sgitg) * N_R0_Q4_K; device float *gate_f32 = (device float *)dst_gate_cur; device float *up_f32 = (device float *)dst_up_cur; const uint64_t pair_row = (uint64_t)i12 * (uint64_t)args.nei0 + (uint64_t)idx; device float *mid_f32 = (device float *)(dst_mid + pair_row * act.mid_row_stride); device const float *route_w = (device const float *)(weights + pair_row * act.weight_stride); const float c = act.clamp_value; const float route_weight = route_w[0]; if (tiisg == 0) { for (int row = 0; row < N_R0_Q4_K && first_row + row < args.ne0; ++row) { const uint out_row = first_row + row; float g = gate_f32[out_row]; float u = up_f32[out_row]; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); } const float silu = g / (1.0f + exp(-g)); mid_f32[out_row] = silu * u * route_weight; } } (void)tiitg; } kernel void kernel_mul_mv_group24_q4_K_id_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src00, device const char * src01, device const char * src02, device const char * src03, device const char * src04, device const char * src05, device const char * src06, device const char * src07, device const char * src08, device const char * src09, device const char * src10, device const char * src11, device const char * src12, device const char * src13, device const char * src14, device const char * src15, device const char * src16, device const char * src17, device const char * src18, device const char * src19, device const char * src20, device const char * src21, device const char * src22, device const char * src23, device const char * src1, device char * dst, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { constexpr uint32_t expert_group_size = 16; const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int32_t expert = ((device const int32_t *)(ids + iid1 * args.nbi1))[idx]; if (expert < 0) { return; } const uint32_t expert_u = (uint32_t)expert; const uint32_t group_id = expert_u / expert_group_size; if (group_id >= 24) { return; } const uint32_t expert_local = expert_u - group_id * expert_group_size; device const char *src0_cur = ds4_q4_group24_select(group_id, src00, src01, src02, src03, src04, src05, src06, src07, src08, src09, src10, src11, src12, src13, src14, src15, src16, src17, src18, src19, src20, src21, src22, src23); const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; src0_cur += (uint64_t)expert_local * args.nb02; device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char *dst_cur = dst + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); ds4_metal_args_mul_mv args0 = { args.ne00, args.ne01, 1, args.nb00, args.nb01, args.nb02, args.nb02, args.ne10, 1, 1, args.nb10, args.nb11, args.nb12, args.nb12, args.ne0, 1, args.nr0, 1, 1, }; kernel_mul_mv_q4_K_f32_impl( args0, src0_cur, src1_cur, dst_cur, shmem, tgpig, tiisg, sgitg); (void)tiitg; } kernel void kernel_mul_mv_group_q4_K_pair_swiglu_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_dsv4_moe_swiglu_weight_args & act, constant ds4_metal_moe_expert_group_args & group, device const char * src0_gate, device const char * src0_up, device const char * src1, device char * dst_gate, device char * dst_up, device char * dst_mid, device const char * ids, device const char * weights, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const int iid1 = tgpig.z / args.nei0; const int idx = tgpig.z % args.nei0; tgpig.z = 0; const int32_t expert_global = ((device const int32_t *)(ids + iid1 * args.nbi1))[idx]; if (expert_global < 0) { return; } const uint32_t expert_u = (uint32_t)expert_global; if (expert_u < group.expert_base || expert_u >= group.expert_base + group.expert_count) { return; } const uint32_t expert_local = expert_u - group.expert_base; const int64_t i11 = idx % args.ne11; const int64_t i12 = iid1; device const char *src0_gate_cur = src0_gate + (uint64_t)expert_local * args.nb02; device const char *src0_up_cur = src0_up + (uint64_t)expert_local * args.nb02; device const char *src1_cur = src1 + i11 * args.nb11 + i12 * args.nb12; device char *dst_gate_cur = dst_gate + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); device char *dst_up_cur = dst_up + (idx * args.ne0 + i12 * args.ne1 * args.ne0) * sizeof(float); ds4_metal_args_mul_mv args0 = { args.ne00, args.ne01, 1, args.nb00, args.nb01, args.nb02, args.nb02, args.ne10, 1, 1, args.nb10, args.nb11, args.nb12, args.nb12, args.ne0, 1, args.nr0, 1, 1, }; kernel_mul_mv_q4_K_f32_impl( args0, src0_gate_cur, src1_cur, dst_gate_cur, shmem, tgpig, tiisg, sgitg); kernel_mul_mv_q4_K_f32_impl( args0, src0_up_cur, src1_cur, dst_up_cur, shmem, tgpig, tiisg, sgitg); const short NSG = FC_mul_mv_nsg; const int first_row = (tgpig.x * NSG + sgitg) * N_R0_Q4_K; device float *gate_f32 = (device float *)dst_gate_cur; device float *up_f32 = (device float *)dst_up_cur; const uint64_t pair_row = (uint64_t)i12 * (uint64_t)args.nei0 + (uint64_t)idx; device float *mid_f32 = (device float *)(dst_mid + pair_row * act.mid_row_stride); device const float *route_w = (device const float *)(weights + pair_row * act.weight_stride); const float c = act.clamp_value; const float route_weight = route_w[0]; if (tiisg == 0) { for (int row = 0; row < N_R0_Q4_K && first_row + row < args.ne0; ++row) { const uint out_row = first_row + row; float g = gate_f32[out_row]; float u = up_f32[out_row]; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); } const float silu = g / (1.0f + exp(-g)); mid_f32[out_row] = silu * u * route_weight; } } (void)tiitg; } /* IQ2_XXS down projection summed over the token's selected experts (the * GLM 5.2 routed blobs are IQ2_XXS end to end, unlike DS4 Flash's Q2_K * down). Same contract as the q2_K sum kernel: mid rows already carry * silu(gate)*up*route_weight, expert ownership honors the TP split, and * add_in folds the shared-expert partial when tp_addend is set. */ kernel void kernel_mul_mv_id_iq2_xxs_sum6_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src0s, device const char * src1, device char * dst, device const char * ids, device const char * add_in, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_IQ2_XXS; const int nb = args.ne00/QK_K; const int nb32 = nb * (QK_K / 32); const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const int32_t *token_ids = (device const int32_t *)(ids + (uint64_t)token * args.nbi1); device const char *token_src1 = src1 + (uint64_t)token * args.nb12; float sumf[nr0] = {0.f}; float yl[32]; threadgroup uint64_t *svalues = (threadgroup uint64_t *)(shmem); threadgroup uint8_t *ssigns = (threadgroup uint8_t *)(svalues + 256); { int nval = 4; int pos = (32 * sgitg + tiisg) * nval; for (int i = 0; i < nval; ++i) svalues[pos + i] = ds4_metal_iq2xxs_grid[pos + i]; nval = 2; pos = (32 * sgitg + tiisg) * nval; for (int i = 0; i < nval; ++i) ssigns[pos + i] = ds4_metal_ksigns_iq2xs[pos + i]; threadgroup_barrier(mem_flags::mem_threadgroup); } const int ix = tiisg; for (int expert_slot = 0; expert_slot < args.nei0; expert_slot++) { const int32_t expert = token_ids[expert_slot]; if (!ds4_tp_owns_expert(expert, args.ne02, args.tp_rank, args.tp_world)) continue; device const block_iq2_xxs *x = (device const block_iq2_xxs *)(src0s + (int64_t)(expert - args.tp_expert_base)*args.nb02 + first_row*args.nb01); device const float *y = (device const float *)(token_src1 + expert_slot*args.nb11); device const float *y4 = y + 32 * ix; for (int ib32 = ix; ib32 < nb32; ib32 += 32) { for (short i = 0; i < 32; ++i) { yl[i] = y4[i]; } const int ibl = ib32 / (QK_K / 32); const int ib = ib32 % (QK_K / 32); device const block_iq2_xxs *xr = x + ibl; device const uint16_t *q2 = xr->qs + 4 * ib; device const half *dh = &xr->d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { device const uint8_t *aux8 = (device const uint8_t *)q2; const uint32_t aux32 = q2[2] | (q2[3] << 16); const float d = (float)dh[0] * (0.5f + (aux32 >> 28)); float s = 0; for (short l = 0; l < 4; ++l) { const threadgroup uint8_t *grid = (const threadgroup uint8_t *)(svalues + aux8[l]); const uint8_t sign = ssigns[(aux32 >> 7 * l) & 127]; for (short j = 0; j < 8; ++j) { s += yl[8 * l + j] * grid[j] * (sign & ds4_metal_kmask_iq2xs[j] ? -1.f : 1.f); } } sumf[row] += d * s; } dh += args.nb01 / 2; q2 += args.nb01 / 2; } y4 += 32 * 32; } } device float * dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]) * 0.25f; if (tiisg == 0) { float outv = sum_all; if (args.tp_addend) { outv += ((device const float *) add_in)[first_row + row]; } dst_f32[first_row + row] = outv; } } (void)shmem; (void)tiitg; (void)tgpig; } kernel void kernel_mul_mv_id_q2_K_sum6_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src0s, device const char * src1, device char * dst, device const char * ids, device const char * add_in, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_Q2_K; const int nb = args.ne00/QK_K; const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const int32_t *token_ids = (device const int32_t *)(ids + (uint64_t)token * args.nbi1); device const char *token_src1 = src1 + (uint64_t)token * args.nb12; float sumf[nr0] = {0.f}; const short ix = tiisg/8; const short it = tiisg%8; const short iq = it/4; const short ir = it%4; const short is = (8*ir)/16; for (int expert_slot = 0; expert_slot < args.nei0; expert_slot++) { const int32_t expert = token_ids[expert_slot]; if (!ds4_tp_owns_expert(expert, args.ne02, args.tp_rank, args.tp_world)) continue; device const block_q2_K * x = (device const block_q2_K *)(src0s + (int64_t)(expert - args.tp_expert_base)*args.nb02 + first_row*args.nb01); device const float * y = (device const float *)(token_src1 + expert_slot*args.nb11); device const float * y4 = y + ix * QK_K + 128 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[32]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i+ 0] = y4[i+ 0]; sumy[0] += yl[i+ 0]; yl[i+ 8] = y4[i+32]; sumy[1] += yl[i+ 8]; yl[i+16] = y4[i+64]; sumy[2] += yl[i+16]; yl[i+24] = y4[i+96]; sumy[3] += yl[i+24]; } device const uint8_t * sc = (device const uint8_t *)x[ib].scales + 8*iq + is; device const uint16_t * qs = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half * dh = &x[ib].d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; for (int i = 0; i < 8; i += 2) { acc1[0] += yl[i+ 0] * (qs[i/2] & 0x0003); acc2[0] += yl[i+ 1] * (qs[i/2] & 0x0300); acc1[1] += yl[i+ 8] * (qs[i/2] & 0x000c); acc2[1] += yl[i+ 9] * (qs[i/2] & 0x0c00); acc1[2] += yl[i+16] * (qs[i/2] & 0x0030); acc2[2] += yl[i+17] * (qs[i/2] & 0x3000); acc1[3] += yl[i+24] * (qs[i/2] & 0x00c0); acc2[3] += yl[i+25] * (qs[i/2] & 0xc000); } float dall = dh[0]; float dmin = dh[1] * 1.f/16.f; sumf[row] += dall * ((acc1[0] + 1.f/256.f * acc2[0]) * (sc[0] & 0xF) * 1.f/ 1.f + (acc1[1] + 1.f/256.f * acc2[1]) * (sc[2] & 0xF) * 1.f/ 4.f + (acc1[2] + 1.f/256.f * acc2[2]) * (sc[4] & 0xF) * 1.f/16.f + (acc1[3] + 1.f/256.f * acc2[3]) * (sc[6] & 0xF) * 1.f/64.f) - dmin * (sumy[0] * (sc[0] & 0xF0) + sumy[1] * (sc[2] & 0xF0) + sumy[2] * (sc[4] & 0xF0) + sumy[3] * (sc[6] & 0xF0)); } qs += args.nb01/2; sc += args.nb01; dh += args.nb01/2; } y4 += 4 * QK_K; } } device float * dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0) { float outv = sum_all; if (args.tp_addend) { outv += ((device const float *) add_in)[first_row + row]; } dst_f32[first_row + row] = outv; } } (void)shmem; (void)tiitg; (void)tgpig; } kernel void kernel_mul_mv_slots6_q2_K_sum6_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src00, device const char * src01, device const char * src02, device const char * src03, device const char * src04, device const char * src05, device const char * src1, device char * dst, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_Q2_K; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const char *token_src1 = src1 + (uint64_t)token * args.nb12; float sumf[nr0] = {0.f}; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const short is = (8 * ir) / 16; for (int expert_slot = 0; expert_slot < 6; expert_slot++) { device const char *src0_cur = src00; switch (expert_slot) { case 1: src0_cur = src01; break; case 2: src0_cur = src02; break; case 3: src0_cur = src03; break; case 4: src0_cur = src04; break; case 5: src0_cur = src05; break; default: break; } device const block_q2_K *x = (device const block_q2_K *)(src0_cur + first_row * args.nb01); device const float *y = (device const float *)(token_src1 + expert_slot * args.nb11); device const float *y4 = y + ix * QK_K + 128 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[32]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yl[i + 16] = y4[i + 64]; sumy[2] += yl[i + 16]; yl[i + 24] = y4[i + 96]; sumy[3] += yl[i + 24]; } device const uint8_t *sc = (device const uint8_t *)x[ib].scales + 8 * iq + is; device const uint16_t *qs = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; for (int i = 0; i < 8; i += 2) { acc1[0] += yl[i + 0] * (qs[i / 2] & 0x0003); acc2[0] += yl[i + 1] * (qs[i / 2] & 0x0300); acc1[1] += yl[i + 8] * (qs[i / 2] & 0x000c); acc2[1] += yl[i + 9] * (qs[i / 2] & 0x0c00); acc1[2] += yl[i + 16] * (qs[i / 2] & 0x0030); acc2[2] += yl[i + 17] * (qs[i / 2] & 0x3000); acc1[3] += yl[i + 24] * (qs[i / 2] & 0x00c0); acc2[3] += yl[i + 25] * (qs[i / 2] & 0xc000); } float dall = dh[0]; float dmin = dh[1] * 1.f / 16.f; sumf[row] += dall * ((acc1[0] + 1.f / 256.f * acc2[0]) * (sc[0] & 0xF) * 1.f / 1.f + (acc1[1] + 1.f / 256.f * acc2[1]) * (sc[2] & 0xF) * 1.f / 4.f + (acc1[2] + 1.f / 256.f * acc2[2]) * (sc[4] & 0xF) * 1.f / 16.f + (acc1[3] + 1.f / 256.f * acc2[3]) * (sc[6] & 0xF) * 1.f / 64.f) - dmin * (sumy[0] * (sc[0] & 0xF0) + sumy[1] * (sc[2] & 0xF0) + sumy[2] * (sc[4] & 0xF0) + sumy[3] * (sc[6] & 0xF0)); } qs += args.nb01 / 2; sc += args.nb01; dh += args.nb01 / 2; } y4 += 4 * QK_K; } } device float *dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0) dst_f32[first_row + row] = sum_all; } (void)shmem; (void)tiitg; (void)tgpig; } kernel void kernel_mul_mv_addr_q2_K_sum6_f32( constant ds4_metal_args_mul_mv_id & args, device const uint64_t * addrs, device const char * src1, device char * dst, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_Q2_K; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const char *token_src1 = src1 + (uint64_t)token * args.nb12; device const int32_t *token_ids = (device const int32_t *)(ids + (uint64_t)token * args.nbi1); float sumf[nr0] = {0.f}; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const short is = (8 * ir) / 16; for (int expert_slot = 0; expert_slot < 6; expert_slot++) { const int32_t expert = token_ids[expert_slot]; if (expert < 0 || expert >= args.ne02 || expert >= 384) { continue; } const uint64_t addr = addrs[(uint)expert]; if (addr == 0) { continue; } device const char *src0_cur = reinterpret_cast(addr); device const block_q2_K *x = (device const block_q2_K *)(src0_cur + first_row * args.nb01); device const float *y = (device const float *)(token_src1 + expert_slot * args.nb11); device const float *y4 = y + ix * QK_K + 128 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[32]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yl[i + 16] = y4[i + 64]; sumy[2] += yl[i + 16]; yl[i + 24] = y4[i + 96]; sumy[3] += yl[i + 24]; } device const uint8_t *sc = (device const uint8_t *)x[ib].scales + 8 * iq + is; device const uint16_t *qs = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; for (int i = 0; i < 8; i += 2) { acc1[0] += yl[i + 0] * (qs[i / 2] & 0x0003); acc2[0] += yl[i + 1] * (qs[i / 2] & 0x0300); acc1[1] += yl[i + 8] * (qs[i / 2] & 0x000c); acc2[1] += yl[i + 9] * (qs[i / 2] & 0x0c00); acc1[2] += yl[i + 16] * (qs[i / 2] & 0x0030); acc2[2] += yl[i + 17] * (qs[i / 2] & 0x3000); acc1[3] += yl[i + 24] * (qs[i / 2] & 0x00c0); acc2[3] += yl[i + 25] * (qs[i / 2] & 0xc000); } float dall = dh[0]; float dmin = dh[1] * 1.f / 16.f; sumf[row] += dall * ((acc1[0] + 1.f / 256.f * acc2[0]) * (sc[0] & 0xF) * 1.f / 1.f + (acc1[1] + 1.f / 256.f * acc2[1]) * (sc[2] & 0xF) * 1.f / 4.f + (acc1[2] + 1.f / 256.f * acc2[2]) * (sc[4] & 0xF) * 1.f / 16.f + (acc1[3] + 1.f / 256.f * acc2[3]) * (sc[6] & 0xF) * 1.f / 64.f) - dmin * (sumy[0] * (sc[0] & 0xF0) + sumy[1] * (sc[2] & 0xF0) + sumy[2] * (sc[4] & 0xF0) + sumy[3] * (sc[6] & 0xF0)); } qs += args.nb01 / 2; sc += args.nb01; dh += args.nb01 / 2; } y4 += 4 * QK_K; } } device float * dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0) dst_f32[first_row + row] = sum_all; } (void)shmem; (void)tiitg; (void)tgpig; } kernel void kernel_mul_mv_addr_q2_K_sum6_masked_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_stream_expert_split_args & split, device const uint64_t * addrs, device const char * src1, device char * dst, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_Q2_K; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const char *token_src1 = src1 + (uint64_t)token * args.nb12; device const int32_t *token_ids = (device const int32_t *)(ids + (uint64_t)token * args.nbi1); float sumf[nr0] = {0.f}; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; const short is = (8 * ir) / 16; for (int expert_slot = 0; expert_slot < 6; expert_slot++) { if ((split.active_mask & (1u << (uint)expert_slot)) == 0) { continue; } const int32_t expert = token_ids[expert_slot]; if (expert < 0 || expert >= args.ne02 || expert >= 384) { continue; } const uint64_t addr = addrs[(uint)expert]; if (addr == 0) { continue; } device const char *src0_cur = reinterpret_cast(addr); device const block_q2_K *x = (device const block_q2_K *)(src0_cur + first_row * args.nb01); device const float *y = (device const float *)(token_src1 + expert_slot * args.nb11); device const float *y4 = y + ix * QK_K + 128 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[32]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yl[i + 16] = y4[i + 64]; sumy[2] += yl[i + 16]; yl[i + 24] = y4[i + 96]; sumy[3] += yl[i + 24]; } device const uint8_t *sc = (device const uint8_t *)x[ib].scales + 8 * iq + is; device const uint16_t *qs = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; for (int i = 0; i < 8; i += 2) { acc1[0] += yl[i + 0] * (qs[i / 2] & 0x0003); acc2[0] += yl[i + 1] * (qs[i / 2] & 0x0300); acc1[1] += yl[i + 8] * (qs[i / 2] & 0x000c); acc2[1] += yl[i + 9] * (qs[i / 2] & 0x0c00); acc1[2] += yl[i + 16] * (qs[i / 2] & 0x0030); acc2[2] += yl[i + 17] * (qs[i / 2] & 0x3000); acc1[3] += yl[i + 24] * (qs[i / 2] & 0x00c0); acc2[3] += yl[i + 25] * (qs[i / 2] & 0xc000); } float dall = dh[0]; float dmin = dh[1] * 1.f / 16.f; sumf[row] += dall * ((acc1[0] + 1.f / 256.f * acc2[0]) * (sc[0] & 0xF) * 1.f / 1.f + (acc1[1] + 1.f / 256.f * acc2[1]) * (sc[2] & 0xF) * 1.f / 4.f + (acc1[2] + 1.f / 256.f * acc2[2]) * (sc[4] & 0xF) * 1.f / 16.f + (acc1[3] + 1.f / 256.f * acc2[3]) * (sc[6] & 0xF) * 1.f / 64.f) - dmin * (sumy[0] * (sc[0] & 0xF0) + sumy[1] * (sc[2] & 0xF0) + sumy[2] * (sc[4] & 0xF0) + sumy[3] * (sc[6] & 0xF0)); } qs += args.nb01 / 2; sc += args.nb01; dh += args.nb01 / 2; } y4 += 4 * QK_K; } } device float * dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0) { if (split.accumulate) { dst_f32[first_row + row] += sum_all; } else { dst_f32[first_row + row] = sum_all; } } } (void)shmem; (void)tiitg; (void)tgpig; } kernel void kernel_mul_mv_id_q4_K_sum6_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src0s, device const char * src1, device char * dst, device const char * ids, device const char * add_in, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_Q4_K; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const int32_t *token_ids = (device const int32_t *)(ids + (uint64_t)token * args.nbi1); device const char *token_src1 = src1 + (uint64_t)token * args.nb12; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; float sumf[nr0] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int expert_slot = 0; expert_slot < args.nei0; expert_slot++) { const int32_t expert = token_ids[expert_slot]; if (!ds4_tp_owns_expert(expert, args.ne02, args.tp_rank, args.tp_world)) continue; device const block_q4_K *x = (device const block_q4_K *)(src0s + (int64_t)(expert - args.tp_expert_base) * args.nb02 + first_row * args.nb01); device const float *y = (device const float *)(token_src1 + expert_slot * args.nb11); device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1[0] += yl[2 * i + 0] * (q1[i] & 0x000F); acc1[1] += yl[2 * i + 1] * (q1[i] & 0x0F00); acc1[2] += yl[2 * i + 8] * (q1[i] & 0x00F0); acc1[3] += yl[2 * i + 9] * (q1[i] & 0xF000); acc2[0] += yh[2 * i + 0] * (q2[i] & 0x000F); acc2[1] += yh[2 * i + 1] * (q2[i] & 0x0F00); acc2[2] += yh[2 * i + 8] * (q2[i] & 0x00F0); acc2[3] += yh[2 * i + 9] * (q2[i] & 0xF000); } sumf[row] += dh[0] * ((acc1[0] + 1.f / 256.f * acc1[1]) * sc8[0] + (acc1[2] + 1.f / 256.f * acc1[3]) * sc8[1] * 1.f / 16.f + (acc2[0] + 1.f / 256.f * acc2[1]) * sc8[4] + (acc2[2] + 1.f / 256.f * acc2[3]) * sc8[5] * 1.f / 16.f) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); } q1 += args.nb01 / 2; sc += args.nb01 / 2; dh += args.nb01 / 2; } y4 += 4 * QK_K; } } device float *dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0) { float outv = sum_all; if (args.tp_addend) { outv += ((device const float *) add_in)[first_row + row]; } dst_f32[first_row + row] = outv; } } (void)shmem; (void)tiitg; (void)tgpig; } kernel void kernel_mul_mv_group_q4_K_sum6_f32( constant ds4_metal_args_mul_mv_id & args, constant ds4_metal_moe_expert_group_args & group, device const char * src0s, device const char * src1, device char * dst, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_Q4_K; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const int32_t *token_ids = (device const int32_t *)(ids + (uint64_t)token * args.nbi1); device const char *token_src1 = src1 + (uint64_t)token * args.nb12; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; float sumf[nr0] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int expert_slot = 0; expert_slot < 6; expert_slot++) { const int32_t expert = token_ids[expert_slot]; if (expert < 0) { continue; } const uint32_t expert_u = (uint32_t)expert; if (expert_u < group.expert_base || expert_u >= group.expert_base + group.expert_count) { continue; } const uint32_t expert_local = expert_u - group.expert_base; device const block_q4_K *x = (device const block_q4_K *)(src0s + (uint64_t)expert_local * args.nb02 + first_row * args.nb01); device const float *y = (device const float *)(token_src1 + expert_slot * args.nb11); device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1[0] += yl[2 * i + 0] * (q1[i] & 0x000F); acc1[1] += yl[2 * i + 1] * (q1[i] & 0x0F00); acc1[2] += yl[2 * i + 8] * (q1[i] & 0x00F0); acc1[3] += yl[2 * i + 9] * (q1[i] & 0xF000); acc2[0] += yh[2 * i + 0] * (q2[i] & 0x000F); acc2[1] += yh[2 * i + 1] * (q2[i] & 0x0F00); acc2[2] += yh[2 * i + 8] * (q2[i] & 0x00F0); acc2[3] += yh[2 * i + 9] * (q2[i] & 0xF000); } sumf[row] += dh[0] * ((acc1[0] + 1.f / 256.f * acc1[1]) * sc8[0] + (acc1[2] + 1.f / 256.f * acc1[3]) * sc8[1] * 1.f / 16.f + (acc2[0] + 1.f / 256.f * acc2[1]) * sc8[4] + (acc2[2] + 1.f / 256.f * acc2[3]) * sc8[5] * 1.f / 16.f) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); } q1 += args.nb01 / 2; sc += args.nb01 / 2; dh += args.nb01 / 2; } y4 += 4 * QK_K; } } device float *dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0) { if (group.accumulate) { dst_f32[first_row + row] += sum_all; } else { dst_f32[first_row + row] = sum_all; } } } (void)shmem; (void)tiitg; (void)tgpig; } kernel void kernel_mul_mv_table_q4_K_sum6_f32( constant ds4_metal_args_mul_mv_id & args, device const ds4_metal_q4_expert_table & table, device const char * src1, device char * dst, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_Q4_K; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const int32_t *token_ids = (device const int32_t *)(ids + (uint64_t)token * args.nbi1); device const char *token_src1 = src1 + (uint64_t)token * args.nb12; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; float sumf[nr0] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int expert_slot = 0; expert_slot < 6; expert_slot++) { const int32_t expert = token_ids[expert_slot]; if (expert < 0 || expert >= args.ne02 || expert >= 384) { return; } device const block_q4_K *x = (device const block_q4_K *)(table.experts[(uint)expert] + first_row * args.nb01); device const float *y = (device const float *)(token_src1 + expert_slot * args.nb11); device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1[0] += yl[2 * i + 0] * (q1[i] & 0x000F); acc1[1] += yl[2 * i + 1] * (q1[i] & 0x0F00); acc1[2] += yl[2 * i + 8] * (q1[i] & 0x00F0); acc1[3] += yl[2 * i + 9] * (q1[i] & 0xF000); acc2[0] += yh[2 * i + 0] * (q2[i] & 0x000F); acc2[1] += yh[2 * i + 1] * (q2[i] & 0x0F00); acc2[2] += yh[2 * i + 8] * (q2[i] & 0x00F0); acc2[3] += yh[2 * i + 9] * (q2[i] & 0xF000); } sumf[row] += dh[0] * ((acc1[0] + 1.f / 256.f * acc1[1]) * sc8[0] + (acc1[2] + 1.f / 256.f * acc1[3]) * sc8[1] * 1.f / 16.f + (acc2[0] + 1.f / 256.f * acc2[1]) * sc8[4] + (acc2[2] + 1.f / 256.f * acc2[3]) * sc8[5] * 1.f / 16.f) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); } q1 += args.nb01 / 2; sc += args.nb01 / 2; dh += args.nb01 / 2; } y4 += 4 * QK_K; } } device float *dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0) dst_f32[first_row + row] = sum_all; } (void)shmem; (void)tiitg; (void)tgpig; } kernel void kernel_mul_mv_addr_q4_K_sum6_f32( constant ds4_metal_args_mul_mv_id & args, device const ulong * addrs, device const char * src1, device char * dst, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_Q4_K; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const int32_t *token_ids = (device const int32_t *)(ids + (uint64_t)token * args.nbi1); device const char *token_src1 = src1 + (uint64_t)token * args.nb12; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; float sumf[nr0] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int expert_slot = 0; expert_slot < 6; expert_slot++) { const int32_t expert = token_ids[expert_slot]; if (expert < 0 || expert >= args.ne02 || expert >= 384) { return; } device const char *expert_base = reinterpret_cast(addrs[(uint)expert]); device const block_q4_K *x = (device const block_q4_K *)(expert_base + first_row * args.nb01); device const float *y = (device const float *)(token_src1 + expert_slot * args.nb11); device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1[0] += yl[2 * i + 0] * (q1[i] & 0x000F); acc1[1] += yl[2 * i + 1] * (q1[i] & 0x0F00); acc1[2] += yl[2 * i + 8] * (q1[i] & 0x00F0); acc1[3] += yl[2 * i + 9] * (q1[i] & 0xF000); acc2[0] += yh[2 * i + 0] * (q2[i] & 0x000F); acc2[1] += yh[2 * i + 1] * (q2[i] & 0x0F00); acc2[2] += yh[2 * i + 8] * (q2[i] & 0x00F0); acc2[3] += yh[2 * i + 9] * (q2[i] & 0xF000); } sumf[row] += dh[0] * ((acc1[0] + 1.f / 256.f * acc1[1]) * sc8[0] + (acc1[2] + 1.f / 256.f * acc1[3]) * sc8[1] * 1.f / 16.f + (acc2[0] + 1.f / 256.f * acc2[1]) * sc8[4] + (acc2[2] + 1.f / 256.f * acc2[3]) * sc8[5] * 1.f / 16.f) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); } q1 += args.nb01 / 2; sc += args.nb01 / 2; dh += args.nb01 / 2; } y4 += 4 * QK_K; } } device float *dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0) dst_f32[first_row + row] = sum_all; } (void)shmem; (void)tiitg; (void)tgpig; } kernel void kernel_mul_mv_slots6_q4_K_sum6_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src00, device const char * src01, device const char * src02, device const char * src03, device const char * src04, device const char * src05, device const char * src1, device char * dst, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_Q4_K; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const char *token_src1 = src1 + (uint64_t)token * args.nb12; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; float sumf[nr0] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int expert_slot = 0; expert_slot < 6; expert_slot++) { device const char *src0_cur = src00; switch (expert_slot) { case 1: src0_cur = src01; break; case 2: src0_cur = src02; break; case 3: src0_cur = src03; break; case 4: src0_cur = src04; break; case 5: src0_cur = src05; break; default: break; } device const block_q4_K *x = (device const block_q4_K *)(src0_cur + first_row * args.nb01); device const float *y = (device const float *)(token_src1 + expert_slot * args.nb11); device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1[0] += yl[2 * i + 0] * (q1[i] & 0x000F); acc1[1] += yl[2 * i + 1] * (q1[i] & 0x0F00); acc1[2] += yl[2 * i + 8] * (q1[i] & 0x00F0); acc1[3] += yl[2 * i + 9] * (q1[i] & 0xF000); acc2[0] += yh[2 * i + 0] * (q2[i] & 0x000F); acc2[1] += yh[2 * i + 1] * (q2[i] & 0x0F00); acc2[2] += yh[2 * i + 8] * (q2[i] & 0x00F0); acc2[3] += yh[2 * i + 9] * (q2[i] & 0xF000); } sumf[row] += dh[0] * ((acc1[0] + 1.f / 256.f * acc1[1]) * sc8[0] + (acc1[2] + 1.f / 256.f * acc1[3]) * sc8[1] * 1.f / 16.f + (acc2[0] + 1.f / 256.f * acc2[1]) * sc8[4] + (acc2[2] + 1.f / 256.f * acc2[3]) * sc8[5] * 1.f / 16.f) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); } q1 += args.nb01 / 2; sc += args.nb01 / 2; dh += args.nb01 / 2; } y4 += 4 * QK_K; } } device float *dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0) dst_f32[first_row + row] = sum_all; } (void)shmem; (void)tiitg; (void)tgpig; } kernel void kernel_mul_mv_group6_q4_K_sum6_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src00, device const char * src01, device const char * src02, device const char * src03, device const char * src04, device const char * src05, device const char * src1, device char * dst, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { constexpr uint32_t expert_group_size = 64; const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_Q4_K; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const int32_t *token_ids = (device const int32_t *)(ids + (uint64_t)token * args.nbi1); device const char *token_src1 = src1 + (uint64_t)token * args.nb12; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; float sumf[nr0] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int expert_slot = 0; expert_slot < 6; expert_slot++) { const int32_t expert = token_ids[expert_slot]; if (expert < 0) { continue; } const uint32_t expert_u = (uint32_t)expert; const uint32_t group_id = expert_u / expert_group_size; if (group_id >= 6) { continue; } const uint32_t expert_local = expert_u - group_id * expert_group_size; device const char *src0_cur = src00; switch (group_id) { case 1: src0_cur = src01; break; case 2: src0_cur = src02; break; case 3: src0_cur = src03; break; case 4: src0_cur = src04; break; case 5: src0_cur = src05; break; default: break; } device const block_q4_K *x = (device const block_q4_K *)(src0_cur + (uint64_t)expert_local * args.nb02 + first_row * args.nb01); device const float *y = (device const float *)(token_src1 + expert_slot * args.nb11); device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1[0] += yl[2 * i + 0] * (q1[i] & 0x000F); acc1[1] += yl[2 * i + 1] * (q1[i] & 0x0F00); acc1[2] += yl[2 * i + 8] * (q1[i] & 0x00F0); acc1[3] += yl[2 * i + 9] * (q1[i] & 0xF000); acc2[0] += yh[2 * i + 0] * (q2[i] & 0x000F); acc2[1] += yh[2 * i + 1] * (q2[i] & 0x0F00); acc2[2] += yh[2 * i + 8] * (q2[i] & 0x00F0); acc2[3] += yh[2 * i + 9] * (q2[i] & 0xF000); } sumf[row] += dh[0] * ((acc1[0] + 1.f / 256.f * acc1[1]) * sc8[0] + (acc1[2] + 1.f / 256.f * acc1[3]) * sc8[1] * 1.f / 16.f + (acc2[0] + 1.f / 256.f * acc2[1]) * sc8[4] + (acc2[2] + 1.f / 256.f * acc2[3]) * sc8[5] * 1.f / 16.f) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); } q1 += args.nb01 / 2; sc += args.nb01 / 2; dh += args.nb01 / 2; } y4 += 4 * QK_K; } } device float *dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0) dst_f32[first_row + row] = sum_all; } (void)shmem; (void)tiitg; (void)tgpig; } kernel void kernel_mul_mv_group8_q4_K_sum6_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src00, device const char * src01, device const char * src02, device const char * src03, device const char * src04, device const char * src05, device const char * src06, device const char * src07, device const char * src1, device char * dst, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { constexpr uint32_t expert_group_size = 48; const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_Q4_K; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const int32_t *token_ids = (device const int32_t *)(ids + (uint64_t)token * args.nbi1); device const char *token_src1 = src1 + (uint64_t)token * args.nb12; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; float sumf[nr0] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int expert_slot = 0; expert_slot < 6; expert_slot++) { const int32_t expert = token_ids[expert_slot]; if (expert < 0) { continue; } const uint32_t expert_u = (uint32_t)expert; const uint32_t group_id = expert_u / expert_group_size; if (group_id >= 8) { continue; } const uint32_t expert_local = expert_u - group_id * expert_group_size; device const char *src0_cur = src00; switch (group_id) { case 1: src0_cur = src01; break; case 2: src0_cur = src02; break; case 3: src0_cur = src03; break; case 4: src0_cur = src04; break; case 5: src0_cur = src05; break; case 6: src0_cur = src06; break; case 7: src0_cur = src07; break; default: break; } device const block_q4_K *x = (device const block_q4_K *)(src0_cur + (uint64_t)expert_local * args.nb02 + first_row * args.nb01); device const float *y = (device const float *)(token_src1 + expert_slot * args.nb11); device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1[0] += yl[2 * i + 0] * (q1[i] & 0x000F); acc1[1] += yl[2 * i + 1] * (q1[i] & 0x0F00); acc1[2] += yl[2 * i + 8] * (q1[i] & 0x00F0); acc1[3] += yl[2 * i + 9] * (q1[i] & 0xF000); acc2[0] += yh[2 * i + 0] * (q2[i] & 0x000F); acc2[1] += yh[2 * i + 1] * (q2[i] & 0x0F00); acc2[2] += yh[2 * i + 8] * (q2[i] & 0x00F0); acc2[3] += yh[2 * i + 9] * (q2[i] & 0xF000); } sumf[row] += dh[0] * ((acc1[0] + 1.f / 256.f * acc1[1]) * sc8[0] + (acc1[2] + 1.f / 256.f * acc1[3]) * sc8[1] * 1.f / 16.f + (acc2[0] + 1.f / 256.f * acc2[1]) * sc8[4] + (acc2[2] + 1.f / 256.f * acc2[3]) * sc8[5] * 1.f / 16.f) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); } q1 += args.nb01 / 2; sc += args.nb01 / 2; dh += args.nb01 / 2; } y4 += 4 * QK_K; } } device float *dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0) dst_f32[first_row + row] = sum_all; } (void)shmem; (void)tiitg; (void)tgpig; } kernel void kernel_mul_mv_group24_q4_K_sum6_f32( constant ds4_metal_args_mul_mv_id & args, device const char * src00, device const char * src01, device const char * src02, device const char * src03, device const char * src04, device const char * src05, device const char * src06, device const char * src07, device const char * src08, device const char * src09, device const char * src10, device const char * src11, device const char * src12, device const char * src13, device const char * src14, device const char * src15, device const char * src16, device const char * src17, device const char * src18, device const char * src19, device const char * src20, device const char * src21, device const char * src22, device const char * src23, device const char * src1, device char * dst, device const char * ids, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { constexpr uint32_t expert_group_size = 16; const short NSG = FC_mul_mv_nsg; const short nr0 = N_R0_Q4_K; const int nb = args.ne00 / QK_K; const int first_row = (tgpig.x * NSG + sgitg) * nr0; const uint token = tgpig.y; device const int32_t *token_ids = (device const int32_t *)(ids + (uint64_t)token * args.nbi1); device const char *token_src1 = src1 + (uint64_t)token * args.nb12; constexpr uint16_t kmask1 = 0x3f3f; constexpr uint16_t kmask2 = 0x0f0f; constexpr uint16_t kmask3 = 0xc0c0; const short ix = tiisg / 8; const short it = tiisg % 8; const short iq = it / 4; const short ir = it % 4; float sumf[nr0] = {0.f}; uint16_t sc16[4]; thread const uint8_t *sc8 = (thread const uint8_t *)sc16; for (int expert_slot = 0; expert_slot < 6; expert_slot++) { const int32_t expert = token_ids[expert_slot]; if (expert < 0) { continue; } const uint32_t expert_u = (uint32_t)expert; const uint32_t group_id = expert_u / expert_group_size; if (group_id >= 24) { continue; } const uint32_t expert_local = expert_u - group_id * expert_group_size; device const char *src0_cur = ds4_q4_group24_select(group_id, src00, src01, src02, src03, src04, src05, src06, src07, src08, src09, src10, src11, src12, src13, src14, src15, src16, src17, src18, src19, src20, src21, src22, src23); device const block_q4_K *x = (device const block_q4_K *)(src0_cur + (uint64_t)expert_local * args.nb02 + first_row * args.nb01); device const float *y = (device const float *)(token_src1 + expert_slot * args.nb11); device const float *y4 = y + ix * QK_K + 64 * iq + 8 * ir; for (int ib = ix; ib < nb; ib += 4) { float yl[16]; float yh[16]; float4 sumy = {0.f, 0.f, 0.f, 0.f}; for (short i = 0; i < 8; ++i) { yl[i + 0] = y4[i + 0]; sumy[0] += yl[i + 0]; yl[i + 8] = y4[i + 32]; sumy[1] += yl[i + 8]; yh[i + 0] = y4[i + 128]; sumy[2] += yh[i + 0]; yh[i + 8] = y4[i + 160]; sumy[3] += yh[i + 8]; } device const uint16_t *sc = (device const uint16_t *)x[ib].scales + iq; device const uint16_t *q1 = (device const uint16_t *)x[ib].qs + 16 * iq + 4 * ir; device const half *dh = &x[ib].d; for (short row = 0; row < nr0; row++) { if (first_row + row < args.ne0) { sc16[0] = sc[0] & kmask1; sc16[1] = sc[2] & kmask1; sc16[2] = ((sc[4] >> 0) & kmask2) | ((sc[0] & kmask3) >> 2); sc16[3] = ((sc[4] >> 4) & kmask2) | ((sc[2] & kmask3) >> 2); device const uint16_t *q2 = q1 + 32; float4 acc1 = {0.f, 0.f, 0.f, 0.f}; float4 acc2 = {0.f, 0.f, 0.f, 0.f}; FOR_UNROLL (short i = 0; i < 4; ++i) { acc1[0] += yl[2 * i + 0] * (q1[i] & 0x000F); acc1[1] += yl[2 * i + 1] * (q1[i] & 0x0F00); acc1[2] += yl[2 * i + 8] * (q1[i] & 0x00F0); acc1[3] += yl[2 * i + 9] * (q1[i] & 0xF000); acc2[0] += yh[2 * i + 0] * (q2[i] & 0x000F); acc2[1] += yh[2 * i + 1] * (q2[i] & 0x0F00); acc2[2] += yh[2 * i + 8] * (q2[i] & 0x00F0); acc2[3] += yh[2 * i + 9] * (q2[i] & 0xF000); } sumf[row] += dh[0] * ((acc1[0] + 1.f / 256.f * acc1[1]) * sc8[0] + (acc1[2] + 1.f / 256.f * acc1[3]) * sc8[1] * 1.f / 16.f + (acc2[0] + 1.f / 256.f * acc2[1]) * sc8[4] + (acc2[2] + 1.f / 256.f * acc2[3]) * sc8[5] * 1.f / 16.f) - dh[1] * (sumy[0] * sc8[2] + sumy[1] * sc8[3] + sumy[2] * sc8[6] + sumy[3] * sc8[7]); } q1 += args.nb01 / 2; sc += args.nb01 / 2; dh += args.nb01 / 2; } y4 += 4 * QK_K; } } device float *dst_f32 = (device float *)(dst + (uint64_t)token * args.nb1); for (int row = 0; row < nr0 && first_row + row < args.ne0; row++) { const float sum_all = simd_sum(sumf[row]); if (tiisg == 0) dst_f32[first_row + row] = sum_all; } (void)shmem; (void)tiitg; (void)tgpig; } #define QK_NL 16 // Builds the compact per-expert work map used by batched MoE matmul. DS4 routes // each token to a small fixed top-k list, so this turns token-major ids into // expert-major slices that the tiled matmul can consume. template kernel void kernel_mul_mm_id_map0( constant ds4_metal_args_mul_mm_id_map0 & args, device const char * src2, device char * htpe, device char * hids, threadgroup char * shmem [[threadgroup(0)]], ushort tpitg[[thread_position_in_threadgroup]], ushort ntg[[threads_per_threadgroup]]) { const short ide = tpitg; uint32_t n_all = 0; device int32_t * ids_i32 = (device int32_t *) hids + ide*args.ne21; for (int i21 = 0; i21 < args.ne21; i21 += ntg) { if (i21 + tpitg < args.ne21) { device const int32_t * src2_i32 = (device const int32_t *) (src2 + (i21 + tpitg)*args.nb21); threadgroup uint16_t * sids = (threadgroup uint16_t *) shmem + tpitg*ne20; #pragma unroll(ne20) for (short i20 = 0; i20 < ne20; i20++) { sids[i20] = src2_i32[i20]; } } threadgroup_barrier(mem_flags::mem_threadgroup); for (short t = 0; t < ntg; t++) { if (i21 + t >= args.ne21) { break; } threadgroup const uint16_t * sids = (threadgroup const uint16_t *) shmem + t*ne20; short sel = 0; #pragma unroll(ne20) for (short i20 = 0; i20 < ne20; i20++) { sel += (sids[i20] == ide)*(i20 + 1); } ids_i32[n_all] = (i21 + t)*ne20 + sel - 1; n_all += sel > 0; } threadgroup_barrier(mem_flags::mem_threadgroup); } device uint32_t * tpe_u32 = (device uint32_t *) (htpe); tpe_u32[ide] = n_all; } typedef decltype(kernel_mul_mm_id_map0<1>) kernel_mul_mm_id_map0_t; // Host-visible map builders for the routed-expert counts used by DS4 graph // shapes. Some arities are generic leftovers retained for nearby batch sizes. template [[host_name("kernel_mul_mm_id_map0_ne20_1" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<1>; template [[host_name("kernel_mul_mm_id_map0_ne20_2" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<2>; template [[host_name("kernel_mul_mm_id_map0_ne20_4" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<4>; template [[host_name("kernel_mul_mm_id_map0_ne20_5" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<5>; template [[host_name("kernel_mul_mm_id_map0_ne20_6" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<6>; template [[host_name("kernel_mul_mm_id_map0_ne20_8" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<8>; template [[host_name("kernel_mul_mm_id_map0_ne20_10")]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<10>; template [[host_name("kernel_mul_mm_id_map0_ne20_16")]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<16>; template [[host_name("kernel_mul_mm_id_map0_ne20_22")]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<22>; // Batched routed-expert matmul. It reads the expert-major map produced above, // loads selected expert weights, and writes results back to token-major slots // so the DS4 FFN can apply SwiGLU, weighting, and the down projection. template kernel void kernel_mul_mm_id( constant ds4_metal_args_mul_mm_id & args, device const char * src0, device const char * src1, device const char * htpe, device const char * hids, device char * dst, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { constexpr int NR0 = 64; static_assert(NR1 == 32, "kernel_mul_mm_id accumulator layout supports only 32 routed rows"); constexpr int NK = 32; constexpr int NL0 = NK/16; constexpr int NL1 = NK/8; constexpr int SA_BYTES = NR0 * NR1 * (int)sizeof(S0); threadgroup S0 * sa = (threadgroup S0 *)(shmem); threadgroup S1 * sb = (threadgroup S1 *)(shmem + SA_BYTES); const int im = tgpig.z; const int r0 = tgpig.y*NR0; const int r1 = tgpig.x*NR1; device const uint32_t * tpe_u32 = (device const uint32_t *) (htpe); device const int32_t * ids_i32 = (device const int32_t *) (hids); const int32_t neh1 = tpe_u32[im]; if (r1 >= neh1) { return; } const short nr0 = (args.ne0 - r0 < NR0) ? (args.ne0 - r0) : NR0; const short nr1 = ( neh1 - r1 < NR1) ? ( neh1 - r1) : NR1; if (!ds4_tp_owns_expert(im, args.ne02, args.tp_rank, args.tp_world)) { /* Unowned expert under the TP split: zero this tile's output rows so * the downstream swiglu/sum stages stay unchanged. Each (token,slot) * row belongs to exactly one expert, so nothing else writes them. */ for (short j = sgitg; j < nr1; j += 4) { const int idj = ids_i32[im*args.ne21 + r1 + j]; const short ide = idj % args.ne20; const short idt = idj / args.ne20; device float * D = (device float *) dst + r0 + ide*args.ne0 + idt*args.ne1*args.ne0; for (int i = tiisg; i < nr0; i += 32) { D[i] = 0.0f; } } return; } const short lr0 = ((short)tiitg/NL0) < nr0 ? ((short)tiitg/NL0) : nr0 - 1; const short lr1 = ((short)tiitg/NL1) < nr1 ? ((short)tiitg/NL1) : nr1 - 1; const short il0 = (tiitg % NL0); short il = il0; const int id = ids_i32[im*args.ne21 + r1 + lr1]; const short i11 = (id % args.ne20) % args.ne11; const short i12 = (id / args.ne20); const short i13 = 0; const uint64_t offset0 = (uint64_t)(im - args.tp_expert_base)*args.nb02 + i13*args.nb03; const short offset1 = il0/nl; device const block_q * x = (device const block_q *)(src0 + args.nb01*(r0 + lr0) + offset0) + offset1; const short iy = 8*(tiitg % NL1); device const T1 * y = (device const T1 *)(src1 + args.nb13*i13 + args.nb12*i12 + args.nb11*i11 + args.nb10*iy); S0_8x8 ma[4]; S1_8x8 mb[2]; simdgroup_float8x8 mc[8]; for (short i = 0; i < 8; i++){ mc[i] = make_filled_simdgroup_matrix(0.f); } for (int loop_k = 0; loop_k < args.ne00; loop_k += NK) { if (is_same::value && FC_mul_mm_bc_inp) { threadgroup_barrier(mem_flags::mem_threadgroup); for (short i = 0; i < 16; i++) { const short sx = 2*il0 + i/8; const short sy = (tiitg/NL0)/8; const short lx = (tiitg/NL0)%8; const short ly = i%8; const short ib = 8*sx + sy; *(sa + 64*ib + 8*ly + lx) = loop_k + 16*il + i < args.ne00 ? *((device T0 *) x + i) : 0; } } else { S0_4x4 temp_a; dequantize_func(x, il, temp_a); threadgroup_barrier(mem_flags::mem_threadgroup); FOR_UNROLL (short i = 0; i < 16; i++) { const short sx = 2*il0 + i/8; const short sy = (tiitg/NL0)/8; const short lx = (tiitg/NL0)%8; const short ly = i%8; const short ib = 8*sx + sy; *(sa + 64*ib + 8*ly + lx) = temp_a[i/4][i%4]; } } if (FC_mul_mm_bc_inp) { for (short i = 0; i < 8; ++i) { const short sx = (tiitg%NL1); const short sy = (tiitg/NL1)/8; const short lx = i; const short ly = (tiitg/NL1)%8; const short ib = 4*sx + sy; *(sb + 64*ib + 8*ly + lx) = loop_k + iy + i < args.ne00 ? (S1) *((device T1 *) y + i) : 0; } } else { const short sx = (tiitg%NL1); const short sy = (tiitg/NL1)/8; const short ly = (tiitg/NL1)%8; const short ib = 4*sx + sy; *(threadgroup S1_2x4 *)(sb + 64*ib + 8*ly) = (S1_2x4)(*((device T1_2x4 *) y)); } il = (il + 2 < nl) ? il + 2 : il % 2; x = (il < 2) ? x + (2 + nl - 1)/nl : x; y += NK; threadgroup_barrier(mem_flags::mem_threadgroup); threadgroup const S0 * lsma = (sa + 4*64*(sgitg%2)); threadgroup const S1 * lsmb = (sb + 2*64*(sgitg/2)); FOR_UNROLL (short ik = 0; ik < NK/8; ik++) { simdgroup_barrier(mem_flags::mem_none); FOR_UNROLL (short i = 0; i < 4; i++) { simdgroup_load(ma[i], lsma + 64*i, 8, 0, false); } simdgroup_barrier(mem_flags::mem_none); FOR_UNROLL (short i = 0; i < 2; i++) { simdgroup_load(mb[i], lsmb + 64*i, 8, 0, false); } simdgroup_barrier(mem_flags::mem_none); FOR_UNROLL (short i = 0; i < 8; i++){ simdgroup_multiply_accumulate(mc[i], mb[i/4], ma[i%4], mc[i]); } lsma += 8*64; lsmb += 4*64; } } threadgroup_barrier(mem_flags::mem_threadgroup); threadgroup float * temp_str = ((threadgroup float *) shmem) + 32*(sgitg&1) + (16*(sgitg >> 1))*NR0; for (short i = 0; i < 8; i++) { simdgroup_store(mc[i], temp_str + 8*(i%4) + 8*NR0*(i/4), NR0, 0, false); } threadgroup_barrier(mem_flags::mem_threadgroup); for (short j = sgitg; j < nr1; j += 4) { const int idj = ids_i32[im*args.ne21 + r1 + j]; const short ide = idj % args.ne20; const short idt = idj / args.ne20; device float * D = (device float *) dst + r0 + ide*args.ne0 + idt*args.ne1*args.ne0; device float4 * D4 = (device float4 *) D; threadgroup float * C = (threadgroup float *) shmem + j*NR0; threadgroup float4 * C4 = (threadgroup float4 *) C; int i = tiisg; for (; i < nr0/4; i += 32) { *(D4 + i) = *(C4 + i); } i = (4*(nr0/4)) + tiisg; for (; i < nr0; i += 32) { *(D + i) = *(C + i); } } } // Address-table variant used by SSD streaming. The routing ids remain the // model's original expert ids, but each expert's resident buffer is found via a // GPU-address table instead of a contiguous full-layer tensor. template kernel void kernel_mul_mm_id_addr( constant ds4_metal_args_mul_mm_id & args, device const uint64_t * src0_addrs, device const char * src1, device const char * htpe, device const char * hids, device char * dst, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { constexpr int NR0 = 64; static_assert(NR1 == 32, "kernel_mul_mm_id_addr accumulator layout supports only 32 routed rows"); constexpr int NK = 32; constexpr int NL0 = NK/16; constexpr int NL1 = NK/8; constexpr int SA_BYTES = NR0 * NR1 * (int)sizeof(S0); threadgroup S0 * sa = (threadgroup S0 *)(shmem); threadgroup S1 * sb = (threadgroup S1 *)(shmem + SA_BYTES); const int im = tgpig.z; const int r0 = tgpig.y*NR0; const int r1 = tgpig.x*NR1; device const uint32_t * tpe_u32 = (device const uint32_t *) (htpe); device const int32_t * ids_i32 = (device const int32_t *) (hids); const int32_t neh1 = tpe_u32[im]; if (r1 >= neh1) { return; } const uint64_t base_addr = src0_addrs[im]; if (base_addr == 0) { return; } device const char * src0 = reinterpret_cast(base_addr); const short nr0 = (args.ne0 - r0 < NR0) ? (args.ne0 - r0) : NR0; const short nr1 = ( neh1 - r1 < NR1) ? ( neh1 - r1) : NR1; const short lr0 = ((short)tiitg/NL0) < nr0 ? ((short)tiitg/NL0) : nr0 - 1; const short lr1 = ((short)tiitg/NL1) < nr1 ? ((short)tiitg/NL1) : nr1 - 1; const short il0 = (tiitg % NL0); short il = il0; const int id = ids_i32[im*args.ne21 + r1 + lr1]; const short i11 = (id % args.ne20) % args.ne11; const short i12 = (id / args.ne20); const short i13 = 0; const short offset1 = il0/nl; device const block_q * x = (device const block_q *)(src0 + args.nb01*(r0 + lr0)) + offset1; const short iy = 8*(tiitg % NL1); device const T1 * y = (device const T1 *)(src1 + args.nb13*i13 + args.nb12*i12 + args.nb11*i11 + args.nb10*iy); S0_8x8 ma[4]; S1_8x8 mb[2]; simdgroup_float8x8 mc[8]; for (short i = 0; i < 8; i++){ mc[i] = make_filled_simdgroup_matrix(0.f); } for (int loop_k = 0; loop_k < args.ne00; loop_k += NK) { if (is_same::value && FC_mul_mm_bc_inp) { threadgroup_barrier(mem_flags::mem_threadgroup); for (short i = 0; i < 16; i++) { const short sx = 2*il0 + i/8; const short sy = (tiitg/NL0)/8; const short lx = (tiitg/NL0)%8; const short ly = i%8; const short ib = 8*sx + sy; *(sa + 64*ib + 8*ly + lx) = loop_k + 16*il + i < args.ne00 ? *((device T0 *) x + i) : 0; } } else { S0_4x4 temp_a; dequantize_func(x, il, temp_a); threadgroup_barrier(mem_flags::mem_threadgroup); FOR_UNROLL (short i = 0; i < 16; i++) { const short sx = 2*il0 + i/8; const short sy = (tiitg/NL0)/8; const short lx = (tiitg/NL0)%8; const short ly = i%8; const short ib = 8*sx + sy; *(sa + 64*ib + 8*ly + lx) = temp_a[i/4][i%4]; } } if (FC_mul_mm_bc_inp) { for (short i = 0; i < 8; ++i) { const short sx = (tiitg%NL1); const short sy = (tiitg/NL1)/8; const short lx = i; const short ly = (tiitg/NL1)%8; const short ib = 4*sx + sy; *(sb + 64*ib + 8*ly + lx) = loop_k + iy + i < args.ne00 ? (S1) *((device T1 *) y + i) : 0; } } else { const short sx = (tiitg%NL1); const short sy = (tiitg/NL1)/8; const short ly = (tiitg/NL1)%8; const short ib = 4*sx + sy; *(threadgroup S1_2x4 *)(sb + 64*ib + 8*ly) = (S1_2x4)(*((device T1_2x4 *) y)); } il = (il + 2 < nl) ? il + 2 : il % 2; x = (il < 2) ? x + (2 + nl - 1)/nl : x; y += NK; threadgroup_barrier(mem_flags::mem_threadgroup); threadgroup const S0 * lsma = (sa + 4*64*(sgitg%2)); threadgroup const S1 * lsmb = (sb + 2*64*(sgitg/2)); FOR_UNROLL (short ik = 0; ik < NK/8; ik++) { simdgroup_barrier(mem_flags::mem_none); FOR_UNROLL (short i = 0; i < 4; i++) { simdgroup_load(ma[i], lsma + 64*i, 8, 0, false); } simdgroup_barrier(mem_flags::mem_none); FOR_UNROLL (short i = 0; i < 2; i++) { simdgroup_load(mb[i], lsmb + 64*i, 8, 0, false); } simdgroup_barrier(mem_flags::mem_none); FOR_UNROLL (short i = 0; i < 8; i++){ simdgroup_multiply_accumulate(mc[i], mb[i/4], ma[i%4], mc[i]); } lsma += 8*64; lsmb += 4*64; } } threadgroup_barrier(mem_flags::mem_threadgroup); threadgroup float * temp_str = ((threadgroup float *) shmem) + 32*(sgitg&1) + (16*(sgitg >> 1))*NR0; for (short i = 0; i < 8; i++) { simdgroup_store(mc[i], temp_str + 8*(i%4) + 8*NR0*(i/4), NR0, 0, false); } threadgroup_barrier(mem_flags::mem_threadgroup); for (short j = sgitg; j < nr1; j += 4) { const int idj = ids_i32[im*args.ne21 + r1 + j]; const short ide = idj % args.ne20; const short idt = idj / args.ne20; device float * D = (device float *) dst + r0 + ide*args.ne0 + idt*args.ne1*args.ne0; device float4 * D4 = (device float4 *) D; threadgroup float * C = (threadgroup float *) shmem + j*NR0; threadgroup float4 * C4 = (threadgroup float4 *) C; int i = tiisg; for (; i < nr0/4; i += 32) { *(D4 + i) = *(C4 + i); } i = (4*(nr0/4)) + tiisg; for (; i < nr0; i += 32) { *(D + i) = *(C + i); } } } // Fused routed gate+up grouped matmul with the SwiGLU epilogue, generic over // the expert quant block. Both A streams share one staged B tile per k-step; // each output keeps the exact MMA accumulation order of the separate GEMMs, // and the epilogue matches kernel_dsv4_moe_swiglu_weight_f16, so the fused // result is bit-identical to the unfused path. template kernel void kernel_mul_mm_id_pair_swiglu_f16_impl( constant ds4_metal_args_mul_mm_id & args, constant ds4_metal_dsv4_moe_swiglu_weight_args & act, device const char * src0_gate, device const char * src0_up, device const char * src1, device const char * htpe, device const char * hids, device char * dst_mid, device const char * weights, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig[[threadgroup_position_in_grid]], ushort tiitg[[thread_index_in_threadgroup]], ushort tiisg[[thread_index_in_simdgroup]], ushort sgitg[[simdgroup_index_in_threadgroup]]) { threadgroup half *sa_gate = (threadgroup half *)(shmem); threadgroup half *sa_up = (threadgroup half *)(shmem + 4096); threadgroup half *sb = (threadgroup half *)(shmem + 8192); constexpr int NR0 = 64; constexpr int NR1 = 32; constexpr int NK = 32; constexpr int NL0 = NK/16; constexpr int NL1 = NK/8; const int im = tgpig.z; const int r0 = tgpig.y*NR0; const int r1 = tgpig.x*NR1; device const uint32_t * tpe_u32 = (device const uint32_t *) (htpe); device const int32_t * ids_i32 = (device const int32_t *) (hids); const int32_t neh1 = tpe_u32[im]; if (r1 >= neh1) { return; } const short nr0 = (args.ne0 - r0 < NR0) ? (args.ne0 - r0) : NR0; const short nr1 = ( neh1 - r1 < NR1) ? ( neh1 - r1) : NR1; const short lr0 = ((short)tiitg/NL0) < nr0 ? ((short)tiitg/NL0) : nr0 - 1; const short lr1 = ((short)tiitg/NL1) < nr1 ? ((short)tiitg/NL1) : nr1 - 1; const short il0 = (tiitg % NL0); short il = il0; const int id = ids_i32[im*args.ne21 + r1 + lr1]; const short i11 = (id % args.ne20) % args.ne11; const short i12 = (id / args.ne20); const short i13 = 0; const uint64_t offset0 = im*args.nb02 + i13*args.nb03; const short offset1 = il0/nl; device const block_q * xg = (device const block_q *)(src0_gate + args.nb01*(r0 + lr0) + offset0) + offset1; device const block_q * xu = (device const block_q *)(src0_up + args.nb01*(r0 + lr0) + offset0) + offset1; const short iy = 8*(tiitg % NL1); device const float * y = (device const float *)(src1 + args.nb13*i13 + args.nb12*i12 + args.nb11*i11 + args.nb10*iy); simdgroup_half8x8 ma_g[4]; simdgroup_half8x8 ma_u[4]; simdgroup_half8x8 mb[2]; simdgroup_float8x8 mc_gate[8]; simdgroup_float8x8 mc_up[8]; for (short i = 0; i < 8; i++) { mc_gate[i] = make_filled_simdgroup_matrix(0.f); mc_up[i] = make_filled_simdgroup_matrix(0.f); } for (int loop_k = 0; loop_k < args.ne00; loop_k += NK) { half4x4 temp_gate; dequantize_func(xg, il, temp_gate); half4x4 temp_up; dequantize_func(xu, il, temp_up); threadgroup_barrier(mem_flags::mem_threadgroup); // Stage the shared B tile and both expert A tiles in one pass: two // threadgroup barriers per k-step total instead of two per matrix. { const short sx_b = (tiitg%NL1); const short sy_b = (tiitg/NL1)/8; const short ly_b = (tiitg/NL1)%8; const short ib_b = 4*sx_b + sy_b; *(threadgroup half2x4 *)(sb + 64*ib_b + 8*ly_b) = (half2x4)(*((device float2x4 *) y)); } FOR_UNROLL (short i = 0; i < 16; i++) { const short sx = 2*il0 + i/8; const short sy = (tiitg/NL0)/8; const short lx = (tiitg/NL0)%8; const short ly = i%8; const short ib = 8*sx + sy; *(sa_gate + 64*ib + 8*ly + lx) = temp_gate[i/4][i%4]; *(sa_up + 64*ib + 8*ly + lx) = temp_up[i/4][i%4]; } threadgroup_barrier(mem_flags::mem_threadgroup); threadgroup const half * lsma_gate = (sa_gate + 4*64*(sgitg%2)); threadgroup const half * lsma_up = (sa_up + 4*64*(sgitg%2)); threadgroup const half * lsmb = (sb + 2*64*(sgitg/2)); FOR_UNROLL (short ik = 0; ik < NK/8; ik++) { simdgroup_barrier(mem_flags::mem_none); FOR_UNROLL (short i = 0; i < 4; i++) { simdgroup_load(ma_g[i], lsma_gate + 64*i, 8, 0, false); simdgroup_load(ma_u[i], lsma_up + 64*i, 8, 0, false); } FOR_UNROLL (short i = 0; i < 2; i++) { simdgroup_load(mb[i], lsmb + 64*i, 8, 0, false); } simdgroup_barrier(mem_flags::mem_none); FOR_UNROLL (short i = 0; i < 8; i++) { simdgroup_multiply_accumulate(mc_gate[i], mb[i/4], ma_g[i%4], mc_gate[i]); simdgroup_multiply_accumulate(mc_up[i], mb[i/4], ma_u[i%4], mc_up[i]); } lsma_gate += 8*64; lsma_up += 8*64; lsmb += 4*64; } il = (il + 2 < nl) ? il + 2 : il % 2; xg = (il < 2) ? xg + (2 + nl - 1)/nl : xg; xu = (il < 2) ? xu + (2 + nl - 1)/nl : xu; y += NK; } threadgroup_barrier(mem_flags::mem_threadgroup); threadgroup float * temp_gate = (threadgroup float *) shmem; threadgroup float * temp_up = temp_gate + NR0*NR1; threadgroup float * temp_gate_str = temp_gate + 32*(sgitg&1) + (16*(sgitg >> 1))*NR0; threadgroup float * temp_up_str = temp_up + 32*(sgitg&1) + (16*(sgitg >> 1))*NR0; for (short i = 0; i < 8; i++) { simdgroup_store(mc_gate[i], temp_gate_str + 8*(i%4) + 8*NR0*(i/4), NR0, 0, false); simdgroup_store(mc_up[i], temp_up_str + 8*(i%4) + 8*NR0*(i/4), NR0, 0, false); } threadgroup_barrier(mem_flags::mem_threadgroup); const float c = act.clamp_value; for (short j = sgitg; j < nr1; j += 4) { const int idj = ids_i32[im*args.ne21 + r1 + j]; const short ide = idj % args.ne20; const short idt = idj / args.ne20; device half *D = (device half *)(dst_mid + ((uint64_t)idt*args.ne1 + (uint64_t)ide)*act.mid_row_stride) + r0; device const float *w = (device const float *)(weights + (uint64_t)idj*act.weight_stride); const float route_weight = w[0]; threadgroup float *Cg = temp_gate + j*NR0; threadgroup float *Cu = temp_up + j*NR0; int i = tiisg; for (; i < nr0; i += 32) { float g = Cg[i]; float u = Cu[i]; if (c > 1.0e-6f) { g = min(g, c); u = clamp(u, -c, c); } const float silu = g / (1.0f + exp(-g)); D[i] = (half)(silu * u * route_weight); } } } typedef decltype(kernel_mul_mm_id_pair_swiglu_f16_impl) mul_mm_id_pair_swiglu_f16_iq2; typedef decltype(kernel_mul_mm_id_pair_swiglu_f16_impl) mul_mm_id_pair_swiglu_f16_q4; typedef decltype(kernel_mul_mm_id_pair_swiglu_f16_impl) mul_mm_id_pair_swiglu_f16_q5; // Host-visible fused routed pair matmuls for the DS4 expert quant formats. template [[host_name("kernel_mul_mm_id_iq2_xxs_pair_swiglu_f16")]] kernel mul_mm_id_pair_swiglu_f16_iq2 kernel_mul_mm_id_pair_swiglu_f16_impl; template [[host_name("kernel_mul_mm_id_q4_K_pair_swiglu_f16")]] kernel mul_mm_id_pair_swiglu_f16_q4 kernel_mul_mm_id_pair_swiglu_f16_impl; template [[host_name("kernel_mul_mm_id_q5_K_pair_swiglu_f16")]] kernel mul_mm_id_pair_swiglu_f16_q5 kernel_mul_mm_id_pair_swiglu_f16_impl; typedef decltype(kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q2_K, QK_NL, dequantize_q2_K, float, float4x4, float, float2x4>) mul_mm_id; typedef decltype(kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q2_K, QK_NL, dequantize_q2_K, half, half4x4, half, half2x4>) mul_mm_id_f16_rhs; typedef decltype(kernel_mul_mm_id<32, float, float4x4, simdgroup_float8x8, float, float2x4, simdgroup_float8x8, block_q2_K, QK_NL, dequantize_q2_K, float, float4x4, float, float2x4>) mul_mm_id_ff32; typedef decltype(kernel_mul_mm_id_addr<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q2_K, QK_NL, dequantize_q2_K, float, float4x4, float, float2x4>) mul_mm_id_addr; typedef decltype(kernel_mul_mm_id_addr<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q2_K, QK_NL, dequantize_q2_K, half, half4x4, half, half2x4>) mul_mm_id_addr_f16_rhs; // Host-visible batched MoE matmul variants for the DS4 quant formats. template [[host_name("kernel_mul_mm_id_q8_0_f32")]] kernel mul_mm_id kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q8_0, 2, dequantize_q8_0, float, float4x4, float, float2x4>; template [[host_name("kernel_mul_mm_id_q2_K_f32")]] kernel mul_mm_id kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q2_K, QK_NL, dequantize_q2_K, float, float4x4, float, float2x4>; template [[host_name("kernel_mul_mm_id_q4_K_f32")]] kernel mul_mm_id kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q4_K, QK_NL, dequantize_q4_K, float, float4x4, float, float2x4>; template [[host_name("kernel_mul_mm_id_q5_K_f32")]] kernel mul_mm_id kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q5_K, QK_NL, dequantize_q5_K, float, float4x4, float, float2x4>; template [[host_name("kernel_mul_mm_id_q6_K_f32")]] kernel mul_mm_id kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q6_K, QK_NL, dequantize_q6_K, float, float4x4, float, float2x4>; template [[host_name("kernel_mul_mm_id_iq2_xxs_f32")]] kernel mul_mm_id kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq2_xxs, QK_NL, dequantize_iq2_xxs, float, float4x4, float, float2x4>; template [[host_name("kernel_mul_mm_id_q8_0_f16")]] kernel mul_mm_id_f16_rhs kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q8_0, 2, dequantize_q8_0, half, half4x4, half, half2x4>; template [[host_name("kernel_mul_mm_id_q2_K_f16")]] kernel mul_mm_id_f16_rhs kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q2_K, QK_NL, dequantize_q2_K, half, half4x4, half, half2x4>; template [[host_name("kernel_mul_mm_id_q4_K_f16")]] kernel mul_mm_id_f16_rhs kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q4_K, QK_NL, dequantize_q4_K, half, half4x4, half, half2x4>; template [[host_name("kernel_mul_mm_id_q5_K_f16")]] kernel mul_mm_id_f16_rhs kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q5_K, QK_NL, dequantize_q5_K, half, half4x4, half, half2x4>; template [[host_name("kernel_mul_mm_id_q6_K_f16")]] kernel mul_mm_id_f16_rhs kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q6_K, QK_NL, dequantize_q6_K, half, half4x4, half, half2x4>; template [[host_name("kernel_mul_mm_id_iq2_xxs_f16")]] kernel mul_mm_id_f16_rhs kernel_mul_mm_id<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq2_xxs, QK_NL, dequantize_iq2_xxs, half, half4x4, half, half2x4>; template [[host_name("kernel_mul_mm_id_q4_K_ff32")]] kernel mul_mm_id_ff32 kernel_mul_mm_id<32, float, float4x4, simdgroup_float8x8, float, float2x4, simdgroup_float8x8, block_q4_K, QK_NL, dequantize_q4_K, float, float4x4, float, float2x4>; template [[host_name("kernel_mul_mm_id_q5_K_ff32")]] kernel mul_mm_id_ff32 kernel_mul_mm_id<32, float, float4x4, simdgroup_float8x8, float, float2x4, simdgroup_float8x8, block_q5_K, QK_NL, dequantize_q5_K, float, float4x4, float, float2x4>; template [[host_name("kernel_mul_mm_id_q6_K_ff32")]] kernel mul_mm_id_ff32 kernel_mul_mm_id<32, float, float4x4, simdgroup_float8x8, float, float2x4, simdgroup_float8x8, block_q6_K, QK_NL, dequantize_q6_K, float, float4x4, float, float2x4>; template [[host_name("kernel_mul_mm_id_addr_q2_K_f32")]] kernel mul_mm_id_addr kernel_mul_mm_id_addr<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q2_K, QK_NL, dequantize_q2_K, float, float4x4, float, float2x4>; template [[host_name("kernel_mul_mm_id_addr_q4_K_f32")]] kernel mul_mm_id_addr kernel_mul_mm_id_addr<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q4_K, QK_NL, dequantize_q4_K, float, float4x4, float, float2x4>; template [[host_name("kernel_mul_mm_id_addr_q2_K_f16")]] kernel mul_mm_id_addr_f16_rhs kernel_mul_mm_id_addr<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q2_K, QK_NL, dequantize_q2_K, half, half4x4, half, half2x4>; template [[host_name("kernel_mul_mm_id_addr_q4_K_f16")]] kernel mul_mm_id_addr_f16_rhs kernel_mul_mm_id_addr<32, half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q4_K, QK_NL, dequantize_q4_K, half, half4x4, half, half2x4>; #ifdef DS4_METAL_HAS_TENSOR // Attention-output low-rank projection retained for Metal4 prefill. It uses // the same direct-RHS idea as dense matmul: dequantize the Q8_0 low projection // weights to a half tile, then let TensorOps read the dense head activations // directly. Only the 64-token direct-RHS instantiation is exported because the // staged-RHS and 32-token variants were benchmark-only experiments. // // Full tiles (the host dispatch guarantee for aligned batches) skip all bounds // work. The weight tile is double-buffered: the next k-step's dequant // overlaps the current cooperative matmul, so the k-loop needs one // threadgroup barrier per step instead of two. template kernel void kernel_attn_out_low_q8_0_mpp_direct_rhs( constant ds4_metal_args_mul_mm_id & args, device const char * srcA, device const char * srcB, device char * dst, threadgroup char * shmem [[threadgroup(0)]], uint3 tgpig [[threadgroup_position_in_grid]], ushort tiitg [[thread_index_in_threadgroup]], ushort sgitg [[simdgroup_index_in_threadgroup]]) { (void) sgitg; constexpr int NR0 = 64; constexpr int NK = 32; constexpr int NL = NK/16; constexpr int NUM_THREADS = 128; const int K = args.ne00; const int M = args.ne0; const int N = args.ne21; const int G = args.ne1; const int group = tgpig.z; const int r0 = tgpig.y*NR0; const int r1 = tgpig.x*NR1; const bool full_tile = r0 + NR0 <= M && r1 + NR1 <= N && (K % NK) == 0; threadgroup half *sa = (threadgroup half *)shmem; auto tA0 = tensor(sa, dextents(NK, NR0)); auto tA1 = tensor(sa + NR0*NK, dextents(NK, NR0)); device float *ptrB = (device float *)(srcB + args.nb11*group); const int strideB = args.nb12/sizeof(float); auto tB = tensor(ptrB, dextents(K, N), array({1, strideB})); matmul2d< matmul2d_descriptor(NR1, NR0, NK, false, true, true, matmul2d_descriptor::mode::multiply_accumulate), execution_simdgroups<4>> mm; auto cT = mm.template get_destination_cooperative_tensor(); #pragma unroll for (uint16_t i = 0; i < cT.get_capacity(); ++i) { if (cT.is_valid_element(i)) { cT[i] = 0.0f; } } auto stage_tile = [&](const int loop_k, threadgroup half *buf) { for (int work = tiitg; work < NR0*NL; work += NUM_THREADS) { const int row = work/NL; const int k_chunk = work%NL; const int k_pos = loop_k + k_chunk*16; const short k_base = k_chunk*16; if (full_tile) { device const block_q8_0 *row_ptr = (device const block_q8_0 *)(srcA + args.nb01*(r0 + row) + group*args.nb02); half4x4 temp_a; dequantize_q8_0_pairs(row_ptr + k_pos/32, (k_pos/16)%2, temp_a); threadgroup half4 *dst4 = (threadgroup half4 *)(buf + row*NK + k_base); dst4[0] = temp_a[0]; dst4[1] = temp_a[1]; dst4[2] = temp_a[2]; dst4[3] = temp_a[3]; } else if (r0 + row < M) { const int block_idx = k_pos/32; const short il = (k_pos/16)%2; device const block_q8_0 *row_ptr = (device const block_q8_0 *)(srcA + args.nb01*(r0 + row) + group*args.nb02); half4x4 temp_a; dequantize_q8_0_pairs(row_ptr + block_idx, il, temp_a); FOR_UNROLL (short i = 0; i < 16; i++) { buf[row*NK + k_base + i] = (k_pos + i < K) ? temp_a[i/4][i%4] : (half)0; } } else { FOR_UNROLL (short i = 0; i < 16; i++) { buf[row*NK + k_base + i] = (half)0; } } } }; stage_tile(0, sa); threadgroup_barrier(mem_flags::mem_threadgroup); uint buf_sel = 0; for (int loop_k = 0; loop_k < K; loop_k += NK) { auto mA = (buf_sel ? tA1 : tA0).slice(0, 0); auto mB = tB.slice(loop_k, r1); mm.run(mB, mA, cT); const int next_k = loop_k + NK; if (next_k < K) { buf_sel ^= 1u; stage_tile(next_k, buf_sel ? sa + NR0*NK : sa); } threadgroup_barrier(mem_flags::mem_threadgroup); } device float *dst_group = (device float *)dst + group*M; if (full_tile) { device float *dst_tile = dst_group + r0 + (uint64_t)r1*G*M; auto tD = tensor(dst_tile, dextents(NR0, NR1), array({1, G*M})); cT.store(tD); } else { auto tD = tensor(dst_group, dextents(M, N), array({1, G*M})); auto mD = tD.slice(r0, r1); cT.store(mD); } } typedef decltype(kernel_attn_out_low_q8_0_mpp_direct_rhs<64>) attn_out_low_q8_0_mpp_direct_rhs_n64_t; template [[host_name("kernel_attn_out_low_q8_0_mpp_direct_rhs_n64")]] kernel attn_out_low_q8_0_mpp_direct_rhs_n64_t kernel_attn_out_low_q8_0_mpp_direct_rhs<64>; #endif #undef QK_NL #undef kmask_iq2xs #undef ksigns_iq2xs #undef iq2xxs_grid #undef QK_K #undef N_R0_Q2_K #undef N_R0_Q4_K #undef N_R0_Q8_K #undef N_R0_GLM_Q4_PAIR2_K #undef N_R0_GLM_Q4_PAIR_K #undef N_R0_Q5_PAIR_K #undef N_R0_Q5_K #undef N_R0_Q6_K #undef N_R0_IQ2_XXS