Skip to main content

wide/
i32x16_.rs

1use super::*;
2
3pick! {
4  if #[cfg(target_feature="avx512f")] {
5    #[derive(Default, Clone, Copy, PartialEq, Eq)]
6    #[repr(C, align(64))]
7    pub struct i32x16 { pub(crate) avx512: m512i }
8  } else {
9    #[derive(Default, Clone, Copy, PartialEq, Eq)]
10    #[repr(C, align(64))]
11    pub struct i32x16 { pub(crate) a : i32x8, pub(crate) b : i32x8 }
12  }
13}
14
15int_uint_consts!(i32, 16, i32x16, 512);
16
17unsafe impl Zeroable for i32x16 {}
18unsafe impl Pod for i32x16 {}
19
20impl AlignTo for i32x16 {
21  type Elem = i32;
22}
23
24impl Add for i32x16 {
25  type Output = Self;
26  #[inline]
27  fn add(self, rhs: Self) -> Self::Output {
28    pick! {
29      if #[cfg(target_feature="avx512f")] {
30        Self { avx512: add_i32_m512i(self.avx512, rhs.avx512) }
31      } else {
32        Self {
33          a : self.a.add(rhs.a),
34          b : self.b.add(rhs.b),
35        }
36      }
37    }
38  }
39}
40
41impl Sub for i32x16 {
42  type Output = Self;
43  #[inline]
44  fn sub(self, rhs: Self) -> Self::Output {
45    pick! {
46      if #[cfg(target_feature="avx512f")] {
47        Self { avx512: sub_i32_m512i(self.avx512, rhs.avx512) }
48      } else {
49        Self {
50          a : self.a.sub(rhs.a),
51          b : self.b.sub(rhs.b),
52        }
53      }
54    }
55  }
56}
57
58impl Mul for i32x16 {
59  type Output = Self;
60  #[inline]
61  fn mul(self, rhs: Self) -> Self::Output {
62    pick! {
63      if #[cfg(target_feature="avx512f")] {
64        Self { avx512: mul_i32_keep_low_m512i(self.avx512, rhs.avx512) }
65      } else {
66        Self { a: self.a.mul(rhs.a), b: self.b.mul(rhs.b) }
67      }
68    }
69  }
70}
71
72integer_impl_div_rem!(
73  i32,
74  i32x16,
75  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
76);
77
78impl Add<i32> for i32x16 {
79  type Output = Self;
80  #[inline]
81  fn add(self, rhs: i32) -> Self::Output {
82    self.add(Self::splat(rhs))
83  }
84}
85
86impl Sub<i32> for i32x16 {
87  type Output = Self;
88  #[inline]
89  fn sub(self, rhs: i32) -> Self::Output {
90    self.sub(Self::splat(rhs))
91  }
92}
93
94impl Mul<i32> for i32x16 {
95  type Output = Self;
96  #[inline]
97  fn mul(self, rhs: i32) -> Self::Output {
98    self.mul(Self::splat(rhs))
99  }
100}
101
102impl Add<i32x16> for i32 {
103  type Output = i32x16;
104  #[inline]
105  fn add(self, rhs: i32x16) -> Self::Output {
106    i32x16::splat(self).add(rhs)
107  }
108}
109
110impl Sub<i32x16> for i32 {
111  type Output = i32x16;
112  #[inline]
113  fn sub(self, rhs: i32x16) -> Self::Output {
114    i32x16::splat(self).sub(rhs)
115  }
116}
117
118impl Mul<i32x16> for i32 {
119  type Output = i32x16;
120  #[inline]
121  fn mul(self, rhs: i32x16) -> Self::Output {
122    i32x16::splat(self).mul(rhs)
123  }
124}
125
126impl BitAnd for i32x16 {
127  type Output = Self;
128  #[inline]
129  fn bitand(self, rhs: Self) -> Self::Output {
130    pick! {
131      if #[cfg(target_feature="avx512f")] {
132        Self { avx512: bitand_m512i(self.avx512, rhs.avx512) }
133      } else {
134        Self {
135          a : self.a.bitand(rhs.a),
136          b : self.b.bitand(rhs.b),
137        }
138      }
139    }
140  }
141}
142
143impl BitOr for i32x16 {
144  type Output = Self;
145  #[inline]
146  fn bitor(self, rhs: Self) -> Self::Output {
147    pick! {
148    if #[cfg(target_feature="avx512f")] {
149        Self { avx512: bitor_m512i(self.avx512, rhs.avx512) }
150      } else {
151        Self {
152          a : self.a.bitor(rhs.a),
153          b : self.b.bitor(rhs.b),
154        }
155      }
156    }
157  }
158}
159
160impl BitXor for i32x16 {
161  type Output = Self;
162  #[inline]
163  fn bitxor(self, rhs: Self) -> Self::Output {
164    pick! {
165      if #[cfg(target_feature="avx512f")] {
166        Self { avx512: bitxor_m512i(self.avx512, rhs.avx512) }
167      } else {
168        Self {
169          a : self.a.bitxor(rhs.a),
170          b : self.b.bitxor(rhs.b),
171        }
172      }
173    }
174  }
175}
176
177impl Shl for i32x16 {
178  type Output = Self;
179
180  /// Shifts lanes by the corresponding lane.
181  ///
182  /// Bitwise shift-left; yields `self << mask(rhs)`, where mask removes any
183  /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
184  /// of the type. (same as `wrapping_shl`)
185  #[inline]
186  fn shl(self, rhs: Self) -> Self::Output {
187    pick! {
188      if #[cfg(target_feature="avx512f")] {
189        // Mask `rhs` to 31 to match `wrapping_shl`.
190        let rhs = bitand_m512i(rhs.avx512, set_splat_i32_m512i(31));
191        Self { avx512: shl_each_u32_m512i(self.avx512, rhs) }
192      } else {
193        Self {
194          a: self.a.shl(rhs.a),
195          b: self.b.shl(rhs.b),
196        }
197      }
198    }
199  }
200}
201
202impl Shr for i32x16 {
203  type Output = Self;
204
205  /// Shifts lanes by the corresponding lane.
206  ///
207  /// Bitwise shift-right; yields `self >> mask(rhs)`, where mask removes any
208  /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
209  /// of the type. (same as `wrapping_shr`)
210  #[inline]
211  fn shr(self, rhs: Self) -> Self::Output {
212    pick! {
213      if #[cfg(target_feature="avx512f")] {
214        #[cfg(target_arch = "x86")]
215        use core::arch::x86::_mm512_srav_epi32;
216        #[cfg(target_arch = "x86_64")]
217        use core::arch::x86_64::_mm512_srav_epi32;
218
219        // Mask `rhs` to 31 to match `wrapping_shr`.
220        let rhs = bitand_m512i(rhs.avx512, set_splat_i16_m512i(31));
221        // TODO(safe_arch): Add `_mm512_srav_epi32`.
222        Self { avx512: m512i(unsafe { _mm512_srav_epi32(self.avx512.0, rhs.0) }) }
223      } else {
224        Self {
225          a: self.a >> rhs.a,
226          b: self.b >> rhs.b,
227        }
228      }
229    }
230  }
231}
232
233macro_rules! impl_shl_t_for_i32x16 {
234  ($($shift_type:ty),+ $(,)?) => {
235    $(impl Shl<$shift_type> for i32x16 {
236      type Output = Self;
237      /// Shifts all lanes by the value given.
238      #[inline]
239      fn shl(self, rhs: $shift_type) -> Self::Output {
240        pick! {
241          if #[cfg(target_feature="avx512f")] {
242            let shift = cast(rhs as u32);
243            Self { avx512: shl_all_u32_m512i(self.avx512, shift) }
244          } else {
245            Self {
246              a : self.a.shl(rhs),
247              b : self.b.shl(rhs),
248            }
249          }
250        }
251      }
252    })+
253  };
254}
255impl_shl_t_for_i32x16!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
256
257macro_rules! impl_shr_t_for_i32x16 {
258  ($($shift_type:ty),+ $(,)?) => {
259    $(impl Shr<$shift_type> for i32x16 {
260      type Output = Self;
261      /// Shifts all lanes by the value given.
262      #[inline]
263      fn shr(self, rhs: $shift_type) -> Self::Output {
264        pick! {
265          if #[cfg(target_feature="avx512f")] {
266            let shift = cast(rhs as u32);
267            Self { avx512: shr_all_i32_m512i(self.avx512, shift) }
268          } else {
269            Self {
270              a : self.a.shr(rhs),
271              b : self.b.shr(rhs),
272            }
273          }
274        }
275      }
276    })+
277  };
278}
279impl_shr_t_for_i32x16!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
280
281#[expect(deprecated)]
282impl CmpEq for i32x16 {
283  type Output = Self;
284  #[inline]
285  fn simd_eq(self, rhs: Self) -> Self::Output {
286    pick! {
287      if #[cfg(target_feature="avx512f")] {
288        Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Eq)}>(self.avx512, rhs.avx512) }
289      } else {
290        Self {
291          a : self.a.simd_eq(rhs.a),
292          b : self.b.simd_eq(rhs.b),
293        }
294      }
295    }
296  }
297}
298
299#[expect(deprecated)]
300impl CmpLt for i32x16 {
301  type Output = Self;
302  #[inline]
303  fn simd_lt(self, rhs: Self) -> Self::Output {
304    pick! {
305      if #[cfg(target_feature="avx512f")] {
306        Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Lt)}>(self.avx512, rhs.avx512) }
307      } else {
308        Self {
309          a : rhs.a.simd_gt(self.a),
310          b : rhs.b.simd_gt(self.b),
311        }
312      }
313    }
314  }
315}
316
317#[expect(deprecated)]
318impl CmpGt for i32x16 {
319  type Output = Self;
320  #[inline]
321  fn simd_gt(self, rhs: Self) -> Self::Output {
322    pick! {
323      if #[cfg(target_feature="avx512f")] {
324        Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Nle)}>(self.avx512, rhs.avx512) }
325      } else {
326        Self {
327          a : self.a.simd_gt(rhs.a),
328          b : self.b.simd_gt(rhs.b),
329        }
330      }
331    }
332  }
333}
334
335#[expect(deprecated)]
336impl CmpNe for i32x16 {
337  type Output = Self;
338  #[inline]
339  fn simd_ne(self, rhs: Self) -> Self::Output {
340    pick! {
341      if #[cfg(target_feature="avx512f")] {
342        Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Ne)}>(self.avx512, rhs.avx512) }
343      } else {
344        Self {
345          a : self.a.simd_ne(rhs.a),
346          b : self.b.simd_ne(rhs.b),
347        }
348      }
349    }
350  }
351}
352
353#[expect(deprecated)]
354impl CmpLe for i32x16 {
355  type Output = Self;
356  #[inline]
357  fn simd_le(self, rhs: Self) -> Self::Output {
358    pick! {
359      if #[cfg(target_feature="avx512f")] {
360        Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Le)}>(self.avx512, rhs.avx512) }
361      } else {
362        Self {
363          a : self.a.simd_le(rhs.a),
364          b : self.b.simd_le(rhs.b),
365        }
366      }
367    }
368  }
369}
370
371#[expect(deprecated)]
372impl CmpGe for i32x16 {
373  type Output = Self;
374  #[inline]
375  fn simd_ge(self, rhs: Self) -> Self::Output {
376    pick! {
377      if #[cfg(target_feature="avx512f")] {
378        Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Nlt)}>(self.avx512, rhs.avx512) }
379      } else {
380        Self {
381          a : self.a.simd_ge(rhs.a),
382          b : self.b.simd_ge(rhs.b),
383        }
384      }
385    }
386  }
387}
388
389impl i32x16 {
390  #[inline]
391  #[must_use]
392  pub const fn new(array: [i32; 16]) -> Self {
393    unsafe { core::mem::transmute(array) }
394  }
395
396  simd_comparison_fns!();
397
398  #[inline]
399  #[must_use]
400  pub fn blend(self, t: Self, f: Self) -> Self {
401    pick! {
402      if #[cfg(target_feature="avx512f")] {
403        Self { avx512: blend_varying_i8_m512i(f.avx512,t.avx512,movepi8_mask_m512i(self.avx512)) }
404      } else {
405        Self {
406          a : self.a.blend(t.a, f.a),
407          b : self.b.blend(t.b, f.b),
408        }
409      }
410    }
411  }
412
413  /// Returns true for each positive element and false if it is zero or
414  /// negative.
415  #[inline]
416  #[must_use]
417  pub fn is_positive(self) -> Self {
418    pick! {
419      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
420        // `neon` has dedicated greater-than-zero intrinsics.
421        Self {
422          a: self.a.is_positive(),
423          b: self.b.is_positive(),
424        }
425      } else {
426        self.simd_gt(Self::ZERO)
427      }
428    }
429  }
430
431  /// Returns true for each negative element and false if it is zero or
432  /// positive.
433  #[inline]
434  #[must_use]
435  pub fn is_negative(self) -> Self {
436    pick! {
437      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
438        // `neon` has dedicated less-than-zero intrinsics.
439        Self {
440          a: self.a.is_negative(),
441          b: self.b.is_negative(),
442        }
443      } else {
444        self.simd_lt(Self::ZERO)
445      }
446    }
447  }
448
449  #[inline]
450  #[must_use]
451  pub fn min(self, rhs: Self) -> Self {
452    pick! {
453      if #[cfg(target_feature="avx512f")] {
454        Self { avx512: min_i32_m512i(self.avx512, rhs.avx512) }
455      } else {
456        Self {
457          a: self.a.min(rhs.a),
458          b: self.b.min(rhs.b),
459        }
460      }
461    }
462  }
463
464  #[inline]
465  #[must_use]
466  pub fn max(self, rhs: Self) -> Self {
467    pick! {
468      if #[cfg(target_feature="avx512f")] {
469        Self { avx512: max_i32_m512i(self.avx512, rhs.avx512) }
470      } else {
471        Self {
472          a: self.a.max(rhs.a),
473          b: self.b.max(rhs.b),
474        }
475      }
476    }
477  }
478
479  integer_fn_clamp!();
480
481  #[inline]
482  #[must_use]
483  pub fn saturating_add(self, rhs: Self) -> Self {
484    pick! {
485      if #[cfg(target_feature="avx512f")] {
486        let result = self + rhs;
487        let overflow = (!(self ^ rhs) & (self ^ result)).is_negative();
488        let negative = self.is_negative();
489
490        overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
491      } else {
492        Self {
493          a: self.a.saturating_add(rhs.a),
494          b: self.b.saturating_add(rhs.b),
495        }
496      }
497    }
498  }
499
500  #[inline]
501  #[must_use]
502  pub fn saturating_sub(self, rhs: Self) -> Self {
503    pick! {
504      if #[cfg(target_feature="avx512f")] {
505        let result = self - rhs;
506        let overflow = ((self ^ rhs) & (self ^ result)).is_negative();
507        let negative = self.is_negative();
508
509        overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
510      } else {
511        Self {
512          a: self.a.saturating_sub(rhs.a),
513          b: self.b.saturating_sub(rhs.b),
514        }
515      }
516    }
517  }
518
519  /// Lanewise saturating multiply.
520  #[inline]
521  #[must_use]
522  pub fn saturating_mul(self, rhs: Self) -> Self {
523    pick! {
524      if #[cfg(all(target_feature="avx512f", target_feature="avx512dq"))] {
525        #[cfg(target_arch = "x86")]
526        use core::arch::x86::{_mm512_unpackhi_epi64, _mm512_unpacklo_epi64};
527        #[cfg(target_arch = "x86_64")]
528        use core::arch::x86_64::{_mm512_unpackhi_epi64, _mm512_unpacklo_epi64};
529
530        let even_wide_mul = mul_i32_wide_m512i(self.avx512, rhs.avx512);
531        let odd_wide_mul = mul_i32_wide_m512i(
532          shuffle_i32_m512i::<0b_00_11_00_01>(self.avx512),
533          shuffle_i32_m512i::<0b_00_11_00_01>(rhs.avx512),
534        );
535
536        let ll_hh_1 = unpack_low_i32_m512i(even_wide_mul, odd_wide_mul);
537        let ll_hh_2 = unpack_high_i32_m512i(even_wide_mul, odd_wide_mul);
538        // TODO(safe_arch): Add `_mm512_unpacklo_epi64` and `_mm512_unpackhi_epi64`.
539        let low = Self {
540          avx512: m512i(unsafe { _mm512_unpacklo_epi64(ll_hh_1.0, ll_hh_2.0) }),
541        };
542        let high = Self {
543          avx512: m512i(unsafe { _mm512_unpackhi_epi64(ll_hh_1.0, ll_hh_2.0) }),
544        };
545
546        let no_overflow = high.simd_eq(low.is_negative());
547        let limit = Self::MAX ^ (self ^ rhs).is_negative();
548        no_overflow.blend(low, limit)
549      } else {
550        let [self_a, self_b]: [i32x8; 2] = cast(self);
551        let [rhs_a, rhs_b]: [i32x8; 2] = cast(rhs);
552
553        cast([self_a.saturating_mul(rhs_a), self_b.saturating_mul(rhs_b)])
554      }
555    }
556  }
557
558  integer_fn_saturating_div!([
559    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
560  ]);
561
562  /// horizontal add of all the elements of the vector
563  #[inline]
564  #[must_use]
565  pub fn reduce_add(self) -> i32 {
566    let arr: [i32x8; 2] = cast(self);
567    (arr[0] + arr[1]).reduce_add()
568  }
569
570  /// horizontal min of all the elements of the vector
571  #[inline]
572  #[must_use]
573  pub fn reduce_min(self) -> i32 {
574    let arr: [i32x8; 2] = cast(self);
575    arr[0].min(arr[1]).reduce_min()
576  }
577
578  /// horizontal max of all the elements of the vector
579  #[inline]
580  #[must_use]
581  pub fn reduce_max(self) -> i32 {
582    let arr: [i32x8; 2] = cast(self);
583    arr[0].max(arr[1]).reduce_max()
584  }
585
586  #[inline]
587  #[must_use]
588  pub fn abs(self) -> Self {
589    pick! {
590      if #[cfg(target_feature="avx512bw")] {
591        Self { avx512: abs_i32_m512i(self.avx512) }
592      } else {
593        Self {
594          a : self.a.abs(),
595          b : self.b.abs(),
596        }
597      }
598    }
599  }
600
601  #[inline]
602  #[must_use]
603  pub fn unsigned_abs(self) -> u32x16 {
604    pick! {
605      if #[cfg(target_feature="avx512bw")] {
606        u32x16 { avx512: abs_i32_m512i(self.avx512) }
607      } else {
608        u32x16 {
609          a : self.a.unsigned_abs(),
610          b : self.b.unsigned_abs(),
611        }
612      }
613    }
614  }
615
616  signed_fn_signum!();
617
618  #[inline]
619  #[must_use]
620  #[doc(alias("movemask", "move_mask"))]
621  pub fn to_bitmask(self) -> u32 {
622    pick! {
623      if #[cfg(target_feature="avx512dq")] {
624        movepi32_mask_m512i(self.avx512) as u32
625      } else {
626        self.a.to_bitmask() | (self.b.to_bitmask() << 8)
627      }
628    }
629  }
630
631  #[inline]
632  #[must_use]
633  pub fn any(self) -> bool {
634    pick! {
635      if #[cfg(target_feature="avx512bw")] {
636        movepi32_mask_m512i(self.avx512) != 0
637      } else {
638        let [a, b]: [i32x8; 2] = cast(self);
639        (a | b).any()
640      }
641    }
642  }
643
644  #[inline]
645  #[must_use]
646  pub fn all(self) -> bool {
647    pick! {
648      if #[cfg(target_feature="avx512bw")] {
649        movepi32_mask_m512i(self.avx512) == 0xFFFF
650      } else {
651        let [a, b]: [i32x8; 2] = cast(self);
652        (a & b).all()
653      }
654    }
655  }
656
657  #[inline]
658  #[must_use]
659  pub fn none(self) -> bool {
660    !self.any()
661  }
662
663  /// Transpose matrix of 16x16 `i32` matrix. Currently not accelerated.
664  #[must_use]
665  #[inline]
666  pub fn transpose(data: [i32x16; 16]) -> [i32x16; 16] {
667    // Can this be optimized?
668
669    #[inline(always)]
670    fn transpose_column(data: &[i32x16; 16], index: usize) -> i32x16 {
671      i32x16::new([
672        data[0].as_array()[index],
673        data[1].as_array()[index],
674        data[2].as_array()[index],
675        data[3].as_array()[index],
676        data[4].as_array()[index],
677        data[5].as_array()[index],
678        data[6].as_array()[index],
679        data[7].as_array()[index],
680        data[8].as_array()[index],
681        data[9].as_array()[index],
682        data[10].as_array()[index],
683        data[11].as_array()[index],
684        data[12].as_array()[index],
685        data[13].as_array()[index],
686        data[14].as_array()[index],
687        data[15].as_array()[index],
688      ])
689    }
690
691    [
692      transpose_column(&data, 0),
693      transpose_column(&data, 1),
694      transpose_column(&data, 2),
695      transpose_column(&data, 3),
696      transpose_column(&data, 4),
697      transpose_column(&data, 5),
698      transpose_column(&data, 6),
699      transpose_column(&data, 7),
700      transpose_column(&data, 8),
701      transpose_column(&data, 9),
702      transpose_column(&data, 10),
703      transpose_column(&data, 11),
704      transpose_column(&data, 12),
705      transpose_column(&data, 13),
706      transpose_column(&data, 14),
707      transpose_column(&data, 15),
708    ]
709  }
710
711  #[inline]
712  pub fn to_array(self) -> [i32; 16] {
713    cast(self)
714  }
715
716  #[inline]
717  pub fn as_array(&self) -> &[i32; 16] {
718    cast_ref(self)
719  }
720
721  #[inline]
722  pub fn as_mut_array(&mut self) -> &mut [i32; 16] {
723    cast_mut(self)
724  }
725
726  #[inline]
727  #[must_use]
728  pub fn round_float(self) -> f32x16 {
729    pick! {
730      if #[cfg(target_feature="avx512f")] {
731        cast(convert_to_m512_from_i32_m512i(self.avx512))
732      } else {
733        f32x16 {
734          a: self.a.round_float(),
735          b: self.b.round_float(),
736        }
737      }
738    }
739  }
740}
741
742impl Not for i32x16 {
743  type Output = Self;
744  #[inline]
745  fn not(self) -> Self::Output {
746    pick! {
747      if #[cfg(target_feature="avx512f")] {
748        Self { avx512: bitxor_m512i(self.avx512, set_splat_i32_m512i(-1)) }
749      } else {
750        Self {
751          a : self.a.not(),
752          b : self.b.not(),
753        }
754      }
755    }
756  }
757}