diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 6359e833ba..fc47d6ad17 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -44,5 +44,5 @@ pub use self::{ typed::{Typed, TypedVal}, units::Pages, untyped::{DecodeUntypedSlice, EncodeUntypedSlice, UntypedError, UntypedVal}, - value::ValType, + value::{CanonicalizeNan, ValType}, }; diff --git a/crates/core/src/typed.rs b/crates/core/src/typed.rs index de7c0de6d1..3059e6f6b5 100644 --- a/crates/core/src/typed.rs +++ b/crates/core/src/typed.rs @@ -299,19 +299,23 @@ impl TypedVal { fn f32_abs(f32) -> f32; fn f32_neg(f32) -> f32; + fn f32_neg_canonicalize_nan(f32) -> f32; fn f32_ceil(f32) -> f32; fn f32_floor(f32) -> f32; fn f32_trunc(f32) -> f32; fn f32_nearest(f32) -> f32; fn f32_sqrt(f32) -> f32; + fn f32_sqrt_canonicalize_nan(f32) -> f32; fn f64_abs(f64) -> f64; fn f64_neg(f64) -> f64; + fn f64_neg_canonicalize_nan(f64) -> f64; fn f64_ceil(f64) -> f64; fn f64_floor(f64) -> f64; fn f64_trunc(f64) -> f64; fn f64_nearest(f64) -> f64; fn f64_sqrt(f64) -> f64; + fn f64_sqrt_canonicalize_nan(f64) -> f64; fn f32_add(f32, f32) -> f32; fn f32_sub(f32, f32) -> f32; @@ -321,6 +325,14 @@ impl TypedVal { fn f32_max(f32, f32) -> f32; fn f32_copysign(f32, f32) -> f32; + fn f32_add_canonicalize_nan(f32, f32) -> f32; + fn f32_sub_canonicalize_nan(f32, f32) -> f32; + fn f32_mul_canonicalize_nan(f32, f32) -> f32; + fn f32_div_canonicalize_nan(f32, f32) -> f32; + fn f32_min_canonicalize_nan(f32, f32) -> f32; + fn f32_max_canonicalize_nan(f32, f32) -> f32; + fn f32_copysign_canonicalize_nan(f32, f32) -> f32; + fn f64_add(f64, f64) -> f64; fn f64_sub(f64, f64) -> f64; fn f64_mul(f64, f64) -> f64; @@ -329,6 +341,14 @@ impl TypedVal { fn f64_max(f64, f64) -> f64; fn f64_copysign(f64, f64) -> f64; + fn f64_add_canonicalize_nan(f64, f64) -> f64; + fn f64_sub_canonicalize_nan(f64, f64) -> f64; + fn f64_mul_canonicalize_nan(f64, f64) -> f64; + fn f64_div_canonicalize_nan(f64, f64) -> f64; + fn f64_min_canonicalize_nan(f64, f64) -> f64; + fn f64_max_canonicalize_nan(f64, f64) -> f64; + fn f64_copysign_canonicalize_nan(f64, f64) -> f64; + // Conversions fn i32_wrap_i64(i64) -> i32; diff --git a/crates/core/src/untyped.rs b/crates/core/src/untyped.rs index b8a8a4eaa1..fa4bb7accf 100644 --- a/crates/core/src/untyped.rs +++ b/crates/core/src/untyped.rs @@ -1,6 +1,7 @@ use crate::{ value::{LoadInto, StoreFrom}, ArithmeticOps, + CanonicalizeNan, ExtendInto, Float, Integer, @@ -931,6 +932,11 @@ impl UntypedVal { self.execute_unary(::neg) } + /// Execute `f32.neg` Wasm operation. + pub fn f32_neg_canonicalize_nan(self) -> Self { + self.execute_unary(|value| ::neg(value).canonicalize_nan()) + } + /// Execute `f32.ceil` Wasm operation. pub fn f32_ceil(self) -> Self { self.execute_unary(::ceil) @@ -953,7 +959,12 @@ impl UntypedVal { /// Execute `f32.sqrt` Wasm operation. pub fn f32_sqrt(self) -> Self { - self.execute_unary(::sqrt) + self.execute_unary(::sqrt) + } + + /// Execute `f32.sqrt` Wasm operation. + pub fn f32_sqrt_canonicalize_nan(self) -> Self { + self.execute_unary(|value| ::sqrt(value).canonicalize_nan()) } /// Execute `f64.abs` Wasm operation. @@ -966,6 +977,11 @@ impl UntypedVal { self.execute_unary(::neg) } + /// Execute `f64.neg` Wasm operation. + pub fn f64_neg_canonicalize_nan(self) -> Self { + self.execute_unary(|value| ::neg(value).canonicalize_nan()) + } + /// Execute `f64.ceil` Wasm operation. pub fn f64_ceil(self) -> Self { self.execute_unary(::ceil) @@ -991,76 +1007,179 @@ impl UntypedVal { self.execute_unary(::sqrt) } + /// Execute `f64.sqrt` Wasm operation. + pub fn f64_sqrt_canonicalize_nan(self) -> Self { + self.execute_unary(|value| ::sqrt(value).canonicalize_nan()) + } + /// Execute `f32.add` Wasm operation. pub fn f32_add(self, rhs: Self) -> Self { self.execute_binary(rhs, ::add) } + /// Execute `f32.add` Wasm operation with NaN canonicalization. + pub fn f32_add_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::add(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f64.add` Wasm operation. pub fn f64_add(self, rhs: Self) -> Self { self.execute_binary(rhs, ::add) } + /// Execute `f64.add` Wasm operation with NaN canonicalization. + pub fn f64_add_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::add(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f32.sub` Wasm operation. pub fn f32_sub(self, rhs: Self) -> Self { self.execute_binary(rhs, ::sub) } + /// Execute `f32.sub` Wasm operation with NaN canonicalization. + pub fn f32_sub_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::sub(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f64.sub` Wasm operation. pub fn f64_sub(self, rhs: Self) -> Self { self.execute_binary(rhs, ::sub) } + /// Execute `f64.sub` Wasm operation with NaN canonicalization. + pub fn f64_sub_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::sub(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f32.mul` Wasm operation. pub fn f32_mul(self, rhs: Self) -> Self { self.execute_binary(rhs, ::mul) } + /// Execute `f32.mul` Wasm operation with NaN canonicalization. + pub fn f32_mul_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::mul(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f64.mul` Wasm operation. pub fn f64_mul(self, rhs: Self) -> Self { self.execute_binary(rhs, ::mul) } + /// Execute `f64.mul` Wasm operation with NaN canonicalization. + pub fn f64_mul_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::mul(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f32.div` Wasm operation. pub fn f32_div(self, rhs: Self) -> Self { self.execute_binary(rhs, ::div) } + /// Execute `f32.div` Wasm operation with NaN canonicalization. + pub fn f32_div_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::div(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f64.div` Wasm operation. pub fn f64_div(self, rhs: Self) -> Self { self.execute_binary(rhs, ::div) } + /// Execute `f64.div` Wasm operation with NaN canonicalization. + pub fn f64_div_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::div(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f32.min` Wasm operation. pub fn f32_min(self, other: Self) -> Self { self.execute_binary(other, ::min) } + /// Execute `f32.min` Wasm operation with NaN canonicalization. + pub fn f32_min_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::min(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f64.min` Wasm operation. pub fn f64_min(self, other: Self) -> Self { self.execute_binary(other, ::min) } + /// Execute `f64.min` Wasm operation with NaN canonicalization. + pub fn f64_min_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::min(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f32.max` Wasm operation. pub fn f32_max(self, other: Self) -> Self { self.execute_binary(other, ::max) } + /// Execute `f32.max` Wasm operation with NaN canonicalization. + pub fn f32_max_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::max(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f64.max` Wasm operation. pub fn f64_max(self, other: Self) -> Self { self.execute_binary(other, ::max) } + /// Execute `f64.max` Wasm operation with NaN canonicalization. + pub fn f64_max_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::max(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f32.copysign` Wasm operation. pub fn f32_copysign(self, other: Self) -> Self { self.execute_binary(other, ::copysign) } + /// Execute `f32.copysign` Wasm operation with NaN canonicalization. + pub fn f32_copysign_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::copysign(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `f64.copysign` Wasm operation. pub fn f64_copysign(self, other: Self) -> Self { self.execute_binary(other, ::copysign) } + /// Execute `f64.copysign` Wasm operation with NaN canonicalization. + pub fn f64_copysign_canonicalize_nan(self, rhs: Self) -> Self { + self.execute_binary(rhs, |lhs, rhs| { + ::copysign(lhs, rhs).canonicalize_nan() + }) + } + /// Execute `i32.wrap_i64` Wasm operation. pub fn i32_wrap_i64(self) -> Self { self.execute_unary(>::wrap_into) diff --git a/crates/core/src/value.rs b/crates/core/src/value.rs index 63c1246d65..9978a97571 100644 --- a/crates/core/src/value.rs +++ b/crates/core/src/value.rs @@ -791,3 +791,34 @@ mod tests { ) } } + +/// Trait implemented by floating point types for NaN canonicalization. +pub trait CanonicalizeNan { + /// Canonicalizes `self` if it is NaN. + fn canonicalize_nan(self) -> Self; +} + +macro_rules! impl_caninocalize_nan { + ( $( $ty:ty as $nan_val:literal),* $(,)? ) => { + $( + impl CanonicalizeNan for $ty { + fn canonicalize_nan(self) -> Self { + if self.is_nan() { + let canonicalized = Self::from_bits($nan_val); + debug_assert!(canonicalized.is_nan()); + return canonicalized + } + self + } + } + )* + }; +} +impl_caninocalize_nan! { + // Note: These patterns ensures that the sign bit can be either 0 or 1, + // the exponent bits are all set to 1 (indicating an infinity or NaN), + // and the fraction (mantissa) bits are all zero, except the most significant bit + // of the fraction is set to 1 to indicate a quiet NaN. + f32 as 0x7FC00000_u32, + f64 as 0x7FF8000000000000_u64, +} diff --git a/crates/ir/src/for_each_op.rs b/crates/ir/src/for_each_op.rs index 8ce43865d0..f685394535 100644 --- a/crates/ir/src/for_each_op.rs +++ b/crates/ir/src/for_each_op.rs @@ -4686,6 +4686,13 @@ macro_rules! for_each_op { /// The register holding the input of the instruction. input: Reg, }, + /// Wasm `f32.neg` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f32_neg_canonicalize_nan)] + F32NegCanonicalizeNan { + @result: Reg, + /// The register holding the input of the instruction. + input: Reg, + }, /// Wasm `f32.ceil` equivalent Wasmi instruction. #[snake_name(f32_ceil)] F32Ceil { @@ -4721,6 +4728,13 @@ macro_rules! for_each_op { /// The register holding the input of the instruction. input: Reg, }, + /// Wasm `f32.sqrt` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f32_sqrt_canonicalize_nan)] + F32SqrtCanonicalizeNan { + @result: Reg, + /// The register holding the input of the instruction. + input: Reg, + }, /// Wasm `f32.add` equivalent Wasmi instruction. #[snake_name(f32_add)] F32Add { @@ -4730,6 +4744,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f32.add` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f32_add_canonicalize_nan)] + F32AddCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f32.sub` equivalent Wasmi instruction. #[snake_name(f32_sub)] F32Sub { @@ -4739,6 +4762,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f32.sub` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f32_sub_canonicalize_nan)] + F32SubCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f32.mul` equivalent Wasmi instruction. #[snake_name(f32_mul)] F32Mul { @@ -4748,6 +4780,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f32.mul` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f32_mul_canonicalize_nan)] + F32MulCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f32.div` equivalent Wasmi instruction. #[snake_name(f32_div)] F32Div { @@ -4757,6 +4798,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f32.div` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f32_div_canonicalize_nan)] + F32DivCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f32.min` equivalent Wasmi instruction. #[snake_name(f32_min)] F32Min { @@ -4766,6 +4816,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f32.min` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f32_min_canonicalize_nan)] + F32MinCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f32.max` equivalent Wasmi instruction. #[snake_name(f32_max)] F32Max { @@ -4775,6 +4834,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f32.max` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f32_max_canonicalize_nan)] + F32MaxCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f32.copysign` equivalent Wasmi instruction. #[snake_name(f32_copysign)] F32Copysign { @@ -4785,6 +4853,15 @@ macro_rules! for_each_op { rhs: Reg, }, /// Wasm `f32.copysign` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f32_copysign_canonicalize_nan)] + F32CopysignCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, + /// Wasm `f32.copysign` equivalent Wasmi instruction with immediate `rhs` value. #[snake_name(f32_copysign_imm)] F32CopysignImm { @result: Reg, @@ -4793,6 +4870,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Sign, }, + /// Wasm `f32.copysign` equivalent Wasmi instruction with immediate `rhs` value and NaN canonicalization. + #[snake_name(f32_copysign_imm_canonicalize_nan)] + F32CopysignImmCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Sign, + }, /// Wasm `f64.abs` equivalent Wasmi instruction. #[snake_name(f64_abs)] @@ -4808,6 +4894,13 @@ macro_rules! for_each_op { /// The register holding the input of the instruction. input: Reg, }, + /// Wasm `f64.neg` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f64_neg_canonicalize_nan)] + F64NegCanonicalizeNan { + @result: Reg, + /// The register holding the input of the instruction. + input: Reg, + }, /// Wasm `f64.ceil` equivalent Wasmi instruction. #[snake_name(f64_ceil)] F64Ceil { @@ -4843,6 +4936,13 @@ macro_rules! for_each_op { /// The register holding the input of the instruction. input: Reg, }, + /// Wasm `f64.sqrt` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f64_sqrt_canonicalize_nan)] + F64SqrtCanonicalizeNan { + @result: Reg, + /// The register holding the input of the instruction. + input: Reg, + }, /// Wasm `f64.add` equivalent Wasmi instruction. #[snake_name(f64_add)] F64Add { @@ -4852,6 +4952,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f64.add` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f64_add_canonicalize_nan)] + F64AddCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f64.sub` equivalent Wasmi instruction. #[snake_name(f64_sub)] F64Sub { @@ -4861,6 +4970,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f64.sub` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f64_sub_canonicalize_nan)] + F64SubCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f64.mul` equivalent Wasmi instruction. #[snake_name(f64_mul)] F64Mul { @@ -4870,6 +4988,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f64.mul` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f64_mul_canonicalize_nan)] + F64MulCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f64.div` equivalent Wasmi instruction. #[snake_name(f64_div)] F64Div { @@ -4879,6 +5006,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f64.div` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f64_div_canonicalize_nan)] + F64DivCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f64.min` equivalent Wasmi instruction. #[snake_name(f64_min)] F64Min { @@ -4888,6 +5024,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f64.min` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f64_min_canonicalize_nan)] + F64MinCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f64.max` equivalent Wasmi instruction. #[snake_name(f64_max)] F64Max { @@ -4897,6 +5042,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f64.max` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f64_max_canonicalize_nan)] + F64MaxCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f64.copysign` equivalent Wasmi instruction. #[snake_name(f64_copysign)] F64Copysign { @@ -4906,6 +5060,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, + /// Wasm `f64.copysign` equivalent Wasmi instruction with NaN canonicalization. + #[snake_name(f64_copysign_canonicalize_nan)] + F64CopysignCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Reg, + }, /// Wasm `f64.copysign` equivalent Wasmi instruction with imediate `rhs` value. #[snake_name(f64_copysign_imm)] F64CopysignImm { @@ -4915,6 +5078,15 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Sign, }, + /// Wasm `f64.copysign_imm` equivalent Wasmi instruction with immediate `rhs` value and NaN canonicalization. + #[snake_name(f64_copysign_imm_canonicalize_nan)] + F64CopysignImmCanonicalizeNan { + @result: Reg, + /// The register holding the left-hand side value. + lhs: Reg, + /// The register holding the right-hand side value. + rhs: Sign, + }, /// Wasm `i32.trunc_f32_s` instruction. #[snake_name(i32_trunc_f32_s)] diff --git a/crates/wasmi/src/engine/config.rs b/crates/wasmi/src/engine/config.rs index 61a63b3f5f..6eb7aa61ad 100644 --- a/crates/wasmi/src/engine/config.rs +++ b/crates/wasmi/src/engine/config.rs @@ -39,6 +39,8 @@ pub struct Config { consume_fuel: bool, /// Is `true` if Wasmi shall ignore Wasm custom sections when parsing Wasm modules. ignore_custom_sections: bool, + /// Is `true` if Wasmi shall canonicalize NaN values. + canonicalize_nans: bool, /// The configured fuel costs of all Wasmi bytecode instructions. fuel_costs: FuelCosts, /// The mode of Wasm to Wasmi bytecode compilation. @@ -185,6 +187,7 @@ impl Default for Config { extended_const: true, floats: true, consume_fuel: false, + canonicalize_nans: false, ignore_custom_sections: false, fuel_costs: FuelCosts::default(), compilation_mode: CompilationMode::default(), @@ -337,6 +340,14 @@ impl Config { self } + /// Enable or disable NaN value canonicalization. + /// + /// Disabled by default. + pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self { + self.canonicalize_nans = enable; + self + } + /// Configures whether Wasmi will consume fuel during execution to either halt execution as desired. /// /// # Note @@ -379,6 +390,11 @@ impl Config { self.ignore_custom_sections } + /// Returns `true` if the [`Config`] mandates to canonicalize NaN values. + pub(crate) fn get_canonicalize_nans(&self) -> bool { + self.canonicalize_nans + } + /// Returns the configured [`FuelCosts`]. pub(crate) fn fuel_costs(&self) -> &FuelCosts { &self.fuel_costs diff --git a/crates/wasmi/src/engine/executor/instrs.rs b/crates/wasmi/src/engine/executor/instrs.rs index 875d1e7fb6..a3146c2a36 100644 --- a/crates/wasmi/src/engine/executor/instrs.rs +++ b/crates/wasmi/src/engine/executor/instrs.rs @@ -1082,42 +1082,102 @@ impl<'engine> Executor<'engine> { Instr::I64Extend32S { result, input } => self.execute_i64_extend32_s(result, input), Instr::F32Abs { result, input } => self.execute_f32_abs(result, input), Instr::F32Neg { result, input } => self.execute_f32_neg(result, input), + Instr::F32NegCanonicalizeNan { result, input } => { + self.execute_f32_neg_canonicalize_nan(result, input) + } Instr::F32Ceil { result, input } => self.execute_f32_ceil(result, input), Instr::F32Floor { result, input } => self.execute_f32_floor(result, input), Instr::F32Trunc { result, input } => self.execute_f32_trunc(result, input), Instr::F32Nearest { result, input } => self.execute_f32_nearest(result, input), Instr::F32Sqrt { result, input } => self.execute_f32_sqrt(result, input), + Instr::F32SqrtCanonicalizeNan { result, input } => { + self.execute_f32_sqrt_canonicalize_nan(result, input) + } Instr::F32Add { result, lhs, rhs } => self.execute_f32_add(result, lhs, rhs), + Instr::F32AddCanonicalizeNan { result, lhs, rhs } => { + self.execute_f32_add_canonicalize_nan(result, lhs, rhs) + } Instr::F32Sub { result, lhs, rhs } => self.execute_f32_sub(result, lhs, rhs), + Instr::F32SubCanonicalizeNan { result, lhs, rhs } => { + self.execute_f32_sub_canonicalize_nan(result, lhs, rhs) + } Instr::F32Mul { result, lhs, rhs } => self.execute_f32_mul(result, lhs, rhs), + Instr::F32MulCanonicalizeNan { result, lhs, rhs } => { + self.execute_f32_mul_canonicalize_nan(result, lhs, rhs) + } Instr::F32Div { result, lhs, rhs } => self.execute_f32_div(result, lhs, rhs), + Instr::F32DivCanonicalizeNan { result, lhs, rhs } => { + self.execute_f32_div_canonicalize_nan(result, lhs, rhs) + } Instr::F32Min { result, lhs, rhs } => self.execute_f32_min(result, lhs, rhs), + Instr::F32MinCanonicalizeNan { result, lhs, rhs } => { + self.execute_f32_min_canonicalize_nan(result, lhs, rhs) + } Instr::F32Max { result, lhs, rhs } => self.execute_f32_max(result, lhs, rhs), + Instr::F32MaxCanonicalizeNan { result, lhs, rhs } => { + self.execute_f32_max_canonicalize_nan(result, lhs, rhs) + } Instr::F32Copysign { result, lhs, rhs } => { self.execute_f32_copysign(result, lhs, rhs) } + Instr::F32CopysignCanonicalizeNan { result, lhs, rhs } => { + self.execute_f32_copysign_canonicalize_nan(result, lhs, rhs) + } Instr::F32CopysignImm { result, lhs, rhs } => { self.execute_f32_copysign_imm(result, lhs, rhs) } + Instr::F32CopysignImmCanonicalizeNan { result, lhs, rhs } => { + self.execute_f32_copysign_imm_canonicalize_nan(result, lhs, rhs) + } Instr::F64Abs { result, input } => self.execute_f64_abs(result, input), Instr::F64Neg { result, input } => self.execute_f64_neg(result, input), + Instr::F64NegCanonicalizeNan { result, input } => { + self.execute_f64_neg_canonicalize_nan(result, input) + } Instr::F64Ceil { result, input } => self.execute_f64_ceil(result, input), Instr::F64Floor { result, input } => self.execute_f64_floor(result, input), Instr::F64Trunc { result, input } => self.execute_f64_trunc(result, input), Instr::F64Nearest { result, input } => self.execute_f64_nearest(result, input), Instr::F64Sqrt { result, input } => self.execute_f64_sqrt(result, input), + Instr::F64SqrtCanonicalizeNan { result, input } => { + self.execute_f64_sqrt_canonicalize_nan(result, input) + } Instr::F64Add { result, lhs, rhs } => self.execute_f64_add(result, lhs, rhs), + Instr::F64AddCanonicalizeNan { result, lhs, rhs } => { + self.execute_f64_add_canonicalize_nan(result, lhs, rhs) + } Instr::F64Sub { result, lhs, rhs } => self.execute_f64_sub(result, lhs, rhs), + Instr::F64SubCanonicalizeNan { result, lhs, rhs } => { + self.execute_f64_sub_canonicalize_nan(result, lhs, rhs) + } Instr::F64Mul { result, lhs, rhs } => self.execute_f64_mul(result, lhs, rhs), + Instr::F64MulCanonicalizeNan { result, lhs, rhs } => { + self.execute_f64_mul_canonicalize_nan(result, lhs, rhs) + } Instr::F64Div { result, lhs, rhs } => self.execute_f64_div(result, lhs, rhs), + Instr::F64DivCanonicalizeNan { result, lhs, rhs } => { + self.execute_f64_div_canonicalize_nan(result, lhs, rhs) + } Instr::F64Min { result, lhs, rhs } => self.execute_f64_min(result, lhs, rhs), + Instr::F64MinCanonicalizeNan { result, lhs, rhs } => { + self.execute_f64_min_canonicalize_nan(result, lhs, rhs) + } Instr::F64Max { result, lhs, rhs } => self.execute_f64_max(result, lhs, rhs), + Instr::F64MaxCanonicalizeNan { result, lhs, rhs } => { + self.execute_f64_max_canonicalize_nan(result, lhs, rhs) + } Instr::F64Copysign { result, lhs, rhs } => { self.execute_f64_copysign(result, lhs, rhs) } + Instr::F64CopysignCanonicalizeNan { result, lhs, rhs } => { + self.execute_f64_copysign_canonicalize_nan(result, lhs, rhs) + } Instr::F64CopysignImm { result, lhs, rhs } => { self.execute_f64_copysign_imm(result, lhs, rhs) } + Instr::F64CopysignImmCanonicalizeNan { result, lhs, rhs } => { + self.execute_f64_copysign_imm_canonicalize_nan(result, lhs, rhs) + } Instr::I32TruncF32S { result, input } => { self.execute_i32_trunc_f32_s(result, input)? } diff --git a/crates/wasmi/src/engine/executor/instrs/binary.rs b/crates/wasmi/src/engine/executor/instrs/binary.rs index 9a48d55fe1..657290acb9 100644 --- a/crates/wasmi/src/engine/executor/instrs/binary.rs +++ b/crates/wasmi/src/engine/executor/instrs/binary.rs @@ -65,6 +65,22 @@ impl Executor<'_> { (Instruction::F64Min, execute_f64_min, UntypedVal::f64_min), (Instruction::F64Max, execute_f64_max, UntypedVal::f64_max), (Instruction::F64Copysign, execute_f64_copysign, UntypedVal::f64_copysign), + + (Instruction::F32AddCanonicalizeNan, execute_f32_add_canonicalize_nan, UntypedVal::f32_add_canonicalize_nan), + (Instruction::F32SubCanonicalizeNan, execute_f32_sub_canonicalize_nan, UntypedVal::f32_sub_canonicalize_nan), + (Instruction::F32MulCanonicalizeNan, execute_f32_mul_canonicalize_nan, UntypedVal::f32_mul_canonicalize_nan), + (Instruction::F32DivCanonicalizeNan, execute_f32_div_canonicalize_nan, UntypedVal::f32_div_canonicalize_nan), + (Instruction::F32MinCanonicalizeNan, execute_f32_min_canonicalize_nan, UntypedVal::f32_min_canonicalize_nan), + (Instruction::F32MaxCanonicalizeNan, execute_f32_max_canonicalize_nan, UntypedVal::f32_max_canonicalize_nan), + (Instruction::F32CopysignCanonicalizeNan, execute_f32_copysign_canonicalize_nan, UntypedVal::f32_copysign_canonicalize_nan), + + (Instruction::F64AddCanonicalizeNan, execute_f64_add_canonicalize_nan, UntypedVal::f64_add_canonicalize_nan), + (Instruction::F64SubCanonicalizeNan, execute_f64_sub_canonicalize_nan, UntypedVal::f64_sub_canonicalize_nan), + (Instruction::F64MulCanonicalizeNan, execute_f64_mul_canonicalize_nan, UntypedVal::f64_mul_canonicalize_nan), + (Instruction::F64DivCanonicalizeNan, execute_f64_div_canonicalize_nan, UntypedVal::f64_div_canonicalize_nan), + (Instruction::F64MinCanonicalizeNan, execute_f64_min_canonicalize_nan, UntypedVal::f64_min_canonicalize_nan), + (Instruction::F64MaxCanonicalizeNan, execute_f64_max_canonicalize_nan, UntypedVal::f64_max_canonicalize_nan), + (Instruction::F64CopysignCanonicalizeNan, execute_f64_copysign_canonicalize_nan, UntypedVal::f64_copysign_canonicalize_nan), } } @@ -308,20 +324,27 @@ impl Executor<'_> { } } -impl Executor<'_> { - /// Executes an [`Instruction::F32CopysignImm`]. - pub fn execute_f32_copysign_imm(&mut self, result: Reg, lhs: Reg, rhs: Sign) { - let lhs = self.get_register(lhs); - let rhs = f32::from(rhs); - self.set_register(result, UntypedVal::f32_copysign(lhs, rhs.into())); - self.next_instr() - } - - /// Executes an [`Instruction::F64CopysignImm`]. - pub fn execute_f64_copysign_imm(&mut self, result: Reg, lhs: Reg, rhs: Sign) { - let lhs = self.get_register(lhs); - let rhs = f64::from(rhs); - self.set_register(result, UntypedVal::f64_copysign(lhs, rhs.into())); - self.next_instr() - } +macro_rules! impl_execute_copysign_imm { + ( + $( $instr_name:ident => fn $fn_name:ident($untyped_fn:ident) -> $float_ty:ty; )* + ) => { + impl Executor<'_> { + $( + #[doc = concat!("Executes an [`Instruction::", stringify!($instr_name), "`]." + )] + pub fn $fn_name(&mut self, result: Reg, lhs: Reg, rhs: Sign<$float_ty>) { + let lhs = self.get_register(lhs); + let rhs = <$float_ty>::from(rhs); + self.set_register(result, UntypedVal::$untyped_fn(lhs, rhs.into())); + self.next_instr() + } + )* + } + }; +} +impl_execute_copysign_imm! { + F32CopysignImm => fn execute_f32_copysign_imm(f32_copysign) -> f32; + F64CopysignImm => fn execute_f64_copysign_imm(f64_copysign) -> f64; + F32CopysignImmCanonicalizeNan => fn execute_f32_copysign_imm_canonicalize_nan(f32_copysign_canonicalize_nan) -> f32; + F64CopysignImmCanonicalizeNan => fn execute_f64_copysign_imm_canonicalize_nan(f64_copysign_canonicalize_nan) -> f64; } diff --git a/crates/wasmi/src/engine/executor/instrs/unary.rs b/crates/wasmi/src/engine/executor/instrs/unary.rs index fd35f290cf..46e10c4af3 100644 --- a/crates/wasmi/src/engine/executor/instrs/unary.rs +++ b/crates/wasmi/src/engine/executor/instrs/unary.rs @@ -27,18 +27,22 @@ impl Executor<'_> { (Instruction::F32Abs, execute_f32_abs, UntypedVal::f32_abs), (Instruction::F32Neg, execute_f32_neg, UntypedVal::f32_neg), + (Instruction::F32NegCanonicalizeNan, execute_f32_neg_canonicalize_nan, UntypedVal::f32_neg_canonicalize_nan), (Instruction::F32Ceil, execute_f32_ceil, UntypedVal::f32_ceil), (Instruction::F32Floor, execute_f32_floor, UntypedVal::f32_floor), (Instruction::F32Trunc, execute_f32_trunc, UntypedVal::f32_trunc), (Instruction::F32Nearest, execute_f32_nearest, UntypedVal::f32_nearest), (Instruction::F32Sqrt, execute_f32_sqrt, UntypedVal::f32_sqrt), + (Instruction::F32SqrtCanonicalizeNan, execute_f32_sqrt_canonicalize_nan, UntypedVal::f32_sqrt_canonicalize_nan), (Instruction::F64Abs, execute_f64_abs, UntypedVal::f64_abs), (Instruction::F64Neg, execute_f64_neg, UntypedVal::f64_neg), + (Instruction::F64NegCanonicalizeNan, execute_f64_neg_canonicalize_nan, UntypedVal::f64_neg_canonicalize_nan), (Instruction::F64Ceil, execute_f64_ceil, UntypedVal::f64_ceil), (Instruction::F64Floor, execute_f64_floor, UntypedVal::f64_floor), (Instruction::F64Trunc, execute_f64_trunc, UntypedVal::f64_trunc), (Instruction::F64Nearest, execute_f64_nearest, UntypedVal::f64_nearest), (Instruction::F64Sqrt, execute_f64_sqrt, UntypedVal::f64_sqrt), + (Instruction::F64SqrtCanonicalizeNan, execute_f64_sqrt_canonicalize_nan, UntypedVal::f64_sqrt_canonicalize_nan), } } diff --git a/crates/wasmi/src/engine/translator/mod.rs b/crates/wasmi/src/engine/translator/mod.rs index d046c9f620..9aa6e5d4d8 100644 --- a/crates/wasmi/src/engine/translator/mod.rs +++ b/crates/wasmi/src/engine/translator/mod.rs @@ -731,6 +731,11 @@ impl FuncTranslator { self.fuel_costs.as_ref() } + /// Returns `true` if NaN canonicalization is enabled in the associated [`Config`]. + fn is_nan_canonicalization_enabled(&self) -> bool { + self.engine.config().get_canonicalize_nans() + } + /// Returns the most recent [`Instruction::ConsumeFuel`] in the translation process. /// /// Returns `None` if fuel metering is disabled. diff --git a/crates/wasmi/src/engine/translator/visit.rs b/crates/wasmi/src/engine/translator/visit.rs index 9d05466770..cd9a07a674 100644 --- a/crates/wasmi/src/engine/translator/visit.rs +++ b/crates/wasmi/src/engine/translator/visit.rs @@ -2662,7 +2662,14 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f32_neg(&mut self) -> Self::Output { - self.translate_unary(Instruction::f32_neg, TypedVal::f32_neg) + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f32_neg as _, TypedVal::f32_neg as _), + true => ( + Instruction::f32_neg_canonicalize_nan as _, + TypedVal::f32_neg_canonicalize_nan as _, + ), + }; + self.translate_unary(make_instr, consteval) } fn visit_f32_ceil(&mut self) -> Self::Output { @@ -2682,22 +2689,43 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f32_sqrt(&mut self) -> Self::Output { - self.translate_unary(Instruction::f32_sqrt, TypedVal::f32_sqrt) + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f32_sqrt as _, TypedVal::f32_sqrt as _), + true => ( + Instruction::f32_sqrt_canonicalize_nan as _, + TypedVal::f32_sqrt_canonicalize_nan as _, + ), + }; + self.translate_unary(make_instr, consteval) } fn visit_f32_add(&mut self) -> Self::Output { + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f32_add as _, TypedVal::f32_add as _), + true => ( + Instruction::f32_add_canonicalize_nan as _, + TypedVal::f32_add_canonicalize_nan as _, + ), + }; self.translate_fbinary_commutative( - Instruction::f32_add, - TypedVal::f32_add, + make_instr, + consteval, Self::no_custom_opt, Self::no_custom_opt::, ) } fn visit_f32_sub(&mut self) -> Self::Output { + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f32_sub as _, TypedVal::f32_sub as _), + true => ( + Instruction::f32_sub_canonicalize_nan as _, + TypedVal::f32_sub_canonicalize_nan as _, + ), + }; self.translate_fbinary( - Instruction::f32_sub, - TypedVal::f32_sub, + make_instr, + consteval, Self::no_custom_opt, Self::no_custom_opt::, // Unfortunately we cannot optimize for the case that `lhs == 0.0` @@ -2708,9 +2736,16 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f32_mul(&mut self) -> Self::Output { + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f32_mul as _, TypedVal::f32_mul as _), + true => ( + Instruction::f32_mul_canonicalize_nan as _, + TypedVal::f32_mul_canonicalize_nan as _, + ), + }; self.translate_fbinary_commutative::( - Instruction::f32_mul, - TypedVal::f32_mul, + make_instr, + consteval, Self::no_custom_opt, // Unfortunately we cannot apply `x * 0` or `0 * x` optimizations // since Wasm mandates different behaviors if `x` is infinite or @@ -2720,9 +2755,16 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f32_div(&mut self) -> Self::Output { + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f32_div as _, TypedVal::f32_div as _), + true => ( + Instruction::f32_div_canonicalize_nan as _, + TypedVal::f32_div_canonicalize_nan as _, + ), + }; self.translate_fbinary::( - Instruction::f32_div, - TypedVal::f32_div, + make_instr, + consteval, Self::no_custom_opt, Self::no_custom_opt, Self::no_custom_opt, @@ -2730,9 +2772,16 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f32_min(&mut self) -> Self::Output { + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f32_min as _, TypedVal::f32_min as _), + true => ( + Instruction::f32_min_canonicalize_nan as _, + TypedVal::f32_min_canonicalize_nan as _, + ), + }; self.translate_fbinary_commutative( - Instruction::f32_min, - TypedVal::f32_min, + make_instr, + consteval, Self::no_custom_opt, |this, reg: Reg, value: f32| { if value.is_infinite() && value.is_sign_positive() { @@ -2746,9 +2795,16 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f32_max(&mut self) -> Self::Output { + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f32_max as _, TypedVal::f32_max as _), + true => ( + Instruction::f32_max_canonicalize_nan as _, + TypedVal::f32_max_canonicalize_nan as _, + ), + }; self.translate_fbinary_commutative( - Instruction::f32_max, - TypedVal::f32_max, + make_instr, + consteval, Self::no_custom_opt, |this, reg: Reg, value: f32| { if value.is_infinite() && value.is_sign_negative() { @@ -2762,11 +2818,19 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f32_copysign(&mut self) -> Self::Output { - self.translate_fcopysign::( - Instruction::f32_copysign, - Instruction::f32_copysign_imm, - TypedVal::f32_copysign, - ) + let (make_instr, make_instr_imm, consteval) = match self.is_nan_canonicalization_enabled() { + false => ( + Instruction::f32_copysign as _, + Instruction::f32_copysign_imm as _, + TypedVal::f32_copysign as _, + ), + true => ( + Instruction::f32_copysign_canonicalize_nan as _, + Instruction::f32_copysign_imm_canonicalize_nan as _, + TypedVal::f32_copysign_canonicalize_nan as _, + ), + }; + self.translate_fcopysign::(make_instr, make_instr_imm, consteval) } fn visit_f64_abs(&mut self) -> Self::Output { @@ -2774,7 +2838,14 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f64_neg(&mut self) -> Self::Output { - self.translate_unary(Instruction::f64_neg, TypedVal::f64_neg) + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f64_neg as _, TypedVal::f64_neg as _), + true => ( + Instruction::f64_neg_canonicalize_nan as _, + TypedVal::f64_neg_canonicalize_nan as _, + ), + }; + self.translate_unary(make_instr, consteval) } fn visit_f64_ceil(&mut self) -> Self::Output { @@ -2794,22 +2865,43 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f64_sqrt(&mut self) -> Self::Output { - self.translate_unary(Instruction::f64_sqrt, TypedVal::f64_sqrt) + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f64_sqrt as _, TypedVal::f64_sqrt as _), + true => ( + Instruction::f64_sqrt_canonicalize_nan as _, + TypedVal::f64_sqrt_canonicalize_nan as _, + ), + }; + self.translate_unary(make_instr, consteval) } fn visit_f64_add(&mut self) -> Self::Output { + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f64_add as _, TypedVal::f64_add as _), + true => ( + Instruction::f64_add_canonicalize_nan as _, + TypedVal::f64_add_canonicalize_nan as _, + ), + }; self.translate_fbinary_commutative( - Instruction::f64_add, - TypedVal::f64_add, + make_instr, + consteval, Self::no_custom_opt, Self::no_custom_opt::, ) } fn visit_f64_sub(&mut self) -> Self::Output { + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f64_sub as _, TypedVal::f64_sub as _), + true => ( + Instruction::f64_sub_canonicalize_nan as _, + TypedVal::f64_sub_canonicalize_nan as _, + ), + }; self.translate_fbinary( - Instruction::f64_sub, - TypedVal::f64_sub, + make_instr, + consteval, Self::no_custom_opt, Self::no_custom_opt::, // Unfortunately we cannot optimize for the case that `lhs == 0.0` @@ -2820,9 +2912,16 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f64_mul(&mut self) -> Self::Output { + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f64_mul as _, TypedVal::f64_mul as _), + true => ( + Instruction::f64_mul_canonicalize_nan as _, + TypedVal::f64_mul_canonicalize_nan as _, + ), + }; self.translate_fbinary_commutative::( - Instruction::f64_mul, - TypedVal::f64_mul, + make_instr, + consteval, Self::no_custom_opt, // Unfortunately we cannot apply `x * 0` or `0 * x` optimizations // since Wasm mandates different behaviors if `x` is infinite or @@ -2832,9 +2931,16 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f64_div(&mut self) -> Self::Output { + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f64_div as _, TypedVal::f64_div as _), + true => ( + Instruction::f64_div_canonicalize_nan as _, + TypedVal::f64_div_canonicalize_nan as _, + ), + }; self.translate_fbinary::( - Instruction::f64_div, - TypedVal::f64_div, + make_instr, + consteval, Self::no_custom_opt, Self::no_custom_opt, Self::no_custom_opt, @@ -2842,9 +2948,16 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f64_min(&mut self) -> Self::Output { + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f64_min as _, TypedVal::f64_min as _), + true => ( + Instruction::f64_min_canonicalize_nan as _, + TypedVal::f64_min_canonicalize_nan as _, + ), + }; self.translate_fbinary_commutative( - Instruction::f64_min, - TypedVal::f64_min, + make_instr, + consteval, Self::no_custom_opt, |this, reg: Reg, value: f64| { if value.is_infinite() && value.is_sign_positive() { @@ -2858,9 +2971,16 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f64_max(&mut self) -> Self::Output { + let (make_instr, consteval) = match self.is_nan_canonicalization_enabled() { + false => (Instruction::f64_max as _, TypedVal::f64_max as _), + true => ( + Instruction::f64_max_canonicalize_nan as _, + TypedVal::f64_max_canonicalize_nan as _, + ), + }; self.translate_fbinary_commutative( - Instruction::f64_max, - TypedVal::f64_max, + make_instr, + consteval, Self::no_custom_opt, |this, reg: Reg, value: f64| { if value.is_infinite() && value.is_sign_negative() { @@ -2874,11 +2994,19 @@ impl<'a> VisitOperator<'a> for FuncTranslator { } fn visit_f64_copysign(&mut self) -> Self::Output { - self.translate_fcopysign::( - Instruction::f64_copysign, - Instruction::f64_copysign_imm, - TypedVal::f64_copysign, - ) + let (make_instr, make_instr_imm, consteval) = match self.is_nan_canonicalization_enabled() { + false => ( + Instruction::f64_copysign as _, + Instruction::f64_copysign_imm as _, + TypedVal::f64_copysign as _, + ), + true => ( + Instruction::f64_copysign_canonicalize_nan as _, + Instruction::f64_copysign_imm_canonicalize_nan as _, + TypedVal::f64_copysign_canonicalize_nan as _, + ), + }; + self.translate_fcopysign::(make_instr, make_instr_imm, consteval) } fn visit_i32_wrap_i64(&mut self) -> Self::Output {