mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 04:57:19 +00:00 
			
		
		
		
	 43c3e1bb97
			
		
	
	
		43c3e1bb97
		
	
	
	
	
		
			
			Most of these were skipped because of a bug with the platform implementation, or some kind of crash unwinding. Since the upgrade to Ubuntu 25.04, these all seem to be resolved with the exception of a bug in the host `__floatundisf` [1]. [1] https://github.com/rust-lang/compiler-builtins/pull/384#issuecomment-740413334
		
			
				
	
	
		
			361 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			361 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| #![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");
 | |
|     }
 | |
| }
 |