Skip to content

Commit f317601

Browse files
authored
Implement globals (#313)
Implements global declarations as of WebAssembly/design#682. Still missing: mutability, import/export.
1 parent 9f945ea commit f317601

File tree

11 files changed

+214
-29
lines changed

11 files changed

+214
-29
lines changed

ml-proto/host/arrange.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@ let rec expr e =
217217
| GetLocal x -> "get_local " ^ var x, []
218218
| SetLocal (x, e) -> "set_local " ^ var x, [expr e]
219219
| TeeLocal (x, e) -> "tee_local " ^ var x, [expr e]
220+
| GetGlobal x -> "get_global " ^ var x, []
221+
| SetGlobal (x, e) -> "set_global " ^ var x, [expr e]
220222
| Load (op, e) -> memop "load" op, [expr e]
221223
| Store (op, e1, e2) -> memop "store" op, [expr e1; expr e2]
222224
| LoadExtend (op, e) -> extop op, [expr e]
@@ -276,6 +278,10 @@ let import i im =
276278
[atom string module_name; atom string func_name; ty]
277279
)
278280

281+
let global g =
282+
let {gtype; init} = g.it in
283+
Node ("global", [atom value_type gtype; expr init])
284+
279285
let export ex =
280286
let {name; kind} = ex.it in
281287
let desc = match kind with `Func x -> var x | `Memory -> "memory" in
@@ -291,6 +297,7 @@ let module_ m =
291297
listi func m.it.funcs @
292298
table m.it.table @
293299
opt memory m.it.memory @
300+
list global m.it.globals @
294301
list export m.it.exports @
295302
opt start m.it.start
296303
)

ml-proto/host/encode.ml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ let encode m =
127127
| Ast.Get_local x -> op 0x14; var x
128128
| Ast.Set_local (x, e) -> unary e 0x15; var x
129129
| Ast.Tee_local (x, e) -> unary e 0x19; var x
130+
| Ast.Get_global x -> op 0xbb; var x
131+
| Ast.Set_global (x, e) -> unary e 0xbc; var x
130132

131133
| Ast.Call (x, es) -> nary es 0x16; var x
132134
| Ast.Call_indirect (x, e, es) -> expr e; nary es 0x17; var x
@@ -334,6 +336,14 @@ let encode m =
334336
let memory_section memo =
335337
section "memory" (opt memory) memo (memo <> None)
336338

339+
(* Global section *)
340+
let global g =
341+
let {gtype = t; init = e} = g.it in
342+
value_type t; expr e; op 0x0f
343+
344+
let global_section gs =
345+
section "global" (vec global) gs (gs <> [])
346+
337347
(* Export section *)
338348
let export exp =
339349
let {Kernel.name; kind} = exp.it in
@@ -352,11 +362,11 @@ let encode m =
352362
section "start" (opt var) xo (xo <> None)
353363

354364
(* Code section *)
355-
let compress locals =
365+
let compress ts =
356366
let combine t = function
357367
| (t', n) :: ts when t = t' -> (t, n + 1) :: ts
358368
| ts -> (t, 1) :: ts
359-
in List.fold_right combine locals []
369+
in List.fold_right combine ts []
360370

361371
let local (t, n) = vu n; value_type t
362372

@@ -390,6 +400,7 @@ let encode m =
390400
func_section m.it.funcs;
391401
table_section m.it.table;
392402
memory_section m.it.memory;
403+
global_section m.it.globals;
393404
export_section m.it.exports;
394405
start_section m.it.start;
395406
code_section m.it.funcs;

ml-proto/host/lexer.mll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ rule token = parse
160160
| "get_local" { GET_LOCAL }
161161
| "set_local" { SET_LOCAL }
162162
| "tee_local" { TEE_LOCAL }
163+
| "get_global" { GET_GLOBAL }
164+
| "set_global" { SET_GLOBAL }
163165

164166
| (nxx as t)".load"
165167
{ LOAD (fun (o, a, e) ->
@@ -360,6 +362,7 @@ rule token = parse
360362
| "param" { PARAM }
361363
| "result" { RESULT }
362364
| "local" { LOCAL }
365+
| "global" { GLOBAL }
363366
| "module" { MODULE }
364367
| "memory" { MEMORY }
365368
| "segment" { SEGMENT }

ml-proto/host/parser.mly

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ let empty_types () = {tmap = VarMap.empty; tlist = []}
5252

5353
type context =
5454
{types : types; funcs : space; imports : space;
55-
locals : space; labels : int VarMap.t}
55+
locals : space; globals : space; labels : int VarMap.t}
5656

5757
let empty_context () =
5858
{types = empty_types (); funcs = empty (); imports = empty ();
59-
locals = empty (); labels = VarMap.empty}
59+
locals = empty (); globals = empty (); labels = VarMap.empty}
6060

6161
let enter_func c =
6262
assert (VarMap.is_empty c.labels);
@@ -73,6 +73,7 @@ let lookup category space x =
7373
let func c x = lookup "function" c.funcs x
7474
let import c x = lookup "import" c.imports x
7575
let local c x = lookup "local" c.locals x
76+
let global c x = lookup "global" c.globals x
7677
let label c x =
7778
try VarMap.find x.it c.labels
7879
with Not_found -> error x.at ("unknown label " ^ x.it)
@@ -92,6 +93,7 @@ let bind category space x =
9293
let bind_func c x = bind "function" c.funcs x
9394
let bind_import c x = bind "import" c.imports x
9495
let bind_local c x = bind "local" c.locals x
96+
let bind_global c x = bind "global" c.globals x
9597
let bind_label c x =
9698
{c with labels = VarMap.add x.it 0 (VarMap.map ((+) 1) c.labels)}
9799

@@ -103,6 +105,7 @@ let anon space n = space.count <- space.count + n
103105
let anon_func c = anon c.funcs 1
104106
let anon_import c = anon c.imports 1
105107
let anon_locals c ts = anon c.locals (List.length ts)
108+
let anon_global c = anon c.globals 1
106109
let anon_label c = {c with labels = VarMap.map ((+) 1) c.labels}
107110

108111
let empty_type = {ins = []; out = None}
@@ -127,10 +130,11 @@ let implicit_decl c t at =
127130
%token NAT INT FLOAT TEXT VAR VALUE_TYPE LPAR RPAR
128131
%token NOP DROP BLOCK IF THEN ELSE SELECT LOOP BR BR_IF BR_TABLE
129132
%token CALL CALL_IMPORT CALL_INDIRECT RETURN
130-
%token GET_LOCAL SET_LOCAL TEE_LOCAL LOAD STORE OFFSET ALIGN
133+
%token GET_LOCAL SET_LOCAL TEE_LOCAL GET_GLOBAL SET_GLOBAL
134+
%token LOAD STORE OFFSET ALIGN
131135
%token CONST UNARY BINARY COMPARE CONVERT
132136
%token UNREACHABLE CURRENT_MEMORY GROW_MEMORY
133-
%token FUNC START TYPE PARAM RESULT LOCAL
137+
%token FUNC START TYPE PARAM RESULT LOCAL GLOBAL
134138
%token MODULE MEMORY SEGMENT IMPORT EXPORT TABLE
135139
%token ASSERT_INVALID ASSERT_RETURN ASSERT_RETURN_NAN ASSERT_TRAP INVOKE
136140
%token INPUT OUTPUT
@@ -262,6 +266,8 @@ expr1 :
262266
| GET_LOCAL var { fun c -> Get_local ($2 c local) }
263267
| SET_LOCAL var expr { fun c -> Set_local ($2 c local, $3 c) }
264268
| TEE_LOCAL var expr { fun c -> Tee_local ($2 c local, $3 c) }
269+
| GET_GLOBAL var { fun c -> Get_global ($2 c global) }
270+
| SET_GLOBAL var expr { fun c -> Set_global ($2 c global, $3 c) }
265271
| LOAD offset align expr { fun c -> $1 ($2, $3, $4 c) }
266272
| STORE offset align expr expr { fun c -> $1 ($2, $3, $4 c, $5 c) }
267273
| CONST literal { fun c -> fst (literal $1 $2) }
@@ -350,6 +356,16 @@ export_opt :
350356
start :
351357
| LPAR START var RPAR
352358
{ fun c -> $3 c func }
359+
;
360+
361+
global :
362+
| LPAR GLOBAL VALUE_TYPE expr RPAR
363+
{ let at = at () in
364+
fun c -> anon_global c; fun () -> {gtype = $3; init = $4 c} @@ at }
365+
| LPAR GLOBAL bind_var VALUE_TYPE expr RPAR /* Sugar */
366+
{ let at = at () in
367+
fun c -> bind_global c $3; fun () -> {gtype = $4; init = $5 c} @@ at }
368+
;
353369
354370
segment :
355371
| LPAR SEGMENT NAT text_list RPAR
@@ -410,11 +426,14 @@ export :
410426
module_fields :
411427
| /* empty */
412428
{ fun c ->
413-
{memory = None; types = c.types.tlist; funcs = []; start = None; imports = [];
414-
exports = []; table = []} }
429+
{memory = None; types = c.types.tlist; globals = []; funcs = [];
430+
start = None; imports = []; exports = []; table = []} }
415431
| func module_fields
416432
{ fun c -> let f = $1 c in let m = $2 c in let func, exs = f () in
417433
{m with funcs = func :: m.funcs; exports = exs @ m.exports} }
434+
| global module_fields
435+
{ fun c -> let g = $1 c in let m = $2 c in
436+
{m with globals = g () :: m.globals} }
418437
| import module_fields
419438
{ fun c -> let i = $1 c in let m = $2 c in
420439
{m with imports = i :: m.imports} }
@@ -423,7 +442,7 @@ module_fields :
423442
{m with exports = $1 c :: m.exports} }
424443
| table module_fields
425444
{ fun c -> let m = $2 c in
426-
{m with table = ($1 c) @ m.table} }
445+
{m with table = $1 c @ m.table} }
427446
| type_def module_fields
428447
{ fun c -> $1 c; $2 c }
429448
| memory module_fields

ml-proto/spec/ast.ml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ and expr' =
2626
| Call_import of var * expr list
2727
| Call_indirect of var * expr * expr list
2828

29-
(* Locals *)
29+
(* Variables *)
3030
| Get_local of var
3131
| Set_local of var * expr
3232
| Tee_local of var * expr
33+
| Get_global of var
34+
| Set_global of var * expr
3335

3436
(* Memory access *)
3537
| I32_load of Memory.offset * int * expr
@@ -194,7 +196,14 @@ and expr' =
194196
| Grow_memory of expr
195197

196198

197-
(* Functions *)
199+
(* Globals and Functions *)
200+
201+
type global = global' Source.phrase
202+
and global' =
203+
{
204+
gtype : Types.value_type;
205+
init : expr;
206+
}
198207

199208
type func = func' Source.phrase
200209
and func' =
@@ -212,6 +221,7 @@ and module' =
212221
{
213222
memory : Kernel.memory option;
214223
types : Types.func_type list;
224+
globals : global list;
215225
funcs : func list;
216226
start : var option;
217227
imports : Kernel.import list;

ml-proto/spec/check.ml

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type context =
2222
funcs : func_type list;
2323
imports : func_type list;
2424
locals : value_type list;
25+
globals : value_type list;
2526
return : expr_type;
2627
labels : expr_type_future list;
2728
has_memory : bool
@@ -35,6 +36,7 @@ let type_ types x = lookup "function type" types x
3536
let func c x = lookup "function" c.funcs x
3637
let import c x = lookup "import" c.imports x
3738
let local c x = lookup "local" c.locals x
39+
let global c x = lookup "global" c.globals x
3840
let label c x = lookup "label" c.labels x
3941

4042

@@ -195,6 +197,13 @@ let rec check_expr c et e =
195197
check_expr c (some (local c x)) e1;
196198
check_type (Some (local c x)) et e.at
197199

200+
| GetGlobal x ->
201+
check_type (Some (global c x)) et e.at
202+
203+
| SetGlobal (x, e1) ->
204+
check_expr c (some (global c x)) e1;
205+
check_type None et e.at
206+
198207
| Load (memop, e1) ->
199208
check_load c et memop e1 e.at
200209

@@ -283,6 +292,11 @@ and check_memop memop at =
283292
and check_mem_type ty sz at =
284293
require (ty = Int64Type || sz <> Memory.Mem32) at "memory size too big"
285294

295+
let check_init_expr e =
296+
match e.it with
297+
| Const _ | GetGlobal _ -> ()
298+
| _ -> error e.at "not an initialization expression"
299+
286300

287301
(*
288302
* check_func : context -> func -> unit
@@ -307,6 +321,11 @@ let check_func c f =
307321
let check_elem c x =
308322
ignore (func c x)
309323

324+
let check_global c g =
325+
let {gtype; init} = g.it in
326+
check_init_expr init;
327+
check_expr c (some gtype) init
328+
310329
module NameSet = Set.Make(String)
311330

312331
let check_export c set ex =
@@ -345,16 +364,19 @@ let check_memory memory =
345364
ignore (List.fold_left (check_segment mem.min) 0L mem.segments)
346365

347366
let check_module m =
348-
let {memory; types; funcs; start; imports; exports; table} = m.it in
367+
let {memory; types; globals; funcs; start; imports; exports; table} = m.it in
349368
Lib.Option.app check_memory memory;
350369
let c = {types;
351370
funcs = List.map (fun f -> type_ types f.it.ftype) funcs;
352371
imports = List.map (fun i -> type_ types i.it.itype) imports;
372+
globals = [];
353373
locals = [];
354374
return = None;
355375
labels = [];
356376
has_memory = memory <> None} in
357-
List.iter (check_func c) funcs;
358-
List.iter (check_elem c) table;
359-
ignore (List.fold_left (check_export c) NameSet.empty exports);
360-
check_start c start
377+
List.iter (check_global c) globals;
378+
let c' = {c with globals = List.map (fun g -> g.it.gtype) globals} in
379+
List.iter (check_func c') funcs;
380+
List.iter (check_elem c') table;
381+
ignore (List.fold_left (check_export c') NameSet.empty exports);
382+
check_start c' start

ml-proto/spec/decode.ml

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ let rec expr stack s =
250250
let x = at var s in
251251
Tee_local (x, e), es
252252

253-
| 0x1a | 0x1b | 0x1c | 0x1d | 0x1e | 0x1f as b, _ ->
253+
| 0x1c | 0x1d | 0x1e | 0x1f as b, _ ->
254254
illegal s pos b
255255

256256
| 0x20, e :: es -> let o, a = memop s in I32_load8_s (o, a, e), es
@@ -415,7 +415,14 @@ let rec expr stack s =
415415
| 0xb9, e2 :: e1 :: es -> I64_rotr (e1, e2), es
416416
| 0xba, e :: es -> I64_eqz e, es
417417

418-
| b, _ when b > 0xba -> illegal s pos b
418+
| 0xbb, es ->
419+
let x = at var s in
420+
Get_global x, es
421+
| 0xbc, e :: es ->
422+
let x = at var s in
423+
Set_global (x, e), es
424+
425+
| b, _ when b > 0xbc -> illegal s pos b
419426

420427
| b, _ -> error s pos "too few operands for operator"
421428

@@ -443,6 +450,7 @@ let id s =
443450
| "function" -> `FuncSection
444451
| "table" -> `TableSection
445452
| "memory" -> `MemorySection
453+
| "global" -> `GlobalSection
446454
| "export" -> `ExportSection
447455
| "start" -> `StartSection
448456
| "code" -> `CodeSection
@@ -503,6 +511,20 @@ let memory_section s =
503511
section `MemorySection (opt (at memory) true) None s
504512

505513

514+
(* Global section *)
515+
516+
let global s =
517+
let t = value_type s in
518+
let pos = pos s in
519+
let es = expr_block s in
520+
require (List.length es = 1) s pos "single expression expected";
521+
expect 0x0f s "`end` opcode expected";
522+
{gtype = t; init = List.hd es}
523+
524+
let global_section s =
525+
section `GlobalSection (vec (at global)) [] s
526+
527+
506528
(* Export section *)
507529

508530
let export s =
@@ -574,6 +596,8 @@ let module_ s =
574596
iterate unknown_section s;
575597
let memory_limits = memory_section s in
576598
iterate unknown_section s;
599+
let globals = global_section s in
600+
iterate unknown_section s;
577601
let exports = export_section s in
578602
iterate unknown_section s;
579603
let start = start_section s in
@@ -596,7 +620,7 @@ let module_ s =
596620
match memory_limits with
597621
| None -> None
598622
| Some memory -> Some Source.({memory.it with segments} @@ memory.at)
599-
in {memory; types; funcs; imports; exports; table; start}
623+
in {memory; types; globals; funcs; imports; exports; table; start}
600624

601625

602626
let decode name bs = at module_ (stream name bs)

0 commit comments

Comments
 (0)