From 1607c5e671464c0880a9def4fb0a997a5352d7df Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Tue, 19 Jul 2016 19:15:55 +0200 Subject: [PATCH 1/4] Implement basic globals --- ml-proto/host/arrange.ml | 6 ++++++ ml-proto/host/encode.ml | 21 +++++++++++++++------ ml-proto/host/lexer.mll | 3 +++ ml-proto/host/parser.mly | 31 ++++++++++++++++++++++++------- ml-proto/spec/ast.ml | 5 ++++- ml-proto/spec/check.ml | 12 +++++++++++- ml-proto/spec/decode.ml | 24 ++++++++++++++++++++++-- ml-proto/spec/desugar.ml | 9 +++++++-- ml-proto/spec/eval.ml | 13 ++++++++++++- ml-proto/spec/kernel.ml | 3 +++ ml-proto/test/globals.wast | 32 ++++++++++++++++++++++++++++++++ 11 files changed, 139 insertions(+), 20 deletions(-) create mode 100644 ml-proto/test/globals.wast diff --git a/ml-proto/host/arrange.ml b/ml-proto/host/arrange.ml index 80001e421a..9442dfb02f 100644 --- a/ml-proto/host/arrange.ml +++ b/ml-proto/host/arrange.ml @@ -217,6 +217,8 @@ let rec expr e = | GetLocal x -> "get_local " ^ var x, [] | SetLocal (x, e) -> "set_local " ^ var x, [expr e] | TeeLocal (x, e) -> "tee_local " ^ var x, [expr e] + | GetGlobal x -> "get_global " ^ var x, [] + | SetGlobal (x, e) -> "set_global " ^ var x, [expr e] | Load (op, e) -> memop "load" op, [expr e] | Store (op, e1, e2) -> memop "store" op, [expr e1; expr e2] | LoadExtend (op, e) -> extop op, [expr e] @@ -276,6 +278,9 @@ let import i im = [atom string module_name; atom string func_name; ty] ) +let global t = + Node ("global", [atom value_type t]) + let export ex = let {name; kind} = ex.it in let desc = match kind with `Func x -> var x | `Memory -> "memory" in @@ -291,6 +296,7 @@ let module_ m = listi func m.it.funcs @ table m.it.table @ opt memory m.it.memory @ + list global m.it.globals @ list export m.it.exports @ opt start m.it.start ) diff --git a/ml-proto/host/encode.ml b/ml-proto/host/encode.ml index 3eff2b8ea6..42fefe177b 100644 --- a/ml-proto/host/encode.ml +++ b/ml-proto/host/encode.ml @@ -127,6 +127,8 @@ let encode m = | Ast.Get_local x -> op 0x14; var x | Ast.Set_local (x, e) -> unary e 0x15; var x | Ast.Tee_local (x, e) -> unary e 0x19; var x + | Ast.Get_global x -> op 0x1a; var x + | Ast.Set_global (x, e) -> unary e 0x1b; var x | Ast.Call (x, es) -> nary es 0x16; var x | Ast.Call_indirect (x, e, es) -> expr e; nary es 0x17; var x @@ -334,6 +336,18 @@ let encode m = let memory_section memo = section "memory" (opt memory) memo (memo <> None) + (* Global section *) + let compress ts = + let combine t = function + | (t', n) :: ts when t = t' -> (t, n + 1) :: ts + | ts -> (t, 1) :: ts + in List.fold_right combine ts [] + + let global (t, n) = vu n; value_type t + + let global_section ts = + section "code" (vec global) (compress ts) (ts <> []) + (* Export section *) let export exp = let {Kernel.name; kind} = exp.it in @@ -352,12 +366,6 @@ let encode m = section "start" (opt var) xo (xo <> None) (* Code section *) - let compress locals = - let combine t = function - | (t', n) :: ts when t = t' -> (t, n + 1) :: ts - | ts -> (t, 1) :: ts - in List.fold_right combine locals [] - let local (t, n) = vu n; value_type t let code f = @@ -390,6 +398,7 @@ let encode m = func_section m.it.funcs; table_section m.it.table; memory_section m.it.memory; + global_section m.it.globals; export_section m.it.exports; start_section m.it.start; code_section m.it.funcs; diff --git a/ml-proto/host/lexer.mll b/ml-proto/host/lexer.mll index 9824be3673..b10cc54d53 100644 --- a/ml-proto/host/lexer.mll +++ b/ml-proto/host/lexer.mll @@ -160,6 +160,8 @@ rule token = parse | "get_local" { GET_LOCAL } | "set_local" { SET_LOCAL } | "tee_local" { TEE_LOCAL } + | "get_global" { GET_GLOBAL } + | "set_global" { SET_GLOBAL } | (nxx as t)".load" { LOAD (fun (o, a, e) -> @@ -360,6 +362,7 @@ rule token = parse | "param" { PARAM } | "result" { RESULT } | "local" { LOCAL } + | "global" { GLOBAL } | "module" { MODULE } | "memory" { MEMORY } | "segment" { SEGMENT } diff --git a/ml-proto/host/parser.mly b/ml-proto/host/parser.mly index c503474bc6..8832250174 100644 --- a/ml-proto/host/parser.mly +++ b/ml-proto/host/parser.mly @@ -52,11 +52,11 @@ let empty_types () = {tmap = VarMap.empty; tlist = []} type context = {types : types; funcs : space; imports : space; - locals : space; labels : int VarMap.t} + locals : space; globals : space; labels : int VarMap.t} let empty_context () = {types = empty_types (); funcs = empty (); imports = empty (); - locals = empty (); labels = VarMap.empty} + locals = empty (); globals = empty (); labels = VarMap.empty} let enter_func c = assert (VarMap.is_empty c.labels); @@ -73,6 +73,7 @@ let lookup category space x = let func c x = lookup "function" c.funcs x let import c x = lookup "import" c.imports x let local c x = lookup "local" c.locals x +let global c x = lookup "global" c.globals x let label c x = try VarMap.find x.it c.labels with Not_found -> error x.at ("unknown label " ^ x.it) @@ -92,6 +93,7 @@ let bind category space x = let bind_func c x = bind "function" c.funcs x let bind_import c x = bind "import" c.imports x let bind_local c x = bind "local" c.locals x +let bind_global c x = bind "global" c.globals x let bind_label c x = {c with labels = VarMap.add x.it 0 (VarMap.map ((+) 1) c.labels)} @@ -103,6 +105,7 @@ let anon space n = space.count <- space.count + n let anon_func c = anon c.funcs 1 let anon_import c = anon c.imports 1 let anon_locals c ts = anon c.locals (List.length ts) +let anon_globals c ts = anon c.globals (List.length ts) let anon_label c = {c with labels = VarMap.map ((+) 1) c.labels} let empty_type = {ins = []; out = None} @@ -127,10 +130,11 @@ let implicit_decl c t at = %token NAT INT FLOAT TEXT VAR VALUE_TYPE LPAR RPAR %token NOP DROP BLOCK IF THEN ELSE SELECT LOOP BR BR_IF BR_TABLE %token CALL CALL_IMPORT CALL_INDIRECT RETURN -%token GET_LOCAL SET_LOCAL TEE_LOCAL LOAD STORE OFFSET ALIGN +%token GET_LOCAL SET_LOCAL TEE_LOCAL GET_GLOBAL SET_GLOBAL +%token LOAD STORE OFFSET ALIGN %token CONST UNARY BINARY COMPARE CONVERT %token UNREACHABLE CURRENT_MEMORY GROW_MEMORY -%token FUNC START TYPE PARAM RESULT LOCAL +%token FUNC START TYPE PARAM RESULT LOCAL GLOBAL %token MODULE MEMORY SEGMENT IMPORT EXPORT TABLE %token ASSERT_INVALID ASSERT_RETURN ASSERT_RETURN_NAN ASSERT_TRAP INVOKE %token INPUT OUTPUT @@ -262,6 +266,8 @@ expr1 : | GET_LOCAL var { fun c -> Get_local ($2 c local) } | SET_LOCAL var expr { fun c -> Set_local ($2 c local, $3 c) } | TEE_LOCAL var expr { fun c -> Tee_local ($2 c local, $3 c) } + | GET_GLOBAL var { fun c -> Get_global ($2 c global) } + | SET_GLOBAL var expr { fun c -> Set_global ($2 c global, $3 c) } | LOAD offset align expr { fun c -> $1 ($2, $3, $4 c) } | STORE offset align expr expr { fun c -> $1 ($2, $3, $4 c, $5 c) } | CONST literal { fun c -> fst (literal $1 $2) } @@ -350,6 +356,14 @@ export_opt : start : | LPAR START var RPAR { fun c -> $3 c func } +; + +global : + | LPAR GLOBAL value_type_list RPAR + { fun c -> anon_globals c $3; $3 } + | LPAR GLOBAL bind_var VALUE_TYPE RPAR /* Sugar */ + { fun c -> bind_global c $3; [$4] } +; segment : | LPAR SEGMENT NAT text_list RPAR @@ -410,11 +424,14 @@ export : module_fields : | /* empty */ { fun c -> - {memory = None; types = c.types.tlist; funcs = []; start = None; imports = []; - exports = []; table = []} } + {memory = None; types = c.types.tlist; globals = []; funcs = []; + start = None; imports = []; exports = []; table = []} } | func module_fields { fun c -> let f = $1 c in let m = $2 c in let func, exs = f () in {m with funcs = func :: m.funcs; exports = exs @ m.exports} } + | global module_fields + { fun c -> let gs = $1 c in let m = $2 c in + {m with globals = gs @ m.globals} } | import module_fields { fun c -> let i = $1 c in let m = $2 c in {m with imports = i :: m.imports} } @@ -423,7 +440,7 @@ module_fields : {m with exports = $1 c :: m.exports} } | table module_fields { fun c -> let m = $2 c in - {m with table = ($1 c) @ m.table} } + {m with table = $1 c @ m.table} } | type_def module_fields { fun c -> $1 c; $2 c } | memory module_fields diff --git a/ml-proto/spec/ast.ml b/ml-proto/spec/ast.ml index 6e9fddb957..ec67c67716 100644 --- a/ml-proto/spec/ast.ml +++ b/ml-proto/spec/ast.ml @@ -26,10 +26,12 @@ and expr' = | Call_import of var * expr list | Call_indirect of var * expr * expr list - (* Locals *) + (* Variables *) | Get_local of var | Set_local of var * expr | Tee_local of var * expr + | Get_global of var + | Set_global of var * expr (* Memory access *) | I32_load of Memory.offset * int * expr @@ -212,6 +214,7 @@ and module' = { memory : Kernel.memory option; types : Types.func_type list; + globals : Types.value_type list; funcs : func list; start : var option; imports : Kernel.import list; diff --git a/ml-proto/spec/check.ml b/ml-proto/spec/check.ml index 508317ea42..f557c1b4d3 100644 --- a/ml-proto/spec/check.ml +++ b/ml-proto/spec/check.ml @@ -22,6 +22,7 @@ type context = funcs : func_type list; imports : func_type list; locals : value_type list; + globals : value_type list; return : expr_type; labels : expr_type_future list; has_memory : bool @@ -35,6 +36,7 @@ let type_ types x = lookup "function type" types x let func c x = lookup "function" c.funcs x let import c x = lookup "import" c.imports x let local c x = lookup "local" c.locals x +let global c x = lookup "global" c.globals x let label c x = lookup "label" c.labels x @@ -195,6 +197,13 @@ let rec check_expr c et e = check_expr c (some (local c x)) e1; check_type (Some (local c x)) et e.at + | GetGlobal x -> + check_type (Some (global c x)) et e.at + + | SetGlobal (x, e1) -> + check_expr c (some (global c x)) e1; + check_type None et e.at + | Load (memop, e1) -> check_load c et memop e1 e.at @@ -345,12 +354,13 @@ let check_memory memory = ignore (List.fold_left (check_segment mem.min) 0L mem.segments) let check_module m = - let {memory; types; funcs; start; imports; exports; table} = m.it in + let {memory; types; globals; funcs; start; imports; exports; table} = m.it in Lib.Option.app check_memory memory; let c = {types; funcs = List.map (fun f -> type_ types f.it.ftype) funcs; imports = List.map (fun i -> type_ types i.it.itype) imports; locals = []; + globals; return = None; labels = []; has_memory = memory <> None} in diff --git a/ml-proto/spec/decode.ml b/ml-proto/spec/decode.ml index b72ee694a4..72963b70f0 100644 --- a/ml-proto/spec/decode.ml +++ b/ml-proto/spec/decode.ml @@ -249,8 +249,14 @@ let rec expr stack s = | 0x19, e :: es -> let x = at var s in Tee_local (x, e), es + | 0x1a, es -> + let x = at var s in + Get_global x, es + | 0x1b, e :: es -> + let x = at var s in + Set_global (x, e), es - | 0x1a | 0x1b | 0x1c | 0x1d | 0x1e | 0x1f as b, _ -> + | 0x1c | 0x1d | 0x1e | 0x1f as b, _ -> illegal s pos b | 0x20, e :: es -> let o, a = memop s in I32_load8_s (o, a, e), es @@ -443,6 +449,7 @@ let id s = | "function" -> `FuncSection | "table" -> `TableSection | "memory" -> `MemorySection + | "global" -> `GlobalSection | "export" -> `ExportSection | "start" -> `StartSection | "code" -> `CodeSection @@ -503,6 +510,17 @@ let memory_section s = section `MemorySection (opt (at memory) true) None s +(* Global section *) + +let global s = + let n = vu s in + let t = value_type s in + Lib.List.make n t + +let global_section s = + section `GlobalSection (fun s -> List.flatten (vec global s)) [] s + + (* Export section *) let export s = @@ -574,6 +592,8 @@ let module_ s = iterate unknown_section s; let memory_limits = memory_section s in iterate unknown_section s; + let globals = global_section s in + iterate unknown_section s; let exports = export_section s in iterate unknown_section s; let start = start_section s in @@ -596,7 +616,7 @@ let module_ s = match memory_limits with | None -> None | Some memory -> Some Source.({memory.it with segments} @@ memory.at) - in {memory; types; funcs; imports; exports; table; start} + in {memory; types; globals; funcs; imports; exports; table; start} let decode name bs = at module_ (stream name bs) diff --git a/ml-proto/spec/desugar.ml b/ml-proto/spec/desugar.ml index b48b09a24c..5790359f1b 100644 --- a/ml-proto/spec/desugar.ml +++ b/ml-proto/spec/desugar.ml @@ -33,6 +33,8 @@ and relabel' f n = function | GetLocal x -> GetLocal x | SetLocal (x, e) -> SetLocal (x, relabel f n e) | TeeLocal (x, e) -> TeeLocal (x, relabel f n e) + | GetGlobal x -> GetGlobal x + | SetGlobal (x, e) -> SetGlobal (x, relabel f n e) | Load (memop, e) -> Load (memop, relabel f n e) | Store (memop, e1, e2) -> Store (memop, relabel f n e1, relabel f n e2) | LoadExtend (extop, e) -> LoadExtend (extop, relabel f n e) @@ -83,6 +85,8 @@ and expr' at = function | Ast.Get_local x -> GetLocal x | Ast.Set_local (x, e) -> SetLocal (x, expr e) | Ast.Tee_local (x, e) -> TeeLocal (x, expr e) + | Ast.Get_global x -> GetGlobal x + | Ast.Set_global (x, e) -> SetGlobal (x, expr e) | Ast.I32_load (offset, align, e) -> Load ({ty = Int32Type; offset; align}, expr e) @@ -301,7 +305,8 @@ and func' = function let rec module_ m = module' m.it @@ m.at and module' = function - | {Ast.funcs = fs; start; memory; types; imports; exports; table} -> - {funcs = List.map func fs; start; memory; types; imports; exports; table} + | {Ast.funcs = fs; start; globals; memory; types; imports; exports; table} -> + let funcs = List.map func fs in + {funcs; start; globals; memory; types; imports; exports; table} let desugar = module_ diff --git a/ml-proto/spec/eval.ml b/ml-proto/spec/eval.ml index a7260a01f5..54ae964d66 100644 --- a/ml-proto/spec/eval.ml +++ b/ml-proto/spec/eval.ml @@ -17,6 +17,7 @@ type instance = module_ : module_; imports : import list; exports : export_map; + globals : value ref list; memory : Memory.t option } @@ -71,6 +72,7 @@ let lookup category list x = let type_ c x = lookup "type" c.instance.module_.it.types x let func c x = lookup "function" c.instance.module_.it.funcs x let import c x = lookup "import" c.instance.imports x +let global c x = lookup "global" c.instance.globals x let local c x = lookup "local" c.locals x let label c x = lookup "label" c.labels x @@ -204,6 +206,14 @@ let rec eval_expr (c : config) (e : expr) = local c x := v1; Some v1 + | GetGlobal x -> + Some !(global c x) + + | SetGlobal (x, e1) -> + let v1 = some (eval_expr c e1) e1.at in + global c x := v1; + None + | Load ({ty; offset; align = _}, e1) -> let mem = memory c e.at in let v1 = address32 (eval_expr c e1) e1.at in @@ -324,11 +334,12 @@ let add_export funcs ex = let init m imports = assert (List.length imports = List.length m.it.Kernel.imports); - let {memory; funcs; exports; start; _} = m.it in + let {memory; funcs; globals; exports; start; _} = m.it in let instance = {module_ = m; imports; exports = List.fold_right (add_export funcs) exports ExportMap.empty; + globals = List.map (fun t -> ref (default_value t)) globals; memory = Lib.Option.map init_memory memory} in Lib.Option.app diff --git a/ml-proto/spec/kernel.ml b/ml-proto/spec/kernel.ml index 50137a498b..79538057a2 100644 --- a/ml-proto/spec/kernel.ml +++ b/ml-proto/spec/kernel.ml @@ -92,6 +92,8 @@ and expr' = | GetLocal of var (* read local variable *) | SetLocal of var * expr (* write local variable *) | TeeLocal of var * expr (* write local variable and keep value *) + | GetGlobal of var (* read global variable *) + | SetGlobal of var * expr (* write global variable *) | Load of memop * expr (* read memory at address *) | Store of memop * expr * expr (* write memory at address *) | LoadExtend of extop * expr (* read memory at address and extend *) @@ -147,6 +149,7 @@ and module_' = { memory : memory option; types : Types.func_type list; + globals : Types.value_type list; funcs : func list; start : var option; imports : import list; diff --git a/ml-proto/test/globals.wast b/ml-proto/test/globals.wast new file mode 100644 index 0000000000..4f67d261be --- /dev/null +++ b/ml-proto/test/globals.wast @@ -0,0 +1,32 @@ +;; TODO: more tests + +(module + (global $x i32) + (global f32 f64) + (global $y i64) + + (func "get-x" (result i32) (get_global $x)) + (func "get-y" (result i64) (get_global $y)) + (func "set-x" (param i32) (set_global $x (get_local 0))) + (func "set-y" (param i64) (set_global $y (get_local 0))) + + (func "get-1" (result f32) (get_global 1)) + (func "get-2" (result f64) (get_global 2)) + (func "set-1" (param f32) (set_global 1 (get_local 0))) + (func "set-2" (param f64) (set_global 2 (get_local 0))) +) + +(assert_return (invoke "get-x") (i32.const 0)) +(assert_return (invoke "get-y") (i64.const 0)) +(assert_return (invoke "get-1") (f32.const 0)) +(assert_return (invoke "get-2") (f64.const 0)) + +(assert_return (invoke "set-x" (i32.const 6))) +(assert_return (invoke "set-y" (i64.const 7))) +(assert_return (invoke "set-1" (f32.const 8))) +(assert_return (invoke "set-2" (f64.const 9))) + +(assert_return (invoke "get-x") (i32.const 6)) +(assert_return (invoke "get-y") (i64.const 7)) +(assert_return (invoke "get-1") (f32.const 8)) +(assert_return (invoke "get-2") (f64.const 9)) From 7e62e5e4a81fcdae47b5d59cc1d3fcd12b174705 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Thu, 21 Jul 2016 17:37:28 +0200 Subject: [PATCH 2/4] Initilizers for globals --- ml-proto/host/arrange.ml | 5 +++-- ml-proto/host/encode.ml | 20 +++++++++++--------- ml-proto/host/parser.mly | 16 +++++++++------- ml-proto/spec/ast.ml | 11 +++++++++-- ml-proto/spec/check.ml | 22 +++++++++++++++++----- ml-proto/spec/decode.ml | 9 ++++++--- ml-proto/spec/desugar.ml | 7 ++++++- ml-proto/spec/eval.ml | 21 +++++++++++++-------- ml-proto/spec/kernel.ml | 9 ++++++++- ml-proto/test/globals.wast | 35 ++++++++++++++++++++++++++++------- 10 files changed, 110 insertions(+), 45 deletions(-) diff --git a/ml-proto/host/arrange.ml b/ml-proto/host/arrange.ml index 9442dfb02f..3201614025 100644 --- a/ml-proto/host/arrange.ml +++ b/ml-proto/host/arrange.ml @@ -278,8 +278,9 @@ let import i im = [atom string module_name; atom string func_name; ty] ) -let global t = - Node ("global", [atom value_type t]) +let global g = + let {gtype; init} = g.it in + Node ("global", [atom value_type gtype; expr init]) let export ex = let {name; kind} = ex.it in diff --git a/ml-proto/host/encode.ml b/ml-proto/host/encode.ml index 42fefe177b..9186f74f83 100644 --- a/ml-proto/host/encode.ml +++ b/ml-proto/host/encode.ml @@ -337,16 +337,12 @@ let encode m = section "memory" (opt memory) memo (memo <> None) (* Global section *) - let compress ts = - let combine t = function - | (t', n) :: ts when t = t' -> (t, n + 1) :: ts - | ts -> (t, 1) :: ts - in List.fold_right combine ts [] - - let global (t, n) = vu n; value_type t + let global g = + let {gtype = t; init = e} = g.it in + value_type t; expr e; op 0x0f - let global_section ts = - section "code" (vec global) (compress ts) (ts <> []) + let global_section gs = + section "code" (vec global) gs (gs <> []) (* Export section *) let export exp = @@ -366,6 +362,12 @@ let encode m = section "start" (opt var) xo (xo <> None) (* Code section *) + let compress ts = + let combine t = function + | (t', n) :: ts when t = t' -> (t, n + 1) :: ts + | ts -> (t, 1) :: ts + in List.fold_right combine ts [] + let local (t, n) = vu n; value_type t let code f = diff --git a/ml-proto/host/parser.mly b/ml-proto/host/parser.mly index 8832250174..f423c59cc0 100644 --- a/ml-proto/host/parser.mly +++ b/ml-proto/host/parser.mly @@ -105,7 +105,7 @@ let anon space n = space.count <- space.count + n let anon_func c = anon c.funcs 1 let anon_import c = anon c.imports 1 let anon_locals c ts = anon c.locals (List.length ts) -let anon_globals c ts = anon c.globals (List.length ts) +let anon_global c = anon c.globals 1 let anon_label c = {c with labels = VarMap.map ((+) 1) c.labels} let empty_type = {ins = []; out = None} @@ -359,10 +359,12 @@ start : ; global : - | LPAR GLOBAL value_type_list RPAR - { fun c -> anon_globals c $3; $3 } - | LPAR GLOBAL bind_var VALUE_TYPE RPAR /* Sugar */ - { fun c -> bind_global c $3; [$4] } + | LPAR GLOBAL VALUE_TYPE expr RPAR + { let at = at () in + fun c -> anon_global c; fun () -> {gtype = $3; init = $4 c} @@ at } + | LPAR GLOBAL bind_var VALUE_TYPE expr RPAR /* Sugar */ + { let at = at () in + fun c -> bind_global c $3; fun () -> {gtype = $4; init = $5 c} @@ at } ; segment : @@ -430,8 +432,8 @@ module_fields : { fun c -> let f = $1 c in let m = $2 c in let func, exs = f () in {m with funcs = func :: m.funcs; exports = exs @ m.exports} } | global module_fields - { fun c -> let gs = $1 c in let m = $2 c in - {m with globals = gs @ m.globals} } + { fun c -> let g = $1 c in let m = $2 c in + {m with globals = g () :: m.globals} } | import module_fields { fun c -> let i = $1 c in let m = $2 c in {m with imports = i :: m.imports} } diff --git a/ml-proto/spec/ast.ml b/ml-proto/spec/ast.ml index ec67c67716..8207c9d3b2 100644 --- a/ml-proto/spec/ast.ml +++ b/ml-proto/spec/ast.ml @@ -196,7 +196,14 @@ and expr' = | Grow_memory of expr -(* Functions *) +(* Globals and Functions *) + +type global = global' Source.phrase +and global' = +{ + gtype : Types.value_type; + init : expr; +} type func = func' Source.phrase and func' = @@ -214,7 +221,7 @@ and module' = { memory : Kernel.memory option; types : Types.func_type list; - globals : Types.value_type list; + globals : global list; funcs : func list; start : var option; imports : Kernel.import list; diff --git a/ml-proto/spec/check.ml b/ml-proto/spec/check.ml index f557c1b4d3..855a9bc52d 100644 --- a/ml-proto/spec/check.ml +++ b/ml-proto/spec/check.ml @@ -292,6 +292,11 @@ and check_memop memop at = and check_mem_type ty sz at = require (ty = Int64Type || sz <> Memory.Mem32) at "memory size too big" +let check_init_expr e = + match e.it with + | Const _ | GetGlobal _ -> () + | _ -> error e.at "not an initialization expression" + (* * check_func : context -> func -> unit @@ -316,6 +321,11 @@ let check_func c f = let check_elem c x = ignore (func c x) +let check_global c g = + let {gtype; init} = g.it in + check_init_expr init; + check_expr c (some gtype) init + module NameSet = Set.Make(String) let check_export c set ex = @@ -359,12 +369,14 @@ let check_module m = let c = {types; funcs = List.map (fun f -> type_ types f.it.ftype) funcs; imports = List.map (fun i -> type_ types i.it.itype) imports; + globals = []; locals = []; - globals; return = None; labels = []; has_memory = memory <> None} in - List.iter (check_func c) funcs; - List.iter (check_elem c) table; - ignore (List.fold_left (check_export c) NameSet.empty exports); - check_start c start + List.iter (check_global c) globals; + let c' = {c with globals = List.map (fun g -> g.it.gtype) globals} in + List.iter (check_func c') funcs; + List.iter (check_elem c') table; + ignore (List.fold_left (check_export c') NameSet.empty exports); + check_start c' start diff --git a/ml-proto/spec/decode.ml b/ml-proto/spec/decode.ml index 72963b70f0..dcb724ac7a 100644 --- a/ml-proto/spec/decode.ml +++ b/ml-proto/spec/decode.ml @@ -513,12 +513,15 @@ let memory_section s = (* Global section *) let global s = - let n = vu s in let t = value_type s in - Lib.List.make n t + let pos = pos s in + let es = expr_block s in + require (List.length es = 1) s pos "single expression expected"; + expect 0x0f s "`end` opcode expected"; + {gtype = t; init = List.hd es} let global_section s = - section `GlobalSection (fun s -> List.flatten (vec global s)) [] s + section `GlobalSection (vec (at global)) [] s (* Export section *) diff --git a/ml-proto/spec/desugar.ml b/ml-proto/spec/desugar.ml index 5790359f1b..46df8ee65a 100644 --- a/ml-proto/spec/desugar.ml +++ b/ml-proto/spec/desugar.ml @@ -299,13 +299,18 @@ and block = function (* Functions and Modules *) +let rec global g = global' g.it @@ g.at +and global' = function + | {Ast.gtype = t; init = e} -> {gtype = t; init = expr e} + let rec func f = func' f.it @@ f.at and func' = function | {Ast.body = es; ftype; locals} -> {body = return (seq es); ftype; locals} let rec module_ m = module' m.it @@ m.at and module' = function - | {Ast.funcs = fs; start; globals; memory; types; imports; exports; table} -> + | {Ast.funcs = fs; start; globals = gs; memory; types; imports; exports; table} -> + let globals = List.map global gs in let funcs = List.map func fs in {funcs; start; globals; memory; types; imports; exports; table} diff --git a/ml-proto/spec/eval.ml b/ml-proto/spec/eval.ml index 54ae964d66..dcc59e303e 100644 --- a/ml-proto/spec/eval.ml +++ b/ml-proto/spec/eval.ml @@ -127,7 +127,7 @@ let memory c at = * vo : value option *) -let rec eval_expr (c : config) (e : expr) = +let rec eval_expr (c : config) (e : expr) : value option = match e.it with | Nop -> None @@ -321,9 +321,13 @@ and eval_hostop c hostop vs at = (* Modules *) -let init_memory {it = {min; segments; _}} = - let mem = Memory.create min in - Memory.init mem (List.map it segments); +let init_global inst r g = + let c = {instance = inst; locals = []; labels = []} in + r := some (eval_expr c g.it.init) g.it.init.at + +let init_memory m = + let mem = Memory.create m.it.min in + Memory.init mem (List.map it m.it.segments); mem let add_export funcs ex = @@ -335,16 +339,17 @@ let add_export funcs ex = let init m imports = assert (List.length imports = List.length m.it.Kernel.imports); let {memory; funcs; globals; exports; start; _} = m.it in - let instance = + let inst = {module_ = m; imports; exports = List.fold_right (add_export funcs) exports ExportMap.empty; - globals = List.map (fun t -> ref (default_value t)) globals; + globals = List.map (fun g -> ref (default_value g.it.gtype)) globals; memory = Lib.Option.map init_memory memory} in + List.iter2 (init_global inst) inst.globals globals; Lib.Option.app - (fun x -> ignore (eval_func instance (lookup "function" funcs x) [])) start; - instance + (fun x -> ignore (eval_func inst (lookup "function" funcs x) [])) start; + inst let invoke instance name vs = try diff --git a/ml-proto/spec/kernel.ml b/ml-proto/spec/kernel.ml index 79538057a2..76a3fa6aa9 100644 --- a/ml-proto/spec/kernel.ml +++ b/ml-proto/spec/kernel.ml @@ -120,6 +120,13 @@ and func' = (* Modules *) +type global = global' Source.phrase +and global' = +{ + gtype : Types.value_type; + init : expr; +} + type memory = memory' Source.phrase and memory' = { @@ -149,7 +156,7 @@ and module_' = { memory : memory option; types : Types.func_type list; - globals : Types.value_type list; + globals : global list; funcs : func list; start : var option; imports : import list; diff --git a/ml-proto/test/globals.wast b/ml-proto/test/globals.wast index 4f67d261be..4fc36a647e 100644 --- a/ml-proto/test/globals.wast +++ b/ml-proto/test/globals.wast @@ -1,9 +1,10 @@ ;; TODO: more tests (module - (global $x i32) - (global f32 f64) - (global $y i64) + (global $x i32 (i32.const -2)) + (global f32 (f32.const -3)) + (global f64 (f64.const -4)) + (global $y i64 (i64.const -5)) (func "get-x" (result i32) (get_global $x)) (func "get-y" (result i64) (get_global $y)) @@ -16,10 +17,10 @@ (func "set-2" (param f64) (set_global 2 (get_local 0))) ) -(assert_return (invoke "get-x") (i32.const 0)) -(assert_return (invoke "get-y") (i64.const 0)) -(assert_return (invoke "get-1") (f32.const 0)) -(assert_return (invoke "get-2") (f64.const 0)) +(assert_return (invoke "get-x") (i32.const -2)) +(assert_return (invoke "get-y") (i64.const -5)) +(assert_return (invoke "get-1") (f32.const -3)) +(assert_return (invoke "get-2") (f64.const -4)) (assert_return (invoke "set-x" (i32.const 6))) (assert_return (invoke "set-y" (i64.const 7))) @@ -30,3 +31,23 @@ (assert_return (invoke "get-y") (i64.const 7)) (assert_return (invoke "get-1") (f32.const 8)) (assert_return (invoke "get-2") (f64.const 9)) + +(assert_invalid + (module (global f32 (f32.neg (f32.const 0)))) + "not an initialization expression" +) + +(assert_invalid + (module (global f32 (get_local 0))) + "not an initialization expression" +) + +(assert_invalid + (module (global i32 (f32.const 0))) + "type mismatch" +) + +(assert_invalid + (module (global i32 (get_global 0))) + "unknown global" +) \ No newline at end of file From aff35625592b3c8480af65ad09418882f5577e61 Mon Sep 17 00:00:00 2001 From: rossberg-chromium Date: Fri, 22 Jul 2016 13:51:23 +0200 Subject: [PATCH 3/4] Fix c&p error --- ml-proto/host/encode.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ml-proto/host/encode.ml b/ml-proto/host/encode.ml index 9186f74f83..ef6a03c7c7 100644 --- a/ml-proto/host/encode.ml +++ b/ml-proto/host/encode.ml @@ -342,7 +342,7 @@ let encode m = value_type t; expr e; op 0x0f let global_section gs = - section "code" (vec global) gs (gs <> []) + section "global" (vec global) gs (gs <> []) (* Export section *) let export exp = From b1feaaa3f2b9a320f339744aa15e7106601d98d6 Mon Sep 17 00:00:00 2001 From: rossberg-chromium Date: Fri, 12 Aug 2016 13:00:54 +0200 Subject: [PATCH 4/4] Update opcodes --- ml-proto/host/encode.ml | 4 ++-- ml-proto/spec/decode.ml | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ml-proto/host/encode.ml b/ml-proto/host/encode.ml index ef6a03c7c7..754243d9e2 100644 --- a/ml-proto/host/encode.ml +++ b/ml-proto/host/encode.ml @@ -127,8 +127,8 @@ let encode m = | Ast.Get_local x -> op 0x14; var x | Ast.Set_local (x, e) -> unary e 0x15; var x | Ast.Tee_local (x, e) -> unary e 0x19; var x - | Ast.Get_global x -> op 0x1a; var x - | Ast.Set_global (x, e) -> unary e 0x1b; var x + | Ast.Get_global x -> op 0xbb; var x + | Ast.Set_global (x, e) -> unary e 0xbc; var x | Ast.Call (x, es) -> nary es 0x16; var x | Ast.Call_indirect (x, e, es) -> expr e; nary es 0x17; var x diff --git a/ml-proto/spec/decode.ml b/ml-proto/spec/decode.ml index dcb724ac7a..8a9c408090 100644 --- a/ml-proto/spec/decode.ml +++ b/ml-proto/spec/decode.ml @@ -249,12 +249,6 @@ let rec expr stack s = | 0x19, e :: es -> let x = at var s in Tee_local (x, e), es - | 0x1a, es -> - let x = at var s in - Get_global x, es - | 0x1b, e :: es -> - let x = at var s in - Set_global (x, e), es | 0x1c | 0x1d | 0x1e | 0x1f as b, _ -> illegal s pos b @@ -421,7 +415,14 @@ let rec expr stack s = | 0xb9, e2 :: e1 :: es -> I64_rotr (e1, e2), es | 0xba, e :: es -> I64_eqz e, es - | b, _ when b > 0xba -> illegal s pos b + | 0xbb, es -> + let x = at var s in + Get_global x, es + | 0xbc, e :: es -> + let x = at var s in + Set_global (x, e), es + + | b, _ when b > 0xbc -> illegal s pos b | b, _ -> error s pos "too few operands for operator"