Skip to main content

wide/
u8x32_.rs

1use super::*;
2
3pick! {
4  if #[cfg(target_feature="avx2")] {
5    /// A SIMD vector with 32 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(32))]
13    pub struct u8x32 { pub(crate) avx: m256i }
14  } else {
15    /// A SIMD vector with 32 elements of type [`u8`].
16    ///
17    /// See the [crate level documentation] for more information about SIMD
18    /// vectors.
19    ///
20    /// [crate level documentation]: crate
21    #[derive(Default, Clone, Copy, PartialEq, Eq)]
22    #[repr(C, align(32))]
23    pub struct u8x32 { pub(crate) a : u8x16, pub(crate) b : u8x16 }
24  }
25}
26
27impl_simd_uint! {
28  unsafe {
29    T = u8,
30    N = 32,
31    Simd = u8x32,
32    IntSimd = i8x32,
33    T_BITS = 8,
34    T_BITS_MUL_2 = 16,
35    [
36      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
37      21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
38    ],
39    optional_type_x86_inner { X86Inner = __m256i },
40    optional_type_arm_inner {},
41    optional_type_wasm_inner {},
42  }
43
44  #[inline]
45  fn not(self) -> Self {
46    pick! {
47      if #[cfg(target_feature="avx2")] {
48        Self { avx: self.avx.not()  }
49      } else {
50        Self {
51          a : self.a.not(),
52          b : self.b.not(),
53        }
54      }
55    }
56  }
57
58  #[inline]
59  fn add(self, rhs: Self) -> Self::Output {
60    pick! {
61      if #[cfg(target_feature="avx2")] {
62        Self { avx: add_i8_m256i(self.avx,rhs.avx) }
63      } else {
64        Self {
65          a : self.a.add(rhs.a),
66          b : self.b.add(rhs.b),
67        }
68      }
69    }
70  }
71
72  #[inline]
73  fn sub(self, rhs: Self) -> Self::Output {
74    pick! {
75      if #[cfg(target_feature="avx2")] {
76        Self { avx: sub_i8_m256i(self.avx,rhs.avx) }
77      } else {
78        Self {
79          a : self.a.sub(rhs.a),
80          b : self.b.sub(rhs.b),
81        }
82      }
83    }
84  }
85
86  #[inline]
87  fn mul(self, rhs: Self) -> Self::Output {
88    // For x86, this technically can be done explicitly by converting to `i16`
89    // then converting back after multiplication, but that may not actually be
90    // faster than auto-vectorization.
91    let [self_a, self_b]: [u8x16; 2] = cast(self);
92    let [rhs_a, rhs_b]: [u8x16; 2] = cast(rhs);
93    cast([self_a * rhs_a, self_b * rhs_b])
94  }
95
96  #[inline]
97  fn bitand(self, rhs: Self) -> Self::Output {
98    pick! {
99      if #[cfg(target_feature="avx2")] {
100          Self { avx : bitand_m256i(self.avx,rhs.avx) }
101      } else {
102          Self {
103            a : self.a.bitand(rhs.a),
104            b : self.b.bitand(rhs.b),
105          }
106      }
107    }
108  }
109
110  #[inline]
111  fn bitor(self, rhs: Self) -> Self::Output {
112    pick! {
113      if #[cfg(target_feature="avx2")] {
114        Self { avx : bitor_m256i(self.avx,rhs.avx) }
115      } else {
116        Self {
117          a : self.a.bitor(rhs.a),
118          b : self.b.bitor(rhs.b),
119        }
120      }
121    }
122  }
123
124  #[inline]
125  fn bitxor(self, rhs: Self) -> Self::Output {
126    pick! {
127      if #[cfg(target_feature="avx2")] {
128        Self { avx : bitxor_m256i(self.avx,rhs.avx) }
129      } else {
130        Self {
131          a : self.a.bitxor(rhs.a),
132          b : self.b.bitxor(rhs.b),
133        }
134      }
135    }
136  }
137
138  #[inline]
139  fn simd_eq(self, rhs: Self) -> Self::Output {
140    pick! {
141      if #[cfg(target_feature="avx2")] {
142        Self { avx : cmp_eq_mask_i8_m256i(self.avx,rhs.avx) }
143      } else {
144        Self {
145          a : self.a.simd_eq(rhs.a),
146          b : self.b.simd_eq(rhs.b),
147        }
148      }
149    }
150  }
151
152  #[inline]
153  fn simd_ne(self, rhs: Self) -> Self::Output {
154    pick! {
155      if #[cfg(target_feature="avx2")] {
156        !self.simd_eq(rhs)
157      } else {
158        Self {
159          a : self.a.simd_ne(rhs.a),
160          b : self.b.simd_ne(rhs.b),
161        }
162      }
163    }
164  }
165
166  #[inline]
167  fn simd_lt(self, rhs: Self) -> Self::Output {
168    pick! {
169      if #[cfg(target_feature="avx2")] {
170        // Convert from u8 to i8.
171        let offset = Self::splat(0x80);
172        let self_i8 = self.bitxor(offset).avx;
173        let rhs_i8 = rhs.bitxor(offset).avx;
174        Self { avx: cmp_gt_mask_i8_m256i(rhs_i8, self_i8)}
175      } else {
176        Self { a: self.a.simd_lt(rhs.a), b: self.b.simd_lt(rhs.b) }
177      }
178    }
179  }
180
181  #[inline]
182  fn simd_gt(self, rhs: Self) -> Self::Output {
183    pick! {
184      if #[cfg(target_feature="avx2")] {
185        // Convert from u8 to i8.
186        let offset = Self::splat(0x80);
187        let self_i8 = self.bitxor(offset).avx;
188        let rhs_i8 = rhs.bitxor(offset).avx;
189        Self { avx : cmp_gt_mask_i8_m256i(self_i8,rhs_i8) }
190      } else {
191        Self { a: self.a.simd_gt(rhs.a), b: self.b.simd_gt(rhs.b) }
192      }
193    }
194  }
195
196  #[inline]
197  fn simd_le(self, rhs: Self) -> Self::Output {
198    pick! {
199      if #[cfg(target_feature="avx2")] {
200        // Convert from u8 to i8.
201        let offset = Self::splat(0x80);
202        let self_i8 = self.bitxor(offset).avx;
203        let rhs_i8 = rhs.bitxor(offset).avx;
204        let gt_mask = Self { avx : cmp_gt_mask_i8_m256i(self_i8,rhs_i8) };
205        Self { avx: gt_mask.bitxor(Self::splat(0xFF)).avx }
206      } else {
207        Self { a: self.a.simd_le(rhs.a), b: self.b.simd_le(rhs.b) }
208      }
209    }
210  }
211
212  #[inline]
213  fn simd_ge(self, rhs: Self) -> Self::Output {
214    pick! {
215      if #[cfg(target_feature="avx2")] {
216        // Convert from u8 to i8.
217        let offset = Self::splat(0x80);
218        let self_i8 = self.bitxor(offset).avx;
219        let rhs_i8 = rhs.bitxor(offset).avx;
220        let lt_mask = Self { avx: cmp_gt_mask_i8_m256i(rhs_i8, self_i8)};
221        Self { avx: lt_mask.bitxor(Self::splat(0xFF)).avx }
222      } else {
223        Self { a: self.a.simd_ge(rhs.a), b: self.b.simd_ge(rhs.b) }
224      }
225    }
226  }
227
228  #[inline]
229  pub fn reduce_add(self) -> u8 {
230    let array: [u8x16; 2] = cast(self);
231    (array[0] + array[1]).reduce_add()
232  }
233
234  #[inline]
235  pub fn reduce_mul(self) -> u8 {
236    let array: [u8x16; 2] = cast(self);
237    (array[0] * array[1]).reduce_mul()
238  }
239
240  #[inline]
241  pub fn bitselect(self, if_one: Self, if_zero: Self) -> Self {
242    pick! {
243      if #[cfg(target_feature="avx2")] {
244        Self {
245          avx: bitor_m256i(
246            bitand_m256i(if_one.avx, self.avx),
247            bitandnot_m256i(self.avx, if_zero.avx),
248          ),
249        }
250      } else {
251        Self {
252          a: self.a.bitselect(if_one.a, if_zero.a),
253          b: self.b.bitselect(if_one.b, if_zero.b),
254        }
255      }
256    }
257  }
258
259  #[inline]
260  fn select(self, if_true: Self, if_false: Self) -> Self {
261    pick! {
262      if #[cfg(target_feature="avx2")] {
263        Self { avx: blend_varying_i8_m256i(if_false.avx, if_true.avx, self.avx) }
264      } else {
265        Self {
266          a : self.a.select(if_true.a, if_false.a),
267          b : self.b.select(if_true.b, if_false.b),
268        }
269      }
270    }
271  }
272
273  #[inline]
274  pub fn to_bitmask(self) -> u32 {
275    pick! {
276      if #[cfg(target_feature="avx2")] {
277        move_mask_i8_m256i(self.avx) as u32
278      } else {
279        self.a.to_bitmask() | (self.b.to_bitmask() << 16)
280      }
281    }
282  }
283
284  #[inline]
285  pub fn any(self) -> bool {
286    pick! {
287      if #[cfg(target_feature="avx2")] {
288        move_mask_i8_m256i(self.avx) != 0
289      } else {
290        (self.a | self.b).any()
291      }
292    }
293  }
294
295  #[inline]
296  pub fn all(self) -> bool {
297    pick! {
298      if #[cfg(target_feature="avx2")] {
299        move_mask_i8_m256i(self.avx) == -1
300      } else {
301        (self.a & self.b).all()
302      }
303    }
304  }
305
306  #[inline]
307  pub fn shuffle(self, indices: u8x32) -> Self {
308    pick! {
309      if #[cfg(all(target_feature="avx512vbmi", target_feature="avx512vl"))] {
310        Self { avx: permute_i8_m256i(indices.avx, self.avx) }
311      } else if #[cfg(target_feature="avx2")] {
312        // Same broadcast+blend as strict, but skip the 0x60 zeroing fold.
313        let tbl_lo = shuffle_abi_i128z_all_m256i::<0x00>(self.avx, self.avx);
314        let tbl_hi = shuffle_abi_i128z_all_m256i::<0x11>(self.avx, self.avx);
315        let res_lo = shuffle_av_i8z_half_m256i(tbl_lo, indices.avx);
316        let res_hi = shuffle_av_i8z_half_m256i(tbl_hi, indices.avx);
317        let sel = shl_imm_u16_m256i::<3>(indices.avx);
318        Self { avx: blend_varying_i8_m256i(res_lo, res_hi, sel) }
319      } else {
320        Self {
321          a: [self.a, self.b].shuffle(indices.a),
322          b: [self.a, self.b].shuffle(indices.b),
323        }
324      }
325    }
326  }
327
328  #[inline]
329  pub fn shuffle_zeroing(self, indices: u8x32) -> Self {
330    pick! {
331      if #[cfg(all(target_feature="avx512vbmi", target_feature="avx512vl"))] {
332        // vpermb takes the index mod 32 and never zeroes, so zero the
333        // out-of-range lanes ourselves: (rhs & 0xE0) == 0  <=>  rhs < 32.
334        let permuted = permute_i8_m256i(indices.avx, self.avx);
335        let hi_bits = bitand_m256i(indices.avx, set_splat_i8_m256i(0xE0_u8 as i8));
336        let in_range = cmp_eq_mask_i8_m256i(hi_bits, zeroed_m256i());
337        Self { avx: bitand_m256i(permuted, in_range) }
338      } else if #[cfg(target_feature="avx2")] {
339        // Broadcast each 16-byte table half into both 128-bit lanes, pshufb
340        // each by the index, blend by index bit 4. Fold the >=32 zeroing into
341        // pshufb with an unsigned saturating add of 0x60 (0x60 + 32 = 0x80).
342        let idx = add_saturating_u8_m256i(indices.avx, set_splat_i8_m256i(0x60));
343        let tbl_lo = shuffle_abi_i128z_all_m256i::<0x00>(self.avx, self.avx);
344        let tbl_hi = shuffle_abi_i128z_all_m256i::<0x11>(self.avx, self.avx);
345        let res_lo = shuffle_av_i8z_half_m256i(tbl_lo, idx);
346        let res_hi = shuffle_av_i8z_half_m256i(tbl_hi, idx);
347        // move index bit 4 into the sign bit (bit 7) for blendv.
348        let sel = shl_imm_u16_m256i::<3>(indices.avx);
349        Self { avx: blend_varying_i8_m256i(res_lo, res_hi, sel) }
350      } else {
351        Self {
352          a: [self.a, self.b].shuffle_zeroing(indices.a),
353          b: [self.a, self.b].shuffle_zeroing(indices.b),
354        }
355      }
356    }
357  }
358
359  #[inline]
360  pub fn shuffle_wrapping(self, indices: u8x32) -> Self {
361    pick! {
362      if #[cfg(all(target_feature="avx512vbmi", target_feature="avx512vl"))] {
363        Self { avx: permute_i8_m256i(indices.avx, self.avx) }
364      } else if #[cfg(target_feature="avx2")] {
365        self.shuffle(indices & 31)
366      } else {
367        Self {
368          a: [self.a, self.b].shuffle_wrapping(indices.a),
369          b: [self.a, self.b].shuffle_wrapping(indices.b),
370        }
371      }
372    }
373  }
374
375  #[inline]
376  fn shuffle(self: [u8x32; 2], indices: u8x32) -> u8x32 {
377    pick! {
378      if #[cfg(all(target_feature = "avx512vbmi", target_feature = "avx512vl"))] {
379        #[cfg(target_arch = "x86")]
380        use core::arch::x86::_mm256_permutex2var_epi8;
381        #[cfg(target_arch = "x86_64")]
382        use core::arch::x86_64::_mm256_permutex2var_epi8;
383        // TODO(safe_arch): add `_mm256_permutex2var_epi8`.
384        u8x32 {
385          avx: unsafe {
386            m256i(_mm256_permutex2var_epi8(self[0].avx.0, indices.avx.0, self[1].avx.0))
387          },
388        }
389      } else if #[cfg(target_feature="avx2")] {
390        self[0].shuffle_zeroing(indices) | self[1].shuffle_zeroing(indices - 32)
391      } else {
392        u8x32 {
393          a: [self[0].a, self[0].b, self[1].a, self[1].b].shuffle(indices.a),
394          b: [self[0].a, self[0].b, self[1].a, self[1].b].shuffle(indices.b),
395        }
396      }
397    }
398  }
399
400  #[inline]
401  fn shuffle_zeroing(self: [u8x32; 2], indices: u8x32) -> u8x32 {
402    pick! {
403      if #[cfg(all(target_feature = "avx512vbmi", target_feature = "avx512vl"))] {
404        self.shuffle(indices) & indices.simd_lt(64)
405      } else if #[cfg(target_feature="avx2")] {
406        self.shuffle(indices)
407      } else {
408        u8x32 {
409          a: [self[0].a, self[0].b, self[1].a, self[1].b].shuffle_zeroing(indices.a),
410          b: [self[0].a, self[0].b, self[1].a, self[1].b].shuffle_zeroing(indices.b),
411        }
412      }
413    }
414  }
415
416  #[inline]
417  fn shuffle_wrapping(self: [u8x32; 2], indices: u8x32) -> u8x32 {
418    pick! {
419      if #[cfg(all(target_feature = "avx512vbmi", target_feature = "avx512vl"))] {
420        // `avx512` shuffle intrinsics are wrapping
421        self.shuffle(indices)
422      } else {
423        self.shuffle(indices & 63)
424      }
425    }
426  }
427
428  #[inline]
429  fn shuffle(self: [u8x32; 3], indices: u8x32) -> u8x32 {
430    [self[0], self[1]].shuffle_zeroing(indices) | self[2].shuffle_zeroing(indices - 64)
431  }
432
433  #[inline]
434  fn shuffle_zeroing(self: [u8x32; 3], indices: u8x32) -> u8x32 {
435    self.shuffle(indices)
436  }
437
438  #[inline]
439  fn shuffle_wrapping(self: [u8x32; 3], indices: u8x32) -> u8x32 {
440    self.shuffle(indices % 96)
441  }
442
443  #[inline]
444  fn shuffle(self: [u8x32; 4], indices: u8x32) -> u8x32 {
445    [self[0], self[1]].shuffle_zeroing(indices) | [self[2], self[3]].shuffle_zeroing(indices - 64)
446  }
447
448  #[inline]
449  fn shuffle_zeroing(self: [u8x32; 4], indices: u8x32) -> u8x32 {
450    self.shuffle(indices)
451  }
452
453  #[inline]
454  fn shuffle_wrapping(self: [u8x32; 4], indices: u8x32) -> u8x32 {
455    self.shuffle(indices & 127)
456  }
457
458  ///
459  /// Currently this function is never accelerated.
460  #[inline]
461  pub fn transpose(data: [Self; 32]) -> [Self; 32] {
462    // Can this be optimized?
463
464    #[inline(always)]
465    fn transpose_column(data: &[u8x32; 32], index: usize) -> u8x32 {
466      u8x32::new([
467        data[0].as_array()[index],
468        data[1].as_array()[index],
469        data[2].as_array()[index],
470        data[3].as_array()[index],
471        data[4].as_array()[index],
472        data[5].as_array()[index],
473        data[6].as_array()[index],
474        data[7].as_array()[index],
475        data[8].as_array()[index],
476        data[9].as_array()[index],
477        data[10].as_array()[index],
478        data[11].as_array()[index],
479        data[12].as_array()[index],
480        data[13].as_array()[index],
481        data[14].as_array()[index],
482        data[15].as_array()[index],
483        data[16].as_array()[index],
484        data[17].as_array()[index],
485        data[18].as_array()[index],
486        data[19].as_array()[index],
487        data[20].as_array()[index],
488        data[21].as_array()[index],
489        data[22].as_array()[index],
490        data[23].as_array()[index],
491        data[24].as_array()[index],
492        data[25].as_array()[index],
493        data[26].as_array()[index],
494        data[27].as_array()[index],
495        data[28].as_array()[index],
496        data[29].as_array()[index],
497        data[30].as_array()[index],
498        data[31].as_array()[index],
499      ])
500    }
501
502    [
503      transpose_column(&data, 0),
504      transpose_column(&data, 1),
505      transpose_column(&data, 2),
506      transpose_column(&data, 3),
507      transpose_column(&data, 4),
508      transpose_column(&data, 5),
509      transpose_column(&data, 6),
510      transpose_column(&data, 7),
511      transpose_column(&data, 8),
512      transpose_column(&data, 9),
513      transpose_column(&data, 10),
514      transpose_column(&data, 11),
515      transpose_column(&data, 12),
516      transpose_column(&data, 13),
517      transpose_column(&data, 14),
518      transpose_column(&data, 15),
519      transpose_column(&data, 16),
520      transpose_column(&data, 17),
521      transpose_column(&data, 18),
522      transpose_column(&data, 19),
523      transpose_column(&data, 20),
524      transpose_column(&data, 21),
525      transpose_column(&data, 22),
526      transpose_column(&data, 23),
527      transpose_column(&data, 24),
528      transpose_column(&data, 25),
529      transpose_column(&data, 26),
530      transpose_column(&data, 27),
531      transpose_column(&data, 28),
532      transpose_column(&data, 29),
533      transpose_column(&data, 30),
534      transpose_column(&data, 31),
535    ]
536  }
537
538  #[inline]
539  fn shl(self, rhs: Self) -> Self::Output {
540    pick! {
541      if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
542        self.shift_each_u16(rhs.avx, false, false)
543      } else {
544        let [self_a, self_b]: [u8x16; 2] = cast(self);
545        let [rhs_a, rhs_b]: [u8x16; 2] = cast(rhs);
546        cast([self_a << rhs_a, self_b << rhs_b])
547      }
548    }
549  }
550
551  #[inline]
552  fn shl(self, rhs: u32) -> Self::Output {
553    pick! {
554      if #[cfg(target_feature="avx2")] {
555        let values: u16x16 = cast(self);
556        let shift = rhs & 7;
557        let shifted = values << shift;
558        let crossed = u16x16::splat(!((0x00FFu16 << shift) & 0xFF00));
559        cast(shifted & crossed)
560      } else {
561        let [self_a, self_b]: [u8x16; 2] = cast(self);
562        cast([self_a << rhs, self_b << rhs])
563      }
564    }
565  }
566
567  #[inline]
568  fn shr(self, rhs: Self) -> Self::Output {
569    pick! {
570      if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
571        self.shift_each_u16(rhs.avx, true, false)
572      } else {
573        let [self_a, self_b]: [u8x16; 2] = cast(self);
574        let [rhs_a, rhs_b]: [u8x16; 2] = cast(rhs);
575        cast([self_a >> rhs_a, self_b >> rhs_b])
576      }
577    }
578  }
579
580  #[inline]
581  fn shr(self, rhs: u32) -> Self::Output {
582    pick! {
583      if #[cfg(target_feature="avx2")] {
584        let values: u16x16 = cast(self);
585        let shift = rhs & 7;
586        let shifted = values >> shift;
587        let crossed = u16x16::splat(!((0xFF00u16 >> shift) & 0x00FF));
588        cast(shifted & crossed)
589      } else {
590        let [self_a, self_b]: [u8x16; 2] = cast(self);
591        cast([self_a >> rhs, self_b >> rhs])
592      }
593    }
594  }
595
596  #[inline]
597  pub fn max(self, rhs: Self) -> Self {
598    pick! {
599      if #[cfg(target_feature="avx2")] {
600        Self { avx: max_u8_m256i(self.avx,rhs.avx) }
601      } else {
602        Self {
603          a : self.a.max(rhs.a),
604          b : self.b.max(rhs.b),
605        }
606      }
607    }
608  }
609
610  #[inline]
611  pub fn min(self, rhs: Self) -> Self {
612    pick! {
613      if #[cfg(target_feature="avx2")] {
614        Self { avx: min_u8_m256i(self.avx,rhs.avx) }
615      } else {
616        Self {
617          a : self.a.min(rhs.a),
618          b : self.b.min(rhs.b),
619        }
620      }
621    }
622  }
623
624  #[inline]
625  pub fn reduce_max(self) -> u8 {
626    let array: [u8x16; 2] = cast(self);
627    array[0].max(array[1]).reduce_max()
628  }
629
630  #[inline]
631  pub fn reduce_min(self) -> u8 {
632    let array: [u8x16; 2] = cast(self);
633    array[0].min(array[1]).reduce_min()
634  }
635
636  #[inline]
637  pub fn unbounded_shl(self, rhs: Self) -> Self {
638    pick! {
639      if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
640        self.shift_each_u16(rhs.avx, false, true)
641      } else {
642        let [self_a, self_b] = cast::<u8x32, [u8x16; 2]>(self);
643        let [rhs_a, rhs_b] = cast::<u8x32, [u8x16; 2]>(rhs);
644        cast([self_a.unbounded_shl(rhs_a), self_b.unbounded_shl(rhs_b)])
645      }
646    }
647  }
648
649  #[inline]
650  pub fn unbounded_shl_scalar(self, rhs: u32) -> Self {
651    pick! {
652      if #[cfg(target_feature="avx2")] {
653        let values: u16x16 = cast(self);
654        let shifted = values.unbounded_shl_scalar(rhs);
655        let crossed = u16x16::splat(!(0x00FFu16.wrapping_shl(rhs) & 0xFF00));
656        cast(shifted & crossed)
657      } else {
658        let [self_a, self_b] = cast::<u8x32, [u8x16; 2]>(self);
659        cast([self_a.unbounded_shl_scalar(rhs), self_b.unbounded_shl_scalar(rhs)])
660      }
661    }
662  }
663
664  #[inline]
665  pub fn unbounded_shr(self, rhs: Self) -> Self {
666    pick! {
667      if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
668        self.shift_each_u16(rhs.avx, true, true)
669      } else {
670        let [self_a, self_b] = cast::<u8x32, [u8x16; 2]>(self);
671        let [rhs_a, rhs_b] = cast::<u8x32, [u8x16; 2]>(rhs);
672        cast([self_a.unbounded_shr(rhs_a), self_b.unbounded_shr(rhs_b)])
673      }
674    }
675  }
676
677  #[inline]
678  pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
679    pick! {
680      if #[cfg(target_feature="avx2")] {
681        let values: u16x16 = cast(self);
682        let shifted = values.unbounded_shr_scalar(rhs);
683        let crossed = u16x16::splat(!(0xFF00u16.wrapping_shr(rhs) & 0x00FF));
684        cast(shifted & crossed)
685      } else {
686        let [self_a, self_b] = cast::<u8x32, [u8x16; 2]>(self);
687        cast([self_a.unbounded_shr_scalar(rhs), self_b.unbounded_shr_scalar(rhs)])
688      }
689    }
690  }
691
692  #[inline]
693  pub fn saturating_add(self, rhs: Self) -> Self {
694    pick! {
695      if #[cfg(target_feature="avx2")] {
696        Self { avx: add_saturating_u8_m256i(self.avx, rhs.avx) }
697      } else {
698        Self {
699          a : self.a.saturating_add(rhs.a),
700          b : self.b.saturating_add(rhs.b),
701        }
702      }
703    }
704  }
705
706  #[inline]
707  pub fn saturating_sub(self, rhs: Self) -> Self {
708    pick! {
709      if #[cfg(target_feature="avx2")] {
710        Self { avx: sub_saturating_u8_m256i(self.avx, rhs.avx) }
711      } else {
712        Self {
713          a : self.a.saturating_sub(rhs.a),
714          b : self.b.saturating_sub(rhs.b),
715        }
716      }
717    }
718  }
719
720  #[inline]
721  pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
722    let (low, high) = self.mul_keep_low_high(rhs);
723    let overflow = high.simd_ne(Self::ZERO);
724    (low, overflow)
725  }
726
727  optional_fn_widening_mul {
728    #[inline]
729    pub fn widening_mul(self, rhs: Self) -> u16x32 {
730      // x86 has no `_mm256_mul_epu8` intrinsic so there is no `avx2`
731      // optimization.
732
733      let [self_a, self_b] = cast::<u8x32, [u8x16; 2]>(self);
734      let [rhs_a, rhs_b] = cast::<u8x32, [u8x16; 2]>(rhs);
735
736      cast([self_a.widening_mul(rhs_a), self_b.widening_mul(rhs_b)])
737    }
738  }
739
740  #[inline]
741  pub fn mul_keep_low_high(self, rhs: Self) -> (Self, Self) {
742    // x86 has no `_mm256_mul_epu8` intrinsic so there is no `avx2`
743    // optimization.
744
745    let [self_a, self_b] = cast::<u8x32, [u8x16; 2]>(self);
746    let [rhs_a, rhs_b] = cast::<u8x32, [u8x16; 2]>(rhs);
747
748    let result_a = self_a.mul_keep_low_high(rhs_a);
749    let result_b = self_b.mul_keep_low_high(rhs_b);
750    (cast([result_a.0, result_b.0]), cast([result_a.1, result_b.1]))
751  }
752
753  #[inline]
754  pub fn mul_keep_high(self, rhs: Self) -> Self {
755    // x86 has no `_mm256_mul_epu8` intrinsic so there is no `avx2`
756    // optimization.
757
758    let [self_a, self_b] = cast::<u8x32, [u8x16; 2]>(self);
759    let [rhs_a, rhs_b] = cast::<u8x32, [u8x16; 2]>(rhs);
760
761    cast([self_a.mul_keep_high(rhs_a), self_b.mul_keep_high(rhs_b)])
762  }
763
764  optional_fn_deserialize {}
765}
766
767/// The following functionality exists only for [`u8x32`], or only for
768/// particular types inconsistently.
769impl u8x32 {
770  /// Returns a new vector with lanes selected from the lanes of the first input
771  /// vector a specified in the second input vector `rhs`.
772  /// The indices i in range `[0, 15]` select the i-th element of `self`. For
773  /// indices outside of the range the resulting lane is `0`.
774  ///
775  /// This note that is the equivalent of two parallel swizzle operations on the
776  /// two halves of the vector, and the indexes each refer to the
777  /// corresponding half.
778  #[inline]
779  pub fn swizzle_half(self, rhs: i8x32) -> i8x32 {
780    cast(i8x32::swizzle_half(cast(self), cast(rhs)))
781  }
782
783  /// Indices in the range `[0, 15]` will select the i-th element of `self`. If
784  /// the high bit of any element of `rhs` is set (negative) then the
785  /// corresponding output lane is guaranteed to be zero. Otherwise if the
786  /// element of `rhs` is within the range `[32, 127]` then the output lane is
787  /// either `0` or `self[rhs[i] % 16]` depending on the implementation.
788  ///
789  /// This is the equivalent to two parallel swizzle operations on the two
790  /// halves of the vector, and the indexes each refer to their corresponding
791  /// half.
792  #[inline]
793  pub fn swizzle_half_relaxed(self, rhs: u8x32) -> u8x32 {
794    cast(i8x32::swizzle_half_relaxed(cast(self), cast(rhs)))
795  }
796
797  /// Full 32-entry byte table lookup. An index in `[0, 31]` selects
798  /// `self[index]`; any index `>= 32` yields `0`.
799  ///
800  /// This function has been deprecated and replaced with [`shuffle_zeroing`].
801  ///
802  /// [`shuffle_zeroing`]: Self::shuffle_zeroing
803  #[inline]
804  #[deprecated(since = "1.7.0", note = "replaced with `shuffle_zeroing`")]
805  pub fn swizzle(self, rhs: u8x32) -> u8x32 {
806    self.shuffle_zeroing(rhs)
807  }
808
809  /// Like [`swizzle`](Self::swizzle), but out-of-range indices yield an
810  /// implementation-defined result (`0` or `self[index % 32]`).
811  ///
812  /// This function has been deprecated and replaced with [`shuffle`].
813  ///
814  /// [`shuffle`]: Self::shuffle
815  #[inline]
816  #[deprecated(since = "1.7.0", note = "replaced with `shuffle`")]
817  pub fn swizzle_relaxed(self, rhs: u8x32) -> u8x32 {
818    self.shuffle(rhs)
819  }
820}
821
822#[cfg(target_feature = "avx2")]
823impl u8x32 {
824  // There's no `u8` shift instruction, so we cheat: widen every byte to a
825  // `u16`, shift in the wider lane, then shrink back down to `u8`. Shifting
826  // left can push bits past `u8::MAX`, so we keep only the low 8 bits before
827  // the saturating pack (shifting right can't overflow, so the mask is a
828  // no-op there). For the unbounded variants we cap the count at 8, because
829  // shifting a byte by 8 or more gets rid of everything anyway.
830  #[cfg(all(target_feature = "avx512bw", target_feature = "avx512vl"))]
831  #[inline]
832  fn shift_each_u16(self, rhs: m256i, right: bool, unbounded: bool) -> Self {
833    let self16 = convert_to_i16_m512i_from_u8_m256i(self.avx);
834    let count16 = if unbounded {
835      convert_to_u16_m512i_from_u8_m256i(min_u8_m256i(
836        rhs,
837        set_splat_i8_m256i(8),
838      ))
839    } else {
840      bitand_m512i(
841        convert_to_u16_m512i_from_u8_m256i(rhs),
842        set_splat_i16_m512i(7),
843      )
844    };
845    let shifted = if right {
846      shr_each_u16_m512i(self16, count16)
847    } else {
848      shl_each_u16_m512i(self16, count16)
849    };
850    let shifted = bitand_m512i(shifted, set_splat_i16_m512i(0xFF));
851    Self {
852      avx: Self::pack_u16_halves(
853        extract_m256i_from_m512i::<0>(shifted),
854        extract_m256i_from_m512i::<1>(shifted),
855      ),
856    }
857  }
858
859  // `pack_i16_to_u8_m256i` packs 128 bits at a time, which scrambles the lane
860  // order. This un-scrambles it: split the packed result in half, interleave
861  // the 64-bit chunks back together, and reassemble.
862  #[cfg(all(target_feature = "avx512bw", target_feature = "avx512vl"))]
863  #[inline]
864  fn pack_u16_halves(low: m256i, high: m256i) -> m256i {
865    let packed = pack_i16_to_u8_m256i(low, high);
866    let packed_low = extract_m128i_m256i::<0>(packed);
867    let packed_high = extract_m128i_m256i::<1>(packed);
868    let combined_low = unpack_low_i64_m128i(packed_low, packed_high);
869    let combined_high = unpack_high_i64_m128i(packed_low, packed_high);
870    insert_m128i_to_m256i::<1>(
871      insert_m128i_to_m256i::<0>(zeroed_m256i(), combined_low),
872      combined_high,
873    )
874  }
875}