From bd7c794f526c55b5b5d38143bb8d6d1d4b13b946 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Fri, 7 Jul 2023 04:07:00 -0700 Subject: [PATCH 001/285] Sync portable-simd to 2023 July 07 Sync up to rust-lang/portable-simd@7c7dbe0c505ccbc02ff30c1e37381ab1d47bf46f --- .github/workflows/ci.yml | 9 ++- crates/core_simd/src/cast.rs | 74 +++++++++--------- crates/core_simd/src/elements/const_ptr.rs | 30 ++++++- crates/core_simd/src/elements/float.rs | 67 +++++++++++++++- crates/core_simd/src/elements/int.rs | 19 ++++- crates/core_simd/src/elements/mut_ptr.rs | 30 ++++++- crates/core_simd/src/elements/uint.rs | 19 ++++- crates/core_simd/src/iter.rs | 4 + crates/core_simd/src/lib.rs | 2 +- crates/core_simd/src/masks.rs | 4 + crates/core_simd/src/mod.rs | 2 + crates/core_simd/src/ops.rs | 8 +- crates/core_simd/src/ord.rs | 4 + crates/core_simd/src/simd/prelude.rs | 80 +++++++++++++++++++ crates/core_simd/src/swizzle_dyn.rs | 24 +++++- crates/core_simd/src/vector.rs | 91 +++++----------------- crates/core_simd/tests/cast.rs | 3 +- crates/core_simd/tests/round.rs | 1 + 18 files changed, 338 insertions(+), 133 deletions(-) create mode 100644 crates/core_simd/src/simd/prelude.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index acd47a3da72b..1ff377fce346 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,8 +38,9 @@ jobs: - i586-unknown-linux-gnu - aarch64-unknown-linux-gnu - armv7-unknown-linux-gnueabihf - - mips-unknown-linux-gnu - - mips64-unknown-linux-gnuabi64 + # non-nightly since https://github.com/rust-lang/rust/pull/113274 + # - mips-unknown-linux-gnu + # - mips64-unknown-linux-gnuabi64 - powerpc-unknown-linux-gnu - powerpc64-unknown-linux-gnu - riscv64gc-unknown-linux-gnu @@ -191,8 +192,8 @@ jobs: # Note: The issue above means neither of these mips targets will use # MSA (mips simd) but MIPS uses a nonstandard binary representation # for NaNs which makes it worth testing on despite that. - - mips-unknown-linux-gnu - - mips64-unknown-linux-gnuabi64 + # - mips-unknown-linux-gnu + # - mips64-unknown-linux-gnuabi64 - riscv64gc-unknown-linux-gnu # TODO this test works, but it appears to time out # - powerpc-unknown-linux-gnu diff --git a/crates/core_simd/src/cast.rs b/crates/core_simd/src/cast.rs index 65a3f845ffca..1c3592f80757 100644 --- a/crates/core_simd/src/cast.rs +++ b/crates/core_simd/src/cast.rs @@ -1,55 +1,51 @@ use crate::simd::SimdElement; +mod sealed { + /// Cast vector elements to other types. + /// + /// # Safety + /// Implementing this trait asserts that the type is a valid vector element for the `simd_cast` + /// or `simd_as` intrinsics. + pub unsafe trait Sealed {} +} +use sealed::Sealed; + /// Supporting trait for `Simd::cast`. Typically doesn't need to be used directly. -/// -/// # Safety -/// Implementing this trait asserts that the type is a valid vector element for the `simd_cast` or -/// `simd_as` intrinsics. -pub unsafe trait SimdCast: SimdElement {} +pub trait SimdCast: Sealed + SimdElement {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for i8 {} +unsafe impl Sealed for i8 {} +impl SimdCast for i8 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for i16 {} +unsafe impl Sealed for i16 {} +impl SimdCast for i16 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for i32 {} +unsafe impl Sealed for i32 {} +impl SimdCast for i32 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for i64 {} +unsafe impl Sealed for i64 {} +impl SimdCast for i64 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for isize {} +unsafe impl Sealed for isize {} +impl SimdCast for isize {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for u8 {} +unsafe impl Sealed for u8 {} +impl SimdCast for u8 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for u16 {} +unsafe impl Sealed for u16 {} +impl SimdCast for u16 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for u32 {} +unsafe impl Sealed for u32 {} +impl SimdCast for u32 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for u64 {} +unsafe impl Sealed for u64 {} +impl SimdCast for u64 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for usize {} +unsafe impl Sealed for usize {} +impl SimdCast for usize {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for f32 {} +unsafe impl Sealed for f32 {} +impl SimdCast for f32 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for f64 {} - -/// Supporting trait for `Simd::cast_ptr`. Typically doesn't need to be used directly. -/// -/// # Safety -/// Implementing this trait asserts that the type is a valid vector element for the `simd_cast_ptr` -/// intrinsic. -pub unsafe trait SimdCastPtr {} - -// Safety: pointers can be cast to other pointer types -unsafe impl SimdCastPtr for *const U -where - U: core::ptr::Pointee, - T: core::ptr::Pointee, -{ -} -// Safety: pointers can be cast to other pointer types -unsafe impl SimdCastPtr for *mut U -where - U: core::ptr::Pointee, - T: core::ptr::Pointee, -{ -} +unsafe impl Sealed for f64 {} +impl SimdCast for f64 {} diff --git a/crates/core_simd/src/elements/const_ptr.rs b/crates/core_simd/src/elements/const_ptr.rs index 0ef9802b5e21..f215f9a61d02 100644 --- a/crates/core_simd/src/elements/const_ptr.rs +++ b/crates/core_simd/src/elements/const_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SupportedLaneCount}; +use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SimdUint, SupportedLaneCount}; /// Operations on SIMD vectors of constant pointers. pub trait SimdConstPtr: Copy + Sealed { @@ -9,6 +9,9 @@ pub trait SimdConstPtr: Copy + Sealed { /// Vector of `isize` with the same number of lanes. type Isize; + /// Vector of const pointers with the same number of lanes. + type CastPtr; + /// Vector of mutable pointers to the same type. type MutPtr; @@ -18,6 +21,11 @@ pub trait SimdConstPtr: Copy + Sealed { /// Returns `true` for each lane that is null. fn is_null(self) -> Self::Mask; + /// Casts to a pointer of another type. + /// + /// Equivalent to calling [`pointer::cast`] on each lane. + fn cast(self) -> Self::CastPtr; + /// Changes constness without changing the type. /// /// Equivalent to calling [`pointer::cast_mut`] on each lane. @@ -78,6 +86,7 @@ where { type Usize = Simd; type Isize = Simd; + type CastPtr = Simd<*const U, LANES>; type MutPtr = Simd<*mut T, LANES>; type Mask = Mask; @@ -86,9 +95,22 @@ where Simd::splat(core::ptr::null()).simd_eq(self) } + #[inline] + fn cast(self) -> Self::CastPtr { + // SimdElement currently requires zero-sized metadata, so this should never fail. + // If this ever changes, `simd_cast_ptr` should produce a post-mono error. + use core::{mem::size_of, ptr::Pointee}; + assert_eq!(size_of::<::Metadata>(), 0); + assert_eq!(size_of::<::Metadata>(), 0); + + // Safety: pointers can be cast + unsafe { intrinsics::simd_cast_ptr(self) } + } + #[inline] fn cast_mut(self) -> Self::MutPtr { - self.cast_ptr() + // Safety: pointers can be cast + unsafe { intrinsics::simd_cast_ptr(self) } } #[inline] @@ -106,9 +128,9 @@ where // In the mean-time, this operation is defined to be "as if" it was // a wrapping_offset, so we can emulate it as such. This should properly // restore pointer provenance even under today's compiler. - self.cast_ptr::<*const u8>() + self.cast::() .wrapping_offset(addr.cast::() - self.addr().cast::()) - .cast_ptr() + .cast() } #[inline] diff --git a/crates/core_simd/src/elements/float.rs b/crates/core_simd/src/elements/float.rs index d60223270556..501c1c5ddd3f 100644 --- a/crates/core_simd/src/elements/float.rs +++ b/crates/core_simd/src/elements/float.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - intrinsics, LaneCount, Mask, Simd, SimdElement, SimdPartialEq, SimdPartialOrd, + intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SimdPartialEq, SimdPartialOrd, SupportedLaneCount, }; @@ -15,6 +15,53 @@ pub trait SimdFloat: Copy + Sealed { /// Bit representation of this SIMD vector type. type Bits; + /// A SIMD vector with a different element type. + type Cast; + + /// Performs elementwise conversion of this vector's elements to another SIMD-valid type. + /// + /// This follows the semantics of Rust's `as` conversion for floats (truncating or saturating + /// at the limits) for each element. + /// + /// # Example + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{SimdFloat, SimdInt, 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])); + /// + /// // Formally equivalent, but `Simd::cast` can optimize better. + /// assert_eq!(ints, Simd::from_array(floats.to_array().map(|x| x as i32))); + /// + /// // The float conversion does not round-trip. + /// let floats_again = ints.cast(); + /// assert_ne!(floats, floats_again); + /// assert_eq!(floats_again, Simd::from_array([1.0, -4.0, 2147483647.0, 0.0])); + /// ``` + #[must_use] + fn cast(self) -> Self::Cast; + + /// Rounds toward zero and converts to the same-width integer type, assuming that + /// the value is finite and fits in that type. + /// + /// # Safety + /// The value must: + /// + /// * Not be NaN + /// * Not be infinite + /// * Be representable in the return type, after truncating off its fractional part + /// + /// If these requirements are infeasible or costly, consider using the safe function [cast], + /// which saturates on conversion. + /// + /// [cast]: Simd::cast + unsafe fn to_int_unchecked(self) -> Self::Cast + where + Self::Scalar: core::convert::FloatToInt; + /// Raw transmutation to an unsigned integer vector type with the /// same size and number of lanes. #[must_use = "method returns a new vector and does not mutate the original value"] @@ -206,6 +253,24 @@ macro_rules! impl_trait { type Mask = Mask<<$mask_ty as SimdElement>::Mask, LANES>; type Scalar = $ty; type Bits = Simd<$bits_ty, LANES>; + type Cast = Simd; + + #[inline] + fn cast(self) -> Self::Cast + { + // Safety: supported types are guaranteed by SimdCast + unsafe { intrinsics::simd_as(self) } + } + + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + unsafe fn to_int_unchecked(self) -> Self::Cast + where + Self::Scalar: core::convert::FloatToInt, + { + // Safety: supported types are guaranteed by SimdCast, the caller is responsible for the extra invariants + unsafe { intrinsics::simd_cast(self) } + } #[inline] fn to_bits(self) -> Simd<$bits_ty, LANES> { diff --git a/crates/core_simd/src/elements/int.rs b/crates/core_simd/src/elements/int.rs index 9b8c37ed466e..6db89ff9a659 100644 --- a/crates/core_simd/src/elements/int.rs +++ b/crates/core_simd/src/elements/int.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - intrinsics, LaneCount, Mask, Simd, SimdElement, SimdPartialOrd, SupportedLaneCount, + intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SimdPartialOrd, SupportedLaneCount, }; /// Operations on SIMD vectors of signed integers. @@ -11,6 +11,16 @@ pub trait SimdInt: Copy + Sealed { /// Scalar type contained by this SIMD vector type. type Scalar; + /// A SIMD vector with a different element type. + type Cast; + + /// Performs elementwise conversion of this vector's elements to another SIMD-valid type. + /// + /// This follows the semantics of Rust's `as` conversion for casting integers (wrapping to + /// other integer types, and saturating to float types). + #[must_use] + fn cast(self) -> Self::Cast; + /// Lanewise saturating add. /// /// # Examples @@ -198,6 +208,13 @@ macro_rules! impl_trait { { type Mask = Mask<<$ty as SimdElement>::Mask, LANES>; type Scalar = $ty; + type Cast = Simd; + + #[inline] + fn cast(self) -> Self::Cast { + // Safety: supported types are guaranteed by SimdCast + unsafe { intrinsics::simd_as(self) } + } #[inline] fn saturating_add(self, second: Self) -> Self { diff --git a/crates/core_simd/src/elements/mut_ptr.rs b/crates/core_simd/src/elements/mut_ptr.rs index d87986b4a091..4bdc6a14ce4a 100644 --- a/crates/core_simd/src/elements/mut_ptr.rs +++ b/crates/core_simd/src/elements/mut_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SupportedLaneCount}; +use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SimdUint, SupportedLaneCount}; /// Operations on SIMD vectors of mutable pointers. pub trait SimdMutPtr: Copy + Sealed { @@ -9,6 +9,9 @@ pub trait SimdMutPtr: Copy + Sealed { /// Vector of `isize` with the same number of lanes. type Isize; + /// Vector of const pointers with the same number of lanes. + type CastPtr; + /// Vector of constant pointers to the same type. type ConstPtr; @@ -18,6 +21,11 @@ pub trait SimdMutPtr: Copy + Sealed { /// Returns `true` for each lane that is null. fn is_null(self) -> Self::Mask; + /// Casts to a pointer of another type. + /// + /// Equivalent to calling [`pointer::cast`] on each lane. + fn cast(self) -> Self::CastPtr; + /// Changes constness without changing the type. /// /// Equivalent to calling [`pointer::cast_const`] on each lane. @@ -73,6 +81,7 @@ where { type Usize = Simd; type Isize = Simd; + type CastPtr = Simd<*mut U, LANES>; type ConstPtr = Simd<*const T, LANES>; type Mask = Mask; @@ -81,9 +90,22 @@ where Simd::splat(core::ptr::null_mut()).simd_eq(self) } + #[inline] + fn cast(self) -> Self::CastPtr { + // SimdElement currently requires zero-sized metadata, so this should never fail. + // If this ever changes, `simd_cast_ptr` should produce a post-mono error. + use core::{mem::size_of, ptr::Pointee}; + assert_eq!(size_of::<::Metadata>(), 0); + assert_eq!(size_of::<::Metadata>(), 0); + + // Safety: pointers can be cast + unsafe { intrinsics::simd_cast_ptr(self) } + } + #[inline] fn cast_const(self) -> Self::ConstPtr { - self.cast_ptr() + // Safety: pointers can be cast + unsafe { intrinsics::simd_cast_ptr(self) } } #[inline] @@ -101,9 +123,9 @@ where // In the mean-time, this operation is defined to be "as if" it was // a wrapping_offset, so we can emulate it as such. This should properly // restore pointer provenance even under today's compiler. - self.cast_ptr::<*mut u8>() + self.cast::() .wrapping_offset(addr.cast::() - self.addr().cast::()) - .cast_ptr() + .cast() } #[inline] diff --git a/crates/core_simd/src/elements/uint.rs b/crates/core_simd/src/elements/uint.rs index 21e7e76eb3de..3926c395ec9a 100644 --- a/crates/core_simd/src/elements/uint.rs +++ b/crates/core_simd/src/elements/uint.rs @@ -1,11 +1,21 @@ use super::sealed::Sealed; -use crate::simd::{intrinsics, LaneCount, Simd, SupportedLaneCount}; +use crate::simd::{intrinsics, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; /// Operations on SIMD vectors of unsigned integers. pub trait SimdUint: Copy + Sealed { /// Scalar type contained by this SIMD vector type. type Scalar; + /// A SIMD vector with a different element type. + type Cast; + + /// Performs elementwise conversion of this vector's elements to another SIMD-valid type. + /// + /// This follows the semantics of Rust's `as` conversion for casting integers (wrapping to + /// other integer types, and saturating to float types). + #[must_use] + fn cast(self) -> Self::Cast; + /// Lanewise saturating add. /// /// # Examples @@ -77,6 +87,13 @@ macro_rules! impl_trait { LaneCount: SupportedLaneCount, { type Scalar = $ty; + type Cast = Simd; + + #[inline] + fn cast(self) -> Self::Cast { + // Safety: supported types are guaranteed by SimdCast + unsafe { intrinsics::simd_as(self) } + } #[inline] fn saturating_add(self, second: Self) -> Self { diff --git a/crates/core_simd/src/iter.rs b/crates/core_simd/src/iter.rs index 3275b4db8e49..328c995b81dd 100644 --- a/crates/core_simd/src/iter.rs +++ b/crates/core_simd/src/iter.rs @@ -10,6 +10,7 @@ macro_rules! impl_traits { where LaneCount: SupportedLaneCount, { + #[inline] fn sum>(iter: I) -> Self { iter.fold(Simd::splat(0 as $type), Add::add) } @@ -19,6 +20,7 @@ macro_rules! impl_traits { where LaneCount: SupportedLaneCount, { + #[inline] fn product>(iter: I) -> Self { iter.fold(Simd::splat(1 as $type), Mul::mul) } @@ -28,6 +30,7 @@ macro_rules! impl_traits { where LaneCount: SupportedLaneCount, { + #[inline] fn sum>(iter: I) -> Self { iter.fold(Simd::splat(0 as $type), Add::add) } @@ -37,6 +40,7 @@ macro_rules! impl_traits { where LaneCount: SupportedLaneCount, { + #[inline] fn product>(iter: I) -> Self { iter.fold(Simd::splat(1 as $type), Mul::mul) } diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index e5307de21552..fde406bda706 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -16,7 +16,7 @@ )] #![cfg_attr(feature = "generic_const_exprs", feature(generic_const_exprs))] #![cfg_attr(feature = "generic_const_exprs", allow(incomplete_features))] -#![warn(missing_docs)] +#![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really #![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)] #![unstable(feature = "portable_simd", issue = "86656")] //! Portable SIMD module. diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index e0f3c7beef68..fea687bdc1ae 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -179,6 +179,7 @@ where /// Panics if any lane is not 0 or -1. #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] + #[track_caller] pub fn from_int(value: Simd) -> Self { assert!(T::valid(value), "all values must be either 0 or -1",); // Safety: the validity has been checked @@ -217,6 +218,7 @@ where /// Panics if `lane` is greater than or equal to the number of lanes in the vector. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] + #[track_caller] pub fn test(&self, lane: usize) -> bool { assert!(lane < LANES, "lane index out of range"); // Safety: the lane index has been checked @@ -240,6 +242,7 @@ where /// # Panics /// Panics if `lane` is greater than or equal to the number of lanes in the vector. #[inline] + #[track_caller] pub fn set(&mut self, lane: usize, value: bool) { assert!(lane < LANES, "lane index out of range"); // Safety: the lane index has been checked @@ -327,6 +330,7 @@ where T: MaskElement + fmt::Debug, LaneCount: SupportedLaneCount, { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list() .entries((0..LANES).map(|lane| self.test(lane))) diff --git a/crates/core_simd/src/mod.rs b/crates/core_simd/src/mod.rs index 35c659b7a429..f9891a3b7c1d 100644 --- a/crates/core_simd/src/mod.rs +++ b/crates/core_simd/src/mod.rs @@ -23,6 +23,8 @@ mod vendor; #[doc = include_str!("core_simd_docs.md")] pub mod simd { + pub mod prelude; + pub(crate) use crate::core_simd::intrinsics; pub use crate::core_simd::alias::*; diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index fc1e0bc426df..b007456cf2cc 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -15,6 +15,7 @@ where I: core::slice::SliceIndex<[T]>, { type Output = I::Output; + #[inline] fn index(&self, index: I) -> &Self::Output { &self.as_array()[index] } @@ -26,6 +27,7 @@ where LaneCount: SupportedLaneCount, I: core::slice::SliceIndex<[T]>, { + #[inline] fn index_mut(&mut self, index: I) -> &mut Self::Output { &mut self.as_mut_array()[index] } @@ -118,10 +120,14 @@ macro_rules! for_base_types { #[inline] #[must_use = "operator returns a new vector without mutating the inputs"] + // TODO: only useful for int Div::div, but we hope that this + // will essentially always always get inlined anyway. + #[track_caller] fn $call(self, rhs: Self) -> Self::Output { $macro_impl!(self, rhs, $inner, $scalar) } - })* + } + )* } } diff --git a/crates/core_simd/src/ord.rs b/crates/core_simd/src/ord.rs index 1ae9cd061fb2..b2455190e823 100644 --- a/crates/core_simd/src/ord.rs +++ b/crates/core_simd/src/ord.rs @@ -94,6 +94,7 @@ macro_rules! impl_integer { } #[inline] + #[track_caller] fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), @@ -200,6 +201,7 @@ macro_rules! impl_mask { } #[inline] + #[track_caller] fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), @@ -254,6 +256,7 @@ where } #[inline] + #[track_caller] fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), @@ -303,6 +306,7 @@ where } #[inline] + #[track_caller] fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), diff --git a/crates/core_simd/src/simd/prelude.rs b/crates/core_simd/src/simd/prelude.rs new file mode 100644 index 000000000000..e8fdc932d490 --- /dev/null +++ b/crates/core_simd/src/simd/prelude.rs @@ -0,0 +1,80 @@ +//! The portable SIMD prelude. +//! +//! Includes important traits and types to be imported with a glob: +//! ```ignore +//! use std::simd::prelude::*; +//! ``` + +#[doc(no_inline)] +pub use super::{ + simd_swizzle, Mask, Simd, SimdConstPtr, SimdFloat, SimdInt, SimdMutPtr, SimdOrd, SimdPartialEq, + SimdPartialOrd, SimdUint, +}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{f32x1, f32x2, f32x4, f32x8, f32x16, f32x32, f32x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{f64x1, f64x2, f64x4, f64x8, f64x16, f64x32, f64x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{i8x1, i8x2, i8x4, i8x8, i8x16, i8x32, i8x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{i16x1, i16x2, i16x4, i16x8, i16x16, i16x32, i16x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{i32x1, i32x2, i32x4, i32x8, i32x16, i32x32, i32x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{i64x1, i64x2, i64x4, i64x8, i64x16, i64x32, i64x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{isizex1, isizex2, isizex4, isizex8, isizex16, isizex32, isizex64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{u8x1, u8x2, u8x4, u8x8, u8x16, u8x32, u8x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{u16x1, u16x2, u16x4, u16x8, u16x16, u16x32, u16x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{u32x1, u32x2, u32x4, u32x8, u32x16, u32x32, u32x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{u64x1, u64x2, u64x4, u64x8, u64x16, u64x32, u64x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{usizex1, usizex2, usizex4, usizex8, usizex16, usizex32, usizex64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{mask8x1, mask8x2, mask8x4, mask8x8, mask8x16, mask8x32, mask8x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{mask16x1, mask16x2, mask16x4, mask16x8, mask16x16, mask16x32, mask16x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{mask32x1, mask32x2, mask32x4, mask32x8, mask32x16, mask32x32, mask32x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{mask64x1, mask64x2, mask64x4, mask64x8, mask64x16, mask64x32, mask64x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{masksizex1, masksizex2, masksizex4, masksizex8, masksizex16, masksizex32, masksizex64}; diff --git a/crates/core_simd/src/swizzle_dyn.rs b/crates/core_simd/src/swizzle_dyn.rs index 6065d6459378..ce621792534e 100644 --- a/crates/core_simd/src/swizzle_dyn.rs +++ b/crates/core_simd/src/swizzle_dyn.rs @@ -16,9 +16,14 @@ where #[inline] pub fn swizzle_dyn(self, idxs: Simd) -> Self { #![allow(unused_imports, unused_unsafe)] - #[cfg(target_arch = "aarch64")] + #[cfg(all(target_arch = "aarch64", target_endian = "little"))] use core::arch::aarch64::{uint8x8_t, vqtbl1q_u8, vtbl1_u8}; - #[cfg(all(target_arch = "arm", target_feature = "v7", target_feature = "neon"))] + #[cfg(all( + target_arch = "arm", + target_feature = "v7", + target_feature = "neon", + target_endian = "little" + ))] use core::arch::arm::{uint8x8_t, vtbl1_u8}; #[cfg(target_arch = "wasm32")] use core::arch::wasm32 as wasm; @@ -29,13 +34,24 @@ where // SAFETY: Intrinsics covered by cfg unsafe { match N { - #[cfg(target_feature = "neon")] + #[cfg(all( + any( + target_arch = "aarch64", + all(target_arch = "arm", target_feature = "v7") + ), + target_feature = "neon", + target_endian = "little" + ))] 8 => transize(vtbl1_u8, self, idxs), #[cfg(target_feature = "ssse3")] 16 => transize(x86::_mm_shuffle_epi8, self, idxs), #[cfg(target_feature = "simd128")] 16 => transize(wasm::i8x16_swizzle, self, idxs), - #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] + #[cfg(all( + target_arch = "aarch64", + target_feature = "neon", + target_endian = "little" + ))] 16 => transize(vqtbl1q_u8, self, idxs), #[cfg(all(target_feature = "avx2", not(target_feature = "avx512vbmi")))] 32 => transize_raw(avx2_pshufb, self, idxs), diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 3809cc961515..9aa7bacfce98 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -1,6 +1,6 @@ use crate::simd::{ - intrinsics, LaneCount, Mask, MaskElement, SimdCast, SimdCastPtr, SimdConstPtr, SimdMutPtr, - SimdPartialOrd, SupportedLaneCount, Swizzle, + intrinsics, LaneCount, Mask, MaskElement, SimdConstPtr, SimdMutPtr, SimdPartialOrd, + SupportedLaneCount, Swizzle, }; use core::convert::{TryFrom, TryInto}; @@ -122,6 +122,7 @@ where /// let v = u32x4::splat(0); /// assert_eq!(v.lanes(), 4); /// ``` + #[inline] pub const fn lanes(&self) -> usize { Self::LANES } @@ -136,6 +137,7 @@ where /// let v = u32x4::splat(8); /// assert_eq!(v.as_array(), &[8, 8, 8, 8]); /// ``` + #[inline] pub fn splat(value: T) -> Self { // This is preferred over `[value; N]`, since it's explicitly a splat: // https://github.com/rust-lang/rust/issues/97804 @@ -156,6 +158,7 @@ where /// let v: u64x4 = Simd::from_array([0, 1, 2, 3]); /// assert_eq!(v.as_array(), &[0, 1, 2, 3]); /// ``` + #[inline] pub const fn as_array(&self) -> &[T; N] { // SAFETY: `Simd` is just an overaligned `[T; N]` with // potential padding at the end, so pointer casting to a @@ -167,6 +170,7 @@ where } /// Returns a mutable array reference containing the entire SIMD vector. + #[inline] pub fn as_mut_array(&mut self) -> &mut [T; N] { // SAFETY: `Simd` is just an overaligned `[T; N]` with // potential padding at the end, so pointer casting to a @@ -184,6 +188,7 @@ where /// /// # Safety /// Reading `ptr` must be safe, as if by `<*const [T; N]>::read_unaligned`. + #[inline] const unsafe fn load(ptr: *const [T; N]) -> Self { // There are potentially simpler ways to write this function, but this should result in // LLVM `load ` @@ -204,6 +209,7 @@ where /// /// # Safety /// Writing to `ptr` must be safe, as if by `<*mut [T; N]>::write_unaligned`. + #[inline] const unsafe fn store(self, ptr: *mut [T; N]) { // There are potentially simpler ways to write this function, but this should result in // LLVM `store ` @@ -216,6 +222,7 @@ where } /// Converts an array to a SIMD vector. + #[inline] pub const fn from_array(array: [T; N]) -> Self { // SAFETY: `&array` is safe to read. // @@ -228,6 +235,7 @@ where } /// Converts a SIMD vector to an array. + #[inline] pub const fn to_array(self) -> [T; N] { let mut tmp = core::mem::MaybeUninit::uninit(); // SAFETY: writing to `tmp` is safe and initializes it. @@ -259,6 +267,8 @@ where /// assert_eq!(v.as_array(), &[1, 2, 3, 4]); /// ``` #[must_use] + #[inline] + #[track_caller] pub const fn from_slice(slice: &[T]) -> Self { assert!( slice.len() >= Self::LANES, @@ -287,6 +297,8 @@ where /// v.copy_to_slice(&mut dest); /// assert_eq!(&dest, &[1, 2, 3, 4, 0, 0]); /// ``` + #[inline] + #[track_caller] pub fn copy_to_slice(self, slice: &mut [T]) { assert!( slice.len() >= Self::LANES, @@ -297,76 +309,6 @@ where unsafe { self.store(slice.as_mut_ptr().cast()) } } - /// Performs elementwise conversion of a SIMD vector's elements to another SIMD-valid type. - /// - /// This follows the semantics of Rust's `as` conversion for casting integers between - /// signed and unsigned (interpreting integers as 2s complement, so `-1` to `U::MAX` and - /// `1 << (U::BITS -1)` becoming `I::MIN` ), and from floats to integers (truncating, - /// or saturating at the limits) for each element. - /// - /// # Examples - /// ``` - /// # #![feature(portable_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])); - /// - /// // Formally equivalent, but `Simd::cast` can optimize better. - /// assert_eq!(ints, Simd::from_array(floats.to_array().map(|x| x as i32))); - /// - /// // The float conversion does not round-trip. - /// let floats_again = ints.cast(); - /// assert_ne!(floats, floats_again); - /// assert_eq!(floats_again, Simd::from_array([1.0, -4.0, 2147483647.0, 0.0])); - /// ``` - #[must_use] - #[inline] - pub fn cast(self) -> Simd - where - T: SimdCast, - { - // Safety: supported types are guaranteed by SimdCast - unsafe { intrinsics::simd_as(self) } - } - - /// Casts a vector of pointers to another pointer type. - #[must_use] - #[inline] - pub fn cast_ptr(self) -> Simd - where - T: SimdCastPtr, - U: SimdElement, - { - // Safety: supported types are guaranteed by SimdCastPtr - unsafe { intrinsics::simd_cast_ptr(self) } - } - - /// Rounds toward zero and converts to the same-width integer type, assuming that - /// the value is finite and fits in that type. - /// - /// # Safety - /// The value must: - /// - /// * Not be NaN - /// * Not be infinite - /// * Be representable in the return type, after truncating off its fractional part - /// - /// If these requirements are infeasible or costly, consider using the safe function [cast], - /// which saturates on conversion. - /// - /// [cast]: Simd::cast - #[inline] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn to_int_unchecked(self) -> Simd - where - T: core::convert::FloatToInt + SimdCast, - I: SimdCast, - { - // Safety: supported types are guaranteed by SimdCast, the caller is responsible for the extra invariants - unsafe { intrinsics::simd_cast(self) } - } - /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. /// If an index is out-of-bounds, the element is instead selected from the `or` vector. /// @@ -717,6 +659,7 @@ where LaneCount: SupportedLaneCount, T: SimdElement, { + #[inline] fn clone(&self) -> Self { *self } @@ -861,6 +804,7 @@ where LaneCount: SupportedLaneCount, T: SimdElement, { + #[inline] fn from(array: [T; N]) -> Self { Self::from_array(array) } @@ -871,6 +815,7 @@ where LaneCount: SupportedLaneCount, T: SimdElement, { + #[inline] fn from(vector: Simd) -> Self { vector.to_array() } @@ -883,6 +828,7 @@ where { type Error = core::array::TryFromSliceError; + #[inline] fn try_from(slice: &[T]) -> Result { Ok(Self::from_array(slice.try_into()?)) } @@ -895,6 +841,7 @@ where { type Error = core::array::TryFromSliceError; + #[inline] fn try_from(slice: &mut [T]) -> Result { Ok(Self::from_array(slice.try_into()?)) } diff --git a/crates/core_simd/tests/cast.rs b/crates/core_simd/tests/cast.rs index ab5650f07132..00545936ea2a 100644 --- a/crates/core_simd/tests/cast.rs +++ b/crates/core_simd/tests/cast.rs @@ -2,7 +2,8 @@ macro_rules! cast_types { ($start:ident, $($target:ident),*) => { mod $start { - use core_simd::simd::Simd; + #[allow(unused)] + use core_simd::simd::{Simd, SimdInt, SimdUint, SimdFloat}; type Vector = Simd<$start, N>; $( mod $target { diff --git a/crates/core_simd/tests/round.rs b/crates/core_simd/tests/round.rs index 8b9638ad4667..aacf7bd3bcc2 100644 --- a/crates/core_simd/tests/round.rs +++ b/crates/core_simd/tests/round.rs @@ -53,6 +53,7 @@ macro_rules! float_rounding_test { test_helpers::test_lanes! { fn to_int_unchecked() { + use core_simd::simd::SimdFloat; // The maximum integer that can be represented by the equivalently sized float has // all of the mantissa digits set to 1, pushed up to the MSB. const ALL_MANTISSA_BITS: IntScalar = ((1 << ::MANTISSA_DIGITS) - 1); From 78c05f92b8b72fdf9a455a2ce9586494cba1767f Mon Sep 17 00:00:00 2001 From: cui fliter Date: Sun, 16 Jul 2023 00:37:30 +0800 Subject: [PATCH 002/285] remove repetitive words Signed-off-by: cui fliter --- crates/core_simd/examples/dot_product.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/examples/dot_product.rs b/crates/core_simd/examples/dot_product.rs index 391f08f55a07..a7973ec74041 100644 --- a/crates/core_simd/examples/dot_product.rs +++ b/crates/core_simd/examples/dot_product.rs @@ -130,7 +130,7 @@ pub fn dot_prod_simd_4(a: &[f32], b: &[f32]) -> f32 { } // This version allocates a single `XMM` register for accumulation, and the folds don't allocate on top of that. -// Notice the the use of `mul_add`, which can do a multiply and an add operation ber iteration. +// Notice the use of `mul_add`, which can do a multiply and an add operation ber iteration. pub fn dot_prod_simd_5(a: &[f32], b: &[f32]) -> f32 { a.array_chunks::<4>() .map(|&a| f32x4::from_array(a)) From 589572941bfbe2530450c729c0ffab21445db397 Mon Sep 17 00:00:00 2001 From: bohan Date: Sun, 15 Oct 2023 19:38:22 +0800 Subject: [PATCH 003/285] use visibility to check unused imports and delete some stmts --- crates/core_simd/src/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/core_simd/src/mod.rs b/crates/core_simd/src/mod.rs index f9891a3b7c1d..19426769858b 100644 --- a/crates/core_simd/src/mod.rs +++ b/crates/core_simd/src/mod.rs @@ -35,6 +35,5 @@ pub mod simd { pub use crate::core_simd::masks::*; pub use crate::core_simd::ord::*; pub use crate::core_simd::swizzle::*; - pub use crate::core_simd::swizzle_dyn::*; pub use crate::core_simd::vector::*; } From 62030c77aeb031b0ca203c5bfe664d633bbe1283 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 26 Nov 2023 08:50:06 -0500 Subject: [PATCH 004/285] Merge commit 'e0e9a4517f9fc021283514da387e70a56061bd3e' into sync-portable-simd-2023-11-19 --- .github/workflows/ci.yml | 71 +-- .gitignore | 1 - Cargo.lock | 304 +++++++++++++ crates/core_simd/Cargo.toml | 1 - crates/core_simd/examples/dot_product.rs | 2 +- crates/core_simd/examples/matrix_inversion.rs | 49 +-- crates/core_simd/examples/nbody.rs | 3 +- crates/core_simd/examples/spectral_norm.rs | 2 +- crates/core_simd/src/core_simd_docs.md | 35 ++ crates/core_simd/src/fmt.rs | 4 +- crates/core_simd/src/intrinsics.rs | 6 + crates/core_simd/src/iter.rs | 16 +- crates/core_simd/src/lane_count.rs | 8 +- crates/core_simd/src/lib.rs | 4 +- crates/core_simd/src/masks.rs | 379 ++++++++++------ crates/core_simd/src/masks/bitmask.rs | 138 +++--- crates/core_simd/src/masks/full_masks.rs | 185 ++++---- crates/core_simd/src/masks/to_bitmask.rs | 97 ----- crates/core_simd/src/mod.rs | 20 +- crates/core_simd/src/ops.rs | 11 +- crates/core_simd/src/ops/assign.rs | 26 +- crates/core_simd/src/ops/deref.rs | 46 +- crates/core_simd/src/ops/shift_scalar.rs | 62 +++ crates/core_simd/src/ops/unary.rs | 46 +- crates/core_simd/src/select.rs | 22 +- crates/core_simd/src/simd/cmp.rs | 7 + crates/core_simd/src/{ => simd/cmp}/eq.rs | 30 +- crates/core_simd/src/{ => simd/cmp}/ord.rs | 67 +-- .../src/{elements.rs => simd/num.rs} | 6 +- .../src/{elements => simd/num}/float.rs | 97 +++-- .../src/{elements => simd/num}/int.rs | 114 +++-- .../src/{elements => simd/num}/uint.rs | 97 ++++- crates/core_simd/src/simd/prelude.rs | 6 +- crates/core_simd/src/simd/ptr.rs | 11 + .../src/{elements => simd/ptr}/const_ptr.rs | 47 +- .../src/{elements => simd/ptr}/mut_ptr.rs | 45 +- crates/core_simd/src/swizzle.rs | 412 +++++++++--------- crates/core_simd/src/swizzle_dyn.rs | 4 +- crates/core_simd/src/to_bytes.rs | 132 +++++- crates/core_simd/src/vector.rs | 43 +- crates/core_simd/src/vendor.rs | 2 +- crates/core_simd/src/vendor/x86.rs | 2 +- crates/core_simd/tests/cast.rs | 2 +- crates/core_simd/tests/masks.rs | 14 +- crates/core_simd/tests/ops_macros.rs | 135 +++++- crates/core_simd/tests/pointers.rs | 5 +- crates/core_simd/tests/round.rs | 4 +- crates/core_simd/tests/swizzle.rs | 28 +- crates/core_simd/tests/swizzle_dyn.rs | 1 - crates/core_simd/tests/to_bytes.rs | 22 +- crates/std_float/src/lib.rs | 9 +- crates/test_helpers/Cargo.toml | 6 +- crates/test_helpers/src/biteq.rs | 32 +- crates/test_helpers/src/lib.rs | 128 +++++- crates/test_helpers/src/subnormals.rs | 91 ++++ 55 files changed, 2119 insertions(+), 1018 deletions(-) create mode 100644 Cargo.lock delete mode 100644 crates/core_simd/src/masks/to_bitmask.rs create mode 100644 crates/core_simd/src/ops/shift_scalar.rs create mode 100644 crates/core_simd/src/simd/cmp.rs rename crates/core_simd/src/{ => simd/cmp}/eq.rs (74%) rename crates/core_simd/src/{ => simd/cmp}/ord.rs (79%) rename crates/core_simd/src/{elements.rs => simd/num.rs} (63%) rename crates/core_simd/src/{elements => simd/num}/float.rs (80%) rename crates/core_simd/src/{elements => simd/num}/int.rs (71%) rename crates/core_simd/src/{elements => simd/num}/uint.rs (58%) create mode 100644 crates/core_simd/src/simd/ptr.rs rename crates/core_simd/src/{elements => simd/ptr}/const_ptr.rs (81%) rename crates/core_simd/src/{elements => simd/ptr}/mut_ptr.rs (81%) create mode 100644 crates/test_helpers/src/subnormals.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ff377fce346..90543044ea84 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -167,40 +167,33 @@ jobs: RUSTFLAGS: ${{ matrix.rustflags }} cross-tests: - name: "${{ matrix.target }} (via cross)" + name: "${{ matrix.target_feature }} on ${{ matrix.target }} (via cross)" runs-on: ubuntu-latest strategy: fail-fast: false - # TODO: Sadly, we cant configure target-feature in a meaningful way - # because `cross` doesn't tell qemu to enable any non-default cpu - # features, nor does it give us a way to do so. - # - # Ultimately, we'd like to do something like [rust-lang/stdarch][stdarch]. - # This is a lot more complex... but in practice it's likely that we can just - # snarf the docker config from around [here][1000-dockerfiles]. - # - # [stdarch]: https://github.com/rust-lang/stdarch/blob/a5db4eaf/.github/workflows/main.yml#L67 - # [1000-dockerfiles]: https://github.com/rust-lang/stdarch/tree/a5db4eaf/ci/docker matrix: target: - - i586-unknown-linux-gnu - # 32-bit arm has a few idiosyncracies like having subnormal flushing - # to zero on by default. Ideally we'd set - armv7-unknown-linux-gnueabihf - - aarch64-unknown-linux-gnu - # Note: The issue above means neither of these mips targets will use - # MSA (mips simd) but MIPS uses a nonstandard binary representation - # for NaNs which makes it worth testing on despite that. + - thumbv7neon-unknown-linux-gnueabihf # includes neon by default + - aarch64-unknown-linux-gnu # includes neon by default + - powerpc-unknown-linux-gnu + - powerpc64le-unknown-linux-gnu # includes altivec by default + - riscv64gc-unknown-linux-gnu + # MIPS uses a nonstandard binary representation for NaNs which makes it worth testing + # non-nightly since https://github.com/rust-lang/rust/pull/113274 # - mips-unknown-linux-gnu # - mips64-unknown-linux-gnuabi64 - - riscv64gc-unknown-linux-gnu - # TODO this test works, but it appears to time out - # - powerpc-unknown-linux-gnu - # TODO this test is broken, but it appears to be a problem with QEMU, not us. - # - powerpc64le-unknown-linux-gnu - # TODO enable this once a new version of cross is released + # Lots of errors in QEMU and no real hardware to test on. Not clear if it's QEMU or bad codegen. # - powerpc64-unknown-linux-gnu + target_feature: [default] + include: + - { target: powerpc64le-unknown-linux-gnu, target_feature: "+vsx" } + # Fails due to QEMU floating point errors, probably handling subnormals incorrectly. + # This target is somewhat redundant, since ppc64le has altivec as well. + # - { target: powerpc-unknown-linux-gnu, target_feature: "+altivec" } + # We should test this, but cross currently can't run it + # - { target: riscv64gc-unknown-linux-gnu, target_feature: "+v,+zvl128b" } steps: - uses: actions/checkout@v2 @@ -217,11 +210,27 @@ jobs: # being part of the tarball means we can't just use the download/latest # URL :( run: | - CROSS_URL=https://github.com/rust-embedded/cross/releases/download/v0.2.1/cross-v0.2.1-x86_64-unknown-linux-gnu.tar.gz + CROSS_URL=https://github.com/cross-rs/cross/releases/download/v0.2.5/cross-x86_64-unknown-linux-gnu.tar.gz mkdir -p "$HOME/.bin" curl -sfSL --retry-delay 10 --retry 5 "${CROSS_URL}" | tar zxf - -C "$HOME/.bin" echo "$HOME/.bin" >> $GITHUB_PATH + - name: Configure Emulated CPUs + run: | + echo "CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc -cpu e600" >> $GITHUB_ENV + # echo "CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_RUNNER=qemu-riscv64 -cpu rv64,zba=true,zbb=true,v=true,vlen=256,vext_spec=v1.0" >> $GITHUB_ENV + + - name: Configure RUSTFLAGS + shell: bash + run: | + case "${{ matrix.target_feature }}" in + default) + echo "RUSTFLAGS=" >> $GITHUB_ENV;; + *) + echo "RUSTFLAGS=-Ctarget-feature=${{ matrix.target_feature }}" >> $GITHUB_ENV + ;; + esac + - name: Test (debug) run: cross test --verbose --target=${{ matrix.target }} @@ -229,7 +238,7 @@ jobs: run: cross test --verbose --target=${{ matrix.target }} --release features: - name: "Check cargo features (${{ matrix.simd }} × ${{ matrix.features }})" + name: "Test cargo features (${{ matrix.simd }} × ${{ matrix.features }})" runs-on: ubuntu-latest strategy: fail-fast: false @@ -240,12 +249,8 @@ jobs: features: - "" - "--features std" - - "--features generic_const_exprs" - - "--features std --features generic_const_exprs" - "--features all_lane_counts" - - "--features all_lane_counts --features std" - - "--features all_lane_counts --features generic_const_exprs" - - "--features all_lane_counts --features std --features generic_const_exprs" + - "--all-features" steps: - uses: actions/checkout@v2 @@ -257,9 +262,9 @@ jobs: run: echo "CPU_FEATURE=$(lscpu | grep -o avx512[a-z]* | sed s/avx/+avx/ | tr '\n' ',' )" >> $GITHUB_ENV - name: Check build if: ${{ matrix.simd == '' }} - run: RUSTFLAGS="-Dwarnings" cargo check --all-targets --no-default-features ${{ matrix.features }} + run: RUSTFLAGS="-Dwarnings" cargo test --all-targets --no-default-features ${{ matrix.features }} - name: Check AVX if: ${{ matrix.simd == 'avx512' && contains(env.CPU_FEATURE, 'avx512') }} run: | echo "Found AVX features: $CPU_FEATURE" - RUSTFLAGS="-Dwarnings -Ctarget-feature=$CPU_FEATURE" cargo check --all-targets --no-default-features ${{ matrix.features }} + RUSTFLAGS="-Dwarnings -Ctarget-feature=$CPU_FEATURE" cargo test --all-targets --no-default-features ${{ matrix.features }} diff --git a/.gitignore b/.gitignore index 96ef6c0b944e..ea8c4bf7f35f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ /target -Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000000..46312c09657d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,304 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "core_simd" +version = "0.1.0" +dependencies = [ + "proptest", + "std_float", + "test_helpers", + "wasm-bindgen", + "wasm-bindgen-test", +] + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12e6c80c1139113c28ee4670dc50cc42915228b51f56a9e407f0ec60f966646f" +dependencies = [ + "bitflags", + "byteorder", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_xorshift" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" +dependencies = [ + "rand_core", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "std_float" +version = "0.1.0" +dependencies = [ + "core_simd", +] + +[[package]] +name = "syn" +version = "2.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "test_helpers" +version = "0.1.0" +dependencies = [ + "proptest", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] diff --git a/crates/core_simd/Cargo.toml b/crates/core_simd/Cargo.toml index d1a3a515a7e8..b4a8fd70f4c0 100644 --- a/crates/core_simd/Cargo.toml +++ b/crates/core_simd/Cargo.toml @@ -12,7 +12,6 @@ license = "MIT OR Apache-2.0" default = ["as_crate"] as_crate = [] std = [] -generic_const_exprs = [] all_lane_counts = [] [target.'cfg(target_arch = "wasm32")'.dev-dependencies] diff --git a/crates/core_simd/examples/dot_product.rs b/crates/core_simd/examples/dot_product.rs index a7973ec74041..f047010a65c1 100644 --- a/crates/core_simd/examples/dot_product.rs +++ b/crates/core_simd/examples/dot_product.rs @@ -6,7 +6,7 @@ #![feature(slice_as_chunks)] // Add these imports to use the stdsimd library #![feature(portable_simd)] -use core_simd::simd::*; +use core_simd::simd::prelude::*; // This is your barebones dot product implementation: // Take 2 vectors, multiply them element wise and *then* diff --git a/crates/core_simd/examples/matrix_inversion.rs b/crates/core_simd/examples/matrix_inversion.rs index 39f530f68f57..bad86414401d 100644 --- a/crates/core_simd/examples/matrix_inversion.rs +++ b/crates/core_simd/examples/matrix_inversion.rs @@ -2,8 +2,7 @@ // Code ported from the `packed_simd` crate // Run this code with `cargo test --example matrix_inversion` #![feature(array_chunks, portable_simd)] -use core_simd::simd::*; -use Which::*; +use core_simd::simd::prelude::*; // Gotta define our own 4x4 matrix since Rust doesn't ship multidim arrays yet :^) #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] @@ -164,10 +163,10 @@ pub fn simd_inv4x4(m: Matrix4x4) -> Option { let m_2 = f32x4::from_array(m[2]); let m_3 = f32x4::from_array(m[3]); - const SHUFFLE01: [Which; 4] = [First(0), First(1), Second(0), Second(1)]; - const SHUFFLE02: [Which; 4] = [First(0), First(2), Second(0), Second(2)]; - const SHUFFLE13: [Which; 4] = [First(1), First(3), Second(1), Second(3)]; - const SHUFFLE23: [Which; 4] = [First(2), First(3), Second(2), Second(3)]; + const SHUFFLE01: [usize; 4] = [0, 1, 4, 5]; + const SHUFFLE02: [usize; 4] = [0, 2, 4, 6]; + const SHUFFLE13: [usize; 4] = [1, 3, 5, 7]; + const SHUFFLE23: [usize; 4] = [2, 3, 6, 7]; let tmp = simd_swizzle!(m_0, m_1, SHUFFLE01); let row1 = simd_swizzle!(m_2, m_3, SHUFFLE01); @@ -180,58 +179,58 @@ pub fn simd_inv4x4(m: Matrix4x4) -> Option { let row2 = simd_swizzle!(tmp, row3, SHUFFLE02); let row3 = simd_swizzle!(row3, tmp, SHUFFLE13); - let tmp = (row2 * row3).reverse().rotate_lanes_right::<2>(); + let tmp = (row2 * row3).reverse().rotate_elements_right::<2>(); let minor0 = row1 * tmp; let minor1 = row0 * tmp; - let tmp = tmp.rotate_lanes_right::<2>(); + let tmp = tmp.rotate_elements_right::<2>(); let minor0 = (row1 * tmp) - minor0; let minor1 = (row0 * tmp) - minor1; - let minor1 = minor1.rotate_lanes_right::<2>(); + let minor1 = minor1.rotate_elements_right::<2>(); - let tmp = (row1 * row2).reverse().rotate_lanes_right::<2>(); + let tmp = (row1 * row2).reverse().rotate_elements_right::<2>(); let minor0 = (row3 * tmp) + minor0; let minor3 = row0 * tmp; - let tmp = tmp.rotate_lanes_right::<2>(); + let tmp = tmp.rotate_elements_right::<2>(); let minor0 = minor0 - row3 * tmp; let minor3 = row0 * tmp - minor3; - let minor3 = minor3.rotate_lanes_right::<2>(); + let minor3 = minor3.rotate_elements_right::<2>(); - let tmp = (row3 * row1.rotate_lanes_right::<2>()) + let tmp = (row3 * row1.rotate_elements_right::<2>()) .reverse() - .rotate_lanes_right::<2>(); - let row2 = row2.rotate_lanes_right::<2>(); + .rotate_elements_right::<2>(); + let row2 = row2.rotate_elements_right::<2>(); let minor0 = row2 * tmp + minor0; let minor2 = row0 * tmp; - let tmp = tmp.rotate_lanes_right::<2>(); + let tmp = tmp.rotate_elements_right::<2>(); let minor0 = minor0 - row2 * tmp; let minor2 = row0 * tmp - minor2; - let minor2 = minor2.rotate_lanes_right::<2>(); + let minor2 = minor2.rotate_elements_right::<2>(); - let tmp = (row0 * row1).reverse().rotate_lanes_right::<2>(); + let tmp = (row0 * row1).reverse().rotate_elements_right::<2>(); let minor2 = minor2 + row3 * tmp; let minor3 = row2 * tmp - minor3; - let tmp = tmp.rotate_lanes_right::<2>(); + let tmp = tmp.rotate_elements_right::<2>(); let minor2 = row3 * tmp - minor2; let minor3 = minor3 - row2 * tmp; - let tmp = (row0 * row3).reverse().rotate_lanes_right::<2>(); + let tmp = (row0 * row3).reverse().rotate_elements_right::<2>(); let minor1 = minor1 - row2 * tmp; let minor2 = row1 * tmp + minor2; - let tmp = tmp.rotate_lanes_right::<2>(); + let tmp = tmp.rotate_elements_right::<2>(); let minor1 = row2 * tmp + minor1; let minor2 = minor2 - row1 * tmp; - let tmp = (row0 * row2).reverse().rotate_lanes_right::<2>(); + let tmp = (row0 * row2).reverse().rotate_elements_right::<2>(); let minor1 = row3 * tmp + minor1; let minor3 = minor3 - row1 * tmp; - let tmp = tmp.rotate_lanes_right::<2>(); + let tmp = tmp.rotate_elements_right::<2>(); let minor1 = minor1 - row3 * tmp; let minor3 = row1 * tmp + minor3; let det = row0 * minor0; - let det = det.rotate_lanes_right::<2>() + det; - let det = det.reverse().rotate_lanes_right::<2>() + det; + let det = det.rotate_elements_right::<2>() + det; + let det = det.reverse().rotate_elements_right::<2>() + det; if det.reduce_sum() == 0. { return None; diff --git a/crates/core_simd/examples/nbody.rs b/crates/core_simd/examples/nbody.rs index df38a00967fe..65820d1340bd 100644 --- a/crates/core_simd/examples/nbody.rs +++ b/crates/core_simd/examples/nbody.rs @@ -1,11 +1,12 @@ #![feature(portable_simd)] +#![allow(clippy::excessive_precision)] extern crate std_float; /// Benchmarks game nbody code /// Taken from the `packed_simd` crate /// Run this benchmark with `cargo test --example nbody` mod nbody { - use core_simd::simd::*; + use core_simd::simd::prelude::*; #[allow(unused)] // False positive? use std_float::StdFloat; diff --git a/crates/core_simd/examples/spectral_norm.rs b/crates/core_simd/examples/spectral_norm.rs index d576bd0ccee0..bc7934c25223 100644 --- a/crates/core_simd/examples/spectral_norm.rs +++ b/crates/core_simd/examples/spectral_norm.rs @@ -1,6 +1,6 @@ #![feature(portable_simd)] -use core_simd::simd::*; +use core_simd::simd::prelude::*; fn a(i: usize, j: usize) -> f64 { ((i + j) * (i + j + 1) / 2 + i + 1) as f64 diff --git a/crates/core_simd/src/core_simd_docs.md b/crates/core_simd/src/core_simd_docs.md index 15e8ed0253e1..fa93155ff5ed 100644 --- a/crates/core_simd/src/core_simd_docs.md +++ b/crates/core_simd/src/core_simd_docs.md @@ -2,3 +2,38 @@ Portable SIMD module. This module offers a portable abstraction for SIMD operations that is not bound to any particular hardware architecture. + +# What is "portable"? + +This module provides a SIMD implementation that is fast and predictable on any target. + +### Portable SIMD works on every target + +Unlike target-specific SIMD in `std::arch`, portable SIMD compiles for every target. +In this regard, it is just like "regular" Rust. + +### Portable SIMD is consistent between targets + +A program using portable SIMD can expect identical behavior on any target. +In most regards, [`Simd`] can be thought of as a parallelized `[T; N]` and operates like a sequence of `T`. + +This has one notable exception: a handful of older architectures (e.g. `armv7` and `powerpc`) flush [subnormal](`f32::is_subnormal`) `f32` values to zero. +On these architectures, subnormal `f32` input values are replaced with zeros, and any operation producing subnormal `f32` values produces zeros instead. +This doesn't affect most architectures or programs. + +### Operations use the best instructions available + +Operations provided by this module compile to the best available SIMD instructions. + +Portable SIMD is not a low-level vendor library, and operations in portable SIMD _do not_ necessarily map to a single instruction. +Instead, they map to a reasonable implementation of the operation for the target. + +Consistency between targets is not compromised to use faster or fewer instructions. +In some cases, `std::arch` will provide a faster function that has slightly different behavior than the `std::simd` equivalent. +For example, [`_mm_min_ps`](`core::arch::x86_64::_mm_min_ps`)[^1] can be slightly faster than [`SimdFloat::simd_min`](`num::SimdFloat::simd_min`), but does not conform to the IEEE standard also used by [`f32::min`]. +When necessary, [`Simd`] can be converted to the types provided by `std::arch` to make use of target-specific functions. + +Many targets simply don't have SIMD, or don't support SIMD for a particular element type. +In those cases, regular scalar operations are generated instead. + +[^1]: `_mm_min_ps(x, y)` is equivalent to `x.simd_lt(y).select(x, y)` diff --git a/crates/core_simd/src/fmt.rs b/crates/core_simd/src/fmt.rs index b7317969cbb4..3a540f5a0490 100644 --- a/crates/core_simd/src/fmt.rs +++ b/crates/core_simd/src/fmt.rs @@ -1,9 +1,9 @@ use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; use core::fmt; -impl fmt::Debug for Simd +impl fmt::Debug for Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement + fmt::Debug, { /// A `Simd` has a debug format like the one for `[T]`: diff --git a/crates/core_simd/src/intrinsics.rs b/crates/core_simd/src/intrinsics.rs index dd6698e2ba56..b27893bc7294 100644 --- a/crates/core_simd/src/intrinsics.rs +++ b/crates/core_simd/src/intrinsics.rs @@ -160,4 +160,10 @@ extern "platform-intrinsic" { /// convert an exposed address back to a pointer pub(crate) fn simd_from_exposed_addr(addr: T) -> U; + + // Integer operations + pub(crate) fn simd_bswap(x: T) -> T; + pub(crate) fn simd_bitreverse(x: T) -> T; + pub(crate) fn simd_ctlz(x: T) -> T; + pub(crate) fn simd_cttz(x: T) -> T; } diff --git a/crates/core_simd/src/iter.rs b/crates/core_simd/src/iter.rs index 328c995b81dd..b3732fd74d5f 100644 --- a/crates/core_simd/src/iter.rs +++ b/crates/core_simd/src/iter.rs @@ -6,9 +6,9 @@ use core::{ macro_rules! impl_traits { { $type:ty } => { - impl Sum for Simd<$type, LANES> + impl Sum for Simd<$type, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn sum>(iter: I) -> Self { @@ -16,9 +16,9 @@ macro_rules! impl_traits { } } - impl Product for Simd<$type, LANES> + impl Product for Simd<$type, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn product>(iter: I) -> Self { @@ -26,9 +26,9 @@ macro_rules! impl_traits { } } - impl<'a, const LANES: usize> Sum<&'a Self> for Simd<$type, LANES> + impl<'a, const N: usize> Sum<&'a Self> for Simd<$type, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn sum>(iter: I) -> Self { @@ -36,9 +36,9 @@ macro_rules! impl_traits { } } - impl<'a, const LANES: usize> Product<&'a Self> for Simd<$type, LANES> + impl<'a, const N: usize> Product<&'a Self> for Simd<$type, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn product>(iter: I) -> Self { diff --git a/crates/core_simd/src/lane_count.rs b/crates/core_simd/src/lane_count.rs index 2b91eb9e8004..4cd7265ed671 100644 --- a/crates/core_simd/src/lane_count.rs +++ b/crates/core_simd/src/lane_count.rs @@ -4,11 +4,11 @@ mod sealed { use sealed::Sealed; /// Specifies the number of lanes in a SIMD vector as a type. -pub struct LaneCount; +pub struct LaneCount; -impl LaneCount { +impl LaneCount { /// The number of bytes in a bitmask with this many lanes. - pub const BITMASK_LEN: usize = (LANES + 7) / 8; + pub const BITMASK_LEN: usize = (N + 7) / 8; } /// Statically guarantees that a lane count is marked as supported. @@ -21,7 +21,7 @@ pub trait SupportedLaneCount: Sealed { type BitMask: Copy + Default + AsRef<[u8]> + AsMut<[u8]>; } -impl Sealed for LaneCount {} +impl Sealed for LaneCount {} macro_rules! supported_lane_count { ($($lanes:literal),+) => { diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index fde406bda706..64ba9705ef52 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -5,6 +5,7 @@ const_mut_refs, convert_float_to_int, decl_macro, + inline_const, intra_doc_pointers, platform_intrinsics, repr_simd, @@ -14,10 +15,9 @@ strict_provenance, ptr_metadata )] -#![cfg_attr(feature = "generic_const_exprs", feature(generic_const_exprs))] -#![cfg_attr(feature = "generic_const_exprs", allow(incomplete_features))] #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really #![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)] +#![allow(internal_features)] #![unstable(feature = "portable_simd", issue = "86656")] //! Portable SIMD module. diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index fea687bdc1ae..0623d2bf3d12 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -1,4 +1,4 @@ -//! Types and traits associated with masking lanes of vectors. +//! Types and traits associated with masking elements of vectors. //! Types representing #![allow(non_camel_case_types)] @@ -12,13 +12,9 @@ )] mod mask_impl; -mod to_bitmask; -pub use to_bitmask::ToBitMask; - -#[cfg(feature = "generic_const_exprs")] -pub use to_bitmask::{bitmask_len, ToBitMaskArray}; - -use crate::simd::{intrinsics, LaneCount, Simd, SimdElement, SimdPartialEq, SupportedLaneCount}; +use crate::simd::{ + cmp::SimdPartialEq, intrinsics, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount, +}; use core::cmp::Ordering; use core::{fmt, mem}; @@ -32,13 +28,17 @@ mod sealed { /// prevent us from ever removing that bound, or from implementing `MaskElement` on /// non-`PartialEq` types in the future. pub trait Sealed { - fn valid(values: Simd) -> bool + fn valid(values: Simd) -> bool where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, Self: SimdElement; fn eq(self, other: Self) -> bool; + fn as_usize(self) -> usize; + + type Unsigned: SimdElement; + const TRUE: Self; const FALSE: Self; @@ -50,15 +50,15 @@ use sealed::Sealed; /// /// # Safety /// Type must be a signed integer. -pub unsafe trait MaskElement: SimdElement + Sealed {} +pub unsafe trait MaskElement: SimdElement + SimdCast + Sealed {} macro_rules! impl_element { - { $ty:ty } => { + { $ty:ty, $unsigned:ty } => { impl Sealed for $ty { #[inline] - fn valid(value: Simd) -> bool + fn valid(value: Simd) -> bool where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { (value.simd_eq(Simd::splat(0 as _)) | value.simd_eq(Simd::splat(-1 as _))).all() } @@ -66,6 +66,13 @@ macro_rules! impl_element { #[inline] fn eq(self, other: Self) -> bool { self == other } + #[inline] + fn as_usize(self) -> usize { + self as usize + } + + type Unsigned = $unsigned; + const TRUE: Self = -1; const FALSE: Self = 0; } @@ -75,36 +82,36 @@ macro_rules! impl_element { } } -impl_element! { i8 } -impl_element! { i16 } -impl_element! { i32 } -impl_element! { i64 } -impl_element! { isize } +impl_element! { i8, u8 } +impl_element! { i16, u16 } +impl_element! { i32, u32 } +impl_element! { i64, u64 } +impl_element! { isize, usize } -/// A SIMD vector mask for `LANES` elements of width specified by `Element`. +/// A SIMD vector mask for `N` elements of width specified by `Element`. /// -/// Masks represent boolean inclusion/exclusion on a per-lane basis. +/// Masks represent boolean inclusion/exclusion on a per-element basis. /// /// The layout of this type is unspecified, and may change between platforms /// and/or Rust versions, and code should not assume that it is equivalent to -/// `[T; LANES]`. -#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 -pub struct Mask(mask_impl::Mask) +/// `[T; N]`. +#[repr(transparent)] +pub struct Mask(mask_impl::Mask) where T: MaskElement, - LaneCount: SupportedLaneCount; + LaneCount: SupportedLaneCount; -impl Copy for Mask +impl Copy for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { } -impl Clone for Mask +impl Clone for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn clone(&self) -> Self { @@ -112,12 +119,12 @@ where } } -impl Mask +impl Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - /// Construct a mask by setting all lanes to the given value. + /// Construct a mask by setting all elements to the given value. #[inline] pub fn splat(value: bool) -> Self { Self(mask_impl::Mask::splat(value)) @@ -125,7 +132,7 @@ where /// Converts an array of bools to a SIMD mask. #[inline] - pub fn from_array(array: [bool; LANES]) -> Self { + pub fn from_array(array: [bool; N]) -> Self { // SAFETY: Rust's bool has a layout of 1 byte (u8) with a value of // true: 0b_0000_0001 // false: 0b_0000_0000 @@ -133,16 +140,15 @@ where // This would be hypothetically valid as an "in-place" transmute, // but these are "dependently-sized" types, so copy elision it is! unsafe { - let bytes: [u8; LANES] = mem::transmute_copy(&array); - let bools: Simd = - intrinsics::simd_ne(Simd::from_array(bytes), Simd::splat(0u8)); + let bytes: [u8; N] = mem::transmute_copy(&array); + let bools: Simd = intrinsics::simd_ne(Simd::from_array(bytes), Simd::splat(0u8)); Mask::from_int_unchecked(intrinsics::simd_cast(bools)) } } /// Converts a SIMD mask to an array of bools. #[inline] - pub fn to_array(self) -> [bool; LANES] { + pub fn to_array(self) -> [bool; N] { // This follows mostly the same logic as from_array. // SAFETY: Rust's bool has a layout of 1 byte (u8) with a value of // true: 0b_0000_0001 @@ -154,7 +160,7 @@ where // This would be hypothetically valid as an "in-place" transmute, // but these are "dependently-sized" types, so copy elision it is! unsafe { - let mut bytes: Simd = intrinsics::simd_cast(self.to_int()); + let mut bytes: Simd = intrinsics::simd_cast(self.to_int()); bytes &= Simd::splat(1i8); mem::transmute_copy(&bytes) } @@ -164,10 +170,10 @@ where /// represents `true`. /// /// # Safety - /// All lanes must be either 0 or -1. + /// All elements must be either 0 or -1. #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub unsafe fn from_int_unchecked(value: Simd) -> Self { + pub unsafe fn from_int_unchecked(value: Simd) -> Self { // Safety: the caller must confirm this invariant unsafe { Self(mask_impl::Mask::from_int_unchecked(value)) } } @@ -176,11 +182,11 @@ where /// represents `true`. /// /// # Panics - /// Panics if any lane is not 0 or -1. + /// Panics if any element is not 0 or -1. #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] #[track_caller] - pub fn from_int(value: Simd) -> Self { + pub fn from_int(value: Simd) -> Self { assert!(T::valid(value), "all values must be either 0 or -1",); // Safety: the validity has been checked unsafe { Self::from_int_unchecked(value) } @@ -190,121 +196,244 @@ where /// represents `true`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_int(self) -> Simd { + pub fn to_int(self) -> Simd { self.0.to_int() } - /// Converts the mask to a mask of any other lane size. + /// Converts the mask to a mask of any other element size. #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn cast(self) -> Mask { + pub fn cast(self) -> Mask { Mask(self.0.convert()) } - /// Tests the value of the specified lane. + /// Tests the value of the specified element. /// /// # Safety - /// `lane` must be less than `LANES`. + /// `index` must be less than `self.len()`. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub unsafe fn test_unchecked(&self, lane: usize) -> bool { + pub unsafe fn test_unchecked(&self, index: usize) -> bool { // Safety: the caller must confirm this invariant - unsafe { self.0.test_unchecked(lane) } + unsafe { self.0.test_unchecked(index) } } - /// Tests the value of the specified lane. + /// Tests the value of the specified element. /// /// # Panics - /// Panics if `lane` is greater than or equal to the number of lanes in the vector. + /// Panics if `index` is greater than or equal to the number of elements in the vector. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] #[track_caller] - pub fn test(&self, lane: usize) -> bool { - assert!(lane < LANES, "lane index out of range"); - // Safety: the lane index has been checked - unsafe { self.test_unchecked(lane) } + pub fn test(&self, index: usize) -> bool { + assert!(index < N, "element index out of range"); + // Safety: the element index has been checked + unsafe { self.test_unchecked(index) } } - /// Sets the value of the specified lane. + /// Sets the value of the specified element. /// /// # Safety - /// `lane` must be less than `LANES`. + /// `index` must be less than `self.len()`. #[inline] - pub unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { + pub unsafe fn set_unchecked(&mut self, index: usize, value: bool) { // Safety: the caller must confirm this invariant unsafe { - self.0.set_unchecked(lane, value); + self.0.set_unchecked(index, value); } } - /// Sets the value of the specified lane. + /// Sets the value of the specified element. /// /// # Panics - /// Panics if `lane` is greater than or equal to the number of lanes in the vector. + /// Panics if `index` is greater than or equal to the number of elements in the vector. #[inline] #[track_caller] - pub fn set(&mut self, lane: usize, value: bool) { - assert!(lane < LANES, "lane index out of range"); - // Safety: the lane index has been checked + pub fn set(&mut self, index: usize, value: bool) { + assert!(index < N, "element index out of range"); + // Safety: the element index has been checked unsafe { - self.set_unchecked(lane, value); + self.set_unchecked(index, value); } } - /// Returns true if any lane is set, or false otherwise. + /// Returns true if any element is set, or false otherwise. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] pub fn any(self) -> bool { self.0.any() } - /// Returns true if all lanes are set, or false otherwise. + /// Returns true if all elements are set, or false otherwise. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] pub fn all(self) -> bool { self.0.all() } + + /// Create a bitmask from a mask. + /// + /// Each bit is set if the corresponding element in the mask is `true`. + /// If the mask contains more than 64 elements, the bitmask is truncated to the first 64. + #[inline] + #[must_use = "method returns a new integer and does not mutate the original value"] + pub fn to_bitmask(self) -> u64 { + self.0.to_bitmask_integer() + } + + /// Create a mask from a bitmask. + /// + /// For each bit, if it is set, the corresponding element in the mask is set to `true`. + /// If the mask contains more than 64 elements, the remainder are set to `false`. + #[inline] + #[must_use = "method returns a new mask and does not mutate the original value"] + pub fn from_bitmask(bitmask: u64) -> Self { + Self(mask_impl::Mask::from_bitmask_integer(bitmask)) + } + + /// Create a bitmask vector from a mask. + /// + /// Each bit is set if the corresponding element in the mask is `true`. + /// The remaining bits are unset. + /// + /// The bits are packed into the first N bits of the vector: + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x8; + /// let mask = mask32x8::from_array([true, false, true, false, false, false, true, false]); + /// assert_eq!(mask.to_bitmask_vector()[0], 0b01000101); + /// ``` + #[inline] + #[must_use = "method returns a new integer and does not mutate the original value"] + pub fn to_bitmask_vector(self) -> Simd { + self.0.to_bitmask_vector() + } + + /// Create a mask from a bitmask vector. + /// + /// For each bit, if it is set, the corresponding element in the mask is set to `true`. + /// + /// The bits are packed into the first N bits of the vector: + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{mask32x8, u8x8}; + /// let bitmask = u8x8::from_array([0b01000101, 0, 0, 0, 0, 0, 0, 0]); + /// assert_eq!( + /// mask32x8::from_bitmask_vector(bitmask), + /// mask32x8::from_array([true, false, true, false, false, false, true, false]), + /// ); + /// ``` + #[inline] + #[must_use = "method returns a new mask and does not mutate the original value"] + pub fn from_bitmask_vector(bitmask: Simd) -> Self { + Self(mask_impl::Mask::from_bitmask_vector(bitmask)) + } + + /// Find the index of the first set element. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x8; + /// assert_eq!(mask32x8::splat(false).first_set(), None); + /// assert_eq!(mask32x8::splat(true).first_set(), Some(0)); + /// + /// let mask = mask32x8::from_array([false, true, false, false, true, false, false, true]); + /// assert_eq!(mask.first_set(), Some(1)); + /// ``` + #[inline] + #[must_use = "method returns the index and does not mutate the original value"] + pub fn first_set(self) -> Option { + // If bitmasks are efficient, using them is better + if cfg!(target_feature = "sse") && N <= 64 { + let tz = self.to_bitmask().trailing_zeros(); + return if tz == 64 { None } else { Some(tz as usize) }; + } + + // To find the first set index: + // * create a vector 0..N + // * replace unset mask elements in that vector with -1 + // * perform _unsigned_ reduce-min + // * check if the result is -1 or an index + + let index = Simd::from_array( + const { + let mut index = [0; N]; + let mut i = 0; + while i < N { + index[i] = i; + i += 1; + } + index + }, + ); + + // Safety: the input and output are integer vectors + let index: Simd = unsafe { intrinsics::simd_cast(index) }; + + let masked_index = self.select(index, Self::splat(true).to_int()); + + // Safety: the input and output are integer vectors + let masked_index: Simd = unsafe { intrinsics::simd_cast(masked_index) }; + + // Safety: the input is an integer vector + let min_index: T::Unsigned = unsafe { intrinsics::simd_reduce_min(masked_index) }; + + // Safety: the return value is the unsigned version of T + let min_index: T = unsafe { core::mem::transmute_copy(&min_index) }; + + if min_index.eq(T::TRUE) { + None + } else { + Some(min_index.as_usize()) + } + } } // vector/array conversion -impl From<[bool; LANES]> for Mask +impl From<[bool; N]> for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] - fn from(array: [bool; LANES]) -> Self { + fn from(array: [bool; N]) -> Self { Self::from_array(array) } } -impl From> for [bool; LANES] +impl From> for [bool; N] where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] - fn from(vector: Mask) -> Self { + fn from(vector: Mask) -> Self { vector.to_array() } } -impl Default for Mask +impl Default for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] - #[must_use = "method returns a defaulted mask with all lanes set to false (0)"] + #[must_use = "method returns a defaulted mask with all elements set to false (0)"] fn default() -> Self { Self::splat(false) } } -impl PartialEq for Mask +impl PartialEq for Mask where T: MaskElement + PartialEq, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] @@ -313,10 +442,10 @@ where } } -impl PartialOrd for Mask +impl PartialOrd for Mask where T: MaskElement + PartialOrd, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] #[must_use = "method returns a new Ordering and does not mutate the original value"] @@ -325,23 +454,23 @@ where } } -impl fmt::Debug for Mask +impl fmt::Debug for Mask where T: MaskElement + fmt::Debug, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list() - .entries((0..LANES).map(|lane| self.test(lane))) + .entries((0..N).map(|i| self.test(i))) .finish() } } -impl core::ops::BitAnd for Mask +impl core::ops::BitAnd for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -351,10 +480,10 @@ where } } -impl core::ops::BitAnd for Mask +impl core::ops::BitAnd for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -364,23 +493,23 @@ where } } -impl core::ops::BitAnd> for bool +impl core::ops::BitAnd> for bool where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Output = Mask; + type Output = Mask; #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - fn bitand(self, rhs: Mask) -> Mask { + fn bitand(self, rhs: Mask) -> Mask { Mask::splat(self) & rhs } } -impl core::ops::BitOr for Mask +impl core::ops::BitOr for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -390,10 +519,10 @@ where } } -impl core::ops::BitOr for Mask +impl core::ops::BitOr for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -403,23 +532,23 @@ where } } -impl core::ops::BitOr> for bool +impl core::ops::BitOr> for bool where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Output = Mask; + type Output = Mask; #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - fn bitor(self, rhs: Mask) -> Mask { + fn bitor(self, rhs: Mask) -> Mask { Mask::splat(self) | rhs } } -impl core::ops::BitXor for Mask +impl core::ops::BitXor for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -429,10 +558,10 @@ where } } -impl core::ops::BitXor for Mask +impl core::ops::BitXor for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -442,25 +571,25 @@ where } } -impl core::ops::BitXor> for bool +impl core::ops::BitXor> for bool where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Output = Mask; + type Output = Mask; #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - fn bitxor(self, rhs: Mask) -> Self::Output { + fn bitxor(self, rhs: Mask) -> Self::Output { Mask::splat(self) ^ rhs } } -impl core::ops::Not for Mask +impl core::ops::Not for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Output = Mask; + type Output = Mask; #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] fn not(self) -> Self::Output { @@ -468,10 +597,10 @@ where } } -impl core::ops::BitAndAssign for Mask +impl core::ops::BitAndAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn bitand_assign(&mut self, rhs: Self) { @@ -479,10 +608,10 @@ where } } -impl core::ops::BitAndAssign for Mask +impl core::ops::BitAndAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn bitand_assign(&mut self, rhs: bool) { @@ -490,10 +619,10 @@ where } } -impl core::ops::BitOrAssign for Mask +impl core::ops::BitOrAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn bitor_assign(&mut self, rhs: Self) { @@ -501,10 +630,10 @@ where } } -impl core::ops::BitOrAssign for Mask +impl core::ops::BitOrAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn bitor_assign(&mut self, rhs: bool) { @@ -512,10 +641,10 @@ where } } -impl core::ops::BitXorAssign for Mask +impl core::ops::BitXorAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn bitxor_assign(&mut self, rhs: Self) { @@ -523,10 +652,10 @@ where } } -impl core::ops::BitXorAssign for Mask +impl core::ops::BitXorAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn bitxor_assign(&mut self, rhs: bool) { @@ -537,12 +666,12 @@ where macro_rules! impl_from { { $from:ty => $($to:ty),* } => { $( - impl From> for Mask<$to, LANES> + impl From> for Mask<$to, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] - fn from(value: Mask<$from, LANES>) -> Self { + fn from(value: Mask<$from, N>) -> Self { value.cast() } } diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index 20465ba9b07e..6ddff07fea25 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -1,30 +1,30 @@ #![allow(unused_imports)] use super::MaskElement; use crate::simd::intrinsics; -use crate::simd::{LaneCount, Simd, SupportedLaneCount, ToBitMask}; +use crate::simd::{LaneCount, Simd, SupportedLaneCount}; use core::marker::PhantomData; /// A mask where each lane is represented by a single bit. #[repr(transparent)] -pub struct Mask( - as SupportedLaneCount>::BitMask, +pub struct Mask( + as SupportedLaneCount>::BitMask, PhantomData, ) where T: MaskElement, - LaneCount: SupportedLaneCount; + LaneCount: SupportedLaneCount; -impl Copy for Mask +impl Copy for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { } -impl Clone for Mask +impl Clone for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn clone(&self) -> Self { @@ -32,10 +32,10 @@ where } } -impl PartialEq for Mask +impl PartialEq for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn eq(&self, other: &Self) -> bool { @@ -43,10 +43,10 @@ where } } -impl PartialOrd for Mask +impl PartialOrd for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn partial_cmp(&self, other: &Self) -> Option { @@ -54,17 +54,17 @@ where } } -impl Eq for Mask +impl Eq for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { } -impl Ord for Mask +impl Ord for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn cmp(&self, other: &Self) -> core::cmp::Ordering { @@ -72,22 +72,22 @@ where } } -impl Mask +impl Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn splat(value: bool) -> Self { - let mut mask = as SupportedLaneCount>::BitMask::default(); + let mut mask = as SupportedLaneCount>::BitMask::default(); if value { mask.as_mut().fill(u8::MAX) } else { mask.as_mut().fill(u8::MIN) } - if LANES % 8 > 0 { - *mask.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - LANES % 8); + if N % 8 > 0 { + *mask.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - N % 8); } Self(mask, PhantomData) } @@ -107,7 +107,7 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_int(self) -> Simd { + pub fn to_int(self) -> Simd { unsafe { intrinsics::simd_select_bitmask(self.0, Simd::splat(T::TRUE), Simd::splat(T::FALSE)) } @@ -115,51 +115,47 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub unsafe fn from_int_unchecked(value: Simd) -> Self { + pub unsafe fn from_int_unchecked(value: Simd) -> Self { unsafe { Self(intrinsics::simd_bitmask(value), PhantomData) } } - #[cfg(feature = "generic_const_exprs")] #[inline] - #[must_use = "method returns a new array and does not mutate the original value"] - pub fn to_bitmask_array(self) -> [u8; N] { - assert!(core::mem::size_of::() == N); - - // Safety: converting an integer to an array of bytes of the same size is safe - unsafe { core::mem::transmute_copy(&self.0) } - } - - #[cfg(feature = "generic_const_exprs")] - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_array(bitmask: [u8; N]) -> Self { - assert!(core::mem::size_of::() == N); - - // Safety: converting an array of bytes to an integer of the same size is safe - Self(unsafe { core::mem::transmute_copy(&bitmask) }, PhantomData) - } - - #[inline] - pub fn to_bitmask_integer(self) -> U - where - super::Mask: ToBitMask, - { - // Safety: these are the same types - unsafe { core::mem::transmute_copy(&self.0) } - } - - #[inline] - pub fn from_bitmask_integer(bitmask: U) -> Self - where - super::Mask: ToBitMask, - { - // Safety: these are the same types - unsafe { Self(core::mem::transmute_copy(&bitmask), PhantomData) } + #[must_use = "method returns a new vector and does not mutate the original value"] + pub fn to_bitmask_vector(self) -> Simd { + let mut bitmask = Simd::splat(0); + bitmask.as_mut_array()[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref()); + bitmask } #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn convert(self) -> Mask + pub fn from_bitmask_vector(bitmask: Simd) -> Self { + let mut bytes = as SupportedLaneCount>::BitMask::default(); + let len = bytes.as_ref().len(); + bytes.as_mut().copy_from_slice(&bitmask.as_array()[..len]); + Self(bytes, PhantomData) + } + + #[inline] + pub fn to_bitmask_integer(self) -> u64 { + let mut bitmask = [0u8; 8]; + bitmask[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref()); + u64::from_ne_bytes(bitmask) + } + + #[inline] + pub fn from_bitmask_integer(bitmask: u64) -> Self { + let mut bytes = as SupportedLaneCount>::BitMask::default(); + let len = bytes.as_mut().len(); + bytes + .as_mut() + .copy_from_slice(&bitmask.to_ne_bytes()[..len]); + Self(bytes, PhantomData) + } + + #[inline] + #[must_use = "method returns a new mask and does not mutate the original value"] + pub fn convert(self) -> Mask where U: MaskElement, { @@ -180,11 +176,11 @@ where } } -impl core::ops::BitAnd for Mask +impl core::ops::BitAnd for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, - as SupportedLaneCount>::BitMask: AsRef<[u8]> + AsMut<[u8]>, + LaneCount: SupportedLaneCount, + as SupportedLaneCount>::BitMask: AsRef<[u8]> + AsMut<[u8]>, { type Output = Self; #[inline] @@ -197,11 +193,11 @@ where } } -impl core::ops::BitOr for Mask +impl core::ops::BitOr for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, - as SupportedLaneCount>::BitMask: AsRef<[u8]> + AsMut<[u8]>, + LaneCount: SupportedLaneCount, + as SupportedLaneCount>::BitMask: AsRef<[u8]> + AsMut<[u8]>, { type Output = Self; #[inline] @@ -214,10 +210,10 @@ where } } -impl core::ops::BitXor for Mask +impl core::ops::BitXor for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -230,10 +226,10 @@ where } } -impl core::ops::Not for Mask +impl core::ops::Not for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -242,8 +238,8 @@ where for x in self.0.as_mut() { *x = !*x; } - if LANES % 8 > 0 { - *self.0.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - LANES % 8); + if N % 8 > 0 { + *self.0.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - N % 8); } self } diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index 1d13c45b8e70..63964f455e05 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -1,29 +1,25 @@ //! Masks that take up full SIMD vector registers. -use super::MaskElement; use crate::simd::intrinsics; -use crate::simd::{LaneCount, Simd, SupportedLaneCount, ToBitMask}; - -#[cfg(feature = "generic_const_exprs")] -use crate::simd::ToBitMaskArray; +use crate::simd::{LaneCount, MaskElement, Simd, SupportedLaneCount}; #[repr(transparent)] -pub struct Mask(Simd) +pub struct Mask(Simd) where T: MaskElement, - LaneCount: SupportedLaneCount; + LaneCount: SupportedLaneCount; -impl Copy for Mask +impl Copy for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { } -impl Clone for Mask +impl Clone for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] @@ -32,10 +28,10 @@ where } } -impl PartialEq for Mask +impl PartialEq for Mask where T: MaskElement + PartialEq, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn eq(&self, other: &Self) -> bool { @@ -43,10 +39,10 @@ where } } -impl PartialOrd for Mask +impl PartialOrd for Mask where T: MaskElement + PartialOrd, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn partial_cmp(&self, other: &Self) -> Option { @@ -54,17 +50,17 @@ where } } -impl Eq for Mask +impl Eq for Mask where T: MaskElement + Eq, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { } -impl Ord for Mask +impl Ord for Mask where T: MaskElement + Ord, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn cmp(&self, other: &Self) -> core::cmp::Ordering { @@ -101,10 +97,10 @@ macro_rules! impl_reverse_bits { impl_reverse_bits! { u8, u16, u32, u64 } -impl Mask +impl Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] @@ -125,19 +121,19 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_int(self) -> Simd { + pub fn to_int(self) -> Simd { self.0 } #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub unsafe fn from_int_unchecked(value: Simd) -> Self { + pub unsafe fn from_int_unchecked(value: Simd) -> Self { Self(value) } #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn convert(self) -> Mask + pub fn convert(self) -> Mask where U: MaskElement, { @@ -145,62 +141,50 @@ where unsafe { Mask(intrinsics::simd_cast(self.0)) } } - #[cfg(feature = "generic_const_exprs")] #[inline] - #[must_use = "method returns a new array and does not mutate the original value"] - pub fn to_bitmask_array(self) -> [u8; N] - where - super::Mask: ToBitMaskArray, - [(); as ToBitMaskArray>::BYTES]: Sized, - { - assert_eq!( as ToBitMaskArray>::BYTES, N); + #[must_use = "method returns a new vector and does not mutate the original value"] + pub fn to_bitmask_vector(self) -> Simd { + let mut bitmask = Simd::splat(0); - // Safety: N is the correct bitmask size + // Safety: Bytes is the right size array unsafe { // Compute the bitmask - let bitmask: [u8; as ToBitMaskArray>::BYTES] = + let mut bytes: as SupportedLaneCount>::BitMask = intrinsics::simd_bitmask(self.0); - // Transmute to the return type, previously asserted to be the same size - let mut bitmask: [u8; N] = core::mem::transmute_copy(&bitmask); - // LLVM assumes bit order should match endianness if cfg!(target_endian = "big") { - for x in bitmask.as_mut() { - *x = x.reverse_bits(); + for x in bytes.as_mut() { + *x = x.reverse_bits() } - }; + } - bitmask + bitmask.as_mut_array()[..bytes.as_ref().len()].copy_from_slice(bytes.as_ref()); } + + bitmask } - #[cfg(feature = "generic_const_exprs")] #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_array(mut bitmask: [u8; N]) -> Self - where - super::Mask: ToBitMaskArray, - [(); as ToBitMaskArray>::BYTES]: Sized, - { - assert_eq!( as ToBitMaskArray>::BYTES, N); + pub fn from_bitmask_vector(bitmask: Simd) -> Self { + let mut bytes = as SupportedLaneCount>::BitMask::default(); - // Safety: N is the correct bitmask size + // Safety: Bytes is the right size array unsafe { + let len = bytes.as_ref().len(); + bytes.as_mut().copy_from_slice(&bitmask.as_array()[..len]); + // LLVM assumes bit order should match endianness if cfg!(target_endian = "big") { - for x in bitmask.as_mut() { + for x in bytes.as_mut() { *x = x.reverse_bits(); } } - // Transmute to the bitmask type, previously asserted to be the same size - let bitmask: [u8; as ToBitMaskArray>::BYTES] = - core::mem::transmute_copy(&bitmask); - // Compute the regular mask Self::from_int_unchecked(intrinsics::simd_select_bitmask( - bitmask, + bytes, Self::splat(true).to_int(), Self::splat(false).to_int(), )) @@ -208,40 +192,81 @@ where } #[inline] - pub(crate) fn to_bitmask_integer(self) -> U + unsafe fn to_bitmask_impl(self) -> U where - super::Mask: ToBitMask, + LaneCount: SupportedLaneCount, { - // Safety: U is required to be the appropriate bitmask type - let bitmask: U = unsafe { intrinsics::simd_bitmask(self.0) }; + let resized = self.to_int().resize::(T::FALSE); + + // Safety: `resized` is an integer vector with length M, which must match T + let bitmask: U = unsafe { intrinsics::simd_bitmask(resized) }; // LLVM assumes bit order should match endianness if cfg!(target_endian = "big") { - bitmask.reverse_bits(LANES) + bitmask.reverse_bits(M) } else { bitmask } } #[inline] - pub(crate) fn from_bitmask_integer(bitmask: U) -> Self + unsafe fn from_bitmask_impl(bitmask: U) -> Self where - super::Mask: ToBitMask, + LaneCount: SupportedLaneCount, { // LLVM assumes bit order should match endianness let bitmask = if cfg!(target_endian = "big") { - bitmask.reverse_bits(LANES) + bitmask.reverse_bits(M) } else { bitmask }; - // Safety: U is required to be the appropriate bitmask type - unsafe { - Self::from_int_unchecked(intrinsics::simd_select_bitmask( + // SAFETY: `mask` is the correct bitmask type for a u64 bitmask + let mask: Simd = unsafe { + intrinsics::simd_select_bitmask( bitmask, - Self::splat(true).to_int(), - Self::splat(false).to_int(), - )) + Simd::::splat(T::TRUE), + Simd::::splat(T::FALSE), + ) + }; + + // SAFETY: `mask` only contains `T::TRUE` or `T::FALSE` + unsafe { Self::from_int_unchecked(mask.resize::(T::FALSE)) } + } + + #[inline] + pub(crate) fn to_bitmask_integer(self) -> u64 { + // TODO modify simd_bitmask to zero-extend output, making this unnecessary + if N <= 8 { + // Safety: bitmask matches length + unsafe { self.to_bitmask_impl::() as u64 } + } else if N <= 16 { + // Safety: bitmask matches length + unsafe { self.to_bitmask_impl::() as u64 } + } else if N <= 32 { + // Safety: bitmask matches length + unsafe { self.to_bitmask_impl::() as u64 } + } else { + // Safety: bitmask matches length + unsafe { self.to_bitmask_impl::() } + } + } + + #[inline] + pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self { + // TODO modify simd_bitmask_select to truncate input, making this unnecessary + if N <= 8 { + // Safety: bitmask matches length + unsafe { Self::from_bitmask_impl::(bitmask as u8) } + } else if N <= 16 { + // Safety: bitmask matches length + unsafe { Self::from_bitmask_impl::(bitmask as u16) } + } else if N <= 32 { + // Safety: bitmask matches length + unsafe { Self::from_bitmask_impl::(bitmask as u32) } + } else { + // Safety: bitmask matches length + unsafe { Self::from_bitmask_impl::(bitmask) } } } @@ -260,21 +285,21 @@ where } } -impl From> for Simd +impl From> for Simd where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] - fn from(value: Mask) -> Self { + fn from(value: Mask) -> Self { value.0 } } -impl core::ops::BitAnd for Mask +impl core::ops::BitAnd for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -285,10 +310,10 @@ where } } -impl core::ops::BitOr for Mask +impl core::ops::BitOr for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -299,10 +324,10 @@ where } } -impl core::ops::BitXor for Mask +impl core::ops::BitXor for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -313,10 +338,10 @@ where } } -impl core::ops::Not for Mask +impl core::ops::Not for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; #[inline] diff --git a/crates/core_simd/src/masks/to_bitmask.rs b/crates/core_simd/src/masks/to_bitmask.rs deleted file mode 100644 index fc7d6b781f2f..000000000000 --- a/crates/core_simd/src/masks/to_bitmask.rs +++ /dev/null @@ -1,97 +0,0 @@ -use super::{mask_impl, Mask, MaskElement}; -use crate::simd::{LaneCount, SupportedLaneCount}; - -mod sealed { - pub trait Sealed {} -} -pub use sealed::Sealed; - -impl Sealed for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} - -/// Converts masks to and from integer bitmasks. -/// -/// Each bit of the bitmask corresponds to a mask lane, starting with the LSB. -pub trait ToBitMask: Sealed { - /// The integer bitmask type. - type BitMask; - - /// Converts a mask to a bitmask. - fn to_bitmask(self) -> Self::BitMask; - - /// Converts a bitmask to a mask. - fn from_bitmask(bitmask: Self::BitMask) -> Self; -} - -/// Converts masks to and from byte array bitmasks. -/// -/// Each bit of the bitmask corresponds to a mask lane, starting with the LSB of the first byte. -#[cfg(feature = "generic_const_exprs")] -pub trait ToBitMaskArray: Sealed { - /// The length of the bitmask array. - const BYTES: usize; - - /// Converts a mask to a bitmask. - fn to_bitmask_array(self) -> [u8; Self::BYTES]; - - /// Converts a bitmask to a mask. - fn from_bitmask_array(bitmask: [u8; Self::BYTES]) -> Self; -} - -macro_rules! impl_integer_intrinsic { - { $(impl ToBitMask for Mask<_, $lanes:literal>)* } => { - $( - impl ToBitMask for Mask { - type BitMask = $int; - - #[inline] - fn to_bitmask(self) -> $int { - self.0.to_bitmask_integer() - } - - #[inline] - fn from_bitmask(bitmask: $int) -> Self { - Self(mask_impl::Mask::from_bitmask_integer(bitmask)) - } - } - )* - } -} - -impl_integer_intrinsic! { - impl ToBitMask for Mask<_, 1> - impl ToBitMask for Mask<_, 2> - impl ToBitMask for Mask<_, 4> - impl ToBitMask for Mask<_, 8> - impl ToBitMask for Mask<_, 16> - impl ToBitMask for Mask<_, 32> - impl ToBitMask for Mask<_, 64> -} - -/// Returns the minimum number of bytes in a bitmask with `lanes` lanes. -#[cfg(feature = "generic_const_exprs")] -pub const fn bitmask_len(lanes: usize) -> usize { - (lanes + 7) / 8 -} - -#[cfg(feature = "generic_const_exprs")] -impl ToBitMaskArray for Mask -where - LaneCount: SupportedLaneCount, -{ - const BYTES: usize = bitmask_len(LANES); - - #[inline] - fn to_bitmask_array(self) -> [u8; Self::BYTES] { - self.0.to_bitmask_array() - } - - #[inline] - fn from_bitmask_array(bitmask: [u8; Self::BYTES]) -> Self { - Mask(mask_impl::Mask::from_bitmask_array(bitmask)) - } -} diff --git a/crates/core_simd/src/mod.rs b/crates/core_simd/src/mod.rs index 19426769858b..fd016f1c6f7a 100644 --- a/crates/core_simd/src/mod.rs +++ b/crates/core_simd/src/mod.rs @@ -3,37 +3,37 @@ mod swizzle; pub(crate) mod intrinsics; -#[cfg(feature = "generic_const_exprs")] -mod to_bytes; - mod alias; mod cast; -mod elements; -mod eq; mod fmt; mod iter; mod lane_count; mod masks; mod ops; -mod ord; mod select; mod swizzle_dyn; +mod to_bytes; mod vector; mod vendor; -#[doc = include_str!("core_simd_docs.md")] pub mod simd { + #![doc = include_str!("core_simd_docs.md")] + pub mod prelude; + pub mod num; + + pub mod ptr; + + pub mod cmp; + pub(crate) use crate::core_simd::intrinsics; pub use crate::core_simd::alias::*; pub use crate::core_simd::cast::*; - pub use crate::core_simd::elements::*; - pub use crate::core_simd::eq::*; pub use crate::core_simd::lane_count::{LaneCount, SupportedLaneCount}; pub use crate::core_simd::masks::*; - pub use crate::core_simd::ord::*; pub use crate::core_simd::swizzle::*; + pub use crate::core_simd::to_bytes::ToBytes; pub use crate::core_simd::vector::*; } diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index b007456cf2cc..8a1b083f0398 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Simd, SimdElement, SimdPartialEq, SupportedLaneCount}; +use crate::simd::{cmp::SimdPartialEq, LaneCount, Simd, SimdElement, SupportedLaneCount}; use core::ops::{Add, Mul}; use core::ops::{BitAnd, BitOr, BitXor}; use core::ops::{Div, Rem, Sub}; @@ -6,12 +6,13 @@ use core::ops::{Shl, Shr}; mod assign; mod deref; +mod shift_scalar; mod unary; -impl core::ops::Index for Simd +impl core::ops::Index for Simd where T: SimdElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, I: core::slice::SliceIndex<[T]>, { type Output = I::Output; @@ -21,10 +22,10 @@ where } } -impl core::ops::IndexMut for Simd +impl core::ops::IndexMut for Simd where T: SimdElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, I: core::slice::SliceIndex<[T]>, { #[inline] diff --git a/crates/core_simd/src/ops/assign.rs b/crates/core_simd/src/ops/assign.rs index d2b48614fc96..0e87785025a3 100644 --- a/crates/core_simd/src/ops/assign.rs +++ b/crates/core_simd/src/ops/assign.rs @@ -8,7 +8,7 @@ use core::ops::{ShlAssign, ShrAssign}; // non-commutative bit binary op-assignme // Arithmetic macro_rules! assign_ops { - ($(impl $assignTrait:ident for Simd + ($(impl $assignTrait:ident for Simd where Self: $trait:ident, { @@ -16,11 +16,11 @@ macro_rules! assign_ops { $call:ident } })*) => { - $(impl $assignTrait for Simd + $(impl $assignTrait for Simd where Self: $trait, T: SimdElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn $assign_call(&mut self, rhs: U) { @@ -32,7 +32,7 @@ macro_rules! assign_ops { assign_ops! { // Arithmetic - impl AddAssign for Simd + impl AddAssign for Simd where Self: Add, { @@ -41,7 +41,7 @@ assign_ops! { } } - impl MulAssign for Simd + impl MulAssign for Simd where Self: Mul, { @@ -50,7 +50,7 @@ assign_ops! { } } - impl SubAssign for Simd + impl SubAssign for Simd where Self: Sub, { @@ -59,7 +59,7 @@ assign_ops! { } } - impl DivAssign for Simd + impl DivAssign for Simd where Self: Div, { @@ -67,7 +67,7 @@ assign_ops! { div } } - impl RemAssign for Simd + impl RemAssign for Simd where Self: Rem, { @@ -77,7 +77,7 @@ assign_ops! { } // Bitops - impl BitAndAssign for Simd + impl BitAndAssign for Simd where Self: BitAnd, { @@ -86,7 +86,7 @@ assign_ops! { } } - impl BitOrAssign for Simd + impl BitOrAssign for Simd where Self: BitOr, { @@ -95,7 +95,7 @@ assign_ops! { } } - impl BitXorAssign for Simd + impl BitXorAssign for Simd where Self: BitXor, { @@ -104,7 +104,7 @@ assign_ops! { } } - impl ShlAssign for Simd + impl ShlAssign for Simd where Self: Shl, { @@ -113,7 +113,7 @@ assign_ops! { } } - impl ShrAssign for Simd + impl ShrAssign for Simd where Self: Shr, { diff --git a/crates/core_simd/src/ops/deref.rs b/crates/core_simd/src/ops/deref.rs index 302bf148bd3e..89a60ba11414 100644 --- a/crates/core_simd/src/ops/deref.rs +++ b/crates/core_simd/src/ops/deref.rs @@ -5,16 +5,16 @@ use super::*; macro_rules! deref_lhs { - (impl $trait:ident for $simd:ty { + (impl $trait:ident for $simd:ty { fn $call:ident }) => { - impl $trait<$simd> for &$simd + impl $trait<$simd> for &$simd where T: SimdElement, $simd: $trait<$simd, Output = $simd>, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Output = Simd; + type Output = Simd; #[inline] #[must_use = "operator returns a new vector without mutating the inputs"] @@ -26,16 +26,16 @@ macro_rules! deref_lhs { } macro_rules! deref_rhs { - (impl $trait:ident for $simd:ty { + (impl $trait:ident for $simd:ty { fn $call:ident }) => { - impl $trait<&$simd> for $simd + impl $trait<&$simd> for $simd where T: SimdElement, $simd: $trait<$simd, Output = $simd>, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Output = Simd; + type Output = Simd; #[inline] #[must_use = "operator returns a new vector without mutating the inputs"] @@ -47,25 +47,25 @@ macro_rules! deref_rhs { } macro_rules! deref_ops { - ($(impl $trait:ident for $simd:ty { + ($(impl $trait:ident for $simd:ty { fn $call:ident })*) => { $( deref_rhs! { - impl $trait for $simd { + impl $trait for $simd { fn $call } } deref_lhs! { - impl $trait for $simd { + impl $trait for $simd { fn $call } } - impl<'lhs, 'rhs, T, const LANES: usize> $trait<&'rhs $simd> for &'lhs $simd + impl<'lhs, 'rhs, T, const N: usize> $trait<&'rhs $simd> for &'lhs $simd where T: SimdElement, $simd: $trait<$simd, Output = $simd>, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = $simd; @@ -81,44 +81,44 @@ macro_rules! deref_ops { deref_ops! { // Arithmetic - impl Add for Simd { + impl Add for Simd { fn add } - impl Mul for Simd { + impl Mul for Simd { fn mul } - impl Sub for Simd { + impl Sub for Simd { fn sub } - impl Div for Simd { + impl Div for Simd { fn div } - impl Rem for Simd { + impl Rem for Simd { fn rem } // Bitops - impl BitAnd for Simd { + impl BitAnd for Simd { fn bitand } - impl BitOr for Simd { + impl BitOr for Simd { fn bitor } - impl BitXor for Simd { + impl BitXor for Simd { fn bitxor } - impl Shl for Simd { + impl Shl for Simd { fn shl } - impl Shr for Simd { + impl Shr for Simd { fn shr } } diff --git a/crates/core_simd/src/ops/shift_scalar.rs b/crates/core_simd/src/ops/shift_scalar.rs new file mode 100644 index 000000000000..f5115a5a5e93 --- /dev/null +++ b/crates/core_simd/src/ops/shift_scalar.rs @@ -0,0 +1,62 @@ +// Shift operations uniquely typically only have a scalar on the right-hand side. +// Here, we implement shifts for scalar RHS arguments. + +use crate::simd::{LaneCount, Simd, SupportedLaneCount}; + +macro_rules! impl_splatted_shifts { + { impl $trait:ident :: $trait_fn:ident for $ty:ty } => { + impl core::ops::$trait<$ty> for Simd<$ty, N> + where + LaneCount: SupportedLaneCount, + { + type Output = Self; + #[inline] + fn $trait_fn(self, rhs: $ty) -> Self::Output { + self.$trait_fn(Simd::splat(rhs)) + } + } + + impl core::ops::$trait<&$ty> for Simd<$ty, N> + where + LaneCount: SupportedLaneCount, + { + type Output = Self; + #[inline] + fn $trait_fn(self, rhs: &$ty) -> Self::Output { + self.$trait_fn(Simd::splat(*rhs)) + } + } + + impl<'lhs, const N: usize> core::ops::$trait<$ty> for &'lhs Simd<$ty, N> + where + LaneCount: SupportedLaneCount, + { + type Output = Simd<$ty, N>; + #[inline] + fn $trait_fn(self, rhs: $ty) -> Self::Output { + self.$trait_fn(Simd::splat(rhs)) + } + } + + impl<'lhs, const N: usize> core::ops::$trait<&$ty> for &'lhs Simd<$ty, N> + where + LaneCount: SupportedLaneCount, + { + type Output = Simd<$ty, N>; + #[inline] + fn $trait_fn(self, rhs: &$ty) -> Self::Output { + self.$trait_fn(Simd::splat(*rhs)) + } + } + }; + { $($ty:ty),* } => { + $( + impl_splatted_shifts! { impl Shl::shl for $ty } + impl_splatted_shifts! { impl Shr::shr for $ty } + )* + } +} + +// In the past there were inference issues when generically splatting arguments. +// Enumerate them instead. +impl_splatted_shifts! { i8, i16, i32, i64, isize, u8, u16, u32, u64, usize } diff --git a/crates/core_simd/src/ops/unary.rs b/crates/core_simd/src/ops/unary.rs index 4ad02215034b..a651aa73e952 100644 --- a/crates/core_simd/src/ops/unary.rs +++ b/crates/core_simd/src/ops/unary.rs @@ -3,11 +3,11 @@ use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; use core::ops::{Neg, Not}; // unary ops macro_rules! neg { - ($(impl Neg for Simd<$scalar:ty, LANES>)*) => { - $(impl Neg for Simd<$scalar, LANES> + ($(impl Neg for Simd<$scalar:ty, N>)*) => { + $(impl Neg for Simd<$scalar, N> where $scalar: SimdElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; @@ -22,27 +22,27 @@ macro_rules! neg { } neg! { - impl Neg for Simd + impl Neg for Simd - impl Neg for Simd + impl Neg for Simd - impl Neg for Simd + impl Neg for Simd - impl Neg for Simd + impl Neg for Simd - impl Neg for Simd + impl Neg for Simd - impl Neg for Simd + impl Neg for Simd - impl Neg for Simd + impl Neg for Simd } macro_rules! not { - ($(impl Not for Simd<$scalar:ty, LANES>)*) => { - $(impl Not for Simd<$scalar, LANES> + ($(impl Not for Simd<$scalar:ty, N>)*) => { + $(impl Not for Simd<$scalar, N> where $scalar: SimdElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Output = Self; @@ -56,23 +56,23 @@ macro_rules! not { } not! { - impl Not for Simd + impl Not for Simd - impl Not for Simd + impl Not for Simd - impl Not for Simd + impl Not for Simd - impl Not for Simd + impl Not for Simd - impl Not for Simd + impl Not for Simd - impl Not for Simd + impl Not for Simd - impl Not for Simd + impl Not for Simd - impl Not for Simd + impl Not for Simd - impl Not for Simd + impl Not for Simd - impl Not for Simd + impl Not for Simd } diff --git a/crates/core_simd/src/select.rs b/crates/core_simd/src/select.rs index 065c5987d3fc..cdcf8eeec815 100644 --- a/crates/core_simd/src/select.rs +++ b/crates/core_simd/src/select.rs @@ -1,15 +1,15 @@ use crate::simd::intrinsics; use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount}; -impl Mask +impl Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - /// Choose lanes from two vectors. + /// Choose elements from two vectors. /// - /// For each lane in the mask, choose the corresponding lane from `true_values` if - /// that lane mask is true, and `false_values` if that lane mask is false. + /// For each element in the mask, choose the corresponding element from `true_values` if + /// that element mask is true, and `false_values` if that element mask is false. /// /// # Examples /// ``` @@ -23,11 +23,7 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn select( - self, - true_values: Simd, - false_values: Simd, - ) -> Simd + pub fn select(self, true_values: Simd, false_values: Simd) -> Simd where U: SimdElement, { @@ -36,10 +32,10 @@ where unsafe { intrinsics::simd_select(self.to_int(), true_values, false_values) } } - /// Choose lanes from two masks. + /// Choose elements from two masks. /// - /// For each lane in the mask, choose the corresponding lane from `true_values` if - /// that lane mask is true, and `false_values` if that lane mask is false. + /// For each element in the mask, choose the corresponding element from `true_values` if + /// that element mask is true, and `false_values` if that element mask is false. /// /// # Examples /// ``` diff --git a/crates/core_simd/src/simd/cmp.rs b/crates/core_simd/src/simd/cmp.rs new file mode 100644 index 000000000000..a8d81dbf20f1 --- /dev/null +++ b/crates/core_simd/src/simd/cmp.rs @@ -0,0 +1,7 @@ +//! Traits for comparing and ordering vectors. + +mod eq; +mod ord; + +pub use eq::*; +pub use ord::*; diff --git a/crates/core_simd/src/eq.rs b/crates/core_simd/src/simd/cmp/eq.rs similarity index 74% rename from crates/core_simd/src/eq.rs rename to crates/core_simd/src/simd/cmp/eq.rs index 80763c072727..f132fa2cc0ca 100644 --- a/crates/core_simd/src/eq.rs +++ b/crates/core_simd/src/simd/cmp/eq.rs @@ -1,5 +1,7 @@ use crate::simd::{ - intrinsics, LaneCount, Mask, Simd, SimdConstPtr, SimdElement, SimdMutPtr, SupportedLaneCount, + intrinsics, + ptr::{SimdConstPtr, SimdMutPtr}, + LaneCount, Mask, Simd, SimdElement, SupportedLaneCount, }; /// Parallel `PartialEq`. @@ -7,11 +9,11 @@ pub trait SimdPartialEq { /// The mask type returned by each comparison. type Mask; - /// Test if each lane is equal to the corresponding lane in `other`. + /// Test if each element is equal to the corresponding element in `other`. #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_eq(self, other: Self) -> Self::Mask; - /// Test if each lane is equal to the corresponding lane in `other`. + /// Test if each element is equal to the corresponding element in `other`. #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_ne(self, other: Self) -> Self::Mask; } @@ -19,11 +21,11 @@ pub trait SimdPartialEq { macro_rules! impl_number { { $($number:ty),* } => { $( - impl SimdPartialEq for Simd<$number, LANES> + impl SimdPartialEq for Simd<$number, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Mask = Mask<<$number as SimdElement>::Mask, LANES>; + type Mask = Mask<<$number as SimdElement>::Mask, N>; #[inline] fn simd_eq(self, other: Self) -> Self::Mask { @@ -48,9 +50,9 @@ impl_number! { f32, f64, u8, u16, u32, u64, usize, i8, i16, i32, i64, isize } macro_rules! impl_mask { { $($integer:ty),* } => { $( - impl SimdPartialEq for Mask<$integer, LANES> + impl SimdPartialEq for Mask<$integer, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Mask = Self; @@ -74,11 +76,11 @@ macro_rules! impl_mask { impl_mask! { i8, i16, i32, i64, isize } -impl SimdPartialEq for Simd<*const T, LANES> +impl SimdPartialEq for Simd<*const T, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Mask = Mask; + type Mask = Mask; #[inline] fn simd_eq(self, other: Self) -> Self::Mask { @@ -91,11 +93,11 @@ where } } -impl SimdPartialEq for Simd<*mut T, LANES> +impl SimdPartialEq for Simd<*mut T, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Mask = Mask; + type Mask = Mask; #[inline] fn simd_eq(self, other: Self) -> Self::Mask { diff --git a/crates/core_simd/src/ord.rs b/crates/core_simd/src/simd/cmp/ord.rs similarity index 79% rename from crates/core_simd/src/ord.rs rename to crates/core_simd/src/simd/cmp/ord.rs index b2455190e823..4e9d49ea2211 100644 --- a/crates/core_simd/src/ord.rs +++ b/crates/core_simd/src/simd/cmp/ord.rs @@ -1,44 +1,47 @@ use crate::simd::{ - intrinsics, LaneCount, Mask, Simd, SimdConstPtr, SimdMutPtr, SimdPartialEq, SupportedLaneCount, + cmp::SimdPartialEq, + intrinsics, + ptr::{SimdConstPtr, SimdMutPtr}, + LaneCount, Mask, Simd, SupportedLaneCount, }; /// Parallel `PartialOrd`. pub trait SimdPartialOrd: SimdPartialEq { - /// Test if each lane is less than the corresponding lane in `other`. + /// Test if each element is less than the corresponding element in `other`. #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_lt(self, other: Self) -> Self::Mask; - /// Test if each lane is less than or equal to the corresponding lane in `other`. + /// Test if each element is less than or equal to the corresponding element in `other`. #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_le(self, other: Self) -> Self::Mask; - /// Test if each lane is greater than the corresponding lane in `other`. + /// Test if each element is greater than the corresponding element in `other`. #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_gt(self, other: Self) -> Self::Mask; - /// Test if each lane is greater than or equal to the corresponding lane in `other`. + /// Test if each element is greater than or equal to the corresponding element in `other`. #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_ge(self, other: Self) -> Self::Mask; } /// Parallel `Ord`. pub trait SimdOrd: SimdPartialOrd { - /// Returns the lane-wise maximum with `other`. + /// Returns the element-wise maximum with `other`. #[must_use = "method returns a new vector and does not mutate the original value"] fn simd_max(self, other: Self) -> Self; - /// Returns the lane-wise minimum with `other`. + /// Returns the element-wise minimum with `other`. #[must_use = "method returns a new vector and does not mutate the original value"] fn simd_min(self, other: Self) -> Self; - /// Restrict each lane to a certain interval. + /// Restrict each element to a certain interval. /// - /// For each lane, returns `max` if `self` is greater than `max`, and `min` if `self` is + /// For each element, 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. + /// Panics if `min > max` on any element. #[must_use = "method returns a new vector and does not mutate the original value"] fn simd_clamp(self, min: Self, max: Self) -> Self; } @@ -46,9 +49,9 @@ pub trait SimdOrd: SimdPartialOrd { macro_rules! impl_integer { { $($integer:ty),* } => { $( - impl SimdPartialOrd for Simd<$integer, LANES> + impl SimdPartialOrd for Simd<$integer, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { @@ -79,9 +82,9 @@ macro_rules! impl_integer { } } - impl SimdOrd for Simd<$integer, LANES> + impl SimdOrd for Simd<$integer, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn simd_max(self, other: Self) -> Self { @@ -98,7 +101,7 @@ macro_rules! impl_integer { fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), - "each lane in `min` must be less than or equal to the corresponding lane in `max`", + "each element in `min` must be less than or equal to the corresponding element in `max`", ); self.simd_max(min).simd_min(max) } @@ -112,9 +115,9 @@ impl_integer! { u8, u16, u32, u64, usize, i8, i16, i32, i64, isize } macro_rules! impl_float { { $($float:ty),* } => { $( - impl SimdPartialOrd for Simd<$float, LANES> + impl SimdPartialOrd for Simd<$float, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { @@ -153,9 +156,9 @@ impl_float! { f32, f64 } macro_rules! impl_mask { { $($integer:ty),* } => { $( - impl SimdPartialOrd for Mask<$integer, LANES> + impl SimdPartialOrd for Mask<$integer, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { @@ -186,9 +189,9 @@ macro_rules! impl_mask { } } - impl SimdOrd for Mask<$integer, LANES> + impl SimdOrd for Mask<$integer, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn simd_max(self, other: Self) -> Self { @@ -205,7 +208,7 @@ macro_rules! impl_mask { fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), - "each lane in `min` must be less than or equal to the corresponding lane in `max`", + "each element in `min` must be less than or equal to the corresponding element in `max`", ); self.simd_max(min).simd_min(max) } @@ -216,9 +219,9 @@ macro_rules! impl_mask { impl_mask! { i8, i16, i32, i64, isize } -impl SimdPartialOrd for Simd<*const T, LANES> +impl SimdPartialOrd for Simd<*const T, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { @@ -241,9 +244,9 @@ where } } -impl SimdOrd for Simd<*const T, LANES> +impl SimdOrd for Simd<*const T, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn simd_max(self, other: Self) -> Self { @@ -260,15 +263,15 @@ where fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), - "each lane in `min` must be less than or equal to the corresponding lane in `max`", + "each element in `min` must be less than or equal to the corresponding element in `max`", ); self.simd_max(min).simd_min(max) } } -impl SimdPartialOrd for Simd<*mut T, LANES> +impl SimdPartialOrd for Simd<*mut T, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { @@ -291,9 +294,9 @@ where } } -impl SimdOrd for Simd<*mut T, LANES> +impl SimdOrd for Simd<*mut T, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { #[inline] fn simd_max(self, other: Self) -> Self { @@ -310,7 +313,7 @@ where fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), - "each lane in `min` must be less than or equal to the corresponding lane in `max`", + "each element in `min` must be less than or equal to the corresponding element in `max`", ); self.simd_max(min).simd_min(max) } diff --git a/crates/core_simd/src/elements.rs b/crates/core_simd/src/simd/num.rs similarity index 63% rename from crates/core_simd/src/elements.rs rename to crates/core_simd/src/simd/num.rs index dc7f52a4d576..22a4802ec6cb 100644 --- a/crates/core_simd/src/elements.rs +++ b/crates/core_simd/src/simd/num.rs @@ -1,15 +1,13 @@ -mod const_ptr; +//! Traits for vectors with numeric elements. + mod float; mod int; -mod mut_ptr; mod uint; mod sealed { pub trait Sealed {} } -pub use const_ptr::*; pub use float::*; pub use int::*; -pub use mut_ptr::*; pub use uint::*; diff --git a/crates/core_simd/src/elements/float.rs b/crates/core_simd/src/simd/num/float.rs similarity index 80% rename from crates/core_simd/src/elements/float.rs rename to crates/core_simd/src/simd/num/float.rs index 501c1c5ddd3f..fc0b99e87a68 100644 --- a/crates/core_simd/src/elements/float.rs +++ b/crates/core_simd/src/simd/num/float.rs @@ -1,7 +1,7 @@ use super::sealed::Sealed; use crate::simd::{ - intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SimdPartialEq, SimdPartialOrd, - SupportedLaneCount, + cmp::{SimdPartialEq, SimdPartialOrd}, + intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, }; /// Operations on SIMD vectors of floats. @@ -28,7 +28,7 @@ pub trait SimdFloat: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{SimdFloat, SimdInt, Simd}; + /// # use simd::prelude::*; /// 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])); @@ -63,64 +63,64 @@ pub trait SimdFloat: Copy + Sealed { Self::Scalar: core::convert::FloatToInt; /// Raw transmutation to an unsigned integer vector type with the - /// same size and number of lanes. + /// same size and number of elements. #[must_use = "method returns a new vector and does not mutate the original value"] fn to_bits(self) -> Self::Bits; /// Raw transmutation from an unsigned integer vector type with the - /// same size and number of lanes. + /// same size and number of elements. #[must_use = "method returns a new vector and does not mutate the original value"] fn from_bits(bits: Self::Bits) -> Self; - /// Produces a vector where every lane has the absolute value of the - /// equivalently-indexed lane in `self`. + /// Produces a vector where every element has the absolute value of the + /// equivalently-indexed element in `self`. #[must_use = "method returns a new vector and does not mutate the original value"] fn abs(self) -> Self; - /// Takes the reciprocal (inverse) of each lane, `1/x`. + /// Takes the reciprocal (inverse) of each element, `1/x`. #[must_use = "method returns a new vector and does not mutate the original value"] fn recip(self) -> Self; - /// Converts each lane from radians to degrees. + /// Converts each element from radians to degrees. #[must_use = "method returns a new vector and does not mutate the original value"] fn to_degrees(self) -> Self; - /// Converts each lane from degrees to radians. + /// Converts each element from degrees to radians. #[must_use = "method returns a new vector and does not mutate the original value"] fn to_radians(self) -> Self; - /// Returns true for each lane if it has a positive sign, including + /// Returns true for each element if it has a positive sign, including /// `+0.0`, `NaN`s with positive sign bit and positive infinity. #[must_use = "method returns a new mask and does not mutate the original value"] fn is_sign_positive(self) -> Self::Mask; - /// Returns true for each lane if it has a negative sign, including + /// Returns true for each element if it has a negative sign, including /// `-0.0`, `NaN`s with negative sign bit and negative infinity. #[must_use = "method returns a new mask and does not mutate the original value"] fn is_sign_negative(self) -> Self::Mask; - /// Returns true for each lane if its value is `NaN`. + /// Returns true for each element if its value is `NaN`. #[must_use = "method returns a new mask and does not mutate the original value"] fn is_nan(self) -> Self::Mask; - /// Returns true for each lane if its value is positive infinity or negative infinity. + /// Returns true for each element if its value is positive infinity or negative infinity. #[must_use = "method returns a new mask and does not mutate the original value"] fn is_infinite(self) -> Self::Mask; - /// Returns true for each lane if its value is neither infinite nor `NaN`. + /// Returns true for each element if its value is neither infinite nor `NaN`. #[must_use = "method returns a new mask and does not mutate the original value"] fn is_finite(self) -> Self::Mask; - /// Returns true for each lane if its value is subnormal. + /// Returns true for each element if its value is subnormal. #[must_use = "method returns a new mask and does not mutate the original value"] fn is_subnormal(self) -> Self::Mask; - /// Returns true for each lane if its value is neither zero, infinite, + /// Returns true for each element if its value is neither zero, infinite, /// subnormal, nor `NaN`. #[must_use = "method returns a new mask and does not mutate the original value"] fn is_normal(self) -> Self::Mask; - /// Replaces each lane with a number that represents its sign. + /// Replaces each element with a number that represents its sign. /// /// * `1.0` if the number is positive, `+0.0`, or `INFINITY` /// * `-1.0` if the number is negative, `-0.0`, or `NEG_INFINITY` @@ -128,33 +128,33 @@ pub trait SimdFloat: Copy + Sealed { #[must_use = "method returns a new vector and does not mutate the original value"] fn signum(self) -> Self; - /// Returns each lane with the magnitude of `self` and the sign of `sign`. + /// Returns each element with the magnitude of `self` and the sign of `sign`. /// - /// For any lane containing a `NAN`, a `NAN` with the sign of `sign` is returned. + /// For any element containing a `NAN`, a `NAN` with the sign of `sign` is returned. #[must_use = "method returns a new vector and does not mutate the original value"] fn copysign(self, sign: Self) -> Self; - /// Returns the minimum of each lane. + /// Returns the minimum of each element. /// /// If one of the values is `NAN`, then the other value is returned. #[must_use = "method returns a new vector and does not mutate the original value"] fn simd_min(self, other: Self) -> Self; - /// Returns the maximum of each lane. + /// Returns the maximum of each element. /// /// If one of the values is `NAN`, then the other value is returned. #[must_use = "method returns a new vector and does not mutate the original value"] fn simd_max(self, other: Self) -> Self; - /// Restrict each lane to a certain interval unless it is NaN. + /// Restrict each element to a certain interval unless it is NaN. /// - /// For each lane in `self`, returns the corresponding lane in `max` if the lane is - /// greater than `max`, and the corresponding lane in `min` if the lane is less - /// than `min`. Otherwise returns the lane in `self`. + /// For each element in `self`, returns the corresponding element in `max` if the element is + /// greater than `max`, and the corresponding element in `min` if the element is less + /// than `min`. Otherwise returns the element in `self`. #[must_use = "method returns a new vector and does not mutate the original value"] fn simd_clamp(self, min: Self, max: Self) -> Self; - /// Returns the sum of the lanes of the vector. + /// Returns the sum of the elements of the vector. /// /// # Examples /// @@ -162,13 +162,13 @@ pub trait SimdFloat: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{f32x2, SimdFloat}; + /// # use simd::prelude::*; /// let v = f32x2::from_array([1., 2.]); /// assert_eq!(v.reduce_sum(), 3.); /// ``` fn reduce_sum(self) -> Self::Scalar; - /// Reducing multiply. Returns the product of the lanes of the vector. + /// Reducing multiply. Returns the product of the elements of the vector. /// /// # Examples /// @@ -176,18 +176,18 @@ pub trait SimdFloat: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{f32x2, SimdFloat}; + /// # use simd::prelude::*; /// let v = f32x2::from_array([3., 4.]); /// assert_eq!(v.reduce_product(), 12.); /// ``` fn reduce_product(self) -> Self::Scalar; - /// Returns the maximum lane in the vector. + /// Returns the maximum element 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`. + /// This function will not return `NaN` unless all elements are `NaN`. /// /// # Examples /// @@ -195,7 +195,7 @@ pub trait SimdFloat: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{f32x2, SimdFloat}; + /// # use simd::prelude::*; /// let v = f32x2::from_array([1., 2.]); /// assert_eq!(v.reduce_max(), 2.); /// @@ -209,12 +209,12 @@ pub trait SimdFloat: Copy + Sealed { /// ``` fn reduce_max(self) -> Self::Scalar; - /// Returns the minimum lane in the vector. + /// Returns the minimum element 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`. + /// This function will not return `NaN` unless all elements are `NaN`. /// /// # Examples /// @@ -222,7 +222,7 @@ pub trait SimdFloat: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{f32x2, SimdFloat}; + /// # use simd::prelude::*; /// let v = f32x2::from_array([3., 7.]); /// assert_eq!(v.reduce_min(), 3.); /// @@ -240,20 +240,20 @@ pub trait SimdFloat: Copy + Sealed { macro_rules! impl_trait { { $($ty:ty { bits: $bits_ty:ty, mask: $mask_ty:ty }),* } => { $( - impl Sealed for Simd<$ty, LANES> + impl Sealed for Simd<$ty, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { } - impl SimdFloat for Simd<$ty, LANES> + impl SimdFloat for Simd<$ty, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Mask = Mask<<$mask_ty as SimdElement>::Mask, LANES>; + type Mask = Mask<<$mask_ty as SimdElement>::Mask, N>; type Scalar = $ty; - type Bits = Simd<$bits_ty, LANES>; - type Cast = Simd; + type Bits = Simd<$bits_ty, N>; + type Cast = Simd; #[inline] fn cast(self) -> Self::Cast @@ -273,14 +273,14 @@ macro_rules! impl_trait { } #[inline] - fn to_bits(self) -> Simd<$bits_ty, LANES> { + fn to_bits(self) -> Simd<$bits_ty, N> { assert_eq!(core::mem::size_of::(), core::mem::size_of::()); // Safety: transmuting between vector types is safe unsafe { core::mem::transmute_copy(&self) } } #[inline] - fn from_bits(bits: Simd<$bits_ty, LANES>) -> Self { + fn from_bits(bits: Simd<$bits_ty, N>) -> Self { assert_eq!(core::mem::size_of::(), core::mem::size_of::()); // Safety: transmuting between vector types is safe unsafe { core::mem::transmute_copy(&bits) } @@ -336,7 +336,10 @@ macro_rules! impl_trait { #[inline] fn is_subnormal(self) -> Self::Mask { - self.abs().simd_ne(Self::splat(0.0)) & (self.to_bits() & Self::splat(Self::Scalar::INFINITY).to_bits()).simd_eq(Simd::splat(0)) + // On some architectures (e.g. armv7 and some ppc) subnormals are flushed to zero, + // so this comparison must be done with integers. + let not_zero = self.abs().to_bits().simd_ne(Self::splat(0.0).to_bits()); + not_zero & (self.to_bits() & Self::splat(Self::Scalar::INFINITY).to_bits()).simd_eq(Simd::splat(0)) } #[inline] @@ -373,7 +376,7 @@ macro_rules! impl_trait { fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), - "each lane in `min` must be less than or equal to the corresponding lane in `max`", + "each element in `min` must be less than or equal to the corresponding element in `max`", ); let mut x = self; x = x.simd_lt(min).select(min, x); diff --git a/crates/core_simd/src/elements/int.rs b/crates/core_simd/src/simd/num/int.rs similarity index 71% rename from crates/core_simd/src/elements/int.rs rename to crates/core_simd/src/simd/num/int.rs index 6db89ff9a659..1f1aa2727829 100644 --- a/crates/core_simd/src/elements/int.rs +++ b/crates/core_simd/src/simd/num/int.rs @@ -1,6 +1,7 @@ use super::sealed::Sealed; use crate::simd::{ - intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SimdPartialOrd, SupportedLaneCount, + cmp::SimdPartialOrd, intrinsics, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, + SupportedLaneCount, }; /// Operations on SIMD vectors of signed integers. @@ -11,6 +12,9 @@ pub trait SimdInt: Copy + Sealed { /// Scalar type contained by this SIMD vector type. type Scalar; + /// A SIMD vector of unsigned integers with the same element size. + type Unsigned; + /// A SIMD vector with a different element type. type Cast; @@ -28,7 +32,7 @@ pub trait SimdInt: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Simd, SimdInt}; + /// # use simd::prelude::*; /// use core::i32::{MIN, MAX}; /// let x = Simd::from_array([MIN, 0, 1, MAX]); /// let max = Simd::splat(MAX); @@ -46,7 +50,7 @@ pub trait SimdInt: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Simd, SimdInt}; + /// # use simd::prelude::*; /// use core::i32::{MIN, MAX}; /// let x = Simd::from_array([MIN, -2, -1, MAX]); /// let max = Simd::splat(MAX); @@ -57,14 +61,14 @@ pub trait SimdInt: Copy + Sealed { fn saturating_sub(self, second: Self) -> Self; /// Lanewise absolute value, implemented in Rust. - /// Every lane becomes its absolute value. + /// Every element becomes its absolute value. /// /// # Examples /// ``` /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Simd, SimdInt}; + /// # use simd::prelude::*; /// use core::i32::{MIN, MAX}; /// let xs = Simd::from_array([MIN, MIN +1, -5, 0]); /// assert_eq!(xs.abs(), Simd::from_array([MIN, MAX, 5, 0])); @@ -79,7 +83,7 @@ pub trait SimdInt: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Simd, SimdInt}; + /// # use simd::prelude::*; /// use core::i32::{MIN, MAX}; /// let xs = Simd::from_array([MIN, -2, 0, 3]); /// let unsat = xs.abs(); @@ -97,7 +101,7 @@ pub trait SimdInt: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Simd, SimdInt}; + /// # use simd::prelude::*; /// use core::i32::{MIN, MAX}; /// let x = Simd::from_array([MIN, -2, 3, MAX]); /// let unsat = -x; @@ -107,19 +111,19 @@ pub trait SimdInt: Copy + Sealed { /// ``` fn saturating_neg(self) -> Self; - /// Returns true for each positive lane and false if it is zero or negative. + /// Returns true for each positive element and false if it is zero or negative. fn is_positive(self) -> Self::Mask; - /// Returns true for each negative lane and false if it is zero or positive. + /// Returns true for each negative element and false if it is zero or positive. fn is_negative(self) -> Self::Mask; - /// Returns numbers representing the sign of each lane. + /// Returns numbers representing the sign of each element. /// * `0` if the number is zero /// * `1` if the number is positive /// * `-1` if the number is negative fn signum(self) -> Self; - /// Returns the sum of the lanes of the vector, with wrapping addition. + /// Returns the sum of the elements of the vector, with wrapping addition. /// /// # Examples /// @@ -127,7 +131,7 @@ pub trait SimdInt: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{i32x4, SimdInt}; + /// # use simd::prelude::*; /// let v = i32x4::from_array([1, 2, 3, 4]); /// assert_eq!(v.reduce_sum(), 10); /// @@ -137,7 +141,7 @@ pub trait SimdInt: Copy + Sealed { /// ``` fn reduce_sum(self) -> Self::Scalar; - /// Returns the product of the lanes of the vector, with wrapping multiplication. + /// Returns the product of the elements of the vector, with wrapping multiplication. /// /// # Examples /// @@ -145,7 +149,7 @@ pub trait SimdInt: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{i32x4, SimdInt}; + /// # use simd::prelude::*; /// let v = i32x4::from_array([1, 2, 3, 4]); /// assert_eq!(v.reduce_product(), 24); /// @@ -155,7 +159,7 @@ pub trait SimdInt: Copy + Sealed { /// ``` fn reduce_product(self) -> Self::Scalar; - /// Returns the maximum lane in the vector. + /// Returns the maximum element in the vector. /// /// # Examples /// @@ -163,13 +167,13 @@ pub trait SimdInt: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{i32x4, SimdInt}; + /// # use simd::prelude::*; /// let v = i32x4::from_array([1, 2, 3, 4]); /// assert_eq!(v.reduce_max(), 4); /// ``` fn reduce_max(self) -> Self::Scalar; - /// Returns the minimum lane in the vector. + /// Returns the minimum element in the vector. /// /// # Examples /// @@ -177,38 +181,58 @@ pub trait SimdInt: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{i32x4, SimdInt}; + /// # use simd::prelude::*; /// let v = i32x4::from_array([1, 2, 3, 4]); /// assert_eq!(v.reduce_min(), 1); /// ``` fn reduce_min(self) -> Self::Scalar; - /// Returns the cumulative bitwise "and" across the lanes of the vector. + /// Returns the cumulative bitwise "and" across the elements of the vector. fn reduce_and(self) -> Self::Scalar; - /// Returns the cumulative bitwise "or" across the lanes of the vector. + /// Returns the cumulative bitwise "or" across the elements of the vector. fn reduce_or(self) -> Self::Scalar; - /// Returns the cumulative bitwise "xor" across the lanes of the vector. + /// Returns the cumulative bitwise "xor" across the elements of the vector. fn reduce_xor(self) -> Self::Scalar; + + /// Reverses the byte order of each element. + fn swap_bytes(self) -> Self; + + /// Reverses the order of bits in each elemnent. + /// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc. + fn reverse_bits(self) -> Self; + + /// Returns the number of leading zeros in the binary representation of each element. + fn leading_zeros(self) -> Self::Unsigned; + + /// Returns the number of trailing zeros in the binary representation of each element. + fn trailing_zeros(self) -> Self::Unsigned; + + /// Returns the number of leading ones in the binary representation of each element. + fn leading_ones(self) -> Self::Unsigned; + + /// Returns the number of trailing ones in the binary representation of each element. + fn trailing_ones(self) -> Self::Unsigned; } macro_rules! impl_trait { - { $($ty:ty),* } => { + { $($ty:ident ($unsigned:ident)),* } => { $( - impl Sealed for Simd<$ty, LANES> + impl Sealed for Simd<$ty, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { } - impl SimdInt for Simd<$ty, LANES> + impl SimdInt for Simd<$ty, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Mask = Mask<<$ty as SimdElement>::Mask, LANES>; + type Mask = Mask<<$ty as SimdElement>::Mask, N>; type Scalar = $ty; - type Cast = Simd; + type Unsigned = Simd<$unsigned, N>; + type Cast = Simd; #[inline] fn cast(self) -> Self::Cast { @@ -307,9 +331,41 @@ macro_rules! impl_trait { // Safety: `self` is an integer vector unsafe { intrinsics::simd_reduce_xor(self) } } + + #[inline] + fn swap_bytes(self) -> Self { + // Safety: `self` is an integer vector + unsafe { intrinsics::simd_bswap(self) } + } + + #[inline] + fn reverse_bits(self) -> Self { + // Safety: `self` is an integer vector + unsafe { intrinsics::simd_bitreverse(self) } + } + + #[inline] + fn leading_zeros(self) -> Self::Unsigned { + self.cast::<$unsigned>().leading_zeros() + } + + #[inline] + fn trailing_zeros(self) -> Self::Unsigned { + self.cast::<$unsigned>().trailing_zeros() + } + + #[inline] + fn leading_ones(self) -> Self::Unsigned { + self.cast::<$unsigned>().leading_ones() + } + + #[inline] + fn trailing_ones(self) -> Self::Unsigned { + self.cast::<$unsigned>().trailing_ones() + } } )* } } -impl_trait! { i8, i16, i32, i64, isize } +impl_trait! { i8 (u8), i16 (u16), i32 (u32), i64 (u64), isize (usize) } diff --git a/crates/core_simd/src/elements/uint.rs b/crates/core_simd/src/simd/num/uint.rs similarity index 58% rename from crates/core_simd/src/elements/uint.rs rename to crates/core_simd/src/simd/num/uint.rs index 3926c395ec9a..c955ee8fe8bd 100644 --- a/crates/core_simd/src/elements/uint.rs +++ b/crates/core_simd/src/simd/num/uint.rs @@ -16,6 +16,12 @@ pub trait SimdUint: Copy + Sealed { #[must_use] fn cast(self) -> Self::Cast; + /// Wrapping negation. + /// + /// Like [`u32::wrapping_neg`], all applications of this function will wrap, with the exception + /// of `-0`. + fn wrapping_neg(self) -> Self; + /// Lanewise saturating add. /// /// # Examples @@ -23,7 +29,7 @@ pub trait SimdUint: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Simd, SimdUint}; + /// # use simd::prelude::*; /// use core::u32::MAX; /// let x = Simd::from_array([2, 1, 0, MAX]); /// let max = Simd::splat(MAX); @@ -41,7 +47,7 @@ pub trait SimdUint: Copy + Sealed { /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Simd, SimdUint}; + /// # use simd::prelude::*; /// use core::u32::MAX; /// let x = Simd::from_array([2, 1, 0, MAX]); /// let max = Simd::splat(MAX); @@ -51,43 +57,62 @@ pub trait SimdUint: Copy + Sealed { /// assert_eq!(sat, Simd::splat(0)); fn saturating_sub(self, second: Self) -> Self; - /// Returns the sum of the lanes of the vector, with wrapping addition. + /// Returns the sum of the elements of the vector, with wrapping addition. fn reduce_sum(self) -> Self::Scalar; - /// Returns the product of the lanes of the vector, with wrapping multiplication. + /// Returns the product of the elements of the vector, with wrapping multiplication. fn reduce_product(self) -> Self::Scalar; - /// Returns the maximum lane in the vector. + /// Returns the maximum element in the vector. fn reduce_max(self) -> Self::Scalar; - /// Returns the minimum lane in the vector. + /// Returns the minimum element in the vector. fn reduce_min(self) -> Self::Scalar; - /// Returns the cumulative bitwise "and" across the lanes of the vector. + /// Returns the cumulative bitwise "and" across the elements of the vector. fn reduce_and(self) -> Self::Scalar; - /// Returns the cumulative bitwise "or" across the lanes of the vector. + /// Returns the cumulative bitwise "or" across the elements of the vector. fn reduce_or(self) -> Self::Scalar; - /// Returns the cumulative bitwise "xor" across the lanes of the vector. + /// Returns the cumulative bitwise "xor" across the elements of the vector. fn reduce_xor(self) -> Self::Scalar; + + /// Reverses the byte order of each element. + fn swap_bytes(self) -> Self; + + /// Reverses the order of bits in each elemnent. + /// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc. + fn reverse_bits(self) -> Self; + + /// Returns the number of leading zeros in the binary representation of each element. + fn leading_zeros(self) -> Self; + + /// Returns the number of trailing zeros in the binary representation of each element. + fn trailing_zeros(self) -> Self; + + /// Returns the number of leading ones in the binary representation of each element. + fn leading_ones(self) -> Self; + + /// Returns the number of trailing ones in the binary representation of each element. + fn trailing_ones(self) -> Self; } macro_rules! impl_trait { - { $($ty:ty),* } => { + { $($ty:ident ($signed:ident)),* } => { $( - impl Sealed for Simd<$ty, LANES> + impl Sealed for Simd<$ty, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { } - impl SimdUint for Simd<$ty, LANES> + impl SimdUint for Simd<$ty, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { type Scalar = $ty; - type Cast = Simd; + type Cast = Simd; #[inline] fn cast(self) -> Self::Cast { @@ -95,6 +120,12 @@ macro_rules! impl_trait { unsafe { intrinsics::simd_as(self) } } + #[inline] + fn wrapping_neg(self) -> Self { + use crate::simd::num::SimdInt; + (-self.cast::<$signed>()).cast() + } + #[inline] fn saturating_add(self, second: Self) -> Self { // Safety: `self` is a vector @@ -148,9 +179,43 @@ macro_rules! impl_trait { // Safety: `self` is an integer vector unsafe { intrinsics::simd_reduce_xor(self) } } + + #[inline] + fn swap_bytes(self) -> Self { + // Safety: `self` is an integer vector + unsafe { intrinsics::simd_bswap(self) } + } + + #[inline] + fn reverse_bits(self) -> Self { + // Safety: `self` is an integer vector + unsafe { intrinsics::simd_bitreverse(self) } + } + + #[inline] + fn leading_zeros(self) -> Self { + // Safety: `self` is an integer vector + unsafe { intrinsics::simd_ctlz(self) } + } + + #[inline] + fn trailing_zeros(self) -> Self { + // Safety: `self` is an integer vector + unsafe { intrinsics::simd_cttz(self) } + } + + #[inline] + fn leading_ones(self) -> Self { + (!self).leading_zeros() + } + + #[inline] + fn trailing_ones(self) -> Self { + (!self).trailing_zeros() + } } )* } } -impl_trait! { u8, u16, u32, u64, usize } +impl_trait! { u8 (i8), u16 (i16), u32 (i32), u64 (i64), usize (isize) } diff --git a/crates/core_simd/src/simd/prelude.rs b/crates/core_simd/src/simd/prelude.rs index e8fdc932d490..4b7c744c0132 100644 --- a/crates/core_simd/src/simd/prelude.rs +++ b/crates/core_simd/src/simd/prelude.rs @@ -7,8 +7,10 @@ #[doc(no_inline)] pub use super::{ - simd_swizzle, Mask, Simd, SimdConstPtr, SimdFloat, SimdInt, SimdMutPtr, SimdOrd, SimdPartialEq, - SimdPartialOrd, SimdUint, + cmp::{SimdOrd, SimdPartialEq, SimdPartialOrd}, + num::{SimdFloat, SimdInt, SimdUint}, + ptr::{SimdConstPtr, SimdMutPtr}, + simd_swizzle, Mask, Simd, }; #[rustfmt::skip] diff --git a/crates/core_simd/src/simd/ptr.rs b/crates/core_simd/src/simd/ptr.rs new file mode 100644 index 000000000000..3f8e66691185 --- /dev/null +++ b/crates/core_simd/src/simd/ptr.rs @@ -0,0 +1,11 @@ +//! Traits for vectors of pointers. + +mod const_ptr; +mod mut_ptr; + +mod sealed { + pub trait Sealed {} +} + +pub use const_ptr::*; +pub use mut_ptr::*; diff --git a/crates/core_simd/src/elements/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs similarity index 81% rename from crates/core_simd/src/elements/const_ptr.rs rename to crates/core_simd/src/simd/ptr/const_ptr.rs index f215f9a61d02..97fe3fb600df 100644 --- a/crates/core_simd/src/elements/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -1,15 +1,17 @@ use super::sealed::Sealed; -use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SimdUint, SupportedLaneCount}; +use crate::simd::{ + cmp::SimdPartialEq, intrinsics, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount, +}; /// Operations on SIMD vectors of constant pointers. pub trait SimdConstPtr: Copy + Sealed { - /// Vector of `usize` with the same number of lanes. + /// Vector of `usize` with the same number of elements. type Usize; - /// Vector of `isize` with the same number of lanes. + /// Vector of `isize` with the same number of elements. type Isize; - /// Vector of const pointers with the same number of lanes. + /// Vector of const pointers with the same number of elements. type CastPtr; /// Vector of mutable pointers to the same type. @@ -18,17 +20,17 @@ pub trait SimdConstPtr: Copy + Sealed { /// Mask type used for manipulating this SIMD vector type. type Mask; - /// Returns `true` for each lane that is null. + /// Returns `true` for each element that is null. fn is_null(self) -> Self::Mask; /// Casts to a pointer of another type. /// - /// Equivalent to calling [`pointer::cast`] on each lane. + /// Equivalent to calling [`pointer::cast`] on each element. fn cast(self) -> Self::CastPtr; /// Changes constness without changing the type. /// - /// Equivalent to calling [`pointer::cast_mut`] on each lane. + /// Equivalent to calling [`pointer::cast_mut`] on each element. fn cast_mut(self) -> Self::MutPtr; /// Gets the "address" portion of the pointer. @@ -39,7 +41,7 @@ pub trait SimdConstPtr: Copy + Sealed { /// This method semantically discards *provenance* and /// *address-space* information. To properly restore that information, use [`Self::with_addr`]. /// - /// Equivalent to calling [`pointer::addr`] on each lane. + /// Equivalent to calling [`pointer::addr`] on each element. fn addr(self) -> Self::Usize; /// Creates a new pointer with the given address. @@ -47,7 +49,7 @@ pub trait SimdConstPtr: Copy + Sealed { /// This performs the same operation as a cast, but copies the *address-space* and /// *provenance* of `self` to the new pointer. /// - /// Equivalent to calling [`pointer::with_addr`] on each lane. + /// Equivalent to calling [`pointer::with_addr`] on each element. fn with_addr(self, addr: Self::Usize) -> Self; /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use @@ -56,39 +58,36 @@ pub trait SimdConstPtr: Copy + Sealed { /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// - /// Equivalent to calling [`core::ptr::from_exposed_addr`] on each lane. + /// Equivalent to calling [`core::ptr::from_exposed_addr`] on each element. fn from_exposed_addr(addr: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// - /// Equivalent to calling [`pointer::wrapping_offset`] on each lane. + /// Equivalent to calling [`pointer::wrapping_offset`] on each element. fn wrapping_offset(self, offset: Self::Isize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// - /// Equivalent to calling [`pointer::wrapping_add`] on each lane. + /// Equivalent to calling [`pointer::wrapping_add`] on each element. fn wrapping_add(self, count: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// - /// Equivalent to calling [`pointer::wrapping_sub`] on each lane. + /// Equivalent to calling [`pointer::wrapping_sub`] on each element. fn wrapping_sub(self, count: Self::Usize) -> Self; } -impl Sealed for Simd<*const T, LANES> where - LaneCount: SupportedLaneCount -{ -} +impl Sealed for Simd<*const T, N> where LaneCount: SupportedLaneCount {} -impl SimdConstPtr for Simd<*const T, LANES> +impl SimdConstPtr for Simd<*const T, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Usize = Simd; - type Isize = Simd; - type CastPtr = Simd<*const U, LANES>; - type MutPtr = Simd<*mut T, LANES>; - type Mask = Mask; + type Usize = Simd; + type Isize = Simd; + type CastPtr = Simd<*const U, N>; + type MutPtr = Simd<*mut T, N>; + type Mask = Mask; #[inline] fn is_null(self) -> Self::Mask { diff --git a/crates/core_simd/src/elements/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs similarity index 81% rename from crates/core_simd/src/elements/mut_ptr.rs rename to crates/core_simd/src/simd/ptr/mut_ptr.rs index 4bdc6a14ce4a..e35633d0433d 100644 --- a/crates/core_simd/src/elements/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -1,15 +1,17 @@ use super::sealed::Sealed; -use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SimdUint, SupportedLaneCount}; +use crate::simd::{ + cmp::SimdPartialEq, intrinsics, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount, +}; /// Operations on SIMD vectors of mutable pointers. pub trait SimdMutPtr: Copy + Sealed { - /// Vector of `usize` with the same number of lanes. + /// Vector of `usize` with the same number of elements. type Usize; - /// Vector of `isize` with the same number of lanes. + /// Vector of `isize` with the same number of elements. type Isize; - /// Vector of const pointers with the same number of lanes. + /// Vector of const pointers with the same number of elements. type CastPtr; /// Vector of constant pointers to the same type. @@ -18,17 +20,17 @@ pub trait SimdMutPtr: Copy + Sealed { /// Mask type used for manipulating this SIMD vector type. type Mask; - /// Returns `true` for each lane that is null. + /// Returns `true` for each element that is null. fn is_null(self) -> Self::Mask; /// Casts to a pointer of another type. /// - /// Equivalent to calling [`pointer::cast`] on each lane. + /// Equivalent to calling [`pointer::cast`] on each element. fn cast(self) -> Self::CastPtr; /// Changes constness without changing the type. /// - /// Equivalent to calling [`pointer::cast_const`] on each lane. + /// Equivalent to calling [`pointer::cast_const`] on each element. fn cast_const(self) -> Self::ConstPtr; /// Gets the "address" portion of the pointer. @@ -36,7 +38,7 @@ pub trait SimdMutPtr: Copy + Sealed { /// This method discards pointer semantic metadata, so the result cannot be /// directly cast into a valid pointer. /// - /// Equivalent to calling [`pointer::addr`] on each lane. + /// Equivalent to calling [`pointer::addr`] on each element. fn addr(self) -> Self::Usize; /// Creates a new pointer with the given address. @@ -44,7 +46,7 @@ pub trait SimdMutPtr: Copy + Sealed { /// This performs the same operation as a cast, but copies the *address-space* and /// *provenance* of `self` to the new pointer. /// - /// Equivalent to calling [`pointer::with_addr`] on each lane. + /// Equivalent to calling [`pointer::with_addr`] on each element. fn with_addr(self, addr: Self::Usize) -> Self; /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use @@ -53,37 +55,36 @@ pub trait SimdMutPtr: Copy + Sealed { /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// - /// Equivalent to calling [`core::ptr::from_exposed_addr_mut`] on each lane. + /// Equivalent to calling [`core::ptr::from_exposed_addr_mut`] on each element. fn from_exposed_addr(addr: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// - /// Equivalent to calling [`pointer::wrapping_offset`] on each lane. + /// Equivalent to calling [`pointer::wrapping_offset`] on each element. fn wrapping_offset(self, offset: Self::Isize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// - /// Equivalent to calling [`pointer::wrapping_add`] on each lane. + /// Equivalent to calling [`pointer::wrapping_add`] on each element. fn wrapping_add(self, count: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// - /// Equivalent to calling [`pointer::wrapping_sub`] on each lane. + /// Equivalent to calling [`pointer::wrapping_sub`] on each element. fn wrapping_sub(self, count: Self::Usize) -> Self; } -impl Sealed for Simd<*mut T, LANES> where LaneCount: SupportedLaneCount -{} +impl Sealed for Simd<*mut T, N> where LaneCount: SupportedLaneCount {} -impl SimdMutPtr for Simd<*mut T, LANES> +impl SimdMutPtr for Simd<*mut T, N> where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - type Usize = Simd; - type Isize = Simd; - type CastPtr = Simd<*mut U, LANES>; - type ConstPtr = Simd<*const T, LANES>; - type Mask = Mask; + type Usize = Simd; + type Isize = Simd; + type CastPtr = Simd<*mut U, N>; + type ConstPtr = Simd<*const T, N>; + type Mask = Mask; #[inline] fn is_null(self) -> Self::Mask { diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 68f20516cf5b..ec8548d55745 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -1,17 +1,15 @@ use crate::simd::intrinsics; -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount}; -/// Constructs a new SIMD vector by copying elements from selected lanes in other vectors. +/// Constructs a new SIMD vector by copying elements from selected elements in other vectors. /// -/// When swizzling one vector, lanes are selected by a `const` array of `usize`, -/// like [`Swizzle`]. +/// When swizzling one vector, elements are selected like [`Swizzle::swizzle`]. /// -/// When swizzling two vectors, lanes are selected by a `const` array of [`Which`], -/// like [`Swizzle2`]. +/// When swizzling two vectors, elements are selected like [`Swizzle::concat_swizzle`]. /// /// # Examples /// -/// With a single SIMD vector, the const array specifies lane indices in that vector: +/// With a single SIMD vector, the const array specifies element indices in that vector: /// ``` /// # #![feature(portable_simd)] /// # use core::simd::{u32x2, u32x4, simd_swizzle}; @@ -21,25 +19,27 @@ use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; /// let r: u32x4 = simd_swizzle!(v, [3, 0, 1, 2]); /// assert_eq!(r.to_array(), [13, 10, 11, 12]); /// -/// // Changing the number of lanes +/// // Changing the number of elements /// let r: u32x2 = simd_swizzle!(v, [3, 1]); /// assert_eq!(r.to_array(), [13, 11]); /// ``` /// -/// With two input SIMD vectors, the const array uses `Which` to specify the source of each index: +/// With two input SIMD vectors, the const array specifies element indices in the concatenation of +/// those vectors: /// ``` /// # #![feature(portable_simd)] -/// # use core::simd::{u32x2, u32x4, simd_swizzle, Which}; -/// use Which::{First, Second}; +/// # #[cfg(feature = "as_crate")] use core_simd::simd; +/// # #[cfg(not(feature = "as_crate"))] use core::simd; +/// # use simd::{u32x2, u32x4, simd_swizzle}; /// let a = u32x4::from_array([0, 1, 2, 3]); /// let b = u32x4::from_array([4, 5, 6, 7]); /// /// // Keeping the same size -/// let r: u32x4 = simd_swizzle!(a, b, [First(0), First(1), Second(2), Second(3)]); +/// let r: u32x4 = simd_swizzle!(a, b, [0, 1, 6, 7]); /// assert_eq!(r.to_array(), [0, 1, 6, 7]); /// -/// // Changing the number of lanes -/// let r: u32x2 = simd_swizzle!(a, b, [First(0), Second(0)]); +/// // Changing the number of elements +/// let r: u32x2 = simd_swizzle!(a, b, [0, 4]); /// assert_eq!(r.to_array(), [0, 4]); /// ``` #[allow(unused_macros)] @@ -50,7 +50,7 @@ pub macro simd_swizzle { { use $crate::simd::Swizzle; struct Impl; - impl Swizzle for Impl { + impl Swizzle<{$index.len()}> for Impl { const INDEX: [usize; {$index.len()}] = $index; } Impl::swizzle($vector) @@ -60,204 +60,194 @@ pub macro simd_swizzle { $first:expr, $second:expr, $index:expr $(,)? ) => { { - use $crate::simd::{Which, Swizzle2}; + use $crate::simd::Swizzle; struct Impl; - impl Swizzle2 for Impl { - const INDEX: [Which; {$index.len()}] = $index; + impl Swizzle<{$index.len()}> for Impl { + const INDEX: [usize; {$index.len()}] = $index; } - Impl::swizzle2($first, $second) + Impl::concat_swizzle($first, $second) } } } -/// 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 { - /// Index of a lane in the first input SIMD vector. - First(usize), - /// Index of a lane in the second input SIMD vector. - Second(usize), -} - /// Create a vector from the elements of another vector. -pub trait Swizzle { - /// Map from the lanes of the input vector to the output vector. - const INDEX: [usize; OUTPUT_LANES]; +pub trait Swizzle { + /// Map from the elements of the input vector to the output vector. + const INDEX: [usize; N]; - /// Create a new vector from the lanes of `vector`. + /// Create a new vector from the elements of `vector`. /// /// Lane `i` of the output is `vector[Self::INDEX[i]]`. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - fn swizzle(vector: Simd) -> Simd + fn swizzle(vector: Simd) -> Simd where T: SimdElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - // Safety: `vector` is a vector, and `INDEX_IMPL` is a const array of u32. - unsafe { intrinsics::simd_shuffle(vector, vector, Self::INDEX_IMPL) } + // Safety: `vector` is a vector, and the index is a const array of u32. + unsafe { + intrinsics::simd_shuffle( + vector, + vector, + const { + let mut output = [0; N]; + let mut i = 0; + while i < N { + let index = Self::INDEX[i]; + assert!(index as u32 as usize == index); + assert!( + index < M, + "source element index exceeds input vector length" + ); + output[i] = index as u32; + i += 1; + } + output + }, + ) + } } -} -/// Create a vector from the elements of two other vectors. -pub trait Swizzle2 { - /// Map from the lanes of the input vectors to the output vector - const INDEX: [Which; OUTPUT_LANES]; - - /// Create a new vector from the lanes of `first` and `second`. + /// Create a new vector from the elements of `first` and `second`. /// - /// Lane `i` is `first[j]` when `Self::INDEX[i]` is `First(j)`, or `second[j]` when it is - /// `Second(j)`. + /// Lane `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of + /// `first` and `second`. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - fn swizzle2( - first: Simd, - second: Simd, - ) -> Simd + fn concat_swizzle(first: Simd, second: Simd) -> Simd where T: SimdElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - // Safety: `first` and `second` are vectors, and `INDEX_IMPL` is a const array of u32. - unsafe { intrinsics::simd_shuffle(first, second, Self::INDEX_IMPL) } + // Safety: `first` and `second` are vectors, and the index is a const array of u32. + unsafe { + intrinsics::simd_shuffle( + first, + second, + const { + let mut output = [0; N]; + let mut i = 0; + while i < N { + let index = Self::INDEX[i]; + assert!(index as u32 as usize == index); + assert!( + index < 2 * M, + "source element index exceeds input vector length" + ); + output[i] = index as u32; + i += 1; + } + output + }, + ) + } + } + + /// Create a new mask from the elements of `mask`. + /// + /// Element `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of + /// `first` and `second`. + #[inline] + #[must_use = "method returns a new mask and does not mutate the original inputs"] + fn swizzle_mask(mask: Mask) -> Mask + where + T: MaskElement, + LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, + { + // SAFETY: all elements of this mask come from another mask + unsafe { Mask::from_int_unchecked(Self::swizzle(mask.to_int())) } + } + + /// Create a new mask from the elements of `first` and `second`. + /// + /// Element `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of + /// `first` and `second`. + #[inline] + #[must_use = "method returns a new mask and does not mutate the original inputs"] + fn concat_swizzle_mask(first: Mask, second: Mask) -> Mask + where + T: MaskElement, + LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, + { + // SAFETY: all elements of this mask come from another mask + unsafe { Mask::from_int_unchecked(Self::concat_swizzle(first.to_int(), second.to_int())) } } } -/// The `simd_shuffle` intrinsic expects `u32`, so do error checking and conversion here. -/// This trait hides `INDEX_IMPL` from the public API. -trait SwizzleImpl { - const INDEX_IMPL: [u32; OUTPUT_LANES]; -} - -impl SwizzleImpl - for T -where - T: Swizzle + ?Sized, -{ - const INDEX_IMPL: [u32; OUTPUT_LANES] = { - let mut output = [0; OUTPUT_LANES]; - let mut i = 0; - while i < OUTPUT_LANES { - let index = Self::INDEX[i]; - assert!(index as u32 as usize == index); - assert!(index < INPUT_LANES, "source lane exceeds input lane count",); - output[i] = index as u32; - i += 1; - } - output - }; -} - -/// The `simd_shuffle` intrinsic expects `u32`, so do error checking and conversion here. -/// This trait hides `INDEX_IMPL` from the public API. -trait Swizzle2Impl { - const INDEX_IMPL: [u32; OUTPUT_LANES]; -} - -impl Swizzle2Impl - for T -where - T: Swizzle2 + ?Sized, -{ - const INDEX_IMPL: [u32; OUTPUT_LANES] = { - let mut output = [0; OUTPUT_LANES]; - let mut i = 0; - while i < OUTPUT_LANES { - let (offset, index) = match Self::INDEX[i] { - Which::First(index) => (false, index), - Which::Second(index) => (true, index), - }; - assert!(index < INPUT_LANES, "source lane exceeds input lane count",); - - // lanes are indexed by the first vector, then second vector - let index = if offset { index + INPUT_LANES } else { index }; - assert!(index as u32 as usize == index); - output[i] = index as u32; - i += 1; - } - output - }; -} - -impl Simd +impl Simd where T: SimdElement, - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, { - /// Reverse the order of the lanes in the vector. + /// Reverse the order of the elements in the vector. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn reverse(self) -> Self { - const fn reverse_index() -> [usize; LANES] { - let mut index = [0; LANES]; - let mut i = 0; - while i < LANES { - index[i] = LANES - i - 1; - i += 1; - } - index - } - struct Reverse; - impl Swizzle for Reverse { - const INDEX: [usize; LANES] = reverse_index::(); + impl Swizzle for Reverse { + const INDEX: [usize; N] = const { + let mut index = [0; N]; + let mut i = 0; + while i < N { + index[i] = N - i - 1; + i += 1; + } + index + }; } Reverse::swizzle(self) } /// Rotates the vector such that the first `OFFSET` elements of the slice move to the end - /// while the last `LANES - OFFSET` elements move to the front. After calling `rotate_lanes_left`, - /// the element previously in lane `OFFSET` will become the first element in the slice. + /// while the last `self.len() - OFFSET` elements move to the front. After calling `rotate_elements_left`, + /// the element previously at index `OFFSET` will become the first element in the slice. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn rotate_lanes_left(self) -> Self { - const fn rotate_index() -> [usize; LANES] { - let offset = OFFSET % LANES; - let mut index = [0; LANES]; - let mut i = 0; - while i < LANES { - index[i] = (i + offset) % LANES; - i += 1; - } - index - } - + pub fn rotate_elements_left(self) -> Self { struct Rotate; - impl Swizzle for Rotate { - const INDEX: [usize; LANES] = rotate_index::(); + impl Swizzle for Rotate { + const INDEX: [usize; N] = const { + let offset = OFFSET % N; + let mut index = [0; N]; + let mut i = 0; + while i < N { + index[i] = (i + offset) % N; + i += 1; + } + index + }; } Rotate::::swizzle(self) } - /// Rotates the vector such that the first `LANES - OFFSET` elements of the vector move to - /// the end while the last `OFFSET` elements move to the front. After calling `rotate_lanes_right`, - /// the element previously at index `LANES - OFFSET` will become the first element in the slice. + /// Rotates the vector such that the first `self.len() - OFFSET` elements of the vector move to + /// the end while the last `OFFSET` elements move to the front. After calling `rotate_elements_right`, + /// the element previously at index `self.len() - OFFSET` will become the first element in the slice. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn rotate_lanes_right(self) -> Self { - const fn rotate_index() -> [usize; LANES] { - let offset = LANES - OFFSET % LANES; - let mut index = [0; LANES]; - let mut i = 0; - while i < LANES { - index[i] = (i + offset) % LANES; - i += 1; - } - index - } - + pub fn rotate_elements_right(self) -> Self { struct Rotate; - impl Swizzle for Rotate { - const INDEX: [usize; LANES] = rotate_index::(); + impl Swizzle for Rotate { + const INDEX: [usize; N] = const { + let offset = N - OFFSET % N; + let mut index = [0; N]; + let mut i = 0; + while i < N { + index[i] = (i + offset) % N; + i += 1; + } + index + }; } Rotate::::swizzle(self) @@ -265,7 +255,7 @@ where /// Interleave two vectors. /// - /// The resulting vectors contain lanes taken alternatively from `self` and `other`, first + /// The resulting vectors contain elements taken alternatively from `self` and `other`, first /// filling the first result, and then the second. /// /// The reverse of this operation is [`Simd::deinterleave`]. @@ -282,18 +272,13 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn interleave(self, other: Self) -> (Self, Self) { - const fn interleave(high: bool) -> [Which; LANES] { - let mut idx = [Which::First(0); LANES]; + const fn interleave(high: bool) -> [usize; N] { + let mut idx = [0; N]; let mut i = 0; - while i < LANES { - // Treat the source as a concatenated vector - let dst_index = if high { i + LANES } else { i }; - let src_index = dst_index / 2 + (dst_index % 2) * LANES; - idx[i] = if src_index < LANES { - Which::First(src_index) - } else { - Which::Second(src_index % LANES) - }; + while i < N { + let dst_index = if high { i + N } else { i }; + let src_index = dst_index / 2 + (dst_index % 2) * N; + idx[i] = src_index; i += 1; } idx @@ -302,24 +287,27 @@ where struct Lo; struct Hi; - impl Swizzle2 for Lo { - const INDEX: [Which; LANES] = interleave::(false); + impl Swizzle for Lo { + const INDEX: [usize; N] = interleave::(false); } - impl Swizzle2 for Hi { - const INDEX: [Which; LANES] = interleave::(true); + impl Swizzle for Hi { + const INDEX: [usize; N] = interleave::(true); } - (Lo::swizzle2(self, other), Hi::swizzle2(self, other)) + ( + Lo::concat_swizzle(self, other), + Hi::concat_swizzle(self, other), + ) } /// Deinterleave two vectors. /// - /// The first result takes every other lane of `self` and then `other`, starting with - /// the first lane. + /// The first result takes every other element of `self` and then `other`, starting with + /// the first element. /// - /// The second result takes every other lane of `self` and then `other`, starting with - /// the second lane. + /// The second result takes every other element of `self` and then `other`, starting with + /// the second element. /// /// The reverse of this operation is [`Simd::interleave`]. /// @@ -335,17 +323,11 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn deinterleave(self, other: Self) -> (Self, Self) { - const fn deinterleave(second: bool) -> [Which; LANES] { - let mut idx = [Which::First(0); LANES]; + const fn deinterleave(second: bool) -> [usize; N] { + let mut idx = [0; N]; let mut i = 0; - while i < LANES { - // Treat the source as a concatenated vector - let src_index = i * 2 + second as usize; - idx[i] = if src_index < LANES { - Which::First(src_index) - } else { - Which::Second(src_index % LANES) - }; + while i < N { + idx[i] = i * 2 + second as usize; i += 1; } idx @@ -354,14 +336,52 @@ where struct Even; struct Odd; - impl Swizzle2 for Even { - const INDEX: [Which; LANES] = deinterleave::(false); + impl Swizzle for Even { + const INDEX: [usize; N] = deinterleave::(false); } - impl Swizzle2 for Odd { - const INDEX: [Which; LANES] = deinterleave::(true); + impl Swizzle for Odd { + const INDEX: [usize; N] = deinterleave::(true); } - (Even::swizzle2(self, other), Odd::swizzle2(self, other)) + ( + Even::concat_swizzle(self, other), + Odd::concat_swizzle(self, other), + ) + } + + /// Resize a vector. + /// + /// If `M` > `N`, extends the length of a vector, setting the new elements to `value`. + /// If `M` < `N`, truncates the vector to the first `M` elements. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::u32x4; + /// let x = u32x4::from_array([0, 1, 2, 3]); + /// assert_eq!(x.resize::<8>(9).to_array(), [0, 1, 2, 3, 9, 9, 9, 9]); + /// assert_eq!(x.resize::<2>(9).to_array(), [0, 1]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn resize(self, value: T) -> Simd + where + LaneCount: SupportedLaneCount, + { + struct Resize; + impl Swizzle for Resize { + const INDEX: [usize; M] = const { + let mut index = [0; M]; + let mut i = 0; + while i < M { + index[i] = if i < N { i } else { N }; + i += 1; + } + index + }; + } + Resize::::concat_swizzle(self, Simd::splat(value)) } } diff --git a/crates/core_simd/src/swizzle_dyn.rs b/crates/core_simd/src/swizzle_dyn.rs index ce621792534e..bd8a38e350d3 100644 --- a/crates/core_simd/src/swizzle_dyn.rs +++ b/crates/core_simd/src/swizzle_dyn.rs @@ -86,7 +86,7 @@ where #[inline] #[allow(clippy::let_and_return)] unsafe fn avx2_pshufb(bytes: Simd, idxs: Simd) -> Simd { - use crate::simd::SimdPartialOrd; + use crate::simd::cmp::SimdPartialOrd; #[cfg(target_arch = "x86")] use core::arch::x86; #[cfg(target_arch = "x86_64")] @@ -149,7 +149,7 @@ where // On x86, make sure the top bit is set. #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] let idxs = { - use crate::simd::SimdPartialOrd; + use crate::simd::cmp::SimdPartialOrd; idxs.simd_lt(Simd::splat(N as u8)) .select(idxs, Simd::splat(u8::MAX)) }; diff --git a/crates/core_simd/src/to_bytes.rs b/crates/core_simd/src/to_bytes.rs index b36b1a347b22..222526c4ab30 100644 --- a/crates/core_simd/src/to_bytes.rs +++ b/crates/core_simd/src/to_bytes.rs @@ -1,24 +1,125 @@ +use crate::simd::{ + num::{SimdFloat, SimdInt, SimdUint}, + LaneCount, Simd, SimdElement, SupportedLaneCount, +}; + +mod sealed { + use super::*; + pub trait Sealed {} + impl Sealed for Simd where LaneCount: SupportedLaneCount {} +} +use sealed::Sealed; + +/// Convert SIMD vectors to vectors of bytes +pub trait ToBytes: Sealed { + /// This type, reinterpreted as bytes. + type Bytes: Copy + + Unpin + + Send + + Sync + + AsRef<[u8]> + + AsMut<[u8]> + + SimdUint + + 'static; + + /// Return the memory representation of this integer as a byte array in native byte + /// order. + fn to_ne_bytes(self) -> Self::Bytes; + + /// Return the memory representation of this integer as a byte array in big-endian + /// (network) byte order. + fn to_be_bytes(self) -> Self::Bytes; + + /// Return the memory representation of this integer as a byte array in little-endian + /// byte order. + fn to_le_bytes(self) -> Self::Bytes; + + /// Create a native endian integer value from its memory representation as a byte array + /// in native endianness. + fn from_ne_bytes(bytes: Self::Bytes) -> Self; + + /// Create an integer value from its representation as a byte array in big endian. + fn from_be_bytes(bytes: Self::Bytes) -> Self; + + /// Create an integer value from its representation as a byte array in little endian. + fn from_le_bytes(bytes: Self::Bytes) -> Self; +} + +macro_rules! swap_bytes { + { f32, $x:expr } => { Simd::from_bits($x.to_bits().swap_bytes()) }; + { f64, $x:expr } => { Simd::from_bits($x.to_bits().swap_bytes()) }; + { $ty:ty, $x:expr } => { $x.swap_bytes() } +} + macro_rules! impl_to_bytes { - { $ty:ty, $size:literal } => { - impl crate::simd::Simd<$ty, LANES> - where - crate::simd::LaneCount: crate::simd::SupportedLaneCount, - crate::simd::LaneCount<{{ $size * LANES }}>: crate::simd::SupportedLaneCount, - { - /// Return the memory representation of this integer as a byte array in native byte - /// order. - pub fn to_ne_bytes(self) -> crate::simd::Simd { + { $ty:tt, 1 } => { impl_to_bytes! { $ty, 1 * [1, 2, 4, 8, 16, 32, 64] } }; + { $ty:tt, 2 } => { impl_to_bytes! { $ty, 2 * [1, 2, 4, 8, 16, 32] } }; + { $ty:tt, 4 } => { impl_to_bytes! { $ty, 4 * [1, 2, 4, 8, 16] } }; + { $ty:tt, 8 } => { impl_to_bytes! { $ty, 8 * [1, 2, 4, 8] } }; + { $ty:tt, 16 } => { impl_to_bytes! { $ty, 16 * [1, 2, 4] } }; + { $ty:tt, 32 } => { impl_to_bytes! { $ty, 32 * [1, 2] } }; + { $ty:tt, 64 } => { impl_to_bytes! { $ty, 64 * [1] } }; + + { $ty:tt, $size:literal * [$($elems:literal),*] } => { + $( + impl ToBytes for Simd<$ty, $elems> { + type Bytes = Simd; + + #[inline] + fn to_ne_bytes(self) -> Self::Bytes { // Safety: transmuting between vectors is safe - unsafe { core::mem::transmute_copy(&self) } + unsafe { + #![allow(clippy::useless_transmute)] + core::mem::transmute(self) + } } - /// Create a native endian integer value from its memory representation as a byte array - /// in native endianness. - pub fn from_ne_bytes(bytes: crate::simd::Simd) -> Self { + #[inline] + fn to_be_bytes(mut self) -> Self::Bytes { + if !cfg!(target_endian = "big") { + self = swap_bytes!($ty, self); + } + self.to_ne_bytes() + } + + #[inline] + fn to_le_bytes(mut self) -> Self::Bytes { + if !cfg!(target_endian = "little") { + self = swap_bytes!($ty, self); + } + self.to_ne_bytes() + } + + #[inline] + fn from_ne_bytes(bytes: Self::Bytes) -> Self { // Safety: transmuting between vectors is safe - unsafe { core::mem::transmute_copy(&bytes) } + unsafe { + #![allow(clippy::useless_transmute)] + core::mem::transmute(bytes) + } + } + + #[inline] + fn from_be_bytes(bytes: Self::Bytes) -> Self { + let ret = Self::from_ne_bytes(bytes); + if cfg!(target_endian = "big") { + ret + } else { + swap_bytes!($ty, ret) + } + } + + #[inline] + fn from_le_bytes(bytes: Self::Bytes) -> Self { + let ret = Self::from_ne_bytes(bytes); + if cfg!(target_endian = "little") { + ret + } else { + swap_bytes!($ty, ret) + } } } + )* } } @@ -39,3 +140,6 @@ impl_to_bytes! { i64, 8 } impl_to_bytes! { isize, 4 } #[cfg(target_pointer_width = "64")] impl_to_bytes! { isize, 8 } + +impl_to_bytes! { f32, 4 } +impl_to_bytes! { f64, 8 } diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 9aa7bacfce98..105c06741c58 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -1,6 +1,8 @@ use crate::simd::{ - intrinsics, LaneCount, Mask, MaskElement, SimdConstPtr, SimdMutPtr, SimdPartialOrd, - SupportedLaneCount, Swizzle, + cmp::SimdPartialOrd, + intrinsics, + ptr::{SimdConstPtr, SimdMutPtr}, + LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, }; use core::convert::{TryFrom, TryInto}; @@ -110,7 +112,7 @@ where T: SimdElement, { /// Number of elements in this vector. - pub const LANES: usize = N; + pub const LEN: usize = N; /// Returns the number of elements in this SIMD vector. /// @@ -118,13 +120,16 @@ where /// /// ``` /// # #![feature(portable_simd)] - /// # use core::simd::u32x4; + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::u32x4; /// let v = u32x4::splat(0); - /// assert_eq!(v.lanes(), 4); + /// assert_eq!(v.len(), 4); /// ``` #[inline] - pub const fn lanes(&self) -> usize { - Self::LANES + #[allow(clippy::len_without_is_empty)] + pub const fn len(&self) -> usize { + Self::LEN } /// Constructs a new SIMD vector with all elements set to the given value. @@ -133,7 +138,9 @@ where /// /// ``` /// # #![feature(portable_simd)] - /// # use core::simd::u32x4; + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::u32x4; /// let v = u32x4::splat(8); /// assert_eq!(v.as_array(), &[8, 8, 8, 8]); /// ``` @@ -142,10 +149,10 @@ where // This is preferred over `[value; N]`, since it's explicitly a splat: // https://github.com/rust-lang/rust/issues/97804 struct Splat; - impl Swizzle<1, N> for Splat { + impl Swizzle for Splat { const INDEX: [usize; N] = [0; N]; } - Splat::swizzle(Simd::::from([value])) + Splat::swizzle::(Simd::::from([value])) } /// Returns an array reference containing the entire SIMD vector. @@ -271,7 +278,7 @@ where #[track_caller] pub const fn from_slice(slice: &[T]) -> Self { assert!( - slice.len() >= Self::LANES, + slice.len() >= Self::LEN, "slice length must be at least the number of elements" ); // SAFETY: We just checked that the slice contains @@ -301,7 +308,7 @@ where #[track_caller] pub fn copy_to_slice(self, slice: &mut [T]) { assert!( - slice.len() >= Self::LANES, + slice.len() >= Self::LEN, "slice length must be at least the number of elements" ); // SAFETY: We just checked that the slice contains @@ -394,7 +401,7 @@ where /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Simd, SimdPartialOrd, Mask}; + /// # use simd::{Simd, cmp::SimdPartialOrd, Mask}; /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; /// let idxs = Simd::from_array([9, 3, 0, 5]); // Includes an out-of-bounds index /// let alt = Simd::from_array([-5, -4, -3, -2]); @@ -434,7 +441,7 @@ where /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Simd, SimdConstPtr}; + /// # use simd::prelude::*; /// let values = [6, 2, 4, 9]; /// let offsets = Simd::from_array([1, 0, 0, 3]); /// let source = Simd::splat(values.as_ptr()).wrapping_add(offsets); @@ -467,7 +474,7 @@ where /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Mask, Simd, SimdConstPtr}; + /// # use simd::prelude::*; /// let values = [6, 2, 4, 9]; /// let enable = Mask::from_array([true, true, false, true]); /// let offsets = Simd::from_array([1, 0, 0, 3]); @@ -550,7 +557,7 @@ where /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Simd, SimdPartialOrd, Mask}; + /// # use simd::{Simd, cmp::SimdPartialOrd, 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]); @@ -604,7 +611,7 @@ where /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Simd, SimdMutPtr}; + /// # use simd::{Simd, ptr::SimdMutPtr}; /// let mut values = [0; 4]; /// let offset = Simd::from_array([3, 2, 1, 0]); /// let ptrs = Simd::splat(values.as_mut_ptr()).wrapping_add(offset); @@ -631,7 +638,7 @@ where /// # #![feature(portable_simd)] /// # #[cfg(feature = "as_crate")] use core_simd::simd; /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{Mask, Simd, SimdMutPtr}; + /// # use simd::{Mask, Simd, ptr::SimdMutPtr}; /// let mut values = [0; 4]; /// let offset = Simd::from_array([3, 2, 1, 0]); /// let ptrs = Simd::splat(values.as_mut_ptr()).wrapping_add(offset); diff --git a/crates/core_simd/src/vendor.rs b/crates/core_simd/src/vendor.rs index 9fb70218c954..6223bedb4e13 100644 --- a/crates/core_simd/src/vendor.rs +++ b/crates/core_simd/src/vendor.rs @@ -21,7 +21,7 @@ macro_rules! from_transmute { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod x86; -#[cfg(any(target_arch = "wasm32"))] +#[cfg(target_arch = "wasm32")] mod wasm32; #[cfg(any(target_arch = "aarch64", target_arch = "arm",))] diff --git a/crates/core_simd/src/vendor/x86.rs b/crates/core_simd/src/vendor/x86.rs index 0dd47015ed22..66aaf90eef59 100644 --- a/crates/core_simd/src/vendor/x86.rs +++ b/crates/core_simd/src/vendor/x86.rs @@ -1,6 +1,6 @@ use crate::simd::*; -#[cfg(any(target_arch = "x86"))] +#[cfg(target_arch = "x86")] use core::arch::x86::*; #[cfg(target_arch = "x86_64")] diff --git a/crates/core_simd/tests/cast.rs b/crates/core_simd/tests/cast.rs index 00545936ea2a..185e1945faa2 100644 --- a/crates/core_simd/tests/cast.rs +++ b/crates/core_simd/tests/cast.rs @@ -3,7 +3,7 @@ macro_rules! cast_types { ($start:ident, $($target:ident),*) => { mod $start { #[allow(unused)] - use core_simd::simd::{Simd, SimdInt, SimdUint, SimdFloat}; + use core_simd::simd::prelude::*; type Vector = Simd<$start, N>; $( mod $target { diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs index 9f8bad1c36c0..00fc2a24e27a 100644 --- a/crates/core_simd/tests/masks.rs +++ b/crates/core_simd/tests/masks.rs @@ -72,7 +72,6 @@ macro_rules! test_mask_api { #[test] fn roundtrip_bitmask_conversion() { - use core_simd::simd::ToBitMask; let values = [ true, false, false, true, false, false, true, false, true, true, false, false, false, false, false, true, @@ -85,8 +84,6 @@ macro_rules! test_mask_api { #[test] fn roundtrip_bitmask_conversion_short() { - use core_simd::simd::ToBitMask; - let values = [ false, false, false, true, ]; @@ -125,18 +122,17 @@ macro_rules! test_mask_api { cast_impl::(); } - #[cfg(feature = "generic_const_exprs")] #[test] - fn roundtrip_bitmask_array_conversion() { - use core_simd::simd::ToBitMaskArray; + fn roundtrip_bitmask_vector_conversion() { + use core_simd::simd::ToBytes; let values = [ true, false, false, true, false, false, true, false, true, true, false, false, false, false, false, true, ]; let mask = Mask::<$type, 16>::from_array(values); - let bitmask = mask.to_bitmask_array(); - assert_eq!(bitmask, [0b01001001, 0b10000011]); - assert_eq!(Mask::<$type, 16>::from_bitmask_array(bitmask), mask); + let bitmask = mask.to_bitmask_vector(); + assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b01001001, 0b10000011]); + assert_eq!(Mask::<$type, 16>::from_bitmask_vector(bitmask), mask); } } } diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index 3a02f3f01e1c..aa565a137527 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -6,7 +6,7 @@ macro_rules! impl_unary_op_test { { $scalar:ty, $trait:ident :: $fn:ident, $scalar_fn:expr } => { test_helpers::test_lanes! { fn $fn() { - test_helpers::test_unary_elementwise( + test_helpers::test_unary_elementwise_flush_subnormals( & as core::ops::$trait>::$fn, &$scalar_fn, &|_| true, @@ -31,7 +31,7 @@ macro_rules! impl_binary_op_test { test_helpers::test_lanes! { fn normal() { - test_helpers::test_binary_elementwise( + test_helpers::test_binary_elementwise_flush_subnormals( & as core::ops::$trait>::$fn, &$scalar_fn, &|_, _| true, @@ -39,7 +39,7 @@ macro_rules! impl_binary_op_test { } fn assign() { - test_helpers::test_binary_elementwise( + test_helpers::test_binary_elementwise_flush_subnormals( &|mut a, b| { as core::ops::$trait_assign>::$fn_assign(&mut a, b); a }, &$scalar_fn, &|_, _| true, @@ -68,6 +68,7 @@ macro_rules! impl_binary_checked_op_test { test_helpers::test_lanes! { fn normal() { + #![allow(clippy::redundant_closure_call)] test_helpers::test_binary_elementwise( & as core::ops::$trait>::$fn, &$scalar_fn, @@ -76,6 +77,7 @@ macro_rules! impl_binary_checked_op_test { } fn assign() { + #![allow(clippy::redundant_closure_call)] test_helpers::test_binary_elementwise( &|mut a, b| { as core::ops::$trait_assign>::$fn_assign(&mut a, b); a }, &$scalar_fn, @@ -94,11 +96,43 @@ macro_rules! impl_binary_checked_op_test { macro_rules! impl_common_integer_tests { { $vector:ident, $scalar:ident } => { test_helpers::test_lanes! { + fn shr() { + use core::ops::Shr; + let shr = |x: $scalar, y: $scalar| x.wrapping_shr(y as _); + test_helpers::test_binary_elementwise( + &<$vector:: as Shr<$vector::>>::shr, + &shr, + &|_, _| true, + ); + test_helpers::test_binary_scalar_rhs_elementwise( + &<$vector:: as Shr<$scalar>>::shr, + &shr, + &|_, _| true, + ); + } + + fn shl() { + use core::ops::Shl; + let shl = |x: $scalar, y: $scalar| x.wrapping_shl(y as _); + test_helpers::test_binary_elementwise( + &<$vector:: as Shl<$vector::>>::shl, + &shl, + &|_, _| true, + ); + test_helpers::test_binary_scalar_rhs_elementwise( + &<$vector:: as Shl<$scalar>>::shl, + &shl, + &|_, _| true, + ); + } + fn reduce_sum() { test_helpers::test_1(&|x| { + use test_helpers::subnormals::{flush, flush_in}; test_helpers::prop_assert_biteq! ( $vector::::from_array(x).reduce_sum(), x.iter().copied().fold(0 as $scalar, $scalar::wrapping_add), + flush(x.iter().copied().map(flush_in).fold(0 as $scalar, $scalar::wrapping_add)), ); Ok(()) }); @@ -106,9 +140,11 @@ macro_rules! impl_common_integer_tests { fn reduce_product() { test_helpers::test_1(&|x| { + use test_helpers::subnormals::{flush, flush_in}; test_helpers::prop_assert_biteq! ( $vector::::from_array(x).reduce_product(), x.iter().copied().fold(1 as $scalar, $scalar::wrapping_mul), + flush(x.iter().copied().map(flush_in).fold(1 as $scalar, $scalar::wrapping_mul)), ); Ok(()) }); @@ -163,6 +199,54 @@ macro_rules! impl_common_integer_tests { Ok(()) }); } + + fn swap_bytes() { + test_helpers::test_unary_elementwise( + &$vector::::swap_bytes, + &$scalar::swap_bytes, + &|_| true, + ) + } + + fn reverse_bits() { + test_helpers::test_unary_elementwise( + &$vector::::reverse_bits, + &$scalar::reverse_bits, + &|_| true, + ) + } + + fn leading_zeros() { + test_helpers::test_unary_elementwise( + &$vector::::leading_zeros, + &|x| x.leading_zeros() as _, + &|_| true, + ) + } + + fn trailing_zeros() { + test_helpers::test_unary_elementwise( + &$vector::::trailing_zeros, + &|x| x.trailing_zeros() as _, + &|_| true, + ) + } + + fn leading_ones() { + test_helpers::test_unary_elementwise( + &$vector::::leading_ones, + &|x| x.leading_ones() as _, + &|_| true, + ) + } + + fn trailing_ones() { + test_helpers::test_unary_elementwise( + &$vector::::trailing_ones, + &|x| x.trailing_ones() as _, + &|_| true, + ) + } } } } @@ -172,7 +256,7 @@ macro_rules! impl_common_integer_tests { macro_rules! impl_signed_tests { { $scalar:tt } => { mod $scalar { - use core_simd::simd::SimdInt; + use core_simd::simd::num::SimdInt; type Vector = core_simd::simd::Simd; type Scalar = $scalar; @@ -224,7 +308,7 @@ macro_rules! impl_signed_tests { } fn simd_min() { - use core_simd::simd::SimdOrd; + use core_simd::simd::cmp::SimdOrd; let a = Vector::::splat(Scalar::MIN); let b = Vector::::splat(0); assert_eq!(a.simd_min(b), a); @@ -234,7 +318,7 @@ macro_rules! impl_signed_tests { } fn simd_max() { - use core_simd::simd::SimdOrd; + use core_simd::simd::cmp::SimdOrd; let a = Vector::::splat(Scalar::MIN); let b = Vector::::splat(0); assert_eq!(a.simd_max(b), b); @@ -244,7 +328,7 @@ macro_rules! impl_signed_tests { } fn simd_clamp() { - use core_simd::simd::SimdOrd; + use core_simd::simd::cmp::SimdOrd; let min = Vector::::splat(Scalar::MIN); let max = Vector::::splat(Scalar::MAX); let zero = Vector::::splat(0); @@ -313,7 +397,7 @@ macro_rules! impl_signed_tests { macro_rules! impl_unsigned_tests { { $scalar:tt } => { mod $scalar { - use core_simd::simd::SimdUint; + use core_simd::simd::num::SimdUint; type Vector = core_simd::simd::Simd; type Scalar = $scalar; @@ -327,6 +411,16 @@ macro_rules! impl_unsigned_tests { } } + test_helpers::test_lanes! { + fn wrapping_neg() { + test_helpers::test_unary_elementwise( + &Vector::::wrapping_neg, + &Scalar::wrapping_neg, + &|_| true, + ); + } + } + impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add); impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub); impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul); @@ -348,7 +442,7 @@ macro_rules! impl_unsigned_tests { macro_rules! impl_float_tests { { $scalar:tt, $int_scalar:tt } => { mod $scalar { - use core_simd::simd::SimdFloat; + use core_simd::simd::num::SimdFloat; type Vector = core_simd::simd::Simd; type Scalar = $scalar; @@ -433,7 +527,7 @@ macro_rules! impl_float_tests { } fn to_degrees() { - test_helpers::test_unary_elementwise( + test_helpers::test_unary_elementwise_flush_subnormals( &Vector::::to_degrees, &Scalar::to_degrees, &|_| true, @@ -441,7 +535,7 @@ macro_rules! impl_float_tests { } fn to_radians() { - test_helpers::test_unary_elementwise( + test_helpers::test_unary_elementwise_flush_subnormals( &Vector::::to_radians, &Scalar::to_radians, &|_| true, @@ -511,7 +605,12 @@ macro_rules! impl_float_tests { } fn simd_clamp() { + if cfg!(all(target_arch = "powerpc64", target_feature = "vsx")) { + // https://gitlab.com/qemu-project/qemu/-/issues/1780 + return; + } test_helpers::test_3(&|value: [Scalar; LANES], mut min: [Scalar; LANES], mut max: [Scalar; LANES]| { + use test_helpers::subnormals::flush_in; for (min, max) in min.iter_mut().zip(max.iter_mut()) { if max < min { core::mem::swap(min, max); @@ -528,8 +627,20 @@ macro_rules! impl_float_tests { for i in 0..LANES { result_scalar[i] = value[i].clamp(min[i], max[i]); } + let mut result_scalar_flush = [Scalar::default(); LANES]; + for i in 0..LANES { + // Comparisons flush-to-zero, but return value selection is _not_ flushed. + let mut value = value[i]; + if flush_in(value) < flush_in(min[i]) { + value = min[i]; + } + if flush_in(value) > flush_in(max[i]) { + value = max[i]; + } + result_scalar_flush[i] = value + } let result_vector = Vector::from_array(value).simd_clamp(min.into(), max.into()).to_array(); - test_helpers::prop_assert_biteq!(result_scalar, result_vector); + test_helpers::prop_assert_biteq!(result_vector, result_scalar, result_scalar_flush); Ok(()) }) } diff --git a/crates/core_simd/tests/pointers.rs b/crates/core_simd/tests/pointers.rs index 0ae8f83b8b97..a90ff928cedc 100644 --- a/crates/core_simd/tests/pointers.rs +++ b/crates/core_simd/tests/pointers.rs @@ -1,6 +1,9 @@ #![feature(portable_simd, strict_provenance)] -use core_simd::simd::{Simd, SimdConstPtr, SimdMutPtr}; +use core_simd::simd::{ + ptr::{SimdConstPtr, SimdMutPtr}, + Simd, +}; macro_rules! common_tests { { $constness:ident } => { diff --git a/crates/core_simd/tests/round.rs b/crates/core_simd/tests/round.rs index aacf7bd3bcc2..847766ec41ed 100644 --- a/crates/core_simd/tests/round.rs +++ b/crates/core_simd/tests/round.rs @@ -43,7 +43,7 @@ macro_rules! float_rounding_test { } fn fract() { - test_helpers::test_unary_elementwise( + test_helpers::test_unary_elementwise_flush_subnormals( &Vector::::fract, &Scalar::fract, &|_| true, @@ -53,7 +53,7 @@ macro_rules! float_rounding_test { test_helpers::test_lanes! { fn to_int_unchecked() { - use core_simd::simd::SimdFloat; + use core_simd::simd::num::SimdFloat; // The maximum integer that can be represented by the equivalently sized float has // all of the mantissa digits set to 1, pushed up to the MSB. const ALL_MANTISSA_BITS: IntScalar = ((1 << ::MANTISSA_DIGITS) - 1); diff --git a/crates/core_simd/tests/swizzle.rs b/crates/core_simd/tests/swizzle.rs index 8cd7c33e823f..522d71439b77 100644 --- a/crates/core_simd/tests/swizzle.rs +++ b/crates/core_simd/tests/swizzle.rs @@ -11,10 +11,10 @@ wasm_bindgen_test_configure!(run_in_browser); #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn swizzle() { struct Index; - impl Swizzle<4, 4> for Index { + impl Swizzle<4> for Index { const INDEX: [usize; 4] = [2, 1, 3, 0]; } - impl Swizzle<4, 2> for Index { + impl Swizzle<2> for Index { const INDEX: [usize; 2] = [1, 1]; } @@ -34,18 +34,18 @@ fn reverse() { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn rotate() { let a = Simd::from_array([1, 2, 3, 4]); - assert_eq!(a.rotate_lanes_left::<0>().to_array(), [1, 2, 3, 4]); - assert_eq!(a.rotate_lanes_left::<1>().to_array(), [2, 3, 4, 1]); - assert_eq!(a.rotate_lanes_left::<2>().to_array(), [3, 4, 1, 2]); - assert_eq!(a.rotate_lanes_left::<3>().to_array(), [4, 1, 2, 3]); - assert_eq!(a.rotate_lanes_left::<4>().to_array(), [1, 2, 3, 4]); - assert_eq!(a.rotate_lanes_left::<5>().to_array(), [2, 3, 4, 1]); - assert_eq!(a.rotate_lanes_right::<0>().to_array(), [1, 2, 3, 4]); - assert_eq!(a.rotate_lanes_right::<1>().to_array(), [4, 1, 2, 3]); - assert_eq!(a.rotate_lanes_right::<2>().to_array(), [3, 4, 1, 2]); - assert_eq!(a.rotate_lanes_right::<3>().to_array(), [2, 3, 4, 1]); - assert_eq!(a.rotate_lanes_right::<4>().to_array(), [1, 2, 3, 4]); - assert_eq!(a.rotate_lanes_right::<5>().to_array(), [4, 1, 2, 3]); + assert_eq!(a.rotate_elements_left::<0>().to_array(), [1, 2, 3, 4]); + assert_eq!(a.rotate_elements_left::<1>().to_array(), [2, 3, 4, 1]); + assert_eq!(a.rotate_elements_left::<2>().to_array(), [3, 4, 1, 2]); + assert_eq!(a.rotate_elements_left::<3>().to_array(), [4, 1, 2, 3]); + assert_eq!(a.rotate_elements_left::<4>().to_array(), [1, 2, 3, 4]); + assert_eq!(a.rotate_elements_left::<5>().to_array(), [2, 3, 4, 1]); + assert_eq!(a.rotate_elements_right::<0>().to_array(), [1, 2, 3, 4]); + assert_eq!(a.rotate_elements_right::<1>().to_array(), [4, 1, 2, 3]); + assert_eq!(a.rotate_elements_right::<2>().to_array(), [3, 4, 1, 2]); + assert_eq!(a.rotate_elements_right::<3>().to_array(), [2, 3, 4, 1]); + assert_eq!(a.rotate_elements_right::<4>().to_array(), [1, 2, 3, 4]); + assert_eq!(a.rotate_elements_right::<5>().to_array(), [4, 1, 2, 3]); } #[test] diff --git a/crates/core_simd/tests/swizzle_dyn.rs b/crates/core_simd/tests/swizzle_dyn.rs index 646cd5f33833..f21a937f01c4 100644 --- a/crates/core_simd/tests/swizzle_dyn.rs +++ b/crates/core_simd/tests/swizzle_dyn.rs @@ -1,6 +1,5 @@ #![feature(portable_simd)] use core::{fmt, ops::RangeInclusive}; -use proptest; use test_helpers::{self, biteq, make_runner, prop_assert_biteq}; fn swizzle_dyn_scalar_ver(values: [u8; N], idxs: [u8; N]) -> [u8; N] { diff --git a/crates/core_simd/tests/to_bytes.rs b/crates/core_simd/tests/to_bytes.rs index be0ee4349c57..66a7981cdc3d 100644 --- a/crates/core_simd/tests/to_bytes.rs +++ b/crates/core_simd/tests/to_bytes.rs @@ -1,14 +1,20 @@ -#![feature(portable_simd, generic_const_exprs, adt_const_params)] -#![allow(incomplete_features)] -#![cfg(feature = "generic_const_exprs")] +#![feature(portable_simd)] -use core_simd::simd::Simd; +use core_simd::simd::{Simd, ToBytes}; #[test] fn byte_convert() { let int = Simd::::from_array([0xdeadbeef, 0x8badf00d]); - let bytes = int.to_ne_bytes(); - assert_eq!(int[0].to_ne_bytes(), bytes[..4]); - assert_eq!(int[1].to_ne_bytes(), bytes[4..]); - assert_eq!(Simd::::from_ne_bytes(bytes), int); + let ne_bytes = int.to_ne_bytes(); + let be_bytes = int.to_be_bytes(); + let le_bytes = int.to_le_bytes(); + assert_eq!(int[0].to_ne_bytes(), ne_bytes[..4]); + assert_eq!(int[1].to_ne_bytes(), ne_bytes[4..]); + assert_eq!(int[0].to_be_bytes(), be_bytes[..4]); + assert_eq!(int[1].to_be_bytes(), be_bytes[4..]); + assert_eq!(int[0].to_le_bytes(), le_bytes[..4]); + assert_eq!(int[1].to_le_bytes(), le_bytes[4..]); + assert_eq!(Simd::::from_ne_bytes(ne_bytes), int); + assert_eq!(Simd::::from_be_bytes(be_bytes), int); + assert_eq!(Simd::::from_le_bytes(le_bytes), int); } diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 4ac60b10c92e..1fef17242ca8 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -1,5 +1,10 @@ #![cfg_attr(feature = "as_crate", no_std)] // We are std! -#![cfg_attr(feature = "as_crate", feature(platform_intrinsics), feature(portable_simd))] +#![cfg_attr( + feature = "as_crate", + feature(platform_intrinsics), + feature(portable_simd), + allow(internal_features) +)] #[cfg(not(feature = "as_crate"))] use core::simd; #[cfg(feature = "as_crate")] @@ -144,7 +149,7 @@ where #[cfg(test)] mod tests { use super::*; - use simd::*; + use simd::prelude::*; #[test] fn everything_works() { diff --git a/crates/test_helpers/Cargo.toml b/crates/test_helpers/Cargo.toml index 1d2bc8b519aa..23dae7c93381 100644 --- a/crates/test_helpers/Cargo.toml +++ b/crates/test_helpers/Cargo.toml @@ -4,10 +4,8 @@ version = "0.1.0" edition = "2021" publish = false -[dependencies.proptest] -version = "0.10" -default-features = false -features = ["alloc"] +[dependencies] +proptest = { version = "0.10", default-features = false, features = ["alloc"] } [features] all_lane_counts = [] diff --git a/crates/test_helpers/src/biteq.rs b/crates/test_helpers/src/biteq.rs index 7d91260d838a..cbc20cda0d62 100644 --- a/crates/test_helpers/src/biteq.rs +++ b/crates/test_helpers/src/biteq.rs @@ -113,6 +113,27 @@ impl core::fmt::Debug for BitEqWrapper<'_, T> { } } +#[doc(hidden)] +pub struct BitEqEitherWrapper<'a, T>(pub &'a T, pub &'a T); + +impl PartialEq> for BitEqWrapper<'_, T> { + fn eq(&self, other: &BitEqEitherWrapper<'_, T>) -> bool { + self.0.biteq(other.0) || self.0.biteq(other.1) + } +} + +impl core::fmt::Debug for BitEqEitherWrapper<'_, T> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + if self.0.biteq(self.1) { + self.0.fmt(f) + } else { + self.0.fmt(f)?; + write!(f, " or ")?; + self.1.fmt(f) + } + } +} + #[macro_export] macro_rules! prop_assert_biteq { { $a:expr, $b:expr $(,)? } => { @@ -122,5 +143,14 @@ macro_rules! prop_assert_biteq { let b = $b; proptest::prop_assert_eq!(BitEqWrapper(&a), BitEqWrapper(&b)); } - } + }; + { $a:expr, $b:expr, $c:expr $(,)? } => { + { + use $crate::biteq::{BitEqWrapper, BitEqEitherWrapper}; + let a = $a; + let b = $b; + let c = $c; + proptest::prop_assert_eq!(BitEqWrapper(&a), BitEqEitherWrapper(&b, &c)); + } + }; } diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs index b26cdc311a21..b80c745aaf2f 100644 --- a/crates/test_helpers/src/lib.rs +++ b/crates/test_helpers/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(stdsimd, powerpc_target_feature)] + pub mod array; #[cfg(target_arch = "wasm32")] @@ -6,6 +8,9 @@ pub mod wasm; #[macro_use] pub mod biteq; +pub mod subnormals; +use subnormals::FlushSubnormals; + /// Specifies the default strategy for testing a type. /// /// This strategy should be what "makes sense" to test. @@ -151,7 +156,6 @@ pub fn test_3< } /// Test a unary vector function against a unary scalar function, applied elementwise. -#[inline(never)] pub fn test_unary_elementwise( fv: &dyn Fn(Vector) -> VectorResult, fs: &dyn Fn(Scalar) -> ScalarResult, @@ -177,6 +181,48 @@ pub fn test_unary_elementwise( + fv: &dyn Fn(Vector) -> VectorResult, + fs: &dyn Fn(Scalar) -> ScalarResult, + check: &dyn Fn([Scalar; LANES]) -> bool, +) where + Scalar: Copy + core::fmt::Debug + DefaultStrategy + FlushSubnormals, + ScalarResult: Copy + biteq::BitEq + core::fmt::Debug + DefaultStrategy + FlushSubnormals, + Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy, + VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, +{ + let flush = |x: Scalar| subnormals::flush(fs(subnormals::flush_in(x))); + test_1(&|x: [Scalar; LANES]| { + proptest::prop_assume!(check(x)); + let result_v: [ScalarResult; LANES] = fv(x.into()).into(); + let result_s: [ScalarResult; LANES] = x + .iter() + .copied() + .map(fs) + .collect::>() + .try_into() + .unwrap(); + let result_sf: [ScalarResult; LANES] = x + .iter() + .copied() + .map(flush) + .collect::>() + .try_into() + .unwrap(); + crate::prop_assert_biteq!(result_v, result_s, result_sf); + Ok(()) + }); +} + /// Test a unary vector function against a unary scalar function, applied elementwise. #[inline(never)] pub fn test_unary_mask_elementwise( @@ -204,7 +250,6 @@ pub fn test_unary_mask_elementwise( } /// Test a binary vector function against a binary scalar function, applied elementwise. -#[inline(never)] pub fn test_binary_elementwise< Scalar1, Scalar2, @@ -241,6 +286,85 @@ pub fn test_binary_elementwise< }); } +/// Test a binary vector function against a binary scalar function, applied elementwise. +/// +/// Where subnormals are flushed, use approximate equality. +pub fn test_binary_elementwise_flush_subnormals< + Scalar1, + Scalar2, + ScalarResult, + Vector1, + Vector2, + VectorResult, + const LANES: usize, +>( + fv: &dyn Fn(Vector1, Vector2) -> VectorResult, + fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult, + check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES]) -> bool, +) where + Scalar1: Copy + core::fmt::Debug + DefaultStrategy + FlushSubnormals, + Scalar2: Copy + core::fmt::Debug + DefaultStrategy + FlushSubnormals, + ScalarResult: Copy + biteq::BitEq + core::fmt::Debug + DefaultStrategy + FlushSubnormals, + Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy, + Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy, + VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, +{ + let flush = |x: Scalar1, y: Scalar2| { + subnormals::flush(fs(subnormals::flush_in(x), subnormals::flush_in(y))) + }; + test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| { + proptest::prop_assume!(check(x, y)); + let result_v: [ScalarResult; LANES] = fv(x.into(), y.into()).into(); + let result_s: [ScalarResult; LANES] = x + .iter() + .copied() + .zip(y.iter().copied()) + .map(|(x, y)| fs(x, y)) + .collect::>() + .try_into() + .unwrap(); + let result_sf: [ScalarResult; LANES] = x + .iter() + .copied() + .zip(y.iter().copied()) + .map(|(x, y)| flush(x, y)) + .collect::>() + .try_into() + .unwrap(); + crate::prop_assert_biteq!(result_v, result_s, result_sf); + Ok(()) + }); +} + +/// Test a unary vector function against a unary scalar function, applied elementwise. +#[inline(never)] +pub fn test_binary_mask_elementwise( + fv: &dyn Fn(Vector1, Vector2) -> Mask, + fs: &dyn Fn(Scalar1, Scalar2) -> bool, + check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES]) -> bool, +) where + Scalar1: Copy + core::fmt::Debug + DefaultStrategy, + Scalar2: Copy + core::fmt::Debug + DefaultStrategy, + Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy, + Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy, + Mask: Into<[bool; LANES]> + From<[bool; LANES]> + Copy, +{ + test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| { + proptest::prop_assume!(check(x, y)); + let result_v: [bool; LANES] = fv(x.into(), y.into()).into(); + let result_s: [bool; LANES] = x + .iter() + .copied() + .zip(y.iter().copied()) + .map(|(x, y)| fs(x, y)) + .collect::>() + .try_into() + .unwrap(); + crate::prop_assert_biteq!(result_v, result_s); + Ok(()) + }); +} + /// Test a binary vector-scalar function against a binary scalar function, applied elementwise. #[inline(never)] pub fn test_binary_scalar_rhs_elementwise< diff --git a/crates/test_helpers/src/subnormals.rs b/crates/test_helpers/src/subnormals.rs new file mode 100644 index 000000000000..ec0f1fb24b93 --- /dev/null +++ b/crates/test_helpers/src/subnormals.rs @@ -0,0 +1,91 @@ +pub trait FlushSubnormals: Sized { + fn flush(self) -> Self { + self + } +} + +impl FlushSubnormals for *const T {} +impl FlushSubnormals for *mut T {} + +macro_rules! impl_float { + { $($ty:ty),* } => { + $( + impl FlushSubnormals for $ty { + fn flush(self) -> Self { + let is_f32 = core::mem::size_of::() == 4; + let ppc_flush = is_f32 && cfg!(all( + any(target_arch = "powerpc", all(target_arch = "powerpc64", target_endian = "big")), + target_feature = "altivec", + not(target_feature = "vsx"), + )); + let arm_flush = is_f32 && cfg!(all(target_arch = "arm", target_feature = "neon")); + let flush = ppc_flush || arm_flush; + if flush && self.is_subnormal() { + <$ty>::copysign(0., self) + } else { + self + } + } + } + )* + } +} + +macro_rules! impl_else { + { $($ty:ty),* } => { + $( + impl FlushSubnormals for $ty {} + )* + } +} + +impl_float! { f32, f64 } +impl_else! { i8, i16, i32, i64, isize, u8, u16, u32, u64, usize } + +/// AltiVec should flush subnormal inputs to zero, but QEMU seems to only flush outputs. +/// https://gitlab.com/qemu-project/qemu/-/issues/1779 +#[cfg(all( + any(target_arch = "powerpc", target_arch = "powerpc64"), + target_feature = "altivec" +))] +fn in_buggy_qemu() -> bool { + use std::sync::OnceLock; + static BUGGY: OnceLock = OnceLock::new(); + + fn add(x: f32, y: f32) -> f32 { + #[cfg(target_arch = "powerpc")] + use core::arch::powerpc::*; + #[cfg(target_arch = "powerpc64")] + use core::arch::powerpc64::*; + + let array: [f32; 4] = + unsafe { core::mem::transmute(vec_add(vec_splats(x), vec_splats(y))) }; + array[0] + } + + *BUGGY.get_or_init(|| add(-1.0857398e-38, 0.).is_sign_negative()) +} + +#[cfg(all( + any(target_arch = "powerpc", target_arch = "powerpc64"), + target_feature = "altivec" +))] +pub fn flush_in(x: T) -> T { + if in_buggy_qemu() { + x + } else { + x.flush() + } +} + +#[cfg(not(all( + any(target_arch = "powerpc", target_arch = "powerpc64"), + target_feature = "altivec" +)))] +pub fn flush_in(x: T) -> T { + x.flush() +} + +pub fn flush(x: T) -> T { + x.flush() +} From c6b3b35badd256fa15eea21e707072d8d18e6eed Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sat, 2 Dec 2023 10:49:21 -0500 Subject: [PATCH 005/285] Remove link to core::arch::x86_64 --- crates/core_simd/src/core_simd_docs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/src/core_simd_docs.md b/crates/core_simd/src/core_simd_docs.md index fa93155ff5ed..bf412e035b51 100644 --- a/crates/core_simd/src/core_simd_docs.md +++ b/crates/core_simd/src/core_simd_docs.md @@ -30,7 +30,7 @@ Instead, they map to a reasonable implementation of the operation for the target Consistency between targets is not compromised to use faster or fewer instructions. In some cases, `std::arch` will provide a faster function that has slightly different behavior than the `std::simd` equivalent. -For example, [`_mm_min_ps`](`core::arch::x86_64::_mm_min_ps`)[^1] can be slightly faster than [`SimdFloat::simd_min`](`num::SimdFloat::simd_min`), but does not conform to the IEEE standard also used by [`f32::min`]. +For example, `_mm_min_ps`[^1] can be slightly faster than [`SimdFloat::simd_min`](`num::SimdFloat::simd_min`), but does not conform to the IEEE standard also used by [`f32::min`]. When necessary, [`Simd`] can be converted to the types provided by `std::arch` to make use of target-specific functions. Many targets simply don't have SIMD, or don't support SIMD for a particular element type. From 08069a52d18185f95df12c5bd50513f25e0929f0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 4 Dec 2023 08:16:03 +0100 Subject: [PATCH 006/285] portable-simd: add missing feature gate --- crates/core_simd/tests/pointers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/tests/pointers.rs b/crates/core_simd/tests/pointers.rs index a90ff928cedc..b9f32d16e01d 100644 --- a/crates/core_simd/tests/pointers.rs +++ b/crates/core_simd/tests/pointers.rs @@ -1,4 +1,4 @@ -#![feature(portable_simd, strict_provenance)] +#![feature(portable_simd, strict_provenance, exposed_provenance)] use core_simd::simd::{ ptr::{SimdConstPtr, SimdMutPtr}, From 98fc771e5b4d568de6a91985553e7b8b85a580c3 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 12 Dec 2023 23:26:45 +0100 Subject: [PATCH 007/285] Fix target_feature config in portable-simd --- crates/core_simd/src/swizzle_dyn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/src/swizzle_dyn.rs b/crates/core_simd/src/swizzle_dyn.rs index bd8a38e350d3..dac013cc98dc 100644 --- a/crates/core_simd/src/swizzle_dyn.rs +++ b/crates/core_simd/src/swizzle_dyn.rs @@ -55,7 +55,7 @@ where 16 => transize(vqtbl1q_u8, self, idxs), #[cfg(all(target_feature = "avx2", not(target_feature = "avx512vbmi")))] 32 => transize_raw(avx2_pshufb, self, idxs), - #[cfg(target_feature = "avx512vl,avx512vbmi")] + #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))] 32 => transize(x86::_mm256_permutexvar_epi8, self, idxs), // Notable absence: avx512bw shuffle // If avx512bw is available, odds of avx512vbmi are good From 7e65a05aa03a7393c52d8bea5014d269f6d147e1 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Tue, 30 Jan 2024 03:40:53 +0000 Subject: [PATCH 008/285] Disable conversions between portable_simd and stdarch on big-endian ARM stdarch no longer provide SIMD on big-endian ARM due to https://github.com/rust-lang/stdarch/issues/1484 --- crates/core_simd/src/vendor/arm.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/core_simd/src/vendor/arm.rs b/crates/core_simd/src/vendor/arm.rs index ff3b69ccf959..ee5c64213736 100644 --- a/crates/core_simd/src/vendor/arm.rs +++ b/crates/core_simd/src/vendor/arm.rs @@ -7,9 +7,12 @@ use core::arch::arm::*; #[cfg(target_arch = "aarch64")] use core::arch::aarch64::*; -#[cfg(any( - target_arch = "aarch64", - all(target_arch = "arm", target_feature = "v7"), +#[cfg(all( + any( + target_arch = "aarch64", + all(target_arch = "arm", target_feature = "v7"), + ), + target_endian = "little" ))] mod neon { use super::*; From f6996612ce2db070ae2a9370c71ac725d08b03e3 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 18 Feb 2024 10:14:03 -0500 Subject: [PATCH 009/285] Merge commit '649110751ef4f27440d7cc711b3e07d11bf02d4a' into sync-portable-simd-2024-02-18 --- crates/core_simd/src/intrinsics.rs | 169 --------------------- crates/core_simd/src/lib.rs | 22 ++- crates/core_simd/src/masks.rs | 41 +++-- crates/core_simd/src/masks/bitmask.rs | 9 +- crates/core_simd/src/masks/full_masks.rs | 27 ++-- crates/core_simd/src/mod.rs | 4 - crates/core_simd/src/ops.rs | 6 +- crates/core_simd/src/ops/unary.rs | 3 +- crates/core_simd/src/select.rs | 3 +- crates/core_simd/src/simd/cmp/eq.rs | 9 +- crates/core_simd/src/simd/cmp/ord.rs | 25 ++- crates/core_simd/src/simd/num/float.rs | 20 +-- crates/core_simd/src/simd/num/int.rs | 26 ++-- crates/core_simd/src/simd/num/uint.rs | 30 ++-- crates/core_simd/src/simd/ptr/const_ptr.rs | 14 +- crates/core_simd/src/simd/ptr/mut_ptr.rs | 14 +- crates/core_simd/src/swizzle.rs | 5 +- crates/core_simd/src/swizzle_dyn.rs | 44 ++---- crates/core_simd/src/vector.rs | 15 +- crates/core_simd/tests/masks.rs | 42 +++++ crates/std_float/src/lib.rs | 38 ++--- crates/test_helpers/src/lib.rs | 6 +- 22 files changed, 218 insertions(+), 354 deletions(-) delete mode 100644 crates/core_simd/src/intrinsics.rs diff --git a/crates/core_simd/src/intrinsics.rs b/crates/core_simd/src/intrinsics.rs deleted file mode 100644 index b27893bc7294..000000000000 --- a/crates/core_simd/src/intrinsics.rs +++ /dev/null @@ -1,169 +0,0 @@ -//! This module contains the LLVM intrinsics bindings that provide the functionality for this -//! crate. -//! -//! The LLVM assembly language is documented here: -//! -//! A quick glossary of jargon that may appear in this module, mostly paraphrasing LLVM's LangRef: -//! - poison: "undefined behavior as a value". specifically, it is like uninit memory (such as padding bytes). it is "safe" to create poison, BUT -//! poison MUST NOT be observed from safe code, as operations on poison return poison, like NaN. unlike NaN, which has defined comparisons, -//! poison is neither true nor false, and LLVM may also convert it to undef (at which point it is both). so, it can't be conditioned on, either. -//! - undef: "a value that is every value". functionally like poison, insofar as Rust is concerned. poison may become this. note: -//! this means that division by poison or undef is like division by zero, which means it inflicts... -//! - "UB": poison and undef cover most of what people call "UB". "UB" means this operation immediately invalidates the program: -//! LLVM is allowed to lower it to `ud2` or other opcodes that may cause an illegal instruction exception, and this is the "good end". -//! The "bad end" is that LLVM may reverse time to the moment control flow diverged on a path towards undefined behavior, -//! and destroy the other branch, potentially deleting safe code and violating Rust's `unsafe` contract. -//! -//! Note that according to LLVM, vectors are not arrays, but they are equivalent when stored to and loaded from memory. -//! -//! 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. -extern "platform-intrinsic" { - /// add/fadd - pub(crate) fn simd_add(x: T, y: T) -> T; - - /// sub/fsub - pub(crate) fn simd_sub(lhs: T, rhs: T) -> T; - - /// mul/fmul - pub(crate) fn simd_mul(x: T, y: T) -> T; - - /// udiv/sdiv/fdiv - /// ints and uints: {s,u}div incur UB if division by zero occurs. - /// ints: sdiv is UB for int::MIN / -1. - /// floats: fdiv is never UB, but may create NaNs or infinities. - pub(crate) fn simd_div(lhs: T, rhs: T) -> T; - - /// urem/srem/frem - /// ints and uints: {s,u}rem incur UB if division by zero occurs. - /// ints: srem is UB for int::MIN / -1. - /// floats: frem is equivalent to libm::fmod in the "default" floating point environment, sans errno. - pub(crate) fn simd_rem(lhs: T, rhs: T) -> T; - - /// shl - /// for (u)ints. poison if rhs >= lhs::BITS - pub(crate) fn simd_shl(lhs: T, rhs: T) -> T; - - /// ints: ashr - /// uints: lshr - /// poison if rhs >= lhs::BITS - pub(crate) fn simd_shr(lhs: T, rhs: T) -> T; - - /// and - pub(crate) fn simd_and(x: T, y: T) -> T; - - /// or - pub(crate) fn simd_or(x: T, y: T) -> T; - - /// xor - pub(crate) fn simd_xor(x: T, y: T) -> T; - - /// fptoui/fptosi/uitofp/sitofp - /// casting floats to integers is truncating, so it is safe to convert values like e.g. 1.5 - /// but the truncated value must fit in the target type or the result is poison. - /// use `simd_as` instead for a cast that performs a saturating conversion. - pub(crate) fn simd_cast(x: T) -> U; - /// follows Rust's `T as U` semantics, including saturating float casts - /// which amounts to the same as `simd_cast` for many cases - pub(crate) fn simd_as(x: T) -> U; - - /// neg/fneg - /// ints: ultimately becomes a call to cg_ssa's BuilderMethods::neg. cg_llvm equates this to `simd_sub(Simd::splat(0), x)`. - /// floats: LLVM's fneg, which changes the floating point sign bit. Some arches have instructions for it. - /// Rust panics for Neg::neg(int::MIN) due to overflow, but it is not UB in LLVM without `nsw`. - pub(crate) fn simd_neg(x: T) -> T; - - /// fabs - pub(crate) fn simd_fabs(x: T) -> T; - - // minnum/maxnum - pub(crate) fn simd_fmin(x: T, y: T) -> T; - pub(crate) fn simd_fmax(x: T, y: T) -> T; - - // these return Simd with the same BITS size as the inputs - pub(crate) fn simd_eq(x: T, y: T) -> U; - pub(crate) fn simd_ne(x: T, y: T) -> U; - pub(crate) fn simd_lt(x: T, y: T) -> U; - pub(crate) fn simd_le(x: T, y: T) -> U; - pub(crate) fn simd_gt(x: T, y: T) -> U; - pub(crate) fn simd_ge(x: T, y: T) -> U; - - // shufflevector - // idx: LLVM calls it a "shuffle mask vector constant", a vector of i32s - pub(crate) fn simd_shuffle(x: T, y: T, idx: U) -> V; - - /// llvm.masked.gather - /// like a loop of pointer reads - /// val: vector of values to select if a lane is masked - /// ptr: vector of pointers to read from - /// mask: a "wide" mask of integers, selects as if simd_select(mask, read(ptr), val) - /// note, the LLVM intrinsic accepts a mask vector of `` - /// FIXME: review this if/when we fix up our mask story in general? - pub(crate) fn simd_gather(val: T, ptr: U, mask: V) -> T; - /// llvm.masked.scatter - /// like gather, but more spicy, as it writes instead of reads - pub(crate) fn simd_scatter(val: T, ptr: U, mask: V); - - // {s,u}add.sat - pub(crate) fn simd_saturating_add(x: T, y: T) -> T; - - // {s,u}sub.sat - pub(crate) fn simd_saturating_sub(lhs: T, rhs: T) -> T; - - // reductions - // llvm.vector.reduce.{add,fadd} - pub(crate) fn simd_reduce_add_ordered(x: T, y: U) -> U; - // llvm.vector.reduce.{mul,fmul} - pub(crate) fn simd_reduce_mul_ordered(x: T, y: U) -> U; - #[allow(unused)] - pub(crate) fn simd_reduce_all(x: T) -> bool; - #[allow(unused)] - pub(crate) fn simd_reduce_any(x: T) -> bool; - pub(crate) fn simd_reduce_max(x: T) -> U; - pub(crate) fn simd_reduce_min(x: T) -> U; - pub(crate) fn simd_reduce_and(x: T) -> U; - pub(crate) fn simd_reduce_or(x: T) -> U; - 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; - - // select - // first argument is a vector of integers, -1 (all bits 1) is "true" - // logically equivalent to (yes & m) | (no & (m^-1), - // but you can use it on floats. - pub(crate) fn simd_select(m: M, yes: T, no: T) -> T; - #[allow(unused)] - pub(crate) fn simd_select_bitmask(m: M, yes: T, no: T) -> T; - - /// getelementptr (without inbounds) - /// equivalent to wrapping_offset - pub(crate) fn simd_arith_offset(ptr: T, offset: U) -> T; - - /// equivalent to `T as U` semantics, specifically for pointers - pub(crate) fn simd_cast_ptr(ptr: T) -> U; - - /// expose a pointer as an address - pub(crate) fn simd_expose_addr(ptr: T) -> U; - - /// convert an exposed address back to a pointer - pub(crate) fn simd_from_exposed_addr(addr: T) -> U; - - // Integer operations - pub(crate) fn simd_bswap(x: T) -> T; - pub(crate) fn simd_bitreverse(x: T) -> T; - pub(crate) fn simd_ctlz(x: T) -> T; - pub(crate) fn simd_cttz(x: T) -> T; -} diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 64ba9705ef52..a25723e11cef 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -1,20 +1,38 @@ #![no_std] #![feature( + const_intrinsic_copy, const_refs_to_cell, const_maybe_uninit_as_mut_ptr, const_mut_refs, convert_float_to_int, + core_intrinsics, decl_macro, inline_const, intra_doc_pointers, - platform_intrinsics, repr_simd, simd_ffi, staged_api, - stdsimd, strict_provenance, ptr_metadata )] +#![cfg_attr( + all( + any(target_arch = "aarch64", target_arch = "arm",), + any( + all(target_feature = "v6", not(target_feature = "mclass")), + all(target_feature = "mclass", target_feature = "dsp"), + ) + ), + feature(stdarch_arm_dsp) +)] +#![cfg_attr( + all(target_arch = "arm", target_feature = "v7"), + feature(stdarch_arm_neon_intrinsics) +)] +#![cfg_attr( + any(target_arch = "powerpc", target_arch = "powerpc64"), + feature(stdarch_powerpc) +)] #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really #![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)] #![allow(internal_features)] diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index 0623d2bf3d12..e480c25a51e6 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -12,9 +12,7 @@ )] mod mask_impl; -use crate::simd::{ - cmp::SimdPartialEq, intrinsics, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount, -}; +use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; use core::cmp::Ordering; use core::{fmt, mem}; @@ -35,7 +33,7 @@ mod sealed { fn eq(self, other: Self) -> bool; - fn as_usize(self) -> usize; + fn to_usize(self) -> usize; type Unsigned: SimdElement; @@ -60,14 +58,23 @@ macro_rules! impl_element { where LaneCount: SupportedLaneCount, { - (value.simd_eq(Simd::splat(0 as _)) | value.simd_eq(Simd::splat(-1 as _))).all() + // We can't use `Simd` directly, because `Simd`'s functions call this function and + // we will end up with an infinite loop. + // Safety: `value` is an integer vector + unsafe { + use core::intrinsics::simd; + let falses: Simd = simd::simd_eq(value, Simd::splat(0 as _)); + let trues: Simd = simd::simd_eq(value, Simd::splat(-1 as _)); + let valid: Simd = simd::simd_or(falses, trues); + simd::simd_reduce_all(valid) + } } #[inline] fn eq(self, other: Self) -> bool { self == other } #[inline] - fn as_usize(self) -> usize { + fn to_usize(self) -> usize { self as usize } @@ -141,8 +148,9 @@ where // but these are "dependently-sized" types, so copy elision it is! unsafe { let bytes: [u8; N] = mem::transmute_copy(&array); - let bools: Simd = intrinsics::simd_ne(Simd::from_array(bytes), Simd::splat(0u8)); - Mask::from_int_unchecked(intrinsics::simd_cast(bools)) + let bools: Simd = + core::intrinsics::simd::simd_ne(Simd::from_array(bytes), Simd::splat(0u8)); + Mask::from_int_unchecked(core::intrinsics::simd::simd_cast(bools)) } } @@ -160,7 +168,7 @@ where // This would be hypothetically valid as an "in-place" transmute, // but these are "dependently-sized" types, so copy elision it is! unsafe { - let mut bytes: Simd = intrinsics::simd_cast(self.to_int()); + let mut bytes: Simd = core::intrinsics::simd::simd_cast(self.to_int()); bytes &= Simd::splat(1i8); mem::transmute_copy(&bytes) } @@ -175,7 +183,10 @@ where #[must_use = "method returns a new mask and does not mutate the original value"] pub unsafe fn from_int_unchecked(value: Simd) -> Self { // Safety: the caller must confirm this invariant - unsafe { Self(mask_impl::Mask::from_int_unchecked(value)) } + unsafe { + core::intrinsics::assume(::valid(value)); + Self(mask_impl::Mask::from_int_unchecked(value)) + } } /// Converts a vector of integers to a mask, where 0 represents `false` and -1 @@ -374,15 +385,17 @@ where ); // Safety: the input and output are integer vectors - let index: Simd = unsafe { intrinsics::simd_cast(index) }; + let index: Simd = unsafe { core::intrinsics::simd::simd_cast(index) }; let masked_index = self.select(index, Self::splat(true).to_int()); // Safety: the input and output are integer vectors - let masked_index: Simd = unsafe { intrinsics::simd_cast(masked_index) }; + let masked_index: Simd = + unsafe { core::intrinsics::simd::simd_cast(masked_index) }; // Safety: the input is an integer vector - let min_index: T::Unsigned = unsafe { intrinsics::simd_reduce_min(masked_index) }; + let min_index: T::Unsigned = + unsafe { core::intrinsics::simd::simd_reduce_min(masked_index) }; // Safety: the return value is the unsigned version of T let min_index: T = unsafe { core::mem::transmute_copy(&min_index) }; @@ -390,7 +403,7 @@ where if min_index.eq(T::TRUE) { None } else { - Some(min_index.as_usize()) + Some(min_index.to_usize()) } } } diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index 6ddff07fea25..96c553426ee7 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -1,6 +1,5 @@ #![allow(unused_imports)] use super::MaskElement; -use crate::simd::intrinsics; use crate::simd::{LaneCount, Simd, SupportedLaneCount}; use core::marker::PhantomData; @@ -109,14 +108,18 @@ where #[must_use = "method returns a new vector and does not mutate the original value"] pub fn to_int(self) -> Simd { unsafe { - intrinsics::simd_select_bitmask(self.0, Simd::splat(T::TRUE), Simd::splat(T::FALSE)) + core::intrinsics::simd::simd_select_bitmask( + self.0, + Simd::splat(T::TRUE), + Simd::splat(T::FALSE), + ) } } #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub unsafe fn from_int_unchecked(value: Simd) -> Self { - unsafe { Self(intrinsics::simd_bitmask(value), PhantomData) } + unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) } } #[inline] diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index 63964f455e05..87f031a9f367 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -1,6 +1,5 @@ //! Masks that take up full SIMD vector registers. -use crate::simd::intrinsics; use crate::simd::{LaneCount, MaskElement, Simd, SupportedLaneCount}; #[repr(transparent)] @@ -138,7 +137,7 @@ where U: MaskElement, { // Safety: masks are simply integer vectors of 0 and -1, and we can cast the element type. - unsafe { Mask(intrinsics::simd_cast(self.0)) } + unsafe { Mask(core::intrinsics::simd::simd_cast(self.0)) } } #[inline] @@ -150,13 +149,16 @@ where unsafe { // Compute the bitmask let mut bytes: as SupportedLaneCount>::BitMask = - intrinsics::simd_bitmask(self.0); + core::intrinsics::simd::simd_bitmask(self.0); // LLVM assumes bit order should match endianness if cfg!(target_endian = "big") { for x in bytes.as_mut() { *x = x.reverse_bits() } + if N % 8 > 0 { + bytes.as_mut()[N / 8] >>= 8 - N % 8; + } } bitmask.as_mut_array()[..bytes.as_ref().len()].copy_from_slice(bytes.as_ref()); @@ -180,10 +182,13 @@ where for x in bytes.as_mut() { *x = x.reverse_bits(); } + if N % 8 > 0 { + bytes.as_mut()[N / 8] >>= 8 - N % 8; + } } // Compute the regular mask - Self::from_int_unchecked(intrinsics::simd_select_bitmask( + Self::from_int_unchecked(core::intrinsics::simd::simd_select_bitmask( bytes, Self::splat(true).to_int(), Self::splat(false).to_int(), @@ -199,7 +204,7 @@ where let resized = self.to_int().resize::(T::FALSE); // Safety: `resized` is an integer vector with length M, which must match T - let bitmask: U = unsafe { intrinsics::simd_bitmask(resized) }; + let bitmask: U = unsafe { core::intrinsics::simd::simd_bitmask(resized) }; // LLVM assumes bit order should match endianness if cfg!(target_endian = "big") { @@ -223,7 +228,7 @@ where // SAFETY: `mask` is the correct bitmask type for a u64 bitmask let mask: Simd = unsafe { - intrinsics::simd_select_bitmask( + core::intrinsics::simd::simd_select_bitmask( bitmask, Simd::::splat(T::TRUE), Simd::::splat(T::FALSE), @@ -274,14 +279,14 @@ where #[must_use = "method returns a new bool and does not mutate the original value"] pub fn any(self) -> bool { // Safety: use `self` as an integer vector - unsafe { intrinsics::simd_reduce_any(self.to_int()) } + unsafe { core::intrinsics::simd::simd_reduce_any(self.to_int()) } } #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] pub fn all(self) -> bool { // Safety: use `self` as an integer vector - unsafe { intrinsics::simd_reduce_all(self.to_int()) } + unsafe { core::intrinsics::simd::simd_reduce_all(self.to_int()) } } } @@ -306,7 +311,7 @@ where #[must_use = "method returns a new mask and does not mutate the original value"] fn bitand(self, rhs: Self) -> Self { // Safety: `self` is an integer vector - unsafe { Self(intrinsics::simd_and(self.0, rhs.0)) } + unsafe { Self(core::intrinsics::simd::simd_and(self.0, rhs.0)) } } } @@ -320,7 +325,7 @@ where #[must_use = "method returns a new mask and does not mutate the original value"] fn bitor(self, rhs: Self) -> Self { // Safety: `self` is an integer vector - unsafe { Self(intrinsics::simd_or(self.0, rhs.0)) } + unsafe { Self(core::intrinsics::simd::simd_or(self.0, rhs.0)) } } } @@ -334,7 +339,7 @@ where #[must_use = "method returns a new mask and does not mutate the original value"] fn bitxor(self, rhs: Self) -> Self { // Safety: `self` is an integer vector - unsafe { Self(intrinsics::simd_xor(self.0, rhs.0)) } + unsafe { Self(core::intrinsics::simd::simd_xor(self.0, rhs.0)) } } } diff --git a/crates/core_simd/src/mod.rs b/crates/core_simd/src/mod.rs index fd016f1c6f7a..45b1a0f97514 100644 --- a/crates/core_simd/src/mod.rs +++ b/crates/core_simd/src/mod.rs @@ -1,8 +1,6 @@ #[macro_use] mod swizzle; -pub(crate) mod intrinsics; - mod alias; mod cast; mod fmt; @@ -27,8 +25,6 @@ pub mod simd { pub mod cmp; - pub(crate) use crate::core_simd::intrinsics; - pub use crate::core_simd::alias::*; pub use crate::core_simd::cast::*; pub use crate::core_simd::lane_count::{LaneCount, SupportedLaneCount}; diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index 8a1b083f0398..d8e10eeaa1a2 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -37,7 +37,7 @@ where macro_rules! unsafe_base { ($lhs:ident, $rhs:ident, {$simd_call:ident}, $($_:tt)*) => { // Safety: $lhs and $rhs are vectors - unsafe { $crate::simd::intrinsics::$simd_call($lhs, $rhs) } + unsafe { core::intrinsics::simd::$simd_call($lhs, $rhs) } }; } @@ -55,7 +55,7 @@ macro_rules! wrap_bitshift { #[allow(clippy::suspicious_arithmetic_impl)] // Safety: $lhs and the bitand result are vectors unsafe { - $crate::simd::intrinsics::$simd_call( + core::intrinsics::simd::$simd_call( $lhs, $rhs.bitand(Simd::splat(<$int>::BITS as $int - 1)), ) @@ -97,7 +97,7 @@ macro_rules! int_divrem_guard { $rhs }; // Safety: $lhs and rhs are vectors - unsafe { $crate::simd::intrinsics::$simd_call($lhs, rhs) } + unsafe { core::intrinsics::simd::$simd_call($lhs, rhs) } } }; } diff --git a/crates/core_simd/src/ops/unary.rs b/crates/core_simd/src/ops/unary.rs index a651aa73e952..bdae96332a3a 100644 --- a/crates/core_simd/src/ops/unary.rs +++ b/crates/core_simd/src/ops/unary.rs @@ -1,4 +1,3 @@ -use crate::simd::intrinsics; use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; use core::ops::{Neg, Not}; // unary ops @@ -15,7 +14,7 @@ macro_rules! neg { #[must_use = "operator returns a new vector without mutating the input"] fn neg(self) -> Self::Output { // Safety: `self` is a signed vector - unsafe { intrinsics::simd_neg(self) } + unsafe { core::intrinsics::simd::simd_neg(self) } } })* } diff --git a/crates/core_simd/src/select.rs b/crates/core_simd/src/select.rs index cdcf8eeec815..f33aa261a928 100644 --- a/crates/core_simd/src/select.rs +++ b/crates/core_simd/src/select.rs @@ -1,4 +1,3 @@ -use crate::simd::intrinsics; use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount}; impl Mask @@ -29,7 +28,7 @@ where { // Safety: The mask has been cast to a vector of integers, // and the operands to select between are vectors of the same type and length. - unsafe { intrinsics::simd_select(self.to_int(), true_values, false_values) } + unsafe { core::intrinsics::simd::simd_select(self.to_int(), true_values, false_values) } } /// Choose elements from two masks. diff --git a/crates/core_simd/src/simd/cmp/eq.rs b/crates/core_simd/src/simd/cmp/eq.rs index f132fa2cc0ca..5b4615ce51d7 100644 --- a/crates/core_simd/src/simd/cmp/eq.rs +++ b/crates/core_simd/src/simd/cmp/eq.rs @@ -1,5 +1,4 @@ use crate::simd::{ - intrinsics, ptr::{SimdConstPtr, SimdMutPtr}, LaneCount, Mask, Simd, SimdElement, SupportedLaneCount, }; @@ -31,14 +30,14 @@ macro_rules! impl_number { fn simd_eq(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(intrinsics::simd_eq(self, other)) } + unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_eq(self, other)) } } #[inline] fn simd_ne(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(intrinsics::simd_ne(self, other)) } + unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ne(self, other)) } } } )* @@ -60,14 +59,14 @@ macro_rules! impl_mask { fn simd_eq(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(intrinsics::simd_eq(self.to_int(), other.to_int())) } + unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_eq(self.to_int(), other.to_int())) } } #[inline] fn simd_ne(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(intrinsics::simd_ne(self.to_int(), other.to_int())) } + unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ne(self.to_int(), other.to_int())) } } } )* diff --git a/crates/core_simd/src/simd/cmp/ord.rs b/crates/core_simd/src/simd/cmp/ord.rs index 4e9d49ea2211..899f00a83164 100644 --- a/crates/core_simd/src/simd/cmp/ord.rs +++ b/crates/core_simd/src/simd/cmp/ord.rs @@ -1,6 +1,5 @@ use crate::simd::{ cmp::SimdPartialEq, - intrinsics, ptr::{SimdConstPtr, SimdMutPtr}, LaneCount, Mask, Simd, SupportedLaneCount, }; @@ -57,28 +56,28 @@ macro_rules! impl_integer { fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(intrinsics::simd_lt(self, other)) } + unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_lt(self, other)) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(intrinsics::simd_le(self, other)) } + unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_le(self, other)) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(intrinsics::simd_gt(self, other)) } + unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_gt(self, other)) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(intrinsics::simd_ge(self, other)) } + unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ge(self, other)) } } } @@ -123,28 +122,28 @@ macro_rules! impl_float { fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(intrinsics::simd_lt(self, other)) } + unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_lt(self, other)) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(intrinsics::simd_le(self, other)) } + unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_le(self, other)) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(intrinsics::simd_gt(self, other)) } + unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_gt(self, other)) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(intrinsics::simd_ge(self, other)) } + unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ge(self, other)) } } } )* @@ -164,28 +163,28 @@ macro_rules! impl_mask { fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(intrinsics::simd_lt(self.to_int(), other.to_int())) } + unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_lt(self.to_int(), other.to_int())) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(intrinsics::simd_le(self.to_int(), other.to_int())) } + unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_le(self.to_int(), other.to_int())) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(intrinsics::simd_gt(self.to_int(), other.to_int())) } + unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_gt(self.to_int(), other.to_int())) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(intrinsics::simd_ge(self.to_int(), other.to_int())) } + unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ge(self.to_int(), other.to_int())) } } } diff --git a/crates/core_simd/src/simd/num/float.rs b/crates/core_simd/src/simd/num/float.rs index fc0b99e87a68..59e43851ea8d 100644 --- a/crates/core_simd/src/simd/num/float.rs +++ b/crates/core_simd/src/simd/num/float.rs @@ -1,7 +1,7 @@ use super::sealed::Sealed; use crate::simd::{ cmp::{SimdPartialEq, SimdPartialOrd}, - intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, + LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, }; /// Operations on SIMD vectors of floats. @@ -259,7 +259,7 @@ macro_rules! impl_trait { fn cast(self) -> Self::Cast { // Safety: supported types are guaranteed by SimdCast - unsafe { intrinsics::simd_as(self) } + unsafe { core::intrinsics::simd::simd_as(self) } } #[inline] @@ -269,7 +269,7 @@ macro_rules! impl_trait { Self::Scalar: core::convert::FloatToInt, { // Safety: supported types are guaranteed by SimdCast, the caller is responsible for the extra invariants - unsafe { intrinsics::simd_cast(self) } + unsafe { core::intrinsics::simd::simd_cast(self) } } #[inline] @@ -289,7 +289,7 @@ macro_rules! impl_trait { #[inline] fn abs(self) -> Self { // Safety: `self` is a float vector - unsafe { intrinsics::simd_fabs(self) } + unsafe { core::intrinsics::simd::simd_fabs(self) } } #[inline] @@ -363,13 +363,13 @@ macro_rules! impl_trait { #[inline] fn simd_min(self, other: Self) -> Self { // Safety: `self` and `other` are float vectors - unsafe { intrinsics::simd_fmin(self, other) } + unsafe { core::intrinsics::simd::simd_fmin(self, other) } } #[inline] fn simd_max(self, other: Self) -> Self { // Safety: `self` and `other` are floating point vectors - unsafe { intrinsics::simd_fmax(self, other) } + unsafe { core::intrinsics::simd::simd_fmax(self, other) } } #[inline] @@ -391,7 +391,7 @@ macro_rules! impl_trait { self.as_array().iter().sum() } else { // Safety: `self` is a float vector - unsafe { intrinsics::simd_reduce_add_ordered(self, 0.) } + unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, 0.) } } } @@ -402,20 +402,20 @@ macro_rules! impl_trait { self.as_array().iter().product() } else { // Safety: `self` is a float vector - unsafe { intrinsics::simd_reduce_mul_ordered(self, 1.) } + unsafe { core::intrinsics::simd::simd_reduce_mul_ordered(self, 1.) } } } #[inline] fn reduce_max(self) -> Self::Scalar { // Safety: `self` is a float vector - unsafe { intrinsics::simd_reduce_max(self) } + unsafe { core::intrinsics::simd::simd_reduce_max(self) } } #[inline] fn reduce_min(self) -> Self::Scalar { // Safety: `self` is a float vector - unsafe { intrinsics::simd_reduce_min(self) } + unsafe { core::intrinsics::simd::simd_reduce_min(self) } } } )* diff --git a/crates/core_simd/src/simd/num/int.rs b/crates/core_simd/src/simd/num/int.rs index 1f1aa2727829..d7598d9ceaf9 100644 --- a/crates/core_simd/src/simd/num/int.rs +++ b/crates/core_simd/src/simd/num/int.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - cmp::SimdPartialOrd, intrinsics, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, + cmp::SimdPartialOrd, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, }; @@ -237,19 +237,19 @@ macro_rules! impl_trait { #[inline] fn cast(self) -> Self::Cast { // Safety: supported types are guaranteed by SimdCast - unsafe { intrinsics::simd_as(self) } + unsafe { core::intrinsics::simd::simd_as(self) } } #[inline] fn saturating_add(self, second: Self) -> Self { // Safety: `self` is a vector - unsafe { intrinsics::simd_saturating_add(self, second) } + unsafe { core::intrinsics::simd::simd_saturating_add(self, second) } } #[inline] fn saturating_sub(self, second: Self) -> Self { // Safety: `self` is a vector - unsafe { intrinsics::simd_saturating_sub(self, second) } + unsafe { core::intrinsics::simd::simd_saturating_sub(self, second) } } #[inline] @@ -293,55 +293,55 @@ macro_rules! impl_trait { #[inline] fn reduce_sum(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_add_ordered(self, 0) } + unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, 0) } } #[inline] fn reduce_product(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_mul_ordered(self, 1) } + unsafe { core::intrinsics::simd::simd_reduce_mul_ordered(self, 1) } } #[inline] fn reduce_max(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_max(self) } + unsafe { core::intrinsics::simd::simd_reduce_max(self) } } #[inline] fn reduce_min(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_min(self) } + unsafe { core::intrinsics::simd::simd_reduce_min(self) } } #[inline] fn reduce_and(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_and(self) } + unsafe { core::intrinsics::simd::simd_reduce_and(self) } } #[inline] fn reduce_or(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_or(self) } + unsafe { core::intrinsics::simd::simd_reduce_or(self) } } #[inline] fn reduce_xor(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_xor(self) } + unsafe { core::intrinsics::simd::simd_reduce_xor(self) } } #[inline] fn swap_bytes(self) -> Self { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_bswap(self) } + unsafe { core::intrinsics::simd::simd_bswap(self) } } #[inline] fn reverse_bits(self) -> Self { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_bitreverse(self) } + unsafe { core::intrinsics::simd::simd_bitreverse(self) } } #[inline] diff --git a/crates/core_simd/src/simd/num/uint.rs b/crates/core_simd/src/simd/num/uint.rs index c955ee8fe8bd..53dd97f501c6 100644 --- a/crates/core_simd/src/simd/num/uint.rs +++ b/crates/core_simd/src/simd/num/uint.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{intrinsics, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; +use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; /// Operations on SIMD vectors of unsigned integers. pub trait SimdUint: Copy + Sealed { @@ -117,7 +117,7 @@ macro_rules! impl_trait { #[inline] fn cast(self) -> Self::Cast { // Safety: supported types are guaranteed by SimdCast - unsafe { intrinsics::simd_as(self) } + unsafe { core::intrinsics::simd::simd_as(self) } } #[inline] @@ -129,79 +129,79 @@ macro_rules! impl_trait { #[inline] fn saturating_add(self, second: Self) -> Self { // Safety: `self` is a vector - unsafe { intrinsics::simd_saturating_add(self, second) } + unsafe { core::intrinsics::simd::simd_saturating_add(self, second) } } #[inline] fn saturating_sub(self, second: Self) -> Self { // Safety: `self` is a vector - unsafe { intrinsics::simd_saturating_sub(self, second) } + unsafe { core::intrinsics::simd::simd_saturating_sub(self, second) } } #[inline] fn reduce_sum(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_add_ordered(self, 0) } + unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, 0) } } #[inline] fn reduce_product(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_mul_ordered(self, 1) } + unsafe { core::intrinsics::simd::simd_reduce_mul_ordered(self, 1) } } #[inline] fn reduce_max(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_max(self) } + unsafe { core::intrinsics::simd::simd_reduce_max(self) } } #[inline] fn reduce_min(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_min(self) } + unsafe { core::intrinsics::simd::simd_reduce_min(self) } } #[inline] fn reduce_and(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_and(self) } + unsafe { core::intrinsics::simd::simd_reduce_and(self) } } #[inline] fn reduce_or(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_or(self) } + unsafe { core::intrinsics::simd::simd_reduce_or(self) } } #[inline] fn reduce_xor(self) -> Self::Scalar { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_reduce_xor(self) } + unsafe { core::intrinsics::simd::simd_reduce_xor(self) } } #[inline] fn swap_bytes(self) -> Self { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_bswap(self) } + unsafe { core::intrinsics::simd::simd_bswap(self) } } #[inline] fn reverse_bits(self) -> Self { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_bitreverse(self) } + unsafe { core::intrinsics::simd::simd_bitreverse(self) } } #[inline] fn leading_zeros(self) -> Self { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_ctlz(self) } + unsafe { core::intrinsics::simd::simd_ctlz(self) } } #[inline] fn trailing_zeros(self) -> Self { // Safety: `self` is an integer vector - unsafe { intrinsics::simd_cttz(self) } + unsafe { core::intrinsics::simd::simd_cttz(self) } } #[inline] diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index 97fe3fb600df..e217d1c8c87c 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -1,7 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{ - cmp::SimdPartialEq, intrinsics, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount, -}; +use crate::simd::{cmp::SimdPartialEq, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount}; /// Operations on SIMD vectors of constant pointers. pub trait SimdConstPtr: Copy + Sealed { @@ -103,13 +101,13 @@ where assert_eq!(size_of::<::Metadata>(), 0); // Safety: pointers can be cast - unsafe { intrinsics::simd_cast_ptr(self) } + unsafe { core::intrinsics::simd::simd_cast_ptr(self) } } #[inline] fn cast_mut(self) -> Self::MutPtr { // Safety: pointers can be cast - unsafe { intrinsics::simd_cast_ptr(self) } + unsafe { core::intrinsics::simd::simd_cast_ptr(self) } } #[inline] @@ -135,19 +133,19 @@ where #[inline] fn expose_addr(self) -> Self::Usize { // Safety: `self` is a pointer vector - unsafe { intrinsics::simd_expose_addr(self) } + unsafe { core::intrinsics::simd::simd_expose_addr(self) } } #[inline] fn from_exposed_addr(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector - unsafe { intrinsics::simd_from_exposed_addr(addr) } + unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } } #[inline] fn wrapping_offset(self, count: Self::Isize) -> Self { // Safety: simd_arith_offset takes a vector of pointers and a vector of offsets - unsafe { intrinsics::simd_arith_offset(self, count) } + unsafe { core::intrinsics::simd::simd_arith_offset(self, count) } } #[inline] diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index e35633d0433d..5cb27af4fdeb 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -1,7 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{ - cmp::SimdPartialEq, intrinsics, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount, -}; +use crate::simd::{cmp::SimdPartialEq, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount}; /// Operations on SIMD vectors of mutable pointers. pub trait SimdMutPtr: Copy + Sealed { @@ -100,13 +98,13 @@ where assert_eq!(size_of::<::Metadata>(), 0); // Safety: pointers can be cast - unsafe { intrinsics::simd_cast_ptr(self) } + unsafe { core::intrinsics::simd::simd_cast_ptr(self) } } #[inline] fn cast_const(self) -> Self::ConstPtr { // Safety: pointers can be cast - unsafe { intrinsics::simd_cast_ptr(self) } + unsafe { core::intrinsics::simd::simd_cast_ptr(self) } } #[inline] @@ -132,19 +130,19 @@ where #[inline] fn expose_addr(self) -> Self::Usize { // Safety: `self` is a pointer vector - unsafe { intrinsics::simd_expose_addr(self) } + unsafe { core::intrinsics::simd::simd_expose_addr(self) } } #[inline] fn from_exposed_addr(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector - unsafe { intrinsics::simd_from_exposed_addr(addr) } + unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } } #[inline] fn wrapping_offset(self, count: Self::Isize) -> Self { // Safety: simd_arith_offset takes a vector of pointers and a vector of offsets - unsafe { intrinsics::simd_arith_offset(self, count) } + unsafe { core::intrinsics::simd::simd_arith_offset(self, count) } } #[inline] diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index ec8548d55745..71110bb28201 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -1,4 +1,3 @@ -use crate::simd::intrinsics; use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount}; /// Constructs a new SIMD vector by copying elements from selected elements in other vectors. @@ -88,7 +87,7 @@ pub trait Swizzle { { // Safety: `vector` is a vector, and the index is a const array of u32. unsafe { - intrinsics::simd_shuffle( + core::intrinsics::simd::simd_shuffle( vector, vector, const { @@ -124,7 +123,7 @@ pub trait Swizzle { { // Safety: `first` and `second` are vectors, and the index is a const array of u32. unsafe { - intrinsics::simd_shuffle( + core::intrinsics::simd::simd_shuffle( first, second, const { diff --git a/crates/core_simd/src/swizzle_dyn.rs b/crates/core_simd/src/swizzle_dyn.rs index dac013cc98dc..ae9ff6894b0a 100644 --- a/crates/core_simd/src/swizzle_dyn.rs +++ b/crates/core_simd/src/swizzle_dyn.rs @@ -44,7 +44,7 @@ where ))] 8 => transize(vtbl1_u8, self, idxs), #[cfg(target_feature = "ssse3")] - 16 => transize(x86::_mm_shuffle_epi8, self, idxs), + 16 => transize(x86::_mm_shuffle_epi8, self, zeroing_idxs(idxs)), #[cfg(target_feature = "simd128")] 16 => transize(wasm::i8x16_swizzle, self, idxs), #[cfg(all( @@ -54,9 +54,9 @@ where ))] 16 => transize(vqtbl1q_u8, self, idxs), #[cfg(all(target_feature = "avx2", not(target_feature = "avx512vbmi")))] - 32 => transize_raw(avx2_pshufb, self, idxs), + 32 => transize(avx2_pshufb, self, idxs), #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))] - 32 => transize(x86::_mm256_permutexvar_epi8, self, idxs), + 32 => transize(x86::_mm256_permutexvar_epi8, zeroing_idxs(idxs), self), // Notable absence: avx512bw shuffle // If avx512bw is available, odds of avx512vbmi are good // FIXME: initial AVX512VBMI variant didn't actually pass muster @@ -129,45 +129,25 @@ unsafe fn avx2_pshufb(bytes: Simd, idxs: Simd) -> Simd { #[inline(always)] unsafe fn transize( f: unsafe fn(T, T) -> T, - bytes: Simd, - idxs: Simd, + a: Simd, + b: Simd, ) -> Simd where LaneCount: SupportedLaneCount, { - let idxs = zeroing_idxs(idxs); // SAFETY: Same obligation to use this function as to use mem::transmute_copy. - unsafe { mem::transmute_copy(&f(mem::transmute_copy(&bytes), mem::transmute_copy(&idxs))) } + unsafe { mem::transmute_copy(&f(mem::transmute_copy(&a), mem::transmute_copy(&b))) } } -/// Make indices that yield 0 for this architecture +/// Make indices that yield 0 for x86 +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[allow(unused)] #[inline(always)] fn zeroing_idxs(idxs: Simd) -> Simd where LaneCount: SupportedLaneCount, { - // On x86, make sure the top bit is set. - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - let idxs = { - use crate::simd::cmp::SimdPartialOrd; - idxs.simd_lt(Simd::splat(N as u8)) - .select(idxs, Simd::splat(u8::MAX)) - }; - // Simply do nothing on most architectures. - idxs -} - -/// As transize but no implicit call to `zeroing_idxs`. -#[allow(dead_code)] -#[inline(always)] -unsafe fn transize_raw( - f: unsafe fn(T, T) -> T, - bytes: Simd, - idxs: Simd, -) -> Simd -where - LaneCount: SupportedLaneCount, -{ - // SAFETY: Same obligation to use this function as to use mem::transmute_copy. - unsafe { mem::transmute_copy(&f(mem::transmute_copy(&bytes), mem::transmute_copy(&idxs))) } + use crate::simd::cmp::SimdPartialOrd; + idxs.simd_lt(Simd::splat(N as u8)) + .select(idxs, Simd::splat(u8::MAX)) } diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 105c06741c58..9e97a3161bb2 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -1,6 +1,5 @@ use crate::simd::{ cmp::SimdPartialOrd, - intrinsics, ptr::{SimdConstPtr, SimdMutPtr}, LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, }; @@ -194,7 +193,7 @@ where /// With padding, `read_unaligned` will read past the end of an array of N elements. /// /// # Safety - /// Reading `ptr` must be safe, as if by `<*const [T; N]>::read_unaligned`. + /// Reading `ptr` must be safe, as if by `<*const [T; N]>::read`. #[inline] const unsafe fn load(ptr: *const [T; N]) -> Self { // There are potentially simpler ways to write this function, but this should result in @@ -215,7 +214,7 @@ where /// See `load` as to why this function is necessary. /// /// # Safety - /// Writing to `ptr` must be safe, as if by `<*mut [T; N]>::write_unaligned`. + /// Writing to `ptr` must be safe, as if by `<*mut [T; N]>::write`. #[inline] const unsafe fn store(self, ptr: *mut [T; N]) { // There are potentially simpler ways to write this function, but this should result in @@ -491,7 +490,7 @@ where or: Self, ) -> Self { // Safety: The caller is responsible for upholding all invariants - unsafe { intrinsics::simd_gather(or, source, enable.to_int()) } + unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_int()) } } /// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`. @@ -650,7 +649,7 @@ where #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn scatter_select_ptr(self, dest: Simd<*mut T, N>, enable: Mask) { // Safety: The caller is responsible for upholding all invariants - unsafe { intrinsics::simd_scatter(self, dest, enable.to_int()) } + unsafe { core::intrinsics::simd::simd_scatter(self, dest, enable.to_int()) } } } @@ -692,7 +691,8 @@ where fn eq(&self, other: &Self) -> bool { // Safety: All SIMD vectors are SimdPartialEq, and the comparison produces a valid mask. let mask = unsafe { - let tfvec: Simd<::Mask, N> = intrinsics::simd_eq(*self, *other); + let tfvec: Simd<::Mask, N> = + core::intrinsics::simd::simd_eq(*self, *other); Mask::from_int_unchecked(tfvec) }; @@ -705,7 +705,8 @@ where fn ne(&self, other: &Self) -> bool { // Safety: All SIMD vectors are SimdPartialEq, and the comparison produces a valid mask. let mask = unsafe { - let tfvec: Simd<::Mask, N> = intrinsics::simd_ne(*self, *other); + let tfvec: Simd<::Mask, N> = + core::intrinsics::simd::simd_ne(*self, *other); Mask::from_int_unchecked(tfvec) }; diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs index 00fc2a24e27a..fc6a3476b7c6 100644 --- a/crates/core_simd/tests/masks.rs +++ b/crates/core_simd/tests/masks.rs @@ -99,6 +99,19 @@ macro_rules! test_mask_api { assert_eq!(Mask::<$type, 2>::from_bitmask(bitmask), mask); } + #[cfg(feature = "all_lane_counts")] + #[test] + fn roundtrip_bitmask_conversion_odd() { + let values = [ + true, false, true, false, true, true, false, false, false, true, true, + ]; + let mask = Mask::<$type, 11>::from_array(values); + let bitmask = mask.to_bitmask(); + assert_eq!(bitmask, 0b11000110101); + assert_eq!(Mask::<$type, 11>::from_bitmask(bitmask), mask); + } + + #[test] fn cast() { fn cast_impl() @@ -134,6 +147,35 @@ macro_rules! test_mask_api { assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b01001001, 0b10000011]); assert_eq!(Mask::<$type, 16>::from_bitmask_vector(bitmask), mask); } + + // rust-lang/portable-simd#379 + #[test] + fn roundtrip_bitmask_vector_conversion_small() { + use core_simd::simd::ToBytes; + let values = [ + true, false, true, true + ]; + let mask = Mask::<$type, 4>::from_array(values); + let bitmask = mask.to_bitmask_vector(); + assert_eq!(bitmask.resize::<1>(0).to_ne_bytes()[0], 0b00001101); + assert_eq!(Mask::<$type, 4>::from_bitmask_vector(bitmask), mask); + } + + /* FIXME doesn't work with non-powers-of-two, yet + // rust-lang/portable-simd#379 + #[cfg(feature = "all_lane_counts")] + #[test] + fn roundtrip_bitmask_vector_conversion_odd() { + use core_simd::simd::ToBytes; + let values = [ + true, false, true, false, true, true, false, false, false, true, true, + ]; + let mask = Mask::<$type, 11>::from_array(values); + let bitmask = mask.to_bitmask_vector(); + assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b00110101, 0b00000110]); + assert_eq!(Mask::<$type, 11>::from_bitmask_vector(bitmask), mask); + } + */ } } } diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 1fef17242ca8..4c547777fdeb 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(feature = "as_crate", no_std)] // We are std! #![cfg_attr( feature = "as_crate", - feature(platform_intrinsics), + feature(core_intrinsics), feature(portable_simd), allow(internal_features) )] @@ -10,6 +10,8 @@ use core::simd; #[cfg(feature = "as_crate")] use core_simd::simd; +use core::intrinsics::simd as intrinsics; + use simd::{LaneCount, Simd, SupportedLaneCount}; #[cfg(feature = "as_crate")] @@ -22,28 +24,6 @@ use experimental as sealed; use crate::sealed::Sealed; -// "platform intrinsics" are essentially "codegen intrinsics" -// each of these may be scalarized and lowered to a libm call -extern "platform-intrinsic" { - // ceil - fn simd_ceil(x: T) -> T; - - // floor - fn simd_floor(x: T) -> T; - - // round - fn simd_round(x: T) -> T; - - // trunc - fn simd_trunc(x: T) -> T; - - // fsqrt - fn simd_fsqrt(x: T) -> T; - - // fma - fn simd_fma(x: T, y: T, z: T) -> T; -} - /// This trait provides a possibly-temporary implementation of float functions /// that may, in the absence of hardware support, canonicalize to calling an /// operating system's `math.h` dynamically-loaded library (also known as a @@ -74,7 +54,7 @@ pub trait StdFloat: Sealed + Sized { #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn mul_add(self, a: Self, b: Self) -> Self { - unsafe { simd_fma(self, a, b) } + unsafe { intrinsics::simd_fma(self, a, b) } } /// Produces a vector where every lane has the square root value @@ -82,35 +62,35 @@ pub trait StdFloat: Sealed + Sized { #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn sqrt(self) -> Self { - unsafe { simd_fsqrt(self) } + unsafe { intrinsics::simd_fsqrt(self) } } /// Returns the smallest integer greater than or equal to each lane. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn ceil(self) -> Self { - unsafe { simd_ceil(self) } + unsafe { intrinsics::simd_ceil(self) } } /// Returns the largest integer value less than or equal to each lane. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn floor(self) -> Self { - unsafe { simd_floor(self) } + unsafe { intrinsics::simd_floor(self) } } /// Rounds to the nearest integer value. Ties round toward zero. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn round(self) -> Self { - unsafe { simd_round(self) } + unsafe { intrinsics::simd_round(self) } } /// Returns the floating point's integer value, with its fractional part removed. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn trunc(self) -> Self { - unsafe { simd_trunc(self) } + unsafe { intrinsics::simd_trunc(self) } } /// Returns the floating point's fractional value, with its integer part removed. diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs index b80c745aaf2f..51b860a86356 100644 --- a/crates/test_helpers/src/lib.rs +++ b/crates/test_helpers/src/lib.rs @@ -1,4 +1,8 @@ -#![feature(stdsimd, powerpc_target_feature)] +#![feature(powerpc_target_feature)] +#![cfg_attr( + any(target_arch = "powerpc", target_arch = "powerpc64"), + feature(stdarch_powerpc) +)] pub mod array; From 8d2208ea3df77d09e4bfb8a28e131cc06bc6183a Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Fri, 22 Mar 2024 16:58:39 -0700 Subject: [PATCH 010/285] Merge commit 'cff979eec1ac0473fc4960ee6cde462c6aeda824' into sync-portable-simd-2024-03-22 --- .github/workflows/ci.yml | 5 + Cargo.lock | 3 + crates/core_simd/src/lib.rs | 12 +- crates/core_simd/src/masks.rs | 6 + crates/core_simd/src/swizzle_dyn.rs | 8 +- crates/core_simd/src/vector.rs | 244 ++++++++++++++++++++ crates/core_simd/src/vendor.rs | 2 +- crates/core_simd/src/vendor/arm.rs | 8 +- crates/core_simd/tests/masked_load_store.rs | 35 +++ crates/core_simd/tests/swizzle_dyn.rs | 2 +- crates/std_float/Cargo.toml | 7 + crates/std_float/src/lib.rs | 151 ++++++++---- crates/std_float/tests/float.rs | 74 ++++++ 13 files changed, 505 insertions(+), 52 deletions(-) create mode 100644 crates/core_simd/tests/masked_load_store.rs create mode 100644 crates/std_float/tests/float.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90543044ea84..b292be2d6f99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -141,6 +141,11 @@ jobs: - name: Test (release) run: cargo test --verbose --target=${{ matrix.target }} --release + - name: Generate docs + run: cargo doc --verbose --target=${{ matrix.target }} + env: + RUSTDOCFLAGS: -Dwarnings + wasm-tests: name: "wasm (firefox, ${{ matrix.name }})" runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 46312c09657d..1584c704fb22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,6 +177,9 @@ name = "std_float" version = "0.1.0" dependencies = [ "core_simd", + "test_helpers", + "wasm-bindgen", + "wasm-bindgen-test", ] [[package]] diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index a25723e11cef..7a161b7e01d2 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -13,11 +13,12 @@ simd_ffi, staged_api, strict_provenance, + prelude_import, ptr_metadata )] #![cfg_attr( all( - any(target_arch = "aarch64", target_arch = "arm",), + any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm",), any( all(target_feature = "v6", not(target_feature = "mclass")), all(target_feature = "mclass", target_feature = "dsp"), @@ -33,12 +34,21 @@ any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc) )] +#![cfg_attr( + all(target_arch = "x86_64", target_feature = "avx512f"), + feature(stdarch_x86_avx512) +)] #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really #![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)] +#![doc(test(attr(deny(warnings))))] #![allow(internal_features)] #![unstable(feature = "portable_simd", issue = "86656")] //! Portable SIMD module. +#[prelude_import] +#[allow(unused_imports)] +use core::prelude::v1::*; + #[path = "mod.rs"] mod core_simd; pub use self::core_simd::simd; diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index e480c25a51e6..e6e27c76a5e9 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -34,6 +34,7 @@ mod sealed { fn eq(self, other: Self) -> bool; fn to_usize(self) -> usize; + fn max_unsigned() -> u64; type Unsigned: SimdElement; @@ -78,6 +79,11 @@ macro_rules! impl_element { self as usize } + #[inline] + fn max_unsigned() -> u64 { + <$unsigned>::MAX as u64 + } + type Unsigned = $unsigned; const TRUE: Self = -1; diff --git a/crates/core_simd/src/swizzle_dyn.rs b/crates/core_simd/src/swizzle_dyn.rs index ae9ff6894b0a..8a1079042f07 100644 --- a/crates/core_simd/src/swizzle_dyn.rs +++ b/crates/core_simd/src/swizzle_dyn.rs @@ -16,7 +16,10 @@ where #[inline] pub fn swizzle_dyn(self, idxs: Simd) -> Self { #![allow(unused_imports, unused_unsafe)] - #[cfg(all(target_arch = "aarch64", target_endian = "little"))] + #[cfg(all( + any(target_arch = "aarch64", target_arch = "arm64ec"), + target_endian = "little" + ))] use core::arch::aarch64::{uint8x8_t, vqtbl1q_u8, vtbl1_u8}; #[cfg(all( target_arch = "arm", @@ -37,6 +40,7 @@ where #[cfg(all( any( target_arch = "aarch64", + target_arch = "arm64ec", all(target_arch = "arm", target_feature = "v7") ), target_feature = "neon", @@ -48,7 +52,7 @@ where #[cfg(target_feature = "simd128")] 16 => transize(wasm::i8x16_swizzle, self, idxs), #[cfg(all( - target_arch = "aarch64", + any(target_arch = "aarch64", target_arch = "arm64ec"), target_feature = "neon", target_endian = "little" ))] diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 9e97a3161bb2..6c8205b112c3 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -1,5 +1,6 @@ use crate::simd::{ cmp::SimdPartialOrd, + num::SimdUint, ptr::{SimdConstPtr, SimdMutPtr}, LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, }; @@ -262,6 +263,7 @@ where /// # Panics /// /// Panics if the slice's length is less than the vector's `Simd::N`. + /// Use `load_or_default` for an alternative that does not panic. /// /// # Example /// @@ -315,6 +317,143 @@ where unsafe { self.store(slice.as_mut_ptr().cast()) } } + /// Reads contiguous elements from `slice`. Elements are read so long as they're in-bounds for + /// the `slice`. Otherwise, the default value for the element type is returned. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::Simd; + /// let vec: Vec = vec![10, 11]; + /// + /// let result = Simd::::load_or_default(&vec); + /// assert_eq!(result, Simd::from_array([10, 11, 0, 0])); + /// ``` + #[must_use] + #[inline] + pub fn load_or_default(slice: &[T]) -> Self + where + T: Default, + { + Self::load_or(slice, Default::default()) + } + + /// Reads contiguous elements from `slice`. Elements are read so long as they're in-bounds for + /// the `slice`. Otherwise, the corresponding value from `or` is passed through. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::Simd; + /// let vec: Vec = vec![10, 11]; + /// let or = Simd::from_array([-5, -4, -3, -2]); + /// + /// let result = Simd::load_or(&vec, or); + /// assert_eq!(result, Simd::from_array([10, 11, -3, -2])); + /// ``` + #[must_use] + #[inline] + pub fn load_or(slice: &[T], or: Self) -> Self { + Self::load_select(slice, Mask::splat(true), or) + } + + /// Reads contiguous elements from `slice`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled or out of bounds for the slice, that memory location + /// is not accessed and the corresponding value from `or` is passed through. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let enable = Mask::from_array([true, true, false, true]); + /// let or = Simd::from_array([-5, -4, -3, -2]); + /// + /// let result = Simd::load_select(&vec, enable, or); + /// assert_eq!(result, Simd::from_array([10, 11, -3, 13])); + /// ``` + #[must_use] + #[inline] + pub fn load_select_or_default(slice: &[T], enable: Mask<::Mask, N>) -> Self + where + T: Default, + { + Self::load_select(slice, enable, Default::default()) + } + + /// Reads contiguous elements from `slice`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled or out of bounds for the slice, that memory location + /// is not accessed and the corresponding value from `or` is passed through. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let enable = Mask::from_array([true, true, false, true]); + /// let or = Simd::from_array([-5, -4, -3, -2]); + /// + /// let result = Simd::load_select(&vec, enable, or); + /// assert_eq!(result, Simd::from_array([10, 11, -3, 13])); + /// ``` + #[must_use] + #[inline] + pub fn load_select( + slice: &[T], + mut enable: Mask<::Mask, N>, + or: Self, + ) -> Self { + enable &= mask_up_to(slice.len()); + // SAFETY: We performed the bounds check by updating the mask. &[T] is properly aligned to + // the element. + unsafe { Self::load_select_ptr(slice.as_ptr(), enable, or) } + } + + /// Reads contiguous elements from `slice`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled, that memory location is not accessed and the corresponding + /// value from `or` is passed through. + #[must_use] + #[inline] + pub unsafe fn load_select_unchecked( + slice: &[T], + enable: Mask<::Mask, N>, + or: Self, + ) -> Self { + let ptr = slice.as_ptr(); + // SAFETY: The safety of reading elements from `slice` is ensured by the caller. + unsafe { Self::load_select_ptr(ptr, enable, or) } + } + + /// Reads contiguous elements starting at `ptr`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled, that memory location is not accessed and the corresponding + /// value from `or` is passed through. + #[must_use] + #[inline] + pub unsafe fn load_select_ptr( + ptr: *const T, + enable: Mask<::Mask, N>, + or: Self, + ) -> Self { + // SAFETY: The safety of reading elements through `ptr` is ensured by the caller. + unsafe { core::intrinsics::simd::simd_masked_load(enable.to_int(), ptr, or) } + } + /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. /// If an index is out-of-bounds, the element is instead selected from the `or` vector. /// @@ -493,6 +632,77 @@ where unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_int()) } } + /// Conditionally write contiguous elements to `slice`. The `enable` mask controls + /// which elements are written, as long as they're in-bounds of the `slice`. + /// If the element is disabled or out of bounds, no memory access to that location + /// is made. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let mut arr = [0i32; 4]; + /// let write = Simd::from_array([-5, -4, -3, -2]); + /// let enable = Mask::from_array([false, true, true, true]); + /// + /// write.store_select(&mut arr[..3], enable); + /// assert_eq!(arr, [0, -4, -3, 0]); + /// ``` + #[inline] + pub fn store_select(self, slice: &mut [T], mut enable: Mask<::Mask, N>) { + enable &= mask_up_to(slice.len()); + // SAFETY: We performed the bounds check by updating the mask. &[T] is properly aligned to + // the element. + unsafe { self.store_select_ptr(slice.as_mut_ptr(), enable) } + } + + /// Conditionally write contiguous elements to `slice`. The `enable` mask controls + /// which elements are written. + /// + /// # Safety + /// + /// Every enabled element must be in bounds for the `slice`. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let mut arr = [0i32; 4]; + /// let write = Simd::from_array([-5, -4, -3, -2]); + /// let enable = Mask::from_array([false, true, true, true]); + /// + /// unsafe { write.store_select_unchecked(&mut arr, enable) }; + /// assert_eq!(arr, [0, -4, -3, -2]); + /// ``` + #[inline] + pub unsafe fn store_select_unchecked( + self, + slice: &mut [T], + enable: Mask<::Mask, N>, + ) { + let ptr = slice.as_mut_ptr(); + // SAFETY: The safety of writing elements in `slice` is ensured by the caller. + unsafe { self.store_select_ptr(ptr, enable) } + } + + /// Conditionally write contiguous elements starting from `ptr`. + /// The `enable` mask controls which elements are written. + /// When disabled, the memory location corresponding to that element is not accessed. + /// + /// # Safety + /// + /// Memory addresses for element are calculated [`pointer::wrapping_offset`] and + /// each enabled element must satisfy the same conditions as [`core::ptr::write`]. + #[inline] + pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<::Mask, N>) { + // SAFETY: The safety of writing elements through `ptr` is ensured by the caller. + unsafe { core::intrinsics::simd::simd_masked_store(enable.to_int(), ptr, self) } + } + /// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`. /// If an index is out-of-bounds, the write is suppressed without panicking. /// If two elements in the scattered vector would write to the same index @@ -980,3 +1190,37 @@ where { type Mask = isize; } + +#[inline] +fn lane_indices() -> Simd +where + LaneCount: SupportedLaneCount, +{ + let mut index = [0; N]; + for i in 0..N { + index[i] = i; + } + Simd::from_array(index) +} + +#[inline] +fn mask_up_to(len: usize) -> Mask +where + LaneCount: SupportedLaneCount, + M: MaskElement, +{ + let index = lane_indices::(); + let max_value: u64 = M::max_unsigned(); + macro_rules! case { + ($ty:ty) => { + if N < <$ty>::MAX as usize && max_value as $ty as u64 == max_value { + return index.cast().simd_lt(Simd::splat(len.min(N) as $ty)).cast(); + } + }; + } + case!(u8); + case!(u16); + case!(u32); + case!(u64); + index.simd_lt(Simd::splat(len)).cast() +} diff --git a/crates/core_simd/src/vendor.rs b/crates/core_simd/src/vendor.rs index 6223bedb4e13..1a34a3a8de5c 100644 --- a/crates/core_simd/src/vendor.rs +++ b/crates/core_simd/src/vendor.rs @@ -24,7 +24,7 @@ mod x86; #[cfg(target_arch = "wasm32")] mod wasm32; -#[cfg(any(target_arch = "aarch64", target_arch = "arm",))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm",))] mod arm; #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] diff --git a/crates/core_simd/src/vendor/arm.rs b/crates/core_simd/src/vendor/arm.rs index ee5c64213736..f8878d11f094 100644 --- a/crates/core_simd/src/vendor/arm.rs +++ b/crates/core_simd/src/vendor/arm.rs @@ -4,12 +4,13 @@ use crate::simd::*; #[cfg(target_arch = "arm")] use core::arch::arm::*; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] use core::arch::aarch64::*; #[cfg(all( any( target_arch = "aarch64", + target_arch = "arm64ec", all(target_arch = "arm", target_feature = "v7"), ), target_endian = "little" @@ -69,7 +70,10 @@ mod simd32 { from_transmute! { unsafe Simd => int8x4_t } } -#[cfg(target_arch = "aarch64")] +#[cfg(all( + any(target_arch = "aarch64", target_arch = "arm64ec"), + target_endian = "little" +))] mod aarch64 { use super::neon::*; use super::*; diff --git a/crates/core_simd/tests/masked_load_store.rs b/crates/core_simd/tests/masked_load_store.rs new file mode 100644 index 000000000000..3d38658e945f --- /dev/null +++ b/crates/core_simd/tests/masked_load_store.rs @@ -0,0 +1,35 @@ +#![feature(portable_simd)] +use core_simd::simd::prelude::*; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_test::*; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test_configure!(run_in_browser); + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn masked_load_store() { + let mut arr = [u8::MAX; 7]; + + u8x4::splat(0).store_select(&mut arr[5..], Mask::from_array([false, true, false, true])); + // write to index 8 is OOB and dropped + assert_eq!(arr, [255u8, 255, 255, 255, 255, 255, 0]); + + u8x4::from_array([0, 1, 2, 3]).store_select(&mut arr[1..], Mask::splat(true)); + assert_eq!(arr, [255u8, 0, 1, 2, 3, 255, 0]); + + // read from index 8 is OOB and dropped + assert_eq!( + u8x4::load_or(&arr[4..], u8x4::splat(42)), + u8x4::from_array([3, 255, 0, 42]) + ); + assert_eq!( + u8x4::load_select( + &arr[4..], + Mask::from_array([true, false, true, true]), + u8x4::splat(42) + ), + u8x4::from_array([3, 42, 0, 42]) + ); +} diff --git a/crates/core_simd/tests/swizzle_dyn.rs b/crates/core_simd/tests/swizzle_dyn.rs index f21a937f01c4..19ffe1417c8c 100644 --- a/crates/core_simd/tests/swizzle_dyn.rs +++ b/crates/core_simd/tests/swizzle_dyn.rs @@ -1,6 +1,6 @@ #![feature(portable_simd)] use core::{fmt, ops::RangeInclusive}; -use test_helpers::{self, biteq, make_runner, prop_assert_biteq}; +use test_helpers::{biteq, make_runner, prop_assert_biteq}; fn swizzle_dyn_scalar_ver(values: [u8; N], idxs: [u8; N]) -> [u8; N] { let mut array = [0; N]; diff --git a/crates/std_float/Cargo.toml b/crates/std_float/Cargo.toml index 84c69774cbdf..0896094ee63f 100644 --- a/crates/std_float/Cargo.toml +++ b/crates/std_float/Cargo.toml @@ -8,6 +8,13 @@ edition = "2021" [dependencies] core_simd = { path = "../core_simd", default-features = false } +[dev-dependencies.test_helpers] +path = "../test_helpers" + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3" + [features] default = ["as_crate"] as_crate = [] diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 4c547777fdeb..148aa5f9f177 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "as_crate", no_std)] // We are std! #![cfg_attr( feature = "as_crate", feature(core_intrinsics), @@ -44,7 +43,7 @@ use crate::sealed::Sealed; /// For now this trait is available to permit experimentation with SIMD float /// operations that may lack hardware support, such as `mul_add`. pub trait StdFloat: Sealed + Sized { - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error, + /// Elementwise fused multiply-add. Computes `(self * a) + b` with only one rounding error, /// yielding a more accurate result than an unfused multiply-add. /// /// Using `mul_add` *may* be more performant than an unfused multiply-add if the target @@ -57,22 +56,65 @@ pub trait StdFloat: Sealed + Sized { unsafe { intrinsics::simd_fma(self, a, b) } } - /// Produces a vector where every lane has the square root value - /// of the equivalently-indexed lane in `self` + /// Produces a vector where every element has the square root value + /// of the equivalently-indexed element in `self` #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn sqrt(self) -> Self { unsafe { intrinsics::simd_fsqrt(self) } } - /// Returns the smallest integer greater than or equal to each lane. + /// Produces a vector where every element has the sine of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn sin(self) -> Self; + + /// Produces a vector where every element has the cosine of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn cos(self) -> Self; + + /// Produces a vector where every element has the exponential (base e) of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn exp(self) -> Self; + + /// Produces a vector where every element has the exponential (base 2) of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn exp2(self) -> Self; + + /// Produces a vector where every element has the natural logarithm of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn ln(self) -> Self; + + /// Produces a vector where every element has the logarithm with respect to an arbitrary + /// in the equivalently-indexed elements in `self` and `base`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log(self, base: Self) -> Self { + unsafe { intrinsics::simd_div(self.ln(), base.ln()) } + } + + /// Produces a vector where every element has the base-2 logarithm of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log2(self) -> Self; + + /// Produces a vector where every element has the base-10 logarithm of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log10(self) -> Self; + + /// Returns the smallest integer greater than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn ceil(self) -> Self { unsafe { intrinsics::simd_ceil(self) } } - /// Returns the largest integer value less than or equal to each lane. + /// Returns the largest integer value less than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn floor(self) -> Self { @@ -101,46 +143,65 @@ pub trait StdFloat: Sealed + Sized { impl Sealed for Simd where LaneCount: SupportedLaneCount {} impl Sealed for Simd where LaneCount: SupportedLaneCount {} -// We can safely just use all the defaults. -impl StdFloat for Simd -where - LaneCount: SupportedLaneCount, -{ - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - fn fract(self) -> Self { - self - self.trunc() +macro_rules! impl_float { + { + $($fn:ident: $intrinsic:ident,)* + } => { + impl StdFloat for Simd + where + LaneCount: SupportedLaneCount, + { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + + $( + #[inline] + fn $fn(self) -> Self { + unsafe { intrinsics::$intrinsic(self) } + } + )* + } + + impl StdFloat for Simd + where + LaneCount: SupportedLaneCount, + { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + + $( + #[inline] + fn $fn(self) -> Self { + // https://github.com/llvm/llvm-project/issues/83729 + #[cfg(target_arch = "aarch64")] + { + let mut ln = Self::splat(0f64); + for i in 0..N { + ln[i] = self[i].$fn() + } + ln + } + + #[cfg(not(target_arch = "aarch64"))] + { + unsafe { intrinsics::$intrinsic(self) } + } + } + )* + } } } -impl StdFloat for Simd -where - LaneCount: SupportedLaneCount, -{ - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - fn fract(self) -> Self { - self - self.trunc() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use simd::prelude::*; - - #[test] - fn everything_works() { - let x = f32x4::from_array([0.1, 0.5, 0.6, -1.5]); - let x2 = x + x; - let _xc = x.ceil(); - let _xf = x.floor(); - let _xr = x.round(); - let _xt = x.trunc(); - let _xfma = x.mul_add(x, x); - let _xsqrt = x.sqrt(); - let _ = x2.abs() * x2; - } +impl_float! { + sin: simd_fsin, + cos: simd_fcos, + exp: simd_fexp, + exp2: simd_fexp2, + ln: simd_flog, + log2: simd_flog2, + log10: simd_flog10, } diff --git a/crates/std_float/tests/float.rs b/crates/std_float/tests/float.rs new file mode 100644 index 000000000000..c66c968f8c66 --- /dev/null +++ b/crates/std_float/tests/float.rs @@ -0,0 +1,74 @@ +#![feature(portable_simd)] + +macro_rules! unary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_unary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_| true, + ) + } + )* + } + } +} + +macro_rules! binary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_binary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_, _| true, + ) + } + )* + } + } +} + +macro_rules! ternary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_ternary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_, _, _| true, + ) + } + )* + } + } +} + +macro_rules! impl_tests { + { $scalar:tt } => { + mod $scalar { + use std_float::StdFloat; + + unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc } + binary_test! { $scalar, log } + ternary_test! { $scalar, mul_add } + + test_helpers::test_lanes! { + fn fract() { + test_helpers::test_unary_elementwise_flush_subnormals( + &core_simd::simd::Simd::<$scalar, LANES>::fract, + &$scalar::fract, + &|_| true, + ) + } + } + } + } +} + +impl_tests! { f32 } +impl_tests! { f64 } From 6c39a26f3319c7e77f8ffcd94e6f36d0c28bf3a9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 Mar 2024 11:52:33 +0100 Subject: [PATCH 011/285] add without_provenance to pointer types --- crates/core_simd/src/simd/ptr/const_ptr.rs | 21 +++++++++++++++++++++ crates/core_simd/src/simd/ptr/mut_ptr.rs | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index e217d1c8c87c..2e7654347c0a 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -42,6 +42,19 @@ pub trait SimdConstPtr: Copy + Sealed { /// Equivalent to calling [`pointer::addr`] on each element. fn addr(self) -> Self::Usize; + /// Convert an address to a pointer without giving it any provenance. + /// + /// Without provenance, this pointer is not associated with any actual allocation. Such a + /// no-provenance pointer may be used for zero-sized memory accesses (if suitably aligned), but + /// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers + /// are little more than a usize address in disguise. + /// + /// This is different from [`Self::from_exposed_addr`], which creates a pointer that picks up a + /// previously exposed provenance. + /// + /// Equivalent to calling [`core::ptr::without_provenance`] on each element. + fn without_provenance(addr: Self::Usize) -> Self; + /// Creates a new pointer with the given address. /// /// This performs the same operation as a cast, but copies the *address-space* and @@ -118,6 +131,14 @@ where unsafe { core::mem::transmute_copy(&self) } } + #[inline] + fn without_provenance(addr: Self::Usize) -> Self { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // SAFETY: Integer-to-pointer transmutes are valid (if you are okay with not getting any + // provenance). + unsafe { core::mem::transmute_copy(&addr) } + } + #[inline] fn with_addr(self, addr: Self::Usize) -> Self { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index 5cb27af4fdeb..5323bb74f6fe 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -39,6 +39,19 @@ pub trait SimdMutPtr: Copy + Sealed { /// Equivalent to calling [`pointer::addr`] on each element. fn addr(self) -> Self::Usize; + /// Convert an address to a pointer without giving it any provenance. + /// + /// Without provenance, this pointer is not associated with any actual allocation. Such a + /// no-provenance pointer may be used for zero-sized memory accesses (if suitably aligned), but + /// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers + /// are little more than a usize address in disguise. + /// + /// This is different from [`Self::from_exposed_addr`], which creates a pointer that picks up a + /// previously exposed provenance. + /// + /// Equivalent to calling [`core::ptr::without_provenance`] on each element. + fn without_provenance(addr: Self::Usize) -> Self; + /// Creates a new pointer with the given address. /// /// This performs the same operation as a cast, but copies the *address-space* and @@ -115,6 +128,14 @@ where unsafe { core::mem::transmute_copy(&self) } } + #[inline] + fn without_provenance(addr: Self::Usize) -> Self { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // SAFETY: Integer-to-pointer transmutes are valid (if you are okay with not getting any + // provenance). + unsafe { core::mem::transmute_copy(&addr) } + } + #[inline] fn with_addr(self, addr: Self::Usize) -> Self { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. From def711f17ba31ab85a292a3eb853e5f39c1976bc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 Mar 2024 11:47:11 +0100 Subject: [PATCH 012/285] rename ptr::from_exposed_addr -> ptr::with_exposed_provenance --- crates/core_simd/src/simd/ptr/const_ptr.rs | 8 ++++---- crates/core_simd/src/simd/ptr/mut_ptr.rs | 8 ++++---- crates/core_simd/tests/pointers.rs | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index e217d1c8c87c..3ec9fccbff95 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -51,13 +51,13 @@ pub trait SimdConstPtr: Copy + Sealed { fn with_addr(self, addr: Self::Usize) -> Self; /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::from_exposed_addr`]. + /// in [`Self::with_exposed_provenance`]. fn expose_addr(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// - /// Equivalent to calling [`core::ptr::from_exposed_addr`] on each element. - fn from_exposed_addr(addr: Self::Usize) -> Self; + /// Equivalent to calling [`core::ptr::with_exposed_provenance`] on each element. + fn with_exposed_provenance(addr: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// @@ -137,7 +137,7 @@ where } #[inline] - fn from_exposed_addr(addr: Self::Usize) -> Self { + fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } } diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index 5cb27af4fdeb..1142839e213b 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -48,13 +48,13 @@ pub trait SimdMutPtr: Copy + Sealed { fn with_addr(self, addr: Self::Usize) -> Self; /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::from_exposed_addr`]. + /// in [`Self::with_exposed_provenance`]. fn expose_addr(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// - /// Equivalent to calling [`core::ptr::from_exposed_addr_mut`] on each element. - fn from_exposed_addr(addr: Self::Usize) -> Self; + /// Equivalent to calling [`core::ptr::with_exposed_provenance_mut`] on each element. + fn with_exposed_provenance(addr: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// @@ -134,7 +134,7 @@ where } #[inline] - fn from_exposed_addr(addr: Self::Usize) -> Self { + fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } } diff --git a/crates/core_simd/tests/pointers.rs b/crates/core_simd/tests/pointers.rs index b9f32d16e01d..5984fdae2f9b 100644 --- a/crates/core_simd/tests/pointers.rs +++ b/crates/core_simd/tests/pointers.rs @@ -80,10 +80,10 @@ mod const_ptr { ); } - fn from_exposed_addr() { + fn with_exposed_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*const u32, LANES>::from_exposed_addr, - &core::ptr::from_exposed_addr::, + &Simd::<*const u32, LANES>::with_exposed_provenance, + &core::ptr::with_exposed_provenance::, &|_| true, ); } @@ -103,10 +103,10 @@ mod mut_ptr { ); } - fn from_exposed_addr() { + fn with_exposed_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*mut u32, LANES>::from_exposed_addr, - &core::ptr::from_exposed_addr_mut::, + &Simd::<*mut u32, LANES>::with_exposed_provenance, + &core::ptr::with_exposed_provenance_mut::, &|_| true, ); } From c7a9561fe2fdcda49a30965a5b0225b17711cfc6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 Mar 2024 23:00:53 +0100 Subject: [PATCH 013/285] also rename the SIMD intrinsic --- crates/core_simd/src/simd/ptr/const_ptr.rs | 2 +- crates/core_simd/src/simd/ptr/mut_ptr.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index 3ec9fccbff95..4d2fe999ca6f 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -139,7 +139,7 @@ where #[inline] fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } + unsafe { core::intrinsics::simd::simd_with_exposed_provenance(addr) } } #[inline] diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index 1142839e213b..b3437b9c4996 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -136,7 +136,7 @@ where #[inline] fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } + unsafe { core::intrinsics::simd::simd_with_exposed_provenance(addr) } } #[inline] From 61daba974050e2fb479927f9b5c4a1518e61e674 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Mon, 25 Mar 2024 11:02:02 -0700 Subject: [PATCH 014/285] Import the 2021 prelude in the core crate --- crates/core_simd/src/lib.rs | 4 ---- crates/core_simd/src/vector.rs | 1 - 2 files changed, 5 deletions(-) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 7a161b7e01d2..48514e52587f 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -45,10 +45,6 @@ #![unstable(feature = "portable_simd", issue = "86656")] //! Portable SIMD module. -#[prelude_import] -#[allow(unused_imports)] -use core::prelude::v1::*; - #[path = "mod.rs"] mod core_simd; pub use self::core_simd::simd; diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 6c8205b112c3..8dbdfc0e1fe0 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -4,7 +4,6 @@ use crate::simd::{ ptr::{SimdConstPtr, SimdMutPtr}, LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, }; -use core::convert::{TryFrom, TryInto}; /// A SIMD vector with the shape of `[T; N]` but the operations of `T`. /// From bfacd1473f244d3a9b6ea837c14b888ee426171c Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Wed, 20 Mar 2024 10:02:44 +0800 Subject: [PATCH 015/285] Add loongarch64 vendor conversions --- crates/core_simd/src/lib.rs | 1 + crates/core_simd/src/vendor.rs | 3 +++ crates/core_simd/src/vendor/loongarch64.rs | 31 ++++++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 crates/core_simd/src/vendor/loongarch64.rs diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 7a161b7e01d2..736beb84d298 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -30,6 +30,7 @@ all(target_arch = "arm", target_feature = "v7"), feature(stdarch_arm_neon_intrinsics) )] +#![cfg_attr(target_arch = "loongarch64", feature(stdarch_loongarch))] #![cfg_attr( any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc) diff --git a/crates/core_simd/src/vendor.rs b/crates/core_simd/src/vendor.rs index 1a34a3a8de5c..57536e4fc77d 100644 --- a/crates/core_simd/src/vendor.rs +++ b/crates/core_simd/src/vendor.rs @@ -29,3 +29,6 @@ mod arm; #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] mod powerpc; + +#[cfg(target_arch = "loongarch64")] +mod loongarch64; diff --git a/crates/core_simd/src/vendor/loongarch64.rs b/crates/core_simd/src/vendor/loongarch64.rs new file mode 100644 index 000000000000..1290bc166b2b --- /dev/null +++ b/crates/core_simd/src/vendor/loongarch64.rs @@ -0,0 +1,31 @@ +use crate::simd::*; +use core::arch::loongarch64::*; + +from_transmute! { unsafe u8x16 => v16u8 } +from_transmute! { unsafe u8x32 => v32u8 } +from_transmute! { unsafe i8x16 => v16i8 } +from_transmute! { unsafe i8x32 => v32i8 } + +from_transmute! { unsafe u16x8 => v8u16 } +from_transmute! { unsafe u16x16 => v16u16 } +from_transmute! { unsafe i16x8 => v8i16 } +from_transmute! { unsafe i16x16 => v16i16 } + +from_transmute! { unsafe u32x4 => v4u32 } +from_transmute! { unsafe u32x8 => v8u32 } +from_transmute! { unsafe i32x4 => v4i32 } +from_transmute! { unsafe i32x8 => v8i32 } +from_transmute! { unsafe f32x4 => v4f32 } +from_transmute! { unsafe f32x8 => v8f32 } + +from_transmute! { unsafe u64x2 => v2u64 } +from_transmute! { unsafe u64x4 => v4u64 } +from_transmute! { unsafe i64x2 => v2i64 } +from_transmute! { unsafe i64x4 => v4i64 } +from_transmute! { unsafe f64x2 => v2f64 } +from_transmute! { unsafe f64x4 => v4f64 } + +from_transmute! { unsafe usizex2 => v2u64 } +from_transmute! { unsafe usizex4 => v4u64 } +from_transmute! { unsafe isizex2 => v2i64 } +from_transmute! { unsafe isizex4 => v4i64 } From b03986147d90adecd96e4d651d2959127a32d535 Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 3 Apr 2024 15:17:00 +0200 Subject: [PATCH 016/285] rename `expose_addr` to `expose_provenance` --- crates/core_simd/src/simd/ptr/const_ptr.rs | 10 +++++----- crates/core_simd/src/simd/ptr/mut_ptr.rs | 10 +++++----- crates/core_simd/tests/pointers.rs | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index 4d2fe999ca6f..0f1719206c9c 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -50,9 +50,9 @@ pub trait SimdConstPtr: Copy + Sealed { /// Equivalent to calling [`pointer::with_addr`] on each element. fn with_addr(self, addr: Self::Usize) -> Self; - /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::with_exposed_provenance`]. - fn expose_addr(self) -> Self::Usize; + /// Exposes the "provenance" part of the pointer for future use in + /// [`Self::with_exposed_provenance`] and returns the "address" portion. + fn expose_provenance(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// @@ -131,9 +131,9 @@ where } #[inline] - fn expose_addr(self) -> Self::Usize { + fn expose_provenance(self) -> Self::Usize { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_expose_addr(self) } + unsafe { core::intrinsics::simd::simd_expose_provenance(self) } } #[inline] diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index b3437b9c4996..7ba996d149c0 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -47,9 +47,9 @@ pub trait SimdMutPtr: Copy + Sealed { /// Equivalent to calling [`pointer::with_addr`] on each element. fn with_addr(self, addr: Self::Usize) -> Self; - /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::with_exposed_provenance`]. - fn expose_addr(self) -> Self::Usize; + /// Exposes the "provenance" part of the pointer for future use in + /// [`Self::with_exposed_provenance`] and returns the "address" portion. + fn expose_provenance(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// @@ -128,9 +128,9 @@ where } #[inline] - fn expose_addr(self) -> Self::Usize { + fn expose_provenance(self) -> Self::Usize { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_expose_addr(self) } + unsafe { core::intrinsics::simd::simd_expose_provenance(self) } } #[inline] diff --git a/crates/core_simd/tests/pointers.rs b/crates/core_simd/tests/pointers.rs index 5984fdae2f9b..90bfc5d5fd6a 100644 --- a/crates/core_simd/tests/pointers.rs +++ b/crates/core_simd/tests/pointers.rs @@ -32,10 +32,10 @@ macro_rules! common_tests { ); } - fn expose_addr() { + fn expose_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*$constness u32, LANES>::expose_addr, - &<*$constness u32>::expose_addr, + &Simd::<*$constness u32, LANES>::expose_provenance, + &<*$constness u32>::expose_provenance, &|_| true, ); } From 0d6e714830574ed8a8afba6719db4837ce3b6b96 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Apr 2024 22:13:18 +0200 Subject: [PATCH 017/285] run Miri tests on CI --- .github/workflows/ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b292be2d6f99..a49cfa44a74d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -273,3 +273,15 @@ jobs: run: | echo "Found AVX features: $CPU_FEATURE" RUSTFLAGS="-Dwarnings -Ctarget-feature=$CPU_FEATURE" cargo test --all-targets --no-default-features ${{ matrix.features }} + + miri: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup Rust + run: | + rustup update nightly --no-self-update + rustup default nightly + rustup component add miri rust-src + - name: Test (Miri) + run: cargo miri test From 55339322beaef8a56ede2b9130e5479cf3e526f3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 9 Apr 2024 07:23:14 +0200 Subject: [PATCH 018/285] do not run the 4-lane tests in Miri --- crates/test_helpers/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs index 51b860a86356..c55099434c8c 100644 --- a/crates/test_helpers/src/lib.rs +++ b/crates/test_helpers/src/lib.rs @@ -539,12 +539,12 @@ macro_rules! test_lanes { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; lanes_1 1; lanes_2 2; - lanes_4 4; ); #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow $crate::test_lanes_helper!( #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; + lanes_4 4; lanes_8 8; lanes_16 16; lanes_32 32; @@ -553,17 +553,17 @@ macro_rules! test_lanes { #[cfg(feature = "all_lane_counts")] $crate::test_lanes_helper!( - // test some odd and even non-power-of-2 lengths on miri + // test one non-power-of-2 length on miri #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; lanes_3 3; - lanes_5 5; - lanes_6 6; ); #[cfg(feature = "all_lane_counts")] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow $crate::test_lanes_helper!( #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; + lanes_5 5; + lanes_6 6; lanes_7 7; lanes_9 9; lanes_10 10; From f79ce11d54f2f2e849c34539d0fdcdb4b4d71b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20H=C3=B6ffner?= Date: Wed, 10 Apr 2024 10:56:51 +0200 Subject: [PATCH 019/285] document PartialOrd difference to simd_min and simd_max Avoid migration pitfall from packed_simd as described in #402. --- crates/core_simd/src/vector.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 6c8205b112c3..4bcbba727a2f 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -925,6 +925,7 @@ where } } +/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl PartialOrd for Simd where LaneCount: SupportedLaneCount, From ef5f073171234fa4816160d83be494305b4e078c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20H=C3=B6ffner?= Date: Wed, 10 Apr 2024 11:32:51 +0200 Subject: [PATCH 020/285] document Ord difference to simd_min and simd_max --- crates/core_simd/src/vector.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 4bcbba727a2f..c4e9ee0684cf 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -945,6 +945,7 @@ where { } +/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl Ord for Simd where LaneCount: SupportedLaneCount, From 7ba49ef81ca1cea7f5eb82440b85ba2ab859602d Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Mon, 25 Mar 2024 11:02:02 -0700 Subject: [PATCH 021/285] Import the 2021 prelude in the core crate --- crates/core_simd/src/lib.rs | 4 ---- crates/core_simd/src/vector.rs | 1 - 2 files changed, 5 deletions(-) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 736beb84d298..ecb7c78b4009 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -46,10 +46,6 @@ #![unstable(feature = "portable_simd", issue = "86656")] //! Portable SIMD module. -#[prelude_import] -#[allow(unused_imports)] -use core::prelude::v1::*; - #[path = "mod.rs"] mod core_simd; pub use self::core_simd::simd; diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 6c8205b112c3..8dbdfc0e1fe0 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -4,7 +4,6 @@ use crate::simd::{ ptr::{SimdConstPtr, SimdMutPtr}, LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, }; -use core::convert::{TryFrom, TryInto}; /// A SIMD vector with the shape of `[T; N]` but the operations of `T`. /// From 2b03143cfeb00df820ae38c5ccf8f59b4496d40d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 Mar 2024 11:47:11 +0100 Subject: [PATCH 022/285] rename ptr::from_exposed_addr -> ptr::with_exposed_provenance --- crates/core_simd/src/simd/ptr/const_ptr.rs | 8 ++++---- crates/core_simd/src/simd/ptr/mut_ptr.rs | 8 ++++---- crates/core_simd/tests/pointers.rs | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index 2e7654347c0a..cab342d61ffc 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -64,13 +64,13 @@ pub trait SimdConstPtr: Copy + Sealed { fn with_addr(self, addr: Self::Usize) -> Self; /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::from_exposed_addr`]. + /// in [`Self::with_exposed_provenance`]. fn expose_addr(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// - /// Equivalent to calling [`core::ptr::from_exposed_addr`] on each element. - fn from_exposed_addr(addr: Self::Usize) -> Self; + /// Equivalent to calling [`core::ptr::with_exposed_provenance`] on each element. + fn with_exposed_provenance(addr: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// @@ -158,7 +158,7 @@ where } #[inline] - fn from_exposed_addr(addr: Self::Usize) -> Self { + fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } } diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index 5323bb74f6fe..25fbc37f8652 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -61,13 +61,13 @@ pub trait SimdMutPtr: Copy + Sealed { fn with_addr(self, addr: Self::Usize) -> Self; /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::from_exposed_addr`]. + /// in [`Self::with_exposed_provenance`]. fn expose_addr(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// - /// Equivalent to calling [`core::ptr::from_exposed_addr_mut`] on each element. - fn from_exposed_addr(addr: Self::Usize) -> Self; + /// Equivalent to calling [`core::ptr::with_exposed_provenance_mut`] on each element. + fn with_exposed_provenance(addr: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// @@ -155,7 +155,7 @@ where } #[inline] - fn from_exposed_addr(addr: Self::Usize) -> Self { + fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } } diff --git a/crates/core_simd/tests/pointers.rs b/crates/core_simd/tests/pointers.rs index b9f32d16e01d..5984fdae2f9b 100644 --- a/crates/core_simd/tests/pointers.rs +++ b/crates/core_simd/tests/pointers.rs @@ -80,10 +80,10 @@ mod const_ptr { ); } - fn from_exposed_addr() { + fn with_exposed_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*const u32, LANES>::from_exposed_addr, - &core::ptr::from_exposed_addr::, + &Simd::<*const u32, LANES>::with_exposed_provenance, + &core::ptr::with_exposed_provenance::, &|_| true, ); } @@ -103,10 +103,10 @@ mod mut_ptr { ); } - fn from_exposed_addr() { + fn with_exposed_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*mut u32, LANES>::from_exposed_addr, - &core::ptr::from_exposed_addr_mut::, + &Simd::<*mut u32, LANES>::with_exposed_provenance, + &core::ptr::with_exposed_provenance_mut::, &|_| true, ); } From 6da23374ef93acfea6f2c75d2cf73b7231586156 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 Mar 2024 23:00:53 +0100 Subject: [PATCH 023/285] also rename the SIMD intrinsic --- crates/core_simd/src/simd/ptr/const_ptr.rs | 2 +- crates/core_simd/src/simd/ptr/mut_ptr.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index cab342d61ffc..fd6e091aed5d 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -160,7 +160,7 @@ where #[inline] fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } + unsafe { core::intrinsics::simd::simd_with_exposed_provenance(addr) } } #[inline] diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index 25fbc37f8652..2f6684b3f570 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -157,7 +157,7 @@ where #[inline] fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } + unsafe { core::intrinsics::simd::simd_with_exposed_provenance(addr) } } #[inline] From b8a18fa96571ce29814d9cf32f3f16eba918a2fa Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 3 Apr 2024 15:17:00 +0200 Subject: [PATCH 024/285] rename `expose_addr` to `expose_provenance` --- crates/core_simd/src/simd/ptr/const_ptr.rs | 10 +++++----- crates/core_simd/src/simd/ptr/mut_ptr.rs | 10 +++++----- crates/core_simd/tests/pointers.rs | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index fd6e091aed5d..809ea7cf43b7 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -63,9 +63,9 @@ pub trait SimdConstPtr: Copy + Sealed { /// Equivalent to calling [`pointer::with_addr`] on each element. fn with_addr(self, addr: Self::Usize) -> Self; - /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::with_exposed_provenance`]. - fn expose_addr(self) -> Self::Usize; + /// Exposes the "provenance" part of the pointer for future use in + /// [`Self::with_exposed_provenance`] and returns the "address" portion. + fn expose_provenance(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// @@ -152,9 +152,9 @@ where } #[inline] - fn expose_addr(self) -> Self::Usize { + fn expose_provenance(self) -> Self::Usize { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_expose_addr(self) } + unsafe { core::intrinsics::simd::simd_expose_provenance(self) } } #[inline] diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index 2f6684b3f570..f418f90154e0 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -60,9 +60,9 @@ pub trait SimdMutPtr: Copy + Sealed { /// Equivalent to calling [`pointer::with_addr`] on each element. fn with_addr(self, addr: Self::Usize) -> Self; - /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::with_exposed_provenance`]. - fn expose_addr(self) -> Self::Usize; + /// Exposes the "provenance" part of the pointer for future use in + /// [`Self::with_exposed_provenance`] and returns the "address" portion. + fn expose_provenance(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// @@ -149,9 +149,9 @@ where } #[inline] - fn expose_addr(self) -> Self::Usize { + fn expose_provenance(self) -> Self::Usize { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_expose_addr(self) } + unsafe { core::intrinsics::simd::simd_expose_provenance(self) } } #[inline] diff --git a/crates/core_simd/tests/pointers.rs b/crates/core_simd/tests/pointers.rs index 5984fdae2f9b..90bfc5d5fd6a 100644 --- a/crates/core_simd/tests/pointers.rs +++ b/crates/core_simd/tests/pointers.rs @@ -32,10 +32,10 @@ macro_rules! common_tests { ); } - fn expose_addr() { + fn expose_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*$constness u32, LANES>::expose_addr, - &<*$constness u32>::expose_addr, + &Simd::<*$constness u32, LANES>::expose_provenance, + &<*$constness u32>::expose_provenance, &|_| true, ); } From 6c6815251a0fc17036998e7ef6a4bfc7b02331e6 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Thu, 11 Apr 2024 00:15:29 -0400 Subject: [PATCH 025/285] Pin toolchain --- rust-toolchain.toml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 rust-toolchain.toml diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000000..5e885801cda2 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2024-04-10" +components = ["rustfmt", "clippy", "miri", "rust-src"] From 14ae03d7737f9ec5b33256524605ffec96395104 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Thu, 11 Apr 2024 00:19:20 -0400 Subject: [PATCH 026/285] Fix docs --- crates/core_simd/src/simd/ptr/const_ptr.rs | 2 +- crates/core_simd/src/simd/ptr/mut_ptr.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index 809ea7cf43b7..4e09e52f4196 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -49,7 +49,7 @@ pub trait SimdConstPtr: Copy + Sealed { /// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers /// are little more than a usize address in disguise. /// - /// This is different from [`Self::from_exposed_addr`], which creates a pointer that picks up a + /// This is different from [`Self::with_exposed_provenance`], which creates a pointer that picks up a /// previously exposed provenance. /// /// Equivalent to calling [`core::ptr::without_provenance`] on each element. diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index f418f90154e0..9ddce68aeb32 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -46,7 +46,7 @@ pub trait SimdMutPtr: Copy + Sealed { /// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers /// are little more than a usize address in disguise. /// - /// This is different from [`Self::from_exposed_addr`], which creates a pointer that picks up a + /// This is different from [`Self::with_exposed_provenance`], which creates a pointer that picks up a /// previously exposed provenance. /// /// Equivalent to calling [`core::ptr::without_provenance`] on each element. From fe815f775f444b789f90ce3aae08721982ecdfd6 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Thu, 11 Apr 2024 00:25:10 -0400 Subject: [PATCH 027/285] Simplify CI --- .github/workflows/ci.yml | 35 +++-------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a49cfa44a74d..d3f79733d3ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,11 +17,6 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup component add rustfmt - name: Run rustfmt run: cargo fmt --all -- --check @@ -51,11 +46,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup target add ${{ matrix.target }} - rustup component add clippy + run: rustup target add ${{ matrix.target }} - name: Run Clippy run: cargo clippy --all-targets --target ${{ matrix.target }} @@ -100,10 +91,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup target add ${{ matrix.target }} + run: rustup target add ${{ matrix.target }} - name: Configure RUSTFLAGS shell: bash @@ -156,10 +144,6 @@ jobs: - { name: simd128, RUSTFLAGS: "-C target-feature=+simd128" } steps: - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - name: Install wasm-pack run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - name: Test (debug) @@ -203,11 +187,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup target add ${{ matrix.target }} - rustup component add rust-src + run: rustup target add ${{ matrix.target }} - name: Install Cross # Equivalent to `cargo install cross`, but downloading a prebuilt @@ -259,10 +239,6 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - name: Detect AVX512 run: echo "CPU_FEATURE=$(lscpu | grep -o avx512[a-z]* | sed s/avx/+avx/ | tr '\n' ',' )" >> $GITHUB_ENV - name: Check build @@ -278,10 +254,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup component add miri rust-src - name: Test (Miri) run: cargo miri test From 4fa3e88811c088d8d3da7e99dc1602f2b48156f9 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 19 Apr 2024 16:17:02 +0100 Subject: [PATCH 028/285] Stabilise `inline_const` --- 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 48514e52587f..331b66262490 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -7,7 +7,6 @@ convert_float_to_int, core_intrinsics, decl_macro, - inline_const, intra_doc_pointers, repr_simd, simd_ffi, From e72b450149f084c8b15539596f1bba12ca0ae36c Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Mon, 29 Apr 2024 21:00:20 -0400 Subject: [PATCH 029/285] Make splat const fn --- crates/core_simd/src/lib.rs | 1 + crates/core_simd/src/vector.rs | 31 ++++++++++++++++++++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index ecb7c78b4009..4506feb80e34 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![feature( + const_eval_select, const_intrinsic_copy, const_refs_to_cell, const_maybe_uninit_as_mut_ptr, diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index cff274dc85e8..9cadc51ba29c 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -144,14 +144,31 @@ where /// assert_eq!(v.as_array(), &[8, 8, 8, 8]); /// ``` #[inline] - pub fn splat(value: T) -> Self { - // This is preferred over `[value; N]`, since it's explicitly a splat: - // https://github.com/rust-lang/rust/issues/97804 - struct Splat; - impl Swizzle for Splat { - const INDEX: [usize; N] = [0; N]; + pub const fn splat(value: T) -> Self { + const fn splat_const(value: T) -> Simd + where + T: SimdElement, + LaneCount: SupportedLaneCount, + { + Simd::from_array([value; N]) } - Splat::swizzle::(Simd::::from([value])) + + fn splat_rt(value: T) -> Simd + where + T: SimdElement, + LaneCount: SupportedLaneCount, + { + // This is preferred over `[value; N]`, since it's explicitly a splat: + // https://github.com/rust-lang/rust/issues/97804 + struct Splat; + impl Swizzle for Splat { + const INDEX: [usize; N] = [0; N]; + } + + Splat::swizzle::(Simd::::from([value])) + } + + core::intrinsics::const_eval_select((value,), splat_const, splat_rt) } /// Returns an array reference containing the entire SIMD vector. From 9314128affcbbe517c8f083645c447980ad6b236 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Mon, 29 Apr 2024 21:08:24 -0400 Subject: [PATCH 030/285] Lock rust version --- crates/core_simd/src/lib.rs | 1 - rust-toolchain.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index ecb7c78b4009..f13b9004ab30 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -7,7 +7,6 @@ convert_float_to_int, core_intrinsics, decl_macro, - inline_const, intra_doc_pointers, repr_simd, simd_ffi, diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5e885801cda2..8f75e5572244 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-04-10" +channel = "nightly-2024-04-29" components = ["rustfmt", "clippy", "miri", "rust-src"] From 4fcf18b5681530c36413f30cdeac9635c6dc3488 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Mon, 29 Apr 2024 21:45:48 -0400 Subject: [PATCH 031/285] Fix macos tests in CI --- .github/workflows/ci.yml | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3f79733d3ab..ef6a9e611ac0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,26 +56,19 @@ jobs: strategy: fail-fast: false matrix: - target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu, x86_64-apple-darwin] + target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu] # `default` means we use the default target config for the target, # `native` means we run with `-Ctarget-cpu=native`, and anything else is # an arg to `-Ctarget-feature` target_feature: [default, native, +sse3, +ssse3, +sse4.1, +sse4.2, +avx, +avx2] exclude: - # The macos runners seem to only reliably support up to `avx`. - - { target: x86_64-apple-darwin, target_feature: +avx2 } - # These features are statically known to be present for all 64 bit - # macs, and thus are covered by the `default` test - - { target: x86_64-apple-darwin, target_feature: +sse3 } - - { target: x86_64-apple-darwin, target_feature: +ssse3 } # -Ctarget-cpu=native sounds like bad-news if target != host - { target: i686-pc-windows-msvc, target_feature: native } - { target: i586-pc-windows-msvc, target_feature: native } include: # Populate the `matrix.os` field - - { target: x86_64-apple-darwin, os: macos-latest } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest } - { target: x86_64-pc-windows-msvc, os: windows-latest } - { target: i686-pc-windows-msvc, os: windows-latest } @@ -133,6 +126,35 @@ jobs: run: cargo doc --verbose --target=${{ matrix.target }} env: RUSTDOCFLAGS: -Dwarnings + + macos-tests: + name: ${{ matrix.target }} + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + target: + - aarch64-apple-darwin + - x86_64-apple-darwin + steps: + - uses: actions/checkout@v2 + - name: Setup Rust + run: rustup target add ${{ matrix.target }} + + - name: Configure RUSTFLAGS + shell: bash + run: echo "RUSTFLAGS=-Dwarnings" >> $GITHUB_ENV + + - name: Test (debug) + run: cargo test --verbose --target=${{ matrix.target }} + + - name: Test (release) + run: cargo test --verbose --target=${{ matrix.target }} --release + + - name: Generate docs + run: cargo doc --verbose --target=${{ matrix.target }} + env: + RUSTDOCFLAGS: -Dwarnings wasm-tests: name: "wasm (firefox, ${{ matrix.name }})" From 3927f4adfa7df520703443e622a395622e68377b Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 28 Mar 2024 14:30:32 +0000 Subject: [PATCH 032/285] Add `size_of`, `size_of_val`, `align_of`, and `align_of_val` to the prelude Many, many projects use `size_of` to get the size of a type. However, it's also often equally easy to hardcode a size (e.g. `8` instead of `size_of::()`). Minimizing friction in the use of `size_of` helps ensure that people use it and make code more self-documenting. The name `size_of` is unambiguous: the name alone, without any prefix or path, is self-explanatory and unmistakeable for any other functionality. Adding it to the prelude cannot produce any name conflicts, as any local definition will silently shadow the one from the prelude. Thus, we don't need to wait for a new edition prelude to add it. Add `size_of_val`, `align_of`, and `align_of_val` as well, with similar justification: widely useful, self-explanatory, unmistakeable for anything else, won't produce conflicts. --- crates/core_simd/src/simd/ptr/const_ptr.rs | 2 +- crates/core_simd/src/simd/ptr/mut_ptr.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index 0f1719206c9c..cbffbc564cfe 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -96,7 +96,7 @@ where fn cast(self) -> Self::CastPtr { // SimdElement currently requires zero-sized metadata, so this should never fail. // If this ever changes, `simd_cast_ptr` should produce a post-mono error. - use core::{mem::size_of, ptr::Pointee}; + use core::ptr::Pointee; assert_eq!(size_of::<::Metadata>(), 0); assert_eq!(size_of::<::Metadata>(), 0); diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index 7ba996d149c0..6bc6ca3ac42d 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -93,7 +93,7 @@ where fn cast(self) -> Self::CastPtr { // SimdElement currently requires zero-sized metadata, so this should never fail. // If this ever changes, `simd_cast_ptr` should produce a post-mono error. - use core::{mem::size_of, ptr::Pointee}; + use core::ptr::Pointee; assert_eq!(size_of::<::Metadata>(), 0); assert_eq!(size_of::<::Metadata>(), 0); From 435f7068b0acd39dc48ef84793e658e8eb89e342 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sat, 18 May 2024 18:08:11 +0200 Subject: [PATCH 033/285] Fix typos (taking into account review comments) --- crates/core_simd/src/ops.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index d8e10eeaa1a2..dd7303a97b19 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -122,7 +122,7 @@ macro_rules! for_base_types { #[inline] #[must_use = "operator returns a new vector without mutating the inputs"] // TODO: only useful for int Div::div, but we hope that this - // will essentially always always get inlined anyway. + // will essentially always get inlined anyway. #[track_caller] fn $call(self, rhs: Self) -> Self::Output { $macro_impl!(self, rhs, $inner, $scalar) From 675401b04bd9662325eac63774e476470e272743 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Wed, 5 Jun 2024 13:51:20 -0400 Subject: [PATCH 034/285] Add extend special swizzle fn, and implement special swizzle fns for masks --- crates/core_simd/src/swizzle.rs | 180 +++++++++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 1 deletion(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 71110bb28201..39e763494395 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -312,7 +312,9 @@ where /// /// ``` /// # #![feature(portable_simd)] - /// # use core::simd::Simd; + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use 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); @@ -383,4 +385,180 @@ where } Resize::::concat_swizzle(self, Simd::splat(value)) } + + /// Extract a vector from another vector. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::u32x4; + /// let x = u32x4::from_array([0, 1, 2, 3]); + /// assert_eq!(x.extract::<1, 2>().to_array(), [1, 2]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn extract(self) -> Simd + where + LaneCount: SupportedLaneCount, + { + struct Extract; + impl Swizzle for Extract { + const INDEX: [usize; LEN] = const { + assert!(START + LEN <= N, "index out of bounds"); + let mut index = [0; LEN]; + let mut i = 0; + while i < LEN { + index[i] = START + i; + i += 1; + } + index + }; + } + Extract::::swizzle(self) + } +} + +impl Mask +where + T: MaskElement, + LaneCount: SupportedLaneCount, +{ + /// Reverse the order of the elements in the mask. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn reverse(self) -> Self { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().reverse()) } + } + + /// Rotates the mask such that the first `OFFSET` elements of the slice move to the end + /// while the last `self.len() - OFFSET` elements move to the front. After calling `rotate_elements_left`, + /// the element previously at index `OFFSET` will become the first element in the slice. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn rotate_elements_left(self) -> Self { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_left::()) } + } + + /// Rotates the mask such that the first `self.len() - OFFSET` elements of the mask move to + /// the end while the last `OFFSET` elements move to the front. After calling `rotate_elements_right`, + /// the element previously at index `self.len() - OFFSET` will become the first element in the slice. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn rotate_elements_right(self) -> Self { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_right::()) } + } + + /// Interleave two masks. + /// + /// The resulting masks contain elements taken alternatively from `self` and `other`, first + /// filling the first result, and then the second. + /// + /// The reverse of this operation is [`Mask::deinterleave`]. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let a = mask32x4::from_array([false, true, false, true]); + /// let b = mask32x4::from_array([false, false, true, true]); + /// let (x, y) = a.interleave(b); + /// assert_eq!(x.to_array(), [false, false, true, false]); + /// assert_eq!(y.to_array(), [false, true, true, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn interleave(self, other: Self) -> (Self, Self) { + // Safety: swizzles are safe for masks + let (lo, hi) = self.to_int().interleave(other.to_int()); + unsafe { (Self::from_int_unchecked(lo), Self::from_int_unchecked(hi)) } + } + + /// Deinterleave two masks. + /// + /// The first result takes every other element of `self` and then `other`, starting with + /// the first element. + /// + /// The second result takes every other element of `self` and then `other`, starting with + /// the second element. + /// + /// The reverse of this operation is [`Mask::interleave`]. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let a = mask32x4::from_array([false, true, false, true]); + /// let b = mask32x4::from_array([false, false, true, true]); + /// let (x, y) = a.deinterleave(b); + /// assert_eq!(x.to_array(), [false, false, false, true]); + /// assert_eq!(y.to_array(), [true, true, false, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn deinterleave(self, other: Self) -> (Self, Self) { + // Safety: swizzles are safe for masks + let (even, odd) = self.to_int().deinterleave(other.to_int()); + unsafe { + ( + Self::from_int_unchecked(even), + Self::from_int_unchecked(odd), + ) + } + } + + /// Resize a mask. + /// + /// If `M` > `N`, extends the length of a mask, setting the new elements to `value`. + /// If `M` < `N`, truncates the mask to the first `M` elements. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let x = mask32x4::from_array([false, true, true, false]); + /// assert_eq!(x.resize::<8>(true).to_array(), [false, true, true, false, true, true, true, true]); + /// assert_eq!(x.resize::<2>(true).to_array(), [false, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn resize(self, value: bool) -> Mask + where + LaneCount: SupportedLaneCount, + { + // Safety: swizzles are safe for masks + unsafe { + Mask::::from_int_unchecked(self.to_int().resize::(if value { + T::TRUE + } else { + T::FALSE + })) + } + } + + /// Extract a vector from another vector. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let x = mask32x4::from_array([false, true, true, false]); + /// assert_eq!(x.extract::<1, 2>().to_array(), [true, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn extract(self) -> Mask + where + LaneCount: SupportedLaneCount, + { + // Safety: swizzles are safe for masks + unsafe { Mask::::from_int_unchecked(self.to_int().extract::()) } + } } From 3733375f53bb968e18b56b7a6a3d1c1d51f15e4b Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Wed, 5 Jun 2024 13:56:13 -0400 Subject: [PATCH 035/285] Remove bitmask vectors in favor of extracting bitmasks --- crates/core_simd/src/masks.rs | 42 ------------------ crates/core_simd/src/masks/bitmask.rs | 17 ------- crates/core_simd/src/masks/full_masks.rs | 56 ------------------------ crates/core_simd/tests/masks.rs | 42 ------------------ 4 files changed, 157 deletions(-) diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index e6e27c76a5e9..d8e984d9e996 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -308,48 +308,6 @@ where Self(mask_impl::Mask::from_bitmask_integer(bitmask)) } - /// Create a bitmask vector from a mask. - /// - /// Each bit is set if the corresponding element in the mask is `true`. - /// The remaining bits are unset. - /// - /// The bits are packed into the first N bits of the vector: - /// ``` - /// # #![feature(portable_simd)] - /// # #[cfg(feature = "as_crate")] use core_simd::simd; - /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::mask32x8; - /// let mask = mask32x8::from_array([true, false, true, false, false, false, true, false]); - /// assert_eq!(mask.to_bitmask_vector()[0], 0b01000101); - /// ``` - #[inline] - #[must_use = "method returns a new integer and does not mutate the original value"] - pub fn to_bitmask_vector(self) -> Simd { - self.0.to_bitmask_vector() - } - - /// Create a mask from a bitmask vector. - /// - /// For each bit, if it is set, the corresponding element in the mask is set to `true`. - /// - /// The bits are packed into the first N bits of the vector: - /// ``` - /// # #![feature(portable_simd)] - /// # #[cfg(feature = "as_crate")] use core_simd::simd; - /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{mask32x8, u8x8}; - /// let bitmask = u8x8::from_array([0b01000101, 0, 0, 0, 0, 0, 0, 0]); - /// assert_eq!( - /// mask32x8::from_bitmask_vector(bitmask), - /// mask32x8::from_array([true, false, true, false, false, false, true, false]), - /// ); - /// ``` - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_vector(bitmask: Simd) -> Self { - Self(mask_impl::Mask::from_bitmask_vector(bitmask)) - } - /// Find the index of the first set element. /// /// ``` diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index 96c553426ee7..db4312d5bf88 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -122,23 +122,6 @@ where unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) } } - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_bitmask_vector(self) -> Simd { - let mut bitmask = Simd::splat(0); - bitmask.as_mut_array()[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref()); - bitmask - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_vector(bitmask: Simd) -> Self { - let mut bytes = as SupportedLaneCount>::BitMask::default(); - let len = bytes.as_ref().len(); - bytes.as_mut().copy_from_slice(&bitmask.as_array()[..len]); - Self(bytes, PhantomData) - } - #[inline] pub fn to_bitmask_integer(self) -> u64 { let mut bitmask = [0u8; 8]; diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index 87f031a9f367..0c041b32761b 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -140,62 +140,6 @@ where unsafe { Mask(core::intrinsics::simd::simd_cast(self.0)) } } - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_bitmask_vector(self) -> Simd { - let mut bitmask = Simd::splat(0); - - // Safety: Bytes is the right size array - unsafe { - // Compute the bitmask - let mut bytes: as SupportedLaneCount>::BitMask = - core::intrinsics::simd::simd_bitmask(self.0); - - // LLVM assumes bit order should match endianness - if cfg!(target_endian = "big") { - for x in bytes.as_mut() { - *x = x.reverse_bits() - } - if N % 8 > 0 { - bytes.as_mut()[N / 8] >>= 8 - N % 8; - } - } - - bitmask.as_mut_array()[..bytes.as_ref().len()].copy_from_slice(bytes.as_ref()); - } - - bitmask - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_vector(bitmask: Simd) -> Self { - let mut bytes = as SupportedLaneCount>::BitMask::default(); - - // Safety: Bytes is the right size array - unsafe { - let len = bytes.as_ref().len(); - bytes.as_mut().copy_from_slice(&bitmask.as_array()[..len]); - - // LLVM assumes bit order should match endianness - if cfg!(target_endian = "big") { - for x in bytes.as_mut() { - *x = x.reverse_bits(); - } - if N % 8 > 0 { - bytes.as_mut()[N / 8] >>= 8 - N % 8; - } - } - - // Compute the regular mask - Self::from_int_unchecked(core::intrinsics::simd::simd_select_bitmask( - bytes, - Self::splat(true).to_int(), - Self::splat(false).to_int(), - )) - } - } - #[inline] unsafe fn to_bitmask_impl(self) -> U where diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs index fc6a3476b7c6..2fe0c97407f0 100644 --- a/crates/core_simd/tests/masks.rs +++ b/crates/core_simd/tests/masks.rs @@ -134,48 +134,6 @@ macro_rules! test_mask_api { cast_impl::(); cast_impl::(); } - - #[test] - fn roundtrip_bitmask_vector_conversion() { - use core_simd::simd::ToBytes; - let values = [ - true, false, false, true, false, false, true, false, - true, true, false, false, false, false, false, true, - ]; - let mask = Mask::<$type, 16>::from_array(values); - let bitmask = mask.to_bitmask_vector(); - assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b01001001, 0b10000011]); - assert_eq!(Mask::<$type, 16>::from_bitmask_vector(bitmask), mask); - } - - // rust-lang/portable-simd#379 - #[test] - fn roundtrip_bitmask_vector_conversion_small() { - use core_simd::simd::ToBytes; - let values = [ - true, false, true, true - ]; - let mask = Mask::<$type, 4>::from_array(values); - let bitmask = mask.to_bitmask_vector(); - assert_eq!(bitmask.resize::<1>(0).to_ne_bytes()[0], 0b00001101); - assert_eq!(Mask::<$type, 4>::from_bitmask_vector(bitmask), mask); - } - - /* FIXME doesn't work with non-powers-of-two, yet - // rust-lang/portable-simd#379 - #[cfg(feature = "all_lane_counts")] - #[test] - fn roundtrip_bitmask_vector_conversion_odd() { - use core_simd::simd::ToBytes; - let values = [ - true, false, true, false, true, true, false, false, false, true, true, - ]; - let mask = Mask::<$type, 11>::from_array(values); - let bitmask = mask.to_bitmask_vector(); - assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b00110101, 0b00000110]); - assert_eq!(Mask::<$type, 11>::from_bitmask_vector(bitmask), mask); - } - */ } } } From bd92b7ccf3ee7cc6018b1b0e187ea979ed099a67 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Wed, 5 Jun 2024 14:17:40 -0400 Subject: [PATCH 036/285] Fix clippy lints --- crates/core_simd/src/swizzle.rs | 4 ++-- crates/core_simd/src/vector.rs | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 39e763494395..a4b6138aa0ab 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -473,8 +473,8 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn interleave(self, other: Self) -> (Self, Self) { - // Safety: swizzles are safe for masks let (lo, hi) = self.to_int().interleave(other.to_int()); + // Safety: swizzles are safe for masks unsafe { (Self::from_int_unchecked(lo), Self::from_int_unchecked(hi)) } } @@ -502,8 +502,8 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn deinterleave(self, other: Self) -> (Self, Self) { - // Safety: swizzles are safe for masks let (even, odd) = self.to_int().deinterleave(other.to_int()); + // Safety: swizzles are safe for masks unsafe { ( Self::from_int_unchecked(even), diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 9cadc51ba29c..fc029548ecac 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -442,6 +442,9 @@ where /// /// When the element is disabled, that memory location is not accessed and the corresponding /// value from `or` is passed through. + /// + /// # Safety + /// Enabled loads must not exceed the length of `slice`. #[must_use] #[inline] pub unsafe fn load_select_unchecked( @@ -459,6 +462,9 @@ where /// /// When the element is disabled, that memory location is not accessed and the corresponding /// value from `or` is passed through. + /// + /// # Safety + /// Enabled `ptr` elements must be safe to read as if by `std::ptr::read`. #[must_use] #[inline] pub unsafe fn load_select_ptr( @@ -1214,6 +1220,7 @@ fn lane_indices() -> Simd where LaneCount: SupportedLaneCount, { + #![allow(clippy::needless_range_loop)] let mut index = [0; N]; for i in 0..N { index[i] = i; From 3c398a08201d9b927f6af09b51bbbbbb9de62e65 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Thu, 13 Jun 2024 09:58:35 -0700 Subject: [PATCH 037/285] Add arm64ec to the list of architectures to check --- .github/workflows/ci.yml | 1 + rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef6a9e611ac0..67d10e555327 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,7 @@ jobs: - i686-unknown-linux-gnu - i586-unknown-linux-gnu - aarch64-unknown-linux-gnu + - arm64ec-pc-windows-msvc - armv7-unknown-linux-gnueabihf # non-nightly since https://github.com/rust-lang/rust/pull/113274 # - mips-unknown-linux-gnu diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8f75e5572244..811fdb49cdba 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-04-29" +channel = "nightly-2024-06-13" components = ["rustfmt", "clippy", "miri", "rust-src"] From 227a9d9e06aa6fd24cb287917bf364b54821f777 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Mon, 3 Jun 2024 17:55:07 -0400 Subject: [PATCH 038/285] Fix layout of non-power-of-two length vectors --- .github/workflows/ci.yml | 28 ------------------ crates/core_simd/Cargo.toml | 3 +- crates/core_simd/src/lane_count.rs | 8 ++--- crates/core_simd/src/vector.rs | 2 +- crates/core_simd/tests/layout.rs | 35 ++++++++++++++++++++++ crates/core_simd/tests/masks.rs | 1 - crates/test_helpers/Cargo.toml | 3 -- crates/test_helpers/src/lib.rs | 47 ++++++++---------------------- 8 files changed, 52 insertions(+), 75 deletions(-) create mode 100644 crates/core_simd/tests/layout.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 67d10e555327..95ba1f2c8c90 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -245,34 +245,6 @@ jobs: - name: Test (release) run: cross test --verbose --target=${{ matrix.target }} --release - features: - name: "Test cargo features (${{ matrix.simd }} × ${{ matrix.features }})" - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - simd: - - "" - - "avx512" - features: - - "" - - "--features std" - - "--features all_lane_counts" - - "--all-features" - - steps: - - uses: actions/checkout@v2 - - name: Detect AVX512 - run: echo "CPU_FEATURE=$(lscpu | grep -o avx512[a-z]* | sed s/avx/+avx/ | tr '\n' ',' )" >> $GITHUB_ENV - - name: Check build - if: ${{ matrix.simd == '' }} - run: RUSTFLAGS="-Dwarnings" cargo test --all-targets --no-default-features ${{ matrix.features }} - - name: Check AVX - if: ${{ matrix.simd == 'avx512' && contains(env.CPU_FEATURE, 'avx512') }} - run: | - echo "Found AVX features: $CPU_FEATURE" - RUSTFLAGS="-Dwarnings -Ctarget-feature=$CPU_FEATURE" cargo test --all-targets --no-default-features ${{ matrix.features }} - miri: runs-on: ubuntu-latest steps: diff --git a/crates/core_simd/Cargo.toml b/crates/core_simd/Cargo.toml index b4a8fd70f4c0..a7a6d43b11d3 100644 --- a/crates/core_simd/Cargo.toml +++ b/crates/core_simd/Cargo.toml @@ -9,10 +9,9 @@ categories = ["hardware-support", "no-std"] license = "MIT OR Apache-2.0" [features] -default = ["as_crate"] +default = ["as_crate", "std"] as_crate = [] std = [] -all_lane_counts = [] [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen = "0.2" diff --git a/crates/core_simd/src/lane_count.rs b/crates/core_simd/src/lane_count.rs index 4cd7265ed671..280b27bc9bc6 100644 --- a/crates/core_simd/src/lane_count.rs +++ b/crates/core_simd/src/lane_count.rs @@ -33,10 +33,8 @@ macro_rules! supported_lane_count { }; } -supported_lane_count!(1, 2, 4, 8, 16, 32, 64); -#[cfg(feature = "all_lane_counts")] supported_lane_count!( - 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63 + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 ); diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index fc029548ecac..fac11d66e1b0 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -99,7 +99,7 @@ use crate::simd::{ // directly constructing an instance of the type (i.e. `let vector = Simd(array)`) should be // avoided, as it will likely become illegal on `#[repr(simd)]` structs in the future. It also // causes rustc to emit illegal LLVM IR in some cases. -#[repr(simd)] +#[repr(simd, packed)] pub struct Simd([T; N]) where LaneCount: SupportedLaneCount, diff --git a/crates/core_simd/tests/layout.rs b/crates/core_simd/tests/layout.rs new file mode 100644 index 000000000000..24114c2d261e --- /dev/null +++ b/crates/core_simd/tests/layout.rs @@ -0,0 +1,35 @@ +#![feature(portable_simd)] + +macro_rules! layout_tests { + { $($mod:ident, $ty:ty,)* } => { + $( + mod $mod { + test_helpers::test_lanes! { + fn no_padding() { + assert_eq!( + core::mem::size_of::>(), + core::mem::size_of::<[$ty; LANES]>(), + ); + } + } + } + )* + } +} + +layout_tests! { + i8, i8, + i16, i16, + i32, i32, + i64, i64, + isize, isize, + u8, u8, + u16, u16, + u32, u32, + u64, u64, + usize, usize, + f32, f32, + f64, f64, + mut_ptr, *mut (), + const_ptr, *const (), +} diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs index 2fe0c97407f0..48786d02440b 100644 --- a/crates/core_simd/tests/masks.rs +++ b/crates/core_simd/tests/masks.rs @@ -99,7 +99,6 @@ macro_rules! test_mask_api { assert_eq!(Mask::<$type, 2>::from_bitmask(bitmask), mask); } - #[cfg(feature = "all_lane_counts")] #[test] fn roundtrip_bitmask_conversion_odd() { let values = [ diff --git a/crates/test_helpers/Cargo.toml b/crates/test_helpers/Cargo.toml index 23dae7c93381..a5359b9abc84 100644 --- a/crates/test_helpers/Cargo.toml +++ b/crates/test_helpers/Cargo.toml @@ -6,6 +6,3 @@ publish = false [dependencies] proptest = { version = "0.10", default-features = false, features = ["alloc"] } - -[features] -all_lane_counts = [] diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs index c55099434c8c..3d70f5ca5fe8 100644 --- a/crates/test_helpers/src/lib.rs +++ b/crates/test_helpers/src/lib.rs @@ -539,32 +539,17 @@ macro_rules! test_lanes { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; lanes_1 1; lanes_2 2; + lanes_3 3; // test one non-power-of-2 length on miri ); #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow $crate::test_lanes_helper!( #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; lanes_4 4; - lanes_8 8; - lanes_16 16; - lanes_32 32; - lanes_64 64; - ); - - #[cfg(feature = "all_lane_counts")] - $crate::test_lanes_helper!( - // test one non-power-of-2 length on miri - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; - lanes_3 3; - ); - - #[cfg(feature = "all_lane_counts")] - #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow - $crate::test_lanes_helper!( - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; lanes_5 5; lanes_6 6; lanes_7 7; + lanes_8 8; lanes_9 9; lanes_10 10; lanes_11 11; @@ -572,6 +557,7 @@ macro_rules! test_lanes { lanes_13 13; lanes_14 14; lanes_15 15; + lanes_16 16; lanes_17 17; lanes_18 18; lanes_19 19; @@ -587,6 +573,7 @@ macro_rules! test_lanes { lanes_29 29; lanes_30 30; lanes_31 31; + lanes_32 32; lanes_33 33; lanes_34 34; lanes_35 35; @@ -618,6 +605,7 @@ macro_rules! test_lanes { lanes_61 61; lanes_62 62; lanes_63 63; + lanes_64 64; ); } )* @@ -639,36 +627,22 @@ macro_rules! test_lanes_panic { core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, $body + // test some odd and even non-power-of-2 lengths on miri $crate::test_lanes_helper!( #[should_panic]; lanes_1 1; lanes_2 2; - lanes_4 4; - ); - - #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow - $crate::test_lanes_helper!( - #[should_panic]; - lanes_8 8; - lanes_16 16; - lanes_32 32; - lanes_64 64; - ); - - #[cfg(feature = "all_lane_counts")] - $crate::test_lanes_helper!( - // test some odd and even non-power-of-2 lengths on miri - #[should_panic]; lanes_3 3; + lanes_4 4; lanes_5 5; - lanes_6 6; ); - #[cfg(feature = "all_lane_counts")] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow $crate::test_lanes_helper!( #[should_panic]; + lanes_6 6; lanes_7 7; + lanes_8 8; lanes_9 9; lanes_10 10; lanes_11 11; @@ -676,6 +650,7 @@ macro_rules! test_lanes_panic { lanes_13 13; lanes_14 14; lanes_15 15; + lanes_16 16; lanes_17 17; lanes_18 18; lanes_19 19; @@ -691,6 +666,7 @@ macro_rules! test_lanes_panic { lanes_29 29; lanes_30 30; lanes_31 31; + lanes_32 32; lanes_33 33; lanes_34 34; lanes_35 35; @@ -722,6 +698,7 @@ macro_rules! test_lanes_panic { lanes_61 61; lanes_62 62; lanes_63 63; + lanes_64 64; ); } )* From 6e084428ec9493a65f410d789220262d84d22267 Mon Sep 17 00:00:00 2001 From: gstvg <28798827+gstvg@users.noreply.github.com> Date: Wed, 19 Jun 2024 11:05:44 -0300 Subject: [PATCH 039/285] Fix Mask::all must_use attribute --- crates/core_simd/src/masks/full_masks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index 0c041b32761b..2d01946b5747 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -227,7 +227,7 @@ where } #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] + #[must_use = "method returns a new bool and does not mutate the original value"] pub fn all(self) -> bool { // Safety: use `self` as an integer vector unsafe { core::intrinsics::simd::simd_reduce_all(self.to_int()) } From 30a631731de667966a1766af0ec5432c791093c8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 20 Jun 2024 05:02:12 +1000 Subject: [PATCH 040/285] Convert some module-level `//` and `///` comments to `//!`. This makes their intent and expected location clearer. We see some examples where these comments were not clearly separate from `use` declarations, which made it hard to understand what the comment is describing. --- crates/core_simd/examples/dot_product.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/core_simd/examples/dot_product.rs b/crates/core_simd/examples/dot_product.rs index f047010a65c1..75d152ae7f0e 100644 --- a/crates/core_simd/examples/dot_product.rs +++ b/crates/core_simd/examples/dot_product.rs @@ -1,6 +1,5 @@ -// Code taken from the `packed_simd` crate -// Run this code with `cargo test --example dot_product` -//use std::iter::zip; +//! Code taken from the `packed_simd` crate. +//! Run this code with `cargo test --example dot_product`. #![feature(array_chunks)] #![feature(slice_as_chunks)] From 2937f6f2cca1a72966e8b75650c290405dc8e578 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 20 Jun 2024 05:04:30 +1000 Subject: [PATCH 041/285] Add blank lines after module-level `//!` comments. Most modules have such a blank line, but some don't. Inserting the blank line makes it clearer that the `//!` comments are describing the entire module, rather than the `use` declaration(s) that immediately follows. --- crates/core_simd/src/ops/assign.rs | 1 + crates/core_simd/src/ops/deref.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/core_simd/src/ops/assign.rs b/crates/core_simd/src/ops/assign.rs index 0e87785025a3..d21d867de26d 100644 --- a/crates/core_simd/src/ops/assign.rs +++ b/crates/core_simd/src/ops/assign.rs @@ -1,4 +1,5 @@ //! Assignment operators + use super::*; use core::ops::{AddAssign, MulAssign}; // commutative binary op-assignment use core::ops::{BitAndAssign, BitOrAssign, BitXorAssign}; // commutative bit binary op-assignment diff --git a/crates/core_simd/src/ops/deref.rs b/crates/core_simd/src/ops/deref.rs index 89a60ba11414..0ff76cfba39b 100644 --- a/crates/core_simd/src/ops/deref.rs +++ b/crates/core_simd/src/ops/deref.rs @@ -2,6 +2,7 @@ //! Ideally, Rust would take care of this itself, //! and method calls usually handle the LHS implicitly. //! But this is not the case with arithmetic ops. + use super::*; macro_rules! deref_lhs { From b17317663573d4ff7d881e3fa1d9403bbda19769 Mon Sep 17 00:00:00 2001 From: wooden-worm <93303706+wooden-worm@users.noreply.github.com> Date: Sun, 23 Jun 2024 22:58:30 -0700 Subject: [PATCH 042/285] wasm64 build with target-feature=+simd128,+atomics --- crates/core_simd/src/swizzle_dyn.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/core_simd/src/swizzle_dyn.rs b/crates/core_simd/src/swizzle_dyn.rs index 8a1079042f07..3b6388d0f275 100644 --- a/crates/core_simd/src/swizzle_dyn.rs +++ b/crates/core_simd/src/swizzle_dyn.rs @@ -30,6 +30,8 @@ where use core::arch::arm::{uint8x8_t, vtbl1_u8}; #[cfg(target_arch = "wasm32")] use core::arch::wasm32 as wasm; + #[cfg(target_arch = "wasm64")] + use core::arch::wasm64 as wasm; #[cfg(target_arch = "x86")] use core::arch::x86; #[cfg(target_arch = "x86_64")] From 048ba3e4b16f97707bacf2baed89e90dc9d6974e Mon Sep 17 00:00:00 2001 From: John Arundel Date: Mon, 15 Jul 2024 12:26:30 +0100 Subject: [PATCH 043/285] Fix doc nits Many tiny changes to stdlib doc comments to make them consistent (for example "Returns foo", rather than "Return foo", per RFC1574), adding missing periods, paragraph breaks, backticks for monospace style, and other minor nits. https://github.com/rust-lang/rfcs/blob/master/text/1574-more-api-documentation-conventions.md#appendix-a-full-conventions-text --- crates/core_simd/src/masks.rs | 12 ++++++------ crates/core_simd/src/simd/ptr/const_ptr.rs | 2 +- crates/core_simd/src/simd/ptr/mut_ptr.rs | 2 +- crates/core_simd/src/swizzle.rs | 10 +++++----- crates/core_simd/src/to_bytes.rs | 14 +++++++------- crates/core_simd/src/vector.rs | 6 +++--- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index e6e27c76a5e9..04de3a968276 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -137,7 +137,7 @@ where T: MaskElement, LaneCount: SupportedLaneCount, { - /// Construct a mask by setting all elements to the given value. + /// Constructs a mask by setting all elements to the given value. #[inline] pub fn splat(value: bool) -> Self { Self(mask_impl::Mask::splat(value)) @@ -288,7 +288,7 @@ where self.0.all() } - /// Create a bitmask from a mask. + /// Creates a bitmask from a mask. /// /// Each bit is set if the corresponding element in the mask is `true`. /// If the mask contains more than 64 elements, the bitmask is truncated to the first 64. @@ -298,7 +298,7 @@ where self.0.to_bitmask_integer() } - /// Create a mask from a bitmask. + /// Creates a mask from a bitmask. /// /// For each bit, if it is set, the corresponding element in the mask is set to `true`. /// If the mask contains more than 64 elements, the remainder are set to `false`. @@ -308,7 +308,7 @@ where Self(mask_impl::Mask::from_bitmask_integer(bitmask)) } - /// Create a bitmask vector from a mask. + /// Creates a bitmask vector from a mask. /// /// Each bit is set if the corresponding element in the mask is `true`. /// The remaining bits are unset. @@ -328,7 +328,7 @@ where self.0.to_bitmask_vector() } - /// Create a mask from a bitmask vector. + /// Creates a mask from a bitmask vector. /// /// For each bit, if it is set, the corresponding element in the mask is set to `true`. /// @@ -350,7 +350,7 @@ where Self(mask_impl::Mask::from_bitmask_vector(bitmask)) } - /// Find the index of the first set element. + /// Finds the index of the first set element. /// /// ``` /// # #![feature(portable_simd)] diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index cbffbc564cfe..be635ea640b8 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -54,7 +54,7 @@ pub trait SimdConstPtr: Copy + Sealed { /// [`Self::with_exposed_provenance`] and returns the "address" portion. fn expose_provenance(self) -> Self::Usize; - /// Convert an address back to a pointer, picking up a previously "exposed" provenance. + /// Converts an address back to a pointer, picking up a previously "exposed" provenance. /// /// Equivalent to calling [`core::ptr::with_exposed_provenance`] on each element. fn with_exposed_provenance(addr: Self::Usize) -> Self; diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index 6bc6ca3ac42d..f6823a949e32 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -51,7 +51,7 @@ pub trait SimdMutPtr: Copy + Sealed { /// [`Self::with_exposed_provenance`] and returns the "address" portion. fn expose_provenance(self) -> Self::Usize; - /// Convert an address back to a pointer, picking up a previously "exposed" provenance. + /// Converts an address back to a pointer, picking up a previously "exposed" provenance. /// /// Equivalent to calling [`core::ptr::with_exposed_provenance_mut`] on each element. fn with_exposed_provenance(addr: Self::Usize) -> Self; diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 71110bb28201..2f4f777b20e2 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -69,12 +69,12 @@ pub macro simd_swizzle { } } -/// Create a vector from the elements of another vector. +/// Creates a vector from the elements of another vector. pub trait Swizzle { /// Map from the elements of the input vector to the output vector. const INDEX: [usize; N]; - /// Create a new vector from the elements of `vector`. + /// Creates a new vector from the elements of `vector`. /// /// Lane `i` of the output is `vector[Self::INDEX[i]]`. #[inline] @@ -109,7 +109,7 @@ pub trait Swizzle { } } - /// Create a new vector from the elements of `first` and `second`. + /// Creates a new vector from the elements of `first` and `second`. /// /// Lane `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of /// `first` and `second`. @@ -145,7 +145,7 @@ pub trait Swizzle { } } - /// Create a new mask from the elements of `mask`. + /// Creates a new mask from the elements of `mask`. /// /// Element `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of /// `first` and `second`. @@ -161,7 +161,7 @@ pub trait Swizzle { unsafe { Mask::from_int_unchecked(Self::swizzle(mask.to_int())) } } - /// Create a new mask from the elements of `first` and `second`. + /// Creates a new mask from the elements of `first` and `second`. /// /// Element `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of /// `first` and `second`. diff --git a/crates/core_simd/src/to_bytes.rs b/crates/core_simd/src/to_bytes.rs index 222526c4ab30..4833ea9e1136 100644 --- a/crates/core_simd/src/to_bytes.rs +++ b/crates/core_simd/src/to_bytes.rs @@ -10,7 +10,7 @@ mod sealed { } use sealed::Sealed; -/// Convert SIMD vectors to vectors of bytes +/// Converts SIMD vectors to vectors of bytes pub trait ToBytes: Sealed { /// This type, reinterpreted as bytes. type Bytes: Copy @@ -22,26 +22,26 @@ pub trait ToBytes: Sealed { + SimdUint + 'static; - /// Return the memory representation of this integer as a byte array in native byte + /// Returns the memory representation of this integer as a byte array in native byte /// order. fn to_ne_bytes(self) -> Self::Bytes; - /// Return the memory representation of this integer as a byte array in big-endian + /// Returns the memory representation of this integer as a byte array in big-endian /// (network) byte order. fn to_be_bytes(self) -> Self::Bytes; - /// Return the memory representation of this integer as a byte array in little-endian + /// Returns the memory representation of this integer as a byte array in little-endian /// byte order. fn to_le_bytes(self) -> Self::Bytes; - /// Create a native endian integer value from its memory representation as a byte array + /// Creates a native endian integer value from its memory representation as a byte array /// in native endianness. fn from_ne_bytes(bytes: Self::Bytes) -> Self; - /// Create an integer value from its representation as a byte array in big endian. + /// Creates an integer value from its representation as a byte array in big endian. fn from_be_bytes(bytes: Self::Bytes) -> Self; - /// Create an integer value from its representation as a byte array in little endian. + /// Creates an integer value from its representation as a byte array in little endian. fn from_le_bytes(bytes: Self::Bytes) -> Self; } diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 8dbdfc0e1fe0..3e2391691496 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -187,7 +187,7 @@ where unsafe { &mut *(self as *mut Self as *mut [T; N]) } } - /// Load a vector from an array of `T`. + /// Loads a vector from an array of `T`. /// /// This function is necessary since `repr(simd)` has padding for non-power-of-2 vectors (at the time of writing). /// With padding, `read_unaligned` will read past the end of an array of N elements. @@ -567,7 +567,7 @@ where unsafe { Self::gather_select_ptr(ptrs, enable, or) } } - /// Read elementwise from pointers into a SIMD vector. + /// Reads elementwise from pointers into a SIMD vector. /// /// # Safety /// @@ -808,7 +808,7 @@ where } } - /// Write pointers elementwise into a SIMD vector. + /// Writes pointers elementwise into a SIMD vector. /// /// # Safety /// From f33640664ff93f250f8a520b6252ff66625eceec Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Thu, 6 Jun 2024 21:09:37 -0400 Subject: [PATCH 044/285] Add aarch64 workarounds --- crates/core_simd/src/ops.rs | 43 ++++++++++++++++++++++++-- crates/core_simd/src/simd/num/float.rs | 28 +++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index d8e10eeaa1a2..dffcc1cad778 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -96,8 +96,47 @@ macro_rules! int_divrem_guard { // Nice base case to make it easy to const-fold away the other branch. $rhs }; - // Safety: $lhs and rhs are vectors - unsafe { core::intrinsics::simd::$simd_call($lhs, rhs) } + + // aarch64 fails for arbitrary `v % 0` for non-powers-of-two + #[cfg(target_arch = "aarch64")] + { + const { assert!(Self::LEN <= 64) }; + if Self::LEN == 1 { + // Safety: $lhs and rhs are vectors + let x: Simd::<_, 1> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<1>(Default::default()), rhs.resize::<1>(Default::default())) }; + x.resize(Default::default()) + } else if Self::LEN <= 2 { + // Safety: $lhs and rhs are vectors + let x: Simd::<_, 2> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<2>(Default::default()), rhs.resize::<2>(Default::default())) }; + x.resize(Default::default()) + } else if Self::LEN <= 4 { + // Safety: $lhs and rhs are vectors + let x: Simd::<_, 4> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<4>(Default::default()), rhs.resize::<4>(Default::default())) }; + x.resize(Default::default()) + } else if Self::LEN <= 8 { + // Safety: $lhs and rhs are vectors + let x: Simd::<_, 8> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<8>(Default::default()), rhs.resize::<8>(Default::default())) }; + x.resize(Default::default()) + } else if Self::LEN <= 16 { + // Safety: $lhs and rhs are vectors + let x: Simd::<_, 16> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<16>(Default::default()), rhs.resize::<16>(Default::default())) }; + x.resize(Default::default()) + } else if Self::LEN <= 32 { + // Safety: $lhs and rhs are vectors + let x: Simd::<_, 32> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<32>(Default::default()), rhs.resize::<32>(Default::default())) }; + x.resize(Default::default()) + } else { + // Safety: $lhs and rhs are vectors + let x: Simd::<_, 64> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<64>(Default::default()), rhs.resize::<64>(Default::default())) }; + x.resize(Default::default()) + } + } + + #[cfg(not(target_arch = "aarch64"))] + { + // Safety: $lhs and rhs are vectors + unsafe { core::intrinsics::simd::$simd_call($lhs, rhs) } + } } }; } diff --git a/crates/core_simd/src/simd/num/float.rs b/crates/core_simd/src/simd/num/float.rs index 59e43851ea8d..48bfca32d53e 100644 --- a/crates/core_simd/src/simd/num/float.rs +++ b/crates/core_simd/src/simd/num/float.rs @@ -255,6 +255,7 @@ macro_rules! impl_trait { type Bits = Simd<$bits_ty, N>; type Cast = Simd; + #[cfg(not(target_arch = "aarch64"))] #[inline] fn cast(self) -> Self::Cast { @@ -262,6 +263,33 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_as(self) } } + // https://github.com/llvm/llvm-project/issues/94694 + #[cfg(target_arch = "aarch64")] + #[inline] + fn cast(self) -> Self::Cast + { + const { assert!(N <= 64) }; + if N <= 2 || N == 4 || N == 8 || N == 16 || N == 32 || N == 64 { + // Safety: supported types are guaranteed by SimdCast + unsafe { core::intrinsics::simd::simd_as(self) } + } else if N < 4 { + let x = self.resize::<4>(Default::default()).cast(); + x.resize::(x[0]) + } else if N < 8 { + let x = self.resize::<8>(Default::default()).cast(); + x.resize::(x[0]) + } else if N < 16 { + let x = self.resize::<16>(Default::default()).cast(); + x.resize::(x[0]) + } else if N < 32 { + let x = self.resize::<32>(Default::default()).cast(); + x.resize::(x[0]) + } else { + let x = self.resize::<64>(Default::default()).cast(); + x.resize::(x[0]) + } + } + #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces unsafe fn to_int_unchecked(self) -> Self::Cast From 9f7fec87d7aafb046f78c9ac2e15fa7ca6ab95b8 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Wed, 7 Aug 2024 00:18:25 -0400 Subject: [PATCH 045/285] Perform aarch64 div/rem as scalar op --- crates/core_simd/src/ops.rs | 42 ++++++++----------------------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index dffcc1cad778..2aad690d83db 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -77,7 +77,7 @@ macro_rules! int_divrem_guard { ( $lhs:ident, $rhs:ident, { const PANIC_ZERO: &'static str = $zero:literal; - $simd_call:ident + $simd_call:ident, $op:tt }, $int:ident ) => { if $rhs.simd_eq(Simd::splat(0 as _)).any() { @@ -97,39 +97,15 @@ macro_rules! int_divrem_guard { $rhs }; - // aarch64 fails for arbitrary `v % 0` for non-powers-of-two + // aarch64 div fails for arbitrary `v % 0`, mod fails when rhs is MIN, for non-powers-of-two + // these operations aren't vectorized on aarch64 anyway #[cfg(target_arch = "aarch64")] { - const { assert!(Self::LEN <= 64) }; - if Self::LEN == 1 { - // Safety: $lhs and rhs are vectors - let x: Simd::<_, 1> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<1>(Default::default()), rhs.resize::<1>(Default::default())) }; - x.resize(Default::default()) - } else if Self::LEN <= 2 { - // Safety: $lhs and rhs are vectors - let x: Simd::<_, 2> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<2>(Default::default()), rhs.resize::<2>(Default::default())) }; - x.resize(Default::default()) - } else if Self::LEN <= 4 { - // Safety: $lhs and rhs are vectors - let x: Simd::<_, 4> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<4>(Default::default()), rhs.resize::<4>(Default::default())) }; - x.resize(Default::default()) - } else if Self::LEN <= 8 { - // Safety: $lhs and rhs are vectors - let x: Simd::<_, 8> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<8>(Default::default()), rhs.resize::<8>(Default::default())) }; - x.resize(Default::default()) - } else if Self::LEN <= 16 { - // Safety: $lhs and rhs are vectors - let x: Simd::<_, 16> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<16>(Default::default()), rhs.resize::<16>(Default::default())) }; - x.resize(Default::default()) - } else if Self::LEN <= 32 { - // Safety: $lhs and rhs are vectors - let x: Simd::<_, 32> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<32>(Default::default()), rhs.resize::<32>(Default::default())) }; - x.resize(Default::default()) - } else { - // Safety: $lhs and rhs are vectors - let x: Simd::<_, 64> = unsafe { core::intrinsics::simd::$simd_call($lhs.resize::<64>(Default::default()), rhs.resize::<64>(Default::default())) }; - x.resize(Default::default()) + let mut out = Simd::splat(0 as _); + for i in 0..Self::LEN { + out[i] = $lhs[i] $op rhs[i]; } + out } #[cfg(not(target_arch = "aarch64"))] @@ -244,14 +220,14 @@ for_base_ops! { impl Div::div { int_divrem_guard { const PANIC_ZERO: &'static str = "attempt to divide by zero"; - simd_div + simd_div, / } } impl Rem::rem { int_divrem_guard { const PANIC_ZERO: &'static str = "attempt to calculate the remainder with a divisor of zero"; - simd_rem + simd_rem, % } } From a49f77eb38d252e946d60dde3c4801ec8a2e1ac4 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Wed, 7 Aug 2024 01:24:30 -0400 Subject: [PATCH 046/285] Swap lanes tested on miri --- crates/test_helpers/src/lib.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs index 3d70f5ca5fe8..a6559de0e765 100644 --- a/crates/test_helpers/src/lib.rs +++ b/crates/test_helpers/src/lib.rs @@ -539,7 +539,9 @@ macro_rules! test_lanes { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; lanes_1 1; lanes_2 2; - lanes_3 3; // test one non-power-of-2 length on miri + lanes_3 3; + + lanes_6 6; ); #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow @@ -547,7 +549,7 @@ macro_rules! test_lanes { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; lanes_4 4; lanes_5 5; - lanes_6 6; + lanes_7 7; lanes_8 8; lanes_9 9; @@ -633,14 +635,16 @@ macro_rules! test_lanes_panic { lanes_1 1; lanes_2 2; lanes_3 3; - lanes_4 4; - lanes_5 5; + + lanes_6 6; ); #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow $crate::test_lanes_helper!( #[should_panic]; - lanes_6 6; + lanes_4 4; + lanes_5 5; + lanes_7 7; lanes_8 8; lanes_9 9; From 751c3b5978b8cbc1ce0c789dd68824f3312f7447 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Wed, 7 Aug 2024 21:25:02 -0400 Subject: [PATCH 047/285] Update crates/test_helpers/src/lib.rs Co-authored-by: Ralf Jung --- crates/test_helpers/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs index a6559de0e765..24efa256c30b 100644 --- a/crates/test_helpers/src/lib.rs +++ b/crates/test_helpers/src/lib.rs @@ -539,6 +539,9 @@ macro_rules! test_lanes { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; lanes_1 1; lanes_2 2; + // Cover an odd and an even non-power-of-2 length in Miri. + // (Even non-power-of-2 vectors have alignment between element + // and vector size, so we want to cover that case as well.) lanes_3 3; lanes_6 6; From 7f6a981b26ca13f1295fa3fe10b21651a19431b2 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Wed, 7 Aug 2024 23:17:09 -0400 Subject: [PATCH 048/285] Disable testing most lanes to improve CI times --- crates/test_helpers/src/lib.rs | 160 ++++++++++++++++----------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs index 24efa256c30b..197c920e11ea 100644 --- a/crates/test_helpers/src/lib.rs +++ b/crates/test_helpers/src/lib.rs @@ -564,51 +564,51 @@ macro_rules! test_lanes { lanes_15 15; lanes_16 16; lanes_17 17; - lanes_18 18; - lanes_19 19; - lanes_20 20; - lanes_21 21; - lanes_22 22; - lanes_23 23; + //lanes_18 18; + //lanes_19 19; + //lanes_20 20; + //lanes_21 21; + //lanes_22 22; + //lanes_23 23; lanes_24 24; - lanes_25 25; - lanes_26 26; - lanes_27 27; - lanes_28 28; - lanes_29 29; - lanes_30 30; - lanes_31 31; + //lanes_25 25; + //lanes_26 26; + //lanes_27 27; + //lanes_28 28; + //lanes_29 29; + //lanes_30 30; + //lanes_31 31; lanes_32 32; - lanes_33 33; - lanes_34 34; - lanes_35 35; - lanes_36 36; - lanes_37 37; - lanes_38 38; - lanes_39 39; - lanes_40 40; - lanes_41 41; - lanes_42 42; - lanes_43 43; - lanes_44 44; - lanes_45 45; - lanes_46 46; + //lanes_33 33; + //lanes_34 34; + //lanes_35 35; + //lanes_36 36; + //lanes_37 37; + //lanes_38 38; + //lanes_39 39; + //lanes_40 40; + //lanes_41 41; + //lanes_42 42; + //lanes_43 43; + //lanes_44 44; + //lanes_45 45; + //lanes_46 46; lanes_47 47; - lanes_48 48; - lanes_49 49; - lanes_50 50; - lanes_51 51; - lanes_52 52; - lanes_53 53; - lanes_54 54; - lanes_55 55; + //lanes_48 48; + //lanes_49 49; + //lanes_50 50; + //lanes_51 51; + //lanes_52 52; + //lanes_53 53; + //lanes_54 54; + //lanes_55 55; lanes_56 56; lanes_57 57; - lanes_58 58; - lanes_59 59; - lanes_60 60; - lanes_61 61; - lanes_62 62; + //lanes_58 58; + //lanes_59 59; + //lanes_60 60; + //lanes_61 61; + //lanes_62 62; lanes_63 63; lanes_64 64; ); @@ -659,51 +659,51 @@ macro_rules! test_lanes_panic { lanes_15 15; lanes_16 16; lanes_17 17; - lanes_18 18; - lanes_19 19; - lanes_20 20; - lanes_21 21; - lanes_22 22; - lanes_23 23; + //lanes_18 18; + //lanes_19 19; + //lanes_20 20; + //lanes_21 21; + //lanes_22 22; + //lanes_23 23; lanes_24 24; - lanes_25 25; - lanes_26 26; - lanes_27 27; - lanes_28 28; - lanes_29 29; - lanes_30 30; - lanes_31 31; + //lanes_25 25; + //lanes_26 26; + //lanes_27 27; + //lanes_28 28; + //lanes_29 29; + //lanes_30 30; + //lanes_31 31; lanes_32 32; - lanes_33 33; - lanes_34 34; - lanes_35 35; - lanes_36 36; - lanes_37 37; - lanes_38 38; - lanes_39 39; - lanes_40 40; - lanes_41 41; - lanes_42 42; - lanes_43 43; - lanes_44 44; - lanes_45 45; - lanes_46 46; + //lanes_33 33; + //lanes_34 34; + //lanes_35 35; + //lanes_36 36; + //lanes_37 37; + //lanes_38 38; + //lanes_39 39; + //lanes_40 40; + //lanes_41 41; + //lanes_42 42; + //lanes_43 43; + //lanes_44 44; + //lanes_45 45; + //lanes_46 46; lanes_47 47; - lanes_48 48; - lanes_49 49; - lanes_50 50; - lanes_51 51; - lanes_52 52; - lanes_53 53; - lanes_54 54; - lanes_55 55; + //lanes_48 48; + //lanes_49 49; + //lanes_50 50; + //lanes_51 51; + //lanes_52 52; + //lanes_53 53; + //lanes_54 54; + //lanes_55 55; lanes_56 56; lanes_57 57; - lanes_58 58; - lanes_59 59; - lanes_60 60; - lanes_61 61; - lanes_62 62; + //lanes_58 58; + //lanes_59 59; + //lanes_60 60; + //lanes_61 61; + //lanes_62 62; lanes_63 63; lanes_64 64; ); From 2a3b8ad2238c493fd73d9577f8a5bf84b0a41618 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Thu, 8 Aug 2024 00:57:49 -0400 Subject: [PATCH 049/285] Reduce proptest iterations --- .github/workflows/ci.yml | 5 +++++ Cross.toml | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 Cross.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95ba1f2c8c90..8b5213376d88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,7 @@ on: env: CARGO_NET_RETRY: 10 RUSTUP_MAX_RETRIES: 10 + PROPTEST_CASES: 64 jobs: rustfmt: @@ -181,6 +182,8 @@ jobs: cross-tests: name: "${{ matrix.target_feature }} on ${{ matrix.target }} (via cross)" runs-on: ubuntu-latest + env: + PROPTEST_CASES: 16 strategy: fail-fast: false @@ -247,6 +250,8 @@ jobs: miri: runs-on: ubuntu-latest + env: + PROPTEST_CASES: 16 steps: - uses: actions/checkout@v2 - name: Test (Miri) diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 000000000000..d21e76b92dd1 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,2 @@ +[build.env] +passthrough = ["PROPTEST_CASES"] From d7d060a0bfb94fead7b3a59fc67fc154f878a21b Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sat, 10 Aug 2024 00:47:08 -0400 Subject: [PATCH 050/285] Build test dependencies with optimization --- Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index d1732aaec2f9..21d4584a9f4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,9 @@ members = [ "crates/std_float", "crates/test_helpers", ] + +[profile.test.package."*"] +opt-level = 2 + +[profile.test.package.test_helpers] +opt-level = 2 From c992db6506c3d384b020bc19ff7a24849e96521e Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Sat, 24 Aug 2024 23:03:27 -0400 Subject: [PATCH 051/285] Add `abs_diff` function to `SimdInt` and `SimdUint` traits Implement `abs_diff` for signed and unsigned integer vectors --- crates/core_simd/src/simd/num/int.rs | 27 +++++++++++++++++++++++++-- crates/core_simd/src/simd/num/uint.rs | 25 ++++++++++++++++++++++++- crates/core_simd/tests/ops_macros.rs | 16 ++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/crates/core_simd/src/simd/num/int.rs b/crates/core_simd/src/simd/num/int.rs index d7598d9ceaf9..5561fe20e61f 100644 --- a/crates/core_simd/src/simd/num/int.rs +++ b/crates/core_simd/src/simd/num/int.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - cmp::SimdPartialOrd, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, + cmp::SimdOrd, cmp::SimdPartialOrd, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, }; @@ -70,11 +70,27 @@ pub trait SimdInt: Copy + Sealed { /// # #[cfg(not(feature = "as_crate"))] use core::simd; /// # use simd::prelude::*; /// use core::i32::{MIN, MAX}; - /// let xs = Simd::from_array([MIN, MIN +1, -5, 0]); + /// let xs = Simd::from_array([MIN, MIN + 1, -5, 0]); /// assert_eq!(xs.abs(), Simd::from_array([MIN, MAX, 5, 0])); /// ``` fn abs(self) -> Self; + /// Lanewise absolute difference. + /// Every element becomes the absolute difference of `self` and `second`. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::prelude::*; + /// use core::i32::{MIN, MAX}; + /// let a = Simd::from_array([MIN, MAX, 100, -100]); + /// let b = Simd::from_array([MAX, MIN, -80, -120]); + /// assert_eq!(a.abs_diff(b), Simd::from_array([u32::MAX, u32::MAX, 180, 20])); + /// ``` + fn abs_diff(self, second: Self) -> Self::Unsigned; + /// Lanewise saturating absolute value, implemented in Rust. /// As abs(), except the MIN value becomes MAX instead of itself. /// @@ -259,6 +275,13 @@ macro_rules! impl_trait { (self^m) - m } + #[inline] + fn abs_diff(self, second: Self) -> Self::Unsigned { + let max = self.simd_max(second); + let min = self.simd_min(second); + (max - min).cast() + } + #[inline] fn saturating_abs(self) -> Self { // arith shift for -1 or 0 mask based on sign bit, giving 2s complement diff --git a/crates/core_simd/src/simd/num/uint.rs b/crates/core_simd/src/simd/num/uint.rs index 53dd97f501c6..7cc1b5bf8b61 100644 --- a/crates/core_simd/src/simd/num/uint.rs +++ b/crates/core_simd/src/simd/num/uint.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; +use crate::simd::{cmp::SimdOrd, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; /// Operations on SIMD vectors of unsigned integers. pub trait SimdUint: Copy + Sealed { @@ -57,6 +57,22 @@ pub trait SimdUint: Copy + Sealed { /// assert_eq!(sat, Simd::splat(0)); fn saturating_sub(self, second: Self) -> Self; + /// Lanewise absolute difference. + /// Every element becomes the absolute difference of `self` and `second`. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::prelude::*; + /// use core::u32::MAX; + /// let a = Simd::from_array([0, MAX, 100, 20]); + /// let b = Simd::from_array([MAX, 0, 80, 200]); + /// assert_eq!(a.abs_diff(b), Simd::from_array([MAX, MAX, 20, 180])); + /// ``` + fn abs_diff(self, second: Self) -> Self; + /// Returns the sum of the elements of the vector, with wrapping addition. fn reduce_sum(self) -> Self::Scalar; @@ -138,6 +154,13 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_saturating_sub(self, second) } } + #[inline] + fn abs_diff(self, second: Self) -> Self { + let max = self.simd_max(second); + let min = self.simd_min(second); + max - min + } + #[inline] fn reduce_sum(self) -> Self::Scalar { // Safety: `self` is an integer vector diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index aa565a137527..6503cc00053c 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -307,6 +307,14 @@ macro_rules! impl_signed_tests { assert_eq!(a % b, Vector::::splat(0)); } + fn abs_diff() { + test_helpers::test_binary_elementwise( + &Vector::::abs_diff, + &Scalar::abs_diff, + &|_, _| true, + ) + } + fn simd_min() { use core_simd::simd::cmp::SimdOrd; let a = Vector::::splat(Scalar::MIN); @@ -419,6 +427,14 @@ macro_rules! impl_unsigned_tests { &|_| true, ); } + + fn abs_diff() { + test_helpers::test_binary_elementwise( + &Vector::::abs_diff, + &Scalar::abs_diff, + &|_, _| true, + ) + } } impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add); From d5abbfa9786552ca516574bd4aa44a39665919ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=20V=C3=AEjdea?= Date: Sun, 25 Aug 2024 23:11:21 +0300 Subject: [PATCH 052/285] Fix avx512vbmi swizzle_dyn implementation --- crates/core_simd/src/swizzle_dyn.rs | 30 +++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/crates/core_simd/src/swizzle_dyn.rs b/crates/core_simd/src/swizzle_dyn.rs index 8a1079042f07..eaf297ba3e35 100644 --- a/crates/core_simd/src/swizzle_dyn.rs +++ b/crates/core_simd/src/swizzle_dyn.rs @@ -60,12 +60,30 @@ where #[cfg(all(target_feature = "avx2", not(target_feature = "avx512vbmi")))] 32 => transize(avx2_pshufb, self, idxs), #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))] - 32 => transize(x86::_mm256_permutexvar_epi8, zeroing_idxs(idxs), self), - // Notable absence: avx512bw shuffle - // If avx512bw is available, odds of avx512vbmi are good - // FIXME: initial AVX512VBMI variant didn't actually pass muster - // #[cfg(target_feature = "avx512vbmi")] - // 64 => transize(x86::_mm512_permutexvar_epi8, self, idxs), + 32 => { + // Unlike vpshufb, vpermb doesn't zero out values in the result based on the index high bit + let swizzler = |bytes, idxs| { + let mask = x86::_mm256_cmp_epu8_mask::<{ x86::_MM_CMPINT_LT }>( + idxs, + Simd::::splat(N as u8).into(), + ); + x86::_mm256_maskz_permutexvar_epi8(mask, idxs, bytes) + }; + transize(swizzler, self, idxs) + } + // Notable absence: avx512bw pshufb shuffle + #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))] + 64 => { + // Unlike vpshufb, vpermb doesn't zero out values in the result based on the index high bit + let swizzler = |bytes, idxs| { + let mask = x86::_mm512_cmp_epu8_mask::<{ x86::_MM_CMPINT_LT }>( + idxs, + Simd::::splat(N as u8).into(), + ); + x86::_mm512_maskz_permutexvar_epi8(mask, idxs, bytes) + }; + transize(swizzler, self, idxs) + } _ => { let mut array = [0; N]; for (i, k) in idxs.to_array().into_iter().enumerate() { From bbcfdb5cbf201f7122c1665881bb28d78d2ee13b Mon Sep 17 00:00:00 2001 From: gstvg <28798827+gstvg@users.noreply.github.com> Date: Thu, 29 Aug 2024 01:53:13 -0300 Subject: [PATCH 053/285] fix: swizzle_mask docs --- crates/core_simd/src/swizzle.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index a4b6138aa0ab..e0edb2cf10a6 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -147,8 +147,7 @@ pub trait Swizzle { /// Create a new mask from the elements of `mask`. /// - /// Element `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of - /// `first` and `second`. + /// Element `i` of the output is `mask[Self::INDEX[i]]`. #[inline] #[must_use = "method returns a new mask and does not mutate the original inputs"] fn swizzle_mask(mask: Mask) -> Mask From c535320c7ec63858bb823ac889a9ae77d646ab2c Mon Sep 17 00:00:00 2001 From: Laiho Date: Sat, 31 Aug 2024 00:28:39 +0300 Subject: [PATCH 054/285] fix typo in cmp_ne docs --- crates/core_simd/src/simd/cmp/eq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/src/simd/cmp/eq.rs b/crates/core_simd/src/simd/cmp/eq.rs index 5b4615ce51d7..93989ce91b89 100644 --- a/crates/core_simd/src/simd/cmp/eq.rs +++ b/crates/core_simd/src/simd/cmp/eq.rs @@ -12,7 +12,7 @@ pub trait SimdPartialEq { #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_eq(self, other: Self) -> Self::Mask; - /// Test if each element is equal to the corresponding element in `other`. + /// Test if each element is not equal to the corresponding element in `other`. #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_ne(self, other: Self) -> Self::Mask; } From b6222cb8e432d2ade0a5b4ec187d420abe9ae113 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Mon, 9 Sep 2024 06:46:12 -0700 Subject: [PATCH 055/285] ci: bump actions/checkout to v4 --- .github/workflows/ci.yml | 14 +++++++------- .github/workflows/doc.yml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b5213376d88..e4a3da359266 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Run rustfmt run: cargo fmt --all -- --check @@ -46,7 +46,7 @@ jobs: - wasm32-unknown-unknown steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust run: rustup target add ${{ matrix.target }} - name: Run Clippy @@ -84,7 +84,7 @@ jobs: # avx512vl, but occasionally doesn't. Maybe one day we can enable it. steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust run: rustup target add ${{ matrix.target }} @@ -139,7 +139,7 @@ jobs: - aarch64-apple-darwin - x86_64-apple-darwin steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust run: rustup target add ${{ matrix.target }} @@ -167,7 +167,7 @@ jobs: - { name: default, RUSTFLAGS: "" } - { name: simd128, RUSTFLAGS: "-C target-feature=+simd128" } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install wasm-pack run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - name: Test (debug) @@ -211,7 +211,7 @@ jobs: # - { target: riscv64gc-unknown-linux-gnu, target_feature: "+v,+zvl128b" } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust run: rustup target add ${{ matrix.target }} @@ -253,6 +253,6 @@ jobs: env: PROPTEST_CASES: 16 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Test (Miri) run: cargo miri test diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 9d1fa66ccb59..22c2cb3f67f1 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Setup Rust run: | From 27e2832e87aff7b19b7695d21d347f476fc83da3 Mon Sep 17 00:00:00 2001 From: Andrew Scull Date: Wed, 11 Sep 2024 17:12:37 +0000 Subject: [PATCH 056/285] Add count_ones() and count_zeros() Implement on integer types using the simd_ctpop intrinsic. --- crates/core_simd/src/simd/num/int.rs | 16 ++++++++++++++++ crates/core_simd/src/simd/num/uint.rs | 17 +++++++++++++++++ crates/core_simd/tests/ops_macros.rs | 16 ++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/crates/core_simd/src/simd/num/int.rs b/crates/core_simd/src/simd/num/int.rs index 5561fe20e61f..3a51235ff954 100644 --- a/crates/core_simd/src/simd/num/int.rs +++ b/crates/core_simd/src/simd/num/int.rs @@ -219,6 +219,12 @@ pub trait SimdInt: Copy + Sealed { /// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc. fn reverse_bits(self) -> Self; + /// Returns the number of ones in the binary representation of each element. + fn count_ones(self) -> Self::Unsigned; + + /// Returns the number of zeros in the binary representation of each element. + fn count_zeros(self) -> Self::Unsigned; + /// Returns the number of leading zeros in the binary representation of each element. fn leading_zeros(self) -> Self::Unsigned; @@ -367,6 +373,16 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_bitreverse(self) } } + #[inline] + fn count_ones(self) -> Self::Unsigned { + self.cast::<$unsigned>().count_ones() + } + + #[inline] + fn count_zeros(self) -> Self::Unsigned { + self.cast::<$unsigned>().count_zeros() + } + #[inline] fn leading_zeros(self) -> Self::Unsigned { self.cast::<$unsigned>().leading_zeros() diff --git a/crates/core_simd/src/simd/num/uint.rs b/crates/core_simd/src/simd/num/uint.rs index 7cc1b5bf8b61..1ab2d8c7b731 100644 --- a/crates/core_simd/src/simd/num/uint.rs +++ b/crates/core_simd/src/simd/num/uint.rs @@ -101,6 +101,12 @@ pub trait SimdUint: Copy + Sealed { /// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc. fn reverse_bits(self) -> Self; + /// Returns the number of ones in the binary representation of each element. + fn count_ones(self) -> Self; + + /// Returns the number of zeros in the binary representation of each element. + fn count_zeros(self) -> Self; + /// Returns the number of leading zeros in the binary representation of each element. fn leading_zeros(self) -> Self; @@ -215,6 +221,17 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_bitreverse(self) } } + #[inline] + fn count_ones(self) -> Self { + // Safety: `self` is an integer vector + unsafe { core::intrinsics::simd::simd_ctpop(self) } + } + + #[inline] + fn count_zeros(self) -> Self { + (!self).count_ones() + } + #[inline] fn leading_zeros(self) -> Self { // Safety: `self` is an integer vector diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index 6503cc00053c..6e64bfcb424e 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -216,6 +216,22 @@ macro_rules! impl_common_integer_tests { ) } + fn count_ones() { + test_helpers::test_unary_elementwise( + &$vector::::count_ones, + &|x| x.count_ones() as _, + &|_| true, + ) + } + + fn count_zeros() { + test_helpers::test_unary_elementwise( + &$vector::::count_zeros, + &|x| x.count_zeros() as _, + &|_| true, + ) + } + fn leading_zeros() { test_helpers::test_unary_elementwise( &$vector::::leading_zeros, From 00c3b6d68e1a50bb9323b13ae5cf1ab3820da56d Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 11 Sep 2024 19:29:51 -0700 Subject: [PATCH 057/285] Use -0.0 as the neutral additive float -0.0 + 0.0 is 0.0 -0.0 + -0.0 is -0.0 Thus, the float additive-zero is actually -0.0, not its positive cousin. This change aligns with a recent change to the impl of Sum for floats, in rust-lang/rust@490818851860fb257e23fe7aa0ee32eaffc4ba40 and accordingly we also have to use the latest toolchain for our tests now. --- crates/core_simd/src/simd/num/float.rs | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/simd/num/float.rs b/crates/core_simd/src/simd/num/float.rs index 48bfca32d53e..79954b937b39 100644 --- a/crates/core_simd/src/simd/num/float.rs +++ b/crates/core_simd/src/simd/num/float.rs @@ -419,7 +419,7 @@ macro_rules! impl_trait { self.as_array().iter().sum() } else { // Safety: `self` is a float vector - unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, 0.) } + unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, -0.) } } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 811fdb49cdba..d6239a040a5b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-06-13" +channel = "nightly-2024-09-11" components = ["rustfmt", "clippy", "miri", "rust-src"] From c080ba539f60f8a3888ddebc52c3db157dd1b2d1 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 11 Sep 2024 20:21:09 -0700 Subject: [PATCH 058/285] Exempt Arm v7 Neon from subnormal-related tests --- crates/core_simd/tests/ops_macros.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index 6e64bfcb424e..6de78f51e59d 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -527,6 +527,9 @@ macro_rules! impl_float_tests { } fn is_normal() { + // Arm v7 Neon violates float opsem re: subnormals, see + // https://github.com/rust-lang/portable-simd/issues/439 + #[cfg(not(target_arch = "arm"))] test_helpers::test_unary_mask_elementwise( &Vector::::is_normal, &Scalar::is_normal, @@ -535,6 +538,9 @@ macro_rules! impl_float_tests { } fn is_subnormal() { + // Arm v7 Neon violates float opsem re: subnormals, see + // https://github.com/rust-lang/portable-simd/issues/439 + #[cfg(not(target_arch = "arm"))] test_helpers::test_unary_mask_elementwise( &Vector::::is_subnormal, &Scalar::is_subnormal, From 4111fb2cbd63e98a85436aa163d0f9a2dac5ee3e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 12 Sep 2024 11:33:13 +0200 Subject: [PATCH 059/285] simd_shuffle: require index argument to be a vector --- crates/core_simd/src/swizzle.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 2f4f777b20e2..d62642fb9061 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -85,7 +85,7 @@ pub trait Swizzle { LaneCount: SupportedLaneCount, LaneCount: SupportedLaneCount, { - // Safety: `vector` is a vector, and the index is a const array of u32. + // Safety: `vector` is a vector, and the index is a const vector of u32. unsafe { core::intrinsics::simd::simd_shuffle( vector, @@ -103,7 +103,11 @@ pub trait Swizzle { output[i] = index as u32; i += 1; } - output + + // The index list needs to be returned as a vector. + #[repr(simd)] + struct SimdShuffleIdx([u32; LEN]); + SimdShuffleIdx(output) }, ) } @@ -121,7 +125,7 @@ pub trait Swizzle { LaneCount: SupportedLaneCount, LaneCount: SupportedLaneCount, { - // Safety: `first` and `second` are vectors, and the index is a const array of u32. + // Safety: `first` and `second` are vectors, and the index is a const vector of u32. unsafe { core::intrinsics::simd::simd_shuffle( first, @@ -139,7 +143,11 @@ pub trait Swizzle { output[i] = index as u32; i += 1; } - output + + // The index list needs to be returned as a vector. + #[repr(simd)] + struct SimdShuffleIdx([u32; LEN]); + SimdShuffleIdx(output) }, ) } From 24587921617a71fbf09f118f85af68a181948395 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 23 Sep 2024 21:29:57 +0200 Subject: [PATCH 060/285] stabilize const_intrinsic_copy --- crates/core_simd/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 331b66262490..cc6246b4a0d4 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -1,8 +1,6 @@ #![no_std] #![feature( - const_intrinsic_copy, const_refs_to_cell, - const_maybe_uninit_as_mut_ptr, const_mut_refs, convert_float_to_int, core_intrinsics, From c7d9ad8c2cae5fcc572fe2f5ed5c88fac25c4a97 Mon Sep 17 00:00:00 2001 From: Samuel Shepard Date: Fri, 27 Sep 2024 15:07:28 -0400 Subject: [PATCH 061/285] Add shift_elements_{left,right} for Simd and Masks --- crates/core_simd/src/swizzle.rs | 74 +++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index e0edb2cf10a6..9fa6a7da8d75 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -251,6 +251,56 @@ where Rotate::::swizzle(self) } + /// Shifts the vector elements to the left by `OFFSET`, padding by the + /// default value (e.g., zero) to the right. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + fn shift_elements_left(self) -> Self + where + T: Default, + { + struct Shift; + + impl Swizzle for Shift { + const INDEX: [usize; N] = const { + let mut index = [N; N]; + let mut i = 0; + while i + OFFSET < N { + index[i] = i + OFFSET; + i += 1; + } + index + }; + } + + Shift::::concat_swizzle(self, Self::default()) + } + + /// Shifts the vector elements to the right by `OFFSET`, padding by the + /// default value (e.g., zero) from the left. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + fn shift_elements_right(self) -> Self + where + T: Default, + { + struct Shift; + + impl Swizzle for Shift { + const INDEX: [usize; N] = const { + let mut index = [N; N]; + let mut i = OFFSET; + while i < N { + index[i] = i - OFFSET; + i += 1; + } + index + }; + } + + Shift::::concat_swizzle(self, Self::default()) + } + /// Interleave two vectors. /// /// The resulting vectors contain elements taken alternatively from `self` and `other`, first @@ -451,6 +501,30 @@ where unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_right::()) } } + /// Shifts the mask elements to the left by `OFFSET`, padding by the + /// default value (e.g., zero) to the right. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn shift_elements_left(self) -> Self + where + T: Default, + { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().shift_elements_left::()) } + } + + /// Shifts the mask elements to the right by `OFFSET`, padding by the + /// default value (e.g., `false`) from the left. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn shift_elements_right(self) -> Self + where + T: Default, + { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().shift_elements_right::()) } + } + /// Interleave two masks. /// /// The resulting masks contain elements taken alternatively from `self` and `other`, first From 55b4b74a120dc83317f143b8a7ce292f6afd34ea Mon Sep 17 00:00:00 2001 From: Samuel Shepard Date: Fri, 27 Sep 2024 15:12:58 -0400 Subject: [PATCH 062/285] Add tests, make public --- crates/core_simd/src/swizzle.rs | 4 ++-- crates/core_simd/tests/swizzle.rs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 9fa6a7da8d75..cf1e08aa668a 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -255,7 +255,7 @@ where /// default value (e.g., zero) to the right. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - fn shift_elements_left(self) -> Self + pub fn shift_elements_left(self) -> Self where T: Default, { @@ -280,7 +280,7 @@ where /// default value (e.g., zero) from the left. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - fn shift_elements_right(self) -> Self + pub fn shift_elements_right(self) -> Self where T: Default, { diff --git a/crates/core_simd/tests/swizzle.rs b/crates/core_simd/tests/swizzle.rs index 522d71439b77..98045fc5c544 100644 --- a/crates/core_simd/tests/swizzle.rs +++ b/crates/core_simd/tests/swizzle.rs @@ -48,6 +48,24 @@ fn rotate() { assert_eq!(a.rotate_elements_right::<5>().to_array(), [4, 1, 2, 3]); } +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn shift() { + let a = Simd::from_array([1, 2, 3, 4]); + assert_eq!(a.shift_elements_left::<0>().to_array(), [1, 2, 3, 4]); + assert_eq!(a.shift_elements_left::<1>().to_array(), [2, 3, 4, 0]); + assert_eq!(a.shift_elements_left::<2>().to_array(), [3, 4, 0, 0]); + assert_eq!(a.shift_elements_left::<3>().to_array(), [4, 0, 0, 0]); + assert_eq!(a.shift_elements_left::<4>().to_array(), [0, 0, 0, 0]); + assert_eq!(a.shift_elements_left::<5>().to_array(), [0, 0, 0, 0]); + assert_eq!(a.shift_elements_right::<0>().to_array(), [1, 2, 3, 4]); + assert_eq!(a.shift_elements_right::<1>().to_array(), [0, 1, 2, 3]); + assert_eq!(a.shift_elements_right::<2>().to_array(), [0, 0, 1, 2]); + assert_eq!(a.shift_elements_right::<3>().to_array(), [0, 0, 0, 1]); + assert_eq!(a.shift_elements_right::<4>().to_array(), [0, 0, 0, 0]); + assert_eq!(a.shift_elements_right::<5>().to_array(), [0, 0, 0, 0]); +} + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn interleave() { From f5fea5702863eb9ff30b42ee4e8942d9f70eea54 Mon Sep 17 00:00:00 2001 From: Samuel Shepard Date: Fri, 27 Sep 2024 19:18:30 -0400 Subject: [PATCH 063/285] Change API to accept a `padding` argument --- crates/core_simd/src/swizzle.rs | 44 +++++++++++-------------------- crates/core_simd/tests/swizzle.rs | 24 ++++++++--------- 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index cf1e08aa668a..6353196e4cf4 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -251,14 +251,11 @@ where Rotate::::swizzle(self) } - /// Shifts the vector elements to the left by `OFFSET`, padding by the - /// default value (e.g., zero) to the right. + /// Shifts the vector elements to the left by `OFFSET`, filling in with + /// `padding` from the right. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn shift_elements_left(self) -> Self - where - T: Default, - { + pub fn shift_elements_left(self, padding: T) -> Self { struct Shift; impl Swizzle for Shift { @@ -273,17 +270,14 @@ where }; } - Shift::::concat_swizzle(self, Self::default()) + Shift::::concat_swizzle(self, Simd::splat(padding)) } - /// Shifts the vector elements to the right by `OFFSET`, padding by the - /// default value (e.g., zero) from the left. + /// Shifts the vector elements to the right by `OFFSET`, filling in with + /// `padding` from the left. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn shift_elements_right(self) -> Self - where - T: Default, - { + pub fn shift_elements_right(self, padding: T) -> Self { struct Shift; impl Swizzle for Shift { @@ -298,7 +292,7 @@ where }; } - Shift::::concat_swizzle(self, Self::default()) + Shift::::concat_swizzle(self, Simd::splat(padding)) } /// Interleave two vectors. @@ -501,28 +495,22 @@ where unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_right::()) } } - /// Shifts the mask elements to the left by `OFFSET`, padding by the - /// default value (e.g., zero) to the right. + /// Shifts the mask elements to the left by `OFFSET`, filling in with + /// `padding` from the right. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn shift_elements_left(self) -> Self - where - T: Default, - { + pub fn shift_elements_left(self, padding: T) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().shift_elements_left::()) } + unsafe { Self::from_int_unchecked(self.to_int().shift_elements_left::(padding)) } } - /// Shifts the mask elements to the right by `OFFSET`, padding by the - /// default value (e.g., `false`) from the left. + /// Shifts the mask elements to the right by `OFFSET`, filling in with + /// `padding` from the left. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn shift_elements_right(self) -> Self - where - T: Default, - { + pub fn shift_elements_right(self, padding: T) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().shift_elements_right::()) } + unsafe { Self::from_int_unchecked(self.to_int().shift_elements_right::(padding)) } } /// Interleave two masks. diff --git a/crates/core_simd/tests/swizzle.rs b/crates/core_simd/tests/swizzle.rs index 98045fc5c544..7001e5f6bf87 100644 --- a/crates/core_simd/tests/swizzle.rs +++ b/crates/core_simd/tests/swizzle.rs @@ -52,18 +52,18 @@ fn rotate() { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn shift() { let a = Simd::from_array([1, 2, 3, 4]); - assert_eq!(a.shift_elements_left::<0>().to_array(), [1, 2, 3, 4]); - assert_eq!(a.shift_elements_left::<1>().to_array(), [2, 3, 4, 0]); - assert_eq!(a.shift_elements_left::<2>().to_array(), [3, 4, 0, 0]); - assert_eq!(a.shift_elements_left::<3>().to_array(), [4, 0, 0, 0]); - assert_eq!(a.shift_elements_left::<4>().to_array(), [0, 0, 0, 0]); - assert_eq!(a.shift_elements_left::<5>().to_array(), [0, 0, 0, 0]); - assert_eq!(a.shift_elements_right::<0>().to_array(), [1, 2, 3, 4]); - assert_eq!(a.shift_elements_right::<1>().to_array(), [0, 1, 2, 3]); - assert_eq!(a.shift_elements_right::<2>().to_array(), [0, 0, 1, 2]); - assert_eq!(a.shift_elements_right::<3>().to_array(), [0, 0, 0, 1]); - assert_eq!(a.shift_elements_right::<4>().to_array(), [0, 0, 0, 0]); - assert_eq!(a.shift_elements_right::<5>().to_array(), [0, 0, 0, 0]); + assert_eq!(a.shift_elements_left::<0>(0).to_array(), [1, 2, 3, 4]); + assert_eq!(a.shift_elements_left::<1>(0).to_array(), [2, 3, 4, 0]); + assert_eq!(a.shift_elements_left::<2>(9).to_array(), [3, 4, 9, 9]); + assert_eq!(a.shift_elements_left::<3>(8).to_array(), [4, 8, 8, 8]); + assert_eq!(a.shift_elements_left::<4>(7).to_array(), [7, 7, 7, 7]); + assert_eq!(a.shift_elements_left::<5>(6).to_array(), [6, 6, 6, 6]); + assert_eq!(a.shift_elements_right::<0>(0).to_array(), [1, 2, 3, 4]); + assert_eq!(a.shift_elements_right::<1>(0).to_array(), [0, 1, 2, 3]); + assert_eq!(a.shift_elements_right::<2>(-1).to_array(), [-1, -1, 1, 2]); + assert_eq!(a.shift_elements_right::<3>(-2).to_array(), [-2, -2, -2, 1]); + assert_eq!(a.shift_elements_right::<4>(-3).to_array(), [-3, -3, -3, -3]); + assert_eq!(a.shift_elements_right::<5>(-4).to_array(), [-4, -4, -4, -4]); } #[test] From 8cff838daa3274c0c4f9bbef67c46587033e267d Mon Sep 17 00:00:00 2001 From: Sam Shepard Date: Fri, 27 Sep 2024 21:04:49 -0400 Subject: [PATCH 064/285] Update crates/core_simd/src/swizzle.rs Co-authored-by: Caleb Zulawski --- crates/core_simd/src/swizzle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 6353196e4cf4..a7833ea92c0f 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -498,7 +498,7 @@ where /// Shifts the mask elements to the left by `OFFSET`, filling in with /// `padding` from the right. #[inline] - #[must_use = "method returns a new vector and does not mutate the original inputs"] + #[must_use = "method returns a new mask and does not mutate the original inputs"] pub fn shift_elements_left(self, padding: T) -> Self { // Safety: swizzles are safe for masks unsafe { Self::from_int_unchecked(self.to_int().shift_elements_left::(padding)) } From c9c0bf97f031cc7ab15209cd12b18ab72d019941 Mon Sep 17 00:00:00 2001 From: Sam Shepard Date: Fri, 27 Sep 2024 21:05:07 -0400 Subject: [PATCH 065/285] Update crates/core_simd/src/swizzle.rs Co-authored-by: Caleb Zulawski --- crates/core_simd/src/swizzle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index a7833ea92c0f..3b552016cb59 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -507,7 +507,7 @@ where /// Shifts the mask elements to the right by `OFFSET`, filling in with /// `padding` from the left. #[inline] - #[must_use = "method returns a new vector and does not mutate the original inputs"] + #[must_use = "method returns a new mask and does not mutate the original inputs"] pub fn shift_elements_right(self, padding: T) -> Self { // Safety: swizzles are safe for masks unsafe { Self::from_int_unchecked(self.to_int().shift_elements_right::(padding)) } From 9392fb1c2b73b453a53392ebfd52462100e0c430 Mon Sep 17 00:00:00 2001 From: Samuel Shepard Date: Fri, 27 Sep 2024 21:27:47 -0400 Subject: [PATCH 066/285] Change mask function to accept bool --- crates/core_simd/src/swizzle.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 3b552016cb59..dbd84543064e 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -499,18 +499,30 @@ where /// `padding` from the right. #[inline] #[must_use = "method returns a new mask and does not mutate the original inputs"] - pub fn shift_elements_left(self, padding: T) -> Self { + pub fn shift_elements_left(self, padding: bool) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().shift_elements_left::(padding)) } + unsafe { + Self::from_int_unchecked(self.to_int().shift_elements_left::(if padding { + T::TRUE + } else { + T::FALSE + })) + } } /// Shifts the mask elements to the right by `OFFSET`, filling in with /// `padding` from the left. #[inline] #[must_use = "method returns a new mask and does not mutate the original inputs"] - pub fn shift_elements_right(self, padding: T) -> Self { + pub fn shift_elements_right(self, padding: bool) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().shift_elements_right::(padding)) } + unsafe { + Self::from_int_unchecked(self.to_int().shift_elements_right::(if padding { + T::TRUE + } else { + T::FALSE + })) + } } /// Interleave two masks. From 6c1656df84004f77d40dfab09f9a3f9f6c20efe7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 14 Sep 2024 13:44:02 +0200 Subject: [PATCH 067/285] move strict provenance lints to new feature gate, remove old feature gates --- crates/core_simd/src/lib.rs | 1 - crates/core_simd/tests/pointers.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index cc6246b4a0d4..992a7705e3c5 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -9,7 +9,6 @@ repr_simd, simd_ffi, staged_api, - strict_provenance, prelude_import, ptr_metadata )] diff --git a/crates/core_simd/tests/pointers.rs b/crates/core_simd/tests/pointers.rs index 90bfc5d5fd6a..d7db4e82b3ca 100644 --- a/crates/core_simd/tests/pointers.rs +++ b/crates/core_simd/tests/pointers.rs @@ -1,4 +1,4 @@ -#![feature(portable_simd, strict_provenance, exposed_provenance)] +#![feature(portable_simd)] use core_simd::simd::{ ptr::{SimdConstPtr, SimdMutPtr}, From 7e162d19dd2e245dbba0e37fd12fe2cdaafdfed8 Mon Sep 17 00:00:00 2001 From: Hans Kratz Date: Wed, 23 Oct 2024 00:42:29 +0200 Subject: [PATCH 068/285] rust-lang/portable-simd#443: Add armv7 neon mplementation for `Simd::swizzle_dyn` Use arm neon intrinsics to swizzle two u8x8 blocks with a u8x8x2 lookup table. --- crates/core_simd/src/swizzle_dyn.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/crates/core_simd/src/swizzle_dyn.rs b/crates/core_simd/src/swizzle_dyn.rs index eaf297ba3e35..0619404e5f70 100644 --- a/crates/core_simd/src/swizzle_dyn.rs +++ b/crates/core_simd/src/swizzle_dyn.rs @@ -57,6 +57,13 @@ where target_endian = "little" ))] 16 => transize(vqtbl1q_u8, self, idxs), + #[cfg(all( + target_arch = "arm", + target_feature = "v7", + target_feature = "neon", + target_endian = "little" + ))] + 16 => transize(armv7_neon_swizzle_u8x16, self, idxs), #[cfg(all(target_feature = "avx2", not(target_feature = "avx512vbmi")))] 32 => transize(avx2_pshufb, self, idxs), #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))] @@ -98,6 +105,28 @@ where } } +/// armv7 neon supports swizzling `u8x16` by swizzling two u8x8 blocks +/// with a u8x8x2 lookup table. +/// +/// # Safety +/// This requires armv7 neon to work +#[cfg(all( + target_arch = "arm", + target_feature = "v7", + target_feature = "neon", + target_endian = "little" +))] +unsafe fn armv7_neon_swizzle_u8x16(bytes: Simd, idxs: Simd) -> Simd { + use core::arch::arm::{uint8x8x2_t, vcombine_u8, vget_high_u8, vget_low_u8, vtbl2_u8}; + // SAFETY: Caller promised arm neon support + unsafe { + let bytes = uint8x8x2_t(vget_low_u8(bytes.into()), vget_high_u8(bytes.into())); + let lo = vtbl2_u8(bytes, vget_low_u8(idxs.into())); + let hi = vtbl2_u8(bytes, vget_high_u8(idxs.into())); + vcombine_u8(lo, hi).into() + } +} + /// "vpshufb like it was meant to be" on AVX2 /// /// # Safety From 35ac70a47830ba1c0590bc55523b70951ba024d3 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Sat, 2 Nov 2024 10:43:43 +0800 Subject: [PATCH 069/285] ci: add support for loongarch64-unknown-linux-gnu --- .github/workflows/ci.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4a3da359266..3984d8f0d8d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,7 @@ jobs: - aarch64-unknown-linux-gnu - arm64ec-pc-windows-msvc - armv7-unknown-linux-gnueabihf + - loongarch64-unknown-linux-gnu # non-nightly since https://github.com/rust-lang/rust/pull/113274 # - mips-unknown-linux-gnu # - mips64-unknown-linux-gnuabi64 @@ -195,6 +196,7 @@ jobs: - powerpc-unknown-linux-gnu - powerpc64le-unknown-linux-gnu # includes altivec by default - riscv64gc-unknown-linux-gnu + - loongarch64-unknown-linux-gnu # MIPS uses a nonstandard binary representation for NaNs which makes it worth testing # non-nightly since https://github.com/rust-lang/rust/pull/113274 # - mips-unknown-linux-gnu @@ -216,15 +218,9 @@ jobs: run: rustup target add ${{ matrix.target }} - name: Install Cross - # Equivalent to `cargo install cross`, but downloading a prebuilt - # binary. Ideally we wouldn't hardcode a version, but the version number - # being part of the tarball means we can't just use the download/latest - # URL :( + # Install the latest git version for newer targets. run: | - CROSS_URL=https://github.com/cross-rs/cross/releases/download/v0.2.5/cross-x86_64-unknown-linux-gnu.tar.gz - mkdir -p "$HOME/.bin" - curl -sfSL --retry-delay 10 --retry 5 "${CROSS_URL}" | tar zxf - -C "$HOME/.bin" - echo "$HOME/.bin" >> $GITHUB_PATH + cargo install cross --git https://github.com/cross-rs/cross --rev 4090beca3cfffa44371a5bba524de3a578aa46c3 - name: Configure Emulated CPUs run: | From f6a227690ee55d77f253c87d4f312879ae578596 Mon Sep 17 00:00:00 2001 From: AquaEBM Date: Mon, 25 Nov 2024 12:44:26 +0100 Subject: [PATCH 070/285] add rustc_const_unstable attribute to Simd::splat --- crates/core_simd/src/vector.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index fac11d66e1b0..6518927db61d 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -144,6 +144,7 @@ where /// assert_eq!(v.as_array(), &[8, 8, 8, 8]); /// ``` #[inline] + #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] pub const fn splat(value: T) -> Self { const fn splat_const(value: T) -> Simd where From 8423171f11c1dc494779bea6f93d63acb876d402 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sun, 15 Dec 2024 09:59:15 +0100 Subject: [PATCH 071/285] Bump `stdarch` --- crates/core_simd/src/vendor/arm.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/crates/core_simd/src/vendor/arm.rs b/crates/core_simd/src/vendor/arm.rs index f8878d11f094..3dc54481b6fd 100644 --- a/crates/core_simd/src/vendor/arm.rs +++ b/crates/core_simd/src/vendor/arm.rs @@ -48,17 +48,6 @@ mod neon { from_transmute! { unsafe u64x2 => poly64x2_t } } -#[cfg(any( - all(target_feature = "v5te", not(target_feature = "mclass")), - all(target_feature = "mclass", target_feature = "dsp"), -))] -mod dsp { - use super::*; - - from_transmute! { unsafe Simd => uint16x2_t } - from_transmute! { unsafe Simd => int16x2_t } -} - #[cfg(any( all(target_feature = "v6", not(target_feature = "mclass")), all(target_feature = "mclass", target_feature = "dsp"), @@ -68,6 +57,8 @@ mod simd32 { from_transmute! { unsafe Simd => uint8x4_t } from_transmute! { unsafe Simd => int8x4_t } + from_transmute! { unsafe Simd => uint16x2_t } + from_transmute! { unsafe Simd => int16x2_t } } #[cfg(all( From 999695bab9779db8b9c6dc5fc2135777562eae47 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 7 Jan 2025 02:26:52 +0000 Subject: [PATCH 072/285] Make sure to use Receiver trait when extracting object method candidate --- .../rustc_hir_typeck/src/method/confirm.rs | 14 ++++++-- ...arbitrary_self_types_dispatch_to_vtable.rs | 33 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 0c93c9817b46..ea06518a6d3d 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -347,9 +347,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // yield an object-type (e.g., `&Object` or `Box` // etc). - // FIXME: this feels, like, super dubious - self.fcx - .autoderef(self.span, self_ty) + let mut autoderef = self.fcx.autoderef(self.span, self_ty); + + // We don't need to gate this behind arbitrary self types + // per se, but it does make things a bit more gated. + if self.tcx.features().arbitrary_self_types() + || self.tcx.features().arbitrary_self_types_pointers() + { + autoderef = autoderef.use_receiver_trait(); + } + + autoderef .include_raw_pointers() .find_map(|(ty, _)| match ty.kind() { ty::Dynamic(data, ..) => Some(closure( diff --git a/tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs b/tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs new file mode 100644 index 000000000000..f9e346ea11e2 --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs @@ -0,0 +1,33 @@ +//@ check-pass + +#![feature(derive_coerce_pointee)] +#![feature(arbitrary_self_types)] + +use std::marker::CoercePointee; +use std::ops::Receiver; + +// `CoercePointee` isn't needed here, it's just a simpler +// (and more conceptual) way of deriving `DispatchFromDyn`. +// You could think of `MyDispatcher` as a smart pointer +// that just doesn't deref to its target type. +#[derive(CoercePointee)] +#[repr(transparent)] +struct MyDispatcher(*const T); + +impl Receiver for MyDispatcher { + type Target = T; +} +struct Test; + +trait Trait { + fn test(self: MyDispatcher); +} + +impl Trait for Test { + fn test(self: MyDispatcher) { + todo!() + } +} +fn main() { + MyDispatcher::(core::ptr::null_mut::()).test(); +} From 638667a135fd84f89a11e34db5ad13ed20593275 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Thu, 16 Jan 2025 01:41:34 -0500 Subject: [PATCH 073/285] Remove stable features --- crates/core_simd/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 3326341921bd..7f57847c9c23 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -1,9 +1,6 @@ #![no_std] #![feature( const_eval_select, - const_intrinsic_copy, - const_refs_to_cell, - const_mut_refs, convert_float_to_int, core_intrinsics, decl_macro, From 15c19722de927ef1e44cb9b07cc5a44efc4e0654 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sat, 18 Jan 2025 15:15:27 -0500 Subject: [PATCH 074/285] Add script for syncing subtree --- .gitignore | 1 + subtree-sync.sh | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100755 subtree-sync.sh diff --git a/.gitignore b/.gitignore index ea8c4bf7f35f..9673e52dcadb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +git-subtree.sh diff --git a/subtree-sync.sh b/subtree-sync.sh new file mode 100755 index 000000000000..18360077623b --- /dev/null +++ b/subtree-sync.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +set -eou pipefail + +git fetch origin +pushd $2 +git fetch origin +popd + +if [ "$(git rev-parse --show-prefix)" != "" ]; then + echo "Run this script from the git root" >&2 + exit 1 +fi + +if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/master)" ]; then + echo "$(pwd) is not at origin/master" >&2 + exit 1 +fi + +if [ ! -f library/portable-simd/git-subtree.sh ]; then + curl -sS https://raw.githubusercontent.com/bjorn3/git/tqc-subtree-portable/contrib/subtree/git-subtree.sh -o library/portable-simd/git-subtree.sh + chmod +x library/portable-simd/git-subtree.sh +fi + +today=$(date +%Y-%m-%d) + +case $1 in + "push") + upstream=rust-upstream-$today + merge=sync-from-rust-$today + + pushd $2 + git checkout master + git pull + popd + + library/portable-simd/git-subtree.sh push -P library/portable-simd $2 $upstream + + pushd $2 + git checkout -B $merge origin/master + git merge $upstream + popd + echo "Branch \`$merge\` created in \`$2\`. You may need to resolve merge conflicts." + ;; + "pull") + branch=sync-from-portable-simd-$today + + git checkout -B $branch + echo "Creating branch \`$branch\`... You may need to resolve merge conflicts." + library/portable-simd/git-subtree.sh pull -P library/portable-simd $2 origin/master + ;; +esac From 52b42d71876d509340c432671c63217427eef165 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sat, 18 Jan 2025 21:44:41 -0500 Subject: [PATCH 075/285] Update tests for std::simd subtree sync --- .../tests/pass/intrinsics/portable-simd.rs | 21 ------------------- ...n.DataflowConstProp.32bit.panic-abort.diff | 2 +- ....DataflowConstProp.32bit.panic-unwind.diff | 2 +- ...n.DataflowConstProp.64bit.panic-abort.diff | 2 +- ....DataflowConstProp.64bit.panic-unwind.diff | 2 +- ...oxed_slice.main.GVN.32bit.panic-abort.diff | 2 +- ...xed_slice.main.GVN.32bit.panic-unwind.diff | 2 +- ...oxed_slice.main.GVN.64bit.panic-abort.diff | 2 +- ...xed_slice.main.GVN.64bit.panic-unwind.diff | 2 +- .../gvn_ptr_eq_with_constant.main.GVN.diff | 2 +- ...ated_loop.PreCodegen.after.panic-abort.mir | 2 +- ...ted_loop.PreCodegen.after.panic-unwind.mir | 2 +- ...ward_loop.PreCodegen.after.panic-abort.mir | 2 +- ...ard_loop.PreCodegen.after.panic-unwind.mir | 2 +- ...erse_loop.PreCodegen.after.panic-abort.mir | 2 +- ...rse_loop.PreCodegen.after.panic-unwind.mir | 2 +- 16 files changed, 15 insertions(+), 36 deletions(-) diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index acd3502f5289..a61f29775c1f 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -300,27 +300,6 @@ fn simd_mask() { } } - // This used to cause an ICE. It exercises simd_select_bitmask with an array as input. - let bitmask = u8x4::from_array([0b00001101, 0, 0, 0]); - assert_eq!( - mask32x4::from_bitmask_vector(bitmask), - mask32x4::from_array([true, false, true, true]), - ); - let bitmask = u8x8::from_array([0b01000101, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!( - mask32x8::from_bitmask_vector(bitmask), - mask32x8::from_array([true, false, true, false, false, false, true, false]), - ); - let bitmask = - u8x16::from_array([0b01000101, 0b11110000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!( - mask32x16::from_bitmask_vector(bitmask), - mask32x16::from_array([ - true, false, true, false, false, false, true, false, false, false, false, false, true, - true, true, true, - ]), - ); - // Also directly call simd_select_bitmask, to test both kinds of argument types. unsafe { // These masks are exactly the results we got out above in the `simd_bitmask` tests. diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff index 5a830254f619..2c89670dcf7d 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff @@ -26,7 +26,7 @@ } scope 11 (inlined NonZero::::get) { } - scope 12 (inlined without_provenance::<[bool; 0]>) { + scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) { scope 13 (inlined without_provenance_mut::<[bool; 0]>) { } } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff index c11368a347c5..8fecfe224cc6 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff @@ -26,7 +26,7 @@ } scope 11 (inlined NonZero::::get) { } - scope 12 (inlined without_provenance::<[bool; 0]>) { + scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) { scope 13 (inlined without_provenance_mut::<[bool; 0]>) { } } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff index 037ed02ce655..976ea252c2f8 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff @@ -26,7 +26,7 @@ } scope 11 (inlined NonZero::::get) { } - scope 12 (inlined without_provenance::<[bool; 0]>) { + scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) { scope 13 (inlined without_provenance_mut::<[bool; 0]>) { } } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff index 86351c787593..6c59f5e3e2e8 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff @@ -26,7 +26,7 @@ } scope 11 (inlined NonZero::::get) { } - scope 12 (inlined without_provenance::<[bool; 0]>) { + scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) { scope 13 (inlined without_provenance_mut::<[bool; 0]>) { } } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff index 20a3897a934f..1f9cf6d6aca8 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff @@ -26,7 +26,7 @@ } scope 11 (inlined NonZero::::get) { } - scope 12 (inlined without_provenance::<[bool; 0]>) { + scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) { scope 13 (inlined without_provenance_mut::<[bool; 0]>) { } } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff index 2e396301fd0e..a8760285fac1 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff @@ -26,7 +26,7 @@ } scope 11 (inlined NonZero::::get) { } - scope 12 (inlined without_provenance::<[bool; 0]>) { + scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) { scope 13 (inlined without_provenance_mut::<[bool; 0]>) { } } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff index 319691174cf6..c398ae70a1a3 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff @@ -26,7 +26,7 @@ } scope 11 (inlined NonZero::::get) { } - scope 12 (inlined without_provenance::<[bool; 0]>) { + scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) { scope 13 (inlined without_provenance_mut::<[bool; 0]>) { } } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff index 5dafc89d53f2..02934c02587d 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff @@ -26,7 +26,7 @@ } scope 11 (inlined NonZero::::get) { } - scope 12 (inlined without_provenance::<[bool; 0]>) { + scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) { scope 13 (inlined without_provenance_mut::<[bool; 0]>) { } } diff --git a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff index 8e7964297d06..f56af33ea603 100644 --- a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff +++ b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff @@ -16,7 +16,7 @@ } scope 8 (inlined NonZero::::get) { } - scope 9 (inlined without_provenance::) { + scope 9 (inlined std::ptr::without_provenance::) { scope 10 (inlined without_provenance_mut::) { } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir index 496ec78fd8d3..b7a9b4a1fe01 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir @@ -59,7 +59,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let _9: *const T; scope 7 { } - scope 12 (inlined without_provenance::) { + scope 12 (inlined std::ptr::without_provenance::) { scope 13 (inlined without_provenance_mut::) { } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir index c4547cb888fa..33dbf04d028d 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir @@ -34,7 +34,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let _9: *const T; scope 7 { } - scope 12 (inlined without_provenance::) { + scope 12 (inlined std::ptr::without_provenance::) { scope 13 (inlined without_provenance_mut::) { } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir index 7d011ea3347f..dc13bb23c310 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -31,7 +31,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let _9: *const T; scope 7 { } - scope 12 (inlined without_provenance::) { + scope 12 (inlined std::ptr::without_provenance::) { scope 13 (inlined without_provenance_mut::) { } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir index 75e6542a3a4b..3f1e0e0f746b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -31,7 +31,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let _9: *const T; scope 7 { } - scope 12 (inlined without_provenance::) { + scope 12 (inlined std::ptr::without_provenance::) { scope 13 (inlined without_provenance_mut::) { } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index 41bc91ab028d..4b7ab4516d25 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -34,7 +34,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let _9: *const T; scope 7 { } - scope 12 (inlined without_provenance::) { + scope 12 (inlined std::ptr::without_provenance::) { scope 13 (inlined without_provenance_mut::) { } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index 6ed8ef9715bb..b2c15247cd7b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -34,7 +34,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let _9: *const T; scope 7 { } - scope 12 (inlined without_provenance::) { + scope 12 (inlined std::ptr::without_provenance::) { scope 13 (inlined without_provenance_mut::) { } } From 68283cced1bb9bd53ff0dae9378e91c903aca604 Mon Sep 17 00:00:00 2001 From: vayunbiyani Date: Fri, 10 Jan 2025 08:08:06 -0500 Subject: [PATCH 076/285] Updated several files to use rust intrinsic macros instead of the legacy extern "rust-intrinsic" blocks --- src/intrinsics/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 2e5813556aaf..26f14532b458 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -1,4 +1,5 @@ //! Codegen of intrinsics. This includes `extern "rust-intrinsic"`, +//! functions marked with the `#[rustc_intrinsic]` attribute //! and LLVM intrinsics that have symbol names starting with `llvm.`. macro_rules! intrinsic_args { From a8f54712da1ea9bec0866bf6a813b72d06676f21 Mon Sep 17 00:00:00 2001 From: Vishruth-Thimmaiah Date: Thu, 16 Jan 2025 16:52:11 +0530 Subject: [PATCH 077/285] fix: upmap ranges in convert_tuple_struct_to_named_struct assist fixes #18766 --- .../convert_tuple_struct_to_named_struct.rs | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 3c84f83906a9..f6e516db8883 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -140,8 +140,10 @@ fn edit_struct_references( match_ast! { match node { ast::TupleStructPat(tuple_struct_pat) => { + let file_range = ctx.sema.original_range_opt(&node)?; + edit.edit_file(file_range.file_id); edit.replace( - tuple_struct_pat.syntax().text_range(), + file_range.range, ast::make::record_pat_with_fields( tuple_struct_pat.path()?, ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map( @@ -921,6 +923,104 @@ pub struct $0Foo(#[my_custom_attr] u32); "#, r#" pub struct Foo { #[my_custom_attr] field1: u32 } +"#, + ); + } + + #[test] + fn convert_in_macro_pattern_args() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +macro_rules! foo { + ($expression:expr, $pattern:pat) => { + match $expression { + $pattern => true, + _ => false + } + }; +} +enum Expr { + A$0(usize), +} +fn main() { + let e = Expr::A(0); + foo!(e, Expr::A(0)); +} +"#, + r#" +macro_rules! foo { + ($expression:expr, $pattern:pat) => { + match $expression { + $pattern => true, + _ => false + } + }; +} +enum Expr { + A { field1: usize }, +} +fn main() { + let e = Expr::A { field1: 0 }; + foo!(e, Expr::A { field1: 0 }); +} +"#, + ); + } + + #[test] + fn convert_in_multi_file_macro_pattern_args() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +//- /main.rs +mod foo; + +enum Test { + A$0(i32) +} + +//- /foo.rs +use crate::Test; + +macro_rules! foo { + ($expression:expr, $pattern:pat) => { + match $expression { + $pattern => true, + _ => false + } + }; +} + +fn foo() { + let a = Test::A(0); + foo!(a, Test::A(0)); +} +"#, + r#" +//- /main.rs +mod foo; + +enum Test { + A { field1: i32 } +} + +//- /foo.rs +use crate::Test; + +macro_rules! foo { + ($expression:expr, $pattern:pat) => { + match $expression { + $pattern => true, + _ => false + } + }; +} + +fn foo() { + let a = Test::A { field1: 0 }; + foo!(a, Test::A { field1: 0 }); +} "#, ); } From f14111806d6ff2a76a828f9b0303603c8e3cc966 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 20 Jan 2025 15:30:04 +0000 Subject: [PATCH 078/285] Merge commit '728bc27f32c05ac8a9b5eb33fd101e479072984f' into sync_cg_clif-2025-01-20 --- rust-toolchain | 2 +- scripts/test_rustc_tests.sh | 5 ++--- src/driver/aot.rs | 10 +++++++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/rust-toolchain b/rust-toolchain index e4c3dd708fd9..9c6aad3490d4 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2025-01-10" +channel = "nightly-2025-01-20" components = ["rust-src", "rustc-dev", "llvm-tools"] profile = "minimal" diff --git a/scripts/test_rustc_tests.sh b/scripts/test_rustc_tests.sh index e569da90cf7b..41aa011e805d 100755 --- a/scripts/test_rustc_tests.sh +++ b/scripts/test_rustc_tests.sh @@ -176,12 +176,11 @@ diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-supp index 9607ff02f96..b7d97caf9a2 100644 --- a/src/tools/run-make-support/src/external_deps/rustdoc.rs +++ b/src/tools/run-make-support/src/external_deps/rustdoc.rs -@@ -34,8 +34,6 @@ pub fn bare() -> Self { +@@ -34,7 +34,6 @@ pub fn bare() -> Self { #[track_caller] pub fn new() -> Self { let mut cmd = setup_common(); -- let target_rpath_dir = env_var_os("TARGET_RPATH_DIR"); -- cmd.arg(format!("-L{}", target_rpath_dir.to_string_lossy())); +- cmd.arg("-L").arg(env_var_os("TARGET_RPATH_DIR")); Self { cmd } } diff --git a/src/driver/aot.rs b/src/driver/aot.rs index fe578e44770f..7d5592daac1c 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -333,9 +333,17 @@ fn make_module(sess: &Session, name: String) -> UnwindModule { let mut builder = ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap(); + + // Disable function sections by default on MSVC as it causes significant slowdowns with link.exe. + // Maybe link.exe has exponential behavior when there are many sections with the same name? Also + // explicitly disable it on MinGW as rustc already disables it by default on MinGW and as such + // isn't tested. If rustc enables it in the future on MinGW, we can re-enable it too once it has + // been on MinGW. + let default_function_sections = sess.target.function_sections && !sess.target.is_like_windows; builder.per_function_section( - sess.opts.unstable_opts.function_sections.unwrap_or(sess.target.function_sections), + sess.opts.unstable_opts.function_sections.unwrap_or(default_function_sections), ); + UnwindModule::new(ObjectModule::new(builder), true) } From ffe312e6750aad2c0bd13ae612b94bdc8e9a06ab Mon Sep 17 00:00:00 2001 From: jyn Date: Mon, 20 Jan 2025 17:52:55 -0500 Subject: [PATCH 079/285] Ignore global git hooks when importing rust-lang/rust git repo These may cause tests to fail. Disable them. --- build_system/prepare.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_system/prepare.rs b/build_system/prepare.rs index a4e9cb5f5c8c..11f73bdb61f9 100644 --- a/build_system/prepare.rs +++ b/build_system/prepare.rs @@ -186,7 +186,7 @@ fn init_git_repo(repo_dir: &Path) { spawn_and_wait(git_add_cmd); let mut git_commit_cmd = git_command(repo_dir, "commit"); - git_commit_cmd.arg("-m").arg("Initial commit").arg("-q"); + git_commit_cmd.arg("-m").arg("Initial commit").arg("-q").arg("--no-verify"); spawn_and_wait(git_commit_cmd); } From c4a3398171c1eadd565491acb4972908b14a0f42 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 14 Dec 2024 09:13:12 +0100 Subject: [PATCH 080/285] remove support for the #[start] attribute --- src/main_shim.rs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/main_shim.rs b/src/main_shim.rs index e6bf0d5b47e4..f68434968954 100644 --- a/src/main_shim.rs +++ b/src/main_shim.rs @@ -1,7 +1,7 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_hir::LangItem; use rustc_middle::ty::{AssocKind, GenericArg}; -use rustc_session::config::{EntryFnType, sigpipe}; +use rustc_session::config::EntryFnType; use rustc_span::{DUMMY_SP, Ident}; use crate::prelude::*; @@ -14,10 +14,9 @@ pub(crate) fn maybe_create_entry_wrapper( is_jit: bool, is_primary_cgu: bool, ) { - let (main_def_id, (is_main_fn, sigpipe)) = match tcx.entry_fn(()) { + let (main_def_id, sigpipe) = match tcx.entry_fn(()) { Some((def_id, entry_ty)) => (def_id, match entry_ty { - EntryFnType::Main { sigpipe } => (true, sigpipe), - EntryFnType::Start => (false, sigpipe::DEFAULT), + EntryFnType::Main { sigpipe } => sigpipe, }), None => return, }; @@ -31,14 +30,13 @@ pub(crate) fn maybe_create_entry_wrapper( return; } - create_entry_fn(tcx, module, main_def_id, is_jit, is_main_fn, sigpipe); + create_entry_fn(tcx, module, main_def_id, is_jit, sigpipe); fn create_entry_fn( tcx: TyCtxt<'_>, m: &mut dyn Module, rust_main_def_id: DefId, ignore_lang_start_wrapper: bool, - is_main_fn: bool, sigpipe: u8, ) { let main_ret_ty = tcx.fn_sig(rust_main_def_id).no_bound_vars().unwrap().output(); @@ -94,8 +92,8 @@ pub(crate) fn maybe_create_entry_wrapper( let main_func_ref = m.declare_func_in_func(main_func_id, &mut bcx.func); - let result = if is_main_fn && ignore_lang_start_wrapper { - // regular main fn, but ignoring #[lang = "start"] as we are running in the jit + let result = if ignore_lang_start_wrapper { + // ignoring #[lang = "start"] as we are running in the jit // FIXME set program arguments somehow let call_inst = bcx.ins().call(main_func_ref, &[]); let call_results = bcx.func.dfg.inst_results(call_inst).to_owned(); @@ -133,7 +131,8 @@ pub(crate) fn maybe_create_entry_wrapper( types::I64 => bcx.ins().sextend(types::I64, res), _ => unimplemented!("16bit systems are not yet supported"), } - } else if is_main_fn { + } else { + // Regular main fn invoked via start lang item. let start_def_id = tcx.require_lang_item(LangItem::Start, None); let start_instance = Instance::expect_resolve( tcx, @@ -150,10 +149,6 @@ pub(crate) fn maybe_create_entry_wrapper( let call_inst = bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv, arg_sigpipe]); bcx.inst_results(call_inst)[0] - } else { - // using user-defined start fn - let call_inst = bcx.ins().call(main_func_ref, &[arg_argc, arg_argv]); - bcx.inst_results(call_inst)[0] }; bcx.ins().return_(&[result]); From ccf89778cdb10d47dcef1f6baf238a9d598c94b2 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 23 Jan 2025 09:51:57 +0000 Subject: [PATCH 081/285] Rustup to rustc 1.86.0-nightly (649b995a9 2025-01-22) --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 9c6aad3490d4..0199bad77a80 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2025-01-20" +channel = "nightly-2025-01-23" components = ["rust-src", "rustc-dev", "llvm-tools"] profile = "minimal" From 50c14cef3f9e6980ed8c033e0cba0c8795ebbb83 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 23 Jan 2025 10:06:11 +0000 Subject: [PATCH 082/285] Fix rustc test suite --- scripts/test_rustc_tests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/test_rustc_tests.sh b/scripts/test_rustc_tests.sh index 41aa011e805d..595d73d25a2c 100755 --- a/scripts/test_rustc_tests.sh +++ b/scripts/test_rustc_tests.sh @@ -98,6 +98,7 @@ rm -r tests/run-make/volatile-intrinsics # same rm -r tests/run-make/llvm-ident # same rm -r tests/run-make/no-builtins-attribute # same rm -r tests/run-make/pgo-gen-no-imp-symbols # same +rm -r tests/run-make/llvm-location-discriminator-limit-dummy-span # same rm tests/ui/abi/stack-protector.rs # requires stack protector support rm -r tests/run-make/emit-stack-sizes # requires support for -Z emit-stack-sizes rm -r tests/run-make/optimization-remarks-dir # remarks are LLVM specific From eeb7930f8d79eea5677589fdbdbec80bdff98cef Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 9 Jan 2025 12:35:27 +0000 Subject: [PATCH 083/285] Add missing entry to builtin_functions --- src/compiler_builtins.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler_builtins.rs b/src/compiler_builtins.rs index 2484c10848ed..bf16e81a06f9 100644 --- a/src/compiler_builtins.rs +++ b/src/compiler_builtins.rs @@ -44,6 +44,7 @@ builtin_functions! { fn __umodti3(n: u128, d: u128) -> u128; fn __modti3(n: i128, d: i128) -> i128; fn __rust_u128_mulo(a: u128, b: u128, oflow: &mut i32) -> u128; + fn __rust_i128_mulo(a: i128, b: i128, oflow: &mut i32) -> i128; // floats fn __floattisf(i: i128) -> f32; From e7b1853b06d22431eb563daf021663895528f970 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 23 Jan 2025 10:10:54 +0000 Subject: [PATCH 084/285] Update to Cranelift 0.116 --- Cargo.lock | 60 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 26 +++++++++++------------ 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b5aba86079fc..ca66ec5c6e93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,24 +43,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac89549be94911dd0e839b4a7db99e9ed29c17517e1c026f61066884c168aa3c" +checksum = "e15d04a0ce86cb36ead88ad68cf693ffd6cda47052b9e0ac114bc47fd9cd23c4" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9bd49369f76c77e34e641af85d0956869237832c118964d08bf5f51f210875a" +checksum = "7c6e3969a7ce267259ce244b7867c5d3bc9e65b0a87e81039588dfdeaede9f34" [[package]] name = "cranelift-codegen" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd96ce9cf8efebd7f5ab8ced5a0ce44250280bbae9f593d74a6d7effc3582a35" +checksum = "2c22032c4cb42558371cf516bb47f26cdad1819d3475c133e93c49f50ebf304e" dependencies = [ "bumpalo", "cranelift-bforest", @@ -82,42 +82,42 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a68e358827afe4bfb6239fcbf6fbd5ac56206ece8a99c8f5f9bbd518773281a" +checksum = "c904bc71c61b27fc57827f4a1379f29de64fe95653b620a3db77d59655eee0b8" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e184c9767afbe73d50c55ec29abcf4c32f9baf0d9d22b86d58c4d55e06dee181" +checksum = "40180f5497572f644ce88c255480981ae2ec1d7bb4d8e0c0136a13b87a2f2ceb" [[package]] name = "cranelift-control" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc7664f2a66f053e33f149e952bb5971d138e3af637f5097727ed6dc0ed95dd" +checksum = "26d132c6d0bd8a489563472afc171759da0707804a65ece7ceb15a8c6d7dd5ef" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "118597e3a9cf86c3556fa579a7a23b955fa18231651a52a77a2475d305a9cf84" +checksum = "4b2d0d9618275474fbf679dd018ac6e009acbd6ae6850f6a67be33fb3b00b323" dependencies = [ "cranelift-bitset", ] [[package]] name = "cranelift-frontend" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7638ea1efb069a0aa18d8ee67401b6b0d19f6bfe5de5e9ede348bfc80bb0d8c7" +checksum = "4fac41e16729107393174b0c9e3730fb072866100e1e64e80a1a963b2e484d57" dependencies = [ "cranelift-codegen", "log", @@ -127,15 +127,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c53e1152a0b01c4ed2b1e0535602b8e86458777dd9d18b28732b16325c7dc0" +checksum = "1ca20d576e5070044d0a72a9effc2deacf4d6aa650403189d8ea50126483944d" [[package]] name = "cranelift-jit" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36972cab12ff246afe8d45b6a427669cf814bd393c661e5e8a8dedc26a81c73f" +checksum = "5e65c42755a719b09662b00c700daaf76cc35d5ace1f5c002ad404b591ff1978" dependencies = [ "anyhow", "cranelift-codegen", @@ -153,9 +153,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11841b3f54ac480db1e8e8d5678ba901a13b387012d315e3f8fba3e7b7a80447" +checksum = "4d55612bebcf16ff7306c8a6f5bdb6d45662b8aa1ee058ecce8807ad87db719b" dependencies = [ "anyhow", "cranelift-codegen", @@ -164,9 +164,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7d8f895444fa52dd7bdd0bed11bf007a7fb43af65a6deac8fcc4094c6372f7" +checksum = "b8dee82f3f1f2c4cba9177f1cc5e350fe98764379bcd29340caa7b01f85076c7" dependencies = [ "cranelift-codegen", "libc", @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.115.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e235ddfd19f100855ad03358c7ae0a13070c38a000701054cab46458cca6e81" +checksum = "aad5a6d3e379493c3f8b35dc61c93d0bf5f27003bbe20614e0200b0ec372ef52" dependencies = [ "anyhow", "cranelift-codegen", @@ -413,9 +413,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.16" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +checksum = "dc12939a1c9b9d391e0b7135f72fd30508b73450753e28341fed159317582a77" [[package]] name = "unicode-ident" @@ -425,9 +425,9 @@ checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "wasmtime-jit-icache-coherence" -version = "28.0.0" +version = "29.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40d7722b9e1fbeae135715710a8a2570b1e6cf72b74dd653962d89831c6c70d" +checksum = "ec5e8552e01692e6c2e5293171704fed8abdec79d1a6995a0870ab190e5747d1" dependencies = [ "anyhow", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index bfdbc3e768a6..670d6f4eef5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,13 +8,13 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.115.0", default-features = false, features = ["std", "unwind", "all-native-arch"] } -cranelift-frontend = { version = "0.115.0" } -cranelift-module = { version = "0.115.0" } -cranelift-native = { version = "0.115.0" } -cranelift-jit = { version = "0.115.0", optional = true } -cranelift-object = { version = "0.115.0" } -target-lexicon = "0.12.0" +cranelift-codegen = { version = "0.116.0", default-features = false, features = ["std", "unwind", "all-native-arch"] } +cranelift-frontend = { version = "0.116.0" } +cranelift-module = { version = "0.116.0" } +cranelift-native = { version = "0.116.0" } +cranelift-jit = { version = "0.116.0", optional = true } +cranelift-object = { version = "0.116.0" } +target-lexicon = "0.13" gimli = { version = "0.31", default-features = false, features = ["write"] } object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } @@ -24,12 +24,12 @@ smallvec = "1.8.1" [patch.crates-io] # Uncomment to use an unreleased version of cranelift -#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-28.0.0", version = "0.115.0" } -#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-28.0.0", version = "0.115.0" } -#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-28.0.0", version = "0.115.0" } -#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-28.0.0", version = "0.115.0" } -#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-28.0.0", version = "0.115.0" } -#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-28.0.0", version = "0.115.0" } +#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } +#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } +#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } +#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } +#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } +#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } # Uncomment to use local checkout of cranelift #cranelift-codegen = { path = "../wasmtime/cranelift/codegen" } From 1f45995dea280baa6381279221c9c60ff7af342d Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Thu, 23 Jan 2025 15:44:20 -0800 Subject: [PATCH 085/285] manual: Convert to mdbook Split manual.adoc into markdown files, one for each chapter. For the parts of the manual that are generated from source code doc comments, update the comments to use markdown syntax and update the code generators to write to `generated.md` files. For the weekly release, stop copying the .adoc files to the `rust-analyzer/rust-analyzer.github.io` at release time. Instead, we'll sync the manual hourly from this repository. See https://github.com/rust-analyzer/rust-analyzer.github.io/pull/226 for the sync. This PR should be merged first, and that PR needs to be merged before the next weekly release. This change is based on #15795, but rebased and updated. I've also manually checked each page for markdown syntax issues and fixed any I encountered. Co-authored-by: Lukas Wirth Co-authored-by: Josh Rotenberg --- src/tools/rust-analyzer/.gitignore | 7 +- .../src/handlers/apply_demorgan.rs | 4 +- .../ide-assists/src/handlers/auto_import.rs | 6 +- .../crates/ide-assists/src/tests/generated.rs | 2 +- .../src/completions/flyimport.rs | 8 +- .../src/completions/postfix/format_like.rs | 2 +- .../crates/ide-completion/src/lib.rs | 8 +- .../crates/ide-completion/src/snippet.rs | 12 +- .../crates/ide-db/src/apply_change.rs | 9 +- .../crates/ide-db/src/symbol_index.rs | 8 +- .../src/handlers/incorrect_case.rs | 2 +- .../rust-analyzer/crates/ide-ssr/src/lib.rs | 18 +- .../crates/ide/src/annotations.rs | 2 +- .../rust-analyzer/crates/ide/src/doc_links.rs | 8 +- .../crates/ide/src/expand_macro.rs | 10 +- .../crates/ide/src/extend_selection.rs | 10 +- .../crates/ide/src/fetch_crates.rs | 10 +- .../crates/ide/src/file_structure.rs | 11 +- .../crates/ide/src/goto_definition.rs | 10 +- .../crates/ide/src/goto_implementation.rs | 10 +- .../crates/ide/src/goto_type_definition.rs | 10 +- .../crates/ide/src/highlight_related.rs | 12 +- .../rust-analyzer/crates/ide/src/hover.rs | 2 +- .../crates/ide/src/inlay_hints.rs | 8 +- .../rust-analyzer/crates/ide/src/interpret.rs | 8 +- .../crates/ide/src/join_lines.rs | 14 +- .../crates/ide/src/matching_brace.rs | 10 +- .../rust-analyzer/crates/ide/src/move_item.rs | 8 +- .../crates/ide/src/parent_module.rs | 10 +- .../crates/ide/src/references.rs | 10 +- .../rust-analyzer/crates/ide/src/rename.rs | 10 +- .../rust-analyzer/crates/ide/src/runnables.rs | 17 +- .../rust-analyzer/crates/ide/src/status.rs | 9 +- .../crates/ide/src/syntax_highlighting.rs | 179 +-- .../rust-analyzer/crates/ide/src/typing.rs | 11 +- .../crates/ide/src/typing/on_enter.rs | 24 +- .../crates/ide/src/view_crate_graph.rs | 8 +- .../rust-analyzer/crates/ide/src/view_hir.rs | 9 +- .../crates/ide/src/view_item_tree.rs | 8 +- .../crates/ide/src/view_memory_layout.rs | 8 +- .../rust-analyzer/crates/ide/src/view_mir.rs | 6 +- .../crates/ide/src/view_syntax_tree.rs | 8 +- .../parser/src/syntax_kind/generated.rs | 2 +- .../crates/rust-analyzer/src/config.rs | 20 +- .../crates/syntax/src/ast/generated/nodes.rs | 2 +- .../crates/syntax/src/ast/generated/tokens.rs | 2 +- src/tools/rust-analyzer/docs/book/README.md | 29 + src/tools/rust-analyzer/docs/book/book.toml | 41 + .../rust-analyzer/docs/book/src/README.md | 21 + .../rust-analyzer/docs/book/src/SUMMARY.md | 13 + .../rust-analyzer/docs/book/src/assists.md | 8 + .../docs/book/src/configuration.md | 51 + .../docs/book/src/configuration_generated.md | 1201 +++++++++++++++++ .../docs/book/src/diagnostics.md | 16 + .../docs/book/src/editor_features.md | 204 +++ .../rust-analyzer/docs/book/src/features.md | 3 + .../docs/book/src/installation.md | 644 +++++++++ .../docs/book/src/non_cargo_based_projects.md | 246 ++++ .../rust-analyzer/docs/book/src/privacy.md | 15 + .../rust-analyzer/docs/book/src/security.md | 19 + .../docs/book/src/troubleshooting.md | 50 + src/tools/rust-analyzer/docs/dev/style.md | 4 +- src/tools/rust-analyzer/docs/user/.gitignore | 1 - .../docs/user/generated_config.adoc | 1197 ---------------- src/tools/rust-analyzer/docs/user/manual.adoc | 1121 --------------- src/tools/rust-analyzer/xtask/src/codegen.rs | 13 +- .../xtask/src/codegen/assists_doc_tests.rs | 10 +- .../xtask/src/codegen/diagnostics_docs.rs | 6 +- .../xtask/src/codegen/feature_docs.rs | 16 +- src/tools/rust-analyzer/xtask/src/release.rs | 21 +- 70 files changed, 2836 insertions(+), 2686 deletions(-) create mode 100644 src/tools/rust-analyzer/docs/book/README.md create mode 100644 src/tools/rust-analyzer/docs/book/book.toml create mode 100644 src/tools/rust-analyzer/docs/book/src/README.md create mode 100644 src/tools/rust-analyzer/docs/book/src/SUMMARY.md create mode 100644 src/tools/rust-analyzer/docs/book/src/assists.md create mode 100644 src/tools/rust-analyzer/docs/book/src/configuration.md create mode 100644 src/tools/rust-analyzer/docs/book/src/configuration_generated.md create mode 100644 src/tools/rust-analyzer/docs/book/src/diagnostics.md create mode 100644 src/tools/rust-analyzer/docs/book/src/editor_features.md create mode 100644 src/tools/rust-analyzer/docs/book/src/features.md create mode 100644 src/tools/rust-analyzer/docs/book/src/installation.md create mode 100644 src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md create mode 100644 src/tools/rust-analyzer/docs/book/src/privacy.md create mode 100644 src/tools/rust-analyzer/docs/book/src/security.md create mode 100644 src/tools/rust-analyzer/docs/book/src/troubleshooting.md delete mode 100644 src/tools/rust-analyzer/docs/user/.gitignore delete mode 100644 src/tools/rust-analyzer/docs/user/generated_config.adoc delete mode 100644 src/tools/rust-analyzer/docs/user/manual.adoc diff --git a/src/tools/rust-analyzer/.gitignore b/src/tools/rust-analyzer/.gitignore index c4470a45078a..7192e685e29b 100644 --- a/src/tools/rust-analyzer/.gitignore +++ b/src/tools/rust-analyzer/.gitignore @@ -6,10 +6,11 @@ target/ *.log *.iml .vscode/settings.json -generated_assists.adoc -generated_features.adoc -generated_diagnostic.adoc .DS_Store /out/ /dump.lsif .envrc +docs/book/book +docs/book/src/assists_generated.md +docs/book/src/diagnostics_generated.md +docs/book/src/features_generated.md diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index 70fb5680052b..491727a30a88 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -15,7 +15,7 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin // Assist: apply_demorgan // -// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law]. +// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). // This transforms expressions of the form `!l || !r` into `!(l && r)`. // This also works with `&&`. This assist can only be applied with the cursor // on either `||` or `&&`. @@ -131,7 +131,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti // Assist: apply_demorgan_iterator // -// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law] to +// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) to // `Iterator::all` and `Iterator::any`. // // This transforms expressions of the form `!iter.any(|x| predicate(x))` into diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index d86948818b16..a92a000c3fbd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -38,7 +38,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; // use super::AssistContext; // ``` // -// .Import Granularity +// #### Import Granularity // // It is possible to configure how use-trees are merged with the `imports.granularity.group` setting. // It has the following configurations: @@ -54,7 +54,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; // // In `VS Code` the configuration for this is `rust-analyzer.imports.granularity.group`. // -// .Import Prefix +// #### Import Prefix // // The style of imports in the same crate is configurable through the `imports.prefix` setting. // It has the following configurations: @@ -68,7 +68,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; // // In `VS Code` the configuration for this is `rust-analyzer.imports.prefix`. // -// image::https://user-images.githubusercontent.com/48062697/113020673-b85be580-917a-11eb-9022-59585f35d4f8.gif[] +// ![Auto Import](https://user-images.githubusercontent.com/48062697/113020673-b85be580-917a-11eb-9022-59585f35d4f8.gif) // Assist: auto_import // diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 54e42f126bc5..4b0fa704a22b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen assists-doc-tests`, do not edit by hand. +//! Generated by `cargo xtask codegen assists-doc-tests`, do not edit by hand. use super::check_doc_test; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 435b88de4ae6..9a2b4cd1b424 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -83,19 +83,19 @@ use crate::{ // NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path, // no imports will be proposed. // -// .Fuzzy search details +// #### Fuzzy search details // // To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only // (i.e. in `HashMap` in the `std::collections::HashMap` path). // For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols // (but shows all associated items for any input length). // -// .Import configuration +// #### Import configuration // // It is possible to configure how use-trees are merged with the `imports.granularity.group` setting. // Mimics the corresponding behavior of the `Auto Import` feature. // -// .LSP and performance implications +// #### LSP and performance implications // // The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits` // (case-sensitive) resolve client capability in its client capabilities. @@ -103,7 +103,7 @@ use crate::{ // For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones, // which might be slow ergo the feature is automatically disabled. // -// .Feature toggle +// #### Feature toggle // // The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag. // Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs index 2755329bb31a..c612170eb54b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs @@ -14,7 +14,7 @@ // ** `logw` -> `log::warn!(...)` // ** `loge` -> `log::error!(...)` // -// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[] +// ![Format String Completion](https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif) use ide_db::{ syntax_helpers::format_string_exprs::{parse_format_exprs, with_placeholders, Arg}, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 56d7eeaf8ea0..c1b4f54b666f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -110,11 +110,13 @@ impl CompletionFieldsToResolve { // // There also snippet completions: // -// .Expressions +// #### Expressions +// // - `pd` -> `eprintln!(" = {:?}", );` // - `ppd` -> `eprintln!(" = {:#?}", );` // -// .Items +// #### Items +// // - `tfn` -> `#[test] fn feature(){}` // - `tmod` -> // ```rust @@ -131,7 +133,7 @@ impl CompletionFieldsToResolve { // Those are the additional completion options with automatic `use` import and options from all project importable items, // fuzzy matched against the completion input. // -// image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[] +// ![Magic Completions](https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif) /// Main entry point for completion. We run completion as a two-phase process. /// diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 04bb178c658f..e5847b01e185 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -8,8 +8,7 @@ // // A custom snippet can be defined by adding it to the `rust-analyzer.completion.snippets.custom` object respectively. // -// [source,json] -// ---- +// ```json // { // "rust-analyzer.completion.snippets.custom": { // "thread spawn": { @@ -25,7 +24,7 @@ // } // } // } -// ---- +// ``` // // In the example above: // @@ -39,6 +38,7 @@ // * `description` is an optional description of the snippet, if unset the snippet name will be used. // // * `requires` is an optional list of item paths that have to be resolvable in the current crate where the completion is rendered. + // On failure of resolution the snippet won't be applicable, otherwise the snippet will insert an import for the items on insertion if // the items aren't yet in scope. // @@ -55,8 +55,8 @@ // // For the VSCode editor, rust-analyzer also ships with a small set of defaults which can be removed // by overwriting the settings object mentioned above, the defaults are: -// [source,json] -// ---- +// +// ```json // { // "Arc::new": { // "postfix": "arc", @@ -98,7 +98,7 @@ // "scope": "expr" // } // } -// ---- +// ```` use hir::{ModPath, Name, Symbol}; use ide_db::imports::import_assets::LocatedImport; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs index 35e3a8d9bf7f..46ff4fbf9e90 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs @@ -44,12 +44,11 @@ impl RootDatabase { // // Clears rust-analyzer's internal database and prints memory usage statistics. // - // |=== - // | Editor | Action Name - // + // | Editor | Action Name | + // |---------|-------------| // | VS Code | **rust-analyzer: Memory Usage (Clears Database)** - // |=== - // image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[] + + // ![Memory Usage](https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif) pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes, usize)> { let mut acc: Vec<(String, Bytes, usize)> = vec![]; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index e5ce10a771ef..bb4c289c9084 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -193,11 +193,9 @@ impl std::ops::Deref for Snap { // `rust-analyzer.workspace.symbol.search.kind` settings. Symbols prefixed // with `__` are hidden from the search results unless configured otherwise. // -// |=== -// | Editor | Shortcut -// -// | VS Code | kbd:[Ctrl+T] -// |=== +// | Editor | Shortcut | +// |---------|-----------| +// | VS Code | Ctrl+T pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec { let _p = tracing::info_span!("world_symbols", query = ?query.query).entered(); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index bbdeb7cf0850..0cc80bda2c8a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -12,7 +12,7 @@ use crate::{ // Diagnostic: incorrect-ident-case // -// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. +// This diagnostic is triggered if an item name doesn't follow [Rust naming convention](https://doc.rust-lang.org/1.0.0/style/style/naming/README.html). pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic { let code = match d.expected_case { CaseType::LowerSnakeCase => DiagnosticCode::RustcLint("non_snake_case"), diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs index 6b654f893451..889258c94c53 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs @@ -33,12 +33,10 @@ // // Supported constraints: // -// |=== -// | Constraint | Restricts placeholder -// -// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`) -// | not(a) | Negates the constraint `a` -// |=== +// | Constraint | Restricts placeholder | +// |---------------|------------------------| +// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`) | +// | not(a) | Negates the constraint `a` | // // Available via the command `rust-analyzer.ssr`. // @@ -52,11 +50,9 @@ // String::from((y + 5).foo(z)) // ``` // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Structural Search Replace** -// |=== +// | Editor | Action Name | +// |---------|--------------| +// | VS Code | **rust-analyzer: Structural Search Replace** | // // Also available as an assist, by writing a comment containing the structural // search and replace rule. You will only see the assist if the comment can diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index 18f866eb9fc9..006e6e8246e1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -21,7 +21,7 @@ mod fn_references; // Provides user with annotations above items for looking up references or impl blocks // and running/debugging binaries. // -// image::https://user-images.githubusercontent.com/48062697/113020672-b7c34f00-917a-11eb-8f6e-858735660a0e.png[] +// ![Annotations](https://user-images.githubusercontent.com/48062697/113020672-b7c34f00-917a-11eb-8f6e-858735660a0e.png) #[derive(Debug, Hash, PartialEq, Eq)] pub struct Annotation { pub range: TextRange, diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index cfd8919730ad..e35e47e74718 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -122,11 +122,9 @@ pub(crate) fn remove_links(markdown: &str) -> String { // The simplest way to use this feature is via the context menu. Right-click on // the selected item. The context menu opens. Select **Open Docs**. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Open Docs** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Open Docs** | pub(crate) fn external_docs( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 0ad894427b2c..ad4308e06a14 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -19,13 +19,11 @@ pub struct ExpandedMacro { // // Shows the full macro expansion of the macro at the current caret position. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Expand macro recursively at caret** | // -// | VS Code | **rust-analyzer: Expand macro recursively at caret** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[] +// ![Expand Macro Recursively](https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif) pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option { let sema = Semantics::new(db); let file = sema.parse_guess_edition(position.file_id); diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs index 3d49082f2858..76414854e91e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs +++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs @@ -17,13 +17,11 @@ use crate::FileRange; // Extends or shrinks the current selection to the encompassing syntactic construct // (expression, statement, item, module, etc). It works with multiple cursors. // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | Alt+Shift+→, Alt+Shift+← | // -// | VS Code | kbd:[Alt+Shift+→], kbd:[Alt+Shift+←] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020651-b42fc800-917a-11eb-8a4f-cf1a07859fac.gif[] +// ![Expand and Shrink Selection](https://user-images.githubusercontent.com/48062697/113020651-b42fc800-917a-11eb-8a4f-cf1a07859fac.gif) pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { let sema = Semantics::new(db); let src = sema.parse_guess_edition(frange.file_id); diff --git a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs index 37b3cb03b333..5ed214443074 100644 --- a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs +++ b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs @@ -14,13 +14,11 @@ pub struct CrateInfo { // // Shows a view tree with all the dependencies of this project // -// |=== -// | Editor | Panel Name +// | Editor | Panel Name | +// |---------|------------| +// | VS Code | **Rust Dependencies** | // -// | VS Code | **Rust Dependencies** -// |=== -// -// image::https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png[] +// ![Show Dependency Tree](https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png) pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet { let crate_graph = db.crate_graph(); crate_graph diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index 50977ee840c8..52fbab6fa12b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -31,14 +31,11 @@ pub enum StructureNodeKind { // * draw breadcrumbs to describe the context around the cursor // * draw outline of the file // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | Ctrl+Shift+O | // -// | VS Code | kbd:[Ctrl+Shift+O] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif[] - +// ![File Structure](https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif) pub(crate) fn file_structure(file: &SourceFile) -> Vec { let mut res = Vec::new(); let mut stack = Vec::new(); diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index d18732a6b846..45f694d2fb6e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -31,13 +31,11 @@ use syntax::{ // // For outline modules, this will navigate to the source file of the module. // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | F12 | // -// | VS Code | kbd:[F12] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[] +// ![Go to Definition](https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif) pub(crate) fn goto_definition( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index e926378367ec..e1d834b5d1c6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -12,13 +12,11 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // // Navigates to the impl items of types. // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | Ctrl+F12 // -// | VS Code | kbd:[Ctrl+F12] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[] +// ![Go to Implementation](https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif) pub(crate) fn goto_implementation( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index 2610d6c8863a..ddc274a83035 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -8,13 +8,11 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // // Navigates to the type of an identifier. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **Go to Type Definition** | // -// | VS Code | **Go to Type Definition** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[] +// ![Go to Type Definition](https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif) pub(crate) fn goto_type_definition( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 612bc36f6284..6463206596af 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -43,12 +43,12 @@ pub struct HighlightRelatedConfig { // // Highlights constructs related to the thing under the cursor: // -// . if on an identifier, highlights all references to that identifier in the current file -// .. additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope -// . if on an `async` or `await` token, highlights all yield points for that async context -// . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context -// . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context -// . if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure. +// 1. if on an identifier, highlights all references to that identifier in the current file +// * additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope +// 1. if on an `async` or `await` token, highlights all yield points for that async context +// 1. if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context +// 1. if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context +// 1. if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure. // // Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor. pub(crate) fn highlight_related( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 18a3fed07ece..c8d0c6a250ca 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -118,7 +118,7 @@ pub struct HoverResult { // Shows additional information, like the type of an expression or the documentation for a definition when "focusing" code. // Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. // -// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[] +// ![Hover](https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif) pub(crate) fn hover( db: &RootDatabase, frange @ FileRange { file_id, range }: FileRange, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 1f723c85df7a..5955876d4468 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -59,7 +59,7 @@ mod range_exclusive; // // Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if // any of the -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria] +// [following criteria](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99) // are met: // // * the parameter name is a suffix of the function's name @@ -68,13 +68,13 @@ mod range_exclusive; // of argument with _ splitting it off // * the parameter name starts with `ra_fixture` // * the parameter name is a -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name] +// [well known name](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200) // in a unary function // * the parameter name is a -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character] +// [single character](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201) // in a unary function // -// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[] +// ![Inlay hints](https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png) pub(crate) fn inlay_hints( db: &RootDatabase, file_id: FileId, diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret.rs b/src/tools/rust-analyzer/crates/ide/src/interpret.rs index e0fdc3dd6f97..ae11072e34b7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/interpret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/interpret.rs @@ -7,11 +7,9 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange}; // Feature: Interpret A Function, Static Or Const. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Interpret** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Interpret** | pub(crate) fn interpret(db: &RootDatabase, position: FilePosition) -> String { match find_and_interpret(db, position) { Some((duration, mut result)) => { diff --git a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs index e4670177ecf2..ea18a97070c3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs +++ b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs @@ -21,17 +21,13 @@ pub struct JoinLinesConfig { // // Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces. // -// See -// https://user-images.githubusercontent.com/1711539/124515923-4504e800-dde9-11eb-8d58-d97945a1a785.gif[this gif] -// for the cases handled specially by joined lines. +// See [this gif](https://user-images.githubusercontent.com/1711539/124515923-4504e800-dde9-11eb-8d58-d97945a1a785.gif) for the cases handled specially by joined lines. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Join lines** | // -// | VS Code | **rust-analyzer: Join lines** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif[] +// ![Join Lines](https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif) pub(crate) fn join_lines( config: &JoinLinesConfig, file: &SourceFile, diff --git a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs index 573561528366..67346ea9cf90 100644 --- a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs @@ -9,13 +9,11 @@ use syntax::{ // moves cursor to the matching brace. It uses the actual parser to determine // braces, so it won't confuse generics with comparisons. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Find matching brace** | // -// | VS Code | **rust-analyzer: Find matching brace** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif[] +// ![Matching Brace](https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif) pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option { const BRACES: &[SyntaxKind] = &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]]; diff --git a/src/tools/rust-analyzer/crates/ide/src/move_item.rs b/src/tools/rust-analyzer/crates/ide/src/move_item.rs index b0df9257ba10..3fb3a788b918 100644 --- a/src/tools/rust-analyzer/crates/ide/src/move_item.rs +++ b/src/tools/rust-analyzer/crates/ide/src/move_item.rs @@ -17,14 +17,12 @@ pub enum Direction { // // Move item under cursor or selection up and down. // -// |=== -// | Editor | Action Name -// +// | Editor | Action Name | +// |---------|-------------| // | VS Code | **rust-analyzer: Move item up** // | VS Code | **rust-analyzer: Move item down** -// |=== // -// image::https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif[] +// ![Move Item](https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif) pub(crate) fn move_item( db: &RootDatabase, range: FileRange, diff --git a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs index 7a0c28d925ae..6d82f9b0634b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs +++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs @@ -15,13 +15,11 @@ use crate::NavigationTarget; // // Navigates to the parent module of the current module. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Locate parent module** | // -// | VS Code | **rust-analyzer: Locate parent module** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif[] +// ![Parent Module](https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif) /// This returns `Vec` because a module may be included from several places. pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec { diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index b1079312d3bb..069818d50e76 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -43,13 +43,11 @@ pub struct Declaration { // // Shows all references of the item at the cursor location // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | Shift+Alt+F12 | // -// | VS Code | kbd:[Shift+Alt+F12] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020670-b7c34f00-917a-11eb-8003-370ac5f2b3cb.gif[] +// ![Find All References](https://user-images.githubusercontent.com/48062697/113020670-b7c34f00-917a-11eb-8003-370ac5f2b3cb.gif) pub(crate) fn find_all_refs( sema: &Semantics<'_, RootDatabase>, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 07dfd83c4eb7..3d7ff5f76a01 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -71,13 +71,11 @@ pub(crate) fn prepare_rename( // // Renames the item below the cursor and all of its references // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | F2 | // -// | VS Code | kbd:[F2] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065582-055aae80-91b1-11eb-8ade-2b58e6d81883.gif[] +// ![Rename](https://user-images.githubusercontent.com/48062697/113065582-055aae80-91b1-11eb-8ade-2b58e6d81883.gif) pub(crate) fn rename( db: &RootDatabase, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 32edacee51c7..78c9f2309a0d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -119,12 +119,11 @@ impl Runnable { // location**. Super useful for repeatedly running just a single test. Do bind this // to a shortcut! // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Run** | // -// | VS Code | **rust-analyzer: Run** -// |=== -// image::https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif[] +// ![Run](https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif) pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { let sema = Semantics::new(db); @@ -207,11 +206,9 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { // The simplest way to use this feature is via the context menu. Right-click on // the selected item. The context menu opens. Select **Peek Related Tests**. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Peek Related Tests** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Peek Related Tests** | pub(crate) fn related_tests( db: &RootDatabase, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs index 9e823daa2bec..b0022cfac765 100644 --- a/src/tools/rust-analyzer/crates/ide/src/status.rs +++ b/src/tools/rust-analyzer/crates/ide/src/status.rs @@ -29,12 +29,11 @@ use triomphe::Arc; // // Shows internal statistic about memory usage of rust-analyzer. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Status** | // -// | VS Code | **rust-analyzer: Status** -// |=== -// image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[] +// ![Status](https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif) pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { let mut buf = String::new(); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index f53f0aec0984..f42b520c3dc9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -76,113 +76,118 @@ pub struct HighlightConfig { // We also give special modifier for `mut` and `&mut` local variables. // // -// .Token Tags +// #### Token Tags // // Rust-analyzer currently emits the following token tags: // // - For items: -// + -// [horizontal] -// attribute:: Emitted for attribute macros. -// enum:: Emitted for enums. -// function:: Emitted for free-standing functions. -// derive:: Emitted for derive macros. -// macro:: Emitted for function-like macros. -// method:: Emitted for associated functions, also knowns as methods. -// namespace:: Emitted for modules. -// struct:: Emitted for structs. -// trait:: Emitted for traits. -// typeAlias:: Emitted for type aliases and `Self` in `impl`s. -// union:: Emitted for unions. +// +// | | | +// |-----------|--------------------------------| +// | attribute | Emitted for attribute macros. | +// |enum| Emitted for enums. | +// |function| Emitted for free-standing functions. | +// |derive| Emitted for derive macros. | +// |macro| Emitted for function-like macros. | +// |method| Emitted for associated functions, also knowns as methods. | +// |namespace| Emitted for modules. | +// |struct| Emitted for structs.| +// |trait| Emitted for traits.| +// |typeAlias| Emitted for type aliases and `Self` in `impl`s.| +// |union| Emitted for unions.| // // - For literals: -// + -// [horizontal] -// boolean:: Emitted for the boolean literals `true` and `false`. -// character:: Emitted for character literals. -// number:: Emitted for numeric literals. -// string:: Emitted for string literals. -// escapeSequence:: Emitted for escaped sequences inside strings like `\n`. -// formatSpecifier:: Emitted for format specifiers `{:?}` in `format!`-like macros. +// +// | | | +// |-----------|--------------------------------| +// | boolean| Emitted for the boolean literals `true` and `false`.| +// | character| Emitted for character literals.| +// | number| Emitted for numeric literals.| +// | string| Emitted for string literals.| +// | escapeSequence| Emitted for escaped sequences inside strings like `\n`.| +// | formatSpecifier| Emitted for format specifiers `{:?}` in `format!`-like macros.| // // - For operators: -// + -// [horizontal] -// operator:: Emitted for general operators. -// arithmetic:: Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`. -// bitwise:: Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`. -// comparison:: Emitted for the comparison operators `>`, `<`, `==`, `>=`, `<=`, `!=`. -// logical:: Emitted for the logical operators `||`, `&&`, `!`. +// +// | | | +// |-----------|--------------------------------| +// |operator| Emitted for general operators.| +// |arithmetic| Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`.| +// |bitwise| Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`.| +// |comparison| Emitted for the comparison oerators `>`, `<`, `==`, `>=`, `<=`, `!=`.| +// |logical| Emitted for the logical operatos `||`, `&&`, `!`.| // // - For punctuation: -// + -// [horizontal] -// punctuation:: Emitted for general punctuation. -// attributeBracket:: Emitted for attribute invocation brackets, that is the `#[` and `]` tokens. -// angle:: Emitted for `<>` angle brackets. -// brace:: Emitted for `{}` braces. -// bracket:: Emitted for `[]` brackets. -// parenthesis:: Emitted for `()` parentheses. -// colon:: Emitted for the `:` token. -// comma:: Emitted for the `,` token. -// dot:: Emitted for the `.` token. -// semi:: Emitted for the `;` token. -// macroBang:: Emitted for the `!` token in macro calls. // -// //- +// | | | +// |-----------|--------------------------------| +// |punctuation| Emitted for general punctuation.| +// |attributeBracket| Emitted for attribute invocation brackets, that is the `#[` and `]` tokens.| +// |angle| Emitted for `<>` angle brackets.| +// |brace| Emitted for `{}` braces.| +// |bracket| Emitted for `[]` brackets.| +// |parenthesis| Emitted for `()` parentheses.| +// |colon| Emitted for the `:` token.| +// |comma| Emitted for the `,` token.| +// |dot| Emitted for the `.` token.| +// |semi| Emitted for the `;` token.| +// |macroBang| Emitted for the `!` token in macro calls.| // -// [horizontal] -// builtinAttribute:: Emitted for names to builtin attributes in attribute path, the `repr` in `#[repr(u8)]` for example. -// builtinType:: Emitted for builtin types like `u32`, `str` and `f32`. -// comment:: Emitted for comments. -// constParameter:: Emitted for const parameters. -// deriveHelper:: Emitted for derive helper attributes. -// enumMember:: Emitted for enum variants. -// generic:: Emitted for generic tokens that have no mapping. -// keyword:: Emitted for keywords. -// label:: Emitted for labels. -// lifetime:: Emitted for lifetimes. -// parameter:: Emitted for non-self function parameters. -// property:: Emitted for struct and union fields. -// selfKeyword:: Emitted for the self function parameter and self path-specifier. -// selfTypeKeyword:: Emitted for the Self type parameter. -// toolModule:: Emitted for tool modules. -// typeParameter:: Emitted for type parameters. -// unresolvedReference:: Emitted for unresolved references, names that rust-analyzer can't find the definition of. -// variable:: Emitted for locals, constants and statics. +//- +// +// | | | +// |-----------|--------------------------------| +// |builtinAttribute| Emitted for names to builtin attributes in attribute path, the `repr` in `#[repr(u8)]` for example.| +// |builtinType| Emitted for builtin types like `u32`, `str` and `f32`.| +// |comment| Emitted for comments.| +// |constParameter| Emitted for const parameters.| +// |deriveHelper| Emitted for derive helper attributes.| +// |enumMember| Emitted for enum variants.| +// |generic| Emitted for generic tokens that have no mapping.| +// |keyword| Emitted for keywords.| +// |label| Emitted for labels.| +// |lifetime| Emitted for lifetimes.| +// |parameter| Emitted for non-self function parameters.| +// |property| Emitted for struct and union fields.| +// |selfKeyword| Emitted for the self function parameter and self path-specifier.| +// |selfTypeKeyword| Emitted for the Self type parameter.| +// |toolModule| Emitted for tool modules.| +// |typeParameter| Emitted for type parameters.| +// |unresolvedReference| Emitted for unresolved references, names that rust-analyzer can't find the definition of.| +// |variable| Emitted for locals, constants and statics.| // // -// .Token Modifiers +// #### Token Modifiers // // Token modifiers allow to style some elements in the source code more precisely. // // Rust-analyzer currently emits the following token modifiers: // -// [horizontal] -// async:: Emitted for async functions and the `async` and `await` keywords. -// attribute:: Emitted for tokens inside attributes. -// callable:: Emitted for locals whose types implements one of the `Fn*` traits. -// constant:: Emitted for consts. -// consuming:: Emitted for locals that are being consumed when use in a function call. -// controlFlow:: Emitted for control-flow related tokens, this includes the `?` operator. -// crateRoot:: Emitted for crate names, like `serde` and `crate`. -// declaration:: Emitted for names of definitions, like `foo` in `fn foo() {}`. -// defaultLibrary:: Emitted for items from built-in crates (std, core, alloc, test and proc_macro). -// documentation:: Emitted for documentation comments. -// injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation. -// intraDocLink:: Emitted for intra doc links in doc-strings. -// library:: Emitted for items that are defined outside of the current crate. -// macro:: Emitted for tokens inside macro calls. -// mutable:: Emitted for mutable locals and statics as well as functions taking `&mut self`. -// public:: Emitted for items that are from the current crate and are `pub`. -// reference:: Emitted for locals behind a reference and functions taking `self` by reference. -// static:: Emitted for "static" functions, also known as functions that do not take a `self` param, as well as statics and consts. -// trait:: Emitted for associated trait items. -// unsafe:: Emitted for unsafe operations, like unsafe function calls, as well as the `unsafe` token. +// | | | +// |-----------|--------------------------------| +// |async| Emitted for async functions and the `async` and `await` keywords.| +// |attribute| Emitted for tokens inside attributes.| +// |callable| Emitted for locals whose types implements one of the `Fn*` traits.| +// |constant| Emitted for const.| +// |consuming| Emitted for locals that are being consumed when use in a function call.| +// |controlFlow| Emitted for control-flow related tokens, this includes th `?` operator.| +// |crateRoot| Emitted for crate names, like `serde` and `crate.| +// |declaration| Emitted for names of definitions, like `foo` in `fn foo(){}`.| +// |defaultLibrary| Emitted for items from built-in crates (std, core, allc, test and proc_macro).| +// |documentation| Emitted for documentation comment.| +// |injected| Emitted for doc-string injected highlighting like rust source blocks in documentation.| +// |intraDocLink| Emitted for intra doc links in doc-string.| +// |library| Emitted for items that are defined outside of the current crae.| +// |macro| Emitted for tokens inside macro call.| +// |mutable| Emitted for mutable locals and statics as well as functions taking `&mut self`.| +// |public| Emitted for items that are from the current crate and are `pub.| +// |reference| Emitted for locals behind a reference and functions taking self` by reference.| +// |static| Emitted for "static" functions, also known as functions that d not take a `self` param, as well as statics and consts.| +// |trait| Emitted for associated trait item.| +// |unsafe| Emitted for unsafe operations, like unsafe function calls, as ell as the `unsafe` token.| // -// -// image::https://user-images.githubusercontent.com/48062697/113164457-06cfb980-9239-11eb-819b-0f93e646acf8.png[] -// image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[] +// ![Semantic Syntax Highlighting](https://user-images.githubusercontent.com/48062697/113164457-06cfb980-9239-11eb-819b-0f93e646acf8.png) +// ![Semantic Syntax Highlighting](https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png) pub(crate) fn highlight( db: &RootDatabase, config: HighlightConfig, diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index 47d75f1c9570..8c9dd0514527 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -51,16 +51,15 @@ struct ExtendedTextEdit { // - typing `{` in a use item adds a closing `}` in the right place // - typing `>` to complete a return type `->` will insert a whitespace after it // -// VS Code:: +// #### VS Code // // Add the following to `settings.json`: -// [source,json] -// ---- +// ```json // "editor.formatOnType": true, -// ---- +// ``` // -// image::https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif[] -// image::https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif[] +// ![On Typing Assists](https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif) +// ![On Typing Assists](https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif) pub(crate) fn on_char_typed( db: &RootDatabase, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs index e249c38c73d6..c6d1c283f4ec 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs @@ -16,12 +16,12 @@ use ide_db::text_edit::TextEdit; // Feature: On Enter // -// rust-analyzer can override kbd:[Enter] key to make it smarter: +// rust-analyzer can override Enter key to make it smarter: // -// - kbd:[Enter] inside triple-slash comments automatically inserts `///` -// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//` -// - kbd:[Enter] inside `//!` doc comments automatically inserts `//!` -// - kbd:[Enter] after `{` indents contents and closing `}` of single-line block +// - Enter inside triple-slash comments automatically inserts `///` +// - Enter in the middle or after a trailing space in `//` inserts `//` +// - Enter inside `//!` doc comments automatically inserts `//!` +// - Enter after `{` indents contents and closing `}` of single-line block // // This action needs to be assigned to shortcut explicitly. // @@ -29,29 +29,27 @@ use ide_db::text_edit::TextEdit; // Similarly, if rust-analyzer crashes or stops responding, `Enter` might not work. // In that case, you can still press `Shift-Enter` to insert a newline. // -// VS Code:: +// #### VS Code // // Add the following to `keybindings.json`: -// [source,json] -// ---- +// ```json // { // "key": "Enter", // "command": "rust-analyzer.onEnter", // "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust" // } -// ---- +// ```` // // When using the Vim plugin: -// [source,json] -// ---- +// ```json // { // "key": "Enter", // "command": "rust-analyzer.onEnter", // "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust && vim.mode == 'Insert'" // } -// ---- +// ```` // -// image::https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif[] +// ![On Enter](https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif) pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option { let parse = db.parse(EditionedFileId::current_edition(position.file_id)); let file = parse.tree(); diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs index 9ff099f479e7..24a986b85bf4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs @@ -12,11 +12,9 @@ use triomphe::Arc; // // Only workspace crates are included, no crates.io dependencies or sysroot crates. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: View Crate Graph** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: View Crate Graph** | pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result { let crate_graph = db.crate_graph(); let crates_to_render = crate_graph diff --git a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs index fe532f4cc55e..bfdf9d0f3374 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs @@ -4,12 +4,11 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode}; // Feature: View Hir // -// |=== -// | Editor | Action Name -// +// | Editor | Action Name | +// |---------|--------------| // | VS Code | **rust-analyzer: View Hir** -// |=== -// image::https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif[] +// +// ![View Hir](https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif) pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String { body_hir(db, position).unwrap_or_else(|| "Not inside a function body".to_owned()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs index a6352b99d4f5..67c241cbb915 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs @@ -6,11 +6,9 @@ use span::EditionedFileId; // // Displays the ItemTree of the currently open file, for debugging. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Debug ItemTree** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Debug ItemTree** | pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String { let sema = Semantics::new(db); let file_id = sema diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index ff74e05e9437..edb83bc4eac4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -75,11 +75,9 @@ impl FieldOrTupleIdx { // // Displays the recursive memory layout of a datatype. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: View Memory Layout** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: View Memory Layout** | pub(crate) fn view_memory_layout( db: &RootDatabase, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs index 7a228375d5e9..aa4ff64a819e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs @@ -4,11 +4,9 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode}; // Feature: View Mir // -// |=== -// | Editor | Action Name -// +// | Editor | Action Name | +// |---------|-------------| // | VS Code | **rust-analyzer: View Mir** -// |=== pub(crate) fn view_mir(db: &RootDatabase, position: FilePosition) -> String { body_mir(db, position).unwrap_or_else(|| "Not inside a function body".to_owned()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs index 218ee15a7dd3..f14ab45a5c32 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs @@ -11,11 +11,9 @@ use syntax::{ // // Shows a tree view with the syntax tree of the current file // -// |=== -// | Editor | Panel Name -// -// | VS Code | **Rust Syntax Tree** -// |=== +// | Editor | Panel Name | +// |---------|-------------| +// | VS Code | **Rust Syntax Tree** | pub(crate) fn view_syntax_tree(db: &RootDatabase, file_id: FileId) -> String { let sema = Semantics::new(db); let parse = sema.parse_guess_edition(file_id); diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 318f71a2d4df..79900425a17c 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen grammar`, do not edit by hand. +//! Generated by `cargo xtask codegen grammar`, do not edit by hand. #![allow(bad_style, missing_docs, unreachable_pub)] use crate::Edition; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 44325fa1a29e..7656c07c9485 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -528,7 +528,7 @@ config_data! { imports_granularity_enforce: bool = false, /// How imports should be grouped into use statements. imports_granularity_group: ImportGranularityDef = ImportGranularityDef::Crate, - /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines. + /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. imports_group_enable: bool = true, /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. imports_merge_glob: bool = true, @@ -3624,21 +3624,9 @@ fn manual(fields: &[SchemaField]) -> String { let name = format!("rust-analyzer.{}", field.replace('_', ".")); let doc = doc_comment_to_string(doc); if default.contains('\n') { - format_to_acc!( - acc, - r#"[[{name}]]{name}:: -+ --- -Default: ----- -{default} ----- -{doc} --- -"# - ) + format_to_acc!(acc, " **{name}**\n\nDefault:\n\n```{default}\n\n```\n\n {doc}\n\n ") } else { - format_to_acc!(acc, "[[{name}]]{name} (default: `{default}`)::\n+\n--\n{doc}--\n") + format_to_acc!(acc, "**{name}** (default: {default})\n\n {doc}\n\n") } }) } @@ -3716,7 +3704,7 @@ mod tests { #[test] fn generate_config_documentation() { - let docs_path = project_root().join("docs/user/generated_config.adoc"); + let docs_path = project_root().join("docs/book/src/configuration_generated.md"); let expected = FullConfigInput::manual(); ensure_file_contents(docs_path.as_std_path(), &expected); } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 69e2a9f9c1b2..287601e2e27f 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen grammar`, do not edit by hand. +//! Generated by `cargo xtask codegen grammar`, do not edit by hand. #![allow(non_snake_case)] use crate::{ diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs index 85d20c2bd8ce..df2e9619db1c 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen grammar`, do not edit by hand. +//! Generated by `cargo xtask codegen grammar`, do not edit by hand. use crate::{ ast::AstToken, diff --git a/src/tools/rust-analyzer/docs/book/README.md b/src/tools/rust-analyzer/docs/book/README.md new file mode 100644 index 000000000000..a9d10df66435 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/README.md @@ -0,0 +1,29 @@ +# rust-analyzer documentation + +The rust analyzer manual uses [mdbook](https://rust-lang.github.io/mdBook/). + +## Quick start + +To run the documentation site locally: + +```shell +cargo install mdbook +cd docs/book +mdbook serve +# make changes to documentation files in doc/book/src +# ... +``` + +mdbook will rebuild the documentation as changes are made. + +## Making updates + +While not required, installing the mdbook binary can be helfpul in order to see the changes. +Start with the mdbook [User Guide](https://rust-lang.github.io/mdBook/guide/installation.html) to familiarize yourself with the tool. + +## Generated documentation + +Four sections are generated dynamically: assists, configuration, diagnostics and features. Their content is found in the `generated.md` files +of the respective book section, for example `src/configuration_generated.md`, and are included in the book via mdbook's +[include](https://rust-lang.github.io/mdBook/format/mdbook.html#including-files) functionality. Generated files can be rebuilt by running the various +test cases that generate them, or by simply running all of the `rust-analyzer` tests with `cargo test`. diff --git a/src/tools/rust-analyzer/docs/book/book.toml b/src/tools/rust-analyzer/docs/book/book.toml new file mode 100644 index 000000000000..ba3c1dede5db --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/book.toml @@ -0,0 +1,41 @@ +[book] +authors = ["The rust-analyzer authors"] +language = "en" +multilingual = false +src = "src" +title = "rust-analyzer" + +[rust] +edition = "2021" + +[output.html] +edit-url-template = "https://github.com/rust-lang/rust-analyzer/edit/master/manual/{path}" +git-repository-url = "https://github.com/rust-lang/rust-analyzer/tree/master/manual" +mathjax-support = true +site-url = "/manual/" + +[output.html.playground] +editable = true +runnable = false +line-numbers = true + +[output.html.search] +boost-hierarchy = 2 +boost-paragraph = 1 +boost-title = 2 +expand = true +heading-split-level = 2 +limit-results = 20 +use-boolean-and = true + +[output.html.redirect] +"/manual.html" = "/index.html" + +[output.html.fold] +enable = true +level = 3 + +[preprocessor.toc] +command = "mdbook-toc" +renderer = ["html"] +max-level = 3 diff --git a/src/tools/rust-analyzer/docs/book/src/README.md b/src/tools/rust-analyzer/docs/book/src/README.md new file mode 100644 index 000000000000..545e4a3e62df --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/README.md @@ -0,0 +1,21 @@ +# rust-analyzer + +At its core, rust-analyzer is a **library** for semantic analysis of +Rust code as it changes over time. This manual focuses on a specific +usage of the library -- running it as part of a server that implements +the [Language Server +Protocol](https://microsoft.github.io/language-server-protocol/) (LSP). +The LSP allows various code editors, like VS Code, Emacs or Vim, to +implement semantic features like completion or goto definition by +talking to an external language server process. + +To improve this document, send a pull request: +[https://github.com/rust-lang/rust-analyzer](https://github.com/rust-lang/rust-analyzer/blob/docs/book/README.md) + +The manual is written in markdown and includes +some extra files which are generated from the source code. Run +`cargo test` and `cargo xtask codegen` to create these. + +If you have questions about using rust-analyzer, please ask them in the +["IDEs and Editors"](https://users.rust-lang.org/c/ide/14) topic of Rust +users forum. diff --git a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md new file mode 100644 index 000000000000..7e6cd5972d40 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md @@ -0,0 +1,13 @@ +# Summary + +- [Introduction](README.md) +- [Installation](installation.md) +- [Troubleshooting](troubleshooting.md) +- [Configuration](configuration.md) +- [Non-Cargo Based Projects](non_cargo_based_projects.md) +- [Security](security.md) +- [Privacy](privacy.md) +- [Features](features.md) +- [Assists (Code Actions)](assists.md) +- [Diagnostics](diagnostics.md) +- [Editor Features](editor_features.md) diff --git a/src/tools/rust-analyzer/docs/book/src/assists.md b/src/tools/rust-analyzer/docs/book/src/assists.md new file mode 100644 index 000000000000..9d7c3bc1d5b9 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/assists.md @@ -0,0 +1,8 @@ +# Assists + +Assists, or code actions, are small local refactorings, available in a +particular context. They are usually triggered by a shortcut or by +clicking a light bulb icon in the editor. Cursor position or selection +is signified by `┃` character. + +{{#include assists_generated.md:2:}} diff --git a/src/tools/rust-analyzer/docs/book/src/configuration.md b/src/tools/rust-analyzer/docs/book/src/configuration.md new file mode 100644 index 000000000000..221a571c17c8 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/configuration.md @@ -0,0 +1,51 @@ +# Configuration + +**Source:** +[config.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/config.rs) + +The [Installation](#_installation) section contains details on +configuration for some of the editors. In general `rust-analyzer` is +configured via LSP messages, which means that it’s up to the editor to +decide on the exact format and location of configuration files. + +Some clients, such as [VS Code](#vs-code) or [COC plugin in +Vim](#coc-rust-analyzer) provide `rust-analyzer` specific configuration +UIs. Others may require you to know a bit more about the interaction +with `rust-analyzer`. + +For the later category, it might help to know that the initial +configuration is specified as a value of the `initializationOptions` +field of the [`InitializeParams` message, in the LSP +protocol](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize). +The spec says that the field type is `any?`, but `rust-analyzer` is +looking for a JSON object that is constructed using settings from the +list below. Name of the setting, ignoring the `rust-analyzer.` prefix, +is used as a path, and value of the setting becomes the JSON property +value. + +For example, a very common configuration is to enable proc-macro +support, can be achieved by sending this JSON: + + { + "cargo": { + "buildScripts": { + "enable": true, + }, + }, + "procMacro": { + "enable": true, + } + } + +Please consult your editor’s documentation to learn more about how to +configure [LSP +servers](https://microsoft.github.io/language-server-protocol/). + +To verify which configuration is actually used by `rust-analyzer`, set +`RA_LOG` environment variable to `rust_analyzer=info` and look for +config-related messages. Logs should show both the JSON that +`rust-analyzer` sees as well as the updated config. + +This is the list of config options `rust-analyzer` supports: + +{{#include configuration_generated.md}} diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md new file mode 100644 index 000000000000..49eb7248898e --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -0,0 +1,1201 @@ +**rust-analyzer.assist.emitMustUse** (default: false) + + Whether to insert #[must_use] when generating `as_` methods +for enum variants. + + +**rust-analyzer.assist.expressionFillDefault** (default: "todo") + + Placeholder expression to use for missing expressions in assists. + + +**rust-analyzer.assist.termSearch.borrowcheck** (default: true) + + Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check. + + +**rust-analyzer.assist.termSearch.fuel** (default: 1800) + + Term search fuel in "units of work" for assists (Defaults to 1800). + + +**rust-analyzer.cachePriming.enable** (default: true) + + Warm up caches on project load. + + +**rust-analyzer.cachePriming.numThreads** (default: "physical") + + How many worker threads to handle priming caches. The default `0` means to pick automatically. + + +**rust-analyzer.cargo.allTargets** (default: true) + + Pass `--all-targets` to cargo invocation. + + +**rust-analyzer.cargo.autoreload** (default: true) + + Automatically refresh project info via `cargo metadata` on +`Cargo.toml` or `.cargo/config.toml` changes. + + +**rust-analyzer.cargo.buildScripts.enable** (default: true) + + Run build scripts (`build.rs`) for more precise code analysis. + + +**rust-analyzer.cargo.buildScripts.invocationStrategy** (default: "per_workspace") + + Specifies the invocation strategy to use when running the build scripts command. +If `per_workspace` is set, the command will be executed for each Rust workspace with the +workspace as the working directory. +If `once` is set, the command will be executed once with the opened project as the +working directory. +This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` +is set. + + +**rust-analyzer.cargo.buildScripts.overrideCommand** (default: null) + + Override the command rust-analyzer uses to run build scripts and +build procedural macros. The command is required to output json +and should therefore include `--message-format=json` or a similar +option. + +If there are multiple linked projects/workspaces, this command is invoked for +each of them, with the working directory being the workspace root +(i.e., the folder containing the `Cargo.toml`). This can be overwritten +by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`. + +By default, a cargo invocation will be constructed for the configured +targets and features, with the following base command line: + +```bash +cargo check --quiet --workspace --message-format=json --all-targets --keep-going +``` +. + + +**rust-analyzer.cargo.buildScripts.rebuildOnSave** (default: true) + + Rerun proc-macros building/build-scripts running when proc-macro +or build-script sources change and are saved. + + +**rust-analyzer.cargo.buildScripts.useRustcWrapper** (default: true) + + Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to +avoid checking unnecessary things. + + + **rust-analyzer.cargo.cfgs** + +Default: + +```[ + "debug_assertions", + "miri" +] + +``` + + List of cfg options to enable with the given values. + + + **rust-analyzer.cargo.extraArgs** (default: []) + + Extra arguments that are passed to every cargo invocation. + + +**rust-analyzer.cargo.extraEnv** (default: {}) + + Extra environment variables that will be set when running cargo, rustc +or other commands within the workspace. Useful for setting RUSTFLAGS. + + +**rust-analyzer.cargo.features** (default: []) + + List of features to activate. + +Set this to `"all"` to pass `--all-features` to cargo. + + +**rust-analyzer.cargo.noDefaultFeatures** (default: false) + + Whether to pass `--no-default-features` to cargo. + + +**rust-analyzer.cargo.sysroot** (default: "discover") + + Relative path to the sysroot, or "discover" to try to automatically find it via +"rustc --print sysroot". + +Unsetting this disables sysroot loading. + +This option does not take effect until rust-analyzer is restarted. + + +**rust-analyzer.cargo.sysrootSrc** (default: null) + + Relative path to the sysroot library sources. If left unset, this will default to +`{cargo.sysroot}/lib/rustlib/src/rust/library`. + +This option does not take effect until rust-analyzer is restarted. + + +**rust-analyzer.cargo.target** (default: null) + + Compilation target override (target tuple). + + +**rust-analyzer.cargo.targetDir** (default: null) + + Optional path to a rust-analyzer specific target directory. +This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro +building from locking the `Cargo.lock` at the expense of duplicating build artifacts. + +Set to `true` to use a subdirectory of the existing target directory or +set to a path relative to the workspace to use that path. + + +**rust-analyzer.cfg.setTest** (default: true) + + Set `cfg(test)` for local crates. Defaults to true. + + +**rust-analyzer.checkOnSave** (default: true) + + Run the check command for diagnostics on save. + + +**rust-analyzer.check.allTargets** (default: null) + + Check all targets and tests (`--all-targets`). Defaults to +`#rust-analyzer.cargo.allTargets#`. + + +**rust-analyzer.check.command** (default: "check") + + Cargo command to use for `cargo check`. + + +**rust-analyzer.check.extraArgs** (default: []) + + Extra arguments for `cargo check`. + + +**rust-analyzer.check.extraEnv** (default: {}) + + Extra environment variables that will be set when running `cargo check`. +Extends `#rust-analyzer.cargo.extraEnv#`. + + +**rust-analyzer.check.features** (default: null) + + List of features to activate. Defaults to +`#rust-analyzer.cargo.features#`. + +Set to `"all"` to pass `--all-features` to Cargo. + + +**rust-analyzer.check.ignore** (default: []) + + List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. + +For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... + + +**rust-analyzer.check.invocationStrategy** (default: "per_workspace") + + Specifies the invocation strategy to use when running the check command. +If `per_workspace` is set, the command will be executed for each workspace. +If `once` is set, the command will be executed once. +This config only has an effect when `#rust-analyzer.check.overrideCommand#` +is set. + + +**rust-analyzer.check.noDefaultFeatures** (default: null) + + Whether to pass `--no-default-features` to Cargo. Defaults to +`#rust-analyzer.cargo.noDefaultFeatures#`. + + +**rust-analyzer.check.overrideCommand** (default: null) + + Override the command rust-analyzer uses instead of `cargo check` for +diagnostics on save. The command is required to output json and +should therefore include `--message-format=json` or a similar option +(if your client supports the `colorDiagnosticOutput` experimental +capability, you can use `--message-format=json-diagnostic-rendered-ansi`). + +If you're changing this because you're using some tool wrapping +Cargo, you might also want to change +`#rust-analyzer.cargo.buildScripts.overrideCommand#`. + +If there are multiple linked projects/workspaces, this command is invoked for +each of them, with the working directory being the workspace root +(i.e., the folder containing the `Cargo.toml`). This can be overwritten +by changing `#rust-analyzer.check.invocationStrategy#`. + +If `$saved_file` is part of the command, rust-analyzer will pass +the absolute path of the saved file to the provided command. This is +intended to be used with non-Cargo build systems. +Note that `$saved_file` is experimental and may be removed in the future. + +An example command would be: + +```bash +cargo check --workspace --message-format=json --all-targets +``` +. + + +**rust-analyzer.check.targets** (default: null) + + Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. + +Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. +`["aarch64-apple-darwin", "x86_64-apple-darwin"]`. + +Aliased as `"checkOnSave.targets"`. + + +**rust-analyzer.check.workspace** (default: true) + + Whether `--workspace` should be passed to `cargo check`. +If false, `-p ` will be passed instead if applicable. In case it is not, no +check will be performed. + + +**rust-analyzer.completion.addSemicolonToUnit** (default: true) + + Whether to automatically add a semicolon when completing unit-returning functions. + +In `match` arms it completes a comma instead. + + +**rust-analyzer.completion.autoAwait.enable** (default: true) + + Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. + + +**rust-analyzer.completion.autoIter.enable** (default: true) + + Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. + + +**rust-analyzer.completion.autoimport.enable** (default: true) + + Toggles the additional completions that automatically add imports when completed. +Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. + + + **rust-analyzer.completion.autoimport.exclude** + +Default: + +```[ + { + "path": "core::borrow::Borrow", + "type": "methods" + }, + { + "path": "core::borrow::BorrowMut", + "type": "methods" + } +] + +``` + + A list of full paths to items to exclude from auto-importing completions. + +Traits in this list won't have their methods suggested in completions unless the trait +is in scope. + +You can either specify a string path which defaults to type "always" or use the more verbose +form `{ "path": "path::to::item", type: "always" }`. + +For traits the type "methods" can be used to only exclude the methods but not the trait itself. + +This setting also inherits `#rust-analyzer.completion.excludeTraits#`. + + + **rust-analyzer.completion.autoself.enable** (default: true) + + Toggles the additional completions that automatically show method calls and field accesses +with `self` prefixed to them when inside a method. + + +**rust-analyzer.completion.callable.snippets** (default: "fill_arguments") + + Whether to add parenthesis and argument snippets when completing function. + + +**rust-analyzer.completion.excludeTraits** (default: []) + + A list of full paths to traits whose methods to exclude from completion. + +Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`. + +Note that the trait themselves can still be completed. + + +**rust-analyzer.completion.fullFunctionSignatures.enable** (default: false) + + Whether to show full function/method signatures in completion docs. + + +**rust-analyzer.completion.hideDeprecated** (default: false) + + Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. + + +**rust-analyzer.completion.limit** (default: null) + + Maximum number of completions to return. If `None`, the limit is infinite. + + +**rust-analyzer.completion.postfix.enable** (default: true) + + Whether to show postfix snippets like `dbg`, `if`, `not`, etc. + + +**rust-analyzer.completion.privateEditable.enable** (default: false) + + Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. + + + **rust-analyzer.completion.snippets.custom** + +Default: + +```{ + "Ok": { + "postfix": "ok", + "body": "Ok(${receiver})", + "description": "Wrap the expression in a `Result::Ok`", + "scope": "expr" + }, + "Box::pin": { + "postfix": "pinbox", + "body": "Box::pin(${receiver})", + "requires": "std::boxed::Box", + "description": "Put the expression into a pinned `Box`", + "scope": "expr" + }, + "Arc::new": { + "postfix": "arc", + "body": "Arc::new(${receiver})", + "requires": "std::sync::Arc", + "description": "Put the expression into an `Arc`", + "scope": "expr" + }, + "Some": { + "postfix": "some", + "body": "Some(${receiver})", + "description": "Wrap the expression in an `Option::Some`", + "scope": "expr" + }, + "Err": { + "postfix": "err", + "body": "Err(${receiver})", + "description": "Wrap the expression in a `Result::Err`", + "scope": "expr" + }, + "Rc::new": { + "postfix": "rc", + "body": "Rc::new(${receiver})", + "requires": "std::rc::Rc", + "description": "Put the expression into an `Rc`", + "scope": "expr" + } +} + +``` + + Custom completion snippets. + + + **rust-analyzer.completion.termSearch.enable** (default: false) + + Whether to enable term search based snippets like `Some(foo.bar().baz())`. + + +**rust-analyzer.completion.termSearch.fuel** (default: 1000) + + Term search fuel in "units of work" for autocompletion (Defaults to 1000). + + +**rust-analyzer.diagnostics.disabled** (default: []) + + List of rust-analyzer diagnostics to disable. + + +**rust-analyzer.diagnostics.enable** (default: true) + + Whether to show native rust-analyzer diagnostics. + + +**rust-analyzer.diagnostics.experimental.enable** (default: false) + + Whether to show experimental rust-analyzer diagnostics that might +have more false positives than usual. + + +**rust-analyzer.diagnostics.remapPrefix** (default: {}) + + Map of prefixes to be substituted when parsing diagnostic file paths. +This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. + + +**rust-analyzer.diagnostics.styleLints.enable** (default: false) + + Whether to run additional style lints. + + +**rust-analyzer.diagnostics.warningsAsHint** (default: []) + + List of warnings that should be displayed with hint severity. + +The warnings will be indicated by faded text or three dots in code +and will not show up in the `Problems Panel`. + + +**rust-analyzer.diagnostics.warningsAsInfo** (default: []) + + List of warnings that should be displayed with info severity. + +The warnings will be indicated by a blue squiggly underline in code +and a blue icon in the `Problems Panel`. + + +**rust-analyzer.files.excludeDirs** (default: []) + + These directories will be ignored by rust-analyzer. They are +relative to the workspace root, and globs are not supported. You may +also need to add the folders to Code's `files.watcherExclude`. + + +**rust-analyzer.files.watcher** (default: "client") + + Controls file watching implementation. + + +**rust-analyzer.highlightRelated.breakPoints.enable** (default: true) + + Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. + + +**rust-analyzer.highlightRelated.closureCaptures.enable** (default: true) + + Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. + + +**rust-analyzer.highlightRelated.exitPoints.enable** (default: true) + + Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). + + +**rust-analyzer.highlightRelated.references.enable** (default: true) + + Enables highlighting of related references while the cursor is on any identifier. + + +**rust-analyzer.highlightRelated.yieldPoints.enable** (default: true) + + Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. + + +**rust-analyzer.hover.actions.debug.enable** (default: true) + + Whether to show `Debug` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.enable** (default: true) + + Whether to show HoverActions in Rust files. + + +**rust-analyzer.hover.actions.gotoTypeDef.enable** (default: true) + + Whether to show `Go to Type Definition` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.implementations.enable** (default: true) + + Whether to show `Implementations` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.references.enable** (default: false) + + Whether to show `References` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.run.enable** (default: true) + + Whether to show `Run` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.updateTest.enable** (default: true) + + Whether to show `Update Test` action. Only applies when +`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set. + + +**rust-analyzer.hover.documentation.enable** (default: true) + + Whether to show documentation on hover. + + +**rust-analyzer.hover.documentation.keywords.enable** (default: true) + + Whether to show keyword hover popups. Only applies when +`#rust-analyzer.hover.documentation.enable#` is set. + + +**rust-analyzer.hover.links.enable** (default: true) + + Use markdown syntax for links on hover. + + +**rust-analyzer.hover.maxSubstitutionLength** (default: 20) + + Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis. + +This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters. + +The default is 20 characters. + + +**rust-analyzer.hover.memoryLayout.alignment** (default: "hexadecimal") + + How to render the align information in a memory layout hover. + + +**rust-analyzer.hover.memoryLayout.enable** (default: true) + + Whether to show memory layout data on hover. + + +**rust-analyzer.hover.memoryLayout.niches** (default: false) + + How to render the niche information in a memory layout hover. + + +**rust-analyzer.hover.memoryLayout.offset** (default: "hexadecimal") + + How to render the offset information in a memory layout hover. + + +**rust-analyzer.hover.memoryLayout.size** (default: "both") + + How to render the size information in a memory layout hover. + + +**rust-analyzer.hover.show.enumVariants** (default: 5) + + How many variants of an enum to display when hovering on. Show none if empty. + + +**rust-analyzer.hover.show.fields** (default: 5) + + How many fields of a struct, variant or union to display when hovering on. Show none if empty. + + +**rust-analyzer.hover.show.traitAssocItems** (default: null) + + How many associated items of a trait to display when hovering a trait. + + +**rust-analyzer.imports.granularity.enforce** (default: false) + + Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. + + +**rust-analyzer.imports.granularity.group** (default: "crate") + + How imports should be grouped into use statements. + + +**rust-analyzer.imports.group.enable** (default: true) + + Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. + + +**rust-analyzer.imports.merge.glob** (default: true) + + Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. + + +**rust-analyzer.imports.preferNoStd** (default: false) + + Prefer to unconditionally use imports of the core and alloc crate, over the std crate. + + +**rust-analyzer.imports.preferPrelude** (default: false) + + Whether to prefer import paths containing a `prelude` module. + + +**rust-analyzer.imports.prefix** (default: "plain") + + The path structure for newly inserted paths to use. + + +**rust-analyzer.imports.prefixExternPrelude** (default: false) + + Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;". + + +**rust-analyzer.inlayHints.bindingModeHints.enable** (default: false) + + Whether to show inlay type hints for binding modes. + + +**rust-analyzer.inlayHints.chainingHints.enable** (default: true) + + Whether to show inlay type hints for method chains. + + +**rust-analyzer.inlayHints.closingBraceHints.enable** (default: true) + + Whether to show inlay hints after a closing `}` to indicate what item it belongs to. + + +**rust-analyzer.inlayHints.closingBraceHints.minLines** (default: 25) + + Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 +to always show them). + + +**rust-analyzer.inlayHints.closureCaptureHints.enable** (default: false) + + Whether to show inlay hints for closure captures. + + +**rust-analyzer.inlayHints.closureReturnTypeHints.enable** (default: "never") + + Whether to show inlay type hints for return types of closures. + + +**rust-analyzer.inlayHints.closureStyle** (default: "impl_fn") + + Closure notation in type and chaining inlay hints. + + +**rust-analyzer.inlayHints.discriminantHints.enable** (default: "never") + + Whether to show enum variant discriminant hints. + + +**rust-analyzer.inlayHints.expressionAdjustmentHints.enable** (default: "never") + + Whether to show inlay hints for type adjustments. + + +**rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe** (default: false) + + Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. + + +**rust-analyzer.inlayHints.expressionAdjustmentHints.mode** (default: "prefix") + + Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). + + +**rust-analyzer.inlayHints.genericParameterHints.const.enable** (default: true) + + Whether to show const generic parameter name inlay hints. + + +**rust-analyzer.inlayHints.genericParameterHints.lifetime.enable** (default: false) + + Whether to show generic lifetime parameter name inlay hints. + + +**rust-analyzer.inlayHints.genericParameterHints.type.enable** (default: false) + + Whether to show generic type parameter name inlay hints. + + +**rust-analyzer.inlayHints.implicitDrops.enable** (default: false) + + Whether to show implicit drop hints. + + +**rust-analyzer.inlayHints.implicitSizedBoundHints.enable** (default: false) + + Whether to show inlay hints for the implied type parameter `Sized` bound. + + +**rust-analyzer.inlayHints.lifetimeElisionHints.enable** (default: "never") + + Whether to show inlay type hints for elided lifetimes in function signatures. + + +**rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames** (default: false) + + Whether to prefer using parameter names as the name for elided lifetime hints if possible. + + +**rust-analyzer.inlayHints.maxLength** (default: 25) + + Maximum length for inlay hints. Set to null to have an unlimited length. + + +**rust-analyzer.inlayHints.parameterHints.enable** (default: true) + + Whether to show function parameter name inlay hints at the call +site. + + +**rust-analyzer.inlayHints.rangeExclusiveHints.enable** (default: false) + + Whether to show exclusive range inlay hints. + + +**rust-analyzer.inlayHints.reborrowHints.enable** (default: "never") + + Whether to show inlay hints for compiler inserted reborrows. +This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. + + +**rust-analyzer.inlayHints.renderColons** (default: true) + + Whether to render leading colons for type hints, and trailing colons for parameter hints. + + +**rust-analyzer.inlayHints.typeHints.enable** (default: true) + + Whether to show inlay type hints for variables. + + +**rust-analyzer.inlayHints.typeHints.hideClosureInitialization** (default: false) + + Whether to hide inlay type hints for `let` statements that initialize to a closure. +Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. + + +**rust-analyzer.inlayHints.typeHints.hideNamedConstructor** (default: false) + + Whether to hide inlay type hints for constructors. + + +**rust-analyzer.interpret.tests** (default: false) + + Enables the experimental support for interpreting tests. + + +**rust-analyzer.joinLines.joinAssignments** (default: true) + + Join lines merges consecutive declaration and initialization of an assignment. + + +**rust-analyzer.joinLines.joinElseIf** (default: true) + + Join lines inserts else between consecutive ifs. + + +**rust-analyzer.joinLines.removeTrailingComma** (default: true) + + Join lines removes trailing commas. + + +**rust-analyzer.joinLines.unwrapTrivialBlock** (default: true) + + Join lines unwraps trivial blocks. + + +**rust-analyzer.lens.debug.enable** (default: true) + + Whether to show `Debug` lens. Only applies when +`#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.enable** (default: true) + + Whether to show CodeLens in Rust files. + + +**rust-analyzer.lens.implementations.enable** (default: true) + + Whether to show `Implementations` lens. Only applies when +`#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.location** (default: "above_name") + + Where to render annotations. + + +**rust-analyzer.lens.references.adt.enable** (default: false) + + Whether to show `References` lens for Struct, Enum, and Union. +Only applies when `#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.references.enumVariant.enable** (default: false) + + Whether to show `References` lens for Enum Variants. +Only applies when `#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.references.method.enable** (default: false) + + Whether to show `Method References` lens. Only applies when +`#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.references.trait.enable** (default: false) + + Whether to show `References` lens for Trait. +Only applies when `#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.run.enable** (default: true) + + Whether to show `Run` lens. Only applies when +`#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.updateTest.enable** (default: true) + + Whether to show `Update Test` lens. Only applies when +`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set. + + +**rust-analyzer.linkedProjects** (default: []) + + Disable project auto-discovery in favor of explicitly specified set +of projects. + +Elements must be paths pointing to `Cargo.toml`, +`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON +objects in `rust-project.json` format. + + +**rust-analyzer.lru.capacity** (default: null) + + Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. + + +**rust-analyzer.lru.query.capacities** (default: {}) + + Sets the LRU capacity of the specified queries. + + +**rust-analyzer.notifications.cargoTomlNotFound** (default: true) + + Whether to show `can't find Cargo.toml` error message. + + +**rust-analyzer.numThreads** (default: null) + + How many worker threads in the main loop. The default `null` means to pick automatically. + + +**rust-analyzer.procMacro.attributes.enable** (default: true) + + Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. + + +**rust-analyzer.procMacro.enable** (default: true) + + Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. + + +**rust-analyzer.procMacro.ignored** (default: {}) + + These proc-macros will be ignored when trying to expand them. + +This config takes a map of crate names with the exported proc-macro names to ignore as values. + + +**rust-analyzer.procMacro.server** (default: null) + + Internal config, path to proc-macro server executable. + + +**rust-analyzer.references.excludeImports** (default: false) + + Exclude imports from find-all-references. + + +**rust-analyzer.references.excludeTests** (default: false) + + Exclude tests from find-all-references and call-hierarchy. + + +**rust-analyzer.runnables.command** (default: null) + + Command to be executed instead of 'cargo' for runnables. + + +**rust-analyzer.runnables.extraArgs** (default: []) + + Additional arguments to be passed to cargo for runnables such as +tests or binaries. For example, it may be `--release`. + + + **rust-analyzer.runnables.extraTestBinaryArgs** + +Default: + +```[ + "--show-output" +] + +``` + + Additional arguments to be passed through Cargo to launched tests, benchmarks, or +doc-tests. + +Unless the launched target uses a +[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), +they will end up being interpreted as options to +[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). + + + **rust-analyzer.rustc.source** (default: null) + + Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private +projects, or "discover" to try to automatically find it if the `rustc-dev` component +is installed. + +Any project which uses rust-analyzer with the rustcPrivate +crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. + +This option does not take effect until rust-analyzer is restarted. + + +**rust-analyzer.rustfmt.extraArgs** (default: []) + + Additional arguments to `rustfmt`. + + +**rust-analyzer.rustfmt.overrideCommand** (default: null) + + Advanced option, fully override the command rust-analyzer uses for +formatting. This should be the equivalent of `rustfmt` here, and +not that of `cargo fmt`. The file contents will be passed on the +standard input and the formatted result will be read from the +standard output. + + +**rust-analyzer.rustfmt.rangeFormatting.enable** (default: false) + + Enables the use of rustfmt's unstable range formatting command for the +`textDocument/rangeFormatting` request. The rustfmt option is unstable and only +available on a nightly build. + + +**rust-analyzer.semanticHighlighting.doc.comment.inject.enable** (default: true) + + Inject additional highlighting into doc comments. + +When enabled, rust-analyzer will highlight rust source in doc comments as well as intra +doc links. + + +**rust-analyzer.semanticHighlighting.nonStandardTokens** (default: true) + + Whether the server is allowed to emit non-standard tokens and modifiers. + + +**rust-analyzer.semanticHighlighting.operator.enable** (default: true) + + Use semantic tokens for operators. + +When disabled, rust-analyzer will emit semantic tokens only for operator tokens when +they are tagged with modifiers. + + +**rust-analyzer.semanticHighlighting.operator.specialization.enable** (default: false) + + Use specialized semantic tokens for operators. + +When enabled, rust-analyzer will emit special token types for operator tokens instead +of the generic `operator` token type. + + +**rust-analyzer.semanticHighlighting.punctuation.enable** (default: false) + + Use semantic tokens for punctuation. + +When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when +they are tagged with modifiers or have a special role. + + +**rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang** (default: false) + + When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro +calls. + + +**rust-analyzer.semanticHighlighting.punctuation.specialization.enable** (default: false) + + Use specialized semantic tokens for punctuation. + +When enabled, rust-analyzer will emit special token types for punctuation tokens instead +of the generic `punctuation` token type. + + +**rust-analyzer.semanticHighlighting.strings.enable** (default: true) + + Use semantic tokens for strings. + +In some editors (e.g. vscode) semantic tokens override other highlighting grammars. +By disabling semantic tokens for strings, other grammars can be used to highlight +their contents. + + +**rust-analyzer.signatureInfo.detail** (default: "full") + + Show full signature of the callable. Only shows parameters if disabled. + + +**rust-analyzer.signatureInfo.documentation.enable** (default: true) + + Show documentation. + + +**rust-analyzer.typing.triggerChars** (default: "=.") + + Specify the characters allowed to invoke special on typing triggers. +- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression +- typing `=` between two expressions adds `;` when in statement position +- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position +- typing `.` in a chain method call auto-indents +- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression +- typing `{` in a use item adds a closing `}` in the right place +- typing `>` to complete a return type `->` will insert a whitespace after it +- typing `<` in a path or type position inserts a closing `>` after the path or type. + + +**rust-analyzer.vfs.extraIncludes** (default: []) + + Additional paths to include in the VFS. Generally for code that is +generated or otherwise managed by a build system outside of Cargo, +though Cargo might be the eventual consumer. + + +**rust-analyzer.workspace.discoverConfig** (default: null) + + Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. + +[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. +`progress_label` is used for the title in progress indicators, whereas `files_to_watch` +is used to determine which build system-specific files should be watched in order to +reload rust-analyzer. + +Below is an example of a valid configuration: +```json +"rust-analyzer.workspace.discoverConfig": { + "command": [ + "rust-project", + "develop-json" + ], + "progressLabel": "rust-analyzer", + "filesToWatch": [ + "BUCK" + ] +} +``` + +## On `DiscoverWorkspaceConfig::command` + +**Warning**: This format is provisional and subject to change. + +[`DiscoverWorkspaceConfig::command`] *must* return a JSON object +corresponding to `DiscoverProjectData::Finished`: + +```norun +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(tag = "kind")] +#[serde(rename_all = "snake_case")] +enum DiscoverProjectData { + Finished { buildfile: Utf8PathBuf, project: ProjectJsonData }, + Error { error: String, source: Option }, + Progress { message: String }, +} +``` + +As JSON, `DiscoverProjectData::Finished` is: + +```json +{ + // the internally-tagged representation of the enum. + "kind": "finished", + // the file used by a non-Cargo build system to define + // a package or target. + "buildfile": "rust-analyzer/BUILD", + // the contents of a rust-project.json, elided for brevity + "project": { + "sysroot": "foo", + "crates": [] + } +} +``` + +It is encouraged, but not required, to use the other variants on +`DiscoverProjectData` to provide a more polished end-user experience. + +`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, +which will be substituted with the JSON-serialized form of the following +enum: + +```norun +#[derive(PartialEq, Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum DiscoverArgument { + Path(AbsPathBuf), + Buildfile(AbsPathBuf), +} +``` + +The JSON representation of `DiscoverArgument::Path` is: + +```json +{ + "path": "src/main.rs" +} +``` + +Similarly, the JSON representation of `DiscoverArgument::Buildfile` is: + +``` +{ + "buildfile": "BUILD" +} +``` + +`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, +and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to +to update an existing workspace. As a reference for implementors, +buck2's `rust-project` will likely be useful: +https://github.com/facebook/buck2/tree/main/integrations/rust-project. + + +**rust-analyzer.workspace.symbol.search.kind** (default: "only_types") + + Workspace symbol search kind. + + +**rust-analyzer.workspace.symbol.search.limit** (default: 128) + + Limits the number of items returned from a workspace symbol search (Defaults to 128). +Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search. +Other clients requires all results upfront and might require a higher limit. + + +**rust-analyzer.workspace.symbol.search.scope** (default: "workspace") + + Workspace symbol search scope. + + diff --git a/src/tools/rust-analyzer/docs/book/src/diagnostics.md b/src/tools/rust-analyzer/docs/book/src/diagnostics.md new file mode 100644 index 000000000000..60685c98da71 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/diagnostics.md @@ -0,0 +1,16 @@ +# Diagnostics + +While most errors and warnings provided by rust-analyzer come from the +`cargo check` integration, there’s a growing number of diagnostics +implemented using rust-analyzer’s own analysis. Some of these +diagnostics don’t respect `#[allow]` or `#[deny]` attributes yet, but +can be turned off using the `rust-analyzer.diagnostics.enable`, +`rust-analyzer.diagnostics.experimental.enable` or +`rust-analyzer.diagnostics.disabled` settings. + +## Clippy + +To run `cargo clippy` instead of `cargo check`, you can set +`"rust-analyzer.check.command": "clippy"`. + +{{#include diagnostics_generated.md:2:}} diff --git a/src/tools/rust-analyzer/docs/book/src/editor_features.md b/src/tools/rust-analyzer/docs/book/src/editor_features.md new file mode 100644 index 000000000000..73fe9f49a96e --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/editor_features.md @@ -0,0 +1,204 @@ +# Editor Features + + +## VS Code + +### Color configurations + +It is possible to change the foreground/background color and font +family/size of inlay hints. Just add this to your `settings.json`: + +```json +{ + "editor.inlayHints.fontFamily": "Courier New", + "editor.inlayHints.fontSize": 11, + + "workbench.colorCustomizations": { + // Name of the theme you are currently using + "[Default Dark+]": { + "editorInlayHint.foreground": "#868686f0", + "editorInlayHint.background": "#3d3d3d48", + + // Overrides for specific kinds of inlay hints + "editorInlayHint.typeForeground": "#fdb6fdf0", + "editorInlayHint.parameterForeground": "#fdb6fdf0", + } + } +} +``` + +### Semantic style customizations + +You can customize the look of different semantic elements in the source +code. For example, mutable bindings are underlined by default and you +can override this behavior by adding the following section to your +`settings.json`: + +```json +{ + "editor.semanticTokenColorCustomizations": { + "rules": { + "*.mutable": { + "fontStyle": "", // underline is the default + }, + } + }, +} +``` + +Most themes doesn’t support styling unsafe operations differently yet. +You can fix this by adding overrides for the rules `operator.unsafe`, +`function.unsafe`, and `method.unsafe`: + +```json +{ + "editor.semanticTokenColorCustomizations": { + "rules": { + "operator.unsafe": "#ff6600", + "function.unsafe": "#ff6600", + "method.unsafe": "#ff6600" + } + }, +} +``` + +In addition to the top-level rules you can specify overrides for +specific themes. For example, if you wanted to use a darker text color +on a specific light theme, you might write: + +```json +{ + "editor.semanticTokenColorCustomizations": { + "rules": { + "operator.unsafe": "#ff6600" + }, + "[Ayu Light]": { + "rules": { + "operator.unsafe": "#572300" + } + } + }, +} +``` + +Make sure you include the brackets around the theme name. For example, +use `"[Ayu Light]"` to customize the theme Ayu Light. + +### Special `when` clause context for keybindings. + +You may use `inRustProject` context to configure keybindings for rust +projects only. For example: + +```json +{ + "key": "ctrl+alt+d", + "command": "rust-analyzer.openDocs", + "when": "inRustProject" +} +``` + +More about `when` clause contexts +[here](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts). + +### Setting runnable environment variables + +You can use "rust-analyzer.runnables.extraEnv" setting to define +runnable environment-specific substitution variables. The simplest way +for all runnables in a bunch: + +```json +"rust-analyzer.runnables.extraEnv": { + "RUN_SLOW_TESTS": "1" +} +``` + +Or it is possible to specify vars more granularly: + +```json +"rust-analyzer.runnables.extraEnv": [ + { + // "mask": null, // null mask means that this rule will be applied for all runnables + env: { + "APP_ID": "1", + "APP_DATA": "asdf" + } + }, + { + "mask": "test_name", + "env": { + "APP_ID": "2", // overwrites only APP_ID + } + } +] +``` + +You can use any valid regular expression as a mask. Also note that a +full runnable name is something like **run bin\_or\_example\_name**, +**test some::mod::test\_name** or **test-mod some::mod**, so it is +possible to distinguish binaries, single tests, and test modules with +this masks: `"^run"`, `"^test "` (the trailing space matters!), and +`"^test-mod"` respectively. + +If needed, you can set different values for different platforms: + +```json +"rust-analyzer.runnables.extraEnv": [ + { + "platform": "win32", // windows only + env: { + "APP_DATA": "windows specific data" + } + }, + { + "platform": ["linux"], + "env": { + "APP_DATA": "linux data", + } + }, + { // for all platforms + "env": { + "APP_COMMON_DATA": "xxx", + } + } +] +``` + +### Compiler feedback from external commands + +Instead of relying on the built-in `cargo check`, you can configure Code +to run a command in the background and use the `$rustc-watch` problem +matcher to generate inline error markers from its output. + +To do this you need to create a new [VS Code +Task](https://code.visualstudio.com/docs/editor/tasks) and set +`"rust-analyzer.checkOnSave": false` in preferences. + +For example, if you want to run +[`cargo watch`](https://crates.io/crates/cargo-watch) instead, you might +add the following to `.vscode/tasks.json`: + +```json +{ + "label": "Watch", + "group": "build", + "type": "shell", + "command": "cargo watch", + "problemMatcher": "$rustc-watch", + "isBackground": true +} +``` + +### Live Share + +VS Code Live Share has partial support for rust-analyzer. + +Live Share *requires* the official Microsoft build of VS Code, OSS +builds will not work correctly. + +The host’s rust-analyzer instance will be shared with all guests joining +the session. The guests do not have to have the rust-analyzer extension +installed for this to work. + +If you are joining a Live Share session and *do* have rust-analyzer +installed locally, commands from the command palette will not work +correctly since they will attempt to communicate with the local server. diff --git a/src/tools/rust-analyzer/docs/book/src/features.md b/src/tools/rust-analyzer/docs/book/src/features.md new file mode 100644 index 000000000000..0829a0213b76 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/features.md @@ -0,0 +1,3 @@ +# Features + +{{#include features_generated.md:2:}} diff --git a/src/tools/rust-analyzer/docs/book/src/installation.md b/src/tools/rust-analyzer/docs/book/src/installation.md new file mode 100644 index 000000000000..5b697e9bc33d --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/installation.md @@ -0,0 +1,644 @@ +# Installation + +In theory, one should be able to just install the [`rust-analyzer` +binary](#rust-analyzer-language-server-binary) and have it automatically +work with any editor. We are not there yet, so some editor specific +setup is required. + +Additionally, rust-analyzer needs the sources of the standard library. +If the source code is not present, rust-analyzer will attempt to install +it automatically. + +To add the sources manually, run the following command: + + $ rustup component add rust-src + +## Toolchain + +Only the latest stable standard library source is officially supported +for use with rust-analyzer. If you are using an older toolchain or have +an override set, rust-analyzer may fail to understand the Rust source. +You will either need to update your toolchain or use an older version of +rust-analyzer that is compatible with your toolchain. + +If you are using an override in your project, you can still force +rust-analyzer to use the stable toolchain via the environment variable +`RUSTUP_TOOLCHAIN`. For example, with VS Code or coc-rust-analyzer: + + { "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } } + +## VS Code + +This is the best supported editor at the moment. The rust-analyzer +plugin for VS Code is maintained [in +tree](https://github.com/rust-lang/rust-analyzer/tree/master/editors/code). + +You can install the latest release of the plugin from [the +marketplace](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer). + +Note that the plugin may cause conflicts with the [previous official +Rust +plugin](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust). +The latter is no longer maintained and should be uninstalled. + +The server binary is stored in the extension install directory, which +starts with `rust-lang.rust-analyzer-` and is located under: + +- Linux: `~/.vscode/extensions` + +- Linux (Remote, such as WSL): `~/.vscode-server/extensions` + +- macOS: `~/.vscode/extensions` + +- Windows: `%USERPROFILE%\.vscode\extensions` + +As an exception, on NixOS, the extension makes a copy of the server and +stores it under +`~/.config/Code/User/globalStorage/rust-lang.rust-analyzer`. + +Note that we only support the two most recent versions of VS Code. + +### Updates + +The extension will be updated automatically as new versions become +available. It will ask your permission to download the matching language +server version binary if needed. + +#### Nightly + +We ship nightly releases for VS Code. To help us out by testing the +newest code, you can enable pre-release versions in the Code extension +page. + +### Manual installation + +Alternatively, download a VSIX corresponding to your platform from the +[releases](https://github.com/rust-lang/rust-analyzer/releases) page. + +Install the extension with the `Extensions: Install from VSIX` command +within VS Code, or from the command line via: + + $ code --install-extension /path/to/rust-analyzer.vsix + +If you are running an unsupported platform, you can install +`rust-analyzer-no-server.vsix` and compile or obtain a server binary. +Copy the server anywhere, then add the path to your settings.json, for +example: + + { "rust-analyzer.server.path": "~/.local/bin/rust-analyzer-linux" } + +### Building From Source + +Both the server and the Code plugin can be installed from source: + + $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer + $ cargo xtask install + +You’ll need Cargo, nodejs (matching a supported version of VS Code) and +npm for this. + +Note that installing via `xtask install` does not work for VS Code +Remote, instead you’ll need to install the `.vsix` manually. + +If you’re not using Code, you can compile and install only the LSP +server: + + $ cargo xtask install --server + +Make sure that `.cargo/bin` is in `$PATH` and precedes paths where +`rust-analyzer` may also be installed. Specifically, `rustup` includes a +proxy called `rust-analyzer`, which can cause problems if you’re +planning to use a source build or even a downloaded binary. + +## rust-analyzer Language Server Binary + +Other editors generally require the `rust-analyzer` binary to be in +`$PATH`. You can download pre-built binaries from the +[releases](https://github.com/rust-lang/rust-analyzer/releases) page. +You will need to uncompress and rename the binary for your platform, +e.g. from `rust-analyzer-aarch64-apple-darwin.gz` on Mac OS to +`rust-analyzer`, make it executable, then move it into a directory in +your `$PATH`. + +On Linux to install the `rust-analyzer` binary into `~/.local/bin`, +these commands should work: + + $ mkdir -p ~/.local/bin + $ curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer + $ chmod +x ~/.local/bin/rust-analyzer + +Make sure that `~/.local/bin` is listed in the `$PATH` variable and use +the appropriate URL if you’re not on a `x86-64` system. + +You don’t have to use `~/.local/bin`, any other path like `~/.cargo/bin` +or `/usr/local/bin` will work just as well. + +Alternatively, you can install it from source using the command below. +You’ll need the latest stable version of the Rust toolchain. + + $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer + $ cargo xtask install --server + +If your editor can’t find the binary even though the binary is on your +`$PATH`, the likely explanation is that it doesn’t see the same `$PATH` +as the shell, see [this +issue](https://github.com/rust-lang/rust-analyzer/issues/1811). On Unix, +running the editor from a shell or changing the `.desktop` file to set +the environment should help. + +### rustup + +`rust-analyzer` is available in `rustup`: + + $ rustup component add rust-analyzer + +### Arch Linux + +The `rust-analyzer` binary can be installed from the repos or AUR (Arch +User Repository): + +- [`rust-analyzer`](https://www.archlinux.org/packages/extra/x86_64/rust-analyzer/) + (built from latest tagged source) + +- [`rust-analyzer-git`](https://aur.archlinux.org/packages/rust-analyzer-git) + (latest Git version) + +Install it with pacman, for example: + + $ pacman -S rust-analyzer + +### Gentoo Linux + +`rust-analyzer` is installed when the `rust-analyzer` use flag is set for dev-lang/rust or dev-lang/rust-bin. You also need to set the `rust-src` use flag. + +### macOS + +The `rust-analyzer` binary can be installed via +[Homebrew](https://brew.sh/). + + $ brew install rust-analyzer + +### Windows + +It is recommended to install the latest Microsoft Visual C++ Redistributable prior to installation. +Download links can be found +[here](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist). + +## VS Code or VSCodium in Flatpak + +Setting up `rust-analyzer` with a Flatpak version of Code is not trivial +because of the Flatpak sandbox. While the sandbox can be disabled for +some directories, `/usr/bin` will always be mounted under +`/run/host/usr/bin`. This prevents access to the system’s C compiler, a +system-wide installation of Rust, or any other libraries you might want +to link to. Some compilers and libraries can be acquired as Flatpak +SDKs, such as `org.freedesktop.Sdk.Extension.rust-stable` or +`org.freedesktop.Sdk.Extension.llvm15`. + +If you use a Flatpak SDK for Rust, it must be in your `PATH`: + + * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm15,rust-stable}//23.08` + * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm15,rust-stable` (this can be done using flatseal or `flatpak override`) + +If you want to use Flatpak in combination with `rustup`, the following +steps might help: + +- both Rust and `rustup` have to be installed using + . Distro packages *will not* work. + +- you need to launch Code, open a terminal and run `echo $PATH` + +- using + [Flatseal](https://flathub.org/apps/details/com.github.tchx84.Flatseal), + you must add an environment variable called `PATH`. Set its value to + the output from above, appending `:~/.cargo/bin`, where `~` is the + path to your home directory. You must replace `~`, as it won’t be + expanded otherwise. + +- while Flatseal is open, you must enable access to "All user files" + +A C compiler should already be available via `org.freedesktop.Sdk`. Any +other tools or libraries you will need to acquire from Flatpak. + +## Emacs + +Prerequisites: You have installed the [`rust-analyzer` +binary](#rust-analyzer-language-server-binary). + +To use `rust-analyzer`, you need to install and enable one of the two +popular LSP client implementations for Emacs, +[Eglot](https://github.com/joaotavora/eglot) or [LSP +Mode](https://github.com/emacs-lsp/lsp-mode). Both enable +`rust-analyzer` by default in rust buffers if it is available. + +### Eglot + +Eglot is the more minimalistic and lightweight LSP client for Emacs, +integrates well with existing Emacs functionality and is built into +Emacs starting from release 29. + +After installing Eglot, e.g. via `M-x package-install` (not needed from +Emacs 29), you can enable it via the `M-x eglot` command or load it +automatically in `rust-mode` via + + (add-hook 'rust-mode-hook 'eglot-ensure) + +To enable clippy, you will need to configure the initialization options +to pass the `check.command` setting. + + (add-to-list 'eglot-server-programs + '((rust-ts-mode rust-mode) . + ("rust-analyzer" :initializationOptions (:check (:command "clippy"))))) + +For more detailed instructions and options see the [Eglot +manual](https://joaotavora.github.io/eglot) (also available from Emacs +via `M-x info`) and the [Eglot +readme](https://github.com/joaotavora/eglot/blob/master/README.md). + +Eglot does not support the rust-analyzer extensions to the +language-server protocol and does not aim to do so in the future. The +[eglot-x](https://github.com/nemethf/eglot-x#rust-analyzer-extensions) +package adds experimental support for those LSP extensions. + +### LSP Mode + +LSP-mode is the original LSP-client for emacs. Compared to Eglot it has +a larger codebase and supports more features, like LSP protocol +extensions. With extension packages like [LSP +UI](https://github.com/emacs-lsp/lsp-mode) it offers a lot of visual +eyecandy. Further it integrates well with [DAP +mode](https://github.com/emacs-lsp/dap-mode) for support of the Debug +Adapter Protocol. + +You can install LSP-mode via `M-x package-install` and then run it via +the `M-x lsp` command or load it automatically in rust buffers with + + (add-hook 'rust-mode-hook 'lsp-deferred) + +For more information on how to set up LSP mode and its extension package +see the instructions in the [LSP mode +manual](https://emacs-lsp.github.io/lsp-mode/page/installation). Also +see the [rust-analyzer +section](https://emacs-lsp.github.io/lsp-mode/page/lsp-rust-analyzer/) +for `rust-analyzer` specific options and commands, which you can +optionally bind to keys. + +Note the excellent +[guide](https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/) from +[@rksm](https://github.com/rksm) on how to set-up Emacs for Rust +development with LSP mode and several other packages. + +## Vim/Neovim + +Prerequisites: You have installed the [`rust-analyzer` +binary](#rust-analyzer-language-server-binary). Not needed if the +extension can install/update it on its own, coc-rust-analyzer is one +example. + +There are several LSP client implementations for Vim or Neovim: + +### coc-rust-analyzer + +1. Install coc.nvim by following the instructions at + [coc.nvim](https://github.com/neoclide/coc.nvim) (Node.js required) + +2. Run `:CocInstall coc-rust-analyzer` to install + [coc-rust-analyzer](https://github.com/fannheyward/coc-rust-analyzer), + this extension implements *most* of the features supported in the + VSCode extension: + + - automatically install and upgrade stable/nightly releases + + - same configurations as VSCode extension, + `rust-analyzer.server.path`, `rust-analyzer.cargo.features` etc. + + - same commands too, `rust-analyzer.analyzerStatus`, + `rust-analyzer.ssr` etc. + + - inlay hints for variables and method chaining, *Neovim Only* + +Note: for code actions, use `coc-codeaction-cursor` and +`coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line` +are unlikely to be useful. + +### LanguageClient-neovim + +1. Install LanguageClient-neovim by following the instructions + [here](https://github.com/autozimu/LanguageClient-neovim) + + - The GitHub project wiki has extra tips on configuration + +2. Configure by adding this to your Vim/Neovim config file (replacing + the existing Rust-specific line if it exists): + + let g:LanguageClient_serverCommands = { + \ 'rust': ['rust-analyzer'], + \ } + +### YouCompleteMe + +Install YouCompleteMe by following the instructions +[here](https://github.com/ycm-core/YouCompleteMe#installation). + +rust-analyzer is the default in ycm, it should work out of the box. + +### ALE + +To use the LSP server in [ale](https://github.com/dense-analysis/ale): + + let g:ale_linters = {'rust': ['analyzer']} + +### nvim-lsp + +Neovim 0.5 has built-in language server support. For a quick start +configuration of rust-analyzer, use +[neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig#rust_analyzer). +Once `neovim/nvim-lspconfig` is installed, use +`lua require'lspconfig'.rust_analyzer.setup({})` in your `init.vim`. + +You can also pass LSP settings to the server: + + lua << EOF + local lspconfig = require'lspconfig' + + local on_attach = function(client) + require'completion'.on_attach(client) + end + + lspconfig.rust_analyzer.setup({ + on_attach = on_attach, + settings = { + ["rust-analyzer"] = { + imports = { + granularity = { + group = "module", + }, + prefix = "self", + }, + cargo = { + buildScripts = { + enable = true, + }, + }, + procMacro = { + enable = true + }, + } + } + }) + EOF + +If you're running Neovim 0.10 or later, you can enable inlay hints via `on_attach`: + +```vim +lspconfig.rust_analyzer.setup({ + on_attach = function(client, bufnr) + vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) + end +}) +``` + +Note that the hints are only visible after `rust-analyzer` has finished loading **and** you have to +edit the file to trigger a re-render. + +See for more tips on +getting started. + +Check out for a batteries +included rust-analyzer setup for Neovim. + +### vim-lsp + +vim-lsp is installed by following [the plugin +instructions](https://github.com/prabirshrestha/vim-lsp). It can be as +simple as adding this line to your `.vimrc`: + + Plug 'prabirshrestha/vim-lsp' + +Next you need to register the `rust-analyzer` binary. If it is avim.lspvailable +in `$PATH`, you may want to add this to your `.vimrc`: + + if executable('rust-analyzer') + au User lsp_setup call lsp#register_server({ + \ 'name': 'Rust Language Server', + \ 'cmd': {server_info->['rust-analyzer']}, + \ 'whitelist': ['rust'], + \ }) + endif + +There is no dedicated UI for the server configuration, so you would need +to send any options as a value of the `initialization_options` field, as +described in the [Configuration](#configuration) section. Here is an +example of how to enable the proc-macro support: + + if executable('rust-analyzer') + au User lsp_setup call lsp#register_server({ + \ 'name': 'Rust Language Server', + \ 'cmd': {server_info->['rust-analyzer']}, + \ 'whitelist': ['rust'], + \ 'initialization_options': { + \ 'cargo': { + \ 'buildScripts': { + \ 'enable': v:true, + \ }, + \ }, + \ 'procMacro': { + \ 'enable': v:true, + \ }, + \ }, + \ }) + endif + +## Sublime Text + +### Sublime Text 4: + +- Follow the instructions in + [LSP-rust-analyzer](https://github.com/sublimelsp/LSP-rust-analyzer). + +Install +[LSP-file-watcher-chokidar](https://packagecontrol.io/packages/LSP-file-watcher-chokidar) +to enable file watching (`workspace/didChangeWatchedFiles`). + +### Sublime Text 3: + +- Install the [`rust-analyzer` + binary](#rust-analyzer-language-server-binary). + +- Install the [LSP package](https://packagecontrol.io/packages/LSP). + +- From the command palette, run `LSP: Enable Language Server Globally` + and select `rust-analyzer`. + +If it worked, you should see "rust-analyzer, Line X, Column Y" on the +left side of the status bar, and after waiting a bit, functionalities +like tooltips on hovering over variables should become available. + +If you get an error saying `No such file or directory: 'rust-analyzer'`, +see the [`rust-analyzer` binary](#rust-analyzer-language-server-binary) +section on installing the language server binary. + +## GNOME Builder + +GNOME Builder 3.37.1 and newer has native `rust-analyzer` support. If +the LSP binary is not available, GNOME Builder can install it when +opening a Rust file. + +## Eclipse IDE + +Support for Rust development in the Eclipse IDE is provided by [Eclipse +Corrosion](https://github.com/eclipse/corrosion). If available in PATH +or in some standard location, `rust-analyzer` is detected and powers +editing of Rust files without further configuration. If `rust-analyzer` +is not detected, Corrosion will prompt you for configuration of your +Rust toolchain and language server with a link to the *Window > +Preferences > Rust* preference page; from here a button allows to +download and configure `rust-analyzer`, but you can also reference +another installation. You’ll need to close and reopen all .rs and Cargo +files, or to restart the IDE, for this change to take effect. + +## Kate Text Editor + +Support for the language server protocol is built into Kate through the +LSP plugin, which is included by default. It is preconfigured to use +rust-analyzer for Rust sources since Kate 21.12. + +To change rust-analyzer config options, start from the following example +and put it into Kate’s "User Server Settings" tab (located under the LSP +Client settings): + + { + "servers": { + "rust": { + "initializationOptions": { + "cachePriming": { + "enable": false + }, + "check": { + "allTargets": false + }, + "checkOnSave": false + } + } + } + } + +Then click on apply, and restart the LSP server for your rust project. + +## juCi++ + +[juCi++](https://gitlab.com/cppit/jucipp) has built-in support for the +language server protocol, and since version 1.7.0 offers installation of +both Rust and rust-analyzer when opening a Rust file. + +## Kakoune + +[Kakoune](https://kakoune.org/) supports LSP with the help of +[`kak-lsp`](https://github.com/kak-lsp/kak-lsp). Follow the +[instructions](https://github.com/kak-lsp/kak-lsp#installation) to +install `kak-lsp`. To configure `kak-lsp`, refer to the [configuration +section](https://github.com/kak-lsp/kak-lsp#configuring-kak-lsp) which +is basically about copying the [configuration +file](https://github.com/kak-lsp/kak-lsp/blob/master/kak-lsp.toml) in +the right place (latest versions should use `rust-analyzer` by default). + +Finally, you need to configure Kakoune to talk to `kak-lsp` (see [Usage +section](https://github.com/kak-lsp/kak-lsp#usage)). A basic +configuration will only get you LSP but you can also activate inlay +diagnostics and auto-formatting on save. The following might help you +get all of this. + + eval %sh{kak-lsp --kakoune -s $kak_session} # Not needed if you load it with plug.kak. + hook global WinSetOption filetype=rust %{ + # Enable LSP + lsp-enable-window + + # Auto-formatting on save + hook window BufWritePre .* lsp-formatting-sync + + # Configure inlay hints (only on save) + hook window -group rust-inlay-hints BufWritePost .* rust-analyzer-inlay-hints + hook -once -always window WinSetOption filetype=.* %{ + remove-hooks window rust-inlay-hints + } + } + +## Helix + +[Helix](https://docs.helix-editor.com/) supports LSP by default. +However, it won’t install `rust-analyzer` automatically. You can follow +instructions for installing [`rust-analyzer` +binary](#rust-analyzer-language-server-binary). + +## Visual Studio 2022 + +There are multiple rust-analyzer extensions for Visual Studio 2022 on +Windows: + +### rust-analyzer.vs + +(License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 +International) + +[Visual Studio +Marketplace](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer) + +[GitHub](https://github.com/kitamstudios/rust-analyzer/) + +Support for Rust development in the Visual Studio IDE is enabled by the +[rust-analyzer](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer) +package. Either click on the download link or install from IDE’s +extension manager. For now [Visual Studio +2022](https://visualstudio.microsoft.com/downloads/) is required. All +editions are supported viz. Community, Professional & Enterprise. The +package aims to provide 0-friction installation and therefore comes +loaded with most things required including rust-analyzer binary. If +anything it needs is missing, appropriate errors / warnings will guide +the user. E.g. cargo.exe needs to be in path and the package will tell +you as much. This package is under rapid active development. So if you +encounter any issues please file it at +[rust-analyzer.vs](https://github.com/kitamstudios/rust-analyzer/). + +### VS\_RustAnalyzer + +(License: GPL) + +[Visual Studio +Marketplace](https://marketplace.visualstudio.com/items?itemName=cchharris.vsrustanalyzer) + +[GitHub](https://github.com/cchharris/VS-RustAnalyzer) + +### SourceGear Rust + +(License: closed source) + +[Visual Studio +Marketplace](https://marketplace.visualstudio.com/items?itemName=SourceGear.SourceGearRust) + +[GitHub (docs, issues, +discussions)](https://github.com/sourcegear/rust-vs-extension) + +- Free (no-cost) + +- Supports all editions of Visual Studio 2022 on Windows: Community, + Professional, or Enterprise + +## Lapce + +[Lapce](https://lapce.dev/) has a Rust plugin which you can install +directly. Unfortunately, it downloads an old version of `rust-analyzer`, +but you can set the server path under Settings. + +## Crates + +There is a package named `ra_ap_rust_analyzer` available on +[crates.io](https://crates.io/crates/ra_ap_rust-analyzer), for someone +who wants to use it programmatically. + +For more details, see [the publish +workflow](https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/autopublish.yaml). + +## Zed + +[Zed](https://zed.dev) has native `rust-analyzer` support. If the LSP +binary is not available, Zed can install it when opening a Rust file. diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md new file mode 100644 index 000000000000..151f8758a176 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md @@ -0,0 +1,246 @@ +# Non-Cargo Based Projects + +rust-analyzer does not require Cargo. However, if you use some other +build system, you’ll have to describe the structure of your project for +rust-analyzer in the `rust-project.json` format: + +```typescript +interface JsonProject { + /// Path to the sysroot directory. + /// + /// The sysroot is where rustc looks for the + /// crates that are built-in to rust, such as + /// std. + /// + /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root + /// + /// To see the current value of sysroot, you + /// can query rustc: + /// + /// ``` + /// $ rustc --print sysroot + /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin + /// ``` + sysroot?: string; + /// Path to the directory with *source code* of + /// sysroot crates. + /// + /// By default, this is `lib/rustlib/src/rust/library` + /// relative to the sysroot. + /// + /// It should point to the directory where std, + /// core, and friends can be found: + /// + /// https://github.com/rust-lang/rust/tree/master/library. + /// + /// If provided, rust-analyzer automatically adds + /// dependencies on sysroot crates. Conversely, + /// if you omit this path, you can specify sysroot + /// dependencies yourself and, for example, have + /// several different "sysroots" in one graph of + /// crates. + sysroot_src?: string; + /// List of groups of common cfg values, to allow + /// sharing them between crates. + /// + /// Maps from group name to its cfgs. Cfg follow + /// the same format as `Crate.cfg`. + cfg_groups?: { [key: string]: string[]; }; + /// The set of crates comprising the current + /// project. Must include all transitive + /// dependencies as well as sysroot crate (libstd, + /// libcore and such). + crates: Crate[]; + /// Configuration for CLI commands. + /// + /// These are used for running and debugging binaries + /// and tests without encoding build system-specific + /// knowledge into rust-analyzer. + /// + /// # Example + /// + /// Below is an example of a test runnable. `{label}` and `{test_id}` + /// are explained in `Runnable::args`'s documentation below. + /// + /// ```json + /// { + /// "program": "buck", + /// "args": [ + /// "test", + /// "{label}", + /// "--", + /// "{test_id}", + /// "--print-passing-details" + /// ], + /// "cwd": "/home/user/repo-root/", + /// "kind": "testOne" + /// } + /// ``` + runnables?: Runnable[]; +} + +interface Crate { + /// Optional crate name used for display purposes, + /// without affecting semantics. See the `deps` + /// key for semantically-significant crate names. + display_name?: string; + /// Path to the root module of the crate. + root_module: string; + /// Edition of the crate. + edition: '2015' | '2018' | '2021' | '2024'; + /// The version of the crate. Used for calculating + /// the correct docs.rs URL. + version?: string; + /// Dependencies + deps: Dep[]; + /// Should this crate be treated as a member of + /// current "workspace". + /// + /// By default, inferred from the `root_module` + /// (members are the crates which reside inside + /// the directory opened in the editor). + /// + /// Set this to `false` for things like standard + /// library and 3rd party crates to enable + /// performance optimizations (rust-analyzer + /// assumes that non-member crates don't change). + is_workspace_member?: boolean; + /// Optionally specify the (super)set of `.rs` + /// files comprising this crate. + /// + /// By default, rust-analyzer assumes that only + /// files under `root_module.parent` can belong + /// to a crate. `include_dirs` are included + /// recursively, unless a subdirectory is in + /// `exclude_dirs`. + /// + /// Different crates can share the same `source`. + /// + /// If two crates share an `.rs` file in common, + /// they *must* have the same `source`. + /// rust-analyzer assumes that files from one + /// source can't refer to files in another source. + source?: { + include_dirs: string[]; + exclude_dirs: string[]; + }; + /// List of cfg groups this crate inherits. + /// + /// All cfg in these groups will be concatenated to + /// `cfg`. It is impossible to replace a value from + /// the groups. + cfg_groups?: string[]; + /// The set of cfgs activated for a given crate, like + /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`. + cfg: string[]; + /// Target tuple for this Crate. + /// + /// Used when running `rustc --print cfg` + /// to get target-specific cfgs. + target?: string; + /// Environment variables, used for + /// the `env!` macro + env: { [key: string]: string; }; + + /// Whether the crate is a proc-macro crate. + is_proc_macro: boolean; + /// For proc-macro crates, path to compiled + /// proc-macro (.so file). + proc_macro_dylib_path?: string; + + /// Repository, matching the URL that would be used + /// in Cargo.toml. + repository?: string; + + /// Build-specific data about this crate. + build?: BuildInfo; +} + +interface Dep { + /// Index of a crate in the `crates` array. + crate: number; + /// Name as should appear in the (implicit) + /// `extern crate name` declaration. + name: string; +} + +interface BuildInfo { + /// The name associated with this crate. + /// + /// This is determined by the build system that produced + /// the `rust-project.json` in question. For instance, if buck were used, + /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`. + /// + /// Do not attempt to parse the contents of this string; it is a build system-specific + /// identifier similar to `Crate::display_name`. + label: string; + /// Path corresponding to the build system-specific file defining the crate. + build_file: string; + /// The kind of target. + /// + /// This information is used to determine what sort + /// of runnable codelens to provide, if any. + target_kind: 'bin' | 'lib' | 'test'; +} + +interface Runnable { + /// The program invoked by the runnable. + /// + /// For example, this might be `cargo`, `buck`, or `bazel`. + program: string; + /// The arguments passed to `program`. + args: string[]; + /// The current working directory of the runnable. + cwd: string; + /// Used to decide what code lens to offer. + /// + /// `testOne`: This runnable will be used when the user clicks the 'Run Test' + /// CodeLens above a test. + /// + /// The args for testOne can contain two template strings: + /// `{label}` and `{test_id}`. `{label}` will be replaced + /// with the `Build::label` and `{test_id}` will be replaced + /// with the test name. + kind: 'testOne' | string; +} +``` + +This format is provisional and subject to change. Specifically, the +`roots` setup will be different eventually. + +There are three ways to feed `rust-project.json` to rust-analyzer: + +- Place `rust-project.json` file at the root of the project, and + rust-analyzer will discover it. + +- Specify + `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in + the settings (and make sure that your LSP client sends settings as a + part of initialize request). + +- Specify + `"rust-analyzer.linkedProjects": [ { "roots": […​], "crates": […​] }]` + inline. + +Relative paths are interpreted relative to `rust-project.json` file +location or (for inline JSON) relative to `rootUri`. + +You can set the `RA_LOG` environment variable to `rust_analyzer=info` to +inspect how rust-analyzer handles config and project loading. + +Note that calls to `cargo check` are disabled when using +`rust-project.json` by default, so compilation errors and warnings will +no longer be sent to your LSP client. To enable these compilation errors +you will need to specify explicitly what command rust-analyzer should +run to perform the checks using the +`rust-analyzer.check.overrideCommand` configuration. As an example, the +following configuration explicitly sets `cargo check` as the `check` +command. + + { "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] } + +`check.overrideCommand` requires the command specified to output json +error messages for rust-analyzer to consume. The `--message-format=json` +flag does this for `cargo check` so whichever command you use must also +output errors in this format. See the [Configuration](#_configuration) +section for more information. diff --git a/src/tools/rust-analyzer/docs/book/src/privacy.md b/src/tools/rust-analyzer/docs/book/src/privacy.md new file mode 100644 index 000000000000..602c68d6f67d --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/privacy.md @@ -0,0 +1,15 @@ +# Privacy + +The LSP server performs no network access in itself, but runs +`cargo metadata` which will update or download the crate registry and +the source code of the project dependencies. If enabled (the default), +build scripts and procedural macros can do anything. + +The Code extension does not access the network. + +Any other editor plugins are not under the control of the +`rust-analyzer` developers. For any privacy concerns, you should check +with their respective developers. + +For `rust-analyzer` developers, `cargo xtask release` uses the GitHub +API to put together the release notes. diff --git a/src/tools/rust-analyzer/docs/book/src/security.md b/src/tools/rust-analyzer/docs/book/src/security.md new file mode 100644 index 000000000000..1444af03248d --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/security.md @@ -0,0 +1,19 @@ +# Security + +At the moment, rust-analyzer assumes that all code is trusted. Here is a +**non-exhaustive** list of ways to make rust-analyzer execute arbitrary +code: + +- proc macros and build scripts are executed by default + +- `.cargo/config` can override `rustc` with an arbitrary executable + +- `rust-toolchain.toml` can override `rustc` with an arbitrary + executable + +- VS Code plugin reads configuration from project directory, and that + can be used to override paths to various executables, like `rustfmt` + or `rust-analyzer` itself. + +- rust-analyzer’s syntax trees library uses a lot of `unsafe` and + hasn’t been properly audited for memory safety. \ No newline at end of file diff --git a/src/tools/rust-analyzer/docs/book/src/troubleshooting.md b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md new file mode 100644 index 000000000000..4092b9de990c --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md @@ -0,0 +1,50 @@ +# Troubleshooting + +Start with looking at the rust-analyzer version. Try **rust-analyzer: +Show RA Version** in VS Code (using **Command Palette** feature +typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the +command line. If the date is more than a week ago, it’s better to update +rust-analyzer version. + +The next thing to check would be panic messages in rust-analyzer’s log. +Log messages are printed to stderr, in VS Code you can see them in the +`Output > Rust Analyzer Language Server` tab of the panel. To see more +logs, set the `RA_LOG=info` environment variable, this can be done +either by setting the environment variable manually or by using +`rust-analyzer.server.extraEnv`, note that both of these approaches +require the server to be restarted. + +To fully capture LSP messages between the editor and the server, run +the `rust-analyzer: Toggle LSP Logs` command and check `Output > Rust +Analyzer Language Server Trace`. + +The root cause for many "nothing works" problems is that rust-analyzer +fails to understand the project structure. To debug that, first note the +`rust-analyzer` section in the status bar. If it has an error icon and +red, that’s the problem (hover will have somewhat helpful error +message). **rust-analyzer: Status** prints dependency information for +the current file. Finally, `RA_LOG=project_model=debug` enables verbose +logs during project loading. + +If rust-analyzer outright crashes, try running +`rust-analyzer analysis-stats /path/to/project/directory/` on the +command line. This command type checks the whole project in batch mode +bypassing LSP machinery. + +When filing issues, it is useful (but not necessary) to try to minimize +examples. An ideal bug reproduction looks like this: + +```shell +$ git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash +$ rust-analyzer --version +rust-analyzer dd12184e4 2021-05-08 dev +$ rust-analyzer analysis-stats . +💀 💀 💀 +``` + +It is especially useful when the `repo` doesn’t use external crates or +the standard library. + +If you want to go as far as to modify the source code to debug the +problem, be sure to take a look at the [dev +docs](https://github.com/rust-lang/rust-analyzer/tree/master/docs/dev)! diff --git a/src/tools/rust-analyzer/docs/dev/style.md b/src/tools/rust-analyzer/docs/dev/style.md index 4c5299bde3e9..51b60ab2ebb2 100644 --- a/src/tools/rust-analyzer/docs/dev/style.md +++ b/src/tools/rust-analyzer/docs/dev/style.md @@ -873,7 +873,7 @@ Use `anyhow::format_err!` rather than `anyhow::anyhow`. **Rationale:** consistent, boring, avoids stuttering. There's no specific guidance on the formatting of error messages, see [anyhow/#209](https://github.com/dtolnay/anyhow/issues/209). -Do not end error and context messages with `.` though. +Do not end error and context messages with `.` though. ## Early Returns @@ -1172,7 +1172,7 @@ MergeBehavior::Last => { **Rationale:** writing a sentence (or maybe even a paragraph) rather just "a comment" creates a more appropriate frame of mind. It tricks you into writing down more of the context you keep in your head while coding. -For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. +For `.md` files prefer a sentence-per-line format, don't wrap lines. If the line is too long, you want to split the sentence in two :-) **Rationale:** much easier to edit the text and read the diff, see [this link](https://asciidoctor.org/docs/asciidoc-recommended-practices/#one-sentence-per-line). diff --git a/src/tools/rust-analyzer/docs/user/.gitignore b/src/tools/rust-analyzer/docs/user/.gitignore deleted file mode 100644 index c32b1bcec2ee..000000000000 --- a/src/tools/rust-analyzer/docs/user/.gitignore +++ /dev/null @@ -1 +0,0 @@ -manual.html diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc deleted file mode 100644 index b33de1956b8d..000000000000 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ /dev/null @@ -1,1197 +0,0 @@ -[[rust-analyzer.assist.emitMustUse]]rust-analyzer.assist.emitMustUse (default: `false`):: -+ --- -Whether to insert #[must_use] when generating `as_` methods -for enum variants. --- -[[rust-analyzer.assist.expressionFillDefault]]rust-analyzer.assist.expressionFillDefault (default: `"todo"`):: -+ --- -Placeholder expression to use for missing expressions in assists. --- -[[rust-analyzer.assist.termSearch.borrowcheck]]rust-analyzer.assist.termSearch.borrowcheck (default: `true`):: -+ --- -Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check. --- -[[rust-analyzer.assist.termSearch.fuel]]rust-analyzer.assist.termSearch.fuel (default: `1800`):: -+ --- -Term search fuel in "units of work" for assists (Defaults to 1800). --- -[[rust-analyzer.cachePriming.enable]]rust-analyzer.cachePriming.enable (default: `true`):: -+ --- -Warm up caches on project load. --- -[[rust-analyzer.cachePriming.numThreads]]rust-analyzer.cachePriming.numThreads (default: `"physical"`):: -+ --- -How many worker threads to handle priming caches. The default `0` means to pick automatically. --- -[[rust-analyzer.cargo.allTargets]]rust-analyzer.cargo.allTargets (default: `true`):: -+ --- -Pass `--all-targets` to cargo invocation. --- -[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`):: -+ --- -Automatically refresh project info via `cargo metadata` on -`Cargo.toml` or `.cargo/config.toml` changes. --- -[[rust-analyzer.cargo.buildScripts.enable]]rust-analyzer.cargo.buildScripts.enable (default: `true`):: -+ --- -Run build scripts (`build.rs`) for more precise code analysis. --- -[[rust-analyzer.cargo.buildScripts.invocationStrategy]]rust-analyzer.cargo.buildScripts.invocationStrategy (default: `"per_workspace"`):: -+ --- -Specifies the invocation strategy to use when running the build scripts command. -If `per_workspace` is set, the command will be executed for each Rust workspace with the -workspace as the working directory. -If `once` is set, the command will be executed once with the opened project as the -working directory. -This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` -is set. --- -[[rust-analyzer.cargo.buildScripts.overrideCommand]]rust-analyzer.cargo.buildScripts.overrideCommand (default: `null`):: -+ --- -Override the command rust-analyzer uses to run build scripts and -build procedural macros. The command is required to output json -and should therefore include `--message-format=json` or a similar -option. - -If there are multiple linked projects/workspaces, this command is invoked for -each of them, with the working directory being the workspace root -(i.e., the folder containing the `Cargo.toml`). This can be overwritten -by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`. - -By default, a cargo invocation will be constructed for the configured -targets and features, with the following base command line: - -```bash -cargo check --quiet --workspace --message-format=json --all-targets --keep-going -``` -. --- -[[rust-analyzer.cargo.buildScripts.rebuildOnSave]]rust-analyzer.cargo.buildScripts.rebuildOnSave (default: `true`):: -+ --- -Rerun proc-macros building/build-scripts running when proc-macro -or build-script sources change and are saved. --- -[[rust-analyzer.cargo.buildScripts.useRustcWrapper]]rust-analyzer.cargo.buildScripts.useRustcWrapper (default: `true`):: -+ --- -Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to -avoid checking unnecessary things. --- -[[rust-analyzer.cargo.cfgs]]rust-analyzer.cargo.cfgs:: -+ --- -Default: ----- -[ - "debug_assertions", - "miri" -] ----- -List of cfg options to enable with the given values. - --- -[[rust-analyzer.cargo.extraArgs]]rust-analyzer.cargo.extraArgs (default: `[]`):: -+ --- -Extra arguments that are passed to every cargo invocation. --- -[[rust-analyzer.cargo.extraEnv]]rust-analyzer.cargo.extraEnv (default: `{}`):: -+ --- -Extra environment variables that will be set when running cargo, rustc -or other commands within the workspace. Useful for setting RUSTFLAGS. --- -[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: -+ --- -List of features to activate. - -Set this to `"all"` to pass `--all-features` to cargo. --- -[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`):: -+ --- -Whether to pass `--no-default-features` to cargo. --- -[[rust-analyzer.cargo.sysroot]]rust-analyzer.cargo.sysroot (default: `"discover"`):: -+ --- -Relative path to the sysroot, or "discover" to try to automatically find it via -"rustc --print sysroot". - -Unsetting this disables sysroot loading. - -This option does not take effect until rust-analyzer is restarted. --- -[[rust-analyzer.cargo.sysrootSrc]]rust-analyzer.cargo.sysrootSrc (default: `null`):: -+ --- -Relative path to the sysroot library sources. If left unset, this will default to -`{cargo.sysroot}/lib/rustlib/src/rust/library`. - -This option does not take effect until rust-analyzer is restarted. --- -[[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`):: -+ --- -Compilation target override (target tuple). --- -[[rust-analyzer.cargo.targetDir]]rust-analyzer.cargo.targetDir (default: `null`):: -+ --- -Optional path to a rust-analyzer specific target directory. -This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro -building from locking the `Cargo.lock` at the expense of duplicating build artifacts. - -Set to `true` to use a subdirectory of the existing target directory or -set to a path relative to the workspace to use that path. --- -[[rust-analyzer.cfg.setTest]]rust-analyzer.cfg.setTest (default: `true`):: -+ --- -Set `cfg(test)` for local crates. Defaults to true. --- -[[rust-analyzer.checkOnSave]]rust-analyzer.checkOnSave (default: `true`):: -+ --- -Run the check command for diagnostics on save. --- -[[rust-analyzer.check.allTargets]]rust-analyzer.check.allTargets (default: `null`):: -+ --- -Check all targets and tests (`--all-targets`). Defaults to -`#rust-analyzer.cargo.allTargets#`. --- -[[rust-analyzer.check.command]]rust-analyzer.check.command (default: `"check"`):: -+ --- -Cargo command to use for `cargo check`. --- -[[rust-analyzer.check.extraArgs]]rust-analyzer.check.extraArgs (default: `[]`):: -+ --- -Extra arguments for `cargo check`. --- -[[rust-analyzer.check.extraEnv]]rust-analyzer.check.extraEnv (default: `{}`):: -+ --- -Extra environment variables that will be set when running `cargo check`. -Extends `#rust-analyzer.cargo.extraEnv#`. --- -[[rust-analyzer.check.features]]rust-analyzer.check.features (default: `null`):: -+ --- -List of features to activate. Defaults to -`#rust-analyzer.cargo.features#`. - -Set to `"all"` to pass `--all-features` to Cargo. --- -[[rust-analyzer.check.ignore]]rust-analyzer.check.ignore (default: `[]`):: -+ --- -List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. - -For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... --- -[[rust-analyzer.check.invocationStrategy]]rust-analyzer.check.invocationStrategy (default: `"per_workspace"`):: -+ --- -Specifies the invocation strategy to use when running the check command. -If `per_workspace` is set, the command will be executed for each workspace. -If `once` is set, the command will be executed once. -This config only has an effect when `#rust-analyzer.check.overrideCommand#` -is set. --- -[[rust-analyzer.check.noDefaultFeatures]]rust-analyzer.check.noDefaultFeatures (default: `null`):: -+ --- -Whether to pass `--no-default-features` to Cargo. Defaults to -`#rust-analyzer.cargo.noDefaultFeatures#`. --- -[[rust-analyzer.check.overrideCommand]]rust-analyzer.check.overrideCommand (default: `null`):: -+ --- -Override the command rust-analyzer uses instead of `cargo check` for -diagnostics on save. The command is required to output json and -should therefore include `--message-format=json` or a similar option -(if your client supports the `colorDiagnosticOutput` experimental -capability, you can use `--message-format=json-diagnostic-rendered-ansi`). - -If you're changing this because you're using some tool wrapping -Cargo, you might also want to change -`#rust-analyzer.cargo.buildScripts.overrideCommand#`. - -If there are multiple linked projects/workspaces, this command is invoked for -each of them, with the working directory being the workspace root -(i.e., the folder containing the `Cargo.toml`). This can be overwritten -by changing `#rust-analyzer.check.invocationStrategy#`. - -If `$saved_file` is part of the command, rust-analyzer will pass -the absolute path of the saved file to the provided command. This is -intended to be used with non-Cargo build systems. -Note that `$saved_file` is experimental and may be removed in the future. - -An example command would be: - -```bash -cargo check --workspace --message-format=json --all-targets -``` -. --- -[[rust-analyzer.check.targets]]rust-analyzer.check.targets (default: `null`):: -+ --- -Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. - -Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. -`["aarch64-apple-darwin", "x86_64-apple-darwin"]`. - -Aliased as `"checkOnSave.targets"`. --- -[[rust-analyzer.check.workspace]]rust-analyzer.check.workspace (default: `true`):: -+ --- -Whether `--workspace` should be passed to `cargo check`. -If false, `-p ` will be passed instead if applicable. In case it is not, no -check will be performed. --- -[[rust-analyzer.completion.addSemicolonToUnit]]rust-analyzer.completion.addSemicolonToUnit (default: `true`):: -+ --- -Whether to automatically add a semicolon when completing unit-returning functions. - -In `match` arms it completes a comma instead. --- -[[rust-analyzer.completion.autoAwait.enable]]rust-analyzer.completion.autoAwait.enable (default: `true`):: -+ --- -Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. --- -[[rust-analyzer.completion.autoIter.enable]]rust-analyzer.completion.autoIter.enable (default: `true`):: -+ --- -Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. --- -[[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: -+ --- -Toggles the additional completions that automatically add imports when completed. -Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. --- -[[rust-analyzer.completion.autoimport.exclude]]rust-analyzer.completion.autoimport.exclude:: -+ --- -Default: ----- -[ - { - "path": "core::borrow::Borrow", - "type": "methods" - }, - { - "path": "core::borrow::BorrowMut", - "type": "methods" - } -] ----- -A list of full paths to items to exclude from auto-importing completions. - -Traits in this list won't have their methods suggested in completions unless the trait -is in scope. - -You can either specify a string path which defaults to type "always" or use the more verbose -form `{ "path": "path::to::item", type: "always" }`. - -For traits the type "methods" can be used to only exclude the methods but not the trait itself. - -This setting also inherits `#rust-analyzer.completion.excludeTraits#`. - --- -[[rust-analyzer.completion.autoself.enable]]rust-analyzer.completion.autoself.enable (default: `true`):: -+ --- -Toggles the additional completions that automatically show method calls and field accesses -with `self` prefixed to them when inside a method. --- -[[rust-analyzer.completion.callable.snippets]]rust-analyzer.completion.callable.snippets (default: `"fill_arguments"`):: -+ --- -Whether to add parenthesis and argument snippets when completing function. --- -[[rust-analyzer.completion.excludeTraits]]rust-analyzer.completion.excludeTraits (default: `[]`):: -+ --- -A list of full paths to traits whose methods to exclude from completion. - -Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`. - -Note that the trait themselves can still be completed. --- -[[rust-analyzer.completion.fullFunctionSignatures.enable]]rust-analyzer.completion.fullFunctionSignatures.enable (default: `false`):: -+ --- -Whether to show full function/method signatures in completion docs. --- -[[rust-analyzer.completion.hideDeprecated]]rust-analyzer.completion.hideDeprecated (default: `false`):: -+ --- -Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. --- -[[rust-analyzer.completion.limit]]rust-analyzer.completion.limit (default: `null`):: -+ --- -Maximum number of completions to return. If `None`, the limit is infinite. --- -[[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`):: -+ --- -Whether to show postfix snippets like `dbg`, `if`, `not`, etc. --- -[[rust-analyzer.completion.privateEditable.enable]]rust-analyzer.completion.privateEditable.enable (default: `false`):: -+ --- -Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. --- -[[rust-analyzer.completion.snippets.custom]]rust-analyzer.completion.snippets.custom:: -+ --- -Default: ----- -{ - "Ok": { - "postfix": "ok", - "body": "Ok(${receiver})", - "description": "Wrap the expression in a `Result::Ok`", - "scope": "expr" - }, - "Box::pin": { - "postfix": "pinbox", - "body": "Box::pin(${receiver})", - "requires": "std::boxed::Box", - "description": "Put the expression into a pinned `Box`", - "scope": "expr" - }, - "Arc::new": { - "postfix": "arc", - "body": "Arc::new(${receiver})", - "requires": "std::sync::Arc", - "description": "Put the expression into an `Arc`", - "scope": "expr" - }, - "Some": { - "postfix": "some", - "body": "Some(${receiver})", - "description": "Wrap the expression in an `Option::Some`", - "scope": "expr" - }, - "Err": { - "postfix": "err", - "body": "Err(${receiver})", - "description": "Wrap the expression in a `Result::Err`", - "scope": "expr" - }, - "Rc::new": { - "postfix": "rc", - "body": "Rc::new(${receiver})", - "requires": "std::rc::Rc", - "description": "Put the expression into an `Rc`", - "scope": "expr" - } -} ----- -Custom completion snippets. - --- -[[rust-analyzer.completion.termSearch.enable]]rust-analyzer.completion.termSearch.enable (default: `false`):: -+ --- -Whether to enable term search based snippets like `Some(foo.bar().baz())`. --- -[[rust-analyzer.completion.termSearch.fuel]]rust-analyzer.completion.termSearch.fuel (default: `1000`):: -+ --- -Term search fuel in "units of work" for autocompletion (Defaults to 1000). --- -[[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`):: -+ --- -List of rust-analyzer diagnostics to disable. --- -[[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: -+ --- -Whether to show native rust-analyzer diagnostics. --- -[[rust-analyzer.diagnostics.experimental.enable]]rust-analyzer.diagnostics.experimental.enable (default: `false`):: -+ --- -Whether to show experimental rust-analyzer diagnostics that might -have more false positives than usual. --- -[[rust-analyzer.diagnostics.remapPrefix]]rust-analyzer.diagnostics.remapPrefix (default: `{}`):: -+ --- -Map of prefixes to be substituted when parsing diagnostic file paths. -This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. --- -[[rust-analyzer.diagnostics.styleLints.enable]]rust-analyzer.diagnostics.styleLints.enable (default: `false`):: -+ --- -Whether to run additional style lints. --- -[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: -+ --- -List of warnings that should be displayed with hint severity. - -The warnings will be indicated by faded text or three dots in code -and will not show up in the `Problems Panel`. --- -[[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`):: -+ --- -List of warnings that should be displayed with info severity. - -The warnings will be indicated by a blue squiggly underline in code -and a blue icon in the `Problems Panel`. --- -[[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`):: -+ --- -These directories will be ignored by rust-analyzer. They are -relative to the workspace root, and globs are not supported. You may -also need to add the folders to Code's `files.watcherExclude`. --- -[[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: -+ --- -Controls file watching implementation. --- -[[rust-analyzer.highlightRelated.breakPoints.enable]]rust-analyzer.highlightRelated.breakPoints.enable (default: `true`):: -+ --- -Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. --- -[[rust-analyzer.highlightRelated.closureCaptures.enable]]rust-analyzer.highlightRelated.closureCaptures.enable (default: `true`):: -+ --- -Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. --- -[[rust-analyzer.highlightRelated.exitPoints.enable]]rust-analyzer.highlightRelated.exitPoints.enable (default: `true`):: -+ --- -Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). --- -[[rust-analyzer.highlightRelated.references.enable]]rust-analyzer.highlightRelated.references.enable (default: `true`):: -+ --- -Enables highlighting of related references while the cursor is on any identifier. --- -[[rust-analyzer.highlightRelated.yieldPoints.enable]]rust-analyzer.highlightRelated.yieldPoints.enable (default: `true`):: -+ --- -Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. --- -[[rust-analyzer.hover.actions.debug.enable]]rust-analyzer.hover.actions.debug.enable (default: `true`):: -+ --- -Whether to show `Debug` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.enable]]rust-analyzer.hover.actions.enable (default: `true`):: -+ --- -Whether to show HoverActions in Rust files. --- -[[rust-analyzer.hover.actions.gotoTypeDef.enable]]rust-analyzer.hover.actions.gotoTypeDef.enable (default: `true`):: -+ --- -Whether to show `Go to Type Definition` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.implementations.enable]]rust-analyzer.hover.actions.implementations.enable (default: `true`):: -+ --- -Whether to show `Implementations` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.references.enable]]rust-analyzer.hover.actions.references.enable (default: `false`):: -+ --- -Whether to show `References` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.run.enable]]rust-analyzer.hover.actions.run.enable (default: `true`):: -+ --- -Whether to show `Run` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.updateTest.enable]]rust-analyzer.hover.actions.updateTest.enable (default: `true`):: -+ --- -Whether to show `Update Test` action. Only applies when -`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set. --- -[[rust-analyzer.hover.documentation.enable]]rust-analyzer.hover.documentation.enable (default: `true`):: -+ --- -Whether to show documentation on hover. --- -[[rust-analyzer.hover.documentation.keywords.enable]]rust-analyzer.hover.documentation.keywords.enable (default: `true`):: -+ --- -Whether to show keyword hover popups. Only applies when -`#rust-analyzer.hover.documentation.enable#` is set. --- -[[rust-analyzer.hover.links.enable]]rust-analyzer.hover.links.enable (default: `true`):: -+ --- -Use markdown syntax for links on hover. --- -[[rust-analyzer.hover.maxSubstitutionLength]]rust-analyzer.hover.maxSubstitutionLength (default: `20`):: -+ --- -Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis. - -This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters. - -The default is 20 characters. --- -[[rust-analyzer.hover.memoryLayout.alignment]]rust-analyzer.hover.memoryLayout.alignment (default: `"hexadecimal"`):: -+ --- -How to render the align information in a memory layout hover. --- -[[rust-analyzer.hover.memoryLayout.enable]]rust-analyzer.hover.memoryLayout.enable (default: `true`):: -+ --- -Whether to show memory layout data on hover. --- -[[rust-analyzer.hover.memoryLayout.niches]]rust-analyzer.hover.memoryLayout.niches (default: `false`):: -+ --- -How to render the niche information in a memory layout hover. --- -[[rust-analyzer.hover.memoryLayout.offset]]rust-analyzer.hover.memoryLayout.offset (default: `"hexadecimal"`):: -+ --- -How to render the offset information in a memory layout hover. --- -[[rust-analyzer.hover.memoryLayout.size]]rust-analyzer.hover.memoryLayout.size (default: `"both"`):: -+ --- -How to render the size information in a memory layout hover. --- -[[rust-analyzer.hover.show.enumVariants]]rust-analyzer.hover.show.enumVariants (default: `5`):: -+ --- -How many variants of an enum to display when hovering on. Show none if empty. --- -[[rust-analyzer.hover.show.fields]]rust-analyzer.hover.show.fields (default: `5`):: -+ --- -How many fields of a struct, variant or union to display when hovering on. Show none if empty. --- -[[rust-analyzer.hover.show.traitAssocItems]]rust-analyzer.hover.show.traitAssocItems (default: `null`):: -+ --- -How many associated items of a trait to display when hovering a trait. --- -[[rust-analyzer.imports.granularity.enforce]]rust-analyzer.imports.granularity.enforce (default: `false`):: -+ --- -Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. --- -[[rust-analyzer.imports.granularity.group]]rust-analyzer.imports.granularity.group (default: `"crate"`):: -+ --- -How imports should be grouped into use statements. --- -[[rust-analyzer.imports.group.enable]]rust-analyzer.imports.group.enable (default: `true`):: -+ --- -Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines. --- -[[rust-analyzer.imports.merge.glob]]rust-analyzer.imports.merge.glob (default: `true`):: -+ --- -Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. --- -[[rust-analyzer.imports.preferNoStd]]rust-analyzer.imports.preferNoStd (default: `false`):: -+ --- -Prefer to unconditionally use imports of the core and alloc crate, over the std crate. --- -[[rust-analyzer.imports.preferPrelude]]rust-analyzer.imports.preferPrelude (default: `false`):: -+ --- -Whether to prefer import paths containing a `prelude` module. --- -[[rust-analyzer.imports.prefix]]rust-analyzer.imports.prefix (default: `"plain"`):: -+ --- -The path structure for newly inserted paths to use. --- -[[rust-analyzer.imports.prefixExternPrelude]]rust-analyzer.imports.prefixExternPrelude (default: `false`):: -+ --- -Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;". --- -[[rust-analyzer.inlayHints.bindingModeHints.enable]]rust-analyzer.inlayHints.bindingModeHints.enable (default: `false`):: -+ --- -Whether to show inlay type hints for binding modes. --- -[[rust-analyzer.inlayHints.chainingHints.enable]]rust-analyzer.inlayHints.chainingHints.enable (default: `true`):: -+ --- -Whether to show inlay type hints for method chains. --- -[[rust-analyzer.inlayHints.closingBraceHints.enable]]rust-analyzer.inlayHints.closingBraceHints.enable (default: `true`):: -+ --- -Whether to show inlay hints after a closing `}` to indicate what item it belongs to. --- -[[rust-analyzer.inlayHints.closingBraceHints.minLines]]rust-analyzer.inlayHints.closingBraceHints.minLines (default: `25`):: -+ --- -Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 -to always show them). --- -[[rust-analyzer.inlayHints.closureCaptureHints.enable]]rust-analyzer.inlayHints.closureCaptureHints.enable (default: `false`):: -+ --- -Whether to show inlay hints for closure captures. --- -[[rust-analyzer.inlayHints.closureReturnTypeHints.enable]]rust-analyzer.inlayHints.closureReturnTypeHints.enable (default: `"never"`):: -+ --- -Whether to show inlay type hints for return types of closures. --- -[[rust-analyzer.inlayHints.closureStyle]]rust-analyzer.inlayHints.closureStyle (default: `"impl_fn"`):: -+ --- -Closure notation in type and chaining inlay hints. --- -[[rust-analyzer.inlayHints.discriminantHints.enable]]rust-analyzer.inlayHints.discriminantHints.enable (default: `"never"`):: -+ --- -Whether to show enum variant discriminant hints. --- -[[rust-analyzer.inlayHints.expressionAdjustmentHints.enable]]rust-analyzer.inlayHints.expressionAdjustmentHints.enable (default: `"never"`):: -+ --- -Whether to show inlay hints for type adjustments. --- -[[rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe]]rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe (default: `false`):: -+ --- -Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. --- -[[rust-analyzer.inlayHints.expressionAdjustmentHints.mode]]rust-analyzer.inlayHints.expressionAdjustmentHints.mode (default: `"prefix"`):: -+ --- -Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). --- -[[rust-analyzer.inlayHints.genericParameterHints.const.enable]]rust-analyzer.inlayHints.genericParameterHints.const.enable (default: `true`):: -+ --- -Whether to show const generic parameter name inlay hints. --- -[[rust-analyzer.inlayHints.genericParameterHints.lifetime.enable]]rust-analyzer.inlayHints.genericParameterHints.lifetime.enable (default: `false`):: -+ --- -Whether to show generic lifetime parameter name inlay hints. --- -[[rust-analyzer.inlayHints.genericParameterHints.type.enable]]rust-analyzer.inlayHints.genericParameterHints.type.enable (default: `false`):: -+ --- -Whether to show generic type parameter name inlay hints. --- -[[rust-analyzer.inlayHints.implicitDrops.enable]]rust-analyzer.inlayHints.implicitDrops.enable (default: `false`):: -+ --- -Whether to show implicit drop hints. --- -[[rust-analyzer.inlayHints.implicitSizedBoundHints.enable]]rust-analyzer.inlayHints.implicitSizedBoundHints.enable (default: `false`):: -+ --- -Whether to show inlay hints for the implied type parameter `Sized` bound. --- -[[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`):: -+ --- -Whether to show inlay type hints for elided lifetimes in function signatures. --- -[[rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames]]rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames (default: `false`):: -+ --- -Whether to prefer using parameter names as the name for elided lifetime hints if possible. --- -[[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `25`):: -+ --- -Maximum length for inlay hints. Set to null to have an unlimited length. --- -[[rust-analyzer.inlayHints.parameterHints.enable]]rust-analyzer.inlayHints.parameterHints.enable (default: `true`):: -+ --- -Whether to show function parameter name inlay hints at the call -site. --- -[[rust-analyzer.inlayHints.rangeExclusiveHints.enable]]rust-analyzer.inlayHints.rangeExclusiveHints.enable (default: `false`):: -+ --- -Whether to show exclusive range inlay hints. --- -[[rust-analyzer.inlayHints.reborrowHints.enable]]rust-analyzer.inlayHints.reborrowHints.enable (default: `"never"`):: -+ --- -Whether to show inlay hints for compiler inserted reborrows. -This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. --- -[[rust-analyzer.inlayHints.renderColons]]rust-analyzer.inlayHints.renderColons (default: `true`):: -+ --- -Whether to render leading colons for type hints, and trailing colons for parameter hints. --- -[[rust-analyzer.inlayHints.typeHints.enable]]rust-analyzer.inlayHints.typeHints.enable (default: `true`):: -+ --- -Whether to show inlay type hints for variables. --- -[[rust-analyzer.inlayHints.typeHints.hideClosureInitialization]]rust-analyzer.inlayHints.typeHints.hideClosureInitialization (default: `false`):: -+ --- -Whether to hide inlay type hints for `let` statements that initialize to a closure. -Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. --- -[[rust-analyzer.inlayHints.typeHints.hideNamedConstructor]]rust-analyzer.inlayHints.typeHints.hideNamedConstructor (default: `false`):: -+ --- -Whether to hide inlay type hints for constructors. --- -[[rust-analyzer.interpret.tests]]rust-analyzer.interpret.tests (default: `false`):: -+ --- -Enables the experimental support for interpreting tests. --- -[[rust-analyzer.joinLines.joinAssignments]]rust-analyzer.joinLines.joinAssignments (default: `true`):: -+ --- -Join lines merges consecutive declaration and initialization of an assignment. --- -[[rust-analyzer.joinLines.joinElseIf]]rust-analyzer.joinLines.joinElseIf (default: `true`):: -+ --- -Join lines inserts else between consecutive ifs. --- -[[rust-analyzer.joinLines.removeTrailingComma]]rust-analyzer.joinLines.removeTrailingComma (default: `true`):: -+ --- -Join lines removes trailing commas. --- -[[rust-analyzer.joinLines.unwrapTrivialBlock]]rust-analyzer.joinLines.unwrapTrivialBlock (default: `true`):: -+ --- -Join lines unwraps trivial blocks. --- -[[rust-analyzer.lens.debug.enable]]rust-analyzer.lens.debug.enable (default: `true`):: -+ --- -Whether to show `Debug` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`):: -+ --- -Whether to show CodeLens in Rust files. --- -[[rust-analyzer.lens.implementations.enable]]rust-analyzer.lens.implementations.enable (default: `true`):: -+ --- -Whether to show `Implementations` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.location]]rust-analyzer.lens.location (default: `"above_name"`):: -+ --- -Where to render annotations. --- -[[rust-analyzer.lens.references.adt.enable]]rust-analyzer.lens.references.adt.enable (default: `false`):: -+ --- -Whether to show `References` lens for Struct, Enum, and Union. -Only applies when `#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.references.enumVariant.enable]]rust-analyzer.lens.references.enumVariant.enable (default: `false`):: -+ --- -Whether to show `References` lens for Enum Variants. -Only applies when `#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.references.method.enable]]rust-analyzer.lens.references.method.enable (default: `false`):: -+ --- -Whether to show `Method References` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.references.trait.enable]]rust-analyzer.lens.references.trait.enable (default: `false`):: -+ --- -Whether to show `References` lens for Trait. -Only applies when `#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.run.enable]]rust-analyzer.lens.run.enable (default: `true`):: -+ --- -Whether to show `Run` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.updateTest.enable]]rust-analyzer.lens.updateTest.enable (default: `true`):: -+ --- -Whether to show `Update Test` lens. Only applies when -`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set. --- -[[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: -+ --- -Disable project auto-discovery in favor of explicitly specified set -of projects. - -Elements must be paths pointing to `Cargo.toml`, -`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON -objects in `rust-project.json` format. --- -[[rust-analyzer.lru.capacity]]rust-analyzer.lru.capacity (default: `null`):: -+ --- -Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. --- -[[rust-analyzer.lru.query.capacities]]rust-analyzer.lru.query.capacities (default: `{}`):: -+ --- -Sets the LRU capacity of the specified queries. --- -[[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`):: -+ --- -Whether to show `can't find Cargo.toml` error message. --- -[[rust-analyzer.numThreads]]rust-analyzer.numThreads (default: `null`):: -+ --- -How many worker threads in the main loop. The default `null` means to pick automatically. --- -[[rust-analyzer.procMacro.attributes.enable]]rust-analyzer.procMacro.attributes.enable (default: `true`):: -+ --- -Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. --- -[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `true`):: -+ --- -Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. --- -[[rust-analyzer.procMacro.ignored]]rust-analyzer.procMacro.ignored (default: `{}`):: -+ --- -These proc-macros will be ignored when trying to expand them. - -This config takes a map of crate names with the exported proc-macro names to ignore as values. --- -[[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`):: -+ --- -Internal config, path to proc-macro server executable. --- -[[rust-analyzer.references.excludeImports]]rust-analyzer.references.excludeImports (default: `false`):: -+ --- -Exclude imports from find-all-references. --- -[[rust-analyzer.references.excludeTests]]rust-analyzer.references.excludeTests (default: `false`):: -+ --- -Exclude tests from find-all-references and call-hierarchy. --- -[[rust-analyzer.runnables.command]]rust-analyzer.runnables.command (default: `null`):: -+ --- -Command to be executed instead of 'cargo' for runnables. --- -[[rust-analyzer.runnables.extraArgs]]rust-analyzer.runnables.extraArgs (default: `[]`):: -+ --- -Additional arguments to be passed to cargo for runnables such as -tests or binaries. For example, it may be `--release`. --- -[[rust-analyzer.runnables.extraTestBinaryArgs]]rust-analyzer.runnables.extraTestBinaryArgs:: -+ --- -Default: ----- -[ - "--show-output" -] ----- -Additional arguments to be passed through Cargo to launched tests, benchmarks, or -doc-tests. - -Unless the launched target uses a -[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), -they will end up being interpreted as options to -[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). - --- -[[rust-analyzer.rustc.source]]rust-analyzer.rustc.source (default: `null`):: -+ --- -Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private -projects, or "discover" to try to automatically find it if the `rustc-dev` component -is installed. - -Any project which uses rust-analyzer with the rustcPrivate -crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. - -This option does not take effect until rust-analyzer is restarted. --- -[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: -+ --- -Additional arguments to `rustfmt`. --- -[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: -+ --- -Advanced option, fully override the command rust-analyzer uses for -formatting. This should be the equivalent of `rustfmt` here, and -not that of `cargo fmt`. The file contents will be passed on the -standard input and the formatted result will be read from the -standard output. --- -[[rust-analyzer.rustfmt.rangeFormatting.enable]]rust-analyzer.rustfmt.rangeFormatting.enable (default: `false`):: -+ --- -Enables the use of rustfmt's unstable range formatting command for the -`textDocument/rangeFormatting` request. The rustfmt option is unstable and only -available on a nightly build. --- -[[rust-analyzer.semanticHighlighting.doc.comment.inject.enable]]rust-analyzer.semanticHighlighting.doc.comment.inject.enable (default: `true`):: -+ --- -Inject additional highlighting into doc comments. - -When enabled, rust-analyzer will highlight rust source in doc comments as well as intra -doc links. --- -[[rust-analyzer.semanticHighlighting.nonStandardTokens]]rust-analyzer.semanticHighlighting.nonStandardTokens (default: `true`):: -+ --- -Whether the server is allowed to emit non-standard tokens and modifiers. --- -[[rust-analyzer.semanticHighlighting.operator.enable]]rust-analyzer.semanticHighlighting.operator.enable (default: `true`):: -+ --- -Use semantic tokens for operators. - -When disabled, rust-analyzer will emit semantic tokens only for operator tokens when -they are tagged with modifiers. --- -[[rust-analyzer.semanticHighlighting.operator.specialization.enable]]rust-analyzer.semanticHighlighting.operator.specialization.enable (default: `false`):: -+ --- -Use specialized semantic tokens for operators. - -When enabled, rust-analyzer will emit special token types for operator tokens instead -of the generic `operator` token type. --- -[[rust-analyzer.semanticHighlighting.punctuation.enable]]rust-analyzer.semanticHighlighting.punctuation.enable (default: `false`):: -+ --- -Use semantic tokens for punctuation. - -When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when -they are tagged with modifiers or have a special role. --- -[[rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang]]rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang (default: `false`):: -+ --- -When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro -calls. --- -[[rust-analyzer.semanticHighlighting.punctuation.specialization.enable]]rust-analyzer.semanticHighlighting.punctuation.specialization.enable (default: `false`):: -+ --- -Use specialized semantic tokens for punctuation. - -When enabled, rust-analyzer will emit special token types for punctuation tokens instead -of the generic `punctuation` token type. --- -[[rust-analyzer.semanticHighlighting.strings.enable]]rust-analyzer.semanticHighlighting.strings.enable (default: `true`):: -+ --- -Use semantic tokens for strings. - -In some editors (e.g. vscode) semantic tokens override other highlighting grammars. -By disabling semantic tokens for strings, other grammars can be used to highlight -their contents. --- -[[rust-analyzer.signatureInfo.detail]]rust-analyzer.signatureInfo.detail (default: `"full"`):: -+ --- -Show full signature of the callable. Only shows parameters if disabled. --- -[[rust-analyzer.signatureInfo.documentation.enable]]rust-analyzer.signatureInfo.documentation.enable (default: `true`):: -+ --- -Show documentation. --- -[[rust-analyzer.typing.triggerChars]]rust-analyzer.typing.triggerChars (default: `"=."`):: -+ --- -Specify the characters allowed to invoke special on typing triggers. -- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression -- typing `=` between two expressions adds `;` when in statement position -- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position -- typing `.` in a chain method call auto-indents -- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression -- typing `{` in a use item adds a closing `}` in the right place -- typing `>` to complete a return type `->` will insert a whitespace after it -- typing `<` in a path or type position inserts a closing `>` after the path or type. --- -[[rust-analyzer.vfs.extraIncludes]]rust-analyzer.vfs.extraIncludes (default: `[]`):: -+ --- -Additional paths to include in the VFS. Generally for code that is -generated or otherwise managed by a build system outside of Cargo, -though Cargo might be the eventual consumer. --- -[[rust-analyzer.workspace.discoverConfig]]rust-analyzer.workspace.discoverConfig (default: `null`):: -+ --- -Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. - -[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. -`progress_label` is used for the title in progress indicators, whereas `files_to_watch` -is used to determine which build system-specific files should be watched in order to -reload rust-analyzer. - -Below is an example of a valid configuration: -```json -"rust-analyzer.workspace.discoverConfig": { - "command": [ - "rust-project", - "develop-json" - ], - "progressLabel": "rust-analyzer", - "filesToWatch": [ - "BUCK" - ] -} -``` - -## On `DiscoverWorkspaceConfig::command` - -**Warning**: This format is provisional and subject to change. - -[`DiscoverWorkspaceConfig::command`] *must* return a JSON object -corresponding to `DiscoverProjectData::Finished`: - -```norun -#[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(tag = "kind")] -#[serde(rename_all = "snake_case")] -enum DiscoverProjectData { - Finished { buildfile: Utf8PathBuf, project: ProjectJsonData }, - Error { error: String, source: Option }, - Progress { message: String }, -} -``` - -As JSON, `DiscoverProjectData::Finished` is: - -```json -{ - // the internally-tagged representation of the enum. - "kind": "finished", - // the file used by a non-Cargo build system to define - // a package or target. - "buildfile": "rust-analyzer/BUILD", - // the contents of a rust-project.json, elided for brevity - "project": { - "sysroot": "foo", - "crates": [] - } -} -``` - -It is encouraged, but not required, to use the other variants on -`DiscoverProjectData` to provide a more polished end-user experience. - -`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, -which will be substituted with the JSON-serialized form of the following -enum: - -```norun -#[derive(PartialEq, Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub enum DiscoverArgument { - Path(AbsPathBuf), - Buildfile(AbsPathBuf), -} -``` - -The JSON representation of `DiscoverArgument::Path` is: - -```json -{ - "path": "src/main.rs" -} -``` - -Similarly, the JSON representation of `DiscoverArgument::Buildfile` is: - -``` -{ - "buildfile": "BUILD" -} -``` - -`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, -and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to -to update an existing workspace. As a reference for implementors, -buck2's `rust-project` will likely be useful: -https://github.com/facebook/buck2/tree/main/integrations/rust-project. --- -[[rust-analyzer.workspace.symbol.search.kind]]rust-analyzer.workspace.symbol.search.kind (default: `"only_types"`):: -+ --- -Workspace symbol search kind. --- -[[rust-analyzer.workspace.symbol.search.limit]]rust-analyzer.workspace.symbol.search.limit (default: `128`):: -+ --- -Limits the number of items returned from a workspace symbol search (Defaults to 128). -Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search. -Other clients requires all results upfront and might require a higher limit. --- -[[rust-analyzer.workspace.symbol.search.scope]]rust-analyzer.workspace.symbol.search.scope (default: `"workspace"`):: -+ --- -Workspace symbol search scope. --- diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc deleted file mode 100644 index 4a2a6f2e3686..000000000000 --- a/src/tools/rust-analyzer/docs/user/manual.adoc +++ /dev/null @@ -1,1121 +0,0 @@ -= User Manual -:toc: preamble -:sectanchors: -:page-layout: post -:icons: font -:source-highlighter: rouge -:experimental: - -//// -IMPORTANT: the master copy of this document lives in the https://github.com/rust-lang/rust-analyzer repository -//// - -At its core, rust-analyzer is a *library* for semantic analysis of Rust code as it changes over time. -This manual focuses on a specific usage of the library -- running it as part of a server that implements the -https://microsoft.github.io/language-server-protocol/[Language Server Protocol] (LSP). -The LSP allows various code editors, like VS Code, Emacs or Vim, to implement semantic features like completion or goto definition by talking to an external language server process. - -[TIP] -==== -[.lead] -To improve this document, send a pull request: + -https://github.com/rust-lang/rust-analyzer/blob/master/docs/user/manual.adoc[https://github.com/rust-analyzer/.../manual.adoc] - -The manual is written in https://asciidoc.org[AsciiDoc] and includes some extra files which are generated from the source code. Run `cargo test` and `cargo xtask codegen` to create these and then `asciidoctor manual.adoc` to create an HTML copy. -==== - -If you have questions about using rust-analyzer, please ask them in the https://users.rust-lang.org/c/ide/14["`IDEs and Editors`"] topic of Rust users forum. - -== Installation - -In theory, one should be able to just install the <> and have it automatically work with any editor. -We are not there yet, so some editor specific setup is required. - -Additionally, rust-analyzer needs the sources of the standard library. -If the source code is not present, rust-analyzer will attempt to install it automatically. - -To add the sources manually, run the following command: - -```bash -$ rustup component add rust-src -``` - -=== Toolchain - -Only the latest stable standard library source is officially supported for use with rust-analyzer. -If you are using an older toolchain or have an override set, rust-analyzer may fail to understand the Rust source. -You will either need to update your toolchain or use an older version of rust-analyzer that is compatible with your toolchain. - -If you are using an override in your project, you can still force rust-analyzer to use the stable toolchain via the environment variable `RUSTUP_TOOLCHAIN`. -For example, with VS Code or coc-rust-analyzer: - -[source,json] ----- -{ "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } } ----- - -=== VS Code - -This is the best supported editor at the moment. -The rust-analyzer plugin for VS Code is maintained -https://github.com/rust-lang/rust-analyzer/tree/master/editors/code[in tree]. - -You can install the latest release of the plugin from -https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer[the marketplace]. - -Note that the plugin may cause conflicts with the -https://marketplace.visualstudio.com/items?itemName=rust-lang.rust[previous official Rust plugin]. -The latter is no longer maintained and should be uninstalled. - -The server binary is stored in the extension install directory, which starts with `rust-lang.rust-analyzer-` and is located under: - -* Linux: `~/.vscode/extensions` -* Linux (Remote, such as WSL): `~/.vscode-server/extensions` -* macOS: `~/.vscode/extensions` -* Windows: `%USERPROFILE%\.vscode\extensions` - -As an exception, on NixOS, the extension makes a copy of the server and stores it under `~/.config/Code/User/globalStorage/rust-lang.rust-analyzer`. - -Note that we only support the two most recent versions of VS Code. - -==== Updates - -The extension will be updated automatically as new versions become available. -It will ask your permission to download the matching language server version binary if needed. - -===== Nightly - -We ship nightly releases for VS Code. -To help us out by testing the newest code, you can enable pre-release versions in the Code extension page. - -==== Manual installation - -Alternatively, download a VSIX corresponding to your platform from the -https://github.com/rust-lang/rust-analyzer/releases[releases] page. - -Install the extension with the `Extensions: Install from VSIX` command within VS Code, or from the command line via: -[source] ----- -$ code --install-extension /path/to/rust-analyzer.vsix ----- - -If you are running an unsupported platform, you can install `rust-analyzer-no-server.vsix` and compile or obtain a server binary. -Copy the server anywhere, then add the path to your settings.json, for example: -[source,json] ----- -{ "rust-analyzer.server.path": "~/.local/bin/rust-analyzer-linux" } ----- - -==== Building From Source - -Both the server and the Code plugin can be installed from source: - -[source] ----- -$ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer -$ cargo xtask install ----- - -You'll need Cargo, nodejs (matching a supported version of VS Code) and npm for this. - -Note that installing via `xtask install` does not work for VS Code Remote, instead you'll need to install the `.vsix` manually. - -If you're not using Code, you can compile and install only the LSP server: - -[source] ----- -$ cargo xtask install --server ----- - -Make sure that `.cargo/bin` is in `$PATH` and precedes paths where `rust-analyzer` may also be installed. -Specifically, `rustup` includes a proxy called `rust-analyzer`, which can cause problems if you're planning to use a source build or even a downloaded binary. - -=== rust-analyzer Language Server Binary - -Other editors generally require the `rust-analyzer` binary to be in `$PATH`. -You can download pre-built binaries from the https://github.com/rust-lang/rust-analyzer/releases[releases] page. -You will need to uncompress and rename the binary for your platform, e.g. from `rust-analyzer-aarch64-apple-darwin.gz` on Mac OS to `rust-analyzer`, make it executable, then move it into a directory in your `$PATH`. - -On Linux to install the `rust-analyzer` binary into `~/.local/bin`, these commands should work: - -[source,bash] ----- -$ mkdir -p ~/.local/bin -$ curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer -$ chmod +x ~/.local/bin/rust-analyzer ----- - -Make sure that `~/.local/bin` is listed in the `$PATH` variable and use the appropriate URL if you're not on a `x86-64` system. - -You don't have to use `~/.local/bin`, any other path like `~/.cargo/bin` or `/usr/local/bin` will work just as well. - -Alternatively, you can install it from source using the command below. -You'll need the latest stable version of the Rust toolchain. - -[source,bash] ----- -$ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer -$ cargo xtask install --server ----- - -If your editor can't find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn't see the same `$PATH` as the shell, see https://github.com/rust-lang/rust-analyzer/issues/1811[this issue]. -On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help. - -==== rustup - -`rust-analyzer` is available in `rustup`: - -[source,bash] ----- -$ rustup component add rust-analyzer ----- - -==== Arch Linux - -The `rust-analyzer` binary can be installed from the repos or AUR (Arch User Repository): - -- https://www.archlinux.org/packages/extra/x86_64/rust-analyzer/[`rust-analyzer`] (built from latest tagged source) -- https://aur.archlinux.org/packages/rust-analyzer-git[`rust-analyzer-git`] (latest Git version) - -Install it with pacman, for example: - -[source,bash] ----- -$ pacman -S rust-analyzer ----- - -==== Gentoo Linux - -There are two ways to install `rust-analyzer` under Gentoo: - -- when installing `dev-lang/rust` or `dev-lang/rust-bin`, enable the `rust-analyzer` and `rust-src` USE flags -- use the `rust-analyzer` component in `rustup` (see instructions above) - -Note that in both cases, the version installed lags for a couple of months behind the official releases on GitHub. -To obtain a newer one, you can download a binary from GitHub Releases or building from source. - -==== macOS - -The `rust-analyzer` binary can be installed via https://brew.sh/[Homebrew]. - -[source,bash] ----- -$ brew install rust-analyzer ----- - -==== Windows - -It is recommended to install the latest Microsoft Visual C++ Redistributable prior to installation. -Download links can be found -https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist[here]. - -=== VS Code or VSCodium in Flatpak - -Setting up `rust-analyzer` with a Flatpak version of Code is not trivial because of the Flatpak sandbox. -While the sandbox can be disabled for some directories, `/usr/bin` will always be mounted under `/run/host/usr/bin`. -This prevents access to the system's C compiler, a system-wide installation of Rust, or any other libraries you might want to link to. -Some compilers and libraries can be acquired as Flatpak SDKs, such as `org.freedesktop.Sdk.Extension.rust-stable` or `org.freedesktop.Sdk.Extension.llvm15`. - -If you use a Flatpak SDK for Rust, it must be in your `PATH`: - - * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm15,rust-stable}//23.08` - * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm15,rust-stable` (this can be done using flatseal or `flatpak override`) - -If you want to use Flatpak in combination with `rustup`, the following steps might help: - - - both Rust and `rustup` have to be installed using https://rustup.rs. Distro packages _will not_ work. - - you need to launch Code, open a terminal and run `echo $PATH` - - using https://flathub.org/apps/details/com.github.tchx84.Flatseal[Flatseal], you must add an environment variable called `PATH`. - Set its value to the output from above, appending `:~/.cargo/bin`, where `~` is the path to your home directory. - You must replace `~`, as it won't be expanded otherwise. - - while Flatseal is open, you must enable access to "All user files" - -A C compiler should already be available via `org.freedesktop.Sdk`. -Any other tools or libraries you will need to acquire from Flatpak. - -=== Emacs - -Prerequisites: You have installed the <>. - -To use `rust-analyzer`, you need to install and enable one of the two popular LSP client implementations for Emacs, https://github.com/joaotavora/eglot[Eglot] or https://github.com/emacs-lsp/lsp-mode[LSP Mode]. Both enable `rust-analyzer` by default in rust buffers if it is available. - -==== Eglot - -Eglot is the more minimalistic and lightweight LSP client for Emacs, integrates well with existing Emacs functionality and is built into Emacs starting from release 29. - -After installing Eglot, e.g. via `M-x package-install` (not needed from Emacs 29), you can enable it via the `M-x eglot` command or load it automatically in `rust-mode` via - -[source,emacs-lisp] ----- -(add-hook 'rust-mode-hook 'eglot-ensure) ----- - -To enable clippy, you will need to configure the initialization options to pass the `check.command` setting. - -[source,emacs-lisp] ----- -(add-to-list 'eglot-server-programs - '((rust-ts-mode rust-mode) . - ("rust-analyzer" :initializationOptions (:check (:command "clippy"))))) ----- - -For more detailed instructions and options see the https://joaotavora.github.io/eglot[Eglot manual] (also available from Emacs via `M-x info`) and the -https://github.com/joaotavora/eglot/blob/master/README.md[Eglot readme]. - -Eglot does not support the rust-analyzer extensions to the language-server protocol and does not aim to do so in the future. The https://github.com/nemethf/eglot-x#rust-analyzer-extensions[eglot-x] package adds experimental support for those LSP extensions. - -==== LSP Mode - -LSP-mode is the original LSP-client for emacs. Compared to Eglot it has a larger codebase and supports more features, like LSP protocol extensions. -With extension packages like https://github.com/emacs-lsp/lsp-mode[LSP UI] it offers a lot of visual eyecandy. -Further it integrates well with https://github.com/emacs-lsp/dap-mode[DAP mode] for support of the Debug Adapter Protocol. - -You can install LSP-mode via `M-x package-install` and then run it via the `M-x lsp` command or load it automatically in rust buffers with - -[source,emacs-lisp] ----- -(add-hook 'rust-mode-hook 'lsp-deferred) ----- - -For more information on how to set up LSP mode and its extension package see the instructions in the https://emacs-lsp.github.io/lsp-mode/page/installation[LSP mode manual]. -Also see the https://emacs-lsp.github.io/lsp-mode/page/lsp-rust-analyzer/[rust-analyzer section] for `rust-analyzer` specific options and commands, which you can optionally bind to keys. - -Note the excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm] on how to set-up Emacs for Rust development with LSP mode and several other packages. - -=== Vim/Neovim - -Prerequisites: You have installed the <>. -Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example. - -There are several LSP client implementations for Vim or Neovim: - -==== coc-rust-analyzer - -1. Install coc.nvim by following the instructions at - https://github.com/neoclide/coc.nvim[coc.nvim] - (Node.js required) -2. Run `:CocInstall coc-rust-analyzer` to install - https://github.com/fannheyward/coc-rust-analyzer[coc-rust-analyzer], - this extension implements _most_ of the features supported in the VSCode extension: - * automatically install and upgrade stable/nightly releases - * same configurations as VSCode extension, `rust-analyzer.server.path`, `rust-analyzer.cargo.features` etc. - * same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.ssr` etc. - * inlay hints for variables and method chaining, _Neovim Only_ - -Note: for code actions, use `coc-codeaction-cursor` and `coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line` are unlikely to be useful. - -==== LanguageClient-neovim - -1. Install LanguageClient-neovim by following the instructions - https://github.com/autozimu/LanguageClient-neovim[here] - * The GitHub project wiki has extra tips on configuration - -2. Configure by adding this to your Vim/Neovim config file (replacing the existing Rust-specific line if it exists): -+ -[source,vim] ----- -let g:LanguageClient_serverCommands = { -\ 'rust': ['rust-analyzer'], -\ } ----- - -==== YouCompleteMe - -Install YouCompleteMe by following the instructions - https://github.com/ycm-core/YouCompleteMe#installation[here]. - -rust-analyzer is the default in ycm, it should work out of the box. - -==== ALE - -To use the LSP server in https://github.com/dense-analysis/ale[ale]: - -[source,vim] ----- -let g:ale_linters = {'rust': ['analyzer']} ----- - -==== nvim-lsp - -Neovim 0.5 has built-in language server support. -For a quick start configuration of rust-analyzer, use https://github.com/neovim/nvim-lspconfig#rust_analyzer[neovim/nvim-lspconfig]. -Once `neovim/nvim-lspconfig` is installed, use `+lua require'lspconfig'.rust_analyzer.setup({})+` in your `init.vim`. - -You can also pass LSP settings to the server: - -[source,vim] ----- -lua << EOF -local lspconfig = require'lspconfig' - -local on_attach = function(client) - require'completion'.on_attach(client) -end - -lspconfig.rust_analyzer.setup({ - on_attach = on_attach, - settings = { - ["rust-analyzer"] = { - imports = { - granularity = { - group = "module", - }, - prefix = "self", - }, - cargo = { - buildScripts = { - enable = true, - }, - }, - procMacro = { - enable = true - }, - } - } -}) -EOF ----- - -If you're running Neovim 0.10 or later, you can enable inlay hints via `on_attach`: - -[source,vim] ----- -lspconfig.rust_analyzer.setup({ - on_attach = function(client, bufnr) - vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) - end -}) ----- - -Note that the hints are only visible after `rust-analyzer` has finished loading **and** you have to edit the file to trigger a re-render. - -See https://sharksforarms.dev/posts/neovim-rust/ for more tips on getting started. - -Check out https://github.com/mrcjkb/rustaceanvim for a batteries included rust-analyzer setup for Neovim. - -==== vim-lsp - -vim-lsp is installed by following https://github.com/prabirshrestha/vim-lsp[the plugin instructions]. -It can be as simple as adding this line to your `.vimrc`: - -[source,vim] ----- -Plug 'prabirshrestha/vim-lsp' ----- - -Next you need to register the `rust-analyzer` binary. -If it is available in `$PATH`, you may want to add this to your `.vimrc`: - -[source,vim] ----- -if executable('rust-analyzer') - au User lsp_setup call lsp#register_server({ - \ 'name': 'Rust Language Server', - \ 'cmd': {server_info->['rust-analyzer']}, - \ 'whitelist': ['rust'], - \ }) -endif ----- - -There is no dedicated UI for the server configuration, so you would need to send any options as a value of the `initialization_options` field, as described in the <> section. -Here is an example of how to enable the proc-macro support: - -[source,vim] ----- -if executable('rust-analyzer') - au User lsp_setup call lsp#register_server({ - \ 'name': 'Rust Language Server', - \ 'cmd': {server_info->['rust-analyzer']}, - \ 'whitelist': ['rust'], - \ 'initialization_options': { - \ 'cargo': { - \ 'buildScripts': { - \ 'enable': v:true, - \ }, - \ }, - \ 'procMacro': { - \ 'enable': v:true, - \ }, - \ }, - \ }) -endif ----- - -=== Sublime Text - -==== Sublime Text 4: -* Follow the instructions in link:https://github.com/sublimelsp/LSP-rust-analyzer[LSP-rust-analyzer]. - -NOTE: Install link:https://packagecontrol.io/packages/LSP-file-watcher-chokidar[LSP-file-watcher-chokidar] to enable file watching (`workspace/didChangeWatchedFiles`). - -==== Sublime Text 3: -* Install the <>. -* Install the link:https://packagecontrol.io/packages/LSP[LSP package]. -* From the command palette, run `LSP: Enable Language Server Globally` and select `rust-analyzer`. - -If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side of the status bar, and after waiting a bit, functionalities like tooltips on hovering over variables should become available. - -If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <> section on installing the language server binary. - -=== GNOME Builder - -GNOME Builder 3.37.1 and newer has native `rust-analyzer` support. -If the LSP binary is not available, GNOME Builder can install it when opening a Rust file. - - -=== Eclipse IDE - -Support for Rust development in the Eclipse IDE is provided by link:https://github.com/eclipse/corrosion[Eclipse Corrosion]. -If available in PATH or in some standard location, `rust-analyzer` is detected and powers editing of Rust files without further configuration. -If `rust-analyzer` is not detected, Corrosion will prompt you for configuration of your Rust toolchain and language server with a link to the __Window > Preferences > Rust__ preference page; from here a button allows to download and configure `rust-analyzer`, but you can also reference another installation. -You'll need to close and reopen all .rs and Cargo files, or to restart the IDE, for this change to take effect. - -=== Kate Text Editor - -Support for the language server protocol is built into Kate through the LSP plugin, which is included by default. -It is preconfigured to use rust-analyzer for Rust sources since Kate 21.12. - -To change rust-analyzer config options, start from the following example and put it into Kate's "User Server Settings" tab (located under the LSP Client settings): -[source,json] ----- -{ - "servers": { - "rust": { - "initializationOptions": { - "cachePriming": { - "enable": false - }, - "check": { - "allTargets": false - }, - "checkOnSave": false - } - } - } -} ----- -Then click on apply, and restart the LSP server for your rust project. - -=== juCi++ - -https://gitlab.com/cppit/jucipp[juCi++] has built-in support for the language server protocol, and since version 1.7.0 offers installation of both Rust and rust-analyzer when opening a Rust file. - -=== Kakoune - -https://kakoune.org/[Kakoune] supports LSP with the help of https://github.com/kak-lsp/kak-lsp[`kak-lsp`]. -Follow the https://github.com/kak-lsp/kak-lsp#installation[instructions] to install `kak-lsp`. -To configure `kak-lsp`, refer to the https://github.com/kak-lsp/kak-lsp#configuring-kak-lsp[configuration section] which is basically about copying the https://github.com/kak-lsp/kak-lsp/blob/master/kak-lsp.toml[configuration file] in the right place (latest versions should use `rust-analyzer` by default). - -Finally, you need to configure Kakoune to talk to `kak-lsp` (see https://github.com/kak-lsp/kak-lsp#usage[Usage section]). -A basic configuration will only get you LSP but you can also activate inlay diagnostics and auto-formatting on save. -The following might help you get all of this. - -[source,txt] ----- -eval %sh{kak-lsp --kakoune -s $kak_session} # Not needed if you load it with plug.kak. -hook global WinSetOption filetype=rust %{ - # Enable LSP - lsp-enable-window - - # Auto-formatting on save - hook window BufWritePre .* lsp-formatting-sync - - # Configure inlay hints (only on save) - hook window -group rust-inlay-hints BufWritePost .* rust-analyzer-inlay-hints - hook -once -always window WinSetOption filetype=.* %{ - remove-hooks window rust-inlay-hints - } -} ----- - -=== Helix - -https://docs.helix-editor.com/[Helix] supports LSP by default. -However, it won't install `rust-analyzer` automatically. -You can follow instructions for installing <>. - -[#visual-studio] -=== [[visual-studio-2022]]Visual Studio 2022 - -There are multiple rust-analyzer extensions for Visual Studio 2022 on Windows: - -==== rust-analyzer.vs - -(License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International) - -https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer[Visual Studio Marketplace] - -https://github.com/kitamstudios/rust-analyzer/[GitHub] - -Support for Rust development in the Visual Studio IDE is enabled by the link:https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer[rust-analyzer] package. Either click on the download link or install from IDE's extension manager. -For now link:https://visualstudio.microsoft.com/downloads/[Visual Studio 2022] is required. All editions are supported viz. Community, Professional & Enterprise. -The package aims to provide 0-friction installation and therefore comes loaded with most things required including rust-analyzer binary. If anything it needs is missing, appropriate errors / warnings will guide the user. E.g. cargo.exe needs to be in path and the package will tell you as much. -This package is under rapid active development. So if you encounter any issues please file it at link:https://github.com/kitamstudios/rust-analyzer/[rust-analyzer.vs]. - -==== VS_RustAnalyzer - -(License: GPL) - -https://marketplace.visualstudio.com/items?itemName=cchharris.vsrustanalyzer[Visual Studio Marketplace] - -https://github.com/cchharris/VS-RustAnalyzer[GitHub] - -==== SourceGear Rust - -(License: closed source) - -https://marketplace.visualstudio.com/items?itemName=SourceGear.SourceGearRust[Visual Studio Marketplace] - -https://github.com/sourcegear/rust-vs-extension[GitHub (docs, issues, discussions)] - -* Free (no-cost) -* Supports all editions of Visual Studio 2022 on Windows: Community, Professional, or Enterprise - -=== Lapce - -https://lapce.dev/[Lapce] has a Rust plugin which you can install directly. -Unfortunately, it downloads an old version of `rust-analyzer`, but you can set the server path under Settings. - -=== Crates - -There is a package named `ra_ap_rust_analyzer` available on https://crates.io/crates/ra_ap_rust-analyzer[crates.io], for someone who wants to use it programmatically. - -For more details, see https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/autopublish.yaml[the publish workflow]. - -=== Zed - -https://zed.dev[Zed] has native `rust-analyzer` support. -If the LSP binary is not available, Zed can install it when opening a Rust file. - -== Troubleshooting - -Start with looking at the rust-analyzer version. -Try **rust-analyzer: Show RA Version** in VS Code (using **Command Palette** feature typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the command line. -If the date is more than a week ago, it's better to update rust-analyzer version. - -The next thing to check would be panic messages in rust-analyzer's log. -Log messages are printed to stderr, in VS Code you can see them in the `Output > Rust Analyzer Language Server` tab of the panel. -To see more logs, set the `RA_LOG=info` environment variable, this can be done either by setting the environment variable manually or by using `rust-analyzer.server.extraEnv`, note that both of these approaches require the server to be restarted. - -To fully capture LSP messages between the editor and the server, run the `rust-analyzer: Toggle LSP Logs` command and check -`Output > Rust Analyzer Language Server Trace`. - -The root cause for many "`nothing works`" problems is that rust-analyzer fails to understand the project structure. -To debug that, first note the `rust-analyzer` section in the status bar. -If it has an error icon and red, that's the problem (hover will have somewhat helpful error message). -**rust-analyzer: Status** prints dependency information for the current file. -Finally, `RA_LOG=project_model=debug` enables verbose logs during project loading. - -If rust-analyzer outright crashes, try running `rust-analyzer analysis-stats /path/to/project/directory/` on the command line. -This command type checks the whole project in batch mode bypassing LSP machinery. - -When filing issues, it is useful (but not necessary) to try to minimize examples. -An ideal bug reproduction looks like this: - -```bash -$ git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash -$ rust-analyzer --version -rust-analyzer dd12184e4 2021-05-08 dev -$ rust-analyzer analysis-stats . -💀 💀 💀 -``` - -It is especially useful when the `repo` doesn't use external crates or the standard library. - -If you want to go as far as to modify the source code to debug the problem, be sure to take a look at the -https://github.com/rust-lang/rust-analyzer/tree/master/docs/dev[dev docs]! - -== Configuration - -**Source:** https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/config.rs[config.rs] - -The <> section contains details on configuration for some of the editors. -In general `rust-analyzer` is configured via LSP messages, which means that it's up to the editor to decide on the exact format and location of configuration files. - -Some clients, such as <> or <> provide `rust-analyzer` specific configuration UIs. Others may require you to know a bit more about the interaction with `rust-analyzer`. - -For the later category, it might help to know that the initial configuration is specified as a value of the `initializationOptions` field of the https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize[`InitializeParams` message, in the LSP protocol]. -The spec says that the field type is `any?`, but `rust-analyzer` is looking for a JSON object that is constructed using settings from the list below. -Name of the setting, ignoring the `rust-analyzer.` prefix, is used as a path, and value of the setting becomes the JSON property value. - -For example, a very common configuration is to enable proc-macro support, can be achieved by sending this JSON: - -[source,json] ----- -{ - "cargo": { - "buildScripts": { - "enable": true, - }, - }, - "procMacro": { - "enable": true, - } -} ----- - -Please consult your editor's documentation to learn more about how to configure https://microsoft.github.io/language-server-protocol/[LSP servers]. - -To verify which configuration is actually used by `rust-analyzer`, set `RA_LOG` environment variable to `rust_analyzer=info` and look for config-related messages. -Logs should show both the JSON that `rust-analyzer` sees as well as the updated config. - -This is the list of config options `rust-analyzer` supports: - -include::./generated_config.adoc[] - -== Non-Cargo Based Projects - -rust-analyzer does not require Cargo. -However, if you use some other build system, you'll have to describe the structure of your project for rust-analyzer in the `rust-project.json` format: - -[source,TypeScript] ----- -interface JsonProject { - /// Path to the sysroot directory. - /// - /// The sysroot is where rustc looks for the - /// crates that are built-in to rust, such as - /// std. - /// - /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root - /// - /// To see the current value of sysroot, you - /// can query rustc: - /// - /// ``` - /// $ rustc --print sysroot - /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin - /// ``` - sysroot?: string; - /// Path to the directory with *source code* of - /// sysroot crates. - /// - /// By default, this is `lib/rustlib/src/rust/library` - /// relative to the sysroot. - /// - /// It should point to the directory where std, - /// core, and friends can be found: - /// - /// https://github.com/rust-lang/rust/tree/master/library. - /// - /// If provided, rust-analyzer automatically adds - /// dependencies on sysroot crates. Conversely, - /// if you omit this path, you can specify sysroot - /// dependencies yourself and, for example, have - /// several different "sysroots" in one graph of - /// crates. - sysroot_src?: string; - /// List of groups of common cfg values, to allow - /// sharing them between crates. - /// - /// Maps from group name to its cfgs. Cfg follow - /// the same format as `Crate.cfg`. - cfg_groups?: { [key: string]: string[]; }; - /// The set of crates comprising the current - /// project. Must include all transitive - /// dependencies as well as sysroot crate (libstd, - /// libcore and such). - crates: Crate[]; - /// Configuration for CLI commands. - /// - /// These are used for running and debugging binaries - /// and tests without encoding build system-specific - /// knowledge into rust-analyzer. - /// - /// # Example - /// - /// Below is an example of a test runnable. `{label}` and `{test_id}` - /// are explained in `Runnable::args`'s documentation below. - /// - /// ```json - /// { - /// "program": "buck", - /// "args": [ - /// "test", - /// "{label}", - /// "--", - /// "{test_id}", - /// "--print-passing-details" - /// ], - /// "cwd": "/home/user/repo-root/", - /// "kind": "testOne" - /// } - /// ``` - runnables?: Runnable[]; -} - -interface Crate { - /// Optional crate name used for display purposes, - /// without affecting semantics. See the `deps` - /// key for semantically-significant crate names. - display_name?: string; - /// Path to the root module of the crate. - root_module: string; - /// Edition of the crate. - edition: '2015' | '2018' | '2021' | '2024'; - /// The version of the crate. Used for calculating - /// the correct docs.rs URL. - version?: string; - /// Dependencies - deps: Dep[]; - /// Should this crate be treated as a member of - /// current "workspace". - /// - /// By default, inferred from the `root_module` - /// (members are the crates which reside inside - /// the directory opened in the editor). - /// - /// Set this to `false` for things like standard - /// library and 3rd party crates to enable - /// performance optimizations (rust-analyzer - /// assumes that non-member crates don't change). - is_workspace_member?: boolean; - /// Optionally specify the (super)set of `.rs` - /// files comprising this crate. - /// - /// By default, rust-analyzer assumes that only - /// files under `root_module.parent` can belong - /// to a crate. `include_dirs` are included - /// recursively, unless a subdirectory is in - /// `exclude_dirs`. - /// - /// Different crates can share the same `source`. - /// - /// If two crates share an `.rs` file in common, - /// they *must* have the same `source`. - /// rust-analyzer assumes that files from one - /// source can't refer to files in another source. - source?: { - include_dirs: string[]; - exclude_dirs: string[]; - }; - /// List of cfg groups this crate inherits. - /// - /// All cfg in these groups will be concatenated to - /// `cfg`. It is impossible to replace a value from - /// the groups. - cfg_groups?: string[]; - /// The set of cfgs activated for a given crate, like - /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`. - cfg: string[]; - /// Target tuple for this Crate. - /// - /// Used when running `rustc --print cfg` - /// to get target-specific cfgs. - target?: string; - /// Environment variables, used for - /// the `env!` macro - env: { [key: string]: string; }; - - /// Whether the crate is a proc-macro crate. - is_proc_macro: boolean; - /// For proc-macro crates, path to compiled - /// proc-macro (.so file). - proc_macro_dylib_path?: string; - - /// Repository, matching the URL that would be used - /// in Cargo.toml. - repository?: string; - - /// Build-specific data about this crate. - build?: BuildInfo; -} - -interface Dep { - /// Index of a crate in the `crates` array. - crate: number; - /// Name as should appear in the (implicit) - /// `extern crate name` declaration. - name: string; -} - -interface BuildInfo { - /// The name associated with this crate. - /// - /// This is determined by the build system that produced - /// the `rust-project.json` in question. For instance, if buck were used, - /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`. - /// - /// Do not attempt to parse the contents of this string; it is a build system-specific - /// identifier similar to `Crate::display_name`. - label: string; - /// Path corresponding to the build system-specific file defining the crate. - build_file: string; - /// The kind of target. - /// - /// This information is used to determine what sort - /// of runnable codelens to provide, if any. - target_kind: 'bin' | 'lib' | 'test'; -} - -interface Runnable { - /// The program invoked by the runnable. - /// - /// For example, this might be `cargo`, `buck`, or `bazel`. - program: string; - /// The arguments passed to `program`. - args: string[]; - /// The current working directory of the runnable. - cwd: string; - /// Used to decide what code lens to offer. - /// - /// `testOne`: This runnable will be used when the user clicks the 'Run Test' - /// CodeLens above a test. - /// - /// The args for testOne can contain two template strings: - /// `{label}` and `{test_id}`. `{label}` will be replaced - /// with the `Build::label` and `{test_id}` will be replaced - /// with the test name. - kind: 'testOne' | string; -} ----- - -This format is provisional and subject to change. -Specifically, the `roots` setup will be different eventually. - -There are three ways to feed `rust-project.json` to rust-analyzer: - -* Place `rust-project.json` file at the root of the project, and rust-analyzer will discover it. -* Specify `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in the settings (and make sure that your LSP client sends settings as a part of initialize request). -* Specify `"rust-analyzer.linkedProjects": [ { "roots": [...], "crates": [...] }]` inline. - -Relative paths are interpreted relative to `rust-project.json` file location or (for inline JSON) relative to `rootUri`. - -You can set the `RA_LOG` environment variable to `rust_analyzer=info` to inspect how rust-analyzer handles config and project loading. - -Note that calls to `cargo check` are disabled when using `rust-project.json` by default, so compilation errors and warnings will no longer be sent to your LSP client. -To enable these compilation errors you will need to specify explicitly what command rust-analyzer should run to perform the checks using the `rust-analyzer.check.overrideCommand` configuration. -As an example, the following configuration explicitly sets `cargo check` as the `check` command. - -[source,json] ----- -{ "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] } ----- - -`check.overrideCommand` requires the command specified to output json error messages for rust-analyzer to consume. -The `--message-format=json` flag does this for `cargo check` so whichever command you use must also output errors in this format. -See the <> section for more information. - -== Security - -At the moment, rust-analyzer assumes that all code is trusted. -Here is a **non-exhaustive** list of ways to make rust-analyzer execute arbitrary code: - -* proc macros and build scripts are executed by default -* `.cargo/config` can override `rustc` with an arbitrary executable -* `rust-toolchain.toml` can override `rustc` with an arbitrary executable -* VS Code plugin reads configuration from project directory, and that can be used to override paths to various executables, like `rustfmt` or `rust-analyzer` itself. -* rust-analyzer's syntax trees library uses a lot of `unsafe` and hasn't been properly audited for memory safety. - -== Privacy - -The LSP server performs no network access in itself, but runs `cargo metadata` which will update or download the crate registry and the source code of the project dependencies. -If enabled (the default), build scripts and procedural macros can do anything. - -The Code extension does not access the network. - -Any other editor plugins are not under the control of the `rust-analyzer` developers. For any privacy concerns, you should check with their respective developers. - -For `rust-analyzer` developers, `cargo xtask release` uses the GitHub API to put together the release notes. - -== Features - -include::./generated_features.adoc[] - -== Assists (Code Actions) - -Assists, or code actions, are small local refactorings, available in a particular context. -They are usually triggered by a shortcut or by clicking a light bulb icon in the editor. -Cursor position or selection is signified by `┃` character. - -include::./generated_assists.adoc[] - -== Diagnostics - -While most errors and warnings provided by rust-analyzer come from the `cargo check` integration, there's a growing number of diagnostics implemented using rust-analyzer's own analysis. -Some of these diagnostics don't respect `\#[allow]` or `\#[deny]` attributes yet, but can be turned off using the `rust-analyzer.diagnostics.enable`, `rust-analyzer.diagnostics.experimental.enable` or `rust-analyzer.diagnostics.disabled` settings. - -=== Clippy - -To run `cargo clippy` instead of `cargo check`, you can set `"rust-analyzer.check.command": "clippy"`. - -include::./generated_diagnostic.adoc[] - -== Editor Features -=== VS Code - -==== Color configurations - -It is possible to change the foreground/background color and font family/size of inlay hints. -Just add this to your `settings.json`: - -[source,jsonc] ----- -{ - "editor.inlayHints.fontFamily": "Courier New", - "editor.inlayHints.fontSize": 11, - - "workbench.colorCustomizations": { - // Name of the theme you are currently using - "[Default Dark+]": { - "editorInlayHint.foreground": "#868686f0", - "editorInlayHint.background": "#3d3d3d48", - - // Overrides for specific kinds of inlay hints - "editorInlayHint.typeForeground": "#fdb6fdf0", - "editorInlayHint.parameterForeground": "#fdb6fdf0", - } - } -} ----- - -==== Semantic style customizations - -You can customize the look of different semantic elements in the source code. -For example, mutable bindings are underlined by default and you can override this behavior by adding the following section to your `settings.json`: - -[source,jsonc] ----- -{ - "editor.semanticTokenColorCustomizations": { - "rules": { - "*.mutable": { - "fontStyle": "", // underline is the default - }, - } - }, -} ----- - -Most themes doesn't support styling unsafe operations differently yet. You can fix this by adding overrides for the rules `operator.unsafe`, `function.unsafe`, and `method.unsafe`: - -[source,jsonc] ----- -{ - "editor.semanticTokenColorCustomizations": { - "rules": { - "operator.unsafe": "#ff6600", - "function.unsafe": "#ff6600", - "method.unsafe": "#ff6600" - } - }, -} ----- - -In addition to the top-level rules you can specify overrides for specific themes. For example, if you wanted to use a darker text color on a specific light theme, you might write: - -[source,jsonc] ----- -{ - "editor.semanticTokenColorCustomizations": { - "rules": { - "operator.unsafe": "#ff6600" - }, - "[Ayu Light]": { - "rules": { - "operator.unsafe": "#572300" - } - } - }, -} ----- - -Make sure you include the brackets around the theme name. For example, use `"[Ayu Light]"` to customize the theme Ayu Light. - -==== Special `when` clause context for keybindings. -You may use `inRustProject` context to configure keybindings for rust projects only. -For example: - -[source,json] ----- -{ - "key": "ctrl+alt+d", - "command": "rust-analyzer.openDocs", - "when": "inRustProject" -} ----- -More about `when` clause contexts https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts[here]. - -==== Setting runnable environment variables -You can use "rust-analyzer.runnables.extraEnv" setting to define runnable environment-specific substitution variables. -The simplest way for all runnables in a bunch: -```jsonc -"rust-analyzer.runnables.extraEnv": { - "RUN_SLOW_TESTS": "1" -} -``` - -Or it is possible to specify vars more granularly: -```jsonc -"rust-analyzer.runnables.extraEnv": [ - { - // "mask": null, // null mask means that this rule will be applied for all runnables - "env": { - "APP_ID": "1", - "APP_DATA": "asdf" - } - }, - { - "mask": "test_name", - "env": { - "APP_ID": "2", // overwrites only APP_ID - } - } -] -``` - -You can use any valid regular expression as a mask. -Also note that a full runnable name is something like *run bin_or_example_name*, *test some::mod::test_name* or *test-mod some::mod*, so it is possible to distinguish binaries, single tests, and test modules with this masks: `"^run"`, `"^test "` (the trailing space matters!), and `"^test-mod"` respectively. - -If needed, you can set different values for different platforms: -```jsonc -"rust-analyzer.runnables.extraEnv": [ - { - "platform": "win32", // windows only - "env": { - "APP_DATA": "windows specific data" - } - }, - { - "platform": ["linux"], - "env": { - "APP_DATA": "linux data", - } - }, - { // for all platforms - "env": { - "APP_COMMON_DATA": "xxx", - } - } -] -``` - -==== Compiler feedback from external commands - -Instead of relying on the built-in `cargo check`, you can configure Code to run a command in the background and use the `$rustc-watch` problem matcher to generate inline error markers from its output. - -To do this you need to create a new https://code.visualstudio.com/docs/editor/tasks[VS Code Task] and set `"rust-analyzer.checkOnSave": false` in preferences. - -For example, if you want to run https://crates.io/crates/cargo-watch[`cargo watch`] instead, you might add the following to `.vscode/tasks.json`: - -```json -{ - "label": "Watch", - "group": "build", - "type": "shell", - "command": "cargo watch", - "problemMatcher": "$rustc-watch", - "isBackground": true -} -``` - -==== Live Share - -VS Code Live Share has partial support for rust-analyzer. - -Live Share _requires_ the official Microsoft build of VS Code, OSS builds will not work correctly. - -The host's rust-analyzer instance will be shared with all guests joining the session. -The guests do not have to have the rust-analyzer extension installed for this to work. - -If you are joining a Live Share session and _do_ have rust-analyzer installed locally, commands from the command palette will not work correctly since they will attempt to communicate with the local server. diff --git a/src/tools/rust-analyzer/xtask/src/codegen.rs b/src/tools/rust-analyzer/xtask/src/codegen.rs index bc04b9474f26..18f49643dc71 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen.rs @@ -24,6 +24,7 @@ impl flags::Codegen { diagnostics_docs::generate(self.check); assists_doc_tests::generate(self.check); parser_inline_tests::generate(self.check); + feature_docs::generate(self.check) // diagnostics_docs::generate(self.check) doesn't generate any tests // lints::generate(self.check) Updating clones the rust repo, so don't run it unless // explicitly asked for @@ -116,13 +117,7 @@ impl fmt::Display for Location { let path = self.file.strip_prefix(project_root()).unwrap().display().to_string(); let path = path.replace('\\', "/"); let name = self.file.file_name().unwrap(); - write!( - f, - "https://github.com/rust-lang/rust-analyzer/blob/master/{}#L{}[{}]", - path, - self.line, - name.to_str().unwrap() - ) + write!(f, " [{}]({}#{}) ", name.to_str().unwrap(), path, self.line) } } @@ -162,7 +157,7 @@ fn reformat(text: String) -> String { } fn add_preamble(cg: CodegenType, mut text: String) -> String { - let preamble = format!("//! Generated by `cargo codegen {cg}`, do not edit by hand.\n\n"); + let preamble = format!("//! Generated by `cargo xtask codegen {cg}`, do not edit by hand.\n\n"); text.insert_str(0, &preamble); text } @@ -186,7 +181,7 @@ fn ensure_file_contents(cg: CodegenType, file: &Path, contents: &str, check: boo file.display(), if std::env::var("CI").is_ok() { format!( - "\n NOTE: run `cargo codegen {cg}` locally and commit the updated files\n" + "\n NOTE: run `cargo xtask codegen {cg}` locally and commit the updated files\n" ) } else { "".to_owned() diff --git a/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs b/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs index d06c9d65df32..0bb18c73cfc6 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs @@ -1,4 +1,4 @@ -//! Generates `assists.md` documentation. +//! Generates `assists_generated.md` documentation. use std::{fmt, fs, path::Path}; @@ -62,7 +62,7 @@ r#####" crate::flags::CodegenType::AssistsDocTests, assists.into_iter().map(|it| it.to_string()).collect::>().join("\n\n"), ); - let dst = project_root().join("docs/user/generated_assists.adoc"); + let dst = project_root().join("docs/book/src/assists_generated.md"); fs::write(dst, contents).unwrap(); } } @@ -146,7 +146,7 @@ impl fmt::Display for Assist { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let _ = writeln!( f, - "[discrete]\n=== `{}` + "### `{}` **Source:** {}", self.id, self.location, ); @@ -159,11 +159,11 @@ impl fmt::Display for Assist { " {} -.Before +#### Before ```rust {}``` -.After +#### After ```rust {}```", section.doc, diff --git a/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs b/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs index 4cb8f3f259d7..cf8f97be009b 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs @@ -1,4 +1,4 @@ -//! Generates `assists.md` documentation. +//! Generates `diagnostics_generated.md` documentation. use std::{fmt, fs, io, path::PathBuf}; @@ -14,7 +14,7 @@ pub(crate) fn generate(check: bool) { let contents = diagnostics.into_iter().map(|it| it.to_string()).collect::>().join("\n\n"); let contents = add_preamble(crate::flags::CodegenType::DiagnosticsDocs, contents); - let dst = project_root().join("docs/user/generated_diagnostic.adoc"); + let dst = project_root().join("docs/book/src/diagnostics_generated.md"); fs::write(dst, contents).unwrap(); } } @@ -73,6 +73,6 @@ fn is_valid_diagnostic_name(diagnostic: &str) -> Result<(), String> { impl fmt::Display for Diagnostic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) + writeln!(f, "#### {}\n\nSource: {}\n\n{}\n\n", self.id, self.location, self.doc) } } diff --git a/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs b/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs index c6451d888b03..51ff13aba81e 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs @@ -1,9 +1,9 @@ -//! Generates `assists.md` documentation. +//! Generates `features_generated.md` documentation. use std::{fmt, fs, io, path::PathBuf}; use crate::{ - codegen::{CommentBlock, Location}, + codegen::{add_preamble, CommentBlock, Location}, project_root, util::list_rust_files, }; @@ -11,14 +11,8 @@ use crate::{ pub(crate) fn generate(_check: bool) { let features = Feature::collect().unwrap(); let contents = features.into_iter().map(|it| it.to_string()).collect::>().join("\n\n"); - let contents = format!( - " -// Generated file, do not edit by hand, see `sourcegen_feature_docs`. -{} -", - contents.trim() - ); - let dst = project_root().join("docs/user/generated_features.adoc"); + let contents = add_preamble(crate::flags::CodegenType::FeatureDocs, contents); + let dst = project_root().join("docs/book/src/features_generated.md"); fs::write(dst, contents).unwrap(); } @@ -80,6 +74,6 @@ fn is_valid_feature_name(feature: &str) -> Result<(), String> { impl fmt::Display for Feature { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) + writeln!(f, "### {}\n**Source:** {}\n{}", self.id, self.location, self.doc) } } diff --git a/src/tools/rust-analyzer/xtask/src/release.rs b/src/tools/rust-analyzer/xtask/src/release.rs index 1a1364c7d10e..8e56ce439c55 100644 --- a/src/tools/rust-analyzer/xtask/src/release.rs +++ b/src/tools/rust-analyzer/xtask/src/release.rs @@ -9,7 +9,7 @@ use directories::ProjectDirs; use stdx::JodChild; use xshell::{cmd, Shell}; -use crate::{codegen, date_iso, flags, is_release_tag, project_root}; +use crate::{date_iso, flags, is_release_tag, project_root}; impl flags::Release { pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { @@ -29,11 +29,6 @@ impl flags::Release { cmd!(sh, "git push --force").run()?; } - // Generates bits of manual.adoc. - codegen::diagnostics_docs::generate(false); - codegen::assists_doc_tests::generate(false); - codegen::feature_docs::generate(false); - let website_root = project_root().join("../rust-analyzer.github.io"); { let _dir = sh.push_dir(&website_root); @@ -54,20 +49,6 @@ impl flags::Release { .max() .unwrap_or_default(); - for adoc in [ - "manual.adoc", - "generated_assists.adoc", - "generated_config.adoc", - "generated_diagnostic.adoc", - "generated_features.adoc", - ] { - let src = project_root().join("./docs/user/").join(adoc); - let dst = website_root.join(adoc); - - let contents = sh.read_file(src)?; - sh.write_file(dst, contents)?; - } - let tags = cmd!(sh, "git tag --list").read()?; let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); From 2ecb40e04a3ff8aeefc470598d8ea51edc197b26 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Tue, 14 Jan 2025 18:13:52 +0300 Subject: [PATCH 086/285] Stabilize `vec_pop_if` --- library/alloc/src/lib.rs | 1 - library/alloc/src/vec/mod.rs | 4 +--- library/alloc/tests/lib.rs | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 28e4217e3039..494f2b7aadb4 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -155,7 +155,6 @@ #![feature(unicode_internals)] #![feature(unsize)] #![feature(unwrap_infallible)] -#![feature(vec_pop_if)] // tidy-alphabetical-end // // Language features: diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 88bd3f414ea1..34aacd337b4f 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2518,8 +2518,6 @@ impl Vec { /// # Examples /// /// ``` - /// #![feature(vec_pop_if)] - /// /// let mut vec = vec![1, 2, 3, 4]; /// let pred = |x: &mut i32| *x % 2 == 0; /// @@ -2527,7 +2525,7 @@ impl Vec { /// assert_eq!(vec, [1, 2, 3]); /// assert_eq!(vec.pop_if(pred), None); /// ``` - #[unstable(feature = "vec_pop_if", issue = "122741")] + #[stable(feature = "vec_pop_if", since = "CURRENT_RUSTC_VERSION")] pub fn pop_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { let last = self.last_mut()?; if predicate(last) { self.pop() } else { None } diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index d8364d750fa8..733b2508f668 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -37,7 +37,6 @@ #![feature(local_waker)] #![feature(str_as_str)] #![feature(strict_provenance_lints)] -#![feature(vec_pop_if)] #![feature(vec_deque_pop_if)] #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] From b46775a5f4e48ea740fd34f9d69bc5bce05cb88e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Jan 2025 14:27:47 +0100 Subject: [PATCH 087/285] Combine `pat_hyigene` and `expr_hygiene` --- .../rust-analyzer/crates/hir-def/src/body.rs | 20 ++++++++----------- .../crates/hir-def/src/body/lower.rs | 6 +++--- .../rust-analyzer/crates/hir-def/src/hir.rs | 2 ++ 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index de4392493063..139e1ba896ce 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -3,6 +3,7 @@ mod lower; mod pretty; pub mod scope; + #[cfg(test)] mod tests; @@ -92,11 +93,9 @@ pub struct Body { binding_hygiene: FxHashMap, /// A map from an variable usages to their hygiene ID. /// - /// Expressions that can be recorded here are single segment path, although not all single segments path refer + /// Expressions (and destructuing patterns) that can be recorded here are single segment path, although not all single segments path refer /// to variables and have hygiene (some refer to items, we don't know at this stage). - expr_hygiene: FxHashMap, - /// A map from a destructuring assignment possible variable usages to their hygiene ID. - pat_hygiene: FxHashMap, + ident_hygiene: FxHashMap, } pub type ExprPtr = AstPtr; @@ -317,8 +316,7 @@ impl Body { bindings, binding_owners, binding_hygiene, - expr_hygiene, - pat_hygiene, + ident_hygiene, types, } = self; block_scopes.shrink_to_fit(); @@ -328,8 +326,7 @@ impl Body { bindings.shrink_to_fit(); binding_owners.shrink_to_fit(); binding_hygiene.shrink_to_fit(); - expr_hygiene.shrink_to_fit(); - pat_hygiene.shrink_to_fit(); + ident_hygiene.shrink_to_fit(); types.shrink_to_fit(); } @@ -658,11 +655,11 @@ impl Body { } pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId { - self.expr_hygiene.get(&expr).copied().unwrap_or(HygieneId::ROOT) + self.ident_hygiene.get(&expr.into()).copied().unwrap_or(HygieneId::ROOT) } pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId { - self.pat_hygiene.get(&pat).copied().unwrap_or(HygieneId::ROOT) + self.ident_hygiene.get(&pat.into()).copied().unwrap_or(HygieneId::ROOT) } pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId { @@ -686,8 +683,7 @@ impl Default for Body { binding_owners: Default::default(), self_param: Default::default(), binding_hygiene: Default::default(), - expr_hygiene: Default::default(), - pat_hygiene: Default::default(), + ident_hygiene: Default::default(), types: Default::default(), } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 16c7b5ca00a0..583c6ac5e8c7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -480,7 +480,7 @@ impl ExprCollector<'_> { .unwrap_or((Expr::Missing, HygieneId::ROOT)); let expr_id = self.alloc_expr(path, syntax_ptr); if !hygiene.is_root() { - self.body.expr_hygiene.insert(expr_id, hygiene); + self.body.ident_hygiene.insert(expr_id.into(), hygiene); } expr_id } @@ -835,7 +835,7 @@ impl ExprCollector<'_> { .unwrap_or((Pat::Missing, HygieneId::ROOT)); let pat_id = self.alloc_pat_from_expr(path, syntax_ptr); if !hygiene.is_root() { - self.body.pat_hygiene.insert(pat_id, hygiene); + self.body.ident_hygiene.insert(pat_id.into(), hygiene); } pat_id } @@ -2023,7 +2023,7 @@ impl ExprCollector<'_> { ); } if !hygiene.is_root() { - self.body.expr_hygiene.insert(expr_id, hygiene); + self.body.ident_hygiene.insert(expr_id.into(), hygiene); } expr_id }, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 859634694302..9392e8d12d40 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -44,6 +44,8 @@ pub(crate) fn dummy_expr_id() -> ExprId { pub type PatId = Idx; +// FIXME: Encode this as a single u32, we won't ever reach all 32 bits especially given these counts +// are local to the body. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum ExprOrPatId { ExprId(ExprId), From 8e7a3381650dd7fc86354649a856163bcbceea52 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Jan 2025 14:43:22 +0100 Subject: [PATCH 088/285] BodyCollector --- .../rust-analyzer/crates/hir-def/src/body.rs | 112 ++++++++++-------- .../crates/hir-def/src/body/lower.rs | 49 ++++---- .../rust-analyzer/crates/hir-def/src/hir.rs | 7 +- 3 files changed, 91 insertions(+), 77 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index 139e1ba896ce..334177751e4c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -25,8 +25,8 @@ use crate::{ db::DefDatabase, expander::Expander, hir::{ - dummy_expr_id, Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, - LabelId, Pat, PatId, RecordFieldPat, Statement, + Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, + PatId, RecordFieldPat, Statement, }, item_tree::AttrOwner, nameres::DefMap, @@ -81,7 +81,7 @@ pub struct Body { pub body_expr: ExprId, pub types: TypesMap, /// Block expressions in this body that may contain inner items. - block_scopes: Vec, + block_scopes: Box<[BlockId]>, /// A map from binding to its hygiene ID. /// @@ -98,6 +98,64 @@ pub struct Body { ident_hygiene: FxHashMap, } +/// The body of an item (function, const etc.). +#[derive(Debug, Eq, PartialEq, Default)] +pub struct BodyCollector { + pub exprs: Arena, + pub pats: Arena, + pub bindings: Arena, + pub labels: Arena