Skip to content

Commit

Permalink
Merge sign-extension-ops proposal into spec
Browse files Browse the repository at this point in the history
See the sign-extension-ops proposal here:

https://github.com/WebAssembly/sign-extension-ops

This PR is built on top of #1143 (merge nontrapping-float-to-int)
  • Loading branch information
binji committed Mar 19, 2020
1 parent bebedb8 commit 87f2a5a
Show file tree
Hide file tree
Showing 21 changed files with 208 additions and 33 deletions.
5 changes: 5 additions & 0 deletions document/core/appendix/index-instructions.rst
Expand Up @@ -199,6 +199,11 @@ Instruction Binary Opcode Type
:math:`\I64.\REINTERPRET\K{\_}\F64` :math:`\hex{BD}` :math:`[\F64] \to [\I64]` :ref:`validation <valid-cvtop>` :ref:`execution <exec-cvtop>`, :ref:`operator <op-reinterpret>`
:math:`\F32.\REINTERPRET\K{\_}\I32` :math:`\hex{BE}` :math:`[\I32] \to [\F32]` :ref:`validation <valid-cvtop>` :ref:`execution <exec-cvtop>`, :ref:`operator <op-reinterpret>`
:math:`\F64.\REINTERPRET\K{\_}\I64` :math:`\hex{BF}` :math:`[\I64] \to [\F64]` :ref:`validation <valid-cvtop>` :ref:`execution <exec-cvtop>`, :ref:`operator <op-reinterpret>`
:math:`\I32.\EXTEND\K{8\_s}` :math:`\hex{C0}` :math:`[\I32] \to [\I32]` :ref:`validation <valid-unop>` :ref:`execution <exec-unop>`, :ref:`operator <op-iextendn_s>`
:math:`\I32.\EXTEND\K{16\_s}` :math:`\hex{C1}` :math:`[\I32] \to [\I32]` :ref:`validation <valid-unop>` :ref:`execution <exec-unop>`, :ref:`operator <op-iextendn_s>`
:math:`\I64.\EXTEND\K{8\_s}` :math:`\hex{C2}` :math:`[\I64] \to [\I64]` :ref:`validation <valid-unop>` :ref:`execution <exec-unop>`, :ref:`operator <op-iextendn_s>`
:math:`\I64.\EXTEND\K{16\_s}` :math:`\hex{C3}` :math:`[\I64] \to [\I64]` :ref:`validation <valid-unop>` :ref:`execution <exec-unop>`, :ref:`operator <op-iextendn_s>`
:math:`\I64.\EXTEND\K{32\_s}` :math:`\hex{C4}` :math:`[\I64] \to [\I64]` :ref:`validation <valid-unop>` :ref:`execution <exec-unop>`, :ref:`operator <op-iextendn_s>`
:math:`\I32.\TRUNC\K{\_sat\_}\F32\K{\_s}` :math:`\hex{FC}~\hex{00}` :math:`[\F32] \to [\I32]` :ref:`validation <valid-cvtop>` :ref:`execution <exec-cvtop>`, :ref:`operator <op-trunc_s_sat>`
:math:`\I32.\TRUNC\K{\_sat\_}\F32\K{\_u}` :math:`\hex{FC}~\hex{01}` :math:`[\F32] \to [\I32]` :ref:`validation <valid-cvtop>` :ref:`execution <exec-cvtop>`, :ref:`operator <op-trunc_u_sat>`
:math:`\I32.\TRUNC\K{\_sat\_}\F64\K{\_s}` :math:`\hex{FC}~\hex{02}` :math:`[\F64] \to [\I32]` :ref:`validation <valid-cvtop>` :ref:`execution <exec-cvtop>`, :ref:`operator <op-trunc_s_sat>`
Expand Down
11 changes: 11 additions & 0 deletions document/core/binary/instructions.rst
Expand Up @@ -381,6 +381,17 @@ The saturating truncation instructions all have a one byte prefix.
\end{array}
.. math::
\begin{array}{llclll}
\phantom{\production{instruction}} & \phantom{\Binstr} &\phantom{::=}& \phantom{\dots} && \phantom{thisshouldbeenough} \\[-2ex] &&|&
\hex{C0} &\Rightarrow& \I32.\EXTEND\K{8\_s} \\ &&|&
\hex{C1} &\Rightarrow& \I32.\EXTEND\K{16\_s} \\ &&|&
\hex{C2} &\Rightarrow& \I64.\EXTEND\K{8\_s} \\ &&|&
\hex{C3} &\Rightarrow& \I64.\EXTEND\K{16\_s} \\ &&|&
\hex{C4} &\Rightarrow& \I64.\EXTEND\K{32\_s} \\
\end{array}
.. index:: expression
pair: binary format; expression
single: expression; constant
Expand Down
13 changes: 13 additions & 0 deletions document/core/exec/numerics.rst
Expand Up @@ -612,6 +612,19 @@ The integer result of predicates -- i.e., :ref:`tests <syntax-testop>` and :ref:
\end{array}
.. _op-iextendn_s:

:math:`\iextendns_N(i)`
.......................

* Return :math:`\extends_{M,N}(i)`.

.. math::
\begin{array}{lll@{\qquad}l}
\iextendns_{N}(i) &=& \extends_{M,N}(i) \\
\end{array}
.. index:: floating-point, IEEE 754
.. _float-ops:

Expand Down
9 changes: 7 additions & 2 deletions document/core/syntax/instructions.rst
Expand Up @@ -64,8 +64,10 @@ These operations closely match respective operations available in hardware.
\K{i}\X{nn}\K{.}\itestop \\&&|&
\K{i}\X{nn}\K{.}\irelop ~|~
\K{f}\X{nn}\K{.}\frelop \\&&|&
\K{i32.}\WRAP\K{\_i64} ~|~
\K{i}\X{nn}\K{.}\EXTEND\K{8\_s} ~|~
\K{i}\X{nn}\K{.}\EXTEND\K{16\_s} ~|~
\K{i64.}\EXTEND\K{\_i32}\K{\_}\sx ~|~
\K{i32.}\WRAP\K{\_i64} ~|~
\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} ~|~
Expand Down Expand Up @@ -153,7 +155,10 @@ Occasionally, it is convenient to group operators together according to the foll

.. math::
\begin{array}{llll}
\production{unary operator} & \unop &::=& \iunop ~|~ \funop \\
\production{unary operator} & \unop &::=&
\iunop ~|~
\funop ~|~
\EXTEND{N}\K{\_s} ~|~ \\
\production{binary operator} & \binop &::=& \ibinop ~|~ \fbinop \\
\production{test operator} & \testop &::=& \itestop \\
\production{relational operator} & \relop &::=& \irelop ~|~ \frelop \\
Expand Down
10 changes: 10 additions & 0 deletions document/core/text/instructions.rst
Expand Up @@ -421,6 +421,16 @@ Numeric Instructions
\text{f64.reinterpret\_i64} &\Rightarrow& \F64.\REINTERPRET\K{\_}\I64 \\
\end{array}
.. math::
\begin{array}{llclll}
\phantom{\production{instruction}} & \phantom{\Tplaininstr_I} &\phantom{::=}& \phantom{thisisenough} && \phantom{thisshouldbeenough} \\[-2ex] &&|&
\text{i32.extend8\_s} &\Rightarrow& \I32.\EXTEND\K{8\_s} \\ &&|&
\text{i32.extend16\_s} &\Rightarrow& \I32.\EXTEND\K{16\_s} \\ &&|&
\text{i64.extend8\_s} &\Rightarrow& \I64.\EXTEND\K{8\_s} \\ &&|&
\text{i64.extend16\_s} &\Rightarrow& \I64.\EXTEND\K{16\_s} \\ &&|&
\text{i64.extend32\_s} &\Rightarrow& \I64.\EXTEND\K{32\_s} \\
\end{array}
.. index:: ! folded instruction, S-expression
.. _text-foldedinstr:
Expand Down
1 change: 1 addition & 0 deletions document/core/util/macros.def
Expand Up @@ -920,6 +920,7 @@
.. |iles| mathdef:: \xref{exec/numerics}{op-ile_s}{\F{ile\_s}}
.. |igeu| mathdef:: \xref{exec/numerics}{op-ige_u}{\F{ige\_u}}
.. |iges| mathdef:: \xref{exec/numerics}{op-ige_s}{\F{ige\_s}}
.. |iextendns| mathdef:: \xref{exec/numerics}{op-iextendn_s}{\F{iextend}M\F{\_s}}

.. |fadd| mathdef:: \xref{exec/numerics}{op-fadd}{\F{fadd}}
.. |fsub| mathdef:: \xref{exec/numerics}{op-fsub}{\F{fsub}}
Expand Down
6 changes: 6 additions & 0 deletions interpreter/binary/decode.ml
Expand Up @@ -445,6 +445,12 @@ let rec instr s =
| 0xbe -> f32_reinterpret_i32
| 0xbf -> f64_reinterpret_i64

| 0xc0 -> i32_extend8_s
| 0xc1 -> i32_extend16_s
| 0xc2 -> i64_extend8_s
| 0xc3 -> i64_extend16_s
| 0xc4 -> i64_extend32_s

| 0xfc -> math_prefix s

| b -> illegal s pos b
Expand Down
7 changes: 6 additions & 1 deletion interpreter/binary/encode.ml
Expand Up @@ -130,7 +130,6 @@ let encode m =
open Source
open Ast
open Values
open Memory

let op n = u8 n
let end_ () = op 0x0b
Expand Down Expand Up @@ -260,10 +259,16 @@ let encode m =
| Unary (I32 I32Op.Clz) -> op 0x67
| Unary (I32 I32Op.Ctz) -> op 0x68
| Unary (I32 I32Op.Popcnt) -> op 0x69
| Unary (I32 (I32Op.ExtendS Pack8)) -> op 0xc0
| Unary (I32 (I32Op.ExtendS Pack16)) -> op 0xc1
| Unary (I32 (I32Op.ExtendS Pack32)) -> assert false

| Unary (I64 I64Op.Clz) -> op 0x79
| Unary (I64 I64Op.Ctz) -> op 0x7a
| Unary (I64 I64Op.Popcnt) -> op 0x7b
| Unary (I64 (I64Op.ExtendS Pack8)) -> op 0xc2
| Unary (I64 (I64Op.ExtendS Pack16)) -> op 0xc3
| Unary (I64 (I64Op.ExtendS Pack32)) -> op 0xc4

| Unary (F32 F32Op.Abs) -> op 0x8b
| Unary (F32 F32Op.Neg) -> op 0x8c
Expand Down
1 change: 1 addition & 0 deletions interpreter/exec/eval_numeric.ml
Expand Up @@ -24,6 +24,7 @@ struct
| Clz -> IXX.clz
| Ctz -> IXX.ctz
| Popcnt -> IXX.popcnt
| ExtendS sz -> IXX.extend_s (8 * packed_size sz)
in fun v -> to_value (f (of_value 1 v))

let binop op =
Expand Down
5 changes: 5 additions & 0 deletions interpreter/exec/int.ml
Expand Up @@ -58,6 +58,7 @@ sig
val clz : t -> t
val ctz : t -> t
val popcnt : t -> t
val extend_s : int -> t -> t
val eqz : t -> bool
val eq : t -> t -> bool
val ne : t -> t -> bool
Expand Down Expand Up @@ -201,6 +202,10 @@ struct
loop acc' (i - 1) (Rep.shift_right_logical n 1)
in Rep.of_int (loop 0 Rep.bitwidth x)

let extend_s n x =
let shift = Rep.bitwidth - n in
Rep.shift_right (Rep.shift_left x shift) shift

let eqz x = x = Rep.zero

let eq x y = x = y
Expand Down
8 changes: 0 additions & 8 deletions interpreter/runtime/memory.ml
Expand Up @@ -7,9 +7,6 @@ type size = int32 (* number of pages *)
type address = int64
type offset = int32

type pack_size = Pack8 | Pack16 | Pack32
type extension = SX | ZX

type memory' = (int, int8_unsigned_elt, c_layout) Array1.t
type memory = {mutable content : memory'; max : size option}
type t = memory
Expand All @@ -22,11 +19,6 @@ exception OutOfMemory

let page_size = 0x10000L (* 64 KiB *)

let packed_size = function
| Pack8 -> 1
| Pack16 -> 2
| Pack32 -> 4

let within_limits n = function
| None -> true
| Some max -> I32.le_u n max
Expand Down
4 changes: 0 additions & 4 deletions interpreter/runtime/memory.mli
Expand Up @@ -8,17 +8,13 @@ type size = int32 (* number of pages *)
type address = int64
type offset = int32

type pack_size = Pack8 | Pack16 | Pack32
type extension = SX | ZX

exception Type
exception Bounds
exception SizeOverflow
exception SizeLimit
exception OutOfMemory

val page_size : int64
val packed_size : pack_size -> int

val alloc : memory_type -> memory (* raises SizeOverflow, OutOfMemory *)
val type_of : memory -> memory_type
Expand Down
6 changes: 3 additions & 3 deletions interpreter/syntax/ast.ml
Expand Up @@ -23,7 +23,7 @@ open Types

module IntOp =
struct
type unop = Clz | Ctz | Popcnt
type unop = Clz | Ctz | Popcnt | ExtendS of pack_size
type binop = Add | Sub | Mul | DivS | DivU | RemS | RemU
| And | Or | Xor | Shl | ShrS | ShrU | Rotl | Rotr
type testop = Eqz
Expand Down Expand Up @@ -58,8 +58,8 @@ type cvtop = (I32Op.cvtop, I64Op.cvtop, F32Op.cvtop, F64Op.cvtop) Values.op

type 'a memop =
{ty : value_type; align : int; offset : Memory.offset; sz : 'a option}
type loadop = (Memory.pack_size * Memory.extension) memop
type storeop = Memory.pack_size memop
type loadop = (pack_size * extension) memop
type storeop = pack_size memop


(* Expressions *)
Expand Down
7 changes: 6 additions & 1 deletion interpreter/syntax/operators.ml
@@ -1,7 +1,6 @@
open Source
open Types
open Values
open Memory
open Ast


Expand Down Expand Up @@ -207,6 +206,12 @@ let i64_reinterpret_f64 = Convert (I64 I64Op.ReinterpretFloat)
let f32_reinterpret_i32 = Convert (F32 F32Op.ReinterpretInt)
let f64_reinterpret_i64 = Convert (F64 F64Op.ReinterpretInt)

let i32_extend8_s = Unary (I32 (I32Op.ExtendS Pack8))
let i32_extend16_s = Unary (I32 (I32Op.ExtendS Pack16))
let i64_extend8_s = Unary (I64 (I64Op.ExtendS Pack8))
let i64_extend16_s = Unary (I64 (I64Op.ExtendS Pack16))
let i64_extend32_s = Unary (I64 (I64Op.ExtendS Pack32))

let memory_size = MemorySize
let memory_grow = MemoryGrow

8 changes: 8 additions & 0 deletions interpreter/syntax/types.ml
Expand Up @@ -16,13 +16,21 @@ type extern_type =
| ExternMemoryType of memory_type
| ExternGlobalType of global_type

type pack_size = Pack8 | Pack16 | Pack32
type extension = SX | ZX


(* Attributes *)

let size = function
| I32Type | F32Type -> 4
| I64Type | F64Type -> 8

let packed_size = function
| Pack8 -> 1
| Pack16 -> 2
| Pack32 -> 4


(* Subtyping *)

Expand Down
23 changes: 12 additions & 11 deletions interpreter/text/arrange.ml
Expand Up @@ -75,6 +75,15 @@ let global_type = function
| GlobalType (t, Immutable) -> atom string_of_value_type t
| GlobalType (t, Mutable) -> Node ("mut", [atom string_of_value_type t])

let pack_size = function
| Pack8 -> "8"
| Pack16 -> "16"
| Pack32 -> "32"

let extension = function
| SX -> "_s"
| ZX -> "_u"


(* Operators *)

Expand All @@ -101,6 +110,7 @@ struct
| Clz -> "clz"
| Ctz -> "ctz"
| Popcnt -> "popcnt"
| ExtendS sz -> "extend" ^ pack_size sz ^ "_s"

let binop xx = function
| Add -> "add"
Expand Down Expand Up @@ -191,15 +201,6 @@ let testop = oper (IntOp.testop, FloatOp.testop)
let relop = oper (IntOp.relop, FloatOp.relop)
let cvtop = oper (IntOp.cvtop, FloatOp.cvtop)

let pack_size = function
| Memory.Pack8 -> "8"
| Memory.Pack16 -> "16"
| Memory.Pack32 -> "32"

let extension = function
| Memory.SX -> "_s"
| Memory.ZX -> "_u"

let memop name {ty; align; offset; _} sz =
value_type ty ^ "." ^ name ^
(if offset = 0l then "" else " offset=" ^ nat32 offset) ^
Expand All @@ -209,12 +210,12 @@ let loadop op =
match op.sz with
| None -> memop "load" op (size op.ty)
| Some (sz, ext) ->
memop ("load" ^ pack_size sz ^ extension ext) op (Memory.packed_size sz)
memop ("load" ^ pack_size sz ^ extension ext) op (packed_size sz)

let storeop op =
match op.sz with
| None -> memop "store" op (size op.ty)
| Some sz -> memop ("store" ^ pack_size sz) op (Memory.packed_size sz)
| Some sz -> memop ("store" ^ pack_size sz) op (packed_size sz)


(* Expressions *)
Expand Down
3 changes: 3 additions & 0 deletions interpreter/text/lexer.mll
Expand Up @@ -238,6 +238,9 @@ rule token = parse
| (ixx as t)".clz" { UNARY (intop t i32_clz i64_clz) }
| (ixx as t)".ctz" { UNARY (intop t i32_ctz i64_ctz) }
| (ixx as t)".popcnt" { UNARY (intop t i32_popcnt i64_popcnt) }
| (ixx as t)".extend8_s" { UNARY (intop t i32_extend8_s i64_extend8_s) }
| (ixx as t)".extend16_s" { UNARY (intop t i32_extend16_s i64_extend16_s) }
| "i64.extend32_s" { UNARY i64_extend32_s }
| (fxx as t)".neg" { UNARY (floatop t f32_neg f64_neg) }
| (fxx as t)".abs" { UNARY (floatop t f32_abs f64_abs) }
| (fxx as t)".sqrt" { UNARY (floatop t f32_sqrt f64_sqrt) }
Expand Down
15 changes: 12 additions & 3 deletions interpreter/valid/valid.ml
Expand Up @@ -139,15 +139,23 @@ let type_cvtop at = function

(* Expressions *)

let check_pack sz t at =
require (packed_size sz < size t) at "invalid sign extension"

let check_unop unop at =
match unop with
| Values.I32 (IntOp.ExtendS sz) | Values.I64 (IntOp.ExtendS sz) ->
check_pack sz (Values.type_of unop) at
| _ -> ()

let check_memop (c : context) (memop : 'a memop) get_sz at =
ignore (memory c (0l @@ at));
let size =
match get_sz memop.sz with
| None -> size memop.ty
| Some sz ->
require (memop.ty = I64Type || sz <> Memory.Pack32) at
"memory size too big";
Memory.packed_size sz
check_pack sz memop.ty at;
packed_size sz
in
require (1 lsl memop.align <= size) at
"alignment must not be larger than natural"
Expand Down Expand Up @@ -279,6 +287,7 @@ let rec check_instr (c : context) (e : instr) (s : infer_stack_type) : op_type =
[t; t] --> [I32Type]

| Unary unop ->
check_unop unop e.at;
let t = type_unop unop in
[t] --> [t]

Expand Down

0 comments on commit 87f2a5a

Please sign in to comment.