From 977f26f692790fa2d024e9f3d726d34c0fd3616d Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sat, 17 Apr 2021 17:00:14 +0000 Subject: [PATCH 1/3] Add some common shuffles --- crates/core_simd/src/permute.rs | 83 +++++++++++++++++++++++++++++++ crates/core_simd/tests/permute.rs | 20 ++++++++ 2 files changed, 103 insertions(+) diff --git a/crates/core_simd/src/permute.rs b/crates/core_simd/src/permute.rs index b27b0a9e141..4f7ffd3a955 100644 --- a/crates/core_simd/src/permute.rs +++ b/crates/core_simd/src/permute.rs @@ -13,6 +13,89 @@ impl $name<$n> { pub fn shuffle(self, second: Self) -> Self { unsafe { crate::intrinsics::$fn(self, second, IDX) } } + + /// Reverse the order of the lanes in the vector. + #[inline] + pub fn reverse(self) -> Self { + const fn idx() -> [u32; $n] { + let mut idx = [0u32; $n]; + let mut i = 0; + while i < $n { + idx[i] = ($n - i - 1) as u32; + i += 1; + } + idx + } + self.shuffle::<{ idx() }>(self) + } + + /// Interleave two vectors. + /// + /// The even lanes of the first result contain the lower half of `self`, and the odd + /// lanes contain the lower half of `other`. + /// + /// The even lanes of the second result contain the upper half of `self`, and the odd + /// lanes contain the upper half of `other`. + #[inline] + pub fn interleave(self, other: Self) -> (Self, Self) { + const fn lo() -> [u32; $n] { + let mut idx = [0u32; $n]; + let mut i = 0; + while i < $n { + let offset = i / 2; + idx[i] = if i % 2 == 0 { + offset + } else { + $n + offset + } as u32; + i += 1; + } + idx + } + const fn hi() -> [u32; $n] { + let mut idx = [0u32; $n]; + let mut i = 0; + while i < $n { + let offset = ($n + i) / 2; + idx[i] = if i % 2 == 0 { + offset + } else { + $n + offset + } as u32; + i += 1; + } + idx + } + (self.shuffle::<{ lo() }>(other), self.shuffle::<{ hi() }>(other)) + } + + /// Deinterleave two vectors. + /// + /// The first result contains the even lanes of `self` and `other` concatenated. + /// + /// The second result contains the odd lanes of `self` and `other` concatenated. + #[inline] + pub fn deinterleave(self, other: Self) -> (Self, Self) { + const fn even() -> [u32; $n] { + let mut idx = [0u32; $n]; + let mut i = 0; + while i < $n { + idx[i] = 2 * i as u32; + i += 1; + } + idx + } + const fn odd() -> [u32; $n] { + let mut idx = [0u32; $n]; + let mut i = 0; + while i < $n { + idx[i] = 1 + 2 * i as u32; + i += 1; + } + idx + } + (self.shuffle::<{ even() }>(other), self.shuffle::<{ odd() }>(other)) + } } } } diff --git a/crates/core_simd/tests/permute.rs b/crates/core_simd/tests/permute.rs index 1c6c391d8d0..2be43c9cf3c 100644 --- a/crates/core_simd/tests/permute.rs +++ b/crates/core_simd/tests/permute.rs @@ -13,3 +13,23 @@ fn simple_shuffle() { let b = a; assert_eq!(a.shuffle::<{ [3, 1, 4, 6] }>(b).to_array(), [9, 4, 2, 1]); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn reverse() { + let a = SimdU32::from_array([0, 1, 2, 3, 4, 5, 6, 7]); + assert_eq!(a.reverse().to_array(), [7, 6, 5, 4, 3, 2, 1, 0]); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn interleave() { + let a = SimdU32::from_array([0, 1, 2, 3, 4, 5, 6, 7]); + let b = SimdU32::from_array([8, 9, 10, 11, 12, 13, 14, 15]); + let (lo, hi) = a.interleave(b); + assert_eq!(lo.to_array(), [0, 8, 1, 9, 2, 10, 3, 11]); + assert_eq!(hi.to_array(), [4, 12, 5, 13, 6, 14, 7, 15]); + let (even, odd) = lo.deinterleave(hi); + assert_eq!(even, a); + assert_eq!(odd, b); +} From 1999c54890d57aec3432335715ce29ec87abfeda Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sat, 17 Apr 2021 15:21:25 -0400 Subject: [PATCH 2/3] Clarify concatenation order Co-authored-by: Jubilee <46493976+workingjubilee@users.noreply.github.com> --- crates/core_simd/src/permute.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/permute.rs b/crates/core_simd/src/permute.rs index 4f7ffd3a955..ba14a019a39 100644 --- a/crates/core_simd/src/permute.rs +++ b/crates/core_simd/src/permute.rs @@ -71,9 +71,9 @@ pub fn interleave(self, other: Self) -> (Self, Self) { /// Deinterleave two vectors. /// - /// The first result contains the even lanes of `self` and `other` concatenated. + /// The first result contains the even lanes of `self` and then `other`, concatenated. /// - /// The second result contains the odd lanes of `self` and `other` concatenated. + /// The second result contains the odd lanes of `self` and then `other`, concatenated. #[inline] pub fn deinterleave(self, other: Self) -> (Self, Self) { const fn even() -> [u32; $n] { From 7028a5829464be5b0087afc7aedfff064b2e545f Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Tue, 20 Apr 2021 01:51:06 +0000 Subject: [PATCH 3/3] Attempt to clarify interleave/deinterleave --- crates/core_simd/src/permute.rs | 38 +++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/crates/core_simd/src/permute.rs b/crates/core_simd/src/permute.rs index ba14a019a39..dd63c69c63d 100644 --- a/crates/core_simd/src/permute.rs +++ b/crates/core_simd/src/permute.rs @@ -31,11 +31,24 @@ pub fn reverse(self) -> Self { /// Interleave two vectors. /// - /// The even lanes of the first result contain the lower half of `self`, and the odd - /// lanes contain the lower half of `other`. + /// Produces two vectors with lanes taken alternately from `self` and `other`. /// - /// The even lanes of the second result contain the upper half of `self`, and the odd - /// lanes contain the upper half of `other`. + /// The first result contains the first `LANES / 2` lanes from `self` and `other`, + /// alternating, starting with the first lane of `self`. + /// + /// The second result contains the last `LANES / 2` lanes from `self` and `other`, + /// alternating, starting with the lane `LANES / 2` from the start of `self`. + /// + /// This particular permutation is efficient on many architectures. + /// + /// ``` + /// # use core_simd::SimdU32; + /// let a = SimdU32::from_array([0, 1, 2, 3]); + /// let b = SimdU32::from_array([4, 5, 6, 7]); + /// let (x, y) = a.interleave(b); + /// assert_eq!(x.to_array(), [0, 4, 1, 5]); + /// assert_eq!(y.to_array(), [2, 6, 3, 7]); + /// ``` #[inline] pub fn interleave(self, other: Self) -> (Self, Self) { const fn lo() -> [u32; $n] { @@ -71,9 +84,22 @@ pub fn interleave(self, other: Self) -> (Self, Self) { /// Deinterleave two vectors. /// - /// The first result contains the even lanes of `self` and then `other`, concatenated. + /// The first result takes every other lane of `self` and then `other`, starting with + /// the first lane. /// - /// The second result contains the odd lanes of `self` and then `other`, concatenated. + /// The second result takes every other lane of `self` and then `other`, starting with + /// the second lane. + /// + /// This particular permutation is efficient on many architectures. + /// + /// ``` + /// # use core_simd::SimdU32; + /// let a = SimdU32::from_array([0, 4, 1, 5]); + /// let b = SimdU32::from_array([2, 6, 3, 7]); + /// let (x, y) = a.deinterleave(b); + /// assert_eq!(x.to_array(), [0, 1, 2, 3]); + /// assert_eq!(y.to_array(), [4, 5, 6, 7]); + /// ``` #[inline] pub fn deinterleave(self, other: Self) -> (Self, Self) { const fn even() -> [u32; $n] {