diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a4ed73d..1b03709f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This release has an [MSRV][] of 1.89. ### Added - Added `Native` to `SimdFloatElement` and `SimdIntElement` to select native-width vectors in scalar-generic code. +- Added the public `ExtractToken` trait for SIMD tokens, vectors, masks, references, and user-defined wrappers. `#[simd]` now extracts its token from the first non-receiver parameter, allowing vector arguments without a separate token parameter. - Added `reverse` for all SIMD vector and mask types. ([#356][] by [@Shnatsel][]) - Added `rotate_elements_left` and `rotate_elements_right` to mask types. Rotations wrap the offset, matching the existing non-mask vector operations. ([#360][] by [@Shnatsel][]) - Added lane-wise `saturating_add` and `saturating_sub` for all integer vector types and backends. ([#352][] by [@Shnatsel][]) @@ -29,6 +30,7 @@ This release has an [MSRV][] of 1.89. ### Changed +- Breaking change: `witness()` has moved from `SimdBase` and `SimdMask` to their new `ExtractToken` supertrait and has been renamed to `token()`. - Breaking change: `SimdBase::N` and `SimdMask::N` have been renamed to `LEN`, matching the `std::simd` naming. ([#366][] by [@Shnatsel][]) - Breaking change: `SimdBase::as_array` now borrows the vector and returns an array reference, while owned extraction has moved to `to_array`. The old `as_array_ref` and `as_array_mut` methods have been replaced by `as_array` and `as_mut_array`, matching the `std::simd` API. ([#351][] by [@Shnatsel][]) - Breaking change: `abs` has moved from `SimdFloat` to `SimdBase` and is now available on integer vectors. Signed integers use wrapping absolute value, leaving the minimum representable value unchanged; unsigned integers are unchanged. ([#371][] by [@Shnatsel][]) diff --git a/fearless_simd/src/generated/avx2.rs b/fearless_simd/src/generated/avx2.rs index 994be915..446ae7e1 100644 --- a/fearless_simd/src/generated/avx2.rs +++ b/fearless_simd/src/generated/avx2.rs @@ -42,6 +42,13 @@ impl Avx2 { } } impl Seal for Avx2 {} +impl ExtractToken for Avx2 { + type S = Self; + #[inline] + fn token(&self) -> Self { + *self + } +} impl ArchTypes for Avx2 { type f32x4 = crate::support::Aligned128<__m128>; type i8x16 = crate::support::Aligned128<__m128i>; diff --git a/fearless_simd/src/generated/avx512.rs b/fearless_simd/src/generated/avx512.rs index 377c4ad7..2e35308c 100644 --- a/fearless_simd/src/generated/avx512.rs +++ b/fearless_simd/src/generated/avx512.rs @@ -52,6 +52,13 @@ impl Avx512 { } } impl Seal for Avx512 {} +impl ExtractToken for Avx512 { + type S = Self; + #[inline] + fn token(&self) -> Self { + *self + } +} impl ArchTypes for Avx512 { type f32x4 = crate::support::Aligned128<__m128>; type i8x16 = crate::support::Aligned128<__m128i>; diff --git a/fearless_simd/src/generated/fallback.rs b/fearless_simd/src/generated/fallback.rs index 8acaa2f9..f84e4dd1 100644 --- a/fearless_simd/src/generated/fallback.rs +++ b/fearless_simd/src/generated/fallback.rs @@ -124,6 +124,13 @@ impl Fallback { } } impl Seal for Fallback {} +impl ExtractToken for Fallback { + type S = Self; + #[inline] + fn token(&self) -> Self { + *self + } +} impl ArchTypes for Fallback { type f32x4 = crate::support::Aligned128<[f32; 4usize]>; type i8x16 = crate::support::Aligned128<[i8; 16usize]>; diff --git a/fearless_simd/src/generated/neon.rs b/fearless_simd/src/generated/neon.rs index 9ffac580..5d040787 100644 --- a/fearless_simd/src/generated/neon.rs +++ b/fearless_simd/src/generated/neon.rs @@ -37,6 +37,13 @@ impl Neon { } } impl Seal for Neon {} +impl ExtractToken for Neon { + type S = Self; + #[inline] + fn token(&self) -> Self { + *self + } +} impl ArchTypes for Neon { type f32x4 = crate::support::Aligned128; type i8x16 = crate::support::Aligned128; diff --git a/fearless_simd/src/generated/simd_trait.rs b/fearless_simd/src/generated/simd_trait.rs index 509f2481..aab7b7cf 100644 --- a/fearless_simd/src/generated/simd_trait.rs +++ b/fearless_simd/src/generated/simd_trait.rs @@ -4,8 +4,8 @@ // This file is autogenerated by fearless_simd_gen use crate::{ - Bytes, Level, Select, SimdCvtFloat, SimdCvtTruncate, SimdElement, SimdFloatElement, SimdFrom, - SimdIntElement, SimdInto, SimdNarrow, SimdWiden, seal::Seal, + Bytes, ExtractToken, Level, Select, SimdCvtFloat, SimdCvtTruncate, SimdElement, + SimdFloatElement, SimdFrom, SimdIntElement, SimdInto, SimdNarrow, SimdWiden, seal::Seal, }; use crate::{ f32x4, f32x8, f32x16, f64x2, f64x4, f64x8, i8x16, i8x32, i8x64, i16x8, i16x16, i16x32, i32x4, @@ -50,7 +50,16 @@ use core::fmt::Debug; #[doc = r" });"] #[doc = r" ```"] pub trait Simd: - Sized + Clone + Copy + Send + Sync + Debug + Seal + arch_types::ArchTypes + 'static + ExtractToken + + Sized + + Clone + + Copy + + Send + + Sync + + Debug + + Seal + + arch_types::ArchTypes + + 'static { #[doc = r" A native-width SIMD vector of [`f32`]s."] type f32s: SimdFloat< @@ -9564,7 +9573,8 @@ pub(crate) mod arch_types { } #[doc = r" Base functionality implemented by all SIMD vectors."] pub trait SimdBase: - Copy + ExtractToken + + Copy + Sync + Send + Debug @@ -9622,8 +9632,6 @@ pub trait SimdBase: + AsRef<[Self::Element]> + AsMut<[Self::Element]> + From; - #[doc = r" Get the [`Simd`] implementation associated with this type."] - fn witness(&self) -> S; fn as_slice(&self) -> &[Self::Element]; fn as_mut_slice(&mut self) -> &mut [Self::Element]; #[doc = r" Create a SIMD vector from a slice."] @@ -9896,7 +9904,7 @@ pub trait SimdBase: #[doc = r" If `OFFSET` is greater than or equal to `Self::LEN`, all lanes are filled with `padding`."] #[inline(always)] fn shift_elements_right(self, padding: Self::Element) -> Self { - let padding = Self::splat(self.witness(), padding); + let padding = Self::splat(self.token(), padding); match Self::LEN.saturating_sub(OFFSET) { 0 => padding.slide::<0>(self), 1 => padding.slide::<1>(self), @@ -10131,7 +10139,8 @@ pub trait SimdInt: #[doc = r" current backends may use all-zero/all-one integer vectors internally, while future"] #[doc = r" predicate-register backends may use a compact representation."] pub trait SimdMask: - Copy + ExtractToken + + Copy + Sync + Send + 'static @@ -10152,8 +10161,6 @@ pub trait SimdMask: type Element: SimdElement; #[doc = r" This mask type's lane count."] const LEN: usize; - #[doc = r" Get the [`Simd`] implementation associated with this type."] - fn witness(&self) -> S; #[doc = r" Create a SIMD mask with all lanes set to the given boolean value."] fn splat(simd: S, val: bool) -> Self; #[doc = r" Create a mask from a compact bitmask."] diff --git a/fearless_simd/src/generated/simd_types.rs b/fearless_simd/src/generated/simd_types.rs index 0ad8de4a..fc9740c5 100644 --- a/fearless_simd/src/generated/simd_types.rs +++ b/fearless_simd/src/generated/simd_types.rs @@ -4,9 +4,16 @@ // This file is autogenerated by fearless_simd_gen use crate::{ - Bytes, Select, Simd, SimdBase, SimdCvtFloat, SimdCvtTruncate, SimdFrom, SimdInterleaved, - SimdInto, SimdMask, SimdNarrow, SimdWiden, seal::Seal, + Bytes, ExtractToken, Select, Simd, SimdBase, SimdCvtFloat, SimdCvtTruncate, SimdFrom, + SimdInterleaved, SimdInto, SimdMask, SimdNarrow, SimdWiden, seal::Seal, }; +impl ExtractToken for f32x4 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 4 [`f32`] elements.\n\nThis type's memory layout is identical to that of [`[f32; 4]`](prim@array), except that it is aligned to 16 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, f32x4};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = f32x4::splat(simd, 1.0);\n let b = f32x4::simd_from(simd, 1.0);\n\n // From a slice:\n let c = f32x4::from_slice(simd, &[1.0, 2.0, 3.0, 4.0]);\n\n // From an array:\n let d = f32x4::simd_from(simd, [1.0, 2.0, 3.0, 4.0]);\n\n // From an element-wise function:\n let e = f32x4::from_fn(simd, |i| i as f32);\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(16))] @@ -98,10 +105,6 @@ impl SimdBase for f32x4 { type Block = f32x4; type Array = [f32; 4]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[f32] { self.as_array().as_slice() } @@ -341,6 +344,13 @@ impl SimdInterleaved for f32x4 { vectors[0].simd.store_four_interleaved_f32x4(vectors, dest); } } +impl ExtractToken for i8x16 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 16 [`i8`] elements.\n\nThis type's memory layout is identical to that of [`[i8; 16]`](prim@array), except that it is aligned to 16 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, i8x16};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = i8x16::splat(simd, 1);\n let b = i8x16::simd_from(simd, 1);\n\n // From a slice:\n let c = i8x16::from_slice(simd, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n\n // From an array:\n let d = i8x16::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n\n // From an element-wise function:\n let e = i8x16::from_fn(simd, |i| i as i8);\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(16))] @@ -432,10 +442,6 @@ impl SimdBase for i8x16 { type Block = i8x16; type Array = [i8; 16]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[i8] { self.as_array().as_slice() } @@ -647,6 +653,13 @@ impl SimdInterleaved for i8x16 { vectors[0].simd.store_four_interleaved_i8x16(vectors, dest); } } +impl ExtractToken for u8x16 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 16 [`u8`] elements.\n\nThis type's memory layout is identical to that of [`[u8; 16]`](prim@array), except that it is aligned to 16 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, u8x16};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = u8x16::splat(simd, 1);\n let b = u8x16::simd_from(simd, 1);\n\n // From a slice:\n let c = u8x16::from_slice(simd, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n\n // From an array:\n let d = u8x16::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n\n // From an element-wise function:\n let e = u8x16::from_fn(simd, |i| i as u8);\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(16))] @@ -738,10 +751,6 @@ impl SimdBase for u8x16 { type Block = u8x16; type Array = [u8; 16]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[u8] { self.as_array().as_slice() } @@ -953,6 +962,13 @@ impl SimdInterleaved for u8x16 { vectors[0].simd.store_four_interleaved_u8x16(vectors, dest); } } +impl ExtractToken for mask8x16 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD mask of 16 logical lanes corresponding to 8-bit vector elements.\n\nThe storage representation of this type is intentionally opaque and may vary depending on the SIMD level.\n\nYou can construct this mask type using the [`Self::splat`], [`Self::from_bitmask`], [`Self::from_slice`], and [`Self::simd_from`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, mask8x16};\nfn construct_mask(simd: S) {\n // From a single boolean value:\n let a = mask8x16::splat(simd, true);\n let b = mask8x16::simd_from(simd, true);\n\n // From signed integer mask lanes:\n let c = mask8x16::from_slice(simd, &[-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n let d = mask8x16::simd_from(simd, [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n\n // From a compact bitmask (same mask as above, least significant bit maps to lane 0):\n let e = mask8x16::from_bitmask(simd, 0b0001);\n\n // By setting individual lanes:\n let mut f = mask8x16::splat(simd, false);\n f.set(0, true);\n}\n```"] #[derive(Clone, Copy)] pub struct mask8x16 { @@ -997,10 +1013,6 @@ impl SimdMask for mask8x16 { type Element = i8; const LEN: usize = 16; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.splat_mask8x16(val) } @@ -1059,6 +1071,13 @@ impl SimdMask for mask8x16 { self.simd.all_false_mask8x16(self) } } +impl ExtractToken for i16x8 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 8 [`i16`] elements.\n\nThis type's memory layout is identical to that of [`[i16; 8]`](prim@array), except that it is aligned to 16 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, i16x8};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = i16x8::splat(simd, 1);\n let b = i16x8::simd_from(simd, 1);\n\n // From a slice:\n let c = i16x8::from_slice(simd, &[1, 2, 3, 4, 5, 6, 7, 8]);\n\n // From an array:\n let d = i16x8::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8]);\n\n // From an element-wise function:\n let e = i16x8::from_fn(simd, |i| i as i16);\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(16))] @@ -1150,10 +1169,6 @@ impl SimdBase for i16x8 { type Block = i16x8; type Array = [i16; 8]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[i16] { self.as_array().as_slice() } @@ -1372,6 +1387,13 @@ impl SimdInterleaved for i16x8 { vectors[0].simd.store_four_interleaved_i16x8(vectors, dest); } } +impl ExtractToken for u16x8 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 8 [`u16`] elements.\n\nThis type's memory layout is identical to that of [`[u16; 8]`](prim@array), except that it is aligned to 16 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, u16x8};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = u16x8::splat(simd, 1);\n let b = u16x8::simd_from(simd, 1);\n\n // From a slice:\n let c = u16x8::from_slice(simd, &[1, 2, 3, 4, 5, 6, 7, 8]);\n\n // From an array:\n let d = u16x8::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8]);\n\n // From an element-wise function:\n let e = u16x8::from_fn(simd, |i| i as u16);\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(16))] @@ -1463,10 +1485,6 @@ impl SimdBase for u16x8 { type Block = u16x8; type Array = [u16; 8]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[u16] { self.as_array().as_slice() } @@ -1685,6 +1703,13 @@ impl SimdInterleaved for u16x8 { vectors[0].simd.store_four_interleaved_u16x8(vectors, dest); } } +impl ExtractToken for mask16x8 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD mask of 8 logical lanes corresponding to 16-bit vector elements.\n\nThe storage representation of this type is intentionally opaque and may vary depending on the SIMD level.\n\nYou can construct this mask type using the [`Self::splat`], [`Self::from_bitmask`], [`Self::from_slice`], and [`Self::simd_from`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, mask16x8};\nfn construct_mask(simd: S) {\n // From a single boolean value:\n let a = mask16x8::splat(simd, true);\n let b = mask16x8::simd_from(simd, true);\n\n // From signed integer mask lanes:\n let c = mask16x8::from_slice(simd, &[-1, 0, 0, 0, 0, 0, 0, 0]);\n let d = mask16x8::simd_from(simd, [-1, 0, 0, 0, 0, 0, 0, 0]);\n\n // From a compact bitmask (same mask as above, least significant bit maps to lane 0):\n let e = mask16x8::from_bitmask(simd, 0b0001);\n\n // By setting individual lanes:\n let mut f = mask16x8::splat(simd, false);\n f.set(0, true);\n}\n```"] #[derive(Clone, Copy)] pub struct mask16x8 { @@ -1729,10 +1754,6 @@ impl SimdMask for mask16x8 { type Element = i16; const LEN: usize = 8; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.splat_mask16x8(val) } @@ -1791,6 +1812,13 @@ impl SimdMask for mask16x8 { self.simd.all_false_mask16x8(self) } } +impl ExtractToken for i32x4 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 4 [`i32`] elements.\n\nThis type's memory layout is identical to that of [`[i32; 4]`](prim@array), except that it is aligned to 16 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, i32x4};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = i32x4::splat(simd, 1);\n let b = i32x4::simd_from(simd, 1);\n\n // From a slice:\n let c = i32x4::from_slice(simd, &[1, 2, 3, 4]);\n\n // From an array:\n let d = i32x4::simd_from(simd, [1, 2, 3, 4]);\n\n // From an element-wise function:\n let e = i32x4::from_fn(simd, |i| i as i32);\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(16))] @@ -1882,10 +1910,6 @@ impl SimdBase for i32x4 { type Block = i32x4; type Array = [i32; 4]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[i32] { self.as_array().as_slice() } @@ -2104,6 +2128,13 @@ impl SimdInterleaved for i32x4 { vectors[0].simd.store_four_interleaved_i32x4(vectors, dest); } } +impl ExtractToken for u32x4 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 4 [`u32`] elements.\n\nThis type's memory layout is identical to that of [`[u32; 4]`](prim@array), except that it is aligned to 16 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, u32x4};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = u32x4::splat(simd, 1);\n let b = u32x4::simd_from(simd, 1);\n\n // From a slice:\n let c = u32x4::from_slice(simd, &[1, 2, 3, 4]);\n\n // From an array:\n let d = u32x4::simd_from(simd, [1, 2, 3, 4]);\n\n // From an element-wise function:\n let e = u32x4::from_fn(simd, |i| i as u32);\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(16))] @@ -2195,10 +2226,6 @@ impl SimdBase for u32x4 { type Block = u32x4; type Array = [u32; 4]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[u32] { self.as_array().as_slice() } @@ -2417,6 +2444,13 @@ impl SimdInterleaved for u32x4 { vectors[0].simd.store_four_interleaved_u32x4(vectors, dest); } } +impl ExtractToken for mask32x4 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD mask of 4 logical lanes corresponding to 32-bit vector elements.\n\nThe storage representation of this type is intentionally opaque and may vary depending on the SIMD level.\n\nYou can construct this mask type using the [`Self::splat`], [`Self::from_bitmask`], [`Self::from_slice`], and [`Self::simd_from`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, mask32x4};\nfn construct_mask(simd: S) {\n // From a single boolean value:\n let a = mask32x4::splat(simd, true);\n let b = mask32x4::simd_from(simd, true);\n\n // From signed integer mask lanes:\n let c = mask32x4::from_slice(simd, &[-1, 0, 0, 0]);\n let d = mask32x4::simd_from(simd, [-1, 0, 0, 0]);\n\n // From a compact bitmask (same mask as above, least significant bit maps to lane 0):\n let e = mask32x4::from_bitmask(simd, 0b0001);\n\n // By setting individual lanes:\n let mut f = mask32x4::splat(simd, false);\n f.set(0, true);\n}\n```"] #[derive(Clone, Copy)] pub struct mask32x4 { @@ -2461,10 +2495,6 @@ impl SimdMask for mask32x4 { type Element = i32; const LEN: usize = 4; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.splat_mask32x4(val) } @@ -2523,6 +2553,13 @@ impl SimdMask for mask32x4 { self.simd.all_false_mask32x4(self) } } +impl ExtractToken for f64x2 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 2 [`f64`] elements.\n\nThis type's memory layout is identical to that of [`[f64; 2]`](prim@array), except that it is aligned to 16 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, f64x2};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = f64x2::splat(simd, 1.0);\n let b = f64x2::simd_from(simd, 1.0);\n\n // From a slice:\n let c = f64x2::from_slice(simd, &[1.0, 2.0]);\n\n // From an array:\n let d = f64x2::simd_from(simd, [1.0, 2.0]);\n\n // From an element-wise function:\n let e = f64x2::from_fn(simd, |i| i as f64);\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(16))] @@ -2614,10 +2651,6 @@ impl SimdBase for f64x2 { type Block = f64x2; type Array = [f64; 2]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[f64] { self.as_array().as_slice() } @@ -2865,6 +2898,13 @@ impl SimdInterleaved for f64x2 { vectors[0].simd.store_four_interleaved_f64x2(vectors, dest); } } +impl ExtractToken for i64x2 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 2 [`i64`] elements.\n\nThis type's memory layout is identical to that of [`[i64; 2]`](prim@array), except that it is aligned to 16 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, i64x2};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = i64x2::splat(simd, 1);\n let b = i64x2::simd_from(simd, 1);\n\n // From a slice:\n let c = i64x2::from_slice(simd, &[1, 2]);\n\n // From an array:\n let d = i64x2::simd_from(simd, [1, 2]);\n\n // From an element-wise function:\n let e = i64x2::from_fn(simd, |i| i as i64);\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(16))] @@ -2956,10 +2996,6 @@ impl SimdBase for i64x2 { type Block = i64x2; type Array = [i64; 2]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[i64] { self.as_array().as_slice() } @@ -3171,6 +3207,13 @@ impl SimdInterleaved for i64x2 { vectors[0].simd.store_four_interleaved_i64x2(vectors, dest); } } +impl ExtractToken for u64x2 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 2 [`u64`] elements.\n\nThis type's memory layout is identical to that of [`[u64; 2]`](prim@array), except that it is aligned to 16 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, u64x2};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = u64x2::splat(simd, 1);\n let b = u64x2::simd_from(simd, 1);\n\n // From a slice:\n let c = u64x2::from_slice(simd, &[1, 2]);\n\n // From an array:\n let d = u64x2::simd_from(simd, [1, 2]);\n\n // From an element-wise function:\n let e = u64x2::from_fn(simd, |i| i as u64);\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(16))] @@ -3262,10 +3305,6 @@ impl SimdBase for u64x2 { type Block = u64x2; type Array = [u64; 2]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[u64] { self.as_array().as_slice() } @@ -3477,6 +3516,13 @@ impl SimdInterleaved for u64x2 { vectors[0].simd.store_four_interleaved_u64x2(vectors, dest); } } +impl ExtractToken for mask64x2 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD mask of 2 logical lanes corresponding to 64-bit vector elements.\n\nThe storage representation of this type is intentionally opaque and may vary depending on the SIMD level.\n\nYou can construct this mask type using the [`Self::splat`], [`Self::from_bitmask`], [`Self::from_slice`], and [`Self::simd_from`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, mask64x2};\nfn construct_mask(simd: S) {\n // From a single boolean value:\n let a = mask64x2::splat(simd, true);\n let b = mask64x2::simd_from(simd, true);\n\n // From signed integer mask lanes:\n let c = mask64x2::from_slice(simd, &[-1, 0]);\n let d = mask64x2::simd_from(simd, [-1, 0]);\n\n // From a compact bitmask (same mask as above, least significant bit maps to lane 0):\n let e = mask64x2::from_bitmask(simd, 0b0001);\n\n // By setting individual lanes:\n let mut f = mask64x2::splat(simd, false);\n f.set(0, true);\n}\n```"] #[derive(Clone, Copy)] pub struct mask64x2 { @@ -3521,10 +3567,6 @@ impl SimdMask for mask64x2 { type Element = i64; const LEN: usize = 2; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.splat_mask64x2(val) } @@ -3583,6 +3625,13 @@ impl SimdMask for mask64x2 { self.simd.all_false_mask64x2(self) } } +impl ExtractToken for f32x8 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 8 [`f32`] elements.\n\nThis type's memory layout is identical to that of [`[f32; 8]`](prim@array), except that it is aligned to 32 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, f32x8};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = f32x8::splat(simd, 1.0);\n let b = f32x8::simd_from(simd, 1.0);\n\n // From a slice:\n let c = f32x8::from_slice(simd, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]);\n\n // From an array:\n let d = f32x8::simd_from(simd, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]);\n\n // From an element-wise function:\n let e = f32x8::from_fn(simd, |i| i as f32);\n # use fearless_simd::f32x4;\n // From `Self::Block`:\n let f = f32x8::block_splat(f32x4::simd_from(simd, [1.0, 2.0, 3.0, 4.0]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(32))] @@ -3674,10 +3723,6 @@ impl SimdBase for f32x8 { type Block = f32x4; type Array = [f32; 8]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[f32] { self.as_array().as_slice() } @@ -3924,6 +3969,13 @@ impl crate::SimdCombine for f32x8 { self.simd.combine_f32x8(self, rhs.simd_into(self.simd)) } } +impl ExtractToken for i8x32 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 32 [`i8`] elements.\n\nThis type's memory layout is identical to that of [`[i8; 32]`](prim@array), except that it is aligned to 32 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, i8x32};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = i8x32::splat(simd, 1);\n let b = i8x32::simd_from(simd, 1);\n\n // From a slice:\n let c = i8x32::from_slice(simd, &[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]);\n\n // From an array:\n let d = i8x32::simd_from(simd, [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]);\n\n // From an element-wise function:\n let e = i8x32::from_fn(simd, |i| i as i8);\n # use fearless_simd::i8x16;\n // From `Self::Block`:\n let f = i8x32::block_splat(i8x16::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(32))] @@ -4015,10 +4067,6 @@ impl SimdBase for i8x32 { type Block = i8x16; type Array = [i8; 32]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[i8] { self.as_array().as_slice() } @@ -4241,6 +4289,13 @@ impl crate::SimdCombine for i8x32 { self.simd.combine_i8x32(self, rhs.simd_into(self.simd)) } } +impl ExtractToken for u8x32 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 32 [`u8`] elements.\n\nThis type's memory layout is identical to that of [`[u8; 32]`](prim@array), except that it is aligned to 32 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, u8x32};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = u8x32::splat(simd, 1);\n let b = u8x32::simd_from(simd, 1);\n\n // From a slice:\n let c = u8x32::from_slice(simd, &[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]);\n\n // From an array:\n let d = u8x32::simd_from(simd, [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]);\n\n // From an element-wise function:\n let e = u8x32::from_fn(simd, |i| i as u8);\n # use fearless_simd::u8x16;\n // From `Self::Block`:\n let f = u8x32::block_splat(u8x16::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(32))] @@ -4332,10 +4387,6 @@ impl SimdBase for u8x32 { type Block = u8x16; type Array = [u8; 32]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[u8] { self.as_array().as_slice() } @@ -4558,6 +4609,13 @@ impl crate::SimdCombine for u8x32 { self.simd.combine_u8x32(self, rhs.simd_into(self.simd)) } } +impl ExtractToken for mask8x32 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD mask of 32 logical lanes corresponding to 8-bit vector elements.\n\nThe storage representation of this type is intentionally opaque and may vary depending on the SIMD level.\n\nYou can construct this mask type using the [`Self::splat`], [`Self::from_bitmask`], [`Self::from_slice`], and [`Self::simd_from`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, mask8x32};\nfn construct_mask(simd: S) {\n // From a single boolean value:\n let a = mask8x32::splat(simd, true);\n let b = mask8x32::simd_from(simd, true);\n\n // From signed integer mask lanes:\n let c = mask8x32::from_slice(simd, &[-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n let d = mask8x32::simd_from(simd, [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n\n // From a compact bitmask (same mask as above, least significant bit maps to lane 0):\n let e = mask8x32::from_bitmask(simd, 0b0001);\n\n // By setting individual lanes:\n let mut f = mask8x32::splat(simd, false);\n f.set(0, true);\n}\n```"] #[derive(Clone, Copy)] pub struct mask8x32 { @@ -4602,10 +4660,6 @@ impl SimdMask for mask8x32 { type Element = i8; const LEN: usize = 32; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.splat_mask8x32(val) } @@ -4664,6 +4718,13 @@ impl SimdMask for mask8x32 { self.simd.all_false_mask8x32(self) } } +impl ExtractToken for i16x16 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 16 [`i16`] elements.\n\nThis type's memory layout is identical to that of [`[i16; 16]`](prim@array), except that it is aligned to 32 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, i16x16};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = i16x16::splat(simd, 1);\n let b = i16x16::simd_from(simd, 1);\n\n // From a slice:\n let c = i16x16::from_slice(simd, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n\n // From an array:\n let d = i16x16::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n\n // From an element-wise function:\n let e = i16x16::from_fn(simd, |i| i as i16);\n # use fearless_simd::i16x8;\n // From `Self::Block`:\n let f = i16x16::block_splat(i16x8::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(32))] @@ -4755,10 +4816,6 @@ impl SimdBase for i16x16 { type Block = i16x8; type Array = [i16; 16]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[i16] { self.as_array().as_slice() } @@ -4981,6 +5038,13 @@ impl crate::SimdCombine for i16x16 { self.simd.combine_i16x16(self, rhs.simd_into(self.simd)) } } +impl ExtractToken for u16x16 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 16 [`u16`] elements.\n\nThis type's memory layout is identical to that of [`[u16; 16]`](prim@array), except that it is aligned to 32 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, u16x16};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = u16x16::splat(simd, 1);\n let b = u16x16::simd_from(simd, 1);\n\n // From a slice:\n let c = u16x16::from_slice(simd, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n\n // From an array:\n let d = u16x16::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n\n // From an element-wise function:\n let e = u16x16::from_fn(simd, |i| i as u16);\n # use fearless_simd::u16x8;\n // From `Self::Block`:\n let f = u16x16::block_splat(u16x8::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(32))] @@ -5072,10 +5136,6 @@ impl SimdBase for u16x16 { type Block = u16x8; type Array = [u16; 16]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[u16] { self.as_array().as_slice() } @@ -5298,6 +5358,13 @@ impl crate::SimdCombine for u16x16 { self.simd.combine_u16x16(self, rhs.simd_into(self.simd)) } } +impl ExtractToken for mask16x16 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD mask of 16 logical lanes corresponding to 16-bit vector elements.\n\nThe storage representation of this type is intentionally opaque and may vary depending on the SIMD level.\n\nYou can construct this mask type using the [`Self::splat`], [`Self::from_bitmask`], [`Self::from_slice`], and [`Self::simd_from`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, mask16x16};\nfn construct_mask(simd: S) {\n // From a single boolean value:\n let a = mask16x16::splat(simd, true);\n let b = mask16x16::simd_from(simd, true);\n\n // From signed integer mask lanes:\n let c = mask16x16::from_slice(simd, &[-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n let d = mask16x16::simd_from(simd, [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n\n // From a compact bitmask (same mask as above, least significant bit maps to lane 0):\n let e = mask16x16::from_bitmask(simd, 0b0001);\n\n // By setting individual lanes:\n let mut f = mask16x16::splat(simd, false);\n f.set(0, true);\n}\n```"] #[derive(Clone, Copy)] pub struct mask16x16 { @@ -5342,10 +5409,6 @@ impl SimdMask for mask16x16 { type Element = i16; const LEN: usize = 16; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.splat_mask16x16(val) } @@ -5404,6 +5467,13 @@ impl SimdMask for mask16x16 { self.simd.all_false_mask16x16(self) } } +impl ExtractToken for i32x8 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 8 [`i32`] elements.\n\nThis type's memory layout is identical to that of [`[i32; 8]`](prim@array), except that it is aligned to 32 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, i32x8};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = i32x8::splat(simd, 1);\n let b = i32x8::simd_from(simd, 1);\n\n // From a slice:\n let c = i32x8::from_slice(simd, &[1, 2, 3, 4, 5, 6, 7, 8]);\n\n // From an array:\n let d = i32x8::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8]);\n\n // From an element-wise function:\n let e = i32x8::from_fn(simd, |i| i as i32);\n # use fearless_simd::i32x4;\n // From `Self::Block`:\n let f = i32x8::block_splat(i32x4::simd_from(simd, [1, 2, 3, 4]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(32))] @@ -5495,10 +5565,6 @@ impl SimdBase for i32x8 { type Block = i32x4; type Array = [i32; 8]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[i32] { self.as_array().as_slice() } @@ -5724,6 +5790,13 @@ impl crate::SimdCombine for i32x8 { self.simd.combine_i32x8(self, rhs.simd_into(self.simd)) } } +impl ExtractToken for u32x8 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 8 [`u32`] elements.\n\nThis type's memory layout is identical to that of [`[u32; 8]`](prim@array), except that it is aligned to 32 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, u32x8};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = u32x8::splat(simd, 1);\n let b = u32x8::simd_from(simd, 1);\n\n // From a slice:\n let c = u32x8::from_slice(simd, &[1, 2, 3, 4, 5, 6, 7, 8]);\n\n // From an array:\n let d = u32x8::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8]);\n\n // From an element-wise function:\n let e = u32x8::from_fn(simd, |i| i as u32);\n # use fearless_simd::u32x4;\n // From `Self::Block`:\n let f = u32x8::block_splat(u32x4::simd_from(simd, [1, 2, 3, 4]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(32))] @@ -5815,10 +5888,6 @@ impl SimdBase for u32x8 { type Block = u32x4; type Array = [u32; 8]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[u32] { self.as_array().as_slice() } @@ -6044,6 +6113,13 @@ impl crate::SimdCombine for u32x8 { self.simd.combine_u32x8(self, rhs.simd_into(self.simd)) } } +impl ExtractToken for mask32x8 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD mask of 8 logical lanes corresponding to 32-bit vector elements.\n\nThe storage representation of this type is intentionally opaque and may vary depending on the SIMD level.\n\nYou can construct this mask type using the [`Self::splat`], [`Self::from_bitmask`], [`Self::from_slice`], and [`Self::simd_from`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, mask32x8};\nfn construct_mask(simd: S) {\n // From a single boolean value:\n let a = mask32x8::splat(simd, true);\n let b = mask32x8::simd_from(simd, true);\n\n // From signed integer mask lanes:\n let c = mask32x8::from_slice(simd, &[-1, 0, 0, 0, 0, 0, 0, 0]);\n let d = mask32x8::simd_from(simd, [-1, 0, 0, 0, 0, 0, 0, 0]);\n\n // From a compact bitmask (same mask as above, least significant bit maps to lane 0):\n let e = mask32x8::from_bitmask(simd, 0b0001);\n\n // By setting individual lanes:\n let mut f = mask32x8::splat(simd, false);\n f.set(0, true);\n}\n```"] #[derive(Clone, Copy)] pub struct mask32x8 { @@ -6088,10 +6164,6 @@ impl SimdMask for mask32x8 { type Element = i32; const LEN: usize = 8; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.splat_mask32x8(val) } @@ -6150,6 +6222,13 @@ impl SimdMask for mask32x8 { self.simd.all_false_mask32x8(self) } } +impl ExtractToken for f64x4 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 4 [`f64`] elements.\n\nThis type's memory layout is identical to that of [`[f64; 4]`](prim@array), except that it is aligned to 32 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, f64x4};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = f64x4::splat(simd, 1.0);\n let b = f64x4::simd_from(simd, 1.0);\n\n // From a slice:\n let c = f64x4::from_slice(simd, &[1.0, 2.0, 3.0, 4.0]);\n\n // From an array:\n let d = f64x4::simd_from(simd, [1.0, 2.0, 3.0, 4.0]);\n\n // From an element-wise function:\n let e = f64x4::from_fn(simd, |i| i as f64);\n # use fearless_simd::f64x2;\n // From `Self::Block`:\n let f = f64x4::block_splat(f64x2::simd_from(simd, [1.0, 2.0]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(32))] @@ -6241,10 +6320,6 @@ impl SimdBase for f64x4 { type Block = f64x2; type Array = [f64; 4]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[f64] { self.as_array().as_slice() } @@ -6487,6 +6562,13 @@ impl crate::SimdCombine for f64x4 { self.simd.combine_f64x4(self, rhs.simd_into(self.simd)) } } +impl ExtractToken for i64x4 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 4 [`i64`] elements.\n\nThis type's memory layout is identical to that of [`[i64; 4]`](prim@array), except that it is aligned to 32 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, i64x4};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = i64x4::splat(simd, 1);\n let b = i64x4::simd_from(simd, 1);\n\n // From a slice:\n let c = i64x4::from_slice(simd, &[1, 2, 3, 4]);\n\n // From an array:\n let d = i64x4::simd_from(simd, [1, 2, 3, 4]);\n\n // From an element-wise function:\n let e = i64x4::from_fn(simd, |i| i as i64);\n # use fearless_simd::i64x2;\n // From `Self::Block`:\n let f = i64x4::block_splat(i64x2::simd_from(simd, [1, 2]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(32))] @@ -6578,10 +6660,6 @@ impl SimdBase for i64x4 { type Block = i64x2; type Array = [i64; 4]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[i64] { self.as_array().as_slice() } @@ -6788,6 +6866,13 @@ impl crate::SimdCombine for i64x4 { self.simd.combine_i64x4(self, rhs.simd_into(self.simd)) } } +impl ExtractToken for u64x4 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 4 [`u64`] elements.\n\nThis type's memory layout is identical to that of [`[u64; 4]`](prim@array), except that it is aligned to 32 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, u64x4};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = u64x4::splat(simd, 1);\n let b = u64x4::simd_from(simd, 1);\n\n // From a slice:\n let c = u64x4::from_slice(simd, &[1, 2, 3, 4]);\n\n // From an array:\n let d = u64x4::simd_from(simd, [1, 2, 3, 4]);\n\n // From an element-wise function:\n let e = u64x4::from_fn(simd, |i| i as u64);\n # use fearless_simd::u64x2;\n // From `Self::Block`:\n let f = u64x4::block_splat(u64x2::simd_from(simd, [1, 2]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(32))] @@ -6879,10 +6964,6 @@ impl SimdBase for u64x4 { type Block = u64x2; type Array = [u64; 4]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[u64] { self.as_array().as_slice() } @@ -7089,6 +7170,13 @@ impl crate::SimdCombine for u64x4 { self.simd.combine_u64x4(self, rhs.simd_into(self.simd)) } } +impl ExtractToken for mask64x4 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD mask of 4 logical lanes corresponding to 64-bit vector elements.\n\nThe storage representation of this type is intentionally opaque and may vary depending on the SIMD level.\n\nYou can construct this mask type using the [`Self::splat`], [`Self::from_bitmask`], [`Self::from_slice`], and [`Self::simd_from`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, mask64x4};\nfn construct_mask(simd: S) {\n // From a single boolean value:\n let a = mask64x4::splat(simd, true);\n let b = mask64x4::simd_from(simd, true);\n\n // From signed integer mask lanes:\n let c = mask64x4::from_slice(simd, &[-1, 0, 0, 0]);\n let d = mask64x4::simd_from(simd, [-1, 0, 0, 0]);\n\n // From a compact bitmask (same mask as above, least significant bit maps to lane 0):\n let e = mask64x4::from_bitmask(simd, 0b0001);\n\n // By setting individual lanes:\n let mut f = mask64x4::splat(simd, false);\n f.set(0, true);\n}\n```"] #[derive(Clone, Copy)] pub struct mask64x4 { @@ -7133,10 +7221,6 @@ impl SimdMask for mask64x4 { type Element = i64; const LEN: usize = 4; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.splat_mask64x4(val) } @@ -7195,6 +7279,13 @@ impl SimdMask for mask64x4 { self.simd.all_false_mask64x4(self) } } +impl ExtractToken for f32x16 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 16 [`f32`] elements.\n\nThis type's memory layout is identical to that of [`[f32; 16]`](prim@array), except that it is aligned to 64 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, f32x16};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = f32x16::splat(simd, 1.0);\n let b = f32x16::simd_from(simd, 1.0);\n\n // From a slice:\n let c = f32x16::from_slice(simd, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0]);\n\n // From an array:\n let d = f32x16::simd_from(simd, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0]);\n\n // From an element-wise function:\n let e = f32x16::from_fn(simd, |i| i as f32);\n # use fearless_simd::f32x4;\n // From `Self::Block`:\n let f = f32x16::block_splat(f32x4::simd_from(simd, [1.0, 2.0, 3.0, 4.0]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(64))] @@ -7286,10 +7377,6 @@ impl SimdBase for f32x16 { type Block = f32x4; type Array = [f32; 16]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[f32] { self.as_array().as_slice() } @@ -7539,6 +7626,13 @@ impl crate::SimdSplit for f32x16 { self.simd.split_f32x16(self) } } +impl ExtractToken for i8x64 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 64 [`i8`] elements.\n\nThis type's memory layout is identical to that of [`[i8; 64]`](prim@array), except that it is aligned to 64 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, i8x64};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = i8x64::splat(simd, 1);\n let b = i8x64::simd_from(simd, 1);\n\n // From a slice:\n let c = i8x64::from_slice(simd, &[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]);\n\n // From an array:\n let d = i8x64::simd_from(simd, [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]);\n\n // From an element-wise function:\n let e = i8x64::from_fn(simd, |i| i as i8);\n # use fearless_simd::i8x16;\n // From `Self::Block`:\n let f = i8x64::block_splat(i8x16::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(64))] @@ -7630,10 +7724,6 @@ impl SimdBase for i8x64 { type Block = i8x16; type Array = [i8; 64]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[i8] { self.as_array().as_slice() } @@ -7882,6 +7972,13 @@ impl crate::SimdSplit for i8x64 { self.simd.split_i8x64(self) } } +impl ExtractToken for u8x64 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 64 [`u8`] elements.\n\nThis type's memory layout is identical to that of [`[u8; 64]`](prim@array), except that it is aligned to 64 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, u8x64};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = u8x64::splat(simd, 1);\n let b = u8x64::simd_from(simd, 1);\n\n // From a slice:\n let c = u8x64::from_slice(simd, &[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]);\n\n // From an array:\n let d = u8x64::simd_from(simd, [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]);\n\n // From an element-wise function:\n let e = u8x64::from_fn(simd, |i| i as u8);\n # use fearless_simd::u8x16;\n // From `Self::Block`:\n let f = u8x64::block_splat(u8x16::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(64))] @@ -7973,10 +8070,6 @@ impl SimdBase for u8x64 { type Block = u8x16; type Array = [u8; 64]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[u8] { self.as_array().as_slice() } @@ -8225,6 +8318,13 @@ impl crate::SimdSplit for u8x64 { self.simd.split_u8x64(self) } } +impl ExtractToken for mask8x64 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD mask of 64 logical lanes corresponding to 8-bit vector elements.\n\nThe storage representation of this type is intentionally opaque and may vary depending on the SIMD level.\n\nYou can construct this mask type using the [`Self::splat`], [`Self::from_bitmask`], [`Self::from_slice`], and [`Self::simd_from`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, mask8x64};\nfn construct_mask(simd: S) {\n // From a single boolean value:\n let a = mask8x64::splat(simd, true);\n let b = mask8x64::simd_from(simd, true);\n\n // From signed integer mask lanes:\n let c = mask8x64::from_slice(simd, &[-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n let d = mask8x64::simd_from(simd, [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n\n // From a compact bitmask (same mask as above, least significant bit maps to lane 0):\n let e = mask8x64::from_bitmask(simd, 0b0001);\n\n // By setting individual lanes:\n let mut f = mask8x64::splat(simd, false);\n f.set(0, true);\n}\n```"] #[derive(Clone, Copy)] pub struct mask8x64 { @@ -8269,10 +8369,6 @@ impl SimdMask for mask8x64 { type Element = i8; const LEN: usize = 64; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.splat_mask8x64(val) } @@ -8331,6 +8427,13 @@ impl SimdMask for mask8x64 { self.simd.all_false_mask8x64(self) } } +impl ExtractToken for i16x32 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 32 [`i16`] elements.\n\nThis type's memory layout is identical to that of [`[i16; 32]`](prim@array), except that it is aligned to 64 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, i16x32};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = i16x32::splat(simd, 1);\n let b = i16x32::simd_from(simd, 1);\n\n // From a slice:\n let c = i16x32::from_slice(simd, &[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]);\n\n // From an array:\n let d = i16x32::simd_from(simd, [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]);\n\n // From an element-wise function:\n let e = i16x32::from_fn(simd, |i| i as i16);\n # use fearless_simd::i16x8;\n // From `Self::Block`:\n let f = i16x32::block_splat(i16x8::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(64))] @@ -8422,10 +8525,6 @@ impl SimdBase for i16x32 { type Block = i16x8; type Array = [i16; 32]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[i16] { self.as_array().as_slice() } @@ -8658,6 +8757,13 @@ impl crate::SimdSplit for i16x32 { self.simd.split_i16x32(self) } } +impl ExtractToken for u16x32 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 32 [`u16`] elements.\n\nThis type's memory layout is identical to that of [`[u16; 32]`](prim@array), except that it is aligned to 64 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, u16x32};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = u16x32::splat(simd, 1);\n let b = u16x32::simd_from(simd, 1);\n\n // From a slice:\n let c = u16x32::from_slice(simd, &[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]);\n\n // From an array:\n let d = u16x32::simd_from(simd, [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]);\n\n // From an element-wise function:\n let e = u16x32::from_fn(simd, |i| i as u16);\n # use fearless_simd::u16x8;\n // From `Self::Block`:\n let f = u16x32::block_splat(u16x8::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(64))] @@ -8749,10 +8855,6 @@ impl SimdBase for u16x32 { type Block = u16x8; type Array = [u16; 32]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[u16] { self.as_array().as_slice() } @@ -8985,6 +9087,13 @@ impl crate::SimdSplit for u16x32 { self.simd.split_u16x32(self) } } +impl ExtractToken for mask16x32 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD mask of 32 logical lanes corresponding to 16-bit vector elements.\n\nThe storage representation of this type is intentionally opaque and may vary depending on the SIMD level.\n\nYou can construct this mask type using the [`Self::splat`], [`Self::from_bitmask`], [`Self::from_slice`], and [`Self::simd_from`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, mask16x32};\nfn construct_mask(simd: S) {\n // From a single boolean value:\n let a = mask16x32::splat(simd, true);\n let b = mask16x32::simd_from(simd, true);\n\n // From signed integer mask lanes:\n let c = mask16x32::from_slice(simd, &[-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n let d = mask16x32::simd_from(simd, [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n\n // From a compact bitmask (same mask as above, least significant bit maps to lane 0):\n let e = mask16x32::from_bitmask(simd, 0b0001);\n\n // By setting individual lanes:\n let mut f = mask16x32::splat(simd, false);\n f.set(0, true);\n}\n```"] #[derive(Clone, Copy)] pub struct mask16x32 { @@ -9029,10 +9138,6 @@ impl SimdMask for mask16x32 { type Element = i16; const LEN: usize = 32; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.splat_mask16x32(val) } @@ -9091,6 +9196,13 @@ impl SimdMask for mask16x32 { self.simd.all_false_mask16x32(self) } } +impl ExtractToken for i32x16 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 16 [`i32`] elements.\n\nThis type's memory layout is identical to that of [`[i32; 16]`](prim@array), except that it is aligned to 64 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, i32x16};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = i32x16::splat(simd, 1);\n let b = i32x16::simd_from(simd, 1);\n\n // From a slice:\n let c = i32x16::from_slice(simd, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n\n // From an array:\n let d = i32x16::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n\n // From an element-wise function:\n let e = i32x16::from_fn(simd, |i| i as i32);\n # use fearless_simd::i32x4;\n // From `Self::Block`:\n let f = i32x16::block_splat(i32x4::simd_from(simd, [1, 2, 3, 4]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(64))] @@ -9182,10 +9294,6 @@ impl SimdBase for i32x16 { type Block = i32x4; type Array = [i32; 16]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[i32] { self.as_array().as_slice() } @@ -9414,6 +9522,13 @@ impl crate::SimdSplit for i32x16 { self.simd.split_i32x16(self) } } +impl ExtractToken for u32x16 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 16 [`u32`] elements.\n\nThis type's memory layout is identical to that of [`[u32; 16]`](prim@array), except that it is aligned to 64 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, u32x16};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = u32x16::splat(simd, 1);\n let b = u32x16::simd_from(simd, 1);\n\n // From a slice:\n let c = u32x16::from_slice(simd, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n\n // From an array:\n let d = u32x16::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);\n\n // From an element-wise function:\n let e = u32x16::from_fn(simd, |i| i as u32);\n # use fearless_simd::u32x4;\n // From `Self::Block`:\n let f = u32x16::block_splat(u32x4::simd_from(simd, [1, 2, 3, 4]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(64))] @@ -9505,10 +9620,6 @@ impl SimdBase for u32x16 { type Block = u32x4; type Array = [u32; 16]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[u32] { self.as_array().as_slice() } @@ -9737,6 +9848,13 @@ impl crate::SimdSplit for u32x16 { self.simd.split_u32x16(self) } } +impl ExtractToken for mask32x16 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD mask of 16 logical lanes corresponding to 32-bit vector elements.\n\nThe storage representation of this type is intentionally opaque and may vary depending on the SIMD level.\n\nYou can construct this mask type using the [`Self::splat`], [`Self::from_bitmask`], [`Self::from_slice`], and [`Self::simd_from`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, mask32x16};\nfn construct_mask(simd: S) {\n // From a single boolean value:\n let a = mask32x16::splat(simd, true);\n let b = mask32x16::simd_from(simd, true);\n\n // From signed integer mask lanes:\n let c = mask32x16::from_slice(simd, &[-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n let d = mask32x16::simd_from(simd, [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n\n // From a compact bitmask (same mask as above, least significant bit maps to lane 0):\n let e = mask32x16::from_bitmask(simd, 0b0001);\n\n // By setting individual lanes:\n let mut f = mask32x16::splat(simd, false);\n f.set(0, true);\n}\n```"] #[derive(Clone, Copy)] pub struct mask32x16 { @@ -9781,10 +9899,6 @@ impl SimdMask for mask32x16 { type Element = i32; const LEN: usize = 16; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.splat_mask32x16(val) } @@ -9843,6 +9957,13 @@ impl SimdMask for mask32x16 { self.simd.all_false_mask32x16(self) } } +impl ExtractToken for f64x8 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 8 [`f64`] elements.\n\nThis type's memory layout is identical to that of [`[f64; 8]`](prim@array), except that it is aligned to 64 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, f64x8};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = f64x8::splat(simd, 1.0);\n let b = f64x8::simd_from(simd, 1.0);\n\n // From a slice:\n let c = f64x8::from_slice(simd, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]);\n\n // From an array:\n let d = f64x8::simd_from(simd, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]);\n\n // From an element-wise function:\n let e = f64x8::from_fn(simd, |i| i as f64);\n # use fearless_simd::f64x2;\n // From `Self::Block`:\n let f = f64x8::block_splat(f64x2::simd_from(simd, [1.0, 2.0]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(64))] @@ -9934,10 +10055,6 @@ impl SimdBase for f64x8 { type Block = f64x2; type Array = [f64; 8]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[f64] { self.as_array().as_slice() } @@ -10186,6 +10303,13 @@ impl crate::SimdSplit for f64x8 { self.simd.split_f64x8(self) } } +impl ExtractToken for i64x8 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 8 [`i64`] elements.\n\nThis type's memory layout is identical to that of [`[i64; 8]`](prim@array), except that it is aligned to 64 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, i64x8};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = i64x8::splat(simd, 1);\n let b = i64x8::simd_from(simd, 1);\n\n // From a slice:\n let c = i64x8::from_slice(simd, &[1, 2, 3, 4, 5, 6, 7, 8]);\n\n // From an array:\n let d = i64x8::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8]);\n\n // From an element-wise function:\n let e = i64x8::from_fn(simd, |i| i as i64);\n # use fearless_simd::i64x2;\n // From `Self::Block`:\n let f = i64x8::block_splat(i64x2::simd_from(simd, [1, 2]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(64))] @@ -10277,10 +10401,6 @@ impl SimdBase for i64x8 { type Block = i64x2; type Array = [i64; 8]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[i64] { self.as_array().as_slice() } @@ -10493,6 +10613,13 @@ impl crate::SimdSplit for i64x8 { self.simd.split_i64x8(self) } } +impl ExtractToken for u64x8 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD vector of 8 [`u64`] elements.\n\nThis type's memory layout is identical to that of [`[u64; 8]`](prim@array), except that it is aligned to 64 bytes.\n\nYou may construct this vector type using the [`Self::splat`], [`Self::from_slice`], [`Self::simd_from`], [`Self::from_fn`], and [`Self::block_splat`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, u64x8};\nfn construct_simd(simd: S) {\n // From a single scalar value:\n let a = u64x8::splat(simd, 1);\n let b = u64x8::simd_from(simd, 1);\n\n // From a slice:\n let c = u64x8::from_slice(simd, &[1, 2, 3, 4, 5, 6, 7, 8]);\n\n // From an array:\n let d = u64x8::simd_from(simd, [1, 2, 3, 4, 5, 6, 7, 8]);\n\n // From an element-wise function:\n let e = u64x8::from_fn(simd, |i| i as u64);\n # use fearless_simd::u64x2;\n // From `Self::Block`:\n let f = u64x8::block_splat(u64x2::simd_from(simd, [1, 2]));\n}\n```"] #[derive(Clone, Copy)] #[repr(C, align(64))] @@ -10584,10 +10711,6 @@ impl SimdBase for u64x8 { type Block = u64x2; type Array = [u64; 8]; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[u64] { self.as_array().as_slice() } @@ -10800,6 +10923,13 @@ impl crate::SimdSplit for u64x8 { self.simd.split_u64x8(self) } } +impl ExtractToken for mask64x8 { + type S = S; + #[inline] + fn token(&self) -> S { + self.simd + } +} #[doc = "A SIMD mask of 8 logical lanes corresponding to 64-bit vector elements.\n\nThe storage representation of this type is intentionally opaque and may vary depending on the SIMD level.\n\nYou can construct this mask type using the [`Self::splat`], [`Self::from_bitmask`], [`Self::from_slice`], and [`Self::simd_from`] methods.\n\n```rust\n# use fearless_simd::{prelude::*, mask64x8};\nfn construct_mask(simd: S) {\n // From a single boolean value:\n let a = mask64x8::splat(simd, true);\n let b = mask64x8::simd_from(simd, true);\n\n // From signed integer mask lanes:\n let c = mask64x8::from_slice(simd, &[-1, 0, 0, 0, 0, 0, 0, 0]);\n let d = mask64x8::simd_from(simd, [-1, 0, 0, 0, 0, 0, 0, 0]);\n\n // From a compact bitmask (same mask as above, least significant bit maps to lane 0):\n let e = mask64x8::from_bitmask(simd, 0b0001);\n\n // By setting individual lanes:\n let mut f = mask64x8::splat(simd, false);\n f.set(0, true);\n}\n```"] #[derive(Clone, Copy)] pub struct mask64x8 { @@ -10844,10 +10974,6 @@ impl SimdMask for mask64x8 { type Element = i64; const LEN: usize = 8; #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.splat_mask64x8(val) } diff --git a/fearless_simd/src/generated/sse2.rs b/fearless_simd/src/generated/sse2.rs index 42318d22..425c0bb8 100644 --- a/fearless_simd/src/generated/sse2.rs +++ b/fearless_simd/src/generated/sse2.rs @@ -140,6 +140,13 @@ impl Sse2 { } } impl Seal for Sse2 {} +impl ExtractToken for Sse2 { + type S = Self; + #[inline] + fn token(&self) -> Self { + *self + } +} impl ArchTypes for Sse2 { type f32x4 = crate::support::Aligned128<__m128>; type i8x16 = crate::support::Aligned128<__m128i>; diff --git a/fearless_simd/src/generated/sse4_2.rs b/fearless_simd/src/generated/sse4_2.rs index b6aef731..1bbeceed 100644 --- a/fearless_simd/src/generated/sse4_2.rs +++ b/fearless_simd/src/generated/sse4_2.rs @@ -118,6 +118,13 @@ impl Sse4_2 { } } impl Seal for Sse4_2 {} +impl ExtractToken for Sse4_2 { + type S = Self; + #[inline] + fn token(&self) -> Self { + *self + } +} impl ArchTypes for Sse4_2 { type f32x4 = crate::support::Aligned128<__m128>; type i8x16 = crate::support::Aligned128<__m128i>; diff --git a/fearless_simd/src/generated/wasm.rs b/fearless_simd/src/generated/wasm.rs index a955ed53..db8470bc 100644 --- a/fearless_simd/src/generated/wasm.rs +++ b/fearless_simd/src/generated/wasm.rs @@ -129,6 +129,13 @@ impl WasmSimd128 { } } impl Seal for WasmSimd128 {} +impl ExtractToken for WasmSimd128 { + type S = Self; + #[inline] + fn token(&self) -> Self { + *self + } +} impl ArchTypes for WasmSimd128 { type f32x4 = crate::support::Aligned128; type i8x16 = crate::support::Aligned128; diff --git a/fearless_simd/src/traits.rs b/fearless_simd/src/traits.rs index 35743897..76f3cf34 100644 --- a/fearless_simd/src/traits.rs +++ b/fearless_simd/src/traits.rs @@ -15,6 +15,61 @@ use core::ops::{ }; use core::str::FromStr; +/// A value that carries a SIMD token. +/// +/// Implemented by all SIMD tokens, vectors, and masks, and by references to these +/// types. User-defined wrappers can implement this trait by returning the token +/// of a contained value. +/// +/// The [`#[simd]`](https://docs.rs/fearless_simd_macros/latest/fearless_simd_macros/attr.simd.html) +/// attribute extracts a token from its first non-receiver parameter using this +/// trait. +/// +/// ``` +/// use fearless_simd::{ExtractToken, Simd, f32x4}; +/// +/// // Four consecutive audio samples, processed together. +/// struct AudioSamples(f32x4); +/// +/// impl ExtractToken for AudioSamples { +/// type S = S; +/// +/// #[inline] +/// fn token(&self) -> S { +/// self.0.token() +/// } +/// } +/// ``` +#[diagnostic::on_unimplemented( + message = "`{Self}` does not carry a SIMD token", + note = "If you are using #[simd], the first non-receiver parameter must carry a SIMD token. See the #[simd] documentation for more information." +)] +pub trait ExtractToken { + /// The SIMD implementation associated with this value. + type S: Simd; + + /// Get the SIMD token associated with this value. + fn token(&self) -> Self::S; +} + +impl ExtractToken for &T { + type S = T::S; + + #[inline] + fn token(&self) -> Self::S { + T::token(*self) + } +} + +impl ExtractToken for &mut T { + type S = T::S; + + #[inline] + fn token(&self) -> Self::S { + T::token(&**self) + } +} + /// Element-wise selection between two SIMD vectors using `self`. pub trait Select: Seal { /// For each logical lane of this mask, select the first operand if the lane is true, and select the second diff --git a/fearless_simd_gen/src/level.rs b/fearless_simd_gen/src/level.rs index f48130c5..23b2bad4 100644 --- a/fearless_simd_gen/src/level.rs +++ b/fearless_simd_gen/src/level.rs @@ -330,6 +330,15 @@ pub(crate) trait Level { impl Seal for #level_tok {} + impl ExtractToken for #level_tok { + type S = Self; + + #[inline] + fn token(&self) -> Self { + *self + } + } + #arch_types_impl #simd_impl diff --git a/fearless_simd_gen/src/mk_simd_trait.rs b/fearless_simd_gen/src/mk_simd_trait.rs index 81149782..487197e5 100644 --- a/fearless_simd_gen/src/mk_simd_trait.rs +++ b/fearless_simd_gen/src/mk_simd_trait.rs @@ -66,7 +66,7 @@ pub(crate) fn mk_simd_trait() -> TokenStream { } let mut code = quote! { use core::fmt::Debug; - use crate::{seal::Seal, Level, SimdElement, SimdIntElement, SimdFloatElement, SimdFrom, SimdInto, SimdCvtTruncate, SimdCvtFloat, SimdWiden, SimdNarrow, Select, Bytes}; + use crate::{seal::Seal, Level, ExtractToken, SimdElement, SimdIntElement, SimdFloatElement, SimdFrom, SimdInto, SimdCvtTruncate, SimdCvtFloat, SimdWiden, SimdNarrow, Select, Bytes}; #imports /// The main SIMD trait, implemented by all SIMD token types. /// @@ -103,7 +103,7 @@ pub(crate) fn mk_simd_trait() -> TokenStream { /// # assert_eq!(*result, [6.0, 8.0, 10.0, 12.0]); /// }); /// ``` - pub trait Simd: Sized + Clone + Copy + Send + Sync + Debug + Seal + arch_types::ArchTypes + 'static { + pub trait Simd: ExtractToken + Sized + Clone + Copy + Send + Sync + Debug + Seal + arch_types::ArchTypes + 'static { /// A native-width SIMD vector of [`f32`]s. type f32s: SimdFloat, Mask = Self::mask32s, ByteVector = Self::u8s> + SimdCvtFloat + SimdCvtFloat + SimdWiden; @@ -289,7 +289,7 @@ fn mk_simd_base() -> TokenStream { quote! { /// Base functionality implemented by all SIMD vectors. pub trait SimdBase: - Copy + Sync + Send + Debug + 'static + ExtractToken + Copy + Sync + Send + Debug + 'static + Seal + Bytes + SimdFrom + SimdFrom + core::ops::Index + core::ops::IndexMut @@ -328,8 +328,6 @@ fn mk_simd_base() -> TokenStream { + AsRef<[Self::Element]> + AsMut<[Self::Element]> + From; - /// Get the [`Simd`] implementation associated with this type. - fn witness(&self) -> S; fn as_slice(&self) -> &[Self::Element]; fn as_mut_slice(&mut self) -> &mut [Self::Element]; /// Create a SIMD vector from a slice. @@ -416,7 +414,7 @@ fn mk_simd_base() -> TokenStream { /// If `OFFSET` is greater than or equal to `Self::LEN`, all lanes are filled with `padding`. #[inline(always)] fn shift_elements_right(self, padding: Self::Element) -> Self { - let padding = Self::splat(self.witness(), padding); + let padding = Self::splat(self.token(), padding); match Self::LEN.saturating_sub(OFFSET) { #(#shift_right_arms,)* _ => unreachable!(), @@ -531,7 +529,7 @@ fn mk_simd_mask() -> TokenStream { /// current backends may use all-zero/all-one integer vectors internally, while future /// predicate-register backends may use a compact representation. pub trait SimdMask: - Copy + Sync + Send + 'static + ExtractToken + Copy + Sync + Send + 'static + Seal + Select #(+ #op_traits)* @@ -545,9 +543,6 @@ fn mk_simd_mask() -> TokenStream { /// This mask type's lane count. const LEN: usize; - /// Get the [`Simd`] implementation associated with this type. - fn witness(&self) -> S; - /// Create a SIMD mask with all lanes set to the given boolean value. fn splat(simd: S, val: bool) -> Self; diff --git a/fearless_simd_gen/src/mk_simd_types.rs b/fearless_simd_gen/src/mk_simd_types.rs index 1e529d42..4d1bf152 100644 --- a/fearless_simd_gen/src/mk_simd_types.rs +++ b/fearless_simd_gen/src/mk_simd_types.rs @@ -16,7 +16,7 @@ use crate::{ pub(crate) fn mk_simd_types() -> TokenStream { let mut result = quote! { - use crate::{Bytes, Select, Simd, SimdBase, SimdFrom, SimdInto, SimdMask, SimdCvtFloat, SimdCvtTruncate, SimdInterleaved, SimdWiden, SimdNarrow, seal::Seal}; + use crate::{Bytes, ExtractToken, Select, Simd, SimdBase, SimdFrom, SimdInto, SimdMask, SimdCvtFloat, SimdCvtTruncate, SimdInterleaved, SimdWiden, SimdNarrow, seal::Seal}; }; for ty in SIMD_TYPES { let name = ty.rust(); @@ -30,6 +30,17 @@ pub(crate) fn mk_simd_types() -> TokenStream { let bytes = ty.bytes_ty().rust(); let mask = ty.mask_ty().rust(); + result.extend(quote! { + impl ExtractToken for #name { + type S = S; + + #[inline] + fn token(&self) -> S { + self.simd + } + } + }); + if ty.scalar == ScalarType::Mask { let splat = Ident::new(&format!("splat_{}", ty.rust_name()), Span::call_site()); let impl_block = simd_mask_impl(ty); @@ -418,11 +429,6 @@ fn simd_mask_impl(ty: &VecType) -> TokenStream { type Element = #scalar; const LEN: usize = #len; - #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn splat(simd: S, val: bool) -> Self { simd.#splat(val) @@ -561,11 +567,6 @@ fn simd_vec_impl(ty: &VecType) -> TokenStream { type Block = #block_ty; type Array = [#scalar; #len]; - #[inline(always)] - fn witness(&self) -> S { - self.simd - } - #[inline(always)] fn as_slice(&self) -> &[#scalar] { self.as_array().as_slice() diff --git a/fearless_simd_macros/README.md b/fearless_simd_macros/README.md index 0614fd13..1e323912 100644 --- a/fearless_simd_macros/README.md +++ b/fearless_simd_macros/README.md @@ -17,7 +17,7 @@ The library must be in scope as `fearless_simd` in the module containing the annotated function. The dependency declaration above makes that name available automatically. `fearless_simd` v1.0 or later is required. -Apply `#[simd]` to a function whose first argument is its SIMD token: +Apply `#[simd]` to a function whose first argument carries its SIMD token: ```rust,ignore use fearless_simd::prelude::*; @@ -40,7 +40,7 @@ Conceptually, the macro expands the body to: ```rust,ignore fn double_u32s(simd: S, values: &mut [u32]) { - simd.vectorize( + ExtractToken::token(&simd).vectorize( #[inline(always)] || { // Original body. @@ -54,26 +54,69 @@ closure are passed as a struct and are often stored on the stack instead of being passed in registers, which incurs some overhead. We avoid that overhead by expanding into a more verbose but slightly more optimal code. +Vectors and masks already carry their token, so they do not need a separate +token argument. Both fixed-width and native-width types work: + +```rust,ignore +use fearless_simd::{f32x4, prelude::*}; +use fearless_simd_macros::simd; + +#[simd] +fn double(value: f32x4) -> f32x4 { + value + value +} + +#[simd] +fn double_native(value: S::f32s) -> S::f32s { + value + value +} +``` + +User-defined wrappers can implement the public `ExtractToken` trait. They do +not need to implement `Copy` or any of the vector operation traits: + +```rust,ignore +use fearless_simd::{f32x4, prelude::*}; +use fearless_simd_macros::simd; + +// Four consecutive audio samples, processed together. +struct AudioSamples(f32x4); + +impl ExtractToken for AudioSamples { + type S = S; + + #[inline] + fn token(&self) -> S { + self.0.token() + } +} + +#[simd] +fn apply_gain(samples: AudioSamples, gain: f32) -> AudioSamples { + AudioSamples(samples.0 * gain) +} +``` + ## Accepted functions The first typed parameter after an optional `self` receiver is treated as the -SIMD token. The parameter takes the token by value and supports identifier -bindings such as `simd: S`, `mut simd: S`, `ref simd: S`, and `ref mut simd: S`. -An unused token may be written as `_: S`; the macro gives it a +SIMD token carrier. Its type must implement `fearless_simd::ExtractToken`; +tokens, vectors, masks, and shared or mutable references to carriers are supported. + +An unused carrier may be written as `_: S`; the macro gives it a private hygienic binding. Destructured and `binding @ pattern` -parameters are not supported for the token. Neither `#[cfg]` nor `#[cfg_attr]` +parameters are not supported for the carrier. Neither `#[cfg]` nor `#[cfg_attr]` may be placed on that parameter. The macro accepts synchronous free functions, inherent methods, trait implementation methods, and default trait methods. Generic parameters, `where` clauses, return types, `unsafe`, and non-variadic `extern` ABIs are preserved. -Attribute arguments are not supported: write `#[simd]`, not `#[simd(...)]`. `async`, `const`, variadic, bodyless, and specialization `default fn` functions are rejected. The attributes `#[track_caller]`, `#[unsafe(naked)]`, and `#[instruction_set]` are also rejected because moving the body into a closure would invalidate their semantics or body requirements. -`#[target_feature]` and other attributes are preserved. +Other attributes are preserved. A trait's `#[track_caller]` attribute is inherited by its implementations, but is not present in the implementation method's token stream when this macro @@ -83,6 +126,12 @@ diagnose it. ## Execution boundaries and captures +The macro calls `ExtractToken::token(&first_argument)` exactly once, before +entering the selected SIMD context, then forwards the original argument to the +body. This borrows the carrier without copying it. Custom `token()` +implementations should be cheap and must not assume the caller already has the +token's target features enabled. An inherent method named `token` is ignored. + Only work performed while the function body is executing is covered by the SIMD context. Code inside a returned future, closure, or lazy iterator runs later and is not covered. Named helper functions do not inherit the enabled @@ -99,8 +148,10 @@ substitute for function-parameter destruction order. Avoid relying on the relative drop order of by-value parameters with observable destructors in a `#[simd]` function. -The selected token must implement the library's `Simd` trait, normally through -an `S: Simd` bound. The procedural macro invokes +The extracted token must implement the library's `Simd` trait, as required by +`ExtractToken::S`. An `S: Simd` bound already implies `ExtractToken`; +`SimdBase` and `SimdMask` also imply `ExtractToken`. +The procedural macro invokes `fearless_simd::__fearless_simd_dispatch!`; the procedural-macro crate itself does not depend on `fearless_simd`. The library helper owns the unsafe calls and resolves all proof types through `$crate`, so a lookalike module cannot diff --git a/fearless_simd_macros/src/lib.rs b/fearless_simd_macros/src/lib.rs index de0b886a..9c33d794 100644 --- a/fearless_simd_macros/src/lib.rs +++ b/fearless_simd_macros/src/lib.rs @@ -13,8 +13,9 @@ use syn::{Attribute, FnArg, ItemFn, Pat, Result}; /// Run a SIMD-generic function body with the token's target features enabled. /// /// The first typed parameter after an optional `self` receiver is used as the -/// SIMD token. The library must be in scope as `fearless_simd`; for a renamed -/// dependency, import it with `use simd_backend as fearless_simd;` in the +/// SIMD token carrier: a token, vector, mask, or a value implementing +/// `fearless_simd::ExtractToken`. The library must be in scope as `fearless_simd`; +/// for a renamed dependency, import it with `use simd_backend as fearless_simd;` in the /// containing module. See the [crate-level documentation](crate) for the complete /// expansion, supported function forms, and semantic caveats. #[proc_macro_attribute] @@ -45,10 +46,10 @@ fn expand(args: TokenStream2, item: TokenStream2) -> Result { reject_unsupported_signature(&function)?; reject_unsupported_attributes(&function.attrs)?; - let original_token = validate_simd_token(&function)?; - // The old expansion used this binding for vectorize even when the body - // did not use it. Preserve that usage for unused-variable lint purposes. - let use_token = original_token.map(|token| quote!(let _ = #token;)); + let original_carrier = validate_token_carrier(&function)?; + // Token extraction uses the outer binding. Also mark the original binding + // in the body closure as used, so unused carriers do not trigger warnings. + let use_carrier = original_carrier.map(|carrier| quote!(let _ = #carrier;)); let original_statements = mem::take(&mut function.block.stmts); // Give the closure the same expected return type so branch and early-return // coercions happen inside its body. Closures cannot name `impl Trait`, so @@ -115,8 +116,13 @@ fn expand(args: TokenStream2, item: TokenStream2) -> Result { helper_arguments.push(helper_name); argument_types.push(ty); } - // The first typed argument is the validated, unconditional SIMD token. - let token = &arguments[0]; + // Borrow the first argument only long enough to extract its token, before + // forwarding the original value. Carriers need not be Copy. Use the trait + // explicitly so an inherent token() method cannot change dispatch. + let carrier = &arguments[0]; + let token = quote! { + fearless_simd::ExtractToken::token(&#carrier) + }; // Inner function attributes are held in function.attrs by Syn. Leaving // them there keeps them at the beginning of the outer function body, @@ -132,7 +138,7 @@ fn expand(args: TokenStream2, item: TokenStream2) -> Result { (fearless_simd::__fearless_simd_dispatch!(#(#argument_types => #helper_arguments),*)).call( #token, #(#arguments,)* #[inline(always)] - |#(#parameters),*| #closure_output { #use_token #(#original_statements)* } + |#(#parameters),*| #closure_output { #use_carrier #(#original_statements)* } ) }; function @@ -216,7 +222,7 @@ fn is_attribute(attr: &Attribute, name: &str) -> bool { .is_ok_and(|path| path.is_ident(name)) } -fn validate_simd_token(function: &ItemFn) -> Result> { +fn validate_token_carrier(function: &ItemFn) -> Result> { let Some(argument) = function .sig .inputs @@ -228,7 +234,7 @@ fn validate_simd_token(function: &ItemFn) -> Result> { else { return Err(syn::Error::new_spanned( &function.sig.inputs, - "`#[simd]` requires a SIMD token parameter after any receiver", + "`#[simd]` requires a SIMD token carrier parameter after any receiver", )); }; @@ -240,7 +246,7 @@ fn validate_simd_token(function: &ItemFn) -> Result> { if let Some((at, _)) = &pattern.subpat { return Err(syn::Error::new( at.span, - "the SIMD token parameter cannot use an `@` subpattern", + "the SIMD token carrier parameter cannot use an `@` subpattern", )); } Ok(Some(pattern.ident.clone())) @@ -251,7 +257,7 @@ fn validate_simd_token(function: &ItemFn) -> Result> { } pattern => Err(syn::Error::new_spanned( pattern, - "the SIMD token parameter must be an identifier or `_`", + "the SIMD token carrier parameter must be an identifier or `_`", )), } } @@ -263,7 +269,7 @@ fn reject_conditional_attributes(attrs: &[Attribute]) -> Result<()> { { return Err(syn::Error::new_spanned( attr, - "the SIMD token parameter cannot be conditional", + "the SIMD token carrier parameter cannot be conditional", )); } Ok(()) diff --git a/fearless_simd_tests/tests/generics.rs b/fearless_simd_tests/tests/generics.rs index e78873e0..25758eb8 100644 --- a/fearless_simd_tests/tests/generics.rs +++ b/fearless_simd_tests/tests/generics.rs @@ -53,7 +53,7 @@ fn generic_saturating_sub>(lhs: V, rhs: V) -> V { // Ensure that a generic vector's byte representation is itself a same-token // byte vector whose byte representation is idempotent. fn generic_bytes>(value: V) -> V { - let simd = value.witness(); + let simd = value.token(); let bytes = value.to_bytes(); let bytes = >::from_slice(simd, >::as_slice(&bytes)); @@ -200,7 +200,7 @@ fn generic_native_width_float_saturating_narrow(low: S::f64s, high: S:: fn generic_array_roundtrip>(vector: V) -> V { fn require_debug(_: &T) {} - let simd = vector.witness(); + let simd = vector.token(); let mut vector = vector; let mut array = vector.to_array(); let array_copy = array; diff --git a/fearless_simd_tests/tests/harness/slide_exhaustive.rs b/fearless_simd_tests/tests/harness/slide_exhaustive.rs index 78e30d7e..df40ebde 100644 --- a/fearless_simd_tests/tests/harness/slide_exhaustive.rs +++ b/fearless_simd_tests/tests/harness/slide_exhaustive.rs @@ -19,7 +19,7 @@ macro_rules! test_vector_slide { assert_eq!( core::hint::black_box( test_vec_a - .witness() + .token() .vectorize(|| test_vec_a.slide::<$shift>(test_vec_b)) .as_slice() ), @@ -51,7 +51,7 @@ macro_rules! test_block_slide { assert_eq!( core::hint::black_box( test_vec_a - .witness() + .token() .vectorize(|| test_vec_a.slide_within_blocks::<$shift>(test_vec_b)) .as_slice() ), diff --git a/fearless_simd_tests/tests/simd_macro.rs b/fearless_simd_tests/tests/simd_macro.rs index 212d5e30..f7f766a1 100644 --- a/fearless_simd_tests/tests/simd_macro.rs +++ b/fearless_simd_tests/tests/simd_macro.rs @@ -6,6 +6,265 @@ use fearless_simd::{Simd, u32x8}; use fearless_simd_dev_macros::simd_test; use fearless_simd_macros::simd; +#[simd_test] +fn simd_attribute_extracts_vector_and_mask_tokens(simd: S) { + #[simd] + fn fixed(a: u32x8) -> u32x8 { + a + a + } + + #[simd] + fn native(a: S::u32s) -> S::u32s { + a + a + } + + #[simd] + fn fixed_mask(a: fearless_simd::mask32x8) -> bool { + a.all_true() + } + + #[simd] + fn native_mask(a: S::mask32s) -> bool { + a.all_true() + } + + let fixed_value = u32x8::splat(simd, 21); + assert_eq!( + fixed(fixed_value).as_slice(), + &[42; 8], + "a fixed-width vector should supply its token" + ); + assert!( + fixed_mask(fixed_value.simd_eq(fixed_value)), + "a fixed-width mask should supply its token" + ); + let native_value = S::u32s::splat(simd, 21); + assert!( + native::(native_value) + .as_slice() + .iter() + .all(|&x| x == 42), + "a native-width vector should supply its token" + ); + assert!( + native_mask::(native_value.simd_eq(native_value)), + "a native-width mask should supply its token" + ); +} + +#[simd_test] +fn simd_attribute_borrows_carriers(simd: S) { + #[simd] + fn shared(a: &u32x8) -> u32x8 { + *a + *a + } + + #[simd] + fn mutable(a: &mut u32x8) -> &mut u32x8 { + *a += 1; + a + } + + #[simd] + fn token_ref(simd: &S) -> S { + *simd + } + + #[simd] + fn token_mut(simd: &mut S) -> S { + *simd + } + + #[simd] + fn nested_ref(a: &&mut u32x8) -> u32x8 { + **a + } + + #[simd] + fn unsized_carrier(a: &T) -> T::S { + a.token() + } + + let mut value = u32x8::splat(simd, 21); + assert_eq!( + shared(&value).as_slice(), + &[42; 8], + "shared carriers should remain borrowed" + ); + mutable(&mut value)[0] = 100; + assert_eq!( + value.as_slice(), + &[100, 22, 22, 22, 22, 22, 22, 22], + "the returned borrow should refer to the original vector" + ); + assert_eq!( + nested_ref(&&mut value).as_slice(), + value.as_slice(), + "reference forwarding should work recursively" + ); + assert_eq!( + core::mem::discriminant(&token_ref(&simd).level()), + core::mem::discriminant(&simd.level()), + "a shared token reference should preserve the backend" + ); + let mut token = simd.token(); + assert_eq!( + core::mem::discriminant(&token_mut(&mut token).level()), + core::mem::discriminant(&simd.level()), + "a mutable token reference should preserve the backend" + ); + let erased: &dyn ExtractToken = &value; + assert_eq!( + core::mem::discriminant(&unsized_carrier(erased).level()), + core::mem::discriminant(&simd.level()), + "an unsized carrier should preserve the backend" + ); +} + +#[simd_test] +fn simd_attribute_preserves_carrier_patterns(simd: S) { + #[simd] + fn wildcard(_: u32x8) -> u32 { + 42 + } + + #[simd] + fn ref_pattern(ref a: u32x8) -> u32x8 { + *a + *a + } + + #[simd] + fn ref_mut_pattern(ref mut a: u32x8) -> u32x8 { + *a += 1; + *a + } + + let value = u32x8::splat(simd, 21); + assert_eq!( + wildcard(value), + 42, + "a wildcard carrier should receive a hidden binding" + ); + assert_eq!( + ref_pattern(value).as_slice(), + &[42; 8], + "ref patterns should borrow the forwarded vector" + ); + assert_eq!( + ref_mut_pattern(value).as_slice(), + &[22; 8], + "ref mut patterns should borrow the forwarded vector mutably" + ); +} + +#[simd_test] +fn simd_attribute_extracts_owned_wrapper_once(simd: S) { + use core::cell::Cell; + + struct Wrapper<'a, S: Simd> { + vector: u32x8, + extractions: &'a Cell, + drops: &'a Cell, + } + + impl ExtractToken for Wrapper<'_, S> { + type S = S; + + #[inline] + fn token(&self) -> S { + self.extractions.set(self.extractions.get() + 1); + self.vector.token() + } + } + + impl Drop for Wrapper<'_, S> { + fn drop(&mut self) { + self.drops.set(self.drops.get() + 1); + } + } + + impl Wrapper<'_, S> { + #[inline] + fn token(&self) -> u32 { + 123 + } + } + + #[simd] + fn update(mut value: Wrapper<'_, S>) -> Wrapper<'_, S> { + assert_eq!( + value.extractions.get(), + 1, + "extraction should happen before the body" + ); + assert_eq!( + value.drops.get(), + 0, + "the body should own the original carrier" + ); + value.vector += 1; + value + } + + let extractions = Cell::new(0); + let drops = Cell::new(0); + let value = Wrapper { + vector: u32x8::splat(simd, 41), + extractions: &extractions, + drops: &drops, + }; + assert_eq!( + value.token(), + 123, + "the inherent method has different semantics" + ); + let result = update(value); + assert_eq!( + result.vector.as_slice(), + &[42; 8], + "the wrapper should be returned with the body's changes" + ); + assert_eq!( + extractions.get(), + 1, + "dispatch should use the trait exactly once" + ); + drop(result); + assert_eq!(drops.get(), 1, "the carrier should be dropped exactly once"); +} + +#[simd_test] +fn simd_attribute_returns_borrow_from_wrapper(simd: S) { + struct Wrapper { + vector: u32x8, + label: String, + } + + impl ExtractToken for Wrapper { + type S = S; + + #[inline] + fn token(&self) -> S { + self.vector.token() + } + } + + #[simd] + fn label(value: &mut Wrapper) -> &mut String { + &mut value.label + } + + let mut value = Wrapper { + vector: u32x8::splat(simd, 0), + label: "hello".into(), + }; + label(&mut value).push('!'); + assert_eq!( + value.label, "hello!", + "the returned borrow should refer to the original wrapper's field" + ); +} + #[simd] fn double_values(_simd: S, values: &mut [u32]) { for value in values { diff --git a/fearless_simd_tests/tests/ui/simd/fail/at_token.rs b/fearless_simd_tests/tests/ui/simd/fail/at_token.rs index d5a2facf..8ba710bf 100644 --- a/fearless_simd_tests/tests/ui/simd/fail/at_token.rs +++ b/fearless_simd_tests/tests/ui/simd/fail/at_token.rs @@ -5,7 +5,7 @@ use fearless_simd_macros::simd; #[simd] fn at_token_is_not_supported(simd @ _: S) { - //~^ ERROR: the SIMD token parameter cannot use an `@` subpattern + //~^ ERROR: the SIMD token carrier parameter cannot use an `@` subpattern let _ = simd; } diff --git a/fearless_simd_tests/tests/ui/simd/fail/counterfeit_library.rs b/fearless_simd_tests/tests/ui/simd/fail/counterfeit_library.rs index 8428a0f7..8b591c5c 100644 --- a/fearless_simd_tests/tests/ui/simd/fail/counterfeit_library.rs +++ b/fearless_simd_tests/tests/ui/simd/fail/counterfeit_library.rs @@ -34,11 +34,25 @@ mod fearless_simd { Level::Avx512(self) } } + + pub trait ExtractToken { + type S: Simd; + fn token(&self) -> Self::S; + } + + impl ExtractToken for S { + type S = S; + + #[inline] + fn token(&self) -> S { + *self + } + } } #[simd] -fn counterfeit(simd: S, value: u32) -> u32 { - //~^ E0277 +//~^ E0277 +fn counterfeit(simd: S, value: u32) -> u32 { let _ = simd.level(); value } diff --git a/fearless_simd_tests/tests/ui/simd/fail/destructured_token.rs b/fearless_simd_tests/tests/ui/simd/fail/destructured_token.rs index 161139f8..ee6edf1b 100644 --- a/fearless_simd_tests/tests/ui/simd/fail/destructured_token.rs +++ b/fearless_simd_tests/tests/ui/simd/fail/destructured_token.rs @@ -5,7 +5,7 @@ use fearless_simd_macros::simd; #[simd] fn destructured_token_is_not_supported((simd,): (S,)) { - //~^ ERROR: the SIMD token parameter must be an identifier or `_` + //~^ ERROR: the SIMD token carrier parameter must be an identifier or `_` let _ = simd; } diff --git a/fearless_simd_tests/tests/ui/simd/fail/invalid_carrier.rs b/fearless_simd_tests/tests/ui/simd/fail/invalid_carrier.rs new file mode 100644 index 00000000..8395d898 --- /dev/null +++ b/fearless_simd_tests/tests/ui/simd/fail/invalid_carrier.rs @@ -0,0 +1,18 @@ +// Copyright 2026 the Fearless_SIMD Authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use fearless_simd::Simd; +use fearless_simd_macros::simd; + +#[simd] +//~^ ERROR: `i32` does not carry a SIMD token +fn invalid(value: i32, simd: S) -> i32 { + let _ = simd; + value +} + +#[simd] +//~^ ERROR: `i32` does not carry a SIMD token +fn wildcard(_: i32) {} + +fn main() {} diff --git a/fearless_simd_tests/tests/ui/simd/fail/no_token.rs b/fearless_simd_tests/tests/ui/simd/fail/no_token.rs index 2c606341..c304dbbb 100644 --- a/fearless_simd_tests/tests/ui/simd/fail/no_token.rs +++ b/fearless_simd_tests/tests/ui/simd/fail/no_token.rs @@ -4,7 +4,7 @@ use fearless_simd_macros::simd; #[simd] -//~^ ERROR: `#[simd]` requires a SIMD token parameter after any receiver +//~^ ERROR: `#[simd]` requires a SIMD token carrier parameter after any receiver fn token_is_required() {} fn main() {} diff --git a/fearless_simd_tests/tests/ui/simd/fail/parameter_cfg.rs b/fearless_simd_tests/tests/ui/simd/fail/parameter_cfg.rs index d842db68..498c4dba 100644 --- a/fearless_simd_tests/tests/ui/simd/fail/parameter_cfg.rs +++ b/fearless_simd_tests/tests/ui/simd/fail/parameter_cfg.rs @@ -5,7 +5,7 @@ use fearless_simd_macros::simd; #[simd] fn conditional_token_is_not_supported(#[cfg(any())] simd: S) { - //~^ ERROR: the SIMD token parameter cannot be conditional + //~^ ERROR: the SIMD token carrier parameter cannot be conditional let _ = simd; } diff --git a/fearless_simd_tests/tests/ui/simd/pass/carriers.rs b/fearless_simd_tests/tests/ui/simd/pass/carriers.rs new file mode 100644 index 00000000..acf19d97 --- /dev/null +++ b/fearless_simd_tests/tests/ui/simd/pass/carriers.rs @@ -0,0 +1,47 @@ +// Copyright 2026 the Fearless_SIMD Authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//@run + +#![deny(warnings)] +#![forbid(unsafe_code)] + +// Deliberately omit ExtractToken: supertrait bounds and macro-generated calls +// must work without that trait being imported. +use fearless_simd::{Fallback, Simd, SimdBase, SimdMask, u32x4}; +use fearless_simd_macros::simd; + +#[simd] +fn vector>(value: V) -> S { + value.token() +} + +#[simd] +fn mask>(value: M) -> S { + value.token() +} + +#[simd] +fn unused(value: u32x4) {} + +#[simd] +fn unit(_: &mut u32x4) -> () {} + +struct Container; + +impl Container { + #[simd] + fn method(&self, value: u32x4) -> u32x4 { + value + value + } +} + +fn main() { + let simd = Fallback::new(); + let mut value = u32x4::splat(simd, 21); + assert!(vector(value).level().is_fallback()); + assert!(mask(value.simd_eq(value)).level().is_fallback()); + unused(value); + unit(&mut value); + assert_eq!(Container.method(value).as_slice(), &[42; 4]); +} diff --git a/fearless_simd_tests/tests/ui/simd/renamed/src/lib.rs b/fearless_simd_tests/tests/ui/simd/renamed/src/lib.rs index d1e17ff8..2e749a19 100644 --- a/fearless_simd_tests/tests/ui/simd/renamed/src/lib.rs +++ b/fearless_simd_tests/tests/ui/simd/renamed/src/lib.rs @@ -18,6 +18,11 @@ pub fn baseline_sum() -> u64 { simd_backend::dispatch!(simd_backend::Level::baseline(), simd => sum(simd, 1, 2, 3)) } +#[simd] +pub fn double(value: simd_backend::u32x4) -> simd_backend::u32x4 { + value + value +} + pub mod facade { pub use simd_backend; } @@ -34,4 +39,9 @@ pub mod through_reexport { pub fn baseline_sum() -> u64 { fearless_simd::dispatch!(fearless_simd::Level::baseline(), simd => sum(simd, 1, 2, 3)) } + + #[simd] + pub fn double(value: fearless_simd::u32x4) -> fearless_simd::u32x4 { + value + value + } }