]> git.lizzy.rs Git - rust.git/blob - library/portable-simd/crates/core_simd/tests/ops_macros.rs
Auto merge of #89167 - workingjubilee:use-simd, r=MarkSimulacrum
[rust.git] / library / portable-simd / crates / core_simd / tests / ops_macros.rs
1 /// Implements a test on a unary operation using proptest.
2 ///
3 /// Compares the vector operation to the equivalent scalar operation.
4 #[macro_export]
5 macro_rules! impl_unary_op_test {
6     { $scalar:ty, $trait:ident :: $fn:ident, $scalar_fn:expr } => {
7         test_helpers::test_lanes! {
8             fn $fn<const LANES: usize>() {
9                 test_helpers::test_unary_elementwise(
10                     &<core_simd::Simd<$scalar, LANES> as core::ops::$trait>::$fn,
11                     &$scalar_fn,
12                     &|_| true,
13                 );
14             }
15         }
16     };
17     { $scalar:ty, $trait:ident :: $fn:ident } => {
18         impl_unary_op_test! { $scalar, $trait::$fn, <$scalar as core::ops::$trait>::$fn }
19     };
20 }
21
22 /// Implements a test on a binary operation using proptest.
23 ///
24 /// Compares the vector operation to the equivalent scalar operation.
25 #[macro_export]
26 macro_rules! impl_binary_op_test {
27     { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr } => {
28         mod $fn {
29             use super::*;
30             use core_simd::Simd;
31
32             test_helpers::test_lanes! {
33                 fn normal<const LANES: usize>() {
34                     test_helpers::test_binary_elementwise(
35                         &<Simd<$scalar, LANES> as core::ops::$trait>::$fn,
36                         &$scalar_fn,
37                         &|_, _| true,
38                     );
39                 }
40
41                 fn scalar_rhs<const LANES: usize>() {
42                     test_helpers::test_binary_scalar_rhs_elementwise(
43                         &<Simd<$scalar, LANES> as core::ops::$trait<$scalar>>::$fn,
44                         &$scalar_fn,
45                         &|_, _| true,
46                     );
47                 }
48
49                 fn scalar_lhs<const LANES: usize>() {
50                     test_helpers::test_binary_scalar_lhs_elementwise(
51                         &<$scalar as core::ops::$trait<Simd<$scalar, LANES>>>::$fn,
52                         &$scalar_fn,
53                         &|_, _| true,
54                     );
55                 }
56
57                 fn assign<const LANES: usize>() {
58                     test_helpers::test_binary_elementwise(
59                         &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
60                         &$scalar_fn,
61                         &|_, _| true,
62                     );
63                 }
64
65                 fn assign_scalar_rhs<const LANES: usize>() {
66                     test_helpers::test_binary_scalar_rhs_elementwise(
67                         &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
68                         &$scalar_fn,
69                         &|_, _| true,
70                     );
71                 }
72             }
73         }
74     };
75     { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident } => {
76         impl_binary_op_test! { $scalar, $trait::$fn, $trait_assign::$fn_assign, <$scalar as core::ops::$trait>::$fn }
77     };
78 }
79
80 /// Implements a test on a binary operation using proptest.
81 ///
82 /// Like `impl_binary_op_test`, but allows providing a function for rejecting particular inputs
83 /// (like the `proptest_assume` macro).
84 ///
85 /// Compares the vector operation to the equivalent scalar operation.
86 #[macro_export]
87 macro_rules! impl_binary_checked_op_test {
88     { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr, $check_fn:expr } => {
89         mod $fn {
90             use super::*;
91             use core_simd::Simd;
92
93             test_helpers::test_lanes! {
94                 fn normal<const LANES: usize>() {
95                     test_helpers::test_binary_elementwise(
96                         &<Simd<$scalar, LANES> as core::ops::$trait>::$fn,
97                         &$scalar_fn,
98                         &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
99                     );
100                 }
101
102                 fn scalar_rhs<const LANES: usize>() {
103                     test_helpers::test_binary_scalar_rhs_elementwise(
104                         &<Simd<$scalar, LANES> as core::ops::$trait<$scalar>>::$fn,
105                         &$scalar_fn,
106                         &|x, y| x.iter().all(|x| $check_fn(*x, y)),
107                     );
108                 }
109
110                 fn scalar_lhs<const LANES: usize>() {
111                     test_helpers::test_binary_scalar_lhs_elementwise(
112                         &<$scalar as core::ops::$trait<Simd<$scalar, LANES>>>::$fn,
113                         &$scalar_fn,
114                         &|x, y| y.iter().all(|y| $check_fn(x, *y)),
115                     );
116                 }
117
118                 fn assign<const LANES: usize>() {
119                     test_helpers::test_binary_elementwise(
120                         &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
121                         &$scalar_fn,
122                         &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
123                     )
124                 }
125
126                 fn assign_scalar_rhs<const LANES: usize>() {
127                     test_helpers::test_binary_scalar_rhs_elementwise(
128                         &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
129                         &$scalar_fn,
130                         &|x, y| x.iter().all(|x| $check_fn(*x, y)),
131                     )
132                 }
133             }
134         }
135     };
136     { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $check_fn:expr } => {
137         impl_binary_checked_op_test! { $scalar, $trait::$fn, $trait_assign::$fn_assign, <$scalar as core::ops::$trait>::$fn, $check_fn }
138     };
139 }
140
141 #[macro_export]
142 macro_rules! impl_common_integer_tests {
143     { $vector:ident, $scalar:ident } => {
144         test_helpers::test_lanes! {
145             fn horizontal_sum<const LANES: usize>() {
146                 test_helpers::test_1(&|x| {
147                     test_helpers::prop_assert_biteq! (
148                         $vector::<LANES>::from_array(x).horizontal_sum(),
149                         x.iter().copied().fold(0 as $scalar, $scalar::wrapping_add),
150                     );
151                     Ok(())
152                 });
153             }
154
155             fn horizontal_product<const LANES: usize>() {
156                 test_helpers::test_1(&|x| {
157                     test_helpers::prop_assert_biteq! (
158                         $vector::<LANES>::from_array(x).horizontal_product(),
159                         x.iter().copied().fold(1 as $scalar, $scalar::wrapping_mul),
160                     );
161                     Ok(())
162                 });
163             }
164
165             fn horizontal_and<const LANES: usize>() {
166                 test_helpers::test_1(&|x| {
167                     test_helpers::prop_assert_biteq! (
168                         $vector::<LANES>::from_array(x).horizontal_and(),
169                         x.iter().copied().fold(-1i8 as $scalar, <$scalar as core::ops::BitAnd>::bitand),
170                     );
171                     Ok(())
172                 });
173             }
174
175             fn horizontal_or<const LANES: usize>() {
176                 test_helpers::test_1(&|x| {
177                     test_helpers::prop_assert_biteq! (
178                         $vector::<LANES>::from_array(x).horizontal_or(),
179                         x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitOr>::bitor),
180                     );
181                     Ok(())
182                 });
183             }
184
185             fn horizontal_xor<const LANES: usize>() {
186                 test_helpers::test_1(&|x| {
187                     test_helpers::prop_assert_biteq! (
188                         $vector::<LANES>::from_array(x).horizontal_xor(),
189                         x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitXor>::bitxor),
190                     );
191                     Ok(())
192                 });
193             }
194
195             fn horizontal_max<const LANES: usize>() {
196                 test_helpers::test_1(&|x| {
197                     test_helpers::prop_assert_biteq! (
198                         $vector::<LANES>::from_array(x).horizontal_max(),
199                         x.iter().copied().max().unwrap(),
200                     );
201                     Ok(())
202                 });
203             }
204
205             fn horizontal_min<const LANES: usize>() {
206                 test_helpers::test_1(&|x| {
207                     test_helpers::prop_assert_biteq! (
208                         $vector::<LANES>::from_array(x).horizontal_min(),
209                         x.iter().copied().min().unwrap(),
210                     );
211                     Ok(())
212                 });
213             }
214         }
215     }
216 }
217
218 /// Implement tests for signed integers.
219 #[macro_export]
220 macro_rules! impl_signed_tests {
221     { $scalar:tt } => {
222         mod $scalar {
223             type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
224             type Scalar = $scalar;
225
226             impl_common_integer_tests! { Vector, Scalar }
227
228             test_helpers::test_lanes! {
229                 fn neg<const LANES: usize>() {
230                     test_helpers::test_unary_elementwise(
231                         &<Vector::<LANES> as core::ops::Neg>::neg,
232                         &<Scalar as core::ops::Neg>::neg,
233                         &|x| !x.contains(&Scalar::MIN),
234                     );
235                 }
236
237                 fn is_positive<const LANES: usize>() {
238                     test_helpers::test_unary_mask_elementwise(
239                         &Vector::<LANES>::is_positive,
240                         &Scalar::is_positive,
241                         &|_| true,
242                     );
243                 }
244
245                 fn is_negative<const LANES: usize>() {
246                     test_helpers::test_unary_mask_elementwise(
247                         &Vector::<LANES>::is_negative,
248                         &Scalar::is_negative,
249                         &|_| true,
250                     );
251                 }
252
253                 fn signum<const LANES: usize>() {
254                     test_helpers::test_unary_elementwise(
255                         &Vector::<LANES>::signum,
256                         &Scalar::signum,
257                         &|_| true,
258                     )
259                 }
260
261             }
262
263             test_helpers::test_lanes_panic! {
264                 fn div_min_overflow_panics<const LANES: usize>() {
265                     let a = Vector::<LANES>::splat(Scalar::MIN);
266                     let b = Vector::<LANES>::splat(-1);
267                     let _ = a / b;
268                 }
269
270                 fn div_by_all_zeros_panics<const LANES: usize>() {
271                     let a = Vector::<LANES>::splat(42);
272                     let b = Vector::<LANES>::splat(0);
273                     let _ = a / b;
274                 }
275
276                 fn div_by_one_zero_panics<const LANES: usize>() {
277                     let a = Vector::<LANES>::splat(42);
278                     let mut b = Vector::<LANES>::splat(21);
279                     b[0] = 0 as _;
280                     let _ = a / b;
281                 }
282
283                 fn rem_min_overflow_panic<const LANES: usize>() {
284                     let a = Vector::<LANES>::splat(Scalar::MIN);
285                     let b = Vector::<LANES>::splat(-1);
286                     let _ = a % b;
287                 }
288
289                 fn rem_zero_panic<const LANES: usize>() {
290                     let a = Vector::<LANES>::splat(42);
291                     let b = Vector::<LANES>::splat(0);
292                     let _ = a % b;
293                 }
294             }
295
296             test_helpers::test_lanes! {
297                 fn div_neg_one_no_panic<const LANES: usize>() {
298                     let a = Vector::<LANES>::splat(42);
299                     let b = Vector::<LANES>::splat(-1);
300                     let _ = a / b;
301                 }
302
303                 fn rem_neg_one_no_panic<const LANES: usize>() {
304                     let a = Vector::<LANES>::splat(42);
305                     let b = Vector::<LANES>::splat(-1);
306                     let _ = a % b;
307                 }
308             }
309
310             impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
311             impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
312             impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
313
314             // Exclude Div and Rem panicking cases
315             impl_binary_checked_op_test!(Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
316             impl_binary_checked_op_test!(Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
317
318             impl_unary_op_test!(Scalar, Not::not);
319             impl_binary_op_test!(Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
320             impl_binary_op_test!(Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
321             impl_binary_op_test!(Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
322         }
323     }
324 }
325
326 /// Implement tests for unsigned integers.
327 #[macro_export]
328 macro_rules! impl_unsigned_tests {
329     { $scalar:tt } => {
330         mod $scalar {
331             type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
332             type Scalar = $scalar;
333
334             impl_common_integer_tests! { Vector, Scalar }
335
336             test_helpers::test_lanes_panic! {
337                 fn rem_zero_panic<const LANES: usize>() {
338                     let a = Vector::<LANES>::splat(42);
339                     let b = Vector::<LANES>::splat(0);
340                     let _ = a % b;
341                 }
342             }
343
344             impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
345             impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
346             impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
347
348             // Exclude Div and Rem panicking cases
349             impl_binary_checked_op_test!(Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |_, y| y != 0);
350             impl_binary_checked_op_test!(Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |_, y| y != 0);
351
352             impl_unary_op_test!(Scalar, Not::not);
353             impl_binary_op_test!(Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
354             impl_binary_op_test!(Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
355             impl_binary_op_test!(Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
356         }
357     }
358 }
359
360 /// Implement tests for floating point numbers.
361 #[macro_export]
362 macro_rules! impl_float_tests {
363     { $scalar:tt, $int_scalar:tt } => {
364         mod $scalar {
365             type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
366             type Scalar = $scalar;
367
368             impl_unary_op_test!(Scalar, Neg::neg);
369             impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign);
370             impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign);
371             impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign);
372             impl_binary_op_test!(Scalar, Div::div, DivAssign::div_assign);
373             impl_binary_op_test!(Scalar, Rem::rem, RemAssign::rem_assign);
374
375             test_helpers::test_lanes! {
376                 fn is_sign_positive<const LANES: usize>() {
377                     test_helpers::test_unary_mask_elementwise(
378                         &Vector::<LANES>::is_sign_positive,
379                         &Scalar::is_sign_positive,
380                         &|_| true,
381                     );
382                 }
383
384                 fn is_sign_negative<const LANES: usize>() {
385                     test_helpers::test_unary_mask_elementwise(
386                         &Vector::<LANES>::is_sign_negative,
387                         &Scalar::is_sign_negative,
388                         &|_| true,
389                     );
390                 }
391
392                 fn is_finite<const LANES: usize>() {
393                     test_helpers::test_unary_mask_elementwise(
394                         &Vector::<LANES>::is_finite,
395                         &Scalar::is_finite,
396                         &|_| true,
397                     );
398                 }
399
400                 fn is_infinite<const LANES: usize>() {
401                     test_helpers::test_unary_mask_elementwise(
402                         &Vector::<LANES>::is_infinite,
403                         &Scalar::is_infinite,
404                         &|_| true,
405                     );
406                 }
407
408                 fn is_nan<const LANES: usize>() {
409                     test_helpers::test_unary_mask_elementwise(
410                         &Vector::<LANES>::is_nan,
411                         &Scalar::is_nan,
412                         &|_| true,
413                     );
414                 }
415
416                 fn is_normal<const LANES: usize>() {
417                     test_helpers::test_unary_mask_elementwise(
418                         &Vector::<LANES>::is_normal,
419                         &Scalar::is_normal,
420                         &|_| true,
421                     );
422                 }
423
424                 fn is_subnormal<const LANES: usize>() {
425                     test_helpers::test_unary_mask_elementwise(
426                         &Vector::<LANES>::is_subnormal,
427                         &Scalar::is_subnormal,
428                         &|_| true,
429                     );
430                 }
431
432                 fn abs<const LANES: usize>() {
433                     test_helpers::test_unary_elementwise(
434                         &Vector::<LANES>::abs,
435                         &Scalar::abs,
436                         &|_| true,
437                     )
438                 }
439
440                 fn recip<const LANES: usize>() {
441                     test_helpers::test_unary_elementwise(
442                         &Vector::<LANES>::recip,
443                         &Scalar::recip,
444                         &|_| true,
445                     )
446                 }
447
448                 fn to_degrees<const LANES: usize>() {
449                     test_helpers::test_unary_elementwise(
450                         &Vector::<LANES>::to_degrees,
451                         &Scalar::to_degrees,
452                         &|_| true,
453                     )
454                 }
455
456                 fn to_radians<const LANES: usize>() {
457                     test_helpers::test_unary_elementwise(
458                         &Vector::<LANES>::to_radians,
459                         &Scalar::to_radians,
460                         &|_| true,
461                     )
462                 }
463
464                 fn signum<const LANES: usize>() {
465                     test_helpers::test_unary_elementwise(
466                         &Vector::<LANES>::signum,
467                         &Scalar::signum,
468                         &|_| true,
469                     )
470                 }
471
472                 fn copysign<const LANES: usize>() {
473                     test_helpers::test_binary_elementwise(
474                         &Vector::<LANES>::copysign,
475                         &Scalar::copysign,
476                         &|_, _| true,
477                     )
478                 }
479
480                 fn min<const LANES: usize>() {
481                     // Regular conditions (both values aren't zero)
482                     test_helpers::test_binary_elementwise(
483                         &Vector::<LANES>::min,
484                         &Scalar::min,
485                         // Reject the case where both values are zero with different signs
486                         &|a, b| {
487                             for (a, b) in a.iter().zip(b.iter()) {
488                                 if *a == 0. && *b == 0. && a.signum() != b.signum() {
489                                     return false;
490                                 }
491                             }
492                             true
493                         }
494                     );
495
496                     // Special case where both values are zero
497                     let p_zero = Vector::<LANES>::splat(0.);
498                     let n_zero = Vector::<LANES>::splat(-0.);
499                     assert!(p_zero.min(n_zero).to_array().iter().all(|x| *x == 0.));
500                     assert!(n_zero.min(p_zero).to_array().iter().all(|x| *x == 0.));
501                 }
502
503                 fn max<const LANES: usize>() {
504                     // Regular conditions (both values aren't zero)
505                     test_helpers::test_binary_elementwise(
506                         &Vector::<LANES>::max,
507                         &Scalar::max,
508                         // Reject the case where both values are zero with different signs
509                         &|a, b| {
510                             for (a, b) in a.iter().zip(b.iter()) {
511                                 if *a == 0. && *b == 0. && a.signum() != b.signum() {
512                                     return false;
513                                 }
514                             }
515                             true
516                         }
517                     );
518
519                     // Special case where both values are zero
520                     let p_zero = Vector::<LANES>::splat(0.);
521                     let n_zero = Vector::<LANES>::splat(-0.);
522                     assert!(p_zero.max(n_zero).to_array().iter().all(|x| *x == 0.));
523                     assert!(n_zero.max(p_zero).to_array().iter().all(|x| *x == 0.));
524                 }
525
526                 fn clamp<const LANES: usize>() {
527                     test_helpers::test_3(&|value: [Scalar; LANES], mut min: [Scalar; LANES], mut max: [Scalar; LANES]| {
528                         for (min, max) in min.iter_mut().zip(max.iter_mut()) {
529                             if max < min {
530                                 core::mem::swap(min, max);
531                             }
532                             if min.is_nan() {
533                                 *min = Scalar::NEG_INFINITY;
534                             }
535                             if max.is_nan() {
536                                 *max = Scalar::INFINITY;
537                             }
538                         }
539
540                         let mut result_scalar = [Scalar::default(); LANES];
541                         for i in 0..LANES {
542                             result_scalar[i] = value[i].clamp(min[i], max[i]);
543                         }
544                         let result_vector = Vector::from_array(value).clamp(min.into(), max.into()).to_array();
545                         test_helpers::prop_assert_biteq!(result_scalar, result_vector);
546                         Ok(())
547                     })
548                 }
549
550                 fn horizontal_sum<const LANES: usize>() {
551                     test_helpers::test_1(&|x| {
552                         test_helpers::prop_assert_biteq! (
553                             Vector::<LANES>::from_array(x).horizontal_sum(),
554                             x.iter().sum(),
555                         );
556                         Ok(())
557                     });
558                 }
559
560                 fn horizontal_product<const LANES: usize>() {
561                     test_helpers::test_1(&|x| {
562                         test_helpers::prop_assert_biteq! (
563                             Vector::<LANES>::from_array(x).horizontal_product(),
564                             x.iter().product(),
565                         );
566                         Ok(())
567                     });
568                 }
569
570                 fn horizontal_max<const LANES: usize>() {
571                     test_helpers::test_1(&|x| {
572                         let vmax = Vector::<LANES>::from_array(x).horizontal_max();
573                         let smax = x.iter().copied().fold(Scalar::NAN, Scalar::max);
574                         // 0 and -0 are treated the same
575                         if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
576                             test_helpers::prop_assert_biteq!(vmax, smax);
577                         }
578                         Ok(())
579                     });
580                 }
581
582                 fn horizontal_min<const LANES: usize>() {
583                     test_helpers::test_1(&|x| {
584                         let vmax = Vector::<LANES>::from_array(x).horizontal_min();
585                         let smax = x.iter().copied().fold(Scalar::NAN, Scalar::min);
586                         // 0 and -0 are treated the same
587                         if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
588                             test_helpers::prop_assert_biteq!(vmax, smax);
589                         }
590                         Ok(())
591                     });
592                 }
593             }
594
595             #[cfg(feature = "std")]
596             mod std {
597                 use super::*;
598                 test_helpers::test_lanes! {
599                     fn sqrt<const LANES: usize>() {
600                         test_helpers::test_unary_elementwise(
601                             &Vector::<LANES>::sqrt,
602                             &Scalar::sqrt,
603                             &|_| true,
604                         )
605                     }
606
607                     fn mul_add<const LANES: usize>() {
608                         test_helpers::test_ternary_elementwise(
609                             &Vector::<LANES>::mul_add,
610                             &Scalar::mul_add,
611                             &|_, _, _| true,
612                         )
613                     }
614                 }
615             }
616         }
617     }
618 }