| //! Internal `#[repr(simd)]` types | |
| #![allow(non_camel_case_types)] | |
| #[inline(always)] | |
| #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] | |
| pub(crate) const unsafe fn simd_imax<T: Copy>(a: T, b: T) -> T { | |
| let mask: T = crate::intrinsics::simd::simd_gt(a, b); | |
| crate::intrinsics::simd::simd_select(mask, a, b) | |
| } | |
| #[inline(always)] | |
| #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] | |
| pub(crate) const unsafe fn simd_imin<T: Copy>(a: T, b: T) -> T { | |
| let mask: T = crate::intrinsics::simd::simd_lt(a, b); | |
| crate::intrinsics::simd::simd_select(mask, a, b) | |
| } | |
| /// SAFETY: All bits patterns must be valid | |
| pub(crate) unsafe trait SimdElement: | |
| Copy + const PartialEq + crate::fmt::Debug | |
| { | |
| // SAFETY: all bits patterns of types implementing this trait must be valid | |
| const ZERO: Self = unsafe { crate::mem::zeroed() }; | |
| } | |
| unsafe impl SimdElement for u8 {} | |
| unsafe impl SimdElement for u16 {} | |
| unsafe impl SimdElement for u32 {} | |
| unsafe impl SimdElement for u64 {} | |
| unsafe impl SimdElement for u128 {} | |
| unsafe impl SimdElement for i8 {} | |
| unsafe impl SimdElement for i16 {} | |
| unsafe impl SimdElement for i32 {} | |
| unsafe impl SimdElement for i64 {} | |
| unsafe impl SimdElement for i128 {} | |
| unsafe impl SimdElement for f16 {} | |
| unsafe impl SimdElement for f32 {} | |
| unsafe impl SimdElement for f64 {} | |
| #[repr(simd)] | |
| #[derive(Copy)] | |
| pub(crate) struct Simd<T: SimdElement, const N: usize>([T; N]); | |
| impl<T: SimdElement, const N: usize> Simd<T, N> { | |
| /// A value of this type where all elements are zeroed out. | |
| pub(crate) const ZERO: Self = Self::splat(T::ZERO); | |
| #[inline(always)] | |
| pub(crate) const fn from_array(elements: [T; N]) -> Self { | |
| Self(elements) | |
| } | |
| #[inline] | |
| #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] | |
| pub(crate) const fn splat(value: T) -> Self { | |
| unsafe { crate::intrinsics::simd::simd_splat(value) } | |
| } | |
| /// Extract the element at position `index`. Note that `index` is not a constant so this | |
| /// operation is not efficient on most platforms. Use for testing only. | |
| #[inline] | |
| #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] | |
| pub(crate) const fn extract_dyn(&self, index: usize) -> T { | |
| assert!(index < N); | |
| // SAFETY: self is a vector, T its element type. | |
| unsafe { crate::intrinsics::simd::simd_extract_dyn(*self, index as u32) } | |
| } | |
| #[inline] | |
| pub(crate) const fn as_array(&self) -> &[T; N] { | |
| let simd_ptr: *const Self = self; | |
| let array_ptr: *const [T; N] = simd_ptr.cast(); | |
| // SAFETY: We can always read the prefix of a simd type as an array. | |
| // There might be more padding afterwards for some widths, but | |
| // that's not a problem for reading less than that. | |
| unsafe { &*array_ptr } | |
| } | |
| } | |
| // `#[derive(Clone)]` causes ICE "Projecting into SIMD type core_arch::simd::Simd is banned by MCP#838" | |
| impl<T: SimdElement, const N: usize> Clone for Simd<T, N> { | |
| #[inline] | |
| fn clone(&self) -> Self { | |
| *self | |
| } | |
| } | |
| #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] | |
| #[rustfmt::skip] // FIXME: https://github.com/rust-lang/stdarch/pull/2133#issuecomment-4524350350 | |
| const impl<T: SimdElement, const N: usize> crate::cmp::PartialEq for Simd<T, N> { | |
| #[inline] | |
| fn eq(&self, other: &Self) -> bool { | |
| self.as_array() == other.as_array() | |
| } | |
| } | |
| impl<T: SimdElement, const N: usize> crate::fmt::Debug for Simd<T, N> { | |
| #[inline] | |
| fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { | |
| debug_simd_finish(f, "Simd", self.as_array()) | |
| } | |
| } | |
| impl<T: SimdElement> Simd<T, 1> { | |
| #[inline] | |
| pub(crate) const fn new(x0: T) -> Self { | |
| Self([x0]) | |
| } | |
| } | |
| impl<T: SimdElement> Simd<T, 2> { | |
| #[inline] | |
| pub(crate) const fn new(x0: T, x1: T) -> Self { | |
| Self([x0, x1]) | |
| } | |
| } | |
| impl<T: SimdElement> Simd<T, 4> { | |
| #[inline] | |
| pub(crate) const fn new(x0: T, x1: T, x2: T, x3: T) -> Self { | |
| Self([x0, x1, x2, x3]) | |
| } | |
| } | |
| impl<T: SimdElement> Simd<T, 8> { | |
| #[inline] | |
| pub(crate) const fn new(x0: T, x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T) -> Self { | |
| Self([x0, x1, x2, x3, x4, x5, x6, x7]) | |
| } | |
| } | |
| impl<T: SimdElement> Simd<T, 16> { | |
| #[inline] | |
| pub(crate) const fn new( | |
| x0: T, | |
| x1: T, | |
| x2: T, | |
| x3: T, | |
| x4: T, | |
| x5: T, | |
| x6: T, | |
| x7: T, | |
| x8: T, | |
| x9: T, | |
| x10: T, | |
| x11: T, | |
| x12: T, | |
| x13: T, | |
| x14: T, | |
| x15: T, | |
| ) -> Self { | |
| Self([ | |
| x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, | |
| ]) | |
| } | |
| } | |
| impl<T: SimdElement> Simd<T, 32> { | |
| #[inline] | |
| pub(crate) const fn new( | |
| x0: T, | |
| x1: T, | |
| x2: T, | |
| x3: T, | |
| x4: T, | |
| x5: T, | |
| x6: T, | |
| x7: T, | |
| x8: T, | |
| x9: T, | |
| x10: T, | |
| x11: T, | |
| x12: T, | |
| x13: T, | |
| x14: T, | |
| x15: T, | |
| x16: T, | |
| x17: T, | |
| x18: T, | |
| x19: T, | |
| x20: T, | |
| x21: T, | |
| x22: T, | |
| x23: T, | |
| x24: T, | |
| x25: T, | |
| x26: T, | |
| x27: T, | |
| x28: T, | |
| x29: T, | |
| x30: T, | |
| x31: T, | |
| ) -> Self { | |
| Self([ | |
| x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, | |
| x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31, | |
| ]) | |
| } | |
| } | |
| impl<const N: usize> Simd<f16, N> { | |
| #[inline] | |
| pub(crate) const fn to_bits(self) -> Simd<u16, N> { | |
| assert!(size_of::<Self>() == size_of::<Simd<u16, N>>()); | |
| unsafe { crate::mem::transmute_copy(&self) } | |
| } | |
| #[inline] | |
| pub(crate) const fn from_bits(bits: Simd<u16, N>) -> Self { | |
| assert!(size_of::<Self>() == size_of::<Simd<u16, N>>()); | |
| unsafe { crate::mem::transmute_copy(&bits) } | |
| } | |
| } | |
| impl<const N: usize> Simd<f32, N> { | |
| #[inline] | |
| pub(crate) const fn to_bits(self) -> Simd<u32, N> { | |
| assert!(size_of::<Self>() == size_of::<Simd<u32, N>>()); | |
| unsafe { crate::mem::transmute_copy(&self) } | |
| } | |
| #[inline] | |
| pub(crate) const fn from_bits(bits: Simd<u32, N>) -> Self { | |
| assert!(size_of::<Self>() == size_of::<Simd<u32, N>>()); | |
| unsafe { crate::mem::transmute_copy(&bits) } | |
| } | |
| } | |
| impl<const N: usize> Simd<f64, N> { | |
| #[inline] | |
| pub(crate) const fn to_bits(self) -> Simd<u64, N> { | |
| assert!(size_of::<Self>() == size_of::<Simd<u64, N>>()); | |
| unsafe { crate::mem::transmute_copy(&self) } | |
| } | |
| #[inline] | |
| pub(crate) const fn from_bits(bits: Simd<u64, N>) -> Self { | |
| assert!(size_of::<Self>() == size_of::<Simd<u64, N>>()); | |
| unsafe { crate::mem::transmute_copy(&bits) } | |
| } | |
| } | |
| #[repr(simd)] | |
| #[derive(Copy)] | |
| pub(crate) struct SimdM<T: SimdElement, const N: usize>([T; N]); | |
| impl<T: SimdElement, const N: usize> SimdM<T, N> { | |
| #[inline(always)] | |
| const fn bool_to_internal(x: bool) -> T { | |
| // SAFETY: `T` implements `SimdElement`, so all bit patterns are valid. | |
| let ones = const { | |
| // Ideally, this would be `transmute([0xFFu8; size_of::<T>()])`, but | |
| // `size_of::<T>()` is not allowed to use a generic parameter there. | |
| let mut r = crate::mem::MaybeUninit::<T>::uninit(); | |
| let mut i = 0; | |
| while i < crate::mem::size_of::<T>() { | |
| r.as_bytes_mut()[i] = crate::mem::MaybeUninit::new(0xFF); | |
| i += 1; | |
| } | |
| unsafe { r.assume_init() } | |
| }; | |
| [T::ZERO, ones][x as usize] | |
| } | |
| #[inline] | |
| pub(crate) const fn from_array(elements: [bool; N]) -> Self { | |
| let mut internal = [T::ZERO; N]; | |
| let mut i = 0; | |
| while i < N { | |
| internal[i] = Self::bool_to_internal(elements[i]); | |
| i += 1; | |
| } | |
| Self(internal) | |
| } | |
| #[inline] | |
| #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] | |
| pub(crate) const fn splat(value: bool) -> Self { | |
| unsafe { crate::intrinsics::simd::simd_splat(Self::bool_to_internal(value)) } | |
| } | |
| #[inline] | |
| pub(crate) const fn as_array(&self) -> &[T; N] { | |
| let simd_ptr: *const Self = self; | |
| let array_ptr: *const [T; N] = simd_ptr.cast(); | |
| // SAFETY: We can always read the prefix of a simd type as an array. | |
| // There might be more padding afterwards for some widths, but | |
| // that's not a problem for reading less than that. | |
| unsafe { &*array_ptr } | |
| } | |
| } | |
| // `#[derive(Clone)]` causes ICE "Projecting into SIMD type core_arch::simd::SimdM is banned by MCP#838" | |
| impl<T: SimdElement, const N: usize> Clone for SimdM<T, N> { | |
| #[inline] | |
| fn clone(&self) -> Self { | |
| *self | |
| } | |
| } | |
| #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] | |
| #[rustfmt::skip] // FIXME: https://github.com/rust-lang/stdarch/pull/2133#issuecomment-4524350350 | |
| const impl<T: SimdElement, const N: usize> crate::cmp::PartialEq for SimdM<T, N> { | |
| #[inline] | |
| fn eq(&self, other: &Self) -> bool { | |
| self.as_array() == other.as_array() | |
| } | |
| } | |
| impl<T: SimdElement, const N: usize> crate::fmt::Debug for SimdM<T, N> { | |
| #[inline] | |
| fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { | |
| debug_simd_finish(f, "SimdM", self.as_array()) | |
| } | |
| } | |
| // 16-bit wide types: | |
| pub(crate) type u8x2 = Simd<u8, 2>; | |
| pub(crate) type i8x2 = Simd<i8, 2>; | |
| // 32-bit wide types: | |
| pub(crate) type u8x4 = Simd<u8, 4>; | |
| pub(crate) type u16x2 = Simd<u16, 2>; | |
| pub(crate) type i8x4 = Simd<i8, 4>; | |
| pub(crate) type i16x2 = Simd<i16, 2>; | |
| // 64-bit wide types: | |
| pub(crate) type u8x8 = Simd<u8, 8>; | |
| pub(crate) type u16x4 = Simd<u16, 4>; | |
| pub(crate) type u32x2 = Simd<u32, 2>; | |
| pub(crate) type u64x1 = Simd<u64, 1>; | |
| pub(crate) type i8x8 = Simd<i8, 8>; | |
| pub(crate) type i16x4 = Simd<i16, 4>; | |
| pub(crate) type i32x2 = Simd<i32, 2>; | |
| pub(crate) type i64x1 = Simd<i64, 1>; | |
| pub(crate) type f16x4 = Simd<f16, 4>; | |
| pub(crate) type f32x2 = Simd<f32, 2>; | |
| pub(crate) type f64x1 = Simd<f64, 1>; | |
| // 128-bit wide types: | |
| pub(crate) type u8x16 = Simd<u8, 16>; | |
| pub(crate) type u16x8 = Simd<u16, 8>; | |
| pub(crate) type u32x4 = Simd<u32, 4>; | |
| pub(crate) type u64x2 = Simd<u64, 2>; | |
| pub(crate) type i8x16 = Simd<i8, 16>; | |
| pub(crate) type i16x8 = Simd<i16, 8>; | |
| pub(crate) type i32x4 = Simd<i32, 4>; | |
| pub(crate) type i64x2 = Simd<i64, 2>; | |
| pub(crate) type f16x8 = Simd<f16, 8>; | |
| pub(crate) type f32x4 = Simd<f32, 4>; | |
| pub(crate) type f64x2 = Simd<f64, 2>; | |
| pub(crate) type m8x16 = SimdM<i8, 16>; | |
| pub(crate) type m16x8 = SimdM<i16, 8>; | |
| pub(crate) type m32x4 = SimdM<i32, 4>; | |
| pub(crate) type m64x2 = SimdM<i64, 2>; | |
| // 256-bit wide types: | |
| pub(crate) type u8x32 = Simd<u8, 32>; | |
| pub(crate) type u16x16 = Simd<u16, 16>; | |
| pub(crate) type u32x8 = Simd<u32, 8>; | |
| pub(crate) type u64x4 = Simd<u64, 4>; | |
| pub(crate) type u128x2 = Simd<u128, 2>; | |
| pub(crate) type i8x32 = Simd<i8, 32>; | |
| pub(crate) type i16x16 = Simd<i16, 16>; | |
| pub(crate) type i32x8 = Simd<i32, 8>; | |
| pub(crate) type i64x4 = Simd<i64, 4>; | |
| pub(crate) type i128x2 = Simd<i128, 2>; | |
| pub(crate) type f16x16 = Simd<f16, 16>; | |
| pub(crate) type f32x8 = Simd<f32, 8>; | |
| pub(crate) type f64x4 = Simd<f64, 4>; | |
| pub(crate) type m8x32 = SimdM<i8, 32>; | |
| pub(crate) type m16x16 = SimdM<i16, 16>; | |
| pub(crate) type m32x8 = SimdM<i32, 8>; | |
| // 512-bit wide types: | |
| pub(crate) type u8x64 = Simd<u8, 64>; | |
| pub(crate) type u16x32 = Simd<u16, 32>; | |
| pub(crate) type u32x16 = Simd<u32, 16>; | |
| pub(crate) type u64x8 = Simd<u64, 8>; | |
| pub(crate) type u128x4 = Simd<u128, 4>; | |
| pub(crate) type i8x64 = Simd<i8, 64>; | |
| pub(crate) type i16x32 = Simd<i16, 32>; | |
| pub(crate) type i32x16 = Simd<i32, 16>; | |
| pub(crate) type i64x8 = Simd<i64, 8>; | |
| pub(crate) type i128x4 = Simd<i128, 4>; | |
| pub(crate) type f16x32 = Simd<f16, 32>; | |
| pub(crate) type f32x16 = Simd<f32, 16>; | |
| pub(crate) type f64x8 = Simd<f64, 8>; | |
| // 1024-bit wide types: | |
| pub(crate) type u16x64 = Simd<u16, 64>; | |
| pub(crate) type u32x32 = Simd<u32, 32>; | |
| pub(crate) type i32x32 = Simd<i32, 32>; | |
| /// Used to continue `Debug`ging SIMD types as `MySimd(1, 2, 3, 4)`, as they | |
| /// were before moving to array-based simd. | |
| #[inline] | |
| pub(crate) fn debug_simd_finish<T: crate::fmt::Debug, const N: usize>( | |
| formatter: &mut crate::fmt::Formatter<'_>, | |
| type_name: &str, | |
| array: &[T; N], | |
| ) -> crate::fmt::Result { | |
| crate::fmt::Formatter::debug_tuple_fields_finish( | |
| formatter, | |
| type_name, | |
| &crate::array::from_fn::<&dyn crate::fmt::Debug, N, _>(|i| &array[i]), | |
| ) | |
| } |