diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index 2ebcef3d8297..5e775d6ca138 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -32,6 +32,29 @@ where } } +macro_rules! unsafe_base_op { + ($(impl $op:ident for Simd<$scalar:ty, LANES> { + fn $call:ident(self, rhs: Self) -> Self::Output { + unsafe{ $simd_call:ident } + } + })*) => { + $(impl $op for Simd<$scalar, LANES> + where + $scalar: SimdElement, + LaneCount: SupportedLaneCount, + { + type Output = Self; + + #[inline] + #[must_use = "operator returns a new vector without mutating the inputs"] + fn $call(self, rhs: Self) -> Self::Output { + unsafe { $crate::intrinsics::$simd_call(self, rhs) } + } + } + )* + } +} + /// SAFETY: This macro should not be used for anything except Shl or Shr, and passed the appropriate shift intrinsic. /// It handles performing a bitand in addition to calling the shift operator, so that the result /// is well-defined: LLVM can return a poison value if you shl, lshr, or ashr if rhs >= ::BITS @@ -41,13 +64,13 @@ where /// // FIXME: Consider implementing this in cg_llvm instead? // cg_clif defaults to this, and scalar MIR shifts also default to wrapping -macro_rules! wrap_bitshift_inner { - (impl $op:ident for Simd<$int:ty, LANES> { +macro_rules! wrap_bitshift { + ($(impl $op:ident for Simd<$int:ty, LANES> { fn $call:ident(self, rhs: Self) -> Self::Output { unsafe { $simd_call:ident } } - }) => { - impl $op for Simd<$int, LANES> + })*) => { + $(impl $op for Simd<$int, LANES> where $int: SimdElement, LaneCount: SupportedLaneCount, @@ -61,24 +84,45 @@ macro_rules! wrap_bitshift_inner { $crate::intrinsics::$simd_call(self, rhs.bitand(Simd::splat(<$int>::BITS as $int - 1))) } } - } + })* }; } -macro_rules! wrap_bitshifts { - ($(impl ShiftOps for Simd<$int:ty, LANES> { +macro_rules! bitops { + ($(impl BitOps for Simd<$int:ty, LANES> { + fn bitand(self, rhs: Self) -> Self::Output; + fn bitor(self, rhs: Self) -> Self::Output; + fn bitxor(self, rhs: Self) -> Self::Output; fn shl(self, rhs: Self) -> Self::Output; fn shr(self, rhs: Self) -> Self::Output; })*) => { $( - wrap_bitshift_inner! { + unsafe_base_op!{ + impl BitAnd for Simd<$int, LANES> { + fn bitand(self, rhs: Self) -> Self::Output { + unsafe { simd_and } + } + } + + impl BitOr for Simd<$int, LANES> { + fn bitor(self, rhs: Self) -> Self::Output { + unsafe { simd_or } + } + } + + impl BitXor for Simd<$int, LANES> { + fn bitxor(self, rhs: Self) -> Self::Output { + unsafe { simd_xor } + } + } + } + wrap_bitshift! { impl Shl for Simd<$int, LANES> { fn shl(self, rhs: Self) -> Self::Output { unsafe { simd_shl } } } - } - wrap_bitshift_inner! { + impl Shr for Simd<$int, LANES> { fn shr(self, rhs: Self) -> Self::Output { // This automatically monomorphizes to lshr or ashr, depending, @@ -91,53 +135,86 @@ macro_rules! wrap_bitshifts { }; } -wrap_bitshifts! { - impl ShiftOps for Simd { +// Integers can always accept bitand, bitor, and bitxor. +// The only question is how to handle shifts >= ::BITS? +// Our current solution uses wrapping logic. +bitops! { + impl BitOps for Simd { + fn bitand(self, rhs: Self) -> Self::Output; + fn bitor(self, rhs: Self) -> Self::Output; + fn bitxor(self, rhs: Self) -> Self::Output; fn shl(self, rhs: Self) -> Self::Output; fn shr(self, rhs: Self) -> Self::Output; } - impl ShiftOps for Simd { + impl BitOps for Simd { + fn bitand(self, rhs: Self) -> Self::Output; + fn bitor(self, rhs: Self) -> Self::Output; + fn bitxor(self, rhs: Self) -> Self::Output; fn shl(self, rhs: Self) -> Self::Output; fn shr(self, rhs: Self) -> Self::Output; } - impl ShiftOps for Simd { + impl BitOps for Simd { + fn bitand(self, rhs: Self) -> Self::Output; + fn bitor(self, rhs: Self) -> Self::Output; + fn bitxor(self, rhs: Self) -> Self::Output; fn shl(self, rhs: Self) -> Self::Output; fn shr(self, rhs: Self) -> Self::Output; } - impl ShiftOps for Simd { + impl BitOps for Simd { + fn bitand(self, rhs: Self) -> Self::Output; + fn bitor(self, rhs: Self) -> Self::Output; + fn bitxor(self, rhs: Self) -> Self::Output; fn shl(self, rhs: Self) -> Self::Output; fn shr(self, rhs: Self) -> Self::Output; } - impl ShiftOps for Simd { + impl BitOps for Simd { + fn bitand(self, rhs: Self) -> Self::Output; + fn bitor(self, rhs: Self) -> Self::Output; + fn bitxor(self, rhs: Self) -> Self::Output; fn shl(self, rhs: Self) -> Self::Output; fn shr(self, rhs: Self) -> Self::Output; } - impl ShiftOps for Simd { + impl BitOps for Simd { + fn bitand(self, rhs: Self) -> Self::Output; + fn bitor(self, rhs: Self) -> Self::Output; + fn bitxor(self, rhs: Self) -> Self::Output; fn shl(self, rhs: Self) -> Self::Output; fn shr(self, rhs: Self) -> Self::Output; } - impl ShiftOps for Simd { + impl BitOps for Simd { + fn bitand(self, rhs: Self) -> Self::Output; + fn bitor(self, rhs: Self) -> Self::Output; + fn bitxor(self, rhs: Self) -> Self::Output; fn shl(self, rhs: Self) -> Self::Output; fn shr(self, rhs: Self) -> Self::Output; } - impl ShiftOps for Simd { + impl BitOps for Simd { + fn bitand(self, rhs: Self) -> Self::Output; + fn bitor(self, rhs: Self) -> Self::Output; + fn bitxor(self, rhs: Self) -> Self::Output; fn shl(self, rhs: Self) -> Self::Output; fn shr(self, rhs: Self) -> Self::Output; } - impl ShiftOps for Simd { + impl BitOps for Simd { + fn bitand(self, rhs: Self) -> Self::Output; + fn bitor(self, rhs: Self) -> Self::Output; + fn bitxor(self, rhs: Self) -> Self::Output; fn shl(self, rhs: Self) -> Self::Output; fn shr(self, rhs: Self) -> Self::Output; } - impl ShiftOps for Simd { + impl BitOps for Simd { + fn bitand(self, rhs: Self) -> Self::Output; + fn bitor(self, rhs: Self) -> Self::Output; + fn bitxor(self, rhs: Self) -> Self::Output; fn shl(self, rhs: Self) -> Self::Output; fn shr(self, rhs: Self) -> Self::Output; } @@ -186,15 +263,6 @@ macro_rules! impl_op { { impl Rem for $scalar:ty } => { impl_op! { @binary $scalar, Rem::rem, simd_rem } }; - { impl BitAnd for $scalar:ty } => { - impl_op! { @binary $scalar, BitAnd::bitand, simd_and } - }; - { impl BitOr for $scalar:ty } => { - impl_op! { @binary $scalar, BitOr::bitor, simd_or } - }; - { impl BitXor for $scalar:ty } => { - impl_op! { @binary $scalar, BitXor::bitxor, simd_xor } - }; // generic binary op with assignment when output is `Self` { @binary $scalar:ty, $trait:ident :: $trait_fn:ident, $intrinsic:ident } => { @@ -236,9 +304,6 @@ macro_rules! impl_unsigned_int_ops { impl_op! { impl Add for $scalar } impl_op! { impl Sub for $scalar } impl_op! { impl Mul for $scalar } - impl_op! { impl BitAnd for $scalar } - impl_op! { impl BitOr for $scalar } - impl_op! { impl BitXor for $scalar } // Integers panic on divide by 0 impl_ref_ops! {