1#![allow(aarch64_softfloat_neon, x86_softfloat_sse)]
4
5use crate::simd::Simd;
6use core::mem;
7
8impl<const N: usize> Simd<u8, N> {
9 #[inline]
18 pub fn swizzle_dyn(self, idxs: Simd<u8, N>) -> Self {
19 #![allow(unused_imports, unused_unsafe)]
20 #[cfg(all(
21 target_arch = "arm",
22 target_feature = "v7",
23 target_feature = "neon",
24 target_endian = "little"
25 ))]
26 use core::arch::arm::{uint8x8_t, vtbl1_u8};
27 #[cfg(target_arch = "wasm32")]
28 use core::arch::wasm32 as wasm;
29 #[cfg(target_arch = "wasm64")]
30 use core::arch::wasm64 as wasm;
31 #[cfg(target_arch = "x86")]
32 use core::arch::x86;
33 #[cfg(target_arch = "x86_64")]
34 use core::arch::x86_64 as x86;
35 unsafe {
37 #[allow(
38 unreachable_patterns,
39 reason = "avoids writing verbose cfg(not), earlier branches take priority"
40 )]
41 match N {
42 #[cfg(all(
44 any(target_arch = "aarch64", target_arch = "arm64ec"),
45 target_feature = "neon",
46 target_endian = "little"
47 ))]
48 8 | 16 | 24 | 32 | 48 | 64 => aarch64_swizzle(self, idxs),
49
50 #[cfg(all(
52 target_arch = "arm",
53 target_feature = "v7",
54 target_feature = "neon",
55 target_endian = "little"
56 ))]
57 16 => transize(armv7_neon_swizzle_u8x16, self, idxs),
58
59 #[cfg(target_feature = "simd128")]
61 16 => transize(wasm::i8x16_swizzle, self, idxs),
62 #[cfg(target_feature = "simd128")]
63 32 => transize(swizzle_dyn_split::<32, 16>, self, idxs),
64
65 #[cfg(all(target_arch = "loongarch64", target_feature = "lsx"))]
67 16 => transize(loong64_lsx_swizzle, self, idxs),
68 #[cfg(all(target_arch = "loongarch64", target_feature = "lasx"))]
69 32 => transize(loong64_lasx_swizzle, self, idxs),
70 #[cfg(all(target_arch = "loongarch64", target_feature = "lsx"))]
71 32 => transize(swizzle_dyn_split::<32, 16>, self, idxs),
72 #[cfg(all(target_arch = "loongarch64", target_feature = "lasx"))]
73 64 => transize(swizzle_dyn_split::<64, 32>, self, idxs),
74
75 #[cfg(target_feature = "ssse3")]
77 16 => transize(x86::_mm_shuffle_epi8, self, zeroing_idxs(idxs)),
78 #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))]
79 32 => {
80 let swizzler = |bytes, idxs| {
82 let mask = x86::_mm256_cmp_epu8_mask::<{ x86::_MM_CMPINT_LT }>(
83 idxs,
84 Simd::<u8, 32>::splat(N as u8).into(),
85 );
86 x86::_mm256_maskz_permutexvar_epi8(mask, idxs, bytes)
87 };
88 transize(swizzler, self, idxs)
89 }
90 #[cfg(target_feature = "avx2")]
91 32 => transize(avx2_pshufb, self, idxs),
92 #[cfg(target_feature = "ssse3")]
93 32 => transize(swizzle_dyn_split::<32, 16>, self, idxs),
94 #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))]
96 64 => {
97 let swizzler = |bytes, idxs| {
99 let mask = x86::_mm512_cmp_epu8_mask::<{ x86::_MM_CMPINT_LT }>(
100 idxs,
101 Simd::<u8, 64>::splat(N as u8).into(),
102 );
103 x86::_mm512_maskz_permutexvar_epi8(mask, idxs, bytes)
104 };
105 transize(swizzler, self, idxs)
106 }
107 #[cfg(target_feature = "avx2")]
108 64 => transize(swizzle_dyn_split::<64, 32>, self, idxs),
109
110 _ => {
112 let mut array = [0; N];
113 for (i, k) in idxs.to_array().into_iter().enumerate() {
114 if (k as usize) < N {
115 array[i] = self[k as usize];
116 };
117 }
118 array.into()
119 }
120 }
121 }
122 }
123}
124
125#[allow(dead_code, reason = "only used on some targets/features")]
126fn swizzle_dyn_split<const N: usize, const HALF: usize>(
129 bytes: Simd<u8, N>,
130 idxs: Simd<u8, N>,
131) -> Simd<u8, N> {
132 let table_low = bytes.extract::<0, HALF>();
133 let table_high = bytes.extract::<HALF, HALF>();
134 let idxs_low = idxs.extract::<0, HALF>();
135 let idxs_high = idxs.extract::<HALF, HALF>();
136 let table_high_offset = Simd::<u8, HALF>::splat(HALF as u8);
137
138 let output_low_from_low = table_low.swizzle_dyn(idxs_low);
139 let output_low_from_high = table_high.swizzle_dyn(idxs_low - table_high_offset);
140 let output_low = output_low_from_low | output_low_from_high;
141
142 let output_high_from_low = table_low.swizzle_dyn(idxs_high);
143 let output_high_from_high = table_high.swizzle_dyn(idxs_high - table_high_offset);
144 let output_high = output_high_from_low | output_high_from_high;
145
146 use crate::simd::Swizzle;
151 struct CombineHalves;
152 impl<const N: usize> Swizzle<N> for CombineHalves {
153 const INDEX: [usize; N] = const {
154 let mut index = [0; N];
155 let mut i = 0;
156 while i < N {
157 index[i] = i;
158 i += 1;
159 }
160 index
161 };
162 }
163
164 CombineHalves::concat_swizzle(output_low, output_high)
165}
166
167#[cfg(all(
173 target_arch = "arm",
174 target_feature = "v7",
175 target_feature = "neon",
176 target_endian = "little"
177))]
178unsafe fn armv7_neon_swizzle_u8x16(bytes: Simd<u8, 16>, idxs: Simd<u8, 16>) -> Simd<u8, 16> {
179 use core::arch::arm::{uint8x8x2_t, vcombine_u8, vget_high_u8, vget_low_u8, vtbl2_u8};
180 unsafe {
182 let bytes = uint8x8x2_t(vget_low_u8(bytes.into()), vget_high_u8(bytes.into()));
183 let lo = vtbl2_u8(bytes, vget_low_u8(idxs.into()));
184 let hi = vtbl2_u8(bytes, vget_high_u8(idxs.into()));
185 vcombine_u8(lo, hi).into()
186 }
187}
188
189#[cfg(all(
194 any(target_arch = "aarch64", target_arch = "arm64ec"),
195 target_feature = "neon",
196 target_endian = "little"
197))]
198unsafe fn aarch64_swizzle<const N: usize>(bytes: Simd<u8, N>, idxs: Simd<u8, N>) -> Simd<u8, N> {
199 use core::arch::aarch64::*;
200 use core::mem::transmute_copy;
201
202 unsafe {
204 match N {
205 8 => transmute_copy(&vtbl1_u8(transmute_copy(&bytes), transmute_copy(&idxs))),
206 16 => transmute_copy(&vqtbl1q_u8(transmute_copy(&bytes), transmute_copy(&idxs))),
207 24 => {
208 let bytes: uint8x8x3_t = transmute_copy(&bytes);
209 let idxs: uint8x8x3_t = transmute_copy(&idxs);
210
211 let ret0 = vtbl3_u8(bytes, idxs.0);
212 let ret1 = vtbl3_u8(bytes, idxs.1);
213 let ret2 = vtbl3_u8(bytes, idxs.2);
214
215 let ret = uint8x8x3_t(ret0, ret1, ret2);
216 transmute_copy(&ret)
217 }
218 32 => {
219 let bytes: uint8x16x2_t = transmute_copy(&bytes);
220 let idxs: uint8x16x2_t = transmute_copy(&idxs);
221
222 let ret0 = vqtbl2q_u8(bytes, idxs.0);
223 let ret1 = vqtbl2q_u8(bytes, idxs.1);
224
225 let ret = uint8x16x2_t(ret0, ret1);
226 transmute_copy(&ret)
227 }
228 48 => {
229 let bytes: uint8x16x3_t = transmute_copy(&bytes);
230 let idxs: uint8x16x3_t = transmute_copy(&idxs);
231
232 let ret0 = vqtbl3q_u8(bytes, idxs.0);
233 let ret1 = vqtbl3q_u8(bytes, idxs.1);
234 let ret2 = vqtbl3q_u8(bytes, idxs.2);
235
236 let ret = uint8x16x3_t(ret0, ret1, ret2);
237 transmute_copy(&ret)
238 }
239 64 => {
240 let bytes: uint8x16x4_t = transmute_copy(&bytes);
241 let idxs: uint8x16x4_t = transmute_copy(&idxs);
242
243 let ret0 = vqtbl4q_u8(bytes, idxs.0);
244 let ret1 = vqtbl4q_u8(bytes, idxs.1);
245 let ret2 = vqtbl4q_u8(bytes, idxs.2);
246 let ret3 = vqtbl4q_u8(bytes, idxs.3);
247
248 let ret = uint8x16x4_t(ret0, ret1, ret2, ret3);
249 transmute_copy(&ret)
250 }
251 _ => unreachable!(),
252 }
253 }
254}
255
256#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
261#[target_feature(enable = "avx2")]
262#[allow(unused)]
263#[inline]
264#[allow(clippy::let_and_return)]
265unsafe fn avx2_pshufb(bytes: Simd<u8, 32>, idxs: Simd<u8, 32>) -> Simd<u8, 32> {
266 #[cfg(target_arch = "x86")]
267 use core::arch::x86;
268 #[cfg(target_arch = "x86_64")]
269 use core::arch::x86_64 as x86;
270 use x86::_mm256_permute2x128_si256 as avx2_cross_shuffle;
271 use x86::_mm256_shuffle_epi8 as avx2_half_pshufb;
272 unsafe {
274 let lolo = avx2_cross_shuffle::<0x00>(bytes.into(), bytes.into());
275 let hihi = avx2_cross_shuffle::<0x11>(bytes.into(), bytes.into());
276
277 let control = x86::_mm256_adds_epu8(idxs.into(), x86::_mm256_set1_epi8(0x60));
281
282 let select_high = x86::_mm256_slli_epi16::<3>(control);
284 let from_low = avx2_half_pshufb(lolo, control);
285 let from_high = avx2_half_pshufb(hihi, control);
286 x86::_mm256_blendv_epi8(from_low, from_high, select_high).into()
287 }
288}
289
290#[cfg(all(target_arch = "loongarch64", target_feature = "lsx"))]
295unsafe fn loong64_lsx_swizzle(bytes: Simd<u8, 16>, idxs: Simd<u8, 16>) -> Simd<u8, 16> {
296 use core::arch::loongarch64::{lsx_vand_v, lsx_vshuf_b, lsx_vslei_bu};
297 unsafe {
299 let bytes = lsx_vshuf_b(bytes.into(), bytes.into(), idxs.into());
300 let mask = lsx_vslei_bu::<15>(idxs.into());
301 lsx_vand_v(bytes, mask).into()
302 }
303}
304
305#[cfg(all(target_arch = "loongarch64", target_feature = "lasx"))]
310unsafe fn loong64_lasx_swizzle(bytes: Simd<u8, 32>, idxs: Simd<u8, 32>) -> Simd<u8, 32> {
311 use core::arch::loongarch64::{lasx_xvand_v, lasx_xvpermi_q, lasx_xvshuf_b, lasx_xvslei_bu};
312 unsafe {
314 let lolo = lasx_xvpermi_q::<0x00>(bytes.into(), bytes.into());
315 let hihi = lasx_xvpermi_q::<0x11>(bytes.into(), bytes.into());
316 let bytes = lasx_xvshuf_b(hihi, lolo, idxs.into());
317 let mask = lasx_xvslei_bu::<31>(idxs.into());
318 lasx_xvand_v(bytes, mask).into()
319 }
320}
321
322#[allow(dead_code)]
330#[inline(always)]
331unsafe fn transize<T, const N: usize>(
332 f: unsafe fn(T, T) -> T,
333 a: Simd<u8, N>,
334 b: Simd<u8, N>,
335) -> Simd<u8, N> {
336 unsafe { mem::transmute_copy(&f(mem::transmute_copy(&a), mem::transmute_copy(&b))) }
338}
339
340#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
342#[allow(unused)]
343#[inline(always)]
344fn zeroing_idxs<const N: usize>(idxs: Simd<u8, N>) -> Simd<u8, N> {
345 let zeroing_bits = idxs + Simd::splat((127 - N + 1) as u8);
348 idxs | zeroing_bits
349}