Repository: bytecodealliance/wasmtime
Pull request #8189 (2024-03-20 14:21:34)

Clarify that `v128` is affected with NaN canonicalization
Brought up on [Zulip] as something that's good to call out in the documentation.

[Zulip]: https://bytecodealliance.zulipchat.com/#narrow/stream/206238-general/topic/Determinism.20of.20Wasm.20SIMD.20in.20Wasmtime/near/427666631

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/RELEASES.md b/RELEASES.md
index 9a554aed0..52b80b5b5 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -16,7 +16,7 @@ Unreleased.
 
 ## 19.0.0
 
-Unreleased.
+Released 2024-03-20
 
 ### Added
 
diff --git a/cranelift/codegen/src/nan_canonicalization.rs b/cranelift/codegen/src/nan_canonicalization.rs
index bb791d60b..c1bc16f77 100644
--- a/cranelift/codegen/src/nan_canonicalization.rs
+++ b/cranelift/codegen/src/nan_canonicalization.rs
@@ -38,6 +38,10 @@ fn is_fp_arith(pos: &mut FuncCursor, inst: Inst) -> bool {
                 || opcode == Opcode::Nearest
                 || opcode == Opcode::Sqrt
                 || opcode == Opcode::Trunc
+                || opcode == Opcode::Fdemote
+                || opcode == Opcode::Fpromote
+                || opcode == Opcode::FvpromoteLow
+                || opcode == Opcode::Fvdemote
         }
         InstructionData::Binary { opcode, .. } => {
             opcode == Opcode::Fadd
@@ -48,24 +52,6 @@ fn is_fp_arith(pos: &mut FuncCursor, inst: Inst) -> bool {
                 || opcode == Opcode::Fsub
         }
         InstructionData::Ternary { opcode, .. } => opcode == Opcode::Fma,
-
-        // bitcasts coming from arbitrary integers need canonicalization as the
-        // integer isn't known to necessarily be a canonical NaN
-        InstructionData::LoadNoOffset {
-            opcode: Opcode::Bitcast,
-            arg,
-            flags: _,
-        } => {
-            let ret = pos.func.dfg.first_result(inst);
-            let ret_type = pos.func.dfg.value_type(ret);
-            let arg_type = pos.func.dfg.value_type(arg);
-            match (arg_type, ret_type) {
-                (types::I32, types::F32) => true,
-                (types::I64, types::F64) => true,
-                _ => false,
-            }
-        }
-
         _ => false,
     }
 }
diff --git a/crates/runtime/src/traphandlers.rs b/crates/runtime/src/traphandlers.rs
index 06855d307..92003e8b3 100644
--- a/crates/runtime/src/traphandlers.rs
+++ b/crates/runtime/src/traphandlers.rs
@@ -521,15 +521,6 @@ impl CallThreadState {
     }
 }
 
-struct ResetCell<'a, T: Copy>(&'a Cell<T>, T);
-
-impl<T: Copy> Drop for ResetCell<'_, T> {
-    #[inline]
-    fn drop(&mut self) {
-        self.0.set(self.1);
-    }
-}
-
 // A private inner module for managing the TLS state that we require across
 // calls in wasm. The WebAssembly code is called from C++ and then a trap may
 // happen which requires us to read some contextual state to figure out what to
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index bef3e7061..271c9bc25 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -1009,9 +1009,14 @@ impl Config {
     /// Configures whether Cranelift should perform a NaN-canonicalization pass.
     ///
     /// When Cranelift is used as a code generation backend this will configure
-    /// it to replace NaNs with a single canonical value. This is useful for users
-    /// requiring entirely deterministic WebAssembly computation.
-    /// This is not required by the WebAssembly spec, so it is not enabled by default.
+    /// it to replace NaNs with a single canonical value. This is useful for
+    /// users requiring entirely deterministic WebAssembly computation.  This is
+    /// not required by the WebAssembly spec, so it is not enabled by default.
+    ///
+    /// Note that this option affects not only WebAssembly's `f32` and `f64`
+    /// types but additionally the `v128` type. This option will cause
+    /// operations using any of these types to have extra checks placed after
+    /// them to normalize NaN values as needed.
     ///
     /// The default value for this is `false`
     #[cfg(any(feature = "cranelift", feature = "winch"))]


Test patch:
diff --git a/tests/misc_testsuite/simd/canonicalize-nan.wast b/tests/misc_testsuite/simd/canonicalize-nan.wast
index b35a941f0..8a0ae71d9 100644
--- a/tests/misc_testsuite/simd/canonicalize-nan.wast
+++ b/tests/misc_testsuite/simd/canonicalize-nan.wast
@@ -39,6 +39,32 @@
     f64.reinterpret_i64
     f32.demote_f64
     i32.reinterpret_f32)
+
+  (func (export "reinterpret-and-promote") (param i32) (result i64)
+    local.get 0
+    f32.reinterpret_i32
+    f64.promote_f32
+    i64.reinterpret_f64)
+
+  (func (export "copysign-and-demote") (param f64) (result f32)
+    local.get 0
+    f64.const -0x1
+    f64.copysign
+    f32.demote_f64)
+
+  (func (export "copysign-and-promote") (param f32) (result f64)
+    local.get 0
+    f32.const -0x1
+    f32.copysign
+    f64.promote_f32)
+
+  (func (export "f32x4.demote_f64x2_zero") (param v128) (result v128)
+    local.get 0
+    f32x4.demote_f64x2_zero)
+
+  (func (export "f64x2.promote_low_f32x4") (param v128) (result v128)
+    local.get 0
+    f64x2.promote_low_f32x4)
 )
 
 (assert_return (invoke "f32x4.floor" (v128.const f32x4 1 -2.2 3.4 nan))
@@ -65,3 +91,17 @@
 
 (assert_return (invoke "reinterpret-and-demote" (i64.const 0xfffefdfccccdcecf))
                (i32.const 0x7fc00000))
+(assert_return (invoke "reinterpret-and-promote" (i32.const 0xfffefdfc))
+               (i64.const 0x7ff8000000000000))
+(assert_return (invoke "copysign-and-demote" (f64.const nan))
+               (f32.const nan:0x7fc00000))
+(assert_return (invoke "copysign-and-promote" (f32.const nan))
+               (f64.const nan:0x7ff8000000000000))
+
+(assert_return (invoke "f32x4.demote_f64x2_zero"
+               (v128.const i64x2 0xfffefdfccccdcecf 0xfffefdfccccdcecf))
+               (v128.const f32x4 nan:0x7fc00000 nan:0x7fc00000 0 0))
+
+(assert_return (invoke "f64x2.promote_low_f32x4"
+               (v128.const i32x4 0xfffefdfc 0xfffefdfc 0 0))
+               (v128.const f64x2 nan:0x7ff8000000000000 nan:0x7ff8000000000000))