Skip to content

Commit

Permalink
[interpreter] Implement load lane instructions (WebAssembly#428)
Browse files Browse the repository at this point in the history
v128.load8_lane
v128.load16_lane
v128.load32_lane
v128.load64_lane

Introduce a new ast type, SimdLoadLane, since it takes a lane index
immediate, on top of the usual memarg, and also pops a v128 off, in
addition to the index.

The exact binary opcodes for these instructions are not yet fixed, I've
used the ones currently implement in V8 and LLVM/Binaryen, we can change
those later.

Also added a new test generation script, and the generated test files.
  • Loading branch information
ngzhian authored Jan 29, 2021
1 parent ffe1db3 commit 0cd0a20
Show file tree
Hide file tree
Showing 18 changed files with 1,087 additions and 17 deletions.
16 changes: 16 additions & 0 deletions interpreter/binary/decode.ml
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,22 @@ let simd_prefix s =
| 0x50l -> v128_or
| 0x51l -> v128_xor
| 0x52l -> v128_bitselect
| 0x58l ->
let a, o = memop s in
let lane = u8 s in
v128_load8_lane a o lane
| 0x59l ->
let a, o = memop s in
let lane = u8 s in
v128_load16_lane a o lane
| 0x5al ->
let a, o = memop s in
let lane = u8 s in
v128_load32_lane a o lane
| 0x5bl ->
let a, o = memop s in
let lane = u8 s in
v128_load64_lane a o lane
| 0x60l -> i8x16_abs
| 0x61l -> i8x16_neg
| 0x62l -> v128_any_true
Expand Down
9 changes: 9 additions & 0 deletions interpreter/binary/encode.ml
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,15 @@ let encode m =
| SimdLoad ({ty= V128Type; sz = Some (Pack64, PackZero); _} as mo) ->
simd_op 0xfdl; memop mo

| SimdLoadLane ({ty = V128Type; sz = Some Pack8; _} as mo, i) ->
simd_op 0x58l; memop mo; u8 i;
| SimdLoadLane ({ty = V128Type; sz = Some Pack16; _} as mo, i) ->
simd_op 0x59l; memop mo; u8 i;
| SimdLoadLane ({ty = V128Type; sz = Some Pack32; _} as mo, i) ->
simd_op 0x5al; memop mo; u8 i;
| SimdLoadLane ({ty = V128Type; sz = Some Pack64; _} as mo, i) ->
simd_op 0x5bl; memop mo; u8 i;

| Store ({ty = I32Type; sz = None; _} as mo) -> op 0x36; memop mo
| Store ({ty = I64Type; sz = None; _} as mo) -> op 0x37; memop mo
| Store ({ty = F32Type; sz = None; _} as mo) -> op 0x38; memop mo
Expand Down
15 changes: 14 additions & 1 deletion interpreter/exec/eval.ml
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,20 @@ let rec step (c : config) : config =
let v =
match sz with
| None -> Memory.load_value mem addr offset ty
| Some (pack_size, simd_load) -> Memory.load_simd_packed pack_size simd_load mem addr offset ty
| Some (pack_size, simd_load) ->
V128 (Memory.load_simd_packed pack_size simd_load mem addr offset ty)
in v :: vs', []
with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at])

| SimdLoadLane ({offset; ty; sz; _}, j), V128 v128 :: I32 i :: vs' ->
let mem = memory frame.inst (0l @@ e.at) in
let addr = I64_convert.extend_i32_u i in
(try
let v =
match sz with
| None -> assert false
| Some pack_size ->
V128 (Memory.load_simd_lane v128 pack_size mem addr offset ty j)
in v :: vs', []
with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at])

Expand Down
34 changes: 22 additions & 12 deletions interpreter/runtime/memory.ml
Original file line number Diff line number Diff line change
Expand Up @@ -139,20 +139,30 @@ let load_simd_packed pack_size simd_load mem a o t =
Bytes.set_int64_le b 0 x;
let v = V128.of_bits (Bytes.to_string b) in
match pack_size, simd_load with
| Pack64, Pack8x8 SX -> V128 (V128.I16x8_convert.widen_low_s v)
| Pack64, Pack8x8 ZX -> V128 (V128.I16x8_convert.widen_low_u v)
| Pack64, Pack16x4 SX -> V128 (V128.I32x4_convert.widen_low_s v)
| Pack64, Pack16x4 ZX -> V128 (V128.I32x4_convert.widen_low_u v)
| Pack64, Pack32x2 SX -> V128 (V128.I64x2_convert.widen_low_s v)
| Pack64, Pack32x2 ZX -> V128 (V128.I64x2_convert.widen_low_u v)
| Pack8, PackSplat -> V128 (V128.I8x16.splat (I8.of_int_s (Int64.to_int x)))
| Pack16, PackSplat -> V128 (V128.I16x8.splat (I16.of_int_s (Int64.to_int x)))
| Pack32, PackSplat -> V128 (V128.I32x4.splat (I32.of_int_s (Int64.to_int x)))
| Pack64, PackSplat -> V128 (V128.I64x2.splat x)
| Pack32, PackZero -> V128 v
| Pack64, PackZero -> V128 v
| Pack64, Pack8x8 SX -> V128.I16x8_convert.widen_low_s v
| Pack64, Pack8x8 ZX -> V128.I16x8_convert.widen_low_u v
| Pack64, Pack16x4 SX -> V128.I32x4_convert.widen_low_s v
| Pack64, Pack16x4 ZX -> V128.I32x4_convert.widen_low_u v
| Pack64, Pack32x2 SX -> V128.I64x2_convert.widen_low_s v
| Pack64, Pack32x2 ZX -> V128.I64x2_convert.widen_low_u v
| Pack8, PackSplat -> V128.I8x16.splat (I8.of_int_s (Int64.to_int x))
| Pack16, PackSplat -> V128.I16x8.splat (I16.of_int_s (Int64.to_int x))
| Pack32, PackSplat -> V128.I32x4.splat (I32.of_int_s (Int64.to_int x))
| Pack64, PackSplat -> V128.I64x2.splat x
| Pack32, PackZero -> v
| Pack64, PackZero -> v
| _ -> assert false

let load_simd_lane v pack_size mem a o t laneidx =
let n = packed_size pack_size in
assert (n < Types.size t);
let x = loadn mem a o n in
match pack_size with
| Pack8 -> V128.I8x16.replace_lane laneidx v (Int64.to_int32 x)
| Pack16 -> V128.I16x8.replace_lane laneidx v (Int64.to_int32 x)
| Pack32 -> V128.I32x4.replace_lane laneidx v (Int64.to_int32 x)
| Pack64 -> V128.I64x2.replace_lane laneidx v x

let store_packed sz mem a o v =
assert (packed_size sz <= Types.size (Values.type_of v));
let n = packed_size sz in
Expand Down
4 changes: 3 additions & 1 deletion interpreter/runtime/memory.mli
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ val load_packed :
pack_size -> extension -> memory -> address -> offset -> value_type -> value
(* raises Type, Bounds *)
val load_simd_packed :
pack_size -> pack_simd -> memory -> address -> offset -> value_type -> value
pack_size -> pack_simd -> memory -> address -> offset -> value_type -> V128.t
(* raises Type, Bounds *)
val load_simd_lane :
V128.t -> pack_size -> memory -> address -> offset -> value_type -> int (* lane index *) -> V128.t
val store_packed :
pack_size -> memory -> address -> offset -> value -> unit
(* raises Type, Bounds *)
2 changes: 2 additions & 0 deletions interpreter/syntax/ast.ml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ type storeop = pack_size memop
type simd_loadop = (pack_size * pack_simd) memop
type empty
type simd_storeop = empty memop
type simd_laneop = pack_size memop * int

(* Expressions *)

Expand Down Expand Up @@ -146,6 +147,7 @@ and instr' =
| Load of loadop (* read memory at address *)
| Store of storeop (* write memory at address *)
| SimdLoad of simd_loadop (* read memory at address *)
| SimdLoadLane of simd_laneop (* read single lane at address *)
| SimdStore of simd_storeop (* write memory at address *)
| MemorySize (* size of linear memory *)
| MemoryGrow (* grow linear memory *)
Expand Down
9 changes: 9 additions & 0 deletions interpreter/syntax/operators.ml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,15 @@ let v128_load32_splat align offset =
let v128_load64_splat align offset =
SimdLoad {ty= V128Type; align; offset; sz = Some (Pack64, PackSplat)}

let v128_load8_lane align offset imm =
SimdLoadLane ({ty = V128Type; align; offset; sz = Some Pack8}, imm)
let v128_load16_lane align offset imm =
SimdLoadLane ({ty = V128Type; align; offset; sz = Some Pack16}, imm)
let v128_load32_lane align offset imm =
SimdLoadLane ({ty = V128Type; align; offset; sz = Some Pack32}, imm)
let v128_load64_lane align offset imm =
SimdLoadLane ({ty = V128Type; align; offset; sz = Some Pack64}, imm)

let v128_load32_zero align offset =
SimdLoad {ty= V128Type; align; offset; sz = Some (Pack32, PackZero)}
let v128_load64_zero align offset =
Expand Down
13 changes: 13 additions & 0 deletions interpreter/text/arrange.ml
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,18 @@ let simd_loadop (op : simd_loadop) =
) in
memop ("load" ^ suffix) op (packed_size sz)

let simd_laneop (op, i) =
match op.sz with
| None -> assert false
| Some sz ->
let suffix =
match sz with
| Pack8 -> "8_lane"
| Pack16 -> "16_lane"
| Pack32 -> "32_lane"
| Pack64 -> "64_lane"
in memop ("load" ^ suffix) op (packed_size sz) ^ " " ^ (nat i)

let storeop op =
match op.sz with
| None -> memop "store" op (size op.ty)
Expand Down Expand Up @@ -501,6 +513,7 @@ let rec instr e =
| GlobalSet x -> "global.set " ^ var x, []
| Load op -> loadop op, []
| SimdLoad op -> simd_loadop op, []
| SimdLoadLane op -> simd_laneop op, []
| SimdStore op -> simd_storeop op, []
| Store op -> storeop op, []
| MemorySize -> "memory.size", []
Expand Down
8 changes: 8 additions & 0 deletions interpreter/text/lexer.mll
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,14 @@ rule token = parse
{ LOAD (fun a o -> (v128_load32_zero (opt a 2)) o) }
| "v128.load64_zero"
{ LOAD (fun a o -> (v128_load64_zero (opt a 3)) o) }
| "v128.load8_lane"
{ SIMD_LOAD_LANE (fun a o i -> (v128_load8_lane (opt a 0)) o i) }
| "v128.load16_lane"
{ SIMD_LOAD_LANE (fun a o i -> (v128_load16_lane (opt a 1)) o i) }
| "v128.load32_lane"
{ SIMD_LOAD_LANE (fun a o i -> (v128_load32_lane (opt a 2)) o i) }
| "v128.load64_lane"
{ SIMD_LOAD_LANE (fun a o i -> (v128_load64_lane (opt a 3)) o i) }
| (ixx as t)".store"(mem_size as sz)
{ if t = "i32" && sz = "32" then error lexbuf "unknown operator";
STORE (fun a o ->
Expand Down
4 changes: 3 additions & 1 deletion interpreter/text/parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ let inline_type_explicit (c : context) x ft at =
%token NOP DROP BLOCK END IF THEN ELSE SELECT LOOP BR BR_IF BR_TABLE
%token CALL CALL_INDIRECT RETURN
%token LOCAL_GET LOCAL_SET LOCAL_TEE GLOBAL_GET GLOBAL_SET
%token LOAD STORE OFFSET_EQ_NAT ALIGN_EQ_NAT
%token LOAD STORE OFFSET_EQ_NAT ALIGN_EQ_NAT SIMD_LOAD_LANE
%token SPLAT EXTRACT_LANE REPLACE_LANE SHIFT SHUFFLE
%token CONST V128_CONST UNARY BINARY TERNARY TEST COMPARE CONVERT
%token UNREACHABLE MEMORY_SIZE MEMORY_GROW
Expand Down Expand Up @@ -232,6 +232,7 @@ let inline_type_explicit (c : context) x ft at =
%token<Ast.instr'> COMPARE
%token<Ast.instr'> CONVERT
%token<int option -> Memory.offset -> Ast.instr'> LOAD
%token<int option -> Memory.offset -> int -> Ast.instr'> SIMD_LOAD_LANE
%token<Ast.instr'> SPLAT
%token<int -> Ast.instr'> EXTRACT_LANE
%token<int -> Ast.instr'> REPLACE_LANE
Expand Down Expand Up @@ -385,6 +386,7 @@ plain_instr :
| GLOBAL_GET var { fun c -> global_get ($2 c global) }
| GLOBAL_SET var { fun c -> global_set ($2 c global) }
| LOAD offset_opt align_opt { fun c -> $1 $3 $2 }
| SIMD_LOAD_LANE offset_opt align_opt NAT { let at = at () in fun c -> $1 $3 $2 (simd_lane_index $4 at) }
| STORE offset_opt align_opt { fun c -> $1 $3 $2 }
| MEMORY_SIZE { fun c -> memory_size }
| MEMORY_GROW { fun c -> memory_grow }
Expand Down
8 changes: 8 additions & 0 deletions interpreter/valid/valid.ml
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,14 @@ let rec check_instr (c : context) (e : instr) (s : infer_stack_type) : op_type =
check_memop c memop (Lib.Option.map fst) e.at;
[I32Type] --> [memop.ty]

| SimdLoadLane (memop, i) ->
check_memop c memop (fun o -> o) e.at;
(match memop.sz with
| Some pack_size ->
require (i < 16 / packed_size pack_size) e.at "invalid lane index";
[I32Type; V128Type] --> [memop.ty]
| _ -> assert false)

| Store memop ->
check_memop c memop (fun sz -> sz) e.at;
[I32Type; memop.ty] --> []
Expand Down
1 change: 1 addition & 0 deletions test/core/simd/meta/gen_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
'simd_f32x4_pmin_pmax',
'simd_f64x2_pmin_pmax',
'simd_i32x4_dot_i16x8',
'simd_load_lane',
)


Expand Down
4 changes: 2 additions & 2 deletions test/core/simd/meta/simd.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def const(value, value_type):
value: constant data, string or list,
lane_type: lane type, [i32, i64, f32, f64]
"""
return SIMD.CONST.format(value_type=value_type, value=''.join(value))
return SIMD.CONST.format(value_type=value_type, value=''.join(str(value)))

@staticmethod
def v128_const(value, lane_type):
Expand Down Expand Up @@ -81,4 +81,4 @@ def v128_const(value, lane_type):
data_elem = ' '.join(data_elem)

# Returns v128 constant text
return SIMD.V128_CONST.format(lane_type=lane_type, value=data_elem)
return SIMD.V128_CONST.format(lane_type=lane_type, value=data_elem)
Loading

0 comments on commit 0cd0a20

Please sign in to comment.