cranelift: Fix fmin/fmax when dealing with zeroes (#4373)
`fmin`/`fmax` are defined as returning -0.0 as smaller than 0.0. This is not how the IEEE754 views these values and the interpreter was returning the wrong value in these operations since it was just using the standard IEEE754 comparisons. This also tries to preserve NaN information by avoiding passing NaN's through any operation that could canonicalize it.
This commit is contained in:
@@ -698,17 +698,21 @@ where
|
||||
Opcode::Fneg => assign(Value::neg(arg(0)?)?),
|
||||
Opcode::Fabs => assign(Value::abs(arg(0)?)?),
|
||||
Opcode::Fcopysign => binary(Value::copysign, arg(0)?, arg(1)?)?,
|
||||
Opcode::Fmin => choose(
|
||||
Value::is_nan(&arg(0)?)? || Value::lt(&arg(0)?, &arg(1)?)?,
|
||||
arg(0)?,
|
||||
arg(1)?,
|
||||
),
|
||||
Opcode::Fmin => assign(match (arg(0)?, arg(1)?) {
|
||||
(a, _) if a.is_nan()? => a,
|
||||
(_, b) if b.is_nan()? => b,
|
||||
(a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => a,
|
||||
(a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => b,
|
||||
(a, b) => a.min(b)?,
|
||||
}),
|
||||
Opcode::FminPseudo => unimplemented!("FminPseudo"),
|
||||
Opcode::Fmax => choose(
|
||||
Value::is_nan(&arg(0)?)? || Value::gt(&arg(0)?, &arg(1)?)?,
|
||||
arg(0)?,
|
||||
arg(1)?,
|
||||
),
|
||||
Opcode::Fmax => assign(match (arg(0)?, arg(1)?) {
|
||||
(a, _) if a.is_nan()? => a,
|
||||
(_, b) if b.is_nan()? => b,
|
||||
(a, b) if a.is_zero()? && b.is_zero()? && a.is_negative()? => b,
|
||||
(a, b) if a.is_zero()? && b.is_zero()? && b.is_negative()? => a,
|
||||
(a, b) => a.max(b)?,
|
||||
}),
|
||||
Opcode::FmaxPseudo => unimplemented!("FmaxPseudo"),
|
||||
Opcode::Ceil => unimplemented!("Ceil"),
|
||||
Opcode::Floor => unimplemented!("Floor"),
|
||||
|
||||
@@ -26,6 +26,9 @@ pub trait Value: Clone + From<DataValue> {
|
||||
fn convert(self, kind: ValueConversionKind) -> ValueResult<Self>;
|
||||
fn concat(self, other: Self) -> ValueResult<Self>;
|
||||
|
||||
fn is_negative(&self) -> ValueResult<bool>;
|
||||
fn is_zero(&self) -> ValueResult<bool>;
|
||||
|
||||
fn max(self, other: Self) -> ValueResult<Self>;
|
||||
fn min(self, other: Self) -> ValueResult<Self>;
|
||||
|
||||
@@ -394,6 +397,22 @@ impl Value for DataValue {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_negative(&self) -> ValueResult<bool> {
|
||||
match self {
|
||||
DataValue::F32(f) => Ok(f.is_negative()),
|
||||
DataValue::F64(f) => Ok(f.is_negative()),
|
||||
_ => Err(ValueError::InvalidType(ValueTypeClass::Float, self.ty())),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> ValueResult<bool> {
|
||||
match self {
|
||||
DataValue::F32(f) => Ok(f.is_zero()),
|
||||
DataValue::F64(f) => Ok(f.is_zero()),
|
||||
_ => Err(ValueError::InvalidType(ValueTypeClass::Float, self.ty())),
|
||||
}
|
||||
}
|
||||
|
||||
fn max(self, other: Self) -> ValueResult<Self> {
|
||||
if Value::gt(&self, &other)? {
|
||||
Ok(self)
|
||||
|
||||
Reference in New Issue
Block a user