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