1use std::array;
2use std::mem::transmute;
3
4use crate::f16;
5use crate::ops::{
6 BitOps, Concat, Extend, FloatOps, IntOps, Interleave, MaskOps, NarrowSaturate, NumOps,
7 SignedIntOps, ToFloat,
8};
9use crate::{Isa, Mask, Simd};
10
11const LEN_X32: usize = 4;
13
14macro_rules! simd_type {
15 ($simd:ident, $elem:ty, $len:expr) => {
16 #[repr(align(16))]
17 #[derive(Copy, Clone, Debug)]
18 pub struct $simd([$elem; $len]);
19
20 impl $simd {
21 #[allow(unused)]
23 #[inline]
24 fn map<U, F: Fn($elem) -> U, R>(self, op: F) -> R
25 where
26 R: From<[U; $len]>,
27 {
28 self.0.map(op).into()
29 }
30
31 #[allow(unused)]
33 #[inline]
34 fn map_with<U, F: Fn($elem, $elem) -> U, R>(self, y: Self, op: F) -> R
35 where
36 R: From<[U; $len]>,
37 {
38 array::from_fn(|i| op(self.0[i], y.0[i])).into()
39 }
40 }
41
42 impl From<[$elem; $len]> for $simd {
43 fn from(val: [$elem; $len]) -> $simd {
44 $simd(val)
45 }
46 }
47 };
48}
49
50simd_type!(F32x4, f32, LEN_X32);
52simd_type!(I32x4, i32, LEN_X32);
53simd_type!(I16x8, i16, LEN_X32 * 2);
54simd_type!(I8x16, i8, LEN_X32 * 4);
55simd_type!(U8x16, u8, LEN_X32 * 4);
56simd_type!(U16x8, u16, LEN_X32 * 2);
57simd_type!(U32x4, u32, LEN_X32);
58simd_type!(F16x8, f16, LEN_X32 * 2);
59
60simd_type!(M32, i32, LEN_X32);
62simd_type!(M16, i16, LEN_X32 * 2);
63simd_type!(M8, i8, LEN_X32 * 4);
64
65#[derive(Copy, Clone)]
66pub struct GenericIsa {
67 _private: (),
68}
69
70impl GenericIsa {
71 pub fn new() -> Self {
72 GenericIsa { _private: () }
73 }
74}
75
76impl Default for GenericIsa {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82unsafe impl Isa for GenericIsa {
84 type M32 = M32;
85 type M16 = M16;
86 type M8 = M8;
87 type F32 = F32x4;
88 type I32 = I32x4;
89 type I16 = I16x8;
90 type I8 = I8x16;
91 type U8 = U8x16;
92 type U16 = U16x8;
93 type U32 = U32x4;
94 type F16 = F16x8;
95 type Bits = I32x4;
96
97 fn f32(
98 self,
99 ) -> impl FloatOps<f32, Simd = Self::F32, Int = Self::I32>
100 + NarrowSaturate<f32, f16, Output = Self::F16> {
101 self
102 }
103
104 fn f16(self) -> impl Extend<f16, Output = Self::F32, Simd = Self::F16> {
105 self
106 }
107
108 fn i32(
109 self,
110 ) -> impl SignedIntOps<i32, Simd = Self::I32>
111 + NarrowSaturate<i32, i16, Output = Self::I16>
112 + Concat<i32>
113 + ToFloat<i32, Output = Self::F32> {
114 self
115 }
116
117 fn i16(
118 self,
119 ) -> impl SignedIntOps<i16, Simd = Self::I16>
120 + NarrowSaturate<i16, u8, Output = Self::U8>
121 + Extend<i16, Output = Self::I32>
122 + Interleave<i16> {
123 self
124 }
125
126 fn i8(
127 self,
128 ) -> impl SignedIntOps<i8, Simd = Self::I8> + Extend<i8, Output = Self::I16> + Interleave<i8>
129 {
130 self
131 }
132
133 fn u8(
134 self,
135 ) -> impl IntOps<u8, Simd = Self::U8> + Extend<u8, Output = Self::U16> + Interleave<u8> {
136 self
137 }
138
139 fn u16(self) -> impl IntOps<u16, Simd = Self::U16> {
140 self
141 }
142
143 fn m32(self) -> impl MaskOps<Self::M32> {
144 self
145 }
146
147 fn m16(self) -> impl MaskOps<Self::M16> {
148 self
149 }
150
151 fn m8(self) -> impl MaskOps<Self::M8> {
152 self
153 }
154}
155
156macro_rules! bit_ops_common {
157 ($simd:ident, $elem:ty, $len:expr, $mask:ident) => {
158 #[inline]
159 fn len(self) -> usize {
160 $len
161 }
162
163 #[inline]
164 fn first_n_mask(self, n: usize) -> $mask {
165 let mask = std::array::from_fn(|i| if i < n { !0 } else { 0 });
166 $mask(mask)
167 }
168
169 #[inline]
170 unsafe fn load_ptr_mask(
171 self,
172 ptr: *const <$simd as Simd>::Elem,
173 mask: <$simd as Simd>::Mask,
174 ) -> $simd {
175 let mask_array = mask.0;
176 let mut vec = <Self as BitOps<$elem>>::zero(self).0;
177 for i in 0..mask_array.len() {
178 if mask_array[i] != 0 {
179 vec[i] = *ptr.add(i);
180 }
181 }
182 self.load_ptr(vec.as_ref().as_ptr())
183 }
184
185 #[inline]
186 unsafe fn store_ptr_mask(
187 self,
188 x: $simd,
189 ptr: *mut <$simd as Simd>::Elem,
190 mask: <$simd as Simd>::Mask,
191 ) {
192 let mask_array = mask.0;
193 let x_array = x.0;
194 for i in 0..<Self as BitOps<$elem>>::len(self) {
195 if mask_array[i] != 0 {
196 *ptr.add(i) = x_array[i];
197 }
198 }
199 }
200
201 #[inline]
202 fn splat(self, x: $elem) -> $simd {
203 $simd([x; $len])
204 }
205
206 #[inline]
207 unsafe fn load_ptr(self, ptr: *const $elem) -> $simd {
208 let xs = array::from_fn(|i| *ptr.add(i));
209 $simd(xs)
210 }
211
212 #[inline]
213 fn select(self, x: $simd, y: $simd, mask: <$simd as Simd>::Mask) -> $simd {
214 let xs = array::from_fn(|i| if mask.0[i] != 0 { x.0[i] } else { y.0[i] });
215 $simd(xs)
216 }
217
218 #[inline]
219 unsafe fn store_ptr(self, x: $simd, ptr: *mut $elem) {
220 for i in 0..$len {
221 *ptr.add(i) = x.0[i];
222 }
223 }
224 };
225}
226
227macro_rules! num_ops_common {
228 ($simd:ident, $mask:ident) => {
229 #[inline]
230 fn add(self, x: $simd, y: $simd) -> $simd {
231 x.map_with(y, |x, y| x + y)
232 }
233
234 #[inline]
235 fn sub(self, x: $simd, y: $simd) -> $simd {
236 x.map_with(y, |x, y| x - y)
237 }
238
239 #[inline]
240 fn mul(self, x: $simd, y: $simd) -> $simd {
241 x.map_with(y, |x, y| x * y)
242 }
243
244 #[inline]
245 fn mul_add(self, a: $simd, b: $simd, c: $simd) -> $simd {
246 let xs = array::from_fn(|i| a.0[i] * b.0[i] + c.0[i]);
247 $simd(xs)
248 }
249
250 #[inline]
251 fn eq(self, x: $simd, y: $simd) -> $mask {
252 x.map_with(y, |x, y| if x == y { !0 } else { 0 })
253 }
254
255 #[inline]
256 fn ge(self, x: $simd, y: $simd) -> $mask {
257 x.map_with(y, |x, y| if x >= y { !0 } else { 0 })
258 }
259
260 #[inline]
261 fn gt(self, x: $simd, y: $simd) -> $mask {
262 x.map_with(y, |x, y| if x > y { !0 } else { 0 })
263 }
264
265 #[inline]
266 fn min(self, x: $simd, y: $simd) -> $simd {
267 x.map_with(y, |x, y| x.min(y))
268 }
269
270 #[inline]
271 fn max(self, x: $simd, y: $simd) -> $simd {
272 x.map_with(y, |x, y| x.max(y))
273 }
274 };
275}
276
277macro_rules! simd_int_ops_common {
278 ($simd:ty) => {
279 #[inline]
280 fn and(self, x: $simd, y: $simd) -> $simd {
281 x.map_with(y, |x, y| x & y)
282 }
283
284 #[inline]
285 fn or(self, x: $simd, y: $simd) -> $simd {
286 x.map_with(y, |x, y| x | y)
287 }
288
289 #[inline]
290 fn not(self, x: $simd) -> $simd {
291 x.map(|x| !x)
292 }
293
294 #[inline]
295 fn xor(self, x: $simd, y: $simd) -> $simd {
296 x.map_with(y, |x, y| x ^ y)
297 }
298 };
299}
300
301unsafe impl BitOps<f32> for GenericIsa {
302 type Simd = F32x4;
303
304 bit_ops_common!(F32x4, f32, 4, M32);
305
306 #[inline]
307 fn and(self, x: F32x4, y: F32x4) -> F32x4 {
308 x.map_with(y, |x, y| f32::from_bits(x.to_bits() & y.to_bits()))
309 }
310
311 #[inline]
312 fn not(self, x: F32x4) -> F32x4 {
313 x.map(|x| f32::from_bits(!x.to_bits()))
314 }
315
316 #[inline]
317 fn or(self, x: F32x4, y: F32x4) -> F32x4 {
318 x.map_with(y, |x, y| f32::from_bits(x.to_bits() | y.to_bits()))
319 }
320
321 #[inline]
322 fn xor(self, x: F32x4, y: F32x4) -> F32x4 {
323 x.map_with(y, |x, y| f32::from_bits(x.to_bits() ^ y.to_bits()))
324 }
325}
326
327unsafe impl NumOps<f32> for GenericIsa {
328 num_ops_common!(F32x4, M32);
329}
330
331impl FloatOps<f32> for GenericIsa {
332 type Int = <Self as Isa>::I32;
333
334 #[inline]
335 fn div(self, x: F32x4, y: F32x4) -> F32x4 {
336 x.map_with(y, |x, y| x / y)
337 }
338
339 #[inline]
340 fn round_ties_even(self, x: F32x4) -> F32x4 {
341 x.map(|x| x.round_ties_even())
342 }
343
344 #[inline]
345 fn neg(self, x: F32x4) -> F32x4 {
346 x.map(|x| -x)
347 }
348
349 #[inline]
350 fn abs(self, x: F32x4) -> F32x4 {
351 x.map(|x| x.abs())
352 }
353
354 #[inline]
355 fn to_int_trunc(self, x: F32x4) -> Self::Int {
356 x.map(|x| x as i32)
357 }
358
359 #[inline]
360 fn to_int_round(self, x: F32x4) -> Self::Int {
361 x.map(|x| x.round_ties_even() as i32)
362 }
363}
364
365macro_rules! impl_simd_int_ops {
366 ($simd:ident, $elem:ty, $len:expr, $mask:ident) => {
367 unsafe impl BitOps<$elem> for GenericIsa {
368 type Simd = $simd;
369
370 bit_ops_common!($simd, $elem, $len, $mask);
371 simd_int_ops_common!($simd);
372 }
373
374 unsafe impl NumOps<$elem> for GenericIsa {
375 num_ops_common!($simd, $mask);
376 }
377
378 impl IntOps<$elem> for GenericIsa {
379 #[inline]
380 fn shift_left<const SHIFT: i32>(self, x: $simd) -> $simd {
381 x.map(|x| x << SHIFT)
382 }
383
384 #[inline]
385 fn shift_right<const SHIFT: i32>(self, x: $simd) -> $simd {
386 x.map(|x| x >> SHIFT)
387 }
388 }
389 };
390}
391
392macro_rules! impl_simd_signed_int_ops {
393 ($simd:ident, $elem:ty, $len:expr, $mask:ident) => {
394 impl_simd_int_ops!($simd, $elem, $len, $mask);
395
396 impl SignedIntOps<$elem> for GenericIsa {
397 #[inline]
398 fn neg(self, x: $simd) -> $simd {
399 x.map(|x| -x)
400 }
401 }
402 };
403}
404
405impl_simd_signed_int_ops!(I32x4, i32, 4, M32);
406impl_simd_signed_int_ops!(I16x8, i16, 8, M16);
407impl_simd_signed_int_ops!(I8x16, i8, 16, M8);
408
409macro_rules! impl_extend {
410 ($src:ty, $elem:ty, $dst:ty) => {
411 impl Extend<$elem> for GenericIsa {
412 type Output = $dst;
413
414 fn extend_low(self, x: $src) -> $dst {
415 let extended = x.0.map(|x| x as <$dst as Simd>::Elem);
416 let low = array::from_fn(|i| extended[i]);
417 low.into()
418 }
419
420 fn extend_high(self, x: $src) -> $dst {
421 let extended = x.0.map(|x| x as <$dst as Simd>::Elem);
422 let high = array::from_fn(|i| extended[i + extended.len() / 2]);
423 high.into()
424 }
425 }
426 };
427}
428impl_extend!(I8x16, i8, I16x8);
429impl_extend!(I16x8, i16, I32x4);
430impl_extend!(U8x16, u8, U16x8);
431
432macro_rules! impl_concat {
433 ($elem:ty, $simd:ty) => {
434 impl Concat<$elem> for GenericIsa {
435 fn concat_low(self, a: $simd, b: $simd) -> $simd {
436 let half_len = a.0.len() / 2;
437 array::from_fn(|i| {
438 if i < half_len {
439 a.0[i]
440 } else {
441 b.0[i - half_len]
442 }
443 })
444 .into()
445 }
446
447 fn concat_high(self, a: $simd, b: $simd) -> $simd {
448 let half_len = a.0.len() / 2;
449 array::from_fn(|i| {
450 if i < half_len {
451 a.0[half_len + i]
452 } else {
453 b.0[i]
454 }
455 })
456 .into()
457 }
458 }
459 };
460}
461
462impl_concat!(i32, I32x4);
463
464macro_rules! impl_interleave {
465 ($elem:ty, $simd:ty) => {
466 impl Interleave<$elem> for GenericIsa {
467 fn interleave_low(self, a: $simd, b: $simd) -> $simd {
468 array::from_fn(|i| if i % 2 == 0 { a.0[i / 2] } else { b.0[i / 2] }).into()
469 }
470
471 fn interleave_high(self, a: $simd, b: $simd) -> $simd {
472 let start = a.0.len() / 2;
473 array::from_fn(|i| {
474 if i % 2 == 0 {
475 a.0[start + i / 2]
476 } else {
477 b.0[start + i / 2]
478 }
479 })
480 .into()
481 }
482 }
483 };
484}
485impl_interleave!(i8, I8x16);
486impl_interleave!(i16, I16x8);
487impl_interleave!(u8, U8x16);
488
489impl_simd_int_ops!(U8x16, u8, 16, M8);
490impl_simd_int_ops!(U16x8, u16, 8, M16);
491
492impl ToFloat<i32> for GenericIsa {
493 type Output = F32x4;
494
495 fn to_float(self, x: I32x4) -> Self::Output {
496 F32x4(x.0.map(|x| x as f32))
497 }
498}
499
500trait NarrowSaturateElem<T> {
501 fn narrow_saturate(self) -> T;
502}
503
504impl NarrowSaturateElem<i16> for i32 {
505 fn narrow_saturate(self) -> i16 {
506 self.clamp(i16::MIN as i32, i16::MAX as i32) as i16
507 }
508}
509
510impl NarrowSaturateElem<u8> for i16 {
511 fn narrow_saturate(self) -> u8 {
512 self.clamp(u8::MIN as i16, u8::MAX as i16) as u8
513 }
514}
515
516macro_rules! impl_narrow {
517 ($from:ident, $from_elem:ty, $to:ident, $to_elem:ty) => {
518 impl NarrowSaturate<$from_elem, $to_elem> for GenericIsa {
519 type Output = $to;
520
521 fn narrow_saturate(self, lo: $from, hi: $from) -> $to {
522 let mid = lo.0.len() / 2;
523 let xs = array::from_fn(|i| {
524 let x = if i < mid { lo.0[i] } else { hi.0[i] };
525 x.narrow_saturate()
526 });
527 $to(xs)
528 }
529 }
530 };
531}
532impl_narrow!(I32x4, i32, I16x8, i16);
533impl_narrow!(I16x8, i16, U8x16, u8);
534
535unsafe impl BitOps<f16> for GenericIsa {
536 type Simd = F16x8;
537
538 bit_ops_common!(F16x8, f16, 8, M16);
539
540 #[inline]
541 fn and(self, x: F16x8, y: F16x8) -> F16x8 {
542 x.map_with(y, |x, y| f16::from_bits(x.to_bits() & y.to_bits()))
543 }
544
545 #[inline]
546 fn not(self, x: F16x8) -> F16x8 {
547 x.map(|x| f16::from_bits(!x.to_bits()))
548 }
549
550 #[inline]
551 fn or(self, x: F16x8, y: F16x8) -> F16x8 {
552 x.map_with(y, |x, y| f16::from_bits(x.to_bits() | y.to_bits()))
553 }
554
555 #[inline]
556 fn xor(self, x: F16x8, y: F16x8) -> F16x8 {
557 x.map_with(y, |x, y| f16::from_bits(x.to_bits() ^ y.to_bits()))
558 }
559}
560
561impl Extend<f16> for GenericIsa {
562 type Output = F32x4;
563
564 fn extend_low(self, x: F16x8) -> F32x4 {
565 let vals = x.0.map(|v| v.to_f32());
566 let low = array::from_fn(|i| vals[i]);
567 low.into()
568 }
569
570 fn extend_high(self, x: F16x8) -> F32x4 {
571 let vals = x.0.map(|v| v.to_f32());
572 let mid = vals.len() / 2;
573 let high = array::from_fn(|i| vals[i + mid]);
574 high.into()
575 }
576}
577
578impl NarrowSaturate<f32, f16> for GenericIsa {
579 type Output = F16x8;
580
581 fn narrow_saturate(self, low: F32x4, high: F32x4) -> F16x8 {
582 let mid = low.0.len();
583 let xs = array::from_fn(|i| {
584 let v = if i < mid { low.0[i] } else { high.0[i - mid] };
585 f16::from_f32(v)
586 });
587 F16x8(xs)
588 }
589}
590
591macro_rules! impl_mask {
592 ($mask:ident, $len:expr) => {
593 impl Mask for $mask {
594 type Array = [bool; $len];
595
596 #[inline]
597 fn to_array(self) -> Self::Array {
598 let array = self.0;
599 array::from_fn(|i| array[i] != 0)
600 }
601 }
602
603 unsafe impl MaskOps<$mask> for GenericIsa {
604 #[inline]
605 fn and(self, x: $mask, y: $mask) -> $mask {
606 let xs = array::from_fn(|i| x.0[i] & y.0[i]);
607 $mask(xs)
608 }
609
610 #[inline]
611 fn any(self, x: $mask) -> bool {
612 x.0.iter().any(|x| *x != 0)
613 }
614
615 #[inline]
616 fn all(self, x: $mask) -> bool {
617 x.0.iter().all(|x| *x != 0)
618 }
619 }
620 };
621}
622
623impl_mask!(M32, LEN_X32);
624impl_mask!(M16, LEN_X32 * 2);
625impl_mask!(M8, LEN_X32 * 4);
626
627macro_rules! impl_simd {
628 ($simd:ty, $elem:ty, $mask:ty, $len:expr) => {
629 impl Simd for $simd {
630 type Mask = $mask;
631 type Elem = $elem;
632 type Array = [$elem; $len];
633 type Isa = GenericIsa;
634
635 #[inline]
636 fn to_bits(self) -> <Self::Isa as Isa>::Bits {
637 #[allow(clippy::useless_transmute)]
638 I32x4(unsafe { transmute::<[$elem; $len], [i32; LEN_X32]>(self.0) })
639 }
640
641 #[inline]
642 fn from_bits(bits: <Self::Isa as Isa>::Bits) -> Self {
643 #[allow(clippy::useless_transmute)]
644 Self(unsafe { transmute::<[i32; LEN_X32], [$elem; $len]>(bits.0) })
645 }
646
647 #[inline]
648 fn to_array(self) -> Self::Array {
649 self.0
650 }
651 }
652 };
653}
654
655impl_simd!(F32x4, f32, M32, 4);
656impl_simd!(F16x8, f16, M16, 8);
657impl_simd!(I32x4, i32, M32, 4);
658impl_simd!(I16x8, i16, M16, 8);
659impl_simd!(I8x16, i8, M8, 16);
660impl_simd!(U8x16, u8, M8, 16);
661impl_simd!(U16x8, u16, M16, 8);
662impl_simd!(U32x4, u32, M32, 4);