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,4017 @@
1
+ /**
2
+ * @brief SIMD-accelerated Batched Dot Products for Sapphire Rapids.
3
+ * @file include/numkong/dots/sapphireamx.h
4
+ * @author Ash Vardanian
5
+ * @date December 27, 2025
6
+ *
7
+ * @sa include/numkong/dots.h
8
+ *
9
+ * This file contains tiled matrix-multiplication kernels optimized for Intel AMX instructions,
10
+ * leveraging the new TMM registers on Intel Sapphire Rapids CPUs. Those are much larger than ZMM:
11
+ *
12
+ * - BF16 tiles: 16 rows × 32 elements = 512 BF16 values = 1KB per tile
13
+ * - INT8 tiles: 16 rows × 64 elements = 1024 INT8 values = 1KB per tile
14
+ *
15
+ * We typically use 4 registers for the 2 × 2 tile output for the matrix C accumulators, leaving
16
+ * 4 other registers for parts of A and B matrices:
17
+ *
18
+ * - TMM0, TMM1: A matrix tiles (row blocks i and i+16)
19
+ * - TMM2, TMM3: B matrix tiles (column blocks j and j+16)
20
+ * - TMM4-7: C accumulator tiles (2 × 2 output grid)
21
+ *
22
+ * In most synthetic benchmarks there seems to be no mahor difference between aggregating into 1 or 4
23
+ * output tiles, implying the CPU's ability to internally pipeline the accumulation; so using 2 × 2 for
24
+ * ouputs is more of memory-bandwidth saving measure.
25
+ *
26
+ * Lacking High Bandwidth Mememory, the performance in GEMM-like BLAS workloads is dominated by memory
27
+ * bandwidth. Latency hiding is also extremely hard, heavily affecting performance numbers. For reference,
28
+ * Intel MKL SGEMM for FP32 inputs yeilds arounf 250 GigaOPS per core on Intel Sapphire Rapids, leveraging
29
+ * AVX-512. At the same time, for AMX:
30
+ *
31
+ * - BF16 peak: ≈ 3 TeraOPS per core in theory, ≈ 500 GigaOPS per core in practice
32
+ * - INT8 peak: ≈ 6 TeraOPS per core in theory, ≈ 1000 GigaOPS per core in practice
33
+ *
34
+ * Several optimizations are used across file:
35
+ *
36
+ * - Pre-pack B matrix once for repeated inference (avoids runtime reordering)
37
+ * - Morton Z-curve tile ordering improves L2 cache hit rate by 5-25%
38
+ * - Use streaming stores for large C matrices to avoid cache pollution
39
+ *
40
+ * @section amx_instructions Intel AMX Instructions (Sapphire Rapids+)
41
+ *
42
+ * Tile configuration and data movement:
43
+ *
44
+ * Intrinsic Instruction Notes
45
+ * _tile_loadconfig LDTILECFG (mem64) Configure tile palette
46
+ * _tile_loadd TILELOADD (TMM, mem, stride) Load tile from memory
47
+ * _tile_stored TILESTORED (mem, TMM, stride) Store tile to memory
48
+ * _tile_zero TILEZERO (TMM) Zero a tile register
49
+ *
50
+ * BF16 matrix multiply (AMX-BF16):
51
+ *
52
+ * Intrinsic Instruction Operation
53
+ * _tile_dpbf16ps TDPBF16PS (TMM, TMM, TMM) C += A × B (bf16 → f32)
54
+ *
55
+ * INT8 matrix multiply (AMX-INT8):
56
+ *
57
+ * Intrinsic Instruction Operation
58
+ * _tile_dpbssd TDPBSSD (TMM, TMM, TMM) C += A × B (i8 × i8 → i32)
59
+ * _tile_dpbsud TDPBSUD (TMM, TMM, TMM) C += A × B (i8 × u8 → i32)
60
+ * _tile_dpbusd TDPBUSD (TMM, TMM, TMM) C += A × B (u8 × i8 → i32)
61
+ * _tile_dpbuud TDPBUUD (TMM, TMM, TMM) C += A × B (u8 × u8 → u32)
62
+ *
63
+ * AMX performance characteristics:
64
+ * - TDPBF16PS: 16 × 16 × 32 = 8192 BF16 MACs per instruction
65
+ * - TDPBSSD: 16 × 16 × 64 = 16384 INT8 MACs per instruction
66
+ * - Tile load latency is ~20-30 cycles; software pipelining essential
67
+ * - PDEP/PEXT used for Morton Z-curve encoding (BMI2): 2-3cy @ p1
68
+ */
69
+ #ifndef NK_DOTS_SAPPHIREAMX_H
70
+ #define NK_DOTS_SAPPHIREAMX_H
71
+
72
+ #if NK_TARGET_X8664_
73
+ #if NK_TARGET_SAPPHIREAMX
74
+
75
+ #include "numkong/cast/icelake.h" // For FP8 ↔ BF16 conversions
76
+ #include "numkong/dots/serial.h" // For nk_dots_reduce_sumsq_bf16_
77
+
78
+ #if defined(__cplusplus)
79
+ extern "C" {
80
+ #endif
81
+
82
+ #if defined(__clang__)
83
+ #pragma clang attribute push( \
84
+ __attribute__((target( \
85
+ "avx2,avx512f,avx512vl,avx512bw,avx512dq,avx512fp16,avx512vbmi,f16c,fma,bmi,bmi2,amx-tile,amx-bf16,amx-int8"))), \
86
+ apply_to = function)
87
+ #elif defined(__GNUC__)
88
+ #pragma GCC push_options
89
+ #pragma GCC target("avx2", "avx512f", "avx512vl", "avx512bw", "avx512dq", "avx512fp16", "avx512vbmi", "f16c", "fma", \
90
+ "bmi", "bmi2", "amx-tile", "amx-bf16", "amx-int8")
91
+ #endif
92
+
93
+ /* AMX-specific packed buffer header (64-byte aligned).
94
+ * Different from nk_dots_amx_packed_header_t as AMX uses tile-based layout.
95
+ */
96
+ typedef struct {
97
+ nk_u32_t full_column_tiles; // Number of full column tiles (16 rows each)
98
+ nk_u32_t full_depth_tiles; // Number of depth tiles (32 columns for BF16, 64 for I8)
99
+ nk_u32_t column_remainder_count; // Remaining rows after full tiles (0-15)
100
+ nk_u32_t column_edge_offset; // Byte offset to edge data region
101
+ nk_u32_t norms_byte_offset; // Byte offset to per-column norms (for angular/euclidean)
102
+ nk_u32_t reserved[11]; // Padding to 64 bytes
103
+ } nk_dots_amx_packed_header_t;
104
+
105
+ /* Composable tile structures for AMX operations.
106
+ * These enable reusable primitives and cross-correlation (A × Aᵀ) use cases.
107
+ */
108
+
109
+ /* BF16 A tile: 16 rows × 32 depth-elements, row-major layout.
110
+ * Loaded from source matrix, used as left operand in AMX multiply.
111
+ */
112
+ typedef struct {
113
+ NK_ALIGN64 nk_bf16_t data[16][32]; // 16 rows × 32 columns = 1KB
114
+ } nk_dots_bf16_a16x32_sapphireamx_t;
115
+
116
+ /* BF16 B tile: 32 depth × 16 columns, pair-interleaved for TDPBF16PS.
117
+ * Access pattern: data[depth/2][column][depth%2] for logical B[depth, column].
118
+ * Pre-packed from column-major or transposed source.
119
+ */
120
+ typedef struct {
121
+ NK_ALIGN64 nk_bf16_t data[16][16][2]; // 16 depth-groups × 16 columns × 2 = 1KB
122
+ } nk_dots_bf16_b32x16_sapphireamx_t;
123
+
124
+ /* BF16 output state: 16 × 16 F32 accumulator tile.
125
+ * Holds partial sums during depth-dimension accumulation.
126
+ */
127
+ typedef struct {
128
+ NK_ALIGN64 nk_f32_t data[16][16]; // 16 × 16 = 1KB
129
+ } nk_dots_bf16_state_sapphireamx_t;
130
+
131
+ /* INT8 A tile: 16 rows × 64 depth-elements, row-major layout.
132
+ */
133
+ typedef struct {
134
+ NK_ALIGN64 nk_i8_t data[16][64]; // 16 rows × 64 columns = 1KB
135
+ } nk_dots_i8_a16x64_sapphireamx_t;
136
+
137
+ /* INT8 B tile: 64 depth × 16 columns, quad-interleaved for TDPBSSD.
138
+ * Access pattern: data[depth/4][column][depth%4] for logical B[depth, column].
139
+ */
140
+ typedef struct {
141
+ NK_ALIGN64 nk_i8_t data[16][16][4]; // 16 depth-groups × 16 columns × 4 = 1KB
142
+ } nk_dots_i8_b64x16_sapphireamx_t;
143
+
144
+ /* INT8 output state: 16 × 16 I32 accumulator tile.
145
+ */
146
+ typedef struct {
147
+ NK_ALIGN64 nk_i32_t data[16][16]; // 16 × 16 = 1KB
148
+ } nk_dots_i8_state_sapphireamx_t;
149
+
150
+ /* BF16 2 × 2 output state: 32 × 32 F32 output (4 accumulator tiles).
151
+ * Used for GEMM's 2 × 2 output blocking pattern.
152
+ */
153
+ typedef struct {
154
+ nk_dots_bf16_state_sapphireamx_t c[2][2]; // 4KB total
155
+ } nk_dots_bf16_state2x2_sapphireamx_t;
156
+
157
+ /* INT8 2 × 2 output state: 32 × 32 I32 output (4 accumulator tiles).
158
+ */
159
+ typedef struct {
160
+ nk_dots_i8_state_sapphireamx_t c[2][2]; // 4KB total
161
+ } nk_dots_i8_state2x2_sapphireamx_t;
162
+
163
+ /* UINT8 A tile: 16 rows × 64 depth-elements, row-major layout.
164
+ * Same layout as I8, different interpretation of signed vs unsigned.
165
+ */
166
+ typedef struct {
167
+ NK_ALIGN64 nk_u8_t data[16][64]; // 16 rows × 64 columns = 1KB
168
+ } nk_dots_u8_a16x64_sapphireamx_t;
169
+
170
+ /* UINT8 B tile: 64 depth × 16 columns, quad-interleaved for TDPBUUD.
171
+ */
172
+ typedef struct {
173
+ NK_ALIGN64 nk_u8_t data[16][16][4]; // 16 depth-groups × 16 columns × 4 = 1KB
174
+ } nk_dots_u8_b64x16_sapphireamx_t;
175
+
176
+ /* UINT8 output state: 16 × 16 U32 accumulator tile.
177
+ */
178
+ typedef struct {
179
+ NK_ALIGN64 nk_u32_t data[16][16]; // 16 × 16 = 1KB
180
+ } nk_dots_u8_state_sapphireamx_t;
181
+
182
+ /* UINT8 2 × 2 output state: 32 × 32 U32 output (4 accumulator tiles).
183
+ */
184
+ typedef struct {
185
+ nk_dots_u8_state_sapphireamx_t c[2][2]; // 4KB total
186
+ } nk_dots_u8_state2x2_sapphireamx_t;
187
+
188
+ /* Morton Z-curve encoding for cache-friendly tile traversal */
189
+ NK_INTERNAL nk_u64_t nk_morton_encode_sapphireamx_(nk_u32_t tile_row, nk_u32_t tile_col) {
190
+ return _pdep_u64(tile_row, 0x5555555555555555ULL) | _pdep_u64(tile_col, 0xAAAAAAAAAAAAAAAAULL);
191
+ }
192
+
193
+ /* Configure AMX tile registers */
194
+ NK_INTERNAL void nk_amx_tile_configure_sapphireamx_(void) {
195
+ NK_ALIGN64 nk_u8_t tile_config[64] = {0};
196
+ tile_config[0] = 1; // palette 1 (standard tile configuration)
197
+
198
+ nk_u16_t *bytes_per_row = (nk_u16_t *)&tile_config[16];
199
+ nk_u8_t *rows_per_tile = &tile_config[48];
200
+
201
+ for (int tile_idx = 0; tile_idx < 8; tile_idx++) {
202
+ rows_per_tile[tile_idx] = 16; // 16 rows per tile
203
+ bytes_per_row[tile_idx] = 64; // 64 bytes per row (1KB total)
204
+ }
205
+ _tile_loadconfig(tile_config);
206
+ }
207
+
208
+ /** @brief Compiler memory barrier to ensure stores complete before AMX tile loads */
209
+ #if defined(_MSC_VER)
210
+ NK_INTERNAL void nk_compiler_barrier_sapphireamx_(void) { _ReadWriteBarrier(); }
211
+ #else
212
+ NK_INTERNAL void nk_compiler_barrier_sapphireamx_(void) { __asm__ volatile("" ::: "memory"); }
213
+ #endif
214
+
215
+ /* Initialize BF16 output state to zero */
216
+ NK_INTERNAL void nk_dots_bf16_init_sapphireamx_(nk_dots_bf16_state_sapphireamx_t *state) {
217
+ __m512 zero_f32x16 = _mm512_setzero_ps();
218
+ for (nk_size_t row_idx = 0; row_idx < 16; row_idx++) { _mm512_store_ps(state->data[row_idx], zero_f32x16); }
219
+ }
220
+
221
+ /* Load A tile from row-major source with masking for edge tiles */
222
+ NK_INTERNAL void nk_dots_bf16_load_a_sapphireamx_( //
223
+ nk_dots_bf16_a16x32_sapphireamx_t *a_tile, //
224
+ nk_bf16_t const *src, nk_size_t src_stride_elements, //
225
+ nk_size_t valid_rows, nk_size_t valid_cols) {
226
+
227
+ __mmask32 column_mask = (valid_cols >= 32) ? 0xFFFFFFFF : ((__mmask32)1 << valid_cols) - 1;
228
+ __m512i zero_i16x32 = _mm512_setzero_si512();
229
+
230
+ for (nk_size_t row_idx = 0; row_idx < 16; row_idx++) {
231
+ if (row_idx < valid_rows) {
232
+ __m512i row_i16x32 = _mm512_maskz_loadu_epi16(column_mask, src + row_idx * src_stride_elements);
233
+ _mm512_store_si512((__m512i *)a_tile->data[row_idx], row_i16x32);
234
+ }
235
+ else { _mm512_store_si512((__m512i *)a_tile->data[row_idx], zero_i16x32); }
236
+ }
237
+ nk_compiler_barrier_sapphireamx_();
238
+ }
239
+
240
+ /* Store state to output matrix with masking for edge tiles */
241
+ NK_INTERNAL void nk_dots_bf16_store_sapphireamx_( //
242
+ nk_dots_bf16_state_sapphireamx_t const *state, //
243
+ nk_f32_t *dst, nk_size_t dst_stride_elements, //
244
+ nk_size_t valid_rows, nk_size_t valid_cols) {
245
+
246
+ __mmask16 column_mask = (valid_cols >= 16) ? 0xFFFF : ((__mmask16)1 << valid_cols) - 1;
247
+
248
+ for (nk_size_t row_idx = 0; row_idx < valid_rows; row_idx++) {
249
+ __m512 row_f32x16 = _mm512_load_ps(state->data[row_idx]);
250
+ _mm512_mask_storeu_ps(dst + row_idx * dst_stride_elements, column_mask, row_f32x16);
251
+ }
252
+ }
253
+
254
+ /* Accumulate 3 A x B tile pairs into state using AMX TDPBF16PS */
255
+ NK_INTERNAL void nk_dots_bf16_update_sapphireamx_( //
256
+ nk_dots_bf16_state_sapphireamx_t *state, //
257
+ nk_dots_bf16_a16x32_sapphireamx_t const *a_tile_0, //
258
+ nk_dots_bf16_a16x32_sapphireamx_t const *a_tile_1, //
259
+ nk_dots_bf16_a16x32_sapphireamx_t const *a_tile_2, //
260
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_0, //
261
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_1, //
262
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_2) {
263
+
264
+ // Load all tiles into registers
265
+ _tile_loadd(0, state->data, 64); // C accumulator
266
+ _tile_loadd(1, a_tile_0->data, 64); // A0
267
+ _tile_loadd(2, a_tile_1->data, 64); // A1
268
+ _tile_loadd(3, a_tile_2->data, 64); // A2
269
+ _tile_loadd(4, b_tile_0->data, 64); // B0
270
+ _tile_loadd(5, b_tile_1->data, 64); // B1
271
+ _tile_loadd(6, b_tile_2->data, 64); // B2
272
+
273
+ // Accumulate: C += A0 × B0 + A1 × B1 + A2 × B2
274
+ _tile_dpbf16ps(0, 1, 4); // C += A0 × B0
275
+ _tile_dpbf16ps(0, 2, 5); // C += A1 × B1
276
+ _tile_dpbf16ps(0, 3, 6); // C += A2 × B2
277
+
278
+ // Store result
279
+ _tile_stored(0, state->data, 64);
280
+ }
281
+
282
+ /* Initialize INT8 output state to zero */
283
+ NK_INTERNAL void nk_dots_i8_init_sapphireamx_(nk_dots_i8_state_sapphireamx_t *state) {
284
+ __m512i zero_i32x16 = _mm512_setzero_si512();
285
+ for (nk_size_t row_idx = 0; row_idx < 16; row_idx++) {
286
+ _mm512_store_si512((__m512i *)state->data[row_idx], zero_i32x16);
287
+ }
288
+ }
289
+
290
+ /* Load A tile from row-major source with masking for edge tiles */
291
+ NK_INTERNAL void nk_dots_i8_load_a_sapphireamx_( //
292
+ nk_dots_i8_a16x64_sapphireamx_t *a_tile, //
293
+ nk_i8_t const *src, nk_size_t src_stride, //
294
+ nk_size_t valid_rows, nk_size_t valid_cols) {
295
+
296
+ __mmask64 column_mask = (valid_cols >= 64) ? 0xFFFFFFFFFFFFFFFFULL : ((__mmask64)1 << valid_cols) - 1;
297
+ __m512i zero_i8x64 = _mm512_setzero_si512();
298
+
299
+ for (nk_size_t row_idx = 0; row_idx < 16; row_idx++) {
300
+ if (row_idx < valid_rows) {
301
+ __m512i row_i8x64 = _mm512_maskz_loadu_epi8(column_mask, src + row_idx * src_stride);
302
+ _mm512_store_si512((__m512i *)a_tile->data[row_idx], row_i8x64);
303
+ }
304
+ else { _mm512_store_si512((__m512i *)a_tile->data[row_idx], zero_i8x64); }
305
+ }
306
+ nk_compiler_barrier_sapphireamx_();
307
+ }
308
+
309
+ /* Store state to output matrix with masking for edge tiles */
310
+ NK_INTERNAL void nk_dots_i8_store_sapphireamx_( //
311
+ nk_dots_i8_state_sapphireamx_t const *state, //
312
+ nk_i32_t *dst, nk_size_t dst_stride_elements, //
313
+ nk_size_t valid_rows, nk_size_t valid_cols) {
314
+
315
+ __mmask16 column_mask = (valid_cols >= 16) ? 0xFFFF : ((__mmask16)1 << valid_cols) - 1;
316
+
317
+ for (nk_size_t row_idx = 0; row_idx < valid_rows; row_idx++) {
318
+ __m512i row_i32x16 = _mm512_load_si512((__m512i const *)state->data[row_idx]);
319
+ _mm512_mask_storeu_epi32(dst + row_idx * dst_stride_elements, column_mask, row_i32x16);
320
+ }
321
+ }
322
+
323
+ /* Accumulate 3 A x B tile pairs into state using AMX TDPBSSD */
324
+ NK_INTERNAL void nk_dots_i8_update_sapphireamx_( //
325
+ nk_dots_i8_state_sapphireamx_t *state, //
326
+ nk_dots_i8_a16x64_sapphireamx_t const *a_tile_0, //
327
+ nk_dots_i8_a16x64_sapphireamx_t const *a_tile_1, //
328
+ nk_dots_i8_a16x64_sapphireamx_t const *a_tile_2, //
329
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile_0, //
330
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile_1, //
331
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile_2) {
332
+
333
+ // Load all tiles into registers
334
+ _tile_loadd(0, state->data, 64); // C accumulator
335
+ _tile_loadd(1, a_tile_0->data, 64); // A0
336
+ _tile_loadd(2, a_tile_1->data, 64); // A1
337
+ _tile_loadd(3, a_tile_2->data, 64); // A2
338
+ _tile_loadd(4, b_tile_0->data, 64); // B0
339
+ _tile_loadd(5, b_tile_1->data, 64); // B1
340
+ _tile_loadd(6, b_tile_2->data, 64); // B2
341
+
342
+ // Accumulate: C += A0 × B0 + A1 × B1 + A2 × B2
343
+ _tile_dpbssd(0, 1, 4); // C += A0 × B0
344
+ _tile_dpbssd(0, 2, 5); // C += A1 × B1
345
+ _tile_dpbssd(0, 3, 6); // C += A2 × B2
346
+
347
+ // Store result
348
+ _tile_stored(0, state->data, 64);
349
+ }
350
+
351
+ /* Store BF16 2x2 state to output matrix with masking for edge tiles */
352
+ NK_INTERNAL void nk_dots_bf16_output2x2_sapphireamx_( //
353
+ nk_dots_bf16_state2x2_sapphireamx_t const *state, //
354
+ nk_f32_t *dst, nk_size_t dst_stride_elements, //
355
+ nk_size_t valid_rows, nk_size_t valid_cols) {
356
+
357
+ // Rows 0-15
358
+ nk_size_t const rows_high = (valid_rows > 16) ? 16 : valid_rows;
359
+ nk_size_t const cols_left = (valid_cols > 16) ? 16 : valid_cols;
360
+ nk_size_t const cols_right = (valid_cols > 16) ? valid_cols - 16 : 0;
361
+
362
+ if (rows_high > 0 && cols_left > 0)
363
+ nk_dots_bf16_store_sapphireamx_(&state->c[0][0], dst, dst_stride_elements, rows_high, cols_left);
364
+ if (rows_high > 0 && cols_right > 0)
365
+ nk_dots_bf16_store_sapphireamx_(&state->c[0][1], dst + 16, dst_stride_elements, rows_high, cols_right);
366
+
367
+ // Rows 16-31
368
+ if (valid_rows > 16) {
369
+ nk_size_t const rows_low = valid_rows - 16;
370
+ nk_f32_t *dst_low = dst + 16 * dst_stride_elements;
371
+ if (cols_left > 0)
372
+ nk_dots_bf16_store_sapphireamx_(&state->c[1][0], dst_low, dst_stride_elements, rows_low, cols_left);
373
+ if (cols_right > 0)
374
+ nk_dots_bf16_store_sapphireamx_(&state->c[1][1], dst_low + 16, dst_stride_elements, rows_low, cols_right);
375
+ }
376
+ }
377
+
378
+ /* Store INT8 2x2 state to output matrix with masking for edge tiles */
379
+ NK_INTERNAL void nk_dots_i8_output2x2_sapphireamx_( //
380
+ nk_dots_i8_state2x2_sapphireamx_t const *state, //
381
+ nk_i32_t *dst, nk_size_t dst_stride_elements, //
382
+ nk_size_t valid_rows, nk_size_t valid_cols) {
383
+
384
+ nk_size_t const rows_high = (valid_rows > 16) ? 16 : valid_rows;
385
+ nk_size_t const cols_left = (valid_cols > 16) ? 16 : valid_cols;
386
+ nk_size_t const cols_right = (valid_cols > 16) ? valid_cols - 16 : 0;
387
+
388
+ if (rows_high > 0 && cols_left > 0)
389
+ nk_dots_i8_store_sapphireamx_(&state->c[0][0], dst, dst_stride_elements, rows_high, cols_left);
390
+ if (rows_high > 0 && cols_right > 0)
391
+ nk_dots_i8_store_sapphireamx_(&state->c[0][1], dst + 16, dst_stride_elements, rows_high, cols_right);
392
+
393
+ if (valid_rows > 16) {
394
+ nk_size_t const rows_low = valid_rows - 16;
395
+ nk_i32_t *dst_low = dst + 16 * dst_stride_elements;
396
+ if (cols_left > 0)
397
+ nk_dots_i8_store_sapphireamx_(&state->c[1][0], dst_low, dst_stride_elements, rows_low, cols_left);
398
+ if (cols_right > 0)
399
+ nk_dots_i8_store_sapphireamx_(&state->c[1][1], dst_low + 16, dst_stride_elements, rows_low, cols_right);
400
+ }
401
+ }
402
+
403
+ /* Initialize UINT8 output state to zero */
404
+ NK_INTERNAL void nk_dots_u8_init_sapphireamx_(nk_dots_u8_state_sapphireamx_t *state) {
405
+ nk_dots_i8_init_sapphireamx_((nk_dots_i8_state_sapphireamx_t *)state);
406
+ }
407
+
408
+ /* Load U8 A tile from row-major source with masking for edge tiles */
409
+ NK_INTERNAL void nk_dots_u8_load_a_sapphireamx_( //
410
+ nk_dots_u8_a16x64_sapphireamx_t *a_tile, //
411
+ nk_u8_t const *src, nk_size_t src_stride, //
412
+ nk_size_t valid_rows, nk_size_t valid_cols) {
413
+ nk_dots_i8_load_a_sapphireamx_( //
414
+ (nk_dots_i8_a16x64_sapphireamx_t *)a_tile, //
415
+ (nk_i8_t const *)src, src_stride, valid_rows, valid_cols);
416
+ }
417
+
418
+ /* Store U8 state to output matrix with masking for edge tiles */
419
+ NK_INTERNAL void nk_dots_u8_store_sapphireamx_( //
420
+ nk_dots_u8_state_sapphireamx_t const *state, //
421
+ nk_u32_t *dst, nk_size_t dst_stride_elements, //
422
+ nk_size_t valid_rows, nk_size_t valid_cols) {
423
+ nk_dots_i8_store_sapphireamx_( //
424
+ (nk_dots_i8_state_sapphireamx_t const *)state, //
425
+ (nk_i32_t *)dst, dst_stride_elements, valid_rows, valid_cols);
426
+ }
427
+
428
+ /* Store UINT8 2x2 state to output matrix with masking for edge tiles */
429
+ NK_INTERNAL void nk_dots_u8_output2x2_sapphireamx_( //
430
+ nk_dots_u8_state2x2_sapphireamx_t const *state, //
431
+ nk_u32_t *dst, nk_size_t dst_stride_elements, //
432
+ nk_size_t valid_rows, nk_size_t valid_cols) {
433
+ nk_dots_i8_output2x2_sapphireamx_( //
434
+ (nk_dots_i8_state2x2_sapphireamx_t const *)state, //
435
+ (nk_i32_t *)dst, dst_stride_elements, valid_rows, valid_cols);
436
+ }
437
+
438
+ /* Pack U8 A transposed into B format */
439
+ NK_INTERNAL void nk_dots_pack_u8_transposed_sapphireamx_( //
440
+ nk_dots_u8_a16x64_sapphireamx_t const *a_tile, //
441
+ nk_dots_u8_b64x16_sapphireamx_t *b_tile) {
442
+
443
+ // Load all 16 rows - each row is 64 UINT8 = 64 bytes = 1 ZMM
444
+ // Treat as 16 × 32-bit elements per row (each 32-bit = quad of UINT8)
445
+ __m512i row00_i32x16 = _mm512_load_si512(&a_tile->data[0][0]);
446
+ __m512i row01_i32x16 = _mm512_load_si512(&a_tile->data[1][0]);
447
+ __m512i row02_i32x16 = _mm512_load_si512(&a_tile->data[2][0]);
448
+ __m512i row03_i32x16 = _mm512_load_si512(&a_tile->data[3][0]);
449
+ __m512i row04_i32x16 = _mm512_load_si512(&a_tile->data[4][0]);
450
+ __m512i row05_i32x16 = _mm512_load_si512(&a_tile->data[5][0]);
451
+ __m512i row06_i32x16 = _mm512_load_si512(&a_tile->data[6][0]);
452
+ __m512i row07_i32x16 = _mm512_load_si512(&a_tile->data[7][0]);
453
+ __m512i row08_i32x16 = _mm512_load_si512(&a_tile->data[8][0]);
454
+ __m512i row09_i32x16 = _mm512_load_si512(&a_tile->data[9][0]);
455
+ __m512i row10_i32x16 = _mm512_load_si512(&a_tile->data[10][0]);
456
+ __m512i row11_i32x16 = _mm512_load_si512(&a_tile->data[11][0]);
457
+ __m512i row12_i32x16 = _mm512_load_si512(&a_tile->data[12][0]);
458
+ __m512i row13_i32x16 = _mm512_load_si512(&a_tile->data[13][0]);
459
+ __m512i row14_i32x16 = _mm512_load_si512(&a_tile->data[14][0]);
460
+ __m512i row15_i32x16 = _mm512_load_si512(&a_tile->data[15][0]);
461
+
462
+ // 16×16 transpose of 32-bit elements using hierarchical unpacks
463
+ // Stage 1: Unpack adjacent row pairs at 32-bit granularity
464
+ __m512i t01_low_i32x16 = _mm512_unpacklo_epi32(row00_i32x16, row01_i32x16);
465
+ __m512i t01_high_i32x16 = _mm512_unpackhi_epi32(row00_i32x16, row01_i32x16);
466
+ __m512i t23_low_i32x16 = _mm512_unpacklo_epi32(row02_i32x16, row03_i32x16);
467
+ __m512i t23_high_i32x16 = _mm512_unpackhi_epi32(row02_i32x16, row03_i32x16);
468
+ __m512i t45_low_i32x16 = _mm512_unpacklo_epi32(row04_i32x16, row05_i32x16);
469
+ __m512i t45_high_i32x16 = _mm512_unpackhi_epi32(row04_i32x16, row05_i32x16);
470
+ __m512i t67_low_i32x16 = _mm512_unpacklo_epi32(row06_i32x16, row07_i32x16);
471
+ __m512i t67_high_i32x16 = _mm512_unpackhi_epi32(row06_i32x16, row07_i32x16);
472
+ __m512i t89_low_i32x16 = _mm512_unpacklo_epi32(row08_i32x16, row09_i32x16);
473
+ __m512i t89_high_i32x16 = _mm512_unpackhi_epi32(row08_i32x16, row09_i32x16);
474
+ __m512i tab_low_i32x16 = _mm512_unpacklo_epi32(row10_i32x16, row11_i32x16);
475
+ __m512i tab_high_i32x16 = _mm512_unpackhi_epi32(row10_i32x16, row11_i32x16);
476
+ __m512i tcd_low_i32x16 = _mm512_unpacklo_epi32(row12_i32x16, row13_i32x16);
477
+ __m512i tcd_high_i32x16 = _mm512_unpackhi_epi32(row12_i32x16, row13_i32x16);
478
+ __m512i tef_low_i32x16 = _mm512_unpacklo_epi32(row14_i32x16, row15_i32x16);
479
+ __m512i tef_high_i32x16 = _mm512_unpackhi_epi32(row14_i32x16, row15_i32x16);
480
+
481
+ // Stage 2: Unpack at 64-bit granularity
482
+ __m512i u0123_ll_i32x16 = _mm512_unpacklo_epi64(t01_low_i32x16, t23_low_i32x16);
483
+ __m512i u0123_lh_i32x16 = _mm512_unpackhi_epi64(t01_low_i32x16, t23_low_i32x16);
484
+ __m512i u0123_hl_i32x16 = _mm512_unpacklo_epi64(t01_high_i32x16, t23_high_i32x16);
485
+ __m512i u0123_hh_i32x16 = _mm512_unpackhi_epi64(t01_high_i32x16, t23_high_i32x16);
486
+ __m512i u4567_ll_i32x16 = _mm512_unpacklo_epi64(t45_low_i32x16, t67_low_i32x16);
487
+ __m512i u4567_lh_i32x16 = _mm512_unpackhi_epi64(t45_low_i32x16, t67_low_i32x16);
488
+ __m512i u4567_hl_i32x16 = _mm512_unpacklo_epi64(t45_high_i32x16, t67_high_i32x16);
489
+ __m512i u4567_hh_i32x16 = _mm512_unpackhi_epi64(t45_high_i32x16, t67_high_i32x16);
490
+ __m512i u89ab_ll_i32x16 = _mm512_unpacklo_epi64(t89_low_i32x16, tab_low_i32x16);
491
+ __m512i u89ab_lh_i32x16 = _mm512_unpackhi_epi64(t89_low_i32x16, tab_low_i32x16);
492
+ __m512i u89ab_hl_i32x16 = _mm512_unpacklo_epi64(t89_high_i32x16, tab_high_i32x16);
493
+ __m512i u89ab_hh_i32x16 = _mm512_unpackhi_epi64(t89_high_i32x16, tab_high_i32x16);
494
+ __m512i ucdef_ll_i32x16 = _mm512_unpacklo_epi64(tcd_low_i32x16, tef_low_i32x16);
495
+ __m512i ucdef_lh_i32x16 = _mm512_unpackhi_epi64(tcd_low_i32x16, tef_low_i32x16);
496
+ __m512i ucdef_hl_i32x16 = _mm512_unpacklo_epi64(tcd_high_i32x16, tef_high_i32x16);
497
+ __m512i ucdef_hh_i32x16 = _mm512_unpackhi_epi64(tcd_high_i32x16, tef_high_i32x16);
498
+
499
+ // Stage 3: Shuffle 128-bit lanes
500
+ __m512i v0_a_i32x16 = _mm512_shuffle_i32x4(u0123_ll_i32x16, u4567_ll_i32x16, 0x88);
501
+ __m512i v0_b_i32x16 = _mm512_shuffle_i32x4(u0123_ll_i32x16, u4567_ll_i32x16, 0xDD);
502
+ __m512i v1_a_i32x16 = _mm512_shuffle_i32x4(u0123_lh_i32x16, u4567_lh_i32x16, 0x88);
503
+ __m512i v1_b_i32x16 = _mm512_shuffle_i32x4(u0123_lh_i32x16, u4567_lh_i32x16, 0xDD);
504
+ __m512i v2_a_i32x16 = _mm512_shuffle_i32x4(u0123_hl_i32x16, u4567_hl_i32x16, 0x88);
505
+ __m512i v2_b_i32x16 = _mm512_shuffle_i32x4(u0123_hl_i32x16, u4567_hl_i32x16, 0xDD);
506
+ __m512i v3_a_i32x16 = _mm512_shuffle_i32x4(u0123_hh_i32x16, u4567_hh_i32x16, 0x88);
507
+ __m512i v3_b_i32x16 = _mm512_shuffle_i32x4(u0123_hh_i32x16, u4567_hh_i32x16, 0xDD);
508
+ __m512i v4_a_i32x16 = _mm512_shuffle_i32x4(u89ab_ll_i32x16, ucdef_ll_i32x16, 0x88);
509
+ __m512i v4_b_i32x16 = _mm512_shuffle_i32x4(u89ab_ll_i32x16, ucdef_ll_i32x16, 0xDD);
510
+ __m512i v5_a_i32x16 = _mm512_shuffle_i32x4(u89ab_lh_i32x16, ucdef_lh_i32x16, 0x88);
511
+ __m512i v5_b_i32x16 = _mm512_shuffle_i32x4(u89ab_lh_i32x16, ucdef_lh_i32x16, 0xDD);
512
+ __m512i v6_a_i32x16 = _mm512_shuffle_i32x4(u89ab_hl_i32x16, ucdef_hl_i32x16, 0x88);
513
+ __m512i v6_b_i32x16 = _mm512_shuffle_i32x4(u89ab_hl_i32x16, ucdef_hl_i32x16, 0xDD);
514
+ __m512i v7_a_i32x16 = _mm512_shuffle_i32x4(u89ab_hh_i32x16, ucdef_hh_i32x16, 0x88);
515
+ __m512i v7_b_i32x16 = _mm512_shuffle_i32x4(u89ab_hh_i32x16, ucdef_hh_i32x16, 0xDD);
516
+
517
+ // Stage 4: Final 256-bit shuffle to complete transpose
518
+ __m512i out00_i32x16 = _mm512_shuffle_i32x4(v0_a_i32x16, v4_a_i32x16, 0x88);
519
+ __m512i out01_i32x16 = _mm512_shuffle_i32x4(v1_a_i32x16, v5_a_i32x16, 0x88);
520
+ __m512i out02_i32x16 = _mm512_shuffle_i32x4(v2_a_i32x16, v6_a_i32x16, 0x88);
521
+ __m512i out03_i32x16 = _mm512_shuffle_i32x4(v3_a_i32x16, v7_a_i32x16, 0x88);
522
+ __m512i out04_i32x16 = _mm512_shuffle_i32x4(v0_a_i32x16, v4_a_i32x16, 0xDD);
523
+ __m512i out05_i32x16 = _mm512_shuffle_i32x4(v1_a_i32x16, v5_a_i32x16, 0xDD);
524
+ __m512i out06_i32x16 = _mm512_shuffle_i32x4(v2_a_i32x16, v6_a_i32x16, 0xDD);
525
+ __m512i out07_i32x16 = _mm512_shuffle_i32x4(v3_a_i32x16, v7_a_i32x16, 0xDD);
526
+ __m512i out08_i32x16 = _mm512_shuffle_i32x4(v0_b_i32x16, v4_b_i32x16, 0x88);
527
+ __m512i out09_i32x16 = _mm512_shuffle_i32x4(v1_b_i32x16, v5_b_i32x16, 0x88);
528
+ __m512i out10_i32x16 = _mm512_shuffle_i32x4(v2_b_i32x16, v6_b_i32x16, 0x88);
529
+ __m512i out11_i32x16 = _mm512_shuffle_i32x4(v3_b_i32x16, v7_b_i32x16, 0x88);
530
+ __m512i out12_i32x16 = _mm512_shuffle_i32x4(v0_b_i32x16, v4_b_i32x16, 0xDD);
531
+ __m512i out13_i32x16 = _mm512_shuffle_i32x4(v1_b_i32x16, v5_b_i32x16, 0xDD);
532
+ __m512i out14_i32x16 = _mm512_shuffle_i32x4(v2_b_i32x16, v6_b_i32x16, 0xDD);
533
+ __m512i out15_i32x16 = _mm512_shuffle_i32x4(v3_b_i32x16, v7_b_i32x16, 0xDD);
534
+
535
+ // Store transposed results - each output row is one depth_group
536
+ // Output layout: B.data[depth_group][column][quad] = 16 columns × 4 UINT8 = 64 bytes
537
+ _mm512_store_si512(&b_tile->data[0][0][0], out00_i32x16);
538
+ _mm512_store_si512(&b_tile->data[1][0][0], out01_i32x16);
539
+ _mm512_store_si512(&b_tile->data[2][0][0], out02_i32x16);
540
+ _mm512_store_si512(&b_tile->data[3][0][0], out03_i32x16);
541
+ _mm512_store_si512(&b_tile->data[4][0][0], out08_i32x16);
542
+ _mm512_store_si512(&b_tile->data[5][0][0], out09_i32x16);
543
+ _mm512_store_si512(&b_tile->data[6][0][0], out10_i32x16);
544
+ _mm512_store_si512(&b_tile->data[7][0][0], out11_i32x16);
545
+ _mm512_store_si512(&b_tile->data[8][0][0], out04_i32x16);
546
+ _mm512_store_si512(&b_tile->data[9][0][0], out05_i32x16);
547
+ _mm512_store_si512(&b_tile->data[10][0][0], out06_i32x16);
548
+ _mm512_store_si512(&b_tile->data[11][0][0], out07_i32x16);
549
+ _mm512_store_si512(&b_tile->data[12][0][0], out12_i32x16);
550
+ _mm512_store_si512(&b_tile->data[13][0][0], out13_i32x16);
551
+ _mm512_store_si512(&b_tile->data[14][0][0], out14_i32x16);
552
+ _mm512_store_si512(&b_tile->data[15][0][0], out15_i32x16);
553
+
554
+ nk_compiler_barrier_sapphireamx_();
555
+ }
556
+
557
+ /* Accumulate 3 A x B tile pairs into state using AMX TDPBUUD */
558
+ NK_INTERNAL void nk_dots_u8_update_sapphireamx_( //
559
+ nk_dots_u8_state_sapphireamx_t *state, //
560
+ nk_dots_u8_a16x64_sapphireamx_t const *a_tile_0, //
561
+ nk_dots_u8_a16x64_sapphireamx_t const *a_tile_1, //
562
+ nk_dots_u8_a16x64_sapphireamx_t const *a_tile_2, //
563
+ nk_dots_u8_b64x16_sapphireamx_t const *b_tile_0, //
564
+ nk_dots_u8_b64x16_sapphireamx_t const *b_tile_1, //
565
+ nk_dots_u8_b64x16_sapphireamx_t const *b_tile_2) {
566
+
567
+ // Load all tiles into registers
568
+ _tile_loadd(0, state->data, 64); // C accumulator
569
+ _tile_loadd(1, a_tile_0->data, 64); // A0
570
+ _tile_loadd(2, a_tile_1->data, 64); // A1
571
+ _tile_loadd(3, a_tile_2->data, 64); // A2
572
+ _tile_loadd(4, b_tile_0->data, 64); // B0
573
+ _tile_loadd(5, b_tile_1->data, 64); // B1
574
+ _tile_loadd(6, b_tile_2->data, 64); // B2
575
+
576
+ // Accumulate: C += A0 × B0 + A1 × B1 + A2 × B2
577
+ _tile_dpbuud(0, 1, 4); // C += A0 × B0
578
+ _tile_dpbuud(0, 2, 5); // C += A1 × B1
579
+ _tile_dpbuud(0, 3, 6); // C += A2 × B2
580
+
581
+ // Store result
582
+ _tile_stored(0, state->data, 64);
583
+ }
584
+
585
+ /* Load E4M3 A tile with FP8 to BF16 conversion */
586
+ NK_INTERNAL void nk_dots_e4m3_load_a_sapphireamx_( //
587
+ nk_dots_bf16_a16x32_sapphireamx_t *a_tile, //
588
+ nk_e4m3_t const *src, nk_size_t src_stride, //
589
+ nk_size_t valid_rows, nk_size_t valid_cols) {
590
+
591
+ __mmask32 column_mask = (valid_cols >= 32) ? 0xFFFFFFFF : ((__mmask32)1 << valid_cols) - 1;
592
+ __m512i zero_i16x32 = _mm512_setzero_si512();
593
+
594
+ for (nk_size_t row_idx = 0; row_idx < 16; row_idx++) {
595
+ if (row_idx < valid_rows) {
596
+ // Load 32 E4M3 bytes with masking
597
+ __m256i e4m3_row_u8x32 = _mm256_maskz_loadu_epi8(column_mask, src + row_idx * src_stride);
598
+ // Convert to 32 BF16 values
599
+ __m512i bf16_row_i16x32 = nk_e4m3x32_to_bf16x32_icelake_(e4m3_row_u8x32);
600
+ _mm512_store_si512((__m512i *)a_tile->data[row_idx], bf16_row_i16x32);
601
+ }
602
+ else { _mm512_store_si512((__m512i *)a_tile->data[row_idx], zero_i16x32); }
603
+ }
604
+ nk_compiler_barrier_sapphireamx_();
605
+ }
606
+
607
+ /* Load E5M2 A tile with FP8 to BF16 conversion */
608
+ NK_INTERNAL void nk_dots_e5m2_load_a_sapphireamx_( //
609
+ nk_dots_bf16_a16x32_sapphireamx_t *a_tile, //
610
+ nk_e5m2_t const *src, nk_size_t src_stride, //
611
+ nk_size_t valid_rows, nk_size_t valid_cols) {
612
+
613
+ __mmask32 column_mask = (valid_cols >= 32) ? 0xFFFFFFFF : ((__mmask32)1 << valid_cols) - 1;
614
+ __m512i zero_i16x32 = _mm512_setzero_si512();
615
+
616
+ for (nk_size_t row_idx = 0; row_idx < 16; row_idx++) {
617
+ if (row_idx < valid_rows) {
618
+ __m256i e5m2_row_u8x32 = _mm256_maskz_loadu_epi8(column_mask, src + row_idx * src_stride);
619
+ __m512i bf16_row_i16x32 = nk_e5m2x32_to_bf16x32_icelake_(e5m2_row_u8x32);
620
+ _mm512_store_si512((__m512i *)a_tile->data[row_idx], bf16_row_i16x32);
621
+ }
622
+ else { _mm512_store_si512((__m512i *)a_tile->data[row_idx], zero_i16x32); }
623
+ }
624
+ nk_compiler_barrier_sapphireamx_();
625
+ }
626
+
627
+ /* Pack A transposed into B format for BF16 */
628
+ NK_INTERNAL void nk_dots_pack_bf16_transposed_sapphireamx_( //
629
+ nk_dots_bf16_a16x32_sapphireamx_t const *a_tile, //
630
+ nk_dots_bf16_b32x16_sapphireamx_t *b_tile) {
631
+
632
+ // Load all 16 rows - each row is 32 BF16 = 64 bytes = 1 ZMM
633
+ // Treat as 16 × 32-bit elements per row (each 32-bit = pair of BF16)
634
+ __m512i row00_i32x16 = _mm512_load_si512(&a_tile->data[0][0]);
635
+ __m512i row01_i32x16 = _mm512_load_si512(&a_tile->data[1][0]);
636
+ __m512i row02_i32x16 = _mm512_load_si512(&a_tile->data[2][0]);
637
+ __m512i row03_i32x16 = _mm512_load_si512(&a_tile->data[3][0]);
638
+ __m512i row04_i32x16 = _mm512_load_si512(&a_tile->data[4][0]);
639
+ __m512i row05_i32x16 = _mm512_load_si512(&a_tile->data[5][0]);
640
+ __m512i row06_i32x16 = _mm512_load_si512(&a_tile->data[6][0]);
641
+ __m512i row07_i32x16 = _mm512_load_si512(&a_tile->data[7][0]);
642
+ __m512i row08_i32x16 = _mm512_load_si512(&a_tile->data[8][0]);
643
+ __m512i row09_i32x16 = _mm512_load_si512(&a_tile->data[9][0]);
644
+ __m512i row10_i32x16 = _mm512_load_si512(&a_tile->data[10][0]);
645
+ __m512i row11_i32x16 = _mm512_load_si512(&a_tile->data[11][0]);
646
+ __m512i row12_i32x16 = _mm512_load_si512(&a_tile->data[12][0]);
647
+ __m512i row13_i32x16 = _mm512_load_si512(&a_tile->data[13][0]);
648
+ __m512i row14_i32x16 = _mm512_load_si512(&a_tile->data[14][0]);
649
+ __m512i row15_i32x16 = _mm512_load_si512(&a_tile->data[15][0]);
650
+
651
+ // 16×16 transpose of 32-bit elements using hierarchical unpacks
652
+ // Stage 1: Unpack adjacent row pairs at 32-bit granularity
653
+ __m512i t01_low_i32x16 = _mm512_unpacklo_epi32(row00_i32x16, row01_i32x16);
654
+ __m512i t01_high_i32x16 = _mm512_unpackhi_epi32(row00_i32x16, row01_i32x16);
655
+ __m512i t23_low_i32x16 = _mm512_unpacklo_epi32(row02_i32x16, row03_i32x16);
656
+ __m512i t23_high_i32x16 = _mm512_unpackhi_epi32(row02_i32x16, row03_i32x16);
657
+ __m512i t45_low_i32x16 = _mm512_unpacklo_epi32(row04_i32x16, row05_i32x16);
658
+ __m512i t45_high_i32x16 = _mm512_unpackhi_epi32(row04_i32x16, row05_i32x16);
659
+ __m512i t67_low_i32x16 = _mm512_unpacklo_epi32(row06_i32x16, row07_i32x16);
660
+ __m512i t67_high_i32x16 = _mm512_unpackhi_epi32(row06_i32x16, row07_i32x16);
661
+ __m512i t89_low_i32x16 = _mm512_unpacklo_epi32(row08_i32x16, row09_i32x16);
662
+ __m512i t89_high_i32x16 = _mm512_unpackhi_epi32(row08_i32x16, row09_i32x16);
663
+ __m512i tab_low_i32x16 = _mm512_unpacklo_epi32(row10_i32x16, row11_i32x16);
664
+ __m512i tab_high_i32x16 = _mm512_unpackhi_epi32(row10_i32x16, row11_i32x16);
665
+ __m512i tcd_low_i32x16 = _mm512_unpacklo_epi32(row12_i32x16, row13_i32x16);
666
+ __m512i tcd_high_i32x16 = _mm512_unpackhi_epi32(row12_i32x16, row13_i32x16);
667
+ __m512i tef_low_i32x16 = _mm512_unpacklo_epi32(row14_i32x16, row15_i32x16);
668
+ __m512i tef_high_i32x16 = _mm512_unpackhi_epi32(row14_i32x16, row15_i32x16);
669
+
670
+ // Stage 2: Unpack at 64-bit granularity
671
+ __m512i u0123_ll_i32x16 = _mm512_unpacklo_epi64(t01_low_i32x16, t23_low_i32x16);
672
+ __m512i u0123_lh_i32x16 = _mm512_unpackhi_epi64(t01_low_i32x16, t23_low_i32x16);
673
+ __m512i u0123_hl_i32x16 = _mm512_unpacklo_epi64(t01_high_i32x16, t23_high_i32x16);
674
+ __m512i u0123_hh_i32x16 = _mm512_unpackhi_epi64(t01_high_i32x16, t23_high_i32x16);
675
+ __m512i u4567_ll_i32x16 = _mm512_unpacklo_epi64(t45_low_i32x16, t67_low_i32x16);
676
+ __m512i u4567_lh_i32x16 = _mm512_unpackhi_epi64(t45_low_i32x16, t67_low_i32x16);
677
+ __m512i u4567_hl_i32x16 = _mm512_unpacklo_epi64(t45_high_i32x16, t67_high_i32x16);
678
+ __m512i u4567_hh_i32x16 = _mm512_unpackhi_epi64(t45_high_i32x16, t67_high_i32x16);
679
+ __m512i u89ab_ll_i32x16 = _mm512_unpacklo_epi64(t89_low_i32x16, tab_low_i32x16);
680
+ __m512i u89ab_lh_i32x16 = _mm512_unpackhi_epi64(t89_low_i32x16, tab_low_i32x16);
681
+ __m512i u89ab_hl_i32x16 = _mm512_unpacklo_epi64(t89_high_i32x16, tab_high_i32x16);
682
+ __m512i u89ab_hh_i32x16 = _mm512_unpackhi_epi64(t89_high_i32x16, tab_high_i32x16);
683
+ __m512i ucdef_ll_i32x16 = _mm512_unpacklo_epi64(tcd_low_i32x16, tef_low_i32x16);
684
+ __m512i ucdef_lh_i32x16 = _mm512_unpackhi_epi64(tcd_low_i32x16, tef_low_i32x16);
685
+ __m512i ucdef_hl_i32x16 = _mm512_unpacklo_epi64(tcd_high_i32x16, tef_high_i32x16);
686
+ __m512i ucdef_hh_i32x16 = _mm512_unpackhi_epi64(tcd_high_i32x16, tef_high_i32x16);
687
+
688
+ // Stage 3: Shuffle 128-bit lanes using permute2x128 equivalent for 512-bit
689
+ // Use shuffle_i32x4 to move 128-bit chunks
690
+ __m512i v0_a_i32x16 = _mm512_shuffle_i32x4(u0123_ll_i32x16, u4567_ll_i32x16, 0x88); // lanes 0,2 from each
691
+ __m512i v0_b_i32x16 = _mm512_shuffle_i32x4(u0123_ll_i32x16, u4567_ll_i32x16, 0xDD); // lanes 1,3 from each
692
+ __m512i v1_a_i32x16 = _mm512_shuffle_i32x4(u0123_lh_i32x16, u4567_lh_i32x16, 0x88);
693
+ __m512i v1_b_i32x16 = _mm512_shuffle_i32x4(u0123_lh_i32x16, u4567_lh_i32x16, 0xDD);
694
+ __m512i v2_a_i32x16 = _mm512_shuffle_i32x4(u0123_hl_i32x16, u4567_hl_i32x16, 0x88);
695
+ __m512i v2_b_i32x16 = _mm512_shuffle_i32x4(u0123_hl_i32x16, u4567_hl_i32x16, 0xDD);
696
+ __m512i v3_a_i32x16 = _mm512_shuffle_i32x4(u0123_hh_i32x16, u4567_hh_i32x16, 0x88);
697
+ __m512i v3_b_i32x16 = _mm512_shuffle_i32x4(u0123_hh_i32x16, u4567_hh_i32x16, 0xDD);
698
+ __m512i v4_a_i32x16 = _mm512_shuffle_i32x4(u89ab_ll_i32x16, ucdef_ll_i32x16, 0x88);
699
+ __m512i v4_b_i32x16 = _mm512_shuffle_i32x4(u89ab_ll_i32x16, ucdef_ll_i32x16, 0xDD);
700
+ __m512i v5_a_i32x16 = _mm512_shuffle_i32x4(u89ab_lh_i32x16, ucdef_lh_i32x16, 0x88);
701
+ __m512i v5_b_i32x16 = _mm512_shuffle_i32x4(u89ab_lh_i32x16, ucdef_lh_i32x16, 0xDD);
702
+ __m512i v6_a_i32x16 = _mm512_shuffle_i32x4(u89ab_hl_i32x16, ucdef_hl_i32x16, 0x88);
703
+ __m512i v6_b_i32x16 = _mm512_shuffle_i32x4(u89ab_hl_i32x16, ucdef_hl_i32x16, 0xDD);
704
+ __m512i v7_a_i32x16 = _mm512_shuffle_i32x4(u89ab_hh_i32x16, ucdef_hh_i32x16, 0x88);
705
+ __m512i v7_b_i32x16 = _mm512_shuffle_i32x4(u89ab_hh_i32x16, ucdef_hh_i32x16, 0xDD);
706
+
707
+ // Stage 4: Final 256-bit shuffle to complete transpose
708
+ __m512i out00_i32x16 = _mm512_shuffle_i32x4(v0_a_i32x16, v4_a_i32x16, 0x88);
709
+ __m512i out01_i32x16 = _mm512_shuffle_i32x4(v1_a_i32x16, v5_a_i32x16, 0x88);
710
+ __m512i out02_i32x16 = _mm512_shuffle_i32x4(v2_a_i32x16, v6_a_i32x16, 0x88);
711
+ __m512i out03_i32x16 = _mm512_shuffle_i32x4(v3_a_i32x16, v7_a_i32x16, 0x88);
712
+ __m512i out04_i32x16 = _mm512_shuffle_i32x4(v0_a_i32x16, v4_a_i32x16, 0xDD);
713
+ __m512i out05_i32x16 = _mm512_shuffle_i32x4(v1_a_i32x16, v5_a_i32x16, 0xDD);
714
+ __m512i out06_i32x16 = _mm512_shuffle_i32x4(v2_a_i32x16, v6_a_i32x16, 0xDD);
715
+ __m512i out07_i32x16 = _mm512_shuffle_i32x4(v3_a_i32x16, v7_a_i32x16, 0xDD);
716
+ __m512i out08_i32x16 = _mm512_shuffle_i32x4(v0_b_i32x16, v4_b_i32x16, 0x88);
717
+ __m512i out09_i32x16 = _mm512_shuffle_i32x4(v1_b_i32x16, v5_b_i32x16, 0x88);
718
+ __m512i out10_i32x16 = _mm512_shuffle_i32x4(v2_b_i32x16, v6_b_i32x16, 0x88);
719
+ __m512i out11_i32x16 = _mm512_shuffle_i32x4(v3_b_i32x16, v7_b_i32x16, 0x88);
720
+ __m512i out12_i32x16 = _mm512_shuffle_i32x4(v0_b_i32x16, v4_b_i32x16, 0xDD);
721
+ __m512i out13_i32x16 = _mm512_shuffle_i32x4(v1_b_i32x16, v5_b_i32x16, 0xDD);
722
+ __m512i out14_i32x16 = _mm512_shuffle_i32x4(v2_b_i32x16, v6_b_i32x16, 0xDD);
723
+ __m512i out15_i32x16 = _mm512_shuffle_i32x4(v3_b_i32x16, v7_b_i32x16, 0xDD);
724
+
725
+ // Store transposed results - each output row is one depth_group
726
+ // Output layout: B.data[depth_group][column][pair] = 16 columns × 2 BF16 = 64 bytes
727
+ _mm512_store_si512(&b_tile->data[0][0][0], out00_i32x16);
728
+ _mm512_store_si512(&b_tile->data[1][0][0], out01_i32x16);
729
+ _mm512_store_si512(&b_tile->data[2][0][0], out02_i32x16);
730
+ _mm512_store_si512(&b_tile->data[3][0][0], out03_i32x16);
731
+ _mm512_store_si512(&b_tile->data[4][0][0], out08_i32x16);
732
+ _mm512_store_si512(&b_tile->data[5][0][0], out09_i32x16);
733
+ _mm512_store_si512(&b_tile->data[6][0][0], out10_i32x16);
734
+ _mm512_store_si512(&b_tile->data[7][0][0], out11_i32x16);
735
+ _mm512_store_si512(&b_tile->data[8][0][0], out04_i32x16);
736
+ _mm512_store_si512(&b_tile->data[9][0][0], out05_i32x16);
737
+ _mm512_store_si512(&b_tile->data[10][0][0], out06_i32x16);
738
+ _mm512_store_si512(&b_tile->data[11][0][0], out07_i32x16);
739
+ _mm512_store_si512(&b_tile->data[12][0][0], out12_i32x16);
740
+ _mm512_store_si512(&b_tile->data[13][0][0], out13_i32x16);
741
+ _mm512_store_si512(&b_tile->data[14][0][0], out14_i32x16);
742
+ _mm512_store_si512(&b_tile->data[15][0][0], out15_i32x16);
743
+
744
+ nk_compiler_barrier_sapphireamx_();
745
+ }
746
+
747
+ /* Pack A transposed into B format for INT8 */
748
+ NK_INTERNAL void nk_dots_pack_i8_transposed_sapphireamx_( //
749
+ nk_dots_i8_a16x64_sapphireamx_t const *a_tile, //
750
+ nk_dots_i8_b64x16_sapphireamx_t *b_tile) {
751
+
752
+ // Load all 16 rows - each row is 64 INT8 = 64 bytes = 1 ZMM
753
+ // Treat as 16 × 32-bit elements per row (each 32-bit = quad of INT8)
754
+ __m512i row00_i32x16 = _mm512_load_si512(&a_tile->data[0][0]);
755
+ __m512i row01_i32x16 = _mm512_load_si512(&a_tile->data[1][0]);
756
+ __m512i row02_i32x16 = _mm512_load_si512(&a_tile->data[2][0]);
757
+ __m512i row03_i32x16 = _mm512_load_si512(&a_tile->data[3][0]);
758
+ __m512i row04_i32x16 = _mm512_load_si512(&a_tile->data[4][0]);
759
+ __m512i row05_i32x16 = _mm512_load_si512(&a_tile->data[5][0]);
760
+ __m512i row06_i32x16 = _mm512_load_si512(&a_tile->data[6][0]);
761
+ __m512i row07_i32x16 = _mm512_load_si512(&a_tile->data[7][0]);
762
+ __m512i row08_i32x16 = _mm512_load_si512(&a_tile->data[8][0]);
763
+ __m512i row09_i32x16 = _mm512_load_si512(&a_tile->data[9][0]);
764
+ __m512i row10_i32x16 = _mm512_load_si512(&a_tile->data[10][0]);
765
+ __m512i row11_i32x16 = _mm512_load_si512(&a_tile->data[11][0]);
766
+ __m512i row12_i32x16 = _mm512_load_si512(&a_tile->data[12][0]);
767
+ __m512i row13_i32x16 = _mm512_load_si512(&a_tile->data[13][0]);
768
+ __m512i row14_i32x16 = _mm512_load_si512(&a_tile->data[14][0]);
769
+ __m512i row15_i32x16 = _mm512_load_si512(&a_tile->data[15][0]);
770
+
771
+ // 16×16 transpose of 32-bit elements using hierarchical unpacks
772
+ // Stage 1: Unpack adjacent row pairs at 32-bit granularity
773
+ __m512i t01_low_i32x16 = _mm512_unpacklo_epi32(row00_i32x16, row01_i32x16);
774
+ __m512i t01_high_i32x16 = _mm512_unpackhi_epi32(row00_i32x16, row01_i32x16);
775
+ __m512i t23_low_i32x16 = _mm512_unpacklo_epi32(row02_i32x16, row03_i32x16);
776
+ __m512i t23_high_i32x16 = _mm512_unpackhi_epi32(row02_i32x16, row03_i32x16);
777
+ __m512i t45_low_i32x16 = _mm512_unpacklo_epi32(row04_i32x16, row05_i32x16);
778
+ __m512i t45_high_i32x16 = _mm512_unpackhi_epi32(row04_i32x16, row05_i32x16);
779
+ __m512i t67_low_i32x16 = _mm512_unpacklo_epi32(row06_i32x16, row07_i32x16);
780
+ __m512i t67_high_i32x16 = _mm512_unpackhi_epi32(row06_i32x16, row07_i32x16);
781
+ __m512i t89_low_i32x16 = _mm512_unpacklo_epi32(row08_i32x16, row09_i32x16);
782
+ __m512i t89_high_i32x16 = _mm512_unpackhi_epi32(row08_i32x16, row09_i32x16);
783
+ __m512i tab_low_i32x16 = _mm512_unpacklo_epi32(row10_i32x16, row11_i32x16);
784
+ __m512i tab_high_i32x16 = _mm512_unpackhi_epi32(row10_i32x16, row11_i32x16);
785
+ __m512i tcd_low_i32x16 = _mm512_unpacklo_epi32(row12_i32x16, row13_i32x16);
786
+ __m512i tcd_high_i32x16 = _mm512_unpackhi_epi32(row12_i32x16, row13_i32x16);
787
+ __m512i tef_low_i32x16 = _mm512_unpacklo_epi32(row14_i32x16, row15_i32x16);
788
+ __m512i tef_high_i32x16 = _mm512_unpackhi_epi32(row14_i32x16, row15_i32x16);
789
+
790
+ // Stage 2: Unpack at 64-bit granularity
791
+ __m512i u0123_ll_i32x16 = _mm512_unpacklo_epi64(t01_low_i32x16, t23_low_i32x16);
792
+ __m512i u0123_lh_i32x16 = _mm512_unpackhi_epi64(t01_low_i32x16, t23_low_i32x16);
793
+ __m512i u0123_hl_i32x16 = _mm512_unpacklo_epi64(t01_high_i32x16, t23_high_i32x16);
794
+ __m512i u0123_hh_i32x16 = _mm512_unpackhi_epi64(t01_high_i32x16, t23_high_i32x16);
795
+ __m512i u4567_ll_i32x16 = _mm512_unpacklo_epi64(t45_low_i32x16, t67_low_i32x16);
796
+ __m512i u4567_lh_i32x16 = _mm512_unpackhi_epi64(t45_low_i32x16, t67_low_i32x16);
797
+ __m512i u4567_hl_i32x16 = _mm512_unpacklo_epi64(t45_high_i32x16, t67_high_i32x16);
798
+ __m512i u4567_hh_i32x16 = _mm512_unpackhi_epi64(t45_high_i32x16, t67_high_i32x16);
799
+ __m512i u89ab_ll_i32x16 = _mm512_unpacklo_epi64(t89_low_i32x16, tab_low_i32x16);
800
+ __m512i u89ab_lh_i32x16 = _mm512_unpackhi_epi64(t89_low_i32x16, tab_low_i32x16);
801
+ __m512i u89ab_hl_i32x16 = _mm512_unpacklo_epi64(t89_high_i32x16, tab_high_i32x16);
802
+ __m512i u89ab_hh_i32x16 = _mm512_unpackhi_epi64(t89_high_i32x16, tab_high_i32x16);
803
+ __m512i ucdef_ll_i32x16 = _mm512_unpacklo_epi64(tcd_low_i32x16, tef_low_i32x16);
804
+ __m512i ucdef_lh_i32x16 = _mm512_unpackhi_epi64(tcd_low_i32x16, tef_low_i32x16);
805
+ __m512i ucdef_hl_i32x16 = _mm512_unpacklo_epi64(tcd_high_i32x16, tef_high_i32x16);
806
+ __m512i ucdef_hh_i32x16 = _mm512_unpackhi_epi64(tcd_high_i32x16, tef_high_i32x16);
807
+
808
+ // Stage 3: Shuffle 128-bit lanes
809
+ __m512i v0_a_i32x16 = _mm512_shuffle_i32x4(u0123_ll_i32x16, u4567_ll_i32x16, 0x88);
810
+ __m512i v0_b_i32x16 = _mm512_shuffle_i32x4(u0123_ll_i32x16, u4567_ll_i32x16, 0xDD);
811
+ __m512i v1_a_i32x16 = _mm512_shuffle_i32x4(u0123_lh_i32x16, u4567_lh_i32x16, 0x88);
812
+ __m512i v1_b_i32x16 = _mm512_shuffle_i32x4(u0123_lh_i32x16, u4567_lh_i32x16, 0xDD);
813
+ __m512i v2_a_i32x16 = _mm512_shuffle_i32x4(u0123_hl_i32x16, u4567_hl_i32x16, 0x88);
814
+ __m512i v2_b_i32x16 = _mm512_shuffle_i32x4(u0123_hl_i32x16, u4567_hl_i32x16, 0xDD);
815
+ __m512i v3_a_i32x16 = _mm512_shuffle_i32x4(u0123_hh_i32x16, u4567_hh_i32x16, 0x88);
816
+ __m512i v3_b_i32x16 = _mm512_shuffle_i32x4(u0123_hh_i32x16, u4567_hh_i32x16, 0xDD);
817
+ __m512i v4_a_i32x16 = _mm512_shuffle_i32x4(u89ab_ll_i32x16, ucdef_ll_i32x16, 0x88);
818
+ __m512i v4_b_i32x16 = _mm512_shuffle_i32x4(u89ab_ll_i32x16, ucdef_ll_i32x16, 0xDD);
819
+ __m512i v5_a_i32x16 = _mm512_shuffle_i32x4(u89ab_lh_i32x16, ucdef_lh_i32x16, 0x88);
820
+ __m512i v5_b_i32x16 = _mm512_shuffle_i32x4(u89ab_lh_i32x16, ucdef_lh_i32x16, 0xDD);
821
+ __m512i v6_a_i32x16 = _mm512_shuffle_i32x4(u89ab_hl_i32x16, ucdef_hl_i32x16, 0x88);
822
+ __m512i v6_b_i32x16 = _mm512_shuffle_i32x4(u89ab_hl_i32x16, ucdef_hl_i32x16, 0xDD);
823
+ __m512i v7_a_i32x16 = _mm512_shuffle_i32x4(u89ab_hh_i32x16, ucdef_hh_i32x16, 0x88);
824
+ __m512i v7_b_i32x16 = _mm512_shuffle_i32x4(u89ab_hh_i32x16, ucdef_hh_i32x16, 0xDD);
825
+
826
+ // Stage 4: Final 256-bit shuffle to complete transpose
827
+ __m512i out00_i32x16 = _mm512_shuffle_i32x4(v0_a_i32x16, v4_a_i32x16, 0x88);
828
+ __m512i out01_i32x16 = _mm512_shuffle_i32x4(v1_a_i32x16, v5_a_i32x16, 0x88);
829
+ __m512i out02_i32x16 = _mm512_shuffle_i32x4(v2_a_i32x16, v6_a_i32x16, 0x88);
830
+ __m512i out03_i32x16 = _mm512_shuffle_i32x4(v3_a_i32x16, v7_a_i32x16, 0x88);
831
+ __m512i out04_i32x16 = _mm512_shuffle_i32x4(v0_a_i32x16, v4_a_i32x16, 0xDD);
832
+ __m512i out05_i32x16 = _mm512_shuffle_i32x4(v1_a_i32x16, v5_a_i32x16, 0xDD);
833
+ __m512i out06_i32x16 = _mm512_shuffle_i32x4(v2_a_i32x16, v6_a_i32x16, 0xDD);
834
+ __m512i out07_i32x16 = _mm512_shuffle_i32x4(v3_a_i32x16, v7_a_i32x16, 0xDD);
835
+ __m512i out08_i32x16 = _mm512_shuffle_i32x4(v0_b_i32x16, v4_b_i32x16, 0x88);
836
+ __m512i out09_i32x16 = _mm512_shuffle_i32x4(v1_b_i32x16, v5_b_i32x16, 0x88);
837
+ __m512i out10_i32x16 = _mm512_shuffle_i32x4(v2_b_i32x16, v6_b_i32x16, 0x88);
838
+ __m512i out11_i32x16 = _mm512_shuffle_i32x4(v3_b_i32x16, v7_b_i32x16, 0x88);
839
+ __m512i out12_i32x16 = _mm512_shuffle_i32x4(v0_b_i32x16, v4_b_i32x16, 0xDD);
840
+ __m512i out13_i32x16 = _mm512_shuffle_i32x4(v1_b_i32x16, v5_b_i32x16, 0xDD);
841
+ __m512i out14_i32x16 = _mm512_shuffle_i32x4(v2_b_i32x16, v6_b_i32x16, 0xDD);
842
+ __m512i out15_i32x16 = _mm512_shuffle_i32x4(v3_b_i32x16, v7_b_i32x16, 0xDD);
843
+
844
+ // Store transposed results - each output row is one depth_group
845
+ // Output layout: B.data[depth_group][column][quad] = 16 columns × 4 INT8 = 64 bytes
846
+ _mm512_store_si512(&b_tile->data[0][0][0], out00_i32x16);
847
+ _mm512_store_si512(&b_tile->data[1][0][0], out01_i32x16);
848
+ _mm512_store_si512(&b_tile->data[2][0][0], out02_i32x16);
849
+ _mm512_store_si512(&b_tile->data[3][0][0], out03_i32x16);
850
+ _mm512_store_si512(&b_tile->data[4][0][0], out08_i32x16);
851
+ _mm512_store_si512(&b_tile->data[5][0][0], out09_i32x16);
852
+ _mm512_store_si512(&b_tile->data[6][0][0], out10_i32x16);
853
+ _mm512_store_si512(&b_tile->data[7][0][0], out11_i32x16);
854
+ _mm512_store_si512(&b_tile->data[8][0][0], out04_i32x16);
855
+ _mm512_store_si512(&b_tile->data[9][0][0], out05_i32x16);
856
+ _mm512_store_si512(&b_tile->data[10][0][0], out06_i32x16);
857
+ _mm512_store_si512(&b_tile->data[11][0][0], out07_i32x16);
858
+ _mm512_store_si512(&b_tile->data[12][0][0], out12_i32x16);
859
+ _mm512_store_si512(&b_tile->data[13][0][0], out13_i32x16);
860
+ _mm512_store_si512(&b_tile->data[14][0][0], out14_i32x16);
861
+ _mm512_store_si512(&b_tile->data[15][0][0], out15_i32x16);
862
+
863
+ nk_compiler_barrier_sapphireamx_();
864
+ }
865
+
866
+ #pragma region F16 Floats
867
+
868
+ NK_PUBLIC nk_size_t nk_dots_packed_size_bf16_sapphireamx(nk_size_t column_count, nk_size_t depth) {
869
+ nk_size_t const tmm_rows = 16;
870
+ nk_size_t const tmm_cols = 32;
871
+ nk_size_t const tile_bytes = 512 * sizeof(nk_bf16_t); // 16 × 32 × 2 = 1KB
872
+
873
+ nk_size_t const full_column_tiles = column_count / tmm_rows;
874
+ nk_size_t const tiles_along_depth = nk_size_divide_round_up_(depth, tmm_cols);
875
+ nk_size_t const column_remainder_count = column_count - full_column_tiles * tmm_rows;
876
+
877
+ // Header (64 bytes aligned)
878
+ nk_size_t size = sizeof(nk_dots_amx_packed_header_t);
879
+
880
+ // All tiles for full column rows (Morton-ordered, pair-interleaved, depth remainder zero-padded)
881
+ size += full_column_tiles * tiles_along_depth * tile_bytes;
882
+
883
+ // Column edge: remaining rows for ALL depth columns, stored row-major
884
+ if (column_remainder_count > 0) size += column_remainder_count * depth * sizeof(nk_bf16_t);
885
+
886
+ // Per-column norms for angular/euclidean distance (4 bytes each: f32 or u32)
887
+ size += column_count * sizeof(nk_f32_t);
888
+
889
+ return size;
890
+ }
891
+
892
+ NK_PUBLIC void nk_dots_pack_bf16_sapphireamx( //
893
+ nk_bf16_t const *b, nk_size_t column_count, nk_size_t depth, //
894
+ nk_size_t b_stride_in_bytes, void *b_packed) {
895
+
896
+ // AMX BF16 tile dimensions: 16 rows × 32 columns (512 BF16 elements = 1KB)
897
+ nk_size_t const tmm_rows = 16;
898
+ nk_size_t const tmm_cols = 32;
899
+ nk_size_t const tile_elements = 512;
900
+ nk_size_t const tile_bytes = tile_elements * sizeof(nk_bf16_t);
901
+ nk_size_t const b_stride_elements = b_stride_in_bytes / sizeof(nk_bf16_t);
902
+
903
+ // Compute layout dimensions
904
+ nk_size_t const column_tiles_count = column_count / tmm_rows;
905
+ nk_size_t const depth_tiles_count = nk_size_divide_round_up_(depth, tmm_cols);
906
+ nk_size_t const column_remainder_count = column_count - column_tiles_count * tmm_rows;
907
+ nk_size_t const total_tiles = column_tiles_count * depth_tiles_count;
908
+
909
+ // Write header with layout metadata
910
+ nk_dots_amx_packed_header_t *header = (nk_dots_amx_packed_header_t *)b_packed;
911
+ header->full_column_tiles = (nk_u32_t)column_tiles_count;
912
+ header->full_depth_tiles = (nk_u32_t)depth_tiles_count;
913
+ header->column_remainder_count = (nk_u32_t)column_remainder_count;
914
+
915
+ // Compute memory region offsets
916
+ nk_size_t const tiles_offset = sizeof(nk_dots_amx_packed_header_t);
917
+ nk_size_t const column_edge_offset = tiles_offset + total_tiles * tile_bytes;
918
+ header->column_edge_offset = (nk_u32_t)column_edge_offset;
919
+
920
+ // Pointers to packed data regions
921
+ nk_bf16_t *tiles_ptr = (nk_bf16_t *)((char *)b_packed + tiles_offset);
922
+ nk_bf16_t *column_edge_ptr = (nk_bf16_t *)((char *)b_packed + column_edge_offset);
923
+
924
+ // Pack tiles using vectorized transposer: gather 16 strided rows into an aligned
925
+ // temporary, transpose via SIMD, then copy the result to the packed buffer.
926
+ for (nk_size_t column_tile_idx = 0; column_tile_idx < column_tiles_count; column_tile_idx++) {
927
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
928
+
929
+ nk_size_t const tile_index = column_tile_idx * depth_tiles_count + depth_tile_idx;
930
+ nk_bf16_t *tile_output = tiles_ptr + tile_index * tile_elements;
931
+
932
+ nk_size_t const src_row_start = column_tile_idx * tmm_rows;
933
+ nk_size_t const src_column_start = depth_tile_idx * tmm_cols;
934
+ nk_size_t const columns_to_pack = (src_column_start + tmm_cols <= depth) ? tmm_cols
935
+ : (depth - src_column_start);
936
+
937
+ // Gather 16 strided source rows into a contiguous aligned tile
938
+ nk_dots_bf16_a16x32_sapphireamx_t source_tile;
939
+ if (columns_to_pack == tmm_cols) {
940
+ for (nk_size_t row_idx = 0; row_idx < tmm_rows; row_idx++) {
941
+ nk_bf16_t const *source_row = b + (src_row_start + row_idx) * b_stride_elements + src_column_start;
942
+ _mm512_store_si512(&source_tile.data[row_idx][0], _mm512_loadu_si512(source_row));
943
+ }
944
+ }
945
+ else {
946
+ __mmask32 depth_mask = (__mmask32)((columns_to_pack < 32) ? ((1U << columns_to_pack) - 1) : ~0U);
947
+ for (nk_size_t row_idx = 0; row_idx < tmm_rows; row_idx++) {
948
+ nk_bf16_t const *source_row = b + (src_row_start + row_idx) * b_stride_elements + src_column_start;
949
+ _mm512_store_si512(&source_tile.data[row_idx][0], _mm512_maskz_loadu_epi16(depth_mask, source_row));
950
+ }
951
+ }
952
+
953
+ // Transpose into aligned local, then copy to (potentially unaligned) packed buffer
954
+ nk_dots_bf16_b32x16_sapphireamx_t transposed_tile;
955
+ nk_dots_pack_bf16_transposed_sapphireamx_(&source_tile, &transposed_tile);
956
+ for (nk_size_t i = 0; i < tile_bytes; i += 64)
957
+ _mm512_storeu_si512((char *)tile_output + i, _mm512_load_si512((char const *)&transposed_tile + i));
958
+ }
959
+ }
960
+
961
+ // Pack column-remainder rows in simple row-major format (for AVX-512 fallback)
962
+ if (column_remainder_count > 0) {
963
+ nk_size_t const remainder_start_row = column_tiles_count * tmm_rows;
964
+ for (nk_size_t row_idx = 0; row_idx < column_remainder_count; row_idx++) {
965
+ for (nk_size_t column_idx = 0; column_idx < depth; column_idx++) {
966
+ column_edge_ptr[row_idx * depth + column_idx] =
967
+ b[(remainder_start_row + row_idx) * b_stride_elements + column_idx];
968
+ }
969
+ }
970
+ }
971
+
972
+ // Compute and store per-column norms for angular/euclidean distance
973
+ nk_size_t norms_offset = column_edge_offset +
974
+ (column_remainder_count > 0 ? column_remainder_count * depth * sizeof(nk_bf16_t) : 0);
975
+ header->norms_byte_offset = (nk_u32_t)norms_offset;
976
+ nk_f32_t *norms = (nk_f32_t *)((char *)b_packed + norms_offset);
977
+ for (nk_size_t col = 0; col < column_count; col++)
978
+ norms[col] = nk_dots_reduce_sumsq_bf16_(b + col * b_stride_elements, depth);
979
+ }
980
+
981
+ NK_PUBLIC void nk_dots_packed_bf16_sapphireamx( //
982
+ nk_bf16_t const *a, void const *b_packed, nk_f32_t *c, //
983
+ nk_size_t rows_count, nk_size_t cols_count, nk_size_t depth, nk_size_t a_stride_bytes, nk_size_t c_stride_bytes) {
984
+ nk_unused_(cols_count);
985
+
986
+ // Parse packed B header
987
+ nk_dots_amx_packed_header_t const *header = (nk_dots_amx_packed_header_t const *)b_packed;
988
+ nk_size_t const column_tiles_count = header->full_column_tiles;
989
+ nk_size_t const depth_tiles_count = header->full_depth_tiles;
990
+ nk_size_t const column_remainder_count = header->column_remainder_count;
991
+
992
+ // Packed B data regions
993
+ nk_bf16_t const *b_tiles_base = (nk_bf16_t const *)((char const *)b_packed + sizeof(nk_dots_amx_packed_header_t));
994
+ nk_bf16_t const *col_edge_ptr = (nk_bf16_t const *)((char const *)b_packed + header->column_edge_offset);
995
+
996
+ // Stride conversions
997
+ nk_size_t const a_stride_elements = a_stride_bytes / sizeof(nk_bf16_t);
998
+ nk_size_t const c_stride_elements = c_stride_bytes / sizeof(nk_f32_t);
999
+
1000
+ // Tile dimensions
1001
+ nk_size_t const tile_depth = 32; // depth elements per BF16 tile
1002
+ nk_size_t const tile_size = 512; // elements per packed tile
1003
+ nk_size_t const full_cols = column_tiles_count * 16;
1004
+
1005
+ // Block counts (32 × 32 output blocks = 2 × 2 tiles)
1006
+ nk_size_t const row_blocks_count = nk_size_divide_round_up_(rows_count, 32);
1007
+ nk_size_t const col_blocks_count = column_tiles_count / 2;
1008
+
1009
+ if (depth_tiles_count == 0) return;
1010
+
1011
+ // Tile buffers for A (only used for edge tiles)
1012
+ nk_dots_bf16_a16x32_sapphireamx_t a_tile_top, a_tile_bottom;
1013
+ nk_dots_bf16_state2x2_sapphireamx_t c_accum_buffer;
1014
+
1015
+ // Precompute: number of full depth-tiles (no masking needed)
1016
+ nk_size_t const full_depth_tiles_count = depth / tile_depth;
1017
+ nk_size_t const depth_remainder = depth % tile_depth;
1018
+
1019
+ nk_amx_tile_configure_sapphireamx_();
1020
+
1021
+ // Loop order: row_blocks outer, col_blocks inner - maximizes A tile L2 cache reuse
1022
+ // A tiles stay in L2 while we sweep through all col_blocks for a given row_block
1023
+ for (nk_size_t row_block_idx = 0; row_block_idx < row_blocks_count; row_block_idx++) {
1024
+ nk_size_t const row_block_start = row_block_idx * 32;
1025
+ nk_size_t const valid_rows_count = (row_block_start + 32 <= rows_count) ? 32 : (rows_count - row_block_start);
1026
+ nk_size_t const is_full_row_block = (valid_rows_count == 32);
1027
+
1028
+ for (nk_size_t column_block_idx = 0; column_block_idx < col_blocks_count; column_block_idx++) {
1029
+ nk_size_t const col_block_start = column_block_idx * 32;
1030
+ nk_size_t const b_column_left_base = (column_block_idx * 2) * depth_tiles_count;
1031
+ nk_size_t const b_column_right_base = (column_block_idx * 2 + 1) * depth_tiles_count;
1032
+
1033
+ // Zero accumulators (TMM4-7 stay resident across entire depth loop)
1034
+ _tile_zero(4);
1035
+ _tile_zero(5);
1036
+ _tile_zero(6);
1037
+ _tile_zero(7);
1038
+
1039
+ // Fast path: full row-block with full depth-tiles → direct A load with 2-deep pipelining
1040
+ if (is_full_row_block && full_depth_tiles_count > 0) {
1041
+ nk_bf16_t const *a_top_base = a + row_block_start * a_stride_elements;
1042
+ nk_bf16_t const *a_bottom_base = a + (row_block_start + 16) * a_stride_elements;
1043
+
1044
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_left =
1045
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base + b_column_left_base * tile_size);
1046
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_right =
1047
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base + b_column_right_base * tile_size);
1048
+
1049
+ // Prologue: load first depth tile
1050
+ _tile_loadd(0, a_top_base, a_stride_bytes);
1051
+ _tile_loadd(1, a_bottom_base, a_stride_bytes);
1052
+ _tile_loadd(2, b_tile_left->data, 64);
1053
+ _tile_loadd(3, b_tile_right->data, 64);
1054
+
1055
+ // Main loop: 2-deep software pipelining
1056
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < full_depth_tiles_count - 1; depth_tile_idx++) {
1057
+ nk_size_t const next_depth_offset = (depth_tile_idx + 1) * tile_depth;
1058
+
1059
+ _tile_dpbf16ps(4, 0, 2);
1060
+ _tile_dpbf16ps(5, 0, 3);
1061
+ _tile_dpbf16ps(6, 1, 2);
1062
+ _tile_dpbf16ps(7, 1, 3);
1063
+
1064
+ _tile_loadd(0, a_top_base + next_depth_offset, a_stride_bytes);
1065
+ _tile_loadd(1, a_bottom_base + next_depth_offset, a_stride_bytes);
1066
+ b_tile_left = (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base + (b_column_left_base +
1067
+ depth_tile_idx + 1) *
1068
+ tile_size);
1069
+ b_tile_right = (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base + (b_column_right_base +
1070
+ depth_tile_idx + 1) *
1071
+ tile_size);
1072
+ _tile_loadd(2, b_tile_left->data, 64);
1073
+ _tile_loadd(3, b_tile_right->data, 64);
1074
+ }
1075
+
1076
+ // Epilogue: final depth tile
1077
+ _tile_dpbf16ps(4, 0, 2);
1078
+ _tile_dpbf16ps(5, 0, 3);
1079
+ _tile_dpbf16ps(6, 1, 2);
1080
+ _tile_dpbf16ps(7, 1, 3);
1081
+
1082
+ // Handle partial depth-tile (if any)
1083
+ if (depth_remainder > 0) {
1084
+ nk_size_t const depth_offset = full_depth_tiles_count * tile_depth;
1085
+
1086
+ nk_dots_bf16_load_a_sapphireamx_(&a_tile_top, a_top_base + depth_offset, a_stride_elements, 16,
1087
+ depth_remainder);
1088
+ nk_dots_bf16_load_a_sapphireamx_(&a_tile_bottom, a_bottom_base + depth_offset, a_stride_elements,
1089
+ 16, depth_remainder);
1090
+
1091
+ b_tile_left = (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base + (b_column_left_base +
1092
+ full_depth_tiles_count) *
1093
+ tile_size);
1094
+ b_tile_right = (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base + (b_column_right_base +
1095
+ full_depth_tiles_count) *
1096
+ tile_size);
1097
+
1098
+ _tile_loadd(0, a_tile_top.data, 64);
1099
+ _tile_loadd(1, a_tile_bottom.data, 64);
1100
+ _tile_loadd(2, b_tile_left->data, 64);
1101
+ _tile_loadd(3, b_tile_right->data, 64);
1102
+
1103
+ _tile_dpbf16ps(4, 0, 2);
1104
+ _tile_dpbf16ps(5, 0, 3);
1105
+ _tile_dpbf16ps(6, 1, 2);
1106
+ _tile_dpbf16ps(7, 1, 3);
1107
+ }
1108
+ }
1109
+ // Full row-block but only partial depth tile (depth < tile_depth)
1110
+ else if (is_full_row_block) {
1111
+ nk_bf16_t const *a_top_base = a + row_block_start * a_stride_elements;
1112
+ nk_bf16_t const *a_bottom_base = a + (row_block_start + 16) * a_stride_elements;
1113
+
1114
+ nk_dots_bf16_load_a_sapphireamx_(&a_tile_top, a_top_base, a_stride_elements, 16, depth_remainder);
1115
+ nk_dots_bf16_load_a_sapphireamx_(&a_tile_bottom, a_bottom_base, a_stride_elements, 16, depth_remainder);
1116
+
1117
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_left =
1118
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base + b_column_left_base * tile_size);
1119
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_right =
1120
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base + b_column_right_base * tile_size);
1121
+
1122
+ _tile_loadd(0, a_tile_top.data, 64);
1123
+ _tile_loadd(1, a_tile_bottom.data, 64);
1124
+ _tile_loadd(2, b_tile_left->data, 64);
1125
+ _tile_loadd(3, b_tile_right->data, 64);
1126
+
1127
+ _tile_dpbf16ps(4, 0, 2);
1128
+ _tile_dpbf16ps(5, 0, 3);
1129
+ _tile_dpbf16ps(6, 1, 2);
1130
+ _tile_dpbf16ps(7, 1, 3);
1131
+ }
1132
+ // Slow path: edge row-block → buffered load with masking
1133
+ else {
1134
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
1135
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
1136
+
1137
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
1138
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
1139
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth
1140
+ : depth_remainder;
1141
+
1142
+ nk_dots_bf16_load_a_sapphireamx_(&a_tile_top,
1143
+ a + row_block_start * a_stride_elements + depth_offset,
1144
+ a_stride_elements, rows_in_high_tile, valid_depth);
1145
+ if (rows_in_low_tile > 0) {
1146
+ nk_dots_bf16_load_a_sapphireamx_(&a_tile_bottom,
1147
+ a + (row_block_start + 16) * a_stride_elements + depth_offset,
1148
+ a_stride_elements, rows_in_low_tile, valid_depth);
1149
+ }
1150
+
1151
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_left =
1152
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base +
1153
+ (b_column_left_base + depth_tile_idx) * tile_size);
1154
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_right =
1155
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base +
1156
+ (b_column_right_base + depth_tile_idx) * tile_size);
1157
+
1158
+ _tile_loadd(0, a_tile_top.data, 64);
1159
+ _tile_loadd(1, a_tile_bottom.data, 64);
1160
+ _tile_loadd(2, b_tile_left->data, 64);
1161
+ _tile_loadd(3, b_tile_right->data, 64);
1162
+
1163
+ _tile_dpbf16ps(4, 0, 2);
1164
+ _tile_dpbf16ps(5, 0, 3);
1165
+ _tile_dpbf16ps(6, 1, 2);
1166
+ _tile_dpbf16ps(7, 1, 3);
1167
+ }
1168
+ }
1169
+
1170
+ // Store accumulators to output (once per output block)
1171
+ if (is_full_row_block) {
1172
+ nk_f32_t *c_block = c + row_block_start * c_stride_elements + col_block_start;
1173
+ _tile_stored(4, c_block, c_stride_bytes);
1174
+ _tile_stored(5, c_block + 16, c_stride_bytes);
1175
+ _tile_stored(6, (nk_f32_t *)((char *)c_block + 16 * c_stride_bytes), c_stride_bytes);
1176
+ _tile_stored(7, (nk_f32_t *)((char *)c_block + 16 * c_stride_bytes) + 16, c_stride_bytes);
1177
+ }
1178
+ else {
1179
+ _tile_stored(4, c_accum_buffer.c[0][0].data, 64);
1180
+ _tile_stored(5, c_accum_buffer.c[0][1].data, 64);
1181
+ _tile_stored(6, c_accum_buffer.c[1][0].data, 64);
1182
+ _tile_stored(7, c_accum_buffer.c[1][1].data, 64);
1183
+ nk_dots_bf16_output2x2_sapphireamx_(&c_accum_buffer,
1184
+ c + row_block_start * c_stride_elements + col_block_start,
1185
+ c_stride_elements, valid_rows_count, 32);
1186
+ }
1187
+ }
1188
+ }
1189
+
1190
+ // Handle odd column-tile (single 16-column tile if column_tiles_count is odd)
1191
+ if (column_tiles_count % 2 == 1) {
1192
+ nk_size_t const column_tile_idx = column_tiles_count - 1;
1193
+ nk_size_t const col_start = column_tile_idx * 16;
1194
+ nk_size_t const b_column_base = column_tile_idx * depth_tiles_count;
1195
+
1196
+ for (nk_size_t row_block_idx = 0; row_block_idx < row_blocks_count; row_block_idx++) {
1197
+ nk_size_t const row_block_start = row_block_idx * 32;
1198
+ nk_size_t const valid_rows_count = (row_block_start + 32 <= rows_count) ? 32
1199
+ : (rows_count - row_block_start);
1200
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
1201
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
1202
+
1203
+ nk_dots_bf16_state_sapphireamx_t c_high_state, c_low_state;
1204
+
1205
+ _tile_zero(4);
1206
+ _tile_zero(6);
1207
+
1208
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
1209
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
1210
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
1211
+
1212
+ nk_dots_bf16_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_elements + depth_offset,
1213
+ a_stride_elements, rows_in_high_tile, valid_depth);
1214
+ if (rows_in_low_tile > 0) {
1215
+ nk_dots_bf16_load_a_sapphireamx_(&a_tile_bottom,
1216
+ a + (row_block_start + 16) * a_stride_elements + depth_offset,
1217
+ a_stride_elements, rows_in_low_tile, valid_depth);
1218
+ }
1219
+
1220
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile =
1221
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base +
1222
+ (b_column_base + depth_tile_idx) * tile_size);
1223
+
1224
+ _tile_loadd(0, a_tile_top.data, 64);
1225
+ _tile_loadd(1, a_tile_bottom.data, 64);
1226
+ _tile_loadd(2, b_tile->data, 64);
1227
+
1228
+ _tile_dpbf16ps(4, 0, 2);
1229
+ _tile_dpbf16ps(6, 1, 2);
1230
+ }
1231
+
1232
+ _tile_stored(4, c_high_state.data, 64);
1233
+ _tile_stored(6, c_low_state.data, 64);
1234
+
1235
+ nk_dots_bf16_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + col_start,
1236
+ c_stride_elements, rows_in_high_tile, 16);
1237
+ if (rows_in_low_tile > 0) {
1238
+ nk_dots_bf16_store_sapphireamx_(&c_low_state,
1239
+ c + (row_block_start + 16) * c_stride_elements + col_start,
1240
+ c_stride_elements, rows_in_low_tile, 16);
1241
+ }
1242
+ }
1243
+ }
1244
+
1245
+ // Handle column-edge (remaining columns < 16) using AMX with partial tiles
1246
+ if (column_remainder_count > 0) {
1247
+ for (nk_size_t row_block_idx = 0; row_block_idx < row_blocks_count; row_block_idx++) {
1248
+ nk_size_t const row_block_start = row_block_idx * 32;
1249
+ nk_size_t const valid_rows_count = (row_block_start + 32 <= rows_count) ? 32
1250
+ : (rows_count - row_block_start);
1251
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
1252
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
1253
+
1254
+ nk_dots_bf16_state_sapphireamx_t c_high_state, c_low_state;
1255
+ nk_dots_bf16_a16x32_sapphireamx_t b_as_a;
1256
+ nk_dots_bf16_b32x16_sapphireamx_t b_tile;
1257
+
1258
+ _tile_zero(4);
1259
+ _tile_zero(6);
1260
+
1261
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
1262
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
1263
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
1264
+
1265
+ nk_dots_bf16_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_elements + depth_offset,
1266
+ a_stride_elements, rows_in_high_tile, valid_depth);
1267
+ if (rows_in_low_tile > 0) {
1268
+ nk_dots_bf16_load_a_sapphireamx_(&a_tile_bottom,
1269
+ a + (row_block_start + 16) * a_stride_elements + depth_offset,
1270
+ a_stride_elements, rows_in_low_tile, valid_depth);
1271
+ }
1272
+
1273
+ nk_dots_bf16_load_a_sapphireamx_(&b_as_a, col_edge_ptr + depth_offset, depth, column_remainder_count,
1274
+ valid_depth);
1275
+ nk_dots_pack_bf16_transposed_sapphireamx_(&b_as_a, &b_tile);
1276
+
1277
+ _tile_loadd(0, a_tile_top.data, 64);
1278
+ _tile_loadd(1, a_tile_bottom.data, 64);
1279
+ _tile_loadd(2, b_tile.data, 64);
1280
+
1281
+ _tile_dpbf16ps(4, 0, 2);
1282
+ _tile_dpbf16ps(6, 1, 2);
1283
+ }
1284
+
1285
+ _tile_stored(4, c_high_state.data, 64);
1286
+ _tile_stored(6, c_low_state.data, 64);
1287
+
1288
+ nk_dots_bf16_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + full_cols,
1289
+ c_stride_elements, rows_in_high_tile, column_remainder_count);
1290
+ if (rows_in_low_tile > 0) {
1291
+ nk_dots_bf16_store_sapphireamx_(&c_low_state,
1292
+ c + (row_block_start + 16) * c_stride_elements + full_cols,
1293
+ c_stride_elements, rows_in_low_tile, column_remainder_count);
1294
+ }
1295
+ }
1296
+ }
1297
+
1298
+ _tile_release();
1299
+ }
1300
+
1301
+ NK_PUBLIC void nk_dots_compact_bf16_sapphireamx( //
1302
+ void *c, nk_size_t row_count, nk_size_t column_count, nk_size_t c_stride_in_bytes) {
1303
+
1304
+ nk_size_t const c_stride_f32 = c_stride_in_bytes / sizeof(nk_f32_t);
1305
+ nk_f32_t const *c_f32 = (nk_f32_t const *)c;
1306
+ nk_bf16_t *c_bf16 = (nk_bf16_t *)c;
1307
+
1308
+ for (nk_size_t row_idx = 0; row_idx < row_count; row_idx++) {
1309
+ nk_f32_t const *src_row = c_f32 + row_idx * c_stride_f32;
1310
+ nk_bf16_t *dst_row = c_bf16 + row_idx * column_count;
1311
+ nk_size_t column_idx = 0;
1312
+
1313
+ // Process 16 floats at a time using AVX512-BF16
1314
+ for (; column_idx + 16 <= column_count; column_idx += 16) {
1315
+ __m512 f32_vec = _mm512_loadu_ps(src_row + column_idx);
1316
+ __m256bh bf16_vec = _mm512_cvtneps_pbh(f32_vec);
1317
+ _mm256_storeu_si256((__m256i *)(dst_row + column_idx), nk_m256i_from_m256bh_(bf16_vec));
1318
+ }
1319
+
1320
+ // Handle remaining elements with masked operations
1321
+ if (column_idx < column_count) {
1322
+ __mmask16 tail_mask = (__mmask16)((1u << (column_count - column_idx)) - 1);
1323
+ __m512 f32_vec = _mm512_maskz_loadu_ps(tail_mask, src_row + column_idx);
1324
+ __m256bh bf16_vec = _mm512_cvtneps_pbh(f32_vec);
1325
+ _mm256_mask_storeu_epi16(dst_row + column_idx, tail_mask, nk_m256i_from_m256bh_(bf16_vec));
1326
+ }
1327
+ }
1328
+ }
1329
+
1330
+ NK_PUBLIC void nk_dots_symmetric_bf16_sapphireamx( //
1331
+ nk_bf16_t const *vectors, nk_size_t vectors_count, nk_size_t depth, //
1332
+ nk_size_t stride_in_bytes, nk_f32_t *result, nk_size_t result_stride_in_bytes, //
1333
+ nk_size_t row_start, nk_size_t row_count) {
1334
+
1335
+ nk_size_t const stride_elements = stride_in_bytes / sizeof(nk_bf16_t);
1336
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f32_t);
1337
+
1338
+ // Handle row slicing: compute rows [row_start, row_end)
1339
+ nk_size_t const row_end = (row_count == 0)
1340
+ ? vectors_count
1341
+ : (row_start + row_count < vectors_count ? row_start + row_count : vectors_count);
1342
+
1343
+ // Round depth up to multiple of 96 (3 tiles × 32 elements)
1344
+ nk_size_t const depth_tiles = nk_size_divide_round_up_(depth, 32);
1345
+ nk_size_t const depth_tile_groups = nk_size_divide_round_up_(depth_tiles, 3);
1346
+
1347
+ nk_dots_bf16_a16x32_sapphireamx_t a_tiles[3];
1348
+ nk_dots_bf16_a16x32_sapphireamx_t b_src_tiles[3];
1349
+ nk_dots_bf16_b32x16_sapphireamx_t b_tiles[3];
1350
+ nk_dots_bf16_state_sapphireamx_t state;
1351
+
1352
+ nk_amx_tile_configure_sapphireamx_();
1353
+
1354
+ for (nk_size_t row_tile = row_start; row_tile < row_end; row_tile += 16) {
1355
+ nk_size_t const valid_rows = (row_tile + 16 <= row_end) ? 16 : (row_end - row_tile);
1356
+
1357
+ for (nk_size_t col_tile = 0; col_tile < vectors_count; col_tile += 16) {
1358
+ nk_size_t const valid_cols = (col_tile + 16 <= vectors_count) ? 16 : (vectors_count - col_tile);
1359
+
1360
+ nk_dots_bf16_init_sapphireamx_(&state);
1361
+
1362
+ for (nk_size_t depth_group_idx = 0; depth_group_idx < depth_tile_groups; depth_group_idx++) {
1363
+ nk_size_t const depth_base = depth_group_idx * 96;
1364
+
1365
+ for (int tile_idx = 0; tile_idx < 3; tile_idx++) {
1366
+ nk_size_t const depth_start = depth_base + tile_idx * 32;
1367
+ nk_size_t const valid_depth = (depth_start + 32 <= depth)
1368
+ ? 32
1369
+ : (depth > depth_start ? depth - depth_start : 0);
1370
+
1371
+ nk_dots_bf16_load_a_sapphireamx_( //
1372
+ &a_tiles[tile_idx], //
1373
+ vectors + row_tile * stride_elements + depth_start, //
1374
+ stride_elements, valid_rows, valid_depth);
1375
+
1376
+ if (row_tile == col_tile) {
1377
+ nk_dots_pack_bf16_transposed_sapphireamx_(&a_tiles[tile_idx], &b_tiles[tile_idx]);
1378
+ }
1379
+ else {
1380
+ nk_dots_bf16_load_a_sapphireamx_( //
1381
+ &b_src_tiles[tile_idx], //
1382
+ vectors + col_tile * stride_elements + depth_start, //
1383
+ stride_elements, valid_cols, valid_depth);
1384
+ nk_dots_pack_bf16_transposed_sapphireamx_(&b_src_tiles[tile_idx], &b_tiles[tile_idx]);
1385
+ }
1386
+ }
1387
+
1388
+ nk_dots_bf16_update_sapphireamx_( //
1389
+ &state, &a_tiles[0], &a_tiles[1], &a_tiles[2], &b_tiles[0], &b_tiles[1], &b_tiles[2]);
1390
+ }
1391
+
1392
+ nk_dots_bf16_store_sapphireamx_( //
1393
+ &state, result + row_tile * result_stride_elements + col_tile, //
1394
+ result_stride_elements, valid_rows, valid_cols);
1395
+ }
1396
+ }
1397
+ }
1398
+
1399
+ #pragma endregion F16 Floats
1400
+
1401
+ #pragma region Signed Integers
1402
+
1403
+ NK_PUBLIC nk_size_t nk_dots_packed_size_i8_sapphireamx(nk_size_t column_count, nk_size_t depth) {
1404
+ nk_size_t const tmm_rows = 16;
1405
+ nk_size_t const tmm_cols = 64;
1406
+ nk_size_t const tile_bytes = 1024 * sizeof(nk_i8_t); // 16 × 64×1 = 1KB
1407
+
1408
+ nk_size_t const full_column_tiles = column_count / tmm_rows;
1409
+ nk_size_t const tiles_along_depth = nk_size_divide_round_up_(depth, tmm_cols);
1410
+ nk_size_t const column_remainder_count = column_count - full_column_tiles * tmm_rows;
1411
+
1412
+ // Header (64 bytes aligned)
1413
+ nk_size_t size = sizeof(nk_dots_amx_packed_header_t);
1414
+
1415
+ // All tiles for full column rows (Morton-ordered, quad-interleaved, depth remainder zero-padded)
1416
+ size += full_column_tiles * tiles_along_depth * tile_bytes;
1417
+
1418
+ // Column edge: remaining rows for ALL depth columns, stored row-major
1419
+ if (column_remainder_count > 0) size += column_remainder_count * depth * sizeof(nk_i8_t);
1420
+
1421
+ // Per-column norms for angular/euclidean distance (4 bytes each: f32 or u32)
1422
+ size += column_count * sizeof(nk_u32_t);
1423
+
1424
+ return size;
1425
+ }
1426
+
1427
+ NK_PUBLIC void nk_dots_pack_i8_sapphireamx( //
1428
+ nk_i8_t const *b, nk_size_t column_count, nk_size_t depth, //
1429
+ nk_size_t b_stride_in_bytes, void *b_packed) {
1430
+
1431
+ // AMX I8 tile dimensions: 16 rows × 64 columns (1024 I8 elements = 1KB)
1432
+ nk_size_t const tmm_rows = 16;
1433
+ nk_size_t const tmm_cols = 64;
1434
+ nk_size_t const tile_elements = 1024;
1435
+ nk_size_t const tile_bytes = tile_elements * sizeof(nk_i8_t);
1436
+
1437
+ // Compute layout dimensions
1438
+ nk_size_t const column_tiles_count = column_count / tmm_rows;
1439
+ nk_size_t const depth_tiles_count = nk_size_divide_round_up_(depth, tmm_cols);
1440
+ nk_size_t const column_remainder_count = column_count - column_tiles_count * tmm_rows;
1441
+ nk_size_t const total_tiles = column_tiles_count * depth_tiles_count;
1442
+
1443
+ // Write header with layout metadata
1444
+ nk_dots_amx_packed_header_t *header = (nk_dots_amx_packed_header_t *)b_packed;
1445
+ header->full_column_tiles = (nk_u32_t)column_tiles_count;
1446
+ header->full_depth_tiles = (nk_u32_t)depth_tiles_count;
1447
+ header->column_remainder_count = (nk_u32_t)column_remainder_count;
1448
+
1449
+ // Compute memory region offsets
1450
+ nk_size_t const tiles_offset = sizeof(nk_dots_amx_packed_header_t);
1451
+ nk_size_t const column_edge_offset = tiles_offset + total_tiles * tile_bytes;
1452
+ header->column_edge_offset = (nk_u32_t)column_edge_offset;
1453
+
1454
+ // Pointers to packed data regions
1455
+ nk_i8_t *tiles_ptr = (nk_i8_t *)((char *)b_packed + tiles_offset);
1456
+ nk_i8_t *column_edge_ptr = (nk_i8_t *)((char *)b_packed + column_edge_offset);
1457
+
1458
+ // Pack tiles using vectorized transposer: gather 16 strided rows into an aligned
1459
+ // temporary, transpose via SIMD, then copy the result to the packed buffer.
1460
+ // Stack-local aligned tiles are needed because the packed buffer may not be 64-byte aligned.
1461
+ for (nk_size_t column_tile_idx = 0; column_tile_idx < column_tiles_count; column_tile_idx++) {
1462
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
1463
+
1464
+ nk_size_t const tile_index = column_tile_idx * depth_tiles_count + depth_tile_idx;
1465
+ nk_i8_t *tile_output = tiles_ptr + tile_index * tile_elements;
1466
+
1467
+ nk_size_t const src_row_start = column_tile_idx * tmm_rows;
1468
+ nk_size_t const src_column_start = depth_tile_idx * tmm_cols;
1469
+ nk_size_t const columns_to_pack = (src_column_start + tmm_cols <= depth) ? tmm_cols
1470
+ : (depth - src_column_start);
1471
+
1472
+ // Gather 16 strided source rows into a contiguous aligned tile
1473
+ nk_dots_i8_a16x64_sapphireamx_t source_tile;
1474
+ if (columns_to_pack == tmm_cols) {
1475
+ for (nk_size_t row_idx = 0; row_idx < tmm_rows; row_idx++) {
1476
+ nk_i8_t const *source_row = (nk_i8_t const *)((char const *)b +
1477
+ (src_row_start + row_idx) * b_stride_in_bytes) +
1478
+ src_column_start;
1479
+ _mm512_store_si512(&source_tile.data[row_idx][0], _mm512_loadu_si512(source_row));
1480
+ }
1481
+ }
1482
+ else {
1483
+ __mmask64 depth_mask = (__mmask64)((columns_to_pack < 64) ? ((1ULL << columns_to_pack) - 1) : ~0ULL);
1484
+ for (nk_size_t row_idx = 0; row_idx < tmm_rows; row_idx++) {
1485
+ nk_i8_t const *source_row = (nk_i8_t const *)((char const *)b +
1486
+ (src_row_start + row_idx) * b_stride_in_bytes) +
1487
+ src_column_start;
1488
+ _mm512_store_si512(&source_tile.data[row_idx][0], _mm512_maskz_loadu_epi8(depth_mask, source_row));
1489
+ }
1490
+ }
1491
+
1492
+ // Transpose into aligned local, then copy to (potentially unaligned) packed buffer
1493
+ nk_dots_i8_b64x16_sapphireamx_t transposed_tile;
1494
+ nk_dots_pack_i8_transposed_sapphireamx_(&source_tile, &transposed_tile);
1495
+ for (nk_size_t i = 0; i < tile_elements; i += 64)
1496
+ _mm512_storeu_si512(tile_output + i, _mm512_load_si512((char const *)&transposed_tile + i));
1497
+ }
1498
+ }
1499
+
1500
+ // Pack column-remainder rows in simple row-major format (for AVX-512 fallback)
1501
+ if (column_remainder_count > 0) {
1502
+ nk_size_t const remainder_start_row = column_tiles_count * tmm_rows;
1503
+ for (nk_size_t row_idx = 0; row_idx < column_remainder_count; row_idx++) {
1504
+ for (nk_size_t column_idx = 0; column_idx < depth; column_idx++) {
1505
+ column_edge_ptr[row_idx * depth + column_idx] =
1506
+ b[(remainder_start_row + row_idx) * b_stride_in_bytes + column_idx];
1507
+ }
1508
+ }
1509
+ }
1510
+
1511
+ // Compute and store per-column norms for angular/euclidean distance
1512
+ nk_size_t norms_offset = column_edge_offset +
1513
+ (column_remainder_count > 0 ? column_remainder_count * depth * sizeof(nk_i8_t) : 0);
1514
+ header->norms_byte_offset = (nk_u32_t)norms_offset;
1515
+ nk_u32_t *norms = (nk_u32_t *)((char *)b_packed + norms_offset);
1516
+ for (nk_size_t col = 0; col < column_count; col++)
1517
+ norms[col] = nk_dots_reduce_sumsq_i8_(b + col * b_stride_in_bytes, depth);
1518
+ }
1519
+
1520
+ NK_PUBLIC void nk_dots_packed_i8_sapphireamx( //
1521
+ nk_i8_t const *a, void const *b_packed, nk_i32_t *c, //
1522
+ nk_size_t rows_count, nk_size_t cols_count, nk_size_t depth, nk_size_t a_stride_bytes, nk_size_t c_stride_bytes) {
1523
+ nk_unused_(cols_count);
1524
+
1525
+ // Parse packed B header
1526
+ nk_dots_amx_packed_header_t const *header = (nk_dots_amx_packed_header_t const *)b_packed;
1527
+ nk_size_t const column_tiles_count = header->full_column_tiles;
1528
+ nk_size_t const depth_tiles_count = header->full_depth_tiles;
1529
+ nk_size_t const column_remainder_count = header->column_remainder_count;
1530
+
1531
+ // Packed B data regions
1532
+ nk_i8_t const *b_tiles_base = (nk_i8_t const *)((char const *)b_packed + sizeof(nk_dots_amx_packed_header_t));
1533
+ nk_i8_t const *col_edge_ptr = (nk_i8_t const *)((char const *)b_packed + header->column_edge_offset);
1534
+
1535
+ // Stride conversions
1536
+ nk_size_t const c_stride_elements = c_stride_bytes / sizeof(nk_i32_t);
1537
+
1538
+ // Tile dimensions
1539
+ nk_size_t const tile_depth = 64; // depth elements per INT8 tile
1540
+ nk_size_t const tile_size = 1024; // bytes per packed tile
1541
+ nk_size_t const full_cols = column_tiles_count * 16;
1542
+
1543
+ // Block counts (32 × 32 output blocks = 2 × 2 tiles)
1544
+ nk_size_t const row_blocks_count = nk_size_divide_round_up_(rows_count, 32);
1545
+ nk_size_t const col_blocks_count = column_tiles_count / 2;
1546
+
1547
+ if (depth_tiles_count == 0) return;
1548
+
1549
+ // Tile buffers for A (only used for edge tiles)
1550
+ nk_dots_i8_a16x64_sapphireamx_t a_tile_top, a_tile_bottom;
1551
+ nk_dots_i8_state2x2_sapphireamx_t c_accum_buffer;
1552
+
1553
+ // Precompute: number of full depth-tiles (no masking needed)
1554
+ nk_size_t const full_depth_tiles_count = depth / tile_depth;
1555
+ nk_size_t const depth_remainder = depth % tile_depth;
1556
+
1557
+ nk_amx_tile_configure_sapphireamx_();
1558
+
1559
+ // Process all 32 × 32 row × column blocks (including partial edge blocks)
1560
+ for (nk_size_t row_block_idx = 0; row_block_idx < row_blocks_count; row_block_idx++) {
1561
+ nk_size_t const row_block_start = row_block_idx * 32;
1562
+ nk_size_t const valid_rows_count = (row_block_start + 32 <= rows_count) ? 32 : (rows_count - row_block_start);
1563
+ nk_size_t const is_full_row_block = (valid_rows_count == 32);
1564
+
1565
+ // Process full column-blocks (pairs of 16-column tiles = 32 columns)
1566
+ for (nk_size_t column_block_idx = 0; column_block_idx < col_blocks_count; column_block_idx++) {
1567
+ nk_size_t const col_block_start = column_block_idx * 32;
1568
+
1569
+ // B tile base indices (linear layout: col_tile × depth_tiles_count + depth_tile)
1570
+ nk_size_t const b_column_left_base = (column_block_idx * 2) * depth_tiles_count;
1571
+ nk_size_t const b_column_right_base = (column_block_idx * 2 + 1) * depth_tiles_count;
1572
+
1573
+ // Zero accumulators (TMM4-7 stay resident across entire depth loop)
1574
+ _tile_zero(4); // C[upper, left]
1575
+ _tile_zero(5); // C[upper, right]
1576
+ _tile_zero(6); // C[lower, left]
1577
+ _tile_zero(7); // C[lower, right]
1578
+
1579
+ // Fast path: full row-block with full depth-tiles → direct A load with 2-deep pipelining
1580
+ if (is_full_row_block && full_depth_tiles_count > 0) {
1581
+ // A row pointers for direct load
1582
+ nk_i8_t const *a_top_base = a + row_block_start * a_stride_bytes;
1583
+ nk_i8_t const *a_bottom_base = a + (row_block_start + 16) * a_stride_bytes;
1584
+
1585
+ // B tile pointers
1586
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile_left =
1587
+ (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base + b_column_left_base * tile_size);
1588
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile_right =
1589
+ (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base + b_column_right_base * tile_size);
1590
+
1591
+ // Prologue: load first depth tile into TMM0-3
1592
+ _tile_loadd(0, a_top_base, a_stride_bytes);
1593
+ _tile_loadd(1, a_bottom_base, a_stride_bytes);
1594
+ _tile_loadd(2, b_tile_left->data, 64);
1595
+ _tile_loadd(3, b_tile_right->data, 64);
1596
+
1597
+ // Main loop: 2-deep software pipelining (compute current while loading next)
1598
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < full_depth_tiles_count - 1; depth_tile_idx++) {
1599
+ nk_size_t const next_depth_offset = (depth_tile_idx + 1) * tile_depth;
1600
+
1601
+ _tile_dpbssd(4, 0, 2);
1602
+ _tile_dpbssd(5, 0, 3);
1603
+ _tile_dpbssd(6, 1, 2);
1604
+ _tile_dpbssd(7, 1, 3);
1605
+
1606
+ _tile_loadd(0, a_top_base + next_depth_offset, a_stride_bytes);
1607
+ _tile_loadd(1, a_bottom_base + next_depth_offset, a_stride_bytes);
1608
+ b_tile_left = (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base +
1609
+ (b_column_left_base + depth_tile_idx + 1) *
1610
+ tile_size);
1611
+ b_tile_right = (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base + (b_column_right_base +
1612
+ depth_tile_idx + 1) *
1613
+ tile_size);
1614
+ _tile_loadd(2, b_tile_left->data, 64);
1615
+ _tile_loadd(3, b_tile_right->data, 64);
1616
+ }
1617
+
1618
+ // Epilogue: final depth tile (no next to load)
1619
+ _tile_dpbssd(4, 0, 2);
1620
+ _tile_dpbssd(5, 0, 3);
1621
+ _tile_dpbssd(6, 1, 2);
1622
+ _tile_dpbssd(7, 1, 3);
1623
+
1624
+ // Handle partial depth-tile (if any) with buffered load
1625
+ if (depth_remainder > 0) {
1626
+ nk_size_t const depth_offset = full_depth_tiles_count * tile_depth;
1627
+
1628
+ nk_dots_i8_load_a_sapphireamx_(&a_tile_top, a_top_base + depth_offset, a_stride_bytes, 16,
1629
+ depth_remainder);
1630
+ nk_dots_i8_load_a_sapphireamx_(&a_tile_bottom, a_bottom_base + depth_offset, a_stride_bytes, 16,
1631
+ depth_remainder);
1632
+
1633
+ b_tile_left = (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base + (b_column_left_base +
1634
+ full_depth_tiles_count) *
1635
+ tile_size);
1636
+ b_tile_right = (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base + (b_column_right_base +
1637
+ full_depth_tiles_count) *
1638
+ tile_size);
1639
+
1640
+ _tile_loadd(0, a_tile_top.data, 64);
1641
+ _tile_loadd(1, a_tile_bottom.data, 64);
1642
+ _tile_loadd(2, b_tile_left->data, 64);
1643
+ _tile_loadd(3, b_tile_right->data, 64);
1644
+
1645
+ _tile_dpbssd(4, 0, 2);
1646
+ _tile_dpbssd(5, 0, 3);
1647
+ _tile_dpbssd(6, 1, 2);
1648
+ _tile_dpbssd(7, 1, 3);
1649
+ }
1650
+ }
1651
+ // Full row-block but only partial depth tile (depth < tile_depth)
1652
+ else if (is_full_row_block) {
1653
+ nk_i8_t const *a_top_base = a + row_block_start * a_stride_bytes;
1654
+ nk_i8_t const *a_bottom_base = a + (row_block_start + 16) * a_stride_bytes;
1655
+
1656
+ nk_dots_i8_load_a_sapphireamx_(&a_tile_top, a_top_base, a_stride_bytes, 16, depth_remainder);
1657
+ nk_dots_i8_load_a_sapphireamx_(&a_tile_bottom, a_bottom_base, a_stride_bytes, 16, depth_remainder);
1658
+
1659
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile_left =
1660
+ (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base + b_column_left_base * tile_size);
1661
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile_right =
1662
+ (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base + b_column_right_base * tile_size);
1663
+
1664
+ _tile_loadd(0, a_tile_top.data, 64);
1665
+ _tile_loadd(1, a_tile_bottom.data, 64);
1666
+ _tile_loadd(2, b_tile_left->data, 64);
1667
+ _tile_loadd(3, b_tile_right->data, 64);
1668
+
1669
+ _tile_dpbssd(4, 0, 2);
1670
+ _tile_dpbssd(5, 0, 3);
1671
+ _tile_dpbssd(6, 1, 2);
1672
+ _tile_dpbssd(7, 1, 3);
1673
+ }
1674
+ // Slow path: edge row-block → always use buffered load with masking
1675
+ else {
1676
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
1677
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
1678
+
1679
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
1680
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
1681
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth
1682
+ : depth_remainder;
1683
+
1684
+ nk_dots_i8_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
1685
+ a_stride_bytes, rows_in_high_tile, valid_depth);
1686
+ if (rows_in_low_tile > 0) {
1687
+ nk_dots_i8_load_a_sapphireamx_(&a_tile_bottom,
1688
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
1689
+ a_stride_bytes, rows_in_low_tile, valid_depth);
1690
+ }
1691
+
1692
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile_left =
1693
+ (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base +
1694
+ (b_column_left_base + depth_tile_idx) * tile_size);
1695
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile_right =
1696
+ (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base +
1697
+ (b_column_right_base + depth_tile_idx) * tile_size);
1698
+
1699
+ _tile_loadd(0, a_tile_top.data, 64);
1700
+ _tile_loadd(1, a_tile_bottom.data, 64);
1701
+ _tile_loadd(2, b_tile_left->data, 64);
1702
+ _tile_loadd(3, b_tile_right->data, 64);
1703
+
1704
+ _tile_dpbssd(4, 0, 2);
1705
+ _tile_dpbssd(5, 0, 3);
1706
+ _tile_dpbssd(6, 1, 2);
1707
+ _tile_dpbssd(7, 1, 3);
1708
+ }
1709
+ }
1710
+
1711
+ // Store accumulators to output (once per output block, not per depth tile)
1712
+ if (is_full_row_block) {
1713
+ nk_i32_t *c_block = c + row_block_start * c_stride_elements + col_block_start;
1714
+ _tile_stored(4, c_block, c_stride_bytes);
1715
+ _tile_stored(5, c_block + 16, c_stride_bytes);
1716
+ _tile_stored(6, (nk_i32_t *)((char *)c_block + 16 * c_stride_bytes), c_stride_bytes);
1717
+ _tile_stored(7, (nk_i32_t *)((char *)c_block + 16 * c_stride_bytes) + 16, c_stride_bytes);
1718
+ }
1719
+ else {
1720
+ // Slow path: edge row-block needs masked output
1721
+ _tile_stored(4, c_accum_buffer.c[0][0].data, 64);
1722
+ _tile_stored(5, c_accum_buffer.c[0][1].data, 64);
1723
+ _tile_stored(6, c_accum_buffer.c[1][0].data, 64);
1724
+ _tile_stored(7, c_accum_buffer.c[1][1].data, 64);
1725
+ nk_dots_i8_output2x2_sapphireamx_(&c_accum_buffer,
1726
+ c + row_block_start * c_stride_elements + col_block_start,
1727
+ c_stride_elements, valid_rows_count, 32);
1728
+ }
1729
+ }
1730
+
1731
+ // Handle odd column-tile (single 16-column tile if column_tiles_count is odd)
1732
+ if (column_tiles_count % 2 == 1) {
1733
+ nk_size_t const column_tile_idx = column_tiles_count - 1;
1734
+ nk_size_t const col_start = column_tile_idx * 16;
1735
+ nk_size_t const b_column_base = column_tile_idx * depth_tiles_count;
1736
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
1737
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
1738
+
1739
+ // Use 1 × 2 blocking for single column-tile (2 row-tiles × 1 column-tile)
1740
+ nk_dots_i8_state_sapphireamx_t c_high_state, c_low_state;
1741
+
1742
+ _tile_zero(4);
1743
+ _tile_zero(6);
1744
+
1745
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
1746
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
1747
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
1748
+
1749
+ nk_dots_i8_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
1750
+ a_stride_bytes, rows_in_high_tile, valid_depth);
1751
+ if (rows_in_low_tile > 0) {
1752
+ nk_dots_i8_load_a_sapphireamx_(&a_tile_bottom,
1753
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
1754
+ a_stride_bytes, rows_in_low_tile, valid_depth);
1755
+ }
1756
+
1757
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile =
1758
+ (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base +
1759
+ (b_column_base + depth_tile_idx) * tile_size);
1760
+
1761
+ _tile_loadd(0, a_tile_top.data, 64);
1762
+ _tile_loadd(1, a_tile_bottom.data, 64);
1763
+ _tile_loadd(2, b_tile->data, 64);
1764
+
1765
+ _tile_dpbssd(4, 0, 2);
1766
+ _tile_dpbssd(6, 1, 2);
1767
+ }
1768
+
1769
+ _tile_stored(4, c_high_state.data, 64);
1770
+ _tile_stored(6, c_low_state.data, 64);
1771
+
1772
+ nk_dots_i8_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + col_start,
1773
+ c_stride_elements, rows_in_high_tile, 16);
1774
+ if (rows_in_low_tile > 0) {
1775
+ nk_dots_i8_store_sapphireamx_(&c_low_state, c + (row_block_start + 16) * c_stride_elements + col_start,
1776
+ c_stride_elements, rows_in_low_tile, 16);
1777
+ }
1778
+ }
1779
+
1780
+ // Handle column-edge (remaining columns < 16) using AMX with partial tiles
1781
+ if (column_remainder_count > 0) {
1782
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
1783
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
1784
+
1785
+ nk_dots_i8_state_sapphireamx_t c_high_state, c_low_state;
1786
+ nk_dots_i8_a16x64_sapphireamx_t b_as_a;
1787
+ nk_dots_i8_b64x16_sapphireamx_t b_tile;
1788
+
1789
+ _tile_zero(4);
1790
+ _tile_zero(6);
1791
+
1792
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
1793
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
1794
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
1795
+
1796
+ // Load A tiles
1797
+ nk_dots_i8_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
1798
+ a_stride_bytes, rows_in_high_tile, valid_depth);
1799
+ if (rows_in_low_tile > 0) {
1800
+ nk_dots_i8_load_a_sapphireamx_(&a_tile_bottom,
1801
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
1802
+ a_stride_bytes, rows_in_low_tile, valid_depth);
1803
+ }
1804
+
1805
+ // Load B edge data (row-major: b_edge[row × depth + column]) and pack into B tile
1806
+ // Each "row" in edge data corresponds to one output column
1807
+ nk_dots_i8_load_a_sapphireamx_(&b_as_a, col_edge_ptr + depth_offset, depth, column_remainder_count,
1808
+ valid_depth);
1809
+ nk_dots_pack_i8_transposed_sapphireamx_(&b_as_a, &b_tile);
1810
+
1811
+ _tile_loadd(0, a_tile_top.data, 64);
1812
+ _tile_loadd(1, a_tile_bottom.data, 64);
1813
+ _tile_loadd(2, b_tile.data, 64);
1814
+
1815
+ _tile_dpbssd(4, 0, 2);
1816
+ _tile_dpbssd(6, 1, 2);
1817
+ }
1818
+
1819
+ _tile_stored(4, c_high_state.data, 64);
1820
+ _tile_stored(6, c_low_state.data, 64);
1821
+
1822
+ nk_dots_i8_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + full_cols,
1823
+ c_stride_elements, rows_in_high_tile, column_remainder_count);
1824
+ if (rows_in_low_tile > 0) {
1825
+ nk_dots_i8_store_sapphireamx_(&c_low_state, c + (row_block_start + 16) * c_stride_elements + full_cols,
1826
+ c_stride_elements, rows_in_low_tile, column_remainder_count);
1827
+ }
1828
+ }
1829
+ }
1830
+
1831
+ _tile_release();
1832
+ }
1833
+
1834
+ NK_PUBLIC void nk_dots_compact_i8_sapphireamx( //
1835
+ void *c, nk_size_t row_count, nk_size_t column_count, nk_size_t c_stride_in_bytes, nk_i32_t const *a_squared_norms,
1836
+ nk_i32_t const *b_squared_norms) {
1837
+
1838
+ nk_size_t const c_stride_i32 = c_stride_in_bytes / sizeof(nk_i32_t);
1839
+ nk_i32_t const *c_i32 = (nk_i32_t const *)c;
1840
+ nk_i8_t *c_i8 = (nk_i8_t *)c;
1841
+
1842
+ // Use space after I8 output for precomputed b_rsqrt (I8 output is 4x smaller than I32 input)
1843
+ nk_f32_t *b_rsqrt = (nk_f32_t *)(c_i8 + row_count * column_count);
1844
+
1845
+ // Precompute rsqrt of all b_norms using AVX512 (16 at a time)
1846
+ __m512 half_vec_f32x16 = _mm512_set1_ps(0.5f);
1847
+ __m512 three_halves_vec_f32x16 = _mm512_set1_ps(1.5f);
1848
+ nk_size_t column_idx = 0;
1849
+
1850
+ for (; column_idx + 16 <= column_count; column_idx += 16) {
1851
+ __m512i b_norms_i32x16 = _mm512_loadu_si512(b_squared_norms + column_idx);
1852
+ __m512 b_norms_f32x16 = _mm512_cvtepi32_ps(b_norms_i32x16);
1853
+ __m512 rsqrt_vec_f32x16 = _mm512_rsqrt14_ps(b_norms_f32x16);
1854
+ // Newton-Raphson refinement
1855
+ rsqrt_vec_f32x16 = _mm512_mul_ps(
1856
+ rsqrt_vec_f32x16,
1857
+ _mm512_sub_ps(
1858
+ three_halves_vec_f32x16,
1859
+ _mm512_mul_ps(half_vec_f32x16,
1860
+ _mm512_mul_ps(b_norms_f32x16, _mm512_mul_ps(rsqrt_vec_f32x16, rsqrt_vec_f32x16)))));
1861
+ // Zero out rsqrt where norm was zero
1862
+ __mmask16 nonzero_mask = _mm512_cmpneq_epi32_mask(b_norms_i32x16, _mm512_setzero_si512());
1863
+ rsqrt_vec_f32x16 = _mm512_maskz_mov_ps(nonzero_mask, rsqrt_vec_f32x16);
1864
+ _mm512_storeu_ps(b_rsqrt + column_idx, rsqrt_vec_f32x16);
1865
+ }
1866
+
1867
+ // Handle remaining b_norms with masked operations
1868
+ if (column_idx < column_count) {
1869
+ __mmask16 tail_mask = (__mmask16)((1u << (column_count - column_idx)) - 1);
1870
+ __m512i b_norms_i32x16 = _mm512_maskz_loadu_epi32(tail_mask, b_squared_norms + column_idx);
1871
+ __m512 b_norms_f32x16 = _mm512_cvtepi32_ps(b_norms_i32x16);
1872
+ __m512 rsqrt_vec_f32x16 = _mm512_rsqrt14_ps(b_norms_f32x16);
1873
+ rsqrt_vec_f32x16 = _mm512_mul_ps(
1874
+ rsqrt_vec_f32x16,
1875
+ _mm512_sub_ps(
1876
+ three_halves_vec_f32x16,
1877
+ _mm512_mul_ps(half_vec_f32x16,
1878
+ _mm512_mul_ps(b_norms_f32x16, _mm512_mul_ps(rsqrt_vec_f32x16, rsqrt_vec_f32x16)))));
1879
+ __mmask16 nonzero_mask = _mm512_cmpneq_epi32_mask(b_norms_i32x16, _mm512_setzero_si512());
1880
+ rsqrt_vec_f32x16 = _mm512_maskz_mov_ps(nonzero_mask & tail_mask, rsqrt_vec_f32x16);
1881
+ _mm512_mask_storeu_ps(b_rsqrt + column_idx, tail_mask, rsqrt_vec_f32x16);
1882
+ }
1883
+
1884
+ __m512 scale_vec_f32x16 = _mm512_set1_ps(127.0f);
1885
+
1886
+ for (nk_size_t row_idx = 0; row_idx < row_count; row_idx++) {
1887
+ nk_i32_t const *src_row = c_i32 + row_idx * c_stride_i32;
1888
+ nk_i8_t *dst_row = c_i8 + row_idx * column_count;
1889
+
1890
+ // Compute rsqrt of a_norm for this row, broadcast to vector
1891
+ nk_f32_t a_norm_f32 = (nk_f32_t)a_squared_norms[row_idx];
1892
+ nk_f32_t a_rsqrt_val = 0.0f;
1893
+ if (a_norm_f32 > 0.0f) {
1894
+ __m128 a_vec_f32x4 = _mm_set_ss(a_norm_f32);
1895
+ __m128 rsqrt_s_f32x4 = _mm_rsqrt_ss(a_vec_f32x4);
1896
+ rsqrt_s_f32x4 = _mm_mul_ss(
1897
+ rsqrt_s_f32x4,
1898
+ _mm_sub_ss(
1899
+ _mm_set_ss(1.5f),
1900
+ _mm_mul_ss(_mm_set_ss(0.5f), _mm_mul_ss(a_vec_f32x4, _mm_mul_ss(rsqrt_s_f32x4, rsqrt_s_f32x4)))));
1901
+ a_rsqrt_val = _mm_cvtss_f32(rsqrt_s_f32x4);
1902
+ }
1903
+ __m512 a_rsqrt_vec_f32x16 = _mm512_set1_ps(a_rsqrt_val);
1904
+ __m512 row_scale_f32x16 = _mm512_mul_ps(a_rsqrt_vec_f32x16, scale_vec_f32x16);
1905
+
1906
+ column_idx = 0;
1907
+
1908
+ // Process 16 elements at a time
1909
+ for (; column_idx + 16 <= column_count; column_idx += 16) {
1910
+ __m512i c_vals_i32x16 = _mm512_loadu_si512(src_row + column_idx);
1911
+ __m512 c_f32_f32x16 = _mm512_cvtepi32_ps(c_vals_i32x16);
1912
+ __m512 b_rsqrt_vec_f32x16 = _mm512_loadu_ps(b_rsqrt + column_idx);
1913
+ __m512 normalized_f32x16 = _mm512_mul_ps(_mm512_mul_ps(c_f32_f32x16, row_scale_f32x16), b_rsqrt_vec_f32x16);
1914
+ __m512i result_i32x16 = _mm512_cvtps_epi32(normalized_f32x16);
1915
+ // Saturating pack I32 → I8 (16 values → 16 bytes in low 128 bits)
1916
+ __m128i result_i8x16 = _mm512_cvtsepi32_epi8(result_i32x16);
1917
+ _mm_storeu_si128((__m128i *)(dst_row + column_idx), result_i8x16);
1918
+ }
1919
+
1920
+ // Handle remaining elements with masked operations
1921
+ if (column_idx < column_count) {
1922
+ __mmask16 tail_mask = (__mmask16)((1u << (column_count - column_idx)) - 1);
1923
+ __m512i c_vals_i32x16 = _mm512_maskz_loadu_epi32(tail_mask, src_row + column_idx);
1924
+ __m512 c_f32_f32x16 = _mm512_cvtepi32_ps(c_vals_i32x16);
1925
+ __m512 b_rsqrt_vec_f32x16 = _mm512_maskz_loadu_ps(tail_mask, b_rsqrt + column_idx);
1926
+ __m512 normalized_f32x16 = _mm512_mul_ps(_mm512_mul_ps(c_f32_f32x16, row_scale_f32x16), b_rsqrt_vec_f32x16);
1927
+ __m512i result_i32x16 = _mm512_cvtps_epi32(normalized_f32x16);
1928
+ __m128i result_i8x16 = _mm512_cvtsepi32_epi8(result_i32x16);
1929
+ _mm_mask_storeu_epi8(dst_row + column_idx, tail_mask, result_i8x16);
1930
+ }
1931
+ }
1932
+ }
1933
+
1934
+ NK_PUBLIC void nk_dots_symmetric_i8_sapphireamx( //
1935
+ nk_i8_t const *vectors, nk_size_t vectors_count, nk_size_t depth, //
1936
+ nk_size_t stride_in_bytes, nk_i32_t *result, nk_size_t result_stride_in_bytes, //
1937
+ nk_size_t row_start, nk_size_t row_count) {
1938
+
1939
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_i32_t);
1940
+
1941
+ // Handle row slicing: compute rows [row_start, row_end)
1942
+ nk_size_t const row_end = (row_count == 0)
1943
+ ? vectors_count
1944
+ : (row_start + row_count < vectors_count ? row_start + row_count : vectors_count);
1945
+
1946
+ // Round depth up to multiple of 192 (3 tiles × 64 elements)
1947
+ nk_size_t const depth_tiles = nk_size_divide_round_up_(depth, 64);
1948
+ nk_size_t const depth_tile_groups = nk_size_divide_round_up_(depth_tiles, 3);
1949
+
1950
+ nk_dots_i8_a16x64_sapphireamx_t a_tiles[3];
1951
+ nk_dots_i8_a16x64_sapphireamx_t b_src_tiles[3];
1952
+ nk_dots_i8_b64x16_sapphireamx_t b_tiles[3];
1953
+ nk_dots_i8_state_sapphireamx_t state;
1954
+
1955
+ nk_amx_tile_configure_sapphireamx_();
1956
+
1957
+ for (nk_size_t row_tile = row_start; row_tile < row_end; row_tile += 16) {
1958
+ nk_size_t const valid_rows = (row_tile + 16 <= row_end) ? 16 : (row_end - row_tile);
1959
+
1960
+ for (nk_size_t col_tile = 0; col_tile < vectors_count; col_tile += 16) {
1961
+ nk_size_t const valid_cols = (col_tile + 16 <= vectors_count) ? 16 : (vectors_count - col_tile);
1962
+
1963
+ nk_dots_i8_init_sapphireamx_(&state);
1964
+
1965
+ for (nk_size_t depth_group_idx = 0; depth_group_idx < depth_tile_groups; depth_group_idx++) {
1966
+ nk_size_t const depth_base = depth_group_idx * 192;
1967
+
1968
+ for (int tile_idx = 0; tile_idx < 3; tile_idx++) {
1969
+ nk_size_t const depth_start = depth_base + tile_idx * 64;
1970
+ nk_size_t const valid_depth = (depth_start + 64 <= depth)
1971
+ ? 64
1972
+ : (depth > depth_start ? depth - depth_start : 0);
1973
+
1974
+ nk_dots_i8_load_a_sapphireamx_( //
1975
+ &a_tiles[tile_idx], //
1976
+ vectors + row_tile * stride_in_bytes + depth_start, //
1977
+ stride_in_bytes, valid_rows, valid_depth);
1978
+
1979
+ if (row_tile == col_tile) {
1980
+ nk_dots_pack_i8_transposed_sapphireamx_(&a_tiles[tile_idx], &b_tiles[tile_idx]);
1981
+ }
1982
+ else {
1983
+ nk_dots_i8_load_a_sapphireamx_( //
1984
+ &b_src_tiles[tile_idx], //
1985
+ vectors + col_tile * stride_in_bytes + depth_start, //
1986
+ stride_in_bytes, valid_cols, valid_depth);
1987
+ nk_dots_pack_i8_transposed_sapphireamx_(&b_src_tiles[tile_idx], &b_tiles[tile_idx]);
1988
+ }
1989
+ }
1990
+
1991
+ nk_dots_i8_update_sapphireamx_( //
1992
+ &state, &a_tiles[0], &a_tiles[1], &a_tiles[2], &b_tiles[0], &b_tiles[1], &b_tiles[2]);
1993
+ }
1994
+
1995
+ nk_dots_i8_store_sapphireamx_( //
1996
+ &state, result + row_tile * result_stride_elements + col_tile, //
1997
+ result_stride_elements, valid_rows, valid_cols);
1998
+ }
1999
+ }
2000
+ }
2001
+
2002
+ #pragma endregion Signed Integers
2003
+
2004
+ #pragma region Unsigned Integers
2005
+
2006
+ NK_PUBLIC nk_size_t nk_dots_packed_size_u8_sapphireamx(nk_size_t column_count, nk_size_t depth) {
2007
+ // Same layout as I8 - just different type interpretation
2008
+ return nk_dots_packed_size_i8_sapphireamx(column_count, depth);
2009
+ }
2010
+
2011
+ NK_PUBLIC void nk_dots_pack_u8_sapphireamx( //
2012
+ nk_u8_t const *b, nk_size_t column_count, nk_size_t depth, //
2013
+ nk_size_t b_stride_in_bytes, void *b_packed) {
2014
+
2015
+ nk_size_t const tmm_rows = 16;
2016
+ nk_size_t const tmm_cols = 64;
2017
+ nk_size_t const tile_elements = 1024;
2018
+ nk_size_t const tile_bytes = tile_elements * sizeof(nk_u8_t);
2019
+
2020
+ nk_size_t const column_tiles_count = column_count / tmm_rows;
2021
+ nk_size_t const depth_tiles_count = nk_size_divide_round_up_(depth, tmm_cols);
2022
+ nk_size_t const column_remainder_count = column_count - column_tiles_count * tmm_rows;
2023
+ nk_size_t const total_tiles = column_tiles_count * depth_tiles_count;
2024
+
2025
+ nk_dots_amx_packed_header_t *header = (nk_dots_amx_packed_header_t *)b_packed;
2026
+ header->full_column_tiles = (nk_u32_t)column_tiles_count;
2027
+ header->full_depth_tiles = (nk_u32_t)depth_tiles_count;
2028
+ header->column_remainder_count = (nk_u32_t)column_remainder_count;
2029
+
2030
+ nk_size_t const tiles_offset = sizeof(nk_dots_amx_packed_header_t);
2031
+ nk_size_t const column_edge_offset = tiles_offset + total_tiles * tile_bytes;
2032
+ header->column_edge_offset = (nk_u32_t)column_edge_offset;
2033
+
2034
+ nk_u8_t *tiles_ptr = (nk_u8_t *)((char *)b_packed + tiles_offset);
2035
+ nk_u8_t *column_edge_ptr = (nk_u8_t *)((char *)b_packed + column_edge_offset);
2036
+
2037
+ // Pack tiles using vectorized transposer: gather 16 strided rows into an aligned
2038
+ // temporary, transpose via SIMD, then copy the result to the packed buffer.
2039
+ // Stack-local aligned tiles are needed because the packed buffer may not be 64-byte aligned.
2040
+ for (nk_size_t column_tile_idx = 0; column_tile_idx < column_tiles_count; column_tile_idx++) {
2041
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
2042
+
2043
+ nk_size_t const tile_index = column_tile_idx * depth_tiles_count + depth_tile_idx;
2044
+ nk_u8_t *tile_output = tiles_ptr + tile_index * tile_elements;
2045
+
2046
+ nk_size_t const src_row_start = column_tile_idx * tmm_rows;
2047
+ nk_size_t const src_column_start = depth_tile_idx * tmm_cols;
2048
+ nk_size_t const columns_to_pack = (src_column_start + tmm_cols <= depth) ? tmm_cols
2049
+ : (depth - src_column_start);
2050
+
2051
+ // Gather 16 strided source rows into a contiguous aligned tile
2052
+ nk_dots_u8_a16x64_sapphireamx_t source_tile;
2053
+ if (columns_to_pack == tmm_cols) {
2054
+ for (nk_size_t row_idx = 0; row_idx < tmm_rows; row_idx++) {
2055
+ nk_u8_t const *source_row = (nk_u8_t const *)((char const *)b +
2056
+ (src_row_start + row_idx) * b_stride_in_bytes) +
2057
+ src_column_start;
2058
+ _mm512_store_si512(&source_tile.data[row_idx][0], _mm512_loadu_si512(source_row));
2059
+ }
2060
+ }
2061
+ else {
2062
+ __mmask64 depth_mask = (__mmask64)((columns_to_pack < 64) ? ((1ULL << columns_to_pack) - 1) : ~0ULL);
2063
+ for (nk_size_t row_idx = 0; row_idx < tmm_rows; row_idx++) {
2064
+ nk_u8_t const *source_row = (nk_u8_t const *)((char const *)b +
2065
+ (src_row_start + row_idx) * b_stride_in_bytes) +
2066
+ src_column_start;
2067
+ _mm512_store_si512(&source_tile.data[row_idx][0], _mm512_maskz_loadu_epi8(depth_mask, source_row));
2068
+ }
2069
+ }
2070
+
2071
+ // Transpose into aligned local, then copy to (potentially unaligned) packed buffer
2072
+ nk_dots_u8_b64x16_sapphireamx_t transposed_tile;
2073
+ nk_dots_pack_u8_transposed_sapphireamx_(&source_tile, &transposed_tile);
2074
+ for (nk_size_t i = 0; i < tile_elements; i += 64)
2075
+ _mm512_storeu_si512(tile_output + i, _mm512_load_si512((char const *)&transposed_tile + i));
2076
+ }
2077
+ }
2078
+
2079
+ if (column_remainder_count > 0) {
2080
+ nk_size_t const remainder_start_row = column_tiles_count * tmm_rows;
2081
+ for (nk_size_t row_idx = 0; row_idx < column_remainder_count; row_idx++) {
2082
+ for (nk_size_t column_idx = 0; column_idx < depth; column_idx++) {
2083
+ column_edge_ptr[row_idx * depth + column_idx] =
2084
+ b[(remainder_start_row + row_idx) * b_stride_in_bytes + column_idx];
2085
+ }
2086
+ }
2087
+ }
2088
+
2089
+ // Compute and store per-column norms for angular/euclidean distance
2090
+ nk_size_t norms_offset = column_edge_offset +
2091
+ (column_remainder_count > 0 ? column_remainder_count * depth * sizeof(nk_u8_t) : 0);
2092
+ header->norms_byte_offset = (nk_u32_t)norms_offset;
2093
+ nk_u32_t *norms = (nk_u32_t *)((char *)b_packed + norms_offset);
2094
+ for (nk_size_t col = 0; col < column_count; col++)
2095
+ norms[col] = nk_dots_reduce_sumsq_u8_(b + col * b_stride_in_bytes, depth);
2096
+ }
2097
+
2098
+ NK_PUBLIC void nk_dots_packed_u8_sapphireamx( //
2099
+ nk_u8_t const *a, void const *b_packed, nk_u32_t *c, //
2100
+ nk_size_t rows_count, nk_size_t cols_count, nk_size_t depth, nk_size_t a_stride_bytes, nk_size_t c_stride_bytes) {
2101
+ nk_unused_(cols_count);
2102
+
2103
+ // Parse packed B header
2104
+ nk_dots_amx_packed_header_t const *header = (nk_dots_amx_packed_header_t const *)b_packed;
2105
+ nk_size_t const column_tiles_count = header->full_column_tiles;
2106
+ nk_size_t const depth_tiles_count = header->full_depth_tiles;
2107
+ nk_size_t const column_remainder_count = header->column_remainder_count;
2108
+
2109
+ // Packed B data regions
2110
+ nk_u8_t const *b_tiles_base = (nk_u8_t const *)((char const *)b_packed + sizeof(nk_dots_amx_packed_header_t));
2111
+ nk_u8_t const *col_edge_ptr = (nk_u8_t const *)((char const *)b_packed + header->column_edge_offset);
2112
+
2113
+ // Stride conversions
2114
+ nk_size_t const c_stride_elements = c_stride_bytes / sizeof(nk_u32_t);
2115
+
2116
+ // Tile dimensions
2117
+ nk_size_t const tile_depth = 64; // depth elements per U8 tile
2118
+ nk_size_t const tile_size = 1024; // bytes per packed tile
2119
+ nk_size_t const full_cols = column_tiles_count * 16;
2120
+
2121
+ // Block counts (32 × 32 output blocks = 2 × 2 tiles)
2122
+ nk_size_t const row_blocks_count = nk_size_divide_round_up_(rows_count, 32);
2123
+ nk_size_t const col_blocks_count = column_tiles_count / 2;
2124
+
2125
+ if (depth_tiles_count == 0) return;
2126
+
2127
+ // Tile buffers for A (only used for edge tiles)
2128
+ nk_dots_u8_a16x64_sapphireamx_t a_tile_top, a_tile_bottom;
2129
+ nk_dots_u8_state2x2_sapphireamx_t c_accum_buffer;
2130
+
2131
+ // Precompute: number of full depth-tiles
2132
+ nk_size_t const full_depth_tiles_count = depth / tile_depth;
2133
+ nk_size_t const depth_remainder = depth % tile_depth;
2134
+
2135
+ nk_amx_tile_configure_sapphireamx_();
2136
+
2137
+ // Process all 32 × 32 row × column blocks
2138
+ for (nk_size_t row_block_idx = 0; row_block_idx < row_blocks_count; row_block_idx++) {
2139
+ nk_size_t const row_block_start = row_block_idx * 32;
2140
+ nk_size_t const valid_rows_count = (row_block_start + 32 <= rows_count) ? 32 : (rows_count - row_block_start);
2141
+ nk_size_t const is_full_row_block = (valid_rows_count == 32);
2142
+
2143
+ // Process full column-blocks (pairs of 16-column tiles = 32 columns)
2144
+ for (nk_size_t column_block_idx = 0; column_block_idx < col_blocks_count; column_block_idx++) {
2145
+ nk_size_t const col_block_start = column_block_idx * 32;
2146
+
2147
+ // B tile base indices
2148
+ nk_size_t const b_column_left_base = (column_block_idx * 2) * depth_tiles_count;
2149
+ nk_size_t const b_column_right_base = (column_block_idx * 2 + 1) * depth_tiles_count;
2150
+
2151
+ // Zero accumulators (TMM4-7 stay resident across entire depth loop)
2152
+ _tile_zero(4);
2153
+ _tile_zero(5);
2154
+ _tile_zero(6);
2155
+ _tile_zero(7);
2156
+
2157
+ // Fast path: full row-block with full depth-tiles → direct A load with 2-deep pipelining
2158
+ if (is_full_row_block && full_depth_tiles_count > 0) {
2159
+ nk_u8_t const *a_top_base = a + row_block_start * a_stride_bytes;
2160
+ nk_u8_t const *a_bottom_base = a + (row_block_start + 16) * a_stride_bytes;
2161
+
2162
+ nk_dots_u8_b64x16_sapphireamx_t const *b_tile_left =
2163
+ (nk_dots_u8_b64x16_sapphireamx_t const *)(b_tiles_base + b_column_left_base * tile_size);
2164
+ nk_dots_u8_b64x16_sapphireamx_t const *b_tile_right =
2165
+ (nk_dots_u8_b64x16_sapphireamx_t const *)(b_tiles_base + b_column_right_base * tile_size);
2166
+
2167
+ // Prologue: load first depth tile into TMM0-3
2168
+ _tile_loadd(0, a_top_base, a_stride_bytes);
2169
+ _tile_loadd(1, a_bottom_base, a_stride_bytes);
2170
+ _tile_loadd(2, b_tile_left->data, 64);
2171
+ _tile_loadd(3, b_tile_right->data, 64);
2172
+
2173
+ // Main loop: 2-deep software pipelining (compute current while loading next)
2174
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < full_depth_tiles_count - 1; depth_tile_idx++) {
2175
+ nk_size_t const next_depth_offset = (depth_tile_idx + 1) * tile_depth;
2176
+
2177
+ _tile_dpbuud(4, 0, 2);
2178
+ _tile_dpbuud(5, 0, 3);
2179
+ _tile_dpbuud(6, 1, 2);
2180
+ _tile_dpbuud(7, 1, 3);
2181
+
2182
+ _tile_loadd(0, a_top_base + next_depth_offset, a_stride_bytes);
2183
+ _tile_loadd(1, a_bottom_base + next_depth_offset, a_stride_bytes);
2184
+ b_tile_left = (nk_dots_u8_b64x16_sapphireamx_t const *)(b_tiles_base +
2185
+ (b_column_left_base + depth_tile_idx + 1) *
2186
+ tile_size);
2187
+ b_tile_right = (nk_dots_u8_b64x16_sapphireamx_t const *)(b_tiles_base + (b_column_right_base +
2188
+ depth_tile_idx + 1) *
2189
+ tile_size);
2190
+ _tile_loadd(2, b_tile_left->data, 64);
2191
+ _tile_loadd(3, b_tile_right->data, 64);
2192
+ }
2193
+
2194
+ // Epilogue: final depth tile (no next to load)
2195
+ _tile_dpbuud(4, 0, 2);
2196
+ _tile_dpbuud(5, 0, 3);
2197
+ _tile_dpbuud(6, 1, 2);
2198
+ _tile_dpbuud(7, 1, 3);
2199
+
2200
+ // Handle partial depth-tile (if any) with buffered load
2201
+ if (depth_remainder > 0) {
2202
+ nk_size_t const depth_offset = full_depth_tiles_count * tile_depth;
2203
+
2204
+ nk_dots_u8_load_a_sapphireamx_(&a_tile_top, a_top_base + depth_offset, a_stride_bytes, 16,
2205
+ depth_remainder);
2206
+ nk_dots_u8_load_a_sapphireamx_(&a_tile_bottom, a_bottom_base + depth_offset, a_stride_bytes, 16,
2207
+ depth_remainder);
2208
+
2209
+ b_tile_left = (nk_dots_u8_b64x16_sapphireamx_t const *)(b_tiles_base + (b_column_left_base +
2210
+ full_depth_tiles_count) *
2211
+ tile_size);
2212
+ b_tile_right = (nk_dots_u8_b64x16_sapphireamx_t const *)(b_tiles_base + (b_column_right_base +
2213
+ full_depth_tiles_count) *
2214
+ tile_size);
2215
+
2216
+ _tile_loadd(0, a_tile_top.data, 64);
2217
+ _tile_loadd(1, a_tile_bottom.data, 64);
2218
+ _tile_loadd(2, b_tile_left->data, 64);
2219
+ _tile_loadd(3, b_tile_right->data, 64);
2220
+
2221
+ _tile_dpbuud(4, 0, 2);
2222
+ _tile_dpbuud(5, 0, 3);
2223
+ _tile_dpbuud(6, 1, 2);
2224
+ _tile_dpbuud(7, 1, 3);
2225
+ }
2226
+ }
2227
+ // Full row-block but only partial depth tile (depth < tile_depth)
2228
+ else if (is_full_row_block) {
2229
+ nk_u8_t const *a_top_base = a + row_block_start * a_stride_bytes;
2230
+ nk_u8_t const *a_bottom_base = a + (row_block_start + 16) * a_stride_bytes;
2231
+
2232
+ nk_dots_u8_load_a_sapphireamx_(&a_tile_top, a_top_base, a_stride_bytes, 16, depth_remainder);
2233
+ nk_dots_u8_load_a_sapphireamx_(&a_tile_bottom, a_bottom_base, a_stride_bytes, 16, depth_remainder);
2234
+
2235
+ nk_dots_u8_b64x16_sapphireamx_t const *b_tile_left =
2236
+ (nk_dots_u8_b64x16_sapphireamx_t const *)(b_tiles_base + b_column_left_base * tile_size);
2237
+ nk_dots_u8_b64x16_sapphireamx_t const *b_tile_right =
2238
+ (nk_dots_u8_b64x16_sapphireamx_t const *)(b_tiles_base + b_column_right_base * tile_size);
2239
+
2240
+ _tile_loadd(0, a_tile_top.data, 64);
2241
+ _tile_loadd(1, a_tile_bottom.data, 64);
2242
+ _tile_loadd(2, b_tile_left->data, 64);
2243
+ _tile_loadd(3, b_tile_right->data, 64);
2244
+
2245
+ _tile_dpbuud(4, 0, 2);
2246
+ _tile_dpbuud(5, 0, 3);
2247
+ _tile_dpbuud(6, 1, 2);
2248
+ _tile_dpbuud(7, 1, 3);
2249
+ }
2250
+ // Slow path: edge row-block → always use buffered load
2251
+ else {
2252
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
2253
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
2254
+
2255
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
2256
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
2257
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth
2258
+ : depth_remainder;
2259
+
2260
+ nk_dots_u8_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
2261
+ a_stride_bytes, rows_in_high_tile, valid_depth);
2262
+ if (rows_in_low_tile > 0) {
2263
+ nk_dots_u8_load_a_sapphireamx_(&a_tile_bottom,
2264
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
2265
+ a_stride_bytes, rows_in_low_tile, valid_depth);
2266
+ }
2267
+
2268
+ nk_dots_u8_b64x16_sapphireamx_t const *b_tile_left =
2269
+ (nk_dots_u8_b64x16_sapphireamx_t const *)(b_tiles_base +
2270
+ (b_column_left_base + depth_tile_idx) * tile_size);
2271
+ nk_dots_u8_b64x16_sapphireamx_t const *b_tile_right =
2272
+ (nk_dots_u8_b64x16_sapphireamx_t const *)(b_tiles_base +
2273
+ (b_column_right_base + depth_tile_idx) * tile_size);
2274
+
2275
+ _tile_loadd(0, a_tile_top.data, 64);
2276
+ _tile_loadd(1, a_tile_bottom.data, 64);
2277
+ _tile_loadd(2, b_tile_left->data, 64);
2278
+ _tile_loadd(3, b_tile_right->data, 64);
2279
+
2280
+ _tile_dpbuud(4, 0, 2);
2281
+ _tile_dpbuud(5, 0, 3);
2282
+ _tile_dpbuud(6, 1, 2);
2283
+ _tile_dpbuud(7, 1, 3);
2284
+ }
2285
+ }
2286
+
2287
+ // Store accumulators to output (once per output block, not per depth tile)
2288
+ if (is_full_row_block) {
2289
+ nk_u32_t *c_block = c + row_block_start * c_stride_elements + col_block_start;
2290
+ _tile_stored(4, c_block, c_stride_bytes);
2291
+ _tile_stored(5, c_block + 16, c_stride_bytes);
2292
+ _tile_stored(6, (nk_u32_t *)((char *)c_block + 16 * c_stride_bytes), c_stride_bytes);
2293
+ _tile_stored(7, (nk_u32_t *)((char *)c_block + 16 * c_stride_bytes) + 16, c_stride_bytes);
2294
+ }
2295
+ else {
2296
+ _tile_stored(4, c_accum_buffer.c[0][0].data, 64);
2297
+ _tile_stored(5, c_accum_buffer.c[0][1].data, 64);
2298
+ _tile_stored(6, c_accum_buffer.c[1][0].data, 64);
2299
+ _tile_stored(7, c_accum_buffer.c[1][1].data, 64);
2300
+ nk_dots_u8_output2x2_sapphireamx_(&c_accum_buffer,
2301
+ c + row_block_start * c_stride_elements + col_block_start,
2302
+ c_stride_elements, valid_rows_count, 32);
2303
+ }
2304
+ }
2305
+
2306
+ // Handle odd column-tile (single 16-column tile if column_tiles_count is odd)
2307
+ if (column_tiles_count % 2 == 1) {
2308
+ nk_size_t const column_tile_idx = column_tiles_count - 1;
2309
+ nk_size_t const col_start = column_tile_idx * 16;
2310
+ nk_size_t const b_column_base = column_tile_idx * depth_tiles_count;
2311
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
2312
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
2313
+
2314
+ nk_dots_u8_state_sapphireamx_t c_high_state, c_low_state;
2315
+
2316
+ _tile_zero(4);
2317
+ _tile_zero(6);
2318
+
2319
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
2320
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
2321
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
2322
+
2323
+ nk_dots_u8_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
2324
+ a_stride_bytes, rows_in_high_tile, valid_depth);
2325
+ if (rows_in_low_tile > 0) {
2326
+ nk_dots_u8_load_a_sapphireamx_(&a_tile_bottom,
2327
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
2328
+ a_stride_bytes, rows_in_low_tile, valid_depth);
2329
+ }
2330
+
2331
+ nk_dots_u8_b64x16_sapphireamx_t const *b_tile =
2332
+ (nk_dots_u8_b64x16_sapphireamx_t const *)(b_tiles_base +
2333
+ (b_column_base + depth_tile_idx) * tile_size);
2334
+
2335
+ _tile_loadd(0, a_tile_top.data, 64);
2336
+ _tile_loadd(1, a_tile_bottom.data, 64);
2337
+ _tile_loadd(2, b_tile->data, 64);
2338
+
2339
+ _tile_dpbuud(4, 0, 2);
2340
+ _tile_dpbuud(6, 1, 2);
2341
+ }
2342
+
2343
+ _tile_stored(4, c_high_state.data, 64);
2344
+ _tile_stored(6, c_low_state.data, 64);
2345
+
2346
+ nk_dots_u8_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + col_start,
2347
+ c_stride_elements, rows_in_high_tile, 16);
2348
+ if (rows_in_low_tile > 0) {
2349
+ nk_dots_u8_store_sapphireamx_(&c_low_state, c + (row_block_start + 16) * c_stride_elements + col_start,
2350
+ c_stride_elements, rows_in_low_tile, 16);
2351
+ }
2352
+ }
2353
+
2354
+ // Handle column-edge (remaining columns < 16) using AMX with partial tiles
2355
+ if (column_remainder_count > 0) {
2356
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
2357
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
2358
+
2359
+ nk_dots_u8_state_sapphireamx_t c_high_state, c_low_state;
2360
+ nk_dots_u8_a16x64_sapphireamx_t b_as_a;
2361
+ nk_dots_u8_b64x16_sapphireamx_t b_tile;
2362
+
2363
+ _tile_zero(4);
2364
+ _tile_zero(6);
2365
+
2366
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
2367
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
2368
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
2369
+
2370
+ nk_dots_u8_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
2371
+ a_stride_bytes, rows_in_high_tile, valid_depth);
2372
+ if (rows_in_low_tile > 0) {
2373
+ nk_dots_u8_load_a_sapphireamx_(&a_tile_bottom,
2374
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
2375
+ a_stride_bytes, rows_in_low_tile, valid_depth);
2376
+ }
2377
+
2378
+ nk_dots_u8_load_a_sapphireamx_(&b_as_a, col_edge_ptr + depth_offset, depth, column_remainder_count,
2379
+ valid_depth);
2380
+ nk_dots_pack_u8_transposed_sapphireamx_(&b_as_a, &b_tile);
2381
+
2382
+ _tile_loadd(0, a_tile_top.data, 64);
2383
+ _tile_loadd(1, a_tile_bottom.data, 64);
2384
+ _tile_loadd(2, b_tile.data, 64);
2385
+
2386
+ _tile_dpbuud(4, 0, 2);
2387
+ _tile_dpbuud(6, 1, 2);
2388
+ }
2389
+
2390
+ _tile_stored(4, c_high_state.data, 64);
2391
+ _tile_stored(6, c_low_state.data, 64);
2392
+
2393
+ nk_dots_u8_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + full_cols,
2394
+ c_stride_elements, rows_in_high_tile, column_remainder_count);
2395
+ if (rows_in_low_tile > 0) {
2396
+ nk_dots_u8_store_sapphireamx_(&c_low_state, c + (row_block_start + 16) * c_stride_elements + full_cols,
2397
+ c_stride_elements, rows_in_low_tile, column_remainder_count);
2398
+ }
2399
+ }
2400
+ }
2401
+
2402
+ _tile_release();
2403
+ }
2404
+
2405
+ NK_PUBLIC void nk_dots_symmetric_u8_sapphireamx( //
2406
+ nk_u8_t const *vectors, nk_size_t vectors_count, nk_size_t depth, //
2407
+ nk_size_t stride_in_bytes, nk_u32_t *result, nk_size_t result_stride_in_bytes, //
2408
+ nk_size_t row_start, nk_size_t row_count) {
2409
+
2410
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_u32_t);
2411
+
2412
+ // Handle row slicing: compute rows [row_start, row_end)
2413
+ nk_size_t const row_end = (row_count == 0)
2414
+ ? vectors_count
2415
+ : (row_start + row_count < vectors_count ? row_start + row_count : vectors_count);
2416
+
2417
+ // Round depth up to multiple of 192 (3 tiles × 64 elements)
2418
+ nk_size_t const depth_tiles = nk_size_divide_round_up_(depth, 64);
2419
+ nk_size_t const depth_tile_groups = nk_size_divide_round_up_(depth_tiles, 3);
2420
+
2421
+ nk_dots_u8_a16x64_sapphireamx_t a_tiles[3];
2422
+ nk_dots_u8_a16x64_sapphireamx_t b_src_tiles[3];
2423
+ nk_dots_u8_b64x16_sapphireamx_t b_tiles[3];
2424
+ nk_dots_u8_state_sapphireamx_t state;
2425
+
2426
+ nk_amx_tile_configure_sapphireamx_();
2427
+
2428
+ for (nk_size_t row_tile = row_start; row_tile < row_end; row_tile += 16) {
2429
+ nk_size_t const valid_rows = (row_tile + 16 <= row_end) ? 16 : (row_end - row_tile);
2430
+
2431
+ for (nk_size_t col_tile = 0; col_tile < vectors_count; col_tile += 16) {
2432
+ nk_size_t const valid_cols = (col_tile + 16 <= vectors_count) ? 16 : (vectors_count - col_tile);
2433
+
2434
+ nk_dots_u8_init_sapphireamx_(&state);
2435
+
2436
+ for (nk_size_t depth_group_idx = 0; depth_group_idx < depth_tile_groups; depth_group_idx++) {
2437
+ nk_size_t const depth_base = depth_group_idx * 192;
2438
+
2439
+ for (int tile_idx = 0; tile_idx < 3; tile_idx++) {
2440
+ nk_size_t const depth_start = depth_base + tile_idx * 64;
2441
+ nk_size_t const valid_depth = (depth_start + 64 <= depth)
2442
+ ? 64
2443
+ : (depth > depth_start ? depth - depth_start : 0);
2444
+
2445
+ nk_dots_u8_load_a_sapphireamx_( //
2446
+ &a_tiles[tile_idx], //
2447
+ vectors + row_tile * stride_in_bytes + depth_start, //
2448
+ stride_in_bytes, valid_rows, valid_depth);
2449
+
2450
+ if (row_tile == col_tile) {
2451
+ nk_dots_pack_u8_transposed_sapphireamx_(&a_tiles[tile_idx], &b_tiles[tile_idx]);
2452
+ }
2453
+ else {
2454
+ nk_dots_u8_load_a_sapphireamx_( //
2455
+ &b_src_tiles[tile_idx], //
2456
+ vectors + col_tile * stride_in_bytes + depth_start, //
2457
+ stride_in_bytes, valid_cols, valid_depth);
2458
+ nk_dots_pack_u8_transposed_sapphireamx_(&b_src_tiles[tile_idx], &b_tiles[tile_idx]);
2459
+ }
2460
+ }
2461
+
2462
+ nk_dots_u8_update_sapphireamx_( //
2463
+ &state, &a_tiles[0], &a_tiles[1], &a_tiles[2], &b_tiles[0], &b_tiles[1], &b_tiles[2]);
2464
+ }
2465
+
2466
+ nk_dots_u8_store_sapphireamx_( //
2467
+ &state, result + row_tile * result_stride_elements + col_tile, //
2468
+ result_stride_elements, valid_rows, valid_cols);
2469
+ }
2470
+ }
2471
+ }
2472
+
2473
+ #pragma endregion Unsigned Integers
2474
+
2475
+ #pragma region E4M3 Floats
2476
+
2477
+ NK_PUBLIC nk_size_t nk_dots_packed_size_e4m3_sapphireamx(nk_size_t column_count, nk_size_t depth) {
2478
+ // FP8 uses BF16 tile layout after conversion (same element count: 32 per row)
2479
+ return nk_dots_packed_size_bf16_sapphireamx(column_count, depth);
2480
+ }
2481
+
2482
+ NK_PUBLIC void nk_dots_pack_e4m3_sapphireamx( //
2483
+ nk_e4m3_t const *b, nk_size_t column_count, nk_size_t depth, //
2484
+ nk_size_t b_stride_in_bytes, void *b_packed) {
2485
+
2486
+ nk_size_t const tmm_rows = 16;
2487
+ nk_size_t const tmm_cols = 32; // Same depth granularity as BF16
2488
+ nk_size_t const tile_elements = 512;
2489
+ nk_size_t const tile_bytes = tile_elements * sizeof(nk_bf16_t);
2490
+
2491
+ nk_size_t const column_tiles_count = column_count / tmm_rows;
2492
+ nk_size_t const depth_tiles_count = nk_size_divide_round_up_(depth, tmm_cols);
2493
+ nk_size_t const column_remainder_count = column_count - column_tiles_count * tmm_rows;
2494
+ nk_size_t const total_tiles = column_tiles_count * depth_tiles_count;
2495
+
2496
+ nk_dots_amx_packed_header_t *header = (nk_dots_amx_packed_header_t *)b_packed;
2497
+ header->full_column_tiles = (nk_u32_t)column_tiles_count;
2498
+ header->full_depth_tiles = (nk_u32_t)depth_tiles_count;
2499
+ header->column_remainder_count = (nk_u32_t)column_remainder_count;
2500
+
2501
+ nk_size_t const tiles_offset = sizeof(nk_dots_amx_packed_header_t);
2502
+ nk_size_t const column_edge_offset = tiles_offset + total_tiles * tile_bytes;
2503
+ header->column_edge_offset = (nk_u32_t)column_edge_offset;
2504
+
2505
+ nk_bf16_t *tiles_ptr = (nk_bf16_t *)((char *)b_packed + tiles_offset);
2506
+ nk_bf16_t *column_edge_ptr = (nk_bf16_t *)((char *)b_packed + column_edge_offset);
2507
+
2508
+ // Pack tiles using vectorized convert + SIMD transpose
2509
+ for (nk_size_t column_tile_idx = 0; column_tile_idx < column_tiles_count; column_tile_idx++) {
2510
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
2511
+ nk_size_t const tile_index = column_tile_idx * depth_tiles_count + depth_tile_idx;
2512
+ nk_bf16_t *tile_output = tiles_ptr + tile_index * tile_elements;
2513
+
2514
+ nk_size_t const src_row_start = column_tile_idx * tmm_rows;
2515
+ nk_size_t const src_column_start = depth_tile_idx * tmm_cols;
2516
+ nk_size_t const columns_to_pack = (src_column_start + tmm_cols <= depth) ? tmm_cols
2517
+ : (depth - src_column_start);
2518
+
2519
+ // Convert E4M3 → BF16 and gather into aligned source tile
2520
+ __mmask32 column_mask = (columns_to_pack >= 32) ? 0xFFFFFFFF : ((__mmask32)1 << columns_to_pack) - 1;
2521
+ nk_dots_bf16_a16x32_sapphireamx_t source_tile;
2522
+ for (nk_size_t row_idx = 0; row_idx < tmm_rows; row_idx++) {
2523
+ __m256i e4m3_row_u8x32 = _mm256_maskz_loadu_epi8(
2524
+ column_mask, b + (src_row_start + row_idx) * b_stride_in_bytes + src_column_start);
2525
+ _mm512_store_si512(&source_tile.data[row_idx][0], nk_e4m3x32_to_bf16x32_icelake_(e4m3_row_u8x32));
2526
+ }
2527
+
2528
+ nk_dots_bf16_b32x16_sapphireamx_t transposed_tile;
2529
+ nk_dots_pack_bf16_transposed_sapphireamx_(&source_tile, &transposed_tile);
2530
+ for (nk_size_t i = 0; i < tile_bytes; i += 64)
2531
+ _mm512_storeu_si512((char *)tile_output + i, _mm512_load_si512((char const *)&transposed_tile + i));
2532
+ }
2533
+ }
2534
+
2535
+ // Pack column-remainder rows (convert E4M3 to BF16)
2536
+ if (column_remainder_count > 0) {
2537
+ nk_size_t const remainder_start_row = column_tiles_count * tmm_rows;
2538
+ for (nk_size_t row_idx = 0; row_idx < column_remainder_count; row_idx++) {
2539
+ for (nk_size_t column_idx = 0; column_idx < depth; column_idx += 32) {
2540
+ nk_size_t columns = (column_idx + 32 <= depth) ? 32 : (depth - column_idx);
2541
+ __mmask32 column_mask = (columns >= 32) ? 0xFFFFFFFF : ((__mmask32)1 << columns) - 1;
2542
+ __m256i e4m3_chunk_u8x32 = _mm256_maskz_loadu_epi8(
2543
+ column_mask, b + (remainder_start_row + row_idx) * b_stride_in_bytes + column_idx);
2544
+ __m512i bf16_chunk_i16x32 = nk_e4m3x32_to_bf16x32_icelake_(e4m3_chunk_u8x32);
2545
+ _mm512_mask_storeu_epi16(column_edge_ptr + row_idx * depth + column_idx, column_mask,
2546
+ bf16_chunk_i16x32);
2547
+ }
2548
+ }
2549
+ }
2550
+
2551
+ // Compute and store per-column norms for angular/euclidean distance
2552
+ nk_size_t norms_offset = column_edge_offset +
2553
+ (column_remainder_count > 0 ? column_remainder_count * depth * sizeof(nk_bf16_t) : 0);
2554
+ header->norms_byte_offset = (nk_u32_t)norms_offset;
2555
+ nk_f32_t *norms = (nk_f32_t *)((char *)b_packed + norms_offset);
2556
+ for (nk_size_t col = 0; col < column_count; col++)
2557
+ norms[col] = nk_dots_reduce_sumsq_e4m3_(b + col * b_stride_in_bytes, depth);
2558
+ }
2559
+
2560
+ NK_PUBLIC void nk_dots_packed_e4m3_sapphireamx( //
2561
+ nk_e4m3_t const *a, void const *b_packed, nk_f32_t *c, //
2562
+ nk_size_t rows_count, nk_size_t cols_count, nk_size_t depth, nk_size_t a_stride_bytes, nk_size_t c_stride_bytes) {
2563
+ nk_unused_(cols_count);
2564
+
2565
+ nk_dots_amx_packed_header_t const *header = (nk_dots_amx_packed_header_t const *)b_packed;
2566
+ nk_size_t const column_tiles_count = header->full_column_tiles;
2567
+ nk_size_t const depth_tiles_count = header->full_depth_tiles;
2568
+ nk_size_t const column_remainder_count = header->column_remainder_count;
2569
+
2570
+ // B tiles are already in BF16 format
2571
+ nk_bf16_t const *b_tiles_base = (nk_bf16_t const *)((char const *)b_packed + sizeof(nk_dots_amx_packed_header_t));
2572
+ nk_bf16_t const *col_edge_ptr = (nk_bf16_t const *)((char const *)b_packed + header->column_edge_offset);
2573
+
2574
+ nk_size_t const c_stride_elements = c_stride_bytes / sizeof(nk_f32_t);
2575
+ nk_size_t const tile_depth = 32;
2576
+ nk_size_t const tile_size = 512;
2577
+ nk_size_t const full_cols = column_tiles_count * 16;
2578
+
2579
+ nk_size_t const row_blocks_count = nk_size_divide_round_up_(rows_count, 32);
2580
+ nk_size_t const col_blocks_count = column_tiles_count / 2;
2581
+
2582
+ if (depth_tiles_count == 0) return;
2583
+
2584
+ nk_dots_bf16_a16x32_sapphireamx_t a_tile_top, a_tile_bottom;
2585
+ nk_dots_bf16_state2x2_sapphireamx_t c_accum_buffer;
2586
+
2587
+ nk_size_t const full_depth_tiles_count = depth / tile_depth;
2588
+ nk_size_t const depth_remainder = depth % tile_depth;
2589
+
2590
+ nk_amx_tile_configure_sapphireamx_();
2591
+
2592
+ // Loop order: row_blocks outer, col_blocks inner
2593
+ for (nk_size_t row_block_idx = 0; row_block_idx < row_blocks_count; row_block_idx++) {
2594
+ nk_size_t const row_block_start = row_block_idx * 32;
2595
+ nk_size_t const valid_rows_count = (row_block_start + 32 <= rows_count) ? 32 : (rows_count - row_block_start);
2596
+ nk_size_t const is_full_row_block = (valid_rows_count == 32);
2597
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
2598
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
2599
+
2600
+ for (nk_size_t column_block_idx = 0; column_block_idx < col_blocks_count; column_block_idx++) {
2601
+ nk_size_t const col_block_start = column_block_idx * 32;
2602
+ nk_size_t const b_column_left_base = (column_block_idx * 2) * depth_tiles_count;
2603
+ nk_size_t const b_column_right_base = (column_block_idx * 2 + 1) * depth_tiles_count;
2604
+
2605
+ // Zero accumulators (TMM4-7 stay resident across entire depth loop)
2606
+ _tile_zero(4);
2607
+ _tile_zero(5);
2608
+ _tile_zero(6);
2609
+ _tile_zero(7);
2610
+
2611
+ // FP8 always uses buffered load for E4M3 → BF16 conversion
2612
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
2613
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
2614
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
2615
+
2616
+ // Load A with FP8 → BF16 conversion
2617
+ nk_dots_e4m3_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
2618
+ a_stride_bytes, rows_in_high_tile, valid_depth);
2619
+ if (rows_in_low_tile > 0) {
2620
+ nk_dots_e4m3_load_a_sapphireamx_(&a_tile_bottom,
2621
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
2622
+ a_stride_bytes, rows_in_low_tile, valid_depth);
2623
+ }
2624
+
2625
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_left =
2626
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base +
2627
+ (b_column_left_base + depth_tile_idx) * tile_size);
2628
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_right =
2629
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base +
2630
+ (b_column_right_base + depth_tile_idx) * tile_size);
2631
+
2632
+ _tile_loadd(0, a_tile_top.data, 64);
2633
+ _tile_loadd(1, a_tile_bottom.data, 64);
2634
+ _tile_loadd(2, b_tile_left->data, 64);
2635
+ _tile_loadd(3, b_tile_right->data, 64);
2636
+
2637
+ _tile_dpbf16ps(4, 0, 2);
2638
+ _tile_dpbf16ps(5, 0, 3);
2639
+ _tile_dpbf16ps(6, 1, 2);
2640
+ _tile_dpbf16ps(7, 1, 3);
2641
+ }
2642
+
2643
+ // Store accumulators to output (once per output block)
2644
+ if (is_full_row_block) {
2645
+ nk_f32_t *c_block = c + row_block_start * c_stride_elements + col_block_start;
2646
+ _tile_stored(4, c_block, c_stride_bytes);
2647
+ _tile_stored(5, c_block + 16, c_stride_bytes);
2648
+ _tile_stored(6, (nk_f32_t *)((char *)c_block + 16 * c_stride_bytes), c_stride_bytes);
2649
+ _tile_stored(7, (nk_f32_t *)((char *)c_block + 16 * c_stride_bytes) + 16, c_stride_bytes);
2650
+ }
2651
+ else {
2652
+ _tile_stored(4, c_accum_buffer.c[0][0].data, 64);
2653
+ _tile_stored(5, c_accum_buffer.c[0][1].data, 64);
2654
+ _tile_stored(6, c_accum_buffer.c[1][0].data, 64);
2655
+ _tile_stored(7, c_accum_buffer.c[1][1].data, 64);
2656
+ nk_dots_bf16_output2x2_sapphireamx_(&c_accum_buffer,
2657
+ c + row_block_start * c_stride_elements + col_block_start,
2658
+ c_stride_elements, valid_rows_count, 32);
2659
+ }
2660
+ }
2661
+
2662
+ // Handle odd column-tile (single 16-column tile if column_tiles_count is odd)
2663
+ if (column_tiles_count % 2 == 1) {
2664
+ nk_size_t const column_tile_idx = column_tiles_count - 1;
2665
+ nk_size_t const col_start = column_tile_idx * 16;
2666
+ nk_size_t const b_column_base = column_tile_idx * depth_tiles_count;
2667
+
2668
+ nk_dots_bf16_state_sapphireamx_t c_high_state, c_low_state;
2669
+ _tile_zero(4);
2670
+ _tile_zero(6);
2671
+
2672
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
2673
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
2674
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
2675
+
2676
+ nk_dots_e4m3_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
2677
+ a_stride_bytes, rows_in_high_tile, valid_depth);
2678
+ if (rows_in_low_tile > 0) {
2679
+ nk_dots_e4m3_load_a_sapphireamx_(&a_tile_bottom,
2680
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
2681
+ a_stride_bytes, rows_in_low_tile, valid_depth);
2682
+ }
2683
+
2684
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile =
2685
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base +
2686
+ (b_column_base + depth_tile_idx) * tile_size);
2687
+
2688
+ _tile_loadd(0, a_tile_top.data, 64);
2689
+ _tile_loadd(1, a_tile_bottom.data, 64);
2690
+ _tile_loadd(2, b_tile->data, 64);
2691
+
2692
+ _tile_dpbf16ps(4, 0, 2);
2693
+ _tile_dpbf16ps(6, 1, 2);
2694
+ }
2695
+
2696
+ _tile_stored(4, c_high_state.data, 64);
2697
+ _tile_stored(6, c_low_state.data, 64);
2698
+
2699
+ nk_dots_bf16_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + col_start,
2700
+ c_stride_elements, rows_in_high_tile, 16);
2701
+ if (rows_in_low_tile > 0) {
2702
+ nk_dots_bf16_store_sapphireamx_(&c_low_state,
2703
+ c + (row_block_start + 16) * c_stride_elements + col_start,
2704
+ c_stride_elements, rows_in_low_tile, 16);
2705
+ }
2706
+ }
2707
+
2708
+ // Handle column-edge (remaining columns < 16) using AMX with partial tiles
2709
+ if (column_remainder_count > 0) {
2710
+ nk_dots_bf16_state_sapphireamx_t c_high_state, c_low_state;
2711
+ nk_dots_bf16_a16x32_sapphireamx_t b_as_a;
2712
+ nk_dots_bf16_b32x16_sapphireamx_t b_tile;
2713
+
2714
+ _tile_zero(4);
2715
+ _tile_zero(6);
2716
+
2717
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
2718
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
2719
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
2720
+
2721
+ nk_dots_e4m3_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
2722
+ a_stride_bytes, rows_in_high_tile, valid_depth);
2723
+ if (rows_in_low_tile > 0) {
2724
+ nk_dots_e4m3_load_a_sapphireamx_(&a_tile_bottom,
2725
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
2726
+ a_stride_bytes, rows_in_low_tile, valid_depth);
2727
+ }
2728
+
2729
+ // B edge data is already in BF16 format
2730
+ nk_dots_bf16_load_a_sapphireamx_(&b_as_a, col_edge_ptr + depth_offset, depth, column_remainder_count,
2731
+ valid_depth);
2732
+ nk_dots_pack_bf16_transposed_sapphireamx_(&b_as_a, &b_tile);
2733
+
2734
+ _tile_loadd(0, a_tile_top.data, 64);
2735
+ _tile_loadd(1, a_tile_bottom.data, 64);
2736
+ _tile_loadd(2, b_tile.data, 64);
2737
+
2738
+ _tile_dpbf16ps(4, 0, 2);
2739
+ _tile_dpbf16ps(6, 1, 2);
2740
+ }
2741
+
2742
+ _tile_stored(4, c_high_state.data, 64);
2743
+ _tile_stored(6, c_low_state.data, 64);
2744
+
2745
+ nk_dots_bf16_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + full_cols,
2746
+ c_stride_elements, rows_in_high_tile, column_remainder_count);
2747
+ if (rows_in_low_tile > 0) {
2748
+ nk_dots_bf16_store_sapphireamx_(&c_low_state,
2749
+ c + (row_block_start + 16) * c_stride_elements + full_cols,
2750
+ c_stride_elements, rows_in_low_tile, column_remainder_count);
2751
+ }
2752
+ }
2753
+ }
2754
+
2755
+ _tile_release();
2756
+ }
2757
+
2758
+ #pragma endregion E4M3 Floats
2759
+
2760
+ #pragma region E5M2 Floats
2761
+
2762
+ NK_PUBLIC nk_size_t nk_dots_packed_size_e5m2_sapphireamx(nk_size_t column_count, nk_size_t depth) {
2763
+ return nk_dots_packed_size_bf16_sapphireamx(column_count, depth);
2764
+ }
2765
+
2766
+ NK_PUBLIC void nk_dots_pack_e5m2_sapphireamx( //
2767
+ nk_e5m2_t const *b, nk_size_t column_count, nk_size_t depth, //
2768
+ nk_size_t b_stride_in_bytes, void *b_packed) {
2769
+
2770
+ nk_size_t const tmm_rows = 16;
2771
+ nk_size_t const tmm_cols = 32;
2772
+ nk_size_t const tile_elements = 512;
2773
+ nk_size_t const tile_bytes = tile_elements * sizeof(nk_bf16_t);
2774
+
2775
+ nk_size_t const column_tiles_count = column_count / tmm_rows;
2776
+ nk_size_t const depth_tiles_count = nk_size_divide_round_up_(depth, tmm_cols);
2777
+ nk_size_t const column_remainder_count = column_count - column_tiles_count * tmm_rows;
2778
+ nk_size_t const total_tiles = column_tiles_count * depth_tiles_count;
2779
+
2780
+ nk_dots_amx_packed_header_t *header = (nk_dots_amx_packed_header_t *)b_packed;
2781
+ header->full_column_tiles = (nk_u32_t)column_tiles_count;
2782
+ header->full_depth_tiles = (nk_u32_t)depth_tiles_count;
2783
+ header->column_remainder_count = (nk_u32_t)column_remainder_count;
2784
+
2785
+ nk_size_t const tiles_offset = sizeof(nk_dots_amx_packed_header_t);
2786
+ nk_size_t const column_edge_offset = tiles_offset + total_tiles * tile_bytes;
2787
+ header->column_edge_offset = (nk_u32_t)column_edge_offset;
2788
+
2789
+ nk_bf16_t *tiles_ptr = (nk_bf16_t *)((char *)b_packed + tiles_offset);
2790
+ nk_bf16_t *column_edge_ptr = (nk_bf16_t *)((char *)b_packed + column_edge_offset);
2791
+
2792
+ // Pack tiles using vectorized convert + SIMD transpose
2793
+ for (nk_size_t column_tile_idx = 0; column_tile_idx < column_tiles_count; column_tile_idx++) {
2794
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
2795
+ nk_size_t const tile_index = column_tile_idx * depth_tiles_count + depth_tile_idx;
2796
+ nk_bf16_t *tile_output = tiles_ptr + tile_index * tile_elements;
2797
+
2798
+ nk_size_t const src_row_start = column_tile_idx * tmm_rows;
2799
+ nk_size_t const src_column_start = depth_tile_idx * tmm_cols;
2800
+ nk_size_t const columns_to_pack = (src_column_start + tmm_cols <= depth) ? tmm_cols
2801
+ : (depth - src_column_start);
2802
+
2803
+ __mmask32 column_mask = (columns_to_pack >= 32) ? 0xFFFFFFFF : ((__mmask32)1 << columns_to_pack) - 1;
2804
+ nk_dots_bf16_a16x32_sapphireamx_t source_tile;
2805
+ for (nk_size_t row_idx = 0; row_idx < tmm_rows; row_idx++) {
2806
+ __m256i e5m2_row_u8x32 = _mm256_maskz_loadu_epi8(
2807
+ column_mask, b + (src_row_start + row_idx) * b_stride_in_bytes + src_column_start);
2808
+ _mm512_store_si512(&source_tile.data[row_idx][0], nk_e5m2x32_to_bf16x32_icelake_(e5m2_row_u8x32));
2809
+ }
2810
+
2811
+ nk_dots_bf16_b32x16_sapphireamx_t transposed_tile;
2812
+ nk_dots_pack_bf16_transposed_sapphireamx_(&source_tile, &transposed_tile);
2813
+ for (nk_size_t i = 0; i < tile_bytes; i += 64)
2814
+ _mm512_storeu_si512((char *)tile_output + i, _mm512_load_si512((char const *)&transposed_tile + i));
2815
+ }
2816
+ }
2817
+
2818
+ if (column_remainder_count > 0) {
2819
+ nk_size_t const remainder_start_row = column_tiles_count * tmm_rows;
2820
+ for (nk_size_t row_idx = 0; row_idx < column_remainder_count; row_idx++) {
2821
+ for (nk_size_t column_idx = 0; column_idx < depth; column_idx += 32) {
2822
+ nk_size_t columns = (column_idx + 32 <= depth) ? 32 : (depth - column_idx);
2823
+ __mmask32 column_mask = (columns >= 32) ? 0xFFFFFFFF : ((__mmask32)1 << columns) - 1;
2824
+ __m256i e5m2_chunk_u8x32 = _mm256_maskz_loadu_epi8(
2825
+ column_mask, b + (remainder_start_row + row_idx) * b_stride_in_bytes + column_idx);
2826
+ __m512i bf16_chunk_i16x32 = nk_e5m2x32_to_bf16x32_icelake_(e5m2_chunk_u8x32);
2827
+ _mm512_mask_storeu_epi16(column_edge_ptr + row_idx * depth + column_idx, column_mask,
2828
+ bf16_chunk_i16x32);
2829
+ }
2830
+ }
2831
+ }
2832
+
2833
+ // Compute and store per-column norms for angular/euclidean distance
2834
+ nk_size_t norms_offset = column_edge_offset +
2835
+ (column_remainder_count > 0 ? column_remainder_count * depth * sizeof(nk_bf16_t) : 0);
2836
+ header->norms_byte_offset = (nk_u32_t)norms_offset;
2837
+ nk_f32_t *norms = (nk_f32_t *)((char *)b_packed + norms_offset);
2838
+ for (nk_size_t col = 0; col < column_count; col++)
2839
+ norms[col] = nk_dots_reduce_sumsq_e5m2_(b + col * b_stride_in_bytes, depth);
2840
+ }
2841
+
2842
+ NK_PUBLIC void nk_dots_packed_e5m2_sapphireamx( //
2843
+ nk_e5m2_t const *a, void const *b_packed, nk_f32_t *c, //
2844
+ nk_size_t rows_count, nk_size_t cols_count, nk_size_t depth, nk_size_t a_stride_bytes, nk_size_t c_stride_bytes) {
2845
+ nk_unused_(cols_count);
2846
+
2847
+ nk_dots_amx_packed_header_t const *header = (nk_dots_amx_packed_header_t const *)b_packed;
2848
+ nk_size_t const column_tiles_count = header->full_column_tiles;
2849
+ nk_size_t const depth_tiles_count = header->full_depth_tiles;
2850
+ nk_size_t const column_remainder_count = header->column_remainder_count;
2851
+
2852
+ nk_bf16_t const *b_tiles_base = (nk_bf16_t const *)((char const *)b_packed + sizeof(nk_dots_amx_packed_header_t));
2853
+ nk_bf16_t const *col_edge_ptr = (nk_bf16_t const *)((char const *)b_packed + header->column_edge_offset);
2854
+
2855
+ nk_size_t const c_stride_elements = c_stride_bytes / sizeof(nk_f32_t);
2856
+ nk_size_t const tile_depth = 32;
2857
+ nk_size_t const tile_size = 512;
2858
+ nk_size_t const full_cols = column_tiles_count * 16;
2859
+
2860
+ nk_size_t const row_blocks_count = nk_size_divide_round_up_(rows_count, 32);
2861
+ nk_size_t const col_blocks_count = column_tiles_count / 2;
2862
+
2863
+ if (depth_tiles_count == 0) return;
2864
+
2865
+ nk_dots_bf16_a16x32_sapphireamx_t a_tile_top, a_tile_bottom;
2866
+ nk_dots_bf16_state2x2_sapphireamx_t c_accum_buffer;
2867
+
2868
+ nk_size_t const full_depth_tiles_count = depth / tile_depth;
2869
+ nk_size_t const depth_remainder = depth % tile_depth;
2870
+
2871
+ nk_amx_tile_configure_sapphireamx_();
2872
+
2873
+ // Loop order: row_blocks outer, col_blocks inner
2874
+ for (nk_size_t row_block_idx = 0; row_block_idx < row_blocks_count; row_block_idx++) {
2875
+ nk_size_t const row_block_start = row_block_idx * 32;
2876
+ nk_size_t const valid_rows_count = (row_block_start + 32 <= rows_count) ? 32 : (rows_count - row_block_start);
2877
+ nk_size_t const is_full_row_block = (valid_rows_count == 32);
2878
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
2879
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
2880
+
2881
+ for (nk_size_t column_block_idx = 0; column_block_idx < col_blocks_count; column_block_idx++) {
2882
+ nk_size_t const col_block_start = column_block_idx * 32;
2883
+ nk_size_t const b_column_left_base = (column_block_idx * 2) * depth_tiles_count;
2884
+ nk_size_t const b_column_right_base = (column_block_idx * 2 + 1) * depth_tiles_count;
2885
+
2886
+ // Zero accumulators (TMM4-7 stay resident across entire depth loop)
2887
+ _tile_zero(4);
2888
+ _tile_zero(5);
2889
+ _tile_zero(6);
2890
+ _tile_zero(7);
2891
+
2892
+ // FP8 always uses buffered load for E5M2 → BF16 conversion
2893
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
2894
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
2895
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
2896
+
2897
+ // Load A with FP8 → BF16 conversion
2898
+ nk_dots_e5m2_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
2899
+ a_stride_bytes, rows_in_high_tile, valid_depth);
2900
+ if (rows_in_low_tile > 0) {
2901
+ nk_dots_e5m2_load_a_sapphireamx_(&a_tile_bottom,
2902
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
2903
+ a_stride_bytes, rows_in_low_tile, valid_depth);
2904
+ }
2905
+
2906
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_left =
2907
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base +
2908
+ (b_column_left_base + depth_tile_idx) * tile_size);
2909
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_right =
2910
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base +
2911
+ (b_column_right_base + depth_tile_idx) * tile_size);
2912
+
2913
+ _tile_loadd(0, a_tile_top.data, 64);
2914
+ _tile_loadd(1, a_tile_bottom.data, 64);
2915
+ _tile_loadd(2, b_tile_left->data, 64);
2916
+ _tile_loadd(3, b_tile_right->data, 64);
2917
+
2918
+ _tile_dpbf16ps(4, 0, 2);
2919
+ _tile_dpbf16ps(5, 0, 3);
2920
+ _tile_dpbf16ps(6, 1, 2);
2921
+ _tile_dpbf16ps(7, 1, 3);
2922
+ }
2923
+
2924
+ // Store accumulators to output (once per output block)
2925
+ if (is_full_row_block) {
2926
+ nk_f32_t *c_block = c + row_block_start * c_stride_elements + col_block_start;
2927
+ _tile_stored(4, c_block, c_stride_bytes);
2928
+ _tile_stored(5, c_block + 16, c_stride_bytes);
2929
+ _tile_stored(6, (nk_f32_t *)((char *)c_block + 16 * c_stride_bytes), c_stride_bytes);
2930
+ _tile_stored(7, (nk_f32_t *)((char *)c_block + 16 * c_stride_bytes) + 16, c_stride_bytes);
2931
+ }
2932
+ else {
2933
+ _tile_stored(4, c_accum_buffer.c[0][0].data, 64);
2934
+ _tile_stored(5, c_accum_buffer.c[0][1].data, 64);
2935
+ _tile_stored(6, c_accum_buffer.c[1][0].data, 64);
2936
+ _tile_stored(7, c_accum_buffer.c[1][1].data, 64);
2937
+ nk_dots_bf16_output2x2_sapphireamx_(&c_accum_buffer,
2938
+ c + row_block_start * c_stride_elements + col_block_start,
2939
+ c_stride_elements, valid_rows_count, 32);
2940
+ }
2941
+ }
2942
+
2943
+ // Handle odd column-tile (single 16-column tile if column_tiles_count is odd)
2944
+ if (column_tiles_count % 2 == 1) {
2945
+ nk_size_t const column_tile_idx = column_tiles_count - 1;
2946
+ nk_size_t const col_start = column_tile_idx * 16;
2947
+ nk_size_t const b_column_base = column_tile_idx * depth_tiles_count;
2948
+
2949
+ nk_dots_bf16_state_sapphireamx_t c_high_state, c_low_state;
2950
+ _tile_zero(4);
2951
+ _tile_zero(6);
2952
+
2953
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
2954
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
2955
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
2956
+
2957
+ nk_dots_e5m2_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
2958
+ a_stride_bytes, rows_in_high_tile, valid_depth);
2959
+ if (rows_in_low_tile > 0) {
2960
+ nk_dots_e5m2_load_a_sapphireamx_(&a_tile_bottom,
2961
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
2962
+ a_stride_bytes, rows_in_low_tile, valid_depth);
2963
+ }
2964
+
2965
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile =
2966
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base +
2967
+ (b_column_base + depth_tile_idx) * tile_size);
2968
+
2969
+ _tile_loadd(0, a_tile_top.data, 64);
2970
+ _tile_loadd(1, a_tile_bottom.data, 64);
2971
+ _tile_loadd(2, b_tile->data, 64);
2972
+
2973
+ _tile_dpbf16ps(4, 0, 2);
2974
+ _tile_dpbf16ps(6, 1, 2);
2975
+ }
2976
+
2977
+ _tile_stored(4, c_high_state.data, 64);
2978
+ _tile_stored(6, c_low_state.data, 64);
2979
+
2980
+ nk_dots_bf16_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + col_start,
2981
+ c_stride_elements, rows_in_high_tile, 16);
2982
+ if (rows_in_low_tile > 0) {
2983
+ nk_dots_bf16_store_sapphireamx_(&c_low_state,
2984
+ c + (row_block_start + 16) * c_stride_elements + col_start,
2985
+ c_stride_elements, rows_in_low_tile, 16);
2986
+ }
2987
+ }
2988
+
2989
+ // Handle column-edge (remaining columns < 16) using AMX with partial tiles
2990
+ if (column_remainder_count > 0) {
2991
+ nk_dots_bf16_state_sapphireamx_t c_high_state, c_low_state;
2992
+ nk_dots_bf16_a16x32_sapphireamx_t b_as_a;
2993
+ nk_dots_bf16_b32x16_sapphireamx_t b_tile;
2994
+
2995
+ _tile_zero(4);
2996
+ _tile_zero(6);
2997
+
2998
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
2999
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
3000
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
3001
+
3002
+ nk_dots_e5m2_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
3003
+ a_stride_bytes, rows_in_high_tile, valid_depth);
3004
+ if (rows_in_low_tile > 0) {
3005
+ nk_dots_e5m2_load_a_sapphireamx_(&a_tile_bottom,
3006
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
3007
+ a_stride_bytes, rows_in_low_tile, valid_depth);
3008
+ }
3009
+
3010
+ nk_dots_bf16_load_a_sapphireamx_(&b_as_a, col_edge_ptr + depth_offset, depth, column_remainder_count,
3011
+ valid_depth);
3012
+ nk_dots_pack_bf16_transposed_sapphireamx_(&b_as_a, &b_tile);
3013
+
3014
+ _tile_loadd(0, a_tile_top.data, 64);
3015
+ _tile_loadd(1, a_tile_bottom.data, 64);
3016
+ _tile_loadd(2, b_tile.data, 64);
3017
+
3018
+ _tile_dpbf16ps(4, 0, 2);
3019
+ _tile_dpbf16ps(6, 1, 2);
3020
+ }
3021
+
3022
+ _tile_stored(4, c_high_state.data, 64);
3023
+ _tile_stored(6, c_low_state.data, 64);
3024
+
3025
+ nk_dots_bf16_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + full_cols,
3026
+ c_stride_elements, rows_in_high_tile, column_remainder_count);
3027
+ if (rows_in_low_tile > 0) {
3028
+ nk_dots_bf16_store_sapphireamx_(&c_low_state,
3029
+ c + (row_block_start + 16) * c_stride_elements + full_cols,
3030
+ c_stride_elements, rows_in_low_tile, column_remainder_count);
3031
+ }
3032
+ }
3033
+ }
3034
+
3035
+ _tile_release();
3036
+ }
3037
+
3038
+ NK_PUBLIC void nk_dots_symmetric_e5m2_sapphireamx( //
3039
+ nk_e5m2_t const *vectors, nk_size_t vectors_count, nk_size_t depth, //
3040
+ nk_size_t stride_in_bytes, nk_f32_t *result, nk_size_t result_stride_in_bytes, //
3041
+ nk_size_t row_start, nk_size_t row_count) {
3042
+
3043
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f32_t);
3044
+
3045
+ // Handle row slicing: compute rows [row_start, row_end)
3046
+ nk_size_t const row_end = (row_count == 0)
3047
+ ? vectors_count
3048
+ : (row_start + row_count < vectors_count ? row_start + row_count : vectors_count);
3049
+
3050
+ // Round depth up to multiple of 96 (3 tiles × 32 elements)
3051
+ nk_size_t const depth_tiles = nk_size_divide_round_up_(depth, 32);
3052
+ nk_size_t const depth_tile_groups = nk_size_divide_round_up_(depth_tiles, 3);
3053
+
3054
+ nk_dots_bf16_a16x32_sapphireamx_t a_tiles[3];
3055
+ nk_dots_bf16_a16x32_sapphireamx_t b_src_tiles[3];
3056
+ nk_dots_bf16_b32x16_sapphireamx_t b_tiles[3];
3057
+ nk_dots_bf16_state_sapphireamx_t state;
3058
+
3059
+ nk_amx_tile_configure_sapphireamx_();
3060
+
3061
+ for (nk_size_t row_tile = row_start; row_tile < row_end; row_tile += 16) {
3062
+ nk_size_t const valid_rows = (row_tile + 16 <= row_end) ? 16 : (row_end - row_tile);
3063
+
3064
+ for (nk_size_t col_tile = 0; col_tile < vectors_count; col_tile += 16) {
3065
+ nk_size_t const valid_cols = (col_tile + 16 <= vectors_count) ? 16 : (vectors_count - col_tile);
3066
+
3067
+ nk_dots_bf16_init_sapphireamx_(&state);
3068
+
3069
+ for (nk_size_t depth_group_idx = 0; depth_group_idx < depth_tile_groups; depth_group_idx++) {
3070
+ nk_size_t const depth_base = depth_group_idx * 96;
3071
+
3072
+ for (int tile_idx = 0; tile_idx < 3; tile_idx++) {
3073
+ nk_size_t const depth_start = depth_base + tile_idx * 32;
3074
+ nk_size_t const valid_depth = (depth_start + 32 <= depth)
3075
+ ? 32
3076
+ : (depth > depth_start ? depth - depth_start : 0);
3077
+
3078
+ nk_dots_e5m2_load_a_sapphireamx_( //
3079
+ &a_tiles[tile_idx], //
3080
+ vectors + row_tile * stride_in_bytes + depth_start, //
3081
+ stride_in_bytes, valid_rows, valid_depth);
3082
+
3083
+ if (row_tile == col_tile) {
3084
+ nk_dots_pack_bf16_transposed_sapphireamx_(&a_tiles[tile_idx], &b_tiles[tile_idx]);
3085
+ }
3086
+ else {
3087
+ nk_dots_e5m2_load_a_sapphireamx_( //
3088
+ &b_src_tiles[tile_idx], //
3089
+ vectors + col_tile * stride_in_bytes + depth_start, //
3090
+ stride_in_bytes, valid_cols, valid_depth);
3091
+ nk_dots_pack_bf16_transposed_sapphireamx_(&b_src_tiles[tile_idx], &b_tiles[tile_idx]);
3092
+ }
3093
+ }
3094
+
3095
+ nk_dots_bf16_update_sapphireamx_( //
3096
+ &state, &a_tiles[0], &a_tiles[1], &a_tiles[2], &b_tiles[0], &b_tiles[1], &b_tiles[2]);
3097
+ }
3098
+
3099
+ nk_dots_bf16_store_sapphireamx_( //
3100
+ &state, result + row_tile * result_stride_elements + col_tile, //
3101
+ result_stride_elements, valid_rows, valid_cols);
3102
+ }
3103
+ }
3104
+ }
3105
+
3106
+ NK_PUBLIC void nk_dots_symmetric_e4m3_sapphireamx( //
3107
+ nk_e4m3_t const *vectors, nk_size_t vectors_count, nk_size_t depth, //
3108
+ nk_size_t stride_in_bytes, nk_f32_t *result, nk_size_t result_stride_in_bytes, //
3109
+ nk_size_t row_start, nk_size_t row_count) {
3110
+
3111
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f32_t);
3112
+
3113
+ // Handle row slicing: compute rows [row_start, row_end)
3114
+ nk_size_t const row_end = (row_count == 0)
3115
+ ? vectors_count
3116
+ : (row_start + row_count < vectors_count ? row_start + row_count : vectors_count);
3117
+
3118
+ // Round depth up to multiple of 96 (3 tiles × 32 elements)
3119
+ nk_size_t const depth_tiles = nk_size_divide_round_up_(depth, 32);
3120
+ nk_size_t const depth_tile_groups = nk_size_divide_round_up_(depth_tiles, 3);
3121
+
3122
+ nk_dots_bf16_a16x32_sapphireamx_t a_tiles[3];
3123
+ nk_dots_bf16_a16x32_sapphireamx_t b_src_tiles[3];
3124
+ nk_dots_bf16_b32x16_sapphireamx_t b_tiles[3];
3125
+ nk_dots_bf16_state_sapphireamx_t state;
3126
+
3127
+ nk_amx_tile_configure_sapphireamx_();
3128
+
3129
+ for (nk_size_t row_tile = row_start; row_tile < row_end; row_tile += 16) {
3130
+ nk_size_t const valid_rows = (row_tile + 16 <= row_end) ? 16 : (row_end - row_tile);
3131
+
3132
+ for (nk_size_t col_tile = 0; col_tile < vectors_count; col_tile += 16) {
3133
+ nk_size_t const valid_cols = (col_tile + 16 <= vectors_count) ? 16 : (vectors_count - col_tile);
3134
+
3135
+ nk_dots_bf16_init_sapphireamx_(&state);
3136
+
3137
+ for (nk_size_t depth_group_idx = 0; depth_group_idx < depth_tile_groups; depth_group_idx++) {
3138
+ nk_size_t const depth_base = depth_group_idx * 96;
3139
+
3140
+ for (int tile_idx = 0; tile_idx < 3; tile_idx++) {
3141
+ nk_size_t const depth_start = depth_base + tile_idx * 32;
3142
+ nk_size_t const valid_depth = (depth_start + 32 <= depth)
3143
+ ? 32
3144
+ : (depth > depth_start ? depth - depth_start : 0);
3145
+
3146
+ nk_dots_e4m3_load_a_sapphireamx_( //
3147
+ &a_tiles[tile_idx], //
3148
+ vectors + row_tile * stride_in_bytes + depth_start, //
3149
+ stride_in_bytes, valid_rows, valid_depth);
3150
+
3151
+ if (row_tile == col_tile) {
3152
+ nk_dots_pack_bf16_transposed_sapphireamx_(&a_tiles[tile_idx], &b_tiles[tile_idx]);
3153
+ }
3154
+ else {
3155
+ nk_dots_e4m3_load_a_sapphireamx_( //
3156
+ &b_src_tiles[tile_idx], //
3157
+ vectors + col_tile * stride_in_bytes + depth_start, //
3158
+ stride_in_bytes, valid_cols, valid_depth);
3159
+ nk_dots_pack_bf16_transposed_sapphireamx_(&b_src_tiles[tile_idx], &b_tiles[tile_idx]);
3160
+ }
3161
+ }
3162
+
3163
+ nk_dots_bf16_update_sapphireamx_( //
3164
+ &state, &a_tiles[0], &a_tiles[1], &a_tiles[2], &b_tiles[0], &b_tiles[1], &b_tiles[2]);
3165
+ }
3166
+
3167
+ nk_dots_bf16_store_sapphireamx_( //
3168
+ &state, result + row_tile * result_stride_elements + col_tile, //
3169
+ result_stride_elements, valid_rows, valid_cols);
3170
+ }
3171
+ }
3172
+ }
3173
+
3174
+ #pragma endregion E5M2 Floats
3175
+
3176
+ #pragma region E2M3 Floats
3177
+
3178
+ /* Load E2M3 A tile with E2M3 to signed I8 conversion via VPERMB LUT.
3179
+ * Each E2M3 byte encodes: bit 5 = sign, bits 4:0 = magnitude (5-bit index).
3180
+ * The LUT maps 5-bit magnitude to value * 16, then sign is applied via conditional negation.
3181
+ * Result is stored in INT8 tile for use with _tile_dpbssd.
3182
+ */
3183
+ NK_INTERNAL void nk_dots_e2m3_load_a_sapphireamx_( //
3184
+ nk_dots_i8_a16x64_sapphireamx_t *a_tile, //
3185
+ nk_e2m3_t const *src, nk_size_t src_stride, //
3186
+ nk_size_t valid_rows, nk_size_t valid_cols) {
3187
+
3188
+ // Build 64-byte LUT for VPERMB: 32 entries replicated to fill both halves.
3189
+ // magnitude → value×16:
3190
+ // e=0 (step 2): {0,2,4,6,8,10,12,14},
3191
+ // e=1 (step 2): {16,18,20,22,24,26,28,30},
3192
+ // e=2 (step 4): {32,36,40,44,48,52,56,60},
3193
+ // e=3 (step 8): {64,72,80,88,96,104,112,120}
3194
+ NK_ALIGN64 static nk_u8_t const lut_bytes[64] = {
3195
+ 0, 2, 4, 6, 8, 10, 12, 14, //
3196
+ 16, 18, 20, 22, 24, 26, 28, 30, //
3197
+ 32, 36, 40, 44, 48, 52, 56, 60, //
3198
+ 64, 72, 80, 88, 96, 104, 112, 120, //
3199
+ 0, 2, 4, 6, 8, 10, 12, 14, //
3200
+ 16, 18, 20, 22, 24, 26, 28, 30, //
3201
+ 32, 36, 40, 44, 48, 52, 56, 60, //
3202
+ 64, 72, 80, 88, 96, 104, 112, 120, //
3203
+ };
3204
+ __m512i magnitude_lut_u8x64 = _mm512_load_si512((__m512i const *)lut_bytes);
3205
+ __m512i sign_mask_u8x64 = _mm512_set1_epi8(0x20);
3206
+ __m512i magnitude_mask_u8x64 = _mm512_set1_epi8(0x1F);
3207
+ __m512i zero_i8x64 = _mm512_setzero_si512();
3208
+
3209
+ __mmask64 column_mask = (valid_cols >= 64) ? 0xFFFFFFFFFFFFFFFFULL : ((__mmask64)1 << valid_cols) - 1;
3210
+
3211
+ for (nk_size_t row = 0; row < 16; row++) {
3212
+ if (row < valid_rows) {
3213
+ __m512i raw_u8x64 = _mm512_maskz_loadu_epi8(column_mask, src + row * src_stride);
3214
+ __m512i magnitude_u8x64 = _mm512_and_si512(raw_u8x64, magnitude_mask_u8x64);
3215
+ __m512i unsigned_value_u8x64 = _mm512_permutexvar_epi8(magnitude_u8x64, magnitude_lut_u8x64);
3216
+ __mmask64 negate_mask = _mm512_test_epi8_mask(raw_u8x64, sign_mask_u8x64);
3217
+ __m512i signed_value_i8x64 = _mm512_mask_sub_epi8(unsigned_value_u8x64, negate_mask, zero_i8x64,
3218
+ unsigned_value_u8x64);
3219
+ _mm512_store_si512(a_tile->data[row], signed_value_i8x64);
3220
+ }
3221
+ else { _mm512_store_si512(a_tile->data[row], zero_i8x64); }
3222
+ }
3223
+ nk_compiler_barrier_sapphireamx_();
3224
+ }
3225
+
3226
+ /* Store E2M3 accumulator: read I32 state, convert to F32, multiply by 1/256, store as F32. */
3227
+ NK_INTERNAL void nk_dots_e2m3_store_sapphireamx_( //
3228
+ nk_dots_i8_state_sapphireamx_t const *state, //
3229
+ nk_f32_t *dst, nk_size_t dst_stride_elements, //
3230
+ nk_size_t valid_rows, nk_size_t valid_cols) {
3231
+
3232
+ __mmask16 column_mask = (valid_cols >= 16) ? 0xFFFF : ((__mmask16)1 << valid_cols) - 1;
3233
+ __m512 scale_f32x16 = _mm512_set1_ps(1.0f / 256.0f);
3234
+
3235
+ for (nk_size_t row = 0; row < valid_rows; row++) {
3236
+ __m512i i32_row_i32x16 = _mm512_load_si512(state->data[row]);
3237
+ __m512 f32_row_f32x16 = _mm512_mul_ps(_mm512_cvtepi32_ps(i32_row_i32x16), scale_f32x16);
3238
+ _mm512_mask_storeu_ps(dst + row * dst_stride_elements, column_mask, f32_row_f32x16);
3239
+ }
3240
+ }
3241
+
3242
+ /* Store E2M3 2x2 accumulator state to F32 output matrix with masking for edge tiles. */
3243
+ NK_INTERNAL void nk_dots_e2m3_output2x2_sapphireamx_( //
3244
+ nk_dots_i8_state2x2_sapphireamx_t const *state, //
3245
+ nk_f32_t *dst, nk_size_t dst_stride_elements, //
3246
+ nk_size_t valid_rows, nk_size_t valid_cols) {
3247
+
3248
+ nk_size_t const rows_high = (valid_rows > 16) ? 16 : valid_rows;
3249
+ nk_size_t const cols_left = (valid_cols > 16) ? 16 : valid_cols;
3250
+ nk_size_t const cols_right = (valid_cols > 16) ? valid_cols - 16 : 0;
3251
+
3252
+ if (rows_high > 0 && cols_left > 0)
3253
+ nk_dots_e2m3_store_sapphireamx_(&state->c[0][0], dst, dst_stride_elements, rows_high, cols_left);
3254
+ if (rows_high > 0 && cols_right > 0)
3255
+ nk_dots_e2m3_store_sapphireamx_(&state->c[0][1], dst + 16, dst_stride_elements, rows_high, cols_right);
3256
+
3257
+ if (valid_rows > 16) {
3258
+ nk_size_t const rows_low = valid_rows - 16;
3259
+ nk_f32_t *dst_low = dst + 16 * dst_stride_elements;
3260
+ if (cols_left > 0)
3261
+ nk_dots_e2m3_store_sapphireamx_(&state->c[1][0], dst_low, dst_stride_elements, rows_low, cols_left);
3262
+ if (cols_right > 0)
3263
+ nk_dots_e2m3_store_sapphireamx_(&state->c[1][1], dst_low + 16, dst_stride_elements, rows_low, cols_right);
3264
+ }
3265
+ }
3266
+
3267
+ NK_PUBLIC nk_size_t nk_dots_packed_size_e2m3_sapphireamx(nk_size_t column_count, nk_size_t depth) {
3268
+ // E2M3 uses INT8 tile layout after conversion (same element count: 64 per row)
3269
+ return nk_dots_packed_size_i8_sapphireamx(column_count, depth);
3270
+ }
3271
+
3272
+ NK_PUBLIC void nk_dots_pack_e2m3_sapphireamx( //
3273
+ nk_e2m3_t const *b, nk_size_t column_count, nk_size_t depth, //
3274
+ nk_size_t b_stride_in_bytes, void *b_packed) {
3275
+
3276
+ // AMX I8 tile dimensions: 16 rows x 64 columns (1024 I8 elements = 1KB)
3277
+ nk_size_t const tmm_rows = 16;
3278
+ nk_size_t const tmm_cols = 64;
3279
+ nk_size_t const tile_elements = 1024;
3280
+ nk_size_t const tile_bytes = tile_elements * sizeof(nk_i8_t);
3281
+
3282
+ nk_size_t const column_tiles_count = column_count / tmm_rows;
3283
+ nk_size_t const depth_tiles_count = nk_size_divide_round_up_(depth, tmm_cols);
3284
+ nk_size_t const column_remainder_count = column_count - column_tiles_count * tmm_rows;
3285
+ nk_size_t const total_tiles = column_tiles_count * depth_tiles_count;
3286
+
3287
+ nk_dots_amx_packed_header_t *header = (nk_dots_amx_packed_header_t *)b_packed;
3288
+ header->full_column_tiles = (nk_u32_t)column_tiles_count;
3289
+ header->full_depth_tiles = (nk_u32_t)depth_tiles_count;
3290
+ header->column_remainder_count = (nk_u32_t)column_remainder_count;
3291
+
3292
+ nk_size_t const tiles_offset = sizeof(nk_dots_amx_packed_header_t);
3293
+ nk_size_t const column_edge_offset = tiles_offset + total_tiles * tile_bytes;
3294
+ header->column_edge_offset = (nk_u32_t)column_edge_offset;
3295
+
3296
+ nk_i8_t *tiles_ptr = (nk_i8_t *)((char *)b_packed + tiles_offset);
3297
+ nk_i8_t *column_edge_ptr = (nk_i8_t *)((char *)b_packed + column_edge_offset);
3298
+
3299
+ // Pack tiles using vectorized E2M3 → I8 conversion + SIMD transpose
3300
+ for (nk_size_t column_tile_idx = 0; column_tile_idx < column_tiles_count; column_tile_idx++) {
3301
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
3302
+ nk_size_t const tile_index = column_tile_idx * depth_tiles_count + depth_tile_idx;
3303
+ nk_i8_t *tile_output = tiles_ptr + tile_index * tile_elements;
3304
+
3305
+ nk_size_t const src_row_start = column_tile_idx * tmm_rows;
3306
+ nk_size_t const src_column_start = depth_tile_idx * tmm_cols;
3307
+ nk_size_t const columns_to_pack = (src_column_start + tmm_cols <= depth) ? tmm_cols
3308
+ : (depth - src_column_start);
3309
+
3310
+ // Convert E2M3 → I8 and gather into aligned source tile
3311
+ nk_dots_i8_a16x64_sapphireamx_t source_tile;
3312
+ if (columns_to_pack == tmm_cols) {
3313
+ for (nk_size_t row_idx = 0; row_idx < tmm_rows; row_idx++) {
3314
+ __m512i raw_row = _mm512_loadu_si512(
3315
+ (nk_e2m3_t const *)((char const *)b + (src_row_start + row_idx) * b_stride_in_bytes) +
3316
+ src_column_start);
3317
+ _mm512_store_si512(&source_tile.data[row_idx][0], nk_e2m3x64_to_i8x64_skylake_(raw_row));
3318
+ }
3319
+ }
3320
+ else {
3321
+ __mmask64 depth_mask = (__mmask64)((columns_to_pack < 64) ? ((1ULL << columns_to_pack) - 1) : ~0ULL);
3322
+ for (nk_size_t row_idx = 0; row_idx < tmm_rows; row_idx++) {
3323
+ __m512i raw_row = _mm512_maskz_loadu_epi8(
3324
+ depth_mask,
3325
+ (nk_e2m3_t const *)((char const *)b + (src_row_start + row_idx) * b_stride_in_bytes) +
3326
+ src_column_start);
3327
+ _mm512_store_si512(&source_tile.data[row_idx][0], nk_e2m3x64_to_i8x64_skylake_(raw_row));
3328
+ }
3329
+ }
3330
+
3331
+ nk_dots_i8_b64x16_sapphireamx_t transposed_tile;
3332
+ nk_dots_pack_i8_transposed_sapphireamx_(&source_tile, &transposed_tile);
3333
+ for (nk_size_t i = 0; i < tile_elements; i += 64)
3334
+ _mm512_storeu_si512(tile_output + i, _mm512_load_si512((char const *)&transposed_tile + i));
3335
+ }
3336
+ }
3337
+
3338
+ // Pack column-remainder rows (convert E2M3 to I8) using scalar LUT
3339
+ static nk_u8_t const lut_magnitude[32] = {
3340
+ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, //
3341
+ 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 88, 96, 104, 112, 120, //
3342
+ };
3343
+ if (column_remainder_count > 0) {
3344
+ nk_size_t const remainder_start_row = column_tiles_count * tmm_rows;
3345
+ for (nk_size_t row_idx = 0; row_idx < column_remainder_count; row_idx++) {
3346
+ for (nk_size_t column_idx = 0; column_idx < depth; column_idx++) {
3347
+ nk_u8_t raw = b[(remainder_start_row + row_idx) * b_stride_in_bytes + column_idx];
3348
+ nk_u8_t magnitude = raw & 0x1F;
3349
+ nk_i8_t val = (nk_i8_t)lut_magnitude[magnitude];
3350
+ if (raw & 0x20) val = -val;
3351
+ column_edge_ptr[row_idx * depth + column_idx] = val;
3352
+ }
3353
+ }
3354
+ }
3355
+
3356
+ // Compute and store per-column norms for angular/euclidean distance
3357
+ nk_size_t norms_offset = column_edge_offset +
3358
+ (column_remainder_count > 0 ? column_remainder_count * depth * sizeof(nk_i8_t) : 0);
3359
+ header->norms_byte_offset = (nk_u32_t)norms_offset;
3360
+ nk_f32_t *norms = (nk_f32_t *)((char *)b_packed + norms_offset);
3361
+ for (nk_size_t col = 0; col < column_count; col++)
3362
+ norms[col] = nk_dots_reduce_sumsq_e2m3_(b + col * b_stride_in_bytes, depth);
3363
+ }
3364
+
3365
+ NK_PUBLIC void nk_dots_packed_e2m3_sapphireamx( //
3366
+ nk_e2m3_t const *a, void const *b_packed, nk_f32_t *c, //
3367
+ nk_size_t rows_count, nk_size_t cols_count, nk_size_t depth, nk_size_t a_stride_bytes, nk_size_t c_stride_bytes) {
3368
+ nk_unused_(cols_count);
3369
+
3370
+ nk_dots_amx_packed_header_t const *header = (nk_dots_amx_packed_header_t const *)b_packed;
3371
+ nk_size_t const column_tiles_count = header->full_column_tiles;
3372
+ nk_size_t const depth_tiles_count = header->full_depth_tiles;
3373
+ nk_size_t const column_remainder_count = header->column_remainder_count;
3374
+
3375
+ // B tiles are already in I8 format
3376
+ nk_i8_t const *b_tiles_base = (nk_i8_t const *)((char const *)b_packed + sizeof(nk_dots_amx_packed_header_t));
3377
+ nk_i8_t const *col_edge_ptr = (nk_i8_t const *)((char const *)b_packed + header->column_edge_offset);
3378
+
3379
+ nk_size_t const c_stride_elements = c_stride_bytes / sizeof(nk_f32_t);
3380
+ nk_size_t const tile_depth = 64;
3381
+ nk_size_t const tile_size = 1024;
3382
+ nk_size_t const full_cols = column_tiles_count * 16;
3383
+
3384
+ nk_size_t const row_blocks_count = nk_size_divide_round_up_(rows_count, 32);
3385
+ nk_size_t const col_blocks_count = column_tiles_count / 2;
3386
+
3387
+ if (depth_tiles_count == 0) return;
3388
+
3389
+ nk_dots_i8_a16x64_sapphireamx_t a_tile_top, a_tile_bottom;
3390
+ nk_dots_i8_state2x2_sapphireamx_t c_accum_buffer;
3391
+
3392
+ nk_size_t const full_depth_tiles_count = depth / tile_depth;
3393
+ nk_size_t const depth_remainder = depth % tile_depth;
3394
+
3395
+ nk_amx_tile_configure_sapphireamx_();
3396
+
3397
+ // Loop order: row_blocks outer, col_blocks inner
3398
+ for (nk_size_t row_block_idx = 0; row_block_idx < row_blocks_count; row_block_idx++) {
3399
+ nk_size_t const row_block_start = row_block_idx * 32;
3400
+ nk_size_t const valid_rows_count = (row_block_start + 32 <= rows_count) ? 32 : (rows_count - row_block_start);
3401
+ nk_size_t const is_full_row_block = (valid_rows_count == 32);
3402
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
3403
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
3404
+
3405
+ for (nk_size_t column_block_idx = 0; column_block_idx < col_blocks_count; column_block_idx++) {
3406
+ nk_size_t const col_block_start = column_block_idx * 32;
3407
+ nk_size_t const b_column_left_base = (column_block_idx * 2) * depth_tiles_count;
3408
+ nk_size_t const b_column_right_base = (column_block_idx * 2 + 1) * depth_tiles_count;
3409
+
3410
+ // Zero accumulators (TMM4-7 stay resident across entire depth loop)
3411
+ _tile_zero(4);
3412
+ _tile_zero(5);
3413
+ _tile_zero(6);
3414
+ _tile_zero(7);
3415
+
3416
+ // E2M3 always uses buffered load for E2M3 -> I8 conversion
3417
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
3418
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
3419
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
3420
+
3421
+ // Load A with E2M3 -> I8 conversion
3422
+ nk_dots_e2m3_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
3423
+ a_stride_bytes, rows_in_high_tile, valid_depth);
3424
+ if (rows_in_low_tile > 0) {
3425
+ nk_dots_e2m3_load_a_sapphireamx_(&a_tile_bottom,
3426
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
3427
+ a_stride_bytes, rows_in_low_tile, valid_depth);
3428
+ }
3429
+
3430
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile_left =
3431
+ (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base +
3432
+ (b_column_left_base + depth_tile_idx) * tile_size);
3433
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile_right =
3434
+ (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base +
3435
+ (b_column_right_base + depth_tile_idx) * tile_size);
3436
+
3437
+ _tile_loadd(0, a_tile_top.data, 64);
3438
+ _tile_loadd(1, a_tile_bottom.data, 64);
3439
+ _tile_loadd(2, b_tile_left->data, 64);
3440
+ _tile_loadd(3, b_tile_right->data, 64);
3441
+
3442
+ _tile_dpbssd(4, 0, 2);
3443
+ _tile_dpbssd(5, 0, 3);
3444
+ _tile_dpbssd(6, 1, 2);
3445
+ _tile_dpbssd(7, 1, 3);
3446
+ }
3447
+
3448
+ // Store accumulators to output (once per output block)
3449
+ // Can't directly store I32 tiles to F32 output, must buffer + convert
3450
+ if (is_full_row_block) {
3451
+ nk_f32_t *c_block = c + row_block_start * c_stride_elements + col_block_start;
3452
+ nk_dots_i8_state2x2_sapphireamx_t c_accum_buffer;
3453
+ _tile_stored(4, c_accum_buffer.c[0][0].data, 64);
3454
+ _tile_stored(5, c_accum_buffer.c[0][1].data, 64);
3455
+ _tile_stored(6, c_accum_buffer.c[1][0].data, 64);
3456
+ _tile_stored(7, c_accum_buffer.c[1][1].data, 64);
3457
+ nk_dots_e2m3_output2x2_sapphireamx_(&c_accum_buffer, c_block, c_stride_elements, valid_rows_count, 32);
3458
+ }
3459
+ else {
3460
+ _tile_stored(4, c_accum_buffer.c[0][0].data, 64);
3461
+ _tile_stored(5, c_accum_buffer.c[0][1].data, 64);
3462
+ _tile_stored(6, c_accum_buffer.c[1][0].data, 64);
3463
+ _tile_stored(7, c_accum_buffer.c[1][1].data, 64);
3464
+ nk_dots_e2m3_output2x2_sapphireamx_(&c_accum_buffer,
3465
+ c + row_block_start * c_stride_elements + col_block_start,
3466
+ c_stride_elements, valid_rows_count, 32);
3467
+ }
3468
+ }
3469
+
3470
+ // Handle odd column-tile (single 16-column tile if column_tiles_count is odd)
3471
+ if (column_tiles_count % 2 == 1) {
3472
+ nk_size_t const column_tile_idx = column_tiles_count - 1;
3473
+ nk_size_t const col_start = column_tile_idx * 16;
3474
+ nk_size_t const b_column_base = column_tile_idx * depth_tiles_count;
3475
+
3476
+ nk_dots_i8_state_sapphireamx_t c_high_state, c_low_state;
3477
+ _tile_zero(4);
3478
+ _tile_zero(6);
3479
+
3480
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
3481
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
3482
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
3483
+
3484
+ nk_dots_e2m3_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
3485
+ a_stride_bytes, rows_in_high_tile, valid_depth);
3486
+ if (rows_in_low_tile > 0) {
3487
+ nk_dots_e2m3_load_a_sapphireamx_(&a_tile_bottom,
3488
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
3489
+ a_stride_bytes, rows_in_low_tile, valid_depth);
3490
+ }
3491
+
3492
+ nk_dots_i8_b64x16_sapphireamx_t const *b_tile =
3493
+ (nk_dots_i8_b64x16_sapphireamx_t const *)(b_tiles_base +
3494
+ (b_column_base + depth_tile_idx) * tile_size);
3495
+
3496
+ _tile_loadd(0, a_tile_top.data, 64);
3497
+ _tile_loadd(1, a_tile_bottom.data, 64);
3498
+ _tile_loadd(2, b_tile->data, 64);
3499
+
3500
+ _tile_dpbssd(4, 0, 2);
3501
+ _tile_dpbssd(6, 1, 2);
3502
+ }
3503
+
3504
+ _tile_stored(4, c_high_state.data, 64);
3505
+ _tile_stored(6, c_low_state.data, 64);
3506
+
3507
+ nk_dots_e2m3_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + col_start,
3508
+ c_stride_elements, rows_in_high_tile, 16);
3509
+ if (rows_in_low_tile > 0) {
3510
+ nk_dots_e2m3_store_sapphireamx_(&c_low_state,
3511
+ c + (row_block_start + 16) * c_stride_elements + col_start,
3512
+ c_stride_elements, rows_in_low_tile, 16);
3513
+ }
3514
+ }
3515
+
3516
+ // Handle column-edge (remaining columns < 16) using AMX with partial tiles
3517
+ if (column_remainder_count > 0) {
3518
+ nk_dots_i8_state_sapphireamx_t c_high_state, c_low_state;
3519
+ nk_dots_i8_a16x64_sapphireamx_t b_as_a;
3520
+ nk_dots_i8_b64x16_sapphireamx_t b_tile;
3521
+
3522
+ _tile_zero(4);
3523
+ _tile_zero(6);
3524
+
3525
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
3526
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
3527
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
3528
+
3529
+ nk_dots_e2m3_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
3530
+ a_stride_bytes, rows_in_high_tile, valid_depth);
3531
+ if (rows_in_low_tile > 0) {
3532
+ nk_dots_e2m3_load_a_sapphireamx_(&a_tile_bottom,
3533
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
3534
+ a_stride_bytes, rows_in_low_tile, valid_depth);
3535
+ }
3536
+
3537
+ // B edge data is already in I8 format
3538
+ nk_dots_i8_load_a_sapphireamx_(&b_as_a, col_edge_ptr + depth_offset, depth, column_remainder_count,
3539
+ valid_depth);
3540
+ nk_dots_pack_i8_transposed_sapphireamx_(&b_as_a, &b_tile);
3541
+
3542
+ _tile_loadd(0, a_tile_top.data, 64);
3543
+ _tile_loadd(1, a_tile_bottom.data, 64);
3544
+ _tile_loadd(2, b_tile.data, 64);
3545
+
3546
+ _tile_dpbssd(4, 0, 2);
3547
+ _tile_dpbssd(6, 1, 2);
3548
+ }
3549
+
3550
+ _tile_stored(4, c_high_state.data, 64);
3551
+ _tile_stored(6, c_low_state.data, 64);
3552
+
3553
+ nk_dots_e2m3_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + full_cols,
3554
+ c_stride_elements, rows_in_high_tile, column_remainder_count);
3555
+ if (rows_in_low_tile > 0) {
3556
+ nk_dots_e2m3_store_sapphireamx_(&c_low_state,
3557
+ c + (row_block_start + 16) * c_stride_elements + full_cols,
3558
+ c_stride_elements, rows_in_low_tile, column_remainder_count);
3559
+ }
3560
+ }
3561
+ }
3562
+
3563
+ _tile_release();
3564
+ }
3565
+
3566
+ NK_PUBLIC void nk_dots_symmetric_e2m3_sapphireamx( //
3567
+ nk_e2m3_t const *vectors, nk_size_t vectors_count, nk_size_t depth, //
3568
+ nk_size_t stride_in_bytes, nk_f32_t *result, nk_size_t result_stride_in_bytes, //
3569
+ nk_size_t row_start, nk_size_t row_count) {
3570
+
3571
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f32_t);
3572
+
3573
+ // Handle row slicing: compute rows [row_start, row_end)
3574
+ nk_size_t const row_end = (row_count == 0)
3575
+ ? vectors_count
3576
+ : (row_start + row_count < vectors_count ? row_start + row_count : vectors_count);
3577
+
3578
+ // Round depth up to multiple of 192 (3 tiles x 64 elements)
3579
+ nk_size_t const depth_tiles = nk_size_divide_round_up_(depth, 64);
3580
+ nk_size_t const depth_tile_groups = nk_size_divide_round_up_(depth_tiles, 3);
3581
+
3582
+ nk_dots_i8_a16x64_sapphireamx_t a_tiles[3];
3583
+ nk_dots_i8_a16x64_sapphireamx_t b_src_tiles[3];
3584
+ nk_dots_i8_b64x16_sapphireamx_t b_tiles[3];
3585
+ nk_dots_i8_state_sapphireamx_t state;
3586
+
3587
+ nk_amx_tile_configure_sapphireamx_();
3588
+
3589
+ for (nk_size_t row_tile = row_start; row_tile < row_end; row_tile += 16) {
3590
+ nk_size_t const valid_rows = (row_tile + 16 <= row_end) ? 16 : (row_end - row_tile);
3591
+
3592
+ for (nk_size_t col_tile = 0; col_tile < vectors_count; col_tile += 16) {
3593
+ nk_size_t const valid_cols = (col_tile + 16 <= vectors_count) ? 16 : (vectors_count - col_tile);
3594
+
3595
+ nk_dots_i8_init_sapphireamx_(&state);
3596
+
3597
+ for (nk_size_t depth_group_idx = 0; depth_group_idx < depth_tile_groups; depth_group_idx++) {
3598
+ nk_size_t const depth_base = depth_group_idx * 192;
3599
+
3600
+ for (int tile_idx = 0; tile_idx < 3; tile_idx++) {
3601
+ nk_size_t const depth_start = depth_base + tile_idx * 64;
3602
+ nk_size_t const valid_depth = (depth_start + 64 <= depth)
3603
+ ? 64
3604
+ : (depth > depth_start ? depth - depth_start : 0);
3605
+
3606
+ nk_dots_e2m3_load_a_sapphireamx_( //
3607
+ &a_tiles[tile_idx], //
3608
+ vectors + row_tile * stride_in_bytes + depth_start, //
3609
+ stride_in_bytes, valid_rows, valid_depth);
3610
+
3611
+ if (row_tile == col_tile) {
3612
+ nk_dots_pack_i8_transposed_sapphireamx_(&a_tiles[tile_idx], &b_tiles[tile_idx]);
3613
+ }
3614
+ else {
3615
+ nk_dots_e2m3_load_a_sapphireamx_( //
3616
+ &b_src_tiles[tile_idx], //
3617
+ vectors + col_tile * stride_in_bytes + depth_start, //
3618
+ stride_in_bytes, valid_cols, valid_depth);
3619
+ nk_dots_pack_i8_transposed_sapphireamx_(&b_src_tiles[tile_idx], &b_tiles[tile_idx]);
3620
+ }
3621
+ }
3622
+
3623
+ nk_dots_i8_update_sapphireamx_( //
3624
+ &state, &a_tiles[0], &a_tiles[1], &a_tiles[2], &b_tiles[0], &b_tiles[1], &b_tiles[2]);
3625
+ }
3626
+
3627
+ nk_dots_e2m3_store_sapphireamx_( //
3628
+ &state, result + row_tile * result_stride_elements + col_tile, //
3629
+ result_stride_elements, valid_rows, valid_cols);
3630
+ }
3631
+ }
3632
+ }
3633
+
3634
+ #pragma endregion E2M3 Floats
3635
+
3636
+ #pragma region E3M2 Floats
3637
+
3638
+ /* Load E3M2 A tile with FP8 to BF16 conversion */
3639
+ NK_INTERNAL void nk_dots_e3m2_load_a_sapphireamx_( //
3640
+ nk_dots_bf16_a16x32_sapphireamx_t *a_tile, //
3641
+ nk_e3m2_t const *src, nk_size_t src_stride, //
3642
+ nk_size_t valid_rows, nk_size_t valid_cols) {
3643
+
3644
+ __mmask32 column_mask = (valid_cols >= 32) ? 0xFFFFFFFF : ((__mmask32)1 << valid_cols) - 1;
3645
+ __m512i zero_i16x32 = _mm512_setzero_si512();
3646
+
3647
+ for (nk_size_t row_idx = 0; row_idx < 16; row_idx++) {
3648
+ if (row_idx < valid_rows) {
3649
+ __m256i e3m2_row_u8x32 = _mm256_maskz_loadu_epi8(column_mask, src + row_idx * src_stride);
3650
+ __m512i bf16_row_i16x32 = nk_e3m2x32_to_bf16x32_icelake_(e3m2_row_u8x32);
3651
+ _mm512_store_si512((__m512i *)a_tile->data[row_idx], bf16_row_i16x32);
3652
+ }
3653
+ else { _mm512_store_si512((__m512i *)a_tile->data[row_idx], zero_i16x32); }
3654
+ }
3655
+ nk_compiler_barrier_sapphireamx_();
3656
+ }
3657
+
3658
+ NK_PUBLIC nk_size_t nk_dots_packed_size_e3m2_sapphireamx(nk_size_t column_count, nk_size_t depth) {
3659
+ return nk_dots_packed_size_bf16_sapphireamx(column_count, depth);
3660
+ }
3661
+
3662
+ NK_PUBLIC void nk_dots_pack_e3m2_sapphireamx( //
3663
+ nk_e3m2_t const *b, nk_size_t column_count, nk_size_t depth, //
3664
+ nk_size_t b_stride_in_bytes, void *b_packed) {
3665
+
3666
+ nk_size_t const tmm_rows = 16;
3667
+ nk_size_t const tmm_cols = 32;
3668
+ nk_size_t const tile_elements = 512;
3669
+ nk_size_t const tile_bytes = tile_elements * sizeof(nk_bf16_t);
3670
+
3671
+ nk_size_t const column_tiles_count = column_count / tmm_rows;
3672
+ nk_size_t const depth_tiles_count = nk_size_divide_round_up_(depth, tmm_cols);
3673
+ nk_size_t const column_remainder_count = column_count - column_tiles_count * tmm_rows;
3674
+ nk_size_t const total_tiles = column_tiles_count * depth_tiles_count;
3675
+
3676
+ nk_dots_amx_packed_header_t *header = (nk_dots_amx_packed_header_t *)b_packed;
3677
+ header->full_column_tiles = (nk_u32_t)column_tiles_count;
3678
+ header->full_depth_tiles = (nk_u32_t)depth_tiles_count;
3679
+ header->column_remainder_count = (nk_u32_t)column_remainder_count;
3680
+
3681
+ nk_size_t const tiles_offset = sizeof(nk_dots_amx_packed_header_t);
3682
+ nk_size_t const column_edge_offset = tiles_offset + total_tiles * tile_bytes;
3683
+ header->column_edge_offset = (nk_u32_t)column_edge_offset;
3684
+
3685
+ nk_bf16_t *tiles_ptr = (nk_bf16_t *)((char *)b_packed + tiles_offset);
3686
+ nk_bf16_t *column_edge_ptr = (nk_bf16_t *)((char *)b_packed + column_edge_offset);
3687
+
3688
+ // Pack tiles using vectorized convert + SIMD transpose
3689
+ for (nk_size_t column_tile_idx = 0; column_tile_idx < column_tiles_count; column_tile_idx++) {
3690
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
3691
+ nk_size_t const tile_index = column_tile_idx * depth_tiles_count + depth_tile_idx;
3692
+ nk_bf16_t *tile_output = tiles_ptr + tile_index * tile_elements;
3693
+
3694
+ nk_size_t const src_row_start = column_tile_idx * tmm_rows;
3695
+ nk_size_t const src_column_start = depth_tile_idx * tmm_cols;
3696
+ nk_size_t const columns_to_pack = (src_column_start + tmm_cols <= depth) ? tmm_cols
3697
+ : (depth - src_column_start);
3698
+
3699
+ __mmask32 column_mask = (columns_to_pack >= 32) ? 0xFFFFFFFF : ((__mmask32)1 << columns_to_pack) - 1;
3700
+ nk_dots_bf16_a16x32_sapphireamx_t source_tile;
3701
+ for (nk_size_t row_idx = 0; row_idx < tmm_rows; row_idx++) {
3702
+ __m256i e3m2_row_u8x32 = _mm256_maskz_loadu_epi8(
3703
+ column_mask, b + (src_row_start + row_idx) * b_stride_in_bytes + src_column_start);
3704
+ _mm512_store_si512(&source_tile.data[row_idx][0], nk_e3m2x32_to_bf16x32_icelake_(e3m2_row_u8x32));
3705
+ }
3706
+
3707
+ nk_dots_bf16_b32x16_sapphireamx_t transposed_tile;
3708
+ nk_dots_pack_bf16_transposed_sapphireamx_(&source_tile, &transposed_tile);
3709
+ for (nk_size_t i = 0; i < tile_bytes; i += 64)
3710
+ _mm512_storeu_si512((char *)tile_output + i, _mm512_load_si512((char const *)&transposed_tile + i));
3711
+ }
3712
+ }
3713
+
3714
+ if (column_remainder_count > 0) {
3715
+ nk_size_t const remainder_start_row = column_tiles_count * tmm_rows;
3716
+ for (nk_size_t row_idx = 0; row_idx < column_remainder_count; row_idx++) {
3717
+ for (nk_size_t column_idx = 0; column_idx < depth; column_idx += 32) {
3718
+ nk_size_t columns = (column_idx + 32 <= depth) ? 32 : (depth - column_idx);
3719
+ __mmask32 column_mask = (columns >= 32) ? 0xFFFFFFFF : ((__mmask32)1 << columns) - 1;
3720
+ __m256i e3m2_chunk_u8x32 = _mm256_maskz_loadu_epi8(
3721
+ column_mask, b + (remainder_start_row + row_idx) * b_stride_in_bytes + column_idx);
3722
+ __m512i bf16_chunk_i16x32 = nk_e3m2x32_to_bf16x32_icelake_(e3m2_chunk_u8x32);
3723
+ _mm512_mask_storeu_epi16(column_edge_ptr + row_idx * depth + column_idx, column_mask,
3724
+ bf16_chunk_i16x32);
3725
+ }
3726
+ }
3727
+ }
3728
+
3729
+ // Compute and store per-column norms for angular/euclidean distance
3730
+ nk_size_t norms_offset = column_edge_offset +
3731
+ (column_remainder_count > 0 ? column_remainder_count * depth * sizeof(nk_bf16_t) : 0);
3732
+ header->norms_byte_offset = (nk_u32_t)norms_offset;
3733
+ nk_f32_t *norms = (nk_f32_t *)((char *)b_packed + norms_offset);
3734
+ for (nk_size_t col = 0; col < column_count; col++)
3735
+ norms[col] = nk_dots_reduce_sumsq_e3m2_(b + col * b_stride_in_bytes, depth);
3736
+ }
3737
+
3738
+ NK_PUBLIC void nk_dots_packed_e3m2_sapphireamx( //
3739
+ nk_e3m2_t const *a, void const *b_packed, nk_f32_t *c, //
3740
+ nk_size_t rows_count, nk_size_t cols_count, nk_size_t depth, nk_size_t a_stride_bytes, nk_size_t c_stride_bytes) {
3741
+ nk_unused_(cols_count);
3742
+
3743
+ nk_dots_amx_packed_header_t const *header = (nk_dots_amx_packed_header_t const *)b_packed;
3744
+ nk_size_t const column_tiles_count = header->full_column_tiles;
3745
+ nk_size_t const depth_tiles_count = header->full_depth_tiles;
3746
+ nk_size_t const column_remainder_count = header->column_remainder_count;
3747
+
3748
+ nk_bf16_t const *b_tiles_base = (nk_bf16_t const *)((char const *)b_packed + sizeof(nk_dots_amx_packed_header_t));
3749
+ nk_bf16_t const *col_edge_ptr = (nk_bf16_t const *)((char const *)b_packed + header->column_edge_offset);
3750
+
3751
+ nk_size_t const c_stride_elements = c_stride_bytes / sizeof(nk_f32_t);
3752
+ nk_size_t const tile_depth = 32;
3753
+ nk_size_t const tile_size = 512;
3754
+ nk_size_t const full_cols = column_tiles_count * 16;
3755
+
3756
+ nk_size_t const row_blocks_count = nk_size_divide_round_up_(rows_count, 32);
3757
+ nk_size_t const col_blocks_count = column_tiles_count / 2;
3758
+
3759
+ if (depth_tiles_count == 0) return;
3760
+
3761
+ nk_dots_bf16_a16x32_sapphireamx_t a_tile_top, a_tile_bottom;
3762
+ nk_dots_bf16_state2x2_sapphireamx_t c_accum_buffer;
3763
+
3764
+ nk_size_t const full_depth_tiles_count = depth / tile_depth;
3765
+ nk_size_t const depth_remainder = depth % tile_depth;
3766
+
3767
+ nk_amx_tile_configure_sapphireamx_();
3768
+
3769
+ // Loop order: row_blocks outer, col_blocks inner
3770
+ for (nk_size_t row_block_idx = 0; row_block_idx < row_blocks_count; row_block_idx++) {
3771
+ nk_size_t const row_block_start = row_block_idx * 32;
3772
+ nk_size_t const valid_rows_count = (row_block_start + 32 <= rows_count) ? 32 : (rows_count - row_block_start);
3773
+ nk_size_t const is_full_row_block = (valid_rows_count == 32);
3774
+ nk_size_t const rows_in_high_tile = (valid_rows_count > 16) ? 16 : valid_rows_count;
3775
+ nk_size_t const rows_in_low_tile = (valid_rows_count > 16) ? valid_rows_count - 16 : 0;
3776
+
3777
+ for (nk_size_t column_block_idx = 0; column_block_idx < col_blocks_count; column_block_idx++) {
3778
+ nk_size_t const col_block_start = column_block_idx * 32;
3779
+ nk_size_t const b_column_left_base = (column_block_idx * 2) * depth_tiles_count;
3780
+ nk_size_t const b_column_right_base = (column_block_idx * 2 + 1) * depth_tiles_count;
3781
+
3782
+ // Zero accumulators (TMM4-7 stay resident across entire depth loop)
3783
+ _tile_zero(4);
3784
+ _tile_zero(5);
3785
+ _tile_zero(6);
3786
+ _tile_zero(7);
3787
+
3788
+ // FP8 always uses buffered load for E3M2 -> BF16 conversion
3789
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
3790
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
3791
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
3792
+
3793
+ // Load A with FP8 -> BF16 conversion
3794
+ nk_dots_e3m2_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
3795
+ a_stride_bytes, rows_in_high_tile, valid_depth);
3796
+ if (rows_in_low_tile > 0) {
3797
+ nk_dots_e3m2_load_a_sapphireamx_(&a_tile_bottom,
3798
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
3799
+ a_stride_bytes, rows_in_low_tile, valid_depth);
3800
+ }
3801
+
3802
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_left =
3803
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base +
3804
+ (b_column_left_base + depth_tile_idx) * tile_size);
3805
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile_right =
3806
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base +
3807
+ (b_column_right_base + depth_tile_idx) * tile_size);
3808
+
3809
+ _tile_loadd(0, a_tile_top.data, 64);
3810
+ _tile_loadd(1, a_tile_bottom.data, 64);
3811
+ _tile_loadd(2, b_tile_left->data, 64);
3812
+ _tile_loadd(3, b_tile_right->data, 64);
3813
+
3814
+ _tile_dpbf16ps(4, 0, 2);
3815
+ _tile_dpbf16ps(5, 0, 3);
3816
+ _tile_dpbf16ps(6, 1, 2);
3817
+ _tile_dpbf16ps(7, 1, 3);
3818
+ }
3819
+
3820
+ // Store accumulators to output (once per output block)
3821
+ if (is_full_row_block) {
3822
+ nk_f32_t *c_block = c + row_block_start * c_stride_elements + col_block_start;
3823
+ _tile_stored(4, c_block, c_stride_bytes);
3824
+ _tile_stored(5, c_block + 16, c_stride_bytes);
3825
+ _tile_stored(6, (nk_f32_t *)((char *)c_block + 16 * c_stride_bytes), c_stride_bytes);
3826
+ _tile_stored(7, (nk_f32_t *)((char *)c_block + 16 * c_stride_bytes) + 16, c_stride_bytes);
3827
+ }
3828
+ else {
3829
+ _tile_stored(4, c_accum_buffer.c[0][0].data, 64);
3830
+ _tile_stored(5, c_accum_buffer.c[0][1].data, 64);
3831
+ _tile_stored(6, c_accum_buffer.c[1][0].data, 64);
3832
+ _tile_stored(7, c_accum_buffer.c[1][1].data, 64);
3833
+ nk_dots_bf16_output2x2_sapphireamx_(&c_accum_buffer,
3834
+ c + row_block_start * c_stride_elements + col_block_start,
3835
+ c_stride_elements, valid_rows_count, 32);
3836
+ }
3837
+ }
3838
+
3839
+ // Handle odd column-tile (single 16-column tile if column_tiles_count is odd)
3840
+ if (column_tiles_count % 2 == 1) {
3841
+ nk_size_t const column_tile_idx = column_tiles_count - 1;
3842
+ nk_size_t const col_start = column_tile_idx * 16;
3843
+ nk_size_t const b_column_base = column_tile_idx * depth_tiles_count;
3844
+
3845
+ nk_dots_bf16_state_sapphireamx_t c_high_state, c_low_state;
3846
+ _tile_zero(4);
3847
+ _tile_zero(6);
3848
+
3849
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
3850
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
3851
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
3852
+
3853
+ nk_dots_e3m2_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
3854
+ a_stride_bytes, rows_in_high_tile, valid_depth);
3855
+ if (rows_in_low_tile > 0) {
3856
+ nk_dots_e3m2_load_a_sapphireamx_(&a_tile_bottom,
3857
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
3858
+ a_stride_bytes, rows_in_low_tile, valid_depth);
3859
+ }
3860
+
3861
+ nk_dots_bf16_b32x16_sapphireamx_t const *b_tile =
3862
+ (nk_dots_bf16_b32x16_sapphireamx_t const *)(b_tiles_base +
3863
+ (b_column_base + depth_tile_idx) * tile_size);
3864
+
3865
+ _tile_loadd(0, a_tile_top.data, 64);
3866
+ _tile_loadd(1, a_tile_bottom.data, 64);
3867
+ _tile_loadd(2, b_tile->data, 64);
3868
+
3869
+ _tile_dpbf16ps(4, 0, 2);
3870
+ _tile_dpbf16ps(6, 1, 2);
3871
+ }
3872
+
3873
+ _tile_stored(4, c_high_state.data, 64);
3874
+ _tile_stored(6, c_low_state.data, 64);
3875
+
3876
+ nk_dots_bf16_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + col_start,
3877
+ c_stride_elements, rows_in_high_tile, 16);
3878
+ if (rows_in_low_tile > 0) {
3879
+ nk_dots_bf16_store_sapphireamx_(&c_low_state,
3880
+ c + (row_block_start + 16) * c_stride_elements + col_start,
3881
+ c_stride_elements, rows_in_low_tile, 16);
3882
+ }
3883
+ }
3884
+
3885
+ // Handle column-edge (remaining columns < 16) using AMX with partial tiles
3886
+ if (column_remainder_count > 0) {
3887
+ nk_dots_bf16_state_sapphireamx_t c_high_state, c_low_state;
3888
+ nk_dots_bf16_a16x32_sapphireamx_t b_as_a;
3889
+ nk_dots_bf16_b32x16_sapphireamx_t b_tile;
3890
+
3891
+ _tile_zero(4);
3892
+ _tile_zero(6);
3893
+
3894
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tiles_count; depth_tile_idx++) {
3895
+ nk_size_t const depth_offset = depth_tile_idx * tile_depth;
3896
+ nk_size_t const valid_depth = (depth_tile_idx < full_depth_tiles_count) ? tile_depth : depth_remainder;
3897
+
3898
+ nk_dots_e3m2_load_a_sapphireamx_(&a_tile_top, a + row_block_start * a_stride_bytes + depth_offset,
3899
+ a_stride_bytes, rows_in_high_tile, valid_depth);
3900
+ if (rows_in_low_tile > 0) {
3901
+ nk_dots_e3m2_load_a_sapphireamx_(&a_tile_bottom,
3902
+ a + (row_block_start + 16) * a_stride_bytes + depth_offset,
3903
+ a_stride_bytes, rows_in_low_tile, valid_depth);
3904
+ }
3905
+
3906
+ nk_dots_bf16_load_a_sapphireamx_(&b_as_a, col_edge_ptr + depth_offset, depth, column_remainder_count,
3907
+ valid_depth);
3908
+ nk_dots_pack_bf16_transposed_sapphireamx_(&b_as_a, &b_tile);
3909
+
3910
+ _tile_loadd(0, a_tile_top.data, 64);
3911
+ _tile_loadd(1, a_tile_bottom.data, 64);
3912
+ _tile_loadd(2, b_tile.data, 64);
3913
+
3914
+ _tile_dpbf16ps(4, 0, 2);
3915
+ _tile_dpbf16ps(6, 1, 2);
3916
+ }
3917
+
3918
+ _tile_stored(4, c_high_state.data, 64);
3919
+ _tile_stored(6, c_low_state.data, 64);
3920
+
3921
+ nk_dots_bf16_store_sapphireamx_(&c_high_state, c + row_block_start * c_stride_elements + full_cols,
3922
+ c_stride_elements, rows_in_high_tile, column_remainder_count);
3923
+ if (rows_in_low_tile > 0) {
3924
+ nk_dots_bf16_store_sapphireamx_(&c_low_state,
3925
+ c + (row_block_start + 16) * c_stride_elements + full_cols,
3926
+ c_stride_elements, rows_in_low_tile, column_remainder_count);
3927
+ }
3928
+ }
3929
+ }
3930
+
3931
+ _tile_release();
3932
+ }
3933
+
3934
+ NK_PUBLIC void nk_dots_symmetric_e3m2_sapphireamx( //
3935
+ nk_e3m2_t const *vectors, nk_size_t vectors_count, nk_size_t depth, //
3936
+ nk_size_t stride_in_bytes, nk_f32_t *result, nk_size_t result_stride_in_bytes, //
3937
+ nk_size_t row_start, nk_size_t row_count) {
3938
+
3939
+ nk_size_t const stride_elements = stride_in_bytes; // sizeof(nk_e3m2_t) == 1, so bytes == elements
3940
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f32_t);
3941
+
3942
+ // Handle row slicing: compute rows [row_start, row_end)
3943
+ nk_size_t const row_end = (row_count == 0)
3944
+ ? vectors_count
3945
+ : (row_start + row_count < vectors_count ? row_start + row_count : vectors_count);
3946
+
3947
+ // Round depth up to multiple of 96 (3 tiles x 32 bf16 elements)
3948
+ nk_size_t const depth_tiles = nk_size_divide_round_up_(depth, 32);
3949
+ nk_size_t const depth_tile_groups = nk_size_divide_round_up_(depth_tiles, 3);
3950
+
3951
+ nk_dots_bf16_a16x32_sapphireamx_t a_tiles[3];
3952
+ nk_dots_bf16_a16x32_sapphireamx_t b_src_tiles[3];
3953
+ nk_dots_bf16_b32x16_sapphireamx_t b_tiles[3];
3954
+ nk_dots_bf16_state_sapphireamx_t state;
3955
+
3956
+ nk_amx_tile_configure_sapphireamx_();
3957
+
3958
+ for (nk_size_t row_tile = row_start; row_tile < row_end; row_tile += 16) {
3959
+ nk_size_t const valid_rows = (row_tile + 16 <= row_end) ? 16 : (row_end - row_tile);
3960
+
3961
+ for (nk_size_t col_tile = 0; col_tile < vectors_count; col_tile += 16) {
3962
+ nk_size_t const valid_cols = (col_tile + 16 <= vectors_count) ? 16 : (vectors_count - col_tile);
3963
+
3964
+ nk_dots_bf16_init_sapphireamx_(&state);
3965
+
3966
+ for (nk_size_t depth_group_idx = 0; depth_group_idx < depth_tile_groups; depth_group_idx++) {
3967
+ nk_size_t const depth_base = depth_group_idx * 96;
3968
+
3969
+ for (int tile_idx = 0; tile_idx < 3; tile_idx++) {
3970
+ nk_size_t const depth_start = depth_base + tile_idx * 32;
3971
+ nk_size_t const valid_depth = (depth_start + 32 <= depth)
3972
+ ? 32
3973
+ : (depth > depth_start ? depth - depth_start : 0);
3974
+
3975
+ nk_dots_e3m2_load_a_sapphireamx_( //
3976
+ &a_tiles[tile_idx], //
3977
+ vectors + row_tile * stride_elements + depth_start, //
3978
+ stride_elements, valid_rows, valid_depth);
3979
+
3980
+ if (row_tile == col_tile) {
3981
+ nk_dots_pack_bf16_transposed_sapphireamx_(&a_tiles[tile_idx], &b_tiles[tile_idx]);
3982
+ }
3983
+ else {
3984
+ nk_dots_e3m2_load_a_sapphireamx_( //
3985
+ &b_src_tiles[tile_idx], //
3986
+ vectors + col_tile * stride_elements + depth_start, //
3987
+ stride_elements, valid_cols, valid_depth);
3988
+ nk_dots_pack_bf16_transposed_sapphireamx_(&b_src_tiles[tile_idx], &b_tiles[tile_idx]);
3989
+ }
3990
+ }
3991
+
3992
+ nk_dots_bf16_update_sapphireamx_( //
3993
+ &state, &a_tiles[0], &a_tiles[1], &a_tiles[2], &b_tiles[0], &b_tiles[1], &b_tiles[2]);
3994
+ }
3995
+
3996
+ nk_dots_bf16_store_sapphireamx_( //
3997
+ &state, result + row_tile * result_stride_elements + col_tile, //
3998
+ result_stride_elements, valid_rows, valid_cols);
3999
+ }
4000
+ }
4001
+ }
4002
+
4003
+ #pragma endregion E3M2 Floats
4004
+
4005
+ #if defined(__clang__)
4006
+ #pragma clang attribute pop
4007
+ #elif defined(__GNUC__)
4008
+ #pragma GCC pop_options
4009
+ #endif
4010
+
4011
+ #if defined(__cplusplus)
4012
+ } // extern "C"
4013
+ #endif
4014
+
4015
+ #endif // NK_TARGET_SAPPHIREAMX
4016
+ #endif // NK_TARGET_X8664_
4017
+ #endif // NK_DOTS_SAPPHIREAMX_H