hermes_simd_intrinsics/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
20#![deny(missing_docs)]
21#![allow(
22 clippy::needless_range_loop,
23 clippy::missing_safety_doc,
24 clippy::new_without_default,
25 clippy::too_many_arguments,
26 clippy::manual_is_multiple_of,
27 clippy::missing_const_for_thread_local
28)]
29extern crate alloc;
30
31use hermes_simd_core::arch::SimdArch;
32
33#[macro_export]
39macro_rules! impl_emulated_kernel {
40 ($arch:ty, $t:ty, $lanes:expr, $cfg:meta) => {
41 #[$cfg]
42 impl hermes_simd_core::kernel::SimdKernel<$t> for $arch {
43 type Vector = [$t; $lanes];
44 type Mask = [bool; $lanes];
45 type IndexVector = [i32; $lanes];
46 const LANE_COUNT: usize = $lanes;
47 const UNROLL_FACTOR: usize = 4;
48
49 #[inline(always)]
50 unsafe fn load_aligned(ptr: *const $t) -> Self::Vector {
51 let mut v = [<$t as hermes_simd_core::scalar::NumericElement>::ZERO; $lanes];
52 core::ptr::copy_nonoverlapping(ptr, v.as_mut_ptr(), $lanes);
53 v
54 }
55
56 #[inline(always)]
57 unsafe fn load_unaligned(ptr: *const $t) -> Self::Vector {
58 let mut v = [<$t as hermes_simd_core::scalar::NumericElement>::ZERO; $lanes];
59 core::ptr::copy_nonoverlapping(ptr, v.as_mut_ptr(), $lanes);
60 v
61 }
62
63 #[inline(always)]
64 unsafe fn store_aligned(ptr: *mut $t, val: Self::Vector) {
65 core::ptr::copy_nonoverlapping(val.as_ptr(), ptr, $lanes);
66 }
67
68 #[inline(always)]
69 unsafe fn store_unaligned(ptr: *mut $t, val: Self::Vector) {
70 core::ptr::copy_nonoverlapping(val.as_ptr(), ptr, $lanes);
71 }
72
73 #[inline(always)]
74 unsafe fn add(a: Self::Vector, b: Self::Vector) -> Self::Vector {
75 core::array::from_fn(|i| a[i] + b[i])
76 }
77
78 #[inline(always)]
79 unsafe fn mul(a: Self::Vector, b: Self::Vector) -> Self::Vector {
80 core::array::from_fn(|i| a[i] * b[i])
81 }
82
83 #[inline(always)]
84 unsafe fn sub(a: Self::Vector, b: Self::Vector) -> Self::Vector {
85 core::array::from_fn(|i| a[i] - b[i])
86 }
87
88 #[inline(always)]
89 unsafe fn neg(a: Self::Vector) -> Self::Vector {
90 core::array::from_fn(|i| -a[i])
91 }
92
93 #[inline(always)]
94 unsafe fn fmadd(a: Self::Vector, b: Self::Vector, c: Self::Vector) -> Self::Vector {
95 core::array::from_fn(|i| {
96 <$t as hermes_simd_core::scalar::NumericElement>::scalar_fmadd(a[i], b[i], c[i])
97 })
98 }
99
100 #[inline(always)]
101 unsafe fn sum_reduce(v: Self::Vector) -> $t {
102 v.iter().copied().fold(
103 <$t as hermes_simd_core::scalar::NumericElement>::ZERO,
104 |acc, x| acc + x,
105 )
106 }
107
108 #[inline(always)]
114 unsafe fn compress(src: Self::Vector, mask: Self::Mask) -> Self::Vector {
115 let mut out = [<$t as hermes_simd_core::scalar::NumericElement>::ZERO; $lanes];
116 let mut k = 0;
117 for i in 0..$lanes {
118 if mask[i] {
119 out[k] = src[i];
120 k += 1;
121 }
122 }
123 out
124 }
125
126 #[inline(always)]
127 unsafe fn expand(
128 src: Self::Vector,
129 mask: Self::Mask,
130 fill: Self::Vector,
131 ) -> Self::Vector {
132 let mut out = fill;
133 let mut k = 0;
134 for i in 0..$lanes {
135 if mask[i] {
136 out[i] = src[k];
137 k += 1;
138 }
139 }
140 out
141 }
142
143 #[inline(always)]
144 unsafe fn gather(base: *const $t, indices: Self::IndexVector) -> Self::Vector {
145 core::array::from_fn(|i| *base.add(indices[i] as usize))
146 }
147
148 #[inline(always)]
149 unsafe fn gather_masked(
150 base: *const $t,
151 indices: Self::IndexVector,
152 mask: Self::Mask,
153 src: Self::Vector,
154 ) -> Self::Vector {
155 core::array::from_fn(|i| {
156 if mask[i] {
157 *base.add(indices[i] as usize)
158 } else {
159 src[i]
160 }
161 })
162 }
163
164 #[inline(always)]
165 unsafe fn mask_from_bools(bits: &[bool]) -> Self::Mask {
166 debug_assert_eq!(bits.len(), $lanes);
167 core::array::from_fn(|i| bits[i])
168 }
169
170 #[inline(always)]
171 unsafe fn leading_k_mask(k: usize) -> Self::Mask {
172 core::array::from_fn(|i| i < k)
173 }
174
175 #[inline(always)]
176 unsafe fn zero() -> Self::Vector {
177 [<$t as hermes_simd_core::scalar::NumericElement>::ZERO; $lanes]
178 }
179
180 #[inline(always)]
181 unsafe fn splat(val: $t) -> Self::Vector {
182 [val; $lanes]
183 }
184
185 #[inline(always)]
186 unsafe fn mask_to_bitmask(mask: Self::Mask) -> u64 {
187 let mut m = 0u64;
188 for i in 0..$lanes {
189 if mask[i] {
190 m |= 1u64 << i;
191 }
192 }
193 m
194 }
195
196 #[inline(always)]
197 unsafe fn mask_to_vector(mask: Self::Mask) -> Self::Vector {
198 core::array::from_fn(|i| {
199 if mask[i] {
200 <$t as hermes_simd_core::scalar::NumericElement>::ALL_ONES
201 } else {
202 <$t as hermes_simd_core::scalar::NumericElement>::ZERO
203 }
204 })
205 }
206
207 #[inline(always)]
208 unsafe fn vector_to_mask(v: Self::Vector) -> Self::Mask {
209 core::array::from_fn(|i| {
210 <$t as hermes_simd_core::scalar::NumericElement>::count_ones(
216 <$t as hermes_simd_core::scalar::NumericElement>::bitand(
217 v[i],
218 <$t as hermes_simd_core::scalar::NumericElement>::SIGN_MASK,
219 ),
220 ) != 0
221 })
222 }
223 }
224 };
225}
226
227pub mod aarch64;
228pub mod bitboard;
229pub mod scalar;
230pub mod x86_64;
231
232pub use aarch64::sve::SveArch;
234
235pub use bitboard::hybrid::HybridSwarMagic;
237pub use bitboard::hyperbola::Hyperbola;
238pub use bitboard::kogge_stone::KoggeStone;
239pub use bitboard::magic::Magic;
240pub use bitboard::swar::{Swar, SwarUtils};
241
242#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
243pub use x86_64::amx::{AmxBatchSession, AmxBf16, AmxConfig, AmxInt8, AmxSession, AmxSessionError};
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq)]
251pub struct Scalar;
252
253#[derive(Debug, Clone, Copy, PartialEq, Eq)]
255pub struct Avx2;
256
257#[derive(Debug, Clone, Copy, PartialEq, Eq)]
259pub struct Avx512;
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq)]
270pub struct AvxVnni;
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq)]
274pub struct Neon;
275
276impl SimdArch for Scalar {
281 const NAME: &'static str = "scalar";
282 const REGISTER_WIDTH_BITS: u32 = 0;
283 const ISA_FAMILY: hermes_simd_core::arch::IsaFamily = hermes_simd_core::arch::IsaFamily::Scalar;
284 const FMA_THROUGHPUT_HINT: u32 = 1;
285
286 #[inline]
287 fn is_runtime_supported() -> bool {
288 true
289 }
290}
291
292impl SimdArch for Avx2 {
293 const NAME: &'static str = "avx2";
294 const REGISTER_WIDTH_BITS: u32 = 256;
295 const ISA_FAMILY: hermes_simd_core::arch::IsaFamily = hermes_simd_core::arch::IsaFamily::X86;
296 const FMA_THROUGHPUT_HINT: u32 = 4;
297
298 #[inline]
299 fn is_runtime_supported() -> bool {
300 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "std"))]
301 {
302 std::is_x86_feature_detected!("avx2") && std::is_x86_feature_detected!("fma")
303 }
304 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), not(feature = "std")))]
305 {
306 cfg!(target_feature = "avx2") && cfg!(target_feature = "fma")
307 }
308 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
309 {
310 false
311 }
312 }
313}
314
315impl SimdArch for Avx512 {
316 const NAME: &'static str = "avx512";
317 const REGISTER_WIDTH_BITS: u32 = 512;
318 const ISA_FAMILY: hermes_simd_core::arch::IsaFamily = hermes_simd_core::arch::IsaFamily::X86;
319 const FMA_THROUGHPUT_HINT: u32 = 8;
320
321 #[inline]
322 fn is_runtime_supported() -> bool {
323 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "std"))]
324 {
325 std::is_x86_feature_detected!("avx512f")
326 }
327 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), not(feature = "std")))]
328 {
329 cfg!(target_feature = "avx512f")
330 }
331 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
332 {
333 false
334 }
335 }
336}
337
338impl SimdArch for AvxVnni {
339 const NAME: &'static str = "avx_vnni";
340 const REGISTER_WIDTH_BITS: u32 = 256;
341 const ISA_FAMILY: hermes_simd_core::arch::IsaFamily = hermes_simd_core::arch::IsaFamily::X86;
342 const FMA_THROUGHPUT_HINT: u32 = 4;
344
345 #[inline]
346 fn is_runtime_supported() -> bool {
347 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "std"))]
348 {
349 std::is_x86_feature_detected!("avxvnni")
350 }
351 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), not(feature = "std")))]
352 {
353 false
354 }
355 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
356 {
357 false
358 }
359 }
360}
361
362impl SimdArch for Neon {
363 const NAME: &'static str = "neon";
364 const REGISTER_WIDTH_BITS: u32 = 128;
365 const ISA_FAMILY: hermes_simd_core::arch::IsaFamily =
366 hermes_simd_core::arch::IsaFamily::AArch64;
367 const FMA_THROUGHPUT_HINT: u32 = 4;
368
369 #[inline]
370 fn is_runtime_supported() -> bool {
371 cfg!(target_arch = "aarch64")
372 }
373}
374
375impl hermes_simd_core::private::Sealed for Scalar {}
376impl hermes_simd_core::private::Sealed for Avx2 {}
377impl hermes_simd_core::private::Sealed for Avx512 {}
378impl hermes_simd_core::private::Sealed for AvxVnni {}
379impl hermes_simd_core::private::Sealed for Neon {}
380impl hermes_simd_core::private::Sealed for SveArch {}