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:
@@ -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))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user