char-fork/llama.cpp

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

Max Krasnyanskyhexagon: support for multi-device model split (aka row-split) (#28589)eafe15a5e

char/aliceai-t5-moe
34.1 KiB803 linesraw
1#pragma clang diagnostic ignored "-Wunused-variable"
2#pragma clang diagnostic ignored "-Wunused-function"
3#pragma clang diagnostic ignored "-Wunused-but-set-variable"
4
5#include <HAP_farf.h>
6#include <HAP_perf.h>
7
8#include <math.h>
9#include <string.h>
10#include <stdlib.h>
11
12#include "hex-dma.h"
13#include "hvx-utils.h"
14#include "hex-fastdiv.h"
15
16#define GGML_COMMON_DECL_C
17#include "ggml-common.h"
18#include "htp-ctx.h"
19#include "htp-ops.h"
20#include "htp-tensor.h"
21#include "rope-ops.h"
22
23// Redefined the rope type constants as we can't include ggml.h
24#define HTP_ROPE_TYPE_NORMAL 0
25#define HTP_ROPE_TYPE_NEOX   2
26#define HTP_ROPE_TYPE_MROPE  8
27#define HTP_ROPE_TYPE_VISION 24
28#define HTP_ROPE_TYPE_IMROPE 40
29
30#define htp_rope_preamble              \
31    const uint32_t ne00 = src0->ne[0]; \
32    const uint32_t ne01 = src0->ne[1]; \
33    const uint32_t ne02 = src0->ne[2]; \
34    const uint32_t ne03 = src0->ne[3]; \
35                                       \
36    const uint32_t ne0 = dst->ne[0];   \
37    const uint32_t ne1 = dst->ne[1];   \
38    const uint32_t ne2 = dst->ne[2];   \
39    const uint32_t ne3 = dst->ne[3];   \
40                                       \
41    const uint32_t nb00 = src0->nb[0]; \
42    const uint32_t nb01 = src0->nb[1]; \
43    const uint32_t nb02 = src0->nb[2]; \
44    const uint32_t nb03 = src0->nb[3]; \
45                                       \
46    const uint32_t nb0 = dst->nb[0];   \
47    const uint32_t nb1 = dst->nb[1];   \
48    const uint32_t nb2 = dst->nb[2];   \
49    const uint32_t nb3 = dst->nb[3];
50
51struct htp_rope_context {
52    int32_t n_dims;
53    int32_t n_offs;
54    int32_t mode;
55    int32_t n_ctx_orig;
56    int32_t sections[4];
57
58    float freq_base;
59    float freq_scale;
60    float ext_factor;
61    float attn_factor;
62    float beta_fast;
63    float beta_slow;
64    float theta_scale;
65    float theta_scale_32;
66    float theta_powers[32];
67    float corr_dims[2];
68
69    uint32_t src0_nrows_per_thread;
70
71    struct htp_ops_context * octx;
72
73    uint8_t * vtcm_base;
74    size_t    spad_per_thread;
75    size_t    theta_cache_offset;
76
77    size_t src0_row_size;
78    size_t src0_row_stride;
79    size_t dst_row_size;
80    size_t dst_row_stride;
81    size_t src0_row_size_aligned;
82    uint32_t src0_nrows;
83    uint32_t row_start;
84    uint32_t nrows;
85
86    struct fastdiv_values div_ne2_ne1;
87    struct fastdiv_values div_ne1;
88};
89
90static float rope_yarn_ramp(const float low, const float high, const int i0) {
91    const float y = (i0 / 2 - low) / MAX(0.001f, high - low);
92
93    return (1 - MIN(1, MAX(0, y)));
94}
95
96// Compute one (cos, sin) pair into cache[i0], cache[i0+1] applying YaRN scaling.
97static inline void rope_yarn_one(float theta, float freq_scale, float * corr_dims,
98                                 uint32_t i0, float ext_factor, float mscale,
99                                 float * cache) {
100    float theta_extrap = theta;
101
102    // Get n-d rotational scaling corrected for extrapolation
103    float theta_interp = freq_scale * theta_extrap;
104    float theta_final  = theta_interp;
105    float mscale_final = mscale;
106
107    if (ext_factor != 0.0f) {
108        float ramp_mix = rope_yarn_ramp(corr_dims[0], corr_dims[1], i0) * ext_factor;
109        theta_final    = theta_interp * (1 - ramp_mix) + theta_extrap * ramp_mix;
110
111        // Get n-d magnitude scaling corrected for interpolation
112        mscale_final  *= 1.0f + 0.1f * logf(1.0f / freq_scale);
113    }
114
115    const uint32_t b = i0 / 64;
116    const uint32_t k = (i0 % 64) / 2;
117    cache[b * 64 + k]      = cosf(theta_final) * mscale_final;
118    cache[b * 64 + 32 + k] = sinf(theta_final) * mscale_final;
119}
120
121// 32 thetas -> 32 deinterleaved pairs [cos[32] | sin[32]] at cache[i0].
122static inline void rope_cache_hvx_32(float * cache, uint32_t i0,
123                                     HVX_Vector v_theta,
124                                     const float * freq_factors,
125                                     HVX_Vector v_freq_scale,
126                                     HVX_Vector v_mscale) {
127    if (freq_factors) {
128        HVX_Vector v_ff = hvx_vmemu(freq_factors + i0 / 2);
129        v_theta = hvx_vec_mul_f32_f32(v_theta, hvx_vec_inverse_f32(v_ff));
130    }
131
132    HVX_Vector v_theta_final = hvx_vec_mul_f32_f32(v_theta, v_freq_scale);
133    HVX_Vector vcos;
134    HVX_Vector vsin;
135    hvx_vec_sincos_f32(v_theta_final, &vcos, &vsin);
136    vcos = hvx_vec_mul_f32_f32(vcos, v_mscale);
137    vsin = hvx_vec_mul_f32_f32(vsin, v_mscale);
138
139    if (((uintptr_t) (cache + i0)) % 128 == 0) {
140        hvx_vmem(cache + i0 + 0)  = vcos;
141        hvx_vmem(cache + i0 + 32) = vsin;
142    } else {
143        hvx_vec_store_u(cache + i0 + 0,  32 * sizeof(float), vcos);
144        hvx_vec_store_u(cache + i0 + 32, 32 * sizeof(float), vsin);
145    }
146}
147
148static __attribute__((noinline)) void rope_cache_init(const float    theta_base,
149                            const float    freq_scale,
150                            const float *  freq_factors,
151                            float *        corr_dims,
152                            const uint32_t n_cache,
153                            const float    ext_factor,
154                            const float    mscale,
155                            float *        cache,
156                            const float    theta_scale,
157                            const float *  theta_powers,
158                            const float    theta_scale_32) {
159    // ref: https://github.com/jquesnelle/yarn/blob/master/scaled_rope/LlamaYaRNScaledRotaryEmbedding.py
160    if (ext_factor == 0.0f) {
161        // Fast path: fully vectorized
162        // We process 32 pairs (64 elements) per iteration.
163        const uint32_t n_blocks = n_cache / 64;
164
165        HVX_Vector v_theta_powers = hvx_vmemu(theta_powers);
166        HVX_Vector v_freq_scale = hvx_vec_splat_f32(freq_scale);
167        HVX_Vector v_mscale = hvx_vec_splat_f32(mscale);
168
169        float theta_block = theta_base;
170
171        for (uint32_t b = 0; b < n_blocks; b++) {
172            uint32_t i0 = b * 64;
173            HVX_Vector v_theta_base = hvx_vec_splat_f32(theta_block);
174            HVX_Vector v_theta = hvx_vec_mul_f32_f32(v_theta_base, v_theta_powers);
175            rope_cache_hvx_32(cache, i0, v_theta, freq_factors, v_freq_scale, v_mscale);
176            theta_block *= theta_scale_32;
177        }
178
179        // Leftovers
180        float theta = theta_block;
181        for (uint32_t i0 = n_blocks * 64; i0 < n_cache; i0 += 2) {
182            const float ff = freq_factors ? freq_factors[i0 / 2] : 1.0f;
183            rope_yarn_one(theta / ff, freq_scale, corr_dims, i0, ext_factor, mscale, cache);
184            theta *= theta_scale;
185        }
186    } else {
187        float theta = theta_base;
188        for (uint32_t i0 = 0; i0 < n_cache; i0 += 2) {
189            const float ff = freq_factors ? freq_factors[i0 / 2] : 1.0f;
190            rope_yarn_one(theta / ff, freq_scale, corr_dims, i0, ext_factor, mscale, cache);
191            theta *= theta_scale;
192        }
193    }
194}
195
196static inline float mrope_pick_theta(float theta_t, float theta_h, float theta_w, float theta_e,
197                                     int sector, const int32_t sections[4], int sec_w, int sec_e,
198                                     bool is_imrope) {
199    if (is_imrope) {
200        if      (sector % 3 == 0 && sector < 3 * sections[0]) { return theta_t; }
201        else if (sector % 3 == 1 && sector < 3 * sections[1]) { return theta_h; }
202        else if (sector % 3 == 2 && sector < 3 * sections[2]) { return theta_w; }
203        else                                                   { return theta_e; }
204    }
205    if      (sector < sections[0]) { return theta_t; }
206    else if (sector < sec_w)       { return theta_h; }
207    else if (sector < sec_e)       { return theta_w; }
208    else                           { return theta_e; }
209}
210
211// lane j is 1 when (j % 3) == rem
212static const float __attribute__((aligned(128))) mrope_mod3_eq0[32] = {
213    1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0
214};
215static const float __attribute__((aligned(128))) mrope_mod3_eq1[32] = {
216    0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1
217};
218static const float __attribute__((aligned(128))) mrope_mod3_eq2[32] = {
219    0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0
220};
221
222static const float __attribute__((aligned(128))) mrope_k_ramp[32] = {
223    0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
224    16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
225};
226
227static inline HVX_VectorPred mrope_mask_eq1(const float * m) {
228    return Q6_Q_vcmp_gt_VsfVsf(hvx_vmemu(m), Q6_V_vzero());
229}
230
231// IMROPE without wrap: theta[k] = pos[k % 3] * scale^k
232static inline HVX_Vector mrope_thetas_imrope_mod3(float pos_t, float pos_h, float pos_w,
233                                                  uint32_t k0, HVX_Vector v_powers, float scale_block) {
234    const int r = (int) (k0 % 3);
235    const float * mt = (r == 0) ? mrope_mod3_eq0 : (r == 1) ? mrope_mod3_eq2 : mrope_mod3_eq1;
236    const float * mh = (r == 0) ? mrope_mod3_eq1 : (r == 1) ? mrope_mod3_eq0 : mrope_mod3_eq2;
237
238    HVX_Vector v = hvx_vec_splat_f32(pos_w);
239    v = Q6_V_vmux_QVV(mrope_mask_eq1(mh), hvx_vec_splat_f32(pos_h), v);
240    v = Q6_V_vmux_QVV(mrope_mask_eq1(mt), hvx_vec_splat_f32(pos_t), v);
241    v = hvx_vec_mul_f32_f32(v, v_powers);
242    return hvx_vec_mul_f32_f32(v, hvx_vec_splat_f32(scale_block));
243}
244
245// Contiguous MROPE without wrap: theta[k] = pos[section(k)] * scale^k
246static inline HVX_Vector mrope_thetas_contig(float pos_t, float pos_h, float pos_w, float pos_e,
247                                             uint32_t k0, int s0, int sec_w, int sec_e,
248                                             HVX_Vector v_powers, float scale_block) {
249    HVX_Vector v_k = hvx_vec_add_f32_f32(hvx_vec_splat_f32((float) k0), hvx_vmemu(mrope_k_ramp));
250    HVX_VectorPred lt_s0 = Q6_Q_vcmp_gt_VsfVsf(hvx_vec_splat_f32((float) s0), v_k);
251    HVX_VectorPred lt_sw = Q6_Q_vcmp_gt_VsfVsf(hvx_vec_splat_f32((float) sec_w), v_k);
252    HVX_VectorPred lt_se = Q6_Q_vcmp_gt_VsfVsf(hvx_vec_splat_f32((float) sec_e), v_k);
253
254    HVX_Vector v = hvx_vec_splat_f32(pos_e);
255    v = Q6_V_vmux_QVV(lt_se, hvx_vec_splat_f32(pos_w), v);
256    v = Q6_V_vmux_QVV(lt_sw, hvx_vec_splat_f32(pos_h), v);
257    v = Q6_V_vmux_QVV(lt_s0, hvx_vec_splat_f32(pos_t), v);
258    v = hvx_vec_mul_f32_f32(v, v_powers);
259    return hvx_vec_mul_f32_f32(v, hvx_vec_splat_f32(scale_block));
260}
261
262// pos_t/h/w/e: the four position ids for this sequence step (t=time, h=height, w=width, e=extra).
263// sections[4]: number of head dims assigned to each position component.
264static __attribute__((noinline)) void mrope_cache_init(const float    pos_t,
265                             const float    pos_h,
266                             const float    pos_w,
267                             const float    pos_e,
268                             const int32_t  sections[4],
269                             const bool     is_imrope,
270                             const bool     indep_sects,
271                             const float    freq_scale,
272                             const float *  freq_factors,
273                             float *        corr_dims,
274                             const uint32_t n_cache,
275                             const float    ext_factor,
276                             const float    mscale,
277                             float *        cache,
278                             const float    theta_scale,
279                             const float *  theta_powers,
280                             const float    theta_scale_32) {
281    const int sect_dims = sections[0] + sections[1] + sections[2] + sections[3];
282    const int sec_w     = sections[0] + sections[1];
283    const int sec_e     = sec_w + sections[2];
284    const uint32_t n_pairs = n_cache / 2;
285
286    const bool no_wrap = (sect_dims > 0) && (n_pairs <= (uint32_t) sect_dims);
287    const bool imrope_mod3 = is_imrope && !indep_sects && no_wrap
288        && sections[0] > 0 && sections[1] > 0 && sections[2] > 0
289        && n_pairs <= (uint32_t) (3 * sections[0])
290        && n_pairs <= (uint32_t) (3 * sections[1])
291        && n_pairs <= (uint32_t) (3 * sections[2]);
292    const bool contig = !is_imrope && !indep_sects && no_wrap;
293
294    if (ext_factor == 0.0f && (imrope_mod3 || contig)) {
295        HVX_Vector v_powers     = hvx_vmemu(theta_powers);
296        HVX_Vector v_freq_scale = hvx_vec_splat_f32(freq_scale);
297        HVX_Vector v_mscale     = hvx_vec_splat_f32(mscale);
298        float scale_block = 1.0f;
299        const uint32_t n_blocks = n_cache / 64;
300
301        for (uint32_t b = 0; b < n_blocks; b++) {
302            const uint32_t i0 = b * 64;
303            const uint32_t k0 = b * 32;
304            HVX_Vector v_theta = imrope_mod3
305                ? mrope_thetas_imrope_mod3(pos_t, pos_h, pos_w, k0, v_powers, scale_block)
306                : mrope_thetas_contig(pos_t, pos_h, pos_w, pos_e, k0, sections[0], sec_w, sec_e,
307                                      v_powers, scale_block);
308            rope_cache_hvx_32(cache, i0, v_theta, freq_factors, v_freq_scale, v_mscale);
309            scale_block *= theta_scale_32;
310        }
311
312        float theta_k = scale_block;
313        for (uint32_t k = n_blocks * 32; k < n_pairs; k++) {
314            const uint32_t i0 = 2 * k;
315            const float pos = mrope_pick_theta(pos_t, pos_h, pos_w, pos_e,
316                                               (int) k, sections, sec_w, sec_e, is_imrope);
317            const float ff = freq_factors ? freq_factors[k] : 1.0f;
318            rope_yarn_one(pos * theta_k / ff, freq_scale, corr_dims, i0, ext_factor, mscale, cache);
319            theta_k *= theta_scale;
320        }
321        return;
322    }
323
324    float theta_t = pos_t;
325    float theta_h = pos_h;
326    float theta_w = pos_w;
327    float theta_e = pos_e;
328
329    const bool use_hvx = (ext_factor == 0.0f);
330    float __attribute__((aligned(128))) thetas[32];
331    uint32_t n_thetas = 0;
332    uint32_t block_i0 = 0;
333
334    HVX_Vector v_freq_scale = hvx_vec_splat_f32(freq_scale);
335    HVX_Vector v_mscale     = hvx_vec_splat_f32(mscale);
336
337    for (uint32_t i0 = 0; i0 < n_cache; i0 += 2) {
338        const int sector = (i0 / 2) % sect_dims;
339
340        if (indep_sects) {
341            // Reset theta when crossing into a new section.
342            if      (sector == 0)           { theta_t = pos_t; }
343            else if (sector == sections[0]) { theta_h = pos_h; }
344            else if (sector == sec_w)       { theta_w = pos_w; }
345            else if (sector == sec_e)       { theta_e = pos_e; }
346        }
347
348        const float theta = mrope_pick_theta(theta_t, theta_h, theta_w, theta_e,
349                                             sector, sections, sec_w, sec_e, is_imrope);
350
351        if (use_hvx) {
352            if (n_thetas == 0) {
353                block_i0 = i0;
354            }
355            thetas[n_thetas++] = theta;
356            if (n_thetas == 32) {
357                rope_cache_hvx_32(cache, block_i0, hvx_vmemu(thetas), freq_factors, v_freq_scale, v_mscale);
358                n_thetas = 0;
359            }
360        } else {
361            const float ff = freq_factors ? freq_factors[i0 / 2] : 1.0f;
362            rope_yarn_one(theta / ff, freq_scale, corr_dims, i0, ext_factor, mscale, cache);
363        }
364
365        theta_t *= theta_scale;
366        theta_h *= theta_scale;
367        theta_w *= theta_scale;
368        theta_e *= theta_scale;
369    }
370
371    for (uint32_t k = 0; k < n_thetas; k++) {
372        const uint32_t i0 = block_i0 + 2 * k;
373        const float ff = freq_factors ? freq_factors[i0 / 2] : 1.0f;
374        rope_yarn_one(thetas[k] / ff, freq_scale, corr_dims, i0, ext_factor, mscale, cache);
375    }
376}
377
378#define M_PI 3.1415926535897932384626433
379
380static void rope_corr_dims(int     n_dims,
381                           int     n_ctx_orig,
382                           float   freq_base,
383                           float   beta_fast,
384                           float   beta_slow,
385                           float * dims) {
386    float start = floorf(n_dims * logf(n_ctx_orig / (beta_fast * 2 * (float) M_PI)) / (2 * logf(freq_base)));
387    float end   = ceilf(n_dims * logf(n_ctx_orig / (beta_slow * 2 * (float) M_PI)) / (2 * logf(freq_base)));
388    dims[0]     = MAX(0, start);
389    dims[1]     = MIN(n_dims - 1, end);
390}
391
392static inline void hvx_rope_neox_mul(HVX_Vector v0, HVX_Vector v1, HVX_Vector vcos, HVX_Vector vsin,
393                                     HVX_Vector * o0, HVX_Vector * o1) {
394    HVX_Vector vx0_c = Q6_Vqf32_vmpy_VsfVsf(v0, vcos);
395    HVX_Vector vx0_s = Q6_Vqf32_vmpy_VsfVsf(v0, vsin);
396    HVX_Vector vx1_c = Q6_Vqf32_vmpy_VsfVsf(v1, vcos);
397    HVX_Vector vx1_s = Q6_Vqf32_vmpy_VsfVsf(v1, vsin);
398    *o0 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_Vqf32Vqf32(vx0_c, vx1_s));
399    *o1 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vqf32(vx0_s, vx1_c));
400}
401
402// theta_cache full 32-pair blocks are deinterleaved [cos | sin].
403static inline void hvx_rope_neox_f32_aa(float * restrict dst, const float * restrict src0, uint32_t ne, const float * restrict theta_cache) {
404    const uint32_t he = ne / 2;
405    const uint32_t nvec = he / 32;
406    const uint32_t nloe = he % 32;
407
408    if (nloe == 0) {
409        const HVX_Vector * vs = (const HVX_Vector *) src0;
410        const HVX_Vector * vt = (const HVX_Vector *) theta_cache;
411        HVX_Vector * vd = (HVX_Vector *) dst;
412        for (uint32_t i = 0; i < nvec; i++) {
413            HVX_Vector o0, o1;
414            hvx_rope_neox_mul(vs[i], vs[nvec + i], vt[i * 2 + 0], vt[i * 2 + 1], &o0, &o1);
415            vd[i] = o0;
416            vd[nvec + i] = o1;
417        }
418        return;
419    }
420
421    for (uint32_t i = 0; i < nvec; i++) {
422        HVX_Vector o0, o1;
423        hvx_rope_neox_mul(((const HVX_Vector *) src0)[i],
424                          hvx_vmemu(src0 + he + i * 32),
425                          ((const HVX_Vector *) theta_cache)[i * 2 + 0],
426                          ((const HVX_Vector *) theta_cache)[i * 2 + 1],
427                          &o0, &o1);
428        ((HVX_Vector *) dst)[i] = o0;
429        hvx_vmemu(dst + he + i * 32) = o1;
430    }
431
432    HVX_Vector v0 = hvx_vmemu(src0 + nvec * 32);
433    HVX_Vector v1 = hvx_vmemu(src0 + he + nvec * 32);
434    HVX_Vector vcos = hvx_vmemu(theta_cache + nvec * 64);
435    HVX_Vector vsin = hvx_vmemu(theta_cache + nvec * 64 + 32);
436    HVX_Vector o0, o1;
437    hvx_rope_neox_mul(v0, v1, vcos, vsin, &o0, &o1);
438    hvx_vec_store_u(dst + nvec * 32, nloe * sizeof(float), o0);
439    hvx_vec_store_u(dst + he + nvec * 32, nloe * sizeof(float), o1);
440}
441
442static inline void hvx_rope_f32_aa(float * restrict dst, const float * restrict src0, uint32_t ne, const float * restrict theta_cache) {
443    const uint32_t nvec = ne / 64;
444    const uint32_t nloe = ne % 64;
445
446    for (uint32_t i = 0; i < nvec; i++) {
447        HVX_Vector v0 = ((const HVX_Vector *) src0)[i * 2 + 0];
448        HVX_Vector v1 = ((const HVX_Vector *) src0)[i * 2 + 1];
449
450        HVX_Vector vcos = ((const HVX_Vector *) theta_cache)[i * 2 + 0];
451        HVX_Vector vsin = ((const HVX_Vector *) theta_cache)[i * 2 + 1];
452
453        HVX_VectorPair vx0_x1 = Q6_W_vdeal_VVR(v1, v0, -4);
454
455        HVX_Vector vx0_c = Q6_Vqf32_vmpy_VsfVsf(Q6_V_lo_W(vx0_x1), vcos);
456        HVX_Vector vx0_s = Q6_Vqf32_vmpy_VsfVsf(Q6_V_lo_W(vx0_x1), vsin);
457        HVX_Vector vx1_c = Q6_Vqf32_vmpy_VsfVsf(Q6_V_hi_W(vx0_x1), vcos);
458        HVX_Vector vx1_s = Q6_Vqf32_vmpy_VsfVsf(Q6_V_hi_W(vx0_x1), vsin);
459
460        HVX_Vector v4 = Q6_Vqf32_vsub_Vqf32Vqf32(vx0_c, vx1_s);
461        HVX_Vector v5 = Q6_Vqf32_vadd_Vqf32Vqf32(vx0_s, vx1_c);
462
463        HVX_VectorPair vstore = Q6_W_vshuff_VVR(Q6_Vsf_equals_Vqf32(v5), Q6_Vsf_equals_Vqf32(v4), -4);
464
465        ((HVX_Vector *) dst)[i * 2 + 0] = Q6_V_lo_W(vstore);
466        ((HVX_Vector *) dst)[i * 2 + 1] = Q6_V_hi_W(vstore);
467    }
468
469    if (nloe > 0) {
470        if (nloe <= 32) {
471            HVX_Vector v0 = hvx_vmemu(src0 + nvec * 64);
472            HVX_Vector vcos = hvx_vmemu(theta_cache + nvec * 64);
473            HVX_Vector vsin = hvx_vmemu(theta_cache + nvec * 64 + 32);
474
475            HVX_VectorPair vx0_x1 = Q6_W_vdeal_VVR(Q6_V_vzero(), v0, -4);
476
477            HVX_Vector vx0_c = Q6_Vqf32_vmpy_VsfVsf(Q6_V_lo_W(vx0_x1), vcos);
478            HVX_Vector vx0_s = Q6_Vqf32_vmpy_VsfVsf(Q6_V_lo_W(vx0_x1), vsin);
479            HVX_Vector vx1_c = Q6_Vqf32_vmpy_VsfVsf(Q6_V_hi_W(vx0_x1), vcos);
480            HVX_Vector vx1_s = Q6_Vqf32_vmpy_VsfVsf(Q6_V_hi_W(vx0_x1), vsin);
481
482            HVX_Vector v4 = Q6_Vqf32_vsub_Vqf32Vqf32(vx0_c, vx1_s);
483            HVX_Vector v5 = Q6_Vqf32_vadd_Vqf32Vqf32(vx0_s, vx1_c);
484
485            HVX_VectorPair vstore = Q6_W_vshuff_VVR(Q6_Vsf_equals_Vqf32(v5), Q6_Vsf_equals_Vqf32(v4), -4);
486
487            hvx_vec_store_u(dst + nvec * 64, nloe * sizeof(float), Q6_V_lo_W(vstore));
488        } else {
489            HVX_Vector v0 = hvx_vmemu(src0 + nvec * 64);
490            HVX_Vector v1 = hvx_vmemu(src0 + nvec * 64 + 32);
491
492            HVX_Vector vcos = hvx_vmemu(theta_cache + nvec * 64);
493            HVX_Vector vsin = hvx_vmemu(theta_cache + nvec * 64 + 32);
494
495            HVX_VectorPair vx0_x1 = Q6_W_vdeal_VVR(v1, v0, -4);
496
497            HVX_Vector vx0_c = Q6_Vqf32_vmpy_VsfVsf(Q6_V_lo_W(vx0_x1), vcos);
498            HVX_Vector vx0_s = Q6_Vqf32_vmpy_VsfVsf(Q6_V_lo_W(vx0_x1), vsin);
499            HVX_Vector vx1_c = Q6_Vqf32_vmpy_VsfVsf(Q6_V_hi_W(vx0_x1), vcos);
500            HVX_Vector vx1_s = Q6_Vqf32_vmpy_VsfVsf(Q6_V_hi_W(vx0_x1), vsin);
501
502            HVX_Vector v4 = Q6_Vqf32_vsub_Vqf32Vqf32(vx0_c, vx1_s);
503            HVX_Vector v5 = Q6_Vqf32_vadd_Vqf32Vqf32(vx0_s, vx1_c);
504
505            HVX_VectorPair vstore = Q6_W_vshuff_VVR(Q6_Vsf_equals_Vqf32(v5), Q6_Vsf_equals_Vqf32(v4), -4);
506
507            ((HVX_Vector *) dst)[nvec * 2 + 0] = Q6_V_lo_W(vstore);
508            hvx_vec_store_u(dst + nvec * 64 + 32, (nloe - 32) * sizeof(float), Q6_V_hi_W(vstore));
509        }
510    }
511}
512
513static void inline rope_basic_f32_inplace(struct htp_rope_context * rctx, uint8_t * src,
514                   uint32_t nr, const float * restrict theta_cache) {
515    const uint32_t n_offs = rctx->n_offs;
516    #pragma unroll(4)
517    for (uint32_t i = 0; i < nr; i++) {
518        float * s = (float *) (src + i * rctx->src0_row_size_aligned);
519        hvx_rope_f32_aa(s + n_offs, s + n_offs, rctx->n_dims, theta_cache);
520    }
521}
522
523static void inline rope_neox_f32_inplace(struct htp_rope_context * rctx, uint8_t * src,
524                   uint32_t nr, uint32_t ne, const float * restrict theta_cache) {
525    const uint32_t n_offs = rctx->n_offs;
526    #pragma unroll(4)
527    for (uint32_t i = 0; i < nr; i++) {
528        float * s = (float *) (src + i * rctx->src0_row_size_aligned);
529        hvx_rope_neox_f32_aa(s + n_offs, s + n_offs, ne, theta_cache);
530    }
531}
532
533static void rope_job_f32(unsigned int nth, unsigned int ith, void * data) {
534    struct htp_rope_context * rctx = (struct htp_rope_context *) data;
535    struct htp_ops_context * octx = rctx->octx;
536
537    const struct htp_tensor * src0 = octx->src[0];
538    const struct htp_tensor * src1 = octx->src[1];
539    const struct htp_tensor * src2 = octx->src[2];
540    const struct htp_tensor * dst  = octx->dst;
541
542    htp_rope_preamble;
543
544    const uint32_t src0_nrows = rctx->nrows;
545    const uint32_t src0_nrows_per_thread = rctx->src0_nrows_per_thread;
546
547    const uint32_t src0_start_row = rctx->row_start + src0_nrows_per_thread * ith;
548    const uint32_t src0_end_row   = MIN(src0_start_row + src0_nrows_per_thread, rctx->row_start + src0_nrows);
549
550    // no work for this thread
551    if (src0_start_row >= src0_end_row) {
552        return;
553    }
554
555    const int32_t mode    = rctx->mode;
556    // MROPE, IMROPE and VISION use NEOX-style pairing for the rotation
557    const bool    is_neox = (mode & HTP_ROPE_TYPE_NEOX) || (mode & HTP_ROPE_TYPE_MROPE);
558    const bool    is_vision = (mode == HTP_ROPE_TYPE_VISION);
559
560    // VTCM setup
561    uint8_t * src0_spad_base = rctx->vtcm_base + (ith * rctx->spad_per_thread);
562    float *   theta_cache    = (float *) (src0_spad_base);
563              src0_spad_base = src0_spad_base + rctx->theta_cache_offset;
564
565    dma_queue * dma_queue = octx->ctx->dma[ith];
566    struct htp_thread_trace * tr = &octx->ctx->trace[ith];
567    const int32_t * pos = (const int32_t *) src1->data;
568    const float * freq_factors = src2 ? (const float *) src2->data : NULL;
569
570    const uint32_t i3_start = fastdiv(src0_start_row, &rctx->div_ne2_ne1);
571    const uint32_t rem      = fastmodulo(src0_start_row, ne2 * ne1, &rctx->div_ne2_ne1);
572    const uint32_t i2_start = fastdiv(rem, &rctx->div_ne1);
573    const uint32_t i1_start = fastmodulo(rem, ne1, &rctx->div_ne1);
574
575    uint32_t ir = src0_start_row;
576    uint32_t prev_i2 = (uint32_t) -1;
577    uint32_t cur_slot = 0;
578
579    for (uint32_t i3 = i3_start; i3 < ne3; i3++) { // batch
580        const uint32_t i2_init = (i3 == i3_start) ? i2_start : 0;
581        for (uint32_t i2 = i2_init; i2 < ne2; i2++) { // seq-len
582            const uint32_t i1_init = (i3 == i3_start && i2 == i2_start) ? i1_start : 0;
583            for (uint32_t i1 = i1_init; i1 < ne1; ) { // attn-heads
584                if (ir >= src0_end_row) goto done;
585
586                // Rows in this block
587                const uint32_t nrows = MIN(src0_end_row - ir, ne1 - i1);
588
589                // Depth before prefetch
590                const uint32_t dma_depth = dma_queue_depth(dma_queue);
591
592                // Prefetch up to 2 blocks
593                const uint32_t p_nrows = MIN(nrows, 2 * HTP_ROPE_SPAD_BLOCK);
594                for (uint32_t pr = 0; pr < p_nrows; pr += HTP_ROPE_SPAD_BLOCK) {
595                    const uint32_t pnr = MIN(nrows - pr, HTP_ROPE_SPAD_BLOCK);
596                    const uint32_t slot = (cur_slot + pr / HTP_ROPE_SPAD_BLOCK) % HTP_ROPE_SPAD_NSLOTS;
597                    uint8_t * spad_slot = rope_spad_slot(src0_spad_base, slot, rctx->src0_row_size_aligned);
598                    const uint8_t * src_addr = (const uint8_t *) src0->data + i3 * nb03 + i2 * nb02 + (i1 + pr) * nb01;
599
600                    // Dummy DMA transaction for sequencing (interleaving wr, rd, wr, rd, ...)
601                    dma_queue_push(dma_queue, dma_make_ptr((void *) dst->data, spad_slot), 0, 0, 0, 0);
602
603                    dma_queue_push(dma_queue, dma_make_ptr(spad_slot, src_addr),
604                        rctx->src0_row_size_aligned, rctx->src0_row_stride, rctx->src0_row_size, pnr);
605                }
606
607                // Update theta cache
608                if (i2 != prev_i2) {
609                    prev_i2 = i2;
610
611                    htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_A_PREP, i2);
612                    // VISION rotates the full row; other modes only rotate n_dims.
613                    const uint32_t n_cache = is_vision ? ne0 : (uint32_t) rctx->n_dims;
614                    const bool is_mrope = (rctx->mode & HTP_ROPE_TYPE_MROPE) != 0;
615                    if (is_mrope) {
616                        // src1 holds four position arrays stacked along ne0:
617                        // pos[i2], pos[i2+ne2], pos[i2+ne2*2], pos[i2+ne2*3]
618                        const bool is_imrope = (rctx->mode == HTP_ROPE_TYPE_IMROPE);
619                        mrope_cache_init(
620                            (float) pos[i2],
621                            (float) pos[i2 + ne2],
622                            (float) pos[i2 + ne2 * 2],
623                            (float) pos[i2 + ne2 * 3],
624                            rctx->sections, is_imrope, is_vision,
625                            rctx->freq_scale, freq_factors, rctx->corr_dims,
626                            n_cache, rctx->ext_factor, rctx->attn_factor,
627                            theta_cache, rctx->theta_scale, rctx->theta_powers, rctx->theta_scale_32);
628                    } else {
629                       rope_cache_init(pos[i2], rctx->freq_scale, freq_factors, rctx->corr_dims,
630                                        n_cache, rctx->ext_factor, rctx->attn_factor,
631                                        theta_cache, rctx->theta_scale, rctx->theta_powers, rctx->theta_scale_32);
632                    }
633                    htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_A_PREP, i2);
634                }
635
636                // Skip output DMA transactions from prev block (if any)
637                for (uint32_t d = 0; d < dma_depth; d++) { dma_queue_pop_nowait(dma_queue); }
638
639                // Compute loop
640                const uint32_t ne = is_vision ? ne0 : rctx->n_dims;
641                const uint32_t base_i1 = i1;
642                const uint32_t base_ir = ir;
643
644                for (uint32_t cnr = 0, cr = 0; cr < nrows; cr += cnr) {
645                    cnr = MIN(nrows - cr, HTP_ROPE_SPAD_BLOCK);
646                    const uint32_t slot = (cur_slot + cr / HTP_ROPE_SPAD_BLOCK) % HTP_ROPE_SPAD_NSLOTS;
647                    const uint32_t cur_ir   = base_ir + cr;
648                    const uint32_t cur_i1   = base_i1 + cr;
649
650                    dma_queue_pop(dma_queue);
651                    uint8_t * cur_spad = (uint8_t *) dma_queue_pop(dma_queue).dst;
652
653                    htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, cur_ir);
654                    if (is_neox || is_vision) {
655                        rope_neox_f32_inplace(rctx, cur_spad, cnr, ne, theta_cache);
656                    } else {
657                        rope_basic_f32_inplace(rctx, cur_spad, cnr, theta_cache);
658                    }
659                    htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, cur_ir);
660
661                    uint8_t * dst_addr = (uint8_t *) dst->data + i3 * nb3 + i2 * nb2 + cur_i1 * nb1;
662                    dma_queue_push(dma_queue, dma_make_ptr(dst_addr, cur_spad),
663                        rctx->dst_row_stride, rctx->src0_row_size_aligned, rctx->dst_row_size, cnr);
664
665                    // Prefetch 2 blocks ahead into the slot just freed
666                    if ((cr + 2 * HTP_ROPE_SPAD_BLOCK) < nrows) {
667                        const uint32_t p_cr   = cr + 2 * HTP_ROPE_SPAD_BLOCK;
668                        const uint32_t pnr    = MIN(nrows - p_cr, HTP_ROPE_SPAD_BLOCK);
669                        const uint32_t p_slot = (cur_slot + p_cr / HTP_ROPE_SPAD_BLOCK) % HTP_ROPE_SPAD_NSLOTS;
670                        uint8_t * p_spad      = rope_spad_slot(src0_spad_base, p_slot, rctx->src0_row_size_aligned);
671                        const uint8_t * src_addr = (const uint8_t *) src0->data + i3 * nb03 + i2 * nb02 + (base_i1 + p_cr) * nb01;
672
673                        dma_queue_push(dma_queue, dma_make_ptr(p_spad, src_addr),
674                            rctx->src0_row_size_aligned, rctx->src0_row_stride, rctx->src0_row_size, pnr);
675                    }
676                }
677
678                const uint32_t n_chunks = (nrows + HTP_ROPE_SPAD_BLOCK - 1) / HTP_ROPE_SPAD_BLOCK;
679                cur_slot = (cur_slot + n_chunks) % HTP_ROPE_SPAD_NSLOTS;
680
681                ir += nrows;
682                i1 += nrows;
683            }
684        }
685    }
686
687done:
688    dma_queue_flush(dma_queue);
689
690    FARF(HIGH, "rope-f32: %d/%d: (%u:%u)\n", ith, nth, src0_start_row, src0_end_row);
691}
692
693static int execute_op_rope_f32(struct htp_ops_context * octx) {
694    int err = HTP_STATUS_OK;
695
696    const struct htp_tensor * src0 = octx->src[0];
697    const struct htp_tensor * src1 = octx->src[1];
698    const struct htp_tensor * src2 = octx->src[2];
699    const struct htp_tensor * dst  = octx->dst;
700
701    switch (octx->op) {
702        case HTP_OP_ROPE:
703            break;
704
705        default:
706            FARF(ERROR, "Unsupported Op %u\n", octx->op);
707            return HTP_STATUS_NO_SUPPORT;
708    }
709
710    const struct htp_rope_kernel_params * kparams = (const struct htp_rope_kernel_params *) octx->kernel_params;
711    if (!htp_ops_context_set_n_threads(octx, kparams->n_threads)) {
712        return HTP_STATUS_INVAL_PARAMS;
713    }
714    assert(octx->ctx->vtcm_size >= kparams->vtcm_size);
715
716    const uint32_t total_rows = src0->ne[1] * src0->ne[2] * src0->ne[3];
717    const size_t dst_data_row_size = dst->ne[0] * sizeof(float);
718
719    uint32_t row_start = 0;
720    uint32_t nrows     = total_rows;
721
722    if (octx->ctx->mdev.count > 1) {
723        uint32_t rows_per_chunk = 0;
724        htp_tensor_mdev_rows_per_chunk(dst, sizeof(float), (uint32_t) dst_data_row_size, &rows_per_chunk);
725        const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(
726            total_rows, rows_per_chunk, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
727        row_start = range.start;
728        nrows     = range.count;
729    }
730
731    if (nrows == 0) {
732        return HTP_STATUS_OK;
733    }
734
735    const uint32_t n_threads = octx->n_threads;
736
737    const uint32_t ne0 = dst->ne[0];
738    const size_t src0_row_size   = src0->ne[0] * sizeof(float);
739    const size_t src0_row_stride = src0->nb[1];
740    const size_t dst_row_size    = dst->ne[0] * sizeof(float);
741    const size_t dst_row_stride  = dst->nb[1];
742
743    struct htp_rope_context rctx;
744    memset(&rctx, 0, sizeof(struct htp_rope_context));
745
746    rctx.octx                  = octx;
747    rctx.vtcm_base             = (uint8_t *) octx->ctx->vtcm_base;
748    rctx.spad_per_thread       = kparams->spad_per_thread;
749    rctx.theta_cache_offset    = kparams->theta_cache_offset;
750
751    const int32_t * op_params = &octx->op_params[0];
752    rctx.n_dims     = ((const int32_t *) op_params)[1];
753    rctx.mode       = ((const int32_t *) op_params)[2];
754    rctx.n_ctx_orig = ((const int32_t *) op_params)[4];
755    rctx.n_offs     = ((const int32_t *) op_params)[15];
756
757    memcpy(&rctx.freq_base,   (int32_t *) op_params + 5,  sizeof(float));
758    memcpy(&rctx.freq_scale,  (int32_t *) op_params + 6,  sizeof(float));
759    memcpy(&rctx.ext_factor,  (int32_t *) op_params + 7,  sizeof(float));
760    memcpy(&rctx.attn_factor, (int32_t *) op_params + 8,  sizeof(float));
761    memcpy(&rctx.beta_fast,   (int32_t *) op_params + 9,  sizeof(float));
762    memcpy(&rctx.beta_slow,   (int32_t *) op_params + 10, sizeof(float));
763    memcpy(&rctx.sections,    (int32_t *) op_params + 11, sizeof(int) * 4);
764
765    rctx.theta_scale = powf(rctx.freq_base, -2.0f / rctx.n_dims);
766    rctx.theta_powers[0] = 1.0f;
767    for (int j = 1; j < 32; j++) {
768        rctx.theta_powers[j] = rctx.theta_powers[j - 1] * rctx.theta_scale;
769    }
770    rctx.theta_scale_32 = rctx.theta_powers[31] * rctx.theta_scale;
771
772    rope_corr_dims(rctx.n_dims, rctx.n_ctx_orig, rctx.freq_base, rctx.beta_fast, rctx.beta_slow, rctx.corr_dims);
773
774    rctx.src0_row_size         = src0_row_size;
775    rctx.src0_row_stride       = src0_row_stride;
776    rctx.dst_row_size          = dst_row_size;
777    rctx.dst_row_stride        = dst_row_stride;
778    rctx.src0_row_size_aligned = kparams->src0_row_size_aligned;
779
780    rctx.src0_nrows            = nrows;
781    rctx.nrows                 = nrows;
782    rctx.row_start             = row_start;
783    rctx.src0_nrows_per_thread = fastdiv(nrows + n_threads - 1, &octx->n_threads_div);
784    rctx.div_ne2_ne1           = kparams->div_ne2_ne1;
785    rctx.div_ne1               = kparams->div_ne1;
786
787    FARF(HIGH, "rope-f32 n-rows %u n-dims %d ne0 %u ext-factor %.6f theta-scale %.6f attn-factor %.6f\n", rctx.src0_nrows, rctx.n_dims, ne0,
788         rctx.ext_factor, rctx.theta_scale, rctx.attn_factor);
789
790    work_queue_run(octx->ctx->work_queue, rope_job_f32, &rctx, n_threads);
791
792    return err;
793}
794
795int op_rope(struct htp_ops_context * octx) {
796    switch (octx->src[0]->type) {
797        case HTP_TYPE_F32:
798            return execute_op_rope_f32(octx);
799
800        default:
801            return HTP_STATUS_NO_SUPPORT;
802    }
803}