diff --git a/document/core/appendix/index-instructions.rst b/document/core/appendix/index-instructions.rst index 788915a9d5..7fb2d74d4f 100644 --- a/document/core/appendix/index-instructions.rst +++ b/document/core/appendix/index-instructions.rst @@ -199,4 +199,12 @@ Instruction Binary Opcode Type :math:`\I64.\REINTERPRET\K{\_}\F64` :math:`\hex{BD}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` :math:`\F32.\REINTERPRET\K{\_}\I32` :math:`\hex{BE}` :math:`[\I32] \to [\F32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` :math:`\F64.\REINTERPRET\K{\_}\I64` :math:`\hex{BF}` :math:`[\I64] \to [\F64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_sat}\F32\K{\_s}` :math:`\hex{FC}~\hex{00}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_sat}\F32\K{\_u}` :math:`\hex{FC}~\hex{01}` :math:`[\F32] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_sat}\F64\K{\_s}` :math:`\hex{FC}~\hex{02}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I32.\TRUNC\K{\_sat}\F64\K{\_u}` :math:`\hex{FC}~\hex{03}` :math:`[\F64] \to [\I32]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_sat}\F32\K{\_s}` :math:`\hex{FC}~\hex{04}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_sat}\F32\K{\_u}` :math:`\hex{FC}~\hex{05}` :math:`[\F32] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_sat}\F64\K{\_s}` :math:`\hex{FC}~\hex{06}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` +:math:`\I64.\TRUNC\K{\_sat}\F64\K{\_u}` :math:`\hex{FC}~\hex{07}` :math:`[\F64] \to [\I64]` :ref:`validation ` :ref:`execution `, :ref:`operator ` ====================================== ================ ========================================== ======================================== =============================================================== diff --git a/document/core/binary/instructions.rst b/document/core/binary/instructions.rst index 1d4a8af8ab..0e58154497 100644 --- a/document/core/binary/instructions.rst +++ b/document/core/binary/instructions.rst @@ -363,6 +363,23 @@ All other numeric instructions are plain opcodes without any immediates. \hex{BF} &\Rightarrow& \F64.\REINTERPRET\K{\_}\I64 \\ \end{array} +.. _binary-cvtop-trunc-sat: + +The saturating truncation instructions all have a one byte prefix. + +.. math:: + \begin{array}{llclll} + \production{instruction} & \Binstr &::=& \dots && \phantom{thisshouldbeenough} \\&&|& + \hex{FC}~\hex{00} &\Rightarrow& \I32.\TRUNC\K{\_sat\_}\F32\K{\_s} \\ &&|& + \hex{FC}~\hex{01} &\Rightarrow& \I32.\TRUNC\K{\_sat\_}\F32\K{\_u} \\ &&|& + \hex{FC}~\hex{02} &\Rightarrow& \I32.\TRUNC\K{\_sat\_}\F64\K{\_s} \\ &&|& + \hex{FC}~\hex{03} &\Rightarrow& \I32.\TRUNC\K{\_sat\_}\F64\K{\_u} \\ &&|& + \hex{FC}~\hex{04} &\Rightarrow& \I64.\TRUNC\K{\_sat\_}\F32\K{\_s} \\ &&|& + \hex{FC}~\hex{05} &\Rightarrow& \I64.\TRUNC\K{\_sat\_}\F32\K{\_u} \\ &&|& + \hex{FC}~\hex{06} &\Rightarrow& \I64.\TRUNC\K{\_sat\_}\F64\K{\_s} \\ &&|& + \hex{FC}~\hex{07} &\Rightarrow& \I64.\TRUNC\K{\_sat\_}\F64\K{\_u} \\ + \end{array} + .. index:: expression pair: binary format; expression diff --git a/document/core/exec/numerics.rst b/document/core/exec/numerics.rst index b83ef1cee9..954c9e9fbf 100644 --- a/document/core/exec/numerics.rst +++ b/document/core/exec/numerics.rst @@ -1464,6 +1464,62 @@ Conversions It is not defined for NaNs, infinities, or values for which the result is out of range. +.. _op-trunc_u_sat: + +:math:`\truncusat_{M,N}(z)` +........................... + +* If :math:`z` is a NaN, then return :math:`0`. + +* Else if :math:`z` is negative infinity, then return :math:`0`. + +* Else if :math:`z` is positive infinity, then return :math:`2^N - 1`. + +* Else if :math:`\trunc(z)` is less than :math:`0`, then return :math:`0`. + +* Else if :math:`\trunc(z)` is greater than :math:`2^N - 1`, then return :math:`2^N - 1`. + +* Else, return :math:`\trunc(z)`. + +.. math:: + \begin{array}{lll@{\qquad}l} + \truncusat_{M,N}(\pm \NAN(n)) &=& 0 \\ + \truncusat_{M,N}(- \infty) &=& 0 \\ + \truncusat_{M,N}(+ \infty) &=& 2^N - 1 \\ + \truncusat_{M,N}(- q) &=& 0 & (\iff \trunc(- q) < 0) \\ + \truncusat_{M,N}(+ q) &=& 2^N - 1 & (\iff \trunc(+ q) > 2^N - 1) \\ + \truncusat_{M,N}(\pm q) &=& \trunc(\pm q) & (otherwise) \\ + \end{array} + + +.. _op-trunc_s_sat: + +:math:`\truncssat_{M,N}(z)` +........................... + +* If :math:`z` is a NaN, then return :math:`0`. + +* Else if :math:`z` is negative infinity, then return :math:`-2^{N-1}`. + +* Else if :math:`z` is positive infinity, then return :math:`2^{N-1} - 1`. + +* Else if :math:`\trunc(z)` is less than :math:`-2^{N-1}`, then return :math:`-2^{N-1}`. + +* Else if :math:`\trunc(z)` is greater than :math:`2^{N-1} - 1`, then return :math:`2^{N-1} - 1`. + +* Else, return :math:`\trunc(z)`. + +.. math:: + \begin{array}{lll@{\qquad}l} + \truncssat_{M,N}(\pm \NAN(n)) &=& 0 \\ + \truncssat_{M,N}(- \infty) &=& -2^{N-1} \\ + \truncssat_{M,N}(+ \infty) &=& 2^{N-1}-1 \\ + \truncssat_{M,N}(- q) &=& -2^{N-1} & (\iff \trunc(- q) < -2^{N-1}) \\ + \truncssat_{M,N}(+ q) &=& 2^{N-1} - 1 & (\iff \trunc(+ q) > 2^{N-1} - 1) \\ + \truncssat_{M,N}(\pm q) &=& \trunc(\pm q) & (otherwise) \\ + \end{array} + + .. _op-promote: :math:`\promote_{M,N}(z)` diff --git a/document/core/syntax/instructions.rst b/document/core/syntax/instructions.rst index 4f6d613c4f..f60f26d508 100644 --- a/document/core/syntax/instructions.rst +++ b/document/core/syntax/instructions.rst @@ -67,6 +67,7 @@ These operations closely match respective operations available in hardware. \K{i32.}\WRAP\K{\_i64} ~|~ \K{i64.}\EXTEND\K{\_i32}\K{\_}\sx ~|~ \K{i}\X{nn}\K{.}\TRUNC\K{\_f}\X{mm}\K{\_}\sx \\&&|& + \K{i}\X{nn}\K{.}\TRUNC\K{\_sat\_f}\X{mm}\K{\_}\sx \\&&|& \K{f32.}\DEMOTE\K{\_f64} ~|~ \K{f64.}\PROMOTE\K{\_f32} ~|~ \K{f}\X{nn}\K{.}\CONVERT\K{\_i}\X{mm}\K{\_}\sx \\&&|& @@ -160,6 +161,7 @@ Occasionally, it is convenient to group operators together according to the foll \WRAP ~|~ \EXTEND ~|~ \TRUNC ~|~ + \TRUNC\K{\_sat} ~|~ \CONVERT ~|~ \DEMOTE ~|~ \PROMOTE ~|~ diff --git a/document/core/text/instructions.rst b/document/core/text/instructions.rst index 11182070b9..f813bae06c 100644 --- a/document/core/text/instructions.rst +++ b/document/core/text/instructions.rst @@ -391,12 +391,20 @@ Numeric Instructions \text{i32.trunc\_f32\_u} &\Rightarrow& \I32.\TRUNC\K{\_}\F32\K{\_u} \\ &&|& \text{i32.trunc\_f64\_s} &\Rightarrow& \I32.\TRUNC\K{\_}\F64\K{\_s} \\ &&|& \text{i32.trunc\_f64\_u} &\Rightarrow& \I32.\TRUNC\K{\_}\F64\K{\_u} \\ &&|& + \text{i32.trunc\_sat_f32\_s} &\Rightarrow& \I32.\TRUNC\K{\_sat\_}\F32\K{\_s} \\ &&|& + \text{i32.trunc\_sat_f32\_u} &\Rightarrow& \I32.\TRUNC\K{\_sat\_}\F32\K{\_u} \\ &&|& + \text{i32.trunc\_sat_f64\_s} &\Rightarrow& \I32.\TRUNC\K{\_sat\_}\F64\K{\_s} \\ &&|& + \text{i32.trunc\_sat_f64\_u} &\Rightarrow& \I32.\TRUNC\K{\_sat\_}\F64\K{\_u} \\ &&|& \text{i64.extend\_i32\_s} &\Rightarrow& \I64.\EXTEND\K{\_}\I32\K{\_s} \\ &&|& \text{i64.extend\_i32\_u} &\Rightarrow& \I64.\EXTEND\K{\_}\I32\K{\_u} \\ &&|& \text{i64.trunc\_f32\_s} &\Rightarrow& \I64.\TRUNC\K{\_}\F32\K{\_s} \\ &&|& \text{i64.trunc\_f32\_u} &\Rightarrow& \I64.\TRUNC\K{\_}\F32\K{\_u} \\ &&|& \text{i64.trunc\_f64\_s} &\Rightarrow& \I64.\TRUNC\K{\_}\F64\K{\_s} \\ &&|& \text{i64.trunc\_f64\_u} &\Rightarrow& \I64.\TRUNC\K{\_}\F64\K{\_u} \\ &&|& + \text{i64.trunc\_sat_f32\_s} &\Rightarrow& \I64.\TRUNC\K{\_sat\_}\F32\K{\_s} \\ &&|& + \text{i64.trunc\_sat_f32\_u} &\Rightarrow& \I64.\TRUNC\K{\_sat\_}\F32\K{\_u} \\ &&|& + \text{i64.trunc\_sat_f64\_s} &\Rightarrow& \I64.\TRUNC\K{\_sat\_}\F64\K{\_s} \\ &&|& + \text{i64.trunc\_sat_f64\_u} &\Rightarrow& \I64.\TRUNC\K{\_sat\_}\F64\K{\_u} \\ &&|& \text{f32.convert\_i32\_s} &\Rightarrow& \F32.\CONVERT\K{\_}\I32\K{\_s} \\ &&|& \text{f32.convert\_i32\_u} &\Rightarrow& \F32.\CONVERT\K{\_}\I32\K{\_u} \\ &&|& \text{f32.convert\_i64\_s} &\Rightarrow& \F32.\CONVERT\K{\_}\I64\K{\_s} \\ &&|& diff --git a/document/core/util/macros.def b/document/core/util/macros.def index 0354e12a00..c9e3cf0cec 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -948,6 +948,8 @@ .. |wrap| mathdef:: \xref{exec/numerics}{op-wrap}{\F{wrap}} .. |truncu| mathdef:: \xref{exec/numerics}{op-trunc_u}{\F{trunc}^{\K{u}}} .. |truncs| mathdef:: \xref{exec/numerics}{op-trunc_s}{\F{trunc}^{\K{s}}} +.. |truncusat| mathdef:: \xref{exec/numerics}{op-trunc_sat_u}{\F{trunc\_sat\_u}} +.. |truncssat| mathdef:: \xref{exec/numerics}{op-trunc_sat_s}{\F{trunc\_sat\_s}} .. |promote| mathdef:: \xref{exec/numerics}{op-promote}{\F{promote}} .. |demote| mathdef:: \xref{exec/numerics}{op-demote}{\F{demote}} .. |convertu| mathdef:: \xref{exec/numerics}{op-convert_u}{\F{convert}^{\K{u}}} diff --git a/interpreter/binary/decode.ml b/interpreter/binary/decode.ml index cff17feaad..da291092a5 100644 --- a/interpreter/binary/decode.ml +++ b/interpreter/binary/decode.ml @@ -200,6 +200,19 @@ let memop s = let offset = vu32 s in Int32.to_int align, offset +let math_prefix s = + let pos = pos s in + match op s with + | 0x00 -> i32_trunc_sat_f32_s + | 0x01 -> i32_trunc_sat_f32_u + | 0x02 -> i32_trunc_sat_f64_s + | 0x03 -> i32_trunc_sat_f64_u + | 0x04 -> i64_trunc_sat_f32_s + | 0x05 -> i64_trunc_sat_f32_u + | 0x06 -> i64_trunc_sat_f64_s + | 0x07 -> i64_trunc_sat_f64_u + | b -> illegal s pos b + let rec instr s = let pos = pos s in match op s with @@ -432,6 +445,8 @@ let rec instr s = | 0xbe -> f32_reinterpret_i32 | 0xbf -> f64_reinterpret_i64 + | 0xfc -> math_prefix s + | b -> illegal s pos b and instr_block s = List.rev (instr_block' s []) diff --git a/interpreter/binary/encode.ml b/interpreter/binary/encode.ml index e6f3275673..4fbb52b157 100644 --- a/interpreter/binary/encode.ml +++ b/interpreter/binary/encode.ml @@ -336,6 +336,10 @@ let encode m = | Convert (I32 I32Op.TruncUF32) -> op 0xa9 | Convert (I32 I32Op.TruncSF64) -> op 0xaa | Convert (I32 I32Op.TruncUF64) -> op 0xab + | Convert (I32 I32Op.TruncSSatF32) -> op 0xfc; op 0x00 + | Convert (I32 I32Op.TruncUSatF32) -> op 0xfc; op 0x01 + | Convert (I32 I32Op.TruncSSatF64) -> op 0xfc; op 0x02 + | Convert (I32 I32Op.TruncUSatF64) -> op 0xfc; op 0x03 | Convert (I32 I32Op.ReinterpretFloat) -> op 0xbc | Convert (I64 I64Op.ExtendSI32) -> op 0xac @@ -345,6 +349,10 @@ let encode m = | Convert (I64 I64Op.TruncUF32) -> op 0xaf | Convert (I64 I64Op.TruncSF64) -> op 0xb0 | Convert (I64 I64Op.TruncUF64) -> op 0xb1 + | Convert (I64 I64Op.TruncSSatF32) -> op 0xfc; op 0x04 + | Convert (I64 I64Op.TruncUSatF32) -> op 0xfc; op 0x05 + | Convert (I64 I64Op.TruncSSatF64) -> op 0xfc; op 0x06 + | Convert (I64 I64Op.TruncUSatF64) -> op 0xfc; op 0x07 | Convert (I64 I64Op.ReinterpretFloat) -> op 0xbd | Convert (F32 F32Op.ConvertSI32) -> op 0xb2 diff --git a/interpreter/exec/eval_numeric.ml b/interpreter/exec/eval_numeric.ml index ea54cf991c..c8f634dfb5 100644 --- a/interpreter/exec/eval_numeric.ml +++ b/interpreter/exec/eval_numeric.ml @@ -130,6 +130,10 @@ struct | TruncUF32 -> I32 (I32_convert.trunc_f32_u (F32Op.of_value 1 v)) | TruncSF64 -> I32 (I32_convert.trunc_f64_s (F64Op.of_value 1 v)) | TruncUF64 -> I32 (I32_convert.trunc_f64_u (F64Op.of_value 1 v)) + | TruncSSatF32 -> I32 (I32_convert.trunc_sat_f32_s (F32Op.of_value 1 v)) + | TruncUSatF32 -> I32 (I32_convert.trunc_sat_f32_u (F32Op.of_value 1 v)) + | TruncSSatF64 -> I32 (I32_convert.trunc_sat_f64_s (F64Op.of_value 1 v)) + | TruncUSatF64 -> I32 (I32_convert.trunc_sat_f64_u (F64Op.of_value 1 v)) | ReinterpretFloat -> I32 (I32_convert.reinterpret_f32 (F32Op.of_value 1 v)) | ExtendSI32 -> raise (TypeError (1, v, I32Type)) | ExtendUI32 -> raise (TypeError (1, v, I32Type)) @@ -147,6 +151,10 @@ struct | TruncUF32 -> I64 (I64_convert.trunc_f32_u (F32Op.of_value 1 v)) | TruncSF64 -> I64 (I64_convert.trunc_f64_s (F64Op.of_value 1 v)) | TruncUF64 -> I64 (I64_convert.trunc_f64_u (F64Op.of_value 1 v)) + | TruncSSatF32 -> I64 (I64_convert.trunc_sat_f32_s (F32Op.of_value 1 v)) + | TruncUSatF32 -> I64 (I64_convert.trunc_sat_f32_u (F32Op.of_value 1 v)) + | TruncSSatF64 -> I64 (I64_convert.trunc_sat_f64_s (F64Op.of_value 1 v)) + | TruncUSatF64 -> I64 (I64_convert.trunc_sat_f64_u (F64Op.of_value 1 v)) | ReinterpretFloat -> I64 (I64_convert.reinterpret_f64 (F64Op.of_value 1 v)) | WrapI64 -> raise (TypeError (1, v, I64Type)) end diff --git a/interpreter/exec/i32_convert.ml b/interpreter/exec/i32_convert.ml index ac4b0f89ad..482783a221 100644 --- a/interpreter/exec/i32_convert.ml +++ b/interpreter/exec/i32_convert.ml @@ -42,4 +42,52 @@ let trunc_f64_u x = else Int64.(to_int32 (of_float xf)) +let trunc_sat_f32_s x = + if F32.ne x x then + 0l + else + let xf = F32.to_float x in + if xf < Int32.(to_float min_int) then + Int32.min_int + else if xf >= -.Int32.(to_float min_int) then + Int32.max_int + else + Int32.of_float xf + +let trunc_sat_f32_u x = + if F32.ne x x then + 0l + else + let xf = F32.to_float x in + if xf <= -1.0 then + 0l + else if xf >= -.Int32.(to_float min_int) *. 2.0 then + -1l + else + Int64.(to_int32 (of_float xf)) + +let trunc_sat_f64_s x = + if F64.ne x x then + 0l + else + let xf = F64.to_float x in + if xf < Int32.(to_float min_int) then + Int32.min_int + else if xf >= -.Int32.(to_float min_int) then + Int32.max_int + else + Int32.of_float xf + +let trunc_sat_f64_u x = + if F64.ne x x then + 0l + else + let xf = F64.to_float x in + if xf <= -1.0 then + 0l + else if xf >= -.Int32.(to_float min_int) *. 2.0 then + -1l + else + Int64.(to_int32 (of_float xf)) + let reinterpret_f32 = F32.to_bits diff --git a/interpreter/exec/i32_convert.mli b/interpreter/exec/i32_convert.mli index 20e4eb40d9..706ef6d8b6 100644 --- a/interpreter/exec/i32_convert.mli +++ b/interpreter/exec/i32_convert.mli @@ -5,4 +5,8 @@ val trunc_f32_s : F32.t -> I32.t val trunc_f32_u : F32.t -> I32.t val trunc_f64_s : F64.t -> I32.t val trunc_f64_u : F64.t -> I32.t +val trunc_sat_f32_s : F32.t -> I32.t +val trunc_sat_f32_u : F32.t -> I32.t +val trunc_sat_f64_s : F64.t -> I32.t +val trunc_sat_f64_u : F64.t -> I32.t val reinterpret_f32 : F32.t -> I32.t diff --git a/interpreter/exec/i64_convert.ml b/interpreter/exec/i64_convert.ml index 0919d32409..f71e9ff702 100644 --- a/interpreter/exec/i64_convert.ml +++ b/interpreter/exec/i64_convert.ml @@ -48,4 +48,56 @@ let trunc_f64_u x = else Int64.of_float xf +let trunc_sat_f32_s x = + if F32.ne x x then + 0L + else + let xf = F32.to_float x in + if xf < Int64.(to_float min_int) then + Int64.min_int + else if xf >= -.Int64.(to_float min_int) then + Int64.max_int + else + Int64.of_float xf + +let trunc_sat_f32_u x = + if F32.ne x x then + 0L + else + let xf = F32.to_float x in + if xf <= -1.0 then + 0L + else if xf >= -.Int64.(to_float min_int) *. 2.0 then + -1L + else if xf >= -.Int64.(to_float min_int) then + Int64.(logxor (of_float (xf -. 9223372036854775808.0)) min_int) + else + Int64.of_float xf + +let trunc_sat_f64_s x = + if F64.ne x x then + 0L + else + let xf = F64.to_float x in + if xf < Int64.(to_float min_int) then + Int64.min_int + else if xf >= -.Int64.(to_float min_int) then + Int64.max_int + else + Int64.of_float xf + +let trunc_sat_f64_u x = + if F64.ne x x then + 0L + else + let xf = F64.to_float x in + if xf <= -1.0 then + 0L + else if xf >= -.Int64.(to_float min_int) *. 2.0 then + -1L + else if xf >= -.Int64.(to_float min_int) then + Int64.(logxor (of_float (xf -. 9223372036854775808.0)) min_int) + else + Int64.of_float xf + let reinterpret_f64 = F64.to_bits diff --git a/interpreter/exec/i64_convert.mli b/interpreter/exec/i64_convert.mli index 02a0ce4506..5b0a47b337 100644 --- a/interpreter/exec/i64_convert.mli +++ b/interpreter/exec/i64_convert.mli @@ -6,4 +6,8 @@ val trunc_f32_s : F32.t -> I64.t val trunc_f32_u : F32.t -> I64.t val trunc_f64_s : F64.t -> I64.t val trunc_f64_u : F64.t -> I64.t +val trunc_sat_f32_s : F32.t -> I64.t +val trunc_sat_f32_u : F32.t -> I64.t +val trunc_sat_f64_s : F64.t -> I64.t +val trunc_sat_f64_u : F64.t -> I64.t val reinterpret_f64 : F64.t -> I64.t diff --git a/interpreter/syntax/ast.ml b/interpreter/syntax/ast.ml index 8eb6d32383..4e3a3e396e 100644 --- a/interpreter/syntax/ast.ml +++ b/interpreter/syntax/ast.ml @@ -30,6 +30,7 @@ struct type relop = Eq | Ne | LtS | LtU | GtS | GtU | LeS | LeU | GeS | GeU type cvtop = ExtendSI32 | ExtendUI32 | WrapI64 | TruncSF32 | TruncUF32 | TruncSF64 | TruncUF64 + | TruncSSatF32 | TruncUSatF32 | TruncSSatF64 | TruncUSatF64 | ReinterpretFloat end diff --git a/interpreter/syntax/operators.ml b/interpreter/syntax/operators.ml index ff7588fa0f..17b424fdcc 100644 --- a/interpreter/syntax/operators.ml +++ b/interpreter/syntax/operators.ml @@ -178,6 +178,10 @@ let i32_trunc_f32_s = Convert (I32 I32Op.TruncSF32) let i32_trunc_f32_u = Convert (I32 I32Op.TruncUF32) let i32_trunc_f64_s = Convert (I32 I32Op.TruncSF64) let i32_trunc_f64_u = Convert (I32 I32Op.TruncUF64) +let i32_trunc_sat_f32_s = Convert (I32 I32Op.TruncSSatF32) +let i32_trunc_sat_f32_u = Convert (I32 I32Op.TruncUSatF32) +let i32_trunc_sat_f64_s = Convert (I32 I32Op.TruncSSatF64) +let i32_trunc_sat_f64_u = Convert (I32 I32Op.TruncUSatF64) let i64_extend_i32_s = Convert (I64 I64Op.ExtendSI32) let i64_extend_i32_u = Convert (I64 I64Op.ExtendUI32) let i64_trunc_f32_s = Convert (I64 I64Op.TruncSF32) @@ -188,6 +192,10 @@ let f32_convert_i32_s = Convert (F32 F32Op.ConvertSI32) let f32_convert_i32_u = Convert (F32 F32Op.ConvertUI32) let f32_convert_i64_s = Convert (F32 F32Op.ConvertSI64) let f32_convert_i64_u = Convert (F32 F32Op.ConvertUI64) +let i64_trunc_sat_f32_s = Convert (I64 I64Op.TruncSSatF32) +let i64_trunc_sat_f32_u = Convert (I64 I64Op.TruncUSatF32) +let i64_trunc_sat_f64_s = Convert (I64 I64Op.TruncSSatF64) +let i64_trunc_sat_f64_u = Convert (I64 I64Op.TruncUSatF64) let f32_demote_f64 = Convert (F32 F32Op.DemoteF64) let f64_convert_i32_s = Convert (F64 F64Op.ConvertSI32) let f64_convert_i32_u = Convert (F64 F64Op.ConvertUI32) diff --git a/interpreter/text/arrange.ml b/interpreter/text/arrange.ml index 7b415bd636..e381b42fea 100644 --- a/interpreter/text/arrange.ml +++ b/interpreter/text/arrange.ml @@ -127,6 +127,10 @@ struct | TruncUF32 -> "trunc_f32_u" | TruncSF64 -> "trunc_f64_s" | TruncUF64 -> "trunc_f64_u" + | TruncSSatF32 -> "trunc_sat_f32_s" + | TruncUSatF32 -> "trunc_sat_f32_u" + | TruncSSatF64 -> "trunc_sat_f64_s" + | TruncUSatF64 -> "trunc_sat_f64_u" | ReinterpretFloat -> "reinterpret_f" ^ xx end diff --git a/interpreter/text/lexer.mll b/interpreter/text/lexer.mll index 3483149df6..3c8d56e90a 100644 --- a/interpreter/text/lexer.mll +++ b/interpreter/text/lexer.mll @@ -301,6 +301,14 @@ rule token = parse { CONVERT (intop t i32_trunc_f64_s i64_trunc_f64_s) } | (ixx as t)".trunc_f64_u" { CONVERT (intop t i32_trunc_f64_u i64_trunc_f64_u) } + | (ixx as t)".trunc_sat_f32_s" + { CONVERT (intop t i32_trunc_sat_f32_s i64_trunc_sat_f32_s) } + | (ixx as t)".trunc_sat_f32_u" + { CONVERT (intop t i32_trunc_sat_f32_u i64_trunc_sat_f32_u) } + | (ixx as t)".trunc_sat_f64_s" + { CONVERT (intop t i32_trunc_sat_f64_s i64_trunc_sat_f64_s) } + | (ixx as t)".trunc_sat_f64_u" + { CONVERT (intop t i32_trunc_sat_f64_u i64_trunc_sat_f64_u) } | (fxx as t)".convert_i32_s" { CONVERT (floatop t f32_convert_i32_s f64_convert_i32_s) } | (fxx as t)".convert_i32_u" diff --git a/interpreter/valid/valid.ml b/interpreter/valid/valid.ml index 5b18d1a1e1..44cbdb75d8 100644 --- a/interpreter/valid/valid.ml +++ b/interpreter/valid/valid.ml @@ -106,16 +106,18 @@ let type_cvtop at = function (match cvtop with | ExtendSI32 | ExtendUI32 -> error at "invalid conversion" | WrapI64 -> I64Type - | TruncSF32 | TruncUF32 | ReinterpretFloat -> F32Type - | TruncSF64 | TruncUF64 -> F64Type + | TruncSF32 | TruncUF32 | TruncSSatF32 | TruncUSatF32 + | ReinterpretFloat -> F32Type + | TruncSF64 | TruncUF64 | TruncSSatF64 | TruncUSatF64 -> F64Type ), I32Type | Values.I64 cvtop -> let open I64Op in (match cvtop with | ExtendSI32 | ExtendUI32 -> I32Type | WrapI64 -> error at "invalid conversion" - | TruncSF32 | TruncUF32 -> F32Type - | TruncSF64 | TruncUF64 | ReinterpretFloat -> F64Type + | TruncSF32 | TruncUF32 | TruncSSatF32 | TruncUSatF32 -> F32Type + | TruncSF64 | TruncUF64 | TruncSSatF64 | TruncUSatF64 + | ReinterpretFloat -> F64Type ), I64Type | Values.F32 cvtop -> let open F32Op in diff --git a/proposals/nontrapping-float-to-int-conversion/Overview.md b/proposals/nontrapping-float-to-int-conversion/Overview.md new file mode 100644 index 0000000000..574fcde70a --- /dev/null +++ b/proposals/nontrapping-float-to-int-conversion/Overview.md @@ -0,0 +1,93 @@ +# Non-trapping Float-to-int Conversions + +## Introduction + +### Motivation + +The primary motivations are: + + - LLVM’s float-to-int conversion has an undefined result, rather than undefined behavior, and it seems LLVM does speculate it under certain conditions. + - For the SIMD proposal, it’s more SIMD-hardware-like if no SIMD operations trap. This proposal would establish a convention for saturating operations which SIMD could share, to avoid introducing trapping. + +This proposal is not motivated by performance data. + +It's plausible that LLVM could be changed, to have something like an "nsw" flag for the "fptosi" instruction, especially with some of the recent proposals to modify the poison concept in LLVM. However, no one is currently working on this. + +### Background + +This issue has been discussed in several areas including here: + +https://github.com/WebAssembly/design/issues/986 + +While performance concerns initially motivated the discussion, the performance +effects were due to a particular implementation detail which has since been +fixed. + +Since then, no real-world performance problems related to this issue have +been reported. + +This topic was discussed at the CG-05 meeting: + +https://github.com/WebAssembly/meetings/pull/3 + +and + +https://github.com/WebAssembly/meetings/blob/master/2017/CG-05.md#non-trapping-float-to-int + +which made decisions about which semantics to choose, and which encoding strategy. + +These decisions were captured in a design repo PR: + +https://github.com/WebAssembly/design/pull/1089 + +At the CG-07-06 meeting it was decided that a full spec repo fork should be +created to follow the new process for new features: + +https://github.com/WebAssembly/meetings/blob/master/2017/CG-07-06.md#float-to-int-conversion + +This led to the creation of the present repo: + +https://github.com/WebAssembly/nontrapping-float-to-int-conversions + +### Design + +This proposal introduces 8 new instructions: + + - `i32.trunc_sat_f32_s` + - `i32.trunc_sat_f32_u` + - `i32.trunc_sat_f64_s` + - `i32.trunc_sat_f64_u` + - `i64.trunc_sat_f32_s` + - `i64.trunc_sat_f32_u` + - `i64.trunc_sat_f64_s` + - `i64.trunc_sat_f64_u` + +The semantics are the same as the corresponding non-`_sat` instructions, except: + - Instead of trapping on positive or negative overflow, they return the maximum + or minimum integer value, respectively, and do not trap. (This behavior is + also referred to as "saturating".) + - Instead of trapping on NaN, they return 0 and do not trap. + +### Encoding + +This proposal introduces a new prefix byte: + +| Prefix | Name | Description | +| ------ | ------- | ----------- | +| `0xfc` | misc | Miscellaneous operations :bowling: | + +which is intended to be used as a prefix for other future miscellaneous operations +as well. + +The encodings for the new instructions use this new prefix and are as follows: + +| Name | Opcode | Immediate | Description | +| ---- | ---- | ---- | ---- | +| `i32.trunc_sat_f32_s` | `0xfc` `0x00` | | :bowling: saturating form of `i32.trunc_f32_s` | +| `i32.trunc_sat_f32_u` | `0xfc` `0x01` | | :bowling: saturating form of `i32.trunc_f32_u` | +| `i32.trunc_sat_f64_s` | `0xfc` `0x02` | | :bowling: saturating form of `i32.trunc_f64_s` | +| `i32.trunc_sat_f64_u` | `0xfc` `0x03` | | :bowling: saturating form of `i32.trunc_f64_u` | +| `i64.trunc_sat_f32_s` | `0xfc` `0x04` | | :bowling: saturating form of `i64.trunc_f32_s` | +| `i64.trunc_sat_f32_u` | `0xfc` `0x05` | | :bowling: saturating form of `i64.trunc_f32_u` | +| `i64.trunc_sat_f64_s` | `0xfc` `0x06` | | :bowling: saturating form of `i64.trunc_f64_s` | +| `i64.trunc_sat_f64_u` | `0xfc` `0x07` | | :bowling: saturating form of `i64.trunc_f64_u` | diff --git a/test/core/conversions.wast b/test/core/conversions.wast index 308296d2e0..99355a0857 100644 --- a/test/core/conversions.wast +++ b/test/core/conversions.wast @@ -10,6 +10,14 @@ (func (export "i64.trunc_f32_u") (param $x f32) (result i64) (i64.trunc_f32_u (local.get $x))) (func (export "i64.trunc_f64_s") (param $x f64) (result i64) (i64.trunc_f64_s (local.get $x))) (func (export "i64.trunc_f64_u") (param $x f64) (result i64) (i64.trunc_f64_u (local.get $x))) + (func (export "i32.trunc_sat_f32_s") (param $x f32) (result i32) (i32.trunc_sat_f32_s (local.get $x))) + (func (export "i32.trunc_sat_f32_u") (param $x f32) (result i32) (i32.trunc_sat_f32_u (local.get $x))) + (func (export "i32.trunc_sat_f64_s") (param $x f64) (result i32) (i32.trunc_sat_f64_s (local.get $x))) + (func (export "i32.trunc_sat_f64_u") (param $x f64) (result i32) (i32.trunc_sat_f64_u (local.get $x))) + (func (export "i64.trunc_sat_f32_s") (param $x f32) (result i64) (i64.trunc_sat_f32_s (local.get $x))) + (func (export "i64.trunc_sat_f32_u") (param $x f32) (result i64) (i64.trunc_sat_f32_u (local.get $x))) + (func (export "i64.trunc_sat_f64_s") (param $x f64) (result i64) (i64.trunc_sat_f64_s (local.get $x))) + (func (export "i64.trunc_sat_f64_u") (param $x f64) (result i64) (i64.trunc_sat_f64_u (local.get $x))) (func (export "f32.convert_i32_s") (param $x i32) (result f32) (f32.convert_i32_s (local.get $x))) (func (export "f32.convert_i64_s") (param $x i64) (result f32) (f32.convert_i64_s (local.get $x))) (func (export "f64.convert_i32_s") (param $x i32) (result f64) (f64.convert_i32_s (local.get $x))) @@ -247,6 +255,197 @@ (assert_return (invoke "f32.convert_i32_s" (i32.const 2147483647)) (f32.const 2147483648)) (assert_return (invoke "f32.convert_i32_s" (i32.const -2147483648)) (f32.const -2147483648)) (assert_return (invoke "f32.convert_i32_s" (i32.const 1234567890)) (f32.const 0x1.26580cp+30)) + +;; Saturating conversions: test all the same values as the non-saturating conversions. + +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 0x1.19999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -1.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -0x1.19999ap+0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -1.5)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -1.9)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -2.0)) (i32.const -2)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 2147483520.0)) (i32.const 2147483520)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -2147483648.0)) (i32.const -2147483648)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 2147483648.0)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -2147483904.0)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const inf)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -inf)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -nan:0x200000)) (i32.const 0)) + +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 0x1.19999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 1.9)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 2.0)) (i32.const 2)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000 +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 4294967040.0)) (i32.const -256)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -0x1.ccccccp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -0x1.fffffep-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 4294967296.0)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -1.0)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const inf)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -inf)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -nan:0x200000)) (i32.const 0)) + +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 0x1.199999999999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -1.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -0x1.199999999999ap+0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -1.5)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -1.9)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -2.0)) (i32.const -2)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 2147483647.0)) (i32.const 2147483647)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -2147483648.0)) (i32.const -2147483648)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 2147483648.0)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -2147483649.0)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const inf)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -inf)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -nan:0x4000000000000)) (i32.const 0)) + +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 0x1.199999999999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1.9)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 2.0)) (i32.const 2)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000 +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 4294967295.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -0x1.ccccccccccccdp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -0x1.fffffffffffffp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1e8)) (i32.const 100000000)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 4294967296.0)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -1.0)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1e16)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1e30)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 9223372036854775808)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const inf)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -inf)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -nan:0x4000000000000)) (i32.const 0)) + +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 0x1.19999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -1.0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -0x1.19999ap+0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -1.5)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -1.9)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -2.0)) (i64.const -2)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000 +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000 +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 9223371487098961920.0)) (i64.const 9223371487098961920)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -9223372036854775808.0)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 9223372036854775808.0)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -9223373136366403584.0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const inf)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -inf)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const nan:0x200000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -nan:0x200000)) (i64.const 0)) + +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 0x1.19999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 4294967296)) (i64.const 4294967296)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 18446742974197923840.0)) (i64.const -1099511627776)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -0x1.ccccccp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -0x1.fffffep-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 18446744073709551616.0)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -1.0)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const inf)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -inf)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const nan:0x200000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -nan:0x200000)) (i64.const 0)) + +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 0x1.199999999999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -1.0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -0x1.199999999999ap+0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -1.5)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -1.9)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -2.0)) (i64.const -2)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000 +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000 +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 9223372036854774784.0)) (i64.const 9223372036854774784)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -9223372036854775808.0)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 9223372036854775808.0)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -9223372036854777856.0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const inf)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -inf)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const nan:0x4000000000000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -nan:0x4000000000000)) (i64.const 0)) + +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 0x1.199999999999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 4294967295)) (i64.const 0xffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 4294967296)) (i64.const 0x100000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 18446744073709549568.0)) (i64.const -2048)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -0x1.ccccccccccccdp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -0x1.fffffffffffffp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 1e8)) (i64.const 100000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 1e16)) (i64.const 10000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 9223372036854775808)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 18446744073709551616.0)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -1.0)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const inf)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -inf)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const nan:0x4000000000000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -nan:0x4000000000000)) (i64.const 0)) + ;; Test rounding directions. (assert_return (invoke "f32.convert_i32_s" (i32.const 16777217)) (f32.const 16777216.0)) (assert_return (invoke "f32.convert_i32_s" (i32.const -16777217)) (f32.const -16777216.0))