From 5b5b259bf3488a07501e638b0af1d00af0c3c1c0 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 3 Mar 2024 10:06:20 -0500 Subject: [PATCH 1/5] Test std_float --- Cargo.lock | 1 + crates/std_float/Cargo.toml | 3 ++ crates/std_float/src/lib.rs | 61 ++++++------------------------- crates/std_float/tests/float.rs | 64 +++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 51 deletions(-) create mode 100644 crates/std_float/tests/float.rs diff --git a/Cargo.lock b/Cargo.lock index 46312c09657d5..1ede15ff00286 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,6 +177,7 @@ name = "std_float" version = "0.1.0" dependencies = [ "core_simd", + "test_helpers", ] [[package]] diff --git a/crates/std_float/Cargo.toml b/crates/std_float/Cargo.toml index 84c69774cbdfe..8842b226104dc 100644 --- a/crates/std_float/Cargo.toml +++ b/crates/std_float/Cargo.toml @@ -8,6 +8,9 @@ edition = "2021" [dependencies] core_simd = { path = "../core_simd", default-features = false } +[dev-dependencies.test_helpers] +path = "../test_helpers" + [features] default = ["as_crate"] as_crate = [] diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 98f10e94a301c..5237f7cce15cf 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -101,10 +101,19 @@ pub trait StdFloat: Sealed + Sized { /// in the equivalently-indexed lane in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn log(self) -> Self { + fn ln(self) -> Self { unsafe { intrinsics::simd_flog(self) } } + /// Produces a vector where every lane has the logarithm with respect to an arbitrary + /// in the equivalently-indexed lanes in `self` and `base`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log(self, base: Self) -> Self { + unsafe { intrinsics::simd_div(self.ln(), base.ln()) } + } + + /// Produces a vector where every lane has the base-2 logarithm of the value /// in the equivalently-indexed lane in `self`. #[inline] @@ -181,53 +190,3 @@ where self - self.trunc() } } - -#[cfg(test)] -mod tests_simd_floats { - use super::*; - use simd::prelude::*; - - #[test] - fn everything_works_f32() { - let x = f32x4::from_array([0.1, 0.5, 0.6, -1.5]); - - let x2 = x + x; - let _xc = x.ceil(); - let _xf = x.floor(); - let _xr = x.round(); - let _xt = x.trunc(); - let _xfma = x.mul_add(x, x); - let _xsqrt = x.sqrt(); - let _abs_mul = x2.abs() * x2; - - let _fexp = x.exp(); - let _fexp2 = x.exp2(); - let _flog = x.log(); - let _flog2 = x.log2(); - let _flog10 = x.log10(); - let _fsin = x.sin(); - let _fcos = x.cos(); - } - - #[test] - fn everything_works_f64() { - let x = f64x4::from_array([0.1, 0.5, 0.6, -1.5]); - - let x2 = x + x; - let _xc = x.ceil(); - let _xf = x.floor(); - let _xr = x.round(); - let _xt = x.trunc(); - let _xfma = x.mul_add(x, x); - let _xsqrt = x.sqrt(); - let _abs_mul = x2.abs() * x2; - - let _fexp = x.exp(); - let _fexp2 = x.exp2(); - let _flog = x.log(); - let _flog2 = x.log2(); - let _flog10 = x.log10(); - let _fsin = x.sin(); - let _fcos = x.cos(); - } -} diff --git a/crates/std_float/tests/float.rs b/crates/std_float/tests/float.rs new file mode 100644 index 0000000000000..60bdf00fba886 --- /dev/null +++ b/crates/std_float/tests/float.rs @@ -0,0 +1,64 @@ +#![feature(portable_simd)] + +macro_rules! unary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_unary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_| true, + ) + } + )* + } + } +} + +macro_rules! binary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_binary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_, _| true, + ) + } + )* + } + } +} + +macro_rules! ternary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_ternary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_, _, _| true, + ) + } + )* + } + } +} + +macro_rules! impl_tests { + { $scalar:tt } => { + mod $scalar { + use std_float::StdFloat; + + unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc, fract } + binary_test! { $scalar, log } + ternary_test! { $scalar, mul_add } + } + } +} + +impl_tests! { f32 } +impl_tests! { f64 } From e5d5006cf319ef31027d0a0c6c20aa136f7737a4 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 3 Mar 2024 10:11:52 -0500 Subject: [PATCH 2/5] Update docs --- crates/std_float/src/lib.rs | 42 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 5237f7cce15cf..23ee55d8ac3ac 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -44,7 +44,7 @@ use crate::sealed::Sealed; /// For now this trait is available to permit experimentation with SIMD float /// operations that may lack hardware support, such as `mul_add`. pub trait StdFloat: Sealed + Sized { - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error, + /// Elementwise fused multiply-add. Computes `(self * a) + b` with only one rounding error, /// yielding a more accurate result than an unfused multiply-add. /// /// Using `mul_add` *may* be more performant than an unfused multiply-add if the target @@ -57,56 +57,56 @@ pub trait StdFloat: Sealed + Sized { unsafe { intrinsics::simd_fma(self, a, b) } } - /// Produces a vector where every lane has the square root value - /// of the equivalently-indexed lane in `self` + /// Produces a vector where every element has the square root value + /// of the equivalently-indexed element in `self` #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn sqrt(self) -> Self { unsafe { intrinsics::simd_fsqrt(self) } } - /// Produces a vector where every lane has the sine of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the sine of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn sin(self) -> Self { unsafe { intrinsics::simd_fsin(self) } } - /// Produces a vector where every lane has the cosine of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the cosine of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn cos(self) -> Self { unsafe { intrinsics::simd_fcos(self) } } - /// Produces a vector where every lane has the exponential (base e) of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the exponential (base e) of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn exp(self) -> Self { unsafe { intrinsics::simd_fexp(self) } } - /// Produces a vector where every lane has the exponential (base 2) of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the exponential (base 2) of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn exp2(self) -> Self { unsafe { intrinsics::simd_fexp2(self) } } - /// Produces a vector where every lane has the natural logarithm of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the natural logarithm of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn ln(self) -> Self { unsafe { intrinsics::simd_flog(self) } } - /// Produces a vector where every lane has the logarithm with respect to an arbitrary - /// in the equivalently-indexed lanes in `self` and `base`. + /// Produces a vector where every element has the logarithm with respect to an arbitrary + /// in the equivalently-indexed elements in `self` and `base`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn log(self, base: Self) -> Self { @@ -114,30 +114,30 @@ pub trait StdFloat: Sealed + Sized { } - /// Produces a vector where every lane has the base-2 logarithm of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the base-2 logarithm of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn log2(self) -> Self { unsafe { intrinsics::simd_flog2(self) } } - /// Produces a vector where every lane has the base-10 logarithm of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the base-10 logarithm of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn log10(self) -> Self { unsafe { intrinsics::simd_flog10(self) } } - /// Returns the smallest integer greater than or equal to each lane. + /// Returns the smallest integer greater than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn ceil(self) -> Self { unsafe { intrinsics::simd_ceil(self) } } - /// Returns the largest integer value less than or equal to each lane. + /// Returns the largest integer value less than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn floor(self) -> Self { From bcedde54568f6bfb0f150ff8fe5d26f427b129d3 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 3 Mar 2024 10:28:33 -0500 Subject: [PATCH 3/5] Fix formatting --- crates/std_float/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 23ee55d8ac3ac..44bbcba412f6d 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -113,7 +113,6 @@ pub trait StdFloat: Sealed + Sized { unsafe { intrinsics::simd_div(self.ln(), base.ln()) } } - /// Produces a vector where every element has the base-2 logarithm of the value /// in the equivalently-indexed element in `self`. #[inline] From 2f062b8f5eb40d566b2e88ab960b176687167faa Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 3 Mar 2024 10:29:32 -0500 Subject: [PATCH 4/5] Fix wasm tests --- crates/std_float/Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/std_float/Cargo.toml b/crates/std_float/Cargo.toml index 8842b226104dc..0896094ee63f4 100644 --- a/crates/std_float/Cargo.toml +++ b/crates/std_float/Cargo.toml @@ -11,6 +11,10 @@ core_simd = { path = "../core_simd", default-features = false } [dev-dependencies.test_helpers] path = "../test_helpers" +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3" + [features] default = ["as_crate"] as_crate = [] From 278eb287b34d6335a3ac4720f1517a12671fd0b8 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 3 Mar 2024 11:28:58 -0500 Subject: [PATCH 5/5] Attempt to avoid LLVM error --- Cargo.lock | 2 + crates/std_float/src/lib.rs | 114 ++++++++++++++++++-------------- crates/std_float/tests/float.rs | 12 +++- 3 files changed, 78 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ede15ff00286..1584c704fb221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,6 +178,8 @@ version = "0.1.0" dependencies = [ "core_simd", "test_helpers", + "wasm-bindgen", + "wasm-bindgen-test", ] [[package]] diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 44bbcba412f6d..148aa5f9f1771 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "as_crate", no_std)] // We are std! #![cfg_attr( feature = "as_crate", feature(core_intrinsics), @@ -67,43 +66,28 @@ pub trait StdFloat: Sealed + Sized { /// Produces a vector where every element has the sine of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn sin(self) -> Self { - unsafe { intrinsics::simd_fsin(self) } - } + fn sin(self) -> Self; /// Produces a vector where every element has the cosine of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn cos(self) -> Self { - unsafe { intrinsics::simd_fcos(self) } - } + fn cos(self) -> Self; /// Produces a vector where every element has the exponential (base e) of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn exp(self) -> Self { - unsafe { intrinsics::simd_fexp(self) } - } + fn exp(self) -> Self; /// Produces a vector where every element has the exponential (base 2) of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn exp2(self) -> Self { - unsafe { intrinsics::simd_fexp2(self) } - } + fn exp2(self) -> Self; /// Produces a vector where every element has the natural logarithm of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn ln(self) -> Self { - unsafe { intrinsics::simd_flog(self) } - } + fn ln(self) -> Self; /// Produces a vector where every element has the logarithm with respect to an arbitrary /// in the equivalently-indexed elements in `self` and `base`. @@ -115,19 +99,13 @@ pub trait StdFloat: Sealed + Sized { /// Produces a vector where every element has the base-2 logarithm of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn log2(self) -> Self { - unsafe { intrinsics::simd_flog2(self) } - } + fn log2(self) -> Self; /// Produces a vector where every element has the base-10 logarithm of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn log10(self) -> Self { - unsafe { intrinsics::simd_flog10(self) } - } + fn log10(self) -> Self; /// Returns the smallest integer greater than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] @@ -165,27 +143,65 @@ pub trait StdFloat: Sealed + Sized { impl Sealed for Simd where LaneCount: SupportedLaneCount {} impl Sealed for Simd where LaneCount: SupportedLaneCount {} -// We can safely just use all the defaults. -impl StdFloat for Simd -where - LaneCount: SupportedLaneCount, -{ - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - fn fract(self) -> Self { - self - self.trunc() +macro_rules! impl_float { + { + $($fn:ident: $intrinsic:ident,)* + } => { + impl StdFloat for Simd + where + LaneCount: SupportedLaneCount, + { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + + $( + #[inline] + fn $fn(self) -> Self { + unsafe { intrinsics::$intrinsic(self) } + } + )* + } + + impl StdFloat for Simd + where + LaneCount: SupportedLaneCount, + { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + + $( + #[inline] + fn $fn(self) -> Self { + // https://github.com/llvm/llvm-project/issues/83729 + #[cfg(target_arch = "aarch64")] + { + let mut ln = Self::splat(0f64); + for i in 0..N { + ln[i] = self[i].$fn() + } + ln + } + + #[cfg(not(target_arch = "aarch64"))] + { + unsafe { intrinsics::$intrinsic(self) } + } + } + )* + } } } -impl StdFloat for Simd -where - LaneCount: SupportedLaneCount, -{ - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - fn fract(self) -> Self { - self - self.trunc() - } +impl_float! { + sin: simd_fsin, + cos: simd_fcos, + exp: simd_fexp, + exp2: simd_fexp2, + ln: simd_flog, + log2: simd_flog2, + log10: simd_flog10, } diff --git a/crates/std_float/tests/float.rs b/crates/std_float/tests/float.rs index 60bdf00fba886..c66c968f8c667 100644 --- a/crates/std_float/tests/float.rs +++ b/crates/std_float/tests/float.rs @@ -53,9 +53,19 @@ macro_rules! impl_tests { mod $scalar { use std_float::StdFloat; - unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc, fract } + unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc } binary_test! { $scalar, log } ternary_test! { $scalar, mul_add } + + test_helpers::test_lanes! { + fn fract() { + test_helpers::test_unary_elementwise_flush_subnormals( + &core_simd::simd::Simd::<$scalar, LANES>::fract, + &$scalar::fract, + &|_| true, + ) + } + } } } }