Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport LLVM apfloat commit to rustc_apfloat #77368

Merged
merged 2 commits into from
Oct 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions compiler/rustc_apfloat/src/ieee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1511,11 +1511,16 @@ impl<S: Semantics, T: Semantics> FloatConvert<IeeeFloat<T>> for IeeeFloat<S> {
sig::set_bit(&mut r.sig, T::PRECISION - 1);
}

// gcc forces the Quiet bit on, which means (float)(double)(float_sNan)
// does not give you back the same bits. This is dubious, and we
// don't currently do it. You're really supposed to get
// an invalid operation signal at runtime, but nobody does that.
status = Status::OK;
// Convert of sNaN creates qNaN and raises an exception (invalid op).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what "raises an exception" means here, but wouldn't this mean the cast fails when run in Miri?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's the purpose of signaling NaNs. Quoting Wikipedia:

Signaling NaNs, or sNaNs, are special forms of a NaN that, when consumed by most operations, should raise the invalid operation exception and then, if appropriate, be "quieted" into a qNaN that may then propagate.

This is precisely what we (and LLVM) do here :).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was my understanding that this is generally not implemented, i.e., sNaNs behave like qNaNs with modern compilers on modern hardware? But maybe I misunderstood.

Given that your new testcases passes, it does not seem like this "exception" actually does anything in Miri though, so I guess it's okay.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah by default trapping is off, but it can be enabled by e.g. feenableexcept. If trapping is off, only some exception flags are set in a specific "status word" register, readable by e.g. _statusfp on Windows (couldn't find the unix analog).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On x86, it's the fstsw instruction.

// This also guarantees that a sNaN does not become Inf on a truncation
// that loses all payload bits.
if self.is_signaling() {
// Quiet signaling NaN.
sig::set_bit(&mut r.sig, T::QNAN_BIT);
status = Status::INVALID_OP;
} else {
status = Status::OK;
}
} else {
*loses_info = false;
status = Status::OK;
Expand Down
34 changes: 25 additions & 9 deletions compiler/rustc_apfloat/tests/ieee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,17 @@ fn fma() {
}
}

#[test]
fn issue_69532() {
let f = Double::from_bits(0x7FF0_0000_0000_0001u64 as u128);
let mut loses_info = false;
let sta = f.convert(&mut loses_info);
let r: Single = sta.value;
assert!(loses_info);
assert!(r.is_nan());
assert_eq!(sta.status, Status::INVALID_OP);
}

#[test]
fn min_num() {
let f1 = Double::from_f64(1.0);
Expand Down Expand Up @@ -1492,27 +1503,32 @@ fn convert() {
assert_eq!(4294967295.0, test.to_f64());
assert!(!loses_info);

let test = Single::snan(None);
let x87_snan = X87DoubleExtended::snan(None);
let test: X87DoubleExtended = test.convert(&mut loses_info).value;
assert!(test.bitwise_eq(x87_snan));
assert!(!loses_info);

let test = Single::qnan(None);
let x87_qnan = X87DoubleExtended::qnan(None);
let test: X87DoubleExtended = test.convert(&mut loses_info).value;
assert!(test.bitwise_eq(x87_qnan));
assert!(!loses_info);

let test = X87DoubleExtended::snan(None);
let test: X87DoubleExtended = test.convert(&mut loses_info).value;
assert!(test.bitwise_eq(x87_snan));
let test = Single::snan(None);
let sta = test.convert(&mut loses_info);
let test: X87DoubleExtended = sta.value;
assert!(test.is_nan());
assert!(!test.is_signaling());
assert!(!loses_info);
assert_eq!(sta.status, Status::INVALID_OP);

let test = X87DoubleExtended::qnan(None);
let test: X87DoubleExtended = test.convert(&mut loses_info).value;
assert!(test.bitwise_eq(x87_qnan));
assert!(!loses_info);

let test = X87DoubleExtended::snan(None);
let sta = test.convert(&mut loses_info);
let test: X87DoubleExtended = sta.value;
assert!(test.is_nan());
assert!(!test.is_signaling());
assert!(!loses_info);
assert_eq!(sta.status, Status::INVALID_OP);
}

#[test]
Expand Down
24 changes: 24 additions & 0 deletions src/test/ui/issues/issue-69532.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// run-pass
#![feature(const_fn_transmute)]

const fn make_nans() -> (f64, f64, f32, f32) {
let nan1: f64 = unsafe { std::mem::transmute(0x7FF0_0001_0000_0001u64) };
let nan2: f64 = unsafe { std::mem::transmute(0x7FF0_0000_0000_0001u64) };

let nan1_32 = nan1 as f32;
let nan2_32 = nan2 as f32;

(nan1, nan2, nan1_32, nan2_32)
}

static NANS: (f64, f64, f32, f32) = make_nans();

fn main() {
let (nan1, nan2, nan1_32, nan2_32) = NANS;

assert!(nan1.is_nan());
assert!(nan2.is_nan());

assert!(nan1_32.is_nan());
assert!(nan2_32.is_nan());
}