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 u16x16 { pub(crate) avx2: m256i }
14 } else {
15 #[derive(Default, Clone, Copy, PartialEq, Eq)]
22 #[repr(C, align(32))]
23 pub struct u16x16 { pub(crate) a : u16x8, pub(crate) b : u16x8 }
24 }
25}
26
27impl_simd! {
28 unsafe {
29 T = u16,
30 N = 16,
31 Simd = u16x16,
32 optional_type_x86_inner { X86Inner = __m256i },
33 optional_type_arm_inner {},
34 optional_type_wasm_inner {},
35 }
36
37 #[inline]
38 fn simd_eq(self, rhs: Self) -> Self::Output {
39 pick! {
40 if #[cfg(target_feature="avx2")] {
41 Self { avx2: cmp_eq_mask_i16_m256i(self.avx2, rhs.avx2) }
42 } else {
43 Self {
44 a : self.a.simd_eq(rhs.a),
45 b : self.b.simd_eq(rhs.b),
46 }
47 }
48 }
49 }
50
51 #[inline]
52 fn simd_ne(self, rhs: Self) -> Self::Output {
53 pick! {
54 if #[cfg(target_feature="avx2")] {
55 !self.simd_eq(rhs)
56 } else {
57 Self {
58 a : self.a.simd_ne(rhs.a),
59 b : self.b.simd_ne(rhs.b),
60 }
61 }
62 }
63 }
64
65 #[inline]
66 fn simd_lt(self, rhs: Self) -> Self::Output {
67 Self::simd_gt(rhs, self)
69 }
70
71 #[inline]
72 fn simd_gt(self, rhs: Self) -> Self::Output {
73 pick! {
74 if #[cfg(target_feature = "avx2")] {
75 let bias = m256i::from([0x8000u16; 16]);
76 let a_biased = sub_i16_m256i(self.avx2, bias);
77 let b_biased = sub_i16_m256i(rhs.avx2, bias);
78 let mask = cmp_gt_mask_i16_m256i(a_biased, b_biased);
79
80 Self { avx2: mask }
81 } else {
82 Self {
83 a: self.a.simd_gt(rhs.a),
84 b: self.b.simd_gt(rhs.b),
85 }
86 }
87 }
88 }
89
90 #[inline]
91 fn simd_le(self, rhs: Self) -> Self::Output {
92 pick! {
93 if #[cfg(target_feature="avx2")] {
94 !self.simd_gt(rhs)
95 } else {
96 Self {
97 a : self.a.simd_le(rhs.a),
98 b : self.b.simd_le(rhs.b),
99 }
100 }
101 }
102 }
103
104 #[inline]
105 fn simd_ge(self, rhs: Self) -> Self::Output {
106 pick! {
107 if #[cfg(target_feature="avx2")] {
108 !self.simd_lt(rhs)
109 } else {
110 Self {
111 a : self.a.simd_ge(rhs.a),
112 b : self.b.simd_ge(rhs.b),
113 }
114 }
115 }
116 }
117
118 #[inline]
119 pub fn bitselect(self, if_one: Self, if_zero: Self) -> Self {
120 pick! {
121 if #[cfg(target_feature="avx2")] {
122 Self {
123 avx2: bitor_m256i(
124 bitand_m256i(if_one.avx2, self.avx2),
125 bitandnot_m256i(self.avx2, if_zero.avx2),
126 ),
127 }
128 } else {
129 Self {
130 a: self.a.bitselect(if_one.a, if_zero.a),
131 b: self.b.bitselect(if_one.b, if_zero.b),
132 }
133 }
134 }
135 }
136
137 #[inline]
138 pub fn select(self, if_true: Self, if_false: Self) -> Self {
139 pick! {
140 if #[cfg(target_feature="avx2")] {
141 Self { avx2: blend_varying_i8_m256i(if_false.avx2, if_true.avx2, self.avx2) }
142 } else {
143 Self {
144 a : self.a.select(if_true.a, if_false.a),
145 b : self.b.select(if_true.b, if_false.b),
146 }
147 }
148 }
149 }
150
151 #[inline]
152 pub fn to_bitmask(self) -> u32 {
153 i16x16::to_bitmask(cast(self))
154 }
155
156 #[inline]
157 pub fn any(self) -> bool {
158 i16x16::any(cast(self))
159 }
160
161 #[inline]
162 pub fn all(self) -> bool {
163 i16x16::all(cast(self))
164 }
165
166 #[inline]
169 pub fn transpose(data: [u16x16; 16]) -> [u16x16; 16] {
170 cast(i16x16::transpose(cast(data)))
171 }
172}
173
174impl_simd_uint! {
175 unsafe {
176 T = u16,
177 N = 16,
178 Simd = u16x16,
179 SignedSimd = i16x16,
180 T_BITS = 16,
181 T_BITS_MUL_2 = 32,
182 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
183 }
184
185 #[inline]
186 fn not(self) -> Self {
187 pick! {
188 if #[cfg(target_feature="avx2")] {
189 Self { avx2: self.avx2.not() }
190 } else {
191 Self {
192 a : self.a.not(),
193 b : self.b.not(),
194 }
195 }
196 }
197 }
198
199 #[inline]
200 fn add(self, rhs: Self) -> Self::Output {
201 pick! {
202 if #[cfg(target_feature="avx2")] {
203 Self { avx2: add_i16_m256i(self.avx2, rhs.avx2) }
204 } else {
205 Self {
206 a : self.a.add(rhs.a),
207 b : self.b.add(rhs.b),
208 }
209 }
210 }
211 }
212
213 #[inline]
214 fn sub(self, rhs: Self) -> Self::Output {
215 pick! {
216 if #[cfg(target_feature="avx2")] {
217 Self { avx2: sub_i16_m256i(self.avx2, rhs.avx2) }
218 } else {
219 Self {
220 a : self.a.sub(rhs.a),
221 b : self.b.sub(rhs.b),
222 }
223 }
224 }
225 }
226
227 #[inline]
228 fn mul(self, rhs: Self) -> Self::Output {
229 pick! {
230 if #[cfg(target_feature="avx2")] {
231 Self { avx2: mul_i16_keep_low_m256i(self.avx2, rhs.avx2) }
233 } else {
234 Self {
235 a : self.a.mul(rhs.a),
236 b : self.b.mul(rhs.b),
237 }
238 }
239 }
240 }
241
242 #[inline]
243 fn shl(self, rhs: Self) -> Self::Output {
244 pick! {
245 if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
246 #[cfg(target_arch = "x86")]
247 use core::arch::x86::_mm256_sllv_epi16;
248 #[cfg(target_arch = "x86_64")]
249 use core::arch::x86_64::_mm256_sllv_epi16;
250
251 let rhs = bitand_m256i(rhs.avx2, set_splat_i16_m256i(15));
253 cast(unsafe { _mm256_sllv_epi16(self.avx2.0, rhs.0) })
255 } else {
256 let [self_a, self_b]: [u16x8; 2] = cast(self);
257 let [rhs_a, rhs_b]: [u16x8; 2] = cast(rhs);
258
259 cast([self_a << rhs_a, self_b << rhs_b])
260 }
261 }
262 }
263
264 #[inline]
265 fn shl(self, rhs: u32) -> Self::Output {
266 pick! {
267 if #[cfg(target_feature="avx2")] {
268 #[expect(clippy::suspicious_arithmetic_impl)]
270 let shift = cast([rhs as u64 & 15, 0]);
271 Self { avx2: shl_all_u16_m256i(self.avx2, shift) }
272 } else {
273 Self {
274 a : self.a.shl(rhs),
275 b : self.b.shl(rhs),
276 }
277 }
278 }
279 }
280
281 #[inline]
282 fn shr(self, rhs: Self) -> Self::Output {
283 pick! {
284 if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
285 #[cfg(target_arch = "x86")]
286 use core::arch::x86::_mm256_srlv_epi16;
287 #[cfg(target_arch = "x86_64")]
288 use core::arch::x86_64::_mm256_srlv_epi16;
289
290 let rhs = bitand_m256i(rhs.avx2, set_splat_i16_m256i(15));
292 cast(unsafe { _mm256_srlv_epi16(self.avx2.0, rhs.0) })
294 } else {
295 let [self_a, self_b]: [u16x8; 2] = cast(self);
296 let [rhs_a, rhs_b]: [u16x8; 2] = cast(rhs);
297
298 cast([self_a >> rhs_a, self_b >> rhs_b])
299 }
300 }
301 }
302
303 #[inline]
304 fn shr(self, rhs: u32) -> Self::Output {
305 pick! {
306 if #[cfg(target_feature="avx2")] {
307 #[expect(clippy::suspicious_arithmetic_impl)]
309 let shift = cast([rhs as u64 & 15, 0]);
310 Self { avx2: shr_all_u16_m256i(self.avx2, shift) }
311 } else {
312 Self {
313 a : self.a.shr(rhs),
314 b : self.b.shr(rhs),
315 }
316 }
317 }
318 }
319
320 #[inline]
321 fn bitand(self, rhs: Self) -> Self::Output {
322 pick! {
323 if #[cfg(target_feature="avx2")] {
324 Self { avx2: bitand_m256i(self.avx2, rhs.avx2) }
325 } else {
326 Self {
327 a : self.a.bitand(rhs.a),
328 b : self.b.bitand(rhs.b),
329 }
330 }
331 }
332 }
333
334 #[inline]
335 fn bitor(self, rhs: Self) -> Self::Output {
336 pick! {
337 if #[cfg(target_feature="avx2")] {
338 Self { avx2: bitor_m256i(self.avx2, rhs.avx2) }
339 } else {
340 Self {
341 a : self.a.bitor(rhs.a),
342 b : self.b.bitor(rhs.b),
343 }
344 }
345 }
346 }
347
348 #[inline]
349 fn bitxor(self, rhs: Self) -> Self::Output {
350 pick! {
351 if #[cfg(target_feature="avx2")] {
352 Self { avx2: bitxor_m256i(self.avx2, rhs.avx2) }
353 } else {
354 Self {
355 a : self.a.bitxor(rhs.a),
356 b : self.b.bitxor(rhs.b),
357 }
358 }
359 }
360 }
361
362 #[inline]
363 pub fn max(self, rhs: Self) -> Self {
364 pick! {
365 if #[cfg(target_feature="avx2")] {
366 Self { avx2: max_u16_m256i(self.avx2, rhs.avx2) }
367 } else {
368 Self {
369 a : self.a.max(rhs.a),
370 b : self.b.max(rhs.b),
371 }
372 }
373 }
374 }
375
376 #[inline]
377 pub fn min(self, rhs: Self) -> Self {
378 pick! {
379 if #[cfg(target_feature="avx2")] {
380 Self { avx2: min_u16_m256i(self.avx2, rhs.avx2) }
381 } else {
382 Self {
383 a : self.a.min(rhs.a),
384 b : self.b.min(rhs.b),
385 }
386 }
387 }
388 }
389
390 #[inline]
391 pub fn reduce_add(self) -> u16 {
392 let array: [u16x8; 2] = cast(self);
393 (array[0] + array[1]).reduce_add()
394 }
395
396 #[inline]
397 pub fn reduce_mul(self) -> u16 {
398 let array: [u16x8; 2] = cast(self);
399 (array[0] * array[1]).reduce_mul()
400 }
401
402 #[inline]
403 pub fn reduce_max(self) -> u16 {
404 let array: [u16x8; 2] = cast(self);
405 array[0].max(array[1]).reduce_max()
406 }
407
408 #[inline]
409 pub fn reduce_min(self) -> u16 {
410 let array: [u16x8; 2] = cast(self);
411 array[0].min(array[1]).reduce_min()
412 }
413
414 #[inline]
415 pub fn unbounded_shl(self, rhs: Self) -> Self {
416 pick! {
417 if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
418 #[cfg(target_arch = "x86")]
419 use core::arch::x86::_mm256_sllv_epi16;
420 #[cfg(target_arch = "x86_64")]
421 use core::arch::x86_64::_mm256_sllv_epi16;
422
423 cast(unsafe { _mm256_sllv_epi16(self.avx2.0, rhs.avx2.0) })
425 } else {
426 let [self_a, self_b] = cast::<u16x16, [u16x8; 2]>(self);
427 let [rhs_a, rhs_b] = cast::<u16x16, [u16x8; 2]>(rhs);
428
429 cast([self_a.unbounded_shl(rhs_a), self_b.unbounded_shl(rhs_b)])
430 }
431 }
432 }
433
434 #[inline]
435 pub fn unbounded_shl_scalar(self, rhs: u32) -> Self {
436 pick! {
437 if #[cfg(target_feature="avx2")] {
438 Self { avx2: shl_all_u16_m256i(self.avx2, cast([rhs as u64, 0])) }
439 } else {
440 Self {
441 a: self.a.unbounded_shl_scalar(rhs),
442 b: self.b.unbounded_shl_scalar(rhs),
443 }
444 }
445 }
446 }
447
448 #[inline]
449 pub fn unbounded_shr(self, rhs: Self) -> Self {
450 pick! {
451 if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
452 #[cfg(target_arch = "x86")]
453 use core::arch::x86::_mm256_srlv_epi16;
454 #[cfg(target_arch = "x86_64")]
455 use core::arch::x86_64::_mm256_srlv_epi16;
456
457 cast(unsafe { _mm256_srlv_epi16(self.avx2.0, rhs.avx2.0) })
459 } else {
460 let [self_a, self_b] = cast::<u16x16, [u16x8; 2]>(self);
461 let [rhs_a, rhs_b] = cast::<u16x16, [u16x8; 2]>(rhs);
462
463 cast([self_a.unbounded_shr(rhs_a), self_b.unbounded_shr(rhs_b)])
464 }
465 }
466 }
467
468 #[inline]
469 pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
470 pick! {
471 if #[cfg(target_feature="avx2")] {
472 Self { avx2: shr_all_u16_m256i(self.avx2, cast([rhs as u64, 0])) }
473 } else {
474 Self {
475 a: self.a.unbounded_shr_scalar(rhs),
476 b: self.b.unbounded_shr_scalar(rhs),
477 }
478 }
479 }
480 }
481
482 #[inline]
483 pub fn saturating_add(self, rhs: Self) -> Self {
484 pick! {
485 if #[cfg(target_feature="avx2")] {
486 Self { avx2: add_saturating_u16_m256i(self.avx2, rhs.avx2) }
487 } else {
488 Self {
489 a : self.a.saturating_add(rhs.a),
490 b : self.b.saturating_add(rhs.b),
491 }
492 }
493 }
494 }
495
496 #[inline]
497 pub fn saturating_sub(self, rhs: Self) -> Self {
498 pick! {
499 if #[cfg(target_feature="avx2")] {
500 Self { avx2: sub_saturating_u16_m256i(self.avx2, rhs.avx2) }
501 } else {
502 Self {
503 a : self.a.saturating_sub(rhs.a),
504 b : self.b.saturating_sub(rhs.b),
505 }
506 }
507 }
508 }
509
510 #[inline]
511 pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
512 let (low, high) = self.mul_keep_low_high(rhs);
513 let overflow = high.simd_ne(Self::ZERO);
514 (low, overflow)
515 }
516
517 optional_fn_widening_mul {
518 #[inline]
519 pub fn widening_mul(self, rhs: Self) -> u32x16 {
520 let [self_a, self_b] = cast::<u16x16, [u16x8; 2]>(self);
524 let [rhs_a, rhs_b] = cast::<u16x16, [u16x8; 2]>(rhs);
525
526 cast([self_a.widening_mul(rhs_a), self_b.widening_mul(rhs_b)])
527 }
528 }
529
530 #[inline]
531 pub fn mul_keep_low_high(self, rhs: Self) -> (Self, Self) {
532 let [self_a, self_b] = cast::<u16x16, [u16x8; 2]>(self);
536 let [rhs_a, rhs_b] = cast::<u16x16, [u16x8; 2]>(rhs);
537
538 let result_a = self_a.mul_keep_low_high(rhs_a);
539 let result_b = self_b.mul_keep_low_high(rhs_b);
540 (cast([result_a.0, result_b.0]), cast([result_a.1, result_b.1]))
541 }
542
543 #[inline]
544 pub fn mul_keep_high(self, rhs: Self) -> Self {
545 let [self_a, self_b] = cast::<u16x16, [u16x8; 2]>(self);
549 let [rhs_a, rhs_b] = cast::<u16x16, [u16x8; 2]>(rhs);
550
551 cast([self_a.mul_keep_high(rhs_a), self_b.mul_keep_high(rhs_b)])
552 }
553}
554
555impl From<u8x16> for u16x16 {
556 #[inline]
558 fn from(v: u8x16) -> Self {
559 pick! {
560 if #[cfg(target_feature="avx2")] {
561 u16x16 { avx2:convert_to_i16_m256i_from_u8_m128i(v.sse) }
562 } else if #[cfg(target_feature="sse2")] {
563 u16x16 {
564 a: u16x8 { sse: shr_imm_u16_m128i::<8>( unpack_low_i8_m128i(v.sse, v.sse)) },
565 b: u16x8 { sse: shr_imm_u16_m128i::<8>( unpack_high_i8_m128i(v.sse, v.sse)) },
566 }
567 } else {
568
569 u16x16::new([
570 v.as_array()[0] as u16,
571 v.as_array()[1] as u16,
572 v.as_array()[2] as u16,
573 v.as_array()[3] as u16,
574 v.as_array()[4] as u16,
575 v.as_array()[5] as u16,
576 v.as_array()[6] as u16,
577 v.as_array()[7] as u16,
578 v.as_array()[8] as u16,
579 v.as_array()[9] as u16,
580 v.as_array()[10] as u16,
581 v.as_array()[11] as u16,
582 v.as_array()[12] as u16,
583 v.as_array()[13] as u16,
584 v.as_array()[14] as u16,
585 v.as_array()[15] as u16,
586 ])
587 }
588 }
589 }
590}