1use super::mask_reg::Mask;
24use super::SimdError;
25use crate::arch::SimdArch;
26use crate::kernel::{SimdKernel, MAX_SIMD_LANES};
27use crate::mask::BitMask;
28use crate::scalar::{CastFrom, Scalar};
29use core::marker::PhantomData;
30
31#[repr(transparent)]
33pub struct Vector<T, Arch>
34where
35 Arch: SimdArch + SimdKernel<T>,
36 T: Scalar,
37{
38 pub raw: Arch::Vector,
40 _marker: PhantomData<T>,
41}
42
43impl<T, Arch> Clone for Vector<T, Arch>
44where
45 Arch: SimdArch + SimdKernel<T>,
46 T: Scalar,
47{
48 #[inline(always)]
49 fn clone(&self) -> Self {
50 *self
51 }
52}
53
54impl<T, Arch> Copy for Vector<T, Arch>
55where
56 Arch: SimdArch + SimdKernel<T>,
57 T: Scalar,
58{
59}
60
61impl<T, Arch> core::fmt::Debug for Vector<T, Arch>
62where
63 Arch: SimdArch + SimdKernel<T>,
64 T: Scalar + core::fmt::Debug,
65{
66 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
67 assert_runtime_supported::<T, Arch>();
68 const { <Arch as SimdKernel<T>>::LANE_BOUND_CHECK };
69 let lane_count = Arch::LANE_COUNT;
70 let mut buf = [core::mem::MaybeUninit::<T>::uninit(); MAX_SIMD_LANES];
71 unsafe {
76 Arch::store_unaligned(buf.as_mut_ptr() as *mut T, self.raw);
77 let init_slice = core::slice::from_raw_parts(buf.as_ptr() as *const T, lane_count);
78 f.debug_list().entries(init_slice).finish()
79 }
80 }
81}
82
83impl<T, Arch> PartialEq for Vector<T, Arch>
84where
85 Arch: SimdArch + SimdKernel<T>,
86 T: Scalar + PartialEq,
87{
88 #[inline]
89 fn eq(&self, other: &Self) -> bool {
90 assert_runtime_supported::<T, Arch>();
91 const { <Arch as SimdKernel<T>>::LANE_BOUND_CHECK };
92 let lane_count = Arch::LANE_COUNT;
93 let mut buf_self = [core::mem::MaybeUninit::<T>::uninit(); MAX_SIMD_LANES];
94 let mut buf_other = [core::mem::MaybeUninit::<T>::uninit(); MAX_SIMD_LANES];
95 unsafe {
99 Arch::store_unaligned(buf_self.as_mut_ptr() as *mut T, self.raw);
100 Arch::store_unaligned(buf_other.as_mut_ptr() as *mut T, other.raw);
101 let slice_self = core::slice::from_raw_parts(buf_self.as_ptr() as *const T, lane_count);
102 let slice_other =
103 core::slice::from_raw_parts(buf_other.as_ptr() as *const T, lane_count);
104 slice_self == slice_other
105 }
106 }
107}
108
109impl<T, Arch> Eq for Vector<T, Arch>
110where
111 Arch: SimdArch + SimdKernel<T>,
112 T: Scalar + Eq,
113{
114}
115
116impl<T, Arch> Vector<T, Arch>
117where
118 Arch: SimdArch + SimdKernel<T>,
119 T: Scalar,
120{
121 #[inline(always)]
123 pub const fn new(raw: Arch::Vector) -> Self {
124 Self {
125 raw,
126 _marker: PhantomData,
127 }
128 }
129
130 #[inline(always)]
132 pub fn zero() -> Self {
133 Self::try_zero().expect("SIMD target is not supported or enabled on this host")
134 }
135
136 #[inline(always)]
138 pub fn try_zero() -> Result<Self, SimdError> {
139 runtime_support_result::<T, Arch>()?;
140 Ok(Self::new(unsafe { Arch::zero() }))
141 }
142
143 #[inline(always)]
145 pub fn splat(val: T) -> Self {
146 Self::try_splat(val).expect("SIMD target is not supported or enabled on this host")
147 }
148
149 #[inline(always)]
151 pub fn try_splat(val: T) -> Result<Self, SimdError> {
152 runtime_support_result::<T, Arch>()?;
153 Ok(Self::new(unsafe { Arch::splat(val) }))
154 }
155
156 #[inline(always)]
162 pub unsafe fn load_aligned(ptr: *const T) -> Self {
163 Self::new(Arch::load_aligned(ptr))
164 }
165
166 #[inline(always)]
172 pub unsafe fn load_unaligned(ptr: *const T) -> Self {
173 Self::new(Arch::load_unaligned(ptr))
174 }
175
176 #[inline(always)]
182 pub unsafe fn store_aligned(self, ptr: *mut T) {
183 Arch::store_aligned(ptr, self.raw);
184 }
185
186 #[inline(always)]
192 pub unsafe fn store_unaligned(self, ptr: *mut T) {
193 Arch::store_unaligned(ptr, self.raw);
194 }
195
196 #[inline(always)]
202 pub unsafe fn masked_load_unaligned(ptr: *const T, mask: Mask<T, Arch>, src: Self) -> Self {
203 Self::new(Arch::masked_load_unaligned(ptr, mask.raw, src.raw))
204 }
205
206 #[inline(always)]
212 pub unsafe fn masked_store_unaligned(self, ptr: *mut T, mask: Mask<T, Arch>) {
213 Arch::masked_store_unaligned(ptr, mask.raw, self.raw);
214 }
215
216 #[inline(always)]
221 pub fn load_unaligned_from_slice(data: &[T]) -> Result<Self, SimdError> {
222 runtime_support_result::<T, Arch>()?;
223 if data.len() < Arch::LANE_COUNT {
224 return Err(SimdError::InsufficientInputLength);
225 }
226 unsafe { Ok(Self::load_unaligned(data.as_ptr())) }
229 }
230
231 #[inline(always)]
237 pub fn load_aligned_from_slice(data: &[T]) -> Result<Self, SimdError> {
238 runtime_support_result::<T, Arch>()?;
239 if data.len() < Arch::LANE_COUNT {
240 return Err(SimdError::InsufficientInputLength);
241 }
242 if !is_vector_aligned::<T, Arch>(data.as_ptr()) {
243 return Err(SimdError::UnalignedAddress);
244 }
245 unsafe { Ok(Self::load_aligned(data.as_ptr())) }
247 }
248
249 #[inline(always)]
254 pub fn store_unaligned_to_slice(self, out: &mut [T]) -> Result<(), SimdError> {
255 runtime_support_result::<T, Arch>()?;
256 if out.len() < Arch::LANE_COUNT {
257 return Err(SimdError::InsufficientOutputLength);
258 }
259 unsafe {
262 self.store_unaligned(out.as_mut_ptr());
263 }
264 Ok(())
265 }
266
267 #[inline(always)]
273 pub fn store_aligned_to_slice(self, out: &mut [T]) -> Result<(), SimdError> {
274 runtime_support_result::<T, Arch>()?;
275 if out.len() < Arch::LANE_COUNT {
276 return Err(SimdError::InsufficientOutputLength);
277 }
278 if !is_vector_aligned::<T, Arch>(out.as_ptr()) {
279 return Err(SimdError::UnalignedAddress);
280 }
281 unsafe {
283 self.store_aligned(out.as_mut_ptr());
284 }
285 Ok(())
286 }
287
288 #[inline]
293 pub fn masked_load_from_slice(
294 data: &[T],
295 mask: Mask<T, Arch>,
296 src: Self,
297 ) -> Result<Self, SimdError> {
298 runtime_support_result::<T, Arch>()?;
299 let len = data.len();
300 let bm = unsafe { mask.to_bitmask().0 };
301 let is_out_of_bounds = if len < u64::BITS as usize {
302 (bm >> len) != 0
303 } else {
304 false
305 };
306 if is_out_of_bounds {
307 return Err(SimdError::IndexOutOfBounds);
308 }
309
310 if len >= Arch::LANE_COUNT {
311 unsafe { Ok(Self::masked_load_unaligned(data.as_ptr(), mask, src)) }
315 } else {
316 const { <Arch as SimdKernel<T>>::LANE_BOUND_CHECK };
321 #[repr(C, align(64))]
322 struct AlignedBuf<T>([core::mem::MaybeUninit<T>; MAX_SIMD_LANES]);
323
324 let mut buf = AlignedBuf([core::mem::MaybeUninit::uninit(); MAX_SIMD_LANES]);
325 for i in 0..len {
326 buf.0[i].write(data[i]);
327 }
328 for i in len..Arch::LANE_COUNT {
329 buf.0[i].write(T::ZERO);
330 }
331
332 unsafe {
333 Ok(Self::masked_load_unaligned(
334 buf.0.as_ptr() as *const T,
335 mask,
336 src,
337 ))
338 }
339 }
340 }
341
342 #[inline]
347 pub fn masked_store_to_slice(
348 self,
349 data: &mut [T],
350 mask: Mask<T, Arch>,
351 ) -> Result<(), SimdError> {
352 runtime_support_result::<T, Arch>()?;
353 let len = data.len();
354 let bm = unsafe { mask.to_bitmask().0 };
355 let is_out_of_bounds = if len < u64::BITS as usize {
356 (bm >> len) != 0
357 } else {
358 false
359 };
360 if is_out_of_bounds {
361 return Err(SimdError::IndexOutOfBounds);
362 }
363
364 if len >= Arch::LANE_COUNT {
365 unsafe {
369 self.masked_store_unaligned(data.as_mut_ptr(), mask);
370 }
371 } else {
372 const { <Arch as SimdKernel<T>>::LANE_BOUND_CHECK };
377 #[repr(C, align(64))]
378 struct AlignedBuf<T>([core::mem::MaybeUninit<T>; MAX_SIMD_LANES]);
379
380 let mut buf = AlignedBuf([core::mem::MaybeUninit::uninit(); MAX_SIMD_LANES]);
381 for i in 0..len {
382 buf.0[i].write(data[i]);
383 }
384
385 unsafe {
386 self.masked_store_unaligned(buf.0.as_mut_ptr() as *mut T, mask);
387 }
388
389 unsafe {
390 let init_slice = core::slice::from_raw_parts(buf.0.as_ptr() as *const T, len);
391 data.copy_from_slice(init_slice);
392 }
393 }
394 Ok(())
395 }
396
397 #[inline(always)]
399 pub fn sum_reduce(self) -> T {
400 assert_runtime_supported::<T, Arch>();
401 unsafe { Arch::sum_reduce(self.raw) }
402 }
403
404 #[inline(always)]
406 pub fn popcount(self) -> Self {
407 assert_runtime_supported::<T, Arch>();
408 Self::new(unsafe { Arch::popcount(self.raw) })
409 }
410
411 #[inline(always)]
413 pub fn horizontal_bitwise_and(self) -> T {
414 assert_runtime_supported::<T, Arch>();
415 unsafe { Arch::horizontal_bitwise_and(self.raw) }
416 }
417
418 #[inline(always)]
420 pub fn horizontal_bitwise_or(self) -> T {
421 assert_runtime_supported::<T, Arch>();
422 unsafe { Arch::horizontal_bitwise_or(self.raw) }
423 }
424
425 #[inline(always)]
427 pub fn horizontal_bitwise_xor(self) -> T {
428 assert_runtime_supported::<T, Arch>();
429 unsafe { Arch::horizontal_bitwise_xor(self.raw) }
430 }
431
432 #[inline(always)]
434 pub fn abs(self) -> Self {
435 assert_runtime_supported::<T, Arch>();
436 Self::new(unsafe { Arch::abs(self.raw) })
437 }
438
439 #[inline(always)]
441 pub fn min(self, other: Self) -> Self {
442 assert_runtime_supported::<T, Arch>();
443 Self::new(unsafe { Arch::min(self.raw, other.raw) })
444 }
445
446 #[inline(always)]
448 pub fn max(self, other: Self) -> Self {
449 assert_runtime_supported::<T, Arch>();
450 Self::new(unsafe { Arch::max(self.raw, other.raw) })
451 }
452
453 #[inline(always)]
455 pub fn sqrt(self) -> Self {
456 assert_runtime_supported::<T, Arch>();
457 Self::new(unsafe { Arch::sqrt(self.raw) })
458 }
459
460 #[inline(always)]
462 pub fn cmp_eq(self, other: Self) -> Self {
463 assert_runtime_supported::<T, Arch>();
464 Self::new(unsafe { Arch::cmp_eq(self.raw, other.raw) })
465 }
466
467 #[inline(always)]
469 pub fn cmp_ne(self, other: Self) -> Self {
470 assert_runtime_supported::<T, Arch>();
471 Self::new(unsafe { Arch::cmp_ne(self.raw, other.raw) })
472 }
473
474 #[inline(always)]
476 pub fn cmp_lt(self, other: Self) -> Self {
477 assert_runtime_supported::<T, Arch>();
478 Self::new(unsafe { Arch::cmp_lt(self.raw, other.raw) })
479 }
480
481 #[inline(always)]
483 pub fn cmp_le(self, other: Self) -> Self {
484 assert_runtime_supported::<T, Arch>();
485 Self::new(unsafe { Arch::cmp_le(self.raw, other.raw) })
486 }
487
488 #[inline(always)]
490 pub fn cmp_gt(self, other: Self) -> Self {
491 assert_runtime_supported::<T, Arch>();
492 Self::new(unsafe { Arch::cmp_gt(self.raw, other.raw) })
493 }
494
495 #[inline(always)]
497 pub fn cmp_ge(self, other: Self) -> Self {
498 assert_runtime_supported::<T, Arch>();
499 Self::new(unsafe { Arch::cmp_ge(self.raw, other.raw) })
500 }
501
502 #[inline(always)]
504 pub fn blend(self, true_val: Self, false_val: Self) -> Self {
505 assert_runtime_supported::<T, Arch>();
506 Self::new(unsafe { Arch::blend(self.raw, true_val.raw, false_val.raw) })
507 }
508
509 #[inline(always)]
511 pub fn from_array<const N: usize>(arr: [T; N]) -> Self {
512 assert_runtime_supported::<T, Arch>();
513 let _ = AssertLaneCount::<T, Arch, N>::OK;
514 unsafe { Self::load_unaligned(arr.as_ptr()) }
518 }
519
520 #[inline(always)]
523 pub fn try_from_array<const N: usize>(arr: [T; N]) -> Result<Self, SimdError> {
524 runtime_support_result::<T, Arch>()?;
525 let _ = AssertLaneCount::<T, Arch, N>::OK;
526 unsafe { Ok(Self::load_unaligned(arr.as_ptr())) }
528 }
529
530 #[inline(always)]
532 pub fn to_array<const N: usize>(self) -> [T; N] {
533 assert_runtime_supported::<T, Arch>();
534 let _ = AssertLaneCount::<T, Arch, N>::OK;
535 let mut arr = [core::mem::MaybeUninit::<T>::uninit(); N];
536 unsafe {
540 self.store_unaligned(arr.as_mut_ptr() as *mut T);
541 core::ptr::read(arr.as_ptr() as *const [T; N])
542 }
543 }
544
545 #[inline(always)]
547 pub fn to_bitmask(self) -> BitMask<64> {
548 assert_runtime_supported::<T, Arch>();
549 const { <Arch as SimdKernel<T>>::LANE_BOUND_CHECK };
550 let mut buf = [core::mem::MaybeUninit::<T>::uninit(); MAX_SIMD_LANES];
551 let lanes = <Arch as SimdKernel<T>>::LANE_COUNT;
552 unsafe {
556 self.store_unaligned(buf.as_mut_ptr() as *mut T);
557 let mut m = 0u64;
558 for i in 0..lanes {
559 let val = buf[i].assume_init();
560 if val.to_f64() != 0.0 || val.is_nan() {
561 m |= 1u64 << i;
562 }
563 }
564 BitMask(m)
565 }
566 }
567
568 #[inline(always)]
570 pub fn cmp_eq_mask(self, other: Self) -> Mask<T, Arch> {
571 unsafe { Mask::from_bitmask(self.cmp_eq(other).to_bitmask()) }
574 }
575
576 #[inline(always)]
578 pub fn cmp_ne_mask(self, other: Self) -> Mask<T, Arch> {
579 unsafe { Mask::from_bitmask(self.cmp_ne(other).to_bitmask()) }
582 }
583
584 #[inline(always)]
586 pub fn cmp_lt_mask(self, other: Self) -> Mask<T, Arch> {
587 unsafe { Mask::from_bitmask(self.cmp_lt(other).to_bitmask()) }
590 }
591
592 #[inline(always)]
594 pub fn cmp_le_mask(self, other: Self) -> Mask<T, Arch> {
595 unsafe { Mask::from_bitmask(self.cmp_le(other).to_bitmask()) }
598 }
599
600 #[inline(always)]
602 pub fn cmp_gt_mask(self, other: Self) -> Mask<T, Arch> {
603 unsafe { Mask::from_bitmask(self.cmp_gt(other).to_bitmask()) }
606 }
607
608 #[inline(always)]
610 pub fn cmp_ge_mask(self, other: Self) -> Mask<T, Arch> {
611 unsafe { Mask::from_bitmask(self.cmp_ge(other).to_bitmask()) }
614 }
615
616 #[inline(always)]
618 pub fn cast<U>(self) -> Vector<U, Arch>
619 where
620 Arch: SimdKernel<U>,
621 U: Scalar,
622 U: CastFrom<T>,
623 {
624 assert_runtime_supported::<T, Arch>();
625 assert_runtime_supported::<U, Arch>();
626 let _ = AssertLaneCountSame::<T, U, Arch>::OK;
627 const { <Arch as SimdKernel<T>>::LANE_BOUND_CHECK };
628 let mut buf_t = [core::mem::MaybeUninit::<T>::uninit(); MAX_SIMD_LANES];
629 let mut buf_u = [core::mem::MaybeUninit::<U>::uninit(); MAX_SIMD_LANES];
630 let lanes = <Arch as SimdKernel<T>>::LANE_COUNT;
631 unsafe {
637 self.store_unaligned(buf_t.as_mut_ptr() as *mut T);
638 for i in 0..lanes {
639 let val_t = buf_t[i].assume_init();
640 buf_u[i].write(U::cast_from(val_t));
641 }
642 Vector::<U, Arch>::new(Arch::load_unaligned(buf_u.as_ptr() as *const U))
643 }
644 }
645
646 #[inline(always)]
648 pub fn extract<const I: usize>(self) -> T {
649 assert_runtime_supported::<T, Arch>();
650 let _ = AssertLaneIndex::<T, Arch, I>::OK;
651 const { <Arch as SimdKernel<T>>::LANE_BOUND_CHECK };
652 let mut buf = [core::mem::MaybeUninit::<T>::uninit(); MAX_SIMD_LANES];
653 unsafe {
657 self.store_unaligned(buf.as_mut_ptr() as *mut T);
658 buf[I].assume_init()
659 }
660 }
661
662 #[inline(always)]
664 pub fn insert<const I: usize>(self, val: T) -> Self {
665 assert_runtime_supported::<T, Arch>();
666 let _ = AssertLaneIndex::<T, Arch, I>::OK;
667 const { <Arch as SimdKernel<T>>::LANE_BOUND_CHECK };
668 let mut buf = [core::mem::MaybeUninit::<T>::uninit(); MAX_SIMD_LANES];
669 unsafe {
674 self.store_unaligned(buf.as_mut_ptr() as *mut T);
675 buf[I].write(val);
676 Self::load_unaligned(buf.as_ptr() as *const T)
677 }
678 }
679
680 #[inline(always)]
682 pub fn from_view_chunk<Align, Mode, Ref>(
683 view: &super::SimdView<'_, T, Arch, Align, Mode, Ref>,
684 chunk_idx: usize,
685 ) -> Self
686 where
687 Align: crate::align::Alignment,
688 Mode: crate::execution::ExecutionMode,
689 Ref: core::ops::Deref<Target = [T]>,
690 {
691 assert_runtime_supported::<T, Arch>();
692 let offset = chunk_idx * Arch::LANE_COUNT;
693 let slice = view.as_slice();
694 assert!(
695 offset + Arch::LANE_COUNT <= slice.len(),
696 "Chunk index out of bounds"
697 );
698 unsafe {
703 if crate::align::is_aligned_for_arch::<Arch, Align>() {
704 Self::load_aligned(slice.as_ptr().add(offset))
705 } else {
706 Self::load_unaligned(slice.as_ptr().add(offset))
707 }
708 }
709 }
710
711 #[inline(always)]
713 pub fn store_to_view_chunk<'a, Align, Mode>(
714 self,
715 view: &mut super::SimdView<'a, T, Arch, Align, Mode, &'a mut [T]>,
716 chunk_idx: usize,
717 ) where
718 Align: crate::align::Alignment,
719 Mode: crate::execution::ExecutionMode,
720 {
721 assert_runtime_supported::<T, Arch>();
722 let offset = chunk_idx * Arch::LANE_COUNT;
723 let slice = view.as_slice_mut();
724 assert!(
725 offset + Arch::LANE_COUNT <= slice.len(),
726 "Chunk index out of bounds"
727 );
728 unsafe {
732 if crate::align::is_aligned_for_arch::<Arch, Align>() {
733 self.store_aligned(slice.as_mut_ptr().add(offset));
734 } else {
735 self.store_unaligned(slice.as_mut_ptr().add(offset));
736 }
737 }
738 }
739}
740
741#[inline(always)]
742fn is_vector_aligned<T, Arch>(ptr: *const T) -> bool
743where
744 Arch: SimdArch + SimdKernel<T>,
745 T: Scalar,
746{
747 let alignment = Arch::LANE_COUNT * core::mem::size_of::<T>();
748 alignment != 0 && (ptr as usize).is_multiple_of(alignment)
749}
750
751#[inline(always)]
752pub(crate) fn runtime_support_result<T, Arch>() -> Result<(), SimdError>
753where
754 Arch: SimdArch + SimdKernel<T>,
755 T: Scalar,
756{
757 if Arch::is_runtime_supported() {
758 Ok(())
759 } else {
760 Err(SimdError::UnsupportedTarget)
761 }
762}
763
764#[inline(always)]
765pub(crate) fn assert_runtime_supported<T, Arch>()
766where
767 Arch: SimdArch + SimdKernel<T>,
768 T: Scalar,
769{
770 assert!(
771 Arch::is_runtime_supported(),
772 "SIMD target is not supported or enabled on this host"
773 );
774}
775
776struct AssertLaneIndex<T, Arch, const I: usize>(PhantomData<(T, Arch)>);
777impl<T, Arch, const I: usize> AssertLaneIndex<T, Arch, I>
778where
779 Arch: SimdArch + SimdKernel<T>,
780 T: Scalar,
781{
782 const OK: () = {
783 assert!(
784 I < <Arch as SimdKernel<T>>::LANE_COUNT,
785 "Lane index out of bounds"
786 );
787 };
788}
789
790struct AssertLaneCountSame<T, U, Arch>(PhantomData<(T, U, Arch)>);
791impl<T, U, Arch> AssertLaneCountSame<T, U, Arch>
792where
793 Arch: SimdArch + SimdKernel<T> + SimdKernel<U>,
794 T: Scalar,
795 U: Scalar,
796{
797 const OK: () = {
798 assert!(
799 <Arch as SimdKernel<T>>::LANE_COUNT == <Arch as SimdKernel<U>>::LANE_COUNT,
800 "Source and destination vectors must have the same lane count"
801 );
802 };
803}
804
805struct AssertLaneCount<T, Arch, const N: usize>(PhantomData<(T, Arch)>);
806impl<T, Arch, const N: usize> AssertLaneCount<T, Arch, N>
807where
808 Arch: SimdArch + SimdKernel<T>,
809 T: Scalar,
810{
811 const OK: () = {
812 assert!(
813 N == Arch::LANE_COUNT,
814 "Array size must match Vector lane count"
815 );
816 };
817}