| #![cfg_attr(f128_enabled, feature(f128))] |
| #![cfg_attr(f16_enabled, feature(f16))] |
| // makes configuration easier |
| #![allow(unused_macros)] |
| #![allow(unused_imports)] |
| |
| use builtins_test::*; |
| use compiler_builtins::float::Float; |
| use rustc_apfloat::{Float as _, FloatConvert as _}; |
| |
| mod i_to_f { |
| use super::*; |
| |
| macro_rules! i_to_f { |
| ($f_ty:ty, $apfloat_ty:ident, $sys_available:meta, $($i_ty:ty, $fn:ident);*;) => { |
| $( |
| #[test] |
| fn $fn() { |
| use compiler_builtins::float::conv::$fn; |
| use compiler_builtins::int::Int; |
| |
| fuzz(N, |x: $i_ty| { |
| let f0 = apfloat_fallback!( |
| $f_ty, $apfloat_ty, $sys_available, |
| |x| x as $f_ty; |
| // When the builtin is not available, we need to use a different conversion |
| // method (since apfloat doesn't support `as` casting). |
| |x: $i_ty| { |
| use compiler_builtins::int::MinInt; |
| |
| let apf = if <$i_ty>::SIGNED { |
| FloatTy::from_i128(x.try_into().unwrap()).value |
| } else { |
| FloatTy::from_u128(x.try_into().unwrap()).value |
| }; |
| |
| <$f_ty>::from_bits(apf.to_bits()) |
| }, |
| x |
| ); |
| let f1: $f_ty = $fn(x); |
| |
| #[cfg($sys_available)] { |
| // This makes sure that the conversion produced the best rounding possible, and does |
| // this independent of `x as $into` rounding correctly. |
| // This assumes that float to integer conversion is correct. |
| let y_minus_ulp = <$f_ty>::from_bits(f1.to_bits().wrapping_sub(1)) as $i_ty; |
| let y = f1 as $i_ty; |
| let y_plus_ulp = <$f_ty>::from_bits(f1.to_bits().wrapping_add(1)) as $i_ty; |
| let error_minus = <$i_ty as Int>::abs_diff(y_minus_ulp, x); |
| let error = <$i_ty as Int>::abs_diff(y, x); |
| let error_plus = <$i_ty as Int>::abs_diff(y_plus_ulp, x); |
| |
| // The first two conditions check that none of the two closest float values are |
| // strictly closer in representation to `x`. The second makes sure that rounding is |
| // towards even significand if two float values are equally close to the integer. |
| if error_minus < error |
| || error_plus < error |
| || ((error_minus == error || error_plus == error) |
| && ((f0.to_bits() & 1) != 0)) |
| { |
| panic!( |
| "incorrect rounding by {}({}): {}, ({}, {}, {}), errors ({}, {}, {})", |
| stringify!($fn), |
| x, |
| f1.to_bits(), |
| y_minus_ulp, |
| y, |
| y_plus_ulp, |
| error_minus, |
| error, |
| error_plus, |
| ); |
| } |
| } |
| |
| // Test against native conversion. |
| // FIXME(x86,ppc): the platform version has rounding bugs on i686 and |
| // PowerPC64le (for PPC this only shows up in Docker, not the native runner). |
| // https://github.com/rust-lang/compiler-builtins/pull/384#issuecomment-740413334 |
| if !Float::eq_repr(f0, f1) && !cfg!(any( |
| target_arch = "x86", |
| all(target_arch = "powerpc64", target_endian = "little") |
| )) { |
| panic!( |
| "{}({}): std: {:?}, builtins: {:?}", |
| stringify!($fn), |
| x, |
| f0, |
| f1, |
| ); |
| } |
| }); |
| } |
| )* |
| }; |
| } |
| |
| i_to_f! { f32, Single, all(), |
| u32, __floatunsisf; |
| i32, __floatsisf; |
| u64, __floatundisf; |
| i64, __floatdisf; |
| u128, __floatuntisf; |
| i128, __floattisf; |
| } |
| |
| i_to_f! { f64, Double, all(), |
| u32, __floatunsidf; |
| i32, __floatsidf; |
| u64, __floatundidf; |
| i64, __floatdidf; |
| u128, __floatuntidf; |
| i128, __floattidf; |
| } |
| |
| #[cfg(f128_enabled)] |
| #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))] |
| i_to_f! { f128, Quad, not(feature = "no-sys-f128-int-convert"), |
| u32, __floatunsitf; |
| i32, __floatsitf; |
| u64, __floatunditf; |
| i64, __floatditf; |
| u128, __floatuntitf; |
| i128, __floattitf; |
| } |
| |
| #[cfg(f128_enabled)] |
| #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] |
| i_to_f! { f128, Quad, not(feature = "no-sys-f128-int-convert"), |
| u32, __floatunsikf; |
| i32, __floatsikf; |
| u64, __floatundikf; |
| i64, __floatdikf; |
| u128, __floatuntikf; |
| i128, __floattikf; |
| } |
| } |
| |
| mod f_to_i { |
| use super::*; |
| |
| macro_rules! f_to_i { |
| ($x:ident, $f_ty:ty, $apfloat_ty:ident, $sys_available:meta, $($i_ty:ty, $fn:ident);*;) => { |
| $( |
| // it is undefined behavior in the first place to do conversions with NaNs |
| if !apfloat_fallback!( |
| $f_ty, $apfloat_ty, $sys_available, |x: FloatTy| x.is_nan() => no_convert, $x |
| ) { |
| let conv0 = apfloat_fallback!( |
| $f_ty, $apfloat_ty, $sys_available, |
| // Use an `as` cast when the builtin is available on the system. |
| |x| x as $i_ty; |
| // When the builtin is not available, we need to use a different conversion |
| // method (since apfloat doesn't support `as` casting). |
| |x: $f_ty| { |
| use compiler_builtins::int::MinInt; |
| |
| let apf = FloatTy::from_bits(x.to_bits().into()); |
| let bits: usize = <$i_ty>::BITS.try_into().unwrap(); |
| |
| let err_fn = || panic!( |
| "Unable to convert value {x:?} to type {}:", stringify!($i_ty) |
| ); |
| |
| if <$i_ty>::SIGNED { |
| <$i_ty>::try_from(apf.to_i128(bits).value).ok().unwrap_or_else(err_fn) |
| } else { |
| <$i_ty>::try_from(apf.to_u128(bits).value).ok().unwrap_or_else(err_fn) |
| } |
| }, |
| $x |
| ); |
| let conv1: $i_ty = $fn($x); |
| if conv0 != conv1 { |
| panic!("{}({:?}): std: {:?}, builtins: {:?}", stringify!($fn), $x, conv0, conv1); |
| } |
| } |
| )* |
| }; |
| } |
| |
| #[test] |
| fn f32_to_int() { |
| use compiler_builtins::float::conv::{ |
| __fixsfdi, __fixsfsi, __fixsfti, __fixunssfdi, __fixunssfsi, __fixunssfti, |
| }; |
| |
| fuzz_float(N, |x: f32| { |
| f_to_i!(x, f32, Single, all(), |
| u32, __fixunssfsi; |
| u64, __fixunssfdi; |
| u128, __fixunssfti; |
| i32, __fixsfsi; |
| i64, __fixsfdi; |
| i128, __fixsfti; |
| ); |
| }); |
| } |
| |
| #[test] |
| fn f64_to_int() { |
| use compiler_builtins::float::conv::{ |
| __fixdfdi, __fixdfsi, __fixdfti, __fixunsdfdi, __fixunsdfsi, __fixunsdfti, |
| }; |
| |
| fuzz_float(N, |x: f64| { |
| f_to_i!(x, f64, Double, all(), |
| u32, __fixunsdfsi; |
| u64, __fixunsdfdi; |
| u128, __fixunsdfti; |
| i32, __fixdfsi; |
| i64, __fixdfdi; |
| i128, __fixdfti; |
| ); |
| }); |
| } |
| |
| #[test] |
| #[cfg(f128_enabled)] |
| fn f128_to_int() { |
| #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] |
| use compiler_builtins::float::conv::{ |
| __fixkfdi as __fixtfdi, __fixkfsi as __fixtfsi, __fixkfti as __fixtfti, |
| __fixunskfdi as __fixunstfdi, __fixunskfsi as __fixunstfsi, |
| __fixunskfti as __fixunstfti, |
| }; |
| #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))] |
| use compiler_builtins::float::conv::{ |
| __fixtfdi, __fixtfsi, __fixtfti, __fixunstfdi, __fixunstfsi, __fixunstfti, |
| }; |
| |
| fuzz_float(N, |x: f128| { |
| f_to_i!( |
| x, |
| f128, |
| Quad, |
| not(feature = "no-sys-f128-int-convert"), |
| u32, __fixunstfsi; |
| u64, __fixunstfdi; |
| u128, __fixunstfti; |
| i32, __fixtfsi; |
| i64, __fixtfdi; |
| i128, __fixtfti; |
| ); |
| }); |
| } |
| } |
| |
| macro_rules! f_to_f { |
| ( |
| $mod:ident, |
| $( |
| $from_ty:ty => $to_ty:ty, |
| $from_ap_ty:ident => $to_ap_ty:ident, |
| $fn:ident, $sys_available:meta |
| );+; |
| ) => {$( |
| #[test] |
| fn $fn() { |
| use compiler_builtins::float::{$mod::$fn, Float}; |
| use rustc_apfloat::ieee::{$from_ap_ty, $to_ap_ty}; |
| |
| fuzz_float(N, |x: $from_ty| { |
| let tmp0: $to_ty = apfloat_fallback!( |
| $from_ty, |
| $from_ap_ty, |
| $sys_available, |
| |x: $from_ty| x as $to_ty; |
| |x: $from_ty| { |
| let from_apf = FloatTy::from_bits(x.to_bits().into()); |
| // Get `value` directly to ignore INVALID_OP |
| let to_apf: $to_ap_ty = from_apf.convert(&mut false).value; |
| <$to_ty>::from_bits(to_apf.to_bits().try_into().unwrap()) |
| }, |
| x |
| ); |
| let tmp1: $to_ty = $fn(x); |
| |
| if !Float::eq_repr(tmp0, tmp1) { |
| panic!( |
| "{}({:?}): std: {:?}, builtins: {:?}", |
| stringify!($fn), |
| x, |
| tmp0, |
| tmp1 |
| ); |
| } |
| }) |
| } |
| )+}; |
| } |
| |
| mod extend { |
| use super::*; |
| |
| f_to_f! { |
| extend, |
| f32 => f64, Single => Double, __extendsfdf2, all(); |
| } |
| |
| #[cfg(all(f16_enabled, f128_enabled))] |
| #[cfg(not(any( |
| target_arch = "powerpc", |
| target_arch = "powerpc64", |
| target_arch = "loongarch64" |
| )))] |
| f_to_f! { |
| extend, |
| f16 => f32, Half => Single, __extendhfsf2, not(feature = "no-sys-f16"); |
| f16 => f32, Half => Single, __gnu_h2f_ieee, not(feature = "no-sys-f16"); |
| f16 => f64, Half => Double, __extendhfdf2, not(feature = "no-sys-f16-f64-convert"); |
| f16 => f128, Half => Quad, __extendhftf2, not(feature = "no-sys-f16-f128-convert"); |
| f32 => f128, Single => Quad, __extendsftf2, not(feature = "no-sys-f128"); |
| f64 => f128, Double => Quad, __extenddftf2, not(feature = "no-sys-f128"); |
| } |
| |
| #[cfg(f128_enabled)] |
| #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] |
| f_to_f! { |
| extend, |
| // FIXME(#655): `f16` tests disabled until we can bootstrap symbols |
| f32 => f128, Single => Quad, __extendsfkf2, not(feature = "no-sys-f128"); |
| f64 => f128, Double => Quad, __extenddfkf2, not(feature = "no-sys-f128"); |
| } |
| } |
| |
| mod trunc { |
| use super::*; |
| |
| f_to_f! { |
| trunc, |
| f64 => f32, Double => Single, __truncdfsf2, all(); |
| } |
| |
| #[cfg(all(f16_enabled, f128_enabled))] |
| #[cfg(not(any( |
| target_arch = "powerpc", |
| target_arch = "powerpc64", |
| target_arch = "loongarch64" |
| )))] |
| f_to_f! { |
| trunc, |
| f32 => f16, Single => Half, __truncsfhf2, not(feature = "no-sys-f16"); |
| f32 => f16, Single => Half, __gnu_f2h_ieee, not(feature = "no-sys-f16"); |
| f64 => f16, Double => Half, __truncdfhf2, not(feature = "no-sys-f16-f64-convert"); |
| f128 => f16, Quad => Half, __trunctfhf2, not(feature = "no-sys-f16-f128-convert"); |
| f128 => f32, Quad => Single, __trunctfsf2, not(feature = "no-sys-f128"); |
| f128 => f64, Quad => Double, __trunctfdf2, not(feature = "no-sys-f128"); |
| } |
| |
| #[cfg(f128_enabled)] |
| #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] |
| f_to_f! { |
| trunc, |
| // FIXME(#655): `f16` tests disabled until we can bootstrap symbols |
| f128 => f32, Quad => Single, __trunckfsf2, not(feature = "no-sys-f128"); |
| f128 => f64, Quad => Double, __trunckfdf2, not(feature = "no-sys-f128"); |
| } |
| } |