From 5c24bb439ab52dc1cd012ca701a3d2bae029c809 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 13 Oct 2024 19:08:24 +0200 Subject: [PATCH 01/19] add Config::canonicalize_nans --- crates/wasmi/src/engine/config.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/crates/wasmi/src/engine/config.rs b/crates/wasmi/src/engine/config.rs index 61a63b3f5f..7cb0649e46 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 From 82ca9b9430e43a23e96fca36a3d8301ed5a8c762 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 13 Oct 2024 19:15:25 +0200 Subject: [PATCH 02/19] improve unary and binary float instruction docs --- crates/ir/src/for_each_op.rs | 60 ++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/crates/ir/src/for_each_op.rs b/crates/ir/src/for_each_op.rs index f4453aefa5..14929fbe0d 100644 --- a/crates/ir/src/for_each_op.rs +++ b/crates/ir/src/for_each_op.rs @@ -4672,56 +4672,56 @@ macro_rules! for_each_op { input: Reg, }, - /// Wasm `f32.abs` instruction. + /// Wasm `f32.abs` equivalent Wasmi instruction. #[snake_name(f32_abs)] F32Abs { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f32.neg` instruction. + /// Wasm `f32.neg` equivalent Wasmi instruction. #[snake_name(f32_neg)] F32Neg { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f32.ceil` instruction. + /// Wasm `f32.ceil` equivalent Wasmi instruction. #[snake_name(f32_ceil)] F32Ceil { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f32.floor` instruction. + /// Wasm `f32.floor` equivalent Wasmi instruction. #[snake_name(f32_floor)] F32Floor { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f32.trunc` instruction. + /// Wasm `f32.trunc` equivalent Wasmi instruction. #[snake_name(f32_trunc)] F32Trunc { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f32.nearest` instruction. + /// Wasm `f32.nearest` equivalent Wasmi instruction. #[snake_name(f32_nearest)] F32Nearest { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f32.sqrt` instruction. + /// Wasm `f32.sqrt` equivalent Wasmi instruction. #[snake_name(f32_sqrt)] F32Sqrt { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f32.add` instruction: `r0 = r1 + r2` + /// Wasm `f32.add` equivalent Wasmi instruction. #[snake_name(f32_add)] F32Add { @result: Reg, @@ -4730,7 +4730,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f32.sub` instruction: `r0 = r1 - r2` + /// Wasm `f32.sub` equivalent Wasmi instruction. #[snake_name(f32_sub)] F32Sub { @result: Reg, @@ -4739,7 +4739,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f32.mul` instruction: `r0 = r1 * r2` + /// Wasm `f32.mul` equivalent Wasmi instruction. #[snake_name(f32_mul)] F32Mul { @result: Reg, @@ -4748,7 +4748,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f32.div` instruction: `r0 = r1 / r2` + /// Wasm `f32.div` equivalent Wasmi instruction. #[snake_name(f32_div)] F32Div { @result: Reg, @@ -4757,7 +4757,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f32.min` instruction: `r0 = min(r1, r2)` + /// Wasm `f32.min` equivalent Wasmi instruction. #[snake_name(f32_min)] F32Min { @result: Reg, @@ -4766,7 +4766,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f32.max` instruction: `r0 = max(r1, r2)` + /// Wasm `f32.max` equivalent Wasmi instruction. #[snake_name(f32_max)] F32Max { @result: Reg, @@ -4775,7 +4775,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f32.copysign` instruction: `r0 = copysign(r1, r2)` + /// Wasm `f32.copysign` equivalent Wasmi instruction. #[snake_name(f32_copysign)] F32Copysign { @result: Reg, @@ -4784,7 +4784,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f32.copysign` instruction with immediate: `r0 = copysign(r1, c0)` + /// Wasm `f32.add` equivalent Wasmi instruction with immediate `rhs` value. #[snake_name(f32_copysign_imm)] F32CopysignImm { @result: Reg, @@ -4794,56 +4794,56 @@ macro_rules! for_each_op { rhs: Sign, }, - /// Wasm `f64.abs` instruction. + /// Wasm `f64.abs` equivalent Wasmi instruction. #[snake_name(f64_abs)] F64Abs { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f64.neg` instruction. + /// Wasm `f64.neg` equivalent Wasmi instruction. #[snake_name(f64_neg)] F64Neg { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f64.ceil` instruction. + /// Wasm `f64.ceil` equivalent Wasmi instruction. #[snake_name(f64_ceil)] F64Ceil { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f64.floor` instruction. + /// Wasm `f64.floor` equivalent Wasmi instruction. #[snake_name(f64_floor)] F64Floor { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f64.trunc` instruction. + /// Wasm `f64.trunc` equivalent Wasmi instruction. #[snake_name(f64_trunc)] F64Trunc { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f64.nearest` instruction. + /// Wasm `f64.nearest` equivalent Wasmi instruction. #[snake_name(f64_nearest)] F64Nearest { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f64.sqrt` instruction. + /// Wasm `f64.sqrt` equivalent Wasmi instruction. #[snake_name(f64_sqrt)] F64Sqrt { @result: Reg, /// The register holding the input of the instruction. input: Reg, }, - /// Wasm `f64.add` instruction: `r0 = r1 + r2` + /// Wasm `f64.add` equivalent Wasmi instruction. #[snake_name(f64_add)] F64Add { @result: Reg, @@ -4852,7 +4852,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f64.sub` instruction: `r0 = r1 - r2` + /// Wasm `f64.sub` equivalent Wasmi instruction. #[snake_name(f64_sub)] F64Sub { @result: Reg, @@ -4861,7 +4861,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f64.mul` instruction: `r0 = r1 * r2` + /// Wasm `f64.mul` equivalent Wasmi instruction. #[snake_name(f64_mul)] F64Mul { @result: Reg, @@ -4870,7 +4870,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f64.div` instruction: `r0 = r1 / r2` + /// Wasm `f64.div` equivalent Wasmi instruction. #[snake_name(f64_div)] F64Div { @result: Reg, @@ -4879,7 +4879,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f64.min` instruction: `r0 = min(r1, r2)` + /// Wasm `f64.min` equivalent Wasmi instruction. #[snake_name(f64_min)] F64Min { @result: Reg, @@ -4888,7 +4888,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f64.max` instruction: `r0 = max(r1, r2)` + /// Wasm `f64.max` equivalent Wasmi instruction. #[snake_name(f64_max)] F64Max { @result: Reg, @@ -4897,7 +4897,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f64.copysign` instruction: `r0 = copysign(r1, r2)` + /// Wasm `f64.copysign` equivalent Wasmi instruction. #[snake_name(f64_copysign)] F64Copysign { @result: Reg, @@ -4906,7 +4906,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f64.copysign` instruction with immediate: `r0 = copysign(r1, c0)` + /// Wasm `f64.copysign` equivalent Wasmi instruction with imediate `rhs` value. #[snake_name(f64_copysign_imm)] F64CopysignImm { @result: Reg, From 7a095beff8a3bb9d2549abbd64027053addb8fb9 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 13 Oct 2024 19:30:25 +0200 Subject: [PATCH 03/19] fix doc comment --- crates/ir/src/for_each_op.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ir/src/for_each_op.rs b/crates/ir/src/for_each_op.rs index 14929fbe0d..8ce43865d0 100644 --- a/crates/ir/src/for_each_op.rs +++ b/crates/ir/src/for_each_op.rs @@ -4784,7 +4784,7 @@ macro_rules! for_each_op { /// The register holding the right-hand side value. rhs: Reg, }, - /// Wasm `f32.add` equivalent Wasmi instruction with immediate `rhs` value. + /// Wasm `f32.copysign` equivalent Wasmi instruction with NaN canonicalization. #[snake_name(f32_copysign_imm)] F32CopysignImm { @result: Reg, From 42142f0d637a0c51b403b3fdcbc46d7fde16cf58 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 13 Oct 2024 22:00:28 +0200 Subject: [PATCH 04/19] add new instructions for NaN canonicalization --- crates/ir/src/for_each_op.rs | 172 +++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/crates/ir/src/for_each_op.rs b/crates/ir/src/for_each_op.rs index 8ce43865d0..4ff6b5f08f 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: Reg, + }, /// Wasm `i32.trunc_f32_s` instruction. #[snake_name(i32_trunc_f32_s)] From be94dff6f673e244630ef9b22f55ec4d662520d0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 13 Oct 2024 22:15:08 +0200 Subject: [PATCH 05/19] add CanonicalizeNan trait to wasmi_core --- crates/core/src/lib.rs | 2 +- crates/core/src/value.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) 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/value.rs b/crates/core/src/value.rs index da233825c9..a1744935fd 100644 --- a/crates/core/src/value.rs +++ b/crates/core/src/value.rs @@ -789,3 +789,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() { + // Note: This pattern 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. + let canonicalized = Self::from_bits($nan_val); + debug_assert!(canonicalized.is_nan()); + return canonicalized + } + self + } + } + )* + }; +} +impl_caninocalize_nan! { + f32 as 0x7FC00000_u32, + f64 as 0x7FF8000000000000_u64, +} From 33346afd84539d35b20a61d0917f6eef27a08dca Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 13 Oct 2024 22:15:12 +0200 Subject: [PATCH 06/19] apply rustfmt --- crates/wasmi/src/engine/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/wasmi/src/engine/config.rs b/crates/wasmi/src/engine/config.rs index 7cb0649e46..6eb7aa61ad 100644 --- a/crates/wasmi/src/engine/config.rs +++ b/crates/wasmi/src/engine/config.rs @@ -341,7 +341,7 @@ impl Config { } /// Enable or disable NaN value canonicalization. - /// + /// /// Disabled by default. pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self { self.canonicalize_nans = enable; From 4d34ee232c97e92b8e86a663a72509eefa40c809 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 11:17:12 +0200 Subject: [PATCH 07/19] fix NaN -> Nan spelling --- crates/ir/src/for_each_op.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/ir/src/for_each_op.rs b/crates/ir/src/for_each_op.rs index 4ff6b5f08f..0337aec7e7 100644 --- a/crates/ir/src/for_each_op.rs +++ b/crates/ir/src/for_each_op.rs @@ -4938,7 +4938,7 @@ macro_rules! for_each_op { }, /// Wasm `f64.sqrt` equivalent Wasmi instruction with NaN canonicalization. #[snake_name(f64_sqrt_canonicalize_nan)] - F64SqrtCanonicalizeNaN { + F64SqrtCanonicalizeNan { @result: Reg, /// The register holding the input of the instruction. input: Reg, @@ -4954,7 +4954,7 @@ macro_rules! for_each_op { }, /// Wasm `f64.add` equivalent Wasmi instruction with NaN canonicalization. #[snake_name(f64_add_canonicalize_nan)] - F64AddCanonicalizeNaN { + F64AddCanonicalizeNan { @result: Reg, /// The register holding the left-hand side value. lhs: Reg, @@ -4972,7 +4972,7 @@ macro_rules! for_each_op { }, /// Wasm `f64.sub` equivalent Wasmi instruction with NaN canonicalization. #[snake_name(f64_sub_canonicalize_nan)] - F64SubCanonicalizeNaN { + F64SubCanonicalizeNan { @result: Reg, /// The register holding the left-hand side value. lhs: Reg, @@ -4990,7 +4990,7 @@ macro_rules! for_each_op { }, /// Wasm `f64.mul` equivalent Wasmi instruction with NaN canonicalization. #[snake_name(f64_mul_canonicalize_nan)] - F64MulCanonicalizeNaN { + F64MulCanonicalizeNan { @result: Reg, /// The register holding the left-hand side value. lhs: Reg, @@ -5008,7 +5008,7 @@ macro_rules! for_each_op { }, /// Wasm `f64.div` equivalent Wasmi instruction with NaN canonicalization. #[snake_name(f64_div_canonicalize_nan)] - F64DivCanonicalizeNaN { + F64DivCanonicalizeNan { @result: Reg, /// The register holding the left-hand side value. lhs: Reg, @@ -5026,7 +5026,7 @@ macro_rules! for_each_op { }, /// Wasm `f64.min` equivalent Wasmi instruction with NaN canonicalization. #[snake_name(f64_min_canonicalize_nan)] - F64MinCanonicalizeNaN { + F64MinCanonicalizeNan { @result: Reg, /// The register holding the left-hand side value. lhs: Reg, @@ -5044,7 +5044,7 @@ macro_rules! for_each_op { }, /// Wasm `f64.max` equivalent Wasmi instruction with NaN canonicalization. #[snake_name(f64_max_canonicalize_nan)] - F64MaxCanonicalizeNaN { + F64MaxCanonicalizeNan { @result: Reg, /// The register holding the left-hand side value. lhs: Reg, @@ -5062,7 +5062,7 @@ macro_rules! for_each_op { }, /// Wasm `f64.copysign` equivalent Wasmi instruction with NaN canonicalization. #[snake_name(f64_copysign_canonicalize_nan)] - F64CopysignCanonicalizeNaN { + F64CopysignCanonicalizeNan { @result: Reg, /// The register holding the left-hand side value. lhs: Reg, @@ -5080,7 +5080,7 @@ macro_rules! for_each_op { }, /// Wasm `f64.copysign_imm` equivalent Wasmi instruction with immediate `rhs` value and NaN canonicalization. #[snake_name(f64_copysign_imm_canonicalize_nan)] - F64CopysignImmCanonicalizeNaN { + F64CopysignImmCanonicalizeNan { @result: Reg, /// The register holding the left-hand side value. lhs: Reg, From 9839e2c6624e2d88a3062c7e53f7f340242f36d6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 11:17:33 +0200 Subject: [PATCH 08/19] add stubs for new instructions in executor --- crates/wasmi/src/engine/executor/instrs.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/wasmi/src/engine/executor/instrs.rs b/crates/wasmi/src/engine/executor/instrs.rs index 875d1e7fb6..0ba9513068 100644 --- a/crates/wasmi/src/engine/executor/instrs.rs +++ b/crates/wasmi/src/engine/executor/instrs.rs @@ -1082,42 +1082,62 @@ 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 { .. } => todo!(), 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 { .. } => todo!(), Instr::F32Add { result, lhs, rhs } => self.execute_f32_add(result, lhs, rhs), + Instr::F32AddCanonicalizeNan { .. } => todo!(), Instr::F32Sub { result, lhs, rhs } => self.execute_f32_sub(result, lhs, rhs), + Instr::F32SubCanonicalizeNan { .. } => todo!(), Instr::F32Mul { result, lhs, rhs } => self.execute_f32_mul(result, lhs, rhs), + Instr::F32MulCanonicalizeNan { .. } => todo!(), Instr::F32Div { result, lhs, rhs } => self.execute_f32_div(result, lhs, rhs), + Instr::F32DivCanonicalizeNan { .. } => todo!(), Instr::F32Min { result, lhs, rhs } => self.execute_f32_min(result, lhs, rhs), + Instr::F32MinCanonicalizeNan { .. } => todo!(), Instr::F32Max { result, lhs, rhs } => self.execute_f32_max(result, lhs, rhs), + Instr::F32MaxCanonicalizeNan { .. } => todo!(), Instr::F32Copysign { result, lhs, rhs } => { self.execute_f32_copysign(result, lhs, rhs) } + Instr::F32CopysignCanonicalizeNan { .. } => todo!(), Instr::F32CopysignImm { result, lhs, rhs } => { self.execute_f32_copysign_imm(result, lhs, rhs) } + Instr::F32CopysignImmCanonicalizeNan { .. } => todo!(), Instr::F64Abs { result, input } => self.execute_f64_abs(result, input), Instr::F64Neg { result, input } => self.execute_f64_neg(result, input), + Instr::F64NegCanonicalizeNan { .. } => todo!(), 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 { .. } => todo!(), Instr::F64Add { result, lhs, rhs } => self.execute_f64_add(result, lhs, rhs), + Instr::F64AddCanonicalizeNan { .. } => todo!(), Instr::F64Sub { result, lhs, rhs } => self.execute_f64_sub(result, lhs, rhs), + Instr::F64SubCanonicalizeNan { .. } => todo!(), Instr::F64Mul { result, lhs, rhs } => self.execute_f64_mul(result, lhs, rhs), + Instr::F64MulCanonicalizeNan { .. } => todo!(), Instr::F64Div { result, lhs, rhs } => self.execute_f64_div(result, lhs, rhs), + Instr::F64DivCanonicalizeNan { .. } => todo!(), Instr::F64Min { result, lhs, rhs } => self.execute_f64_min(result, lhs, rhs), + Instr::F64MinCanonicalizeNan { .. } => todo!(), Instr::F64Max { result, lhs, rhs } => self.execute_f64_max(result, lhs, rhs), + Instr::F64MaxCanonicalizeNan { .. } => todo!(), Instr::F64Copysign { result, lhs, rhs } => { self.execute_f64_copysign(result, lhs, rhs) } + Instr::F64CopysignCanonicalizeNan { .. } => todo!(), Instr::F64CopysignImm { result, lhs, rhs } => { self.execute_f64_copysign_imm(result, lhs, rhs) } + Instr::F64CopysignImmCanonicalizeNan { .. } => todo!(), Instr::I32TruncF32S { result, input } => { self.execute_i32_trunc_f32_s(result, input)? } From c442a66e397c03a0fdb7b494f4f63664cb015a6d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 11:50:32 +0200 Subject: [PATCH 09/19] move f{32,64}.{min,max} down in file --- crates/core/src/untyped.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/core/src/untyped.rs b/crates/core/src/untyped.rs index 1baf7f5219..4cafbb6642 100644 --- a/crates/core/src/untyped.rs +++ b/crates/core/src/untyped.rs @@ -956,16 +956,6 @@ impl UntypedVal { self.execute_unary(>::sqrt) } - /// Execute `f32.min` Wasm operation. - pub fn f32_min(self, other: Self) -> Self { - self.execute_binary(other, >::min) - } - - /// Execute `f32.max` Wasm operation. - pub fn f32_max(self, other: Self) -> Self { - self.execute_binary(other, >::max) - } - /// Execute `f32.copysign` Wasm operation. pub fn f32_copysign(self, other: Self) -> Self { self.execute_binary(other, >::copysign) @@ -1046,11 +1036,21 @@ impl UntypedVal { self.execute_binary(rhs, >::div) } + /// Execute `f32.min` Wasm operation. + pub fn f32_min(self, other: Self) -> Self { + self.execute_binary(other, >::min) + } + /// Execute `f64.min` Wasm operation. pub fn f64_min(self, other: Self) -> Self { self.execute_binary(other, >::min) } + /// Execute `f32.max` Wasm operation. + pub fn f32_max(self, other: Self) -> Self { + self.execute_binary(other, >::max) + } + /// Execute `f64.max` Wasm operation. pub fn f64_max(self, other: Self) -> Self { self.execute_binary(other, >::max) From bae827543c18c3ddfedc4aee2029cd8615e9df26 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 11:52:41 +0200 Subject: [PATCH 10/19] move f32_copysign down in file --- crates/core/src/untyped.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/core/src/untyped.rs b/crates/core/src/untyped.rs index 4cafbb6642..f9ea4ab24f 100644 --- a/crates/core/src/untyped.rs +++ b/crates/core/src/untyped.rs @@ -956,11 +956,6 @@ impl UntypedVal { self.execute_unary(>::sqrt) } - /// Execute `f32.copysign` Wasm operation. - pub fn f32_copysign(self, other: Self) -> Self { - self.execute_binary(other, >::copysign) - } - /// Execute `f64.abs` Wasm operation. pub fn f64_abs(self) -> Self { self.execute_unary(>::abs) @@ -1056,6 +1051,11 @@ impl UntypedVal { self.execute_binary(other, >::max) } + /// Execute `f32.copysign` Wasm operation. + pub fn f32_copysign(self, other: Self) -> Self { + self.execute_binary(other, >::copysign) + } + /// Execute `f64.copysign` Wasm operation. pub fn f64_copysign(self, other: Self) -> Self { self.execute_binary(other, >::copysign) From 63956758beb181b23f06c70c7bc226dcc24a657f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 12:00:21 +0200 Subject: [PATCH 11/19] add default T=Self to ArithmeticOps and Float traits --- crates/core/src/value.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/core/src/value.rs b/crates/core/src/value.rs index a1744935fd..a9817e8073 100644 --- a/crates/core/src/value.rs +++ b/crates/core/src/value.rs @@ -197,7 +197,7 @@ impl_little_endian_convert_float!( ); /// Arithmetic operations. -pub trait ArithmeticOps: Copy { +pub trait ArithmeticOps: Copy { /// Add two values. fn add(self, other: T) -> T; /// Subtract two values. @@ -233,7 +233,7 @@ pub trait Integer: ArithmeticOps { } /// Float-point value. -pub trait Float: ArithmeticOps { +pub trait Float: ArithmeticOps { /// Get absolute value. fn abs(self) -> T; /// Returns the largest integer less than or equal to a number. @@ -624,6 +624,8 @@ macro_rules! impl_float { } impl_float!( type F32 as f32 ); impl_float!( type F64 as f64 ); +impl_float!( type f32 as f32 ); +impl_float!( type f64 as f64 ); /// Low-level Wasm float interface to support `no_std` environments. /// From be1a839f73c9ad5dc504e5230c889c45cddb9f8a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 12:01:04 +0200 Subject: [PATCH 12/19] UntypedVal, TypedVal: add Nan canonicalization ops --- crates/core/src/typed.rs | 20 +++++++ crates/core/src/untyped.rs | 119 +++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) 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 f9ea4ab24f..5d39a3d537 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) @@ -956,6 +962,11 @@ impl UntypedVal { 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. pub fn f64_abs(self) -> Self { self.execute_unary(>::abs) @@ -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) From 8704403b80ae4de5bf6052aaaf1fef052301fb98 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 12:12:25 +0200 Subject: [PATCH 13/19] fix F64CopysignImmCanonicalizeNan `rhs` field type --- crates/ir/src/for_each_op.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ir/src/for_each_op.rs b/crates/ir/src/for_each_op.rs index 0337aec7e7..f685394535 100644 --- a/crates/ir/src/for_each_op.rs +++ b/crates/ir/src/for_each_op.rs @@ -5085,7 +5085,7 @@ macro_rules! for_each_op { /// The register holding the left-hand side value. lhs: Reg, /// The register holding the right-hand side value. - rhs: Reg, + rhs: Sign, }, /// Wasm `i32.trunc_f32_s` instruction. From 549e3eddc199179d33e72490fa7c50c15c24edda Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 12:12:50 +0200 Subject: [PATCH 14/19] add proper execution handlers for Nan canonicalization ops --- crates/wasmi/src/engine/executor/instrs.rs | 48 +++++++++++-------- .../src/engine/executor/instrs/binary.rs | 32 +++++++++++++ .../wasmi/src/engine/executor/instrs/unary.rs | 4 ++ 3 files changed, 64 insertions(+), 20 deletions(-) diff --git a/crates/wasmi/src/engine/executor/instrs.rs b/crates/wasmi/src/engine/executor/instrs.rs index 0ba9513068..65b6703752 100644 --- a/crates/wasmi/src/engine/executor/instrs.rs +++ b/crates/wasmi/src/engine/executor/instrs.rs @@ -1082,62 +1082,70 @@ 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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 { .. } => todo!(), + 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..1f612aaf64 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), } } @@ -317,6 +333,14 @@ impl Executor<'_> { self.next_instr() } + /// Executes an [`Instruction::F32CopysignImm`] with NaN canonicalization. + pub fn execute_f32_copysign_imm_canonicalize_nan(&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_canonicalize_nan(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); @@ -324,4 +348,12 @@ impl Executor<'_> { self.set_register(result, UntypedVal::f64_copysign(lhs, rhs.into())); self.next_instr() } + + /// Executes an [`Instruction::F64CopysignImm`] with NaN canonicalization. + pub fn execute_f64_copysign_imm_canonicalize_nan(&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_canonicalize_nan(lhs, rhs.into())); + self.next_instr() + } } 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), } } From d4e310995967854409aa94a7bce8eb8a40832e3c Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 12:15:31 +0200 Subject: [PATCH 15/19] apply rustfmt --- crates/wasmi/src/engine/executor/instrs.rs | 64 ++++++++++++++----- .../src/engine/executor/instrs/binary.rs | 24 +++++-- 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/crates/wasmi/src/engine/executor/instrs.rs b/crates/wasmi/src/engine/executor/instrs.rs index 65b6703752..a3146c2a36 100644 --- a/crates/wasmi/src/engine/executor/instrs.rs +++ b/crates/wasmi/src/engine/executor/instrs.rs @@ -1082,25 +1082,41 @@ 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::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::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::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::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::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::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::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::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) } @@ -1115,25 +1131,41 @@ impl<'engine> Executor<'engine> { } 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::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::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::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::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::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::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::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::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) } diff --git a/crates/wasmi/src/engine/executor/instrs/binary.rs b/crates/wasmi/src/engine/executor/instrs/binary.rs index 1f612aaf64..efd440c41d 100644 --- a/crates/wasmi/src/engine/executor/instrs/binary.rs +++ b/crates/wasmi/src/engine/executor/instrs/binary.rs @@ -334,10 +334,18 @@ impl Executor<'_> { } /// Executes an [`Instruction::F32CopysignImm`] with NaN canonicalization. - pub fn execute_f32_copysign_imm_canonicalize_nan(&mut self, result: Reg, lhs: Reg, rhs: Sign) { + pub fn execute_f32_copysign_imm_canonicalize_nan( + &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_canonicalize_nan(lhs, rhs.into())); + self.set_register( + result, + UntypedVal::f32_copysign_canonicalize_nan(lhs, rhs.into()), + ); self.next_instr() } @@ -350,10 +358,18 @@ impl Executor<'_> { } /// Executes an [`Instruction::F64CopysignImm`] with NaN canonicalization. - pub fn execute_f64_copysign_imm_canonicalize_nan(&mut self, result: Reg, lhs: Reg, rhs: Sign) { + pub fn execute_f64_copysign_imm_canonicalize_nan( + &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_canonicalize_nan(lhs, rhs.into())); + self.set_register( + result, + UntypedVal::f64_copysign_canonicalize_nan(lhs, rhs.into()), + ); self.next_instr() } } From 12dcd31438fad10ca770084963e636b50912717f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 14:56:33 +0200 Subject: [PATCH 16/19] add support for NaN canonicalization to Wasmi translation --- crates/wasmi/src/engine/translator/mod.rs | 5 + crates/wasmi/src/engine/translator/visit.rs | 204 ++++++++++++++++---- 2 files changed, 171 insertions(+), 38 deletions(-) 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 { From 51a2516bdffb913988553f5d724af707a49913d0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 15:01:29 +0200 Subject: [PATCH 17/19] move comment about canonical NaN bit patterns --- crates/core/src/value.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/core/src/value.rs b/crates/core/src/value.rs index a9817e8073..9978a97571 100644 --- a/crates/core/src/value.rs +++ b/crates/core/src/value.rs @@ -804,10 +804,6 @@ macro_rules! impl_caninocalize_nan { impl CanonicalizeNan for $ty { fn canonicalize_nan(self) -> Self { if self.is_nan() { - // Note: This pattern 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. let canonicalized = Self::from_bits($nan_val); debug_assert!(canonicalized.is_nan()); return canonicalized @@ -819,6 +815,10 @@ macro_rules! impl_caninocalize_nan { }; } 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, } From 3795e7445ccad41aa74e8d0e81060ee598b13d8a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 19:06:49 +0200 Subject: [PATCH 18/19] fix invalid code merge --- crates/core/src/untyped.rs | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/crates/core/src/untyped.rs b/crates/core/src/untyped.rs index 7ba49cee7b..fa4bb7accf 100644 --- a/crates/core/src/untyped.rs +++ b/crates/core/src/untyped.rs @@ -1101,11 +1101,6 @@ impl UntypedVal { self.execute_binary(rhs, ::div) } - /// Execute `f32.min` Wasm operation. - pub fn f32_min(self, other: Self) -> Self { - self.execute_binary(other, ::min) - } - /// Execute `f64.div` Wasm operation with NaN canonicalization. pub fn f64_div_canonicalize_nan(self, rhs: Self) -> Self { self.execute_binary(rhs, |lhs, rhs| { @@ -1115,7 +1110,7 @@ impl UntypedVal { /// Execute `f32.min` Wasm operation. pub fn f32_min(self, other: Self) -> Self { - self.execute_binary(other, >::min) + self.execute_binary(other, ::min) } /// Execute `f32.min` Wasm operation with NaN canonicalization. @@ -1130,11 +1125,6 @@ impl UntypedVal { self.execute_binary(other, ::min) } - /// Execute `f32.max` Wasm operation. - pub fn f32_max(self, other: Self) -> Self { - self.execute_binary(other, ::max) - } - /// Execute `f64.min` Wasm operation with NaN canonicalization. pub fn f64_min_canonicalize_nan(self, rhs: Self) -> Self { self.execute_binary(rhs, |lhs, rhs| { @@ -1144,7 +1134,7 @@ impl UntypedVal { /// Execute `f32.max` Wasm operation. pub fn f32_max(self, other: Self) -> Self { - self.execute_binary(other, >::max) + self.execute_binary(other, ::max) } /// Execute `f32.max` Wasm operation with NaN canonicalization. @@ -1159,11 +1149,6 @@ impl UntypedVal { self.execute_binary(other, ::max) } - /// Execute `f32.copysign` Wasm operation. - pub fn f32_copysign(self, other: Self) -> Self { - self.execute_binary(other, ::copysign) - } - /// Execute `f64.max` Wasm operation with NaN canonicalization. pub fn f64_max_canonicalize_nan(self, rhs: Self) -> Self { self.execute_binary(rhs, |lhs, rhs| { @@ -1173,7 +1158,7 @@ impl UntypedVal { /// Execute `f32.copysign` Wasm operation. pub fn f32_copysign(self, other: Self) -> Self { - self.execute_binary(other, >::copysign) + self.execute_binary(other, ::copysign) } /// Execute `f32.copysign` Wasm operation with NaN canonicalization. From 6a726a429dfbbb9ba85089bc526423161823fbd1 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 14 Oct 2024 19:26:21 +0200 Subject: [PATCH 19/19] impl copysign_imm exec handlers via macro --- .../src/engine/executor/instrs/binary.rs | 71 ++++++------------- 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/crates/wasmi/src/engine/executor/instrs/binary.rs b/crates/wasmi/src/engine/executor/instrs/binary.rs index efd440c41d..657290acb9 100644 --- a/crates/wasmi/src/engine/executor/instrs/binary.rs +++ b/crates/wasmi/src/engine/executor/instrs/binary.rs @@ -324,52 +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::F32CopysignImm`] with NaN canonicalization. - pub fn execute_f32_copysign_imm_canonicalize_nan( - &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_canonicalize_nan(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() - } - - /// Executes an [`Instruction::F64CopysignImm`] with NaN canonicalization. - pub fn execute_f64_copysign_imm_canonicalize_nan( - &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_canonicalize_nan(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; }