From 1be662630700d380dc57160f9faf33ae23001e3d Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 10 May 2024 19:01:49 -0500 Subject: [PATCH] Refactor float test macros to have a fallback Change float test macros to fall back to testing against `rustc_apfloat` when system implementations are not available, rather than just skipping tests. This allows for easier debugging where operations may not be supported. --- .../compiler-builtins/testcrate/src/lib.rs | 52 +++++++++++++++++++ .../testcrate/tests/addsub.rs | 21 ++++---- .../compiler-builtins/testcrate/tests/cmp.rs | 39 +++++++++++--- .../testcrate/tests/div_rem.rs | 35 ++++++++----- .../compiler-builtins/testcrate/tests/mul.rs | 16 +++--- 5 files changed, 127 insertions(+), 36 deletions(-) diff --git a/library/compiler-builtins/testcrate/src/lib.rs b/library/compiler-builtins/testcrate/src/lib.rs index 13abf459eba1..1f3a4b826217 100644 --- a/library/compiler-builtins/testcrate/src/lib.rs +++ b/library/compiler-builtins/testcrate/src/lib.rs @@ -263,3 +263,55 @@ pub fn fuzz_float_2(n: u32, f: E) { f(x, y) } } + +/// Perform an operation using builtin types if available, falling back to apfloat if not. +#[macro_export] +macro_rules! apfloat_fallback { + ( + $float_ty:ty, + // Type name in `rustc_apfloat::ieee`. Not a full path, it automatically gets the prefix. + $apfloat_ty:ident, + // Cfg expression for when builtin system operations should be used + $sys_available:meta, + // The expression to run. This expression may use `FloatTy` for its signature. + // Optionally, the final conversion back to a float can be suppressed using + // `=> no_convert` (for e.g. operations that return a bool). + $op:expr $(=> $convert:ident)?, + // Arguments that get passed to `$op` after converting to a float + $($arg:expr),+ + $(,)? + ) => {{ + #[cfg($sys_available)] + let ret = { + type FloatTy = $float_ty; + $op( $($arg),+ ) + }; + + #[cfg(not($sys_available))] + let ret = { + use rustc_apfloat::Float; + type FloatTy = rustc_apfloat::ieee::$apfloat_ty; + + let op_res = $op( $(FloatTy::from_bits($arg.to_bits().into())),+ ); + + apfloat_fallback!(@convert $float_ty, op_res $(,$convert)?) + }; + + ret + }}; + + // Operations that do not need converting back to a float + (@convert $float_ty:ty, $val:expr, no_convert) => { + $val + }; + + // Some apfloat operations return a `StatusAnd` that we need to extract the value from. This + // is the default. + (@convert $float_ty:ty, $val:expr) => {{ + // ignore the status, just get the value + let unwrapped = $val.value; + + <$float_ty>::from_bits(FloatTy::to_bits(unwrapped).try_into().unwrap()) + }}; + +} diff --git a/library/compiler-builtins/testcrate/tests/addsub.rs b/library/compiler-builtins/testcrate/tests/addsub.rs index da7684ec9012..85250ac3d489 100644 --- a/library/compiler-builtins/testcrate/tests/addsub.rs +++ b/library/compiler-builtins/testcrate/tests/addsub.rs @@ -1,5 +1,6 @@ #![allow(unused_macros)] +use core::ops::{Add, Sub}; use testcrate::*; macro_rules! sum { @@ -71,28 +72,28 @@ fn addsub() { } macro_rules! float_sum { - ($($f:ty, $fn_add:ident, $fn_sub:ident);*;) => { + ($($f:ty, $fn_add:ident, $fn_sub:ident, $apfloat_ty:ident, $sys_available:meta);*;) => { $( fuzz_float_2(N, |x: $f, y: $f| { - let add0 = x + y; - let sub0 = x - y; + let add0 = apfloat_fallback!($f, $apfloat_ty, $sys_available, Add::add, x, y); + let sub0 = apfloat_fallback!($f, $apfloat_ty, $sys_available, Sub::sub, x, y); let add1: $f = $fn_add(x, y); let sub1: $f = $fn_sub(x, y); if !Float::eq_repr(add0, add1) { panic!( - "{}({}, {}): std: {}, builtins: {}", + "{}({:?}, {:?}): std: {:?}, builtins: {:?}", stringify!($fn_add), x, y, add0, add1 ); } if !Float::eq_repr(sub0, sub1) { panic!( - "{}({}, {}): std: {}, builtins: {}", + "{}({:?}, {:?}): std: {:?}, builtins: {:?}", stringify!($fn_sub), x, y, sub0, sub1 ); } }); )* - }; + } } #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] @@ -105,8 +106,8 @@ fn float_addsub() { }; float_sum!( - f32, __addsf3, __subsf3; - f64, __adddf3, __subdf3; + f32, __addsf3, __subsf3, Single, all(); + f64, __adddf3, __subdf3, Double, all(); ); } @@ -120,7 +121,7 @@ fn float_addsub_arm() { }; float_sum!( - f32, __addsf3vfp, __subsf3vfp; - f64, __adddf3vfp, __subdf3vfp; + f32, __addsf3vfp, __subsf3vfp, Single, all(); + f64, __adddf3vfp, __subdf3vfp, Double, all(); ); } diff --git a/library/compiler-builtins/testcrate/tests/cmp.rs b/library/compiler-builtins/testcrate/tests/cmp.rs index 14dd76b2d7a7..20915b25d120 100644 --- a/library/compiler-builtins/testcrate/tests/cmp.rs +++ b/library/compiler-builtins/testcrate/tests/cmp.rs @@ -1,23 +1,48 @@ #![allow(unused_macros)] +#![allow(unreachable_code)] #[cfg(not(target_arch = "powerpc64"))] use testcrate::*; macro_rules! cmp { - ($x:ident, $y:ident, $($unordered_val:expr, $fn:ident);*;) => { + ( + $f:ty, $x:ident, $y:ident, $apfloat_ty:ident, $sys_available:meta, + $($unordered_val:expr, $fn:ident);*; + ) => { $( - let cmp0 = if $x.is_nan() || $y.is_nan() { + let cmp0 = if apfloat_fallback!( + $f, $apfloat_ty, $sys_available, + |x: FloatTy| x.is_nan() => no_convert, + $x + ) || apfloat_fallback!( + $f, $apfloat_ty, $sys_available, + |y: FloatTy| y.is_nan() => no_convert, + $y + ) + { $unordered_val - } else if $x < $y { + } else if apfloat_fallback!( + $f, $apfloat_ty, $sys_available, + |x, y| x < y => no_convert, + $x, $y + ) { -1 - } else if $x == $y { + } else if apfloat_fallback!( + $f, $apfloat_ty, $sys_available, + |x, y| x == y => no_convert, + $x, $y + ) { 0 } else { 1 }; + let cmp1 = $fn($x, $y); if cmp0 != cmp1 { - panic!("{}({}, {}): std: {}, builtins: {}", stringify!($fn_builtins), $x, $y, cmp0, cmp1); + panic!( + "{}({:?}, {:?}): std: {:?}, builtins: {:?}", + stringify!($fn), $x, $y, cmp0, cmp1 + ); } )* }; @@ -34,7 +59,7 @@ fn float_comparisons() { fuzz_float_2(N, |x: f32, y: f32| { assert_eq!(__unordsf2(x, y) != 0, x.is_nan() || y.is_nan()); - cmp!(x, y, + cmp!(f32, x, y, Single, all(), 1, __ltsf2; 1, __lesf2; 1, __eqsf2; @@ -45,7 +70,7 @@ fn float_comparisons() { }); fuzz_float_2(N, |x: f64, y: f64| { assert_eq!(__unorddf2(x, y) != 0, x.is_nan() || y.is_nan()); - cmp!(x, y, + cmp!(f64, x, y, Double, all(), 1, __ltdf2; 1, __ledf2; 1, __eqdf2; diff --git a/library/compiler-builtins/testcrate/tests/div_rem.rs b/library/compiler-builtins/testcrate/tests/div_rem.rs index de3bd9bee8f1..461e084d068f 100644 --- a/library/compiler-builtins/testcrate/tests/div_rem.rs +++ b/library/compiler-builtins/testcrate/tests/div_rem.rs @@ -2,6 +2,7 @@ use compiler_builtins::int::sdiv::{__divmoddi4, __divmodsi4, __divmodti4}; use compiler_builtins::int::udiv::{__udivmoddi4, __udivmodsi4, __udivmodti4, u128_divide_sparc}; + use testcrate::*; // Division algorithms have by far the nastiest and largest number of edge cases, and experience shows @@ -104,16 +105,20 @@ fn divide_sparc() { } macro_rules! float { - ($($i:ty, $fn:ident);*;) => { + ($($f:ty, $fn:ident, $apfloat_ty:ident, $sys_available:meta);*;) => { $( - fuzz_float_2(N, |x: $i, y: $i| { - let quo0 = x / y; - let quo1: $i = $fn(x, y); + fuzz_float_2(N, |x: $f, y: $f| { + let quo0: $f = apfloat_fallback!($f, $apfloat_ty, $sys_available, Div::div, x, y); + let quo1: $f = $fn(x, y); #[cfg(not(target_arch = "arm"))] if !Float::eq_repr(quo0, quo1) { panic!( - "{}({}, {}): std: {}, builtins: {}", - stringify!($fn), x, y, quo0, quo1 + "{}({:?}, {:?}): std: {:?}, builtins: {:?}", + stringify!($fn), + x, + y, + quo0, + quo1 ); } @@ -122,8 +127,12 @@ macro_rules! float { if !(Float::is_subnormal(quo0) || Float::is_subnormal(quo1)) { if !Float::eq_repr(quo0, quo1) { panic!( - "{}({}, {}): std: {}, builtins: {}", - stringify!($fn), x, y, quo0, quo1 + "{}({:?}, {:?}): std: {:?}, builtins: {:?}", + stringify!($fn), + x, + y, + quo0, + quo1 ); } } @@ -139,10 +148,11 @@ fn float_div() { div::{__divdf3, __divsf3}, Float, }; + use core::ops::Div; float!( - f32, __divsf3; - f64, __divdf3; + f32, __divsf3, Single, all(); + f64, __divdf3, Double, all(); ); } @@ -153,9 +163,10 @@ fn float_div_arm() { div::{__divdf3vfp, __divsf3vfp}, Float, }; + use core::ops::Div; float!( - f32, __divsf3vfp; - f64, __divdf3vfp; + f32, __divsf3vfp, Single, all(); + f64, __divdf3vfp, Double, all(); ); } diff --git a/library/compiler-builtins/testcrate/tests/mul.rs b/library/compiler-builtins/testcrate/tests/mul.rs index 819f06ca9ef7..4fca2c13fa66 100644 --- a/library/compiler-builtins/testcrate/tests/mul.rs +++ b/library/compiler-builtins/testcrate/tests/mul.rs @@ -82,16 +82,16 @@ fn overflowing_mul() { } macro_rules! float_mul { - ($($f:ty, $fn:ident);*;) => { + ($($f:ty, $fn:ident, $apfloat_ty:ident, $sys_available:meta);*;) => { $( fuzz_float_2(N, |x: $f, y: $f| { - let mul0 = x * y; + let mul0 = apfloat_fallback!($f, $apfloat_ty, $sys_available, Mul::mul, x, y); let mul1: $f = $fn(x, y); // multiplication of subnormals is not currently handled if !(Float::is_subnormal(mul0) || Float::is_subnormal(mul1)) { if !Float::eq_repr(mul0, mul1) { panic!( - "{}({}, {}): std: {}, builtins: {}", + "{}({:?}, {:?}): std: {:?}, builtins: {:?}", stringify!($fn), x, y, mul0, mul1 ); } @@ -108,10 +108,11 @@ fn float_mul() { mul::{__muldf3, __mulsf3}, Float, }; + use core::ops::Mul; float_mul!( - f32, __mulsf3; - f64, __muldf3; + f32, __mulsf3, Single, all(); + f64, __muldf3, Double, all(); ); } @@ -122,9 +123,10 @@ fn float_mul_arm() { mul::{__muldf3vfp, __mulsf3vfp}, Float, }; + use core::ops::Mul; float_mul!( - f32, __mulsf3vfp; - f64, __muldf3vfp; + f32, __mulsf3vfp, Single, all(); + f64, __muldf3vfp, Double, all(); ); }