Skip to content

Documentation update for reduce functions, swizzle #273

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions crates/core_simd/src/lane_count.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ mod sealed {
}
use sealed::Sealed;

/// A type representing a vector lane count.
/// Specifies the number of lanes in a SIMD vector as a type.
pub struct LaneCount<const LANES: usize>;

impl<const LANES: usize> LaneCount<LANES> {
/// The number of bytes in a bitmask with this many lanes.
pub const BITMASK_LEN: usize = (LANES + 7) / 8;
}

/// Helper trait for vector lane counts.
/// Statically guarantees that a lane count is marked as supported.
///
/// This trait is *sealed*: the list of implementors below is total.
/// Users do not have the ability to mark additional `LaneCount<N>` values as supported.
/// Only SIMD vectors with supported lane counts are constructable.
pub trait SupportedLaneCount: Sealed {
#[doc(hidden)]
type BitMask: Copy + Default + AsRef<[u8]> + AsMut<[u8]>;
Expand Down
120 changes: 117 additions & 3 deletions crates/core_simd/src/reduction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,75 @@ macro_rules! impl_integer_reductions {
LaneCount<LANES>: SupportedLaneCount,
{
/// Reducing wrapping add. Returns the sum of the lanes of the vector, with wrapping addition.
///
/// # Examples
///
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::Simd;
#[doc = concat!("# use core::simd::", stringify!($scalar), "x4;")]
#[doc = concat!("let v = ", stringify!($scalar), "x4::from_array([1, 2, 3, 4]);")]
/// assert_eq!(v.reduce_sum(), 10);
///
/// // SIMD integer addition is always wrapping
#[doc = concat!("let v = ", stringify!($scalar), "x4::from_array([", stringify!($scalar) ,"::MAX, 1, 0, 0]);")]
#[doc = concat!("assert_eq!(v.reduce_sum(), ", stringify!($scalar), "::MIN);")]
/// ```
#[inline]
pub fn reduce_sum(self) -> $scalar {
// Safety: `self` is an integer vector
unsafe { simd_reduce_add_ordered(self, 0) }
}

/// Reducing 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.
///
/// # Examples
///
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::Simd;
#[doc = concat!("# use core::simd::", stringify!($scalar), "x4;")]
#[doc = concat!("let v = ", stringify!($scalar), "x4::from_array([1, 2, 3, 4]);")]
/// assert_eq!(v.reduce_product(), 24);
///
/// // SIMD integer multiplication is always wrapping
#[doc = concat!("let v = ", stringify!($scalar), "x4::from_array([", stringify!($scalar) ,"::MAX, 2, 1, 1]);")]
#[doc = concat!("assert!(v.reduce_product() < ", stringify!($scalar), "::MAX);")]
/// ```
#[inline]
pub fn reduce_product(self) -> $scalar {
// Safety: `self` is an integer vector
unsafe { simd_reduce_mul_ordered(self, 1) }
}

/// Reducing maximum. Returns the maximum lane in the vector.
///
/// # Examples
///
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::Simd;
#[doc = concat!("# use core::simd::", stringify!($scalar), "x4;")]
#[doc = concat!("let v = ", stringify!($scalar), "x4::from_array([1, 2, 3, 4]);")]
/// assert_eq!(v.reduce_max(), 4);
/// ```
#[inline]
pub fn reduce_max(self) -> $scalar {
// Safety: `self` is an integer vector
unsafe { simd_reduce_max(self) }
}

/// Reducing minimum. Returns the minimum lane in the vector.
///
/// # Examples
///
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::Simd;
#[doc = concat!("# use core::simd::", stringify!($scalar), "x4;")]
#[doc = concat!("let v = ", stringify!($scalar), "x4::from_array([1, 2, 3, 4]);")]
/// assert_eq!(v.reduce_min(), 1);
/// ```
#[inline]
pub fn reduce_min(self) -> $scalar {
// Safety: `self` is an integer vector
Expand Down Expand Up @@ -61,6 +109,16 @@ macro_rules! impl_float_reductions {
{

/// Reducing add. Returns the sum of the lanes of the vector.
///
/// # Examples
///
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::Simd;
#[doc = concat!("# use core::simd::", stringify!($scalar), "x2;")]
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([1., 2.]);")]
/// assert_eq!(v.reduce_sum(), 3.);
/// ```
#[inline]
pub fn reduce_sum(self) -> $scalar {
// LLVM sum is inaccurate on i586
Expand All @@ -73,6 +131,16 @@ macro_rules! impl_float_reductions {
}

/// Reducing multiply. Returns the product of the lanes of the vector.
///
/// # Examples
///
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::Simd;
#[doc = concat!("# use core::simd::", stringify!($scalar), "x2;")]
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([3., 4.]);")]
/// assert_eq!(v.reduce_product(), 12.);
/// ```
#[inline]
pub fn reduce_product(self) -> $scalar {
// LLVM product is inaccurate on i586
Expand All @@ -87,7 +155,30 @@ macro_rules! impl_float_reductions {
/// 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`.
/// return either.
///
/// This function will not return `NaN` unless all lanes are `NaN`.
///
/// # Examples
///
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::Simd;
#[doc = concat!("# use core::simd::", stringify!($scalar), "x2;")]
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([1., 2.]);")]
/// assert_eq!(v.reduce_max(), 2.);
///
/// // NaN values are skipped...
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([1., ", stringify!($scalar), "::NAN]);")]
/// assert_eq!(v.reduce_max(), 1.);
///
/// // ...unless all values are NaN
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([",
stringify!($scalar), "::NAN, ",
stringify!($scalar), "::NAN]);"
)]
/// assert!(v.reduce_max().is_nan());
/// ```
#[inline]
pub fn reduce_max(self) -> $scalar {
// Safety: `self` is a float vector
Expand All @@ -97,7 +188,30 @@ macro_rules! impl_float_reductions {
/// 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`.
/// return either.
///
/// This function will not return `NaN` unless all lanes are `NaN`.
///
/// # Examples
///
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::Simd;
#[doc = concat!("# use core::simd::", stringify!($scalar), "x2;")]
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([3., 7.]);")]
/// assert_eq!(v.reduce_min(), 3.);
///
/// // NaN values are skipped...
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([1., ", stringify!($scalar), "::NAN]);")]
/// assert_eq!(v.reduce_min(), 1.);
///
/// // ...unless all values are NaN
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([",
stringify!($scalar), "::NAN, ",
stringify!($scalar), "::NAN]);"
)]
/// assert!(v.reduce_min().is_nan());
/// ```
#[inline]
pub fn reduce_min(self) -> $scalar {
// Safety: `self` is a float vector
Expand Down
52 changes: 28 additions & 24 deletions crates/core_simd/src/swizzle.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,46 @@
use crate::simd::intrinsics;
use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};

/// Constructs a new vector by selecting values from the lanes of the source vector or vectors to use.
/// Constructs a new SIMD vector by copying elements from selected lanes in other vectors.
///
/// When swizzling one vector, the indices of the result vector are indicated by a `const` array
/// of `usize`, like [`Swizzle`].
/// When swizzling two vectors, the indices are indicated by a `const` array of [`Which`], like
/// [`Swizzle2`].
/// When swizzling one vector, lanes are selected by a `const` array of `usize`,
/// like [`Swizzle`].
///
/// When swizzling two vectors, lanes are selected by a `const` array of [`Which`],
/// like [`Swizzle2`].
///
/// # Examples
/// ## One source vector
///
/// With a single SIMD vector, the const array specifies lane indices in that vector:
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::{Simd, simd_swizzle};
/// let v = Simd::<f32, 4>::from_array([0., 1., 2., 3.]);
/// # use core::simd::{u32x2, u32x4, simd_swizzle};
/// let v = u32x4::from_array([10, 11, 12, 13]);
///
/// // Keeping the same size
/// let r = simd_swizzle!(v, [3, 0, 1, 2]);
/// assert_eq!(r.to_array(), [3., 0., 1., 2.]);
/// let r: u32x4 = simd_swizzle!(v, [3, 0, 1, 2]);
/// assert_eq!(r.to_array(), [13, 10, 11, 12]);
///
/// // Changing the number of lanes
/// let r = simd_swizzle!(v, [3, 1]);
/// assert_eq!(r.to_array(), [3., 1.]);
/// let r: u32x2 = simd_swizzle!(v, [3, 1]);
/// assert_eq!(r.to_array(), [13, 11]);
/// ```
///
/// ## Two source vectors
/// With two input SIMD vectors, the const array uses `Which` to specify the source of each index:
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::{Simd, simd_swizzle, Which};
/// use Which::*;
/// let a = Simd::<f32, 4>::from_array([0., 1., 2., 3.]);
/// let b = Simd::<f32, 4>::from_array([4., 5., 6., 7.]);
/// # use core::simd::{u32x2, u32x4, simd_swizzle, Which};
/// use Which::{First, Second};
/// let a = u32x4::from_array([0, 1, 2, 3]);
/// let b = u32x4::from_array([4, 5, 6, 7]);
///
/// // Keeping the same size
/// let r = simd_swizzle!(a, b, [First(0), First(1), Second(2), Second(3)]);
/// assert_eq!(r.to_array(), [0., 1., 6., 7.]);
/// let r: u32x4 = simd_swizzle!(a, b, [First(0), First(1), Second(2), Second(3)]);
/// assert_eq!(r.to_array(), [0, 1, 6, 7]);
///
/// // Changing the number of lanes
/// let r = simd_swizzle!(a, b, [First(0), Second(0)]);
/// assert_eq!(r.to_array(), [0., 4.]);
/// let r: u32x2 = simd_swizzle!(a, b, [First(0), Second(0)]);
/// assert_eq!(r.to_array(), [0, 4]);
/// ```
#[allow(unused_macros)]
pub macro simd_swizzle {
Expand Down Expand Up @@ -68,12 +70,14 @@ pub macro simd_swizzle {
}
}

/// An index into one of two vectors.
/// Specifies a lane index into one of two SIMD vectors.
///
/// This is an input type for [Swizzle2] and helper macros like [simd_swizzle].
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Which {
/// Indexes the first vector.
/// Index of a lane in the first input SIMD vector.
First(usize),
/// Indexes the second vector.
/// Index of a lane in the second input SIMD vector.
Second(usize),
}

Expand Down
48 changes: 44 additions & 4 deletions crates/core_simd/src/vector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,44 @@ where
/// Number of lanes in this vector.
pub const LANES: usize = LANES;

/// Get the number of lanes in this vector.
/// Returns the number of lanes in this SIMD vector.
///
/// # Examples
///
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::u32x4;
/// let v = u32x4::splat(0);
/// assert_eq!(v.lanes(), 4);
/// ```
pub const fn lanes(&self) -> usize {
LANES
}

/// Construct a SIMD vector by setting all lanes to the given value.
/// Constructs a new SIMD vector with all lanes set to the given value.
///
/// # Examples
///
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::u32x4;
/// let v = u32x4::splat(8);
/// assert_eq!(v.as_array(), &[8, 8, 8, 8]);
/// ```
pub const fn splat(value: T) -> Self {
Self([value; LANES])
}

/// Returns an array reference containing the entire SIMD vector.
///
/// # Examples
///
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::{Simd, u64x4};
/// let v: u64x4 = Simd::from_array([0, 1, 2, 3]);
/// assert_eq!(v.as_array(), &[0, 1, 2, 3]);
/// ```
pub const fn as_array(&self) -> &[T; LANES] {
&self.0
}
Expand All @@ -129,9 +156,21 @@ where
self.0
}

/// Converts a slice to a SIMD vector containing `slice[..LANES]`
/// Converts a slice to a SIMD vector containing `slice[..LANES]`.
///
/// # Panics
/// `from_slice` will panic if the slice's `len` is less than the vector's `Simd::LANES`.
///
/// Panics if the slice's length is less than the vector's `Simd::LANES`.
///
/// # Examples
///
/// ```
/// # #![feature(portable_simd)]
/// # use core::simd::{Simd, u32x4};
/// let source = vec![1, 2, 3, 4, 5, 6];
/// let v = u32x4::from_slice(&source);
/// assert_eq!(v.as_array(), &[1, 2, 3, 4]);
/// ```
#[must_use]
pub const fn from_slice(slice: &[T]) -> Self {
assert!(
Expand All @@ -148,6 +187,7 @@ where
}

/// Performs lanewise conversion of a SIMD vector's elements to another SIMD-valid type.
///
/// This follows the semantics of Rust's `as` conversion for casting
/// integers to unsigned integers (interpreting as the other type, so `-1` to `MAX`),
/// and from floats to integers (truncating, or saturating at the limits) for each lane,
Expand Down