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