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