usearch 2.21.4 → 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 +224 -48
  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,2593 @@
1
+ /**
2
+ * @brief SIMD-accelerated Batched Dot Products for RISC-V.
3
+ * @file include/numkong/dots/rvv.h
4
+ * @author Ash Vardanian
5
+ * @date February 6, 2026
6
+ *
7
+ * @sa include/numkong/dots.h
8
+ *
9
+ * Custom RVV-native register-tiled GEMM implementation, analogous to how AMX
10
+ * (dots/sapphireamx.h) and SME (dots/sme.h) each have their own unique implementations
11
+ * independent of the cross-product macros.
12
+ *
13
+ * RVV's variable-length vectors and widening multiply-accumulate (`vfwmacc`) make it
14
+ * fundamentally different from fixed-width SIMD. Key design choices:
15
+ *
16
+ * - f32 GEMM: Uses `vfwmacc_vv_f64m4` for f64 accumulation (vector-vector widened FMA),
17
+ * Process 4 rows per tile (rows_per_tile=4). Narrowed to f32 on store.
18
+ * - f64 GEMM: Uses `vfmul`+Kahan with Kahan compensation,
19
+ * Process 2 rows per tile (rows_per_tile=2, tighter register budget at LMUL=4).
20
+ * - B packing: Column-panel layout with cache-line padding. Each depth step stores
21
+ * contiguous elements along depth — one `vle32`/`vle64` per vectorized chunk.
22
+ * - Edge handling: RVV's `vsetvl` returns actual VL for partial vectors — no separate
23
+ * edge kernel needed.
24
+ * - Vectorization axis: depth (k dimension). Each inner loop iteration loads a chunk of
25
+ * both A and B along depth, computing element-wise widened FMA.
26
+ *
27
+ * - e2m3 GEMM: Integer arithmetic via LUT (5-bit magnitude → i8 value×16).
28
+ * B is pre-packed as signed i8. A is converted on-the-fly via `vluxei8` gather.
29
+ * Uses `vwmul` (i8→i16) then `vwadd_wv` (i32+=i16) for K-vectorized accumulation.
30
+ * Final result scaled by 1/256. Process 4 rows per tile (rows_per_tile=4).
31
+ * - e3m2 GEMM: Integer arithmetic via LUT (5-bit magnitude → i16 value×16).
32
+ * B is pre-packed as signed i16. A is converted on-the-fly via `vluxei16` gather.
33
+ * Uses `vwmacc` (i16×i16→i32) for K-vectorized widening MAC.
34
+ * Final result scaled by 1/256. Process 2 rows per tile (rows_per_tile=2, wider accumulator elements).
35
+ * - e4m3 GEMM: f32 LUT gather (7-bit magnitude → f32 bit pattern, 128 entries).
36
+ * B is pre-packed as f32. A is converted on-the-fly via `vluxei32` gather with
37
+ * sign injection (bit 7 → bit 31). Uses `vfwmacc_vv_f64m4` for f64 accumulation.
38
+ * Process 2 rows per tile (rows_per_tile=2, u32m2 gather + f64m4 accumulator is register-heavy).
39
+ * - e5m2 GEMM: Same f32 LUT gather approach as e4m3, different LUT contents.
40
+ * E5M2 has 5 exponent bits (wider range, lower precision than e4m3).
41
+ * Process 2 rows per tile (rows_per_tile=2).
42
+ */
43
+ #ifndef NK_DOTS_RVV_H
44
+ #define NK_DOTS_RVV_H
45
+
46
+ #if NK_TARGET_RISCV64_
47
+ #if NK_TARGET_RVV
48
+
49
+ #include "numkong/types.h"
50
+ #include "numkong/dots/serial.h"
51
+ #include "numkong/cast/rvv.h" // `nk_bf16m1_to_f32m2_rvv_`
52
+
53
+ #if defined(__clang__)
54
+ #pragma clang attribute push(__attribute__((target("arch=+v"))), apply_to = function)
55
+ #elif defined(__GNUC__)
56
+ #pragma GCC push_options
57
+ #pragma GCC target("arch=+v")
58
+ #endif
59
+
60
+ #if defined(__cplusplus)
61
+ extern "C" {
62
+ #endif
63
+
64
+ /**
65
+ * @brief E2M3 magnitude LUT: 5-bit magnitude → unsigned value×16 (u8).
66
+ * Shared across scalar helper, packed kernel, and symmetric kernel.
67
+ */
68
+ static nk_u8_t const nk_e2m3_magnitude_lut_rvv_[32] = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20,
69
+ 22, 24, 26, 28, 30, 32, 36, 40, 44, 48, 52,
70
+ 56, 60, 64, 72, 80, 88, 96, 104, 112, 120};
71
+
72
+ /**
73
+ * @brief E3M2 magnitude LUT: 5-bit magnitude → unsigned value×16 (u16).
74
+ * Shared across scalar helper, packed kernel, and symmetric kernel.
75
+ */
76
+ static nk_u16_t const nk_e3m2_magnitude_lut_rvv_[32] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12,
77
+ 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80,
78
+ 96, 112, 128, 160, 192, 224, 256, 320, 384, 448};
79
+
80
+ #pragma region F32 Floats
81
+
82
+ NK_PUBLIC nk_size_t nk_dots_packed_size_f32_rvv(nk_size_t column_count, nk_size_t depth) {
83
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
84
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
85
+ // Break power-of-2 strides for cache associativity
86
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_f32_t);
87
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
88
+ return sizeof(nk_cross_packed_buffer_header_t) + column_count * depth_padded * sizeof(nk_f32_t) +
89
+ column_count * sizeof(nk_f64_t); // per-column norms
90
+ }
91
+
92
+ NK_PUBLIC void nk_dots_pack_f32_rvv(nk_f32_t const *b, nk_size_t column_count, nk_size_t depth,
93
+ nk_size_t b_stride_in_bytes, void *b_packed) {
94
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
95
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
96
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_f32_t);
97
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
98
+
99
+ nk_cross_packed_buffer_header_t *header = (nk_cross_packed_buffer_header_t *)b_packed;
100
+ header->column_count = (nk_u32_t)column_count;
101
+ header->depth_dimensions = (nk_u32_t)depth;
102
+ header->depth_padded_values = (nk_u32_t)depth_padded;
103
+
104
+ nk_f32_t *packed = (nk_f32_t *)((char *)b_packed + sizeof(nk_cross_packed_buffer_header_t));
105
+ nk_size_t total = column_count * depth_padded;
106
+ {
107
+ nk_u8_t *zero_ptr = (nk_u8_t *)packed;
108
+ nk_size_t total_bytes = total * sizeof(nk_f32_t);
109
+ for (nk_size_t i = 0; i < total_bytes;) {
110
+ nk_size_t vector_length = __riscv_vsetvl_e8m8(total_bytes - i);
111
+ __riscv_vse8_v_u8m8(zero_ptr + i, __riscv_vmv_v_x_u8m8(0, vector_length), vector_length);
112
+ i += vector_length;
113
+ }
114
+ }
115
+
116
+ for (nk_size_t column = 0; column < column_count; ++column) {
117
+ nk_f32_t const *src = (nk_f32_t const *)((char const *)b + column * b_stride_in_bytes);
118
+ nk_f32_t *dst = packed + column * depth_padded;
119
+ for (nk_size_t k = 0; k < depth;) {
120
+ nk_size_t vector_length = __riscv_vsetvl_e32m8(depth - k);
121
+ __riscv_vse32_v_f32m8(dst + k, __riscv_vle32_v_f32m8(src + k, vector_length), vector_length);
122
+ k += vector_length;
123
+ }
124
+ }
125
+
126
+ // Append per-column norms after packed data
127
+ nk_f64_t *norms = (nk_f64_t *)(packed + total);
128
+ for (nk_size_t column = 0; column < column_count; ++column) {
129
+ nk_f32_t const *src = (nk_f32_t const *)((char const *)b + column * b_stride_in_bytes);
130
+ norms[column] = nk_dots_reduce_sumsq_f32_(src, depth);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * @brief f32 packed GEMM kernel: C += A * B_packed^T with f64 widened accumulation.
136
+ *
137
+ * Vectorizes over the depth dimension (k). For each (row, column) pair:
138
+ * acc_f64 = sum_k f64(a[row][k]) * f64(b_packed[column][k])
139
+ * using `vfwmacc_vv_f64m4` which widens both operands from f32m2 to f64m4.
140
+ *
141
+ * Register tile: process 4 rows per iteration (rows_per_tile=4).
142
+ * Each row loads its own A vector; B vector is shared across rows per depth chunk.
143
+ */
144
+ NK_INTERNAL void nk_dots_packed_f32_rvv_aligned_(nk_f32_t const *a_matrix, void const *b_packed_buffer,
145
+ nk_f64_t *c_matrix, nk_size_t row_count, nk_size_t column_count,
146
+ nk_size_t depth, nk_size_t a_stride_in_bytes,
147
+ nk_size_t c_stride_in_bytes) {
148
+ nk_cross_packed_buffer_header_t const *header = (nk_cross_packed_buffer_header_t const *)b_packed_buffer;
149
+ nk_size_t const depth_padded = header->depth_padded_values;
150
+ nk_f32_t const *packed_data = (nk_f32_t const *)((char const *)b_packed_buffer +
151
+ sizeof(nk_cross_packed_buffer_header_t));
152
+
153
+ // Zero output matrix
154
+ for (nk_size_t i = 0; i < row_count; ++i) {
155
+ nk_f64_t *c_row = (nk_f64_t *)((char *)c_matrix + i * c_stride_in_bytes);
156
+ for (nk_size_t j = 0; j < column_count; ++j) c_row[j] = 0;
157
+ }
158
+
159
+ // mr=4 register tile over rows
160
+ nk_size_t row = 0;
161
+ for (; row + 4 <= row_count; row += 4) {
162
+ nk_f32_t const *a_row_0 = (nk_f32_t const *)((char const *)a_matrix + (row + 0) * a_stride_in_bytes);
163
+ nk_f32_t const *a_row_1 = (nk_f32_t const *)((char const *)a_matrix + (row + 1) * a_stride_in_bytes);
164
+ nk_f32_t const *a_row_2 = (nk_f32_t const *)((char const *)a_matrix + (row + 2) * a_stride_in_bytes);
165
+ nk_f32_t const *a_row_3 = (nk_f32_t const *)((char const *)a_matrix + (row + 3) * a_stride_in_bytes);
166
+ nk_f64_t *c_row_0 = (nk_f64_t *)((char *)c_matrix + (row + 0) * c_stride_in_bytes);
167
+ nk_f64_t *c_row_1 = (nk_f64_t *)((char *)c_matrix + (row + 1) * c_stride_in_bytes);
168
+ nk_f64_t *c_row_2 = (nk_f64_t *)((char *)c_matrix + (row + 2) * c_stride_in_bytes);
169
+ nk_f64_t *c_row_3 = (nk_f64_t *)((char *)c_matrix + (row + 3) * c_stride_in_bytes);
170
+
171
+ for (nk_size_t column = 0; column < column_count; ++column) {
172
+ nk_f32_t const *b_column = packed_data + column * depth_padded;
173
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
174
+ vfloat64m4_t accumulator_0_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
175
+ vfloat64m4_t accumulator_1_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
176
+ vfloat64m4_t accumulator_2_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
177
+ vfloat64m4_t accumulator_3_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
178
+
179
+ nk_size_t remaining = depth;
180
+ nk_size_t k = 0;
181
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
182
+ vector_length = __riscv_vsetvl_e32m2(remaining);
183
+ vfloat32m2_t b_vector_f32m2 = __riscv_vle32_v_f32m2(b_column + k, vector_length);
184
+ vfloat32m2_t a_vector_0_f32m2 = __riscv_vle32_v_f32m2(a_row_0 + k, vector_length);
185
+ vfloat32m2_t a_vector_1_f32m2 = __riscv_vle32_v_f32m2(a_row_1 + k, vector_length);
186
+ vfloat32m2_t a_vector_2_f32m2 = __riscv_vle32_v_f32m2(a_row_2 + k, vector_length);
187
+ vfloat32m2_t a_vector_3_f32m2 = __riscv_vle32_v_f32m2(a_row_3 + k, vector_length);
188
+ accumulator_0_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_0_f64m4, a_vector_0_f32m2, b_vector_f32m2,
189
+ vector_length);
190
+ accumulator_1_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_1_f64m4, a_vector_1_f32m2, b_vector_f32m2,
191
+ vector_length);
192
+ accumulator_2_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_2_f64m4, a_vector_2_f32m2, b_vector_f32m2,
193
+ vector_length);
194
+ accumulator_3_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_3_f64m4, a_vector_3_f32m2, b_vector_f32m2,
195
+ vector_length);
196
+ }
197
+
198
+ // Horizontal reduce directly to f64
199
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
200
+ c_row_0[column] = __riscv_vfmv_f_s_f64m1_f64(
201
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_0_f64m4, zero_f64m1, max_vector_length));
202
+ c_row_1[column] = __riscv_vfmv_f_s_f64m1_f64(
203
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_1_f64m4, zero_f64m1, max_vector_length));
204
+ c_row_2[column] = __riscv_vfmv_f_s_f64m1_f64(
205
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_2_f64m4, zero_f64m1, max_vector_length));
206
+ c_row_3[column] = __riscv_vfmv_f_s_f64m1_f64(
207
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_3_f64m4, zero_f64m1, max_vector_length));
208
+ }
209
+ }
210
+ // Remainder rows (mr < 4)
211
+ for (; row < row_count; ++row) {
212
+ nk_f32_t const *a_row = (nk_f32_t const *)((char const *)a_matrix + row * a_stride_in_bytes);
213
+ nk_f64_t *c_row = (nk_f64_t *)((char *)c_matrix + row * c_stride_in_bytes);
214
+ for (nk_size_t column = 0; column < column_count; ++column) {
215
+ nk_f32_t const *b_column = packed_data + column * depth_padded;
216
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
217
+ vfloat64m4_t accumulator_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
218
+ nk_size_t remaining = depth;
219
+ nk_size_t k = 0;
220
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
221
+ vector_length = __riscv_vsetvl_e32m2(remaining);
222
+ vfloat32m2_t b_vector_f32m2 = __riscv_vle32_v_f32m2(b_column + k, vector_length);
223
+ vfloat32m2_t a_vector_f32m2 = __riscv_vle32_v_f32m2(a_row + k, vector_length);
224
+ accumulator_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_f64m4, a_vector_f32m2, b_vector_f32m2,
225
+ vector_length);
226
+ }
227
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
228
+ c_row[column] = __riscv_vfmv_f_s_f64m1_f64(
229
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_f64m4, zero_f64m1, max_vector_length));
230
+ }
231
+ }
232
+ }
233
+
234
+ /**
235
+ * @brief Public f32 packed GEMM wrapper matching the declared signature in dots.h.
236
+ *
237
+ * Dispatches to the aligned kernel for all cases — RVV's `vsetvl` handles partial
238
+ * vectors naturally, so no separate edge kernel is needed.
239
+ */
240
+ NK_PUBLIC void nk_dots_packed_f32_rvv(nk_f32_t const *a, void const *b_packed, nk_f64_t *c, nk_size_t rows,
241
+ nk_size_t columns, nk_size_t depth, nk_size_t a_stride_in_bytes,
242
+ nk_size_t c_stride_in_bytes) {
243
+ nk_dots_packed_f32_rvv_aligned_(a, b_packed, c, rows, columns, depth, a_stride_in_bytes, c_stride_in_bytes);
244
+ }
245
+
246
+ /**
247
+ * @brief Symmetric f32 GEMM: C = A * A^T, upper triangle + mirror.
248
+ *
249
+ * Uses f64 widened accumulation via `vfwmacc_vv_f64m4` for precision.
250
+ * Processes only the rows in [row_start, row_start + row_count) for parallelism.
251
+ */
252
+ NK_PUBLIC void nk_dots_symmetric_f32_rvv(nk_f32_t const *vectors, nk_size_t vectors_count, nk_size_t depth,
253
+ nk_size_t stride_in_bytes, nk_f64_t *result, nk_size_t result_stride_in_bytes,
254
+ nk_size_t row_start, nk_size_t row_count) {
255
+ nk_size_t const stride_elements = stride_in_bytes / sizeof(nk_f32_t);
256
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f64_t);
257
+ nk_size_t const row_end = (row_start + row_count < vectors_count) ? (row_start + row_count) : vectors_count;
258
+
259
+ for (nk_size_t i = row_start; i < row_end; ++i) {
260
+ nk_f32_t const *a_i = vectors + i * stride_elements;
261
+ for (nk_size_t j = i; j < vectors_count; ++j) {
262
+ nk_f32_t const *a_j = vectors + j * stride_elements;
263
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
264
+ vfloat64m4_t accumulator_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
265
+ nk_size_t remaining = depth;
266
+ nk_size_t k = 0;
267
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
268
+ vector_length = __riscv_vsetvl_e32m2(remaining);
269
+ vfloat32m2_t a_vector_f32m2 = __riscv_vle32_v_f32m2(a_i + k, vector_length);
270
+ vfloat32m2_t b_vector_f32m2 = __riscv_vle32_v_f32m2(a_j + k, vector_length);
271
+ accumulator_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_f64m4, a_vector_f32m2, b_vector_f32m2,
272
+ vector_length);
273
+ }
274
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
275
+ nk_f64_t dot = __riscv_vfmv_f_s_f64m1_f64(
276
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_f64m4, zero_f64m1, max_vector_length));
277
+ result[i * result_stride_elements + j] = dot;
278
+ }
279
+ }
280
+ }
281
+
282
+ #pragma endregion F32 Floats
283
+
284
+ #pragma region F64 Floats
285
+
286
+ NK_PUBLIC nk_size_t nk_dots_packed_size_f64_rvv(nk_size_t column_count, nk_size_t depth) {
287
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e64m4();
288
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
289
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_f64_t);
290
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
291
+ return sizeof(nk_cross_packed_buffer_header_t) + column_count * depth_padded * sizeof(nk_f64_t) +
292
+ column_count * sizeof(nk_f64_t); // per-column norms
293
+ }
294
+
295
+ NK_PUBLIC void nk_dots_pack_f64_rvv(nk_f64_t const *b, nk_size_t column_count, nk_size_t depth,
296
+ nk_size_t b_stride_in_bytes, void *b_packed) {
297
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e64m4();
298
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
299
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_f64_t);
300
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
301
+
302
+ nk_cross_packed_buffer_header_t *header = (nk_cross_packed_buffer_header_t *)b_packed;
303
+ header->column_count = (nk_u32_t)column_count;
304
+ header->depth_dimensions = (nk_u32_t)depth;
305
+ header->depth_padded_values = (nk_u32_t)depth_padded;
306
+
307
+ nk_f64_t *packed = (nk_f64_t *)((char *)b_packed + sizeof(nk_cross_packed_buffer_header_t));
308
+ nk_size_t total = column_count * depth_padded;
309
+ {
310
+ nk_u8_t *zero_ptr = (nk_u8_t *)packed;
311
+ nk_size_t total_bytes = total * sizeof(nk_f64_t);
312
+ for (nk_size_t i = 0; i < total_bytes;) {
313
+ nk_size_t vector_length = __riscv_vsetvl_e8m8(total_bytes - i);
314
+ __riscv_vse8_v_u8m8(zero_ptr + i, __riscv_vmv_v_x_u8m8(0, vector_length), vector_length);
315
+ i += vector_length;
316
+ }
317
+ }
318
+
319
+ for (nk_size_t column = 0; column < column_count; ++column) {
320
+ nk_f64_t const *src = (nk_f64_t const *)((char const *)b + column * b_stride_in_bytes);
321
+ nk_f64_t *dst = packed + column * depth_padded;
322
+ for (nk_size_t k = 0; k < depth;) {
323
+ nk_size_t vector_length = __riscv_vsetvl_e64m8(depth - k);
324
+ __riscv_vse64_v_f64m8(dst + k, __riscv_vle64_v_f64m8(src + k, vector_length), vector_length);
325
+ k += vector_length;
326
+ }
327
+ }
328
+
329
+ // Append per-column norms after packed data
330
+ nk_f64_t *norms = (nk_f64_t *)(packed + total);
331
+ for (nk_size_t column = 0; column < column_count; ++column) {
332
+ nk_f64_t const *src = (nk_f64_t const *)((char const *)b + column * b_stride_in_bytes);
333
+ norms[column] = nk_dots_reduce_sumsq_f64_(src, depth);
334
+ }
335
+ }
336
+
337
+ /**
338
+ * @brief f64 packed GEMM kernel: C += A * B_packed^T with Kahan compensation.
339
+ *
340
+ * Vectorizes over depth dimension k using `vfmul`+Kahan (vector-vector multiply).
341
+ * Uses Kahan summation over full depth to maintain precision.
342
+ * Register tile: process 2 rows per iteration (rows_per_tile=2, budget: 32 regs at LMUL=4).
343
+ */
344
+ NK_INTERNAL void nk_dots_packed_f64_rvv_aligned_(nk_f64_t const *a_matrix, void const *b_packed_buffer,
345
+ nk_f64_t *c_matrix, nk_size_t row_count, nk_size_t column_count,
346
+ nk_size_t depth, nk_size_t a_stride_in_bytes,
347
+ nk_size_t c_stride_in_bytes) {
348
+ nk_cross_packed_buffer_header_t const *header = (nk_cross_packed_buffer_header_t const *)b_packed_buffer;
349
+ nk_size_t const depth_padded = header->depth_padded_values;
350
+ nk_f64_t const *packed_data = (nk_f64_t const *)((char const *)b_packed_buffer +
351
+ sizeof(nk_cross_packed_buffer_header_t));
352
+
353
+ // Zero output matrix
354
+ for (nk_size_t i = 0; i < row_count; ++i) {
355
+ nk_f64_t *c_row = (nk_f64_t *)((char *)c_matrix + i * c_stride_in_bytes);
356
+ for (nk_size_t j = 0; j < column_count; ++j) c_row[j] = 0;
357
+ }
358
+
359
+ // Process 2 rows per tile (rows_per_tile=2, tighter register budget for f64 at LMUL=4)
360
+ nk_size_t row = 0;
361
+ for (; row + 2 <= row_count; row += 2) {
362
+ nk_f64_t const *a_row_0 = (nk_f64_t const *)((char const *)a_matrix + (row + 0) * a_stride_in_bytes);
363
+ nk_f64_t const *a_row_1 = (nk_f64_t const *)((char const *)a_matrix + (row + 1) * a_stride_in_bytes);
364
+ nk_f64_t *c_row_0 = (nk_f64_t *)((char *)c_matrix + (row + 0) * c_stride_in_bytes);
365
+ nk_f64_t *c_row_1 = (nk_f64_t *)((char *)c_matrix + (row + 1) * c_stride_in_bytes);
366
+
367
+ for (nk_size_t column = 0; column < column_count; ++column) {
368
+ nk_f64_t const *b_column = packed_data + column * depth_padded;
369
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e64m4();
370
+ vfloat64m4_t accumulator_0_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
371
+ vfloat64m4_t accumulator_1_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
372
+ vfloat64m4_t compensation_0_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
373
+ vfloat64m4_t compensation_1_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
374
+
375
+ nk_size_t remaining = depth;
376
+ nk_size_t k = 0;
377
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
378
+ vector_length = __riscv_vsetvl_e64m4(remaining);
379
+ vfloat64m4_t b_vector_f64m4 = __riscv_vle64_v_f64m4(b_column + k, vector_length);
380
+ vfloat64m4_t a_vector_0_f64m4 = __riscv_vle64_v_f64m4(a_row_0 + k, vector_length);
381
+ vfloat64m4_t a_vector_1_f64m4 = __riscv_vle64_v_f64m4(a_row_1 + k, vector_length);
382
+
383
+ // Kahan step for row 0: product = a*b; corrected = product - comp; running = acc + corrected; comp =
384
+ // (running - acc) - corrected; acc = running
385
+ vfloat64m4_t product_0_f64m4 = __riscv_vfmul_vv_f64m4(a_vector_0_f64m4, b_vector_f64m4, vector_length);
386
+ vfloat64m4_t corrected_term_0_f64m4 = __riscv_vfsub_vv_f64m4(product_0_f64m4, compensation_0_f64m4,
387
+ vector_length);
388
+ vfloat64m4_t running_sum_0_f64m4 = __riscv_vfadd_vv_f64m4_tu(accumulator_0_f64m4, accumulator_0_f64m4,
389
+ corrected_term_0_f64m4, vector_length);
390
+ compensation_0_f64m4 = __riscv_vfsub_vv_f64m4_tu(
391
+ compensation_0_f64m4,
392
+ __riscv_vfsub_vv_f64m4(running_sum_0_f64m4, accumulator_0_f64m4, vector_length),
393
+ corrected_term_0_f64m4, vector_length);
394
+ accumulator_0_f64m4 = running_sum_0_f64m4;
395
+
396
+ // Kahan step for row 1
397
+ vfloat64m4_t product_1_f64m4 = __riscv_vfmul_vv_f64m4(a_vector_1_f64m4, b_vector_f64m4, vector_length);
398
+ vfloat64m4_t corrected_term_1_f64m4 = __riscv_vfsub_vv_f64m4(product_1_f64m4, compensation_1_f64m4,
399
+ vector_length);
400
+ vfloat64m4_t running_sum_1_f64m4 = __riscv_vfadd_vv_f64m4_tu(accumulator_1_f64m4, accumulator_1_f64m4,
401
+ corrected_term_1_f64m4, vector_length);
402
+ compensation_1_f64m4 = __riscv_vfsub_vv_f64m4_tu(
403
+ compensation_1_f64m4,
404
+ __riscv_vfsub_vv_f64m4(running_sum_1_f64m4, accumulator_1_f64m4, vector_length),
405
+ corrected_term_1_f64m4, vector_length);
406
+ accumulator_1_f64m4 = running_sum_1_f64m4;
407
+ }
408
+
409
+ // Horizontal reduce
410
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
411
+ c_row_0[column] = __riscv_vfmv_f_s_f64m1_f64(
412
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_0_f64m4, zero_f64m1, max_vector_length));
413
+ c_row_1[column] = __riscv_vfmv_f_s_f64m1_f64(
414
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_1_f64m4, zero_f64m1, max_vector_length));
415
+ }
416
+ }
417
+ // Remainder rows
418
+ for (; row < row_count; ++row) {
419
+ nk_f64_t const *a_row = (nk_f64_t const *)((char const *)a_matrix + row * a_stride_in_bytes);
420
+ nk_f64_t *c_row = (nk_f64_t *)((char *)c_matrix + row * c_stride_in_bytes);
421
+ for (nk_size_t column = 0; column < column_count; ++column) {
422
+ nk_f64_t const *b_column = packed_data + column * depth_padded;
423
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e64m4();
424
+ vfloat64m4_t accumulator_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
425
+ vfloat64m4_t compensation_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
426
+
427
+ nk_size_t remaining = depth;
428
+ nk_size_t k = 0;
429
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
430
+ vector_length = __riscv_vsetvl_e64m4(remaining);
431
+ vfloat64m4_t b_vector_f64m4 = __riscv_vle64_v_f64m4(b_column + k, vector_length);
432
+ vfloat64m4_t a_vector_f64m4 = __riscv_vle64_v_f64m4(a_row + k, vector_length);
433
+
434
+ vfloat64m4_t product_f64m4 = __riscv_vfmul_vv_f64m4(a_vector_f64m4, b_vector_f64m4, vector_length);
435
+ vfloat64m4_t corrected_term_f64m4 = __riscv_vfsub_vv_f64m4(product_f64m4, compensation_f64m4,
436
+ vector_length);
437
+ vfloat64m4_t running_sum_f64m4 = __riscv_vfadd_vv_f64m4_tu(accumulator_f64m4, accumulator_f64m4,
438
+ corrected_term_f64m4, vector_length);
439
+ compensation_f64m4 = __riscv_vfsub_vv_f64m4_tu(
440
+ compensation_f64m4, __riscv_vfsub_vv_f64m4(running_sum_f64m4, accumulator_f64m4, vector_length),
441
+ corrected_term_f64m4, vector_length);
442
+ accumulator_f64m4 = running_sum_f64m4;
443
+ }
444
+
445
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
446
+ c_row[column] = __riscv_vfmv_f_s_f64m1_f64(
447
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_f64m4, zero_f64m1, max_vector_length));
448
+ }
449
+ }
450
+ }
451
+
452
+ /**
453
+ * @brief Public f64 packed GEMM wrapper matching the declared signature in dots.h.
454
+ */
455
+ NK_PUBLIC void nk_dots_packed_f64_rvv(nk_f64_t const *a, void const *b_packed, nk_f64_t *c, nk_size_t rows,
456
+ nk_size_t columns, nk_size_t depth, nk_size_t a_stride_in_bytes,
457
+ nk_size_t c_stride_in_bytes) {
458
+ nk_dots_packed_f64_rvv_aligned_(a, b_packed, c, rows, columns, depth, a_stride_in_bytes, c_stride_in_bytes);
459
+ }
460
+
461
+ /**
462
+ * @brief Symmetric f64 GEMM: C = A * A^T, upper triangle + mirror.
463
+ *
464
+ * Uses Kahan compensation over full depth for precision.
465
+ * Processes only the rows in [row_start, row_start + row_count) for parallelism.
466
+ */
467
+ NK_PUBLIC void nk_dots_symmetric_f64_rvv(nk_f64_t const *vectors, nk_size_t vectors_count, nk_size_t depth,
468
+ nk_size_t stride_in_bytes, nk_f64_t *result, nk_size_t result_stride_in_bytes,
469
+ nk_size_t row_start, nk_size_t row_count) {
470
+ nk_size_t const stride_elements = stride_in_bytes / sizeof(nk_f64_t);
471
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f64_t);
472
+ nk_size_t const row_end = (row_start + row_count < vectors_count) ? (row_start + row_count) : vectors_count;
473
+
474
+ for (nk_size_t i = row_start; i < row_end; ++i) {
475
+ nk_f64_t const *a_i = vectors + i * stride_elements;
476
+ for (nk_size_t j = i; j < vectors_count; ++j) {
477
+ nk_f64_t const *a_j = vectors + j * stride_elements;
478
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e64m4();
479
+ vfloat64m4_t accumulator_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
480
+ vfloat64m4_t compensation_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
481
+
482
+ nk_size_t remaining = depth;
483
+ nk_size_t k = 0;
484
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
485
+ vector_length = __riscv_vsetvl_e64m4(remaining);
486
+ vfloat64m4_t a_vector_f64m4 = __riscv_vle64_v_f64m4(a_i + k, vector_length);
487
+ vfloat64m4_t b_vector_f64m4 = __riscv_vle64_v_f64m4(a_j + k, vector_length);
488
+
489
+ vfloat64m4_t product_f64m4 = __riscv_vfmul_vv_f64m4(a_vector_f64m4, b_vector_f64m4, vector_length);
490
+ vfloat64m4_t corrected_term_f64m4 = __riscv_vfsub_vv_f64m4(product_f64m4, compensation_f64m4,
491
+ vector_length);
492
+ vfloat64m4_t running_sum_f64m4 = __riscv_vfadd_vv_f64m4_tu(accumulator_f64m4, accumulator_f64m4,
493
+ corrected_term_f64m4, vector_length);
494
+ compensation_f64m4 = __riscv_vfsub_vv_f64m4_tu(
495
+ compensation_f64m4, __riscv_vfsub_vv_f64m4(running_sum_f64m4, accumulator_f64m4, vector_length),
496
+ corrected_term_f64m4, vector_length);
497
+ accumulator_f64m4 = running_sum_f64m4;
498
+ }
499
+
500
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
501
+ nk_f64_t dot = __riscv_vfmv_f_s_f64m1_f64(
502
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_f64m4, zero_f64m1, max_vector_length));
503
+ result[i * result_stride_elements + j] = dot;
504
+ }
505
+ }
506
+ }
507
+
508
+ #pragma endregion F64 Floats
509
+
510
+ #pragma region E2M3 Floats
511
+
512
+ /**
513
+ * @brief Scalar conversion helper: e2m3 byte → signed i8 (value × 16).
514
+ *
515
+ * Extracts 5-bit magnitude, looks up in LUT, applies sign from bit 5.
516
+ * Every e2m3 value × 16 is an exact integer in [-120, +120], fitting in i8.
517
+ */
518
+ NK_INTERNAL nk_i8_t nk_e2m3_to_i8_rvv_(nk_u8_t raw) {
519
+ nk_u8_t magnitude = raw & 0x1Fu;
520
+ nk_i8_t val = (nk_i8_t)nk_e2m3_magnitude_lut_rvv_[magnitude];
521
+ return (raw & 0x20u) ? (nk_i8_t)(-val) : val;
522
+ }
523
+
524
+ NK_PUBLIC nk_size_t nk_dots_packed_size_e2m3_rvv(nk_size_t column_count, nk_size_t depth) {
525
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e8m1();
526
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
527
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_i8_t);
528
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
529
+ return sizeof(nk_cross_packed_buffer_header_t) + column_count * depth_padded * sizeof(nk_i8_t) +
530
+ column_count * sizeof(nk_f32_t); // per-column norms
531
+ }
532
+
533
+ /**
534
+ * @brief Pack B matrix from e2m3 to signed i8 (value × 16) for integer dot product.
535
+ *
536
+ * Each e2m3 byte is converted to a signed i8 via scalar LUT lookup.
537
+ * Padding values are zeroed. Column-panel layout with depth-contiguous storage.
538
+ */
539
+ NK_PUBLIC void nk_dots_pack_e2m3_rvv(nk_e2m3_t const *b, nk_size_t column_count, nk_size_t depth,
540
+ nk_size_t b_stride_in_bytes, void *b_packed) {
541
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e8m1();
542
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
543
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_i8_t);
544
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
545
+
546
+ nk_cross_packed_buffer_header_t *header = (nk_cross_packed_buffer_header_t *)b_packed;
547
+ header->column_count = (nk_u32_t)column_count;
548
+ header->depth_dimensions = (nk_u32_t)depth;
549
+ header->depth_padded_values = (nk_u32_t)depth_padded;
550
+
551
+ nk_i8_t *packed = (nk_i8_t *)((char *)b_packed + sizeof(nk_cross_packed_buffer_header_t));
552
+ nk_size_t total = column_count * depth_padded;
553
+ {
554
+ nk_u8_t *zero_ptr = (nk_u8_t *)packed;
555
+ nk_size_t total_bytes = total * sizeof(nk_i8_t);
556
+ for (nk_size_t i = 0; i < total_bytes;) {
557
+ nk_size_t vector_length = __riscv_vsetvl_e8m8(total_bytes - i);
558
+ __riscv_vse8_v_u8m8(zero_ptr + i, __riscv_vmv_v_x_u8m8(0, vector_length), vector_length);
559
+ i += vector_length;
560
+ }
561
+ }
562
+
563
+ for (nk_size_t column = 0; column < column_count; ++column) {
564
+ nk_u8_t const *src = (nk_u8_t const *)((char const *)b + column * b_stride_in_bytes);
565
+ nk_i8_t *dst = packed + column * depth_padded;
566
+ for (nk_size_t k = 0; k < depth; ++k) dst[k] = nk_e2m3_to_i8_rvv_(src[k]);
567
+ }
568
+
569
+ // Append per-column norms after packed data
570
+ nk_f32_t *norms = (nk_f32_t *)(packed + total);
571
+ for (nk_size_t column = 0; column < column_count; ++column) {
572
+ nk_e2m3_t const *src = (nk_e2m3_t const *)((char const *)b + column * b_stride_in_bytes);
573
+ norms[column] = nk_dots_reduce_sumsq_e2m3_(src, depth);
574
+ }
575
+ }
576
+
577
+ /**
578
+ * @brief e2m3 packed GEMM kernel: C += A * B_packed^T with integer i8 LUT arithmetic.
579
+ *
580
+ * Vectorizes over the depth dimension (k). For each (row, column) pair:
581
+ * - Load raw e2m3 bytes from A, extract magnitude via `vluxei8` gather LUT
582
+ * - Apply sign from bit 5 via masked negate to produce signed i8 A values
583
+ * - Load pre-packed signed i8 values from B
584
+ * - Widening multiply i8×i8 → i16, then widen-accumulate i32 += i16
585
+ * - Final result = i32_sum / 256.0f
586
+ *
587
+ * Register tile: process 4 rows per iteration (rows_per_tile=4).
588
+ * The LUT gather on A magnitudes uses `vluxei8_v_u8m1` (byte-indexed byte gather).
589
+ */
590
+ NK_INTERNAL void nk_dots_packed_e2m3_rvv_aligned_(nk_e2m3_t const *a_matrix, void const *b_packed_buffer,
591
+ nk_f32_t *c_matrix, nk_size_t row_count, nk_size_t column_count,
592
+ nk_size_t depth, nk_size_t a_stride_in_bytes,
593
+ nk_size_t c_stride_in_bytes) {
594
+ nk_f32_t const lut_scale_reciprocal = 1.0f / 256.0f;
595
+
596
+ nk_cross_packed_buffer_header_t const *header = (nk_cross_packed_buffer_header_t const *)b_packed_buffer;
597
+ nk_size_t const depth_padded = header->depth_padded_values;
598
+ nk_i8_t const *packed_data = (nk_i8_t const *)((char const *)b_packed_buffer +
599
+ sizeof(nk_cross_packed_buffer_header_t));
600
+
601
+ // Zero output matrix
602
+ for (nk_size_t i = 0; i < row_count; ++i) {
603
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c_matrix + i * c_stride_in_bytes);
604
+ for (nk_size_t j = 0; j < column_count; ++j) c_row[j] = 0;
605
+ }
606
+
607
+ // mr=4 register tile over rows
608
+ nk_size_t row = 0;
609
+ for (; row + 4 <= row_count; row += 4) {
610
+ nk_u8_t const *a_row_0 = (nk_u8_t const *)((char const *)a_matrix + (row + 0) * a_stride_in_bytes);
611
+ nk_u8_t const *a_row_1 = (nk_u8_t const *)((char const *)a_matrix + (row + 1) * a_stride_in_bytes);
612
+ nk_u8_t const *a_row_2 = (nk_u8_t const *)((char const *)a_matrix + (row + 2) * a_stride_in_bytes);
613
+ nk_u8_t const *a_row_3 = (nk_u8_t const *)((char const *)a_matrix + (row + 3) * a_stride_in_bytes);
614
+ nk_f32_t *c_row_0 = (nk_f32_t *)((char *)c_matrix + (row + 0) * c_stride_in_bytes);
615
+ nk_f32_t *c_row_1 = (nk_f32_t *)((char *)c_matrix + (row + 1) * c_stride_in_bytes);
616
+ nk_f32_t *c_row_2 = (nk_f32_t *)((char *)c_matrix + (row + 2) * c_stride_in_bytes);
617
+ nk_f32_t *c_row_3 = (nk_f32_t *)((char *)c_matrix + (row + 3) * c_stride_in_bytes);
618
+
619
+ for (nk_size_t column = 0; column < column_count; ++column) {
620
+ nk_i8_t const *b_column = packed_data + column * depth_padded;
621
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m4();
622
+ vint32m4_t accumulator_0_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
623
+ vint32m4_t accumulator_1_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
624
+ vint32m4_t accumulator_2_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
625
+ vint32m4_t accumulator_3_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
626
+
627
+ nk_size_t remaining = depth;
628
+ nk_size_t k = 0;
629
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
630
+ vector_length = __riscv_vsetvl_e8m1(remaining);
631
+
632
+ // Load pre-packed i8 B values
633
+ vint8m1_t b_vector_i8m1 = __riscv_vle8_v_i8m1(b_column + k, vector_length);
634
+
635
+ // Load raw e2m3 bytes from each A row and convert via LUT
636
+ vuint8m1_t raw0_u8m1 = __riscv_vle8_v_u8m1(a_row_0 + k, vector_length);
637
+ vuint8m1_t raw1_u8m1 = __riscv_vle8_v_u8m1(a_row_1 + k, vector_length);
638
+ vuint8m1_t raw2_u8m1 = __riscv_vle8_v_u8m1(a_row_2 + k, vector_length);
639
+ vuint8m1_t raw3_u8m1 = __riscv_vle8_v_u8m1(a_row_3 + k, vector_length);
640
+
641
+ // Extract magnitudes and gather from LUT
642
+ vuint8m1_t mag0_u8m1 = __riscv_vand_vx_u8m1(raw0_u8m1, 0x1F, vector_length);
643
+ vuint8m1_t mag1_u8m1 = __riscv_vand_vx_u8m1(raw1_u8m1, 0x1F, vector_length);
644
+ vuint8m1_t mag2_u8m1 = __riscv_vand_vx_u8m1(raw2_u8m1, 0x1F, vector_length);
645
+ vuint8m1_t mag3_u8m1 = __riscv_vand_vx_u8m1(raw3_u8m1, 0x1F, vector_length);
646
+ vuint8m1_t uval0_u8m1 = __riscv_vluxei8_v_u8m1(nk_e2m3_magnitude_lut_rvv_, mag0_u8m1, vector_length);
647
+ vuint8m1_t uval1_u8m1 = __riscv_vluxei8_v_u8m1(nk_e2m3_magnitude_lut_rvv_, mag1_u8m1, vector_length);
648
+ vuint8m1_t uval2_u8m1 = __riscv_vluxei8_v_u8m1(nk_e2m3_magnitude_lut_rvv_, mag2_u8m1, vector_length);
649
+ vuint8m1_t uval3_u8m1 = __riscv_vluxei8_v_u8m1(nk_e2m3_magnitude_lut_rvv_, mag3_u8m1, vector_length);
650
+
651
+ // Apply sign to A: negate where bit 5 is set.
652
+ // B is already signed from packing, so A sign completes the product sign.
653
+ vint8m1_t a_vector_0_i8m1 = __riscv_vreinterpret_v_u8m1_i8m1(uval0_u8m1);
654
+ vbool8_t negated_0_b8 = __riscv_vmsne_vx_u8m1_b8(__riscv_vand_vx_u8m1(raw0_u8m1, 0x20, vector_length),
655
+ 0, vector_length);
656
+ a_vector_0_i8m1 = __riscv_vneg_v_i8m1_mu(negated_0_b8, a_vector_0_i8m1, a_vector_0_i8m1, vector_length);
657
+
658
+ vint8m1_t a_vector_1_i8m1 = __riscv_vreinterpret_v_u8m1_i8m1(uval1_u8m1);
659
+ vbool8_t negated_1_b8 = __riscv_vmsne_vx_u8m1_b8(__riscv_vand_vx_u8m1(raw1_u8m1, 0x20, vector_length),
660
+ 0, vector_length);
661
+ a_vector_1_i8m1 = __riscv_vneg_v_i8m1_mu(negated_1_b8, a_vector_1_i8m1, a_vector_1_i8m1, vector_length);
662
+
663
+ vint8m1_t a_vector_2_i8m1 = __riscv_vreinterpret_v_u8m1_i8m1(uval2_u8m1);
664
+ vbool8_t negated_2_b8 = __riscv_vmsne_vx_u8m1_b8(__riscv_vand_vx_u8m1(raw2_u8m1, 0x20, vector_length),
665
+ 0, vector_length);
666
+ a_vector_2_i8m1 = __riscv_vneg_v_i8m1_mu(negated_2_b8, a_vector_2_i8m1, a_vector_2_i8m1, vector_length);
667
+
668
+ vint8m1_t a_vector_3_i8m1 = __riscv_vreinterpret_v_u8m1_i8m1(uval3_u8m1);
669
+ vbool8_t negated_3_b8 = __riscv_vmsne_vx_u8m1_b8(__riscv_vand_vx_u8m1(raw3_u8m1, 0x20, vector_length),
670
+ 0, vector_length);
671
+ a_vector_3_i8m1 = __riscv_vneg_v_i8m1_mu(negated_3_b8, a_vector_3_i8m1, a_vector_3_i8m1, vector_length);
672
+
673
+ // Widening multiply: i8×i8 → i16, then accumulate: i32 += i16
674
+ vint16m2_t product_0_i16m2 = __riscv_vwmul_vv_i16m2(a_vector_0_i8m1, b_vector_i8m1, vector_length);
675
+ vint16m2_t product_1_i16m2 = __riscv_vwmul_vv_i16m2(a_vector_1_i8m1, b_vector_i8m1, vector_length);
676
+ vint16m2_t product_2_i16m2 = __riscv_vwmul_vv_i16m2(a_vector_2_i8m1, b_vector_i8m1, vector_length);
677
+ vint16m2_t product_3_i16m2 = __riscv_vwmul_vv_i16m2(a_vector_3_i8m1, b_vector_i8m1, vector_length);
678
+ accumulator_0_i32m4 = __riscv_vwadd_wv_i32m4_tu(accumulator_0_i32m4, accumulator_0_i32m4,
679
+ product_0_i16m2, vector_length);
680
+ accumulator_1_i32m4 = __riscv_vwadd_wv_i32m4_tu(accumulator_1_i32m4, accumulator_1_i32m4,
681
+ product_1_i16m2, vector_length);
682
+ accumulator_2_i32m4 = __riscv_vwadd_wv_i32m4_tu(accumulator_2_i32m4, accumulator_2_i32m4,
683
+ product_2_i16m2, vector_length);
684
+ accumulator_3_i32m4 = __riscv_vwadd_wv_i32m4_tu(accumulator_3_i32m4, accumulator_3_i32m4,
685
+ product_3_i16m2, vector_length);
686
+ }
687
+
688
+ // Horizontal reduce and convert to f32 with scaling
689
+ vint32m1_t zero_i32m1 = __riscv_vmv_v_x_i32m1(0, 1);
690
+ c_row_0[column] = (nk_f32_t)__riscv_vmv_x_s_i32m1_i32(
691
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_0_i32m4, zero_i32m1, max_vector_length)) *
692
+ lut_scale_reciprocal;
693
+ c_row_1[column] = (nk_f32_t)__riscv_vmv_x_s_i32m1_i32(
694
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_1_i32m4, zero_i32m1, max_vector_length)) *
695
+ lut_scale_reciprocal;
696
+ c_row_2[column] = (nk_f32_t)__riscv_vmv_x_s_i32m1_i32(
697
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_2_i32m4, zero_i32m1, max_vector_length)) *
698
+ lut_scale_reciprocal;
699
+ c_row_3[column] = (nk_f32_t)__riscv_vmv_x_s_i32m1_i32(
700
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_3_i32m4, zero_i32m1, max_vector_length)) *
701
+ lut_scale_reciprocal;
702
+ }
703
+ }
704
+ // Remainder rows (mr < 4)
705
+ for (; row < row_count; ++row) {
706
+ nk_u8_t const *a_row = (nk_u8_t const *)((char const *)a_matrix + row * a_stride_in_bytes);
707
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c_matrix + row * c_stride_in_bytes);
708
+ for (nk_size_t column = 0; column < column_count; ++column) {
709
+ nk_i8_t const *b_column = packed_data + column * depth_padded;
710
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m4();
711
+ vint32m4_t accumulator_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
712
+ nk_size_t remaining = depth;
713
+ nk_size_t k = 0;
714
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
715
+ vector_length = __riscv_vsetvl_e8m1(remaining);
716
+ vint8m1_t b_vector_i8m1 = __riscv_vle8_v_i8m1(b_column + k, vector_length);
717
+ vuint8m1_t raw_a_u8m1 = __riscv_vle8_v_u8m1(a_row + k, vector_length);
718
+ vuint8m1_t mag_a_u8m1 = __riscv_vand_vx_u8m1(raw_a_u8m1, 0x1F, vector_length);
719
+ vuint8m1_t uval_a_u8m1 = __riscv_vluxei8_v_u8m1(nk_e2m3_magnitude_lut_rvv_, mag_a_u8m1, vector_length);
720
+ vint8m1_t a_vector_i8m1 = __riscv_vreinterpret_v_u8m1_i8m1(uval_a_u8m1);
721
+ vbool8_t negated_a_b8 = __riscv_vmsne_vx_u8m1_b8(__riscv_vand_vx_u8m1(raw_a_u8m1, 0x20, vector_length),
722
+ 0, vector_length);
723
+ a_vector_i8m1 = __riscv_vneg_v_i8m1_mu(negated_a_b8, a_vector_i8m1, a_vector_i8m1, vector_length);
724
+ vint16m2_t product_i16m2 = __riscv_vwmul_vv_i16m2(a_vector_i8m1, b_vector_i8m1, vector_length);
725
+ accumulator_i32m4 = __riscv_vwadd_wv_i32m4_tu(accumulator_i32m4, accumulator_i32m4, product_i16m2,
726
+ vector_length);
727
+ }
728
+ vint32m1_t zero_i32m1 = __riscv_vmv_v_x_i32m1(0, 1);
729
+ c_row[column] = (nk_f32_t)__riscv_vmv_x_s_i32m1_i32(
730
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_i32m4, zero_i32m1, max_vector_length)) *
731
+ lut_scale_reciprocal;
732
+ }
733
+ }
734
+ }
735
+
736
+ /**
737
+ * @brief Public e2m3 packed GEMM wrapper matching the declared signature in dots.h.
738
+ */
739
+ NK_PUBLIC void nk_dots_packed_e2m3_rvv(nk_e2m3_t const *a, void const *b_packed, nk_f32_t *c, nk_size_t rows,
740
+ nk_size_t columns, nk_size_t depth, nk_size_t a_stride_in_bytes,
741
+ nk_size_t c_stride_in_bytes) {
742
+ nk_dots_packed_e2m3_rvv_aligned_(a, b_packed, c, rows, columns, depth, a_stride_in_bytes, c_stride_in_bytes);
743
+ }
744
+
745
+ /**
746
+ * @brief Symmetric e2m3 GEMM: C = A * A^T, upper triangle + mirror.
747
+ *
748
+ * Uses integer i8 LUT arithmetic with i32 accumulation, scaled by 1/256.
749
+ * Processes only the rows in [row_start, row_start + row_count) for parallelism.
750
+ */
751
+ NK_PUBLIC void nk_dots_symmetric_e2m3_rvv(nk_e2m3_t const *vectors, nk_size_t vectors_count, nk_size_t depth,
752
+ nk_size_t stride_in_bytes, nk_f32_t *result, nk_size_t result_stride_in_bytes,
753
+ nk_size_t row_start, nk_size_t row_count) {
754
+ nk_f32_t const lut_scale_reciprocal = 1.0f / 256.0f;
755
+
756
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f32_t);
757
+ nk_size_t const row_end = (row_start + row_count < vectors_count) ? (row_start + row_count) : vectors_count;
758
+
759
+ for (nk_size_t i = row_start; i < row_end; ++i) {
760
+ nk_u8_t const *a_i = (nk_u8_t const *)vectors + i * stride_in_bytes;
761
+ for (nk_size_t j = i; j < vectors_count; ++j) {
762
+ nk_u8_t const *a_j = (nk_u8_t const *)vectors + j * stride_in_bytes;
763
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m4();
764
+ vint32m4_t accumulator_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
765
+ nk_size_t remaining = depth;
766
+ nk_size_t k = 0;
767
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
768
+ vector_length = __riscv_vsetvl_e8m1(remaining);
769
+ vuint8m1_t raw_i_u8m1 = __riscv_vle8_v_u8m1(a_i + k, vector_length);
770
+ vuint8m1_t raw_j_u8m1 = __riscv_vle8_v_u8m1(a_j + k, vector_length);
771
+
772
+ // Extract magnitudes and gather from LUT
773
+ vuint8m1_t mag_i_u8m1 = __riscv_vand_vx_u8m1(raw_i_u8m1, 0x1F, vector_length);
774
+ vuint8m1_t mag_j_u8m1 = __riscv_vand_vx_u8m1(raw_j_u8m1, 0x1F, vector_length);
775
+ vuint8m1_t uval_i_u8m1 = __riscv_vluxei8_v_u8m1(nk_e2m3_magnitude_lut_rvv_, mag_i_u8m1, vector_length);
776
+ vuint8m1_t uval_j_u8m1 = __riscv_vluxei8_v_u8m1(nk_e2m3_magnitude_lut_rvv_, mag_j_u8m1, vector_length);
777
+
778
+ // Combined sign: XOR sign bits → conditional negate on B side
779
+ vuint8m1_t sign_xor_u8m1 = __riscv_vand_vx_u8m1(
780
+ __riscv_vxor_vv_u8m1(raw_i_u8m1, raw_j_u8m1, vector_length), 0x20, vector_length);
781
+ vbool8_t negate_b8 = __riscv_vmsne_vx_u8m1_b8(sign_xor_u8m1, 0, vector_length);
782
+ vint8m1_t val_i_i8m1 = __riscv_vreinterpret_v_u8m1_i8m1(uval_i_u8m1);
783
+ vint8m1_t val_j_i8m1 = __riscv_vreinterpret_v_u8m1_i8m1(uval_j_u8m1);
784
+ val_j_i8m1 = __riscv_vneg_v_i8m1_mu(negate_b8, val_j_i8m1, val_j_i8m1, vector_length);
785
+
786
+ // Widening multiply: i8×i8 → i16, then accumulate: i32 += i16
787
+ vint16m2_t product_i16m2 = __riscv_vwmul_vv_i16m2(val_i_i8m1, val_j_i8m1, vector_length);
788
+ accumulator_i32m4 = __riscv_vwadd_wv_i32m4_tu(accumulator_i32m4, accumulator_i32m4, product_i16m2,
789
+ vector_length);
790
+ }
791
+ vint32m1_t zero_i32m1 = __riscv_vmv_v_x_i32m1(0, 1);
792
+ nk_f32_t dot = (nk_f32_t)__riscv_vmv_x_s_i32m1_i32(
793
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_i32m4, zero_i32m1, max_vector_length)) *
794
+ lut_scale_reciprocal;
795
+ result[i * result_stride_elements + j] = dot;
796
+ }
797
+ }
798
+ }
799
+
800
+ #pragma endregion E2M3 Floats
801
+
802
+ #pragma region E3M2 Floats
803
+
804
+ /**
805
+ * @brief Scalar conversion helper: e3m2 byte → signed i16 (value × 16).
806
+ *
807
+ * Extracts 5-bit magnitude, looks up in LUT, applies sign from bit 5.
808
+ * Every e3m2 value × 16 is an exact integer in [-448, +448], requiring i16.
809
+ */
810
+ NK_INTERNAL nk_i16_t nk_e3m2_to_i16_rvv_(nk_u8_t raw) {
811
+ nk_u8_t magnitude = raw & 0x1Fu;
812
+ nk_i16_t val = (nk_i16_t)nk_e3m2_magnitude_lut_rvv_[magnitude];
813
+ return (raw & 0x20u) ? (nk_i16_t)(-val) : val;
814
+ }
815
+
816
+ NK_PUBLIC nk_size_t nk_dots_packed_size_e3m2_rvv(nk_size_t column_count, nk_size_t depth) {
817
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e16m2();
818
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
819
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_i16_t);
820
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
821
+ return sizeof(nk_cross_packed_buffer_header_t) + column_count * depth_padded * sizeof(nk_i16_t) +
822
+ column_count * sizeof(nk_f32_t); // per-column norms
823
+ }
824
+
825
+ /**
826
+ * @brief Pack B matrix from e3m2 to signed i16 (value × 16) for integer dot product.
827
+ *
828
+ * Each e3m2 byte is converted to a signed i16 via scalar LUT lookup.
829
+ * Padding values are zeroed. Column-panel layout with depth-contiguous storage.
830
+ */
831
+ NK_PUBLIC void nk_dots_pack_e3m2_rvv(nk_e3m2_t const *b, nk_size_t column_count, nk_size_t depth,
832
+ nk_size_t b_stride_in_bytes, void *b_packed) {
833
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e16m2();
834
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
835
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_i16_t);
836
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
837
+
838
+ nk_cross_packed_buffer_header_t *header = (nk_cross_packed_buffer_header_t *)b_packed;
839
+ header->column_count = (nk_u32_t)column_count;
840
+ header->depth_dimensions = (nk_u32_t)depth;
841
+ header->depth_padded_values = (nk_u32_t)depth_padded;
842
+
843
+ nk_i16_t *packed = (nk_i16_t *)((char *)b_packed + sizeof(nk_cross_packed_buffer_header_t));
844
+ nk_size_t total = column_count * depth_padded;
845
+ {
846
+ nk_u8_t *zero_ptr = (nk_u8_t *)packed;
847
+ nk_size_t total_bytes = total * sizeof(nk_i16_t);
848
+ for (nk_size_t i = 0; i < total_bytes;) {
849
+ nk_size_t vector_length = __riscv_vsetvl_e8m8(total_bytes - i);
850
+ __riscv_vse8_v_u8m8(zero_ptr + i, __riscv_vmv_v_x_u8m8(0, vector_length), vector_length);
851
+ i += vector_length;
852
+ }
853
+ }
854
+
855
+ for (nk_size_t column = 0; column < column_count; ++column) {
856
+ nk_u8_t const *src = (nk_u8_t const *)((char const *)b + column * b_stride_in_bytes);
857
+ nk_i16_t *dst = packed + column * depth_padded;
858
+ for (nk_size_t k = 0; k < depth; ++k) dst[k] = nk_e3m2_to_i16_rvv_(src[k]);
859
+ }
860
+
861
+ // Append per-column norms after packed data
862
+ nk_f32_t *norms = (nk_f32_t *)(packed + total);
863
+ for (nk_size_t column = 0; column < column_count; ++column) {
864
+ nk_e3m2_t const *src = (nk_e3m2_t const *)((char const *)b + column * b_stride_in_bytes);
865
+ norms[column] = nk_dots_reduce_sumsq_e3m2_(src, depth);
866
+ }
867
+ }
868
+
869
+ /**
870
+ * @brief e3m2 packed GEMM kernel: C += A * B_packed^T with integer i16 LUT arithmetic.
871
+ *
872
+ * Vectorizes over the depth dimension (k). For each (row, column) pair:
873
+ * - Load raw e3m2 bytes from A, convert to signed i16 via `vluxei16` gather LUT
874
+ * - Load pre-packed i16 values from B
875
+ * - Widening multiply-accumulate: i16×i16 → i32 via `vwmacc`
876
+ * - Final result = i32_sum / 256.0f
877
+ *
878
+ * Register tile: process 2 rows per iteration (rows_per_tile=2, wider i16/i32 elements reduce VL).
879
+ * The LUT gather on A magnitudes uses `vluxei16_v_u16m2` (16-bit indexed 16-bit gather).
880
+ */
881
+ NK_INTERNAL void nk_dots_packed_e3m2_rvv_aligned_(nk_e3m2_t const *a_matrix, void const *b_packed_buffer,
882
+ nk_f32_t *c_matrix, nk_size_t row_count, nk_size_t column_count,
883
+ nk_size_t depth, nk_size_t a_stride_in_bytes,
884
+ nk_size_t c_stride_in_bytes) {
885
+ nk_f32_t const lut_scale_reciprocal = 1.0f / 256.0f;
886
+
887
+ nk_cross_packed_buffer_header_t const *header = (nk_cross_packed_buffer_header_t const *)b_packed_buffer;
888
+ nk_size_t const depth_padded = header->depth_padded_values;
889
+ nk_i16_t const *packed_data = (nk_i16_t const *)((char const *)b_packed_buffer +
890
+ sizeof(nk_cross_packed_buffer_header_t));
891
+
892
+ // Zero output matrix
893
+ for (nk_size_t i = 0; i < row_count; ++i) {
894
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c_matrix + i * c_stride_in_bytes);
895
+ for (nk_size_t j = 0; j < column_count; ++j) c_row[j] = 0;
896
+ }
897
+
898
+ // mr=2 register tile (i16 at LMUL=2 and i32 at LMUL=4 leaves fewer spare registers)
899
+ nk_size_t row = 0;
900
+ for (; row + 2 <= row_count; row += 2) {
901
+ nk_u8_t const *a_row_0 = (nk_u8_t const *)((char const *)a_matrix + (row + 0) * a_stride_in_bytes);
902
+ nk_u8_t const *a_row_1 = (nk_u8_t const *)((char const *)a_matrix + (row + 1) * a_stride_in_bytes);
903
+ nk_f32_t *c_row_0 = (nk_f32_t *)((char *)c_matrix + (row + 0) * c_stride_in_bytes);
904
+ nk_f32_t *c_row_1 = (nk_f32_t *)((char *)c_matrix + (row + 1) * c_stride_in_bytes);
905
+
906
+ for (nk_size_t column = 0; column < column_count; ++column) {
907
+ nk_i16_t const *b_column = packed_data + column * depth_padded;
908
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m4();
909
+ vint32m4_t accumulator_0_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
910
+ vint32m4_t accumulator_1_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
911
+
912
+ nk_size_t remaining = depth;
913
+ nk_size_t k = 0;
914
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
915
+ vector_length = __riscv_vsetvl_e16m2(remaining);
916
+
917
+ // Load pre-packed i16 B values
918
+ vint16m2_t b_vector_i16m2 = __riscv_vle16_v_i16m2(b_column + k, vector_length);
919
+
920
+ // Load raw e3m2 bytes from each A row
921
+ vuint8m1_t raw0_u8m1 = __riscv_vle8_v_u8m1(a_row_0 + k, vector_length);
922
+ vuint8m1_t raw1_u8m1 = __riscv_vle8_v_u8m1(a_row_1 + k, vector_length);
923
+
924
+ // Extract magnitudes, zero-extend to u16, compute byte offsets for i16 LUT gather
925
+ vuint8m1_t mag0_u8m1 = __riscv_vand_vx_u8m1(raw0_u8m1, 0x1F, vector_length);
926
+ vuint8m1_t mag1_u8m1 = __riscv_vand_vx_u8m1(raw1_u8m1, 0x1F, vector_length);
927
+ vuint16m2_t idx0_u16m2 = __riscv_vzext_vf2_u16m2(mag0_u8m1, vector_length);
928
+ vuint16m2_t idx1_u16m2 = __riscv_vzext_vf2_u16m2(mag1_u8m1, vector_length);
929
+ vuint16m2_t off0_u16m2 = __riscv_vsll_vx_u16m2(idx0_u16m2, 1,
930
+ vector_length); // byte offsets = index × 2
931
+ vuint16m2_t off1_u16m2 = __riscv_vsll_vx_u16m2(idx1_u16m2, 1, vector_length);
932
+
933
+ // Gather unsigned magnitudes from i16 LUT
934
+ vuint16m2_t uval0_u16m2 = __riscv_vluxei16_v_u16m2(nk_e3m2_magnitude_lut_rvv_, off0_u16m2,
935
+ vector_length);
936
+ vuint16m2_t uval1_u16m2 = __riscv_vluxei16_v_u16m2(nk_e3m2_magnitude_lut_rvv_, off1_u16m2,
937
+ vector_length);
938
+
939
+ // Apply sign: negate where bit 5 is set
940
+ vuint8m1_t sign0_u8m1 = __riscv_vand_vx_u8m1(raw0_u8m1, 0x20, vector_length);
941
+ vuint8m1_t sign1_u8m1 = __riscv_vand_vx_u8m1(raw1_u8m1, 0x20, vector_length);
942
+ vbool8_t negated_0_b8 = __riscv_vmsne_vx_u8m1_b8(sign0_u8m1, 0, vector_length);
943
+ vbool8_t negated_1_b8 = __riscv_vmsne_vx_u8m1_b8(sign1_u8m1, 0, vector_length);
944
+
945
+ vint16m2_t a_vector_0_i16m2 = __riscv_vreinterpret_v_u16m2_i16m2(uval0_u16m2);
946
+ a_vector_0_i16m2 = __riscv_vneg_v_i16m2_mu(negated_0_b8, a_vector_0_i16m2, a_vector_0_i16m2,
947
+ vector_length);
948
+ vint16m2_t a_vector_1_i16m2 = __riscv_vreinterpret_v_u16m2_i16m2(uval1_u16m2);
949
+ a_vector_1_i16m2 = __riscv_vneg_v_i16m2_mu(negated_1_b8, a_vector_1_i16m2, a_vector_1_i16m2,
950
+ vector_length);
951
+
952
+ // Widening multiply-accumulate: i16×i16 → i32
953
+ accumulator_0_i32m4 = __riscv_vwmacc_vv_i32m4_tu(accumulator_0_i32m4, a_vector_0_i16m2, b_vector_i16m2,
954
+ vector_length);
955
+ accumulator_1_i32m4 = __riscv_vwmacc_vv_i32m4_tu(accumulator_1_i32m4, a_vector_1_i16m2, b_vector_i16m2,
956
+ vector_length);
957
+ }
958
+
959
+ // Horizontal reduce and convert to f32 with scaling
960
+ vint32m1_t zero_i32m1 = __riscv_vmv_v_x_i32m1(0, 1);
961
+ c_row_0[column] = (nk_f32_t)__riscv_vmv_x_s_i32m1_i32(
962
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_0_i32m4, zero_i32m1, max_vector_length)) *
963
+ lut_scale_reciprocal;
964
+ c_row_1[column] = (nk_f32_t)__riscv_vmv_x_s_i32m1_i32(
965
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_1_i32m4, zero_i32m1, max_vector_length)) *
966
+ lut_scale_reciprocal;
967
+ }
968
+ }
969
+ // Remainder rows
970
+ for (; row < row_count; ++row) {
971
+ nk_u8_t const *a_row = (nk_u8_t const *)((char const *)a_matrix + row * a_stride_in_bytes);
972
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c_matrix + row * c_stride_in_bytes);
973
+ for (nk_size_t column = 0; column < column_count; ++column) {
974
+ nk_i16_t const *b_column = packed_data + column * depth_padded;
975
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m4();
976
+ vint32m4_t accumulator_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
977
+ nk_size_t remaining = depth;
978
+ nk_size_t k = 0;
979
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
980
+ vector_length = __riscv_vsetvl_e16m2(remaining);
981
+ vint16m2_t b_vector_i16m2 = __riscv_vle16_v_i16m2(b_column + k, vector_length);
982
+ vuint8m1_t raw_a_u8m1 = __riscv_vle8_v_u8m1(a_row + k, vector_length);
983
+ vuint8m1_t mag_a_u8m1 = __riscv_vand_vx_u8m1(raw_a_u8m1, 0x1F, vector_length);
984
+ vuint16m2_t idx_a_u16m2 = __riscv_vzext_vf2_u16m2(mag_a_u8m1, vector_length);
985
+ vuint16m2_t off_a_u16m2 = __riscv_vsll_vx_u16m2(idx_a_u16m2, 1, vector_length);
986
+ vuint16m2_t uval_a_u16m2 = __riscv_vluxei16_v_u16m2(nk_e3m2_magnitude_lut_rvv_, off_a_u16m2,
987
+ vector_length);
988
+ vint16m2_t a_vector_i16m2 = __riscv_vreinterpret_v_u16m2_i16m2(uval_a_u16m2);
989
+ vbool8_t negated_a_b8 = __riscv_vmsne_vx_u8m1_b8(__riscv_vand_vx_u8m1(raw_a_u8m1, 0x20, vector_length),
990
+ 0, vector_length);
991
+ a_vector_i16m2 = __riscv_vneg_v_i16m2_mu(negated_a_b8, a_vector_i16m2, a_vector_i16m2, vector_length);
992
+ accumulator_i32m4 = __riscv_vwmacc_vv_i32m4_tu(accumulator_i32m4, a_vector_i16m2, b_vector_i16m2,
993
+ vector_length);
994
+ }
995
+ vint32m1_t zero_i32m1 = __riscv_vmv_v_x_i32m1(0, 1);
996
+ c_row[column] = (nk_f32_t)__riscv_vmv_x_s_i32m1_i32(
997
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_i32m4, zero_i32m1, max_vector_length)) *
998
+ lut_scale_reciprocal;
999
+ }
1000
+ }
1001
+ }
1002
+
1003
+ /**
1004
+ * @brief Public e3m2 packed GEMM wrapper matching the declared signature in dots.h.
1005
+ */
1006
+ NK_PUBLIC void nk_dots_packed_e3m2_rvv(nk_e3m2_t const *a, void const *b_packed, nk_f32_t *c, nk_size_t rows,
1007
+ nk_size_t columns, nk_size_t depth, nk_size_t a_stride_in_bytes,
1008
+ nk_size_t c_stride_in_bytes) {
1009
+ nk_dots_packed_e3m2_rvv_aligned_(a, b_packed, c, rows, columns, depth, a_stride_in_bytes, c_stride_in_bytes);
1010
+ }
1011
+
1012
+ /**
1013
+ * @brief Symmetric e3m2 GEMM: C = A * A^T, upper triangle + mirror.
1014
+ *
1015
+ * Uses integer i16 LUT arithmetic with i32 widening MAC, scaled by 1/256.
1016
+ * Processes only the rows in [row_start, row_start + row_count) for parallelism.
1017
+ */
1018
+ NK_PUBLIC void nk_dots_symmetric_e3m2_rvv(nk_e3m2_t const *vectors, nk_size_t vectors_count, nk_size_t depth,
1019
+ nk_size_t stride_in_bytes, nk_f32_t *result, nk_size_t result_stride_in_bytes,
1020
+ nk_size_t row_start, nk_size_t row_count) {
1021
+ nk_f32_t const lut_scale_reciprocal = 1.0f / 256.0f;
1022
+
1023
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f32_t);
1024
+ nk_size_t const row_end = (row_start + row_count < vectors_count) ? (row_start + row_count) : vectors_count;
1025
+
1026
+ for (nk_size_t i = row_start; i < row_end; ++i) {
1027
+ nk_u8_t const *a_i = (nk_u8_t const *)vectors + i * stride_in_bytes;
1028
+ for (nk_size_t j = i; j < vectors_count; ++j) {
1029
+ nk_u8_t const *a_j = (nk_u8_t const *)vectors + j * stride_in_bytes;
1030
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m4();
1031
+ vint32m4_t accumulator_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
1032
+ nk_size_t remaining = depth;
1033
+ nk_size_t k = 0;
1034
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1035
+ vector_length = __riscv_vsetvl_e16m2(remaining);
1036
+ vuint8m1_t raw_i_u8m1 = __riscv_vle8_v_u8m1(a_i + k, vector_length);
1037
+ vuint8m1_t raw_j_u8m1 = __riscv_vle8_v_u8m1(a_j + k, vector_length);
1038
+
1039
+ // Extract magnitudes, zero-extend to u16, compute byte offsets
1040
+ vuint8m1_t mag_i_u8m1 = __riscv_vand_vx_u8m1(raw_i_u8m1, 0x1F, vector_length);
1041
+ vuint8m1_t mag_j_u8m1 = __riscv_vand_vx_u8m1(raw_j_u8m1, 0x1F, vector_length);
1042
+ vuint16m2_t idx_i_u16m2 = __riscv_vzext_vf2_u16m2(mag_i_u8m1, vector_length);
1043
+ vuint16m2_t idx_j_u16m2 = __riscv_vzext_vf2_u16m2(mag_j_u8m1, vector_length);
1044
+ vuint16m2_t off_i_u16m2 = __riscv_vsll_vx_u16m2(idx_i_u16m2, 1, vector_length);
1045
+ vuint16m2_t off_j_u16m2 = __riscv_vsll_vx_u16m2(idx_j_u16m2, 1, vector_length);
1046
+
1047
+ // Gather unsigned magnitudes
1048
+ vuint16m2_t uval_i_u16m2 = __riscv_vluxei16_v_u16m2(nk_e3m2_magnitude_lut_rvv_, off_i_u16m2,
1049
+ vector_length);
1050
+ vuint16m2_t uval_j_u16m2 = __riscv_vluxei16_v_u16m2(nk_e3m2_magnitude_lut_rvv_, off_j_u16m2,
1051
+ vector_length);
1052
+
1053
+ // Apply individual signs
1054
+ vuint8m1_t sign_i_u8m1 = __riscv_vand_vx_u8m1(raw_i_u8m1, 0x20, vector_length);
1055
+ vuint8m1_t sign_j_u8m1 = __riscv_vand_vx_u8m1(raw_j_u8m1, 0x20, vector_length);
1056
+ vbool8_t negated_i_b8 = __riscv_vmsne_vx_u8m1_b8(sign_i_u8m1, 0, vector_length);
1057
+ vbool8_t negated_j_b8 = __riscv_vmsne_vx_u8m1_b8(sign_j_u8m1, 0, vector_length);
1058
+
1059
+ vint16m2_t val_i_i16m2 = __riscv_vreinterpret_v_u16m2_i16m2(uval_i_u16m2);
1060
+ val_i_i16m2 = __riscv_vneg_v_i16m2_mu(negated_i_b8, val_i_i16m2, val_i_i16m2, vector_length);
1061
+ vint16m2_t val_j_i16m2 = __riscv_vreinterpret_v_u16m2_i16m2(uval_j_u16m2);
1062
+ val_j_i16m2 = __riscv_vneg_v_i16m2_mu(negated_j_b8, val_j_i16m2, val_j_i16m2, vector_length);
1063
+
1064
+ // Widening multiply-accumulate: i16×i16 → i32
1065
+ accumulator_i32m4 = __riscv_vwmacc_vv_i32m4_tu(accumulator_i32m4, val_i_i16m2, val_j_i16m2,
1066
+ vector_length);
1067
+ }
1068
+ vint32m1_t zero_i32m1 = __riscv_vmv_v_x_i32m1(0, 1);
1069
+ nk_f32_t dot = (nk_f32_t)__riscv_vmv_x_s_i32m1_i32(
1070
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_i32m4, zero_i32m1, max_vector_length)) *
1071
+ lut_scale_reciprocal;
1072
+ result[i * result_stride_elements + j] = dot;
1073
+ }
1074
+ }
1075
+ }
1076
+
1077
+ #pragma endregion E3M2 Floats
1078
+
1079
+ #pragma region BF16 Floats
1080
+
1081
+ /**
1082
+ * @brief Compute the packed buffer size for bf16 GEMM (B stored as f32).
1083
+ *
1084
+ * VL is determined by `__riscv_vsetvlmax_e32m2()` since B is stored as f32.
1085
+ * Layout: column-panel with depth-contiguous f32 values, cache-line padding.
1086
+ */
1087
+ NK_PUBLIC nk_size_t nk_dots_packed_size_bf16_rvv(nk_size_t column_count, nk_size_t depth) {
1088
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
1089
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
1090
+ // Break power-of-2 strides for cache associativity
1091
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_f32_t);
1092
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
1093
+ return sizeof(nk_cross_packed_buffer_header_t) + column_count * depth_padded * sizeof(nk_f32_t) +
1094
+ column_count * sizeof(nk_f32_t); // per-column norms
1095
+ }
1096
+
1097
+ /**
1098
+ * @brief Pack B matrix from bf16 to f32 for widened dot product.
1099
+ *
1100
+ * Each bf16 value is converted to f32 via bit shift (bf16 is the upper 16 bits of f32).
1101
+ * Padding values are zeroed. Column-panel layout with depth-contiguous storage.
1102
+ */
1103
+ NK_PUBLIC void nk_dots_pack_bf16_rvv(nk_bf16_t const *b, nk_size_t column_count, nk_size_t depth,
1104
+ nk_size_t b_stride_in_bytes, void *b_packed) {
1105
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
1106
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
1107
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_f32_t);
1108
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
1109
+
1110
+ nk_cross_packed_buffer_header_t *header = (nk_cross_packed_buffer_header_t *)b_packed;
1111
+ header->column_count = (nk_u32_t)column_count;
1112
+ header->depth_dimensions = (nk_u32_t)depth;
1113
+ header->depth_padded_values = (nk_u32_t)depth_padded;
1114
+
1115
+ nk_f32_t *packed = (nk_f32_t *)((char *)b_packed + sizeof(nk_cross_packed_buffer_header_t));
1116
+ nk_size_t total = column_count * depth_padded;
1117
+ {
1118
+ nk_u8_t *zero_ptr = (nk_u8_t *)packed;
1119
+ nk_size_t total_bytes = total * sizeof(nk_f32_t);
1120
+ for (nk_size_t i = 0; i < total_bytes;) {
1121
+ nk_size_t vector_length = __riscv_vsetvl_e8m8(total_bytes - i);
1122
+ __riscv_vse8_v_u8m8(zero_ptr + i, __riscv_vmv_v_x_u8m8(0, vector_length), vector_length);
1123
+ i += vector_length;
1124
+ }
1125
+ }
1126
+
1127
+ for (nk_size_t column = 0; column < column_count; ++column) {
1128
+ nk_u16_t const *src = (nk_u16_t const *)((char const *)b + column * b_stride_in_bytes);
1129
+ nk_f32_t *dst = packed + column * depth_padded;
1130
+ for (nk_size_t k = 0; k < depth; ++k) {
1131
+ union {
1132
+ nk_u32_t u;
1133
+ nk_f32_t f;
1134
+ } conv;
1135
+ conv.u = (nk_u32_t)src[k] << 16;
1136
+ dst[k] = conv.f;
1137
+ }
1138
+ }
1139
+
1140
+ // Append per-column norms after packed data
1141
+ nk_f32_t *norms = (nk_f32_t *)(packed + total);
1142
+ for (nk_size_t column = 0; column < column_count; ++column) {
1143
+ nk_bf16_t const *src = (nk_bf16_t const *)((char const *)b + column * b_stride_in_bytes);
1144
+ norms[column] = nk_dots_reduce_sumsq_bf16_(src, depth);
1145
+ }
1146
+ }
1147
+
1148
+ /**
1149
+ * @brief bf16 packed GEMM kernel: C += A * B_packed^T with f64 widened accumulation.
1150
+ *
1151
+ * Vectorizes over the depth dimension (k). For each (row, column) pair:
1152
+ * - Load A as u16m1 and convert to f32m2 via `nk_bf16m1_to_f32m2_rvv_`
1153
+ * - Load B as f32m2 directly (pre-packed)
1154
+ * - Accumulate via `vfwmacc_vv_f64m4` which widens both f32 operands to f64
1155
+ * - Horizontal reduce and narrow to f32 on store
1156
+ *
1157
+ * Register tile: process 4 rows per iteration (rows_per_tile=4).
1158
+ */
1159
+ NK_INTERNAL void nk_dots_packed_bf16_rvv_aligned_(nk_bf16_t const *a_matrix, void const *b_packed_buffer,
1160
+ nk_f32_t *c_matrix, nk_size_t row_count, nk_size_t column_count,
1161
+ nk_size_t depth, nk_size_t a_stride_in_bytes,
1162
+ nk_size_t c_stride_in_bytes) {
1163
+ nk_cross_packed_buffer_header_t const *header = (nk_cross_packed_buffer_header_t const *)b_packed_buffer;
1164
+ nk_size_t const depth_padded = header->depth_padded_values;
1165
+ nk_f32_t const *packed_data = (nk_f32_t const *)((char const *)b_packed_buffer +
1166
+ sizeof(nk_cross_packed_buffer_header_t));
1167
+
1168
+ // Zero output matrix
1169
+ for (nk_size_t i = 0; i < row_count; ++i) {
1170
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c_matrix + i * c_stride_in_bytes);
1171
+ for (nk_size_t j = 0; j < column_count; ++j) c_row[j] = 0;
1172
+ }
1173
+
1174
+ // mr=4 register tile over rows
1175
+ nk_size_t row = 0;
1176
+ for (; row + 4 <= row_count; row += 4) {
1177
+ nk_u16_t const *a_row_0 = (nk_u16_t const *)((char const *)a_matrix + (row + 0) * a_stride_in_bytes);
1178
+ nk_u16_t const *a_row_1 = (nk_u16_t const *)((char const *)a_matrix + (row + 1) * a_stride_in_bytes);
1179
+ nk_u16_t const *a_row_2 = (nk_u16_t const *)((char const *)a_matrix + (row + 2) * a_stride_in_bytes);
1180
+ nk_u16_t const *a_row_3 = (nk_u16_t const *)((char const *)a_matrix + (row + 3) * a_stride_in_bytes);
1181
+ nk_f32_t *c_row_0 = (nk_f32_t *)((char *)c_matrix + (row + 0) * c_stride_in_bytes);
1182
+ nk_f32_t *c_row_1 = (nk_f32_t *)((char *)c_matrix + (row + 1) * c_stride_in_bytes);
1183
+ nk_f32_t *c_row_2 = (nk_f32_t *)((char *)c_matrix + (row + 2) * c_stride_in_bytes);
1184
+ nk_f32_t *c_row_3 = (nk_f32_t *)((char *)c_matrix + (row + 3) * c_stride_in_bytes);
1185
+
1186
+ for (nk_size_t column = 0; column < column_count; ++column) {
1187
+ nk_f32_t const *b_column = packed_data + column * depth_padded;
1188
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
1189
+ vfloat64m4_t accumulator_0_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
1190
+ vfloat64m4_t accumulator_1_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
1191
+ vfloat64m4_t accumulator_2_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
1192
+ vfloat64m4_t accumulator_3_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
1193
+
1194
+ nk_size_t remaining = depth;
1195
+ nk_size_t k = 0;
1196
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1197
+ vector_length = __riscv_vsetvl_e32m2(remaining);
1198
+ vfloat32m2_t b_vector_f32m2 = __riscv_vle32_v_f32m2(b_column + k, vector_length);
1199
+ // Load A as u16m1 and convert to f32m2
1200
+ vuint16m1_t a_raw_0_u16m1 = __riscv_vle16_v_u16m1(a_row_0 + k, vector_length);
1201
+ vfloat32m2_t a_vector_0_f32m2 = nk_bf16m1_to_f32m2_rvv_(a_raw_0_u16m1, vector_length);
1202
+ vuint16m1_t a_raw_1_u16m1 = __riscv_vle16_v_u16m1(a_row_1 + k, vector_length);
1203
+ vfloat32m2_t a_vector_1_f32m2 = nk_bf16m1_to_f32m2_rvv_(a_raw_1_u16m1, vector_length);
1204
+ vuint16m1_t a_raw_2_u16m1 = __riscv_vle16_v_u16m1(a_row_2 + k, vector_length);
1205
+ vfloat32m2_t a_vector_2_f32m2 = nk_bf16m1_to_f32m2_rvv_(a_raw_2_u16m1, vector_length);
1206
+ vuint16m1_t a_raw_3_u16m1 = __riscv_vle16_v_u16m1(a_row_3 + k, vector_length);
1207
+ vfloat32m2_t a_vector_3_f32m2 = nk_bf16m1_to_f32m2_rvv_(a_raw_3_u16m1, vector_length);
1208
+ accumulator_0_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_0_f64m4, a_vector_0_f32m2, b_vector_f32m2,
1209
+ vector_length);
1210
+ accumulator_1_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_1_f64m4, a_vector_1_f32m2, b_vector_f32m2,
1211
+ vector_length);
1212
+ accumulator_2_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_2_f64m4, a_vector_2_f32m2, b_vector_f32m2,
1213
+ vector_length);
1214
+ accumulator_3_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_3_f64m4, a_vector_3_f32m2, b_vector_f32m2,
1215
+ vector_length);
1216
+ }
1217
+
1218
+ // Horizontal reduce and narrow to f32
1219
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
1220
+ c_row_0[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
1221
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_0_f64m4, zero_f64m1, max_vector_length));
1222
+ c_row_1[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
1223
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_1_f64m4, zero_f64m1, max_vector_length));
1224
+ c_row_2[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
1225
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_2_f64m4, zero_f64m1, max_vector_length));
1226
+ c_row_3[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
1227
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_3_f64m4, zero_f64m1, max_vector_length));
1228
+ }
1229
+ }
1230
+ // Remainder rows (mr < 4)
1231
+ for (; row < row_count; ++row) {
1232
+ nk_u16_t const *a_row = (nk_u16_t const *)((char const *)a_matrix + row * a_stride_in_bytes);
1233
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c_matrix + row * c_stride_in_bytes);
1234
+ for (nk_size_t column = 0; column < column_count; ++column) {
1235
+ nk_f32_t const *b_column = packed_data + column * depth_padded;
1236
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
1237
+ vfloat64m4_t accumulator_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
1238
+ nk_size_t remaining = depth;
1239
+ nk_size_t k = 0;
1240
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1241
+ vector_length = __riscv_vsetvl_e32m2(remaining);
1242
+ vfloat32m2_t b_vector_f32m2 = __riscv_vle32_v_f32m2(b_column + k, vector_length);
1243
+ vuint16m1_t a_raw_u16m1 = __riscv_vle16_v_u16m1(a_row + k, vector_length);
1244
+ vfloat32m2_t a_vector_f32m2 = nk_bf16m1_to_f32m2_rvv_(a_raw_u16m1, vector_length);
1245
+ accumulator_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_f64m4, a_vector_f32m2, b_vector_f32m2,
1246
+ vector_length);
1247
+ }
1248
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
1249
+ c_row[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
1250
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_f64m4, zero_f64m1, max_vector_length));
1251
+ }
1252
+ }
1253
+ }
1254
+
1255
+ /**
1256
+ * @brief Public bf16 packed GEMM wrapper matching the declared signature in dots.h.
1257
+ *
1258
+ * Dispatches to the aligned kernel for all cases — RVV's `vsetvl` handles partial
1259
+ * vectors naturally, so no separate edge kernel is needed.
1260
+ */
1261
+ NK_PUBLIC void nk_dots_packed_bf16_rvv(nk_bf16_t const *a, void const *b_packed, nk_f32_t *c, nk_size_t rows,
1262
+ nk_size_t columns, nk_size_t depth, nk_size_t a_stride_in_bytes,
1263
+ nk_size_t c_stride_in_bytes) {
1264
+ nk_dots_packed_bf16_rvv_aligned_(a, b_packed, c, rows, columns, depth, a_stride_in_bytes, c_stride_in_bytes);
1265
+ }
1266
+
1267
+ /**
1268
+ * @brief Symmetric bf16 GEMM: C = A * A^T, upper triangle + mirror.
1269
+ *
1270
+ * Uses f64 widened accumulation via `vfwmacc_vv_f64m4` for precision.
1271
+ * Both inputs are bf16, loaded as u16 and converted to f32 via `nk_bf16m1_to_f32m2_rvv_`.
1272
+ * Stride is in bytes.
1273
+ * Processes only the rows in [row_start, row_start + row_count) for parallelism.
1274
+ */
1275
+ NK_PUBLIC void nk_dots_symmetric_bf16_rvv(nk_bf16_t const *vectors, nk_size_t vectors_count, nk_size_t depth,
1276
+ nk_size_t stride_in_bytes, nk_f32_t *result, nk_size_t result_stride_in_bytes,
1277
+ nk_size_t row_start, nk_size_t row_count) {
1278
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f32_t);
1279
+ nk_size_t const row_end = (row_start + row_count < vectors_count) ? (row_start + row_count) : vectors_count;
1280
+
1281
+ for (nk_size_t i = row_start; i < row_end; ++i) {
1282
+ nk_u16_t const *a_i = (nk_u16_t const *)((char const *)vectors + i * stride_in_bytes);
1283
+ for (nk_size_t j = i; j < vectors_count; ++j) {
1284
+ nk_u16_t const *a_j = (nk_u16_t const *)((char const *)vectors + j * stride_in_bytes);
1285
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
1286
+ vfloat64m4_t accumulator_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
1287
+ nk_size_t remaining = depth;
1288
+ nk_size_t k = 0;
1289
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1290
+ vector_length = __riscv_vsetvl_e32m2(remaining);
1291
+ vuint16m1_t a_raw_u16m1 = __riscv_vle16_v_u16m1(a_i + k, vector_length);
1292
+ vfloat32m2_t a_vector_f32m2 = nk_bf16m1_to_f32m2_rvv_(a_raw_u16m1, vector_length);
1293
+ vuint16m1_t b_raw_u16m1 = __riscv_vle16_v_u16m1(a_j + k, vector_length);
1294
+ vfloat32m2_t b_vector_f32m2 = nk_bf16m1_to_f32m2_rvv_(b_raw_u16m1, vector_length);
1295
+ accumulator_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_f64m4, a_vector_f32m2, b_vector_f32m2,
1296
+ vector_length);
1297
+ }
1298
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
1299
+ nk_f32_t dot = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
1300
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_f64m4, zero_f64m1, max_vector_length));
1301
+ result[i * result_stride_elements + j] = dot;
1302
+ }
1303
+ }
1304
+ }
1305
+
1306
+ #pragma endregion BF16 Floats
1307
+
1308
+ #pragma region F16 Floats
1309
+
1310
+ /**
1311
+ * @brief Compute the packed buffer size for f16 GEMM (B stored as f32).
1312
+ *
1313
+ * VL is determined by `__riscv_vsetvlmax_e32m2()` since B is stored as f32.
1314
+ * Layout: column-panel with depth-contiguous f32 values, cache-line padding.
1315
+ */
1316
+ NK_PUBLIC nk_size_t nk_dots_packed_size_f16_rvv(nk_size_t column_count, nk_size_t depth) {
1317
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
1318
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
1319
+ // Break power-of-2 strides for cache associativity
1320
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_f32_t);
1321
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
1322
+ return sizeof(nk_cross_packed_buffer_header_t) + column_count * depth_padded * sizeof(nk_f32_t) +
1323
+ column_count * sizeof(nk_f32_t); // per-column norms
1324
+ }
1325
+
1326
+ /**
1327
+ * @brief Pack B matrix from f16 to f32 for widened dot product.
1328
+ *
1329
+ * Each f16 value is converted to f32 via `nk_f16_to_f32_serial`.
1330
+ * Padding values are zeroed. Column-panel layout with depth-contiguous storage.
1331
+ */
1332
+ NK_PUBLIC void nk_dots_pack_f16_rvv(nk_f16_t const *b, nk_size_t column_count, nk_size_t depth,
1333
+ nk_size_t b_stride_in_bytes, void *b_packed) {
1334
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
1335
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
1336
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_f32_t);
1337
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
1338
+
1339
+ nk_cross_packed_buffer_header_t *header = (nk_cross_packed_buffer_header_t *)b_packed;
1340
+ header->column_count = (nk_u32_t)column_count;
1341
+ header->depth_dimensions = (nk_u32_t)depth;
1342
+ header->depth_padded_values = (nk_u32_t)depth_padded;
1343
+
1344
+ nk_f32_t *packed = (nk_f32_t *)((char *)b_packed + sizeof(nk_cross_packed_buffer_header_t));
1345
+ nk_size_t total = column_count * depth_padded;
1346
+ {
1347
+ nk_u8_t *zero_ptr = (nk_u8_t *)packed;
1348
+ nk_size_t total_bytes = total * sizeof(nk_f32_t);
1349
+ for (nk_size_t i = 0; i < total_bytes;) {
1350
+ nk_size_t vector_length = __riscv_vsetvl_e8m8(total_bytes - i);
1351
+ __riscv_vse8_v_u8m8(zero_ptr + i, __riscv_vmv_v_x_u8m8(0, vector_length), vector_length);
1352
+ i += vector_length;
1353
+ }
1354
+ }
1355
+
1356
+ for (nk_size_t column = 0; column < column_count; ++column) {
1357
+ nk_f16_t const *src = (nk_f16_t const *)((char const *)b + column * b_stride_in_bytes);
1358
+ nk_f32_t *dst = packed + column * depth_padded;
1359
+ for (nk_size_t k = 0; k < depth; ++k) nk_f16_to_f32_serial(&src[k], &dst[k]);
1360
+ }
1361
+
1362
+ // Append per-column norms after packed data
1363
+ nk_f32_t *norms = (nk_f32_t *)(packed + total);
1364
+ for (nk_size_t column = 0; column < column_count; ++column) {
1365
+ nk_f16_t const *src = (nk_f16_t const *)((char const *)b + column * b_stride_in_bytes);
1366
+ norms[column] = nk_dots_reduce_sumsq_f16_(src, depth);
1367
+ }
1368
+ }
1369
+
1370
+ /**
1371
+ * @brief f16 packed GEMM kernel: C += A * B_packed^T with f64 widened accumulation.
1372
+ *
1373
+ * Vectorizes over the depth dimension (k). For each (row, column) pair:
1374
+ * - Load A as u16m1 and convert to f32m2 via `nk_f16m1_to_f32m2_rvv_`
1375
+ * - Load B as f32m2 directly (pre-packed)
1376
+ * - Accumulate via `vfwmacc_vv_f64m4` which widens both f32 operands to f64
1377
+ * - Horizontal reduce and narrow to f32 on store
1378
+ *
1379
+ * Register tile: process 4 rows per iteration (rows_per_tile=4).
1380
+ */
1381
+ NK_INTERNAL void nk_dots_packed_f16_rvv_aligned_(nk_f16_t const *a_matrix, void const *b_packed_buffer,
1382
+ nk_f32_t *c_matrix, nk_size_t row_count, nk_size_t column_count,
1383
+ nk_size_t depth, nk_size_t a_stride_in_bytes,
1384
+ nk_size_t c_stride_in_bytes) {
1385
+ nk_cross_packed_buffer_header_t const *header = (nk_cross_packed_buffer_header_t const *)b_packed_buffer;
1386
+ nk_size_t const depth_padded = header->depth_padded_values;
1387
+ nk_f32_t const *packed_data = (nk_f32_t const *)((char const *)b_packed_buffer +
1388
+ sizeof(nk_cross_packed_buffer_header_t));
1389
+
1390
+ // Zero output matrix
1391
+ for (nk_size_t i = 0; i < row_count; ++i) {
1392
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c_matrix + i * c_stride_in_bytes);
1393
+ for (nk_size_t j = 0; j < column_count; ++j) c_row[j] = 0;
1394
+ }
1395
+
1396
+ // mr=4 register tile over rows
1397
+ nk_size_t row = 0;
1398
+ for (; row + 4 <= row_count; row += 4) {
1399
+ nk_u16_t const *a_row_0 = (nk_u16_t const *)((char const *)a_matrix + (row + 0) * a_stride_in_bytes);
1400
+ nk_u16_t const *a_row_1 = (nk_u16_t const *)((char const *)a_matrix + (row + 1) * a_stride_in_bytes);
1401
+ nk_u16_t const *a_row_2 = (nk_u16_t const *)((char const *)a_matrix + (row + 2) * a_stride_in_bytes);
1402
+ nk_u16_t const *a_row_3 = (nk_u16_t const *)((char const *)a_matrix + (row + 3) * a_stride_in_bytes);
1403
+ nk_f32_t *c_row_0 = (nk_f32_t *)((char *)c_matrix + (row + 0) * c_stride_in_bytes);
1404
+ nk_f32_t *c_row_1 = (nk_f32_t *)((char *)c_matrix + (row + 1) * c_stride_in_bytes);
1405
+ nk_f32_t *c_row_2 = (nk_f32_t *)((char *)c_matrix + (row + 2) * c_stride_in_bytes);
1406
+ nk_f32_t *c_row_3 = (nk_f32_t *)((char *)c_matrix + (row + 3) * c_stride_in_bytes);
1407
+
1408
+ for (nk_size_t column = 0; column < column_count; ++column) {
1409
+ nk_f32_t const *b_column = packed_data + column * depth_padded;
1410
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
1411
+ vfloat64m4_t accumulator_0_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
1412
+ vfloat64m4_t accumulator_1_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
1413
+ vfloat64m4_t accumulator_2_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
1414
+ vfloat64m4_t accumulator_3_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
1415
+
1416
+ nk_size_t remaining = depth;
1417
+ nk_size_t k = 0;
1418
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1419
+ vector_length = __riscv_vsetvl_e32m2(remaining);
1420
+ vfloat32m2_t b_vector_f32m2 = __riscv_vle32_v_f32m2(b_column + k, vector_length);
1421
+ // Load A as u16m1 and convert to f32m2
1422
+ vuint16m1_t a_raw_0_u16m1 = __riscv_vle16_v_u16m1(a_row_0 + k, vector_length);
1423
+ vfloat32m2_t a_vector_0_f32m2 = nk_f16m1_to_f32m2_rvv_(a_raw_0_u16m1, vector_length);
1424
+ vuint16m1_t a_raw_1_u16m1 = __riscv_vle16_v_u16m1(a_row_1 + k, vector_length);
1425
+ vfloat32m2_t a_vector_1_f32m2 = nk_f16m1_to_f32m2_rvv_(a_raw_1_u16m1, vector_length);
1426
+ vuint16m1_t a_raw_2_u16m1 = __riscv_vle16_v_u16m1(a_row_2 + k, vector_length);
1427
+ vfloat32m2_t a_vector_2_f32m2 = nk_f16m1_to_f32m2_rvv_(a_raw_2_u16m1, vector_length);
1428
+ vuint16m1_t a_raw_3_u16m1 = __riscv_vle16_v_u16m1(a_row_3 + k, vector_length);
1429
+ vfloat32m2_t a_vector_3_f32m2 = nk_f16m1_to_f32m2_rvv_(a_raw_3_u16m1, vector_length);
1430
+ accumulator_0_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_0_f64m4, a_vector_0_f32m2, b_vector_f32m2,
1431
+ vector_length);
1432
+ accumulator_1_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_1_f64m4, a_vector_1_f32m2, b_vector_f32m2,
1433
+ vector_length);
1434
+ accumulator_2_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_2_f64m4, a_vector_2_f32m2, b_vector_f32m2,
1435
+ vector_length);
1436
+ accumulator_3_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_3_f64m4, a_vector_3_f32m2, b_vector_f32m2,
1437
+ vector_length);
1438
+ }
1439
+
1440
+ // Horizontal reduce and narrow to f32
1441
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
1442
+ c_row_0[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
1443
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_0_f64m4, zero_f64m1, max_vector_length));
1444
+ c_row_1[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
1445
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_1_f64m4, zero_f64m1, max_vector_length));
1446
+ c_row_2[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
1447
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_2_f64m4, zero_f64m1, max_vector_length));
1448
+ c_row_3[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
1449
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_3_f64m4, zero_f64m1, max_vector_length));
1450
+ }
1451
+ }
1452
+ // Remainder rows (mr < 4)
1453
+ for (; row < row_count; ++row) {
1454
+ nk_u16_t const *a_row = (nk_u16_t const *)((char const *)a_matrix + row * a_stride_in_bytes);
1455
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c_matrix + row * c_stride_in_bytes);
1456
+ for (nk_size_t column = 0; column < column_count; ++column) {
1457
+ nk_f32_t const *b_column = packed_data + column * depth_padded;
1458
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
1459
+ vfloat64m4_t accumulator_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
1460
+ nk_size_t remaining = depth;
1461
+ nk_size_t k = 0;
1462
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1463
+ vector_length = __riscv_vsetvl_e32m2(remaining);
1464
+ vfloat32m2_t b_vector_f32m2 = __riscv_vle32_v_f32m2(b_column + k, vector_length);
1465
+ vuint16m1_t a_raw_u16m1 = __riscv_vle16_v_u16m1(a_row + k, vector_length);
1466
+ vfloat32m2_t a_vector_f32m2 = nk_f16m1_to_f32m2_rvv_(a_raw_u16m1, vector_length);
1467
+ accumulator_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_f64m4, a_vector_f32m2, b_vector_f32m2,
1468
+ vector_length);
1469
+ }
1470
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
1471
+ c_row[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
1472
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_f64m4, zero_f64m1, max_vector_length));
1473
+ }
1474
+ }
1475
+ }
1476
+
1477
+ /**
1478
+ * @brief Public f16 packed GEMM wrapper matching the declared signature in dots.h.
1479
+ *
1480
+ * Dispatches to the aligned kernel for all cases — RVV's `vsetvl` handles partial
1481
+ * vectors naturally, so no separate edge kernel is needed.
1482
+ */
1483
+ NK_PUBLIC void nk_dots_packed_f16_rvv(nk_f16_t const *a, void const *b_packed, nk_f32_t *c, nk_size_t rows,
1484
+ nk_size_t columns, nk_size_t depth, nk_size_t a_stride_in_bytes,
1485
+ nk_size_t c_stride_in_bytes) {
1486
+ nk_dots_packed_f16_rvv_aligned_(a, b_packed, c, rows, columns, depth, a_stride_in_bytes, c_stride_in_bytes);
1487
+ }
1488
+
1489
+ /**
1490
+ * @brief Symmetric f16 GEMM: C = A * A^T, upper triangle + mirror.
1491
+ *
1492
+ * Uses f64 widened accumulation via `vfwmacc_vv_f64m4` for precision.
1493
+ * Both inputs are f16, loaded as u16 and converted to f32 via `nk_f16m1_to_f32m2_rvv_`.
1494
+ * Stride is in bytes.
1495
+ * Processes only the rows in [row_start, row_start + row_count) for parallelism.
1496
+ */
1497
+ NK_PUBLIC void nk_dots_symmetric_f16_rvv(nk_f16_t const *vectors, nk_size_t vectors_count, nk_size_t depth,
1498
+ nk_size_t stride_in_bytes, nk_f32_t *result, nk_size_t result_stride_in_bytes,
1499
+ nk_size_t row_start, nk_size_t row_count) {
1500
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f32_t);
1501
+ nk_size_t const row_end = (row_start + row_count < vectors_count) ? (row_start + row_count) : vectors_count;
1502
+
1503
+ for (nk_size_t i = row_start; i < row_end; ++i) {
1504
+ nk_u16_t const *a_i = (nk_u16_t const *)((char const *)vectors + i * stride_in_bytes);
1505
+ for (nk_size_t j = i; j < vectors_count; ++j) {
1506
+ nk_u16_t const *a_j = (nk_u16_t const *)((char const *)vectors + j * stride_in_bytes);
1507
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
1508
+ vfloat64m4_t accumulator_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
1509
+ nk_size_t remaining = depth;
1510
+ nk_size_t k = 0;
1511
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1512
+ vector_length = __riscv_vsetvl_e32m2(remaining);
1513
+ vuint16m1_t a_raw_u16m1 = __riscv_vle16_v_u16m1(a_i + k, vector_length);
1514
+ vfloat32m2_t a_vector_f32m2 = nk_f16m1_to_f32m2_rvv_(a_raw_u16m1, vector_length);
1515
+ vuint16m1_t b_raw_u16m1 = __riscv_vle16_v_u16m1(a_j + k, vector_length);
1516
+ vfloat32m2_t b_vector_f32m2 = nk_f16m1_to_f32m2_rvv_(b_raw_u16m1, vector_length);
1517
+ accumulator_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_f64m4, a_vector_f32m2, b_vector_f32m2,
1518
+ vector_length);
1519
+ }
1520
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
1521
+ nk_f32_t dot = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
1522
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_f64m4, zero_f64m1, max_vector_length));
1523
+ result[i * result_stride_elements + j] = dot;
1524
+ }
1525
+ }
1526
+ }
1527
+
1528
+ #pragma endregion F16 Floats
1529
+
1530
+ #pragma region I8 Integers
1531
+
1532
+ /**
1533
+ * @brief Compute the packed buffer size for i8 GEMM (B stored as i8).
1534
+ *
1535
+ * VL is determined by `__riscv_vsetvlmax_e8m1()` since B is stored as i8.
1536
+ * Layout: column-panel with depth-contiguous i8 values, cache-line padding.
1537
+ */
1538
+ NK_PUBLIC nk_size_t nk_dots_packed_size_i8_rvv(nk_size_t column_count, nk_size_t depth) {
1539
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e8m1();
1540
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
1541
+ // Break power-of-2 strides for cache associativity
1542
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_i8_t);
1543
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
1544
+ return sizeof(nk_cross_packed_buffer_header_t) + column_count * depth_padded * sizeof(nk_i8_t) +
1545
+ column_count * sizeof(nk_u32_t); // per-column norms
1546
+ }
1547
+
1548
+ /**
1549
+ * @brief Pack B matrix from i8 to i8 (direct copy) for integer dot product.
1550
+ *
1551
+ * No conversion needed — values are copied directly.
1552
+ * Padding values are zeroed. Column-panel layout with depth-contiguous storage.
1553
+ */
1554
+ NK_PUBLIC void nk_dots_pack_i8_rvv(nk_i8_t const *b, nk_size_t column_count, nk_size_t depth,
1555
+ nk_size_t b_stride_in_bytes, void *b_packed) {
1556
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e8m1();
1557
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
1558
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_i8_t);
1559
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
1560
+
1561
+ nk_cross_packed_buffer_header_t *header = (nk_cross_packed_buffer_header_t *)b_packed;
1562
+ header->column_count = (nk_u32_t)column_count;
1563
+ header->depth_dimensions = (nk_u32_t)depth;
1564
+ header->depth_padded_values = (nk_u32_t)depth_padded;
1565
+
1566
+ nk_i8_t *packed = (nk_i8_t *)((char *)b_packed + sizeof(nk_cross_packed_buffer_header_t));
1567
+ nk_size_t total = column_count * depth_padded;
1568
+ {
1569
+ nk_u8_t *zero_ptr = (nk_u8_t *)packed;
1570
+ nk_size_t total_bytes = total * sizeof(nk_i8_t);
1571
+ for (nk_size_t i = 0; i < total_bytes;) {
1572
+ nk_size_t vector_length = __riscv_vsetvl_e8m8(total_bytes - i);
1573
+ __riscv_vse8_v_u8m8(zero_ptr + i, __riscv_vmv_v_x_u8m8(0, vector_length), vector_length);
1574
+ i += vector_length;
1575
+ }
1576
+ }
1577
+
1578
+ for (nk_size_t column = 0; column < column_count; ++column) {
1579
+ nk_i8_t const *src = (nk_i8_t const *)((char const *)b + column * b_stride_in_bytes);
1580
+ nk_i8_t *dst = packed + column * depth_padded;
1581
+ for (nk_size_t k = 0; k < depth;) {
1582
+ nk_size_t vector_length = __riscv_vsetvl_e8m8(depth - k);
1583
+ __riscv_vse8_v_u8m8((nk_u8_t *)(dst + k), __riscv_vle8_v_u8m8((nk_u8_t const *)(src + k), vector_length),
1584
+ vector_length);
1585
+ k += vector_length;
1586
+ }
1587
+ }
1588
+
1589
+ // Append per-column norms after packed data
1590
+ nk_u32_t *norms = (nk_u32_t *)(packed + total);
1591
+ for (nk_size_t column = 0; column < column_count; ++column) {
1592
+ nk_i8_t const *src = (nk_i8_t const *)((char const *)b + column * b_stride_in_bytes);
1593
+ norms[column] = nk_dots_reduce_sumsq_i8_(src, depth);
1594
+ }
1595
+ }
1596
+
1597
+ /**
1598
+ * @brief i8 packed GEMM kernel: C += A * B_packed^T with i32 accumulation.
1599
+ *
1600
+ * Vectorizes over the depth dimension (k). For each (row, column) pair:
1601
+ * - Load i8 values from A and pre-packed i8 values from B
1602
+ * - Widening multiply: i8 × i8 → i16 via `vwmul`
1603
+ * - Widen-accumulate: i32 += i16 via `vwadd_wv`
1604
+ * - Horizontal reduce via `vredsum`
1605
+ *
1606
+ * Register tile: process 4 rows per iteration (rows_per_tile=4).
1607
+ * Output is nk_i32_t (integer result, no scaling).
1608
+ */
1609
+ NK_INTERNAL void nk_dots_packed_i8_rvv_aligned_(nk_i8_t const *a_matrix, void const *b_packed_buffer,
1610
+ nk_i32_t *c_matrix, nk_size_t row_count, nk_size_t column_count,
1611
+ nk_size_t depth, nk_size_t a_stride_in_bytes,
1612
+ nk_size_t c_stride_in_bytes) {
1613
+ nk_cross_packed_buffer_header_t const *header = (nk_cross_packed_buffer_header_t const *)b_packed_buffer;
1614
+ nk_size_t const depth_padded = header->depth_padded_values;
1615
+ nk_i8_t const *packed_data = (nk_i8_t const *)((char const *)b_packed_buffer +
1616
+ sizeof(nk_cross_packed_buffer_header_t));
1617
+
1618
+ // Zero output matrix
1619
+ for (nk_size_t i = 0; i < row_count; ++i) {
1620
+ nk_i32_t *c_row = (nk_i32_t *)((char *)c_matrix + i * c_stride_in_bytes);
1621
+ for (nk_size_t j = 0; j < column_count; ++j) c_row[j] = 0;
1622
+ }
1623
+
1624
+ // mr=4 register tile over rows
1625
+ nk_size_t row = 0;
1626
+ for (; row + 4 <= row_count; row += 4) {
1627
+ nk_i8_t const *a_row_0 = (nk_i8_t const *)((char const *)a_matrix + (row + 0) * a_stride_in_bytes);
1628
+ nk_i8_t const *a_row_1 = (nk_i8_t const *)((char const *)a_matrix + (row + 1) * a_stride_in_bytes);
1629
+ nk_i8_t const *a_row_2 = (nk_i8_t const *)((char const *)a_matrix + (row + 2) * a_stride_in_bytes);
1630
+ nk_i8_t const *a_row_3 = (nk_i8_t const *)((char const *)a_matrix + (row + 3) * a_stride_in_bytes);
1631
+ nk_i32_t *c_row_0 = (nk_i32_t *)((char *)c_matrix + (row + 0) * c_stride_in_bytes);
1632
+ nk_i32_t *c_row_1 = (nk_i32_t *)((char *)c_matrix + (row + 1) * c_stride_in_bytes);
1633
+ nk_i32_t *c_row_2 = (nk_i32_t *)((char *)c_matrix + (row + 2) * c_stride_in_bytes);
1634
+ nk_i32_t *c_row_3 = (nk_i32_t *)((char *)c_matrix + (row + 3) * c_stride_in_bytes);
1635
+
1636
+ for (nk_size_t column = 0; column < column_count; ++column) {
1637
+ nk_i8_t const *b_column = packed_data + column * depth_padded;
1638
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m4();
1639
+ vint32m4_t accumulator_0_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
1640
+ vint32m4_t accumulator_1_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
1641
+ vint32m4_t accumulator_2_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
1642
+ vint32m4_t accumulator_3_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
1643
+
1644
+ nk_size_t remaining = depth;
1645
+ nk_size_t k = 0;
1646
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1647
+ vector_length = __riscv_vsetvl_e8m1(remaining);
1648
+ vint8m1_t b_vector_i8m1 = __riscv_vle8_v_i8m1(b_column + k, vector_length);
1649
+ vint8m1_t a_vector_0_i8m1 = __riscv_vle8_v_i8m1(a_row_0 + k, vector_length);
1650
+ vint8m1_t a_vector_1_i8m1 = __riscv_vle8_v_i8m1(a_row_1 + k, vector_length);
1651
+ vint8m1_t a_vector_2_i8m1 = __riscv_vle8_v_i8m1(a_row_2 + k, vector_length);
1652
+ vint8m1_t a_vector_3_i8m1 = __riscv_vle8_v_i8m1(a_row_3 + k, vector_length);
1653
+ vint16m2_t product_0_i16m2 = __riscv_vwmul_vv_i16m2(a_vector_0_i8m1, b_vector_i8m1, vector_length);
1654
+ vint16m2_t product_1_i16m2 = __riscv_vwmul_vv_i16m2(a_vector_1_i8m1, b_vector_i8m1, vector_length);
1655
+ vint16m2_t product_2_i16m2 = __riscv_vwmul_vv_i16m2(a_vector_2_i8m1, b_vector_i8m1, vector_length);
1656
+ vint16m2_t product_3_i16m2 = __riscv_vwmul_vv_i16m2(a_vector_3_i8m1, b_vector_i8m1, vector_length);
1657
+ accumulator_0_i32m4 = __riscv_vwadd_wv_i32m4_tu(accumulator_0_i32m4, accumulator_0_i32m4,
1658
+ product_0_i16m2, vector_length);
1659
+ accumulator_1_i32m4 = __riscv_vwadd_wv_i32m4_tu(accumulator_1_i32m4, accumulator_1_i32m4,
1660
+ product_1_i16m2, vector_length);
1661
+ accumulator_2_i32m4 = __riscv_vwadd_wv_i32m4_tu(accumulator_2_i32m4, accumulator_2_i32m4,
1662
+ product_2_i16m2, vector_length);
1663
+ accumulator_3_i32m4 = __riscv_vwadd_wv_i32m4_tu(accumulator_3_i32m4, accumulator_3_i32m4,
1664
+ product_3_i16m2, vector_length);
1665
+ }
1666
+
1667
+ // Horizontal reduce
1668
+ vint32m1_t zero_i32m1 = __riscv_vmv_v_x_i32m1(0, 1);
1669
+ c_row_0[column] = (nk_i32_t)__riscv_vmv_x_s_i32m1_i32(
1670
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_0_i32m4, zero_i32m1, max_vector_length));
1671
+ c_row_1[column] = (nk_i32_t)__riscv_vmv_x_s_i32m1_i32(
1672
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_1_i32m4, zero_i32m1, max_vector_length));
1673
+ c_row_2[column] = (nk_i32_t)__riscv_vmv_x_s_i32m1_i32(
1674
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_2_i32m4, zero_i32m1, max_vector_length));
1675
+ c_row_3[column] = (nk_i32_t)__riscv_vmv_x_s_i32m1_i32(
1676
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_3_i32m4, zero_i32m1, max_vector_length));
1677
+ }
1678
+ }
1679
+ // Remainder rows (mr < 4)
1680
+ for (; row < row_count; ++row) {
1681
+ nk_i8_t const *a_row = (nk_i8_t const *)((char const *)a_matrix + row * a_stride_in_bytes);
1682
+ nk_i32_t *c_row = (nk_i32_t *)((char *)c_matrix + row * c_stride_in_bytes);
1683
+ for (nk_size_t column = 0; column < column_count; ++column) {
1684
+ nk_i8_t const *b_column = packed_data + column * depth_padded;
1685
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m4();
1686
+ vint32m4_t accumulator_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
1687
+ nk_size_t remaining = depth;
1688
+ nk_size_t k = 0;
1689
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1690
+ vector_length = __riscv_vsetvl_e8m1(remaining);
1691
+ vint8m1_t b_vector_i8m1 = __riscv_vle8_v_i8m1(b_column + k, vector_length);
1692
+ vint8m1_t a_vector_i8m1 = __riscv_vle8_v_i8m1(a_row + k, vector_length);
1693
+ vint16m2_t product_i16m2 = __riscv_vwmul_vv_i16m2(a_vector_i8m1, b_vector_i8m1, vector_length);
1694
+ accumulator_i32m4 = __riscv_vwadd_wv_i32m4_tu(accumulator_i32m4, accumulator_i32m4, product_i16m2,
1695
+ vector_length);
1696
+ }
1697
+ vint32m1_t zero_i32m1 = __riscv_vmv_v_x_i32m1(0, 1);
1698
+ c_row[column] = (nk_i32_t)__riscv_vmv_x_s_i32m1_i32(
1699
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_i32m4, zero_i32m1, max_vector_length));
1700
+ }
1701
+ }
1702
+ }
1703
+
1704
+ /**
1705
+ * @brief Public i8 packed GEMM wrapper matching the declared signature in dots.h.
1706
+ *
1707
+ * Dispatches to the aligned kernel for all cases — RVV's `vsetvl` handles partial
1708
+ * vectors naturally, so no separate edge kernel is needed.
1709
+ */
1710
+ NK_PUBLIC void nk_dots_packed_i8_rvv(nk_i8_t const *a, void const *b_packed, nk_i32_t *c, nk_size_t rows,
1711
+ nk_size_t columns, nk_size_t depth, nk_size_t a_stride_in_bytes,
1712
+ nk_size_t c_stride_in_bytes) {
1713
+ nk_dots_packed_i8_rvv_aligned_(a, b_packed, c, rows, columns, depth, a_stride_in_bytes, c_stride_in_bytes);
1714
+ }
1715
+
1716
+ /**
1717
+ * @brief Symmetric i8 GEMM: C = A * A^T, upper triangle + mirror.
1718
+ *
1719
+ * Uses integer i8 arithmetic with i32 accumulation.
1720
+ * Both inputs are i8, widened via i8 × i8 → i16 → i32 accumulation.
1721
+ * Stride is in bytes.
1722
+ * Processes only the rows in [row_start, row_start + row_count) for parallelism.
1723
+ */
1724
+ NK_PUBLIC void nk_dots_symmetric_i8_rvv(nk_i8_t const *vectors, nk_size_t vectors_count, nk_size_t depth,
1725
+ nk_size_t stride_in_bytes, nk_i32_t *result, nk_size_t result_stride_in_bytes,
1726
+ nk_size_t row_start, nk_size_t row_count) {
1727
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_i32_t);
1728
+ nk_size_t const row_end = (row_start + row_count < vectors_count) ? (row_start + row_count) : vectors_count;
1729
+
1730
+ for (nk_size_t i = row_start; i < row_end; ++i) {
1731
+ nk_i8_t const *a_i = (nk_i8_t const *)((char const *)vectors + i * stride_in_bytes);
1732
+ for (nk_size_t j = i; j < vectors_count; ++j) {
1733
+ nk_i8_t const *a_j = (nk_i8_t const *)((char const *)vectors + j * stride_in_bytes);
1734
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m4();
1735
+ vint32m4_t accumulator_i32m4 = __riscv_vmv_v_x_i32m4(0, max_vector_length);
1736
+ nk_size_t remaining = depth;
1737
+ nk_size_t k = 0;
1738
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1739
+ vector_length = __riscv_vsetvl_e8m1(remaining);
1740
+ vint8m1_t a_vector_i8m1 = __riscv_vle8_v_i8m1(a_i + k, vector_length);
1741
+ vint8m1_t b_vector_i8m1 = __riscv_vle8_v_i8m1(a_j + k, vector_length);
1742
+ vint16m2_t product_i16m2 = __riscv_vwmul_vv_i16m2(a_vector_i8m1, b_vector_i8m1, vector_length);
1743
+ accumulator_i32m4 = __riscv_vwadd_wv_i32m4_tu(accumulator_i32m4, accumulator_i32m4, product_i16m2,
1744
+ vector_length);
1745
+ }
1746
+ vint32m1_t zero_i32m1 = __riscv_vmv_v_x_i32m1(0, 1);
1747
+ nk_i32_t dot = (nk_i32_t)__riscv_vmv_x_s_i32m1_i32(
1748
+ __riscv_vredsum_vs_i32m4_i32m1(accumulator_i32m4, zero_i32m1, max_vector_length));
1749
+ result[i * result_stride_elements + j] = dot;
1750
+ }
1751
+ }
1752
+ }
1753
+
1754
+ #pragma endregion I8 Integers
1755
+
1756
+ #pragma region U8 Integers
1757
+
1758
+ /**
1759
+ * @brief Compute the packed buffer size for u8 GEMM (B stored as u8).
1760
+ *
1761
+ * VL is determined by `__riscv_vsetvlmax_e8m1()` since B is stored as u8.
1762
+ * Layout: column-panel with depth-contiguous u8 values, cache-line padding.
1763
+ */
1764
+ NK_PUBLIC nk_size_t nk_dots_packed_size_u8_rvv(nk_size_t column_count, nk_size_t depth) {
1765
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e8m1();
1766
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
1767
+ // Break power-of-2 strides for cache associativity
1768
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_u8_t);
1769
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
1770
+ return sizeof(nk_cross_packed_buffer_header_t) + column_count * depth_padded * sizeof(nk_u8_t) +
1771
+ column_count * sizeof(nk_u32_t); // per-column norms
1772
+ }
1773
+
1774
+ /**
1775
+ * @brief Pack B matrix from u8 to u8 (direct copy) for integer dot product.
1776
+ *
1777
+ * No conversion needed — values are copied directly.
1778
+ * Padding values are zeroed. Column-panel layout with depth-contiguous storage.
1779
+ */
1780
+ NK_PUBLIC void nk_dots_pack_u8_rvv(nk_u8_t const *b, nk_size_t column_count, nk_size_t depth,
1781
+ nk_size_t b_stride_in_bytes, void *b_packed) {
1782
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e8m1();
1783
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
1784
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_u8_t);
1785
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
1786
+
1787
+ nk_cross_packed_buffer_header_t *header = (nk_cross_packed_buffer_header_t *)b_packed;
1788
+ header->column_count = (nk_u32_t)column_count;
1789
+ header->depth_dimensions = (nk_u32_t)depth;
1790
+ header->depth_padded_values = (nk_u32_t)depth_padded;
1791
+
1792
+ nk_u8_t *packed = (nk_u8_t *)((char *)b_packed + sizeof(nk_cross_packed_buffer_header_t));
1793
+ nk_size_t total = column_count * depth_padded;
1794
+ {
1795
+ nk_u8_t *zero_ptr = (nk_u8_t *)packed;
1796
+ nk_size_t total_bytes = total * sizeof(nk_u8_t);
1797
+ for (nk_size_t i = 0; i < total_bytes;) {
1798
+ nk_size_t vector_length = __riscv_vsetvl_e8m8(total_bytes - i);
1799
+ __riscv_vse8_v_u8m8(zero_ptr + i, __riscv_vmv_v_x_u8m8(0, vector_length), vector_length);
1800
+ i += vector_length;
1801
+ }
1802
+ }
1803
+
1804
+ for (nk_size_t column = 0; column < column_count; ++column) {
1805
+ nk_u8_t const *src = (nk_u8_t const *)((char const *)b + column * b_stride_in_bytes);
1806
+ nk_u8_t *dst = packed + column * depth_padded;
1807
+ for (nk_size_t k = 0; k < depth;) {
1808
+ nk_size_t vector_length = __riscv_vsetvl_e8m8(depth - k);
1809
+ __riscv_vse8_v_u8m8(dst + k, __riscv_vle8_v_u8m8(src + k, vector_length), vector_length);
1810
+ k += vector_length;
1811
+ }
1812
+ }
1813
+
1814
+ // Append per-column norms after packed data
1815
+ nk_u32_t *norms = (nk_u32_t *)(packed + total);
1816
+ for (nk_size_t column = 0; column < column_count; ++column) {
1817
+ nk_u8_t const *src = (nk_u8_t const *)((char const *)b + column * b_stride_in_bytes);
1818
+ norms[column] = nk_dots_reduce_sumsq_u8_(src, depth);
1819
+ }
1820
+ }
1821
+
1822
+ /**
1823
+ * @brief u8 packed GEMM kernel: C += A * B_packed^T with u32 accumulation.
1824
+ *
1825
+ * Vectorizes over the depth dimension (k). For each (row, column) pair:
1826
+ * - Load u8 values from A and pre-packed u8 values from B
1827
+ * - Widening multiply: u8 × u8 → u16 via `vwmulu`
1828
+ * - Widen-accumulate: u32 += u16 via `vwaddu_wv`
1829
+ * - Horizontal reduce via `vredsum`
1830
+ *
1831
+ * Register tile: process 4 rows per iteration (rows_per_tile=4).
1832
+ * Output is nk_u32_t (unsigned integer result, no scaling).
1833
+ */
1834
+ NK_INTERNAL void nk_dots_packed_u8_rvv_aligned_(nk_u8_t const *a_matrix, void const *b_packed_buffer,
1835
+ nk_u32_t *c_matrix, nk_size_t row_count, nk_size_t column_count,
1836
+ nk_size_t depth, nk_size_t a_stride_in_bytes,
1837
+ nk_size_t c_stride_in_bytes) {
1838
+ nk_cross_packed_buffer_header_t const *header = (nk_cross_packed_buffer_header_t const *)b_packed_buffer;
1839
+ nk_size_t const depth_padded = header->depth_padded_values;
1840
+ nk_u8_t const *packed_data = (nk_u8_t const *)((char const *)b_packed_buffer +
1841
+ sizeof(nk_cross_packed_buffer_header_t));
1842
+
1843
+ // Zero output matrix
1844
+ for (nk_size_t i = 0; i < row_count; ++i) {
1845
+ nk_u32_t *c_row = (nk_u32_t *)((char *)c_matrix + i * c_stride_in_bytes);
1846
+ for (nk_size_t j = 0; j < column_count; ++j) c_row[j] = 0;
1847
+ }
1848
+
1849
+ // mr=4 register tile over rows
1850
+ nk_size_t row = 0;
1851
+ for (; row + 4 <= row_count; row += 4) {
1852
+ nk_u8_t const *a_row_0 = (nk_u8_t const *)((char const *)a_matrix + (row + 0) * a_stride_in_bytes);
1853
+ nk_u8_t const *a_row_1 = (nk_u8_t const *)((char const *)a_matrix + (row + 1) * a_stride_in_bytes);
1854
+ nk_u8_t const *a_row_2 = (nk_u8_t const *)((char const *)a_matrix + (row + 2) * a_stride_in_bytes);
1855
+ nk_u8_t const *a_row_3 = (nk_u8_t const *)((char const *)a_matrix + (row + 3) * a_stride_in_bytes);
1856
+ nk_u32_t *c_row_0 = (nk_u32_t *)((char *)c_matrix + (row + 0) * c_stride_in_bytes);
1857
+ nk_u32_t *c_row_1 = (nk_u32_t *)((char *)c_matrix + (row + 1) * c_stride_in_bytes);
1858
+ nk_u32_t *c_row_2 = (nk_u32_t *)((char *)c_matrix + (row + 2) * c_stride_in_bytes);
1859
+ nk_u32_t *c_row_3 = (nk_u32_t *)((char *)c_matrix + (row + 3) * c_stride_in_bytes);
1860
+
1861
+ for (nk_size_t column = 0; column < column_count; ++column) {
1862
+ nk_u8_t const *b_column = packed_data + column * depth_padded;
1863
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m4();
1864
+ vuint32m4_t accumulator_0_u32m4 = __riscv_vmv_v_x_u32m4(0, max_vector_length);
1865
+ vuint32m4_t accumulator_1_u32m4 = __riscv_vmv_v_x_u32m4(0, max_vector_length);
1866
+ vuint32m4_t accumulator_2_u32m4 = __riscv_vmv_v_x_u32m4(0, max_vector_length);
1867
+ vuint32m4_t accumulator_3_u32m4 = __riscv_vmv_v_x_u32m4(0, max_vector_length);
1868
+
1869
+ nk_size_t remaining = depth;
1870
+ nk_size_t k = 0;
1871
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1872
+ vector_length = __riscv_vsetvl_e8m1(remaining);
1873
+ vuint8m1_t b_vector_u8m1 = __riscv_vle8_v_u8m1(b_column + k, vector_length);
1874
+ vuint8m1_t a_vector_0_u8m1 = __riscv_vle8_v_u8m1(a_row_0 + k, vector_length);
1875
+ vuint8m1_t a_vector_1_u8m1 = __riscv_vle8_v_u8m1(a_row_1 + k, vector_length);
1876
+ vuint8m1_t a_vector_2_u8m1 = __riscv_vle8_v_u8m1(a_row_2 + k, vector_length);
1877
+ vuint8m1_t a_vector_3_u8m1 = __riscv_vle8_v_u8m1(a_row_3 + k, vector_length);
1878
+ vuint16m2_t product_0_u16m2 = __riscv_vwmulu_vv_u16m2(a_vector_0_u8m1, b_vector_u8m1, vector_length);
1879
+ vuint16m2_t product_1_u16m2 = __riscv_vwmulu_vv_u16m2(a_vector_1_u8m1, b_vector_u8m1, vector_length);
1880
+ vuint16m2_t product_2_u16m2 = __riscv_vwmulu_vv_u16m2(a_vector_2_u8m1, b_vector_u8m1, vector_length);
1881
+ vuint16m2_t product_3_u16m2 = __riscv_vwmulu_vv_u16m2(a_vector_3_u8m1, b_vector_u8m1, vector_length);
1882
+ accumulator_0_u32m4 = __riscv_vwaddu_wv_u32m4_tu(accumulator_0_u32m4, accumulator_0_u32m4,
1883
+ product_0_u16m2, vector_length);
1884
+ accumulator_1_u32m4 = __riscv_vwaddu_wv_u32m4_tu(accumulator_1_u32m4, accumulator_1_u32m4,
1885
+ product_1_u16m2, vector_length);
1886
+ accumulator_2_u32m4 = __riscv_vwaddu_wv_u32m4_tu(accumulator_2_u32m4, accumulator_2_u32m4,
1887
+ product_2_u16m2, vector_length);
1888
+ accumulator_3_u32m4 = __riscv_vwaddu_wv_u32m4_tu(accumulator_3_u32m4, accumulator_3_u32m4,
1889
+ product_3_u16m2, vector_length);
1890
+ }
1891
+
1892
+ // Horizontal reduce
1893
+ vuint32m1_t zero_u32m1 = __riscv_vmv_v_x_u32m1(0, 1);
1894
+ c_row_0[column] = (nk_u32_t)__riscv_vmv_x_s_u32m1_u32(
1895
+ __riscv_vredsum_vs_u32m4_u32m1(accumulator_0_u32m4, zero_u32m1, max_vector_length));
1896
+ c_row_1[column] = (nk_u32_t)__riscv_vmv_x_s_u32m1_u32(
1897
+ __riscv_vredsum_vs_u32m4_u32m1(accumulator_1_u32m4, zero_u32m1, max_vector_length));
1898
+ c_row_2[column] = (nk_u32_t)__riscv_vmv_x_s_u32m1_u32(
1899
+ __riscv_vredsum_vs_u32m4_u32m1(accumulator_2_u32m4, zero_u32m1, max_vector_length));
1900
+ c_row_3[column] = (nk_u32_t)__riscv_vmv_x_s_u32m1_u32(
1901
+ __riscv_vredsum_vs_u32m4_u32m1(accumulator_3_u32m4, zero_u32m1, max_vector_length));
1902
+ }
1903
+ }
1904
+ // Remainder rows (mr < 4)
1905
+ for (; row < row_count; ++row) {
1906
+ nk_u8_t const *a_row = (nk_u8_t const *)((char const *)a_matrix + row * a_stride_in_bytes);
1907
+ nk_u32_t *c_row = (nk_u32_t *)((char *)c_matrix + row * c_stride_in_bytes);
1908
+ for (nk_size_t column = 0; column < column_count; ++column) {
1909
+ nk_u8_t const *b_column = packed_data + column * depth_padded;
1910
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m4();
1911
+ vuint32m4_t accumulator_u32m4 = __riscv_vmv_v_x_u32m4(0, max_vector_length);
1912
+ nk_size_t remaining = depth;
1913
+ nk_size_t k = 0;
1914
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1915
+ vector_length = __riscv_vsetvl_e8m1(remaining);
1916
+ vuint8m1_t b_vector_u8m1 = __riscv_vle8_v_u8m1(b_column + k, vector_length);
1917
+ vuint8m1_t a_vector_u8m1 = __riscv_vle8_v_u8m1(a_row + k, vector_length);
1918
+ vuint16m2_t product_u16m2 = __riscv_vwmulu_vv_u16m2(a_vector_u8m1, b_vector_u8m1, vector_length);
1919
+ accumulator_u32m4 = __riscv_vwaddu_wv_u32m4_tu(accumulator_u32m4, accumulator_u32m4, product_u16m2,
1920
+ vector_length);
1921
+ }
1922
+ vuint32m1_t zero_u32m1 = __riscv_vmv_v_x_u32m1(0, 1);
1923
+ c_row[column] = (nk_u32_t)__riscv_vmv_x_s_u32m1_u32(
1924
+ __riscv_vredsum_vs_u32m4_u32m1(accumulator_u32m4, zero_u32m1, max_vector_length));
1925
+ }
1926
+ }
1927
+ }
1928
+
1929
+ /**
1930
+ * @brief Public u8 packed GEMM wrapper matching the declared signature in dots.h.
1931
+ *
1932
+ * Dispatches to the aligned kernel for all cases — RVV's `vsetvl` handles partial
1933
+ * vectors naturally, so no separate edge kernel is needed.
1934
+ */
1935
+ NK_PUBLIC void nk_dots_packed_u8_rvv(nk_u8_t const *a, void const *b_packed, nk_u32_t *c, nk_size_t rows,
1936
+ nk_size_t columns, nk_size_t depth, nk_size_t a_stride_in_bytes,
1937
+ nk_size_t c_stride_in_bytes) {
1938
+ nk_dots_packed_u8_rvv_aligned_(a, b_packed, c, rows, columns, depth, a_stride_in_bytes, c_stride_in_bytes);
1939
+ }
1940
+
1941
+ /**
1942
+ * @brief Symmetric u8 GEMM: C = A * A^T, upper triangle + mirror.
1943
+ *
1944
+ * Uses unsigned integer u8 arithmetic with u32 accumulation.
1945
+ * Both inputs are u8, widened via u8 × u8 → u16 → u32 accumulation.
1946
+ * Stride is in bytes.
1947
+ * Processes only the rows in [row_start, row_start + row_count) for parallelism.
1948
+ */
1949
+ NK_PUBLIC void nk_dots_symmetric_u8_rvv(nk_u8_t const *vectors, nk_size_t vectors_count, nk_size_t depth,
1950
+ nk_size_t stride_in_bytes, nk_u32_t *result, nk_size_t result_stride_in_bytes,
1951
+ nk_size_t row_start, nk_size_t row_count) {
1952
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_u32_t);
1953
+ nk_size_t const row_end = (row_start + row_count < vectors_count) ? (row_start + row_count) : vectors_count;
1954
+
1955
+ for (nk_size_t i = row_start; i < row_end; ++i) {
1956
+ nk_u8_t const *a_i = (nk_u8_t const *)((char const *)vectors + i * stride_in_bytes);
1957
+ for (nk_size_t j = i; j < vectors_count; ++j) {
1958
+ nk_u8_t const *a_j = (nk_u8_t const *)((char const *)vectors + j * stride_in_bytes);
1959
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m4();
1960
+ vuint32m4_t accumulator_u32m4 = __riscv_vmv_v_x_u32m4(0, max_vector_length);
1961
+ nk_size_t remaining = depth;
1962
+ nk_size_t k = 0;
1963
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
1964
+ vector_length = __riscv_vsetvl_e8m1(remaining);
1965
+ vuint8m1_t a_vector_u8m1 = __riscv_vle8_v_u8m1(a_i + k, vector_length);
1966
+ vuint8m1_t b_vector_u8m1 = __riscv_vle8_v_u8m1(a_j + k, vector_length);
1967
+ vuint16m2_t product_u16m2 = __riscv_vwmulu_vv_u16m2(a_vector_u8m1, b_vector_u8m1, vector_length);
1968
+ accumulator_u32m4 = __riscv_vwaddu_wv_u32m4_tu(accumulator_u32m4, accumulator_u32m4, product_u16m2,
1969
+ vector_length);
1970
+ }
1971
+ vuint32m1_t zero_u32m1 = __riscv_vmv_v_x_u32m1(0, 1);
1972
+ nk_u32_t dot = (nk_u32_t)__riscv_vmv_x_s_u32m1_u32(
1973
+ __riscv_vredsum_vs_u32m4_u32m1(accumulator_u32m4, zero_u32m1, max_vector_length));
1974
+ result[i * result_stride_elements + j] = dot;
1975
+ }
1976
+ }
1977
+ }
1978
+
1979
+ #pragma endregion U8 Integers
1980
+
1981
+ #pragma region E4M3 Floats
1982
+
1983
+ /**
1984
+ * @brief E4M3 magnitude LUT: 7-bit magnitude → f32 bit pattern (u32).
1985
+ * nk_e4m3_magnitude_lut_rvv_[i] = float_to_bits(e4m3_to_f32(i)) for i=0..127.
1986
+ * E4M3FN: 4 exponent bits (bias=7), 3 mantissa bits, no infinity,
1987
+ * NaN = magnitude 0x7F only.
1988
+ */
1989
+ static nk_u32_t const nk_e4m3_magnitude_lut_rvv_[128] = {
1990
+ 0x00000000u, 0x3B000000u, 0x3B800000u, 0x3BC00000u,
1991
+ 0x3C000000u, 0x3C200000u, 0x3C400000u, 0x3C600000u, /* [ 0.. 7] */
1992
+ 0x3C800000u, 0x3C900000u, 0x3CA00000u, 0x3CB00000u,
1993
+ 0x3CC00000u, 0x3CD00000u, 0x3CE00000u, 0x3CF00000u, /* [ 8.. 15] */
1994
+ 0x3D000000u, 0x3D100000u, 0x3D200000u, 0x3D300000u,
1995
+ 0x3D400000u, 0x3D500000u, 0x3D600000u, 0x3D700000u, /* [ 16.. 23] */
1996
+ 0x3D800000u, 0x3D900000u, 0x3DA00000u, 0x3DB00000u,
1997
+ 0x3DC00000u, 0x3DD00000u, 0x3DE00000u, 0x3DF00000u, /* [ 24.. 31] */
1998
+ 0x3E000000u, 0x3E100000u, 0x3E200000u, 0x3E300000u,
1999
+ 0x3E400000u, 0x3E500000u, 0x3E600000u, 0x3E700000u, /* [ 32.. 39] */
2000
+ 0x3E800000u, 0x3E900000u, 0x3EA00000u, 0x3EB00000u,
2001
+ 0x3EC00000u, 0x3ED00000u, 0x3EE00000u, 0x3EF00000u, /* [ 40.. 47] */
2002
+ 0x3F000000u, 0x3F100000u, 0x3F200000u, 0x3F300000u,
2003
+ 0x3F400000u, 0x3F500000u, 0x3F600000u, 0x3F700000u, /* [ 48.. 55] */
2004
+ 0x3F800000u, 0x3F900000u, 0x3FA00000u, 0x3FB00000u,
2005
+ 0x3FC00000u, 0x3FD00000u, 0x3FE00000u, 0x3FF00000u, /* [ 56.. 63] */
2006
+ 0x40000000u, 0x40100000u, 0x40200000u, 0x40300000u,
2007
+ 0x40400000u, 0x40500000u, 0x40600000u, 0x40700000u, /* [ 64.. 71] */
2008
+ 0x40800000u, 0x40900000u, 0x40A00000u, 0x40B00000u,
2009
+ 0x40C00000u, 0x40D00000u, 0x40E00000u, 0x40F00000u, /* [ 72.. 79] */
2010
+ 0x41000000u, 0x41100000u, 0x41200000u, 0x41300000u,
2011
+ 0x41400000u, 0x41500000u, 0x41600000u, 0x41700000u, /* [ 80.. 87] */
2012
+ 0x41800000u, 0x41900000u, 0x41A00000u, 0x41B00000u,
2013
+ 0x41C00000u, 0x41D00000u, 0x41E00000u, 0x41F00000u, /* [ 88.. 95] */
2014
+ 0x42000000u, 0x42100000u, 0x42200000u, 0x42300000u,
2015
+ 0x42400000u, 0x42500000u, 0x42600000u, 0x42700000u, /* [ 96..103] */
2016
+ 0x42800000u, 0x42900000u, 0x42A00000u, 0x42B00000u,
2017
+ 0x42C00000u, 0x42D00000u, 0x42E00000u, 0x42F00000u, /* [104..111] */
2018
+ 0x43000000u, 0x43100000u, 0x43200000u, 0x43300000u,
2019
+ 0x43400000u, 0x43500000u, 0x43600000u, 0x43700000u, /* [112..119] */
2020
+ 0x43800000u, 0x43900000u, 0x43A00000u, 0x43B00000u,
2021
+ 0x43C00000u, 0x43D00000u, 0x43E00000u, 0x7FC00000u /* [120..127] */
2022
+ };
2023
+
2024
+ NK_PUBLIC nk_size_t nk_dots_packed_size_e4m3_rvv(nk_size_t column_count, nk_size_t depth) {
2025
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
2026
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
2027
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_f32_t);
2028
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
2029
+ return sizeof(nk_cross_packed_buffer_header_t) + column_count * depth_padded * sizeof(nk_f32_t) +
2030
+ column_count * sizeof(nk_f32_t); // per-column norms
2031
+ }
2032
+
2033
+ /**
2034
+ * @brief Pack B matrix from e4m3 to f32 for floating-point dot product.
2035
+ *
2036
+ * Each e4m3 byte is converted to f32 via `nk_e4m3_to_f32_serial`.
2037
+ * Padding values are zeroed. Column-panel layout with depth-contiguous storage.
2038
+ */
2039
+ NK_PUBLIC void nk_dots_pack_e4m3_rvv(nk_e4m3_t const *b, nk_size_t column_count, nk_size_t depth,
2040
+ nk_size_t b_stride_in_bytes, void *b_packed) {
2041
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
2042
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
2043
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_f32_t);
2044
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
2045
+
2046
+ nk_cross_packed_buffer_header_t *header = (nk_cross_packed_buffer_header_t *)b_packed;
2047
+ header->column_count = (nk_u32_t)column_count;
2048
+ header->depth_dimensions = (nk_u32_t)depth;
2049
+ header->depth_padded_values = (nk_u32_t)depth_padded;
2050
+
2051
+ nk_f32_t *packed = (nk_f32_t *)((char *)b_packed + sizeof(nk_cross_packed_buffer_header_t));
2052
+ nk_size_t total = column_count * depth_padded;
2053
+ {
2054
+ nk_u8_t *zero_ptr = (nk_u8_t *)packed;
2055
+ nk_size_t total_bytes = total * sizeof(nk_f32_t);
2056
+ for (nk_size_t i = 0; i < total_bytes;) {
2057
+ nk_size_t vector_length = __riscv_vsetvl_e8m8(total_bytes - i);
2058
+ __riscv_vse8_v_u8m8(zero_ptr + i, __riscv_vmv_v_x_u8m8(0, vector_length), vector_length);
2059
+ i += vector_length;
2060
+ }
2061
+ }
2062
+
2063
+ for (nk_size_t column = 0; column < column_count; ++column) {
2064
+ nk_e4m3_t const *src = (nk_e4m3_t const *)((char const *)b + column * b_stride_in_bytes);
2065
+ nk_f32_t *dst = packed + column * depth_padded;
2066
+ for (nk_size_t k = 0; k < depth; ++k) nk_e4m3_to_f32_serial(&src[k], &dst[k]);
2067
+ }
2068
+
2069
+ // Append per-column norms after packed data
2070
+ nk_f32_t *norms = (nk_f32_t *)(packed + total);
2071
+ for (nk_size_t column = 0; column < column_count; ++column) {
2072
+ nk_e4m3_t const *src = (nk_e4m3_t const *)((char const *)b + column * b_stride_in_bytes);
2073
+ norms[column] = nk_dots_reduce_sumsq_e4m3_(src, depth);
2074
+ }
2075
+ }
2076
+
2077
+ /**
2078
+ * @brief e4m3 packed GEMM kernel: C += A * B_packed^T with f64 widened accumulation.
2079
+ *
2080
+ * Vectorizes over the depth dimension (k). For each (row, column) pair:
2081
+ * - Load pre-packed f32 values from B
2082
+ * - Load raw e4m3 bytes from A, convert on-the-fly via 128-entry f32 LUT gather:
2083
+ * extract 7-bit magnitude, zero-extend to u32, compute byte offsets (x4),
2084
+ * gather f32 bit patterns, inject sign bit from bit 7 (<<24), reinterpret as f32
2085
+ * - Widening FMA: f32xf32 → f64 via `vfwmacc_vv_f64m4`
2086
+ *
2087
+ * Register tile: process 2 rows per iteration (rows_per_tile=2, u32m2 gather + f64m4 accumulator is register-heavy).
2088
+ */
2089
+ NK_INTERNAL void nk_dots_packed_e4m3_rvv_aligned_(nk_e4m3_t const *a_matrix, void const *b_packed_buffer,
2090
+ nk_f32_t *c_matrix, nk_size_t row_count, nk_size_t column_count,
2091
+ nk_size_t depth, nk_size_t a_stride_in_bytes,
2092
+ nk_size_t c_stride_in_bytes) {
2093
+ nk_cross_packed_buffer_header_t const *header = (nk_cross_packed_buffer_header_t const *)b_packed_buffer;
2094
+ nk_size_t const depth_padded = header->depth_padded_values;
2095
+ nk_f32_t const *packed_data = (nk_f32_t const *)((char const *)b_packed_buffer +
2096
+ sizeof(nk_cross_packed_buffer_header_t));
2097
+
2098
+ // Zero output matrix
2099
+ for (nk_size_t i = 0; i < row_count; ++i) {
2100
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c_matrix + i * c_stride_in_bytes);
2101
+ for (nk_size_t j = 0; j < column_count; ++j) c_row[j] = 0;
2102
+ }
2103
+
2104
+ // mr=2 register tile over rows
2105
+ nk_size_t row = 0;
2106
+ for (; row + 2 <= row_count; row += 2) {
2107
+ nk_u8_t const *a_row_0 = (nk_u8_t const *)((char const *)a_matrix + (row + 0) * a_stride_in_bytes);
2108
+ nk_u8_t const *a_row_1 = (nk_u8_t const *)((char const *)a_matrix + (row + 1) * a_stride_in_bytes);
2109
+ nk_f32_t *c_row_0 = (nk_f32_t *)((char *)c_matrix + (row + 0) * c_stride_in_bytes);
2110
+ nk_f32_t *c_row_1 = (nk_f32_t *)((char *)c_matrix + (row + 1) * c_stride_in_bytes);
2111
+
2112
+ for (nk_size_t column = 0; column < column_count; ++column) {
2113
+ nk_f32_t const *b_column = packed_data + column * depth_padded;
2114
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
2115
+ vfloat64m4_t accumulator_0_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
2116
+ vfloat64m4_t accumulator_1_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
2117
+
2118
+ nk_size_t remaining = depth;
2119
+ nk_size_t k = 0;
2120
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
2121
+ vector_length = __riscv_vsetvl_e32m2(remaining);
2122
+
2123
+ // Load pre-packed f32 B values
2124
+ vfloat32m2_t b_vector_f32m2 = __riscv_vle32_v_f32m2(b_column + k, vector_length);
2125
+
2126
+ // Load raw e4m3 bytes from each A row
2127
+ vuint8mf2_t raw0_u8mf2 = __riscv_vle8_v_u8mf2(a_row_0 + k, vector_length);
2128
+ vuint8mf2_t raw1_u8mf2 = __riscv_vle8_v_u8mf2(a_row_1 + k, vector_length);
2129
+
2130
+ // Extract 7-bit magnitudes, zero-extend to u32, compute byte offsets for f32 LUT
2131
+ vuint8mf2_t mag0_u8mf2 = __riscv_vand_vx_u8mf2(raw0_u8mf2, 0x7F, vector_length);
2132
+ vuint8mf2_t mag1_u8mf2 = __riscv_vand_vx_u8mf2(raw1_u8mf2, 0x7F, vector_length);
2133
+ vuint32m2_t idx0_u32m2 = __riscv_vzext_vf4_u32m2(mag0_u8mf2, vector_length);
2134
+ vuint32m2_t idx1_u32m2 = __riscv_vzext_vf4_u32m2(mag1_u8mf2, vector_length);
2135
+ vuint32m2_t off0_u32m2 = __riscv_vsll_vx_u32m2(idx0_u32m2, 2,
2136
+ vector_length); // byte offsets = index * 4
2137
+ vuint32m2_t off1_u32m2 = __riscv_vsll_vx_u32m2(idx1_u32m2, 2, vector_length);
2138
+
2139
+ // Gather f32 bit patterns from magnitude LUT
2140
+ vuint32m2_t bits0_u32m2 = __riscv_vluxei32_v_u32m2(nk_e4m3_magnitude_lut_rvv_, off0_u32m2,
2141
+ vector_length);
2142
+ vuint32m2_t bits1_u32m2 = __riscv_vluxei32_v_u32m2(nk_e4m3_magnitude_lut_rvv_, off1_u32m2,
2143
+ vector_length);
2144
+
2145
+ // Extract sign bit 7, shift to f32 sign position (bit 31)
2146
+ vuint8mf2_t sign0_u8mf2 = __riscv_vand_vx_u8mf2(raw0_u8mf2, 0x80, vector_length);
2147
+ vuint8mf2_t sign1_u8mf2 = __riscv_vand_vx_u8mf2(raw1_u8mf2, 0x80, vector_length);
2148
+ vuint32m2_t sign0_u32m2 = __riscv_vsll_vx_u32m2(__riscv_vzext_vf4_u32m2(sign0_u8mf2, vector_length), 24,
2149
+ vector_length);
2150
+ vuint32m2_t sign1_u32m2 = __riscv_vsll_vx_u32m2(__riscv_vzext_vf4_u32m2(sign1_u8mf2, vector_length), 24,
2151
+ vector_length);
2152
+
2153
+ // Apply sign and reinterpret as f32
2154
+ vfloat32m2_t a_vector_0_f32m2 = __riscv_vreinterpret_v_u32m2_f32m2(
2155
+ __riscv_vor_vv_u32m2(bits0_u32m2, sign0_u32m2, vector_length));
2156
+ vfloat32m2_t a_vector_1_f32m2 = __riscv_vreinterpret_v_u32m2_f32m2(
2157
+ __riscv_vor_vv_u32m2(bits1_u32m2, sign1_u32m2, vector_length));
2158
+
2159
+ // Widening FMA: f32xf32 → f64
2160
+ accumulator_0_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_0_f64m4, a_vector_0_f32m2, b_vector_f32m2,
2161
+ vector_length);
2162
+ accumulator_1_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_1_f64m4, a_vector_1_f32m2, b_vector_f32m2,
2163
+ vector_length);
2164
+ }
2165
+
2166
+ // Horizontal reduce and narrow to f32
2167
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
2168
+ c_row_0[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
2169
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_0_f64m4, zero_f64m1, max_vector_length));
2170
+ c_row_1[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
2171
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_1_f64m4, zero_f64m1, max_vector_length));
2172
+ }
2173
+ }
2174
+ // Remainder rows
2175
+ for (; row < row_count; ++row) {
2176
+ nk_u8_t const *a_row = (nk_u8_t const *)((char const *)a_matrix + row * a_stride_in_bytes);
2177
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c_matrix + row * c_stride_in_bytes);
2178
+ for (nk_size_t column = 0; column < column_count; ++column) {
2179
+ nk_f32_t const *b_column = packed_data + column * depth_padded;
2180
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
2181
+ vfloat64m4_t accumulator_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
2182
+ nk_size_t remaining = depth;
2183
+ nk_size_t k = 0;
2184
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
2185
+ vector_length = __riscv_vsetvl_e32m2(remaining);
2186
+ vfloat32m2_t b_vector_f32m2 = __riscv_vle32_v_f32m2(b_column + k, vector_length);
2187
+ vuint8mf2_t raw_a_u8mf2 = __riscv_vle8_v_u8mf2(a_row + k, vector_length);
2188
+ vuint8mf2_t mag_a_u8mf2 = __riscv_vand_vx_u8mf2(raw_a_u8mf2, 0x7F, vector_length);
2189
+ vuint32m2_t idx_a_u32m2 = __riscv_vzext_vf4_u32m2(mag_a_u8mf2, vector_length);
2190
+ vuint32m2_t off_a_u32m2 = __riscv_vsll_vx_u32m2(idx_a_u32m2, 2, vector_length);
2191
+ vuint32m2_t bits_a_u32m2 = __riscv_vluxei32_v_u32m2(nk_e4m3_magnitude_lut_rvv_, off_a_u32m2,
2192
+ vector_length);
2193
+ vuint8mf2_t sign_a_u8mf2 = __riscv_vand_vx_u8mf2(raw_a_u8mf2, 0x80, vector_length);
2194
+ vuint32m2_t sign_a_u32m2 = __riscv_vsll_vx_u32m2(__riscv_vzext_vf4_u32m2(sign_a_u8mf2, vector_length),
2195
+ 24, vector_length);
2196
+ vfloat32m2_t a_vector_f32m2 = __riscv_vreinterpret_v_u32m2_f32m2(
2197
+ __riscv_vor_vv_u32m2(bits_a_u32m2, sign_a_u32m2, vector_length));
2198
+ accumulator_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_f64m4, a_vector_f32m2, b_vector_f32m2,
2199
+ vector_length);
2200
+ }
2201
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
2202
+ c_row[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
2203
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_f64m4, zero_f64m1, max_vector_length));
2204
+ }
2205
+ }
2206
+ }
2207
+
2208
+ /**
2209
+ * @brief Public e4m3 packed GEMM wrapper matching the declared signature in dots.h.
2210
+ */
2211
+ NK_PUBLIC void nk_dots_packed_e4m3_rvv(nk_e4m3_t const *a, void const *b_packed, nk_f32_t *c, nk_size_t rows,
2212
+ nk_size_t columns, nk_size_t depth, nk_size_t a_stride_in_bytes,
2213
+ nk_size_t c_stride_in_bytes) {
2214
+ nk_dots_packed_e4m3_rvv_aligned_(a, b_packed, c, rows, columns, depth, a_stride_in_bytes, c_stride_in_bytes);
2215
+ }
2216
+
2217
+ /**
2218
+ * @brief Symmetric e4m3 GEMM: C = A * A^T, upper triangle + mirror.
2219
+ *
2220
+ * Uses f32 LUT gather with f64 widened accumulation for precision.
2221
+ * Both operands are converted from e4m3 on-the-fly via magnitude LUT.
2222
+ * Processes only the rows in [row_start, row_start + row_count) for parallelism.
2223
+ */
2224
+ NK_PUBLIC void nk_dots_symmetric_e4m3_rvv(nk_e4m3_t const *vectors, nk_size_t vectors_count, nk_size_t depth,
2225
+ nk_size_t stride_in_bytes, nk_f32_t *result, nk_size_t result_stride_in_bytes,
2226
+ nk_size_t row_start, nk_size_t row_count) {
2227
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f32_t);
2228
+ nk_size_t const row_end = (row_start + row_count < vectors_count) ? (row_start + row_count) : vectors_count;
2229
+
2230
+ for (nk_size_t i = row_start; i < row_end; ++i) {
2231
+ nk_u8_t const *a_i = (nk_u8_t const *)vectors + i * stride_in_bytes;
2232
+ for (nk_size_t j = i; j < vectors_count; ++j) {
2233
+ nk_u8_t const *a_j = (nk_u8_t const *)vectors + j * stride_in_bytes;
2234
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
2235
+ vfloat64m4_t accumulator_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
2236
+ nk_size_t remaining = depth;
2237
+ nk_size_t k = 0;
2238
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
2239
+ vector_length = __riscv_vsetvl_e32m2(remaining);
2240
+ vuint8mf2_t raw_i_u8mf2 = __riscv_vle8_v_u8mf2(a_i + k, vector_length);
2241
+ vuint8mf2_t raw_j_u8mf2 = __riscv_vle8_v_u8mf2(a_j + k, vector_length);
2242
+
2243
+ // Convert i-vector via LUT gather
2244
+ vuint8mf2_t mag_i_u8mf2 = __riscv_vand_vx_u8mf2(raw_i_u8mf2, 0x7F, vector_length);
2245
+ vuint32m2_t idx_i_u32m2 = __riscv_vzext_vf4_u32m2(mag_i_u8mf2, vector_length);
2246
+ vuint32m2_t off_i_u32m2 = __riscv_vsll_vx_u32m2(idx_i_u32m2, 2, vector_length);
2247
+ vuint32m2_t bits_i_u32m2 = __riscv_vluxei32_v_u32m2(nk_e4m3_magnitude_lut_rvv_, off_i_u32m2,
2248
+ vector_length);
2249
+ vuint8mf2_t sign_i_u8mf2 = __riscv_vand_vx_u8mf2(raw_i_u8mf2, 0x80, vector_length);
2250
+ vuint32m2_t sign_i_u32m2 = __riscv_vsll_vx_u32m2(__riscv_vzext_vf4_u32m2(sign_i_u8mf2, vector_length),
2251
+ 24, vector_length);
2252
+ vfloat32m2_t val_i_f32m2 = __riscv_vreinterpret_v_u32m2_f32m2(
2253
+ __riscv_vor_vv_u32m2(bits_i_u32m2, sign_i_u32m2, vector_length));
2254
+
2255
+ // Convert j-vector via LUT gather
2256
+ vuint8mf2_t mag_j_u8mf2 = __riscv_vand_vx_u8mf2(raw_j_u8mf2, 0x7F, vector_length);
2257
+ vuint32m2_t idx_j_u32m2 = __riscv_vzext_vf4_u32m2(mag_j_u8mf2, vector_length);
2258
+ vuint32m2_t off_j_u32m2 = __riscv_vsll_vx_u32m2(idx_j_u32m2, 2, vector_length);
2259
+ vuint32m2_t bits_j_u32m2 = __riscv_vluxei32_v_u32m2(nk_e4m3_magnitude_lut_rvv_, off_j_u32m2,
2260
+ vector_length);
2261
+ vuint8mf2_t sign_j_u8mf2 = __riscv_vand_vx_u8mf2(raw_j_u8mf2, 0x80, vector_length);
2262
+ vuint32m2_t sign_j_u32m2 = __riscv_vsll_vx_u32m2(__riscv_vzext_vf4_u32m2(sign_j_u8mf2, vector_length),
2263
+ 24, vector_length);
2264
+ vfloat32m2_t val_j_f32m2 = __riscv_vreinterpret_v_u32m2_f32m2(
2265
+ __riscv_vor_vv_u32m2(bits_j_u32m2, sign_j_u32m2, vector_length));
2266
+
2267
+ // Widening FMA: f32xf32 → f64
2268
+ accumulator_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_f64m4, val_i_f32m2, val_j_f32m2,
2269
+ vector_length);
2270
+ }
2271
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
2272
+ nk_f32_t dot = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
2273
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_f64m4, zero_f64m1, max_vector_length));
2274
+ result[i * result_stride_elements + j] = dot;
2275
+ }
2276
+ }
2277
+ }
2278
+
2279
+ #pragma endregion E4M3 Floats
2280
+
2281
+ #pragma region E5M2 Floats
2282
+
2283
+ /**
2284
+ * @brief E5M2 magnitude LUT: 7-bit magnitude → f32 bit pattern (u32).
2285
+ * nk_e5m2_magnitude_lut_rvv_[i] = float_to_bits(e5m2_to_f32(i)) for i=0..127.
2286
+ * E5M2: 5 exponent bits (bias=15), 2 mantissa bits, has infinity (0x7C) and
2287
+ * NaN (magnitudes 0x7D..0x7F).
2288
+ */
2289
+ static nk_u32_t const nk_e5m2_magnitude_lut_rvv_[128] = {
2290
+ 0x00000000u, 0x37800000u, 0x38000000u, 0x38400000u,
2291
+ 0x38800000u, 0x38A00000u, 0x38C00000u, 0x38E00000u, /* [ 0.. 7] */
2292
+ 0x39000000u, 0x39200000u, 0x39400000u, 0x39600000u,
2293
+ 0x39800000u, 0x39A00000u, 0x39C00000u, 0x39E00000u, /* [ 8.. 15] */
2294
+ 0x3A000000u, 0x3A200000u, 0x3A400000u, 0x3A600000u,
2295
+ 0x3A800000u, 0x3AA00000u, 0x3AC00000u, 0x3AE00000u, /* [ 16.. 23] */
2296
+ 0x3B000000u, 0x3B200000u, 0x3B400000u, 0x3B600000u,
2297
+ 0x3B800000u, 0x3BA00000u, 0x3BC00000u, 0x3BE00000u, /* [ 24.. 31] */
2298
+ 0x3C000000u, 0x3C200000u, 0x3C400000u, 0x3C600000u,
2299
+ 0x3C800000u, 0x3CA00000u, 0x3CC00000u, 0x3CE00000u, /* [ 32.. 39] */
2300
+ 0x3D000000u, 0x3D200000u, 0x3D400000u, 0x3D600000u,
2301
+ 0x3D800000u, 0x3DA00000u, 0x3DC00000u, 0x3DE00000u, /* [ 40.. 47] */
2302
+ 0x3E000000u, 0x3E200000u, 0x3E400000u, 0x3E600000u,
2303
+ 0x3E800000u, 0x3EA00000u, 0x3EC00000u, 0x3EE00000u, /* [ 48.. 55] */
2304
+ 0x3F000000u, 0x3F200000u, 0x3F400000u, 0x3F600000u,
2305
+ 0x3F800000u, 0x3FA00000u, 0x3FC00000u, 0x3FE00000u, /* [ 56.. 63] */
2306
+ 0x40000000u, 0x40200000u, 0x40400000u, 0x40600000u,
2307
+ 0x40800000u, 0x40A00000u, 0x40C00000u, 0x40E00000u, /* [ 64.. 71] */
2308
+ 0x41000000u, 0x41200000u, 0x41400000u, 0x41600000u,
2309
+ 0x41800000u, 0x41A00000u, 0x41C00000u, 0x41E00000u, /* [ 72.. 79] */
2310
+ 0x42000000u, 0x42200000u, 0x42400000u, 0x42600000u,
2311
+ 0x42800000u, 0x42A00000u, 0x42C00000u, 0x42E00000u, /* [ 80.. 87] */
2312
+ 0x43000000u, 0x43200000u, 0x43400000u, 0x43600000u,
2313
+ 0x43800000u, 0x43A00000u, 0x43C00000u, 0x43E00000u, /* [ 88.. 95] */
2314
+ 0x44000000u, 0x44200000u, 0x44400000u, 0x44600000u,
2315
+ 0x44800000u, 0x44A00000u, 0x44C00000u, 0x44E00000u, /* [ 96..103] */
2316
+ 0x45000000u, 0x45200000u, 0x45400000u, 0x45600000u,
2317
+ 0x45800000u, 0x45A00000u, 0x45C00000u, 0x45E00000u, /* [104..111] */
2318
+ 0x46000000u, 0x46200000u, 0x46400000u, 0x46600000u,
2319
+ 0x46800000u, 0x46A00000u, 0x46C00000u, 0x46E00000u, /* [112..119] */
2320
+ 0x47000000u, 0x47200000u, 0x47400000u, 0x47600000u,
2321
+ 0x7F800000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u /* [120..127] */
2322
+ };
2323
+
2324
+ NK_PUBLIC nk_size_t nk_dots_packed_size_e5m2_rvv(nk_size_t column_count, nk_size_t depth) {
2325
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
2326
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
2327
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_f32_t);
2328
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
2329
+ return sizeof(nk_cross_packed_buffer_header_t) + column_count * depth_padded * sizeof(nk_f32_t) +
2330
+ column_count * sizeof(nk_f32_t); // per-column norms
2331
+ }
2332
+
2333
+ /**
2334
+ * @brief Pack B matrix from e5m2 to f32 for floating-point dot product.
2335
+ *
2336
+ * Each e5m2 byte is converted to f32 via `nk_e5m2_to_f32_serial`.
2337
+ * Padding values are zeroed. Column-panel layout with depth-contiguous storage.
2338
+ */
2339
+ NK_PUBLIC void nk_dots_pack_e5m2_rvv(nk_e5m2_t const *b, nk_size_t column_count, nk_size_t depth,
2340
+ nk_size_t b_stride_in_bytes, void *b_packed) {
2341
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
2342
+ nk_size_t depth_padded = nk_size_round_up_to_multiple_(depth, max_vector_length);
2343
+ nk_size_t stride_bytes = depth_padded * sizeof(nk_f32_t);
2344
+ if (stride_bytes > 0 && (stride_bytes & (stride_bytes - 1)) == 0) depth_padded += max_vector_length;
2345
+
2346
+ nk_cross_packed_buffer_header_t *header = (nk_cross_packed_buffer_header_t *)b_packed;
2347
+ header->column_count = (nk_u32_t)column_count;
2348
+ header->depth_dimensions = (nk_u32_t)depth;
2349
+ header->depth_padded_values = (nk_u32_t)depth_padded;
2350
+
2351
+ nk_f32_t *packed = (nk_f32_t *)((char *)b_packed + sizeof(nk_cross_packed_buffer_header_t));
2352
+ nk_size_t total = column_count * depth_padded;
2353
+ {
2354
+ nk_u8_t *zero_ptr = (nk_u8_t *)packed;
2355
+ nk_size_t total_bytes = total * sizeof(nk_f32_t);
2356
+ for (nk_size_t i = 0; i < total_bytes;) {
2357
+ nk_size_t vector_length = __riscv_vsetvl_e8m8(total_bytes - i);
2358
+ __riscv_vse8_v_u8m8(zero_ptr + i, __riscv_vmv_v_x_u8m8(0, vector_length), vector_length);
2359
+ i += vector_length;
2360
+ }
2361
+ }
2362
+
2363
+ for (nk_size_t column = 0; column < column_count; ++column) {
2364
+ nk_e5m2_t const *src = (nk_e5m2_t const *)((char const *)b + column * b_stride_in_bytes);
2365
+ nk_f32_t *dst = packed + column * depth_padded;
2366
+ for (nk_size_t k = 0; k < depth; ++k) nk_e5m2_to_f32_serial(&src[k], &dst[k]);
2367
+ }
2368
+
2369
+ // Append per-column norms after packed data
2370
+ nk_f32_t *norms = (nk_f32_t *)(packed + total);
2371
+ for (nk_size_t column = 0; column < column_count; ++column) {
2372
+ nk_e5m2_t const *src = (nk_e5m2_t const *)((char const *)b + column * b_stride_in_bytes);
2373
+ norms[column] = nk_dots_reduce_sumsq_e5m2_(src, depth);
2374
+ }
2375
+ }
2376
+
2377
+ /**
2378
+ * @brief e5m2 packed GEMM kernel: C += A * B_packed^T with f64 widened accumulation.
2379
+ *
2380
+ * Vectorizes over the depth dimension (k). For each (row, column) pair:
2381
+ * - Load pre-packed f32 values from B
2382
+ * - Load raw e5m2 bytes from A, convert on-the-fly via 128-entry f32 LUT gather:
2383
+ * extract 7-bit magnitude, zero-extend to u32, compute byte offsets (x4),
2384
+ * gather f32 bit patterns, inject sign bit from bit 7 (<<24), reinterpret as f32
2385
+ * - Widening FMA: f32xf32 → f64 via `vfwmacc_vv_f64m4`
2386
+ *
2387
+ * Register tile: process 2 rows per iteration (rows_per_tile=2, u32m2 gather + f64m4 accumulator is register-heavy).
2388
+ */
2389
+ NK_INTERNAL void nk_dots_packed_e5m2_rvv_aligned_(nk_e5m2_t const *a_matrix, void const *b_packed_buffer,
2390
+ nk_f32_t *c_matrix, nk_size_t row_count, nk_size_t column_count,
2391
+ nk_size_t depth, nk_size_t a_stride_in_bytes,
2392
+ nk_size_t c_stride_in_bytes) {
2393
+ nk_cross_packed_buffer_header_t const *header = (nk_cross_packed_buffer_header_t const *)b_packed_buffer;
2394
+ nk_size_t const depth_padded = header->depth_padded_values;
2395
+ nk_f32_t const *packed_data = (nk_f32_t const *)((char const *)b_packed_buffer +
2396
+ sizeof(nk_cross_packed_buffer_header_t));
2397
+
2398
+ // Zero output matrix
2399
+ for (nk_size_t i = 0; i < row_count; ++i) {
2400
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c_matrix + i * c_stride_in_bytes);
2401
+ for (nk_size_t j = 0; j < column_count; ++j) c_row[j] = 0;
2402
+ }
2403
+
2404
+ // mr=2 register tile over rows
2405
+ nk_size_t row = 0;
2406
+ for (; row + 2 <= row_count; row += 2) {
2407
+ nk_u8_t const *a_row_0 = (nk_u8_t const *)((char const *)a_matrix + (row + 0) * a_stride_in_bytes);
2408
+ nk_u8_t const *a_row_1 = (nk_u8_t const *)((char const *)a_matrix + (row + 1) * a_stride_in_bytes);
2409
+ nk_f32_t *c_row_0 = (nk_f32_t *)((char *)c_matrix + (row + 0) * c_stride_in_bytes);
2410
+ nk_f32_t *c_row_1 = (nk_f32_t *)((char *)c_matrix + (row + 1) * c_stride_in_bytes);
2411
+
2412
+ for (nk_size_t column = 0; column < column_count; ++column) {
2413
+ nk_f32_t const *b_column = packed_data + column * depth_padded;
2414
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
2415
+ vfloat64m4_t accumulator_0_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
2416
+ vfloat64m4_t accumulator_1_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
2417
+
2418
+ nk_size_t remaining = depth;
2419
+ nk_size_t k = 0;
2420
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
2421
+ vector_length = __riscv_vsetvl_e32m2(remaining);
2422
+
2423
+ // Load pre-packed f32 B values
2424
+ vfloat32m2_t b_vector_f32m2 = __riscv_vle32_v_f32m2(b_column + k, vector_length);
2425
+
2426
+ // Load raw e5m2 bytes from each A row
2427
+ vuint8mf2_t raw0_u8mf2 = __riscv_vle8_v_u8mf2(a_row_0 + k, vector_length);
2428
+ vuint8mf2_t raw1_u8mf2 = __riscv_vle8_v_u8mf2(a_row_1 + k, vector_length);
2429
+
2430
+ // Extract 7-bit magnitudes, zero-extend to u32, compute byte offsets for f32 LUT
2431
+ vuint8mf2_t mag0_u8mf2 = __riscv_vand_vx_u8mf2(raw0_u8mf2, 0x7F, vector_length);
2432
+ vuint8mf2_t mag1_u8mf2 = __riscv_vand_vx_u8mf2(raw1_u8mf2, 0x7F, vector_length);
2433
+ vuint32m2_t idx0_u32m2 = __riscv_vzext_vf4_u32m2(mag0_u8mf2, vector_length);
2434
+ vuint32m2_t idx1_u32m2 = __riscv_vzext_vf4_u32m2(mag1_u8mf2, vector_length);
2435
+ vuint32m2_t off0_u32m2 = __riscv_vsll_vx_u32m2(idx0_u32m2, 2,
2436
+ vector_length); // byte offsets = index * 4
2437
+ vuint32m2_t off1_u32m2 = __riscv_vsll_vx_u32m2(idx1_u32m2, 2, vector_length);
2438
+
2439
+ // Gather f32 bit patterns from magnitude LUT
2440
+ vuint32m2_t bits0_u32m2 = __riscv_vluxei32_v_u32m2(nk_e5m2_magnitude_lut_rvv_, off0_u32m2,
2441
+ vector_length);
2442
+ vuint32m2_t bits1_u32m2 = __riscv_vluxei32_v_u32m2(nk_e5m2_magnitude_lut_rvv_, off1_u32m2,
2443
+ vector_length);
2444
+
2445
+ // Extract sign bit 7, shift to f32 sign position (bit 31)
2446
+ vuint8mf2_t sign0_u8mf2 = __riscv_vand_vx_u8mf2(raw0_u8mf2, 0x80, vector_length);
2447
+ vuint8mf2_t sign1_u8mf2 = __riscv_vand_vx_u8mf2(raw1_u8mf2, 0x80, vector_length);
2448
+ vuint32m2_t sign0_u32m2 = __riscv_vsll_vx_u32m2(__riscv_vzext_vf4_u32m2(sign0_u8mf2, vector_length), 24,
2449
+ vector_length);
2450
+ vuint32m2_t sign1_u32m2 = __riscv_vsll_vx_u32m2(__riscv_vzext_vf4_u32m2(sign1_u8mf2, vector_length), 24,
2451
+ vector_length);
2452
+
2453
+ // Apply sign and reinterpret as f32
2454
+ vfloat32m2_t a_vector_0_f32m2 = __riscv_vreinterpret_v_u32m2_f32m2(
2455
+ __riscv_vor_vv_u32m2(bits0_u32m2, sign0_u32m2, vector_length));
2456
+ vfloat32m2_t a_vector_1_f32m2 = __riscv_vreinterpret_v_u32m2_f32m2(
2457
+ __riscv_vor_vv_u32m2(bits1_u32m2, sign1_u32m2, vector_length));
2458
+
2459
+ // Widening FMA: f32xf32 → f64
2460
+ accumulator_0_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_0_f64m4, a_vector_0_f32m2, b_vector_f32m2,
2461
+ vector_length);
2462
+ accumulator_1_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_1_f64m4, a_vector_1_f32m2, b_vector_f32m2,
2463
+ vector_length);
2464
+ }
2465
+
2466
+ // Horizontal reduce and narrow to f32
2467
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
2468
+ c_row_0[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
2469
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_0_f64m4, zero_f64m1, max_vector_length));
2470
+ c_row_1[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
2471
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_1_f64m4, zero_f64m1, max_vector_length));
2472
+ }
2473
+ }
2474
+ // Remainder rows
2475
+ for (; row < row_count; ++row) {
2476
+ nk_u8_t const *a_row = (nk_u8_t const *)((char const *)a_matrix + row * a_stride_in_bytes);
2477
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c_matrix + row * c_stride_in_bytes);
2478
+ for (nk_size_t column = 0; column < column_count; ++column) {
2479
+ nk_f32_t const *b_column = packed_data + column * depth_padded;
2480
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
2481
+ vfloat64m4_t accumulator_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
2482
+ nk_size_t remaining = depth;
2483
+ nk_size_t k = 0;
2484
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
2485
+ vector_length = __riscv_vsetvl_e32m2(remaining);
2486
+ vfloat32m2_t b_vector_f32m2 = __riscv_vle32_v_f32m2(b_column + k, vector_length);
2487
+ vuint8mf2_t raw_a_u8mf2 = __riscv_vle8_v_u8mf2(a_row + k, vector_length);
2488
+ vuint8mf2_t mag_a_u8mf2 = __riscv_vand_vx_u8mf2(raw_a_u8mf2, 0x7F, vector_length);
2489
+ vuint32m2_t idx_a_u32m2 = __riscv_vzext_vf4_u32m2(mag_a_u8mf2, vector_length);
2490
+ vuint32m2_t off_a_u32m2 = __riscv_vsll_vx_u32m2(idx_a_u32m2, 2, vector_length);
2491
+ vuint32m2_t bits_a_u32m2 = __riscv_vluxei32_v_u32m2(nk_e5m2_magnitude_lut_rvv_, off_a_u32m2,
2492
+ vector_length);
2493
+ vuint8mf2_t sign_a_u8mf2 = __riscv_vand_vx_u8mf2(raw_a_u8mf2, 0x80, vector_length);
2494
+ vuint32m2_t sign_a_u32m2 = __riscv_vsll_vx_u32m2(__riscv_vzext_vf4_u32m2(sign_a_u8mf2, vector_length),
2495
+ 24, vector_length);
2496
+ vfloat32m2_t a_vector_f32m2 = __riscv_vreinterpret_v_u32m2_f32m2(
2497
+ __riscv_vor_vv_u32m2(bits_a_u32m2, sign_a_u32m2, vector_length));
2498
+ accumulator_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_f64m4, a_vector_f32m2, b_vector_f32m2,
2499
+ vector_length);
2500
+ }
2501
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
2502
+ c_row[column] = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
2503
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_f64m4, zero_f64m1, max_vector_length));
2504
+ }
2505
+ }
2506
+ }
2507
+
2508
+ /**
2509
+ * @brief Public e5m2 packed GEMM wrapper matching the declared signature in dots.h.
2510
+ */
2511
+ NK_PUBLIC void nk_dots_packed_e5m2_rvv(nk_e5m2_t const *a, void const *b_packed, nk_f32_t *c, nk_size_t rows,
2512
+ nk_size_t columns, nk_size_t depth, nk_size_t a_stride_in_bytes,
2513
+ nk_size_t c_stride_in_bytes) {
2514
+ nk_dots_packed_e5m2_rvv_aligned_(a, b_packed, c, rows, columns, depth, a_stride_in_bytes, c_stride_in_bytes);
2515
+ }
2516
+
2517
+ /**
2518
+ * @brief Symmetric e5m2 GEMM: C = A * A^T, upper triangle + mirror.
2519
+ *
2520
+ * Uses f32 LUT gather with f64 widened accumulation for precision.
2521
+ * Both operands are converted from e5m2 on-the-fly via magnitude LUT.
2522
+ * Processes only the rows in [row_start, row_start + row_count) for parallelism.
2523
+ */
2524
+ NK_PUBLIC void nk_dots_symmetric_e5m2_rvv(nk_e5m2_t const *vectors, nk_size_t vectors_count, nk_size_t depth,
2525
+ nk_size_t stride_in_bytes, nk_f32_t *result, nk_size_t result_stride_in_bytes,
2526
+ nk_size_t row_start, nk_size_t row_count) {
2527
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f32_t);
2528
+ nk_size_t const row_end = (row_start + row_count < vectors_count) ? (row_start + row_count) : vectors_count;
2529
+
2530
+ for (nk_size_t i = row_start; i < row_end; ++i) {
2531
+ nk_u8_t const *a_i = (nk_u8_t const *)vectors + i * stride_in_bytes;
2532
+ for (nk_size_t j = i; j < vectors_count; ++j) {
2533
+ nk_u8_t const *a_j = (nk_u8_t const *)vectors + j * stride_in_bytes;
2534
+ nk_size_t max_vector_length = __riscv_vsetvlmax_e32m2();
2535
+ vfloat64m4_t accumulator_f64m4 = __riscv_vfmv_v_f_f64m4(0.0, max_vector_length);
2536
+ nk_size_t remaining = depth;
2537
+ nk_size_t k = 0;
2538
+ for (nk_size_t vector_length = 0; remaining > 0; remaining -= vector_length, k += vector_length) {
2539
+ vector_length = __riscv_vsetvl_e32m2(remaining);
2540
+ vuint8mf2_t raw_i_u8mf2 = __riscv_vle8_v_u8mf2(a_i + k, vector_length);
2541
+ vuint8mf2_t raw_j_u8mf2 = __riscv_vle8_v_u8mf2(a_j + k, vector_length);
2542
+
2543
+ // Convert i-vector via LUT gather
2544
+ vuint8mf2_t mag_i_u8mf2 = __riscv_vand_vx_u8mf2(raw_i_u8mf2, 0x7F, vector_length);
2545
+ vuint32m2_t idx_i_u32m2 = __riscv_vzext_vf4_u32m2(mag_i_u8mf2, vector_length);
2546
+ vuint32m2_t off_i_u32m2 = __riscv_vsll_vx_u32m2(idx_i_u32m2, 2, vector_length);
2547
+ vuint32m2_t bits_i_u32m2 = __riscv_vluxei32_v_u32m2(nk_e5m2_magnitude_lut_rvv_, off_i_u32m2,
2548
+ vector_length);
2549
+ vuint8mf2_t sign_i_u8mf2 = __riscv_vand_vx_u8mf2(raw_i_u8mf2, 0x80, vector_length);
2550
+ vuint32m2_t sign_i_u32m2 = __riscv_vsll_vx_u32m2(__riscv_vzext_vf4_u32m2(sign_i_u8mf2, vector_length),
2551
+ 24, vector_length);
2552
+ vfloat32m2_t val_i_f32m2 = __riscv_vreinterpret_v_u32m2_f32m2(
2553
+ __riscv_vor_vv_u32m2(bits_i_u32m2, sign_i_u32m2, vector_length));
2554
+
2555
+ // Convert j-vector via LUT gather
2556
+ vuint8mf2_t mag_j_u8mf2 = __riscv_vand_vx_u8mf2(raw_j_u8mf2, 0x7F, vector_length);
2557
+ vuint32m2_t idx_j_u32m2 = __riscv_vzext_vf4_u32m2(mag_j_u8mf2, vector_length);
2558
+ vuint32m2_t off_j_u32m2 = __riscv_vsll_vx_u32m2(idx_j_u32m2, 2, vector_length);
2559
+ vuint32m2_t bits_j_u32m2 = __riscv_vluxei32_v_u32m2(nk_e5m2_magnitude_lut_rvv_, off_j_u32m2,
2560
+ vector_length);
2561
+ vuint8mf2_t sign_j_u8mf2 = __riscv_vand_vx_u8mf2(raw_j_u8mf2, 0x80, vector_length);
2562
+ vuint32m2_t sign_j_u32m2 = __riscv_vsll_vx_u32m2(__riscv_vzext_vf4_u32m2(sign_j_u8mf2, vector_length),
2563
+ 24, vector_length);
2564
+ vfloat32m2_t val_j_f32m2 = __riscv_vreinterpret_v_u32m2_f32m2(
2565
+ __riscv_vor_vv_u32m2(bits_j_u32m2, sign_j_u32m2, vector_length));
2566
+
2567
+ // Widening FMA: f32xf32 → f64
2568
+ accumulator_f64m4 = __riscv_vfwmacc_vv_f64m4_tu(accumulator_f64m4, val_i_f32m2, val_j_f32m2,
2569
+ vector_length);
2570
+ }
2571
+ vfloat64m1_t zero_f64m1 = __riscv_vfmv_v_f_f64m1(0.0, 1);
2572
+ nk_f32_t dot = (nk_f32_t)__riscv_vfmv_f_s_f64m1_f64(
2573
+ __riscv_vfredusum_vs_f64m4_f64m1(accumulator_f64m4, zero_f64m1, max_vector_length));
2574
+ result[i * result_stride_elements + j] = dot;
2575
+ }
2576
+ }
2577
+ }
2578
+
2579
+ #pragma endregion E5M2 Floats
2580
+
2581
+ #if defined(__cplusplus)
2582
+ } // extern "C"
2583
+ #endif
2584
+
2585
+ #if defined(__clang__)
2586
+ #pragma clang attribute pop
2587
+ #elif defined(__GNUC__)
2588
+ #pragma GCC pop_options
2589
+ #endif
2590
+
2591
+ #endif // NK_TARGET_RVV
2592
+ #endif // NK_TARGET_RISCV64_
2593
+ #endif // NK_DOTS_RVV_H