From a5789d17bfc8bb794ea6e3f1497b8026d07edd60 Mon Sep 17 00:00:00 2001 From: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Mon, 28 Feb 2022 19:56:31 -0800 Subject: [PATCH 01/13] rust-lang/portable-simd#248: Remove default features Now that we are thoroughly embedded in libcore, we don't need these on by default. Indeed, their presence may provide confusing results during integration attempts. --- crates/core_simd/Cargo.toml | 2 +- crates/core_simd/tests/round.rs | 1 - crates/std_float/Cargo.toml | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/core_simd/Cargo.toml b/crates/core_simd/Cargo.toml index d2ff5f3b1b1..8877c6df66e 100644 --- a/crates/core_simd/Cargo.toml +++ b/crates/core_simd/Cargo.toml @@ -9,7 +9,7 @@ categories = ["hardware-support", "no-std"] license = "MIT OR Apache-2.0" [features] -default = ["std", "generic_const_exprs"] +default = [] std = [] generic_const_exprs = [] diff --git a/crates/core_simd/tests/round.rs b/crates/core_simd/tests/round.rs index 53732329237..7feb0320a16 100644 --- a/crates/core_simd/tests/round.rs +++ b/crates/core_simd/tests/round.rs @@ -9,7 +9,6 @@ macro_rules! float_rounding_test { type Scalar = $scalar; type IntScalar = $int_scalar; - #[cfg(feature = "std")] test_helpers::test_lanes! { fn ceil() { test_helpers::test_unary_elementwise( diff --git a/crates/std_float/Cargo.toml b/crates/std_float/Cargo.toml index 82f66b8dcb7..84c69774cbd 100644 --- a/crates/std_float/Cargo.toml +++ b/crates/std_float/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -core_simd = { path = "../core_simd" } +core_simd = { path = "../core_simd", default-features = false } [features] default = ["as_crate"] From 30975615b7c206ee96eddbf84fc0f11ee896f849 Mon Sep 17 00:00:00 2001 From: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Tue, 1 Mar 2022 16:10:49 -0800 Subject: [PATCH 02/13] rust-lang/portable-simd#250: Add bitmask i{N <8} -> u8 impls ...and copy the notes for why they're legal. --- crates/core_simd/src/intrinsics.rs | 8 ++++++++ crates/core_simd/src/masks/to_bitmask.rs | 3 +++ 2 files changed, 11 insertions(+) diff --git a/crates/core_simd/src/intrinsics.rs b/crates/core_simd/src/intrinsics.rs index e150946c705..47edff4a66a 100644 --- a/crates/core_simd/src/intrinsics.rs +++ b/crates/core_simd/src/intrinsics.rs @@ -130,6 +130,14 @@ extern "platform-intrinsic" { pub(crate) fn simd_reduce_xor(x: T) -> U; // truncate integer vector to bitmask + // `fn simd_bitmask(vector) -> unsigned integer` takes a vector of integers and + // returns either an unsigned integer or array of `u8`. + // Every element in the vector becomes a single bit in the returned bitmask. + // If the vector has less than 8 lanes, a u8 is returned with zeroed trailing bits. + // The bit order of the result depends on the byte endianness. LSB-first for little + // endian and MSB-first for big endian. + // + // UB if called on a vector with values other than 0 and -1. #[allow(unused)] pub(crate) fn simd_bitmask(x: T) -> U; diff --git a/crates/core_simd/src/masks/to_bitmask.rs b/crates/core_simd/src/masks/to_bitmask.rs index 1c2037764c1..c263f6a4eec 100644 --- a/crates/core_simd/src/masks/to_bitmask.rs +++ b/crates/core_simd/src/masks/to_bitmask.rs @@ -50,6 +50,9 @@ macro_rules! impl_integer_intrinsic { } impl_integer_intrinsic! { + unsafe impl ToBitMask for Mask<_, 1> + unsafe impl ToBitMask for Mask<_, 2> + unsafe impl ToBitMask for Mask<_, 4> unsafe impl ToBitMask for Mask<_, 8> unsafe impl ToBitMask for Mask<_, 16> unsafe impl ToBitMask for Mask<_, 32> From 4ddcc006a9d8b4db65d7809881d058bf9b6cd8b0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 4 Mar 2022 14:14:49 -0500 Subject: [PATCH 03/13] rust-lang/portable-simd#252: extern blocks don't have doc comments --- crates/core_simd/src/intrinsics.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/core_simd/src/intrinsics.rs b/crates/core_simd/src/intrinsics.rs index 47edff4a66a..ee7408b62de 100644 --- a/crates/core_simd/src/intrinsics.rs +++ b/crates/core_simd/src/intrinsics.rs @@ -18,9 +18,9 @@ //! //! Unless stated otherwise, all intrinsics for binary operations require SIMD vectors of equal types and lengths. -/// These intrinsics aren't linked directly from LLVM and are mostly undocumented, however they are -/// mostly lowered to the matching LLVM instructions by the compiler in a fairly straightforward manner. -/// The associated LLVM instruction or intrinsic is documented alongside each Rust intrinsic function. +// These intrinsics aren't linked directly from LLVM and are mostly undocumented, however they are +// mostly lowered to the matching LLVM instructions by the compiler in a fairly straightforward manner. +// The associated LLVM instruction or intrinsic is documented alongside each Rust intrinsic function. extern "platform-intrinsic" { /// add/fadd pub(crate) fn simd_add(x: T, y: T) -> T; From 2e5e0ec3807972cb699a97cd15dee5d6dbc936a5 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 8 Mar 2022 10:18:06 -0800 Subject: [PATCH 04/13] Remove #![feature(const_fn_trait_bound)] --- crates/core_simd/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 91ae34c05e0..42212aef132 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] #![feature( - const_fn_trait_bound, convert_float_to_int, decl_macro, intra_doc_pointers, From 2d13059ae9162997c5b88997bda44443615f12ab Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 8 Mar 2022 10:28:27 -0800 Subject: [PATCH 05/13] Clean up use of cargo feature "std" --- crates/core_simd/src/lib.rs | 2 +- crates/core_simd/src/math.rs | 21 +++++++-------------- crates/core_simd/src/select.rs | 6 ++---- crates/core_simd/src/swizzle.rs | 12 ++++-------- crates/core_simd/src/vector.rs | 24 ++++++++---------------- 5 files changed, 22 insertions(+), 43 deletions(-) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 42212aef132..2632073622e 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(feature = "std"), no_std)] +#![no_std] #![feature( convert_float_to_int, decl_macro, diff --git a/crates/core_simd/src/math.rs b/crates/core_simd/src/math.rs index 0b4e40983af..606021e983e 100644 --- a/crates/core_simd/src/math.rs +++ b/crates/core_simd/src/math.rs @@ -10,8 +10,7 @@ macro_rules! impl_uint_arith { /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; #[doc = concat!("# use core::", stringify!($ty), "::MAX;")] /// let x = Simd::from_array([2, 1, 0, MAX]); /// let max = Simd::splat(MAX); @@ -31,8 +30,7 @@ macro_rules! impl_uint_arith { /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; #[doc = concat!("# use core::", stringify!($ty), "::MAX;")] /// let x = Simd::from_array([2, 1, 0, MAX]); /// let max = Simd::splat(MAX); @@ -58,8 +56,7 @@ macro_rules! impl_int_arith { /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; #[doc = concat!("# use core::", stringify!($ty), "::{MIN, MAX};")] /// let x = Simd::from_array([MIN, 0, 1, MAX]); /// let max = Simd::splat(MAX); @@ -79,8 +76,7 @@ macro_rules! impl_int_arith { /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; #[doc = concat!("# use core::", stringify!($ty), "::{MIN, MAX};")] /// let x = Simd::from_array([MIN, -2, -1, MAX]); /// let max = Simd::splat(MAX); @@ -100,8 +96,7 @@ macro_rules! impl_int_arith { /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; #[doc = concat!("# use core::", stringify!($ty), "::{MIN, MAX};")] /// let xs = Simd::from_array([MIN, MIN +1, -5, 0]); /// assert_eq!(xs.abs(), Simd::from_array([MIN, MAX, 5, 0])); @@ -119,8 +114,7 @@ macro_rules! impl_int_arith { /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; #[doc = concat!("# use core::", stringify!($ty), "::{MIN, MAX};")] /// let xs = Simd::from_array([MIN, -2, 0, 3]); /// let unsat = xs.abs(); @@ -142,8 +136,7 @@ macro_rules! impl_int_arith { /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; #[doc = concat!("# use core::", stringify!($ty), "::{MIN, MAX};")] /// let x = Simd::from_array([MIN, -2, 3, MAX]); /// let unsat = -x; diff --git a/crates/core_simd/src/select.rs b/crates/core_simd/src/select.rs index 3acf07260e1..065c5987d3f 100644 --- a/crates/core_simd/src/select.rs +++ b/crates/core_simd/src/select.rs @@ -14,8 +14,7 @@ where /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::{Simd, Mask}; - /// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask}; + /// # use core::simd::{Simd, Mask}; /// let a = Simd::from_array([0, 1, 2, 3]); /// let b = Simd::from_array([4, 5, 6, 7]); /// let mask = Mask::from_array([true, false, false, true]); @@ -45,8 +44,7 @@ where /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Mask; - /// # #[cfg(not(feature = "std"))] use core::simd::Mask; + /// # use core::simd::Mask; /// let a = Mask::::from_array([true, true, false, false]); /// let b = Mask::::from_array([false, false, true, true]); /// let mask = Mask::::from_array([true, false, false, true]); diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 08b2add1166..ef47c4f3a4c 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -12,8 +12,7 @@ use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; /// ## One source vector /// ``` /// # #![feature(portable_simd)] -/// # #[cfg(feature = "std")] use core_simd::{Simd, simd_swizzle}; -/// # #[cfg(not(feature = "std"))] use core::simd::{Simd, simd_swizzle}; +/// # use core::simd::{Simd, simd_swizzle}; /// let v = Simd::::from_array([0., 1., 2., 3.]); /// /// // Keeping the same size @@ -28,8 +27,7 @@ use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; /// ## Two source vectors /// ``` /// # #![feature(portable_simd)] -/// # #[cfg(feature = "std")] use core_simd::{Simd, simd_swizzle, Which}; -/// # #[cfg(not(feature = "std"))] use core::simd::{Simd, simd_swizzle, Which}; +/// # use core::simd::{Simd, simd_swizzle, Which}; /// use Which::*; /// let a = Simd::::from_array([0., 1., 2., 3.]); /// let b = Simd::::from_array([4., 5., 6., 7.]); @@ -273,8 +271,7 @@ where /// /// ``` /// #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; /// let a = Simd::from_array([0, 1, 2, 3]); /// let b = Simd::from_array([4, 5, 6, 7]); /// let (x, y) = a.interleave(b); @@ -337,8 +334,7 @@ where /// /// ``` /// #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; /// let a = Simd::from_array([0, 4, 1, 5]); /// let b = Simd::from_array([2, 6, 3, 7]); /// let (x, y) = a.deinterleave(b); diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index ff1b2c756ad..2405c1429b3 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -156,8 +156,7 @@ where /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; /// let floats: Simd = Simd::from_array([1.9, -4.5, f32::INFINITY, f32::NAN]); /// let ints = floats.cast::(); /// assert_eq!(ints, Simd::from_array([1, -4, i32::MAX, 0])); @@ -184,8 +183,7 @@ where /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; /// let idxs = Simd::from_array([9, 3, 0, 5]); /// let alt = Simd::from_array([-5, -4, -3, -2]); @@ -205,8 +203,7 @@ where /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; /// let idxs = Simd::from_array([9, 3, 0, 5]); /// @@ -229,8 +226,7 @@ where /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::{Simd, Mask}; - /// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask}; + /// # use core::simd::{Simd, Mask}; /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; /// let idxs = Simd::from_array([9, 3, 0, 5]); /// let alt = Simd::from_array([-5, -4, -3, -2]); @@ -264,8 +260,7 @@ where /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::{Simd, Mask}; - /// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask}; + /// # use core::simd::{Simd, Mask}; /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; /// let idxs = Simd::from_array([9, 3, 0, 5]); /// let alt = Simd::from_array([-5, -4, -3, -2]); @@ -300,8 +295,7 @@ where /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::Simd; - /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// # use core::simd::Simd; /// let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; /// let idxs = Simd::from_array([9, 3, 0, 0]); /// let vals = Simd::from_array([-27, 82, -41, 124]); @@ -323,8 +317,7 @@ where /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::{Simd, Mask}; - /// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask}; + /// # use core::simd::{Simd, Mask}; /// let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; /// let idxs = Simd::from_array([9, 3, 0, 0]); /// let vals = Simd::from_array([-27, 82, -41, 124]); @@ -358,8 +351,7 @@ where /// # Examples /// ``` /// # #![feature(portable_simd)] - /// # #[cfg(feature = "std")] use core_simd::{Simd, Mask}; - /// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask}; + /// # use core::simd::{Simd, Mask}; /// let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; /// let idxs = Simd::from_array([9, 3, 0, 0]); /// let vals = Simd::from_array([-27, 82, -41, 124]); From adbd47973e7b5c1973d512c67ab8f373556202dc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 5 Mar 2022 19:07:41 -0500 Subject: [PATCH 06/13] reduce number of tests being run under Miri --- crates/test_helpers/src/lib.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs index 7edd6096381..8bf7f5ed3d2 100644 --- a/crates/test_helpers/src/lib.rs +++ b/crates/test_helpers/src/lib.rs @@ -77,11 +77,21 @@ impl DefaultStrategy } } +#[cfg(not(miri))] +fn make_runner() -> proptest::test_runner::TestRunner { + Default::default() +} +#[cfg(miri)] +fn make_runner() -> proptest::test_runner::TestRunner { + // Only run a few tests on Miri + proptest::test_runner::TestRunner::new(proptest::test_runner::Config::with_cases(4)) +} + /// Test a function that takes a single value. pub fn test_1( f: &dyn Fn(A) -> proptest::test_runner::TestCaseResult, ) { - let mut runner = proptest::test_runner::TestRunner::default(); + let mut runner = make_runner(); runner.run(&A::default_strategy(), f).unwrap(); } @@ -89,7 +99,7 @@ pub fn test_1( pub fn test_2( f: &dyn Fn(A, B) -> proptest::test_runner::TestCaseResult, ) { - let mut runner = proptest::test_runner::TestRunner::default(); + let mut runner = make_runner(); runner .run(&(A::default_strategy(), B::default_strategy()), |(a, b)| { f(a, b) @@ -105,7 +115,7 @@ pub fn test_3< >( f: &dyn Fn(A, B, C) -> proptest::test_runner::TestCaseResult, ) { - let mut runner = proptest::test_runner::TestRunner::default(); + let mut runner = make_runner(); runner .run( &( @@ -361,24 +371,28 @@ macro_rules! test_lanes { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow fn lanes_8() { implementation::<8>(); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow fn lanes_16() { implementation::<16>(); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow fn lanes_32() { implementation::<32>(); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow fn lanes_64() { implementation::<64>(); } From 4023d77e39c3af4a735b8d0974414ec06d5391c7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 9 Mar 2022 09:00:46 -0500 Subject: [PATCH 07/13] run Miri on CI (but allowed to fail) --- .github/workflows/ci.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d50dfa1be4c..54d74764790 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,6 +58,23 @@ jobs: - name: Run Clippy run: cargo clippy --all-targets --target ${{ matrix.target }} + miri: + name: "miri" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Miri + run: | + rustup toolchain install nightly --component miri + rustup override set nightly + cargo miri setup + - name: Test with Miri (failures allowed) + continue-on-error: true + run: | + cargo miri test --test i32_ops + cargo miri test --test f32_ops + cargo miri test --test cast + x86-tests: name: "${{ matrix.target_feature }} on ${{ matrix.target }}" runs-on: ${{ matrix.os }} From 86b9f69622badf863df71993e8b1e3468cf92eeb Mon Sep 17 00:00:00 2001 From: Jorge Leitao Date: Fri, 11 Mar 2022 00:12:40 +0100 Subject: [PATCH 08/13] rust-lang/portable-simd#260: Add `.min` and `.max` for integers --- crates/core_simd/src/comparisons.rs | 34 ++++++++++++++++++++++++++++ crates/core_simd/tests/i16_ops.rs | 15 ++++++++++++ crates/core_simd/tests/ops_macros.rs | 17 ++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/crates/core_simd/src/comparisons.rs b/crates/core_simd/src/comparisons.rs index d024cf4ddbe..88270a9b7e9 100644 --- a/crates/core_simd/src/comparisons.rs +++ b/crates/core_simd/src/comparisons.rs @@ -66,3 +66,37 @@ where unsafe { Mask::from_int_unchecked(intrinsics::simd_ge(self, other)) } } } + +macro_rules! impl_min_max_vector { + { $type:ty } => { + impl Simd<$type, LANES> + where + LaneCount: SupportedLaneCount, + { + /// Returns the lane-wise minimum with other + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + pub fn min(self, other: Self) -> Self { + self.lanes_gt(other).select(other, self) + } + + /// Returns the lane-wise maximum with other + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + pub fn max(self, other: Self) -> Self { + self.lanes_lt(other).select(other, self) + } + } + } +} + +impl_min_max_vector!(i8); +impl_min_max_vector!(i16); +impl_min_max_vector!(i32); +impl_min_max_vector!(i64); +impl_min_max_vector!(isize); +impl_min_max_vector!(u8); +impl_min_max_vector!(u16); +impl_min_max_vector!(u32); +impl_min_max_vector!(u64); +impl_min_max_vector!(usize); diff --git a/crates/core_simd/tests/i16_ops.rs b/crates/core_simd/tests/i16_ops.rs index f6c5d74fbbc..cd6cadc2d5e 100644 --- a/crates/core_simd/tests/i16_ops.rs +++ b/crates/core_simd/tests/i16_ops.rs @@ -1,5 +1,20 @@ #![feature(portable_simd)] +use core_simd::i16x2; #[macro_use] mod ops_macros; impl_signed_tests! { i16 } + +#[test] +fn max_is_not_lexicographic() { + let a = i16x2::splat(10); + let b = i16x2::from_array([-4, 12]); + assert_eq!(a.max(b), i16x2::from_array([10, 12])); +} + +#[test] +fn min_is_not_lexicographic() { + let a = i16x2::splat(10); + let b = i16x2::from_array([12, -4]); + assert_eq!(a.min(b), i16x2::from_array([10, -4])); +} diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index 50f7a4ca170..96da8c1b8dc 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -222,6 +222,23 @@ macro_rules! impl_signed_tests { assert_eq!(a % b, Vector::::splat(0)); } + fn min() { + let a = Vector::::splat(Scalar::MIN); + let b = Vector::::splat(0); + assert_eq!(a.min(b), a); + let a = Vector::::splat(Scalar::MAX); + let b = Vector::::splat(0); + assert_eq!(a.min(b), b); + } + + fn max() { + let a = Vector::::splat(Scalar::MIN); + let b = Vector::::splat(0); + assert_eq!(a.max(b), b); + let a = Vector::::splat(Scalar::MAX); + let b = Vector::::splat(0); + assert_eq!(a.max(b), a); + } } test_helpers::test_lanes_panic! { From c196b8abaafaf360647513c64fac0c8568f9b747 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 11 Mar 2022 14:49:06 -0800 Subject: [PATCH 09/13] replace horizontal_* with reduce_* --- crates/core_simd/examples/matrix_inversion.rs | 2 +- crates/core_simd/examples/nbody.rs | 8 ++-- crates/core_simd/examples/spectral_norm.rs | 4 +- crates/core_simd/src/reduction.rs | 44 +++++++++---------- crates/core_simd/tests/ops_macros.rs | 44 +++++++++---------- 5 files changed, 51 insertions(+), 51 deletions(-) diff --git a/crates/core_simd/examples/matrix_inversion.rs b/crates/core_simd/examples/matrix_inversion.rs index c51a566deb5..39f530f68f5 100644 --- a/crates/core_simd/examples/matrix_inversion.rs +++ b/crates/core_simd/examples/matrix_inversion.rs @@ -233,7 +233,7 @@ pub fn simd_inv4x4(m: Matrix4x4) -> Option { let det = det.rotate_lanes_right::<2>() + det; let det = det.reverse().rotate_lanes_right::<2>() + det; - if det.horizontal_sum() == 0. { + if det.reduce_sum() == 0. { return None; } // calculate the reciprocal diff --git a/crates/core_simd/examples/nbody.rs b/crates/core_simd/examples/nbody.rs index 7b1e6840f64..664a0454bbd 100644 --- a/crates/core_simd/examples/nbody.rs +++ b/crates/core_simd/examples/nbody.rs @@ -107,10 +107,10 @@ mod nbody { let mut e = 0.; for i in 0..N_BODIES { let bi = &bodies[i]; - e += bi.mass * (bi.v * bi.v).horizontal_sum() * 0.5; + e += bi.mass * (bi.v * bi.v).reduce_sum() * 0.5; for bj in bodies.iter().take(N_BODIES).skip(i + 1) { let dx = bi.x - bj.x; - e -= bi.mass * bj.mass / (dx * dx).horizontal_sum().sqrt() + e -= bi.mass * bj.mass / (dx * dx).reduce_sum().sqrt() } } e @@ -134,8 +134,8 @@ mod nbody { let mut mag = [0.0; N]; for i in (0..N).step_by(2) { let d2s = f64x2::from_array([ - (r[i] * r[i]).horizontal_sum(), - (r[i + 1] * r[i + 1]).horizontal_sum(), + (r[i] * r[i]).reduce_sum(), + (r[i + 1] * r[i + 1]).reduce_sum(), ]); let dmags = f64x2::splat(dt) / (d2s * d2s.sqrt()); mag[i] = dmags[0]; diff --git a/crates/core_simd/examples/spectral_norm.rs b/crates/core_simd/examples/spectral_norm.rs index c515dad4dea..012182e090b 100644 --- a/crates/core_simd/examples/spectral_norm.rs +++ b/crates/core_simd/examples/spectral_norm.rs @@ -20,7 +20,7 @@ fn mult_av(v: &[f64], out: &mut [f64]) { sum += b / a; j += 2 } - *out = sum.horizontal_sum(); + *out = sum.reduce_sum(); } } @@ -38,7 +38,7 @@ fn mult_atv(v: &[f64], out: &mut [f64]) { sum += b / a; j += 2 } - *out = sum.horizontal_sum(); + *out = sum.reduce_sum(); } } diff --git a/crates/core_simd/src/reduction.rs b/crates/core_simd/src/reduction.rs index e1cd743e442..3177fd167fc 100644 --- a/crates/core_simd/src/reduction.rs +++ b/crates/core_simd/src/reduction.rs @@ -11,30 +11,30 @@ macro_rules! impl_integer_reductions { where LaneCount: SupportedLaneCount, { - /// Horizontal wrapping add. Returns the sum of the lanes of the vector, with wrapping addition. + /// Reducing wrapping add. Returns the sum of the lanes of the vector, with wrapping addition. #[inline] - pub fn horizontal_sum(self) -> $scalar { + pub fn reduce_sum(self) -> $scalar { // Safety: `self` is an integer vector unsafe { simd_reduce_add_ordered(self, 0) } } - /// Horizontal wrapping multiply. Returns the product of the lanes of the vector, with wrapping multiplication. + /// Reducing wrapping multiply. Returns the product of the lanes of the vector, with wrapping multiplication. #[inline] - pub fn horizontal_product(self) -> $scalar { + pub fn reduce_product(self) -> $scalar { // Safety: `self` is an integer vector unsafe { simd_reduce_mul_ordered(self, 1) } } - /// Horizontal maximum. Returns the maximum lane in the vector. + /// Reducing maximum. Returns the maximum lane in the vector. #[inline] - pub fn horizontal_max(self) -> $scalar { + pub fn reduce_max(self) -> $scalar { // Safety: `self` is an integer vector unsafe { simd_reduce_max(self) } } - /// Horizontal minimum. Returns the minimum lane in the vector. + /// Reducing minimum. Returns the minimum lane in the vector. #[inline] - pub fn horizontal_min(self) -> $scalar { + pub fn reduce_min(self) -> $scalar { // Safety: `self` is an integer vector unsafe { simd_reduce_min(self) } } @@ -60,9 +60,9 @@ macro_rules! impl_float_reductions { LaneCount: SupportedLaneCount, { - /// Horizontal add. Returns the sum of the lanes of the vector. + /// Reducing add. Returns the sum of the lanes of the vector. #[inline] - pub fn horizontal_sum(self) -> $scalar { + pub fn reduce_sum(self) -> $scalar { // LLVM sum is inaccurate on i586 if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) { self.as_array().iter().sum() @@ -72,9 +72,9 @@ macro_rules! impl_float_reductions { } } - /// Horizontal multiply. Returns the product of the lanes of the vector. + /// Reducing multiply. Returns the product of the lanes of the vector. #[inline] - pub fn horizontal_product(self) -> $scalar { + pub fn reduce_product(self) -> $scalar { // LLVM product is inaccurate on i586 if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) { self.as_array().iter().product() @@ -84,22 +84,22 @@ macro_rules! impl_float_reductions { } } - /// Horizontal maximum. Returns the maximum lane in the vector. + /// Reducing maximum. Returns the maximum lane in the vector. /// /// Returns values based on equality, so a vector containing both `0.` and `-0.` may /// return either. This function will not return `NaN` unless all lanes are `NaN`. #[inline] - pub fn horizontal_max(self) -> $scalar { + pub fn reduce_max(self) -> $scalar { // Safety: `self` is a float vector unsafe { simd_reduce_max(self) } } - /// Horizontal minimum. Returns the minimum lane in the vector. + /// Reducing minimum. Returns the minimum lane in the vector. /// /// Returns values based on equality, so a vector containing both `0.` and `-0.` may /// return either. This function will not return `NaN` unless all lanes are `NaN`. #[inline] - pub fn horizontal_min(self) -> $scalar { + pub fn reduce_min(self) -> $scalar { // Safety: `self` is a float vector unsafe { simd_reduce_min(self) } } @@ -116,10 +116,10 @@ where T: SimdElement + BitAnd, LaneCount: SupportedLaneCount, { - /// Horizontal bitwise "and". Returns the cumulative bitwise "and" across the lanes of + /// Reducing bitwise "and". Returns the cumulative bitwise "and" across the lanes of /// the vector. #[inline] - pub fn horizontal_and(self) -> T { + pub fn reduce_and(self) -> T { unsafe { simd_reduce_and(self) } } } @@ -130,10 +130,10 @@ where T: SimdElement + BitOr, LaneCount: SupportedLaneCount, { - /// Horizontal bitwise "or". Returns the cumulative bitwise "or" across the lanes of + /// Reducing bitwise "or". Returns the cumulative bitwise "or" across the lanes of /// the vector. #[inline] - pub fn horizontal_or(self) -> T { + pub fn reduce_or(self) -> T { unsafe { simd_reduce_or(self) } } } @@ -144,10 +144,10 @@ where T: SimdElement + BitXor, LaneCount: SupportedLaneCount, { - /// Horizontal bitwise "xor". Returns the cumulative bitwise "xor" across the lanes of + /// Reducing bitwise "xor". Returns the cumulative bitwise "xor" across the lanes of /// the vector. #[inline] - pub fn horizontal_xor(self) -> T { + pub fn reduce_xor(self) -> T { unsafe { simd_reduce_xor(self) } } } diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index 96da8c1b8dc..0b3f8979a37 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -94,70 +94,70 @@ macro_rules! impl_binary_checked_op_test { macro_rules! impl_common_integer_tests { { $vector:ident, $scalar:ident } => { test_helpers::test_lanes! { - fn horizontal_sum() { + fn reduce_sum() { test_helpers::test_1(&|x| { test_helpers::prop_assert_biteq! ( - $vector::::from_array(x).horizontal_sum(), + $vector::::from_array(x).reduce_sum(), x.iter().copied().fold(0 as $scalar, $scalar::wrapping_add), ); Ok(()) }); } - fn horizontal_product() { + fn reduce_product() { test_helpers::test_1(&|x| { test_helpers::prop_assert_biteq! ( - $vector::::from_array(x).horizontal_product(), + $vector::::from_array(x).reduce_product(), x.iter().copied().fold(1 as $scalar, $scalar::wrapping_mul), ); Ok(()) }); } - fn horizontal_and() { + fn reduce_and() { test_helpers::test_1(&|x| { test_helpers::prop_assert_biteq! ( - $vector::::from_array(x).horizontal_and(), + $vector::::from_array(x).reduce_and(), x.iter().copied().fold(-1i8 as $scalar, <$scalar as core::ops::BitAnd>::bitand), ); Ok(()) }); } - fn horizontal_or() { + fn reduce_or() { test_helpers::test_1(&|x| { test_helpers::prop_assert_biteq! ( - $vector::::from_array(x).horizontal_or(), + $vector::::from_array(x).reduce_or(), x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitOr>::bitor), ); Ok(()) }); } - fn horizontal_xor() { + fn reduce_xor() { test_helpers::test_1(&|x| { test_helpers::prop_assert_biteq! ( - $vector::::from_array(x).horizontal_xor(), + $vector::::from_array(x).reduce_xor(), x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitXor>::bitxor), ); Ok(()) }); } - fn horizontal_max() { + fn reduce_max() { test_helpers::test_1(&|x| { test_helpers::prop_assert_biteq! ( - $vector::::from_array(x).horizontal_max(), + $vector::::from_array(x).reduce_max(), x.iter().copied().max().unwrap(), ); Ok(()) }); } - fn horizontal_min() { + fn reduce_min() { test_helpers::test_1(&|x| { test_helpers::prop_assert_biteq! ( - $vector::::from_array(x).horizontal_min(), + $vector::::from_array(x).reduce_min(), x.iter().copied().min().unwrap(), ); Ok(()) @@ -516,29 +516,29 @@ macro_rules! impl_float_tests { }) } - fn horizontal_sum() { + fn reduce_sum() { test_helpers::test_1(&|x| { test_helpers::prop_assert_biteq! ( - Vector::::from_array(x).horizontal_sum(), + Vector::::from_array(x).reduce_sum(), x.iter().sum(), ); Ok(()) }); } - fn horizontal_product() { + fn reduce_product() { test_helpers::test_1(&|x| { test_helpers::prop_assert_biteq! ( - Vector::::from_array(x).horizontal_product(), + Vector::::from_array(x).reduce_product(), x.iter().product(), ); Ok(()) }); } - fn horizontal_max() { + fn reduce_max() { test_helpers::test_1(&|x| { - let vmax = Vector::::from_array(x).horizontal_max(); + let vmax = Vector::::from_array(x).reduce_max(); let smax = x.iter().copied().fold(Scalar::NAN, Scalar::max); // 0 and -0 are treated the same if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) { @@ -548,9 +548,9 @@ macro_rules! impl_float_tests { }); } - fn horizontal_min() { + fn reduce_min() { test_helpers::test_1(&|x| { - let vmax = Vector::::from_array(x).horizontal_min(); + let vmax = Vector::::from_array(x).reduce_min(); let smax = x.iter().copied().fold(Scalar::NAN, Scalar::min); // 0 and -0 are treated the same if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) { From b6e03f58864dde979dbe97b7d983d0ba29b16227 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 11 Mar 2022 14:54:36 -0800 Subject: [PATCH 10/13] Change beginner's guide to explain Reducing rather than Horizontal. --- beginners-guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beginners-guide.md b/beginners-guide.md index dfd357c4592..75158e5aa85 100644 --- a/beginners-guide.md +++ b/beginners-guide.md @@ -33,7 +33,7 @@ SIMD has a few special vocabulary terms you should know: * **Vertical:** When an operation is "vertical", each lane processes individually without regard to the other lanes in the same vector. For example, a "vertical add" between two vectors would add lane 0 in `a` with lane 0 in `b`, with the total in lane 0 of `out`, and then the same thing for lanes 1, 2, etc. Most SIMD operations are vertical operations, so if your problem is a vertical problem then you can probably solve it with SIMD. -* **Horizontal:** When an operation is "horizontal", the lanes within a single vector interact in some way. A "horizontal add" might add up lane 0 of `a` with lane 1 of `a`, with the total in lane 0 of `out`. +* **Reducing/Reduce:** When an operation is "reducing" (functions named `reduce_*`), the lanes within a single vector are merged using some operation such as addition, returning the merged value as a scalar. For instance, a reducing add would return the sum of all the lanes' values. * **Target Feature:** Rust calls a CPU architecture extension a `target_feature`. Proper SIMD requires various CPU extensions to be enabled (details below). Don't confuse this with `feature`, which is a Cargo crate concept. @@ -83,4 +83,4 @@ Fortunately, most SIMD types have a fairly predictable size. `i32x4` is bit-equi However, this is not the same as alignment. Computer architectures generally prefer aligned accesses, especially when moving data between memory and vector registers, and while some support specialized operations that can bend the rules to help with this, unaligned access is still typically slow, or even undefined behavior. In addition, different architectures can require different alignments when interacting with their native SIMD types. For this reason, any `#[repr(simd)]` type has a non-portable alignment. If it is necessary to directly interact with the alignment of these types, it should be via [`mem::align_of`]. [`mem::transmute`]: https://doc.rust-lang.org/core/mem/fn.transmute.html -[`mem::align_of`]: https://doc.rust-lang.org/core/mem/fn.align_of.html \ No newline at end of file +[`mem::align_of`]: https://doc.rust-lang.org/core/mem/fn.align_of.html From b6ee5293f43a6e9ec1c0d977a4037462bd4d02ca Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 11 Mar 2022 21:14:52 -0500 Subject: [PATCH 11/13] rust-lang/portable-simd#259: remove Miri from CI --- .github/workflows/ci.yml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54d74764790..d50dfa1be4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,23 +58,6 @@ jobs: - name: Run Clippy run: cargo clippy --all-targets --target ${{ matrix.target }} - miri: - name: "miri" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install Miri - run: | - rustup toolchain install nightly --component miri - rustup override set nightly - cargo miri setup - - name: Test with Miri (failures allowed) - continue-on-error: true - run: | - cargo miri test --test i32_ops - cargo miri test --test f32_ops - cargo miri test --test cast - x86-tests: name: "${{ matrix.target_feature }} on ${{ matrix.target }}" runs-on: ${{ matrix.os }} From 49043f4434b02d0a8a68c87d7672cc2e944a3deb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 12 Mar 2022 18:32:28 -0500 Subject: [PATCH 12/13] rust-lang/portable-simd#262: also implement clamp for integer vectors * add test from issue rust-lang/portable-simd#253 --- crates/core_simd/src/comparisons.rs | 44 ++++++++++++++++++++-------- crates/core_simd/tests/i16_ops.rs | 12 ++++++++ crates/core_simd/tests/ops_macros.rs | 12 ++++++++ 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/crates/core_simd/src/comparisons.rs b/crates/core_simd/src/comparisons.rs index 88270a9b7e9..7b0d0a6864b 100644 --- a/crates/core_simd/src/comparisons.rs +++ b/crates/core_simd/src/comparisons.rs @@ -67,36 +67,54 @@ where } } -macro_rules! impl_min_max_vector { +macro_rules! impl_ord_methods_vector { { $type:ty } => { impl Simd<$type, LANES> where LaneCount: SupportedLaneCount, { - /// Returns the lane-wise minimum with other + /// Returns the lane-wise minimum with `other`. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] pub fn min(self, other: Self) -> Self { self.lanes_gt(other).select(other, self) } - /// Returns the lane-wise maximum with other + /// Returns the lane-wise maximum with `other`. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] pub fn max(self, other: Self) -> Self { self.lanes_lt(other).select(other, self) } + + /// Restrict each lane to a certain interval. + /// + /// For each lane, returns `max` if `self` is greater than `max`, and `min` if `self` is + /// less than `min`. Otherwise returns `self`. + /// + /// # Panics + /// + /// Panics if `min > max` on any lane. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + pub fn clamp(self, min: Self, max: Self) -> Self { + assert!( + min.lanes_le(max).all(), + "each lane in `min` must be less than or equal to the corresponding lane in `max`", + ); + self.max(min).min(max) + } } } } -impl_min_max_vector!(i8); -impl_min_max_vector!(i16); -impl_min_max_vector!(i32); -impl_min_max_vector!(i64); -impl_min_max_vector!(isize); -impl_min_max_vector!(u8); -impl_min_max_vector!(u16); -impl_min_max_vector!(u32); -impl_min_max_vector!(u64); -impl_min_max_vector!(usize); +impl_ord_methods_vector!(i8); +impl_ord_methods_vector!(i16); +impl_ord_methods_vector!(i32); +impl_ord_methods_vector!(i64); +impl_ord_methods_vector!(isize); +impl_ord_methods_vector!(u8); +impl_ord_methods_vector!(u16); +impl_ord_methods_vector!(u32); +impl_ord_methods_vector!(u64); +impl_ord_methods_vector!(usize); diff --git a/crates/core_simd/tests/i16_ops.rs b/crates/core_simd/tests/i16_ops.rs index cd6cadc2d5e..171e5b472fa 100644 --- a/crates/core_simd/tests/i16_ops.rs +++ b/crates/core_simd/tests/i16_ops.rs @@ -18,3 +18,15 @@ fn min_is_not_lexicographic() { let b = i16x2::from_array([12, -4]); assert_eq!(a.min(b), i16x2::from_array([10, -4])); } + +#[test] +fn clamp_is_not_lexicographic() { + let a = i16x2::splat(10); + let lo = i16x2::from_array([-12, -4]); + let up = i16x2::from_array([-4, 12]); + assert_eq!(a.clamp(lo, up), i16x2::from_array([-4, 10])); + + let x = i16x2::from_array([1, 10]); + let y = x.clamp(i16x2::splat(0), i16x2::splat(9)); + assert_eq!(y, i16x2::from_array([1, 9])); +} diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index 96da8c1b8dc..bea02750ef2 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -239,6 +239,18 @@ macro_rules! impl_signed_tests { let b = Vector::::splat(0); assert_eq!(a.max(b), a); } + + fn clamp() { + let min = Vector::::splat(Scalar::MIN); + let max = Vector::::splat(Scalar::MAX); + let zero = Vector::::splat(0); + let one = Vector::::splat(1); + let negone = Vector::::splat(-1); + assert_eq!(zero.clamp(min, max), zero); + assert_eq!(zero.clamp(min, one), zero); + assert_eq!(zero.clamp(one, max), one); + assert_eq!(zero.clamp(min, negone), negone); + } } test_helpers::test_lanes_panic! { From 2b1f249ecf4a3b52bdac520a641c502be5048f8a Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Sat, 12 Mar 2022 16:43:38 -0800 Subject: [PATCH 13/13] Use reduce_sum in as_simd example --- library/core/src/slice/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 32d8988f149..c0eb000e877 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3536,7 +3536,7 @@ impl [T] { /// suffix.iter().copied().sum(), /// ]); /// let sums = middle.iter().copied().fold(sums, f32x4::add); - /// sums.horizontal_sum() + /// sums.reduce_sum() /// } /// /// let numbers: Vec = (1..101).map(|x| x as _).collect();