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:
parent
7136841cbd
commit
fcc5ca0f93
4 changed files with 195 additions and 33 deletions
|
|
@ -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]>;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue