1#![cfg_attr(not(feature = "std"), no_std)]
38#![deny(missing_docs)]
39#![allow(
40 unused_unsafe,
41 clippy::too_many_arguments,
42 clippy::needless_range_loop,
43 clippy::assign_op_pattern,
44 clippy::manual_memcpy
45)]
46
47extern crate alloc;
48
49pub use hermes_simd_core::{
50 align::{Aligned, Alignment, Unaligned},
51 arch::SimdArch,
52 bitboard::{BitBoardKernel, BitBoardView},
53 compute::{ComputeReduce, ComputeView},
55 cow::{ArchivedPacked4Cow, ArchivedSimdCow, Packed4CowResolver, SimdCow, SimdCowResolver},
56 current_numa_node,
57 execution::{ExecutionMode, Masked, Unmasked},
58 iter::{SimdChunks, SimdChunksMut, ZipChunks, ZipChunksMut},
60 kernel::SimdKernel,
61 mask::BitMask,
62 refresh_numa_node,
63 scalar::{CastFrom, CastTo, FloatElement, Scalar as SimdScalar},
64 vec::AlignedVec,
65 verify_numa_locality,
66 view::{Mask, SimdError, SimdView, TileMatrixMultiply, TileView, Vector},
67 Abs,
69 Add,
70 BitAnd,
71 BitOr,
72 BitXor,
73 Clamp,
74 Div,
75 Dot,
76 ElementOp,
77 Exclusive,
78 FmaAdd,
80 Inclusive,
81 Mul,
82 Neg,
83 NumaBinding,
84 Popcount,
85 Product,
86 RecipSqrt,
87 ReductionOp,
89 ScanAdd,
90 ScanMax,
91 ScanMin,
92 ScanMode,
93 ScanMul,
94 ScanOp,
96 Sqrt,
97 Sub,
98 Sum,
99 UnaryOp,
100};
101
102pub use hermes_simd_core::sparse::{
104 BlockedCoo,
105 BlockedCooData,
106 CowFormat,
108 Csr,
109 CsrData,
110 DenseWithMask,
111 DenseWithMaskData,
112 OwnedBlockedCoo,
113 OwnedCsr,
115 OwnedDenseWithMask,
116 OwnedSellP,
117 SellP,
118 SellPData,
119 SparseCow,
121 SparseFormat,
122 SparseOps,
123 SparseSpMv,
124 SparseView,
125 Validated,
126 ValidatedData,
127};
128
129pub use hermes_simd_core::tiling::{tiled_dot, tiled_gemv, TilingPolicy, TilingStrategy};
131
132pub use hermes_simd_core::tensor::{ColMajor, RowMajor, TensorCow, TensorError, TensorView};
134
135pub use hermes_simd_intrinsics::{
137 Avx2, Avx512, AvxVnni, HybridSwarMagic, Hyperbola, KoggeStone, Magic, Neon, Scalar, SveArch,
138 Swar, SwarUtils,
139};
140
141#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
142pub use hermes_simd_intrinsics::{
143 AmxBatchSession, AmxBf16, AmxConfig, AmxInt8, AmxSession, AmxSessionError,
144};
145
146pub use eunomia::{
147 unpack_bf4_to_bf16, unpack_bf4_to_bf16_packed, unpack_bf8_to_bf16, unpack_f4_to_f32,
148 unpack_f4_to_f32_packed, unpack_f8_to_f32, Bf16, Bf4, Bf8, Packable4, Packed4Cow, Packed4Iter,
149 Packed4Slice, Packed4SliceMut, Packed4Vec, PackedBf4Cow, PackedBf4Slice, PackedBf4SliceMut,
150 PackedBf4Vec, PackedF4Cow, PackedF4Slice, PackedF4SliceMut, PackedF4Vec, F16, F32, F4, F64, F8,
151 I16, I32, I8,
152};
153
154pub use hermes_simd_types::{
156 MaskBf16, MaskBf4, MaskBf8, MaskF16, MaskF32, MaskF4, MaskF64, MaskF8, MaskI16, MaskI32,
157 MaskI8, PreferredArch, ScalarBf16, ScalarBf4, ScalarBf8, ScalarF16, ScalarF32, ScalarF4,
158 ScalarF64, ScalarF8, ScalarI16, ScalarI32, ScalarI8, ScalarMaskF32, ScalarMaskF64, SimdBf16,
159 SimdBf4, SimdBf8, SimdF16, SimdF32, SimdF4, SimdF64, SimdF8, SimdI16, SimdI32, SimdI8,
160 SimdMaskBf16, SimdMaskBf4, SimdMaskBf8, SimdMaskF16, SimdMaskF32, SimdMaskF4, SimdMaskF64,
161 SimdMaskF8, SimdMaskI16, SimdMaskI32, SimdMaskI8, VectorBf16, VectorBf4, VectorBf8, VectorF16,
162 VectorF32, VectorF4, VectorF64, VectorF8, VectorI16, VectorI32, VectorI8,
163};
164
165#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
166pub use hermes_simd_types::{
167 Avx2Bf16, Avx2Bf4, Avx2Bf8, Avx2F16, Avx2F32, Avx2F4, Avx2F64, Avx2F8, Avx2I16, Avx2I32,
168 Avx2I8, Avx2MaskBf16, Avx2MaskF16, Avx2MaskF32, Avx2MaskF64, Avx512Bf16, Avx512Bf4, Avx512Bf8,
169 Avx512F16, Avx512F32, Avx512F4, Avx512F64, Avx512F8, Avx512I16, Avx512I32, Avx512I8,
170 Avx512MaskBf16, Avx512MaskF16, Avx512MaskF32, Avx512MaskF64,
171};
172
173#[cfg(target_arch = "aarch64")]
174pub use hermes_simd_types::{
175 NeonBf16, NeonBf4, NeonBf8, NeonF16, NeonF32, NeonF4, NeonF64, NeonF8, NeonI16, NeonI32,
176 NeonI8, NeonMaskBf16, NeonMaskF16, NeonMaskF32, NeonMaskF64,
177};
178
179pub mod attacks;
181pub mod cpu;
183pub mod dispatcher;
185
186pub mod tile_matmul;
188
189pub mod dispatch;
191pub mod target;
193
194pub use attacks::{bishop_attacks, queen_attacks, rook_attacks};
195pub use cpu::{has_fma3, AmxSupport, Avx512Support, FmaSupport};
196pub use dispatcher::{AdaptiveDispatcher, DispatchDecision};
197pub use target::{dispatch_view_mut_to, dispatch_view_to, TargetId};
198pub use tile_matmul::{
199 dispatch_tile_matmul, gemm, unpack_int4, widen_I8_to_I16, widen_I8_to_I32, widen_i8_to_i16,
200 widen_i8_to_i32, TiledGemm,
201};
202
203pub use dispatch::{
206 abs_max,
207 abs_sum,
208 argmax,
209 argmin,
210 axpy,
211 axpy_rows,
212 axpy_rows_batch,
213 dot,
214 elementwise_add,
215 elementwise_div,
216 elementwise_mul,
217 elementwise_sub,
218 gemv,
219 gemv_strided,
220 gemv_transpose,
221 gemv_transpose_strided,
222 interleaved_complex_dot,
223 interleaved_complex_dot_runtime,
224 interleaved_complex_mul_assign,
225 interleaved_complex_mul_assign_runtime,
226 masked_add,
227 masked_dot,
228 masked_sum,
229 max,
230 min,
231 ntt_butterfly_stage_u64,
232 reduce_popcount,
234 reduce_popcount_and,
235 reduce_popcount_or,
236 reduce_popcount_xor,
237 scale,
238 spmv_bcoo,
239 spmv_csr,
241 spmv_dense_masked,
242 spmv_sellp,
243 sum,
244 tiled_gemm,
245 SimdOps,
247};
248
249pub enum DispatchedView<'a, T, Align = Unaligned, Mode = Unmasked, Ref = &'a [T]>
251where
252 Align: hermes_simd_core::align::Alignment,
253 Mode: hermes_simd_core::execution::ExecutionMode,
254 Ref: core::ops::Deref<Target = [T]>,
255{
256 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
258 Avx512(SimdView<'a, T, Avx512, Align, Mode, Ref>),
259 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
261 Avx2(SimdView<'a, T, Avx2, Align, Mode, Ref>),
262 #[cfg(target_arch = "aarch64")]
264 Neon(SimdView<'a, T, Neon, Align, Mode, Ref>),
265 Scalar(SimdView<'a, T, Scalar, Align, Mode, Ref>),
267}
268
269#[inline]
271#[allow(unreachable_code)]
272pub fn dispatch_view<'a, T, Align>(
273 data: &'a [T],
274) -> Option<DispatchedView<'a, T, Align, Unmasked, &'a [T]>>
275where
276 T: FloatElement,
277 Align: Alignment,
278{
279 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
280 {
281 #[cfg(feature = "std")]
282 {
283 if std::is_x86_feature_detected!("avx512f") {
284 return SimdView::<T, Avx512, Align, Unmasked, &'a [T]>::new(data)
285 .map(DispatchedView::Avx512);
286 }
287 if std::is_x86_feature_detected!("avx2") && std::is_x86_feature_detected!("fma") {
288 return SimdView::<T, Avx2, Align, Unmasked, &'a [T]>::new(data)
289 .map(DispatchedView::Avx2);
290 }
291 }
292 #[cfg(not(feature = "std"))]
293 {
294 if cfg!(target_feature = "avx512f") {
295 return SimdView::<T, Avx512, Align, Unmasked, &'a [T]>::new(data)
296 .map(DispatchedView::Avx512);
297 }
298 if cfg!(target_feature = "avx2") && cfg!(target_feature = "fma") {
299 return SimdView::<T, Avx2, Align, Unmasked, &'a [T]>::new(data)
300 .map(DispatchedView::Avx2);
301 }
302 }
303 }
304 #[cfg(target_arch = "aarch64")]
305 {
306 return SimdView::<T, Neon, Align, Unmasked, &'a [T]>::new(data).map(DispatchedView::Neon);
307 }
308 SimdView::<T, Scalar, Align, Unmasked, &'a [T]>::new(data).map(DispatchedView::Scalar)
309}
310
311#[inline]
313#[allow(unreachable_code)]
314pub fn dispatch_view_mut<'a, T, Align>(
315 data: &'a mut [T],
316) -> Option<DispatchedView<'a, T, Align, Unmasked, &'a mut [T]>>
317where
318 T: FloatElement,
319 Align: Alignment,
320{
321 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
322 {
323 #[cfg(feature = "std")]
324 {
325 if std::is_x86_feature_detected!("avx512f") {
326 return SimdView::<T, Avx512, Align, Unmasked, &'a mut [T]>::new_mut(data)
327 .map(DispatchedView::Avx512);
328 }
329 if std::is_x86_feature_detected!("avx2") && std::is_x86_feature_detected!("fma") {
330 return SimdView::<T, Avx2, Align, Unmasked, &'a mut [T]>::new_mut(data)
331 .map(DispatchedView::Avx2);
332 }
333 }
334 #[cfg(not(feature = "std"))]
335 {
336 if cfg!(target_feature = "avx512f") {
337 return SimdView::<T, Avx512, Align, Unmasked, &'a mut [T]>::new_mut(data)
338 .map(DispatchedView::Avx512);
339 }
340 if cfg!(target_feature = "avx2") && cfg!(target_feature = "fma") {
341 return SimdView::<T, Avx2, Align, Unmasked, &'a mut [T]>::new_mut(data)
342 .map(DispatchedView::Avx2);
343 }
344 }
345 }
346 #[cfg(target_arch = "aarch64")]
347 {
348 return SimdView::<T, Neon, Align, Unmasked, &'a mut [T]>::new_mut(data)
349 .map(DispatchedView::Neon);
350 }
351 SimdView::<T, Scalar, Align, Unmasked, &'a mut [T]>::new_mut(data).map(DispatchedView::Scalar)
352}
353
354pub trait SimdCowExt<T: SimdScalar, Arch: SimdArch + SimdKernel<T>, Align: Alignment> {
356 fn transform_vectors<F>(&mut self, f: F)
361 where
362 F: FnMut(Vector<T, Arch>) -> Vector<T, Arch>;
363}
364
365impl<'a, T, Arch, Align> SimdCowExt<T, Arch, Align> for SimdCow<'a, T, Arch, Align>
366where
367 T: SimdScalar,
368 Arch: SimdArch + SimdKernel<T>,
369 Align: Alignment,
370{
371 #[inline]
372 fn transform_vectors<F>(&mut self, mut f: F)
373 where
374 F: FnMut(Vector<T, Arch>) -> Vector<T, Arch>,
375 {
376 let owned_vec = self.to_mut();
377 let len = owned_vec.len();
378 let lane_count = Arch::LANE_COUNT;
379 let simd_len = (len / lane_count) * lane_count;
380 let slice = owned_vec.as_mut_slice();
381
382 let mut i = 0;
383 while i < simd_len {
384 unsafe {
385 let ptr = slice.as_mut_ptr().add(i);
386 let vec = if Align::IS_ALIGNED {
387 Vector::load_aligned(ptr)
388 } else {
389 Vector::load_unaligned(ptr)
390 };
391 let res = f(vec);
392 if Align::IS_ALIGNED {
393 res.store_aligned(ptr);
394 } else {
395 res.store_unaligned(ptr);
396 }
397 }
398 i += lane_count;
399 }
400
401 if simd_len < len {
402 debug_assert!(
406 lane_count <= 64,
407 "tail_buf[64] too small: lane_count={lane_count}"
408 );
409 let mut tail_buf = [core::mem::MaybeUninit::<T>::uninit(); 64];
410 let tail_len = len - simd_len;
411 unsafe {
412 for idx in 0..tail_len {
413 tail_buf[idx].write(slice[simd_len + idx]);
414 }
415 for idx in tail_len..lane_count {
416 tail_buf[idx].write(T::ZERO);
417 }
418 let vec = Vector::load_unaligned(tail_buf.as_ptr() as *const T);
419 let res = f(vec);
420 res.store_unaligned(tail_buf.as_mut_ptr() as *mut T);
421 for idx in 0..tail_len {
422 slice[simd_len + idx] = tail_buf[idx].assume_init();
423 }
424 }
425 }
426 }
427}
428
429pub trait Packed4CowExt<'a, T: Packable4> {
431 fn unpack_to_cow<Arch, Align>(&self) -> SimdCow<'static, T::Unpacked, Arch, Align>
433 where
434 Arch: SimdArch,
435 Align: Alignment;
436}
437
438impl<'a, T: Packable4> Packed4CowExt<'a, T> for Packed4Cow<'a, T> {
439 #[inline]
440 fn unpack_to_cow<Arch, Align>(&self) -> SimdCow<'static, T::Unpacked, Arch, Align>
441 where
442 Arch: SimdArch,
443 Align: Alignment,
444 {
445 let len = self.len();
446 let mut dest = AlignedVec::with_capacity(len);
447 unsafe {
448 dest.set_len(len);
449 }
450 let view = self.as_view();
451 let n = view.len().min(dest.len());
452 let even_len = (n / 2) * 2;
453 T::unpack_slice_packed(
454 &view.as_packed_slice()[..even_len / 2],
455 &mut dest[..even_len],
456 );
457 if n % 2 != 0 {
458 if let Some(b) = view.get(n - 1) {
459 dest[n - 1] = T::unpack_single(b);
460 }
461 }
462 SimdCow::Owned(dest)
463 }
464}