whispercpp 1.3.7 → 1.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -4
  3. data/ext/options.rb +1 -1
  4. data/ext/ruby_whisper.c +0 -1
  5. data/ext/ruby_whisper.h +7 -1
  6. data/ext/ruby_whisper_context.c +50 -1
  7. data/ext/ruby_whisper_log_settable.h +1 -2
  8. data/ext/ruby_whisper_params.c +9 -8
  9. data/ext/ruby_whisper_transcribe.cpp +0 -19
  10. data/ext/ruby_whisper_vad_context.c +30 -10
  11. data/ext/ruby_whisper_vad_context_detect.cpp +8 -9
  12. data/ext/ruby_whisper_vad_params.c +4 -4
  13. data/ext/ruby_whisper_vad_segment.c +2 -2
  14. data/ext/sources/CMakeLists.txt +2 -1
  15. data/ext/sources/cmake/parakeet.pc.in +2 -2
  16. data/ext/sources/cmake/whisper.pc.in +2 -2
  17. data/ext/sources/examples/cli/cli.cpp +9 -1
  18. data/ext/sources/examples/common-ggml.cpp +2 -0
  19. data/ext/sources/examples/vad-speech-segments/speech.cpp +3 -2
  20. data/ext/sources/ggml/CMakeLists.txt +3 -4
  21. data/ext/sources/ggml/include/ggml-cuda.h +0 -3
  22. data/ext/sources/ggml/include/ggml-sycl.h +8 -0
  23. data/ext/sources/ggml/include/ggml.h +3 -1
  24. data/ext/sources/ggml/src/CMakeLists.txt +8 -1
  25. data/ext/sources/ggml/src/ggml-backend-meta.cpp +7 -4
  26. data/ext/sources/ggml/src/ggml-common.h +13 -2
  27. data/ext/sources/ggml/src/ggml-cpu/CMakeLists.txt +1 -1
  28. data/ext/sources/ggml/src/ggml-cpu/amx/mmq.cpp +5 -6
  29. data/ext/sources/ggml/src/ggml-cpu/arch/arm/quants.c +78 -4
  30. data/ext/sources/ggml/src/ggml-cpu/arch/x86/quants.c +142 -4
  31. data/ext/sources/ggml/src/ggml-cpu/arch-fallback.h +7 -2
  32. data/ext/sources/ggml/src/ggml-cpu/ggml-cpu.c +14 -0
  33. data/ext/sources/ggml/src/ggml-cpu/llamafile/sgemm.cpp +26 -19
  34. data/ext/sources/ggml/src/ggml-cpu/ops.cpp +129 -46
  35. data/ext/sources/ggml/src/ggml-cpu/quants.c +51 -0
  36. data/ext/sources/ggml/src/ggml-cpu/quants.h +3 -0
  37. data/ext/sources/ggml/src/ggml-cpu/simd-gemm.h +1 -1
  38. data/ext/sources/ggml/src/ggml-cpu/simd-mappings.h +11 -0
  39. data/ext/sources/ggml/src/ggml-cpu/vec.cpp +2 -2
  40. data/ext/sources/ggml/src/ggml-cuda/binbcast.cu +90 -46
  41. data/ext/sources/ggml/src/ggml-cuda/col2im-1d.cu +81 -0
  42. data/ext/sources/ggml/src/ggml-cuda/col2im-1d.cuh +3 -0
  43. data/ext/sources/ggml/src/ggml-cuda/common.cuh +4 -0
  44. data/ext/sources/ggml/src/ggml-cuda/concat.cu +33 -21
  45. data/ext/sources/ggml/src/ggml-cuda/conv-transpose-1d.cu +14 -12
  46. data/ext/sources/ggml/src/ggml-cuda/convert.cu +86 -34
  47. data/ext/sources/ggml/src/ggml-cuda/cpy.cu +80 -29
  48. data/ext/sources/ggml/src/ggml-cuda/fattn-common.cuh +9 -5
  49. data/ext/sources/ggml/src/ggml-cuda/fattn-mma-f16.cuh +4 -0
  50. data/ext/sources/ggml/src/ggml-cuda/fattn-tile.cuh +9 -5
  51. data/ext/sources/ggml/src/ggml-cuda/fattn.cu +27 -21
  52. data/ext/sources/ggml/src/ggml-cuda/gated_delta_net.cu +40 -25
  53. data/ext/sources/ggml/src/ggml-cuda/gated_delta_net.cuh +10 -0
  54. data/ext/sources/ggml/src/ggml-cuda/getrows.cu +15 -12
  55. data/ext/sources/ggml/src/ggml-cuda/ggml-cuda.cu +718 -1248
  56. data/ext/sources/ggml/src/ggml-cuda/mmq.cu +7 -0
  57. data/ext/sources/ggml/src/ggml-cuda/mmvq.cu +77 -40
  58. data/ext/sources/ggml/src/ggml-cuda/out-prod.cu +55 -12
  59. data/ext/sources/ggml/src/ggml-cuda/set-rows.cu +64 -4
  60. data/ext/sources/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_16-ncols2_2.cu +1 -0
  61. data/ext/sources/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_32-ncols2_2.cu +1 -0
  62. data/ext/sources/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_2.cu +1 -0
  63. data/ext/sources/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_2.cu +1 -0
  64. data/ext/sources/ggml/src/ggml-cuda/topk-moe.cu +7 -1
  65. data/ext/sources/ggml/src/ggml-cuda/vendors/hip.h +1 -0
  66. data/ext/sources/ggml/src/ggml-cuda/vendors/musa.h +1 -0
  67. data/ext/sources/ggml/src/ggml-hexagon/CMakeLists.txt +0 -5
  68. data/ext/sources/ggml/src/ggml-hexagon/ggml-hexagon.cpp +1634 -1293
  69. data/ext/sources/ggml/src/ggml-hexagon/htp/CMakeLists.txt +11 -40
  70. data/ext/sources/ggml/src/ggml-hexagon/htp/cmake-toolchain.cmake +13 -15
  71. data/ext/sources/ggml/src/ggml-hexagon/htp/concat-ops.c +1 -1
  72. data/ext/sources/ggml/src/ggml-hexagon/htp/flash-attn-ops.c +1749 -399
  73. data/ext/sources/ggml/src/ggml-hexagon/htp/flash-attn-ops.h +303 -0
  74. data/ext/sources/ggml/src/ggml-hexagon/htp/hex-common.h +80 -0
  75. data/ext/sources/ggml/src/ggml-hexagon/htp/hex-dma.h +26 -23
  76. data/ext/sources/ggml/src/ggml-hexagon/htp/hex-profile.h +64 -0
  77. data/ext/sources/ggml/src/ggml-hexagon/htp/hex-utils.h +1 -83
  78. data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-fa-kernels.h +555 -0
  79. data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-mm-kernels-tiled.h +1303 -0
  80. data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-queue.c +9 -0
  81. data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-queue.h +27 -4
  82. data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-utils.h +59 -37
  83. data/ext/sources/ggml/src/ggml-hexagon/htp/htp-ctx.h +11 -3
  84. data/ext/sources/ggml/src/ggml-hexagon/htp/htp-ops.h +52 -12
  85. data/ext/sources/ggml/src/ggml-hexagon/htp/htp-vtcm.h +19 -0
  86. data/ext/sources/ggml/src/ggml-hexagon/htp/htp_iface.idl +2 -1
  87. data/ext/sources/ggml/src/ggml-hexagon/htp/hvx-base.h +14 -30
  88. data/ext/sources/ggml/src/ggml-hexagon/htp/hvx-exp.h +39 -0
  89. data/ext/sources/ggml/src/ggml-hexagon/htp/hvx-fa-kernels.h +232 -0
  90. data/ext/sources/ggml/src/ggml-hexagon/htp/hvx-mm-kernels-flat.h +1511 -0
  91. data/ext/sources/ggml/src/ggml-hexagon/htp/hvx-mm-kernels-tiled.h +1200 -0
  92. data/ext/sources/ggml/src/ggml-hexagon/htp/hvx-sigmoid.h +39 -0
  93. data/ext/sources/ggml/src/ggml-hexagon/htp/main.c +127 -32
  94. data/ext/sources/ggml/src/ggml-hexagon/htp/matmul-ops.c +3023 -4425
  95. data/ext/sources/ggml/src/ggml-hexagon/htp/matmul-ops.h +650 -0
  96. data/ext/sources/ggml/src/ggml-hexagon/htp/rope-ops.c +48 -13
  97. data/ext/sources/ggml/src/ggml-hexagon/htp/ssm-conv.c +10 -9
  98. data/ext/sources/ggml/src/ggml-hexagon/htp/worker-pool.c +15 -3
  99. data/ext/sources/ggml/src/ggml-hexagon/htp/worker-pool.h +8 -0
  100. data/ext/sources/ggml/src/ggml-hexagon/htp-opnode.h +168 -50
  101. data/ext/sources/ggml/src/ggml-hexagon/libggml-htp.inf +0 -4
  102. data/ext/sources/ggml/src/ggml-hip/CMakeLists.txt +5 -0
  103. data/ext/sources/ggml/src/ggml-metal/ggml-metal-device.cpp +69 -5
  104. data/ext/sources/ggml/src/ggml-metal/ggml-metal-device.h +4 -1
  105. data/ext/sources/ggml/src/ggml-metal/ggml-metal-device.m +27 -6
  106. data/ext/sources/ggml/src/ggml-metal/ggml-metal-impl.h +38 -0
  107. data/ext/sources/ggml/src/ggml-metal/ggml-metal-ops.cpp +132 -2
  108. data/ext/sources/ggml/src/ggml-metal/ggml-metal-ops.h +2 -0
  109. data/ext/sources/ggml/src/ggml-metal/ggml-metal.metal +345 -87
  110. data/ext/sources/ggml/src/ggml-opencl/CMakeLists.txt +13 -0
  111. data/ext/sources/ggml/src/ggml-opencl/fa_tune.h +92 -0
  112. data/ext/sources/ggml/src/ggml-opencl/ggml-opencl.cpp +4060 -357
  113. data/ext/sources/ggml/src/ggml-opencl/kernels/cvt.cl +198 -0
  114. data/ext/sources/ggml/src/ggml-opencl/kernels/flash_attn_f16.cl +81 -41
  115. data/ext/sources/ggml/src/ggml-opencl/kernels/flash_attn_f32.cl +88 -39
  116. data/ext/sources/ggml/src/ggml-opencl/kernels/flash_attn_f32_f16.cl +1995 -96
  117. data/ext/sources/ggml/src/ggml-opencl/kernels/flash_attn_f32_q4_0.cl +1615 -0
  118. data/ext/sources/ggml/src/ggml-opencl/kernels/flash_attn_f32_q8_0.cl +1486 -0
  119. data/ext/sources/ggml/src/ggml-opencl/kernels/flash_attn_pre_f16.cl +156 -0
  120. data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_mxfp4_f32_ns.cl +74 -6
  121. data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q4_0_f32_ns.cl +74 -6
  122. data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q4_1_f32_ns.cl +74 -6
  123. data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q4_k_f32_ns.cl +71 -6
  124. data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q5_0_f32_ns.cl +74 -6
  125. data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q5_1_f32_ns.cl +74 -6
  126. data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q5_k_f32_ns.cl +74 -6
  127. data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_moe_q6_k_f32_ns.cl +74 -6
  128. data/ext/sources/ggml/src/ggml-opencl/kernels/gemm_noshuffle_q1_0_f32.cl +94 -0
  129. data/ext/sources/ggml/src/ggml-opencl/kernels/gemv_noshuffle_q1_0_f32.cl +121 -0
  130. data/ext/sources/ggml/src/ggml-opencl/kernels/gemv_noshuffle_q8_0_f32.cl +1 -1
  131. data/ext/sources/ggml/src/ggml-opencl/kernels/mul_mm_q1_0_f32_l4_lm.cl +156 -0
  132. data/ext/sources/ggml/src/ggml-opencl/kernels/mul_mv_f16_f32_l4.cl +1149 -0
  133. data/ext/sources/ggml/src/ggml-opencl/kernels/mul_mv_q1_0_f32.cl +141 -0
  134. data/ext/sources/ggml/src/ggml-opencl/kernels/mul_mv_q1_0_f32_flat.cl +190 -0
  135. data/ext/sources/ggml/src/ggml-opencl/kernels/norm.cl +5 -2
  136. data/ext/sources/ggml/src/ggml-opencl/kernels/set_rows.cl +500 -0
  137. data/ext/sources/ggml/src/ggml-opencl/libdl.h +79 -0
  138. data/ext/sources/ggml/src/ggml-openvino/.clang-format +0 -5
  139. data/ext/sources/ggml/src/ggml-openvino/CMakeLists.txt +2 -4
  140. data/ext/sources/ggml/src/ggml-openvino/ggml-decoder.cpp +733 -130
  141. data/ext/sources/ggml/src/ggml-openvino/ggml-decoder.h +76 -23
  142. data/ext/sources/ggml/src/ggml-openvino/ggml-openvino-extra.cpp +57 -3
  143. data/ext/sources/ggml/src/ggml-openvino/ggml-openvino-extra.h +29 -8
  144. data/ext/sources/ggml/src/ggml-openvino/ggml-openvino.cpp +307 -59
  145. data/ext/sources/ggml/src/ggml-openvino/ggml-quants.cpp +66 -0
  146. data/ext/sources/ggml/src/ggml-openvino/ggml-quants.h +10 -4
  147. data/ext/sources/ggml/src/ggml-openvino/openvino/decoder.h +56 -16
  148. data/ext/sources/ggml/src/ggml-openvino/openvino/frontend.h +1 -1
  149. data/ext/sources/ggml/src/ggml-openvino/openvino/input_model.h +4 -4
  150. data/ext/sources/ggml/src/ggml-openvino/openvino/node_context.h +94 -37
  151. data/ext/sources/ggml/src/ggml-openvino/openvino/op/add_id.cpp +76 -0
  152. data/ext/sources/ggml/src/ggml-openvino/openvino/op/argsort.cpp +47 -0
  153. data/ext/sources/ggml/src/ggml-openvino/openvino/op/clamp.cpp +33 -0
  154. data/ext/sources/ggml/src/ggml-openvino/openvino/op/concat.cpp +48 -0
  155. data/ext/sources/ggml/src/ggml-openvino/openvino/op/cont.cpp +8 -16
  156. data/ext/sources/ggml/src/ggml-openvino/openvino/op/cpy.cpp +14 -1
  157. data/ext/sources/ggml/src/ggml-openvino/openvino/op/div.cpp +146 -0
  158. data/ext/sources/ggml/src/ggml-openvino/openvino/op/flash_attn_ext.cpp +108 -21
  159. data/ext/sources/ggml/src/ggml-openvino/openvino/op/gated_delta_net.cpp +282 -0
  160. data/ext/sources/ggml/src/ggml-openvino/openvino/op/gated_delta_net.hpp +65 -0
  161. data/ext/sources/ggml/src/ggml-openvino/openvino/op/get_rows.cpp +2 -9
  162. data/ext/sources/ggml/src/ggml-openvino/openvino/op/glu_geglu.cpp +21 -7
  163. data/ext/sources/ggml/src/ggml-openvino/openvino/op/glu_swiglu.cpp +41 -8
  164. data/ext/sources/ggml/src/ggml-openvino/openvino/op/im2col.cpp +120 -0
  165. data/ext/sources/ggml/src/ggml-openvino/openvino/op/l2_norm.cpp +44 -0
  166. data/ext/sources/ggml/src/ggml-openvino/openvino/op/mul_mat_id.cpp +226 -0
  167. data/ext/sources/ggml/src/ggml-openvino/openvino/op/mulmat.cpp +19 -9
  168. data/ext/sources/ggml/src/ggml-openvino/openvino/op/norm.cpp +58 -0
  169. data/ext/sources/ggml/src/ggml-openvino/openvino/op/pad.cpp +95 -0
  170. data/ext/sources/ggml/src/ggml-openvino/openvino/op/permute.cpp +58 -13
  171. data/ext/sources/ggml/src/ggml-openvino/openvino/op/repeat.cpp +74 -0
  172. data/ext/sources/ggml/src/ggml-openvino/openvino/op/reshape.cpp +13 -6
  173. data/ext/sources/ggml/src/ggml-openvino/openvino/op/rms_norm.cpp +1 -1
  174. data/ext/sources/ggml/src/ggml-openvino/openvino/op/rope.cpp +134 -38
  175. data/ext/sources/ggml/src/ggml-openvino/openvino/op/set_rows.cpp +3 -3
  176. data/ext/sources/ggml/src/ggml-openvino/openvino/op/softmax.cpp +126 -49
  177. data/ext/sources/ggml/src/ggml-openvino/openvino/op/ssm_conv.cpp +59 -0
  178. data/ext/sources/ggml/src/ggml-openvino/openvino/op/sum_rows.cpp +27 -0
  179. data/ext/sources/ggml/src/ggml-openvino/openvino/op/transpose.cpp +32 -1
  180. data/ext/sources/ggml/src/ggml-openvino/openvino/op/unary_silu.cpp +1 -1
  181. data/ext/sources/ggml/src/ggml-openvino/openvino/op/unary_softplus.cpp +38 -0
  182. data/ext/sources/ggml/src/ggml-openvino/openvino/op/view.cpp +90 -25
  183. data/ext/sources/ggml/src/ggml-openvino/openvino/op_table.cpp +41 -23
  184. data/ext/sources/ggml/src/ggml-openvino/openvino/op_table.h +18 -5
  185. data/ext/sources/ggml/src/ggml-openvino/openvino/pass/mark_decompression_convert_constant_folding.h +1 -1
  186. data/ext/sources/ggml/src/ggml-openvino/openvino/translate_session.cpp +43 -40
  187. data/ext/sources/ggml/src/ggml-openvino/openvino/translate_session.h +5 -4
  188. data/ext/sources/ggml/src/ggml-openvino/openvino/utils.cpp +548 -3
  189. data/ext/sources/ggml/src/ggml-openvino/openvino/utils.h +28 -26
  190. data/ext/sources/ggml/src/ggml-openvino/utils.cpp +383 -94
  191. data/ext/sources/ggml/src/ggml-openvino/utils.h +11 -8
  192. data/ext/sources/ggml/src/ggml-quants.c +76 -0
  193. data/ext/sources/ggml/src/ggml-quants.h +3 -0
  194. data/ext/sources/ggml/src/ggml-sycl/CMakeLists.txt +5 -5
  195. data/ext/sources/ggml/src/ggml-sycl/backend.hpp +2 -0
  196. data/ext/sources/ggml/src/ggml-sycl/binbcast.cpp +12 -0
  197. data/ext/sources/ggml/src/ggml-sycl/col2im-1d.cpp +102 -0
  198. data/ext/sources/ggml/src/ggml-sycl/col2im-1d.hpp +8 -0
  199. data/ext/sources/ggml/src/ggml-sycl/common.cpp +6 -8
  200. data/ext/sources/ggml/src/ggml-sycl/common.hpp +19 -2
  201. data/ext/sources/ggml/src/ggml-sycl/concat.cpp +21 -1
  202. data/ext/sources/ggml/src/ggml-sycl/conv2d-dw.cpp +158 -0
  203. data/ext/sources/ggml/src/ggml-sycl/conv2d-dw.hpp +10 -0
  204. data/ext/sources/ggml/src/ggml-sycl/conv2d-transpose.cpp +125 -0
  205. data/ext/sources/ggml/src/ggml-sycl/conv2d-transpose.hpp +10 -0
  206. data/ext/sources/ggml/src/ggml-sycl/conv2d.cpp +150 -0
  207. data/ext/sources/ggml/src/ggml-sycl/conv2d.hpp +10 -0
  208. data/ext/sources/ggml/src/ggml-sycl/conv3d.cpp +224 -0
  209. data/ext/sources/ggml/src/ggml-sycl/conv3d.hpp +8 -0
  210. data/ext/sources/ggml/src/ggml-sycl/convert.cpp +6 -0
  211. data/ext/sources/ggml/src/ggml-sycl/cpy.cpp +706 -0
  212. data/ext/sources/ggml/src/ggml-sycl/cpy.hpp +281 -0
  213. data/ext/sources/ggml/src/ggml-sycl/cross_entropy_loss.cpp +255 -0
  214. data/ext/sources/ggml/src/ggml-sycl/cross_entropy_loss.hpp +7 -0
  215. data/ext/sources/ggml/src/ggml-sycl/dequantize.hpp +15 -0
  216. data/ext/sources/ggml/src/ggml-sycl/dmmv.cpp +492 -319
  217. data/ext/sources/ggml/src/ggml-sycl/dpct/helper.hpp +15 -7
  218. data/ext/sources/ggml/src/ggml-sycl/element_wise.cpp +215 -115
  219. data/ext/sources/ggml/src/ggml-sycl/element_wise.hpp +2 -0
  220. data/ext/sources/ggml/src/ggml-sycl/ggml-sycl.cpp +1006 -336
  221. data/ext/sources/ggml/src/ggml-sycl/mmvq.cpp +252 -67
  222. data/ext/sources/ggml/src/ggml-sycl/mmvq.hpp +17 -0
  223. data/ext/sources/ggml/src/ggml-sycl/norm.cpp +103 -49
  224. data/ext/sources/ggml/src/ggml-sycl/outprod.cpp +45 -9
  225. data/ext/sources/ggml/src/ggml-sycl/pool.cpp +185 -0
  226. data/ext/sources/ggml/src/ggml-sycl/pool.hpp +22 -0
  227. data/ext/sources/ggml/src/ggml-sycl/presets.hpp +3 -1
  228. data/ext/sources/ggml/src/ggml-sycl/set_rows.cpp +10 -2
  229. data/ext/sources/ggml/src/ggml-sycl/softmax.cpp +9 -10
  230. data/ext/sources/ggml/src/ggml-sycl/vecdotq.hpp +35 -0
  231. data/ext/sources/ggml/src/ggml-vulkan/CMakeLists.txt +5 -0
  232. data/ext/sources/ggml/src/ggml-vulkan/ggml-vulkan.cpp +833 -215
  233. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/col2im_1d.comp +61 -0
  234. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/conv2d_mm.comp +1 -1
  235. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/conv3d_mm.comp +431 -0
  236. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/diag.comp +3 -3
  237. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/flash_attn.comp +1 -0
  238. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/flash_attn_cm1.comp +1 -0
  239. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/generic_unary_head.glsl +21 -19
  240. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/get_rows_back.comp +25 -0
  241. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/glu_head.glsl +23 -4
  242. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/glu_main.glsl +14 -18
  243. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/l2_norm.comp +4 -7
  244. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vecq.comp +21 -24
  245. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm.comp +31 -23
  246. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_cm2.comp +6 -5
  247. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_funcs.glsl +84 -67
  248. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/norm.comp +10 -10
  249. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/repeat_back.comp +3 -3
  250. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/roll.comp +3 -3
  251. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/tri.comp +3 -3
  252. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/unary.comp +168 -0
  253. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp +121 -74
  254. data/ext/sources/ggml/src/ggml-webgpu/ggml-webgpu-shader-lib.hpp +26 -19
  255. data/ext/sources/ggml/src/ggml-webgpu/ggml-webgpu.cpp +31 -36
  256. data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/common_decls.tmpl +16 -2
  257. data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/flash_attn_vec_split.wgsl +7 -7
  258. data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/get_rows.wgsl +21 -0
  259. data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/mul_mat_decls.tmpl +439 -320
  260. data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/mul_mat_id_vec.wgsl +2 -2
  261. data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/mul_mat_vec.wgsl +45 -39
  262. data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/mul_mat_vec_acc.tmpl +586 -465
  263. data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/mul_mat_vec_q_acc.tmpl +63 -69
  264. data/ext/sources/ggml/src/ggml-webgpu/wgsl-shaders/quantize_q8.wgsl +14 -9
  265. data/ext/sources/ggml/src/ggml.c +36 -14
  266. data/ext/sources/include/whisper.h +21 -0
  267. data/ext/sources/src/whisper.cpp +164 -14
  268. data/lib/whisper/log_settable.rb +5 -8
  269. data/lib/whisper/model/uri.rb +0 -7
  270. data/sig/whisper.rbs +6 -0
  271. data/test/test_vad.rb +9 -0
  272. data/test/test_vad_context.rb +2 -2
  273. data/whispercpp.gemspec +1 -1
  274. metadata +62 -37
  275. data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-flash-attn-ops.c +0 -1878
  276. data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-matmul-ops.c +0 -2066
  277. data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-ops.c +0 -6
  278. data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-ops.h +0 -88
  279. data/ext/sources/ggml/src/ggml-hexagon/htp/hmx-profile.h +0 -34
  280. data/ext/sources/ggml/src/ggml-hexagon/htp/vtcm-utils.h +0 -16
  281. data/ext/sources/ggml/src/ggml-openvino/openvino/op/unary_gelu.cpp +0 -25
  282. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/abs.comp +0 -21
  283. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/ceil.comp +0 -22
  284. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/clamp.comp +0 -17
  285. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/cos.comp +0 -17
  286. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/elu.comp +0 -27
  287. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/exp.comp +0 -20
  288. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/floor.comp +0 -22
  289. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/gelu.comp +0 -25
  290. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/gelu_erf.comp +0 -39
  291. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/gelu_quick.comp +0 -23
  292. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/hardsigmoid.comp +0 -22
  293. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/hardswish.comp +0 -22
  294. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/leaky_relu.comp +0 -22
  295. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/neg.comp +0 -20
  296. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/relu.comp +0 -21
  297. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/round.comp +0 -29
  298. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/sgn.comp +0 -21
  299. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/sigmoid.comp +0 -20
  300. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/silu.comp +0 -22
  301. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/sin.comp +0 -17
  302. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/softplus.comp +0 -23
  303. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/sqrt.comp +0 -17
  304. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/square.comp +0 -17
  305. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/step.comp +0 -22
  306. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/tanh.comp +0 -20
  307. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/trunc.comp +0 -22
  308. data/ext/sources/ggml/src/ggml-vulkan/vulkan-shaders/xielu.comp +0 -35
@@ -18,6 +18,7 @@
18
18
  #include <unordered_map>
19
19
  #include <regex>
20
20
  #include <queue>
21
+ #include <algorithm>
21
22
 
22
23
  #ifdef _WIN32
23
24
  # include <sal.h>
@@ -41,6 +42,8 @@
41
42
  #include "ggml-quants.h"
42
43
  #include "htp-opnode.h"
43
44
  #include "htp-ops.h"
45
+ #include "htp/matmul-ops.h"
46
+ #include "htp/flash-attn-ops.h"
44
47
  #include "htp_iface.h"
45
48
  #include "htp-drv.h"
46
49
 
@@ -51,7 +54,7 @@ using u32vec = std::vector<uint32_t>;
51
54
  static int opt_arch = 0; // autodetect
52
55
  static size_t opt_ndev = 1;
53
56
  static size_t opt_nhvx = 0; // use all
54
- static int opt_use_hmx = 1; // when set, enable HMX; when 0, use HVX only
57
+ static int opt_nhmx = 1; // when set, enable HMX; when 0, use HVX only
55
58
  static size_t opt_vmem = HTP_OP_MAX_VMEM_DEFAULT; // max available va space for buffer mappings
56
59
  static size_t opt_mbuf = 1ul * 1024 * 1024 * 1024; // max buffer size
57
60
  static int opt_etm = 0;
@@ -59,6 +62,9 @@ static int opt_verbose = 0;
59
62
  static int opt_profile = 0; // profiling mode (0-disabled, 1-basic, 2-pmu)
60
63
  static int opt_hostbuf = 1; // hostbuf ON by default
61
64
 
65
+ static int opt_mm_select = 3; // 3 = HMX -> Tiled -> Flat -> CPU, 2 = Tiled -> Flat -> CPU, 1 = Flat -> CPU
66
+ static int opt_fa_select = 2; // 2 = HMX -> HVX -> CPU, 1 = HVX -> CPU, 0 = CPU (unsupported)
67
+
62
68
  // Default PMU events, if profiling with PMU (mode=2) is enabled
63
69
  // See https://docs.qualcomm.com/doc/80-N2040-60/topic/pmu-events.html
64
70
  // https://docs.qualcomm.com/doc/80-N2040-61/topic/hvx-pmu-events.html
@@ -68,21 +74,15 @@ static u32vec opt_pmu_evt { 0x3, 0x111, 0x100, 0x105, 0x240, 0x256, 0x7D, 0x8C }
68
74
  static int opt_opstage = HTP_OPSTAGE_QUEUE | HTP_OPSTAGE_COMPUTE;
69
75
  static int opt_opbatch = 1024; // max number of ops in a batch
70
76
  static int opt_opqueue = 16; // max number of pending batches
77
+ static int opt_optrace = 0; // trace buffer size per thread (0 means default)
71
78
  static int opt_oppoll = 0; // polling for batch completions
79
+ static int opt_opfusion = 1; // enable/disable op fusion
72
80
 
73
81
  static std::regex* opt_opfilter = NULL; // regex of ops to not claim
74
82
 
75
83
  #define HEX_VERBOSE(...) \
76
84
  if (opt_verbose) GGML_LOG_DEBUG(__VA_ARGS__)
77
85
 
78
- static inline uint64_t hex_is_aligned(void * addr, uint32_t align) {
79
- return ((size_t) addr & (align - 1)) == 0;
80
- }
81
-
82
- static inline size_t hex_round_up(size_t n, size_t m) {
83
- return m * ((n + m - 1) / m);
84
- }
85
-
86
86
  static const char * status_to_str(uint32_t status) {
87
87
  switch (status) {
88
88
  case HTP_STATUS_OK:
@@ -106,34 +106,94 @@ static void ggml_hexagon_dump_op_exec(const std::string &sess_name, const htp_op
106
106
  if (!opt_verbose) return;
107
107
 
108
108
  htp_opformat fmt(node);
109
- GGML_LOG_DEBUG("ggml-hex: %s execute-op %s: %s : %s : %s : %s : %s : flags 0x%x\n", sess_name.c_str(),
110
- node.op_name().c_str(), fmt.names, fmt.dims, fmt.types, fmt.strides, fmt.buffs, req_flags);
109
+ GGML_LOG_DEBUG("ggml-hex: %s execute-op %s|%s|%s|%s|%s|%s|%s|flags 0x%x\n", sess_name.c_str(),
110
+ node.op_name().c_str(), fmt.names, fmt.dims, fmt.types, fmt.strides, fmt.buffs, fmt.kparams, req_flags);
111
111
  }
112
112
 
113
113
  static void ggml_hexagon_dump_op_supp(const std::string &sess_name, const struct ggml_tensor * op, bool supp) {
114
114
  if (!opt_verbose) return;
115
115
 
116
116
  htp_opformat fmt(htp_opformat(htp_opnode{const_cast<ggml_tensor*>(op), {}, HTP_OP_INVALID}));
117
- GGML_LOG_DEBUG("ggml-hex: %s supports-op %s: %s : %s : %s : %s : %s : %s\n", sess_name.c_str(),
117
+ GGML_LOG_DEBUG("ggml-hex: %s supports-op %s|%s|%s|%s|%s|%s|%s\n", sess_name.c_str(),
118
118
  ggml_op_desc(op), fmt.names, fmt.dims, fmt.types, fmt.strides, fmt.buffs, supp ? "yes" : "no");
119
119
  }
120
120
 
121
+ static const char * htp_event_name(uint16_t id) {
122
+ switch (id) {
123
+ case HTP_TRACE_EVT_DMA: return "DMA";
124
+ case HTP_TRACE_EVT_HVX_COMP: return "HVX_COMP";
125
+ case HTP_TRACE_EVT_HVX_A_QUANT: return "HVX_A_QUANT";
126
+ case HTP_TRACE_EVT_HVX_A_PREP: return "HVX_A_PREP";
127
+ case HTP_TRACE_EVT_HVX_W_DEQUANT: return "HVX_W_DEQUANT";
128
+ case HTP_TRACE_EVT_HVX_W_PREP: return "HVX_W_PREP";
129
+ case HTP_TRACE_EVT_HVX_O_PROC: return "HVX_O_PROC";
130
+ case HTP_TRACE_EVT_HVX_FA_QK: return "HVX_QK_FA";
131
+ case HTP_TRACE_EVT_HVX_FA_SFM: return "HVX_SFM_FA";
132
+ case HTP_TRACE_EVT_HVX_FA_Q_PREP: return "HVX_Q_PREP";
133
+ case HTP_TRACE_EVT_HVX_FA_K_PREP: return "HVX_K_PREP";
134
+ case HTP_TRACE_EVT_HVX_FA_V_PREP: return "HVX_V_PREP";
135
+ case HTP_TRACE_EVT_HMX_COMP: return "HMX_COMP";
136
+ default: return "UNKNOWN";
137
+ }
138
+ }
139
+
121
140
  static void ggml_hexagon_dump_op_prof(const std::string &sess_name, const htp_opnode & node,
122
- uint32_t op_usec, uint32_t op_cycles, const uint32_t pmu[]) {
141
+ const htp_prof_desc & pd) {
123
142
  if (!opt_profile) return;
124
143
 
144
+ uint32_t op_usec = pd.usecs;
145
+ uint32_t op_cycles = pd.cycles_stop - pd.cycles_start;
146
+ const uint32_t * pmu = pd.pmu;
147
+
125
148
  char pmu_str[256] = "";
126
- if (opt_profile > 1) {
149
+ if (opt_profile == 2) {
127
150
  static_assert(HTP_PROF_PMU_NCNT == 8, "current implementation assumes 8 PMU counters");
128
- sprintf(pmu_str, " pmu [%u,%u,%u,%u,%u,%u,%u,%u]",
151
+ snprintf(pmu_str, sizeof(pmu_str), " pmu [%u,%u,%u,%u,%u,%u,%u,%u]",
129
152
  pmu[0], pmu[1], pmu[2], pmu[3], pmu[4], pmu[5], pmu[6], pmu[7]);
130
153
  }
131
154
 
132
155
  htp_opformat fmt(node);
133
- GGML_LOG_DEBUG("ggml-hex: %s profile-op %s: %s : %s : %s : %s : usec %u cycles %u%s\n", sess_name.c_str(),
134
- node.op_name().c_str(), fmt.names, fmt.dims, fmt.types, fmt.strides, op_usec, op_cycles, pmu_str);
156
+ float mhz = op_usec > 0 ? (float) op_cycles / op_usec : 0.0f;
157
+ GGML_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(),
158
+ node.op_name().c_str(), fmt.names, fmt.dims, fmt.types, fmt.strides, fmt.kparams, op_usec, op_cycles, pd.cycles_start, mhz, pmu_str);
159
+ }
160
+
161
+ // **
162
+
163
+ static inline bool ggml_hexagon_is_repack_type(enum ggml_type type) {
164
+ return type == GGML_TYPE_Q4_0 || type == GGML_TYPE_Q4_1 ||
165
+ type == GGML_TYPE_Q8_0 || type == GGML_TYPE_IQ4_NL ||
166
+ type == GGML_TYPE_MXFP4;
135
167
  }
136
168
 
169
+ static inline bool ggml_hexagon_is_hmx_weight_type(enum ggml_type type) {
170
+ return type == GGML_TYPE_F16 || type == GGML_TYPE_F32 || ggml_hexagon_is_repack_type(type);
171
+ }
172
+
173
+ struct htp_mm_kernel_params;
174
+ struct ggml_hexagon_session;
175
+ static void ggml_hexagon_precompute_matmul_params(
176
+ const struct ggml_hexagon_session * sess,
177
+ const struct ggml_tensor * src0,
178
+ const struct ggml_tensor * src1,
179
+ const struct ggml_tensor * dst,
180
+ struct htp_mm_kernel_params * kparams
181
+ );
182
+
183
+ static void ggml_hexagon_precompute_fused_qkv_params(
184
+ const struct ggml_hexagon_session * sess,
185
+ const struct ggml_tensor * src0,
186
+ const struct ggml_tensor * src1,
187
+ struct htp_mm_kernel_params * kparams
188
+ );
189
+
190
+ static void ggml_hexagon_precompute_fused_ffn_params(
191
+ const struct ggml_hexagon_session * sess,
192
+ const struct ggml_tensor * src0,
193
+ const struct ggml_tensor * src1,
194
+ struct htp_mm_kernel_params * kparams
195
+ );
196
+
137
197
  // ** backend sessions
138
198
 
139
199
  struct ggml_hexagon_opbatch;
@@ -160,6 +220,18 @@ struct ggml_hexagon_session {
160
220
  ggml_backend_buffer_type buffer_type = {};
161
221
  ggml_backend_buffer_type repack_buffer_type = {};
162
222
 
223
+ uint32_t n_threads = 0;
224
+ uint32_t n_hvx = 0;
225
+ uint32_t n_hmx = 0;
226
+ uint64_t vtcm_size = 0;
227
+ size_t max_vmem = 0;
228
+ size_t max_bufsize = 0;
229
+
230
+ struct {
231
+ uint64_t uid = 0;
232
+ std::vector<htp_opnode> htp_nodes;
233
+ } cached_graph;
234
+
163
235
  ggml_hexagon_session(int dev_id, ggml_backend_dev_t dev) noexcept(false);
164
236
  ~ggml_hexagon_session() noexcept(true);
165
237
 
@@ -305,47 +377,7 @@ static enum ggml_status ggml_backend_hexagon_buffer_init_tensor(ggml_backend_buf
305
377
  return GGML_STATUS_SUCCESS;
306
378
  }
307
379
 
308
- // ======== Q4x4x2 ====================
309
- struct x2_q4 {
310
- int v[2];
311
- };
312
-
313
- static x2_q4 unpack_q4(uint8_t v) {
314
- x2_q4 x = { (int) (v & 0x0f) - 8, (int) (v >> 4) - 8 };
315
- return x;
316
- }
317
-
318
- static void dump_block_q4_0(const block_q4_0 * b, int i) {
319
- HEX_VERBOSE("ggml-hex: repack q4_0 %d: %d %d %d %d ... %d %d %d %d : %.6f\n", i, unpack_q4(b->qs[0]).v[0],
320
- unpack_q4(b->qs[1]).v[0], unpack_q4(b->qs[2]).v[0], unpack_q4(b->qs[3]).v[0], unpack_q4(b->qs[12]).v[1],
321
- unpack_q4(b->qs[13]).v[1], unpack_q4(b->qs[14]).v[1], unpack_q4(b->qs[15]).v[1],
322
- GGML_FP16_TO_FP32(b->d));
323
- }
324
-
325
- static void dump_packed_block_q4x4x2(const uint8_t * v, unsigned int i, size_t k) {
326
- static const int qk = QK_Q4_0x4x2;
327
- const int dblk_size = 8 * 2; // 8x __fp16
328
- const int qblk_size = qk / 2; // int4
329
- const int qrow_size = k / 2; // int4 (not padded)
330
-
331
- const uint8_t * v_q = v + 0; // quants first
332
- const uint8_t * v_d = v + qrow_size; // then scales
333
-
334
- const uint8_t * q = v_q + i * qblk_size;
335
- const ggml_half * d = (const ggml_half *) (v_d + i * dblk_size);
336
-
337
- HEX_VERBOSE("ggml-hex: repack q4x4x2-%d: %d %d %d %d ... %d %d %d %d ... %d %d %d %d : %.6f %.6f %.6f %.6f\n", i,
338
- unpack_q4(q[0]).v[0], unpack_q4(q[1]).v[0], unpack_q4(q[2]).v[0], unpack_q4(q[3]).v[0],
339
- unpack_q4(q[60]).v[0], unpack_q4(q[61]).v[0], unpack_q4(q[62]).v[0], unpack_q4(q[63]).v[0],
340
- unpack_q4(q[124]).v[0], unpack_q4(q[125]).v[0], unpack_q4(q[126]).v[0], unpack_q4(q[127]).v[0],
341
- GGML_FP16_TO_FP32(d[0]), GGML_FP16_TO_FP32(d[1]), GGML_FP16_TO_FP32(d[2]), GGML_FP16_TO_FP32(d[3]));
342
-
343
- HEX_VERBOSE("ggml-hex: repack q4x4x2-%d: %d %d %d %d ... %d %d %d %d ... %d %d %d %d : %.6f %.6f %.6f %.6f\n",
344
- i + 1, unpack_q4(q[0]).v[1], unpack_q4(q[1]).v[1], unpack_q4(q[2]).v[1], unpack_q4(q[3]).v[1],
345
- unpack_q4(q[60]).v[1], unpack_q4(q[61]).v[1], unpack_q4(q[62]).v[1], unpack_q4(q[63]).v[1],
346
- unpack_q4(q[124]).v[1], unpack_q4(q[125]).v[1], unpack_q4(q[126]).v[1], unpack_q4(q[127]).v[1],
347
- GGML_FP16_TO_FP32(d[4]), GGML_FP16_TO_FP32(d[5]), GGML_FP16_TO_FP32(d[6]), GGML_FP16_TO_FP32(d[7]));
348
- }
380
+ // ** Repack helpers for tiled quantized weights
349
381
 
350
382
  static void unpack_q4_0_quants(uint8_t * qs, const block_q4_0 * x, unsigned int bi) {
351
383
  static const int qk = QK4_0;
@@ -368,300 +400,6 @@ static void pack_q4_0_quants(block_q4_0 * x, const uint8_t * qs, unsigned int bi
368
400
  }
369
401
  }
370
402
 
371
- static void repack_row_q4x4x2(uint8_t * y, const block_q4_0 * x, int64_t k) {
372
- static const int qk = QK_Q4_0x4x2;
373
- const int nb = (k + qk - 1) / qk; // number of blocks (padded)
374
- const int nloe = k % qk; // leftovers
375
-
376
- const int dblk_size = 8 * 2; // 8x __fp16
377
- const int qblk_size = qk / 2; // int4
378
- const int qrow_size = k / 2; // int4 (not padded to blocks)
379
-
380
- uint8_t * y_q = y + 0; // quants first
381
- uint8_t * y_d = y + qrow_size; // then scales
382
-
383
- if (opt_verbose > 2) {
384
- for (int i = 0; i < nb; i++) {
385
- dump_block_q4_0(&x[i * 8 + 0], 0);
386
- dump_block_q4_0(&x[i * 8 + 1], 1);
387
- dump_block_q4_0(&x[i * 8 + 2], 2);
388
- dump_block_q4_0(&x[i * 8 + 3], 3);
389
- dump_block_q4_0(&x[i * 8 + 4], 4);
390
- dump_block_q4_0(&x[i * 8 + 5], 5);
391
- dump_block_q4_0(&x[i * 8 + 6], 6);
392
- dump_block_q4_0(&x[i * 8 + 7], 7);
393
- }
394
- }
395
-
396
- // Repack the quants
397
- for (int i = 0; i < nb; i++) {
398
- uint8_t qs[QK_Q4_0x4x2]; // unpacked quants
399
- unpack_q4_0_quants(qs, &x[i * 8 + 0], 0);
400
- unpack_q4_0_quants(qs, &x[i * 8 + 1], 1);
401
- unpack_q4_0_quants(qs, &x[i * 8 + 2], 2);
402
- unpack_q4_0_quants(qs, &x[i * 8 + 3], 3);
403
- unpack_q4_0_quants(qs, &x[i * 8 + 4], 4);
404
- unpack_q4_0_quants(qs, &x[i * 8 + 5], 5);
405
- unpack_q4_0_quants(qs, &x[i * 8 + 6], 6);
406
- unpack_q4_0_quants(qs, &x[i * 8 + 7], 7);
407
-
408
- bool partial = (nloe && i == nb-1);
409
-
410
- uint8_t * q = y_q + (i * qblk_size);
411
- for (int j = 0; j < qk / 2; j++) {
412
- q[j] = partial ? (qs[j*2+1] << 4) | qs[j*2+0] : (qs[j+128] << 4) | qs[j+000];
413
- }
414
- }
415
-
416
- // Repack the scales
417
- // Note: Do not combine with the loop above. For tensor sizes not multiple of 256 (QK_Q4_0x4x2)
418
- // the last block is truncated and overridden by the scales.
419
- for (int i = 0; i < nb; i++) {
420
- // Repack the scales
421
- ggml_half * d = (ggml_half *) (y_d + i * dblk_size);
422
- d[0] = x[i * 8 + 0].d;
423
- d[1] = x[i * 8 + 1].d;
424
- d[2] = x[i * 8 + 2].d;
425
- d[3] = x[i * 8 + 3].d;
426
- d[4] = x[i * 8 + 4].d;
427
- d[5] = x[i * 8 + 5].d;
428
- d[6] = x[i * 8 + 6].d;
429
- d[7] = x[i * 8 + 7].d;
430
- }
431
-
432
- if (opt_verbose > 2) {
433
- for (int i = 0; i < nb; i++) {
434
- dump_packed_block_q4x4x2(y, i, k);
435
- }
436
- }
437
- }
438
-
439
- static void unpack_row_q4x4x2(block_q4_0 * x, const uint8_t * y, int64_t k) {
440
- static const int qk = QK_Q4_0x4x2;
441
- const int nb = (k + qk - 1) / qk; // number of blocks (padded)
442
- const int nloe = k % qk; // leftovers
443
-
444
- const int dblk_size = 8 * 2; // 8x __fp16
445
- const int qblk_size = qk / 2; // int4
446
- const int qrow_size = k / 2; // int4 (not padded to blocks)
447
-
448
- const uint8_t * y_q = y + 0; // quants first
449
- const uint8_t * y_d = y + qrow_size; // then scales
450
-
451
- if (opt_verbose > 2) {
452
- for (int i = 0; i < nb; i++) {
453
- dump_packed_block_q4x4x2(y, i, k);
454
- }
455
- }
456
-
457
- // Unpack the quants
458
- for (int i = 0; i < nb; i++) {
459
- uint8_t qs[QK_Q4_0x4x2]; // unpacked quants
460
-
461
- bool partial = (nloe && i == nb-1);
462
-
463
- const uint8_t * q = y_q + (i * qblk_size);
464
- for (int j = 0; j < qk / 2; j++) {
465
- if (partial) {
466
- qs[j*2+0] = q[j] & 0xf;
467
- qs[j*2+1] = q[j] >> 4;
468
- } else {
469
- qs[j+000] = q[j] & 0xf;
470
- qs[j+128] = q[j] >> 4;
471
- }
472
- }
473
-
474
- pack_q4_0_quants(&x[i * 8 + 0], qs, 0);
475
- pack_q4_0_quants(&x[i * 8 + 1], qs, 1);
476
- pack_q4_0_quants(&x[i * 8 + 2], qs, 2);
477
- pack_q4_0_quants(&x[i * 8 + 3], qs, 3);
478
- pack_q4_0_quants(&x[i * 8 + 4], qs, 4);
479
- pack_q4_0_quants(&x[i * 8 + 5], qs, 5);
480
- pack_q4_0_quants(&x[i * 8 + 6], qs, 6);
481
- pack_q4_0_quants(&x[i * 8 + 7], qs, 7);
482
- }
483
-
484
- // Repack the scales
485
- // Note: Do not combine with the loop above. For tensor sizes not multiple of 256 (QK_Q4_0x4x2)
486
- // the last block is truncated and overridden by the scales.
487
- for (int i = 0; i < nb; i++) {
488
- // Unpack the scales
489
- const ggml_half * d = (const ggml_half *) (y_d + i * dblk_size);
490
- x[i * 8 + 0].d = d[0];
491
- x[i * 8 + 1].d = d[1];
492
- x[i * 8 + 2].d = d[2];
493
- x[i * 8 + 3].d = d[3];
494
- x[i * 8 + 4].d = d[4];
495
- x[i * 8 + 5].d = d[5];
496
- x[i * 8 + 6].d = d[6];
497
- x[i * 8 + 7].d = d[7];
498
- }
499
-
500
- if (opt_verbose > 2) {
501
- for (int i = 0; i < nb; i++) {
502
- dump_block_q4_0(&x[i * 8 + 0], 0);
503
- dump_block_q4_0(&x[i * 8 + 1], 1);
504
- dump_block_q4_0(&x[i * 8 + 2], 2);
505
- dump_block_q4_0(&x[i * 8 + 3], 3);
506
- dump_block_q4_0(&x[i * 8 + 4], 4);
507
- dump_block_q4_0(&x[i * 8 + 5], 5);
508
- dump_block_q4_0(&x[i * 8 + 6], 6);
509
- dump_block_q4_0(&x[i * 8 + 7], 7);
510
- }
511
- }
512
- }
513
-
514
- static void init_row_q4x4x2(block_q4_0 * x, int64_t k) {
515
- static const int qk = QK_Q4_0x4x2;
516
- const int nb = (k + qk - 1) / qk; // number of blocks (padded)
517
-
518
- // Init the quants such that they unpack into zeros
519
- uint8_t qs[QK_Q4_0x4x2]; // unpacked quants
520
- memset(qs, 8, sizeof(qs));
521
-
522
- for (int i = 0; i < nb; i++) {
523
- pack_q4_0_quants(&x[i * 8 + 0], qs, 0);
524
- pack_q4_0_quants(&x[i * 8 + 1], qs, 1);
525
- pack_q4_0_quants(&x[i * 8 + 2], qs, 2);
526
- pack_q4_0_quants(&x[i * 8 + 3], qs, 3);
527
- pack_q4_0_quants(&x[i * 8 + 4], qs, 4);
528
- pack_q4_0_quants(&x[i * 8 + 5], qs, 5);
529
- pack_q4_0_quants(&x[i * 8 + 6], qs, 6);
530
- pack_q4_0_quants(&x[i * 8 + 7], qs, 7);
531
- }
532
-
533
- // Init the scales
534
- // Note: Do not combine with the loop above. For tensor sizes not multiple of 256 (QK_Q4_0x4x2)
535
- // the last block is truncated and overridden by the scales.
536
- for (int i = 0; i < nb; i++) {
537
- // Unpack the scales
538
- x[i * 8 + 0].d = 0;
539
- x[i * 8 + 1].d = 0;
540
- x[i * 8 + 2].d = 0;
541
- x[i * 8 + 3].d = 0;
542
- x[i * 8 + 4].d = 0;
543
- x[i * 8 + 5].d = 0;
544
- x[i * 8 + 6].d = 0;
545
- x[i * 8 + 7].d = 0;
546
- }
547
- }
548
-
549
- // repack q4_0 data into q4x4x2 tensor
550
- static void repack_q4_0_q4x4x2(ggml_tensor * t, const void * data, size_t size) {
551
- int64_t nrows = ggml_nrows(t);
552
-
553
- size_t row_size = ggml_row_size(t->type, t->ne[0]);
554
- size_t row_size_pd = ggml_row_size(t->type, hex_round_up(t->ne[0], QK_Q4_0x4x2)); // extra elements for the pad
555
- size_t row_size_rp = row_size_pd; // scratch must hold one full padded tile (qblk_size/2 quants + scales)
556
-
557
- // Ensure we don't try to read more data than is available in the source buffer 'data'
558
- // or write more than the tensor can hold.
559
- const size_t total_tensor_size = (size_t)nrows * row_size;
560
- const size_t n_bytes_to_copy = size < total_tensor_size ? size : total_tensor_size;
561
-
562
- // Calculate how many full rows and how many remaining bytes we need to process.
563
- const int64_t n_full_rows = n_bytes_to_copy / row_size;
564
- const size_t n_rem_bytes = n_bytes_to_copy % row_size;
565
-
566
- void * buf_pd = ggml_aligned_malloc(row_size_pd);
567
- GGML_ASSERT(buf_pd != NULL);
568
-
569
- void * buf_rp = ggml_aligned_malloc(row_size_rp);
570
- GGML_ASSERT(buf_rp != NULL);
571
-
572
- HEX_VERBOSE("ggml-hex: repack-q4_0-q4x4x2 %s : data %p size %zu dims %ldx%ld row-size %zu\n", t->name, data, size,
573
- t->ne[0], nrows, row_size);
574
-
575
- init_row_q4x4x2((block_q4_0 *) buf_pd, t->ne[0]); // init padded buffer to make sure the tail is all zeros
576
-
577
- // 1. Process all the full rows
578
- for (int64_t i = 0; i < n_full_rows; i++) {
579
- const uint8_t * src = (const uint8_t *) data + (i * row_size);
580
- uint8_t * dst = (uint8_t *) t->data + (i * row_size);
581
-
582
- memcpy(buf_pd, src, row_size);
583
- repack_row_q4x4x2((uint8_t *) buf_rp, (const block_q4_0 *) buf_pd, t->ne[0]);
584
- memcpy(dst, buf_rp, row_size);
585
- }
586
-
587
- // 2. Process the final, potentially partial, row
588
- if (n_rem_bytes > 0) {
589
- const int64_t i = n_full_rows;
590
- const uint8_t * src = (const uint8_t *) data + (i * row_size);
591
- uint8_t * dst = (uint8_t *) t->data + (i * row_size);
592
-
593
- // re-init the row because we are potentially copying a partial row
594
- init_row_q4x4x2((block_q4_0 *) buf_pd, t->ne[0]);
595
-
596
- // Copy only the remaining bytes from the source.
597
- memcpy(buf_pd, src, n_rem_bytes);
598
-
599
- // Repack the entire buffer
600
- repack_row_q4x4x2((uint8_t *) buf_rp, (const block_q4_0 *) buf_pd, t->ne[0]);
601
-
602
- // Write only the corresponding remaining bytes to the destination tensor.
603
- memcpy(dst, buf_rp, n_rem_bytes);
604
- }
605
-
606
- ggml_aligned_free(buf_pd, row_size_pd);
607
- ggml_aligned_free(buf_rp, row_size_rp);
608
- }
609
-
610
- // repack q4x4x2 tensor into q4_0 data
611
- static void repack_q4x4x2_q4_0(void * data, const ggml_tensor * t, size_t size) {
612
- int64_t nrows = ggml_nrows(t);
613
-
614
- size_t row_size = ggml_row_size(t->type, t->ne[0]);
615
- size_t row_size_pd = ggml_row_size(t->type, hex_round_up(t->ne[0], QK_Q4_0x4x2)); // extra elements for the pad
616
- size_t row_size_rp = row_size_pd; // scratch must hold one full padded tile (qblk_size/2 quants + scales)
617
-
618
- // Ensure we don't try to copy more data than the tensor actually contains.
619
- const size_t total_tensor_size = (size_t)nrows * row_size;
620
- const size_t n_bytes_to_copy = size < total_tensor_size ? size : total_tensor_size;
621
-
622
- // Calculate how many full rows and how many remaining bytes we need to process.
623
- const int64_t n_full_rows = n_bytes_to_copy / row_size;
624
- const size_t n_rem_bytes = n_bytes_to_copy % row_size;
625
-
626
- void * buf_pd = ggml_aligned_malloc(row_size_pd);
627
- GGML_ASSERT(buf_pd != NULL);
628
-
629
- void * buf_rp = ggml_aligned_malloc(row_size_rp);
630
- GGML_ASSERT(buf_rp != NULL);
631
-
632
- HEX_VERBOSE("ggml-hex: repack-q4x4x2-q4_0 %s : data %p size %zu dims %ldx%ld row-size %zu\n", t->name, data, size,
633
- t->ne[0], nrows, row_size);
634
-
635
- memset(buf_pd, 0, row_size_pd); // clear-out padded buffer to make sure the tail is all zeros
636
-
637
- // 1. Process all the full rows
638
- for (int64_t i = 0; i < n_full_rows; i++) {
639
- const uint8_t * src = (const uint8_t *) t->data + (i * row_size);
640
- uint8_t * dst = (uint8_t *) data + (i * row_size);
641
-
642
- memcpy(buf_pd, src, row_size);
643
- unpack_row_q4x4x2((block_q4_0 *) buf_rp, (const uint8_t *) buf_pd, t->ne[0]);
644
- memcpy(dst, buf_rp, row_size);
645
- }
646
-
647
- // 2. Process the final, potentially partial, row
648
- if (n_rem_bytes > 0) {
649
- const int64_t i = n_full_rows;
650
- const uint8_t * src = (const uint8_t *) t->data + (i * row_size);
651
- uint8_t * dst = (uint8_t *) data + (i * row_size);
652
-
653
- // We still need to read and unpack the entire source row because quantization is block-based.
654
- memcpy(buf_pd, src, row_size);
655
- unpack_row_q4x4x2((block_q4_0 *) buf_rp, (const uint8_t *) buf_pd, t->ne[0]);
656
-
657
- // But we only copy the remaining number of bytes to the destination.
658
- memcpy(dst, buf_rp, n_rem_bytes);
659
- }
660
-
661
- ggml_aligned_free(buf_pd, row_size_pd);
662
- ggml_aligned_free(buf_rp, row_size_rp);
663
- }
664
-
665
403
  static void unpack_q4_1_quants(uint8_t * qs, const block_q4_1 * x, unsigned int bi) {
666
404
  static const int qk = QK4_1;
667
405
 
@@ -683,603 +421,19 @@ static void pack_q4_1_quants(block_q4_1 * x, const uint8_t * qs, unsigned int bi
683
421
  }
684
422
  }
685
423
 
686
- static void repack_row_q4_1x4x2(uint8_t * y, const block_q4_1 * x, int64_t k) {
687
- static const int qk = QK_Q4_0x4x2;
688
- const int nb = (k + qk - 1) / qk; // number of blocks (padded)
689
- const int nloe = k % qk; // leftovers
690
-
691
- const int dblk_size = 8 * 4; // 8x (d, m) __fp16 = 32 bytes
692
- const int qblk_size = qk / 2; // int4 = 128 bytes
693
- const int qrow_size = k / 2; // int4 (not padded to blocks)
694
-
695
- uint8_t * y_q = y + 0; // quants first
696
- uint8_t * y_d = y + qrow_size; // then scales/offsets
697
-
698
- // Repack the quants
699
- for (int i = 0; i < nb; i++) {
700
- uint8_t qs[QK_Q4_0x4x2]; // unpacked quants
701
- unpack_q4_1_quants(qs, &x[i * 8 + 0], 0);
702
- unpack_q4_1_quants(qs, &x[i * 8 + 1], 1);
703
- unpack_q4_1_quants(qs, &x[i * 8 + 2], 2);
704
- unpack_q4_1_quants(qs, &x[i * 8 + 3], 3);
705
- unpack_q4_1_quants(qs, &x[i * 8 + 4], 4);
706
- unpack_q4_1_quants(qs, &x[i * 8 + 5], 5);
707
- unpack_q4_1_quants(qs, &x[i * 8 + 6], 6);
708
- unpack_q4_1_quants(qs, &x[i * 8 + 7], 7);
709
-
710
- bool partial = (nloe && i == nb-1);
711
-
712
- uint8_t * q = y_q + (i * qblk_size);
713
- for (int j = 0; j < qk / 2; j++) {
714
- q[j] = partial ? (qs[j*2+1] << 4) | qs[j*2+0] : (qs[j+128] << 4) | qs[j+000];
715
- }
716
- }
717
-
718
- // Repack the scales and offsets
719
- for (int i = 0; i < nb; i++) {
720
- ggml_half * d_m = (ggml_half *) (y_d + i * dblk_size);
721
- for (int j = 0; j < 8; j++) {
722
- d_m[j * 2 + 0] = x[i * 8 + j].d;
723
- d_m[j * 2 + 1] = x[i * 8 + j].m;
724
- }
725
- }
726
- }
727
-
728
- static void unpack_row_q4_1x4x2(block_q4_1 * x, const uint8_t * y, int64_t k) {
729
- static const int qk = QK_Q4_0x4x2;
730
- const int nb = (k + qk - 1) / qk; // number of blocks (padded)
731
- const int nloe = k % qk; // leftovers
732
-
733
- const int dblk_size = 8 * 4; // 8x (d, m) __fp16 = 32 bytes
734
- const int qblk_size = qk / 2; // int4 = 128 bytes
735
- const int qrow_size = k / 2; // int4 (not padded to blocks)
736
-
737
- const uint8_t * y_q = y + 0; // quants first
738
- const uint8_t * y_d = y + qrow_size; // then scales/offsets
739
-
740
- // Unpack the quants
741
- for (int i = 0; i < nb; i++) {
742
- uint8_t qs[QK_Q4_0x4x2];
743
- bool partial = (nloe && i == nb-1);
744
-
745
- const uint8_t * q = y_q + (i * qblk_size);
746
- for (int j = 0; j < qk / 2; j++) {
747
- if (partial) {
748
- qs[j*2+0] = q[j] & 0x0F;
749
- qs[j*2+1] = q[j] >> 4;
750
- } else {
751
- qs[j+000] = q[j] & 0x0F;
752
- qs[j+128] = q[j] >> 4;
753
- }
754
- }
755
-
756
- pack_q4_1_quants(&x[i * 8 + 0], qs, 0);
757
- pack_q4_1_quants(&x[i * 8 + 1], qs, 1);
758
- pack_q4_1_quants(&x[i * 8 + 2], qs, 2);
759
- pack_q4_1_quants(&x[i * 8 + 3], qs, 3);
760
- pack_q4_1_quants(&x[i * 8 + 4], qs, 4);
761
- pack_q4_1_quants(&x[i * 8 + 5], qs, 5);
762
- pack_q4_1_quants(&x[i * 8 + 6], qs, 6);
763
- pack_q4_1_quants(&x[i * 8 + 7], qs, 7);
764
- }
765
-
766
- // Unpack the scales and offsets
767
- for (int i = 0; i < nb; i++) {
768
- const ggml_half * d_m = (const ggml_half *) (y_d + i * dblk_size);
769
- for (int j = 0; j < 8; j++) {
770
- x[i * 8 + j].d = d_m[j * 2 + 0];
771
- x[i * 8 + j].m = d_m[j * 2 + 1];
772
- }
773
- }
774
- }
775
-
776
- static void init_row_q4_1x4x2(block_q4_1 * x, int64_t k) {
777
- static const int qk = QK_Q4_0x4x2;
778
- const int nb = (k + qk - 1) / qk; // number of blocks (padded)
779
-
780
- uint8_t qs[QK_Q4_0x4x2]; // unpacked quants
781
- memset(qs, 0, sizeof(qs));
782
-
783
- for (int i = 0; i < nb; i++) {
784
- pack_q4_1_quants(&x[i * 8 + 0], qs, 0);
785
- pack_q4_1_quants(&x[i * 8 + 1], qs, 1);
786
- pack_q4_1_quants(&x[i * 8 + 2], qs, 2);
787
- pack_q4_1_quants(&x[i * 8 + 3], qs, 3);
788
- pack_q4_1_quants(&x[i * 8 + 4], qs, 4);
789
- pack_q4_1_quants(&x[i * 8 + 5], qs, 5);
790
- pack_q4_1_quants(&x[i * 8 + 6], qs, 6);
791
- pack_q4_1_quants(&x[i * 8 + 7], qs, 7);
792
- }
793
-
794
- for (int i = 0; i < nb; i++) {
795
- for (int j = 0; j < 8; j++) {
796
- x[i * 8 + j].d = 0;
797
- x[i * 8 + j].m = 0;
798
- }
799
- }
800
- }
801
-
802
- static void repack_q4_1_q4x4x2(ggml_tensor * t, const void * data, size_t size) {
803
- int64_t nrows = ggml_nrows(t);
804
-
805
- size_t row_size = ggml_row_size(t->type, t->ne[0]);
806
- size_t row_size_pd = ggml_row_size(t->type, hex_round_up(t->ne[0], QK_Q4_0x4x2));
807
- size_t row_size_rp = row_size_pd; // scratch must hold one full padded tile (qblk_size/2 quants + scales)
808
-
809
- const size_t total_tensor_size = (size_t)nrows * row_size;
810
- const size_t n_bytes_to_copy = size < total_tensor_size ? size : total_tensor_size;
811
-
812
- const int64_t n_full_rows = n_bytes_to_copy / row_size;
813
- const size_t n_rem_bytes = n_bytes_to_copy % row_size;
814
-
815
- void * buf_pd = ggml_aligned_malloc(row_size_pd);
816
- GGML_ASSERT(buf_pd != NULL);
817
-
818
- void * buf_rp = ggml_aligned_malloc(row_size_rp);
819
- GGML_ASSERT(buf_rp != NULL);
820
-
821
- HEX_VERBOSE("ggml-hex: repack-q4_1-q4x4x2 %s : data %p size %zu dims %ldx%ld row-size %zu\n", t->name, data, size,
822
- t->ne[0], nrows, row_size);
823
-
824
- init_row_q4_1x4x2((block_q4_1 *) buf_pd, t->ne[0]);
825
-
826
- for (int64_t i = 0; i < n_full_rows; i++) {
827
- const uint8_t * src = (const uint8_t *) data + (i * row_size);
828
- uint8_t * dst = (uint8_t *) t->data + (i * row_size);
829
-
830
- memcpy(buf_pd, src, row_size);
831
- repack_row_q4_1x4x2((uint8_t *) buf_rp, (const block_q4_1 *) buf_pd, t->ne[0]);
832
- memcpy(dst, buf_rp, row_size);
833
- }
834
-
835
- if (n_rem_bytes > 0) {
836
- const int64_t i = n_full_rows;
837
- const uint8_t * src = (const uint8_t *) data + (i * row_size);
838
- uint8_t * dst = (uint8_t *) t->data + (i * row_size);
839
-
840
- init_row_q4_1x4x2((block_q4_1 *) buf_pd, t->ne[0]);
841
- memcpy(buf_pd, src, n_rem_bytes);
842
- repack_row_q4_1x4x2((uint8_t *) buf_rp, (const block_q4_1 *) buf_pd, t->ne[0]);
843
- memcpy(dst, buf_rp, n_rem_bytes);
844
- }
845
-
846
- ggml_aligned_free(buf_pd, row_size_pd);
847
- ggml_aligned_free(buf_rp, row_size_rp);
848
- }
849
-
850
- static void repack_q4x4x2_q4_1(void * data, const ggml_tensor * t, size_t size) {
851
- int64_t nrows = ggml_nrows(t);
852
-
853
- size_t row_size = ggml_row_size(t->type, t->ne[0]);
854
- size_t row_size_pd = ggml_row_size(t->type, hex_round_up(t->ne[0], QK_Q4_0x4x2));
855
- size_t row_size_rp = row_size_pd; // scratch must hold one full padded tile (qblk_size/2 quants + scales)
856
-
857
- const size_t total_tensor_size = (size_t)nrows * row_size;
858
- const size_t n_bytes_to_copy = size < total_tensor_size ? size : total_tensor_size;
859
-
860
- const int64_t n_full_rows = n_bytes_to_copy / row_size;
861
- const size_t n_rem_bytes = n_bytes_to_copy % row_size;
862
-
863
- void * buf_pd = ggml_aligned_malloc(row_size_pd);
864
- GGML_ASSERT(buf_pd != NULL);
865
-
866
- void * buf_rp = ggml_aligned_malloc(row_size_rp);
867
- GGML_ASSERT(buf_rp != NULL);
868
-
869
- HEX_VERBOSE("ggml-hex: repack-q4x4x2-q4_1 %s : data %p size %zu dims %ldx%ld row-size %zu\n", t->name, data, size,
870
- t->ne[0], nrows, row_size);
871
-
872
- memset(buf_rp, 0, row_size_rp); // clear-out padded buffer to make sure the tail is all zeros
873
-
874
- for (int64_t i = 0; i < n_full_rows; i++) {
875
- const uint8_t * src = (const uint8_t *) t->data + (i * row_size);
876
- uint8_t * dst = (uint8_t *) data + (i * row_size);
877
-
878
- memcpy(buf_rp, src, row_size);
879
- unpack_row_q4_1x4x2((block_q4_1 *) buf_pd, (const uint8_t *) buf_rp, t->ne[0]);
880
- memcpy(dst, buf_pd, row_size);
881
- }
882
-
883
- if (n_rem_bytes > 0) {
884
- const int64_t i = n_full_rows;
885
- const uint8_t * src = (const uint8_t *) t->data + (i * row_size);
886
- uint8_t * dst = (uint8_t *) data + (i * row_size);
887
-
888
- // We still need to read and unpack the entire source row because quantization is block-based.
889
- memcpy(buf_rp, src, row_size);
890
- unpack_row_q4_1x4x2((block_q4_1 *) buf_pd, (const uint8_t *) buf_rp, t->ne[0]);
891
- memcpy(dst, buf_pd, n_rem_bytes);
892
- }
893
-
894
- ggml_aligned_free(buf_pd, row_size_pd);
895
- ggml_aligned_free(buf_rp, row_size_rp);
896
- }
897
-
898
- // ======== Q8x4x2 ====================
899
- static void dump_block_q8_0(const block_q8_0 * b, int i) {
900
- HEX_VERBOSE("ggml-hex: repack q8_0 %d: %d %d %d %d ... %d %d %d %d : %.6f\n", i, b->qs[0], b->qs[1], b->qs[2],
901
- b->qs[3], b->qs[28], b->qs[29], b->qs[30], b->qs[31], GGML_FP16_TO_FP32(b->d));
902
- }
903
-
904
- static void dump_packed_block_q8x4x2(const uint8_t * v, unsigned int i, size_t k) {
905
- static const int qk = QK_Q8_0x4x2;
906
- const int dblk_size = 8 * 2; // 8x __fp16
907
- const int qblk_size = qk; // int8
908
- const int qrow_size = k; // int8 (not padded)
909
-
910
- const uint8_t * v_q = v + 0; // quants first
911
- const uint8_t * v_d = v + qrow_size; // then scales
912
-
913
- const uint8_t * q = v_q + i * qblk_size;
914
- const ggml_half * d = (const ggml_half *) (v_d + i * dblk_size);
915
-
916
- HEX_VERBOSE("ggml-hex: repack q8x4x2-%d: %d %d %d %d ... %d %d %d %d ... %d %d %d %d : %.6f %.6f %.6f %.6f\n", i,
917
- q[0], q[1], q[2], q[3], q[60], q[61], q[62], q[63], q[124], q[125], q[126], q[127],
918
- GGML_FP16_TO_FP32(d[0]), GGML_FP16_TO_FP32(d[1]), GGML_FP16_TO_FP32(d[2]), GGML_FP16_TO_FP32(d[3]));
919
-
920
- HEX_VERBOSE("ggml-hex: repack q8x4x2-%d: %d %d %d %d ... %d %d %d %d ... %d %d %d %d : %.6f %.6f %.6f %.6f\n",
921
- i + 1, q[128], q[129], q[130], q[131], q[192], q[193], q[194], q[195], q[252], q[253], q[254], q[255],
922
- GGML_FP16_TO_FP32(d[4]), GGML_FP16_TO_FP32(d[5]), GGML_FP16_TO_FP32(d[6]), GGML_FP16_TO_FP32(d[7]));
923
- }
924
-
925
- static void unpack_q8_0_quants(uint8_t * qs, const block_q8_0 * x, unsigned int bi) {
926
- static const int qk = QK8_0;
927
-
928
- for (unsigned int i = 0; i < qk; ++i) {
929
- qs[bi * qk + i] = x->qs[i];
930
- }
931
- }
932
-
933
- static void pack_q8_0_quants(block_q8_0 * x, const uint8_t * qs, unsigned int bi) {
934
- static const int qk = QK8_0;
935
-
936
- for (unsigned int i = 0; i < qk; ++i) {
937
- x->qs[i] = qs[bi * qk + i];
938
- }
939
- }
940
-
941
- static void repack_row_q8x4x2(uint8_t * y, const block_q8_0 * x, int64_t k) {
942
- static const int qk = QK_Q8_0x4x2;
943
- const int nb = (k + qk - 1) / qk; // number of blocks (padded)
944
-
945
- const int dblk_size = 8 * 2; // 8x __fp16
946
- const int qblk_size = qk; // int8
947
- const int qrow_size = k; // int8 (not padded to blocks)
948
-
949
- uint8_t * y_q = y + 0; // quants first
950
- uint8_t * y_d = y + qrow_size; // then scales
951
-
952
- if (opt_verbose > 2) {
953
- for (int i = 0; i < nb; i++) {
954
- dump_block_q8_0(&x[i * 8 + 0], 0);
955
- dump_block_q8_0(&x[i * 8 + 1], 1);
956
- dump_block_q8_0(&x[i * 8 + 2], 2);
957
- dump_block_q8_0(&x[i * 8 + 3], 3);
958
- dump_block_q8_0(&x[i * 8 + 4], 4);
959
- dump_block_q8_0(&x[i * 8 + 5], 5);
960
- dump_block_q8_0(&x[i * 8 + 6], 6);
961
- dump_block_q8_0(&x[i * 8 + 7], 7);
962
- }
963
- }
964
-
965
- // Repack the quants
966
- for (int i = 0; i < nb; i++) {
967
- uint8_t qs[QK_Q8_0x4x2]; // unpacked quants
968
-
969
- unpack_q8_0_quants(qs, &x[i * 8 + 0], 0);
970
- unpack_q8_0_quants(qs, &x[i * 8 + 1], 1);
971
- unpack_q8_0_quants(qs, &x[i * 8 + 2], 2);
972
- unpack_q8_0_quants(qs, &x[i * 8 + 3], 3);
973
- unpack_q8_0_quants(qs, &x[i * 8 + 4], 4);
974
- unpack_q8_0_quants(qs, &x[i * 8 + 5], 5);
975
- unpack_q8_0_quants(qs, &x[i * 8 + 6], 6);
976
- unpack_q8_0_quants(qs, &x[i * 8 + 7], 7);
977
-
978
- uint8_t * q = y_q + (i * qblk_size);
979
- for (int j = 0; j < qk; j++) {
980
- q[j] = qs[j];
981
- }
982
- }
983
-
984
- // Repack the scales
985
- // Note: Do not combine with the loop above. For tensor sizes not multiple of 256 (QK_Q4_0x4x2)
986
- // the last block is truncated and overridden by the scales.
987
- for (int i = 0; i < nb; i++) {
988
- // Repack the scales
989
- ggml_half * d = (ggml_half *) (y_d + i * dblk_size);
990
- d[0] = x[i * 8 + 0].d;
991
- d[1] = x[i * 8 + 1].d;
992
- d[2] = x[i * 8 + 2].d;
993
- d[3] = x[i * 8 + 3].d;
994
- d[4] = x[i * 8 + 4].d;
995
- d[5] = x[i * 8 + 5].d;
996
- d[6] = x[i * 8 + 6].d;
997
- d[7] = x[i * 8 + 7].d;
998
- }
999
-
1000
- if (opt_verbose > 2) {
1001
- for (int i = 0; i < nb; i++) {
1002
- dump_packed_block_q8x4x2(y, i, k);
1003
- }
1004
- }
1005
- }
1006
-
1007
- static void unpack_row_q8x4x2(block_q8_0 * x, const uint8_t * y, int64_t k) {
1008
- static const int qk = QK_Q8_0x4x2;
1009
- const int nb = (k + qk - 1) / qk; // number of blocks (padded)
1010
-
1011
- const int dblk_size = 8 * 2; // 8x __fp16
1012
- const int qblk_size = qk; // int8
1013
- const int qrow_size = k; // int8 (not padded to blocks)
1014
-
1015
- const uint8_t * y_q = y + 0; // quants first
1016
- const uint8_t * y_d = y + qrow_size; // then scales
1017
-
1018
- if (opt_verbose > 2) {
1019
- for (int i = 0; i < nb; i++) {
1020
- dump_packed_block_q8x4x2(y, i, k);
1021
- }
1022
- }
1023
-
1024
- // Unpack the quants
1025
- for (int i = 0; i < nb; i++) {
1026
- uint8_t qs[QK_Q4_0x4x2]; // unpacked quants
1027
-
1028
- const uint8_t * q = y_q + (i * qblk_size);
1029
- for (int j = 0; j < qk; j++) {
1030
- qs[j] = q[j];
1031
- }
1032
-
1033
- pack_q8_0_quants(&x[i * 8 + 0], qs, 0);
1034
- pack_q8_0_quants(&x[i * 8 + 1], qs, 1);
1035
- pack_q8_0_quants(&x[i * 8 + 2], qs, 2);
1036
- pack_q8_0_quants(&x[i * 8 + 3], qs, 3);
1037
- pack_q8_0_quants(&x[i * 8 + 4], qs, 4);
1038
- pack_q8_0_quants(&x[i * 8 + 5], qs, 5);
1039
- pack_q8_0_quants(&x[i * 8 + 6], qs, 6);
1040
- pack_q8_0_quants(&x[i * 8 + 7], qs, 7);
1041
- }
1042
-
1043
- // Repack the scales
1044
- // Note: Do not combine with the loop above. For tensor sizes not multiple of 256 (QK_Q4_0x4x2)
1045
- // the last block is truncated and overridden by the scales.
1046
- for (int i = 0; i < nb; i++) {
1047
- // Unpack the scales
1048
- const ggml_half * d = (const ggml_half *) (y_d + i * dblk_size);
1049
- x[i * 8 + 0].d = d[0];
1050
- x[i * 8 + 1].d = d[1];
1051
- x[i * 8 + 2].d = d[2];
1052
- x[i * 8 + 3].d = d[3];
1053
- x[i * 8 + 4].d = d[4];
1054
- x[i * 8 + 5].d = d[5];
1055
- x[i * 8 + 6].d = d[6];
1056
- x[i * 8 + 7].d = d[7];
1057
- }
1058
-
1059
- if (opt_verbose > 2) {
1060
- for (int i = 0; i < nb; i++) {
1061
- dump_block_q8_0(&x[i * 8 + 0], 0);
1062
- dump_block_q8_0(&x[i * 8 + 1], 1);
1063
- dump_block_q8_0(&x[i * 8 + 2], 2);
1064
- dump_block_q8_0(&x[i * 8 + 3], 3);
1065
- dump_block_q8_0(&x[i * 8 + 4], 4);
1066
- dump_block_q8_0(&x[i * 8 + 5], 5);
1067
- dump_block_q8_0(&x[i * 8 + 6], 6);
1068
- dump_block_q8_0(&x[i * 8 + 7], 7);
1069
- }
1070
- }
1071
- }
1072
-
1073
- static void init_row_q8x4x2(block_q8_0 * x, int64_t k) {
1074
- static const int qk = QK_Q8_0x4x2;
1075
- const int nb = (k + qk - 1) / qk; // number of blocks (padded)
1076
-
1077
- // Init the quants such that they unpack into zeros
1078
- uint8_t qs[QK_Q8_0x4x2]; // unpacked quants
1079
- memset(qs, 0, sizeof(qs));
1080
-
1081
- for (int i = 0; i < nb; i++) {
1082
- pack_q8_0_quants(&x[i * 8 + 0], qs, 0);
1083
- pack_q8_0_quants(&x[i * 8 + 1], qs, 1);
1084
- pack_q8_0_quants(&x[i * 8 + 2], qs, 2);
1085
- pack_q8_0_quants(&x[i * 8 + 3], qs, 3);
1086
- pack_q8_0_quants(&x[i * 8 + 4], qs, 4);
1087
- pack_q8_0_quants(&x[i * 8 + 5], qs, 5);
1088
- pack_q8_0_quants(&x[i * 8 + 6], qs, 6);
1089
- pack_q8_0_quants(&x[i * 8 + 7], qs, 7);
1090
- }
1091
-
1092
- // Init the scales
1093
- // Note: Do not combine with the loop above. For tensor sizes not multiple of 256 (QK_Q8_0x4x2)
1094
- // the last block is truncated and overridden by the scales.
1095
- for (int i = 0; i < nb; i++) {
1096
- // Unpack the scales
1097
- x[i * 8 + 0].d = 0;
1098
- x[i * 8 + 1].d = 0;
1099
- x[i * 8 + 2].d = 0;
1100
- x[i * 8 + 3].d = 0;
1101
- x[i * 8 + 4].d = 0;
1102
- x[i * 8 + 5].d = 0;
1103
- x[i * 8 + 6].d = 0;
1104
- x[i * 8 + 7].d = 0;
1105
- }
1106
- }
1107
-
1108
- // repack q8_0 data into q8x4x2 tensor
1109
- static void repack_q8_0_q8x4x2(ggml_tensor * t, const void * data, size_t size) {
1110
- int64_t nrows = ggml_nrows(t);
1111
-
1112
- size_t row_size = ggml_row_size(t->type, t->ne[0]);
1113
- size_t row_size_pd = ggml_row_size(t->type, hex_round_up(t->ne[0], QK_Q8_0x4x2)); // extra elements for the pad
1114
- size_t row_size_rp = row_size_pd; // scratch must hold one full padded tile (qblk_size quants + scales)
1115
-
1116
- // Ensure we don't try to read more data than is available in the source buffer 'data'
1117
- // or write more than the tensor can hold.
1118
- const size_t total_tensor_size = (size_t)nrows * row_size;
1119
- const size_t n_bytes_to_copy = size < total_tensor_size ? size : total_tensor_size;
1120
-
1121
- // Calculate how many full rows and how many remaining bytes we need to process.
1122
- const int64_t n_full_rows = n_bytes_to_copy / row_size;
1123
- const size_t n_rem_bytes = n_bytes_to_copy % row_size;
1124
-
1125
- void * buf_pd = ggml_aligned_malloc(row_size_pd);
1126
- GGML_ASSERT(buf_pd != NULL);
1127
-
1128
- void * buf_rp = ggml_aligned_malloc(row_size_rp);
1129
- GGML_ASSERT(buf_rp != NULL);
1130
-
1131
- HEX_VERBOSE("ggml-hex: repack-q8_0-q8x4x2 %s : data %p size %zu dims %ldx%ld row-size %zu\n", t->name, data, size,
1132
- t->ne[0], nrows, row_size);
1133
-
1134
- init_row_q8x4x2((block_q8_0 *) buf_pd, t->ne[0]); // init padded buffer to make sure the tail is all zeros
1135
-
1136
- // 1. Process all the full rows
1137
- for (int64_t i = 0; i < n_full_rows; i++) {
1138
- const uint8_t * src = (const uint8_t *) data + (i * row_size);
1139
- uint8_t * dst = (uint8_t *) t->data + (i * row_size);
1140
-
1141
- memcpy(buf_pd, src, row_size);
1142
- repack_row_q8x4x2((uint8_t *) buf_rp, (const block_q8_0 *) buf_pd, t->ne[0]);
1143
- memcpy(dst, buf_rp, row_size);
1144
- }
1145
-
1146
- // 2. Process the final, potentially partial, row
1147
- if (n_rem_bytes > 0) {
1148
- const int64_t i = n_full_rows;
1149
- const uint8_t * src = (const uint8_t *) data + (i * row_size);
1150
- uint8_t * dst = (uint8_t *) t->data + (i * row_size);
1151
-
1152
- // re-init the row because we are potentially copying a partial row
1153
- init_row_q8x4x2((block_q8_0 *) buf_pd, t->ne[0]);
1154
-
1155
- // Copy only the remaining bytes from the source.
1156
- memcpy(buf_pd, src, n_rem_bytes);
1157
-
1158
- // Repack the entire buffer
1159
- repack_row_q8x4x2((uint8_t *) buf_rp, (const block_q8_0 *) buf_pd, t->ne[0]);
1160
-
1161
- // Write only the corresponding remaining bytes to the destination tensor.
1162
- memcpy(dst, buf_rp, n_rem_bytes);
1163
- }
1164
-
1165
- ggml_aligned_free(buf_pd, row_size_pd);
1166
- ggml_aligned_free(buf_rp, row_size_rp);
1167
- }
1168
-
1169
- // repack q8x4x2 tensor into q8_0 data
1170
- static void repack_q8x4x2_q8_0(void * data, const ggml_tensor * t, size_t size) {
1171
- int64_t nrows = ggml_nrows(t);
1172
-
1173
- size_t row_size = ggml_row_size(t->type, t->ne[0]);
1174
- size_t row_size_pd = ggml_row_size(t->type, hex_round_up(t->ne[0], QK_Q8_0x4x2)); // extra elements for the pad
1175
- size_t row_size_rp = row_size_pd; // scratch must hold one full padded tile (qblk_size quants + scales)
1176
-
1177
- // Ensure we don't try to copy more data than the tensor actually contains.
1178
- const size_t total_tensor_size = (size_t)nrows * row_size;
1179
- const size_t n_bytes_to_copy = size < total_tensor_size ? size : total_tensor_size;
1180
-
1181
- // Calculate how many full rows and how many remaining bytes we need to process.
1182
- const int64_t n_full_rows = n_bytes_to_copy / row_size;
1183
- const size_t n_rem_bytes = n_bytes_to_copy % row_size;
1184
-
1185
- void * buf_pd = ggml_aligned_malloc(row_size_pd);
1186
- GGML_ASSERT(buf_pd != NULL);
1187
-
1188
- void * buf_rp = ggml_aligned_malloc(row_size_rp);
1189
- GGML_ASSERT(buf_rp != NULL);
1190
-
1191
- HEX_VERBOSE("ggml-hex: repack-q8x4x2-q8_0 %s : data %p size %zu dims %ldx%ld row-size %zu\n", t->name, data, size,
1192
- t->ne[0], nrows, row_size);
1193
-
1194
- memset(buf_pd, 0, row_size_pd); // clear-out padded buffer to make sure the tail is all zeros
1195
-
1196
- // 1. Process all the full rows
1197
- for (int64_t i = 0; i < n_full_rows; i++) {
1198
- const uint8_t * src = (const uint8_t *) t->data + (i * row_size);
1199
- uint8_t * dst = (uint8_t *) data + (i * row_size);
1200
-
1201
- memcpy(buf_pd, src, row_size);
1202
- unpack_row_q8x4x2((block_q8_0 *) buf_rp, (const uint8_t *) buf_pd, t->ne[0]);
1203
- memcpy(dst, buf_rp, row_size);
1204
- }
1205
-
1206
- // 2. Process the final, potentially partial, row
1207
- if (n_rem_bytes > 0) {
1208
- const int64_t i = n_full_rows;
1209
- const uint8_t * src = (const uint8_t *) t->data + (i * row_size);
1210
- uint8_t * dst = (uint8_t *) data + (i * row_size);
1211
-
1212
- // We still need to read and unpack the entire source row because quantization is block-based.
1213
- memcpy(buf_pd, src, row_size);
1214
- unpack_row_q8x4x2((block_q8_0 *) buf_rp, (const uint8_t *) buf_pd, t->ne[0]);
1215
-
1216
- // But we only copy the remaining number of bytes to the destination.
1217
- memcpy(dst, buf_rp, n_rem_bytes);
1218
- }
1219
-
1220
- ggml_aligned_free(buf_pd, row_size_pd);
1221
- ggml_aligned_free(buf_rp, row_size_rp);
1222
- }
1223
-
1224
- // ======== MXFP4x4x2 ====================
1225
- struct x2_mxfp4 {
1226
- int v[2];
1227
- };
1228
-
1229
- static x2_mxfp4 unpack_mxfp4(uint8_t v) {
1230
- x2_mxfp4 x;
1231
- x.v[0] = kvalues_mxfp4[(v & 0x0f)];
1232
- x.v[1] = kvalues_mxfp4[(v >> 4)];
1233
- return x;
1234
- }
1235
-
1236
- static void dump_block_mxfp4(const block_mxfp4 * b, int i) {
1237
- HEX_VERBOSE("ggml-hex: repack mxfp4 %d: %d %d %d %d ... %d %d %d %d : %.6f\n", i, unpack_mxfp4(b->qs[0]).v[0],
1238
- unpack_mxfp4(b->qs[1]).v[0], unpack_mxfp4(b->qs[2]).v[0], unpack_mxfp4(b->qs[3]).v[0],
1239
- unpack_mxfp4(b->qs[12]).v[1], unpack_mxfp4(b->qs[13]).v[1], unpack_mxfp4(b->qs[14]).v[1],
1240
- unpack_mxfp4(b->qs[15]).v[1], GGML_E8M0_TO_FP32_HALF(b->e));
1241
- }
1242
-
1243
- static void dump_packed_block_mxfp4x4x2(const uint8_t * v, unsigned int i, size_t k) {
1244
- static const int qk = QK_MXFP4x4x2;
1245
- const int eblk_size = 8 * 1; // 8x E8M0
1246
- const int qblk_size = qk / 2; // int4
1247
- const int qrow_size = k / 2; // int4 (not padded)
1248
-
1249
- const uint8_t * v_q = v + 0; // quants first
1250
- const uint8_t * v_e = v + qrow_size; // then scales
1251
-
1252
- const uint8_t * q = v_q + i * qblk_size;
1253
- const uint8_t * e = (const uint8_t *) (v_e + i * eblk_size);
1254
-
1255
- HEX_VERBOSE("ggml-hex: repack mxfp4x4x2-%d: %d %d %d %d ... %d %d %d %d ... %d %d %d %d : %.6f %.6f %.6f %.6f\n", i,
1256
- unpack_mxfp4(q[0]).v[0], unpack_mxfp4(q[1]).v[0], unpack_mxfp4(q[2]).v[0], unpack_mxfp4(q[3]).v[0],
1257
- unpack_mxfp4(q[60]).v[0], unpack_mxfp4(q[61]).v[0], unpack_mxfp4(q[62]).v[0], unpack_mxfp4(q[63]).v[0],
1258
- unpack_mxfp4(q[124]).v[0], unpack_mxfp4(q[125]).v[0], unpack_mxfp4(q[126]).v[0],
1259
- unpack_mxfp4(q[127]).v[0], GGML_E8M0_TO_FP32_HALF(e[0]), GGML_E8M0_TO_FP32_HALF(e[1]),
1260
- GGML_E8M0_TO_FP32_HALF(e[2]), GGML_E8M0_TO_FP32_HALF(e[3]));
1261
-
1262
- HEX_VERBOSE("ggml-hex: repack mxfp4x4x2-%d: %d %d %d %d ... %d %d %d %d ... %d %d %d %d : %.6f %.6f %.6f %.6f\n",
1263
- i + 1, unpack_mxfp4(q[0]).v[1], unpack_mxfp4(q[1]).v[1], unpack_mxfp4(q[2]).v[1],
1264
- unpack_mxfp4(q[3]).v[1], unpack_mxfp4(q[60]).v[1], unpack_mxfp4(q[61]).v[1], unpack_mxfp4(q[62]).v[1],
1265
- unpack_mxfp4(q[63]).v[1], unpack_mxfp4(q[124]).v[1], unpack_mxfp4(q[125]).v[1],
1266
- unpack_mxfp4(q[126]).v[1], unpack_mxfp4(q[127]).v[1], GGML_E8M0_TO_FP32_HALF(e[4]),
1267
- GGML_E8M0_TO_FP32_HALF(e[5]), GGML_E8M0_TO_FP32_HALF(e[6]), GGML_E8M0_TO_FP32_HALF(e[7]));
1268
- }
1269
-
1270
424
  static void unpack_mxfp4_quants(uint8_t * qs, const block_mxfp4 * x, unsigned int bi) {
1271
425
  static const int qk = QK_MXFP4;
1272
426
 
1273
427
  for (unsigned int i = 0; i < qk / 2; ++i) {
1274
- const uint8_t x0 = (x->qs[i] & 0x0F);
1275
- const uint8_t x1 = (x->qs[i] >> 4);
428
+ const int x0 = (x->qs[i] & 0x0F);
429
+ const int x1 = (x->qs[i] >> 4);
1276
430
  qs[bi * qk + i + 0] = x0;
1277
431
  qs[bi * qk + i + qk / 2] = x1;
1278
432
  }
1279
433
  }
1280
434
 
1281
435
  static void pack_mxfp4_quants(block_mxfp4 * x, const uint8_t * qs, unsigned int bi) {
1282
- static const int qk = QK4_0;
436
+ static const int qk = QK_MXFP4;
1283
437
 
1284
438
  for (unsigned int i = 0; i < qk / 2; ++i) {
1285
439
  const uint8_t x0 = qs[bi * qk + i + 0];
@@ -1288,299 +442,419 @@ static void pack_mxfp4_quants(block_mxfp4 * x, const uint8_t * qs, unsigned int
1288
442
  }
1289
443
  }
1290
444
 
1291
- static void repack_row_mxfp4x4x2(uint8_t * y, const block_mxfp4 * x, int64_t k) {
1292
- static const int qk = QK_MXFP4x4x2;
1293
- const int nb = (k + qk - 1) / qk; // number of blocks (padded)
1294
- const int nloe = k % qk; // leftovers
1295
-
1296
- const int eblk_size = 8 * 1; // 8x E8M0
1297
- const int qblk_size = qk / 2; // int4
1298
- const int qrow_size = k / 2; // int4 (not padded to blocks)
1299
-
1300
- uint8_t * y_q = y + 0; // quants first
1301
- uint8_t * y_e = y + qrow_size; // then scales
1302
-
1303
- if (opt_verbose > 2) {
1304
- for (int i = 0; i < nb; i++) {
1305
- dump_block_mxfp4(&x[i * 8 + 0], 0);
1306
- dump_block_mxfp4(&x[i * 8 + 1], 1);
1307
- dump_block_mxfp4(&x[i * 8 + 2], 2);
1308
- dump_block_mxfp4(&x[i * 8 + 3], 3);
1309
- dump_block_mxfp4(&x[i * 8 + 4], 4);
1310
- dump_block_mxfp4(&x[i * 8 + 5], 5);
1311
- dump_block_mxfp4(&x[i * 8 + 6], 6);
1312
- dump_block_mxfp4(&x[i * 8 + 7], 7);
445
+ // repack q4_0 data into q4_0_tiled tensor
446
+ static void repack_q4_0_tiled(ggml_tensor * t, const void * data, size_t size) {
447
+ const block_q4_0 * src_matrix = (const block_q4_0 *) data;
448
+ int64_t ne0 = t->ne[0];
449
+ int64_t ne1 = t->ne[1];
450
+ int64_t ne2 = t->ne[2];
451
+ int64_t ne3 = t->ne[3];
452
+ int64_t ne0_padded = hex_round_up(ne0, 32);
453
+ int64_t ne1_padded = hex_round_up(ne1, 32);
454
+
455
+ int n_col_tiles = ne1_padded / 32;
456
+ int n_k_tiles = ne0_padded / 32;
457
+ const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_Q4_0;
458
+ const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size;
459
+
460
+ for (int i3 = 0; i3 < ne3; i3++) {
461
+ for (int i2 = 0; i2 < ne2; i2++) {
462
+ const block_q4_0 * src_expert = src_matrix + (i3 * ne2 + i2) * (ne1 * (ne0 / 32));
463
+ uint8_t * matrix_dst = (uint8_t *) t->data + (i3 * ne2 + i2) * matrix_size;
464
+
465
+ for (int ct = 0; ct < n_col_tiles; ct++) {
466
+ for (int kt = 0; kt < n_k_tiles; kt++) {
467
+ uint8_t * tile_dst = matrix_dst + (ct * n_k_tiles + kt) * tile_size;
468
+
469
+ uint8_t tile_quants[32][32];
470
+ for (int row = 0; row < 32; row++) {
471
+ int64_t r = ct * 32 + row;
472
+ if (r < ne1 && kt < ne0 / 32) {
473
+ unpack_q4_0_quants(tile_quants[row], &src_expert[r * (ne0 / 32) + kt], 0);
474
+ } else {
475
+ memset(tile_quants[row], 8, 32);
476
+ }
477
+ }
478
+
479
+ for (int cp = 0; cp < 16; cp++) {
480
+ for (int row = 0; row < 32; row++) {
481
+ tile_dst[cp * 32 + row] = (tile_quants[row][2 * cp + 1] << 4) | tile_quants[row][2 * cp];
482
+ }
483
+ }
484
+
485
+ ggml_half * scale_dst = (ggml_half *)(tile_dst + 512);
486
+ for (int row = 0; row < 32; row++) {
487
+ int64_t r = ct * 32 + row;
488
+ scale_dst[row] = (r < ne1 && kt < ne0 / 32) ? src_expert[r * (ne0 / 32) + kt].d : 0;
489
+ }
490
+ }
491
+ }
1313
492
  }
1314
493
  }
494
+ }
1315
495
 
1316
- // Repack the quants
1317
- for (int i = 0; i < nb; i++) {
1318
- uint8_t qs[QK_MXFP4x4x2]; // unpacked quants
1319
-
1320
- unpack_mxfp4_quants(qs, &x[i * 8 + 0], 0);
1321
- unpack_mxfp4_quants(qs, &x[i * 8 + 1], 1);
1322
- unpack_mxfp4_quants(qs, &x[i * 8 + 2], 2);
1323
- unpack_mxfp4_quants(qs, &x[i * 8 + 3], 3);
1324
- unpack_mxfp4_quants(qs, &x[i * 8 + 4], 4);
1325
- unpack_mxfp4_quants(qs, &x[i * 8 + 5], 5);
1326
- unpack_mxfp4_quants(qs, &x[i * 8 + 6], 6);
1327
- unpack_mxfp4_quants(qs, &x[i * 8 + 7], 7);
1328
-
1329
- bool partial = (nloe && i == nb-1);
1330
-
1331
- uint8_t * q = y_q + (i * qblk_size);
1332
- for (int j = 0; j < qk / 2; j++) {
1333
- q[j] = partial ? (qs[j*2+1] << 4) | qs[j*2+0] : (qs[j+128] << 4) | qs[j+000];
496
+ // repack q4_0_tiled tensor into q4_0 data
497
+ static void repack_tiled_q4_0(void * data, const ggml_tensor * t, size_t size) {
498
+ block_q4_0 * dst_matrix = (block_q4_0 *) data;
499
+ int64_t ne0 = t->ne[0];
500
+ int64_t ne1 = t->ne[1];
501
+ int64_t ne2 = t->ne[2];
502
+ int64_t ne3 = t->ne[3];
503
+ int64_t ne0_padded = hex_round_up(ne0, 32);
504
+ int64_t ne1_padded = hex_round_up(ne1, 32);
505
+
506
+ int n_col_tiles = ne1_padded / 32;
507
+ int n_k_tiles = ne0_padded / 32;
508
+ const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_Q4_0;
509
+ const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size;
510
+
511
+ for (int i3 = 0; i3 < ne3; i3++) {
512
+ for (int i2 = 0; i2 < ne2; i2++) {
513
+ block_q4_0 * dst_expert = dst_matrix + (i3 * ne2 + i2) * (ne1 * (ne0 / 32));
514
+ const uint8_t * matrix_src = (const uint8_t *) t->data + (i3 * ne2 + i2) * matrix_size;
515
+
516
+ for (int ct = 0; ct < n_col_tiles; ct++) {
517
+ for (int kt = 0; kt < n_k_tiles; kt++) {
518
+ const uint8_t * tile_src = matrix_src + (ct * n_k_tiles + kt) * tile_size;
519
+
520
+ uint8_t tile_quants[32][32];
521
+ for (int cp = 0; cp < 16; cp++) {
522
+ for (int row = 0; row < 32; row++) {
523
+ uint8_t val = tile_src[cp * 32 + row];
524
+ tile_quants[row][2 * cp + 0] = val & 0x0F;
525
+ tile_quants[row][2 * cp + 1] = val >> 4;
526
+ }
527
+ }
528
+
529
+ for (int row = 0; row < 32; row++) {
530
+ int64_t r = ct * 32 + row;
531
+ if (r < ne1 && kt < ne0 / 32) {
532
+ pack_q4_0_quants(&dst_expert[r * (ne0 / 32) + kt], tile_quants[row], 0);
533
+ }
534
+ }
535
+
536
+ const ggml_half * scale_src = (const ggml_half *)(tile_src + 512);
537
+ for (int row = 0; row < 32; row++) {
538
+ int64_t r = ct * 32 + row;
539
+ if (r < ne1 && kt < ne0 / 32) {
540
+ dst_expert[r * (ne0 / 32) + kt].d = scale_src[row];
541
+ }
542
+ }
543
+ }
544
+ }
1334
545
  }
1335
546
  }
547
+ }
1336
548
 
1337
- // Repack the scales
1338
- // Note: Do not combine with the loop above. For tensor sizes not multiple of 256 (QK_MXFP4x4x2)
1339
- // the last block is truncated and overridden by the scales.
1340
- for (int i = 0; i < nb; i++) {
1341
- // Repack the scales
1342
- uint8_t * e = (uint8_t *) (y_e + i * eblk_size);
1343
- e[0] = x[i * 8 + 0].e;
1344
- e[1] = x[i * 8 + 1].e;
1345
- e[2] = x[i * 8 + 2].e;
1346
- e[3] = x[i * 8 + 3].e;
1347
- e[4] = x[i * 8 + 4].e;
1348
- e[5] = x[i * 8 + 5].e;
1349
- e[6] = x[i * 8 + 6].e;
1350
- e[7] = x[i * 8 + 7].e;
1351
- }
1352
-
1353
- if (opt_verbose > 2) {
1354
- for (int i = 0; i < nb; i++) {
1355
- dump_packed_block_mxfp4x4x2(y, i, k);
549
+ // repack q4_1 data into q4_1_tiled tensor
550
+ static void repack_q4_1_tiled(ggml_tensor * t, const void * data, size_t size) {
551
+ const block_q4_1 * src_matrix = (const block_q4_1 *) data;
552
+ int64_t ne0 = t->ne[0];
553
+ int64_t ne1 = t->ne[1];
554
+ int64_t ne2 = t->ne[2];
555
+ int64_t ne3 = t->ne[3];
556
+ int64_t ne0_padded = hex_round_up(ne0, 32);
557
+ int64_t ne1_padded = hex_round_up(ne1, 32);
558
+
559
+ int n_col_tiles = ne1_padded / 32;
560
+ int n_k_tiles = ne0_padded / 32;
561
+ const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_Q4_1;
562
+ const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size;
563
+
564
+ for (int i3 = 0; i3 < ne3; i3++) {
565
+ for (int i2 = 0; i2 < ne2; i2++) {
566
+ const block_q4_1 * src_expert = src_matrix + (i3 * ne2 + i2) * (ne1 * (ne0 / 32));
567
+ uint8_t * matrix_dst = (uint8_t *) t->data + (i3 * ne2 + i2) * matrix_size;
568
+
569
+ for (int ct = 0; ct < n_col_tiles; ct++) {
570
+ for (int kt = 0; kt < n_k_tiles; kt++) {
571
+ uint8_t * tile_dst = matrix_dst + (ct * n_k_tiles + kt) * tile_size;
572
+
573
+ uint8_t tile_quants[32][32];
574
+ for (int row = 0; row < 32; row++) {
575
+ int64_t r = ct * 32 + row;
576
+ if (r < ne1 && kt < ne0 / 32) {
577
+ unpack_q4_1_quants(tile_quants[row], &src_expert[r * (ne0 / 32) + kt], 0);
578
+ } else {
579
+ memset(tile_quants[row], 0, 32);
580
+ }
581
+ }
582
+
583
+ for (int cp = 0; cp < 16; cp++) {
584
+ for (int row = 0; row < 32; row++) {
585
+ tile_dst[cp * 32 + row] = (tile_quants[row][2 * cp + 1] << 4) | tile_quants[row][2 * cp];
586
+ }
587
+ }
588
+
589
+ ggml_half * scale_dst = (ggml_half *)(tile_dst + 512);
590
+ for (int row = 0; row < 32; row++) {
591
+ int64_t r = ct * 32 + row;
592
+ if (r < ne1 && kt < ne0 / 32) {
593
+ scale_dst[2 * row + 0] = src_expert[r * (ne0 / 32) + kt].d;
594
+ scale_dst[2 * row + 1] = src_expert[r * (ne0 / 32) + kt].m;
595
+ } else {
596
+ scale_dst[2 * row + 0] = 0;
597
+ scale_dst[2 * row + 1] = 0;
598
+ }
599
+ }
600
+ }
601
+ }
1356
602
  }
1357
603
  }
1358
604
  }
1359
605
 
1360
- static void unpack_row_mxfp4x4x2(block_mxfp4 * x, const uint8_t * y, int64_t k) {
1361
- static const int qk = QK_MXFP4x4x2;
1362
- const int nb = (k + qk - 1) / qk; // number of blocks (padded)
1363
- const int nloe = k % qk; // leftovers
1364
-
1365
- const int eblk_size = 8 * 1; // 8x E8M0
1366
- const int qblk_size = qk / 2; // int4
1367
- const int qrow_size = k / 2; // int4 (not padded to blocks)
1368
-
1369
- const uint8_t * y_q = y + 0; // quants first
1370
- const uint8_t * y_e = y + qrow_size; // then scales
1371
-
1372
- if (opt_verbose > 2) {
1373
- for (int i = 0; i < nb; i++) {
1374
- dump_packed_block_mxfp4x4x2(y, i, k);
606
+ // repack q4_1_tiled tensor into q4_1 data
607
+ static void repack_tiled_q4_1(void * data, const ggml_tensor * t, size_t size) {
608
+ block_q4_1 * dst_matrix = (block_q4_1 *) data;
609
+ int64_t ne0 = t->ne[0];
610
+ int64_t ne1 = t->ne[1];
611
+ int64_t ne2 = t->ne[2];
612
+ int64_t ne3 = t->ne[3];
613
+ int64_t ne0_padded = hex_round_up(ne0, 32);
614
+ int64_t ne1_padded = hex_round_up(ne1, 32);
615
+
616
+ int n_col_tiles = ne1_padded / 32;
617
+ int n_k_tiles = ne0_padded / 32;
618
+ const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_Q4_1;
619
+ const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size;
620
+
621
+ for (int i3 = 0; i3 < ne3; i3++) {
622
+ for (int i2 = 0; i2 < ne2; i2++) {
623
+ block_q4_1 * dst_expert = dst_matrix + (i3 * ne2 + i2) * (ne1 * (ne0 / 32));
624
+ const uint8_t * matrix_src = (const uint8_t *) t->data + (i3 * ne2 + i2) * matrix_size;
625
+
626
+ for (int ct = 0; ct < n_col_tiles; ct++) {
627
+ for (int kt = 0; kt < n_k_tiles; kt++) {
628
+ const uint8_t * tile_src = matrix_src + (ct * n_k_tiles + kt) * tile_size;
629
+
630
+ uint8_t tile_quants[32][32];
631
+ for (int cp = 0; cp < 16; cp++) {
632
+ for (int row = 0; row < 32; row++) {
633
+ uint8_t val = tile_src[cp * 32 + row];
634
+ tile_quants[row][2 * cp + 0] = val & 0x0F;
635
+ tile_quants[row][2 * cp + 1] = val >> 4;
636
+ }
637
+ }
638
+
639
+ for (int row = 0; row < 32; row++) {
640
+ int64_t r = ct * 32 + row;
641
+ if (r < ne1 && kt < ne0 / 32) {
642
+ pack_q4_1_quants(&dst_expert[r * (ne0 / 32) + kt], tile_quants[row], 0);
643
+ }
644
+ }
645
+
646
+ const ggml_half * scale_src = (const ggml_half *)(tile_src + 512);
647
+ for (int row = 0; row < 32; row++) {
648
+ int64_t r = ct * 32 + row;
649
+ if (r < ne1 && kt < ne0 / 32) {
650
+ dst_expert[r * (ne0 / 32) + kt].d = scale_src[2 * row];
651
+ dst_expert[r * (ne0 / 32) + kt].m = scale_src[2 * row + 1];
652
+ }
653
+ }
654
+ }
655
+ }
1375
656
  }
1376
657
  }
658
+ }
1377
659
 
1378
- // Unpack the quants
1379
- for (int i = 0; i < nb; i++) {
1380
- uint8_t qs[QK_MXFP4x4x2]; // unpacked quants
1381
-
1382
- bool partial = (nloe && i == nb-1);
1383
-
1384
- const uint8_t * q = y_q + (i * qblk_size);
1385
- for (int j = 0; j < qk / 2; j++) {
1386
- if (partial) {
1387
- qs[j*2+0] = q[j] & 0xf;
1388
- qs[j*2+1] = q[j] >> 4;
1389
- } else {
1390
- qs[j+000] = q[j] & 0xf;
1391
- qs[j+128] = q[j] >> 4;
660
+ // repack q8_0 data into q8_0_tiled tensor
661
+ static void repack_q8_0_tiled(ggml_tensor * t, const void * data, size_t size) {
662
+ const block_q8_0 * src_matrix = (const block_q8_0 *) data;
663
+ int64_t ne0 = t->ne[0];
664
+ int64_t ne1 = t->ne[1];
665
+ int64_t ne2 = t->ne[2];
666
+ int64_t ne3 = t->ne[3];
667
+ int64_t ne0_padded = hex_round_up(ne0, 32);
668
+ int64_t ne1_padded = hex_round_up(ne1, 32);
669
+
670
+ int n_col_tiles = ne1_padded / 32;
671
+ int n_k_tiles = ne0_padded / 32;
672
+ const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_Q8_0;
673
+ const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size;
674
+
675
+ for (int i3 = 0; i3 < ne3; i3++) {
676
+ for (int i2 = 0; i2 < ne2; i2++) {
677
+ const block_q8_0 * src_expert = src_matrix + (i3 * ne2 + i2) * (ne1 * (ne0 / 32));
678
+ uint8_t * matrix_dst = (uint8_t *) t->data + (i3 * ne2 + i2) * matrix_size;
679
+
680
+ for (int ct = 0; ct < n_col_tiles; ct++) {
681
+ for (int kt = 0; kt < n_k_tiles; kt++) {
682
+ uint8_t * tile_dst = matrix_dst + (ct * n_k_tiles + kt) * tile_size;
683
+
684
+ for (int cp = 0; cp < 16; cp++) {
685
+ int col0 = cp * 2;
686
+ int col1 = col0 + 1;
687
+ for (int row = 0; row < 32; row++) {
688
+ int64_t r = ct * 32 + row;
689
+ const block_q8_0 * b = (r < ne1 && kt < ne0 / 32) ? &src_expert[r * (ne0 / 32) + kt] : NULL;
690
+ tile_dst[cp * 64 + 2 * row + 0] = b ? b->qs[col0] : 0;
691
+ tile_dst[cp * 64 + 2 * row + 1] = b ? b->qs[col1] : 0;
692
+ }
693
+ }
694
+
695
+ ggml_half * scale_dst = (ggml_half *)(tile_dst + 1024);
696
+ for (int row = 0; row < 32; row++) {
697
+ int64_t r = ct * 32 + row;
698
+ scale_dst[row] = (r < ne1 && kt < ne0 / 32) ? src_expert[r * (ne0 / 32) + kt].d : 0;
699
+ }
700
+ }
1392
701
  }
1393
702
  }
1394
-
1395
- pack_mxfp4_quants(&x[i * 8 + 0], qs, 0);
1396
- pack_mxfp4_quants(&x[i * 8 + 1], qs, 1);
1397
- pack_mxfp4_quants(&x[i * 8 + 2], qs, 2);
1398
- pack_mxfp4_quants(&x[i * 8 + 3], qs, 3);
1399
- pack_mxfp4_quants(&x[i * 8 + 4], qs, 4);
1400
- pack_mxfp4_quants(&x[i * 8 + 5], qs, 5);
1401
- pack_mxfp4_quants(&x[i * 8 + 6], qs, 6);
1402
- pack_mxfp4_quants(&x[i * 8 + 7], qs, 7);
1403
- }
1404
-
1405
- // Repack the scales
1406
- // Note: Do not combine with the loop above. For tensor sizes not multiple of 256 (QK_MXFP4_0x4x2)
1407
- // the last block is truncated and overridden by the scales.
1408
- for (int i = 0; i < nb; i++) {
1409
- // Unpack the scales
1410
- const uint8_t * e = (const uint8_t *) (y_e + i * eblk_size);
1411
- x[i * 8 + 0].e = e[0];
1412
- x[i * 8 + 1].e = e[1];
1413
- x[i * 8 + 2].e = e[2];
1414
- x[i * 8 + 3].e = e[3];
1415
- x[i * 8 + 4].e = e[4];
1416
- x[i * 8 + 5].e = e[5];
1417
- x[i * 8 + 6].e = e[6];
1418
- x[i * 8 + 7].e = e[7];
1419
- }
1420
-
1421
- if (opt_verbose > 2) {
1422
- for (int i = 0; i < nb; i++) {
1423
- dump_block_mxfp4(&x[i * 8 + 0], 0);
1424
- dump_block_mxfp4(&x[i * 8 + 1], 1);
1425
- dump_block_mxfp4(&x[i * 8 + 2], 2);
1426
- dump_block_mxfp4(&x[i * 8 + 3], 3);
1427
- dump_block_mxfp4(&x[i * 8 + 4], 4);
1428
- dump_block_mxfp4(&x[i * 8 + 5], 5);
1429
- dump_block_mxfp4(&x[i * 8 + 6], 6);
1430
- dump_block_mxfp4(&x[i * 8 + 7], 7);
1431
- }
1432
703
  }
1433
704
  }
1434
705
 
1435
- static void init_row_mxfp4x4x2(block_mxfp4 * x, int64_t k) {
1436
- static const int qk = QK_MXFP4x4x2;
1437
- const int nb = (k + qk - 1) / qk; // number of blocks (padded)
1438
-
1439
- // Init the quants such that they unpack into zeros
1440
- uint8_t qs[QK_MXFP4x4x2]; // unpacked quants
1441
- memset(qs, 0, sizeof(qs));
1442
-
1443
- for (int i = 0; i < nb; i++) {
1444
- pack_mxfp4_quants(&x[i * 8 + 0], qs, 0);
1445
- pack_mxfp4_quants(&x[i * 8 + 1], qs, 1);
1446
- pack_mxfp4_quants(&x[i * 8 + 2], qs, 2);
1447
- pack_mxfp4_quants(&x[i * 8 + 3], qs, 3);
1448
- pack_mxfp4_quants(&x[i * 8 + 4], qs, 4);
1449
- pack_mxfp4_quants(&x[i * 8 + 5], qs, 5);
1450
- pack_mxfp4_quants(&x[i * 8 + 6], qs, 6);
1451
- pack_mxfp4_quants(&x[i * 8 + 7], qs, 7);
1452
- }
1453
-
1454
- // Init the scales
1455
- // Note: Do not combine with the loop above. For tensor sizes not multiple of 256 (QK_MXFP4x4x2)
1456
- // the last block is truncated and overridden by the scales.
1457
- for (int i = 0; i < nb; i++) {
1458
- // Unpack the scales
1459
- x[i * 8 + 0].e = 0;
1460
- x[i * 8 + 1].e = 0;
1461
- x[i * 8 + 2].e = 0;
1462
- x[i * 8 + 3].e = 0;
1463
- x[i * 8 + 4].e = 0;
1464
- x[i * 8 + 5].e = 0;
1465
- x[i * 8 + 6].e = 0;
1466
- x[i * 8 + 7].e = 0;
706
+ // repack q8_0_tiled tensor into q8_0 data
707
+ static void repack_tiled_q8_0(void * data, const ggml_tensor * t, size_t size) {
708
+ block_q8_0 * dst_matrix = (block_q8_0 *) data;
709
+ int64_t ne0 = t->ne[0];
710
+ int64_t ne1 = t->ne[1];
711
+ int64_t ne2 = t->ne[2];
712
+ int64_t ne3 = t->ne[3];
713
+ int64_t ne0_padded = hex_round_up(ne0, 32);
714
+ int64_t ne1_padded = hex_round_up(ne1, 32);
715
+
716
+ int n_col_tiles = ne1_padded / 32;
717
+ int n_k_tiles = ne0_padded / 32;
718
+ const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_Q8_0;
719
+ const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size;
720
+
721
+ for (int i3 = 0; i3 < ne3; i3++) {
722
+ for (int i2 = 0; i2 < ne2; i2++) {
723
+ block_q8_0 * dst_expert = dst_matrix + (i3 * ne2 + i2) * (ne1 * (ne0 / 32));
724
+ const uint8_t * matrix_src = (const uint8_t *) t->data + (i3 * ne2 + i2) * matrix_size;
725
+
726
+ for (int ct = 0; ct < n_col_tiles; ct++) {
727
+ for (int kt = 0; kt < n_k_tiles; kt++) {
728
+ const uint8_t * tile_src = matrix_src + (ct * n_k_tiles + kt) * tile_size;
729
+
730
+ for (int cp = 0; cp < 16; cp++) {
731
+ int col0 = cp * 2;
732
+ int col1 = col0 + 1;
733
+ for (int row = 0; row < 32; row++) {
734
+ int64_t r = ct * 32 + row;
735
+ if (r < ne1 && kt < ne0 / 32) {
736
+ block_q8_0 & b = dst_expert[r * (ne0 / 32) + kt];
737
+ b.qs[col0] = tile_src[cp * 64 + 2 * row + 0];
738
+ b.qs[col1] = tile_src[cp * 64 + 2 * row + 1];
739
+ }
740
+ }
741
+ }
742
+
743
+ const ggml_half * scale_src = (const ggml_half *)(tile_src + 1024);
744
+ for (int row = 0; row < 32; row++) {
745
+ int64_t r = ct * 32 + row;
746
+ if (r < ne1 && kt < ne0 / 32) {
747
+ dst_expert[r * (ne0 / 32) + kt].d = scale_src[row];
748
+ }
749
+ }
750
+ }
751
+ }
752
+ }
1467
753
  }
1468
754
  }
1469
755
 
1470
- // repack mxfp4 data into mxfp4x4x2 tensor
1471
- static void repack_mxfp4_mxfp4x4x2(ggml_tensor * t, const void * data, size_t size) {
1472
- int64_t nrows = ggml_nrows(t);
1473
-
1474
- size_t row_size = ggml_row_size(t->type, t->ne[0]);
1475
- size_t row_size_pd = ggml_row_size(t->type, hex_round_up(t->ne[0], QK_MXFP4x4x2)); // extra elements for the pad
1476
- size_t row_size_rp = row_size_pd; // scratch must hold one full padded tile (qblk_size/2 quants + scales)
1477
-
1478
- // Ensure we don't try to read more data than is available in the source buffer 'data'
1479
- // or write more than the tensor can hold.
1480
- const size_t total_tensor_size = (size_t)nrows * row_size;
1481
- const size_t n_bytes_to_copy = size < total_tensor_size ? size : total_tensor_size;
1482
-
1483
- // Calculate how many full rows and how many remaining bytes we need to process.
1484
- const int64_t n_full_rows = n_bytes_to_copy / row_size;
1485
- const size_t n_rem_bytes = n_bytes_to_copy % row_size;
1486
-
1487
- void * buf_pd = ggml_aligned_malloc(row_size_pd);
1488
- GGML_ASSERT(buf_pd != NULL);
1489
-
1490
- void * buf_rp = ggml_aligned_malloc(row_size_rp);
1491
- GGML_ASSERT(buf_rp != NULL);
1492
-
1493
- HEX_VERBOSE("ggml-hex: repack-mxfp4-mxfp4x4x2 %s : data %p size %zu dims %ldx%ld row-size %zu\n", t->name, data,
1494
- size, t->ne[0], nrows, row_size);
1495
-
1496
- init_row_mxfp4x4x2((block_mxfp4 *) buf_pd, t->ne[0]); // init padded buffer to make sure the tail is all zeros
1497
-
1498
- // 1. Process all the full rows
1499
- for (int64_t i = 0; i < n_full_rows; i++) {
1500
- const uint8_t * src = (const uint8_t *) data + (i * row_size);
1501
- uint8_t * dst = (uint8_t *) t->data + (i * row_size);
1502
-
1503
- memcpy(buf_pd, src, row_size);
1504
- repack_row_mxfp4x4x2((uint8_t *) buf_rp, (const block_mxfp4 *) buf_pd, t->ne[0]);
1505
- memcpy(dst, buf_rp, row_size);
1506
- }
1507
-
1508
- // 2. Process the final, potentially partial, row
1509
- if (n_rem_bytes > 0) {
1510
- const int64_t i = n_full_rows;
1511
- const uint8_t * src = (const uint8_t *) data + (i * row_size);
1512
- uint8_t * dst = (uint8_t *) t->data + (i * row_size);
1513
-
1514
- // re-init the row because we are potentially copying a partial row
1515
- init_row_mxfp4x4x2((block_mxfp4 *) buf_pd, t->ne[0]);
1516
-
1517
- // Copy only the remaining bytes from the source.
1518
- memcpy(buf_pd, src, n_rem_bytes);
1519
-
1520
- // Repack the entire buffer (partial data + zero padding).
1521
- repack_row_mxfp4x4x2((uint8_t *) buf_rp, (const block_mxfp4 *) buf_pd, t->ne[0]);
1522
-
1523
- // Write only the corresponding remaining bytes to the destination tensor.
1524
- memcpy(dst, buf_rp, n_rem_bytes);
756
+ // repack mxfp4 data into mxfp4_tiled tensor
757
+ static void repack_mxfp4_tiled(ggml_tensor * t, const void * data, size_t size) {
758
+ const block_mxfp4 * src_matrix = (const block_mxfp4 *) data;
759
+ int64_t ne0 = t->ne[0];
760
+ int64_t ne1 = t->ne[1];
761
+ int64_t ne2 = t->ne[2];
762
+ int64_t ne3 = t->ne[3];
763
+ int64_t ne0_padded = hex_round_up(ne0, 32);
764
+ int64_t ne1_padded = hex_round_up(ne1, 32);
765
+
766
+ int n_col_tiles = ne1_padded / 32;
767
+ int n_k_tiles = ne0_padded / 32;
768
+ const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_MXFP4;
769
+ const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size;
770
+
771
+ for (int i3 = 0; i3 < ne3; i3++) {
772
+ for (int i2 = 0; i2 < ne2; i2++) {
773
+ const block_mxfp4 * src_expert = src_matrix + (i3 * ne2 + i2) * (ne1 * (ne0 / 32));
774
+ uint8_t * matrix_dst = (uint8_t *) t->data + (i3 * ne2 + i2) * matrix_size;
775
+
776
+ for (int ct = 0; ct < n_col_tiles; ct++) {
777
+ for (int kt = 0; kt < n_k_tiles; kt++) {
778
+ uint8_t * tile_dst = matrix_dst + (ct * n_k_tiles + kt) * tile_size;
779
+
780
+ uint8_t tile_quants[32][32];
781
+ for (int row = 0; row < 32; row++) {
782
+ int64_t r = ct * 32 + row;
783
+ if (r < ne1 && kt < ne0 / 32) {
784
+ unpack_mxfp4_quants(tile_quants[row], &src_expert[r * (ne0 / 32) + kt], 0);
785
+ } else {
786
+ memset(tile_quants[row], 0, 32);
787
+ }
788
+ }
789
+
790
+ for (int cp = 0; cp < 16; cp++) {
791
+ for (int row = 0; row < 32; row++) {
792
+ tile_dst[cp * 32 + row] = (tile_quants[row][2 * cp + 1] << 4) | tile_quants[row][2 * cp];
793
+ }
794
+ }
795
+
796
+ uint8_t * scale_dst = tile_dst + 512;
797
+ for (int row = 0; row < 32; row++) {
798
+ int64_t r = ct * 32 + row;
799
+ scale_dst[row] = (r < ne1 && kt < ne0 / 32) ? src_expert[r * (ne0 / 32) + kt].e : 0;
800
+ }
801
+ }
802
+ }
803
+ }
1525
804
  }
1526
-
1527
- ggml_aligned_free(buf_pd, row_size_pd);
1528
- ggml_aligned_free(buf_rp, row_size_rp);
1529
805
  }
1530
806
 
1531
- // repack mxfp4x4x2 tensor into mxfp4 data
1532
- static void repack_mxfp4x4x2_mxfp4(void * data, const ggml_tensor * t, size_t size) {
1533
- int64_t nrows = ggml_nrows(t);
1534
-
1535
- size_t row_size = ggml_row_size(t->type, t->ne[0]);
1536
- size_t row_size_pd = ggml_row_size(t->type, hex_round_up(t->ne[0], QK_MXFP4x4x2)); // extra elements for the pad
1537
- size_t row_size_rp = row_size_pd; // scratch must hold one full padded tile (qblk_size/2 quants + scales)
1538
-
1539
- // Ensure we don't try to copy more data than the tensor actually contains.
1540
- const size_t total_tensor_size = (size_t)nrows * row_size;
1541
- const size_t n_bytes_to_copy = size < total_tensor_size ? size : total_tensor_size;
1542
-
1543
- // Calculate how many full rows and how many remaining bytes we need to process.
1544
- const int64_t n_full_rows = n_bytes_to_copy / row_size;
1545
- const size_t n_rem_bytes = n_bytes_to_copy % row_size;
1546
-
1547
- void * buf_pd = ggml_aligned_malloc(row_size_pd);
1548
- GGML_ASSERT(buf_pd != NULL);
1549
-
1550
- void * buf_rp = ggml_aligned_malloc(row_size_rp);
1551
- GGML_ASSERT(buf_rp != NULL);
1552
-
1553
- HEX_VERBOSE("ggml-hex: repack-mxfp4x4x2-mxfp4 %s : data %p size %zu dims %ldx%ld row-size %zu\n", t->name, data,
1554
- size, t->ne[0], nrows, row_size);
1555
-
1556
- memset(buf_pd, 0, row_size_pd); // clear-out padded buffer to make sure the tail is all zeros
1557
-
1558
- // 1. Process all the full rows
1559
- for (int64_t i = 0; i < n_full_rows; i++) {
1560
- const uint8_t * src = (const uint8_t *) t->data + (i * row_size);
1561
- uint8_t * dst = (uint8_t *) data + (i * row_size);
1562
-
1563
- memcpy(buf_pd, src, row_size);
1564
- unpack_row_mxfp4x4x2((block_mxfp4 *) buf_rp, (const uint8_t *) buf_pd, t->ne[0]);
1565
- memcpy(dst, buf_rp, row_size);
1566
- }
1567
-
1568
- // 2. Process the final, potentially partial, row
1569
- if (n_rem_bytes > 0) {
1570
- const int64_t i = n_full_rows;
1571
- const uint8_t * src = (const uint8_t *) t->data + (i * row_size);
1572
- uint8_t * dst = (uint8_t *) data + (i * row_size);
1573
-
1574
- // We still need to read and unpack the entire source row because the format is block-based.
1575
- memcpy(buf_pd, src, row_size);
1576
- unpack_row_mxfp4x4x2((block_mxfp4 *) buf_rp, (const uint8_t *) buf_pd, t->ne[0]);
1577
-
1578
- // But we only copy the remaining number of bytes to the destination to respect the size limit.
1579
- memcpy(dst, buf_rp, n_rem_bytes);
807
+ // repack mxfp4_tiled tensor into mxfp4 data
808
+ static void repack_tiled_mxfp4(void * data, const ggml_tensor * t, size_t size) {
809
+ block_mxfp4 * dst_matrix = (block_mxfp4 *) data;
810
+ int64_t ne0 = t->ne[0];
811
+ int64_t ne1 = t->ne[1];
812
+ int64_t ne2 = t->ne[2];
813
+ int64_t ne3 = t->ne[3];
814
+ int64_t ne0_padded = hex_round_up(ne0, 32);
815
+ int64_t ne1_padded = hex_round_up(ne1, 32);
816
+
817
+ int n_col_tiles = ne1_padded / 32;
818
+ int n_k_tiles = ne0_padded / 32;
819
+ const size_t tile_size = HTP_MM_WEIGHT_TILE_SIZE_MXFP4;
820
+ const size_t matrix_size = n_col_tiles * n_k_tiles * tile_size;
821
+
822
+ for (int i3 = 0; i3 < ne3; i3++) {
823
+ for (int i2 = 0; i2 < ne2; i2++) {
824
+ block_mxfp4 * dst_expert = dst_matrix + (i3 * ne2 + i2) * (ne1 * (ne0 / 32));
825
+ const uint8_t * matrix_src = (const uint8_t *) t->data + (i3 * ne2 + i2) * matrix_size;
826
+
827
+ for (int ct = 0; ct < n_col_tiles; ct++) {
828
+ for (int kt = 0; kt < n_k_tiles; kt++) {
829
+ const uint8_t * tile_src = matrix_src + (ct * n_k_tiles + kt) * tile_size;
830
+
831
+ uint8_t tile_quants[32][32];
832
+ for (int cp = 0; cp < 16; cp++) {
833
+ for (int row = 0; row < 32; row++) {
834
+ uint8_t val = tile_src[cp * 32 + row];
835
+ tile_quants[row][2 * cp + 0] = val & 0x0F;
836
+ tile_quants[row][2 * cp + 1] = val >> 4;
837
+ }
838
+ }
839
+
840
+ for (int row = 0; row < 32; row++) {
841
+ int64_t r = ct * 32 + row;
842
+ if (r < ne1 && kt < ne0 / 32) {
843
+ pack_mxfp4_quants(&dst_expert[r * (ne0 / 32) + kt], tile_quants[row], 0);
844
+ }
845
+ }
846
+
847
+ const uint8_t * scale_src = tile_src + 512;
848
+ for (int row = 0; row < 32; row++) {
849
+ int64_t r = ct * 32 + row;
850
+ if (r < ne1 && kt < ne0 / 32) {
851
+ dst_expert[r * (ne0 / 32) + kt].e = scale_src[row];
852
+ }
853
+ }
854
+ }
855
+ }
856
+ }
1580
857
  }
1581
-
1582
- ggml_aligned_free(buf_pd, row_size_pd);
1583
- ggml_aligned_free(buf_rp, row_size_rp);
1584
858
  }
1585
859
 
1586
860
  static void ggml_backend_hexagon_buffer_set_tensor(ggml_backend_buffer_t buffer,
@@ -1597,32 +871,32 @@ static void ggml_backend_hexagon_buffer_set_tensor(ggml_backend_buffer_t buffer,
1597
871
  case GGML_TYPE_Q4_0:
1598
872
  GGML_ASSERT(offset == 0);
1599
873
  GGML_ASSERT(offset + size <= ggml_nbytes(tensor));
1600
- repack_q4_0_q4x4x2(tensor, data, size);
874
+ repack_q4_0_tiled(tensor, data, size);
1601
875
  break;
1602
876
 
1603
877
  case GGML_TYPE_Q4_1:
1604
878
  GGML_ASSERT(offset == 0);
1605
879
  GGML_ASSERT(offset + size <= ggml_nbytes(tensor));
1606
- repack_q4_1_q4x4x2(tensor, data, size);
880
+ repack_q4_1_tiled(tensor, data, size);
1607
881
  break;
1608
882
 
1609
883
  case GGML_TYPE_Q8_0:
1610
884
  GGML_ASSERT(offset == 0);
1611
885
  GGML_ASSERT(offset + size <= ggml_nbytes(tensor));
1612
- repack_q8_0_q8x4x2(tensor, data, size);
886
+ repack_q8_0_tiled(tensor, data, size);
1613
887
  break;
1614
888
 
1615
889
  case GGML_TYPE_IQ4_NL:
1616
890
  GGML_ASSERT(offset == 0);
1617
891
  GGML_ASSERT(offset + size <= ggml_nbytes(tensor));
1618
892
  // IQ4_NL has identical block layout to Q4_0 (ggml_half d + uint8_t qs[16])
1619
- repack_q4_0_q4x4x2(tensor, data, size);
893
+ repack_q4_0_tiled(tensor, data, size);
1620
894
  break;
1621
895
 
1622
896
  case GGML_TYPE_MXFP4:
1623
897
  GGML_ASSERT(offset == 0);
1624
898
  GGML_ASSERT(offset + size <= ggml_nbytes(tensor));
1625
- repack_mxfp4_mxfp4x4x2(tensor, data, size);
899
+ repack_mxfp4_tiled(tensor, data, size);
1626
900
  break;
1627
901
 
1628
902
  default:
@@ -1645,31 +919,31 @@ static void ggml_backend_hexagon_buffer_get_tensor(ggml_backend_buffer_t buffer,
1645
919
  case GGML_TYPE_Q4_0:
1646
920
  GGML_ASSERT(offset == 0);
1647
921
  GGML_ASSERT(offset + size <= ggml_nbytes(tensor));
1648
- repack_q4x4x2_q4_0(data, tensor, size);
922
+ repack_tiled_q4_0(data, tensor, size);
1649
923
  break;
1650
924
 
1651
925
  case GGML_TYPE_Q4_1:
1652
926
  GGML_ASSERT(offset == 0);
1653
927
  GGML_ASSERT(offset + size <= ggml_nbytes(tensor));
1654
- repack_q4x4x2_q4_1(data, tensor, size);
928
+ repack_tiled_q4_1(data, tensor, size);
1655
929
  break;
1656
930
 
1657
931
  case GGML_TYPE_Q8_0:
1658
932
  GGML_ASSERT(offset == 0);
1659
933
  GGML_ASSERT(offset + size <= ggml_nbytes(tensor));
1660
- repack_q8x4x2_q8_0(data, tensor, size);
934
+ repack_tiled_q8_0(data, tensor, size);
1661
935
  break;
1662
936
 
1663
937
  case GGML_TYPE_IQ4_NL:
1664
938
  GGML_ASSERT(offset == 0);
1665
939
  GGML_ASSERT(offset + size <= ggml_nbytes(tensor));
1666
- repack_q4x4x2_q4_0(data, tensor, size);
940
+ repack_tiled_q4_0(data, tensor, size);
1667
941
  break;
1668
942
 
1669
943
  case GGML_TYPE_MXFP4:
1670
944
  GGML_ASSERT(offset == 0);
1671
945
  GGML_ASSERT(offset + size <= ggml_nbytes(tensor));
1672
- repack_mxfp4x4x2_mxfp4(data, tensor, size);
946
+ repack_tiled_mxfp4(data, tensor, size);
1673
947
  break;
1674
948
 
1675
949
  default:
@@ -1747,12 +1021,19 @@ static size_t ggml_backend_hexagon_buffer_type_get_alignment(ggml_backend_buffer
1747
1021
  }
1748
1022
 
1749
1023
  static size_t ggml_backend_hexagon_buffer_type_get_alloc_size(ggml_backend_buffer_type_t buft, const struct ggml_tensor * t) {
1024
+ if (t->type == GGML_TYPE_Q4_0 || t->type == GGML_TYPE_Q4_1 || t->type == GGML_TYPE_Q8_0 || t->type == GGML_TYPE_IQ4_NL || t->type == GGML_TYPE_MXFP4) {
1025
+ int64_t ne0 = hex_round_up(t->ne[0], 32);
1026
+ int64_t ne1 = hex_round_up(t->ne[1], 32);
1027
+ int64_t ne2 = t->ne[2];
1028
+ int64_t ne3 = t->ne[3];
1029
+ return ggml_row_size(t->type, ne0) * ne1 * ne2 * ne3;
1030
+ }
1750
1031
  return ggml_nbytes(t);
1751
1032
  }
1752
1033
 
1753
1034
  static size_t ggml_backend_hexagon_buffer_type_get_max_size(ggml_backend_buffer_type_t buffer_type) {
1754
- return opt_mbuf; // typically 1GB per buffer
1755
- GGML_UNUSED(buffer_type);
1035
+ auto * context = static_cast<ggml_backend_hexagon_buffer_type_context *>(buffer_type->context);
1036
+ return context->sess->max_bufsize;
1756
1037
  }
1757
1038
 
1758
1039
  static bool ggml_backend_hexagon_buffer_type_is_host(ggml_backend_buffer_type_t buft) {
@@ -1783,6 +1064,17 @@ static ggml_backend_buffer_type_i ggml_backend_hexagon_repack_buffer_type_interf
1783
1064
  /* .is_host = */ ggml_backend_hexagon_repack_buffer_type_is_host,
1784
1065
  };
1785
1066
 
1067
+ static bool ggml_backend_buffer_is_hexagon(const struct ggml_backend_buffer * b) {
1068
+ return b->buft->iface.get_alignment == ggml_backend_hexagon_buffer_type_get_alignment;
1069
+ }
1070
+
1071
+ static inline bool ggml_backend_buffer_is_hexagon_repack(const struct ggml_backend_buffer * b) {
1072
+ if (!opt_hostbuf) {
1073
+ return ggml_backend_buffer_is_hexagon(b);
1074
+ }
1075
+ return b->buft->iface.alloc_buffer == ggml_backend_hexagon_repack_buffer_type_alloc_buffer;
1076
+ }
1077
+
1786
1078
  struct ggml_hexagon_opbatch {
1787
1079
  ggml_hexagon_session* sess;
1788
1080
 
@@ -1863,14 +1155,25 @@ struct ggml_hexagon_opbatch {
1863
1155
 
1864
1156
  b_vmem += b.size;
1865
1157
 
1866
- HEX_VERBOSE("ggml-hex: add-buffer #%u : fd %d base %p size %zu : vmem %zu\n", bi, b.fd, (void*) sbuf->base, (size_t) b.size, b_vmem);
1158
+ HEX_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);
1867
1159
 
1868
1160
  return bi;
1869
1161
  }
1870
1162
 
1871
1163
  bool same_shape(const htp_tensor * h, const ggml_tensor * t) const {
1872
- return (h->ne[0] == t->ne[0]) && (h->ne[1] == t->ne[1]) && (h->ne[2] == t->ne[2]) && (h->ne[3] == t->ne[3]) &&
1873
- (h->nb[0] == t->nb[0]) && (h->nb[1] == t->nb[1]) && (h->nb[2] == t->nb[2]) && (h->nb[3] == t->nb[3]);
1164
+ int64_t ne0 = t->ne[0];
1165
+ int64_t ne1 = t->ne[1];
1166
+ const bool is_repack = ggml_backend_buffer_is_hexagon_repack(t->buffer) && ggml_hexagon_is_repack_type(t->type);
1167
+ if (is_repack) {
1168
+ ne0 = hex_round_up(ne0, 32);
1169
+ ne1 = hex_round_up(ne1, 32);
1170
+ }
1171
+ int64_t nb1 = is_repack ? ggml_row_size(t->type, ne0) : t->nb[1];
1172
+ int64_t nb2 = is_repack ? nb1 * ne1 : t->nb[2];
1173
+ int64_t nb3 = is_repack ? nb2 * t->ne[2] : t->nb[3];
1174
+
1175
+ return (h->ne[0] == ne0) && (h->ne[1] == ne1) && (h->ne[2] == t->ne[2]) && (h->ne[3] == t->ne[3]) &&
1176
+ (h->nb[0] == t->nb[0]) && (h->nb[1] == nb1) && (h->nb[2] == nb2) && (h->nb[3] == nb3);
1874
1177
  }
1875
1178
 
1876
1179
  // add tensor and return its index
@@ -1901,19 +1204,35 @@ struct ggml_hexagon_opbatch {
1901
1204
  htp_tensor &h = h_tens[ti];
1902
1205
  h.bi = add_buffer(sbuf);
1903
1206
  h.data = t_offset;
1904
- h.size = t_size;
1905
1207
  h.type = t->type;
1906
- h.ne[0] = t->ne[0]; h.ne[1] = t->ne[1]; h.ne[2] = t->ne[2]; h.ne[3] = t->ne[3];
1907
- h.nb[0] = t->nb[0]; h.nb[1] = t->nb[1]; h.nb[2] = t->nb[2]; h.nb[3] = t->nb[3];
1208
+
1209
+ const bool is_repack = ggml_backend_buffer_is_hexagon_repack(t->buffer) && ggml_hexagon_is_repack_type(t->type);
1210
+ if (is_repack) {
1211
+ h.ne[0] = hex_round_up(t->ne[0], 32);
1212
+ h.ne[1] = hex_round_up(t->ne[1], 32);
1213
+ h.ne[2] = t->ne[2];
1214
+ h.ne[3] = t->ne[3];
1215
+
1216
+ h.nb[0] = t->nb[0];
1217
+ h.nb[1] = ggml_row_size(t->type, h.ne[0]);
1218
+ h.nb[2] = h.nb[1] * h.ne[1];
1219
+ h.nb[3] = h.nb[2] * h.ne[2];
1220
+ h.size = h.nb[3] * h.ne[3];
1221
+ t_size = h.size;
1222
+ } else {
1223
+ h.size = t_size;
1224
+ h.ne[0] = t->ne[0]; h.ne[1] = t->ne[1]; h.ne[2] = t->ne[2]; h.ne[3] = t->ne[3];
1225
+ h.nb[0] = t->nb[0]; h.nb[1] = t->nb[1]; h.nb[2] = t->nb[2]; h.nb[3] = t->nb[3];
1226
+ }
1908
1227
 
1909
1228
  h.flags = 0;
1910
1229
  if (ggml_backend_buffer_get_usage(t->buffer) == GGML_BACKEND_BUFFER_USAGE_COMPUTE) {
1911
1230
  h.flags |= HTP_TENSOR_COMPUTE;
1912
1231
  }
1913
1232
 
1914
- HEX_VERBOSE("ggml-hex: add-tensor #%u %s : bi %d data %p offset %zu size %zu flags 0x%x : %zu:%zu:%zu:%zu\n",
1233
+ HEX_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(),
1915
1234
  ti, t->name, h.bi, (void*) t->data, (size_t) t_offset, t_size, h.flags,
1916
- (size_t) t->ne[0], (size_t) t->ne[1], (size_t) t->ne[2], (size_t) t->ne[3]);
1235
+ (size_t) h.ne[0], (size_t) h.ne[1], (size_t) h.ne[2], (size_t) h.ne[3]);
1917
1236
 
1918
1237
  return ti;
1919
1238
  }
@@ -1942,7 +1261,9 @@ struct ggml_hexagon_opbatch {
1942
1261
  for (const auto * src : node.get_inputs()) {
1943
1262
  fit_tensor(src);
1944
1263
  }
1945
- fit_tensor(node.dst());
1264
+ for (const auto * output : node.get_outputs()) {
1265
+ fit_tensor(output);
1266
+ }
1946
1267
 
1947
1268
  if ((extra_bufs + n_bufs) > n_bufs_max) return false;
1948
1269
  if ((extra_tens + n_tens) > n_tens_max) return false;
@@ -1961,7 +1282,8 @@ struct ggml_hexagon_opbatch {
1961
1282
  ops[n] = node;
1962
1283
 
1963
1284
  htp_op_desc &o = h_ops[n];
1964
- memcpy(&o.params, &node.node->op_params, sizeof(node.node->op_params));
1285
+ memcpy(o.params, node.node->op_params, sizeof(node.node->op_params));
1286
+ memcpy(o.kernel_params, node.kernel_params, sizeof(o.kernel_params));
1965
1287
  o.opcode = node.opcode;
1966
1288
  o.flags = 0;
1967
1289
 
@@ -1969,13 +1291,17 @@ struct ggml_hexagon_opbatch {
1969
1291
  o.flags |= HTP_OPFLAGS_SKIP_COMPUTE;
1970
1292
  }
1971
1293
 
1972
- ggml_hexagon_dump_op_exec(sess->c_name(), node, o.flags);
1294
+ ggml_hexagon_dump_op_exec(sess->c_name(), ops[n], o.flags);
1973
1295
 
1974
1296
  auto inputs = node.get_inputs();
1975
1297
  for (unsigned int i=0; i < HTP_OP_MAX_INPUTS; i++) {
1976
- o.src[i] = (i < inputs.size() && inputs[i]) ? add_tensor(inputs[i]) : 0xffff;
1298
+ o.src[i] = (i < inputs.size() && inputs[i]) ? add_tensor(inputs[i]) : 0xffff;
1299
+ }
1300
+
1301
+ auto outputs = node.get_outputs();
1302
+ for (unsigned int i=0; i < HTP_OP_MAX_OUTPUTS; i++) {
1303
+ o.dst[i] = (i < outputs.size() && outputs[i]) ? add_tensor(outputs[i]) : 0xffff;
1977
1304
  }
1978
- o.dst = add_tensor(node.dst());
1979
1305
  }
1980
1306
  };
1981
1307
 
@@ -1986,19 +1312,25 @@ struct ggml_hexagon_opqueue {
1986
1312
 
1987
1313
  using opvec = std::vector<htp_opnode>;
1988
1314
 
1989
- std::queue<unsigned int> done; // completed batch ids
1990
- std::vector<opvec> op_cache; // per batch op cache
1991
- std::vector<uint64_t> start_usec; // per batch start time
1315
+ std::queue<unsigned int> done; // completed batch ids
1316
+ std::vector<opvec> op_cache; // per batch op cache
1317
+ std::vector<uint64_t> start_usec; // per batch start time
1992
1318
 
1993
1319
  ggml_hexagon_opqueue(ggml_hexagon_session *sess, size_t batch_size, size_t depth) {
1994
1320
  size_t n_bufs = HTP_OP_MAX_BUFS;
1995
1321
  size_t n_ops = batch_size;
1996
- size_t n_tensors = n_ops + n_ops * HTP_OP_MAX_INPUTS;
1322
+ size_t n_tensors = n_ops * HTP_OP_MAX_OUTPUTS + n_ops * HTP_OP_MAX_INPUTS;
1323
+
1324
+ size_t tr_size = 0;
1325
+ if (opt_profile == 3) {
1326
+ tr_size = (HTP_MAX_NTHREADS + 1) * opt_optrace * sizeof(htp_trace_desc);
1327
+ }
1997
1328
 
1998
1329
  shm_blk_size = sizeof(htp_buf_desc) * n_bufs +
1999
1330
  sizeof(htp_tensor) * n_tensors +
2000
1331
  sizeof(htp_op_desc) * n_ops +
2001
- sizeof(htp_prof_desc) * n_ops;
1332
+ sizeof(htp_prof_desc) * n_ops +
1333
+ tr_size;
2002
1334
 
2003
1335
  shm_buf = new ggml_hexagon_shared_buffer(sess, shm_blk_size * depth, true /* pinned */);
2004
1336
 
@@ -2042,11 +1374,19 @@ struct ggml_hexagon_opqueue {
2042
1374
  const size_t o_size = sizeof(htp_op_desc) * req.n_ops;
2043
1375
  const size_t p_size = sizeof(htp_prof_desc) * req.n_ops;
2044
1376
 
1377
+ size_t tr_size = 0;
1378
+ if (opt_profile == 3) {
1379
+ req.n_traces = opt_optrace;
1380
+ tr_size = (HTP_MAX_NTHREADS + 1) * req.n_traces * sizeof(htp_trace_desc);
1381
+ } else {
1382
+ req.n_traces = 0;
1383
+ }
1384
+
2045
1385
  dbuf.ptr = shm_buf->base + (req.id * shm_blk_size);
2046
1386
  dbuf.fd = shm_buf->fd;
2047
1387
  dbuf.flags = DSPQUEUE_BUFFER_FLAG_FLUSH_SENDER | DSPQUEUE_BUFFER_FLAG_INVALIDATE_RECIPIENT;
2048
1388
  dbuf.offset = (uint8_t*) dbuf.ptr - (uint8_t*) shm_buf->base;
2049
- dbuf.size = b_size + t_size + o_size + p_size;
1389
+ dbuf.size = b_size + t_size + o_size + p_size + tr_size;
2050
1390
 
2051
1391
  GGML_ASSERT(dbuf.size <= shm_blk_size);
2052
1392
 
@@ -2092,7 +1432,14 @@ struct ggml_hexagon_opqueue {
2092
1432
  const size_t o_size = sizeof(htp_op_desc) * rsp.n_ops;
2093
1433
  const size_t p_size = sizeof(htp_prof_desc) * rsp.n_ops;
2094
1434
 
2095
- const size_t m_size = b_size + t_size + o_size + p_size;
1435
+ size_t tr_size = 0;
1436
+ uint32_t n_traces = 0;
1437
+ if (opt_profile == 3) {
1438
+ n_traces = opt_optrace;
1439
+ tr_size = (HTP_MAX_NTHREADS + 1) * n_traces * sizeof(htp_trace_desc);
1440
+ }
1441
+
1442
+ const size_t m_size = b_size + t_size + o_size + p_size + tr_size;
2096
1443
  GGML_ASSERT(m_size <= shm_blk_size);
2097
1444
 
2098
1445
  HEX_VERBOSE("ggml-hex: %s op-queue pop batch #%u : n-bufs %u n-tensors %u n-ops %u : m-size %zu b-size %zu t-size %zu o-size %zu\n",
@@ -2111,13 +1458,62 @@ struct ggml_hexagon_opqueue {
2111
1458
  GGML_ASSERT(rsp.n_ops <= ops.size());
2112
1459
 
2113
1460
  const htp_prof_desc * pd = (const htp_prof_desc *) p_ptr;
1461
+
1462
+ const htp_trace_desc * trace_events = nullptr;
1463
+
1464
+ if (opt_profile == 3) {
1465
+ trace_events = (const htp_trace_desc *) (p_ptr + p_size);
1466
+ }
1467
+
1468
+ uint32_t trace_idx[HTP_MAX_NTHREADS + 1] = {0};
1469
+ uint32_t valid_cnt[HTP_MAX_NTHREADS + 1] = {0};
1470
+
1471
+ if (opt_profile == 3) {
1472
+ for (uint32_t t = 0; t <= HTP_MAX_NTHREADS; t++) {
1473
+ uint32_t count = rsp.n_traces[t];
1474
+ valid_cnt[t] = count > n_traces ? n_traces : count;
1475
+ }
1476
+ }
1477
+
2114
1478
  for (uint32_t i = 0; i < rsp.n_ops; i++) {
2115
1479
  htp_usec += pd[i].usecs;
2116
- ggml_hexagon_dump_op_prof(shm_buf->sess->name, ops[i], pd[i].usecs, pd[i].cycles, pd[i].pmu);
1480
+
1481
+ ggml_hexagon_dump_op_prof(shm_buf->sess->name, ops[i], pd[i]);
1482
+
1483
+ if (opt_profile == 3) {
1484
+ uint32_t op_duration = pd[i].cycles_stop - pd[i].cycles_start;
1485
+
1486
+ for (uint32_t t = 0; t <= HTP_MAX_NTHREADS; t++) {
1487
+ while (trace_idx[t] < valid_cnt[t]) {
1488
+ const auto & e = trace_events[t * n_traces + trace_idx[t]];
1489
+ uint32_t offset = e.cycles - pd[i].cycles_start;
1490
+ if (offset >= 0x80000000) {
1491
+ trace_idx[t]++;
1492
+ continue;
1493
+ }
1494
+ if (offset > op_duration) {
1495
+ break;
1496
+ }
1497
+ bool is_stop = (e.info & 0x8000) != 0;
1498
+ uint16_t info = e.info & 0x7FFF;
1499
+ GGML_LOG_DEBUG("ggml-hex: %s trace-op %s: thread %u event %s info %u %s %u\n",
1500
+ shm_buf->sess->c_name(), ops[i].op_name().c_str(), t, htp_event_name(e.id), info, is_stop ? "stop" : "start", e.cycles);
1501
+ trace_idx[t]++;
1502
+ }
1503
+ }
1504
+ }
1505
+ }
1506
+
1507
+ char evt_str[256] = "";
1508
+ if (opt_profile == 3) {
1509
+ snprintf(evt_str, sizeof(evt_str), " evt [%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u]",
1510
+ rsp.n_traces[0], rsp.n_traces[1], rsp.n_traces[2], rsp.n_traces[3],
1511
+ rsp.n_traces[4], rsp.n_traces[5], rsp.n_traces[6], rsp.n_traces[7],
1512
+ rsp.n_traces[8], rsp.n_traces[9], rsp.n_traces[10]);
2117
1513
  }
2118
1514
 
2119
- GGML_LOG_DEBUG("ggml-hex: %s profile-batch n-ops %u batch-dur-usec %lld htp-ops-usec %u\n",
2120
- shm_buf->sess->c_name(), rsp.n_ops, (long long) batch_usec, htp_usec);
1515
+ GGML_LOG_DEBUG("ggml-hex: %s profile-batch n-ops %u batch-dur-usec %lld htp-ops-usec %u%s\n",
1516
+ shm_buf->sess->c_name(), rsp.n_ops, (long long) batch_usec, htp_usec, evt_str);
2121
1517
  }
2122
1518
  }
2123
1519
  };
@@ -2134,6 +1530,7 @@ void ggml_hexagon_session::flush_pending(bool all) {
2134
1530
 
2135
1531
  // Read response packet from queue
2136
1532
  const uint32_t timeo = opt_oppoll ? 0 : DSPQUEUE_TIMEOUT;
1533
+
2137
1534
  int err = dspqueue_read(this->queue, &flags, 1, &n_dbufs, &dbuf, sizeof(rsp), &rsp_size, (uint8_t *) &rsp, timeo);
2138
1535
  if (err == AEE_EEXPIRED) {
2139
1536
  continue;
@@ -2314,6 +1711,31 @@ void ggml_hexagon_session::allocate(int dev_id) noexcept(false) {
2314
1711
 
2315
1712
  this->valid_handle = true;
2316
1713
 
1714
+ // Query HW info and resolve session options
1715
+ this->max_bufsize = opt_mbuf;
1716
+ {
1717
+ unsigned int hw_n_threads = 0;
1718
+ unsigned int hw_n_hvx = 0;
1719
+ unsigned int hw_n_hmx = 0;
1720
+ unsigned long long hw_vtcm_size = 0;
1721
+ int hw_err = htp_iface_hwinfo(this->handle, &hw_n_threads, &hw_n_hvx, &hw_n_hmx, &hw_vtcm_size);
1722
+ if (hw_err == 0) {
1723
+ this->n_threads = opt_nhvx > 0 ? (uint32_t)opt_nhvx : (uint32_t)hw_n_threads;
1724
+ this->n_hvx = opt_nhvx > 0 ? (uint32_t)opt_nhvx : (uint32_t)hw_n_hvx;
1725
+ this->n_hmx = (opt_nhmx != 0) ? (uint32_t)hw_n_hmx : 0;
1726
+ this->vtcm_size = (uint64_t)hw_vtcm_size;
1727
+ GGML_LOG_INFO("ggml-hex: %s hwinfo: threads %u, hvx %u, hmx %u, vtcm %llu MB\n",
1728
+ this->c_name(), this->n_threads, this->n_hvx, this->n_hmx,
1729
+ (unsigned long long)(this->vtcm_size / (1024 * 1024)));
1730
+ } else {
1731
+ GGML_LOG_WARN("ggml-hex: %s failed to query hwinfo (0x%x), using defaults\n", this->c_name(), hw_err);
1732
+ this->n_threads = opt_nhvx > 0 ? (uint32_t)opt_nhvx : 8;
1733
+ this->n_hvx = opt_nhvx > 0 ? (uint32_t)opt_nhvx : 8;
1734
+ this->n_hmx = (opt_nhmx != 0) ? 1 : 0;
1735
+ this->vtcm_size = 8 * 1024 * 1024;
1736
+ }
1737
+ }
1738
+
2317
1739
  // Enable FastRPC QoS mode
2318
1740
  {
2319
1741
  struct remote_rpc_control_latency l;
@@ -2378,11 +1800,12 @@ void ggml_hexagon_session::allocate(int dev_id) noexcept(false) {
2378
1800
  opt_vmem = ggml_hexagon_measure_max_vmem(this);
2379
1801
  GGML_LOG_INFO("ggml-hex: %s measured max vmem %zu\n", this->c_name(), opt_vmem);
2380
1802
  }
1803
+ this->max_vmem = opt_vmem;
2381
1804
 
2382
- this->op_batch = new ggml_hexagon_opbatch(this, opt_opbatch, opt_vmem);
1805
+ this->op_batch = new ggml_hexagon_opbatch(this, opt_opbatch, this->max_vmem);
2383
1806
 
2384
1807
  // Start dspqueue/opbatch processing
2385
- err = htp_iface_start(this->handle, dev_id, this->queue_id, opt_nhvx, opt_use_hmx, opt_vmem);
1808
+ err = htp_iface_start(this->handle, dev_id, this->queue_id, opt_nhvx, opt_nhmx, this->max_vmem);
2386
1809
  if (err != 0) {
2387
1810
  GGML_LOG_ERROR("ggml-hex: %s failed to start session: 0x%08x\n", this->c_name(), (unsigned) err);
2388
1811
  throw std::runtime_error("ggml-hex: iface start failed (see log for details)");
@@ -2463,15 +1886,161 @@ ggml_hexagon_session::~ggml_hexagon_session() noexcept(true) {
2463
1886
 
2464
1887
  // ** backend interface
2465
1888
 
2466
- static bool ggml_backend_buffer_is_hexagon(const struct ggml_backend_buffer * b) {
2467
- return b->buft->iface.get_alignment == ggml_backend_hexagon_buffer_type_get_alignment;
1889
+ static bool ggml_hexagon_flash_attn_is_hmx_eligible(
1890
+ const struct ggml_hexagon_session * sess,
1891
+ const struct ggml_tensor * q,
1892
+ const struct ggml_tensor * k,
1893
+ const struct ggml_tensor * v,
1894
+ const struct ggml_tensor * sinks
1895
+ ) {
1896
+ if (sess->n_hmx == 0) {
1897
+ return false;
1898
+ }
1899
+
1900
+ if (opt_fa_select < 2) {
1901
+ return false;
1902
+ }
1903
+
1904
+ if (k->type != GGML_TYPE_F16 || v->type != GGML_TYPE_F16) {
1905
+ return false;
1906
+ }
1907
+
1908
+ const uint32_t DK = q->ne[0];
1909
+ const uint32_t DV = v->ne[0];
1910
+
1911
+ if (DK % 64 != 0 || DV % 64 != 0) {
1912
+ return false;
1913
+ }
1914
+
1915
+ // Fall back to HVX for small token counts if head dimension is small (DK <= 128)
1916
+ const uint32_t neq1 = q->ne[1];
1917
+ if (DK <= 128 && neq1 < 5) {
1918
+ return false;
1919
+ }
1920
+
1921
+ return true;
2468
1922
  }
2469
1923
 
2470
- static inline bool ggml_backend_buffer_is_hexagon_repack(const struct ggml_backend_buffer * b) {
2471
- if (!opt_hostbuf) {
2472
- return ggml_backend_buffer_is_hexagon(b);
1924
+ static bool ggml_hexagon_precompute_flash_attn_params(
1925
+ const struct ggml_hexagon_session * sess,
1926
+ const struct ggml_tensor * op,
1927
+ struct htp_fa_kernel_params * kparams
1928
+ ) {
1929
+ if (opt_fa_select < 1) {
1930
+ return false;
2473
1931
  }
2474
- return b->buft->iface.alloc_buffer == ggml_backend_hexagon_repack_buffer_type_alloc_buffer;
1932
+
1933
+ memset(kparams, 0, sizeof(*kparams));
1934
+
1935
+ const struct ggml_tensor * q = op->src[0];
1936
+ const struct ggml_tensor * k = op->src[1];
1937
+ const struct ggml_tensor * v = op->src[2];
1938
+ const struct ggml_tensor * mask = op->src[3];
1939
+ const struct ggml_tensor * dst = op;
1940
+
1941
+ const uint32_t neq0 = q->ne[0]; // head_dim (DK)
1942
+ const uint32_t neq1 = q->ne[1]; // n_tokens
1943
+ const uint32_t neq2 = q->ne[2]; // n_heads
1944
+
1945
+ const uint32_t nek1 = k->ne[1]; // kv_len
1946
+
1947
+ const uint32_t nev0 = v->ne[0]; // head_dim (DV)
1948
+
1949
+ const uint32_t DK = neq0;
1950
+ const uint32_t DV = nev0;
1951
+
1952
+ const uint32_t n_kv_heads = k->ne[2];
1953
+ const uint32_t G = neq2 / n_kv_heads;
1954
+
1955
+ float scale = 1.0f;
1956
+ float max_bias = 0.0f;
1957
+ float logit_softcap = 0.0f;
1958
+ memcpy(&scale, &op->op_params[0], sizeof(float));
1959
+ memcpy(&max_bias, &op->op_params[1], sizeof(float));
1960
+ memcpy(&logit_softcap, &op->op_params[2], sizeof(float));
1961
+
1962
+ if (logit_softcap != 0.0f) {
1963
+ scale /= logit_softcap;
1964
+ }
1965
+
1966
+ kparams->scale = scale;
1967
+ kparams->max_bias = max_bias;
1968
+ kparams->logit_softcap = logit_softcap;
1969
+
1970
+ kparams->is_q_fp32 = (q->type == GGML_TYPE_F32) ? 1 : 0;
1971
+ kparams->is_dst_fp32 = (dst->type == GGML_TYPE_F32) ? 1 : 0;
1972
+ kparams->G = G;
1973
+
1974
+ const uint32_t n_head = q->ne[2];
1975
+ kparams->n_head_log2 = 1u << (uint32_t) std::floor(std::log2(n_head));
1976
+ kparams->m0 = std::pow(2.0f, -(max_bias) / kparams->n_head_log2);
1977
+ kparams->m1 = std::pow(2.0f, -(max_bias / 2.0f) / kparams->n_head_log2);
1978
+
1979
+ // Check HMX eligibility
1980
+ const struct ggml_tensor * sinks = op->src[4];
1981
+ if (ggml_hexagon_flash_attn_is_hmx_eligible(sess, q, k, v, sinks)) {
1982
+ size_t Br = 0, Bc = 0;
1983
+ int ret = hmx_fa_find_chunk_size(&Br, &Bc, G, DK, DV, neq1, nek1, sess->vtcm_size, sess->n_threads);
1984
+ if (ret == 0) {
1985
+ kparams->kernel_type = HTP_FA_KERNEL_HMX;
1986
+ kparams->Br = Br;
1987
+ kparams->Bc = Bc;
1988
+ kparams->n_kv_blocks = (nek1 + Bc - 1) / Bc;
1989
+ kparams->n_threads = (kparams->n_kv_blocks >= 3 && sess->n_threads >= 2) ? sess->n_threads : 1;
1990
+
1991
+ kparams->u.hmx.g_br = hex_align_up(G * Br, 32);
1992
+ kparams->u.hmx.pipeline = (kparams->n_kv_blocks >= 3 && sess->n_threads >= 2) ? 1 : 0;
1993
+ kparams->vtcm_size = hmx_fa_compute_vtcm_usage(G, DK, DV, Br, Bc, kparams->n_threads, kparams->u.hmx.pipeline != 0);
1994
+
1995
+ const size_t row_vec_bytes = hex_align_up(Bc * sizeof(uint16_t), 256);
1996
+ kparams->u.hmx.row_buf_stride = row_vec_bytes / 128; // HVX vector is 128 bytes
1997
+
1998
+ const size_t m_line_bytes = hex_align_up(Bc * sizeof(uint16_t), 128);
1999
+ kparams->u.hmx.mask_buf_row_stride = m_line_bytes / sizeof(uint16_t);
2000
+ kparams->u.hmx.mask_broadcast = (mask != nullptr && mask->ne[2] == 1) ? 1 : 0;
2001
+ kparams->u.hmx.div_G = init_fastdiv_values(G);
2002
+ if (mask) {
2003
+ kparams->src3_div2 = init_fastdiv_values(mask->ne[2]);
2004
+ kparams->src3_div3 = init_fastdiv_values(mask->ne[3]);
2005
+ }
2006
+
2007
+ kparams->qrows = 0;
2008
+ kparams->qrows_per_thread = 0;
2009
+ return true;
2010
+ }
2011
+ }
2012
+
2013
+ // Fallback to HVX
2014
+ kparams->kernel_type = HTP_FA_KERNEL_HVX;
2015
+ kparams->Br = 1;
2016
+ kparams->Bc = 64; // FLASH_ATTN_BLOCK_SIZE
2017
+ kparams->n_kv_blocks = (k->ne[1] + 64 - 1) / 64;
2018
+ kparams->n_threads = sess->n_threads;
2019
+
2020
+ const size_t size_q_row_padded = hex_round_up(q->ne[0] * (kparams->is_q_fp32 ? 4 : 2), 128);
2021
+ const size_t size_k_row_padded = hex_round_up(k->ne[0] * 2, 128);
2022
+ const size_t size_v_row_padded = hex_round_up(v->ne[0] * 2, 128);
2023
+
2024
+ kparams->vtcm_size = hvx_fa_compute_vtcm_usage(DK, DV, kparams->is_q_fp32 != 0, mask != nullptr, sess->n_threads);
2025
+
2026
+ kparams->u.hvx.size_q_row_padded = size_q_row_padded;
2027
+ kparams->u.hvx.size_k_row_padded = size_k_row_padded;
2028
+ kparams->u.hvx.size_v_row_padded = size_v_row_padded;
2029
+ kparams->u.hvx.src0_div21 = init_fastdiv_values(q->ne[2] * q->ne[1]);
2030
+ kparams->u.hvx.src0_div1 = init_fastdiv_values(q->ne[1]);
2031
+ kparams->broadcast_rk2 = init_fastdiv_values(q->ne[2]/k->ne[2]);
2032
+ kparams->broadcast_rk3 = init_fastdiv_values(q->ne[3]/k->ne[3]);
2033
+ kparams->broadcast_rv2 = init_fastdiv_values(q->ne[2]/v->ne[2]);
2034
+ kparams->broadcast_rv3 = init_fastdiv_values(q->ne[3]/v->ne[3]);
2035
+ if (mask) {
2036
+ kparams->src3_div2 = init_fastdiv_values(mask->ne[2]);
2037
+ kparams->src3_div3 = init_fastdiv_values(mask->ne[3]);
2038
+ }
2039
+
2040
+ kparams->qrows = q->ne[1] * q->ne[2] * q->ne[3];
2041
+ kparams->qrows_per_thread = (kparams->qrows + sess->n_threads - 1) / sess->n_threads;
2042
+
2043
+ return true;
2475
2044
  }
2476
2045
 
2477
2046
  static bool ggml_hexagon_supported_flash_attn_ext(const struct ggml_hexagon_session * sess, const struct ggml_tensor * op) {
@@ -2506,6 +2075,17 @@ static bool ggml_hexagon_supported_flash_attn_ext(const struct ggml_hexagon_sess
2506
2075
  return false;
2507
2076
  }
2508
2077
 
2078
+ struct htp_fa_kernel_params kparams;
2079
+ if (!ggml_hexagon_precompute_flash_attn_params(sess, op, &kparams)) {
2080
+ return false;
2081
+ }
2082
+
2083
+ if ((size_t) kparams.vtcm_size > sess->vtcm_size) {
2084
+ HEX_VERBOSE("ggml-hex: skip flash_attn_ext because VTCM needed (%d) > budget (%zu)\n",
2085
+ kparams.vtcm_size, sess->vtcm_size);
2086
+ return false;
2087
+ }
2088
+
2509
2089
  return true;
2510
2090
  }
2511
2091
 
@@ -2563,6 +2143,592 @@ static bool ggml_hexagon_supported_gated_delta_net(const struct ggml_hexagon_ses
2563
2143
  return true;
2564
2144
  }
2565
2145
 
2146
+ static bool ggml_hexagon_matmul_is_hmx_eligible(
2147
+ const struct ggml_tensor * src0,
2148
+ const struct ggml_tensor * src1,
2149
+ const struct ggml_tensor * dst,
2150
+ int ne01_padded,
2151
+ bool is_matmul_id,
2152
+ bool is_batched
2153
+ ) {
2154
+ const int ne00 = src0->ne[0];
2155
+ const int ne11 = src1->ne[1];
2156
+ const int ne12 = src1->ne[2];
2157
+ const int wtype = src0->type;
2158
+
2159
+ // HMX weight tile requires N to be 32-aligned.
2160
+ if (ne01_padded % 32 != 0) {
2161
+ return false;
2162
+ }
2163
+
2164
+ // HMX supports F16, F32, and repack quantized types.
2165
+ if (!ggml_hexagon_is_hmx_weight_type((ggml_type) wtype)) {
2166
+ return false;
2167
+ }
2168
+
2169
+ // HMX paths require K aligned to 32.
2170
+ if (ne00 % 32 != 0) {
2171
+ return false;
2172
+ }
2173
+
2174
+ // Quantized HMX kernels only handle flat 2D matmul (or matmul_id wrapping flat 2D matmuls).
2175
+ if (!is_matmul_id && is_batched && wtype != GGML_TYPE_F16) {
2176
+ return false;
2177
+ }
2178
+
2179
+ // HMX assumes contiguous row-major layout.
2180
+ if (src0->nb[0] > src0->nb[1] || src1->nb[0] > src1->nb[1]) {
2181
+ return false;
2182
+ }
2183
+
2184
+ // M alignment: Use HMX when M > HTP_MM_HMX_MIN_NROWS
2185
+ const int m = is_matmul_id ? ne12 : ne11;
2186
+ if (m <= HTP_MM_HMX_MIN_NROWS) {
2187
+ return false;
2188
+ }
2189
+
2190
+ return true;
2191
+ }
2192
+
2193
+ static bool ggml_hexagon_precompute_hmx_mm_params(
2194
+ const struct ggml_hexagon_session * sess,
2195
+ const struct ggml_tensor * src0,
2196
+ const struct ggml_tensor * src1,
2197
+ const struct ggml_tensor * dst,
2198
+ int wtype,
2199
+ int ne00_padded,
2200
+ int ne01_padded,
2201
+ int ne02,
2202
+ int ne11,
2203
+ int ne12,
2204
+ int ne11_padded,
2205
+ bool is_matmul_id,
2206
+ bool is_batched,
2207
+ size_t vtcm_budget,
2208
+ struct htp_mm_kernel_params * kparams
2209
+ ) {
2210
+ const int aligned_tile_size = htp_mm_get_weight_aligned_tile_size(wtype);
2211
+ const bool pipeline = is_matmul_id ? false : htp_mm_hmx_pipeline(ne11);
2212
+ const int n_threads = (int)sess->n_threads;
2213
+ const int ne10 = src1->ne[0];
2214
+
2215
+ const bool is_batched_val = is_matmul_id ? false : is_batched;
2216
+ const int group_size = (ne02 > 0 ? ne12 / ne02 : 1);
2217
+
2218
+ size_t m_chunk = 0;
2219
+ size_t n_chunk = 0;
2220
+ size_t vtcm_size = 0;
2221
+ bool use_grouped = false;
2222
+ int act_threads_selected = 0;
2223
+
2224
+ if (is_batched_val && wtype == GGML_TYPE_F16 && group_size > 1) {
2225
+ // Try grouped path first
2226
+ const bool use_dma_activation = (src1->nb[1]/sizeof(float) > (size_t)ne00_padded);
2227
+ size_t best_mblocks = SIZE_MAX;
2228
+ int best_act_threads = 0;
2229
+ size_t best_m_chunk = 0;
2230
+ size_t best_n_chunk = 0;
2231
+ size_t best_vtcm_size = 0;
2232
+
2233
+ int act_threads = n_threads;
2234
+ while (act_threads >= 1) {
2235
+ const size_t f32_scratch_size = use_dma_activation ? hex_align_up(act_threads * HTP_MM_DMA_ACT_MULTIPLIER * ne00_padded * sizeof(float), HTP_MM_HMX_TILE_SIZE) : 0;
2236
+ size_t group_overhead = 256 + f32_scratch_size;
2237
+ size_t group_size_per_n, group_size_per_m, group_size_per_mn;
2238
+ htp_mm_hmx_get_batched_chunk_costs(ne00_padded, group_size, &group_size_per_n, &group_size_per_m, &group_size_per_mn);
2239
+
2240
+ size_t m_chunk_candidate = 0;
2241
+ size_t n_chunk_candidate = 0;
2242
+ size_t vtcm_size_candidate = 0;
2243
+
2244
+ if (htp_mm_hmx_compute_chunks(vtcm_budget, group_overhead, group_size_per_n, group_size_per_m, group_size_per_mn, hex_align_up(ne11, 32), ne01_padded,
2245
+ (size_t) ne01_padded * HTP_MM_HMX_COST_W_DEQUANT, (size_t) ne11 * HTP_MM_HMX_COST_A_CONVERT,
2246
+ &m_chunk_candidate, &n_chunk_candidate, &vtcm_size_candidate) == 0) {
2247
+ size_t exact_size = htp_mm_hmx_get_batched_vtcm_size(wtype, ne00_padded, m_chunk_candidate, n_chunk_candidate, group_size, use_dma_activation, pipeline, act_threads);
2248
+ if (exact_size <= vtcm_budget) {
2249
+ size_t mblocks = ((size_t) ne11 + m_chunk_candidate - 1) / m_chunk_candidate;
2250
+ if (mblocks < best_mblocks || (mblocks == best_mblocks && act_threads > best_act_threads)) {
2251
+ best_mblocks = mblocks;
2252
+ best_act_threads = act_threads;
2253
+ best_m_chunk = m_chunk_candidate;
2254
+ best_n_chunk = n_chunk_candidate;
2255
+ best_vtcm_size = exact_size;
2256
+ }
2257
+ }
2258
+ }
2259
+ if (act_threads == 1) {
2260
+ act_threads = 0;
2261
+ } else {
2262
+ act_threads /= 2;
2263
+ }
2264
+ }
2265
+
2266
+ if (best_act_threads > 0) {
2267
+ m_chunk = best_m_chunk;
2268
+ n_chunk = best_n_chunk;
2269
+ vtcm_size = best_vtcm_size;
2270
+ act_threads_selected = best_act_threads;
2271
+ use_grouped = true;
2272
+ }
2273
+ }
2274
+
2275
+ if (!use_grouped) {
2276
+ // Fallback to simple 2D path (group_size = 1)
2277
+ size_t best_mblocks = SIZE_MAX;
2278
+ int best_act_threads = 0;
2279
+ size_t best_m_chunk = 0;
2280
+ size_t best_n_chunk = 0;
2281
+ size_t best_vtcm_size = 0;
2282
+
2283
+ // For MUL_MAT_ID the kernel runs one 2D matmul per expert, with M equal to the number of rows routed to that expert.
2284
+ // A single expert can receive up to all routed rows (dst->ne[1]*dst->ne[2] = n_expert_used*n_tokens), so size the chunk
2285
+ // search for that upper bound rather than ne12 (token positions only).
2286
+ // We recompute m_chunk per expert against the actual count in the NPU kernel.
2287
+ const int m_id_rows = (int) ((size_t) dst->ne[1] * dst->ne[2]);
2288
+ const int m_for_chunks = is_matmul_id ? hex_align_up(m_id_rows, 32) : ne11_padded;
2289
+ const int m_for_cost = is_matmul_id ? m_id_rows : ne11;
2290
+
2291
+ int act_threads = n_threads;
2292
+ while (act_threads >= 1) {
2293
+ const size_t act_f32_size = is_matmul_id ? 0 : hex_align_up(act_threads * HTP_MM_DMA_ACT_MULTIPLIER * ne00_padded * sizeof(float), HTP_MM_HMX_TILE_SIZE);
2294
+ size_t simple_2d_overhead = 256 + act_f32_size;
2295
+ size_t simple_2d_size_per_n, simple_2d_size_per_m, simple_2d_size_per_mn;
2296
+ htp_mm_hmx_get_2d_chunk_costs(wtype, ne00_padded, pipeline, aligned_tile_size, &simple_2d_size_per_n, &simple_2d_size_per_m, &simple_2d_size_per_mn);
2297
+
2298
+ size_t m_chunk_candidate = 0;
2299
+ size_t n_chunk_candidate = 0;
2300
+ size_t vtcm_size_candidate = 0;
2301
+
2302
+ if (htp_mm_hmx_compute_chunks(vtcm_budget, simple_2d_overhead, simple_2d_size_per_n, simple_2d_size_per_m, simple_2d_size_per_mn, m_for_chunks, ne01_padded,
2303
+ (size_t) ne01_padded * HTP_MM_HMX_COST_W_DEQUANT, (size_t) m_for_cost * HTP_MM_HMX_COST_A_CONVERT,
2304
+ &m_chunk_candidate, &n_chunk_candidate, &vtcm_size_candidate) == 0) {
2305
+ size_t exact_size = htp_mm_hmx_get_2d_vtcm_size(wtype, ne00_padded, m_chunk_candidate, n_chunk_candidate, pipeline, is_matmul_id ? 0 : act_threads, aligned_tile_size);
2306
+ if (exact_size <= vtcm_budget) {
2307
+ size_t mblocks = ((size_t) m_for_cost + m_chunk_candidate - 1) / m_chunk_candidate;
2308
+ if (mblocks < best_mblocks || (mblocks == best_mblocks && act_threads > best_act_threads)) {
2309
+ best_mblocks = mblocks;
2310
+ best_act_threads = act_threads;
2311
+ best_m_chunk = m_chunk_candidate;
2312
+ best_n_chunk = n_chunk_candidate;
2313
+ best_vtcm_size = exact_size;
2314
+ }
2315
+ }
2316
+ }
2317
+ if (act_threads == 1) {
2318
+ act_threads = 0;
2319
+ } else {
2320
+ act_threads /= 2;
2321
+ }
2322
+ }
2323
+
2324
+ if (best_act_threads > 0) {
2325
+ m_chunk = best_m_chunk;
2326
+ n_chunk = best_n_chunk;
2327
+ vtcm_size = best_vtcm_size;
2328
+ act_threads_selected = best_act_threads;
2329
+ } else {
2330
+ return false;
2331
+ }
2332
+ }
2333
+
2334
+ kparams->n_hmx = 1;
2335
+ kparams->pipeline = pipeline ? 1 : 0;
2336
+ kparams->m_chunk = m_chunk;
2337
+ kparams->n_chunk = n_chunk;
2338
+ kparams->n_threads = n_threads;
2339
+ kparams->n_act_threads = act_threads_selected;
2340
+ kparams->tile_size = htp_mm_get_weight_tile_size(wtype);
2341
+ kparams->aligned_tile_size = aligned_tile_size;
2342
+ kparams->src1_row_size = (wtype == GGML_TYPE_Q4_1) ? htp_mm_q8_1_tiled_row_size(ne10) : htp_mm_q8_0_tiled_row_size(ne10);
2343
+ kparams->vtcm_size = vtcm_size;
2344
+ kparams->vtcm_src0_size = 0;
2345
+ kparams->vtcm_src1_size = 0;
2346
+ kparams->vtcm_dst_size = 0;
2347
+
2348
+ if (is_batched && !is_matmul_id) {
2349
+ kparams->kernel_type = HTP_MM_KERNEL_HMX_F16_BATCHED;
2350
+ } else {
2351
+ kparams->kernel_type = HTP_MM_KERNEL_HMX_2D;
2352
+ }
2353
+ return true;
2354
+ }
2355
+
2356
+ static void ggml_hexagon_precompute_hvx_mm_params(
2357
+ const struct ggml_hexagon_session * sess,
2358
+ const struct ggml_tensor * src0,
2359
+ const struct ggml_tensor * src1,
2360
+ const struct ggml_tensor * dst,
2361
+ int wtype,
2362
+ int ne02,
2363
+ int ne03,
2364
+ int ne10,
2365
+ int ne11,
2366
+ int ne12,
2367
+ int ne13,
2368
+ bool is_matmul_id,
2369
+ size_t vtcm_budget,
2370
+ struct htp_mm_kernel_params * kparams
2371
+ ) {
2372
+ kparams->n_hmx = 0;
2373
+
2374
+ const bool is_quant = (wtype != GGML_TYPE_F16 && wtype != GGML_TYPE_F32);
2375
+ const int src1_nrows = ne11 * ne12 * ne13;
2376
+
2377
+ if (is_quant) {
2378
+ // Quantized HVX
2379
+ kparams->tile_size = htp_mm_get_weight_tile_size(wtype);
2380
+ kparams->aligned_tile_size = htp_mm_get_weight_aligned_tile_size(wtype);
2381
+
2382
+ const bool k_align = (ne10 % 32 == 0);
2383
+
2384
+ if (is_matmul_id) {
2385
+ kparams->kernel_type = (src1_nrows < (int) sess->n_threads) ? HTP_MM_KERNEL_HVX_QUANT_BLOCK : HTP_MM_KERNEL_HVX_QUANT_ROW;
2386
+ kparams->src1_row_size = (wtype == GGML_TYPE_Q4_1) ? htp_mm_q8_1_tiled_row_size(ne10) : htp_mm_q8_0_tiled_row_size(ne10);
2387
+
2388
+ struct htp_mm_hvx_vtcm_layout L;
2389
+ uint32_t max_prefetch = (src1_nrows > HTP_MM_HMX_MIN_NROWS) ? 2 : 16;
2390
+ uint32_t best_n_prefetch = 2;
2391
+ for (uint32_t d = max_prefetch; d >= 2; d /= 2) {
2392
+ htp_mm_hvx_vtcm_layout_build(
2393
+ &L, kparams->kernel_type, wtype, ne10, src1_nrows, sess->n_threads,
2394
+ 0, src0->nb[1], 0, d, true, false, false
2395
+ );
2396
+ if (L.total_bytes <= vtcm_budget) {
2397
+ best_n_prefetch = d;
2398
+ break;
2399
+ }
2400
+ }
2401
+ if (best_n_prefetch == 2 && L.total_bytes > vtcm_budget) {
2402
+ htp_mm_hvx_vtcm_layout_build(
2403
+ &L, kparams->kernel_type, wtype, ne10, src1_nrows, sess->n_threads,
2404
+ 0, src0->nb[1], 0, 2, true, false, false
2405
+ );
2406
+ }
2407
+ kparams->n_prefetch = best_n_prefetch;
2408
+ kparams->vtcm_size = L.total_bytes;
2409
+ kparams->vtcm_src0_size = L.src0_bytes;
2410
+ kparams->vtcm_src1_size = L.src1_bytes;
2411
+ kparams->vtcm_dst_size = L.dst_bytes;
2412
+ } else {
2413
+ bool try_tiled = (k_align && opt_mm_select >= 2);
2414
+ if (try_tiled) {
2415
+ kparams->src1_row_size = (wtype == GGML_TYPE_Q4_1) ? htp_mm_q8_1_tiled_row_size(ne10) : htp_mm_q8_0_tiled_row_size(ne10);
2416
+ if (src1_nrows < (int)sess->n_threads) {
2417
+ kparams->kernel_type = HTP_MM_KERNEL_HVX_QUANT_BLOCK;
2418
+ } else {
2419
+ kparams->kernel_type = HTP_MM_KERNEL_HVX_QUANT_ROW;
2420
+ }
2421
+
2422
+ struct htp_mm_hvx_vtcm_layout L;
2423
+ uint32_t max_prefetch = (src1_nrows > HTP_MM_HMX_MIN_NROWS) ? 2 : 16;
2424
+ uint32_t best_n_prefetch = 2;
2425
+ for (uint32_t d = max_prefetch; d >= 2; d /= 2) {
2426
+ htp_mm_hvx_vtcm_layout_build(
2427
+ &L, kparams->kernel_type, wtype, ne10, src1_nrows, sess->n_threads,
2428
+ dst->nb[1], src0->nb[1], src1->nb[1], d, false, false, false
2429
+ );
2430
+ if (L.total_bytes <= vtcm_budget) {
2431
+ best_n_prefetch = d;
2432
+ break;
2433
+ }
2434
+ }
2435
+ if (best_n_prefetch == 2 && L.total_bytes > vtcm_budget) {
2436
+ htp_mm_hvx_vtcm_layout_build(
2437
+ &L, kparams->kernel_type, wtype, ne10, src1_nrows, sess->n_threads,
2438
+ dst->nb[1], src0->nb[1], src1->nb[1], 2, false, false, false
2439
+ );
2440
+ }
2441
+
2442
+ kparams->n_prefetch = best_n_prefetch;
2443
+
2444
+ if (L.total_bytes <= vtcm_budget) {
2445
+ kparams->vtcm_size = L.total_bytes;
2446
+ kparams->vtcm_src0_size = L.src0_bytes;
2447
+ kparams->vtcm_src1_size = L.src1_bytes;
2448
+ kparams->vtcm_dst_size = L.dst_bytes;
2449
+ goto done_quant;
2450
+ }
2451
+ HEX_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);
2452
+ }
2453
+
2454
+ // Flat HVX fallback
2455
+ {
2456
+ kparams->src1_row_size = (wtype == GGML_TYPE_Q4_1) ? htp_mm_q8_1_flat_row_size(ne10) : htp_mm_q8_0_flat_row_size(ne10);
2457
+ kparams->kernel_type = HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT;
2458
+
2459
+ struct htp_mm_hvx_vtcm_layout L;
2460
+ htp_mm_hvx_vtcm_layout_build(
2461
+ &L, kparams->kernel_type, wtype, ne10, src1_nrows, sess->n_threads,
2462
+ dst->nb[1], src0->nb[1], src1->nb[1], 16, false, false, false
2463
+ );
2464
+
2465
+ kparams->n_prefetch = 16;
2466
+ kparams->vtcm_size = L.total_bytes;
2467
+ kparams->vtcm_src0_size = L.src0_bytes;
2468
+ kparams->vtcm_src1_size = L.src1_bytes;
2469
+ kparams->vtcm_dst_size = L.dst_bytes;
2470
+ }
2471
+ }
2472
+
2473
+ done_quant:;
2474
+ } else if (wtype == GGML_TYPE_F16) {
2475
+ // F16 HVX
2476
+ const bool is_batched = (ne02 > 1) || (ne03 > 1);
2477
+ const bool is_permuted = ggml_is_permuted(src0) || ggml_is_permuted(src1);
2478
+
2479
+ struct htp_mm_hvx_vtcm_layout L;
2480
+ htp_mm_hvx_vtcm_layout_build(
2481
+ &L, HTP_MM_KERNEL_HVX_F16_F16_VTCM, wtype, ne10, src1_nrows, sess->n_threads,
2482
+ dst->nb[1], src0->nb[1], src1->nb[1], 16, false, false, false
2483
+ );
2484
+
2485
+ if (!is_batched && !is_permuted && L.total_bytes <= vtcm_budget) {
2486
+ kparams->kernel_type = HTP_MM_KERNEL_HVX_F16_F16_VTCM;
2487
+ kparams->src1_row_size = hex_round_up(ne10 * 2, 128);
2488
+ kparams->vtcm_size = L.total_bytes;
2489
+ kparams->vtcm_src0_size = L.src0_bytes;
2490
+ kparams->vtcm_src1_size = L.src1_bytes;
2491
+ kparams->vtcm_dst_size = L.dst_bytes;
2492
+ kparams->n_prefetch = 16;
2493
+ } else {
2494
+ if (src1->type == GGML_TYPE_F32) {
2495
+ kparams->kernel_type = HTP_MM_KERNEL_HVX_F16_F32_DDR;
2496
+ } else {
2497
+ kparams->kernel_type = HTP_MM_KERNEL_HVX_F16_F16_DDR;
2498
+ }
2499
+ kparams->src1_row_size = src1->nb[1];
2500
+ htp_mm_hvx_vtcm_layout_build(
2501
+ &L, kparams->kernel_type, wtype, ne10, src1_nrows, sess->n_threads,
2502
+ dst->nb[1], src0->nb[1], src1->nb[1], 16, false, false, false
2503
+ );
2504
+ kparams->vtcm_size = L.total_bytes;
2505
+ kparams->vtcm_src0_size = L.src0_bytes;
2506
+ kparams->vtcm_src1_size = L.src1_bytes;
2507
+ kparams->vtcm_dst_size = L.dst_bytes;
2508
+ kparams->n_prefetch = 16;
2509
+ }
2510
+ } else {
2511
+ // F32 HVX
2512
+ const bool is_batched = (ne02 > 1) || (ne03 > 1);
2513
+ const bool is_permuted = ggml_is_permuted(src0) || ggml_is_permuted(src1);
2514
+
2515
+ struct htp_mm_hvx_vtcm_layout L;
2516
+ htp_mm_hvx_vtcm_layout_build(
2517
+ &L, HTP_MM_KERNEL_HVX_F32_F32_VTCM, wtype, ne10, src1_nrows, sess->n_threads,
2518
+ dst->nb[1], src0->nb[1], src1->nb[1], 16, false, false, false
2519
+ );
2520
+
2521
+ if (!is_batched && !is_permuted && L.total_bytes <= vtcm_budget) {
2522
+ kparams->kernel_type = HTP_MM_KERNEL_HVX_F32_F32_VTCM;
2523
+ kparams->src1_row_size = hex_round_up(ne10 * 4, 128);
2524
+ kparams->vtcm_size = L.total_bytes;
2525
+ kparams->vtcm_src0_size = L.src0_bytes;
2526
+ kparams->vtcm_src1_size = L.src1_bytes;
2527
+ kparams->vtcm_dst_size = L.dst_bytes;
2528
+ kparams->n_prefetch = 16;
2529
+ } else {
2530
+ kparams->kernel_type = HTP_MM_KERNEL_HVX_F32_F32_DDR;
2531
+ kparams->src1_row_size = src1->nb[1];
2532
+ htp_mm_hvx_vtcm_layout_build(
2533
+ &L, kparams->kernel_type, wtype, ne10, src1_nrows, sess->n_threads,
2534
+ dst->nb[1], src0->nb[1], src1->nb[1], 16, false, false, false
2535
+ );
2536
+ kparams->vtcm_size = L.total_bytes;
2537
+ kparams->vtcm_src0_size = L.src0_bytes;
2538
+ kparams->vtcm_src1_size = L.src1_bytes;
2539
+ kparams->vtcm_dst_size = L.dst_bytes;
2540
+ kparams->n_prefetch = 16;
2541
+ }
2542
+ }
2543
+ }
2544
+
2545
+ static void ggml_hexagon_precompute_matmul_params(
2546
+ const struct ggml_hexagon_session * sess,
2547
+ const struct ggml_tensor * src0,
2548
+ const struct ggml_tensor * src1,
2549
+ const struct ggml_tensor * dst,
2550
+ struct htp_mm_kernel_params * kparams
2551
+ ) {
2552
+ memset(kparams, 0, sizeof(*kparams));
2553
+
2554
+ const int ne00 = src0->ne[0];
2555
+ const int ne01 = src0->ne[1];
2556
+ const int ne02 = src0->ne[2];
2557
+ const int ne03 = src0->ne[3];
2558
+
2559
+ const int ne10 = src1->ne[0];
2560
+ const int ne11 = src1->ne[1];
2561
+ const int ne12 = src1->ne[2];
2562
+ const int ne13 = src1->ne[3];
2563
+
2564
+ const int wtype = src0->type;
2565
+ const bool is_repack = ggml_hexagon_is_repack_type((ggml_type) wtype);
2566
+ const int ne00_padded = is_repack ? hex_round_up(ne00, 32) : ne00;
2567
+ const int ne01_padded = is_repack ? hex_round_up(ne01, 32) : ne01;
2568
+ const int ne11_padded = hex_round_up(ne11, 32);
2569
+
2570
+ const bool is_matmul_id = (dst->op == GGML_OP_MUL_MAT_ID);
2571
+ const bool is_batched = (ne02 * ne03 > 1 || ne12 * ne13 > 1);
2572
+
2573
+ const size_t vtcm_budget = sess->vtcm_size;
2574
+
2575
+ // Check HMX eligibility and try precomputing HMX parameters
2576
+ bool hmx_enabled = (sess->n_hmx > 0) && (opt_mm_select >= 3);
2577
+ if (hmx_enabled && ggml_hexagon_matmul_is_hmx_eligible(src0, src1, dst, ne01_padded, is_matmul_id, is_batched)) {
2578
+ if (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)) {
2579
+ goto finalize;
2580
+ }
2581
+ }
2582
+
2583
+ // Fallback to HVX parameter computation
2584
+ ggml_hexagon_precompute_hvx_mm_params(sess, src0, src1, dst, wtype, ne02, ne03, ne10, ne11, ne12, ne13, is_matmul_id, vtcm_budget, kparams);
2585
+
2586
+ finalize:
2587
+ kparams->div_ne12_ne1 = init_fastdiv_values(ne12 * ne11);
2588
+ kparams->div_ne1 = init_fastdiv_values(ne11);
2589
+ kparams->div_r2 = init_fastdiv_values(ne02 > 0 ? ne12 / ne02 : 1);
2590
+ kparams->div_r3 = init_fastdiv_values(ne03 > 0 ? ne13 / ne03 : 1);
2591
+ kparams->div_ne11 = init_fastdiv_values(ne11);
2592
+ }
2593
+
2594
+ static void ggml_hexagon_precompute_fused_qkv_params(
2595
+ const struct ggml_hexagon_session * sess,
2596
+ const struct ggml_tensor * src0, // Wk
2597
+ const struct ggml_tensor * src1, // x
2598
+ struct htp_mm_kernel_params * kparams
2599
+ ) {
2600
+ memset(kparams, 0, sizeof(*kparams));
2601
+
2602
+ const int wtype = src0->type;
2603
+ const bool is_repack = ggml_hexagon_is_repack_type((ggml_type) wtype);
2604
+
2605
+ const int ne10 = src1->ne[0];
2606
+ const int src1_nrows = src1->ne[1] * src1->ne[2] * src1->ne[3];
2607
+ const 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);
2608
+ const size_t src0_row_size = src0->nb[1];
2609
+
2610
+ uint32_t best_n_prefetch = 16;
2611
+
2612
+ if (is_repack) {
2613
+ const uint32_t max_prefetch = (src1_nrows > HTP_MM_HMX_MIN_NROWS) ? 2 : 16;
2614
+ best_n_prefetch = 2;
2615
+ for (uint32_t d = max_prefetch; d >= 2; d /= 2) {
2616
+ struct htp_mm_hvx_vtcm_layout L;
2617
+ htp_mm_hvx_vtcm_layout_build(
2618
+ &L, HTP_MM_KERNEL_HVX_QUANT_ROW, wtype, ne10, src1_nrows, sess->n_threads,
2619
+ 0, src0_row_size, src1_row_size, d, false, true, false
2620
+ );
2621
+ if (L.total_bytes <= sess->vtcm_size) {
2622
+ best_n_prefetch = d;
2623
+ break;
2624
+ }
2625
+ }
2626
+ }
2627
+
2628
+ struct htp_mm_hvx_vtcm_layout L;
2629
+ bool try_tiled = (opt_mm_select >= 2);
2630
+
2631
+ // Test tiled first
2632
+ htp_mm_hvx_vtcm_layout_build(
2633
+ &L, HTP_MM_KERNEL_HVX_QUANT_ROW, wtype, ne10, src1_nrows, sess->n_threads,
2634
+ 0, src0_row_size, src1_row_size, best_n_prefetch, false, true, false
2635
+ );
2636
+
2637
+ if (try_tiled && L.total_bytes <= sess->vtcm_size) {
2638
+ kparams->kernel_type = HTP_MM_KERNEL_HVX_QUANT_ROW;
2639
+ kparams->vtcm_src0_size = L.src0_bytes;
2640
+ kparams->vtcm_src1_size = L.src1_bytes;
2641
+ kparams->vtcm_src2_size = L.src2_bytes;
2642
+ kparams->vtcm_src3_size = L.src3_bytes;
2643
+ kparams->vtcm_dst_size = L.dst_bytes;
2644
+ kparams->vtcm_size = L.total_bytes;
2645
+ kparams->n_prefetch = best_n_prefetch;
2646
+ } else {
2647
+ kparams->kernel_type = HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT;
2648
+ size_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);
2649
+
2650
+ htp_mm_hvx_vtcm_layout_build(
2651
+ &L, HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT, wtype, ne10, src1_nrows, sess->n_threads,
2652
+ 0, src0_row_size, flat_src1_row_size, best_n_prefetch, false, true, false
2653
+ );
2654
+ kparams->vtcm_src0_size = L.src0_bytes;
2655
+ kparams->vtcm_src1_size = L.src1_bytes;
2656
+ kparams->vtcm_src2_size = L.src2_bytes;
2657
+ kparams->vtcm_src3_size = L.src3_bytes;
2658
+ kparams->vtcm_dst_size = L.dst_bytes;
2659
+ kparams->vtcm_size = L.total_bytes;
2660
+ kparams->n_prefetch = best_n_prefetch;
2661
+ }
2662
+ }
2663
+
2664
+ static void ggml_hexagon_precompute_fused_ffn_params(
2665
+ const struct ggml_hexagon_session * sess,
2666
+ const struct ggml_tensor * src0, // Wgate
2667
+ const struct ggml_tensor * src1, // y
2668
+ struct htp_mm_kernel_params * kparams
2669
+ ) {
2670
+ memset(kparams, 0, sizeof(*kparams));
2671
+
2672
+ const int wtype = src0->type;
2673
+ const bool is_repack = ggml_hexagon_is_repack_type((ggml_type) wtype);
2674
+
2675
+ const int ne10 = src1->ne[0];
2676
+ const int src1_nrows = src1->ne[1] * src1->ne[2] * src1->ne[3];
2677
+ const 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);
2678
+ const size_t src0_row_size = src0->nb[1];
2679
+
2680
+ uint32_t best_n_prefetch = 16;
2681
+
2682
+ if (is_repack) {
2683
+ const uint32_t max_prefetch = (src1_nrows > HTP_MM_HMX_MIN_NROWS) ? 2 : 16;
2684
+ best_n_prefetch = 2;
2685
+ for (uint32_t d = max_prefetch; d >= 2; d /= 2) {
2686
+ struct htp_mm_hvx_vtcm_layout L;
2687
+ htp_mm_hvx_vtcm_layout_build(
2688
+ &L, HTP_MM_KERNEL_HVX_QUANT_ROW, wtype, ne10, src1_nrows, sess->n_threads,
2689
+ 0, src0_row_size, src1_row_size, d, false, false, true
2690
+ );
2691
+ if (L.total_bytes <= sess->vtcm_size) {
2692
+ best_n_prefetch = d;
2693
+ break;
2694
+ }
2695
+ }
2696
+ }
2697
+
2698
+ struct htp_mm_hvx_vtcm_layout L;
2699
+ bool try_tiled = (opt_mm_select >= 2);
2700
+
2701
+ // Test tiled first
2702
+ htp_mm_hvx_vtcm_layout_build(
2703
+ &L, HTP_MM_KERNEL_HVX_QUANT_ROW, wtype, ne10, src1_nrows, sess->n_threads,
2704
+ 0, src0_row_size, src1_row_size, best_n_prefetch, false, false, true
2705
+ );
2706
+
2707
+ if (try_tiled && L.total_bytes <= sess->vtcm_size) {
2708
+ kparams->kernel_type = HTP_MM_KERNEL_HVX_QUANT_ROW;
2709
+ kparams->vtcm_src0_size = L.src0_bytes;
2710
+ kparams->vtcm_src1_size = L.src1_bytes;
2711
+ kparams->vtcm_src2_size = L.src2_bytes;
2712
+ kparams->vtcm_dst_size = L.dst_bytes;
2713
+ kparams->vtcm_size = L.total_bytes;
2714
+ kparams->n_prefetch = best_n_prefetch;
2715
+ } else {
2716
+ kparams->kernel_type = HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT;
2717
+ size_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);
2718
+
2719
+ htp_mm_hvx_vtcm_layout_build(
2720
+ &L, HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT, wtype, ne10, src1_nrows, sess->n_threads,
2721
+ 0, src0_row_size, flat_src1_row_size, best_n_prefetch, false, false, true
2722
+ );
2723
+ kparams->vtcm_src0_size = L.src0_bytes;
2724
+ kparams->vtcm_src1_size = L.src1_bytes;
2725
+ kparams->vtcm_src2_size = L.src2_bytes;
2726
+ kparams->vtcm_dst_size = L.dst_bytes;
2727
+ kparams->vtcm_size = L.total_bytes;
2728
+ kparams->n_prefetch = best_n_prefetch;
2729
+ }
2730
+ }
2731
+
2566
2732
  static bool ggml_hexagon_supported_mul_mat(const struct ggml_hexagon_session * sess, const struct ggml_tensor * dst) {
2567
2733
  const struct ggml_tensor * src0 = dst->src[0];
2568
2734
  const struct ggml_tensor * src1 = dst->src[1];
@@ -2585,12 +2751,13 @@ static bool ggml_hexagon_supported_mul_mat(const struct ggml_hexagon_session * s
2585
2751
  return false;
2586
2752
  }
2587
2753
 
2588
- if (ggml_nrows(src0) > 16 * 1024) {
2589
- return false; // typically the lm-head which would be too large for VTCM
2754
+ // hardcoded limit to refuse the lm-head for now
2755
+ if (src0->ne[1] > 32768) {
2756
+ return false;
2590
2757
  }
2591
2758
 
2592
- if (ggml_nrows(src1) > 1024 || src1->ne[2] != 1 || src1->ne[3] != 1) {
2593
- return false; // no huge batches or broadcasting (for now)
2759
+ if (src1->ne[2] != 1 || src1->ne[3] != 1) {
2760
+ return false; // no broadcasting (for now)
2594
2761
  }
2595
2762
 
2596
2763
  // src0 (weights) must be repacked
@@ -2601,16 +2768,11 @@ static bool ggml_hexagon_supported_mul_mat(const struct ggml_hexagon_session * s
2601
2768
 
2602
2769
  case GGML_TYPE_F16:
2603
2770
  if (src0->nb[1] < src0->nb[0]) {
2604
- GGML_LOG_DEBUG("ggml_hexagon_supported_mul_mat: permuted F16 src0 not supported\n");
2605
2771
  return false;
2606
2772
  }
2607
2773
  if (src1->ne[2] < src0->ne[2] || src1->ne[3] < src0->ne[3]) {
2608
- GGML_LOG_DEBUG("ggml_hexagon_supported_mul_mat: src1 broadcasting not supported\n");
2609
2774
  return false;
2610
2775
  }
2611
- if (ggml_nrows(src1) > 1024) {
2612
- return false; // no huge batches (for now)
2613
- }
2614
2776
  break;
2615
2777
 
2616
2778
  case GGML_TYPE_F32:
@@ -2618,22 +2780,24 @@ static bool ggml_hexagon_supported_mul_mat(const struct ggml_hexagon_session * s
2618
2780
  return false;
2619
2781
  }
2620
2782
  if (src0->nb[1] < src0->nb[0]) {
2621
- GGML_LOG_DEBUG("ggml_hexagon_supported_mul_mat: permuted F32 src0 not supported\n");
2622
2783
  return false;
2623
2784
  }
2624
2785
  if (src1->ne[2] < src0->ne[2] || src1->ne[3] < src0->ne[3]) {
2625
- GGML_LOG_DEBUG("ggml_hexagon_supported_mul_mat: src1 broadcasting not supported\n");
2626
2786
  return false;
2627
2787
  }
2628
- if (ggml_nrows(src1) > 1024) {
2629
- return false; // no huge batches (for now)
2630
- }
2631
2788
  break;
2632
2789
 
2633
2790
  default:
2634
2791
  return false;
2635
2792
  }
2636
2793
 
2794
+ struct htp_mm_kernel_params kparams;
2795
+ ggml_hexagon_precompute_matmul_params(sess, src0, src1, dst, &kparams);
2796
+ if ((size_t)kparams.vtcm_size > sess->vtcm_size) {
2797
+ HEX_VERBOSE("ggml-hex: %s supported MUL_MAT VTCM size needed (%d) > budget (%zu)\n", sess->c_name(), kparams.vtcm_size, sess->vtcm_size);
2798
+ return false;
2799
+ }
2800
+
2637
2801
  return true;
2638
2802
  }
2639
2803
 
@@ -2667,6 +2831,13 @@ static bool ggml_hexagon_supported_mul_mat_id(const struct ggml_hexagon_session
2667
2831
  return false;
2668
2832
  }
2669
2833
 
2834
+ struct htp_mm_kernel_params kparams;
2835
+ ggml_hexagon_precompute_matmul_params(sess, src0, src1, dst, &kparams);
2836
+ if ((size_t)kparams.vtcm_size > sess->vtcm_size) {
2837
+ HEX_VERBOSE("ggml-hex: %s supported MUL_MAT_ID VTCM size needed (%d) > budget (%zu)\n", sess->c_name(), kparams.vtcm_size, sess->vtcm_size);
2838
+ return false;
2839
+ }
2840
+
2670
2841
  return true;
2671
2842
  }
2672
2843
 
@@ -2934,8 +3105,12 @@ static bool ggml_hexagon_supported_rope(const struct ggml_hexagon_session * sess
2934
3105
 
2935
3106
  int mode = op_params[2];
2936
3107
 
3108
+ // n_dims == ne0/2, so the rotation spans the full row
2937
3109
  if (mode == GGML_ROPE_TYPE_VISION) {
2938
- return false;
3110
+ const int n_dims = op_params[1];
3111
+ if (n_dims != (int) (op->src[0]->ne[0] / 2)) {
3112
+ return false;
3113
+ }
2939
3114
  }
2940
3115
  if (mode & 1) {
2941
3116
  return false;
@@ -2966,16 +3141,23 @@ static bool ggml_hexagon_supported_rope(const struct ggml_hexagon_session * sess
2966
3141
  }
2967
3142
 
2968
3143
  if (src2) {
2969
- if (!ggml_is_contiguous(src0) || !ggml_is_contiguous(src1) || !ggml_is_contiguous(src2) ||
2970
- !ggml_is_contiguous(dst)) {
3144
+ if (!ggml_is_contiguous(src1) || !ggml_is_contiguous(src2)) {
2971
3145
  return false;
2972
3146
  }
2973
3147
  } else {
2974
- if (!ggml_is_contiguous(src0) || !ggml_is_contiguous(src1) || !ggml_is_contiguous(dst)) {
3148
+ if (!ggml_is_contiguous(src1)) {
2975
3149
  return false;
2976
3150
  }
2977
3151
  }
2978
3152
 
3153
+ // src0/dst elements within a row must be contiguous (nb[0] == sizeof(float)).
3154
+ // nb[1] may exceed ne[0]*sizeof(float) when the tensor is a strided view of a larger one
3155
+ if (src0->nb[0] != sizeof(float) || dst->nb[0] != sizeof(float)) {
3156
+ return false;
3157
+ }
3158
+ if (src0->nb[1] < src0->ne[0] * sizeof(float) || dst->nb[1] < dst->ne[0] * sizeof(float)) {
3159
+ return false;
3160
+ }
2979
3161
  return true;
2980
3162
  }
2981
3163
 
@@ -3198,47 +3380,197 @@ static inline bool op_is_compute(ggml_tensor *node)
3198
3380
  return !ggml_op_is_empty(node->op) && !ggml_is_empty(node) && (node->flags & GGML_TENSOR_FLAG_COMPUTE);
3199
3381
  }
3200
3382
 
3383
+ static bool mm_is_hmx_eligible(const ggml_tensor * t) {
3384
+ if (opt_nhmx == 0) { return false; }
3385
+
3386
+ const ggml_tensor * src0 = t->src[0];
3387
+ const ggml_tensor * src1 = t->src[1];
3388
+
3389
+ const int wtype = src0->type;
3390
+ const bool is_repack = ggml_hexagon_is_repack_type((ggml_type) wtype);
3391
+ const bool is_matmul_id = (t->op == GGML_OP_MUL_MAT_ID);
3392
+ const bool is_batched = (src0->ne[2] * src0->ne[3] > 1 || src1->ne[2] * src1->ne[3] > 1);
3393
+
3394
+ const int ne01_padded = is_repack ? hex_round_up(src0->ne[1], 32) : src0->ne[1];
3395
+
3396
+ return ggml_hexagon_matmul_is_hmx_eligible(src0, src1, t, ne01_padded, is_matmul_id, is_batched);
3397
+ }
3398
+
3399
+ static bool is_mergeable_mul_mat(const ggml_tensor * t) {
3400
+ if (!t || t->op != GGML_OP_MUL_MAT) return false;
3401
+ if (t->src[1]->type != GGML_TYPE_F32) return false;
3402
+ return ggml_is_quantized(t->src[0]->type) && !mm_is_hmx_eligible(t);
3403
+ }
3404
+
3405
+ static bool is_mergeable_mul_mat_pair(const ggml_tensor * n1, const ggml_tensor * n2) {
3406
+ if (!is_mergeable_mul_mat(n1) || !is_mergeable_mul_mat(n2)) {
3407
+ return false;
3408
+ }
3409
+ if (n1->src[1] != n2->src[1]) {
3410
+ return false;
3411
+ }
3412
+ if (n1->src[0]->ne[0] != n2->src[0]->ne[0] ||
3413
+ n1->src[0]->ne[1] != n2->src[0]->ne[1]) {
3414
+ return false;
3415
+ }
3416
+ if (n1->src[0]->type != n2->src[0]->type) {
3417
+ return false;
3418
+ }
3419
+ return true;
3420
+ }
3421
+
3422
+ static bool is_qkv_mergeable(const ggml_tensor * n_q, const ggml_tensor * n_k, const ggml_tensor * n_v) {
3423
+ if (!is_mergeable_mul_mat(n_q) || !is_mergeable_mul_mat(n_k) || !is_mergeable_mul_mat(n_v)) {
3424
+ return false;
3425
+ }
3426
+ if (n_q->src[1] != n_k->src[1] || n_q->src[1] != n_v->src[1]) {
3427
+ return false;
3428
+ }
3429
+ if (n_q->src[0]->type != n_k->src[0]->type || n_q->src[0]->type != n_v->src[0]->type) {
3430
+ return false;
3431
+ }
3432
+ if (n_k->src[0]->ne[0] != n_v->src[0]->ne[0] ||
3433
+ n_k->src[0]->ne[1] != n_v->src[0]->ne[1]) {
3434
+ return false;
3435
+ }
3436
+ if (n_q->src[0]->ne[0] != n_k->src[0]->ne[0]) {
3437
+ return false;
3438
+ }
3439
+ return true;
3440
+ }
3441
+
3442
+ static bool try_fuse_node(const ggml_hexagon_session * sess, const ggml_cgraph * graph, int & i, std::vector<htp_opnode> & nodes) {
3443
+ if (!opt_opfusion) {
3444
+ return false;
3445
+ }
3446
+
3447
+ ggml_tensor * n = graph->nodes[i];
3448
+ ggml_tensor * next_node = (i + 1 < graph->n_nodes) ? graph->nodes[i + 1] : nullptr;
3449
+
3450
+ if (n->op == GGML_OP_RMS_NORM && next_node) {
3451
+ if (next_node->op == GGML_OP_MUL && op_is_compute(next_node) && ggml_can_fuse(graph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL })) {
3452
+ htp_opnode node(n, {}, HTP_OP_RMS_NORM_MUL);
3453
+ node.add_fused(next_node);
3454
+ nodes.push_back(std::move(node));
3455
+ i++; // skip the fused MUL node
3456
+ return true;
3457
+ }
3458
+ }
3459
+
3460
+ if (is_mergeable_mul_mat(n)) {
3461
+ ggml_tensor * n1 = (i + 1 < graph->n_nodes) ? graph->nodes[i + 1] : nullptr;
3462
+ ggml_tensor * n2 = (i + 2 < graph->n_nodes) ? graph->nodes[i + 2] : nullptr;
3463
+ if (is_qkv_mergeable(n, n1, n2)) {
3464
+ struct htp_mm_kernel_params kparams;
3465
+ ggml_hexagon_precompute_fused_qkv_params(sess, n1->src[0], n1->src[1], &kparams);
3466
+ if ((size_t)kparams.vtcm_size <= sess->vtcm_size) {
3467
+ // Reorder to KVQ: K (n1), V (n2), Q (n)
3468
+ htp_opnode node(n1, {}, HTP_OP_MUL_MAT_QKV);
3469
+ node.add_fused(n2, true);
3470
+ node.add_fused(n, true);
3471
+ memcpy(node.kernel_params, &kparams, sizeof(kparams));
3472
+ nodes.push_back(std::move(node));
3473
+ i += 2;
3474
+ return true;
3475
+ } else {
3476
+ HEX_VERBOSE("ggml-hex: skip QKV fusion because VTCM needed (%d) > budget (%zu)\n",
3477
+ kparams.vtcm_size, sess->vtcm_size);
3478
+ }
3479
+ }
3480
+ if (is_mergeable_mul_mat_pair(n, n1)) {
3481
+ struct htp_mm_kernel_params kparams;
3482
+ ggml_hexagon_precompute_fused_ffn_params(sess, n->src[0], n->src[1], &kparams);
3483
+ if ((size_t)kparams.vtcm_size <= sess->vtcm_size) {
3484
+ htp_opnode node(n, {}, HTP_OP_MUL_MAT_FFN);
3485
+ node.add_fused(n1, true);
3486
+ memcpy(node.kernel_params, &kparams, sizeof(kparams));
3487
+ nodes.push_back(std::move(node));
3488
+ i += 1;
3489
+ return true;
3490
+ } else {
3491
+ HEX_VERBOSE("ggml-hex: skip FFN fusion because VTCM needed (%d) > budget (%zu)\n",
3492
+ kparams.vtcm_size, sess->vtcm_size);
3493
+ }
3494
+ }
3495
+ }
3496
+
3497
+ if (n->op == GGML_OP_MUL_MAT && next_node) {
3498
+ if (next_node->op == GGML_OP_ADD && op_is_compute(next_node) && ggml_can_fuse(graph, i, { GGML_OP_MUL_MAT, GGML_OP_ADD })) {
3499
+ if (next_node->src[0] == n || next_node->src[1] == n) {
3500
+ struct htp_mm_kernel_params kparams;
3501
+ ggml_hexagon_precompute_matmul_params(sess, n->src[0], n->src[1], next_node, &kparams);
3502
+ if ((size_t)kparams.vtcm_size <= sess->vtcm_size) {
3503
+ htp_opnode node(n, {}, HTP_OP_MUL_MAT_ADD);
3504
+ node.add_fused(next_node);
3505
+ memcpy(node.kernel_params, &kparams, sizeof(kparams));
3506
+ nodes.push_back(std::move(node));
3507
+ i += 1;
3508
+ return true;
3509
+ } else {
3510
+ HEX_VERBOSE("ggml-hex: skip MUL_MAT_ADD fusion because VTCM needed (%d) > budget (%zu)\n",
3511
+ kparams.vtcm_size, sess->vtcm_size);
3512
+ }
3513
+ }
3514
+ }
3515
+ }
3516
+
3517
+ return false;
3518
+ }
3519
+
3201
3520
  static ggml_status ggml_backend_hexagon_graph_compute(ggml_backend_t backend, ggml_cgraph * graph) {
3202
3521
  auto sess = static_cast<ggml_hexagon_session *>(backend->context);
3203
3522
 
3204
3523
  HEX_VERBOSE("ggml-hex: %s graph-compute n_nodes %d\n", sess->c_name(), graph->n_nodes);
3205
3524
 
3206
- std::vector<htp_opnode> nodes;
3207
- nodes.reserve(graph->n_nodes);
3208
-
3209
- // Fusion
3210
- for (int i = 0; i < graph->n_nodes; ++i) {
3211
- ggml_tensor * n = graph->nodes[i];
3212
- if (!op_is_compute(n)) {
3213
- continue;
3214
- }
3525
+ const std::vector<htp_opnode> * nodes_ptr = nullptr;
3526
+ std::vector<htp_opnode> computed_nodes;
3215
3527
 
3216
- ggml_tensor * next_node = (i + 1 < graph->n_nodes) ? graph->nodes[i + 1] : nullptr;
3528
+ // Check for cache hit
3529
+ bool cache_hit = (graph->uid != 0 && sess->cached_graph.uid == graph->uid);
3530
+ if (cache_hit) {
3531
+ nodes_ptr = &sess->cached_graph.htp_nodes;
3532
+ } else {
3533
+ computed_nodes.reserve(graph->n_nodes);
3217
3534
 
3218
- htp_opnode node = {
3219
- /*.node =*/ n,
3220
- /*.fused =*/ {},
3221
- /*.opcode =*/ HTP_OP_INVALID
3222
- };
3535
+ // Fuse and finalize
3536
+ for (int i = 0; i < graph->n_nodes; ++i) {
3537
+ ggml_tensor * n = graph->nodes[i];
3538
+ if (!op_is_compute(n)) {
3539
+ continue;
3540
+ }
3223
3541
 
3224
- if (n->op == GGML_OP_RMS_NORM && next_node) {
3225
- if (next_node->op == GGML_OP_MUL && op_is_compute(next_node) && ggml_can_fuse(graph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL })) {
3226
- node.add_fused(next_node);
3227
- node.opcode = HTP_OP_RMS_NORM_MUL;
3228
- i++; // skip the fused MUL node
3542
+ if (try_fuse_node(sess, graph, i, computed_nodes)) {
3543
+ continue;
3229
3544
  }
3230
- }
3231
3545
 
3232
- if (node.opcode == HTP_OP_INVALID) {
3546
+ htp_opnode node(n, {}, HTP_OP_INVALID);
3233
3547
  node.opcode = op_remap_to_htp(n);
3548
+ if (node.opcode == HTP_OP_MUL_MAT || node.opcode == HTP_OP_MUL_MAT_ID) {
3549
+ ggml_hexagon_precompute_matmul_params(sess,
3550
+ node.node->src[0], node.node->src[1], node.node,
3551
+ (struct htp_mm_kernel_params *)node.kernel_params
3552
+ );
3553
+ } else if (node.opcode == HTP_OP_FLASH_ATTN_EXT) {
3554
+ ggml_hexagon_precompute_flash_attn_params(sess,
3555
+ node.node,
3556
+ (struct htp_fa_kernel_params *)node.kernel_params
3557
+ );
3558
+ }
3559
+ computed_nodes.push_back(std::move(node));
3234
3560
  }
3235
3561
 
3236
- nodes.push_back(std::move(node));
3562
+ if (graph->uid != 0) {
3563
+ sess->cached_graph.uid = graph->uid;
3564
+ sess->cached_graph.htp_nodes = std::move(computed_nodes);
3565
+ nodes_ptr = &sess->cached_graph.htp_nodes;
3566
+ } else {
3567
+ nodes_ptr = &computed_nodes;
3568
+ }
3237
3569
  }
3238
3570
 
3239
3571
  // Queue and execute
3240
3572
  if (opt_opstage & HTP_OPSTAGE_QUEUE) {
3241
- for (const auto & node : nodes) {
3573
+ for (const auto & node : *nodes_ptr) {
3242
3574
  sess->enqueue_op(node);
3243
3575
  }
3244
3576
  }
@@ -3901,15 +4233,20 @@ static void ggml_hexagon_init(ggml_backend_reg * reg) {
3901
4233
  const char * str_opbatch = getenv("GGML_HEXAGON_OPBATCH");
3902
4234
  const char * str_opqueue = getenv("GGML_HEXAGON_OPQUEUE");
3903
4235
  const char * str_oppoll = getenv("GGML_HEXAGON_OPPOLL");
4236
+ const char * str_opfusion = getenv("GGML_HEXAGON_OPFUSION");
3904
4237
  const char * str_opfilter = getenv("GGML_HEXAGON_OPFILTER");
3905
4238
  const char * str_profile = getenv("GGML_HEXAGON_PROFILE");
3906
4239
  const char * str_etm = getenv("GGML_HEXAGON_ETM");
3907
4240
  const char * str_nhvx = getenv("GGML_HEXAGON_NHVX");
3908
4241
  const char * str_use_hmx = getenv("GGML_HEXAGON_USE_HMX");
4242
+ const char * str_nhmx = getenv("GGML_HEXAGON_NHMX");
4243
+ const char * str_mm_select = getenv("GGML_HEXAGON_MM_SELECT");
4244
+ const char * str_fa_select = getenv("GGML_HEXAGON_FA_SELECT");
3909
4245
  const char * str_ndev = getenv("GGML_HEXAGON_NDEV");
3910
4246
  const char * str_arch = getenv("GGML_HEXAGON_ARCH");
3911
4247
  const char * str_vmem = getenv("GGML_HEXAGON_VMEM");
3912
4248
  const char * str_mbuf = getenv("GGML_HEXAGON_MBUF");
4249
+ const char * str_optrace = getenv("GGML_HEXAGON_OPTRACE");
3913
4250
 
3914
4251
  // Init Arch first since it affects other defaults
3915
4252
  if (!str_arch) {
@@ -3938,11 +4275,15 @@ static void ggml_hexagon_init(ggml_backend_reg * reg) {
3938
4275
  opt_opstage = str_opstage ? strtoul(str_opstage, NULL, 0) : opt_opstage;
3939
4276
  opt_opbatch = str_opbatch ? strtoul(str_opbatch, NULL, 0) : opt_opbatch;
3940
4277
  opt_opqueue = str_opqueue ? strtoul(str_opqueue, NULL, 0) : opt_opqueue;
4278
+ opt_optrace = str_optrace ? strtoul(str_optrace, NULL, 0) : (opt_opbatch * 128);
3941
4279
  opt_oppoll = str_oppoll ? strtoul(str_oppoll, NULL, 0) : opt_oppoll;
4280
+ opt_opfusion = str_opfusion ? atoi(str_opfusion) : opt_opfusion;
3942
4281
  opt_profile = str_profile ? atoi(str_profile) : 0;
3943
4282
  opt_etm = str_etm ? atoi(str_etm) : 0;
3944
4283
  opt_nhvx = str_nhvx ? strtoul(str_nhvx, NULL, 0) : opt_nhvx;
3945
- opt_use_hmx = str_use_hmx ? atoi(str_use_hmx) : opt_use_hmx;
4284
+ opt_nhmx = str_nhmx ? atoi(str_nhmx) : (str_use_hmx ? atoi(str_use_hmx) : opt_nhmx);
4285
+ opt_mm_select = str_mm_select ? atoi(str_mm_select) : opt_mm_select;
4286
+ opt_fa_select = str_fa_select ? atoi(str_fa_select) : opt_fa_select;
3946
4287
  opt_ndev = str_ndev ? strtoul(str_ndev, NULL, 0) : opt_ndev;
3947
4288
  opt_hostbuf = str_hostbuf ? atoi(str_hostbuf) : opt_hostbuf;
3948
4289
  opt_mbuf = str_mbuf ? strtoul(str_mbuf, NULL, 0) * MiB : opt_mbuf;