char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
eafe15a5e
char/aliceai-t5-moe
1#include <assert.h> 2#include <inttypes.h> 3#include <stdio.h> 4#include <stdlib.h> 5#include <string.h> 6#include <time.h> 7 8#include <atomic> 9#include <memory> 10#include <chrono> 11#include <mutex> 12#include <thread> 13#include <cstddef> 14#include <stdexcept> 15#include <string> 16#include <sstream> 17#include <iomanip> 18#include <unordered_set> 19#include <unordered_map> 20#include <regex> 21#include <queue> 22#include <deque> 23#include <algorithm> 24 25#ifdef _WIN32 26# define WIN32_LEAN_AND_MEAN 27# ifndef NOMINMAX 28# define NOMINMAX 29# endif 30# include <windows.h> 31# include <sal.h> 32#else 33# include <semaphore.h> 34# include <unistd.h> 35#endif 36 37#pragma clang diagnostic ignored "-Wnested-anon-types" 38#pragma clang diagnostic ignored "-Wlanguage-extension-token" 39#pragma clang diagnostic ignored "-Wgnu-anonymous-struct" 40#pragma clang diagnostic ignored "-Wmicrosoft-enum-value" 41 42#include <AEEStdErr.h> 43#include <dspqueue.h> 44#include <rpcmem.h> 45 46#define GGML_COMMON_IMPL_CPP 47#include "ggml-backend-impl.h" 48#include "ggml-common.h" 49#include "ggml-hexagon.h" 50#include "ggml-impl.h" 51#include "ggml-quants.h" 52#include "htp-opnode.h" 53#include "htp-ops.h" 54#include "htp/matmul-ops.h" 55#include "htp/flash-attn-ops.h" 56#include "htp/unary-ops.h" 57#include "htp/get-rows-ops.h" 58#include "htp/set-rows-ops.h" 59#include "htp/rope-ops.h" 60#include "htp_iface.h" 61#include "htp-drv.h" 62 63using intvec = std::vector < int > ; 64using uintvec = std::vector < unsigned int > ; 65using u32vec = std::vector < uint32_t > ; 66 67#define GGML_HEXAGON_MAX_SESSIONS 16 68 69#define GGML_HEXAGON_FENCE_SLOT_SIZE 128 70 71struct ggml_hexagon_device_config { 72int physical_idx = 0 ; 73int virtual_idx = 0 ; 74int domain_id = 0 ; 75 std::string domain_name ; 76 std::string name ; 77 78 std::vector < ggml_hexagon_device_config > mdev_group ; 79}; 80 81static ggml_hexagon_device_config opt_device_configs [GGML_HEXAGON_MAX_SESSIONS ]; 82 83static int opt_arch = 0 ;// autodetect 84static size_t opt_ndev = 1 ; 85static size_t opt_nhvx = 0 ;// use all 86static int opt_nhmx = 1 ;// when set, enable HMX; when 0, use HVX only 87static size_t opt_vmem = HTP_OP_MAX_VMEM_DEFAULT ;// max available va space for buffer mappings 88static size_t opt_mbuf = 1ul * 1024 * 1024 * 1024 ;// max buffer size 89static int opt_etm = 0 ; 90static int opt_verbose = 0 ; 91static int opt_profile = 0 ;// profiling mode (0-disabled, 1-basic, 2-pmu) 92static bool opt_hostbuf = false; 93 94static int opt_mm_select = 3 ;// 3 = HMX -> Tiled -> Flat -> CPU, 2 = Tiled -> Flat -> CPU, 1 = Flat -> CPU 95static int opt_fa_select = 2 ;// 2 = HMX -> HVX -> CPU, 1 = HVX -> CPU, 0 = CPU (unsupported) 96static int opt_ar_select = 2 ;// 2 = fused ALLREDUCE+ADD (DMA, default), 1 = unfused ALLREDUCE (DMA), 0 = fallback to CPY+FENCE 97 98// Default PMU events, if profiling with PMU (mode=2) is enabled 99// See https://docs.qualcomm.com/doc/80-N2040-60/topic/pmu-events.html 100// https://docs.qualcomm.com/doc/80-N2040-61/topic/hvx-pmu-events.html 101static u32vec opt_pmu_evt {0x3 ,0x111 ,0x100 ,0x105 ,0x240 ,0x256 ,0x7D ,0x8C }; 102 103static int opt_opbatch = 1280 ;// max number of ops in a batch 104static int opt_opqueue = 32 ;// max number of pending batches 105static int opt_optrace = 0 ;// trace buffer size per thread (0 means default) 106static int opt_oppoll = 0 ;// polling for batch completions 107static int opt_opfusion = 1 ;// enable/disable op fusion 108 109enum ggml_hexagon_fusion_flags { 110GGML_HEXAGON_FUSE_ALLREDUCE_ADD = (1 <<1 ),// 2 111GGML_HEXAGON_FUSE_RMS_NORM_MUL = (1 <<2 ),// 4 112GGML_HEXAGON_FUSE_MUL_MAT_ADD = (1 <<3 ),// 8 113GGML_HEXAGON_FUSE_MUL_MAT_NX = (1 <<4 ),// 16 114GGML_HEXAGON_FUSE_MUL_MAT_ID_NX = (1 <<5 ),// 32 115}; 116 117static inline bool ggml_hexagon_is_fusion_enabled (int flag ) { 118if (opt_opfusion <=0 )return false; 119if (opt_opfusion == 1 )return true;// 1 enables all 120return (opt_opfusion & flag )!= 0 ; 121} 122 123static std::regex * opt_opfilter = NULL ;// regex of ops to not claim 124 125#define HEX_VERBOSE (...) \ 126 if (opt_verbose) GGML_LOG_DEBUG(__VA_ARGS__) 127 128static const char * status_to_str (uint32_t status ) { 129switch (status ) { 130case HTP_STATUS_OK : 131return "OK" ; 132case HTP_STATUS_NO_SUPPORT : 133return "NO-SUPPORT" ; 134case HTP_STATUS_INVAL_PARAMS : 135return "INVAL-PARAMS" ; 136case HTP_STATUS_VTCM_TOO_SMALL : 137return "VTCM-TOO-SMALL" ; 138case HTP_STATUS_INTERNAL_ERR : 139return "INTERNAL-ERROR" ; 140default : 141return "UNKNOWN" ; 142 } 143} 144 145// ** debug helpers 146 147static void ggml_hexagon_dump_op_exec (const std::string & sess_name ,const htp_opnode & node ,const uint32_t req_flags ) { 148if (!opt_verbose )return ; 149 150htp_opformat fmt (node ); 151GGML_LOG_DEBUG ("ggml-hex: %s execute-op %s|%s|%s|%s|%s|%s|%s|flags 0x%x\n" ,sess_name .c_str (), 152node .op_name ().c_str (),fmt .names ,fmt .dims ,fmt .types ,fmt .strides ,fmt .buffs ,fmt .kparams ,req_flags ); 153} 154 155static void ggml_hexagon_dump_op_supp (const std::string & sess_name ,const struct ggml_tensor * op ,bool supp ) { 156if (!opt_verbose )return ; 157 158htp_opformat fmt (htp_opformat (htp_opnode (HTP_OP_INVALID ,const_cast < ggml_tensor *> (op )))); 159GGML_LOG_DEBUG ("ggml-hex: %s supports-op %s|%s|%s|%s|%s|%s|%s\n" ,sess_name .c_str (), 160ggml_op_desc (op ),fmt .names ,fmt .dims ,fmt .types ,fmt .strides ,fmt .buffs ,supp ?"yes" :"no" ); 161} 162 163static const char * htp_event_name (uint16_t id ) { 164switch (id ) { 165case HTP_TRACE_EVT_DMA :return "DMA" ; 166case HTP_TRACE_EVT_HVX_COMP :return "HVX_COMP" ; 167case HTP_TRACE_EVT_HVX_A_QUANT :return "HVX_A_QUANT" ; 168case HTP_TRACE_EVT_HVX_A_PREP :return "HVX_A_PREP" ; 169case HTP_TRACE_EVT_HVX_W_DEQUANT :return "HVX_W_DEQUANT" ; 170case HTP_TRACE_EVT_HVX_W_PREP :return "HVX_W_PREP" ; 171case HTP_TRACE_EVT_HVX_O_PROC :return "HVX_O_PROC" ; 172case HTP_TRACE_EVT_HVX_FA_QK :return "HVX_QK_FA" ; 173case HTP_TRACE_EVT_HVX_FA_SFM :return "HVX_SFM_FA" ; 174case HTP_TRACE_EVT_HVX_FA_Q_PREP :return "HVX_Q_PREP" ; 175case HTP_TRACE_EVT_HVX_FA_K_PREP :return "HVX_K_PREP" ; 176case HTP_TRACE_EVT_HVX_FA_V_PREP :return "HVX_V_PREP" ; 177case HTP_TRACE_EVT_HMX_COMP :return "HMX_COMP" ; 178case HTP_TRACE_EVT_L2FLUSH :return "L2FLUSH" ; 179case HTP_TRACE_EVT_INIT :return "INIT" ; 180case HTP_TRACE_EVT_BUFF :return "BUFF" ; 181case HTP_TRACE_EVT_FENCE :return "FENCE" ; 182default :return "UNKNOWN" ; 183 } 184} 185 186static void ggml_hexagon_dump_op_prof (const std::string & sess_name ,const htp_opnode & node ,const htp_prof_desc & pd ) { 187if (!opt_profile )return ; 188 189uint32_t op_usec = pd .usecs ; 190uint32_t op_cycles = pd .cycles_stop - pd .cycles_start ; 191const uint32_t * pmu = pd .pmu ; 192 193char pmu_str [256 ]= "" ; 194if (opt_profile == 2 ) { 195 static_assert(HTP_PROF_PMU_NCNT == 8 ,"current implementation assumes 8 PMU counters" ); 196snprintf (pmu_str ,sizeof (pmu_str )," pmu [%u,%u,%u,%u,%u,%u,%u,%u]" , 197pmu [0 ],pmu [1 ],pmu [2 ],pmu [3 ],pmu [4 ],pmu [5 ],pmu [6 ],pmu [7 ]); 198 } 199 200htp_opformat fmt (node ); 201float mhz = op_usec > 0 ? (float )op_cycles /op_usec :0.0f ; 202GGML_LOG_DEBUG ("ggml-hex: %s profile-op %s|%s|%s|%s|%s|%s|usec %u cycles %u start %u mhz %.1f%s\n" ,sess_name .c_str (), 203node .op_name ().c_str (),fmt .names ,fmt .dims ,fmt .types ,fmt .strides ,fmt .kparams ,op_usec ,op_cycles ,pd .cycles_start ,mhz ,pmu_str ); 204} 205 206static void ggml_hexagon_dump_batch_prof (const std::string & sess_name ,const htp_opbatch_rsp & rsp ) { 207uint64_t batch_cycles = rsp .cycles_stop - rsp .cycles_start ; 208float batch_mhz = rsp .usecs > 0 ? (float )batch_cycles /rsp .usecs :0.0f ; 209 210char evt_str [256 ]= "----" ; 211if (opt_profile == 3 ) { 212snprintf (evt_str ,sizeof (evt_str ),"evt-cnt %u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u" , 213rsp .n_traces [0 ],rsp .n_traces [1 ],rsp .n_traces [2 ],rsp .n_traces [3 ], 214rsp .n_traces [4 ],rsp .n_traces [5 ],rsp .n_traces [6 ],rsp .n_traces [7 ], 215rsp .n_traces [8 ],rsp .n_traces [9 ],rsp .n_traces [10 ]); 216 } 217 218GGML_LOG_DEBUG ("ggml-hex: %s profile-op OPBATCH|----|n-ops %u|%s|----|----|usec %u cycles %llu start %llu mhz %.1f\n" , 219sess_name .c_str (),rsp .n_ops ,evt_str ,rsp .usecs , (unsigned long long )batch_cycles , (unsigned long long )rsp .cycles_start ,batch_mhz ); 220} 221 222static void ggml_hexagon_dump_trace_events (const std::string & sess_name ,const htp_opbatch_rsp & rsp , 223const htp_trace_desc * trace_events ,uint32_t n_traces ) { 224if (opt_profile == 3 && trace_events ) { 225uint32_t valid_cnt [HTP_MAX_NTHREADS + 1 ]= {0 }; 226for (uint32_t t = 0 ;t <=HTP_MAX_NTHREADS ;t ++ ) { 227uint32_t count = rsp .n_traces [t ]; 228valid_cnt [t ]= count > n_traces ?n_traces :count ; 229 } 230 231for (uint32_t t = 0 ;t <=HTP_MAX_NTHREADS ;t ++ ) { 232for (uint32_t idx = 0 ;idx < valid_cnt [t ];idx ++ ) { 233const auto & e = trace_events [t * n_traces + idx ]; 234bool is_stop = (e .info & 0x8000 )!= 0 ; 235uint16_t info = e .info & 0x7FFF ; 236GGML_LOG_DEBUG ("ggml-hex: %s trace-evt %s: thread %u info %u %s %u\n" , 237sess_name .c_str (),htp_event_name (e .id ),t ,info ,is_stop ?"stop" :"start" ,e .cycles ); 238 } 239 } 240 } 241} 242 243enum ggml_hexagon_tensor_flags { 244GGML_HEXAGON_TENSOR_REPACK = (1 <<0 ), 245GGML_HEXAGON_TENSOR_WEIGHT = (1 <<1 ), 246GGML_HEXAGON_TENSOR_FENCE = (1 <<2 ), 247GGML_HEXAGON_TENSOR_FUSEABLE = (1 <<3 ), 248}; 249 250static inline bool ggml_hexagon_is_repack_type (enum ggml_type type ) { 251return type == GGML_TYPE_Q4_0 || type == GGML_TYPE_Q4_1 || 252type == GGML_TYPE_Q8_0 || type == GGML_TYPE_IQ4_NL || 253type == GGML_TYPE_MXFP4 ; 254} 255 256static inline bool ggml_hexagon_is_hmx_weight_type (enum ggml_type type ) { 257return type == GGML_TYPE_F16 || type == GGML_TYPE_F32 || ggml_hexagon_is_repack_type (type ); 258} 259 260struct ggml_hexagon_session ; 261 262static void ggml_hexagon_precompute_matmul_params ( 263const struct ggml_hexagon_session * sess , 264const struct ggml_tensor * src0 , 265const struct ggml_tensor * src1 , 266const struct ggml_tensor * dst , 267struct htp_mm_kernel_params * kparams 268); 269 270static void ggml_hexagon_precompute_fused_matmul_add_params ( 271const struct ggml_hexagon_session * sess , 272const struct ggml_tensor * src0 , 273const struct ggml_tensor * src1 , 274const struct ggml_tensor * src2 , 275const struct ggml_tensor * dst , 276struct htp_mm_kernel_params * kparams 277); 278 279static void ggml_hexagon_precompute_unary_params ( 280const struct ggml_hexagon_session * sess , 281uint32_t op , 282const struct ggml_tensor * src0 , 283const struct ggml_tensor * src1 , 284const struct ggml_tensor * dst , 285struct htp_unary_kernel_params * kparams 286); 287 288static void ggml_hexagon_precompute_get_rows_params ( 289const struct ggml_hexagon_session * sess , 290const struct ggml_tensor * src0 , 291const struct ggml_tensor * src1 , 292const struct ggml_tensor * dst , 293struct htp_get_rows_kernel_params * kparams 294); 295 296static void ggml_hexagon_precompute_set_rows_params ( 297const struct ggml_hexagon_session * sess , 298const struct ggml_tensor * src0 , 299const struct ggml_tensor * src1 , 300const struct ggml_tensor * dst , 301struct htp_set_rows_kernel_params * kparams 302); 303 304static void ggml_hexagon_precompute_rope_params ( 305const struct ggml_hexagon_session * sess , 306const struct ggml_tensor * op , 307struct htp_rope_kernel_params * kparams 308); 309 310static void ggml_hexagon_precompute_fused_mmnx_params ( 311const struct ggml_hexagon_session * sess , 312const struct ggml_tensor * src0 , 313const struct ggml_tensor * src1 , 314int32_t n_weights , 315struct htp_mm_kernel_params * kparams 316); 317 318static void ggml_hexagon_precompute_fused_mmidnx_params ( 319const struct ggml_hexagon_session * sess , 320const struct ggml_tensor * src0 , 321const struct ggml_tensor * src1 , 322const struct ggml_tensor * dst , 323int32_t n_weights , 324struct htp_mm_kernel_params * kparams 325); 326 327static bool ggml_hexagon_precompute_allreduce_params ( 328const struct ggml_hexagon_session * sess , 329const struct ggml_tensor * dst , 330uint32_t rank , 331uint32_t n_ranks , 332bool has_add , 333bool is_row_bcast , 334struct htp_allreduce_kernel_params * kparams 335); 336 337static bool mm_is_hmx_eligible (const ggml_tensor * t ); 338static bool is_supported_mul_mat_nx_kernel (const ggml_tensor * src0 ,const struct htp_mm_kernel_params * kparams ); 339static bool is_supported_mul_mat_id_nx_kernel (const ggml_tensor * src0 ,const struct htp_mm_kernel_params * kparams ); 340static bool is_mergeable_mul_mat (const ggml_tensor * t ); 341static bool is_mergeable_mul_mat_pair (const ggml_tensor * n1 ,const ggml_tensor * n2 ); 342static bool is_mergeable_mul_mat_id (const ggml_tensor * t ); 343static bool is_mergeable_mul_mat_id_pair (const ggml_tensor * n1 ,const ggml_tensor * n2 ); 344 345// ** backend sessions 346 347struct ggml_hexagon_tensor_extra { 348 std::vector < uint8_t > shadow_buf ; 349size_t shadow_size {0 }; 350uint32_t flags {0 }; 351}; 352 353static inline bool ggml_hexagon_tensor_is_fuseable (const struct ggml_tensor * t ) { 354if (!t -> extra )return false; 355auto extra = (const struct ggml_hexagon_tensor_extra * )t -> extra ; 356return (extra -> flags & GGML_HEXAGON_TENSOR_FUSEABLE )!= 0 ; 357} 358 359static inline bool ggml_hexagon_tensors_overlap (const struct ggml_tensor * a ,const struct ggml_tensor * b ) { 360const uintptr_t a0 = (uintptr_t )a -> data ; 361const uintptr_t b0 = (uintptr_t )b -> data ; 362const uintptr_t a1 = a0 + ggml_nbytes (a ); 363const uintptr_t b1 = b0 + ggml_nbytes (b ); 364 365return a0 < b1 && b0 < a1 ; 366} 367 368struct htp_opnode ; 369 370struct ggml_hexagon_opbatch ; 371struct ggml_hexagon_opqueue ; 372struct ggml_hexagon_shared_buffer ; 373struct ggml_hexagon_fence_buffer ; 374struct ggml_hexagon_session ; 375struct ggml_backend_hexagon_device_context ; 376 377struct ggml_hexagon_mdev_group { 378uint32_t idx = 0 ; 379uint32_t count = 1 ; 380 std::vector < std::unique_ptr < ggml_hexagon_session >> sessions ; 381}; 382 383struct ggml_backend_hexagon_comm_context { 384 std::vector < ggml_backend_t > backends ; 385size_t n_backends = 0 ; 386volatile uint32_t * fence_slots [GGML_HEXAGON_MAX_SESSIONS ]= {}; 387ggml_tensor fence_tensors [GGML_HEXAGON_MAX_SESSIONS ]= {}; 388}; 389 390struct ggml_hexagon_event { 391ggml_hexagon_session * sess = nullptr ; 392ggml_hexagon_session * fence_sess = nullptr ; 393volatile uint32_t * fence_slot = nullptr ; 394ggml_tensor fence_tensor = {}; 395uint32_t seq = 0 ; 396}; 397 398struct ggml_hexagon_session { 399 std::string name ; 400remote_handle64 handle ; 401dspqueue_t queue ; 402uint32_t session_id ; 403uint32_t domain_id ; 404uint64_t queue_id ; 405int phys_idx ; 406int virt_idx ; 407bool valid_session ; 408bool valid_handle ; 409bool valid_queue ; 410bool valid_iface ; 411 412ggml_hexagon_opbatch * op_batch ; 413ggml_hexagon_opqueue * op_queue ; 414 415 std::unordered_map < int , std::unique_ptr < ggml_hexagon_shared_buffer >> cloned_buffers ; 416 std::unordered_set < ggml_hexagon_session *> virt_peers ; 417 std::unordered_set < ggml_hexagon_session *> phys_peers ; 418 419uint32_t n_threads = 0 ; 420uint32_t n_hvx = 0 ; 421uint32_t n_hmx = 0 ; 422uint64_t vtcm_size = 0 ; 423size_t max_vmem = 0 ; 424size_t max_bufsize = 0 ; 425uint32_t fence_seq = 0 ; 426 427 std::atomic < uint64_t > batch_req_seq {0 }; 428 std::atomic < uint64_t > batch_rsp_seq {0 }; 429 std::atomic < uint32_t > last_error {HTP_STATUS_OK }; 430 431uint64_t cached_uid = 0 ; 432 std::vector < htp_opnode > cached_nodes ; 433 434mutable std::unordered_set < const ggml_tensor *> needs_repack ; 435 436ggml_hexagon_mdev_group mdev ; 437ggml_backend_dev_t dev = nullptr ; 438ggml_backend_hexagon_device_context * dev_ctx = nullptr ; 439ggml_hexagon_fence_buffer * fence_buf = nullptr ; 440 441ggml_hexagon_session (const ggml_hexagon_device_config & config ,ggml_backend_dev_t dev = nullptr ,uint32_t mdev_idx = 0 ,uint32_t mdev_count = 0 )noexcept (false); 442 ~ggml_hexagon_session ()noexcept (true); 443 444const char * c_name ()const {return name .c_str (); } 445 446void allocate (const ggml_hexagon_device_config & config )noexcept (false); 447void release ()noexcept (true); 448 449uint8_t * alloc_fence (uint32_t n_slots = 1 ); 450void free_fence (void * ptr ,uint32_t n_slots = 1 ); 451 452uint8_t * mdev_fence_slot = nullptr ; 453 std::unordered_map < uint64_t ,volatile uint32_t *> cpy_fence_slots ; 454 455void enqueue_mdev_group (); 456void enqueue_op (const htp_opnode & node ); 457void enqueue_cpy (const ggml_tensor * src ,ggml_tensor * dst ,const ggml_tensor * sync_tensor = nullptr ,uint32_t fence_seq = 0 ); 458void enqueue_fence (const ggml_tensor * sync_tensor ,uint32_t fence_seq = 0 ,bool wait = true); 459void enqueue_allreduce (const ggml_tensor * dst ,const std::vector < const ggml_tensor *> & src_tensors , 460const std::vector < const ggml_tensor *> & sync_tensors ,uint32_t rank ,uint32_t n_ranks , 461uint32_t fence_seq_entry = 0 ,uint32_t fence_seq_exit = 0 ); 462 463void flush_sync (bool all = true); 464void flush_async (); 465void flush_batch (size_t min_ops = 1 ); 466void flush_peers (); 467void flush_pending (bool all = true); 468 469bool clone_buffer (const ggml_hexagon_shared_buffer * ); 470void release_buffer (const ggml_hexagon_shared_buffer * ); 471void unclone_buffer (const ggml_hexagon_shared_buffer * ); 472 473void add_peer (ggml_hexagon_session * peer ) { 474if (this -> phys_idx == peer -> phys_idx ) { 475virt_peers .insert (peer ); 476 }else { 477phys_peers .insert (peer ); 478 } 479 } 480}; 481 482// ** backend buffers 483 484struct ggml_backend_hexagon_device_context { 485int dev_id ; 486ggml_hexagon_device_config config ; 487ggml_backend_dev_t dev = nullptr ; 488size_t max_bufsize = 0 ; 489 490ggml_backend_buffer_type buffer_type = {}; 491ggml_backend_buffer_type host_buffer_type = {}; 492ggml_backend_buffer_type fence_buffer_type = {}; 493 494 std::unique_ptr < ggml_hexagon_session > sess ; 495 496ggml_backend_hexagon_device_context (int dev_id ,const ggml_hexagon_device_config & config ,ggml_backend_dev_t dev ); 497 ~ggml_backend_hexagon_device_context (); 498 499const char * c_name ()const {return config .name .c_str (); } 500 501ggml_hexagon_session * session () { 502if (!sess ) { 503sess = std::make_unique < ggml_hexagon_session > (config ,dev ); 504 } 505return sess .get (); 506 } 507}; 508 509struct ggml_backend_hexagon_buffer_type_context { 510ggml_backend_hexagon_buffer_type_context (const std::string & name ,ggml_backend_hexagon_device_context * dev_ctx ) { 511this -> dev_ctx = dev_ctx ; 512this -> name = name ; 513 } 514 515ggml_backend_hexagon_device_context * dev_ctx ; 516 std::string name ; 517}; 518 519struct ggml_hexagon_rpcmem_block { 520uint8_t * base = nullptr ; 521int fd = -1 ; 522size_t size = 0 ; 523 524 std::unordered_set < ggml_hexagon_session *> mapped_clones ; 525 526ggml_hexagon_rpcmem_block (size_t size ) { 527base = (uint8_t * )rpcmem_alloc2 (RPCMEM_HEAP_ID_SYSTEM ,RPCMEM_DEFAULT_FLAGS ,size ); 528if (!base ) { 529throw std::runtime_error ("ggml-hex: rpcmem_alloc failed" ); 530 } 531fd = rpcmem_to_fd (base ); 532if (fd < 0 ) { 533rpcmem_free (base ); 534throw std::runtime_error ("ggml-hex: rpcmem_to_fd failed" ); 535 } 536this -> size = size ; 537 } 538 539 ~ggml_hexagon_rpcmem_block () { 540if (base ) { 541rpcmem_free (base ); 542 } 543 } 544}; 545 546struct ggml_hexagon_shared_buffer { 547ggml_hexagon_session * sess ; 548 std::shared_ptr < ggml_hexagon_rpcmem_block > mem ; 549 std::vector < ggml_hexagon_tensor_extra *> tensor_extra ; 550bool mapped ; 551bool pinned ; 552 553const char * c_name ()const {return sess -> c_name (); } 554uint8_t * base ()const {return mem ?mem -> base :nullptr ; } 555size_t size ()const {return mem ?mem -> size :0 ; } 556int fd ()const {return mem ?mem -> fd :-1 ; } 557 558void mmap () { 559if (!this -> mem )return ; 560fastrpc_map_flags flags = this -> pinned ?FASTRPC_MAP_FD :FASTRPC_MAP_FD_DELAYED ; 561 562int err = fastrpc_mmap (sess -> domain_id ,fd (), (void * )base (),0 ,size (),flags ); 563if (err != 0 ) { 564GGML_LOG_ERROR ("ggml-hex: %s buffer mapping failed : domain_id %d size %zu fd %d error 0x%08x\n" ,sess -> c_name (), 565sess -> domain_id ,size (),fd (), (unsigned )err ); 566throw std::runtime_error ("ggml-hex: fastrpc_mmap failed (see log for details)" ); 567 } 568 569HEX_VERBOSE ("ggml-hex: %s mapped buffer: base %p size %zu fd %d pinned %u\n" , 570sess -> c_name (), (void * )base (),size (),fd (),pinned ); 571 572this -> mapped = true; 573 } 574 575void unmap () { 576if (!this -> mapped )return ; 577 578if (!this -> pinned && mem ) { 579// HTP might still hold a reference, tell it drop it 580htp_iface_munmap (sess -> handle ,fd ()); 581 } 582 583if (mem ) { 584fastrpc_munmap (sess -> domain_id ,fd (), (void * )base (),size ()); 585 } 586 587HEX_VERBOSE ("ggml-hex: %s unmapped buffer: base %p size %zu fd %d\n" ,sess -> c_name (), 588 (void * )base (),size (),fd ()); 589 590this -> mapped = false; 591 } 592 593void alloc (size_t size ) { 594if (this -> mem )return ; 595 596this -> mem = std::make_shared < ggml_hexagon_rpcmem_block > (size ); 597 598HEX_VERBOSE ("ggml-hex: %s allocated buffer: base %p size %zu fd %d pinned %d\n" ,sess -> c_name (), 599 (void * )base (),this -> size (),fd (), (int )pinned ); 600mmap (); 601 } 602 603void free () { 604unmap (); 605// The memory is freed when the shared_ptr refcount drops to 0. 606HEX_VERBOSE ("ggml-hex: %s release ref on buffer: base %p size %zu fd %d\n" ,sess -> c_name (), 607 (void * )base (),size (),fd ()); 608this -> mem = nullptr ; 609 } 610 611ggml_hexagon_shared_buffer (ggml_hexagon_session * sess ,size_t size ,bool pinned = false) { 612this -> sess = sess ; 613this -> mapped = false; 614this -> pinned = pinned ; 615 616// Size adjustment inside the buffer class: 4K aligned data size + 4K guard page 617size_t guard_offset = (size + 4095 )& ~4095 ; 618size_t total_size = guard_offset + 4096 ; 619 620alloc (total_size ); 621 } 622 623// Clone constructor for cross-session mapping 624ggml_hexagon_shared_buffer (ggml_hexagon_session * sess ,const ggml_hexagon_shared_buffer & other ) { 625this -> sess = sess ; 626this -> mem = other .mem ; 627this -> mapped = false; 628this -> pinned = other .pinned ; 629 } 630 631 ~ggml_hexagon_shared_buffer () { 632free (); 633for (auto * extra :tensor_extra ) { 634delete extra ; 635 } 636 } 637}; 638 639struct ggml_hexagon_fence_buffer :public ggml_hexagon_shared_buffer { 640uint32_t slot_count = 0 ; 641uint32_t slot_head = 0 ; 642 std::vector < uint32_t > free_slots ; 643ggml_backend_buffer backend_buffer {}; 644 645ggml_hexagon_fence_buffer (ggml_hexagon_session * sess ,ggml_backend_buffer_type_t buft ,size_t size ) 646 :ggml_hexagon_shared_buffer (sess ,size , false/* pinned */ ), 647slot_count (size /GGML_HEXAGON_FENCE_SLOT_SIZE ), 648slot_head (0 ) { 649backend_buffer .buft = buft ; 650backend_buffer .context = static_cast < ggml_hexagon_shared_buffer *> (this ); 651backend_buffer .size = size ; 652 } 653 654uint8_t * alloc_slot (uint32_t n_slots = 1 ) { 655uint8_t * ptr = nullptr ; 656if (n_slots == 1 && !free_slots .empty ()) { 657uint32_t slot = free_slots .back (); 658free_slots .pop_back (); 659ptr = base ()+ (size_t )slot * GGML_HEXAGON_FENCE_SLOT_SIZE ; 660 }else if (slot_head + n_slots <=slot_count ) { 661uint32_t slot = slot_head ; 662slot_head += n_slots ; 663ptr = base ()+ (size_t )slot * GGML_HEXAGON_FENCE_SLOT_SIZE ; 664 } 665if (ptr ) { 666memset (ptr ,0 , (size_t )n_slots * GGML_HEXAGON_FENCE_SLOT_SIZE ); 667 } 668return ptr ; 669 } 670 671void free_slot (void * ptr ,uint32_t n_slots = 1 ) { 672if (!ptr )return ; 673uint32_t slot = ((uint8_t * )ptr - base ()) /GGML_HEXAGON_FENCE_SLOT_SIZE ; 674for (uint32_t i = 0 ;i < n_slots ;i ++ ) { 675free_slots .push_back (slot + i ); 676 } 677 } 678}; 679 680inline uint8_t * ggml_hexagon_session::alloc_fence (uint32_t n_slots ) { 681uint8_t * ptr = fence_buf -> alloc_slot (n_slots ); 682GGML_ASSERT (ptr ); 683return ptr ; 684} 685 686inline void ggml_hexagon_session::free_fence (void * ptr ,uint32_t n_slots ) { 687if (fence_buf ) { 688fence_buf -> free_slot (ptr ,n_slots ); 689 } 690} 691 692static ggml_hexagon_session * ggml_backend_hexagon_buffer_get_sess (ggml_backend_buffer_t buffer ) { 693auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (buffer -> context ); 694return sbuf -> sess ; 695} 696 697static void ggml_backend_hexagon_buffer_free_buffer (ggml_backend_buffer_t buffer ) { 698auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (buffer -> context ); 699sbuf -> sess -> unclone_buffer (sbuf ); 700delete sbuf ; 701} 702 703static void * ggml_backend_hexagon_buffer_get_base (ggml_backend_buffer_t buffer ) { 704auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (buffer -> context ); 705return sbuf -> base (); 706} 707 708static enum ggml_status ggml_backend_hexagon_buffer_init_tensor (ggml_backend_buffer_t buffer ,ggml_tensor * tensor ) { 709auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (buffer -> context ); 710auto sess = sbuf -> sess ; 711 712HEX_VERBOSE ("ggml-hex: %s init-tensor %s : base %p data %p nbytes %zu\n" ,sess -> c_name (), 713tensor -> name , (void * )sbuf -> base (),tensor -> data ,ggml_nbytes (tensor )); 714 715auto extra = new ggml_hexagon_tensor_extra (); 716sbuf -> tensor_extra .push_back (extra ); 717 718tensor -> extra = extra ; 719if (ggml_hexagon_is_repack_type (tensor -> type )) { 720if (sess -> needs_repack .count (tensor )) { 721extra -> flags |=GGML_HEXAGON_TENSOR_REPACK ; 722sess -> needs_repack .erase (tensor ); 723 } 724 } 725 726return GGML_STATUS_SUCCESS ; 727} 728 729// ** Repack helpers for tiled quantized weights 730 731static void unpack_q4_0_quants (uint8_t * qs ,const block_q4_0 * x ,unsigned int bi ) { 732static const int qk = QK4_0 ; 733 734for (unsigned int i = 0 ;i < qk /2 ;++ i ) { 735const int x0 = (x -> qs [i ]& 0x0F ); 736const int x1 = (x -> qs [i ] >>4 ); 737qs [bi * qk + i + 0 ]= x0 ; 738qs [bi * qk + i + qk /2 ]= x1 ; 739 } 740} 741 742static void pack_q4_0_quants (block_q4_0 * x ,const uint8_t * qs ,unsigned int bi ) { 743static const int qk = QK4_0 ; 744 745for (unsigned int i = 0 ;i < qk /2 ;++ i ) { 746const uint8_t x0 = qs [bi * qk + i + 0 ]; 747const uint8_t x1 = qs [bi * qk + i + qk /2 ]; 748x -> qs [i ]= x0 | (x1 <<4 ); 749 } 750} 751 752static void unpack_q4_1_quants (uint8_t * qs ,const block_q4_1 * x ,unsigned int bi ) { 753static const int qk = QK4_1 ; 754 755for (unsigned int i = 0 ;i < qk /2 ;++ i ) { 756const int x0 = (x -> qs [i ]& 0x0F ); 757const int x1 = (x -> qs [i ] >>4 ); 758qs [bi * qk + i + 0 ]= x0 ; 759qs [bi * qk + i + qk /2 ]= x1 ; 760 } 761} 762 763static void pack_q4_1_quants (block_q4_1 * x ,const uint8_t * qs ,unsigned int bi ) { 764static const int qk = QK4_1 ; 765 766for (unsigned int i = 0 ;i < qk /2 ;++ i ) { 767const uint8_t x0 = qs [bi * qk + i + 0 ]; 768const uint8_t x1 = qs [bi * qk + i + qk /2 ]; 769x -> qs [i ]= x0 | (x1 <<4 ); 770 } 771} 772 773static void unpack_mxfp4_quants (uint8_t * qs ,const block_mxfp4 * x ,unsigned int bi ) { 774static const int qk = QK_MXFP4 ; 775 776for (unsigned int i = 0 ;i < qk /2 ;++ i ) { 777const int x0 = (x -> qs [i ]& 0x0F ); 778const int x1 = (x -> qs [i ] >>4 ); 779qs [bi * qk + i + 0 ]= x0 ; 780qs [bi * qk + i + qk /2 ]= x1 ; 781 } 782} 783 784static void pack_mxfp4_quants (block_mxfp4 * x ,const uint8_t * qs ,unsigned int bi ) { 785static const int qk = QK_MXFP4 ; 786 787for (unsigned int i = 0 ;i < qk /2 ;++ i ) { 788const uint8_t x0 = qs [bi * qk + i + 0 ]; 789const uint8_t x1 = qs [bi * qk + i + qk /2 ]; 790x -> qs [i ]= x0 | (x1 <<4 ); 791 } 792} 793 794// repack q4_0 data into q4_0_tiled tensor 795static void repack_q4_0_tiled (ggml_tensor * t ,const void * data ,size_t offset ,size_t size ) { 796const block_q4_0 * src_matrix = (const block_q4_0 * )data ; 797int64_t ne0 = t -> ne [0 ]; 798int64_t ne1 = t -> ne [1 ]; 799int64_t ne2 = t -> ne [2 ]; 800int64_t ne3 = t -> ne [3 ]; 801int64_t ne0_padded = hex_round_up (ne0 ,32 ); 802int64_t ne1_padded = hex_round_up (ne1 ,32 ); 803 804int n_col_tiles = ne1_padded /32 ; 805int n_k_tiles = ne0_padded /32 ; 806const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_Q4_0 ; 807const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size ; 808 809size_t slice_size = ne1 * ggml_row_size (t -> type ,ne0 ); 810int64_t start_slice = offset /slice_size ; 811int64_t end_slice = (offset + size + slice_size - 1 ) /slice_size ; 812if (end_slice > ne2 * ne3 ) { 813end_slice = ne2 * ne3 ; 814 } 815 816for (int64_t slice_idx = start_slice ;slice_idx < end_slice ;slice_idx ++ ) { 817const block_q4_0 * src_slice = src_matrix + (slice_idx - start_slice )* (ne1 * (ne0 /32 )); 818uint8_t * matrix_dst = (uint8_t * )t -> data + slice_idx * matrix_size ; 819 820for (int ct = 0 ;ct < n_col_tiles ;ct ++ ) { 821for (int kt = 0 ;kt < n_k_tiles ;kt ++ ) { 822uint8_t * tile_dst = matrix_dst + (ct * n_k_tiles + kt )* tile_size ; 823 824uint8_t tile_quants [32 ][32 ]; 825for (int row = 0 ;row < 32 ;row ++ ) { 826int64_t r = ct * 32 + row ; 827if (r < ne1 && kt < ne0 /32 ) { 828unpack_q4_0_quants (tile_quants [row ],& src_slice [r * (ne0 /32 )+ kt ],0 ); 829 }else { 830memset (tile_quants [row ],8 ,32 ); 831 } 832 } 833 834for (int cp = 0 ;cp < 16 ;cp ++ ) { 835for (int row = 0 ;row < 32 ;row ++ ) { 836tile_dst [cp * 32 + row ]= (tile_quants [row ][2 * cp + 1 ] <<4 ) |tile_quants [row ][2 * cp ]; 837 } 838 } 839 840ggml_half * scale_dst = (ggml_half * )(tile_dst + 512 ); 841for (int row = 0 ;row < 32 ;row ++ ) { 842int64_t r = ct * 32 + row ; 843scale_dst [row ]= (r < ne1 && kt < ne0 /32 ) ?src_slice [r * (ne0 /32 )+ kt ].d :0 ; 844 } 845 } 846 } 847 } 848} 849 850// repack q4_0_tiled tensor into q4_0 data 851static void repack_tiled_q4_0 (void * data ,const ggml_tensor * t ,size_t offset ,size_t size ) { 852block_q4_0 * dst_matrix = (block_q4_0 * )data ; 853int64_t ne0 = t -> ne [0 ]; 854int64_t ne1 = t -> ne [1 ]; 855int64_t ne2 = t -> ne [2 ]; 856int64_t ne3 = t -> ne [3 ]; 857int64_t ne0_padded = hex_round_up (ne0 ,32 ); 858int64_t ne1_padded = hex_round_up (ne1 ,32 ); 859 860int n_col_tiles = ne1_padded /32 ; 861int n_k_tiles = ne0_padded /32 ; 862const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_Q4_0 ; 863const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size ; 864 865size_t slice_size = ne1 * ggml_row_size (t -> type ,ne0 ); 866size_t row_size_bytes = ggml_row_size (t -> type ,ne0 ); 867int64_t start_slice = offset /slice_size ; 868int64_t end_slice = (offset + size + slice_size - 1 ) /slice_size ; 869if (end_slice > ne2 * ne3 ) { 870end_slice = ne2 * ne3 ; 871 } 872 873for (int64_t slice_idx = start_slice ;slice_idx < end_slice ;slice_idx ++ ) { 874size_t cur_start_byte = (std::max )(offset , (size_t )slice_idx * slice_size ); 875size_t cur_end_byte = (std::min )(offset + size , (size_t ) (slice_idx + 1 )* slice_size ); 876size_t slice_offset_start = cur_start_byte - (size_t )slice_idx * slice_size ; 877size_t slice_offset_end = cur_end_byte - (size_t )slice_idx * slice_size ; 878 879int64_t start_row = slice_offset_start /row_size_bytes ; 880int64_t end_row = (slice_offset_end + row_size_bytes - 1 ) /row_size_bytes ; 881end_row = (std::min )(end_row ,ne1 ); 882 883int start_ct = start_row /32 ; 884int end_ct = (end_row + 31 ) /32 ; 885end_ct = (std::min )(end_ct ,n_col_tiles ); 886 887block_q4_0 * dst_slice = dst_matrix + (cur_start_byte - offset ) /sizeof (block_q4_0 ); 888const uint8_t * matrix_src = (const uint8_t * )t -> data + slice_idx * matrix_size ; 889 890for (int ct = start_ct ;ct < end_ct ;ct ++ ) { 891for (int kt = 0 ;kt < n_k_tiles ;kt ++ ) { 892const uint8_t * tile_src = matrix_src + (ct * n_k_tiles + kt )* tile_size ; 893 894uint8_t tile_quants [32 ][32 ]; 895for (int cp = 0 ;cp < 16 ;cp ++ ) { 896for (int row = 0 ;row < 32 ;row ++ ) { 897uint8_t val = tile_src [cp * 32 + row ]; 898tile_quants [row ][2 * cp + 0 ]= val & 0x0F ; 899tile_quants [row ][2 * cp + 1 ]= val >>4 ; 900 } 901 } 902 903for (int row = 0 ;row < 32 ;row ++ ) { 904int64_t r = ct * 32 + row ; 905if (r >=start_row && r < end_row && kt < ne0 /32 ) { 906pack_q4_0_quants (& dst_slice [(r - start_row )* (ne0 /32 )+ kt ],tile_quants [row ],0 ); 907 } 908 } 909 910const ggml_half * scale_src = (const ggml_half * )(tile_src + 512 ); 911for (int row = 0 ;row < 32 ;row ++ ) { 912int64_t r = ct * 32 + row ; 913if (r >=start_row && r < end_row && kt < ne0 /32 ) { 914dst_slice [(r - start_row )* (ne0 /32 )+ kt ].d = scale_src [row ]; 915 } 916 } 917 } 918 } 919 } 920} 921 922// repack q4_1 data into q4_1_tiled tensor 923static void repack_q4_1_tiled (ggml_tensor * t ,const void * data ,size_t offset ,size_t size ) { 924const block_q4_1 * src_matrix = (const block_q4_1 * )data ; 925int64_t ne0 = t -> ne [0 ]; 926int64_t ne1 = t -> ne [1 ]; 927int64_t ne2 = t -> ne [2 ]; 928int64_t ne3 = t -> ne [3 ]; 929int64_t ne0_padded = hex_round_up (ne0 ,32 ); 930int64_t ne1_padded = hex_round_up (ne1 ,32 ); 931 932int n_col_tiles = ne1_padded /32 ; 933int n_k_tiles = ne0_padded /32 ; 934const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_Q4_1 ; 935const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size ; 936 937size_t slice_size = ne1 * ggml_row_size (t -> type ,ne0 ); 938int64_t start_slice = offset /slice_size ; 939int64_t end_slice = (offset + size + slice_size - 1 ) /slice_size ; 940if (end_slice > ne2 * ne3 ) { 941end_slice = ne2 * ne3 ; 942 } 943 944for (int64_t slice_idx = start_slice ;slice_idx < end_slice ;slice_idx ++ ) { 945const block_q4_1 * src_slice = src_matrix + (slice_idx - start_slice )* (ne1 * (ne0 /32 )); 946uint8_t * matrix_dst = (uint8_t * )t -> data + slice_idx * matrix_size ; 947 948for (int ct = 0 ;ct < n_col_tiles ;ct ++ ) { 949for (int kt = 0 ;kt < n_k_tiles ;kt ++ ) { 950uint8_t * tile_dst = matrix_dst + (ct * n_k_tiles + kt )* tile_size ; 951 952uint8_t tile_quants [32 ][32 ]; 953for (int row = 0 ;row < 32 ;row ++ ) { 954int64_t r = ct * 32 + row ; 955if (r < ne1 && kt < ne0 /32 ) { 956unpack_q4_1_quants (tile_quants [row ],& src_slice [r * (ne0 /32 )+ kt ],0 ); 957 }else { 958memset (tile_quants [row ],0 ,32 ); 959 } 960 } 961 962for (int cp = 0 ;cp < 16 ;cp ++ ) { 963for (int row = 0 ;row < 32 ;row ++ ) { 964tile_dst [cp * 32 + row ]= (tile_quants [row ][2 * cp + 1 ] <<4 ) |tile_quants [row ][2 * cp ]; 965 } 966 } 967 968ggml_half * scale_dst = (ggml_half * )(tile_dst + 512 ); 969for (int row = 0 ;row < 32 ;row ++ ) { 970int64_t r = ct * 32 + row ; 971if (r < ne1 && kt < ne0 /32 ) { 972scale_dst [2 * row + 0 ]= src_slice [r * (ne0 /32 )+ kt ].d ; 973scale_dst [2 * row + 1 ]= src_slice [r * (ne0 /32 )+ kt ].m ; 974 }else { 975scale_dst [2 * row + 0 ]= 0 ; 976scale_dst [2 * row + 1 ]= 0 ; 977 } 978 } 979 } 980 } 981 } 982} 983 984// repack q4_1_tiled tensor into q4_1 data 985static void repack_tiled_q4_1 (void * data ,const ggml_tensor * t ,size_t offset ,size_t size ) { 986block_q4_1 * dst_matrix = (block_q4_1 * )data ; 987int64_t ne0 = t -> ne [0 ]; 988int64_t ne1 = t -> ne [1 ]; 989int64_t ne2 = t -> ne [2 ]; 990int64_t ne3 = t -> ne [3 ]; 991int64_t ne0_padded = hex_round_up (ne0 ,32 ); 992int64_t ne1_padded = hex_round_up (ne1 ,32 ); 993 994int n_col_tiles = ne1_padded /32 ; 995int n_k_tiles = ne0_padded /32 ; 996const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_Q4_1 ; 997const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size ; 998 999size_t slice_size = ne1 * ggml_row_size (t -> type ,ne0 ); 1000size_t row_size_bytes = ggml_row_size (t -> type ,ne0 ); 1001int64_t start_slice = offset /slice_size ; 1002int64_t end_slice = (offset + size + slice_size - 1 ) /slice_size ; 1003if (end_slice > ne2 * ne3 ) { 1004end_slice = ne2 * ne3 ; 1005 } 1006 1007for (int64_t slice_idx = start_slice ;slice_idx < end_slice ;slice_idx ++ ) { 1008size_t cur_start_byte = (std::max )(offset , (size_t )slice_idx * slice_size ); 1009size_t cur_end_byte = (std::min )(offset + size , (size_t ) (slice_idx + 1 )* slice_size ); 1010size_t slice_offset_start = cur_start_byte - (size_t )slice_idx * slice_size ; 1011size_t slice_offset_end = cur_end_byte - (size_t )slice_idx * slice_size ; 1012 1013int64_t start_row = slice_offset_start /row_size_bytes ; 1014int64_t end_row = (slice_offset_end + row_size_bytes - 1 ) /row_size_bytes ; 1015end_row = (std::min )(end_row ,ne1 ); 1016 1017int start_ct = start_row /32 ; 1018int end_ct = (end_row + 31 ) /32 ; 1019end_ct = (std::min )(end_ct ,n_col_tiles ); 1020 1021block_q4_1 * dst_slice = dst_matrix + (cur_start_byte - offset ) /sizeof (block_q4_1 ); 1022const uint8_t * matrix_src = (const uint8_t * )t -> data + slice_idx * matrix_size ; 1023 1024for (int ct = start_ct ;ct < end_ct ;ct ++ ) { 1025for (int kt = 0 ;kt < n_k_tiles ;kt ++ ) { 1026const uint8_t * tile_src = matrix_src + (ct * n_k_tiles + kt )* tile_size ; 1027 1028uint8_t tile_quants [32 ][32 ]; 1029for (int cp = 0 ;cp < 16 ;cp ++ ) { 1030for (int row = 0 ;row < 32 ;row ++ ) { 1031uint8_t val = tile_src [cp * 32 + row ]; 1032tile_quants [row ][2 * cp + 0 ]= val & 0x0F ; 1033tile_quants [row ][2 * cp + 1 ]= val >>4 ; 1034 } 1035 } 1036 1037for (int row = 0 ;row < 32 ;row ++ ) { 1038int64_t r = ct * 32 + row ; 1039if (r >=start_row && r < end_row && kt < ne0 /32 ) { 1040pack_q4_1_quants (& dst_slice [(r - start_row )* (ne0 /32 )+ kt ],tile_quants [row ],0 ); 1041 } 1042 } 1043 1044const ggml_half * scale_src = (const ggml_half * )(tile_src + 512 ); 1045for (int row = 0 ;row < 32 ;row ++ ) { 1046int64_t r = ct * 32 + row ; 1047if (r >=start_row && r < end_row && kt < ne0 /32 ) { 1048dst_slice [(r - start_row )* (ne0 /32 )+ kt ].d = scale_src [2 * row ]; 1049dst_slice [(r - start_row )* (ne0 /32 )+ kt ].m = scale_src [2 * row + 1 ]; 1050 } 1051 } 1052 } 1053 } 1054 } 1055} 1056 1057// repack q8_0 data into q8_0_tiled tensor 1058static void repack_q8_0_tiled (ggml_tensor * t ,const void * data ,size_t offset ,size_t size ) { 1059const block_q8_0 * src_matrix = (const block_q8_0 * )data ; 1060int64_t ne0 = t -> ne [0 ]; 1061int64_t ne1 = t -> ne [1 ]; 1062int64_t ne2 = t -> ne [2 ]; 1063int64_t ne3 = t -> ne [3 ]; 1064int64_t ne0_padded = hex_round_up (ne0 ,32 ); 1065int64_t ne1_padded = hex_round_up (ne1 ,32 ); 1066 1067int n_col_tiles = ne1_padded /32 ; 1068int n_k_tiles = ne0_padded /32 ; 1069const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_Q8_0 ; 1070const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size ; 1071 1072size_t slice_size = ne1 * ggml_row_size (t -> type ,ne0 ); 1073int64_t start_slice = offset /slice_size ; 1074int64_t end_slice = (offset + size + slice_size - 1 ) /slice_size ; 1075if (end_slice > ne2 * ne3 ) { 1076end_slice = ne2 * ne3 ; 1077 } 1078 1079for (int64_t slice_idx = start_slice ;slice_idx < end_slice ;slice_idx ++ ) { 1080const block_q8_0 * src_slice = src_matrix + (slice_idx - start_slice )* (ne1 * (ne0 /32 )); 1081uint8_t * matrix_dst = (uint8_t * )t -> data + slice_idx * matrix_size ; 1082 1083for (int ct = 0 ;ct < n_col_tiles ;ct ++ ) { 1084for (int kt = 0 ;kt < n_k_tiles ;kt ++ ) { 1085uint8_t * tile_dst = matrix_dst + (ct * n_k_tiles + kt )* tile_size ; 1086 1087for (int cp = 0 ;cp < 16 ;cp ++ ) { 1088int col0 = cp * 2 ; 1089int col1 = col0 + 1 ; 1090for (int row = 0 ;row < 32 ;row ++ ) { 1091int64_t r = ct * 32 + row ; 1092const block_q8_0 * b = (r < ne1 && kt < ne0 /32 ) ?& src_slice [r * (ne0 /32 )+ kt ] :NULL ; 1093tile_dst [cp * 64 + 2 * row + 0 ]= b ?b -> qs [col0 ] :0 ; 1094tile_dst [cp * 64 + 2 * row + 1 ]= b ?b -> qs [col1 ] :0 ; 1095 } 1096 } 1097 1098ggml_half * scale_dst = (ggml_half * )(tile_dst + 1024 ); 1099for (int row = 0 ;row < 32 ;row ++ ) { 1100int64_t r = ct * 32 + row ; 1101scale_dst [row ]= (r < ne1 && kt < ne0 /32 ) ?src_slice [r * (ne0 /32 )+ kt ].d :0 ; 1102 } 1103 } 1104 } 1105 } 1106} 1107 1108// repack q8_0_tiled tensor into q8_0 data 1109static void repack_tiled_q8_0 (void * data ,const ggml_tensor * t ,size_t offset ,size_t size ) { 1110block_q8_0 * dst_matrix = (block_q8_0 * )data ; 1111int64_t ne0 = t -> ne [0 ]; 1112int64_t ne1 = t -> ne [1 ]; 1113int64_t ne2 = t -> ne [2 ]; 1114int64_t ne3 = t -> ne [3 ]; 1115int64_t ne0_padded = hex_round_up (ne0 ,32 ); 1116int64_t ne1_padded = hex_round_up (ne1 ,32 ); 1117 1118int n_col_tiles = ne1_padded /32 ; 1119int n_k_tiles = ne0_padded /32 ; 1120const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_Q8_0 ; 1121const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size ; 1122 1123size_t slice_size = ne1 * ggml_row_size (t -> type ,ne0 ); 1124size_t row_size_bytes = ggml_row_size (t -> type ,ne0 ); 1125int64_t start_slice = offset /slice_size ; 1126int64_t end_slice = (offset + size + slice_size - 1 ) /slice_size ; 1127if (end_slice > ne2 * ne3 ) { 1128end_slice = ne2 * ne3 ; 1129 } 1130 1131for (int64_t slice_idx = start_slice ;slice_idx < end_slice ;slice_idx ++ ) { 1132size_t cur_start_byte = (std::max )(offset , (size_t )slice_idx * slice_size ); 1133size_t cur_end_byte = (std::min )(offset + size , (size_t ) (slice_idx + 1 )* slice_size ); 1134size_t slice_offset_start = cur_start_byte - (size_t )slice_idx * slice_size ; 1135size_t slice_offset_end = cur_end_byte - (size_t )slice_idx * slice_size ; 1136 1137int64_t start_row = slice_offset_start /row_size_bytes ; 1138int64_t end_row = (slice_offset_end + row_size_bytes - 1 ) /row_size_bytes ; 1139end_row = (std::min )(end_row ,ne1 ); 1140 1141int start_ct = start_row /32 ; 1142int end_ct = (end_row + 31 ) /32 ; 1143end_ct = (std::min )(end_ct ,n_col_tiles ); 1144 1145block_q8_0 * dst_slice = dst_matrix + (cur_start_byte - offset ) /sizeof (block_q8_0 ); 1146const uint8_t * matrix_src = (const uint8_t * )t -> data + slice_idx * matrix_size ; 1147 1148for (int ct = start_ct ;ct < end_ct ;ct ++ ) { 1149for (int kt = 0 ;kt < n_k_tiles ;kt ++ ) { 1150const uint8_t * tile_src = matrix_src + (ct * n_k_tiles + kt )* tile_size ; 1151 1152for (int cp = 0 ;cp < 16 ;cp ++ ) { 1153int col0 = cp * 2 ; 1154int col1 = col0 + 1 ; 1155for (int row = 0 ;row < 32 ;row ++ ) { 1156int64_t r = ct * 32 + row ; 1157if (r >=start_row && r < end_row && kt < ne0 /32 ) { 1158block_q8_0 & b = dst_slice [(r - start_row )* (ne0 /32 )+ kt ]; 1159b .qs [col0 ]= tile_src [cp * 64 + 2 * row + 0 ]; 1160b .qs [col1 ]= tile_src [cp * 64 + 2 * row + 1 ]; 1161 } 1162 } 1163 } 1164 1165const ggml_half * scale_src = (const ggml_half * )(tile_src + 1024 ); 1166for (int row = 0 ;row < 32 ;row ++ ) { 1167int64_t r = ct * 32 + row ; 1168if (r >=start_row && r < end_row && kt < ne0 /32 ) { 1169dst_slice [(r - start_row )* (ne0 /32 )+ kt ].d = scale_src [row ]; 1170 } 1171 } 1172 } 1173 } 1174 } 1175} 1176 1177// repack mxfp4 data into mxfp4_tiled tensor 1178static void repack_mxfp4_tiled (ggml_tensor * t ,const void * data ,size_t offset ,size_t size ) { 1179const block_mxfp4 * src_matrix = (const block_mxfp4 * )data ; 1180int64_t ne0 = t -> ne [0 ]; 1181int64_t ne1 = t -> ne [1 ]; 1182int64_t ne2 = t -> ne [2 ]; 1183int64_t ne3 = t -> ne [3 ]; 1184int64_t ne0_padded = hex_round_up (ne0 ,32 ); 1185int64_t ne1_padded = hex_round_up (ne1 ,32 ); 1186 1187int n_col_tiles = ne1_padded /32 ; 1188int n_k_tiles = ne0_padded /32 ; 1189const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_MXFP4 ; 1190const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size ; 1191 1192size_t slice_size = ne1 * ggml_row_size (t -> type ,ne0 ); 1193int64_t start_slice = offset /slice_size ; 1194int64_t end_slice = (offset + size + slice_size - 1 ) /slice_size ; 1195if (end_slice > ne2 * ne3 ) { 1196end_slice = ne2 * ne3 ; 1197 } 1198 1199for (int64_t slice_idx = start_slice ;slice_idx < end_slice ;slice_idx ++ ) { 1200const block_mxfp4 * src_slice = src_matrix + (slice_idx - start_slice )* (ne1 * (ne0 /32 )); 1201uint8_t * matrix_dst = (uint8_t * )t -> data + slice_idx * matrix_size ; 1202 1203for (int ct = 0 ;ct < n_col_tiles ;ct ++ ) { 1204for (int kt = 0 ;kt < n_k_tiles ;kt ++ ) { 1205uint8_t * tile_dst = matrix_dst + (ct * n_k_tiles + kt )* tile_size ; 1206 1207uint8_t tile_quants [32 ][32 ]; 1208for (int row = 0 ;row < 32 ;row ++ ) { 1209int64_t r = ct * 32 + row ; 1210if (r < ne1 && kt < ne0 /32 ) { 1211unpack_mxfp4_quants (tile_quants [row ],& src_slice [r * (ne0 /32 )+ kt ],0 ); 1212 }else { 1213memset (tile_quants [row ],0 ,32 ); 1214 } 1215 } 1216 1217for (int cp = 0 ;cp < 16 ;cp ++ ) { 1218for (int row = 0 ;row < 32 ;row ++ ) { 1219tile_dst [cp * 32 + row ]= (tile_quants [row ][2 * cp + 1 ] <<4 ) |tile_quants [row ][2 * cp ]; 1220 } 1221 } 1222 1223uint8_t * scale_dst = tile_dst + 512 ; 1224for (int row = 0 ;row < 32 ;row ++ ) { 1225int64_t r = ct * 32 + row ; 1226scale_dst [row ]= (r < ne1 && kt < ne0 /32 ) ?src_slice [r * (ne0 /32 )+ kt ].e :0 ; 1227 } 1228 } 1229 } 1230 } 1231} 1232 1233// repack mxfp4_tiled tensor into mxfp4 data 1234static void repack_tiled_mxfp4 (void * data ,const ggml_tensor * t ,size_t offset ,size_t size ) { 1235block_mxfp4 * dst_matrix = (block_mxfp4 * )data ; 1236int64_t ne0 = t -> ne [0 ]; 1237int64_t ne1 = t -> ne [1 ]; 1238int64_t ne2 = t -> ne [2 ]; 1239int64_t ne3 = t -> ne [3 ]; 1240int64_t ne0_padded = hex_round_up (ne0 ,32 ); 1241int64_t ne1_padded = hex_round_up (ne1 ,32 ); 1242 1243int n_col_tiles = ne1_padded /32 ; 1244int n_k_tiles = ne0_padded /32 ; 1245const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_MXFP4 ; 1246const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size ; 1247 1248size_t slice_size = ne1 * ggml_row_size (t -> type ,ne0 ); 1249size_t row_size_bytes = ggml_row_size (t -> type ,ne0 ); 1250int64_t start_slice = offset /slice_size ; 1251int64_t end_slice = (offset + size + slice_size - 1 ) /slice_size ; 1252if (end_slice > ne2 * ne3 ) { 1253end_slice = ne2 * ne3 ; 1254 } 1255 1256for (int64_t slice_idx = start_slice ;slice_idx < end_slice ;slice_idx ++ ) { 1257size_t cur_start_byte = (std::max )(offset , (size_t )slice_idx * slice_size ); 1258size_t cur_end_byte = (std::min )(offset + size , (size_t ) (slice_idx + 1 )* slice_size ); 1259size_t slice_offset_start = cur_start_byte - (size_t )slice_idx * slice_size ; 1260size_t slice_offset_end = cur_end_byte - (size_t )slice_idx * slice_size ; 1261 1262int64_t start_row = slice_offset_start /row_size_bytes ; 1263int64_t end_row = (slice_offset_end + row_size_bytes - 1 ) /row_size_bytes ; 1264end_row = (std::min )(end_row ,ne1 ); 1265 1266int start_ct = start_row /32 ; 1267int end_ct = (end_row + 31 ) /32 ; 1268end_ct = (std::min )(end_ct ,n_col_tiles ); 1269 1270block_mxfp4 * dst_slice = dst_matrix + (cur_start_byte - offset ) /sizeof (block_mxfp4 ); 1271const uint8_t * matrix_src = (const uint8_t * )t -> data + slice_idx * matrix_size ; 1272 1273for (int ct = start_ct ;ct < end_ct ;ct ++ ) { 1274for (int kt = 0 ;kt < n_k_tiles ;kt ++ ) { 1275const uint8_t * tile_src = matrix_src + (ct * n_k_tiles + kt )* tile_size ; 1276 1277uint8_t tile_quants [32 ][32 ]; 1278for (int cp = 0 ;cp < 16 ;cp ++ ) { 1279for (int row = 0 ;row < 32 ;row ++ ) { 1280uint8_t val = tile_src [cp * 32 + row ]; 1281tile_quants [row ][2 * cp + 0 ]= val & 0x0F ; 1282tile_quants [row ][2 * cp + 1 ]= val >>4 ; 1283 } 1284 } 1285 1286for (int row = 0 ;row < 32 ;row ++ ) { 1287int64_t r = ct * 32 + row ; 1288if (r >=start_row && r < end_row && kt < ne0 /32 ) { 1289pack_mxfp4_quants (& dst_slice [(r - start_row )* (ne0 /32 )+ kt ],tile_quants [row ],0 ); 1290 } 1291 } 1292 1293const uint8_t * scale_src = tile_src + 512 ; 1294for (int row = 0 ;row < 32 ;row ++ ) { 1295int64_t r = ct * 32 + row ; 1296if (r >=start_row && r < end_row && kt < ne0 /32 ) { 1297dst_slice [(r - start_row )* (ne0 /32 )+ kt ].e = scale_src [row ]; 1298 } 1299 } 1300 } 1301 } 1302 } 1303} 1304 1305static void repack_tensor_tiled (ggml_tensor * tensor ,const void * data ,size_t size ) { 1306switch (tensor -> type ) { 1307case GGML_TYPE_Q4_0 : 1308repack_q4_0_tiled (tensor ,data ,0 ,size ); 1309break ; 1310 1311case GGML_TYPE_Q4_1 : 1312repack_q4_1_tiled (tensor ,data ,0 ,size ); 1313break ; 1314 1315case GGML_TYPE_Q8_0 : 1316repack_q8_0_tiled (tensor ,data ,0 ,size ); 1317break ; 1318 1319case GGML_TYPE_IQ4_NL : 1320repack_q4_0_tiled (tensor ,data ,0 ,size ); 1321break ; 1322 1323case GGML_TYPE_MXFP4 : 1324repack_mxfp4_tiled (tensor ,data ,0 ,size ); 1325break ; 1326 1327default : 1328break ; 1329 } 1330} 1331 1332static void ggml_backend_hexagon_buffer_set_tensor (ggml_backend_buffer_t buffer , 1333ggml_tensor * tensor , 1334const void * data , 1335size_t offset , 1336size_t size ) { 1337auto extra = (ggml_hexagon_tensor_extra * )tensor -> extra ; 1338auto sbuf = (ggml_hexagon_shared_buffer * )buffer -> context ; 1339auto sess = sbuf -> sess ; 1340 1341if (ggml_backend_buffer_get_usage (buffer )== GGML_BACKEND_BUFFER_USAGE_WEIGHTS ) { 1342extra -> flags |=GGML_HEXAGON_TENSOR_WEIGHT ; 1343if (ggml_hexagon_is_repack_type (tensor -> type )) { 1344extra -> flags |=GGML_HEXAGON_TENSOR_REPACK ; 1345 } 1346 } 1347 1348HEX_VERBOSE ("ggml-hex: %s set-tensor %s : data %p offset %zu size %zu usage %d flags 0x%x\n" , 1349sess -> c_name (),tensor -> name ,data ,offset ,size , (int )buffer -> usage ,extra -> flags ); 1350 1351if ((extra -> flags & GGML_HEXAGON_TENSOR_REPACK )== 0 ) { 1352memcpy ((char * )tensor -> data + offset ,data ,size ); 1353return ; 1354 } 1355 1356if (offset == 0 && size == ggml_nbytes (tensor )&& extra -> shadow_buf .empty ()) { 1357repack_tensor_tiled (tensor ,data ,size ); 1358return ; 1359 } 1360 1361if (extra -> shadow_buf .size ()< ggml_nbytes (tensor )) { 1362extra -> shadow_buf .resize (ggml_nbytes (tensor )); 1363 } 1364memcpy (extra -> shadow_buf .data ()+ offset ,data ,size ); 1365extra -> shadow_size += size ; 1366 1367if (extra -> shadow_size >=ggml_nbytes (tensor )) { 1368repack_tensor_tiled (tensor ,extra -> shadow_buf .data (),extra -> shadow_buf .size ()); 1369extra -> shadow_buf .clear (); 1370extra -> shadow_buf .shrink_to_fit (); 1371extra -> shadow_size = 0 ; 1372 } 1373} 1374 1375static void ggml_backend_hexagon_buffer_get_tensor (ggml_backend_buffer_t buffer , 1376const ggml_tensor * tensor , 1377void * data , 1378size_t offset , 1379size_t size ) { 1380auto extra = (ggml_hexagon_tensor_extra * )tensor -> extra ; 1381auto sbuf = (ggml_hexagon_shared_buffer * )buffer -> context ; 1382auto sess = sbuf -> sess ; 1383 1384HEX_VERBOSE ("ggml-hex: %s get-tensor %s : data %p offset %zu size %zu usage %d flags 0x%x\n" , 1385sess -> c_name (),tensor -> name ,data ,offset ,size , (int )buffer -> usage ,extra -> flags ); 1386 1387if ((extra -> flags & GGML_HEXAGON_TENSOR_REPACK )== 0 ) { 1388memcpy (data , (const char * )tensor -> data + offset ,size ); 1389return ; 1390 } 1391 1392switch (tensor -> type ) { 1393case GGML_TYPE_Q4_0 : 1394GGML_ASSERT (offset == 0 ); 1395GGML_ASSERT (offset + size <=ggml_nbytes (tensor )); 1396repack_tiled_q4_0 (data ,tensor ,offset ,size ); 1397break ; 1398 1399case GGML_TYPE_Q4_1 : 1400GGML_ASSERT (offset == 0 ); 1401GGML_ASSERT (offset + size <=ggml_nbytes (tensor )); 1402repack_tiled_q4_1 (data ,tensor ,offset ,size ); 1403break ; 1404 1405case GGML_TYPE_Q8_0 : 1406GGML_ASSERT (offset == 0 ); 1407GGML_ASSERT (offset + size <=ggml_nbytes (tensor )); 1408repack_tiled_q8_0 (data ,tensor ,offset ,size ); 1409break ; 1410 1411case GGML_TYPE_IQ4_NL : 1412GGML_ASSERT (offset == 0 ); 1413GGML_ASSERT (offset + size <=ggml_nbytes (tensor )); 1414repack_tiled_q4_0 (data ,tensor ,offset ,size ); 1415break ; 1416 1417case GGML_TYPE_MXFP4 : 1418GGML_ASSERT (offset == 0 ); 1419GGML_ASSERT (offset + size <=ggml_nbytes (tensor )); 1420repack_tiled_mxfp4 (data ,tensor ,offset ,size ); 1421break ; 1422 1423default : 1424memcpy (data , (const char * )tensor -> data + offset ,size ); 1425break ; 1426 } 1427} 1428 1429static bool ggml_backend_hexagon_buffer_cpy_tensor (ggml_backend_buffer_t buffer , 1430const struct ggml_tensor * src , 1431struct ggml_tensor * dst ) { 1432// we might optimize this later, for now take the slow path (ie get/set_tensor) 1433return false; 1434 1435GGML_UNUSED (buffer ); 1436GGML_UNUSED (src ); 1437GGML_UNUSED (dst ); 1438} 1439 1440static void ggml_backend_hexagon_buffer_set_tensor_2d (ggml_backend_buffer_t buffer , 1441ggml_tensor * tensor , 1442const void * data , 1443size_t offset , 1444size_t size , 1445size_t n_copies , 1446size_t stride_tensor , 1447size_t stride_data ) { 1448auto extra = (ggml_hexagon_tensor_extra * )tensor -> extra ; 1449auto sbuf = (ggml_hexagon_shared_buffer * )buffer -> context ; 1450auto sess = sbuf -> sess ; 1451 1452if (ggml_backend_buffer_get_usage (buffer )== GGML_BACKEND_BUFFER_USAGE_WEIGHTS ) { 1453extra -> flags |=GGML_HEXAGON_TENSOR_WEIGHT ; 1454if (ggml_hexagon_is_repack_type (tensor -> type )) { 1455extra -> flags |=GGML_HEXAGON_TENSOR_REPACK ; 1456 } 1457 } 1458 1459HEX_VERBOSE ("ggml-hex: %s set-tensor-2d %s : data %p offset %zu size %zu n_copies %zu stride_tensor %zu stride_data %zu usage %d flags 0x%x\n" , 1460sess -> c_name (),tensor -> name ,data ,offset ,size ,n_copies ,stride_tensor ,stride_data , (int )buffer -> usage ,extra -> flags ); 1461 1462if ((extra -> flags & GGML_HEXAGON_TENSOR_REPACK )== 0 ) { 1463for (size_t i = 0 ;i < n_copies ;i ++ ) { 1464memcpy ((uint8_t * )tensor -> data + offset + i * stride_tensor , (const uint8_t * )data + i * stride_data ,size ); 1465 } 1466return ; 1467 } 1468 1469if (extra -> shadow_buf .size ()< ggml_nbytes (tensor )) { 1470extra -> shadow_buf .resize (ggml_nbytes (tensor )); 1471 } 1472for (size_t i = 0 ;i < n_copies ;i ++ ) { 1473memcpy (extra -> shadow_buf .data ()+ offset + i * stride_tensor , (const uint8_t * )data + i * stride_data ,size ); 1474 } 1475extra -> shadow_size += n_copies * size ; 1476 1477if (extra -> shadow_size >=ggml_nbytes (tensor )) { 1478repack_tensor_tiled (tensor ,extra -> shadow_buf .data (),extra -> shadow_buf .size ()); 1479extra -> shadow_buf .clear (); 1480extra -> shadow_buf .shrink_to_fit (); 1481extra -> shadow_size = 0 ; 1482 } 1483} 1484 1485static void ggml_backend_hexagon_buffer_get_tensor_2d (ggml_backend_buffer_t buffer , 1486const ggml_tensor * tensor , 1487void * data , 1488size_t offset , 1489size_t size , 1490size_t n_copies , 1491size_t stride_tensor , 1492size_t stride_data ) { 1493auto extra = (ggml_hexagon_tensor_extra * )tensor -> extra ; 1494auto sbuf = (ggml_hexagon_shared_buffer * )buffer -> context ; 1495auto sess = sbuf -> sess ; 1496 1497HEX_VERBOSE ("ggml-hex: %s get-tensor-2d %s : data %p offset %zu size %zu n_copies %zu stride_tensor %zu stride_data %zu usage %d\n" , 1498sess -> c_name (),tensor -> name ,data ,offset ,size ,n_copies ,stride_tensor ,stride_data , (int )buffer -> usage ); 1499 1500if ((extra -> flags & GGML_HEXAGON_TENSOR_REPACK )== 0 ) { 1501for (size_t i = 0 ;i < n_copies ;i ++ ) { 1502memcpy ((uint8_t * )data + i * stride_data , (const uint8_t * )tensor -> data + offset + i * stride_tensor ,size ); 1503 } 1504return ; 1505 } 1506 1507size_t temp_size = n_copies > 0 ? (n_copies - 1 )* stride_tensor + size :0 ; 1508size_t slice_size = tensor -> ne [1 ]* ggml_row_size (tensor -> type ,tensor -> ne [0 ]); 1509size_t slice_offset = offset %slice_size ; 1510size_t row_size_bytes = ggml_row_size (tensor -> type ,tensor -> ne [0 ]); 1511 1512GGML_ASSERT ((slice_offset %row_size_bytes )== 0 && "offset must be aligned to row boundary" ); 1513GGML_ASSERT ((temp_size %row_size_bytes )== 0 && "temp_size must be a multiple of row size" ); 1514GGML_ASSERT ((slice_offset /row_size_bytes ) %32 == 0 && "offset must be aligned to tile size (32 rows)" ); 1515GGML_ASSERT ((offset + temp_size ) <=ggml_nbytes (tensor )); 1516 1517 std::vector < uint8_t > temp_buf (temp_size ); 1518 1519switch (tensor -> type ) { 1520case GGML_TYPE_Q4_0 : 1521repack_tiled_q4_0 (temp_buf .data (),tensor ,offset ,temp_size ); 1522break ; 1523 1524case GGML_TYPE_Q4_1 : 1525repack_tiled_q4_1 (temp_buf .data (),tensor ,offset ,temp_size ); 1526break ; 1527 1528case GGML_TYPE_Q8_0 : 1529repack_tiled_q8_0 (temp_buf .data (),tensor ,offset ,temp_size ); 1530break ; 1531 1532case GGML_TYPE_IQ4_NL : 1533repack_tiled_q4_0 (temp_buf .data (),tensor ,offset ,temp_size ); 1534break ; 1535 1536case GGML_TYPE_MXFP4 : 1537repack_tiled_mxfp4 (temp_buf .data (),tensor ,offset ,temp_size ); 1538break ; 1539 1540default : 1541memcpy (temp_buf .data (), (const uint8_t * )tensor -> data + offset ,temp_size ); 1542break ; 1543 } 1544 1545for (size_t i = 0 ;i < n_copies ;i ++ ) { 1546memcpy ((uint8_t * )data + i * stride_data ,temp_buf .data ()+ i * stride_tensor ,size ); 1547 } 1548} 1549 1550static void ggml_backend_hexagon_buffer_clear (ggml_backend_buffer_t buffer ,uint8_t value ) { 1551auto sbuf = (ggml_hexagon_shared_buffer * )buffer -> context ; 1552auto sess = sbuf -> sess ; 1553HEX_VERBOSE ("ggml-hex: %s clear-buff base %p size %zu\n" ,sess -> c_name (), (void * )sbuf -> base (),sbuf -> size ()); 1554memset (sbuf -> base (),value ,sbuf -> size ()); 1555} 1556 1557static ggml_backend_buffer_i ggml_backend_hexagon_buffer_interface = { 1558/* .free_buffer = */ ggml_backend_hexagon_buffer_free_buffer , 1559/* .get_base = */ ggml_backend_hexagon_buffer_get_base , 1560/* .init_tensor = */ ggml_backend_hexagon_buffer_init_tensor , 1561/* .memset_tensor = */ NULL , 1562/* .set_tensor = */ ggml_backend_hexagon_buffer_set_tensor , 1563/* .get_tensor = */ ggml_backend_hexagon_buffer_get_tensor , 1564/* .set_tensor_2d = */ ggml_backend_hexagon_buffer_set_tensor_2d , 1565/* .get_tensor_2d = */ ggml_backend_hexagon_buffer_get_tensor_2d , 1566/* .cpy_tensor = */ ggml_backend_hexagon_buffer_cpy_tensor , 1567/* .clear = */ ggml_backend_hexagon_buffer_clear , 1568/* .reset = */ NULL , 1569}; 1570 1571// ** backend buffer type 1572 1573static void ggml_backend_hexagon_host_buffer_set_tensor (ggml_backend_buffer_t buffer , 1574ggml_tensor * tensor , 1575const void * data , 1576size_t offset , 1577size_t size ) { 1578memcpy ((char * )tensor -> data + offset ,data ,size ); 1579GGML_UNUSED (buffer ); 1580} 1581 1582static void ggml_backend_hexagon_host_buffer_get_tensor (ggml_backend_buffer_t buffer , 1583const ggml_tensor * tensor , 1584void * data , 1585size_t offset , 1586size_t size ) { 1587memcpy (data , (const char * )tensor -> data + offset ,size ); 1588GGML_UNUSED (buffer ); 1589} 1590 1591static ggml_backend_buffer_i ggml_backend_hexagon_host_buffer_interface = { 1592/* .free_buffer = */ ggml_backend_hexagon_buffer_free_buffer , 1593/* .get_base = */ ggml_backend_hexagon_buffer_get_base , 1594/* .init_tensor = */ ggml_backend_hexagon_buffer_init_tensor , 1595/* .memset_tensor = */ NULL , 1596/* .set_tensor = */ ggml_backend_hexagon_host_buffer_set_tensor , 1597/* .get_tensor = */ ggml_backend_hexagon_host_buffer_get_tensor , 1598/* .set_tensor_2d = */ NULL , 1599/* .get_tensor_2d = */ NULL , 1600/* .cpy_tensor = */ ggml_backend_hexagon_buffer_cpy_tensor , 1601/* .clear = */ ggml_backend_hexagon_buffer_clear , 1602/* .reset = */ NULL , 1603}; 1604 1605// ** backend buffer type 1606 1607static const char * ggml_backend_hexagon_buffer_type_name (ggml_backend_buffer_type_t buffer_type ) { 1608return static_cast < ggml_backend_hexagon_buffer_type_context *> (buffer_type -> context )-> name .c_str (); 1609} 1610 1611static ggml_backend_buffer_t ggml_backend_hexagon_buffer_type_alloc_buffer ( 1612ggml_backend_buffer_type_t buffer_type ,size_t size ) { 1613auto dev_ctx = static_cast < ggml_backend_hexagon_buffer_type_context *> (buffer_type -> context )-> dev_ctx ; 1614auto sess = dev_ctx -> session (); 1615try { 1616ggml_hexagon_shared_buffer * sbuf = new ggml_hexagon_shared_buffer (sess ,size , false); 1617return ggml_backend_buffer_init (buffer_type ,ggml_backend_hexagon_buffer_interface ,sbuf ,size ); 1618 }catch (const std::exception & exc ) { 1619GGML_LOG_ERROR ("ggml-hex: %s failed to allocate device buffer context: %s\n" ,dev_ctx -> c_name (),exc .what ()); 1620return nullptr ; 1621 } 1622} 1623 1624static ggml_backend_buffer_t ggml_backend_hexagon_host_buffer_type_alloc_buffer ( 1625ggml_backend_buffer_type_t buffer_type ,size_t size ) { 1626auto dev_ctx = static_cast < ggml_backend_hexagon_buffer_type_context *> (buffer_type -> context )-> dev_ctx ; 1627auto sess = dev_ctx -> session (); 1628try { 1629ggml_hexagon_shared_buffer * sbuf = new ggml_hexagon_shared_buffer (sess ,size , false); 1630return ggml_backend_buffer_init (buffer_type ,ggml_backend_hexagon_host_buffer_interface ,sbuf ,size ); 1631 }catch (const std::exception & exc ) { 1632GGML_LOG_ERROR ("ggml-hex: %s failed to allocate host buffer context: %s\n" ,dev_ctx -> c_name (),exc .what ()); 1633return nullptr ; 1634 } 1635} 1636 1637static size_t ggml_backend_hexagon_buffer_type_get_alignment (ggml_backend_buffer_type_t buft ) { 1638return 128 ;// HVX alignment 1639GGML_UNUSED (buft ); 1640} 1641 1642static size_t ggml_backend_hexagon_buffer_type_get_alloc_size (ggml_backend_buffer_type_t buft ,const struct ggml_tensor * t ) { 1643if (ggml_hexagon_is_repack_type (t -> type )) { 1644int64_t ne0 = hex_round_up (t -> ne [0 ],32 ); 1645int64_t ne1 = hex_round_up (t -> ne [1 ],32 ); 1646int64_t ne2 = t -> ne [2 ]; 1647int64_t ne3 = t -> ne [3 ]; 1648return ggml_row_size (t -> type ,ne0 )* ne1 * ne2 * ne3 ; 1649 } 1650return ggml_nbytes (t ); 1651 1652GGML_UNUSED (buft ); 1653} 1654 1655static size_t ggml_backend_hexagon_buffer_type_get_max_size (ggml_backend_buffer_type_t buft ) { 1656auto * context = static_cast < ggml_backend_hexagon_buffer_type_context *> (buft -> context ); 1657return context -> dev_ctx -> max_bufsize ; 1658} 1659 1660static bool ggml_backend_hexagon_buffer_type_is_host (ggml_backend_buffer_type_t buft ) { 1661return false; 1662GGML_UNUSED (buft ); 1663} 1664 1665static bool ggml_backend_hexagon_host_buffer_type_is_host (ggml_backend_buffer_type_t buft ) { 1666return true; 1667GGML_UNUSED (buft ); 1668} 1669 1670static ggml_backend_buffer_type_i ggml_backend_hexagon_buffer_type_interface = { 1671/* .get_name = */ ggml_backend_hexagon_buffer_type_name , 1672/* .alloc_buffer = */ ggml_backend_hexagon_buffer_type_alloc_buffer , 1673/* .get_alignment = */ ggml_backend_hexagon_buffer_type_get_alignment , 1674/* .get_max_size = */ ggml_backend_hexagon_buffer_type_get_max_size , 1675/* .get_alloc_size = */ ggml_backend_hexagon_buffer_type_get_alloc_size , 1676/* .is_host = */ ggml_backend_hexagon_buffer_type_is_host , 1677}; 1678 1679static ggml_backend_buffer_type_i ggml_backend_hexagon_host_buffer_type_interface = { 1680/* .get_name = */ ggml_backend_hexagon_buffer_type_name , 1681/* .alloc_buffer = */ ggml_backend_hexagon_host_buffer_type_alloc_buffer , 1682/* .get_alignment = */ ggml_backend_hexagon_buffer_type_get_alignment , 1683/* .get_max_size = */ ggml_backend_hexagon_buffer_type_get_max_size , 1684/* .get_alloc_size = */ ggml_backend_hexagon_buffer_type_get_alloc_size , 1685/* .is_host = */ ggml_backend_hexagon_host_buffer_type_is_host , 1686}; 1687 1688ggml_backend_hexagon_device_context::ggml_backend_hexagon_device_context (int dev_id ,const ggml_hexagon_device_config & config ,ggml_backend_dev_t dev ) 1689 :dev_id (dev_id ),config (config ),dev (dev ),max_bufsize (opt_mbuf ) { 1690buffer_type .device = dev ; 1691buffer_type .iface = ggml_backend_hexagon_buffer_type_interface ; 1692buffer_type .context = new ggml_backend_hexagon_buffer_type_context (config .name ,this ); 1693 1694host_buffer_type .device = dev ; 1695host_buffer_type .iface = ggml_backend_hexagon_host_buffer_type_interface ; 1696host_buffer_type .context = new ggml_backend_hexagon_buffer_type_context (config .name + "-HOST" ,this ); 1697 1698fence_buffer_type .device = dev ; 1699fence_buffer_type .iface = ggml_backend_hexagon_buffer_type_interface ; 1700fence_buffer_type .context = new ggml_backend_hexagon_buffer_type_context (config .name + "-FENCE" ,this ); 1701} 1702 1703ggml_backend_hexagon_device_context::~ggml_backend_hexagon_device_context () { 1704delete static_cast < ggml_backend_hexagon_buffer_type_context *> (buffer_type .context ); 1705delete static_cast < ggml_backend_hexagon_buffer_type_context *> (host_buffer_type .context ); 1706delete static_cast < ggml_backend_hexagon_buffer_type_context *> (fence_buffer_type .context ); 1707} 1708 1709static bool ggml_backend_buffer_is_hexagon (const struct ggml_backend_buffer * b ) { 1710return b -> buft -> iface .get_alignment == ggml_backend_hexagon_buffer_type_get_alignment ; 1711} 1712 1713struct ggml_hexagon_opbatch { 1714ggml_hexagon_session * sess ; 1715 1716 std::vector < htp_opnode > ops ;// htp_opnode of ops 1717 1718 std::vector < htp_buf_desc > h_bufs ;// htp buffer descriptors 1719 std::vector < htp_tensor > h_tens ;// htp tensor descriptors 1720 std::vector < htp_op_desc > h_ops ;// htp op descriptors 1721 1722 std::unordered_map < int ,int > b_map ;// buffer fd to index 1723 std::unordered_map < const ggml_tensor * ,int > t_map ;// tensor ptr to index 1724 std::unordered_multimap < void * ,int > d_map ;// tensor data to index 1725 1726unsigned int n_bufs ;// num buffers in the batch 1727unsigned int n_tens ;// num tensors ... 1728unsigned int n_ops ;// num ops ... 1729size_t b_vmem ;// sum of all buffer sizes 1730 1731unsigned int n_bufs_max ; 1732unsigned int n_tens_max ; 1733unsigned int n_ops_max ; 1734size_t b_vmem_max ; 1735 1736void reset () { 1737n_bufs = 0 ; 1738n_tens = 0 ; 1739n_ops = 0 ; 1740b_vmem = 0 ; 1741 1742b_map .clear (); 1743t_map .clear (); 1744d_map .clear (); 1745ops .resize (n_ops_max ); 1746 } 1747 1748ggml_hexagon_opbatch (ggml_hexagon_session * sess ,size_t batch_size ,size_t max_vmem ) { 1749this -> sess = sess ; 1750 1751n_bufs_max = HTP_OP_MAX_BUFS ; 1752n_ops_max = batch_size ; 1753n_tens_max = std::min < size_t > (n_ops_max + n_ops_max * HTP_OP_MAX_INPUTS ,HTP_OP_MAX_TENSORS ); 1754 1755b_vmem_max = max_vmem ; 1756 1757ops .resize (n_ops_max ); 1758 1759h_bufs .resize (n_bufs_max ); 1760h_tens .resize (n_tens_max ); 1761h_ops .resize (n_ops_max ); 1762 1763b_map .reserve (n_bufs_max ); 1764t_map .reserve (n_tens_max ); 1765d_map .reserve (n_tens_max ); 1766 1767GGML_LOG_INFO ("ggml-hex: %s op batching: n-bufs %u n-tensors %u n-ops %u vmem %zu\n" , 1768sess -> c_name (),n_bufs_max ,n_tens_max ,n_ops_max ,b_vmem_max ); 1769 1770reset (); 1771 } 1772 1773bool empty ()const {return n_ops == 0 ; } 1774 1775// add buffer and return its index 1776int add_buffer (ggml_hexagon_shared_buffer * sbuf ) { 1777// Lookup by fd 1778auto it = b_map .find (sbuf -> fd ()); 1779if (it != b_map .end ()) {return it -> second ; } 1780 1781// Add new buffer to the batch 1782GGML_ASSERT (n_bufs < HTP_OP_MAX_BUFS ); 1783int bi = n_bufs ++ ; 1784 1785b_map .insert ({sbuf -> fd (),bi }); 1786 1787htp_buf_desc & b = h_bufs [bi ]; 1788b .base = (uint64_t )sbuf -> base (); 1789b .fd = sbuf -> fd (); 1790b .size = sbuf -> size (); 1791 1792b_vmem += b .size ; 1793 1794HEX_VERBOSE ("ggml-hex: %s add-buffer #%u : fd %d base %p size %zu : vmem %zu\n" ,sess -> c_name (),bi ,b .fd , (void * )sbuf -> base (), (size_t )b .size ,b_vmem ); 1795 1796return bi ; 1797 } 1798 1799bool same_shape (const htp_tensor * h ,const ggml_tensor * t )const { 1800auto extra = (ggml_hexagon_tensor_extra * )t -> extra ; 1801 1802int64_t ne0 = t -> ne [0 ]; 1803int64_t ne1 = t -> ne [1 ]; 1804const bool is_repack = (extra -> flags & GGML_HEXAGON_TENSOR_REPACK )!= 0 ; 1805if (is_repack ) { 1806ne0 = hex_round_up (ne0 ,32 ); 1807ne1 = hex_round_up (ne1 ,32 ); 1808 } 1809int64_t nb1 = is_repack ?ggml_row_size (t -> type ,ne0 ) :t -> nb [1 ]; 1810int64_t nb2 = is_repack ?nb1 * ne1 :t -> nb [2 ]; 1811int64_t nb3 = is_repack ?nb2 * t -> ne [2 ] :t -> nb [3 ]; 1812 1813return (h -> type == t -> type )&& 1814 (h -> ne [0 ]== ne0 )&& (h -> ne [1 ]== ne1 )&& (h -> ne [2 ]== t -> ne [2 ])&& (h -> ne [3 ]== t -> ne [3 ])&& 1815 (h -> nb [0 ]== t -> nb [0 ])&& (h -> nb [1 ]== nb1 )&& (h -> nb [2 ]== nb2 )&& (h -> nb [3 ]== nb3 ); 1816 } 1817 1818// add tensor and return its index 1819int add_tensor (const ggml_tensor * t ) { 1820auto extra = (ggml_hexagon_tensor_extra * )t -> extra ; 1821auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (t -> buffer -> context ); 1822 1823// First lookup by tensor data 1824auto range = d_map .equal_range (t -> data ); 1825for (auto it = range .first ;it != range .second ;++ it ) { 1826htp_tensor * h = & h_tens [it -> second ]; 1827if (same_shape (h ,t )) {return it -> second ; } 1828 } 1829 1830// Lookup by tensor ptr 1831auto it = t_map .find (t ); 1832if (it != t_map .end ()) {return it -> second ; } 1833 1834// Add new tensor to the batch 1835int ti = n_tens ++ ; 1836GGML_ASSERT (n_tens <=n_tens_max ); 1837 1838t_map .insert ({t ,ti }); 1839d_map .insert ({t -> data ,ti }); 1840 1841uint64_t t_offset = (uint8_t * )t -> data - sbuf -> base (); 1842size_t t_size = ggml_nbytes (t ); 1843 1844htp_tensor & h = h_tens [ti ]; 1845h .bi = add_buffer (sbuf ); 1846h .ti = ti ; 1847h .data = t_offset ; 1848h .type = t -> type ; 1849 1850const bool is_repack = (extra -> flags & GGML_HEXAGON_TENSOR_REPACK )!= 0 ; 1851if (is_repack ) { 1852h .ne [0 ]= hex_round_up (t -> ne [0 ],32 ); 1853h .ne [1 ]= hex_round_up (t -> ne [1 ],32 ); 1854h .ne [2 ]= t -> ne [2 ]; 1855h .ne [3 ]= t -> ne [3 ]; 1856 1857h .nb [0 ]= t -> nb [0 ]; 1858h .nb [1 ]= ggml_row_size (t -> type ,h .ne [0 ]); 1859h .nb [2 ]= h .nb [1 ]* h .ne [1 ]; 1860h .nb [3 ]= h .nb [2 ]* h .ne [2 ]; 1861h .size = h .nb [3 ]* h .ne [3 ]; 1862t_size = h .size ; 1863 }else { 1864h .size = t_size ; 1865h .ne [0 ]= t -> ne [0 ];h .ne [1 ]= t -> ne [1 ];h .ne [2 ]= t -> ne [2 ];h .ne [3 ]= t -> ne [3 ]; 1866h .nb [0 ]= t -> nb [0 ];h .nb [1 ]= t -> nb [1 ];h .nb [2 ]= t -> nb [2 ];h .nb [3 ]= t -> nb [3 ]; 1867 } 1868 1869h .flags = 0 ; 1870if ((extra -> flags & GGML_HEXAGON_TENSOR_WEIGHT )!= 0 ) { 1871h .flags |=HTP_TENSOR_WEIGHT ; 1872 } 1873if ((extra -> flags & GGML_HEXAGON_TENSOR_REPACK )!= 0 ) { 1874h .flags |=HTP_TENSOR_REPACK ; 1875 } 1876if ((extra -> flags & GGML_HEXAGON_TENSOR_FENCE )!= 0 ) { 1877h .flags |=HTP_TENSOR_FENCE ; 1878 } 1879 1880HEX_VERBOSE ("ggml-hex: %s add-tensor #%u %s : bi %d data %p offset %zu size %zu flags 0x%x : %zu:%zu:%zu:%zu\n" ,sess -> c_name (), 1881ti ,t -> name ,h .bi , (void * )t -> data , (size_t )t_offset ,t_size ,h .flags , 1882 (size_t )h .ne [0 ], (size_t )h .ne [1 ], (size_t )h .ne [2 ], (size_t )h .ne [3 ]); 1883 1884return ti ; 1885 } 1886 1887bool fit_op (const htp_opnode & node )const { 1888if (n_ops >=n_ops_max )return false; 1889 1890// check how much extras we will need 1891size_t extra_bufs = 0 ; 1892size_t extra_vmem = 0 ; 1893size_t extra_tens = 0 ; 1894 1895auto fit_tensor = [& ](const ggml_tensor * t ) { 1896if (!t )return ; 1897if (!t_map .count (t )) { 1898extra_tens ++ ; 1899 1900auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (t -> buffer -> context ); 1901if (!b_map .count (sbuf -> fd ())) { 1902extra_vmem += sbuf -> size (); 1903extra_bufs += 1 ; 1904 } 1905 } 1906 }; 1907 1908for (const auto * src :node .get_inputs ()) { 1909fit_tensor (src ); 1910 } 1911for (const auto * output :node .get_outputs ()) { 1912fit_tensor (output ); 1913 } 1914 1915if ((extra_bufs + n_bufs )> n_bufs_max )return false; 1916if ((extra_tens + n_tens )> n_tens_max )return false; 1917if ((extra_vmem + b_vmem )> b_vmem_max )return false; 1918 1919return true; 1920 } 1921 1922// assumes that fit_op() was called first and returned true 1923void add_op (const htp_opnode & node ) { 1924// Add new op 1925 1926unsigned int n = n_ops ++ ; 1927GGML_ASSERT (n_ops <=n_ops_max ); 1928 1929ops [n ]= node ; 1930 1931htp_op_desc & o = h_ops [n ]; 1932memcpy (o .params ,node .node -> op_params ,sizeof (node .node -> op_params )); 1933memcpy (o .kernel_params ,node .kernel_params ,sizeof (o .kernel_params )); 1934o .opcode = node .opcode ; 1935o .flags = 0 ; 1936 1937ggml_hexagon_dump_op_exec (sess -> c_name (),ops [n ],o .flags ); 1938 1939auto inputs = node .get_inputs (); 1940for (unsigned int i = 0 ;i < HTP_OP_MAX_INPUTS ;i ++ ) { 1941o .src [i ]= (i < inputs .size ()&& inputs [i ]) ?add_tensor (inputs [i ]) :0xffff ; 1942 } 1943 1944auto outputs = node .get_outputs (); 1945for (unsigned int i = 0 ;i < HTP_OP_MAX_OUTPUTS ;i ++ ) { 1946o .dst [i ]= (i < outputs .size ()&& outputs [i ]) ?add_tensor (outputs [i ]) :0xffff ; 1947 } 1948 } 1949 1950void sort_buffers () { 1951if (n_bufs <=1 )return ; 1952 1953 std::vector < int > order (n_bufs ); 1954for (unsigned int i = 0 ;i < n_bufs ;i ++ ) {order [i ]= (int )i ; } 1955 1956 std::stable_sort (order .begin (),order .end (), [& ](int a ,int b ) { 1957return h_bufs [a ].size > h_bufs [b ].size ; 1958 }); 1959 1960bool already_sorted = true; 1961for (unsigned int i = 0 ;i < n_bufs ;i ++ ) { 1962if (order [i ]!= (int )i ) { 1963already_sorted = false; 1964break ; 1965 } 1966 } 1967if (already_sorted )return ; 1968 1969 std::vector < uint16_t > remap (n_bufs ); 1970 std::vector < htp_buf_desc > sorted_bufs (n_bufs ); 1971for (unsigned int new_bi = 0 ;new_bi < n_bufs ;new_bi ++ ) { 1972int old_bi = order [new_bi ]; 1973remap [old_bi ]= (uint16_t )new_bi ; 1974sorted_bufs [new_bi ]= h_bufs [old_bi ]; 1975 } 1976 1977for (unsigned int i = 0 ;i < n_bufs ;i ++ ) { 1978h_bufs [i ]= sorted_bufs [i ]; 1979 } 1980 1981for (unsigned int i = 0 ;i < n_tens ;i ++ ) { 1982h_tens [i ].bi = remap [h_tens [i ].bi ]; 1983 } 1984 } 1985 1986void update_mdev_group (uint32_t mdev_idx ) { 1987if (n_ops > 0 && h_ops [0 ].opcode == HTP_OP_MDEV_GROUP ) { 1988h_ops [0 ].params [0 ]= (int32_t )mdev_idx ; 1989 } 1990 } 1991 1992bool try_fuse_allreduce_add (const htp_opnode & node ) { 1993if (n_ops == 0 || opt_ar_select != 2 )return false; 1994if (node .opcode != HTP_OP_ADD )return false; 1995 1996htp_opnode & last_node = ops [n_ops - 1 ]; 1997if (last_node .opcode != HTP_OP_ALLREDUCE )return false; 1998 1999auto * ar_kparams = (struct htp_allreduce_kernel_params * )last_node .kernel_params ; 2000const uint32_t rank = (uint32_t )ar_kparams -> rank ; 2001const uint32_t n_ranks = (uint32_t )ar_kparams -> n_ranks ; 2002const ggml_tensor * ar_local = last_node .inputs [rank ]; 2003const ggml_tensor * add_src0 = node .src0 (); 2004const ggml_tensor * add_src1 = node .src1 (); 2005const ggml_tensor * add_dst = node .dst (); 2006 2007if (!ggml_hexagon_tensor_is_fuseable (ar_local ))return false; 2008 2009const ggml_tensor * res_tensor ; 2010if (add_src0 == ar_local || add_src0 -> data == ar_local -> data ) { 2011res_tensor = add_src1 ; 2012 }else if (add_src1 == ar_local || add_src1 -> data == ar_local -> data ) { 2013res_tensor = add_src0 ; 2014 }else { 2015return false; 2016 } 2017 2018if (ar_local -> type != res_tensor -> type )return false; 2019 2020const bool is_same_shape = (ar_local -> ne [0 ]== res_tensor -> ne [0 ]&& ar_local -> ne [1 ]== res_tensor -> ne [1 ]&& 2021ar_local -> ne [2 ]== res_tensor -> ne [2 ]&& ar_local -> ne [3 ]== res_tensor -> ne [3 ]); 2022const bool is_row_bcast = !is_same_shape && (ar_local -> ne [0 ]== res_tensor -> ne [0 ]&& res_tensor -> ne [1 ]== 1 && 2023res_tensor -> ne [2 ]== 1 && res_tensor -> ne [3 ]== 1 ); 2024 2025if (!is_same_shape && !is_row_bcast )return false; 2026 2027if (is_same_shape ) { 2028if (ar_local -> nb [1 ]!= res_tensor -> nb [1 ]|| ar_local -> nb [2 ]!= res_tensor -> nb [2 ]|| 2029ar_local -> nb [3 ]!= res_tensor -> nb [3 ]) { 2030return false; 2031 } 2032if (ggml_is_contiguous (ar_local )!= ggml_is_contiguous (res_tensor )) { 2033return false; 2034 } 2035 } 2036if (ggml_is_contiguous (ar_local )!= ggml_is_contiguous (add_dst )) { 2037return false; 2038 } 2039 2040for (uint32_t r = 0 ;r < n_ranks ;r ++ ) { 2041const ggml_tensor * ar_src = last_node .inputs [r ]; 2042if (ggml_hexagon_tensors_overlap (add_dst ,ar_src )) { 2043HEX_VERBOSE ("ggml-hex: %s skip ALLREDUCE_ADD fusion: dst overlaps allreduce src %u\n" ,sess -> c_name (),r ); 2044return false; 2045 } 2046 } 2047 2048struct htp_allreduce_kernel_params new_kparams ; 2049if (!ggml_hexagon_precompute_allreduce_params ( 2050sess ,add_dst , (uint32_t )ar_kparams -> rank , (uint32_t )ar_kparams -> n_ranks , true,is_row_bcast ,& new_kparams 2051 )) { 2052HEX_VERBOSE ("ggml-hex: %s skip ALLREDUCE_ADD fusion: solver failed\n" ,sess -> c_name ()); 2053return false; 2054 } 2055 2056size_t extra_bufs = 0 ,extra_vmem = 0 ,extra_tens = 0 ; 2057auto fit_t = [& ](const ggml_tensor * t ) { 2058if (!t_map .count (t )) { 2059extra_tens ++ ; 2060auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (t -> buffer -> context ); 2061if (!b_map .count (sbuf -> fd ())) { 2062extra_vmem += sbuf -> size (); 2063extra_bufs += 1 ; 2064 } 2065 } 2066 }; 2067fit_t (res_tensor ); 2068fit_t (add_dst ); 2069if ((extra_bufs + n_bufs )> n_bufs_max || (extra_tens + n_tens )> n_tens_max || (extra_vmem + b_vmem )> b_vmem_max ) { 2070return false; 2071 } 2072 2073last_node .opcode = HTP_OP_ALLREDUCE_ADD ; 2074last_node .name = "ALLREDUCE+ADD" ; 2075last_node .inputs .push_back (res_tensor ); 2076last_node .outputs .clear (); 2077last_node .outputs .push_back (add_dst ); 2078last_node .fused .push_back (node .node ); 2079memcpy (last_node .kernel_params ,& new_kparams ,sizeof (new_kparams )); 2080 2081htp_op_desc & o = h_ops [n_ops - 1 ]; 2082o .opcode = HTP_OP_ALLREDUCE_ADD ; 2083memcpy (o .kernel_params ,& new_kparams ,sizeof (new_kparams )); 2084 2085o .src [2 * n_ranks ]= add_tensor (res_tensor ); 2086o .dst [0 ]= add_tensor (add_dst ); 2087for (uint32_t d = 1 ;d < HTP_OP_MAX_OUTPUTS ;d ++ ) { 2088o .dst [d ]= 0xffff ; 2089 } 2090 2091HEX_VERBOSE ("ggml-hex: %s fused ALLREDUCE+ADD (#%u)\n" ,sess -> c_name (),n_ops - 1 ); 2092return true; 2093 } 2094 2095bool try_fuse_rms_norm_mul (const htp_opnode & node ) { 2096if (n_ops == 0 )return false; 2097if (node .opcode != HTP_OP_MUL )return false; 2098 2099htp_opnode & last_node = ops [n_ops - 1 ]; 2100if (last_node .opcode != HTP_OP_RMS_NORM )return false; 2101 2102const ggml_tensor * mul_src0 = node .src0 (); 2103const ggml_tensor * mul_src1 = node .src1 (); 2104const ggml_tensor * rms_out = last_node .dst (); 2105 2106if (!ggml_hexagon_tensor_is_fuseable (rms_out ))return false; 2107 2108const ggml_tensor * weight ; 2109if (mul_src0 == rms_out || mul_src0 -> data == rms_out -> data ) { 2110weight = mul_src1 ; 2111 }else if (mul_src1 == rms_out || mul_src1 -> data == rms_out -> data ) { 2112weight = mul_src0 ; 2113 }else { 2114return false; 2115 } 2116 2117const ggml_tensor * src0 = last_node .src0 (); 2118 2119if (src0 -> ne [0 ]!= weight -> ne [0 ]|| src0 -> ne [0 ]!= node .dst ()-> ne [0 ]) { 2120return false; 2121 } 2122 2123const bool is_row_bcast = (weight -> ne [1 ]== 1 && weight -> ne [2 ]== 1 && weight -> ne [3 ]== 1 ); 2124const bool is_same_shape = (src0 -> ne [0 ]== weight -> ne [0 ]&& src0 -> ne [1 ]== weight -> ne [1 ]&& 2125src0 -> ne [2 ]== weight -> ne [2 ]&& src0 -> ne [3 ]== weight -> ne [3 ]); 2126if (!is_row_bcast && !is_same_shape )return false; 2127 2128if (!ggml_are_same_shape (src0 ,node .dst ())) { 2129return false; 2130 } 2131if (ggml_is_contiguous (src0 )!= ggml_is_contiguous (node .dst ())) { 2132return false; 2133 } 2134 2135struct htp_unary_kernel_params new_kparams ; 2136ggml_hexagon_precompute_unary_params ( 2137sess ,HTP_OP_RMS_NORM_MUL ,src0 ,weight ,node .dst (),& new_kparams 2138 ); 2139 2140if ((size_t )new_kparams .vtcm_size > sess -> vtcm_size ) { 2141HEX_VERBOSE ("ggml-hex: %s skip RMS_NORM_MUL fusion: VTCM needed (%d) > budget (%zu)\n" , 2142sess -> c_name (),new_kparams .vtcm_size ,sess -> vtcm_size ); 2143return false; 2144 } 2145 2146size_t extra_bufs = 0 ,extra_vmem = 0 ,extra_tens = 0 ; 2147auto fit_t = [& ](const ggml_tensor * t ) { 2148if (!t_map .count (t )) { 2149extra_tens ++ ; 2150auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (t -> buffer -> context ); 2151if (!b_map .count (sbuf -> fd ())) { 2152extra_vmem += sbuf -> size (); 2153extra_bufs += 1 ; 2154 } 2155 } 2156 }; 2157fit_t (weight ); 2158fit_t (node .dst ()); 2159if ((extra_bufs + n_bufs )> n_bufs_max || (extra_tens + n_tens )> n_tens_max || (extra_vmem + b_vmem )> b_vmem_max ) { 2160return false; 2161 } 2162 2163last_node .opcode = HTP_OP_RMS_NORM_MUL ; 2164last_node .name = "RMS_NORM+MUL" ; 2165last_node .inputs .clear (); 2166last_node .inputs .push_back (src0 ); 2167last_node .inputs .push_back (weight ); 2168last_node .outputs .clear (); 2169last_node .outputs .push_back (node .dst ()); 2170last_node .fused .push_back (node .node ); 2171memcpy (last_node .kernel_params ,& new_kparams ,sizeof (new_kparams )); 2172 2173htp_op_desc & o = h_ops [n_ops - 1 ]; 2174o .opcode = HTP_OP_RMS_NORM_MUL ; 2175memcpy (o .kernel_params ,& new_kparams ,sizeof (new_kparams )); 2176 2177o .src [0 ]= add_tensor (src0 ); 2178o .src [1 ]= add_tensor (weight ); 2179for (uint32_t s = 2 ;s < HTP_OP_MAX_INPUTS ;s ++ ) { 2180o .src [s ]= 0xffff ; 2181 } 2182o .dst [0 ]= add_tensor (node .dst ()); 2183for (uint32_t d = 1 ;d < HTP_OP_MAX_OUTPUTS ;d ++ ) { 2184o .dst [d ]= 0xffff ; 2185 } 2186 2187HEX_VERBOSE ("ggml-hex: %s fused RMS_NORM+MUL (#%u)\n" ,sess -> c_name (),n_ops - 1 ); 2188return true; 2189 } 2190 2191bool try_fuse_mul_mat_add (const htp_opnode & node ) { 2192if (n_ops == 0 )return false; 2193if (node .opcode != HTP_OP_ADD )return false; 2194 2195htp_opnode & last_node = ops [n_ops - 1 ]; 2196if (last_node .opcode != HTP_OP_MUL_MAT )return false; 2197 2198const ggml_tensor * add_src0 = node .src0 (); 2199const ggml_tensor * add_src1 = node .src1 (); 2200const ggml_tensor * mm_out = last_node .dst (); 2201 2202if (!ggml_hexagon_tensor_is_fuseable (mm_out ))return false; 2203 2204const ggml_tensor * src2 ; 2205if (add_src0 == mm_out || add_src0 -> data == mm_out -> data ) { 2206src2 = add_src1 ; 2207 }else if (add_src1 == mm_out || add_src1 -> data == mm_out -> data ) { 2208src2 = add_src0 ; 2209 }else { 2210return false; 2211 } 2212 2213const ggml_tensor * src0 = last_node .src0 (); 2214const ggml_tensor * src1 = last_node .src1 (); 2215 2216struct htp_mm_kernel_params kparams ; 2217ggml_hexagon_precompute_fused_matmul_add_params (sess ,src0 ,src1 ,src2 ,node .dst (),& kparams ); 2218const int src1_nrows = src1 -> ne [1 ]* src1 -> ne [2 ]* src1 -> ne [3 ]; 2219const bool can_fuse = (kparams .n_hmx > 0 )|| (src1_nrows == 1 ); 2220if (!can_fuse )return false; 2221 2222if ((size_t )kparams .vtcm_size > sess -> vtcm_size ) { 2223HEX_VERBOSE ("ggml-hex: %s skip MUL_MAT_ADD fusion: VTCM needed (%d) > budget (%zu)\n" , 2224sess -> c_name (),kparams .vtcm_size ,sess -> vtcm_size ); 2225return false; 2226 } 2227 2228size_t extra_bufs = 0 ,extra_vmem = 0 ,extra_tens = 0 ; 2229auto fit_t = [& ](const ggml_tensor * t ) { 2230if (!t_map .count (t )) { 2231extra_tens ++ ; 2232auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (t -> buffer -> context ); 2233if (!b_map .count (sbuf -> fd ())) { 2234extra_vmem += sbuf -> size (); 2235extra_bufs += 1 ; 2236 } 2237 } 2238 }; 2239fit_t (src2 ); 2240fit_t (node .dst ()); 2241if ((extra_bufs + n_bufs )> n_bufs_max || (extra_tens + n_tens )> n_tens_max || (extra_vmem + b_vmem )> b_vmem_max ) { 2242return false; 2243 } 2244 2245last_node .opcode = HTP_OP_MUL_MAT_ADD ; 2246last_node .name = "MUL_MAT+ADD" ; 2247last_node .inputs .clear (); 2248last_node .inputs .push_back (src0 ); 2249last_node .inputs .push_back (src1 ); 2250last_node .inputs .push_back (src2 ); 2251last_node .outputs .clear (); 2252last_node .outputs .push_back (node .dst ()); 2253last_node .fused .push_back (node .node ); 2254memcpy (last_node .kernel_params ,& kparams ,sizeof (kparams )); 2255 2256htp_op_desc & o = h_ops [n_ops - 1 ]; 2257o .opcode = HTP_OP_MUL_MAT_ADD ; 2258memcpy (o .kernel_params ,& kparams ,sizeof (kparams )); 2259 2260o .src [0 ]= add_tensor (src0 ); 2261o .src [1 ]= add_tensor (src1 ); 2262o .src [2 ]= add_tensor (src2 ); 2263for (uint32_t s = 3 ;s < HTP_OP_MAX_INPUTS ;s ++ ) { 2264o .src [s ]= 0xffff ; 2265 } 2266o .dst [0 ]= add_tensor (node .dst ()); 2267for (uint32_t d = 1 ;d < HTP_OP_MAX_OUTPUTS ;d ++ ) { 2268o .dst [d ]= 0xffff ; 2269 } 2270 2271HEX_VERBOSE ("ggml-hex: %s fused MUL_MAT+ADD (#%u)\n" ,sess -> c_name (),n_ops - 1 ); 2272return true; 2273 } 2274 2275bool try_fuse_mul_mat_nx (const htp_opnode & node ) { 2276if (n_ops == 0 || node .opcode != HTP_OP_MUL_MAT )return false; 2277if (!is_mergeable_mul_mat (node .node ))return false; 2278 2279const ggml_tensor * w_in = node .src0 (); 2280const ggml_tensor * x_in = node .src1 (); 2281const ggml_tensor * d_in = node .dst (); 2282 2283htp_opnode & last_node = ops [n_ops - 1 ]; 2284 2285// Case 1: last_node is already MUL_MAT_NX 2286if (last_node .opcode == HTP_OP_MUL_MAT_NX ) { 2287const uint32_t curr_n = (uint32_t )last_node .outputs .size (); 2288if (curr_n >=HTP_OP_MAX_OUTPUTS || curr_n + 1 >=HTP_OP_MAX_INPUTS ) { 2289return false; 2290 } 2291 2292const ggml_tensor * w0 = last_node .inputs [0 ]; 2293const ggml_tensor * x = last_node .inputs [curr_n ]; 2294 2295if (x_in != x || w_in -> type != w0 -> type || w_in -> ne [0 ]!= w0 -> ne [0 ]) { 2296return false; 2297 } 2298if (!last_node .fused .empty ()&& (mm_is_hmx_eligible (last_node .fused [0 ])!= mm_is_hmx_eligible (node .node ))) { 2299return false; 2300 } 2301 2302struct htp_mm_kernel_params kparams ; 2303ggml_hexagon_precompute_fused_mmnx_params (sess ,w0 ,x ,curr_n + 1 ,& kparams ); 2304if (!is_supported_mul_mat_nx_kernel (w0 ,& kparams )) { 2305return false; 2306 } 2307if ((size_t )kparams .vtcm_size > sess -> vtcm_size ) { 2308HEX_VERBOSE ("ggml-hex: %s skip NX fusion: VTCM needed (%d) > budget (%zu)\n" , 2309sess -> c_name (),kparams .vtcm_size ,sess -> vtcm_size ); 2310return false; 2311 } 2312 2313size_t extra_bufs = 0 ,extra_vmem = 0 ,extra_tens = 0 ; 2314auto fit_t = [& ](const ggml_tensor * t ) { 2315if (!t_map .count (t )) { 2316extra_tens ++ ; 2317auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (t -> buffer -> context ); 2318if (!b_map .count (sbuf -> fd ())) { 2319extra_vmem += sbuf -> size (); 2320extra_bufs += 1 ; 2321 } 2322 } 2323 }; 2324fit_t (w_in ); 2325fit_t (d_in ); 2326if ((extra_bufs + n_bufs )> n_bufs_max || (extra_tens + n_tens )> n_tens_max || (extra_vmem + b_vmem )> b_vmem_max ) { 2327return false; 2328 } 2329 2330last_node .inputs [curr_n ]= w_in ; 2331last_node .inputs .push_back (x ); 2332last_node .outputs .push_back (d_in ); 2333last_node .fused .push_back (node .node ); 2334memcpy (last_node .kernel_params ,& kparams ,sizeof (kparams )); 2335 2336htp_op_desc & o = h_ops [n_ops - 1 ]; 2337memcpy (o .kernel_params ,& kparams ,sizeof (kparams )); 2338 2339for (uint32_t s = 0 ;s <=curr_n + 1 ;s ++ ) { 2340o .src [s ]= add_tensor (last_node .inputs [s ]); 2341 } 2342for (uint32_t s = curr_n + 2 ;s < HTP_OP_MAX_INPUTS ;s ++ ) { 2343o .src [s ]= 0xffff ; 2344 } 2345for (uint32_t d = 0 ;d <=curr_n ;d ++ ) { 2346o .dst [d ]= add_tensor (last_node .outputs [d ]); 2347 } 2348for (uint32_t d = curr_n + 1 ;d < HTP_OP_MAX_OUTPUTS ;d ++ ) { 2349o .dst [d ]= 0xffff ; 2350 } 2351 2352HEX_VERBOSE ("ggml-hex: %s fused MUL_MAT_NX (N=%u, #%u)\n" ,sess -> c_name (),curr_n + 1 ,n_ops - 1 ); 2353return true; 2354 } 2355 2356// Case 2: last_node is single MUL_MAT 2357if (last_node .opcode == HTP_OP_MUL_MAT ) { 2358if (!is_mergeable_mul_mat_pair (last_node .node ,node .node )) { 2359return false; 2360 } 2361 2362const ggml_tensor * w0 = last_node .src0 (); 2363const ggml_tensor * x = last_node .src1 (); 2364const ggml_tensor * w1 = node .src0 (); 2365 2366struct htp_mm_kernel_params kparams ; 2367ggml_hexagon_precompute_fused_mmnx_params (sess ,w0 ,x ,2 ,& kparams ); 2368if (!is_supported_mul_mat_nx_kernel (w0 ,& kparams )) { 2369return false; 2370 } 2371if ((size_t )kparams .vtcm_size > sess -> vtcm_size ) { 2372HEX_VERBOSE ("ggml-hex: %s skip NX fusion: VTCM needed (%d) > budget (%zu)\n" , 2373sess -> c_name (),kparams .vtcm_size ,sess -> vtcm_size ); 2374return false; 2375 } 2376 2377size_t extra_bufs = 0 ,extra_vmem = 0 ,extra_tens = 0 ; 2378auto fit_t = [& ](const ggml_tensor * t ) { 2379if (!t_map .count (t )) { 2380extra_tens ++ ; 2381auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (t -> buffer -> context ); 2382if (!b_map .count (sbuf -> fd ())) { 2383extra_vmem += sbuf -> size (); 2384extra_bufs += 1 ; 2385 } 2386 } 2387 }; 2388fit_t (w1 ); 2389fit_t (node .dst ()); 2390if ((extra_bufs + n_bufs )> n_bufs_max || (extra_tens + n_tens )> n_tens_max || (extra_vmem + b_vmem )> b_vmem_max ) { 2391return false; 2392 } 2393 2394const ggml_tensor * dst_0 = last_node .dst (); 2395const ggml_tensor * dst_1 = node .dst (); 2396 2397last_node .opcode = HTP_OP_MUL_MAT_NX ; 2398last_node .name = "MUL_MAT_NX" ; 2399last_node .inputs .clear (); 2400last_node .inputs .push_back (w0 ); 2401last_node .inputs .push_back (w1 ); 2402last_node .inputs .push_back (x ); 2403last_node .outputs .clear (); 2404last_node .outputs .push_back (dst_0 ); 2405last_node .outputs .push_back (dst_1 ); 2406last_node .fused .push_back (node .node ); 2407memcpy (last_node .kernel_params ,& kparams ,sizeof (kparams )); 2408 2409htp_op_desc & o = h_ops [n_ops - 1 ]; 2410o .opcode = HTP_OP_MUL_MAT_NX ; 2411memcpy (o .kernel_params ,& kparams ,sizeof (kparams )); 2412 2413o .src [0 ]= add_tensor (w0 ); 2414o .src [1 ]= add_tensor (w1 ); 2415o .src [2 ]= add_tensor (x ); 2416for (uint32_t s = 3 ;s < HTP_OP_MAX_INPUTS ;s ++ ) { 2417o .src [s ]= 0xffff ; 2418 } 2419o .dst [0 ]= add_tensor (dst_0 ); 2420o .dst [1 ]= add_tensor (dst_1 ); 2421for (uint32_t d = 2 ;d < HTP_OP_MAX_OUTPUTS ;d ++ ) { 2422o .dst [d ]= 0xffff ; 2423 } 2424 2425HEX_VERBOSE ("ggml-hex: %s fused MUL_MAT_NX (N=2, #%u)\n" ,sess -> c_name (),n_ops - 1 ); 2426return true; 2427 } 2428 2429return false; 2430 } 2431 2432bool try_fuse_mul_mat_id_nx (const htp_opnode & node ) { 2433if (n_ops == 0 || node .opcode != HTP_OP_MUL_MAT_ID )return false; 2434if (!is_mergeable_mul_mat_id (node .node ))return false; 2435 2436const ggml_tensor * w_in = node .src0 (); 2437const ggml_tensor * x_in = node .src1 (); 2438const ggml_tensor * ids_in = node .node -> src [2 ]; 2439const ggml_tensor * d_in = node .dst (); 2440 2441htp_opnode & last_node = ops [n_ops - 1 ]; 2442 2443// Case 1: last_node is already MUL_MAT_ID_NX 2444if (last_node .opcode == HTP_OP_MUL_MAT_ID_NX ) { 2445const uint32_t curr_n = (uint32_t )last_node .outputs .size (); 2446if (curr_n >=HTP_OP_MAX_OUTPUTS || curr_n + 2 >=HTP_OP_MAX_INPUTS ) { 2447return false; 2448 } 2449 2450const ggml_tensor * w0 = last_node .inputs [0 ]; 2451const ggml_tensor * x = last_node .inputs [curr_n ]; 2452const ggml_tensor * ids = last_node .inputs [curr_n + 1 ]; 2453 2454if (x_in != x || ids_in != ids || w_in -> type != w0 -> type || w_in -> ne [0 ]!= w0 -> ne [0 ]|| w_in -> ne [2 ]!= w0 -> ne [2 ]) { 2455return false; 2456 } 2457if (!last_node .fused .empty ()&& (mm_is_hmx_eligible (last_node .fused [0 ])!= mm_is_hmx_eligible (node .node ))) { 2458return false; 2459 } 2460 2461struct htp_mm_kernel_params kparams ; 2462ggml_hexagon_precompute_fused_mmidnx_params (sess ,w0 ,x ,d_in ,curr_n + 1 ,& kparams ); 2463if (!is_supported_mul_mat_id_nx_kernel (w0 ,& kparams )) { 2464return false; 2465 } 2466if ((size_t )kparams .vtcm_size > sess -> vtcm_size ) { 2467HEX_VERBOSE ("ggml-hex: %s skip ID NX fusion: VTCM needed (%d) > budget (%zu)\n" , 2468sess -> c_name (),kparams .vtcm_size ,sess -> vtcm_size ); 2469return false; 2470 } 2471 2472size_t extra_bufs = 0 ,extra_vmem = 0 ,extra_tens = 0 ; 2473auto fit_t = [& ](const ggml_tensor * t ) { 2474if (!t_map .count (t )) { 2475extra_tens ++ ; 2476auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (t -> buffer -> context ); 2477if (!b_map .count (sbuf -> fd ())) { 2478extra_vmem += sbuf -> size (); 2479extra_bufs += 1 ; 2480 } 2481 } 2482 }; 2483fit_t (w_in ); 2484fit_t (d_in ); 2485if ((extra_bufs + n_bufs )> n_bufs_max || (extra_tens + n_tens )> n_tens_max || (extra_vmem + b_vmem )> b_vmem_max ) { 2486return false; 2487 } 2488 2489last_node .inputs [curr_n ]= w_in ; 2490last_node .inputs [curr_n + 1 ]= x ; 2491last_node .inputs .push_back (ids ); 2492last_node .outputs .push_back (d_in ); 2493last_node .fused .push_back (node .node ); 2494memcpy (last_node .kernel_params ,& kparams ,sizeof (kparams )); 2495 2496htp_op_desc & o = h_ops [n_ops - 1 ]; 2497memcpy (o .kernel_params ,& kparams ,sizeof (kparams )); 2498 2499for (uint32_t s = 0 ;s <=curr_n + 2 ;s ++ ) { 2500o .src [s ]= add_tensor (last_node .inputs [s ]); 2501 } 2502for (uint32_t s = curr_n + 3 ;s < HTP_OP_MAX_INPUTS ;s ++ ) { 2503o .src [s ]= 0xffff ; 2504 } 2505for (uint32_t d = 0 ;d <=curr_n ;d ++ ) { 2506o .dst [d ]= add_tensor (last_node .outputs [d ]); 2507 } 2508for (uint32_t d = curr_n + 1 ;d < HTP_OP_MAX_OUTPUTS ;d ++ ) { 2509o .dst [d ]= 0xffff ; 2510 } 2511 2512HEX_VERBOSE ("ggml-hex: %s fused MUL_MAT_ID_NX (N=%u, #%u)\n" ,sess -> c_name (),curr_n + 1 ,n_ops - 1 ); 2513return true; 2514 } 2515 2516// Case 2: last_node is single MUL_MAT_ID 2517if (last_node .opcode == HTP_OP_MUL_MAT_ID ) { 2518if (!is_mergeable_mul_mat_id_pair (last_node .node ,node .node )) { 2519return false; 2520 } 2521 2522const ggml_tensor * w0 = last_node .src0 (); 2523const ggml_tensor * x = last_node .src1 (); 2524const ggml_tensor * ids = last_node .node -> src [2 ]; 2525const ggml_tensor * w1 = node .src0 (); 2526 2527struct htp_mm_kernel_params kparams ; 2528ggml_hexagon_precompute_fused_mmidnx_params (sess ,w0 ,x ,node .dst (),2 ,& kparams ); 2529if (!is_supported_mul_mat_id_nx_kernel (w0 ,& kparams )) { 2530return false; 2531 } 2532if ((size_t )kparams .vtcm_size > sess -> vtcm_size ) { 2533HEX_VERBOSE ("ggml-hex: %s skip ID NX fusion: VTCM needed (%d) > budget (%zu)\n" , 2534sess -> c_name (),kparams .vtcm_size ,sess -> vtcm_size ); 2535return false; 2536 } 2537 2538size_t extra_bufs = 0 ,extra_vmem = 0 ,extra_tens = 0 ; 2539auto fit_t = [& ](const ggml_tensor * t ) { 2540if (!t_map .count (t )) { 2541extra_tens ++ ; 2542auto sbuf = static_cast < ggml_hexagon_shared_buffer *> (t -> buffer -> context ); 2543if (!b_map .count (sbuf -> fd ())) { 2544extra_vmem += sbuf -> size (); 2545extra_bufs += 1 ; 2546 } 2547 } 2548 }; 2549fit_t (w1 ); 2550fit_t (node .dst ()); 2551if ((extra_bufs + n_bufs )> n_bufs_max || (extra_tens + n_tens )> n_tens_max || (extra_vmem + b_vmem )> b_vmem_max ) { 2552return false; 2553 } 2554 2555const ggml_tensor * dst_0 = last_node .dst (); 2556const ggml_tensor * dst_1 = node .dst (); 2557 2558last_node .opcode = HTP_OP_MUL_MAT_ID_NX ; 2559last_node .name = "MUL_MAT_ID_NX" ; 2560last_node .inputs .clear (); 2561last_node .inputs .push_back (w0 ); 2562last_node .inputs .push_back (w1 ); 2563last_node .inputs .push_back (x ); 2564last_node .inputs .push_back (ids ); 2565last_node .outputs .clear (); 2566last_node .outputs .push_back (dst_0 ); 2567last_node .outputs .push_back (dst_1 ); 2568last_node .fused .push_back (node .node ); 2569memcpy (last_node .kernel_params ,& kparams ,sizeof (kparams )); 2570 2571htp_op_desc & o = h_ops [n_ops - 1 ]; 2572o .opcode = HTP_OP_MUL_MAT_ID_NX ; 2573memcpy (o .kernel_params ,& kparams ,sizeof (kparams )); 2574 2575o .src [0 ]= add_tensor (w0 ); 2576o .src [1 ]= add_tensor (w1 ); 2577o .src [2 ]= add_tensor (x ); 2578o .src [3 ]= add_tensor (ids ); 2579for (uint32_t s = 4 ;s < HTP_OP_MAX_INPUTS ;s ++ ) { 2580o .src [s ]= 0xffff ; 2581 } 2582o .dst [0 ]= add_tensor (dst_0 ); 2583o .dst [1 ]= add_tensor (dst_1 ); 2584for (uint32_t d = 2 ;d < HTP_OP_MAX_OUTPUTS ;d ++ ) { 2585o .dst [d ]= 0xffff ; 2586 } 2587 2588HEX_VERBOSE ("ggml-hex: %s fused MUL_MAT_ID_NX (N=2, #%u)\n" ,sess -> c_name (),n_ops - 1 ); 2589return true; 2590 } 2591 2592return false; 2593 } 2594 2595bool try_fuse (const htp_opnode & node ) { 2596if (!opt_opfusion )return false; 2597if (ggml_hexagon_is_fusion_enabled (GGML_HEXAGON_FUSE_ALLREDUCE_ADD )&& try_fuse_allreduce_add (node ))return true; 2598if (ggml_hexagon_is_fusion_enabled (GGML_HEXAGON_FUSE_RMS_NORM_MUL )&& try_fuse_rms_norm_mul (node ))return true; 2599if (ggml_hexagon_is_fusion_enabled (GGML_HEXAGON_FUSE_MUL_MAT_ADD )&& try_fuse_mul_mat_add (node ))return true; 2600if (ggml_hexagon_is_fusion_enabled (GGML_HEXAGON_FUSE_MUL_MAT_NX )&& try_fuse_mul_mat_nx (node ))return true; 2601if (ggml_hexagon_is_fusion_enabled (GGML_HEXAGON_FUSE_MUL_MAT_ID_NX )&& try_fuse_mul_mat_id_nx (node ))return true; 2602return false; 2603 } 2604}; 2605 2606struct ggml_hexagon_registry { 2607ggml_hexagon_registry (ggml_backend_reg_t reg ); 2608 ~ggml_hexagon_registry (); 2609 2610ggml_backend_device devices [GGML_HEXAGON_MAX_SESSIONS ]; 2611}; 2612 2613struct ggml_hexagon_opqueue { 2614// Shared buffer for storing batches 2615ggml_hexagon_shared_buffer * shm_buf ; 2616size_t shm_blk_size ; 2617size_t depth ; 2618 2619using opvec = std::vector < htp_opnode > ; 2620 2621 std::vector < opvec > op_cache ;// per batch op cache 2622 std::vector < uint64_t > start_usec ;// per batch start time 2623 2624ggml_hexagon_opqueue (ggml_hexagon_session * sess ,size_t batch_size ,size_t depth ) :depth (depth ) { 2625size_t n_bufs = HTP_OP_MAX_BUFS ; 2626size_t n_ops = batch_size ; 2627size_t n_tensors = n_ops * HTP_OP_MAX_OUTPUTS + n_ops * HTP_OP_MAX_INPUTS ; 2628 2629size_t tr_size = 0 ; 2630if (opt_profile == 3 ) { 2631tr_size = (HTP_MAX_NTHREADS + 1 )* opt_optrace * sizeof (htp_trace_desc ); 2632 } 2633 2634shm_blk_size = sizeof (htp_buf_desc )* n_bufs + 2635sizeof (htp_tensor )* n_tensors + 2636sizeof (htp_op_desc )* n_ops + 2637sizeof (htp_prof_desc )* n_ops + 2638tr_size ; 2639 2640shm_buf = new ggml_hexagon_shared_buffer (sess ,shm_blk_size * depth , true/* pinned */ ); 2641 2642op_cache .resize (depth ); 2643start_usec .resize (depth ,0 ); 2644 2645if (opt_verbose ) { 2646GGML_LOG_INFO ("ggml-hex: %s allocated opqueue : batch-size %zu depth %zu shm-size %zu shm-block-size %zu\n" , 2647sess -> c_name (),batch_size ,depth ,shm_buf -> size (),shm_blk_size ); 2648 } 2649 } 2650 2651 ~ggml_hexagon_opqueue () { 2652delete shm_buf ; 2653 } 2654 2655size_t shm_size ()const {return shm_buf ?shm_buf -> size () :0 ; } 2656 2657// push new batch 2658bool push (htp_opbatch_req & req ,dspqueue_buffer & dbuf ,const ggml_hexagon_opbatch * op_batch ,uint64_t seq ) { 2659 static_assert(sizeof (htp_opbatch_req ) %8 == 0 ,"sizeof(htp_opbatch_req) must be multiple of 8" ); 2660 static_assert(sizeof (htp_opbatch_rsp ) %8 == 0 ,"sizeof(htp_opbatch_rsp) must be multiple of 8" ); 2661 static_assert(sizeof (htp_buf_desc ) %8 == 0 ,"sizeof(htp_buf_desc) must be multiple of 8" ); 2662 static_assert(sizeof (htp_tensor ) %8 == 0 ,"sizeof(htp_tensor) must be multiple of 8" ); 2663 static_assert(sizeof (htp_op_desc ) %8 == 0 ,"sizeof(htp_op_desc) must be multiple of 8" ); 2664 static_assert(sizeof (htp_prof_desc ) %8 == 0 ,"sizeof(htp_prof_desc) must be multiple of 8" ); 2665 2666if (seq - shm_buf -> sess -> batch_rsp_seq > depth ) {return false; } 2667 2668const uint32_t slot = (uint32_t ) ((seq - 1 ) %depth ); 2669 2670req .seq = seq ; 2671req .n_bufs = op_batch -> n_bufs ; 2672req .n_tensors = op_batch -> n_tens ; 2673req .n_ops = op_batch -> n_ops ; 2674 2675op_cache [slot ]= op_batch -> ops ; 2676start_usec [slot ]= ggml_time_us (); 2677 2678const size_t b_size = sizeof (htp_buf_desc )* req .n_bufs ; 2679const size_t t_size = sizeof (htp_tensor )* req .n_tensors ; 2680const size_t o_size = sizeof (htp_op_desc )* req .n_ops ; 2681const size_t p_size = sizeof (htp_prof_desc )* req .n_ops ; 2682 2683size_t tr_size = 0 ; 2684if (opt_profile == 3 ) { 2685req .n_traces = opt_optrace ; 2686tr_size = (HTP_MAX_NTHREADS + 1 )* req .n_traces * sizeof (htp_trace_desc ); 2687 }else { 2688req .n_traces = 0 ; 2689 } 2690 2691dbuf .ptr = shm_buf -> base ()+ ((size_t )slot * shm_blk_size ); 2692dbuf .fd = shm_buf -> fd (); 2693dbuf .flags = DSPQUEUE_BUFFER_FLAG_FLUSH_SENDER |DSPQUEUE_BUFFER_FLAG_INVALIDATE_RECIPIENT ; 2694dbuf .offset = (uint8_t * )dbuf .ptr - (uint8_t * )shm_buf -> base (); 2695dbuf .size = b_size + t_size + o_size + p_size + tr_size ; 2696 2697GGML_ASSERT (dbuf .size <=shm_blk_size ); 2698 2699uint8_t * m_ptr = (uint8_t * )dbuf .ptr ; 2700uint8_t * b_ptr = m_ptr ;m_ptr += b_size ; 2701uint8_t * t_ptr = m_ptr ;m_ptr += t_size ; 2702uint8_t * o_ptr = m_ptr ; 2703 2704memcpy (b_ptr , (void * )op_batch -> h_bufs .data (),b_size ); 2705memcpy (t_ptr , (void * )op_batch -> h_tens .data (),t_size ); 2706memcpy (o_ptr , (void * )op_batch -> h_ops .data (),o_size ); 2707 2708HEX_VERBOSE ("ggml-hex: %s opqueue-push batch #%llu : n-bufs %u n-tensors %u n-ops %u vmem %zu : b-size %zu t-size %zu o-size %zu m-size %zu\n" , 2709shm_buf -> sess -> c_name (), (unsigned long long )req .seq ,req .n_bufs ,req .n_tensors ,req .n_ops ,op_batch -> b_vmem , 2710b_size ,t_size ,o_size , (size_t )dbuf .size ); 2711 2712if (opt_verbose > 1 ) { 2713htp_buf_desc * b = (htp_buf_desc * )b_ptr ; 2714for (unsigned int i = 0 ;i < req .n_bufs ;i ++ ) { 2715GGML_LOG_DEBUG ("ggml-hex: %s htp-buf #%u : fd %d base %p size %zu\n" ,shm_buf -> sess -> c_name (),i , 2716b [i ].fd , (void * )b [i ].base , (size_t )b [i ].size ); 2717 } 2718htp_tensor * t = (htp_tensor * )t_ptr ; 2719for (unsigned int i = 0 ;i < req .n_tensors ;i ++ ) { 2720GGML_LOG_DEBUG ("ggml-hex: %s htp-tensor #%u : bi %u offset %u size %u : %zu:%zu:%zu:%zu\n" , 2721shm_buf -> sess -> c_name (),i ,t [i ].bi ,t [i ].data ,t [i ].size , 2722 (size_t )t [i ].ne [0 ], (size_t )t [i ].ne [1 ], (size_t )t [i ].ne [2 ], (size_t )t [i ].ne [3 ]); 2723 } 2724 } 2725 2726return true; 2727 } 2728 2729void pop (htp_opbatch_rsp rsp ,dspqueue_buffer dbuf ) { 2730const uint32_t slot = (uint32_t ) ((rsp .seq - 1 ) %depth ); 2731 2732const size_t b_size = sizeof (htp_buf_desc )* rsp .n_bufs ; 2733const size_t t_size = sizeof (htp_tensor )* rsp .n_tensors ; 2734const size_t o_size = sizeof (htp_op_desc )* rsp .n_ops ; 2735const size_t p_size = sizeof (htp_prof_desc )* rsp .n_ops ; 2736 2737size_t tr_size = 0 ; 2738uint32_t n_traces = 0 ; 2739if (opt_profile == 3 ) { 2740n_traces = opt_optrace ; 2741tr_size = (HTP_MAX_NTHREADS + 1 )* n_traces * sizeof (htp_trace_desc ); 2742 } 2743 2744const size_t m_size = b_size + t_size + o_size + p_size + tr_size ; 2745GGML_ASSERT (m_size <=shm_blk_size ); 2746 2747HEX_VERBOSE ("ggml-hex: %s opqueue-pop batch #%llu : n-bufs %u n-tensors %u n-ops %u : m-size %zu b-size %zu t-size %zu o-size %zu\n" , 2748shm_buf -> sess -> c_name (), (unsigned long long )rsp .seq ,rsp .n_bufs ,rsp .n_tensors ,rsp .n_ops , 2749 (size_t )dbuf .size ,b_size ,t_size ,o_size ); 2750 2751uint8_t * m_ptr = (uint8_t * )dbuf .ptr ; 2752uint8_t * p_ptr = m_ptr + (b_size + t_size + o_size ); 2753 2754if (rsp .n_ops > 0 ) { 2755auto & ops = op_cache [slot ]; 2756GGML_ASSERT (rsp .n_ops <=ops .size ()); 2757 2758const htp_prof_desc * pd = (const htp_prof_desc * )p_ptr ; 2759const htp_trace_desc * trace_events = nullptr ; 2760if (opt_profile == 3 ) { 2761trace_events = (const htp_trace_desc * ) (p_ptr + p_size ); 2762 } 2763 2764if (opt_profile ) { 2765ggml_hexagon_dump_batch_prof (shm_buf -> sess -> name ,rsp ); 2766 } 2767 2768for (uint32_t i = 0 ;i < rsp .n_ops ;i ++ ) { 2769if (opt_profile ) { 2770ggml_hexagon_dump_op_prof (shm_buf -> sess -> name ,ops [i ],pd [i ]); 2771 } 2772 } 2773 2774if (opt_profile ) { 2775ggml_hexagon_dump_trace_events (shm_buf -> sess -> name ,rsp ,trace_events ,n_traces ); 2776 } 2777 } 2778 } 2779}; 2780 2781void ggml_hexagon_session::flush_peers () { 2782auto vpeers = std::move (virt_peers ); 2783virt_peers .clear (); 2784for (auto * peer :vpeers ) { 2785peer -> flush_sync (); 2786 } 2787 2788auto ppeers = std::move (phys_peers ); 2789phys_peers .clear (); 2790for (auto * peer :ppeers ) { 2791peer -> flush_async (); 2792 } 2793 2794for (auto & sub :this -> mdev .sessions ) { 2795sub -> flush_peers (); 2796 } 2797} 2798 2799void ggml_hexagon_session::flush_async () { 2800flush_peers (); 2801flush_batch (); 2802} 2803 2804void ggml_hexagon_session::flush_pending (bool all ) { 2805for (auto & sub :this -> mdev .sessions ) { 2806sub -> flush_pending (all ); 2807if (sub -> last_error > HTP_STATUS_OK ) { 2808this -> last_error = sub -> last_error .load (); 2809 } 2810 } 2811 2812while (this -> batch_rsp_seq < this -> batch_req_seq ) { 2813struct htp_opbatch_rsp rsp ; 2814uint32_t rsp_size ; 2815uint32_t flags ; 2816 2817struct dspqueue_buffer dbuf ; 2818uint32_t n_dbufs ; 2819 2820// Read response packet from queue 2821const uint32_t timeo = opt_oppoll ?0 :DSPQUEUE_TIMEOUT ; 2822 2823int err = dspqueue_read (this -> queue ,& flags ,1 ,& n_dbufs ,& dbuf ,sizeof (rsp ),& rsp_size , (uint8_t * )& rsp ,timeo ); 2824if (err == AEE_EEXPIRED || err == AEE_EWOULDBLOCK ) { 2825continue ; 2826 } 2827 2828if (err != 0 ) { 2829GGML_ABORT ("ggml-hex: dspqueue_read failed: 0x%08x\n" , (unsigned )err ); 2830 } 2831 2832// Basic sanity checks 2833if (rsp_size != sizeof (rsp )|| n_dbufs != 1 ) { 2834GGML_ABORT ("ggml-hex: %s dspcall : bad response : size %u dspbufs %u\n" ,this -> c_name (),rsp_size ,n_dbufs ); 2835 } 2836 2837if (rsp .status > HTP_STATUS_OK ) { 2838GGML_LOG_ERROR ("ggml-hex: %s dspcall : dsp-rsp %s\n" ,this -> c_name (),status_to_str (rsp .status )); 2839this -> last_error = rsp .status ; 2840for (auto & sub :this -> mdev .sessions ) { 2841sub -> last_error = rsp .status ; 2842 } 2843 } 2844 2845op_queue -> pop (rsp ,dbuf ); 2846 2847GGML_ASSERT (rsp .seq == this -> batch_rsp_seq + 1 ); 2848this -> batch_rsp_seq = rsp .seq ; 2849 2850if (!all )break ; 2851 } 2852} 2853 2854void ggml_hexagon_session::flush_sync (bool all ) { 2855flush_async (); 2856flush_pending (all ); 2857} 2858 2859void ggml_hexagon_session::flush_batch (size_t min_ops ) { 2860if (op_batch -> n_ops < min_ops ) {return ; } 2861 2862op_batch -> sort_buffers (); 2863 2864htp_opbatch_req req {}; 2865dspqueue_buffer dbuf {}; 2866 2867const uint64_t seq = ++ this -> batch_req_seq ; 2868 2869op_batch -> update_mdev_group (this -> mdev .idx ); 2870 2871if (!op_queue -> push (req ,dbuf ,op_batch ,seq )) { 2872flush_pending (false); 2873op_queue -> push (req ,dbuf ,op_batch ,seq ); 2874 } 2875 2876for (auto & sub :this -> mdev .sessions ) { 2877htp_opbatch_req sub_req {}; 2878dspqueue_buffer sub_dbuf {}; 2879 2880sub -> batch_req_seq = seq ; 2881op_batch -> update_mdev_group (sub -> mdev .idx ); 2882 2883if (!sub -> op_queue -> push (sub_req ,sub_dbuf ,op_batch ,seq )) { 2884sub -> flush_pending (false); 2885sub -> op_queue -> push (sub_req ,sub_dbuf ,op_batch ,seq ); 2886 } 2887 2888HEX_VERBOSE ("ggml-hex: %s queue-opbatch: %p size %u\n" ,sub -> c_name (),sub_dbuf .ptr ,sub_dbuf .size ); 2889 2890int err = dspqueue_write (sub -> queue ,0 ,1 ,& sub_dbuf ,sizeof (sub_req ), (const uint8_t * )& sub_req ,DSPQUEUE_TIMEOUT ); 2891if (err != 0 ) { 2892GGML_ABORT ("ggml-hex: %s dspqueue_write failed: 0x%08x\n" ,sub -> c_name (), (unsigned )err ); 2893 } 2894 } 2895 2896HEX_VERBOSE ("ggml-hex: %s queue-opbatch: %p size %u\n" ,this -> c_name (),dbuf .ptr ,dbuf .size ); 2897 2898int err = dspqueue_write (this -> queue ,0 ,1 ,& dbuf ,sizeof (req ), (const uint8_t * )& req ,DSPQUEUE_TIMEOUT ); 2899if (err != 0 ) { 2900GGML_ABORT ("ggml-hex: %s dspqueue_write failed: 0x%08x\n" ,this -> c_name (), (unsigned )err ); 2901 } 2902 2903op_batch -> reset (); 2904} 2905 2906void ggml_hexagon_session::enqueue_op (const htp_opnode & node ) { 2907auto clone_tensor_buffer = [this ](const ggml_tensor * t ) { 2908if (t && t -> buffer && ggml_backend_buffer_is_hexagon (t -> buffer )) { 2909auto sbuf = static_cast < const ggml_hexagon_shared_buffer *> (t -> buffer -> context ); 2910if (ggml_backend_hexagon_buffer_get_sess (t -> buffer )!= this ) { 2911this -> clone_buffer (sbuf ); 2912 } 2913for (auto & sub :this -> mdev .sessions ) { 2914sub -> clone_buffer (sbuf ); 2915 } 2916 } 2917 }; 2918 2919for (auto t :node .get_inputs ()) { 2920clone_tensor_buffer (t ); 2921 } 2922for (auto t :node .get_outputs ()) { 2923clone_tensor_buffer (t ); 2924 } 2925 2926if (opt_opfusion && op_batch -> try_fuse (node )) { 2927return ; 2928 } 2929 2930if (!op_batch -> fit_op (node )) { 2931flush_async (); 2932 } 2933 2934if (this -> mdev .count > 1 && op_batch -> n_ops == 0 ) { 2935enqueue_mdev_group (); 2936 } 2937 2938op_batch -> add_op (node ); 2939} 2940 2941void ggml_hexagon_session::enqueue_mdev_group () { 2942htp_opnode group_node (HTP_OP_MDEV_GROUP ); 2943 2944uint8_t * fence_slot = this -> mdev_fence_slot ; 2945 2946static ggml_hexagon_tensor_extra fence_extra { {},0 ,GGML_HEXAGON_TENSOR_FENCE }; 2947ggml_tensor dummy_t {}; 2948dummy_t .buffer = & this -> fence_buf -> backend_buffer ; 2949dummy_t .extra = & fence_extra ; 2950dummy_t .data = (void * )fence_slot ; 2951dummy_t .type = GGML_TYPE_I8 ; 2952dummy_t .ne [0 ]= HTP_FENCE_SLOT_SIZE ; 2953dummy_t .ne [1 ]= (int64_t )this -> mdev .count ; 2954dummy_t .ne [2 ]= 1 ; 2955dummy_t .ne [3 ]= 1 ; 2956dummy_t .nb [0 ]= 1 ; 2957dummy_t .nb [1 ]= HTP_FENCE_SLOT_SIZE ; 2958dummy_t .nb [2 ]= dummy_t .nb [1 ]* dummy_t .ne [1 ]; 2959dummy_t .nb [3 ]= dummy_t .nb [2 ]; 2960dummy_t .op = GGML_OP_NONE ; 2961dummy_t .op_params [0 ]= (int32_t )this -> mdev .idx ; 2962 2963ggml_tensor * node = group_node .add_dummy (dummy_t ); 2964node -> src [0 ]= node ; 2965group_node .init (node ); 2966group_node .outputs .clear (); 2967group_node .name = "MDEV_GROUP" ; 2968 2969if (this -> fence_buf -> sess != this ) { 2970this -> clone_buffer (this -> fence_buf ); 2971 } 2972for (auto & sub :this -> mdev .sessions ) { 2973sub -> clone_buffer (this -> fence_buf ); 2974 } 2975 2976op_batch -> add_op (group_node ); 2977} 2978 2979void ggml_hexagon_session::enqueue_cpy (const ggml_tensor * src ,ggml_tensor * dst ,const ggml_tensor * sync_tensor ,uint32_t fence_seq ) { 2980const bool with_fence = sync_tensor != nullptr ; 2981htp_opnode cpy_node (with_fence ?HTP_OP_CPY_FENCE :HTP_OP_CPY ); 2982 2983ggml_tensor * node = cpy_node .add_dummy (* dst ); 2984node -> op = GGML_OP_CPY ; 2985node -> src [0 ]= const_cast < ggml_tensor *> (src ); 2986node -> src [1 ]= with_fence ?cpy_node .add_dummy (* sync_tensor ) :nullptr ; 2987if (with_fence ) { 2988node -> op_params [0 ]= (int32_t )fence_seq ; 2989 } 2990 2991cpy_node .init (node ); 2992if (with_fence ) { 2993cpy_node .name = "CPY+FENCE" ; 2994 } 2995this -> enqueue_op (cpy_node ); 2996} 2997 2998void ggml_hexagon_session::enqueue_fence (const ggml_tensor * sync_tensor ,uint32_t fence_seq ,bool wait ) { 2999htp_opnode sync_node (HTP_OP_FENCE ); 3000 3001ggml_tensor * node = sync_node .add_dummy (* sync_tensor ); 3002node -> op = GGML_OP_NONE ; 3003node -> src [0 ]= node ; 3004node -> op_params [0 ]= (int32_t )fence_seq ; 3005node -> op_params [1 ]= wait ?0 :1 ; 3006 3007sync_node .init (node ); 3008sync_node .name = wait ?"FENCE_WAIT" :"FENCE_SIGNAL" ; 3009this -> enqueue_op (sync_node ); 3010} 3011 3012static bool ggml_hexagon_precompute_allreduce_params ( 3013const struct ggml_hexagon_session * sess , 3014const struct ggml_tensor * dst , 3015uint32_t rank , 3016uint32_t n_ranks , 3017bool has_add , 3018bool is_row_bcast , 3019struct htp_allreduce_kernel_params * kparams 3020) { 3021memset (kparams ,0 ,sizeof (* kparams )); 3022kparams -> rank = (int32_t )rank ; 3023kparams -> n_ranks = (int32_t )n_ranks ; 3024kparams -> is_row_bcast = (has_add && is_row_bcast ) ?1 :0 ; 3025 3026const uint32_t nelem = (uint32_t )ggml_nelements (dst ); 3027const uint32_t elem_size = (dst -> type == GGML_TYPE_F16 ) ?sizeof (ggml_fp16_t ) :sizeof (float ); 3028const bool is_contiguous = ggml_is_contiguous (dst ); 3029 3030const uint32_t ne0 = (uint32_t )dst -> ne [0 ]; 3031const uint32_t ne1 = (uint32_t ) (dst -> ne [1 ]* dst -> ne [2 ]* dst -> ne [3 ]); 3032kparams -> ne0 = (int32_t )ne0 ; 3033kparams -> ne1 = (int32_t )ne1 ; 3034 3035const bool use_1d = is_contiguous && !(has_add && is_row_bcast && ne1 > 1 ); 3036 3037if (has_add ) { 3038kparams -> n_dsts = 1 ; 3039if (use_1d ) { 3040kparams -> rank_elem_start = 0 ; 3041kparams -> rank_nelem = (int32_t )nelem ; 3042 }else { 3043kparams -> rank_elem_start = 0 ; 3044kparams -> rank_nelem = (int32_t )ne1 ; 3045 } 3046 }else { 3047kparams -> n_dsts = (int32_t )n_ranks ; 3048if (use_1d ) { 3049const uint32_t rank_chunk_elems = hex_round_up ((nelem + n_ranks - 1 ) /n_ranks ,128 ); 3050const uint32_t rank_elem_start = (std::min )(rank * rank_chunk_elems ,nelem ); 3051const uint32_t rank_elem_end = (std::min )(rank_elem_start + rank_chunk_elems ,nelem ); 3052const uint32_t rank_nelem = rank_elem_end - rank_elem_start ; 3053kparams -> rank_elem_start = (int32_t )rank_elem_start ; 3054kparams -> rank_nelem = (int32_t )rank_nelem ; 3055 }else { 3056const uint32_t rank_chunk_rows = (ne1 + n_ranks - 1 ) /n_ranks ; 3057const uint32_t rank_r0 = (std::min )(rank * rank_chunk_rows ,ne1 ); 3058const uint32_t rank_r1 = (std::min )(rank_r0 + rank_chunk_rows ,ne1 ); 3059const uint32_t rank_nrows = rank_r1 - rank_r0 ; 3060kparams -> rank_elem_start = (int32_t )rank_r0 ; 3061kparams -> rank_nelem = (int32_t )rank_nrows ; 3062 } 3063 } 3064 3065if (use_1d ) { 3066const uint32_t rank_nelem = (uint32_t )kparams -> rank_nelem ; 3067const uint32_t n_threads = (std::min )((uint32_t )sess -> n_threads , (std::max )(1u ,rank_nelem /128 )); 3068kparams -> n_threads = n_threads ; 3069const size_t n_vtcm_buffers = htp_allreduce_vtcm_buffer_count (n_ranks ,n_threads ,has_add ,is_row_bcast ); 3070 3071uint32_t block_elems = 65536 ; 3072if (block_elems > rank_nelem /n_threads && rank_nelem /n_threads > 128 ) { 3073block_elems = hex_round_up (rank_nelem / (n_threads * 2 ),128 ); 3074 } 3075block_elems = (std::max )(128u ,block_elems ); 3076 3077kparams -> block_elems = block_elems ; 3078kparams -> vtcm_size_per_thread = 2 * block_elems * elem_size ; 3079kparams -> vtcm_size = n_vtcm_buffers * kparams -> vtcm_size_per_thread ; 3080 3081while ((size_t )kparams -> vtcm_size > sess -> vtcm_size && block_elems > 128 ) { 3082const size_t max_bytes_per_buf = sess -> vtcm_size / (n_vtcm_buffers * 2 ); 3083block_elems = (uint32_t )hex_align_down ((size_t ) (max_bytes_per_buf /elem_size ),128 ); 3084if (block_elems < 128 )break ; 3085kparams -> block_elems = block_elems ; 3086kparams -> vtcm_size_per_thread = 2 * block_elems * elem_size ; 3087kparams -> vtcm_size = n_vtcm_buffers * kparams -> vtcm_size_per_thread ; 3088 } 3089 3090if (sess -> vtcm_size < (size_t )kparams -> vtcm_size || block_elems < 128 ) { 3091HEX_VERBOSE ("ggml-hex: %s allreduce 1D solver failed to fit VTCM (%d > %zu)\n" , 3092sess -> c_name (),kparams -> vtcm_size ,sess -> vtcm_size ); 3093return false; 3094 } 3095 3096kparams -> elems_per_thread = hex_round_up ((rank_nelem + n_threads - 1 ) /n_threads ,block_elems ); 3097kparams -> kernel_type = HTP_ALLREDUCE_KERNEL_DMA_1D ; 3098return true; 3099 }else { 3100const uint32_t rank_nrows = (uint32_t )kparams -> rank_nelem ; 3101const uint32_t n_threads = (std::min )((uint32_t )sess -> n_threads , (std::max )(1u ,rank_nrows )); 3102kparams -> n_threads = n_threads ; 3103const size_t n_vtcm_buffers = htp_allreduce_vtcm_buffer_count (n_ranks ,n_threads ,has_add ,is_row_bcast ); 3104 3105const uint32_t row_bytes = ne0 * elem_size ; 3106const uint32_t row_size_aligned = (uint32_t )hex_align_up (row_bytes ,128 ); 3107kparams -> row_size_aligned = row_size_aligned ; 3108 3109const uint32_t nrows_per_thread = (rank_nrows + n_threads - 1 ) /n_threads ; 3110uint32_t block_rows = (std::min )(128u ,nrows_per_thread ); 3111block_rows = (std::max )(1u ,block_rows ); 3112kparams -> block_elems = block_rows ; 3113 3114kparams -> vtcm_size_per_thread = 2 * (block_rows * row_size_aligned ); 3115kparams -> vtcm_size = n_vtcm_buffers * kparams -> vtcm_size_per_thread ; 3116 3117while ((size_t )kparams -> vtcm_size > sess -> vtcm_size && block_rows > 1 ) { 3118const size_t max_rows_per_buf = sess -> vtcm_size / (n_vtcm_buffers * 2 * row_size_aligned ); 3119block_rows = (std::max )(1u , (uint32_t )max_rows_per_buf ); 3120kparams -> block_elems = block_rows ; 3121kparams -> vtcm_size_per_thread = 2 * (block_rows * row_size_aligned ); 3122kparams -> vtcm_size = n_vtcm_buffers * kparams -> vtcm_size_per_thread ; 3123if (max_rows_per_buf == 0 )break ; 3124 } 3125 3126if (sess -> vtcm_size < (size_t )kparams -> vtcm_size || block_rows < 1 ) { 3127HEX_VERBOSE ("ggml-hex: %s allreduce 2D solver failed to fit VTCM (%d > %zu)\n" , 3128sess -> c_name (),kparams -> vtcm_size ,sess -> vtcm_size ); 3129return false; 3130 } 3131 3132kparams -> elems_per_thread = nrows_per_thread ; 3133kparams -> kernel_type = HTP_ALLREDUCE_KERNEL_DMA_2D ; 3134return true; 3135 } 3136} 3137 3138void ggml_hexagon_session::enqueue_allreduce ( 3139const ggml_tensor * dst , 3140const std::vector < const ggml_tensor *> & src_tensors , 3141const std::vector < const ggml_tensor *> & sync_tensors , 3142uint32_t rank , 3143uint32_t n_ranks , 3144uint32_t fence_seq_entry , 3145uint32_t fence_seq_exit 3146) { 3147htp_opnode ar_node (HTP_OP_ALLREDUCE ); 3148 3149ggml_tensor * node = ar_node .add_dummy (* dst ); 3150node -> op = GGML_OP_NONE ; 3151node -> op_params [0 ]= (int32_t )fence_seq_entry ; 3152node -> op_params [1 ]= (int32_t )fence_seq_exit ; 3153 3154ar_node .init (node ); 3155 3156ar_node .inputs .clear (); 3157for (size_t i = 0 ;i < src_tensors .size ();i ++ ) { 3158ar_node .inputs .push_back (src_tensors [i ]); 3159 } 3160for (size_t i = 0 ;i < sync_tensors .size ();i ++ ) { 3161ar_node .inputs .push_back (ar_node .add_dummy (* sync_tensors [i ])); 3162 } 3163 3164ar_node .outputs .clear (); 3165for (size_t i = 0 ;i < src_tensors .size ();i ++ ) { 3166ar_node .outputs .push_back (src_tensors [i ]); 3167 } 3168 3169ggml_hexagon_precompute_allreduce_params ( 3170this ,dst ,rank ,n_ranks , false, false, 3171 (struct htp_allreduce_kernel_params * )ar_node .kernel_params 3172 ); 3173 3174ar_node .name = "ALLREDUCE" ; 3175this -> enqueue_op (ar_node ); 3176} 3177 3178bool ggml_hexagon_session::clone_buffer (const ggml_hexagon_shared_buffer * sbuf ) 3179{ 3180GGML_ASSERT (sbuf && sbuf -> mem ); 3181if (sbuf -> sess == this )return true; 3182 3183auto mem = sbuf -> mem ; 3184int fd = mem -> fd ; 3185 3186GGML_ASSERT (fd >=0 ); 3187 3188if (this -> cloned_buffers .find (fd )!= this -> cloned_buffers .end ())return true; 3189 3190HEX_VERBOSE ("ggml-hex: %s clone-buffer: %s base %p size %zu fd %d\n" ,this -> name .c_str (), 3191sbuf -> c_name (),sbuf -> base (),sbuf -> size (),fd ); 3192 3193auto clone = std::make_unique < ggml_hexagon_shared_buffer > (this ,* sbuf ); 3194try { 3195clone -> mmap (); 3196 }catch (const std::exception & exc ) { 3197GGML_LOG_ERROR ("ggml-hex: %s lazy mapping of buffer context failed: %s\n" ,this -> c_name (),exc .what ()); 3198return false; 3199 } 3200 3201this -> cloned_buffers [fd ]= std::move (clone ); 3202mem -> mapped_clones .insert (this ); 3203return true; 3204} 3205 3206void ggml_hexagon_session::release_buffer (const ggml_hexagon_shared_buffer * sbuf ) { 3207GGML_ASSERT (sbuf && sbuf -> mem ); 3208 3209auto mem = sbuf -> mem ; 3210int fd = mem -> fd ; 3211 3212GGML_ASSERT (fd >=0 ); 3213 3214auto it = this -> cloned_buffers .find (fd ); 3215if (it != this -> cloned_buffers .end ()) { 3216auto clone = std::move (it -> second ); 3217this -> cloned_buffers .erase (it ); 3218 } 3219mem -> mapped_clones .erase (this ); 3220} 3221 3222void ggml_hexagon_session::unclone_buffer (const ggml_hexagon_shared_buffer * sbuf ) { 3223GGML_ASSERT (sbuf && sbuf -> mem ); 3224 3225auto mem = sbuf -> mem ; 3226 std::vector < ggml_hexagon_session *> sessions (mem -> mapped_clones .begin (),mem -> mapped_clones .end ()); 3227 3228for (auto * sess :sessions ) { 3229sess -> release_buffer (sbuf ); 3230 } 3231} 3232 3233static size_t ggml_hexagon_measure_max_vmem (ggml_hexagon_session * sess ) { 3234// Allocate a bunch pinned buffers till failure. 3235// This is kind of expensive but handy for figuring out exactly how much we can mmap on a specific device. 3236// Typically we're going to allocate all/most of these buffers anyway for the model weights. 3237 3238 std::vector < ggml_hexagon_shared_buffer *> sbufs ; 3239 3240const size_t MiB = 1024 * 1024 ; 3241const size_t GiB = MiB * 1024 ; 3242 3243size_t vmem = 0 ; 3244size_t step = 256u * MiB ; 3245 3246try { 3247sbufs .push_back (new ggml_hexagon_shared_buffer (sess ,GiB , true));vmem += GiB ; 3248sbufs .push_back (new ggml_hexagon_shared_buffer (sess ,GiB , true));vmem += GiB ; 3249sbufs .push_back (new ggml_hexagon_shared_buffer (sess ,GiB , true));vmem += GiB ; 3250 3251while (1 ) { 3252sbufs .push_back (new ggml_hexagon_shared_buffer (sess ,step , true)); 3253vmem += step ; 3254 } 3255 }catch (...) { } 3256 3257for (auto b :sbufs ) {delete b ; } 3258 3259return vmem - step ;// backoff to account for overhead from internal mappings 3260} 3261 3262void ggml_hexagon_session::allocate (const ggml_hexagon_device_config & config )noexcept (false) { 3263int phys_idx = config .physical_idx ; 3264int virt_idx = config .virtual_idx ; 3265 3266this -> valid_session = false; 3267this -> valid_handle = false; 3268this -> valid_queue = false; 3269this -> valid_iface = false; 3270 3271this -> name = config .name ; 3272this -> phys_idx = phys_idx ; 3273this -> virt_idx = virt_idx ; 3274this -> domain_id = config .domain_id ; 3275this -> session_id = 0 ; 3276this -> batch_req_seq = 0 ; 3277this -> batch_rsp_seq = 0 ; 3278this -> last_error = HTP_STATUS_OK ; 3279 3280GGML_LOG_DEBUG ("ggml-hex: %s allocating new session : domain %u phys-idx %u virt-idx %u\n" ,this -> name .c_str (),this -> domain_id ,phys_idx ,virt_idx ); 3281 3282if (config .domain_id < 0 || config .domain_name .empty ()) { 3283GGML_LOG_ERROR ("ggml-hex: %s: invalid physical CDSP core %d\n" ,config .name .c_str (),config .physical_idx ); 3284throw std::runtime_error ("ggml-hex: invalid physical CDSP core" ); 3285 } 3286 3287const std::string & dom_name = config .domain_name ; 3288 3289// Create new session if virtual_idx > 0 3290if (virt_idx > 0 ) { 3291struct remote_rpc_reserve_new_session n {}; 3292n .domain_name_len = dom_name .size (); 3293n .domain_name = const_cast < char *> (dom_name .c_str ()); 3294n .session_name = const_cast < char *> (this -> name .c_str ()); 3295n .session_name_len = this -> name .size (); 3296n .session_id = virt_idx ; 3297 3298int err = remote_session_control (FASTRPC_RESERVE_NEW_SESSION , (void * )& n ,sizeof (n )); 3299if (err != AEE_SUCCESS ) { 3300GGML_LOG_ERROR ("ggml-hex: %s failed to reserve new session (physical %d, virtual %d) : error 0x%x\n" , 3301this -> c_name (),phys_idx ,virt_idx ,err ); 3302throw std::runtime_error ("ggml-hex: remote_session_control(new-sess) failed (see log for details)" ); 3303 } 3304 3305// Save the IDs 3306this -> session_id = n .session_id ; 3307this -> domain_id = n .effective_domain_id ; 3308this -> valid_session = true; 3309 }else { 3310struct remote_rpc_effective_domain_id eff {}; 3311eff .domain_name = const_cast < char *> (dom_name .c_str ()); 3312eff .domain_name_len = dom_name .size (); 3313eff .session_id = 0 ; 3314 3315int err = remote_session_control (FASTRPC_GET_EFFECTIVE_DOMAIN_ID , (void * )& eff ,sizeof (eff )); 3316if (err == AEE_SUCCESS ) { 3317this -> domain_id = eff .effective_domain_id ; 3318 }else { 3319GGML_LOG_DEBUG ("ggml-hex: %s FASTRPC_GET_EFFECTIVE_DOMAIN_ID returned 0x%x, using domain_id %d\n" , 3320this -> name .c_str (),err ,this -> domain_id ); 3321 } 3322 } 3323 3324// Enable unsigned modules 3325 { 3326struct remote_rpc_control_unsigned_module u ; 3327u .domain = this -> domain_id ; 3328u .enable = 1 ; 3329int err = remote_session_control (DSPRPC_CONTROL_UNSIGNED_MODULE , (void * )& u ,sizeof (u )); 3330if (err != AEE_SUCCESS ) { 3331GGML_LOG_ERROR ("ggml-hex: %s failed to enable unsigned PD : error 0x%x\n" ,this -> c_name (),err ); 3332throw std::runtime_error ("ggml-hex: remote_session_control(unsign) failed (see log for details)" ); 3333 } 3334 } 3335 3336char session_uri [256 ]; 3337 { 3338char htp_uri [256 ]; 3339snprintf (htp_uri ,sizeof (htp_uri ),"file:///libggml-htp-v%u.so?htp_iface_skel_handle_invoke&_modver=1.0" ,opt_arch ); 3340 3341struct remote_rpc_get_uri u = {}; 3342u .session_id = this -> session_id ; 3343u .domain_name = const_cast < char *> (dom_name .c_str ()); 3344u .domain_name_len = dom_name .size (); 3345u .module_uri = const_cast < char *> (htp_uri ); 3346u .module_uri_len = strlen (htp_uri ); 3347u .uri = session_uri ; 3348u .uri_len = sizeof (session_uri ); 3349 3350int err = remote_session_control (FASTRPC_GET_URI , (void * )& u ,sizeof (u )); 3351if (err != AEE_SUCCESS ) { 3352snprintf (session_uri ,sizeof (session_uri ),"%s&_dom=%s&_session=%u" , 3353htp_uri ,dom_name .c_str (),this -> session_id ); 3354 3355GGML_LOG_WARN ("ggml-hex: %s failed to get URI (physical %d, virtual %d) : error 0x%x. Falling back to single session URI: %s\n" , 3356this -> c_name (),phys_idx ,virt_idx ,err ,session_uri ); 3357 } 3358 } 3359 3360// Open session 3361int err = htp_iface_open (session_uri ,& this -> handle ); 3362if (err != AEE_SUCCESS ) { 3363GGML_LOG_ERROR ("ggml-hex: %s failed to open session : uri %s error 0x%x\n" ,this -> c_name (),session_uri ,err ); 3364throw std::runtime_error ("ggml-hex: failed to open session (see log for details)" ); 3365 } 3366 3367this -> valid_handle = true; 3368 3369// Query HW info and resolve session options 3370this -> max_bufsize = opt_mbuf ; 3371 { 3372unsigned int hw_n_threads = 0 ; 3373unsigned int hw_n_hvx = 0 ; 3374unsigned int hw_n_hmx = 0 ; 3375unsigned long long hw_vtcm_size = 0 ; 3376int hw_err = htp_iface_hwinfo (this -> handle ,& hw_n_threads ,& hw_n_hvx ,& hw_n_hmx ,& hw_vtcm_size ); 3377if (hw_err == 0 ) { 3378const uint32_t max_n_threads = (std::min )((uint32_t )HTP_MAX_NTHREADS , (uint32_t )hw_n_threads ); 3379this -> n_threads = opt_nhvx > 0 ? (uint32_t ) (std::min )(opt_nhvx , (size_t )max_n_threads ) :max_n_threads ; 3380this -> n_hvx = this -> n_threads ; 3381this -> n_hmx = (opt_nhmx != 0 ) ? (uint32_t )hw_n_hmx :0 ; 3382this -> vtcm_size = (uint64_t )hw_vtcm_size ; 3383GGML_LOG_INFO ("ggml-hex: %s hwinfo: threads %u, hvx %u, hmx %u, vtcm %llu MB\n" , 3384this -> c_name (),this -> n_threads ,this -> n_hvx ,this -> n_hmx , 3385 (unsigned long long )(this -> vtcm_size / (1024 * 1024 ))); 3386 }else { 3387GGML_LOG_WARN ("ggml-hex: %s failed to query hwinfo (0x%x), using defaults\n" ,this -> c_name (),hw_err ); 3388const uint32_t default_n_threads = (std::min )(8u , (uint32_t )HTP_MAX_NTHREADS ); 3389this -> n_threads = opt_nhvx > 0 ? (uint32_t ) (std::min )(opt_nhvx , (size_t )HTP_MAX_NTHREADS ) :default_n_threads ; 3390this -> n_hvx = this -> n_threads ; 3391this -> n_hmx = (opt_nhmx != 0 ) ?1 :0 ; 3392this -> vtcm_size = 8 * 1024 * 1024 ; 3393 } 3394 } 3395 3396// Enable FastRPC QoS mode 3397 { 3398struct remote_rpc_control_latency l ; 3399l .enable = 1 ; 3400 3401int err = remote_handle64_control (this -> handle ,DSPRPC_CONTROL_LATENCY , (void * )& l ,sizeof (l )); 3402if (err != 0 ) { 3403GGML_LOG_WARN ("ggml-hex: failed to enable fastrpc QOS mode: 0x%08x\n" , (unsigned )err ); 3404 } 3405 } 3406 3407GGML_LOG_INFO ("ggml-hex: %s new session : session-id %d domain-id %d uri %s handle 0x%lx\n" ,this -> c_name (), 3408this -> session_id ,this -> domain_id ,session_uri , (unsigned long )this -> handle ); 3409 3410const size_t req_q_size = (sizeof (htp_opbatch_req )* opt_opqueue * 2 )+ 1024 ; 3411const size_t rsp_q_size = (sizeof (htp_opbatch_rsp )* opt_opqueue * 2 )+ 1024 ; 3412 3413// Now let's setup the DSP queue 3414err = dspqueue_create (this -> domain_id , 34150 ,// Flags 3416req_q_size ,// Request queue size (in bytes) 3417rsp_q_size ,// Response queue size (in bytes) 3418nullptr ,// Read packet callback (we handle reads explicitly) 3419nullptr ,// Error callback (we handle errors during reads) 3420 (void * )this ,// Callback context 3421& queue ); 3422if (err != 0 ) { 3423GGML_LOG_ERROR ("ggml-hex: %s dspqueue_create failed: 0x%08x\n" ,this -> name .c_str (), (unsigned )err ); 3424throw std::runtime_error ("ggml-hex: failed to create dspqueue (see log for details)" ); 3425 } 3426 3427this -> valid_queue = true; 3428 3429// Export queue for use on the DSP 3430err = dspqueue_export (queue ,& this -> queue_id ); 3431if (err != 0 ) { 3432GGML_LOG_ERROR ("ggml-hex: dspqueue_export failed: 0x%08x\n" , (unsigned )err ); 3433throw std::runtime_error ("ggml-hex: dspqueue export failed (see log for details)" ); 3434 } 3435 3436if (opt_etm ) { 3437err = htp_iface_etm (this -> handle ,1 ); 3438if (err != 0 ) { 3439GGML_LOG_ERROR ("ggml-hex: failed to enable ETM tracing: 0x%08x\n" , (unsigned )err ); 3440 } 3441 } 3442 3443// Allocate buffers and state for op batching 3444this -> op_queue = new ggml_hexagon_opqueue (this ,opt_opbatch ,opt_opqueue ); 3445 3446this -> fence_buf = new ggml_hexagon_fence_buffer (this ,& dev_ctx -> fence_buffer_type ,64 * 1024 ); 3447if (this -> mdev .count > 1 ) { 3448this -> mdev_fence_slot = this -> alloc_fence (this -> mdev .count ); 3449 } 3450 3451if (!opt_vmem ) { 3452opt_vmem = ggml_hexagon_measure_max_vmem (this ); 3453GGML_LOG_INFO ("ggml-hex: %s measured max vmem %zu\n" ,this -> c_name (),opt_vmem ); 3454 } 3455const size_t shm_size = this -> op_queue -> shm_size (); 3456this -> max_vmem = (opt_vmem > shm_size ) ? (opt_vmem - shm_size ) :opt_vmem ; 3457 3458this -> op_batch = new ggml_hexagon_opbatch (this ,opt_opbatch ,this -> max_vmem ); 3459 3460// Start dspqueue/opbatch processing 3461err = htp_iface_start (this -> handle ,this -> session_id ,this -> queue_id ,this -> n_threads ,opt_nhmx ,this -> max_vmem ); 3462if (err != 0 ) { 3463GGML_LOG_ERROR ("ggml-hex: %s failed to start session: 0x%08x\n" ,this -> c_name (), (unsigned )err ); 3464throw std::runtime_error ("ggml-hex: iface start failed (see log for details)" ); 3465 } 3466this -> valid_iface = true; 3467 3468if (opt_profile ) { 3469htp_iface_pmu_conf pmu_conf {}; 3470 std::copy (opt_pmu_evt .begin (),opt_pmu_evt .end (),pmu_conf .events ); 3471 3472err = htp_iface_profiler (this -> handle ,opt_profile ,& pmu_conf ); 3473if (err != 0 ) { 3474GGML_LOG_ERROR ("ggml-hex: failed to enable profiling: 0x%08x\n" , (unsigned )err ); 3475 } 3476 } 3477} 3478 3479void ggml_hexagon_session::release ()noexcept (true) { 3480GGML_LOG_INFO ("ggml-hex: releasing session: %s\n" ,this -> name .c_str ()); 3481 3482this -> mdev .sessions .clear (); 3483 3484int err ; 3485 3486if (this -> valid_iface ) { 3487// Stop dspqueue/opbatch processing 3488err = htp_iface_stop (this -> handle ); 3489if (err != 0 ) { 3490GGML_ABORT ("ggml-hex: htp_iface_stop failed: 0x%08x\n" , (unsigned )err ); 3491 } 3492 } 3493 3494delete this -> op_batch ; 3495delete this -> op_queue ; 3496for (auto & it :this -> cpy_fence_slots ) { 3497free_fence ((void * )it .second ,1 ); 3498 } 3499this -> cpy_fence_slots .clear (); 3500 3501if (this -> fence_buf ) { 3502unclone_buffer (this -> fence_buf ); 3503delete this -> fence_buf ; 3504this -> fence_buf = nullptr ; 3505 } 3506while (!this -> cloned_buffers .empty ()) { 3507release_buffer (this -> cloned_buffers .begin ()-> second .get ()); 3508 } 3509 3510if (opt_etm ) { 3511err = htp_iface_etm (this -> handle ,0 ); 3512if (err != 0 ) { 3513GGML_LOG_ERROR ("ggml-hex: warn : failed to disable ETM tracing: 0x%08x\n" , (unsigned )err ); 3514 } 3515 } 3516 3517if (opt_profile ) { 3518htp_iface_pmu_conf pmu_conf {}; 3519err = htp_iface_profiler (this -> handle ,0 ,& pmu_conf ); 3520if (err != 0 ) { 3521GGML_LOG_ERROR ("ggml-hex: warn : failed to disable profiling: 0x%08x\n" , (unsigned )err ); 3522 } 3523 } 3524 3525if (this -> valid_queue ) { 3526err = dspqueue_close (queue ); 3527if (err != 0 ) { 3528GGML_ABORT ("ggml-hex: dspqueue_close failed: 0x%08x\n" , (unsigned )err ); 3529 } 3530 } 3531 3532if (this -> valid_handle ) { 3533htp_iface_close (this -> handle ); 3534 } 3535} 3536 3537ggml_hexagon_session::ggml_hexagon_session (const ggml_hexagon_device_config & config ,ggml_backend_dev_t dev ,uint32_t mdev_idx ,uint32_t mdev_count )noexcept (false) { 3538this -> dev = dev ; 3539this -> dev_ctx = static_cast < ggml_backend_hexagon_device_context *> (dev -> context ); 3540this -> mdev .idx = mdev_idx ; 3541this -> mdev .count = mdev_count > 0 ?mdev_count : (uint32_t ) (1 + config .mdev_group .size ()); 3542op_batch = nullptr ; 3543op_queue = nullptr ; 3544fence_buf = nullptr ; 3545fence_seq = ((uintptr_t )this )& 0xFFFF ; 3546 3547try { 3548allocate (config ); 3549if (this -> mdev .idx == 0 && !config .mdev_group .empty ()) { 3550for (size_t i = 0 ;i < config .mdev_group .size ();i ++ ) { 3551this -> mdev .sessions .push_back (std::make_unique < ggml_hexagon_session > ( 3552config .mdev_group [i ],this -> dev , (uint32_t ) (i + 1 ),this -> mdev .count )); 3553 } 3554 } 3555 }catch (const std::exception & exc ) { 3556release (); 3557throw ; 3558 } 3559} 3560 3561ggml_hexagon_session::~ggml_hexagon_session ()noexcept (true) { 3562release (); 3563} 3564 3565// ** backend interface 3566 3567static bool ggml_hexagon_flash_attn_is_hmx_eligible ( 3568const struct ggml_hexagon_session * sess , 3569const struct ggml_tensor * q , 3570const struct ggml_tensor * k , 3571const struct ggml_tensor * v , 3572const struct ggml_tensor * sinks 3573) { 3574if (sess -> n_hmx == 0 ) { 3575return false; 3576 } 3577 3578if (opt_fa_select < 2 ) { 3579return false; 3580 } 3581 3582if ((k -> type != GGML_TYPE_F16 && k -> type != GGML_TYPE_Q8_0 )|| 3583 (v -> type != GGML_TYPE_F16 && v -> type != GGML_TYPE_Q8_0 )) { 3584return false; 3585 } 3586 3587const uint32_t DK = q -> ne [0 ]; 3588const uint32_t DV = v -> ne [0 ]; 3589 3590if (DK %64 != 0 || DV %64 != 0 ) { 3591return false; 3592 } 3593 3594// Fall back to HVX for small token counts if head dimension is small (DK <= 128) 3595const uint32_t neq1 = q -> ne [1 ]; 3596if (DK <=128 && neq1 < 5 ) { 3597return false; 3598 } 3599 3600return true; 3601 3602GGML_UNUSED (sinks ); 3603} 3604 3605static bool ggml_hexagon_precompute_flash_attn_params ( 3606const struct ggml_hexagon_session * sess , 3607const struct ggml_tensor * op , 3608struct htp_fa_kernel_params * kparams 3609) { 3610if (opt_fa_select < 1 ) { 3611return false; 3612 } 3613 3614memset (kparams ,0 ,sizeof (* kparams )); 3615 3616const struct ggml_tensor * q = op -> src [0 ]; 3617const struct ggml_tensor * k = op -> src [1 ]; 3618const struct ggml_tensor * v = op -> src [2 ]; 3619const struct ggml_tensor * mask = op -> src [3 ]; 3620const struct ggml_tensor * dst = op ; 3621 3622const uint32_t neq0 = q -> ne [0 ];// head_dim (DK) 3623const uint32_t neq1 = q -> ne [1 ];// n_tokens 3624const uint32_t neq2 = q -> ne [2 ];// n_heads 3625 3626const uint32_t nek1 = k -> ne [1 ];// kv_len 3627 3628const uint32_t nev0 = v -> ne [0 ];// head_dim (DV) 3629 3630const uint32_t DK = neq0 ; 3631const uint32_t DV = nev0 ; 3632 3633const uint32_t n_kv_heads = k -> ne [2 ]; 3634const uint32_t G = neq2 /n_kv_heads ; 3635 3636float scale = 1.0f ; 3637float max_bias = 0.0f ; 3638float logit_softcap = 0.0f ; 3639memcpy (& scale ,& op -> op_params [0 ],sizeof (float )); 3640memcpy (& max_bias ,& op -> op_params [1 ],sizeof (float )); 3641memcpy (& logit_softcap ,& op -> op_params [2 ],sizeof (float )); 3642 3643if (logit_softcap != 0.0f ) { 3644scale /=logit_softcap ; 3645 } 3646 3647kparams -> scale = scale ; 3648kparams -> max_bias = max_bias ; 3649kparams -> logit_softcap = logit_softcap ; 3650 3651kparams -> is_q_fp32 = (q -> type == GGML_TYPE_F32 ) ?1 :0 ; 3652kparams -> is_dst_fp32 = (dst -> type == GGML_TYPE_F32 ) ?1 :0 ; 3653kparams -> G = G ; 3654 3655const uint32_t n_head = q -> ne [2 ]; 3656kparams -> n_head_log2 = 1u << (uint32_t ) std::floor (std::log2 (n_head )); 3657kparams -> m0 = std::pow (2.0f ,- (max_bias ) /kparams -> n_head_log2 ); 3658kparams -> m1 = std::pow (2.0f ,- (max_bias /2.0f ) /kparams -> n_head_log2 ); 3659 3660// Check HMX eligibility 3661const struct ggml_tensor * sinks = op -> src [4 ]; 3662if (ggml_hexagon_flash_attn_is_hmx_eligible (sess ,q ,k ,v ,sinks )) { 3663size_t Br = 0 ,Bc = 0 ; 3664int ret = hmx_fa_find_chunk_size (& Br ,& Bc ,G ,DK ,DV ,neq1 ,nek1 ,sess -> vtcm_size ,sess -> n_threads ,kparams -> is_q_fp32 != 0 ); 3665if (ret == 0 ) { 3666kparams -> kernel_type = HTP_FA_KERNEL_HMX ; 3667kparams -> Br = Br ; 3668kparams -> Bc = Bc ; 3669kparams -> n_kv_blocks = (nek1 + Bc - 1 ) /Bc ; 3670kparams -> n_threads = (kparams -> n_kv_blocks >=3 && sess -> n_threads >=2 ) ?sess -> n_threads :1 ; 3671 3672kparams -> u .hmx .g_br = hex_align_up (G * Br ,32 ); 3673kparams -> u .hmx .pipeline = (kparams -> n_kv_blocks >=3 && sess -> n_threads >=2 ) ?1 :0 ; 3674kparams -> vtcm_size = hmx_fa_compute_vtcm_usage (G ,DK ,DV ,Br ,Bc ,kparams -> n_threads ,kparams -> u .hmx .pipeline != 0 ,kparams -> is_q_fp32 != 0 ); 3675 3676const size_t row_vec_bytes = hex_align_up (Bc * sizeof (uint16_t ),256 ); 3677kparams -> u .hmx .row_buf_stride = row_vec_bytes /128 ;// HVX vector is 128 bytes 3678 3679const size_t m_line_bytes = hex_align_up (Bc * sizeof (uint16_t ),128 ); 3680kparams -> u .hmx .mask_buf_row_stride = m_line_bytes /sizeof (uint16_t ); 3681kparams -> u .hmx .mask_broadcast = (mask != nullptr && mask -> ne [2 ]== 1 ) ?1 :0 ; 3682kparams -> u .hmx .div_G = init_fastdiv_values (G ); 3683if (mask ) { 3684kparams -> src3_div2 = init_fastdiv_values (mask -> ne [2 ]); 3685kparams -> src3_div3 = init_fastdiv_values (mask -> ne [3 ]); 3686 } 3687 3688kparams -> qrows = 0 ; 3689kparams -> qrows_per_thread = 0 ; 3690return true; 3691 } 3692 } 3693 3694// Fallback to HVX 3695kparams -> kernel_type = HTP_FA_KERNEL_HVX ; 3696kparams -> Br = 1 ; 3697kparams -> Bc = 64 ;// FLASH_ATTN_BLOCK_SIZE 3698kparams -> n_kv_blocks = (k -> ne [1 ]+ 64 - 1 ) /64 ; 3699kparams -> n_threads = sess -> n_threads ; 3700 3701const size_t size_q_row_padded = hex_round_up (q -> ne [0 ]* (kparams -> is_q_fp32 ?4 :2 ),128 ); 3702const size_t size_k_row_padded = hex_round_up (k -> ne [0 ]* 2 ,128 ); 3703const size_t size_v_row_padded = hex_round_up (v -> ne [0 ]* 2 ,128 ); 3704 3705kparams -> vtcm_size = hvx_fa_compute_vtcm_usage (DK ,DV ,kparams -> is_q_fp32 != 0 ,mask != nullptr ,sess -> n_threads ); 3706 3707kparams -> u .hvx .size_q_row_padded = size_q_row_padded ; 3708kparams -> u .hvx .size_k_row_padded = size_k_row_padded ; 3709kparams -> u .hvx .size_v_row_padded = size_v_row_padded ; 3710kparams -> u .hvx .src0_div21 = init_fastdiv_values (q -> ne [2 ]* q -> ne [1 ]); 3711kparams -> u .hvx .src0_div1 = init_fastdiv_values (q -> ne [1 ]); 3712kparams -> broadcast_rk2 = init_fastdiv_values (q -> ne [2 ]/k -> ne [2 ]); 3713kparams -> broadcast_rk3 = init_fastdiv_values (q -> ne [3 ]/k -> ne [3 ]); 3714kparams -> broadcast_rv2 = init_fastdiv_values (q -> ne [2 ]/v -> ne [2 ]); 3715kparams -> broadcast_rv3 = init_fastdiv_values (q -> ne [3 ]/v -> ne [3 ]); 3716if (mask ) { 3717kparams -> src3_div2 = init_fastdiv_values (mask -> ne [2 ]); 3718kparams -> src3_div3 = init_fastdiv_values (mask -> ne [3 ]); 3719 } 3720 3721kparams -> qrows = q -> ne [1 ]* q -> ne [2 ]* q -> ne [3 ]; 3722kparams -> qrows_per_thread = (kparams -> qrows + sess -> n_threads - 1 ) /sess -> n_threads ; 3723 3724return true; 3725} 3726 3727static bool ggml_hexagon_supported_flash_attn_ext (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 3728const struct ggml_tensor * src0 = op -> src [0 ]; 3729const struct ggml_tensor * src1 = op -> src [1 ]; 3730const struct ggml_tensor * src2 = op -> src [2 ]; 3731const struct ggml_tensor * src3 = op -> src [3 ]; 3732const struct ggml_tensor * src4 = op -> src [4 ]; 3733const struct ggml_tensor * dst = op ; 3734 3735// Check for F16/Q8_0 support 3736if ((src0 -> type != GGML_TYPE_F16 && src0 -> type != GGML_TYPE_F32 )|| 3737 (src1 -> type != GGML_TYPE_F16 && src1 -> type != GGML_TYPE_Q8_0 )|| 3738 (src2 -> type != GGML_TYPE_F16 && src2 -> type != GGML_TYPE_Q8_0 )) { 3739return false; 3740 } 3741 3742if (src3 && src3 -> type != GGML_TYPE_F16 ) {// mask 3743return false; 3744 } 3745 3746if (src4 && src4 -> type != GGML_TYPE_F32 ) {// sinks 3747return false; 3748 } 3749 3750// For now we support F32 or F16 output as htp backend often converts output on the fly if needed, 3751// but the op implementation writes to F16 or F32. 3752// Let's assume dst can be F32 or F16. 3753if (dst -> type != GGML_TYPE_F32 && dst -> type != GGML_TYPE_F16 ) { 3754return false; 3755 } 3756 3757if (dst -> ne [3 ]!= 1 ) { 3758return false; 3759 } 3760 3761struct htp_fa_kernel_params kparams ; 3762if (!ggml_hexagon_precompute_flash_attn_params (sess ,op ,& kparams )) { 3763return false; 3764 } 3765 3766if ((size_t )kparams .vtcm_size > sess -> vtcm_size ) { 3767HEX_VERBOSE ("ggml-hex: skip flash_attn_ext because VTCM needed (%d) > budget (%zu)\n" , 3768kparams .vtcm_size ,sess -> vtcm_size ); 3769return false; 3770 } 3771 3772return true; 3773} 3774 3775static bool ggml_hexagon_supported_gated_delta_net (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 3776const struct ggml_tensor * q = op -> src [0 ]; 3777const struct ggml_tensor * k = op -> src [1 ]; 3778const struct ggml_tensor * v = op -> src [2 ]; 3779const struct ggml_tensor * g = op -> src [3 ]; 3780const struct ggml_tensor * beta = op -> src [4 ]; 3781const struct ggml_tensor * state = op -> src [5 ]; 3782const struct ggml_tensor * dst = op ; 3783 3784if (q -> type != GGML_TYPE_F32 || k -> type != GGML_TYPE_F32 || v -> type != GGML_TYPE_F32 || 3785g -> type != GGML_TYPE_F32 || beta -> type != GGML_TYPE_F32 || state -> type != GGML_TYPE_F32 || 3786dst -> type != GGML_TYPE_F32 ) { 3787return false; 3788 } 3789 3790if (!ggml_is_contiguous_rows (q )|| !ggml_is_contiguous_rows (k )|| !ggml_is_contiguous_rows (v )|| 3791 !ggml_is_contiguous (g )|| !ggml_is_contiguous (beta )|| !ggml_is_contiguous (state )|| 3792 !ggml_is_contiguous (dst )) { 3793return false; 3794 } 3795 3796const int64_t S_v = v -> ne [0 ]; 3797const int64_t H = v -> ne [1 ]; 3798const int64_t n_tokens = v -> ne [2 ]; 3799const int64_t n_seqs = v -> ne [3 ]; 3800const int64_t K = ggml_get_op_params_i32 (op ,0 ); 3801 3802if (S_v <=0 || S_v > 128 || H <=0 || n_tokens <=0 || n_seqs <=0 ) { 3803return false; 3804 } 3805if (q -> ne [0 ]!= S_v || k -> ne [0 ]!= S_v || q -> ne [1 ] <=0 || k -> ne [1 ] <=0 || 3806q -> ne [2 ]!= n_tokens || k -> ne [2 ]!= n_tokens || q -> ne [3 ] <=0 || k -> ne [3 ] <=0 || 3807 (n_seqs %q -> ne [3 ])!= 0 || (n_seqs %k -> ne [3 ])!= 0 ) { 3808return false; 3809 } 3810if ((g -> ne [0 ]!= 1 && g -> ne [0 ]!= S_v )|| beta -> ne [0 ]!= 1 ) { 3811return false; 3812 } 3813// state holds s0 only [S_v, S_v, H, n_seqs]; K is op param 0. 3814if (ggml_nelements (state )!= S_v * S_v * H * n_seqs ) { 3815return false; 3816 } 3817if (dst -> ne [0 ]!= S_v * H || dst -> ne [1 ]!= n_tokens * n_seqs + S_v * n_seqs * K ) { 3818return false; 3819 } 3820 3821return true; 3822 3823GGML_UNUSED (sess ); 3824} 3825 3826static bool ggml_hexagon_matmul_is_hmx_eligible ( 3827const struct ggml_tensor * src0 , 3828const struct ggml_tensor * src1 , 3829const struct ggml_tensor * dst , 3830int ne01_padded , 3831bool is_matmul_id , 3832bool is_batched 3833) { 3834if (src1 -> type != GGML_TYPE_F32 ) { 3835return false; 3836 } 3837 3838const int ne00 = src0 -> ne [0 ]; 3839const int ne11 = src1 -> ne [1 ]; 3840const int ne12 = src1 -> ne [2 ]; 3841const int wtype = src0 -> type ; 3842 3843// HMX weight tile requires N to be 32-aligned. 3844if (ne01_padded %32 != 0 ) { 3845return false; 3846 } 3847 3848// HMX supports F16, F32, and repack quantized types. 3849if (!ggml_hexagon_is_hmx_weight_type ((ggml_type )wtype )) { 3850return false; 3851 } 3852 3853// HMX paths require K aligned to 32. 3854if (ne00 %32 != 0 ) { 3855return false; 3856 } 3857 3858// Quantized HMX kernels only handle flat 2D matmul (or matmul_id wrapping flat 2D matmuls). 3859if (!is_matmul_id && is_batched && wtype != GGML_TYPE_F16 ) { 3860return false; 3861 } 3862 3863// HMX assumes contiguous row-major layout. 3864if (src0 -> nb [0 ]> src0 -> nb [1 ]|| src1 -> nb [0 ]> src1 -> nb [1 ]) { 3865return false; 3866 } 3867 3868// M alignment: Use HMX when M > HTP_MM_HMX_MIN_NROWS. 3869// For MUL_MAT_ID, src1 shape is [K, n_expert_used, n_tokens, 1], so n_tokens is ne12. 3870const int m = is_matmul_id ?ne12 :ne11 ; 3871if (m <=HTP_MM_HMX_MIN_NROWS ) { 3872return false; 3873 } 3874 3875return true; 3876 3877GGML_UNUSED (dst ); 3878} 3879 3880static bool ggml_hexagon_precompute_hmx_mm_params ( 3881const struct ggml_hexagon_session * sess , 3882const struct ggml_tensor * src0 , 3883const struct ggml_tensor * src1 , 3884const struct ggml_tensor * dst , 3885int wtype , 3886int ne00_padded , 3887int ne01_padded , 3888int ne02 , 3889int ne11 , 3890int ne12 , 3891int ne11_padded , 3892bool is_matmul_id , 3893bool is_batched , 3894size_t vtcm_budget , 3895struct htp_mm_kernel_params * kparams 3896) { 3897const int aligned_tile_size = htp_mm_get_weight_aligned_tile_size (wtype ); 3898const bool pipeline = is_matmul_id ? false :htp_mm_hmx_pipeline (ne11 ); 3899const int n_threads = (int )sess -> n_threads ; 3900const int ne10 = src1 -> ne [0 ]; 3901 3902const bool is_batched_val = is_matmul_id ? false :is_batched ; 3903const int group_size = (ne02 > 0 ?ne12 /ne02 :1 ); 3904 3905size_t m_chunk = 0 ; 3906size_t n_chunk = 0 ; 3907size_t vtcm_size = 0 ; 3908bool use_grouped = false; 3909int act_threads_selected = 0 ; 3910 3911if (is_batched_val && wtype == GGML_TYPE_F16 && group_size > 1 ) { 3912// Try grouped path first 3913const bool use_dma_activation = (src1 -> nb [1 ]/sizeof (float )> (size_t )ne00_padded ); 3914if (htp_mm_hmx_solve_batched_params (wtype ,ne00_padded ,ne01_padded ,ne11 ,group_size ,use_dma_activation ,n_threads ,pipeline ,vtcm_budget ,& m_chunk ,& n_chunk ,& act_threads_selected ,& vtcm_size )) { 3915use_grouped = true; 3916 } 3917 } 3918 3919if (!use_grouped ) { 3920// Fallback to simple 2D path (group_size = 1) 3921const int m_id_rows = (dst && is_matmul_id ) ? (int ) ((size_t )dst -> ne [1 ]* dst -> ne [2 ]) :0 ; 3922if (!htp_mm_hmx_solve_2d_params (wtype ,ne00_padded ,m_id_rows ,ne01_padded ,ne11_padded ,ne11 ,n_threads ,pipeline ,is_matmul_id ,aligned_tile_size ,vtcm_budget ,& m_chunk ,& n_chunk ,& act_threads_selected ,& vtcm_size )) { 3923return false; 3924 } 3925 } 3926 3927kparams -> n_hmx = 1 ; 3928kparams -> pipeline = pipeline ?1 :0 ; 3929kparams -> m_chunk = m_chunk ; 3930kparams -> n_chunk = n_chunk ; 3931kparams -> n_threads = n_threads ; 3932kparams -> n_act_threads = act_threads_selected ; 3933kparams -> tile_size = htp_mm_get_weight_tile_size (wtype ); 3934kparams -> aligned_tile_size = aligned_tile_size ; 3935kparams -> src1_row_size = (wtype == GGML_TYPE_Q4_1 ) ?htp_mm_q8_1_tiled_row_size (ne10 ) :htp_mm_q8_0_tiled_row_size (ne10 ); 3936kparams -> vtcm_size = vtcm_size ; 3937kparams -> vtcm_src0_size = 0 ; 3938kparams -> div_n_act_threads = init_fastdiv_values (act_threads_selected ); 3939kparams -> div_ne00_padded = init_fastdiv_values (ne00_padded ); 3940kparams -> vtcm_src1_size = 0 ; 3941kparams -> vtcm_dst_size = 0 ; 3942 3943if (is_batched && !is_matmul_id ) { 3944kparams -> kernel_type = HTP_MM_KERNEL_HMX_F16_BATCHED ; 3945 }else { 3946kparams -> kernel_type = HTP_MM_KERNEL_HMX_2D ; 3947 } 3948return true; 3949 3950GGML_UNUSED (src0 ); 3951} 3952 3953static void ggml_hexagon_precompute_hvx_mm_params ( 3954const struct ggml_hexagon_session * sess , 3955const struct ggml_tensor * src0 , 3956const struct ggml_tensor * src1 , 3957const struct ggml_tensor * dst , 3958int wtype , 3959int ne02 , 3960int ne03 , 3961int ne10 , 3962int ne11 , 3963int ne12 , 3964int ne13 , 3965bool is_matmul_id , 3966const size_t src2_row_size , 3967size_t vtcm_budget , 3968struct htp_mm_kernel_params * kparams 3969) { 3970kparams -> n_hmx = 0 ; 3971kparams -> n_threads = sess -> n_threads ; 3972 3973const bool is_quant = (wtype != GGML_TYPE_F16 && wtype != GGML_TYPE_F32 ); 3974const int src1_nrows = ne11 * ne12 * ne13 ; 3975 3976if (is_quant ) { 3977// Quantized HVX 3978kparams -> tile_size = htp_mm_get_weight_tile_size (wtype ); 3979kparams -> aligned_tile_size = htp_mm_get_weight_aligned_tile_size (wtype ); 3980 3981const bool k_align = (ne10 %32 == 0 ); 3982 3983if (is_matmul_id ) { 3984kparams -> kernel_type = (src1_nrows < (int )sess -> n_threads ) ?HTP_MM_KERNEL_HVX_QUANT_BLOCK :HTP_MM_KERNEL_HVX_QUANT_ROW ; 3985kparams -> src1_row_size = (wtype == GGML_TYPE_Q4_1 ) ?htp_mm_q8_1_tiled_row_size (ne10 ) :htp_mm_q8_0_tiled_row_size (ne10 ); 3986 3987struct htp_mm_hvx_vtcm_layout L ; 3988uint32_t max_prefetch = (src1_nrows > HTP_MM_HMX_MIN_NROWS ) ?2 :16 ; 3989uint32_t best_n_prefetch = 2 ; 3990for (uint32_t d = max_prefetch ;d >=2 ;d /=2 ) { 3991htp_mm_hvx_vtcm_layout_build ( 3992& L ,kparams -> kernel_type ,wtype ,ne10 ,src1_nrows ,sess -> n_threads , 39930 ,src0 -> nb [1 ],0 ,src2_row_size ,d , true, false 3994 ); 3995if (L .total_bytes <=vtcm_budget ) { 3996best_n_prefetch = d ; 3997break ; 3998 } 3999 } 4000if (best_n_prefetch == 2 && L .total_bytes > vtcm_budget ) { 4001htp_mm_hvx_vtcm_layout_build ( 4002& L ,kparams -> kernel_type ,wtype ,ne10 ,src1_nrows ,sess -> n_threads , 40030 ,src0 -> nb [1 ],0 ,src2_row_size ,2 , true, false 4004 ); 4005 } 4006kparams -> n_prefetch = best_n_prefetch ; 4007kparams -> vtcm_size = L .total_bytes ; 4008kparams -> vtcm_src0_size = L .src0_bytes ; 4009kparams -> vtcm_src1_size = L .src1_bytes ; 4010kparams -> vtcm_dst_size = L .dst_bytes ; 4011 }else { 4012bool try_tiled = (k_align && opt_mm_select >=2 ); 4013if (try_tiled ) { 4014kparams -> src1_row_size = (wtype == GGML_TYPE_Q4_1 ) ?htp_mm_q8_1_tiled_row_size (ne10 ) :htp_mm_q8_0_tiled_row_size (ne10 ); 4015if (src1_nrows < (int )sess -> n_threads ) { 4016kparams -> kernel_type = HTP_MM_KERNEL_HVX_QUANT_BLOCK ; 4017 }else { 4018kparams -> kernel_type = HTP_MM_KERNEL_HVX_QUANT_ROW ; 4019 } 4020 4021struct htp_mm_hvx_vtcm_layout L ; 4022uint32_t max_prefetch = (src1_nrows > HTP_MM_HMX_MIN_NROWS ) ?2 :16 ; 4023uint32_t best_n_prefetch = 2 ; 4024for (uint32_t d = max_prefetch ;d >=2 ;d /=2 ) { 4025htp_mm_hvx_vtcm_layout_build ( 4026& L ,kparams -> kernel_type ,wtype ,ne10 ,src1_nrows ,sess -> n_threads , 4027dst -> nb [1 ],src0 -> nb [1 ],src1 -> nb [1 ],src2_row_size ,d , false, false 4028 ); 4029if (L .total_bytes <=vtcm_budget ) { 4030best_n_prefetch = d ; 4031break ; 4032 } 4033 } 4034if (best_n_prefetch == 2 && L .total_bytes > vtcm_budget ) { 4035htp_mm_hvx_vtcm_layout_build ( 4036& L ,kparams -> kernel_type ,wtype ,ne10 ,src1_nrows ,sess -> n_threads , 4037dst -> nb [1 ],src0 -> nb [1 ],src1 -> nb [1 ],src2_row_size ,2 , false, false 4038 ); 4039 } 4040 4041kparams -> n_prefetch = best_n_prefetch ; 4042 4043if (L .total_bytes <=vtcm_budget ) { 4044kparams -> vtcm_size = L .total_bytes ; 4045kparams -> vtcm_src0_size = L .src0_bytes ; 4046kparams -> vtcm_src1_size = L .src1_bytes ; 4047kparams -> vtcm_dst_size = L .dst_bytes ; 4048 gotodone_quant ; 4049 } 4050HEX_VERBOSE ("ggml-hex: %s HVX tiled path VTCM size needed (%zu) > budget (%zu), falling back to HVX flat\n" ,sess -> name .c_str (),L .total_bytes ,vtcm_budget ); 4051 } 4052 4053// Flat HVX fallback 4054 { 4055kparams -> src1_row_size = (wtype == GGML_TYPE_Q4_1 ) ?htp_mm_q8_1_flat_row_size (ne10 ) :htp_mm_q8_0_flat_row_size (ne10 ); 4056kparams -> kernel_type = HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT ; 4057 4058struct htp_mm_hvx_vtcm_layout L ; 4059htp_mm_hvx_vtcm_layout_build ( 4060& L ,kparams -> kernel_type ,wtype ,ne10 ,src1_nrows ,sess -> n_threads , 4061dst -> nb [1 ],src0 -> nb [1 ],src1 -> nb [1 ],src2_row_size ,16 , false, false 4062 ); 4063 4064kparams -> n_prefetch = 16 ; 4065kparams -> vtcm_size = L .total_bytes ; 4066kparams -> vtcm_src0_size = L .src0_bytes ; 4067kparams -> vtcm_src1_size = L .src1_bytes ; 4068kparams -> vtcm_dst_size = L .dst_bytes ; 4069 } 4070 } 4071 4072done_quant :; 4073 }else if (wtype == GGML_TYPE_F16 ) { 4074// F16 HVX 4075const bool is_batched = (ne02 > 1 )|| (ne03 > 1 ); 4076const bool is_permuted = ggml_is_permuted (src0 )|| ggml_is_permuted (src1 ); 4077 4078struct htp_mm_hvx_vtcm_layout L ; 4079htp_mm_hvx_vtcm_layout_build ( 4080& L ,HTP_MM_KERNEL_HVX_F16_F16_VTCM ,wtype ,ne10 ,src1_nrows ,sess -> n_threads , 4081dst -> nb [1 ],src0 -> nb [1 ],src1 -> nb [1 ],src2_row_size ,16 , false, false 4082 ); 4083 4084if (!is_batched && !is_permuted && L .total_bytes <=vtcm_budget ) { 4085kparams -> kernel_type = HTP_MM_KERNEL_HVX_F16_F16_VTCM ; 4086kparams -> src1_row_size = hex_round_up (ne10 * 2 ,128 ); 4087kparams -> vtcm_size = L .total_bytes ; 4088kparams -> vtcm_src0_size = L .src0_bytes ; 4089kparams -> vtcm_src1_size = L .src1_bytes ; 4090kparams -> vtcm_dst_size = L .dst_bytes ; 4091kparams -> n_prefetch = 16 ; 4092 }else { 4093if (src1 -> type == GGML_TYPE_F32 ) { 4094kparams -> kernel_type = HTP_MM_KERNEL_HVX_F16_F32_DDR ; 4095 }else { 4096kparams -> kernel_type = HTP_MM_KERNEL_HVX_F16_F16_DDR ; 4097 } 4098kparams -> src1_row_size = src1 -> nb [1 ]; 4099htp_mm_hvx_vtcm_layout_build ( 4100& L ,kparams -> kernel_type ,wtype ,ne10 ,src1_nrows ,sess -> n_threads , 4101dst -> nb [1 ],src0 -> nb [1 ],src1 -> nb [1 ],src2_row_size ,16 , false, false 4102 ); 4103kparams -> vtcm_size = L .total_bytes ; 4104kparams -> vtcm_src0_size = L .src0_bytes ; 4105kparams -> vtcm_src1_size = L .src1_bytes ; 4106kparams -> vtcm_dst_size = L .dst_bytes ; 4107kparams -> n_prefetch = 16 ; 4108 } 4109 }else { 4110// F32 HVX 4111const bool is_batched = (ne02 > 1 )|| (ne03 > 1 ); 4112const bool is_permuted = ggml_is_permuted (src0 )|| ggml_is_permuted (src1 ); 4113 4114struct htp_mm_hvx_vtcm_layout L ; 4115htp_mm_hvx_vtcm_layout_build ( 4116& L ,HTP_MM_KERNEL_HVX_F32_F32_VTCM ,wtype ,ne10 ,src1_nrows ,sess -> n_threads , 4117dst -> nb [1 ],src0 -> nb [1 ],src1 -> nb [1 ],src2_row_size ,16 , false, false 4118 ); 4119 4120if (!is_batched && !is_permuted && L .total_bytes <=vtcm_budget ) { 4121kparams -> kernel_type = HTP_MM_KERNEL_HVX_F32_F32_VTCM ; 4122kparams -> src1_row_size = hex_round_up (ne10 * 4 ,128 ); 4123kparams -> vtcm_size = L .total_bytes ; 4124kparams -> vtcm_src0_size = L .src0_bytes ; 4125kparams -> vtcm_src1_size = L .src1_bytes ; 4126kparams -> vtcm_dst_size = L .dst_bytes ; 4127kparams -> n_prefetch = 16 ; 4128 }else { 4129kparams -> kernel_type = HTP_MM_KERNEL_HVX_F32_F32_DDR ; 4130kparams -> src1_row_size = src1 -> nb [1 ]; 4131htp_mm_hvx_vtcm_layout_build ( 4132& L ,kparams -> kernel_type ,wtype ,ne10 ,src1_nrows ,sess -> n_threads , 4133dst -> nb [1 ],src0 -> nb [1 ],src1 -> nb [1 ],src2_row_size ,16 , false, false 4134 ); 4135kparams -> vtcm_size = L .total_bytes ; 4136kparams -> vtcm_src0_size = L .src0_bytes ; 4137kparams -> vtcm_src1_size = L .src1_bytes ; 4138kparams -> vtcm_dst_size = L .dst_bytes ; 4139kparams -> n_prefetch = 16 ; 4140 } 4141 } 4142} 4143 4144static void ggml_hexagon_precompute_matmul_params_impl ( 4145const struct ggml_hexagon_session * sess , 4146const struct ggml_tensor * src0 , 4147const struct ggml_tensor * src1 , 4148const struct ggml_tensor * dst , 4149const size_t src2_row_size , 4150struct htp_mm_kernel_params * kparams 4151) { 4152memset (kparams ,0 ,sizeof (* kparams )); 4153 4154const int ne00 = src0 -> ne [0 ]; 4155const int ne01 = src0 -> ne [1 ]; 4156const int ne02 = src0 -> ne [2 ]; 4157const int ne03 = src0 -> ne [3 ]; 4158 4159const int ne10 = src1 -> ne [0 ]; 4160const int ne11 = src1 -> ne [1 ]; 4161const int ne12 = src1 -> ne [2 ]; 4162const int ne13 = src1 -> ne [3 ]; 4163 4164const int wtype = src0 -> type ; 4165const bool is_repack = ggml_hexagon_is_repack_type ((ggml_type )wtype ); 4166const int ne00_padded = is_repack ?hex_round_up (ne00 ,32 ) :ne00 ; 4167const int ne01_padded = is_repack ?hex_round_up (ne01 ,32 ) :ne01 ; 4168const int ne11_padded = hex_round_up (ne11 ,32 ); 4169 4170const bool is_matmul_id = (dst -> op == GGML_OP_MUL_MAT_ID ); 4171const bool is_batched = (ne02 * ne03 > 1 || ne12 * ne13 > 1 ); 4172 4173const size_t vtcm_budget = sess -> vtcm_size ; 4174 4175// Check HMX eligibility and try precomputing HMX parameters 4176bool hmx_enabled = (sess -> n_hmx > 0 )&& (opt_mm_select >=3 ); 4177if (hmx_enabled && ggml_hexagon_matmul_is_hmx_eligible (src0 ,src1 ,dst ,ne01_padded ,is_matmul_id ,is_batched )) { 4178if (ggml_hexagon_precompute_hmx_mm_params (sess ,src0 ,src1 ,dst ,wtype ,ne00_padded ,ne01_padded ,ne02 ,ne11 ,ne12 ,ne11_padded ,is_matmul_id ,is_batched ,vtcm_budget ,kparams )) { 4179 gotofinalize ; 4180 } 4181 } 4182 4183// Fallback to HVX parameter computation 4184ggml_hexagon_precompute_hvx_mm_params (sess ,src0 ,src1 ,dst ,wtype ,ne02 ,ne03 ,ne10 ,ne11 ,ne12 ,ne13 ,is_matmul_id ,src2_row_size ,vtcm_budget ,kparams ); 4185 4186finalize : 4187kparams -> div_ne12_ne1 = init_fastdiv_values (ne12 * ne11 ); 4188kparams -> div_ne1 = init_fastdiv_values (ne11 ); 4189kparams -> div_r2 = init_fastdiv_values (ne02 > 0 ?ne12 /ne02 :1 ); 4190kparams -> div_r3 = init_fastdiv_values (ne03 > 0 ?ne13 /ne03 :1 ); 4191kparams -> div_ne11 = init_fastdiv_values (ne11 ); 4192} 4193 4194static void ggml_hexagon_precompute_matmul_params ( 4195const struct ggml_hexagon_session * sess , 4196const struct ggml_tensor * src0 , 4197const struct ggml_tensor * src1 , 4198const struct ggml_tensor * dst , 4199struct htp_mm_kernel_params * kparams 4200) { 4201ggml_hexagon_precompute_matmul_params_impl (sess ,src0 ,src1 ,dst ,0 ,kparams ); 4202} 4203 4204static void ggml_hexagon_precompute_fused_matmul_add_params ( 4205const struct ggml_hexagon_session * sess , 4206const struct ggml_tensor * src0 , 4207const struct ggml_tensor * src1 , 4208const struct ggml_tensor * src2 , 4209const struct ggml_tensor * dst , 4210struct htp_mm_kernel_params * kparams 4211) { 4212ggml_hexagon_precompute_matmul_params_impl (sess ,src0 ,src1 ,dst ,src2 -> nb [1 ],kparams ); 4213} 4214 4215static void ggml_hexagon_precompute_unary_params ( 4216const struct ggml_hexagon_session * sess , 4217uint32_t op , 4218const struct ggml_tensor * src0 , 4219const struct ggml_tensor * src1 , 4220const struct ggml_tensor * dst , 4221struct htp_unary_kernel_params * kparams 4222) { 4223memset (kparams ,0 ,sizeof (* kparams )); 4224 4225const uint32_t src0_nrows = src0 -> ne [1 ]* src0 -> ne [2 ]* src0 -> ne [3 ]; 4226const uint32_t n_threads = (std::min )((uint32_t )sess -> n_threads ,src0_nrows ); 4227 4228kparams -> n_threads = n_threads ; 4229 4230const size_t elem_size = ggml_type_size (src0 -> type ); 4231 4232const size_t src0_data_row_size = src0 -> ne [0 ]* elem_size ; 4233const size_t dst_data_row_size = dst -> ne [0 ]* ggml_type_size (dst -> type ); 4234 4235const size_t src0_row_size_aligned = hex_round_up (src0_data_row_size ,128 ); 4236const size_t dst_row_size_aligned = hex_round_up (dst_data_row_size ,128 ); 4237 4238kparams -> src0_row_size_aligned = src0_row_size_aligned ; 4239kparams -> dst_row_size_aligned = dst_row_size_aligned ; 4240 4241size_t src1_data_row_size = 0 ; 4242size_t src1_row_size_aligned = 0 ; 4243bool broadcast_weight = false; 4244 4245if (op == HTP_OP_RMS_NORM_MUL ) { 4246GGML_ASSERT (src1 != nullptr ); 4247src1_data_row_size = src1 -> ne [0 ]* ggml_type_size (src1 -> type ); 4248src1_row_size_aligned = hex_round_up (src1_data_row_size ,128 ); 4249broadcast_weight = (src1 -> ne [1 ]* src1 -> ne [2 ]* src1 -> ne [3 ]== 1 ); 4250 } 4251 4252kparams -> src1_row_size_aligned = src1_row_size_aligned ; 4253kparams -> broadcast_weight = broadcast_weight ; 4254 4255struct htp_unary_vtcm_layout L ; 4256uint32_t col_tile = 0 ; 4257uint32_t vtcm_row_per_thread = 0 ; 4258 4259htp_unary_vtcm_layout_build (& L ,op ,src0 -> ne [0 ],dst -> ne [0 ], 4260op == HTP_OP_RMS_NORM_MUL ?src1 -> ne [0 ] :0 , 4261broadcast_weight ,n_threads ,sess -> vtcm_size ,elem_size , 4262& col_tile ,& vtcm_row_per_thread ); 4263 4264kparams -> col_tile = col_tile ; 4265kparams -> vtcm_row_per_thread = vtcm_row_per_thread ; 4266kparams -> vtcm_size = L .total_bytes ; 4267 4268kparams -> vtcm_src0_size_per_thread = L .src0_bytes ; 4269kparams -> vtcm_src1_size_per_thread = L .src1_bytes ; 4270kparams -> vtcm_dst_size_per_thread = L .dst_bytes ; 4271 4272kparams -> vtcm_src0_size = L .src0_bytes * n_threads ; 4273kparams -> vtcm_src1_size = L .src1_bytes * n_threads ; 4274kparams -> vtcm_dst_size = L .dst_bytes * n_threads ; 4275 4276kparams -> block = col_tile ?0 : ((L .src0_bytes /2 ) /src0_row_size_aligned ); 4277 4278const uint32_t tiles_per_row = col_tile > 0 ? (src0 -> ne [0 ]+ col_tile - 1 ) /col_tile :1 ; 4279kparams -> div_ne01 = init_fastdiv_values (src0 -> ne [1 ]); 4280kparams -> div_ne02 = init_fastdiv_values (src0 -> ne [2 ]); 4281kparams -> div_ne012 = init_fastdiv_values (src0 -> ne [1 ]* src0 -> ne [2 ]); 4282kparams -> div_tpr = init_fastdiv_values (tiles_per_row ); 4283} 4284 4285static void ggml_hexagon_precompute_get_rows_params ( 4286const struct ggml_hexagon_session * sess , 4287const struct ggml_tensor * src0 , 4288const struct ggml_tensor * src1 , 4289const struct ggml_tensor * dst , 4290struct htp_get_rows_kernel_params * kparams 4291) { 4292memset (kparams ,0 ,sizeof (* kparams )); 4293 4294const uint32_t ne00 = src0 -> ne [0 ]; 4295const uint32_t ne02 = src0 -> ne [2 ]; 4296const uint32_t ne03 = src0 -> ne [3 ]; 4297 4298const uint32_t ne10 = src1 -> ne [0 ]; 4299const uint32_t ne11 = src1 -> ne [1 ]; 4300const uint32_t ne12 = src1 -> ne [2 ]; 4301const uint32_t nr = ne10 * ne11 * ne12 ; 4302 4303const size_t nb01 = src0 -> nb [1 ]; 4304const size_t nb1 = dst -> nb [1 ]; 4305 4306const bool can_use_dma = (src0 -> type == dst -> type )&& (nb01 == nb1 ); 4307const bool use_dma = can_use_dma && (ne00 >=2048 ); 4308 4309kparams -> use_dma = use_dma ?1 :0 ; 4310 4311uint32_t chunks_per_row = 1 ; 4312uint32_t chunk_size = ne00 ; 4313uint32_t total_tasks = nr ; 4314 4315if (use_dma ) { 4316kparams -> n_threads = (std::min )((uint32_t )sess -> n_threads ,nr ); 4317kparams -> tasks_per_thread = (nr + kparams -> n_threads - 1 ) /kparams -> n_threads ; 4318 }else { 4319if (src0 -> type == GGML_TYPE_F32 && nr < sess -> n_threads ) { 4320const uint32_t min_chunk_size = 1024 ; 4321uint32_t max_chunks = ne00 /min_chunk_size ; 4322if (max_chunks == 0 ) { 4323max_chunks = 1 ; 4324 } 4325chunks_per_row = (std::min )((sess -> n_threads + nr - 1 ) /nr ,max_chunks ); 4326chunk_size = (ne00 + chunks_per_row - 1 ) /chunks_per_row ; 4327total_tasks = nr * chunks_per_row ; 4328 } 4329kparams -> n_threads = (std::min )(total_tasks , (uint32_t )sess -> n_threads ); 4330kparams -> tasks_per_thread = (total_tasks + kparams -> n_threads - 1 ) /kparams -> n_threads ; 4331 } 4332 4333kparams -> chunks_per_row = chunks_per_row ; 4334kparams -> chunk_size = chunk_size ; 4335kparams -> total_tasks = total_tasks ; 4336 4337kparams -> div_ne10 = init_fastdiv_values (ne10 ); 4338kparams -> div_ne10_ne11 = init_fastdiv_values (ne10 * ne11 ); 4339kparams -> div_chunks_per_row = init_fastdiv_values (chunks_per_row ); 4340kparams -> div_ne02 = init_fastdiv_values (ne02 ); 4341kparams -> div_ne03 = init_fastdiv_values (ne03 ); 4342 4343struct htp_get_rows_vtcm_layout vtcm_layout ; 4344htp_get_rows_vtcm_layout_build (& vtcm_layout ,src0 -> type ,ne00 ,kparams -> n_threads ); 4345kparams -> vtcm_size = vtcm_layout .total_bytes ; 4346} 4347 4348static void ggml_hexagon_precompute_set_rows_params ( 4349const struct ggml_hexagon_session * sess , 4350const struct ggml_tensor * src0 ,// values 4351const struct ggml_tensor * src1 ,// indices 4352const struct ggml_tensor * dst ,// destination 4353struct htp_set_rows_kernel_params * kparams 4354) { 4355memset (kparams ,0 ,sizeof (* kparams )); 4356 4357const uint32_t nr = src0 -> ne [1 ]; 4358 4359kparams -> n_threads = (std::min )((uint32_t )sess -> n_threads ,nr ); 4360kparams -> tasks_per_thread = (nr + kparams -> n_threads - 1 ) /kparams -> n_threads ; 4361kparams -> total_tasks = nr ; 4362 4363kparams -> div_ne11 = init_fastdiv_values (src1 -> ne [1 ]); 4364kparams -> div_ne12 = init_fastdiv_values (src1 -> ne [2 ]); 4365kparams -> div_tasks_per_thread = init_fastdiv_values (kparams -> tasks_per_thread ); 4366kparams -> div_ne02 = init_fastdiv_values (src0 -> ne [2 ]); 4367 4368struct htp_set_rows_vtcm_layout vtcm_layout ; 4369htp_set_rows_vtcm_layout_build (& vtcm_layout ,dst -> type ,src0 -> ne [0 ],kparams -> n_threads ); 4370kparams -> vtcm_size = vtcm_layout .total_bytes ; 4371} 4372 4373static void ggml_hexagon_precompute_rope_params ( 4374const struct ggml_hexagon_session * sess , 4375const struct ggml_tensor * op , 4376struct htp_rope_kernel_params * kparams 4377) { 4378memset (kparams ,0 ,sizeof (* kparams )); 4379 4380const struct ggml_tensor * src0 = op -> src [0 ]; 4381const struct ggml_tensor * dst = op ; 4382 4383const uint32_t src0_nrows = src0 -> ne [1 ]* src0 -> ne [2 ]* src0 -> ne [3 ]; 4384const uint32_t n_threads = (std::min )((uint32_t )sess -> n_threads ,src0_nrows ); 4385 4386struct htp_rope_vtcm_layout layout ; 4387htp_rope_vtcm_layout_build (& layout ,src0 -> ne [0 ],n_threads ); 4388 4389kparams -> n_threads = n_threads ; 4390kparams -> src0_nrows = src0_nrows ; 4391kparams -> src0_nrows_per_thread = (src0_nrows + n_threads - 1 ) /n_threads ; 4392kparams -> vtcm_size = (uint32_t )layout .total_bytes ; 4393kparams -> spad_per_thread = (uint32_t )layout .bytes_per_thread ; 4394kparams -> theta_cache_offset = (uint32_t )layout .theta_cache_size_aligned ; 4395kparams -> src0_row_size_aligned = (uint32_t )layout .src0_row_size_aligned ; 4396 4397if (src0_nrows > 0 ) { 4398kparams -> div_ne2_ne1 = init_fastdiv_values (dst -> ne [2 ]* dst -> ne [1 ]); 4399kparams -> div_ne1 = init_fastdiv_values (dst -> ne [1 ]); 4400 } 4401} 4402 4403static void ggml_hexagon_precompute_fused_mmnx_params ( 4404const struct ggml_hexagon_session * sess , 4405const struct ggml_tensor * src0 ,// W0 4406const struct ggml_tensor * src1 ,// x 4407int32_t n_weights , 4408struct htp_mm_kernel_params * kparams 4409) { 4410memset (kparams ,0 ,sizeof (* kparams )); 4411kparams -> n_threads = sess -> n_threads ; 4412 4413const int ne00 = src0 -> ne [0 ]; 4414const int ne01 = src0 -> ne [1 ]; 4415const int ne02 = src0 -> ne [2 ]; 4416const int ne03 = src0 -> ne [3 ]; 4417 4418const int ne10 = src1 -> ne [0 ]; 4419const int ne11 = src1 -> ne [1 ]; 4420const int ne12 = src1 -> ne [2 ]; 4421const int ne13 = src1 -> ne [3 ]; 4422 4423const int wtype = src0 -> type ; 4424const bool is_repack = ggml_hexagon_is_repack_type ((ggml_type )wtype ); 4425const int ne00_padded = is_repack ?hex_round_up (ne00 ,32 ) :ne00 ; 4426const int ne01_padded = is_repack ?hex_round_up (ne01 ,32 ) :ne01 ; 4427const int ne11_padded = hex_round_up (ne11 ,32 ); 4428 4429const size_t vtcm_budget = sess -> vtcm_size ; 4430const bool is_batched = (ne02 * ne03 > 1 || ne12 * ne13 > 1 ); 4431 4432bool hmx_enabled = (sess -> n_hmx > 0 )&& (opt_mm_select >=3 ); 4433if (hmx_enabled && ggml_hexagon_matmul_is_hmx_eligible (src0 ,src1 ,nullptr ,ne01_padded , false,is_batched )) { 4434if (ggml_hexagon_precompute_hmx_mm_params (sess ,src0 ,src1 ,nullptr ,wtype ,ne00_padded ,ne01_padded ,ne02 ,ne11 ,ne12 ,ne11_padded , false,is_batched ,vtcm_budget ,kparams )) { 4435kparams -> n_weights = n_weights ; 4436 gotofinalize ; 4437 } 4438 } 4439 4440if (!is_repack ) { 4441kparams -> kernel_type = HTP_MM_KERNEL_UNSUPPORTED ; 4442return ; 4443 } 4444 4445 { 4446const int src1_nrows = ne11 * ne12 * ne13 ; 4447const size_t src1_row_size = (wtype == GGML_TYPE_Q4_1 ) ?htp_mm_q8_1_tiled_row_size (ne10 ) :htp_mm_q8_0_tiled_row_size (ne10 ); 4448const size_t src0_row_size = src0 -> nb [1 ]; 4449 4450uint32_t best_n_prefetch = 16 ; 4451 4452if (is_repack ) { 4453const uint32_t max_prefetch = (src1_nrows > HTP_MM_HMX_MIN_NROWS ) ?2 :16 ; 4454best_n_prefetch = 2 ; 4455for (uint32_t d = max_prefetch ;d >=2 ;d /=2 ) { 4456struct htp_mm_hvx_vtcm_layout L ; 4457htp_mm_hvx_vtcm_layout_build ( 4458& L ,HTP_MM_KERNEL_HVX_QUANT_ROW ,wtype ,ne10 ,src1_nrows ,sess -> n_threads , 44590 ,src0_row_size ,src1_row_size ,0 ,d , false, true 4460 ); 4461if (L .total_bytes <=sess -> vtcm_size ) { 4462best_n_prefetch = d ; 4463break ; 4464 } 4465 } 4466 } 4467 4468struct htp_mm_hvx_vtcm_layout L ; 4469bool try_tiled = (opt_mm_select >=2 ); 4470 4471// Test tiled first 4472htp_mm_hvx_vtcm_layout_build ( 4473& L ,HTP_MM_KERNEL_HVX_QUANT_ROW ,wtype ,ne10 ,src1_nrows ,sess -> n_threads , 44740 ,src0_row_size ,src1_row_size ,0 ,best_n_prefetch , false, true 4475 ); 4476 4477if (try_tiled && L .total_bytes <=sess -> vtcm_size ) { 4478kparams -> kernel_type = HTP_MM_KERNEL_HVX_QUANT_ROW ; 4479kparams -> vtcm_src0_size = L .src0_bytes ; 4480kparams -> vtcm_src1_size = L .src1_bytes ; 4481kparams -> vtcm_dst_size = L .dst_bytes ; 4482kparams -> vtcm_size = L .total_bytes ; 4483kparams -> n_prefetch = best_n_prefetch ; 4484kparams -> n_weights = n_weights ; 4485 }else { 4486kparams -> kernel_type = HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT ; 4487size_t flat_src1_row_size = (wtype == GGML_TYPE_Q4_1 ) ?htp_mm_q8_1_flat_row_size (ne10 ) :htp_mm_q8_0_flat_row_size (ne10 ); 4488 4489htp_mm_hvx_vtcm_layout_build ( 4490& L ,HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT ,wtype ,ne10 ,src1_nrows ,sess -> n_threads , 44910 ,src0_row_size ,flat_src1_row_size ,0 ,best_n_prefetch , false, true 4492 ); 4493kparams -> vtcm_src0_size = L .src0_bytes ; 4494kparams -> vtcm_src1_size = L .src1_bytes ; 4495kparams -> vtcm_dst_size = L .dst_bytes ; 4496kparams -> vtcm_size = L .total_bytes ; 4497kparams -> n_prefetch = best_n_prefetch ; 4498kparams -> n_weights = n_weights ; 4499 } 4500 } 4501 4502finalize : 4503kparams -> div_ne12_ne1 = init_fastdiv_values (ne12 * ne11 ); 4504kparams -> div_ne1 = init_fastdiv_values (ne11 ); 4505kparams -> div_r2 = init_fastdiv_values (ne02 > 0 ?ne12 /ne02 :1 ); 4506kparams -> div_r3 = init_fastdiv_values (ne03 > 0 ?ne13 /ne03 :1 ); 4507kparams -> div_ne11 = init_fastdiv_values (ne11 ); 4508} 4509 4510static void ggml_hexagon_precompute_fused_mmidnx_params ( 4511const struct ggml_hexagon_session * sess , 4512const struct ggml_tensor * src0 ,// W0 4513const struct ggml_tensor * src1 ,// x 4514const struct ggml_tensor * dst ,// dst0 4515int32_t n_weights , 4516struct htp_mm_kernel_params * kparams 4517) { 4518ggml_hexagon_precompute_matmul_params_impl (sess ,src0 ,src1 ,dst ,0 ,kparams ); 4519kparams -> n_weights = n_weights ; 4520} 4521 4522static bool ggml_hexagon_tensor_is_host (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * t ) { 4523return t && t -> buffer && ggml_backend_buft_is_host (t -> buffer -> buft ); 4524GGML_UNUSED (sess ); 4525} 4526 4527static bool ggml_hexagon_tensor_is_non_host (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * t ) { 4528return t && t -> buffer && !ggml_backend_buft_is_host (t -> buffer -> buft ); 4529GGML_UNUSED (sess ); 4530} 4531 4532static bool ggml_hexagon_supported_mul_mat (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * dst ) { 4533const struct ggml_tensor * src0 = dst -> src [0 ]; 4534const struct ggml_tensor * src1 = dst -> src [1 ]; 4535 4536if (dst -> type != GGML_TYPE_F32 ) { 4537return false; 4538 } 4539 4540if (src1 -> type != GGML_TYPE_F32 && src1 -> type != GGML_TYPE_F16 ) { 4541return false; 4542 } 4543 4544switch (src0 -> type ) { 4545case GGML_TYPE_Q4_0 : 4546case GGML_TYPE_Q4_1 : 4547case GGML_TYPE_Q8_0 : 4548case GGML_TYPE_IQ4_NL : 4549case GGML_TYPE_MXFP4 : 4550if (src0 -> ne [0 ] %32 ) { 4551return false; 4552 } 4553 4554if (src1 -> ne [2 ]!= 1 || src1 -> ne [3 ]!= 1 ) { 4555return false;// no broadcasting (for now) 4556 } 4557 4558if (!src0 -> buffer ) { 4559sess -> needs_repack .insert (src0 ); 4560 } 4561break ; 4562 4563case GGML_TYPE_F16 : 4564if (src0 -> nb [1 ]< src0 -> nb [0 ]) { 4565return false; 4566 } 4567if (src1 -> ne [2 ]< src0 -> ne [2 ]|| src1 -> ne [3 ]< src0 -> ne [3 ]) { 4568return false; 4569 } 4570break ; 4571 4572case GGML_TYPE_F32 : 4573if (src1 -> type != GGML_TYPE_F32 ) { 4574return false; 4575 } 4576if (src0 -> nb [1 ]< src0 -> nb [0 ]) { 4577return false; 4578 } 4579if (src1 -> ne [2 ]< src0 -> ne [2 ]|| src1 -> ne [3 ]< src0 -> ne [3 ]) { 4580return false; 4581 } 4582break ; 4583 4584default : 4585return false; 4586 } 4587 4588struct htp_mm_kernel_params kparams ; 4589ggml_hexagon_precompute_matmul_params (sess ,src0 ,src1 ,dst ,& kparams ); 4590if ((size_t )kparams .vtcm_size > sess -> vtcm_size ) { 4591HEX_VERBOSE ("ggml-hex: %s supported MUL_MAT VTCM size needed (%d) > budget (%zu)\n" ,sess -> c_name (),kparams .vtcm_size ,sess -> vtcm_size ); 4592return false; 4593 } 4594 4595return true; 4596} 4597 4598static bool ggml_hexagon_supported_mul_mat_id (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 4599const struct ggml_tensor * src0 = op -> src [0 ]; 4600const struct ggml_tensor * src1 = op -> src [1 ]; 4601const struct ggml_tensor * src2 = op -> src [2 ]; 4602const struct ggml_tensor * dst = op ; 4603 4604if (src1 -> type != GGML_TYPE_F32 || dst -> type != GGML_TYPE_F32 || src2 -> type != GGML_TYPE_I32 ) { 4605return false; 4606 } 4607 4608switch (src0 -> type ) { 4609case GGML_TYPE_Q4_0 : 4610case GGML_TYPE_Q4_1 : 4611case GGML_TYPE_Q8_0 : 4612case GGML_TYPE_IQ4_NL : 4613case GGML_TYPE_MXFP4 : 4614if ((src0 -> ne [0 ] %32 )) { 4615return false; 4616 } 4617 4618if (!src0 -> buffer ) { 4619sess -> needs_repack .insert (src0 ); 4620 } 4621break ; 4622 4623default : 4624return false; 4625 } 4626 4627struct htp_mm_kernel_params kparams ; 4628ggml_hexagon_precompute_matmul_params (sess ,src0 ,src1 ,dst ,& kparams ); 4629if ((size_t )kparams .vtcm_size > sess -> vtcm_size ) { 4630HEX_VERBOSE ("ggml-hex: %s supported MUL_MAT_ID VTCM size needed (%d) > budget (%zu)\n" ,sess -> c_name (),kparams .vtcm_size ,sess -> vtcm_size ); 4631return false; 4632 } 4633 4634return true; 4635} 4636 4637static bool ggml_hexagon_supported_binary (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 4638const struct ggml_tensor * src0 = op -> src [0 ]; 4639const struct ggml_tensor * src1 = op -> src [1 ]; 4640const struct ggml_tensor * dst = op ; 4641 4642if (src0 -> type == GGML_TYPE_F32 ) { 4643if (src1 -> type != GGML_TYPE_F32 ) { 4644return false; 4645 } 4646if (dst -> type != GGML_TYPE_F32 ) { 4647return false; 4648 } 4649 } 4650else if (src0 -> type == GGML_TYPE_F16 ) { 4651if (src1 -> type != GGML_TYPE_F16 ) { 4652return false; 4653 } 4654if (dst -> type != GGML_TYPE_F16 ) { 4655return false; 4656 } 4657 } 4658else { 4659return false; 4660 } 4661 4662if (ggml_is_permuted (src0 )|| ggml_is_permuted (dst )) { 4663return false; 4664 } 4665if (!ggml_are_same_shape (src0 ,dst )) { 4666return false; 4667 } 4668if (!ggml_can_repeat (src1 ,src0 )|| ggml_is_permuted (src1 )) { 4669return false; 4670 } 4671 4672return true; 4673 4674GGML_UNUSED (sess ); 4675} 4676 4677static bool ggml_hexagon_supported_add_id (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 4678const struct ggml_tensor * src0 = op -> src [0 ]; 4679const struct ggml_tensor * src1 = op -> src [1 ]; 4680const struct ggml_tensor * dst = op ; 4681 4682if (src0 -> type != GGML_TYPE_F32 ) { 4683return false; 4684 } 4685if (src1 -> type != GGML_TYPE_F32 ) { 4686return false; 4687 } 4688if (dst -> type != GGML_TYPE_F32 ) { 4689return false; 4690 } 4691if (!ggml_are_same_shape (src0 ,dst )) { 4692return false; 4693 } 4694 4695// REVISIT: add support for non-contigiuos tensors 4696if (!ggml_is_contiguous (src0 )|| !ggml_is_contiguous (src1 )|| !ggml_is_contiguous (dst )) { 4697return false; 4698 } 4699 4700return true; 4701 4702GGML_UNUSED (sess ); 4703} 4704 4705static bool ggml_hexagon_supported_unary (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 4706const struct ggml_tensor * src0 = op -> src [0 ]; 4707const struct ggml_tensor * dst = op ; 4708 4709if (src0 -> type != GGML_TYPE_F32 && src0 -> type != GGML_TYPE_F16 ) { 4710return false; 4711 } 4712if (dst -> type != src0 -> type ) { 4713return false; 4714 } 4715if (!ggml_is_contiguous_rows (src0 )) { 4716return false; 4717 } 4718 4719// F16 device kernels only cover this explicit whitelist (must stay in sync with 4720// the is_f16 whitelist in execute_op_unary(), unary-ops.c). 4721if (src0 -> type == GGML_TYPE_F16 ) { 4722switch (op -> op ) { 4723case GGML_OP_NORM : 4724case GGML_OP_RMS_NORM : 4725case GGML_OP_L2_NORM : 4726case GGML_OP_SCALE : 4727case GGML_OP_CLAMP : 4728case GGML_OP_SQR : 4729case GGML_OP_SQRT : 4730case GGML_OP_LOG : 4731break ; 4732case GGML_OP_UNARY : 4733if (ggml_get_unary_op (op )!= GGML_UNARY_OP_ABS ) { 4734return false; 4735 } 4736break ; 4737default : 4738return false; 4739 } 4740 } 4741 4742if (!ggml_are_same_shape (src0 ,dst )) { 4743return false; 4744 } 4745 4746// dst must be contiguous; src0 may be non-contiguous 4747if (!ggml_is_contiguous (dst )) { 4748return false; 4749 } 4750 4751return true; 4752 4753GGML_UNUSED (sess ); 4754} 4755 4756static bool ggml_hexagon_supported_sum_rows (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 4757const struct ggml_tensor * src0 = op -> src [0 ]; 4758const struct ggml_tensor * dst = op ; 4759 4760if (src0 -> type != GGML_TYPE_F32 ) { 4761return false; 4762 } 4763if (dst -> type != GGML_TYPE_F32 ) { 4764return false; 4765 } 4766 4767// TODO: add support for non-contigiuos tensors 4768if (!ggml_is_contiguous (src0 )|| !ggml_is_contiguous (dst )) { 4769return false; 4770 } 4771 4772return true; 4773 4774GGML_UNUSED (sess ); 4775} 4776 4777static bool ggml_hexagon_supported_activations (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 4778const struct ggml_tensor * src0 = op -> src [0 ]; 4779const struct ggml_tensor * src1 = op -> src [1 ]; 4780const struct ggml_tensor * dst = op ; 4781 4782if (src0 -> type != GGML_TYPE_F32 ) { 4783return false; 4784 } 4785if (dst -> type != GGML_TYPE_F32 ) { 4786return false; 4787 } 4788 4789if (!ggml_is_contiguous_1 (src0 )) { 4790return false; 4791 } 4792if (!ggml_is_contiguous (dst )) { 4793return false; 4794 } 4795 4796if (src1 ) { 4797if (src1 -> type != GGML_TYPE_F32 ) { 4798return false; 4799 } 4800if (!ggml_are_same_shape (src0 ,src1 )) { 4801return false; 4802 } 4803if (!ggml_is_contiguous_1 (src1 )) { 4804return false; 4805 } 4806 } 4807 4808return true; 4809 4810GGML_UNUSED (sess ); 4811} 4812 4813static bool ggml_hexagon_supported_softmax (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 4814const struct ggml_tensor * src0 = op -> src [0 ]; 4815const struct ggml_tensor * src1 = op -> src [1 ]; 4816const struct ggml_tensor * src2 = op -> src [2 ]; 4817const struct ggml_tensor * dst = op ; 4818 4819if (src2 ) { 4820return false;// FIXME: add support for sinks 4821 } 4822 4823if (src0 -> type != GGML_TYPE_F32 ) { 4824return false; 4825 } 4826if (dst -> type != GGML_TYPE_F32 ) { 4827return false; 4828 } 4829 4830if (src1 ) { 4831if (src1 -> type != GGML_TYPE_F32 && src1 -> type != GGML_TYPE_F16 ) { 4832return false; 4833 } 4834if (src0 -> ne [0 ]!= src1 -> ne [0 ]) { 4835return false; 4836 } 4837if (src1 -> ne [1 ]< src0 -> ne [1 ]) { 4838return false; 4839 } 4840if (src0 -> ne [2 ] %src1 -> ne [2 ]!= 0 ) { 4841return false; 4842 } 4843if (src0 -> ne [3 ] %src1 -> ne [3 ]!= 0 ) { 4844return false; 4845 } 4846 } 4847 4848if (src1 ) { 4849if (!ggml_is_contiguous (src0 )|| !ggml_is_contiguous (src1 )|| !ggml_is_contiguous (dst )) { 4850return false; 4851 } 4852 }else { 4853if (!ggml_is_contiguous (src0 )|| !ggml_is_contiguous (dst )) { 4854return false; 4855 } 4856 } 4857 4858// Reject non-HVX-aligned sizes when ne[0] > HVX_F32_LANES 4859// The HVX softmax implementation has issues with tail handling for larger non-aligned sizes 4860// Small sizes (ne[0] <= 32) work correctly with tail-only processing 4861const int64_t ne0 = src0 -> ne [0 ]; 4862if (ne0 > 32 && (ne0 & (32 - 1 ))!= 0 ) { 4863return false; 4864 } 4865 4866// HVX vector size constraints for softmax 4867#define SOFTMAX_MAX_ROW_SIZE 131072 // 128K elements max for numerical precision 4868 4869// Reject very large row sizes to avoid numerical precision issues 4870// Softmax accumulation over many elements can lead to precision loss 4871if (ne0 > SOFTMAX_MAX_ROW_SIZE ) { 4872return false; 4873 } 4874 4875return true; 4876 4877GGML_UNUSED (sess ); 4878} 4879 4880static bool ggml_hexagon_supported_set_rows (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 4881const struct ggml_tensor * src0 = op -> src [0 ];// values 4882const struct ggml_tensor * src1 = op -> src [1 ];// indices 4883const struct ggml_tensor * dst = op -> src [2 ] ?op -> src [2 ] :op ; 4884 4885if (dst -> type == GGML_TYPE_Q8_0 && src0 -> ne [0 ]< 32 ) { 4886return false; 4887 } 4888 4889if (src0 -> type != GGML_TYPE_F32 ) { 4890return false; 4891 } 4892 4893if (src1 -> type != GGML_TYPE_I32 && src1 -> type != GGML_TYPE_I64 ) { 4894return false; 4895 } 4896 4897if (dst -> type != GGML_TYPE_F32 && dst -> type != GGML_TYPE_F16 && dst -> type != GGML_TYPE_Q8_0 ) { 4898return false; 4899 } 4900 4901return true; 4902 4903GGML_UNUSED (sess ); 4904} 4905 4906static bool ggml_hexagon_supported_get_rows (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 4907const struct ggml_tensor * src0 = op -> src [0 ];// values 4908const struct ggml_tensor * src1 = op -> src [1 ];// indices 4909const struct ggml_tensor * dst = op ; 4910 4911if (src0 -> extra ) { 4912const auto * extra = (const ggml_hexagon_tensor_extra * )src0 -> extra ; 4913if (extra -> flags & GGML_HEXAGON_TENSOR_REPACK ) { 4914return false; 4915 } 4916 } 4917 4918if (src0 -> type != GGML_TYPE_F32 && src0 -> ne [0 ]< 32 ) { 4919return false; 4920 } 4921 4922if (src0 -> type != GGML_TYPE_F32 && src0 -> type != GGML_TYPE_F16 && src0 -> type != GGML_TYPE_Q8_0 ) { 4923return false; 4924 } 4925 4926if (src1 -> type != GGML_TYPE_I32 && src1 -> type != GGML_TYPE_I64 ) { 4927return false; 4928 } 4929 4930if (dst -> type != GGML_TYPE_F32 ) { 4931return false; 4932 } 4933 4934return true; 4935 4936GGML_UNUSED (sess ); 4937} 4938 4939static bool ggml_hexagon_supported_argsort (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 4940const struct ggml_tensor * src0 = op -> src [0 ];// values 4941const struct ggml_tensor * dst = op ;// indices 4942 4943if (src0 -> type != GGML_TYPE_F32 ) { 4944return false; 4945 } 4946 4947if (dst -> type != GGML_TYPE_I32 ) { 4948return false; 4949 } 4950 4951if (src0 -> ne [0 ]> (16 * 1024 )) { 4952// reject tensors with huge rows for now 4953return false; 4954 } 4955 4956return true; 4957 4958GGML_UNUSED (sess ); 4959} 4960 4961static bool ggml_hexagon_supported_rope (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 4962const struct ggml_tensor * src0 = op -> src [0 ]; 4963const struct ggml_tensor * src1 = op -> src [1 ]; 4964const struct ggml_tensor * src2 = op -> src [2 ]; 4965const struct ggml_tensor * dst = op ; 4966 4967if (!ggml_are_same_shape (src0 ,dst )) { 4968return false; 4969 } 4970 4971if (src0 -> type != GGML_TYPE_F32 || dst -> type != GGML_TYPE_F32 || src1 -> type != GGML_TYPE_I32 ) { 4972return false; 4973 } 4974 4975if (src0 -> ne [0 ] <=0 ) { 4976return false; 4977 } 4978 4979const uint32_t src0_nrows = src0 -> ne [1 ]* src0 -> ne [2 ]* src0 -> ne [3 ]; 4980if (src0_nrows == 0 ) { 4981return false; 4982 } 4983 4984const int32_t * op_params = & op -> op_params [0 ]; 4985const int n_dims = op_params [1 ]; 4986const int mode = op_params [2 ]; 4987const int n_offs = op_params [15 ]; 4988 4989if (n_dims <=0 || n_dims %2 != 0 ) { 4990return false; 4991 } 4992 4993// ggml_rope_set_offset: HVX kernels need a VLEN-aligned window start (32 f32 elems) 4994if (n_offs < 0 || (n_offs %32 != 0 )|| (n_offs + n_dims > src0 -> ne [0 ])) { 4995return false; 4996 } 4997 4998float freq_base ; 4999memcpy (& freq_base ,op_params + 5 ,sizeof (float )); 5000if (freq_base <=0.0f ) { 5001return false; 5002 } 5003 5004if (mode != GGML_ROPE_TYPE_NORMAL && 5005mode != GGML_ROPE_TYPE_NEOX && 5006mode != GGML_ROPE_TYPE_MROPE && 5007mode != GGML_ROPE_TYPE_VISION && 5008mode != GGML_ROPE_TYPE_IMROPE ) { 5009return false; 5010 } 5011 5012const bool is_mrope = (mode & GGML_ROPE_TYPE_MROPE )!= 0 ; 5013 5014// n_dims == ne0/2, so the rotation spans the full row 5015if (mode == GGML_ROPE_TYPE_VISION ) { 5016if (n_dims != (int ) (src0 -> ne [0 ] /2 )|| n_offs != 0 ) { 5017return false; 5018 } 5019 } 5020 5021if (is_mrope ) { 5022const int32_t * sections = op_params + 11 ; 5023if (sections [0 ] <=0 && sections [1 ] <=0 && sections [2 ] <=0 ) { 5024return false; 5025 } 5026 } 5027 5028const int64_t min_pos_len = (is_mrope || mode == GGML_ROPE_TYPE_VISION ) ?src0 -> ne [2 ]* 4 :src0 -> ne [2 ]; 5029if (src1 -> ne [0 ]< min_pos_len || !ggml_is_contiguous (src1 )) { 5030return false; 5031 } 5032 5033if (src2 ) { 5034if (src2 -> type != GGML_TYPE_F32 || !ggml_is_contiguous (src2 )) { 5035return false; 5036 } 5037if (src2 -> ne [0 ]< (n_dims /2 )) { 5038return false; 5039 } 5040 } 5041 5042// src0/dst elements within a row must be contiguous (nb[0] == sizeof(float)). 5043// nb[1] may exceed ne[0]*sizeof(float) when the tensor is a strided view of a larger one 5044if (src0 -> nb [0 ]!= sizeof (float )|| dst -> nb [0 ]!= sizeof (float )) { 5045return false; 5046 } 5047if (src0 -> nb [1 ]< src0 -> ne [0 ]* sizeof (float )|| dst -> nb [1 ]< dst -> ne [0 ]* sizeof (float )) { 5048return false; 5049 } 5050 5051const uint32_t n_threads = (std::min )((uint32_t )sess -> n_threads ,src0_nrows ); 5052 5053struct htp_rope_vtcm_layout layout ; 5054htp_rope_vtcm_layout_build (& layout ,src0 -> ne [0 ],n_threads ); 5055if (layout .total_bytes > sess -> vtcm_size ) { 5056return false; 5057 } 5058 5059return true; 5060} 5061 5062static bool ggml_hexagon_supported_ssm_conv (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 5063const struct ggml_tensor * src0 = op -> src [0 ]; 5064const struct ggml_tensor * src1 = op -> src [1 ]; 5065const struct ggml_tensor * dst = op ; 5066 5067// Only support FP32 for now 5068if (src0 -> type != GGML_TYPE_F32 || src1 -> type != GGML_TYPE_F32 || dst -> type != GGML_TYPE_F32 ) { 5069return false; 5070 } 5071 5072// Check IO tensor shapes and dims 5073if (src0 -> ne [3 ]!= 1 || src1 -> ne [2 ]!= 1 || src1 -> ne [3 ]!= 1 || dst -> ne [3 ]!= 1 ) { 5074return false;// src0 should be effectively 3D 5075 } 5076 5077const int d_conv = src1 -> ne [0 ]; 5078const int d_inner = src0 -> ne [1 ]; 5079const int n_t = dst -> ne [1 ]; 5080const int n_s = dst -> ne [2 ]; 5081 5082if (src0 -> ne [0 ]!= d_conv - 1 + n_t || src0 -> ne [1 ]!= d_inner || src0 -> ne [2 ]!= n_s ) { 5083return false; 5084 } 5085if (src1 -> ne [0 ]!= d_conv || src1 -> ne [1 ]!= d_inner ) { 5086return false; 5087 } 5088if (dst -> ne [0 ]!= d_inner || dst -> ne [1 ]!= n_t || dst -> ne [2 ]!= n_s ) { 5089return false; 5090 } 5091if (src0 -> nb [0 ]!= sizeof (float )|| src1 -> nb [0 ]!= sizeof (float )|| dst -> nb [0 ]!= sizeof (float )) { 5092return false; 5093 } 5094if (src0 -> nb [1 ]!= src0 -> ne [0 ]* sizeof (float )|| src1 -> nb [1 ]!= src1 -> ne [0 ]* sizeof (float )) { 5095return false; 5096 } 5097 5098return true; 5099 5100GGML_UNUSED (sess ); 5101} 5102 5103static bool ggml_hexagon_supported_im2col (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 5104const struct ggml_tensor * src1 = op -> src [1 ]; 5105const struct ggml_tensor * dst = op ; 5106 5107const bool is_2D = ((const int32_t * )op -> op_params )[6 ]== 1 ; 5108if (!is_2D ) { 5109return false; 5110 } 5111 5112// For now support F32->F32 and F32->F16 only. 5113if (src1 -> type != GGML_TYPE_F32 || (dst -> type != GGML_TYPE_F16 && dst -> type != GGML_TYPE_F32 )) { 5114return false; 5115 } 5116 5117if (!ggml_is_contiguous (src1 )|| !ggml_is_contiguous (dst )) { 5118return false; 5119 } 5120 5121// For now keep padded OPs on CPU. Will revisit once we expand coverage past patch-embed OPs. 5122const int32_t p0 = ((const int32_t * )op -> op_params )[2 ]; 5123const int32_t p1 = ((const int32_t * )op -> op_params )[3 ]; 5124if (p0 != 0 || p1 != 0 ) { 5125return false; 5126 } 5127 5128GGML_UNUSED (sess ); 5129return true; 5130} 5131 5132static bool ggml_hexagon_supported_pad (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 5133const struct ggml_tensor * src0 = op -> src [0 ]; 5134const struct ggml_tensor * dst = op ; 5135 5136if (src0 -> type != GGML_TYPE_F32 || dst -> type != GGML_TYPE_F32 ) { 5137return false; 5138 } 5139 5140const int32_t lp0 = ((const int32_t * )op -> op_params )[0 ]; 5141const int32_t rp0 = ((const int32_t * )op -> op_params )[1 ]; 5142const int32_t circular = ((const int32_t * )op -> op_params )[8 ]; 5143 5144if (circular && (lp0 > src0 -> ne [0 ]|| rp0 > src0 -> ne [0 ])) { 5145return false; 5146 } 5147 5148return true; 5149 5150GGML_UNUSED (sess ); 5151} 5152 5153static bool ggml_hexagon_supported_cumsum (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 5154const struct ggml_tensor * src0 = op -> src [0 ]; 5155const struct ggml_tensor * dst = op ; 5156 5157if (src0 -> type != GGML_TYPE_F32 || dst -> type != GGML_TYPE_F32 ) { 5158return false; 5159 } 5160 5161if (!ggml_is_contiguous (src0 )|| !ggml_is_contiguous (dst )) { 5162return false; 5163 } 5164 5165return true; 5166 5167GGML_UNUSED (sess ); 5168} 5169 5170static bool ggml_hexagon_supported_diag (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 5171const struct ggml_tensor * src0 = op -> src [0 ]; 5172const struct ggml_tensor * dst = op ; 5173 5174// diag only supports F32 currently 5175if (src0 -> type != GGML_TYPE_F32 || dst -> type != GGML_TYPE_F32 ) { 5176return false; 5177 } 5178 5179// Input must have ne[1] == 1 (vector input) 5180if (src0 -> ne [1 ]!= 1 ) { 5181return false; 5182 } 5183 5184// Output must be square in first two dimensions 5185if (dst -> ne [0 ]!= dst -> ne [1 ]|| dst -> ne [0 ]!= src0 -> ne [0 ]) { 5186return false; 5187 } 5188 5189return true; 5190 5191GGML_UNUSED (sess ); 5192} 5193 5194static bool ggml_hexagon_supported_solve_tri (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 5195const struct ggml_tensor * src0 = op -> src [0 ];// A 5196const struct ggml_tensor * src1 = op -> src [1 ];// B 5197const struct ggml_tensor * dst = op ;// X 5198 5199if (src0 -> type != GGML_TYPE_F32 || src1 -> type != GGML_TYPE_F32 || dst -> type != GGML_TYPE_F32 ) { 5200return false; 5201 } 5202 5203if (src0 -> ne [0 ]!= src0 -> ne [1 ]) { 5204return false; 5205 } 5206 5207if (src0 -> ne [1 ]!= src1 -> ne [1 ]) { 5208return false; 5209 } 5210 5211if (src0 -> ne [2 ]!= src1 -> ne [2 ]|| src0 -> ne [3 ]!= src1 -> ne [3 ]) { 5212return false; 5213 } 5214 5215if (dst -> ne [0 ]!= src1 -> ne [0 ]|| dst -> ne [1 ]!= src1 -> ne [1 ]|| dst -> ne [2 ]!= src1 -> ne [2 ]|| dst -> ne [3 ]!= src1 -> ne [3 ]) { 5216return false; 5217 } 5218 5219return true; 5220 5221GGML_UNUSED (sess ); 5222} 5223 5224static bool ggml_hexagon_supported_tri (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 5225 5226const struct ggml_tensor * src0 = op -> src [0 ]; 5227const struct ggml_tensor * dst = op ; 5228 5229if (src0 -> type != GGML_TYPE_F32 ) {return false; } 5230if (dst -> type != GGML_TYPE_F32 ) {return false; } 5231if (!ggml_are_same_shape (src0 ,dst )) {return false; } 5232if (!ggml_is_contiguous (src0 )|| !ggml_is_contiguous (dst )) {return false; } 5233 5234return true; 5235 5236GGML_UNUSED (sess ); 5237} 5238 5239static const char * ggml_backend_hexagon_name (ggml_backend_t backend ) { 5240auto sess = static_cast < ggml_hexagon_session *> (backend -> context ); 5241return sess -> c_name (); 5242} 5243 5244static void ggml_backend_hexagon_free (ggml_backend_t backend ) { 5245// we just need to delete the backend here 5246// the sessions are allocated & freed as part of the registry 5247delete backend ; 5248} 5249 5250static htp_op_code op_remap_to_htp (const ggml_tensor * t ) { 5251switch (t -> op ) { 5252case GGML_OP_FLASH_ATTN_EXT :return HTP_OP_FLASH_ATTN_EXT ; 5253case GGML_OP_MUL_MAT :return HTP_OP_MUL_MAT ; 5254case GGML_OP_MUL_MAT_ID :return HTP_OP_MUL_MAT_ID ; 5255case GGML_OP_MUL :return HTP_OP_MUL ; 5256case GGML_OP_ADD :return HTP_OP_ADD ; 5257case GGML_OP_ADD_ID :return HTP_OP_ADD_ID ; 5258case GGML_OP_SUB :return HTP_OP_SUB ; 5259case GGML_OP_DIV :return HTP_OP_DIV ; 5260case GGML_OP_CPY :return HTP_OP_CPY ; 5261case GGML_OP_CONT :return HTP_OP_CPY ; 5262case GGML_OP_GET_ROWS :return HTP_OP_GET_ROWS ; 5263case GGML_OP_SET_ROWS :return HTP_OP_SET_ROWS ; 5264case GGML_OP_SUM_ROWS :return HTP_OP_SUM_ROWS ; 5265case GGML_OP_ARGSORT :return HTP_OP_ARGSORT ; 5266case GGML_OP_NORM :return HTP_OP_NORM ; 5267case GGML_OP_L2_NORM :return HTP_OP_L2_NORM ; 5268case GGML_OP_RMS_NORM :return HTP_OP_RMS_NORM ; 5269case GGML_OP_CONCAT :return HTP_OP_CONCAT ; 5270case GGML_OP_SCALE :return HTP_OP_SCALE ; 5271case GGML_OP_CLAMP :return HTP_OP_CLAMP ; 5272case GGML_OP_LEAKY_RELU :return HTP_OP_LEAKY_RELU ; 5273case GGML_OP_SQR :return HTP_OP_SQR ; 5274case GGML_OP_SQRT :return HTP_OP_SQRT ; 5275case GGML_OP_LOG :return HTP_OP_UNARY_LOG ; 5276case GGML_OP_SOFT_MAX :return HTP_OP_SOFTMAX ; 5277case GGML_OP_SSM_CONV :return HTP_OP_SSM_CONV ; 5278case GGML_OP_GATED_DELTA_NET :return HTP_OP_GATED_DELTA_NET ; 5279case GGML_OP_ROPE :return HTP_OP_ROPE ; 5280case GGML_OP_REPEAT :return HTP_OP_REPEAT ; 5281case GGML_OP_CUMSUM :return HTP_OP_CUMSUM ; 5282case GGML_OP_FILL :return HTP_OP_FILL ; 5283case GGML_OP_DIAG :return HTP_OP_DIAG ; 5284case GGML_OP_SOLVE_TRI :return HTP_OP_SOLVE_TRI ; 5285case GGML_OP_TRI :return HTP_OP_TRI ; 5286case GGML_OP_PAD :return HTP_OP_PAD ; 5287case GGML_OP_IM2COL :return HTP_OP_IM2COL ; 5288 5289case GGML_OP_UNARY : 5290switch (ggml_get_unary_op (t )) { 5291case GGML_UNARY_OP_SILU :return HTP_OP_UNARY_SILU ; 5292case GGML_UNARY_OP_GELU :return HTP_OP_UNARY_GELU ; 5293case GGML_UNARY_OP_GELU_QUICK :return HTP_OP_UNARY_GELU ; 5294case GGML_UNARY_OP_SIGMOID :return HTP_OP_UNARY_SIGMOID ; 5295case GGML_UNARY_OP_NEG :return HTP_OP_UNARY_NEG ; 5296case GGML_UNARY_OP_EXP :return HTP_OP_UNARY_EXP ; 5297case GGML_UNARY_OP_SOFTPLUS :return HTP_OP_UNARY_SOFTPLUS ; 5298case GGML_UNARY_OP_TANH :return HTP_OP_UNARY_TANH ; 5299case GGML_UNARY_OP_ABS :return HTP_OP_UNARY_ABS ; 5300case GGML_UNARY_OP_RELU :return HTP_OP_UNARY_RELU ; 5301default : 5302break ; 5303 } 5304break ; 5305 5306case GGML_OP_GLU : 5307switch (ggml_get_glu_op (t )) { 5308case GGML_GLU_OP_SWIGLU :return HTP_OP_GLU_SWIGLU ; 5309case GGML_GLU_OP_SWIGLU_OAI :return HTP_OP_GLU_SWIGLU_OAI ; 5310case GGML_GLU_OP_SWIGLU_CLAMP :return HTP_OP_GLU_SWIGLU_CLAMP ; 5311case GGML_GLU_OP_GEGLU :return HTP_OP_GLU_GEGLU ; 5312default :break ; 5313 } 5314break ; 5315 5316default : 5317GGML_ABORT ("\nggml-hex: graph-compute %s is not supported\n" ,ggml_op_desc (t )); 5318 } 5319return HTP_OP_INVALID ; 5320} 5321 5322static inline bool op_is_compute (ggml_tensor * node ) 5323{ 5324return !ggml_op_is_empty (node -> op )&& !ggml_is_empty (node )&& (node -> flags & GGML_TENSOR_FLAG_COMPUTE ); 5325} 5326 5327static bool mm_is_hmx_eligible (const ggml_tensor * t ) { 5328if (opt_nhmx == 0 ) {return false; } 5329 5330const ggml_tensor * src0 = t -> src [0 ]; 5331const ggml_tensor * src1 = t -> src [1 ]; 5332 5333const int wtype = src0 -> type ; 5334const bool is_repack = ggml_hexagon_is_repack_type ((ggml_type )wtype ); 5335const bool is_matmul_id = (t -> op == GGML_OP_MUL_MAT_ID ); 5336const bool is_batched = (src0 -> ne [2 ]* src0 -> ne [3 ]> 1 || src1 -> ne [2 ]* src1 -> ne [3 ]> 1 ); 5337 5338const int ne01_padded = is_repack ?hex_round_up (src0 -> ne [1 ],32 ) :src0 -> ne [1 ]; 5339 5340return ggml_hexagon_matmul_is_hmx_eligible (src0 ,src1 ,t ,ne01_padded ,is_matmul_id ,is_batched ); 5341} 5342 5343static bool is_supported_mul_mat_nx_kernel (const ggml_tensor * src0 ,const struct htp_mm_kernel_params * kparams ) { 5344if (kparams -> n_hmx ) { 5345return kparams -> kernel_type == HTP_MM_KERNEL_HMX_2D ; 5346 } 5347 5348if (!ggml_hexagon_is_repack_type (src0 -> type )) { 5349return false; 5350 } 5351 5352return kparams -> kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW || kparams -> kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT ; 5353} 5354 5355static bool is_supported_mul_mat_id_nx_kernel (const ggml_tensor * src0 ,const struct htp_mm_kernel_params * kparams ) { 5356if (kparams -> n_hmx ) { 5357return kparams -> kernel_type == HTP_MM_KERNEL_HMX_2D ; 5358 } 5359 5360if (!ggml_hexagon_is_repack_type (src0 -> type )) { 5361return false; 5362 } 5363 5364return kparams -> kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW || kparams -> kernel_type == HTP_MM_KERNEL_HVX_QUANT_BLOCK ; 5365} 5366 5367static bool is_mergeable_mul_mat (const ggml_tensor * t ) { 5368if (t -> op != GGML_OP_MUL_MAT )return false; 5369 5370const ggml_tensor * src0 = t -> src [0 ]; 5371const ggml_tensor * src1 = t -> src [1 ]; 5372if (src1 -> type != GGML_TYPE_F32 )return false; 5373if (src0 -> ne [2 ]!= 1 || src0 -> ne [3 ]!= 1 )return false; 5374 5375if (mm_is_hmx_eligible (t )) { 5376return ggml_hexagon_is_hmx_weight_type (src0 -> type ); 5377 } 5378 5379return ggml_hexagon_is_repack_type (src0 -> type ); 5380} 5381 5382static bool is_mergeable_mul_mat_pair (const ggml_tensor * n1 ,const ggml_tensor * n2 ) { 5383if (!is_mergeable_mul_mat (n1 )|| !is_mergeable_mul_mat (n2 )) { 5384return false; 5385 } 5386if (n1 -> src [1 ]!= n2 -> src [1 ]) { 5387return false; 5388 } 5389if (n1 -> src [0 ]-> ne [0 ]!= n2 -> src [0 ]-> ne [0 ]) { 5390return false; 5391 } 5392if (n1 -> src [0 ]-> type != n2 -> src [0 ]-> type ) { 5393return false; 5394 } 5395if (mm_is_hmx_eligible (n1 )!= mm_is_hmx_eligible (n2 )) { 5396return false; 5397 } 5398return true; 5399} 5400 5401static bool is_mergeable_mul_mat_id (const ggml_tensor * t ) { 5402if (t -> op != GGML_OP_MUL_MAT_ID )return false; 5403 5404const ggml_tensor * src0 = t -> src [0 ]; 5405return ggml_hexagon_is_repack_type (src0 -> type ); 5406} 5407 5408static bool is_mergeable_mul_mat_id_pair (const ggml_tensor * n1 ,const ggml_tensor * n2 ) { 5409if (!is_mergeable_mul_mat_id (n1 )|| !is_mergeable_mul_mat_id (n2 )) { 5410return false; 5411 } 5412if (n1 -> src [1 ]!= n2 -> src [1 ]) { 5413return false; 5414 } 5415if (n1 -> src [2 ]!= n2 -> src [2 ]) { 5416return false; 5417 } 5418if (n1 -> src [0 ]-> ne [0 ]!= n2 -> src [0 ]-> ne [0 ]) { 5419return false; 5420 } 5421if (n1 -> src [0 ]-> ne [2 ]!= n2 -> src [0 ]-> ne [2 ]) { 5422return false; 5423 } 5424if (n1 -> src [0 ]-> type != n2 -> src [0 ]-> type ) { 5425return false; 5426 } 5427if (mm_is_hmx_eligible (n1 )!= mm_is_hmx_eligible (n2 )) { 5428return false; 5429 } 5430return true; 5431} 5432 5433static ggml_status ggml_backend_hexagon_graph_compute (ggml_backend_t backend ,ggml_cgraph * graph ) { 5434auto sess = static_cast < ggml_hexagon_session *> (backend -> context ); 5435 5436if (sess -> last_error > HTP_STATUS_OK ) { 5437return GGML_STATUS_FAILED ; 5438 } 5439 5440HEX_VERBOSE ("ggml-hex: %s graph-compute n_nodes %d\n" ,sess -> c_name (),graph -> n_nodes ); 5441 5442const std::vector < htp_opnode > * nodes_ptr = nullptr ; 5443 std::vector < htp_opnode > computed_nodes ; 5444 5445// Check for cache hit 5446bool cache_hit = (graph -> uid != 0 && sess -> cached_uid == graph -> uid ); 5447if (cache_hit ) { 5448nodes_ptr = & sess -> cached_nodes ; 5449 }else { 5450// Tag fusable tensors in graph 5451for (int i = 0 ;i < graph -> n_nodes ;i ++ ) { 5452auto * extra = (ggml_hexagon_tensor_extra * )graph -> nodes [i ]-> extra ; 5453if (!extra )continue ; 5454 5455extra -> flags &= ~GGML_HEXAGON_TENSOR_FUSEABLE ; 5456 5457if (graph -> nodes [i ]-> op == GGML_OP_RMS_NORM && ggml_can_fuse (graph ,i , {GGML_OP_RMS_NORM ,GGML_OP_MUL })) { 5458extra -> flags |=GGML_HEXAGON_TENSOR_FUSEABLE ; 5459 }else if (graph -> nodes [i ]-> op == GGML_OP_MUL_MAT || graph -> nodes [i ]-> op == GGML_OP_MUL_MAT_ID ) { 5460if ((i + 1 < graph -> n_nodes && graph -> nodes [i + 1 ]-> op == GGML_OP_ADD && ggml_can_fuse (graph ,i , {graph -> nodes [i ]-> op ,GGML_OP_ADD }))|| 5461ggml_node_has_n_uses (graph ,i ,1 )) { 5462extra -> flags |=GGML_HEXAGON_TENSOR_FUSEABLE ; 5463 } 5464 } 5465 } 5466 5467computed_nodes .reserve (graph -> n_nodes ); 5468 5469for (int i = 0 ;i < graph -> n_nodes ;++ i ) { 5470ggml_tensor * n = graph -> nodes [i ]; 5471if (!op_is_compute (n )) { 5472continue ; 5473 } 5474 5475htp_opnode node (HTP_OP_INVALID ,n ); 5476node .opcode = op_remap_to_htp (n ); 5477if (node .opcode == HTP_OP_MUL_MAT || node .opcode == HTP_OP_MUL_MAT_ID ) { 5478ggml_hexagon_precompute_matmul_params (sess , 5479node .node -> src [0 ],node .node -> src [1 ],node .node , 5480 (struct htp_mm_kernel_params * )node .kernel_params 5481 ); 5482 }else if (node .opcode == HTP_OP_FLASH_ATTN_EXT ) { 5483ggml_hexagon_precompute_flash_attn_params (sess , 5484node .node , 5485 (struct htp_fa_kernel_params * )node .kernel_params 5486 ); 5487 }else if (htp_op_is_unary (node .opcode )) { 5488auto inputs = node .get_inputs (); 5489const struct ggml_tensor * src0 = inputs [0 ]; 5490const struct ggml_tensor * src1 = inputs .size ()> 1 ?inputs [1 ] :nullptr ; 5491ggml_hexagon_precompute_unary_params (sess , 5492node .opcode ,src0 ,src1 ,node .dst (), 5493 (struct htp_unary_kernel_params * )node .kernel_params 5494 ); 5495 }else if (node .opcode == HTP_OP_GET_ROWS ) { 5496ggml_hexagon_precompute_get_rows_params (sess , 5497node .node -> src [0 ],node .node -> src [1 ],node .dst (), 5498 (struct htp_get_rows_kernel_params * )node .kernel_params 5499 ); 5500 }else if (node .opcode == HTP_OP_SET_ROWS ) { 5501ggml_hexagon_precompute_set_rows_params (sess , 5502node .node -> src [0 ],node .node -> src [1 ],node .dst (), 5503 (struct htp_set_rows_kernel_params * )node .kernel_params 5504 ); 5505 }else if (node .opcode == HTP_OP_ROPE ) { 5506ggml_hexagon_precompute_rope_params (sess , 5507node .node , 5508 (struct htp_rope_kernel_params * )node .kernel_params 5509 ); 5510 } 5511computed_nodes .push_back (std::move (node )); 5512 } 5513 5514if (graph -> uid != 0 ) { 5515sess -> cached_uid = graph -> uid ; 5516sess -> cached_nodes = std::move (computed_nodes ); 5517nodes_ptr = & sess -> cached_nodes ; 5518 }else { 5519nodes_ptr = & computed_nodes ; 5520 } 5521 } 5522 5523// Queue and execute 5524for (const auto & node :* nodes_ptr ) { 5525sess -> enqueue_op (node ); 5526 } 5527 5528if (sess -> last_error > HTP_STATUS_OK ) { 5529return GGML_STATUS_FAILED ; 5530 } 5531 5532return GGML_STATUS_SUCCESS ; 5533} 5534 5535static void ggml_backend_hexagon_synchronize (ggml_backend_t backend ) { 5536auto sess = static_cast < ggml_hexagon_session *> (backend -> context ); 5537 5538HEX_VERBOSE ("ggml-hex: %s synchronize\n" ,sess -> c_name ()); 5539 5540// Wait until all pending ops complete 5541sess -> flush_sync (); 5542if (sess -> last_error > HTP_STATUS_OK ) { 5543GGML_ABORT ("ggml-hex: %s synchronize failed : dsp-error %s\n" ,sess -> c_name (),status_to_str (sess -> last_error )); 5544 } 5545} 5546 5547enum ggml_hexagon_mem_range_type { 5548HEXAGON_MEM_RANGE_TYPE_SRC , 5549HEXAGON_MEM_RANGE_TYPE_DST , 5550}; 5551 5552struct ggml_hexagon_mem_range { 5553uint64_t pb ; 5554uint64_t p0 ; 5555uint64_t p1 ; 5556ggml_hexagon_mem_range_type pt ; 5557}; 5558 5559struct ggml_hexagon_mem_ranges { 5560 std::vector < ggml_hexagon_mem_range > ranges ; 5561 5562void reset () { 5563ranges .clear (); 5564 } 5565 5566void add (const ggml_hexagon_mem_range & mr ) { 5567ranges .push_back (mr ); 5568 } 5569 5570bool check (const ggml_hexagon_mem_range & mr )const { 5571for (const auto & cmp :ranges ) { 5572if (mr .pb != cmp .pb ) { 5573continue ; 5574 } 5575if (mr .pt == HEXAGON_MEM_RANGE_TYPE_SRC && cmp .pt == HEXAGON_MEM_RANGE_TYPE_SRC ) { 5576continue ; 5577 } 5578if (mr .p0 < cmp .p1 && mr .p1 > cmp .p0 ) { 5579return false; 5580 } 5581 } 5582return true; 5583 } 5584}; 5585 5586static ggml_hexagon_mem_range ggml_hexagon_mem_range_from_tensor (const ggml_tensor * tensor ,ggml_hexagon_mem_range_type pt ) { 5587const ggml_tensor * base = tensor -> view_src ?tensor -> view_src :tensor ; 5588ggml_hexagon_mem_range mr ; 5589if (tensor -> buffer ) { 5590mr = { 5591/*.pb =*/ (uint64_t )tensor -> buffer , 5592/*.p0 =*/ (uint64_t )tensor -> data , 5593/*.p1 =*/ (uint64_t )tensor -> data + ggml_backend_buft_get_alloc_size (tensor -> buffer -> buft ,tensor ), 5594/*.pt =*/ pt , 5595 }; 5596 }else { 5597mr = { 5598/*.pb =*/ (uint64_t )base , 5599/*.p0 =*/ 0 , 5600/*.p1 =*/ 1024 , 5601/*.pt =*/ pt , 5602 }; 5603 } 5604return mr ; 5605} 5606 5607static void ggml_hexagon_mem_ranges_add_node (ggml_hexagon_mem_ranges & mrs ,const htp_opnode & node ) { 5608if (node .is_empty ())return ; 5609 5610for (int i = 0 ;i < GGML_MAX_SRC ;i ++ ) { 5611if (node .node -> src [i ]) { 5612mrs .add (ggml_hexagon_mem_range_from_tensor (node .node -> src [i ],HEXAGON_MEM_RANGE_TYPE_SRC )); 5613 } 5614 } 5615for (const auto * fused :node .fused ) { 5616for (int i = 0 ;i < GGML_MAX_SRC ;i ++ ) { 5617if (fused -> src [i ]) { 5618mrs .add (ggml_hexagon_mem_range_from_tensor (fused -> src [i ],HEXAGON_MEM_RANGE_TYPE_SRC )); 5619 } 5620 } 5621 } 5622mrs .add (ggml_hexagon_mem_range_from_tensor (node .dst (),HEXAGON_MEM_RANGE_TYPE_DST )); 5623} 5624 5625static bool ggml_hexagon_mem_ranges_check_node (const ggml_hexagon_mem_ranges & mrs ,const htp_opnode & node ) { 5626if (node .is_empty ())return true; 5627 5628for (int i = 0 ;i < GGML_MAX_SRC ;i ++ ) { 5629if (node .node -> src [i ]) { 5630if (!mrs .check (ggml_hexagon_mem_range_from_tensor (node .node -> src [i ],HEXAGON_MEM_RANGE_TYPE_SRC ))) { 5631return false; 5632 } 5633 } 5634 } 5635for (const auto * fused :node .fused ) { 5636for (int i = 0 ;i < GGML_MAX_SRC ;i ++ ) { 5637if (fused -> src [i ]) { 5638if (!mrs .check (ggml_hexagon_mem_range_from_tensor (fused -> src [i ],HEXAGON_MEM_RANGE_TYPE_SRC ))) { 5639return false; 5640 } 5641 } 5642 } 5643 } 5644return mrs .check (ggml_hexagon_mem_range_from_tensor (node .dst (),HEXAGON_MEM_RANGE_TYPE_DST )); 5645} 5646 5647static std::vector < int > ggml_hexagon_graph_optimize_reorder (const std::vector < htp_opnode > & nodes ) { 5648const int n = nodes .size (); 5649 5650 std::vector < int > res ; 5651res .reserve (n ); 5652 5653 std::vector < bool > used (n ,false ); 5654 5655ggml_hexagon_mem_ranges mrs ; 5656 5657// The main goal here is to stack the MUL_MAT ops with the same src1 input. 5658// This allows us to reuse dynamically quantized src1 in VTCM. 5659 5660for (int i0 = 0 ;i0 < n ;i0 ++ ) { 5661if (used [i0 ]) { 5662continue ; 5663 } 5664 5665const auto & node0 = nodes [i0 ]; 5666 5667if (!node0 .stackable ()) { 5668res .push_back (i0 ); 5669used [i0 ]= true; 5670continue ; 5671 } 5672 5673// that many nodes forward to search for stackable nodes that can reuse VTCM 5674constexpr int N_FORWARD = 16 ; 5675 5676 std::vector < int > stack ; 5677stack .push_back (i0 ); 5678 5679mrs .reset (); 5680 5681for (int i1 = i0 + 1 ;i1 < i0 + N_FORWARD && i1 < n ;i1 ++ ) { 5682if (used [i1 ]) { 5683continue ; 5684 } 5685 5686const auto & node1 = nodes [i1 ]; 5687 5688if (node1 .stackable ()&& node1 .same_input (node0 )&& ggml_hexagon_mem_ranges_check_node (mrs ,node1 )) { 5689stack .push_back (i1 ); 5690 }else { 5691ggml_hexagon_mem_ranges_add_node (mrs ,node1 ); 5692 } 5693 } 5694 5695for (int idx :stack ) { 5696res .push_back (idx ); 5697used [idx ]= true; 5698 } 5699 } 5700 5701return res ; 5702} 5703 5704static void ggml_backend_hexagon_graph_optimize (ggml_backend_t backend ,ggml_cgraph * gf ,ggml_backend_graph_optimize_params * params ) { 5705GGML_UNUSED (params ); 5706 5707const int n = gf -> n_nodes ; 5708 5709constexpr int MAX_FUSE = 16 ; 5710 5711enum ggml_op ops [MAX_FUSE ]; 5712 5713 std::vector < htp_opnode > nodes ; 5714nodes .reserve (gf -> n_nodes ); 5715 5716// Pack nodes for reordering 5717for (int i = 0 ;i < n ;i ++ ) { 5718htp_opnode node (HTP_OP_INVALID ,gf -> nodes [i ]); 5719 5720// fuse only ops that start with these operations 5721// can be expanded when needed 5722if (node .op ()== GGML_OP_ADD || 5723node .op ()== GGML_OP_NORM || 5724node .op ()== GGML_OP_RMS_NORM ) { 5725ops [0 ]= node .op (); 5726 5727int f = i + 1 ; 5728while (f < n && f < i + MAX_FUSE ) { 5729// conservatively allow fusing only these ops 5730// can be expanded when needed 5731if (gf -> nodes [f ]-> op != GGML_OP_ADD && 5732gf -> nodes [f ]-> op != GGML_OP_MUL && 5733gf -> nodes [f ]-> op != GGML_OP_NORM && 5734gf -> nodes [f ]-> op != GGML_OP_RMS_NORM ) { 5735break ; 5736 } 5737ops [f - i ]= gf -> nodes [f ]-> op ; 5738f ++ ; 5739 } 5740 5741f -= i ; 5742for (;f > 1 ;f -- ) { 5743if (ggml_can_fuse (gf ,i ,ops ,f )) { 5744break ; 5745 } 5746 } 5747 5748// add the fused tensors into the node info so we can unfuse them later 5749for (int k = 1 ;k < f ;k ++ ) { 5750++ i ; 5751 5752// the .dst() becomes the last fused tensor 5753node .add_fused (gf -> nodes [i ]); 5754 } 5755 } 5756 5757nodes .push_back (std::move (node )); 5758 } 5759 5760const auto order = ggml_hexagon_graph_optimize_reorder (nodes ); 5761 5762// unfuse 5763 { 5764int j = 0 ; 5765for (const auto i :order ) { 5766const auto & node = nodes [i ]; 5767 5768gf -> nodes [j ++ ]= node .node ; 5769 5770for (auto * fused :node .fused ) { 5771gf -> nodes [j ++ ]= fused ; 5772 } 5773 } 5774 } 5775 5776GGML_UNUSED (backend ); 5777} 5778 5779static uint64_t ggml_hexagon_session_key (const ggml_hexagon_session * sess ) { 5780return ((uint64_t ) (uint32_t )sess -> phys_idx <<32 ) | (uint32_t )sess -> virt_idx ; 5781} 5782 5783static bool ggml_hexagon_cpy_tensor_async_phys (ggml_backend_t backend_src ,ggml_backend_t backend_dst ,const ggml_tensor * src ,ggml_tensor * dst ) { 5784auto sess_src = static_cast < ggml_hexagon_session *> (backend_src -> context ); 5785auto sess_dst = static_cast < ggml_hexagon_session *> (backend_dst -> context ); 5786auto sbuf_dst = (ggml_hexagon_shared_buffer * )dst -> buffer -> context ; 5787 5788if (!sess_src -> clone_buffer (sbuf_dst )) {return false; } 5789 5790const uint64_t src_key = ggml_hexagon_session_key (sess_src ); 5791auto & fence_slot = sess_dst -> cpy_fence_slots [src_key ]; 5792if (!fence_slot ) { 5793fence_slot = (volatile uint32_t * )sess_dst -> alloc_fence (1 ); 5794 } 5795 5796if (!sess_src -> clone_buffer (sess_dst -> fence_buf )) {return false; } 5797 5798if (++ sess_dst -> fence_seq == 0 )sess_dst -> fence_seq = 1 ; 5799uint32_t fence_seq = sess_dst -> fence_seq ; 5800 5801HEX_VERBOSE ("ggml-hex: %s cpy-tensor-async %s -> %s size %zu : seq 0x%x\n" , 5802sess_dst -> name .c_str (),src -> name ,dst -> name ,ggml_nbytes (src ),fence_seq ); 5803 5804// dummy fence extra (must be static) 5805static ggml_hexagon_tensor_extra fence_extra { {},0 ,GGML_HEXAGON_TENSOR_FENCE }; 5806 5807ggml_tensor fence_tensor {}; 5808fence_tensor .buffer = & sess_dst -> fence_buf -> backend_buffer ; 5809fence_tensor .extra = & fence_extra ; 5810fence_tensor .data = (void * )fence_slot ; 5811fence_tensor .type = GGML_TYPE_I32 ; 5812fence_tensor .ne [0 ]= 1 ; 5813fence_tensor .ne [1 ]= 1 ; 5814fence_tensor .ne [2 ]= 1 ; 5815fence_tensor .ne [3 ]= 1 ; 5816fence_tensor .nb [0 ]= sizeof (int32_t ); 5817fence_tensor .nb [1 ]= sizeof (int32_t ); 5818fence_tensor .nb [2 ]= sizeof (int32_t ); 5819fence_tensor .nb [3 ]= sizeof (int32_t ); 5820fence_tensor .op = GGML_OP_NONE ; 5821 5822sess_src -> enqueue_cpy (src ,dst ,& fence_tensor ,fence_seq ); 5823sess_dst -> enqueue_fence (& fence_tensor ,fence_seq ,/* wait = */ true); 5824 5825sess_dst -> add_peer (sess_src ); 5826 5827return true; 5828} 5829 5830static bool ggml_hexagon_cpy_tensor_async_virt (ggml_backend_t backend_src ,ggml_backend_t backend_dst ,const ggml_tensor * src ,ggml_tensor * dst ) { 5831auto sess_src = static_cast < ggml_hexagon_session *> (backend_src -> context ); 5832auto sess_dst = static_cast < ggml_hexagon_session *> (backend_dst -> context ); 5833auto sbuf_src = (ggml_hexagon_shared_buffer * )src -> buffer -> context ; 5834 5835if (!sess_dst -> clone_buffer (sbuf_src )) {return false; } 5836 5837HEX_VERBOSE ("ggml-hex: %s cpy-tensor-async %s -> %s size %zu\n" , 5838sess_dst -> name .c_str (),src -> name ,dst -> name ,ggml_nbytes (src )); 5839 5840sess_dst -> enqueue_cpy (src ,dst ); 5841sess_dst -> add_peer (sess_src ); 5842 5843return true; 5844} 5845 5846static bool ggml_backend_hexagon_cpy_tensor_async (ggml_backend_t backend_src ,ggml_backend_t backend_dst ,const ggml_tensor * src ,ggml_tensor * dst ) { 5847if (!ggml_backend_is_hexagon (backend_src )|| !ggml_backend_is_hexagon (backend_dst )) { 5848return false; 5849 } 5850 5851// FIXME: ggml-meta needs to call init_tensor on auxiliary tensors 5852if (!dst -> extra ) { 5853ggml_backend_buffer_init_tensor (dst -> buffer ,dst ); 5854 } 5855 5856auto * dst_extra = static_cast < ggml_hexagon_tensor_extra *> (dst -> extra ); 5857const auto * src_extra = static_cast < const ggml_hexagon_tensor_extra *> (src -> extra ); 5858dst_extra -> flags = src_extra -> flags & ~GGML_HEXAGON_TENSOR_FUSEABLE ; 5859 5860auto sess_src = static_cast < ggml_hexagon_session *> (backend_src -> context ); 5861auto sess_dst = static_cast < ggml_hexagon_session *> (backend_dst -> context ); 5862 5863if (sess_src == sess_dst ) { 5864HEX_VERBOSE ("ggml-hex: %s cpy-tensor-async %s -> %s size %zu\n" ,sess_dst -> name .c_str (),src -> name ,dst -> name ,ggml_nbytes (src )); 5865sess_src -> enqueue_cpy (src ,dst ); 5866return true; 5867 } 5868 5869if (sess_src -> phys_idx != sess_dst -> phys_idx ) 5870return ggml_hexagon_cpy_tensor_async_phys (backend_src ,backend_dst ,src ,dst ); 5871 5872return ggml_hexagon_cpy_tensor_async_virt (backend_src ,backend_dst ,src ,dst ); 5873} 5874 5875static ggml_backend_event_t ggml_backend_hexagon_device_event_new (ggml_backend_dev_t dev ) { 5876auto dev_ctx = static_cast < ggml_backend_hexagon_device_context *> (dev -> context ); 5877auto sess = dev_ctx -> session (); 5878 5879ggml_hexagon_event * hex_event = new ggml_hexagon_event (); 5880hex_event -> fence_sess = sess ; 5881hex_event -> sess = sess ; 5882hex_event -> fence_slot = (volatile uint32_t * )sess -> alloc_fence (1 ); 5883 5884static ggml_hexagon_tensor_extra fence_extra { {},0 ,GGML_HEXAGON_TENSOR_FENCE }; 5885hex_event -> fence_tensor .buffer = & sess -> fence_buf -> backend_buffer ; 5886hex_event -> fence_tensor .extra = & fence_extra ; 5887hex_event -> fence_tensor .data = (void * )hex_event -> fence_slot ; 5888hex_event -> fence_tensor .type = GGML_TYPE_I32 ; 5889hex_event -> fence_tensor .ne [0 ]= 1 ; 5890hex_event -> fence_tensor .ne [1 ]= 1 ; 5891hex_event -> fence_tensor .ne [2 ]= 1 ; 5892hex_event -> fence_tensor .ne [3 ]= 1 ; 5893hex_event -> fence_tensor .nb [0 ]= sizeof (int32_t ); 5894hex_event -> fence_tensor .nb [1 ]= sizeof (int32_t ); 5895hex_event -> fence_tensor .nb [2 ]= sizeof (int32_t ); 5896hex_event -> fence_tensor .nb [3 ]= sizeof (int32_t ); 5897hex_event -> fence_tensor .op = GGML_OP_NONE ; 5898 5899HEX_VERBOSE ("ggml-hex: %s event-new : event %p fence %p\n" ,ggml_backend_dev_name (dev ), (void * )hex_event , (void * )hex_event -> fence_slot ); 5900 5901return new ggml_backend_event { 5902/* .device = */ dev , 5903/* .context = */ hex_event , 5904 }; 5905} 5906 5907static void ggml_hexagon_event_synchronize (ggml_backend_dev_t dev ,ggml_hexagon_event * hex_event ) { 5908if (hex_event -> seq == 0 ) { 5909return ; 5910 } 5911 5912HEX_VERBOSE ("ggml-hex: %s event-synchronize : event %p seq 0x%x fence %p\n" , 5913ggml_backend_dev_name (dev ), (void * )hex_event ,hex_event -> seq , (void * )hex_event -> fence_slot ); 5914 5915auto * fence = reinterpret_cast < const volatile std::atomic < uint32_t > *> (hex_event -> fence_slot ); 5916 5917if ((int32_t )(fence [0 ].load (std::memory_order_relaxed )- hex_event -> seq )< 0 ) { 5918hex_event -> sess -> flush_async (); 5919 } 5920 5921while (true) { 5922if ((int32_t )(fence [0 ].load (std::memory_order_acquire )- hex_event -> seq ) >=0 ) { 5923uint32_t status = fence [1 ].load (std::memory_order_acquire ); 5924if (status > HTP_STATUS_OK ) { 5925GGML_ABORT ("ggml-hex: %s event-synchronize failed : dsp-error %s\n" , 5926hex_event -> sess -> c_name (),status_to_str (status )); 5927 } 5928break ; 5929 } 5930 std::this_thread::yield (); 5931 } 5932} 5933 5934static void ggml_backend_hexagon_device_event_free (ggml_backend_dev_t dev ,ggml_backend_event_t event ) { 5935auto * hex_event = static_cast < ggml_hexagon_event *> (event -> context ); 5936ggml_hexagon_event_synchronize (dev ,hex_event ); 5937HEX_VERBOSE ("ggml-hex: %s event-free : event %p\n" ,ggml_backend_dev_name (dev ), (void * )hex_event ); 5938hex_event -> fence_sess -> free_fence ((void * )hex_event -> fence_slot ,1 ); 5939delete hex_event ; 5940delete event ; 5941} 5942 5943static void ggml_backend_hexagon_device_event_synchronize (ggml_backend_dev_t dev ,ggml_backend_event_t event ) { 5944auto * hex_event = static_cast < ggml_hexagon_event *> (event -> context ); 5945ggml_hexagon_event_synchronize (dev ,hex_event ); 5946} 5947 5948static void ggml_backend_hexagon_event_record (ggml_backend_t backend ,ggml_backend_event_t event ) { 5949auto sess = static_cast < ggml_hexagon_session *> (backend -> context ); 5950auto hex_event = static_cast < ggml_hexagon_event *> (event -> context ); 5951 5952if (++ sess -> fence_seq == 0 )sess -> fence_seq = 1 ; 5953hex_event -> sess = sess ; 5954hex_event -> seq = sess -> fence_seq ; 5955 5956sess -> enqueue_fence (& hex_event -> fence_tensor ,hex_event -> seq ,/* wait = */ false); 5957 5958HEX_VERBOSE ("ggml-hex: %s event-record : event %p seq 0x%x fence %p\n" , 5959sess -> c_name (), (void * )hex_event ,hex_event -> seq , (void * )hex_event -> fence_slot ); 5960} 5961 5962static void ggml_backend_hexagon_event_wait (ggml_backend_t backend ,ggml_backend_event_t event ) { 5963auto sess = static_cast < ggml_hexagon_session *> (backend -> context ); 5964auto hex_event = static_cast < ggml_hexagon_event *> (event -> context ); 5965 5966if (hex_event -> seq == 0 ) { 5967return ; 5968 } 5969 5970HEX_VERBOSE ("ggml-hex: %s event-wait : event %p seq 0x%x fence %p\n" , 5971sess -> c_name (), (void * )hex_event ,hex_event -> seq , (void * )hex_event -> fence_slot ); 5972 5973// same physical NPU runs sequentially in FIFO order 5974if (sess -> phys_idx == hex_event -> sess -> phys_idx ) { 5975if (sess != hex_event -> sess ) { 5976sess -> add_peer (hex_event -> sess ); 5977 } 5978return ; 5979 } 5980 5981sess -> clone_buffer (hex_event -> fence_sess -> fence_buf ); 5982sess -> add_peer (hex_event -> sess ); 5983sess -> enqueue_fence (& hex_event -> fence_tensor ,hex_event -> seq ,/* wait = */ true); 5984} 5985 5986static void ggml_backend_hexagon_set_tensor_async (ggml_backend_t backend ,struct ggml_tensor * tensor ,const void * data ,size_t offset ,size_t size ) { 5987auto sess = static_cast < ggml_hexagon_session *> (backend -> context ); 5988HEX_VERBOSE ("ggml-hex: %s set-tensor-async %s : data %p offset %zu size %zu usage %d\n" , 5989sess -> c_name (),tensor -> name ,data ,offset ,size ,tensor -> buffer ? (int )tensor -> buffer -> usage :-1 ); 5990ggml_backend_tensor_set (tensor ,data ,offset ,size ); 5991} 5992 5993static void ggml_backend_hexagon_get_tensor_async (ggml_backend_t backend ,const struct ggml_tensor * tensor ,void * data ,size_t offset ,size_t size ) { 5994auto sess = static_cast < ggml_hexagon_session *> (backend -> context ); 5995HEX_VERBOSE ("ggml-hex: %s get-tensor-async %s : data %p offset %zu size %zu usage %d\n" , 5996sess -> c_name (),tensor -> name ,data ,offset ,size ,tensor -> buffer ? (int )tensor -> buffer -> usage :-1 ); 5997sess -> flush_sync (); 5998if (sess -> last_error > HTP_STATUS_OK ) { 5999GGML_ABORT ("ggml-hex: %s get-tensor-async failed : dsp-error %s\n" ,sess -> c_name (),status_to_str (sess -> last_error )); 6000 } 6001ggml_backend_tensor_get (tensor ,data ,offset ,size ); 6002} 6003 6004static void ggml_backend_hexagon_set_tensor_2d_async (ggml_backend_t backend , 6005struct ggml_tensor * tensor , 6006const void * data , 6007size_t offset , 6008size_t size , 6009size_t n_copies , 6010size_t stride_tensor , 6011size_t stride_data ) { 6012auto sess = static_cast < ggml_hexagon_session *> (backend -> context ); 6013HEX_VERBOSE ("ggml-hex: %s set-tensor-2d-async %s : data %p offset %zu size %zu n_copies %zu stride_tensor %zu stride_data %zu usage %d\n" , 6014sess -> c_name (),tensor -> name ,data ,offset ,size ,n_copies ,stride_tensor ,stride_data ,tensor -> buffer ? (int )tensor -> buffer -> usage :-1 ); 6015ggml_backend_tensor_set_2d (tensor ,data ,offset ,size ,n_copies ,stride_tensor ,stride_data ); 6016} 6017 6018static void ggml_backend_hexagon_get_tensor_2d_async (ggml_backend_t backend , 6019const struct ggml_tensor * tensor , 6020void * data , 6021size_t offset , 6022size_t size , 6023size_t n_copies , 6024size_t stride_tensor , 6025size_t stride_data ) { 6026auto sess = static_cast < ggml_hexagon_session *> (backend -> context ); 6027HEX_VERBOSE ("ggml-hex: %s get-tensor-2d-async %s : data %p offset %zu size %zu n_copies %zu stride_tensor %zu stride_data %zu usage %d\n" , 6028sess -> c_name (),tensor -> name ,data ,offset ,size ,n_copies ,stride_tensor ,stride_data ,tensor -> buffer ? (int )tensor -> buffer -> usage :-1 ); 6029sess -> flush_sync (); 6030if (sess -> last_error > HTP_STATUS_OK ) { 6031GGML_ABORT ("ggml-hex: %s get-tensor-2d-async failed : dsp-error %s\n" ,sess -> c_name (),status_to_str (sess -> last_error )); 6032 } 6033ggml_backend_tensor_get_2d (tensor ,data ,offset ,size ,n_copies ,stride_tensor ,stride_data ); 6034} 6035 6036static struct ggml_backend_i hexagon_backend_i = { 6037/* .get_name = */ ggml_backend_hexagon_name , 6038/* .free = */ ggml_backend_hexagon_free , 6039/* .set_tensor_async = */ ggml_backend_hexagon_set_tensor_async , 6040/* .get_tensor_async = */ ggml_backend_hexagon_get_tensor_async , 6041/* .set_tensor_2d_async = */ ggml_backend_hexagon_set_tensor_2d_async , 6042/* .get_tensor_2d_async = */ ggml_backend_hexagon_get_tensor_2d_async , 6043/* .cpy_tensor_async = */ ggml_backend_hexagon_cpy_tensor_async , 6044/* .synchronize = */ ggml_backend_hexagon_synchronize , 6045/* .graph_plan_create = */ NULL , 6046/* .graph_plan_free = */ NULL , 6047/* .graph_plan_update = */ NULL , 6048/* .graph_plan_compute = */ NULL , 6049/* .graph_compute = */ ggml_backend_hexagon_graph_compute , 6050/* .event_record = */ ggml_backend_hexagon_event_record , 6051/* .event_wait = */ ggml_backend_hexagon_event_wait , 6052/* .graph_optimize = */ ggml_backend_hexagon_graph_optimize , 6053}; 6054 6055static ggml_guid_t ggml_backend_hexagon_guid () { 6056static ggml_guid guid = {0x7b ,0x57 ,0xdc ,0xaf ,0xde ,0x12 ,0x1d ,0x49 , 60570x11 ,0x11 ,0x11 ,0x11 ,0x11 ,0x11 ,0x11 ,0x11 }; 6058return & guid ; 6059} 6060 6061bool ggml_backend_is_hexagon (ggml_backend_t backend ) { 6062return backend && backend -> iface .get_name == ggml_backend_hexagon_name ; 6063} 6064 6065// device interface 6066 6067static ggml_backend_t ggml_backend_hexagon_device_init (ggml_backend_dev_t dev ,const char * params ) { 6068auto dev_ctx = static_cast < ggml_backend_hexagon_device_context *> (dev -> context ); 6069auto sess = dev_ctx -> session (); 6070 6071return new ggml_backend { 6072/* .guid = */ ggml_backend_hexagon_guid (), 6073/* .interface = */ hexagon_backend_i , 6074/* .device = */ dev , 6075/* .context = */ sess , 6076 }; 6077 6078GGML_UNUSED (params ); 6079} 6080 6081static const char * ggml_backend_hexagon_device_get_name (ggml_backend_dev_t dev ) { 6082auto dev_ctx = static_cast < ggml_backend_hexagon_device_context *> (dev -> context ); 6083return dev_ctx -> c_name (); 6084 6085GGML_UNUSED (dev ); 6086} 6087 6088static const char * ggml_backend_hexagon_device_get_description (ggml_backend_dev_t dev ) { 6089return "Hexagon" ; 6090GGML_UNUSED (dev ); 6091} 6092 6093static void ggml_backend_hexagon_device_get_memory (ggml_backend_dev_t dev ,size_t * free ,size_t * total ) { 6094* free = 0 ; 6095* total = * free ; 6096 6097GGML_UNUSED (dev ); 6098} 6099 6100static enum ggml_backend_dev_type ggml_backend_hexagon_device_get_type (ggml_backend_dev_t dev ) { 6101return GGML_BACKEND_DEVICE_TYPE_GPU ; 6102 6103GGML_UNUSED (dev ); 6104} 6105 6106static void ggml_backend_hexagon_device_get_props (ggml_backend_dev_t dev ,struct ggml_backend_dev_props * props ) { 6107props -> name = ggml_backend_hexagon_device_get_name (dev ); 6108props -> description = ggml_backend_hexagon_device_get_description (dev ); 6109props -> type = ggml_backend_hexagon_device_get_type (dev ); 6110ggml_backend_hexagon_device_get_memory (dev ,& props -> memory_free ,& props -> memory_total ); 6111props -> caps = { 6112/* .async = */ true, 6113/* .host_buffer = */ false, 6114/* .buffer_from_host_ptr = */ false, 6115/* .events = */ true, 6116/* .mmap_support = */ false, 6117 }; 6118} 6119 6120static ggml_backend_buffer_type_t ggml_backend_hexagon_device_get_buffer_type (ggml_backend_dev_t dev ) { 6121auto dev_ctx = static_cast < ggml_backend_hexagon_device_context *> (dev -> context ); 6122return & dev_ctx -> buffer_type ; 6123} 6124 6125static ggml_backend_buffer_type_t ggml_backend_hexagon_device_get_host_buffer_type (ggml_backend_dev_t dev ) { 6126if (!opt_hostbuf ) { 6127return NULL ; 6128 } 6129auto dev_ctx = static_cast < ggml_backend_hexagon_device_context *> (dev -> context ); 6130return & dev_ctx -> host_buffer_type ; 6131} 6132 6133static bool ggml_hexagon_supported_cpy (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 6134GGML_UNUSED (sess ); 6135 6136const struct ggml_tensor * src0 = op -> src [0 ]; 6137const struct ggml_tensor * dst = op ; 6138 6139// for now we can do f32 -> f16 and f16 -> f32 (without reshaping) 6140if (src0 -> type != GGML_TYPE_F32 && src0 -> type != GGML_TYPE_F16 )return false; 6141if (dst -> type != GGML_TYPE_F32 && dst -> type != GGML_TYPE_F16 )return false; 6142 6143const bool sametype = (src0 -> type == dst -> type ); 6144const bool transposed = ggml_is_transposed (src0 )|| ggml_is_transposed (dst ); 6145const bool sameshape = !transposed && ggml_are_same_shape (src0 ,dst ); 6146 6147// can handle any shape and any same-type (pretty slow if reshaping is required) 6148if (sametype )return true; 6149 6150// cannot handle re-shaping and type conversion at the same time 6151if (!sameshape )return false; 6152 6153return true; 6154} 6155 6156static bool ggml_hexagon_supported_cont (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 6157GGML_UNUSED (sess ); 6158const struct ggml_tensor * src0 = op -> src [0 ]; 6159 6160// CONT is same-type only, supports f32 and f16 6161if (src0 -> type != GGML_TYPE_F32 && src0 -> type != GGML_TYPE_F16 )return false; 6162 6163return true; 6164} 6165 6166static bool ggml_hexagon_supported_repeat (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 6167GGML_UNUSED (sess ); 6168const struct ggml_tensor * src0 = op -> src [0 ]; 6169const struct ggml_tensor * dst = op ; 6170 6171// Support f32 and f16 6172if (src0 -> type != GGML_TYPE_F32 && src0 -> type != GGML_TYPE_F16 )return false; 6173 6174// src and dst must be the same type 6175if (src0 -> type != dst -> type )return false; 6176 6177// dst dims must be multiples of src dims 6178if (dst -> ne [0 ] %src0 -> ne [0 ]!= 0 )return false; 6179if (dst -> ne [1 ] %src0 -> ne [1 ]!= 0 )return false; 6180if (dst -> ne [2 ] %src0 -> ne [2 ]!= 0 )return false; 6181if (dst -> ne [3 ] %src0 -> ne [3 ]!= 0 )return false; 6182 6183// require contiguous tensors (no transposition) 6184if (ggml_is_transposed (src0 )|| ggml_is_transposed (dst ))return false; 6185 6186return true; 6187} 6188 6189static bool ggml_hexagon_supported_concat (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 6190int dim = ((const int32_t * )op -> op_params )[0 ]; 6191if (dim < 0 || dim >=GGML_MAX_DIMS ) { 6192return false; 6193 } 6194 6195for (int i = 0 ;i < GGML_MAX_SRC ;++ i ) { 6196const struct ggml_tensor * src = op -> src [i ]; 6197if (!src ) { 6198continue ; 6199 } 6200if (src -> type != GGML_TYPE_F32 && src -> type != GGML_TYPE_I32 && src -> type != GGML_TYPE_F16 ) { 6201return false; 6202 } 6203 } 6204 6205return true; 6206GGML_UNUSED (sess ); 6207} 6208 6209static bool ggml_hexagon_supported_fill (const struct ggml_hexagon_session * sess ,const struct ggml_tensor * op ) { 6210const struct ggml_tensor * dst = op ; 6211 6212if (dst -> type != GGML_TYPE_F32 && dst -> type != GGML_TYPE_F16 ) { 6213return false; 6214 } 6215 6216return true; 6217GGML_UNUSED (sess ); 6218} 6219 6220static bool ggml_backend_hexagon_device_supports_op (ggml_backend_dev_t dev ,const struct ggml_tensor * op ) { 6221auto dev_ctx = static_cast < ggml_backend_hexagon_device_context *> (dev -> context ); 6222auto sess = dev_ctx -> session (); 6223 6224// reject ops that match the filter 6225if (opt_opfilter && std::regex_match (ggml_op_desc (op ),* opt_opfilter )) { 6226return false; 6227 } 6228 6229bool supp = false; 6230switch (op -> op ) { 6231case GGML_OP_NONE : 6232case GGML_OP_RESHAPE : 6233case GGML_OP_VIEW : 6234case GGML_OP_PERMUTE : 6235case GGML_OP_TRANSPOSE : 6236supp = true; 6237break ; 6238 6239case GGML_OP_MUL : 6240case GGML_OP_ADD : 6241case GGML_OP_SUB : 6242case GGML_OP_DIV : 6243supp = ggml_hexagon_supported_binary (sess ,op ); 6244break ; 6245 6246case GGML_OP_MUL_MAT : 6247supp = ggml_hexagon_supported_mul_mat (sess ,op ); 6248break ; 6249 6250case GGML_OP_MUL_MAT_ID : 6251supp = ggml_hexagon_supported_mul_mat_id (sess ,op ); 6252break ; 6253 6254case GGML_OP_ADD_ID : 6255supp = ggml_hexagon_supported_add_id (sess ,op ); 6256break ; 6257 6258case GGML_OP_NORM : 6259case GGML_OP_L2_NORM : 6260case GGML_OP_RMS_NORM : 6261case GGML_OP_SCALE : 6262case GGML_OP_CLAMP : 6263case GGML_OP_LEAKY_RELU : 6264supp = ggml_hexagon_supported_unary (sess ,op ); 6265break ; 6266 6267case GGML_OP_SQR : 6268case GGML_OP_SQRT : 6269case GGML_OP_LOG : 6270supp = ggml_hexagon_supported_unary (sess ,op ); 6271break ; 6272 6273case GGML_OP_SUM_ROWS : 6274supp = ggml_hexagon_supported_sum_rows (sess ,op ); 6275break ; 6276 6277case GGML_OP_SOFT_MAX : 6278supp = ggml_hexagon_supported_softmax (sess ,op ); 6279break ; 6280 6281case GGML_OP_UNARY : 6282switch (ggml_get_unary_op (op )) { 6283case GGML_UNARY_OP_NEG : 6284case GGML_UNARY_OP_EXP : 6285case GGML_UNARY_OP_SIGMOID : 6286case GGML_UNARY_OP_SOFTPLUS : 6287case GGML_UNARY_OP_TANH : 6288case GGML_UNARY_OP_ABS : 6289case GGML_UNARY_OP_SILU : 6290case GGML_UNARY_OP_GELU : 6291case GGML_UNARY_OP_GELU_QUICK : 6292case GGML_UNARY_OP_RELU : 6293supp = ggml_hexagon_supported_unary (sess ,op ); 6294break ; 6295default : 6296supp = false; 6297break ; 6298 } 6299break ; 6300 6301case GGML_OP_GLU : 6302switch (ggml_get_glu_op (op )) { 6303case GGML_GLU_OP_SWIGLU : 6304case GGML_GLU_OP_SWIGLU_OAI : 6305case GGML_GLU_OP_SWIGLU_CLAMP : 6306case GGML_GLU_OP_GEGLU : 6307supp = ggml_hexagon_supported_activations (sess ,op ); 6308break ; 6309default : 6310supp = false; 6311break ; 6312 } 6313break ; 6314 6315case GGML_OP_ROPE : 6316supp = ggml_hexagon_supported_rope (sess ,op ); 6317break ; 6318 6319case GGML_OP_FLASH_ATTN_EXT : 6320supp = ggml_hexagon_supported_flash_attn_ext (sess ,op ); 6321break ; 6322 6323case GGML_OP_SET_ROWS : 6324supp = ggml_hexagon_supported_set_rows (sess ,op ); 6325break ; 6326 6327case GGML_OP_GET_ROWS : 6328supp = ggml_hexagon_supported_get_rows (sess ,op ); 6329break ; 6330 6331case GGML_OP_CPY : 6332supp = ggml_hexagon_supported_cpy (sess ,op ); 6333break ; 6334 6335case GGML_OP_CONT : 6336supp = ggml_hexagon_supported_cont (sess ,op ); 6337break ; 6338 6339case GGML_OP_REPEAT : 6340supp = ggml_hexagon_supported_repeat (sess ,op ); 6341break ; 6342 6343case GGML_OP_ARGSORT : 6344supp = ggml_hexagon_supported_argsort (sess ,op ); 6345break ; 6346 6347case GGML_OP_SSM_CONV : 6348supp = ggml_hexagon_supported_ssm_conv (sess ,op ); 6349break ; 6350 6351case GGML_OP_IM2COL : 6352supp = ggml_hexagon_supported_im2col (sess ,op ); 6353break ; 6354 6355case GGML_OP_GATED_DELTA_NET : 6356supp = ggml_hexagon_supported_gated_delta_net (sess ,op ); 6357break ; 6358 6359case GGML_OP_CUMSUM : 6360supp = ggml_hexagon_supported_cumsum (sess ,op ); 6361break ; 6362 6363case GGML_OP_CONCAT : 6364supp = ggml_hexagon_supported_concat (sess ,op ); 6365break ; 6366 6367case GGML_OP_FILL : 6368supp = ggml_hexagon_supported_fill (sess ,op ); 6369break ; 6370 6371case GGML_OP_DIAG : 6372supp = ggml_hexagon_supported_diag (sess ,op ); 6373break ; 6374 6375case GGML_OP_SOLVE_TRI : 6376supp = ggml_hexagon_supported_solve_tri (sess ,op ); 6377break ; 6378 6379case GGML_OP_TRI : 6380supp = ggml_hexagon_supported_tri (sess ,op ); 6381break ; 6382 6383case GGML_OP_PAD : 6384supp = ggml_hexagon_supported_pad (sess ,op ); 6385break ; 6386 6387default : 6388break ; 6389 } 6390 6391ggml_hexagon_dump_op_supp (sess -> name ,op ,supp ); 6392return supp ; 6393} 6394 6395static bool ggml_backend_hexagon_device_supports_buft (ggml_backend_dev_t dev ,ggml_backend_buffer_type_t buft ) { 6396auto dev_ctx = static_cast < ggml_backend_hexagon_device_context *> (dev -> context ); 6397 6398bool supp = (buft == & dev_ctx -> host_buffer_type )|| (buft == & dev_ctx -> buffer_type ); 6399 6400HEX_VERBOSE ("ggml-hex: %s device-supports-buft %s %s\n" ,dev_ctx -> c_name (),ggml_backend_buft_name (buft ),supp ?"yes" :"no" ); 6401return supp ; 6402} 6403 6404static const struct ggml_backend_device_i ggml_backend_hexagon_device_i = { 6405/* .get_name = */ ggml_backend_hexagon_device_get_name , 6406/* .get_description = */ ggml_backend_hexagon_device_get_description , 6407/* .get_memory = */ ggml_backend_hexagon_device_get_memory , 6408/* .get_type = */ ggml_backend_hexagon_device_get_type , 6409/* .get_props = */ ggml_backend_hexagon_device_get_props , 6410/* .init_backend = */ ggml_backend_hexagon_device_init , 6411/* .get_buffer_type = */ ggml_backend_hexagon_device_get_buffer_type , 6412/* .get_host_buffer_type = */ ggml_backend_hexagon_device_get_host_buffer_type , 6413/* .buffer_from_host_ptr = */ NULL ,// ggml_backend_hexagon_device_buffer_from_ptr, 6414/* .supports_op = */ ggml_backend_hexagon_device_supports_op , 6415/* .supports_buft = */ ggml_backend_hexagon_device_supports_buft , 6416/* .offload_op = */ NULL ,// ggml_backend_hexagon_device_offload_op, 6417/* .event_new = */ ggml_backend_hexagon_device_event_new , 6418/* .event_free = */ ggml_backend_hexagon_device_event_free , 6419/* .event_synchronize = */ ggml_backend_hexagon_device_event_synchronize , 6420}; 6421 6422//** backend registry 6423 6424ggml_hexagon_registry::ggml_hexagon_registry (ggml_backend_reg_t reg ) { 6425GGML_LOG_INFO ("ggml-hex: Hexagon backend (experimental) : allocating new registry : ndev %zu\n" ,opt_ndev ); 6426 6427GGML_LOG_INFO ("ggml-hex: Hexagon Arch version v%d\n" ,opt_arch ); 6428 6429// Create devices 6430for (size_t i = 0 ;i < opt_ndev ;i ++ ) { 6431const auto & cfg = opt_device_configs [i ]; 6432if (cfg .mdev_group .empty ()) { 6433GGML_LOG_INFO ("ggml-hex: device %zu: %s (phys=%d, virt=%d, domain=%s:%d)\n" , 6434i ,cfg .name .c_str (),cfg .physical_idx ,cfg .virtual_idx ,cfg .domain_name .c_str (),cfg .domain_id ); 6435 }else { 6436 std::string peers_str ; 6437for (const auto & p :cfg .mdev_group ) { 6438if (!peers_str .empty ())peers_str += ", " ; 6439peers_str += p .name + " (phys=" + std::to_string (p .physical_idx )+ ")" ; 6440 } 6441GGML_LOG_INFO ("ggml-hex: device %zu: %s (phys=%d, virt=%d, domain=%s:%d) [mdev peers: %s]\n" , 6442i ,cfg .name .c_str (),cfg .physical_idx ,cfg .virtual_idx ,cfg .domain_name .c_str (),cfg .domain_id ,peers_str .c_str ()); 6443 } 6444devices [i ].iface = ggml_backend_hexagon_device_i ; 6445devices [i ].reg = reg ; 6446devices [i ].context = new ggml_backend_hexagon_device_context (i ,opt_device_configs [i ],& devices [i ]); 6447 } 6448 6449} 6450 6451ggml_hexagon_registry::~ggml_hexagon_registry () { 6452GGML_LOG_INFO ("ggml-hex: releasing registry\n" ); 6453 6454// Release devices 6455for (size_t i = 0 ;i < opt_ndev ;i ++ ) { 6456auto dev_ctx = static_cast < ggml_backend_hexagon_device_context *> (devices [i ].context ); 6457delete dev_ctx ; 6458 } 6459} 6460 6461static const char * ggml_backend_hexagon_reg_get_name (ggml_backend_reg_t reg ) { 6462return "HTP" ; 6463GGML_UNUSED (reg ); 6464} 6465 6466static size_t ggml_backend_hexagon_reg_get_device_count (ggml_backend_reg_t reg ) { 6467return opt_ndev ; 6468GGML_UNUSED (reg ); 6469} 6470 6471static ggml_backend_dev_t ggml_backend_hexagon_reg_get_device (ggml_backend_reg_t reg ,size_t index ) { 6472auto hreg = static_cast < ggml_hexagon_registry *> (reg -> context ); 6473 6474if (index >=opt_ndev || !hreg -> devices [index ].context ) { 6475return nullptr ; 6476 } 6477 6478return & hreg -> devices [index ]; 6479} 6480 6481// ** communication context for tensor-split allreduce 6482 6483static void * ggml_backend_hexagon_comm_init (ggml_backend_t * backends ,size_t n_backends ) { 6484if (n_backends < 2 || n_backends > 4 ) { 6485return nullptr ; 6486 } 6487 6488for (size_t i = 0 ;i < n_backends ;++ i ) { 6489if (!ggml_backend_is_hexagon (backends [i ])) { 6490return nullptr ; 6491 } 6492 } 6493 6494for (size_t i = 0 ;i < n_backends ;i ++ ) { 6495auto sess_i = static_cast < ggml_hexagon_session *> (backends [i ]-> context ); 6496for (size_t j = i + 1 ;j < n_backends ;j ++ ) { 6497auto sess_j = static_cast < ggml_hexagon_session *> (backends [j ]-> context ); 6498if (sess_i -> phys_idx == sess_j -> phys_idx ) { 6499return nullptr ; 6500 } 6501 } 6502 } 6503 6504auto * ctx = new ggml_backend_hexagon_comm_context (); 6505ctx -> backends .assign (backends ,backends + n_backends ); 6506ctx -> n_backends = n_backends ; 6507 6508static ggml_hexagon_tensor_extra fence_extra { {},0 ,GGML_HEXAGON_TENSOR_FENCE }; 6509for (size_t i = 0 ;i < n_backends ;i ++ ) { 6510auto sess_i = static_cast < ggml_hexagon_session *> (backends [i ]-> context ); 6511ctx -> fence_slots [i ]= (volatile uint32_t * )sess_i -> alloc_fence (1 ); 6512ctx -> fence_tensors [i ]= {}; 6513ctx -> fence_tensors [i ].buffer = & sess_i -> fence_buf -> backend_buffer ; 6514ctx -> fence_tensors [i ].extra = & fence_extra ; 6515ctx -> fence_tensors [i ].data = (void * )ctx -> fence_slots [i ]; 6516ctx -> fence_tensors [i ].type = GGML_TYPE_I32 ; 6517ctx -> fence_tensors [i ].ne [0 ]= 4 ; 6518ctx -> fence_tensors [i ].ne [1 ]= 1 ; 6519ctx -> fence_tensors [i ].ne [2 ]= 1 ; 6520ctx -> fence_tensors [i ].ne [3 ]= 1 ; 6521ctx -> fence_tensors [i ].nb [0 ]= sizeof (int32_t ); 6522ctx -> fence_tensors [i ].nb [1 ]= sizeof (int32_t ); 6523ctx -> fence_tensors [i ].nb [2 ]= sizeof (int32_t ); 6524ctx -> fence_tensors [i ].nb [3 ]= sizeof (int32_t ); 6525ctx -> fence_tensors [i ].op = GGML_OP_NONE ; 6526 } 6527 6528return ctx ; 6529} 6530 6531static void ggml_backend_hexagon_comm_free (void * comm_ctx_v ) { 6532if (!comm_ctx_v )return ; 6533auto * ctx = static_cast < ggml_backend_hexagon_comm_context *> (comm_ctx_v ); 6534for (size_t i = 0 ;i < ctx -> n_backends ;i ++ ) { 6535auto sess_i = static_cast < ggml_hexagon_session *> (ctx -> backends [i ]-> context ); 6536sess_i -> free_fence ((void * )ctx -> fence_slots [i ],1 ); 6537 } 6538delete ctx ; 6539} 6540 6541static bool ggml_backend_hexagon_comm_allreduce_tensor (void * comm_ctx_v ,struct ggml_tensor ** tensors ) { 6542if (opt_ar_select == 0 || !comm_ctx_v )return false; 6543auto * comm_ctx = static_cast < ggml_backend_hexagon_comm_context *> (comm_ctx_v ); 6544const size_t n_backends = comm_ctx -> n_backends ; 6545 6546if (n_backends < 2 || n_backends > 4 )return false; 6547 6548for (size_t i = 0 ;i < n_backends ;i ++ ) { 6549auto sess_i = static_cast < ggml_hexagon_session *> (comm_ctx -> backends [i ]-> context ); 6550for (size_t j = i + 1 ;j < n_backends ;j ++ ) { 6551auto sess_j = static_cast < ggml_hexagon_session *> (comm_ctx -> backends [j ]-> context ); 6552if (sess_i -> phys_idx == sess_j -> phys_idx ) { 6553return false; 6554 } 6555 } 6556 } 6557 6558for (size_t i = 0 ;i < n_backends ;i ++ ) { 6559if (!tensors [i ]|| !tensors [i ]-> buffer || !ggml_backend_buffer_is_hexagon (tensors [i ]-> buffer )) { 6560return false; 6561 } 6562if (tensors [i ]-> type != tensors [0 ]-> type ) { 6563return false; 6564 } 6565if (!ggml_is_contiguous (tensors [i ])) { 6566return false; 6567 } 6568if (ggml_nelements (tensors [i ])!= ggml_nelements (tensors [0 ])) { 6569return false; 6570 } 6571 } 6572 6573if (tensors [0 ]-> type != GGML_TYPE_F16 && tensors [0 ]-> type != GGML_TYPE_F32 ) { 6574return false; 6575 } 6576 6577for (size_t r = 0 ;r < n_backends ;r ++ ) { 6578auto sess = static_cast < ggml_hexagon_session *> (comm_ctx -> backends [r ]-> context ); 6579struct htp_allreduce_kernel_params kparams ; 6580if (!ggml_hexagon_precompute_allreduce_params (sess ,tensors [r ], (uint32_t )r , (uint32_t )n_backends , false, false,& kparams )) { 6581return false; 6582 } 6583 } 6584 6585uint32_t max_seq = static_cast < ggml_hexagon_session *> (comm_ctx -> backends [0 ]-> context )-> fence_seq ; 6586for (size_t i = 1 ;i < n_backends ;i ++ ) { 6587auto sess_i = static_cast < ggml_hexagon_session *> (comm_ctx -> backends [i ]-> context ); 6588if ((int32_t )(sess_i -> fence_seq - max_seq )> 0 ) { 6589max_seq = sess_i -> fence_seq ; 6590 } 6591 } 6592if (++ max_seq == 0 )max_seq = 1 ; 6593uint32_t fence_seq_entry = max_seq ; 6594if (++ max_seq == 0 )max_seq = 1 ; 6595uint32_t fence_seq_exit = max_seq ; 6596 6597for (size_t i = 0 ;i < n_backends ;i ++ ) { 6598auto sess_i = static_cast < ggml_hexagon_session *> (comm_ctx -> backends [i ]-> context ); 6599sess_i -> fence_seq = max_seq ; 6600 } 6601 6602 std::vector < const ggml_tensor *> data_tensors (n_backends ); 6603 std::vector < const ggml_tensor *> sync_tensors (n_backends ); 6604for (size_t i = 0 ;i < n_backends ;i ++ ) { 6605data_tensors [i ]= tensors [i ]; 6606sync_tensors [i ]= & comm_ctx -> fence_tensors [i ]; 6607 } 6608 6609for (size_t r = 0 ;r < n_backends ;r ++ ) { 6610auto sess = static_cast < ggml_hexagon_session *> (comm_ctx -> backends [r ]-> context ); 6611sess -> enqueue_allreduce (tensors [r ],data_tensors ,sync_tensors , (uint32_t )r , (uint32_t )n_backends ,fence_seq_entry ,fence_seq_exit ); 6612for (size_t j = 0 ;j < n_backends ;j ++ ) { 6613if (r != j ) { 6614sess -> add_peer (static_cast < ggml_hexagon_session *> (comm_ctx -> backends [j ]-> context )); 6615 } 6616 } 6617 } 6618 6619return true; 6620} 6621 6622static ggml_backend_buffer_type_t ggml_backend_hexagon_split_buffer_type (int main_device ,const float * tensor_split ) { 6623GGML_UNUSED (tensor_split ); 6624auto reg = ggml_backend_hexagon_reg (); 6625auto dev = ggml_backend_reg_dev_get (reg ,main_device ); 6626if (!dev ) { 6627dev = ggml_backend_reg_dev_get (reg ,0 ); 6628 } 6629if (!dev )return nullptr ; 6630auto dev_ctx = static_cast < ggml_backend_hexagon_device_context *> (dev -> context ); 6631return & dev_ctx -> buffer_type ; 6632} 6633 6634static void * ggml_backend_hexagon_get_proc_address (ggml_backend_reg_t reg ,const char * name ) { 6635GGML_UNUSED (reg ); 6636if (strcmp (name ,"ggml_backend_split_buffer_type" )== 0 ) { 6637return (void * )ggml_backend_hexagon_split_buffer_type ; 6638 } 6639if (strcmp (name ,"ggml_backend_comm_init" )== 0 ) { 6640return (void * )ggml_backend_hexagon_comm_init ; 6641 } 6642if (strcmp (name ,"ggml_backend_comm_free" )== 0 ) { 6643return (void * )ggml_backend_hexagon_comm_free ; 6644 } 6645if (strcmp (name ,"ggml_backend_comm_allreduce_tensor" )== 0 ) { 6646return (void * )ggml_backend_hexagon_comm_allreduce_tensor ; 6647 } 6648return NULL ; 6649} 6650 6651template < typename T > std::vector < T > str_to_vec (const char * str ) { 6652 std::stringstream ss (str ); 6653 std::vector < T > v ; 6654 std::string t ; 6655 6656while (std::getline (ss ,t ,',' )) { 6657v .push_back (std::stoul (t ,nullptr ,0 )); 6658 } 6659 6660return v ; 6661} 6662 6663template < typename T ,int BASE = 10 > std::string vec_to_str (std::vector < T > v ) { 6664 std::stringstream ss ; 6665ss << std::setbase (BASE ) << std::showbase ; 6666for (auto i :v ) {ss <<i <<',' ; } 6667auto str = ss .str ();str .pop_back ();// drop last comma 6668return str ; 6669} 6670 6671static void ggml_hexagon_resolve_device_domain (ggml_hexagon_device_config & cfg ,bool discovery_supported ,const std::unordered_map < int ,fastrpc_domain > & cdsp_map ) { 6672if (discovery_supported ) { 6673auto it = cdsp_map .find (cfg .physical_idx ); 6674if (it != cdsp_map .end ()) { 6675cfg .domain_id = it -> second .id ; 6676cfg .domain_name = it -> second .name ; 6677 }else { 6678GGML_LOG_ERROR ("ggml-hex: physical CDSP core %d not found on device (%zu CDSP core(s) available)\n" , 6679cfg .physical_idx ,cdsp_map .size ()); 6680cfg .domain_id = -1 ; 6681cfg .domain_name = "" ; 6682 } 6683 }else { 6684switch (cfg .physical_idx ) { 6685case 0 : 6686cfg .domain_id = 3 ; 6687cfg .domain_name = CDSP_DOMAIN_NAME ; 6688break ; 6689case 1 : 6690cfg .domain_id = 4 ; 6691cfg .domain_name = "cdsp1" ; 6692break ; 6693default : 6694GGML_LOG_ERROR ("ggml-hex: physical CDSP core %d not supported without dynamic discovery\n" , 6695cfg .physical_idx ); 6696cfg .domain_id = -1 ; 6697cfg .domain_name = "" ; 6698break ; 6699 } 6700 } 6701for (auto & sub_cfg :cfg .mdev_group ) { 6702ggml_hexagon_resolve_device_domain (sub_cfg ,discovery_supported ,cdsp_map ); 6703 } 6704} 6705 6706// Enumerate NPU (aka CDSP) domains via FASTRPC_GET_DOMAINS if supported, 6707// and populate domain_id and domain_name for all configured devices. 6708static void ggml_hexagon_discover_devices () { 6709 std::unordered_map < int ,fastrpc_domain > cdsp_map ; 6710bool discovery_supported = false; 6711 6712system_req_payload domain_info = {}; 6713domain_info .id = FASTRPC_GET_DOMAINS ; 6714domain_info .sys .domains = nullptr ; 6715domain_info .sys .max_domains = 0 ; 6716domain_info .sys .flags = DOMAINS_LIST_FLAGS_SET_TYPE (0 ,FASTRPC_NSP ); 6717 6718int err = remote_system_request (& domain_info ); 6719if (err == AEE_SUCCESS && domain_info .sys .num_domains > 0 ) { 6720 std::vector < fastrpc_domain > domains (domain_info .sys .num_domains ); 6721domain_info .sys .domains = domains .data (); 6722domain_info .sys .max_domains = (int )domains .size (); 6723 6724err = remote_system_request (& domain_info ); 6725if (err == AEE_SUCCESS ) { 6726discovery_supported = true; 6727const int n_domains = std::min (domain_info .sys .num_domains , (int )domains .size ()); 6728for (int i = 0 ;i < n_domains ;i ++ ) { 6729GGML_LOG_INFO ("ggml-hex: FASTRPC_GET_DOMAINS[%d]: type %d id %d name '%s' status %d instance-id %d\n" , 6730i , (int )domains [i ].type ,domains [i ].id ,domains [i ].name ,domains [i ].status ,domains [i ].instance_id ); 6731if (domains [i ].type != FASTRPC_NSP ) { 6732GGML_LOG_DEBUG ("ggml-hex: skipping non-CDSP domain (type=%d)\n" , (int )domains [i ].type ); 6733continue ; 6734 } 6735if (!domains [i ].status ) { 6736GGML_LOG_WARN ("ggml-hex: skipping CDSP domain id=%d (status=down)\n" ,domains [i ].id ); 6737continue ; 6738 } 6739cdsp_map [domains [i ].instance_id ]= domains [i ]; 6740GGML_LOG_INFO ("ggml-hex: using CDSP domain: instance-id %d id %d name '%s'\n" , 6741domains [i ].instance_id ,domains [i ].id ,domains [i ].name ); 6742 } 6743 }else { 6744GGML_LOG_WARN ("ggml-hex: FASTRPC_GET_DOMAINS fetch failed (0x%x), using static CDSP domains\n" , (unsigned )err ); 6745 } 6746 }else if (err != AEE_SUCCESS ) { 6747GGML_LOG_DEBUG ("ggml-hex: FASTRPC_GET_DOMAINS query failed (0x%x), using static CDSP domains\n" , (unsigned )err ); 6748 } 6749 6750// Populate domain IDs and names for all configured devices 6751for (size_t i = 0 ;i < opt_ndev ;i ++ ) { 6752ggml_hexagon_resolve_device_domain (opt_device_configs [i ],discovery_supported ,cdsp_map ); 6753 } 6754} 6755 6756static void ggml_hexagon_init (ggml_backend_reg * reg ) { 6757// Basic sanity checks to make sure definitions match 6758 static_assert((unsigned int )HTP_TYPE_Q4_0 == (unsigned int )GGML_TYPE_Q4_0 , 6759"please update hexagon_type to match ggml_type" ); 6760 static_assert((unsigned int )HTP_TYPE_Q4_1 == (unsigned int )GGML_TYPE_Q4_1 , 6761"please update hexagon_type to match ggml_type" ); 6762 static_assert((unsigned int )HTP_TYPE_Q8_0 == (unsigned int )GGML_TYPE_Q8_0 , 6763"please update hexagon_type to match ggml_type" ); 6764 static_assert((unsigned int )HTP_TYPE_MXFP4 == (unsigned int )GGML_TYPE_MXFP4 , 6765"please update hexagon_type to match ggml_type" ); 6766 static_assert((unsigned int )HTP_TYPE_IQ4_NL == (unsigned int )GGML_TYPE_IQ4_NL , 6767"please update hexagon_type to match ggml_type" ); 6768 6769const char * str_verbose = getenv ("GGML_HEXAGON_VERBOSE" ); 6770const char * str_opbatch = getenv ("GGML_HEXAGON_OPBATCH" ); 6771const char * str_opqueue = getenv ("GGML_HEXAGON_OPQUEUE" ); 6772const char * str_oppoll = getenv ("GGML_HEXAGON_OPPOLL" ); 6773const char * str_opfusion = getenv ("GGML_HEXAGON_OPFUSION" ); 6774const char * str_opfilter = getenv ("GGML_HEXAGON_OPFILTER" ); 6775const char * str_profile = getenv ("GGML_HEXAGON_PROFILE" ); 6776const char * str_etm = getenv ("GGML_HEXAGON_ETM" ); 6777const char * str_nhvx = getenv ("GGML_HEXAGON_NHVX" ); 6778const char * str_nhmx = getenv ("GGML_HEXAGON_NHMX" ); 6779const char * str_mm_select = getenv ("GGML_HEXAGON_MM_SELECT" ); 6780const char * str_fa_select = getenv ("GGML_HEXAGON_FA_SELECT" ); 6781const char * str_ar_select = getenv ("GGML_HEXAGON_AR_SELECT" ); 6782const char * str_ndev = getenv ("GGML_HEXAGON_NDEV" ); 6783const char * str_arch = getenv ("GGML_HEXAGON_ARCH" ); 6784const char * str_vmem = getenv ("GGML_HEXAGON_VMEM" ); 6785const char * str_mbuf = getenv ("GGML_HEXAGON_MBUF" ); 6786const char * str_optrace = getenv ("GGML_HEXAGON_OPTRACE" ); 6787const char * str_hostbuf = getenv ("GGML_HEXAGON_HOSTBUF" ); 6788 6789// Init Arch first since it affects other defaults 6790if (!str_arch ) { 6791int err = htpdrv_get_arch (CDSP_DOMAIN_ID ,& opt_arch ); 6792if (err != 0 ) { 6793GGML_LOG_ERROR ("ggml-hex: failed to query HTP version (err %d) defaulting to v73\n" ,err ); 6794opt_arch = 73 ; 6795 }else { 6796if (opt_arch < 73 ) { 6797GGML_LOG_WARN ("ggml-hex: Hexagon arch v%d is under supported range, capping at v73\n" ,opt_arch ); 6798opt_arch = 73 ; 6799 }else if (opt_arch > 81 ) { 6800GGML_LOG_WARN ("ggml-hex: Hexagon arch v%d is over supported range, capping at v81\n" ,opt_arch ); 6801opt_arch = 81 ; 6802 } 6803 } 6804 }else { 6805if (str_arch [0 ]== 'v' || str_arch [0 ]== 'V' ) { 6806str_arch ++ ; 6807 } 6808opt_arch = strtoul (str_arch ,NULL ,0 ); 6809 } 6810 6811size_t MiB = 1024 * 1024 ; 6812 6813// Update vmem default 6814opt_vmem = opt_arch >=75 ?HTP_OP_MAX_VMEM_DEFAULT :3000 * MiB ; 6815 6816auto RE_ICASE = std::regex_constants::icase ; 6817 6818opt_opfilter = str_opfilter ?new std::regex (str_opfilter ,RE_ICASE ) :NULL ; 6819opt_verbose = str_verbose ?atoi (str_verbose ) :0 ; 6820opt_opbatch = str_opbatch ?strtoul (str_opbatch ,NULL ,0 ) :opt_opbatch ; 6821opt_opqueue = str_opqueue ?strtoul (str_opqueue ,NULL ,0 ) :opt_opqueue ; 6822opt_optrace = str_optrace ?strtoul (str_optrace ,NULL ,0 ) : (opt_opbatch * 256 ); 6823opt_oppoll = str_oppoll ?strtoul (str_oppoll ,NULL ,0 ) :opt_oppoll ; 6824opt_opfusion = str_opfusion ?atoi (str_opfusion ) :opt_opfusion ; 6825opt_profile = str_profile ?atoi (str_profile ) :0 ; 6826opt_etm = str_etm ?atoi (str_etm ) :0 ; 6827opt_nhvx = str_nhvx ?strtoul (str_nhvx ,NULL ,0 ) :opt_nhvx ; 6828opt_nhmx = str_nhmx ?atoi (str_nhmx ) :opt_nhmx ; 6829opt_mm_select = str_mm_select ?atoi (str_mm_select ) :opt_mm_select ; 6830opt_fa_select = str_fa_select ?atoi (str_fa_select ) :opt_fa_select ; 6831opt_ar_select = str_ar_select ?atoi (str_ar_select ) :opt_ar_select ; 6832opt_mbuf = str_mbuf ?strtoul (str_mbuf ,NULL ,0 )* MiB :opt_mbuf ; 6833opt_vmem = str_vmem ?strtoul (str_vmem ,NULL ,0 )* MiB :opt_vmem ; 6834opt_hostbuf = str_hostbuf ?atoi (str_hostbuf )!= 0 :opt_hostbuf ; 6835 6836// Parse device configuration 6837const char * str_devices = getenv ("GGML_HEXAGON_DEVICES" ); 6838if (!str_devices && str_ndev && str_ndev [0 ]!= '\0' ) { 6839GGML_LOG_WARN ("DEPRECATED: GGML_HEXAGON_NDEV is deprecated. use GGML_HEXAGON_DEVICES instead\n" ); 6840str_devices = str_ndev ; 6841 } 6842 6843if (str_devices && str_devices [0 ]!= '\0' ) { 6844bool is_single_number = true; 6845for (int i = 0 ;str_devices [i ]!= '\0' ;i ++ ) { 6846if (!isdigit ((unsigned char )str_devices [i ])) { 6847is_single_number = false; 6848break ; 6849 } 6850 } 6851if (is_single_number ) { 6852int n = atoi (str_devices ); 6853if (n < 1 )n = 1 ; 6854if (n > GGML_HEXAGON_MAX_SESSIONS )n = GGML_HEXAGON_MAX_SESSIONS ; 6855opt_ndev = n ; 6856for (size_t i = 0 ;i < opt_ndev ;i ++ ) { 6857opt_device_configs [i ].physical_idx = 0 ; 6858opt_device_configs [i ].virtual_idx = (int )i ; 6859opt_device_configs [i ].name = "HTP" + std::to_string (i ); 6860opt_device_configs [i ].mdev_group .clear (); 6861 } 6862 }else { 6863 std::string s_devices (str_devices ); 6864 std::vector < std::string > items ; 6865 std::string curr_item ; 6866int bracket_depth = 0 ; 6867for (char ch :s_devices ) { 6868if (ch == '[' ) { 6869bracket_depth ++ ; 6870curr_item += ch ; 6871 }else if (ch == ']' ) { 6872if (bracket_depth > 0 )bracket_depth -- ; 6873curr_item += ch ; 6874 }else if (ch == ',' && bracket_depth == 0 ) { 6875size_t s = curr_item .find_first_not_of (" \t\r\n" ); 6876size_t e = curr_item .find_last_not_of (" \t\r\n" ); 6877if (s != std::string::npos ) { 6878items .push_back (curr_item .substr (s ,e - s + 1 )); 6879 } 6880curr_item .clear (); 6881 }else { 6882curr_item += ch ; 6883 } 6884 } 6885size_t s = curr_item .find_first_not_of (" \t\r\n" ); 6886size_t e = curr_item .find_last_not_of (" \t\r\n" ); 6887if (s != std::string::npos ) { 6888items .push_back (curr_item .substr (s ,e - s + 1 )); 6889 } 6890 6891opt_ndev = 0 ; 6892for (const auto & item :items ) { 6893size_t b_open = item .find ('[' ); 6894size_t b_close = item .rfind (']' ); 6895 6896if (b_open != std::string::npos && b_close != std::string::npos && b_close > b_open ) { 6897// Grouped / composite syntax: Name[phys_spec:virt] or Name[phys_spec] 6898 std::string dev_name = item .substr (0 ,b_open ); 6899 std::string content = item .substr (b_open + 1 ,b_close - b_open - 1 ); 6900 6901int virt = 0 ; 6902 std::string phys_spec = content ; 6903size_t colon_pos = content .find (':' ); 6904if (colon_pos != std::string::npos ) { 6905phys_spec = content .substr (0 ,colon_pos ); 6906try { 6907virt = std::stoi (content .substr (colon_pos + 1 )); 6908 }catch (...) { 6909virt = 0 ; 6910 } 6911 }else { 6912size_t dev_colon = dev_name .find (':' ); 6913if (dev_colon != std::string::npos ) { 6914try { 6915virt = std::stoi (dev_name .substr (dev_colon + 1 )); 6916 }catch (...) { 6917virt = 0 ; 6918 } 6919 } 6920 } 6921 6922// Parse physical indices from phys_spec (e.g. 0-1, 0,1, 0-3, etc.) 6923 std::vector < int > phys_list ; 6924 std::stringstream pss (phys_spec ); 6925 std::string p_part ; 6926while (std::getline (pss ,p_part ,',' )) { 6927size_t ps = p_part .find_first_not_of (" \t\r\n" ); 6928size_t pe = p_part .find_last_not_of (" \t\r\n" ); 6929if (ps == std::string::npos )continue ; 6930p_part = p_part .substr (ps ,pe - ps + 1 ); 6931 6932size_t dash_pos = p_part .find ('-' ); 6933if (dash_pos != std::string::npos ) { 6934try { 6935int p_start = std::stoi (p_part .substr (0 ,dash_pos )); 6936int p_end = std::stoi (p_part .substr (dash_pos + 1 )); 6937for (int p = p_start ;p <=p_end ;p ++ ) { 6938if (std::find (phys_list .begin (),phys_list .end (),p )== phys_list .end ()) { 6939phys_list .push_back (p ); 6940 } 6941 } 6942 }catch (...) { 6943GGML_LOG_WARN ("ggml-hex: failed to parse physical range in '%s'\n" ,p_part .c_str ()); 6944 } 6945 }else { 6946try { 6947int p = std::stoi (p_part ); 6948if (std::find (phys_list .begin (),phys_list .end (),p )== phys_list .end ()) { 6949phys_list .push_back (p ); 6950 } 6951 }catch (...) { 6952GGML_LOG_WARN ("ggml-hex: failed to parse physical index in '%s'\n" ,p_part .c_str ()); 6953 } 6954 } 6955 } 6956 6957if (phys_list .empty ()) { 6958phys_list .push_back (0 ); 6959 } 6960 6961if (opt_ndev < GGML_HEXAGON_MAX_SESSIONS ) { 6962auto & cfg = opt_device_configs [opt_ndev ]; 6963cfg .name = dev_name ; 6964cfg .physical_idx = phys_list [0 ]; 6965cfg .virtual_idx = virt ; 6966cfg .mdev_group .clear (); 6967 6968for (size_t k = 1 ;k < phys_list .size ();k ++ ) { 6969ggml_hexagon_device_config sub_cfg ; 6970sub_cfg .physical_idx = phys_list [k ]; 6971sub_cfg .virtual_idx = virt ; 6972sub_cfg .name = "HTP" + std::to_string (phys_list [k ])+ ":" + std::to_string (virt ); 6973cfg .mdev_group .push_back (sub_cfg ); 6974 } 6975opt_ndev ++ ; 6976 }else { 6977GGML_LOG_WARN ("ggml-hex: max sessions limit reached (%d), ignoring device %s\n" ,GGML_HEXAGON_MAX_SESSIONS ,item .c_str ()); 6978 } 6979 }else if (item .rfind ("HTP" ,0 )== 0 ) { 6980 std::string rest = item .substr (3 ); 6981size_t colon_pos = rest .find (':' ); 6982int phys = 0 ; 6983int virt = 0 ; 6984try { 6985if (colon_pos == std::string::npos ) { 6986phys = std::stoi (rest ); 6987virt = 0 ; 6988 }else { 6989phys = std::stoi (rest .substr (0 ,colon_pos )); 6990virt = std::stoi (rest .substr (colon_pos + 1 )); 6991 } 6992 }catch (...) { 6993GGML_LOG_WARN ("ggml-hex: failed to parse device index in '%s'\n" ,item .c_str ()); 6994continue ; 6995 } 6996 6997if (opt_ndev < GGML_HEXAGON_MAX_SESSIONS ) { 6998opt_device_configs [opt_ndev ].physical_idx = phys ; 6999opt_device_configs [opt_ndev ].virtual_idx = virt ; 7000opt_device_configs [opt_ndev ].name = colon_pos == std::string::npos 7001 ?"HTP" + std::to_string (phys ) 7002 :"HTP" + std::to_string (phys )+ ":" + std::to_string (virt ); 7003opt_device_configs [opt_ndev ].mdev_group .clear (); 7004opt_ndev ++ ; 7005 }else { 7006GGML_LOG_WARN ("ggml-hex: max sessions limit reached (%d), ignoring device %s\n" ,GGML_HEXAGON_MAX_SESSIONS ,item .c_str ()); 7007 } 7008 }else { 7009GGML_LOG_WARN ("ggml-hex: invalid device name format '%s', must start with HTP\n" ,item .c_str ()); 7010 } 7011 } 7012 } 7013 }else { 7014opt_ndev = 1 ; 7015opt_device_configs [0 ].physical_idx = 0 ; 7016opt_device_configs [0 ].virtual_idx = 0 ; 7017opt_device_configs [0 ].name = "HTP0" ; 7018opt_device_configs [0 ].mdev_group .clear (); 7019 } 7020 7021#if defined(__ANDROID__ ) 7022if (opt_arch < 75 ) { 7023opt_ndev = 1 ; 7024GGML_LOG_WARN ("ggml-hex: forcing ndev to 1 for SoCs archs lower than v75.\n" ); 7025 } 7026#endif 7027 7028// Resolve domain info for all configured devices 7029ggml_hexagon_discover_devices (); 7030 7031if (str_profile ) { 7032opt_pmu_evt = [& ]()-> std::vector < uint32_t > { 7033auto v = str_to_vec < uint32_t > (str_profile ); 7034switch (v .size ()) { 7035case 1 :opt_profile = v [0 ];return opt_pmu_evt ;// mode with default pmu events 7036case 8 :opt_profile = 2 ;return v ;// mode with custom pmu events 7037default :opt_profile = 0 ;return {};// garbage input 7038 }}(); 7039if (opt_profile == 1 )opt_pmu_evt = {}; 7040GGML_LOG_INFO ("ggml-hex: Profiling mode %u : pmu-evt [ %s ]\n" ,opt_profile , 7041vec_to_str < uint32_t ,16 > (opt_pmu_evt ).c_str ()); 7042 } 7043 7044reg -> context = new ggml_hexagon_registry (reg ); 7045} 7046 7047static const struct ggml_backend_reg_i ggml_backend_hexagon_reg_i = { 7048/* .get_name = */ ggml_backend_hexagon_reg_get_name , 7049/* .get_device_count = */ ggml_backend_hexagon_reg_get_device_count , 7050/* .get_device = */ ggml_backend_hexagon_reg_get_device , 7051/* .get_proc_address = */ ggml_backend_hexagon_get_proc_address , 7052}; 7053 7054ggml_backend_reg_t ggml_backend_hexagon_reg (void ) { 7055static bool initialized = false; 7056 7057static ggml_backend_reg reg = {/* .api_version = */ GGML_BACKEND_API_VERSION , 7058/* .iface = */ ggml_backend_hexagon_reg_i , 7059/* .context = */ NULL }; 7060 7061 { 7062static std::mutex mutex ; 7063 std::lock_guard < std::mutex > lock (mutex ); 7064if (!initialized ) { 7065auto nErr = htpdrv_init (); 7066if (nErr != AEE_SUCCESS ) { 7067return NULL ; 7068 } 7069 7070ggml_hexagon_init (& reg ); 7071 } 7072 7073initialized = true; 7074 } 7075 7076return & reg ; 7077} 7078 7079GGML_BACKEND_DL_IMPL (ggml_backend_hexagon_reg )