diff options
| author | Jubilee Young <workingjubilee@gmail.com> | 2021-12-08 18:42:06 -0800 |
|---|---|---|
| committer | Jubilee Young <workingjubilee@gmail.com> | 2021-12-09 13:23:45 -0800 |
| commit | 5dcd397f47a17aec3b049af2d7541530b859e47b (patch) | |
| tree | 57401164ef99af0566f2daed4c8c1e1a90010361 | |
| parent | 049e8ca7f7fc42501b98afcb9c32fd51080bd75a (diff) | |
| download | rust-5dcd397f47a17aec3b049af2d7541530b859e47b.tar.gz rust-5dcd397f47a17aec3b049af2d7541530b859e47b.zip | |
Finish refactoring ints in ops.rs
This should perform a SIMD check for whether or not we can div/rem, so that we can panic several times faster!
| -rw-r--r-- | crates/core_simd/src/ops.rs | 271 |
1 files changed, 147 insertions, 124 deletions
diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index 65b461d3981..e6d7e695391 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -1,5 +1,4 @@ -use crate::simd::intrinsics; -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{LaneCount, Mask, Simd, SimdElement, SupportedLaneCount}; use core::ops::{Add, Mul}; use core::ops::{BitAnd, BitOr, BitXor}; use core::ops::{Div, Rem, Sub}; @@ -284,145 +283,169 @@ float_arith! { } } -/// Automatically implements operators over references in addition to the provided operator. -macro_rules! impl_ref_ops { - // binary op - { - impl<const $lanes:ident: usize> core::ops::$trait:ident<$rhs:ty> for $type:ty - where - LaneCount<$lanes2:ident>: SupportedLaneCount, - { - type Output = $output:ty; - - $(#[$attrs:meta])* - fn $fn:ident($self_tok:ident, $rhs_arg:ident: $rhs_arg_ty:ty) -> Self::Output $body:tt +// Division by zero is poison, according to LLVM. +// So is dividing the MIN value of a signed integer by -1, +// since that would return MAX + 1. +// FIXME: Rust allows <SInt>::MIN / -1, +// so we should probably figure out how to make that safe. +macro_rules! int_divrem_guard { + ($(impl<const LANES: usize> $op:ident for Simd<$sint:ty, LANES> { + const PANIC_ZERO: &'static str = $zero:literal; + const PANIC_OVERFLOW: &'static str = $overflow:literal; + fn $call:ident { + unsafe { $simd_call:ident } } - } => { - impl<const $lanes: usize> core::ops::$trait<$rhs> for $type + })*) => { + $(impl<const LANES: usize> $op for Simd<$sint, LANES> where - LaneCount<$lanes2>: SupportedLaneCount, + $sint: SimdElement, + LaneCount<LANES>: SupportedLaneCount, { - type Output = $output; - - $(#[$attrs])* - fn $fn($self_tok, $rhs_arg: $rhs_arg_ty) -> Self::Output $body - } + type Output = Self; + #[inline] + #[must_use = "operator returns a new vector without mutating the inputs"] + fn $call(self, rhs: Self) -> Self::Output { + if rhs.lanes_eq(Simd::splat(0)).any() { + panic!("attempt to calculate the remainder with a divisor of zero"); + } else if <$sint>::MIN != 0 && self.lanes_eq(Simd::splat(<$sint>::MIN)) & rhs.lanes_eq(Simd::splat(-1 as _)) + != Mask::splat(false) + { + panic!("attempt to calculate the remainder with overflow"); + } else { + unsafe { $crate::intrinsics::$simd_call(self, rhs) } + } + } + })* }; } -/// Automatically implements operators over vectors and scalars for a particular vector. -macro_rules! impl_op { - { impl Add for $scalar:ty } => { - impl_op! { @binary $scalar, Add::add, simd_add } - }; - { impl Sub for $scalar:ty } => { - impl_op! { @binary $scalar, Sub::sub, simd_sub } - }; - { impl Mul for $scalar:ty } => { - impl_op! { @binary $scalar, Mul::mul, simd_mul } - }; - { impl Div for $scalar:ty } => { - impl_op! { @binary $scalar, Div::div, simd_div } - }; - { impl Rem for $scalar:ty } => { - impl_op! { @binary $scalar, Rem::rem, simd_rem } - }; +macro_rules! int_arith { + ($(impl<const LANES: usize> IntArith for Simd<$sint:ty, LANES> { + fn add(self, rhs: Self) -> Self::Output; + fn mul(self, rhs: Self) -> Self::Output; + fn sub(self, rhs: Self) -> Self::Output; + fn div(self, rhs: Self) -> Self::Output; + fn rem(self, rhs: Self) -> Self::Output; + })*) => { + $( + unsafe_base_op!{ + impl<const LANES: usize> Add for Simd<$sint, LANES> { + fn add(self, rhs: Self) -> Self::Output { + unsafe { simd_add } + } + } - // generic binary op with assignment when output is `Self` - { @binary $scalar:ty, $trait:ident :: $trait_fn:ident, $intrinsic:ident } => { - impl_ref_ops! { - impl<const LANES: usize> core::ops::$trait<Self> for Simd<$scalar, LANES> - where - LaneCount<LANES>: SupportedLaneCount, - { - type Output = Self; + impl<const LANES: usize> Mul for Simd<$sint, LANES> { + fn mul(self, rhs: Self) -> Self::Output { + unsafe { simd_mul } + } + } - #[inline] - fn $trait_fn(self, rhs: Self) -> Self::Output { - unsafe { - intrinsics::$intrinsic(self, rhs) - } + impl<const LANES: usize> Sub for Simd<$sint, LANES> { + fn sub(self, rhs: Self) -> Self::Output { + unsafe { simd_sub } } } } - }; -} -/// Implements unsigned integer operators for the provided types. -macro_rules! impl_unsigned_int_ops { - { $($scalar:ty),* } => { - $( - impl_op! { impl Add for $scalar } - impl_op! { impl Sub for $scalar } - impl_op! { impl Mul for $scalar } - - // Integers panic on divide by 0 - impl_ref_ops! { - impl<const LANES: usize> core::ops::Div<Self> for Simd<$scalar, LANES> - where - LaneCount<LANES>: SupportedLaneCount, - { - type Output = Self; - - #[inline] - fn div(self, rhs: Self) -> Self::Output { - if rhs.as_array() - .iter() - .any(|x| *x == 0) - { - panic!("attempt to divide by zero"); - } - - // Guards for div(MIN, -1), - // this check only applies to signed ints - if <$scalar>::MIN != 0 && self.as_array().iter() - .zip(rhs.as_array().iter()) - .any(|(x,y)| *x == <$scalar>::MIN && *y == -1 as _) { - panic!("attempt to divide with overflow"); - } - unsafe { intrinsics::simd_div(self, rhs) } - } + int_divrem_guard!{ + impl<const LANES: usize> Div for Simd<$sint, LANES> { + const PANIC_ZERO: &'static str = "attempt to divide by zero"; + const PANIC_OVERFLOW: &'static str = "attempt to divide with overflow"; + fn div { + unsafe { simd_div } } } - // remainder panics on zero divisor - impl_ref_ops! { - impl<const LANES: usize> core::ops::Rem<Self> for Simd<$scalar, LANES> - where - LaneCount<LANES>: SupportedLaneCount, - { - type Output = Self; - - #[inline] - fn rem(self, rhs: Self) -> Self::Output { - if rhs.as_array() - .iter() - .any(|x| *x == 0) - { - panic!("attempt to calculate the remainder with a divisor of zero"); - } - - // Guards for rem(MIN, -1) - // this branch applies the check only to signed ints - if <$scalar>::MIN != 0 && self.as_array().iter() - .zip(rhs.as_array().iter()) - .any(|(x,y)| *x == <$scalar>::MIN && *y == -1 as _) { - panic!("attempt to calculate the remainder with overflow"); - } - unsafe { intrinsics::simd_rem(self, rhs) } - } + impl<const LANES: usize> Rem for Simd<$sint, LANES> { + const PANIC_ZERO: &'static str = "attempt to calculate the remainder with a divisor of zero"; + const PANIC_OVERFLOW: &'static str = "attempt to calculate the remainder with overflow"; + fn rem { + unsafe { simd_rem } } } - )* - }; + })* + } } -/// Implements unsigned integer operators for the provided types. -macro_rules! impl_signed_int_ops { - { $($scalar:ty),* } => { - impl_unsigned_int_ops! { $($scalar),* } - }; -} +int_arith! { + impl<const LANES: usize> IntArith for Simd<i8, LANES> { + fn add(self, rhs: Self) -> Self::Output; + fn mul(self, rhs: Self) -> Self::Output; + fn sub(self, rhs: Self) -> Self::Output; + fn div(self, rhs: Self) -> Self::Output; + fn rem(self, rhs: Self) -> Self::Output; + } -impl_unsigned_int_ops! { u8, u16, u32, u64, usize } -impl_signed_int_ops! { i8, i16, i32, i64, isize } + impl<const LANES: usize> IntArith for Simd<i16, LANES> { + fn add(self, rhs: Self) -> Self::Output; + fn mul(self, rhs: Self) -> Self::Output; + fn sub(self, rhs: Self) -> Self::Output; + fn div(self, rhs: Self) -> Self::Output; + fn rem(self, rhs: Self) -> Self::Output; + } + + impl<const LANES: usize> IntArith for Simd<i32, LANES> { + fn add(self, rhs: Self) -> Self::Output; + fn mul(self, rhs: Self) -> Self::Output; + fn sub(self, rhs: Self) -> Self::Output; + fn div(self, rhs: Self) -> Self::Output; + fn rem(self, rhs: Self) -> Self::Output; + } + + impl<const LANES: usize> IntArith for Simd<i64, LANES> { + fn add(self, rhs: Self) -> Self::Output; + fn mul(self, rhs: Self) -> Self::Output; + fn sub(self, rhs: Self) -> Self::Output; + fn div(self, rhs: Self) -> Self::Output; + fn rem(self, rhs: Self) -> Self::Output; + } + + impl<const LANES: usize> IntArith for Simd<isize, LANES> { + fn add(self, rhs: Self) -> Self::Output; + fn mul(self, rhs: Self) -> Self::Output; + fn sub(self, rhs: Self) -> Self::Output; + fn div(self, rhs: Self) -> Self::Output; + fn rem(self, rhs: Self) -> Self::Output; + } + + impl<const LANES: usize> IntArith for Simd<u8, LANES> { + fn add(self, rhs: Self) -> Self::Output; + fn mul(self, rhs: Self) -> Self::Output; + fn sub(self, rhs: Self) -> Self::Output; + fn div(self, rhs: Self) -> Self::Output; + fn rem(self, rhs: Self) -> Self::Output; + } + + impl<const LANES: usize> IntArith for Simd<u16, LANES> { + fn add(self, rhs: Self) -> Self::Output; + fn mul(self, rhs: Self) -> Self::Output; + fn sub(self, rhs: Self) -> Self::Output; + fn div(self, rhs: Self) -> Self::Output; + fn rem(self, rhs: Self) -> Self::Output; + } + + impl<const LANES: usize> IntArith for Simd<u32, LANES> { + fn add(self, rhs: Self) -> Self::Output; + fn mul(self, rhs: Self) -> Self::Output; + fn sub(self, rhs: Self) -> Self::Output; + fn div(self, rhs: Self) -> Self::Output; + fn rem(self, rhs: Self) -> Self::Output; + } + + impl<const LANES: usize> IntArith for Simd<u64, LANES> { + fn add(self, rhs: Self) -> Self::Output; + fn mul(self, rhs: Self) -> Self::Output; + fn sub(self, rhs: Self) -> Self::Output; + fn div(self, rhs: Self) -> Self::Output; + fn rem(self, rhs: Self) -> Self::Output; + } + + impl<const LANES: usize> IntArith for Simd<usize, LANES> { + fn add(self, rhs: Self) -> Self::Output; + fn mul(self, rhs: Self) -> Self::Output; + fn sub(self, rhs: Self) -> Self::Output; + fn div(self, rhs: Self) -> Self::Output; + fn rem(self, rhs: Self) -> Self::Output; + } +} |
