macro_rules! impl_reduction_min_max {
([$elem_ty:ident; $elem_count:expr]: $id:ident
| $ielem_ty:ident | $test_tt:tt) => {
impl $id {
#[inline]
pub fn max_element(self) -> $elem_ty {
#[cfg(not(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc64",
target_arch = "wasm32",
)))]
{
use crate::llvm::simd_reduce_max;
let v: $ielem_ty = unsafe { simd_reduce_max(self.0) };
v as $elem_ty
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc64",
target_arch = "wasm32",
))]
{
let mut x = self.extract(0);
for i in 1..$id::lanes() {
x = x.max(self.extract(i));
}
x
}
}
#[inline]
pub fn min_element(self) -> $elem_ty {
#[cfg(not(any(
target_arch = "aarch64",
target_arch = "arm",
all(target_arch = "x86", not(target_feature = "sse2")),
target_arch = "powerpc64",
target_arch = "wasm32",
),))]
{
use crate::llvm::simd_reduce_min;
let v: $ielem_ty = unsafe { simd_reduce_min(self.0) };
v as $elem_ty
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
all(target_arch = "x86", not(target_feature = "sse2")),
target_arch = "powerpc64",
target_arch = "wasm32",
))]
{
let mut x = self.extract(0);
for i in 1..$id::lanes() {
x = x.min(self.extract(i));
}
x
}
}
}
test_if! {$test_tt:
paste::item! {
pub mod [<$id _reduction_min_max>] {
use super::*;
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
pub fn max_element() {
let v = $id::splat(0 as $elem_ty);
assert_eq!(v.max_element(), 0 as $elem_ty);
if $id::lanes() > 1 {
let v = v.replace(1, 1 as $elem_ty);
assert_eq!(v.max_element(), 1 as $elem_ty);
}
let v = v.replace(0, 2 as $elem_ty);
assert_eq!(v.max_element(), 2 as $elem_ty);
}
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
pub fn min_element() {
let v = $id::splat(0 as $elem_ty);
assert_eq!(v.min_element(), 0 as $elem_ty);
if $id::lanes() > 1 {
let v = v.replace(1, 1 as $elem_ty);
assert_eq!(v.min_element(), 0 as $elem_ty);
}
let v = $id::splat(1 as $elem_ty);
let v = v.replace(0, 2 as $elem_ty);
if $id::lanes() > 1 {
assert_eq!(v.min_element(), 1 as $elem_ty);
} else {
assert_eq!(v.min_element(), 2 as $elem_ty);
}
if $id::lanes() > 1 {
let v = $id::splat(2 as $elem_ty);
let v = v.replace(1, 1 as $elem_ty);
assert_eq!(v.min_element(), 1 as $elem_ty);
}
}
}
}
}
};
}
macro_rules! test_reduction_float_min_max {
([$elem_ty:ident; $elem_count:expr]: $id:ident | $test_tt:tt) => {
test_if!{
$test_tt:
paste::item! {
pub mod [<$id _reduction_min_max_nan>] {
use super::*;
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn min_element_test() {
let n = crate::$elem_ty::NAN;
assert_eq!(n.min(-3.), -3.);
assert_eq!((-3. as $elem_ty).min(n), -3.);
let v0 = $id::splat(-3.);
let target_with_broken_last_lane_nan = !cfg!(any(
target_arch = "arm", target_arch = "aarch64",
all(target_arch = "x86",
not(target_feature = "sse2")
),
target_arch = "powerpc64",
target_arch = "wasm32",
));
for i in 0..$id::lanes() {
let mut v = v0.replace(i, n);
if i == $id::lanes() - 1 &&
target_with_broken_last_lane_nan {
assert!(v.min_element().is_nan(),
"[A]: nan at {} => {} | {:?}",
i, v.min_element(), v);
for j in 0..i {
v = v.replace(j, n);
assert!(v.min_element().is_nan(),
"[B]: nan at {} => {} | {:?}",
i, v.min_element(), v);
}
break
}
if $id::lanes() == 1 {
assert!(v.min_element().is_nan(),
"[C]: all nans | v={:?} | min={} | \
is_nan: {}",
v, v.min_element(),
v.min_element().is_nan()
);
break;
}
assert_eq!(v.min_element(), -3.,
"[D]: nan at {} => {} | {:?}",
i, v.min_element(), v);
for j in 0..i {
v = v.replace(j, n);
if i == $id::lanes() - 1 && j == i - 1 {
assert!(v.min_element().is_nan(),
"[E]: all nans | v={:?} | min={} | \
is_nan: {}",
v, v.min_element(),
v.min_element().is_nan());
} else {
assert_eq!(v.min_element(), -3.,
"[F]: nan at {} => {} | {:?}",
i, v.min_element(), v);
}
}
}
assert!($id::splat(n).min_element().is_nan(),
"all nans | v={:?} | min={} | is_nan: {}",
$id::splat(n), $id::splat(n).min_element(),
$id::splat(n).min_element().is_nan());
}
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn max_element_test() {
let n = crate::$elem_ty::NAN;
assert_eq!(n.max(-3.), -3.);
assert_eq!((-3. as $elem_ty).max(n), -3.);
let v0 = $id::splat(-3.);
let target_with_broken_last_lane_nan = !cfg!(any(
target_arch = "arm", target_arch = "aarch64",
target_arch = "powerpc64", target_arch = "wasm32",
));
for i in 0..$id::lanes() {
let mut v = v0.replace(i, n);
if i == $id::lanes() - 1 &&
target_with_broken_last_lane_nan {
assert!(v.max_element().is_nan(),
"[A]: nan at {} => {} | {:?}",
i, v.max_element(), v);
for j in 0..i {
v = v.replace(j, n);
assert!(v.max_element().is_nan(),
"[B]: nan at {} => {} | {:?}",
i, v.max_element(), v);
}
break
}
if $id::lanes() == 1 {
assert!(v.max_element().is_nan(),
"[C]: all nans | v={:?} | min={} | \
is_nan: {}",
v, v.max_element(),
v.max_element().is_nan());
break;
}
assert_eq!(v.max_element(), -3.,
"[D]: nan at {} => {} | {:?}",
i, v.max_element(), v);
for j in 0..i {
v = v.replace(j, n);
if i == $id::lanes() - 1 && j == i - 1 {
assert!(v.max_element().is_nan(),
"[E]: all nans | v={:?} | max={} | \
is_nan: {}",
v, v.max_element(),
v.max_element().is_nan());
} else {
assert_eq!(v.max_element(), -3.,
"[F]: nan at {} => {} | {:?}",
i, v.max_element(), v);
}
}
}
assert!($id::splat(n).max_element().is_nan(),
"all nans | v={:?} | max={} | is_nan: {}",
$id::splat(n), $id::splat(n).max_element(),
$id::splat(n).max_element().is_nan());
}
}
}
}
}
}