Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
[Interpreter] Implement memory.copy, memory.fill (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
binji committed Feb 21, 2019
1 parent 5e3df28 commit d91b74e
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 0 deletions.
13 changes: 13 additions & 0 deletions interpreter/exec/eval.ml
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,19 @@ let rec step (c : config) : config =
with Memory.SizeOverflow | Memory.SizeLimit | Memory.OutOfMemory -> -1l
in I32 result :: vs', []

| MemoryFill, I32 n :: I32 b :: I32 i :: vs' ->
let mem = memory frame.inst (0l @@ e.at) in
let addr = I64_convert.extend_u_i32 i in
(try Memory.fill mem addr (Int32.to_int b) n; vs', []
with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at])

| MemoryCopy, I32 n :: I32 s :: I32 d :: vs' ->
let mem = memory frame.inst (0l @@ e.at) in
let dst = I64_convert.extend_u_i32 d in
let src = I64_convert.extend_u_i32 s in
(try Memory.copy mem dst src n; vs', []
with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at])

| Const v, vs ->
v.it :: vs, []

Expand Down
27 changes: 27 additions & 0 deletions interpreter/runtime/memory.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ open Values
type size = int32 (* number of pages *)
type address = int64
type offset = int32
type count = int32

type pack_size = Pack8 | Pack16 | Pack32
type extension = SX | ZX
Expand Down Expand Up @@ -143,3 +144,29 @@ let store_packed sz mem a o v =
| I64 x -> x
| _ -> raise Type
in storen mem a o n x

let check_bounds mem a = if I64.ge_u a (bound mem) then raise Bounds

let fill mem a v n =
let rec loop a n =
if n > 0l then begin
store_byte mem a v;
loop (Int64.add a 1L) (Int32.sub n 1l)
end
in check_bounds mem a; loop a n

let copy mem d s n =
let n' = Int64.of_int32 n in
let overlap = I64.lt_s Int64.(abs (sub d s)) n' in
let rec loop d s n dx =
if n > 0l then begin
store_byte mem d (load_byte mem s);
loop (Int64.add d dx) (Int64.add s dx) (Int32.sub n 1l) dx
end
in
check_bounds mem d;
check_bounds mem s;
if overlap && s < d then
loop Int64.(add d (sub n' 1L)) Int64.(add s (sub n' 1L)) n (-1L)
else
loop d s n 1L
4 changes: 4 additions & 0 deletions interpreter/runtime/memory.mli
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type t = memory
type size = int32 (* number of pages *)
type address = int64
type offset = int32
type count = int32

type pack_size = Pack8 | Pack16 | Pack32
type extension = SX | ZX
Expand Down Expand Up @@ -42,3 +43,6 @@ val load_packed :
val store_packed :
pack_size -> memory -> address -> offset -> value -> unit
(* raises Type, Bounds *)

val fill : memory -> address -> int -> count -> unit (* raises Bounds *)
val copy : memory -> address -> address -> count -> unit (* raises Bounds *)
97 changes: 97 additions & 0 deletions test/core/bulk.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
(module
(memory 1)

(func (export "fill") (param i32 i32 i32)
(memory.fill
(get_local 0)
(get_local 1)
(get_local 2)))

(func (export "load8_u") (param i32) (result i32)
(i32.load8_u (get_local 0)))
)

;; Basic fill test.
(invoke "fill" (i32.const 1) (i32.const 0xff) (i32.const 3))
(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0))
(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0xff))
(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 0xff))
(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 0xff))
(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 0))

;; Fill value is stored as a byte.
(invoke "fill" (i32.const 0) (i32.const 0xbbaa) (i32.const 2))
(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0xaa))
(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0xaa))

;; Fill all of memory
(invoke "fill" (i32.const 0) (i32.const 0) (i32.const 0x10000))

;; Out-of-bounds writes trap, but all previous writes succeed.
(assert_trap (invoke "fill" (i32.const 0xff00) (i32.const 1) (i32.const 0x101))
"out of bounds memory access")
(assert_return (invoke "load8_u" (i32.const 0xff00)) (i32.const 1))
(assert_return (invoke "load8_u" (i32.const 0xffff)) (i32.const 1))

;; Fail on out-of-bounds even if filling 0 bytes.
(assert_trap (invoke "fill" (i32.const 0x10000) (i32.const 0) (i32.const 0))
"out of bounds memory access")


(module
(memory (data "\aa\bb\cc\dd"))

(func (export "copy") (param i32 i32 i32)
(memory.copy
(get_local 0)
(get_local 1)
(get_local 2)))

(func (export "load8_u") (param i32) (result i32)
(i32.load8_u (get_local 0)))
)

;; Non-overlapping copy.
(invoke "copy" (i32.const 10) (i32.const 0) (i32.const 4))

(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0))
(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0xaa))
(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0xbb))
(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0xcc))
(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0xdd))
(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 0))

;; Overlap, source > dest
(invoke "copy" (i32.const 8) (i32.const 10) (i32.const 4))
(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0xaa))
(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0xbb))
(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0xcc))
(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0xdd))
(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0xcc))
(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0xdd))

;; Overlap, source < dest
(invoke "copy" (i32.const 10) (i32.const 7) (i32.const 6))
(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0))
(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0xaa))
(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0xbb))
(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0xcc))
(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 0xdd))
(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 0xcc))
(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 0))

;; Copy ending at memory limit is ok.
(invoke "copy" (i32.const 0xff00) (i32.const 0) (i32.const 0x100))
(invoke "copy" (i32.const 0xfe00) (i32.const 0xff00) (i32.const 0x100))

;; Out-of-bounds writes trap, but all previous writes succeed.
(assert_trap (invoke "copy" (i32.const 0xfffe) (i32.const 0) (i32.const 3))
"out of bounds memory access")
(assert_return (invoke "load8_u" (i32.const 0xfffe)) (i32.const 0xaa))
(assert_return (invoke "load8_u" (i32.const 0xffff)) (i32.const 0xbb))

;; Fail on out-of-bounds even if copying 0 bytes.
(assert_trap (invoke "copy" (i32.const 0x10000) (i32.const 0) (i32.const 0))
"out of bounds memory access")
(assert_trap (invoke "copy" (i32.const 0) (i32.const 0x10000) (i32.const 0))
"out of bounds memory access")

0 comments on commit d91b74e

Please sign in to comment.