char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
eafe15a5e
char/aliceai-t5-moe
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 <assert.h> 6#include <HAP_compute_res.h> 7#include <HAP_farf.h> 8#include <math.h> 9#include <stdbool.h> 10#include <stdatomic.h> 11#include <stddef.h> 12#include <stdint.h> 13#include <string.h> 14 15#include "hex-dma.h" 16#include "hex-fastdiv.h" 17#include "hex-profile.h" 18#include "hmx-queue.h" 19#include "hmx-utils.h" 20#include "hvx-utils.h" 21#include "hvx-dump.h" 22#include "hvx-copy.h" 23#include "hvx-reduce.h" 24#include "hvx-flash-attn.h" 25#include "htp-vtcm.h" 26#include "work-queue.h" 27 28#define GGML_COMMON_DECL_C 29#include "ggml-common.h" 30#include "htp-ctx.h" 31#include "htp-ops.h" 32#include "htp-tensor.h" 33#include "hvx-quant.h" 34 35#include "flash-attn-ops.h" 36#include "hvx-fa-kernels.h" 37#include "hmx-fa-kernels.h" 38 39// Must be multiple of 32 40#define FLASH_ATTN_BLOCK_SIZE (32 * 2) 41 42struct htp_fa_context { 43const struct htp_ops_context * octx ; 44 45struct fastdiv_values src0_div21 ; 46struct fastdiv_values src0_div1 ; 47 48struct fastdiv_values broadcast_rk2 ; 49struct fastdiv_values broadcast_rk3 ; 50struct fastdiv_values broadcast_rv2 ; 51struct fastdiv_values broadcast_rv3 ; 52 53struct fastdiv_values src3_div2 ; 54struct fastdiv_values src3_div3 ; 55 56float scale ; 57float max_bias ; 58__fp16 logit_softcap ; 59 60uint32_t n_head_log2 ; 61float m0 ; 62float m1 ; 63__fp16 slopes [512 ]; 64 65uint32_t n_blocks ; 66 67size_t size_q_row_padded ; 68size_t size_k_row_padded ; 69size_t size_v_row_padded ; 70 71size_t size_k_block ; 72size_t size_v_block ; 73size_t size_m_block ; 74 75uint32_t qrows ; 76uint32_t qrows_per_thread ; 77uint32_t qrow_start ; 78 79bool is_q_fp32 ; 80 81size_t size_q_block ; 82size_t size_vkq_acc ; 83 84uint8_t * spad_q ; 85uint8_t * spad_k ; 86uint8_t * spad_v ; 87uint8_t * spad_m ; 88uint8_t * spad_a ; 89 90const struct htp_tensor * k ; 91const struct htp_tensor * v ; 92}; 93 94struct hmx_fa_context { 95const struct htp_ops_context * octx ; 96const struct htp_tensor * sinks ;// attention sinks (src[4]), NULL if absent 97const struct htp_tensor * k ; 98const struct htp_tensor * v ; 99bool pipeline ;// true when n_kv_blocks >= FA_MIN_KV_BLOCKS && n_threads >= 2 100uint32_t n_threads ; 101 102// Op parameters 103__fp16 scale ; 104float max_bias ; 105__fp16 logit_softcap ; 106uint32_t n_head_log2 ; 107float m0 ,m1 ; 108 109// Dimensions 110uint32_t DK ,DV ; 111uint32_t n_kv ;// kv_len 112uint32_t n_kv_heads ;// number of KV heads 113uint32_t n_heads ;// number of Q heads 114uint32_t G ;// GQA factor = n_heads / n_kv_heads 115struct fastdiv_values div_G ; 116struct fastdiv_values src3_div2 ; 117struct fastdiv_values src3_div3 ; 118uint32_t n_kv_blocks ; 119uint32_t neq1 ;// Q token count 120 121// Types 122bool is_q_fp32 ; 123bool is_dst_fp32 ; 124 125// Dynamic block sizes 126uint32_t Br ;// Q tokens per block (before GQA expansion) 127uint32_t Bc ; 128uint32_t g_br ;// hex_align_up(G * Br, 32) - actual tile row dim 129 130// VTCM buffers (allocated by vtcm_seq_alloc) 131__fp16 * vtcm_q_dma ;// Q DMA fetch buffer 132__fp16 * vtcm_q_tiles ;// Q tile format [g_br, D] 133__fp16 * vtcm_o_tiles [2 ];// O ping-pong [g_br, D] 134__fp16 * vtcm_k_fp16 [2 ];// K DMA double-buffer [Bc, D] 135__fp16 * vtcm_v_fp16 [2 ];// V DMA double-buffer [Bc, D] 136__fp16 * vtcm_k_tiles [2 ];// K tiles (transposed, double-buffered) 137__fp16 * vtcm_v_tiles [2 ];// V tiles (column-major, double-buffered) 138__fp16 * vtcm_s_tiles [2 ];// S = QK^T [g_br, Bc] (double-buffered) 139__fp16 * vtcm_p_tiles [2 ];// P = softmax(S) [g_br, Bc] 140__fp16 * vtcm_d_tiles [2 ];// Diagonal rescale, g_br/32 packed diagonal tiles (double-buffered) 141__fp16 * vtcm_d_inv_l ;// Diagonal rescale (1/l), same packed layout 142HVX_Vector * vtcm_m_vec ;// Row max [g_br] 143HVX_Vector * vtcm_l_vec ;// Row sum [g_br] 144HVX_Vector * vtcm_s_rowmax ;// Softmax intermediate [g_br] 145HVX_Vector * vtcm_p_rowsum ;// Softmax intermediate [g_br] 146HVX_Vector * vtcm_row_bufs ;// Per-thread softmax row scratch [n_threads][2][Bc/64] 147uint8_t * vtcm_hmx_scales_id ;// HMX output scales (identity) 148uint8_t * vtcm_hmx_scales_qk ;// HMX output scales (qk_scale) 149__fp16 * vtcm_mask_buf ;// VTCM mask buffer [Br * m_line], DMA'd per KV block 150__fp16 * vtcm_slopes ;// ALiBi slopes [g_br] 151size_t row_buf_stride ;// HVX vectors per row buffer (Bc/64) 152size_t mask_buf_row_stride ;// elements (__fp16) per row in mask buffer 153size_t q_tile_bytes ; 154size_t o_tile_bytes ; 155size_t col_vec_bytes ; 156size_t d_tile_bytes ; 157bool mask_broadcast ;// true when mask->ne[2] == 1 (head-independent, single 2D DMA) 158dma_cache m_cache ; 159}; 160 161static void flash_attn_ext_f16_thread (unsigned int nth ,unsigned int ith ,void * data ) { 162struct htp_fa_context * factx = (struct htp_fa_context * )data ; 163const struct htp_ops_context * octx = factx -> octx ; 164const struct htp_tensor * q = octx -> src [0 ]; 165const struct htp_tensor * k = octx -> src [1 ]; 166const struct htp_tensor * v = octx -> src [2 ]; 167const struct htp_tensor * mask = octx -> src [3 ]; 168const struct htp_tensor * sinks = octx -> src [4 ]; 169const struct htp_tensor * dst = octx -> dst ; 170 171const uint32_t neq0 = q -> ne [0 ]; 172const uint32_t neq1 = q -> ne [1 ]; 173const uint32_t neq2 = q -> ne [2 ]; 174const uint32_t neq3 = q -> ne [3 ]; 175 176const uint32_t nek0 = k -> ne [0 ]; 177const uint32_t nek1 = k -> ne [1 ]; 178const uint32_t nek2 = k -> ne [2 ]; 179const uint32_t nek3 = k -> ne [3 ]; 180 181const uint32_t nev0 = v -> ne [0 ]; 182const uint32_t nev1 = v -> ne [1 ]; 183const uint32_t nev2 = v -> ne [2 ]; 184const uint32_t nev3 = v -> ne [3 ]; 185 186const uint32_t nbq1 = q -> nb [1 ]; 187const uint32_t nbq2 = q -> nb [2 ]; 188const uint32_t nbq3 = q -> nb [3 ]; 189 190const uint32_t nbk1 = k -> nb [1 ]; 191const uint32_t nbk2 = k -> nb [2 ]; 192const uint32_t nbk3 = k -> nb [3 ]; 193 194const uint32_t nbv1 = v -> nb [1 ]; 195const uint32_t nbv2 = v -> nb [2 ]; 196const uint32_t nbv3 = v -> nb [3 ]; 197 198const uint32_t ne1 = dst -> ne [1 ]; 199const uint32_t ne2 = dst -> ne [2 ]; 200const uint32_t ne3 = dst -> ne [3 ]; 201 202const uint32_t nb1 = dst -> nb [1 ]; 203const uint32_t nb2 = dst -> nb [2 ]; 204const uint32_t nb3 = dst -> nb [3 ]; 205 206// total rows in q 207const uint32_t dr = factx -> qrows_per_thread ; 208const uint32_t ir0 = factx -> qrow_start + dr * ith ; 209const uint32_t ir1 = MIN (ir0 + dr ,factx -> qrow_start + factx -> qrows ); 210 211if (ir0 >=ir1 )return ; 212 213struct htp_thread_trace * tr = & octx -> ctx -> trace [ith ]; 214 215dma_queue * dma = octx -> ctx -> dma [ith ]; 216 217const uint32_t DK = nek0 ; 218const uint32_t DV = nev0 ; 219 220const size_t size_q_row = DK * ((q -> type == HTP_TYPE_F32 ) ?4 :2 ); 221const size_t size_k_row = htp_tensor_get_row_size (k -> type ,DK ); 222const size_t size_v_row = htp_tensor_get_row_size (v -> type ,DV ); 223 224// Scratchpad buffers for Q, K, V, Mask, and VKQ32 accumulator 225uint8_t * spad_q = factx -> spad_q + factx -> size_q_block * ith ; 226uint8_t * spad_k = factx -> spad_k + factx -> size_k_block * 2 * ith ; 227uint8_t * spad_v = factx -> spad_v + factx -> size_v_block * 2 * ith ; 228uint8_t * spad_m = factx -> spad_m + (mask ?factx -> size_m_block * HVX_FA_DMA_CACHE_SIZE :0 )* ith ; 229uint8_t * spad_a = factx -> spad_a + factx -> size_vkq_acc * ith ; 230 231dma_cache m_cache ; 232dma_cache_init (& m_cache ,spad_m ,factx -> size_m_block ,HVX_FA_DMA_CACHE_SIZE ); 233 234for (uint32_t ir = ir0 ;ir < ir1 ;++ ir ) { 235const uint32_t iq3 = fastdiv (ir ,& factx -> src0_div21 ); 236const uint32_t iq2 = fastdiv (ir - iq3 * neq2 * neq1 ,& factx -> src0_div1 ); 237const uint32_t iq1 = (ir - iq3 * neq2 * neq1 - iq2 * neq1 ); 238 239const uint32_t ik3 = fastdiv (iq3 ,& factx -> broadcast_rk3 ); 240const uint32_t ik2 = fastdiv (iq2 ,& factx -> broadcast_rk2 ); 241 242const uint32_t iv3 = fastdiv (iq3 ,& factx -> broadcast_rv3 ); 243const uint32_t iv2 = fastdiv (iq2 ,& factx -> broadcast_rv2 ); 244 245const __fp16 * mp_base = NULL ; 246if (mask ) { 247const uint32_t im2 = fastmodulo (iq2 ,mask -> ne [2 ],& factx -> src3_div2 ); 248const uint32_t im3 = fastmodulo (iq3 ,mask -> ne [3 ],& factx -> src3_div3 ); 249mp_base = (const __fp16 * ) ((const uint8_t * )mask -> data + iq1 * mask -> nb [1 ]+ im2 * mask -> nb [2 ]+ im3 * mask -> nb [3 ]); 250 } 251 252// Precalculate next row variables if there is a next row 253bool has_next_ir = (ir + 1 < ir1 ); 254uint32_t next_ik2 = 0 ,next_ik3 = 0 ,next_iv2 = 0 ,next_iv3 = 0 ; 255const uint8_t * next_q_row_ptr = NULL ; 256const __fp16 * next_mp_base = NULL ; 257 258const uint8_t * next_k_src0 = NULL ; 259const uint8_t * next_v_src0 = NULL ; 260const uint8_t * next_m_src0 = NULL ; 261uint32_t next_block_size0 = 0 ; 262 263const uint8_t * next_k_src1 = NULL ; 264const uint8_t * next_v_src1 = NULL ; 265const uint8_t * next_m_src1 = NULL ; 266uint32_t next_block_size1 = 0 ; 267 268if (has_next_ir ) { 269const uint32_t next_ir = ir + 1 ; 270const uint32_t next_iq3 = fastdiv (next_ir ,& factx -> src0_div21 ); 271const uint32_t next_iq2 = fastdiv (next_ir - next_iq3 * neq2 * neq1 ,& factx -> src0_div1 ); 272const uint32_t next_iq1 = (next_ir - next_iq3 * neq2 * neq1 - next_iq2 * neq1 ); 273 274next_ik3 = fastdiv (next_iq3 ,& factx -> broadcast_rk3 ); 275next_ik2 = fastdiv (next_iq2 ,& factx -> broadcast_rk2 ); 276 277next_iv3 = fastdiv (next_iq3 ,& factx -> broadcast_rv3 ); 278next_iv2 = fastdiv (next_iq2 ,& factx -> broadcast_rv2 ); 279 280next_q_row_ptr = (const uint8_t * )q -> data + (next_iq1 * nbq1 + next_iq2 * nbq2 + next_iq3 * nbq3 ); 281 282if (mask ) { 283const uint32_t next_im2 = fastmodulo (next_iq2 ,mask -> ne [2 ],& factx -> src3_div2 ); 284const uint32_t next_im3 = fastmodulo (next_iq3 ,mask -> ne [3 ],& factx -> src3_div3 ); 285next_mp_base = (const __fp16 * ) ((const uint8_t * )mask -> data + next_iq1 * mask -> nb [1 ]+ next_im2 * mask -> nb [2 ]+ next_im3 * mask -> nb [3 ]); 286 } 287 288// Precalculate next K/V block 0 source pointers 289 { 290const uint32_t ic_start = 0 ; 291next_block_size0 = MIN (FLASH_ATTN_BLOCK_SIZE ,nek1 - ic_start ); 292next_k_src0 = (const uint8_t * )k -> data + (ic_start * nbk1 + next_ik2 * nbk2 + next_ik3 * nbk3 ); 293next_v_src0 = (const uint8_t * )v -> data + (ic_start * nbv1 + next_iv2 * nbv2 + next_iv3 * nbv3 ); 294if (mask ) { 295next_m_src0 = (const uint8_t * ) (next_mp_base + ic_start ); 296 } 297 } 298 299// Precalculate next K/V block 1 source pointers (if n_blocks > 1) 300if (factx -> n_blocks > 1 ) { 301const uint32_t ic_start = 1 * FLASH_ATTN_BLOCK_SIZE ; 302next_block_size1 = MIN (FLASH_ATTN_BLOCK_SIZE ,nek1 - ic_start ); 303next_k_src1 = (const uint8_t * )k -> data + (ic_start * nbk1 + next_ik2 * nbk2 + next_ik3 * nbk3 ); 304next_v_src1 = (const uint8_t * )v -> data + (ic_start * nbv1 + next_iv2 * nbv2 + next_iv3 * nbv3 ); 305if (mask ) { 306next_m_src1 = (const uint8_t * ) (next_mp_base + ic_start ); 307 } 308 } 309 } 310 311if (ir == ir0 ) { 312// Fetch Q row 313const uint8_t * q_row_ptr = (const uint8_t * )q -> data + (iq1 * nbq1 + iq2 * nbq2 + iq3 * nbq3 ); 314dma_queue_push (dma ,dma_make_ptr (spad_q ,q_row_ptr ),factx -> size_q_row_padded ,nbq1 ,size_q_row ,1 ); 315 316// Prefetch first two blocks 317for (uint32_t ib = 0 ;ib < MIN (factx -> n_blocks ,2 );++ ib ) { 318const uint32_t ic_start = ib * FLASH_ATTN_BLOCK_SIZE ; 319const uint32_t current_block_size = MIN (FLASH_ATTN_BLOCK_SIZE ,nek1 - ic_start ); 320 321// K 322const uint8_t * k_src = (const uint8_t * )k -> data + (ic_start * nbk1 + ik2 * nbk2 + ik3 * nbk3 ); 323uint8_t * k_dst = spad_k + (ib %2 )* factx -> size_k_block ; 324dma_queue_push (dma ,dma_make_ptr (k_dst ,k_src ),factx -> size_k_row_padded ,nbk1 ,size_k_row ,current_block_size ); 325 326// V 327const uint8_t * v_src = (const uint8_t * )v -> data + (ic_start * nbv1 + iv2 * nbv2 + iv3 * nbv3 ); 328uint8_t * v_dst = spad_v + (ib %2 )* factx -> size_v_block ; 329dma_queue_push (dma ,dma_make_ptr (v_dst ,v_src ),factx -> size_v_row_padded ,nbv1 ,size_v_row ,current_block_size ); 330 331// Mask 332if (mask ) { 333const uint8_t * m_src = (const uint8_t * ) (mp_base + ic_start ); 334// Mask is 1D contiguous for this row 335dma_cache_push (dma ,& m_cache ,m_src ,current_block_size * 2 ,current_block_size * 2 ,current_block_size * 2 ,1 ); 336 } 337 } 338 } 339 340const uint32_t h = iq2 ;// head index 341const __fp16 slope = factx -> slopes [h ]; 342 343HVX_Vector S_vec = hvx_vec_splat_f32 (0.0f ); 344HVX_Vector M_vec = hvx_vec_splat_f32 (HTP_FA_M_INITIAL_VAL ); 345 346// Clear accumulator 347hvx_splat_f32_a (spad_a ,0 ,DV ); 348float * VKQ32 = (float * ) (spad_a + 0 ); 349 350uint8_t * q_ptr_vtcm = dma_queue_pop (dma ).dst ; 351if (factx -> is_q_fp32 ) { 352hvx_copy_f16_f32_aa (q_ptr_vtcm ,q_ptr_vtcm ,DK );// inplace convert f32 to f16 353 } 354 355const HVX_Vector slope_vec = hvx_vec_splat_f16 (slope ); 356const HVX_Vector v_neg_inf = Q6_Vh_vsplat_R (0xfbff ); 357const HVX_Vector v_cap = (factx -> logit_softcap != 0.0f ) ?hvx_vec_splat_f16 (factx -> logit_softcap ) :Q6_V_vzero (); 358const HVX_Vector vinf = Q6_Vh_vsplat_R (0xFC00 ); 359const HVX_Vector vmin = Q6_Vh_vsplat_R (0xFBFF ); 360const HVX_Vector v_log2e = hvx_vec_splat_f16 (EXP_LOG2E_F ); 361const uint32_t stride_v2 = factx -> size_v_row_padded * 2 ; 362for (uint32_t ib = 0 ;ib < factx -> n_blocks ;++ ib ) { 363const uint32_t ic_start = ib * FLASH_ATTN_BLOCK_SIZE ; 364const uint32_t current_block_size = MIN (FLASH_ATTN_BLOCK_SIZE ,nek1 - ic_start ); 365 366// Wait for DMA 367uint8_t * k_base = dma_queue_pop (dma ).dst ;// K 368uint8_t * v_base = dma_queue_pop (dma ).dst ;// V 369__fp16 * m_base = mask ?dma_queue_pop (dma ).dst :NULL ;// M 370 371if (factx -> k -> type == HTP_TYPE_Q8_0 ) { 372htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_FA_K_PREP ,ir ); 373for (uint32_t r = 0 ;r < current_block_size ;++ r ) { 374__fp16 * row_k = (__fp16 * )(k_base + r * factx -> size_k_row_padded ); 375hvx_dequantize_row_q8_0_f16 (row_k ,row_k ,DK ); 376 } 377htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_FA_K_PREP ,ir ); 378 } 379if (factx -> v -> type == HTP_TYPE_Q8_0 ) { 380htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_FA_V_PREP ,ir ); 381for (uint32_t r = 0 ;r < current_block_size ;++ r ) { 382__fp16 * row_v = (__fp16 * )(v_base + r * factx -> size_v_row_padded ); 383hvx_dequantize_row_q8_0_f16 (row_v ,row_v ,DV ); 384 } 385htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_FA_V_PREP ,ir ); 386 } 387 388htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_FA_QK ,ir ); 389 390// Inner loop processing the block from VTCM 391// 1. Compute scores (64 elements FP16) 392HVX_Vector scores_f16 = Q6_V_vzero (); 393if (current_block_size > 0 ) { 394HVX_Vector scores0 = hvx_dot_f16_f16_aa_rx32 (q_ptr_vtcm ,k_base ,factx -> size_k_row_padded ,DK ,factx -> scale ); 395HVX_Vector scores1 = (current_block_size > 32 ) ?hvx_dot_f16_f16_aa_rx32 (q_ptr_vtcm ,k_base + 32 * factx -> size_k_row_padded ,factx -> size_k_row_padded ,DK ,factx -> scale ) :Q6_V_vzero (); 396scores_f16 = hvx_vec_f32_to_f16 (scores0 ,scores1 ); 397 } 398 399// 2. Softcap (in FP16) 400if (factx -> logit_softcap != 0.0f ) { 401scores_f16 = hvx_vec_tanh_f16 (scores_f16 ); 402scores_f16 = hvx_vec_mul_f16_f16 (scores_f16 ,v_cap ); 403 } 404 405HVX_VectorPred q_tail_keep = Q6_Q_vsetq2_R (current_block_size * sizeof (__fp16 )); 406 407// 3. Mask (in FP16) 408if (mask ) { 409HVX_Vector m_vals_f16 = * (const HVX_UVector * )m_base ; 410HVX_VectorPred is_inf = Q6_Q_vcmp_eq_VhVh (m_vals_f16 ,vinf ); 411m_vals_f16 = Q6_V_vmux_QVV (is_inf ,vmin ,m_vals_f16 ); 412 413HVX_Vector m_scaled = hvx_vec_mul_f16_f16 (m_vals_f16 ,slope_vec ); 414scores_f16 = Q6_V_vmux_QVV (q_tail_keep ,hvx_vec_add_f16_f16 (scores_f16 ,m_scaled ),v_neg_inf ); 415 }else { 416scores_f16 = Q6_V_vmux_QVV (q_tail_keep ,scores_f16 ,v_neg_inf ); 417 } 418 419// Compute block max in FP16 420HVX_Vector v_max_f16 = hvx_vec_reduce_max_f16 (scores_f16 ); 421HVX_Vector v_max = Q6_V_lo_W (hvx_vec_f16_to_f32 (v_max_f16 ));// splat block max in FP32 422htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_FA_QK ,ir ); 423 424if (ib + 1 == factx -> n_blocks && has_next_ir ) { 425// Queue next row's Q row! 426dma_queue_push (dma ,dma_make_ptr (spad_q ,next_q_row_ptr ),factx -> size_q_row_padded ,nbq1 ,size_q_row ,1 ); 427 428if (factx -> n_blocks %2 == 0 ) { 429// Queue next row's block 0 (into buffer slot 0) 430uint8_t * k_dst = spad_k + 0 * factx -> size_k_block ; 431uint8_t * v_dst = spad_v + 0 * factx -> size_v_block ; 432 433// K (block 0 of next row) 434dma_queue_push (dma ,dma_make_ptr (k_dst ,next_k_src0 ),factx -> size_k_row_padded ,nbk1 ,size_k_row ,next_block_size0 ); 435 436// V (block 0 of next row) 437dma_queue_push (dma ,dma_make_ptr (v_dst ,next_v_src0 ),factx -> size_v_row_padded ,nbv1 ,size_v_row ,next_block_size0 ); 438 439// Mask (block 0 of next row) 440if (mask ) { 441dma_cache_push (dma ,& m_cache ,next_m_src0 ,next_block_size0 * 2 ,next_block_size0 * 2 ,next_block_size0 * 2 ,1 ); 442 } 443 } 444 } 445 446htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_FA_SFM ,ir ); 447 { 448// 4. Online Softmax Update 449HVX_Vector M_new_vec = Q6_Vsf_vmax_VsfVsf (v_max ,M_vec ); 450HVX_Vector diff_vec = HVX_OP_SUB_F32 (M_vec ,M_new_vec ); 451 452HVX_Vector diff_f16 = hvx_vec_f32_to_f16 (diff_vec ,diff_vec ); 453HVX_Vector diff_base2 = hvx_vec_mul_f16_f16 (diff_f16 ,v_log2e ); 454HVX_Vector ms_f16 = hvx_vec_exp2_f16 (diff_base2 ); 455HVX_Vector ms_vec = Q6_V_lo_W (hvx_vec_f16_to_f32 (ms_f16 )); 456 457M_vec = M_new_vec ; 458 459hvx_scale_vec_f32_aa ((uint8_t * )VKQ32 , (const uint8_t * )VKQ32 ,DV ,ms_vec ); 460 461// Compute P = exp2((S - M) * log2(e)) in FP16 462HVX_Vector v_m_vec_f16 = hvx_vec_f32_to_f16 (M_vec ,M_vec ); 463HVX_Vector v_s_minus_m = Q6_Vqf16_vsub_VhfVhf (scores_f16 ,v_m_vec_f16 ); 464 465HVX_Vector v_s_minus_m_base2 = hvx_vec_mul_f16_f16 (Q6_Vhf_equals_Vqf16 (v_s_minus_m ),v_log2e ); 466 467HVX_Vector P = hvx_vec_exp2_f16 (v_s_minus_m_base2 ); 468P = Q6_V_vmux_QVV (q_tail_keep ,P ,Q6_V_vzero ()); 469 470// Convert P to FP32 to update the running sum S_vec 471HVX_VectorPair P_pair = hvx_vec_f16_to_f32 (P ); 472HVX_Vector P0 = Q6_V_lo_W (P_pair ); 473HVX_Vector P1 = Q6_V_hi_W (P_pair ); 474HVX_Vector p_sum_vec = hvx_vec_reduce_sum_f32 (HVX_OP_ADD_F32 (P0 ,P1 )); 475 476S_vec = HVX_OP_ADD_F32 (HVX_OP_MUL_F32 (S_vec ,ms_vec ),p_sum_vec ); 477 478// 5. Accumulate V (F16 * F16 -> F32 accumulator) 479const uint8_t * v_ptr = v_base ; 480 481for (uint32_t j = 0 ;j < current_block_size ;j += 2 ) { 482if (j + 1 == current_block_size ) { 483HVX_Vector S0 = hvx_vec_repl_f16 (Q6_V_vror_VR (P ,j * 2 )); 484hvx_mad_f32_f16_aa_vec (VKQ32 ,v_ptr ,S0 ,DV ); 485break ; 486 } 487 488HVX_Vector S0 = hvx_vec_repl_f16 (Q6_V_vror_VR (P ,j * 2 )); 489HVX_Vector S1 = hvx_vec_repl_f16 (Q6_V_vror_VR (P , (j + 1 )* 2 )); 490 491hvx_mad_f32_f16_aa_rx2_vec (VKQ32 ,v_ptr ,v_ptr + factx -> size_v_row_padded ,S0 ,S1 ,DV ); 492v_ptr += stride_v2 ; 493 } 494 } 495htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_FA_SFM ,ir ); 496 497// Issue DMA for next+1 block (if exists) 498if (ib + 2 < factx -> n_blocks ) { 499const uint32_t next_ib = ib + 2 ; 500const uint32_t next_ic_start = next_ib * FLASH_ATTN_BLOCK_SIZE ; 501const uint32_t next_block_size = MIN (FLASH_ATTN_BLOCK_SIZE ,nek1 - next_ic_start ); 502 503// K 504const uint8_t * k_src = (const uint8_t * )k -> data + (next_ic_start * nbk1 + ik2 * nbk2 + ik3 * nbk3 ); 505dma_queue_push (dma ,dma_make_ptr (k_base ,k_src ),factx -> size_k_row_padded ,nbk1 ,size_k_row ,next_block_size ); 506 507// V 508const uint8_t * v_src = (const uint8_t * )v -> data + (next_ic_start * nbv1 + iv2 * nbv2 + iv3 * nbv3 ); 509dma_queue_push (dma ,dma_make_ptr (v_base ,v_src ),factx -> size_v_row_padded ,nbv1 ,size_v_row ,next_block_size ); 510 511// Mask 512if (mask ) { 513const uint8_t * m_src = (const uint8_t * ) (mp_base + next_ic_start ); 514dma_cache_push (dma ,& m_cache ,m_src ,next_block_size * 2 ,next_block_size * 2 ,next_block_size * 2 ,1 ); 515 } 516 } 517 } 518 519if (has_next_ir ) { 520if (factx -> n_blocks %2 == 0 ) { 521// Queue next row's block 1 (into buffer slot 1, if n_blocks > 1) 522if (factx -> n_blocks > 1 ) { 523uint8_t * k_dst = spad_k + 1 * factx -> size_k_block ; 524uint8_t * v_dst = spad_v + 1 * factx -> size_v_block ; 525 526// K (block 1 of next row) 527dma_queue_push (dma ,dma_make_ptr (k_dst ,next_k_src1 ),factx -> size_k_row_padded ,nbk1 ,size_k_row ,next_block_size1 ); 528 529// V (block 1 of next row) 530dma_queue_push (dma ,dma_make_ptr (v_dst ,next_v_src1 ),factx -> size_v_row_padded ,nbv1 ,size_v_row ,next_block_size1 ); 531 532// Mask (block 1 of next row) 533if (mask ) { 534dma_cache_push (dma ,& m_cache ,next_m_src1 ,next_block_size1 * 2 ,next_block_size1 * 2 ,next_block_size1 * 2 ,1 ); 535 } 536 } 537 }else { 538// Queue next row's block 0 (into buffer slot 0) 539 { 540uint8_t * k_dst = spad_k + 0 * factx -> size_k_block ; 541uint8_t * v_dst = spad_v + 0 * factx -> size_v_block ; 542 543// K (block 0 of next row) 544dma_queue_push (dma ,dma_make_ptr (k_dst ,next_k_src0 ),factx -> size_k_row_padded ,nbk1 ,size_k_row ,next_block_size0 ); 545 546// V (block 0 of next row) 547dma_queue_push (dma ,dma_make_ptr (v_dst ,next_v_src0 ),factx -> size_v_row_padded ,nbv1 ,size_v_row ,next_block_size0 ); 548 549// Mask (block 0 of next row) 550if (mask ) { 551dma_cache_push (dma ,& m_cache ,next_m_src0 ,next_block_size0 * 2 ,next_block_size0 * 2 ,next_block_size0 * 2 ,1 ); 552 } 553 } 554 555// Queue next row's block 1 (into buffer slot 1, if n_blocks > 1) 556if (factx -> n_blocks > 1 ) { 557uint8_t * k_dst = spad_k + 1 * factx -> size_k_block ; 558uint8_t * v_dst = spad_v + 1 * factx -> size_v_block ; 559 560// K (block 1 of next row) 561dma_queue_push (dma ,dma_make_ptr (k_dst ,next_k_src1 ),factx -> size_k_row_padded ,nbk1 ,size_k_row ,next_block_size1 ); 562 563// V (block 1 of next row) 564dma_queue_push (dma ,dma_make_ptr (v_dst ,next_v_src1 ),factx -> size_v_row_padded ,nbv1 ,size_v_row ,next_block_size1 ); 565 566// Mask (block 1 of next row) 567if (mask ) { 568dma_cache_push (dma ,& m_cache ,next_m_src1 ,next_block_size1 * 2 ,next_block_size1 * 2 ,next_block_size1 * 2 ,1 ); 569 } 570 } 571 } 572 } 573 574htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_O_PROC ,ir ); 575// sinks 576float M = hvx_vec_get_f32 (M_vec ); 577float S = hvx_vec_get_f32 (S_vec ); 578 579if (sinks ) { 580const float s = ((float * )((char * )sinks -> data ))[h ]; 581 582float vs = 1.0f ; 583 584if (s > M ) { 585HVX_Vector diff_vec = hvx_vec_splat_f32 (M - s ); 586HVX_Vector ms_vec = hvx_vec_exp_f32 (diff_vec ); 587hvx_scale_vec_f32_aa ((uint8_t * )VKQ32 , (const uint8_t * )VKQ32 ,DV ,ms_vec ); 588 589float ms = hvx_vec_get_f32 (ms_vec ); 590S = S * ms + vs ; 591 }else { 592HVX_Vector diff_vec = hvx_vec_splat_f32 (s - M ); 593vs = hvx_vec_get_f32 (hvx_vec_exp_f32 (diff_vec )); 594S += vs ; 595 } 596 } 597 598const float S_inv = S == 0.0f ?0.0f :1.0f /S ; 599hvx_scale_f32_aa ((uint8_t * )VKQ32 , (const uint8_t * )VKQ32 ,DV ,S_inv ); 600 601// Store result 602// dst indices 603const uint32_t i1 = iq1 ; 604const uint32_t i2 = iq2 ; 605const uint32_t i3 = iq3 ; 606 607// dst is permuted: [DV, n_heads, n_tokens, n_seq] 608// head stride is nb[1], token stride is nb[2], batch stride is nb[3] 609uint8_t * dst_ptr = (uint8_t * )dst -> data + i2 * dst -> nb [1 ]+ i1 * dst -> nb [2 ]+ i3 * dst -> nb [3 ]; 610 611if (dst -> type == HTP_TYPE_F32 ) { 612hvx_copy_f32_ua (dst_ptr , (uint8_t * )VKQ32 ,DV ); 613 }else if (dst -> type == HTP_TYPE_F16 ) { 614hvx_copy_f16_f32_ua (dst_ptr , (uint8_t * )VKQ32 ,DV ); 615 } 616htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_O_PROC ,ir ); 617 } 618} 619 620// ============================================================================ 621// HMX Phase args and thread logic 622// ============================================================================ 623 624typedef struct { 625struct hmx_fa_context * factx ; 626uint32_t kv_rows ; 627size_t src_stride ; 628void * curr_k ; 629uint32_t kv_start ; 630uint32_t rows_per_t ; 631size_t buf_idx ; 632}fa_k_int_args_t ; 633 634static void fa_k_interleave_thread (unsigned int n ,unsigned int i ,void * data ) { 635fa_k_int_args_t * args = (fa_k_int_args_t * )data ; 636struct hmx_fa_context * factx = args -> factx ; 637 638const uint32_t total_rows = args -> kv_rows ; 639const uint32_t rows_per_t = args -> rows_per_t ; 640const uint32_t start = i * rows_per_t ; 641const uint32_t end = (uint32_t )hex_smin (start + rows_per_t ,total_rows ); 642 643if (start >=total_rows ) { 644return ; 645 } 646 647struct htp_thread_trace * tr = & factx -> octx -> ctx -> trace [i ]; 648htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_FA_K_PREP , (uint16_t ) (args -> kv_start + start )); 649if (factx -> k -> type == HTP_TYPE_Q8_0 ) { 650for (uint32_t r = start ;r < end ;++ r ) { 651__fp16 * row_k = (__fp16 * )((char * )args -> curr_k + r * args -> src_stride * sizeof (__fp16 )); 652hvx_dequantize_row_q8_0_f16 (row_k ,row_k ,factx -> DK ); 653 } 654 } 655hmx_interleave_rows_to_tiles (factx -> vtcm_k_tiles [args -> buf_idx ], (const __fp16 * )args -> curr_k ,total_rows ,factx -> DK , 656args -> src_stride ,start ,end ); 657htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_FA_K_PREP , (uint16_t ) (args -> kv_start + start )); 658} 659 660static void fa_phase_k_interleave (struct hmx_fa_context * factx ,uint32_t kv_rows ,size_t src_stride ,void * curr_k ,uint32_t kv_start ,size_t buf_idx ) { 661work_queue_t wp = factx -> octx -> ctx -> work_queue ; 662uint32_t n = 1 ; 663if (factx -> n_threads > 1 && kv_rows >=factx -> n_threads * 2 ) { 664n = factx -> n_threads ; 665 } 666uint32_t rows_per_t = hex_align_up (hmx_ceil_div (kv_rows ,n ),2 ); 667fa_k_int_args_t args = {factx ,kv_rows ,src_stride ,curr_k ,kv_start ,rows_per_t ,buf_idx }; 668if (n > 1 ) { 669work_queue_run (wp ,fa_k_interleave_thread ,& args ,n ); 670 }else { 671fa_k_interleave_thread (1 ,0 ,& args ); 672 } 673} 674 675typedef struct { 676struct hmx_fa_context * factx ; 677uint32_t kv_rows ; 678size_t src_stride ; 679void * v_src ; 680void * v_tiles_dst ; 681size_t n_col_tiles ; 682uint32_t kv_start ; 683uint32_t rows_per_t ; 684}fa_v_int_args_t ; 685 686static void fa_v_interleave_thread (unsigned int n ,unsigned int i ,void * data ) { 687fa_v_int_args_t * args = (fa_v_int_args_t * )data ; 688struct hmx_fa_context * factx = args -> factx ; 689 690const uint32_t total_rows = args -> kv_rows ; 691const uint32_t rows_per_t = args -> rows_per_t ; 692const uint32_t start = i * rows_per_t ; 693const uint32_t end = (uint32_t )hex_smin (start + rows_per_t ,total_rows ); 694 695if (start >=total_rows ) { 696return ; 697 } 698 699__fp16 * v_tiles_dst = (__fp16 * )args -> v_tiles_dst ; 700 701struct htp_thread_trace * tr = & factx -> octx -> ctx -> trace [i ]; 702htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_FA_V_PREP , (uint16_t ) (args -> kv_start + start )); 703if (factx -> v -> type == HTP_TYPE_Q8_0 ) { 704for (uint32_t r = start ;r < end ;++ r ) { 705__fp16 * row_v = (__fp16 * )((char * )args -> v_src + r * args -> src_stride * sizeof (__fp16 )); 706hvx_dequantize_row_q8_0_f16 (row_v ,row_v ,factx -> DV ); 707 } 708 } 709hmx_interleave_cols_to_tiles (v_tiles_dst , (const __fp16 * )args -> v_src ,total_rows ,factx -> DV , 710args -> src_stride , (uint32_t )args -> n_col_tiles ,start ,end ); 711htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_FA_V_PREP , (uint16_t ) (args -> kv_start + start )); 712} 713 714static void fa_phase_v_interleave (struct hmx_fa_context * factx , 715uint32_t kv_rows , 716size_t src_stride , 717void * v_src , 718void * v_tiles_dst , 719size_t n_col_tiles , 720uint32_t kv_start ) { 721work_queue_t wp = factx -> octx -> ctx -> work_queue ; 722uint32_t n = 1 ; 723if (factx -> n_threads > 1 && kv_rows >=factx -> n_threads * 2 ) { 724n = factx -> n_threads ; 725 } 726uint32_t rows_per_t = hex_align_up (hmx_ceil_div (kv_rows ,n ),2 ); 727fa_v_int_args_t args = {factx ,kv_rows ,src_stride ,v_src ,v_tiles_dst ,n_col_tiles ,kv_start ,rows_per_t }; 728if (n > 1 ) { 729work_queue_run (wp ,fa_v_interleave_thread ,& args ,n ); 730 }else { 731fa_v_interleave_thread (1 ,0 ,& args ); 732 } 733} 734 735typedef struct { 736struct hmx_fa_context * factx ; 737const struct htp_tensor * q ; 738uint32_t q_start ; 739uint32_t kv_head ; 740uint32_t ib3 ; 741size_t n_rows_g ; 742size_t rows_per_t ; 743size_t n_rows_q ; 744bool q_transposed ; 745atomic_uint barrier ; 746}fa_q_load_args_t ; 747 748static void fa_q_load_thread (unsigned int n ,unsigned int i ,void * data ) { 749fa_q_load_args_t * args = (fa_q_load_args_t * )data ; 750struct hmx_fa_context * factx = args -> factx ; 751 752const size_t n_rows_g = args -> n_rows_g ; 753const size_t G = factx -> G ; 754const size_t DK = factx -> DK ; 755 756// Partition the padded Q rows (g_br) across threads. 757// Keep start/end even so r and r+1 are always in the same thread's range. 758const size_t rows_per_t = args -> rows_per_t ; 759const size_t start = (size_t )i * rows_per_t ; 760const size_t end = hex_smin (start + rows_per_t ,factx -> g_br ); 761 762struct htp_thread_trace * tr = & factx -> octx -> ctx -> trace [i ]; 763htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_FA_Q_PREP , (uint16_t ) (args -> q_start * G + start )); 764 765// Parallel initialization of per-block state 766 { 767const uint32_t g_br = factx -> g_br ; 768const uint32_t DV = factx -> DV ; 769 770const size_t col_vec_bytes = factx -> col_vec_bytes ; 771const size_t d_tile_bytes = factx -> d_tile_bytes ; 772 773// Initialize vtcm_l_vec & vtcm_m_vec 774const size_t l_bytes_per_t = hex_align_up (col_vec_bytes /n ,128 ); 775const size_t l_start = i * l_bytes_per_t ; 776const size_t l_end = hex_smin (l_start + l_bytes_per_t ,col_vec_bytes ); 777 778const size_t m_bytes_per_t = hex_align_up (col_vec_bytes /n ,128 ); 779const size_t m_start = i * m_bytes_per_t ; 780const size_t m_end = hex_smin (m_start + m_bytes_per_t ,col_vec_bytes ); 781 782if (factx -> sinks ) { 783const float * sinks_data = (const float * ) (uintptr_t )factx -> sinks -> data ; 784float * m_vec = (float * )factx -> vtcm_m_vec ; 785const size_t r_start = l_start /sizeof (float ); 786const size_t r_end = l_end /sizeof (float ); 787const float scale_factor = EXP_LOG2E_F ; 788 789const HVX_Vector v_scale = hvx_vec_splat_f32 (scale_factor ); 790 791for (size_t r = r_start ;r < r_end ;r += 32 ) { 792HVX_VectorAlias local_m ; 793for (size_t j = 0 ;j < 32 ;++ j ) { 794size_t curr_r = r + j ; 795if (curr_r < n_rows_g ) { 796const size_t h_idx = fastmodulo (curr_r ,G ,& factx -> div_G ); 797const size_t head = args -> kv_head * G + h_idx ; 798local_m .fp32 [j ]= sinks_data [head ]; 799 }else { 800local_m .fp32 [j ]= HTP_FA_M_INITIAL_VAL ; 801 } 802 } 803HVX_Vector v_scaled = HVX_OP_MUL_F32 (local_m .v ,v_scale ); 804* (HVX_Vector * ) (m_vec + r )= v_scaled ; 805 } 806if (l_start < col_vec_bytes ) { 807hvx_splat_u8_a ((char * )factx -> vtcm_l_vec + l_start ,0 ,l_end - l_start ); 808 } 809 }else { 810if (l_start < col_vec_bytes ) { 811hvx_splat_u8_a ((char * )factx -> vtcm_l_vec + l_start ,0 ,l_end - l_start ); 812 } 813if (m_start < col_vec_bytes ) { 814hvx_splat_f32_a ((char * )factx -> vtcm_m_vec + m_start ,HTP_FA_M_INITIAL_VAL , (m_end - m_start ) /sizeof (float )); 815 } 816 } 817 818// Zero the whole rescale region: vtcm_d_tiles[0], the optional vtcm_d_tiles[1] 819// and vtcm_d_inv_l are equal-sized and allocated back to back, so one run covers 820// them all. The scatter only ever writes the diagonal, ignore the rest. 821const size_t d_bytes_per_t = hex_align_up (d_tile_bytes /n ,128 ); 822const size_t d_start = i * d_bytes_per_t ; 823const size_t d_end = hex_smin (d_start + d_bytes_per_t ,d_tile_bytes ); 824if (d_start < d_tile_bytes ) { 825hvx_splat_u8_a ((char * )factx -> vtcm_d_tiles [0 ]+ d_start ,0 ,d_end - d_start ); 826 } 827 } 828 829if (start < factx -> g_br ) { 830const struct htp_tensor * q = args -> q ; 831const uint32_t q_start = args -> q_start ; 832const uint32_t kv_head = args -> kv_head ; 833const uint32_t ib3 = args -> ib3 ; 834 835assert (factx -> DK == factx -> DV ); 836 837const bool use_q_dma = (factx -> vtcm_q_dma != NULL ); 838 839__fp16 * q_tiles = factx -> vtcm_q_tiles ; 840if (use_q_dma ) { 841const size_t g_rows_end = hex_smin (end ,n_rows_g ); 842const uint32_t d_limit = factx -> is_q_fp32 ?DK /32 :DK /64 ; 843 844uint8_t * q_flat = (uint8_t * )factx -> vtcm_q_dma ; 845if (factx -> is_q_fp32 ) { 846switch (d_limit ) { 847case 2 :hmx_fa_q_prep_fp32_d2 (q_tiles ,q_flat ,start ,end ,g_rows_end ,DK ,G ,args -> n_rows_q ,& factx -> div_G ,args -> q_transposed );break ; 848case 4 :hmx_fa_q_prep_fp32_d4 (q_tiles ,q_flat ,start ,end ,g_rows_end ,DK ,G ,args -> n_rows_q ,& factx -> div_G ,args -> q_transposed );break ; 849default :hmx_fa_q_prep_fp32 (q_tiles ,q_flat ,start ,end ,g_rows_end ,DK ,G ,args -> n_rows_q ,& factx -> div_G ,d_limit ,args -> q_transposed );break ; 850 } 851 }else { 852switch (d_limit ) { 853case 1 :hmx_fa_q_prep_fp16_d1 (q_tiles ,q_flat ,start ,end ,g_rows_end ,DK ,G ,args -> n_rows_q ,& factx -> div_G ,args -> q_transposed );break ; 854case 2 :hmx_fa_q_prep_fp16_d2 (q_tiles ,q_flat ,start ,end ,g_rows_end ,DK ,G ,args -> n_rows_q ,& factx -> div_G ,args -> q_transposed );break ; 855default :hmx_fa_q_prep_fp16 (q_tiles ,q_flat ,start ,end ,g_rows_end ,DK ,G ,args -> n_rows_q ,& factx -> div_G ,d_limit ,args -> q_transposed );break ; 856 } 857 } 858 }else { 859// Fallback: direct-from-DDR/L2 path 860hmx_fa_q_prep_fallback (q_tiles ,q -> data ,q -> nb [1 ],q -> nb [2 ],q -> nb [3 ], 861q_start ,kv_head ,ib3 ,start ,end ,n_rows_g ,G ,DK ,factx -> is_q_fp32 ,& factx -> div_G ); 862 } 863 } 864 865// Synchronize threads before zeroing out vtcm_o_tiles[0] to prevent race condition 866if (n > 1 ) { 867atomic_fetch_sub (& args -> barrier ,1 ); 868while (atomic_load (& args -> barrier )> 0 ) { 869// spin wait 870 } 871 } 872 873// Zero out vtcm_o_tiles[0] as it was used as temp_q_vtcm 874 { 875const uint32_t g_br = factx -> g_br ; 876const uint32_t DV = factx -> DV ; 877const size_t o_tile_bytes = factx -> o_tile_bytes ; 878const size_t o_bytes_per_t = hex_align_up (o_tile_bytes /n ,128 ); 879const size_t o_start = i * o_bytes_per_t ; 880const size_t o_end = hex_smin (o_start + o_bytes_per_t ,o_tile_bytes ); 881if (o_start < o_tile_bytes ) { 882hvx_splat_u8_a ((char * )factx -> vtcm_o_tiles [0 ]+ o_start ,0 ,o_end - o_start ); 883 } 884 } 885htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_FA_Q_PREP , (uint16_t ) (args -> q_start * G + start )); 886} 887 888static void fa_phase_q_load (struct hmx_fa_context * factx , 889const struct htp_tensor * q , 890uint32_t q_start , 891uint32_t kv_head , 892uint32_t ib3 , 893size_t n_rows_g ) { 894work_queue_t wp = factx -> octx -> ctx -> work_queue ; 895uint32_t n = 1 ; 896if (factx -> n_threads > 1 && n_rows_g >= (size_t ) (factx -> n_threads * 2 )) { 897n = factx -> n_threads ; 898 } 899size_t rows_per_t = hex_align_up (hmx_ceil_div (factx -> g_br ,n ),2 ); 900const uint32_t n_rows_q = hex_smin (factx -> Br ,factx -> neq1 - q_start ); 901fa_q_load_args_t args ; 902args .factx = factx ; 903args .q = q ; 904args .q_start = q_start ; 905args .kv_head = kv_head ; 906args .ib3 = ib3 ; 907args .n_rows_g = n_rows_g ; 908args .rows_per_t = rows_per_t ; 909args .n_rows_q = n_rows_q ; 910args .q_transposed = q -> nb [1 ]< q -> nb [2 ]; 911atomic_init (& args .barrier ,n ); 912if (n > 1 ) { 913work_queue_run (wp ,fa_q_load_thread ,& args ,n ); 914 }else { 915fa_q_load_thread (1 ,0 ,& args ); 916 } 917} 918 919typedef struct { 920struct hmx_fa_context * factx ; 921const struct htp_tensor * dst ; 922const __fp16 * o_tile_src ; 923uint32_t q_start ; 924uint32_t kv_head ; 925uint32_t ib3 ; 926size_t n_rows_g ; 927size_t rows_per_t ; 928}fa_o_store_args_t ; 929 930static void fa_o_store_thread_f32 (unsigned int n ,unsigned int i ,void * data ) { 931fa_o_store_args_t * args = (fa_o_store_args_t * )data ; 932struct hmx_fa_context * factx = args -> factx ; 933 934const size_t n_rows_g = args -> n_rows_g ; 935const size_t G = factx -> G ; 936const size_t DV = factx -> DV ; 937 938const size_t rows_per_t = args -> rows_per_t ; 939const size_t start = (size_t )i * rows_per_t ; 940const size_t end = hex_smin (start + rows_per_t ,n_rows_g ); 941 942if (start >=n_rows_g ) { 943return ; 944 } 945 946struct htp_thread_trace * tr = & factx -> octx -> ctx -> trace [i ]; 947htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_O_PROC , (uint16_t ) (args -> q_start * G + start )); 948 949const struct htp_tensor * dst = args -> dst ; 950const __fp16 * o_tile_src = args -> o_tile_src ; 951const uint32_t q_start = args -> q_start ; 952const uint32_t kv_head = args -> kv_head ; 953const uint32_t ib3 = args -> ib3 ; 954 955size_t q_idx = fastdiv (start ,& factx -> div_G ); 956size_t h_idx = fastmodulo (start ,G ,& factx -> div_G ); 957 958for (size_t r = start ;r < end ;++ r ) { 959float * out = (float * ) ((uint8_t * )dst -> data + (kv_head * G + h_idx )* dst -> nb [1 ]+ 960 (q_start + q_idx )* dst -> nb [2 ]+ ib3 * dst -> nb [3 ]); 961 962size_t r0 = r /HMX_FP16_TILE_N_ROWS ; 963size_t r1 = r %HMX_FP16_TILE_N_ROWS ; 964const __fp16 * tile_row_base = o_tile_src + r0 * HMX_FP16_TILE_N_ROWS * DV ; 965 966for (uint32_t d = 0 ;d < DV /32 ;++ d ) { 967const HVX_Vector * in_tile = (const HVX_Vector * ) (tile_row_base + d * HMX_FP16_TILE_N_ELMS ); 968HVX_VectorPair vp = hvx_vec_f16_to_f32_shuff (in_tile [r1 /2 ]); 969if (r1 %2 == 0 ) { 970* (HVX_UVector * ) (out + d * 32 )= Q6_V_lo_W (vp ); 971 }else { 972* (HVX_UVector * ) (out + d * 32 )= Q6_V_hi_W (vp ); 973 } 974 } 975 976h_idx ++ ; 977if (h_idx == G ) { 978h_idx = 0 ; 979q_idx ++ ; 980 } 981 } 982htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_O_PROC , (uint16_t ) (args -> q_start * G + start )); 983} 984 985static void fa_o_store_thread_f16 (unsigned int n ,unsigned int i ,void * data ) { 986fa_o_store_args_t * args = (fa_o_store_args_t * )data ; 987struct hmx_fa_context * factx = args -> factx ; 988 989const size_t n_rows_g = args -> n_rows_g ; 990const size_t rows_per_t = args -> rows_per_t ; 991const size_t G = factx -> G ; 992const size_t DV = factx -> DV ; 993const size_t start = (size_t )i * rows_per_t ; 994const size_t end = hex_smin (start + rows_per_t ,n_rows_g ); 995 996if (start >=n_rows_g ) { 997return ; 998 } 999 1000struct htp_thread_trace * tr = & factx -> octx -> ctx -> trace [i ]; 1001htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_O_PROC , (uint16_t ) (args -> q_start * G + start )); 1002 1003const struct htp_tensor * dst = args -> dst ; 1004const __fp16 * o_tile_src = args -> o_tile_src ; 1005const uint32_t q_start = args -> q_start ; 1006const uint32_t kv_head = args -> kv_head ; 1007const uint32_t ib3 = args -> ib3 ; 1008 1009size_t q_idx = fastdiv (start ,& factx -> div_G ); 1010size_t h_idx = fastmodulo (start ,G ,& factx -> div_G ); 1011 1012for (size_t r = start ;r < end ;++ r ) { 1013__fp16 * out = (__fp16 * ) ((uint8_t * )dst -> data + (kv_head * G + h_idx )* dst -> nb [1 ]+ 1014 (q_start + q_idx )* dst -> nb [2 ]+ ib3 * dst -> nb [3 ]); 1015 1016size_t r0 = r /HMX_FP16_TILE_N_ROWS ; 1017size_t r1 = r %HMX_FP16_TILE_N_ROWS ; 1018const __fp16 * tile_row_base = o_tile_src + r0 * HMX_FP16_TILE_N_ROWS * DV ; 1019 1020for (uint32_t d = 0 ;d < DV /64 ;++ d ) { 1021const __fp16 * in_dtile = tile_row_base + d * HMX_FP16_TILE_N_ELMS * 2 ; 1022const HVX_Vector * pv_in0 = ((const HVX_Vector * )in_dtile )+ r1 /2 ; 1023const HVX_Vector * pv_in1 = pv_in0 + 16 ; 1024HVX_VectorPair vp = Q6_W_vdeal_VVR (* pv_in1 ,* pv_in0 ,-2 ); 1025if (r1 %2 == 0 ) { 1026* (HVX_UVector * ) (out + d * 64 )= Q6_V_lo_W (vp ); 1027 }else { 1028* (HVX_UVector * ) (out + d * 64 )= Q6_V_hi_W (vp ); 1029 } 1030 } 1031 1032h_idx ++ ; 1033if (h_idx == G ) { 1034h_idx = 0 ; 1035q_idx ++ ; 1036 } 1037 } 1038htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_O_PROC , (uint16_t ) (args -> q_start * G + start )); 1039} 1040 1041static void fa_phase_o_store (struct hmx_fa_context * factx , 1042const struct htp_tensor * dst , 1043const __fp16 * o_tile_src , 1044uint32_t q_start , 1045uint32_t kv_head , 1046uint32_t ib3 , 1047size_t n_rows_g ) { 1048work_queue_t wp = factx -> octx -> ctx -> work_queue ; 1049uint32_t n = 1 ; 1050if (factx -> n_threads > 1 && n_rows_g >= (size_t ) (factx -> n_threads * 2 )) { 1051n = factx -> n_threads ; 1052 } 1053size_t rows_per_t = hmx_ceil_div (n_rows_g ,n ); 1054fa_o_store_args_t args = {factx ,dst ,o_tile_src ,q_start ,kv_head ,ib3 ,n_rows_g ,rows_per_t }; 1055worker_callback_t store_fn = factx -> is_dst_fp32 ?fa_o_store_thread_f32 :fa_o_store_thread_f16 ; 1056if (n > 1 ) { 1057work_queue_run (wp ,store_fn ,& args ,n ); 1058 }else { 1059store_fn (1 ,0 ,& args ); 1060 } 1061} 1062 1063typedef struct { 1064struct hmx_fa_context * factx ; 1065size_t buf_idx ; 1066size_t kv_rows ; 1067size_t n_rows_g ; 1068size_t n_col_tiles ; 1069size_t n_tiles_per_bc ; 1070size_t n_row_tiles ; 1071size_t n_row_tiles_g_br ; 1072uint32_t Bc ; 1073uint32_t G ; 1074uint32_t kv_head ; 1075uint32_t kv_start ; 1076uint32_t q_start ; 1077uint32_t ib3 ; 1078bool has_alibi ;// true when max_bias != 0 (need slope * mask + add) 1079__fp16 * slopes ; 1080const struct htp_tensor * mask ; 1081const __fp16 * mask_vtcm ;// VTCM mask buffer base (NULL = DDR fallback) 1082size_t mask_vtcm_row_stride ;// elements (__fp16) per row in VTCM mask buffer 1083struct fastdiv_values thread_div ; 1084}fa_softmax_args_t ; 1085 1086static inline void fa_softmax_impl ( 1087unsigned int n ,unsigned int i ,void * data , 1088const bool has_mask , 1089const bool mask_broadcast , 1090const bool is_g1 , 1091const bool has_alibi , 1092const bool has_softcap 1093) { 1094fa_softmax_args_t * args = (fa_softmax_args_t * )data ; 1095struct hmx_fa_context * factx = args -> factx ; 1096 1097const size_t n_rows_g = args -> n_rows_g ; 1098const size_t kv_rows = args -> kv_rows ; 1099const size_t Bc = args -> Bc ; 1100const size_t G = args -> G ; 1101const size_t n_tiles_per_bc = args -> n_tiles_per_bc ; 1102const size_t n_row_vec_cnt = hmx_ceil_div (n_rows_g ,64 ); 1103const uint32_t im3 = has_mask ?fastmodulo (args -> ib3 ,args -> mask -> ne [3 ],& factx -> src3_div3 ) :0 ; 1104 1105size_t vec_start = 0 ; 1106size_t vec_end = n_row_vec_cnt ; 1107if (n > 1 ) { 1108const size_t vecs_per_t = fastdiv (n_row_vec_cnt + n - 1 ,& args -> thread_div ); 1109vec_start = i * vecs_per_t ; 1110vec_end = hex_smin (vec_start + vecs_per_t ,n_row_vec_cnt ); 1111 } 1112 1113if (vec_start >=n_row_vec_cnt ) { 1114return ; 1115 } 1116 1117struct htp_thread_trace * tr = & factx -> octx -> ctx -> trace [i ]; 1118htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_FA_SFM , (uint16_t ) (args -> q_start * G + vec_start * 64 )); 1119 1120// Per-thread row scratch: thread i uses bufs at offset i * 2 * stride 1121const size_t row_buf_stride = factx -> row_buf_stride ; 1122HVX_Vector * my_row_buf0 = factx -> vtcm_row_bufs + i * 2 * row_buf_stride ; 1123HVX_Vector * my_row_buf1 = my_row_buf0 + row_buf_stride ; 1124 1125const HVX_Vector v_neg_inf = Q6_Vh_vsplat_R (0xfbff ); 1126 1127for (size_t r_vec_idx = vec_start ;r_vec_idx < vec_end ;++ r_vec_idx ) { 1128HVX_Vector rowmax_acc_v = v_neg_inf ; 1129HVX_Vector rowsum_acc_v = Q6_V_vzero (); 1130HVX_Vector m_prev_v0 = factx -> vtcm_m_vec [r_vec_idx * 2 + 0 ]; 1131HVX_Vector m_prev_v1 = factx -> vtcm_m_vec [r_vec_idx * 2 + 1 ]; 1132 1133HVX_Vector v_slopes = Q6_V_vzero (); 1134if (has_alibi ) { 1135v_slopes = hvx_vmem (args -> slopes + r_vec_idx * 64 ); 1136 } 1137 1138for (uint32_t r_vec_off = 0 ;r_vec_off < 64 ;r_vec_off += 2 ) { 1139uint32_t r = r_vec_idx * 64 + r_vec_off ; 1140if (r >=hex_align_up (n_rows_g ,2 )) { 1141break ; 1142 } 1143 1144uint32_t r0 = r /HMX_FP16_TILE_N_ROWS ; 1145uint32_t r1 = r %HMX_FP16_TILE_N_ROWS ; 1146 1147const __fp16 * s_ld_base = factx -> vtcm_s_tiles [args -> buf_idx ]+ r0 * HMX_FP16_TILE_N_ROWS * Bc ; 1148__fp16 * p_st_base = factx -> vtcm_p_tiles [args -> buf_idx ]+ r0 * HMX_FP16_TILE_N_ROWS * Bc ; 1149 1150// Decode 2 rows from S tiles into per-thread row buffers 1151if (has_softcap ) { 1152const HVX_Vector v_cap = hvx_vec_splat_f16 (factx -> logit_softcap ); 1153for (size_t c = 0 ;c < kv_rows ;c += 64 ) { 1154size_t ci = c /64 ; 1155const __fp16 * in_dtile = s_ld_base + ci * HMX_FP16_TILE_N_ELMS * 2 ; 1156const HVX_Vector * pv_s_in0 = ((const HVX_Vector * )in_dtile )+ r1 /2 ; 1157const HVX_Vector * pv_s_in1 = pv_s_in0 + 16 ; 1158 1159HVX_VectorPair vp_s_drow = Q6_W_vdeal_VVR (* pv_s_in1 ,* pv_s_in0 ,-2 ); 1160HVX_Vector v_s_row0 = Q6_V_lo_W (vp_s_drow ); 1161HVX_Vector v_s_row1 = Q6_V_hi_W (vp_s_drow ); 1162 1163HVX_Vector t0 = hvx_vec_tanh_f16 (v_s_row0 ); 1164my_row_buf0 [ci ]= hvx_vec_mul_f16_f16 (t0 ,v_cap ); 1165 1166HVX_Vector t1 = hvx_vec_tanh_f16 (v_s_row1 ); 1167my_row_buf1 [ci ]= hvx_vec_mul_f16_f16 (t1 ,v_cap ); 1168 } 1169 }else { 1170size_t c = 0 ; 1171for (;c + 64 < kv_rows ;c += 128 ) { 1172size_t ci0 = c /64 ; 1173size_t ci1 = ci0 + 1 ; 1174const __fp16 * in_dtile0 = s_ld_base + ci0 * HMX_FP16_TILE_N_ELMS * 2 ; 1175const __fp16 * in_dtile1 = s_ld_base + ci1 * HMX_FP16_TILE_N_ELMS * 2 ; 1176const HVX_Vector * pv_s_in0_0 = ((const HVX_Vector * )in_dtile0 )+ r1 /2 ; 1177const HVX_Vector * pv_s_in1_0 = pv_s_in0_0 + 16 ; 1178const HVX_Vector * pv_s_in0_1 = ((const HVX_Vector * )in_dtile1 )+ r1 /2 ; 1179const HVX_Vector * pv_s_in1_1 = pv_s_in0_1 + 16 ; 1180 1181HVX_VectorPair vp_s_drow0 = Q6_W_vdeal_VVR (* pv_s_in1_0 ,* pv_s_in0_0 ,-2 ); 1182my_row_buf0 [ci0 ]= Q6_V_lo_W (vp_s_drow0 ); 1183my_row_buf1 [ci0 ]= Q6_V_hi_W (vp_s_drow0 ); 1184 1185HVX_VectorPair vp_s_drow1 = Q6_W_vdeal_VVR (* pv_s_in1_1 ,* pv_s_in0_1 ,-2 ); 1186my_row_buf0 [ci1 ]= Q6_V_lo_W (vp_s_drow1 ); 1187my_row_buf1 [ci1 ]= Q6_V_hi_W (vp_s_drow1 ); 1188 } 1189for (;c < kv_rows ;c += 64 ) { 1190size_t ci = c /64 ; 1191const __fp16 * in_dtile = s_ld_base + ci * HMX_FP16_TILE_N_ELMS * 2 ; 1192const HVX_Vector * pv_s_in0 = ((const HVX_Vector * )in_dtile )+ r1 /2 ; 1193const HVX_Vector * pv_s_in1 = pv_s_in0 + 16 ; 1194 1195HVX_VectorPair vp_s_drow = Q6_W_vdeal_VVR (* pv_s_in1 ,* pv_s_in0 ,-2 ); 1196my_row_buf0 [ci ]= Q6_V_lo_W (vp_s_drow ); 1197my_row_buf1 [ci ]= Q6_V_hi_W (vp_s_drow ); 1198 } 1199 } 1200 1201// Apply mask & compute rowmax(S) 1202HVX_Vector v_slope0 = Q6_V_vzero (); 1203HVX_Vector v_slope1 = Q6_V_vzero (); 1204if (has_alibi ) { 1205v_slope0 = hvx_vec_repl_f16 (Q6_V_vror_VR (v_slopes ,r_vec_off * 2 )); 1206v_slope1 = (r + 1 < n_rows_g ) ?hvx_vec_repl_f16 (Q6_V_vror_VR (v_slopes , (r_vec_off + 1 )* 2 )) :Q6_V_vzero (); 1207 } 1208 1209const HVX_Vector v_threshold = Q6_Vh_vsplat_R (0xcc00 );// fp16 -16.0 1210 1211HVX_Vector v_s_rowmax0 = v_neg_inf ; 1212HVX_Vector v_s_rowmax1 = v_neg_inf ; 1213if (has_mask ) { 1214for (size_t c = 0 ;c < kv_rows ;c += 64 ) { 1215size_t ci = c /64 ; 1216const size_t ne = hex_smin (kv_rows - c ,64 ); 1217HVX_VectorPred q_tail_keep = Q6_Q_vsetq2_R (ne * sizeof (__fp16 )); 1218 1219HVX_Vector v_mask0 ,v_mask1 ; 1220 1221if (mask_broadcast ) { 1222if (is_g1 ) { 1223const size_t qi0 = r + 0 ; 1224v_mask0 = * (const HVX_Vector * ) (args -> mask_vtcm + qi0 * args -> mask_vtcm_row_stride + c ); 1225v_mask1 = v_neg_inf ; 1226if (r + 1 < n_rows_g ) { 1227const size_t qi1 = r + 1 ; 1228v_mask1 = * (const HVX_Vector * ) (args -> mask_vtcm + qi1 * args -> mask_vtcm_row_stride + c ); 1229 } 1230 }else { 1231const size_t qi0 = fastdiv (r + 0 ,& factx -> div_G ); 1232v_mask0 = * (const HVX_Vector * ) (args -> mask_vtcm + qi0 * args -> mask_vtcm_row_stride + c ); 1233v_mask1 = v_neg_inf ; 1234if (r + 1 < n_rows_g ) { 1235const size_t qi1 = fastdiv (r + 1 ,& factx -> div_G ); 1236if (qi1 == qi0 ) { 1237v_mask1 = v_mask0 ; 1238 }else { 1239v_mask1 = * (const HVX_Vector * ) (args -> mask_vtcm + qi1 * args -> mask_vtcm_row_stride + c ); 1240 } 1241 } 1242 } 1243 }else { 1244// Head-dependent mask: pre-interleaved per row r. 1245const size_t r0 = r + 0 ; 1246v_mask0 = * (const HVX_Vector * ) (args -> mask_vtcm + r0 * args -> mask_vtcm_row_stride + c ); 1247v_mask1 = v_neg_inf ; 1248if (r + 1 < n_rows_g ) { 1249const size_t r1 = r + 1 ; 1250v_mask1 = * (const HVX_Vector * ) (args -> mask_vtcm + r1 * args -> mask_vtcm_row_stride + c ); 1251 } 1252 } 1253 1254// Threshold: mask values below -16.0 are treated as -inf (causal mask). 1255HVX_VectorPred q_keep0 = Q6_Q_and_QQ (Q6_Q_vcmp_gt_VhfVhf (v_mask0 ,v_threshold ),q_tail_keep ); 1256HVX_VectorPred q_keep1 = Q6_Q_and_QQ (Q6_Q_vcmp_gt_VhfVhf (v_mask1 ,v_threshold ),q_tail_keep ); 1257 1258// Scale mask values by log2(e) for base-2 calculations 1259const HVX_Vector v_log2e = hvx_vec_splat_f16 (EXP_LOG2E_F ); 1260HVX_Vector v_mask0_scaled = hvx_vec_mul_f16_f16 (v_mask0 ,v_log2e ); 1261HVX_Vector v_mask1_scaled = hvx_vec_mul_f16_f16 (v_mask1 ,v_log2e ); 1262 1263if (has_alibi ) { 1264HVX_Vector v_sm0 = hvx_vec_mul_f16_f16 (v_mask0_scaled ,v_slope0 ); 1265HVX_Vector v_sm1 = hvx_vec_mul_f16_f16 (v_mask1_scaled ,v_slope1 ); 1266my_row_buf0 [ci ]= Q6_V_vmux_QVV (q_keep0 ,hvx_vec_add_f16_f16 (my_row_buf0 [ci ],v_sm0 ),v_neg_inf ); 1267my_row_buf1 [ci ]= Q6_V_vmux_QVV (q_keep1 ,hvx_vec_add_f16_f16 (my_row_buf1 [ci ],v_sm1 ),v_neg_inf ); 1268 }else { 1269my_row_buf0 [ci ]= Q6_V_vmux_QVV (q_keep0 ,hvx_vec_add_f16_f16 (my_row_buf0 [ci ],v_mask0_scaled ),v_neg_inf ); 1270my_row_buf1 [ci ]= Q6_V_vmux_QVV (q_keep1 ,hvx_vec_add_f16_f16 (my_row_buf1 [ci ],v_mask1_scaled ),v_neg_inf ); 1271 } 1272 1273v_s_rowmax0 = Q6_Vhf_vmax_VhfVhf (v_s_rowmax0 ,my_row_buf0 [ci ]); 1274v_s_rowmax1 = Q6_Vhf_vmax_VhfVhf (v_s_rowmax1 ,my_row_buf1 [ci ]); 1275 } 1276 }else { 1277size_t c = 0 ; 1278for (;c + 64 < kv_rows ;c += 128 ) { 1279size_t ci0 = c /64 ; 1280size_t ci1 = ci0 + 1 ; 1281v_s_rowmax0 = Q6_Vhf_vmax_VhfVhf (v_s_rowmax0 ,my_row_buf0 [ci0 ]); 1282v_s_rowmax1 = Q6_Vhf_vmax_VhfVhf (v_s_rowmax1 ,my_row_buf1 [ci0 ]); 1283v_s_rowmax0 = Q6_Vhf_vmax_VhfVhf (v_s_rowmax0 ,my_row_buf0 [ci1 ]); 1284v_s_rowmax1 = Q6_Vhf_vmax_VhfVhf (v_s_rowmax1 ,my_row_buf1 [ci1 ]); 1285 } 1286for (;c < kv_rows ;c += 64 ) { 1287size_t ci = c /64 ; 1288const size_t ne = hex_smin (kv_rows - c ,64 ); 1289HVX_VectorPred q_tail_keep = Q6_Q_vsetq2_R (ne * sizeof (__fp16 )); 1290if (ne < 64 ) { 1291my_row_buf0 [ci ]= Q6_V_vmux_QVV (q_tail_keep ,my_row_buf0 [ci ],v_neg_inf ); 1292my_row_buf1 [ci ]= Q6_V_vmux_QVV (q_tail_keep ,my_row_buf1 [ci ],v_neg_inf ); 1293 } 1294v_s_rowmax0 = Q6_Vhf_vmax_VhfVhf (v_s_rowmax0 ,my_row_buf0 [ci ]); 1295v_s_rowmax1 = Q6_Vhf_vmax_VhfVhf (v_s_rowmax1 ,my_row_buf1 [ci ]); 1296 } 1297 } 1298 1299v_s_rowmax0 = hvx_vec_reduce_max_f16 (v_s_rowmax0 ); 1300v_s_rowmax1 = hvx_vec_reduce_max_f16 (v_s_rowmax1 ); 1301 1302// Splat m_prev[r], m_prev[r+1] from the float per-row accumulators and convert to fp16 vectors 1303HVX_Vector v_m_prev0 ,v_m_prev1 ; 1304if (r_vec_off < 32 ) { 1305HVX_Vector v0 = hvx_vec_repl_f32 (Q6_V_vror_VR (m_prev_v0 ,r_vec_off * 4 )); 1306v_m_prev0 = hvx_vec_f32_to_f16 (v0 ,v0 ); 1307if (r + 1 < n_rows_g ) { 1308HVX_Vector v1 = hvx_vec_repl_f32 (Q6_V_vror_VR (m_prev_v0 , (r_vec_off + 1 )* 4 )); 1309v_m_prev1 = hvx_vec_f32_to_f16 (v1 ,v1 ); 1310 }else { 1311v_m_prev1 = Q6_V_vzero (); 1312 } 1313 }else { 1314HVX_Vector v0 = hvx_vec_repl_f32 (Q6_V_vror_VR (m_prev_v1 , (r_vec_off - 32 )* 4 )); 1315v_m_prev0 = hvx_vec_f32_to_f16 (v0 ,v0 ); 1316if (r + 1 < n_rows_g ) { 1317HVX_Vector v1 = hvx_vec_repl_f32 (Q6_V_vror_VR (m_prev_v1 , (r_vec_off + 1 - 32 )* 4 )); 1318v_m_prev1 = hvx_vec_f32_to_f16 (v1 ,v1 ); 1319 }else { 1320v_m_prev1 = Q6_V_vzero (); 1321 } 1322 } 1323 1324HVX_Vector v_dup_m0 = Q6_Vhf_vmax_VhfVhf (v_m_prev0 ,v_s_rowmax0 ); 1325HVX_Vector v_dup_m1 = Q6_Vhf_vmax_VhfVhf (v_m_prev1 ,v_s_rowmax1 ); 1326 1327// Insert row r, r+1 rowmax into rowmax_acc_v 1328 { 1329HVX_VectorPred p_start = Q6_Q_vsetq_R (r_vec_off * 2 ); 1330HVX_VectorPred p_mid = Q6_Q_vsetq_R ((r_vec_off + 1 )* 2 ); 1331HVX_VectorPred p_end = Q6_Q_vsetq2_R ((r_vec_off + 2 )* 2 ); 1332HVX_VectorPred p_lane0 = Q6_Q_and_QQn (p_mid ,p_start ); 1333HVX_VectorPred p_lane1 = Q6_Q_and_QQn (p_end ,p_mid ); 1334rowmax_acc_v = Q6_V_vmux_QVV (p_lane0 ,v_dup_m0 ,rowmax_acc_v ); 1335rowmax_acc_v = Q6_V_vmux_QVV (p_lane1 ,v_dup_m1 ,rowmax_acc_v ); 1336 } 1337 1338// Compute P = exp(S - m_new) 1339const HVX_Vector v_zero = Q6_V_vzero (); 1340HVX_Vector v_p_rowsum0 = v_zero ; 1341HVX_Vector v_p_rowsum1 = v_zero ; 1342 1343size_t c = 0 ; 1344for (;c + 64 < kv_rows ;c += 128 ) { 1345size_t ci0 = c /64 ; 1346size_t ci1 = ci0 + 1 ; 1347 1348HVX_Vector v_s_minus_m0_0 = Q6_Vqf16_vsub_VhfVhf (my_row_buf0 [ci0 ],v_dup_m0 ); 1349HVX_Vector v_s_minus_m1_0 = Q6_Vqf16_vsub_VhfVhf (my_row_buf1 [ci0 ],v_dup_m1 ); 1350HVX_Vector v_s_minus_m0_1 = Q6_Vqf16_vsub_VhfVhf (my_row_buf0 [ci1 ],v_dup_m0 ); 1351HVX_Vector v_s_minus_m1_1 = Q6_Vqf16_vsub_VhfVhf (my_row_buf1 [ci1 ],v_dup_m1 ); 1352 1353HVX_Vector v_p_row0_hf_0 = hvx_vec_exp2_f16 (Q6_Vhf_equals_Vqf16 (v_s_minus_m0_0 )); 1354HVX_Vector v_p_row1_hf_0 = hvx_vec_exp2_f16 (Q6_Vhf_equals_Vqf16 (v_s_minus_m1_0 )); 1355HVX_Vector v_p_row0_hf_1 = hvx_vec_exp2_f16 (Q6_Vhf_equals_Vqf16 (v_s_minus_m0_1 )); 1356HVX_Vector v_p_row1_hf_1 = hvx_vec_exp2_f16 (Q6_Vhf_equals_Vqf16 (v_s_minus_m1_1 )); 1357 1358__fp16 * out_dtile0 = p_st_base + ci0 * HMX_FP16_TILE_N_ELMS * 2 ; 1359__fp16 * out_dtile1 = p_st_base + ci1 * HMX_FP16_TILE_N_ELMS * 2 ; 1360HVX_Vector * pv_p_out0_0 = ((HVX_Vector * )out_dtile0 )+ r1 /2 ; 1361HVX_Vector * pv_p_out1_0 = pv_p_out0_0 + 16 ; 1362HVX_Vector * pv_p_out0_1 = ((HVX_Vector * )out_dtile1 )+ r1 /2 ; 1363HVX_Vector * pv_p_out1_1 = pv_p_out0_1 + 16 ; 1364 1365HVX_VectorPair vp_p_dual0 = Q6_W_vshuff_VVR (v_p_row1_hf_0 ,v_p_row0_hf_0 ,-2 ); 1366* pv_p_out0_0 = Q6_V_lo_W (vp_p_dual0 ); 1367* pv_p_out1_0 = Q6_V_hi_W (vp_p_dual0 ); 1368 1369HVX_VectorPair vp_p_dual1 = Q6_W_vshuff_VVR (v_p_row1_hf_1 ,v_p_row0_hf_1 ,-2 ); 1370* pv_p_out0_1 = Q6_V_lo_W (vp_p_dual1 ); 1371* pv_p_out1_1 = Q6_V_hi_W (vp_p_dual1 ); 1372 1373HVX_VectorPair vp_p0_0 = hvx_vec_f16_to_f32_shuff (v_p_row0_hf_0 ); 1374HVX_VectorPair vp_p1_0 = hvx_vec_f16_to_f32_shuff (v_p_row1_hf_0 ); 1375HVX_VectorPair vp_p0_1 = hvx_vec_f16_to_f32_shuff (v_p_row0_hf_1 ); 1376HVX_VectorPair vp_p1_1 = hvx_vec_f16_to_f32_shuff (v_p_row1_hf_1 ); 1377 1378v_p_rowsum0 = Q6_Vqf32_vadd_Vqf32Vqf32 (v_p_rowsum0 ,Q6_Vqf32_vadd_VsfVsf (Q6_V_lo_W (vp_p0_0 ),Q6_V_hi_W (vp_p0_0 ))); 1379v_p_rowsum0 = Q6_Vqf32_vadd_Vqf32Vqf32 (v_p_rowsum0 ,Q6_Vqf32_vadd_VsfVsf (Q6_V_lo_W (vp_p0_1 ),Q6_V_hi_W (vp_p0_1 ))); 1380v_p_rowsum1 = Q6_Vqf32_vadd_Vqf32Vqf32 (v_p_rowsum1 ,Q6_Vqf32_vadd_VsfVsf (Q6_V_lo_W (vp_p1_0 ),Q6_V_hi_W (vp_p1_0 ))); 1381v_p_rowsum1 = Q6_Vqf32_vadd_Vqf32Vqf32 (v_p_rowsum1 ,Q6_Vqf32_vadd_VsfVsf (Q6_V_lo_W (vp_p1_1 ),Q6_V_hi_W (vp_p1_1 ))); 1382 } 1383for (size_t c_rem = c ;c_rem < kv_rows ;c_rem += 64 ) { 1384size_t ci = c_rem /64 ; 1385HVX_Vector v_s_minus_m0 = Q6_Vqf16_vsub_VhfVhf (my_row_buf0 [ci ],v_dup_m0 ); 1386HVX_Vector v_s_minus_m1 = Q6_Vqf16_vsub_VhfVhf (my_row_buf1 [ci ],v_dup_m1 ); 1387 1388HVX_Vector v_p_row0_hf = hvx_vec_exp2_f16 (Q6_Vhf_equals_Vqf16 (v_s_minus_m0 )); 1389HVX_Vector v_p_row1_hf = hvx_vec_exp2_f16 (Q6_Vhf_equals_Vqf16 (v_s_minus_m1 )); 1390__fp16 * out_dtile = p_st_base + ci * HMX_FP16_TILE_N_ELMS * 2 ; 1391HVX_Vector * pv_p_out0 = ((HVX_Vector * )out_dtile )+ r1 /2 ; 1392HVX_Vector * pv_p_out1 = pv_p_out0 + 16 ; 1393 1394HVX_VectorPair vp_p_dual = Q6_W_vshuff_VVR (v_p_row1_hf ,v_p_row0_hf ,-2 ); 1395* pv_p_out0 = Q6_V_lo_W (vp_p_dual ); 1396* pv_p_out1 = Q6_V_hi_W (vp_p_dual ); 1397 1398HVX_VectorPair vp_p0 = hvx_vec_f16_to_f32_shuff (v_p_row0_hf ); 1399HVX_VectorPair vp_p1 = hvx_vec_f16_to_f32_shuff (v_p_row1_hf ); 1400 1401v_p_rowsum0 = Q6_Vqf32_vadd_Vqf32Vqf32 (v_p_rowsum0 ,Q6_Vqf32_vadd_VsfVsf (Q6_V_lo_W (vp_p0 ),Q6_V_hi_W (vp_p0 ))); 1402v_p_rowsum1 = Q6_Vqf32_vadd_Vqf32Vqf32 (v_p_rowsum1 ,Q6_Vqf32_vadd_VsfVsf (Q6_V_lo_W (vp_p1 ),Q6_V_hi_W (vp_p1 ))); 1403 } 1404 1405HVX_Vector rowsum0_sf = hvx_vec_reduce_sum_f32 (Q6_Vsf_equals_Vqf32 (v_p_rowsum0 )); 1406HVX_Vector rowsum1_sf = hvx_vec_reduce_sum_f32 (Q6_Vsf_equals_Vqf32 (v_p_rowsum1 )); 1407 { 1408HVX_Vector rv0_v = hvx_vec_f32_to_f16 (rowsum0_sf ,rowsum0_sf ); 1409HVX_Vector rv1_v = hvx_vec_f32_to_f16 (rowsum1_sf ,rowsum1_sf ); 1410 1411HVX_VectorPred p_start = Q6_Q_vsetq_R (r_vec_off * 2 ); 1412HVX_VectorPred p_mid = Q6_Q_vsetq_R ((r_vec_off + 1 )* 2 ); 1413HVX_VectorPred p_end = Q6_Q_vsetq2_R ((r_vec_off + 2 )* 2 ); 1414HVX_VectorPred p_lane0 = Q6_Q_and_QQn (p_mid ,p_start ); 1415HVX_VectorPred p_lane1 = Q6_Q_and_QQn (p_end ,p_mid ); 1416rowsum_acc_v = Q6_V_vmux_QVV (p_lane0 ,rv0_v ,rowsum_acc_v ); 1417rowsum_acc_v = Q6_V_vmux_QVV (p_lane1 ,rv1_v ,rowsum_acc_v ); 1418 } 1419 } 1420 1421// Inline fa_ml_update_and_build_d for this vector (lock-free and in parallel) 1422HVX_VectorPair rowmax_acc_pair = hvx_vec_f16_to_f32 (rowmax_acc_v ); 1423HVX_Vector v_rowmax_acc_f32_0 = Q6_V_lo_W (rowmax_acc_pair ); 1424HVX_Vector v_rowmax_acc_f32_1 = Q6_V_hi_W (rowmax_acc_pair ); 1425 1426HVX_Vector v_m_curr0 = Q6_Vsf_vmax_VsfVsf (m_prev_v0 ,v_rowmax_acc_f32_0 ); 1427HVX_Vector v_m_curr1 = Q6_Vsf_vmax_VsfVsf (m_prev_v1 ,v_rowmax_acc_f32_1 ); 1428 1429HVX_Vector v_m_diff0 = HVX_OP_SUB_F32 (m_prev_v0 ,v_m_curr0 ); 1430HVX_Vector v_m_diff1 = HVX_OP_SUB_F32 (m_prev_v1 ,v_m_curr1 ); 1431 1432HVX_Vector v_m_diff_f16 = hvx_vec_f32_to_f16 (v_m_diff0 ,v_m_diff1 ); 1433HVX_Vector exp_m_diff_f16 = hvx_vec_exp2_f16 (v_m_diff_f16 ); 1434 1435HVX_VectorPair exp_m_diff_pair = hvx_vec_f16_to_f32 (exp_m_diff_f16 ); 1436HVX_Vector exp_m_diff0 = Q6_V_lo_W (exp_m_diff_pair ); 1437HVX_Vector exp_m_diff1 = Q6_V_hi_W (exp_m_diff_pair ); 1438 1439HVX_VectorPair rowsum_acc_pair = hvx_vec_f16_to_f32 (rowsum_acc_v ); 1440HVX_Vector v_rowsum_acc_f32_0 = Q6_V_lo_W (rowsum_acc_pair ); 1441HVX_Vector v_rowsum_acc_f32_1 = Q6_V_hi_W (rowsum_acc_pair ); 1442 1443HVX_Vector v_l_curr0 ; 1444HVX_Vector v_l_curr1 ; 1445if (args -> kv_start == 0 && factx -> sinks != NULL ) { 1446// First KV block with sinks: m_prev holds the seeded sink value (not -inf), 1447// so exp_m_diff = exp2(sink - m_curr) is the sink's contribution to the 1448// denominator. l_prev is 0 here, so add exp_m_diff directly instead of 1449// multiplying the (uninitialized) l_prev term. 1450v_l_curr0 = HVX_OP_ADD_F32 (exp_m_diff0 ,v_rowsum_acc_f32_0 ); 1451v_l_curr1 = HVX_OP_ADD_F32 (exp_m_diff1 ,v_rowsum_acc_f32_1 ); 1452 }else { 1453HVX_Vector l_prev_v0 = factx -> vtcm_l_vec [r_vec_idx * 2 + 0 ]; 1454HVX_Vector l_prev_v1 = factx -> vtcm_l_vec [r_vec_idx * 2 + 1 ]; 1455v_l_curr0 = HVX_OP_ADD_F32 (HVX_OP_MUL_F32 (l_prev_v0 ,exp_m_diff0 ),v_rowsum_acc_f32_0 ); 1456v_l_curr1 = HVX_OP_ADD_F32 (HVX_OP_MUL_F32 (l_prev_v1 ,exp_m_diff1 ),v_rowsum_acc_f32_1 ); 1457 } 1458 1459factx -> vtcm_m_vec [r_vec_idx * 2 + 0 ]= v_m_curr0 ; 1460factx -> vtcm_m_vec [r_vec_idx * 2 + 1 ]= v_m_curr1 ; 1461factx -> vtcm_l_vec [r_vec_idx * 2 + 0 ]= v_l_curr0 ; 1462factx -> vtcm_l_vec [r_vec_idx * 2 + 1 ]= v_l_curr1 ; 1463 1464// Build diagonal tile D = diag(exp(m_diff)) 1465const HVX_Vector v_offsets = * (const HVX_Vector * )d_tile_scatter_offsets ; 1466const HVX_VectorPred q_32_mask = Q6_Q_vsetq_R (32 * sizeof (__fp16 )); 1467HVX_Vector v_exp_m_diff = exp_m_diff_f16 ; 1468 1469__fp16 * const d_tiles_out = factx -> vtcm_d_tiles [args -> buf_idx ]; 1470 1471size_t t0 = r_vec_idx * 2 ; 1472if (t0 < args -> n_row_tiles ) { 1473const HVX_Vector v_content = v_exp_m_diff ; 1474__fp16 * out_base = d_tiles_out + t0 * HMX_FP16_TILE_N_ELMS ; 1475Q6_vscatter_QRMVhV (q_32_mask , (size_t )out_base ,HMX_FP16_TILE_SIZE - 1 ,v_offsets ,v_content ); 1476 } 1477 1478size_t t1 = r_vec_idx * 2 + 1 ; 1479if (t1 < args -> n_row_tiles ) { 1480const HVX_Vector v_content = Q6_V_vror_VR (v_exp_m_diff ,64 ); 1481__fp16 * out_base = d_tiles_out + t1 * HMX_FP16_TILE_N_ELMS ; 1482Q6_vscatter_QRMVhV (q_32_mask , (size_t )out_base ,HMX_FP16_TILE_SIZE - 1 ,v_offsets ,v_content ); 1483 } 1484 } 1485htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_FA_SFM , (uint16_t ) (args -> q_start * G + vec_start * 64 )); 1486} 1487 1488static void fa_softmax_thread_nomask (unsigned int n ,unsigned int i ,void * data ) { 1489fa_softmax_impl (n ,i ,data , 1490/*has_mask=*/ false, 1491/*mask_broadcast=*/ false, 1492/*is_g1=*/ false, 1493/*has_alibi=*/ false, 1494/*has_softcap=*/ false); 1495} 1496 1497static void fa_softmax_thread_mask_broadcast_g1 (unsigned int n ,unsigned int i ,void * data ) { 1498fa_softmax_impl (n ,i ,data , 1499/*has_mask=*/ true, 1500/*mask_broadcast=*/ true, 1501/*is_g1=*/ true, 1502/*has_alibi=*/ false, 1503/*has_softcap=*/ false); 1504} 1505 1506static void fa_softmax_thread_mask_broadcast_gn (unsigned int n ,unsigned int i ,void * data ) { 1507fa_softmax_impl (n ,i ,data , 1508/*has_mask=*/ true, 1509/*mask_broadcast=*/ true, 1510/*is_g1=*/ false, 1511/*has_alibi=*/ false, 1512/*has_softcap=*/ false); 1513} 1514 1515static void fa_softmax_thread (unsigned int n ,unsigned int i ,void * data ) { 1516fa_softmax_args_t * args = (fa_softmax_args_t * )data ; 1517struct hmx_fa_context * factx = args -> factx ; 1518 1519const bool has_mask = (args -> mask != NULL ); 1520const bool mask_broadcast = factx -> mask_broadcast ; 1521const bool is_g1 = (args -> G == 1 ); 1522const bool has_alibi = args -> has_alibi ; 1523const bool has_softcap = (factx -> logit_softcap != 0.0f ); 1524 1525fa_softmax_impl (n ,i ,data ,has_mask ,mask_broadcast ,is_g1 ,has_alibi ,has_softcap ); 1526} 1527 1528static __attribute__((noinline ))void fa_build_d_diag_inv_l (struct hmx_fa_context * factx , 1529size_t n_row_tiles , 1530size_t n_row_tiles_g_br ) { 1531const HVX_Vector v_offsets = * (const HVX_Vector * )d_tile_scatter_offsets ; 1532const HVX_VectorPred q_32_mask = Q6_Q_vsetq_R (32 * sizeof (__fp16 )); 1533const HVX_Vector one = hvx_vec_splat_f32 (1.0f ); 1534 1535HVX_Vector v_content = Q6_V_vzero (); 1536for (size_t i = 0 ;i < n_row_tiles ;++ i ) { 1537if ((i %2 )== 0 ) { 1538HVX_Vector inv_lo = HVX_OP_MUL_F32 (one ,hvx_vec_inverse_f32 (factx -> vtcm_l_vec [i ])); 1539HVX_Vector inv_hi = (i + 1 < n_row_tiles ) ?HVX_OP_MUL_F32 (one ,hvx_vec_inverse_f32 (factx -> vtcm_l_vec [i + 1 ])) :Q6_V_vzero (); 1540v_content = hvx_vec_f32_to_f16 (inv_lo ,inv_hi ); 1541 }else { 1542v_content = Q6_V_vror_VR (v_content ,64 ); 1543 } 1544 1545__fp16 * out_base = factx -> vtcm_d_inv_l + i * HMX_FP16_TILE_N_ELMS ; 1546Q6_vscatter_QRMVhV (q_32_mask , (size_t )out_base ,HMX_FP16_TILE_SIZE - 1 ,v_offsets ,v_content ); 1547 } 1548} 1549 1550static void fa_phase_softmax_and_build_d (struct hmx_fa_context * factx , 1551fa_softmax_args_t * sargs , 1552size_t n_row_tiles , 1553size_t n_row_tiles_g_br ) { 1554work_queue_t wp = factx -> octx -> ctx -> work_queue ; 1555const size_t n_row_vec_cnt = hmx_ceil_div (sargs -> n_rows_g ,64 ); 1556 1557worker_callback_t softmax_fn = fa_softmax_thread ; 1558if (sargs -> mask == NULL && factx -> logit_softcap == 0.0f && !sargs -> has_alibi ) { 1559softmax_fn = fa_softmax_thread_nomask ; 1560 }else if (sargs -> mask != NULL && factx -> mask_broadcast && factx -> logit_softcap == 0.0f && !sargs -> has_alibi ) { 1561if (sargs -> G == 1 ) { 1562softmax_fn = fa_softmax_thread_mask_broadcast_g1 ; 1563 }else { 1564softmax_fn = fa_softmax_thread_mask_broadcast_gn ; 1565 } 1566 } 1567 1568if (factx -> n_threads > 1 && n_row_vec_cnt >=2 ) { 1569uint32_t n_use = (uint32_t )hex_smin ((size_t )factx -> n_threads ,n_row_vec_cnt ); 1570sargs -> thread_div = init_fastdiv_values (n_use ); 1571work_queue_run (wp ,softmax_fn ,sargs ,n_use ); 1572 }else { 1573softmax_fn (1 ,0 ,sargs ); 1574 } 1575} 1576 1577// ============================================================================ 1578// HMX job structs and worker functions 1579// ============================================================================ 1580 1581typedef struct { 1582const __fp16 * q_tiles ; 1583const __fp16 * k_tiles ; 1584__fp16 * s_tiles ; 1585size_t n_row_tiles ; 1586size_t n_col_tiles ; 1587size_t n_dot_tiles ;// DK / 32 1588size_t n_tiles_per_bc ; 1589uint8_t * hmx_scales ; 1590}hmx_fa_qk_job_t ; 1591 1592static void hmx_fa_qk_dot_worker (void * data ) { 1593hmx_fa_qk_job_t * job = (hmx_fa_qk_job_t * )data ; 1594const size_t n_row_tiles = job -> n_row_tiles ; 1595const size_t n_col_tiles = job -> n_col_tiles ; 1596const size_t n_dot_tiles = job -> n_dot_tiles ; 1597const size_t n_tiles_per_bc = job -> n_tiles_per_bc ; 1598const __fp16 * restrictq_tiles = job -> q_tiles ; 1599const __fp16 * restrictk_tiles = job -> k_tiles ; 1600__fp16 * restricts_tiles = job -> s_tiles ; 1601__builtin_assume (n_row_tiles > 0 ); 1602__builtin_assume (n_col_tiles > 0 ); 1603__builtin_assume (n_dot_tiles > 0 ); 1604 1605 asmvolatile (HMX_SET_BIAS ("%0" ) ::"r" ((unsigned int )job -> hmx_scales )); 1606const size_t dot_stride = n_dot_tiles * HMX_FP16_TILE_N_ELMS ; 1607for (size_t r = 0 ;r < n_row_tiles ;++ r ) { 1608const __fp16 * row_tiles = q_tiles + r * dot_stride ; 1609const __fp16 * col_tiles = k_tiles ; 1610__fp16 * out_tile = s_tiles + r * n_tiles_per_bc * HMX_FP16_TILE_N_ELMS ; 1611 1612for (size_t c = 0 ;c < n_col_tiles ;++ c ) { 1613hmx_fa_qk_dot_tile (row_tiles ,col_tiles ,out_tile ,n_dot_tiles ); 1614col_tiles += dot_stride ; 1615out_tile += HMX_FP16_TILE_N_ELMS ; 1616 } 1617 } 1618} 1619 1620typedef struct { 1621__fp16 * o_curr ; 1622const __fp16 * o_prev ; 1623const __fp16 * p_tiles ; 1624const __fp16 * v_tiles ; 1625const __fp16 * d_tiles ; 1626uint8_t * hmx_scales ; 1627size_t n_row_tiles ; 1628size_t n_col_tiles ; 1629size_t n_row_tiles_g_br ; 1630size_t n_tiles_per_bc ; 1631size_t DV ; 1632}hmx_fa_o_update_job_t ; 1633 1634static void hmx_fa_o_update_worker (void * data ) { 1635hmx_fa_o_update_job_t * job = (hmx_fa_o_update_job_t * )data ; 1636const size_t n_row_tiles = job -> n_row_tiles ; 1637const size_t n_col_tiles = job -> n_col_tiles ; 1638const size_t n_row_tiles_g_br = job -> n_row_tiles_g_br ; 1639const size_t n_tiles_per_bc = job -> n_tiles_per_bc ; 1640const size_t DV_tiles = job -> DV /32 ; 1641const __fp16 * restrictd_tiles = job -> d_tiles ; 1642const __fp16 * restrictp_tiles = job -> p_tiles ; 1643const __fp16 * restrictv_tiles = job -> v_tiles ; 1644const __fp16 * restricto_prev = job -> o_prev ; 1645__fp16 * restricto_curr = job -> o_curr ; 1646__builtin_assume (n_row_tiles > 0 ); 1647__builtin_assume (n_col_tiles > 0 ); 1648__builtin_assume (DV_tiles > 0 ); 1649 1650 asmvolatile (HMX_SET_BIAS ("%0" ) ::"r" ((unsigned int )job -> hmx_scales )); 1651const size_t o_stride = n_row_tiles_g_br * HMX_FP16_TILE_N_ELMS ; 1652const size_t v_stride = n_tiles_per_bc * HMX_FP16_TILE_N_ELMS ; 1653for (size_t r = 0 ;r < n_row_tiles ;++ r ) { 1654const __fp16 * d_diag = d_tiles + r * HMX_FP16_TILE_N_ELMS ; 1655const __fp16 * p_tile_in = p_tiles + (r * n_tiles_per_bc )* HMX_FP16_TILE_N_ELMS ; 1656const __fp16 * o_rc = o_prev + r * HMX_FP16_TILE_N_ELMS ; 1657const __fp16 * v_tile_in = v_tiles ; 1658__fp16 * o_tile_out = o_curr + r * HMX_FP16_TILE_N_ELMS ; 1659 1660for (size_t c = 0 ;c < DV_tiles ;++ c ) { 1661hmx_fa_o_update_tile (d_diag ,o_rc ,p_tile_in ,v_tile_in ,o_tile_out ,n_col_tiles ); 1662o_rc += o_stride ; 1663v_tile_in += v_stride ; 1664o_tile_out += o_stride ; 1665 } 1666 } 1667} 1668 1669typedef struct { 1670__fp16 * o_curr ;// output (row-major tile layout) 1671const __fp16 * o_prev ;// input (column-major tile layout) 1672const __fp16 * d_tiles ;// diag(1/l) tiles 1673uint8_t * hmx_scales ; 1674size_t n_row_tiles ; 1675size_t n_row_tiles_g_br ; 1676size_t DV ; 1677}hmx_fa_o_norm_job_t ; 1678 1679static void hmx_fa_o_norm_worker (void * data ) { 1680hmx_fa_o_norm_job_t * job = (hmx_fa_o_norm_job_t * )data ; 1681const size_t n_row_tiles = job -> n_row_tiles ; 1682const size_t n_row_tiles_g_br = job -> n_row_tiles_g_br ; 1683const size_t DV_tiles = job -> DV /32 ; 1684const __fp16 * restrictd_tiles = job -> d_tiles ; 1685const __fp16 * restricto_prev = job -> o_prev ; 1686__fp16 * restricto_curr = job -> o_curr ; 1687__builtin_assume (n_row_tiles > 0 ); 1688__builtin_assume (DV_tiles > 0 ); 1689 1690 asmvolatile (HMX_SET_BIAS ("%0" ) ::"r" ((unsigned int )job -> hmx_scales )); 1691const size_t o_stride = n_row_tiles_g_br * HMX_FP16_TILE_N_ELMS ; 1692for (size_t r = 0 ;r < n_row_tiles ;++ r ) { 1693const __fp16 * d_diag = d_tiles + r * HMX_FP16_TILE_N_ELMS ; 1694const __fp16 * o_rc = o_prev + r * HMX_FP16_TILE_N_ELMS ; 1695__fp16 * o_out = o_curr + r * DV_tiles * HMX_FP16_TILE_N_ELMS ; 1696 1697for (size_t c = 0 ;c < DV_tiles ;++ c ) { 1698hmx_fa_o_norm_tile (d_diag ,o_rc ,o_out ); 1699o_rc += o_stride ; 1700o_out += HMX_FP16_TILE_N_ELMS ; 1701 } 1702 } 1703} 1704 1705// Populate per-GQA-row ALiBi slopes for a given KV head. 1706static __attribute__((noinline ))void fa_compute_slopes ( 1707const struct hmx_fa_context * factx , 1708uint32_t kv_head , 1709size_t n_rows_g ) { 1710__fp16 * slopes = factx -> vtcm_slopes ; 1711if (factx -> max_bias == 0.0f ) { 1712hvx_splat_f16_a (slopes ,1.0f ,n_rows_g ); 1713return ; 1714 } 1715 1716const uint32_t G = factx -> G ; 1717const uint32_t n_head_log2 = factx -> n_head_log2 ; 1718const float m0 = factx -> m0 ; 1719const float m1 = factx -> m1 ; 1720 1721__fp16 temp_slopes [512 ] __attribute__((aligned (128 ))); 1722if (G <=32 ) { 1723// Fast path: Compute G unique slope values in vector registers 1724HVX_Vector v_val = hvx_alibi_slopes (kv_head ,G ,n_head_log2 ,m0 ,m1 ); 1725 1726__fp16 temp_slopes_aligned [64 ] __attribute__((aligned (128 ))); 1727hvx_vmem (temp_slopes_aligned )= hvx_vec_f32_to_f16 (v_val ,Q6_V_vzero ()); 1728 1729for (uint32_t i = 0 ;i < G ;++ i ) { 1730temp_slopes [i ]= temp_slopes_aligned [i ]; 1731 } 1732 }else { 1733// Fallback path: G > 32 (rare configurations) 1734for (uint32_t i = 0 ;i < G ;++ i ) { 1735temp_slopes [i ]= (__fp16 )alibi_slope (kv_head * G + i ,n_head_log2 ,m0 ,m1 ); 1736 } 1737 } 1738 1739// Allocate stack buffer to avoid scalar writes to VTCM (which generates L2 misses) 1740__fp16 local_slopes [n_rows_g ] __attribute__((aligned (128 ))); 1741for (size_t r = 0 ;r < n_rows_g ;++ r ) { 1742local_slopes [r ]= temp_slopes [fastmodulo (r ,G ,& factx -> div_G )]; 1743 } 1744 1745// Copy to VTCM slopes using HVX block copy (both are aligned to 128 bytes) 1746hvx_copy_f16_aa ((uint8_t * )slopes , (const uint8_t * )local_slopes ,n_rows_g ); 1747} 1748 1749static void fa_push_mask_dma_gqa ( 1750dma_queue * dma , 1751const struct htp_tensor * mask , 1752uint32_t q_start , 1753uint32_t im3 , 1754uint32_t kv_start , 1755uint32_t kv_head , 1756uint32_t G , 1757uint32_t m_line_bytes , 1758uint32_t kv_rows , 1759uint32_t n_rows_q , 1760struct hmx_fa_context * factx 1761) { 1762for (uint32_t g = 0 ;g < G ;++ g ) { 1763const uint32_t h_idx = kv_head * G + g ; 1764const uint32_t im2 = fastmodulo (h_idx ,mask -> ne [2 ],& factx -> src3_div2 ); 1765const uint8_t * ms_src = (const uint8_t * )mask -> data + q_start * mask -> nb [1 ]+ 1766im2 * mask -> nb [2 ]+ im3 * mask -> nb [3 ]+ kv_start * sizeof (__fp16 ); 1767uint8_t * ms_dst = (uint8_t * )factx -> vtcm_mask_buf + g * m_line_bytes ; 1768dma_queue_push (dma ,dma_make_ptr (ms_dst ,ms_src ),G * m_line_bytes ,mask -> nb [1 ],kv_rows * sizeof (__fp16 ),n_rows_q ); 1769 } 1770} 1771 1772static void fa_pop_mask_dma_gqa (dma_queue * dma ,uint32_t G ) { 1773for (uint32_t g = 0 ;g < G ;++ g ) { 1774dma_queue_pop (dma ); 1775 } 1776} 1777 1778static inline void fa_prefetch_block (dma_queue * dma ,const struct htp_tensor * k ,const struct htp_tensor * v ,const struct htp_tensor * mask , 1779uint32_t b ,size_t Bc ,size_t size_k_row_padded ,size_t size_k_row ,size_t size_v_row_padded ,size_t size_v_row , 1780uint32_t ik2 ,uint32_t ik3 ,uint32_t iv2 ,uint32_t iv3 ,uint32_t q_start ,uint32_t im3 ,uint32_t kv_head ,uint32_t G , 1781size_t m_line_bytes ,size_t n_rows_q ,size_t nek1 ,size_t prefetch_buf ,struct hmx_fa_context * factx ) { 1782const uint32_t prefetch_start = b * Bc ; 1783const uint32_t prefetch_rows = hex_smin (Bc ,nek1 - prefetch_start ); 1784const uint8_t * k_prefetch_src = (const uint8_t * )k -> data + prefetch_start * k -> nb [1 ]+ ik2 * k -> nb [2 ]+ ik3 * k -> nb [3 ]; 1785dma_queue_push (dma ,dma_make_ptr (factx -> vtcm_k_fp16 [prefetch_buf ],k_prefetch_src ),size_k_row_padded ,k -> nb [1 ],size_k_row ,prefetch_rows ); 1786const uint8_t * v_prefetch_src = (const uint8_t * )v -> data + prefetch_start * v -> nb [1 ]+ iv2 * v -> nb [2 ]+ iv3 * v -> nb [3 ]; 1787dma_queue_push (dma ,dma_make_ptr (factx -> vtcm_v_fp16 [prefetch_buf ],v_prefetch_src ),size_v_row_padded ,v -> nb [1 ],size_v_row ,prefetch_rows ); 1788 1789if (mask ) { 1790if (__builtin_expect (factx -> mask_broadcast , true)) { 1791const uint8_t * ms_src = (const uint8_t * )mask -> data + q_start * mask -> nb [1 ]+ im3 * mask -> nb [3 ]+ prefetch_start * sizeof (__fp16 ); 1792dma_cache_push (dma ,& factx -> m_cache ,ms_src ,m_line_bytes ,mask -> nb [1 ],prefetch_rows * sizeof (__fp16 ),n_rows_q ); 1793 }else { 1794fa_push_mask_dma_gqa (dma ,mask ,q_start ,im3 ,prefetch_start ,kv_head ,G ,m_line_bytes ,prefetch_rows ,n_rows_q ,factx ); 1795 } 1796 } 1797} 1798 1799// ============================================================================ 1800// Core HMX flash attention algorithm (GQA-merged) 1801// ============================================================================ 1802 1803int hmx_flash_attn_ext (struct htp_ops_context * octx ) { 1804struct htp_thread_trace * tr_hvx = & octx -> ctx -> trace [0 ]; 1805struct htp_thread_trace * tr_hmx = & octx -> ctx -> trace [HTP_MAX_NTHREADS ]; 1806const struct htp_tensor * q = octx -> src [0 ]; 1807const struct htp_tensor * k = octx -> src [1 ]; 1808const struct htp_tensor * v = octx -> src [2 ]; 1809const struct htp_tensor * mask = (octx -> src [3 ]&& octx -> src [3 ]-> data ) ?octx -> src [3 ] :NULL ; 1810const struct htp_tensor * dst = octx -> dst ; 1811 1812struct htp_context * const ctx = octx -> ctx ; 1813 1814if (!ctx -> hmx_enabled ) { 1815return HTP_STATUS_NO_SUPPORT ; 1816 } 1817 1818// Dimensions 1819const uint32_t neq0 = q -> ne [0 ];// head_dim (DK) 1820const uint32_t neq1 = q -> ne [1 ];// n_tokens 1821const uint32_t neq2 = q -> ne [2 ];// n_heads 1822const uint32_t neq3 = q -> ne [3 ];// n_seqs 1823 1824const uint32_t nek0 = k -> ne [0 ];// head_dim 1825const uint32_t nek1 = k -> ne [1 ];// kv_len 1826 1827const uint32_t nev0 = v -> ne [0 ];// head_dim (DV) 1828 1829const uint32_t DK = neq0 ; 1830const uint32_t DV = nev0 ; 1831 1832// HMX requires head_dim to be multiple of 32 1833if (DK %32 != 0 || DV %32 != 0 ) { 1834return HTP_STATUS_NO_SUPPORT ; 1835 } 1836 1837const struct htp_fa_kernel_params * kparams = (const struct htp_fa_kernel_params * )octx -> kernel_params ; 1838const uint32_t n_kv_heads = k -> ne [2 ]; 1839 1840// ======== Build context ======== 1841struct hmx_fa_context factx ; 1842memset (& factx ,0 ,sizeof (factx )); 1843factx .octx = octx ; 1844factx .sinks = octx -> src [4 ];// NULL if this op has no attention sinks 1845factx .k = k ; 1846factx .v = v ; 1847factx .n_threads = kparams -> n_threads ; 1848factx .DK = DK ; 1849factx .DV = DV ; 1850factx .n_kv = nek1 ; 1851factx .n_kv_heads = n_kv_heads ; 1852factx .n_heads = neq2 ; 1853factx .G = kparams -> G ; 1854factx .div_G = kparams -> u .hmx .div_G ; 1855factx .neq1 = neq1 ; 1856factx .Br = kparams -> Br ; 1857factx .Bc = kparams -> Bc ; 1858factx .g_br = kparams -> u .hmx .g_br ; 1859factx .n_kv_blocks = kparams -> n_kv_blocks ; 1860factx .is_q_fp32 = (kparams -> is_q_fp32 != 0 ); 1861factx .is_dst_fp32 = (kparams -> is_dst_fp32 != 0 ); 1862factx .pipeline = (kparams -> u .hmx .pipeline != 0 ); 1863factx .mask_broadcast = (kparams -> u .hmx .mask_broadcast != 0 ); 1864if (mask ) { 1865factx .src3_div2 = kparams -> src3_div2 ; 1866factx .src3_div3 = kparams -> src3_div3 ; 1867 } 1868 1869if (kparams -> logit_softcap == 0.0f ) { 1870factx .scale = (__fp16 ) (kparams -> scale * EXP_LOG2E_F );// log2(e) 1871 }else { 1872factx .scale = (__fp16 )kparams -> scale ; 1873 } 1874factx .max_bias = kparams -> max_bias ; 1875factx .logit_softcap = (__fp16 ) (kparams -> logit_softcap * EXP_LOG2E_F ); 1876 1877factx .n_head_log2 = kparams -> n_head_log2 ; 1878factx .m0 = kparams -> m0 ; 1879factx .m1 = kparams -> m1 ; 1880 1881const uint32_t Br = factx .Br ; 1882const uint32_t Bc = factx .Bc ; 1883const uint32_t g_br = factx .g_br ; 1884const bool pipeline = factx .pipeline ; 1885const uint32_t n_threads = factx .n_threads ; 1886const uint32_t G = factx .G ; 1887 1888// Multi-device: split Q blocks across devices 1889const uint32_t n_q_blocks = (neq1 + Br - 1 ) /Br ; 1890uint32_t q_start_min = 0 ; 1891uint32_t q_start_max = neq1 ; 1892 1893if (octx -> ctx -> mdev .count > 1 ) { 1894const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition (n_q_blocks ,htp_tensor_mdev_data_aligned (dst ) ?1 :0 ,octx -> ctx -> mdev .idx ,octx -> ctx -> mdev .count ,& octx -> ctx -> mdev .count_div ); 1895const uint32_t block_start = range .start ; 1896const uint32_t block_end = range .start + range .count ; 1897 1898if (block_start >=block_end ) { 1899return HTP_STATUS_OK ; 1900 } 1901 1902q_start_min = block_start * Br ; 1903q_start_max = MIN (block_end * Br ,neq1 ); 1904 } 1905 1906// ======== VTCM allocation (GQA-aware) ======== 1907// K/V row sizes drive the DMA descriptors (not the VTCM layout) and are used 1908// throughout the KV loop below. 1909const size_t size_k_row = htp_tensor_get_row_size (k -> type ,DK ); 1910const size_t size_v_row = htp_tensor_get_row_size (v -> type ,DV ); 1911const size_t size_k_row_padded = hex_round_up (DK * sizeof (__fp16 ),128 ); 1912const size_t size_v_row_padded = hex_round_up (DV * sizeof (__fp16 ),128 ); 1913 1914// Build the VTCM layout once (shared with the host estimator) and place every 1915// scratch buffer at its computed offset. 1916struct hmx_fa_vtcm_layout L ; 1917hmx_fa_vtcm_layout_build (& L ,G ,DK ,DV ,Br ,Bc ,n_threads ,pipeline ,factx .is_q_fp32 ); 1918 1919if (L .total_bytes > ctx -> vtcm_size ) { 1920return HTP_STATUS_VTCM_TOO_SMALL ; 1921 } 1922 1923uint8_t * const base = ctx -> vtcm_base ; 1924 1925factx .vtcm_q_dma = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_q_dma ); 1926factx .vtcm_q_tiles = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_q_tiles ); 1927factx .vtcm_o_tiles [0 ]= VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_o_tiles [0 ]); 1928factx .vtcm_o_tiles [1 ]= VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_o_tiles [1 ]); 1929factx .vtcm_k_fp16 [0 ]= VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_k_fp16 [0 ]); 1930factx .vtcm_k_fp16 [1 ]= VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_k_fp16 [1 ]); 1931factx .vtcm_v_fp16 [0 ]= VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_v_fp16 [0 ]); 1932factx .vtcm_v_fp16 [1 ]= VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_v_fp16 [1 ]); 1933factx .vtcm_k_tiles [0 ]= VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_k_tiles [0 ]); 1934factx .vtcm_k_tiles [1 ]= VTCM_LAYOUT_PTR_OPTIONAL (__fp16 ,base ,L .off_k_tiles [1 ],pipeline ); 1935factx .vtcm_v_tiles [0 ]= VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_v_tiles [0 ]); 1936factx .vtcm_v_tiles [1 ]= VTCM_LAYOUT_PTR_OPTIONAL (__fp16 ,base ,L .off_v_tiles [1 ],pipeline ); 1937factx .vtcm_s_tiles [0 ]= VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_s_tiles [0 ]); 1938factx .vtcm_s_tiles [1 ]= VTCM_LAYOUT_PTR_OPTIONAL (__fp16 ,base ,L .off_s_tiles [1 ],pipeline ); 1939factx .vtcm_p_tiles [0 ]= VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_p_tiles [0 ]); 1940factx .vtcm_p_tiles [1 ]= VTCM_LAYOUT_PTR_OPTIONAL (__fp16 ,base ,L .off_p_tiles [1 ],pipeline ); 1941factx .vtcm_d_tiles [0 ]= VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_d_tiles [0 ]); 1942factx .vtcm_d_tiles [1 ]= VTCM_LAYOUT_PTR_OPTIONAL (__fp16 ,base ,L .off_d_tiles [1 ],pipeline ); 1943factx .vtcm_d_inv_l = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_d_inv_l ); 1944factx .vtcm_m_vec = VTCM_LAYOUT_PTR (HVX_Vector ,base ,L .off_m_vec ); 1945factx .vtcm_l_vec = VTCM_LAYOUT_PTR (HVX_Vector ,base ,L .off_l_vec ); 1946factx .vtcm_s_rowmax = VTCM_LAYOUT_PTR (HVX_Vector ,base ,L .off_s_rowmax ); 1947factx .vtcm_p_rowsum = VTCM_LAYOUT_PTR (HVX_Vector ,base ,L .off_p_rowsum ); 1948factx .vtcm_row_bufs = VTCM_LAYOUT_PTR (HVX_Vector ,base ,L .off_row_bufs ); 1949factx .row_buf_stride = L .row_buf_stride ; 1950factx .vtcm_hmx_scales_id = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_hmx_scales_id ); 1951factx .vtcm_hmx_scales_qk = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_hmx_scales_qk ); 1952factx .vtcm_mask_buf = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_mask_buf ); 1953factx .mask_buf_row_stride = L .mask_buf_row_stride ; 1954factx .q_tile_bytes = L .q_tile_bytes ; 1955factx .o_tile_bytes = L .o_tile_bytes ; 1956factx .col_vec_bytes = L .col_vec_bytes ; 1957factx .d_tile_bytes = L .d_tile_bytes ; 1958factx .vtcm_slopes = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_slopes ); 1959 1960const size_t m_line_bytes = L .m_line_bytes ;// used by the mask DMAs in the KV loop 1961 1962dma_cache_init (& factx .m_cache , (uint8_t * )factx .vtcm_mask_buf ,L .m_buf_slot_bytes ,HMX_FA_DMA_CACHE_SIZE ); 1963 1964// ======== Initialize HMX output scales ======== 1965hmx_init_column_scales (factx .vtcm_hmx_scales_id ,Q6_V_vsplat_R (0x3c00 ));// 1.0 1966hmx_init_column_scales (factx .vtcm_hmx_scales_qk ,hvx_vec_splat_f16 (factx .scale )); 1967 1968// ======== Skip compute if profiling ======== 1969if (octx -> flags & HTP_OPFLAGS_SKIP_COMPUTE ) { 1970return HTP_STATUS_OK ; 1971 } 1972 1973// ======== DMA setup ======== 1974dma_queue * const dma = ctx -> dma [0 ]; 1975 1976const size_t n_row_tiles_g_br = g_br /HMX_FP16_TILE_N_ROWS ; 1977const size_t n_tiles_per_bc = Bc /HMX_FP16_TILE_N_COLS ; 1978 1979const size_t qo_element_size = factx .is_q_fp32 ?sizeof (float ) :sizeof (__fp16 ); 1980 1981const bool q_transposed = q -> nb [1 ]< q -> nb [2 ]; 1982const size_t q_src_stride = q_transposed ?q -> nb [2 ] :q -> nb [1 ]; 1983const size_t q_row_bytes_untransposed = factx .G * factx .DK * qo_element_size ; 1984const size_t q_row_bytes_trans_factor = factx .DK * qo_element_size ; 1985const uint32_t kv_rows0 = hex_smin (Bc ,nek1 ); 1986 1987// ======== Reusable job descriptors for pipeline ======== 1988hmx_fa_qk_job_t qk_job ; 1989hmx_fa_o_update_job_t ou_job ; 1990hmx_fa_o_norm_job_t on_job ; 1991 1992// ======== Main loop ======== 1993for (uint32_t ib3 = 0 ;ib3 < neq3 ;++ ib3 ) { 1994const uint32_t im3 = mask ?fastmodulo (ib3 ,mask -> ne [3 ],& factx .src3_div3 ) :0 ; 1995for (uint32_t q_start = q_start_min ;q_start < q_start_max ;q_start += Br ) { 1996const uint32_t n_rows_q = hex_smin (Br ,neq1 - q_start ); 1997const size_t n_rows_g = n_rows_q * G ; 1998const size_t g_br_actual = hex_align_up (n_rows_g ,HMX_FP16_TILE_N_ROWS ); 1999const size_t n_row_tiles = g_br_actual /HMX_FP16_TILE_N_ROWS ; 2000 2001for (uint32_t kv_head = 0 ;kv_head < n_kv_heads ;++ kv_head ) { 2002const uint32_t ik2 = kv_head ; 2003const uint32_t ik3 = fastdiv (ib3 ,& kparams -> broadcast_rk3 ); 2004const uint32_t iv2 = kv_head ; 2005const uint32_t iv3 = fastdiv (ib3 ,& kparams -> broadcast_rv3 ); 2006 2007// 1. Push Q and KV DMAs for the very first iteration. 2008// Subsequent iterations are enqueued early at the end of the previous iteration. 2009if (ib3 == 0 && q_start == q_start_min && kv_head == 0 ) { 2010const uint8_t * q_ptr = (const uint8_t * )q -> data + q_start * q -> nb [1 ]+ 2011 (kv_head * factx .G )* q -> nb [2 ]+ ib3 * q -> nb [3 ]; 2012const size_t q_row_bytes = q_transposed ?n_rows_q * q_row_bytes_trans_factor :q_row_bytes_untransposed ; 2013const size_t n_rows = q_transposed ?factx .G :n_rows_q ; 2014dma_queue_push (dma ,dma_make_ptr (factx .vtcm_q_dma ,q_ptr ),q_row_bytes ,hex_smax (q_src_stride ,q_row_bytes ),q_row_bytes ,n_rows ); 2015 2016if (factx .n_kv_blocks > 0 ) { 2017const uint8_t * k_src = (const uint8_t * )k -> data + ik2 * k -> nb [2 ]+ ik3 * k -> nb [3 ]; 2018dma_queue_push (dma ,dma_make_ptr (factx .vtcm_k_fp16 [0 ],k_src ),size_k_row_padded ,k -> nb [1 ],size_k_row ,kv_rows0 ); 2019 2020const uint8_t * v_src = (const uint8_t * )v -> data + iv2 * v -> nb [2 ]+ iv3 * v -> nb [3 ]; 2021dma_queue_push (dma ,dma_make_ptr (factx .vtcm_v_fp16 [0 ],v_src ),size_v_row_padded ,v -> nb [1 ],size_v_row ,kv_rows0 ); 2022 2023if (factx .pipeline && mask ) { 2024if (__builtin_expect (factx .mask_broadcast , true)) { 2025const uint8_t * ms_src = (const uint8_t * )mask -> data + q_start * mask -> nb [1 ]+ im3 * mask -> nb [3 ]+ 0 ; 2026dma_cache_push (dma ,& factx .m_cache ,ms_src ,m_line_bytes ,mask -> nb [1 ],kv_rows0 * sizeof (__fp16 ),n_rows_q ); 2027 }else { 2028fa_push_mask_dma_gqa (dma ,mask ,q_start ,im3 ,0 ,kv_head ,G ,m_line_bytes ,kv_rows0 ,n_rows_q ,& factx ); 2029 } 2030 } 2031 } 2032 } 2033 2034// 2. Pop Q DMA (blocks until Q is loaded) 2035dma_queue_pop (dma ); 2036 2037// ---- Load Q block & Initialize per-block state ---- 2038fa_phase_q_load (& factx ,q ,q_start ,kv_head ,ib3 ,n_rows_g ); 2039 2040__fp16 * o_tile_prev = factx .vtcm_o_tiles [0 ]; 2041__fp16 * o_tile_curr = factx .vtcm_o_tiles [1 ]; 2042 2043// ---- KV block loop with DMA double-buffering ---- 2044size_t buf_idx = 0 ; 2045 2046htp_trace_event_start (tr_hvx ,HTP_TRACE_EVT_HVX_A_PREP , (uint16_t )q_start ); 2047fa_compute_slopes (& factx ,kv_head ,n_rows_g ); 2048htp_trace_event_stop (tr_hvx ,HTP_TRACE_EVT_HVX_A_PREP , (uint16_t )q_start ); 2049 2050const size_t k_src_stride = size_k_row_padded /sizeof (__fp16 ); 2051const size_t v_src_stride = size_v_row_padded /sizeof (__fp16 ); 2052 2053hmx_queue_t hmx_q = ctx -> hmx_queue ; 2054 2055if (factx .pipeline ) { 2056// Double-buffered job structs because HMX queue runs asynchronously 2057hmx_fa_qk_job_t qk_job [2 ]; 2058hmx_fa_o_update_job_t ou_job [2 ]; 2059 2060// Prefetch block 1 early if there are multiple blocks 2061if (factx .n_kv_blocks > 1 ) { 2062fa_prefetch_block (dma ,k ,v ,mask ,1 ,Bc ,size_k_row_padded ,size_k_row ,size_v_row_padded ,size_v_row , 2063ik2 ,ik3 ,iv2 ,iv3 ,q_start ,im3 ,kv_head ,G ,m_line_bytes ,n_rows_q ,nek1 ,1 ,& factx ); 2064 } 2065 2066// Prep and start QK-dot(0) 2067void * curr_k0 = dma_queue_pop (dma ).dst ; 2068fa_phase_k_interleave (& factx ,kv_rows0 ,k_src_stride ,curr_k0 ,0 ,0 ); 2069 2070qk_job [0 ].q_tiles = factx .vtcm_q_tiles ; 2071qk_job [0 ].k_tiles = factx .vtcm_k_tiles [0 ]; 2072qk_job [0 ].s_tiles = factx .vtcm_s_tiles [0 ]; 2073qk_job [0 ].n_row_tiles = n_row_tiles ; 2074qk_job [0 ].n_col_tiles = hmx_ceil_div (kv_rows0 ,HMX_FP16_TILE_N_COLS ); 2075qk_job [0 ].n_dot_tiles = DK /32 ; 2076qk_job [0 ].n_tiles_per_bc = n_tiles_per_bc ; 2077qk_job [0 ].hmx_scales = factx .vtcm_hmx_scales_qk ; 2078hmx_queue_push (hmx_q ,hmx_queue_make_desc (hmx_fa_qk_dot_worker ,& qk_job [0 ])); 2079 2080for (uint32_t kv_blk = 0 ;kv_blk < factx .n_kv_blocks ;++ kv_blk ) { 2081const uint32_t kv_start = kv_blk * Bc ; 2082const uint32_t kv_rows = hex_smin (Bc ,nek1 - kv_start ); 2083const size_t n_col_tiles = hmx_ceil_div (kv_rows ,HMX_FP16_TILE_N_COLS ); 2084 2085// ---- 1. Pop and run V-prep for current block ---- 2086void * curr_v = dma_queue_pop (dma ).dst ; 2087fa_phase_v_interleave (& factx ,kv_rows ,v_src_stride ,curr_v ,factx .vtcm_v_tiles [buf_idx ],n_tiles_per_bc ,kv_start ); 2088 2089// ---- 2. Pop and run mask-prep for current block ---- 2090__fp16 * current_mask_vtcm = NULL ; 2091if (mask ) { 2092if (__builtin_expect (factx .mask_broadcast , true)) { 2093current_mask_vtcm = (__fp16 * )dma_queue_pop (dma ).dst ; 2094 }else { 2095fa_pop_mask_dma_gqa (dma ,G ); 2096current_mask_vtcm = factx .vtcm_mask_buf ; 2097 } 2098 } 2099 2100// ---- 3. Start HMX O update for block kv_blk - 1 (reads P[1 - buf_idx], V[1 - buf_idx], D) ---- 2101// O update relys on the previous block's P and V tiles. 2102// O update MUST be pushed before the next block's QK-dot: hmx_queue_pop() retires the 2103// oldest descriptor, so push order alone decides which pop waits for which job. 2104// If OU went in after QK(i+1), the pop below would retire QK(i+1) and leave 2105// OU(i-1) in flight into the next iteration, where V-prep overwrites V[prev_buf]. 2106if (kv_blk > 0 ) { 2107const size_t prev_buf = 1 - buf_idx ; 2108ou_job [prev_buf ].o_curr = o_tile_curr ; 2109ou_job [prev_buf ].o_prev = o_tile_prev ; 2110ou_job [prev_buf ].p_tiles = factx .vtcm_p_tiles [prev_buf ]; 2111ou_job [prev_buf ].v_tiles = factx .vtcm_v_tiles [prev_buf ]; 2112ou_job [prev_buf ].d_tiles = factx .vtcm_d_tiles [prev_buf ]; 2113ou_job [prev_buf ].hmx_scales = factx .vtcm_hmx_scales_id ; 2114ou_job [prev_buf ].n_row_tiles = n_row_tiles ; 2115ou_job [prev_buf ].n_col_tiles = 2116hmx_ceil_div (hex_smin (Bc ,nek1 - (kv_blk - 1 )* Bc ),HMX_FP16_TILE_N_COLS ); 2117ou_job [prev_buf ].n_row_tiles_g_br = n_row_tiles_g_br ; 2118ou_job [prev_buf ].n_tiles_per_bc = n_tiles_per_bc ; 2119ou_job [prev_buf ].DV = DV ; 2120hmx_queue_push (hmx_q ,hmx_queue_make_desc (hmx_fa_o_update_worker ,& ou_job [prev_buf ])); 2121 } 2122 2123// ---- 4. Pop and run K-prep for next block & push next QK-dot ---- 2124if (kv_blk + 1 < factx .n_kv_blocks ) { 2125const uint32_t next_start = (kv_blk + 1 )* Bc ; 2126const uint32_t next_rows = hex_smin (Bc ,nek1 - next_start ); 2127const size_t next_buf = 1 - buf_idx ; 2128 2129void * next_k = dma_queue_pop (dma ).dst ; 2130fa_phase_k_interleave (& factx ,next_rows ,k_src_stride ,next_k ,next_start ,next_buf ); 2131 2132qk_job [next_buf ].q_tiles = factx .vtcm_q_tiles ; 2133qk_job [next_buf ].k_tiles = factx .vtcm_k_tiles [next_buf ]; 2134qk_job [next_buf ].s_tiles = factx .vtcm_s_tiles [next_buf ]; 2135qk_job [next_buf ].n_row_tiles = n_row_tiles ; 2136qk_job [next_buf ].n_col_tiles = hmx_ceil_div (next_rows ,HMX_FP16_TILE_N_COLS ); 2137qk_job [next_buf ].n_dot_tiles = DK /32 ; 2138qk_job [next_buf ].n_tiles_per_bc = n_tiles_per_bc ; 2139qk_job [next_buf ].hmx_scales = factx .vtcm_hmx_scales_qk ; 2140hmx_queue_push (hmx_q ,hmx_queue_make_desc (hmx_fa_qk_dot_worker ,& qk_job [next_buf ])); 2141 } 2142 2143// ---- 5. Wait for current block's QK-dot to finish ---- 2144hmx_queue_pop (hmx_q ); 2145 2146// ---- 6. Phase 2: softmax + build_D ---- 2147fa_softmax_args_t sargs ; 2148memset (& sargs ,0 ,sizeof (sargs )); 2149sargs .factx = & factx ; 2150sargs .buf_idx = buf_idx ; 2151sargs .kv_rows = kv_rows ; 2152sargs .n_rows_g = n_rows_g ; 2153sargs .n_col_tiles = n_col_tiles ; 2154sargs .n_tiles_per_bc = n_tiles_per_bc ; 2155sargs .n_row_tiles = n_row_tiles ; 2156sargs .n_row_tiles_g_br = n_row_tiles_g_br ; 2157sargs .Bc = Bc ; 2158sargs .G = G ; 2159sargs .kv_head = kv_head ; 2160sargs .kv_start = kv_start ; 2161sargs .q_start = q_start ; 2162sargs .ib3 = ib3 ; 2163sargs .has_alibi = (factx .max_bias != 0.0f ); 2164sargs .mask = mask ; 2165sargs .mask_vtcm = current_mask_vtcm ; 2166sargs .mask_vtcm_row_stride = factx .mask_buf_row_stride ; 2167sargs .slopes = factx .vtcm_slopes ; 2168 2169// Run Softmax on HVX (blocking call) 2170fa_phase_softmax_and_build_d (& factx ,& sargs ,n_row_tiles ,n_row_tiles_g_br ); 2171 2172// Wait for HMX O update for block kv_blk - 1 to finish 2173if (kv_blk > 0 ) { 2174hmx_queue_pop (hmx_q ); 2175hex_swap_ptr ((void ** )& o_tile_curr , (void ** )& o_tile_prev ); 2176 } 2177 2178// Prefetch block kv_blk + 2 2179if (kv_blk + 2 < factx .n_kv_blocks ) { 2180fa_prefetch_block (dma ,k ,v ,mask ,kv_blk + 2 ,Bc ,size_k_row_padded ,size_k_row ,size_v_row_padded ,size_v_row , 2181ik2 ,ik3 ,iv2 ,iv3 ,q_start ,im3 ,kv_head ,G ,m_line_bytes ,n_rows_q ,nek1 ,buf_idx ,& factx ); 2182 } 2183 2184buf_idx = 1 - buf_idx ; 2185 } 2186 2187// Epilogue 2188if (factx .n_kv_blocks > 0 ) { 2189const uint32_t last_blk = factx .n_kv_blocks - 1 ; 2190const size_t last_cols = hmx_ceil_div (hex_smin (Bc ,nek1 - last_blk * Bc ),HMX_FP16_TILE_N_COLS ); 2191ou_job [0 ].o_curr = o_tile_curr ; 2192ou_job [0 ].o_prev = o_tile_prev ; 2193ou_job [0 ].p_tiles = factx .vtcm_p_tiles [1 - buf_idx ]; 2194ou_job [0 ].v_tiles = factx .vtcm_v_tiles [1 - buf_idx ]; 2195ou_job [0 ].d_tiles = factx .vtcm_d_tiles [1 - buf_idx ]; 2196ou_job [0 ].hmx_scales = factx .vtcm_hmx_scales_id ; 2197ou_job [0 ].n_row_tiles = n_row_tiles ; 2198ou_job [0 ].n_col_tiles = last_cols ; 2199ou_job [0 ].n_row_tiles_g_br = n_row_tiles_g_br ; 2200ou_job [0 ].n_tiles_per_bc = n_tiles_per_bc ; 2201ou_job [0 ].DV = DV ; 2202hmx_queue_push (hmx_q ,hmx_queue_make_desc (hmx_fa_o_update_worker ,& ou_job [0 ])); 2203 2204// Overlapped: run HVX build diag inv L while HMX is busy executing the update 2205htp_trace_event_start (tr_hvx ,HTP_TRACE_EVT_HVX_O_PROC , (uint16_t )q_start ); 2206fa_build_d_diag_inv_l (& factx ,n_row_tiles ,n_row_tiles_g_br ); 2207htp_trace_event_stop (tr_hvx ,HTP_TRACE_EVT_HVX_O_PROC , (uint16_t )q_start ); 2208hmx_queue_pop (hmx_q ); 2209 2210hex_swap_ptr ((void ** )& o_tile_curr , (void ** )& o_tile_prev ); 2211 } 2212 2213 }else { 2214// Fallback path 2215for (uint32_t kv_blk = 0 ;kv_blk < factx .n_kv_blocks ;++ kv_blk ) { 2216const uint32_t kv_start = kv_blk * Bc ; 2217const uint32_t kv_rows = hex_smin (Bc ,nek1 - kv_start ); 2218const size_t n_col_tiles = hmx_ceil_div (kv_rows ,HMX_FP16_TILE_N_COLS ); 2219 2220if (mask ) { 2221if (__builtin_expect (factx .mask_broadcast , true)) { 2222const uint8_t * ms_src = (const uint8_t * )mask -> data + q_start * mask -> nb [1 ]+ im3 * mask -> nb [3 ]+ kv_start * sizeof (__fp16 ); 2223dma_cache_push (dma ,& factx .m_cache ,ms_src ,m_line_bytes ,mask -> nb [1 ],kv_rows * sizeof (__fp16 ),n_rows_q ); 2224 }else { 2225fa_push_mask_dma_gqa (dma ,mask ,q_start ,im3 ,kv_start ,kv_head ,G ,m_line_bytes ,kv_rows ,n_rows_q ,& factx ); 2226 } 2227 } 2228 2229if (kv_blk + 1 < factx .n_kv_blocks ) { 2230const uint32_t prefetch_start = (kv_blk + 1 )* Bc ; 2231const uint32_t prefetch_rows = hex_smin (Bc ,nek1 - prefetch_start ); 2232const size_t prefetch_buf = 1 - buf_idx ; 2233const uint8_t * k_prefetch_src = (const uint8_t * )k -> data + prefetch_start * k -> nb [1 ]+ ik2 * k -> nb [2 ]+ ik3 * k -> nb [3 ]; 2234dma_queue_push (dma ,dma_make_ptr (factx .vtcm_k_fp16 [prefetch_buf ],k_prefetch_src ),size_k_row_padded ,k -> nb [1 ],size_k_row ,prefetch_rows ); 2235const uint8_t * v_prefetch_src = (const uint8_t * )v -> data + prefetch_start * v -> nb [1 ]+ iv2 * v -> nb [2 ]+ iv3 * v -> nb [3 ]; 2236dma_queue_push (dma ,dma_make_ptr (factx .vtcm_v_fp16 [prefetch_buf ],v_prefetch_src ),size_v_row_padded ,v -> nb [1 ],size_v_row ,prefetch_rows ); 2237 } 2238 2239// Wait for current K DMA and interleave 2240void * curr_k = dma_queue_pop (dma ).dst ; 2241fa_phase_k_interleave (& factx ,kv_rows ,k_src_stride ,curr_k ,kv_start ,0 ); 2242 2243 { 2244qk_job .q_tiles = factx .vtcm_q_tiles ; 2245qk_job .k_tiles = factx .vtcm_k_tiles [0 ]; 2246qk_job .s_tiles = factx .vtcm_s_tiles [0 ]; 2247qk_job .n_row_tiles = n_row_tiles ; 2248qk_job .n_col_tiles = n_col_tiles ; 2249qk_job .n_dot_tiles = (size_t ) (DK /32 ); 2250qk_job .n_tiles_per_bc = n_tiles_per_bc ; 2251qk_job .hmx_scales = factx .vtcm_hmx_scales_qk ; 2252 2253hmx_queue_push (ctx -> hmx_queue ,hmx_queue_make_desc (hmx_fa_qk_dot_worker ,& qk_job )); 2254hmx_queue_pop (ctx -> hmx_queue ); 2255 } 2256 2257// Wait for current V DMA and interleave 2258void * curr_v = dma_queue_pop (dma ).dst ; 2259fa_phase_v_interleave (& factx ,kv_rows ,v_src_stride ,curr_v ,factx .vtcm_v_tiles [0 ],n_tiles_per_bc ,kv_start ); 2260 2261// ---- Phase 3: softmax + build_D ---- 2262__fp16 * current_mask_vtcm = NULL ; 2263if (mask ) { 2264if (__builtin_expect (factx .mask_broadcast , true)) { 2265current_mask_vtcm = (__fp16 * )dma_queue_pop (dma ).dst ; 2266 }else { 2267fa_pop_mask_dma_gqa (dma ,G ); 2268current_mask_vtcm = factx .vtcm_mask_buf ; 2269 } 2270 } 2271 2272fa_softmax_args_t sargs ; 2273memset (& sargs ,0 ,sizeof (sargs )); 2274sargs .factx = & factx ; 2275sargs .kv_rows = kv_rows ; 2276sargs .n_rows_g = n_rows_g ; 2277sargs .n_col_tiles = n_col_tiles ; 2278sargs .n_tiles_per_bc = n_tiles_per_bc ; 2279sargs .n_row_tiles = n_row_tiles ; 2280sargs .n_row_tiles_g_br = n_row_tiles_g_br ; 2281sargs .Bc = Bc ; 2282sargs .G = G ; 2283sargs .kv_head = kv_head ; 2284sargs .kv_start = kv_start ; 2285sargs .q_start = q_start ; 2286sargs .ib3 = ib3 ; 2287sargs .has_alibi = (factx .max_bias != 0.0f ); 2288sargs .mask = mask ; 2289sargs .mask_vtcm = current_mask_vtcm ; 2290sargs .mask_vtcm_row_stride = factx .mask_buf_row_stride ; 2291sargs .slopes = factx .vtcm_slopes ; 2292fa_phase_softmax_and_build_d (& factx ,& sargs ,n_row_tiles ,n_row_tiles_g_br ); 2293 2294 { 2295ou_job .o_curr = o_tile_curr ; 2296ou_job .o_prev = o_tile_prev ; 2297ou_job .p_tiles = factx .vtcm_p_tiles [0 ]; 2298ou_job .v_tiles = factx .vtcm_v_tiles [0 ]; 2299ou_job .d_tiles = factx .vtcm_d_tiles [0 ]; 2300ou_job .hmx_scales = factx .vtcm_hmx_scales_id ; 2301ou_job .n_row_tiles = n_row_tiles ; 2302ou_job .n_col_tiles = n_col_tiles ; 2303ou_job .n_row_tiles_g_br = n_row_tiles_g_br ; 2304ou_job .n_tiles_per_bc = n_tiles_per_bc ; 2305ou_job .DV = DV ; 2306 2307hmx_queue_push (ctx -> hmx_queue ,hmx_queue_make_desc (hmx_fa_o_update_worker ,& ou_job )); 2308if (kv_blk + 1 == factx .n_kv_blocks ) { 2309// Overlapped: run HVX build diag inv L while HMX is busy executing the update 2310htp_trace_event_start (tr_hvx ,HTP_TRACE_EVT_HVX_O_PROC , (uint16_t )q_start ); 2311fa_build_d_diag_inv_l (& factx ,n_row_tiles ,n_row_tiles_g_br ); 2312htp_trace_event_stop (tr_hvx ,HTP_TRACE_EVT_HVX_O_PROC , (uint16_t )q_start ); 2313 } 2314hmx_queue_pop (ctx -> hmx_queue ); 2315 2316hex_swap_ptr ((void ** )& o_tile_curr , (void ** )& o_tile_prev ); 2317 } 2318 2319buf_idx = 1 - buf_idx ; 2320 } 2321 } 2322 2323// Enqueue DMAs for the next iteration early so they overlap with O-PROC 2324uint32_t next_kv_head = kv_head + 1 ; 2325uint32_t next_q_start = q_start ; 2326uint32_t next_ib3 = ib3 ; 2327if (next_kv_head >=n_kv_heads ) { 2328next_kv_head = 0 ; 2329next_q_start = q_start + Br ; 2330if (next_q_start >=q_start_max ) { 2331next_q_start = q_start_min ; 2332next_ib3 = ib3 + 1 ; 2333 } 2334 } 2335bool has_next = (next_ib3 < neq3 ); 2336 2337if (has_next ) { 2338const uint32_t next_n_rows_q = hex_smin (Br ,neq1 - next_q_start ); 2339const uint8_t * next_q_ptr = (const uint8_t * )q -> data + next_q_start * q -> nb [1 ]+ (next_kv_head * factx .G )* q -> nb [2 ]+ next_ib3 * q -> nb [3 ]; 2340const size_t next_q_row_bytes = q_transposed ?next_n_rows_q * q_row_bytes_trans_factor :q_row_bytes_untransposed ; 2341const size_t next_n_rows = q_transposed ?factx .G :next_n_rows_q ; 2342dma_queue_push (dma ,dma_make_ptr (factx .vtcm_q_dma ,next_q_ptr ),next_q_row_bytes ,hex_smax (q_src_stride ,next_q_row_bytes ),next_q_row_bytes ,next_n_rows ); 2343 2344if (factx .n_kv_blocks > 0 ) { 2345const uint32_t next_ik2 = next_kv_head ; 2346const uint32_t next_iv2 = next_kv_head ; 2347uint32_t next_ik3 = ik3 ; 2348uint32_t next_iv3 = iv3 ; 2349if (next_ib3 != ib3 ) { 2350next_ik3 = fastdiv (next_ib3 ,& kparams -> broadcast_rk3 ); 2351next_iv3 = fastdiv (next_ib3 ,& kparams -> broadcast_rv3 ); 2352 } 2353 2354const uint8_t * next_k_src = (const uint8_t * )k -> data + next_ik2 * k -> nb [2 ]+ next_ik3 * k -> nb [3 ]; 2355dma_queue_push (dma ,dma_make_ptr (factx .vtcm_k_fp16 [0 ],next_k_src ),size_k_row_padded ,k -> nb [1 ],size_k_row ,kv_rows0 ); 2356 2357const uint8_t * next_v_src = (const uint8_t * )v -> data + next_iv2 * v -> nb [2 ]+ next_iv3 * v -> nb [3 ]; 2358dma_queue_push (dma ,dma_make_ptr (factx .vtcm_v_fp16 [0 ],next_v_src ),size_v_row_padded ,v -> nb [1 ],size_v_row ,kv_rows0 ); 2359 2360if (factx .pipeline && mask ) { 2361uint32_t next_im3 = im3 ; 2362if (next_ib3 != ib3 ) { 2363next_im3 = fastmodulo (next_ib3 ,mask -> ne [3 ],& factx .src3_div3 ); 2364 } 2365if (__builtin_expect (factx .mask_broadcast , true)) { 2366const uint8_t * ms_src = (const uint8_t * )mask -> data + next_q_start * mask -> nb [1 ]+ next_im3 * mask -> nb [3 ]+ 0 ; 2367dma_cache_push (dma ,& factx .m_cache ,ms_src ,m_line_bytes ,mask -> nb [1 ],kv_rows0 * sizeof (__fp16 ),next_n_rows_q ); 2368 }else { 2369fa_push_mask_dma_gqa (dma ,mask ,next_q_start ,next_im3 ,0 ,next_kv_head ,G ,m_line_bytes ,kv_rows0 ,next_n_rows_q ,& factx ); 2370 } 2371 } 2372 } 2373 } 2374 2375// ---- Final normalization ---- 2376 { 2377on_job .o_curr = o_tile_curr ; 2378on_job .o_prev = o_tile_prev ; 2379on_job .d_tiles = factx .vtcm_d_inv_l ; 2380on_job .hmx_scales = factx .vtcm_hmx_scales_id ; 2381on_job .n_row_tiles = n_row_tiles ; 2382on_job .n_row_tiles_g_br = n_row_tiles_g_br ; 2383on_job .DV = DV ; 2384hmx_queue_push (ctx -> hmx_queue ,hmx_queue_make_desc (hmx_fa_o_norm_worker ,& on_job )); 2385hmx_queue_pop (ctx -> hmx_queue ); 2386 } 2387 2388// ---- Store O block ---- 2389fa_phase_o_store (& factx ,dst ,o_tile_curr ,q_start ,kv_head ,ib3 ,n_rows_g ); 2390 } 2391 } 2392 } 2393 2394return HTP_STATUS_OK ; 2395} 2396 2397int op_flash_attn_ext (struct htp_ops_context * octx ) { 2398const struct htp_tensor * q = octx -> src [0 ]; 2399const struct htp_tensor * k = octx -> src [1 ]; 2400const struct htp_tensor * v = octx -> src [2 ]; 2401const struct htp_tensor * mask = octx -> src [3 ]; 2402const struct htp_tensor * dst = octx -> dst ; 2403 2404// Check support 2405if ((q -> type != HTP_TYPE_F16 && q -> type != HTP_TYPE_F32 )|| 2406 (k -> type != HTP_TYPE_F16 && k -> type != HTP_TYPE_Q8_0 )|| 2407 (v -> type != HTP_TYPE_F16 && v -> type != HTP_TYPE_Q8_0 )) { 2408return HTP_STATUS_NO_SUPPORT ; 2409 } 2410 2411const struct htp_fa_kernel_params * kparams = (const struct htp_fa_kernel_params * )octx -> kernel_params ; 2412 2413if (kparams -> kernel_type == HTP_FA_KERNEL_UNSUPPORTED ) { 2414return HTP_STATUS_NO_SUPPORT ; 2415 } 2416 2417if (!htp_ops_context_set_n_threads (octx ,kparams -> n_threads )) { 2418return HTP_STATUS_INVAL_PARAMS ; 2419 } 2420 2421if (kparams -> kernel_type == HTP_FA_KERNEL_HMX ) { 2422return hmx_flash_attn_ext (octx ); 2423 } 2424 2425struct htp_fa_context factx ; 2426factx .octx = octx ; 2427factx .k = k ; 2428factx .v = v ; 2429 2430factx .src0_div21 = kparams -> u .hvx .src0_div21 ; 2431factx .src0_div1 = kparams -> u .hvx .src0_div1 ; 2432 2433factx .broadcast_rk2 = kparams -> broadcast_rk2 ; 2434factx .broadcast_rk3 = kparams -> broadcast_rk3 ; 2435factx .broadcast_rv2 = kparams -> broadcast_rv2 ; 2436factx .broadcast_rv3 = kparams -> broadcast_rv3 ; 2437 2438if (mask ) { 2439factx .src3_div2 = kparams -> src3_div2 ; 2440factx .src3_div3 = kparams -> src3_div3 ; 2441 } 2442 2443factx .is_q_fp32 = (kparams -> is_q_fp32 != 0 ); 2444factx .size_q_row_padded = kparams -> u .hvx .size_q_row_padded ; 2445factx .size_k_row_padded = kparams -> u .hvx .size_k_row_padded ; 2446factx .size_v_row_padded = kparams -> u .hvx .size_v_row_padded ; 2447 2448size_t size_q_block = factx .size_q_row_padded * 1 ;// single row for now 2449factx .size_k_block = factx .size_k_row_padded * FLASH_ATTN_BLOCK_SIZE ; 2450factx .size_v_block = factx .size_v_row_padded * FLASH_ATTN_BLOCK_SIZE ; 2451factx .size_m_block = hex_round_up (FLASH_ATTN_BLOCK_SIZE * sizeof (__fp16 ),128 ); 2452 2453factx .n_blocks = kparams -> n_kv_blocks ; 2454 2455factx .scale = kparams -> scale ; 2456factx .max_bias = kparams -> max_bias ; 2457factx .logit_softcap = (__fp16 )kparams -> logit_softcap ; 2458 2459factx .n_head_log2 = kparams -> n_head_log2 ; 2460factx .m0 = kparams -> m0 ; 2461factx .m1 = kparams -> m1 ; 2462 2463const uint32_t n_head = q -> ne [2 ]; 2464if (n_head > 512 ) { 2465return HTP_STATUS_NO_SUPPORT ; 2466 } 2467for (uint32_t h = 0 ;h < n_head ;++ h ) { 2468factx .slopes [h ]= (__fp16 ) ((kparams -> max_bias > 0.0f ) ?alibi_slope (h ,factx .n_head_log2 ,factx .m0 ,factx .m1 ) :1.0f ); 2469 } 2470 2471// total rows in q 2472const uint32_t neq1 = q -> ne [1 ]; 2473const uint32_t neq2 = q -> ne [2 ]; 2474const uint32_t neq3 = q -> ne [3 ]; 2475const uint32_t total_qrows = neq1 * neq2 * neq3 ; 2476 2477uint32_t qrow_start = 0 ; 2478uint32_t qrows = total_qrows ; 2479 2480if (octx -> ctx -> mdev .count > 1 ) { 2481const bool can_split = htp_tensor_mdev_data_aligned (dst )&& ((dst -> nb [1 ]& (HTP_TENSOR_MDEV_LINE_SIZE - 1 ))== 0 ); 2482const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition (total_qrows ,can_split ?1 :0 ,octx -> ctx -> mdev .idx ,octx -> ctx -> mdev .count ,& octx -> ctx -> mdev .count_div ); 2483qrow_start = range .start ; 2484qrows = range .count ; 2485 } 2486 2487if (qrows == 0 ) { 2488return HTP_STATUS_OK ; 2489 } 2490 2491const uint32_t n_threads = octx -> n_threads ; 2492 2493factx .qrows = qrows ; 2494factx .qrow_start = qrow_start ; 2495factx .qrows_per_thread = fastdiv (qrows + n_threads - 1 ,& octx -> n_threads_div ); 2496 2497size_t size_vkq_acc = hex_round_up (v -> ne [0 ]* sizeof (float ),128 );// VKQ32 2498 2499factx .size_q_block = size_q_block ; 2500factx .size_vkq_acc = size_vkq_acc ; 2501 2502uint8_t * vtcm_cur = octx -> ctx -> vtcm_base ; 2503 2504factx .spad_q = vtcm_seq_alloc (& vtcm_cur ,size_q_block * n_threads ); 2505factx .spad_k = vtcm_seq_alloc (& vtcm_cur ,factx .size_k_block * 2 * n_threads ); 2506factx .spad_v = vtcm_seq_alloc (& vtcm_cur ,factx .size_v_block * 2 * n_threads ); 2507factx .spad_m = vtcm_seq_alloc (& vtcm_cur , (mask ?factx .size_m_block * HVX_FA_DMA_CACHE_SIZE :0 )* n_threads ); 2508factx .spad_a = vtcm_seq_alloc (& vtcm_cur ,size_vkq_acc * n_threads ); 2509 2510if ((size_t ) (vtcm_cur - octx -> ctx -> vtcm_base )> octx -> ctx -> vtcm_size ) { 2511return HTP_STATUS_VTCM_TOO_SMALL ; 2512 } 2513 2514if (!(octx -> flags & HTP_OPFLAGS_SKIP_COMPUTE )) { 2515work_queue_run (octx -> ctx -> work_queue ,flash_attn_ext_f16_thread ,& factx ,n_threads ); 2516 } 2517 2518return HTP_STATUS_OK ; 2519}