usearch 2.21.3 → 2.25.0

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 (602) hide show
  1. package/README.md +24 -24
  2. package/binding.gyp +15 -37
  3. package/c/CMakeLists.txt +4 -5
  4. package/c/README.md +7 -7
  5. package/c/lib.cpp +32 -14
  6. package/c/test.c +83 -1
  7. package/c/usearch.h +24 -3
  8. package/cpp/CMakeLists.txt +4 -0
  9. package/cpp/README.md +5 -5
  10. package/cpp/bench.cpp +97 -116
  11. package/cpp/test.cpp +117 -20
  12. package/include/usearch/index.hpp +235 -52
  13. package/include/usearch/index_dense.hpp +105 -40
  14. package/include/usearch/index_plugins.hpp +1387 -204
  15. package/javascript/dist/cjs/usearch.d.ts +8 -3
  16. package/javascript/dist/cjs/usearch.js +12 -4
  17. package/javascript/dist/esm/usearch.d.ts +8 -3
  18. package/javascript/dist/esm/usearch.js +12 -4
  19. package/javascript/lib.cpp +17 -0
  20. package/javascript/usearch.ts +14 -6
  21. package/numkong/.clang-tidy +30 -0
  22. package/{simsimd → numkong}/.cmake-format.py +18 -0
  23. package/numkong/.editorconfig +12 -0
  24. package/numkong/.git-blame-ignore-revs +123 -0
  25. package/numkong/.mailmap +15 -0
  26. package/numkong/.spi.yml +11 -0
  27. package/numkong/.swift-format +14 -0
  28. package/numkong/CITATION.cff +81 -0
  29. package/numkong/CONTRIBUTING.md +500 -0
  30. package/numkong/Cargo.toml +54 -0
  31. package/numkong/MANIFEST.in +8 -0
  32. package/numkong/Package.swift +84 -0
  33. package/numkong/README.md +569 -0
  34. package/numkong/bench/README.md +217 -0
  35. package/numkong/bench/bench-browser.html +323 -0
  36. package/numkong/bench/bench.cpp +344 -0
  37. package/numkong/bench/bench.hpp +816 -0
  38. package/numkong/bench/bench.js +148 -0
  39. package/numkong/bench/bench.mjs +616 -0
  40. package/numkong/bench/bench_attention.cpp +113 -0
  41. package/numkong/bench/bench_cast.cpp +264 -0
  42. package/numkong/bench/bench_cross_amx.cpp +62 -0
  43. package/numkong/bench/bench_cross_arm.cpp +245 -0
  44. package/numkong/bench/bench_cross_blas.cpp +309 -0
  45. package/numkong/bench/bench_cross_loongarch.cpp +106 -0
  46. package/numkong/bench/bench_cross_power.cpp +84 -0
  47. package/numkong/bench/bench_cross_rvv.cpp +121 -0
  48. package/numkong/bench/bench_cross_serial.cpp +151 -0
  49. package/numkong/bench/bench_cross_sme.cpp +163 -0
  50. package/numkong/bench/bench_cross_wasm.cpp +142 -0
  51. package/numkong/bench/bench_cross_x86.cpp +455 -0
  52. package/numkong/bench/bench_curved.cpp +210 -0
  53. package/numkong/bench/bench_dot.cpp +315 -0
  54. package/numkong/bench/bench_each.cpp +503 -0
  55. package/numkong/bench/bench_geospatial.cpp +112 -0
  56. package/numkong/bench/bench_maxsim.cpp +154 -0
  57. package/numkong/bench/bench_mesh.cpp +148 -0
  58. package/numkong/bench/bench_probability.cpp +60 -0
  59. package/numkong/bench/bench_reduce.cpp +371 -0
  60. package/numkong/bench/bench_set.cpp +73 -0
  61. package/numkong/bench/bench_sparse.cpp +198 -0
  62. package/numkong/bench/bench_spatial.cpp +347 -0
  63. package/numkong/bench/bench_trigonometry.cpp +137 -0
  64. package/numkong/bench/swift/Bench.swift +506 -0
  65. package/numkong/binding.gyp +169 -0
  66. package/numkong/build.rs +484 -0
  67. package/numkong/c/dispatch.h +512 -0
  68. package/numkong/c/dispatch_bf16.c +447 -0
  69. package/numkong/c/dispatch_bf16c.c +52 -0
  70. package/numkong/c/dispatch_e2m3.c +296 -0
  71. package/numkong/c/dispatch_e3m2.c +284 -0
  72. package/numkong/c/dispatch_e4m3.c +318 -0
  73. package/numkong/c/dispatch_e5m2.c +316 -0
  74. package/numkong/c/dispatch_f16.c +452 -0
  75. package/numkong/c/dispatch_f16c.c +58 -0
  76. package/numkong/c/dispatch_f32.c +434 -0
  77. package/numkong/c/dispatch_f32c.c +99 -0
  78. package/numkong/c/dispatch_f64.c +348 -0
  79. package/numkong/c/dispatch_f64c.c +98 -0
  80. package/numkong/c/dispatch_i16.c +96 -0
  81. package/numkong/c/dispatch_i32.c +89 -0
  82. package/numkong/c/dispatch_i4.c +153 -0
  83. package/numkong/c/dispatch_i64.c +86 -0
  84. package/numkong/c/dispatch_i8.c +348 -0
  85. package/numkong/c/dispatch_other.c +348 -0
  86. package/numkong/c/dispatch_u1.c +193 -0
  87. package/numkong/c/dispatch_u16.c +124 -0
  88. package/numkong/c/dispatch_u32.c +118 -0
  89. package/numkong/c/dispatch_u4.c +153 -0
  90. package/numkong/c/dispatch_u64.c +102 -0
  91. package/numkong/c/dispatch_u8.c +364 -0
  92. package/numkong/c/numkong.c +940 -0
  93. package/numkong/cmake/nk_arm_isa_probes.cmake +61 -0
  94. package/numkong/cmake/nk_isa_probe.cmake +90 -0
  95. package/numkong/cmake/nk_loongarch_isa_probes.cmake +15 -0
  96. package/numkong/cmake/nk_math_lib.cmake +46 -0
  97. package/numkong/cmake/nk_power_isa_probes.cmake +15 -0
  98. package/numkong/cmake/nk_riscv_isa_probes.cmake +19 -0
  99. package/numkong/cmake/nk_x86_isa_probes.cmake +35 -0
  100. package/numkong/cmake/toolchain-aarch64-gnu.cmake +86 -0
  101. package/numkong/cmake/toolchain-android-arm64.cmake +25 -0
  102. package/numkong/cmake/toolchain-android-armv7.cmake +25 -0
  103. package/numkong/cmake/toolchain-loongarch64-gnu.cmake +68 -0
  104. package/numkong/cmake/toolchain-ppc64le-gnu.cmake +74 -0
  105. package/numkong/cmake/toolchain-riscv64-gnu.cmake +107 -0
  106. package/numkong/cmake/toolchain-riscv64-llvm.cmake +114 -0
  107. package/numkong/cmake/toolchain-wasi.cmake +77 -0
  108. package/numkong/cmake/toolchain-wasm.cmake +70 -0
  109. package/numkong/cmake/toolchain-wasm64.cmake +62 -0
  110. package/numkong/cmake/toolchain-x86_64-llvm.cmake +47 -0
  111. package/numkong/include/README.md +643 -0
  112. package/numkong/include/module.modulemap +129 -0
  113. package/numkong/include/numkong/attention/sapphireamx.h +1363 -0
  114. package/numkong/include/numkong/attention/sme.h +2075 -0
  115. package/numkong/include/numkong/attention.h +49 -0
  116. package/numkong/include/numkong/capabilities.h +930 -0
  117. package/numkong/include/numkong/cast/README.md +262 -0
  118. package/numkong/include/numkong/cast/diamond.h +64 -0
  119. package/numkong/include/numkong/cast/haswell.h +823 -0
  120. package/numkong/include/numkong/cast/icelake.h +475 -0
  121. package/numkong/include/numkong/cast/loongsonasx.h +252 -0
  122. package/numkong/include/numkong/cast/neon.h +1174 -0
  123. package/numkong/include/numkong/cast/powervsx.h +449 -0
  124. package/numkong/include/numkong/cast/rvv.h +970 -0
  125. package/numkong/include/numkong/cast/sapphire.h +262 -0
  126. package/numkong/include/numkong/cast/serial.h +2336 -0
  127. package/numkong/include/numkong/cast/skylake.h +915 -0
  128. package/numkong/include/numkong/cast/v128relaxed.h +537 -0
  129. package/numkong/include/numkong/cast.h +254 -0
  130. package/numkong/include/numkong/cast.hpp +44 -0
  131. package/numkong/include/numkong/curved/README.md +223 -0
  132. package/numkong/include/numkong/curved/genoa.h +182 -0
  133. package/numkong/include/numkong/curved/haswell.h +276 -0
  134. package/numkong/include/numkong/curved/neon.h +329 -0
  135. package/numkong/include/numkong/curved/neonbfdot.h +211 -0
  136. package/numkong/include/numkong/curved/rvv.h +305 -0
  137. package/numkong/include/numkong/curved/serial.h +207 -0
  138. package/numkong/include/numkong/curved/skylake.h +457 -0
  139. package/numkong/include/numkong/curved/smef64.h +530 -0
  140. package/numkong/include/numkong/curved.h +513 -0
  141. package/numkong/include/numkong/curved.hpp +144 -0
  142. package/numkong/include/numkong/dot/README.md +442 -0
  143. package/numkong/include/numkong/dot/alder.h +563 -0
  144. package/numkong/include/numkong/dot/diamond.h +158 -0
  145. package/numkong/include/numkong/dot/genoa.h +289 -0
  146. package/numkong/include/numkong/dot/haswell.h +1723 -0
  147. package/numkong/include/numkong/dot/icelake.h +997 -0
  148. package/numkong/include/numkong/dot/loongsonasx.h +671 -0
  149. package/numkong/include/numkong/dot/neon.h +869 -0
  150. package/numkong/include/numkong/dot/neonbfdot.h +243 -0
  151. package/numkong/include/numkong/dot/neonfhm.h +358 -0
  152. package/numkong/include/numkong/dot/neonfp8.h +323 -0
  153. package/numkong/include/numkong/dot/neonsdot.h +622 -0
  154. package/numkong/include/numkong/dot/powervsx.h +752 -0
  155. package/numkong/include/numkong/dot/rvv.h +722 -0
  156. package/numkong/include/numkong/dot/rvvbb.h +72 -0
  157. package/numkong/include/numkong/dot/rvvbf16.h +123 -0
  158. package/numkong/include/numkong/dot/rvvhalf.h +129 -0
  159. package/numkong/include/numkong/dot/sapphire.h +141 -0
  160. package/numkong/include/numkong/dot/serial.h +874 -0
  161. package/numkong/include/numkong/dot/sierra.h +405 -0
  162. package/numkong/include/numkong/dot/skylake.h +1116 -0
  163. package/numkong/include/numkong/dot/sve.h +420 -0
  164. package/numkong/include/numkong/dot/svebfdot.h +75 -0
  165. package/numkong/include/numkong/dot/svehalf.h +168 -0
  166. package/numkong/include/numkong/dot/svesdot.h +90 -0
  167. package/numkong/include/numkong/dot/v128relaxed.h +1293 -0
  168. package/numkong/include/numkong/dot.h +1136 -0
  169. package/numkong/include/numkong/dot.hpp +94 -0
  170. package/numkong/include/numkong/dots/README.md +496 -0
  171. package/numkong/include/numkong/dots/alder.h +117 -0
  172. package/numkong/include/numkong/dots/diamond.h +86 -0
  173. package/numkong/include/numkong/dots/genoa.h +100 -0
  174. package/numkong/include/numkong/dots/graniteamx.h +733 -0
  175. package/numkong/include/numkong/dots/haswell.h +310 -0
  176. package/numkong/include/numkong/dots/icelake.h +180 -0
  177. package/numkong/include/numkong/dots/loongsonasx.h +176 -0
  178. package/numkong/include/numkong/dots/neon.h +123 -0
  179. package/numkong/include/numkong/dots/neonbfdot.h +59 -0
  180. package/numkong/include/numkong/dots/neonfhm.h +96 -0
  181. package/numkong/include/numkong/dots/neonfp8.h +99 -0
  182. package/numkong/include/numkong/dots/neonsdot.h +144 -0
  183. package/numkong/include/numkong/dots/powervsx.h +194 -0
  184. package/numkong/include/numkong/dots/rvv.h +2593 -0
  185. package/numkong/include/numkong/dots/sapphireamx.h +4017 -0
  186. package/numkong/include/numkong/dots/serial.h +2867 -0
  187. package/numkong/include/numkong/dots/sierra.h +100 -0
  188. package/numkong/include/numkong/dots/skylake.h +207 -0
  189. package/numkong/include/numkong/dots/sme.h +5041 -0
  190. package/numkong/include/numkong/dots/smebi32.h +477 -0
  191. package/numkong/include/numkong/dots/smef64.h +1328 -0
  192. package/numkong/include/numkong/dots/smehalf.h +47 -0
  193. package/numkong/include/numkong/dots/v128relaxed.h +317 -0
  194. package/numkong/include/numkong/dots.h +3110 -0
  195. package/numkong/include/numkong/dots.hpp +641 -0
  196. package/numkong/include/numkong/each/README.md +465 -0
  197. package/numkong/include/numkong/each/haswell.h +1658 -0
  198. package/numkong/include/numkong/each/icelake.h +272 -0
  199. package/numkong/include/numkong/each/neon.h +1138 -0
  200. package/numkong/include/numkong/each/neonbfdot.h +211 -0
  201. package/numkong/include/numkong/each/neonhalf.h +318 -0
  202. package/numkong/include/numkong/each/rvv.h +1121 -0
  203. package/numkong/include/numkong/each/sapphire.h +343 -0
  204. package/numkong/include/numkong/each/serial.h +282 -0
  205. package/numkong/include/numkong/each/skylake.h +1562 -0
  206. package/numkong/include/numkong/each/v128relaxed.h +562 -0
  207. package/numkong/include/numkong/each.h +2232 -0
  208. package/numkong/include/numkong/each.hpp +434 -0
  209. package/numkong/include/numkong/geospatial/README.md +147 -0
  210. package/numkong/include/numkong/geospatial/haswell.h +633 -0
  211. package/numkong/include/numkong/geospatial/neon.h +615 -0
  212. package/numkong/include/numkong/geospatial/rvv.h +701 -0
  213. package/numkong/include/numkong/geospatial/serial.h +309 -0
  214. package/numkong/include/numkong/geospatial/skylake.h +613 -0
  215. package/numkong/include/numkong/geospatial/v128relaxed.h +652 -0
  216. package/numkong/include/numkong/geospatial.h +453 -0
  217. package/numkong/include/numkong/geospatial.hpp +235 -0
  218. package/numkong/include/numkong/matrix.hpp +336 -0
  219. package/numkong/include/numkong/maxsim/README.md +187 -0
  220. package/numkong/include/numkong/maxsim/alder.h +511 -0
  221. package/numkong/include/numkong/maxsim/genoa.h +115 -0
  222. package/numkong/include/numkong/maxsim/haswell.h +553 -0
  223. package/numkong/include/numkong/maxsim/icelake.h +480 -0
  224. package/numkong/include/numkong/maxsim/neonsdot.h +394 -0
  225. package/numkong/include/numkong/maxsim/sapphireamx.h +877 -0
  226. package/numkong/include/numkong/maxsim/serial.h +490 -0
  227. package/numkong/include/numkong/maxsim/sme.h +993 -0
  228. package/numkong/include/numkong/maxsim/v128relaxed.h +280 -0
  229. package/numkong/include/numkong/maxsim.h +571 -0
  230. package/numkong/include/numkong/maxsim.hpp +133 -0
  231. package/numkong/include/numkong/mesh/README.md +219 -0
  232. package/numkong/include/numkong/mesh/haswell.h +2013 -0
  233. package/numkong/include/numkong/mesh/neon.h +1821 -0
  234. package/numkong/include/numkong/mesh/neonbfdot.h +771 -0
  235. package/numkong/include/numkong/mesh/rvv.h +936 -0
  236. package/numkong/include/numkong/mesh/serial.h +721 -0
  237. package/numkong/include/numkong/mesh/skylake.h +2344 -0
  238. package/numkong/include/numkong/mesh/v128relaxed.h +1081 -0
  239. package/numkong/include/numkong/mesh.h +666 -0
  240. package/numkong/include/numkong/mesh.hpp +701 -0
  241. package/numkong/include/numkong/numkong.h +78 -0
  242. package/numkong/include/numkong/numkong.hpp +58 -0
  243. package/numkong/include/numkong/probability/README.md +177 -0
  244. package/numkong/include/numkong/probability/haswell.h +267 -0
  245. package/numkong/include/numkong/probability/neon.h +255 -0
  246. package/numkong/include/numkong/probability/rvv.h +414 -0
  247. package/numkong/include/numkong/probability/serial.h +181 -0
  248. package/numkong/include/numkong/probability/skylake.h +324 -0
  249. package/numkong/include/numkong/probability.h +380 -0
  250. package/numkong/include/numkong/probability.hpp +120 -0
  251. package/numkong/include/numkong/random.h +50 -0
  252. package/numkong/include/numkong/random.hpp +285 -0
  253. package/numkong/include/numkong/reduce/README.md +552 -0
  254. package/numkong/include/numkong/reduce/alder.h +636 -0
  255. package/numkong/include/numkong/reduce/genoa.h +201 -0
  256. package/numkong/include/numkong/reduce/haswell.h +3785 -0
  257. package/numkong/include/numkong/reduce/icelake.h +549 -0
  258. package/numkong/include/numkong/reduce/neon.h +3980 -0
  259. package/numkong/include/numkong/reduce/neonbfdot.h +142 -0
  260. package/numkong/include/numkong/reduce/neonfhm.h +293 -0
  261. package/numkong/include/numkong/reduce/neonsdot.h +357 -0
  262. package/numkong/include/numkong/reduce/rvv.h +3533 -0
  263. package/numkong/include/numkong/reduce/serial.h +753 -0
  264. package/numkong/include/numkong/reduce/sierra.h +338 -0
  265. package/numkong/include/numkong/reduce/skylake.h +3794 -0
  266. package/numkong/include/numkong/reduce/sve.h +52 -0
  267. package/numkong/include/numkong/reduce/v128relaxed.h +2302 -0
  268. package/numkong/include/numkong/reduce.h +1582 -0
  269. package/numkong/include/numkong/reduce.hpp +778 -0
  270. package/numkong/include/numkong/scalar/README.md +89 -0
  271. package/numkong/include/numkong/scalar/haswell.h +122 -0
  272. package/numkong/include/numkong/scalar/loongsonasx.h +74 -0
  273. package/numkong/include/numkong/scalar/neon.h +122 -0
  274. package/numkong/include/numkong/scalar/neonhalf.h +70 -0
  275. package/numkong/include/numkong/scalar/powervsx.h +96 -0
  276. package/numkong/include/numkong/scalar/rvv.h +211 -0
  277. package/numkong/include/numkong/scalar/sapphire.h +74 -0
  278. package/numkong/include/numkong/scalar/serial.h +332 -0
  279. package/numkong/include/numkong/scalar/v128relaxed.h +56 -0
  280. package/numkong/include/numkong/scalar.h +696 -0
  281. package/numkong/include/numkong/set/README.md +179 -0
  282. package/numkong/include/numkong/set/haswell.h +334 -0
  283. package/numkong/include/numkong/set/icelake.h +485 -0
  284. package/numkong/include/numkong/set/loongsonasx.h +181 -0
  285. package/numkong/include/numkong/set/neon.h +363 -0
  286. package/numkong/include/numkong/set/powervsx.h +326 -0
  287. package/numkong/include/numkong/set/rvv.h +226 -0
  288. package/numkong/include/numkong/set/rvvbb.h +117 -0
  289. package/numkong/include/numkong/set/serial.h +174 -0
  290. package/numkong/include/numkong/set/sve.h +187 -0
  291. package/numkong/include/numkong/set/v128relaxed.h +240 -0
  292. package/numkong/include/numkong/set.h +471 -0
  293. package/numkong/include/numkong/set.hpp +114 -0
  294. package/numkong/include/numkong/sets/README.md +149 -0
  295. package/numkong/include/numkong/sets/haswell.h +63 -0
  296. package/numkong/include/numkong/sets/icelake.h +66 -0
  297. package/numkong/include/numkong/sets/loongsonasx.h +52 -0
  298. package/numkong/include/numkong/sets/neon.h +61 -0
  299. package/numkong/include/numkong/sets/powervsx.h +65 -0
  300. package/numkong/include/numkong/sets/serial.h +43 -0
  301. package/numkong/include/numkong/sets/smebi32.h +1135 -0
  302. package/numkong/include/numkong/sets/v128relaxed.h +58 -0
  303. package/numkong/include/numkong/sets.h +382 -0
  304. package/numkong/include/numkong/sparse/README.md +156 -0
  305. package/numkong/include/numkong/sparse/icelake.h +463 -0
  306. package/numkong/include/numkong/sparse/neon.h +288 -0
  307. package/numkong/include/numkong/sparse/serial.h +117 -0
  308. package/numkong/include/numkong/sparse/sve2.h +504 -0
  309. package/numkong/include/numkong/sparse/turin.h +322 -0
  310. package/numkong/include/numkong/sparse.h +363 -0
  311. package/numkong/include/numkong/sparse.hpp +113 -0
  312. package/numkong/include/numkong/spatial/README.md +439 -0
  313. package/numkong/include/numkong/spatial/alder.h +604 -0
  314. package/numkong/include/numkong/spatial/diamond.h +240 -0
  315. package/numkong/include/numkong/spatial/genoa.h +222 -0
  316. package/numkong/include/numkong/spatial/haswell.h +979 -0
  317. package/numkong/include/numkong/spatial/icelake.h +1067 -0
  318. package/numkong/include/numkong/spatial/loongsonasx.h +483 -0
  319. package/numkong/include/numkong/spatial/neon.h +846 -0
  320. package/numkong/include/numkong/spatial/neonbfdot.h +164 -0
  321. package/numkong/include/numkong/spatial/neonfp8.h +258 -0
  322. package/numkong/include/numkong/spatial/neonsdot.h +429 -0
  323. package/numkong/include/numkong/spatial/powervsx.h +738 -0
  324. package/numkong/include/numkong/spatial/rvv.h +991 -0
  325. package/numkong/include/numkong/spatial/rvvbf16.h +128 -0
  326. package/numkong/include/numkong/spatial/rvvhalf.h +120 -0
  327. package/numkong/include/numkong/spatial/serial.h +347 -0
  328. package/numkong/include/numkong/spatial/sierra.h +516 -0
  329. package/numkong/include/numkong/spatial/skylake.h +605 -0
  330. package/numkong/include/numkong/spatial/sve.h +246 -0
  331. package/numkong/include/numkong/spatial/svebfdot.h +125 -0
  332. package/numkong/include/numkong/spatial/svehalf.h +136 -0
  333. package/numkong/include/numkong/spatial/svesdot.h +143 -0
  334. package/numkong/include/numkong/spatial/v128relaxed.h +969 -0
  335. package/numkong/include/numkong/spatial.h +1681 -0
  336. package/numkong/include/numkong/spatial.hpp +183 -0
  337. package/numkong/include/numkong/spatials/README.md +580 -0
  338. package/numkong/include/numkong/spatials/alder.h +94 -0
  339. package/numkong/include/numkong/spatials/diamond.h +82 -0
  340. package/numkong/include/numkong/spatials/genoa.h +94 -0
  341. package/numkong/include/numkong/spatials/graniteamx.h +173 -0
  342. package/numkong/include/numkong/spatials/haswell.h +219 -0
  343. package/numkong/include/numkong/spatials/icelake.h +113 -0
  344. package/numkong/include/numkong/spatials/loongsonasx.h +153 -0
  345. package/numkong/include/numkong/spatials/neon.h +109 -0
  346. package/numkong/include/numkong/spatials/neonbfdot.h +60 -0
  347. package/numkong/include/numkong/spatials/neonfhm.h +92 -0
  348. package/numkong/include/numkong/spatials/neonfp8.h +111 -0
  349. package/numkong/include/numkong/spatials/neonsdot.h +143 -0
  350. package/numkong/include/numkong/spatials/powervsx.h +153 -0
  351. package/numkong/include/numkong/spatials/rvv.h +1976 -0
  352. package/numkong/include/numkong/spatials/sapphireamx.h +1149 -0
  353. package/numkong/include/numkong/spatials/serial.h +248 -0
  354. package/numkong/include/numkong/spatials/sierra.h +96 -0
  355. package/numkong/include/numkong/spatials/skylake.h +184 -0
  356. package/numkong/include/numkong/spatials/sme.h +1927 -0
  357. package/numkong/include/numkong/spatials/smef64.h +483 -0
  358. package/numkong/include/numkong/spatials/v128relaxed.h +240 -0
  359. package/numkong/include/numkong/spatials.h +3374 -0
  360. package/numkong/include/numkong/spatials.hpp +509 -0
  361. package/numkong/include/numkong/tensor.hpp +1981 -0
  362. package/numkong/include/numkong/trigonometry/README.md +184 -0
  363. package/numkong/include/numkong/trigonometry/haswell.h +653 -0
  364. package/numkong/include/numkong/trigonometry/neon.h +638 -0
  365. package/numkong/include/numkong/trigonometry/rvv.h +700 -0
  366. package/numkong/include/numkong/trigonometry/serial.h +703 -0
  367. package/numkong/include/numkong/trigonometry/skylake.h +725 -0
  368. package/numkong/include/numkong/trigonometry/v128relaxed.h +667 -0
  369. package/numkong/include/numkong/trigonometry.h +467 -0
  370. package/numkong/include/numkong/trigonometry.hpp +166 -0
  371. package/numkong/include/numkong/types.h +1695 -0
  372. package/numkong/include/numkong/types.hpp +6027 -0
  373. package/numkong/include/numkong/vector.hpp +766 -0
  374. package/numkong/javascript/@numkong-darwin-arm64/package.json +26 -0
  375. package/numkong/javascript/@numkong-darwin-x64/package.json +26 -0
  376. package/numkong/javascript/@numkong-linux-arm64/package.json +25 -0
  377. package/numkong/javascript/@numkong-linux-x64/package.json +25 -0
  378. package/numkong/javascript/@numkong-win32-arm64/package.json +25 -0
  379. package/numkong/javascript/@numkong-win32-x64/package.json +25 -0
  380. package/numkong/javascript/README.md +254 -0
  381. package/numkong/javascript/node-gyp-build.d.ts +4 -0
  382. package/numkong/javascript/numkong-browser.ts +40 -0
  383. package/numkong/javascript/numkong-wasm.ts +750 -0
  384. package/numkong/javascript/numkong.c +712 -0
  385. package/numkong/javascript/numkong.ts +613 -0
  386. package/{simsimd → numkong}/javascript/tsconfig-base.json +6 -3
  387. package/numkong/javascript/tsconfig-cjs.json +13 -0
  388. package/{simsimd → numkong}/javascript/tsconfig-esm.json +1 -1
  389. package/numkong/javascript/types.ts +784 -0
  390. package/numkong/numkong.gypi +104 -0
  391. package/numkong/package.json +108 -0
  392. package/numkong/probes/arm_neon.c +8 -0
  393. package/numkong/probes/arm_neon_bfdot.c +9 -0
  394. package/numkong/probes/arm_neon_fhm.c +9 -0
  395. package/numkong/probes/arm_neon_half.c +8 -0
  396. package/numkong/probes/arm_neon_sdot.c +9 -0
  397. package/numkong/probes/arm_neonfp8.c +9 -0
  398. package/numkong/probes/arm_sme.c +16 -0
  399. package/numkong/probes/arm_sme2.c +16 -0
  400. package/numkong/probes/arm_sme2p1.c +16 -0
  401. package/numkong/probes/arm_sme_bf16.c +16 -0
  402. package/numkong/probes/arm_sme_bi32.c +16 -0
  403. package/numkong/probes/arm_sme_f64.c +16 -0
  404. package/numkong/probes/arm_sme_fa64.c +14 -0
  405. package/numkong/probes/arm_sme_half.c +16 -0
  406. package/numkong/probes/arm_sme_lut2.c +15 -0
  407. package/numkong/probes/arm_sve.c +18 -0
  408. package/numkong/probes/arm_sve2.c +20 -0
  409. package/numkong/probes/arm_sve2p1.c +18 -0
  410. package/numkong/probes/arm_sve_bfdot.c +20 -0
  411. package/numkong/probes/arm_sve_half.c +18 -0
  412. package/numkong/probes/arm_sve_sdot.c +21 -0
  413. package/numkong/probes/loongarch_lasx.c +12 -0
  414. package/numkong/probes/power_vsx.c +12 -0
  415. package/numkong/probes/probe.js +127 -0
  416. package/numkong/probes/riscv_rvv.c +14 -0
  417. package/numkong/probes/riscv_rvv_bb.c +15 -0
  418. package/numkong/probes/riscv_rvv_bf16.c +17 -0
  419. package/numkong/probes/riscv_rvv_half.c +14 -0
  420. package/numkong/probes/wasm_v128relaxed.c +11 -0
  421. package/numkong/probes/x86_alder.c +17 -0
  422. package/numkong/probes/x86_diamond.c +17 -0
  423. package/numkong/probes/x86_genoa.c +17 -0
  424. package/numkong/probes/x86_graniteamx.c +19 -0
  425. package/numkong/probes/x86_haswell.c +11 -0
  426. package/numkong/probes/x86_icelake.c +17 -0
  427. package/numkong/probes/x86_sapphire.c +16 -0
  428. package/numkong/probes/x86_sapphireamx.c +18 -0
  429. package/numkong/probes/x86_sierra.c +17 -0
  430. package/numkong/probes/x86_skylake.c +15 -0
  431. package/numkong/probes/x86_turin.c +17 -0
  432. package/numkong/rust/README.md +698 -0
  433. package/numkong/rust/capabilities.rs +96 -0
  434. package/numkong/rust/cast.rs +314 -0
  435. package/numkong/rust/curved.rs +391 -0
  436. package/numkong/rust/each.rs +2721 -0
  437. package/numkong/rust/geospatial.rs +343 -0
  438. package/numkong/rust/matrix.rs +5424 -0
  439. package/numkong/rust/maxsim.rs +540 -0
  440. package/numkong/rust/mesh.rs +837 -0
  441. package/numkong/rust/numkong.rs +409 -0
  442. package/numkong/rust/probability.rs +272 -0
  443. package/numkong/rust/reduce.rs +1249 -0
  444. package/numkong/rust/set.rs +194 -0
  445. package/numkong/rust/sparse.rs +513 -0
  446. package/numkong/rust/spatial.rs +1815 -0
  447. package/numkong/rust/tensor.rs +7565 -0
  448. package/numkong/rust/types.rs +3666 -0
  449. package/numkong/rust/vector.rs +1899 -0
  450. package/numkong/rustfmt.toml +2 -0
  451. package/numkong/swift/Matrix.swift +1388 -0
  452. package/numkong/swift/MaxSim.swift +151 -0
  453. package/numkong/swift/NumKong.swift +283 -0
  454. package/numkong/swift/Numerics.swift +329 -0
  455. package/numkong/swift/README.md +433 -0
  456. package/numkong/swift/Scalar.swift +741 -0
  457. package/numkong/swift/Tensor.swift +209 -0
  458. package/numkong/test/README.md +315 -0
  459. package/numkong/test/playwright.config.ts +16 -0
  460. package/numkong/test/swift/Test.swift +534 -0
  461. package/numkong/test/test-browser-bundle-runner.mjs +36 -0
  462. package/numkong/test/test-browser-bundle.html +188 -0
  463. package/numkong/test/test-browser-runner.mjs +43 -0
  464. package/numkong/test/test-browser.html +305 -0
  465. package/numkong/test/test-dtypes.mjs +453 -0
  466. package/numkong/test/test-wasm.mjs +267 -0
  467. package/numkong/test/test.cpp +442 -0
  468. package/numkong/test/test.hpp +580 -0
  469. package/numkong/test/test.mjs +363 -0
  470. package/numkong/test/test_base.py +1041 -0
  471. package/numkong/test/test_cast.cpp +242 -0
  472. package/numkong/test/test_cross.hpp +551 -0
  473. package/numkong/test/test_cross.py +467 -0
  474. package/numkong/test/test_cross_amx.cpp +50 -0
  475. package/numkong/test/test_cross_arm.cpp +225 -0
  476. package/numkong/test/test_cross_blas.cpp +354 -0
  477. package/numkong/test/test_cross_loongarch.cpp +90 -0
  478. package/numkong/test/test_cross_power.cpp +73 -0
  479. package/numkong/test/test_cross_rvv.cpp +109 -0
  480. package/numkong/test/test_cross_serial.cpp +181 -0
  481. package/numkong/test/test_cross_sme.cpp +148 -0
  482. package/numkong/test/test_cross_wasm.cpp +141 -0
  483. package/numkong/test/test_cross_x86.cpp +442 -0
  484. package/numkong/test/test_curved.cpp +182 -0
  485. package/numkong/test/test_curved.py +207 -0
  486. package/numkong/test/test_dot.cpp +353 -0
  487. package/numkong/test/test_dot.py +296 -0
  488. package/numkong/test/test_each.cpp +437 -0
  489. package/numkong/test/test_each.py +756 -0
  490. package/numkong/test/test_geospatial.cpp +155 -0
  491. package/numkong/test/test_geospatial.py +241 -0
  492. package/numkong/test/test_maxsim.cpp +138 -0
  493. package/numkong/test/test_maxsim.py +209 -0
  494. package/numkong/test/test_mesh.cpp +203 -0
  495. package/numkong/test/test_mesh.py +394 -0
  496. package/numkong/test/test_probability.cpp +136 -0
  497. package/numkong/test/test_probability.py +166 -0
  498. package/numkong/test/test_reduce.cpp +563 -0
  499. package/numkong/test/test_reduce.py +781 -0
  500. package/numkong/test/test_set.cpp +151 -0
  501. package/numkong/test/test_set.py +120 -0
  502. package/numkong/test/test_similarities.py +579 -0
  503. package/numkong/test/test_sparse.cpp +121 -0
  504. package/numkong/test/test_sparse.py +118 -0
  505. package/numkong/test/test_spatial.cpp +518 -0
  506. package/numkong/test/test_spatial.py +269 -0
  507. package/numkong/test/test_tensor.cpp +884 -0
  508. package/numkong/test/test_tensor.py +1417 -0
  509. package/numkong/test/test_trigonometry.cpp +156 -0
  510. package/numkong/test/test_trigonometry.py +180 -0
  511. package/package.json +7 -8
  512. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  513. package/prebuilds/linux-arm64/usearch.node +0 -0
  514. package/prebuilds/linux-x64/usearch.node +0 -0
  515. package/fp16/CMakeLists.txt +0 -176
  516. package/fp16/LICENSE +0 -11
  517. package/fp16/README.md +0 -20
  518. package/fp16/bench/alt-element.cc +0 -89
  519. package/fp16/bench/from-alt-array.cc +0 -175
  520. package/fp16/bench/from-ieee-array.cc +0 -399
  521. package/fp16/bench/ieee-element.cc +0 -272
  522. package/fp16/bench/to-alt-array.cc +0 -91
  523. package/fp16/bench/to-ieee-array.cc +0 -303
  524. package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
  525. package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
  526. package/fp16/cmake/DownloadPSimd.cmake +0 -15
  527. package/fp16/configure.py +0 -65
  528. package/fp16/confu.yaml +0 -8
  529. package/fp16/include/fp16/__init__.py +0 -0
  530. package/fp16/include/fp16/avx.py +0 -50
  531. package/fp16/include/fp16/avx2.py +0 -53
  532. package/fp16/include/fp16/bitcasts.h +0 -92
  533. package/fp16/include/fp16/fp16.h +0 -451
  534. package/fp16/include/fp16/psimd.h +0 -131
  535. package/fp16/include/fp16.h +0 -11
  536. package/fp16/jni/Android.mk +0 -6
  537. package/fp16/jni/Application.mk +0 -4
  538. package/fp16/test/alt-from-fp32-value.cc +0 -498
  539. package/fp16/test/alt-to-fp32-bits.cc +0 -263
  540. package/fp16/test/alt-to-fp32-psimd.cc +0 -145
  541. package/fp16/test/alt-to-fp32-value.cc +0 -348
  542. package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
  543. package/fp16/test/bitcasts.cc +0 -56
  544. package/fp16/test/ieee-from-fp32-value.cc +0 -523
  545. package/fp16/test/ieee-to-fp32-bits.cc +0 -333
  546. package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
  547. package/fp16/test/ieee-to-fp32-value.cc +0 -420
  548. package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
  549. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
  550. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
  551. package/fp16/test/peachpy/stubs.py +0 -38
  552. package/fp16/test/tables.cc +0 -530
  553. package/fp16/test/tables.h +0 -13
  554. package/fp16/third-party/THHalf.h +0 -92
  555. package/fp16/third-party/eigen-half.h +0 -170
  556. package/fp16/third-party/float16-compressor.h +0 -80
  557. package/fp16/third-party/half.hpp +0 -2916
  558. package/fp16/third-party/npy-halffloat.h +0 -328
  559. package/simsimd/.git-blame-ignore-revs +0 -2
  560. package/simsimd/CONTRIBUTING.md +0 -321
  561. package/simsimd/Cargo.toml +0 -49
  562. package/simsimd/MANIFEST.in +0 -4
  563. package/simsimd/Package.swift +0 -28
  564. package/simsimd/README.md +0 -1493
  565. package/simsimd/binding.gyp +0 -35
  566. package/simsimd/build.rs +0 -70
  567. package/simsimd/c/lib.c +0 -374
  568. package/simsimd/include/module.modulemap +0 -4
  569. package/simsimd/include/simsimd/binary.h +0 -486
  570. package/simsimd/include/simsimd/curved.h +0 -1541
  571. package/simsimd/include/simsimd/dot.h +0 -1853
  572. package/simsimd/include/simsimd/elementwise.h +0 -2465
  573. package/simsimd/include/simsimd/geospatial.h +0 -43
  574. package/simsimd/include/simsimd/mesh.h +0 -69
  575. package/simsimd/include/simsimd/probability.h +0 -606
  576. package/simsimd/include/simsimd/simsimd.h +0 -2567
  577. package/simsimd/include/simsimd/sparse.h +0 -1384
  578. package/simsimd/include/simsimd/spatial.h +0 -2335
  579. package/simsimd/include/simsimd/types.h +0 -666
  580. package/simsimd/javascript/fallback.ts +0 -233
  581. package/simsimd/javascript/lib.c +0 -125
  582. package/simsimd/javascript/node-gyp-build.d.ts +0 -1
  583. package/simsimd/javascript/simsimd.ts +0 -179
  584. package/simsimd/javascript/tsconfig-cjs.json +0 -8
  585. package/simsimd/package-lock.json +0 -1761
  586. package/simsimd/package.json +0 -61
  587. package/simsimd/rust/lib.rs +0 -1821
  588. package/simsimd/scripts/bench.cxx +0 -1170
  589. package/simsimd/scripts/bench.ipynb +0 -187
  590. package/simsimd/scripts/bench.js +0 -127
  591. package/simsimd/scripts/bench.rs +0 -876
  592. package/simsimd/scripts/bench_vectors.py +0 -734
  593. package/simsimd/scripts/bench_vectors_live.py +0 -213
  594. package/simsimd/scripts/test.c +0 -187
  595. package/simsimd/scripts/test.mjs +0 -220
  596. package/simsimd/scripts/test.py +0 -1505
  597. package/simsimd/swift/SimSIMD.swift +0 -149
  598. package/simsimd/swift/Test.swift +0 -128
  599. /package/{simsimd → numkong}/CODE_OF_CONDUCT.md +0 -0
  600. /package/{simsimd → numkong}/LICENSE +0 -0
  601. /package/{simsimd → numkong}/javascript/dist-package-cjs.json +0 -0
  602. /package/{simsimd → numkong}/javascript/dist-package-esm.json +0 -0
@@ -0,0 +1,2075 @@
1
+ /**
2
+ * @brief FlashAttention-style kernels for SME.
3
+ * @file include/numkong/attention/sme.h
4
+ * @author Ash Vardanian
5
+ * @date January 11, 2026
6
+ *
7
+ * @sa include/numkong/attention.h
8
+ *
9
+ * This file implements FlashAttention-2 style scaled dot-product attention (SDPA) optimized
10
+ * for ARM SME instructions on Apple M4 and similar processors. The kernel computes:
11
+ *
12
+ * O = softmax(Q × Kᵀ / √d) × V
13
+ *
14
+ * Key features:
15
+ * - Online softmax: Mathematically exact, processes KV blocks incrementally
16
+ * - Pre-packed KV cache: BFMOPA/FMOPA-interleaved format amortizes packing for repeated inference
17
+ * - GQA/MQA support: Different `num_heads` and `num_kv_heads` for grouped-query attention
18
+ * - Pure Streaming SVE: No NEON intrinsics for non-linear operations
19
+ *
20
+ * Target models (2025):
21
+ * - Kimi K2: `head_dim`=112, 64 heads, MHA, 128K context
22
+ * - LLaMA 3.1 405B: `head_dim`=128, 128 heads, 16 KV heads (GQA 8:1), 128K context
23
+ * - Qwen 2.5 72B: `head_dim`=128, 64 heads, 8 KV heads (GQA 8:1), 32K context
24
+ *
25
+ * @section attention_sme_architecture Architecture
26
+ *
27
+ * Both Q×Kᵀ and P×V phases use BFMOPA/FMOPA outer products on ZA tiles, eliminating
28
+ * element-wise SVE loops that dominated the original implementation. The Q matrix is
29
+ * pre-transposed once into a buffer matching the interleaving that ZA vertical reads
30
+ * would produce, so Q×Kᵀ runs as pure memory-to-BFMOPA with no per-block ZA staging.
31
+ *
32
+ * Block sizes:
33
+ * - Bᵣ = 16 (query block rows, matches ZA32 tile height)
34
+ * - Bᶜ = 32 (main prefill loop, processes two KV blocks per iteration using ZA2+ZA3)
35
+ * - Bᶜ = 16 (tail loop for remaining KV positions, and decode path)
36
+ *
37
+ * KV packing format:
38
+ * - K is stored in BFMOPA-interleaved format: `K_packed[kv_block][depth_step][32]` where
39
+ * `packed[2*ki + sub] = K[kv_block*16 + ki][2*depth_step + sub]`
40
+ * - V is stored in BFMOPA-interleaved format: `V_packed[kv_block][dim_tile][depth_step][32]`
41
+ * where `packed[2*dj + sub] = V[kv_block*16 + 2*depth_step + sub][dim_tile*16 + dj]`
42
+ * - The `reserved[0]` header field stores `v_dim_tile_count` for efficient V addressing
43
+ *
44
+ * Softmax:
45
+ * - Column-wise max and exp using ZA tile vertical reads (avoids per-row horizontal extracts)
46
+ * - Correction skip: when the block max does not exceed the running max, the output
47
+ * accumulator rescaling is skipped entirely (common in later KV blocks)
48
+ * - Degree-3 fast exp (`nk_exp_fast_f32_sve_`) saves 1 FMA per call vs degree-4
49
+ * - Weights stored directly as bf16/f16 in ZA0 columns via `svzip1` (no f32 round-trip)
50
+ *
51
+ * Decode path (query_len=1):
52
+ * - Uses element-wise SVE with scalar weight broadcasts instead of BFMOPA P×V
53
+ * - BFMOPA overhead too high for single-query case due to ZA setup cost
54
+ *
55
+ * P×V prefill path:
56
+ * - 4-tile BFMOPA processing: 4 dim-tiles × 8 depth steps per KV block = 32 BFMOPA ops
57
+ * - ZA0-ZA3 accumulate simultaneously, read results with MOVA, add to output accumulator
58
+ * - Remainder dim-tiles handled 1-at-a-time using ZA0 only
59
+ *
60
+ * SME tile dimensions (for SVL=512, i.e., Apple M4):
61
+ * - ZA32 tile: 16 × 16 `f32` elements (1KB)
62
+ * - `bf16`/`f16` vectors: 32 elements per SVE vector
63
+ *
64
+ * @section attention_sme_history Optimization History
65
+ *
66
+ * Phase 1 (January 2026): Initial implementation using ZA staging transpose for Q×Kᵀ
67
+ * and element-wise SVE for P×V. Q and K rows were loaded into ZA0/ZA1 horizontally,
68
+ * read back vertically to produce interleaved vectors for BFMOPA. The P×V phase used
69
+ * scalar `svmla_f32_x` loops over head_dim for each query-key pair. Softmax used
70
+ * degree-4 polynomial exp with per-row horizontal max/sum. Performance: ~25-50 GFLOP/s
71
+ * on Apple M4 (bf16, 8 heads, query_len=64, kv_len=4096, head_dim=128).
72
+ *
73
+ * Phase 2 (February 2026): BFMOPA/FMOPA P×V with pre-packed V in interleaved format.
74
+ * Key changes integrated:
75
+ * - Q pre-transposed once into a buffer, eliminating per-block ZA staging for Q
76
+ * - K pre-packed in interleaved format, enabling pure memory-to-BFMOPA Q×Kᵀ
77
+ * - V pre-packed in BFMOPA-interleaved format with dim-tile blocking
78
+ * - P×V uses 4-tile BFMOPA accumulation (ZA0-ZA3) with pre-extracted P columns
79
+ * - Bᶜ=32 main loop for prefill (2 KV blocks per iteration via ZA2+ZA3)
80
+ * - Column-wise softmax: vertical ZA reads for max/exp instead of per-row horizontal
81
+ * - Correction skip when running max is unchanged
82
+ * - Degree-3 fast exp (~0.5% max relative error, saves 1 FMA per call)
83
+ * - Weights stored directly as bf16/f16 via `svzip1` (no f32 quantization round-trip)
84
+ * Performance: ~300-400 GFLOP/s on Apple M4 (same configuration), a 6-14× improvement.
85
+ *
86
+ * Rejected approaches:
87
+ * - BFMOPA P×V for decode (query_len=1): ZA setup overhead exceeds element-wise SVE cost
88
+ * - `svdot_lane` for Q×Kᵀ: lower throughput than BFMOPA on M4
89
+ * - Shared ZA tiles between softmax and P×V: register pressure too high with 4-tile P×V
90
+ */
91
+ #ifndef NK_ATTENTION_SME_H
92
+ #define NK_ATTENTION_SME_H
93
+
94
+ #if NK_TARGET_ARM64_
95
+ #if NK_TARGET_SME
96
+
97
+ #include "numkong/types.h"
98
+
99
+ #if defined(__cplusplus)
100
+ extern "C" {
101
+ #endif
102
+
103
+ #if defined(__clang__)
104
+ #pragma clang attribute push(__attribute__((target("sme"))), apply_to = function)
105
+ #elif defined(__GNUC__)
106
+ #pragma GCC push_options
107
+ #pragma GCC target("+sme")
108
+ #endif
109
+
110
+ /**
111
+ * @brief Convert bf16 vector to f32 in registers (streaming SVE compatible).
112
+ *
113
+ * BF16 is the upper 16 bits of F32, so we:
114
+ * 1. Reinterpret bf16 as u16
115
+ * 2. Zero-extend to u32 (unpklo for lower half)
116
+ * 3. Shift left by 16 to place in f32 exponent+mantissa position
117
+ * 4. Reinterpret as f32
118
+ */
119
+ NK_INTERNAL svfloat32_t nk_bf16_to_f32_sve_(svbool_t predicate_b32x, svbfloat16_t x_bf16x) __arm_streaming {
120
+ svuint16_t x_u16x = svreinterpret_u16_bf16(x_bf16x);
121
+ svuint32_t x_u32x = svunpklo_u32(x_u16x);
122
+ x_u32x = svlsl_n_u32_x(predicate_b32x, x_u32x, 16);
123
+ return svreinterpret_f32_u32(x_u32x);
124
+ }
125
+
126
+ /**
127
+ * @brief Convert f32 vector to bf16 in registers with rounding (streaming SVE compatible).
128
+ *
129
+ * 1. Reinterpret f32 as u32
130
+ * 2. Add rounding bias (0x8000) for round-to-nearest
131
+ * 3. Shift right by 16
132
+ * 4. Narrow to u16 and reinterpret as bf16
133
+ */
134
+ NK_INTERNAL svbfloat16_t nk_f32_to_bf16_sve_(svbool_t predicate_b32x, svfloat32_t x_f32x) __arm_streaming {
135
+ svuint32_t x_u32x = svreinterpret_u32_f32(x_f32x);
136
+ x_u32x = svadd_n_u32_x(predicate_b32x, x_u32x, 0x8000); // Round to nearest
137
+ x_u32x = svlsr_n_u32_x(predicate_b32x, x_u32x, 16);
138
+ svuint16_t x_u16x = svuzp1_u16(svreinterpret_u16_u32(x_u32x), svreinterpret_u16_u32(x_u32x));
139
+ return svreinterpret_bf16_u16(x_u16x);
140
+ }
141
+
142
+ /**
143
+ * @brief Packed KV cache header for attention (64-byte aligned).
144
+ *
145
+ * Layout in memory:
146
+ * [header: 64 bytes][K tiles: variable][V tiles: variable]
147
+ */
148
+ typedef struct {
149
+ nk_u32_t num_kv_heads; ///< Number of K/V heads (for GQA, may differ from Q heads)
150
+ nk_u32_t head_dim; ///< Original head dimension (64, 112, 128)
151
+ nk_u32_t head_dim_padded; ///< Padded to multiple of 32 for SME
152
+ nk_u32_t seq_len; ///< Current sequence length
153
+ nk_u32_t max_seq_len; ///< Maximum sequence length (for pre-allocation)
154
+ nk_u32_t k_offset; ///< Byte offset to K data from header start
155
+ nk_u32_t v_offset; ///< Byte offset to V data from header start
156
+ nk_u32_t reserved[9]; ///< reserved[0] = v_dim_tile_count; remainder pads to 64 bytes
157
+ } nk_attention_sme_packed_header_t;
158
+
159
+ /**
160
+ * @brief Fast exp approximation in Streaming SVE.
161
+ *
162
+ * Uses Cody-Waite range reduction + Horner polynomial (degree 4).
163
+ * Accuracy: ~0.1% relative error, acceptable for softmax normalization.
164
+ *
165
+ * @param pg Active predicate
166
+ * @param x Input vector
167
+ * @return exp(x) approximation
168
+ */
169
+ NK_INTERNAL svfloat32_t nk_exp_f32_sve_(svbool_t predicate_b32x, svfloat32_t x_f32x) __arm_streaming {
170
+ // Constants for Cody-Waite range reduction
171
+ svfloat32_t log2e_f32x = svdup_f32(1.4426950408889634f);
172
+ svfloat32_t ln2_high_f32x = svdup_f32(0.693145751953125f);
173
+ svfloat32_t ln2_low_f32x = svdup_f32(1.42860682030941723212e-6f);
174
+
175
+ // Clamp to avoid overflow/underflow
176
+ svfloat32_t max_x_f32x = svdup_f32(88.3762626647949f);
177
+ svfloat32_t min_x_f32x = svdup_f32(-87.3365447504021f);
178
+ x_f32x = svmax_f32_m(predicate_b32x, svmin_f32_m(predicate_b32x, x_f32x, max_x_f32x), min_x_f32x);
179
+
180
+ // n = round(x / ln(2))
181
+ svfloat32_t n_f32x = svrintn_f32_m(svundef_f32(), predicate_b32x, svmul_f32_m(predicate_b32x, x_f32x, log2e_f32x));
182
+
183
+ // r = x - n × ln(2) using Cody-Waite for precision
184
+ svfloat32_t r_f32x = svmsb_f32_m(predicate_b32x, n_f32x, ln2_high_f32x, x_f32x);
185
+ r_f32x = svmsb_f32_m(predicate_b32x, n_f32x, ln2_low_f32x, r_f32x);
186
+
187
+ // Polynomial approximation for exp(r): degree 4
188
+ // exp(r) ≈ 1 + r + r²/2 + r³/6 + r⁴/24
189
+ svfloat32_t p_f32x = svdup_f32(4.1666666667e-2f); // 1/24
190
+ p_f32x = svmad_f32_m(predicate_b32x, p_f32x, r_f32x, svdup_f32(1.6666666667e-1f)); // 1/6
191
+ p_f32x = svmad_f32_m(predicate_b32x, p_f32x, r_f32x, svdup_f32(5.0000000000e-1f)); // 1/2
192
+ p_f32x = svmad_f32_m(predicate_b32x, p_f32x, r_f32x, svdup_f32(1.0f)); // 1
193
+ p_f32x = svmad_f32_m(predicate_b32x, p_f32x, r_f32x, svdup_f32(1.0f)); // 1
194
+
195
+ // Reconstruct: exp(x) = 2ⁿ × exp(r)
196
+ // 2ⁿ via IEEE 754 exponent manipulation
197
+ svint32_t n_i32x = svcvt_s32_f32_m(svundef_s32(), predicate_b32x, n_f32x);
198
+ n_i32x = svadd_s32_m(predicate_b32x, n_i32x, svdup_s32(127));
199
+ n_i32x = svlsl_n_s32_m(predicate_b32x, n_i32x, 23);
200
+ svfloat32_t pow2n_f32x = svreinterpret_f32_s32(n_i32x);
201
+
202
+ return svmul_f32_m(predicate_b32x, p_f32x, pow2n_f32x);
203
+ }
204
+
205
+ /**
206
+ * @brief Degree-3 fast exp approximation. Max relative error ~0.5%.
207
+ * Saves 1 FMA per call vs degree-4 nk_exp_f32_sve_.
208
+ */
209
+ NK_INTERNAL svfloat32_t nk_exp_fast_f32_sve_(svbool_t predicate_b32x, svfloat32_t x_f32x) __arm_streaming {
210
+ svfloat32_t log2e_f32x = svdup_f32(1.4426950408889634f);
211
+ svfloat32_t ln2_high_f32x = svdup_f32(0.693145751953125f);
212
+ svfloat32_t ln2_low_f32x = svdup_f32(1.42860682030941723212e-6f);
213
+
214
+ svfloat32_t max_x_f32x = svdup_f32(88.3762626647949f);
215
+ svfloat32_t min_x_f32x = svdup_f32(-87.3365447504021f);
216
+ x_f32x = svmax_f32_m(predicate_b32x, svmin_f32_m(predicate_b32x, x_f32x, max_x_f32x), min_x_f32x);
217
+
218
+ svfloat32_t n_f32x = svrintn_f32_m(svundef_f32(), predicate_b32x, svmul_f32_m(predicate_b32x, x_f32x, log2e_f32x));
219
+ svfloat32_t r_f32x = svmsb_f32_m(predicate_b32x, n_f32x, ln2_high_f32x, x_f32x);
220
+ r_f32x = svmsb_f32_m(predicate_b32x, n_f32x, ln2_low_f32x, r_f32x);
221
+
222
+ // Degree-3: exp(r) ~ 1 + r + r^2/2 + r^3/6 (drop 1/24 term)
223
+ svfloat32_t p_f32x = svdup_f32(1.6666666667e-1f); // 1/6
224
+ p_f32x = svmad_f32_m(predicate_b32x, p_f32x, r_f32x, svdup_f32(5.0000000000e-1f)); // 1/2
225
+ p_f32x = svmad_f32_m(predicate_b32x, p_f32x, r_f32x, svdup_f32(1.0f)); // 1
226
+ p_f32x = svmad_f32_m(predicate_b32x, p_f32x, r_f32x, svdup_f32(1.0f)); // 1
227
+
228
+ svint32_t n_i32x = svcvt_s32_f32_m(svundef_s32(), predicate_b32x, n_f32x);
229
+ n_i32x = svadd_s32_m(predicate_b32x, n_i32x, svdup_s32(127));
230
+ n_i32x = svlsl_n_s32_m(predicate_b32x, n_i32x, 23);
231
+ svfloat32_t pow2n_f32x = svreinterpret_f32_s32(n_i32x);
232
+
233
+ return svmul_f32_m(predicate_b32x, p_f32x, pow2n_f32x);
234
+ }
235
+
236
+ NK_PUBLIC nk_size_t nk_attention_packed_kv_size_bf16_sme(nk_size_t num_kv_heads, nk_size_t head_dim,
237
+ nk_size_t max_seq_len) {
238
+ nk_size_t head_dim_padded = (head_dim + 31) / 32 * 32;
239
+ nk_size_t kv_blocks = (max_seq_len + 15) / 16;
240
+ nk_size_t seq_padded = kv_blocks * 16;
241
+ // K and V both use BFMOPA-interleaved format: [num_kv_heads, kv_blocks, depth_steps, 32]
242
+ nk_size_t k_size = num_kv_heads * seq_padded * head_dim_padded * sizeof(nk_bf16_t);
243
+ nk_size_t v_size = k_size;
244
+ return sizeof(nk_attention_sme_packed_header_t) + k_size + v_size;
245
+ }
246
+
247
+ NK_PUBLIC nk_size_t nk_attention_packed_kv_size_f16_sme(nk_size_t num_kv_heads, nk_size_t head_dim,
248
+ nk_size_t max_seq_len) {
249
+ return nk_attention_packed_kv_size_bf16_sme(num_kv_heads, head_dim, max_seq_len);
250
+ }
251
+
252
+ static void nk_attention_pack_kv_bf16_sme_ssve_( //
253
+ nk_bf16_t const *k, nk_bf16_t const *v, nk_size_t num_kv_heads, nk_size_t head_dim, nk_size_t seq_len,
254
+ nk_size_t k_stride, nk_size_t v_stride, void *kv_packed) NK_STREAMING_ {
255
+
256
+ nk_attention_sme_packed_header_t *header = (nk_attention_sme_packed_header_t *)kv_packed;
257
+ nk_size_t head_dim_padded = (head_dim + 31) / 32 * 32;
258
+ nk_size_t dim_tile_count = (head_dim_padded + 15) / 16;
259
+ nk_size_t kv_block_count = (seq_len + 15) / 16;
260
+
261
+ nk_size_t k_depth_step_count = head_dim_padded / 2;
262
+ nk_size_t head_elems = kv_block_count * 16 * head_dim_padded;
263
+
264
+ header->num_kv_heads = (nk_u32_t)num_kv_heads;
265
+ header->head_dim = (nk_u32_t)head_dim;
266
+ header->head_dim_padded = (nk_u32_t)head_dim_padded;
267
+ header->seq_len = (nk_u32_t)seq_len;
268
+ header->k_offset = sizeof(nk_attention_sme_packed_header_t);
269
+ header->reserved[0] = (nk_u32_t)dim_tile_count; // v_dim_tile_count
270
+ header->v_offset = header->k_offset + (nk_u32_t)(num_kv_heads * head_elems * sizeof(nk_bf16_t));
271
+
272
+ nk_bf16_t *k_packed = (nk_bf16_t *)((char *)kv_packed + header->k_offset);
273
+ nk_bf16_t *v_packed = (nk_bf16_t *)((char *)kv_packed + header->v_offset);
274
+
275
+ for (nk_size_t h = 0; h < num_kv_heads; h++) {
276
+ nk_bf16_t const *k_head = k + h * k_stride;
277
+ nk_bf16_t const *v_head = v + h * v_stride;
278
+
279
+ // K packing: BFMOPA-interleaved format
280
+ // K_packed[kv_block][depth_step][32] where
281
+ // packed[2*ki + sub] = K[kv_block*16 + ki][2*depth_step + sub]
282
+ nk_bf16_t *k_out = k_packed + h * head_elems;
283
+ for (nk_size_t kv_block = 0; kv_block < kv_block_count; kv_block++) {
284
+ for (nk_size_t depth_step = 0; depth_step < k_depth_step_count; depth_step++) {
285
+ nk_bf16_t *vec_out = k_out + (kv_block * k_depth_step_count + depth_step) * 32;
286
+ for (nk_size_t ki = 0; ki < 16; ki++) {
287
+ for (nk_size_t sub = 0; sub < 2; sub++) {
288
+ nk_size_t row = kv_block * 16 + ki;
289
+ nk_size_t col = 2 * depth_step + sub;
290
+ nk_bf16_t zero = {0};
291
+ vec_out[2 * ki + sub] = (row < seq_len && col < head_dim) ? k_head[row * head_dim + col] : zero;
292
+ }
293
+ }
294
+ }
295
+ }
296
+
297
+ // V packing: BFMOPA-interleaved format
298
+ nk_bf16_t *v_out = v_packed + h * head_elems;
299
+ for (nk_size_t kv_block = 0; kv_block < kv_block_count; kv_block++) {
300
+ for (nk_size_t dim_tile = 0; dim_tile < dim_tile_count; dim_tile++) {
301
+ for (nk_size_t depth_step = 0; depth_step < 8; depth_step++) {
302
+ nk_bf16_t *vec_out = v_out + (kv_block * dim_tile_count * 8 + dim_tile * 8 + depth_step) * 32;
303
+ for (nk_size_t dj = 0; dj < 16; dj++) {
304
+ for (nk_size_t sub = 0; sub < 2; sub++) {
305
+ nk_size_t ki = kv_block * 16 + 2 * depth_step + sub;
306
+ nk_size_t d = dim_tile * 16 + dj;
307
+ nk_bf16_t zero = {0};
308
+ vec_out[2 * dj + sub] = (ki < seq_len && d < head_dim) ? v_head[ki * head_dim + d] : zero;
309
+ }
310
+ }
311
+ }
312
+ }
313
+ }
314
+ }
315
+ }
316
+
317
+ NK_PUBLIC void nk_attention_pack_kv_bf16_sme( //
318
+ nk_bf16_t const *k, nk_bf16_t const *v, nk_size_t num_kv_heads, nk_size_t head_dim, nk_size_t seq_len,
319
+ nk_size_t k_stride, nk_size_t v_stride, void *kv_packed) {
320
+ nk_sme_start_streaming_();
321
+ nk_attention_pack_kv_bf16_sme_ssve_(k, v, num_kv_heads, head_dim, seq_len, k_stride, v_stride, kv_packed);
322
+ nk_sme_stop_streaming_();
323
+ }
324
+
325
+ static void nk_attention_pack_kv_f16_sme_ssve_( //
326
+ nk_f16_t const *k, nk_f16_t const *v, nk_size_t num_kv_heads, nk_size_t head_dim, nk_size_t seq_len,
327
+ nk_size_t k_stride, nk_size_t v_stride, void *kv_packed) NK_STREAMING_ {
328
+
329
+ nk_attention_sme_packed_header_t *header = (nk_attention_sme_packed_header_t *)kv_packed;
330
+ nk_size_t head_dim_padded = (head_dim + 31) / 32 * 32;
331
+ nk_size_t dim_tile_count = (head_dim_padded + 15) / 16;
332
+ nk_size_t kv_block_count = (seq_len + 15) / 16;
333
+
334
+ nk_size_t k_depth_step_count = head_dim_padded / 2;
335
+ nk_size_t head_elems = kv_block_count * 16 * head_dim_padded;
336
+
337
+ header->num_kv_heads = (nk_u32_t)num_kv_heads;
338
+ header->head_dim = (nk_u32_t)head_dim;
339
+ header->head_dim_padded = (nk_u32_t)head_dim_padded;
340
+ header->seq_len = (nk_u32_t)seq_len;
341
+ header->k_offset = sizeof(nk_attention_sme_packed_header_t);
342
+ header->reserved[0] = (nk_u32_t)dim_tile_count; // v_dim_tile_count
343
+ header->v_offset = header->k_offset + (nk_u32_t)(num_kv_heads * head_elems * sizeof(nk_f16_t));
344
+
345
+ nk_f16_t *k_packed = (nk_f16_t *)((char *)kv_packed + header->k_offset);
346
+ nk_f16_t *v_packed = (nk_f16_t *)((char *)kv_packed + header->v_offset);
347
+
348
+ for (nk_size_t h = 0; h < num_kv_heads; h++) {
349
+ nk_f16_t const *k_head = k + h * k_stride;
350
+ nk_f16_t const *v_head = v + h * v_stride;
351
+
352
+ // K packing: FMOPA-interleaved format
353
+ nk_f16_t *k_out = k_packed + h * head_elems;
354
+ for (nk_size_t kv_block = 0; kv_block < kv_block_count; kv_block++) {
355
+ for (nk_size_t depth_step = 0; depth_step < k_depth_step_count; depth_step++) {
356
+ nk_f16_t *vec_out = k_out + (kv_block * k_depth_step_count + depth_step) * 32;
357
+ for (nk_size_t ki = 0; ki < 16; ki++) {
358
+ for (nk_size_t sub = 0; sub < 2; sub++) {
359
+ nk_size_t row = kv_block * 16 + ki;
360
+ nk_size_t col = 2 * depth_step + sub;
361
+ nk_f16_t zero = {0};
362
+ vec_out[2 * ki + sub] = (row < seq_len && col < head_dim) ? k_head[row * head_dim + col] : zero;
363
+ }
364
+ }
365
+ }
366
+ }
367
+
368
+ // V packing: FMOPA-interleaved format
369
+ nk_f16_t *v_out = v_packed + h * head_elems;
370
+ for (nk_size_t kv_block = 0; kv_block < kv_block_count; kv_block++) {
371
+ for (nk_size_t dim_tile = 0; dim_tile < dim_tile_count; dim_tile++) {
372
+ for (nk_size_t depth_step = 0; depth_step < 8; depth_step++) {
373
+ nk_f16_t *vec_out = v_out + (kv_block * dim_tile_count * 8 + dim_tile * 8 + depth_step) * 32;
374
+ for (nk_size_t dj = 0; dj < 16; dj++) {
375
+ for (nk_size_t sub = 0; sub < 2; sub++) {
376
+ nk_size_t ki = kv_block * 16 + 2 * depth_step + sub;
377
+ nk_size_t d = dim_tile * 16 + dj;
378
+ nk_f16_t zero = {0};
379
+ vec_out[2 * dj + sub] = (ki < seq_len && d < head_dim) ? v_head[ki * head_dim + d] : zero;
380
+ }
381
+ }
382
+ }
383
+ }
384
+ }
385
+ }
386
+ }
387
+
388
+ NK_PUBLIC void nk_attention_pack_kv_f16_sme( //
389
+ nk_f16_t const *k, nk_f16_t const *v, nk_size_t num_kv_heads, nk_size_t head_dim, nk_size_t seq_len,
390
+ nk_size_t k_stride, nk_size_t v_stride, void *kv_packed) {
391
+ nk_sme_start_streaming_();
392
+ nk_attention_pack_kv_f16_sme_ssve_(k, v, num_kv_heads, head_dim, seq_len, k_stride, v_stride, kv_packed);
393
+ nk_sme_stop_streaming_();
394
+ }
395
+
396
+ /**
397
+ * @brief Optimized bf16 attention kernel with BFMOPA P×V.
398
+ *
399
+ * Key design choices:
400
+ * - P×V uses BFMOPA with pre-packed V (4-tile accumulation) instead of element-wise SVE
401
+ * - Scores read via column-wise vertical ZA reads for vectorized max/exp
402
+ * - Weights stored directly as bf16 (no f32 round-trip)
403
+ * - Uses degree-3 fast exp for softmax
404
+ * - Correction skip when running max is unchanged
405
+ * - Decode path (valid_query_count==1) remains element-wise SVE (BFMOPA overhead too high)
406
+ */
407
+ __arm_new("za") static void nk_attention_bf16_sme_streaming_(
408
+ nk_bf16_t const *q, // [query_len, head_dim]
409
+ nk_bf16_t const *k, // [kv_len, head_dim_padded] BFMOPA-interleaved
410
+ nk_bf16_t const *v_packed, // BFMOPA-interleaved V for this KV head
411
+ nk_bf16_t *output, // [query_len, head_dim]
412
+ nk_size_t query_len, nk_size_t kv_len, nk_size_t head_dim, nk_size_t head_dim_padded, nk_size_t dim_tile_count,
413
+ nk_f32_t scale) NK_STREAMING_ {
414
+
415
+ svbool_t const predicate_all_b32x = svptrue_b32();
416
+ svbool_t const predicate_all_b16x = svptrue_b16();
417
+ nk_size_t const valid_query_count = (query_len < 16) ? query_len : 16;
418
+
419
+ svfloat32_t row_max_f32x = svdup_f32(NK_F32_MIN);
420
+ svfloat32_t row_sum_f32x = svdup_f32(0.0f);
421
+
422
+ NK_ALIGN64 nk_f32_t output_accumulator[16 * 256];
423
+ svfloat32_t zero_f32x = svdup_f32(0.0f);
424
+ for (nk_size_t i = 0; i < 16 * head_dim_padded; i += svcntw()) {
425
+ svst1_f32(predicate_all_b32x, output_accumulator + i, zero_f32x);
426
+ }
427
+
428
+ nk_size_t kv_block_index = 0;
429
+ nk_size_t kv_start = 0;
430
+ svbool_t const batch_predicate_b32x = svwhilelt_b32(0u, 16u);
431
+
432
+ nk_size_t const k_depth_step_count = head_dim_padded / 2;
433
+
434
+ // Pre-transpose Q once: queries_transposed[step][16 f32 words]
435
+ NK_ALIGN64 nk_f32_t queries_transposed[128 * 16]; // max head_dim_padded/2 * 16 = 128 * 16
436
+ for (nk_size_t batch = 0; batch < head_dim_padded / 32; batch++) {
437
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
438
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++)
439
+ svld1_hor_za32(0, query_index, batch_predicate_b32x,
440
+ (nk_f32_t const *)(q + query_index * head_dim + batch * 32));
441
+ for (nk_size_t step = 0; step < 16; step++)
442
+ svst1_f32(predicate_all_b32x, queries_transposed + (batch * 16 + step) * 16,
443
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, step));
444
+ }
445
+
446
+ // Bc=32 main loop (prefill only, skipped for decode)
447
+ if (valid_query_count > 1) {
448
+ for (; kv_start + 32 <= kv_len; kv_start += 32, kv_block_index += 2) {
449
+ // Q×K^T: pure memory→BFMOPA, no ZA staging for Q or K
450
+ svzero_mask_za(nk_sme_zero_za32_tile_2_);
451
+ svzero_mask_za(nk_sme_zero_za32_tile_3_);
452
+ nk_bf16_t const *keys_block_low = k + kv_block_index * k_depth_step_count * 32;
453
+ nk_bf16_t const *keys_block_high = k + (kv_block_index + 1) * k_depth_step_count * 32;
454
+ for (nk_size_t step = 0; step < k_depth_step_count; step++) {
455
+ svbfloat16_t zn_bf16x = svreinterpret_bf16_f32(
456
+ svld1_f32(predicate_all_b32x, queries_transposed + step * 16));
457
+ svbfloat16_t zm0_bf16x = svld1_bf16(predicate_all_b16x,
458
+ (bfloat16_t const *)(keys_block_low + step * 32));
459
+ svbfloat16_t zm1_bf16x = svld1_bf16(predicate_all_b16x,
460
+ (bfloat16_t const *)(keys_block_high + step * 32));
461
+ svmopa_za32_bf16_m(2, predicate_all_b32x, predicate_all_b32x, zn_bf16x, zm0_bf16x);
462
+ svmopa_za32_bf16_m(3, predicate_all_b32x, predicate_all_b32x, zn_bf16x, zm1_bf16x);
463
+ }
464
+
465
+ // Pass 1: Column-wise max (read ZA2/ZA3 columns vertically)
466
+ svfloat32_t scale_f32x = svdup_f32(scale);
467
+ svfloat32_t block_max_f32x = svdup_f32(NK_F32_MIN);
468
+ for (nk_size_t column_index = 0; column_index < 16; column_index++) {
469
+ svfloat32_t score_column_f32x = svmul_f32_x(
470
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, column_index),
471
+ scale_f32x);
472
+ block_max_f32x = svmax_f32_x(predicate_all_b32x, block_max_f32x, score_column_f32x);
473
+ }
474
+ for (nk_size_t column_index = 0; column_index < 16; column_index++) {
475
+ svfloat32_t score_column_f32x = svmul_f32_x(
476
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 3, column_index),
477
+ scale_f32x);
478
+ block_max_f32x = svmax_f32_x(predicate_all_b32x, block_max_f32x, score_column_f32x);
479
+ }
480
+
481
+ // Softmax correction (fully vectorized)
482
+ svfloat32_t new_max_f32x = svmax_f32_x(predicate_all_b32x, row_max_f32x, block_max_f32x);
483
+ svfloat32_t correction_f32x = nk_exp_fast_f32_sve_(
484
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, row_max_f32x, new_max_f32x));
485
+ svbool_t max_changed_b32x = svcmplt_f32(predicate_all_b32x, correction_f32x, svdup_f32(1.0f));
486
+ nk_u32_t max_was_updated = svptest_any(predicate_all_b32x, max_changed_b32x) ? 1 : 0;
487
+ if (max_was_updated) row_sum_f32x = svmul_f32_x(predicate_all_b32x, row_sum_f32x, correction_f32x);
488
+ NK_ALIGN64 nk_f32_t corrections[16];
489
+ svst1_f32(predicate_all_b32x, corrections, correction_f32x);
490
+
491
+ // Pass 2: Column-wise exp + fused P write + sum
492
+ svfloat32_t sum_delta_f32x = svdup_f32(0.0f);
493
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
494
+ // ZA2 columns in pairs → ZA0 columns 0-7
495
+ for (nk_size_t column_index = 0; column_index < 16; column_index += 2) {
496
+ svfloat32_t score_even_f32x = svmul_f32_x(
497
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, column_index),
498
+ scale_f32x);
499
+ svfloat32_t score_odd_f32x = svmul_f32_x(
500
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, column_index + 1),
501
+ scale_f32x);
502
+ svfloat32_t weight_even_f32x = nk_exp_fast_f32_sve_(
503
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, score_even_f32x, new_max_f32x));
504
+ svfloat32_t weight_odd_f32x = nk_exp_fast_f32_sve_(
505
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, score_odd_f32x, new_max_f32x));
506
+ sum_delta_f32x = svadd_f32_x(predicate_all_b32x, sum_delta_f32x, weight_even_f32x);
507
+ sum_delta_f32x = svadd_f32_x(predicate_all_b32x, sum_delta_f32x, weight_odd_f32x);
508
+ svbfloat16_t weight_pair_bf16x = svzip1_bf16(nk_f32_to_bf16_sve_(predicate_all_b32x, weight_even_f32x),
509
+ nk_f32_to_bf16_sve_(predicate_all_b32x, weight_odd_f32x));
510
+ svwrite_ver_za32_f32_m(0, column_index / 2, predicate_all_b32x,
511
+ svreinterpret_f32_bf16(weight_pair_bf16x));
512
+ }
513
+ // ZA3 columns in pairs → ZA0 columns 8-15
514
+ for (nk_size_t column_index = 0; column_index < 16; column_index += 2) {
515
+ svfloat32_t score_even_f32x = svmul_f32_x(
516
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 3, column_index),
517
+ scale_f32x);
518
+ svfloat32_t score_odd_f32x = svmul_f32_x(
519
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 3, column_index + 1),
520
+ scale_f32x);
521
+ svfloat32_t weight_even_f32x = nk_exp_fast_f32_sve_(
522
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, score_even_f32x, new_max_f32x));
523
+ svfloat32_t weight_odd_f32x = nk_exp_fast_f32_sve_(
524
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, score_odd_f32x, new_max_f32x));
525
+ sum_delta_f32x = svadd_f32_x(predicate_all_b32x, sum_delta_f32x, weight_even_f32x);
526
+ sum_delta_f32x = svadd_f32_x(predicate_all_b32x, sum_delta_f32x, weight_odd_f32x);
527
+ svbfloat16_t weight_pair_bf16x = svzip1_bf16(nk_f32_to_bf16_sve_(predicate_all_b32x, weight_even_f32x),
528
+ nk_f32_to_bf16_sve_(predicate_all_b32x, weight_odd_f32x));
529
+ svwrite_ver_za32_f32_m(0, 8 + column_index / 2, predicate_all_b32x,
530
+ svreinterpret_f32_bf16(weight_pair_bf16x));
531
+ }
532
+ row_sum_f32x = svadd_f32_x(predicate_all_b32x, row_sum_f32x, sum_delta_f32x);
533
+ row_max_f32x = new_max_f32x;
534
+
535
+ // Extract P columns from ZA0
536
+ svbfloat16_t probability_column_0_f32x = svreinterpret_bf16_f32(
537
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 0));
538
+ svbfloat16_t probability_column_1_f32x = svreinterpret_bf16_f32(
539
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 1));
540
+ svbfloat16_t probability_column_2_f32x = svreinterpret_bf16_f32(
541
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 2));
542
+ svbfloat16_t probability_column_3_f32x = svreinterpret_bf16_f32(
543
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 3));
544
+ svbfloat16_t probability_column_4_f32x = svreinterpret_bf16_f32(
545
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 4));
546
+ svbfloat16_t probability_column_5_f32x = svreinterpret_bf16_f32(
547
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 5));
548
+ svbfloat16_t probability_column_6_f32x = svreinterpret_bf16_f32(
549
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 6));
550
+ svbfloat16_t probability_column_7_f32x = svreinterpret_bf16_f32(
551
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 7));
552
+ svbfloat16_t probability_column_8_f32x = svreinterpret_bf16_f32(
553
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 8));
554
+ svbfloat16_t probability_column_9_f32x = svreinterpret_bf16_f32(
555
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 9));
556
+ svbfloat16_t probability_column_10_f32x = svreinterpret_bf16_f32(
557
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 10));
558
+ svbfloat16_t probability_column_11_f32x = svreinterpret_bf16_f32(
559
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 11));
560
+ svbfloat16_t probability_column_12_f32x = svreinterpret_bf16_f32(
561
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 12));
562
+ svbfloat16_t probability_column_13_f32x = svreinterpret_bf16_f32(
563
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 13));
564
+ svbfloat16_t probability_column_14_f32x = svreinterpret_bf16_f32(
565
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 14));
566
+ svbfloat16_t probability_column_15_f32x = svreinterpret_bf16_f32(
567
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 15));
568
+
569
+ // Pre-apply correction once before P×V
570
+ svbool_t query_predicate_b16x = svwhilelt_b16_u64(0u, valid_query_count * 2);
571
+ nk_bf16_t const *values_block_low = v_packed + kv_block_index * dim_tile_count * 8 * 32;
572
+ nk_bf16_t const *values_block_high = v_packed + (kv_block_index + 1) * dim_tile_count * 8 * 32;
573
+
574
+ if (max_was_updated) {
575
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++) {
576
+ svfloat32_t correction_scalar_f32x = svdup_f32(corrections[query_index]);
577
+ for (nk_size_t dim_offset = 0; dim_offset < head_dim_padded; dim_offset += 16)
578
+ svst1_f32(
579
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + dim_offset,
580
+ svmul_f32_x(predicate_all_b32x,
581
+ svld1_f32(predicate_all_b32x,
582
+ output_accumulator + query_index * head_dim_padded + dim_offset),
583
+ correction_scalar_f32x));
584
+ }
585
+ }
586
+
587
+ // P×V: zero → BFMOPA → read → add (no ZA writes for output_accumulator)
588
+ nk_size_t dim_tile = 0;
589
+ for (; dim_tile + 4 <= dim_tile_count; dim_tile += 4) {
590
+ svzero_za();
591
+ // Block0: 8 depth steps (KV positions 0-15)
592
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
593
+ svld1_bf16(predicate_all_b16x,
594
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 0) * 32)));
595
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
596
+ svld1_bf16(predicate_all_b16x,
597
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 0) * 32)));
598
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
599
+ svld1_bf16(predicate_all_b16x,
600
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 0) * 32)));
601
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
602
+ svld1_bf16(predicate_all_b16x,
603
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 0) * 32)));
604
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
605
+ svld1_bf16(predicate_all_b16x,
606
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 1) * 32)));
607
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
608
+ svld1_bf16(predicate_all_b16x,
609
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 1) * 32)));
610
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
611
+ svld1_bf16(predicate_all_b16x,
612
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 1) * 32)));
613
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
614
+ svld1_bf16(predicate_all_b16x,
615
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 1) * 32)));
616
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
617
+ svld1_bf16(predicate_all_b16x,
618
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 2) * 32)));
619
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
620
+ svld1_bf16(predicate_all_b16x,
621
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 2) * 32)));
622
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
623
+ svld1_bf16(predicate_all_b16x,
624
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 2) * 32)));
625
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
626
+ svld1_bf16(predicate_all_b16x,
627
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 2) * 32)));
628
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
629
+ svld1_bf16(predicate_all_b16x,
630
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 3) * 32)));
631
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
632
+ svld1_bf16(predicate_all_b16x,
633
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 3) * 32)));
634
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
635
+ svld1_bf16(predicate_all_b16x,
636
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 3) * 32)));
637
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
638
+ svld1_bf16(predicate_all_b16x,
639
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 3) * 32)));
640
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
641
+ svld1_bf16(predicate_all_b16x,
642
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 4) * 32)));
643
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
644
+ svld1_bf16(predicate_all_b16x,
645
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 4) * 32)));
646
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
647
+ svld1_bf16(predicate_all_b16x,
648
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 4) * 32)));
649
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
650
+ svld1_bf16(predicate_all_b16x,
651
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 4) * 32)));
652
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
653
+ svld1_bf16(predicate_all_b16x,
654
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 5) * 32)));
655
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
656
+ svld1_bf16(predicate_all_b16x,
657
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 5) * 32)));
658
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
659
+ svld1_bf16(predicate_all_b16x,
660
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 5) * 32)));
661
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
662
+ svld1_bf16(predicate_all_b16x,
663
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 5) * 32)));
664
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
665
+ svld1_bf16(predicate_all_b16x,
666
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 6) * 32)));
667
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
668
+ svld1_bf16(predicate_all_b16x,
669
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 6) * 32)));
670
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
671
+ svld1_bf16(predicate_all_b16x,
672
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 6) * 32)));
673
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
674
+ svld1_bf16(predicate_all_b16x,
675
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 6) * 32)));
676
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
677
+ svld1_bf16(predicate_all_b16x,
678
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 7) * 32)));
679
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
680
+ svld1_bf16(predicate_all_b16x,
681
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 7) * 32)));
682
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
683
+ svld1_bf16(predicate_all_b16x,
684
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 7) * 32)));
685
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
686
+ svld1_bf16(predicate_all_b16x,
687
+ (bfloat16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 7) * 32)));
688
+ // Block1: 8 depth steps (KV positions 16-31)
689
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_8_f32x,
690
+ svld1_bf16(predicate_all_b16x,
691
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 0) * 32)));
692
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_8_f32x,
693
+ svld1_bf16(predicate_all_b16x,
694
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 0) * 32)));
695
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_8_f32x,
696
+ svld1_bf16(predicate_all_b16x,
697
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 0) * 32)));
698
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_8_f32x,
699
+ svld1_bf16(predicate_all_b16x,
700
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 0) * 32)));
701
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_9_f32x,
702
+ svld1_bf16(predicate_all_b16x,
703
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 1) * 32)));
704
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_9_f32x,
705
+ svld1_bf16(predicate_all_b16x,
706
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 1) * 32)));
707
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_9_f32x,
708
+ svld1_bf16(predicate_all_b16x,
709
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 1) * 32)));
710
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_9_f32x,
711
+ svld1_bf16(predicate_all_b16x,
712
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 1) * 32)));
713
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_10_f32x,
714
+ svld1_bf16(predicate_all_b16x,
715
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 2) * 32)));
716
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_10_f32x,
717
+ svld1_bf16(predicate_all_b16x,
718
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 2) * 32)));
719
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_10_f32x,
720
+ svld1_bf16(predicate_all_b16x,
721
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 2) * 32)));
722
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_10_f32x,
723
+ svld1_bf16(predicate_all_b16x,
724
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 2) * 32)));
725
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_11_f32x,
726
+ svld1_bf16(predicate_all_b16x,
727
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 3) * 32)));
728
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_11_f32x,
729
+ svld1_bf16(predicate_all_b16x,
730
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 3) * 32)));
731
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_11_f32x,
732
+ svld1_bf16(predicate_all_b16x,
733
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 3) * 32)));
734
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_11_f32x,
735
+ svld1_bf16(predicate_all_b16x,
736
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 3) * 32)));
737
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_12_f32x,
738
+ svld1_bf16(predicate_all_b16x,
739
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 4) * 32)));
740
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_12_f32x,
741
+ svld1_bf16(predicate_all_b16x,
742
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 4) * 32)));
743
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_12_f32x,
744
+ svld1_bf16(predicate_all_b16x,
745
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 4) * 32)));
746
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_12_f32x,
747
+ svld1_bf16(predicate_all_b16x,
748
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 4) * 32)));
749
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_13_f32x,
750
+ svld1_bf16(predicate_all_b16x,
751
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 5) * 32)));
752
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_13_f32x,
753
+ svld1_bf16(predicate_all_b16x,
754
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 5) * 32)));
755
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_13_f32x,
756
+ svld1_bf16(predicate_all_b16x,
757
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 5) * 32)));
758
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_13_f32x,
759
+ svld1_bf16(predicate_all_b16x,
760
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 5) * 32)));
761
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_14_f32x,
762
+ svld1_bf16(predicate_all_b16x,
763
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 6) * 32)));
764
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_14_f32x,
765
+ svld1_bf16(predicate_all_b16x,
766
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 6) * 32)));
767
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_14_f32x,
768
+ svld1_bf16(predicate_all_b16x,
769
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 6) * 32)));
770
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_14_f32x,
771
+ svld1_bf16(predicate_all_b16x,
772
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 6) * 32)));
773
+ svmopa_za32_bf16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_15_f32x,
774
+ svld1_bf16(predicate_all_b16x,
775
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 7) * 32)));
776
+ svmopa_za32_bf16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_15_f32x,
777
+ svld1_bf16(predicate_all_b16x,
778
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 7) * 32)));
779
+ svmopa_za32_bf16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_15_f32x,
780
+ svld1_bf16(predicate_all_b16x,
781
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 7) * 32)));
782
+ svmopa_za32_bf16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_15_f32x,
783
+ svld1_bf16(predicate_all_b16x,
784
+ (bfloat16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 7) * 32)));
785
+ // Read BFMOPA result and ADD to output_accumulator
786
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++) {
787
+ svst1_f32(
788
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 0) * 16,
789
+ svadd_f32_x(predicate_all_b32x,
790
+ svld1_f32(predicate_all_b32x,
791
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 0) * 16),
792
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, query_index)));
793
+ svst1_f32(
794
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 1) * 16,
795
+ svadd_f32_x(predicate_all_b32x,
796
+ svld1_f32(predicate_all_b32x,
797
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 1) * 16),
798
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 1, query_index)));
799
+ svst1_f32(
800
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 2) * 16,
801
+ svadd_f32_x(predicate_all_b32x,
802
+ svld1_f32(predicate_all_b32x,
803
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 2) * 16),
804
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, query_index)));
805
+ svst1_f32(
806
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 3) * 16,
807
+ svadd_f32_x(predicate_all_b32x,
808
+ svld1_f32(predicate_all_b32x,
809
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 3) * 16),
810
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 3, query_index)));
811
+ }
812
+ }
813
+ // Remainder: 1 dim_tile at a time using ZA0
814
+ for (; dim_tile < dim_tile_count; dim_tile++) {
815
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
816
+ svmopa_za32_bf16_m(
817
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
818
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_low + (dim_tile * 8 + 0) * 32)));
819
+ svmopa_za32_bf16_m(
820
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
821
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_low + (dim_tile * 8 + 1) * 32)));
822
+ svmopa_za32_bf16_m(
823
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
824
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_low + (dim_tile * 8 + 2) * 32)));
825
+ svmopa_za32_bf16_m(
826
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
827
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_low + (dim_tile * 8 + 3) * 32)));
828
+ svmopa_za32_bf16_m(
829
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
830
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_low + (dim_tile * 8 + 4) * 32)));
831
+ svmopa_za32_bf16_m(
832
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
833
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_low + (dim_tile * 8 + 5) * 32)));
834
+ svmopa_za32_bf16_m(
835
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
836
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_low + (dim_tile * 8 + 6) * 32)));
837
+ svmopa_za32_bf16_m(
838
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
839
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_low + (dim_tile * 8 + 7) * 32)));
840
+ svmopa_za32_bf16_m(
841
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_8_f32x,
842
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_high + (dim_tile * 8 + 0) * 32)));
843
+ svmopa_za32_bf16_m(
844
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_9_f32x,
845
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_high + (dim_tile * 8 + 1) * 32)));
846
+ svmopa_za32_bf16_m(
847
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_10_f32x,
848
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_high + (dim_tile * 8 + 2) * 32)));
849
+ svmopa_za32_bf16_m(
850
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_11_f32x,
851
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_high + (dim_tile * 8 + 3) * 32)));
852
+ svmopa_za32_bf16_m(
853
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_12_f32x,
854
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_high + (dim_tile * 8 + 4) * 32)));
855
+ svmopa_za32_bf16_m(
856
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_13_f32x,
857
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_high + (dim_tile * 8 + 5) * 32)));
858
+ svmopa_za32_bf16_m(
859
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_14_f32x,
860
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_high + (dim_tile * 8 + 6) * 32)));
861
+ svmopa_za32_bf16_m(
862
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_15_f32x,
863
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(values_block_high + (dim_tile * 8 + 7) * 32)));
864
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++)
865
+ svst1_f32(predicate_all_b32x, output_accumulator + query_index * head_dim_padded + dim_tile * 16,
866
+ svadd_f32_x(predicate_all_b32x,
867
+ svld1_f32(predicate_all_b32x,
868
+ output_accumulator + query_index * head_dim_padded + dim_tile * 16),
869
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, query_index)));
870
+ }
871
+ }
872
+ }
873
+
874
+ // Bc=16 tail loop (handles remaining KV positions and decode path)
875
+ for (; kv_start < kv_len; kv_start += 16, kv_block_index++) {
876
+ nk_size_t const valid_kv = ((kv_start + 16) <= kv_len) ? 16 : (kv_len - kv_start);
877
+
878
+ // Q×K^T: pure memory→BFMOPA, no ZA staging
879
+ svzero_mask_za(nk_sme_zero_za32_tile_2_);
880
+ nk_bf16_t const *k_block = k + kv_block_index * k_depth_step_count * 32;
881
+ for (nk_size_t step = 0; step < k_depth_step_count; step++) {
882
+ svbfloat16_t zn_bf16x = svreinterpret_bf16_f32(
883
+ svld1_f32(predicate_all_b32x, queries_transposed + step * 16));
884
+ svbfloat16_t zm_bf16x = svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(k_block + step * 32));
885
+ svmopa_za32_bf16_m(2, predicate_all_b32x, predicate_all_b32x, zn_bf16x, zm_bf16x);
886
+ }
887
+
888
+ // Pass 1: Column-wise max (read ZA2 columns vertically)
889
+ svfloat32_t scale_16_f32x = svdup_f32(scale);
890
+ svfloat32_t block_max_16_f32x = svdup_f32(NK_F32_MIN);
891
+ for (nk_size_t column_index = 0; column_index < 16; column_index++) {
892
+ svfloat32_t score_column_f32x = svmul_f32_x(
893
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, column_index),
894
+ scale_16_f32x);
895
+ block_max_16_f32x = svmax_f32_x(predicate_all_b32x, block_max_16_f32x, score_column_f32x);
896
+ }
897
+
898
+ // Softmax correction (fully vectorized)
899
+ svfloat32_t new_max_f32x = svmax_f32_x(predicate_all_b32x, row_max_f32x, block_max_16_f32x);
900
+ svfloat32_t correction_f32x = nk_exp_fast_f32_sve_(predicate_all_b32x,
901
+ svsub_f32_x(predicate_all_b32x, row_max_f32x, new_max_f32x));
902
+ svbool_t max_changed_16_b32x = svcmplt_f32(predicate_all_b32x, correction_f32x, svdup_f32(1.0f));
903
+ nk_u32_t max_was_updated_16 = svptest_any(predicate_all_b32x, max_changed_16_b32x) ? 1 : 0;
904
+ if (max_was_updated_16) row_sum_f32x = svmul_f32_x(predicate_all_b32x, row_sum_f32x, correction_f32x);
905
+ NK_ALIGN64 nk_f32_t corrections[16];
906
+ svst1_f32(predicate_all_b32x, corrections, correction_f32x);
907
+
908
+ // Pass 2: Column-wise exp + fused P write + sum (ZA2 → ZA0 columns 0-7)
909
+ svfloat32_t sum_delta_16_f32x = svdup_f32(0.0f);
910
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
911
+ for (nk_size_t column_index = 0; column_index < 16; column_index += 2) {
912
+ svfloat32_t score_even_f32x = svmul_f32_x(
913
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, column_index),
914
+ scale_16_f32x);
915
+ svfloat32_t score_odd_f32x = svmul_f32_x(
916
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, column_index + 1),
917
+ scale_16_f32x);
918
+ svfloat32_t weight_even_f32x = nk_exp_fast_f32_sve_(
919
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, score_even_f32x, new_max_f32x));
920
+ svfloat32_t weight_odd_f32x = nk_exp_fast_f32_sve_(
921
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, score_odd_f32x, new_max_f32x));
922
+ sum_delta_16_f32x = svadd_f32_x(predicate_all_b32x, sum_delta_16_f32x, weight_even_f32x);
923
+ sum_delta_16_f32x = svadd_f32_x(predicate_all_b32x, sum_delta_16_f32x, weight_odd_f32x);
924
+ svbfloat16_t weight_pair_bf16x = svzip1_bf16(nk_f32_to_bf16_sve_(predicate_all_b32x, weight_even_f32x),
925
+ nk_f32_to_bf16_sve_(predicate_all_b32x, weight_odd_f32x));
926
+ svwrite_ver_za32_f32_m(0, column_index / 2, predicate_all_b32x, svreinterpret_f32_bf16(weight_pair_bf16x));
927
+ }
928
+ row_sum_f32x = svadd_f32_x(predicate_all_b32x, row_sum_f32x, sum_delta_16_f32x);
929
+ row_max_f32x = new_max_f32x;
930
+
931
+ if (valid_query_count == 1) {
932
+ // Decode path: extract f32 weights from ZA0 row 0 using SVE
933
+ svbfloat16_t row0_bf16x = svreinterpret_bf16_f32(
934
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 0));
935
+ svbfloat16_t weights_even_bf16x = svuzp1_bf16(row0_bf16x, row0_bf16x);
936
+ svbfloat16_t weights_odd_bf16x = svuzp2_bf16(row0_bf16x, row0_bf16x);
937
+ NK_ALIGN64 nk_f32_t decode_weights[16];
938
+ svst1_f32(svwhilelt_b32(0u, 8u), decode_weights,
939
+ nk_bf16_to_f32_sve_(svwhilelt_b32(0u, 8u), weights_even_bf16x));
940
+ svst1_f32(svwhilelt_b32(0u, 8u), decode_weights + 8,
941
+ nk_bf16_to_f32_sve_(svwhilelt_b32(0u, 8u), weights_odd_bf16x));
942
+ NK_ALIGN64 nk_f32_t decode_weights_ordered[16];
943
+ for (nk_size_t i = 0; i < 8; i++) {
944
+ decode_weights_ordered[2 * i] = decode_weights[i];
945
+ decode_weights_ordered[2 * i + 1] = decode_weights[8 + i];
946
+ }
947
+ svfloat32_t corr_f32x = svdup_f32(corrections[0]);
948
+ for (nk_size_t d = 0; d < head_dim; d += svcntw()) {
949
+ svbool_t predicate_b32x = svwhilelt_b32_u64(d, head_dim);
950
+ svfloat32_t acc_f32x = svmul_f32_x(predicate_b32x, svld1_f32(predicate_b32x, output_accumulator + d),
951
+ corr_f32x);
952
+ for (nk_size_t ki = 0; ki < valid_kv; ki++) {
953
+ nk_size_t dim_tile = d / 16, depth_s = ki / 2, sub = ki % 2;
954
+ nk_bf16_t const *v_vec = v_packed +
955
+ (kv_block_index * dim_tile_count * 8 + dim_tile * 8 + depth_s) * 32;
956
+ svbfloat16_t packed_bf16x = svld1_bf16(predicate_all_b16x, (bfloat16_t const *)v_vec);
957
+ svbfloat16_t v_selected_bf16x = (sub == 0) ? svuzp1_bf16(packed_bf16x, packed_bf16x)
958
+ : svuzp2_bf16(packed_bf16x, packed_bf16x);
959
+ acc_f32x = svmla_f32_x(predicate_b32x, acc_f32x, svdup_f32(decode_weights_ordered[ki]),
960
+ nk_bf16_to_f32_sve_(predicate_b32x, v_selected_bf16x));
961
+ }
962
+ svst1_f32(predicate_b32x, output_accumulator + d, acc_f32x);
963
+ }
964
+ }
965
+ else {
966
+ // Prefill Bc=16: extract P columns, pre-apply correction, add-after P×V
967
+ svbool_t query_predicate_b16x = svwhilelt_b16_u64(0u, valid_query_count * 2);
968
+
969
+ svbfloat16_t probability_column_0_f32x = svreinterpret_bf16_f32(
970
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 0));
971
+ svbfloat16_t probability_column_1_f32x = svreinterpret_bf16_f32(
972
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 1));
973
+ svbfloat16_t probability_column_2_f32x = svreinterpret_bf16_f32(
974
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 2));
975
+ svbfloat16_t probability_column_3_f32x = svreinterpret_bf16_f32(
976
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 3));
977
+ svbfloat16_t probability_column_4_f32x = svreinterpret_bf16_f32(
978
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 4));
979
+ svbfloat16_t probability_column_5_f32x = svreinterpret_bf16_f32(
980
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 5));
981
+ svbfloat16_t probability_column_6_f32x = svreinterpret_bf16_f32(
982
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 6));
983
+ svbfloat16_t probability_column_7_f32x = svreinterpret_bf16_f32(
984
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 7));
985
+
986
+ nk_bf16_t const *v_block = v_packed + kv_block_index * dim_tile_count * 8 * 32;
987
+
988
+ // Pre-apply correction
989
+ if (max_was_updated_16) {
990
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++) {
991
+ svfloat32_t correction_scalar_f32x = svdup_f32(corrections[query_index]);
992
+ for (nk_size_t dim_offset = 0; dim_offset < head_dim_padded; dim_offset += 16)
993
+ svst1_f32(
994
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + dim_offset,
995
+ svmul_f32_x(predicate_all_b32x,
996
+ svld1_f32(predicate_all_b32x,
997
+ output_accumulator + query_index * head_dim_padded + dim_offset),
998
+ correction_scalar_f32x));
999
+ }
1000
+ }
1001
+
1002
+ // P×V: zero → BFMOPA → read → add
1003
+ nk_size_t dim_tile = 0;
1004
+ for (; dim_tile + 4 <= dim_tile_count; dim_tile += 4) {
1005
+ svzero_za();
1006
+ svmopa_za32_bf16_m(
1007
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1008
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 0) * 8 + 0) * 32)));
1009
+ svmopa_za32_bf16_m(
1010
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1011
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 1) * 8 + 0) * 32)));
1012
+ svmopa_za32_bf16_m(
1013
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1014
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 2) * 8 + 0) * 32)));
1015
+ svmopa_za32_bf16_m(
1016
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1017
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 3) * 8 + 0) * 32)));
1018
+ svmopa_za32_bf16_m(
1019
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1020
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 0) * 8 + 1) * 32)));
1021
+ svmopa_za32_bf16_m(
1022
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1023
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 1) * 8 + 1) * 32)));
1024
+ svmopa_za32_bf16_m(
1025
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1026
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 2) * 8 + 1) * 32)));
1027
+ svmopa_za32_bf16_m(
1028
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1029
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 3) * 8 + 1) * 32)));
1030
+ svmopa_za32_bf16_m(
1031
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1032
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 0) * 8 + 2) * 32)));
1033
+ svmopa_za32_bf16_m(
1034
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1035
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 1) * 8 + 2) * 32)));
1036
+ svmopa_za32_bf16_m(
1037
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1038
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 2) * 8 + 2) * 32)));
1039
+ svmopa_za32_bf16_m(
1040
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1041
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 3) * 8 + 2) * 32)));
1042
+ svmopa_za32_bf16_m(
1043
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1044
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 0) * 8 + 3) * 32)));
1045
+ svmopa_za32_bf16_m(
1046
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1047
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 1) * 8 + 3) * 32)));
1048
+ svmopa_za32_bf16_m(
1049
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1050
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 2) * 8 + 3) * 32)));
1051
+ svmopa_za32_bf16_m(
1052
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1053
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 3) * 8 + 3) * 32)));
1054
+ svmopa_za32_bf16_m(
1055
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1056
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 0) * 8 + 4) * 32)));
1057
+ svmopa_za32_bf16_m(
1058
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1059
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 1) * 8 + 4) * 32)));
1060
+ svmopa_za32_bf16_m(
1061
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1062
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 2) * 8 + 4) * 32)));
1063
+ svmopa_za32_bf16_m(
1064
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1065
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 3) * 8 + 4) * 32)));
1066
+ svmopa_za32_bf16_m(
1067
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1068
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 0) * 8 + 5) * 32)));
1069
+ svmopa_za32_bf16_m(
1070
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1071
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 1) * 8 + 5) * 32)));
1072
+ svmopa_za32_bf16_m(
1073
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1074
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 2) * 8 + 5) * 32)));
1075
+ svmopa_za32_bf16_m(
1076
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1077
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 3) * 8 + 5) * 32)));
1078
+ svmopa_za32_bf16_m(
1079
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1080
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 0) * 8 + 6) * 32)));
1081
+ svmopa_za32_bf16_m(
1082
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1083
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 1) * 8 + 6) * 32)));
1084
+ svmopa_za32_bf16_m(
1085
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1086
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 2) * 8 + 6) * 32)));
1087
+ svmopa_za32_bf16_m(
1088
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1089
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 3) * 8 + 6) * 32)));
1090
+ svmopa_za32_bf16_m(
1091
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1092
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 0) * 8 + 7) * 32)));
1093
+ svmopa_za32_bf16_m(
1094
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1095
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 1) * 8 + 7) * 32)));
1096
+ svmopa_za32_bf16_m(
1097
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1098
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 2) * 8 + 7) * 32)));
1099
+ svmopa_za32_bf16_m(
1100
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1101
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + ((dim_tile + 3) * 8 + 7) * 32)));
1102
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++) {
1103
+ svst1_f32(
1104
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 0) * 16,
1105
+ svadd_f32_x(predicate_all_b32x,
1106
+ svld1_f32(predicate_all_b32x,
1107
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 0) * 16),
1108
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, query_index)));
1109
+ svst1_f32(
1110
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 1) * 16,
1111
+ svadd_f32_x(predicate_all_b32x,
1112
+ svld1_f32(predicate_all_b32x,
1113
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 1) * 16),
1114
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 1, query_index)));
1115
+ svst1_f32(
1116
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 2) * 16,
1117
+ svadd_f32_x(predicate_all_b32x,
1118
+ svld1_f32(predicate_all_b32x,
1119
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 2) * 16),
1120
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, query_index)));
1121
+ svst1_f32(
1122
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 3) * 16,
1123
+ svadd_f32_x(predicate_all_b32x,
1124
+ svld1_f32(predicate_all_b32x,
1125
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 3) * 16),
1126
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 3, query_index)));
1127
+ }
1128
+ }
1129
+ for (; dim_tile < dim_tile_count; dim_tile++) {
1130
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
1131
+ svmopa_za32_bf16_m(
1132
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1133
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + (dim_tile * 8 + 0) * 32)));
1134
+ svmopa_za32_bf16_m(
1135
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1136
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + (dim_tile * 8 + 1) * 32)));
1137
+ svmopa_za32_bf16_m(
1138
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1139
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + (dim_tile * 8 + 2) * 32)));
1140
+ svmopa_za32_bf16_m(
1141
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1142
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + (dim_tile * 8 + 3) * 32)));
1143
+ svmopa_za32_bf16_m(
1144
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1145
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + (dim_tile * 8 + 4) * 32)));
1146
+ svmopa_za32_bf16_m(
1147
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1148
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + (dim_tile * 8 + 5) * 32)));
1149
+ svmopa_za32_bf16_m(
1150
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1151
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + (dim_tile * 8 + 6) * 32)));
1152
+ svmopa_za32_bf16_m(
1153
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1154
+ svld1_bf16(predicate_all_b16x, (bfloat16_t const *)(v_block + (dim_tile * 8 + 7) * 32)));
1155
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++)
1156
+ svst1_f32(predicate_all_b32x, output_accumulator + query_index * head_dim_padded + dim_tile * 16,
1157
+ svadd_f32_x(predicate_all_b32x,
1158
+ svld1_f32(predicate_all_b32x,
1159
+ output_accumulator + query_index * head_dim_padded + dim_tile * 16),
1160
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, query_index)));
1161
+ }
1162
+ }
1163
+ }
1164
+
1165
+ // Final normalization
1166
+ NK_ALIGN64 nk_f32_t final_sums[16];
1167
+ svst1_f32(predicate_all_b32x, final_sums, row_sum_f32x);
1168
+
1169
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++) {
1170
+ nk_f32_t inv_sum = (final_sums[query_index] > 0.0f) ? (1.0f / final_sums[query_index]) : 0.0f;
1171
+ svfloat32_t inv_sum_f32x = svdup_f32(inv_sum);
1172
+
1173
+ for (nk_size_t dim_offset = 0; dim_offset < head_dim; dim_offset += svcntw()) {
1174
+ svbool_t predicate_b32x = svwhilelt_b32_u64(dim_offset, head_dim);
1175
+ svfloat32_t output_f32x = svmul_f32_x(
1176
+ predicate_b32x,
1177
+ svld1_f32(predicate_b32x, output_accumulator + query_index * head_dim_padded + dim_offset),
1178
+ inv_sum_f32x);
1179
+ svbfloat16_t output_bf16x = nk_f32_to_bf16_sve_(predicate_b32x, output_f32x);
1180
+ nk_size_t store_count = (head_dim - dim_offset) < (nk_size_t)svcntw() ? (head_dim - dim_offset)
1181
+ : (nk_size_t)svcntw();
1182
+ svbool_t store_predicate_b16x = svwhilelt_b16_u64(0u, store_count);
1183
+ svst1_bf16(store_predicate_b16x, (bfloat16_t *)(output + query_index * head_dim + dim_offset),
1184
+ output_bf16x);
1185
+ }
1186
+ }
1187
+ }
1188
+
1189
+ NK_PUBLIC void nk_attention_bf16_sme( //
1190
+ nk_bf16_t const *q, void const *kv_packed, nk_bf16_t *output, nk_size_t num_heads, nk_size_t num_kv_heads,
1191
+ nk_size_t query_len, nk_size_t kv_len, nk_size_t head_dim, nk_f32_t scale) {
1192
+
1193
+ nk_attention_sme_packed_header_t const *header = (nk_attention_sme_packed_header_t const *)kv_packed;
1194
+ nk_size_t head_dim_padded = header->head_dim_padded;
1195
+ nk_size_t dim_tile_count = header->reserved[0]; // v_dim_tile_count
1196
+ nk_size_t kv_blocks = (kv_len + 15) / 16;
1197
+ nk_size_t kv_head_stride = kv_blocks * 16 * head_dim_padded;
1198
+
1199
+ nk_bf16_t const *k_packed = (nk_bf16_t const *)((char const *)kv_packed + header->k_offset);
1200
+ nk_bf16_t const *v_packed = (nk_bf16_t const *)((char const *)kv_packed + header->v_offset);
1201
+
1202
+ nk_size_t group_size = (num_kv_heads > 0) ? num_heads / num_kv_heads : 1;
1203
+
1204
+ nk_sme_start_streaming_();
1205
+ for (nk_size_t q_head = 0; q_head < num_heads; q_head++) {
1206
+ nk_size_t kv_head = q_head / group_size;
1207
+
1208
+ nk_bf16_t const *q_ptr = q + q_head * query_len * head_dim;
1209
+ nk_bf16_t const *k_ptr = k_packed + kv_head * kv_head_stride;
1210
+ nk_bf16_t const *v_ptr = v_packed + kv_head * kv_head_stride;
1211
+ nk_bf16_t *out_ptr = output + q_head * query_len * head_dim;
1212
+
1213
+ for (nk_size_t q_start = 0; q_start < query_len; q_start += 16) {
1214
+ nk_size_t q_block_len = (q_start + 16 < query_len) ? 16 : (query_len - q_start);
1215
+
1216
+ nk_attention_bf16_sme_streaming_(q_ptr + q_start * head_dim, k_ptr, v_ptr, out_ptr + q_start * head_dim,
1217
+ q_block_len, kv_len, head_dim, head_dim_padded, dim_tile_count, scale);
1218
+ }
1219
+ }
1220
+ nk_sme_stop_streaming_();
1221
+ }
1222
+
1223
+ __arm_new("za") static void nk_attention_f16_sme_streaming_( //
1224
+ nk_f16_t const *q, nk_f16_t const *k, nk_f16_t const *v_packed, nk_f16_t *output, nk_size_t query_len,
1225
+ nk_size_t kv_len, nk_size_t head_dim, nk_size_t head_dim_padded, nk_size_t dim_tile_count,
1226
+ nk_f32_t scale) NK_STREAMING_ {
1227
+
1228
+ svbool_t const predicate_all_b32x = svptrue_b32();
1229
+ svbool_t const predicate_all_b16x = svptrue_b16();
1230
+ nk_size_t const valid_query_count = (query_len < 16) ? query_len : 16;
1231
+
1232
+ NK_ALIGN64 nk_f32_t row_max[16];
1233
+ NK_ALIGN64 nk_f32_t row_sum[16];
1234
+ NK_ALIGN64 nk_f32_t output_accumulator[16 * 256];
1235
+
1236
+ svst1_f32(predicate_all_b32x, row_max, svdup_f32(NK_F32_MIN));
1237
+ svst1_f32(predicate_all_b32x, row_sum, svdup_f32(0.0f));
1238
+ svfloat32_t zero_f32x = svdup_f32(0.0f);
1239
+ for (nk_size_t i = 0; i < 16 * head_dim_padded; i += svcntw()) {
1240
+ svst1_f32(predicate_all_b32x, output_accumulator + i, zero_f32x);
1241
+ }
1242
+
1243
+ nk_size_t kv_block_index = 0;
1244
+ nk_size_t kv_start = 0;
1245
+ svbool_t const batch_predicate_b32x = svwhilelt_b32(0u, 16u);
1246
+
1247
+ nk_size_t const k_depth_step_count = head_dim_padded / 2;
1248
+
1249
+ // Pre-transpose Q once: queries_transposed[step][16 f32 words]
1250
+ // queries_transposed[step] reinterpret-as-f16 = {Q[0][2s], Q[0][2s+1], Q[1][2s], Q[1][2s+1], ...}
1251
+ // This is the same interleaving ZA0 vertical reads would produce.
1252
+ NK_ALIGN64 nk_f32_t queries_transposed[128 * 16]; // max head_dim_padded/2 * 16 = 128 * 16
1253
+ for (nk_size_t batch = 0; batch < head_dim_padded / 32; batch++) {
1254
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
1255
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++)
1256
+ svld1_hor_za32(0, query_index, batch_predicate_b32x,
1257
+ (nk_f32_t const *)(q + query_index * head_dim + batch * 32));
1258
+ for (nk_size_t step = 0; step < 16; step++)
1259
+ svst1_f32(predicate_all_b32x, queries_transposed + (batch * 16 + step) * 16,
1260
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, step));
1261
+ }
1262
+
1263
+ // === Bc=32 main loop (prefill only, skipped for decode) ===
1264
+ if (valid_query_count > 1) {
1265
+ for (; kv_start + 32 <= kv_len; kv_start += 32, kv_block_index += 2) {
1266
+ // Q×K^T: pure memory→FMOPA, no ZA staging for Q or K
1267
+ svzero_mask_za(nk_sme_zero_za32_tile_2_);
1268
+ svzero_mask_za(nk_sme_zero_za32_tile_3_);
1269
+ nk_f16_t const *keys_block_low = k + kv_block_index * k_depth_step_count * 32;
1270
+ nk_f16_t const *keys_block_high = k + (kv_block_index + 1) * k_depth_step_count * 32;
1271
+ for (nk_size_t step = 0; step < k_depth_step_count; step++) {
1272
+ svfloat16_t zn_f16x = svreinterpret_f16_f32(
1273
+ svld1_f32(predicate_all_b32x, queries_transposed + step * 16));
1274
+ svfloat16_t zm0_f16x = svld1_f16(predicate_all_b16x, (float16_t const *)(keys_block_low + step * 32));
1275
+ svfloat16_t zm1_f16x = svld1_f16(predicate_all_b16x, (float16_t const *)(keys_block_high + step * 32));
1276
+ svmopa_za32_f16_m(2, predicate_all_b32x, predicate_all_b32x, zn_f16x, zm0_f16x);
1277
+ svmopa_za32_f16_m(3, predicate_all_b32x, predicate_all_b32x, zn_f16x, zm1_f16x);
1278
+ }
1279
+ // ZA2 = scores[query_index][0:15], ZA3 = scores[query_index][16:31]
1280
+
1281
+ // Pass 1: Column-wise max (read ZA2/ZA3 columns vertically)
1282
+ svfloat32_t scale_f32x = svdup_f32(scale);
1283
+ svfloat32_t block_max_f32x = svdup_f32(NK_F32_MIN);
1284
+ for (nk_size_t column_index = 0; column_index < 16; column_index++) {
1285
+ svfloat32_t score_column_f32x = svmul_f32_x(
1286
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, column_index),
1287
+ scale_f32x);
1288
+ block_max_f32x = svmax_f32_x(predicate_all_b32x, block_max_f32x, score_column_f32x);
1289
+ }
1290
+ for (nk_size_t column_index = 0; column_index < 16; column_index++) {
1291
+ svfloat32_t score_column_f32x = svmul_f32_x(
1292
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 3, column_index),
1293
+ scale_f32x);
1294
+ block_max_f32x = svmax_f32_x(predicate_all_b32x, block_max_f32x, score_column_f32x);
1295
+ }
1296
+
1297
+ // Softmax correction (vectorized via array load/store)
1298
+ svfloat32_t old_max_f32x = svld1_f32(predicate_all_b32x, row_max);
1299
+ svfloat32_t new_max_f32x = svmax_f32_x(predicate_all_b32x, old_max_f32x, block_max_f32x);
1300
+ svfloat32_t correction_f32x = nk_exp_fast_f32_sve_(
1301
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, old_max_f32x, new_max_f32x));
1302
+ svbool_t max_changed_b32x = svcmplt_f32(predicate_all_b32x, correction_f32x, svdup_f32(1.0f));
1303
+ nk_u32_t max_was_updated = svptest_any(predicate_all_b32x, max_changed_b32x) ? 1 : 0;
1304
+ svfloat32_t row_sum_corrected_f32x = svld1_f32(predicate_all_b32x, row_sum);
1305
+ if (max_was_updated)
1306
+ row_sum_corrected_f32x = svmul_f32_x(predicate_all_b32x, row_sum_corrected_f32x, correction_f32x);
1307
+ NK_ALIGN64 nk_f32_t corrections[16];
1308
+ svst1_f32(predicate_all_b32x, corrections, correction_f32x);
1309
+
1310
+ // Pass 2: Column-wise exp + fused P write + sum
1311
+ svfloat32_t sum_delta_f32x = svdup_f32(0.0f);
1312
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
1313
+ // ZA2 columns in pairs -> ZA0 columns 0-7
1314
+ for (nk_size_t column_index = 0; column_index < 16; column_index += 2) {
1315
+ svfloat32_t score_even_f32x = svmul_f32_x(
1316
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, column_index),
1317
+ scale_f32x);
1318
+ svfloat32_t score_odd_f32x = svmul_f32_x(
1319
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, column_index + 1),
1320
+ scale_f32x);
1321
+ svfloat32_t weight_even_f32x = nk_exp_fast_f32_sve_(
1322
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, score_even_f32x, new_max_f32x));
1323
+ svfloat32_t weight_odd_f32x = nk_exp_fast_f32_sve_(
1324
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, score_odd_f32x, new_max_f32x));
1325
+ sum_delta_f32x = svadd_f32_x(predicate_all_b32x, sum_delta_f32x, weight_even_f32x);
1326
+ sum_delta_f32x = svadd_f32_x(predicate_all_b32x, sum_delta_f32x, weight_odd_f32x);
1327
+ svfloat16_t weight_pair_f16x = svzip1_f16(svcvt_f16_f32_x(predicate_all_b32x, weight_even_f32x),
1328
+ svcvt_f16_f32_x(predicate_all_b32x, weight_odd_f32x));
1329
+ svwrite_ver_za32_f32_m(0, column_index / 2, predicate_all_b32x,
1330
+ svreinterpret_f32_f16(weight_pair_f16x));
1331
+ }
1332
+ // ZA3 columns in pairs -> ZA0 columns 8-15
1333
+ for (nk_size_t column_index = 0; column_index < 16; column_index += 2) {
1334
+ svfloat32_t score_even_f32x = svmul_f32_x(
1335
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 3, column_index),
1336
+ scale_f32x);
1337
+ svfloat32_t score_odd_f32x = svmul_f32_x(
1338
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 3, column_index + 1),
1339
+ scale_f32x);
1340
+ svfloat32_t weight_even_f32x = nk_exp_fast_f32_sve_(
1341
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, score_even_f32x, new_max_f32x));
1342
+ svfloat32_t weight_odd_f32x = nk_exp_fast_f32_sve_(
1343
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, score_odd_f32x, new_max_f32x));
1344
+ sum_delta_f32x = svadd_f32_x(predicate_all_b32x, sum_delta_f32x, weight_even_f32x);
1345
+ sum_delta_f32x = svadd_f32_x(predicate_all_b32x, sum_delta_f32x, weight_odd_f32x);
1346
+ svfloat16_t weight_pair_f16x = svzip1_f16(svcvt_f16_f32_x(predicate_all_b32x, weight_even_f32x),
1347
+ svcvt_f16_f32_x(predicate_all_b32x, weight_odd_f32x));
1348
+ svwrite_ver_za32_f32_m(0, 8 + column_index / 2, predicate_all_b32x,
1349
+ svreinterpret_f32_f16(weight_pair_f16x));
1350
+ }
1351
+ row_sum_corrected_f32x = svadd_f32_x(predicate_all_b32x, row_sum_corrected_f32x, sum_delta_f32x);
1352
+ svst1_f32(predicate_all_b32x, row_sum, row_sum_corrected_f32x);
1353
+ svst1_f32(predicate_all_b32x, row_max, new_max_f32x);
1354
+
1355
+ // Extract P columns from ZA0
1356
+ svfloat16_t probability_column_0_f32x = svreinterpret_f16_f32(
1357
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 0));
1358
+ svfloat16_t probability_column_1_f32x = svreinterpret_f16_f32(
1359
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 1));
1360
+ svfloat16_t probability_column_2_f32x = svreinterpret_f16_f32(
1361
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 2));
1362
+ svfloat16_t probability_column_3_f32x = svreinterpret_f16_f32(
1363
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 3));
1364
+ svfloat16_t probability_column_4_f32x = svreinterpret_f16_f32(
1365
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 4));
1366
+ svfloat16_t probability_column_5_f32x = svreinterpret_f16_f32(
1367
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 5));
1368
+ svfloat16_t probability_column_6_f32x = svreinterpret_f16_f32(
1369
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 6));
1370
+ svfloat16_t probability_column_7_f32x = svreinterpret_f16_f32(
1371
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 7));
1372
+ svfloat16_t probability_column_8_f32x = svreinterpret_f16_f32(
1373
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 8));
1374
+ svfloat16_t probability_column_9_f32x = svreinterpret_f16_f32(
1375
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 9));
1376
+ svfloat16_t probability_column_10_f32x = svreinterpret_f16_f32(
1377
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 10));
1378
+ svfloat16_t probability_column_11_f32x = svreinterpret_f16_f32(
1379
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 11));
1380
+ svfloat16_t probability_column_12_f32x = svreinterpret_f16_f32(
1381
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 12));
1382
+ svfloat16_t probability_column_13_f32x = svreinterpret_f16_f32(
1383
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 13));
1384
+ svfloat16_t probability_column_14_f32x = svreinterpret_f16_f32(
1385
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 14));
1386
+ svfloat16_t probability_column_15_f32x = svreinterpret_f16_f32(
1387
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 15));
1388
+
1389
+ // Pre-apply correction once before P×V
1390
+ svbool_t query_predicate_b16x = svwhilelt_b16_u64(0u, valid_query_count * 2);
1391
+ nk_f16_t const *values_block_low = v_packed + kv_block_index * dim_tile_count * 8 * 32;
1392
+ nk_f16_t const *values_block_high = v_packed + (kv_block_index + 1) * dim_tile_count * 8 * 32;
1393
+
1394
+ if (max_was_updated) {
1395
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++) {
1396
+ svfloat32_t correction_scalar_f32x = svdup_f32(corrections[query_index]);
1397
+ for (nk_size_t dim_offset = 0; dim_offset < head_dim_padded; dim_offset += 16)
1398
+ svst1_f32(
1399
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + dim_offset,
1400
+ svmul_f32_x(predicate_all_b32x,
1401
+ svld1_f32(predicate_all_b32x,
1402
+ output_accumulator + query_index * head_dim_padded + dim_offset),
1403
+ correction_scalar_f32x));
1404
+ }
1405
+ }
1406
+
1407
+ // P×V: zero -> FMOPA -> read -> add (no ZA writes for output_accumulator)
1408
+ nk_size_t dim_tile = 0;
1409
+ for (; dim_tile + 4 <= dim_tile_count; dim_tile += 4) {
1410
+ svzero_za();
1411
+ // Block0: 8 depth steps (KV positions 0-15)
1412
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1413
+ svld1_f16(predicate_all_b16x,
1414
+ (float16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 0) * 32)));
1415
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1416
+ svld1_f16(predicate_all_b16x,
1417
+ (float16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 0) * 32)));
1418
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1419
+ svld1_f16(predicate_all_b16x,
1420
+ (float16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 0) * 32)));
1421
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1422
+ svld1_f16(predicate_all_b16x,
1423
+ (float16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 0) * 32)));
1424
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1425
+ svld1_f16(predicate_all_b16x,
1426
+ (float16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 1) * 32)));
1427
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1428
+ svld1_f16(predicate_all_b16x,
1429
+ (float16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 1) * 32)));
1430
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1431
+ svld1_f16(predicate_all_b16x,
1432
+ (float16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 1) * 32)));
1433
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1434
+ svld1_f16(predicate_all_b16x,
1435
+ (float16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 1) * 32)));
1436
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1437
+ svld1_f16(predicate_all_b16x,
1438
+ (float16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 2) * 32)));
1439
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1440
+ svld1_f16(predicate_all_b16x,
1441
+ (float16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 2) * 32)));
1442
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1443
+ svld1_f16(predicate_all_b16x,
1444
+ (float16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 2) * 32)));
1445
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1446
+ svld1_f16(predicate_all_b16x,
1447
+ (float16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 2) * 32)));
1448
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1449
+ svld1_f16(predicate_all_b16x,
1450
+ (float16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 3) * 32)));
1451
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1452
+ svld1_f16(predicate_all_b16x,
1453
+ (float16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 3) * 32)));
1454
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1455
+ svld1_f16(predicate_all_b16x,
1456
+ (float16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 3) * 32)));
1457
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1458
+ svld1_f16(predicate_all_b16x,
1459
+ (float16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 3) * 32)));
1460
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1461
+ svld1_f16(predicate_all_b16x,
1462
+ (float16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 4) * 32)));
1463
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1464
+ svld1_f16(predicate_all_b16x,
1465
+ (float16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 4) * 32)));
1466
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1467
+ svld1_f16(predicate_all_b16x,
1468
+ (float16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 4) * 32)));
1469
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1470
+ svld1_f16(predicate_all_b16x,
1471
+ (float16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 4) * 32)));
1472
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1473
+ svld1_f16(predicate_all_b16x,
1474
+ (float16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 5) * 32)));
1475
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1476
+ svld1_f16(predicate_all_b16x,
1477
+ (float16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 5) * 32)));
1478
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1479
+ svld1_f16(predicate_all_b16x,
1480
+ (float16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 5) * 32)));
1481
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1482
+ svld1_f16(predicate_all_b16x,
1483
+ (float16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 5) * 32)));
1484
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1485
+ svld1_f16(predicate_all_b16x,
1486
+ (float16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 6) * 32)));
1487
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1488
+ svld1_f16(predicate_all_b16x,
1489
+ (float16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 6) * 32)));
1490
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1491
+ svld1_f16(predicate_all_b16x,
1492
+ (float16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 6) * 32)));
1493
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1494
+ svld1_f16(predicate_all_b16x,
1495
+ (float16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 6) * 32)));
1496
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1497
+ svld1_f16(predicate_all_b16x,
1498
+ (float16_t const *)(values_block_low + ((dim_tile + 0) * 8 + 7) * 32)));
1499
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1500
+ svld1_f16(predicate_all_b16x,
1501
+ (float16_t const *)(values_block_low + ((dim_tile + 1) * 8 + 7) * 32)));
1502
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1503
+ svld1_f16(predicate_all_b16x,
1504
+ (float16_t const *)(values_block_low + ((dim_tile + 2) * 8 + 7) * 32)));
1505
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1506
+ svld1_f16(predicate_all_b16x,
1507
+ (float16_t const *)(values_block_low + ((dim_tile + 3) * 8 + 7) * 32)));
1508
+ // Block1: 8 depth steps (KV positions 16-31)
1509
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_8_f32x,
1510
+ svld1_f16(predicate_all_b16x,
1511
+ (float16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 0) * 32)));
1512
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_8_f32x,
1513
+ svld1_f16(predicate_all_b16x,
1514
+ (float16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 0) * 32)));
1515
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_8_f32x,
1516
+ svld1_f16(predicate_all_b16x,
1517
+ (float16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 0) * 32)));
1518
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_8_f32x,
1519
+ svld1_f16(predicate_all_b16x,
1520
+ (float16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 0) * 32)));
1521
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_9_f32x,
1522
+ svld1_f16(predicate_all_b16x,
1523
+ (float16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 1) * 32)));
1524
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_9_f32x,
1525
+ svld1_f16(predicate_all_b16x,
1526
+ (float16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 1) * 32)));
1527
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_9_f32x,
1528
+ svld1_f16(predicate_all_b16x,
1529
+ (float16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 1) * 32)));
1530
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_9_f32x,
1531
+ svld1_f16(predicate_all_b16x,
1532
+ (float16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 1) * 32)));
1533
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_10_f32x,
1534
+ svld1_f16(predicate_all_b16x,
1535
+ (float16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 2) * 32)));
1536
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_10_f32x,
1537
+ svld1_f16(predicate_all_b16x,
1538
+ (float16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 2) * 32)));
1539
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_10_f32x,
1540
+ svld1_f16(predicate_all_b16x,
1541
+ (float16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 2) * 32)));
1542
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_10_f32x,
1543
+ svld1_f16(predicate_all_b16x,
1544
+ (float16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 2) * 32)));
1545
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_11_f32x,
1546
+ svld1_f16(predicate_all_b16x,
1547
+ (float16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 3) * 32)));
1548
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_11_f32x,
1549
+ svld1_f16(predicate_all_b16x,
1550
+ (float16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 3) * 32)));
1551
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_11_f32x,
1552
+ svld1_f16(predicate_all_b16x,
1553
+ (float16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 3) * 32)));
1554
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_11_f32x,
1555
+ svld1_f16(predicate_all_b16x,
1556
+ (float16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 3) * 32)));
1557
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_12_f32x,
1558
+ svld1_f16(predicate_all_b16x,
1559
+ (float16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 4) * 32)));
1560
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_12_f32x,
1561
+ svld1_f16(predicate_all_b16x,
1562
+ (float16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 4) * 32)));
1563
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_12_f32x,
1564
+ svld1_f16(predicate_all_b16x,
1565
+ (float16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 4) * 32)));
1566
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_12_f32x,
1567
+ svld1_f16(predicate_all_b16x,
1568
+ (float16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 4) * 32)));
1569
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_13_f32x,
1570
+ svld1_f16(predicate_all_b16x,
1571
+ (float16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 5) * 32)));
1572
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_13_f32x,
1573
+ svld1_f16(predicate_all_b16x,
1574
+ (float16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 5) * 32)));
1575
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_13_f32x,
1576
+ svld1_f16(predicate_all_b16x,
1577
+ (float16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 5) * 32)));
1578
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_13_f32x,
1579
+ svld1_f16(predicate_all_b16x,
1580
+ (float16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 5) * 32)));
1581
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_14_f32x,
1582
+ svld1_f16(predicate_all_b16x,
1583
+ (float16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 6) * 32)));
1584
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_14_f32x,
1585
+ svld1_f16(predicate_all_b16x,
1586
+ (float16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 6) * 32)));
1587
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_14_f32x,
1588
+ svld1_f16(predicate_all_b16x,
1589
+ (float16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 6) * 32)));
1590
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_14_f32x,
1591
+ svld1_f16(predicate_all_b16x,
1592
+ (float16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 6) * 32)));
1593
+ svmopa_za32_f16_m(0, query_predicate_b16x, predicate_all_b16x, probability_column_15_f32x,
1594
+ svld1_f16(predicate_all_b16x,
1595
+ (float16_t const *)(values_block_high + ((dim_tile + 0) * 8 + 7) * 32)));
1596
+ svmopa_za32_f16_m(1, query_predicate_b16x, predicate_all_b16x, probability_column_15_f32x,
1597
+ svld1_f16(predicate_all_b16x,
1598
+ (float16_t const *)(values_block_high + ((dim_tile + 1) * 8 + 7) * 32)));
1599
+ svmopa_za32_f16_m(2, query_predicate_b16x, predicate_all_b16x, probability_column_15_f32x,
1600
+ svld1_f16(predicate_all_b16x,
1601
+ (float16_t const *)(values_block_high + ((dim_tile + 2) * 8 + 7) * 32)));
1602
+ svmopa_za32_f16_m(3, query_predicate_b16x, predicate_all_b16x, probability_column_15_f32x,
1603
+ svld1_f16(predicate_all_b16x,
1604
+ (float16_t const *)(values_block_high + ((dim_tile + 3) * 8 + 7) * 32)));
1605
+ // Read FMOPA result and ADD to output_accumulator
1606
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++) {
1607
+ svst1_f32(
1608
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 0) * 16,
1609
+ svadd_f32_x(predicate_all_b32x,
1610
+ svld1_f32(predicate_all_b32x,
1611
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 0) * 16),
1612
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, query_index)));
1613
+ svst1_f32(
1614
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 1) * 16,
1615
+ svadd_f32_x(predicate_all_b32x,
1616
+ svld1_f32(predicate_all_b32x,
1617
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 1) * 16),
1618
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 1, query_index)));
1619
+ svst1_f32(
1620
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 2) * 16,
1621
+ svadd_f32_x(predicate_all_b32x,
1622
+ svld1_f32(predicate_all_b32x,
1623
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 2) * 16),
1624
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, query_index)));
1625
+ svst1_f32(
1626
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 3) * 16,
1627
+ svadd_f32_x(predicate_all_b32x,
1628
+ svld1_f32(predicate_all_b32x,
1629
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 3) * 16),
1630
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 3, query_index)));
1631
+ }
1632
+ }
1633
+ // Remainder: 1 dim_tile at a time using ZA0
1634
+ for (; dim_tile < dim_tile_count; dim_tile++) {
1635
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
1636
+ svmopa_za32_f16_m(
1637
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1638
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_low + (dim_tile * 8 + 0) * 32)));
1639
+ svmopa_za32_f16_m(
1640
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1641
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_low + (dim_tile * 8 + 1) * 32)));
1642
+ svmopa_za32_f16_m(
1643
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1644
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_low + (dim_tile * 8 + 2) * 32)));
1645
+ svmopa_za32_f16_m(
1646
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1647
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_low + (dim_tile * 8 + 3) * 32)));
1648
+ svmopa_za32_f16_m(
1649
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1650
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_low + (dim_tile * 8 + 4) * 32)));
1651
+ svmopa_za32_f16_m(
1652
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1653
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_low + (dim_tile * 8 + 5) * 32)));
1654
+ svmopa_za32_f16_m(
1655
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1656
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_low + (dim_tile * 8 + 6) * 32)));
1657
+ svmopa_za32_f16_m(
1658
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1659
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_low + (dim_tile * 8 + 7) * 32)));
1660
+ svmopa_za32_f16_m(
1661
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_8_f32x,
1662
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_high + (dim_tile * 8 + 0) * 32)));
1663
+ svmopa_za32_f16_m(
1664
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_9_f32x,
1665
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_high + (dim_tile * 8 + 1) * 32)));
1666
+ svmopa_za32_f16_m(
1667
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_10_f32x,
1668
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_high + (dim_tile * 8 + 2) * 32)));
1669
+ svmopa_za32_f16_m(
1670
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_11_f32x,
1671
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_high + (dim_tile * 8 + 3) * 32)));
1672
+ svmopa_za32_f16_m(
1673
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_12_f32x,
1674
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_high + (dim_tile * 8 + 4) * 32)));
1675
+ svmopa_za32_f16_m(
1676
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_13_f32x,
1677
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_high + (dim_tile * 8 + 5) * 32)));
1678
+ svmopa_za32_f16_m(
1679
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_14_f32x,
1680
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_high + (dim_tile * 8 + 6) * 32)));
1681
+ svmopa_za32_f16_m(
1682
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_15_f32x,
1683
+ svld1_f16(predicate_all_b16x, (float16_t const *)(values_block_high + (dim_tile * 8 + 7) * 32)));
1684
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++)
1685
+ svst1_f32(predicate_all_b32x, output_accumulator + query_index * head_dim_padded + dim_tile * 16,
1686
+ svadd_f32_x(predicate_all_b32x,
1687
+ svld1_f32(predicate_all_b32x,
1688
+ output_accumulator + query_index * head_dim_padded + dim_tile * 16),
1689
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, query_index)));
1690
+ }
1691
+ }
1692
+ }
1693
+
1694
+ // === Bc=16 tail loop (handles remaining KV positions and decode path) ===
1695
+ for (; kv_start < kv_len; kv_start += 16, kv_block_index++) {
1696
+ nk_size_t const valid_kv = ((kv_start + 16) <= kv_len) ? 16 : (kv_len - kv_start);
1697
+
1698
+ // Q×K^T: pure memory→FMOPA, no ZA staging
1699
+ svzero_mask_za(nk_sme_zero_za32_tile_2_);
1700
+ nk_f16_t const *k_block = k + kv_block_index * k_depth_step_count * 32;
1701
+ for (nk_size_t step = 0; step < k_depth_step_count; step++) {
1702
+ svfloat16_t zn_f16x = svreinterpret_f16_f32(svld1_f32(predicate_all_b32x, queries_transposed + step * 16));
1703
+ svfloat16_t zm_f16x = svld1_f16(predicate_all_b16x, (float16_t const *)(k_block + step * 32));
1704
+ svmopa_za32_f16_m(2, predicate_all_b32x, predicate_all_b32x, zn_f16x, zm_f16x);
1705
+ }
1706
+
1707
+ // Pass 1: Column-wise max (read ZA2 columns vertically)
1708
+ svfloat32_t scale_16_f32x = svdup_f32(scale);
1709
+ svfloat32_t block_max_16_f32x = svdup_f32(NK_F32_MIN);
1710
+ for (nk_size_t column_index = 0; column_index < 16; column_index++) {
1711
+ svfloat32_t score_column_f32x = svmul_f32_x(
1712
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, column_index),
1713
+ scale_16_f32x);
1714
+ block_max_16_f32x = svmax_f32_x(predicate_all_b32x, block_max_16_f32x, score_column_f32x);
1715
+ }
1716
+
1717
+ svfloat32_t old_max_f32x = svld1_f32(predicate_all_b32x, row_max);
1718
+ svfloat32_t new_max_f32x = svmax_f32_x(predicate_all_b32x, old_max_f32x, block_max_16_f32x);
1719
+ svfloat32_t correction_f32x = nk_exp_fast_f32_sve_(predicate_all_b32x,
1720
+ svsub_f32_x(predicate_all_b32x, old_max_f32x, new_max_f32x));
1721
+ svbool_t max_changed_16_b32x = svcmplt_f32(predicate_all_b32x, correction_f32x, svdup_f32(1.0f));
1722
+ nk_u32_t max_was_updated_16 = svptest_any(predicate_all_b32x, max_changed_16_b32x) ? 1 : 0;
1723
+ svfloat32_t row_sum_corrected_f32x = svld1_f32(predicate_all_b32x, row_sum);
1724
+ if (max_was_updated_16)
1725
+ row_sum_corrected_f32x = svmul_f32_x(predicate_all_b32x, row_sum_corrected_f32x, correction_f32x);
1726
+ NK_ALIGN64 nk_f32_t corrections[16];
1727
+ svst1_f32(predicate_all_b32x, corrections, correction_f32x);
1728
+
1729
+ // Pass 2: Column-wise exp + fused P write + sum (ZA2 → ZA0 columns 0-7)
1730
+ svfloat32_t sum_delta_16_f32x = svdup_f32(0.0f);
1731
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
1732
+ for (nk_size_t column_index = 0; column_index < 16; column_index += 2) {
1733
+ svfloat32_t score_even_f32x = svmul_f32_x(
1734
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, column_index),
1735
+ scale_16_f32x);
1736
+ svfloat32_t score_odd_f32x = svmul_f32_x(
1737
+ predicate_all_b32x, svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, column_index + 1),
1738
+ scale_16_f32x);
1739
+ svfloat32_t weight_even_f32x = nk_exp_fast_f32_sve_(
1740
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, score_even_f32x, new_max_f32x));
1741
+ svfloat32_t weight_odd_f32x = nk_exp_fast_f32_sve_(
1742
+ predicate_all_b32x, svsub_f32_x(predicate_all_b32x, score_odd_f32x, new_max_f32x));
1743
+ sum_delta_16_f32x = svadd_f32_x(predicate_all_b32x, sum_delta_16_f32x, weight_even_f32x);
1744
+ sum_delta_16_f32x = svadd_f32_x(predicate_all_b32x, sum_delta_16_f32x, weight_odd_f32x);
1745
+ svfloat16_t weight_pair_f16x = svzip1_f16(svcvt_f16_f32_x(predicate_all_b32x, weight_even_f32x),
1746
+ svcvt_f16_f32_x(predicate_all_b32x, weight_odd_f32x));
1747
+ svwrite_ver_za32_f32_m(0, column_index / 2, predicate_all_b32x, svreinterpret_f32_f16(weight_pair_f16x));
1748
+ }
1749
+ row_sum_corrected_f32x = svadd_f32_x(predicate_all_b32x, row_sum_corrected_f32x, sum_delta_16_f32x);
1750
+ svst1_f32(predicate_all_b32x, row_sum, row_sum_corrected_f32x);
1751
+ svst1_f32(predicate_all_b32x, row_max, new_max_f32x);
1752
+
1753
+ if (valid_query_count == 1) {
1754
+ // Decode path: extract f32 weights from ZA0 row 0 using SVE
1755
+ svfloat16_t row0_f16x = svreinterpret_f16_f32(
1756
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 0));
1757
+ svfloat16_t weights_even_f16x = svuzp1_f16(row0_f16x, row0_f16x);
1758
+ svfloat16_t weights_odd_f16x = svuzp2_f16(row0_f16x, row0_f16x);
1759
+ NK_ALIGN64 nk_f32_t decode_weights[16];
1760
+ svst1_f32(svwhilelt_b32(0u, 8u), decode_weights, svcvt_f32_f16_x(svwhilelt_b32(0u, 8u), weights_even_f16x));
1761
+ svst1_f32(svwhilelt_b32(0u, 8u), decode_weights + 8,
1762
+ svcvt_f32_f16_x(svwhilelt_b32(0u, 8u), weights_odd_f16x));
1763
+ NK_ALIGN64 nk_f32_t decode_weights_ordered[16];
1764
+ for (nk_size_t i = 0; i < 8; i++) {
1765
+ decode_weights_ordered[2 * i] = decode_weights[i];
1766
+ decode_weights_ordered[2 * i + 1] = decode_weights[8 + i];
1767
+ }
1768
+ svfloat32_t corr_f32x = svdup_f32(corrections[0]);
1769
+ for (nk_size_t d = 0; d < head_dim; d += svcntw()) {
1770
+ svbool_t predicate_b32x = svwhilelt_b32_u64(d, head_dim);
1771
+ svfloat32_t acc_f32x = svmul_f32_x(predicate_b32x, svld1_f32(predicate_b32x, output_accumulator + d),
1772
+ corr_f32x);
1773
+ for (nk_size_t ki = 0; ki < valid_kv; ki++) {
1774
+ nk_size_t dim_tile = d / 16, depth_s = ki / 2, sub = ki % 2;
1775
+ nk_f16_t const *v_vec = v_packed +
1776
+ (kv_block_index * dim_tile_count * 8 + dim_tile * 8 + depth_s) * 32;
1777
+ svfloat16_t packed_f16x = svld1_f16(predicate_all_b16x, (float16_t const *)v_vec);
1778
+ svfloat16_t v_selected_f16x = (sub == 0) ? svuzp1_f16(packed_f16x, packed_f16x)
1779
+ : svuzp2_f16(packed_f16x, packed_f16x);
1780
+ acc_f32x = svmla_f32_x(predicate_b32x, acc_f32x, svdup_f32(decode_weights_ordered[ki]),
1781
+ svcvt_f32_f16_x(predicate_b32x, v_selected_f16x));
1782
+ }
1783
+ svst1_f32(predicate_b32x, output_accumulator + d, acc_f32x);
1784
+ }
1785
+ }
1786
+ else {
1787
+ // Prefill Bc=16: extract P columns, pre-apply correction, add-after P×V
1788
+ svbool_t query_predicate_b16x = svwhilelt_b16_u64(0u, valid_query_count * 2);
1789
+
1790
+ svfloat16_t probability_column_0_f32x = svreinterpret_f16_f32(
1791
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 0));
1792
+ svfloat16_t probability_column_1_f32x = svreinterpret_f16_f32(
1793
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 1));
1794
+ svfloat16_t probability_column_2_f32x = svreinterpret_f16_f32(
1795
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 2));
1796
+ svfloat16_t probability_column_3_f32x = svreinterpret_f16_f32(
1797
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 3));
1798
+ svfloat16_t probability_column_4_f32x = svreinterpret_f16_f32(
1799
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 4));
1800
+ svfloat16_t probability_column_5_f32x = svreinterpret_f16_f32(
1801
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 5));
1802
+ svfloat16_t probability_column_6_f32x = svreinterpret_f16_f32(
1803
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 6));
1804
+ svfloat16_t probability_column_7_f32x = svreinterpret_f16_f32(
1805
+ svread_ver_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, 7));
1806
+
1807
+ nk_f16_t const *v_block = v_packed + kv_block_index * dim_tile_count * 8 * 32;
1808
+
1809
+ if (max_was_updated_16) {
1810
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++) {
1811
+ svfloat32_t correction_scalar_f32x = svdup_f32(corrections[query_index]);
1812
+ for (nk_size_t dim_offset = 0; dim_offset < head_dim_padded; dim_offset += 16)
1813
+ svst1_f32(
1814
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + dim_offset,
1815
+ svmul_f32_x(predicate_all_b32x,
1816
+ svld1_f32(predicate_all_b32x,
1817
+ output_accumulator + query_index * head_dim_padded + dim_offset),
1818
+ correction_scalar_f32x));
1819
+ }
1820
+ }
1821
+
1822
+ nk_size_t dim_tile = 0;
1823
+ for (; dim_tile + 4 <= dim_tile_count; dim_tile += 4) {
1824
+ svzero_za();
1825
+ svmopa_za32_f16_m(
1826
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1827
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 0) * 8 + 0) * 32)));
1828
+ svmopa_za32_f16_m(
1829
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1830
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 1) * 8 + 0) * 32)));
1831
+ svmopa_za32_f16_m(
1832
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1833
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 2) * 8 + 0) * 32)));
1834
+ svmopa_za32_f16_m(
1835
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1836
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 3) * 8 + 0) * 32)));
1837
+ svmopa_za32_f16_m(
1838
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1839
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 0) * 8 + 1) * 32)));
1840
+ svmopa_za32_f16_m(
1841
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1842
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 1) * 8 + 1) * 32)));
1843
+ svmopa_za32_f16_m(
1844
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1845
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 2) * 8 + 1) * 32)));
1846
+ svmopa_za32_f16_m(
1847
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1848
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 3) * 8 + 1) * 32)));
1849
+ svmopa_za32_f16_m(
1850
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1851
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 0) * 8 + 2) * 32)));
1852
+ svmopa_za32_f16_m(
1853
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1854
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 1) * 8 + 2) * 32)));
1855
+ svmopa_za32_f16_m(
1856
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1857
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 2) * 8 + 2) * 32)));
1858
+ svmopa_za32_f16_m(
1859
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1860
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 3) * 8 + 2) * 32)));
1861
+ svmopa_za32_f16_m(
1862
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1863
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 0) * 8 + 3) * 32)));
1864
+ svmopa_za32_f16_m(
1865
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1866
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 1) * 8 + 3) * 32)));
1867
+ svmopa_za32_f16_m(
1868
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1869
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 2) * 8 + 3) * 32)));
1870
+ svmopa_za32_f16_m(
1871
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1872
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 3) * 8 + 3) * 32)));
1873
+ svmopa_za32_f16_m(
1874
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1875
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 0) * 8 + 4) * 32)));
1876
+ svmopa_za32_f16_m(
1877
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1878
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 1) * 8 + 4) * 32)));
1879
+ svmopa_za32_f16_m(
1880
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1881
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 2) * 8 + 4) * 32)));
1882
+ svmopa_za32_f16_m(
1883
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1884
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 3) * 8 + 4) * 32)));
1885
+ svmopa_za32_f16_m(
1886
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1887
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 0) * 8 + 5) * 32)));
1888
+ svmopa_za32_f16_m(
1889
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1890
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 1) * 8 + 5) * 32)));
1891
+ svmopa_za32_f16_m(
1892
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1893
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 2) * 8 + 5) * 32)));
1894
+ svmopa_za32_f16_m(
1895
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1896
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 3) * 8 + 5) * 32)));
1897
+ svmopa_za32_f16_m(
1898
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1899
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 0) * 8 + 6) * 32)));
1900
+ svmopa_za32_f16_m(
1901
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1902
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 1) * 8 + 6) * 32)));
1903
+ svmopa_za32_f16_m(
1904
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1905
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 2) * 8 + 6) * 32)));
1906
+ svmopa_za32_f16_m(
1907
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1908
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 3) * 8 + 6) * 32)));
1909
+ svmopa_za32_f16_m(
1910
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1911
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 0) * 8 + 7) * 32)));
1912
+ svmopa_za32_f16_m(
1913
+ 1, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1914
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 1) * 8 + 7) * 32)));
1915
+ svmopa_za32_f16_m(
1916
+ 2, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1917
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 2) * 8 + 7) * 32)));
1918
+ svmopa_za32_f16_m(
1919
+ 3, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1920
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + ((dim_tile + 3) * 8 + 7) * 32)));
1921
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++) {
1922
+ svst1_f32(
1923
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 0) * 16,
1924
+ svadd_f32_x(predicate_all_b32x,
1925
+ svld1_f32(predicate_all_b32x,
1926
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 0) * 16),
1927
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, query_index)));
1928
+ svst1_f32(
1929
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 1) * 16,
1930
+ svadd_f32_x(predicate_all_b32x,
1931
+ svld1_f32(predicate_all_b32x,
1932
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 1) * 16),
1933
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 1, query_index)));
1934
+ svst1_f32(
1935
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 2) * 16,
1936
+ svadd_f32_x(predicate_all_b32x,
1937
+ svld1_f32(predicate_all_b32x,
1938
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 2) * 16),
1939
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 2, query_index)));
1940
+ svst1_f32(
1941
+ predicate_all_b32x, output_accumulator + query_index * head_dim_padded + (dim_tile + 3) * 16,
1942
+ svadd_f32_x(predicate_all_b32x,
1943
+ svld1_f32(predicate_all_b32x,
1944
+ output_accumulator + query_index * head_dim_padded + (dim_tile + 3) * 16),
1945
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 3, query_index)));
1946
+ }
1947
+ }
1948
+ for (; dim_tile < dim_tile_count; dim_tile++) {
1949
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
1950
+ svmopa_za32_f16_m(
1951
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_0_f32x,
1952
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + (dim_tile * 8 + 0) * 32)));
1953
+ svmopa_za32_f16_m(
1954
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_1_f32x,
1955
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + (dim_tile * 8 + 1) * 32)));
1956
+ svmopa_za32_f16_m(
1957
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_2_f32x,
1958
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + (dim_tile * 8 + 2) * 32)));
1959
+ svmopa_za32_f16_m(
1960
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_3_f32x,
1961
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + (dim_tile * 8 + 3) * 32)));
1962
+ svmopa_za32_f16_m(
1963
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_4_f32x,
1964
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + (dim_tile * 8 + 4) * 32)));
1965
+ svmopa_za32_f16_m(
1966
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_5_f32x,
1967
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + (dim_tile * 8 + 5) * 32)));
1968
+ svmopa_za32_f16_m(
1969
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_6_f32x,
1970
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + (dim_tile * 8 + 6) * 32)));
1971
+ svmopa_za32_f16_m(
1972
+ 0, query_predicate_b16x, predicate_all_b16x, probability_column_7_f32x,
1973
+ svld1_f16(predicate_all_b16x, (float16_t const *)(v_block + (dim_tile * 8 + 7) * 32)));
1974
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++)
1975
+ svst1_f32(predicate_all_b32x, output_accumulator + query_index * head_dim_padded + dim_tile * 16,
1976
+ svadd_f32_x(predicate_all_b32x,
1977
+ svld1_f32(predicate_all_b32x,
1978
+ output_accumulator + query_index * head_dim_padded + dim_tile * 16),
1979
+ svread_hor_za32_f32_m(svdup_f32(0), predicate_all_b32x, 0, query_index)));
1980
+ }
1981
+ }
1982
+ }
1983
+
1984
+ // Final normalization
1985
+ svfloat32_t final_sum_f32x = svld1_f32(predicate_all_b32x, row_sum);
1986
+ svfloat32_t ones_f32x = svdup_f32(1.0f);
1987
+ svfloat32_t zeros_f32x = svdup_f32(0.0f);
1988
+ svbool_t sum_positive_b32x = svcmpgt_f32(predicate_all_b32x, final_sum_f32x, zeros_f32x);
1989
+ svfloat32_t inv_sum_f32x = svsel_f32(sum_positive_b32x, svdiv_f32_x(predicate_all_b32x, ones_f32x, final_sum_f32x),
1990
+ zeros_f32x);
1991
+
1992
+ NK_ALIGN64 nk_f32_t inv_sums[16];
1993
+ svst1_f32(predicate_all_b32x, inv_sums, inv_sum_f32x);
1994
+
1995
+ for (nk_size_t query_index = 0; query_index < valid_query_count; query_index++) {
1996
+ svfloat32_t inv_sum_f32x = svdup_f32(inv_sums[query_index]);
1997
+ for (nk_size_t dim_offset = 0; dim_offset < head_dim; dim_offset += svcntw()) {
1998
+ svbool_t predicate_b32x = svwhilelt_b32_u64(dim_offset, head_dim);
1999
+ svfloat32_t output_f32x = svmul_f32_x(
2000
+ predicate_b32x,
2001
+ svld1_f32(predicate_b32x, output_accumulator + query_index * head_dim_padded + dim_offset),
2002
+ inv_sum_f32x);
2003
+ svfloat16_t output_f16x = svcvt_f16_f32_x(predicate_b32x, output_f32x);
2004
+ nk_size_t store_count = (head_dim - dim_offset) < (nk_size_t)svcntw() ? (head_dim - dim_offset)
2005
+ : (nk_size_t)svcntw();
2006
+ svbool_t predicate_b16x = svwhilelt_b16_u64(0u, store_count);
2007
+ svst1_f16(predicate_b16x, (float16_t *)(output + query_index * head_dim + dim_offset), output_f16x);
2008
+ }
2009
+ }
2010
+ }
2011
+
2012
+ NK_PUBLIC void nk_attention_f16_sme( //
2013
+ nk_f16_t const *q, void const *kv_packed, nk_f16_t *output, nk_size_t num_heads, nk_size_t num_kv_heads,
2014
+ nk_size_t query_len, nk_size_t kv_len, nk_size_t head_dim, nk_f32_t scale) {
2015
+
2016
+ nk_attention_sme_packed_header_t const *header = (nk_attention_sme_packed_header_t const *)kv_packed;
2017
+ nk_size_t head_dim_padded = header->head_dim_padded;
2018
+ nk_size_t dim_tile_count = header->reserved[0];
2019
+ nk_size_t kv_blocks = (kv_len + 15) / 16;
2020
+ // K and V both use interleaved format: kv_blocks * 16 * head_dim_padded elements per head
2021
+ nk_size_t kv_head_stride = kv_blocks * 16 * head_dim_padded;
2022
+
2023
+ nk_f16_t const *k_packed = (nk_f16_t const *)((char const *)kv_packed + header->k_offset);
2024
+ nk_f16_t const *v_packed = (nk_f16_t const *)((char const *)kv_packed + header->v_offset);
2025
+
2026
+ nk_size_t group_size = (num_kv_heads > 0) ? num_heads / num_kv_heads : 1;
2027
+
2028
+ nk_sme_start_streaming_();
2029
+ for (nk_size_t q_head = 0; q_head < num_heads; q_head++) {
2030
+ nk_size_t kv_head = q_head / group_size;
2031
+
2032
+ nk_f16_t const *q_ptr = q + q_head * query_len * head_dim;
2033
+ nk_f16_t const *k_ptr = k_packed + kv_head * kv_head_stride;
2034
+ nk_f16_t const *v_ptr = v_packed + kv_head * kv_head_stride;
2035
+ nk_f16_t *out_ptr = output + q_head * query_len * head_dim;
2036
+
2037
+ for (nk_size_t q_start = 0; q_start < query_len; q_start += 16) {
2038
+ nk_size_t q_block_len = (q_start + 16 < query_len) ? 16 : (query_len - q_start);
2039
+
2040
+ nk_attention_f16_sme_streaming_(q_ptr + q_start * head_dim, k_ptr, v_ptr, out_ptr + q_start * head_dim,
2041
+ q_block_len, kv_len, head_dim, head_dim_padded, dim_tile_count, scale);
2042
+ }
2043
+ }
2044
+ nk_sme_stop_streaming_();
2045
+ }
2046
+
2047
+ NK_PUBLIC void nk_attention_causal_bf16_sme(nk_bf16_t const *q, void const *kv_packed, nk_bf16_t *output,
2048
+ nk_size_t num_heads, nk_size_t num_kv_heads, nk_size_t query_len,
2049
+ nk_size_t kv_len, nk_size_t head_dim, nk_f32_t scale) {
2050
+ // TODO: Implement proper causal masking with block skipping
2051
+ // For now, delegate to full attention (correct for decode where query_len=1)
2052
+ nk_attention_bf16_sme(q, kv_packed, output, num_heads, num_kv_heads, query_len, kv_len, head_dim, scale);
2053
+ }
2054
+
2055
+ NK_PUBLIC void nk_attention_causal_f16_sme(nk_f16_t const *q, void const *kv_packed, nk_f16_t *output,
2056
+ nk_size_t num_heads, nk_size_t num_kv_heads, nk_size_t query_len,
2057
+ nk_size_t kv_len, nk_size_t head_dim, nk_f32_t scale) {
2058
+ // TODO: Implement proper causal masking with block skipping
2059
+ // For now, delegate to full attention (correct for decode where query_len=1)
2060
+ nk_attention_f16_sme(q, kv_packed, output, num_heads, num_kv_heads, query_len, kv_len, head_dim, scale);
2061
+ }
2062
+
2063
+ #if defined(__clang__)
2064
+ #pragma clang attribute pop
2065
+ #elif defined(__GNUC__)
2066
+ #pragma GCC pop_options
2067
+ #endif
2068
+
2069
+ #if defined(__cplusplus)
2070
+ } // extern "C"
2071
+ #endif
2072
+
2073
+ #endif // NK_TARGET_SME
2074
+ #endif // NK_TARGET_ARM64_
2075
+ #endif // NK_ATTENTION_SME_H