Reference Types for WebAssembly
February 19, 2018 ยท View on GitHub
Introduction
Use cases:
- Easier and more efficient interop with host
- Manipulation of tables inside Wasm
- Tyoed function pointers
- Setting the stage for other proposals like exception handling
- A smoother transition path to GC
Design:
- Identify separate levels of feature support that could be turned into incremental proposals
- Goal: get the most important parts soon
Level 0: Add anyref value type
Motivation: Allow host references to be represented directly by type anyref,
without having to go through tables, allocating slots, and maintaining index bijections at the boundaries.
Changes:
- Add
anyrefas a fifth value type (valtype = numtype | reftype) - Add
nullinstruction and (default) value - Any non-primitive value from the host can be passed as
anyrefto Wasm function
That's all!
Notes:
- Does not imply GC by itself, only if host refs are GCed pointers!
- Reference types are opaque.
- No way to create a non-null
anyrefvalue inside Wasm. - No way to consume an
anyrefvalue inside Wasm. - No way to put
anyrefinto a table. - No subtyping between
anyfuncandanyref.
Level 1: Allow anyref as an element type
Motivation: Allow representing data structures containing references by repurposng tables as a general memory for opaque data types; allow manipulating function tables from within Wasm.
Changes:
- Add
anyrefelement type andanyfuncvalue type (unify elemtype and reftype) - Allow multiple tables
- Add
(table.get $t)and(table.set $t)instructions
Notes:
call_indirecttakes table index immediate.call_indirectrequires element type <anyfunc.- Element segment has table index immediate.
- Still no subtyping between
anyfuncandanyrefelement types.
Question:
- Should we rename
get/set_globaltoglobal.get/set?
Level 2: Function references
Motivation: Allow function pointers to be expressed directly without going through table and dynamic type check; enable functions to be passed to other modules easily.
Changes:
- Add
(ref $t)as reference (i.e., value type and element) type - Add
(ref.func $f)and(call_ref)instructions - Introduce subtyping
ref <functype><anyfunc<anyref
Notes:
- Function references cannot be null.
Question:
- General function have no reasonable default, do we need scoped variables like
let? - Should there be a down cast instruction?
- Should there be depth subtyping for function types?
- Should we rename
call_indirecttocall_table?
Level 3: Type import/export
Motivation: Allow the host (or Wasm modules) to distinguish different reference types.
Changes:
- Add
(type)external type, enables types to be imported and exported - Add
WebAssembly.Typeclass to JS API, creates new opaque data types - Subtyping
ref <abstype><anyref
Notes:
- Type
(ref $t)can now denote an abstract type or a function reference - Imported types have index starting from 0.
- May need to impose constraints on order of imports, to stratify section dependencies.
Questions:
- Should subsumption be implicit of require an explicit upcast?
- Do we need a nullable
(ref opt $t)type to allow use with locals etc.? - Should we add
(new)definitional type to enable Wasm modules to define new types, too? - Should we add a
(cast $t)instruction for down casts? - Should JS API allow specifying subtyping between new types?
Level 4+: Possible future additions
- Introduce reference types pointing to tables, memories, or globals (first-class tables, memories, globals)
- Allow all value types as element types (unify elemtype with valtype)
- GC extension
Detailed Changes
Multiple Tables:
- more than one table allowed (already representable in binary format)
- element section has table index (already reserved in binary format)
- call_indirect has table index (already reserved in binary format)
Reference Types:
numtype ::= i32 | i64 | f32 | f64valtype ::= numtype | reftypereftype ::= anyref | anyfunc | ref <typeidx>elemtype ::= reftype- subtying:
ref $t < anyref,ref <functype><anyfunc<anyref - subsumption:
e : tiffe : t'andt' < t call_indirectrequires element type <anyfunc
Type Import/Export:
externtype ::= ... | type- reserve byte in binary format to allow refinements later
- new type import section? (before type)
- new type export section? (before export)
deftype ::= ... | newnew WebAssembly.Type(name)creates new unique type
Instructions for Manipulating Tables:
table.init $src $dst : [i32 i32 i32] -> []table.copy $src $dst : [i32 i32 i32] -> []table.clear $dst : [i32 i32] -> []table.grow $dst : [i32] -> [i32]table.size $dst : [] -> [i32]table.get $dst : [i32] -> [t]table.set $dst : [i32 t] -> [](call_indirect $t $dst)reduces to(table.get $dst) (cast (ref $t)) (call_ref (ref $t))
Instructions for Function References:
ref.func $f : [] -> (ref $t) iff $f : $tcall_ref : [ts1 (ref $t)] -> [ts2] iff $t = [ts1] -> [ts2]
Instructions for Casting:
cast t : [t'] -> [t]ifft<t'
Pssobile Future Changes
References to all external types:
deftype := ... | globaltype | tabletype | memtype
Instructions for global references:
ref.global $g : [] -> (ref $t) iff $g : $tget_global_ref : [(ref $t)] -> [t] iff $t = mut tset_global_ref : [t (ref $t)] -> [] iff $t = var t
Instructions for table references:
ref.table $x : [] -> (ref $t) iff $x : $t- ...
Instructions for memory references:
ref.mem $m : [] -> (ref $t) iff $m : $t- ...