Skip to content

Commit d87559a

Browse files
committed
TEST ONLY: remove some x87 work-arounds and make some functions extern-C to try to reproduce an issue
1 parent 3940fba commit d87559a

File tree

5 files changed

+32
-120
lines changed

5 files changed

+32
-120
lines changed

library/core/src/num/f128.rs

-4
Original file line numberDiff line numberDiff line change
@@ -439,10 +439,6 @@ impl f128 {
439439
#[unstable(feature = "f128", issue = "116909")]
440440
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
441441
pub const fn classify(self) -> FpCategory {
442-
// Other float types suffer from various platform bugs that violate the usual IEEE semantics
443-
// and also make bitwise classification not always work reliably. However, `f128` cannot fit
444-
// into any other float types so this is not a concern, and we can rely on bit patterns.
445-
446442
let bits = self.to_bits();
447443
match (bits & Self::MAN_MASK, bits & Self::EXP_MASK) {
448444
(0, Self::EXP_MASK) => FpCategory::Infinite,

library/core/src/num/f16.rs

+7-37
Original file line numberDiff line numberDiff line change
@@ -424,43 +424,13 @@ impl f16 {
424424
#[unstable(feature = "f16", issue = "116909")]
425425
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
426426
pub const fn classify(self) -> FpCategory {
427-
// A previous implementation for f32/f64 tried to only use bitmask-based checks,
428-
// using `to_bits` to transmute the float to its bit repr and match on that.
429-
// If we only cared about being "technically" correct, that's an entirely legit
430-
// implementation.
431-
//
432-
// Unfortunately, there are platforms out there that do not correctly implement the IEEE
433-
// float semantics Rust relies on: some hardware flushes denormals to zero, and some
434-
// platforms convert to `f32` to perform operations without properly rounding back (e.g.
435-
// WASM, see llvm/llvm-project#96437). These are platforms bugs, and Rust will misbehave on
436-
// such platforms, but we can at least try to make things seem as sane as possible by being
437-
// careful here.
438-
// Cc https://github.com/rust-lang/rust/issues/114479
439-
if self.is_infinite() {
440-
// Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
441-
FpCategory::Infinite
442-
} else if self.is_nan() {
443-
// And it may not be NaN, as it can simply be an "overextended" finite value.
444-
FpCategory::Nan
445-
} else {
446-
// However, std can't simply compare to zero to check for zero, either,
447-
// as correctness requires avoiding equality tests that may be Subnormal == -0.0
448-
// because it may be wrong under "denormals are zero" and "flush to zero" modes.
449-
// Most of std's targets don't use those, but they are used for thumbv7neon.
450-
// So, this does use bitpattern matching for the rest. On x87, due to the incorrect
451-
// float codegen on this hardware, this doesn't actually return a right answer for NaN
452-
// because it cannot correctly discern between a floating point NaN, and some normal
453-
// floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
454-
// we are fine.
455-
// FIXME(jubilee): This probably could at least answer things correctly for Infinity,
456-
// like the f64 version does, but I need to run more checks on how things go on x86.
457-
// I fear losing mantissa data that would have answered that differently.
458-
let b = self.to_bits();
459-
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
460-
(0, 0) => FpCategory::Zero,
461-
(_, 0) => FpCategory::Subnormal,
462-
_ => FpCategory::Normal,
463-
}
427+
let b = self.to_bits();
428+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
429+
(0, Self::EXP_MASK) => FpCategory::Infinite,
430+
(_, Self::EXP_MASK) => FpCategory::Nan,
431+
(0, 0) => FpCategory::Zero,
432+
(_, 0) => FpCategory::Subnormal,
433+
_ => FpCategory::Normal,
464434
}
465435
}
466436

library/core/src/num/f32.rs

+10-39
Original file line numberDiff line numberDiff line change
@@ -652,42 +652,13 @@ impl f32 {
652652
#[stable(feature = "rust1", since = "1.0.0")]
653653
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
654654
pub const fn classify(self) -> FpCategory {
655-
// A previous implementation tried to only use bitmask-based checks,
656-
// using f32::to_bits to transmute the float to its bit repr and match on that.
657-
// If we only cared about being "technically" correct, that's an entirely legit
658-
// implementation.
659-
//
660-
// Unfortunately, there is hardware out there that does not correctly implement the IEEE
661-
// float semantics Rust relies on: x87 uses a too-large mantissa and exponent, and some
662-
// hardware flushes subnormals to zero. These are platforms bugs, and Rust will misbehave on
663-
// such hardware, but we can at least try to make things seem as sane as possible by being
664-
// careful here.
665-
// Cc https://github.com/rust-lang/rust/issues/114479
666-
if self.is_infinite() {
667-
// A value may compare unequal to infinity, despite having a "full" exponent mask.
668-
FpCategory::Infinite
669-
} else if self.is_nan() {
670-
// And it may not be NaN, as it can simply be an "overextended" finite value.
671-
FpCategory::Nan
672-
} else {
673-
// However, std can't simply compare to zero to check for zero, either,
674-
// as correctness requires avoiding equality tests that may be Subnormal == -0.0
675-
// because it may be wrong under "denormals are zero" and "flush to zero" modes.
676-
// Most of std's targets don't use those, but they are used for thumbv7neon.
677-
// So, this does use bitpattern matching for the rest. On x87, due to the incorrect
678-
// float codegen on this hardware, this doesn't actually return a right answer for NaN
679-
// because it cannot correctly discern between a floating point NaN, and some normal
680-
// floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
681-
// we are fine.
682-
// FIXME(jubilee): This probably could at least answer things correctly for Infinity,
683-
// like the f64 version does, but I need to run more checks on how things go on x86.
684-
// I fear losing mantissa data that would have answered that differently.
685-
let b = self.to_bits();
686-
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
687-
(0, 0) => FpCategory::Zero,
688-
(_, 0) => FpCategory::Subnormal,
689-
_ => FpCategory::Normal,
690-
}
655+
let b = self.to_bits();
656+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
657+
(0, Self::EXP_MASK) => FpCategory::Infinite,
658+
(_, Self::EXP_MASK) => FpCategory::Nan,
659+
(0, 0) => FpCategory::Zero,
660+
(_, 0) => FpCategory::Subnormal,
661+
_ => FpCategory::Normal,
691662
}
692663
}
693664

@@ -773,7 +744,7 @@ impl f32 {
773744
#[inline]
774745
#[unstable(feature = "float_next_up_down", issue = "91399")]
775746
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
776-
pub const fn next_up(self) -> Self {
747+
pub const extern "C" fn next_up(self) -> Self {
777748
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
778749
// denormals to zero. This is in general unsound and unsupported, but here
779750
// we do our best to still produce the correct result on such targets.
@@ -822,7 +793,7 @@ impl f32 {
822793
#[inline]
823794
#[unstable(feature = "float_next_up_down", issue = "91399")]
824795
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
825-
pub const fn next_down(self) -> Self {
796+
pub const extern "C" fn next_down(self) -> Self {
826797
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
827798
// denormals to zero. This is in general unsound and unsupported, but here
828799
// we do our best to still produce the correct result on such targets.
@@ -1114,7 +1085,7 @@ impl f32 {
11141085
#[stable(feature = "float_bits_conv", since = "1.20.0")]
11151086
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
11161087
#[inline]
1117-
pub const fn to_bits(self) -> u32 {
1088+
pub const extern "C" fn to_bits(self) -> u32 {
11181089
// SAFETY: `u32` is a plain old datatype so we can always transmute to it.
11191090
unsafe { mem::transmute(self) }
11201091
}

library/core/src/num/f64.rs

+10-35
Original file line numberDiff line numberDiff line change
@@ -651,38 +651,13 @@ impl f64 {
651651
#[stable(feature = "rust1", since = "1.0.0")]
652652
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
653653
pub const fn classify(self) -> FpCategory {
654-
// A previous implementation tried to only use bitmask-based checks,
655-
// using f64::to_bits to transmute the float to its bit repr and match on that.
656-
// If we only cared about being "technically" correct, that's an entirely legit
657-
// implementation.
658-
//
659-
// Unfortunately, there is hardware out there that does not correctly implement the IEEE
660-
// float semantics Rust relies on: x87 uses a too-large exponent, and some hardware flushes
661-
// subnormals to zero. These are platforms bugs, and Rust will misbehave on such hardware,
662-
// but we can at least try to make things seem as sane as possible by being careful here.
663-
// Cc https://github.com/rust-lang/rust/issues/114479
664-
//
665-
// Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
666-
// And it may not be NaN, as it can simply be an "overextended" finite value.
667-
if self.is_nan() {
668-
FpCategory::Nan
669-
} else {
670-
// However, std can't simply compare to zero to check for zero, either,
671-
// as correctness requires avoiding equality tests that may be Subnormal == -0.0
672-
// because it may be wrong under "denormals are zero" and "flush to zero" modes.
673-
// Most of std's targets don't use those, but they are used for thumbv7neon.
674-
// So, this does use bitpattern matching for the rest. On x87, due to the incorrect
675-
// float codegen on this hardware, this doesn't actually return a right answer for NaN
676-
// because it cannot correctly discern between a floating point NaN, and some normal
677-
// floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
678-
// we are fine.
679-
let b = self.to_bits();
680-
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
681-
(0, Self::EXP_MASK) => FpCategory::Infinite,
682-
(0, 0) => FpCategory::Zero,
683-
(_, 0) => FpCategory::Subnormal,
684-
_ => FpCategory::Normal,
685-
}
654+
let b = self.to_bits();
655+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
656+
(0, Self::EXP_MASK) => FpCategory::Infinite,
657+
(_, Self::EXP_MASK) => FpCategory::Nan,
658+
(0, 0) => FpCategory::Zero,
659+
(_, 0) => FpCategory::Subnormal,
660+
_ => FpCategory::Normal,
686661
}
687662
}
688663

@@ -786,7 +761,7 @@ impl f64 {
786761
#[inline]
787762
#[unstable(feature = "float_next_up_down", issue = "91399")]
788763
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
789-
pub const fn next_up(self) -> Self {
764+
pub const extern "C" fn next_up(self) -> Self {
790765
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
791766
// denormals to zero. This is in general unsound and unsupported, but here
792767
// we do our best to still produce the correct result on such targets.
@@ -835,7 +810,7 @@ impl f64 {
835810
#[inline]
836811
#[unstable(feature = "float_next_up_down", issue = "91399")]
837812
#[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")]
838-
pub const fn next_down(self) -> Self {
813+
pub const extern "C" fn next_down(self) -> Self {
839814
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
840815
// denormals to zero. This is in general unsound and unsupported, but here
841816
// we do our best to still produce the correct result on such targets.
@@ -1155,7 +1130,7 @@ impl f64 {
11551130
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
11561131
#[must_use]
11571132
#[inline]
1158-
pub const fn from_bits(v: u64) -> Self {
1133+
pub const extern "C" fn from_bits(v: u64) -> Self {
11591134
// It turns out the safety issues with sNaN were overblown! Hooray!
11601135
// SAFETY: `u64` is a plain old datatype so we can always transmute from it.
11611136
unsafe { mem::transmute(v) }

library/std/src/f32/tests.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -348,11 +348,11 @@ fn test_is_sign_negative() {
348348

349349
#[test]
350350
fn test_next_up() {
351-
let tiny = f32::from_bits(TINY_BITS);
352-
let tiny_up = f32::from_bits(TINY_UP_BITS);
353-
let max_down = f32::from_bits(MAX_DOWN_BITS);
354-
let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS);
355-
let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS);
351+
let tiny = crate::hint::black_box(f32::from_bits(TINY_BITS));
352+
let tiny_up = crate::hint::black_box(f32::from_bits(TINY_UP_BITS));
353+
let max_down = crate::hint::black_box(f32::from_bits(MAX_DOWN_BITS));
354+
let largest_subnormal = crate::hint::black_box(f32::from_bits(LARGEST_SUBNORMAL_BITS));
355+
let smallest_normal = crate::hint::black_box(f32::from_bits(SMALLEST_NORMAL_BITS));
356356
assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN);
357357
assert_f32_biteq!(f32::MIN.next_up(), -max_down);
358358
assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0);

0 commit comments

Comments
 (0)