Avoid using floating-point values in expand_fcvt_to_sint.

Compute the bound values for expand_fcvt_to_sint using bitwise integer
arithmetic rather than floating-point arithmetic, to avoid relying on
host floating point arithmetic.
This commit is contained in:
Dan Gohman
2018-03-12 10:28:42 -07:00
parent f04e02c0a1
commit 11eddafef8
2 changed files with 53 additions and 4 deletions

View File

@@ -538,6 +538,17 @@ impl Ieee32 {
Ieee32(exponent << t) Ieee32(exponent << t)
} }
/// Create an `Ieee32` number representing the greatest negative value
/// not convertable from f32 to a signed integer with width n.
pub fn fcvt_to_sint_negative_overflow<I: Into<i32>>(n: I) -> Ieee32 {
let n = n.into();
debug_assert!(n < 32);
debug_assert!(23 + 1 - n < 32);
Self::with_bits(
(1u32 << (32 - 1)) | Self::pow2(n - 1).0 | (1u32 << (23 + 1 - n)),
)
}
/// Return self negated. /// Return self negated.
pub fn neg(self) -> Ieee32 { pub fn neg(self) -> Ieee32 {
Ieee32(self.0 ^ (1 << 31)) Ieee32(self.0 ^ (1 << 31))
@@ -590,6 +601,17 @@ impl Ieee64 {
Ieee64(exponent << t) Ieee64(exponent << t)
} }
/// Create an `Ieee64` number representing the greatest negative value
/// not convertable from f64 to a signed integer with width n.
pub fn fcvt_to_sint_negative_overflow<I: Into<i64>>(n: I) -> Ieee64 {
let n = n.into();
debug_assert!(n < 64);
debug_assert!(52 + 1 - n < 64);
Self::with_bits(
(1u64 << (64 - 1)) | Self::pow2(n - 1).0 | (1u64 << (52 + 1 - n)),
)
}
/// Return self negated. /// Return self negated.
pub fn neg(self) -> Ieee64 { pub fn neg(self) -> Ieee64 {
Ieee64(self.0 ^ (1 << 63)) Ieee64(self.0 ^ (1 << 63))
@@ -857,6 +879,15 @@ mod tests {
assert_eq!(Ieee32::pow2(1).neg().to_string(), "-0x1.000000p1"); assert_eq!(Ieee32::pow2(1).neg().to_string(), "-0x1.000000p1");
} }
#[test]
fn fcvt_to_sint_negative_overflow_ieee32() {
for n in &[8, 16] {
assert_eq!(-((1u32 << (n - 1)) as f32) - 1.0, unsafe {
mem::transmute(Ieee32::fcvt_to_sint_negative_overflow(*n))
});
}
}
#[test] #[test]
fn format_ieee64() { fn format_ieee64() {
assert_eq!(Ieee64::with_float(0.0).to_string(), "0.0"); assert_eq!(Ieee64::with_float(0.0).to_string(), "0.0");
@@ -985,4 +1016,13 @@ mod tests {
assert_eq!(Ieee64::pow2(1).neg().to_string(), "-0x1.0000000000000p1"); assert_eq!(Ieee64::pow2(1).neg().to_string(), "-0x1.0000000000000p1");
} }
#[test]
fn fcvt_to_sint_negative_overflow_ieee64() {
for n in &[8, 16, 32] {
assert_eq!(-((1u64 << (n - 1)) as f64) - 1.0, unsafe {
mem::transmute(Ieee64::fcvt_to_sint_negative_overflow(*n))
});
}
}
} }

View File

@@ -376,13 +376,22 @@ fn expand_fcvt_to_sint(
let mut overflow_cc = FloatCC::LessThan; let mut overflow_cc = FloatCC::LessThan;
let output_bits = ty.lane_bits(); let output_bits = ty.lane_bits();
let flimit = match xty { let flimit = match xty {
ir::types::F32 => pos.ins().f32const(Ieee32::pow2(output_bits - 1).neg()), // An f32 can represent `i16::min_value() - 1` exactly with precision to spare, so
// there are values less than -2^(N-1) that convert correctly to INT_MIN.
ir::types::F32 => {
pos.ins().f32const(if output_bits < 32 {
overflow_cc = FloatCC::LessThanOrEqual;
Ieee32::fcvt_to_sint_negative_overflow(output_bits)
} else {
Ieee32::pow2(output_bits - 1).neg()
})
}
ir::types::F64 => { ir::types::F64 => {
// An f64 can represent `i32::min_value() - 1` exactly with precision to spare, so // An f64 can represent `i32::min_value() - 1` exactly with precision to spare, so
// there are values less than -2^(N-1) that convert correctly to INT_MIN. // there are values less than -2^(N-1) that convert correctly to INT_MIN.
pos.ins().f64const(if output_bits < 64 { pos.ins().f64const(if output_bits < 64 {
overflow_cc = FloatCC::LessThanOrEqual; overflow_cc = FloatCC::LessThanOrEqual;
Ieee64::with_float(-((1u64 << (output_bits - 1)) as f64) - 1.0) Ieee64::fcvt_to_sint_negative_overflow(output_bits)
} else { } else {
Ieee64::pow2(output_bits - 1).neg() Ieee64::pow2(output_bits - 1).neg()
}) })
@@ -394,8 +403,8 @@ fn expand_fcvt_to_sint(
// Finally, we could have a positive value that is too large. // Finally, we could have a positive value that is too large.
let fzero = match xty { let fzero = match xty {
ir::types::F32 => pos.ins().f32const(Ieee32::with_float(0.0)), ir::types::F32 => pos.ins().f32const(Ieee32::with_bits(0)),
ir::types::F64 => pos.ins().f64const(Ieee64::with_float(0.0)), ir::types::F64 => pos.ins().f64const(Ieee64::with_bits(0)),
_ => panic!("Can't convert {}", xty), _ => panic!("Can't convert {}", xty),
}; };
let overflow = pos.ins().fcmp(FloatCC::GreaterThanOrEqual, x, fzero); let overflow = pos.ins().fcmp(FloatCC::GreaterThanOrEqual, x, fzero);