Skip to content

[spec/test] Relax relaxed_trunc semantics #1906

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: wasm-3.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions document/core/exec/numerics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2205,20 +2205,20 @@ The implementation-specific behaviour of this operation is determined by the glo
:math:`\relaxedtrunc^u_{M,N}(z)`
................................

The implementation-specific behaviour of this operation is determined by the global parameter :math:`R_{\F{trunc\_u}} \in \{0, 1, 2, 3\}`.
The implementation-specific behaviour of this operation is determined by the global parameter :math:`R_{\F{trunc\_u}} \in \{0, 1\}`.

* If :math:`z` is normal or subnormal and :math:`\trunc(z)` is non-negative and less than :math:`2^N`, then return :math:`\truncu_{M,N}(z)`.

* Else, return :math:`\relaxed(R_{\F{trunc\_u}})[ \truncsatu_{M,N}(z), 2^N-1, 2^N-2, 2^(N-1) ]`.
* Else, return :math:`\relaxed(R_{\F{trunc\_u}})[ \truncsatu_{M,N}(z), \mathbf{R} ]`.

.. math::
\begin{array}{@{}lcll}
\relaxedtrunc^u_{M,N}(\pm q) &=& \truncu_{M,N}(\pm q) & (\iff 0 \leq \trunc(\pm q) < 2^N) \\
\relaxedtrunc^u_{M,N}(z) &=& \relaxed(R_{\F{trunc\_u}})[ \truncsatu_{M,N}(z), 2^{N}-1, 2^{N}-2, 2^{N-1}] & (\otherwise) \\
\relaxedtrunc^u_{M,N}(z) &=& \relaxed(R_{\F{trunc\_u}})[ \truncsatu_{M,N}(z), \mathbf{R}] & (\otherwise) \\
\end{array}

.. note::
Relaxed unsigned truncation is implementation-dependent for NaNs and out-of-range values.
Relaxed unsigned truncation is non-deterministic for NaNs and out-of-range values.
In the :ref:`deterministic profile <profile-deterministic>`,
it behaves like regular :math:`\truncsatu`.

Expand All @@ -2232,16 +2232,16 @@ The implementation-specific behaviour of this operation is determined by the glo

* If :math:`z` is normal or subnormal and :math:`\trunc(z)` is greater than or equal to :math:`-2^{N-1}` and less than :math:`2^{N-1}`, then return :math:`\truncs_{M,N}(z)`.

* Else, return :math:`\relaxed(R_{\F{trunc\_s}})[ \truncsats_{M,N}(z), 2^N-1, 2^N-2, 2^(N-1) ]`.
* Else, return :math:`\relaxed(R_{\F{trunc\_s}})[ \truncsats_{M,N}(z), \mathbf{R} ]`.

.. math::
\begin{array}{@{}lcll}
\relaxedtrunc^s_{M,N}(\pm q) &=& \truncs_{M,N}(\pm q) & (\iff -2^{N-1} \leq \trunc(\pm q) < 2^{N-1}) \\
\relaxedtrunc^s_{M,N}(z) &=& \relaxed(R_{\F{trunc\_s}})[ \truncsats_{M,N}(z), \signed^{-1}_N(-2^{N-1})] & (\otherwise) \\
\relaxedtrunc^s_{M,N}(z) &=& \relaxed(R_{\F{trunc\_s}})[ \truncsats_{M,N}(z), \mathbf{R}] & (\otherwise) \\
\end{array}

.. note::
Relaxed signed truncation is implementation-dependent for NaNs and out-of-range values.
Relaxed signed truncation is non-deterministic for NaNs and out-of-range values.
In the :ref:`deterministic profile <profile-deterministic>`,
it behaves like regular :math:`\truncsats`.

Expand Down
116 changes: 0 additions & 116 deletions test/core/relaxed-simd/i32x4_relaxed_trunc.wast
Original file line number Diff line number Diff line change
@@ -1,124 +1,8 @@
;; Tests for i32x4.relaxed_trunc_f32x4_s, i32x4.relaxed_trunc_f32x4_u, i32x4.relaxed_trunc_f64x2_s_zero, and i32x4.relaxed_trunc_f64x2_u_zero.
;; `either` comes from https://github.com/WebAssembly/threads.

(module
(func (export "i32x4.relaxed_trunc_f32x4_s") (param v128) (result v128) (i32x4.relaxed_trunc_f32x4_s (local.get 0)))
(func (export "i32x4.relaxed_trunc_f32x4_u") (param v128) (result v128) (i32x4.relaxed_trunc_f32x4_u (local.get 0)))
(func (export "i32x4.relaxed_trunc_f64x2_s_zero") (param v128) (result v128) (i32x4.relaxed_trunc_f64x2_s_zero (local.get 0)))
(func (export "i32x4.relaxed_trunc_f64x2_u_zero") (param v128) (result v128) (i32x4.relaxed_trunc_f64x2_u_zero (local.get 0)))

(func (export "i32x4.relaxed_trunc_f32x4_s_cmp") (param v128) (result v128)
(i32x4.eq
(i32x4.relaxed_trunc_f32x4_s (local.get 0))
(i32x4.relaxed_trunc_f32x4_s (local.get 0))))
(func (export "i32x4.relaxed_trunc_f32x4_u_cmp") (param v128) (result v128)
(i32x4.eq
(i32x4.relaxed_trunc_f32x4_u (local.get 0))
(i32x4.relaxed_trunc_f32x4_u (local.get 0))))
(func (export "i32x4.relaxed_trunc_f64x2_s_zero_cmp") (param v128) (result v128)
(i32x4.eq
(i32x4.relaxed_trunc_f64x2_s_zero (local.get 0))
(i32x4.relaxed_trunc_f64x2_s_zero (local.get 0))))
(func (export "i32x4.relaxed_trunc_f64x2_u_zero_cmp") (param v128) (result v128)
(i32x4.eq
(i32x4.relaxed_trunc_f64x2_u_zero (local.get 0))
(i32x4.relaxed_trunc_f64x2_u_zero (local.get 0))))
)

;; Test some edge cases around min/max to ensure that the instruction either
;; saturates correctly or returns INT_MIN.
;;
;; Note, though, that INT_MAX itself is not tested. The value for INT_MAX is
;; 2147483647 but that is not representable in a `f32` since it requires 31 bits
;; when a f32 has only 24 bits available. This means that the closest integers
;; to INT_MAX which can be represented are 2147483520 and 2147483648, meaning
;; that the INT_MAX test case cannot be tested.
(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s"
;; INT32_MIN <INT32_MIN >INT32_MAX
(v128.const f32x4 -2147483648.0 -2147483904.0 2.0 2147483904.0))
;; out of range -> saturate or INT32_MIN
(either (v128.const i32x4 -2147483648 -2147483648 2 2147483647)
(v128.const i32x4 -2147483648 -2147483648 2 -2147483648)))

(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s"
(v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
;; nans -> 0 or INT32_MIN
(either (v128.const i32x4 0 0 0 0)
(v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)))

(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u"
;; UINT32_MIN UINT32_MIN-1 <UINT32_MAX UINT32_MAX+1
(v128.const f32x4 0 -1.0 4294967040.0 4294967296.0))
;; out of range -> saturate or UINT32_MAX
(either (v128.const i32x4 0 0 4294967040 0xffffffff)
(v128.const i32x4 0 0xffffffff 4294967040 0xffffffff)))

(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u"
(v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
;; nans -> 0 or UINT32_MAX
(either (v128.const i32x4 0 0 0 0)
(v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)))

(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero"
(v128.const f64x2 -2147483904.0 2147483904.0))
;; out of range -> saturate or INT32_MIN
(either (v128.const i32x4 -2147483648 2147483647 0 0)
(v128.const i32x4 -2147483648 -2147483648 0 0)))

(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero"
(v128.const f64x2 nan -nan))
(either (v128.const i32x4 0 0 0 0)
(v128.const i32x4 0x80000000 0x80000000 0 0)))

(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero"
(v128.const f64x2 -1.0 4294967296.0))
;; out of range -> saturate or UINT32_MAX
(either (v128.const i32x4 0 0xffffffff 0 0)
(v128.const i32x4 0xffffffff 0xffffffff 0 0)))

(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero"
(v128.const f64x2 nan -nan))
(either (v128.const i32x4 0 0 0 0)
(v128.const i32x4 0 0 0xffffffff 0xffffffff)))

;; Check that multiple calls to the relaxed instruction with same inputs returns same results.

(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s_cmp"
;; INT32_MIN <INT32_MIN INT32_MAX >INT32_MAX
(v128.const f32x4 -2147483648.0 -2147483904.0 2147483647.0 2147483904.0))
;; out of range -> saturate or INT32_MIN
(v128.const i32x4 -1 -1 -1 -1))

(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s_cmp"
(v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
;; nans -> 0 or INT32_MIN
(v128.const i32x4 -1 -1 -1 -1))

(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u_cmp"
;; UINT32_MIN UINT32_MIN-1 <UINT32_MAX UINT32_MAX+1
(v128.const f32x4 0 -1.0 4294967040.0 4294967296.0))
;; out of range -> saturate or UINT32_MAX
(v128.const i32x4 -1 -1 -1 -1))

(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u_cmp"
(v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
;; nans -> 0 or UINT32_MAX
(v128.const i32x4 -1 -1 -1 -1))

(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero_cmp"
(v128.const f64x2 -2147483904.0 2147483904.0))
;; out of range -> saturate or INT32_MIN
(v128.const i32x4 -1 -1 -1 -1))

(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero_cmp"
(v128.const f64x2 nan -nan))
(v128.const i32x4 -1 -1 -1 -1))

(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero_cmp"
(v128.const f64x2 -1.0 4294967296.0))
;; out of range -> saturate or UINT32_MAX
(v128.const i32x4 -1 -1 -1 -1))

(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero_cmp"
(v128.const f64x2 nan -nan))
(v128.const i32x4 -1 -1 -1 -1))
Loading