1use super::*;
2
3pick! {
4 if #[cfg(target_feature="avx2")] {
5 #[derive(Default, Clone, Copy, PartialEq, Eq)]
12 #[repr(C, align(32))]
13 pub struct i32x8 { pub(crate) avx2: m256i }
14 } else {
15 #[derive(Default, Clone, Copy, PartialEq, Eq)]
22 #[repr(C, align(32))]
23 pub struct i32x8 { pub(crate) a : i32x4, pub(crate) b : i32x4}
24 }
25}
26
27impl_simd_int! {
28 unsafe {
29 T = i32,
30 N = 8,
31 Simd = i32x8,
32 UintSimd = u32x8,
33 T_BITS = 32,
34 T_BITS_MUL_2 = 64,
35 BitmaskType = u32,
36 [0, 1, 2, 3, 4, 5, 6, 7],
37 optional_type_x86_inner { X86Inner = __m256i },
38 optional_type_arm_inner {},
39 optional_type_wasm_inner {},
40 }
41
42 #[inline]
43 fn simd_lt(self, rhs: Self) -> Self::Output {
44 pick! {
45 if #[cfg(target_feature="avx2")] {
46 Self { avx2: cmp_gt_mask_i32_m256i(rhs.avx2, self.avx2) }
47 } else {
48 Self {
49 a : self.a.simd_lt(rhs.a),
50 b : self.b.simd_lt(rhs.b),
51 }
52 }
53 }
54 }
55
56 #[inline]
57 fn simd_gt(self, rhs: Self) -> Self::Output {
58 pick! {
59 if #[cfg(target_feature="avx2")] {
60 Self { avx2: cmp_gt_mask_i32_m256i(self.avx2, rhs.avx2) }
61 } else {
62 Self {
63 a : self.a.simd_gt(rhs.a),
64 b : self.b.simd_gt(rhs.b),
65 }
66 }
67 }
68 }
69
70 #[inline]
71 fn simd_le(self, rhs: Self) -> Self::Output {
72 pick! {
73 if #[cfg(target_feature="avx2")] {
74 !self.simd_gt(rhs)
75 } else {
76 Self {
77 a : self.a.simd_le(rhs.a),
78 b : self.b.simd_le(rhs.b),
79 }
80 }
81 }
82 }
83
84 #[inline]
85 fn simd_ge(self, rhs: Self) -> Self::Output {
86 pick! {
87 if #[cfg(target_feature="avx2")] {
88 !self.simd_lt(rhs)
89 } else {
90 Self {
91 a : self.a.simd_ge(rhs.a),
92 b : self.b.simd_ge(rhs.b),
93 }
94 }
95 }
96 }
97
98 #[inline]
99 fn shr(self, rhs: u32x8) -> Self::Output {
100 pick! {
101 if #[cfg(target_feature="avx2")] {
102 let shift_by = bitand_m256i(rhs.avx2, set_splat_i32_m256i(31));
104 Self { avx2: shr_each_i32_m256i(self.avx2, shift_by ) }
105 } else {
106 Self {
107 a : self.a.shr(rhs.a),
108 b : self.b.shr(rhs.b),
109 }
110 }
111 }
112 }
113
114 #[inline]
115 fn shr(self, rhs: u32) -> Self::Output {
116 pick! {
117 if #[cfg(target_feature="avx2")] {
118 #[expect(clippy::suspicious_arithmetic_impl)]
120 let shift = cast([rhs as u64 & 31, 0]);
121 Self { avx2: shr_all_i32_m256i(self.avx2, shift) }
122 } else {
123 Self {
124 a : self.a.shr(rhs),
125 b : self.b.shr(rhs),
126 }
127 }
128 }
129 }
130
131 #[inline]
132 pub fn max(self, rhs: Self) -> Self {
133 pick! {
134 if #[cfg(target_feature="avx2")] {
135 Self { avx2: max_i32_m256i(self.avx2, rhs.avx2) }
136 } else {
137 Self {
138 a : self.a.max(rhs.a),
139 b : self.b.max(rhs.b),
140 }
141 }
142 }
143 }
144
145 #[inline]
146 pub fn min(self, rhs: Self) -> Self {
147 pick! {
148 if #[cfg(target_feature="avx2")] {
149 Self { avx2: min_i32_m256i(self.avx2, rhs.avx2) }
150 } else {
151 Self {
152 a : self.a.min(rhs.a),
153 b : self.b.min(rhs.b),
154 }
155 }
156 }
157 }
158
159 #[inline]
160 pub fn reduce_max(self) -> i32 {
161 let arr: [i32x4; 2] = cast(self);
162 arr[0].max(arr[1]).reduce_max()
163 }
164
165 #[inline]
166 pub fn reduce_min(self) -> i32 {
167 let arr: [i32x4; 2] = cast(self);
168 arr[0].min(arr[1]).reduce_min()
169 }
170
171 #[inline]
172 pub fn unbounded_shr(self, rhs: u32x8) -> Self {
173 pick! {
174 if #[cfg(target_feature="avx2")] {
175 Self { avx2: shr_each_i32_m256i(self.avx2, rhs.avx2) }
176 } else {
177 Self {
178 a: self.a.unbounded_shr(rhs.a),
179 b: self.b.unbounded_shr(rhs.b),
180 }
181 }
182 }
183 }
184
185 #[inline]
186 pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
187 pick! {
188 if #[cfg(target_feature="avx2")] {
189 Self { avx2: shr_all_i32_m256i(self.avx2, cast([rhs as u64, 0])) }
190 } else {
191 Self {
192 a: self.a.unbounded_shr_scalar(rhs),
193 b: self.b.unbounded_shr_scalar(rhs),
194 }
195 }
196 }
197 }
198
199 #[inline]
200 pub fn saturating_add(self, rhs: Self) -> Self {
201 pick! {
202 if #[cfg(target_feature="avx2")] {
203 let result = self + rhs;
204 let overflow = (!(self ^ rhs) & (self ^ result)).is_negative();
205 let negative = self.is_negative();
206
207 overflow.select(Self::MAX ^ negative, result)
209 } else {
210 Self {
211 a: self.a.saturating_add(rhs.a),
212 b: self.b.saturating_add(rhs.b),
213 }
214 }
215 }
216 }
217
218 #[inline]
219 pub fn saturating_sub(self, rhs: Self) -> Self {
220 pick! {
221 if #[cfg(target_feature="avx2")] {
222 let result = self - rhs;
223 let overflow = ((self ^ rhs) & (self ^ result)).is_negative();
224 let negative = self.is_negative();
225
226 overflow.select(Self::MAX ^ negative, result)
228 } else {
229 Self {
230 a: self.a.saturating_sub(rhs.a),
231 b: self.b.saturating_sub(rhs.b),
232 }
233 }
234 }
235 }
236
237 #[inline]
238 pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
239 let (low, high) = self.mul_keep_low_high(rhs);
240 let low = cast::<u32x8, i32x8>(low);
241
242 let overflow = high.simd_ne(low.is_negative());
243 (low, overflow)
244 }
245
246 optional_fn_widening_mul {
247 #[inline]
248 pub fn widening_mul(self, rhs: Self) -> i64x8 {
249 pick! {
250 if #[cfg(all(target_feature="avx512f", target_feature="avx2"))] {
251 const SHUFFLE_INDICES: m512i = i64x8::new([0, 4, 1, 5, 2, 6, 3, 7]).avx512;
252
253 let even_wide_mul = mul_i64_low_bits_m256i(self.avx2, rhs.avx2);
254 let odd_wide_mul = mul_i64_low_bits_m256i(
255 shuffle_ai_i32_half_m256i::<0b_00_11_00_01>(self.avx2),
256 shuffle_ai_i32_half_m256i::<0b_00_11_00_01>(rhs.avx2),
257 );
258 let even_then_odd = cast::<[m256i; 2], m512i>([even_wide_mul, odd_wide_mul]);
259 i64x8 {
260 avx512: permute_i64_m512i(SHUFFLE_INDICES, even_then_odd),
261 }
262 } else if #[cfg(target_feature="avx2")] {
263 let even_wide_mul = mul_i64_low_bits_m256i(self.avx2, rhs.avx2);
264 let odd_wide_mul = mul_i64_low_bits_m256i(
265 shuffle_ai_i32_half_m256i::<0b_00_11_00_01>(self.avx2),
266 shuffle_ai_i32_half_m256i::<0b_00_11_00_01>(rhs.avx2),
267 );
268 let m0145 = unpack_low_i64_m256i(even_wide_mul, odd_wide_mul);
269 let m2367 = unpack_high_i64_m256i(even_wide_mul, odd_wide_mul);
270
271 cast([
272 shuffle_abi_i128z_all_m256i::<0b_0010_0000>(m0145, m2367),
273 shuffle_abi_i128z_all_m256i::<0b_0011_0001>(m0145, m2367),
274 ])
275 } else {
276 let [self_a, self_b] = cast::<i32x8, [i32x4; 2]>(self);
277 let [rhs_a, rhs_b] = cast::<i32x8, [i32x4; 2]>(rhs);
278
279 cast([self_a.widening_mul(rhs_a), self_b.widening_mul(rhs_b)])
280 }
281 }
282 }
283 }
284
285 #[inline]
286 pub fn mul_keep_low_high(self, rhs: Self) -> (u32x8, i32x8) {
287 pick! {
288 if #[cfg(target_feature="avx2")] {
289 let even_wide_mul = mul_i64_low_bits_m256i(self.avx2, rhs.avx2);
290 let odd_wide_mul = mul_i64_low_bits_m256i(
291 shuffle_ai_i32_half_m256i::<0b_00_11_00_01>(self.avx2),
292 shuffle_ai_i32_half_m256i::<0b_00_11_00_01>(rhs.avx2),
293 );
294 let ll_hh_1 = unpack_low_i32_m256i(even_wide_mul, odd_wide_mul);
295 let ll_hh_2 = unpack_high_i32_m256i(even_wide_mul, odd_wide_mul);
296
297 (
298 u32x8 { avx2: unpack_low_i64_m256i(ll_hh_1, ll_hh_2) },
299 i32x8 { avx2: unpack_high_i64_m256i(ll_hh_1, ll_hh_2) },
300 )
301 } else {
302 let [self_a, self_b] = cast::<i32x8, [i32x4; 2]>(self);
303 let [rhs_a, rhs_b] = cast::<i32x8, [i32x4; 2]>(rhs);
304
305 let result_a = self_a.mul_keep_low_high(rhs_a);
306 let result_b = self_b.mul_keep_low_high(rhs_b);
307 (
308 cast([result_a.0, result_b.0]),
309 cast([result_a.1, result_b.1]),
310 )
311 }
312 }
313 }
314
315 #[inline]
316 pub fn mul_keep_high(self, rhs: Self) -> Self {
317 pick! {
318 if #[cfg(target_feature="avx2")] {
319 let even_wide_mul = mul_i64_low_bits_m256i(self.avx2, rhs.avx2);
320 let odd_wide_mul = mul_i64_low_bits_m256i(
321 shuffle_ai_i32_half_m256i::<0b_00_11_00_01>(self.avx2),
322 shuffle_ai_i32_half_m256i::<0b_00_11_00_01>(rhs.avx2),
323 );
324 let ll_hh_1 = unpack_low_i32_m256i(even_wide_mul, odd_wide_mul);
325 let ll_hh_2 = unpack_high_i32_m256i(even_wide_mul, odd_wide_mul);
326
327 Self { avx2: unpack_high_i64_m256i(ll_hh_1, ll_hh_2) }
328 } else {
329 let [self_a, self_b] = cast::<i32x8, [i32x4; 2]>(self);
330 let [rhs_a, rhs_b] = cast::<i32x8, [i32x4; 2]>(rhs);
331
332 cast([self_a.mul_keep_high(rhs_a), self_b.mul_keep_high(rhs_b)])
333 }
334 }
335 }
336
337 #[inline]
338 pub fn abs(self) -> Self {
339 pick! {
340 if #[cfg(target_feature="avx2")] {
341 Self { avx2: abs_i32_m256i(self.avx2) }
342 } else {
343 Self {
344 a : self.a.abs(),
345 b : self.b.abs(),
346 }
347 }
348 }
349 }
350
351 #[inline]
352 pub fn is_positive(self) -> Self {
353 pick! {
354 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
355 Self {
357 a: self.a.is_positive(),
358 b: self.b.is_positive(),
359 }
360 } else {
361 self.simd_gt(Self::ZERO)
362 }
363 }
364 }
365
366 #[inline]
367 pub fn is_negative(self) -> Self {
368 pick! {
369 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
370 Self {
372 a: self.a.is_negative(),
373 b: self.b.is_negative(),
374 }
375 } else {
376 self.simd_lt(Self::ZERO)
377 }
378 }
379 }
380
381 optional_fn_deserialize {}
382}
383
384impl From<i16x8> for i32x8 {
385 #[inline]
386 fn from(value: i16x8) -> Self {
387 i32x8::from_i16x8(value)
388 }
389}
390
391impl i32x8 {
394 #[inline]
396 #[must_use]
397 pub fn from_i16x8(v: i16x8) -> Self {
398 pick! {
399 if #[cfg(target_feature="avx2")] {
400 i32x8 { avx2:convert_to_i32_m256i_from_i16_m128i(v.sse) }
401 } else if #[cfg(target_feature="sse2")] {
402 i32x8 {
403 a: i32x4 { sse: shr_imm_i32_m128i::<16>( unpack_low_i16_m128i(v.sse, v.sse)) },
404 b: i32x4 { sse: shr_imm_i32_m128i::<16>( unpack_high_i16_m128i(v.sse, v.sse)) },
405 }
406 } else {
407 i32x8::new([
408 i32::from(v.as_array()[0]),
409 i32::from(v.as_array()[1]),
410 i32::from(v.as_array()[2]),
411 i32::from(v.as_array()[3]),
412 i32::from(v.as_array()[4]),
413 i32::from(v.as_array()[5]),
414 i32::from(v.as_array()[6]),
415 i32::from(v.as_array()[7]),
416 ])
417 }
418 }
419 }
420
421 #[inline]
423 #[must_use]
424 pub fn from_u16x8(v: u16x8) -> Self {
425 pick! {
426 if #[cfg(target_feature="avx2")] {
427 i32x8 { avx2:convert_to_i32_m256i_from_u16_m128i(v.sse) }
428 } else if #[cfg(target_feature="sse2")] {
429 i32x8 {
430 a: i32x4 { sse: shr_imm_u32_m128i::<16>( unpack_low_i16_m128i(v.sse, v.sse)) },
431 b: i32x4 { sse: shr_imm_u32_m128i::<16>( unpack_high_i16_m128i(v.sse, v.sse)) },
432 }
433 } else {
434 i32x8::new([
435 i32::from(v.as_array()[0]),
436 i32::from(v.as_array()[1]),
437 i32::from(v.as_array()[2]),
438 i32::from(v.as_array()[3]),
439 i32::from(v.as_array()[4]),
440 i32::from(v.as_array()[5]),
441 i32::from(v.as_array()[6]),
442 i32::from(v.as_array()[7]),
443 ])
444 }
445 }
446 }
447
448 #[inline]
450 #[must_use]
451 pub fn round_float(self) -> f32x8 {
452 pick! {
453 if #[cfg(target_feature="avx2")] {
454 cast(convert_to_m256_from_i32_m256i(self.avx2))
455 } else {
456 cast([
457 self.a.round_float(),
458 self.b.round_float(),
459 ])
460 }
461 }
462 }
463}