Fix a corner case in fcvt_to_sint.i32.f64 legalization.
An f64 can represent multiple values in the range INT_MIN-1 < x <= INT_MIN which all truncate to INT_MIN, so comparing the input value against INT_MIN is not good enough. Instead, detect overflow on x <= INT_MIN-1 when INT_MIN-1 is an exact floating point value.
This commit is contained in:
@@ -6,11 +6,9 @@
|
|||||||
//! module in the meta language.
|
//! module in the meta language.
|
||||||
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::{i32, u32};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::{i32, u32};
|
||||||
|
|
||||||
/// 64-bit immediate integer operand.
|
/// 64-bit immediate integer operand.
|
||||||
///
|
///
|
||||||
@@ -547,7 +545,6 @@ impl Ieee32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `Ieee32` representing the number `x`.
|
/// Create a new `Ieee32` representing the number `x`.
|
||||||
#[cfg(test)]
|
|
||||||
pub fn with_float(x: f32) -> Ieee32 {
|
pub fn with_float(x: f32) -> Ieee32 {
|
||||||
Ieee32(unsafe { mem::transmute(x) })
|
Ieee32(unsafe { mem::transmute(x) })
|
||||||
}
|
}
|
||||||
@@ -600,7 +597,6 @@ impl Ieee64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `Ieee64` representing the number `x`.
|
/// Create a new `Ieee64` representing the number `x`.
|
||||||
#[cfg(test)]
|
|
||||||
pub fn with_float(x: f64) -> Ieee64 {
|
pub fn with_float(x: f64) -> Ieee64 {
|
||||||
Ieee64(unsafe { mem::transmute(x) })
|
Ieee64(unsafe { mem::transmute(x) })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,15 +222,25 @@ fn expand_fcvt_to_sint(inst: ir::Inst, func: &mut ir::Function, cfg: &mut Contro
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Check for case 1: INT_MIN is the correct result.
|
// Check for case 1: INT_MIN is the correct result.
|
||||||
// We use a `ueq` condition here because that can be translated into a single branch, and we
|
// Determine the smallest floating point number that would convert to INT_MIN.
|
||||||
// already know that we don't have a NaN.
|
let mut overflow_cc = FloatCC::LessThan;
|
||||||
let fintmin = match xty {
|
let output_bits = ty.lane_bits();
|
||||||
ir::types::F32 => pos.ins().f32const(Ieee32::pow2(ty.lane_bits() - 1).neg()),
|
let flimit = match xty {
|
||||||
ir::types::F64 => pos.ins().f64const(Ieee64::pow2(ty.lane_bits() - 1).neg()),
|
ir::types::F32 => pos.ins().f32const(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)
|
||||||
|
} else {
|
||||||
|
Ieee64::pow2(output_bits - 1).neg()
|
||||||
|
})
|
||||||
|
}
|
||||||
_ => panic!("Can't convert {}", xty),
|
_ => panic!("Can't convert {}", xty),
|
||||||
};
|
};
|
||||||
let in_range = pos.ins().fcmp(FloatCC::UnorderedOrEqual, x, fintmin);
|
let overflow = pos.ins().fcmp(overflow_cc, x, flimit);
|
||||||
pos.ins().trapz(in_range, ir::TrapCode::IntegerOverflow);
|
pos.ins().trapnz(overflow, ir::TrapCode::IntegerOverflow);
|
||||||
|
|
||||||
pos.ins().jump(done, &[]);
|
pos.ins().jump(done, &[]);
|
||||||
pos.insert_ebb(done);
|
pos.insert_ebb(done);
|
||||||
|
|||||||
Reference in New Issue
Block a user