Skip to content
This repository has been archived by the owner on Aug 17, 2022. It is now read-only.

Commit

Permalink
Merge pull request #26 from WebAssembly/fix-issue-19
Browse files Browse the repository at this point in the history
Change alias sugar, remove local indices, give module/instance types fresh index spaces
  • Loading branch information
lukewagner committed Feb 16, 2021
2 parents 0a9b92e + 3365dc8 commit 63cd6c0
Show file tree
Hide file tree
Showing 5 changed files with 353 additions and 318 deletions.
91 changes: 40 additions & 51 deletions proposals/module-linking/Binary.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,41 @@ Updates to
typesec ::= t*:section_1(vec(type))
type ::=
0x60 ft:functype -> ft
0x61 mt:moduletype -> mt
0x62 it:instancetype -> it
0x60 ft:functype -> ft
0x61 mt:moduletype -> mt
0x62 it:instancetype -> it
functype ::= rt_1:resulttype rt_2:resulttype -> rt_1 -> rt-2
functype ::= rt_1:resulttype rt_2:resulttype -> rt_1 -> rt-2
moduletype ::= mtd*:vec(moduletypedef) -> mtd*
instancetype ::= itd*:vec(instancetypedef) -> itd*
moduletype ::=
i*:vec(import) e*:vec(exporttype) -> {imports i*, exports e*}
moduletypedef ::=
itd -> itd
0x02 i:import -> {import i}
instancetype ::= e*:vec(exporttype) -> {exports e*}
instancetypedef ::=
0x01 t:type -> {type t}
0x07 et:exporttype -> {export et}
0x0f a:alias -> {alias a}
exporttype ::= nm:name d:importdesc -> {name nm, desc d}
exporttype ::= nm:name d:importdesc -> {name nm, desc d}
```

referencing:
Note: the numeric discriminants in `moduletypedef` are chosen to match the
section numbers for the corresponding sections.

Referencing:
* [`resulttype`](https://webassembly.github.io/spec/core/binary/types.html#binary-resulttype)
* [`import`](https://webassembly.github.io/spec/core/binary/modules.html#binary-import)
* [`importdesc`](https://webassembly.github.io/spec/core/binary/modules.html#binary-importdesc) -
although note that this is updated below as well.

**Validation**

* each `importdesc` is valid according to import section
* The `moduletypedef`s of a `moduletype` are validated in a fresh set of index
spaces (currently, only the type index space is used). Thus, the function
type of a function export must either be defined inside the `moduletype` or
aliased. The same goes for `instancetypedef`s and `instancetype`s.
* Types can only reference preceding type definitions. This forces everything to
be a DAG and forbids cyclic types. TODO: with type imports we may need cyclic
types, so this validation will likely change in some form.
Expand Down Expand Up @@ -115,7 +126,7 @@ A new section defining local instances
```
instancesec ::= i*:section_15(vec(instancedef)) -> i*
instancedef ::= 0x00 m:moduleidx e*:vec(exportdesc) -> {instantiate m, imports e*}
instancedef ::= 0x00 m:moduleidx arg*:vec(export) -> {instantiate m, imports arg*}
```

This defines instances in the module, appending to the instance index space.
Expand All @@ -130,18 +141,9 @@ the future we'll likely want this binary value to match that.
* The module index `m` must be in bounds.
* Indices of items referred to by `exportdesc` are all in-bounds. Can only refer
to imports/previous aliases, since only those sections can precede.
* The `e*` list is validated against the module type's declared list
of [imports pairwise and in-order](Explainer.md#module-imports-and-nested-instances).
The type of each item must be a subtype of the expected type listed in the
module's type.
* The `arg*` list is validated against `m`'s imports according to
[module subtyping](Subtyping.md#instantiation) rules.

**Execution notes**

* The actual module being instantiated does not need to list imports in the
exact same order as its type declaration. The `e*` has names based on the
local module type's declaration.
* Be sure to read up on [subtyping](./Subtyping.md) to ensure that instances
with a single name can be used to match imports with a two-level namespace.

## Alias section (16)

Expand All @@ -151,39 +153,26 @@ A new module section is added
aliassec ::= a*:section_16(vec(alias)) -> a*
alias ::=
0x00 i:instanceidx 0x00 e:exportidx -> (alias (instance $i) (func $e))
0x00 i:instanceidx 0x01 e:exportidx -> (alias (instance $i) (table $e))
0x00 i:instanceidx 0x02 e:exportidx -> (alias (instance $i) (memory $e))
0x00 i:instanceidx 0x03 e:exportidx -> (alias (instance $i) (global $e))
0x00 i:instanceidx 0x05 e:exportidx -> (alias (instance $i) (module $e))
0x00 i:instanceidx 0x06 e:exportidx -> (alias (instance $i) (instance $e))
0x01 0x05 m:moduleidx -> (alias parent (module $m))
0x01 0x07 t:typeidx -> (alias parent (type $t))
0x00 i:instanceidx 0x00 nm:name -> (alias $i "nm" (func))
0x00 i:instanceidx 0x01 nm:name -> (alias $i "nm" (table))
0x00 i:instanceidx 0x02 nm:name -> (alias $i "nm" (memory))
0x00 i:instanceidx 0x03 nm:name -> (alias $i "nm" (global))
0x00 i:instanceidx 0x05 nm:name -> (alias $i "nm" (module))
0x00 i:instanceidx 0x06 nm:name -> (alias $i "nm" (instance))
0x01 ct:varu32 0x05 m:moduleidx -> (alias outer ct m (module))
0x01 ct:varu32 0x07 t:typeidx -> (alias outer ct m (type))
```

**Validation**

* Aliased instance indexes are all in bounds. Remember "in bounds" here means it
can't refer to instances defined after the `alias` item.
* Aliased instance export indices are in bounds relative to the instance's
*locally-declared* (via module or instance type) list of exports.
* Export indices match the actual type of the export
* Aliases append to the respective index space.
* Parent aliases can only happen in submodules (not the top-level module) and
the index specifies is the entry, in the parent's raw index space, for that
item.
* Parent aliases can only refer to preceeding module/type definitions, relative
to the binary location where the inline module's type was declared. Note that
when the module code section is defined all of the parent's modules/types are
available, but inline modules still may not have access to all of these if the
items were declared after the module's type in the corresponding module
section.

**Execution notes**

* Note for child aliases that while the export is referred to by index it's
actually loaded from the specified instance by name. The name loaded
corresponds to the `i`th export's name in the locally defined type.
* The instance, module and type indices are all in bounds. Remember that "in bounds"
means only those definitions preceding this alias definition in binary format
order. In the case of outer aliases, this means the position of the nested module
definition in the outer module.
* Aliased instance export names must be found in `i`'s instance type with a
matching definition kind.
* The `ct` of an outer alias must be *less than* the number of enclosing modules
which implicitly prevents outer aliases from appearing in top-level modules.

## Function section

Expand Down
50 changes: 24 additions & 26 deletions proposals/module-linking/Example-LinkTimeVirtualization.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@ We start with a child module that has been written and compiled separately,
without regard to the parent module. It imports `wasi_file` to do file I/O:

```wasm
;; child.wat
(module
(type $WasiFile (instance
(export "read" (func $read (param i32 i32 i32) (result i32)))
(export "write" (func $write (param i32 i32 i32) (result i32)))
(module $CHILD
(import "wasi_file" (instance $wasi-file
(export "read" (func (param i32 i32 i32) (result i32)))
(export "write" (func (param i32 i32 i32) (result i32)))
))
(import "wasi_file" (instance $wasi-file (type $WasiFile)))
(func $play (export "play")
...
call $wasi-file.$read
call (func $wasi-file "read")
...
)
)
Expand All @@ -31,8 +29,11 @@ as a separate module that imports a "real" `wasi_file` instance and uses it
to implement a new virtualized `wasi_file` instance:

```wasm
;; virtualize.wat
(module
(module $VIRTUALIZE
(type $WasiFile (instance
(export "read" (func (param i32 i32 i32) (result i32)))
(export "write" (func (param i32 i32 i32) (result i32)))
))
(import "wasi_file" (instance $wasi-file (type $WasiFile)))
(func (export "read") (param i32 i32 i32) (result i32)
Expand All @@ -44,44 +45,41 @@ to implement a new virtualized `wasi_file` instance:
)
```

We can now write the parent module by composing `virtualize.wasm` and
`child.wasm` appropriately:
We can now write the parent module by composing `$VIRTUALIZE` and `$CHILD`:

```wasm
;; parent.wat
(module
(module $PARENT
(type $WasiFile (instance
(export "read" (func $read (param i32 i32 i32) (result i32)))
(export "write" (func $write (param i32 i32 i32) (result i32)))
(export "read" (func (param i32 i32 i32) (result i32)))
(export "write" (func (param i32 i32 i32) (result i32)))
))
(import "wasi_file" (instance $real-wasi (type $WasiFile)))
(import "./virtualize.wasm" (module $VIRTUALIZE
(import "wasi_file" (instance (type $WasiFile)))
(export $WasiFile)
(import "virtualize" (module $VIRTUALIZE
(import "wasi_file" (instance (type outer $PARENT $WasiFile)))
(export (type outer $PARENT $WasiFile))
))
(import "./child.wasm" (module $CHILD
(import "wasi_file" (instance (type $WasiFile)))
(import "child" (module $CHILD
(import "wasi_file" (instance (type outer $PARENT $WasiFile)))
(export "play" (func))
))
(instance $virt-wasi (instantiate $VIRTUALIZE (instance $real-wasi)))
(instance $child (instantiate $CHILD (instance $virt-wasi)))
(instance $virt-wasi (instantiate $VIRTUALIZE (import "wasi_file" (instance $real-wasi))))
(instance $child (instantiate $CHILD (import "wasi_file" (instance $virt-wasi))))
(func (export "work")
...
call $child.$play
call (func $child "play")
...
)
)
```

Here, we assume the host understands relative file paths, but we could also bundle
all 3 files into one compound `parent-bundle.wasm`:
all 3 files into one compound `$PARENT-BUNDLE` module:

```wasm
;; parent-bundled.wat
(module
(module $PARENT-BUNDLE
(type $WasiFile ...same as above)
(import "wasi_file" ...same as above)
Expand Down
Loading

0 comments on commit 63cd6c0

Please sign in to comment.