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