char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

hrushitfujitsuggml : add sve tuned code for gemm_q8_0_4x8_q8_0() kernel (#21916)bdc9c743a

char/aliceai-t5-moe
249.6 KiB5156 linesraw
1#define GGML_COMMON_IMPL_CPP
2#define GGML_COMMON_DECL_CPP
3#include "ggml-common.h"
4#include "ggml-backend-impl.h"
5
6#include "ggml-impl.h"
7#include "ggml-cpu.h"
8#include "ggml-cpu-impl.h"
9#include "simd-mappings.h"
10#include "traits.h"
11
12#include <cmath>
13#include <cstring>
14#include <cassert>
15#include <cstdlib> // for qsort
16#include <cstdio>  // for GGML_ASSERT
17
18#define GGML_CPU_CLANG_WORKAROUND
19#include "../../repack.h"
20
21#if defined(__GNUC__)
22#pragma GCC diagnostic ignored "-Woverlength-strings"
23#endif
24
25#define UNUSED GGML_UNUSED
26
27#if defined(__aarch64__) && defined(__ARM_NEON) && (defined(__ARM_FEATURE_MATMUL_INT8) || defined(__ARM_FEATURE_DOTPROD))
28// Helper for decoding scales and mins of Q4_K and Q5_K block formats
29static inline void decode_q_Kx8_6bit_scales(const uint8_t * scales_in, int16x8_t * out_mins, int8_t * out_scales) {
30    constexpr uint32_t kmask1 = 0x3f3f3f3f;
31    constexpr uint32_t kmask2 = 0x0f0f0f0f;
32    constexpr uint32_t kmask3 = 0x03030303;
33    constexpr uint8_t  scales_size = 12;
34
35    uint32_t sm[3];
36    memcpy(sm, scales_in, scales_size);
37
38    const uint32_t   mins_0_3 = sm[1] & kmask1;
39    const uint32_t   mins_4_7 = ((sm[2] >> 4) & kmask2) | (((sm[1] >> 6) & kmask3) << 4);
40    const uint32x2_t mins_u32 = { mins_0_3, mins_4_7 };
41
42    *out_mins = vreinterpretq_s16_u16(vmovl_u8(vreinterpret_u8_u32(mins_u32)));
43
44    uint32_t scales_u32[2];
45    scales_u32[0] = sm[0] & kmask1;
46    scales_u32[1] = (sm[2] & kmask2) | (((sm[0] >> 6) & kmask3) << 4);
47    memcpy(out_scales, scales_u32, 8);
48}
49#endif
50
51void ggml_quantize_mat_q8_0_4x4(const float * GGML_RESTRICT x, void * GGML_RESTRICT vy, int64_t k) {
52    assert(QK8_0 == 32);
53    assert(k % QK8_0 == 0);
54    const int nb = k / QK8_0;
55
56    block_q8_0x4 * GGML_RESTRICT y = (block_q8_0x4 *) vy;
57
58#if defined(__ARM_NEON)
59    float32x4_t srcv[4][8];
60    float id[4];
61
62    for (int i = 0; i < nb; i++) {
63        float32x4_t asrcv[8];
64        float32x4_t amaxv[8];
65
66        for (int row_iter = 0; row_iter < 4; row_iter++) {
67            for (int j = 0; j < 8; j++) srcv[row_iter][j] = vld1q_f32(x + row_iter * k + i * 32 + 4 * j);
68            for (int j = 0; j < 8; j++) asrcv[j] = vabsq_f32(srcv[row_iter][j]);
69
70            for (int j = 0; j < 4; j++) amaxv[2 * j] = vmaxq_f32(asrcv[2 * j], asrcv[2 * j + 1]);
71            for (int j = 0; j < 2; j++) amaxv[4 * j] = vmaxq_f32(amaxv[4 * j], amaxv[4 * j + 2]);
72            for (int j = 0; j < 1; j++) amaxv[8 * j] = vmaxq_f32(amaxv[8 * j], amaxv[8 * j + 4]);
73
74            const float amax = vmaxvq_f32(amaxv[0]);
75
76            const float d = amax / ((1 << 7) - 1);
77            id[row_iter] = d ? 1.0f / d : 0.0f;
78
79            y[i].d[row_iter] = GGML_CPU_FP32_TO_FP16(d);
80        }
81
82        for (int j = 0; j < 8; j++) {
83            float32x4_t v = vmulq_n_f32(srcv[0][j], id[0]);
84            int32x4_t vi = vcvtnq_s32_f32(v);
85            y[i].qs[16 * j + 0] = vgetq_lane_s32(vi, 0);
86            y[i].qs[16 * j + 1] = vgetq_lane_s32(vi, 1);
87            y[i].qs[16 * j + 2] = vgetq_lane_s32(vi, 2);
88            y[i].qs[16 * j + 3] = vgetq_lane_s32(vi, 3);
89
90            v = vmulq_n_f32(srcv[1][j], id[1]);
91            vi = vcvtnq_s32_f32(v);
92            y[i].qs[16 * j + 4] = vgetq_lane_s32(vi, 0);
93            y[i].qs[16 * j + 5] = vgetq_lane_s32(vi, 1);
94            y[i].qs[16 * j + 6] = vgetq_lane_s32(vi, 2);
95            y[i].qs[16 * j + 7] = vgetq_lane_s32(vi, 3);
96
97            v = vmulq_n_f32(srcv[2][j], id[2]);
98            vi = vcvtnq_s32_f32(v);
99            y[i].qs[16 * j + 8] = vgetq_lane_s32(vi, 0);
100            y[i].qs[16 * j + 9] = vgetq_lane_s32(vi, 1);
101            y[i].qs[16 * j + 10] = vgetq_lane_s32(vi, 2);
102            y[i].qs[16 * j + 11] = vgetq_lane_s32(vi, 3);
103
104            v = vmulq_n_f32(srcv[3][j], id[3]);
105            vi = vcvtnq_s32_f32(v);
106            y[i].qs[16 * j + 12] = vgetq_lane_s32(vi, 0);
107            y[i].qs[16 * j + 13] = vgetq_lane_s32(vi, 1);
108            y[i].qs[16 * j + 14] = vgetq_lane_s32(vi, 2);
109            y[i].qs[16 * j + 15] = vgetq_lane_s32(vi, 3);
110        }
111    }
112#else
113    UNUSED(nb);
114    UNUSED(y);
115    ggml_quantize_mat_q8_0_4x4_generic(x, vy, k);
116#endif
117}
118
119void ggml_quantize_mat_q8_0_4x8(const float * GGML_RESTRICT x, void * GGML_RESTRICT vy, int64_t k) {
120    assert(QK8_0 == 32);
121    assert(k % QK8_0 == 0);
122    const int nb = k / QK8_0;
123
124    block_q8_0x4 * GGML_RESTRICT y = (block_q8_0x4 *) vy;
125
126#if defined(__ARM_NEON)
127    float32x4_t srcv[4][8];
128    float id[4];
129
130    for (int i = 0; i < nb; i++) {
131        float32x4_t asrcv[8];
132        float32x4_t amaxv[8];
133
134        for (int row_iter = 0; row_iter < 4; row_iter++) {
135            for (int j = 0; j < 8; j++) srcv[row_iter][j] = vld1q_f32(x + row_iter * k + i * 32 + 4 * j);
136            for (int j = 0; j < 8; j++) asrcv[j] = vabsq_f32(srcv[row_iter][j]);
137
138            for (int j = 0; j < 4; j++) amaxv[2 * j] = vmaxq_f32(asrcv[2 * j], asrcv[2 * j + 1]);
139            for (int j = 0; j < 2; j++) amaxv[4 * j] = vmaxq_f32(amaxv[4 * j], amaxv[4 * j + 2]);
140            for (int j = 0; j < 1; j++) amaxv[8 * j] = vmaxq_f32(amaxv[8 * j], amaxv[8 * j + 4]);
141
142            const float amax = vmaxvq_f32(amaxv[0]);
143
144            const float d = amax / ((1 << 7) - 1);
145            id[row_iter] = d ? 1.0f / d : 0.0f;
146
147            y[i].d[row_iter] = GGML_CPU_FP32_TO_FP16(d);
148        }
149
150        for (int j = 0; j < 4; j++) {
151            float32x4_t v = vmulq_n_f32(srcv[0][2 * j], id[0]);
152            int32x4_t vi = vcvtnq_s32_f32(v);
153            y[i].qs[32 * j + 0] = vgetq_lane_s32(vi, 0);
154            y[i].qs[32 * j + 1] = vgetq_lane_s32(vi, 1);
155            y[i].qs[32 * j + 2] = vgetq_lane_s32(vi, 2);
156            y[i].qs[32 * j + 3] = vgetq_lane_s32(vi, 3);
157            v = vmulq_n_f32(srcv[0][2 * j + 1], id[0]);
158            vi = vcvtnq_s32_f32(v);
159            y[i].qs[32 * j + 4] = vgetq_lane_s32(vi, 0);
160            y[i].qs[32 * j + 5] = vgetq_lane_s32(vi, 1);
161            y[i].qs[32 * j + 6] = vgetq_lane_s32(vi, 2);
162            y[i].qs[32 * j + 7] = vgetq_lane_s32(vi, 3);
163
164            v = vmulq_n_f32(srcv[1][2 * j], id[1]);
165            vi = vcvtnq_s32_f32(v);
166            y[i].qs[32 * j + 8] = vgetq_lane_s32(vi, 0);
167            y[i].qs[32 * j + 9] = vgetq_lane_s32(vi, 1);
168            y[i].qs[32 * j + 10] = vgetq_lane_s32(vi, 2);
169            y[i].qs[32 * j + 11] = vgetq_lane_s32(vi, 3);
170            v = vmulq_n_f32(srcv[1][2 * j + 1], id[1]);
171            vi = vcvtnq_s32_f32(v);
172            y[i].qs[32 * j + 12] = vgetq_lane_s32(vi, 0);
173            y[i].qs[32 * j + 13] = vgetq_lane_s32(vi, 1);
174            y[i].qs[32 * j + 14] = vgetq_lane_s32(vi, 2);
175            y[i].qs[32 * j + 15] = vgetq_lane_s32(vi, 3);
176
177            v = vmulq_n_f32(srcv[2][2 * j], id[2]);
178            vi = vcvtnq_s32_f32(v);
179            y[i].qs[32 * j + 16] = vgetq_lane_s32(vi, 0);
180            y[i].qs[32 * j + 17] = vgetq_lane_s32(vi, 1);
181            y[i].qs[32 * j + 18] = vgetq_lane_s32(vi, 2);
182            y[i].qs[32 * j + 19] = vgetq_lane_s32(vi, 3);
183            v = vmulq_n_f32(srcv[2][2 * j + 1], id[2]);
184            vi = vcvtnq_s32_f32(v);
185            y[i].qs[32 * j + 20] = vgetq_lane_s32(vi, 0);
186            y[i].qs[32 * j + 21] = vgetq_lane_s32(vi, 1);
187            y[i].qs[32 * j + 22] = vgetq_lane_s32(vi, 2);
188            y[i].qs[32 * j + 23] = vgetq_lane_s32(vi, 3);
189
190            v = vmulq_n_f32(srcv[3][2 * j], id[3]);
191            vi = vcvtnq_s32_f32(v);
192            y[i].qs[32 * j + 24] = vgetq_lane_s32(vi, 0);
193            y[i].qs[32 * j + 25] = vgetq_lane_s32(vi, 1);
194            y[i].qs[32 * j + 26] = vgetq_lane_s32(vi, 2);
195            y[i].qs[32 * j + 27] = vgetq_lane_s32(vi, 3);
196            v = vmulq_n_f32(srcv[3][2 * j + 1], id[3]);
197            vi = vcvtnq_s32_f32(v);
198            y[i].qs[32 * j + 28] = vgetq_lane_s32(vi, 0);
199            y[i].qs[32 * j + 29] = vgetq_lane_s32(vi, 1);
200            y[i].qs[32 * j + 30] = vgetq_lane_s32(vi, 2);
201            y[i].qs[32 * j + 31] = vgetq_lane_s32(vi, 3);
202        }
203    }
204
205#else
206    UNUSED(nb);
207    UNUSED(y);
208    ggml_quantize_mat_q8_0_4x8_generic(x, vy, k);
209#endif
210}
211
212void ggml_gemv_q4_0_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
213    const int qk = QK8_0;
214    const int nb = n / qk;
215    const int ncols_interleaved = 4;
216    const int blocklen = 4;
217
218    assert (n % qk == 0);
219    assert (nc % ncols_interleaved == 0);
220
221    UNUSED(s);
222    UNUSED(bs);
223    UNUSED(vx);
224    UNUSED(vy);
225    UNUSED(nr);
226    UNUSED(nc);
227    UNUSED(nb);
228    UNUSED(ncols_interleaved);
229    UNUSED(blocklen);
230
231#if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
232    const block_q4_0x4 * b_ptr = (const block_q4_0x4 *) vx;
233
234    for (int c = 0; c < nc; c += ncols_interleaved) {
235        const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
236        float32x4_t acc = vdupq_n_f32(0);
237        for (int b = 0; b < nb; b++) {
238            int8x16_t b0 = vld1q_s8((const int8_t *) b_ptr->qs);
239            int8x16_t b1 = vld1q_s8((const int8_t *) b_ptr->qs + 16);
240            int8x16_t b2 = vld1q_s8((const int8_t *) b_ptr->qs + 32);
241            int8x16_t b3 = vld1q_s8((const int8_t *) b_ptr->qs + 48);
242            float16x4_t bd = vld1_f16((const __fp16 *) b_ptr->d);
243
244            int8x16_t a0 = vld1q_s8(a_ptr->qs);
245            int8x16_t a1 = vld1q_s8(a_ptr->qs + qk/2);
246            float16x4_t ad = vld1_dup_f16((const __fp16 *) &a_ptr->d);
247
248            int32x4_t ret = vdupq_n_s32(0);
249
250            ret = vdotq_laneq_s32(ret, b0 << 4, a0, 0);
251            ret = vdotq_laneq_s32(ret, b1 << 4, a0, 1);
252            ret = vdotq_laneq_s32(ret, b2 << 4, a0, 2);
253            ret = vdotq_laneq_s32(ret, b3 << 4, a0, 3);
254
255            ret = vdotq_laneq_s32(ret, b0 & 0xf0U, a1, 0);
256            ret = vdotq_laneq_s32(ret, b1 & 0xf0U, a1, 1);
257            ret = vdotq_laneq_s32(ret, b2 & 0xf0U, a1, 2);
258            ret = vdotq_laneq_s32(ret, b3 & 0xf0U, a1, 3);
259
260            acc = vfmaq_f32(acc, vcvtq_n_f32_s32(ret, 4),
261                            vmulq_f32(vcvt_f32_f16(ad), vcvt_f32_f16(bd)));
262            a_ptr++;
263            b_ptr++;
264        }
265        vst1q_f32(s, acc);
266        s += ncols_interleaved;
267    }
268    return;
269#endif // #if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
270    ggml_gemv_q4_0_4x4_q8_0_generic(n, s, bs, vx, vy, nr, nc);
271}
272
273void ggml_gemv_q4_0_4x8_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
274    const int qk = QK8_0;
275    const int nb = n / qk;
276    const int ncols_interleaved = 4;
277    const int blocklen = 8;
278
279    assert (n % qk == 0);
280    assert (nc % ncols_interleaved == 0);
281
282    UNUSED(s);
283    UNUSED(bs);
284    UNUSED(vx);
285    UNUSED(vy);
286    UNUSED(nr);
287    UNUSED(nc);
288    UNUSED(nb);
289    UNUSED(ncols_interleaved);
290    UNUSED(blocklen);
291
292#if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
293    const block_q4_0x4 * b_ptr = (const block_q4_0x4 *) vx;
294
295    for (int c = 0; c < nc; c += ncols_interleaved) {
296        const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
297        float32x4_t acc = vdupq_n_f32(0);
298        for (int b = 0; b < nb; b++) {
299            int8x16_t b0 = vld1q_s8((const int8_t *) b_ptr->qs);
300            int8x16_t b1 = vld1q_s8((const int8_t *) b_ptr->qs + 16);
301            int8x16_t b2 = vld1q_s8((const int8_t *) b_ptr->qs + 32);
302            int8x16_t b3 = vld1q_s8((const int8_t *) b_ptr->qs + 48);
303            float16x4_t bd = vld1_f16((const __fp16 *) b_ptr->d);
304
305            int8x16_t a0 = (int8x16_t) vld1q_dup_s64((const int64_t *) a_ptr->qs);
306            int8x16_t a1 = (int8x16_t) vld1q_dup_s64((const int64_t *) a_ptr->qs + 1);
307            int8x16_t a2 = (int8x16_t) vld1q_dup_s64((const int64_t *) a_ptr->qs + 2);
308            int8x16_t a3 = (int8x16_t) vld1q_dup_s64((const int64_t *) a_ptr->qs + 3);
309            float16x4_t ad = vld1_dup_f16((const __fp16 *) &a_ptr->d);
310
311            int32x4_t ret0 = vdupq_n_s32(0);
312            int32x4_t ret1 = vdupq_n_s32(0);
313
314            ret0 = vdotq_s32(ret0, b0 << 4, a0);
315            ret1 = vdotq_s32(ret1, b1 << 4, a0);
316            ret0 = vdotq_s32(ret0, b2 << 4, a1);
317            ret1 = vdotq_s32(ret1, b3 << 4, a1);
318
319            ret0 = vdotq_s32(ret0, b0 & 0xf0U, a2);
320            ret1 = vdotq_s32(ret1, b1 & 0xf0U, a2);
321            ret0 = vdotq_s32(ret0, b2 & 0xf0U, a3);
322            ret1 = vdotq_s32(ret1, b3 & 0xf0U, a3);
323
324            int32x4_t ret = vpaddq_s32(ret0, ret1);
325
326            acc = vfmaq_f32(acc, vcvtq_n_f32_s32(ret, 4),
327                    vmulq_f32(vcvt_f32_f16(ad), vcvt_f32_f16(bd)));
328            a_ptr++;
329            b_ptr++;
330        }
331        vst1q_f32(s, acc);
332        s += ncols_interleaved;
333    }
334    return;
335#endif // #if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
336    ggml_gemv_q4_0_4x8_q8_0_generic(n, s, bs, vx, vy, nr, nc);
337}
338
339void ggml_gemv_q4_0_8x8_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
340    const int qk = QK8_0;
341    const int nb = n / qk;
342    const int ncols_interleaved = 8;
343    const int blocklen = 8;
344
345    assert (n % qk == 0);
346    assert (nc % ncols_interleaved == 0);
347
348    UNUSED(s);
349    UNUSED(bs);
350    UNUSED(vx);
351    UNUSED(vy);
352    UNUSED(nr);
353    UNUSED(nc);
354    UNUSED(nb);
355    UNUSED(ncols_interleaved);
356    UNUSED(blocklen);
357
358#if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__)
359#if defined(__ARM_FEATURE_SVE)
360    if (ggml_cpu_get_sve_cnt() == QK8_0) {
361        const void * b_ptr = vx;
362        const void * a_ptr = vy;
363        float * res_ptr = s;
364
365        __asm__ __volatile__(
366            "ptrue p0.b\n"
367            "add %x[b_ptr], %x[b_ptr], #0x10\n"
368            "1:"  // Column loop
369            "add x22, %x[a_ptr], #0x2\n"
370            "mov z31.b, #0x0\n"
371            "mov x21, %x[nb]\n"
372            "2:"  // Block loop
373            "ld1b { z30.b }, p0/Z, [%x[b_ptr]]\n"
374            "ld1b { z29.b }, p0/Z, [%x[b_ptr], #1, MUL VL]\n"
375            "mov z28.s, #0x0\n"
376            "mov z27.s, #0x0\n"
377            "ld1rd { z26.d }, p0/Z, [x22]\n"
378            "ld1b { z25.b }, p0/Z, [%x[b_ptr], #2, MUL VL]\n"
379            "sub x20, x22, #0x2\n"
380            "sub x21, x21, #0x1\n"
381            "ld1b { z24.b }, p0/Z, [%x[b_ptr], #3, MUL VL]\n"
382            "ld1rd { z23.d }, p0/Z, [x22, #8]\n"
383            "lsl z22.b, z30.b, #0x4\n"
384            "lsl z16.b, z29.b, #0x4\n"
385            "and z30.b, z30.b, #0xf0\n"
386            "and z29.b, z29.b, #0xf0\n"
387            "ld1rd { z21.d }, p0/Z, [x22, #16]\n"
388            "ld1rd { z20.d }, p0/Z, [x22, #24]\n"
389            "lsl z19.b, z25.b, #0x4\n"
390            "and z25.b, z25.b, #0xf0\n"
391            "ld1rh { z17.h }, p0/Z, [x20]\n"
392            "ld1h { z18.s }, p0/Z, [%x[b_ptr], #-1, MUL VL]\n"
393            "sdot z28.s, z22.b, z26.b\n"
394            "sdot z27.s, z16.b, z26.b\n"
395            "lsl z16.b, z24.b, #0x4\n"
396            "add x22, x22, #0x22\n"
397            "and z24.b, z24.b, #0xf0\n"
398            "add %x[b_ptr], %x[b_ptr], #0x90\n"
399            "fcvt z17.s, p0/m, z17.h\n"
400            "fcvt z18.s, p0/m, z18.h\n"
401            "sdot z28.s, z19.b, z23.b\n"
402            "sdot z27.s, z16.b, z23.b\n"
403            "fmul z18.s, z18.s, z17.s\n"
404            "sdot z28.s, z30.b, z21.b\n"
405            "sdot z27.s, z29.b, z21.b\n"
406            "sdot z28.s, z25.b, z20.b\n"
407            "sdot z27.s, z24.b, z20.b\n"
408            "uzp1 z17.s, z28.s, z27.s\n"
409            "uzp2 z16.s, z28.s, z27.s\n"
410            "add z17.s, z17.s, z16.s\n"
411            "asr z17.s, z17.s, #0x4\n"
412            "scvtf z17.s, p0/m, z17.s\n"
413            "fmla z31.s, p0/M, z17.s, z18.s\n"
414            "cbnz x21, 2b\n"
415            "sub %x[nc], %x[nc], #0x8\n"
416            "st1w { z31.s }, p0, [%x[res_ptr]]\n"
417            "add %x[res_ptr], %x[res_ptr], #0x20\n"
418            "cbnz %x[nc], 1b\n"
419            : [b_ptr] "+&r" (b_ptr), [res_ptr] "+&r" (res_ptr), [nc] "+&r" (nc)
420            : [a_ptr] "r" (a_ptr), [nb] "r" (nb)
421            : "memory", "p0", "x20", "x21", "x22", "z16", "z17", "z18", "z19", "z20", "z21", "z22", "z23", "z24", "z25", "z26", "z27", "z28", "z29", "z30", "z31"
422        );
423        return;
424    }
425#endif // #if defined(__ARM_FEATURE_SVE)
426
427#endif // #if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__)
428    ggml_gemv_q4_0_8x8_q8_0_generic(n, s, bs, vx, vy, nr, nc);
429}
430
431void ggml_gemv_iq4_nl_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
432    const int qk = QK8_0;
433    const int nb = n / qk;
434    const int ncols_interleaved = 4;
435    const int blocklen = 4;
436
437    assert (n % qk == 0);
438    assert (nc % ncols_interleaved == 0);
439
440    UNUSED(s);
441    UNUSED(bs);
442    UNUSED(vx);
443    UNUSED(vy);
444    UNUSED(nr);
445    UNUSED(nc);
446    UNUSED(nb);
447    UNUSED(ncols_interleaved);
448    UNUSED(blocklen);
449
450#if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
451    const int8x16_t kvalues = vld1q_s8(kvalues_iq4nl);
452    const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
453    float * res_ptr = s;
454
455    for (int x = 0; x < nc / ncols_interleaved; x++) {
456        const block_iq4_nlx4 * b_ptr = (const block_iq4_nlx4 *) vx + (x * nb);
457
458        float32x4_t sumf = vdupq_n_f32(0);
459        for (int l = 0; l < nb; l++) {
460            uint8x16_t b_0 = vld1q_u8(b_ptr[l].qs + 0);
461            uint8x16_t b_1 = vld1q_u8(b_ptr[l].qs + 16);
462            uint8x16_t b_2 = vld1q_u8(b_ptr[l].qs + 32);
463            uint8x16_t b_3 = vld1q_u8(b_ptr[l].qs + 48);
464
465            int8x16_t b_0_hi = vqtbl1q_s8(kvalues, b_0 >> 4);
466            int8x16_t b_0_lo = vqtbl1q_s8(kvalues, b_0 & 0x0F);
467            int8x16_t b_1_hi = vqtbl1q_s8(kvalues, b_1 >> 4);
468            int8x16_t b_1_lo = vqtbl1q_s8(kvalues, b_1 & 0x0F);
469            int8x16_t b_2_hi = vqtbl1q_s8(kvalues, b_2 >> 4);
470            int8x16_t b_2_lo = vqtbl1q_s8(kvalues, b_2 & 0x0F);
471            int8x16_t b_3_hi = vqtbl1q_s8(kvalues, b_3 >> 4);
472            int8x16_t b_3_lo = vqtbl1q_s8(kvalues, b_3 & 0x0F);
473
474            int8x16_t a_0 = vld1q_s8(a_ptr[l].qs + 0);
475            int8x16_t a_1 = vld1q_s8(a_ptr[l].qs + 16);
476
477            int32x4_t sumi = vdupq_n_s32(0);
478            sumi = vdotq_laneq_s32(sumi, b_0_lo, a_0, 0);
479            sumi = vdotq_laneq_s32(sumi, b_0_hi, a_1, 0);
480            sumi = vdotq_laneq_s32(sumi, b_1_lo, a_0, 1);
481            sumi = vdotq_laneq_s32(sumi, b_1_hi, a_1, 1);
482            sumi = vdotq_laneq_s32(sumi, b_2_lo, a_0, 2);
483            sumi = vdotq_laneq_s32(sumi, b_2_hi, a_1, 2);
484            sumi = vdotq_laneq_s32(sumi, b_3_lo, a_0, 3);
485            sumi = vdotq_laneq_s32(sumi, b_3_hi, a_1, 3);
486
487            float32x4_t a_d = vcvt_f32_f16(vld1_dup_f16((const float16_t *)&a_ptr[l].d));
488            float32x4_t b_d = vcvt_f32_f16(vld1_f16((const float16_t *)b_ptr[l].d));
489            float32x4_t d = a_d * b_d;
490
491            sumf = vmlaq_f32(sumf, d, vcvtq_f32_s32(sumi));
492        }
493
494        vst1q_f32(res_ptr + x * 4, sumf);
495    }
496    return;
497#endif // #if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON)
498    ggml_gemv_iq4_nl_4x4_q8_0_generic(n, s, bs, vx, vy, nr, nc);
499}
500
501void ggml_gemv_mxfp4_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
502    const int qk = QK8_0;
503    const int nb = n / qk;
504    const int ncols_interleaved = 4;
505    const int blocklen = 4;
506
507    assert (n % qk == 0);
508    assert (nc % ncols_interleaved == 0);
509
510    UNUSED(s);
511    UNUSED(bs);
512    UNUSED(vx);
513    UNUSED(vy);
514    UNUSED(nr);
515    UNUSED(nc);
516    UNUSED(nb);
517    UNUSED(ncols_interleaved);
518    UNUSED(blocklen);
519
520#if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
521    const int8x16_t kvalues = vld1q_s8(kvalues_mxfp4);
522    const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
523    float * res_ptr = s;
524
525    for (int x = 0; x < nc / ncols_interleaved; x++) {
526        const block_mxfp4x4 * b_ptr = (const block_mxfp4x4 *) vx + (x * nb);
527
528        float32x4_t sumf = vdupq_n_f32(0);
529        for (int l = 0; l < nb; l++) {
530            uint8x16_t b_0 = vld1q_u8(b_ptr[l].qs + 0);
531            uint8x16_t b_1 = vld1q_u8(b_ptr[l].qs + 16);
532            uint8x16_t b_2 = vld1q_u8(b_ptr[l].qs + 32);
533            uint8x16_t b_3 = vld1q_u8(b_ptr[l].qs + 48);
534
535            int8x16_t b_0_hi = vqtbl1q_s8(kvalues, b_0 >> 4);
536            int8x16_t b_0_lo = vqtbl1q_s8(kvalues, b_0 & 0x0F);
537            int8x16_t b_1_hi = vqtbl1q_s8(kvalues, b_1 >> 4);
538            int8x16_t b_1_lo = vqtbl1q_s8(kvalues, b_1 & 0x0F);
539            int8x16_t b_2_hi = vqtbl1q_s8(kvalues, b_2 >> 4);
540            int8x16_t b_2_lo = vqtbl1q_s8(kvalues, b_2 & 0x0F);
541            int8x16_t b_3_hi = vqtbl1q_s8(kvalues, b_3 >> 4);
542            int8x16_t b_3_lo = vqtbl1q_s8(kvalues, b_3 & 0x0F);
543
544            int8x16_t a_0 = vld1q_s8(a_ptr[l].qs + 0);
545            int8x16_t a_1 = vld1q_s8(a_ptr[l].qs + 16);
546
547            int32x4_t sumi = vdupq_n_s32(0);
548            sumi = vdotq_laneq_s32(sumi, b_0_lo, a_0, 0);
549            sumi = vdotq_laneq_s32(sumi, b_0_hi, a_1, 0);
550            sumi = vdotq_laneq_s32(sumi, b_1_lo, a_0, 1);
551            sumi = vdotq_laneq_s32(sumi, b_1_hi, a_1, 1);
552            sumi = vdotq_laneq_s32(sumi, b_2_lo, a_0, 2);
553            sumi = vdotq_laneq_s32(sumi, b_2_hi, a_1, 2);
554            sumi = vdotq_laneq_s32(sumi, b_3_lo, a_0, 3);
555            sumi = vdotq_laneq_s32(sumi, b_3_hi, a_1, 3);
556
557            float32x4_t a_d = vcvt_f32_f16(vld1_dup_f16((const float16_t *)&a_ptr[l].d));
558            float32x4_t b_d = {
559                GGML_CPU_E8M0_TO_FP32_HALF(b_ptr[l].e[0]),
560                GGML_CPU_E8M0_TO_FP32_HALF(b_ptr[l].e[1]),
561                GGML_CPU_E8M0_TO_FP32_HALF(b_ptr[l].e[2]),
562                GGML_CPU_E8M0_TO_FP32_HALF(b_ptr[l].e[3]),
563            };
564            float32x4_t d = a_d * b_d;
565
566            sumf = vmlaq_f32(sumf, d, vcvtq_f32_s32(sumi));
567        }
568
569        vst1q_f32(res_ptr + x * 4, sumf);
570    }
571    return;
572#endif // #if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON)
573    ggml_gemv_mxfp4_4x4_q8_0_generic(n, s, bs, vx, vy, nr, nc);
574}
575
576void ggml_gemv_q4_K_8x4_q8_K(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
577    constexpr int qk = QK_K;
578    const int     nb = n / qk;
579
580    constexpr int ncols_interleaved = 8;
581    constexpr int blocklen          = 8;
582
583    assert(n % qk == 0);
584    assert(nc % ncols_interleaved == 0);
585
586    UNUSED(nb);
587    UNUSED(ncols_interleaved);
588    UNUSED(blocklen);
589
590#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
591    constexpr int    col_groups = ncols_interleaved / 4; // 0123 and 4567
592    const uint8x16_t m4b        = vdupq_n_u8(0x0f);
593
594    // 1x8 tile = 2 x 4
595    float32x4_t acc_f32[col_groups];
596
597    const block_q8_K * GGML_RESTRICT q8_ptr = (const block_q8_K *) vy;
598
599    for (int x = 0; x < nc / ncols_interleaved; x++) {
600        const block_q4_Kx8 * GGML_RESTRICT q4_ptr = (const block_q4_Kx8 *) vx + (x * nb);
601
602        for (int i = 0; i < col_groups; i++) {
603            acc_f32[i] = vdupq_n_f32(0);
604        }
605
606        for (int b = 0; b < nb; b++) {
607            float32x4_t q4_d_0        = vcvt_f32_f16(vld1_f16((const __fp16 *) q4_ptr[b].d));      // d0 d1 d2 d3
608            float32x4_t q4_d_1        = vcvt_f32_f16(vld1_f16((const __fp16 *) q4_ptr[b].d + 4));  // d4 d5 d6 d7
609            float32x4_t q8_d          = vdupq_n_f32(q8_ptr[b].d);
610            float32x4_t sb_scale_0123 = vmulq_f32(q4_d_0, q8_d);
611            float32x4_t sb_scale_4567 = vmulq_f32(q4_d_1, q8_d);
612            float32x4_t q4_dmin_0     = vcvt_f32_f16(vld1_f16((const __fp16 *) q4_ptr[b].dmin));      // dmin 0..3
613            float32x4_t q4_dmin_1     = vcvt_f32_f16(vld1_f16((const __fp16 *) q4_ptr[b].dmin + 4));  // dmin 4..7
614            float32x4_t sb_min_0123   = vmulq_f32(q4_dmin_0, q8_d);
615            float32x4_t sb_min_4567   = vmulq_f32(q4_dmin_1, q8_d);
616
617            // interleaved bias_acc: [0]->r0 0123, [1]->r0 4567
618            int32x4_t bias_acc[2] = { vdupq_n_s32(0), vdupq_n_s32(0) };
619            int32x4_t acc_lo[col_groups];
620            int32x4_t acc_hi[col_groups];
621
622            // Each bsum is 16 elements, pairwise add leaves us with the 8 bsums of the entire block
623            const int16x8_t bsums = vpaddq_s16(vld1q_s16(q8_ptr[b].bsums), vld1q_s16(q8_ptr[b].bsums + 8));
624            int16_t         bsums_arr[8];
625            vst1q_s16(bsums_arr, bsums);
626            for (int sb = 0; sb < QK_K / 64; sb++) {
627                for (int i = 0; i < col_groups; i++) {
628                    acc_lo[i] = vdupq_n_s32(0);
629                    acc_hi[i] = vdupq_n_s32(0);
630                }
631                // Need scales for the low and high nibbles
632                // 2 * 12 = 24 bytes per subblock, 4 sbs -> 4 * 24 = 96 bytes total
633                int16x8_t q4sb_mins[2];
634                int16x8_t q4sb_scales[2];
635                for (int i = 0; i < 2; i++) {
636                    int8_t    aux_q4sb[8];
637                    const int offset = sb * 24 + i * 12;
638                    decode_q_Kx8_6bit_scales(&q4_ptr[b].scales[offset], &q4sb_mins[i], aux_q4sb);
639                    q4sb_scales[i] = vmovl_s8(vld1_s8(aux_q4sb));
640                }
641
642                int8x16_t q8_qs[64 / 16];
643                for (int i = 0; i < 64 / 16; i++) {
644                    q8_qs[i] = vld1q_s8(q8_ptr[b].qs + sb * 64 + i * 16);
645                }
646
647                for (int c = 0; c < col_groups; c++) {
648                    uint8x16_t q4_cols[8];
649                    for (int i = 0; i < 8; i++) {
650                        q4_cols[i] = vld1q_u8(q4_ptr[b].qs + sb * QK_K + i * 32 + 16 * c);
651                    }
652
653                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], vreinterpretq_s8_u8(vandq_u8(q4_cols[0], m4b)), q8_qs[0], 0);
654                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], vreinterpretq_s8_u8(vandq_u8(q4_cols[1], m4b)), q8_qs[0], 1);
655                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], vreinterpretq_s8_u8(vandq_u8(q4_cols[2], m4b)), q8_qs[0], 2);
656                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], vreinterpretq_s8_u8(vandq_u8(q4_cols[3], m4b)), q8_qs[0], 3);
657                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], vreinterpretq_s8_u8(vandq_u8(q4_cols[4], m4b)), q8_qs[1], 0);
658                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], vreinterpretq_s8_u8(vandq_u8(q4_cols[5], m4b)), q8_qs[1], 1);
659                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], vreinterpretq_s8_u8(vandq_u8(q4_cols[6], m4b)), q8_qs[1], 2);
660                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], vreinterpretq_s8_u8(vandq_u8(q4_cols[7], m4b)), q8_qs[1], 3);
661
662                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], vreinterpretq_s8_u8(vshrq_n_u8(q4_cols[0], 4)), q8_qs[2], 0);
663                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], vreinterpretq_s8_u8(vshrq_n_u8(q4_cols[1], 4)), q8_qs[2], 1);
664                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], vreinterpretq_s8_u8(vshrq_n_u8(q4_cols[2], 4)), q8_qs[2], 2);
665                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], vreinterpretq_s8_u8(vshrq_n_u8(q4_cols[3], 4)), q8_qs[2], 3);
666                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], vreinterpretq_s8_u8(vshrq_n_u8(q4_cols[4], 4)), q8_qs[3], 0);
667                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], vreinterpretq_s8_u8(vshrq_n_u8(q4_cols[5], 4)), q8_qs[3], 1);
668                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], vreinterpretq_s8_u8(vshrq_n_u8(q4_cols[6], 4)), q8_qs[3], 2);
669                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], vreinterpretq_s8_u8(vshrq_n_u8(q4_cols[7], 4)), q8_qs[3], 3);
670                }
671
672                // Scales
673                // row c0123 blk0 and blk1
674                const int16x4_t   sc_0123_lo = vget_low_s16(q4sb_scales[0]);
675                const int16x4_t   sc_0123_hi = vget_low_s16(q4sb_scales[1]);
676                const float32x4_t sumf_0123  = vcvtq_f32_s32(vaddq_s32(vmulq_s32(vmovl_s16(sc_0123_lo), acc_lo[0]),
677                                                                       vmulq_s32(vmovl_s16(sc_0123_hi), acc_hi[0])));
678                acc_f32[0]                   = vfmaq_f32(acc_f32[0], sb_scale_0123, sumf_0123);
679                // row c4567 blk0 and blk1
680                const int16x4_t   sc_4567_lo = vget_high_s16(q4sb_scales[0]);
681                const int16x4_t   sc_4567_hi = vget_high_s16(q4sb_scales[1]);
682                const float32x4_t sumf_4567  = vcvtq_f32_s32(vaddq_s32(vmulq_s32(vmovl_s16(sc_4567_lo), acc_lo[1]),
683                                                                       vmulq_s32(vmovl_s16(sc_4567_hi), acc_hi[1])));
684                acc_f32[1]                   = vfmaq_f32(acc_f32[1], sb_scale_4567, sumf_4567);
685
686                // Bias Correction
687                const int16x4_t bsums_vec_lo = vdup_n_s16(bsums_arr[2 * sb + 0]);
688                const int16x4_t bsums_vec_hi = vdup_n_s16(bsums_arr[2 * sb + 1]);
689
690                bias_acc[0] = vmlal_s16(bias_acc[0], bsums_vec_lo, vget_low_s16(q4sb_mins[0]));
691                bias_acc[0] = vmlal_s16(bias_acc[0], bsums_vec_hi, vget_low_s16(q4sb_mins[1]));
692                bias_acc[1] = vmlal_s16(bias_acc[1], bsums_vec_lo, vget_high_s16(q4sb_mins[0]));
693                bias_acc[1] = vmlal_s16(bias_acc[1], bsums_vec_hi, vget_high_s16(q4sb_mins[1]));
694            }  // for sb
695
696            acc_f32[0] = vmlsq_f32(acc_f32[0], vcvtq_f32_s32(bias_acc[0]), sb_min_0123);
697            acc_f32[1] = vmlsq_f32(acc_f32[1], vcvtq_f32_s32(bias_acc[1]), sb_min_4567);
698        }  // for b
699
700        int base = x * ncols_interleaved;
701        vst1q_f32(s + base, acc_f32[0]);
702        vst1q_f32(s + base + 4, acc_f32[1]);
703    }  // for x
704    return;
705#endif  // #if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
706    ggml_gemv_q4_K_8x4_q8_K_generic(n, s, bs, vx, vy, nr, nc);
707}
708
709void ggml_gemv_q4_K_8x8_q8_K(int                        n,
710                             float * GGML_RESTRICT      s,
711                             size_t                     bs,
712                             const void * GGML_RESTRICT vx,
713                             const void * GGML_RESTRICT vy,
714                             int                        nr,
715                             int                        nc) {
716    constexpr int qk = QK_K;
717    const int     nb = n / qk;
718
719    constexpr int ncols_interleaved = 8;
720    constexpr int blocklen          = 8;
721
722    assert(n % qk == 0);
723    assert(nc % ncols_interleaved == 0);
724
725    UNUSED(nb);
726    UNUSED(ncols_interleaved);
727    UNUSED(blocklen);
728
729#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
730    constexpr int    col_pairs = ncols_interleaved / 2;
731    const uint8x16_t m4b       = vdupq_n_u8(0x0f);
732
733    // 1x8 tile = 2 x 4
734    float32x4_t acc_f32[ncols_interleaved / 4];
735
736    const block_q8_K * GGML_RESTRICT q8_ptr = (const block_q8_K *) vy;
737
738    for (int x = 0; x < nc / ncols_interleaved; x++) {
739        const block_q4_Kx8 * GGML_RESTRICT q4_ptr = (const block_q4_Kx8 *) vx + (x * nb);
740
741        for (int i = 0; i < ncols_interleaved / 4; i++) {
742            acc_f32[i] = vdupq_n_f32(0);
743        }
744
745        for (int b = 0; b < nb; b++) {
746            float32x4_t q4_d_0     = vcvt_f32_f16(vld1_f16((const __fp16 *) q4_ptr[b].d));      // d0 d1 d2 d3
747            float32x4_t q4_d_1     = vcvt_f32_f16(vld1_f16((const __fp16 *) q4_ptr[b].d + 4));  // d4 d5 d6 d7
748            float32x4_t q8_d       = vdupq_n_f32(q8_ptr[b].d);
749            float32x4_t sb_scale_0 = vmulq_f32(q4_d_0, q8_d);
750            float32x4_t sb_scale_1 = vmulq_f32(q4_d_1, q8_d);
751            float32x4_t q4_dmin_0  = vcvt_f32_f16(vld1_f16((const __fp16 *) q4_ptr[b].dmin));      // dmin 0..3
752            float32x4_t q4_dmin_1  = vcvt_f32_f16(vld1_f16((const __fp16 *) q4_ptr[b].dmin + 4));  // dmin 4..7
753            float32x4_t sb_min_0   = vmulq_f32(q4_dmin_0, q8_d);
754            float32x4_t sb_min_1   = vmulq_f32(q4_dmin_1, q8_d);
755
756            // interleaved bias_acc: [0]->r0 0123, [1]->r0 4567
757            int32x4_t bias_acc[2] = { vdupq_n_s32(0), vdupq_n_s32(0) };
758            // 2 sb each iteration
759            int32x4_t acc_lo[col_pairs];
760            int32x4_t acc_hi[col_pairs];
761
762            // Each bsum is 16 elements, pairwise add leaves us with the 8 bsums of the entire block
763            const int16x8_t bsums = vpaddq_s16(vld1q_s16(q8_ptr[b].bsums), vld1q_s16(q8_ptr[b].bsums + 8));
764            int16_t         bsums_arr[8];
765            vst1q_s16(bsums_arr, bsums);
766            for (int sb = 0; sb < QK_K / 64; sb++) {
767                for (int i = 0; i < col_pairs; i++) {
768                    acc_lo[i] = vdupq_n_s32(0);
769                    acc_hi[i] = vdupq_n_s32(0);
770                }
771                // Need scales for the low and high nibbles
772                // 2 * 12 = 24 bytes per subblock, 4 sbs -> 4 * 24 = 96 bytes total
773                int16x8_t q4sb_mins[2];  // int16 as its needed for bias_acc later
774                int16x8_t q4sb_scales[2];
775                for (int i = 0; i < 2; i++) {
776                    int8_t    aux_q4sb[8];
777                    const int offset = sb * 24 + i * 12;
778                    decode_q_Kx8_6bit_scales(&q4_ptr[b].scales[offset], &q4sb_mins[i], aux_q4sb);
779                    q4sb_scales[i] = vmovl_s8(vld1_s8(aux_q4sb));
780                }
781
782                const uint8_t * q4_base = q4_ptr[b].qs + sb * QK_K;
783
784                // Load the 64 quants from q8K duplicated to use vecdots with the interleaved columns
785                // but still need the qs to use the low and hi bits from q4
786                const int8_t * q8_base = q8_ptr[b].qs + sb * 64;
787                int8x16_t      q8_qs[8];
788                for (int i = 0; i < 8; i++) {
789                    q8_qs[i] = (int8x16_t) vld1q_dup_s64((const int64_t *) (q8_base + i * 8));
790                }
791
792                // Q4s columns iterated in pairs (01, 23, 45, 67)
793                for (int cp = 0; cp < col_pairs; cp++) {
794                    uint8x16_t q4_qs_cp_0 = vld1q_u8(q4_base + 16 * cp);
795                    uint8x16_t q4_qs_cp_1 = vld1q_u8(q4_base + 16 * cp + 64);
796                    uint8x16_t q4_qs_cp_2 = vld1q_u8(q4_base + 16 * cp + 128);
797                    uint8x16_t q4_qs_cp_3 = vld1q_u8(q4_base + 16 * cp + 192);
798
799                    acc_lo[cp] =
800                        ggml_vdotq_s32(acc_lo[cp], vreinterpretq_s8_u8(vandq_u8(q4_qs_cp_0, m4b)), q8_qs[0]);  // 0 .. 7
801                    acc_lo[cp] =
802                        ggml_vdotq_s32(acc_lo[cp], vreinterpretq_s8_u8(vandq_u8(q4_qs_cp_1, m4b)), q8_qs[1]);  // 8 ..15
803                    acc_lo[cp] =
804                        ggml_vdotq_s32(acc_lo[cp], vreinterpretq_s8_u8(vandq_u8(q4_qs_cp_2, m4b)), q8_qs[2]);  // 16..23
805                    acc_lo[cp] =
806                        ggml_vdotq_s32(acc_lo[cp], vreinterpretq_s8_u8(vandq_u8(q4_qs_cp_3, m4b)), q8_qs[3]);  // 24..31
807
808                    acc_hi[cp] =
809                        ggml_vdotq_s32(acc_hi[cp], vreinterpretq_s8_u8(vshrq_n_u8(q4_qs_cp_0, 4)), q8_qs[4]);  // 32..39
810                    acc_hi[cp] =
811                        ggml_vdotq_s32(acc_hi[cp], vreinterpretq_s8_u8(vshrq_n_u8(q4_qs_cp_1, 4)), q8_qs[5]);  // 40..47
812                    acc_hi[cp] =
813                        ggml_vdotq_s32(acc_hi[cp], vreinterpretq_s8_u8(vshrq_n_u8(q4_qs_cp_2, 4)), q8_qs[6]);  // 48..55
814                    acc_hi[cp] =
815                        ggml_vdotq_s32(acc_hi[cp], vreinterpretq_s8_u8(vshrq_n_u8(q4_qs_cp_3, 4)), q8_qs[7]);  // 56..63
816                }
817
818                // Iterates over a pair of column pairs (4 columns) to use a single 128 register
819                // p = 0 -> 0123  p2 -> 4567
820                for (int i = 0, p = 0; p < col_pairs; i++, p += 2) {
821                    int16x4_t   group_scales_lo = p == 0 ? vget_low_s16(q4sb_scales[0]) : vget_high_s16(q4sb_scales[0]);
822                    int16x4_t   group_scales_hi = p == 0 ? vget_low_s16(q4sb_scales[1]) : vget_high_s16(q4sb_scales[1]);
823                    float32x4_t sb_scale        = p == 0 ? sb_scale_0 : sb_scale_1;
824
825                    // 0123 or 4567
826                    float32x4_t sumf_0 =
827                        vcvtq_f32_s32(vmulq_s32(vmovl_s16(group_scales_lo), vpaddq_s32(acc_lo[p], acc_lo[p + 1])));
828                    acc_f32[i] = vfmaq_f32(acc_f32[i], sb_scale, sumf_0);
829
830                    float32x4_t sumf_1 =
831                        vcvtq_f32_s32(vmulq_s32(vmovl_s16(group_scales_hi), vpaddq_s32(acc_hi[p], acc_hi[p + 1])));
832                    acc_f32[i] = vfmaq_f32(acc_f32[i], sb_scale, sumf_1);
833                }
834
835                // Multiply Acc bsum + mins
836                // Each pair of subblocks share the same bsums
837                // Load scalar bsum → broadcast to a vector (vdupq_n_s16(s)).
838                int16x4_t bsums_vec_lo = vdup_n_s16(bsums_arr[2 * sb + 0]);
839                int16x4_t bsums_vec_hi = vdup_n_s16(bsums_arr[2 * sb + 1]);
840
841                // cols 0-3 bias
842                bias_acc[0] = vmlal_s16(bias_acc[0], bsums_vec_lo, vget_low_s16(q4sb_mins[0]));
843                bias_acc[0] = vmlal_s16(bias_acc[0], bsums_vec_hi, vget_low_s16(q4sb_mins[1]));
844
845                // cols 4-7 bias
846                bias_acc[1] = vmlal_s16(bias_acc[1], bsums_vec_lo, vget_high_s16(q4sb_mins[0]));
847                bias_acc[1] = vmlal_s16(bias_acc[1], bsums_vec_hi, vget_high_s16(q4sb_mins[1]));
848            }  // for sb
849
850            acc_f32[0] = vmlsq_f32(acc_f32[0], vcvtq_f32_s32(bias_acc[0]), sb_min_0);
851            acc_f32[1] = vmlsq_f32(acc_f32[1], vcvtq_f32_s32(bias_acc[1]), sb_min_1);
852        }  // for b
853
854        int base = x * ncols_interleaved;
855        vst1q_f32(s + base, acc_f32[0]);
856        vst1q_f32(s + base + 4, acc_f32[1]);
857    }  // for x
858    return;
859#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
860    ggml_gemv_q4_K_8x8_q8_K_generic(n, s, bs, vx, vy, nr, nc);
861}
862
863void ggml_gemv_q5_K_8x4_q8_K(int                        n,
864                             float * GGML_RESTRICT      s,
865                             size_t                     bs,
866                             const void * GGML_RESTRICT vx,
867                             const void * GGML_RESTRICT vy,
868                             int                        nr,
869                             int                        nc) {
870    constexpr int qk = QK_K;
871    const int     nb = n / qk;
872
873    constexpr int ncols_interleaved = 8;
874    constexpr int blocklen          = 4;
875
876    assert(n % qk == 0);
877    assert(nc % ncols_interleaved == 0);
878
879    UNUSED(nb);
880    UNUSED(ncols_interleaved);
881    UNUSED(blocklen);
882
883#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
884    constexpr int    col_groups = ncols_interleaved / 4;  // 0123 and 4567
885    const uint8x16_t m4b        = vdupq_n_u8(0x0f);
886    const uint8x16_t mone       = vdupq_n_u8(1);
887    const uint8x16_t mtwo       = vdupq_n_u8(2);
888
889    // 1x8 tile = 2 x 4
890    float32x4_t acc_f32[col_groups];
891
892    const block_q8_K * GGML_RESTRICT q8_ptr = (const block_q8_K *) vy;
893
894    for (int x = 0; x < nc / ncols_interleaved; x++) {
895        const block_q5_Kx8 * GGML_RESTRICT q5_ptr = (const block_q5_Kx8 *) vx + (x * nb);
896
897        for (int i = 0; i < col_groups; i++) {
898            acc_f32[i] = vdupq_n_f32(0);
899        }
900
901        for (int b = 0; b < nb; b++) {
902            float32x4_t q5_d_0        = vcvt_f32_f16(vld1_f16((const __fp16 *) q5_ptr[b].d));      // d0 d1 d2 d3
903            float32x4_t q5_d_1        = vcvt_f32_f16(vld1_f16((const __fp16 *) q5_ptr[b].d + 4));  // d4 d5 d6 d7
904            float32x4_t q8_d          = vdupq_n_f32(q8_ptr[b].d);
905            float32x4_t sb_scale_0123 = vmulq_f32(q5_d_0, q8_d);
906            float32x4_t sb_scale_4567 = vmulq_f32(q5_d_1, q8_d);
907            float32x4_t q5_dmin_0     = vcvt_f32_f16(vld1_f16((const __fp16 *) q5_ptr[b].dmin));      // dmin 0..3
908            float32x4_t q5_dmin_1     = vcvt_f32_f16(vld1_f16((const __fp16 *) q5_ptr[b].dmin + 4));  // dmin 4..7
909            float32x4_t sb_min_0123   = vmulq_f32(q5_dmin_0, q8_d);
910            float32x4_t sb_min_4567   = vmulq_f32(q5_dmin_1, q8_d);
911
912            // interleaved bias_acc: [0]->r0 0123, [1]->r0 4567
913            int32x4_t bias_acc[2] = { vdupq_n_s32(0), vdupq_n_s32(0) };
914            int32x4_t acc_lo[col_groups];
915            int32x4_t acc_hi[col_groups];
916
917            // Each bsum is 16 elements, pairwise add leaves us with the 8 bsums of the entire block
918            const int16x8_t bsums = vpaddq_s16(vld1q_s16(q8_ptr[b].bsums), vld1q_s16(q8_ptr[b].bsums + 8));
919            int16_t         bsums_arr[8];
920            vst1q_s16(bsums_arr, bsums);
921
922            uint8x16_t qh[col_groups][8];
923            for (int c = 0; c < col_groups; c++) {
924                for (int i = 0; i < 8; i++) {
925                    qh[c][i] = vld1q_u8(q5_ptr[b].qh + i * 32 + 16 * c);
926                }
927            }
928
929            for (int sb = 0; sb < QK_K / 64; sb++) {
930                for (int i = 0; i < col_groups; i++) {
931                    acc_lo[i] = vdupq_n_s32(0);
932                    acc_hi[i] = vdupq_n_s32(0);
933                }
934                // Need scales for the low and high nibbles
935                // 2 * 12 = 24 bytes per subblock, 4 sbs -> 4 * 24 = 96 bytes total
936                int16x8_t q5sb_mins[2];
937                int16x8_t q5sb_scales[2];
938                for (int i = 0; i < 2; i++) {
939                    int8_t    aux_q5sb[8];
940                    const int offset = sb * 24 + i * 12;
941                    decode_q_Kx8_6bit_scales(&q5_ptr[b].scales[offset], &q5sb_mins[i], aux_q5sb);
942                    q5sb_scales[i] = vmovl_s8(vld1_s8(aux_q5sb));
943                }
944
945                int8x16_t q8_qs[4];
946                for (int i = 0; i < 4; i++) {
947                    q8_qs[i] = vld1q_s8(q8_ptr[b].qs + sb * 64 + i * 16);
948                }
949
950                for (int c = 0; c < col_groups; c++) {
951                    uint8x16_t q5_cols[8];
952                    uint8x16_t hbit_lo[8];
953                    uint8x16_t hbit_hi[8];
954                    int8x16_t  q5_lo[8];
955                    int8x16_t  q5_hi[8];
956
957                    for (int i = 0; i < 8; i++) {
958                        q5_cols[i] = vld1q_u8(q5_ptr[b].qs + sb * QK_K + i * 32 + 16 * c);
959                        hbit_lo[i] = vandq_u8(qh[c][i], mone);
960                        hbit_hi[i] = vshlq_n_u8(vandq_u8(qh[c][i], mtwo), 3);
961                        qh[c][i]   = vshrq_n_u8(qh[c][i], 2);
962                        q5_lo[i]   = vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(q5_cols[i], m4b), hbit_lo[i], 4));
963                        q5_hi[i]   = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q5_cols[i], 4), hbit_hi[i]));
964                    }
965
966                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], q5_lo[0], q8_qs[0], 0);
967                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], q5_lo[1], q8_qs[0], 1);
968                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], q5_lo[2], q8_qs[0], 2);
969                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], q5_lo[3], q8_qs[0], 3);
970                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], q5_lo[4], q8_qs[1], 0);
971                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], q5_lo[5], q8_qs[1], 1);
972                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], q5_lo[6], q8_qs[1], 2);
973                    acc_lo[c] = vdotq_laneq_s32(acc_lo[c], q5_lo[7], q8_qs[1], 3);
974
975                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], q5_hi[0], q8_qs[2], 0);
976                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], q5_hi[1], q8_qs[2], 1);
977                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], q5_hi[2], q8_qs[2], 2);
978                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], q5_hi[3], q8_qs[2], 3);
979                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], q5_hi[4], q8_qs[3], 0);
980                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], q5_hi[5], q8_qs[3], 1);
981                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], q5_hi[6], q8_qs[3], 2);
982                    acc_hi[c] = vdotq_laneq_s32(acc_hi[c], q5_hi[7], q8_qs[3], 3);
983                }
984
985                // Scales
986                // row c0123 blk0 and blk1
987                const int16x4_t   sc_0123_lo = vget_low_s16(q5sb_scales[0]);
988                const int16x4_t   sc_0123_hi = vget_low_s16(q5sb_scales[1]);
989                const float32x4_t sumf_0123  = vcvtq_f32_s32(vaddq_s32(vmulq_s32(vmovl_s16(sc_0123_lo), acc_lo[0]),
990                                                                       vmulq_s32(vmovl_s16(sc_0123_hi), acc_hi[0])));
991                acc_f32[0]                   = vfmaq_f32(acc_f32[0], sb_scale_0123, sumf_0123);
992                // row c4567 blk0 and blk1
993                const int16x4_t   sc_4567_lo = vget_high_s16(q5sb_scales[0]);
994                const int16x4_t   sc_4567_hi = vget_high_s16(q5sb_scales[1]);
995                const float32x4_t sumf_4567  = vcvtq_f32_s32(vaddq_s32(vmulq_s32(vmovl_s16(sc_4567_lo), acc_lo[1]),
996                                                                       vmulq_s32(vmovl_s16(sc_4567_hi), acc_hi[1])));
997                acc_f32[1]                   = vfmaq_f32(acc_f32[1], sb_scale_4567, sumf_4567);
998
999                // Bias Correction
1000                const int16x4_t bsums_vec_lo = vdup_n_s16(bsums_arr[2 * sb + 0]);
1001                const int16x4_t bsums_vec_hi = vdup_n_s16(bsums_arr[2 * sb + 1]);
1002
1003                bias_acc[0] = vmlal_s16(bias_acc[0], bsums_vec_lo, vget_low_s16(q5sb_mins[0]));
1004                bias_acc[0] = vmlal_s16(bias_acc[0], bsums_vec_hi, vget_low_s16(q5sb_mins[1]));
1005                bias_acc[1] = vmlal_s16(bias_acc[1], bsums_vec_lo, vget_high_s16(q5sb_mins[0]));
1006                bias_acc[1] = vmlal_s16(bias_acc[1], bsums_vec_hi, vget_high_s16(q5sb_mins[1]));
1007            }  // for sb
1008
1009            acc_f32[0] = vmlsq_f32(acc_f32[0], vcvtq_f32_s32(bias_acc[0]), sb_min_0123);
1010            acc_f32[1] = vmlsq_f32(acc_f32[1], vcvtq_f32_s32(bias_acc[1]), sb_min_4567);
1011        }  // for b
1012
1013        int base = x * ncols_interleaved;
1014        vst1q_f32(s + base, acc_f32[0]);
1015        vst1q_f32(s + base + 4, acc_f32[1]);
1016    }  // for x
1017    return;
1018#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
1019    ggml_gemv_q5_K_8x4_q8_K_generic(n, s, bs, vx, vy, nr, nc);
1020}
1021
1022void ggml_gemv_q5_K_8x8_q8_K(int                        n,
1023                             float * GGML_RESTRICT      s,
1024                             size_t                     bs,
1025                             const void * GGML_RESTRICT vx,
1026                             const void * GGML_RESTRICT vy,
1027                             int                        nr,
1028                             int                        nc) {
1029    constexpr int qk = QK_K;
1030    const int     nb = n / qk;
1031
1032    constexpr int ncols_interleaved = 8;
1033    constexpr int blocklen          = 8;
1034
1035    assert(n % qk == 0);
1036    assert(nc % ncols_interleaved == 0);
1037
1038    UNUSED(nb);
1039    UNUSED(ncols_interleaved);
1040    UNUSED(blocklen);
1041
1042#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
1043    constexpr int    col_pairs = ncols_interleaved / 2;
1044    const uint8x16_t m4b       = vdupq_n_u8(0x0f);
1045    const uint8x16_t mone      = vdupq_n_u8(1);
1046    const uint8x16_t mtwo      = vdupq_n_u8(2);
1047
1048    // 1x8 tile = 2 x 4
1049    float32x4_t acc_f32[ncols_interleaved / 4];
1050
1051    const block_q8_K * GGML_RESTRICT q8_ptr = (const block_q8_K *) vy;
1052
1053    for (int x = 0; x < nc / ncols_interleaved; x++) {
1054        const block_q5_Kx8 * GGML_RESTRICT q5_ptr = (const block_q5_Kx8 *) vx + (x * nb);
1055
1056        for (int i = 0; i < ncols_interleaved / 4; i++) {
1057            acc_f32[i] = vdupq_n_f32(0);
1058        }
1059
1060        for (int b = 0; b < nb; b++) {
1061            float32x4_t q5_d_0     = vcvt_f32_f16(vld1_f16((const __fp16 *) q5_ptr[b].d));      // d0 d1 d2 d3
1062            float32x4_t q5_d_1     = vcvt_f32_f16(vld1_f16((const __fp16 *) q5_ptr[b].d + 4));  // d4 d5 d6 d7
1063            float32x4_t q8_d       = vdupq_n_f32(q8_ptr[b].d);
1064            float32x4_t sb_scale_0 = vmulq_f32(q5_d_0, q8_d);
1065            float32x4_t sb_scale_1 = vmulq_f32(q5_d_1, q8_d);
1066            float32x4_t q5_dmin_0  = vcvt_f32_f16(vld1_f16((const __fp16 *) q5_ptr[b].dmin));      // dmin 0..3
1067            float32x4_t q5_dmin_1  = vcvt_f32_f16(vld1_f16((const __fp16 *) q5_ptr[b].dmin + 4));  // dmin 4..7
1068            float32x4_t sb_min_0   = vmulq_f32(q5_dmin_0, q8_d);
1069            float32x4_t sb_min_1   = vmulq_f32(q5_dmin_1, q8_d);
1070
1071            // 2 sb each iteration
1072            int32x4_t acc_lo[col_pairs];
1073            int32x4_t acc_hi[col_pairs];
1074
1075            // Each bsum is 16 elements, pairwise add leaves us with the 8 bsums of the entire block
1076            const int16x8_t bsums = vpaddq_s16(vld1q_s16(q8_ptr[b].bsums), vld1q_s16(q8_ptr[b].bsums + 8));
1077            int16_t         bsums_arr[8];
1078            vst1q_s16(bsums_arr, bsums);
1079
1080            // Load qh once per block and shift after each subblock
1081            const uint8_t * qh_base = q5_ptr[b].qh;
1082            uint8x16_t      qh[col_pairs][4];
1083            for (int cp = 0; cp < col_pairs; cp++) {
1084                qh[cp][0] = vld1q_u8(qh_base + 16 * cp);
1085                qh[cp][1] = vld1q_u8(qh_base + 16 * cp + 64);
1086                qh[cp][2] = vld1q_u8(qh_base + 16 * cp + 128);
1087                qh[cp][3] = vld1q_u8(qh_base + 16 * cp + 192);
1088            }
1089
1090            for (int sb = 0; sb < QK_K / 64; sb++) {
1091                for (int i = 0; i < col_pairs; i++) {
1092                    acc_lo[i] = vdupq_n_s32(0);
1093                    acc_hi[i] = vdupq_n_s32(0);
1094                }
1095                // Need scales for the low and high nibbles
1096                // 2 * 12 = 24 bytes per subblock, 4 sbs -> 4 * 24 = 96 bytes total
1097                int16x8_t q5sb_mins[2];  // int16 as its needed for bias_acc later
1098                int16x8_t q5sb_scales[2];
1099                for (int i = 0; i < 2; i++) {
1100                    int8_t    aux_q5sb[8];
1101                    const int offset = sb * 24 + i * 12;
1102                    decode_q_Kx8_6bit_scales(&q5_ptr[b].scales[offset], &q5sb_mins[i], aux_q5sb);
1103                    q5sb_scales[i] = vmovl_s8(vld1_s8(aux_q5sb));
1104                }
1105
1106                const uint8_t * qs_base = q5_ptr[b].qs + sb * QK_K;
1107
1108                // Load the 64 quants from q8K duplicated to use vecdots with the interleaved columns
1109                const int8_t * q8_base = q8_ptr[b].qs + sb * 64;
1110                int8x16_t      q8_qs[8];
1111                for (int i = 0; i < 8; i++) {
1112                    q8_qs[i] = (int8x16_t) vld1q_dup_s64((const int64_t *) (q8_base + i * 8));
1113                }
1114
1115                // Q5s column pair loop unrolled
1116                {
1117                    // Cols 01
1118                    uint8x16_t qs_0 = vld1q_u8(qs_base);
1119                    uint8x16_t qs_1 = vld1q_u8(qs_base + 64);
1120                    uint8x16_t qs_2 = vld1q_u8(qs_base + 128);
1121                    uint8x16_t qs_3 = vld1q_u8(qs_base + 192);
1122
1123                    uint8x16_t hbit_lo_0 = vandq_u8(qh[0][0], mone);
1124                    uint8x16_t hbit_lo_1 = vandq_u8(qh[0][1], mone);
1125                    uint8x16_t hbit_lo_2 = vandq_u8(qh[0][2], mone);
1126                    uint8x16_t hbit_lo_3 = vandq_u8(qh[0][3], mone);
1127                    uint8x16_t hbit_hi_0 = vshlq_n_u8(vandq_u8(qh[0][0], mtwo), 3);
1128                    uint8x16_t hbit_hi_1 = vshlq_n_u8(vandq_u8(qh[0][1], mtwo), 3);
1129                    uint8x16_t hbit_hi_2 = vshlq_n_u8(vandq_u8(qh[0][2], mtwo), 3);
1130                    uint8x16_t hbit_hi_3 = vshlq_n_u8(vandq_u8(qh[0][3], mtwo), 3);
1131
1132                    qh[0][0] = vshrq_n_u8(qh[0][0], 2);
1133                    qh[0][1] = vshrq_n_u8(qh[0][1], 2);
1134                    qh[0][2] = vshrq_n_u8(qh[0][2], 2);
1135                    qh[0][3] = vshrq_n_u8(qh[0][3], 2);
1136
1137                    acc_lo[0] = ggml_vdotq_s32(
1138                        acc_lo[0], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_0, m4b), hbit_lo_0, 4)), q8_qs[0]);
1139                    acc_lo[0] = ggml_vdotq_s32(
1140                        acc_lo[0], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_1, m4b), hbit_lo_1, 4)), q8_qs[1]);
1141                    acc_lo[0] = ggml_vdotq_s32(
1142                        acc_lo[0], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_2, m4b), hbit_lo_2, 4)), q8_qs[2]);
1143                    acc_lo[0] = ggml_vdotq_s32(
1144                        acc_lo[0], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_3, m4b), hbit_lo_3, 4)), q8_qs[3]);
1145                    acc_hi[0] = ggml_vdotq_s32(acc_hi[0], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_0, 4), hbit_hi_0)),
1146                                               q8_qs[4]);
1147                    acc_hi[0] = ggml_vdotq_s32(acc_hi[0], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_1, 4), hbit_hi_1)),
1148                                               q8_qs[5]);
1149                    acc_hi[0] = ggml_vdotq_s32(acc_hi[0], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_2, 4), hbit_hi_2)),
1150                                               q8_qs[6]);
1151                    acc_hi[0] = ggml_vdotq_s32(acc_hi[0], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_3, 4), hbit_hi_3)),
1152                                               q8_qs[7]);
1153
1154                    // Cols 23
1155                    qs_0 = vld1q_u8(qs_base + 16);
1156                    qs_1 = vld1q_u8(qs_base + 80);
1157                    qs_2 = vld1q_u8(qs_base + 144);
1158                    qs_3 = vld1q_u8(qs_base + 208);
1159
1160                    hbit_lo_0 = vandq_u8(qh[1][0], mone);
1161                    hbit_lo_1 = vandq_u8(qh[1][1], mone);
1162                    hbit_lo_2 = vandq_u8(qh[1][2], mone);
1163                    hbit_lo_3 = vandq_u8(qh[1][3], mone);
1164                    hbit_hi_0 = vshlq_n_u8(vandq_u8(qh[1][0], mtwo), 3);
1165                    hbit_hi_1 = vshlq_n_u8(vandq_u8(qh[1][1], mtwo), 3);
1166                    hbit_hi_2 = vshlq_n_u8(vandq_u8(qh[1][2], mtwo), 3);
1167                    hbit_hi_3 = vshlq_n_u8(vandq_u8(qh[1][3], mtwo), 3);
1168
1169                    qh[1][0] = vshrq_n_u8(qh[1][0], 2);
1170                    qh[1][1] = vshrq_n_u8(qh[1][1], 2);
1171                    qh[1][2] = vshrq_n_u8(qh[1][2], 2);
1172                    qh[1][3] = vshrq_n_u8(qh[1][3], 2);
1173
1174                    acc_lo[1] = ggml_vdotq_s32(
1175                        acc_lo[1], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_0, m4b), hbit_lo_0, 4)), q8_qs[0]);
1176                    acc_lo[1] = ggml_vdotq_s32(
1177                        acc_lo[1], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_1, m4b), hbit_lo_1, 4)), q8_qs[1]);
1178                    acc_lo[1] = ggml_vdotq_s32(
1179                        acc_lo[1], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_2, m4b), hbit_lo_2, 4)), q8_qs[2]);
1180                    acc_lo[1] = ggml_vdotq_s32(
1181                        acc_lo[1], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_3, m4b), hbit_lo_3, 4)), q8_qs[3]);
1182                    acc_hi[1] = ggml_vdotq_s32(acc_hi[1], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_0, 4), hbit_hi_0)),
1183                                               q8_qs[4]);
1184                    acc_hi[1] = ggml_vdotq_s32(acc_hi[1], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_1, 4), hbit_hi_1)),
1185                                               q8_qs[5]);
1186                    acc_hi[1] = ggml_vdotq_s32(acc_hi[1], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_2, 4), hbit_hi_2)),
1187                                               q8_qs[6]);
1188                    acc_hi[1] = ggml_vdotq_s32(acc_hi[1], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_3, 4), hbit_hi_3)),
1189                                               q8_qs[7]);
1190
1191                    // Cols 45
1192                    qs_0 = vld1q_u8(qs_base + 32);
1193                    qs_1 = vld1q_u8(qs_base + 96);
1194                    qs_2 = vld1q_u8(qs_base + 160);
1195                    qs_3 = vld1q_u8(qs_base + 224);
1196
1197                    hbit_lo_0 = vandq_u8(qh[2][0], mone);
1198                    hbit_lo_1 = vandq_u8(qh[2][1], mone);
1199                    hbit_lo_2 = vandq_u8(qh[2][2], mone);
1200                    hbit_lo_3 = vandq_u8(qh[2][3], mone);
1201                    hbit_hi_0 = vshlq_n_u8(vandq_u8(qh[2][0], mtwo), 3);
1202                    hbit_hi_1 = vshlq_n_u8(vandq_u8(qh[2][1], mtwo), 3);
1203                    hbit_hi_2 = vshlq_n_u8(vandq_u8(qh[2][2], mtwo), 3);
1204                    hbit_hi_3 = vshlq_n_u8(vandq_u8(qh[2][3], mtwo), 3);
1205
1206                    qh[2][0] = vshrq_n_u8(qh[2][0], 2);
1207                    qh[2][1] = vshrq_n_u8(qh[2][1], 2);
1208                    qh[2][2] = vshrq_n_u8(qh[2][2], 2);
1209                    qh[2][3] = vshrq_n_u8(qh[2][3], 2);
1210
1211                    acc_lo[2] = ggml_vdotq_s32(
1212                        acc_lo[2], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_0, m4b), hbit_lo_0, 4)), q8_qs[0]);
1213                    acc_lo[2] = ggml_vdotq_s32(
1214                        acc_lo[2], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_1, m4b), hbit_lo_1, 4)), q8_qs[1]);
1215                    acc_lo[2] = ggml_vdotq_s32(
1216                        acc_lo[2], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_2, m4b), hbit_lo_2, 4)), q8_qs[2]);
1217                    acc_lo[2] = ggml_vdotq_s32(
1218                        acc_lo[2], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_3, m4b), hbit_lo_3, 4)), q8_qs[3]);
1219                    acc_hi[2] = ggml_vdotq_s32(acc_hi[2], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_0, 4), hbit_hi_0)),
1220                                               q8_qs[4]);
1221                    acc_hi[2] = ggml_vdotq_s32(acc_hi[2], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_1, 4), hbit_hi_1)),
1222                                               q8_qs[5]);
1223                    acc_hi[2] = ggml_vdotq_s32(acc_hi[2], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_2, 4), hbit_hi_2)),
1224                                               q8_qs[6]);
1225                    acc_hi[2] = ggml_vdotq_s32(acc_hi[2], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_3, 4), hbit_hi_3)),
1226                                               q8_qs[7]);
1227
1228                    // Cols 45
1229                    qs_0 = vld1q_u8(qs_base + 48);
1230                    qs_1 = vld1q_u8(qs_base + 112);
1231                    qs_2 = vld1q_u8(qs_base + 176);
1232                    qs_3 = vld1q_u8(qs_base + 240);
1233
1234                    hbit_lo_0 = vandq_u8(qh[3][0], mone);
1235                    hbit_lo_1 = vandq_u8(qh[3][1], mone);
1236                    hbit_lo_2 = vandq_u8(qh[3][2], mone);
1237                    hbit_lo_3 = vandq_u8(qh[3][3], mone);
1238                    hbit_hi_0 = vshlq_n_u8(vandq_u8(qh[3][0], mtwo), 3);
1239                    hbit_hi_1 = vshlq_n_u8(vandq_u8(qh[3][1], mtwo), 3);
1240                    hbit_hi_2 = vshlq_n_u8(vandq_u8(qh[3][2], mtwo), 3);
1241                    hbit_hi_3 = vshlq_n_u8(vandq_u8(qh[3][3], mtwo), 3);
1242
1243                    qh[3][0] = vshrq_n_u8(qh[3][0], 2);
1244                    qh[3][1] = vshrq_n_u8(qh[3][1], 2);
1245                    qh[3][2] = vshrq_n_u8(qh[3][2], 2);
1246                    qh[3][3] = vshrq_n_u8(qh[3][3], 2);
1247
1248                    acc_lo[3] = ggml_vdotq_s32(
1249                        acc_lo[3], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_0, m4b), hbit_lo_0, 4)), q8_qs[0]);
1250                    acc_lo[3] = ggml_vdotq_s32(
1251                        acc_lo[3], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_1, m4b), hbit_lo_1, 4)), q8_qs[1]);
1252                    acc_lo[3] = ggml_vdotq_s32(
1253                        acc_lo[3], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_2, m4b), hbit_lo_2, 4)), q8_qs[2]);
1254                    acc_lo[3] = ggml_vdotq_s32(
1255                        acc_lo[3], vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_3, m4b), hbit_lo_3, 4)), q8_qs[3]);
1256                    acc_hi[3] = ggml_vdotq_s32(acc_hi[3], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_0, 4), hbit_hi_0)),
1257                                               q8_qs[4]);
1258                    acc_hi[3] = ggml_vdotq_s32(acc_hi[3], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_1, 4), hbit_hi_1)),
1259                                               q8_qs[5]);
1260                    acc_hi[3] = ggml_vdotq_s32(acc_hi[3], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_2, 4), hbit_hi_2)),
1261                                               q8_qs[6]);
1262                    acc_hi[3] = ggml_vdotq_s32(acc_hi[3], vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_3, 4), hbit_hi_3)),
1263                                               q8_qs[7]);
1264                }
1265
1266                // Prepare bsum vectors for bias computation
1267                // Each pair of subblocks share the same bsums
1268                int16x4_t bsums_vec_lo = vdup_n_s16(bsums_arr[2 * sb + 0]);
1269                int16x4_t bsums_vec_hi = vdup_n_s16(bsums_arr[2 * sb + 1]);
1270
1271                // Iterates over a pair of column pairs (4 columns) to use a single 128 register
1272                // p = 0 -> 0123  p2 -> 4567
1273                for (int i = 0, p = 0; p < col_pairs; i++, p += 2) {
1274                    int16x4_t   group_scales_lo = p == 0 ? vget_low_s16(q5sb_scales[0]) : vget_high_s16(q5sb_scales[0]);
1275                    int16x4_t   group_scales_hi = p == 0 ? vget_low_s16(q5sb_scales[1]) : vget_high_s16(q5sb_scales[1]);
1276                    int16x4_t   group_mins_lo   = p == 0 ? vget_low_s16(q5sb_mins[0]) : vget_high_s16(q5sb_mins[0]);
1277                    int16x4_t   group_mins_hi   = p == 0 ? vget_low_s16(q5sb_mins[1]) : vget_high_s16(q5sb_mins[1]);
1278                    float32x4_t sb_scale        = p == 0 ? sb_scale_0 : sb_scale_1;
1279                    float32x4_t sb_min          = p == 0 ? sb_min_0 : sb_min_1;
1280
1281                    // 0123 or 4567
1282                    float32x4_t sumf_0 =
1283                        vcvtq_f32_s32(vmulq_s32(vmovl_s16(group_scales_lo), vpaddq_s32(acc_lo[p], acc_lo[p + 1])));
1284                    acc_f32[i] = vfmaq_f32(acc_f32[i], sb_scale, sumf_0);
1285
1286                    float32x4_t sumf_1 =
1287                        vcvtq_f32_s32(vmulq_s32(vmovl_s16(group_scales_hi), vpaddq_s32(acc_hi[p], acc_hi[p + 1])));
1288                    acc_f32[i] = vfmaq_f32(acc_f32[i], sb_scale, sumf_1);
1289
1290                    // FUSED BIAS: Compute and subtract bias immediately
1291                    // bias = (bsums_lo * mins_lo + bsums_hi * mins_hi) * sb_min
1292                    int32x4_t bias       = vmull_s16(bsums_vec_lo, group_mins_lo);
1293                    bias                 = vmlal_s16(bias, bsums_vec_hi, group_mins_hi);
1294                    float32x4_t bias_f32 = vcvtq_f32_s32(bias);
1295                    acc_f32[i]           = vmlsq_f32(acc_f32[i], sb_min, bias_f32);
1296                }
1297            }  // for sb
1298        }  // for b
1299
1300        int base = x * ncols_interleaved;
1301        vst1q_f32(s + base, acc_f32[0]);
1302        vst1q_f32(s + base + 4, acc_f32[1]);
1303    }  // for x
1304    return;
1305#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
1306    ggml_gemv_q5_K_8x8_q8_K_generic(n, s, bs, vx, vy, nr, nc);
1307}
1308
1309void ggml_gemv_q6_K_8x4_q8_K(int                        n,
1310                             float * GGML_RESTRICT      s,
1311                             size_t                     bs,
1312                             const void * GGML_RESTRICT vx,
1313                             const void * GGML_RESTRICT vy,
1314                             int                        nr,
1315                             int                        nc) {
1316    constexpr int qk = QK_K;
1317    const int     nb = n / qk;
1318
1319    constexpr int ncols_interleaved = 8;
1320    constexpr int blocklen          = 4;
1321
1322    assert(n % qk == 0);
1323    assert(nc % ncols_interleaved == 0);
1324
1325    UNUSED(nb);
1326    UNUSED(ncols_interleaved);
1327    UNUSED(blocklen);
1328
1329#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
1330    constexpr int    col_groups = ncols_interleaved / 4;
1331    const uint8x16_t m4b        = vdupq_n_u8(0x0f);
1332    const uint8x16_t mask_lo    = vdupq_n_u8(0x03);
1333    const uint8x16_t mask_hi    = vdupq_n_u8(0x30);
1334
1335    // 1x8 tile = 2 x 4
1336    float32x4_t acc_f32[2];
1337
1338    const block_q8_K * GGML_RESTRICT q8_ptr = (const block_q8_K *) vy;
1339
1340    for (int x = 0; x < nc / ncols_interleaved; x++) {
1341        const block_q6_Kx8 * GGML_RESTRICT q6_ptr = (const block_q6_Kx8 *) vx + (x * nb);
1342
1343        for (int i = 0; i < col_groups; i++) {
1344            acc_f32[i] = vdupq_n_f32(0);
1345        }
1346
1347        for (int b = 0; b < nb; b++) {
1348            float32x4_t q6_d_0     = vcvt_f32_f16(vld1_f16((const __fp16 *) q6_ptr[b].d));      // d0 d1 d2 d3
1349            float32x4_t q6_d_1     = vcvt_f32_f16(vld1_f16((const __fp16 *) q6_ptr[b].d + 4));  // d4 d5 d6 d7
1350            float32x4_t q8_d       = vdupq_n_f32(q8_ptr[b].d);
1351            float32x4_t sb_scale_0 = vmulq_f32(q6_d_0, q8_d);
1352            float32x4_t sb_scale_1 = vmulq_f32(q6_d_1, q8_d);
1353
1354            int32x4_t acc[col_groups];
1355            for (int i = 0; i < col_groups; i++) {
1356                acc[i] = vdupq_n_s32(0);
1357            }
1358
1359            // Load all 16 scales once and widen to int16 (Q6_K has 16 scales per block)
1360            // Reused for bias and dequantization later
1361            int16_t q6_scales[16 * 8];
1362            for (int i = 0; i < 16; i++) {
1363                int16x8_t scales = vmovl_s8(vld1_s8(q6_ptr[b].scales + i * 8));
1364                vst1q_s16(q6_scales + i * 8, scales);
1365            }
1366
1367            // Compute bias per column using q8 bsums and preloaded scales to skip the -32 shift
1368            int32x4_t bias_lo = vdupq_n_s32(0);
1369            int32x4_t bias_hi = vdupq_n_s32(0);
1370
1371            // Load bsums in chunks of 4 to process with vectorized operations
1372            for (int i = 0; i < 16; i += 4) {
1373                int16x4_t bsums_vec   = vld1_s16(q8_ptr[b].bsums + i);
1374                int16x4_t scales_lo_0 = vld1_s16(q6_scales + (i + 0) * 8);
1375                int16x4_t scales_hi_0 = vld1_s16(q6_scales + (i + 0) * 8 + 4);
1376                int16x4_t scales_lo_1 = vld1_s16(q6_scales + (i + 1) * 8);
1377                int16x4_t scales_hi_1 = vld1_s16(q6_scales + (i + 1) * 8 + 4);
1378                int16x4_t scales_lo_2 = vld1_s16(q6_scales + (i + 2) * 8);
1379                int16x4_t scales_hi_2 = vld1_s16(q6_scales + (i + 2) * 8 + 4);
1380                int16x4_t scales_lo_3 = vld1_s16(q6_scales + (i + 3) * 8);
1381                int16x4_t scales_hi_3 = vld1_s16(q6_scales + (i + 3) * 8 + 4);
1382
1383                bias_lo = vmlal_lane_s16(bias_lo, scales_lo_0, bsums_vec, 0);
1384                bias_hi = vmlal_lane_s16(bias_hi, scales_hi_0, bsums_vec, 0);
1385                bias_lo = vmlal_lane_s16(bias_lo, scales_lo_1, bsums_vec, 1);
1386                bias_hi = vmlal_lane_s16(bias_hi, scales_hi_1, bsums_vec, 1);
1387                bias_lo = vmlal_lane_s16(bias_lo, scales_lo_2, bsums_vec, 2);
1388                bias_hi = vmlal_lane_s16(bias_hi, scales_hi_2, bsums_vec, 2);
1389                bias_lo = vmlal_lane_s16(bias_lo, scales_lo_3, bsums_vec, 3);
1390                bias_hi = vmlal_lane_s16(bias_hi, scales_hi_3, bsums_vec, 3);
1391            }
1392            bias_lo = vshlq_n_s32(bias_lo, 5);
1393            bias_hi = vshlq_n_s32(bias_hi, 5);
1394
1395            // Process two 128-value halves per superblock
1396            for (int half = 0; half < 2; half++) {
1397                const uint8_t * ql_base = q6_ptr[b].ql + half * 512;
1398                const uint8_t * qh_base = q6_ptr[b].qh + half * 256;
1399
1400                // A subblock (sb) is a set of weights that share the scale
1401                // Since q6_K scales are per 16 elements
1402                // num sbs -> 256 elements / (16 elements/scale * 2 elements/byte * 2 halves)
1403                for (int sb = 0; sb < QK_K / 64; sb++) {
1404                    const int8_t * q8_base_l = q8_ptr[b].qs + half * 128 + sb * 16;
1405                    const int8_t * q8_base_h = q8_base_l + 64;
1406
1407                    // Load and duplicate q8 values (each register covers four interleaved columns of q6)
1408                    int8x16_t q8_l[4];
1409                    int8x16_t q8_h[4];
1410                    for (int i = 0; i < 4; i++) {
1411                        q8_l[i] = (int8x16_t) vld1q_dup_s32((const int32_t *) (q8_base_l + i * 4));
1412                        q8_h[i] = (int8x16_t) vld1q_dup_s32((const int32_t *) (q8_base_h + i * 4));
1413                    }
1414
1415                    const int ql_off_base = sb * QK_K / 2;
1416                    const int qh_off_base = ql_off_base & 255;  // wraps after 256 bytes
1417
1418                    // Load 4 vectors at once (64 bytes each for ql_0, ql_1, qh_0, qh_1)
1419                    uint8x16x4_t q6_ql_0 = vld1q_u8_x4(ql_base + ql_off_base);
1420                    uint8x16x4_t q6_ql_1 = vld1q_u8_x4(ql_base + ql_off_base + 64);
1421                    uint8x16x4_t q6_qh_0 = vld1q_u8_x4(qh_base + qh_off_base);
1422                    uint8x16x4_t q6_qh_1 = vld1q_u8_x4(qh_base + qh_off_base + 64);
1423
1424                    // Adjust qh for subblocks 2 and 3 (shift right by 2)
1425                    if (sb > 1) {
1426                        q6_qh_0.val[0] = vshrq_n_u8(q6_qh_0.val[0], 2);
1427                        q6_qh_0.val[1] = vshrq_n_u8(q6_qh_0.val[1], 2);
1428                        q6_qh_0.val[2] = vshrq_n_u8(q6_qh_0.val[2], 2);
1429                        q6_qh_0.val[3] = vshrq_n_u8(q6_qh_0.val[3], 2);
1430                        q6_qh_1.val[0] = vshrq_n_u8(q6_qh_1.val[0], 2);
1431                        q6_qh_1.val[1] = vshrq_n_u8(q6_qh_1.val[1], 2);
1432                        q6_qh_1.val[2] = vshrq_n_u8(q6_qh_1.val[2], 2);
1433                        q6_qh_1.val[3] = vshrq_n_u8(q6_qh_1.val[3], 2);
1434                    }
1435
1436                    const uint8x16_t q6_ql[8] = { q6_ql_0.val[0], q6_ql_0.val[1], q6_ql_0.val[2], q6_ql_0.val[3],
1437                                                  q6_ql_1.val[0], q6_ql_1.val[1], q6_ql_1.val[2], q6_ql_1.val[3] };
1438                    const uint8x16_t q6_qh[8] = { q6_qh_0.val[0], q6_qh_0.val[1], q6_qh_0.val[2], q6_qh_0.val[3],
1439                                                  q6_qh_1.val[0], q6_qh_1.val[1], q6_qh_1.val[2], q6_qh_1.val[3] };
1440
1441                    // Process column groups (0-3, 4-7)
1442                    for (int g = 0; g < col_groups; g++) {
1443                        int32x4_t sb_acc_l = vdupq_n_s32(0);
1444                        int32x4_t sb_acc_h = vdupq_n_s32(0);
1445
1446                        for (int chunk = 0; chunk < 4; chunk++) {
1447                            const int idx = chunk * 2 + g;
1448
1449                            const uint8x16_t q6_qs_l = q6_ql[idx];
1450                            const uint8x16_t q6_qs_h = q6_qh[idx];
1451
1452                            // Extract high 2 bits for upper nibble reconstruction
1453                            const uint8x16_t q6_qs_hh = vandq_u8(q6_qs_h, mask_hi);
1454
1455                            // q6 = (low4 | high2<<4), without -32 bias (handled via bsums)
1456                            const int8x16_t q6_l =
1457                                vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(q6_qs_l, m4b), vandq_u8(q6_qs_h, mask_lo), 4));
1458                            const int8x16_t q6_h = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_qs_l, 4), q6_qs_hh));
1459
1460                            sb_acc_l = vdotq_s32(sb_acc_l, q6_l, q8_l[chunk]);
1461                            sb_acc_h = vdotq_s32(sb_acc_h, q6_h, q8_h[chunk]);
1462                        }
1463
1464                        const int scale_idx_l = half * 8 + sb;
1465                        const int scale_idx_h = half * 8 + sb + 4;
1466
1467                        const int32x4_t scale_vec_l = vmovl_s16(vld1_s16(q6_scales + scale_idx_l * 8 + g * 4));
1468                        const int32x4_t scale_vec_h = vmovl_s16(vld1_s16(q6_scales + scale_idx_h * 8 + g * 4));
1469
1470                        acc[g] = vmlaq_s32(acc[g], sb_acc_l, scale_vec_l);
1471                        acc[g] = vmlaq_s32(acc[g], sb_acc_h, scale_vec_h);
1472                    }
1473                }
1474            }  // for half
1475
1476            // Bias correction
1477            acc[0] = vsubq_s32(acc[0], bias_lo);
1478            acc[1] = vsubq_s32(acc[1], bias_hi);
1479
1480            // Apply superblock scale (no mins for q6_K)
1481            // acc[g] has [c0, c1, c2, c3]
1482            float32x4_t w_0123 = vmulq_f32(vcvtq_f32_s32(acc[0]), sb_scale_0);
1483            float32x4_t w_4567 = vmulq_f32(vcvtq_f32_s32(acc[1]), sb_scale_1);
1484
1485            acc_f32[0] = vaddq_f32(acc_f32[0], w_0123);
1486            acc_f32[1] = vaddq_f32(acc_f32[1], w_4567);
1487        }  // for b
1488
1489        int base = x * ncols_interleaved;
1490        vst1q_f32(s + base, acc_f32[0]);
1491        vst1q_f32(s + base + 4, acc_f32[1]);
1492    }  // for x
1493    return;
1494#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
1495    ggml_gemv_q6_K_8x4_q8_K_generic(n, s, bs, vx, vy, nr, nc);
1496}
1497
1498void ggml_gemv_q6_K_8x8_q8_K(int                        n,
1499                             float * GGML_RESTRICT      s,
1500                             size_t                     bs,
1501                             const void * GGML_RESTRICT vx,
1502                             const void * GGML_RESTRICT vy,
1503                             int                        nr,
1504                             int                        nc) {
1505    constexpr int qk = QK_K;
1506    const int     nb = n / qk;
1507
1508    constexpr int ncols_interleaved = 8;
1509    constexpr int blocklen          = 8;
1510
1511    assert(n % qk == 0);
1512    assert(nc % ncols_interleaved == 0);
1513
1514    UNUSED(nb);
1515    UNUSED(ncols_interleaved);
1516    UNUSED(blocklen);
1517
1518#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
1519    constexpr int    col_pairs = ncols_interleaved / 2;
1520    const uint8x16_t m4b       = vdupq_n_u8(0x0f);
1521    const uint8x16_t mask_lo   = vdupq_n_u8(0x03);
1522    const uint8x16_t mask_hi   = vdupq_n_u8(0x30);
1523
1524    // 1x8 tile = 2 x 4
1525    float32x4_t acc_f32[2];
1526
1527    const block_q8_K * GGML_RESTRICT q8_ptr = (const block_q8_K *) vy;
1528
1529    for (int x = 0; x < nc / ncols_interleaved; x++) {
1530        const block_q6_Kx8 * GGML_RESTRICT q6_ptr = (const block_q6_Kx8 *) vx + (x * nb);
1531
1532        acc_f32[0] = vdupq_n_f32(0);
1533        acc_f32[1] = vdupq_n_f32(0);
1534
1535        for (int b = 0; b < nb; b++) {
1536            float32x4_t q6_d_0     = vcvt_f32_f16(vld1_f16((const __fp16 *) q6_ptr[b].d));      // d0 d1 d2 d3
1537            float32x4_t q6_d_1     = vcvt_f32_f16(vld1_f16((const __fp16 *) q6_ptr[b].d + 4));  // d4 d5 d6 d7
1538            float32x4_t q8_d       = vdupq_n_f32(q8_ptr[b].d);
1539            float32x4_t sb_scale_0 = vmulq_f32(q6_d_0, q8_d);
1540            float32x4_t sb_scale_1 = vmulq_f32(q6_d_1, q8_d);
1541
1542            int32x2_t acc[col_pairs];
1543            for (int i = 0; i < col_pairs; i++) {
1544                acc[i] = vdup_n_s32(0);
1545            }
1546
1547            // Load all 16 scales once and widen to int16 (Q6_K has 16 scales per block)
1548            // Reused for bias and dequantization later
1549            int16_t q6_scales[16 * 8];
1550            for (int i = 0; i < 16; i++) {
1551                int16x8_t scales = vmovl_s8(vld1_s8(q6_ptr[b].scales + i * 8));
1552                vst1q_s16(q6_scales + i * 8, scales);
1553            }
1554
1555            // Compute bias per column using q8 bsums and preloaded scales to skip the -32 shift
1556            int32x4_t bias_lo = vdupq_n_s32(0);
1557            int32x4_t bias_hi = vdupq_n_s32(0);
1558
1559            // Load bsums in chunks of 4 to process with vectorized operations
1560            for (int i = 0; i < 16; i += 4) {
1561                int16x4_t bsums_vec   = vld1_s16(q8_ptr[b].bsums + i);
1562                int16x4_t scales_lo_0 = vld1_s16(q6_scales + (i + 0) * 8);
1563                int16x4_t scales_hi_0 = vld1_s16(q6_scales + (i + 0) * 8 + 4);
1564                int16x4_t scales_lo_1 = vld1_s16(q6_scales + (i + 1) * 8);
1565                int16x4_t scales_hi_1 = vld1_s16(q6_scales + (i + 1) * 8 + 4);
1566                int16x4_t scales_lo_2 = vld1_s16(q6_scales + (i + 2) * 8);
1567                int16x4_t scales_hi_2 = vld1_s16(q6_scales + (i + 2) * 8 + 4);
1568                int16x4_t scales_lo_3 = vld1_s16(q6_scales + (i + 3) * 8);
1569                int16x4_t scales_hi_3 = vld1_s16(q6_scales + (i + 3) * 8 + 4);
1570
1571                bias_lo = vmlal_lane_s16(bias_lo, scales_lo_0, bsums_vec, 0);
1572                bias_hi = vmlal_lane_s16(bias_hi, scales_hi_0, bsums_vec, 0);
1573                bias_lo = vmlal_lane_s16(bias_lo, scales_lo_1, bsums_vec, 1);
1574                bias_hi = vmlal_lane_s16(bias_hi, scales_hi_1, bsums_vec, 1);
1575                bias_lo = vmlal_lane_s16(bias_lo, scales_lo_2, bsums_vec, 2);
1576                bias_hi = vmlal_lane_s16(bias_hi, scales_hi_2, bsums_vec, 2);
1577                bias_lo = vmlal_lane_s16(bias_lo, scales_lo_3, bsums_vec, 3);
1578                bias_hi = vmlal_lane_s16(bias_hi, scales_hi_3, bsums_vec, 3);
1579            }
1580            bias_lo = vshlq_n_s32(bias_lo, 5);
1581            bias_hi = vshlq_n_s32(bias_hi, 5);
1582
1583            // Process two 128-value halves per superblock
1584            for (int half = 0; half < 2; half++) {
1585                const uint8_t * ql_base = q6_ptr[b].ql + half * 512;
1586                const uint8_t * qh_base = q6_ptr[b].qh + half * 256;
1587
1588                // A subblock (sb) is a set of weights that share the scale
1589                // Since q6_K scales are per 16 elements
1590                // num sbs -> 256 elements / (16 elements/scale * 2 elements/byte * 2 halves)
1591                for (int sb = 0; sb < QK_K / 64; sb++) {
1592                    const int8_t * q8_base_l = q8_ptr[b].qs + half * 128 + sb * 16;
1593                    const int8_t * q8_base_h = q8_base_l + 64;
1594
1595                    // Load and duplicate q8 values (each register covers two interleaved columns of q6)
1596                    int8x16_t q8_l[2];
1597                    int8x16_t q8_h[2];
1598                    for (int i = 0; i < 2; i++) {
1599                        q8_l[i] = (int8x16_t) vld1q_dup_s64((const int64_t *) (q8_base_l + i * 8));
1600                        q8_h[i] = (int8x16_t) vld1q_dup_s64((const int64_t *) (q8_base_h + i * 8));
1601                    }
1602
1603                    const int ql_off_base = sb * QK_K / 2;
1604                    const int qh_off_base = ql_off_base & 255;  // wraps after 256 bytes
1605
1606                    // Load 4 vectors at once (64 bytes each for ql_0, ql_1, qh_0, qh_1)
1607                    uint8x16x4_t q6_ql_0 = vld1q_u8_x4(ql_base + ql_off_base);
1608                    uint8x16x4_t q6_ql_1 = vld1q_u8_x4(ql_base + ql_off_base + 64);
1609                    uint8x16x4_t q6_qh_0 = vld1q_u8_x4(qh_base + qh_off_base);
1610                    uint8x16x4_t q6_qh_1 = vld1q_u8_x4(qh_base + qh_off_base + 64);
1611
1612                    // Adjust qh for subblocks 2 and 3 (shift right by 2)
1613                    if (sb > 1) {
1614                        q6_qh_0.val[0] = vshrq_n_u8(q6_qh_0.val[0], 2);
1615                        q6_qh_0.val[1] = vshrq_n_u8(q6_qh_0.val[1], 2);
1616                        q6_qh_0.val[2] = vshrq_n_u8(q6_qh_0.val[2], 2);
1617                        q6_qh_0.val[3] = vshrq_n_u8(q6_qh_0.val[3], 2);
1618                        q6_qh_1.val[0] = vshrq_n_u8(q6_qh_1.val[0], 2);
1619                        q6_qh_1.val[1] = vshrq_n_u8(q6_qh_1.val[1], 2);
1620                        q6_qh_1.val[2] = vshrq_n_u8(q6_qh_1.val[2], 2);
1621                        q6_qh_1.val[3] = vshrq_n_u8(q6_qh_1.val[3], 2);
1622                    }
1623
1624                    // Process column pairs (0-1, 2-3, 4-5, 6-7)
1625                    for (int cp = 0; cp < col_pairs; cp++) {
1626                        const uint8x16_t q6_qs_cp_0_l = q6_ql_0.val[cp];
1627                        const uint8x16_t q6_qs_cp_1_l = q6_ql_1.val[cp];
1628                        const uint8x16_t q6_qs_cp_0_h = q6_qh_0.val[cp];
1629                        const uint8x16_t q6_qs_cp_1_h = q6_qh_1.val[cp];
1630
1631                        // Extract high 2 bits for upper nibble reconstruction
1632                        const uint8x16_t q6_qs_cp_0_hh = vandq_u8(q6_qs_cp_0_h, mask_hi);
1633                        const uint8x16_t q6_qs_cp_1_hh = vandq_u8(q6_qs_cp_1_h, mask_hi);
1634
1635                        // q6 = (low4 | high2<<4), without -32 bias (handled via bsums)
1636                        const int8x16_t q6_l0 = vreinterpretq_s8_u8(
1637                            vsliq_n_u8(vandq_u8(q6_qs_cp_0_l, m4b), vandq_u8(q6_qs_cp_0_h, mask_lo), 4));
1638                        const int8x16_t q6_l1 = vreinterpretq_s8_u8(
1639                            vsliq_n_u8(vandq_u8(q6_qs_cp_1_l, m4b), vandq_u8(q6_qs_cp_1_h, mask_lo), 4));
1640                        const int8x16_t q6_h0 =
1641                            vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_qs_cp_0_l, 4), q6_qs_cp_0_hh));
1642                        const int8x16_t q6_h1 =
1643                            vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_qs_cp_1_l, 4), q6_qs_cp_1_hh));
1644
1645                        int32x4_t sb_acc_l = vdupq_n_s32(0);
1646                        sb_acc_l           = vdotq_s32(sb_acc_l, q6_l0, q8_l[0]);
1647                        sb_acc_l           = vdotq_s32(sb_acc_l, q6_l1, q8_l[1]);
1648
1649                        int32x4_t sb_acc_h = vdupq_n_s32(0);
1650                        sb_acc_h           = vdotq_s32(sb_acc_h, q6_h0, q8_h[0]);
1651                        sb_acc_h           = vdotq_s32(sb_acc_h, q6_h1, q8_h[1]);
1652
1653                        // Pairwise add to get per-column sums: [col0, col1]
1654                        int32x2_t sum_l = vpadd_s32(vget_low_s32(sb_acc_l), vget_high_s32(sb_acc_l));
1655                        int32x2_t sum_h = vpadd_s32(vget_low_s32(sb_acc_h), vget_high_s32(sb_acc_h));
1656
1657                        const int scale_idx_l = half * 8 + sb;
1658                        const int scale_idx_h = half * 8 + sb + 4;
1659
1660                        // Access scales using array indexing (scales are interleaved by column)
1661                        const int32x2_t scale_vec_l = { (int32_t) q6_scales[scale_idx_l * 8 + cp * 2],
1662                                                        (int32_t) q6_scales[scale_idx_l * 8 + cp * 2 + 1] };
1663                        const int32x2_t scale_vec_h = { (int32_t) q6_scales[scale_idx_h * 8 + cp * 2],
1664                                                        (int32_t) q6_scales[scale_idx_h * 8 + cp * 2 + 1] };
1665
1666                        // Accumulate scaled results
1667                        acc[cp] = vmla_s32(acc[cp], sum_l, scale_vec_l);
1668                        acc[cp] = vmla_s32(acc[cp], sum_h, scale_vec_h);
1669                    }
1670                }
1671            }  // for half
1672
1673            // Bias correction
1674            acc[0] = vsub_s32(acc[0], vget_low_s32(bias_lo));
1675            acc[1] = vsub_s32(acc[1], vget_high_s32(bias_lo));
1676            acc[2] = vsub_s32(acc[2], vget_low_s32(bias_hi));
1677            acc[3] = vsub_s32(acc[3], vget_high_s32(bias_hi));
1678
1679            // Apply superblock scale (no mins for q6_K)
1680            // acc[cp] has [c0, c1]
1681            float32x2_t w_01 = vmul_f32(vcvt_f32_s32(acc[0]), vget_low_f32(sb_scale_0));
1682            float32x2_t w_23 = vmul_f32(vcvt_f32_s32(acc[1]), vget_high_f32(sb_scale_0));
1683            float32x2_t w_45 = vmul_f32(vcvt_f32_s32(acc[2]), vget_low_f32(sb_scale_1));
1684            float32x2_t w_67 = vmul_f32(vcvt_f32_s32(acc[3]), vget_high_f32(sb_scale_1));
1685
1686            acc_f32[0] = vaddq_f32(acc_f32[0], vcombine_f32(w_01, w_23));
1687            acc_f32[1] = vaddq_f32(acc_f32[1], vcombine_f32(w_45, w_67));
1688        }  // for b
1689
1690        int base = x * ncols_interleaved;
1691        vst1q_f32(s + base, acc_f32[0]);
1692        vst1q_f32(s + base + 4, acc_f32[1]);
1693    }  // for x
1694    return;
1695#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
1696    ggml_gemv_q6_K_8x8_q8_K_generic(n, s, bs, vx, vy, nr, nc);
1697}
1698
1699void ggml_gemv_q8_0_4x4_q8_0(int                        n,
1700                             float * GGML_RESTRICT      s,
1701                             size_t                     bs,
1702                             const void * GGML_RESTRICT vx,
1703                             const void * GGML_RESTRICT vy,
1704                             int                        nr,
1705                             int                        nc) {
1706    const int qk                = QK8_0;
1707    const int nb                = n / qk;
1708    const int ncols_interleaved = 4;
1709    const int blocklen          = 4;
1710
1711    assert(n % qk == 0);
1712    assert(nc % ncols_interleaved == 0);
1713
1714    UNUSED(nb);
1715    UNUSED(ncols_interleaved);
1716    UNUSED(blocklen);
1717
1718#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
1719    const block_q8_0x4 * b_ptr = (const block_q8_0x4 *) vx;
1720
1721    for (int c = 0; c < nc; c += ncols_interleaved) {
1722        const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
1723        float32x4_t        acc   = vdupq_n_f32(0);
1724        for (int b = 0; b < nb; b++) {
1725            int8x16x4_t b_low  = vld1q_s8_x4((const int8_t *) b_ptr->qs);
1726            int8x16x4_t b_high = vld1q_s8_x4((const int8_t *) b_ptr->qs + 64);
1727            float16x4_t bd     = vld1_f16((const __fp16 *) b_ptr->d);
1728
1729            int8x16x2_t a  = vld1q_s8_x2(a_ptr->qs);
1730            float16x4_t ad = vld1_dup_f16((const __fp16 *) &a_ptr->d);
1731
1732            int32x4_t ret = vdupq_n_s32(0);
1733
1734            ret = vdotq_laneq_s32(ret, b_low.val[0], a.val[0], 0);
1735            ret = vdotq_laneq_s32(ret, b_low.val[1], a.val[0], 1);
1736            ret = vdotq_laneq_s32(ret, b_low.val[2], a.val[0], 2);
1737            ret = vdotq_laneq_s32(ret, b_low.val[3], a.val[0], 3);
1738
1739            ret = vdotq_laneq_s32(ret, b_high.val[0], a.val[1], 0);
1740            ret = vdotq_laneq_s32(ret, b_high.val[1], a.val[1], 1);
1741            ret = vdotq_laneq_s32(ret, b_high.val[2], a.val[1], 2);
1742            ret = vdotq_laneq_s32(ret, b_high.val[3], a.val[1], 3);
1743
1744            acc = vfmaq_f32(acc, vcvtq_f32_s32(ret), vmulq_f32(vcvt_f32_f16(ad), vcvt_f32_f16(bd)));
1745            a_ptr++;
1746            b_ptr++;
1747        }
1748        vst1q_f32(s, acc);
1749        s += ncols_interleaved;
1750    }
1751    return;
1752
1753#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
1754    ggml_gemv_q8_0_4x4_q8_0_generic(n, s, bs, vx, vy, nr, nc);
1755}
1756
1757void ggml_gemv_q8_0_4x8_q8_0(int                        n,
1758                             float * GGML_RESTRICT      s,
1759                             size_t                     bs,
1760                             const void * GGML_RESTRICT vx,
1761                             const void * GGML_RESTRICT vy,
1762                             int                        nr,
1763                             int                        nc) {
1764    const int qk                = QK8_0;
1765    const int nb                = n / qk;
1766    const int ncols_interleaved = 4;
1767    const int blocklen          = 8;
1768
1769    assert(n % qk == 0);
1770    assert(nc % ncols_interleaved == 0);
1771
1772    UNUSED(nb);
1773    UNUSED(ncols_interleaved);
1774    UNUSED(blocklen);
1775
1776#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
1777    const block_q8_0x4 * b_ptr = (const block_q8_0x4 *) vx;
1778
1779    for (int c = 0; c < nc; c += ncols_interleaved) {
1780        const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
1781        float32x4_t        acc   = vdupq_n_f32(0);
1782
1783        for (int b = 0; b < nb; b++) {
1784            int8x16x4_t b_low  = vld1q_s8_x4((const int8_t *) b_ptr->qs);
1785            int8x16x4_t b_high = vld1q_s8_x4((const int8_t *) b_ptr->qs + 64);
1786            float16x4_t bd     = vld1_f16((const __fp16 *) b_ptr->d);
1787
1788            int8x8x4_t  a_chunks = vld1_s8_x4(a_ptr->qs);
1789            int8x16_t   a0       = vcombine_s8(a_chunks.val[0], a_chunks.val[0]);
1790            int8x16_t   a1       = vcombine_s8(a_chunks.val[1], a_chunks.val[1]);
1791            int8x16_t   a2       = vcombine_s8(a_chunks.val[2], a_chunks.val[2]);
1792            int8x16_t   a3       = vcombine_s8(a_chunks.val[3], a_chunks.val[3]);
1793            float16x4_t ad       = vld1_dup_f16((const __fp16 *) &a_ptr->d);
1794
1795            int32x4_t ret0 = vdupq_n_s32(0);
1796            int32x4_t ret1 = vdupq_n_s32(0);
1797
1798            // 0..7
1799            ret0 = vdotq_s32(ret0, b_low.val[0], a0);
1800            ret1 = vdotq_s32(ret1, b_low.val[1], a0);
1801            // 8..15
1802            ret0 = vdotq_s32(ret0, b_low.val[2], a1);
1803            ret1 = vdotq_s32(ret1, b_low.val[3], a1);
1804            // 16..23
1805            ret0 = vdotq_s32(ret0, b_high.val[0], a2);
1806            ret1 = vdotq_s32(ret1, b_high.val[1], a2);
1807            // 24..31
1808            ret0 = vdotq_s32(ret0, b_high.val[2], a3);
1809            ret1 = vdotq_s32(ret1, b_high.val[3], a3);
1810
1811            int32x4_t ret = vpaddq_s32(ret0, ret1);
1812
1813            acc = vfmaq_f32(acc, vcvtq_f32_s32(ret), vmulq_f32(vcvt_f32_f16(ad), vcvt_f32_f16(bd)));
1814            a_ptr++;
1815            b_ptr++;
1816        }
1817        vst1q_f32(s, acc);
1818        s += ncols_interleaved;
1819    }
1820    return;
1821
1822#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
1823    ggml_gemv_q8_0_4x8_q8_0_generic(n, s, bs, vx, vy, nr, nc);
1824}
1825
1826void ggml_gemm_q4_0_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
1827    const int qk = QK8_0;
1828    const int nb = n / qk;
1829    const int ncols_interleaved = 4;
1830    const int blocklen = 4;
1831
1832    assert (n % qk == 0);
1833    assert (nr % 4 == 0);
1834    assert (nc % ncols_interleaved == 0);
1835
1836    UNUSED(s);
1837    UNUSED(bs);
1838    UNUSED(vx);
1839    UNUSED(vy);
1840    UNUSED(nr);
1841    UNUSED(nc);
1842    UNUSED(nb);
1843    UNUSED(ncols_interleaved);
1844    UNUSED(blocklen);
1845
1846#if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
1847    const void * b_ptr = vx;
1848    const void * a_ptr = vy;
1849    float * res_ptr = s;
1850    size_t res_stride = bs * sizeof(float);
1851
1852    __asm__ __volatile__(
1853        "mov x10, %x[nr]\n"
1854        "mov x9, #0x88\n"
1855        "cmp x10, #0x10\n"
1856        "mul x9, %x[nb], x9\n"
1857        "blt 4f\n"
1858        "1:"  // Row loop
1859        "add x28, %x[b_ptr], #0x8\n"
1860        "mov x27, %x[nc]\n"
1861        "add x26, %x[res_ptr], %x[res_stride], LSL #4\n"
1862        "2:"  // Column loop
1863        "add x25, %x[a_ptr], #0x8\n"
1864        "movi v15.16b, #0x0\n"
1865        "movi v19.16b, #0x0\n"
1866        "mov x24, %x[nb]\n"
1867        "add x23, x25, x9\n"
1868        "movi v18.16b, #0x0\n"
1869        "movi v14.16b, #0x0\n"
1870        "add x22, x23, x9\n"
1871        "movi v11.16b, #0x0\n"
1872        "movi v13.16b, #0x0\n"
1873        "add x21, x22, x9\n"
1874        "movi v23.16b, #0x0\n"
1875        "movi v16.16b, #0x0\n"
1876        "movi v25.16b, #0x0\n"
1877        "movi v7.16b, #0x0\n"
1878        "movi v0.16b, #0x0\n"
1879        "movi v4.16b, #0x0\n"
1880        "movi v5.16b, #0x0\n"
1881        "movi v21.16b, #0x0\n"
1882        "movi v8.16b, #0x0\n"
1883        "movi v1.16b, #0x0\n"
1884        "3:"  // Block loop
1885        "ldr q3, [x28, #0x0]\n"
1886        "ldr q31, [x25, #0x0]\n"
1887        "movi v28.16b, #0x4\n"
1888        "movi v10.4s, #0x0\n"
1889        "ldr q22, [x28, #0x10]\n"
1890        "ldr q6, [x25, #0x10]\n"
1891        "movi v29.4s, #0x0\n"
1892        "movi v9.4s, #0x0\n"
1893        "ldr q27, [x28, #0x20]\n"
1894        "ldr q30, [x28, #0x30]\n"
1895        "movi v20.4s, #0x0\n"
1896        "movi v24.16b, #0xf0\n"
1897        "ldr d2, [x25, #-0x8]\n"
1898        "ldr d26, [x23, #-0x8]\n"
1899        "sshl v12.16b, v3.16b, v28.16b\n"
1900        "sub x20, x28, #0x8\n"
1901        "ldr d17, [x20, #0x0]\n"
1902        "and v3.16b, v3.16b, v24.16b\n"
1903        "subs x24, x24, #0x1\n"
1904        "add x28, x28, #0x48\n"
1905        ".inst 0x4f9fe18a  // sdot v10.4s, v12.16b, v31.4b[0]\n"
1906        ".inst 0x4fbfe19d  // sdot v29.4s, v12.16b, v31.4b[1]\n"
1907        ".inst 0x4f9fe989  // sdot v9.4s, v12.16b, v31.4b[2]\n"
1908        ".inst 0x4fbfe994  // sdot v20.4s, v12.16b, v31.4b[3]\n"
1909        "sshl v31.16b, v22.16b, v28.16b\n"
1910        "and v22.16b, v22.16b, v24.16b\n"
1911        "fcvtl v17.4s, v17.4h\n"
1912        "fcvtl v2.4s, v2.4h\n"
1913        "fcvtl v26.4s, v26.4h\n"
1914        ".inst 0x4f86e3ea  // sdot v10.4s, v31.16b, v6.4b[0]\n"
1915        ".inst 0x4fa6e3fd  // sdot v29.4s, v31.16b, v6.4b[1]\n"
1916        ".inst 0x4f86ebe9  // sdot v9.4s, v31.16b, v6.4b[2]\n"
1917        ".inst 0x4fa6ebf4  // sdot v20.4s, v31.16b, v6.4b[3]\n"
1918        "sshl v6.16b, v27.16b, v28.16b\n"
1919        "sshl v28.16b, v30.16b, v28.16b\n"
1920        "and v27.16b, v27.16b, v24.16b\n"
1921        "and v30.16b, v30.16b, v24.16b\n"
1922        "ldr q24, [x25, #0x20]\n"
1923        ".inst 0x4f98e0ca  // sdot v10.4s, v6.16b, v24.4b[0]\n"
1924        ".inst 0x4fb8e0dd  // sdot v29.4s, v6.16b, v24.4b[1]\n"
1925        ".inst 0x4f98e8c9  // sdot v9.4s, v6.16b, v24.4b[2]\n"
1926        ".inst 0x4fb8e8d4  // sdot v20.4s, v6.16b, v24.4b[3]\n"
1927        "ldr q24, [x25, #0x30]\n"
1928        ".inst 0x4f98e38a  // sdot v10.4s, v28.16b, v24.4b[0]\n"
1929        ".inst 0x4fb8e39d  // sdot v29.4s, v28.16b, v24.4b[1]\n"
1930        ".inst 0x4f98eb89  // sdot v9.4s, v28.16b, v24.4b[2]\n"
1931        ".inst 0x4fb8eb94  // sdot v20.4s, v28.16b, v24.4b[3]\n"
1932        "ldr q24, [x25, #0x40]\n"
1933        ".inst 0x4f98e06a  // sdot v10.4s, v3.16b, v24.4b[0]\n"
1934        ".inst 0x4fb8e07d  // sdot v29.4s, v3.16b, v24.4b[1]\n"
1935        ".inst 0x4f98e869  // sdot v9.4s, v3.16b, v24.4b[2]\n"
1936        ".inst 0x4fb8e874  // sdot v20.4s, v3.16b, v24.4b[3]\n"
1937        "ldr q24, [x25, #0x50]\n"
1938        ".inst 0x4f98e2ca  // sdot v10.4s, v22.16b, v24.4b[0]\n"
1939        ".inst 0x4fb8e2dd  // sdot v29.4s, v22.16b, v24.4b[1]\n"
1940        ".inst 0x4f98eac9  // sdot v9.4s, v22.16b, v24.4b[2]\n"
1941        ".inst 0x4fb8ead4  // sdot v20.4s, v22.16b, v24.4b[3]\n"
1942        "ldr q24, [x25, #0x60]\n"
1943        ".inst 0x4f98e36a  // sdot v10.4s, v27.16b, v24.4b[0]\n"
1944        ".inst 0x4fb8e37d  // sdot v29.4s, v27.16b, v24.4b[1]\n"
1945        ".inst 0x4f98eb69  // sdot v9.4s, v27.16b, v24.4b[2]\n"
1946        ".inst 0x4fb8eb74  // sdot v20.4s, v27.16b, v24.4b[3]\n"
1947        "ldr q24, [x25, #0x70]\n"
1948        "add x25, x25, #0x88\n"
1949        ".inst 0x4f98e3ca  // sdot v10.4s, v30.16b, v24.4b[0]\n"
1950        ".inst 0x4fb8e3dd  // sdot v29.4s, v30.16b, v24.4b[1]\n"
1951        ".inst 0x4f98ebc9  // sdot v9.4s, v30.16b, v24.4b[2]\n"
1952        ".inst 0x4fb8ebd4  // sdot v20.4s, v30.16b, v24.4b[3]\n"
1953        "fmul v24.4s, v17.4s, v2.s[0]\n"
1954        "scvtf v10.4s, v10.4s, #0x4\n"
1955        "scvtf v29.4s, v29.4s, #0x4\n"
1956        "scvtf v9.4s, v9.4s, #0x4\n"
1957        "scvtf v20.4s, v20.4s, #0x4\n"
1958        "fmla v15.4s, v10.4s, v24.4s\n"
1959        "ldr q24, [x23, #0x0]\n"
1960        "fmul v10.4s, v17.4s, v2.s[1]\n"
1961        "fmla v19.4s, v29.4s, v10.4s\n"
1962        "ldr q10, [x23, #0x10]\n"
1963        "fmul v29.4s, v17.4s, v2.s[2]\n"
1964        "fmul v2.4s, v17.4s, v2.s[3]\n"
1965        "fmla v18.4s, v9.4s, v29.4s\n"
1966        "movi v9.4s, #0x0\n"
1967        "movi v29.4s, #0x0\n"
1968        ".inst 0x4f98e189  // sdot v9.4s, v12.16b, v24.4b[0]\n"
1969        ".inst 0x4fb8e19d  // sdot v29.4s, v12.16b, v24.4b[1]\n"
1970        "fmla v14.4s, v20.4s, v2.4s\n"
1971        "movi v20.4s, #0x0\n"
1972        "movi v2.4s, #0x0\n"
1973        ".inst 0x4f98e994  // sdot v20.4s, v12.16b, v24.4b[2]\n"
1974        ".inst 0x4fb8e982  // sdot v2.4s, v12.16b, v24.4b[3]\n"
1975        "ldr q24, [x23, #0x20]\n"
1976        ".inst 0x4f8ae3e9  // sdot v9.4s, v31.16b, v10.4b[0]\n"
1977        ".inst 0x4faae3fd  // sdot v29.4s, v31.16b, v10.4b[1]\n"
1978        ".inst 0x4f8aebf4  // sdot v20.4s, v31.16b, v10.4b[2]\n"
1979        ".inst 0x4faaebe2  // sdot v2.4s, v31.16b, v10.4b[3]\n"
1980        "ldr q10, [x23, #0x30]\n"
1981        ".inst 0x4f98e0c9  // sdot v9.4s, v6.16b, v24.4b[0]\n"
1982        ".inst 0x4fb8e0dd  // sdot v29.4s, v6.16b, v24.4b[1]\n"
1983        ".inst 0x4f98e8d4  // sdot v20.4s, v6.16b, v24.4b[2]\n"
1984        ".inst 0x4fb8e8c2  // sdot v2.4s, v6.16b, v24.4b[3]\n"
1985        "ldr q24, [x23, #0x40]\n"
1986        ".inst 0x4f8ae389  // sdot v9.4s, v28.16b, v10.4b[0]\n"
1987        ".inst 0x4faae39d  // sdot v29.4s, v28.16b, v10.4b[1]\n"
1988        ".inst 0x4f8aeb94  // sdot v20.4s, v28.16b, v10.4b[2]\n"
1989        ".inst 0x4faaeb82  // sdot v2.4s, v28.16b, v10.4b[3]\n"
1990        "ldr q10, [x23, #0x50]\n"
1991        ".inst 0x4f98e069  // sdot v9.4s, v3.16b, v24.4b[0]\n"
1992        ".inst 0x4fb8e07d  // sdot v29.4s, v3.16b, v24.4b[1]\n"
1993        ".inst 0x4f98e874  // sdot v20.4s, v3.16b, v24.4b[2]\n"
1994        ".inst 0x4fb8e862  // sdot v2.4s, v3.16b, v24.4b[3]\n"
1995        "ldr q24, [x23, #0x60]\n"
1996        ".inst 0x4f8ae2c9  // sdot v9.4s, v22.16b, v10.4b[0]\n"
1997        ".inst 0x4faae2dd  // sdot v29.4s, v22.16b, v10.4b[1]\n"
1998        ".inst 0x4f8aead4  // sdot v20.4s, v22.16b, v10.4b[2]\n"
1999        ".inst 0x4faaeac2  // sdot v2.4s, v22.16b, v10.4b[3]\n"
2000        "ldr q10, [x23, #0x70]\n"
2001        "add x23, x23, #0x88\n"
2002        ".inst 0x4f98e369  // sdot v9.4s, v27.16b, v24.4b[0]\n"
2003        ".inst 0x4fb8e37d  // sdot v29.4s, v27.16b, v24.4b[1]\n"
2004        ".inst 0x4f98eb74  // sdot v20.4s, v27.16b, v24.4b[2]\n"
2005        ".inst 0x4fb8eb62  // sdot v2.4s, v27.16b, v24.4b[3]\n"
2006        "ldr q24, [x22, #0x0]\n"
2007        ".inst 0x4f8ae3c9  // sdot v9.4s, v30.16b, v10.4b[0]\n"
2008        ".inst 0x4faae3dd  // sdot v29.4s, v30.16b, v10.4b[1]\n"
2009        ".inst 0x4f8aebd4  // sdot v20.4s, v30.16b, v10.4b[2]\n"
2010        ".inst 0x4faaebc2  // sdot v2.4s, v30.16b, v10.4b[3]\n"
2011        "fmul v10.4s, v17.4s, v26.s[0]\n"
2012        "scvtf v9.4s, v9.4s, #0x4\n"
2013        "scvtf v29.4s, v29.4s, #0x4\n"
2014        "scvtf v20.4s, v20.4s, #0x4\n"
2015        "scvtf v2.4s, v2.4s, #0x4\n"
2016        "fmla v11.4s, v9.4s, v10.4s\n"
2017        "ldr q9, [x22, #0x10]\n"
2018        "fmul v10.4s, v17.4s, v26.s[1]\n"
2019        "fmla v13.4s, v29.4s, v10.4s\n"
2020        "ldr d29, [x22, #-0x8]\n"
2021        "fmul v10.4s, v17.4s, v26.s[2]\n"
2022        "fmul v26.4s, v17.4s, v26.s[3]\n"
2023        "fcvtl v29.4s, v29.4h\n"
2024        "fmla v23.4s, v20.4s, v10.4s\n"
2025        "movi v20.4s, #0x0\n"
2026        "movi v10.4s, #0x0\n"
2027        "fmla v16.4s, v2.4s, v26.4s\n"
2028        "movi v26.4s, #0x0\n"
2029        "movi v2.4s, #0x0\n"
2030        ".inst 0x4f98e194  // sdot v20.4s, v12.16b, v24.4b[0]\n"
2031        ".inst 0x4fb8e18a  // sdot v10.4s, v12.16b, v24.4b[1]\n"
2032        ".inst 0x4f98e99a  // sdot v26.4s, v12.16b, v24.4b[2]\n"
2033        ".inst 0x4fb8e982  // sdot v2.4s, v12.16b, v24.4b[3]\n"
2034        "ldr q24, [x22, #0x20]\n"
2035        ".inst 0x4f89e3f4  // sdot v20.4s, v31.16b, v9.4b[0]\n"
2036        ".inst 0x4fa9e3ea  // sdot v10.4s, v31.16b, v9.4b[1]\n"
2037        ".inst 0x4f89ebfa  // sdot v26.4s, v31.16b, v9.4b[2]\n"
2038        ".inst 0x4fa9ebe2  // sdot v2.4s, v31.16b, v9.4b[3]\n"
2039        "ldr q9, [x22, #0x30]\n"
2040        ".inst 0x4f98e0d4  // sdot v20.4s, v6.16b, v24.4b[0]\n"
2041        ".inst 0x4fb8e0ca  // sdot v10.4s, v6.16b, v24.4b[1]\n"
2042        ".inst 0x4f98e8da  // sdot v26.4s, v6.16b, v24.4b[2]\n"
2043        ".inst 0x4fb8e8c2  // sdot v2.4s, v6.16b, v24.4b[3]\n"
2044        "ldr q24, [x22, #0x40]\n"
2045        ".inst 0x4f89e394  // sdot v20.4s, v28.16b, v9.4b[0]\n"
2046        ".inst 0x4fa9e38a  // sdot v10.4s, v28.16b, v9.4b[1]\n"
2047        ".inst 0x4f89eb9a  // sdot v26.4s, v28.16b, v9.4b[2]\n"
2048        ".inst 0x4fa9eb82  // sdot v2.4s, v28.16b, v9.4b[3]\n"
2049        "ldr q9, [x22, #0x50]\n"
2050        ".inst 0x4f98e074  // sdot v20.4s, v3.16b, v24.4b[0]\n"
2051        ".inst 0x4fb8e06a  // sdot v10.4s, v3.16b, v24.4b[1]\n"
2052        ".inst 0x4f98e87a  // sdot v26.4s, v3.16b, v24.4b[2]\n"
2053        ".inst 0x4fb8e862  // sdot v2.4s, v3.16b, v24.4b[3]\n"
2054        "ldr q24, [x22, #0x60]\n"
2055        ".inst 0x4f89e2d4  // sdot v20.4s, v22.16b, v9.4b[0]\n"
2056        ".inst 0x4fa9e2ca  // sdot v10.4s, v22.16b, v9.4b[1]\n"
2057        ".inst 0x4f89eada  // sdot v26.4s, v22.16b, v9.4b[2]\n"
2058        ".inst 0x4fa9eac2  // sdot v2.4s, v22.16b, v9.4b[3]\n"
2059        "ldr q9, [x22, #0x70]\n"
2060        "add x22, x22, #0x88\n"
2061        ".inst 0x4f98e374  // sdot v20.4s, v27.16b, v24.4b[0]\n"
2062        ".inst 0x4fb8e36a  // sdot v10.4s, v27.16b, v24.4b[1]\n"
2063        ".inst 0x4f98eb7a  // sdot v26.4s, v27.16b, v24.4b[2]\n"
2064        ".inst 0x4fb8eb62  // sdot v2.4s, v27.16b, v24.4b[3]\n"
2065        "ldr q24, [x21, #0x0]\n"
2066        ".inst 0x4f89e3d4  // sdot v20.4s, v30.16b, v9.4b[0]\n"
2067        ".inst 0x4fa9e3ca  // sdot v10.4s, v30.16b, v9.4b[1]\n"
2068        ".inst 0x4f89ebda  // sdot v26.4s, v30.16b, v9.4b[2]\n"
2069        ".inst 0x4fa9ebc2  // sdot v2.4s, v30.16b, v9.4b[3]\n"
2070        "fmul v9.4s, v17.4s, v29.s[0]\n"
2071        "scvtf v20.4s, v20.4s, #0x4\n"
2072        "scvtf v10.4s, v10.4s, #0x4\n"
2073        "scvtf v26.4s, v26.4s, #0x4\n"
2074        "scvtf v2.4s, v2.4s, #0x4\n"
2075        "fmla v25.4s, v20.4s, v9.4s\n"
2076        "ldr q9, [x21, #0x10]\n"
2077        "fmul v20.4s, v17.4s, v29.s[1]\n"
2078        "fmla v7.4s, v10.4s, v20.4s\n"
2079        "ldr d20, [x21, #-0x8]\n"
2080        "fmul v10.4s, v17.4s, v29.s[2]\n"
2081        "fmul v29.4s, v17.4s, v29.s[3]\n"
2082        "fcvtl v20.4s, v20.4h\n"
2083        "fmla v0.4s, v26.4s, v10.4s\n"
2084        "movi v26.4s, #0x0\n"
2085        "movi v10.4s, #0x0\n"
2086        "fmla v4.4s, v2.4s, v29.4s\n"
2087        "movi v2.4s, #0x0\n"
2088        "movi v29.4s, #0x0\n"
2089        ".inst 0x4f98e19a  // sdot v26.4s, v12.16b, v24.4b[0]\n"
2090        ".inst 0x4fb8e18a  // sdot v10.4s, v12.16b, v24.4b[1]\n"
2091        ".inst 0x4f98e982  // sdot v2.4s, v12.16b, v24.4b[2]\n"
2092        ".inst 0x4fb8e99d  // sdot v29.4s, v12.16b, v24.4b[3]\n"
2093        "ldr q12, [x21, #0x20]\n"
2094        "fmul v24.4s, v17.4s, v20.s[0]\n"
2095        ".inst 0x4f89e3fa  // sdot v26.4s, v31.16b, v9.4b[0]\n"
2096        ".inst 0x4fa9e3ea  // sdot v10.4s, v31.16b, v9.4b[1]\n"
2097        ".inst 0x4f89ebe2  // sdot v2.4s, v31.16b, v9.4b[2]\n"
2098        ".inst 0x4fa9ebfd  // sdot v29.4s, v31.16b, v9.4b[3]\n"
2099        "ldr q9, [x21, #0x30]\n"
2100        "fmul v31.4s, v17.4s, v20.s[1]\n"
2101        ".inst 0x4f8ce0da  // sdot v26.4s, v6.16b, v12.4b[0]\n"
2102        ".inst 0x4face0ca  // sdot v10.4s, v6.16b, v12.4b[1]\n"
2103        ".inst 0x4f8ce8c2  // sdot v2.4s, v6.16b, v12.4b[2]\n"
2104        ".inst 0x4face8dd  // sdot v29.4s, v6.16b, v12.4b[3]\n"
2105        "ldr q12, [x21, #0x40]\n"
2106        "fmul v6.4s, v17.4s, v20.s[2]\n"
2107        "fmul v20.4s, v17.4s, v20.s[3]\n"
2108        ".inst 0x4f89e39a  // sdot v26.4s, v28.16b, v9.4b[0]\n"
2109        ".inst 0x4fa9e38a  // sdot v10.4s, v28.16b, v9.4b[1]\n"
2110        ".inst 0x4f89eb82  // sdot v2.4s, v28.16b, v9.4b[2]\n"
2111        ".inst 0x4fa9eb9d  // sdot v29.4s, v28.16b, v9.4b[3]\n"
2112        "ldr q9, [x21, #0x50]\n"
2113        ".inst 0x4f8ce07a  // sdot v26.4s, v3.16b, v12.4b[0]\n"
2114        ".inst 0x4face06a  // sdot v10.4s, v3.16b, v12.4b[1]\n"
2115        ".inst 0x4f8ce862  // sdot v2.4s, v3.16b, v12.4b[2]\n"
2116        ".inst 0x4face87d  // sdot v29.4s, v3.16b, v12.4b[3]\n"
2117        "ldr q12, [x21, #0x60]\n"
2118        ".inst 0x4f89e2da  // sdot v26.4s, v22.16b, v9.4b[0]\n"
2119        ".inst 0x4fa9e2ca  // sdot v10.4s, v22.16b, v9.4b[1]\n"
2120        ".inst 0x4f89eac2  // sdot v2.4s, v22.16b, v9.4b[2]\n"
2121        ".inst 0x4fa9eadd  // sdot v29.4s, v22.16b, v9.4b[3]\n"
2122        "ldr q17, [x21, #0x70]\n"
2123        "add x21, x21, #0x88\n"
2124        ".inst 0x4f8ce37a  // sdot v26.4s, v27.16b, v12.4b[0]\n"
2125        ".inst 0x4face36a  // sdot v10.4s, v27.16b, v12.4b[1]\n"
2126        ".inst 0x4f8ceb62  // sdot v2.4s, v27.16b, v12.4b[2]\n"
2127        ".inst 0x4faceb7d  // sdot v29.4s, v27.16b, v12.4b[3]\n"
2128        ".inst 0x4f91e3da  // sdot v26.4s, v30.16b, v17.4b[0]\n"
2129        ".inst 0x4fb1e3ca  // sdot v10.4s, v30.16b, v17.4b[1]\n"
2130        ".inst 0x4f91ebc2  // sdot v2.4s, v30.16b, v17.4b[2]\n"
2131        ".inst 0x4fb1ebdd  // sdot v29.4s, v30.16b, v17.4b[3]\n"
2132        "scvtf v26.4s, v26.4s, #0x4\n"
2133        "scvtf v10.4s, v10.4s, #0x4\n"
2134        "fmla v5.4s, v26.4s, v24.4s\n"
2135        "scvtf v2.4s, v2.4s, #0x4\n"
2136        "scvtf v29.4s, v29.4s, #0x4\n"
2137        "fmla v21.4s, v10.4s, v31.4s\n"
2138        "fmla v8.4s, v2.4s, v6.4s\n"
2139        "fmla v1.4s, v29.4s, v20.4s\n"
2140        "bgt 3b\n"
2141        "mov x20, %x[res_ptr]\n"
2142        "subs x27, x27, #0x4\n"
2143        "add %x[res_ptr], %x[res_ptr], #0x10\n"
2144        "str q15, [x20, #0x0]\n"
2145        "add x20, x20, %x[res_stride]\n"
2146        "str q19, [x20, #0x0]\n"
2147        "add x20, x20, %x[res_stride]\n"
2148        "str q18, [x20, #0x0]\n"
2149        "add x20, x20, %x[res_stride]\n"
2150        "str q14, [x20, #0x0]\n"
2151        "add x20, x20, %x[res_stride]\n"
2152        "str q11, [x20, #0x0]\n"
2153        "add x20, x20, %x[res_stride]\n"
2154        "str q13, [x20, #0x0]\n"
2155        "add x20, x20, %x[res_stride]\n"
2156        "str q23, [x20, #0x0]\n"
2157        "add x20, x20, %x[res_stride]\n"
2158        "str q16, [x20, #0x0]\n"
2159        "add x20, x20, %x[res_stride]\n"
2160        "str q25, [x20, #0x0]\n"
2161        "add x20, x20, %x[res_stride]\n"
2162        "str q7, [x20, #0x0]\n"
2163        "add x20, x20, %x[res_stride]\n"
2164        "str q0, [x20, #0x0]\n"
2165        "add x20, x20, %x[res_stride]\n"
2166        "str q4, [x20, #0x0]\n"
2167        "add x20, x20, %x[res_stride]\n"
2168        "str q5, [x20, #0x0]\n"
2169        "add x20, x20, %x[res_stride]\n"
2170        "str q21, [x20, #0x0]\n"
2171        "add x20, x20, %x[res_stride]\n"
2172        "str q8, [x20, #0x0]\n"
2173        "add x20, x20, %x[res_stride]\n"
2174        "str q1, [x20, #0x0]\n"
2175        "bne 2b\n"
2176        "mov x20, #0x4\n"
2177        "sub x10, x10, #0x10\n"
2178        "cmp x10, #0x10\n"
2179        "mov %x[res_ptr], x26\n"
2180        "madd %x[a_ptr], x20, x9, %x[a_ptr]\n"
2181        "bge 1b\n"
2182        "4:"  // Row loop skip
2183        "cbz x10, 9f\n"
2184        "5:"  // Row tail: Row loop
2185        "add x24, %x[b_ptr], #0x8\n"
2186        "mov x23, %x[nc]\n"
2187        "add x22, %x[res_ptr], %x[res_stride], LSL #2\n"
2188        "6:"  // Row tail: Column loop
2189        "movi v15.16b, #0x0\n"
2190        "movi v19.16b, #0x0\n"
2191        "add x25, %x[a_ptr], #0x8\n"
2192        "mov x21, %x[nb]\n"
2193        "movi v18.16b, #0x0\n"
2194        "movi v14.16b, #0x0\n"
2195        "7:"  // Row tail: Block loop
2196        "ldr q7, [x24, #0x0]\n"
2197        "ldr q5, [x25, #0x0]\n"
2198        "movi v9.16b, #0x4\n"
2199        "movi v4.4s, #0x0\n"
2200        "ldr q3, [x24, #0x10]\n"
2201        "ldr q2, [x25, #0x10]\n"
2202        "movi v1.4s, #0x0\n"
2203        "movi v0.4s, #0x0\n"
2204        "ldr q13, [x24, #0x20]\n"
2205        "ldr q31, [x25, #0x20]\n"
2206        "movi v30.4s, #0x0\n"
2207        "movi v29.16b, #0xf0\n"
2208        "ldr q28, [x24, #0x30]\n"
2209        "ldr q27, [x25, #0x30]\n"
2210        "sshl v20.16b, v7.16b, v9.16b\n"
2211        "sub x20, x24, #0x8\n"
2212        "ldr q26, [x25, #0x40]\n"
2213        "ldr q25, [x25, #0x50]\n"
2214        "sshl v17.16b, v3.16b, v9.16b\n"
2215        "and v7.16b, v7.16b, v29.16b\n"
2216        "ldr q24, [x25, #0x60]\n"
2217        "ldr q16, [x25, #0x70]\n"
2218        "sshl v22.16b, v13.16b, v9.16b\n"
2219        "and v3.16b, v3.16b, v29.16b\n"
2220        "ldr d21, [x20, #0x0]\n"
2221        "ldr d12, [x25, #-0x8]\n"
2222        ".inst 0x4f85e284  // sdot v4.4s, v20.16b, v5.4b[0]\n"
2223        ".inst 0x4fa5e281  // sdot v1.4s, v20.16b, v5.4b[1]\n"
2224        ".inst 0x4f85ea80  // sdot v0.4s, v20.16b, v5.4b[2]\n"
2225        ".inst 0x4fa5ea9e  // sdot v30.4s, v20.16b, v5.4b[3]\n"
2226        "sshl v9.16b, v28.16b, v9.16b\n"
2227        "subs x21, x21, #0x1\n"
2228        "and v13.16b, v13.16b, v29.16b\n"
2229        "and v28.16b, v28.16b, v29.16b\n"
2230        "add x25, x25, #0x88\n"
2231        "add x24, x24, #0x48\n"
2232        "fcvtl v21.4s, v21.4h\n"
2233        "fcvtl v12.4s, v12.4h\n"
2234        ".inst 0x4f82e224  // sdot v4.4s, v17.16b, v2.4b[0]\n"
2235        ".inst 0x4fa2e221  // sdot v1.4s, v17.16b, v2.4b[1]\n"
2236        ".inst 0x4f82ea20  // sdot v0.4s, v17.16b, v2.4b[2]\n"
2237        ".inst 0x4fa2ea3e  // sdot v30.4s, v17.16b, v2.4b[3]\n"
2238        "fmul v11.4s, v21.4s, v12.s[0]\n"
2239        "fmul v23.4s, v21.4s, v12.s[1]\n"
2240        "fmul v17.4s, v21.4s, v12.s[2]\n"
2241        ".inst 0x4f9fe2c4  // sdot v4.4s, v22.16b, v31.4b[0]\n"
2242        "fmul v6.4s, v21.4s, v12.s[3]\n"
2243        ".inst 0x4fbfe2c1  // sdot v1.4s, v22.16b, v31.4b[1]\n"
2244        ".inst 0x4f9feac0  // sdot v0.4s, v22.16b, v31.4b[2]\n"
2245        ".inst 0x4fbfeade  // sdot v30.4s, v22.16b, v31.4b[3]\n"
2246        ".inst 0x4f9be124  // sdot v4.4s, v9.16b, v27.4b[0]\n"
2247        ".inst 0x4fbbe121  // sdot v1.4s, v9.16b, v27.4b[1]\n"
2248        ".inst 0x4f9be920  // sdot v0.4s, v9.16b, v27.4b[2]\n"
2249        ".inst 0x4fbbe93e  // sdot v30.4s, v9.16b, v27.4b[3]\n"
2250        ".inst 0x4f9ae0e4  // sdot v4.4s, v7.16b, v26.4b[0]\n"
2251        ".inst 0x4fbae0e1  // sdot v1.4s, v7.16b, v26.4b[1]\n"
2252        ".inst 0x4f9ae8e0  // sdot v0.4s, v7.16b, v26.4b[2]\n"
2253        ".inst 0x4fbae8fe  // sdot v30.4s, v7.16b, v26.4b[3]\n"
2254        ".inst 0x4f99e064  // sdot v4.4s, v3.16b, v25.4b[0]\n"
2255        ".inst 0x4fb9e061  // sdot v1.4s, v3.16b, v25.4b[1]\n"
2256        ".inst 0x4f99e860  // sdot v0.4s, v3.16b, v25.4b[2]\n"
2257        ".inst 0x4fb9e87e  // sdot v30.4s, v3.16b, v25.4b[3]\n"
2258        ".inst 0x4f98e1a4  // sdot v4.4s, v13.16b, v24.4b[0]\n"
2259        ".inst 0x4fb8e1a1  // sdot v1.4s, v13.16b, v24.4b[1]\n"
2260        ".inst 0x4f98e9a0  // sdot v0.4s, v13.16b, v24.4b[2]\n"
2261        ".inst 0x4fb8e9be  // sdot v30.4s, v13.16b, v24.4b[3]\n"
2262        ".inst 0x4f90e384  // sdot v4.4s, v28.16b, v16.4b[0]\n"
2263        ".inst 0x4fb0e381  // sdot v1.4s, v28.16b, v16.4b[1]\n"
2264        ".inst 0x4f90eb80  // sdot v0.4s, v28.16b, v16.4b[2]\n"
2265        ".inst 0x4fb0eb9e  // sdot v30.4s, v28.16b, v16.4b[3]\n"
2266        "scvtf v4.4s, v4.4s, #0x4\n"
2267        "scvtf v1.4s, v1.4s, #0x4\n"
2268        "scvtf v0.4s, v0.4s, #0x4\n"
2269        "fmla v15.4s, v4.4s, v11.4s\n"
2270        "scvtf v30.4s, v30.4s, #0x4\n"
2271        "fmla v19.4s, v1.4s, v23.4s\n"
2272        "fmla v18.4s, v0.4s, v17.4s\n"
2273        "fmla v14.4s, v30.4s, v6.4s\n"
2274        "bgt 7b\n"
2275        "mov x20, %x[res_ptr]\n"
2276        "cmp x10, #0x1\n"
2277        "str q15, [x20, #0x0]\n"
2278        "add x20, x20, %x[res_stride]\n"
2279        "ble 8f\n"
2280        "cmp x10, #0x2\n"
2281        "str q19, [x20, #0x0]\n"
2282        "add x20, x20, %x[res_stride]\n"
2283        "ble 8f\n"
2284        "cmp x10, #0x3\n"
2285        "str q18, [x20, #0x0]\n"
2286        "add x20, x20, %x[res_stride]\n"
2287        "ble 8f\n"
2288        "str q14, [x20, #0x0]\n"
2289        "8:"  // Row tail: Accumulator store skip
2290        "subs x23, x23, #0x4\n"
2291        "add %x[res_ptr], %x[res_ptr], #0x10\n"
2292        "bne 6b\n"
2293        "subs x10, x10, #0x4\n"
2294        "add %x[a_ptr], %x[a_ptr], x9\n"
2295        "mov %x[res_ptr], x22\n"
2296        "bgt 5b\n"
2297        "9:"  // Row tail: Row loop skip
2298        : [a_ptr] "+&r" (a_ptr), [res_ptr] "+&r" (res_ptr)
2299        : [b_ptr] "r" (b_ptr), [nr] "r" (nr), [nb] "r" (nb), [res_stride] "r" (res_stride), [nc] "r" (nc)
2300        : "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", "x9", "x10", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28"
2301    );
2302    return;
2303#endif // #if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON)
2304    ggml_gemm_q4_0_4x4_q8_0_generic(n, s, bs, vx, vy, nr, nc);
2305}
2306
2307void ggml_gemm_q4_0_4x8_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
2308    const int qk = QK8_0;
2309    const int nb = n / qk;
2310    const int ncols_interleaved = 4;
2311    const int blocklen = 8;
2312
2313    assert (n % qk == 0);
2314    assert (nr % 4 == 0);
2315    assert (nc % ncols_interleaved == 0);
2316
2317    UNUSED(s);
2318    UNUSED(bs);
2319    UNUSED(vx);
2320    UNUSED(vy);
2321    UNUSED(nr);
2322    UNUSED(nc);
2323    UNUSED(nb);
2324    UNUSED(ncols_interleaved);
2325    UNUSED(blocklen);
2326
2327#if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
2328    const void * b_ptr = vx;
2329    const void * a_ptr = vy;
2330    float * res_ptr = s;
2331    size_t res_stride = bs * sizeof(float);
2332
2333    __asm__ __volatile__(
2334        "mov x10, %x[nr]\n"
2335        "mov x9, #0x88\n"
2336        "cmp x10, #0x10\n"
2337        "mul x9, %x[nb], x9\n"
2338        "blt 4f\n"
2339        "1:"  // Row loop
2340        "add x28, %x[b_ptr], #0x8\n"
2341        "mov x27, %x[nc]\n"
2342        "add x26, %x[res_ptr], %x[res_stride], LSL #4\n"
2343        "2:"  // Column loop
2344        "add x25, %x[a_ptr], #0x8\n"
2345        "movi v2.16b, #0x0\n"
2346        "movi v10.16b, #0x0\n"
2347        "mov x24, %x[nb]\n"
2348        "add x23, x25, x9\n"
2349        "movi v12.16b, #0x0\n"
2350        "movi v28.16b, #0x0\n"
2351        "add x22, x23, x9\n"
2352        "movi v11.16b, #0x0\n"
2353        "movi v13.16b, #0x0\n"
2354        "add x21, x22, x9\n"
2355        "movi v22.16b, #0x0\n"
2356        "movi v23.16b, #0x0\n"
2357        "movi v25.16b, #0x0\n"
2358        "movi v5.16b, #0x0\n"
2359        "movi v7.16b, #0x0\n"
2360        "movi v4.16b, #0x0\n"
2361        "movi v6.16b, #0x0\n"
2362        "movi v30.16b, #0x0\n"
2363        "movi v24.16b, #0x0\n"
2364        "movi v14.16b, #0x0\n"
2365        "3:"  // Block loop
2366        "ldr q21, [x28, #0x0]\n"
2367        "ldr q16, [x28, #0x10]\n"
2368        "movi v1.16b, #0x4\n"
2369        "movi v19.4s, #0x0\n"
2370        "ldr q27, [x25, #0x0]\n"
2371        "ldr q15, [x25, #0x10]\n"
2372        "movi v26.4s, #0x0\n"
2373        "movi v18.4s, #0x0\n"
2374        "ldr q29, [x28, #0x20]\n"
2375        "ldr q3, [x28, #0x30]\n"
2376        "movi v17.4s, #0x0\n"
2377        "movi v0.16b, #0xf0\n"
2378        "ldr d20, [x25, #-0x8]\n"
2379        "ldr d9, [x23, #-0x8]\n"
2380        "sshl v8.16b, v21.16b, v1.16b\n"
2381        "sshl v31.16b, v16.16b, v1.16b\n"
2382        "and v21.16b, v21.16b, v0.16b\n"
2383        "and v16.16b, v16.16b, v0.16b\n"
2384        "sub x20, x28, #0x8\n"
2385        "subs x24, x24, #0x1\n"
2386        "add x28, x28, #0x48\n"
2387        ".inst 0x4e88a773  // smmla v19.4s, v27.16b, v8.16b\n"
2388        ".inst 0x4e9fa77a  // smmla v26.4s, v27.16b, v31.16b\n"
2389        "ldr q27, [x25, #0x20]\n"
2390        ".inst 0x4e88a5f2  // smmla v18.4s, v15.16b, v8.16b\n"
2391        ".inst 0x4e9fa5f1  // smmla v17.4s, v15.16b, v31.16b\n"
2392        "sshl v15.16b, v29.16b, v1.16b\n"
2393        "sshl v1.16b, v3.16b, v1.16b\n"
2394        "and v29.16b, v29.16b, v0.16b\n"
2395        "and v3.16b, v3.16b, v0.16b\n"
2396        "ldr q0, [x25, #0x30]\n"
2397        "fcvtl v20.4s, v20.4h\n"
2398        ".inst 0x4e8fa773  // smmla v19.4s, v27.16b, v15.16b\n"
2399        "fcvtl v9.4s, v9.4h\n"
2400        ".inst 0x4e81a77a  // smmla v26.4s, v27.16b, v1.16b\n"
2401        "ldr q27, [x25, #0x40]\n"
2402        ".inst 0x4e8fa412  // smmla v18.4s, v0.16b, v15.16b\n"
2403        ".inst 0x4e81a411  // smmla v17.4s, v0.16b, v1.16b\n"
2404        "ldr q0, [x25, #0x50]\n"
2405        ".inst 0x4e95a773  // smmla v19.4s, v27.16b, v21.16b\n"
2406        ".inst 0x4e90a77a  // smmla v26.4s, v27.16b, v16.16b\n"
2407        "ldr q27, [x25, #0x60]\n"
2408        ".inst 0x4e95a412  // smmla v18.4s, v0.16b, v21.16b\n"
2409        ".inst 0x4e90a411  // smmla v17.4s, v0.16b, v16.16b\n"
2410        "ldr q0, [x25, #0x70]\n"
2411        "add x25, x25, #0x88\n"
2412        ".inst 0x4e9da773  // smmla v19.4s, v27.16b, v29.16b\n"
2413        ".inst 0x4e83a77a  // smmla v26.4s, v27.16b, v3.16b\n"
2414        "ldr d27, [x20, #0x0]\n"
2415        ".inst 0x4e9da412  // smmla v18.4s, v0.16b, v29.16b\n"
2416        ".inst 0x4e83a411  // smmla v17.4s, v0.16b, v3.16b\n"
2417        "fcvtl v27.4s, v27.4h\n"
2418        "uzp1 v0.2d, v19.2d, v26.2d\n"
2419        "uzp2 v26.2d, v19.2d, v26.2d\n"
2420        "fmul v19.4s, v27.4s, v20.s[0]\n"
2421        "scvtf v0.4s, v0.4s, #0x4\n"
2422        "scvtf v26.4s, v26.4s, #0x4\n"
2423        "fmla v2.4s, v0.4s, v19.4s\n"
2424        "ldr q19, [x23, #0x0]\n"
2425        "uzp1 v0.2d, v18.2d, v17.2d\n"
2426        "uzp2 v18.2d, v18.2d, v17.2d\n"
2427        "fmul v17.4s, v27.4s, v20.s[1]\n"
2428        "scvtf v0.4s, v0.4s, #0x4\n"
2429        "scvtf v18.4s, v18.4s, #0x4\n"
2430        "fmla v10.4s, v26.4s, v17.4s\n"
2431        "ldr q17, [x23, #0x10]\n"
2432        "fmul v26.4s, v27.4s, v20.s[2]\n"
2433        "fmul v20.4s, v27.4s, v20.s[3]\n"
2434        "fmla v12.4s, v0.4s, v26.4s\n"
2435        "ldr d0, [x22, #-0x8]\n"
2436        "ldr d26, [x21, #-0x8]\n"
2437        "fcvtl v0.4s, v0.4h\n"
2438        "fmla v28.4s, v18.4s, v20.4s\n"
2439        "movi v20.4s, #0x0\n"
2440        "movi v18.4s, #0x0\n"
2441        ".inst 0x4e88a674  // smmla v20.4s, v19.16b, v8.16b\n"
2442        ".inst 0x4e9fa672  // smmla v18.4s, v19.16b, v31.16b\n"
2443        "ldr q19, [x23, #0x20]\n"
2444        "fcvtl v26.4s, v26.4h\n"
2445        ".inst 0x4e8fa674  // smmla v20.4s, v19.16b, v15.16b\n"
2446        ".inst 0x4e81a672  // smmla v18.4s, v19.16b, v1.16b\n"
2447        "ldr q19, [x23, #0x40]\n"
2448        ".inst 0x4e95a674  // smmla v20.4s, v19.16b, v21.16b\n"
2449        ".inst 0x4e90a672  // smmla v18.4s, v19.16b, v16.16b\n"
2450        "ldr q19, [x23, #0x60]\n"
2451        ".inst 0x4e9da674  // smmla v20.4s, v19.16b, v29.16b\n"
2452        ".inst 0x4e83a672  // smmla v18.4s, v19.16b, v3.16b\n"
2453        "uzp1 v19.2d, v20.2d, v18.2d\n"
2454        "scvtf v19.4s, v19.4s, #0x4\n"
2455        "uzp2 v20.2d, v20.2d, v18.2d\n"
2456        "fmul v18.4s, v27.4s, v9.s[0]\n"
2457        "scvtf v20.4s, v20.4s, #0x4\n"
2458        "fmla v11.4s, v19.4s, v18.4s\n"
2459        "ldr q18, [x22, #0x0]\n"
2460        "fmul v19.4s, v27.4s, v9.s[1]\n"
2461        "fmla v13.4s, v20.4s, v19.4s\n"
2462        "movi v19.4s, #0x0\n"
2463        "movi v20.4s, #0x0\n"
2464        ".inst 0x4e88a633  // smmla v19.4s, v17.16b, v8.16b\n"
2465        ".inst 0x4e9fa634  // smmla v20.4s, v17.16b, v31.16b\n"
2466        "ldr q17, [x23, #0x30]\n"
2467        ".inst 0x4e8fa633  // smmla v19.4s, v17.16b, v15.16b\n"
2468        ".inst 0x4e81a634  // smmla v20.4s, v17.16b, v1.16b\n"
2469        "ldr q17, [x23, #0x50]\n"
2470        ".inst 0x4e95a633  // smmla v19.4s, v17.16b, v21.16b\n"
2471        ".inst 0x4e90a634  // smmla v20.4s, v17.16b, v16.16b\n"
2472        "ldr q17, [x23, #0x70]\n"
2473        "add x23, x23, #0x88\n"
2474        ".inst 0x4e9da633  // smmla v19.4s, v17.16b, v29.16b\n"
2475        ".inst 0x4e83a634  // smmla v20.4s, v17.16b, v3.16b\n"
2476        "uzp1 v17.2d, v19.2d, v20.2d\n"
2477        "scvtf v17.4s, v17.4s, #0x4\n"
2478        "uzp2 v20.2d, v19.2d, v20.2d\n"
2479        "fmul v19.4s, v27.4s, v9.s[2]\n"
2480        "fmul v9.4s, v27.4s, v9.s[3]\n"
2481        "scvtf v20.4s, v20.4s, #0x4\n"
2482        "fmla v22.4s, v17.4s, v19.4s\n"
2483        "ldr q17, [x22, #0x10]\n"
2484        "movi v19.4s, #0x0\n"
2485        ".inst 0x4e88a653  // smmla v19.4s, v18.16b, v8.16b\n"
2486        "fmla v23.4s, v20.4s, v9.4s\n"
2487        "movi v20.4s, #0x0\n"
2488        "movi v9.4s, #0x0\n"
2489        ".inst 0x4e9fa654  // smmla v20.4s, v18.16b, v31.16b\n"
2490        "ldr q18, [x22, #0x20]\n"
2491        ".inst 0x4e88a629  // smmla v9.4s, v17.16b, v8.16b\n"
2492        ".inst 0x4e8fa653  // smmla v19.4s, v18.16b, v15.16b\n"
2493        ".inst 0x4e81a654  // smmla v20.4s, v18.16b, v1.16b\n"
2494        "ldr q18, [x22, #0x40]\n"
2495        ".inst 0x4e95a653  // smmla v19.4s, v18.16b, v21.16b\n"
2496        ".inst 0x4e90a654  // smmla v20.4s, v18.16b, v16.16b\n"
2497        "ldr q18, [x22, #0x60]\n"
2498        ".inst 0x4e9da653  // smmla v19.4s, v18.16b, v29.16b\n"
2499        ".inst 0x4e83a654  // smmla v20.4s, v18.16b, v3.16b\n"
2500        "movi v18.4s, #0x0\n"
2501        ".inst 0x4e9fa632  // smmla v18.4s, v17.16b, v31.16b\n"
2502        "ldr q17, [x22, #0x30]\n"
2503        ".inst 0x4e8fa629  // smmla v9.4s, v17.16b, v15.16b\n"
2504        ".inst 0x4e81a632  // smmla v18.4s, v17.16b, v1.16b\n"
2505        "ldr q17, [x22, #0x50]\n"
2506        ".inst 0x4e95a629  // smmla v9.4s, v17.16b, v21.16b\n"
2507        ".inst 0x4e90a632  // smmla v18.4s, v17.16b, v16.16b\n"
2508        "ldr q17, [x22, #0x70]\n"
2509        "add x22, x22, #0x88\n"
2510        ".inst 0x4e9da629  // smmla v9.4s, v17.16b, v29.16b\n"
2511        ".inst 0x4e83a632  // smmla v18.4s, v17.16b, v3.16b\n"
2512        "uzp1 v17.2d, v19.2d, v20.2d\n"
2513        "uzp2 v20.2d, v19.2d, v20.2d\n"
2514        "fmul v19.4s, v27.4s, v0.s[0]\n"
2515        "scvtf v17.4s, v17.4s, #0x4\n"
2516        "scvtf v20.4s, v20.4s, #0x4\n"
2517        "fmla v25.4s, v17.4s, v19.4s\n"
2518        "ldr q19, [x21, #0x0]\n"
2519        "fmul v17.4s, v27.4s, v0.s[1]\n"
2520        "fmla v5.4s, v20.4s, v17.4s\n"
2521        "ldr q17, [x21, #0x10]\n"
2522        "uzp1 v20.2d, v9.2d, v18.2d\n"
2523        "uzp2 v9.2d, v9.2d, v18.2d\n"
2524        "fmul v18.4s, v27.4s, v0.s[2]\n"
2525        "fmul v0.4s, v27.4s, v0.s[3]\n"
2526        "scvtf v20.4s, v20.4s, #0x4\n"
2527        "scvtf v9.4s, v9.4s, #0x4\n"
2528        "fmla v7.4s, v20.4s, v18.4s\n"
2529        "movi v20.4s, #0x0\n"
2530        "movi v18.4s, #0x0\n"
2531        ".inst 0x4e88a674  // smmla v20.4s, v19.16b, v8.16b\n"
2532        ".inst 0x4e9fa672  // smmla v18.4s, v19.16b, v31.16b\n"
2533        "ldr q19, [x21, #0x20]\n"
2534        "fmla v4.4s, v9.4s, v0.4s\n"
2535        "movi v9.4s, #0x0\n"
2536        "movi v0.4s, #0x0\n"
2537        ".inst 0x4e88a629  // smmla v9.4s, v17.16b, v8.16b\n"
2538        "fmul v8.4s, v27.4s, v26.s[0]\n"
2539        ".inst 0x4e9fa620  // smmla v0.4s, v17.16b, v31.16b\n"
2540        "ldr q17, [x21, #0x30]\n"
2541        ".inst 0x4e8fa674  // smmla v20.4s, v19.16b, v15.16b\n"
2542        "fmul v31.4s, v27.4s, v26.s[1]\n"
2543        ".inst 0x4e81a672  // smmla v18.4s, v19.16b, v1.16b\n"
2544        "ldr q19, [x21, #0x40]\n"
2545        ".inst 0x4e8fa629  // smmla v9.4s, v17.16b, v15.16b\n"
2546        "fmul v15.4s, v27.4s, v26.s[2]\n"
2547        "fmul v27.4s, v27.4s, v26.s[3]\n"
2548        ".inst 0x4e81a620  // smmla v0.4s, v17.16b, v1.16b\n"
2549        "ldr q1, [x21, #0x50]\n"
2550        ".inst 0x4e95a674  // smmla v20.4s, v19.16b, v21.16b\n"
2551        ".inst 0x4e90a672  // smmla v18.4s, v19.16b, v16.16b\n"
2552        "ldr q26, [x21, #0x60]\n"
2553        ".inst 0x4e95a429  // smmla v9.4s, v1.16b, v21.16b\n"
2554        ".inst 0x4e90a420  // smmla v0.4s, v1.16b, v16.16b\n"
2555        "ldr q21, [x21, #0x70]\n"
2556        "add x21, x21, #0x88\n"
2557        ".inst 0x4e9da754  // smmla v20.4s, v26.16b, v29.16b\n"
2558        ".inst 0x4e83a752  // smmla v18.4s, v26.16b, v3.16b\n"
2559        ".inst 0x4e9da6a9  // smmla v9.4s, v21.16b, v29.16b\n"
2560        ".inst 0x4e83a6a0  // smmla v0.4s, v21.16b, v3.16b\n"
2561        "uzp1 v29.2d, v20.2d, v18.2d\n"
2562        "uzp2 v21.2d, v20.2d, v18.2d\n"
2563        "scvtf v29.4s, v29.4s, #0x4\n"
2564        "uzp1 v18.2d, v9.2d, v0.2d\n"
2565        "uzp2 v16.2d, v9.2d, v0.2d\n"
2566        "scvtf v21.4s, v21.4s, #0x4\n"
2567        "fmla v6.4s, v29.4s, v8.4s\n"
2568        "scvtf v18.4s, v18.4s, #0x4\n"
2569        "scvtf v16.4s, v16.4s, #0x4\n"
2570        "fmla v30.4s, v21.4s, v31.4s\n"
2571        "fmla v24.4s, v18.4s, v15.4s\n"
2572        "fmla v14.4s, v16.4s, v27.4s\n"
2573        "bgt 3b\n"
2574        "mov x20, %x[res_ptr]\n"
2575        "subs x27, x27, #0x4\n"
2576        "add %x[res_ptr], %x[res_ptr], #0x10\n"
2577        "str q2, [x20, #0x0]\n"
2578        "add x20, x20, %x[res_stride]\n"
2579        "str q10, [x20, #0x0]\n"
2580        "add x20, x20, %x[res_stride]\n"
2581        "str q12, [x20, #0x0]\n"
2582        "add x20, x20, %x[res_stride]\n"
2583        "str q28, [x20, #0x0]\n"
2584        "add x20, x20, %x[res_stride]\n"
2585        "str q11, [x20, #0x0]\n"
2586        "add x20, x20, %x[res_stride]\n"
2587        "str q13, [x20, #0x0]\n"
2588        "add x20, x20, %x[res_stride]\n"
2589        "str q22, [x20, #0x0]\n"
2590        "add x20, x20, %x[res_stride]\n"
2591        "str q23, [x20, #0x0]\n"
2592        "add x20, x20, %x[res_stride]\n"
2593        "str q25, [x20, #0x0]\n"
2594        "add x20, x20, %x[res_stride]\n"
2595        "str q5, [x20, #0x0]\n"
2596        "add x20, x20, %x[res_stride]\n"
2597        "str q7, [x20, #0x0]\n"
2598        "add x20, x20, %x[res_stride]\n"
2599        "str q4, [x20, #0x0]\n"
2600        "add x20, x20, %x[res_stride]\n"
2601        "str q6, [x20, #0x0]\n"
2602        "add x20, x20, %x[res_stride]\n"
2603        "str q30, [x20, #0x0]\n"
2604        "add x20, x20, %x[res_stride]\n"
2605        "str q24, [x20, #0x0]\n"
2606        "add x20, x20, %x[res_stride]\n"
2607        "str q14, [x20, #0x0]\n"
2608        "bne 2b\n"
2609        "mov x20, #0x4\n"
2610        "sub x10, x10, #0x10\n"
2611        "cmp x10, #0x10\n"
2612        "mov %x[res_ptr], x26\n"
2613        "madd %x[a_ptr], x20, x9, %x[a_ptr]\n"
2614        "bge 1b\n"
2615        "4:"  // Row loop skip
2616        "cbz x10, 9f\n"
2617        "5:"  // Row tail: Row loop
2618        "add x24, %x[b_ptr], #0x8\n"
2619        "mov x23, %x[nc]\n"
2620        "add x22, %x[res_ptr], %x[res_stride], LSL #2\n"
2621        "6:"  // Row tail: Column loop
2622        "movi v2.16b, #0x0\n"
2623        "movi v10.16b, #0x0\n"
2624        "add x25, %x[a_ptr], #0x8\n"
2625        "mov x21, %x[nb]\n"
2626        "movi v12.16b, #0x0\n"
2627        "movi v28.16b, #0x0\n"
2628        "7:"  // Row tail: Block loop
2629        "ldr q6, [x24, #0x0]\n"
2630        "ldr q5, [x24, #0x10]\n"
2631        "movi v17.16b, #0x4\n"
2632        "movi v8.4s, #0x0\n"
2633        "ldr q4, [x25, #0x0]\n"
2634        "ldr q13, [x25, #0x10]\n"
2635        "movi v27.4s, #0x0\n"
2636        "movi v0.4s, #0x0\n"
2637        "ldr q31, [x24, #0x20]\n"
2638        "ldr q14, [x24, #0x30]\n"
2639        "movi v29.4s, #0x0\n"
2640        "movi v22.16b, #0xf0\n"
2641        "ldr q11, [x25, #0x20]\n"
2642        "ldr q23, [x25, #0x30]\n"
2643        "sshl v21.16b, v6.16b, v17.16b\n"
2644        "sshl v16.16b, v5.16b, v17.16b\n"
2645        "ldr q20, [x25, #0x40]\n"
2646        "ldr q26, [x25, #0x50]\n"
2647        "and v6.16b, v6.16b, v22.16b\n"
2648        "and v5.16b, v5.16b, v22.16b\n"
2649        "ldr q25, [x25, #0x60]\n"
2650        "ldr q3, [x25, #0x70]\n"
2651        "sshl v19.16b, v31.16b, v17.16b\n"
2652        "sshl v18.16b, v14.16b, v17.16b\n"
2653        "ldr d17, [x25, #-0x8]\n"
2654        ".inst 0x4e95a488  // smmla v8.4s, v4.16b, v21.16b\n"
2655        ".inst 0x4e90a49b  // smmla v27.4s, v4.16b, v16.16b\n"
2656        "and v31.16b, v31.16b, v22.16b\n"
2657        ".inst 0x4e95a5a0  // smmla v0.4s, v13.16b, v21.16b\n"
2658        ".inst 0x4e90a5bd  // smmla v29.4s, v13.16b, v16.16b\n"
2659        "and v14.16b, v14.16b, v22.16b\n"
2660        "sub x20, x24, #0x8\n"
2661        "ldr d16, [x20, #0x0]\n"
2662        "subs x21, x21, #0x1\n"
2663        "add x25, x25, #0x88\n"
2664        "fcvtl v17.4s, v17.4h\n"
2665        "add x24, x24, #0x48\n"
2666        ".inst 0x4e93a568  // smmla v8.4s, v11.16b, v19.16b\n"
2667        ".inst 0x4e92a57b  // smmla v27.4s, v11.16b, v18.16b\n"
2668        ".inst 0x4e93a6e0  // smmla v0.4s, v23.16b, v19.16b\n"
2669        ".inst 0x4e92a6fd  // smmla v29.4s, v23.16b, v18.16b\n"
2670        "fcvtl v16.4s, v16.4h\n"
2671        ".inst 0x4e86a688  // smmla v8.4s, v20.16b, v6.16b\n"
2672        ".inst 0x4e85a69b  // smmla v27.4s, v20.16b, v5.16b\n"
2673        "fmul v23.4s, v16.4s, v17.s[0]\n"
2674        "fmul v21.4s, v16.4s, v17.s[1]\n"
2675        "fmul v1.4s, v16.4s, v17.s[2]\n"
2676        "fmul v20.4s, v16.4s, v17.s[3]\n"
2677        ".inst 0x4e86a740  // smmla v0.4s, v26.16b, v6.16b\n"
2678        ".inst 0x4e85a75d  // smmla v29.4s, v26.16b, v5.16b\n"
2679        ".inst 0x4e9fa728  // smmla v8.4s, v25.16b, v31.16b\n"
2680        ".inst 0x4e8ea73b  // smmla v27.4s, v25.16b, v14.16b\n"
2681        ".inst 0x4e9fa460  // smmla v0.4s, v3.16b, v31.16b\n"
2682        ".inst 0x4e8ea47d  // smmla v29.4s, v3.16b, v14.16b\n"
2683        "uzp1 v19.2d, v8.2d, v27.2d\n"
2684        "uzp2 v18.2d, v8.2d, v27.2d\n"
2685        "scvtf v19.4s, v19.4s, #0x4\n"
2686        "uzp1 v17.2d, v0.2d, v29.2d\n"
2687        "uzp2 v16.2d, v0.2d, v29.2d\n"
2688        "scvtf v18.4s, v18.4s, #0x4\n"
2689        "fmla v2.4s, v19.4s, v23.4s\n"
2690        "scvtf v17.4s, v17.4s, #0x4\n"
2691        "scvtf v16.4s, v16.4s, #0x4\n"
2692        "fmla v10.4s, v18.4s, v21.4s\n"
2693        "fmla v12.4s, v17.4s, v1.4s\n"
2694        "fmla v28.4s, v16.4s, v20.4s\n"
2695        "bgt 7b\n"
2696        "mov x20, %x[res_ptr]\n"
2697        "cmp x10, #0x1\n"
2698        "str q2, [x20, #0x0]\n"
2699        "add x20, x20, %x[res_stride]\n"
2700        "ble 8f\n"
2701        "cmp x10, #0x2\n"
2702        "str q10, [x20, #0x0]\n"
2703        "add x20, x20, %x[res_stride]\n"
2704        "ble 8f\n"
2705        "cmp x10, #0x3\n"
2706        "str q12, [x20, #0x0]\n"
2707        "add x20, x20, %x[res_stride]\n"
2708        "ble 8f\n"
2709        "str q28, [x20, #0x0]\n"
2710        "8:"  // Row tail: Accumulator store skip
2711        "subs x23, x23, #0x4\n"
2712        "add %x[res_ptr], %x[res_ptr], #0x10\n"
2713        "bne 6b\n"
2714        "subs x10, x10, #0x4\n"
2715        "add %x[a_ptr], %x[a_ptr], x9\n"
2716        "mov %x[res_ptr], x22\n"
2717        "bgt 5b\n"
2718        "9:"  // Row tail: Row loop skip
2719        : [a_ptr] "+&r" (a_ptr), [res_ptr] "+&r" (res_ptr)
2720        : [b_ptr] "r" (b_ptr), [nr] "r" (nr), [nb] "r" (nb), [res_stride] "r" (res_stride), [nc] "r" (nc)
2721        : "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", "x9", "x10", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28"
2722    );
2723    return;
2724#endif // #if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
2725    ggml_gemm_q4_0_4x8_q8_0_generic(n, s, bs, vx, vy, nr, nc);
2726}
2727
2728void ggml_gemm_q4_0_8x8_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
2729    const int qk = QK8_0;
2730    const int nb = n / qk;
2731    const int ncols_interleaved = 8;
2732    const int blocklen = 8;
2733
2734    assert (n % qk == 0);
2735    assert (nr % 4 == 0);
2736    assert (nc % ncols_interleaved == 0);
2737
2738    UNUSED(s);
2739    UNUSED(bs);
2740    UNUSED(vx);
2741    UNUSED(vy);
2742    UNUSED(nr);
2743    UNUSED(nc);
2744    UNUSED(nb);
2745    UNUSED(ncols_interleaved);
2746    UNUSED(blocklen);
2747
2748#if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__)
2749#if defined(__ARM_FEATURE_SVE) && defined(__ARM_FEATURE_MATMUL_INT8)
2750    if (ggml_cpu_get_sve_cnt() == QK8_0) {
2751        const void * b_ptr = vx;
2752        const void * a_ptr = vy;
2753        float * res_ptr = s;
2754        size_t res_stride = bs * sizeof(float);
2755
2756        __asm__ __volatile__(
2757            "mov x20, #0x4\n"
2758            "mov x13, %x[nr]\n"
2759            "mov z28.s, #-0x4\n"
2760            "mov x12, #0x88\n"
2761            "ptrue p1.b\n"
2762            "whilelt p0.s, XZR, x20\n"
2763            "cmp x13, #0x10\n"
2764            "mul x12, %x[nb], x12\n"
2765            "blt 4f\n"
2766            "1:"  // Row loop
2767            "add x11, %x[b_ptr], #0x10\n"
2768            "mov x10, %x[nc]\n"
2769            "add x9, %x[res_ptr], %x[res_stride], LSL #4\n"
2770            "2:"  // Column loop
2771            "add x28, %x[a_ptr], #0x8\n"
2772            "mov z24.b, #0x0\n"
2773            "mov z15.b, #0x0\n"
2774            "mov x27, %x[nb]\n"
2775            "add x26, x28, x12\n"
2776            "mov z12.b, #0x0\n"
2777            "mov z0.b, #0x0\n"
2778            "add x25, x26, x12\n"
2779            "mov z13.b, #0x0\n"
2780            "mov z1.b, #0x0\n"
2781            "add x24, x25, x12\n"
2782            "mov z20.b, #0x0\n"
2783            "mov z25.b, #0x0\n"
2784            "mov z11.b, #0x0\n"
2785            "mov z16.b, #0x0\n"
2786            "mov z19.b, #0x0\n"
2787            "mov z26.b, #0x0\n"
2788            "mov z8.b, #0x0\n"
2789            "mov z29.b, #0x0\n"
2790            "mov z27.b, #0x0\n"
2791            "mov z10.b, #0x0\n"
2792            "3:"  // Block loop
2793            "ld1b { z30.b }, p1/Z, [x11]\n"
2794            "ld1b { z21.b }, p1/Z, [x11, #1, MUL VL]\n"
2795            "mov z18.s, #0x0\n"
2796            "mov z7.s, #0x0\n"
2797            "ld1rqb { z3.b }, p1/Z, [x28]\n"
2798            "ld1rqb { z5.b }, p1/Z, [x28, #16]\n"
2799            "mov z9.s, #0x0\n"
2800            "mov z22.s, #0x0\n"
2801            "ld1b { z4.b }, p1/Z, [x11, #2, MUL VL]\n"
2802            "ld1b { z17.b }, p1/Z, [x11, #3, MUL VL]\n"
2803            "sub x20, x11, #0x10\n"
2804            "sub x23, x28, #0x8\n"
2805            "lsl z31.b, z30.b, #0x4\n"
2806            "lsl z6.b, z21.b, #0x4\n"
2807            "ld1h { z23.s }, p1/Z, [x20]\n"
2808            "sub x22, x26, #0x8\n"
2809            "and z30.b, z30.b, #0xf0\n"
2810            "and z21.b, z21.b, #0xf0\n"
2811            "sub x21, x25, #0x8\n"
2812            "sub x20, x24, #0x8\n"
2813            "lsl z14.b, z4.b, #0x4\n"
2814            "lsl z2.b, z17.b, #0x4\n"
2815            "subs x27, x27, #0x1\n"
2816            "add x11, x11, #0x90\n"
2817            ".inst 0x451f9872  // smmla z18.s, z3.b, z31.b\n"
2818            ".inst 0x45069867  // smmla z7.s, z3.b, z6.b\n"
2819            "ld1rqb { z3.b }, p1/Z, [x28, #32]\n"
2820            "and z4.b, z4.b, #0xf0\n"
2821            ".inst 0x451f98a9  // smmla z9.s, z5.b, z31.b\n"
2822            ".inst 0x450698b6  // smmla z22.s, z5.b, z6.b\n"
2823            "ld1rqb { z5.b }, p1/Z, [x28, #48]\n"
2824            "and z17.b, z17.b, #0xf0\n"
2825            "fcvt z23.s, p1/m, z23.h\n"
2826            ".inst 0x450e9872  // smmla z18.s, z3.b, z14.b\n"
2827            ".inst 0x45029867  // smmla z7.s, z3.b, z2.b\n"
2828            "ld1rqb { z3.b }, p1/Z, [x28, #64]\n"
2829            ".inst 0x450e98a9  // smmla z9.s, z5.b, z14.b\n"
2830            ".inst 0x450298b6  // smmla z22.s, z5.b, z2.b\n"
2831            "ld1rqb { z5.b }, p1/Z, [x28, #80]\n"
2832            "fscale z23.s, p1/m, z23.s, z28.s\n"
2833            ".inst 0x451e9872  // smmla z18.s, z3.b, z30.b\n"
2834            ".inst 0x45159867  // smmla z7.s, z3.b, z21.b\n"
2835            "ld1rqb { z3.b }, p1/Z, [x28, #96]\n"
2836            ".inst 0x451e98a9  // smmla z9.s, z5.b, z30.b\n"
2837            ".inst 0x451598b6  // smmla z22.s, z5.b, z21.b\n"
2838            "ld1rqb { z5.b }, p1/Z, [x28, #112]\n"
2839            "add x28, x28, #0x88\n"
2840            ".inst 0x45049872  // smmla z18.s, z3.b, z4.b\n"
2841            ".inst 0x45119867  // smmla z7.s, z3.b, z17.b\n"
2842            "ld1h { z3.s }, p0/Z, [x23]\n"
2843            ".inst 0x450498a9  // smmla z9.s, z5.b, z4.b\n"
2844            ".inst 0x451198b6  // smmla z22.s, z5.b, z17.b\n"
2845            "fcvt z3.s, p1/m, z3.h\n"
2846            "uzp1 z5.d, z18.d, z7.d\n"
2847            "uzp2 z18.d, z18.d, z7.d\n"
2848            "mov z3.q, z3.q[0]\n"
2849            "uzp1 z7.d, z9.d, z22.d\n"
2850            "uzp2 z22.d, z9.d, z22.d\n"
2851            "fmul z9.s, z23.s, z3.s[0]\n"
2852            "scvtf z5.s, p1/m, z5.s\n"
2853            "scvtf z18.s, p1/m, z18.s\n"
2854            "scvtf z7.s, p1/m, z7.s\n"
2855            "scvtf z22.s, p1/m, z22.s\n"
2856            "fmla z24.s, p1/M, z5.s, z9.s\n"
2857            "ld1rqb { z5.b }, p1/Z, [x26]\n"
2858            "fmul z9.s, z23.s, z3.s[1]\n"
2859            "fmla z15.s, p1/M, z18.s, z9.s\n"
2860            "ld1rqb { z18.b }, p1/Z, [x26, #16]\n"
2861            "fmul z9.s, z23.s, z3.s[2]\n"
2862            "fmul z3.s, z23.s, z3.s[3]\n"
2863            "fmla z12.s, p1/M, z7.s, z9.s\n"
2864            "mov z9.s, #0x0\n"
2865            "ld1h { z7.s }, p0/Z, [x22]\n"
2866            ".inst 0x451f98a9  // smmla z9.s, z5.b, z31.b\n"
2867            "fmla z0.s, p1/M, z22.s, z3.s\n"
2868            "mov z22.s, #0x0\n"
2869            "ld1h { z3.s }, p0/Z, [x21]\n"
2870            ".inst 0x450698b6  // smmla z22.s, z5.b, z6.b\n"
2871            "ld1rqb { z5.b }, p1/Z, [x26, #32]\n"
2872            "fcvt z7.s, p1/m, z7.h\n"
2873            "fcvt z3.s, p1/m, z3.h\n"
2874            ".inst 0x450e98a9  // smmla z9.s, z5.b, z14.b\n"
2875            ".inst 0x450298b6  // smmla z22.s, z5.b, z2.b\n"
2876            "ld1rqb { z5.b }, p1/Z, [x26, #64]\n"
2877            "mov z7.q, z7.q[0]\n"
2878            "mov z3.q, z3.q[0]\n"
2879            ".inst 0x451e98a9  // smmla z9.s, z5.b, z30.b\n"
2880            ".inst 0x451598b6  // smmla z22.s, z5.b, z21.b\n"
2881            "ld1rqb { z5.b }, p1/Z, [x26, #96]\n"
2882            ".inst 0x450498a9  // smmla z9.s, z5.b, z4.b\n"
2883            ".inst 0x451198b6  // smmla z22.s, z5.b, z17.b\n"
2884            "uzp1 z5.d, z9.d, z22.d\n"
2885            "scvtf z5.s, p1/m, z5.s\n"
2886            "uzp2 z22.d, z9.d, z22.d\n"
2887            "fmul z9.s, z23.s, z7.s[0]\n"
2888            "scvtf z22.s, p1/m, z22.s\n"
2889            "fmla z13.s, p1/M, z5.s, z9.s\n"
2890            "ld1rqb { z9.b }, p1/Z, [x25]\n"
2891            "fmul z5.s, z23.s, z7.s[1]\n"
2892            "fmla z1.s, p1/M, z22.s, z5.s\n"
2893            "mov z5.s, #0x0\n"
2894            "mov z22.s, #0x0\n"
2895            ".inst 0x451f9a45  // smmla z5.s, z18.b, z31.b\n"
2896            ".inst 0x45069a56  // smmla z22.s, z18.b, z6.b\n"
2897            "ld1rqb { z18.b }, p1/Z, [x26, #48]\n"
2898            ".inst 0x450e9a45  // smmla z5.s, z18.b, z14.b\n"
2899            ".inst 0x45029a56  // smmla z22.s, z18.b, z2.b\n"
2900            "ld1rqb { z18.b }, p1/Z, [x26, #80]\n"
2901            ".inst 0x451e9a45  // smmla z5.s, z18.b, z30.b\n"
2902            ".inst 0x45159a56  // smmla z22.s, z18.b, z21.b\n"
2903            "ld1rqb { z18.b }, p1/Z, [x26, #112]\n"
2904            "add x26, x26, #0x88\n"
2905            ".inst 0x45049a45  // smmla z5.s, z18.b, z4.b\n"
2906            ".inst 0x45119a56  // smmla z22.s, z18.b, z17.b\n"
2907            "uzp1 z18.d, z5.d, z22.d\n"
2908            "scvtf z18.s, p1/m, z18.s\n"
2909            "uzp2 z22.d, z5.d, z22.d\n"
2910            "fmul z5.s, z23.s, z7.s[2]\n"
2911            "fmul z7.s, z23.s, z7.s[3]\n"
2912            "scvtf z22.s, p1/m, z22.s\n"
2913            "fmla z20.s, p1/M, z18.s, z5.s\n"
2914            "ld1rqb { z18.b }, p1/Z, [x25, #16]\n"
2915            "ld1h { z5.s }, p0/Z, [x20]\n"
2916            "fcvt z5.s, p1/m, z5.h\n"
2917            "fmla z25.s, p1/M, z22.s, z7.s\n"
2918            "mov z22.s, #0x0\n"
2919            "mov z7.s, #0x0\n"
2920            ".inst 0x451f9936  // smmla z22.s, z9.b, z31.b\n"
2921            ".inst 0x45069927  // smmla z7.s, z9.b, z6.b\n"
2922            "ld1rqb { z9.b }, p1/Z, [x25, #32]\n"
2923            "mov z5.q, z5.q[0]\n"
2924            ".inst 0x450e9936  // smmla z22.s, z9.b, z14.b\n"
2925            ".inst 0x45029927  // smmla z7.s, z9.b, z2.b\n"
2926            "ld1rqb { z9.b }, p1/Z, [x25, #64]\n"
2927            ".inst 0x451e9936  // smmla z22.s, z9.b, z30.b\n"
2928            ".inst 0x45159927  // smmla z7.s, z9.b, z21.b\n"
2929            "ld1rqb { z9.b }, p1/Z, [x25, #96]\n"
2930            ".inst 0x45049936  // smmla z22.s, z9.b, z4.b\n"
2931            ".inst 0x45119927  // smmla z7.s, z9.b, z17.b\n"
2932            "uzp1 z9.d, z22.d, z7.d\n"
2933            "scvtf z9.s, p1/m, z9.s\n"
2934            "uzp2 z22.d, z22.d, z7.d\n"
2935            "fmul z7.s, z23.s, z3.s[0]\n"
2936            "scvtf z22.s, p1/m, z22.s\n"
2937            "fmla z11.s, p1/M, z9.s, z7.s\n"
2938            "ld1rqb { z9.b }, p1/Z, [x24]\n"
2939            "fmul z7.s, z23.s, z3.s[1]\n"
2940            "fmla z16.s, p1/M, z22.s, z7.s\n"
2941            "mov z22.s, #0x0\n"
2942            "mov z7.s, #0x0\n"
2943            ".inst 0x451f9a56  // smmla z22.s, z18.b, z31.b\n"
2944            ".inst 0x45069a47  // smmla z7.s, z18.b, z6.b\n"
2945            "ld1rqb { z18.b }, p1/Z, [x25, #48]\n"
2946            ".inst 0x450e9a56  // smmla z22.s, z18.b, z14.b\n"
2947            ".inst 0x45029a47  // smmla z7.s, z18.b, z2.b\n"
2948            "ld1rqb { z18.b }, p1/Z, [x25, #80]\n"
2949            ".inst 0x451e9a56  // smmla z22.s, z18.b, z30.b\n"
2950            ".inst 0x45159a47  // smmla z7.s, z18.b, z21.b\n"
2951            "ld1rqb { z18.b }, p1/Z, [x25, #112]\n"
2952            "add x25, x25, #0x88\n"
2953            ".inst 0x45049a56  // smmla z22.s, z18.b, z4.b\n"
2954            ".inst 0x45119a47  // smmla z7.s, z18.b, z17.b\n"
2955            "uzp1 z18.d, z22.d, z7.d\n"
2956            "scvtf z18.s, p1/m, z18.s\n"
2957            "uzp2 z7.d, z22.d, z7.d\n"
2958            "fmul z22.s, z23.s, z3.s[2]\n"
2959            "fmul z3.s, z23.s, z3.s[3]\n"
2960            "scvtf z7.s, p1/m, z7.s\n"
2961            "fmla z19.s, p1/M, z18.s, z22.s\n"
2962            "ld1rqb { z18.b }, p1/Z, [x24, #16]\n"
2963            "fmul z22.s, z23.s, z5.s[0]\n"
2964            "fmla z26.s, p1/M, z7.s, z3.s\n"
2965            "mov z3.s, #0x0\n"
2966            "mov z7.s, #0x0\n"
2967            ".inst 0x451f9923  // smmla z3.s, z9.b, z31.b\n"
2968            ".inst 0x45069927  // smmla z7.s, z9.b, z6.b\n"
2969            "ld1rqb { z9.b }, p1/Z, [x24, #32]\n"
2970            ".inst 0x450e9923  // smmla z3.s, z9.b, z14.b\n"
2971            ".inst 0x45029927  // smmla z7.s, z9.b, z2.b\n"
2972            "mov z9.s, #0x0\n"
2973            ".inst 0x451f9a49  // smmla z9.s, z18.b, z31.b\n"
2974            "mov z31.s, #0x0\n"
2975            ".inst 0x45069a5f  // smmla z31.s, z18.b, z6.b\n"
2976            "ld1rqb { z6.b }, p1/Z, [x24, #48]\n"
2977            "ld1rqb { z18.b }, p1/Z, [x24, #64]\n"
2978            ".inst 0x450e98c9  // smmla z9.s, z6.b, z14.b\n"
2979            "fmul z14.s, z23.s, z5.s[1]\n"
2980            ".inst 0x450298df  // smmla z31.s, z6.b, z2.b\n"
2981            "ld1rqb { z6.b }, p1/Z, [x24, #80]\n"
2982            "fmul z2.s, z23.s, z5.s[2]\n"
2983            "fmul z23.s, z23.s, z5.s[3]\n"
2984            ".inst 0x451e9a43  // smmla z3.s, z18.b, z30.b\n"
2985            ".inst 0x45159a47  // smmla z7.s, z18.b, z21.b\n"
2986            "ld1rqb { z5.b }, p1/Z, [x24, #96]\n"
2987            ".inst 0x451e98c9  // smmla z9.s, z6.b, z30.b\n"
2988            ".inst 0x451598df  // smmla z31.s, z6.b, z21.b\n"
2989            "ld1rqb { z18.b }, p1/Z, [x24, #112]\n"
2990            "add x24, x24, #0x88\n"
2991            ".inst 0x450498a3  // smmla z3.s, z5.b, z4.b\n"
2992            ".inst 0x451198a7  // smmla z7.s, z5.b, z17.b\n"
2993            ".inst 0x45049a49  // smmla z9.s, z18.b, z4.b\n"
2994            ".inst 0x45119a5f  // smmla z31.s, z18.b, z17.b\n"
2995            "uzp1 z18.d, z3.d, z7.d\n"
2996            "uzp2 z5.d, z3.d, z7.d\n"
2997            "scvtf z18.s, p1/m, z18.s\n"
2998            "uzp1 z6.d, z9.d, z31.d\n"
2999            "uzp2 z9.d, z9.d, z31.d\n"
3000            "scvtf z5.s, p1/m, z5.s\n"
3001            "fmla z8.s, p1/M, z18.s, z22.s\n"
3002            "scvtf z6.s, p1/m, z6.s\n"
3003            "scvtf z9.s, p1/m, z9.s\n"
3004            "fmla z29.s, p1/M, z5.s, z14.s\n"
3005            "fmla z27.s, p1/M, z6.s, z2.s\n"
3006            "fmla z10.s, p1/M, z9.s, z23.s\n"
3007            "bgt 3b\n"
3008            "mov x20, %x[res_ptr]\n"
3009            "subs x10, x10, #0x8\n"
3010            "add %x[res_ptr], %x[res_ptr], #0x20\n"
3011            "st1w { z24.s }, p1, [x20]\n"
3012            "add x20, x20, %x[res_stride]\n"
3013            "st1w { z15.s }, p1, [x20]\n"
3014            "add x20, x20, %x[res_stride]\n"
3015            "st1w { z12.s }, p1, [x20]\n"
3016            "add x20, x20, %x[res_stride]\n"
3017            "st1w { z0.s }, p1, [x20]\n"
3018            "add x20, x20, %x[res_stride]\n"
3019            "st1w { z13.s }, p1, [x20]\n"
3020            "add x20, x20, %x[res_stride]\n"
3021            "st1w { z1.s }, p1, [x20]\n"
3022            "add x20, x20, %x[res_stride]\n"
3023            "st1w { z20.s }, p1, [x20]\n"
3024            "add x20, x20, %x[res_stride]\n"
3025            "st1w { z25.s }, p1, [x20]\n"
3026            "add x20, x20, %x[res_stride]\n"
3027            "st1w { z11.s }, p1, [x20]\n"
3028            "add x20, x20, %x[res_stride]\n"
3029            "st1w { z16.s }, p1, [x20]\n"
3030            "add x20, x20, %x[res_stride]\n"
3031            "st1w { z19.s }, p1, [x20]\n"
3032            "add x20, x20, %x[res_stride]\n"
3033            "st1w { z26.s }, p1, [x20]\n"
3034            "add x20, x20, %x[res_stride]\n"
3035            "st1w { z8.s }, p1, [x20]\n"
3036            "add x20, x20, %x[res_stride]\n"
3037            "st1w { z29.s }, p1, [x20]\n"
3038            "add x20, x20, %x[res_stride]\n"
3039            "st1w { z27.s }, p1, [x20]\n"
3040            "add x20, x20, %x[res_stride]\n"
3041            "st1w { z10.s }, p1, [x20]\n"
3042            "bne 2b\n"
3043            "mov x20, #0x4\n"
3044            "sub x13, x13, #0x10\n"
3045            "cmp x13, #0x10\n"
3046            "mov %x[res_ptr], x9\n"
3047            "madd %x[a_ptr], x20, x12, %x[a_ptr]\n"
3048            "bge 1b\n"
3049            "4:"  // Row loop skip
3050            "cbz x13, 9f\n"
3051            "5:"  // Row tail: Row loop
3052            "add x25, %x[b_ptr], #0x10\n"
3053            "mov x24, %x[nc]\n"
3054            "add x23, %x[res_ptr], %x[res_stride], LSL #2\n"
3055            "6:"  // Row tail: Column loop
3056            "mov z24.b, #0x0\n"
3057            "mov z15.b, #0x0\n"
3058            "add x28, %x[a_ptr], #0x8\n"
3059            "mov x22, %x[nb]\n"
3060            "mov z12.b, #0x0\n"
3061            "mov z0.b, #0x0\n"
3062            "7:"  // Row tail: Block loop
3063            "ld1b { z3.b }, p1/Z, [x25]\n"
3064            "ld1b { z6.b }, p1/Z, [x25, #1, MUL VL]\n"
3065            "mov z2.s, #0x0\n"
3066            "mov z25.s, #0x0\n"
3067            "ld1rqb { z26.b }, p1/Z, [x28]\n"
3068            "ld1rqb { z21.b }, p1/Z, [x28, #16]\n"
3069            "mov z27.s, #0x0\n"
3070            "mov z19.s, #0x0\n"
3071            "ld1b { z29.b }, p1/Z, [x25, #2, MUL VL]\n"
3072            "ld1b { z16.b }, p1/Z, [x25, #3, MUL VL]\n"
3073            "sub x21, x25, #0x10\n"
3074            "sub x20, x28, #0x8\n"
3075            "lsl z20.b, z3.b, #0x4\n"
3076            "lsl z4.b, z6.b, #0x4\n"
3077            "ld1rqb { z10.b }, p1/Z, [x28, #32]\n"
3078            "ld1rqb { z23.b }, p1/Z, [x28, #48]\n"
3079            "and z3.b, z3.b, #0xf0\n"
3080            "and z6.b, z6.b, #0xf0\n"
3081            "ld1rqb { z11.b }, p1/Z, [x28, #64]\n"
3082            "ld1rqb { z7.b }, p1/Z, [x28, #80]\n"
3083            "lsl z8.b, z29.b, #0x4\n"
3084            "lsl z14.b, z16.b, #0x4\n"
3085            "ld1rqb { z18.b }, p1/Z, [x28, #96]\n"
3086            "ld1rqb { z30.b }, p1/Z, [x28, #112]\n"
3087            ".inst 0x45149b42  // smmla z2.s, z26.b, z20.b\n"
3088            ".inst 0x45049b59  // smmla z25.s, z26.b, z4.b\n"
3089            "and z29.b, z29.b, #0xf0\n"
3090            "ld1h { z17.s }, p1/Z, [x21]\n"
3091            ".inst 0x45149abb  // smmla z27.s, z21.b, z20.b\n"
3092            ".inst 0x45049ab3  // smmla z19.s, z21.b, z4.b\n"
3093            "and z16.b, z16.b, #0xf0\n"
3094            "ld1h { z4.s }, p0/Z, [x20]\n"
3095            "subs x22, x22, #0x1\n"
3096            "add x28, x28, #0x88\n"
3097            "fcvt z17.s, p1/m, z17.h\n"
3098            "add x25, x25, #0x90\n"
3099            ".inst 0x45089942  // smmla z2.s, z10.b, z8.b\n"
3100            ".inst 0x450e9959  // smmla z25.s, z10.b, z14.b\n"
3101            "fcvt z4.s, p1/m, z4.h\n"
3102            ".inst 0x45089afb  // smmla z27.s, z23.b, z8.b\n"
3103            ".inst 0x450e9af3  // smmla z19.s, z23.b, z14.b\n"
3104            "fscale z17.s, p1/m, z17.s, z28.s\n"
3105            "mov z4.q, z4.q[0]\n"
3106            ".inst 0x45039962  // smmla z2.s, z11.b, z3.b\n"
3107            ".inst 0x45069979  // smmla z25.s, z11.b, z6.b\n"
3108            "fmul z23.s, z17.s, z4.s[0]\n"
3109            "fmul z9.s, z17.s, z4.s[1]\n"
3110            "fmul z21.s, z17.s, z4.s[2]\n"
3111            "fmul z4.s, z17.s, z4.s[3]\n"
3112            ".inst 0x450398fb  // smmla z27.s, z7.b, z3.b\n"
3113            ".inst 0x450698f3  // smmla z19.s, z7.b, z6.b\n"
3114            ".inst 0x451d9a42  // smmla z2.s, z18.b, z29.b\n"
3115            ".inst 0x45109a59  // smmla z25.s, z18.b, z16.b\n"
3116            ".inst 0x451d9bdb  // smmla z27.s, z30.b, z29.b\n"
3117            ".inst 0x45109bd3  // smmla z19.s, z30.b, z16.b\n"
3118            "uzp1 z31.d, z2.d, z25.d\n"
3119            "uzp2 z13.d, z2.d, z25.d\n"
3120            "scvtf z31.s, p1/m, z31.s\n"
3121            "uzp1 z17.d, z27.d, z19.d\n"
3122            "uzp2 z18.d, z27.d, z19.d\n"
3123            "scvtf z13.s, p1/m, z13.s\n"
3124            "fmla z24.s, p1/M, z31.s, z23.s\n"
3125            "scvtf z17.s, p1/m, z17.s\n"
3126            "scvtf z18.s, p1/m, z18.s\n"
3127            "fmla z15.s, p1/M, z13.s, z9.s\n"
3128            "fmla z12.s, p1/M, z17.s, z21.s\n"
3129            "fmla z0.s, p1/M, z18.s, z4.s\n"
3130            "bgt 7b\n"
3131            "mov x20, %x[res_ptr]\n"
3132            "cmp x13, #0x1\n"
3133            "st1w { z24.s }, p1, [x20]\n"
3134            "add x20, x20, %x[res_stride]\n"
3135            "ble 8f\n"
3136            "cmp x13, #0x2\n"
3137            "st1w { z15.s }, p1, [x20]\n"
3138            "add x20, x20, %x[res_stride]\n"
3139            "ble 8f\n"
3140            "cmp x13, #0x3\n"
3141            "st1w { z12.s }, p1, [x20]\n"
3142            "add x20, x20, %x[res_stride]\n"
3143            "ble 8f\n"
3144            "st1w { z0.s }, p1, [x20]\n"
3145            "8:"  // Row tail: Accumulator store skip
3146            "subs x24, x24, #0x8\n"
3147            "add %x[res_ptr], %x[res_ptr], #0x20\n"
3148            "bne 6b\n"
3149            "subs x13, x13, #0x4\n"
3150            "add %x[a_ptr], %x[a_ptr], x12\n"
3151            "mov %x[res_ptr], x23\n"
3152            "bgt 5b\n"
3153            "9:"  // Row tail: Row loop skip
3154            : [a_ptr] "+&r" (a_ptr), [res_ptr] "+&r" (res_ptr)
3155            : [b_ptr] "r" (b_ptr), [nr] "r" (nr), [nb] "r" (nb), [res_stride] "r" (res_stride), [nc] "r" (nc)
3156            : "cc", "memory", "p0", "p1", "x9", "x10", "x11", "x12", "x13", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "z0", "z1", "z2", "z3", "z4", "z5", "z6", "z7", "z8", "z9", "z10", "z11", "z12", "z13", "z14", "z15", "z16", "z17", "z18", "z19", "z20", "z21", "z22", "z23", "z24", "z25", "z26", "z27", "z28", "z29", "z30", "z31"
3157        );
3158        return;
3159    }
3160#endif // #if defined(__ARM_FEATURE_SVE) && defined(__ARM_FEATURE_MATMUL_INT8)
3161
3162#endif // #if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__)
3163    ggml_gemm_q4_0_8x8_q8_0_generic(n, s, bs, vx, vy, nr, nc);
3164}
3165
3166void ggml_gemm_iq4_nl_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
3167    const int qk = QK8_0;
3168    const int nb = n / qk;
3169    const int ncols_interleaved = 4;
3170    const int blocklen = 4;
3171
3172    assert (n % qk == 0);
3173    assert (nr % 4 == 0);
3174    assert (nc % ncols_interleaved == 0);
3175
3176    UNUSED(s);
3177    UNUSED(bs);
3178    UNUSED(vx);
3179    UNUSED(vy);
3180    UNUSED(nr);
3181    UNUSED(nc);
3182    UNUSED(nb);
3183    UNUSED(ncols_interleaved);
3184    UNUSED(blocklen);
3185
3186#if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
3187    const int8x16_t kvalues = vld1q_s8(kvalues_iq4nl);
3188
3189    for (int y = 0; y < nr / 4; y++) {
3190        const block_q8_0x4 * a_ptr = (const block_q8_0x4 *) vy + (y * nb);
3191        for (int x = 0; x < nc / ncols_interleaved; x++) {
3192            const block_iq4_nlx4 * b_ptr = (const block_iq4_nlx4 *) vx + (x * nb);
3193
3194            float32x4_t sumf[4];
3195            for (int m = 0; m < 4; m++) {
3196                sumf[m] = vdupq_n_f32(0);
3197            }
3198
3199            for (int l = 0; l < nb; l++) {
3200                float32x4_t a_d = vcvt_f32_f16(vld1_f16((const float16_t *)a_ptr[l].d));
3201                float32x4_t b_d = vcvt_f32_f16(vld1_f16((const float16_t *)b_ptr[l].d));
3202
3203                int32x4_t sumi_0 = vdupq_n_s32(0);
3204                int32x4_t sumi_1 = vdupq_n_s32(0);
3205                int32x4_t sumi_2 = vdupq_n_s32(0);
3206                int32x4_t sumi_3 = vdupq_n_s32(0);
3207
3208                for (int k = 0; k < 4; k++) {
3209                    int8x16_t a_0 = vld1q_s8(a_ptr[l].qs + 16 * k + 0);
3210                    int8x16_t a_1 = vld1q_s8(a_ptr[l].qs + 16 * k + 64);
3211
3212                    uint8x16_t b = vld1q_u8(b_ptr[l].qs + 16 * k);
3213                    int8x16_t b_hi = vqtbl1q_s8(kvalues, b >> 4);
3214                    int8x16_t b_lo = vqtbl1q_s8(kvalues, b & 0xF);
3215
3216                    sumi_0 = vdotq_laneq_s32(sumi_0, b_lo, a_0, 0);
3217                    sumi_1 = vdotq_laneq_s32(sumi_1, b_lo, a_0, 1);
3218                    sumi_2 = vdotq_laneq_s32(sumi_2, b_lo, a_0, 2);
3219                    sumi_3 = vdotq_laneq_s32(sumi_3, b_lo, a_0, 3);
3220                    sumi_0 = vdotq_laneq_s32(sumi_0, b_hi, a_1, 0);
3221                    sumi_1 = vdotq_laneq_s32(sumi_1, b_hi, a_1, 1);
3222                    sumi_2 = vdotq_laneq_s32(sumi_2, b_hi, a_1, 2);
3223                    sumi_3 = vdotq_laneq_s32(sumi_3, b_hi, a_1, 3);
3224                }
3225
3226                sumf[0] = vmlaq_f32(sumf[0], vmulq_laneq_f32(b_d, a_d, 0), vcvtq_f32_s32(sumi_0));
3227                sumf[1] = vmlaq_f32(sumf[1], vmulq_laneq_f32(b_d, a_d, 1), vcvtq_f32_s32(sumi_1));
3228                sumf[2] = vmlaq_f32(sumf[2], vmulq_laneq_f32(b_d, a_d, 2), vcvtq_f32_s32(sumi_2));
3229                sumf[3] = vmlaq_f32(sumf[3], vmulq_laneq_f32(b_d, a_d, 3), vcvtq_f32_s32(sumi_3));
3230            }
3231
3232            for (int m = 0; m < 4; m++) {
3233                vst1q_f32(s + (y * 4 + m) * bs + x * 4, sumf[m]);
3234            }
3235        }
3236    }
3237    return;
3238#endif // #if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON)
3239    ggml_gemm_iq4_nl_4x4_q8_0_generic(n, s, bs, vx, vy, nr, nc);
3240}
3241
3242void ggml_gemm_mxfp4_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
3243    const int qk = QK8_0;
3244    const int nb = n / qk;
3245    const int ncols_interleaved = 4;
3246    const int blocklen = 4;
3247
3248    assert (n % qk == 0);
3249    assert (nr % 4 == 0);
3250    assert (nc % ncols_interleaved == 0);
3251
3252    UNUSED(s);
3253    UNUSED(bs);
3254    UNUSED(vx);
3255    UNUSED(vy);
3256    UNUSED(nr);
3257    UNUSED(nc);
3258    UNUSED(nb);
3259    UNUSED(ncols_interleaved);
3260    UNUSED(blocklen);
3261
3262#if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
3263    const int8x16_t kvalues = vld1q_s8(kvalues_mxfp4);
3264
3265    for (int y = 0; y < nr / 4; y++) {
3266        const block_q8_0x4 * a_ptr = (const block_q8_0x4 *) vy + (y * nb);
3267        for (int x = 0; x < nc / ncols_interleaved; x++) {
3268            const block_mxfp4x4 * b_ptr = (const block_mxfp4x4 *) vx + (x * nb);
3269
3270            float32x4_t sumf[4];
3271            for (int m = 0; m < 4; m++) {
3272                sumf[m] = vdupq_n_f32(0);
3273            }
3274
3275            for (int l = 0; l < nb; l++) {
3276                float32x4_t a_d = vcvt_f32_f16(vld1_f16((const float16_t *)a_ptr[l].d));
3277                float32x4_t b_d = {
3278                    GGML_CPU_E8M0_TO_FP32_HALF(b_ptr[l].e[0]),
3279                    GGML_CPU_E8M0_TO_FP32_HALF(b_ptr[l].e[1]),
3280                    GGML_CPU_E8M0_TO_FP32_HALF(b_ptr[l].e[2]),
3281                    GGML_CPU_E8M0_TO_FP32_HALF(b_ptr[l].e[3]),
3282                };
3283
3284                int32x4_t sumi_0 = vdupq_n_s32(0);
3285                int32x4_t sumi_1 = vdupq_n_s32(0);
3286                int32x4_t sumi_2 = vdupq_n_s32(0);
3287                int32x4_t sumi_3 = vdupq_n_s32(0);
3288
3289                for (int k = 0; k < 4; k++) {
3290                    int8x16_t a_0 = vld1q_s8(a_ptr[l].qs + 16 * k + 0);
3291                    int8x16_t a_1 = vld1q_s8(a_ptr[l].qs + 16 * k + 64);
3292
3293                    uint8x16_t b = vld1q_u8(b_ptr[l].qs + 16 * k);
3294                    int8x16_t b_hi = vqtbl1q_s8(kvalues, b >> 4);
3295                    int8x16_t b_lo = vqtbl1q_s8(kvalues, b & 0xF);
3296
3297                    sumi_0 = vdotq_laneq_s32(sumi_0, b_lo, a_0, 0);
3298                    sumi_1 = vdotq_laneq_s32(sumi_1, b_lo, a_0, 1);
3299                    sumi_2 = vdotq_laneq_s32(sumi_2, b_lo, a_0, 2);
3300                    sumi_3 = vdotq_laneq_s32(sumi_3, b_lo, a_0, 3);
3301                    sumi_0 = vdotq_laneq_s32(sumi_0, b_hi, a_1, 0);
3302                    sumi_1 = vdotq_laneq_s32(sumi_1, b_hi, a_1, 1);
3303                    sumi_2 = vdotq_laneq_s32(sumi_2, b_hi, a_1, 2);
3304                    sumi_3 = vdotq_laneq_s32(sumi_3, b_hi, a_1, 3);
3305                }
3306
3307                sumf[0] = vmlaq_f32(sumf[0], vmulq_laneq_f32(b_d, a_d, 0), vcvtq_f32_s32(sumi_0));
3308                sumf[1] = vmlaq_f32(sumf[1], vmulq_laneq_f32(b_d, a_d, 1), vcvtq_f32_s32(sumi_1));
3309                sumf[2] = vmlaq_f32(sumf[2], vmulq_laneq_f32(b_d, a_d, 2), vcvtq_f32_s32(sumi_2));
3310                sumf[3] = vmlaq_f32(sumf[3], vmulq_laneq_f32(b_d, a_d, 3), vcvtq_f32_s32(sumi_3));
3311            }
3312
3313            for (int m = 0; m < 4; m++) {
3314                vst1q_f32(s + (y * 4 + m) * bs + x * 4, sumf[m]);
3315            }
3316        }
3317    }
3318    return;
3319#endif // #if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON)
3320    ggml_gemm_mxfp4_4x4_q8_0_generic(n, s, bs, vx, vy, nr, nc);
3321}
3322
3323void ggml_gemm_q4_K_8x4_q8_K(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
3324    constexpr int qk = QK_K;
3325    const int     nb = n / qk;
3326
3327    constexpr int ncols_interleaved = 8;
3328    constexpr int blocklen          = 4;
3329
3330    assert(n % qk == 0);
3331    assert(nr % 4 == 0);
3332    assert(nc % ncols_interleaved == 0);
3333
3334    UNUSED(nb);
3335    UNUSED(ncols_interleaved);
3336    UNUSED(blocklen);
3337
3338#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
3339    constexpr int    q8_k_blocklen = 4;
3340    constexpr int    acc_size  = 2 * 4;  // 2 row pairs × 4 col pairs
3341    const uint8x16_t m4b       = vdupq_n_u8(0x0f);
3342
3343    // 8 accumulators: 2 row pairs × 4 col pairs
3344    float32x4_t acc_f32[acc_size];
3345
3346    for (int y = 0; y < nr / q8_k_blocklen; y++) {
3347        const block_q8_Kx4 * GGML_RESTRICT q8_ptr = (const block_q8_Kx4 *) vy + (y * nb);
3348
3349        for (int x = 0; x < nc / ncols_interleaved; x++) {
3350            const block_q4_Kx8 * GGML_RESTRICT q4_ptr = (const block_q4_Kx8 *) vx + (x * nb);
3351
3352            for (int i = 0; i < acc_size; i++) {
3353                acc_f32[i] = vdupq_n_f32(0);
3354            }
3355
3356            for (int b = 0; b < nb; b++) {
3357                // d4 0 1 2 3, 4 5 6 7
3358                float32x4_t q4_d_0123    = vcvt_f32_f16(vld1_f16((const __fp16 *) q4_ptr[b].d));
3359                float32x4_t q4_d_4567    = vcvt_f32_f16(vld1_f16((const __fp16 *) q4_ptr[b].d + 4));
3360                // d8 0 1 2 3
3361                float32x4_t q8_d_0123    = vld1q_f32(q8_ptr[b].d);
3362                // mins
3363                float32x4_t q4_dmin_0123 = vcvt_f32_f16(vld1_f16((const __fp16 *) q4_ptr[b].dmin));
3364                float32x4_t q4_dmin_4567 = vcvt_f32_f16(vld1_f16((const __fp16 *) q4_ptr[b].dmin + 4));
3365
3366                // Precomputation of scales and mins
3367                float32x4_t sbd_scale_0123[q8_k_blocklen];
3368                float32x4_t sbd_scale_4567[q8_k_blocklen];
3369                float32x4_t sbd_min_0123[q8_k_blocklen];
3370                float32x4_t sbd_min_4567[q8_k_blocklen];
3371
3372                sbd_scale_0123[0] = vmulq_laneq_f32(q4_d_0123, q8_d_0123, 0);
3373                sbd_scale_4567[0] = vmulq_laneq_f32(q4_d_4567, q8_d_0123, 0);
3374                sbd_min_0123[0]   = vmulq_laneq_f32(q4_dmin_0123, q8_d_0123, 0);
3375                sbd_min_4567[0]   = vmulq_laneq_f32(q4_dmin_4567, q8_d_0123, 0);
3376
3377                sbd_scale_0123[1] = vmulq_laneq_f32(q4_d_0123, q8_d_0123, 1);
3378                sbd_scale_4567[1] = vmulq_laneq_f32(q4_d_4567, q8_d_0123, 1);
3379                sbd_min_0123[1]   = vmulq_laneq_f32(q4_dmin_0123, q8_d_0123, 1);
3380                sbd_min_4567[1]   = vmulq_laneq_f32(q4_dmin_4567, q8_d_0123, 1);
3381
3382                sbd_scale_0123[2] = vmulq_laneq_f32(q4_d_0123, q8_d_0123, 2);
3383                sbd_scale_4567[2] = vmulq_laneq_f32(q4_d_4567, q8_d_0123, 2);
3384                sbd_min_0123[2]   = vmulq_laneq_f32(q4_dmin_0123, q8_d_0123, 2);
3385                sbd_min_4567[2]   = vmulq_laneq_f32(q4_dmin_4567, q8_d_0123, 2);
3386
3387                sbd_scale_0123[3] = vmulq_laneq_f32(q4_d_0123, q8_d_0123, 3);
3388                sbd_scale_4567[3] = vmulq_laneq_f32(q4_d_4567, q8_d_0123, 3);
3389                sbd_min_0123[3]   = vmulq_laneq_f32(q4_dmin_0123, q8_d_0123, 3);
3390                sbd_min_4567[3]   = vmulq_laneq_f32(q4_dmin_4567, q8_d_0123, 3);
3391
3392                // Precomputation of bsums, each vpaddq calcs all the bsums for each row
3393                const int16x8_t bsums[q8_k_blocklen] = {
3394                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 0), vld1q_s16(q8_ptr[b].bsums + 16 * 0 + 8)),
3395                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 1), vld1q_s16(q8_ptr[b].bsums + 16 * 1 + 8)),
3396                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 2), vld1q_s16(q8_ptr[b].bsums + 16 * 2 + 8)),
3397                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 3), vld1q_s16(q8_ptr[b].bsums + 16 * 3 + 8)),
3398                };
3399                int16_t bsums_arr[QK_K / 64][8];
3400                for (int q8_row = 0; q8_row < 4; q8_row++) {
3401                    vst1q_s16(bsums_arr[q8_row], bsums[q8_row]);
3402                }
3403
3404                // interleaved bias_acc: [0]->r0 0123, [1]->r1 0123, .., [4]->r0 4567, [5]->r1 4567 ..
3405                int32x4_t bias_acc[acc_size];
3406                for (int i = 0; i < acc_size; i++) {
3407                    bias_acc[i] = vdupq_n_s32(0);
3408                }
3409
3410                for (int sb = 0; sb < QK_K / 64; sb++) {
3411                    // Int accumulators for qs vecdot (4 row x 2 col quartets)
3412                    int32x4_t acc_lo[acc_size];
3413                    int32x4_t acc_hi[acc_size];
3414                    for (int i = 0; i < acc_size; i++) {
3415                        acc_lo[i] = vdupq_n_s32(0);
3416                        acc_hi[i] = vdupq_n_s32(0);
3417                    }
3418                    // Need scales for the low and high nibbles
3419                    // 2 * 12 = 24 bytes per subblock, 4 sbs -> 4 * 24 = 96 bytes total
3420                    int16x8_t q4sb_scales[2];
3421                    int16x8_t q4sb_mins[2];
3422                    for (int i = 0; i < 2; i++) {
3423                        int8_t    aux_q4sb[8];
3424                        const int offset = sb * 24 + i * 12;
3425                        decode_q_Kx8_6bit_scales(&q4_ptr[b].scales[offset], &q4sb_mins[i], aux_q4sb);
3426                        q4sb_scales[i] = vmovl_s8(vld1_s8(aux_q4sb));
3427                    }
3428
3429                    constexpr int reads_per_sb = 8;  // 8 * 16 bytes each => 32 qs * 4 rows
3430                    for (int k = 0; k < reads_per_sb; k++) {
3431                        const int8x16_t q8_blk0 = vld1q_s8(q8_ptr[b].qs + sb * 256 + 16 * k);
3432                        const int8x16_t q8_blk1 = vld1q_s8(q8_ptr[b].qs + sb * 256 + 16 * k + 128);
3433
3434                        // 0..3 & 32..35
3435                        const uint8x16_t q4_0123 = vld1q_u8(q4_ptr[b].qs + sb * QK_K + 32 * k);
3436                        const uint8x16_t q4_4567 = vld1q_u8(q4_ptr[b].qs + sb * QK_K + 32 * k + 16);
3437
3438                        const int8x16_t q4_0123_lo = vreinterpretq_s8_u8(vandq_u8(q4_0123, m4b));
3439                        const int8x16_t q4_0123_hi = vreinterpretq_s8_u8(vshrq_n_u8(q4_0123, 4));
3440
3441                        acc_lo[0] = vdotq_laneq_s32(acc_lo[0], q4_0123_lo, q8_blk0, 0);  //  0..3  r0 c0123
3442                        acc_lo[1] = vdotq_laneq_s32(acc_lo[1], q4_0123_lo, q8_blk0, 1);  //  0..3  r1 c0123
3443                        acc_lo[2] = vdotq_laneq_s32(acc_lo[2], q4_0123_lo, q8_blk0, 2);  //  0..3  r2 c0123
3444                        acc_lo[3] = vdotq_laneq_s32(acc_lo[3], q4_0123_lo, q8_blk0, 3);  //  0..3  r3 c0123
3445
3446                        acc_hi[0] = vdotq_laneq_s32(acc_hi[0], q4_0123_hi, q8_blk1, 0);  // 32..35 r0 c0123
3447                        acc_hi[1] = vdotq_laneq_s32(acc_hi[1], q4_0123_hi, q8_blk1, 1);  // 32..35 r1 c0123
3448                        acc_hi[2] = vdotq_laneq_s32(acc_hi[2], q4_0123_hi, q8_blk1, 2);  // 32..35 r2 c0123
3449                        acc_hi[3] = vdotq_laneq_s32(acc_hi[3], q4_0123_hi, q8_blk1, 3);  // 32..35 r3 c0123
3450
3451                        const int8x16_t q4_4567_lo = vreinterpretq_s8_u8(vandq_u8(q4_4567, m4b));
3452                        const int8x16_t q4_4567_hi = vreinterpretq_s8_u8(vshrq_n_u8(q4_4567, 4));
3453
3454                        acc_lo[4] = vdotq_laneq_s32(acc_lo[4], q4_4567_lo, q8_blk0, 0);  //  0..3  r0 c4567
3455                        acc_lo[5] = vdotq_laneq_s32(acc_lo[5], q4_4567_lo, q8_blk0, 1);  //  0..3  r1 c4567
3456                        acc_lo[6] = vdotq_laneq_s32(acc_lo[6], q4_4567_lo, q8_blk0, 2);  //  0..3  r2 c4567
3457                        acc_lo[7] = vdotq_laneq_s32(acc_lo[7], q4_4567_lo, q8_blk0, 3);  //  0..3  r3 c4567
3458
3459                        acc_hi[4] = vdotq_laneq_s32(acc_hi[4], q4_4567_hi, q8_blk1, 0);  // 32..35 r0 c4567
3460                        acc_hi[5] = vdotq_laneq_s32(acc_hi[5], q4_4567_hi, q8_blk1, 1);  // 32..35 r1 c4567
3461                        acc_hi[6] = vdotq_laneq_s32(acc_hi[6], q4_4567_hi, q8_blk1, 2);  // 32..35 r2 c4567
3462                        acc_hi[7] = vdotq_laneq_s32(acc_hi[7], q4_4567_hi, q8_blk1, 3);  // 32..35 r3 c4567
3463                    }
3464
3465                    // Scale and bias application
3466                    // acc is stored interleaved to match output layout
3467                    const int16x4_t sc_0123_lo = vget_low_s16(q4sb_scales[0]);
3468                    const int16x4_t sc_4567_lo = vget_high_s16(q4sb_scales[0]);
3469                    const int16x4_t sc_0123_hi = vget_low_s16(q4sb_scales[1]);
3470                    const int16x4_t sc_4567_hi = vget_high_s16(q4sb_scales[1]);
3471                    for (int row = 0; row < q8_k_blocklen; row++) {
3472                        // Bias correction
3473                        // row c0123 blk0 and blk1
3474                        const float32x4_t sumf_0123 =
3475                            vcvtq_f32_s32(vaddq_s32(vmulq_s32(vmovl_s16(sc_0123_lo), acc_lo[row]),
3476                                                    vmulq_s32(vmovl_s16(sc_0123_hi), acc_hi[row])));
3477                        acc_f32[2 * row] = vfmaq_f32(acc_f32[2 * row], sbd_scale_0123[row], sumf_0123);
3478
3479                        // row c4567 blk0 and blk1
3480                        const float32x4_t sumf_4567 =
3481                            vcvtq_f32_s32(vaddq_s32(vmulq_s32(vmovl_s16(sc_4567_lo), acc_lo[row + 4]),
3482                                                    vmulq_s32(vmovl_s16(sc_4567_hi), acc_hi[row + 4])));
3483                        acc_f32[2 * row + 1] = vfmaq_f32(acc_f32[2 * row + 1], sbd_scale_4567[row], sumf_4567);
3484
3485                        // Bias
3486                        const int16x4_t bsums_vec_lo = vdup_n_s16(bsums_arr[sb][row * 2]);
3487                        const int16x4_t bsums_vec_hi = vdup_n_s16(bsums_arr[sb][row * 2 + 1]);
3488
3489                        // row c0123 blk0 and blk1
3490                        bias_acc[2 * row] = vmlal_s16(bias_acc[2 * row], bsums_vec_lo, vget_low_s16(q4sb_mins[0]));
3491                        bias_acc[2 * row] = vmlal_s16(bias_acc[2 * row], bsums_vec_hi, vget_low_s16(q4sb_mins[1]));
3492
3493                        // row c4567 blk0 and blk1
3494                        bias_acc[2 * row + 1] =
3495                            vmlal_s16(bias_acc[2 * row + 1], bsums_vec_lo, vget_high_s16(q4sb_mins[0]));
3496                        bias_acc[2 * row + 1] =
3497                            vmlal_s16(bias_acc[2 * row + 1], bsums_vec_hi, vget_high_s16(q4sb_mins[1]));
3498                    }
3499                }  // for sb
3500
3501                for (int row = 0; row < q8_k_blocklen; row++) {
3502                    acc_f32[2 * row] = vmlsq_f32(acc_f32[2 * row], vcvtq_f32_s32(bias_acc[2 * row]), sbd_min_0123[row]);
3503                    acc_f32[2 * row + 1] =
3504                        vmlsq_f32(acc_f32[2 * row + 1], vcvtq_f32_s32(bias_acc[2 * row + 1]), sbd_min_4567[row]);
3505                }
3506            }  // for b
3507
3508            for (int i = 0; i < q8_k_blocklen; i++) {
3509                int row = y * q8_k_blocklen + i;
3510                for (int j = 0; j < 2; j++) {
3511                    int col    = x * ncols_interleaved + j * 4;
3512                    int offset = row * bs + col;
3513                    vst1q_f32(s + offset, acc_f32[2 * i + j]);
3514                }
3515            }
3516        }  // for x
3517    }  // for y
3518    return;
3519#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
3520    ggml_gemm_q4_K_8x4_q8_K_generic(n, s, bs, vx, vy, nr, nc);
3521}
3522
3523void ggml_gemm_q5_K_8x4_q8_K(int                        n,
3524                             float * GGML_RESTRICT      s,
3525                             size_t                     bs,
3526                             const void * GGML_RESTRICT vx,
3527                             const void * GGML_RESTRICT vy,
3528                             int                        nr,
3529                             int                        nc) {
3530    constexpr int qk = QK_K;
3531    const int     nb = n / qk;
3532
3533    constexpr int ncols_interleaved = 8;
3534    constexpr int blocklen          = 4;
3535
3536    assert(n % qk == 0);
3537    assert(nr % 4 == 0);
3538    assert(nc % ncols_interleaved == 0);
3539
3540    UNUSED(nb);
3541    UNUSED(ncols_interleaved);
3542    UNUSED(blocklen);
3543
3544#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
3545    constexpr int    q8_k_blocklen = 4;
3546    constexpr int    acc_size      = 2 * 4;  // 2 row pairs, 4 col pairs
3547    constexpr int    col_groups    = ncols_interleaved / 4;
3548    const uint8x16_t m4b           = vdupq_n_u8(0x0f);
3549    const uint8x16_t mone          = vdupq_n_u8(1);
3550    const uint8x16_t mtwo          = vdupq_n_u8(2);
3551
3552    // 8 accumulators: 2 row pairs, 4 col pairs
3553    float32x4_t acc_f32[acc_size];
3554
3555    for (int y = 0; y < nr / q8_k_blocklen; y++) {
3556        const block_q8_Kx4 * GGML_RESTRICT q8_ptr = (const block_q8_Kx4 *) vy + (y * nb);
3557
3558        for (int x = 0; x < nc / ncols_interleaved; x++) {
3559            const block_q5_Kx8 * GGML_RESTRICT q5_ptr = (const block_q5_Kx8 *) vx + (x * nb);
3560
3561            for (int i = 0; i < acc_size; i++) {
3562                acc_f32[i] = vdupq_n_f32(0);
3563            }
3564
3565            for (int b = 0; b < nb; b++) {
3566                // d5 0 1 2 3, 4 5 6 7
3567                float32x4_t q5_d_0123    = vcvt_f32_f16(vld1_f16((const __fp16 *) q5_ptr[b].d));
3568                float32x4_t q5_d_4567    = vcvt_f32_f16(vld1_f16((const __fp16 *) q5_ptr[b].d + 4));
3569                // d8 0 1 2 3
3570                float32x4_t q8_d_0123    = vld1q_f32(q8_ptr[b].d);
3571                // mins
3572                float32x4_t q5_dmin_0123 = vcvt_f32_f16(vld1_f16((const __fp16 *) q5_ptr[b].dmin));
3573                float32x4_t q5_dmin_4567 = vcvt_f32_f16(vld1_f16((const __fp16 *) q5_ptr[b].dmin + 4));
3574
3575                // Precomputation of scales and mins
3576                float32x4_t sbd_scale_0123[q8_k_blocklen];
3577                float32x4_t sbd_scale_4567[q8_k_blocklen];
3578                float32x4_t sbd_min_0123[q8_k_blocklen];
3579                float32x4_t sbd_min_4567[q8_k_blocklen];
3580
3581                sbd_scale_0123[0] = vmulq_laneq_f32(q5_d_0123, q8_d_0123, 0);
3582                sbd_scale_4567[0] = vmulq_laneq_f32(q5_d_4567, q8_d_0123, 0);
3583                sbd_min_0123[0]   = vmulq_laneq_f32(q5_dmin_0123, q8_d_0123, 0);
3584                sbd_min_4567[0]   = vmulq_laneq_f32(q5_dmin_4567, q8_d_0123, 0);
3585
3586                sbd_scale_0123[1] = vmulq_laneq_f32(q5_d_0123, q8_d_0123, 1);
3587                sbd_scale_4567[1] = vmulq_laneq_f32(q5_d_4567, q8_d_0123, 1);
3588                sbd_min_0123[1]   = vmulq_laneq_f32(q5_dmin_0123, q8_d_0123, 1);
3589                sbd_min_4567[1]   = vmulq_laneq_f32(q5_dmin_4567, q8_d_0123, 1);
3590
3591                sbd_scale_0123[2] = vmulq_laneq_f32(q5_d_0123, q8_d_0123, 2);
3592                sbd_scale_4567[2] = vmulq_laneq_f32(q5_d_4567, q8_d_0123, 2);
3593                sbd_min_0123[2]   = vmulq_laneq_f32(q5_dmin_0123, q8_d_0123, 2);
3594                sbd_min_4567[2]   = vmulq_laneq_f32(q5_dmin_4567, q8_d_0123, 2);
3595
3596                sbd_scale_0123[3] = vmulq_laneq_f32(q5_d_0123, q8_d_0123, 3);
3597                sbd_scale_4567[3] = vmulq_laneq_f32(q5_d_4567, q8_d_0123, 3);
3598                sbd_min_0123[3]   = vmulq_laneq_f32(q5_dmin_0123, q8_d_0123, 3);
3599                sbd_min_4567[3]   = vmulq_laneq_f32(q5_dmin_4567, q8_d_0123, 3);
3600
3601                // Precomputation of bsums, each vpaddq calcs all the bsums for each row
3602                const int16x8_t bsums[q8_k_blocklen] = {
3603                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 0), vld1q_s16(q8_ptr[b].bsums + 16 * 0 + 8)),
3604                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 1), vld1q_s16(q8_ptr[b].bsums + 16 * 1 + 8)),
3605                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 2), vld1q_s16(q8_ptr[b].bsums + 16 * 2 + 8)),
3606                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 3), vld1q_s16(q8_ptr[b].bsums + 16 * 3 + 8)),
3607                };
3608                int16_t bsums_arr[QK_K / 64][8];
3609                for (int q8_row = 0; q8_row < 4; q8_row++) {
3610                    vst1q_s16(bsums_arr[q8_row], bsums[q8_row]);
3611                }
3612
3613                // interleaved bias_acc: [0]->r0 0123, [1]->r1 0123, .., [4]->r0 4567, [5]->r1 4567 ..
3614                int32x4_t bias_acc[acc_size];
3615                for (int i = 0; i < acc_size; i++) {
3616                    bias_acc[i] = vdupq_n_s32(0);
3617                }
3618
3619                uint8x16_t qh[col_groups][8];
3620                for (int c = 0; c < col_groups; c++) {
3621                    for (int i = 0; i < 8; i++) {
3622                        qh[c][i] = vld1q_u8(q5_ptr[b].qh + i * 32 + 16 * c);
3623                    }
3624                }
3625
3626                for (int sb = 0; sb < QK_K / 64; sb++) {
3627                    // Int accumulators for qs vecdot (4 row * 2 col quartets)
3628                    int32x4_t acc_lo[acc_size];
3629                    int32x4_t acc_hi[acc_size];
3630                    for (int i = 0; i < acc_size; i++) {
3631                        acc_lo[i] = vdupq_n_s32(0);
3632                        acc_hi[i] = vdupq_n_s32(0);
3633                    }
3634                    // Need scales for the low and high nibbles
3635                    // 2 * 12 = 24 bytes per subblock, 4 sbs -> 4 * 24 = 96 bytes total
3636                    int16x8_t q5sb_scales[2];
3637                    int16x8_t q5sb_mins[2];
3638                    for (int i = 0; i < 2; i++) {
3639                        int8_t    aux_q5sb[8];
3640                        const int offset = sb * 24 + i * 12;
3641                        decode_q_Kx8_6bit_scales(&q5_ptr[b].scales[offset], &q5sb_mins[i], aux_q5sb);
3642                        q5sb_scales[i] = vmovl_s8(vld1_s8(aux_q5sb));
3643                    }
3644
3645                    constexpr int reads_per_sb = 8;  // 8 * 16 bytes each => 32 qs * 4 rows
3646                    for (int k = 0; k < reads_per_sb; k++) {
3647                        const int8x16_t q8_blk0 = vld1q_s8(q8_ptr[b].qs + sb * 256 + 16 * k);
3648                        const int8x16_t q8_blk1 = vld1q_s8(q8_ptr[b].qs + sb * 256 + 16 * k + 128);
3649
3650                        // 0..3 & 32..35
3651                        const uint8x16_t q5_0123 = vld1q_u8(q5_ptr[b].qs + sb * QK_K + 32 * k);
3652                        const uint8x16_t q5_4567 = vld1q_u8(q5_ptr[b].qs + sb * QK_K + 32 * k + 16);
3653
3654                        // NOTE: This is the only difference with q4_K
3655                        const uint8x16_t hbit_lo_0123 = vandq_u8(qh[0][k], mone);
3656                        const uint8x16_t hbit_hi_0123 = vshlq_n_u8(vandq_u8(qh[0][k], mtwo), 3);
3657                        qh[0][k]                      = vshrq_n_u8(qh[0][k], 2);
3658                        const uint8x16_t hbit_lo_4567 = vandq_u8(qh[1][k], mone);
3659                        const uint8x16_t hbit_hi_4567 = vshlq_n_u8(vandq_u8(qh[1][k], mtwo), 3);
3660                        qh[1][k]                      = vshrq_n_u8(qh[1][k], 2);
3661                        // From here, same as q4_K
3662
3663                        const int8x16_t q5_0123_lo =
3664                            vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(q5_0123, m4b), hbit_lo_0123, 4));
3665                        const int8x16_t q5_0123_hi =
3666                            vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q5_0123, 4), hbit_hi_0123));
3667
3668                        acc_lo[0] = vdotq_laneq_s32(acc_lo[0], q5_0123_lo, q8_blk0, 0);  //  0..3  r0 c0123
3669                        acc_lo[1] = vdotq_laneq_s32(acc_lo[1], q5_0123_lo, q8_blk0, 1);  //  0..3  r1 c0123
3670                        acc_lo[2] = vdotq_laneq_s32(acc_lo[2], q5_0123_lo, q8_blk0, 2);  //  0..3  r2 c0123
3671                        acc_lo[3] = vdotq_laneq_s32(acc_lo[3], q5_0123_lo, q8_blk0, 3);  //  0..3  r3 c0123
3672
3673                        acc_hi[0] = vdotq_laneq_s32(acc_hi[0], q5_0123_hi, q8_blk1, 0);  // 32..35 r0 c0123
3674                        acc_hi[1] = vdotq_laneq_s32(acc_hi[1], q5_0123_hi, q8_blk1, 1);  // 32..35 r1 c0123
3675                        acc_hi[2] = vdotq_laneq_s32(acc_hi[2], q5_0123_hi, q8_blk1, 2);  // 32..35 r2 c0123
3676                        acc_hi[3] = vdotq_laneq_s32(acc_hi[3], q5_0123_hi, q8_blk1, 3);  // 32..35 r3 c0123
3677
3678                        const int8x16_t q5_4567_lo =
3679                            vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(q5_4567, m4b), hbit_lo_4567, 4));
3680                        const int8x16_t q5_4567_hi =
3681                            vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q5_4567, 4), hbit_hi_4567));
3682
3683                        acc_lo[4] = vdotq_laneq_s32(acc_lo[4], q5_4567_lo, q8_blk0, 0);  //  0..3  r0 c4567
3684                        acc_lo[5] = vdotq_laneq_s32(acc_lo[5], q5_4567_lo, q8_blk0, 1);  //  0..3  r1 c4567
3685                        acc_lo[6] = vdotq_laneq_s32(acc_lo[6], q5_4567_lo, q8_blk0, 2);  //  0..3  r2 c4567
3686                        acc_lo[7] = vdotq_laneq_s32(acc_lo[7], q5_4567_lo, q8_blk0, 3);  //  0..3  r3 c4567
3687
3688                        acc_hi[4] = vdotq_laneq_s32(acc_hi[4], q5_4567_hi, q8_blk1, 0);  // 32..35 r0 c4567
3689                        acc_hi[5] = vdotq_laneq_s32(acc_hi[5], q5_4567_hi, q8_blk1, 1);  // 32..35 r1 c4567
3690                        acc_hi[6] = vdotq_laneq_s32(acc_hi[6], q5_4567_hi, q8_blk1, 2);  // 32..35 r2 c4567
3691                        acc_hi[7] = vdotq_laneq_s32(acc_hi[7], q5_4567_hi, q8_blk1, 3);  // 32..35 r3 c4567
3692                    }
3693
3694                    // Scale and bias application
3695                    // acc is stored interleaved to match output layout
3696                    const int16x4_t sc_0123_lo = vget_low_s16(q5sb_scales[0]);
3697                    const int16x4_t sc_4567_lo = vget_high_s16(q5sb_scales[0]);
3698                    const int16x4_t sc_0123_hi = vget_low_s16(q5sb_scales[1]);
3699                    const int16x4_t sc_4567_hi = vget_high_s16(q5sb_scales[1]);
3700                    for (int row = 0; row < q8_k_blocklen; row++) {
3701                        // Bias correction
3702                        // row c0123 blk0 and blk1
3703                        const float32x4_t sumf_0123 =
3704                            vcvtq_f32_s32(vaddq_s32(vmulq_s32(vmovl_s16(sc_0123_lo), acc_lo[row]),
3705                                                    vmulq_s32(vmovl_s16(sc_0123_hi), acc_hi[row])));
3706                        acc_f32[2 * row] = vfmaq_f32(acc_f32[2 * row], sbd_scale_0123[row], sumf_0123);
3707
3708                        // row c4567 blk0 and blk1
3709                        const float32x4_t sumf_4567 =
3710                            vcvtq_f32_s32(vaddq_s32(vmulq_s32(vmovl_s16(sc_4567_lo), acc_lo[row + 4]),
3711                                                    vmulq_s32(vmovl_s16(sc_4567_hi), acc_hi[row + 4])));
3712                        acc_f32[2 * row + 1] = vfmaq_f32(acc_f32[2 * row + 1], sbd_scale_4567[row], sumf_4567);
3713
3714                        // Bias
3715                        const int16x4_t bsums_vec_lo = vdup_n_s16(bsums_arr[sb][row * 2]);
3716                        const int16x4_t bsums_vec_hi = vdup_n_s16(bsums_arr[sb][row * 2 + 1]);
3717
3718                        // row c0123 blk0 and blk1
3719                        bias_acc[2 * row] = vmlal_s16(bias_acc[2 * row], bsums_vec_lo, vget_low_s16(q5sb_mins[0]));
3720                        bias_acc[2 * row] = vmlal_s16(bias_acc[2 * row], bsums_vec_hi, vget_low_s16(q5sb_mins[1]));
3721
3722                        // row c4567 blk0 and blk1
3723                        bias_acc[2 * row + 1] =
3724                            vmlal_s16(bias_acc[2 * row + 1], bsums_vec_lo, vget_high_s16(q5sb_mins[0]));
3725                        bias_acc[2 * row + 1] =
3726                            vmlal_s16(bias_acc[2 * row + 1], bsums_vec_hi, vget_high_s16(q5sb_mins[1]));
3727                    }
3728                }  // for sb
3729
3730                for (int row = 0; row < q8_k_blocklen; row++) {
3731                    acc_f32[2 * row] = vmlsq_f32(acc_f32[2 * row], vcvtq_f32_s32(bias_acc[2 * row]), sbd_min_0123[row]);
3732                    acc_f32[2 * row + 1] =
3733                        vmlsq_f32(acc_f32[2 * row + 1], vcvtq_f32_s32(bias_acc[2 * row + 1]), sbd_min_4567[row]);
3734                }
3735            }  // for b
3736
3737            for (int i = 0; i < q8_k_blocklen; i++) {
3738                int row = y * q8_k_blocklen + i;
3739                for (int j = 0; j < 2; j++) {
3740                    int col    = x * ncols_interleaved + j * 4;
3741                    int offset = row * bs + col;
3742                    vst1q_f32(s + offset, acc_f32[2 * i + j]);
3743                }
3744            }
3745        }  // for x
3746    }  // for y
3747    return;
3748#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
3749    ggml_gemm_q5_K_8x4_q8_K_generic(n, s, bs, vx, vy, nr, nc);
3750}
3751
3752void ggml_gemm_q4_K_8x8_q8_K(int                        n,
3753                             float * GGML_RESTRICT      s,
3754                             size_t                     bs,
3755                             const void * GGML_RESTRICT vx,
3756                             const void * GGML_RESTRICT vy,
3757                             int                        nr,
3758                             int                        nc) {
3759    constexpr int qk = QK_K;
3760    const int     nb = n / qk;
3761
3762    constexpr int ncols_interleaved = 8;
3763    constexpr int blocklen          = 8;
3764
3765    assert(n % qk == 0);
3766    assert(nr % 4 == 0);
3767    assert(nc % ncols_interleaved == 0);
3768
3769    UNUSED(nb);
3770    UNUSED(ncols_interleaved);
3771    UNUSED(blocklen);
3772
3773#if defined(__aarch64__) && defined(__ARM_FEATURE_SVE) && defined(__ARM_FEATURE_MATMUL_INT8)
3774    if (svcntb() * 8 == 256) {
3775        constexpr int    q8_k_blocklen = 4;
3776        const svuint8_t m4b_1          = svdup_n_u8(0x0f);
3777        // 8 accumulators: 2 row pairs × 4 col pairs
3778        svfloat32_t acc_f32_01, acc_f32_23, acc_f32_45, acc_f32_67;
3779        uint32_t idx_arr[8] = { 0, 2, 4, 6,  1, 3, 5, 7 };
3780        svbool_t pg = svptrue_pat_b32(SV_VL8);
3781        svuint32_t idx = svld1(pg, idx_arr);
3782
3783        static const uint32_t idx_data[8] = {0, 4, 2, 6, 1, 5, 3, 7};
3784        svuint32_t idx1 = svld1_u32(svptrue_b32(), idx_data);
3785
3786        for (int y = 0; y < nr / q8_k_blocklen; y++) {
3787            const block_q8_Kx4 * GGML_RESTRICT q8_ptr = (const block_q8_Kx4 *) vy + (y * nb);
3788
3789            for (int x = 0; x < nc / ncols_interleaved; x++) {
3790                const block_q4_Kx8 * GGML_RESTRICT q4_ptr = (const block_q4_Kx8 *) vx + (x * nb);
3791
3792                acc_f32_01 = svdup_n_f32(0);
3793                acc_f32_23 = svdup_n_f32(0);
3794                acc_f32_45 = svdup_n_f32(0);
3795                acc_f32_67 = svdup_n_f32(0);
3796
3797                for (int b = 0; b < nb; b++) {
3798                    // bsums pairs belongs to the same q8_k subblock
3799                    // 64 elements loaded and made sum of 0-7 and 8-15 sum || 16-23 and 24 - 31 sum
3800                    const int16x8_t bsums[4]{
3801                        vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 0), vld1q_s16(q8_ptr[b].bsums + 16 * 0 + 8)),
3802                        vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 1), vld1q_s16(q8_ptr[b].bsums + 16 * 1 + 8)),
3803                        vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 2), vld1q_s16(q8_ptr[b].bsums + 16 * 2 + 8)),
3804                        vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 3), vld1q_s16(q8_ptr[b].bsums + 16 * 3 + 8)),
3805                    };
3806
3807                    int32_t bsums_arr32[4][8];
3808
3809                    for (int q8_row = 0; q8_row < 4; q8_row++) {
3810                        int16x8_t v16 = bsums[q8_row];
3811
3812                        // low 4
3813                        int32x4_t v32_lo = vmovl_s16(vget_low_s16(v16));
3814                        vst1q_s32(&bsums_arr32[q8_row][0], v32_lo);
3815
3816                        // high 4
3817                        int32x4_t v32_hi = vmovl_s16(vget_high_s16(v16));
3818                        vst1q_s32(&bsums_arr32[q8_row][4], v32_hi);
3819                    }
3820
3821                    svint32_t sb_acc_0 = svdup_n_s32(0);
3822                    svint32_t sb_acc_2 = svdup_n_s32(0);
3823
3824                    svint32_t acc_00 = svdup_n_s32(0);
3825                    svint32_t acc_11 = svdup_n_s32(0);
3826                    svint32_t acc_22 = svdup_n_s32(0);
3827                    svint32_t acc_33 = svdup_n_s32(0);
3828                    svint32_t acc_44 = svdup_n_s32(0);
3829                    svint32_t acc_55 = svdup_n_s32(0);
3830                    svint32_t acc_66 = svdup_n_s32(0);
3831                    svint32_t acc_77 = svdup_n_s32(0);
3832
3833                    svint32_t bias_acc_00 = svdup_n_s32(0);
3834                    svint32_t bias_acc_22 = svdup_n_s32(0);
3835                    svint32_t bias_acc_44 = svdup_n_s32(0);
3836                    svint32_t bias_acc_66 = svdup_n_s32(0);
3837
3838                    for (int sb = 0; sb < QK_K / 64; sb++) {
3839                        // Need scales for the low and high nibbles
3840                        // 2 * 12 = 24 bytes per subblock, 4 sbs -> 4 * 24 = 96 bytes total
3841                        svint32_t block_scale_0, block_scale_1, block_scale_2, block_scale_3;
3842                        svint32_t q4sb_mins_0, q4sb_mins_1;
3843                        {
3844                            // 2-superblock I am working on
3845                            const int offset = sb * 24 + 0 * 12;
3846                            const uint8_t * scales_in = &q4_ptr[b].scales[offset];
3847
3848                            const int offset1 = sb * 24 + 12;
3849                            const uint8_t * scales_in1 = &q4_ptr[b].scales[offset1];
3850
3851                            constexpr uint32_t kmask1 = 0x3f3f3f3f;
3852                            constexpr uint32_t kmask2 = 0x0f0f0f0f;
3853                            constexpr uint32_t kmask3 = 0x03030303;
3854                            constexpr uint8_t  scales_size = 12;
3855
3856                            uint32_t sm[3];
3857                            memcpy(sm, scales_in, scales_size);
3858
3859                            uint32_t sm1[3];
3860                            memcpy(sm1, scales_in1, scales_size);
3861
3862                            const uint32_t mins_0_3 = sm[1] & kmask1;
3863                            const uint32_t mins_4_7 = ((sm[2] >> 4) & kmask2) | (((sm[1] >> 6) & kmask3) << 4);
3864
3865                            const uint32_t mins_0_3_1 = sm1[1] & kmask1;
3866                            const uint32_t mins_4_7_1 = ((sm1[2] >> 4) & kmask2) | (((sm1[1] >> 6) & kmask3) << 4);
3867
3868                            svuint32_t mins_u32_temp = svzip1_u32(svdup_n_u32(mins_0_3), svdup_n_u32(mins_4_7));
3869                            svuint32_t mins_u32_temp_1 = svzip1_u32(svdup_n_u32(mins_0_3_1), svdup_n_u32(mins_4_7_1));
3870
3871                            /* reinterpret u32 → u8 */
3872                            svuint8_t mins_u8 = svreinterpret_u8_u32(mins_u32_temp);
3873                            svuint8_t mins_u8_1 = svreinterpret_u8_u32(mins_u32_temp_1);
3874
3875                            /* widen u8 → u16->u32 (lower half only) */
3876                            svuint32_t mins_u16 = svunpklo_u32(svunpklo_u16(mins_u8));
3877                            svuint32_t mins_u16_1 = svunpklo_u32(svunpklo_u16(mins_u8_1));
3878
3879                            q4sb_mins_0 = svreinterpret_s32_u32(mins_u16);
3880                            q4sb_mins_1 = svreinterpret_s32_u32(mins_u16_1);
3881
3882                            uint32_t scales_u32_0 = sm[0] & kmask1;
3883                            uint32_t scales_u32_1 = (sm[2] & kmask2) | (((sm[0] >> 6) & kmask3) << 4);
3884                            uint32_t scales_u32_2 = sm1[0] & kmask1;
3885                            uint32_t scales_u32_3 = (sm1[2] & kmask2) | (((sm1[0] >> 6) & kmask3) << 4);
3886
3887                            svuint32_t S01 = svdup_n_u32(scales_u32_0);
3888                            svuint32_t S23 = svdup_n_u32(scales_u32_1);
3889                            svuint32_t R01 = svdup_n_u32(scales_u32_2);
3890                            svuint32_t R23 = svdup_n_u32(scales_u32_3);
3891
3892                            svint8_t S01_b = svreinterpret_s8_u32(S01);
3893                            svint8_t S23_b = svreinterpret_s8_u32(S23);
3894                            svint8_t R01_b = svreinterpret_s8_u32(R01);
3895                            svint8_t R23_b = svreinterpret_s8_u32(R23);
3896
3897                            svint32_t S01_d = svunpklo_s32(svunpklo_s16(svzip1_s8(S01_b, S01_b)));
3898                            svint32_t R01_d = svunpklo_s32(svunpklo_s16(svzip1_s8(R01_b, R01_b)));
3899                            svint32_t S23_d = svunpklo_s32(svunpklo_s16(svzip1_s8(S23_b, S23_b)));
3900                            svint32_t R23_d = svunpklo_s32(svunpklo_s16(svzip1_s8(R23_b, R23_b)));
3901
3902                            block_scale_0 = svtbl_s32(svzip1_s32(S01_d, R01_d), idx);
3903                            block_scale_1 = svtbl_s32(svzip2_s32(S01_d, R01_d), idx);
3904                            block_scale_2 = svtbl_s32(svzip1_s32(S23_d, R23_d), idx);
3905                            block_scale_3 = svtbl_s32(svzip2_s32(S23_d, R23_d), idx);
3906                        }
3907
3908                        const int8_t * q8_base_1 = q8_ptr[b].qs + sb * 256;
3909
3910                        // Load 32-byte per row pair, 1 subblock each time
3911                        // predicate for activating higher lanes for 16 int8 elements
3912                        const svbool_t ph16 = svptrue_pat_b8(SV_VL16);
3913                        // predicate for activating lower lanes for  16 int8 elements
3914                        const svbool_t pl16 = svnot_b_z(svptrue_b8(), ph16);
3915
3916                        svint8_t q8_qs_0 = svadd_s8_x(svptrue_b8(), svld1_s8(ph16, q8_base_1 + 0), svld1_s8(pl16, q8_base_1 + 112));
3917                        svint8_t q8_qs_2 = svadd_s8_x(svptrue_b8(), svld1_s8(ph16, q8_base_1 + 32), svld1_s8(pl16, q8_base_1 + 144));
3918                        svint8_t q8_qs_4 = svadd_s8_x(svptrue_b8(), svld1_s8(ph16, q8_base_1 + 64), svld1_s8(pl16, q8_base_1 + 176));
3919                        svint8_t q8_qs_6 = svadd_s8_x(svptrue_b8(), svld1_s8(ph16, q8_base_1 + 96), svld1_s8(pl16, q8_base_1 + 208));
3920
3921                        svint8_t q8_qs_1 = svadd_s8_x(svptrue_b8(), svld1_s8(ph16, q8_base_1 + 16), svld1_s8(pl16, q8_base_1 + 128));
3922                        svint8_t q8_qs_3 = svadd_s8_x(svptrue_b8(), svld1_s8(ph16, q8_base_1 + 48), svld1_s8(pl16, q8_base_1 + 160));
3923                        svint8_t q8_qs_5 = svadd_s8_x(svptrue_b8(), svld1_s8(ph16, q8_base_1 + 80), svld1_s8(pl16, q8_base_1 + 192));
3924                        svint8_t q8_qs_7 = svadd_s8_x(svptrue_b8(), svld1_s8(ph16, q8_base_1 + 112), svld1_s8(pl16, q8_base_1 + 224));
3925
3926                        // Q4s columns iterated in pairs (01, 23, 45, 67)
3927                        for (int cp = 0; cp < ncols_interleaved / 2; cp++) {
3928
3929                            sb_acc_0 = svdup_n_s32(0);
3930                            sb_acc_2 = svdup_n_s32(0);
3931
3932                            svuint8_t q4_qs_cp_00 = svld1rq_u8(svptrue_b8(), q4_ptr[b].qs + sb * QK_K + 16 * cp + 0);
3933                            svuint8_t q4_qs_cp_01 = svld1rq_u8(svptrue_b8(), q4_ptr[b].qs + sb * QK_K + 16 * cp + 64);
3934                            svuint8_t q4_qs_cp_02 = svld1rq_u8(svptrue_b8(), q4_ptr[b].qs + sb * QK_K + 16 * cp + 128);
3935                            svuint8_t q4_qs_cp_03 = svld1rq_u8(svptrue_b8(), q4_ptr[b].qs + sb * QK_K + 16 * cp + 192);
3936
3937                            svint8_t q4_nibbles_00 = svreinterpret_s8_u8(svlsr_n_u8_m(pl16, svand_u8_m(ph16, q4_qs_cp_00, m4b_1), 4));
3938                            svint8_t q4_nibbles_01 = svreinterpret_s8_u8(svlsr_n_u8_m(pl16, svand_u8_m(ph16, q4_qs_cp_01, m4b_1), 4));
3939                            svint8_t q4_nibbles_02 = svreinterpret_s8_u8(svlsr_n_u8_m(pl16, svand_u8_m(ph16, q4_qs_cp_02, m4b_1), 4));
3940                            svint8_t q4_nibbles_03 = svreinterpret_s8_u8(svlsr_n_u8_m(pl16, svand_u8_m(ph16, q4_qs_cp_03, m4b_1), 4));
3941
3942                            sb_acc_0 = svmmla_s32(sb_acc_0, q4_nibbles_00, q8_qs_0);
3943                            sb_acc_0 = svmmla_s32(sb_acc_0, q4_nibbles_01, q8_qs_2);
3944
3945                            sb_acc_0 = svmmla_s32(sb_acc_0, q4_nibbles_02, q8_qs_4);
3946                            sb_acc_0 = svmmla_s32(sb_acc_0, q4_nibbles_03, q8_qs_6);
3947
3948                            sb_acc_2 = svmmla_s32(sb_acc_2, q4_nibbles_00, q8_qs_1);
3949                            sb_acc_2 = svmmla_s32(sb_acc_2, q4_nibbles_01, q8_qs_3);
3950
3951                            sb_acc_2 = svmmla_s32(sb_acc_2, q4_nibbles_02, q8_qs_5);
3952                            sb_acc_2 = svmmla_s32(sb_acc_2, q4_nibbles_03, q8_qs_7);
3953
3954                            if(cp == 0) {
3955                                acc_00 = svmla_s32_m(svptrue_b32(), acc_00, sb_acc_0, block_scale_0);
3956                                acc_44 = svmla_s32_m(svptrue_b32(), acc_44, sb_acc_2, block_scale_0);
3957                            }
3958                            if(cp == 1) {
3959                                acc_11 = svmla_s32_m(svptrue_b32(), acc_11, sb_acc_0, block_scale_1);
3960                                acc_55 = svmla_s32_m(svptrue_b32(), acc_55, sb_acc_2, block_scale_1);
3961                            }
3962                            if(cp == 2) {
3963                                acc_22 = svmla_s32_m(svptrue_b32(), acc_22, sb_acc_0, block_scale_2);
3964                                acc_66 = svmla_s32_m(svptrue_b32(), acc_66, sb_acc_2, block_scale_2);
3965                            }
3966                            if(cp == 3) {
3967                                acc_33 = svmla_s32_m(svptrue_b32(), acc_33, sb_acc_0, block_scale_3);
3968                                acc_77 = svmla_s32_m(svptrue_b32(), acc_77, sb_acc_2, block_scale_3);
3969                            }
3970                        }
3971
3972                        bias_acc_00 = svmla_s32_m(svptrue_pat_b32(SV_VL8), bias_acc_00, svdup_n_s32(bsums_arr32[sb][0]), q4sb_mins_0);
3973                        bias_acc_00 = svmla_s32_m(svptrue_pat_b32(SV_VL8), bias_acc_00, svdup_n_s32(bsums_arr32[sb][1]), q4sb_mins_1);
3974
3975                        bias_acc_22 = svmla_s32_m(svptrue_pat_b32(SV_VL8), bias_acc_22, svdup_n_s32(bsums_arr32[sb][2]), q4sb_mins_0);
3976                        bias_acc_22 = svmla_s32_m(svptrue_pat_b32(SV_VL8), bias_acc_22, svdup_n_s32(bsums_arr32[sb][3]), q4sb_mins_1);
3977
3978                        bias_acc_44 = svmla_s32_m(svptrue_pat_b32(SV_VL8), bias_acc_44, svdup_n_s32(bsums_arr32[sb][4]), q4sb_mins_0);
3979                        bias_acc_44 = svmla_s32_m(svptrue_pat_b32(SV_VL8), bias_acc_44, svdup_n_s32(bsums_arr32[sb][5]), q4sb_mins_1);
3980
3981                        bias_acc_66 = svmla_s32_m(svptrue_pat_b32(SV_VL8), bias_acc_66, svdup_n_s32(bsums_arr32[sb][6]), q4sb_mins_0);
3982                        bias_acc_66 = svmla_s32_m(svptrue_pat_b32(SV_VL8), bias_acc_66, svdup_n_s32(bsums_arr32[sb][7]), q4sb_mins_1);
3983                    }  // for sb
3984
3985
3986                    acc_00 = svadd_s32_z(svptrue_pat_b32(SV_VL4), acc_00, svext_s32(acc_00, acc_00, 4));
3987                    acc_11 = svadd_s32_z(svptrue_pat_b32(SV_VL4), acc_11, svext_s32(acc_11, acc_11, 4));
3988                    acc_22 = svadd_s32_z(svptrue_pat_b32(SV_VL4), acc_22, svext_s32(acc_22, acc_22, 4));
3989                    acc_33 = svadd_s32_z(svptrue_pat_b32(SV_VL4), acc_33, svext_s32(acc_33, acc_33, 4));
3990                    acc_44 = svadd_s32_z(svptrue_pat_b32(SV_VL4), acc_44, svext_s32(acc_44, acc_44, 4));
3991                    acc_55 = svadd_s32_z(svptrue_pat_b32(SV_VL4), acc_55, svext_s32(acc_55, acc_55, 4));
3992                    acc_66 = svadd_s32_z(svptrue_pat_b32(SV_VL4), acc_66, svext_s32(acc_66, acc_66, 4));
3993                    acc_77 = svadd_s32_z(svptrue_pat_b32(SV_VL4), acc_77, svext_s32(acc_77, acc_77, 4));
3994
3995                    svint32_t reorder_acc_01 = svtbl_s32( svzip1_s32( svtrn1_s32(acc_00, acc_11), svtrn1_s32(acc_22, acc_33)), idx1);
3996                    svint32_t reorder_acc_23 = svtbl_s32( svzip1_s32( svtrn2_s32(acc_00, acc_11), svtrn2_s32(acc_22, acc_33)), idx1);
3997
3998                    svint32_t reorder_acc_45 = svtbl_s32( svzip1_s32( svtrn1_s32(acc_44, acc_55), svtrn1_s32(acc_66, acc_77)), idx1);
3999                    svint32_t reorder_acc_67 = svtbl_s32( svzip1_s32( svtrn2_s32(acc_44, acc_55), svtrn2_s32(acc_66, acc_77)), idx1);
4000
4001                    // Broadcast q8 scalar
4002                    svfloat32_t q8_d = svdup_f32(q8_ptr[b].d[0]);
4003
4004                    svfloat32_t q4_dmin_temp = svcvt_f32_f16_x(svptrue_b32(), svzip1_f16( svld1_f16(svptrue_pat_b16(SV_VL8), (const __fp16 *)q4_ptr[b].dmin), svdup_f16(0)));
4005
4006                    svfloat32_t q4_d_temp = svcvt_f32_f16_x(svptrue_b32(), svzip1_f16( svld1_f16(svptrue_pat_b16(SV_VL8), (const __fp16 *)q4_ptr[b].d), svdup_f16(0)));
4007
4008                    svfloat32_t scale1 = svmul_f32_x(svptrue_b32(), q4_d_temp, q8_d);
4009                    svfloat32_t dmins1 = svmul_f32_x(svptrue_b32(), q4_dmin_temp, q8_d);
4010
4011                    acc_f32_01 = svmls_f32_m(svptrue_b32(), acc_f32_01, svcvt_f32_s32_m(svdup_n_f32(0), svptrue_b32(), bias_acc_00), dmins1);
4012                    acc_f32_01 = svmla_f32_m(svptrue_b32(), acc_f32_01, svcvt_f32_s32_m(svdup_n_f32(0), svptrue_b32(), reorder_acc_01), scale1);
4013
4014                    q8_d = svdup_f32(q8_ptr[b].d[1]);
4015
4016                    scale1 = svmul_f32_x(svptrue_b32(), q4_d_temp, q8_d);
4017                    dmins1 = svmul_f32_x(svptrue_b32(), q4_dmin_temp, q8_d);
4018
4019                    acc_f32_23 = svmls_f32_m(svptrue_b32(), acc_f32_23, svcvt_f32_s32_m(svdup_n_f32(0), svptrue_b32(), bias_acc_22), dmins1);
4020                    acc_f32_23 = svmla_f32_m(svptrue_b32(), acc_f32_23, svcvt_f32_s32_m(svdup_n_f32(0), svptrue_b32(), reorder_acc_23), scale1);
4021
4022                    q8_d = svdup_f32(q8_ptr[b].d[2]);
4023
4024
4025                    scale1 = svmul_f32_x(svptrue_b32(), q4_d_temp, q8_d);
4026                    dmins1 = svmul_f32_x(svptrue_b32(), q4_dmin_temp, q8_d);
4027
4028                    acc_f32_45 = svmls_f32_m(svptrue_b32(), acc_f32_45, svcvt_f32_s32_m(svdup_n_f32(0), svptrue_b32(), bias_acc_44), dmins1);
4029                    acc_f32_45 = svmla_f32_m(svptrue_b32(), acc_f32_45, svcvt_f32_s32_m(svdup_n_f32(0), svptrue_b32(), reorder_acc_45), scale1);
4030
4031                    q8_d = svdup_f32(q8_ptr[b].d[3]);
4032
4033                    scale1 = svmul_f32_x(svptrue_b32(), q4_d_temp, q8_d);
4034                    dmins1 = svmul_f32_x(svptrue_b32(), q4_dmin_temp, q8_d);
4035
4036                    acc_f32_67 = svmls_f32_m(svptrue_b32(), acc_f32_67, svcvt_f32_s32_m(svdup_n_f32(0), svptrue_b32(), bias_acc_66), dmins1);
4037                    acc_f32_67 = svmla_f32_m(svptrue_b32(), acc_f32_67, svcvt_f32_s32_m(svdup_n_f32(0), svptrue_b32(), reorder_acc_67), scale1);
4038
4039                }  // for b
4040
4041                // With the previous reorder, the tile is already in the correct memory layout.
4042                // Predicate for exactly 4 lanes
4043                svbool_t pg4 = svptrue_pat_b32(SV_VL4);
4044                for (int i = 0; i < q8_k_blocklen; i++) {
4045                    int row = y * q8_k_blocklen + i;
4046                    for (int j = 0; j < 2; j++) {
4047                        int col    = x * ncols_interleaved + j * 4;
4048                        int offset = row * bs + col;
4049
4050                        if (i == 0 && j == 0) {
4051                            // acc_f32_0 → lower half of acc_f32_01
4052                            svst1_f32(pg4, s + offset, acc_f32_01);
4053                        } else if (i == 0 && j == 1) {
4054                            // acc_f32_1 → upper half of acc_f32_01
4055                            svst1_f32(pg4, s + offset, svext_f32(acc_f32_01, acc_f32_01, 4));
4056                        } else if (i == 1 && j == 0) {
4057                            // acc_f32_2
4058                            svst1_f32(pg4, s + offset, acc_f32_23);
4059                        } else if (i == 1 && j == 1) {
4060                            // acc_f32_3
4061                            svst1_f32(pg4, s + offset, svext_f32(acc_f32_23, acc_f32_23, 4));
4062                        } else if (i == 2 && j == 0) {
4063                            // acc_f32_4
4064                            svst1_f32(pg4, s + offset, acc_f32_45);
4065                        } else if (i == 2 && j == 1) {
4066                            // acc_f32_5
4067                            svst1_f32(pg4, s + offset, svext_f32(acc_f32_45, acc_f32_45, 4));
4068                        } else if (i == 3 && j == 0) {
4069                            // acc_f32_6
4070                            svst1_f32(pg4, s + offset, acc_f32_67);
4071                        } else if (i == 3 && j == 1) {
4072                            // acc_f32_7
4073                            svst1_f32(pg4, s + offset, svext_f32(acc_f32_67, acc_f32_67, 4));
4074                        }
4075                    }
4076                }
4077            }  // for x
4078        }  // for y
4079        return;
4080    }
4081#endif  // SVE compile-time end
4082
4083#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
4084    constexpr int    q8_k_blocklen = 4;
4085    const uint8x16_t m4b           = vdupq_n_u8(0x0f);
4086
4087    // 8 accumulators: 2 row pairs × 4 col pairs
4088    float32x4_t acc_f32[blocklen];
4089
4090    for (int y = 0; y < nr / q8_k_blocklen; y++) {
4091        const block_q8_Kx4 * GGML_RESTRICT q8_ptr = (const block_q8_Kx4 *) vy + (y * nb);
4092
4093        for (int x = 0; x < nc / ncols_interleaved; x++) {
4094            const block_q4_Kx8 * GGML_RESTRICT q4_ptr = (const block_q4_Kx8 *) vx + (x * nb);
4095
4096            for (int i = 0; i < blocklen; i++) {
4097                acc_f32[i] = vdupq_n_f32(0);
4098            }
4099
4100            for (int b = 0; b < nb; b++) {
4101                // bsums pairs belongs to the same q8_k subblock
4102                const int16x8_t bsums[4]{
4103                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 0), vld1q_s16(q8_ptr[b].bsums + 16 * 0 + 8)),
4104                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 1), vld1q_s16(q8_ptr[b].bsums + 16 * 1 + 8)),
4105                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 2), vld1q_s16(q8_ptr[b].bsums + 16 * 2 + 8)),
4106                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 3), vld1q_s16(q8_ptr[b].bsums + 16 * 3 + 8)),
4107                };
4108                int16_t bsums_arr[4][8];
4109                for (int q8_row = 0; q8_row < 4; q8_row++) {
4110                    vst1q_s16(bsums_arr[q8_row], bsums[q8_row]);
4111                }
4112
4113                int32x4_t sb_acc[4];    // Aux accumulators to store subblock (partial) results
4114                int32x4_t acc[8];       // rows 01 stored in [0][1][2][3] rows 23 stored in [4][5][6][7]
4115                int32x4_t bias_acc[8];  // interleaved bias_acc: [0]->r0 0123, [1]->r0 4567, [2]->r1 0123 ...
4116                for (int i = 0; i < 8; i++) {
4117                    acc[i]      = vdupq_n_s32(0);
4118                    bias_acc[i] = vdupq_n_s32(0);
4119                }
4120
4121                for (int sb = 0; sb < QK_K / 64; sb++) {
4122                    // Need scales for the low and high nibbles
4123                    // 2 * 12 = 24 bytes per subblock, 4 sbs -> 4 * 24 = 96 bytes total
4124                    int8_t    q4sb_scales[2][8];
4125                    int16x8_t q4sb_mins[2];  // int16 as its needed for bias_acc later
4126                    for (int i = 0; i < 2; i++) {
4127                        const int offset = sb * 24 + i * 12;
4128                        decode_q_Kx8_6bit_scales(&q4_ptr[b].scales[offset], &q4sb_mins[i], q4sb_scales[i]);
4129                    }
4130
4131                    // q8_ptr[b].qs has interleaved Q8 rows (01, 23)
4132                    const int8_t * q8_base = q8_ptr[b].qs + sb * 256;
4133
4134                    int8x16_t q8_qs_01[8];
4135                    int8x16_t q8_qs_23[8];
4136
4137                    // Load 32-byte per row pair, 1 subblock each time
4138                    for (int i = 0; i < 8; i++) {
4139                        const int offset = i * 32;  // 16 for row 01, 16 for row 23
4140                        q8_qs_01[i]      = vld1q_s8(q8_base + offset);
4141                        q8_qs_23[i]      = vld1q_s8(q8_base + offset + 16);
4142                    }
4143
4144                    const int8x16_t q8s[2][8] = {
4145                        { q8_qs_01[0], q8_qs_01[1], q8_qs_01[2], q8_qs_01[3],
4146                          q8_qs_01[4], q8_qs_01[5], q8_qs_01[6], q8_qs_01[7] },
4147                        { q8_qs_23[0], q8_qs_23[1], q8_qs_23[2], q8_qs_23[3],
4148                          q8_qs_23[4], q8_qs_23[5], q8_qs_23[6], q8_qs_23[7] },
4149                    };
4150
4151                    // Q4s columns iterated in pairs (01, 23, 45, 67)
4152                    for (int cp = 0; cp < ncols_interleaved / 2; cp++) {
4153                        for (int i = 0; i < 4; i++) {
4154                            sb_acc[i] = vdupq_n_s32(0);
4155                        }
4156
4157                        uint8x16_t q4_qs_cp_0 = vld1q_u8(q4_ptr[b].qs + sb * QK_K + 16 * cp + 0);    // 0 .. 7 & 32..39
4158                        uint8x16_t q4_qs_cp_1 = vld1q_u8(q4_ptr[b].qs + sb * QK_K + 16 * cp + 64);   // 8 ..15 & 40..47
4159                        uint8x16_t q4_qs_cp_2 = vld1q_u8(q4_ptr[b].qs + sb * QK_K + 16 * cp + 128);  // 16..23 & 48..55
4160                        uint8x16_t q4_qs_cp_3 = vld1q_u8(q4_ptr[b].qs + sb * QK_K + 16 * cp + 192);  // 24..31 & 56..63
4161                        const int8x16_t q4_nibbles[2][4] = {
4162                            {
4163                                vreinterpretq_s8_u8(vandq_u8(q4_qs_cp_0, m4b)),
4164                                vreinterpretq_s8_u8(vandq_u8(q4_qs_cp_1, m4b)),
4165                                vreinterpretq_s8_u8(vandq_u8(q4_qs_cp_2, m4b)),
4166                                vreinterpretq_s8_u8(vandq_u8(q4_qs_cp_3, m4b)),
4167                            },
4168                            {
4169                                vreinterpretq_s8_u8(vshrq_n_u8(q4_qs_cp_0, 4)),
4170                                vreinterpretq_s8_u8(vshrq_n_u8(q4_qs_cp_1, 4)),
4171                                vreinterpretq_s8_u8(vshrq_n_u8(q4_qs_cp_2, 4)),
4172                                vreinterpretq_s8_u8(vshrq_n_u8(q4_qs_cp_3, 4)),
4173                            }
4174                        };
4175
4176                        // Calculates the Qs muladd of every row pair (rp) rows 01 and 23 of q8
4177                        // for each of the internal 32 qs subblock (blk)
4178                        for (int rp = 0; rp < 2; rp++) {
4179                            for (int blk = 0; blk < 2; blk++) {
4180                                const int8x16_t * q8  = &q8s[rp][4 * blk];
4181                                const int8x16_t * q4  = q4_nibbles[blk];
4182                                int32x4_t         acc = sb_acc[2 * rp + blk];
4183                                // mul add for each qs in the same subblock
4184                                for (int qs_offset = 0; qs_offset < 4; qs_offset++) {
4185                                    acc = vmmlaq_s32(acc, q4[qs_offset], q8[qs_offset]);
4186                                }
4187                                sb_acc[2 * rp + blk] = acc;
4188                            }
4189                        }
4190
4191                        // Scales[i] corresponds to column i
4192                        const int scale_offset = cp * 2;
4193                        const int32_t scale_00 = q4sb_scales[0][scale_offset];
4194                        const int32_t scale_01 = q4sb_scales[0][scale_offset + 1];
4195                        const int32_t scale_10 = q4sb_scales[1][scale_offset];
4196                        const int32_t scale_11 = q4sb_scales[1][scale_offset + 1];
4197                        const int32x4_t block_scale_0 = vcombine_s32(vdup_n_s32(scale_00), vdup_n_s32(scale_01));
4198                        const int32x4_t block_scale_1 = vcombine_s32(vdup_n_s32(scale_10), vdup_n_s32(scale_11));
4199
4200                        acc[cp]     = vmlaq_s32(acc[cp], sb_acc[0], block_scale_0);
4201                        acc[cp + 4] = vmlaq_s32(acc[cp + 4], sb_acc[2], block_scale_0);
4202                        acc[cp]     = vmlaq_s32(acc[cp], sb_acc[1], block_scale_1);
4203                        acc[cp + 4] = vmlaq_s32(acc[cp + 4], sb_acc[3], block_scale_1);
4204                    }
4205
4206                    // Multiply Acc bsum + mins
4207                    for (int q8_row = 0; q8_row < 4; q8_row++) {
4208                        // Each pair of subblocks share the same bsums
4209                        // Load scalar bsum → broadcast to a vector (vdupq_n_s16(s)).
4210                        int16x4_t bsums_vec_lo = vdup_n_s16(bsums_arr[sb][q8_row * 2]);
4211                        int16x4_t bsums_vec_hi = vdup_n_s16(bsums_arr[sb][q8_row * 2 + 1]);
4212
4213                        bias_acc[2 * q8_row] =
4214                            vmlal_s16(bias_acc[2 * q8_row], bsums_vec_lo, vget_low_s16(q4sb_mins[0]));
4215                        bias_acc[2 * q8_row] =
4216                            vmlal_s16(bias_acc[2 * q8_row], bsums_vec_hi, vget_low_s16(q4sb_mins[1]));
4217                        bias_acc[2 * q8_row + 1] =
4218                            vmlal_s16(bias_acc[2 * q8_row + 1], bsums_vec_lo, vget_high_s16(q4sb_mins[0]));
4219                        bias_acc[2 * q8_row + 1] =
4220                            vmlal_s16(bias_acc[2 * q8_row + 1], bsums_vec_hi, vget_high_s16(q4sb_mins[1]));
4221                    }
4222                }  // for sb
4223
4224                // Reorder of i8mm output with bias and output layout
4225                for (int i = 0; i < 8; i++) {
4226                    int32x2x2_t aux = vzip_s32(vget_low_s32(acc[i]), vget_high_s32(acc[i]));
4227                    acc[i]          = vcombine_s32(aux.val[0], aux.val[1]);
4228                }
4229                int32x4_t reorder_acc[8] = {
4230                    vcombine_s32(vget_low_s32(acc[0]), vget_low_s32(acc[1])),
4231                    vcombine_s32(vget_low_s32(acc[2]), vget_low_s32(acc[3])),
4232                    vcombine_s32(vget_high_s32(acc[0]), vget_high_s32(acc[1])),
4233                    vcombine_s32(vget_high_s32(acc[2]), vget_high_s32(acc[3])),
4234                    vcombine_s32(vget_low_s32(acc[4]), vget_low_s32(acc[5])),
4235                    vcombine_s32(vget_low_s32(acc[6]), vget_low_s32(acc[7])),
4236                    vcombine_s32(vget_high_s32(acc[4]), vget_high_s32(acc[5])),
4237                    vcombine_s32(vget_high_s32(acc[6]), vget_high_s32(acc[7])),
4238                };
4239
4240                for (int i = 0; i < q8_k_blocklen; i++) {
4241                    for (int j = 0; j < 2; j++) {
4242                        float32x4_t       q8_d    = vdupq_n_f32(q8_ptr[b].d[i]);
4243                        float32x4_t       q4_dmin = vcvt_f32_f16(vld1_f16((const __fp16 *) (q4_ptr[b].dmin + j * 4)));
4244                        const float32x4_t dmins   = vmulq_f32(q4_dmin, q8_d);
4245
4246                        float32x4_t       q4_d  = vcvt_f32_f16(vld1_f16((const __fp16 *) (q4_ptr[b].d + j * 4)));
4247                        const float32x4_t scale = vmulq_f32(q4_d, q8_d);
4248
4249                        acc_f32[2 * i + j] = vmlsq_f32(acc_f32[2 * i + j], vcvtq_f32_s32(bias_acc[2 * i + j]), dmins);
4250                        acc_f32[2 * i + j] =
4251                            vmlaq_f32(acc_f32[2 * i + j], vcvtq_f32_s32(reorder_acc[2 * i + j]), scale);
4252                    }
4253                }
4254            }  // for b
4255
4256            // With the previous reorder, the tile is already in the correct memory layout.
4257            for (int i = 0; i < q8_k_blocklen; i++) {
4258                int row = y * q8_k_blocklen + i;
4259                for (int j = 0; j < 2; j++) {
4260                    int col    = x * ncols_interleaved + j * 4;
4261                    int offset = row * bs + col;
4262                    vst1q_f32(s + offset, acc_f32[2 * i + j]);
4263                }
4264            }
4265        }  // for x
4266    }  // for y
4267    return;
4268#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
4269    ggml_gemm_q4_K_8x8_q8_K_generic(n, s, bs, vx, vy, nr, nc);
4270}
4271
4272void ggml_gemm_q5_K_8x8_q8_K(int                        n,
4273                             float * GGML_RESTRICT      s,
4274                             size_t                     bs,
4275                             const void * GGML_RESTRICT vx,
4276                             const void * GGML_RESTRICT vy,
4277                             int                        nr,
4278                             int                        nc) {
4279    constexpr int qk = QK_K;
4280    const int     nb = n / qk;
4281
4282    constexpr int ncols_interleaved = 8;
4283    constexpr int blocklen          = 8;
4284
4285    assert(n % qk == 0);
4286    assert(nr % 4 == 0);
4287    assert(nc % ncols_interleaved == 0);
4288
4289    UNUSED(nb);
4290    UNUSED(ncols_interleaved);
4291    UNUSED(blocklen);
4292
4293#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
4294    constexpr int    q8_k_blocklen = 4;
4295    constexpr int    col_pairs     = ncols_interleaved / 2;
4296    const uint8x16_t m4b           = vdupq_n_u8(0x0f);
4297    const uint8x16_t mone          = vdupq_n_u8(1);
4298    const uint8x16_t mtwo          = vdupq_n_u8(2);
4299
4300    // 8 accumulators: 2 row pairs × 4 col pairs
4301    float32x4_t acc_f32[blocklen];
4302
4303    for (int y = 0; y < nr / q8_k_blocklen; y++) {
4304        const block_q8_Kx4 * GGML_RESTRICT q8_ptr = (const block_q8_Kx4 *) vy + (y * nb);
4305
4306        for (int x = 0; x < nc / ncols_interleaved; x++) {
4307            const block_q5_Kx8 * GGML_RESTRICT q5_ptr = (const block_q5_Kx8 *) vx + (x * nb);
4308
4309            for (int i = 0; i < blocklen; i++) {
4310                acc_f32[i] = vdupq_n_f32(0);
4311            }
4312
4313            for (int b = 0; b < nb; b++) {
4314                // bsums pairs belongs to the same q8_k subblock
4315                const int16x8_t bsums[4]{
4316                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 0), vld1q_s16(q8_ptr[b].bsums + 16 * 0 + 8)),
4317                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 1), vld1q_s16(q8_ptr[b].bsums + 16 * 1 + 8)),
4318                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 2), vld1q_s16(q8_ptr[b].bsums + 16 * 2 + 8)),
4319                    vpaddq_s16(vld1q_s16(q8_ptr[b].bsums + 16 * 3), vld1q_s16(q8_ptr[b].bsums + 16 * 3 + 8)),
4320                };
4321                int16_t bsums_arr[4][8];
4322                for (int q8_row = 0; q8_row < 4; q8_row++) {
4323                    vst1q_s16(bsums_arr[q8_row], bsums[q8_row]);
4324                }
4325
4326                int32x4_t sb_acc[4];    // Aux accumulators to store subblock (partial) results
4327                int32x4_t acc[8];       // rows 01 stored in [0][1][2][3] rows 23 stored in [4][5][6][7]
4328                int32x4_t bias_acc[8];  // interleaved bias_acc: [0]->r0 0123, [1]->r0 4567, [2]->r1 0123 ...
4329                for (int i = 0; i < 8; i++) {
4330                    acc[i]      = vdupq_n_s32(0);
4331                    bias_acc[i] = vdupq_n_s32(0);
4332                }
4333
4334                // Load qh once per block and shift after each subblock
4335                const uint8_t * qh_base = q5_ptr[b].qh;
4336                uint8x16_t      qh[col_pairs][4];
4337                for (int cp = 0; cp < col_pairs; cp++) {
4338                    qh[cp][0] = vld1q_u8(qh_base + 16 * cp);
4339                    qh[cp][1] = vld1q_u8(qh_base + 16 * cp + 64);
4340                    qh[cp][2] = vld1q_u8(qh_base + 16 * cp + 128);
4341                    qh[cp][3] = vld1q_u8(qh_base + 16 * cp + 192);
4342                }
4343
4344                for (int sb = 0; sb < QK_K / 64; sb++) {
4345                    // Need scales for the low and high nibbles
4346                    // 2 * 12 = 24 bytes per subblock, 4 sbs -> 4 * 24 = 96 bytes total
4347                    int8_t    q5sb_scales[2][8];
4348                    int16x8_t q5sb_mins[2];  // int16 as its needed for bias_acc later
4349                    for (int i = 0; i < 2; i++) {
4350                        const int offset = sb * 24 + i * 12;
4351                        decode_q_Kx8_6bit_scales(&q5_ptr[b].scales[offset], &q5sb_mins[i], q5sb_scales[i]);
4352                    }
4353
4354                    // q8_ptr[b].qs has interleaved Q8 rows (01, 23)
4355                    const int8_t * q8_base = q8_ptr[b].qs + sb * 256;
4356
4357                    int8x16_t q8_qs_01[8];
4358                    int8x16_t q8_qs_23[8];
4359
4360                    // Load 32-byte per row pair, 1 subblock each time
4361                    for (int i = 0; i < 8; i++) {
4362                        const int offset = i * 32;  // 16 for row 01, 16 for row 23
4363                        q8_qs_01[i]      = vld1q_s8(q8_base + offset);
4364                        q8_qs_23[i]      = vld1q_s8(q8_base + offset + 16);
4365                    }
4366
4367                    const int8x16_t q8s[2][8] = {
4368                        { q8_qs_01[0], q8_qs_01[1], q8_qs_01[2], q8_qs_01[3], q8_qs_01[4], q8_qs_01[5], q8_qs_01[6],
4369                         q8_qs_01[7] },
4370                        { q8_qs_23[0], q8_qs_23[1], q8_qs_23[2], q8_qs_23[3], q8_qs_23[4], q8_qs_23[5], q8_qs_23[6],
4371                         q8_qs_23[7] },
4372                    };
4373
4374                    // Q5s columns iterated in pairs (01, 23, 45, 67)
4375                    for (int cp = 0; cp < col_pairs; cp++) {
4376                        for (int i = 0; i < 4; i++) {
4377                            sb_acc[i] = vdupq_n_s32(0);
4378                        }
4379
4380                        uint8x16_t qs_cp_0 = vld1q_u8(q5_ptr[b].qs + sb * QK_K + 16 * cp + 0);    // 0 .. 7 & 32..39
4381                        uint8x16_t qs_cp_1 = vld1q_u8(q5_ptr[b].qs + sb * QK_K + 16 * cp + 64);   // 8 ..15 & 40..47
4382                        uint8x16_t qs_cp_2 = vld1q_u8(q5_ptr[b].qs + sb * QK_K + 16 * cp + 128);  // 16..23 & 48..55
4383                        uint8x16_t qs_cp_3 = vld1q_u8(q5_ptr[b].qs + sb * QK_K + 16 * cp + 192);  // 24..31 & 56..63
4384
4385                        // This is the only part of the algorithm that differs with Q4_K
4386                        // Extract High bits and pack into 5 bit weights
4387                        uint8x16_t hbit_lo_0    = vandq_u8(qh[cp][0], mone);
4388                        uint8x16_t hbit_hi_0    = vshlq_n_u8(vandq_u8(qh[cp][0], mtwo), 3);
4389                        qh[cp][0]               = vshrq_n_u8(qh[cp][0], 2);
4390                        // Same as Q4_K, i8mm to dequantize the weights.
4391                        const int8x16_t qs_lo_0 = vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_cp_0, m4b), hbit_lo_0, 4));
4392                        int32x4_t       acc_0   = sb_acc[0];
4393                        acc_0                   = vmmlaq_s32(acc_0, qs_lo_0, q8s[0][0]);
4394                        int32x4_t acc_2         = sb_acc[2];
4395                        acc_2                   = vmmlaq_s32(acc_2, qs_lo_0, q8s[1][0]);
4396                        const int8x16_t qs_hi_0 = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_cp_0, 4), hbit_hi_0));
4397                        int32x4_t       acc_1   = sb_acc[1];
4398                        acc_1                   = vmmlaq_s32(acc_1, qs_hi_0, q8s[0][4]);
4399                        int32x4_t acc_3         = sb_acc[3];
4400                        acc_3                   = vmmlaq_s32(acc_3, qs_hi_0, q8s[1][4]);
4401
4402                        // Repeat for the other 3 columns (8..15, 16..23, 24..31)
4403                        uint8x16_t hbit_hi_1    = vshlq_n_u8(vandq_u8(qh[cp][1], mtwo), 3);
4404                        uint8x16_t hbit_lo_1    = vandq_u8(qh[cp][1], mone);
4405                        qh[cp][1]               = vshrq_n_u8(qh[cp][1], 2);
4406                        const int8x16_t qs_lo_1 = vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_cp_1, m4b), hbit_lo_1, 4));
4407                        acc_0                   = vmmlaq_s32(acc_0, qs_lo_1, q8s[0][1]);
4408                        acc_2                   = vmmlaq_s32(acc_2, qs_lo_1, q8s[1][1]);
4409                        const int8x16_t qs_hi_1 = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_cp_1, 4), hbit_hi_1));
4410                        acc_1                   = vmmlaq_s32(acc_1, qs_hi_1, q8s[0][5]);
4411                        acc_3                   = vmmlaq_s32(acc_3, qs_hi_1, q8s[1][5]);
4412
4413                        uint8x16_t hbit_hi_2    = vshlq_n_u8(vandq_u8(qh[cp][2], mtwo), 3);
4414                        uint8x16_t hbit_lo_2    = vandq_u8(qh[cp][2], mone);
4415                        qh[cp][2]               = vshrq_n_u8(qh[cp][2], 2);
4416                        const int8x16_t qs_lo_2 = vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_cp_2, m4b), hbit_lo_2, 4));
4417                        acc_0                   = vmmlaq_s32(acc_0, qs_lo_2, q8s[0][2]);
4418                        acc_2                   = vmmlaq_s32(acc_2, qs_lo_2, q8s[1][2]);
4419                        const int8x16_t qs_hi_2 = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_cp_2, 4), hbit_hi_2));
4420                        acc_1                   = vmmlaq_s32(acc_1, qs_hi_2, q8s[0][6]);
4421                        acc_3                   = vmmlaq_s32(acc_3, qs_hi_2, q8s[1][6]);
4422
4423                        uint8x16_t hbit_lo_3    = vandq_u8(qh[cp][3], mone);
4424                        uint8x16_t hbit_hi_3    = vshlq_n_u8(vandq_u8(qh[cp][3], mtwo), 3);
4425                        qh[cp][3]               = vshrq_n_u8(qh[cp][3], 2);
4426                        const int8x16_t qs_lo_3 = vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(qs_cp_3, m4b), hbit_lo_3, 4));
4427                        acc_0                   = vmmlaq_s32(acc_0, qs_lo_3, q8s[0][3]);
4428                        sb_acc[0]               = acc_0;
4429                        acc_2                   = vmmlaq_s32(acc_2, qs_lo_3, q8s[1][3]);
4430                        sb_acc[2]               = acc_2;
4431
4432                        // Scales[i] corresponds to column i
4433                        const int       scale_offset = cp * 2;
4434                        const int32_t   s0           = q5sb_scales[0][scale_offset];
4435                        const int32_t   s1           = q5sb_scales[0][scale_offset + 1];
4436                        const int32x4_t block_scale  = vcombine_s32(vdup_n_s32(s0), vdup_n_s32(s1));
4437                        acc[cp]                      = vmlaq_s32(acc[cp], sb_acc[0], block_scale);
4438                        acc[cp + 4]                  = vmlaq_s32(acc[cp + 4], sb_acc[2], block_scale);
4439
4440                        const int8x16_t qs_hi_3 = vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(qs_cp_3, 4), hbit_hi_3));
4441                        acc_1                   = vmmlaq_s32(acc_1, qs_hi_3, q8s[0][7]);
4442                        sb_acc[1]               = acc_1;
4443                        acc_3                   = vmmlaq_s32(acc_3, qs_hi_3, q8s[1][7]);
4444                        sb_acc[3]               = acc_3;
4445
4446                        const int32_t   s2           = q5sb_scales[1][scale_offset];
4447                        const int32_t   s3           = q5sb_scales[1][scale_offset + 1];
4448                        const int32x4_t block_scale2 = vcombine_s32(vdup_n_s32(s2), vdup_n_s32(s3));
4449                        acc[cp]                      = vmlaq_s32(acc[cp], sb_acc[1], block_scale2);
4450                        acc[cp + 4]                  = vmlaq_s32(acc[cp + 4], sb_acc[3], block_scale2);
4451                    }
4452
4453                    // Multiply Acc bsum + mins
4454                    for (int q8_row = 0; q8_row < 4; q8_row++) {
4455                        // Each pair of subblocks share the same bsums
4456                        // Load scalar bsum → broadcast to a vector (vdupq_n_s16(s)).
4457                        int16x4_t bsums_vec_lo = vdup_n_s16(bsums_arr[sb][q8_row * 2]);
4458                        int16x4_t bsums_vec_hi = vdup_n_s16(bsums_arr[sb][q8_row * 2 + 1]);
4459
4460                        bias_acc[2 * q8_row] =
4461                            vmlal_s16(bias_acc[2 * q8_row], bsums_vec_lo, vget_low_s16(q5sb_mins[0]));
4462                        bias_acc[2 * q8_row] =
4463                            vmlal_s16(bias_acc[2 * q8_row], bsums_vec_hi, vget_low_s16(q5sb_mins[1]));
4464                        bias_acc[2 * q8_row + 1] =
4465                            vmlal_s16(bias_acc[2 * q8_row + 1], bsums_vec_lo, vget_high_s16(q5sb_mins[0]));
4466                        bias_acc[2 * q8_row + 1] =
4467                            vmlal_s16(bias_acc[2 * q8_row + 1], bsums_vec_hi, vget_high_s16(q5sb_mins[1]));
4468                    }
4469                }  // for sb
4470
4471                // Reorder of i8mm output with bias and output layout
4472                for (int i = 0; i < 8; i++) {
4473                    int32x2x2_t aux = vzip_s32(vget_low_s32(acc[i]), vget_high_s32(acc[i]));
4474                    acc[i]          = vcombine_s32(aux.val[0], aux.val[1]);
4475                }
4476                int32x4_t reorder_acc[8] = {
4477                    vcombine_s32(vget_low_s32(acc[0]), vget_low_s32(acc[1])),
4478                    vcombine_s32(vget_low_s32(acc[2]), vget_low_s32(acc[3])),
4479                    vcombine_s32(vget_high_s32(acc[0]), vget_high_s32(acc[1])),
4480                    vcombine_s32(vget_high_s32(acc[2]), vget_high_s32(acc[3])),
4481                    vcombine_s32(vget_low_s32(acc[4]), vget_low_s32(acc[5])),
4482                    vcombine_s32(vget_low_s32(acc[6]), vget_low_s32(acc[7])),
4483                    vcombine_s32(vget_high_s32(acc[4]), vget_high_s32(acc[5])),
4484                    vcombine_s32(vget_high_s32(acc[6]), vget_high_s32(acc[7])),
4485                };
4486
4487                for (int i = 0; i < q8_k_blocklen; i++) {
4488                    for (int j = 0; j < 2; j++) {
4489                        float32x4_t       q8_d    = vdupq_n_f32(q8_ptr[b].d[i]);
4490                        float32x4_t       q5_dmin = vcvt_f32_f16(vld1_f16((const __fp16 *) (q5_ptr[b].dmin + j * 4)));
4491                        const float32x4_t dmins   = vmulq_f32(q5_dmin, q8_d);
4492
4493                        float32x4_t       q5_d  = vcvt_f32_f16(vld1_f16((const __fp16 *) (q5_ptr[b].d + j * 4)));
4494                        const float32x4_t scale = vmulq_f32(q5_d, q8_d);
4495
4496                        acc_f32[2 * i + j] = vmlsq_f32(acc_f32[2 * i + j], vcvtq_f32_s32(bias_acc[2 * i + j]), dmins);
4497                        acc_f32[2 * i + j] =
4498                            vmlaq_f32(acc_f32[2 * i + j], vcvtq_f32_s32(reorder_acc[2 * i + j]), scale);
4499                    }
4500                }
4501            }  // for b
4502
4503            // With the previous reorder, the tile is already in the correct memory layout.
4504            for (int i = 0; i < q8_k_blocklen; i++) {
4505                int row = y * q8_k_blocklen + i;
4506                for (int j = 0; j < 2; j++) {
4507                    int col    = x * ncols_interleaved + j * 4;
4508                    int offset = row * bs + col;
4509                    vst1q_f32(s + offset, acc_f32[2 * i + j]);
4510                }
4511            }
4512        }  // for x
4513    }  // for y
4514    return;
4515#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
4516    ggml_gemm_q5_K_8x8_q8_K_generic(n, s, bs, vx, vy, nr, nc);
4517}
4518
4519void ggml_gemm_q6_K_8x4_q8_K(int                        n,
4520                             float * GGML_RESTRICT      s,
4521                             size_t                     bs,
4522                             const void * GGML_RESTRICT vx,
4523                             const void * GGML_RESTRICT vy,
4524                             int                        nr,
4525                             int                        nc) {
4526    constexpr int qk = QK_K;
4527    const int     nb = n / qk;
4528
4529    constexpr int ncols_interleaved = 8;
4530    constexpr int blocklen          = 4;
4531
4532    assert(n % qk == 0);
4533    assert(nr % 4 == 0);
4534    assert(nc % ncols_interleaved == 0);
4535
4536    UNUSED(nb);
4537    UNUSED(ncols_interleaved);
4538    UNUSED(blocklen);
4539
4540#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
4541    constexpr int    q8_k_blocklen = 4;
4542    constexpr int    col_groups    = ncols_interleaved / 4;
4543    constexpr int    acc_size      = q8_k_blocklen * col_groups;  // 4 rows, 2 column groups
4544    const uint8x16_t m4b           = vdupq_n_u8(0x0f);
4545    const uint8x16_t mask_lo       = vdupq_n_u8(0x03);
4546    const uint8x16_t mask_hi       = vdupq_n_u8(0x30);
4547    const int8x16_t  m32s          = vdupq_n_s8(32);
4548
4549    float32x4_t acc_f32[acc_size];
4550
4551    for (int y = 0; y < nr / q8_k_blocklen; y++) {
4552        const block_q8_Kx4 * GGML_RESTRICT q8_ptr = (const block_q8_Kx4 *) vy + (y * nb);
4553
4554        for (int x = 0; x < nc / ncols_interleaved; x++) {
4555            const block_q6_Kx8 * GGML_RESTRICT q6_ptr = (const block_q6_Kx8 *) vx + (x * nb);
4556
4557            for (int i = 0; i < acc_size; i++) {
4558                acc_f32[i] = vdupq_n_f32(0);
4559            }
4560
4561            for (int b = 0; b < nb; b++) {
4562                float32x4_t q6_d_0123 = vcvt_f32_f16(vld1_f16((const __fp16 *) q6_ptr[b].d));
4563                float32x4_t q6_d_4567 = vcvt_f32_f16(vld1_f16((const __fp16 *) q6_ptr[b].d + 4));
4564                float32x4_t q8_d_0123 = vld1q_f32(q8_ptr[b].d);
4565
4566                float32x4_t sbd_scale_0123[q8_k_blocklen];
4567                float32x4_t sbd_scale_4567[q8_k_blocklen];
4568
4569                sbd_scale_0123[0] = vmulq_laneq_f32(q6_d_0123, q8_d_0123, 0);
4570                sbd_scale_4567[0] = vmulq_laneq_f32(q6_d_4567, q8_d_0123, 0);
4571                sbd_scale_0123[1] = vmulq_laneq_f32(q6_d_0123, q8_d_0123, 1);
4572                sbd_scale_4567[1] = vmulq_laneq_f32(q6_d_4567, q8_d_0123, 1);
4573                sbd_scale_0123[2] = vmulq_laneq_f32(q6_d_0123, q8_d_0123, 2);
4574                sbd_scale_4567[2] = vmulq_laneq_f32(q6_d_4567, q8_d_0123, 2);
4575                sbd_scale_0123[3] = vmulq_laneq_f32(q6_d_0123, q8_d_0123, 3);
4576                sbd_scale_4567[3] = vmulq_laneq_f32(q6_d_4567, q8_d_0123, 3);
4577
4578                int32x4_t acc_s32[acc_size];
4579                for (int i = 0; i < acc_size; i++) {
4580                    acc_s32[i] = vdupq_n_s32(0);
4581                }
4582
4583                int16_t q6_scales[8 * 16];
4584                for (int i = 0; i < 16; i++) {
4585                    int16x8_t scales = vmovl_s8(vld1_s8(q6_ptr[b].scales + i * 8));
4586                    vst1q_s16(q6_scales + i * 8, scales);
4587                }
4588
4589                for (int half = 0; half < 2; half++) {
4590                    const uint8_t * ql_base = q6_ptr[b].ql + half * 512;
4591                    const uint8_t * qh_base = q6_ptr[b].qh + half * 256;
4592
4593                    for (int sb = 0; sb < QK_K / 64; sb++) {
4594                        int32x4_t acc_lo[acc_size];
4595                        int32x4_t acc_hi[acc_size];
4596                        for (int i = 0; i < acc_size; i++) {
4597                            acc_lo[i] = vdupq_n_s32(0);
4598                            acc_hi[i] = vdupq_n_s32(0);
4599                        }
4600
4601                        const int8_t * q8_base_l = q8_ptr[b].qs + half * 512 + sb * 64;
4602                        const int8_t * q8_base_h = q8_ptr[b].qs + half * 512 + 256 + sb * 64;
4603
4604                        // 4 rows * 16 elements per scale
4605                        // 4 reads of 16 bytes each
4606                        constexpr int reads_per_sb = 4;
4607                        int8x16_t     q8_l[reads_per_sb];
4608                        int8x16_t     q8_h[reads_per_sb];
4609                        for (int k = 0; k < reads_per_sb; k++) {
4610                            q8_l[k] = vld1q_s8(q8_base_l + 16 * k);
4611                            q8_h[k] = vld1q_s8(q8_base_h + 16 * k);
4612                        }
4613
4614                        const int ql_off_base = sb * QK_K / 2;
4615                        const int qh_off_base = ql_off_base & 255;
4616
4617                        uint8x16_t q6_ql_0123[reads_per_sb];
4618                        uint8x16_t q6_ql_4567[reads_per_sb];
4619                        uint8x16_t q6_qh_0123[reads_per_sb];
4620                        uint8x16_t q6_qh_4567[reads_per_sb];
4621
4622                        for (int k = 0; k < reads_per_sb; k++) {
4623                            q6_ql_0123[k] = vld1q_u8(ql_base + ql_off_base + k * 32);
4624                            q6_ql_4567[k] = vld1q_u8(ql_base + ql_off_base + k * 32 + 16);
4625                            q6_qh_0123[k] = vld1q_u8(qh_base + qh_off_base + k * 32);
4626                            q6_qh_4567[k] = vld1q_u8(qh_base + qh_off_base + k * 32 + 16);
4627                        }
4628
4629                        if (sb > 1) {
4630                            for (int k = 0; k < reads_per_sb; k++) {
4631                                q6_qh_0123[k] = vshrq_n_u8(q6_qh_0123[k], 2);
4632                                q6_qh_4567[k] = vshrq_n_u8(q6_qh_4567[k], 2);
4633                            }
4634                        }
4635
4636                        for (int k = 0; k < reads_per_sb; k++) {
4637                            // q = (ql | qh) - 32
4638                            const uint8x16_t hbit_lo_0123 = vandq_u8(q6_qh_0123[k], mask_lo);
4639                            const uint8x16_t hbit_hi_0123 = vandq_u8(q6_qh_0123[k], mask_hi);
4640                            const uint8x16_t hbit_lo_4567 = vandq_u8(q6_qh_4567[k], mask_lo);
4641                            const uint8x16_t hbit_hi_4567 = vandq_u8(q6_qh_4567[k], mask_hi);
4642
4643                            const int8x16_t q6_0123_lo = vsubq_s8(
4644                                vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(q6_ql_0123[k], m4b), hbit_lo_0123, 4)), m32s);
4645                            const int8x16_t q6_0123_hi = vsubq_s8(
4646                                vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_ql_0123[k], 4), hbit_hi_0123)), m32s);
4647
4648                            acc_lo[0] = vdotq_laneq_s32(acc_lo[0], q6_0123_lo, q8_l[k], 0);  //  0..3  r0 c0123
4649                            acc_lo[1] = vdotq_laneq_s32(acc_lo[1], q6_0123_lo, q8_l[k], 1);  //  0..3  r1 c0123
4650                            acc_lo[2] = vdotq_laneq_s32(acc_lo[2], q6_0123_lo, q8_l[k], 2);  //  0..3  r2 c0123
4651                            acc_lo[3] = vdotq_laneq_s32(acc_lo[3], q6_0123_lo, q8_l[k], 3);  //  0..3  r3 c0123
4652
4653                            acc_hi[0] = vdotq_laneq_s32(acc_hi[0], q6_0123_hi, q8_h[k], 0);  // 64..67 r0 c0123
4654                            acc_hi[1] = vdotq_laneq_s32(acc_hi[1], q6_0123_hi, q8_h[k], 1);  // 64..67 r1 c0123
4655                            acc_hi[2] = vdotq_laneq_s32(acc_hi[2], q6_0123_hi, q8_h[k], 2);  // 64..67 r2 c0123
4656                            acc_hi[3] = vdotq_laneq_s32(acc_hi[3], q6_0123_hi, q8_h[k], 3);  // 64..67 r3 c0123
4657
4658                            const int8x16_t q6_4567_lo = vsubq_s8(
4659                                vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(q6_ql_4567[k], m4b), hbit_lo_4567, 4)), m32s);
4660                            const int8x16_t q6_4567_hi = vsubq_s8(
4661                                vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_ql_4567[k], 4), hbit_hi_4567)), m32s);
4662
4663                            acc_lo[4] = vdotq_laneq_s32(acc_lo[4], q6_4567_lo, q8_l[k], 0);  //  0..3  r0 c4567
4664                            acc_lo[5] = vdotq_laneq_s32(acc_lo[5], q6_4567_lo, q8_l[k], 1);  //  0..3  r1 c4567
4665                            acc_lo[6] = vdotq_laneq_s32(acc_lo[6], q6_4567_lo, q8_l[k], 2);  //  0..3  r2 c4567
4666                            acc_lo[7] = vdotq_laneq_s32(acc_lo[7], q6_4567_lo, q8_l[k], 3);  //  0..3  r3 c4567
4667
4668                            acc_hi[4] = vdotq_laneq_s32(acc_hi[4], q6_4567_hi, q8_h[k], 0);  // 64..67 r0 c4567
4669                            acc_hi[5] = vdotq_laneq_s32(acc_hi[5], q6_4567_hi, q8_h[k], 1);  // 64..67 r1 c4567
4670                            acc_hi[6] = vdotq_laneq_s32(acc_hi[6], q6_4567_hi, q8_h[k], 2);  // 64..67 r2 c4567
4671                            acc_hi[7] = vdotq_laneq_s32(acc_hi[7], q6_4567_hi, q8_h[k], 3);  // 64..67 r3 c4567
4672                        }
4673
4674                        // Scale and bias
4675                        const int scale_idx_l = half * 8 + sb;
4676                        const int scale_idx_h = half * 8 + sb + 4;
4677
4678                        for (int g = 0; g < col_groups; g++) {
4679                            const int16x4_t scales_l16  = vld1_s16(q6_scales + scale_idx_l * 8 + g * 4);
4680                            const int16x4_t scales_h16  = vld1_s16(q6_scales + scale_idx_h * 8 + g * 4);
4681                            const int32x4_t scale_vec_l = vmovl_s16(scales_l16);
4682                            const int32x4_t scale_vec_h = vmovl_s16(scales_h16);
4683                            const int       acc_offset  = g * q8_k_blocklen;
4684
4685                            for (int row = 0; row < q8_k_blocklen; row++) {
4686                                const int idx = row * 2 + g;
4687                                acc_s32[idx]  = vmlaq_s32(acc_s32[idx], acc_lo[acc_offset + row], scale_vec_l);
4688                                acc_s32[idx]  = vmlaq_s32(acc_s32[idx], acc_hi[acc_offset + row], scale_vec_h);
4689                            }
4690                        }
4691                    }
4692                }
4693
4694                // Finally we apply the superblock scales
4695                for (int row = 0; row < q8_k_blocklen; row++) {
4696                    const int       idx0     = 2 * row;
4697                    const int       idx1     = 2 * row + 1;
4698                    const int32x4_t acc_0123 = acc_s32[idx0];
4699                    const int32x4_t acc_4567 = acc_s32[idx1];
4700
4701                    acc_f32[idx0] = vmlaq_f32(acc_f32[idx0], vcvtq_f32_s32(acc_0123), sbd_scale_0123[row]);
4702                    acc_f32[idx1] = vmlaq_f32(acc_f32[idx1], vcvtq_f32_s32(acc_4567), sbd_scale_4567[row]);
4703                }
4704            }  // for b
4705
4706            for (int i = 0; i < q8_k_blocklen; i++) {
4707                int row = y * q8_k_blocklen + i;
4708                for (int j = 0; j < 2; j++) {
4709                    int col    = x * ncols_interleaved + j * 4;
4710                    int offset = row * bs + col;
4711                    vst1q_f32(s + offset, acc_f32[2 * i + j]);
4712                }
4713            }
4714        }  // for x
4715    }  // for y
4716    return;
4717#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
4718    ggml_gemm_q6_K_8x4_q8_K_generic(n, s, bs, vx, vy, nr, nc);
4719}
4720
4721void ggml_gemm_q6_K_8x8_q8_K(int                        n,
4722                             float * GGML_RESTRICT      s,
4723                             size_t                     bs,
4724                             const void * GGML_RESTRICT vx,
4725                             const void * GGML_RESTRICT vy,
4726                             int                        nr,
4727                             int                        nc) {
4728    constexpr int qk = QK_K;
4729    const int     nb = n / qk;
4730
4731    constexpr int ncols_interleaved = 8;
4732    constexpr int blocklen          = 8;
4733
4734    assert(n % qk == 0);
4735    assert(nr % 4 == 0);
4736    assert(nc % ncols_interleaved == 0);
4737
4738    UNUSED(nb);
4739    UNUSED(ncols_interleaved);
4740    UNUSED(blocklen);
4741
4742#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
4743    constexpr int    q8_k_blocklen = 4;
4744    const uint8x16_t m4b           = vdupq_n_u8(0x0f);
4745    const uint8x16_t mask_lo       = vdupq_n_u8(0x03);
4746    const uint8x16_t mask_hi       = vdupq_n_u8(0x30);
4747    const int8x16_t  m32s          = vdupq_n_s8(32);
4748
4749    // 8 accumulators: 4 q8 rows × 2 col groups (0-3, 4-7)
4750    float32x4_t acc_f32[blocklen];
4751
4752    for (int y = 0; y < nr / q8_k_blocklen; y++) {
4753        const block_q8_Kx4 * GGML_RESTRICT q8_ptr = (const block_q8_Kx4 *) vy + (y * nb);
4754
4755        for (int x = 0; x < nc / ncols_interleaved; x++) {
4756            const block_q6_Kx8 * GGML_RESTRICT q6_ptr = (const block_q6_Kx8 *) vx + (x * nb);
4757
4758            for (int i = 0; i < blocklen; i++) {
4759                acc_f32[i] = vdupq_n_f32(0);
4760            }
4761
4762            for (int b = 0; b < nb; b++) {
4763                int32x4_t acc[8];  // rows 01 stored in [0][1][2][3], rows 23 stored in [4][5][6][7]
4764                for (int i = 0; i < 8; i++) {
4765                    acc[i] = vdupq_n_s32(0);
4766                }
4767
4768                // Q6_K has simple 8-bit scales, 16 per block (one per 16 values)
4769                // Reused for bias and dequantization later
4770                int16_t q6_scales[16 * 8];
4771                for (int i = 0; i < 16; ++i) {
4772                    int16x8_t s16 = vmovl_s8(vld1_s8(q6_ptr[b].scales + i * 8));
4773                    vst1q_s16(q6_scales + i * 8, s16);
4774                }
4775
4776                // Process two 128-value halves per superblock
4777                for (int half = 0; half < 2; half++) {
4778
4779                    const uint8_t * ql_base = q6_ptr[b].ql + half * 512;
4780                    const uint8_t * qh_base = q6_ptr[b].qh + half * 256;
4781
4782                    // A subblock (sb) is a set of weights that share the scale
4783                    // Since q6_K scales are per 16 elements
4784                    // num sbs -> 256 elements / (16 elements/scale * 2 elements/byte * 2 halves)
4785                    for (int sb = 0; sb < QK_K / 64; sb++) {
4786                        // Q6_K weight index increasing by 64 instead of 32 requires
4787                        // loading various q8 memory regions
4788                        const int8_t * q8_base_l = q8_ptr[b].qs + half * 512 + sb * 64;
4789                        const int8_t * q8_base_h = q8_ptr[b].qs + half * 512 + 256 + sb * 64;
4790
4791                        int8x16_t q8_l_01[2];
4792                        int8x16_t q8_l_23[2];
4793                        for (int i = 0; i < 2; i++) {
4794                            const int offset = i * 32;
4795                            q8_l_01[i]       = vld1q_s8(q8_base_l + offset);       // 0..7 & 8..15 (r01)
4796                            q8_l_23[i]       = vld1q_s8(q8_base_l + offset + 16);  // 0..7 & 8..15 (r23)
4797                        }
4798
4799                        int8x16_t q8_h_01[2];
4800                        int8x16_t q8_h_23[2];
4801                        for (int i = 0; i < 2; i++) {
4802                            const int offset = i * 32;
4803                            q8_h_01[i]       = vld1q_s8(q8_base_h + offset);
4804                            q8_h_23[i]       = vld1q_s8(q8_base_h + offset + 16);
4805                        }
4806
4807                        const int ql_off_base = sb * QK_K / 2;
4808
4809                        uint8x16_t q6_ql_0[4];
4810                        uint8x16_t q6_ql_1[4];
4811                        for (int k = 0; k < 4; k++) {
4812                            q6_ql_0[k] = vld1q_u8(ql_base + ql_off_base + 16 * k);
4813                            q6_ql_1[k] = vld1q_u8(ql_base + ql_off_base + 64 + 16 * k);
4814                        }
4815
4816                        const int  qh_off_base = (sb * QK_K / 2) & 255;  // wrap after 256 bytes
4817                        uint8x16_t q6_qh_0[4];
4818                        uint8x16_t q6_qh_1[4];
4819                        for (int k = 0; k < 4; k++) {
4820                            q6_qh_0[k] = vld1q_u8(qh_base + qh_off_base + 16 * k);
4821                            q6_qh_1[k] = vld1q_u8(qh_base + qh_off_base + 64 + 16 * k);
4822                        }
4823
4824                        // Adjust for the proper high bits (Sb 2 and 3)
4825                        if (sb > 1) {
4826                            for (int k = 0; k < 4; k++) {
4827                                q6_qh_0[k] = vshrq_n_u8(q6_qh_0[k], 2);
4828                                q6_qh_1[k] = vshrq_n_u8(q6_qh_1[k], 2);
4829                            }
4830                        }
4831
4832                        // Process column pairs (0-1, 2-3, 4-5, 6-7)
4833                        for (int cp = 0; cp < ncols_interleaved / 2; cp++) {
4834                            const uint8x16_t q6_qs_cp_0_l = q6_ql_0[cp];
4835                            const uint8x16_t q6_qs_cp_1_l = q6_ql_1[cp];
4836                            const uint8x16_t q6_qs_cp_0_h = q6_qh_0[cp];
4837                            const uint8x16_t q6_qs_cp_1_h = q6_qh_1[cp];
4838
4839                            // Extract high 2 bits for upper nibble reconstruction
4840                            const uint8x16_t q6_qs_cp_0_hh = vandq_u8(q6_qs_cp_0_h, mask_hi);
4841                            const uint8x16_t q6_qs_cp_1_hh = vandq_u8(q6_qs_cp_1_h, mask_hi);
4842
4843                            // q6 = (low4 | high2<<4) - 32
4844                            // Use vsliq_n_u8 to combine shift-left-insert in one instruction (like Q5_K)
4845                            const int8x16_t q6_l0 = vsubq_s8(
4846                                vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(q6_qs_cp_0_l, m4b), vandq_u8(q6_qs_cp_0_h, mask_lo), 4)),
4847                                m32s);
4848                            const int8x16_t q6_l1 = vsubq_s8(
4849                                vreinterpretq_s8_u8(vsliq_n_u8(vandq_u8(q6_qs_cp_1_l, m4b), vandq_u8(q6_qs_cp_1_h, mask_lo), 4)),
4850                                m32s);
4851                            const int8x16_t q6_h0 = vsubq_s8(
4852                                vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_qs_cp_0_l, 4), q6_qs_cp_0_hh)), m32s);
4853                            const int8x16_t q6_h1 = vsubq_s8(
4854                                vreinterpretq_s8_u8(vorrq_u8(vshrq_n_u8(q6_qs_cp_1_l, 4), q6_qs_cp_1_hh)), m32s);
4855
4856                            // row pair 0, base_l
4857                            int32x4_t sb_acc_0l = vmmlaq_s32(vdupq_n_s32(0), q6_l0, q8_l_01[0]);
4858                            sb_acc_0l           = vmmlaq_s32(sb_acc_0l, q6_l1, q8_l_01[1]);
4859                            // row pair 0, base_h
4860                            int32x4_t sb_acc_0h = vmmlaq_s32(vdupq_n_s32(0), q6_h0, q8_h_01[0]);
4861                            sb_acc_0h           = vmmlaq_s32(sb_acc_0h, q6_h1, q8_h_01[1]);
4862                            // row pair 1, base_l
4863                            int32x4_t sb_acc_1l = vmmlaq_s32(vdupq_n_s32(0), q6_l0, q8_l_23[0]);
4864                            sb_acc_1l           = vmmlaq_s32(sb_acc_1l, q6_l1, q8_l_23[1]);
4865                            // row pair 1, base_h
4866                            int32x4_t sb_acc_1h = vmmlaq_s32(vdupq_n_s32(0), q6_h0, q8_h_23[0]);
4867                            sb_acc_1h           = vmmlaq_s32(sb_acc_1h, q6_h1, q8_h_23[1]);
4868
4869                            const int scale_idx_l = half * 8 + sb;
4870                            const int scale_idx_h = half * 8 + sb + 4;
4871
4872                            const int32x4_t scale_vec_l = {
4873                                q6_scales[scale_idx_l * 8 + cp * 2 + 0],
4874                                q6_scales[scale_idx_l * 8 + cp * 2 + 0],
4875                                q6_scales[scale_idx_l * 8 + cp * 2 + 1],
4876                                q6_scales[scale_idx_l * 8 + cp * 2 + 1],
4877                            };
4878                            const int32x4_t scale_vec_h = {
4879                                q6_scales[scale_idx_h * 8 + cp * 2 + 0],
4880                                q6_scales[scale_idx_h * 8 + cp * 2 + 0],
4881                                q6_scales[scale_idx_h * 8 + cp * 2 + 1],
4882                                q6_scales[scale_idx_h * 8 + cp * 2 + 1],
4883                            };
4884
4885                            acc[cp]     = vmlaq_s32(acc[cp], sb_acc_0l, scale_vec_l);
4886                            acc[cp]     = vmlaq_s32(acc[cp], sb_acc_0h, scale_vec_h);
4887                            acc[cp + 4] = vmlaq_s32(acc[cp + 4], sb_acc_1l, scale_vec_l);
4888                            acc[cp + 4] = vmlaq_s32(acc[cp + 4], sb_acc_1h, scale_vec_h);
4889                        }
4890                    }
4891                }  // for half
4892
4893                // Reorder i8mm output to match memory layout
4894                for (int i = 0; i < 8; i++) {
4895                    int32x2x2_t aux = vzip_s32(vget_low_s32(acc[i]), vget_high_s32(acc[i]));
4896                    acc[i]          = vcombine_s32(aux.val[0], aux.val[1]);
4897                }
4898                int32x4_t reorder_acc[8] = {
4899                    vcombine_s32(vget_low_s32(acc[0]), vget_low_s32(acc[1])),
4900                    vcombine_s32(vget_low_s32(acc[2]), vget_low_s32(acc[3])),
4901                    vcombine_s32(vget_high_s32(acc[0]), vget_high_s32(acc[1])),
4902                    vcombine_s32(vget_high_s32(acc[2]), vget_high_s32(acc[3])),
4903                    vcombine_s32(vget_low_s32(acc[4]), vget_low_s32(acc[5])),
4904                    vcombine_s32(vget_low_s32(acc[6]), vget_low_s32(acc[7])),
4905                    vcombine_s32(vget_high_s32(acc[4]), vget_high_s32(acc[5])),
4906                    vcombine_s32(vget_high_s32(acc[6]), vget_high_s32(acc[7])),
4907                };
4908
4909                // Apply superblock scale (no mins for q6_K)
4910                for (int i = 0; i < q8_k_blocklen; i++) {
4911                    for (int j = 0; j < 2; j++) {
4912                        float32x4_t       q8_d  = vdupq_n_f32(q8_ptr[b].d[i]);
4913                        float32x4_t       q6_d  = vcvt_f32_f16(vld1_f16((const __fp16 *) (q6_ptr[b].d + j * 4)));
4914                        const float32x4_t scale = vmulq_f32(q6_d, q8_d);
4915
4916                        acc_f32[2 * i + j] =
4917                            vmlaq_f32(acc_f32[2 * i + j], vcvtq_f32_s32(reorder_acc[2 * i + j]), scale);
4918                    }
4919                }
4920            }  // for b
4921
4922            // Store results
4923            for (int i = 0; i < q8_k_blocklen; i++) {
4924                int row = y * q8_k_blocklen + i;
4925                for (int j = 0; j < 2; j++) {
4926                    int col    = x * ncols_interleaved + j * 4;
4927                    int offset = row * bs + col;
4928                    vst1q_f32(s + offset, acc_f32[2 * i + j]);
4929                }
4930            }
4931        }  // for x
4932    }  // for y
4933    return;
4934#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
4935    ggml_gemm_q6_K_8x8_q8_K_generic(n, s, bs, vx, vy, nr, nc);
4936}
4937
4938void ggml_gemm_q8_0_4x4_q8_0(int                        n,
4939                             float * GGML_RESTRICT      s,
4940                             size_t                     bs,
4941                             const void * GGML_RESTRICT vx,
4942                             const void * GGML_RESTRICT vy,
4943                             int                        nr,
4944                             int                        nc) {
4945    const int qk                = QK8_0;
4946    const int nb                = n / qk;
4947    const int ncols_interleaved = 4;
4948    const int blocklen          = 4;
4949
4950    assert(n % qk == 0);
4951    assert(nr % 4 == 0);
4952    assert(nc % ncols_interleaved == 0);
4953
4954    UNUSED(nb);
4955    UNUSED(ncols_interleaved);
4956    UNUSED(blocklen);
4957
4958#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
4959    for (int y = 0; y < nr / 4; y++) {
4960        const block_q8_0x4 * a_ptr = (const block_q8_0x4 *) vy + (y * nb);
4961        for (int x = 0; x < nc / ncols_interleaved; x++) {
4962            const block_q8_0x4 * b_ptr = (const block_q8_0x4 *) vx + (x * nb);
4963
4964            float32x4_t sumf[4];
4965            for (int m = 0; m < 4; m++) {
4966                sumf[m] = vdupq_n_f32(0);
4967            }
4968
4969            for (int l = 0; l < nb; l++) {
4970                float32x4_t a_d = vcvt_f32_f16(vld1_f16((const float16_t *) a_ptr[l].d));
4971                float32x4_t b_d = vcvt_f32_f16(vld1_f16((const float16_t *) b_ptr[l].d));
4972
4973                int32x4_t sumi_0 = vdupq_n_s32(0);
4974                int32x4_t sumi_1 = vdupq_n_s32(0);
4975                int32x4_t sumi_2 = vdupq_n_s32(0);
4976                int32x4_t sumi_3 = vdupq_n_s32(0);
4977
4978                for (int k_group = 0; k_group < 8; k_group += 4) {
4979                    int8x16x4_t a = vld1q_s8_x4(a_ptr[l].qs + 16 * k_group);
4980                    int8x16x4_t b = vld1q_s8_x4(b_ptr[l].qs + 16 * k_group);
4981
4982                    for (int k = 0; k < 4; k++) {
4983                        sumi_0 = vdotq_laneq_s32(sumi_0, b.val[k], a.val[k], 0);
4984                        sumi_1 = vdotq_laneq_s32(sumi_1, b.val[k], a.val[k], 1);
4985                        sumi_2 = vdotq_laneq_s32(sumi_2, b.val[k], a.val[k], 2);
4986                        sumi_3 = vdotq_laneq_s32(sumi_3, b.val[k], a.val[k], 3);
4987                    }
4988                }
4989
4990                sumf[0] = vmlaq_f32(sumf[0], vmulq_laneq_f32(b_d, a_d, 0), vcvtq_f32_s32(sumi_0));
4991                sumf[1] = vmlaq_f32(sumf[1], vmulq_laneq_f32(b_d, a_d, 1), vcvtq_f32_s32(sumi_1));
4992                sumf[2] = vmlaq_f32(sumf[2], vmulq_laneq_f32(b_d, a_d, 2), vcvtq_f32_s32(sumi_2));
4993                sumf[3] = vmlaq_f32(sumf[3], vmulq_laneq_f32(b_d, a_d, 3), vcvtq_f32_s32(sumi_3));
4994            }
4995
4996            for (int m = 0; m < 4; m++) {
4997                vst1q_f32(s + (y * 4 + m) * bs + x * 4, sumf[m]);
4998            }
4999        }
5000    }
5001    return;
5002#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
5003    ggml_gemm_q8_0_4x4_q8_0_generic(n, s, bs, vx, vy, nr, nc);
5004}
5005
5006void ggml_gemm_q8_0_4x8_q8_0(int                        n,
5007                             float * GGML_RESTRICT      s,
5008                             size_t                     bs,
5009                             const void * GGML_RESTRICT vx,
5010                             const void * GGML_RESTRICT vy,
5011                             int                        nr,
5012                             int                        nc) {
5013    const int qk                = QK8_0;
5014    const int nb                = n / qk;
5015    const int ncols_interleaved = 4;
5016    const int blocklen          = 8;
5017
5018    assert(n % qk == 0);
5019    assert(nr % 4 == 0);
5020    assert(nc % ncols_interleaved == 0);
5021
5022    UNUSED(nb);
5023    UNUSED(ncols_interleaved);
5024    UNUSED(blocklen);
5025
5026#if defined(__aarch64__) && defined(__ARM_FEATURE_SVE) && defined(__ARM_FEATURE_MATMUL_INT8)
5027    if (svcntb() * 8 == 256) {
5028        const block_q8_0x4 * b_ptr_base = (const block_q8_0x4 *) vx;
5029
5030        static const uint32_t idx_arr[8] = {0, 1, 4, 5, 2, 3, 6, 7};
5031        svuint32_t idx = svld1(svptrue_b32(), idx_arr);
5032        static const uint32_t idx_arr1[8] = {0, 1, 2, 3, 1, 2, 3, 0};
5033        svuint32_t idx_sc1 = svld1(svptrue_b32(), idx_arr1);
5034        static const uint32_t idx_arr2[8] = {0, 1, 2, 3, 0, 1, 2, 3};
5035        svuint32_t idx_sc2 = svld1(svptrue_b32(), idx_arr2);
5036
5037        for (int y = 0; y < nr; y += 4) {
5038            const block_q8_0x4 * a_ptr_base = (const block_q8_0x4 *) vy + (y / 4) * nb;
5039
5040            for (int x = 0; x < nc; x += ncols_interleaved) {
5041                const block_q8_0x4 * b_ptr = b_ptr_base + (x / 4) * nb;
5042                const block_q8_0x4 * a_ptr = a_ptr_base;
5043
5044                svfloat32_t acc_f32_01 = svdup_f32(0);
5045                svfloat32_t acc_f32_23 = svdup_f32(0);
5046
5047                for (int b = 0; b < nb; b++) {
5048
5049                    svint32_t acc_01 = svdup_s32(0);
5050                    svint32_t acc_23 = svdup_s32(0);
5051
5052                    // Process 4 chunks of 8 positions each
5053                    for (int chunk = 0; chunk < 4; chunk++) {
5054                        svint8_t s_a01 = svld1rq_s8(svptrue_b8(), a_ptr->qs + chunk * 32);
5055                        svint8_t s_a23 = svld1rq_s8(svptrue_b8(), a_ptr->qs + chunk * 32 + 16);
5056                        svint8_t s_b0123 = svld1_s8(svptrue_b8(), b_ptr->qs + chunk * 32);
5057
5058                        acc_01 = svmmla_s32(acc_01, s_a01, s_b0123);
5059                        acc_23 = svmmla_s32(acc_23, s_a23, s_b0123);
5060                    }
5061
5062                    // Reorder outputs from 2×2 tiles to row-major
5063                    // acc[01] = [r0c0, r0c1, r1c0, r1c1, r0c2, r0c3, r1c2, r1c3]
5064                    // acc[23] = [r2c0, r2c1, r3c0, r3c1, r2c2, r2c3, r3c2, r3c3]
5065
5066                    svint32_t row01 = svtbl_s32(acc_01, idx);
5067                    svint32_t row23 = svtbl_s32(acc_23, idx);
5068
5069                    svfloat16_t temp1 = svld1_f16(svptrue_pat_b16(SV_VL4), (const __fp16 *) a_ptr->d);
5070                    svfloat16_t temp2 = svld1_f16(svptrue_pat_b16(SV_VL4), (const __fp16 *) b_ptr->d);
5071                    svfloat32_t sv_a_d = svtbl_f32(svcvt_f32_f16_x(svptrue_b32(), svzip1_f16(temp1, temp1)), idx_sc1);
5072                    svfloat32_t sv_b_d = svtbl_f32(svcvt_f32_f16_x(svptrue_b32(), svzip1_f16(temp2, temp2)), idx_sc2);
5073
5074                    acc_f32_01 = svmla_f32_x(svptrue_b32(), acc_f32_01, svcvt_f32_s32_x(svptrue_b32(), row01), svmul_lane_f32(sv_b_d, sv_a_d, 0));
5075                    acc_f32_23 = svmla_f32_x(svptrue_b32(), acc_f32_23, svcvt_f32_s32_x(svptrue_b32(), row23), svmul_lane_f32(sv_b_d, sv_a_d, 2));
5076                    a_ptr++;
5077                    b_ptr++;
5078                }
5079
5080                svbool_t pg4 = svptrue_pat_b32(SV_VL4);
5081                svst1_f32(pg4, s + (y+0) * bs + x, acc_f32_01);
5082                svst1_f32(pg4, s + (y+1) * bs + x, svext_f32(acc_f32_01, acc_f32_01, 4));
5083                svst1_f32(pg4, s + (y+2) * bs + x, acc_f32_23);
5084                svst1_f32(pg4, s + (y+3) * bs + x, svext_f32(acc_f32_23, acc_f32_23, 4));
5085            }
5086        }
5087        return;
5088    }
5089#endif  // SVE compile-time end
5090
5091#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
5092    const block_q8_0x4 * b_ptr_base = (const block_q8_0x4 *) vx;
5093
5094    for (int y = 0; y < nr; y += 4) {
5095        const block_q8_0x4 * a_ptr_base = (const block_q8_0x4 *) vy + (y / 4) * nb;
5096
5097        for (int x = 0; x < nc; x += ncols_interleaved) {
5098            const block_q8_0x4 * b_ptr = b_ptr_base + (x / 4) * nb;
5099            const block_q8_0x4 * a_ptr = a_ptr_base;
5100
5101            float32x4_t acc_f32[4];
5102            for (int i = 0; i < 4; i++) {
5103                acc_f32[i] = vdupq_n_f32(0);
5104            }
5105
5106            for (int b = 0; b < nb; b++) {
5107                int32x4_t acc[4];
5108                for (int i = 0; i < 4; i++) {
5109                    acc[i] = vdupq_n_s32(0);
5110                }
5111
5112                // Process 4 chunks of 8 positions each
5113                for (int chunk = 0; chunk < 4; chunk++) {
5114                    int8x16_t a01 = vld1q_s8(a_ptr->qs + chunk * 32);
5115                    int8x16_t a23 = vld1q_s8(a_ptr->qs + chunk * 32 + 16);
5116                    int8x16_t b01 = vld1q_s8(b_ptr->qs + chunk * 32);
5117                    int8x16_t b23 = vld1q_s8(b_ptr->qs + chunk * 32 + 16);
5118
5119                    acc[0] = vmmlaq_s32(acc[0], a01, b01);
5120                    acc[1] = vmmlaq_s32(acc[1], a01, b23);
5121                    acc[2] = vmmlaq_s32(acc[2], a23, b01);
5122                    acc[3] = vmmlaq_s32(acc[3], a23, b23);
5123                }
5124
5125                // Reorder outputs from 2×2 tiles to row-major
5126                // acc[0] = [r0c0, r0c1, r1c0, r1c1]
5127                // acc[1] = [r0c2, r0c3, r1c2, r1c3]
5128                // acc[2] = [r2c0, r2c1, r3c0, r3c1]
5129                // acc[3] = [r2c2, r2c3, r3c2, r3c3]
5130                int32x4_t row0 = vcombine_s32(vget_low_s32(acc[0]), vget_low_s32(acc[1]));
5131                int32x4_t row1 = vcombine_s32(vget_high_s32(acc[0]), vget_high_s32(acc[1]));
5132                int32x4_t row2 = vcombine_s32(vget_low_s32(acc[2]), vget_low_s32(acc[3]));
5133                int32x4_t row3 = vcombine_s32(vget_high_s32(acc[2]), vget_high_s32(acc[3]));
5134
5135                // Scales
5136                float32x4_t a_d = vcvt_f32_f16(vld1_f16((const __fp16 *) a_ptr->d));
5137                float32x4_t b_d = vcvt_f32_f16(vld1_f16((const __fp16 *) b_ptr->d));
5138
5139                acc_f32[0] = vfmaq_f32(acc_f32[0], vcvtq_f32_s32(row0), vmulq_laneq_f32(b_d, a_d, 0));
5140                acc_f32[1] = vfmaq_f32(acc_f32[1], vcvtq_f32_s32(row1), vmulq_laneq_f32(b_d, a_d, 1));
5141                acc_f32[2] = vfmaq_f32(acc_f32[2], vcvtq_f32_s32(row2), vmulq_laneq_f32(b_d, a_d, 2));
5142                acc_f32[3] = vfmaq_f32(acc_f32[3], vcvtq_f32_s32(row3), vmulq_laneq_f32(b_d, a_d, 3));
5143
5144                a_ptr++;
5145                b_ptr++;
5146            }
5147
5148            for (int row = 0; row < 4; row++) {
5149                vst1q_f32(s + (y + row) * bs + x, acc_f32[row]);
5150            }
5151        }
5152    }
5153    return;
5154#endif  // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
5155    ggml_gemm_q8_0_4x8_q8_0_generic(n, s, bs, vx, vy, nr, nc);
5156}