| open Bigarray |
| open Lib.Bigarray |
| open Types |
| open Values |
| |
| type size = int32 (* number of pages *) |
| type address = int64 |
| type offset = int32 |
| type count = int32 |
| |
| type memory' = (int, int8_unsigned_elt, c_layout) Array1.t |
| type memory = {mutable ty : memory_type; mutable content : memory'} |
| type t = memory |
| |
| exception Type |
| exception Bounds |
| exception SizeOverflow |
| exception SizeLimit |
| exception OutOfMemory |
| |
| let page_size = 0x10000L (* 64 KiB *) |
| |
| let valid_limits {min; max} = |
| match max with |
| | None -> true |
| | Some m -> I32.le_u min m |
| |
| let create n = |
| if I32.gt_u n 0x10000l then raise SizeOverflow else |
| try |
| let size = Int64.(mul (of_int32 n) page_size) in |
| let mem = Array1_64.create Int8_unsigned C_layout size in |
| Array1.fill mem 0; |
| mem |
| with Out_of_memory -> raise OutOfMemory |
| |
| let alloc (MemoryType lim as ty) = |
| if not (valid_limits lim) then raise Type; |
| {ty; content = create lim.min} |
| |
| let bound mem = |
| Array1_64.dim mem.content |
| |
| let size mem = |
| Int64.(to_int32 (div (bound mem) page_size)) |
| |
| let type_of mem = |
| mem.ty |
| |
| let grow mem delta = |
| let MemoryType lim = mem.ty in |
| assert (lim.min = size mem); |
| let old_size = lim.min in |
| let new_size = Int32.add old_size delta in |
| if I32.gt_u old_size new_size then raise SizeOverflow else |
| let lim' = {lim with min = new_size} in |
| if not (valid_limits lim') then raise SizeLimit else |
| let after = create new_size in |
| let dim = Array1_64.dim mem.content in |
| Array1.blit (Array1_64.sub mem.content 0L dim) (Array1_64.sub after 0L dim); |
| mem.ty <- MemoryType lim'; |
| mem.content <- after |
| |
| let load_byte mem a = |
| try Array1_64.get mem.content a with Invalid_argument _ -> raise Bounds |
| |
| let store_byte mem a b = |
| try Array1_64.set mem.content a b with Invalid_argument _ -> raise Bounds |
| |
| let load_bytes mem a n = |
| let buf = Buffer.create n in |
| for i = 0 to n - 1 do |
| Buffer.add_char buf (Char.chr (load_byte mem Int64.(add a (of_int i)))) |
| done; |
| Buffer.contents buf |
| |
| let store_bytes mem a bs = |
| for i = String.length bs - 1 downto 0 do |
| store_byte mem Int64.(add a (of_int i)) (Char.code bs.[i]) |
| done |
| |
| let effective_address a o = |
| let ea = Int64.(add a (of_int32 o)) in |
| if I64.lt_u ea a then raise Bounds; |
| ea |
| |
| let loadn mem a o n = |
| assert (n > 0 && n <= 8); |
| let rec loop a n = |
| if n = 0 then 0L else begin |
| let x = Int64.(shift_left (loop (add a 1L) (n - 1)) 8) in |
| Int64.logor (Int64.of_int (load_byte mem a)) x |
| end |
| in loop (effective_address a o) n |
| |
| let storen mem a o n x = |
| assert (n > 0 && n <= 8); |
| let rec loop a n x = |
| if n > 0 then begin |
| Int64.(loop (add a 1L) (n - 1) (shift_right x 8)); |
| store_byte mem a (Int64.to_int x land 0xff) |
| end |
| in loop (effective_address a o) n x |
| |
| let load_num mem a o t = |
| let n = loadn mem a o (Types.num_size t) in |
| match t with |
| | I32Type -> I32 (Int64.to_int32 n) |
| | I64Type -> I64 n |
| | F32Type -> F32 (F32.of_bits (Int64.to_int32 n)) |
| | F64Type -> F64 (F64.of_bits n) |
| |
| let store_num mem a o n = |
| let store = storen mem a o (Types.num_size (Values.type_of_num n)) in |
| match n with |
| | I32 x -> store (Int64.of_int32 x) |
| | I64 x -> store x |
| | F32 x -> store (Int64.of_int32 (F32.to_bits x)) |
| | F64 x -> store (F64.to_bits x) |
| |
| let extend x n = function |
| | ZX -> x |
| | SX -> let sh = 64 - 8 * n in Int64.(shift_right (shift_left x sh) sh) |
| |
| let load_num_packed sz ext mem a o t = |
| assert (packed_size sz <= num_size t); |
| let w = packed_size sz in |
| let x = extend (loadn mem a o w) w ext in |
| match t with |
| | I32Type -> I32 (Int64.to_int32 x) |
| | I64Type -> I64 x |
| | _ -> raise Type |
| |
| let store_num_packed sz mem a o n = |
| assert (packed_size sz <= num_size (Values.type_of_num n)); |
| let w = packed_size sz in |
| let x = |
| match n with |
| | I32 x -> Int64.of_int32 x |
| | I64 x -> x |
| | _ -> raise Type |
| in storen mem a o w x |
| |
| let load_vec mem a o t = |
| match t with |
| | V128Type -> |
| V128 (V128.of_bits (load_bytes mem (effective_address a o) (Types.vec_size t))) |
| |
| let store_vec mem a o n = |
| match n with |
| | V128 x -> store_bytes mem (effective_address a o) (V128.to_bits x) |
| |
| let load_vec_packed sz ext mem a o t = |
| assert (packed_size sz < vec_size t); |
| let x = loadn mem a o (packed_size sz) in |
| let b = Bytes.make 16 '\x00' in |
| Bytes.set_int64_le b 0 x; |
| let v = V128.of_bits (Bytes.to_string b) in |
| let r = |
| match sz, ext with |
| | Pack64, ExtLane (Pack8x8, SX) -> V128.I16x8_convert.extend_low_s v |
| | Pack64, ExtLane (Pack8x8, ZX) -> V128.I16x8_convert.extend_low_u v |
| | Pack64, ExtLane (Pack16x4, SX) -> V128.I32x4_convert.extend_low_s v |
| | Pack64, ExtLane (Pack16x4, ZX) -> V128.I32x4_convert.extend_low_u v |
| | Pack64, ExtLane (Pack32x2, SX) -> V128.I64x2_convert.extend_low_s v |
| | Pack64, ExtLane (Pack32x2, ZX) -> V128.I64x2_convert.extend_low_u v |
| | _, ExtLane _ -> assert false |
| | Pack8, ExtSplat -> V128.I8x16.splat (I8.of_int_s (Int64.to_int x)) |
| | Pack16, ExtSplat -> V128.I16x8.splat (I16.of_int_s (Int64.to_int x)) |
| | Pack32, ExtSplat -> V128.I32x4.splat (I32.of_int_s (Int64.to_int x)) |
| | Pack64, ExtSplat -> V128.I64x2.splat x |
| | Pack32, ExtZero -> v |
| | Pack64, ExtZero -> v |
| | _, ExtZero -> assert false |
| in V128 r |