1 use crate::simd::intrinsics;
2 use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
3 use core::ops::{Add, Mul};
4 use core::ops::{BitAnd, BitOr, BitXor};
5 use core::ops::{Div, Rem, Sub};
6 use core::ops::{Shl, Shr};
12 impl<I, T, const LANES: usize> core::ops::Index<I> for Simd<T, LANES>
15 LaneCount<LANES>: SupportedLaneCount,
16 I: core::slice::SliceIndex<[T]>,
18 type Output = I::Output;
19 fn index(&self, index: I) -> &Self::Output {
20 &self.as_array()[index]
24 impl<I, T, const LANES: usize> core::ops::IndexMut<I> for Simd<T, LANES>
27 LaneCount<LANES>: SupportedLaneCount,
28 I: core::slice::SliceIndex<[T]>,
30 fn index_mut(&mut self, index: I) -> &mut Self::Output {
31 &mut self.as_mut_array()[index]
35 /// Checks if the right-hand side argument of a left- or right-shift would cause overflow.
36 fn invalid_shift_rhs<T>(rhs: T) -> bool
38 T: Default + PartialOrd + core::convert::TryFrom<usize>,
39 <T as core::convert::TryFrom<usize>>::Error: core::fmt::Debug,
41 let bits_in_type = T::try_from(8 * core::mem::size_of::<T>()).unwrap();
42 rhs < T::default() || rhs >= bits_in_type
45 /// Automatically implements operators over references in addition to the provided operator.
46 macro_rules! impl_ref_ops {
49 impl<const $lanes:ident: usize> core::ops::$trait:ident<$rhs:ty> for $type:ty
51 LaneCount<$lanes2:ident>: SupportedLaneCount,
53 type Output = $output:ty;
56 fn $fn:ident($self_tok:ident, $rhs_arg:ident: $rhs_arg_ty:ty) -> Self::Output $body:tt
59 impl<const $lanes: usize> core::ops::$trait<$rhs> for $type
61 LaneCount<$lanes2>: SupportedLaneCount,
63 type Output = $output;
66 fn $fn($self_tok, $rhs_arg: $rhs_arg_ty) -> Self::Output $body
71 /// Automatically implements operators over vectors and scalars for a particular vector.
72 macro_rules! impl_op {
73 { impl Add for $scalar:ty } => {
74 impl_op! { @binary $scalar, Add::add, simd_add }
76 { impl Sub for $scalar:ty } => {
77 impl_op! { @binary $scalar, Sub::sub, simd_sub }
79 { impl Mul for $scalar:ty } => {
80 impl_op! { @binary $scalar, Mul::mul, simd_mul }
82 { impl Div for $scalar:ty } => {
83 impl_op! { @binary $scalar, Div::div, simd_div }
85 { impl Rem for $scalar:ty } => {
86 impl_op! { @binary $scalar, Rem::rem, simd_rem }
88 { impl Shl for $scalar:ty } => {
89 impl_op! { @binary $scalar, Shl::shl, simd_shl }
91 { impl Shr for $scalar:ty } => {
92 impl_op! { @binary $scalar, Shr::shr, simd_shr }
94 { impl BitAnd for $scalar:ty } => {
95 impl_op! { @binary $scalar, BitAnd::bitand, simd_and }
97 { impl BitOr for $scalar:ty } => {
98 impl_op! { @binary $scalar, BitOr::bitor, simd_or }
100 { impl BitXor for $scalar:ty } => {
101 impl_op! { @binary $scalar, BitXor::bitxor, simd_xor }
104 // generic binary op with assignment when output is `Self`
105 { @binary $scalar:ty, $trait:ident :: $trait_fn:ident, $intrinsic:ident } => {
107 impl<const LANES: usize> core::ops::$trait<Self> for Simd<$scalar, LANES>
109 LaneCount<LANES>: SupportedLaneCount,
114 fn $trait_fn(self, rhs: Self) -> Self::Output {
116 intrinsics::$intrinsic(self, rhs)
124 /// Implements floating-point operators for the provided types.
125 macro_rules! impl_float_ops {
126 { $($scalar:ty),* } => {
128 impl_op! { impl Add for $scalar }
129 impl_op! { impl Sub for $scalar }
130 impl_op! { impl Mul for $scalar }
131 impl_op! { impl Div for $scalar }
132 impl_op! { impl Rem for $scalar }
137 /// Implements unsigned integer operators for the provided types.
138 macro_rules! impl_unsigned_int_ops {
139 { $($scalar:ty),* } => {
141 impl_op! { impl Add for $scalar }
142 impl_op! { impl Sub for $scalar }
143 impl_op! { impl Mul for $scalar }
144 impl_op! { impl BitAnd for $scalar }
145 impl_op! { impl BitOr for $scalar }
146 impl_op! { impl BitXor for $scalar }
148 // Integers panic on divide by 0
150 impl<const LANES: usize> core::ops::Div<Self> for Simd<$scalar, LANES>
152 LaneCount<LANES>: SupportedLaneCount,
157 fn div(self, rhs: Self) -> Self::Output {
162 panic!("attempt to divide by zero");
165 // Guards for div(MIN, -1),
166 // this check only applies to signed ints
167 if <$scalar>::MIN != 0 && self.as_array().iter()
168 .zip(rhs.as_array().iter())
169 .any(|(x,y)| *x == <$scalar>::MIN && *y == -1 as _) {
170 panic!("attempt to divide with overflow");
172 unsafe { intrinsics::simd_div(self, rhs) }
177 // remainder panics on zero divisor
179 impl<const LANES: usize> core::ops::Rem<Self> for Simd<$scalar, LANES>
181 LaneCount<LANES>: SupportedLaneCount,
186 fn rem(self, rhs: Self) -> Self::Output {
191 panic!("attempt to calculate the remainder with a divisor of zero");
194 // Guards for rem(MIN, -1)
195 // this branch applies the check only to signed ints
196 if <$scalar>::MIN != 0 && self.as_array().iter()
197 .zip(rhs.as_array().iter())
198 .any(|(x,y)| *x == <$scalar>::MIN && *y == -1 as _) {
199 panic!("attempt to calculate the remainder with overflow");
201 unsafe { intrinsics::simd_rem(self, rhs) }
206 // shifts panic on overflow
208 impl<const LANES: usize> core::ops::Shl<Self> for Simd<$scalar, LANES>
210 LaneCount<LANES>: SupportedLaneCount,
215 fn shl(self, rhs: Self) -> Self::Output {
216 // TODO there is probably a better way of doing this
220 .any(invalid_shift_rhs)
222 panic!("attempt to shift left with overflow");
224 unsafe { intrinsics::simd_shl(self, rhs) }
230 impl<const LANES: usize> core::ops::Shr<Self> for Simd<$scalar, LANES>
232 LaneCount<LANES>: SupportedLaneCount,
237 fn shr(self, rhs: Self) -> Self::Output {
238 // TODO there is probably a better way of doing this
242 .any(invalid_shift_rhs)
244 panic!("attempt to shift with overflow");
246 unsafe { intrinsics::simd_shr(self, rhs) }
254 /// Implements unsigned integer operators for the provided types.
255 macro_rules! impl_signed_int_ops {
256 { $($scalar:ty),* } => {
257 impl_unsigned_int_ops! { $($scalar),* }
261 impl_unsigned_int_ops! { u8, u16, u32, u64, usize }
262 impl_signed_int_ops! { i8, i16, i32, i64, isize }
263 impl_float_ops! { f32, f64 }