Add "known to not contain non-arithmetic NaNs" to ExtraInfo in LLVM backend.

Not wired up yet.
This commit is contained in:
Nick Lewycky 2019-11-06 13:40:51 -08:00
parent 7715be9ccb
commit fafc7ad38c
2 changed files with 120 additions and 70 deletions

View File

@ -388,16 +388,14 @@ fn v128_into_int_vec(
info: ExtraInfo, info: ExtraInfo,
int_vec_ty: VectorType, int_vec_ty: VectorType,
) -> VectorValue { ) -> VectorValue {
let value = match info { let value = if info.has_pending_f32_nan() {
ExtraInfo::None => value, let value = builder.build_bitcast(value, intrinsics.f32x4_ty, "");
ExtraInfo::PendingF32NaN => { canonicalize_nans(builder, intrinsics, value)
let value = builder.build_bitcast(value, intrinsics.f32x4_ty, ""); } else if info.has_pending_f64_nan() {
canonicalize_nans(builder, intrinsics, value) let value = builder.build_bitcast(value, intrinsics.f64x2_ty, "");
} canonicalize_nans(builder, intrinsics, value)
ExtraInfo::PendingF64NaN => { } else {
let value = builder.build_bitcast(value, intrinsics.f64x2_ty, ""); value
canonicalize_nans(builder, intrinsics, value)
}
}; };
builder builder
.build_bitcast(value, int_vec_ty, "") .build_bitcast(value, int_vec_ty, "")
@ -448,7 +446,7 @@ fn v128_into_f32x4(
value: BasicValueEnum, value: BasicValueEnum,
info: ExtraInfo, info: ExtraInfo,
) -> VectorValue { ) -> VectorValue {
let value = if info == ExtraInfo::PendingF64NaN { let value = if info.has_pending_f64_nan() {
let value = builder.build_bitcast(value, intrinsics.f64x2_ty, ""); let value = builder.build_bitcast(value, intrinsics.f64x2_ty, "");
canonicalize_nans(builder, intrinsics, value) canonicalize_nans(builder, intrinsics, value)
} else { } else {
@ -467,7 +465,7 @@ fn v128_into_f64x2(
value: BasicValueEnum, value: BasicValueEnum,
info: ExtraInfo, info: ExtraInfo,
) -> VectorValue { ) -> VectorValue {
let value = if info == ExtraInfo::PendingF32NaN { let value = if info.has_pending_f32_nan() {
let value = builder.build_bitcast(value, intrinsics.f32x4_ty, ""); let value = builder.build_bitcast(value, intrinsics.f32x4_ty, "");
canonicalize_nans(builder, intrinsics, value) canonicalize_nans(builder, intrinsics, value)
} else { } else {
@ -484,32 +482,30 @@ fn apply_pending_canonicalization(
value: BasicValueEnum, value: BasicValueEnum,
info: ExtraInfo, info: ExtraInfo,
) -> BasicValueEnum { ) -> BasicValueEnum {
match info { if info.has_pending_f32_nan() {
ExtraInfo::None => value, if value.get_type().is_vector_type()
ExtraInfo::PendingF32NaN => { || value.get_type() == intrinsics.i128_ty.as_basic_type_enum()
if value.get_type().is_vector_type() {
|| value.get_type() == intrinsics.i128_ty.as_basic_type_enum() let ty = value.get_type();
{ let value = builder.build_bitcast(value, intrinsics.f32x4_ty, "");
let ty = value.get_type(); let value = canonicalize_nans(builder, intrinsics, value);
let value = builder.build_bitcast(value, intrinsics.f32x4_ty, ""); builder.build_bitcast(value, ty, "")
let value = canonicalize_nans(builder, intrinsics, value); } else {
builder.build_bitcast(value, ty, "") canonicalize_nans(builder, intrinsics, value)
} else {
canonicalize_nans(builder, intrinsics, value)
}
} }
ExtraInfo::PendingF64NaN => { } else if info.has_pending_f64_nan() {
if value.get_type().is_vector_type() if value.get_type().is_vector_type()
|| value.get_type() == intrinsics.i128_ty.as_basic_type_enum() || value.get_type() == intrinsics.i128_ty.as_basic_type_enum()
{ {
let ty = value.get_type(); let ty = value.get_type();
let value = builder.build_bitcast(value, intrinsics.f64x2_ty, ""); let value = builder.build_bitcast(value, intrinsics.f64x2_ty, "");
let value = canonicalize_nans(builder, intrinsics, value); let value = canonicalize_nans(builder, intrinsics, value);
builder.build_bitcast(value, ty, "") builder.build_bitcast(value, ty, "")
} else { } else {
canonicalize_nans(builder, intrinsics, value) canonicalize_nans(builder, intrinsics, value)
}
} }
} else {
value
} }
} }
@ -2747,13 +2743,13 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
let (v1, v2) = state.pop2()?; let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res = builder.build_float_add(v1, v2, &state.var_name()); let res = builder.build_float_add(v1, v2, &state.var_name());
state.push1_extra(res, ExtraInfo::PendingF32NaN); state.push1_extra(res, ExtraInfo::pending_f32_nan());
} }
Operator::F64Add => { Operator::F64Add => {
let (v1, v2) = state.pop2()?; let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res = builder.build_float_add(v1, v2, &state.var_name()); let res = builder.build_float_add(v1, v2, &state.var_name());
state.push1_extra(res, ExtraInfo::PendingF64NaN); state.push1_extra(res, ExtraInfo::pending_f64_nan());
} }
Operator::F32x4Add => { Operator::F32x4Add => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?; let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
@ -2761,7 +2757,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
let v2 = v128_into_f32x4(builder, intrinsics, v2, i2); let v2 = v128_into_f32x4(builder, intrinsics, v2, i2);
let res = builder.build_float_add(v1, v2, &state.var_name()); let res = builder.build_float_add(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, ExtraInfo::PendingF32NaN); state.push1_extra(res, ExtraInfo::pending_f32_nan());
} }
Operator::F64x2Add => { Operator::F64x2Add => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?; let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
@ -2769,19 +2765,19 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
let v2 = v128_into_f64x2(builder, intrinsics, v2, i2); let v2 = v128_into_f64x2(builder, intrinsics, v2, i2);
let res = builder.build_float_add(v1, v2, &state.var_name()); let res = builder.build_float_add(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, ExtraInfo::PendingF64NaN); state.push1_extra(res, ExtraInfo::pending_f64_nan());
} }
Operator::F32Sub => { Operator::F32Sub => {
let (v1, v2) = state.pop2()?; let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res = builder.build_float_sub(v1, v2, &state.var_name()); let res = builder.build_float_sub(v1, v2, &state.var_name());
state.push1_extra(res, ExtraInfo::PendingF32NaN); state.push1_extra(res, ExtraInfo::pending_f32_nan());
} }
Operator::F64Sub => { Operator::F64Sub => {
let (v1, v2) = state.pop2()?; let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res = builder.build_float_sub(v1, v2, &state.var_name()); let res = builder.build_float_sub(v1, v2, &state.var_name());
state.push1_extra(res, ExtraInfo::PendingF64NaN); state.push1_extra(res, ExtraInfo::pending_f64_nan());
} }
Operator::F32x4Sub => { Operator::F32x4Sub => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?; let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
@ -2789,7 +2785,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
let v2 = v128_into_f32x4(builder, intrinsics, v2, i2); let v2 = v128_into_f32x4(builder, intrinsics, v2, i2);
let res = builder.build_float_sub(v1, v2, &state.var_name()); let res = builder.build_float_sub(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, ExtraInfo::PendingF32NaN); state.push1_extra(res, ExtraInfo::pending_f32_nan());
} }
Operator::F64x2Sub => { Operator::F64x2Sub => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?; let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
@ -2797,19 +2793,19 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
let v2 = v128_into_f64x2(builder, intrinsics, v2, i2); let v2 = v128_into_f64x2(builder, intrinsics, v2, i2);
let res = builder.build_float_sub(v1, v2, &state.var_name()); let res = builder.build_float_sub(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, ExtraInfo::PendingF64NaN); state.push1_extra(res, ExtraInfo::pending_f64_nan());
} }
Operator::F32Mul => { Operator::F32Mul => {
let (v1, v2) = state.pop2()?; let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res = builder.build_float_mul(v1, v2, &state.var_name()); let res = builder.build_float_mul(v1, v2, &state.var_name());
state.push1_extra(res, ExtraInfo::PendingF32NaN); state.push1_extra(res, ExtraInfo::pending_f32_nan());
} }
Operator::F64Mul => { Operator::F64Mul => {
let (v1, v2) = state.pop2()?; let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res = builder.build_float_mul(v1, v2, &state.var_name()); let res = builder.build_float_mul(v1, v2, &state.var_name());
state.push1_extra(res, ExtraInfo::PendingF64NaN); state.push1_extra(res, ExtraInfo::pending_f64_nan());
} }
Operator::F32x4Mul => { Operator::F32x4Mul => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?; let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
@ -2817,7 +2813,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
let v2 = v128_into_f32x4(builder, intrinsics, v2, i2); let v2 = v128_into_f32x4(builder, intrinsics, v2, i2);
let res = builder.build_float_mul(v1, v2, &state.var_name()); let res = builder.build_float_mul(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, ExtraInfo::PendingF32NaN); state.push1_extra(res, ExtraInfo::pending_f32_nan());
} }
Operator::F64x2Mul => { Operator::F64x2Mul => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?; let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
@ -2825,19 +2821,19 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
let v2 = v128_into_f64x2(builder, intrinsics, v2, i2); let v2 = v128_into_f64x2(builder, intrinsics, v2, i2);
let res = builder.build_float_mul(v1, v2, &state.var_name()); let res = builder.build_float_mul(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, ExtraInfo::PendingF64NaN); state.push1_extra(res, ExtraInfo::pending_f64_nan());
} }
Operator::F32Div => { Operator::F32Div => {
let (v1, v2) = state.pop2()?; let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res = builder.build_float_div(v1, v2, &state.var_name()); let res = builder.build_float_div(v1, v2, &state.var_name());
state.push1_extra(res, ExtraInfo::PendingF32NaN); state.push1_extra(res, ExtraInfo::pending_f32_nan());
} }
Operator::F64Div => { Operator::F64Div => {
let (v1, v2) = state.pop2()?; let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value()); let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res = builder.build_float_div(v1, v2, &state.var_name()); let res = builder.build_float_div(v1, v2, &state.var_name());
state.push1_extra(res, ExtraInfo::PendingF64NaN); state.push1_extra(res, ExtraInfo::pending_f64_nan());
} }
Operator::F32x4Div => { Operator::F32x4Div => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?; let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
@ -2845,7 +2841,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
let v2 = v128_into_f32x4(builder, intrinsics, v2, i2); let v2 = v128_into_f32x4(builder, intrinsics, v2, i2);
let res = builder.build_float_div(v1, v2, &state.var_name()); let res = builder.build_float_div(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, ExtraInfo::PendingF32NaN); state.push1_extra(res, ExtraInfo::pending_f32_nan());
} }
Operator::F64x2Div => { Operator::F64x2Div => {
let ((v1, i1), (v2, i2)) = state.pop2_extra()?; let ((v1, i1), (v2, i2)) = state.pop2_extra()?;
@ -2853,7 +2849,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
let v2 = v128_into_f64x2(builder, intrinsics, v2, i2); let v2 = v128_into_f64x2(builder, intrinsics, v2, i2);
let res = builder.build_float_div(v1, v2, &state.var_name()); let res = builder.build_float_div(v1, v2, &state.var_name());
let res = builder.build_bitcast(res, intrinsics.i128_ty, ""); let res = builder.build_bitcast(res, intrinsics.i128_ty, "");
state.push1_extra(res, ExtraInfo::PendingF64NaN); state.push1_extra(res, ExtraInfo::pending_f64_nan());
} }
Operator::F32Sqrt => { Operator::F32Sqrt => {
let input = state.pop1()?; let input = state.pop1()?;
@ -2862,7 +2858,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
.try_as_basic_value() .try_as_basic_value()
.left() .left()
.unwrap(); .unwrap();
state.push1_extra(res, ExtraInfo::PendingF32NaN); state.push1_extra(res, ExtraInfo::pending_f32_nan());
} }
Operator::F64Sqrt => { Operator::F64Sqrt => {
let input = state.pop1()?; let input = state.pop1()?;
@ -2871,7 +2867,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
.try_as_basic_value() .try_as_basic_value()
.left() .left()
.unwrap(); .unwrap();
state.push1_extra(res, ExtraInfo::PendingF64NaN); state.push1_extra(res, ExtraInfo::pending_f64_nan());
} }
Operator::F32x4Sqrt => { Operator::F32x4Sqrt => {
let (v, i) = state.pop1_extra()?; let (v, i) = state.pop1_extra()?;
@ -2886,7 +2882,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
.left() .left()
.unwrap(); .unwrap();
let bits = builder.build_bitcast(res, intrinsics.i128_ty, "bits"); let bits = builder.build_bitcast(res, intrinsics.i128_ty, "bits");
state.push1_extra(bits, ExtraInfo::PendingF32NaN); state.push1_extra(bits, ExtraInfo::pending_f32_nan());
} }
Operator::F64x2Sqrt => { Operator::F64x2Sqrt => {
let (v, i) = state.pop1_extra()?; let (v, i) = state.pop1_extra()?;
@ -3386,7 +3382,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
.try_as_basic_value() .try_as_basic_value()
.left() .left()
.unwrap(); .unwrap();
state.push1_extra(res, ExtraInfo::PendingF32NaN); state.push1_extra(res, ExtraInfo::pending_f32_nan());
} }
Operator::F64Ceil => { Operator::F64Ceil => {
let input = state.pop1()?; let input = state.pop1()?;
@ -3395,7 +3391,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
.try_as_basic_value() .try_as_basic_value()
.left() .left()
.unwrap(); .unwrap();
state.push1_extra(res, ExtraInfo::PendingF64NaN); state.push1_extra(res, ExtraInfo::pending_f64_nan());
} }
Operator::F32Floor => { Operator::F32Floor => {
let input = state.pop1()?; let input = state.pop1()?;
@ -3404,7 +3400,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
.try_as_basic_value() .try_as_basic_value()
.left() .left()
.unwrap(); .unwrap();
state.push1_extra(res, ExtraInfo::PendingF32NaN); state.push1_extra(res, ExtraInfo::pending_f32_nan());
} }
Operator::F64Floor => { Operator::F64Floor => {
let input = state.pop1()?; let input = state.pop1()?;
@ -3413,7 +3409,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
.try_as_basic_value() .try_as_basic_value()
.left() .left()
.unwrap(); .unwrap();
state.push1_extra(res, ExtraInfo::PendingF64NaN); state.push1_extra(res, ExtraInfo::pending_f64_nan());
} }
Operator::F32Trunc => { Operator::F32Trunc => {
let (v, i) = state.pop1_extra()?; let (v, i) = state.pop1_extra()?;
@ -4311,13 +4307,13 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
let v = state.pop1()?; let v = state.pop1()?;
let v = v.into_float_value(); let v = v.into_float_value();
let res = builder.build_float_trunc(v, intrinsics.f32_ty, &state.var_name()); let res = builder.build_float_trunc(v, intrinsics.f32_ty, &state.var_name());
state.push1_extra(res, ExtraInfo::PendingF32NaN); state.push1_extra(res, ExtraInfo::pending_f32_nan());
} }
Operator::F64PromoteF32 => { Operator::F64PromoteF32 => {
let v = state.pop1()?; let v = state.pop1()?;
let v = v.into_float_value(); let v = v.into_float_value();
let res = builder.build_float_ext(v, intrinsics.f64_ty, &state.var_name()); let res = builder.build_float_ext(v, intrinsics.f64_ty, &state.var_name());
state.push1_extra(res, ExtraInfo::PendingF64NaN); state.push1_extra(res, ExtraInfo::pending_f64_nan());
} }
Operator::F32ConvertSI32 | Operator::F32ConvertSI64 => { Operator::F32ConvertSI32 | Operator::F32ConvertSI64 => {
let v1 = state.pop1()?.into_int_value(); let v1 = state.pop1()?.into_int_value();

View File

@ -4,6 +4,7 @@ use inkwell::{
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use std::cell::Cell; use std::cell::Cell;
use std::ops::Add;
use wasmparser::BinaryReaderError; use wasmparser::BinaryReaderError;
#[derive(Debug)] #[derive(Debug)]
@ -68,22 +69,75 @@ impl ControlFrame {
} }
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)] #[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
pub enum ExtraInfo { pub struct ExtraInfo {
None, state: u8,
}
// This values is required to be arithmetic 32-bit NaN (or 32x4) by the WAsm impl ExtraInfo {
// This value is required to be arithmetic 32-bit NaN (or 32x4) by the WAsm
// machine, but which might not be in the LLVM value. The conversion to // machine, but which might not be in the LLVM value. The conversion to
// arithmetic NaN is pending. It is required for correctness. // arithmetic NaN is pending. It is required for correctness.
PendingF32NaN, pub fn pending_f32_nan() -> ExtraInfo {
ExtraInfo { state: 1 }
}
// This values is required to be arithmetic 64-bit NaN (or 64x2) by the WAsm // This value is required to be arithmetic 64-bit NaN (or 64x2) by the WAsm
// machine, but which might not be in the LLVM value. The conversion to // machine, but which might not be in the LLVM value. The conversion to
// arithmetic NaN is pending. It is required for correctness. // arithmetic NaN is pending. It is required for correctness.
PendingF64NaN, pub fn pending_f64_nan() -> ExtraInfo {
ExtraInfo { state: 2 }
}
// This value either does not contain a 32-bit NaN, or it contains an
// arithmetic NaN. In SIMD, applies to all 4 lanes.
pub fn arithmetic_f32() -> ExtraInfo {
ExtraInfo { state: 4 }
}
// This value either does not contain a 64-bit NaN, or it contains an
// arithmetic NaN. In SIMD, applies to both lanes.
pub fn arithmetic_f64() -> ExtraInfo {
ExtraInfo { state: 8 }
}
pub fn has_pending_f32_nan(&self) -> bool {
self.state & ExtraInfo::pending_f32_nan().state != 0
}
pub fn has_pending_f64_nan(&self) -> bool {
self.state & ExtraInfo::pending_f64_nan().state != 0
}
pub fn is_arithmetic_f32(&self) -> bool {
self.state & ExtraInfo::arithmetic_f32().state != 0
}
pub fn is_arithmetic_f64(&self) -> bool {
self.state & ExtraInfo::arithmetic_f64().state != 0
}
} }
impl Default for ExtraInfo { impl Default for ExtraInfo {
fn default() -> Self { fn default() -> Self {
ExtraInfo::None ExtraInfo { state: 0 }
}
}
impl Add for ExtraInfo {
type Output = Self;
fn add(self, other: Self) -> Self {
assert!(self.has_pending_f32_nan() && other.has_pending_f64_nan());
assert!(self.has_pending_f64_nan() && other.has_pending_f32_nan());
ExtraInfo {
state: if self.is_arithmetic_f32() || other.is_arithmetic_f32() {
ExtraInfo::arithmetic_f32().state
} else if self.has_pending_f32_nan() || other.has_pending_f32_nan() {
ExtraInfo::pending_f32_nan().state
} else {
0
} + if self.is_arithmetic_f64() || other.is_arithmetic_f64() {
ExtraInfo::arithmetic_f64().state
} else if self.has_pending_f64_nan() || other.has_pending_f64_nan() {
ExtraInfo::pending_f64_nan().state
} else {
0
},
}
} }
} }
@ -165,7 +219,7 @@ impl State {
} }
pub fn push1<T: BasicValue>(&mut self, value: T) { pub fn push1<T: BasicValue>(&mut self, value: T) {
self.push1_extra(value, ExtraInfo::None); self.push1_extra(value, Default::default());
} }
pub fn push1_extra<T: BasicValue>(&mut self, value: T, info: ExtraInfo) { pub fn push1_extra<T: BasicValue>(&mut self, value: T, info: ExtraInfo) {