1use super::*;
2
3pick! {
4 if #[cfg(target_feature="sse2")] {
5 #[derive(Default, Clone, Copy, PartialEq, Eq)]
12 #[repr(C, align(16))]
13 pub struct u32x4 { pub(crate) sse: m128i }
14 } else if #[cfg(target_feature="simd128")] {
15 use core::arch::wasm32::*;
16
17 #[derive(Clone, Copy)]
24 #[repr(transparent)]
25 pub struct u32x4 { pub(crate) simd: v128 }
26
27 impl Default for u32x4 {
28 fn default() -> Self {
29 Self::splat(0)
30 }
31 }
32
33 impl PartialEq for u32x4 {
34 fn eq(&self, other: &Self) -> bool {
35 u32x4_all_true(u32x4_eq(self.simd, other.simd))
36 }
37 }
38
39 impl Eq for u32x4 { }
40 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
41 use core::arch::aarch64::*;
42
43 #[repr(C)]
50 #[derive(Copy, Clone)]
51 pub struct u32x4 { pub(crate) neon : uint32x4_t }
52
53 impl Default for u32x4 {
54 #[inline]
55 fn default() -> Self {
56 Self::splat(0)
57 }
58 }
59
60 impl PartialEq for u32x4 {
61 #[inline]
62 fn eq(&self, other: &Self) -> bool {
63 unsafe { vminvq_u32(vceqq_u32(self.neon, other.neon))==u32::MAX }
64 }
65 }
66
67 impl Eq for u32x4 { }
68} else {
69 #[derive(Default, Clone, Copy, PartialEq, Eq)]
76 #[repr(C, align(16))]
77 pub struct u32x4 { arr: [u32;4] }
78 }
79}
80
81impl_simd_uint! {
82 unsafe {
83 T = u32,
84 N = 4,
85 Simd = u32x4,
86 IntSimd = i32x4,
87 T_BITS = 32,
88 T_BITS_MUL_2 = 64,
89 [0, 1, 2, 3],
90 optional_type_x86_inner { X86Inner = __m128i },
91 optional_type_arm_inner { ArmInner = uint32x4_t },
92 optional_type_wasm_inner { WasmInner = v128 },
93 }
94
95 #[inline]
96 fn not(self) -> Self::Output {
97 self ^ cast::<u128, u32x4>(u128::MAX)
98 }
99
100 #[inline]
101 fn add(self, rhs: Self) -> Self::Output {
102 pick! {
103 if #[cfg(target_feature="sse2")] {
104 Self { sse: add_i32_m128i(self.sse, rhs.sse) }
105 } else if #[cfg(target_feature="simd128")] {
106 Self { simd: u32x4_add(self.simd, rhs.simd) }
107 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
108 unsafe { Self { neon: vaddq_u32(self.neon, rhs.neon) } }
109 } else {
110 Self { arr: [
111 self.arr[0].wrapping_add(rhs.arr[0]),
112 self.arr[1].wrapping_add(rhs.arr[1]),
113 self.arr[2].wrapping_add(rhs.arr[2]),
114 self.arr[3].wrapping_add(rhs.arr[3]),
115 ]}
116 }
117 }
118 }
119
120 #[inline]
121 fn sub(self, rhs: Self) -> Self::Output {
122 pick! {
123 if #[cfg(target_feature="sse2")] {
124 Self { sse: sub_i32_m128i(self.sse, rhs.sse) }
125 } else if #[cfg(target_feature="simd128")] {
126 Self { simd: u32x4_sub(self.simd, rhs.simd) }
127 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
128 unsafe {Self { neon: vsubq_u32(self.neon, rhs.neon) }}
129 } else {
130 Self { arr: [
131 self.arr[0].wrapping_sub(rhs.arr[0]),
132 self.arr[1].wrapping_sub(rhs.arr[1]),
133 self.arr[2].wrapping_sub(rhs.arr[2]),
134 self.arr[3].wrapping_sub(rhs.arr[3]),
135 ]}
136 }
137 }
138 }
139
140 #[inline]
141 fn mul(self, rhs: Self) -> Self::Output {
142 pick! {
143 if #[cfg(target_feature="sse4.1")] {
144 Self { sse: mul_32_m128i(self.sse, rhs.sse) }
145 } else if #[cfg(target_feature="simd128")] {
146 Self { simd: u32x4_mul(self.simd, rhs.simd) }
147 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
148 unsafe {Self { neon: vmulq_u32(self.neon, rhs.neon) }}
149 } else {
150 let arr1: [u32; 4] = cast(self);
151 let arr2: [u32; 4] = cast(rhs);
152 cast([
153 arr1[0].wrapping_mul(arr2[0]),
154 arr1[1].wrapping_mul(arr2[1]),
155 arr1[2].wrapping_mul(arr2[2]),
156 arr1[3].wrapping_mul(arr2[3]),
157 ])
158 }
159 }
160 }
161
162 #[inline]
163 fn bitand(self, rhs: Self) -> Self::Output {
164 pick! {
165 if #[cfg(target_feature="sse2")] {
166 Self { sse: bitand_m128i(self.sse, rhs.sse) }
167 } else if #[cfg(target_feature="simd128")] {
168 Self { simd: v128_and(self.simd, rhs.simd) }
169 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
170 unsafe {Self { neon: vandq_u32(self.neon, rhs.neon) }}
171 } else {
172 Self { arr: [
173 self.arr[0].bitand(rhs.arr[0]),
174 self.arr[1].bitand(rhs.arr[1]),
175 self.arr[2].bitand(rhs.arr[2]),
176 self.arr[3].bitand(rhs.arr[3]),
177 ]}
178 }
179 }
180 }
181
182 #[inline]
183 fn bitor(self, rhs: Self) -> Self::Output {
184 pick! {
185 if #[cfg(target_feature="sse2")] {
186 Self { sse: bitor_m128i(self.sse, rhs.sse) }
187 } else if #[cfg(target_feature="simd128")] {
188 Self { simd: v128_or(self.simd, rhs.simd) }
189 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
190 unsafe {Self { neon: vorrq_u32(self.neon, rhs.neon) }}
191 } else {
192 Self { arr: [
193 self.arr[0].bitor(rhs.arr[0]),
194 self.arr[1].bitor(rhs.arr[1]),
195 self.arr[2].bitor(rhs.arr[2]),
196 self.arr[3].bitor(rhs.arr[3]),
197 ]}
198 }
199 }
200 }
201
202 #[inline]
203 fn bitxor(self, rhs: Self) -> Self::Output {
204 pick! {
205 if #[cfg(target_feature="sse2")] {
206 Self { sse: bitxor_m128i(self.sse, rhs.sse) }
207 } else if #[cfg(target_feature="simd128")] {
208 Self { simd: v128_xor(self.simd, rhs.simd) }
209 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
210 unsafe {Self { neon: veorq_u32(self.neon, rhs.neon) }}
211 } else {
212 Self { arr: [
213 self.arr[0].bitxor(rhs.arr[0]),
214 self.arr[1].bitxor(rhs.arr[1]),
215 self.arr[2].bitxor(rhs.arr[2]),
216 self.arr[3].bitxor(rhs.arr[3]),
217 ]}
218 }
219 }
220 }
221
222 #[inline]
223 fn simd_eq(self, rhs: Self) -> Self::Output {
224 pick! {
225 if #[cfg(target_feature="sse2")] {
226 Self { sse: cmp_eq_mask_i32_m128i(self.sse, rhs.sse) }
227 } else if #[cfg(target_feature="simd128")] {
228 Self { simd: u32x4_eq(self.simd, rhs.simd) }
229 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
230 unsafe {Self { neon: vceqq_u32(self.neon, rhs.neon) }}
231 } else {
232 Self { arr: [
233 if self.arr[0] == rhs.arr[0] { u32::MAX } else { 0 },
234 if self.arr[1] == rhs.arr[1] { u32::MAX } else { 0 },
235 if self.arr[2] == rhs.arr[2] { u32::MAX } else { 0 },
236 if self.arr[3] == rhs.arr[3] { u32::MAX } else { 0 },
237 ]}
238 }
239 }
240 }
241
242 #[inline]
243 fn simd_ne(self, rhs: Self) -> Self::Output {
244 pick! {
245 if #[cfg(target_feature="sse2")] {
246 !self.simd_eq(rhs)
247 } else if #[cfg(target_feature="simd128")] {
248 Self { simd: u32x4_ne(self.simd, rhs.simd) }
249 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
250 !self.simd_eq(rhs)
251 } else {
252 Self { arr: [
253 if self.arr[0] != rhs.arr[0] { u32::MAX } else { 0 },
254 if self.arr[1] != rhs.arr[1] { u32::MAX } else { 0 },
255 if self.arr[2] != rhs.arr[2] { u32::MAX } else { 0 },
256 if self.arr[3] != rhs.arr[3] { u32::MAX } else { 0 },
257 ]}
258 }
259 }
260 }
261
262 #[inline]
263 fn simd_lt(self, rhs: Self) -> Self::Output {
264 rhs.simd_gt(self)
266 }
267
268 #[inline]
269 fn simd_gt(self, rhs: Self) -> Self::Output {
270 pick! {
271 if #[cfg(target_feature="sse2")] {
272 let h = u32x4::splat(1 << 31);
274 Self { sse: cmp_gt_mask_i32_m128i((self ^ h).sse, (rhs ^ h).sse) }
275 } else if #[cfg(target_feature="simd128")] {
276 Self { simd: u32x4_gt(self.simd, rhs.simd) }
277 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
278 unsafe {Self { neon: vcgtq_u32(self.neon, rhs.neon) }}
279 } else {
280 Self { arr: [
281 if self.arr[0] > rhs.arr[0] { u32::MAX } else { 0 },
282 if self.arr[1] > rhs.arr[1] { u32::MAX } else { 0 },
283 if self.arr[2] > rhs.arr[2] { u32::MAX } else { 0 },
284 if self.arr[3] > rhs.arr[3] { u32::MAX } else { 0 },
285 ]}
286 }
287 }
288 }
289
290 #[inline]
291 fn simd_le(self, rhs: Self) -> Self::Output {
292 pick! {
293 if #[cfg(target_feature="sse2")] {
294 !self.simd_gt(rhs)
295 } else if #[cfg(target_feature="simd128")] {
296 Self { simd: u32x4_le(self.simd, rhs.simd) }
297 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
298 !self.simd_gt(rhs)
299 } else {
300 Self { arr: [
301 if self.arr[0] <= rhs.arr[0] { u32::MAX } else { 0 },
302 if self.arr[1] <= rhs.arr[1] { u32::MAX } else { 0 },
303 if self.arr[2] <= rhs.arr[2] { u32::MAX } else { 0 },
304 if self.arr[3] <= rhs.arr[3] { u32::MAX } else { 0 },
305 ]}
306 }
307 }
308 }
309
310 #[inline]
311 fn simd_ge(self, rhs: Self) -> Self::Output {
312 pick! {
313 if #[cfg(target_feature="sse2")] {
314 !self.simd_lt(rhs)
315 } else if #[cfg(target_feature="simd128")] {
316 Self { simd: u32x4_ge(self.simd, rhs.simd) }
317 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
318 !self.simd_lt(rhs)
319 } else {
320 Self { arr: [
321 if self.arr[0] >= rhs.arr[0] { u32::MAX } else { 0 },
322 if self.arr[1] >= rhs.arr[1] { u32::MAX } else { 0 },
323 if self.arr[2] >= rhs.arr[2] { u32::MAX } else { 0 },
324 if self.arr[3] >= rhs.arr[3] { u32::MAX } else { 0 },
325 ]}
326 }
327 }
328 }
329
330 #[inline]
331 pub fn reduce_add(self) -> u32 {
332 pick! {
333 if #[cfg(target_feature="sse2")] {
334 let hi64 = unpack_high_i64_m128i(self.sse, self.sse);
335 let sum64 = add_i32_m128i(hi64, self.sse);
336 let hi32 = shuffle_ai_f32_all_m128i::<0b10_11_00_01>(sum64); let sum32 = add_i32_m128i(sum64, hi32);
338 get_i32_from_m128i_s(sum32).cast_unsigned()
339 } else {
340 let arr: [u32; 4] = cast(self);
341 arr[0].wrapping_add(arr[1]).wrapping_add(
342 arr[2].wrapping_add(arr[3]))
343 }
344 }
345 }
346
347 #[inline]
348 pub fn reduce_mul(self) -> u32 {
349 pick! {
350 if #[cfg(target_feature="sse4.1")] {
351 let high_64 = unpack_high_i64_m128i(self.sse, self.sse);
352 let reduce_64 = mul_32_m128i(high_64, self.sse);
353 let high_32 = shuffle_ai_f32_all_m128i::<0b10_11_00_01>(reduce_64);
354 let reduce_32 = mul_32_m128i(reduce_64, high_32);
355 get_i32_from_m128i_s(reduce_32).cast_unsigned()
356 } else if #[cfg(target_feature="simd128")] {
357 let high_64 = u64x2_shuffle::<1, 0>(self.simd, self.simd);
358 let reduce_64 = u32x4_mul(self.simd, high_64);
359 let high_32 = u32x4_shuffle::<1, 0, 0, 0>(reduce_64, reduce_64);
360 let reduce_32 = u32x4_mul(reduce_64, high_32);
361 u32x4_extract_lane::<0>(reduce_32)
362 } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
363 unsafe {
364 let high_64 = vextq_u32::<2>(self.neon, self.neon);
365 let reduce_64 = vmulq_u32(self.neon, high_64);
366 let high_32 = vrev64q_u32(reduce_64);
367 let reduce_32 = vmulq_u32(reduce_64, high_32);
368 vgetq_lane_u32::<0>(reduce_32)
369 }
370 } else {
371 let array = self.to_array();
372 array[0].wrapping_mul(array[1]).wrapping_mul(array[2].wrapping_mul(array[3]))
373 }
374 }
375 }
376
377 #[inline]
378 pub fn bitselect(self, if_one: Self, if_zero: Self) -> Self {
379 pick! {
380 if #[cfg(target_feature="sse2")] {
381 Self {
382 sse: bitor_m128i(
383 bitand_m128i(if_one.sse, self.sse),
384 bitandnot_m128i(self.sse, if_zero.sse),
385 ),
386 }
387 } else if #[cfg(target_feature="simd128")] {
388 Self { simd: v128_bitselect(if_one.simd, if_zero.simd, self.simd) }
389 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
390 unsafe {Self { neon: vbslq_u32(self.neon, if_one.neon, if_zero.neon) }}
391 } else {
392 generic_bit_blend(self, if_one, if_zero)
393 }
394 }
395 }
396
397 #[inline]
398 fn select(self, if_true: Self, if_false: Self) -> Self {
399 pick! {
400 if #[cfg(target_feature="sse4.1")] {
401 Self { sse: blend_varying_i8_m128i(if_false.sse, if_true.sse, self.sse) }
402 } else if #[cfg(target_feature="simd128")] {
403 Self { simd: v128_bitselect(if_true.simd, if_false.simd, self.simd) }
404 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
405 unsafe {Self { neon: vbslq_u32(self.neon, if_true.neon, if_false.neon) }}
406 } else {
407 generic_bit_blend(self, if_true, if_false)
408 }
409 }
410 }
411
412 #[inline]
413 pub fn to_bitmask(self) -> u32 {
414 pick! {
415 if #[cfg(target_feature="sse2")] {
416 move_mask_m128(cast(self.sse)) as u32
418 } else if #[cfg(target_feature="simd128")] {
419 u32x4_bitmask(self.simd) as u32
420 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
421 unsafe
422 {
423 let masked = vcltq_s32(self.cast_signed().neon, vdupq_n_s32(0));
425
426 let selectbit : uint32x4_t = core::mem::transmute([1u32, 2, 4, 8]);
428 let r = vandq_u32(masked, selectbit);
429
430 vaddvq_u32(r) as u32
432 }
433 } else {
434 self.arr[0] >> 31 |
435 self.arr[1] >> 31 << 1 |
436 self.arr[2] >> 31 << 2 |
437 self.arr[3] >> 31 << 3
438 }
439 }
440 }
441
442 #[inline]
443 pub fn any(self) -> bool {
444 pick! {
445 if #[cfg(target_feature="sse2")] {
446 move_mask_m128(cast(self.sse)) != 0
448 } else if #[cfg(target_feature="simd128")] {
449 u32x4_bitmask(self.simd) != 0
450 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
451 unsafe {
453 vminvq_s32(self.cast_signed().neon) < 0
454 }
455 } else {
456 let v : [u64;2] = cast(self);
457 ((v[0] | v[1]) & 0x8000000080000000) != 0
458 }
459 }
460 }
461
462 #[inline]
463 pub fn all(self) -> bool {
464 pick! {
465 if #[cfg(target_feature="sse2")] {
466 move_mask_m128(cast(self.sse)) == 0b1111
468 } else if #[cfg(target_feature="simd128")] {
469 u32x4_bitmask(self.simd) == 0b1111
470 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
471 unsafe {
473 vmaxvq_s32(self.cast_signed().neon) < 0
474 }
475 } else {
476 let v : [u64;2] = cast(self);
477 (v[0] & v[1] & 0x8000000080000000) == 0x8000000080000000
478 }
479 }
480 }
481
482 #[inline]
483 pub fn shuffle(self, indices: u32x4) -> Self {
484 pick! {
485 if #[cfg(target_feature = "avx")] {
486 cast(shuffle_av_f32_all_m128(cast(self), indices.sse))
487 } else if #[cfg(any(
488 target_feature = "ssse3",
489 all(target_arch = "aarch64", target_feature = "neon"),
490 target_feature = "simd128",
491 ))] {
492 let self_bytes = cast::<u32x4, u8x16>(self);
493 let byte_indices = indices.to_byte_indices();
494
495 cast::<u8x16, u32x4>(self_bytes.shuffle(byte_indices))
496 } else {
497 let self_array = self.to_array();
498 let indices_array = indices.to_array();
499
500 let mut result = [0; 4];
501 for i in 0..4 {
502 let index = indices_array[i] as usize;
503 if index < 4 {
504 result[i] = self_array[index];
505 }
506 }
507
508 Self::new(result)
509 }
510 }
511 }
512
513 #[inline]
514 pub fn shuffle_zeroing(self, indices: u32x4) -> Self {
515 pick! {
516 if #[cfg(any(
517 target_feature = "avx",
518 target_feature = "ssse3",
519 all(target_arch = "aarch64", target_feature = "neon"),
520 target_feature = "simd128",
521 ))] {
522 self.shuffle(indices) & indices.simd_lt(4)
525 } else {
526 self.shuffle(indices)
528 }
529 }
530 }
531
532 #[inline]
533 pub fn shuffle_wrapping(self, indices: u32x4) -> Self {
534 self.shuffle(indices & 3)
535 }
536
537 #[inline]
538 fn shuffle(self: [u32x4; 2], indices: u32x4) -> u32x4 {
539 pick! {
540 if #[cfg(all(target_feature = "avx512f", target_feature = "avx512vl"))] {
541 u32x4 { sse: shuffle_abv_i32_all_m128i(self[0].sse, indices.sse, self[1].sse) }
542 } else {
543 let self_bytes = cast::<[u32x4; 2], [u8x16; 2]>(self);
544 let byte_indices = indices.to_byte_indices();
545
546 cast::<u8x16, u32x4>(self_bytes.shuffle(byte_indices))
547 }
548 }
549 }
550
551 #[inline]
552 fn shuffle_zeroing(self: [u32x4; 2], indices: u32x4) -> u32x4 {
553 self.shuffle(indices) & indices.simd_lt(8)
556 }
557
558 #[inline]
559 fn shuffle_wrapping(self: [u32x4; 2], indices: u32x4) -> u32x4 {
560 pick! {
561 if #[cfg(all(target_feature = "avx512f", target_feature = "avx512vl"))] {
562 self.shuffle(indices)
564 } else {
565 self.shuffle(indices & 7)
566 }
567 }
568 }
569
570 #[inline]
571 fn shuffle(self: [u32x4; 3], indices: u32x4) -> u32x4 {
572 let self_bytes = cast::<[u32x4; 3], [u8x16; 3]>(self);
573 let byte_indices = indices.to_byte_indices();
574
575 cast::<u8x16, u32x4>(self_bytes.shuffle(byte_indices))
576 }
577
578 #[inline]
579 fn shuffle_zeroing(self: [u32x4; 3], indices: u32x4) -> u32x4 {
580 self.shuffle(indices) & indices.simd_lt(12)
583 }
584
585 #[inline]
586 fn shuffle_wrapping(self: [u32x4; 3], indices: u32x4) -> u32x4 {
587 self.shuffle(indices % 12)
588 }
589
590 #[inline]
591 fn shuffle(self: [u32x4; 4], indices: u32x4) -> u32x4 {
592 let self_bytes = cast::<[u32x4; 4], [u8x16; 4]>(self);
593 let byte_indices = indices.to_byte_indices();
594
595 cast::<u8x16, u32x4>(self_bytes.shuffle(byte_indices))
596 }
597
598 #[inline]
599 fn shuffle_zeroing(self: [u32x4; 4], indices: u32x4) -> u32x4 {
600 self.shuffle(indices) & indices.simd_lt(16)
603 }
604
605 #[inline]
606 fn shuffle_wrapping(self: [u32x4; 4], indices: u32x4) -> u32x4 {
607 self.shuffle(indices & 15)
608 }
609
610 #[inline]
613 pub fn transpose(data: [Self; 4]) -> [Self; 4] {
614 pick! {
615 if #[cfg(target_feature="sse")] {
616 let mut e0 = data[0];
617 let mut e1 = data[1];
618 let mut e2 = data[2];
619 let mut e3 = data[3];
620
621 transpose_four_m128(
622 cast_mut(&mut e0.sse),
623 cast_mut(&mut e1.sse),
624 cast_mut(&mut e2.sse),
625 cast_mut(&mut e3.sse),
626 );
627
628 [e0, e1, e2, e3]
629 } else {
630 #[inline(always)]
631 fn transpose_column(data: &[u32x4; 4], index: usize) -> u32x4 {
632 u32x4::new([
633 data[0].as_array()[index],
634 data[1].as_array()[index],
635 data[2].as_array()[index],
636 data[3].as_array()[index],
637 ])
638 }
639
640 [
641 transpose_column(&data, 0),
642 transpose_column(&data, 1),
643 transpose_column(&data, 2),
644 transpose_column(&data, 3),
645 ]
646 }
647 }
648 }
649
650 #[inline]
651 fn shl(self, rhs: u32x4) -> Self::Output {
652 pick! {
653 if #[cfg(target_feature="avx2")] {
654 let shift_by = bitand_m128i(rhs.sse, set_splat_i32_m128i(31));
656 Self { sse: shl_each_u32_m128i(self.sse, shift_by) }
657 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
658 unsafe {
659 let shift_by = vreinterpretq_s32_u32(vandq_u32(rhs.neon, vmovq_n_u32(31)));
661 Self { neon: vshlq_u32(self.neon, shift_by) }
662 }
663 } else {
664 let arr: [u32; 4] = cast(self);
665 let rhs: [u32; 4] = cast(rhs);
666 cast([
667 arr[0].wrapping_shl(rhs[0]),
668 arr[1].wrapping_shl(rhs[1]),
669 arr[2].wrapping_shl(rhs[2]),
670 arr[3].wrapping_shl(rhs[3]),
671 ])
672 }
673 }
674 }
675
676 #[inline]
677 fn shl(self, rhs: u32) -> Self::Output {
678 pick! {
679 if #[cfg(target_feature="sse2")] {
680 #[expect(clippy::suspicious_arithmetic_impl)]
682 let shift = cast([rhs as u64 & 31, 0]);
683 Self { sse: shl_all_u32_m128i(self.sse, shift) }
684 } else if #[cfg(target_feature="simd128")] {
685 Self { simd: u32x4_shl(self.simd, rhs) }
686 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
687 #[expect(clippy::suspicious_arithmetic_impl)]
689 unsafe {Self { neon: vshlq_u32(self.neon, vmovq_n_s32(rhs as i32 & 31)) }}
690 } else {
691 Self { arr: [
692 self.arr[0].wrapping_shl(rhs),
693 self.arr[1].wrapping_shl(rhs),
694 self.arr[2].wrapping_shl(rhs),
695 self.arr[3].wrapping_shl(rhs),
696 ]}
697 }
698 }
699 }
700
701 #[inline]
702 fn shr(self, rhs: u32x4) -> Self::Output {
703 pick! {
704 if #[cfg(target_feature="avx2")] {
705 let shift_by = bitand_m128i(rhs.sse, set_splat_i32_m128i(31));
707 Self { sse: shr_each_u32_m128i(self.sse, shift_by) }
708 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
709 unsafe {
710 let shift_by = vnegq_s32(vreinterpretq_s32_u32(vandq_u32(rhs.neon, vmovq_n_u32(31))));
713 Self { neon: vshlq_u32(self.neon, shift_by) }
714 }
715 } else {
716 let arr: [u32; 4] = cast(self);
717 let rhs: [u32; 4] = cast(rhs);
718 cast([
719 arr[0].wrapping_shr(rhs[0]),
720 arr[1].wrapping_shr(rhs[1]),
721 arr[2].wrapping_shr(rhs[2]),
722 arr[3].wrapping_shr(rhs[3]),
723 ])
724 }
725 }
726 }
727
728 #[inline]
729 fn shr(self, rhs: u32) -> Self::Output {
730 pick! {
731 if #[cfg(target_feature="sse2")] {
732 #[expect(clippy::suspicious_arithmetic_impl)]
734 let shift = cast([rhs as u64 & 31, 0]);
735 Self { sse: shr_all_u32_m128i(self.sse, shift) }
736 } else if #[cfg(target_feature="simd128")] {
737 Self { simd: u32x4_shr(self.simd, rhs) }
738 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
739 #[expect(clippy::suspicious_arithmetic_impl)]
741 unsafe {Self { neon: vshlq_u32(self.neon, vmovq_n_s32( -(rhs as i32 & 31))) }}
742 } else {
743 Self { arr: [
744 self.arr[0].wrapping_shr(rhs),
745 self.arr[1].wrapping_shr(rhs),
746 self.arr[2].wrapping_shr(rhs),
747 self.arr[3].wrapping_shr(rhs),
748 ]}
749 }
750 }
751 }
752
753 #[inline]
754 pub fn max(self, rhs: Self) -> Self {
755 pick! {
756 if #[cfg(target_feature="sse4.1")] {
757 Self { sse: max_u32_m128i(self.sse, rhs.sse) }
758 } else if #[cfg(target_feature="simd128")] {
759 Self { simd: u32x4_max(self.simd, rhs.simd) }
760 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
761 unsafe {Self { neon: vmaxq_u32(self.neon, rhs.neon) }}
762 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
763 unsafe {Self { neon: vmaxq_u16(self.neon, rhs.neon) }}
764 } else {
765 let arr: [u32; 4] = cast(self);
766 let rhs: [u32; 4] = cast(rhs);
767 cast([
768 arr[0].max(rhs[0]),
769 arr[1].max(rhs[1]),
770 arr[2].max(rhs[2]),
771 arr[3].max(rhs[3]),
772 ])
773 }
774 }
775 }
776
777 #[inline]
778 pub fn min(self, rhs: Self) -> Self {
779 pick! {
780 if #[cfg(target_feature="sse4.1")] {
781 Self { sse: min_u32_m128i(self.sse, rhs.sse) }
782 } else if #[cfg(target_feature="simd128")] {
783 Self { simd: u32x4_min(self.simd, rhs.simd) }
784 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
785 unsafe {Self { neon: vminq_u32(self.neon, rhs.neon) }}
786 } else {
787 let arr: [u32; 4] = cast(self);
788 let rhs: [u32; 4] = cast(rhs);
789 cast([
790 arr[0].min(rhs[0]),
791 arr[1].min(rhs[1]),
792 arr[2].min(rhs[2]),
793 arr[3].min(rhs[3]),
794 ])
795 }
796 }
797 }
798
799 #[inline]
800 pub fn reduce_max(self) -> u32 {
801 let arr: [u32; 4] = cast(self);
802 arr[0].max(arr[1]).max(arr[2].max(arr[3]))
803 }
804
805 #[inline]
806 pub fn reduce_min(self) -> u32 {
807 let arr: [u32; 4] = cast(self);
808 arr[0].min(arr[1]).min(arr[2].min(arr[3]))
809 }
810
811 #[inline]
812 pub fn unbounded_shl(self, rhs: Self) -> Self {
813 pick! {
814 if #[cfg(target_feature="avx2")] {
815 Self { sse: shl_each_u32_m128i(self.sse, rhs.sse) }
816 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
817 unsafe {
818 Self { neon: vshlq_u32(self.neon, vreinterpretq_s32_u32(rhs.neon)) } & rhs.simd_lt(32)
820 }
821 } else {
822 let self_array = self.to_array();
823 let rhs_array = rhs.to_array();
824
825 cast([
826 self_array[0].unbounded_shl(rhs_array[0]),
827 self_array[1].unbounded_shl(rhs_array[1]),
828 self_array[2].unbounded_shl(rhs_array[2]),
829 self_array[3].unbounded_shl(rhs_array[3]),
830 ])
831 }
832 }
833 }
834
835 #[inline]
836 pub fn unbounded_shl_scalar(self, rhs: u32) -> Self {
837 pick! {
838 if #[cfg(target_feature="sse2")] {
839 Self { sse: shl_all_u32_m128i(self.sse, cast([rhs as u64, 0])) }
840 } else if #[cfg(target_feature="simd128")] {
841 Self { simd: u32x4_shl(self.simd, rhs) } & Self::splat(rhs).simd_lt(32)
843 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
844 unsafe { Self { neon: vshlq_u32(self.neon, vmovq_n_s32(rhs.min(32) as i32)) } }
846 } else {
847 Self { arr: [
848 self.arr[0].unbounded_shl(rhs),
849 self.arr[1].unbounded_shl(rhs),
850 self.arr[2].unbounded_shl(rhs),
851 self.arr[3].unbounded_shl(rhs),
852 ]}
853 }
854 }
855 }
856
857 #[inline]
858 pub fn unbounded_shr(self, rhs: Self) -> Self {
859 pick! {
860 if #[cfg(target_feature="avx2")] {
861 Self { sse: shr_each_u32_m128i(self.sse, rhs.sse) }
862 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
863 unsafe {
864 Self { neon: vshlq_u32(self.neon, vnegq_s32(vreinterpretq_s32_u32(rhs.neon))) } & rhs.simd_lt(32)
867 }
868 } else {
869 let self_array = self.to_array();
870 let rhs_array = rhs.to_array();
871
872 Self::new([
873 self_array[0].unbounded_shr(rhs_array[0]),
874 self_array[1].unbounded_shr(rhs_array[1]),
875 self_array[2].unbounded_shr(rhs_array[2]),
876 self_array[3].unbounded_shr(rhs_array[3]),
877 ])
878 }
879 }
880 }
881
882 #[inline]
883 pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
884 pick! {
885 if #[cfg(target_feature="sse2")] {
886 Self { sse: shr_all_u32_m128i(self.sse, cast([rhs as u64, 0])) }
887 } else if #[cfg(target_feature="simd128")] {
888 if rhs < 32 { Self { simd: u32x4_shr(self.simd, rhs) } } else { Self::ZERO }
889 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
890 unsafe {
891 Self { neon: vshlq_u32(self.neon, vmovq_n_s32(-rhs.min(32).cast_signed())) }
894 }
895 } else {
896 Self {
897 arr: [
898 self.arr[0].unbounded_shr(rhs),
899 self.arr[1].unbounded_shr(rhs),
900 self.arr[2].unbounded_shr(rhs),
901 self.arr[3].unbounded_shr(rhs),
902 ],
903 }
904 }
905 }
906 }
907
908 #[inline]
909 pub fn saturating_add(self, rhs: Self) -> Self {
910 pick! {
911 if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
912 let result = self + rhs;
913 let overflow = result.simd_lt(self);
914 result | overflow
916 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
917 unsafe { Self { neon: vqaddq_u32(self.neon, rhs.neon) } }
918 } else {
919 Self {
920 arr: [
921 self.arr[0].saturating_add(rhs.arr[0]),
922 self.arr[1].saturating_add(rhs.arr[1]),
923 self.arr[2].saturating_add(rhs.arr[2]),
924 self.arr[3].saturating_add(rhs.arr[3]),
925 ],
926 }
927 }
928 }
929 }
930
931 #[inline]
932 pub fn saturating_sub(self, rhs: Self) -> Self {
933 pick! {
934 if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
935 let result = self - rhs;
936 let no_overflow = result.simd_le(self);
937 result & no_overflow
939 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
940 unsafe { Self { neon: vqsubq_u32(self.neon, rhs.neon) } }
941 } else {
942 Self {
943 arr: [
944 self.arr[0].saturating_sub(rhs.arr[0]),
945 self.arr[1].saturating_sub(rhs.arr[1]),
946 self.arr[2].saturating_sub(rhs.arr[2]),
947 self.arr[3].saturating_sub(rhs.arr[3]),
948 ],
949 }
950 }
951 }
952 }
953
954 #[inline]
955 pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
956 let (low, high) = self.mul_keep_low_high(rhs);
957 let overflow = high.simd_ne(Self::ZERO);
958 (low, overflow)
959 }
960
961 optional_fn_widening_mul {
962 #[inline]
963 pub fn widening_mul(self, rhs: Self) -> u64x4 {
964 pick! {
965 if #[cfg(target_feature="avx2")] {
966 let a = convert_to_i64_m256i_from_i32_m128i(self.sse);
968 let b = convert_to_i64_m256i_from_i32_m128i(rhs.sse);
969 cast(mul_u64_low_bits_m256i(a, b))
970 } else if #[cfg(target_feature="sse2")] {
971 let evenp = mul_widen_u32_odd_m128i(self.sse, rhs.sse);
972
973 let oddp = mul_widen_u32_odd_m128i(
974 shr_imm_u64_m128i::<32>(self.sse),
975 shr_imm_u64_m128i::<32>(rhs.sse));
976
977 u64x4 {
978 a: u64x2 { sse: unpack_low_i64_m128i(evenp, oddp)},
979 b: u64x2 { sse: unpack_high_i64_m128i(evenp, oddp)}
980 }
981 } else if #[cfg(target_feature="simd128")] {
982 u64x4 {
983 a: u64x2 { simd: u64x2_extmul_low_u32x4(self.simd, rhs.simd) },
984 b: u64x2 { simd: u64x2_extmul_high_u32x4(self.simd, rhs.simd) },
985 }
986 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
987 unsafe {
988 u64x4 { a: u64x2 { neon: vmull_u32(vget_low_u32(self.neon), vget_low_u32(rhs.neon)) },
989 b: u64x2 { neon: vmull_u32(vget_high_u32(self.neon), vget_high_u32(rhs.neon)) } }
990 }
991 } else {
992 let a: [u32; 4] = cast(self);
993 let b: [u32; 4] = cast(rhs);
994 cast([
995 u64::from(a[0]) * u64::from(b[0]),
996 u64::from(a[1]) * u64::from(b[1]),
997 u64::from(a[2]) * u64::from(b[2]),
998 u64::from(a[3]) * u64::from(b[3]),
999 ])
1000 }
1001 }
1002 }
1003 }
1004
1005 #[inline]
1006 pub fn mul_keep_low_high(self, rhs: Self) -> (Self, Self) {
1007 pick! {
1008 if #[cfg(target_feature="sse4.1")] {
1009 let even_wide_mul = mul_widen_u32_odd_m128i(self.sse, rhs.sse);
1010 let odd_wide_mul = mul_widen_u32_odd_m128i(
1011 shuffle_ai_f32_all_m128i::<0b_00_11_00_01>(self.sse),
1012 shuffle_ai_f32_all_m128i::<0b_00_11_00_01>(rhs.sse),
1013 );
1014
1015 let ll_hh_1 = unpack_low_i32_m128i(even_wide_mul, odd_wide_mul);
1016 let ll_hh_2 = unpack_high_i32_m128i(even_wide_mul, odd_wide_mul);
1017 (
1018 Self { sse: unpack_low_i64_m128i(ll_hh_1, ll_hh_2) },
1019 Self { sse: unpack_high_i64_m128i(ll_hh_1, ll_hh_2) },
1020 )
1021 } else if #[cfg(target_feature="simd128")] {
1022 let low_wide_mul = u64x2_extmul_low_u32x4(self.simd, rhs.simd);
1023 let high_wide_mul = u64x2_extmul_high_u32x4(self.simd, rhs.simd);
1024 (
1025 Self { simd: u32x4_shuffle::<0, 2, 4, 6>(low_wide_mul, high_wide_mul) },
1026 Self { simd: u32x4_shuffle::<1, 3, 5, 7>(low_wide_mul, high_wide_mul) },
1027 )
1028 } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
1029 unsafe {
1030 let low_wide_mul = vreinterpretq_u32_u64(
1031 vmull_u32(vget_low_u32(self.neon), vget_low_u32(rhs.neon)),
1032 );
1033 let high_wide_mul = vreinterpretq_u32_u64(
1034 vmull_u32(vget_high_u32(self.neon), vget_high_u32(rhs.neon)),
1035 );
1036 let low_high = vuzpq_u32(low_wide_mul, high_wide_mul);
1037 (
1038 Self { neon: low_high.0 },
1039 Self { neon: low_high.1 },
1040 )
1041 }
1042 } else {
1043 let self_array = self.to_array();
1047 let rhs_array = rhs.to_array();
1048
1049 let widening_mul = [
1050 (self_array[0] as u64).wrapping_mul(rhs_array[0] as u64),
1051 (self_array[1] as u64).wrapping_mul(rhs_array[1] as u64),
1052 (self_array[2] as u64).wrapping_mul(rhs_array[2] as u64),
1053 (self_array[3] as u64).wrapping_mul(rhs_array[3] as u64),
1054 ];
1055
1056 (
1057 Self::new([
1058 widening_mul[0] as u32,
1059 widening_mul[1] as u32,
1060 widening_mul[2] as u32,
1061 widening_mul[3] as u32,
1062 ]),
1063 Self::new([
1064 (widening_mul[0] >> 32) as u32,
1065 (widening_mul[1] >> 32) as u32,
1066 (widening_mul[2] >> 32) as u32,
1067 (widening_mul[3] >> 32) as u32,
1068 ]),
1069 )
1070 }
1071 }
1072 }
1073
1074 #[inline]
1075 pub fn mul_keep_high(self, rhs: Self) -> Self {
1076 pick! {
1077 if #[cfg(target_feature="avx2")] {
1078 let a = convert_to_i64_m256i_from_u32_m128i(self.sse);
1079 let b = convert_to_i64_m256i_from_u32_m128i(rhs.sse);
1080 let r = mul_u64_low_bits_m256i(a, b);
1081
1082 let b : [u32;8] = cast(r);
1084 cast([b[1],b[3],b[5],b[7]])
1085 } else if #[cfg(target_feature="sse2")] {
1086 let evenp = mul_widen_u32_odd_m128i(self.sse, rhs.sse);
1087
1088 let oddp = mul_widen_u32_odd_m128i(
1089 shr_imm_u64_m128i::<32>(self.sse),
1090 shr_imm_u64_m128i::<32>(rhs.sse));
1091
1092 let a : [u32;4]= cast(evenp);
1094 let b : [u32;4]= cast(oddp);
1095 cast([a[1],b[1],a[3],b[3]])
1096
1097 } else if #[cfg(target_feature="simd128")] {
1098 let low = u64x2_extmul_low_u32x4(self.simd, rhs.simd);
1099 let high = u64x2_extmul_high_u32x4(self.simd, rhs.simd);
1100
1101 Self { simd: u32x4_shuffle::<1, 3, 5, 7>(low, high) }
1102 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
1103 unsafe {
1104 let l = vmull_u32(vget_low_u32(self.neon), vget_low_u32(rhs.neon));
1105 let h = vmull_u32(vget_high_u32(self.neon), vget_high_u32(rhs.neon));
1106 u32x4 { neon: vcombine_u32(vshrn_n_u64(l,32), vshrn_n_u64(h,32)) }
1107 }
1108 } else {
1109 let a: [u32; 4] = cast(self);
1110 let b: [u32; 4] = cast(rhs);
1111 cast([
1112 ((u64::from(a[0]) * u64::from(b[0])) >> 32) as u32,
1113 ((u64::from(a[1]) * u64::from(b[1])) >> 32) as u32,
1114 ((u64::from(a[2]) * u64::from(b[2])) >> 32) as u32,
1115 ((u64::from(a[3]) * u64::from(b[3])) >> 32) as u32,
1116 ])
1117 }
1118 }
1119 }
1120
1121 optional_fn_deserialize {}
1122}
1123
1124impl u32x4 {
1127 #[inline]
1136 #[must_use]
1137 #[deprecated(since = "1.6.0", note = "renamed to `widening_mul`")]
1138 pub fn mul_widen(self, rhs: Self) -> u64x4 {
1139 self.widening_mul(rhs)
1140 }
1141
1142 #[inline]
1145 #[must_use]
1146 pub fn unpack_lo(self, b: Self) -> Self {
1147 pick! {
1148 if #[cfg(target_feature="sse2")] {
1149 Self { sse: unpack_low_i32_m128i(self.sse, b.sse) }
1150 } else if #[cfg(target_feature="simd128")] {
1151 Self { simd: u32x4_shuffle::<0, 4, 1, 5>(self.simd, b.simd) }
1152 } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
1153 Self { neon: unsafe { vzip1q_u32(self.neon, b.neon) } }
1154 } else {
1155 let s = self.as_array();
1156 let b = b.as_array();
1157 Self::new([s[0], b[0], s[1], b[1]])
1158 }
1159 }
1160 }
1161
1162 #[inline]
1165 #[must_use]
1166 pub fn unpack_hi(self, b: Self) -> Self {
1167 pick! {
1168 if #[cfg(target_feature="sse2")] {
1169 Self { sse: unpack_high_i32_m128i(self.sse, b.sse) }
1170 } else if #[cfg(target_feature="simd128")] {
1171 Self { simd: u32x4_shuffle::<2, 6, 3, 7>(self.simd, b.simd) }
1172 } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
1173 Self { neon: unsafe { vzip2q_u32(self.neon, b.neon) } }
1174 } else {
1175 let s = self.as_array();
1176 let b = b.as_array();
1177 Self::new([s[2], b[2], s[3], b[3]])
1178 }
1179 }
1180 }
1181
1182 #[inline]
1189 #[must_use]
1190 pub fn add_mul_lo<const W: u32>(self, a: Self, b: Self) -> Self {
1191 if W <= 16 {
1192 let mask = Self::splat(add_mul_operand_mask_u32::<W>());
1193 return self + (((a & mask) * (b & mask)) & mask);
1194 }
1195
1196 let acc = self.to_array();
1197 let a = a.to_array();
1198 let b = b.to_array();
1199 Self::new(core::array::from_fn(|i| {
1200 add_mul_lo_lane_u32::<W>(acc[i], a[i], b[i])
1201 }))
1202 }
1203
1204 #[inline]
1207 #[must_use]
1208 pub fn add_mul_hi<const W: u32>(self, a: Self, b: Self) -> Self {
1209 if W <= 16 {
1212 let mask = Self::splat(add_mul_operand_mask_u32::<W>());
1213 return self + (((a & mask) * (b & mask)) >> W);
1214 }
1215
1216 let acc = self.to_array();
1217 let a = a.to_array();
1218 let b = b.to_array();
1219 Self::new(core::array::from_fn(|i| {
1220 add_mul_hi_lane_u32::<W>(acc[i], a[i], b[i])
1221 }))
1222 }
1223
1224 #[allow(dead_code)]
1233 #[inline]
1234 fn to_byte_indices(self) -> u8x16 {
1235 let base = self.unbounded_shl_scalar(2);
1237 let base = base | base.unbounded_shl_scalar(8);
1238 let base = base | base.unbounded_shl_scalar(16);
1239
1240 const WITHIN_LANE: u32x4 = u32x4::splat(u32::from_ne_bytes([0, 1, 2, 3]));
1244
1245 cast::<u32x4, u8x16>(base | WITHIN_LANE)
1246 }
1247}