From e5b5939a3eea8c4623436fb1c7db13f46b19061a Mon Sep 17 00:00:00 2001 From: Ng Zhi An Date: Fri, 7 Aug 2020 18:03:26 -0700 Subject: [PATCH] Implement SIMD load splat and load extend Introduce a new SimdLoad AST node to represent SIMD load splat and load extend. The existing parameters for loadop is not sufficient to represent all the load splats and extends, as it only has 3 pack sizes, and 2 sign-extensions, and 1 none case, giving at total of 7 cases: - sz = (pack_size * extension) option while we have 6 load extends and 4 load splats (10 in total). We can add 1 more case to pack_size, Pack64, which will only be used by SIMD loads, but that still only gives 8 cases. We can also add 1 more data type to extension, that will give us enough, but it will be useless for non-v128 loads. --- interpreter/binary/encode.ml | 2 ++ interpreter/exec/eval.ml | 11 +++++++++++ interpreter/exec/simd.ml | 14 ++++++++++++++ interpreter/runtime/memory.ml | 22 ++++++++++++++++++++++ interpreter/runtime/memory.mli | 3 +++ interpreter/syntax/ast.ml | 2 ++ interpreter/syntax/operators.ml | 24 ++++++++++++++++++++++++ interpreter/syntax/types.ml | 11 +++++++++++ interpreter/text/arrange.ml | 1 + interpreter/text/lexer.mll | 14 ++++++++++++++ interpreter/valid/valid.ml | 15 +++++++++++++++ 11 files changed, 119 insertions(+) diff --git a/interpreter/binary/encode.ml b/interpreter/binary/encode.ml index 72087e0b1..934fd5296 100644 --- a/interpreter/binary/encode.ml +++ b/interpreter/binary/encode.ml @@ -200,6 +200,8 @@ let encode m = | Load ({ty = V128Type; _} as mo) -> simd_op 0x00l; memop mo + | SimdLoad _ -> failwith "TODO v128 SimdLoad" + | 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 diff --git a/interpreter/exec/eval.ml b/interpreter/exec/eval.ml index e7ab76dcb..d9e41d4e5 100644 --- a/interpreter/exec/eval.ml +++ b/interpreter/exec/eval.ml @@ -220,6 +220,17 @@ let rec step (c : config) : config = in v :: vs', [] with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at]) + | SimdLoad {offset; ty; sz; _}, 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 load_kind -> Memory.load_simd load_kind mem addr offset ty + in v :: vs', [] + with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at]) + | Store {offset; sz; _}, v :: I32 i :: vs' -> let mem = memory frame.inst (0l @@ e.at) in let addr = I64_convert.extend_i32_u i in diff --git a/interpreter/exec/simd.ml b/interpreter/exec/simd.ml index b3e24bafc..511f26173 100644 --- a/interpreter/exec/simd.ml +++ b/interpreter/exec/simd.ml @@ -173,6 +173,10 @@ sig val widen_low_u : t -> t val widen_high_u : t -> t end + module I64x2_convert : sig + val widen_low_s : t -> t + val widen_low_u : t -> t + end module F32x4_convert : sig val convert_i32x4_s : t -> t val convert_i32x4_u : t -> t @@ -395,6 +399,16 @@ struct let widen_high_u = widen Lib.List.drop 0xffffl end + module I64x2_convert = struct + let widen mask x = + Rep.of_i64x2 + (List.map + (fun i32 -> Int64.(logand mask (of_int32 i32))) + (Lib.List.take 2 (Rep.to_i32x4 x))) + let widen_low_s = widen 0xffffffffffffffffL + let widen_low_u = widen 0xffffffffL + end + module F32x4_convert = struct let convert f v = Rep.of_f32x4 (List.map f (Rep.to_i32x4 v)) let convert_i32x4_s = convert F32_convert.convert_i32_s diff --git a/interpreter/runtime/memory.ml b/interpreter/runtime/memory.ml index 2c9f198f3..b366f6068 100644 --- a/interpreter/runtime/memory.ml +++ b/interpreter/runtime/memory.ml @@ -131,6 +131,28 @@ let load_packed sz ext mem a o t = | I64Type -> I64 x | _ -> raise Type +let load_simd load_kind mem a o t = + let n = match load_kind with + | LoadSplat sz -> simd_packed_size sz + | LoadExtend (sz, ext) -> 8 + in + assert (n <= Types.size t); + let x = (loadn mem a o n) in + let b = Bytes.create 16 in + Bytes.set_int64_le b 0 x; + let v = V128.of_bits (Bytes.to_string b) in + match load_kind with + | LoadExtend (SimdPack8, SX) -> V128 (V128.I16x8_convert.widen_low_s v) + | LoadExtend (SimdPack8, ZX) -> V128 (V128.I16x8_convert.widen_low_u v) + | LoadExtend (SimdPack16, SX) -> V128 (V128.I32x4_convert.widen_low_s v) + | LoadExtend (SimdPack16, ZX) -> V128 (V128.I32x4_convert.widen_low_u v) + | LoadExtend (SimdPack32, SX) -> V128 (V128.I64x2_convert.widen_low_s v) + | LoadExtend (SimdPack32, ZX) -> V128 (V128.I64x2_convert.widen_low_u v) + | LoadSplat (SimdPack8) -> V128 (V128.I8x16.splat (I8.of_int_s (Int64.to_int x))) + | LoadSplat (SimdPack16) -> V128 (V128.I16x8.splat (I16.of_int_s (Int64.to_int x))) + | LoadSplat (SimdPack32) -> V128 (V128.I32x4.splat (I32.of_int_s (Int64.to_int x))) + | _ -> assert false + let store_packed sz mem a o v = assert (packed_size sz <= Types.size (Values.type_of v)); let n = packed_size sz in diff --git a/interpreter/runtime/memory.mli b/interpreter/runtime/memory.mli index f611e4647..e2524424d 100644 --- a/interpreter/runtime/memory.mli +++ b/interpreter/runtime/memory.mli @@ -35,6 +35,9 @@ val store_value : val load_packed : pack_size -> extension -> memory -> address -> offset -> value_type -> value (* raises Type, Bounds *) +val load_simd : + simd_load_kind -> memory -> address -> offset -> value_type -> value + (* raises Type, Bounds *) val store_packed : pack_size -> memory -> address -> offset -> value -> unit (* raises Type, Bounds *) diff --git a/interpreter/syntax/ast.ml b/interpreter/syntax/ast.ml index a9d0fe1a7..3625f0c20 100644 --- a/interpreter/syntax/ast.ml +++ b/interpreter/syntax/ast.ml @@ -107,6 +107,7 @@ type 'a memop = {ty : value_type; align : int; offset : Memory.offset; sz : 'a option} type loadop = (pack_size * extension) memop type storeop = pack_size memop +type simdloadop = simd_load_kind memop (* Expressions *) @@ -138,6 +139,7 @@ and instr' = | GlobalGet of var (* read global variable *) | GlobalSet of var (* write global variable *) | Load of loadop (* read memory at address *) + | SimdLoad of simdloadop (* read memory at address *) | Store of storeop (* write memory at address *) | MemorySize (* size of linear memory *) | MemoryGrow (* grow linear memory *) diff --git a/interpreter/syntax/operators.ml b/interpreter/syntax/operators.ml index 32327f9fe..9ccd7f50b 100644 --- a/interpreter/syntax/operators.ml +++ b/interpreter/syntax/operators.ml @@ -229,6 +229,30 @@ let v128_bitselect = Ternary (V128Op.Bitselect) let v8x16_swizzle = Binary (V128 V128Op.(I8x16 Swizzle)) let v8x16_shuffle imms = Binary (V128 V128Op.(I8x16 (Shuffle imms))) +let i16x8_load8x8_s align offset = + SimdLoad {ty = V128Type; align; offset; sz = Some (LoadExtend (SimdPack8, SX))} +let i16x8_load8x8_u align offset = + SimdLoad {ty = V128Type; align; offset; sz = Some (LoadExtend (SimdPack8, ZX))} + +let i32x4_load16x4_s align offset = + SimdLoad {ty = V128Type; align; offset; sz = Some (LoadExtend (SimdPack16, SX))} +let i32x4_load16x4_u align offset = + SimdLoad {ty = V128Type; align; offset; sz = Some (LoadExtend (SimdPack16, ZX))} + +let i64x2_load32x2_s align offset = + SimdLoad {ty = V128Type; align; offset; sz = Some (LoadExtend (SimdPack32, SX))} +let i64x2_load32x2_u align offset = + SimdLoad {ty = V128Type; align; offset; sz = Some (LoadExtend (SimdPack32, ZX))} + +let v8x16_load_splat align offset = + SimdLoad {ty= V128Type; align; offset; sz = Some (LoadSplat SimdPack8)} +let v16x8_load_splat align offset = + SimdLoad {ty= V128Type; align; offset; sz = Some (LoadSplat SimdPack16)} +let v32x4_load_splat align offset = + SimdLoad {ty= V128Type; align; offset; sz = Some (LoadSplat SimdPack32)} +let v64x2_load_splat align offset = + SimdLoad {ty= V128Type; align; offset; sz = Some (LoadSplat SimdPack64)} + let i8x16_splat = Convert (V128 (V128Op.I8x16 V128Op.Splat)) let i8x16_extract_lane_s imm = SimdExtract (V128Op.I8x16 (SX, imm)) let i8x16_extract_lane_u imm = SimdExtract (V128Op.I8x16 (ZX, imm)) diff --git a/interpreter/syntax/types.ml b/interpreter/syntax/types.ml index db06fabf8..cf914dd81 100644 --- a/interpreter/syntax/types.ml +++ b/interpreter/syntax/types.ml @@ -19,6 +19,12 @@ type extern_type = type pack_size = Pack8 | Pack16 | Pack32 type extension = SX | ZX +type simd_pack_size = + | SimdPack8 | SimdPack16 | SimdPack32 | SimdPack64 +type simd_load_kind = + LoadSplat of simd_pack_size + | LoadExtend of simd_pack_size * extension + (* Attributes *) @@ -32,6 +38,11 @@ let packed_size = function | Pack16 -> 2 | Pack32 -> 4 +let simd_packed_size = function + | SimdPack8 -> 1 + | SimdPack16 -> 2 + | SimdPack32 -> 4 + | SimdPack64 -> 8 (* Subtyping *) diff --git a/interpreter/text/arrange.ml b/interpreter/text/arrange.ml index b1099f176..080d5e810 100644 --- a/interpreter/text/arrange.ml +++ b/interpreter/text/arrange.ml @@ -321,6 +321,7 @@ let rec instr e = | SimdExtract op -> failwith "TODO v128" | SimdReplace op -> failwith "TODO v128" | SimdShift op -> failwith "TODO v128" + | SimdLoad _ -> failwith "TODO v128 SimdLoad" in Node (head, inner) let const c = diff --git a/interpreter/text/lexer.mll b/interpreter/text/lexer.mll index 27615e60d..95597a770 100644 --- a/interpreter/text/lexer.mll +++ b/interpreter/text/lexer.mll @@ -288,6 +288,20 @@ rule token = parse (ext s i64_load8_s i64_load8_u (opt a 0)) (ext s i64_load16_s i64_load16_u (opt a 1)) (ext s i64_load32_s i64_load32_u (opt a 2)) o)) } + | "i16x8.load8x8_"(sign as s) + { LOAD (fun a o -> (ext s i16x8_load8x8_s i16x8_load8x8_u (opt a 3)) o) } + | "i32x4.load16x4_"(sign as s) + { LOAD (fun a o -> (ext s i32x4_load16x4_s i32x4_load16x4_u (opt a 3)) o) } + | "i64x2.load32x2_"(sign as s) + { LOAD (fun a o -> (ext s i64x2_load32x2_s i64x2_load32x2_u (opt a 3)) o) } + | "v8x16.load_splat" + { LOAD (fun a o -> (v8x16_load_splat (opt a 0)) o) } + | "v16x8.load_splat" + { LOAD (fun a o -> (v16x8_load_splat (opt a 1)) o) } + | "v32x4.load_splat" + { LOAD (fun a o -> (v32x4_load_splat (opt a 2)) o) } + | "v64x2.load_splat" + { LOAD (fun a o -> (v64x2_load_splat (opt a 3)) o) } | (ixx as t)".store"(mem_size as sz) { if t = "i32" && sz = "32" then error lexbuf "unknown operator"; STORE (fun a o -> diff --git a/interpreter/valid/valid.ml b/interpreter/valid/valid.ml index e8273ed17..038449323 100644 --- a/interpreter/valid/valid.ml +++ b/interpreter/valid/valid.ml @@ -201,6 +201,17 @@ let check_simd_extract_lane_index op at = let check_simd_replace_lane_index op at = check_simd_lane_index Fun.id op at +let check_simd_memop (c : context) (memop : 'a memop) at = + ignore (memory c (0l @@ at)); + let size = + match memop.sz with + | Some (LoadExtend _) -> 8 + | Some (LoadSplat sz) -> simd_packed_size sz + | _ -> size memop.ty + in + require (1 lsl memop.align <= size) at + "alignment must not be larger than natural" + (* * Conventions: * c : context @@ -303,6 +314,10 @@ 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] + | SimdLoad memop -> + check_simd_memop c memop e.at; + [I32Type] --> [memop.ty] + | Store memop -> check_memop c memop (fun sz -> sz) e.at; [I32Type; memop.ty] --> []