rust/crates/core_simd/tests/ops_macros.rs

529 lines
22 KiB
Rust
Raw Normal View History

2021-02-13 14:58:44 -06:00
/// Implements a test on a unary operation using proptest.
///
/// Compares the vector operation to the equivalent scalar operation.
2021-02-13 13:19:16 -06:00
#[macro_export]
2021-01-07 00:26:29 -06:00
macro_rules! impl_unary_op_test {
{ $vector:ty, $scalar:ty, $trait:ident :: $fn:ident, $scalar_fn:expr } => {
test_helpers::test_lanes! {
fn $fn<const LANES: usize>() {
test_helpers::test_unary_elementwise(
2021-02-13 01:14:47 -06:00
&<$vector as core::ops::$trait>::$fn,
&$scalar_fn,
&|_| true,
2021-01-07 00:26:29 -06:00
);
}
}
};
{ $vector:ty, $scalar:ty, $trait:ident :: $fn:ident } => {
impl_unary_op_test! { $vector, $scalar, $trait::$fn, <$scalar as core::ops::$trait>::$fn }
};
}
2021-02-13 14:58:44 -06:00
/// Implements a test on a binary operation using proptest.
///
/// Compares the vector operation to the equivalent scalar operation.
2021-02-13 13:19:16 -06:00
#[macro_export]
2021-01-07 00:26:29 -06:00
macro_rules! impl_binary_op_test {
{ $vector:ty, $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr } => {
mod $fn {
use super::*;
test_helpers::test_lanes! {
fn normal<const LANES: usize>() {
test_helpers::test_binary_elementwise(
2021-02-13 01:14:47 -06:00
&<$vector as core::ops::$trait>::$fn,
&$scalar_fn,
&|_, _| true,
2021-01-07 00:26:29 -06:00
);
}
fn scalar_rhs<const LANES: usize>() {
test_helpers::test_binary_scalar_rhs_elementwise(
2021-02-13 01:14:47 -06:00
&<$vector as core::ops::$trait<$scalar>>::$fn,
&$scalar_fn,
&|_, _| true,
2021-01-07 00:26:29 -06:00
);
}
fn scalar_lhs<const LANES: usize>() {
test_helpers::test_binary_scalar_lhs_elementwise(
2021-02-13 01:14:47 -06:00
&<$scalar as core::ops::$trait<$vector>>::$fn,
&$scalar_fn,
&|_, _| true,
2021-01-07 00:26:29 -06:00
);
}
fn assign<const LANES: usize>() {
test_helpers::test_binary_elementwise(
2021-02-13 01:14:47 -06:00
&|mut a, b| { <$vector as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
&$scalar_fn,
&|_, _| true,
);
2021-01-07 00:26:29 -06:00
}
fn assign_scalar_rhs<const LANES: usize>() {
test_helpers::test_binary_scalar_rhs_elementwise(
2021-02-13 01:14:47 -06:00
&|mut a, b| { <$vector as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
&$scalar_fn,
&|_, _| true,
);
2021-01-07 00:26:29 -06:00
}
}
}
};
{ $vector:ty, $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident } => {
impl_binary_op_test! { $vector, $scalar, $trait::$fn, $trait_assign::$fn_assign, <$scalar as core::ops::$trait>::$fn }
};
}
2021-02-13 14:58:44 -06:00
/// Implements a test on a binary operation using proptest.
///
/// Like `impl_binary_op_test`, but allows providing a function for rejecting particular inputs
/// (like the `proptest_assume` macro).
///
/// Compares the vector operation to the equivalent scalar operation.
2021-02-13 13:19:16 -06:00
#[macro_export]
2021-01-07 00:26:29 -06:00
macro_rules! impl_binary_checked_op_test {
{ $vector:ty, $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr, $check_fn:expr } => {
mod $fn {
use super::*;
test_helpers::test_lanes! {
fn normal<const LANES: usize>() {
test_helpers::test_binary_elementwise(
2021-02-13 01:14:47 -06:00
&<$vector as core::ops::$trait>::$fn,
&$scalar_fn,
&|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
2021-01-07 00:26:29 -06:00
);
}
fn scalar_rhs<const LANES: usize>() {
test_helpers::test_binary_scalar_rhs_elementwise(
2021-02-13 01:14:47 -06:00
&<$vector as core::ops::$trait<$scalar>>::$fn,
&$scalar_fn,
&|x, y| x.iter().all(|x| $check_fn(*x, y)),
2021-01-07 00:26:29 -06:00
);
}
fn scalar_lhs<const LANES: usize>() {
test_helpers::test_binary_scalar_lhs_elementwise(
2021-02-13 01:14:47 -06:00
&<$scalar as core::ops::$trait<$vector>>::$fn,
&$scalar_fn,
&|x, y| y.iter().all(|y| $check_fn(x, *y)),
2021-01-07 00:26:29 -06:00
);
}
fn assign<const LANES: usize>() {
test_helpers::test_binary_elementwise(
2021-02-13 01:14:47 -06:00
&|mut a, b| { <$vector as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
&$scalar_fn,
&|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
2021-01-07 00:26:29 -06:00
)
}
fn assign_scalar_rhs<const LANES: usize>() {
test_helpers::test_binary_scalar_rhs_elementwise(
2021-02-13 01:14:47 -06:00
&|mut a, b| { <$vector as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
&$scalar_fn,
&|x, y| x.iter().all(|x| $check_fn(*x, y)),
2021-01-07 00:26:29 -06:00
)
}
}
}
};
{ $vector:ty, $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $check_fn:expr } => {
impl_binary_nonzero_rhs_op_test! { $vector, $scalar, $trait::$fn, $trait_assign::$fn_assign, <$scalar as core::ops::$trait>::$fn, $check_fn }
};
}
2021-03-07 23:48:18 -06:00
#[macro_export]
macro_rules! impl_common_integer_tests {
{ $vector:ident, $scalar:ident } => {
test_helpers::test_lanes! {
fn wrapping_sum<const LANES: usize>() {
test_helpers::test_1(&|x| {
test_helpers::prop_assert_biteq! (
$vector::<LANES>::from_array(x).wrapping_sum(),
x.iter().copied().fold(0 as $scalar, $scalar::wrapping_add),
);
Ok(())
});
}
fn wrapping_product<const LANES: usize>() {
test_helpers::test_1(&|x| {
test_helpers::prop_assert_biteq! (
$vector::<LANES>::from_array(x).wrapping_product(),
x.iter().copied().fold(1 as $scalar, $scalar::wrapping_mul),
);
Ok(())
});
}
fn and_lanes<const LANES: usize>() {
test_helpers::test_1(&|x| {
test_helpers::prop_assert_biteq! (
$vector::<LANES>::from_array(x).and_lanes(),
x.iter().copied().fold(-1i8 as $scalar, <$scalar as core::ops::BitAnd>::bitand),
);
Ok(())
});
}
fn or_lanes<const LANES: usize>() {
test_helpers::test_1(&|x| {
test_helpers::prop_assert_biteq! (
$vector::<LANES>::from_array(x).or_lanes(),
x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitOr>::bitor),
);
Ok(())
});
}
fn xor_lanes<const LANES: usize>() {
test_helpers::test_1(&|x| {
test_helpers::prop_assert_biteq! (
$vector::<LANES>::from_array(x).xor_lanes(),
x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitXor>::bitxor),
);
Ok(())
});
}
fn max_lane<const LANES: usize>() {
test_helpers::test_1(&|x| {
test_helpers::prop_assert_biteq! (
$vector::<LANES>::from_array(x).max_lane(),
x.iter().copied().max().unwrap(),
);
Ok(())
});
}
fn min_lane<const LANES: usize>() {
test_helpers::test_1(&|x| {
test_helpers::prop_assert_biteq! (
$vector::<LANES>::from_array(x).min_lane(),
x.iter().copied().min().unwrap(),
);
Ok(())
});
}
}
}
}
2021-02-13 14:58:44 -06:00
/// Implement tests for signed integers.
2021-02-13 13:19:16 -06:00
#[macro_export]
2021-01-07 00:26:29 -06:00
macro_rules! impl_signed_tests {
{ $vector:ident, $scalar:tt } => {
mod $scalar {
type Vector<const LANES: usize> = core_simd::$vector<LANES>;
type Scalar = $scalar;
2021-03-07 23:48:18 -06:00
impl_common_integer_tests! { Vector, Scalar }
2021-01-07 00:26:29 -06:00
test_helpers::test_lanes! {
fn neg<const LANES: usize>() {
test_helpers::test_unary_elementwise(
2021-03-06 01:14:58 -06:00
&<Vector::<LANES> as core::ops::Neg>::neg,
2021-02-13 01:14:47 -06:00
&<Scalar as core::ops::Neg>::neg,
&|x| !x.contains(&Scalar::MIN),
2021-01-07 00:26:29 -06:00
);
}
2021-03-06 01:14:58 -06:00
fn is_positive<const LANES: usize>() {
test_helpers::test_unary_mask_elementwise(
&Vector::<LANES>::is_positive,
&Scalar::is_positive,
&|_| true,
);
}
fn is_negative<const LANES: usize>() {
test_helpers::test_unary_mask_elementwise(
&Vector::<LANES>::is_negative,
&Scalar::is_negative,
&|_| true,
);
}
2021-01-07 00:26:29 -06:00
}
test_helpers::test_lanes_panic! {
fn div_min_overflow_panics<const LANES: usize>() {
let a = Vector::<LANES>::splat(Scalar::MIN);
let b = Vector::<LANES>::splat(-1);
let _ = a / b;
}
fn div_by_all_zeros_panics<const LANES: usize>() {
let a = Vector::<LANES>::splat(42);
let b = Vector::<LANES>::splat(0);
let _ = a / b;
}
fn div_by_one_zero_panics<const LANES: usize>() {
let a = Vector::<LANES>::splat(42);
let mut b = Vector::<LANES>::splat(21);
b[0] = 0 as _;
let _ = a / b;
}
fn rem_min_overflow_panic<const LANES: usize>() {
let a = Vector::<LANES>::splat(Scalar::MIN);
let b = Vector::<LANES>::splat(-1);
let _ = a % b;
}
fn rem_zero_panic<const LANES: usize>() {
let a = Vector::<LANES>::splat(42);
let b = Vector::<LANES>::splat(0);
let _ = a % b;
}
}
test_helpers::test_lanes! {
fn div_neg_one_no_panic<const LANES: usize>() {
let a = Vector::<LANES>::splat(42);
let b = Vector::<LANES>::splat(-1);
let _ = a / b;
}
fn rem_neg_one_no_panic<const LANES: usize>() {
let a = Vector::<LANES>::splat(42);
let b = Vector::<LANES>::splat(-1);
let _ = a % b;
}
}
2021-01-07 00:26:29 -06:00
impl_binary_op_test!(Vector<LANES>, Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
impl_binary_op_test!(Vector<LANES>, Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
impl_binary_op_test!(Vector<LANES>, Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
2021-02-13 14:58:44 -06:00
// Exclude Div and Rem panicking cases
2021-01-07 00:26:29 -06:00
impl_binary_checked_op_test!(Vector<LANES>, Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
impl_binary_checked_op_test!(Vector<LANES>, Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
impl_unary_op_test!(Vector<LANES>, Scalar, Not::not);
impl_binary_op_test!(Vector<LANES>, Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
impl_binary_op_test!(Vector<LANES>, Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
impl_binary_op_test!(Vector<LANES>, Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
}
}
}
2021-02-13 14:58:44 -06:00
/// Implement tests for unsigned integers.
2021-02-13 13:19:16 -06:00
#[macro_export]
2021-01-07 00:26:29 -06:00
macro_rules! impl_unsigned_tests {
{ $vector:ident, $scalar:tt } => {
mod $scalar {
type Vector<const LANES: usize> = core_simd::$vector<LANES>;
type Scalar = $scalar;
2021-03-07 23:48:18 -06:00
impl_common_integer_tests! { Vector, Scalar }
test_helpers::test_lanes_panic! {
fn rem_zero_panic<const LANES: usize>() {
let a = Vector::<LANES>::splat(42);
let b = Vector::<LANES>::splat(0);
let _ = a % b;
}
}
2021-01-07 00:26:29 -06:00
impl_binary_op_test!(Vector<LANES>, Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
impl_binary_op_test!(Vector<LANES>, Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
impl_binary_op_test!(Vector<LANES>, Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
2021-02-13 14:58:44 -06:00
// Exclude Div and Rem panicking cases
2021-01-07 00:26:29 -06:00
impl_binary_checked_op_test!(Vector<LANES>, Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |_, y| y != 0);
impl_binary_checked_op_test!(Vector<LANES>, Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |_, y| y != 0);
impl_unary_op_test!(Vector<LANES>, Scalar, Not::not);
impl_binary_op_test!(Vector<LANES>, Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
impl_binary_op_test!(Vector<LANES>, Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
impl_binary_op_test!(Vector<LANES>, Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
}
}
}
2021-02-13 14:58:44 -06:00
/// Implement tests for floating point numbers.
2021-02-13 13:19:16 -06:00
#[macro_export]
macro_rules! impl_float_tests {
{ $vector:ident, $scalar:tt, $int_scalar:tt } => {
mod $scalar {
type Vector<const LANES: usize> = core_simd::$vector<LANES>;
type Scalar = $scalar;
type IntScalar = $int_scalar;
impl_unary_op_test!(Vector<LANES>, Scalar, Neg::neg);
impl_binary_op_test!(Vector<LANES>, Scalar, Add::add, AddAssign::add_assign);
impl_binary_op_test!(Vector<LANES>, Scalar, Sub::sub, SubAssign::sub_assign);
impl_binary_op_test!(Vector<LANES>, Scalar, Mul::mul, MulAssign::mul_assign);
2021-02-13 13:19:16 -06:00
impl_binary_op_test!(Vector<LANES>, Scalar, Div::div, DivAssign::div_assign);
impl_binary_op_test!(Vector<LANES>, Scalar, Rem::rem, RemAssign::rem_assign);
2021-03-06 20:56:01 -06:00
#[cfg(feature = "std")]
2021-02-13 13:19:16 -06:00
test_helpers::test_lanes! {
fn ceil<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::ceil,
&Scalar::ceil,
&|_| true,
)
}
fn floor<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::floor,
&Scalar::floor,
&|_| true,
)
}
2021-03-06 20:56:01 -06:00
}
test_helpers::test_lanes! {
2021-03-06 01:14:58 -06:00
fn is_sign_positive<const LANES: usize>() {
test_helpers::test_unary_mask_elementwise(
&Vector::<LANES>::is_sign_positive,
&Scalar::is_sign_positive,
&|_| true,
);
}
fn is_sign_negative<const LANES: usize>() {
test_helpers::test_unary_mask_elementwise(
&Vector::<LANES>::is_sign_negative,
&Scalar::is_sign_negative,
&|_| true,
);
}
fn is_finite<const LANES: usize>() {
test_helpers::test_unary_mask_elementwise(
&Vector::<LANES>::is_finite,
&Scalar::is_finite,
&|_| true,
);
}
fn is_infinite<const LANES: usize>() {
test_helpers::test_unary_mask_elementwise(
&Vector::<LANES>::is_infinite,
&Scalar::is_infinite,
&|_| true,
);
}
fn is_nan<const LANES: usize>() {
test_helpers::test_unary_mask_elementwise(
&Vector::<LANES>::is_nan,
&Scalar::is_nan,
&|_| true,
);
}
fn is_normal<const LANES: usize>() {
test_helpers::test_unary_mask_elementwise(
&Vector::<LANES>::is_normal,
&Scalar::is_normal,
&|_| true,
);
}
fn is_subnormal<const LANES: usize>() {
test_helpers::test_unary_mask_elementwise(
&Vector::<LANES>::is_subnormal,
&Scalar::is_subnormal,
&|_| true,
);
}
2021-03-06 20:56:01 -06:00
fn abs<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::abs,
&Scalar::abs,
&|_| true,
)
}
2021-02-13 13:19:16 -06:00
fn round_from_int<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::round_from_int,
&|x| x as Scalar,
&|_| true,
)
}
fn to_int_unchecked<const LANES: usize>() {
// The maximum integer that can be represented by the equivalently sized float has
// all of the mantissa digits set to 1, pushed up to the MSB.
const ALL_MANTISSA_BITS: IntScalar = ((1 << <Scalar>::MANTISSA_DIGITS) - 1);
const MAX_REPRESENTABLE_VALUE: Scalar =
(ALL_MANTISSA_BITS << (core::mem::size_of::<Scalar>() * 8 - <Scalar>::MANTISSA_DIGITS as usize - 1)) as Scalar;
let mut runner = proptest::test_runner::TestRunner::default();
runner.run(
&test_helpers::array::UniformArrayStrategy::new(-MAX_REPRESENTABLE_VALUE..MAX_REPRESENTABLE_VALUE),
|x| {
let result_1 = unsafe { Vector::from_array(x).to_int_unchecked().to_array() };
let result_2 = {
let mut result = [0; LANES];
for (i, o) in x.iter().zip(result.iter_mut()) {
*o = unsafe { i.to_int_unchecked() };
}
result
};
test_helpers::prop_assert_biteq!(result_1, result_2);
Ok(())
},
).unwrap();
}
2021-03-07 23:48:18 -06:00
fn sum<const LANES: usize>() {
test_helpers::test_1(&|x| {
test_helpers::prop_assert_biteq! (
Vector::<LANES>::from_array(x).sum(),
2021-03-11 23:29:18 -06:00
x.iter().sum(),
2021-03-07 23:48:18 -06:00
);
Ok(())
});
}
fn product<const LANES: usize>() {
test_helpers::test_1(&|x| {
test_helpers::prop_assert_biteq! (
Vector::<LANES>::from_array(x).product(),
2021-03-11 23:29:18 -06:00
x.iter().product(),
2021-03-07 23:48:18 -06:00
);
Ok(())
});
}
fn max_lane<const LANES: usize>() {
test_helpers::test_1(&|x| {
2021-03-10 22:47:43 -06:00
let vmax = Vector::<LANES>::from_array(x).max_lane();
let smax = x.iter().copied().fold(Scalar::NAN, Scalar::max);
// 0 and -0 are treated the same
if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
test_helpers::prop_assert_biteq!(vmax, smax);
}
2021-03-07 23:48:18 -06:00
Ok(())
});
}
fn min_lane<const LANES: usize>() {
test_helpers::test_1(&|x| {
2021-03-10 22:47:43 -06:00
let vmax = Vector::<LANES>::from_array(x).min_lane();
let smax = x.iter().copied().fold(Scalar::NAN, Scalar::min);
// 0 and -0 are treated the same
if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
test_helpers::prop_assert_biteq!(vmax, smax);
}
2021-03-07 23:48:18 -06:00
Ok(())
});
}
2021-02-13 13:19:16 -06:00
}
}
}
}