Skip to content
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

Add [T]::as_simd(_mut) #91479

Merged
merged 2 commits into from
Dec 15, 2021
Merged
Changes from 1 commit
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
83 changes: 83 additions & 0 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use crate::option::Option::{None, Some};
use crate::ptr;
use crate::result::Result;
use crate::result::Result::{Err, Ok};
#[cfg(not(miri))] // Miri does not support all SIMD intrinsics
use crate::simd::{self, Simd};
use crate::slice;

#[unstable(
Expand Down Expand Up @@ -3434,6 +3436,87 @@ impl<T> [T] {
}
}

/// Split a slice into a prefix, a middle of aligned simd types, and a suffix.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Split a slice into a prefix, a middle of aligned simd types, and a suffix.
/// Split a slice into a prefix, a middle of aligned SIMD types, and a suffix.

///
/// This is a safe wrapper around [`slice::align_to`], so has the same weak
/// preconditions as that method. Notably, you must not assume any particular
/// split between the three parts: it's legal for the middle slice to be
/// empty even if the input slice is longer than `3 * LANES`.
Copy link
Member

@workingjubilee workingjubilee Dec 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this wrapper is safe, this should clarify that this is an invalid assumption for unsafe code, as the function can no longer be relied upon to maintain invariants in the same way. Safe code trying to "rely" on it would be "merely" logically incorrect.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I wrote "preconditions" here when I meant "postconditions" 🤦

///
/// # Examples
///
/// ```
/// #![feature(portable_simd)]
///
/// let short = &[1, 2, 3];
/// let (prefix, middle, suffix) = short.as_simd::<4>();
/// assert_eq!(middle, []); // Not enough elements for anything in the middle
///
/// // They might be split in any possible way between prefix and suffix
/// let it = prefix.iter().chain(suffix).copied();
/// assert_eq!(it.collect::<Vec<_>>(), vec![1, 2, 3]);
///
/// fn basic_simd_sum(x: &[f32]) -> f32 {
/// use std::ops::Add;
/// use std::simd::f32x4;
/// let (prefix, middle, suffix) = x.as_simd();
/// let sums = f32x4::from_array([
/// prefix.iter().copied().sum(),
/// 0.0,
/// 0.0,
/// suffix.iter().copied().sum(),
/// ]);
/// let sums = middle.iter().copied().fold(sums, f32x4::add);
/// sums.horizontal_sum()
/// }
///
/// let numbers: Vec<f32> = (1..101).map(|x| x as _).collect();
/// assert_eq!(basic_simd_sum(&numbers[1..99]), 4949.0);
/// ```
#[unstable(feature = "portable_simd", issue = "86656")]
#[cfg(not(miri))] // Miri does not support all SIMD intrinsics
pub fn as_simd<const LANES: usize>(&self) -> (&[T], &[Simd<T, LANES>], &[T])
where
Simd<T, LANES>: AsRef<[T; LANES]>,
T: simd::SimdElement,
simd::LaneCount<LANES>: simd::SupportedLaneCount,
{
// These are expected to always match, as vector types are laid out like
// arrays per <https://llvm.org/docs/LangRef.html#vector-type>, but we
// might as well double-check since it'll optimize away anyhow.
assert_eq!(mem::size_of::<Simd<T, LANES>>(), mem::size_of::<[T; LANES]>());

// SAFETY: The simd types have the same layout as arrays, just with
// potentially-higher alignment, so the de-facto transmutes are sound.
unsafe { self.align_to() }
Comment on lines +3507 to +3509
Copy link
Member

@workingjubilee workingjubilee Dec 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sound, yes, but it would likely exhibit unexpected behavior if LANES is an odd number (like 3), as that would potentially result in bytes no longer being read (because the stride of the type will be as if LANES is 4). This is not supported yet, but we have not yet ruled this possibility out. This function therefore introduces an unanswered question. Should we:

  • allow people to transmute data into padding
  • add additional bounds on this if we relax those on LANES
  • rule out types like Simd<f32, 3> entirely?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I guess my assert_eq on the sizes (line 3487) is more useful than I'd thought, as if 3-simd is padded out to 4-simd it'd make the function just panic instead of skipping things.

The other possibility would be to have it just return everything in the prefix for that case, since align_to is allowed to do that anyway (for MIRI), and it'd just be slower than desired if someone uses the non-power-of-two sizes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't consider that, but it is indeed also an option!
This is definitely a bridge we can cross when we actually come to it, I just wanted to note the bridge is indeed up ahead.

}

/// Split a slice into a prefix, a middle of aligned simd types, and a suffix.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Split a slice into a prefix, a middle of aligned simd types, and a suffix.
/// Split a slice into a prefix, a middle of aligned SIMD types, and a suffix.

///
/// This is a safe wrapper around [`slice::align_to`], so has the same weak
/// preconditions as that method. Notably, you must not assume any particular
/// split between the three parts: it's legal for the middle slice to be
/// empty even if the input slice is longer than `3 * LANES`.
///
/// This is the mutable version of [`slice::as_simd`]; see that for more.
#[unstable(feature = "portable_simd", issue = "86656")]
#[cfg(not(miri))] // Miri does not support all SIMD intrinsics
pub fn as_simd_mut<const LANES: usize>(&mut self) -> (&mut [T], &mut [Simd<T, LANES>], &mut [T])
where
Simd<T, LANES>: AsMut<[T; LANES]>,
T: simd::SimdElement,
simd::LaneCount<LANES>: simd::SupportedLaneCount,
{
// These are expected to always match, as vector types are laid out like
// arrays per <https://llvm.org/docs/LangRef.html#vector-type>, but we
// might as well double-check since it'll optimize away anyhow.
assert_eq!(mem::size_of::<Simd<T, LANES>>(), mem::size_of::<[T; LANES]>());

// SAFETY: The simd types have the same layout as arrays, just with
// potentially-higher alignment, so the de-facto transmutes are sound.
unsafe { self.align_to_mut() }
}

/// Checks if the elements of this slice are sorted.
///
/// That is, for each element `a` and its following element `b`, `a <= b` must hold. If the
Expand Down