summaryrefslogtreecommitdiffstats
path: root/library/portable-simd/crates/test_helpers
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
commit9918693037dce8aa4bb6f08741b6812923486c18 (patch)
tree21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /library/portable-simd/crates/test_helpers
parentReleasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff)
downloadrustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz
rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/portable-simd/crates/test_helpers')
-rw-r--r--library/portable-simd/crates/test_helpers/Cargo.toml6
-rw-r--r--library/portable-simd/crates/test_helpers/src/biteq.rs32
-rw-r--r--library/portable-simd/crates/test_helpers/src/lib.rs128
-rw-r--r--library/portable-simd/crates/test_helpers/src/subnormals.rs91
4 files changed, 250 insertions, 7 deletions
diff --git a/library/portable-simd/crates/test_helpers/Cargo.toml b/library/portable-simd/crates/test_helpers/Cargo.toml
index 1d2bc8b51..23dae7c93 100644
--- a/library/portable-simd/crates/test_helpers/Cargo.toml
+++ b/library/portable-simd/crates/test_helpers/Cargo.toml
@@ -4,10 +4,8 @@ version = "0.1.0"
edition = "2021"
publish = false
-[dependencies.proptest]
-version = "0.10"
-default-features = false
-features = ["alloc"]
+[dependencies]
+proptest = { version = "0.10", default-features = false, features = ["alloc"] }
[features]
all_lane_counts = []
diff --git a/library/portable-simd/crates/test_helpers/src/biteq.rs b/library/portable-simd/crates/test_helpers/src/biteq.rs
index 7d91260d8..cbc20cda0 100644
--- a/library/portable-simd/crates/test_helpers/src/biteq.rs
+++ b/library/portable-simd/crates/test_helpers/src/biteq.rs
@@ -113,6 +113,27 @@ impl<T: BitEq> core::fmt::Debug for BitEqWrapper<'_, T> {
}
}
+#[doc(hidden)]
+pub struct BitEqEitherWrapper<'a, T>(pub &'a T, pub &'a T);
+
+impl<T: BitEq> PartialEq<BitEqEitherWrapper<'_, T>> for BitEqWrapper<'_, T> {
+ fn eq(&self, other: &BitEqEitherWrapper<'_, T>) -> bool {
+ self.0.biteq(other.0) || self.0.biteq(other.1)
+ }
+}
+
+impl<T: BitEq> core::fmt::Debug for BitEqEitherWrapper<'_, T> {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ if self.0.biteq(self.1) {
+ self.0.fmt(f)
+ } else {
+ self.0.fmt(f)?;
+ write!(f, " or ")?;
+ self.1.fmt(f)
+ }
+ }
+}
+
#[macro_export]
macro_rules! prop_assert_biteq {
{ $a:expr, $b:expr $(,)? } => {
@@ -122,5 +143,14 @@ macro_rules! prop_assert_biteq {
let b = $b;
proptest::prop_assert_eq!(BitEqWrapper(&a), BitEqWrapper(&b));
}
- }
+ };
+ { $a:expr, $b:expr, $c:expr $(,)? } => {
+ {
+ use $crate::biteq::{BitEqWrapper, BitEqEitherWrapper};
+ let a = $a;
+ let b = $b;
+ let c = $c;
+ proptest::prop_assert_eq!(BitEqWrapper(&a), BitEqEitherWrapper(&b, &c));
+ }
+ };
}
diff --git a/library/portable-simd/crates/test_helpers/src/lib.rs b/library/portable-simd/crates/test_helpers/src/lib.rs
index b26cdc311..b80c745aa 100644
--- a/library/portable-simd/crates/test_helpers/src/lib.rs
+++ b/library/portable-simd/crates/test_helpers/src/lib.rs
@@ -1,3 +1,5 @@
+#![feature(stdsimd, powerpc_target_feature)]
+
pub mod array;
#[cfg(target_arch = "wasm32")]
@@ -6,6 +8,9 @@ pub mod wasm;
#[macro_use]
pub mod biteq;
+pub mod subnormals;
+use subnormals::FlushSubnormals;
+
/// Specifies the default strategy for testing a type.
///
/// This strategy should be what "makes sense" to test.
@@ -151,7 +156,6 @@ pub fn test_3<
}
/// Test a unary vector function against a unary scalar function, applied elementwise.
-#[inline(never)]
pub fn test_unary_elementwise<Scalar, ScalarResult, Vector, VectorResult, const LANES: usize>(
fv: &dyn Fn(Vector) -> VectorResult,
fs: &dyn Fn(Scalar) -> ScalarResult,
@@ -178,6 +182,48 @@ pub fn test_unary_elementwise<Scalar, ScalarResult, Vector, VectorResult, const
}
/// Test a unary vector function against a unary scalar function, applied elementwise.
+///
+/// Where subnormals are flushed, use approximate equality.
+pub fn test_unary_elementwise_flush_subnormals<
+ Scalar,
+ ScalarResult,
+ Vector,
+ VectorResult,
+ const LANES: usize,
+>(
+ fv: &dyn Fn(Vector) -> VectorResult,
+ fs: &dyn Fn(Scalar) -> ScalarResult,
+ check: &dyn Fn([Scalar; LANES]) -> bool,
+) where
+ Scalar: Copy + core::fmt::Debug + DefaultStrategy + FlushSubnormals,
+ ScalarResult: Copy + biteq::BitEq + core::fmt::Debug + DefaultStrategy + FlushSubnormals,
+ Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy,
+ VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
+{
+ let flush = |x: Scalar| subnormals::flush(fs(subnormals::flush_in(x)));
+ test_1(&|x: [Scalar; LANES]| {
+ proptest::prop_assume!(check(x));
+ let result_v: [ScalarResult; LANES] = fv(x.into()).into();
+ let result_s: [ScalarResult; LANES] = x
+ .iter()
+ .copied()
+ .map(fs)
+ .collect::<Vec<_>>()
+ .try_into()
+ .unwrap();
+ let result_sf: [ScalarResult; LANES] = x
+ .iter()
+ .copied()
+ .map(flush)
+ .collect::<Vec<_>>()
+ .try_into()
+ .unwrap();
+ crate::prop_assert_biteq!(result_v, result_s, result_sf);
+ Ok(())
+ });
+}
+
+/// Test a unary vector function against a unary scalar function, applied elementwise.
#[inline(never)]
pub fn test_unary_mask_elementwise<Scalar, Vector, Mask, const LANES: usize>(
fv: &dyn Fn(Vector) -> Mask,
@@ -204,7 +250,6 @@ pub fn test_unary_mask_elementwise<Scalar, Vector, Mask, const LANES: usize>(
}
/// Test a binary vector function against a binary scalar function, applied elementwise.
-#[inline(never)]
pub fn test_binary_elementwise<
Scalar1,
Scalar2,
@@ -241,6 +286,85 @@ pub fn test_binary_elementwise<
});
}
+/// Test a binary vector function against a binary scalar function, applied elementwise.
+///
+/// Where subnormals are flushed, use approximate equality.
+pub fn test_binary_elementwise_flush_subnormals<
+ Scalar1,
+ Scalar2,
+ ScalarResult,
+ Vector1,
+ Vector2,
+ VectorResult,
+ const LANES: usize,
+>(
+ fv: &dyn Fn(Vector1, Vector2) -> VectorResult,
+ fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult,
+ check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES]) -> bool,
+) where
+ Scalar1: Copy + core::fmt::Debug + DefaultStrategy + FlushSubnormals,
+ Scalar2: Copy + core::fmt::Debug + DefaultStrategy + FlushSubnormals,
+ ScalarResult: Copy + biteq::BitEq + core::fmt::Debug + DefaultStrategy + FlushSubnormals,
+ Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy,
+ Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy,
+ VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
+{
+ let flush = |x: Scalar1, y: Scalar2| {
+ subnormals::flush(fs(subnormals::flush_in(x), subnormals::flush_in(y)))
+ };
+ test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| {
+ proptest::prop_assume!(check(x, y));
+ let result_v: [ScalarResult; LANES] = fv(x.into(), y.into()).into();
+ let result_s: [ScalarResult; LANES] = x
+ .iter()
+ .copied()
+ .zip(y.iter().copied())
+ .map(|(x, y)| fs(x, y))
+ .collect::<Vec<_>>()
+ .try_into()
+ .unwrap();
+ let result_sf: [ScalarResult; LANES] = x
+ .iter()
+ .copied()
+ .zip(y.iter().copied())
+ .map(|(x, y)| flush(x, y))
+ .collect::<Vec<_>>()
+ .try_into()
+ .unwrap();
+ crate::prop_assert_biteq!(result_v, result_s, result_sf);
+ Ok(())
+ });
+}
+
+/// Test a unary vector function against a unary scalar function, applied elementwise.
+#[inline(never)]
+pub fn test_binary_mask_elementwise<Scalar1, Scalar2, Vector1, Vector2, Mask, const LANES: usize>(
+ fv: &dyn Fn(Vector1, Vector2) -> Mask,
+ fs: &dyn Fn(Scalar1, Scalar2) -> bool,
+ check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES]) -> bool,
+) where
+ Scalar1: Copy + core::fmt::Debug + DefaultStrategy,
+ Scalar2: Copy + core::fmt::Debug + DefaultStrategy,
+ Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy,
+ Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy,
+ Mask: Into<[bool; LANES]> + From<[bool; LANES]> + Copy,
+{
+ test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| {
+ proptest::prop_assume!(check(x, y));
+ let result_v: [bool; LANES] = fv(x.into(), y.into()).into();
+ let result_s: [bool; LANES] = x
+ .iter()
+ .copied()
+ .zip(y.iter().copied())
+ .map(|(x, y)| fs(x, y))
+ .collect::<Vec<_>>()
+ .try_into()
+ .unwrap();
+ crate::prop_assert_biteq!(result_v, result_s);
+ Ok(())
+ });
+}
+
/// Test a binary vector-scalar function against a binary scalar function, applied elementwise.
#[inline(never)]
pub fn test_binary_scalar_rhs_elementwise<
diff --git a/library/portable-simd/crates/test_helpers/src/subnormals.rs b/library/portable-simd/crates/test_helpers/src/subnormals.rs
new file mode 100644
index 000000000..ec0f1fb24
--- /dev/null
+++ b/library/portable-simd/crates/test_helpers/src/subnormals.rs
@@ -0,0 +1,91 @@
+pub trait FlushSubnormals: Sized {
+ fn flush(self) -> Self {
+ self
+ }
+}
+
+impl<T> FlushSubnormals for *const T {}
+impl<T> FlushSubnormals for *mut T {}
+
+macro_rules! impl_float {
+ { $($ty:ty),* } => {
+ $(
+ impl FlushSubnormals for $ty {
+ fn flush(self) -> Self {
+ let is_f32 = core::mem::size_of::<Self>() == 4;
+ let ppc_flush = is_f32 && cfg!(all(
+ any(target_arch = "powerpc", all(target_arch = "powerpc64", target_endian = "big")),
+ target_feature = "altivec",
+ not(target_feature = "vsx"),
+ ));
+ let arm_flush = is_f32 && cfg!(all(target_arch = "arm", target_feature = "neon"));
+ let flush = ppc_flush || arm_flush;
+ if flush && self.is_subnormal() {
+ <$ty>::copysign(0., self)
+ } else {
+ self
+ }
+ }
+ }
+ )*
+ }
+}
+
+macro_rules! impl_else {
+ { $($ty:ty),* } => {
+ $(
+ impl FlushSubnormals for $ty {}
+ )*
+ }
+}
+
+impl_float! { f32, f64 }
+impl_else! { i8, i16, i32, i64, isize, u8, u16, u32, u64, usize }
+
+/// AltiVec should flush subnormal inputs to zero, but QEMU seems to only flush outputs.
+/// https://gitlab.com/qemu-project/qemu/-/issues/1779
+#[cfg(all(
+ any(target_arch = "powerpc", target_arch = "powerpc64"),
+ target_feature = "altivec"
+))]
+fn in_buggy_qemu() -> bool {
+ use std::sync::OnceLock;
+ static BUGGY: OnceLock<bool> = OnceLock::new();
+
+ fn add(x: f32, y: f32) -> f32 {
+ #[cfg(target_arch = "powerpc")]
+ use core::arch::powerpc::*;
+ #[cfg(target_arch = "powerpc64")]
+ use core::arch::powerpc64::*;
+
+ let array: [f32; 4] =
+ unsafe { core::mem::transmute(vec_add(vec_splats(x), vec_splats(y))) };
+ array[0]
+ }
+
+ *BUGGY.get_or_init(|| add(-1.0857398e-38, 0.).is_sign_negative())
+}
+
+#[cfg(all(
+ any(target_arch = "powerpc", target_arch = "powerpc64"),
+ target_feature = "altivec"
+))]
+pub fn flush_in<T: FlushSubnormals>(x: T) -> T {
+ if in_buggy_qemu() {
+ x
+ } else {
+ x.flush()
+ }
+}
+
+#[cfg(not(all(
+ any(target_arch = "powerpc", target_arch = "powerpc64"),
+ target_feature = "altivec"
+)))]
+pub fn flush_in<T: FlushSubnormals>(x: T) -> T {
+ x.flush()
+}
+
+pub fn flush<T: FlushSubnormals>(x: T) -> T {
+ x.flush()
+}