rust-lang/portable-simd#273: Documentation update for reduce functions, swizzle

Working through giving example documentation to every Simd function.

The major change in this patch is using doc macros to generate
type-specific examples for each function, using a visually-apparent type
constructor. This makes it feel nicer to have twelve separate
documentation entries for reduce_product(), for example.
This commit is contained in:
Sean Stangl 2022-04-11 00:08:34 -06:00 committed by GitHub
parent 7136841cbd
commit fcc5ca0f93
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 195 additions and 33 deletions

View file

@ -3,7 +3,7 @@ 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> {
@ -11,7 +11,11 @@ impl<const LANES: usize> LaneCount<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]>;

View file

@ -12,13 +12,41 @@ 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
@ -26,6 +54,16 @@ macro_rules! impl_integer_reductions {
}
/// 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
@ -33,6 +71,16 @@ macro_rules! impl_integer_reductions {
}
/// 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
@ -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
@ -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
@ -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
@ -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

View file

@ -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 {
@ -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),
}

View file

@ -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
}
@ -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!(
@ -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,