diff --git a/fearless_simd/src/generated/avx2.rs b/fearless_simd/src/generated/avx2.rs index 3f4ee93e..ff118915 100644 --- a/fearless_simd/src/generated/avx2.rs +++ b/fearless_simd/src/generated/avx2.rs @@ -967,6 +967,16 @@ impl Simd for Avx2 { kernel(self, a) } #[inline(always)] + fn swizzle_dyn_i8x16(self, a: i8x16, idxs: u8x16) -> i8x16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: i8x16, idxs: u8x16) -> i8x16 { + _mm_shuffle_epi8(a.into(), idxs.into()).simd_into(token) + } + ); + kernel(self, a, idxs) + } + #[inline(always)] fn reinterpret_u8_i8x16(self, a: i8x16) -> u8x16 { crate::kernel!( #[inline(always)] @@ -1331,6 +1341,16 @@ impl Simd for Avx2 { kernel(self, a, b) } #[inline(always)] + fn swizzle_dyn_u8x16(self, a: u8x16, idxs: u8x16) -> u8x16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: u8x16, idxs: u8x16) -> u8x16 { + _mm_shuffle_epi8(a.into(), idxs.into()).simd_into(token) + } + ); + kernel(self, a, idxs) + } + #[inline(always)] fn widen_u8x16(self, a: u8x16) -> u16x16 { crate::kernel!( #[inline(always)] diff --git a/fearless_simd/src/generated/avx512.rs b/fearless_simd/src/generated/avx512.rs index 976ebd2a..a54b0dfd 100644 --- a/fearless_simd/src/generated/avx512.rs +++ b/fearless_simd/src/generated/avx512.rs @@ -1050,6 +1050,16 @@ impl Simd for Avx512 { kernel(self, a) } #[inline(always)] + fn swizzle_dyn_i8x16(self, a: i8x16, idxs: u8x16) -> i8x16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: i8x16, idxs: u8x16) -> i8x16 { + _mm_shuffle_epi8(a.into(), idxs.into()).simd_into(token) + } + ); + kernel(self, a, idxs) + } + #[inline(always)] fn reinterpret_u8_i8x16(self, a: i8x16) -> u8x16 { crate::kernel!( #[inline(always)] @@ -1486,6 +1496,16 @@ impl Simd for Avx512 { kernel(self, a, b) } #[inline(always)] + fn swizzle_dyn_u8x16(self, a: u8x16, idxs: u8x16) -> u8x16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: u8x16, idxs: u8x16) -> u8x16 { + _mm_shuffle_epi8(a.into(), idxs.into()).simd_into(token) + } + ); + kernel(self, a, idxs) + } + #[inline(always)] fn widen_u8x16(self, a: u8x16) -> u16x16 { crate::kernel!( #[inline(always)] diff --git a/fearless_simd/src/generated/fallback.rs b/fearless_simd/src/generated/fallback.rs index 1024b172..84753a13 100644 --- a/fearless_simd/src/generated/fallback.rs +++ b/fearless_simd/src/generated/fallback.rs @@ -1165,6 +1165,14 @@ impl Simd for Fallback { .simd_into(self) } #[inline(always)] + fn swizzle_dyn_i8x16(self, a: i8x16, idxs: u8x16) -> i8x16 { + let mut dest = [Default::default(); 16]; + for (i, idx) in idxs.val.0.iter().copied().enumerate() { + dest[i] = a.val.0.get(idx as usize).copied().unwrap_or(0); + } + dest.simd_into(self) + } + #[inline(always)] fn reinterpret_u8_i8x16(self, a: i8x16) -> u8x16 { a.bitcast() } @@ -1768,6 +1776,14 @@ impl Simd for Fallback { result.simd_into(self) } #[inline(always)] + fn swizzle_dyn_u8x16(self, a: u8x16, idxs: u8x16) -> u8x16 { + let mut dest = [Default::default(); 16]; + for (i, idx) in idxs.val.0.iter().copied().enumerate() { + dest[i] = a.val.0.get(idx as usize).copied().unwrap_or(0); + } + dest.simd_into(self) + } + #[inline(always)] fn widen_u8x16(self, a: u8x16) -> u16x16 { [ a[0usize] as u16, diff --git a/fearless_simd/src/generated/neon.rs b/fearless_simd/src/generated/neon.rs index 8553ff66..45cc12dc 100644 --- a/fearless_simd/src/generated/neon.rs +++ b/fearless_simd/src/generated/neon.rs @@ -910,6 +910,16 @@ impl Simd for Neon { kernel(self, a) } #[inline(always)] + fn swizzle_dyn_i8x16(self, a: i8x16, idxs: u8x16) -> i8x16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: i8x16, idxs: u8x16) -> i8x16 { + vqtbl1q_s8(a.into(), idxs.into()).simd_into(token) + } + ); + kernel(self, a, idxs) + } + #[inline(always)] fn reinterpret_u8_i8x16(self, a: i8x16) -> u8x16 { crate::kernel!( #[inline(always)] @@ -1266,6 +1276,16 @@ impl Simd for Neon { } } #[inline(always)] + fn swizzle_dyn_u8x16(self, a: u8x16, idxs: u8x16) -> u8x16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: u8x16, idxs: u8x16) -> u8x16 { + vqtbl1q_u8(a.into(), idxs.into()).simd_into(token) + } + ); + kernel(self, a, idxs) + } + #[inline(always)] fn widen_u8x16(self, a: u8x16) -> u16x16 { crate::kernel!( #[inline(always)] diff --git a/fearless_simd/src/generated/simd_trait.rs b/fearless_simd/src/generated/simd_trait.rs index 7690bca3..6ee87dc9 100644 --- a/fearless_simd/src/generated/simd_trait.rs +++ b/fearless_simd/src/generated/simd_trait.rs @@ -306,6 +306,8 @@ pub trait Simd: fn combine_i8x16(self, a: i8x16, b: i8x16) -> i8x32; #[doc = "Negate each element of the vector, wrapping on overflow."] fn neg_i8x16(self, a: i8x16) -> i8x16; + #[doc = "Swizzle a vector of elements according to an index vector.\n\nThe behavior for out-of-bound indices is unspecified and backend-dependent, but it is guaranteed to not panic."] + fn swizzle_dyn_i8x16(self, a: i8x16, idxs: u8x16) -> i8x16; #[doc = "Reinterpret the bits of this vector as a vector of `u8` elements.\n\nThe total bit width is preserved; the number of elements changes accordingly."] fn reinterpret_u8_i8x16(self, a: i8x16) -> u8x16; #[doc = "Reinterpret the bits of this vector as a vector of `u32` elements.\n\nThe total bit width is preserved; the number of elements changes accordingly."] @@ -388,6 +390,8 @@ pub trait Simd: fn max_u8x16(self, a: u8x16, b: u8x16) -> u8x16; #[doc = "Combine two vectors into a single vector with twice the width.\n\n`a` provides the lower elements and `b` provides the upper elements."] fn combine_u8x16(self, a: u8x16, b: u8x16) -> u8x32; + #[doc = "Swizzle a vector of elements according to an index vector.\n\nThe behavior for out-of-bound indices is unspecified and backend-dependent, but it is guaranteed to not panic."] + fn swizzle_dyn_u8x16(self, a: u8x16, idxs: u8x16) -> u8x16; #[doc = "Zero-extend each element to a wider integer type.\n\nThe number of elements in the result is half that of the input."] fn widen_u8x16(self, a: u8x16) -> u16x16; #[doc = "Reinterpret the bits of this vector as a vector of `u32` elements.\n\nThe total bit width is preserved; the number of elements changes accordingly."] diff --git a/fearless_simd/src/generated/simd_types.rs b/fearless_simd/src/generated/simd_types.rs index c05fa1b7..b4b1bd01 100644 --- a/fearless_simd/src/generated/simd_types.rs +++ b/fearless_simd/src/generated/simd_types.rs @@ -5,7 +5,7 @@ use crate::{ Bytes, Select, Simd, SimdBase, SimdCvtFloat, SimdCvtTruncate, SimdFrom, SimdInto, SimdMask, - seal::Seal, + SimdSwizzleDyn, seal::Seal, }; #[doc = "A SIMD vector of 4 [`f32`] elements.\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)] @@ -445,6 +445,13 @@ impl crate::SimdCombine for i8x16 { self.simd.combine_i8x16(self, rhs.simd_into(self.simd)) } } +impl SimdSwizzleDyn for i8x16 { + #[doc = "Swizzle a vector of elements according to an index vector.\n\nThe behavior for out-of-bound indices is unspecified and backend-dependent, but it is guaranteed to not panic."] + #[inline(always)] + fn swizzle_dyn(self, idxs: u8x16) -> Self { + self.simd.swizzle_dyn_i8x16(self, idxs) + } +} #[doc = "A SIMD vector of 16 [`u8`] elements.\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))] @@ -630,6 +637,13 @@ impl crate::SimdCombine for u8x16 { self.simd.combine_u8x16(self, rhs.simd_into(self.simd)) } } +impl SimdSwizzleDyn for u8x16 { + #[doc = "Swizzle a vector of elements according to an index vector.\n\nThe behavior for out-of-bound indices is unspecified and backend-dependent, but it is guaranteed to not panic."] + #[inline(always)] + fn swizzle_dyn(self, idxs: u8x16) -> Self { + self.simd.swizzle_dyn_u8x16(self, idxs) + } +} #[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 { diff --git a/fearless_simd/src/generated/sse4_2.rs b/fearless_simd/src/generated/sse4_2.rs index abbac0c5..865ba9ad 100644 --- a/fearless_simd/src/generated/sse4_2.rs +++ b/fearless_simd/src/generated/sse4_2.rs @@ -975,6 +975,16 @@ impl Simd for Sse4_2 { kernel(self, a) } #[inline(always)] + fn swizzle_dyn_i8x16(self, a: i8x16, idxs: u8x16) -> i8x16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: i8x16, idxs: u8x16) -> i8x16 { + _mm_shuffle_epi8(a.into(), idxs.into()).simd_into(token) + } + ); + kernel(self, a, idxs) + } + #[inline(always)] fn reinterpret_u8_i8x16(self, a: i8x16) -> u8x16 { crate::kernel!( #[inline(always)] @@ -1336,6 +1346,16 @@ impl Simd for Sse4_2 { } } #[inline(always)] + fn swizzle_dyn_u8x16(self, a: u8x16, idxs: u8x16) -> u8x16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: u8x16, idxs: u8x16) -> u8x16 { + _mm_shuffle_epi8(a.into(), idxs.into()).simd_into(token) + } + ); + kernel(self, a, idxs) + } + #[inline(always)] fn widen_u8x16(self, a: u8x16) -> u16x16 { crate::kernel!( #[inline(always)] diff --git a/fearless_simd/src/generated/wasm.rs b/fearless_simd/src/generated/wasm.rs index 09a2c004..fe4d7725 100644 --- a/fearless_simd/src/generated/wasm.rs +++ b/fearless_simd/src/generated/wasm.rs @@ -585,6 +585,10 @@ impl Simd for WasmSimd128 { i8x16_neg(a.into()).simd_into(self) } #[inline(always)] + fn swizzle_dyn_i8x16(self, a: i8x16, idxs: u8x16) -> i8x16 { + i8x16_swizzle(a.into(), idxs.into()).simd_into(self) + } + #[inline(always)] fn reinterpret_u8_i8x16(self, a: i8x16) -> u8x16 { ::from(a).simd_into(self) } @@ -794,6 +798,10 @@ impl Simd for WasmSimd128 { } } #[inline(always)] + fn swizzle_dyn_u8x16(self, a: u8x16, idxs: u8x16) -> u8x16 { + i8x16_swizzle(a.into(), idxs.into()).simd_into(self) + } + #[inline(always)] fn widen_u8x16(self, a: u8x16) -> u16x16 { let low = u16x8_extend_low_u8x16(a.into()); let high = u16x8_extend_high_u8x16(a.into()); diff --git a/fearless_simd/src/traits.rs b/fearless_simd/src/traits.rs index e51b0a5f..c75b3527 100644 --- a/fearless_simd/src/traits.rs +++ b/fearless_simd/src/traits.rs @@ -156,6 +156,11 @@ pub trait SimdCvtFloat: Seal { fn float_from(x: T) -> Self; } +/// Dynamic swizzle for byte vectors. +pub trait SimdSwizzleDyn: SimdBase + Seal { + fn swizzle_dyn(self, idxs: crate::u8x16) -> Self; +} + /// Concatenation of two SIMD vectors. /// /// This is implemented on all vectors 256 bits and lower, producing vectors of up to 512 bits. diff --git a/fearless_simd_gen/src/generic.rs b/fearless_simd_gen/src/generic.rs index 96b710c0..fa55f573 100644 --- a/fearless_simd_gen/src/generic.rs +++ b/fearless_simd_gen/src/generic.rs @@ -229,7 +229,7 @@ pub(crate) fn generic_op(op: &Op, ty: &VecType) -> TokenStream { } } } - OpSig::StoreInterleaved { .. } => { + OpSig::StoreInterleaved { .. } | OpSig::SwizzleDyn => { panic!("The generic fallback is not implemented for this operation") } OpSig::Split { .. } diff --git a/fearless_simd_gen/src/mk_fallback.rs b/fearless_simd_gen/src/mk_fallback.rs index 92099258..7a45b410 100644 --- a/fearless_simd_gen/src/mk_fallback.rs +++ b/fearless_simd_gen/src/mk_fallback.rs @@ -219,6 +219,24 @@ impl Level for Fallback { } } } + OpSig::SwizzleDyn => { + assert!( + matches!(vec_ty.scalar, ScalarType::Unsigned | ScalarType::Int) + && vec_ty.scalar_bits == 8 + && vec_ty.len == 16, + "swizzle_dyn is currently only supported for 8-bit 128-bit vectors" + ); + + quote! { + #method_sig { + let mut dest = [Default::default(); 16]; + for (i, idx) in idxs.val.0.iter().copied().enumerate() { + dest[i] = a.val.0.get(idx as usize).copied().unwrap_or(0); + } + dest.simd_into(self) + } + } + } OpSig::Shift => { let items = make_list( (0..vec_ty.len) diff --git a/fearless_simd_gen/src/mk_neon.rs b/fearless_simd_gen/src/mk_neon.rs index 2cdf737f..4a604c9d 100644 --- a/fearless_simd_gen/src/mk_neon.rs +++ b/fearless_simd_gen/src/mk_neon.rs @@ -294,6 +294,23 @@ impl Level for Neon { quote! { #vbsl(#reinterpret(a.into()), b.into(), c.into()).simd_into(#token) } }) } + OpSig::SwizzleDyn => { + assert!( + matches!(vec_ty.scalar, ScalarType::Unsigned | ScalarType::Int) + && vec_ty.scalar_bits == 8 + && vec_ty.len == 16, + "swizzle_dyn is currently only supported for 8-bit 128-bit vectors" + ); + + let (tbl, idxs) = match vec_ty.scalar { + ScalarType::Unsigned => (quote! { vqtbl1q_u8 }, quote! { idxs.into() }), + ScalarType::Int => (quote! { vqtbl1q_s8 }, quote! { idxs.into() }), + _ => unreachable!(), + }; + self.kernel_method(op, vec_ty, |token| { + quote! { #tbl(a.into(), #idxs).simd_into(#token) } + }) + } OpSig::Combine { combined_ty } => { let combined_wrapper = combined_ty.aligned_wrapper(); let combined_arch_ty = self.arch_ty(&combined_ty); diff --git a/fearless_simd_gen/src/mk_simd_types.rs b/fearless_simd_gen/src/mk_simd_types.rs index b6f2aafc..e39d73c7 100644 --- a/fearless_simd_gen/src/mk_simd_types.rs +++ b/fearless_simd_gen/src/mk_simd_types.rs @@ -8,14 +8,14 @@ use crate::{ generic::generic_op_name, ops::{ F32_TO_I32, F32_TO_I32_PRECISE, F32_TO_U32, F32_TO_U32_PRECISE, I32_TO_F32, Op, OpSig, - TyFlavor, U32_TO_F32, vec_trait_ops_for, + SWIZZLE_DYN, TyFlavor, U32_TO_F32, vec_trait_ops_for, }, types::{SIMD_TYPES, ScalarType, VecType}, }; pub(crate) fn mk_simd_types() -> TokenStream { let mut result = quote! { - use crate::{Bytes, Select, Simd, SimdBase, SimdFrom, SimdInto, SimdMask, SimdCvtFloat, SimdCvtTruncate, seal::Seal}; + use crate::{Bytes, Select, Simd, SimdBase, SimdFrom, SimdInto, SimdMask, SimdCvtFloat, SimdCvtTruncate, SimdSwizzleDyn, seal::Seal}; }; for ty in SIMD_TYPES { let name = ty.rust(); @@ -214,6 +214,23 @@ pub(crate) fn mk_simd_types() -> TokenStream { } }); } + if matches!(ty.scalar, ScalarType::Unsigned | ScalarType::Int) + && ty.scalar_bits == 8 + && ty.len == 16 + { + let swizzle_dyn = generic_op_name("swizzle_dyn", ty); + let idxs_ty = VecType::new(ScalarType::Unsigned, 8, 16).rust(); + let doc = SWIZZLE_DYN.format_docstring(TyFlavor::VecImpl); + conditional_impls.push(quote! { + impl SimdSwizzleDyn for #name { + #[doc = #doc] + #[inline(always)] + fn swizzle_dyn(self, idxs: #idxs_ty) -> Self { + self.simd.#swizzle_dyn(self, idxs) + } + } + }); + } result.extend(quote! { #[doc = #doc] #[derive(Clone, Copy)] diff --git a/fearless_simd_gen/src/mk_wasm.rs b/fearless_simd_gen/src/mk_wasm.rs index f62014bd..f6a04b13 100644 --- a/fearless_simd_gen/src/mk_wasm.rs +++ b/fearless_simd_gen/src/mk_wasm.rs @@ -330,6 +330,20 @@ impl Level for WasmSimd128 { } } } + OpSig::SwizzleDyn => { + assert!( + matches!(vec_ty.scalar, ScalarType::Unsigned | ScalarType::Int) + && vec_ty.scalar_bits == 8 + && vec_ty.len == 16, + "swizzle_dyn is currently only supported for 8-bit 128-bit vectors" + ); + + quote! { + #method_sig { + i8x16_swizzle(a.into(), idxs.into()).simd_into(self) + } + } + } OpSig::Combine { combined_ty } => generic_block_combine(method_sig, &combined_ty, 128), OpSig::Split { half_ty } => generic_block_split(method_sig, &half_ty, 128), OpSig::Zip { select_low } => { diff --git a/fearless_simd_gen/src/mk_x86.rs b/fearless_simd_gen/src/mk_x86.rs index 724c12ef..513b728f 100644 --- a/fearless_simd_gen/src/mk_x86.rs +++ b/fearless_simd_gen/src/mk_x86.rs @@ -306,6 +306,18 @@ impl Level for X86 { OpSig::Zip { select_low } => self.handle_zip(op, vec_ty, select_low), OpSig::Unzip { select_even } => self.handle_unzip(op, vec_ty, select_even), OpSig::Slide { granularity } => self.handle_slide(method_sig, vec_ty, granularity), + OpSig::SwizzleDyn => { + assert!( + matches!(vec_ty.scalar, ScalarType::Unsigned | ScalarType::Int) + && vec_ty.scalar_bits == 8 + && vec_ty.len == 16, + "swizzle_dyn is currently only supported for 8-bit 128-bit vectors" + ); + + self.kernel_method(op, vec_ty, |token| { + quote! { _mm_shuffle_epi8(a.into(), idxs.into()).simd_into(#token) } + }) + } OpSig::Cvt { target_ty, scalar_bits, diff --git a/fearless_simd_gen/src/ops.rs b/fearless_simd_gen/src/ops.rs index 17495b41..48965bb8 100644 --- a/fearless_simd_gen/src/ops.rs +++ b/fearless_simd_gen/src/ops.rs @@ -82,6 +82,10 @@ pub(crate) enum OpSig { Deinterleave, /// Takes two arguments of a vector type, plus a const generic shift amount, and returns that same vector type. Slide { granularity: SlideGranularity }, + /// Takes two arguments of a vector type, and returns a new vector of + /// elements, where the second vector is used as an index into the elements + /// of the first vector. + SwizzleDyn, /// Takes a single argument of the source vector type, and returns a vector type of the target scalar type and the /// same length. Cvt { @@ -304,6 +308,10 @@ impl Op { (vec![vec.clone(), vec.clone()], quote! { (#vec, #vec) }) } OpSig::Slide { .. } => (vec![vec.clone(), vec.clone()], vec), + OpSig::SwizzleDyn => { + let idxs_ty = VecType::new(ScalarType::Unsigned, 8, 16).rust(); + (vec![vec.clone(), quote! { #idxs_ty<#simd_ty> }], vec) + } OpSig::Cvt { target_ty, scalar_bits, @@ -437,6 +445,8 @@ impl Op { let arg2 = &arg_names[2]; quote! { (#arg0, #arg1: impl SimdInto, #arg2: impl SimdInto) -> Self } } + // Currently not implemented for all vector types. + OpSig::SwizzleDyn => return None, // select is currently done by trait, but maybe we'll implement for // masks. OpSig::Select => return None, @@ -1198,6 +1208,15 @@ const NEGATE_INT: Op = Op::new( "Negate each element of the vector, wrapping on overflow.", ); +pub(crate) const SWIZZLE_DYN: Op = Op::new( + "swizzle_dyn", + OpKind::AssociatedOnly, + OpSig::SwizzleDyn, + "Swizzle a vector of elements according to an index vector.\n\n\ + The behavior for out-of-bound indices is unspecified and backend-dependent, \ + but it is guaranteed to not panic.", +); + pub(crate) fn overloaded_ops_for(scalar: ScalarType) -> Vec { let base = match scalar { ScalarType::Float => FLOAT_OPS, @@ -1318,6 +1337,13 @@ pub(crate) fn ops_for_type(ty: &VecType) -> Vec { ops.push(NEGATE_INT); } + if matches!(ty.scalar, ScalarType::Unsigned | ScalarType::Int) + && ty.scalar_bits == 8 + && ty.len == 16 + { + ops.push(SWIZZLE_DYN); + } + if ty.scalar == ScalarType::Float { if ty.scalar_bits == 64 { ops.push(Op::new( @@ -1619,6 +1645,7 @@ impl OpSig { | Self::Interleave | Self::Deinterleave | Self::Slide { .. } => &["a", "b"], + Self::SwizzleDyn => &["a", "idxs"], Self::Ternary | Self::Select => &["a", "b", "c"], Self::Shift => &["a", "shift"], Self::LoadInterleaved { .. } => &["src"], @@ -1635,6 +1662,7 @@ impl OpSig { | Self::MaskToBitmask | Self::MaskSet | Self::FromBytes { .. } + | Self::SwizzleDyn | Self::StoreArray => &[], Self::Unary | Self::Cvt { .. } @@ -1704,7 +1732,8 @@ impl OpSig { | Self::StoreArray | Self::FromBytes | Self::ToBytes - | Self::Slide { .. } => return None, + | Self::Slide { .. } + | Self::SwizzleDyn => return None, }; Some(args) } diff --git a/fearless_simd_tests/tests/harness/mod.rs b/fearless_simd_tests/tests/harness/mod.rs index c250667c..cbc325f5 100644 --- a/fearless_simd_tests/tests/harness/mod.rs +++ b/fearless_simd_tests/tests/harness/mod.rs @@ -40,6 +40,64 @@ fn splat_native_mask(simd: S) { assert!(all_false.all_false()); } +#[simd_test] +fn swizzle_dyn_u8x16(simd: S) { + let bytes = u8x16::from_slice( + simd, + &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + ); + let idxs = u8x16::from_slice(simd, &[15, 14, 13, 12, 0, 0, 7, 7, 8, 9, 10, 4, 3, 2, 1, 0]); + + assert_eq!( + *bytes.swizzle_dyn(idxs), + [15, 14, 13, 12, 0, 0, 7, 7, 8, 9, 10, 4, 3, 2, 1, 0] + ); +} + +#[simd_test] +fn swizzle_dyn_u8x16_oob_indices(simd: S) { + let bytes = u8x16::from_slice( + simd, + &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + ); + let idxs = u8x16::from_slice( + simd, + &[15, 16, 17, 255, 0, 1, 2, 3, 128, 129, 4, 5, 6, 7, 8, 9], + ); + + // Just make sure we don't panic. + let _ = bytes.swizzle_dyn(idxs); +} + +#[simd_test] +fn swizzle_dyn_i8x16(simd: S) { + let bytes = i8x16::from_slice( + simd, + &[0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15], + ); + let idxs = u8x16::from_slice(simd, &[15, 14, 13, 12, 1, 1, 7, 7, 8, 9, 10, 4, 3, 2, 1, 0]); + + assert_eq!( + *bytes.swizzle_dyn(idxs), + [-15, 14, -13, 12, -1, -1, -7, -7, 8, -9, 10, 4, -3, 2, -1, 0] + ); +} + +#[simd_test] +fn swizzle_dyn_i8x16_oob_indices(simd: S) { + let bytes = i8x16::from_slice( + simd, + &[0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15], + ); + let idxs = u8x16::from_slice( + simd, + &[15, 16, 17, 255, 0, 1, 2, 3, 128, 129, 4, 5, 6, 7, 8, 9], + ); + + // Just make sure we don't panic. + let _ = bytes.swizzle_dyn(idxs); +} + #[simd_test] fn abs_f32x4(simd: S) { let a = f32x4::from_slice(simd, &[-1.0, 2.0, -3.0, 4.0]);