From 11eddafef8cea4ced9cc36a47bb511119320f8ff Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 12 Mar 2018 10:28:42 -0700 Subject: [PATCH] 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. --- lib/cretonne/src/ir/immediates.rs | 40 ++++++++++++++++++++++++ lib/cretonne/src/isa/intel/enc_tables.rs | 17 +++++++--- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/lib/cretonne/src/ir/immediates.rs b/lib/cretonne/src/ir/immediates.rs index 2d3b1236ef..69c9aab51a 100644 --- a/lib/cretonne/src/ir/immediates.rs +++ b/lib/cretonne/src/ir/immediates.rs @@ -538,6 +538,17 @@ impl Ieee32 { 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>(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. pub fn neg(self) -> Ieee32 { Ieee32(self.0 ^ (1 << 31)) @@ -590,6 +601,17 @@ impl Ieee64 { 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>(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. pub fn neg(self) -> Ieee64 { Ieee64(self.0 ^ (1 << 63)) @@ -857,6 +879,15 @@ mod tests { 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] fn format_ieee64() { 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"); } + + #[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)) + }); + } + } } diff --git a/lib/cretonne/src/isa/intel/enc_tables.rs b/lib/cretonne/src/isa/intel/enc_tables.rs index 00c5ab99cb..d3f62adf7c 100644 --- a/lib/cretonne/src/isa/intel/enc_tables.rs +++ b/lib/cretonne/src/isa/intel/enc_tables.rs @@ -376,13 +376,22 @@ fn expand_fcvt_to_sint( let mut overflow_cc = FloatCC::LessThan; let output_bits = ty.lane_bits(); 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 => { // 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. pos.ins().f64const(if output_bits < 64 { overflow_cc = FloatCC::LessThanOrEqual; - Ieee64::with_float(-((1u64 << (output_bits - 1)) as f64) - 1.0) + Ieee64::fcvt_to_sint_negative_overflow(output_bits) } else { 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. let fzero = match xty { - ir::types::F32 => pos.ins().f32const(Ieee32::with_float(0.0)), - ir::types::F64 => pos.ins().f64const(Ieee64::with_float(0.0)), + ir::types::F32 => pos.ins().f32const(Ieee32::with_bits(0)), + ir::types::F64 => pos.ins().f64const(Ieee64::with_bits(0)), _ => panic!("Can't convert {}", xty), }; let overflow = pos.ins().fcmp(FloatCC::GreaterThanOrEqual, x, fzero);