From 71baa3ff2d820903def4bfe14262afced3d67e2f Mon Sep 17 00:00:00 2001 From: Reiner Pope Date: Mon, 13 Mar 2023 05:10:06 +0000 Subject: [PATCH 01/12] Add common swizzles. --- crates/core_simd/src/swizzle.rs | 106 ++++++++++++++++++++++++++++++ crates/core_simd/tests/swizzle.rs | 35 ++++++++++ 2 files changed, 141 insertions(+) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 68f20516cf5..de8c1ea17d7 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -364,4 +364,110 @@ where (Even::swizzle2(self, other), Odd::swizzle2(self, other)) } + + /// Takes a slice of a vector to produce a shorter vector. + /// + /// This is equivalent to computing `&self[OFFSET..OFFSET+LEN]` on + /// the underlying array. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # use core::simd::Simd; + /// let x = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); + /// let y = x.slice::<2, 4>(); + /// assert_eq!(y.to_array(), [2, 3, 4, 5]); + /// ``` + /// + /// Will be rejected at compile time if `OFFSET + LEN > LANES`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn slice(self) -> Simd + where LaneCount: SupportedLaneCount { + const fn slice_index(offset: usize, lanes: usize) -> [usize; LEN] { + assert!(offset + LEN <= lanes, "slice out of bounds"); + let mut index = [0; LEN]; + let mut i = 0; + while i < LEN { + index[i] = i + offset; + i += 1; + } + index + } + struct Slice; + impl Swizzle for Slice { + const INDEX: [usize; LEN] = slice_index::(OFFSET, LANES); + } + Slice::::swizzle(self) + } + + /// Concatenates two vectors of equal length. + /// + /// Due to limitations in const generics, the length of the resulting vector cannot be inferred + /// from the input vectors. You must specify it explicitly. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # use core::simd::Simd; + /// let x = Simd::from_array([0, 1, 2, 3]); + /// let y = Simd::from_array([4, 5, 6, 7]); + /// let z = x.concat_to::<8>(y); + /// assert_eq!(z.to_array(), [0, 1, 2, 3, 4, 5, 6, 7]); + /// ``` + /// + /// Will be rejected at compile time if `LANES * 2 != DOUBLE_LANES`. + pub fn concat_to(self, other: Self) -> Simd + where LaneCount: SupportedLaneCount + { + const fn concat_index(lanes: usize) -> [Which; DOUBLE_LANES] { + assert!(lanes * 2 == DOUBLE_LANES); + let mut index = [Which::First(0); DOUBLE_LANES]; + let mut i = 0; + while i < lanes { + index[i] = Which::First(i); + index[i + lanes] = Which::Second(i); + i += 1; + } + index + } + struct Concat; + impl Swizzle2 for Concat { + const INDEX: [Which; DOUBLE_LANES] = concat_index::(LANES); + } + Concat::swizzle2(self, other) + } + + /// For each lane `i`, swaps it with lane `i ^ SWAP_MASK`. + /// + /// Also known as `grev` in the RISC-V Bitmanip specification, this is a powerful + /// swizzle operation that can implement many common patterns as special cases. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # use core::simd::Simd; + /// let x = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); + /// // Swap adjacent lanes: + /// assert_eq!(x.general_reverse::<1>().to_array(), [1, 0, 3, 2, 5, 4, 7, 6]); + /// // Swap lanes separated by distance 2: + /// assert_eq!(x.general_reverse::<2>().to_array(), [2, 3, 0, 1, 6, 7, 4, 5]); + /// // Swap lanes separated by distance 4: + /// assert_eq!(x.general_reverse::<4>().to_array(), [4, 5, 6, 7, 0, 1, 2, 3]); + /// // Reverse lanes, within each 4-lane group: + /// assert_eq!(x.general_reverse::<3>().to_array(), [3, 2, 1, 0, 7, 6, 5, 4]); + /// ``` + pub fn general_reverse(self) -> Self { + const fn general_reverse_index(swap_mask: usize) -> [usize; LANES] { + let mut index = [0; LANES]; + let mut i = 0; + while i < LANES { + index[i] = i ^ swap_mask; + i += 1; + } + index + } + struct GeneralReverse; + impl Swizzle for GeneralReverse { + const INDEX: [usize; LANES] = general_reverse_index::(DISTANCE); + } + GeneralReverse::::swizzle(self) + } } diff --git a/crates/core_simd/tests/swizzle.rs b/crates/core_simd/tests/swizzle.rs index 8cd7c33e823..8aaefee97b8 100644 --- a/crates/core_simd/tests/swizzle.rs +++ b/crates/core_simd/tests/swizzle.rs @@ -74,3 +74,38 @@ fn interleave_one() { assert_eq!(even, a); assert_eq!(odd, b); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn slice() { + let a = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); + let lo = a.slice::<0, 4>(); + let mid = a.slice::<3, 2>(); + let hi = a.slice::<4, 4>(); + assert_eq!(lo.to_array(), [0, 1, 2, 3]); + assert_eq!(mid.to_array(), [3, 4]); + assert_eq!(hi.to_array(), [4, 5, 6, 7]); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn concat() { + let x = Simd::from_array([0, 1, 2, 3]); + let y = Simd::from_array([4, 5, 6, 7]); + let z = x.concat_to::<8>(y); + assert_eq!(z.to_array(), [0, 1, 2, 3, 4, 5, 6, 7]); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn general_reverse() { + let x = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); + // Swap adjacent lanes: + assert_eq!(x.general_reverse::<1>().to_array(), [1, 0, 3, 2, 5, 4, 7, 6]); + // Swap lanes separated by distance 2: + assert_eq!(x.general_reverse::<2>().to_array(), [2, 3, 0, 1, 6, 7, 4, 5]); + // Swap lanes separated by distance 4: + assert_eq!(x.general_reverse::<4>().to_array(), [4, 5, 6, 7, 0, 1, 2, 3]); + // Reverse lanes, within each 4-lane group: + assert_eq!(x.general_reverse::<3>().to_array(), [3, 2, 1, 0, 7, 6, 5, 4]); +} \ No newline at end of file From c28564dfd44e2f13cbfc45e3fb72cce166b3648e Mon Sep 17 00:00:00 2001 From: Reiner Pope Date: Mon, 13 Mar 2023 05:14:52 +0000 Subject: [PATCH 02/12] Document general_reverse() more. --- crates/core_simd/src/swizzle.rs | 13 +++++++++++++ crates/core_simd/tests/swizzle.rs | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index de8c1ea17d7..9bdfff7ca6a 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -454,6 +454,19 @@ where /// // Reverse lanes, within each 4-lane group: /// assert_eq!(x.general_reverse::<3>().to_array(), [3, 2, 1, 0, 7, 6, 5, 4]); /// ``` + /// + /// Commonly useful for horizontal reductions, for example: + /// + /// ``` + /// # #![feature(portable_simd)] + /// # use core::simd::Simd; + /// let x = Simd::from_array([0u32, 1, 2, 3, 4, 5, 6, 7]); + /// let x = x + x.general_reverse::<1>(); + /// let x = x + x.general_reverse::<2>(); + /// let x = x + x.general_reverse::<4>(); + /// assert_eq!(x.to_array(), [28, 28, 28, 28, 28, 28, 28, 28]); + /// ``` + pub fn general_reverse(self) -> Self { const fn general_reverse_index(swap_mask: usize) -> [usize; LANES] { let mut index = [0; LANES]; diff --git a/crates/core_simd/tests/swizzle.rs b/crates/core_simd/tests/swizzle.rs index 8aaefee97b8..513f00a849a 100644 --- a/crates/core_simd/tests/swizzle.rs +++ b/crates/core_simd/tests/swizzle.rs @@ -108,4 +108,14 @@ fn general_reverse() { assert_eq!(x.general_reverse::<4>().to_array(), [4, 5, 6, 7, 0, 1, 2, 3]); // Reverse lanes, within each 4-lane group: assert_eq!(x.general_reverse::<3>().to_array(), [3, 2, 1, 0, 7, 6, 5, 4]); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn general_reverse_sum() { + let x = Simd::from_array([0u32, 1, 2, 3, 4, 5, 6, 7]); + let x = x + x.general_reverse::<1>(); + let x = x + x.general_reverse::<2>(); + let x = x + x.general_reverse::<4>(); + assert_eq!(x.to_array(), [28, 28, 28, 28, 28, 28, 28, 28]); } \ No newline at end of file From ffe58c9932c33a7b3c99c9918bcaa2f330fd1a97 Mon Sep 17 00:00:00 2001 From: Reiner Pope Date: Mon, 13 Mar 2023 05:17:33 +0000 Subject: [PATCH 03/12] rustfmt --- crates/core_simd/src/swizzle.rs | 31 ++++++++++++++++++------------- crates/core_simd/tests/swizzle.rs | 22 +++++++++++++++++----- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 9bdfff7ca6a..b707b675f9e 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -369,7 +369,7 @@ where /// /// This is equivalent to computing `&self[OFFSET..OFFSET+LEN]` on /// the underlying array. - /// + /// /// ``` /// # #![feature(portable_simd)] /// # use core::simd::Simd; @@ -377,12 +377,14 @@ where /// let y = x.slice::<2, 4>(); /// assert_eq!(y.to_array(), [2, 3, 4, 5]); /// ``` - /// + /// /// Will be rejected at compile time if `OFFSET + LEN > LANES`. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn slice(self) -> Simd - where LaneCount: SupportedLaneCount { + pub fn slice(self) -> Simd + where + LaneCount: SupportedLaneCount, + { const fn slice_index(offset: usize, lanes: usize) -> [usize; LEN] { assert!(offset + LEN <= lanes, "slice out of bounds"); let mut index = [0; LEN]; @@ -394,17 +396,19 @@ where index } struct Slice; - impl Swizzle for Slice { + impl Swizzle + for Slice + { const INDEX: [usize; LEN] = slice_index::(OFFSET, LANES); } Slice::::swizzle(self) } /// Concatenates two vectors of equal length. - /// + /// /// Due to limitations in const generics, the length of the resulting vector cannot be inferred /// from the input vectors. You must specify it explicitly. - /// + /// /// ``` /// # #![feature(portable_simd)] /// # use core::simd::Simd; @@ -413,10 +417,11 @@ where /// let z = x.concat_to::<8>(y); /// assert_eq!(z.to_array(), [0, 1, 2, 3, 4, 5, 6, 7]); /// ``` - /// + /// /// Will be rejected at compile time if `LANES * 2 != DOUBLE_LANES`. pub fn concat_to(self, other: Self) -> Simd - where LaneCount: SupportedLaneCount + where + LaneCount: SupportedLaneCount, { const fn concat_index(lanes: usize) -> [Which; DOUBLE_LANES] { assert!(lanes * 2 == DOUBLE_LANES); @@ -437,10 +442,10 @@ where } /// For each lane `i`, swaps it with lane `i ^ SWAP_MASK`. - /// + /// /// Also known as `grev` in the RISC-V Bitmanip specification, this is a powerful /// swizzle operation that can implement many common patterns as special cases. - /// + /// /// ``` /// # #![feature(portable_simd)] /// # use core::simd::Simd; @@ -454,9 +459,9 @@ where /// // Reverse lanes, within each 4-lane group: /// assert_eq!(x.general_reverse::<3>().to_array(), [3, 2, 1, 0, 7, 6, 5, 4]); /// ``` - /// + /// /// Commonly useful for horizontal reductions, for example: - /// + /// /// ``` /// # #![feature(portable_simd)] /// # use core::simd::Simd; diff --git a/crates/core_simd/tests/swizzle.rs b/crates/core_simd/tests/swizzle.rs index 513f00a849a..465e8a8bf3d 100644 --- a/crates/core_simd/tests/swizzle.rs +++ b/crates/core_simd/tests/swizzle.rs @@ -101,13 +101,25 @@ fn concat() { fn general_reverse() { let x = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); // Swap adjacent lanes: - assert_eq!(x.general_reverse::<1>().to_array(), [1, 0, 3, 2, 5, 4, 7, 6]); + assert_eq!( + x.general_reverse::<1>().to_array(), + [1, 0, 3, 2, 5, 4, 7, 6] + ); // Swap lanes separated by distance 2: - assert_eq!(x.general_reverse::<2>().to_array(), [2, 3, 0, 1, 6, 7, 4, 5]); + assert_eq!( + x.general_reverse::<2>().to_array(), + [2, 3, 0, 1, 6, 7, 4, 5] + ); // Swap lanes separated by distance 4: - assert_eq!(x.general_reverse::<4>().to_array(), [4, 5, 6, 7, 0, 1, 2, 3]); + assert_eq!( + x.general_reverse::<4>().to_array(), + [4, 5, 6, 7, 0, 1, 2, 3] + ); // Reverse lanes, within each 4-lane group: - assert_eq!(x.general_reverse::<3>().to_array(), [3, 2, 1, 0, 7, 6, 5, 4]); + assert_eq!( + x.general_reverse::<3>().to_array(), + [3, 2, 1, 0, 7, 6, 5, 4] + ); } #[test] @@ -118,4 +130,4 @@ fn general_reverse_sum() { let x = x + x.general_reverse::<2>(); let x = x + x.general_reverse::<4>(); assert_eq!(x.to_array(), [28, 28, 28, 28, 28, 28, 28, 28]); -} \ No newline at end of file +} From 5d30d9f50eb0fd108197865ac69fa6371ff2e1b0 Mon Sep 17 00:00:00 2001 From: Reiner Pope Date: Mon, 13 Mar 2023 05:18:50 +0000 Subject: [PATCH 04/12] Attributes --- crates/core_simd/src/swizzle.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index b707b675f9e..b5b67d5efd0 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -419,6 +419,8 @@ where /// ``` /// /// Will be rejected at compile time if `LANES * 2 != DOUBLE_LANES`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn concat_to(self, other: Self) -> Simd where LaneCount: SupportedLaneCount, @@ -471,7 +473,8 @@ where /// let x = x + x.general_reverse::<4>(); /// assert_eq!(x.to_array(), [28, 28, 28, 28, 28, 28, 28, 28]); /// ``` - + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn general_reverse(self) -> Self { const fn general_reverse_index(swap_mask: usize) -> [usize; LANES] { let mut index = [0; LANES]; From 024fb9afe5484c103865d41946a514bbb55dabb6 Mon Sep 17 00:00:00 2001 From: Reiner Pope Date: Mon, 13 Mar 2023 19:02:03 +0000 Subject: [PATCH 05/12] Fix tests --- crates/core_simd/src/swizzle.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index b5b67d5efd0..df41bd5551a 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -372,7 +372,8 @@ where /// /// ``` /// # #![feature(portable_simd)] - /// # use core::simd::Simd; + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; /// let x = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); /// let y = x.slice::<2, 4>(); /// assert_eq!(y.to_array(), [2, 3, 4, 5]); @@ -381,7 +382,7 @@ where /// Will be rejected at compile time if `OFFSET + LEN > LANES`. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn slice(self) -> Simd + pub fn split_to(self) -> Simd where LaneCount: SupportedLaneCount, { @@ -411,7 +412,8 @@ where /// /// ``` /// # #![feature(portable_simd)] - /// # use core::simd::Simd; + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; /// let x = Simd::from_array([0, 1, 2, 3]); /// let y = Simd::from_array([4, 5, 6, 7]); /// let z = x.concat_to::<8>(y); @@ -450,7 +452,8 @@ where /// /// ``` /// # #![feature(portable_simd)] - /// # use core::simd::Simd; + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; /// let x = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); /// // Swap adjacent lanes: /// assert_eq!(x.general_reverse::<1>().to_array(), [1, 0, 3, 2, 5, 4, 7, 6]); @@ -466,7 +469,8 @@ where /// /// ``` /// # #![feature(portable_simd)] - /// # use core::simd::Simd; + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; /// let x = Simd::from_array([0u32, 1, 2, 3, 4, 5, 6, 7]); /// let x = x + x.general_reverse::<1>(); /// let x = x + x.general_reverse::<2>(); From 4575d282b56e873dcbc8877be2ea1e4e2d3aa175 Mon Sep 17 00:00:00 2001 From: Reiner Pope Date: Mon, 13 Mar 2023 19:14:14 +0000 Subject: [PATCH 06/12] Switch from `slice` to `split` --- crates/core_simd/src/swizzle.rs | 39 +++++++++++++++++-------------- crates/core_simd/tests/swizzle.rs | 5 +--- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index df41bd5551a..a3fa8752f3b 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -365,29 +365,33 @@ where (Even::swizzle2(self, other), Odd::swizzle2(self, other)) } - /// Takes a slice of a vector to produce a shorter vector. + /// Splits a vector into its two halves. /// - /// This is equivalent to computing `&self[OFFSET..OFFSET+LEN]` on - /// the underlying array. + /// Due to limitations in const generics, the length of the resulting vector cannot be inferred + /// from the input vectors. You must specify it explicitly. A compile-time error will be raised + /// if `HALF_LANES * 2 != LANES`. /// /// ``` /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; /// let x = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); - /// let y = x.slice::<2, 4>(); - /// assert_eq!(y.to_array(), [2, 3, 4, 5]); + /// let [y, z] = x.split_to::<4>(); + /// assert_eq!(y.to_array(), [0, 1, 2, 3]); + /// assert_eq!(z.to_array(), [4, 5, 6, 7]); /// ``` - /// - /// Will be rejected at compile time if `OFFSET + LEN > LANES`. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn split_to(self) -> Simd + pub fn split_to(self) -> [Simd; 2] where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - const fn slice_index(offset: usize, lanes: usize) -> [usize; LEN] { - assert!(offset + LEN <= lanes, "slice out of bounds"); + const fn slice_index(hi_half: bool, lanes: usize) -> [usize; LEN] { + assert!( + LEN * 2 == lanes, + "x.split_to::() must provide N=x.lanes()/2" + ); + let offset = if hi_half { LEN } else { 0 }; let mut index = [0; LEN]; let mut i = 0; while i < LEN { @@ -396,19 +400,20 @@ where } index } - struct Slice; - impl Swizzle - for Slice + struct Split; + impl Swizzle + for Split { - const INDEX: [usize; LEN] = slice_index::(OFFSET, LANES); + const INDEX: [usize; LEN] = slice_index::(HI_HALF, LANES); } - Slice::::swizzle(self) + [Split::::swizzle(self), Split::::swizzle(self)] } /// Concatenates two vectors of equal length. /// /// Due to limitations in const generics, the length of the resulting vector cannot be inferred - /// from the input vectors. You must specify it explicitly. + /// from the input vectors. You must specify it explicitly. A compile time error will be raised + /// if `LANES * 2 != DOUBLE_LANES` /// /// ``` /// # #![feature(portable_simd)] diff --git a/crates/core_simd/tests/swizzle.rs b/crates/core_simd/tests/swizzle.rs index 465e8a8bf3d..0d4bd85a07d 100644 --- a/crates/core_simd/tests/swizzle.rs +++ b/crates/core_simd/tests/swizzle.rs @@ -79,11 +79,8 @@ fn interleave_one() { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn slice() { let a = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); - let lo = a.slice::<0, 4>(); - let mid = a.slice::<3, 2>(); - let hi = a.slice::<4, 4>(); + let [lo, hi] = a.split_to::<4>(); assert_eq!(lo.to_array(), [0, 1, 2, 3]); - assert_eq!(mid.to_array(), [3, 4]); assert_eq!(hi.to_array(), [4, 5, 6, 7]); } From 94b2f080c044aa6d770552c72c4cac40553c6757 Mon Sep 17 00:00:00 2001 From: Reiner Pope Date: Mon, 13 Mar 2023 19:16:16 +0000 Subject: [PATCH 07/12] Add doc aliases --- crates/core_simd/src/swizzle.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index a3fa8752f3b..1b8cfdcba71 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -484,6 +484,9 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] + #[doc(alias = "grev")] + #[doc(alias = "butterfly")] + #[doc(alias = "bfly")] pub fn general_reverse(self) -> Self { const fn general_reverse_index(swap_mask: usize) -> [usize; LANES] { let mut index = [0; LANES]; From ae495bdc790ebbfde2befc651f05f0cb31fe2b9c Mon Sep 17 00:00:00 2001 From: Reiner Pope Date: Mon, 13 Mar 2023 19:43:28 +0000 Subject: [PATCH 08/12] Add TODO relating to generic_const_exprs. --- crates/core_simd/src/swizzle.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 1b8cfdcba71..712b0b72b2b 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -382,6 +382,9 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] + // TODO: when `generic_const_exprs` supports it, provide + // `pub fn split(self) -> [Simd; 2]` + // and deprecate `split_to`. pub fn split_to(self) -> [Simd; 2] where LaneCount: SupportedLaneCount, @@ -428,6 +431,9 @@ where /// Will be rejected at compile time if `LANES * 2 != DOUBLE_LANES`. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] + // TODO: when `generic_const_exprs` supports it, provide + // `pub fn concat(self, other: Self) -> Simd` + // and deprecate `concat_to`. pub fn concat_to(self, other: Self) -> Simd where LaneCount: SupportedLaneCount, @@ -452,8 +458,12 @@ where /// For each lane `i`, swaps it with lane `i ^ SWAP_MASK`. /// - /// Also known as `grev` in the RISC-V Bitmanip specification, this is a powerful - /// swizzle operation that can implement many common patterns as special cases. + /// This is a powerful swizzle operation that can implement many common patterns as special cases. + /// For power-of-2 swap masks, this produces the [butterfly shuffles](https://en.wikipedia.org/wiki/Butterfly_network) + /// that are often useful for horizontal reductions. + /// + /// A similar operation (operating on bits instead of lanes) is known as `grev` in the RISC-V + /// Bitmanip specification. /// /// ``` /// # #![feature(portable_simd)] From 0c9a7ab23801b2577f734b3c1f2e872203ae5969 Mon Sep 17 00:00:00 2001 From: Reiner Pope Date: Mon, 13 Mar 2023 19:52:25 +0000 Subject: [PATCH 09/12] rustfmt --- crates/core_simd/src/swizzle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 712b0b72b2b..9be273c7940 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -461,7 +461,7 @@ where /// This is a powerful swizzle operation that can implement many common patterns as special cases. /// For power-of-2 swap masks, this produces the [butterfly shuffles](https://en.wikipedia.org/wiki/Butterfly_network) /// that are often useful for horizontal reductions. - /// + /// /// A similar operation (operating on bits instead of lanes) is known as `grev` in the RISC-V /// Bitmanip specification. /// From 14904c9375f1a0bc5ab94157ce0f5505578891f9 Mon Sep 17 00:00:00 2001 From: Reiner Pope Date: Tue, 14 Mar 2023 02:47:45 +0000 Subject: [PATCH 10/12] concat supports mismatched widths --- crates/core_simd/src/swizzle.rs | 79 ++++++++++++++++++++----------- crates/core_simd/tests/swizzle.rs | 15 ++++-- 2 files changed, 63 insertions(+), 31 deletions(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 9be273c7940..4c553f8bb68 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -368,24 +368,23 @@ where /// Splits a vector into its two halves. /// /// Due to limitations in const generics, the length of the resulting vector cannot be inferred - /// from the input vectors. You must specify it explicitly. A compile-time error will be raised - /// if `HALF_LANES * 2 != LANES`. + /// from the input vectors. You must specify it explicitly or constrain it by context. A + /// compile-time error will be raised if `HALF_LANES * 2 != LANES`. /// /// ``` /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; /// let x = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); - /// let [y, z] = x.split_to::<4>(); + /// let [y, z] = x.split(); /// assert_eq!(y.to_array(), [0, 1, 2, 3]); /// assert_eq!(z.to_array(), [4, 5, 6, 7]); /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - // TODO: when `generic_const_exprs` supports it, provide + // TODO: when `generic_const_exprs` supports it, change signature to // `pub fn split(self) -> [Simd; 2]` - // and deprecate `split_to`. - pub fn split_to(self) -> [Simd; 2] + pub fn split(self) -> [Simd; 2] where LaneCount: SupportedLaneCount, { @@ -415,8 +414,8 @@ where /// Concatenates two vectors of equal length. /// /// Due to limitations in const generics, the length of the resulting vector cannot be inferred - /// from the input vectors. You must specify it explicitly. A compile time error will be raised - /// if `LANES * 2 != DOUBLE_LANES` + /// from the input vectors. You must specify it explicitly or constrain it by context. + /// A compile time error will be raised if `LANES + LANES2 != OUTPUT_LANES`. /// /// ``` /// # #![feature(portable_simd)] @@ -424,36 +423,60 @@ where /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; /// let x = Simd::from_array([0, 1, 2, 3]); /// let y = Simd::from_array([4, 5, 6, 7]); - /// let z = x.concat_to::<8>(y); + /// let z = x.concat(y); /// assert_eq!(z.to_array(), [0, 1, 2, 3, 4, 5, 6, 7]); /// ``` /// /// Will be rejected at compile time if `LANES * 2 != DOUBLE_LANES`. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - // TODO: when `generic_const_exprs` supports it, provide - // `pub fn concat(self, other: Self) -> Simd` - // and deprecate `concat_to`. - pub fn concat_to(self, other: Self) -> Simd + // TODO: when `generic_const_exprs` supports it, change signature to + // `pub fn concat(self, other: Simd) -> Simd` + pub fn concat( + self, + other: Simd, + ) -> Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - const fn concat_index(lanes: usize) -> [Which; DOUBLE_LANES] { - assert!(lanes * 2 == DOUBLE_LANES); - let mut index = [Which::First(0); DOUBLE_LANES]; - let mut i = 0; - while i < lanes { - index[i] = Which::First(i); - index[i + lanes] = Which::Second(i); - i += 1; - } - index + struct Extend; + impl Swizzle for Extend { + const INDEX: [usize; O] = { + assert!(I <= O); + let mut index = [0; O]; + let mut i = 0; + while i < I { + index[i] = i; + i += 1; + } + index + }; } - struct Concat; - impl Swizzle2 for Concat { - const INDEX: [Which; DOUBLE_LANES] = concat_index::(LANES); + struct Concat; + impl Swizzle2 for Concat { + const INDEX: [Which; Y] = { + assert!( + A + B == Y, + "concat: OUTPUT_LANES must be the sum of all input lane counts" + ); + let mut retval = [Which::First(0); Y]; + let mut i = 0; + while i < Y { + if i < A { + retval[i] = Which::First(i); + } else { + retval[i] = Which::Second(i - A); + } + i += 1; + } + retval + }; } - Concat::swizzle2(self, other) + Concat::::swizzle2( + Extend::swizzle(self), + Extend::swizzle(other), + ) } /// For each lane `i`, swaps it with lane `i ^ SWAP_MASK`. diff --git a/crates/core_simd/tests/swizzle.rs b/crates/core_simd/tests/swizzle.rs index 0d4bd85a07d..726034a002e 100644 --- a/crates/core_simd/tests/swizzle.rs +++ b/crates/core_simd/tests/swizzle.rs @@ -79,20 +79,29 @@ fn interleave_one() { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn slice() { let a = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); - let [lo, hi] = a.split_to::<4>(); + let [lo, hi] = a.split(); assert_eq!(lo.to_array(), [0, 1, 2, 3]); assert_eq!(hi.to_array(), [4, 5, 6, 7]); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] -fn concat() { +fn concat_equal_width() { let x = Simd::from_array([0, 1, 2, 3]); let y = Simd::from_array([4, 5, 6, 7]); - let z = x.concat_to::<8>(y); + let z = x.concat(y); assert_eq!(z.to_array(), [0, 1, 2, 3, 4, 5, 6, 7]); } +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn concat_different_width() { + let x = Simd::from_array([0, 1, 2, 3]); + let y = Simd::from_array([4, 5]); + let z = x.concat(y); + assert_eq!(z.to_array(), [0, 1, 2, 3, 4, 5]); +} + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn general_reverse() { From 73621abda9afd09592c5bc90e8e2639f58bd2bce Mon Sep 17 00:00:00 2001 From: Reiner Pope Date: Mon, 13 Mar 2023 23:32:31 -0700 Subject: [PATCH 11/12] Fix test Co-authored-by: Jacob Lifshay --- crates/core_simd/tests/swizzle.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/core_simd/tests/swizzle.rs b/crates/core_simd/tests/swizzle.rs index 726034a002e..5442de08ef9 100644 --- a/crates/core_simd/tests/swizzle.rs +++ b/crates/core_simd/tests/swizzle.rs @@ -95,6 +95,7 @@ fn concat_equal_width() { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "all_lane_counts")] fn concat_different_width() { let x = Simd::from_array([0, 1, 2, 3]); let y = Simd::from_array([4, 5]); From 8c07d1a16de2cabd848ee2d8b20352db1f66fe1a Mon Sep 17 00:00:00 2001 From: Reiner Pope Date: Sun, 26 Mar 2023 07:57:58 +0000 Subject: [PATCH 12/12] Rename. --- crates/core_simd/src/swizzle.rs | 39 +++++++++++++++++-------------- crates/core_simd/tests/swizzle.rs | 14 +++++------ 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 4c553f8bb68..4d49a4a2078 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -426,8 +426,6 @@ where /// let z = x.concat(y); /// assert_eq!(z.to_array(), [0, 1, 2, 3, 4, 5, 6, 7]); /// ``` - /// - /// Will be rejected at compile time if `LANES * 2 != DOUBLE_LANES`. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] // TODO: when `generic_const_exprs` supports it, change signature to @@ -481,8 +479,8 @@ where /// For each lane `i`, swaps it with lane `i ^ SWAP_MASK`. /// - /// This is a powerful swizzle operation that can implement many common patterns as special cases. - /// For power-of-2 swap masks, this produces the [butterfly shuffles](https://en.wikipedia.org/wiki/Butterfly_network) + /// This is a powerful swizzle operation that can implement many common patterns as special cases: + /// Each power-of-2 swap mask produces a single stage of the [butterfly shuffles](https://en.wikipedia.org/wiki/Butterfly_network) /// that are often useful for horizontal reductions. /// /// A similar operation (operating on bits instead of lanes) is known as `grev` in the RISC-V @@ -494,34 +492,37 @@ where /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; /// let x = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); /// // Swap adjacent lanes: - /// assert_eq!(x.general_reverse::<1>().to_array(), [1, 0, 3, 2, 5, 4, 7, 6]); + /// assert_eq!(x.swizzle_to_xor_indices::<1>().to_array(), [1, 0, 3, 2, 5, 4, 7, 6]); /// // Swap lanes separated by distance 2: - /// assert_eq!(x.general_reverse::<2>().to_array(), [2, 3, 0, 1, 6, 7, 4, 5]); + /// assert_eq!(x.swizzle_to_xor_indices::<2>().to_array(), [2, 3, 0, 1, 6, 7, 4, 5]); /// // Swap lanes separated by distance 4: - /// assert_eq!(x.general_reverse::<4>().to_array(), [4, 5, 6, 7, 0, 1, 2, 3]); + /// assert_eq!(x.swizzle_to_xor_indices::<4>().to_array(), [4, 5, 6, 7, 0, 1, 2, 3]); /// // Reverse lanes, within each 4-lane group: - /// assert_eq!(x.general_reverse::<3>().to_array(), [3, 2, 1, 0, 7, 6, 5, 4]); + /// assert_eq!(x.swizzle_to_xor_indices::<3>().to_array(), [3, 2, 1, 0, 7, 6, 5, 4]); /// ``` /// - /// Commonly useful for horizontal reductions, for example: + /// This operation is commonly useful for horizontal reductions, for example: /// /// ``` /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; /// let x = Simd::from_array([0u32, 1, 2, 3, 4, 5, 6, 7]); - /// let x = x + x.general_reverse::<1>(); - /// let x = x + x.general_reverse::<2>(); - /// let x = x + x.general_reverse::<4>(); + /// let x = x + x.swizzle_to_xor_indices::<1>(); + /// let x = x + x.swizzle_to_xor_indices::<2>(); + /// let x = x + x.swizzle_to_xor_indices::<4>(); /// assert_eq!(x.to_array(), [28, 28, 28, 28, 28, 28, 28, 28]); /// ``` + #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] #[doc(alias = "grev")] #[doc(alias = "butterfly")] #[doc(alias = "bfly")] - pub fn general_reverse(self) -> Self { - const fn general_reverse_index(swap_mask: usize) -> [usize; LANES] { + pub fn swizzle_to_xor_indices(self) -> Self { + const fn swizzle_to_xor_indices_index( + swap_mask: usize, + ) -> [usize; LANES] { let mut index = [0; LANES]; let mut i = 0; while i < LANES { @@ -530,10 +531,12 @@ where } index } - struct GeneralReverse; - impl Swizzle for GeneralReverse { - const INDEX: [usize; LANES] = general_reverse_index::(DISTANCE); + struct ButterflySwizzle; + impl Swizzle + for ButterflySwizzle + { + const INDEX: [usize; LANES] = swizzle_to_xor_indices_index::(DISTANCE); } - GeneralReverse::::swizzle(self) + ButterflySwizzle::::swizzle(self) } } diff --git a/crates/core_simd/tests/swizzle.rs b/crates/core_simd/tests/swizzle.rs index 5442de08ef9..981108db8c8 100644 --- a/crates/core_simd/tests/swizzle.rs +++ b/crates/core_simd/tests/swizzle.rs @@ -109,22 +109,22 @@ fn general_reverse() { let x = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); // Swap adjacent lanes: assert_eq!( - x.general_reverse::<1>().to_array(), + x.swizzle_to_xor_indices::<1>().to_array(), [1, 0, 3, 2, 5, 4, 7, 6] ); // Swap lanes separated by distance 2: assert_eq!( - x.general_reverse::<2>().to_array(), + x.swizzle_to_xor_indices::<2>().to_array(), [2, 3, 0, 1, 6, 7, 4, 5] ); // Swap lanes separated by distance 4: assert_eq!( - x.general_reverse::<4>().to_array(), + x.swizzle_to_xor_indices::<4>().to_array(), [4, 5, 6, 7, 0, 1, 2, 3] ); // Reverse lanes, within each 4-lane group: assert_eq!( - x.general_reverse::<3>().to_array(), + x.swizzle_to_xor_indices::<3>().to_array(), [3, 2, 1, 0, 7, 6, 5, 4] ); } @@ -133,8 +133,8 @@ fn general_reverse() { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn general_reverse_sum() { let x = Simd::from_array([0u32, 1, 2, 3, 4, 5, 6, 7]); - let x = x + x.general_reverse::<1>(); - let x = x + x.general_reverse::<2>(); - let x = x + x.general_reverse::<4>(); + let x = x + x.swizzle_to_xor_indices::<1>(); + let x = x + x.swizzle_to_xor_indices::<2>(); + let x = x + x.swizzle_to_xor_indices::<4>(); assert_eq!(x.to_array(), [28, 28, 28, 28, 28, 28, 28, 28]); }