1 use crate::simd::intrinsics;
2 use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
4 impl<I, T, const LANES: usize> core::ops::Index<I> for Simd<T, LANES>
7 LaneCount<LANES>: SupportedLaneCount,
8 I: core::slice::SliceIndex<[T]>,
10 type Output = I::Output;
11 fn index(&self, index: I) -> &Self::Output {
12 &self.as_array()[index]
16 impl<I, T, const LANES: usize> core::ops::IndexMut<I> for Simd<T, LANES>
19 LaneCount<LANES>: SupportedLaneCount,
20 I: core::slice::SliceIndex<[T]>,
22 fn index_mut(&mut self, index: I) -> &mut Self::Output {
23 &mut self.as_mut_array()[index]
27 /// Checks if the right-hand side argument of a left- or right-shift would cause overflow.
28 fn invalid_shift_rhs<T>(rhs: T) -> bool
30 T: Default + PartialOrd + core::convert::TryFrom<usize>,
31 <T as core::convert::TryFrom<usize>>::Error: core::fmt::Debug,
33 let bits_in_type = T::try_from(8 * core::mem::size_of::<T>()).unwrap();
34 rhs < T::default() || rhs >= bits_in_type
37 /// Automatically implements operators over references in addition to the provided operator.
38 macro_rules! impl_ref_ops {
41 impl<const $lanes:ident: usize> core::ops::$trait:ident<$rhs:ty> for $type:ty
43 LaneCount<$lanes2:ident>: SupportedLaneCount,
45 type Output = $output:ty;
48 fn $fn:ident($self_tok:ident, $rhs_arg:ident: $rhs_arg_ty:ty) -> Self::Output $body:tt
51 impl<const $lanes: usize> core::ops::$trait<$rhs> for $type
53 LaneCount<$lanes2>: SupportedLaneCount,
55 type Output = $output;
58 fn $fn($self_tok, $rhs_arg: $rhs_arg_ty) -> Self::Output $body
61 impl<const $lanes: usize> core::ops::$trait<&'_ $rhs> for $type
63 LaneCount<$lanes2>: SupportedLaneCount,
65 type Output = <$type as core::ops::$trait<$rhs>>::Output;
68 fn $fn($self_tok, $rhs_arg: &$rhs) -> Self::Output {
69 core::ops::$trait::$fn($self_tok, *$rhs_arg)
73 impl<const $lanes: usize> core::ops::$trait<$rhs> for &'_ $type
75 LaneCount<$lanes2>: SupportedLaneCount,
77 type Output = <$type as core::ops::$trait<$rhs>>::Output;
80 fn $fn($self_tok, $rhs_arg: $rhs) -> Self::Output {
81 core::ops::$trait::$fn(*$self_tok, $rhs_arg)
85 impl<const $lanes: usize> core::ops::$trait<&'_ $rhs> for &'_ $type
87 LaneCount<$lanes2>: SupportedLaneCount,
89 type Output = <$type as core::ops::$trait<$rhs>>::Output;
92 fn $fn($self_tok, $rhs_arg: &$rhs) -> Self::Output {
93 core::ops::$trait::$fn(*$self_tok, *$rhs_arg)
98 // binary assignment op
100 impl<const $lanes:ident: usize> core::ops::$trait:ident<$rhs:ty> for $type:ty
102 LaneCount<$lanes2:ident>: SupportedLaneCount,
105 fn $fn:ident(&mut $self_tok:ident, $rhs_arg:ident: $rhs_arg_ty:ty) $body:tt
108 impl<const $lanes: usize> core::ops::$trait<$rhs> for $type
110 LaneCount<$lanes2>: SupportedLaneCount,
113 fn $fn(&mut $self_tok, $rhs_arg: $rhs_arg_ty) $body
116 impl<const $lanes: usize> core::ops::$trait<&'_ $rhs> for $type
118 LaneCount<$lanes2>: SupportedLaneCount,
121 fn $fn(&mut $self_tok, $rhs_arg: &$rhs_arg_ty) {
122 core::ops::$trait::$fn($self_tok, *$rhs_arg)
129 impl<const $lanes:ident: usize> core::ops::$trait:ident for $type:ty
131 LaneCount<$lanes2:ident>: SupportedLaneCount,
133 type Output = $output:ty;
134 fn $fn:ident($self_tok:ident) -> Self::Output $body:tt
137 impl<const $lanes: usize> core::ops::$trait for $type
139 LaneCount<$lanes2>: SupportedLaneCount,
141 type Output = $output;
142 fn $fn($self_tok) -> Self::Output $body
145 impl<const $lanes: usize> core::ops::$trait for &'_ $type
147 LaneCount<$lanes2>: SupportedLaneCount,
149 type Output = <$type as core::ops::$trait>::Output;
150 fn $fn($self_tok) -> Self::Output {
151 core::ops::$trait::$fn(*$self_tok)
157 /// Automatically implements operators over vectors and scalars for a particular vector.
158 macro_rules! impl_op {
159 { impl Add for $scalar:ty } => {
160 impl_op! { @binary $scalar, Add::add, AddAssign::add_assign, simd_add }
162 { impl Sub for $scalar:ty } => {
163 impl_op! { @binary $scalar, Sub::sub, SubAssign::sub_assign, simd_sub }
165 { impl Mul for $scalar:ty } => {
166 impl_op! { @binary $scalar, Mul::mul, MulAssign::mul_assign, simd_mul }
168 { impl Div for $scalar:ty } => {
169 impl_op! { @binary $scalar, Div::div, DivAssign::div_assign, simd_div }
171 { impl Rem for $scalar:ty } => {
172 impl_op! { @binary $scalar, Rem::rem, RemAssign::rem_assign, simd_rem }
174 { impl Shl for $scalar:ty } => {
175 impl_op! { @binary $scalar, Shl::shl, ShlAssign::shl_assign, simd_shl }
177 { impl Shr for $scalar:ty } => {
178 impl_op! { @binary $scalar, Shr::shr, ShrAssign::shr_assign, simd_shr }
180 { impl BitAnd for $scalar:ty } => {
181 impl_op! { @binary $scalar, BitAnd::bitand, BitAndAssign::bitand_assign, simd_and }
183 { impl BitOr for $scalar:ty } => {
184 impl_op! { @binary $scalar, BitOr::bitor, BitOrAssign::bitor_assign, simd_or }
186 { impl BitXor for $scalar:ty } => {
187 impl_op! { @binary $scalar, BitXor::bitxor, BitXorAssign::bitxor_assign, simd_xor }
190 { impl Not for $scalar:ty } => {
192 impl<const LANES: usize> core::ops::Not for Simd<$scalar, LANES>
194 LaneCount<LANES>: SupportedLaneCount,
197 fn not(self) -> Self::Output {
198 self ^ Self::splat(!<$scalar>::default())
204 { impl Neg for $scalar:ty } => {
206 impl<const LANES: usize> core::ops::Neg for Simd<$scalar, LANES>
208 LaneCount<LANES>: SupportedLaneCount,
211 fn neg(self) -> Self::Output {
212 unsafe { intrinsics::simd_neg(self) }
218 // generic binary op with assignment when output is `Self`
219 { @binary $scalar:ty, $trait:ident :: $trait_fn:ident, $assign_trait:ident :: $assign_trait_fn:ident, $intrinsic:ident } => {
221 impl<const LANES: usize> core::ops::$trait<Self> for Simd<$scalar, LANES>
223 LaneCount<LANES>: SupportedLaneCount,
228 fn $trait_fn(self, rhs: Self) -> Self::Output {
230 intrinsics::$intrinsic(self, rhs)
237 impl<const LANES: usize> core::ops::$trait<$scalar> for Simd<$scalar, LANES>
239 LaneCount<LANES>: SupportedLaneCount,
244 fn $trait_fn(self, rhs: $scalar) -> Self::Output {
245 core::ops::$trait::$trait_fn(self, Self::splat(rhs))
251 impl<const LANES: usize> core::ops::$trait<Simd<$scalar, LANES>> for $scalar
253 LaneCount<LANES>: SupportedLaneCount,
255 type Output = Simd<$scalar, LANES>;
258 fn $trait_fn(self, rhs: Simd<$scalar, LANES>) -> Self::Output {
259 core::ops::$trait::$trait_fn(Simd::splat(self), rhs)
265 impl<const LANES: usize> core::ops::$assign_trait<Self> for Simd<$scalar, LANES>
267 LaneCount<LANES>: SupportedLaneCount,
270 fn $assign_trait_fn(&mut self, rhs: Self) {
272 *self = intrinsics::$intrinsic(*self, rhs);
279 impl<const LANES: usize> core::ops::$assign_trait<$scalar> for Simd<$scalar, LANES>
281 LaneCount<LANES>: SupportedLaneCount,
284 fn $assign_trait_fn(&mut self, rhs: $scalar) {
285 core::ops::$assign_trait::$assign_trait_fn(self, Self::splat(rhs));
292 /// Implements floating-point operators for the provided types.
293 macro_rules! impl_float_ops {
294 { $($scalar:ty),* } => {
296 impl_op! { impl Add for $scalar }
297 impl_op! { impl Sub for $scalar }
298 impl_op! { impl Mul for $scalar }
299 impl_op! { impl Div for $scalar }
300 impl_op! { impl Rem for $scalar }
301 impl_op! { impl Neg for $scalar }
306 /// Implements unsigned integer operators for the provided types.
307 macro_rules! impl_unsigned_int_ops {
308 { $($scalar:ty),* } => {
310 impl_op! { impl Add for $scalar }
311 impl_op! { impl Sub for $scalar }
312 impl_op! { impl Mul for $scalar }
313 impl_op! { impl BitAnd for $scalar }
314 impl_op! { impl BitOr for $scalar }
315 impl_op! { impl BitXor for $scalar }
316 impl_op! { impl Not for $scalar }
318 // Integers panic on divide by 0
320 impl<const LANES: usize> core::ops::Div<Self> for Simd<$scalar, LANES>
322 LaneCount<LANES>: SupportedLaneCount,
327 fn div(self, rhs: Self) -> Self::Output {
332 panic!("attempt to divide by zero");
335 // Guards for div(MIN, -1),
336 // this check only applies to signed ints
337 if <$scalar>::MIN != 0 && self.as_array().iter()
338 .zip(rhs.as_array().iter())
339 .any(|(x,y)| *x == <$scalar>::MIN && *y == -1 as _) {
340 panic!("attempt to divide with overflow");
342 unsafe { intrinsics::simd_div(self, rhs) }
348 impl<const LANES: usize> core::ops::Div<$scalar> for Simd<$scalar, LANES>
350 LaneCount<LANES>: SupportedLaneCount,
355 fn div(self, rhs: $scalar) -> Self::Output {
357 panic!("attempt to divide by zero");
359 if <$scalar>::MIN != 0 &&
360 self.as_array().iter().any(|x| *x == <$scalar>::MIN) &&
362 panic!("attempt to divide with overflow");
364 let rhs = Self::splat(rhs);
365 unsafe { intrinsics::simd_div(self, rhs) }
371 impl<const LANES: usize> core::ops::Div<Simd<$scalar, LANES>> for $scalar
373 LaneCount<LANES>: SupportedLaneCount,
375 type Output = Simd<$scalar, LANES>;
378 fn div(self, rhs: Simd<$scalar, LANES>) -> Self::Output {
379 Simd::splat(self) / rhs
385 impl<const LANES: usize> core::ops::DivAssign<Self> for Simd<$scalar, LANES>
387 LaneCount<LANES>: SupportedLaneCount,
390 fn div_assign(&mut self, rhs: Self) {
397 impl<const LANES: usize> core::ops::DivAssign<$scalar> for Simd<$scalar, LANES>
399 LaneCount<LANES>: SupportedLaneCount,
402 fn div_assign(&mut self, rhs: $scalar) {
408 // remainder panics on zero divisor
410 impl<const LANES: usize> core::ops::Rem<Self> for Simd<$scalar, LANES>
412 LaneCount<LANES>: SupportedLaneCount,
417 fn rem(self, rhs: Self) -> Self::Output {
422 panic!("attempt to calculate the remainder with a divisor of zero");
425 // Guards for rem(MIN, -1)
426 // this branch applies the check only to signed ints
427 if <$scalar>::MIN != 0 && self.as_array().iter()
428 .zip(rhs.as_array().iter())
429 .any(|(x,y)| *x == <$scalar>::MIN && *y == -1 as _) {
430 panic!("attempt to calculate the remainder with overflow");
432 unsafe { intrinsics::simd_rem(self, rhs) }
438 impl<const LANES: usize> core::ops::Rem<$scalar> for Simd<$scalar, LANES>
440 LaneCount<LANES>: SupportedLaneCount,
445 fn rem(self, rhs: $scalar) -> Self::Output {
447 panic!("attempt to calculate the remainder with a divisor of zero");
449 if <$scalar>::MIN != 0 &&
450 self.as_array().iter().any(|x| *x == <$scalar>::MIN) &&
452 panic!("attempt to calculate the remainder with overflow");
454 let rhs = Self::splat(rhs);
455 unsafe { intrinsics::simd_rem(self, rhs) }
461 impl<const LANES: usize> core::ops::Rem<Simd<$scalar, LANES>> for $scalar
463 LaneCount<LANES>: SupportedLaneCount,
465 type Output = Simd<$scalar, LANES>;
468 fn rem(self, rhs: Simd<$scalar, LANES>) -> Self::Output {
469 Simd::splat(self) % rhs
475 impl<const LANES: usize> core::ops::RemAssign<Self> for Simd<$scalar, LANES>
477 LaneCount<LANES>: SupportedLaneCount,
480 fn rem_assign(&mut self, rhs: Self) {
487 impl<const LANES: usize> core::ops::RemAssign<$scalar> for Simd<$scalar, LANES>
489 LaneCount<LANES>: SupportedLaneCount,
492 fn rem_assign(&mut self, rhs: $scalar) {
498 // shifts panic on overflow
500 impl<const LANES: usize> core::ops::Shl<Self> for Simd<$scalar, LANES>
502 LaneCount<LANES>: SupportedLaneCount,
507 fn shl(self, rhs: Self) -> Self::Output {
508 // TODO there is probably a better way of doing this
512 .any(invalid_shift_rhs)
514 panic!("attempt to shift left with overflow");
516 unsafe { intrinsics::simd_shl(self, rhs) }
522 impl<const LANES: usize> core::ops::Shl<$scalar> for Simd<$scalar, LANES>
524 LaneCount<LANES>: SupportedLaneCount,
529 fn shl(self, rhs: $scalar) -> Self::Output {
530 if invalid_shift_rhs(rhs) {
531 panic!("attempt to shift left with overflow");
533 let rhs = Self::splat(rhs);
534 unsafe { intrinsics::simd_shl(self, rhs) }
541 impl<const LANES: usize> core::ops::ShlAssign<Self> for Simd<$scalar, LANES>
543 LaneCount<LANES>: SupportedLaneCount,
546 fn shl_assign(&mut self, rhs: Self) {
547 *self = *self << rhs;
553 impl<const LANES: usize> core::ops::ShlAssign<$scalar> for Simd<$scalar, LANES>
555 LaneCount<LANES>: SupportedLaneCount,
558 fn shl_assign(&mut self, rhs: $scalar) {
559 *self = *self << rhs;
565 impl<const LANES: usize> core::ops::Shr<Self> for Simd<$scalar, LANES>
567 LaneCount<LANES>: SupportedLaneCount,
572 fn shr(self, rhs: Self) -> Self::Output {
573 // TODO there is probably a better way of doing this
577 .any(invalid_shift_rhs)
579 panic!("attempt to shift with overflow");
581 unsafe { intrinsics::simd_shr(self, rhs) }
587 impl<const LANES: usize> core::ops::Shr<$scalar> for Simd<$scalar, LANES>
589 LaneCount<LANES>: SupportedLaneCount,
594 fn shr(self, rhs: $scalar) -> Self::Output {
595 if invalid_shift_rhs(rhs) {
596 panic!("attempt to shift with overflow");
598 let rhs = Self::splat(rhs);
599 unsafe { intrinsics::simd_shr(self, rhs) }
606 impl<const LANES: usize> core::ops::ShrAssign<Self> for Simd<$scalar, LANES>
608 LaneCount<LANES>: SupportedLaneCount,
611 fn shr_assign(&mut self, rhs: Self) {
612 *self = *self >> rhs;
618 impl<const LANES: usize> core::ops::ShrAssign<$scalar> for Simd<$scalar, LANES>
620 LaneCount<LANES>: SupportedLaneCount,
623 fn shr_assign(&mut self, rhs: $scalar) {
624 *self = *self >> rhs;
632 /// Implements unsigned integer operators for the provided types.
633 macro_rules! impl_signed_int_ops {
634 { $($scalar:ty),* } => {
635 impl_unsigned_int_ops! { $($scalar),* }
637 impl_op! { impl Neg for $scalar }
642 impl_unsigned_int_ops! { u8, u16, u32, u64, usize }
643 impl_signed_int_ops! { i8, i16, i32, i64, isize }
644 impl_float_ops! { f32, f64 }