diff --git a/coresimd/ppsv/api/mod.rs b/coresimd/ppsv/api/mod.rs index f2441fe832..5851fc8709 100644 --- a/coresimd/ppsv/api/mod.rs +++ b/coresimd/ppsv/api/mod.rs @@ -127,6 +127,8 @@ mod shifts; #[macro_use] mod rotates; +pub use self::rotates::Rotate; + /// Sealed trait used for constraining select implementations. pub trait Lanes {} diff --git a/coresimd/ppsv/api/rotates.rs b/coresimd/ppsv/api/rotates.rs index 940f6aea25..6e50d2333c 100644 --- a/coresimd/ppsv/api/rotates.rs +++ b/coresimd/ppsv/api/rotates.rs @@ -1,39 +1,59 @@ //! Implements integer rotates. #![allow(unused)] +/// Trait used for overloading rotates +pub trait Rotate { + /// Shifts the bits of each lane to the left by the specified amount in + /// the corresponding lane of `n`, wrapping the truncated bits to + /// the end of the resulting integer. + /// + /// Please note this isn't the same operation as `<<`!. Also note it + /// isn't equivalent to `slice::rotate_left`, it doesn't move the vector's + /// lanes around. (that can be implemented with vector shuffles). + fn rotate_left(self, n: T) -> Self; + + /// Shifts the bits of each lane to the right by the specified amount in + /// the corresponding lane of `n`, wrapping the truncated bits to + /// the beginning of the resulting integer. + /// + /// Please note this isn't the same operation as `>>`!. Also note it + /// isn't similar to `slice::rotate_right`, it doesn't move the vector's + /// lanes around. (that can be implemented with vector shuffles). + fn rotate_right(self, n: T) -> Self; +} + macro_rules! impl_vector_rotates { ($id:ident, $elem_ty:ident) => { - impl $id { - /// Shifts the bits of each lane to the left by the specified amount in - /// the corresponding lane of `n`, wrapping the truncated bits to - /// the end of the resulting integer. - /// - /// Please note this isn't the same operation as `<<`!. Also note it - /// isn't equivalent to `slice::rotate_left` (that can be implemented - /// with vector shuffles). + impl super::api::Rotate<$id> for $id { #[inline] - pub fn rotate_left(self, n: $id) -> $id { + fn rotate_left(self, n: $id) -> $id { const LANE_WIDTH: $elem_ty = ::mem::size_of::<$elem_ty>() as $elem_ty * 8; // Protect against undefined behavior for over-long bit shifts let n = n % LANE_WIDTH; (self << n) | (self >> ((LANE_WIDTH - n) % LANE_WIDTH)) } - /// Shifts the bits of each lane to the right by the specified amount in - /// the corresponding lane of `n`, wrapping the truncated bits to - /// the beginning of the resulting integer. - /// - /// Please note this isn't the same operation as `>>`!. Also note it - /// isn't similar to `slice::rotate_right`, it doesn't move the vector's - /// lanes around. (that can be implemented with vector shuffles). #[inline] - pub fn rotate_right(self, n: $id) -> $id { + fn rotate_right(self, n: $id) -> $id { const LANE_WIDTH: $elem_ty = ::mem::size_of::<$elem_ty>() as $elem_ty * 8; // Protect against undefined behavior for over-long bit shifts let n = n % LANE_WIDTH; (self >> n) | (self << ((LANE_WIDTH - n) % LANE_WIDTH)) } } + + impl super::api::Rotate<$elem_ty> for $id { + #[inline] + fn rotate_left(self, n: $elem_ty) -> $id { + self.rotate_left($id::splat(n)) + } + + + #[inline] + fn rotate_right(self, n: $elem_ty) -> $id { + self.rotate_right($id::splat(n)) + } + } }; } @@ -43,6 +63,7 @@ macro_rules! test_vector_rotate_ops { #[test] fn rotate_ops() { use coresimd::simd::$id; + use coresimd::simd::Rotate;; use std::mem; let z = $id::splat(0 as $elem_ty); let o = $id::splat(1 as $elem_ty); diff --git a/coresimd/ppsv/mod.rs b/coresimd/ppsv/mod.rs index 4d5c92dad0..8d311604a7 100644 --- a/coresimd/ppsv/mod.rs +++ b/coresimd/ppsv/mod.rs @@ -48,6 +48,8 @@ pub use self::v32::*; pub use self::v512::*; pub use self::v64::*; +pub use self::api::Rotate; + /// Safe lossless bitwise conversion from `T` to `Self`. pub trait FromBits: ::marker::Sized { /// Safe lossless bitwise from `T` to `Self`. diff --git a/crates/coresimd/tests/rotate_tests.rs b/crates/coresimd/tests/rotate_tests.rs new file mode 100644 index 0000000000..39e8cfa82a --- /dev/null +++ b/crates/coresimd/tests/rotate_tests.rs @@ -0,0 +1,44 @@ +//! rotate instruction tests + +#![feature(stdsimd)] +#![feature(proc_macro)] +#![feature(avx512_target_feature)] +#![feature(abi_vectorcall)] + +#[macro_use] +// extern crate assert_instr_macro; +extern crate stdsimd_test; +extern crate stdsimd; + +use stdsimd::simd::*; +use stdsimd_test::assert_instr; + +// Verify that supported hardware compiles rotates into single instructions + +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vprorvq))] +unsafe fn rotate_right_variable(x: u64x8, y: u64x8) -> u64x8 { + x.rotate_right(y) +} + +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vprolvq))] +unsafe fn rotate_left_variable(x: u64x8, y: u64x8) -> u64x8 { + x.rotate_left(y) +} + +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vprorq))] +unsafe fn rotate_right(x: u64x8, y: u64) -> u64x8 { + x.rotate_right(y) +} + +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vprolq))] +unsafe fn rotate_left(x: u64x8, y: u64) -> u64x8 { + x.rotate_left(y) +}