Skip to main content

wide/
u8x16_.rs

1use super::*;
2
3pick! {
4  if #[cfg(target_feature="sse2")] {
5    /// A SIMD vector with 16 elements of type [`u8`].
6    ///
7    /// See the [crate level documentation] for more information about SIMD
8    /// vectors.
9    ///
10    /// [crate level documentation]: crate
11    #[derive(Default, Clone, Copy, PartialEq, Eq)]
12    #[repr(C, align(16))]
13    pub struct u8x16 { pub(crate) sse: m128i }
14  } else if #[cfg(target_feature="simd128")] {
15    use core::arch::wasm32::*;
16
17    /// A SIMD vector with 16 elements of type [`u8`].
18    ///
19    /// See the [crate level documentation] for more information about SIMD
20    /// vectors.
21    ///
22    /// [crate level documentation]: crate
23    #[derive(Clone, Copy)]
24    #[repr(transparent)]
25    pub struct u8x16 { pub(crate) simd: v128 }
26
27    impl Default for u8x16 {
28      fn default() -> Self {
29        Self::splat(0)
30      }
31    }
32
33    impl PartialEq for u8x16 {
34      fn eq(&self, other: &Self) -> bool {
35        u8x16_all_true(u8x16_eq(self.simd, other.simd))
36      }
37    }
38
39    impl Eq for u8x16 { }
40  } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
41    use core::arch::aarch64::*;
42
43    /// A SIMD vector with 16 elements of type [`u8`].
44    ///
45    /// See the [crate level documentation] for more information about SIMD
46    /// vectors.
47    ///
48    /// [crate level documentation]: crate
49    #[repr(C)]
50    #[derive(Copy, Clone)]
51    pub struct u8x16 { pub(crate) neon : uint8x16_t }
52
53    impl Default for u8x16 {
54      #[inline]
55      fn default() -> Self {
56        Self::splat(0)
57      }
58    }
59
60    impl PartialEq for u8x16 {
61      #[inline]
62      fn eq(&self, other: &Self) -> bool {
63        unsafe { vminvq_u8(vceqq_u8(self.neon, other.neon))==u8::MAX }
64      }
65    }
66
67    impl Eq for u8x16 { }
68  } else {
69    /// A SIMD vector with 16 elements of type [`u8`].
70    ///
71    /// See the [crate level documentation] for more information about SIMD
72    /// vectors.
73    ///
74    /// [crate level documentation]: crate
75    #[derive(Default, Clone, Copy, PartialEq, Eq)]
76    #[repr(C, align(16))]
77    pub struct u8x16 { pub(crate) arr: [u8;16] }
78  }
79}
80
81impl_simd_uint! {
82  unsafe {
83    T = u8,
84    N = 16,
85    Simd = u8x16,
86    IntSimd = i8x16,
87    T_BITS = 8,
88    T_BITS_MUL_2 = 16,
89    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
90    optional_type_x86_inner { X86Inner = __m128i },
91    optional_type_arm_inner { ArmInner = uint8x16_t },
92    optional_type_wasm_inner { WasmInner = v128 },
93  }
94
95  #[inline]
96  fn not(self) -> Self::Output {
97    self ^ cast::<u128, u8x16>(u128::MAX)
98  }
99
100  #[inline]
101  fn add(self, rhs: Self) -> Self::Output {
102    pick! {
103      if #[cfg(target_feature="sse2")] {
104        Self { sse: add_i8_m128i(self.sse, rhs.sse) }
105      } else if #[cfg(target_feature="simd128")] {
106        Self { simd: u8x16_add(self.simd, rhs.simd) }
107      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
108        unsafe { Self { neon: vaddq_u8(self.neon, rhs.neon) } }
109      } else {
110        Self { arr: [
111          self.arr[0].wrapping_add(rhs.arr[0]),
112          self.arr[1].wrapping_add(rhs.arr[1]),
113          self.arr[2].wrapping_add(rhs.arr[2]),
114          self.arr[3].wrapping_add(rhs.arr[3]),
115          self.arr[4].wrapping_add(rhs.arr[4]),
116          self.arr[5].wrapping_add(rhs.arr[5]),
117          self.arr[6].wrapping_add(rhs.arr[6]),
118          self.arr[7].wrapping_add(rhs.arr[7]),
119          self.arr[8].wrapping_add(rhs.arr[8]),
120          self.arr[9].wrapping_add(rhs.arr[9]),
121          self.arr[10].wrapping_add(rhs.arr[10]),
122          self.arr[11].wrapping_add(rhs.arr[11]),
123          self.arr[12].wrapping_add(rhs.arr[12]),
124          self.arr[13].wrapping_add(rhs.arr[13]),
125          self.arr[14].wrapping_add(rhs.arr[14]),
126          self.arr[15].wrapping_add(rhs.arr[15]),
127        ]}
128      }
129    }
130  }
131
132  #[inline]
133  fn sub(self, rhs: Self) -> Self::Output {
134    pick! {
135      if #[cfg(target_feature="sse2")] {
136        Self { sse: sub_i8_m128i(self.sse, rhs.sse) }
137      } else if #[cfg(target_feature="simd128")] {
138        Self { simd: u8x16_sub(self.simd, rhs.simd) }
139      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
140        unsafe {Self { neon: vsubq_u8(self.neon, rhs.neon) }}
141      } else {
142        Self { arr: [
143          self.arr[0].wrapping_sub(rhs.arr[0]),
144          self.arr[1].wrapping_sub(rhs.arr[1]),
145          self.arr[2].wrapping_sub(rhs.arr[2]),
146          self.arr[3].wrapping_sub(rhs.arr[3]),
147          self.arr[4].wrapping_sub(rhs.arr[4]),
148          self.arr[5].wrapping_sub(rhs.arr[5]),
149          self.arr[6].wrapping_sub(rhs.arr[6]),
150          self.arr[7].wrapping_sub(rhs.arr[7]),
151          self.arr[8].wrapping_sub(rhs.arr[8]),
152          self.arr[9].wrapping_sub(rhs.arr[9]),
153          self.arr[10].wrapping_sub(rhs.arr[10]),
154          self.arr[11].wrapping_sub(rhs.arr[11]),
155          self.arr[12].wrapping_sub(rhs.arr[12]),
156          self.arr[13].wrapping_sub(rhs.arr[13]),
157          self.arr[14].wrapping_sub(rhs.arr[14]),
158          self.arr[15].wrapping_sub(rhs.arr[15]),
159        ]}
160      }
161    }
162  }
163
164  #[inline]
165  fn mul(self, rhs: Self) -> Self::Output {
166    // For x86 and wasm, this technically can be done explicitly by converting
167    // to `i16` then converting back after multiplication, but that may not
168    // actually be faster than auto-vectorization.
169    pick! {
170      if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
171        unsafe { Self { neon: vmulq_u8(self.neon, rhs.neon) } }
172      } else {
173        let self_array: [u8; 16] = cast(self);
174        let rhs_array: [u8; 16] = cast(rhs);
175
176        Self::new([
177          self_array[0].wrapping_mul(rhs_array[0]),
178          self_array[1].wrapping_mul(rhs_array[1]),
179          self_array[2].wrapping_mul(rhs_array[2]),
180          self_array[3].wrapping_mul(rhs_array[3]),
181          self_array[4].wrapping_mul(rhs_array[4]),
182          self_array[5].wrapping_mul(rhs_array[5]),
183          self_array[6].wrapping_mul(rhs_array[6]),
184          self_array[7].wrapping_mul(rhs_array[7]),
185          self_array[8].wrapping_mul(rhs_array[8]),
186          self_array[9].wrapping_mul(rhs_array[9]),
187          self_array[10].wrapping_mul(rhs_array[10]),
188          self_array[11].wrapping_mul(rhs_array[11]),
189          self_array[12].wrapping_mul(rhs_array[12]),
190          self_array[13].wrapping_mul(rhs_array[13]),
191          self_array[14].wrapping_mul(rhs_array[14]),
192          self_array[15].wrapping_mul(rhs_array[15]),
193        ])
194      }
195    }
196  }
197
198  #[inline]
199  fn bitand(self, rhs: Self) -> Self::Output {
200    pick! {
201      if #[cfg(target_feature="sse2")] {
202        Self { sse: bitand_m128i(self.sse, rhs.sse) }
203      } else if #[cfg(target_feature="simd128")] {
204        Self { simd: v128_and(self.simd, rhs.simd) }
205      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
206        unsafe {Self { neon: vandq_u8(self.neon, rhs.neon) }}
207      } else {
208        Self { arr: [
209          self.arr[0].bitand(rhs.arr[0]),
210          self.arr[1].bitand(rhs.arr[1]),
211          self.arr[2].bitand(rhs.arr[2]),
212          self.arr[3].bitand(rhs.arr[3]),
213          self.arr[4].bitand(rhs.arr[4]),
214          self.arr[5].bitand(rhs.arr[5]),
215          self.arr[6].bitand(rhs.arr[6]),
216          self.arr[7].bitand(rhs.arr[7]),
217          self.arr[8].bitand(rhs.arr[8]),
218          self.arr[9].bitand(rhs.arr[9]),
219          self.arr[10].bitand(rhs.arr[10]),
220          self.arr[11].bitand(rhs.arr[11]),
221          self.arr[12].bitand(rhs.arr[12]),
222          self.arr[13].bitand(rhs.arr[13]),
223          self.arr[14].bitand(rhs.arr[14]),
224          self.arr[15].bitand(rhs.arr[15]),
225        ]}
226      }
227    }
228  }
229
230  #[inline]
231  fn bitor(self, rhs: Self) -> Self::Output {
232    pick! {
233      if #[cfg(target_feature="sse2")] {
234        Self { sse: bitor_m128i(self.sse, rhs.sse) }
235      } else if #[cfg(target_feature="simd128")] {
236        Self { simd: v128_or(self.simd, rhs.simd) }
237      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
238        unsafe {Self { neon: vorrq_u8(self.neon, rhs.neon) }}
239      } else {
240        Self { arr: [
241          self.arr[0].bitor(rhs.arr[0]),
242          self.arr[1].bitor(rhs.arr[1]),
243          self.arr[2].bitor(rhs.arr[2]),
244          self.arr[3].bitor(rhs.arr[3]),
245          self.arr[4].bitor(rhs.arr[4]),
246          self.arr[5].bitor(rhs.arr[5]),
247          self.arr[6].bitor(rhs.arr[6]),
248          self.arr[7].bitor(rhs.arr[7]),
249          self.arr[8].bitor(rhs.arr[8]),
250          self.arr[9].bitor(rhs.arr[9]),
251          self.arr[10].bitor(rhs.arr[10]),
252          self.arr[11].bitor(rhs.arr[11]),
253          self.arr[12].bitor(rhs.arr[12]),
254          self.arr[13].bitor(rhs.arr[13]),
255          self.arr[14].bitor(rhs.arr[14]),
256          self.arr[15].bitor(rhs.arr[15]),
257        ]}
258      }
259    }
260  }
261
262  #[inline]
263  fn bitxor(self, rhs: Self) -> Self::Output {
264    pick! {
265      if #[cfg(target_feature="sse2")] {
266        Self { sse: bitxor_m128i(self.sse, rhs.sse) }
267      } else if #[cfg(target_feature="simd128")] {
268        Self { simd: v128_xor(self.simd, rhs.simd) }
269      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
270        unsafe {Self { neon: veorq_u8(self.neon, rhs.neon) }}
271      } else {
272        Self { arr: [
273          self.arr[0].bitxor(rhs.arr[0]),
274          self.arr[1].bitxor(rhs.arr[1]),
275          self.arr[2].bitxor(rhs.arr[2]),
276          self.arr[3].bitxor(rhs.arr[3]),
277          self.arr[4].bitxor(rhs.arr[4]),
278          self.arr[5].bitxor(rhs.arr[5]),
279          self.arr[6].bitxor(rhs.arr[6]),
280          self.arr[7].bitxor(rhs.arr[7]),
281          self.arr[8].bitxor(rhs.arr[8]),
282          self.arr[9].bitxor(rhs.arr[9]),
283          self.arr[10].bitxor(rhs.arr[10]),
284          self.arr[11].bitxor(rhs.arr[11]),
285          self.arr[12].bitxor(rhs.arr[12]),
286          self.arr[13].bitxor(rhs.arr[13]),
287          self.arr[14].bitxor(rhs.arr[14]),
288          self.arr[15].bitxor(rhs.arr[15]),
289        ]}
290      }
291    }
292  }
293
294  #[inline]
295  fn simd_eq(self, rhs: Self) -> Self::Output {
296    pick! {
297      if #[cfg(target_feature="sse2")] {
298        Self { sse: cmp_eq_mask_i8_m128i(self.sse, rhs.sse) }
299      } else if #[cfg(target_feature="simd128")] {
300        Self { simd: u8x16_eq(self.simd, rhs.simd) }
301      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
302        unsafe {Self { neon: vceqq_u8(self.neon, rhs.neon) }}
303      } else {
304        Self { arr: [
305          if self.arr[0] == rhs.arr[0] { u8::MAX } else { 0 },
306          if self.arr[1] == rhs.arr[1] { u8::MAX } else { 0 },
307          if self.arr[2] == rhs.arr[2] { u8::MAX } else { 0 },
308          if self.arr[3] == rhs.arr[3] { u8::MAX } else { 0 },
309          if self.arr[4] == rhs.arr[4] { u8::MAX } else { 0 },
310          if self.arr[5] == rhs.arr[5] { u8::MAX } else { 0 },
311          if self.arr[6] == rhs.arr[6] { u8::MAX } else { 0 },
312          if self.arr[7] == rhs.arr[7] { u8::MAX } else { 0 },
313          if self.arr[8] == rhs.arr[8] { u8::MAX } else { 0 },
314          if self.arr[9] == rhs.arr[9] { u8::MAX } else { 0 },
315          if self.arr[10] == rhs.arr[10] { u8::MAX } else { 0 },
316          if self.arr[11] == rhs.arr[11] { u8::MAX } else { 0 },
317          if self.arr[12] == rhs.arr[12] { u8::MAX } else { 0 },
318          if self.arr[13] == rhs.arr[13] { u8::MAX } else { 0 },
319          if self.arr[14] == rhs.arr[14] { u8::MAX } else { 0 },
320          if self.arr[15] == rhs.arr[15] { u8::MAX } else { 0 },
321        ]}
322      }
323    }
324  }
325
326  #[inline]
327  fn simd_ne(self, rhs: Self) -> Self::Output {
328    pick! {
329      if #[cfg(target_feature="sse2")] {
330        !self.simd_eq(rhs)
331      } else if #[cfg(target_feature="simd128")] {
332        Self { simd: u8x16_ne(self.simd, rhs.simd) }
333      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
334        !self.simd_eq(rhs)
335      } else {
336        Self { arr: [
337          if self.arr[0] != rhs.arr[0] { u8::MAX } else { 0 },
338          if self.arr[1] != rhs.arr[1] { u8::MAX } else { 0 },
339          if self.arr[2] != rhs.arr[2] { u8::MAX } else { 0 },
340          if self.arr[3] != rhs.arr[3] { u8::MAX } else { 0 },
341          if self.arr[4] != rhs.arr[4] { u8::MAX } else { 0 },
342          if self.arr[5] != rhs.arr[5] { u8::MAX } else { 0 },
343          if self.arr[6] != rhs.arr[6] { u8::MAX } else { 0 },
344          if self.arr[7] != rhs.arr[7] { u8::MAX } else { 0 },
345          if self.arr[8] != rhs.arr[8] { u8::MAX } else { 0 },
346          if self.arr[9] != rhs.arr[9] { u8::MAX } else { 0 },
347          if self.arr[10] != rhs.arr[10] { u8::MAX } else { 0 },
348          if self.arr[11] != rhs.arr[11] { u8::MAX } else { 0 },
349          if self.arr[12] != rhs.arr[12] { u8::MAX } else { 0 },
350          if self.arr[13] != rhs.arr[13] { u8::MAX } else { 0 },
351          if self.arr[14] != rhs.arr[14] { u8::MAX } else { 0 },
352          if self.arr[15] != rhs.arr[15] { u8::MAX } else { 0 },
353        ]}
354      }
355    }
356  }
357
358  #[inline]
359  fn simd_lt(self, rhs: Self) -> Self::Output {
360    pick! {
361      if #[cfg(target_feature="sse2")] {
362        // Convert from u8 to i8.
363        let offset = Self::splat(0x80);
364        let self_i8 = self.bitxor(offset).sse;
365        let rhs_i8 = rhs.bitxor(offset).sse;
366        Self { sse: cmp_lt_mask_i8_m128i(self_i8, rhs_i8) }
367      } else if #[cfg(target_feature="simd128")] {
368        Self { simd: u8x16_lt(self.simd, rhs.simd) }
369      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
370        unsafe {Self { neon: vcltq_u8(self.neon, rhs.neon) }}
371      } else {
372        Self { arr: [
373          if self.arr[0] < rhs.arr[0] { u8::MAX } else { 0 },
374          if self.arr[1] < rhs.arr[1] { u8::MAX } else { 0 },
375          if self.arr[2] < rhs.arr[2] { u8::MAX } else { 0 },
376          if self.arr[3] < rhs.arr[3] { u8::MAX } else { 0 },
377          if self.arr[4] < rhs.arr[4] { u8::MAX } else { 0 },
378          if self.arr[5] < rhs.arr[5] { u8::MAX } else { 0 },
379          if self.arr[6] < rhs.arr[6] { u8::MAX } else { 0 },
380          if self.arr[7] < rhs.arr[7] { u8::MAX } else { 0 },
381          if self.arr[8] < rhs.arr[8] { u8::MAX } else { 0 },
382          if self.arr[9] < rhs.arr[9] { u8::MAX } else { 0 },
383          if self.arr[10] < rhs.arr[10] { u8::MAX } else { 0 },
384          if self.arr[11] < rhs.arr[11] { u8::MAX } else { 0 },
385          if self.arr[12] < rhs.arr[12] { u8::MAX } else { 0 },
386          if self.arr[13] < rhs.arr[13] { u8::MAX } else { 0 },
387          if self.arr[14] < rhs.arr[14] { u8::MAX } else { 0 },
388          if self.arr[15] < rhs.arr[15] { u8::MAX } else { 0 },
389        ]}
390      }
391    }
392  }
393
394  #[inline]
395  fn simd_gt(self, rhs: Self) -> Self::Output {
396    pick! {
397      if #[cfg(target_feature="sse2")] {
398        // Convert from u8 to i8.
399        let offset = Self::splat(0x80);
400        let self_i8 = self.bitxor(offset).sse;
401        let rhs_i8 = rhs.bitxor(offset).sse;
402        Self { sse: cmp_gt_mask_i8_m128i(self_i8, rhs_i8) }
403      } else if #[cfg(target_feature="simd128")] {
404        Self { simd: u8x16_gt(self.simd, rhs.simd) }
405      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
406        unsafe {Self { neon: vcgtq_u8(self.neon, rhs.neon) }}
407      } else {
408        Self { arr: [
409          if self.arr[0] > rhs.arr[0] { u8::MAX } else { 0 },
410          if self.arr[1] > rhs.arr[1] { u8::MAX } else { 0 },
411          if self.arr[2] > rhs.arr[2] { u8::MAX } else { 0 },
412          if self.arr[3] > rhs.arr[3] { u8::MAX } else { 0 },
413          if self.arr[4] > rhs.arr[4] { u8::MAX } else { 0 },
414          if self.arr[5] > rhs.arr[5] { u8::MAX } else { 0 },
415          if self.arr[6] > rhs.arr[6] { u8::MAX } else { 0 },
416          if self.arr[7] > rhs.arr[7] { u8::MAX } else { 0 },
417          if self.arr[8] > rhs.arr[8] { u8::MAX } else { 0 },
418          if self.arr[9] > rhs.arr[9] { u8::MAX } else { 0 },
419          if self.arr[10] > rhs.arr[10] { u8::MAX } else { 0 },
420          if self.arr[11] > rhs.arr[11] { u8::MAX } else { 0 },
421          if self.arr[12] > rhs.arr[12] { u8::MAX } else { 0 },
422          if self.arr[13] > rhs.arr[13] { u8::MAX } else { 0 },
423          if self.arr[14] > rhs.arr[14] { u8::MAX } else { 0 },
424          if self.arr[15] > rhs.arr[15] { u8::MAX } else { 0 },
425        ]}
426      }
427    }
428  }
429
430  #[inline]
431  fn simd_le(self, rhs: Self) -> Self::Output {
432    pick! {
433      if #[cfg(target_feature="sse2")] {
434        // Convert from u8 to i8.
435        let offset = Self::splat(0x80);
436        let self_i8 = self.bitxor(offset).sse;
437        let rhs_i8 = rhs.bitxor(offset).sse;
438        // a <= b  is equivalent to  !(b < a)  or  !(a > b)
439        let gt_mask = u8x16 { sse: cmp_gt_mask_i8_m128i(self_i8, rhs_i8) };
440        Self { sse: gt_mask.bitxor(u8x16::splat(0xFF)).sse }
441      } else if #[cfg(target_feature="simd128")] {
442        Self { simd: u8x16_le(self.simd, rhs.simd) }
443      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
444        unsafe {Self { neon: vcleq_u8(self.neon, rhs.neon) }}
445      } else {
446        Self { arr: [
447          if self.arr[0] <= rhs.arr[0] { u8::MAX } else { 0 },
448          if self.arr[1] <= rhs.arr[1] { u8::MAX } else { 0 },
449          if self.arr[2] <= rhs.arr[2] { u8::MAX } else { 0 },
450          if self.arr[3] <= rhs.arr[3] { u8::MAX } else { 0 },
451          if self.arr[4] <= rhs.arr[4] { u8::MAX } else { 0 },
452          if self.arr[5] <= rhs.arr[5] { u8::MAX } else { 0 },
453          if self.arr[6] <= rhs.arr[6] { u8::MAX } else { 0 },
454          if self.arr[7] <= rhs.arr[7] { u8::MAX } else { 0 },
455          if self.arr[8] <= rhs.arr[8] { u8::MAX } else { 0 },
456          if self.arr[9] <= rhs.arr[9] { u8::MAX } else { 0 },
457          if self.arr[10] <= rhs.arr[10] { u8::MAX } else { 0 },
458          if self.arr[11] <= rhs.arr[11] { u8::MAX } else { 0 },
459          if self.arr[12] <= rhs.arr[12] { u8::MAX } else { 0 },
460          if self.arr[13] <= rhs.arr[13] { u8::MAX } else { 0 },
461          if self.arr[14] <= rhs.arr[14] { u8::MAX } else { 0 },
462          if self.arr[15] <= rhs.arr[15] { u8::MAX } else { 0 },
463        ]}
464      }
465    }
466  }
467
468  #[inline]
469  fn simd_ge(self, rhs: Self) -> Self::Output {
470    pick! {
471      if #[cfg(target_feature="sse2")] {
472        // Convert from u8 to i8.
473        let offset = Self::splat(0x80);
474        let self_i8 = self.bitxor(offset).sse;
475        let rhs_i8 = rhs.bitxor(offset).sse;
476        // a >= b  is equivalent to  !(b > a)  or  !(a < b)
477        let lt_mask = u8x16 { sse: cmp_lt_mask_i8_m128i(self_i8, rhs_i8) };
478        Self { sse: lt_mask.bitxor(u8x16::splat(0xFF)).sse }
479      } else if #[cfg(target_feature="simd128")] {
480        Self { simd: u8x16_ge(self.simd, rhs.simd) }
481      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
482        unsafe {Self { neon: vcgeq_u8(self.neon, rhs.neon) }}
483      } else {
484        Self { arr: [
485          if self.arr[0] >= rhs.arr[0] { u8::MAX } else { 0 },
486          if self.arr[1] >= rhs.arr[1] { u8::MAX } else { 0 },
487          if self.arr[2] >= rhs.arr[2] { u8::MAX } else { 0 },
488          if self.arr[3] >= rhs.arr[3] { u8::MAX } else { 0 },
489          if self.arr[4] >= rhs.arr[4] { u8::MAX } else { 0 },
490          if self.arr[5] >= rhs.arr[5] { u8::MAX } else { 0 },
491          if self.arr[6] >= rhs.arr[6] { u8::MAX } else { 0 },
492          if self.arr[7] >= rhs.arr[7] { u8::MAX } else { 0 },
493          if self.arr[8] >= rhs.arr[8] { u8::MAX } else { 0 },
494          if self.arr[9] >= rhs.arr[9] { u8::MAX } else { 0 },
495          if self.arr[10] >= rhs.arr[10] { u8::MAX } else { 0 },
496          if self.arr[11] >= rhs.arr[11] { u8::MAX } else { 0 },
497          if self.arr[12] >= rhs.arr[12] { u8::MAX } else { 0 },
498          if self.arr[13] >= rhs.arr[13] { u8::MAX } else { 0 },
499          if self.arr[14] >= rhs.arr[14] { u8::MAX } else { 0 },
500          if self.arr[15] >= rhs.arr[15] { u8::MAX } else { 0 },
501        ]}
502      }
503    }
504  }
505
506  #[inline]
507  pub fn reduce_add(self) -> u8 {
508    #[allow(dead_code)]
509    const SHUFFLE_1: [u8; 16] =
510      [8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0];
511    #[allow(dead_code)]
512    const SHUFFLE_2: [u8; 16] =
513      [4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0];
514    #[allow(dead_code)]
515    const SHUFFLE_3: [u8; 16] =
516      [2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
517    #[allow(dead_code)]
518    const SHUFFLE_4: [u8; 16] =
519      [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
520
521    pick! {
522      if #[cfg(target_feature="ssse3")] {
523        let rhs = shuffle_av_i8z_all_m128i(self.sse, m128i::from(SHUFFLE_1));
524        let sum = add_i8_m128i(self.sse, rhs);
525        let rhs = shuffle_av_i8z_all_m128i(sum, m128i::from(SHUFFLE_2));
526        let sum = add_i8_m128i(sum, rhs);
527        let rhs = shuffle_av_i8z_all_m128i(sum, m128i::from(SHUFFLE_3));
528        let sum = add_i8_m128i(sum, rhs);
529        let rhs = shuffle_av_i8z_all_m128i(sum, m128i::from(SHUFFLE_4));
530        let sum = add_i8_m128i(sum, rhs);
531        get_i32_from_m128i_s(sum) as u8
532      } else if #[cfg(target_feature="simd128")] {
533        let rhs = u8x16_shuffle::<8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7>(self.simd, self.simd);
534        let sum = u8x16_add(self.simd, rhs);
535        let rhs = u8x16_shuffle::<4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0>(sum, sum);
536        let sum = u8x16_add(sum, rhs);
537        let rhs = u8x16_shuffle::<2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>(sum, sum);
538        let sum = u8x16_add(sum, rhs);
539        let rhs = u8x16_shuffle::<1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>(sum, sum);
540        let sum = u8x16_add(sum, rhs);
541        u8x16_extract_lane::<0>(sum)
542      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
543        unsafe {
544          // Use `transmute` instead of `cast` because `uint8x16_t` does not
545          // implement `bytemuck::Pod`.
546          let rhs = vqtbl1q_u8(self.neon, core::mem::transmute(SHUFFLE_1));
547          let sum = vaddq_u8(self.neon, rhs);
548          let rhs = vqtbl1q_u8(sum, core::mem::transmute(SHUFFLE_2));
549          let sum = vaddq_u8(sum, rhs);
550          let rhs = vqtbl1q_u8(sum, core::mem::transmute(SHUFFLE_3));
551          let sum = vaddq_u8(sum, rhs);
552          let rhs = vqtbl1q_u8(sum, core::mem::transmute(SHUFFLE_4));
553          let sum = vaddq_u8(sum, rhs);
554          vgetq_lane_u8(sum, 0)
555        }
556      } else {
557        let array: [u8; 16] = cast(self);
558        array.into_iter().reduce(u8::wrapping_add).unwrap()
559      }
560    }
561  }
562
563  #[inline]
564  pub fn reduce_mul(self) -> u8 {
565    pick! {
566      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
567        const HIGH_64: [u8; 16] = [8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0];
568        const HIGH_32: [u8; 16] = [4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0];
569        const HIGH_16: [u8; 16] = [2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
570        const HIGH_8: [u8; 16] = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
571
572        unsafe {
573          // Use `transmute` instead of `cast` because `int8x16_t` does not
574          // implement `bytemuck::Pod`.
575          let high_64 = vqtbl1q_u8(self.neon, core::mem::transmute(HIGH_64));
576          let reduce_64 = vmulq_u8(self.neon, high_64);
577          let high_32 = vqtbl1q_u8(reduce_64, core::mem::transmute(HIGH_32));
578          let reduce_32 = vmulq_u8(reduce_64, high_32);
579          let high_16 = vqtbl1q_u8(reduce_32, core::mem::transmute(HIGH_16));
580          let reduce_16 = vmulq_u8(reduce_32, high_16);
581          let high_8 = vqtbl1q_u8(reduce_16, core::mem::transmute(HIGH_8));
582          let reduce_8 = vmulq_u8(reduce_16, high_8);
583          vgetq_lane_u8::<0>(reduce_8)
584        }
585      } else {
586        self.to_array().into_iter().reduce(u8::wrapping_mul).unwrap()
587      }
588    }
589  }
590
591  #[inline]
592  pub fn bitselect(self, if_one: Self, if_zero: Self) -> Self {
593    pick! {
594      if #[cfg(target_feature="sse2")] {
595        Self {
596          sse: bitor_m128i(
597            bitand_m128i(if_one.sse, self.sse),
598            bitandnot_m128i(self.sse, if_zero.sse),
599          ),
600        }
601      } else if #[cfg(target_feature="simd128")] {
602        Self { simd: v128_bitselect(if_one.simd, if_zero.simd, self.simd) }
603      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
604        unsafe {Self { neon: vbslq_u8(self.neon, if_one.neon, if_zero.neon) }}
605      } else {
606        generic_bit_blend(self, if_one, if_zero)
607      }
608    }
609  }
610
611  #[inline]
612  fn select(self, if_true: Self, if_false: Self) -> Self {
613    pick! {
614      if #[cfg(target_feature="sse4.1")] {
615        Self { sse: blend_varying_i8_m128i(if_false.sse, if_true.sse, self.sse) }
616      } else if #[cfg(target_feature="simd128")] {
617        Self { simd: v128_bitselect(if_true.simd, if_false.simd, self.simd) }
618      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
619        unsafe {Self { neon: vbslq_u8(self.neon, if_true.neon, if_false.neon) }}
620      } else {
621        generic_bit_blend(self, if_true, if_false)
622      }
623    }
624  }
625
626  #[inline]
627  pub fn to_bitmask(self) -> u32 {
628    pick! {
629      if #[cfg(target_feature="sse2")] {
630        move_mask_i8_m128i(self.sse) as u32
631      } else if #[cfg(target_feature="simd128")] {
632        u8x16_bitmask(self.simd) as u32
633      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
634        unsafe {
635          // set all to 1 if top bit is set, else 0
636          let masked = vcltzq_s8(self.cast_signed().neon);
637
638          // select the right bit out of each lane
639          let selectbit : uint8x16_t = core::mem::transmute([1u8, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128]);
640          let out = vandq_u8(masked, selectbit);
641
642          // interleave the lanes so that a 16-bit sum accumulates the bits in the right order
643          let table : uint8x16_t = core::mem::transmute([0u8, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15]);
644          let r = vqtbl1q_u8(out, table);
645
646          // horizontally add the 16-bit lanes
647          vaddvq_u16(vreinterpretq_u16_u8(r)) as u32
648        }
649       } else {
650        ((self.arr[0].cast_signed() < 0) as u32) |
651        ((self.arr[1].cast_signed() < 0) as u32) << 1 |
652        ((self.arr[2].cast_signed() < 0) as u32) << 2 |
653        ((self.arr[3].cast_signed() < 0) as u32) << 3 |
654        ((self.arr[4].cast_signed() < 0) as u32) << 4 |
655        ((self.arr[5].cast_signed() < 0) as u32) << 5 |
656        ((self.arr[6].cast_signed() < 0) as u32) << 6 |
657        ((self.arr[7].cast_signed() < 0) as u32) << 7 |
658        ((self.arr[8].cast_signed() < 0) as u32) << 8 |
659        ((self.arr[9].cast_signed() < 0) as u32) << 9 |
660        ((self.arr[10].cast_signed() < 0) as u32) << 10 |
661        ((self.arr[11].cast_signed() < 0) as u32) << 11 |
662        ((self.arr[12].cast_signed() < 0) as u32) << 12 |
663        ((self.arr[13].cast_signed() < 0) as u32) << 13 |
664        ((self.arr[14].cast_signed() < 0) as u32) << 14 |
665        ((self.arr[15].cast_signed() < 0) as u32) << 15
666      }
667    }
668  }
669
670  #[inline]
671  pub fn any(self) -> bool {
672    pick! {
673      if #[cfg(target_feature="sse2")] {
674        move_mask_i8_m128i(self.sse) != 0
675      } else if #[cfg(target_feature="simd128")] {
676        u8x16_bitmask(self.simd) != 0
677      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
678        unsafe {
679          vminvq_s8(self.cast_signed().neon) < 0
680        }
681      } else {
682        let v : [u64;2] = cast(self);
683        ((v[0] | v[1]) & 0x8080808080808080) != 0
684      }
685    }
686  }
687
688  #[inline]
689  pub fn all(self) -> bool {
690    pick! {
691      if #[cfg(target_feature="sse2")] {
692        move_mask_i8_m128i(self.sse) == 0b1111_1111_1111_1111
693      } else if #[cfg(target_feature="simd128")] {
694        u8x16_bitmask(self.simd) == 0b1111_1111_1111_1111
695      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
696        unsafe {
697          vmaxvq_s8(self.cast_signed().neon) < 0
698        }
699      } else {
700        let v : [u64;2] = cast(self);
701        (v[0] & v[1] & 0x8080808080808080) == 0x8080808080808080
702      }
703    }
704  }
705
706  #[inline]
707  pub fn shuffle(self, indices: u8x16) -> Self {
708    pick! {
709      if #[cfg(target_feature="ssse3")] {
710        Self { sse: shuffle_av_i8z_all_m128i(self.sse, indices.sse) }
711      } else if #[cfg(target_feature="relaxed-simd")] {
712        Self { simd: u8x16_relaxed_swizzle(self.simd, indices.simd) }
713      } else if #[cfg(target_feature="simd128")] {
714        Self { simd: u8x16_swizzle(self.simd, indices.simd) }
715      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
716        unsafe { Self { neon: vqtbl1q_u8(self.neon, indices.neon) } }
717      } else {
718        let self_array = self.to_array();
719        let indices_array = indices.to_array();
720
721        let mut result = [0; 16];
722        for i in 0..16 {
723          let index = indices_array[i] as usize;
724          if index < 16 {
725            result[i] = self_array[index];
726          }
727        }
728
729        Self::new(result)
730      }
731    }
732  }
733
734  #[inline]
735  pub fn shuffle_zeroing(self, indices: u8x16) -> Self {
736    pick! {
737      if #[cfg(target_feature="ssse3")] {
738        Self {
739          sse: shuffle_av_i8z_all_m128i(
740            self.sse,
741            add_saturating_u8_m128i(indices.sse, set_splat_i8_m128i(0x70)),
742          ),
743        }
744      } else if #[cfg(target_feature="simd128")] {
745        Self { simd: i8x16_swizzle(self.simd, indices.simd) }
746      } else {
747        // The remaining branches of `shuffle` already have the behavior we want
748        self.shuffle(indices)
749      }
750    }
751  }
752
753  #[inline]
754  pub fn shuffle_wrapping(self, indices: u8x16) -> Self {
755    pick! {
756      if #[cfg(all(target_feature = "avx512vbmi", target_feature = "avx512vl"))] {
757        #[cfg(target_arch = "x86")]
758        use core::arch::x86::_mm_permutexvar_epi8;
759        #[cfg(target_arch = "x86_64")]
760        use core::arch::x86_64::_mm_permutexvar_epi8;
761
762        // TODO(safe_arch): add `_mm_permutexvar_epi8`.
763        Self { sse: unsafe { m128i(_mm_permutexvar_epi8(indices.sse.0, self.sse.0)) } }
764      } else {
765        self.shuffle(indices & 15)
766      }
767    }
768  }
769
770  #[inline]
771  fn shuffle(self: [u8x16; 2], indices: u8x16) -> u8x16 {
772    pick! {
773      if #[cfg(all(target_feature = "avx512vbmi", target_feature = "avx512vl"))] {
774        #[cfg(target_arch = "x86")]
775        use core::arch::x86::_mm_permutex2var_epi8;
776        #[cfg(target_arch = "x86_64")]
777        use core::arch::x86_64::_mm_permutex2var_epi8;
778
779        // TODO(safe_arch): add `_mm_permutex2var_epi8`.
780        u8x16 {
781          sse: unsafe {
782            m128i(_mm_permutex2var_epi8(self[0].sse.0, indices.sse.0, self[1].sse.0))
783          },
784        }
785      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
786        let table = uint8x16x2_t(self[0].neon, self[1].neon);
787        unsafe { u8x16 { neon: vqtbl2q_u8(table, indices.neon) } }
788      } else {
789        self[0].shuffle_zeroing(indices) | self[1].shuffle_zeroing(indices - 16)
790      }
791    }
792  }
793
794  #[inline]
795  fn shuffle_zeroing(self: [u8x16; 2], indices: u8x16) -> u8x16 {
796    pick! {
797      if #[cfg(all(target_feature = "avx512vbmi", target_feature = "avx512vl"))] {
798        self.shuffle(indices) & indices.simd_lt(32)
799      } else {
800        self.shuffle(indices)
801      }
802    }
803  }
804
805  #[inline]
806  fn shuffle_wrapping(self: [u8x16; 2], indices: u8x16) -> u8x16 {
807    pick! {
808      if #[cfg(all(target_feature = "avx512vbmi", target_feature = "avx512vl"))] {
809        // `avx512` shuffle intrinsics are wrapping
810        self.shuffle(indices)
811      } else {
812        self.shuffle(indices & 31)
813      }
814    }
815  }
816
817  #[inline]
818  fn shuffle(self: [u8x16; 3], indices: u8x16) -> u8x16 {
819    pick! {
820      if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
821        let table = uint8x16x3_t(self[0].neon, self[1].neon, self[2].neon);
822        unsafe { u8x16 { neon: vqtbl3q_u8(table, indices.neon) } }
823      } else {
824        [self[0], self[1]].shuffle_zeroing(indices) | self[2].shuffle_zeroing(indices - 32)
825      }
826    }
827  }
828
829  #[inline]
830  fn shuffle_zeroing(self: [u8x16; 3], indices: u8x16) -> u8x16 {
831    self.shuffle(indices)
832  }
833
834  #[inline]
835  fn shuffle_wrapping(self: [u8x16; 3], indices: u8x16) -> u8x16 {
836    self.shuffle(indices % 48)
837  }
838
839  #[inline]
840  fn shuffle(self: [u8x16; 4], indices: u8x16) -> u8x16 {
841    pick! {
842      if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
843        let table = uint8x16x4_t(self[0].neon, self[1].neon, self[2].neon, self[3].neon);
844        unsafe { u8x16 { neon: vqtbl4q_u8(table, indices.neon) } }
845      } else {
846        [self[0], self[1]].shuffle_zeroing(indices)
847          | [self[2], self[3]].shuffle_zeroing(indices - 32)
848      }
849    }
850  }
851
852  #[inline]
853  fn shuffle_zeroing(self: [u8x16; 4], indices: u8x16) -> u8x16 {
854    self.shuffle(indices)
855  }
856
857  #[inline]
858  fn shuffle_wrapping(self: [u8x16; 4], indices: u8x16) -> u8x16 {
859    self.shuffle(indices & 63)
860  }
861
862  ///
863  /// Currently this function is never accelerated.
864  #[inline]
865  pub fn transpose(data: [Self; 16]) -> [Self; 16] {
866    // Can this be optimized?
867
868    #[inline(always)]
869    fn transpose_column(data: &[u8x16; 16], index: usize) -> u8x16 {
870      u8x16::new([
871        data[0].as_array()[index],
872        data[1].as_array()[index],
873        data[2].as_array()[index],
874        data[3].as_array()[index],
875        data[4].as_array()[index],
876        data[5].as_array()[index],
877        data[6].as_array()[index],
878        data[7].as_array()[index],
879        data[8].as_array()[index],
880        data[9].as_array()[index],
881        data[10].as_array()[index],
882        data[11].as_array()[index],
883        data[12].as_array()[index],
884        data[13].as_array()[index],
885        data[14].as_array()[index],
886        data[15].as_array()[index],
887      ])
888    }
889
890    [
891      transpose_column(&data, 0),
892      transpose_column(&data, 1),
893      transpose_column(&data, 2),
894      transpose_column(&data, 3),
895      transpose_column(&data, 4),
896      transpose_column(&data, 5),
897      transpose_column(&data, 6),
898      transpose_column(&data, 7),
899      transpose_column(&data, 8),
900      transpose_column(&data, 9),
901      transpose_column(&data, 10),
902      transpose_column(&data, 11),
903      transpose_column(&data, 12),
904      transpose_column(&data, 13),
905      transpose_column(&data, 14),
906      transpose_column(&data, 15),
907    ]
908  }
909
910  #[inline]
911  fn shl(self, rhs: Self) -> Self::Output {
912    // For x86, this technically can be done explicitly by converting
913    // to `u16` or `u32` then converting back after multiplication, but that may
914    // not actually be faster than auto-vectorization.
915    pick! {
916      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
917        unsafe {
918          // Mask `rhs` to 7 to match `wrapping_shl`.
919          let shift_by = vreinterpretq_s8_u8(vandq_u8(rhs.neon, vmovq_n_u8(7)));
920          Self { neon: vshlq_u8(self.neon, shift_by) }
921        }
922      } else {
923        let self_array: [u8; 16] = cast(self);
924        let rhs_array: [u8; 16] = cast(rhs);
925
926        Self::new([
927          self_array[0].wrapping_shl(rhs_array[0] as u32),
928          self_array[1].wrapping_shl(rhs_array[1] as u32),
929          self_array[2].wrapping_shl(rhs_array[2] as u32),
930          self_array[3].wrapping_shl(rhs_array[3] as u32),
931          self_array[4].wrapping_shl(rhs_array[4] as u32),
932          self_array[5].wrapping_shl(rhs_array[5] as u32),
933          self_array[6].wrapping_shl(rhs_array[6] as u32),
934          self_array[7].wrapping_shl(rhs_array[7] as u32),
935          self_array[8].wrapping_shl(rhs_array[8] as u32),
936          self_array[9].wrapping_shl(rhs_array[9] as u32),
937          self_array[10].wrapping_shl(rhs_array[10] as u32),
938          self_array[11].wrapping_shl(rhs_array[11] as u32),
939          self_array[12].wrapping_shl(rhs_array[12] as u32),
940          self_array[13].wrapping_shl(rhs_array[13] as u32),
941          self_array[14].wrapping_shl(rhs_array[14] as u32),
942          self_array[15].wrapping_shl(rhs_array[15] as u32),
943        ])
944      }
945    }
946  }
947
948  #[inline]
949  fn shl(self, rhs: u32) -> Self::Output {
950    // For x86, this technically can be done explicitly by converting
951    // to `u16` or `u32` then converting back after multiplication, but that
952    // may not actually be faster than auto-vectorization.
953    pick! {
954      if #[cfg(target_feature="simd128")] {
955        // Mask `rhs` to 7 to match `wrapping_shl`.
956        Self { simd: u8x16_shl(self.simd, rhs & 7) }
957      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
958        // Mask `rhs` to 7 to match `wrapping_shl`.
959        unsafe { Self { neon: vshlq_u8(self.neon, vmovq_n_s8(rhs as i8 & 7)) } }
960      } else {
961        let values: u16x8 = cast(self);
962        let shift = rhs & 7;
963        let shifted = values << shift;
964        let crossed = u16x8::splat(!((0x00FFu16 << shift) & 0xFF00));
965        cast(shifted & crossed)
966      }
967    }
968  }
969
970  #[inline]
971  fn shr(self, rhs: Self) -> Self::Output {
972    // For x86, this technically can be done explicitly by converting
973    // to `u16` or `u32` then converting back after multiplication, but that may
974    // not actually be faster than auto-vectorization.
975    pick! {
976      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
977        unsafe {
978          // Mask `rhs` to 7 to match `wrapping_shr`, and negate it because
979          // there is no shift-right intrinsic.
980          let neg_rhs = vnegq_s8(vreinterpretq_s8_u8(vandq_u8(rhs.neon, vmovq_n_u8(7))));
981          Self { neon: vshlq_u8(self.neon, neg_rhs) }
982        }
983      } else {
984        let self_array: [u8; 16] = cast(self);
985        let rhs_array: [u8; 16] = cast(rhs);
986
987        Self::new([
988          self_array[0].wrapping_shr(rhs_array[0] as u32),
989          self_array[1].wrapping_shr(rhs_array[1] as u32),
990          self_array[2].wrapping_shr(rhs_array[2] as u32),
991          self_array[3].wrapping_shr(rhs_array[3] as u32),
992          self_array[4].wrapping_shr(rhs_array[4] as u32),
993          self_array[5].wrapping_shr(rhs_array[5] as u32),
994          self_array[6].wrapping_shr(rhs_array[6] as u32),
995          self_array[7].wrapping_shr(rhs_array[7] as u32),
996          self_array[8].wrapping_shr(rhs_array[8] as u32),
997          self_array[9].wrapping_shr(rhs_array[9] as u32),
998          self_array[10].wrapping_shr(rhs_array[10] as u32),
999          self_array[11].wrapping_shr(rhs_array[11] as u32),
1000          self_array[12].wrapping_shr(rhs_array[12] as u32),
1001          self_array[13].wrapping_shr(rhs_array[13] as u32),
1002          self_array[14].wrapping_shr(rhs_array[14] as u32),
1003          self_array[15].wrapping_shr(rhs_array[15] as u32),
1004        ])
1005      }
1006    }
1007  }
1008
1009  #[inline]
1010  fn shr(self, rhs: u32) -> Self::Output {
1011    // For x86, this technically can be done explicitly by converting
1012    // to `u16` or `u32` then converting back after multiplication, but that
1013    // may not actually be faster than auto-vectorization.
1014    pick! {
1015      if #[cfg(target_feature="simd128")] {
1016        // Mask `rhs` to 7 to match `wrapping_shr`.
1017        Self { simd: u8x16_shr(self.simd, rhs & 7) }
1018      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1019        // Mask `rhs` to 7 to match `wrapping_shr`, and negate it because
1020        // there is no shift-right intrinsic.
1021        unsafe { Self { neon: vshlq_u8(self.neon, vmovq_n_s8(-(rhs as i8 & 7))) } }
1022      } else {
1023        let values: u16x8 = cast(self);
1024        let shift = rhs & 7;
1025        let shifted = values >> shift;
1026        let crossed = u16x8::splat(!((0xFF00u16 >> shift) & 0x00FF));
1027        cast(shifted & crossed)
1028      }
1029    }
1030  }
1031
1032  #[inline]
1033  pub fn max(self, rhs: Self) -> Self {
1034    pick! {
1035      if #[cfg(target_feature="sse2")] {
1036        Self { sse: max_u8_m128i(self.sse, rhs.sse) }
1037      } else if #[cfg(target_feature="simd128")] {
1038        Self { simd: u8x16_max(self.simd, rhs.simd) }
1039      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1040        unsafe {Self { neon: vmaxq_u8(self.neon, rhs.neon) }}
1041      } else {
1042        Self { arr: [
1043          self.arr[0].max(rhs.arr[0]),
1044          self.arr[1].max(rhs.arr[1]),
1045          self.arr[2].max(rhs.arr[2]),
1046          self.arr[3].max(rhs.arr[3]),
1047          self.arr[4].max(rhs.arr[4]),
1048          self.arr[5].max(rhs.arr[5]),
1049          self.arr[6].max(rhs.arr[6]),
1050          self.arr[7].max(rhs.arr[7]),
1051          self.arr[8].max(rhs.arr[8]),
1052          self.arr[9].max(rhs.arr[9]),
1053          self.arr[10].max(rhs.arr[10]),
1054          self.arr[11].max(rhs.arr[11]),
1055          self.arr[12].max(rhs.arr[12]),
1056          self.arr[13].max(rhs.arr[13]),
1057          self.arr[14].max(rhs.arr[14]),
1058          self.arr[15].max(rhs.arr[15]),
1059        ]}
1060      }
1061    }
1062  }
1063
1064  #[inline]
1065  pub fn min(self, rhs: Self) -> Self {
1066    pick! {
1067      if #[cfg(target_feature="sse2")] {
1068        Self { sse: min_u8_m128i(self.sse, rhs.sse) }
1069      } else if #[cfg(target_feature="simd128")] {
1070        Self { simd: u8x16_min(self.simd, rhs.simd) }
1071      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1072        unsafe {Self { neon: vminq_u8(self.neon, rhs.neon) }}
1073      } else {
1074        Self { arr: [
1075          self.arr[0].min(rhs.arr[0]),
1076          self.arr[1].min(rhs.arr[1]),
1077          self.arr[2].min(rhs.arr[2]),
1078          self.arr[3].min(rhs.arr[3]),
1079          self.arr[4].min(rhs.arr[4]),
1080          self.arr[5].min(rhs.arr[5]),
1081          self.arr[6].min(rhs.arr[6]),
1082          self.arr[7].min(rhs.arr[7]),
1083          self.arr[8].min(rhs.arr[8]),
1084          self.arr[9].min(rhs.arr[9]),
1085          self.arr[10].min(rhs.arr[10]),
1086          self.arr[11].min(rhs.arr[11]),
1087          self.arr[12].min(rhs.arr[12]),
1088          self.arr[13].min(rhs.arr[13]),
1089          self.arr[14].min(rhs.arr[14]),
1090          self.arr[15].min(rhs.arr[15]),
1091        ]}
1092      }
1093    }
1094  }
1095
1096  #[inline]
1097  pub fn reduce_max(self) -> u8 {
1098    #[allow(dead_code)]
1099    const SHUFFLE_1: [i8; 16] =
1100      [8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0];
1101    #[allow(dead_code)]
1102    const SHUFFLE_2: [i8; 16] =
1103      [4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0];
1104    #[allow(dead_code)]
1105    const SHUFFLE_3: [i8; 16] =
1106      [2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1107    #[allow(dead_code)]
1108    const SHUFFLE_4: [i8; 16] =
1109      [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1110
1111    pick! {
1112      if #[cfg(target_feature="ssse3")] {
1113        let rhs = shuffle_av_i8z_all_m128i(self.sse, m128i::from(SHUFFLE_1));
1114        let max = max_u8_m128i(self.sse, rhs);
1115        let rhs = shuffle_av_i8z_all_m128i(max, m128i::from(SHUFFLE_2));
1116        let max = max_u8_m128i(max, rhs);
1117        let rhs = shuffle_av_i8z_all_m128i(max, m128i::from(SHUFFLE_3));
1118        let max = max_u8_m128i(max, rhs);
1119        let rhs = shuffle_av_i8z_all_m128i(max, m128i::from(SHUFFLE_4));
1120        let max = max_u8_m128i(max, rhs);
1121        get_i32_from_m128i_s(max) as u8
1122      } else if #[cfg(target_feature="simd128")] {
1123        let rhs = u8x16_shuffle::<8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7>(self.simd, self.simd);
1124        let max = u8x16_max(self.simd, rhs);
1125        let rhs = u8x16_shuffle::<4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0>(max, max);
1126        let max = u8x16_max(max, rhs);
1127        let rhs = u8x16_shuffle::<2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>(max, max);
1128        let max = u8x16_max(max, rhs);
1129        let rhs = u8x16_shuffle::<1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>(max, max);
1130        let max = u8x16_max(max, rhs);
1131        u8x16_extract_lane::<0>(max)
1132      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1133        unsafe {
1134          // Use `transmute` instead of `cast` because `uint8x16_t` does not
1135          // implement `bytemuck::Pod`.
1136          let rhs = vqtbl1q_u8(self.neon, core::mem::transmute(SHUFFLE_1));
1137          let max = vmaxq_u8(self.neon, rhs);
1138          let rhs = vqtbl1q_u8(max, core::mem::transmute(SHUFFLE_2));
1139          let max = vmaxq_u8(max, rhs);
1140          let rhs = vqtbl1q_u8(max, core::mem::transmute(SHUFFLE_3));
1141          let max = vmaxq_u8(max, rhs);
1142          let rhs = vqtbl1q_u8(max, core::mem::transmute(SHUFFLE_4));
1143          let max = vmaxq_u8(max, rhs);
1144          vgetq_lane_u8(max, 0)
1145        }
1146      } else {
1147        let array: [u8; 16] = cast(self);
1148        array.into_iter().reduce(u8::max).unwrap()
1149      }
1150    }
1151  }
1152
1153  #[inline]
1154  pub fn reduce_min(self) -> u8 {
1155    #[allow(dead_code)]
1156    const SHUFFLE_1: [i8; 16] =
1157      [8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0];
1158    #[allow(dead_code)]
1159    const SHUFFLE_2: [i8; 16] =
1160      [4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0];
1161    #[allow(dead_code)]
1162    const SHUFFLE_3: [i8; 16] =
1163      [2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1164    #[allow(dead_code)]
1165    const SHUFFLE_4: [i8; 16] =
1166      [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1167
1168    pick! {
1169      if #[cfg(target_feature="ssse3")] {
1170        let rhs = shuffle_av_i8z_all_m128i(self.sse, m128i::from(SHUFFLE_1));
1171        let min = min_u8_m128i(self.sse, rhs);
1172        let rhs = shuffle_av_i8z_all_m128i(min, m128i::from(SHUFFLE_2));
1173        let min = min_u8_m128i(min, rhs);
1174        let rhs = shuffle_av_i8z_all_m128i(min, m128i::from(SHUFFLE_3));
1175        let min = min_u8_m128i(min, rhs);
1176        let rhs = shuffle_av_i8z_all_m128i(min, m128i::from(SHUFFLE_4));
1177        let min = min_u8_m128i(min, rhs);
1178        get_i32_from_m128i_s(min) as u8
1179      } else if #[cfg(target_feature="simd128")] {
1180        let rhs = u8x16_shuffle::<8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7>(self.simd, self.simd);
1181        let min = u8x16_min(self.simd, rhs);
1182        let rhs = u8x16_shuffle::<4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0>(min, min);
1183        let min = u8x16_min(min, rhs);
1184        let rhs = u8x16_shuffle::<2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>(min, min);
1185        let min = u8x16_min(min, rhs);
1186        let rhs = u8x16_shuffle::<1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>(min, min);
1187        let min = u8x16_min(min, rhs);
1188        u8x16_extract_lane::<0>(min)
1189      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1190        unsafe {
1191          // Use `transmute` instead of `cast` because `uint8x16_t` does not
1192          // implement `bytemuck::Pod`.
1193          let rhs = vqtbl1q_u8(self.neon, core::mem::transmute(SHUFFLE_1));
1194          let min = vminq_u8(self.neon, rhs);
1195          let rhs = vqtbl1q_u8(min, core::mem::transmute(SHUFFLE_2));
1196          let min = vminq_u8(min, rhs);
1197          let rhs = vqtbl1q_u8(min, core::mem::transmute(SHUFFLE_3));
1198          let min = vminq_u8(min, rhs);
1199          let rhs = vqtbl1q_u8(min, core::mem::transmute(SHUFFLE_4));
1200          let min = vminq_u8(min, rhs);
1201          vgetq_lane_u8(min, 0)
1202        }
1203      } else {
1204        let array: [u8; 16] = cast(self);
1205        array.into_iter().reduce(u8::min).unwrap()
1206      }
1207    }
1208  }
1209
1210  #[inline]
1211  pub fn unbounded_shl(self, rhs: Self) -> Self {
1212    // For x86, this technically can be done explicitly by converting to `u16`
1213    // or `u32` then converting back after multiplication, but that may not
1214    // actually be faster than auto-vectorization.
1215    pick! {
1216      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
1217        unsafe {
1218          Self { neon: vshlq_u8(self.neon, vreinterpretq_s8_u8(rhs.neon)) } & rhs.simd_lt(8)
1219        }
1220      } else {
1221        let self_array = self.to_array();
1222        let rhs_array = rhs.to_array();
1223
1224        Self::new([
1225          self_array[0].unbounded_shl(rhs_array[0] as u32),
1226          self_array[1].unbounded_shl(rhs_array[1] as u32),
1227          self_array[2].unbounded_shl(rhs_array[2] as u32),
1228          self_array[3].unbounded_shl(rhs_array[3] as u32),
1229          self_array[4].unbounded_shl(rhs_array[4] as u32),
1230          self_array[5].unbounded_shl(rhs_array[5] as u32),
1231          self_array[6].unbounded_shl(rhs_array[6] as u32),
1232          self_array[7].unbounded_shl(rhs_array[7] as u32),
1233          self_array[8].unbounded_shl(rhs_array[8] as u32),
1234          self_array[9].unbounded_shl(rhs_array[9] as u32),
1235          self_array[10].unbounded_shl(rhs_array[10] as u32),
1236          self_array[11].unbounded_shl(rhs_array[11] as u32),
1237          self_array[12].unbounded_shl(rhs_array[12] as u32),
1238          self_array[13].unbounded_shl(rhs_array[13] as u32),
1239          self_array[14].unbounded_shl(rhs_array[14] as u32),
1240          self_array[15].unbounded_shl(rhs_array[15] as u32),
1241        ])
1242      }
1243    }
1244  }
1245
1246  #[inline]
1247  pub fn unbounded_shl_scalar(self, rhs: u32) -> Self {
1248    // For x86, this technically can be done explicitly by converting
1249    // to `u16` or `u32` then converting back after multiplication, but that
1250    // may not actually be faster than auto-vectorization.
1251    pick! {
1252      if #[cfg(target_feature="simd128")] {
1253        // The intrinsic performs wrapping shift so we need to mask the result.
1254        if rhs >= 8 { Self::ZERO } else { Self { simd: u8x16_shl(self.simd, rhs) } }
1255      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1256        // The intrinsic has different semantics so we need to saturate `rhs`.
1257        unsafe { Self { neon: vshlq_u8(self.neon, vmovq_n_s8(rhs.min(i8::MAX as u32) as i8)) } }
1258      } else {
1259        let values: u16x8 = cast(self);
1260        let shifted = values.unbounded_shl_scalar(rhs);
1261        let crossed = u16x8::splat(!(0x00FFu16.wrapping_shl(rhs) & 0xFF00));
1262        cast(shifted & crossed)
1263      }
1264    }
1265  }
1266
1267  #[inline]
1268  pub fn unbounded_shr(self, rhs: Self) -> Self {
1269    // For x86, this technically can be done explicitly by converting
1270    // to `u16` or `u32` then converting back after multiplication, but that may
1271    // not actually be faster than auto-vectorization.
1272    pick! {
1273      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
1274        unsafe {
1275          // Negate `rhs` because there is no direct shift-right intrinsic, and
1276          // mask to hide `rhs` overflow.
1277          Self { neon: vshlq_u8(self.neon, vnegq_s8(vreinterpretq_s8_u8(rhs.neon))) } & rhs.simd_lt(8)
1278        }
1279      } else {
1280        let self_array = self.to_array();
1281        let rhs_array = rhs.to_array();
1282
1283        Self::new([
1284          self_array[0].unbounded_shr(rhs_array[0] as u32),
1285          self_array[1].unbounded_shr(rhs_array[1] as u32),
1286          self_array[2].unbounded_shr(rhs_array[2] as u32),
1287          self_array[3].unbounded_shr(rhs_array[3] as u32),
1288          self_array[4].unbounded_shr(rhs_array[4] as u32),
1289          self_array[5].unbounded_shr(rhs_array[5] as u32),
1290          self_array[6].unbounded_shr(rhs_array[6] as u32),
1291          self_array[7].unbounded_shr(rhs_array[7] as u32),
1292          self_array[8].unbounded_shr(rhs_array[8] as u32),
1293          self_array[9].unbounded_shr(rhs_array[9] as u32),
1294          self_array[10].unbounded_shr(rhs_array[10] as u32),
1295          self_array[11].unbounded_shr(rhs_array[11] as u32),
1296          self_array[12].unbounded_shr(rhs_array[12] as u32),
1297          self_array[13].unbounded_shr(rhs_array[13] as u32),
1298          self_array[14].unbounded_shr(rhs_array[14] as u32),
1299          self_array[15].unbounded_shr(rhs_array[15] as u32),
1300        ])
1301      }
1302    }
1303  }
1304
1305  #[inline]
1306  pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
1307    // For x86, this technically can be done explicitly by converting
1308    // to `u16` or `u32` then converting back after multiplication, but that
1309    // may not actually be faster than auto-vectorization.
1310    pick! {
1311      if #[cfg(target_feature="simd128")] {
1312        if rhs < 8 { Self { simd: u8x16_shr(self.simd, rhs) } } else { Self::ZERO }
1313      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1314        unsafe {
1315          // Negate `rhs` because there is no direct shift-right intrinsic, and
1316          // restrict it to prevent overflow.
1317          Self { neon: vshlq_u8(self.neon, vmovq_n_s8(-rhs.min(8).cast_signed() as i8)) }
1318        }
1319      } else {
1320        let values: u16x8 = cast(self);
1321        let shifted = values.unbounded_shr_scalar(rhs);
1322        let crossed = u16x8::splat(!(0xFF00u16.wrapping_shr(rhs) & 0x00FF));
1323        cast(shifted & crossed)
1324      }
1325    }
1326  }
1327
1328  #[inline]
1329  pub fn saturating_add(self, rhs: Self) -> Self {
1330    pick! {
1331      if #[cfg(target_feature="sse2")] {
1332        Self { sse: add_saturating_u8_m128i(self.sse, rhs.sse) }
1333      } else if #[cfg(target_feature="simd128")] {
1334        Self { simd: u8x16_add_sat(self.simd, rhs.simd) }
1335      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1336        unsafe {Self { neon: vqaddq_u8(self.neon, rhs.neon) }}
1337      } else {
1338        Self { arr: [
1339          self.arr[0].saturating_add(rhs.arr[0]),
1340          self.arr[1].saturating_add(rhs.arr[1]),
1341          self.arr[2].saturating_add(rhs.arr[2]),
1342          self.arr[3].saturating_add(rhs.arr[3]),
1343          self.arr[4].saturating_add(rhs.arr[4]),
1344          self.arr[5].saturating_add(rhs.arr[5]),
1345          self.arr[6].saturating_add(rhs.arr[6]),
1346          self.arr[7].saturating_add(rhs.arr[7]),
1347          self.arr[8].saturating_add(rhs.arr[8]),
1348          self.arr[9].saturating_add(rhs.arr[9]),
1349          self.arr[10].saturating_add(rhs.arr[10]),
1350          self.arr[11].saturating_add(rhs.arr[11]),
1351          self.arr[12].saturating_add(rhs.arr[12]),
1352          self.arr[13].saturating_add(rhs.arr[13]),
1353          self.arr[14].saturating_add(rhs.arr[14]),
1354          self.arr[15].saturating_add(rhs.arr[15]),
1355        ]}
1356      }
1357    }
1358  }
1359
1360  #[inline]
1361  pub fn saturating_sub(self, rhs: Self) -> Self {
1362    pick! {
1363      if #[cfg(target_feature="sse2")] {
1364        Self { sse: sub_saturating_u8_m128i(self.sse, rhs.sse) }
1365      } else if #[cfg(target_feature="simd128")] {
1366        Self { simd: u8x16_sub_sat(self.simd, rhs.simd) }
1367      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1368        unsafe { Self { neon: vqsubq_u8(self.neon, rhs.neon) } }
1369      } else {
1370        Self { arr: [
1371          self.arr[0].saturating_sub(rhs.arr[0]),
1372          self.arr[1].saturating_sub(rhs.arr[1]),
1373          self.arr[2].saturating_sub(rhs.arr[2]),
1374          self.arr[3].saturating_sub(rhs.arr[3]),
1375          self.arr[4].saturating_sub(rhs.arr[4]),
1376          self.arr[5].saturating_sub(rhs.arr[5]),
1377          self.arr[6].saturating_sub(rhs.arr[6]),
1378          self.arr[7].saturating_sub(rhs.arr[7]),
1379          self.arr[8].saturating_sub(rhs.arr[8]),
1380          self.arr[9].saturating_sub(rhs.arr[9]),
1381          self.arr[10].saturating_sub(rhs.arr[10]),
1382          self.arr[11].saturating_sub(rhs.arr[11]),
1383          self.arr[12].saturating_sub(rhs.arr[12]),
1384          self.arr[13].saturating_sub(rhs.arr[13]),
1385          self.arr[14].saturating_sub(rhs.arr[14]),
1386          self.arr[15].saturating_sub(rhs.arr[15]),
1387        ]}
1388      }
1389    }
1390  }
1391
1392  #[inline]
1393  pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
1394    let (low, high) = self.mul_keep_low_high(rhs);
1395    let overflow = high.simd_ne(Self::ZERO);
1396    (low, overflow)
1397  }
1398
1399  optional_fn_widening_mul {
1400    #[inline]
1401    pub fn widening_mul(self, rhs: Self) -> u16x16 {
1402      pick! {
1403        if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
1404          unsafe {
1405            let low_wide_mul = vmull_u8(vget_low_u8(self.neon), vget_low_u8(rhs.neon));
1406            let high_wide_mul = vmull_u8(vget_high_u8(self.neon), vget_high_u8(rhs.neon));
1407
1408            u16x16 {
1409              a: u16x8 { neon: low_wide_mul },
1410              b: u16x8 { neon: high_wide_mul },
1411            }
1412          }
1413        } else {
1414          let self_array = self.to_array();
1415          let rhs_array = rhs.to_array();
1416
1417          u16x16::new([
1418            (self_array[0] as u16).wrapping_mul(rhs_array[0] as u16),
1419            (self_array[1] as u16).wrapping_mul(rhs_array[1] as u16),
1420            (self_array[2] as u16).wrapping_mul(rhs_array[2] as u16),
1421            (self_array[3] as u16).wrapping_mul(rhs_array[3] as u16),
1422            (self_array[4] as u16).wrapping_mul(rhs_array[4] as u16),
1423            (self_array[5] as u16).wrapping_mul(rhs_array[5] as u16),
1424            (self_array[6] as u16).wrapping_mul(rhs_array[6] as u16),
1425            (self_array[7] as u16).wrapping_mul(rhs_array[7] as u16),
1426            (self_array[8] as u16).wrapping_mul(rhs_array[8] as u16),
1427            (self_array[9] as u16).wrapping_mul(rhs_array[9] as u16),
1428            (self_array[10] as u16).wrapping_mul(rhs_array[10] as u16),
1429            (self_array[11] as u16).wrapping_mul(rhs_array[11] as u16),
1430            (self_array[12] as u16).wrapping_mul(rhs_array[12] as u16),
1431            (self_array[13] as u16).wrapping_mul(rhs_array[13] as u16),
1432            (self_array[14] as u16).wrapping_mul(rhs_array[14] as u16),
1433            (self_array[15] as u16).wrapping_mul(rhs_array[15] as u16),
1434          ])
1435        }
1436      }
1437    }
1438  }
1439
1440  #[inline]
1441  pub fn mul_keep_low_high(self, rhs: Self) -> (Self, Self) {
1442    pick! {
1443      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
1444        unsafe {
1445          let low_wide_mul = vreinterpretq_u8_u16(
1446            vmull_u8(vget_low_u8(self.neon), vget_low_u8(rhs.neon)),
1447          );
1448          let high_wide_mul = vreinterpretq_u8_u16(
1449            vmull_u8(vget_high_u8(self.neon), vget_high_u8(rhs.neon)),
1450          );
1451          let low_high = vuzpq_u8(low_wide_mul, high_wide_mul);
1452          (
1453            Self { neon: low_high.0 },
1454            Self { neon: low_high.1 },
1455          )
1456        }
1457      } else {
1458        // TODO(perf): This implementation looks quite bad. Is there a better
1459        // one?
1460
1461        let self_array = self.to_array();
1462        let rhs_array = rhs.to_array();
1463
1464        let widening_mul = [
1465          (self_array[0] as u16).wrapping_mul(rhs_array[0] as u16),
1466          (self_array[1] as u16).wrapping_mul(rhs_array[1] as u16),
1467          (self_array[2] as u16).wrapping_mul(rhs_array[2] as u16),
1468          (self_array[3] as u16).wrapping_mul(rhs_array[3] as u16),
1469          (self_array[4] as u16).wrapping_mul(rhs_array[4] as u16),
1470          (self_array[5] as u16).wrapping_mul(rhs_array[5] as u16),
1471          (self_array[6] as u16).wrapping_mul(rhs_array[6] as u16),
1472          (self_array[7] as u16).wrapping_mul(rhs_array[7] as u16),
1473          (self_array[8] as u16).wrapping_mul(rhs_array[8] as u16),
1474          (self_array[9] as u16).wrapping_mul(rhs_array[9] as u16),
1475          (self_array[10] as u16).wrapping_mul(rhs_array[10] as u16),
1476          (self_array[11] as u16).wrapping_mul(rhs_array[11] as u16),
1477          (self_array[12] as u16).wrapping_mul(rhs_array[12] as u16),
1478          (self_array[13] as u16).wrapping_mul(rhs_array[13] as u16),
1479          (self_array[14] as u16).wrapping_mul(rhs_array[14] as u16),
1480          (self_array[15] as u16).wrapping_mul(rhs_array[15] as u16),
1481        ];
1482
1483        (
1484          Self::new([
1485            widening_mul[0] as u8,
1486            widening_mul[1] as u8,
1487            widening_mul[2] as u8,
1488            widening_mul[3] as u8,
1489            widening_mul[4] as u8,
1490            widening_mul[5] as u8,
1491            widening_mul[6] as u8,
1492            widening_mul[7] as u8,
1493            widening_mul[8] as u8,
1494            widening_mul[9] as u8,
1495            widening_mul[10] as u8,
1496            widening_mul[11] as u8,
1497            widening_mul[12] as u8,
1498            widening_mul[13] as u8,
1499            widening_mul[14] as u8,
1500            widening_mul[15] as u8,
1501          ]),
1502          Self::new([
1503            (widening_mul[0] >> 8) as u8,
1504            (widening_mul[1] >> 8) as u8,
1505            (widening_mul[2] >> 8) as u8,
1506            (widening_mul[3] >> 8) as u8,
1507            (widening_mul[4] >> 8) as u8,
1508            (widening_mul[5] >> 8) as u8,
1509            (widening_mul[6] >> 8) as u8,
1510            (widening_mul[7] >> 8) as u8,
1511            (widening_mul[8] >> 8) as u8,
1512            (widening_mul[9] >> 8) as u8,
1513            (widening_mul[10] >> 8) as u8,
1514            (widening_mul[11] >> 8) as u8,
1515            (widening_mul[12] >> 8) as u8,
1516            (widening_mul[13] >> 8) as u8,
1517            (widening_mul[14] >> 8) as u8,
1518            (widening_mul[15] >> 8) as u8,
1519          ]),
1520        )
1521      }
1522    }
1523  }
1524
1525  #[inline]
1526  pub fn mul_keep_high(self, rhs: Self) -> Self {
1527    pick! {
1528      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
1529        unsafe {
1530          let low_wide_mul = vreinterpretq_u8_u16(
1531            vmull_u8(vget_low_u8(self.neon), vget_low_u8(rhs.neon)),
1532          );
1533          let high_wide_mul = vreinterpretq_u8_u16(
1534            vmull_u8(vget_high_u8(self.neon), vget_high_u8(rhs.neon)),
1535          );
1536          Self { neon: vuzpq_u8(low_wide_mul, high_wide_mul).1 }
1537        }
1538      } else {
1539        let self_array = self.to_array();
1540        let rhs_array = rhs.to_array();
1541
1542        Self::new([
1543          ((self_array[0] as u16).wrapping_mul(rhs_array[0] as u16) >> 8) as u8,
1544          ((self_array[1] as u16).wrapping_mul(rhs_array[1] as u16) >> 8) as u8,
1545          ((self_array[2] as u16).wrapping_mul(rhs_array[2] as u16) >> 8) as u8,
1546          ((self_array[3] as u16).wrapping_mul(rhs_array[3] as u16) >> 8) as u8,
1547          ((self_array[4] as u16).wrapping_mul(rhs_array[4] as u16) >> 8) as u8,
1548          ((self_array[5] as u16).wrapping_mul(rhs_array[5] as u16) >> 8) as u8,
1549          ((self_array[6] as u16).wrapping_mul(rhs_array[6] as u16) >> 8) as u8,
1550          ((self_array[7] as u16).wrapping_mul(rhs_array[7] as u16) >> 8) as u8,
1551          ((self_array[8] as u16).wrapping_mul(rhs_array[8] as u16) >> 8) as u8,
1552          ((self_array[9] as u16).wrapping_mul(rhs_array[9] as u16) >> 8) as u8,
1553          ((self_array[10] as u16).wrapping_mul(rhs_array[10] as u16) >> 8) as u8,
1554          ((self_array[11] as u16).wrapping_mul(rhs_array[11] as u16) >> 8) as u8,
1555          ((self_array[12] as u16).wrapping_mul(rhs_array[12] as u16) >> 8) as u8,
1556          ((self_array[13] as u16).wrapping_mul(rhs_array[13] as u16) >> 8) as u8,
1557          ((self_array[14] as u16).wrapping_mul(rhs_array[14] as u16) >> 8) as u8,
1558          ((self_array[15] as u16).wrapping_mul(rhs_array[15] as u16) >> 8) as u8,
1559        ])
1560      }
1561    }
1562  }
1563
1564  optional_fn_deserialize {}
1565}
1566
1567/// The following functionality exists only for [`u8x16`], or only for
1568/// particular types inconsistently.
1569impl u8x16 {
1570  /// Returns `[lhs[0], rhs[0], lhs[1], rhs[1], ...]`, taking the first 8
1571  /// elements of each input and dropping their last 8 elements.
1572  #[inline]
1573  #[must_use]
1574  pub fn unpack_low(lhs: u8x16, rhs: u8x16) -> u8x16 {
1575    pick! {
1576        if #[cfg(target_feature = "sse2")] {
1577            u8x16 { sse: unpack_low_i8_m128i(lhs.sse, rhs.sse) }
1578        } else if #[cfg(target_feature = "simd128")] {
1579          u8x16 { simd: u8x16_shuffle::<0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23>(lhs.simd, rhs.simd) }
1580        } else if #[cfg(all(target_feature = "neon", target_arch = "aarch64"))] {
1581            let lhs = unsafe { vget_low_u8(lhs.neon) };
1582            let rhs = unsafe { vget_low_u8(rhs.neon) };
1583
1584            let zipped = unsafe { vzip_u8(lhs, rhs) };
1585            u8x16 { neon: unsafe { vcombine_u8(zipped.0, zipped.1) } }
1586        } else {
1587            u8x16::new([
1588                lhs.as_array()[0], rhs.as_array()[0],
1589                lhs.as_array()[1], rhs.as_array()[1],
1590                lhs.as_array()[2], rhs.as_array()[2],
1591                lhs.as_array()[3], rhs.as_array()[3],
1592                lhs.as_array()[4], rhs.as_array()[4],
1593                lhs.as_array()[5], rhs.as_array()[5],
1594                lhs.as_array()[6], rhs.as_array()[6],
1595                lhs.as_array()[7], rhs.as_array()[7],
1596            ])
1597        }
1598    }
1599  }
1600
1601  /// Returns `[lhs[8], rhs[8], lhs[9], rhs[9], ...]`, taking the last 8
1602  /// elements of each input and dropping their first 8 elements.
1603  #[inline]
1604  #[must_use]
1605  pub fn unpack_high(lhs: u8x16, rhs: u8x16) -> u8x16 {
1606    pick! {
1607        if #[cfg(target_feature = "sse2")] {
1608            u8x16 { sse: unpack_high_i8_m128i(lhs.sse, rhs.sse) }
1609        } else if #[cfg(target_feature = "simd128")] {
1610            u8x16 { simd: u8x16_shuffle::<8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31>(lhs.simd, rhs.simd) }
1611        } else if #[cfg(all(target_feature = "neon", target_arch = "aarch64"))] {
1612            let lhs = unsafe { vget_high_u8(lhs.neon) };
1613            let rhs = unsafe { vget_high_u8(rhs.neon) };
1614
1615            let zipped = unsafe { vzip_u8(lhs, rhs) };
1616            u8x16 { neon: unsafe { vcombine_u8(zipped.0, zipped.1) } }
1617        } else {
1618            u8x16::new([
1619                lhs.as_array()[8], rhs.as_array()[8],
1620                lhs.as_array()[9], rhs.as_array()[9],
1621                lhs.as_array()[10], rhs.as_array()[10],
1622                lhs.as_array()[11], rhs.as_array()[11],
1623                lhs.as_array()[12], rhs.as_array()[12],
1624                lhs.as_array()[13], rhs.as_array()[13],
1625                lhs.as_array()[14], rhs.as_array()[14],
1626                lhs.as_array()[15], rhs.as_array()[15],
1627            ])
1628        }
1629    }
1630  }
1631
1632  /// Treats two [`i16x8`] values as a single [`i16x16`] value, then converts
1633  /// each element from [`i16`] to [`u8`], saturating out of range values.
1634  #[inline]
1635  #[must_use]
1636  pub fn narrow_i16x8(lhs: i16x8, rhs: i16x8) -> Self {
1637    pick! {
1638        if #[cfg(target_feature = "sse2")] {
1639            u8x16 { sse: pack_i16_to_u8_m128i(lhs.sse, rhs.sse) }
1640        } else if #[cfg(target_feature = "simd128")] {
1641            u8x16 { simd: u8x16_narrow_i16x8(lhs.simd, rhs.simd) }
1642        } else if #[cfg(all(target_feature = "neon", target_arch = "aarch64"))] {
1643            let lhs = unsafe { vqmovun_s16(lhs.neon) };
1644            let rhs = unsafe { vqmovun_s16(rhs.neon) };
1645            u8x16 { neon: unsafe { vcombine_u8(lhs, rhs) } }
1646        } else {
1647            fn clamp(a: i16) -> u8 {
1648                  if a < u8::MIN as i16 {
1649                      u8::MIN
1650                  } else if a > u8::MAX as i16 {
1651                      u8::MAX
1652                  } else {
1653                      a as u8
1654                  }
1655            }
1656
1657            Self { arr: [
1658                clamp(lhs.as_array()[0]),
1659                clamp(lhs.as_array()[1]),
1660                clamp(lhs.as_array()[2]),
1661                clamp(lhs.as_array()[3]),
1662                clamp(lhs.as_array()[4]),
1663                clamp(lhs.as_array()[5]),
1664                clamp(lhs.as_array()[6]),
1665                clamp(lhs.as_array()[7]),
1666                clamp(rhs.as_array()[0]),
1667                clamp(rhs.as_array()[1]),
1668                clamp(rhs.as_array()[2]),
1669                clamp(rhs.as_array()[3]),
1670                clamp(rhs.as_array()[4]),
1671                clamp(rhs.as_array()[5]),
1672                clamp(rhs.as_array()[6]),
1673                clamp(rhs.as_array()[7]),
1674            ]}
1675        }
1676    }
1677  }
1678
1679  /// Returns a new vector where each element is based on the index values in
1680  /// `rhs`.
1681  ///
1682  /// * Index values in the range `[0, 15]` select the i-th element of `self`.
1683  /// * Index values that are out of range will cause that output lane to be
1684  ///   `0`.
1685  ///
1686  /// This function has been deprecated and replaced with [`shuffle_zeroing`].
1687  ///
1688  /// [`shuffle_zeroing`]: Self::shuffle_zeroing
1689  #[inline]
1690  #[deprecated(since = "1.7.0", note = "replaced with `shuffle_zeroing`")]
1691  pub fn swizzle(self, rhs: i8x16) -> i8x16 {
1692    self.shuffle_zeroing(rhs.cast_unsigned()).cast_signed()
1693  }
1694
1695  /// Works like [`swizzle`](Self::swizzle) with the following additional
1696  /// details
1697  ///
1698  /// * Indices in the range `[0, 15]` will select the i-th element of `self`.
1699  /// * If the high bit of any index is set (meaning that the index is
1700  ///   negative), then the corresponding output lane is guaranteed to be zero.
1701  /// * Otherwise the output lane is either `0` or `self[rhs[i] % 16]`,
1702  ///   depending on the implementation.
1703  ///
1704  /// This function has been deprecated and replaced with [`shuffle`].
1705  ///
1706  /// [`shuffle`]: Self::shuffle
1707  #[inline]
1708  #[deprecated(since = "1.7.0", note = "replaced with `shuffle`")]
1709  pub fn swizzle_relaxed(self, rhs: u8x16) -> u8x16 {
1710    self.shuffle(rhs)
1711  }
1712}