char-fork/llama.cpp

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

Max Krasnyanskyhexagon: MUL_MAT and MUL_MAT_ID fusion and fixes (#28202)960dffab0

char/aliceai-t5-moe
31.8 KiB778 linesraw
1#ifndef HTP_MATMUL_OPS_H
2#define HTP_MATMUL_OPS_H
3
4#include <stdint.h>
5#include <stddef.h>
6#include "htp-ops.h"
7#include "hex-fastdiv.h"
8#include "hex-common.h"
9#include "htp-vtcm.h"
10
11#ifdef __cplusplus
12extern "C" {
13#endif
14
15// --- HMX Tile Constraints ---
16#define HTP_MM_HMX_TILE_N_COLS 32
17#define HTP_MM_HMX_TILE_N_ROWS 32
18#define HTP_MM_HMX_TILE_SIZE   (32 * 32 * sizeof(__fp16)) // 2048 bytes
19#define HTP_MM_HMX_TILE_N_ELMS 1024
20#define HTP_MM_HMX_MIN_NROWS   4
21
22// --- Weight Repacked Tile Sizes ---
23#define HTP_MM_WEIGHT_TILE_SIZE_Q4_0   576
24#define HTP_MM_WEIGHT_TILE_SIZE_Q4_1   640
25#define HTP_MM_WEIGHT_TILE_SIZE_Q8_0   1088
26#define HTP_MM_WEIGHT_TILE_SIZE_IQ4_NL 576
27#define HTP_MM_WEIGHT_TILE_SIZE_MXFP4  544
28
29// --- Weight Repacked Aligned Tile Sizes ---
30#define HTP_MM_WEIGHT_ALIGNED_TILE_SIZE_Q4_0   640
31#define HTP_MM_WEIGHT_ALIGNED_TILE_SIZE_Q4_1   640
32#define HTP_MM_WEIGHT_ALIGNED_TILE_SIZE_Q8_0   1152
33#define HTP_MM_WEIGHT_ALIGNED_TILE_SIZE_IQ4_NL 640
34#define HTP_MM_WEIGHT_ALIGNED_TILE_SIZE_MXFP4  640
35
36// --- Activation Tiled Block Sizes (including padding) ---
37#define HTP_MM_ACT_TILE_SIZE_Q8_0      1152
38#define HTP_MM_ACT_TILE_SIZE_Q8_1      1280
39
40#define HTP_MM_MAX_PREFETCH 16
41
42// --- Solver Cost Model Penalty Weights (HMX-specific) ---
43#define HTP_MM_HMX_COST_W_DEQUANT 3 // cost penalty for quantized weight loading/dequantization
44#define HTP_MM_HMX_COST_A_CONVERT 2 // cost penalty for activation loading/conversion
45
46// --- DMA Activation Transfer Configuration ---
47#define HTP_MM_DMA_ACT_ROWS_PER_STEP 2
48#define HTP_MM_DMA_ACT_MULTIPLIER    (2 * HTP_MM_DMA_ACT_ROWS_PER_STEP)
49
50enum htp_mm_kernel_type {
51    HTP_MM_KERNEL_UNSUPPORTED = 0,
52
53    // HMX paths
54    HTP_MM_KERNEL_HMX_2D,
55    HTP_MM_KERNEL_HMX_F16_BATCHED,
56
57    // HVX floating-point paths
58    HTP_MM_KERNEL_HVX_F16_F16_VTCM,
59    HTP_MM_KERNEL_HVX_F16_F16_DDR,
60    HTP_MM_KERNEL_HVX_F16_F32_DDR,
61
62    HTP_MM_KERNEL_HVX_F32_F32_VTCM,
63    HTP_MM_KERNEL_HVX_F32_F32_DDR,
64    HTP_MM_KERNEL_HVX_F32_F16_DDR,
65
66    // HVX quantized paths
67    HTP_MM_KERNEL_HVX_QUANT_ROW,      // standard row-wise parallel quantization
68    HTP_MM_KERNEL_HVX_QUANT_BLOCK,    // parallel block-wise quantization
69    HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT, // row-wise fallback flat quantization
70};
71
72// Op-specific struct for precomputed matmul params
73struct htp_mm_kernel_params {
74    int32_t  kernel_type;        // enum htp_mm_kernel_type
75    int32_t  pipeline;           // 1 = pipelined execution, 0 = standard
76    int32_t  m_chunk;            // Row chunk size (M chunk)
77    int32_t  n_chunk;            // Col chunk size (N chunk)
78    int32_t  n_threads;          // Number of threads to spawn
79    int32_t  n_act_threads;      // Number of threads for activation preparation
80    int32_t  n_hmx;              // 1 = use HMX, 0 = use HVX
81    int32_t  n_prefetch;         // Prefetch lookahead buffers/rows in VTCM
82    int32_t  tile_size;          // Weight tile size
83    int32_t  aligned_tile_size;  // Aligned weight tile size (padded to 128)
84    int32_t  src1_row_size;      // Row size for quantized activation
85    int32_t  vtcm_size;          // Total required scratchpad size in VTCM
86    int32_t  vtcm_src0_size;     // src0 scratchpad size in VTCM
87    int32_t  vtcm_src1_size;     // src1 scratchpad size in VTCM
88    int32_t  vtcm_src2_size;     // src2 scratchpad size in VTCM (fused only)
89    int32_t  vtcm_src3_size;     // src3 scratchpad size in VTCM (fused only)
90    int32_t  vtcm_dst_size;      // dst scratchpad size in VTCM
91    int32_t  n_weights;          // Number of weights for fused NX
92
93    // Precomputed division values
94    struct fastdiv_values div_ne12_ne1;
95    struct fastdiv_values div_ne1;
96    struct fastdiv_values div_r2;
97    struct fastdiv_values div_r3;
98    struct fastdiv_values div_ne11;
99    struct fastdiv_values div_n_act_threads;
100    struct fastdiv_values div_ne00_padded;
101};
102
103#if defined(__cplusplus)
104static_assert(sizeof(struct htp_mm_kernel_params) <= 128, "htp_matmul_kernel_params is too large for kernel_params blob");
105#else
106_Static_assert(sizeof(struct htp_mm_kernel_params) <= 128, "htp_matmul_kernel_params is too large for kernel_params blob");
107#endif
108
109struct mmid_row_mapping {
110    uint32_t i1;
111    uint32_t i2;
112};
113
114// Search for optimal (mc, nc) chunk sizes within VTCM budget.
115static inline int htp_mm_hmx_compute_chunks(size_t   vtcm_total,
116                              size_t   overhead,
117                              size_t   per_n_cost,
118                              size_t   per_m_cost,
119                              size_t   per_mn_cost,
120                              size_t   m,
121                              size_t   n,
122                              size_t   m_block_cost,
123                              size_t   n_block_cost,
124                              size_t * m_chunk_out,
125                              size_t * n_chunk_out,
126                              size_t * total_out) {
127    if (m == 0 || n == 0) return -1;
128    if (vtcm_total <= overhead) return -1;
129    if (per_n_cost == 0 || per_m_cost == 0 || per_mn_cost == 0) return -1;
130
131    const size_t usable = vtcm_total - overhead;
132
133    size_t best_cost = SIZE_MAX;
134    size_t best_mn   = 0;
135    size_t best_m = 0, best_n = 0;
136
137    const size_t max_nc_budget = (usable / per_n_cost);
138    const size_t n_max = hex_align_down(hex_smin((size_t)n, max_nc_budget), HTP_MM_HMX_TILE_N_COLS);
139    for (size_t nc = n_max; nc >= HTP_MM_HMX_TILE_N_COLS; nc -= HTP_MM_HMX_TILE_N_COLS) {
140        size_t n_fixed = 0, ncmn = 0, mc_denom = 0;
141        if (hex_mul_overflow(nc, per_n_cost, &n_fixed)) continue;
142        if (n_fixed >= usable) goto next_nc;
143
144        if (hex_mul_overflow(nc, per_mn_cost, &ncmn)) goto next_nc;
145        if (hex_add_overflow(per_m_cost, ncmn, &mc_denom) || mc_denom == 0) goto next_nc;
146
147        {
148            size_t remain = usable - n_fixed;
149            size_t mc = remain / mc_denom;
150            mc = hex_align_down(mc, HTP_MM_HMX_TILE_N_ROWS);
151            mc = hex_smin(mc, m);
152
153            if (mc == 0) {
154                goto next_nc;
155            }
156
157            size_t mblocks = ((size_t) m + mc - 1) / mc;
158            size_t nblocks = ((size_t) n + nc - 1) / nc;
159            size_t cost    = mblocks * m_block_cost + nblocks * n_block_cost;
160            size_t mn      = mc * nc;
161            if (cost < best_cost || (cost == best_cost && mn > best_mn)) {
162                best_cost = cost;
163                best_mn   = mn;
164                best_m    = mc;
165                best_n    = nc;
166            }
167        }
168
169next_nc:
170        if (nc == HTP_MM_HMX_TILE_N_COLS) break;  // avoid size_t underflow
171    }
172
173    if (best_m == 0 || best_n == 0) return -1;
174
175    // Compute exact total (with overflow checks)
176    size_t t0 = 0, t1 = 0, t2 = 0, mn = 0, total = 0;
177    if (hex_mul_overflow(best_n, per_n_cost, &t0)) return -1;
178    if (hex_mul_overflow(best_m, per_m_cost, &t1)) return -1;
179    if (hex_mul_overflow(best_m, best_n, &mn))     return -1;
180    if (hex_mul_overflow(mn, per_mn_cost, &t2))    return -1;
181    if (hex_add_overflow(t0, t1, &total))          return -1;
182    if (hex_add_overflow(total, t2, &total))       return -1;
183    if (hex_add_overflow(total, overhead, &total)) return -1;
184
185    *m_chunk_out = best_m;
186    *n_chunk_out = best_n;
187    *total_out   = total;
188    return 0;
189}
190
191// --- Tile Size Helpers ---
192static inline uint32_t htp_mm_get_weight_tile_size(int weight_type) {
193    switch (weight_type) {
194        case HTP_TYPE_Q4_0:
195        case HTP_TYPE_IQ4_NL:
196            return HTP_MM_WEIGHT_TILE_SIZE_Q4_0;
197        case HTP_TYPE_Q4_1:
198            return HTP_MM_WEIGHT_TILE_SIZE_Q4_1;
199        case HTP_TYPE_Q8_0:
200            return HTP_MM_WEIGHT_TILE_SIZE_Q8_0;
201        case HTP_TYPE_MXFP4:
202            return HTP_MM_WEIGHT_TILE_SIZE_MXFP4;
203        default:
204            return 0;
205    }
206}
207
208static inline uint32_t htp_mm_get_weight_aligned_tile_size(int weight_type) {
209    switch (weight_type) {
210        case HTP_TYPE_Q4_0:
211        case HTP_TYPE_IQ4_NL:
212            return HTP_MM_WEIGHT_ALIGNED_TILE_SIZE_Q4_0;
213        case HTP_TYPE_Q4_1:
214            return HTP_MM_WEIGHT_ALIGNED_TILE_SIZE_Q4_1;
215        case HTP_TYPE_Q8_0:
216            return HTP_MM_WEIGHT_ALIGNED_TILE_SIZE_Q8_0;
217        case HTP_TYPE_MXFP4:
218            return HTP_MM_WEIGHT_ALIGNED_TILE_SIZE_MXFP4;
219        default:
220            return 0;
221    }
222}
223
224// --- Activation/Row Size Helpers ---
225static inline size_t htp_mm_q8_0_tiled_row_size(uint32_t ne) {
226    const uint32_t ne_padded = ((ne + 127) / 128) * 128;
227    const uint32_t nb_32 = ne_padded / 32;
228    return nb_32 * HTP_MM_ACT_TILE_SIZE_Q8_0;
229}
230
231static inline size_t htp_mm_q8_1_tiled_row_size(uint32_t ne) {
232    const uint32_t ne_padded = ((ne + 127) / 128) * 128;
233    const uint32_t nb_32 = ne_padded / 32;
234    return nb_32 * HTP_MM_ACT_TILE_SIZE_Q8_1;
235}
236
237static inline size_t htp_mm_q8_0_flat_row_size(uint32_t ne) {
238    const uint32_t quants_size = hex_align_up(ne, 128);
239    const uint32_t num_scales = (ne + 31) / 32;
240    const uint32_t scales_size = hex_align_up(num_scales * 2, 128);
241    return quants_size + scales_size;
242}
243
244static inline size_t htp_mm_q8_1_flat_row_size(uint32_t ne) {
245    const uint32_t quants_size = hex_align_up(ne, 128);
246    const uint32_t num_scales = (ne + 31) / 32;
247    const uint32_t scales_size = hex_align_up(num_scales * 4, 128);
248    return quants_size + scales_size;
249}
250
251static inline size_t htp_mm_get_tiled_row_stride(int weight_type, uint32_t k) {
252    uint32_t nb = (k + QK_Q4_0_TILED - 1) / QK_Q4_0_TILED;
253    switch (weight_type) {
254        case HTP_TYPE_Q4_0:
255        case HTP_TYPE_IQ4_NL:
256        case HTP_TYPE_Q4_1:
257        case HTP_TYPE_Q8_0:
258        case HTP_TYPE_MXFP4:
259            return (size_t) nb * htp_mm_get_weight_tile_size(weight_type);
260        case HTP_TYPE_F16:
261            return (size_t) k * sizeof(__fp16);
262        case HTP_TYPE_F32:
263            return (size_t) k * sizeof(float);
264        default:
265            return 0;
266    }
267}
268
269static inline size_t htp_mm_round_up(size_t n, size_t m) {
270    return ((n + m - 1) / m) * m;
271}
272
273static inline bool htp_mm_hmx_pipeline(uint32_t m) {
274    return m > 32;
275}
276
277static inline void htp_mm_hmx_get_2d_chunk_costs(
278    int wtype, uint32_t k, bool pipeline, uint32_t aligned_tile_size,
279    size_t * size_per_n_out, size_t * size_per_m_out, size_t * size_per_mn_out
280) {
281    const bool is_quant = (wtype != HTP_TYPE_F16 && wtype != HTP_TYPE_F32);
282    const size_t row_stride = htp_mm_get_tiled_row_stride(wtype, k);
283    const size_t vec_dot_size = k * sizeof(uint16_t);
284    const uint32_t n_k_tiles = k / HTP_MM_HMX_TILE_N_COLS;
285    const size_t qweight_row_stride = is_quant ? (size_t)(n_k_tiles * aligned_tile_size) / 32 : 0;
286
287    *size_per_n_out = (pipeline ? 2 : 1) * (is_quant ? qweight_row_stride : row_stride) +
288                      (pipeline ? 2 * vec_dot_size : vec_dot_size);
289    *size_per_m_out = vec_dot_size;
290    *size_per_mn_out = (pipeline ? 2 : 1) * sizeof(uint16_t);
291}
292
293static inline void htp_mm_hmx_get_batched_chunk_costs(
294    uint32_t k, uint32_t group_size,
295    size_t * size_per_n_out, size_t * size_per_m_out, size_t * size_per_mn_out
296) {
297    const size_t vec_dot_size = k * sizeof(uint16_t);
298    *size_per_n_out = 3 * vec_dot_size;
299    *size_per_m_out = group_size * vec_dot_size;
300    *size_per_mn_out = sizeof(uint16_t);
301}
302
303static inline size_t htp_mm_hmx_get_2d_overhead(bool pipeline, bool is_matmul_id) {
304    size_t num_regions = pipeline ? 7 : (is_matmul_id ? 4 : 5);
305    return num_regions * HTP_MM_HMX_TILE_SIZE + 256;
306}
307
308static inline size_t htp_mm_hmx_get_batched_overhead(void) {
309    return 5 * HTP_MM_HMX_TILE_SIZE + 256;
310}
311
312struct htp_mm_hmx_vtcm_layout {
313    // Byte offsets from vtcm_base for each region
314    size_t off_weight[2];     // [1] is only used when pipelined
315    size_t off_act;
316    size_t off_act_f32;       // fp32 activation conversion scratch
317    size_t off_dst[2];        // [1] is only used when pipelined
318    size_t off_scratch[2];    // dequantization scratch pads
319    size_t off_scales;        // HMX scales (256 bytes)
320
321    // Cached sizes of regions for HMX kernel use
322    size_t weight_area_bytes;
323    size_t act_area_bytes;
324    size_t act_f32_bytes;
325    size_t output_area_bytes;
326    size_t scratch_bytes[2];
327    size_t act_head_stride;
328
329    size_t total_bytes;
330};
331
332struct htp_mm_hvx_vtcm_layout {
333    // Byte offsets from vtcm_base for each region
334    size_t off_src1;          // vtcm_src1 (activation)
335    size_t off_src0;          // vtcm_src0 (weight/Wk)
336    size_t off_src2;          // vtcm_src2 (Wq / fused only)
337    size_t off_src3;          // vtcm_src3 (Wv / fused only)
338    size_t off_dst;           // vtcm_dst (output scratch)
339
340    // Cached sizes
341    size_t src0_bytes;
342    size_t src1_bytes;
343    size_t src2_bytes;
344    size_t src3_bytes;
345    size_t dst_bytes;
346
347    size_t total_bytes;
348};
349
350static inline void htp_mm_hmx_vtcm_layout_build(
351    struct htp_mm_hmx_vtcm_layout * L,
352    int kernel_type,
353    int wtype,
354    uint32_t k,
355    size_t mc,
356    size_t nc,
357    uint32_t group_size,
358    bool use_dma_activation,
359    bool pipeline,
360    uint32_t act_threads,
361    uint32_t aligned_tile_size
362) {
363    size_t off = 0;
364
365    if (kernel_type == HTP_MM_KERNEL_HMX_F16_BATCHED) {
366        const size_t vec_dot_size     = k * sizeof(uint16_t);
367        const size_t act_head_stride   = mc * k;
368        const size_t weight_area_size  = hex_align_up(nc * vec_dot_size, HTP_MM_HMX_TILE_SIZE);
369        const size_t activation_area_size = hex_align_up(group_size * act_head_stride * sizeof(uint16_t), HTP_MM_HMX_TILE_SIZE);
370        const size_t output_area_size  = hex_align_up(group_size * mc * nc * sizeof(uint16_t), HTP_MM_HMX_TILE_SIZE);
371        const size_t scratch_area_size = hex_align_up(nc * vec_dot_size, HTP_MM_HMX_TILE_SIZE);
372        const size_t min_f32_size = use_dma_activation
373            ? hex_align_up(act_threads * HTP_MM_DMA_ACT_MULTIPLIER * k * sizeof(float), 128) : 0;
374
375        // Group A: Permanent activation tiles and scales
376        size_t off_group_a = 0;
377        VTCM_LAYOUT_ALLOC(off_group_a, off_act, activation_area_size);
378        VTCM_LAYOUT_ALLOC(off_group_a, off_scales, HTP_MM_HMX_TILE_SIZE); // Padded to 2K for alignment and future persistent data
379
380        // Group B: Compute-only buffers (starts at off_group_a)
381        size_t off_group_b = off_group_a;
382        VTCM_LAYOUT_ALLOC(off_group_b, off_weight[0], weight_area_size);
383        VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_weight[1], weight_area_size, false);
384        VTCM_LAYOUT_ALLOC(off_group_b, off_dst[0], output_area_size);
385        VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_dst[1], output_area_size, false);
386        VTCM_LAYOUT_ALLOC(off_group_b, off_scratch[0], scratch_area_size);
387        VTCM_LAYOUT_ALLOC(off_group_b, off_scratch[1], scratch_area_size);
388
389        const size_t group_b_size = off_group_b - off_group_a;
390
391        // Group C: Activation prep temporary buffer (overlaps Group B, starting at off_group_a)
392        const size_t max_f32_size = act_threads * 64 * k * sizeof(float);
393        const size_t act_f32_size = use_dma_activation
394            ? hex_align_up(hex_smin(max_f32_size, hex_smax(min_f32_size, group_b_size)), 128) : 0;
395        size_t off_group_c = off_group_a;
396        VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_c, off_act_f32, act_f32_size, use_dma_activation);
397
398        const size_t group_c_size = off_group_c - off_group_a;
399
400        L->weight_area_bytes = weight_area_size;
401        L->act_area_bytes    = activation_area_size;
402        L->act_f32_bytes     = act_f32_size;
403        L->output_area_bytes = output_area_size;
404        L->scratch_bytes[0]  = scratch_area_size;
405        L->scratch_bytes[1]  = scratch_area_size;
406        L->act_head_stride   = act_head_stride;
407
408        off = off_group_a + hex_smax(group_b_size, group_c_size);
409    } else {
410        // HTP_MM_KERNEL_HMX_2D
411        const bool is_quant = (wtype != HTP_TYPE_F16 && wtype != HTP_TYPE_F32);
412        const size_t row_stride = htp_mm_get_tiled_row_stride(wtype, k);
413        const size_t vec_dot_size = k * sizeof(uint16_t);
414        const uint32_t n_k_tiles = k / HTP_MM_HMX_TILE_N_COLS;
415
416        const size_t min_f32_size = hex_align_up(act_threads * HTP_MM_DMA_ACT_MULTIPLIER * k * sizeof(float), 128);
417        const size_t weight_area_size = is_quant
418            ? hex_align_up((nc / 32) * n_k_tiles * aligned_tile_size, HTP_MM_HMX_TILE_SIZE)
419            : hex_align_up(nc * row_stride, HTP_MM_HMX_TILE_SIZE);
420        const size_t act_area_size    = hex_align_up(mc * vec_dot_size, HTP_MM_HMX_TILE_SIZE);
421        const size_t output_area_size = hex_align_up(mc * nc * sizeof(__fp16), HTP_MM_HMX_TILE_SIZE);
422
423        const size_t scratch0_size = hex_align_up(nc * vec_dot_size, HTP_MM_HMX_TILE_SIZE);
424        const size_t scratch1_size = pipeline ? scratch0_size : 0;
425
426        // Group A:  Scales and activation tiles (must not overlap with Group B or C)
427        size_t off_group_a = 0;
428        VTCM_LAYOUT_ALLOC(off_group_a, off_scales, HTP_MM_HMX_TILE_SIZE); // Padded to 2K for alignment and future persistent data
429        VTCM_LAYOUT_ALLOC(off_group_a, off_act, act_area_size);
430
431        // Group B: Compute-only buffers (starts at off_group_a)
432        size_t off_group_b = off_group_a;
433        VTCM_LAYOUT_ALLOC(off_group_b, off_weight[0], weight_area_size);
434        VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_weight[1], weight_area_size, pipeline);
435        VTCM_LAYOUT_ALLOC(off_group_b, off_dst[0], output_area_size);
436        VTCM_LAYOUT_ALLOC(off_group_b, off_scratch[0], scratch0_size);
437        VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_scratch[1], scratch0_size, pipeline);
438        VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_dst[1], output_area_size, pipeline);
439
440        const size_t group_b_size = off_group_b - off_group_a;
441
442        // Group C: Activation prep temporary buffer (overlaps Group B, starting at off_group_a)
443        const size_t max_f32_size = act_threads * 64 * k * sizeof(float);
444        const size_t act_f32_size = hex_align_up(hex_smin(max_f32_size, hex_smax(min_f32_size, group_b_size)), 128);
445        size_t off_group_c = off_group_a;
446        VTCM_LAYOUT_ALLOC(off_group_c, off_act_f32, act_f32_size);
447
448        const size_t group_c_size = off_group_c - off_group_a;
449
450        L->weight_area_bytes = weight_area_size;
451        L->act_area_bytes    = act_area_size;
452        L->act_f32_bytes     = act_f32_size;
453        L->output_area_bytes = output_area_size;
454        L->scratch_bytes[0]  = scratch0_size;
455        L->scratch_bytes[1]  = scratch1_size;
456        L->act_head_stride   = 0;
457
458        off = off_group_a + hex_smax(group_b_size, group_c_size);
459    }
460
461    L->total_bytes = off;
462}
463
464static inline void htp_mm_hvx_vtcm_layout_build(
465    struct htp_mm_hvx_vtcm_layout * L,
466    int kernel_type,
467    int wtype,
468    uint32_t ne10,       // k
469    uint32_t src1_nrows, // m_total
470    uint32_t n_threads,
471    size_t dst_row_size,
472    size_t src0_row_size,
473    size_t src1_row_size,
474    size_t src2_row_size,
475    uint32_t n_prefetch,
476    bool is_matmul_id,
477    bool is_fused_nx
478) {
479    size_t src0_sz = 0;
480    size_t src1_sz = 0;
481    size_t src2_sz = src2_row_size > 0 ? htp_mm_round_up(src2_row_size, 128) : 0;
482    size_t src3_sz = 0;
483    size_t dst_sz  = 0;
484
485    const bool is_repack = (wtype == HTP_TYPE_Q4_0 || wtype == HTP_TYPE_Q4_1 ||
486                            wtype == HTP_TYPE_Q8_0 || wtype == HTP_TYPE_IQ4_NL ||
487                            wtype == HTP_TYPE_MXFP4);
488
489    if (is_fused_nx) {
490        const size_t src0_row_size_padded = hex_round_up(src0_row_size, 128);
491        const size_t quant_scratch_size = hex_round_up(ne10 * sizeof(float), QK_Q8_0_TILED * sizeof(float)) * n_threads;
492
493        size_t weight_sz_per_thread = 0;
494
495        if (is_repack) {
496            uint32_t aligned_tile_size = htp_mm_get_weight_aligned_tile_size(wtype);
497            uint32_t n_k_tiles = hex_round_up(ne10, 32) / 32;
498            uint32_t tile_row_size = n_k_tiles * aligned_tile_size;
499
500            weight_sz_per_thread = hex_round_up(n_prefetch * tile_row_size, 128);
501        } else {
502            weight_sz_per_thread = hex_round_up(n_prefetch * src0_row_size_padded, 128);
503        }
504
505        size_t flat_act_row_size  = (wtype == HTP_TYPE_Q4_1) ? htp_mm_q8_1_flat_row_size(ne10)  : htp_mm_q8_0_flat_row_size(ne10);
506        size_t tiled_act_row_size = (wtype == HTP_TYPE_Q4_1) ? htp_mm_q8_1_tiled_row_size(ne10) : htp_mm_q8_0_tiled_row_size(ne10);
507
508        size_t act_sz = (kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT)
509            ? hex_round_up(flat_act_row_size  * src1_nrows, 128)
510            : hex_round_up(tiled_act_row_size * src1_nrows, 128);
511
512        src0_sz = weight_sz_per_thread * n_threads; // shared single-weight prefetch buffer
513        src1_sz = act_sz;                           // quantized activation buffer
514        src2_sz = 0;
515        src3_sz = 0;
516        dst_sz  = quant_scratch_size;
517    } else if (is_matmul_id) {
518        const size_t src0_row_size_padded = htp_mm_round_up(src0_row_size, 128);
519        const size_t src1_row_size_tiled = (wtype == HTP_TYPE_Q4_1) ? htp_mm_q8_1_tiled_row_size(ne10)
520                                                                    : htp_mm_q8_0_tiled_row_size(ne10);
521
522        size_t src0_sz_per_thread = htp_mm_round_up(n_prefetch * src0_row_size_padded, 256);
523        src1_sz                   = htp_mm_round_up(src1_row_size_tiled * src1_nrows, 256);
524
525        if (is_repack) {
526            const uint32_t aligned_tile_size = htp_mm_get_weight_aligned_tile_size(wtype);
527            const uint32_t n_k_tiles         = ne10 / 32;
528            const uint32_t tile_row_size     = n_k_tiles * aligned_tile_size;
529            size_t repacked_vtcm_size        = htp_mm_round_up(n_prefetch * tile_row_size, 256);
530            src0_sz_per_thread               = repacked_vtcm_size;
531        }
532
533        src0_sz = src0_sz_per_thread * n_threads;
534        dst_sz  = htp_mm_round_up(ne10 * sizeof(float), QK_Q8_0_TILED * sizeof(float)) * n_threads;
535    } else {
536        const size_t src0_row_size_padded = htp_mm_round_up(src0_row_size, 128);
537        const size_t dst_nrows = (src1_nrows > 1) ? 0 : 1;
538
539        switch (kernel_type) {
540            case HTP_MM_KERNEL_HVX_F16_F16_VTCM: {
541                size_t f16_src1_row_size = htp_mm_round_up(ne10 * 2, 128);
542                src1_sz = htp_mm_round_up(f16_src1_row_size * src1_nrows, 256);
543                src0_sz = htp_mm_round_up(n_prefetch * src0_row_size_padded, 256) * n_threads;
544                dst_sz  = dst_nrows > 0 ? htp_mm_round_up(dst_row_size, 128) * n_threads : 0;
545                break;
546            }
547            case HTP_MM_KERNEL_HVX_F16_F32_DDR:
548            case HTP_MM_KERNEL_HVX_F16_F16_DDR:
549            case HTP_MM_KERNEL_HVX_F32_F32_DDR:
550            case HTP_MM_KERNEL_HVX_F32_F16_DDR: {
551                src0_sz = htp_mm_round_up(n_prefetch * src0_row_size, 256) * n_threads;
552                src1_sz = htp_mm_round_up(n_prefetch * src1_row_size, 256) * n_threads;
553                dst_sz  = dst_nrows > 0 ? htp_mm_round_up(dst_row_size, 128) * n_threads : 0;
554                break;
555            }
556            case HTP_MM_KERNEL_HVX_F32_F32_VTCM: {
557                size_t f32_src1_row_size = htp_mm_round_up(ne10 * 4, 128);
558                src1_sz = htp_mm_round_up(f32_src1_row_size * src1_nrows, 256);
559                src0_sz = htp_mm_round_up(n_prefetch * src0_row_size_padded, 256) * n_threads;
560                dst_sz  = dst_nrows > 0 ? htp_mm_round_up(dst_row_size, 128) * n_threads : 0;
561                break;
562            }
563            case HTP_MM_KERNEL_HVX_QUANT_BLOCK:
564            case HTP_MM_KERNEL_HVX_QUANT_ROW: {
565                size_t q_src1_row_size = (wtype == HTP_TYPE_Q4_1) ? htp_mm_q8_1_tiled_row_size(ne10) : htp_mm_q8_0_tiled_row_size(ne10);
566
567                src0_sz = htp_mm_round_up(n_prefetch * src0_row_size_padded, 256);
568                src1_sz = htp_mm_round_up(q_src1_row_size * src1_nrows, 256);
569
570                src0_sz = src0_sz * n_threads;
571
572                if (is_repack) {
573                    uint32_t aligned_tile_size = htp_mm_get_weight_aligned_tile_size(wtype);
574                    uint32_t n_k_tiles = ne10 / 32;
575                    uint32_t tile_row_size = n_k_tiles * aligned_tile_size;
576                    size_t repacked_vtcm_size = htp_mm_round_up(n_prefetch * tile_row_size, 256);
577                    src0_sz = repacked_vtcm_size * n_threads;
578                }
579
580                size_t quant_scratch_size_per_thread = htp_mm_round_up(ne10 * sizeof(float), QK_Q8_0_TILED * sizeof(float));
581                size_t dst_slice_per_thread = (dst_nrows > 0 && src1_nrows == 1) ? htp_mm_round_up((dst_row_size + n_threads - 1) / n_threads, 128) : 0;
582                size_t dst_size_per_thread = (dst_slice_per_thread > quant_scratch_size_per_thread) ? dst_slice_per_thread : quant_scratch_size_per_thread;
583                dst_sz = dst_size_per_thread * n_threads;
584                break;
585            }
586            case HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT: {
587                size_t q_src1_row_size = (wtype == HTP_TYPE_Q4_1) ? htp_mm_q8_1_flat_row_size(ne10) : htp_mm_q8_0_flat_row_size(ne10);
588
589                src0_sz = htp_mm_round_up(n_prefetch * src0_row_size_padded, 256);
590                src1_sz = htp_mm_round_up(q_src1_row_size * src1_nrows, 256);
591
592                src0_sz = src0_sz * n_threads;
593
594                if (is_repack) {
595                    uint32_t aligned_tile_size = htp_mm_get_weight_aligned_tile_size(wtype);
596                    uint32_t n_k_tiles = ne10 / 32;
597                    uint32_t tile_row_size = n_k_tiles * aligned_tile_size;
598                    size_t repacked_vtcm_size = htp_mm_round_up(n_prefetch * tile_row_size, 256);
599                    src0_sz = repacked_vtcm_size * n_threads;
600                }
601
602                size_t quant_scratch_size_per_thread = htp_mm_round_up(ne10 * sizeof(float), QK_Q8_0_TILED * sizeof(float));
603                size_t dst_slice_per_thread = dst_nrows > 0 ? htp_mm_round_up((dst_row_size + n_threads - 1) / n_threads, 128) : 0;
604                size_t dst_size_per_thread = (dst_slice_per_thread > quant_scratch_size_per_thread) ? dst_slice_per_thread : quant_scratch_size_per_thread;
605                dst_sz = dst_size_per_thread * n_threads;
606                break;
607            }
608            default:
609                break;
610        }
611    }
612
613    size_t off = 0;
614    VTCM_LAYOUT_ALLOC(off, off_src0, src0_sz);
615    VTCM_LAYOUT_ALLOC(off, off_src1, src1_sz);
616    VTCM_LAYOUT_ALLOC(off, off_src2, src2_sz);
617    VTCM_LAYOUT_ALLOC(off, off_src3, src3_sz);
618    VTCM_LAYOUT_ALLOC(off, off_dst,  dst_sz);
619
620    L->src0_bytes = src0_sz;
621    L->src1_bytes = src1_sz;
622    L->src2_bytes = src2_sz;
623    L->src3_bytes = src3_sz;
624    L->dst_bytes  = dst_sz;
625    L->total_bytes = off;
626}
627
628static inline size_t htp_mm_hmx_get_2d_vtcm_size(
629    int wtype, uint32_t k, size_t mc, size_t nc, bool pipeline, uint32_t act_threads, uint32_t aligned_tile_size
630) {
631    struct htp_mm_hmx_vtcm_layout L;
632    htp_mm_hmx_vtcm_layout_build(&L, HTP_MM_KERNEL_HMX_2D, wtype, k, mc, nc, 1, false, pipeline, act_threads, aligned_tile_size);
633    return L.total_bytes;
634}
635
636static inline size_t htp_mm_hmx_get_batched_vtcm_size(
637    int wtype, uint32_t k, size_t mc, size_t nc, uint32_t group_size, bool use_dma_activation, bool pipeline, uint32_t act_threads) {
638    (void)pipeline;
639    struct htp_mm_hmx_vtcm_layout L;
640    htp_mm_hmx_vtcm_layout_build(&L, HTP_MM_KERNEL_HMX_F16_BATCHED, wtype, k, mc, nc, group_size, use_dma_activation, false, act_threads, 0);
641    return L.total_bytes;
642}
643
644static inline bool htp_mm_hmx_solve_batched_params(
645    int wtype,
646    uint32_t k,
647    uint32_t ne01_padded,
648    uint32_t ne11,
649    uint32_t group_size,
650    bool use_dma_activation,
651    int n_threads,
652    bool pipeline,
653    size_t vtcm_budget,
654    size_t * m_chunk_out,
655    size_t * n_chunk_out,
656    int * act_threads_out,
657    size_t * vtcm_size_out
658) {
659    size_t best_mblocks = SIZE_MAX;
660    int best_act_threads = 0;
661    size_t best_m_chunk = 0;
662    size_t best_n_chunk = 0;
663    size_t best_vtcm_size = 0;
664
665    int act_threads = n_threads;
666    while (act_threads >= 1) {
667        size_t group_overhead = htp_mm_hmx_get_batched_overhead();
668        size_t group_size_per_n, group_size_per_m, group_size_per_mn;
669        htp_mm_hmx_get_batched_chunk_costs(k, group_size, &group_size_per_n, &group_size_per_m, &group_size_per_mn);
670
671        size_t m_chunk_candidate = 0;
672        size_t n_chunk_candidate = 0;
673        size_t vtcm_size_candidate = 0;
674
675        if (htp_mm_hmx_compute_chunks(vtcm_budget, group_overhead, group_size_per_n, group_size_per_m, group_size_per_mn, hex_align_up(ne11, 32), ne01_padded,
676                               (size_t) ne01_padded * HTP_MM_HMX_COST_W_DEQUANT, (size_t) ne11 * HTP_MM_HMX_COST_A_CONVERT,
677                               &m_chunk_candidate, &n_chunk_candidate, &vtcm_size_candidate) == 0) {
678            size_t exact_size = htp_mm_hmx_get_batched_vtcm_size(wtype, k, m_chunk_candidate, n_chunk_candidate, group_size, use_dma_activation, pipeline, act_threads);
679            if (exact_size <= vtcm_budget) {
680                size_t mblocks = ((size_t) ne11 + m_chunk_candidate - 1) / m_chunk_candidate;
681                if (mblocks < best_mblocks || (mblocks == best_mblocks && act_threads > best_act_threads)) {
682                    best_mblocks = mblocks;
683                    best_act_threads = act_threads;
684                    best_m_chunk = m_chunk_candidate;
685                    best_n_chunk = n_chunk_candidate;
686                    best_vtcm_size = exact_size;
687                }
688            }
689        }
690        if (act_threads == 1) {
691            act_threads = 0;
692        } else {
693            act_threads /= 2;
694        }
695    }
696
697    if (best_act_threads > 0) {
698        *m_chunk_out = best_m_chunk;
699        *n_chunk_out = best_n_chunk;
700        *vtcm_size_out = best_vtcm_size;
701        *act_threads_out = best_act_threads;
702        return true;
703    }
704    return false;
705}
706
707static inline bool htp_mm_hmx_solve_2d_params(
708    int wtype,
709    uint32_t k,
710    uint32_t m_id_rows,
711    uint32_t ne01_padded,
712    uint32_t ne11_padded,
713    uint32_t m_for_cost,
714    int n_threads,
715    bool pipeline,
716    bool is_matmul_id,
717    uint32_t aligned_tile_size,
718    size_t vtcm_budget,
719    size_t * m_chunk_out,
720    size_t * n_chunk_out,
721    int * act_threads_out,
722    size_t * vtcm_size_out
723) {
724    size_t best_mblocks = SIZE_MAX;
725    int best_act_threads = 0;
726    size_t best_m_chunk = 0;
727    size_t best_n_chunk = 0;
728    size_t best_vtcm_size = 0;
729
730    const int m_for_chunks = is_matmul_id ? hex_align_up(m_id_rows, 32) : ne11_padded;
731
732    int act_threads = n_threads;
733    while (act_threads >= 1) {
734        size_t simple_2d_overhead = htp_mm_hmx_get_2d_overhead(pipeline, is_matmul_id);
735        size_t simple_2d_size_per_n, simple_2d_size_per_m, simple_2d_size_per_mn;
736        htp_mm_hmx_get_2d_chunk_costs(wtype, k, pipeline, aligned_tile_size, &simple_2d_size_per_n, &simple_2d_size_per_m, &simple_2d_size_per_mn);
737
738        size_t m_chunk_candidate = 0;
739        size_t n_chunk_candidate = 0;
740        size_t vtcm_size_candidate = 0;
741
742        if (htp_mm_hmx_compute_chunks(vtcm_budget, simple_2d_overhead, simple_2d_size_per_n, simple_2d_size_per_m, simple_2d_size_per_mn, m_for_chunks, ne01_padded,
743                               (size_t) ne01_padded * HTP_MM_HMX_COST_W_DEQUANT, (size_t) m_for_cost * HTP_MM_HMX_COST_A_CONVERT,
744                               &m_chunk_candidate, &n_chunk_candidate, &vtcm_size_candidate) == 0) {
745            size_t exact_size = htp_mm_hmx_get_2d_vtcm_size(wtype, k, m_chunk_candidate, n_chunk_candidate, pipeline, is_matmul_id ? 0 : act_threads, aligned_tile_size);
746            if (exact_size <= vtcm_budget) {
747                size_t mblocks = ((size_t) m_for_cost + m_chunk_candidate - 1) / m_chunk_candidate;
748                if (mblocks < best_mblocks || (mblocks == best_mblocks && act_threads > best_act_threads)) {
749                    best_mblocks = mblocks;
750                    best_act_threads = act_threads;
751                    best_m_chunk = m_chunk_candidate;
752                    best_n_chunk = n_chunk_candidate;
753                    best_vtcm_size = exact_size;
754                }
755            }
756        }
757        if (act_threads == 1) {
758            act_threads = 0;
759        } else {
760            act_threads /= 2;
761        }
762    }
763
764    if (best_act_threads > 0) {
765        *m_chunk_out = best_m_chunk;
766        *n_chunk_out = best_n_chunk;
767        *vtcm_size_out = best_vtcm_size;
768        *act_threads_out = best_act_threads;
769        return true;
770    }
771    return false;
772}
773
774#ifdef __cplusplus
775}
776#endif
777
778#endif // HTP_MATMUL_OPS_H