From 6df9516cfe0bdb64676fe34d90a497918d9432bf Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 9 Oct 2015 11:26:34 -0700 Subject: [PATCH 1/4] Add stdio.write import for writing a sequence of bytes to stdout --- ml-proto/host/builtins.ml | 41 ++++++++++++++++++- ml-proto/spec/eval.ml | 13 ++++-- ml-proto/spec/eval.mli | 3 +- .../test/expected-output/stdio_write.wast.log | 2 + ml-proto/test/stdio_write.wast | 14 +++++++ 5 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 ml-proto/test/expected-output/stdio_write.wast.log create mode 100644 ml-proto/test/stdio_write.wast diff --git a/ml-proto/host/builtins.ml b/ml-proto/host/builtins.ml index fa599e7987..84d0ae38ca 100644 --- a/ml-proto/host/builtins.ml +++ b/ml-proto/host/builtins.ml @@ -1,9 +1,44 @@ open Source +open Eval +open Memory +open Types -let print vs = +let print m vs = List.iter Print.print_value (List.map (fun v -> Some v) vs); None +let stdout_write at m vs = + if List.length vs != 2 then + Error.error at "stdio.write expects 2 arguments (offset, count)"; + + let mem = Eval.get_module_memory at m in + let offset_arg = List.nth vs 0 in + let offset = match offset_arg with + | Values.Int32 i -> + Int32.to_int i + | _ -> + Error.error at "stdio.write offset was wrong type"; + in + let count_arg = List.nth vs 1 in + let count = match count_arg with + | Values.Int32 i -> + Int32.to_int i + | _ -> + Error.error at "stdio.write count was wrong type"; + in + + for i=0 to (count-1) do + let load_result = Memory.load_extend mem (Int64.of_int (offset + i)) Mem8 ZX Int32Type in + match load_result with + | Values.Int32 i -> + print_char (Char.chr (Int32.to_int i)); + | _ -> + Error.error at "load_extend returned wrong type"; + + done; + + None + let match_import i = let {Ast.module_name; func_name; func_params; func_result} = i.it in if module_name <> "stdio" then @@ -13,6 +48,10 @@ let match_import i = if func_result <> None then Error.error i.at "stdio.print has no result"; print + | "write" -> + if func_result <> None then + Error.error i.at "stdio.write has no result"; + stdout_write i.at | _ -> Error.error i.at ("no stdio." ^ func_name ^ "\"") diff --git a/ml-proto/spec/eval.ml b/ml-proto/spec/eval.ml index 121d5fa51a..87030c32cb 100644 --- a/ml-proto/spec/eval.ml +++ b/ml-proto/spec/eval.ml @@ -13,13 +13,13 @@ let error = Error.error type value = Values.value type func = Ast.func -type import = value list -> value option type host_params = {page_size : Memory.size} module ExportMap = Map.Make(String) type export_map = func ExportMap.t -type instance = +type import = instance -> value list -> value option +and instance = { funcs : func list; imports : import list; @@ -159,7 +159,7 @@ let rec eval_expr (c : config) (e : expr) = | CallImport (x, es) -> let vs = List.map (fun ev -> some (eval_expr c ev) ev.at) es in - (import c x) vs + (import c x) c.module_ vs | CallIndirect (x, e1, es) -> let i = int32 (eval_expr c e1) e1.at in @@ -312,3 +312,10 @@ let host_eval e = let host = {page_size = 1L} in let m = {imports = []; exports; tables = []; funcs = [f]; memory = None; host} in eval_func m f [] + +let get_module_memory at m = + match m.memory with + | Some mem -> + mem + | _ -> + error at "Module has no memory"; diff --git a/ml-proto/spec/eval.mli b/ml-proto/spec/eval.mli index 2442bfe13f..ce0d903a98 100644 --- a/ml-proto/spec/eval.mli +++ b/ml-proto/spec/eval.mli @@ -4,7 +4,7 @@ type instance type value = Values.value -type import = value list -> value option +type import = instance -> value list -> value option type host_params = {page_size : Memory.size} val init : Ast.module_ -> import list -> host_params -> instance @@ -13,3 +13,4 @@ val invoke : instance -> string -> value list -> value option (* This function is not part of the spec. *) val host_eval : Ast.expr -> value option (* raise Error.Error *) +val get_module_memory : Source.region -> instance -> Memory.t \ No newline at end of file diff --git a/ml-proto/test/expected-output/stdio_write.wast.log b/ml-proto/test/expected-output/stdio_write.wast.log new file mode 100644 index 0000000000..b25171d5e0 --- /dev/null +++ b/ml-proto/test/expected-output/stdio_write.wast.log @@ -0,0 +1,2 @@ +hello, world! +o, \ No newline at end of file diff --git a/ml-proto/test/stdio_write.wast b/ml-proto/test/stdio_write.wast new file mode 100644 index 0000000000..86e7b1a4c1 --- /dev/null +++ b/ml-proto/test/stdio_write.wast @@ -0,0 +1,14 @@ +(module + (import $write "stdio" "write" (param i32 i32)) + + (memory 4096 4096 (segment 0 "hello, world!\0D\0A\00")) + + (func $print_hello + (call_import $write (i32.const 0) (i32.const 15)) + (call_import $write (i32.const 4) (i32.const 2)) + ) + + (export "print_hello" $print_hello) +) + +(invoke "print_hello") \ No newline at end of file From f41e4b63024515d37b9898dd3aee5ec3b6a82ee4 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 9 Oct 2015 13:23:26 -0700 Subject: [PATCH 2/4] Change stdio_write --- ml-proto/test/stdio_write.wast | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ml-proto/test/stdio_write.wast b/ml-proto/test/stdio_write.wast index 86e7b1a4c1..80eae6fbd2 100644 --- a/ml-proto/test/stdio_write.wast +++ b/ml-proto/test/stdio_write.wast @@ -1,14 +1,13 @@ (module (import $write "stdio" "write" (param i32 i32)) - (memory 4096 4096 (segment 0 "hello, world!\0D\0A\00")) + (memory 4096 4096 (segment 0 "\89\50\4e\47\0d\0a\1a\0a\00")) - (func $print_hello - (call_import $write (i32.const 0) (i32.const 15)) - (call_import $write (i32.const 4) (i32.const 2)) + (func $write_png_header + (call_import $write (i32.const 0) (i32.const 9)) ) - (export "print_hello" $print_hello) + (export "write_png_header" $write_png_header) ) -(invoke "print_hello") \ No newline at end of file +(invoke "write_png_header") \ No newline at end of file From f59782a6a38307fb615a3318102f4d36a3d26906 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 9 Oct 2015 13:24:26 -0700 Subject: [PATCH 3/4] Update expected output --- ml-proto/test/expected-output/stdio_write.wast.log | Bin 17 -> 9 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/ml-proto/test/expected-output/stdio_write.wast.log b/ml-proto/test/expected-output/stdio_write.wast.log index b25171d5e052ad1a77e5525ffeb8981cceae64d4..aa860abb474c16b64e6a58a44d70206090fc2c56 100644 GIT binary patch literal 9 QcmeAS@N?(olHy_j01NQ}ssI20 literal 17 Ycmc~u&B@8vQ7F$Z%1Ke=<;vFq05$>zTmS$7 From 6ff3c7e85686b67cdb5f81060e189bef732f5175 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 12 Oct 2015 11:26:30 -0700 Subject: [PATCH 4/4] Address style nits. Write output in binary mode so newlines aren't normalized. Iterate using Int64 (since the offset and count are Int64) --- ml-proto/host/builtins.ml | 58 +++++++++++++++++++++------------------ ml-proto/spec/eval.ml | 2 +- ml-proto/spec/eval.mli | 4 +-- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/ml-proto/host/builtins.ml b/ml-proto/host/builtins.ml index 84d0ae38ca..dfa6eaa39a 100644 --- a/ml-proto/host/builtins.ml +++ b/ml-proto/host/builtins.ml @@ -7,37 +7,43 @@ let print m vs = List.iter Print.print_value (List.map (fun v -> Some v) vs); None -let stdout_write at m vs = - if List.length vs != 2 then - Error.error at "stdio.write expects 2 arguments (offset, count)"; - - let mem = Eval.get_module_memory at m in - let offset_arg = List.nth vs 0 in - let offset = match offset_arg with - | Values.Int32 i -> - Int32.to_int i - | _ -> - Error.error at "stdio.write offset was wrong type"; - in - let count_arg = List.nth vs 1 in - let count = match count_arg with - | Values.Int32 i -> - Int32.to_int i - | _ -> - Error.error at "stdio.write count was wrong type"; - in +let rec stdout_write_inner at mem offset count i = + let load_result = Memory.load_extend mem (Int64.add offset i) Mem8 ZX Int32Type in - for i=0 to (count-1) do - let load_result = Memory.load_extend mem (Int64.of_int (offset + i)) Mem8 ZX Int32Type in + begin match load_result with - | Values.Int32 i -> - print_char (Char.chr (Int32.to_int i)); + | Values.Int32 byte -> + print_char (Char.chr (Int32.to_int byte)) | _ -> - Error.error at "load_extend returned wrong type"; + ignore (Error.error at "load_extend returned wrong type") + end; - done; + let next = Int64.succ i in + let should_iterate = (Int64.compare next count) < 0 in - None + if should_iterate then + ignore (stdout_write_inner at mem offset count next) + else + (); + +and stdout_write at m vs = + if List.length vs != 2 then + Error.error at "stdio.write expects 2 arguments (offset, count)"; + + let mem = Eval.memory_for_module at m in + match vs with + | [Values.Int32 _offset; Values.Int32 _count] -> + let offset = (Int64.of_int32 _offset) in + let count = (Int64.of_int32 _count) in + + set_binary_mode_out stdout false; + ignore (stdout_write_inner at mem offset count Int64.zero); + set_binary_mode_out stdout true; + None + + | _ -> + ignore (Error.error at "stdio.write expected i32 offset, i32 count"); + None let match_import i = let {Ast.module_name; func_name; func_params; func_result} = i.it in diff --git a/ml-proto/spec/eval.ml b/ml-proto/spec/eval.ml index 87030c32cb..e5fef35362 100644 --- a/ml-proto/spec/eval.ml +++ b/ml-proto/spec/eval.ml @@ -313,7 +313,7 @@ let host_eval e = let m = {imports = []; exports; tables = []; funcs = [f]; memory = None; host} in eval_func m f [] -let get_module_memory at m = +let memory_for_module at m = match m.memory with | Some mem -> mem diff --git a/ml-proto/spec/eval.mli b/ml-proto/spec/eval.mli index ce0d903a98..91b207dec7 100644 --- a/ml-proto/spec/eval.mli +++ b/ml-proto/spec/eval.mli @@ -11,6 +11,6 @@ val init : Ast.module_ -> import list -> host_params -> instance val invoke : instance -> string -> value list -> value option (* raise Error.Error *) -(* This function is not part of the spec. *) +(* These functions are not part of the spec. *) val host_eval : Ast.expr -> value option (* raise Error.Error *) -val get_module_memory : Source.region -> instance -> Memory.t \ No newline at end of file +val memory_for_module : Source.region -> instance -> Memory.t \ No newline at end of file