Skip to main content

rten_simd/arch/x86_64/
avx512.rs

1use std::arch::x86_64::{
2    __m512, __m512i, __mmask16, __mmask32, __mmask64, _CMP_EQ_OQ, _CMP_GE_OQ, _CMP_GT_OQ,
3    _CMP_LE_OQ, _CMP_LT_OQ, _MM_CMPINT_EQ, _MM_CMPINT_NLE, _MM_CMPINT_NLT,
4    _MM_FROUND_TO_NEAREST_INT, _MM_HINT_ET0, _MM_HINT_T0, _mm_prefetch, _mm512_add_epi8,
5    _mm512_add_epi16, _mm512_add_epi32, _mm512_add_ps, _mm512_and_ps, _mm512_and_si512,
6    _mm512_andnot_ps, _mm512_andnot_si512, _mm512_castsi256_si512, _mm512_castsi512_si256,
7    _mm512_cmp_epi16_mask, _mm512_cmp_epi32_mask, _mm512_cmp_epu16_mask, _mm512_cmp_ps_mask,
8    _mm512_cmpeq_epi8_mask, _mm512_cmpeq_epu8_mask, _mm512_cmpge_epi8_mask, _mm512_cmpge_epu8_mask,
9    _mm512_cmpgt_epi8_mask, _mm512_cmpgt_epu8_mask, _mm512_cvtepi8_epi16, _mm512_cvtepi16_epi8,
10    _mm512_cvtepi16_epi32, _mm512_cvtepi32_ps, _mm512_cvtepu8_epi16, _mm512_cvtph_ps,
11    _mm512_cvtps_epi32, _mm512_cvtps_ph, _mm512_cvttps_epi32, _mm512_div_ps,
12    _mm512_extracti64x4_epi64, _mm512_fmadd_ps, _mm512_fnmadd_ps, _mm512_inserti64x4,
13    _mm512_loadu_ps, _mm512_loadu_si512, _mm512_mask_blend_epi8, _mm512_mask_blend_epi16,
14    _mm512_mask_blend_epi32, _mm512_mask_blend_ps, _mm512_mask_loadu_epi8, _mm512_mask_loadu_epi16,
15    _mm512_mask_loadu_epi32, _mm512_mask_loadu_ps, _mm512_mask_storeu_epi8,
16    _mm512_mask_storeu_epi16, _mm512_mask_storeu_epi32, _mm512_mask_storeu_ps, _mm512_max_ps,
17    _mm512_min_ps, _mm512_mul_ps, _mm512_mullo_epi16, _mm512_mullo_epi32, _mm512_or_ps,
18    _mm512_or_si512, _mm512_packs_epi32, _mm512_packus_epi16, _mm512_permutex2var_epi32,
19    _mm512_permutexvar_epi64, _mm512_reduce_add_ps, _mm512_roundscale_ps, _mm512_set1_epi8,
20    _mm512_set1_epi16, _mm512_set1_epi32, _mm512_set1_ps, _mm512_setr_epi32, _mm512_setr_epi64,
21    _mm512_setzero_si512, _mm512_sllv_epi16, _mm512_sllv_epi32, _mm512_srav_epi16,
22    _mm512_srav_epi32, _mm512_srlv_epi16, _mm512_storeu_ps, _mm512_storeu_si512, _mm512_sub_epi8,
23    _mm512_sub_epi16, _mm512_sub_epi32, _mm512_sub_ps, _mm512_unpackhi_epi8, _mm512_unpackhi_epi16,
24    _mm512_unpacklo_epi8, _mm512_unpacklo_epi16, _mm512_xor_ps, _mm512_xor_si512,
25};
26use std::mem::transmute;
27
28use super::super::{lanes, simd_type};
29use crate::f16;
30use crate::ops::{
31    BitOps, Concat, Extend, FloatOps, IntOps, Interleave, MaskOps, Narrow, NarrowSaturate, NumOps,
32    SignedIntOps, ToFloat,
33};
34use crate::{Isa, Mask, Simd};
35
36simd_type!(F32x16, __m512, f32, __mmask16, Avx512Isa);
37simd_type!(F16x32, __m512i, f16, __mmask32, Avx512Isa);
38simd_type!(I32x16, __m512i, i32, __mmask16, Avx512Isa);
39simd_type!(I16x32, __m512i, i16, __mmask32, Avx512Isa);
40simd_type!(I8x64, __m512i, i8, __mmask64, Avx512Isa);
41simd_type!(U8x64, __m512i, u8, __mmask64, Avx512Isa);
42simd_type!(U16x32, __m512i, u16, __mmask32, Avx512Isa);
43simd_type!(U32x16, __m512i, u32, __mmask16, Avx512Isa);
44
45#[derive(Copy, Clone)]
46pub struct Avx512Isa {
47    _private: (),
48}
49
50impl Avx512Isa {
51    pub fn new() -> Option<Self> {
52        if crate::is_avx512_supported() && std::is_x86_feature_detected!("f16c") {
53            Some(Avx512Isa { _private: () })
54        } else {
55            None
56        }
57    }
58}
59
60// Safety: AVX-512 is supported as `Avx512Isa::new` checks this.
61unsafe impl Isa for Avx512Isa {
62    type M32 = __mmask16;
63    type M16 = __mmask32;
64    type M8 = __mmask64;
65    type F32 = F32x16;
66    type I32 = I32x16;
67    type I16 = I16x32;
68    type I8 = I8x64;
69    type U8 = U8x64;
70    type U16 = U16x32;
71    type U32 = U32x16;
72    type F16 = F16x32;
73    type Bits = I32x16;
74
75    fn f32(
76        self,
77    ) -> impl FloatOps<f32, Simd = Self::F32, Int = Self::I32>
78    + NarrowSaturate<f32, f16, Output = Self::F16> {
79        self
80    }
81
82    fn f16(self) -> impl Extend<f16, Output = Self::F32, Simd = Self::F16> {
83        self
84    }
85
86    fn i32(
87        self,
88    ) -> impl SignedIntOps<i32, Simd = Self::I32>
89    + NarrowSaturate<i32, i16, Output = Self::I16>
90    + Concat<i32>
91    + ToFloat<i32, Output = Self::F32> {
92        self
93    }
94
95    fn i16(
96        self,
97    ) -> impl SignedIntOps<i16, Simd = Self::I16>
98    + NarrowSaturate<i16, u8, Output = Self::U8>
99    + Extend<i16, Output = Self::I32>
100    + Interleave<i16> {
101        self
102    }
103
104    fn i8(
105        self,
106    ) -> impl SignedIntOps<i8, Simd = Self::I8> + Extend<i8, Output = Self::I16> + Interleave<i8>
107    {
108        self
109    }
110
111    fn u8(
112        self,
113    ) -> impl IntOps<u8, Simd = Self::U8> + Extend<u8, Output = Self::U16> + Interleave<u8> {
114        self
115    }
116
117    fn u16(self) -> impl IntOps<u16, Simd = Self::U16> {
118        self
119    }
120
121    fn m32(self) -> impl MaskOps<Self::M32> {
122        self
123    }
124
125    fn m16(self) -> impl MaskOps<Self::M16> {
126        self
127    }
128
129    fn m8(self) -> impl MaskOps<Self::M8> {
130        self
131    }
132}
133
134macro_rules! simd_ops_common {
135    ($simd:ty, $mask:ty) => {
136        type Simd = $simd;
137
138        #[inline]
139        fn len(self) -> usize {
140            lanes::<$simd>()
141        }
142
143        #[inline]
144        fn first_n_mask(self, n: usize) -> $mask {
145            let mut mask = 0;
146            for i in 0..n {
147                mask |= 1 << i;
148            }
149            mask
150        }
151
152        #[inline]
153        fn prefetch(self, ptr: *const <$simd as Simd>::Elem) {
154            unsafe { _mm_prefetch(ptr as *const i8, _MM_HINT_T0) }
155        }
156
157        #[inline]
158        fn prefetch_write(self, ptr: *mut <$simd as Simd>::Elem) {
159            unsafe { _mm_prefetch(ptr as *const i8, _MM_HINT_ET0) }
160        }
161    };
162}
163
164macro_rules! simd_int_ops_common {
165    ($simd:ty) => {
166        #[inline]
167        fn and(self, x: $simd, y: $simd) -> $simd {
168            unsafe { _mm512_and_si512(x.0, y.0) }.into()
169        }
170
171        #[inline]
172        fn or(self, x: $simd, y: $simd) -> $simd {
173            unsafe { _mm512_or_si512(x.0, y.0) }.into()
174        }
175
176        #[inline]
177        fn xor(self, x: $simd, y: $simd) -> $simd {
178            unsafe { _mm512_xor_si512(x.0, y.0) }.into()
179        }
180
181        #[inline]
182        fn not(self, x: $simd) -> $simd {
183            unsafe { _mm512_andnot_si512(x.0, _mm512_set1_epi8(-1)) }.into()
184        }
185    };
186}
187
188unsafe impl BitOps<f32> for Avx512Isa {
189    simd_ops_common!(F32x16, __mmask16);
190
191    #[inline]
192    fn and(self, x: F32x16, y: F32x16) -> F32x16 {
193        unsafe { _mm512_and_ps(x.0, y.0) }.into()
194    }
195
196    #[inline]
197    fn not(self, x: F32x16) -> F32x16 {
198        let all_ones: F32x16 = self.splat(f32::from_bits(0xFFFFFFFF));
199        unsafe { _mm512_andnot_ps(x.0, all_ones.0) }.into()
200    }
201
202    #[inline]
203    fn or(self, x: F32x16, y: F32x16) -> F32x16 {
204        unsafe { _mm512_or_ps(x.0, y.0) }.into()
205    }
206
207    #[inline]
208    fn xor(self, x: F32x16, y: F32x16) -> F32x16 {
209        unsafe { _mm512_xor_ps(x.0, y.0) }.into()
210    }
211
212    #[inline]
213    fn splat(self, x: f32) -> F32x16 {
214        unsafe { _mm512_set1_ps(x) }.into()
215    }
216
217    #[inline]
218    unsafe fn load_ptr(self, ptr: *const f32) -> F32x16 {
219        unsafe { _mm512_loadu_ps(ptr) }.into()
220    }
221
222    #[inline]
223    fn select(self, x: F32x16, y: F32x16, mask: <F32x16 as Simd>::Mask) -> F32x16 {
224        unsafe { _mm512_mask_blend_ps(mask, y.0, x.0) }.into()
225    }
226
227    #[inline]
228    unsafe fn load_ptr_mask(self, ptr: *const f32, mask: __mmask16) -> F32x16 {
229        unsafe { _mm512_mask_loadu_ps(_mm512_set1_ps(0.), mask, ptr) }.into()
230    }
231
232    #[inline]
233    unsafe fn store_ptr_mask(self, x: F32x16, ptr: *mut f32, mask: __mmask16) {
234        unsafe { _mm512_mask_storeu_ps(ptr, mask, x.0) }
235    }
236
237    #[inline]
238    unsafe fn store_ptr(self, x: F32x16, ptr: *mut f32) {
239        unsafe { _mm512_storeu_ps(ptr, x.0) }
240    }
241}
242
243unsafe impl NumOps<f32> for Avx512Isa {
244    #[inline]
245    fn add(self, x: F32x16, y: F32x16) -> F32x16 {
246        unsafe { _mm512_add_ps(x.0, y.0) }.into()
247    }
248
249    #[inline]
250    fn sub(self, x: F32x16, y: F32x16) -> F32x16 {
251        unsafe { _mm512_sub_ps(x.0, y.0) }.into()
252    }
253
254    #[inline]
255    fn mul(self, x: F32x16, y: F32x16) -> F32x16 {
256        unsafe { _mm512_mul_ps(x.0, y.0) }.into()
257    }
258
259    #[inline]
260    fn mul_add(self, a: F32x16, b: F32x16, c: F32x16) -> F32x16 {
261        unsafe { _mm512_fmadd_ps(a.0, b.0, c.0) }.into()
262    }
263
264    #[inline]
265    fn lt(self, x: F32x16, y: F32x16) -> __mmask16 {
266        unsafe { _mm512_cmp_ps_mask(x.0, y.0, _CMP_LT_OQ) }
267    }
268
269    #[inline]
270    fn le(self, x: F32x16, y: F32x16) -> __mmask16 {
271        unsafe { _mm512_cmp_ps_mask(x.0, y.0, _CMP_LE_OQ) }
272    }
273
274    #[inline]
275    fn eq(self, x: F32x16, y: F32x16) -> __mmask16 {
276        unsafe { _mm512_cmp_ps_mask(x.0, y.0, _CMP_EQ_OQ) }
277    }
278
279    #[inline]
280    fn ge(self, x: F32x16, y: F32x16) -> __mmask16 {
281        unsafe { _mm512_cmp_ps_mask(x.0, y.0, _CMP_GE_OQ) }
282    }
283
284    #[inline]
285    fn gt(self, x: F32x16, y: F32x16) -> __mmask16 {
286        unsafe { _mm512_cmp_ps_mask(x.0, y.0, _CMP_GT_OQ) }
287    }
288
289    #[inline]
290    fn min(self, x: F32x16, y: F32x16) -> F32x16 {
291        unsafe { _mm512_min_ps(x.0, y.0) }.into()
292    }
293
294    #[inline]
295    fn max(self, x: F32x16, y: F32x16) -> F32x16 {
296        unsafe { _mm512_max_ps(x.0, y.0) }.into()
297    }
298
299    #[inline]
300    fn sum(self, x: F32x16) -> f32 {
301        unsafe { _mm512_reduce_add_ps(x.0) }
302    }
303}
304
305impl FloatOps<f32> for Avx512Isa {
306    type Int = <Self as Isa>::I32;
307
308    #[inline]
309    fn div(self, x: F32x16, y: F32x16) -> F32x16 {
310        unsafe { _mm512_div_ps(x.0, y.0) }.into()
311    }
312
313    #[inline]
314    fn abs(self, x: F32x16) -> F32x16 {
315        unsafe { _mm512_andnot_ps(_mm512_set1_ps(-0.0), x.0) }.into()
316    }
317
318    #[inline]
319    fn neg(self, x: F32x16) -> F32x16 {
320        unsafe { _mm512_xor_ps(x.0, _mm512_set1_ps(-0.0)) }.into()
321    }
322
323    #[inline]
324    fn mul_sub_from(self, a: F32x16, b: F32x16, c: F32x16) -> F32x16 {
325        unsafe { _mm512_fnmadd_ps(a.0, b.0, c.0) }.into()
326    }
327
328    #[inline]
329    fn round_ties_even(self, x: F32x16) -> F32x16 {
330        unsafe { _mm512_roundscale_ps(x.0, _MM_FROUND_TO_NEAREST_INT) }.into()
331    }
332
333    #[inline]
334    fn to_int_trunc(self, x: F32x16) -> Self::Int {
335        unsafe { _mm512_cvttps_epi32(x.0) }.into()
336    }
337
338    #[inline]
339    fn to_int_round(self, x: F32x16) -> Self::Int {
340        unsafe { _mm512_cvtps_epi32(x.0) }.into()
341    }
342}
343
344unsafe impl BitOps<i32> for Avx512Isa {
345    simd_ops_common!(I32x16, __mmask16);
346    simd_int_ops_common!(I32x16);
347
348    #[inline]
349    fn splat(self, x: i32) -> I32x16 {
350        unsafe { _mm512_set1_epi32(x) }.into()
351    }
352
353    #[inline]
354    unsafe fn load_ptr(self, ptr: *const i32) -> I32x16 {
355        unsafe { _mm512_loadu_si512(ptr as *const __m512i) }.into()
356    }
357
358    #[inline]
359    fn select(self, x: I32x16, y: I32x16, mask: <I32x16 as Simd>::Mask) -> I32x16 {
360        unsafe { _mm512_mask_blend_epi32(mask, y.0, x.0) }.into()
361    }
362
363    #[inline]
364    unsafe fn store_ptr(self, x: I32x16, ptr: *mut i32) {
365        unsafe { _mm512_storeu_si512(ptr as *mut __m512i, x.0) }
366    }
367
368    #[inline]
369    unsafe fn load_ptr_mask(self, ptr: *const i32, mask: __mmask16) -> I32x16 {
370        unsafe { _mm512_mask_loadu_epi32(_mm512_set1_epi32(0), mask, ptr) }.into()
371    }
372
373    #[inline]
374    unsafe fn store_ptr_mask(self, x: I32x16, ptr: *mut i32, mask: __mmask16) {
375        unsafe { _mm512_mask_storeu_epi32(ptr, mask, x.0) }
376    }
377}
378
379unsafe impl NumOps<i32> for Avx512Isa {
380    #[inline]
381    fn add(self, x: I32x16, y: I32x16) -> I32x16 {
382        unsafe { _mm512_add_epi32(x.0, y.0) }.into()
383    }
384
385    #[inline]
386    fn sub(self, x: I32x16, y: I32x16) -> I32x16 {
387        unsafe { _mm512_sub_epi32(x.0, y.0) }.into()
388    }
389
390    #[inline]
391    fn mul(self, x: I32x16, y: I32x16) -> I32x16 {
392        unsafe { _mm512_mullo_epi32(x.0, y.0) }.into()
393    }
394
395    #[inline]
396    fn eq(self, x: I32x16, y: I32x16) -> __mmask16 {
397        unsafe { _mm512_cmp_epi32_mask(x.0, y.0, _MM_CMPINT_EQ) }
398    }
399
400    #[inline]
401    fn ge(self, x: I32x16, y: I32x16) -> __mmask16 {
402        unsafe { _mm512_cmp_epi32_mask(x.0, y.0, _MM_CMPINT_NLT) }
403    }
404
405    #[inline]
406    fn gt(self, x: I32x16, y: I32x16) -> __mmask16 {
407        unsafe { _mm512_cmp_epi32_mask(x.0, y.0, _MM_CMPINT_NLE) }
408    }
409}
410
411impl IntOps<i32> for Avx512Isa {
412    #[inline]
413    fn shift_left<const SHIFT: i32>(self, x: I32x16) -> I32x16 {
414        let count: I32x16 = self.splat(SHIFT);
415        unsafe { _mm512_sllv_epi32(x.0, count.0) }.into()
416    }
417
418    #[inline]
419    fn shift_right<const SHIFT: i32>(self, x: I32x16) -> I32x16 {
420        let count: I32x16 = self.splat(SHIFT);
421        unsafe { _mm512_srav_epi32(x.0, count.0) }.into()
422    }
423}
424
425impl SignedIntOps<i32> for Avx512Isa {
426    #[inline]
427    fn neg(self, x: I32x16) -> I32x16 {
428        unsafe { _mm512_sub_epi32(_mm512_setzero_si512(), x.0) }.into()
429    }
430}
431
432impl NarrowSaturate<i32, i16> for Avx512Isa {
433    type Output = I16x32;
434
435    #[inline]
436    fn narrow_saturate(self, low: I32x16, high: I32x16) -> I16x32 {
437        unsafe {
438            // _mm512_packs_epi32 treats each input as 4 128-bit lanes and
439            // interleaves narrowed 64-bit blocks from each input. Shuffle the
440            // output to get narrowed lanes from `low` followed by lanes from
441            // `high`.
442            let packed = _mm512_packs_epi32(low.0, high.0);
443            let permutation = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7);
444            _mm512_permutexvar_epi64(permutation, packed)
445        }
446        .into()
447    }
448}
449
450impl Concat<i32> for Avx512Isa {
451    #[inline]
452    fn concat_low(self, a: I32x16, b: I32x16) -> I32x16 {
453        unsafe {
454            let a_lo = _mm512_castsi512_si256(a.0);
455            let b_lo = _mm512_castsi512_si256(b.0);
456            _mm512_inserti64x4(_mm512_castsi256_si512(a_lo), b_lo, 1)
457        }
458        .into()
459    }
460
461    #[inline]
462    fn concat_high(self, a: I32x16, b: I32x16) -> I32x16 {
463        unsafe {
464            let a_hi = _mm512_extracti64x4_epi64(a.0, 1);
465            let b_hi = _mm512_extracti64x4_epi64(b.0, 1);
466            _mm512_inserti64x4(_mm512_castsi256_si512(a_hi), b_hi, 1)
467        }
468        .into()
469    }
470}
471
472impl ToFloat<i32> for Avx512Isa {
473    type Output = F32x16;
474
475    #[inline]
476    fn to_float(self, x: I32x16) -> F32x16 {
477        unsafe { _mm512_cvtepi32_ps(x.0) }.into()
478    }
479}
480
481unsafe impl BitOps<i16> for Avx512Isa {
482    simd_ops_common!(I16x32, __mmask32);
483    simd_int_ops_common!(I16x32);
484
485    #[inline]
486    fn splat(self, x: i16) -> I16x32 {
487        unsafe { _mm512_set1_epi16(x) }.into()
488    }
489
490    #[inline]
491    unsafe fn load_ptr(self, ptr: *const i16) -> I16x32 {
492        unsafe { _mm512_loadu_si512(ptr as *const __m512i) }.into()
493    }
494
495    #[inline]
496    fn select(self, x: I16x32, y: I16x32, mask: <I16x32 as Simd>::Mask) -> I16x32 {
497        unsafe { _mm512_mask_blend_epi16(mask, y.0, x.0) }.into()
498    }
499
500    #[inline]
501    unsafe fn store_ptr(self, x: I16x32, ptr: *mut i16) {
502        unsafe { _mm512_storeu_si512(ptr as *mut __m512i, x.0) }
503    }
504
505    #[inline]
506    unsafe fn load_ptr_mask(self, ptr: *const i16, mask: __mmask32) -> I16x32 {
507        unsafe { _mm512_mask_loadu_epi16(_mm512_set1_epi16(0), mask, ptr) }.into()
508    }
509
510    #[inline]
511    unsafe fn store_ptr_mask(self, x: I16x32, ptr: *mut i16, mask: __mmask32) {
512        unsafe { _mm512_mask_storeu_epi16(ptr, mask, x.0) }
513    }
514}
515
516unsafe impl NumOps<i16> for Avx512Isa {
517    #[inline]
518    fn add(self, x: I16x32, y: I16x32) -> I16x32 {
519        unsafe { _mm512_add_epi16(x.0, y.0) }.into()
520    }
521
522    #[inline]
523    fn sub(self, x: I16x32, y: I16x32) -> I16x32 {
524        unsafe { _mm512_sub_epi16(x.0, y.0) }.into()
525    }
526
527    #[inline]
528    fn mul(self, x: I16x32, y: I16x32) -> I16x32 {
529        unsafe { _mm512_mullo_epi16(x.0, y.0) }.into()
530    }
531
532    #[inline]
533    fn eq(self, x: I16x32, y: I16x32) -> __mmask32 {
534        unsafe { _mm512_cmp_epi16_mask(x.0, y.0, _MM_CMPINT_EQ) }
535    }
536
537    #[inline]
538    fn ge(self, x: I16x32, y: I16x32) -> __mmask32 {
539        unsafe { _mm512_cmp_epi16_mask(x.0, y.0, _MM_CMPINT_NLT) }
540    }
541
542    #[inline]
543    fn gt(self, x: I16x32, y: I16x32) -> __mmask32 {
544        unsafe { _mm512_cmp_epi16_mask(x.0, y.0, _MM_CMPINT_NLE) }
545    }
546}
547
548impl IntOps<i16> for Avx512Isa {
549    #[inline]
550    fn shift_left<const SHIFT: i32>(self, x: I16x32) -> I16x32 {
551        let count: I16x32 = self.splat(SHIFT as i16);
552        unsafe { _mm512_sllv_epi16(x.0, count.0) }.into()
553    }
554
555    #[inline]
556    fn shift_right<const SHIFT: i32>(self, x: I16x32) -> I16x32 {
557        let count: I16x32 = self.splat(SHIFT as i16);
558        unsafe { _mm512_srav_epi16(x.0, count.0) }.into()
559    }
560}
561
562impl SignedIntOps<i16> for Avx512Isa {
563    #[inline]
564    fn neg(self, x: I16x32) -> I16x32 {
565        unsafe { _mm512_sub_epi16(_mm512_setzero_si512(), x.0) }.into()
566    }
567}
568
569impl NarrowSaturate<i16, u8> for Avx512Isa {
570    type Output = U8x64;
571
572    #[inline]
573    fn narrow_saturate(self, low: I16x32, high: I16x32) -> U8x64 {
574        unsafe {
575            // _mm512_packus_epi16 treats each input as 4 128-bit lanes and
576            // interleaves narrowed 64-bit blocks from each input. Shuffle the
577            // output to get narrowed lanes from `low` followed by lanes from
578            // `high`.
579            let packed = _mm512_packus_epi16(low.0, high.0);
580            let permutation = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7);
581            _mm512_permutexvar_epi64(permutation, packed)
582        }
583        .into()
584    }
585}
586
587impl Interleave<i16> for Avx512Isa {
588    #[inline]
589    fn interleave_low(self, a: I16x32, b: I16x32) -> I16x32 {
590        unsafe {
591            // AB{N} = Interleaved Nth 64-bit block.
592            let lo = _mm512_unpacklo_epi16(a.0, b.0); // AB0 AB2 AB4 AB6
593            let hi = _mm512_unpackhi_epi16(a.0, b.0); // AB1 AB3 AB5 AB7
594            let idx = _mm512_setr_epi32(0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20, 21, 22, 23);
595            _mm512_permutex2var_epi32(lo, idx, hi) // AB0 AB1 AB2 AB3
596        }
597        .into()
598    }
599
600    #[inline]
601    fn interleave_high(self, a: I16x32, b: I16x32) -> I16x32 {
602        unsafe {
603            // AB{N} = Interleaved Nth 64-bit block.
604            let lo = _mm512_unpacklo_epi16(a.0, b.0); // AB0 AB2 AB4 AB6
605            let hi = _mm512_unpackhi_epi16(a.0, b.0); // AB1 AB3 AB5 AB7
606            let idx =
607                _mm512_setr_epi32(8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15, 28, 29, 30, 31);
608            _mm512_permutex2var_epi32(lo, idx, hi) // AB4 AB5 AB6 AB7
609        }
610        .into()
611    }
612}
613
614unsafe impl BitOps<i8> for Avx512Isa {
615    simd_ops_common!(I8x64, __mmask64);
616    simd_int_ops_common!(I8x64);
617
618    #[inline]
619    fn splat(self, x: i8) -> I8x64 {
620        unsafe { _mm512_set1_epi8(x) }.into()
621    }
622
623    #[inline]
624    unsafe fn load_ptr(self, ptr: *const i8) -> I8x64 {
625        unsafe { _mm512_loadu_si512(ptr as *const __m512i) }.into()
626    }
627
628    #[inline]
629    fn select(self, x: I8x64, y: I8x64, mask: <I8x64 as Simd>::Mask) -> I8x64 {
630        unsafe { _mm512_mask_blend_epi8(mask, y.0, x.0) }.into()
631    }
632
633    #[inline]
634    unsafe fn store_ptr(self, x: I8x64, ptr: *mut i8) {
635        unsafe { _mm512_storeu_si512(ptr as *mut __m512i, x.0) }
636    }
637
638    #[inline]
639    unsafe fn load_ptr_mask(self, ptr: *const i8, mask: __mmask64) -> I8x64 {
640        unsafe { _mm512_mask_loadu_epi8(_mm512_set1_epi8(0), mask, ptr) }.into()
641    }
642
643    #[inline]
644    unsafe fn store_ptr_mask(self, x: I8x64, ptr: *mut i8, mask: __mmask64) {
645        unsafe { _mm512_mask_storeu_epi8(ptr, mask, x.0) }
646    }
647}
648
649unsafe impl NumOps<i8> for Avx512Isa {
650    #[inline]
651    fn add(self, x: I8x64, y: I8x64) -> I8x64 {
652        unsafe { _mm512_add_epi8(x.0, y.0) }.into()
653    }
654
655    #[inline]
656    fn sub(self, x: I8x64, y: I8x64) -> I8x64 {
657        unsafe { _mm512_sub_epi8(x.0, y.0) }.into()
658    }
659
660    #[inline]
661    fn mul(self, x: I8x64, y: I8x64) -> I8x64 {
662        let x_lo = Extend::<i8>::extend_low(self, x);
663        let x_hi = Extend::<i8>::extend_high(self, x);
664        let y_lo = Extend::<i8>::extend_low(self, y);
665        let y_hi = Extend::<i8>::extend_high(self, y);
666
667        let i16_ops = self.i16();
668        let prod_lo = i16_ops.mul(x_lo, y_lo);
669        let prod_hi = i16_ops.mul(x_hi, y_hi);
670
671        self.narrow_truncate(prod_lo, prod_hi)
672    }
673
674    #[inline]
675    fn eq(self, x: I8x64, y: I8x64) -> __mmask64 {
676        unsafe { _mm512_cmpeq_epi8_mask(x.0, y.0) }
677    }
678
679    #[inline]
680    fn ge(self, x: I8x64, y: I8x64) -> __mmask64 {
681        unsafe { _mm512_cmpge_epi8_mask(x.0, y.0) }
682    }
683
684    #[inline]
685    fn gt(self, x: I8x64, y: I8x64) -> __mmask64 {
686        unsafe { _mm512_cmpgt_epi8_mask(x.0, y.0) }
687    }
688}
689
690impl IntOps<i8> for Avx512Isa {
691    #[inline]
692    fn shift_left<const SHIFT: i32>(self, x: I8x64) -> I8x64 {
693        let x_lo = Extend::<i8>::extend_low(self, x);
694        let x_hi = Extend::<i8>::extend_high(self, x);
695
696        let i16_ops = self.i16();
697        let (y_lo, y_hi) = (
698            i16_ops.shift_left::<SHIFT>(x_lo),
699            i16_ops.shift_left::<SHIFT>(x_hi),
700        );
701
702        self.narrow_truncate(y_lo, y_hi)
703    }
704
705    #[inline]
706    fn shift_right<const SHIFT: i32>(self, x: I8x64) -> I8x64 {
707        let x_lo = Extend::<i8>::extend_low(self, x);
708        let x_hi = Extend::<i8>::extend_high(self, x);
709
710        let i16_ops = self.i16();
711        let (y_lo, y_hi) = (
712            i16_ops.shift_right::<SHIFT>(x_lo),
713            i16_ops.shift_right::<SHIFT>(x_hi),
714        );
715
716        self.narrow_truncate(y_lo, y_hi)
717    }
718}
719
720impl SignedIntOps<i8> for Avx512Isa {
721    #[inline]
722    fn neg(self, x: I8x64) -> I8x64 {
723        unsafe { _mm512_sub_epi8(_mm512_setzero_si512(), x.0) }.into()
724    }
725}
726
727#[inline]
728fn interleave_low_x8(a: __m512i, b: __m512i) -> __m512i {
729    unsafe {
730        // AB{N} = Interleaved Nth 64-bit block.
731        let lo = _mm512_unpacklo_epi8(a, b); // AB0 AB2 AB4 AB6
732        let hi = _mm512_unpackhi_epi8(a, b); // AB1 AB3 AB5 AB7
733        let idx = _mm512_setr_epi32(0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20, 21, 22, 23);
734        _mm512_permutex2var_epi32(lo, idx, hi) // AB0 AB1 AB2 AB3
735    }
736}
737
738#[inline]
739fn interleave_high_x8(a: __m512i, b: __m512i) -> __m512i {
740    unsafe {
741        // AB{N} = Interleaved Nth 64-bit block.
742        let lo = _mm512_unpacklo_epi8(a, b); // AB0 AB2 AB4 AB6
743        let hi = _mm512_unpackhi_epi8(a, b); // AB1 AB3 AB5 AB7
744        let idx = _mm512_setr_epi32(8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15, 28, 29, 30, 31);
745        _mm512_permutex2var_epi32(lo, idx, hi) // AB4 AB5 AB6 AB7
746    }
747}
748
749impl Interleave<i8> for Avx512Isa {
750    #[inline]
751    fn interleave_low(self, a: I8x64, b: I8x64) -> I8x64 {
752        interleave_low_x8(a.0, b.0).into()
753    }
754
755    #[inline]
756    fn interleave_high(self, a: I8x64, b: I8x64) -> I8x64 {
757        interleave_high_x8(a.0, b.0).into()
758    }
759}
760
761unsafe impl BitOps<u8> for Avx512Isa {
762    simd_ops_common!(U8x64, __mmask64);
763    simd_int_ops_common!(U8x64);
764
765    #[inline]
766    fn splat(self, x: u8) -> U8x64 {
767        unsafe { _mm512_set1_epi8(x as i8) }.into()
768    }
769
770    #[inline]
771    unsafe fn load_ptr(self, ptr: *const u8) -> U8x64 {
772        unsafe { _mm512_loadu_si512(ptr as *const __m512i) }.into()
773    }
774
775    #[inline]
776    fn select(self, x: U8x64, y: U8x64, mask: <U8x64 as Simd>::Mask) -> U8x64 {
777        unsafe { _mm512_mask_blend_epi8(mask, y.0, x.0) }.into()
778    }
779
780    #[inline]
781    unsafe fn store_ptr(self, x: U8x64, ptr: *mut u8) {
782        unsafe { _mm512_storeu_si512(ptr as *mut __m512i, x.0) }
783    }
784
785    #[inline]
786    unsafe fn load_ptr_mask(self, ptr: *const u8, mask: __mmask64) -> U8x64 {
787        unsafe { _mm512_mask_loadu_epi8(_mm512_set1_epi8(0), mask, ptr as *const i8) }.into()
788    }
789
790    #[inline]
791    unsafe fn store_ptr_mask(self, x: U8x64, ptr: *mut u8, mask: __mmask64) {
792        unsafe { _mm512_mask_storeu_epi8(ptr as *mut i8, mask, x.0) }
793    }
794}
795
796unsafe impl NumOps<u8> for Avx512Isa {
797    #[inline]
798    fn add(self, x: U8x64, y: U8x64) -> U8x64 {
799        unsafe { _mm512_add_epi8(x.0, y.0) }.into()
800    }
801
802    #[inline]
803    fn sub(self, x: U8x64, y: U8x64) -> U8x64 {
804        unsafe { _mm512_sub_epi8(x.0, y.0) }.into()
805    }
806
807    #[inline]
808    fn mul(self, x: U8x64, y: U8x64) -> U8x64 {
809        let x_lo = Extend::<u8>::extend_low(self, x);
810        let x_hi = Extend::<u8>::extend_high(self, x);
811        let y_lo = Extend::<u8>::extend_low(self, y);
812        let y_hi = Extend::<u8>::extend_high(self, y);
813
814        let u16_ops = self.u16();
815        let prod_lo = u16_ops.mul(x_lo, y_lo);
816        let prod_hi = u16_ops.mul(x_hi, y_hi);
817
818        self.narrow_truncate(prod_lo, prod_hi)
819    }
820
821    #[inline]
822    fn eq(self, x: U8x64, y: U8x64) -> __mmask64 {
823        unsafe { _mm512_cmpeq_epu8_mask(x.0, y.0) }
824    }
825
826    #[inline]
827    fn ge(self, x: U8x64, y: U8x64) -> __mmask64 {
828        unsafe { _mm512_cmpge_epu8_mask(x.0, y.0) }
829    }
830
831    #[inline]
832    fn gt(self, x: U8x64, y: U8x64) -> __mmask64 {
833        unsafe { _mm512_cmpgt_epu8_mask(x.0, y.0) }
834    }
835}
836
837impl Extend<i16> for Avx512Isa {
838    type Output = I32x16;
839
840    #[inline]
841    fn extend_low(self, x: I16x32) -> Self::Output {
842        unsafe { _mm512_cvtepi16_epi32(_mm512_extracti64x4_epi64(x.0, 0)).into() }
843    }
844
845    #[inline]
846    fn extend_high(self, x: I16x32) -> Self::Output {
847        unsafe { _mm512_cvtepi16_epi32(_mm512_extracti64x4_epi64(x.0, 1)).into() }
848    }
849}
850
851impl Extend<i8> for Avx512Isa {
852    type Output = I16x32;
853
854    #[inline]
855    fn extend_low(self, x: I8x64) -> I16x32 {
856        unsafe { _mm512_cvtepi8_epi16(_mm512_extracti64x4_epi64(x.0, 0)).into() }
857    }
858
859    #[inline]
860    fn extend_high(self, x: I8x64) -> I16x32 {
861        unsafe { _mm512_cvtepi8_epi16(_mm512_extracti64x4_epi64(x.0, 1)).into() }
862    }
863}
864
865impl Extend<u8> for Avx512Isa {
866    type Output = U16x32;
867
868    #[inline]
869    fn extend_low(self, x: U8x64) -> U16x32 {
870        unsafe { _mm512_cvtepu8_epi16(_mm512_extracti64x4_epi64(x.0, 0)).into() }
871    }
872
873    #[inline]
874    fn extend_high(self, x: U8x64) -> U16x32 {
875        unsafe { _mm512_cvtepu8_epi16(_mm512_extracti64x4_epi64(x.0, 1)).into() }
876    }
877}
878
879impl IntOps<u8> for Avx512Isa {
880    #[inline]
881    fn shift_left<const SHIFT: i32>(self, x: U8x64) -> U8x64 {
882        let x_lo = Extend::<u8>::extend_low(self, x);
883        let x_hi = Extend::<u8>::extend_high(self, x);
884
885        let u16_ops = self.u16();
886        let (y_lo, y_hi) = (
887            u16_ops.shift_left::<SHIFT>(x_lo),
888            u16_ops.shift_left::<SHIFT>(x_hi),
889        );
890
891        self.narrow_truncate(y_lo, y_hi)
892    }
893
894    #[inline]
895    fn shift_right<const SHIFT: i32>(self, x: U8x64) -> U8x64 {
896        let x_lo = Extend::<u8>::extend_low(self, x);
897        let x_hi = Extend::<u8>::extend_high(self, x);
898
899        let u16_ops = self.u16();
900        let (y_lo, y_hi) = (
901            u16_ops.shift_right::<SHIFT>(x_lo),
902            u16_ops.shift_right::<SHIFT>(x_hi),
903        );
904
905        self.narrow_truncate(y_lo, y_hi)
906    }
907}
908
909impl Interleave<u8> for Avx512Isa {
910    #[inline]
911    fn interleave_low(self, a: U8x64, b: U8x64) -> U8x64 {
912        unsafe {
913            // AB{N} = Interleaved Nth 64-bit block.
914            let lo = _mm512_unpacklo_epi8(a.0, b.0); // AB0 AB2 AB4 AB6
915            let hi = _mm512_unpackhi_epi8(a.0, b.0); // AB1 AB3 AB5 AB7
916            let idx = _mm512_setr_epi32(0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20, 21, 22, 23);
917            _mm512_permutex2var_epi32(lo, idx, hi) // AB0 AB1 AB2 AB3
918        }
919        .into()
920    }
921
922    #[inline]
923    fn interleave_high(self, a: U8x64, b: U8x64) -> U8x64 {
924        unsafe {
925            // AB{N} = Interleaved Nth 64-bit block.
926            let lo = _mm512_unpacklo_epi8(a.0, b.0); // AB0 AB2 AB4 AB6
927            let hi = _mm512_unpackhi_epi8(a.0, b.0); // AB1 AB3 AB5 AB7
928            let idx =
929                _mm512_setr_epi32(8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15, 28, 29, 30, 31);
930            _mm512_permutex2var_epi32(lo, idx, hi) // AB4 AB5 AB6 AB7
931        }
932        .into()
933    }
934}
935
936impl Narrow<I16x32> for Avx512Isa {
937    type Output = I8x64;
938
939    #[inline]
940    fn narrow_truncate(self, a: I16x32, b: I16x32) -> I8x64 {
941        let y = unsafe {
942            let lo_i8 = _mm512_cvtepi16_epi8(a.0);
943            let hi_i8 = _mm512_cvtepi16_epi8(b.0);
944            _mm512_inserti64x4(_mm512_castsi256_si512(lo_i8), hi_i8, 1)
945        };
946        I8x64(y)
947    }
948}
949
950impl Narrow<U16x32> for Avx512Isa {
951    type Output = U8x64;
952
953    #[inline]
954    fn narrow_truncate(self, a: U16x32, b: U16x32) -> U8x64 {
955        let y = unsafe {
956            let lo_u8 = _mm512_cvtepi16_epi8(a.0);
957            let hi_u8 = _mm512_cvtepi16_epi8(b.0);
958            _mm512_inserti64x4(_mm512_castsi256_si512(lo_u8), hi_u8, 1)
959        };
960        U8x64(y)
961    }
962}
963
964unsafe impl BitOps<u16> for Avx512Isa {
965    simd_ops_common!(U16x32, __mmask32);
966    simd_int_ops_common!(U16x32);
967
968    #[inline]
969    fn splat(self, x: u16) -> U16x32 {
970        unsafe { _mm512_set1_epi16(x as i16) }.into()
971    }
972
973    #[inline]
974    unsafe fn load_ptr(self, ptr: *const u16) -> U16x32 {
975        unsafe { _mm512_loadu_si512(ptr as *const __m512i) }.into()
976    }
977
978    #[inline]
979    fn select(self, x: U16x32, y: U16x32, mask: <U16x32 as Simd>::Mask) -> U16x32 {
980        unsafe { _mm512_mask_blend_epi16(mask, y.0, x.0) }.into()
981    }
982
983    #[inline]
984    unsafe fn store_ptr(self, x: U16x32, ptr: *mut u16) {
985        unsafe { _mm512_storeu_si512(ptr as *mut __m512i, x.0) }
986    }
987
988    #[inline]
989    unsafe fn load_ptr_mask(self, ptr: *const u16, mask: __mmask32) -> U16x32 {
990        unsafe { _mm512_mask_loadu_epi16(_mm512_set1_epi16(0), mask, ptr as *const i16) }.into()
991    }
992
993    #[inline]
994    unsafe fn store_ptr_mask(self, x: U16x32, ptr: *mut u16, mask: __mmask32) {
995        unsafe { _mm512_mask_storeu_epi16(ptr as *mut i16, mask, x.0) }
996    }
997}
998
999unsafe impl NumOps<u16> for Avx512Isa {
1000    #[inline]
1001    fn add(self, x: U16x32, y: U16x32) -> U16x32 {
1002        unsafe { _mm512_add_epi16(x.0, y.0) }.into()
1003    }
1004
1005    #[inline]
1006    fn sub(self, x: U16x32, y: U16x32) -> U16x32 {
1007        unsafe { _mm512_sub_epi16(x.0, y.0) }.into()
1008    }
1009
1010    #[inline]
1011    fn mul(self, x: U16x32, y: U16x32) -> U16x32 {
1012        unsafe { _mm512_mullo_epi16(x.0, y.0) }.into()
1013    }
1014
1015    #[inline]
1016    fn eq(self, x: U16x32, y: U16x32) -> __mmask32 {
1017        unsafe { _mm512_cmp_epu16_mask(x.0, y.0, _MM_CMPINT_EQ) }
1018    }
1019
1020    #[inline]
1021    fn ge(self, x: U16x32, y: U16x32) -> __mmask32 {
1022        unsafe { _mm512_cmp_epu16_mask(x.0, y.0, _MM_CMPINT_NLT) }
1023    }
1024
1025    #[inline]
1026    fn gt(self, x: U16x32, y: U16x32) -> __mmask32 {
1027        unsafe { _mm512_cmp_epu16_mask(x.0, y.0, _MM_CMPINT_NLE) }
1028    }
1029}
1030
1031impl IntOps<u16> for Avx512Isa {
1032    #[inline]
1033    fn shift_left<const SHIFT: i32>(self, x: U16x32) -> U16x32 {
1034        let count: I16x32 = self.splat(SHIFT as i16);
1035        unsafe { _mm512_sllv_epi16(x.0, count.0) }.into()
1036    }
1037
1038    #[inline]
1039    fn shift_right<const SHIFT: i32>(self, x: U16x32) -> U16x32 {
1040        let count: I16x32 = self.splat(SHIFT as i16);
1041        unsafe { _mm512_srlv_epi16(x.0, count.0) }.into()
1042    }
1043}
1044
1045unsafe impl BitOps<f16> for Avx512Isa {
1046    simd_ops_common!(F16x32, __mmask32);
1047    simd_int_ops_common!(F16x32);
1048
1049    #[inline]
1050    fn splat(self, x: f16) -> F16x32 {
1051        unsafe { _mm512_set1_epi16(x.to_bits() as i16) }.into()
1052    }
1053
1054    #[inline]
1055    unsafe fn load_ptr(self, ptr: *const f16) -> F16x32 {
1056        unsafe { _mm512_loadu_si512(ptr as *const __m512i) }.into()
1057    }
1058
1059    #[inline]
1060    fn select(self, x: F16x32, y: F16x32, mask: <F16x32 as Simd>::Mask) -> F16x32 {
1061        unsafe { _mm512_mask_blend_epi16(mask, y.0, x.0) }.into()
1062    }
1063
1064    #[inline]
1065    unsafe fn store_ptr(self, x: F16x32, ptr: *mut f16) {
1066        unsafe { _mm512_storeu_si512(ptr as *mut __m512i, x.0) }
1067    }
1068
1069    #[inline]
1070    unsafe fn load_ptr_mask(self, ptr: *const f16, mask: __mmask32) -> F16x32 {
1071        unsafe { _mm512_mask_loadu_epi16(_mm512_set1_epi16(0), mask, ptr as *const i16) }.into()
1072    }
1073
1074    #[inline]
1075    unsafe fn store_ptr_mask(self, x: F16x32, ptr: *mut f16, mask: __mmask32) {
1076        unsafe { _mm512_mask_storeu_epi16(ptr as *mut i16, mask, x.0) }
1077    }
1078}
1079
1080impl Extend<f16> for Avx512Isa {
1081    type Output = F32x16;
1082
1083    #[inline]
1084    fn extend_low(self, x: F16x32) -> F32x16 {
1085        unsafe { _mm512_cvtph_ps(_mm512_castsi512_si256(x.0)).into() }
1086    }
1087
1088    #[inline]
1089    fn extend_high(self, x: F16x32) -> F32x16 {
1090        unsafe { _mm512_cvtph_ps(_mm512_extracti64x4_epi64(x.0, 1)).into() }
1091    }
1092}
1093
1094impl NarrowSaturate<f32, f16> for Avx512Isa {
1095    type Output = F16x32;
1096
1097    #[inline]
1098    fn narrow_saturate(self, low: F32x16, high: F32x16) -> F16x32 {
1099        unsafe {
1100            let low_i256 = _mm512_cvtps_ph::<_MM_FROUND_TO_NEAREST_INT>(low.0);
1101            let high_i256 = _mm512_cvtps_ph::<_MM_FROUND_TO_NEAREST_INT>(high.0);
1102            _mm512_inserti64x4(_mm512_castsi256_si512(low_i256), high_i256, 1).into()
1103        }
1104    }
1105}
1106
1107macro_rules! impl_mask {
1108    ($mask:ty) => {
1109        impl Mask for $mask {
1110            type Array = [bool; size_of::<$mask>() * 8];
1111
1112            #[inline]
1113            fn to_array(self) -> Self::Array {
1114                std::array::from_fn(|i| self & (1 << i) != 0)
1115            }
1116        }
1117
1118        unsafe impl MaskOps<$mask> for Avx512Isa {
1119            #[inline]
1120            fn and(self, x: $mask, y: $mask) -> $mask {
1121                x & y
1122            }
1123
1124            #[inline]
1125            fn any(self, x: $mask) -> bool {
1126                x != 0
1127            }
1128
1129            #[inline]
1130            fn all(self, x: $mask) -> bool {
1131                x == !0
1132            }
1133        }
1134    };
1135}
1136
1137impl_mask!(__mmask16);
1138impl_mask!(__mmask32);
1139impl_mask!(__mmask64);