diff --git a/Cargo.lock b/Cargo.lock index f1fddefcb0144..59f7f3dda8fcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -738,7 +738,7 @@ dependencies = [ "tracing-subscriber", "unified-diff", "walkdir", - "windows 0.59.0", + "windows", ] [[package]] @@ -1587,7 +1587,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core", ] [[package]] @@ -3493,7 +3493,7 @@ dependencies = [ "thorin-dwp", "tracing", "wasm-encoder 0.219.2", - "windows 0.59.0", + "windows", ] [[package]] @@ -3552,7 +3552,7 @@ dependencies = [ "tempfile", "thin-vec", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -3615,7 +3615,7 @@ dependencies = [ "shlex", "stable_mir", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -3670,7 +3670,7 @@ dependencies = [ "termcolor", "termize", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -4415,7 +4415,7 @@ dependencies = [ "smallvec", "termize", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -5102,7 +5102,7 @@ dependencies = [ "libc", "objc2-core-foundation", "objc2-io-kit", - "windows 0.61.1", + "windows", ] [[package]] @@ -6002,16 +6002,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" -dependencies = [ - "windows-core 0.59.0", - "windows-targets 0.53.0", -] - [[package]] name = "windows" version = "0.61.1" @@ -6019,7 +6009,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.61.0", + "windows-core", "windows-future", "windows-link", "windows-numerics", @@ -6042,20 +6032,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.0", -] - -[[package]] -name = "windows-core" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" -dependencies = [ - "windows-implement 0.59.0", - "windows-interface", - "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-core", ] [[package]] @@ -6064,11 +6041,11 @@ version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.60.0", + "windows-implement", "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.0", + "windows-strings", ] [[package]] @@ -6077,21 +6054,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] -[[package]] -name = "windows-implement" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -6126,7 +6092,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] @@ -6139,15 +6105,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-strings" version = "0.4.0" diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 97eebffd1fe8e..d4c8ab80a3313 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -58,5 +58,5 @@ default-features = false features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"] [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = ["Win32_Globalization"] diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index f48c73b13b961..f6a0201161851 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -38,7 +38,7 @@ features = ["nightly"] # for may_dangle version = "0.12" [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_Storage_FileSystem", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 9da4f2dbc2730..1971d06aad64d 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -60,7 +60,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_System_Diagnostics_Debug", ] diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index b11793c190a14..82e7468211db0 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -33,7 +33,7 @@ tracing = "0.1" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_Security", diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 6692ea735401a..a662694ac38f2 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -255,6 +255,7 @@ fn main() { } else if target.contains("haiku") || target.contains("darwin") || (is_crossed && (target.contains("dragonfly") || target.contains("solaris"))) + || target.contains("cygwin") { println!("cargo:rustc-link-lib=z"); } else if target.contains("netbsd") { diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index f63d8b2d79f61..2b85d7b26cea0 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -6,7 +6,6 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![allow(unused_crate_dependencies)] -#![cfg_attr(all(feature = "rustc", bootstrap), feature(let_chains))] // tidy-alphabetical-end pub mod constructor; diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 63772a3222210..f0ee19e3c6774 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -33,7 +33,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_System_LibraryLoader", diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index bdeca91eb64a4..207ba5157bdb6 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -78,10 +78,16 @@ fn current_dll_path() -> Result { if libc::dladdr(addr, &mut info) == 0 { return Err("dladdr failed".into()); } - if info.dli_fname.is_null() { - return Err("dladdr returned null pointer".into()); - } - let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); + #[cfg(target_os = "cygwin")] + let fname_ptr = info.dli_fname.as_ptr(); + #[cfg(not(target_os = "cygwin"))] + let fname_ptr = { + if info.dli_fname.is_null() { + return Err("dladdr returned null pointer".into()); + } + info.dli_fname + }; + let bytes = CStr::from_ptr(fname_ptr).to_bytes(); let os = OsStr::from_bytes(bytes); Ok(PathBuf::from(os)) } diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 870ad9df4fd33..556db239f2499 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -20,6 +20,8 @@ macro_rules! impl_general_format { } } +#[cfg(target_has_reliable_f16)] +impl_general_format! { f16 } impl_general_format! { f32 f64 } // Don't inline this so callers don't use the stack space this function @@ -231,6 +233,13 @@ macro_rules! floating { floating! { f32 f64 } +#[cfg(target_has_reliable_f16)] +floating! { f16 } + +// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order +// to avoid ICEs. + +#[cfg(not(target_has_reliable_f16))] #[stable(feature = "rust1", since = "1.0.0")] impl Debug for f16 { #[inline] @@ -239,6 +248,33 @@ impl Debug for f16 { } } +#[cfg(not(target_has_reliable_f16))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Display for f16 { + #[inline] + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Debug::fmt(self, fmt) + } +} + +#[cfg(not(target_has_reliable_f16))] +#[stable(feature = "rust1", since = "1.0.0")] +impl LowerExp for f16 { + #[inline] + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Debug::fmt(self, fmt) + } +} + +#[cfg(not(target_has_reliable_f16))] +#[stable(feature = "rust1", since = "1.0.0")] +impl UpperExp for f16 { + #[inline] + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Debug::fmt(self, fmt) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Debug for f128 { #[inline] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 64a7ec8906b6b..54555f8beec63 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -101,6 +101,7 @@ #![feature(bstr)] #![feature(bstr_internals)] #![feature(cfg_match)] +#![feature(cfg_target_has_reliable_f16_f128)] #![feature(const_carrying_mul_add)] #![feature(const_eval_select)] #![feature(core_intrinsics)] diff --git a/library/core/src/num/dec2flt/float.rs b/library/core/src/num/dec2flt/float.rs index b8a28a6756917..5bf0faf0bc910 100644 --- a/library/core/src/num/dec2flt/float.rs +++ b/library/core/src/num/dec2flt/float.rs @@ -45,7 +45,7 @@ macro_rules! int { } } -int!(u32, u64); +int!(u16, u32, u64); /// A helper trait to avoid duplicating basically all the conversion code for IEEE floats. /// @@ -189,9 +189,14 @@ pub trait RawFloat: /// Returns the mantissa, exponent and sign as integers. /// - /// That is, this returns `(m, p, s)` such that `s * m * 2^p` represents the original float. - /// For 0, the exponent will be `-(EXP_BIAS + SIG_BITS`, which is the - /// minimum subnormal power. + /// This returns `(m, p, s)` such that `s * m * 2^p` represents the original float. For 0, the + /// exponent will be `-(EXP_BIAS + SIG_BITS)`, which is the minimum subnormal power. For + /// infinity or NaN, the exponent will be `EXP_SAT - EXP_BIAS - SIG_BITS`. + /// + /// If subnormal, the mantissa will be shifted one bit to the left. Otherwise, it is returned + /// with the explicit bit set but otherwise unshifted + /// + /// `s` is only ever +/-1. fn integer_decode(self) -> (u64, i16, i8) { let bits = self.to_bits(); let sign: i8 = if bits >> (Self::BITS - 1) == Self::Int::ZERO { 1 } else { -1 }; @@ -213,6 +218,50 @@ const fn pow2_to_pow10(a: i64) -> i64 { res as i64 } +#[cfg(target_has_reliable_f16)] +impl RawFloat for f16 { + type Int = u16; + + const INFINITY: Self = Self::INFINITY; + const NEG_INFINITY: Self = Self::NEG_INFINITY; + const NAN: Self = Self::NAN; + const NEG_NAN: Self = -Self::NAN; + + const BITS: u32 = 16; + const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS; + const EXP_MASK: Self::Int = Self::EXP_MASK; + const SIG_MASK: Self::Int = Self::MAN_MASK; + + const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -22; + const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 5; + const SMALLEST_POWER_OF_TEN: i32 = -27; + + #[inline] + fn from_u64(v: u64) -> Self { + debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); + v as _ + } + + #[inline] + fn from_u64_bits(v: u64) -> Self { + Self::from_bits((v & 0xFFFF) as u16) + } + + fn pow10_fast_path(exponent: usize) -> Self { + #[allow(clippy::use_self)] + const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.]; + TABLE[exponent & 7] + } + + fn to_bits(self) -> Self::Int { + self.to_bits() + } + + fn classify(self) -> FpCategory { + self.classify() + } +} + impl RawFloat for f32 { type Int = u32; diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs index d1a0e1db31314..abad7acb1046a 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/dec2flt/mod.rs @@ -171,9 +171,25 @@ macro_rules! from_str_float_impl { } }; } + +#[cfg(target_has_reliable_f16)] +from_str_float_impl!(f16); from_str_float_impl!(f32); from_str_float_impl!(f64); +// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order +// to avoid ICEs. + +#[cfg(not(target_has_reliable_f16))] +impl FromStr for f16 { + type Err = ParseFloatError; + + #[inline] + fn from_str(_src: &str) -> Result { + unimplemented!("requires target_has_reliable_f16") + } +} + /// An error which can be returned when parsing a float. /// /// This error is used as the error type for the [`FromStr`] implementation diff --git a/library/core/src/num/flt2dec/decoder.rs b/library/core/src/num/flt2dec/decoder.rs index 40b3aae24a536..bd6e2cdbafec8 100644 --- a/library/core/src/num/flt2dec/decoder.rs +++ b/library/core/src/num/flt2dec/decoder.rs @@ -45,6 +45,13 @@ pub trait DecodableFloat: RawFloat + Copy { fn min_pos_norm_value() -> Self; } +#[cfg(target_has_reliable_f16)] +impl DecodableFloat for f16 { + fn min_pos_norm_value() -> Self { + f16::MIN_POSITIVE + } +} + impl DecodableFloat for f32 { fn min_pos_norm_value() -> Self { f32::MIN_POSITIVE diff --git a/library/coretests/tests/num/dec2flt/decimal.rs b/library/coretests/tests/num/dec2flt/decimal.rs index 1fa06de692e07..f759e1dbde6cb 100644 --- a/library/coretests/tests/num/dec2flt/decimal.rs +++ b/library/coretests/tests/num/dec2flt/decimal.rs @@ -7,6 +7,20 @@ const FPATHS_F32: &[FPath] = const FPATHS_F64: &[FPath] = &[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))]; +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn check_fast_path_f16() { + const FPATHS_F16: &[FPath] = + &[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))]; + for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F16.iter().copied() { + let dec = Decimal { exponent, mantissa, negative, many_digits }; + let actual = dec.try_fast_path::(); + + assert_eq!(actual, expected); + } +} + #[test] fn check_fast_path_f32() { for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F32.iter().copied() { diff --git a/library/coretests/tests/num/dec2flt/float.rs b/library/coretests/tests/num/dec2flt/float.rs index b5afd3e3b2436..2c0f99447442c 100644 --- a/library/coretests/tests/num/dec2flt/float.rs +++ b/library/coretests/tests/num/dec2flt/float.rs @@ -1,5 +1,23 @@ use core::num::dec2flt::float::RawFloat; +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn test_f16_integer_decode() { + assert_eq!(3.14159265359f16.integer_decode(), (1608, -9, 1)); + assert_eq!((-8573.5918555f16).integer_decode(), (1072, 3, -1)); + assert_eq!(2f16.powf(14.0).integer_decode(), (1 << 10, 4, 1)); + assert_eq!(0f16.integer_decode(), (0, -25, 1)); + assert_eq!((-0f16).integer_decode(), (0, -25, -1)); + assert_eq!(f16::INFINITY.integer_decode(), (1 << 10, 6, 1)); + assert_eq!(f16::NEG_INFINITY.integer_decode(), (1 << 10, 6, -1)); + + // Ignore the "sign" (quiet / signalling flag) of NAN. + // It can vary between runtime operations and LLVM folding. + let (nan_m, nan_p, _nan_s) = f16::NAN.integer_decode(); + assert_eq!((nan_m, nan_p), (1536, 6)); +} + #[test] fn test_f32_integer_decode() { assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1)); @@ -34,6 +52,27 @@ fn test_f64_integer_decode() { /* Sanity checks of computed magic numbers */ +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn test_f16_consts() { + assert_eq!(::INFINITY, f16::INFINITY); + assert_eq!(::NEG_INFINITY, -f16::INFINITY); + assert_eq!(::NAN.to_bits(), f16::NAN.to_bits()); + assert_eq!(::NEG_NAN.to_bits(), (-f16::NAN).to_bits()); + assert_eq!(::SIG_BITS, 10); + assert_eq!(::MIN_EXPONENT_ROUND_TO_EVEN, -22); + assert_eq!(::MAX_EXPONENT_ROUND_TO_EVEN, 5); + assert_eq!(::MIN_EXPONENT_FAST_PATH, -4); + assert_eq!(::MAX_EXPONENT_FAST_PATH, 4); + assert_eq!(::MAX_EXPONENT_DISGUISED_FAST_PATH, 7); + assert_eq!(::EXP_MIN, -14); + assert_eq!(::EXP_SAT, 0x1f); + assert_eq!(::SMALLEST_POWER_OF_TEN, -27); + assert_eq!(::LARGEST_POWER_OF_TEN, 4); + assert_eq!(::MAX_MANTISSA_FAST_PATH, 2048); +} + #[test] fn test_f32_consts() { assert_eq!(::INFINITY, f32::INFINITY); diff --git a/library/coretests/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs index 0db80fbd52506..6d49d85170e2d 100644 --- a/library/coretests/tests/num/dec2flt/lemire.rs +++ b/library/coretests/tests/num/dec2flt/lemire.rs @@ -1,6 +1,12 @@ use core::num::dec2flt::float::RawFloat; use core::num::dec2flt::lemire::compute_float; +#[cfg(target_has_reliable_f16)] +fn compute_float16(q: i64, w: u64) -> (i32, u64) { + let fp = compute_float::(q, w); + (fp.p_biased, fp.m) +} + fn compute_float32(q: i64, w: u64) -> (i32, u64) { let fp = compute_float::(q, w); (fp.p_biased, fp.m) @@ -11,23 +17,73 @@ fn compute_float64(q: i64, w: u64) -> (i32, u64) { (fp.p_biased, fp.m) } +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn compute_float_f16_rounding() { + // The maximum integer that cna be converted to a `f16` without lost precision. + let val = 1 << 11; + let scale = 10_u64.pow(10); + + // These test near-halfway cases for half-precision floats. + assert_eq!(compute_float16(0, val), (26, 0)); + assert_eq!(compute_float16(0, val + 1), (26, 0)); + assert_eq!(compute_float16(0, val + 2), (26, 1)); + assert_eq!(compute_float16(0, val + 3), (26, 2)); + assert_eq!(compute_float16(0, val + 4), (26, 2)); + + // For the next power up, the two nearest representable numbers are twice as far apart. + let val2 = 1 << 12; + assert_eq!(compute_float16(0, val2), (27, 0)); + assert_eq!(compute_float16(0, val2 + 2), (27, 0)); + assert_eq!(compute_float16(0, val2 + 4), (27, 1)); + assert_eq!(compute_float16(0, val2 + 6), (27, 2)); + assert_eq!(compute_float16(0, val2 + 8), (27, 2)); + + // These are examples of the above tests, with digits from the exponent shifted + // to the mantissa. + assert_eq!(compute_float16(-10, val * scale), (26, 0)); + assert_eq!(compute_float16(-10, (val + 1) * scale), (26, 0)); + assert_eq!(compute_float16(-10, (val + 2) * scale), (26, 1)); + // Let's check the lines to see if anything is different in table... + assert_eq!(compute_float16(-10, (val + 3) * scale), (26, 2)); + assert_eq!(compute_float16(-10, (val + 4) * scale), (26, 2)); + + // Check the rounding point between infinity and the next representable number down + assert_eq!(compute_float16(4, 6), (f16::INFINITE_POWER - 1, 851)); + assert_eq!(compute_float16(4, 7), (f16::INFINITE_POWER, 0)); // infinity + assert_eq!(compute_float16(2, 655), (f16::INFINITE_POWER - 1, 1023)); +} + #[test] fn compute_float_f32_rounding() { + // the maximum integer that cna be converted to a `f32` without lost precision. + let val = 1 << 24; + let scale = 10_u64.pow(10); + // These test near-halfway cases for single-precision floats. - assert_eq!(compute_float32(0, 16777216), (151, 0)); - assert_eq!(compute_float32(0, 16777217), (151, 0)); - assert_eq!(compute_float32(0, 16777218), (151, 1)); - assert_eq!(compute_float32(0, 16777219), (151, 2)); - assert_eq!(compute_float32(0, 16777220), (151, 2)); - - // These are examples of the above tests, with - // digits from the exponent shifted to the mantissa. - assert_eq!(compute_float32(-10, 167772160000000000), (151, 0)); - assert_eq!(compute_float32(-10, 167772170000000000), (151, 0)); - assert_eq!(compute_float32(-10, 167772180000000000), (151, 1)); + assert_eq!(compute_float32(0, val), (151, 0)); + assert_eq!(compute_float32(0, val + 1), (151, 0)); + assert_eq!(compute_float32(0, val + 2), (151, 1)); + assert_eq!(compute_float32(0, val + 3), (151, 2)); + assert_eq!(compute_float32(0, val + 4), (151, 2)); + + // For the next power up, the two nearest representable numbers are twice as far apart. + let val2 = 1 << 25; + assert_eq!(compute_float32(0, val2), (152, 0)); + assert_eq!(compute_float32(0, val2 + 2), (152, 0)); + assert_eq!(compute_float32(0, val2 + 4), (152, 1)); + assert_eq!(compute_float32(0, val2 + 6), (152, 2)); + assert_eq!(compute_float32(0, val2 + 8), (152, 2)); + + // These are examples of the above tests, with digits from the exponent shifted + // to the mantissa. + assert_eq!(compute_float32(-10, val * scale), (151, 0)); + assert_eq!(compute_float32(-10, (val + 1) * scale), (151, 0)); + assert_eq!(compute_float32(-10, (val + 2) * scale), (151, 1)); // Let's check the lines to see if anything is different in table... - assert_eq!(compute_float32(-10, 167772190000000000), (151, 2)); - assert_eq!(compute_float32(-10, 167772200000000000), (151, 2)); + assert_eq!(compute_float32(-10, (val + 3) * scale), (151, 2)); + assert_eq!(compute_float32(-10, (val + 4) * scale), (151, 2)); // Check the rounding point between infinity and the next representable number down assert_eq!(compute_float32(38, 3), (f32::INFINITE_POWER - 1, 6402534)); @@ -37,23 +93,38 @@ fn compute_float_f32_rounding() { #[test] fn compute_float_f64_rounding() { + // The maximum integer that cna be converted to a `f64` without lost precision. + let val = 1 << 53; + let scale = 1000; + // These test near-halfway cases for double-precision floats. - assert_eq!(compute_float64(0, 9007199254740992), (1076, 0)); - assert_eq!(compute_float64(0, 9007199254740993), (1076, 0)); - assert_eq!(compute_float64(0, 9007199254740994), (1076, 1)); - assert_eq!(compute_float64(0, 9007199254740995), (1076, 2)); - assert_eq!(compute_float64(0, 9007199254740996), (1076, 2)); - assert_eq!(compute_float64(0, 18014398509481984), (1077, 0)); - assert_eq!(compute_float64(0, 18014398509481986), (1077, 0)); - assert_eq!(compute_float64(0, 18014398509481988), (1077, 1)); - assert_eq!(compute_float64(0, 18014398509481990), (1077, 2)); - assert_eq!(compute_float64(0, 18014398509481992), (1077, 2)); - - // These are examples of the above tests, with - // digits from the exponent shifted to the mantissa. - assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0)); - assert_eq!(compute_float64(-3, 9007199254740993000), (1076, 0)); - assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1)); - assert_eq!(compute_float64(-3, 9007199254740995000), (1076, 2)); - assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2)); + assert_eq!(compute_float64(0, val), (1076, 0)); + assert_eq!(compute_float64(0, val + 1), (1076, 0)); + assert_eq!(compute_float64(0, val + 2), (1076, 1)); + assert_eq!(compute_float64(0, val + 3), (1076, 2)); + assert_eq!(compute_float64(0, val + 4), (1076, 2)); + + // For the next power up, the two nearest representable numbers are twice as far apart. + let val2 = 1 << 54; + assert_eq!(compute_float64(0, val2), (1077, 0)); + assert_eq!(compute_float64(0, val2 + 2), (1077, 0)); + assert_eq!(compute_float64(0, val2 + 4), (1077, 1)); + assert_eq!(compute_float64(0, val2 + 6), (1077, 2)); + assert_eq!(compute_float64(0, val2 + 8), (1077, 2)); + + // These are examples of the above tests, with digits from the exponent shifted + // to the mantissa. + assert_eq!(compute_float64(-3, val * scale), (1076, 0)); + assert_eq!(compute_float64(-3, (val + 1) * scale), (1076, 0)); + assert_eq!(compute_float64(-3, (val + 2) * scale), (1076, 1)); + assert_eq!(compute_float64(-3, (val + 3) * scale), (1076, 2)); + assert_eq!(compute_float64(-3, (val + 4) * scale), (1076, 2)); + + // Check the rounding point between infinity and the next representable number down + assert_eq!(compute_float64(308, 1), (f64::INFINITE_POWER - 1, 506821272651936)); + assert_eq!(compute_float64(308, 2), (f64::INFINITE_POWER, 0)); // infinity + assert_eq!( + compute_float64(292, 17976931348623157), + (f64::INFINITE_POWER - 1, 4503599627370495) + ); } diff --git a/library/coretests/tests/num/dec2flt/mod.rs b/library/coretests/tests/num/dec2flt/mod.rs index a9025be5ca7f1..b8ca220847cfa 100644 --- a/library/coretests/tests/num/dec2flt/mod.rs +++ b/library/coretests/tests/num/dec2flt/mod.rs @@ -11,15 +11,23 @@ mod parse; // Requires a *polymorphic literal*, i.e., one that can serve as f64 as well as f32. macro_rules! test_literal { ($x: expr) => {{ + #[cfg(target_has_reliable_f16)] + let x16: f16 = $x; let x32: f32 = $x; let x64: f64 = $x; let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)]; + for input in inputs { - assert_eq!(input.parse(), Ok(x64)); - assert_eq!(input.parse(), Ok(x32)); + assert_eq!(input.parse(), Ok(x64), "failed f64 {input}"); + assert_eq!(input.parse(), Ok(x32), "failed f32 {input}"); + #[cfg(target_has_reliable_f16)] + assert_eq!(input.parse(), Ok(x16), "failed f16 {input}"); + let neg_input = format!("-{input}"); - assert_eq!(neg_input.parse(), Ok(-x64)); - assert_eq!(neg_input.parse(), Ok(-x32)); + assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}"); + assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}"); + #[cfg(target_has_reliable_f16)] + assert_eq!(neg_input.parse(), Ok(-x16), "failed f16 {neg_input}"); } }}; } @@ -84,48 +92,87 @@ fn fast_path_correct() { test_literal!(1.448997445238699); } +// FIXME(f16_f128): remove gates once tests work on all targets + #[test] fn lonely_dot() { + #[cfg(target_has_reliable_f16)] + assert!(".".parse::().is_err()); assert!(".".parse::().is_err()); assert!(".".parse::().is_err()); } #[test] fn exponentiated_dot() { + #[cfg(target_has_reliable_f16)] + assert!(".e0".parse::().is_err()); assert!(".e0".parse::().is_err()); assert!(".e0".parse::().is_err()); } #[test] fn lonely_sign() { - assert!("+".parse::().is_err()); - assert!("-".parse::().is_err()); + #[cfg(target_has_reliable_f16)] + assert!("+".parse::().is_err()); + assert!("-".parse::().is_err()); + assert!("+".parse::().is_err()); } #[test] fn whitespace() { + #[cfg(target_has_reliable_f16)] + assert!("1.0 ".parse::().is_err()); assert!(" 1.0".parse::().is_err()); assert!("1.0 ".parse::().is_err()); } #[test] fn nan() { + #[cfg(target_has_reliable_f16)] + { + assert!("NaN".parse::().unwrap().is_nan()); + assert!("-NaN".parse::().unwrap().is_nan()); + } + assert!("NaN".parse::().unwrap().is_nan()); + assert!("-NaN".parse::().unwrap().is_nan()); + assert!("NaN".parse::().unwrap().is_nan()); + assert!("-NaN".parse::().unwrap().is_nan()); } #[test] fn inf() { - assert_eq!("inf".parse(), Ok(f64::INFINITY)); - assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY)); + #[cfg(target_has_reliable_f16)] + { + assert_eq!("inf".parse(), Ok(f16::INFINITY)); + assert_eq!("-inf".parse(), Ok(f16::NEG_INFINITY)); + } + assert_eq!("inf".parse(), Ok(f32::INFINITY)); assert_eq!("-inf".parse(), Ok(f32::NEG_INFINITY)); + + assert_eq!("inf".parse(), Ok(f64::INFINITY)); + assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY)); } #[test] fn massive_exponent() { + #[cfg(target_has_reliable_f16)] + { + let max = i16::MAX; + assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY)); + assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f16)); + assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY)); + } + + let max = i32::MAX; + assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY)); + assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f32)); + assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY)); + let max = i64::MAX; assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY)); - assert_eq!(format!("1e-{max}000").parse(), Ok(0.0)); + assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f64)); assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY)); } diff --git a/library/coretests/tests/num/dec2flt/parse.rs b/library/coretests/tests/num/dec2flt/parse.rs index 59be3915052d8..dccb6b5528d4c 100644 --- a/library/coretests/tests/num/dec2flt/parse.rs +++ b/library/coretests/tests/num/dec2flt/parse.rs @@ -10,6 +10,9 @@ fn new_dec(e: i64, m: u64) -> Decimal { fn missing_pieces() { let permutations = &[".e", "1e", "e4", "e", ".12e", "321.e", "32.12e+", "12.32e-"]; for &s in permutations { + #[cfg(target_has_reliable_f16)] + assert_eq!(dec2flt::(s), Err(pfe_invalid())); + assert_eq!(dec2flt::(s), Err(pfe_invalid())); assert_eq!(dec2flt::(s), Err(pfe_invalid())); } } @@ -17,15 +20,31 @@ fn missing_pieces() { #[test] fn invalid_chars() { let invalid = "r,?(&input) == error, "did not reject invalid {:?}", input); + + #[cfg(target_has_reliable_f16)] + assert_eq!( + dec2flt::(&input), + Err(pfe_invalid()), + "f16 did not reject invalid {input:?}", + ); + assert_eq!( + dec2flt::(&input), + Err(pfe_invalid()), + "f32 did not reject invalid {input:?}", + ); + assert_eq!( + dec2flt::(&input), + Err(pfe_invalid()), + "f64 did not reject invalid {input:?}", + ); } } } diff --git a/library/coretests/tests/num/flt2dec/mod.rs b/library/coretests/tests/num/flt2dec/mod.rs index c64bb0a30720a..ce36db33d05f3 100644 --- a/library/coretests/tests/num/flt2dec/mod.rs +++ b/library/coretests/tests/num/flt2dec/mod.rs @@ -16,7 +16,7 @@ mod random; pub fn decode_finite(v: T) -> Decoded { match decode(v).1 { FullDecoded::Finite(decoded) => decoded, - full_decoded => panic!("expected finite, got {full_decoded:?} instead"), + full_decoded => panic!("expected finite, got {full_decoded:?} instead for {v:?}"), } } @@ -75,6 +75,11 @@ macro_rules! try_fixed { }) } +#[cfg(target_has_reliable_f16)] +fn ldexp_f16(a: f16, b: i32) -> f16 { + ldexp_f64(a as f64, b) as f16 +} + fn ldexp_f32(a: f32, b: i32) -> f32 { ldexp_f64(a as f64, b) as f32 } @@ -176,6 +181,13 @@ trait TestableFloat: DecodableFloat + fmt::Display { fn ldexpi(f: i64, exp: isize) -> Self; } +#[cfg(target_has_reliable_f16)] +impl TestableFloat for f16 { + fn ldexpi(f: i64, exp: isize) -> Self { + f as Self * (exp as Self).exp2() + } +} + impl TestableFloat for f32 { fn ldexpi(f: i64, exp: isize) -> Self { f as Self * (exp as Self).exp2() @@ -225,6 +237,76 @@ macro_rules! check_exact_one { // // [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion // ftp://ftp.ee.lbl.gov/testbase-report.ps.Z +// or https://www.icir.org/vern/papers/testbase-report.pdf + +#[cfg(target_has_reliable_f16)] +pub fn f16_shortest_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + // 0.0999145507813 + // 0.0999755859375 + // 0.100036621094 + check_shortest!(f(0.1f16) => b"1", 0); + + // 0.3330078125 + // 0.333251953125 (1/3 in the default rounding) + // 0.33349609375 + check_shortest!(f(1.0f16/3.0) => b"3333", 0); + + // 10^1 * 0.3138671875 + // 10^1 * 0.3140625 + // 10^1 * 0.3142578125 + check_shortest!(f(3.14f16) => b"314", 1); + + // 10^18 * 0.31415916243714048 + // 10^18 * 0.314159196796878848 + // 10^18 * 0.314159231156617216 + check_shortest!(f(3.1415e4f16) => b"3141", 5); + + // regression test for decoders + // 10^2 * 0.31984375 + // 10^2 * 0.32 + // 10^2 * 0.3203125 + check_shortest!(f(ldexp_f16(1.0, 5)) => b"32", 2); + + // 10^5 * 0.65472 + // 10^5 * 0.65504 + // 10^5 * 0.65536 + check_shortest!(f(f16::MAX) => b"655", 5); + + // 10^-4 * 0.60975551605224609375 + // 10^-4 * 0.6103515625 + // 10^-4 * 0.61094760894775390625 + check_shortest!(f(f16::MIN_POSITIVE) => b"6104", -4); + + // 10^-9 * 0 + // 10^-9 * 0.59604644775390625 + // 10^-8 * 0.11920928955078125 + let minf16 = ldexp_f16(1.0, -24); + check_shortest!(f(minf16) => b"6", -7); +} + +#[cfg(target_has_reliable_f16)] +pub fn f16_exact_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + let minf16 = ldexp_f16(1.0, -24); + + check_exact!(f(0.1f16) => b"999755859375 ", -1); + check_exact!(f(0.5f16) => b"5 ", 0); + check_exact!(f(1.0f16/3.0) => b"333251953125 ", 0); + check_exact!(f(3.141f16) => b"3140625 ", 1); + check_exact!(f(3.141e4f16) => b"31408 ", 5); + check_exact!(f(f16::MAX) => b"65504 ", 5); + check_exact!(f(f16::MIN_POSITIVE) => b"6103515625 ", -4); + check_exact!(f(minf16) => b"59604644775390625", -7); + + // FIXME(f16_f128): these should gain the check_exact_one tests like `f32` and `f64` have, + // but these values are not easy to generate. The algorithm from the Paxon paper [1] needs + // to be adapted to binary16. +} pub fn f32_shortest_sanity_test(mut f: F) where @@ -553,23 +635,45 @@ where assert_eq!(to_string(f, 1.9971e20, Minus, 1), "199710000000000000000.0"); assert_eq!(to_string(f, 1.9971e20, Minus, 8), "199710000000000000000.00000000"); - assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", "")); - assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", "")); - assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", "")); - - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", "")); - assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", "")); - assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", "")); + #[cfg(target_has_reliable_f16)] + { + // f16 + assert_eq!(to_string(f, f16::MAX, Minus, 0), "65500"); + assert_eq!(to_string(f, f16::MAX, Minus, 1), "65500.0"); + assert_eq!(to_string(f, f16::MAX, Minus, 8), "65500.00000000"); + + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, 0), "0.00000006"); + assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006"); + assert_eq!(to_string(f, minf16, Minus, 9), "0.000000060"); + } - assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", "")); - assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", "")); - assert_eq!(to_string(f, f64::MAX, Minus, 8), format!("17976931348623157{:0>292}.00000000", "")); + { + // f32 + assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", "")); + + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", "")); + } - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", "")); + { + // f64 + assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", "")); + assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", "")); + assert_eq!( + to_string(f, f64::MAX, Minus, 8), + format!("17976931348623157{:0>292}.00000000", "") + ); + + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", "")); + } if cfg!(miri) { // Miri is too slow @@ -655,27 +759,45 @@ where assert_eq!(to_string(f, 1.0e23, Minus, (23, 24), false), "100000000000000000000000"); assert_eq!(to_string(f, 1.0e23, Minus, (24, 25), false), "1e23"); - assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38"); - assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38"); - assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", "")); - - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45"); - assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45"); - assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", "")); - - assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308"); - assert_eq!( - to_string(f, f64::MAX, Minus, (-308, 309), false), - format!("17976931348623157{:0>292}", "") - ); - assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308"); + #[cfg(target_has_reliable_f16)] + { + // f16 + assert_eq!(to_string(f, f16::MAX, Minus, (-2, 2), false), "6.55e4"); + assert_eq!(to_string(f, f16::MAX, Minus, (-4, 4), false), "6.55e4"); + assert_eq!(to_string(f, f16::MAX, Minus, (-5, 5), false), "65500"); + + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, (-2, 2), false), "6e-8"); + assert_eq!(to_string(f, minf16, Minus, (-7, 7), false), "6e-8"); + assert_eq!(to_string(f, minf16, Minus, (-8, 8), false), "0.00000006"); + } - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324"); - assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324"); + { + // f32 + assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", "")); + + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", "")); + } + { + // f64 + assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308"); + assert_eq!( + to_string(f, f64::MAX, Minus, (-308, 309), false), + format!("17976931348623157{:0>292}", "") + ); + assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308"); + + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324"); + assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324"); + } assert_eq!(to_string(f, 1.1, Minus, (i16::MIN, i16::MAX), false), "1.1"); } @@ -791,6 +913,26 @@ where "9.999999999999999547481118258862586856139387236908078193664550781250000e-7" ); + #[cfg(target_has_reliable_f16)] + { + assert_eq!(to_string(f, f16::MAX, Minus, 1, false), "7e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 2, false), "6.6e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 4, false), "6.550e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 5, false), "6.5504e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 6, false), "6.55040e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 16, false), "6.550400000000000e4"); + + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, 1, false), "6e-8"); + assert_eq!(to_string(f, minf16, Minus, 2, false), "6.0e-8"); + assert_eq!(to_string(f, minf16, Minus, 4, false), "5.960e-8"); + assert_eq!(to_string(f, minf16, Minus, 8, false), "5.9604645e-8"); + assert_eq!(to_string(f, minf16, Minus, 16, false), "5.960464477539062e-8"); + assert_eq!(to_string(f, minf16, Minus, 17, false), "5.9604644775390625e-8"); + assert_eq!(to_string(f, minf16, Minus, 18, false), "5.96046447753906250e-8"); + assert_eq!(to_string(f, minf16, Minus, 24, false), "5.96046447753906250000000e-8"); + } + assert_eq!(to_string(f, f32::MAX, Minus, 1, false), "3e38"); assert_eq!(to_string(f, f32::MAX, Minus, 2, false), "3.4e38"); assert_eq!(to_string(f, f32::MAX, Minus, 4, false), "3.403e38"); @@ -1069,6 +1211,13 @@ where "0.000000999999999999999954748111825886258685613938723690807819366455078125000" ); + #[cfg(target_has_reliable_f16)] + { + assert_eq!(to_string(f, f16::MAX, Minus, 0), "65504"); + assert_eq!(to_string(f, f16::MAX, Minus, 1), "65504.0"); + assert_eq!(to_string(f, f16::MAX, Minus, 2), "65504.00"); + } + assert_eq!(to_string(f, f32::MAX, Minus, 0), "340282346638528859811704183484516925440"); assert_eq!(to_string(f, f32::MAX, Minus, 1), "340282346638528859811704183484516925440.0"); assert_eq!(to_string(f, f32::MAX, Minus, 2), "340282346638528859811704183484516925440.00"); @@ -1078,6 +1227,21 @@ where return; } + #[cfg(target_has_reliable_f16)] + { + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, 0), "0"); + assert_eq!(to_string(f, minf16, Minus, 1), "0.0"); + assert_eq!(to_string(f, minf16, Minus, 2), "0.00"); + assert_eq!(to_string(f, minf16, Minus, 4), "0.0000"); + assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006"); + assert_eq!(to_string(f, minf16, Minus, 10), "0.0000000596"); + assert_eq!(to_string(f, minf16, Minus, 15), "0.000000059604645"); + assert_eq!(to_string(f, minf16, Minus, 20), "0.00000005960464477539"); + assert_eq!(to_string(f, minf16, Minus, 24), "0.000000059604644775390625"); + assert_eq!(to_string(f, minf16, Minus, 32), "0.00000005960464477539062500000000"); + } + let minf32 = ldexp_f32(1.0, -149); assert_eq!(to_string(f, minf32, Minus, 0), "0"); assert_eq!(to_string(f, minf32, Minus, 1), "0.0"); diff --git a/library/coretests/tests/num/flt2dec/random.rs b/library/coretests/tests/num/flt2dec/random.rs index 586b49df7d9b2..7386139aaced5 100644 --- a/library/coretests/tests/num/flt2dec/random.rs +++ b/library/coretests/tests/num/flt2dec/random.rs @@ -79,6 +79,20 @@ where (npassed, nignored) } +#[cfg(target_has_reliable_f16)] +pub fn f16_random_equivalence_test(f: F, g: G, k: usize, n: usize) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + let mut rng = crate::test_rng(); + let f16_range = Uniform::new(0x0001u16, 0x7c00).unwrap(); + iterate("f16_random_equivalence_test", k, n, f, g, |_| { + let x = f16::from_bits(f16_range.sample(&mut rng)); + decode_finite(x) + }); +} + pub fn f32_random_equivalence_test(f: F, g: G, k: usize, n: usize) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, @@ -105,6 +119,24 @@ where }); } +#[cfg(target_has_reliable_f16)] +pub fn f16_exhaustive_equivalence_test(f: F, g: G, k: usize) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + // Unlike the other float types, `f16` is small enough that these exhaustive tests + // can run in less than a second so we don't need to ignore it. + + // iterate from 0x0001 to 0x7bff, i.e., all finite ranges + let (npassed, nignored) = + iterate("f16_exhaustive_equivalence_test", k, 0x7bff, f, g, |i: usize| { + let x = f16::from_bits(i as u16 + 1); + decode_finite(x) + }); + assert_eq!((npassed, nignored), (29735, 2008)); +} + pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, @@ -133,6 +165,17 @@ fn shortest_random_equivalence_test() { f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); + #[cfg(target_has_reliable_f16)] + f16_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); +} + +#[test] +#[cfg_attr(miri, ignore)] // Miri is to slow +#[cfg(target_has_reliable_f16)] +fn shortest_f16_exhaustive_equivalence_test() { + // see the f32 version + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f16_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); } #[test] @@ -158,6 +201,23 @@ fn shortest_f64_hard_random_equivalence_test() { f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 100_000_000); } +#[test] +#[cfg(target_has_reliable_f16)] +fn exact_f16_random_equivalence_test() { + use core::num::flt2dec::strategy::dragon::format_exact as fallback; + // Miri is too slow + let n = if cfg!(miri) { 3 } else { 1_000 }; + + for k in 1..21 { + f16_random_equivalence_test( + |d, buf| format_exact_opt(d, buf, i16::MIN), + |d, buf| fallback(d, buf, i16::MIN), + k, + n, + ); + } +} + #[test] fn exact_f32_random_equivalence_test() { use core::num::flt2dec::strategy::dragon::format_exact as fallback; diff --git a/library/coretests/tests/num/flt2dec/strategy/dragon.rs b/library/coretests/tests/num/flt2dec/strategy/dragon.rs index be25fee3f6c71..43bb6024f9cee 100644 --- a/library/coretests/tests/num/flt2dec/strategy/dragon.rs +++ b/library/coretests/tests/num/flt2dec/strategy/dragon.rs @@ -18,6 +18,8 @@ fn test_mul_pow10() { fn shortest_sanity_test() { f64_shortest_sanity_test(format_shortest); f32_shortest_sanity_test(format_shortest); + #[cfg(target_has_reliable_f16)] + f16_shortest_sanity_test(format_shortest); more_shortest_sanity_test(format_shortest); } @@ -41,6 +43,9 @@ fn exact_sanity_test() { f64_exact_sanity_test(format_exact); } f32_exact_sanity_test(format_exact); + + #[cfg(target_has_reliable_f16)] + f16_exact_sanity_test(format_exact); } #[test] diff --git a/library/coretests/tests/num/flt2dec/strategy/grisu.rs b/library/coretests/tests/num/flt2dec/strategy/grisu.rs index 9b2f0453de73e..117191e0c8fdb 100644 --- a/library/coretests/tests/num/flt2dec/strategy/grisu.rs +++ b/library/coretests/tests/num/flt2dec/strategy/grisu.rs @@ -38,6 +38,8 @@ fn test_max_pow10_no_more_than() { fn shortest_sanity_test() { f64_shortest_sanity_test(format_shortest); f32_shortest_sanity_test(format_shortest); + #[cfg(target_has_reliable_f16)] + f16_shortest_sanity_test(format_shortest); more_shortest_sanity_test(format_shortest); } @@ -50,6 +52,8 @@ fn exact_sanity_test() { f64_exact_sanity_test(format_exact); } f32_exact_sanity_test(format_exact); + #[cfg(target_has_reliable_f16)] + f16_exact_sanity_test(format_exact); } #[test] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5c1d2deb4811a..ca04a381271b2 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -305,7 +305,6 @@ #![feature(iter_advance_by)] #![feature(iter_next_chunk)] #![feature(lang_items)] -#![feature(let_chains)] #![feature(link_cfg)] #![feature(linkage)] #![feature(macro_metavar_expr_concat)] diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 8bf6d8335159b..a3be2cdf738f5 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -25,15 +25,36 @@ impl Drop for Handler { } } -#[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", +#[cfg(all( + not(miri), + any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + ), +))] +mod thread_info; + +// miri doesn't model signals nor stack overflows and this code has some +// synchronization properties that we don't want to expose to user code, +// hence we disable it on miri. +#[cfg(all( + not(miri), + any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + ) ))] mod imp { use libc::{ @@ -46,22 +67,13 @@ mod imp { use libc::{mmap64, mprotect, munmap}; use super::Handler; - use crate::cell::Cell; + use super::thread_info::{delete_current_info, set_current_info, with_current_info}; use crate::ops::Range; use crate::sync::OnceLock; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; - use crate::{io, mem, ptr, thread}; - - // We use a TLS variable to store the address of the guard page. While TLS - // variables are not guaranteed to be signal-safe, this works out in practice - // since we make sure to write to the variable before the signal stack is - // installed, thereby ensuring that the variable is always allocated when - // the signal handler is called. - thread_local! { - // FIXME: use `Range` once that implements `Copy`. - static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) }; - } + use crate::thread::with_current_name; + use crate::{io, mem, panic, ptr}; // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages // (unmapped pages) at the end of every thread's stack, so if a thread ends @@ -93,29 +105,35 @@ mod imp { info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { - let (start, end) = GUARD.get(); // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`. - let addr = unsafe { (*info).si_addr().addr() }; + let fault_addr = unsafe { (*info).si_addr().addr() }; + + // `with_current_info` expects that the process aborts after it is + // called. If the signal was not caused by a memory access, this might + // not be true. We detect this by noticing that the `si_addr` field is + // zero if the signal is synthetic. + if fault_addr != 0 { + with_current_info(|thread_info| { + // If the faulting address is within the guard page, then we print a + // message saying so and abort. + if let Some(thread_info) = thread_info + && thread_info.guard_page_range.contains(&fault_addr) + { + let name = thread_info.thread_name.as_deref().unwrap_or(""); + rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + rtabort!("stack overflow"); + } + }) + } - // If the faulting address is within the guard page, then we print a - // message saying so and abort. - if start <= addr && addr < end { - thread::with_current_name(|name| { - let name = name.unwrap_or(""); - rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); - }); + // Unregister ourselves by reverting back to the default behavior. + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; + action.sa_sigaction = SIG_DFL; + // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction + unsafe { sigaction(signum, &action, ptr::null_mut()) }; - rtabort!("stack overflow"); - } else { - // Unregister ourselves by reverting back to the default behavior. - // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" - let mut action: sigaction = unsafe { mem::zeroed() }; - action.sa_sigaction = SIG_DFL; - // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction - unsafe { sigaction(signum, &action, ptr::null_mut()) }; - - // See comment above for why this function returns. - } + // See comment above for why this function returns. } static PAGE_SIZE: Atomic = AtomicUsize::new(0); @@ -128,9 +146,7 @@ mod imp { pub unsafe fn init() { PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); - // Always write to GUARD to ensure the TLS variable is allocated. - let guard = unsafe { install_main_guard().unwrap_or(0..0) }; - GUARD.set((guard.start, guard.end)); + let mut guard_page_range = unsafe { install_main_guard() }; // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" let mut action: sigaction = unsafe { mem::zeroed() }; @@ -145,7 +161,13 @@ mod imp { let handler = unsafe { make_handler(true) }; MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); + + if let Some(guard_page_range) = guard_page_range.take() { + let thread_name = with_current_name(|name| name.map(Box::from)); + set_current_info(guard_page_range, thread_name); + } } + action.sa_flags = SA_SIGINFO | SA_ONSTACK; action.sa_sigaction = signal_handler as sighandler_t; // SAFETY: only overriding signals if the default is set @@ -214,9 +236,10 @@ mod imp { } if !main_thread { - // Always write to GUARD to ensure the TLS variable is allocated. - let guard = unsafe { current_guard() }.unwrap_or(0..0); - GUARD.set((guard.start, guard.end)); + if let Some(guard_page_range) = unsafe { current_guard() } { + let thread_name = with_current_name(|name| name.map(Box::from)); + set_current_info(guard_page_range, thread_name); + } } // SAFETY: assuming stack_t is zero-initializable @@ -261,6 +284,8 @@ mod imp { // a mapping that started one page earlier, so walk back a page and unmap from there. unsafe { munmap(data.sub(page_size), sigstack_size + page_size) }; } + + delete_current_info(); } /// Modern kernels on modern hardware can have dynamic signal stack sizes. @@ -590,17 +615,20 @@ mod imp { // usually have fewer qualms about forwards compatibility, since the runtime // is shipped with the OS): // -#[cfg(not(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", - target_os = "cygwin", -)))] +#[cfg(any( + miri, + not(any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + target_os = "cygwin", + )) +))] mod imp { pub unsafe fn init() {} diff --git a/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs new file mode 100644 index 0000000000000..e81429b98a6c7 --- /dev/null +++ b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs @@ -0,0 +1,129 @@ +//! TLS, but async-signal-safe. +//! +//! Unfortunately, because thread local storage isn't async-signal-safe, we +//! cannot soundly use it in our stack overflow handler. While this works +//! without problems on most platforms, it can lead to undefined behaviour +//! on others (such as GNU/Linux). Luckily, the POSIX specification documents +//! two thread-specific values that can be accessed in asynchronous signal +//! handlers: the value of `pthread_self()` and the address of `errno`. As +//! `pthread_t` is an opaque platform-specific type, we use the address of +//! `errno` here. As it is thread-specific and does not change over the +//! lifetime of a thread, we can use `&errno` as a key for a `BTreeMap` +//! that stores thread-specific data. +//! +//! Concurrent access to this map is synchronized by two locks – an outer +//! [`Mutex`] and an inner spin lock that also remembers the identity of +//! the lock owner: +//! * The spin lock is the primary means of synchronization: since it only +//! uses native atomics, it can be soundly used inside the signal handle +//! as opposed to [`Mutex`], which might not be async-signal-safe. +//! * The [`Mutex`] prevents busy-waiting in the setup logic, as all accesses +//! there are performed with the [`Mutex`] held, which makes the spin-lock +//! redundant in the common case. +//! * Finally, by using the `errno` address as the locked value of the spin +//! lock, we can detect cases where a SIGSEGV occurred while the thread +//! info is being modified. + +use crate::collections::BTreeMap; +use crate::hint::spin_loop; +use crate::ops::Range; +use crate::sync::Mutex; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sys::os::errno_location; + +pub struct ThreadInfo { + pub guard_page_range: Range, + pub thread_name: Option>, +} + +static LOCK: Mutex<()> = Mutex::new(()); +static SPIN_LOCK: AtomicUsize = AtomicUsize::new(0); +// This uses a `BTreeMap` instead of a hashmap since it supports constant +// initialization and automatically reduces the amount of memory used when +// items are removed. +static mut THREAD_INFO: BTreeMap = BTreeMap::new(); + +struct UnlockOnDrop; + +impl Drop for UnlockOnDrop { + fn drop(&mut self) { + SPIN_LOCK.store(0, Ordering::Release); + } +} + +/// Get the current thread's information, if available. +/// +/// Calling this function might freeze other threads if they attempt to modify +/// their thread information. Thus, the caller should ensure that the process +/// is aborted shortly after this function is called. +/// +/// This function is guaranteed to be async-signal-safe if `f` is too. +pub fn with_current_info(f: impl FnOnce(Option<&ThreadInfo>) -> R) -> R { + let this = errno_location().addr(); + let mut attempt = 0; + let _guard = loop { + // If we are just spinning endlessly, it's very likely that the thread + // modifying the thread info map has a lower priority than us and will + // not continue until we stop running. Just give up in that case. + if attempt == 10_000_000 { + rtprintpanic!("deadlock in SIGSEGV handler"); + return f(None); + } + + match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => break UnlockOnDrop, + Err(owner) if owner == this => { + rtabort!("a thread received SIGSEGV while modifying its stack overflow information") + } + // Spin until the lock can be acquired – there is nothing better to + // do. This is unfortunately a priority hole, but a stack overflow + // is a fatal error anyway. + Err(_) => { + spin_loop(); + attempt += 1; + } + } + }; + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased. + let thread_info = unsafe { &*(&raw const THREAD_INFO) }; + f(thread_info.get(&this)) +} + +fn spin_lock_in_setup(this: usize) -> UnlockOnDrop { + loop { + match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => return UnlockOnDrop, + Err(owner) if owner == this => { + unreachable!("the thread info setup logic isn't recursive") + } + // This function is always called with the outer lock held, + // meaning the only time locking can fail is if another thread has + // encountered a stack overflow. Since that will abort the process, + // we just stop the current thread until that time. We use `pause` + // instead of spinning to avoid priority inversion. + // SAFETY: this doesn't have any safety preconditions. + Err(_) => drop(unsafe { libc::pause() }), + } + } +} + +pub fn set_current_info(guard_page_range: Range, thread_name: Option>) { + let this = errno_location().addr(); + let _lock_guard = LOCK.lock(); + let _spin_guard = spin_lock_in_setup(this); + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot be aliased. + let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) }; + thread_info.insert(this, ThreadInfo { guard_page_range, thread_name }); +} + +pub fn delete_current_info() { + let this = errno_location().addr(); + let _lock_guard = LOCK.lock(); + let _spin_guard = spin_lock_in_setup(this); + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased. + let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) }; + thread_info.remove(&this); +} diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index ff63b8c62d3a0..0c8e66335609b 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -64,7 +64,7 @@ dependencies = [ "tracing-subscriber", "tracing-tree", "walkdir", - "windows 0.57.0", + "windows", "xz2", ] @@ -89,9 +89,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.17" +version = "1.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" dependencies = [ "shlex", ] @@ -703,7 +703,7 @@ dependencies = [ "ntapi", "objc2-core-foundation", "objc2-io-kit", - "windows 0.61.1", + "windows", ] [[package]] @@ -916,16 +916,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets", -] - [[package]] name = "windows" version = "0.61.1" @@ -933,7 +923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.61.0", + "windows-core", "windows-future", "windows-link", "windows-numerics", @@ -945,19 +935,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.0", -] - -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets", + "windows-core", ] [[package]] @@ -966,10 +944,10 @@ version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", + "windows-implement", + "windows-interface", "windows-link", - "windows-result 0.3.2", + "windows-result", "windows-strings", ] @@ -979,21 +957,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -1005,17 +972,6 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-interface" version = "0.59.1" @@ -1039,19 +995,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-result" version = "0.3.2" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index e34de924cc18a..b12b3dfc7b2a6 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -32,7 +32,7 @@ test = false # Most of the time updating these dependencies requires modifications to the # bootstrap codebase(e.g., https://github.com/rust-lang/rust/issues/124565); # otherwise, some targets will fail. That's why these dependencies are explicitly pinned. -cc = "=1.2.17" +cc = "=1.2.23" cmake = "=0.1.54" build_helper = { path = "../build_helper" } @@ -70,7 +70,7 @@ tracing-tree = { version = "0.4.0", optional = true } version = "1.0.0" [target.'cfg(windows)'.dependencies.windows] -version = "0.57" +version = "0.61" features = [ "Win32_Foundation", "Win32_Security", diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 85c682a46c5d1..374884d8a9a07 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -342,7 +342,7 @@ fn format_rusage_data(child: Child) -> Option { use windows::Win32::System::Threading::GetProcessTimes; use windows::Win32::System::Time::FileTimeToSystemTime; - let handle = HANDLE(child.as_raw_handle() as isize); + let handle = HANDLE(child.as_raw_handle()); let mut user_filetime = Default::default(); let mut user_time = Default::default(); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index b2dc509ddca0e..27791825aa0f6 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3554,7 +3554,7 @@ impl Step for TestFloatParse { builder.ensure(tool::TestFloatParse { host: self.host }); // Run any unit tests in the crate - let cargo_test = tool::prepare_tool_cargo( + let mut cargo_test = tool::prepare_tool_cargo( builder, compiler, Mode::ToolStd, @@ -3564,6 +3564,7 @@ impl Step for TestFloatParse { SourceType::InTree, &[], ); + cargo_test.allow_features(tool::TestFloatParse::ALLOW_FEATURES); run_cargo_test(cargo_test, &[], &[], crate_name, bootstrap_host, builder); @@ -3578,6 +3579,7 @@ impl Step for TestFloatParse { SourceType::InTree, &[], ); + cargo_run.allow_features(tool::TestFloatParse::ALLOW_FEATURES); if !matches!(env::var("FLOAT_PARSE_TESTS_NO_SKIP_HUGE").as_deref(), Ok("1") | Ok("true")) { cargo_run.args(["--", "--skip-huge"]); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index ac568eab2e8a5..678aa9b01e4ad 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1259,6 +1259,10 @@ pub struct TestFloatParse { pub host: TargetSelection, } +impl TestFloatParse { + pub const ALLOW_FEATURES: &'static str = "f16,cfg_target_has_reliable_f16_f128"; +} + impl Step for TestFloatParse { type Output = ToolBuildResult; const ONLY_HOSTS: bool = true; @@ -1280,7 +1284,7 @@ impl Step for TestFloatParse { path: "src/etc/test-float-parse", source_type: SourceType::InTree, extra_features: Vec::new(), - allow_features: "", + allow_features: Self::ALLOW_FEATURES, cargo_args: Vec::new(), artifact_kind: ToolArtifactKind::Binary, }) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index cc4fa953ddc5c..af3e3cc37b928 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1388,7 +1388,7 @@ impl<'a> Builder<'a> { // Windows doesn't need dylib path munging because the dlls for the // compiler live next to the compiler and the system will find them // automatically. - if cfg!(windows) { + if cfg!(any(windows, target_os = "cygwin")) { return; } diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index ceac24d4315c7..5c9e30706ee0d 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -22,43 +22,13 @@ //! everything. use std::collections::HashSet; +use std::iter; use std::path::{Path, PathBuf}; -use std::{env, iter}; use crate::core::config::TargetSelection; use crate::utils::exec::{BootstrapCommand, command}; use crate::{Build, CLang, GitRepo}; -/// Finds archiver tool for the given target if possible. -/// FIXME(onur-ozkan): This logic should be replaced by calling into the `cc` crate. -fn cc2ar(cc: &Path, target: TargetSelection, default_ar: PathBuf) -> Option { - if let Some(ar) = env::var_os(format!("AR_{}", target.triple.replace('-', "_"))) { - Some(PathBuf::from(ar)) - } else if let Some(ar) = env::var_os("AR") { - Some(PathBuf::from(ar)) - } else if target.is_msvc() { - None - } else if target.contains("musl") || target.contains("openbsd") { - Some(PathBuf::from("ar")) - } else if target.contains("vxworks") { - Some(PathBuf::from("wr-ar")) - } else if target.contains("-nto-") { - if target.starts_with("i586") { - Some(PathBuf::from("ntox86-ar")) - } else if target.starts_with("aarch64") { - Some(PathBuf::from("ntoaarch64-ar")) - } else if target.starts_with("x86_64") { - Some(PathBuf::from("ntox86_64-ar")) - } else { - panic!("Unknown architecture, cannot determine archiver for Neutrino QNX"); - } - } else if target.contains("android") || target.contains("-wasi") { - Some(cc.parent().unwrap().join(PathBuf::from("llvm-ar"))) - } else { - Some(default_ar) - } -} - /// Creates and configures a new [`cc::Build`] instance for the given target. fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build { let mut cfg = cc::Build::new(); @@ -140,7 +110,7 @@ pub fn find_target(build: &Build, target: TargetSelection) { let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) { ar } else { - cc2ar(compiler.path(), target, PathBuf::from(cfg.get_archiver().get_program())) + cfg.try_get_archiver().map(|c| PathBuf::from(c.get_program())).ok() }; build.cc.borrow_mut().insert(target, compiler.clone()); diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs index 43d61ce02c5af..225fb7619b55a 100644 --- a/src/bootstrap/src/utils/cc_detect/tests.rs +++ b/src/bootstrap/src/utils/cc_detect/tests.rs @@ -5,119 +5,6 @@ use super::*; use crate::core::config::{Target, TargetSelection}; use crate::{Build, Config, Flags}; -#[test] -fn test_cc2ar_env_specific() { - let triple = "x86_64-unknown-linux-gnu"; - let key = "AR_x86_64_unknown_linux_gnu"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::set_var(key, "custom-ar") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var(key) }; - assert_eq!(result, Some(PathBuf::from("custom-ar"))); -} - -#[test] -fn test_cc2ar_musl() { - let triple = "x86_64-unknown-linux-musl"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_x86_64_unknown_linux_musl") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ar"))); -} - -#[test] -fn test_cc2ar_openbsd() { - let triple = "x86_64-unknown-openbsd"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_x86_64_unknown_openbsd") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/cc"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ar"))); -} - -#[test] -fn test_cc2ar_vxworks() { - let triple = "armv7-wrs-vxworks"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_armv7_wrs_vxworks") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("wr-ar"))); -} - -#[test] -fn test_cc2ar_nto_i586() { - let triple = "i586-unknown-nto-something"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_i586_unknown_nto_something") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ntox86-ar"))); -} - -#[test] -fn test_cc2ar_nto_aarch64() { - let triple = "aarch64-unknown-nto-something"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_aarch64_unknown_nto_something") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ntoaarch64-ar"))); -} - -#[test] -fn test_cc2ar_nto_x86_64() { - let triple = "x86_64-unknown-nto-something"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_x86_64_unknown_nto_something") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ntox86_64-ar"))); -} - -#[test] -#[should_panic(expected = "Unknown architecture, cannot determine archiver for Neutrino QNX")] -fn test_cc2ar_nto_unknown() { - let triple = "powerpc-unknown-nto-something"; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR_powerpc_unknown_nto_something") }; - // SAFETY: bootstrap tests run on a single thread - unsafe { env::remove_var("AR") }; - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let _ = cc2ar(cc, target, default_ar); -} - #[test] fn test_ndk_compiler_c() { let ndk_path = PathBuf::from("/ndk"); diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index b29c1fb3889c2..f2c3e8c0df464 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -130,7 +130,7 @@ pub fn is_debug_info(name: &str) -> bool { /// Returns the corresponding relative library directory that the compiler's /// dylibs will be found in. pub fn libdir(target: TargetSelection) -> &'static str { - if target.is_windows() { "bin" } else { "lib" } + if target.is_windows() || target.contains("cygwin") { "bin" } else { "lib" } } /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path. diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs index 4949518de79b0..887deb41ca8bc 100644 --- a/src/bootstrap/src/utils/job.rs +++ b/src/bootstrap/src/utils/job.rs @@ -66,7 +66,6 @@ mod for_windows { // Enable the Windows Error Reporting dialog which msys disables, // so we can JIT debug rustc let mode = SetErrorMode(THREAD_ERROR_MODE::default()); - let mode = THREAD_ERROR_MODE(mode); SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX); // Create a new job object for us to use diff --git a/src/bootstrap/src/utils/shared_helpers.rs b/src/bootstrap/src/utils/shared_helpers.rs index 1297a53d48804..08e1c21e58e7c 100644 --- a/src/bootstrap/src/utils/shared_helpers.rs +++ b/src/bootstrap/src/utils/shared_helpers.rs @@ -20,7 +20,7 @@ use std::str::FromStr; /// Returns the environment variable which the dynamic library lookup path /// resides in for this platform. pub fn dylib_path_var() -> &'static str { - if cfg!(target_os = "windows") { + if cfg!(any(target_os = "windows", target_os = "cygwin")) { "PATH" } else if cfg!(target_vendor = "apple") { "DYLD_LIBRARY_PATH" diff --git a/src/etc/test-float-parse/Cargo.toml b/src/etc/test-float-parse/Cargo.toml index 8a9c5322ef7bd..e407e322f9e19 100644 --- a/src/etc/test-float-parse/Cargo.toml +++ b/src/etc/test-float-parse/Cargo.toml @@ -13,3 +13,10 @@ rayon = "1" [lib] name = "test_float_parse" + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + # Internal features aren't marked known config by default + 'cfg(target_has_reliable_f16)', +] diff --git a/src/etc/test-float-parse/src/gen_/subnorm.rs b/src/etc/test-float-parse/src/gen_/subnorm.rs index 4fe3b90a3ddf4..654f324b9b011 100644 --- a/src/etc/test-float-parse/src/gen_/subnorm.rs +++ b/src/etc/test-float-parse/src/gen_/subnorm.rs @@ -1,4 +1,3 @@ -use std::cmp::min; use std::fmt::Write; use std::ops::RangeInclusive; @@ -83,7 +82,13 @@ where } fn new() -> Self { - Self { iter: F::Int::ZERO..=min(F::Int::ONE << 22, F::MAN_BITS.try_into().unwrap()) } + let upper_lim = if F::MAN_BITS >= 22 { + F::Int::ONE << 22 + } else { + (F::Int::ONE << F::MAN_BITS) - F::Int::ONE + }; + + Self { iter: F::Int::ZERO..=upper_lim } } fn write_string(s: &mut String, ctx: Self::WriteCtx) { diff --git a/src/etc/test-float-parse/src/lib.rs b/src/etc/test-float-parse/src/lib.rs index 3c3ef5802b6aa..0bd4878f9a626 100644 --- a/src/etc/test-float-parse/src/lib.rs +++ b/src/etc/test-float-parse/src/lib.rs @@ -1,3 +1,7 @@ +#![feature(f16)] +#![feature(cfg_target_has_reliable_f16_f128)] +#![expect(internal_features)] // reliable_f16_f128 + mod traits; mod ui; mod validate; @@ -114,6 +118,9 @@ pub fn register_tests(cfg: &Config) -> Vec { let mut tests = Vec::new(); // Register normal generators for all floats. + + #[cfg(target_has_reliable_f16)] + register_float::(&mut tests, cfg); register_float::(&mut tests, cfg); register_float::(&mut tests, cfg); diff --git a/src/etc/test-float-parse/src/traits.rs b/src/etc/test-float-parse/src/traits.rs index 57e702b7d0913..65a8721bfa5cd 100644 --- a/src/etc/test-float-parse/src/traits.rs +++ b/src/etc/test-float-parse/src/traits.rs @@ -98,7 +98,7 @@ macro_rules! impl_int { } } -impl_int!(u32, i32; u64, i64); +impl_int!(u16, i16; u32, i32; u64, i64); /// Floating point types. pub trait Float: @@ -170,6 +170,9 @@ macro_rules! impl_float { impl_float!(f32, u32; f64, u64); +#[cfg(target_has_reliable_f16)] +impl_float!(f16, u16); + /// A test generator. Should provide an iterator that produces unique patterns to parse. /// /// The iterator needs to provide a `WriteCtx` (could be anything), which is then used to diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index b4210e7b51819..b4003044e207b 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -11,7 +11,6 @@ #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(never_type)] #![feature(round_char_boundary)] #![feature(test)] diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 93f7b1cb7cf2c..3b544d8b82817 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -37,7 +37,7 @@ libc = "0.2" miow = "0.6" [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_System_Diagnostics_Debug", diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs index 0258a76c3e703..837e1b31cac50 100644 --- a/src/tools/miri/src/shims/native_lib.rs +++ b/src/tools/miri/src/shims/native_lib.rs @@ -117,7 +117,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut info = std::mem::MaybeUninit::::uninit(); unsafe { if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 { - if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() + let info = info.assume_init(); + #[cfg(target_os = "cygwin")] + let fname_ptr = info.dli_fname.as_ptr(); + #[cfg(not(target_os = "cygwin"))] + let fname_ptr = info.dli_fname; + if std::ffi::CStr::from_ptr(fname_ptr).to_str().unwrap() != _lib_path.to_str().unwrap() { return None; diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 4195258af8854..9bb06c31c5c06 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -429,10 +429,13 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "winapi-util", "winapi-x86_64-pc-windows-gnu", "windows", + "windows-collections", "windows-core", + "windows-future", "windows-implement", "windows-interface", "windows-link", + "windows-numerics", "windows-result", "windows-strings", "windows-sys", diff --git a/tests/run-make/core-no-oom-handling/rmake.rs b/tests/run-make/core-no-oom-handling/rmake.rs index a9e2b33e21073..5194d77311448 100644 --- a/tests/run-make/core-no-oom-handling/rmake.rs +++ b/tests/run-make/core-no-oom-handling/rmake.rs @@ -6,7 +6,7 @@ use run_make_support::{rustc, source_root}; fn main() { rustc() - .edition("2021") + .edition("2024") .arg("-Dwarnings") .crate_type("rlib") .input(source_root().join("library/core/src/lib.rs"))