Scoping and Layering the Module Linking and Interface Types proposals
WebAssembly CG
April 27th, 2021
Outline
Current Module Linking proposal summary
(module $APP
(import “libc” (module $LIBC� (export “malloc” (func (param i32) (result i32)))� ))
(instance $libc (instantiate $LIBC))
(module $CODE
(import “libc” (instance $libc� (export “malloc” (func (param i32) (result i32)))� ))
(func (export “run”) (param i32 i32) …)� )
(instance $code (instantiate $CODE (import “libc” (instance $libc))))
(func (export “run”) (param i32 i32)� (call (func $code “run”) …)� )�)
$libc
$code
import
instance�of $APP
import
Current Module Linking proposal summary
(module $COMPOUND
(module $LIBC … )
(module $APP_A� (import “libc” (module $LIBC …))� (instance $libc (instantiate $LIBC))� … � )
(module $APP_B� (import “libc” (module $LIBC …))� (instance $libc (instantiate $LIBC))� … � )
(instance $app_a (instantiate $APP_A (import “libc” (module $LIBC))))� (instance $app_b (instantiate $APP_B (import “libc” (module $LIBC))))�)
$app_a/�$libc
$app_b/�$libc
instance�of $COMPOUND
$app_b
$app_a
Current Module Linking proposal summary
What would Module Linking look like as a new layer?
Core wasm is unchanged, all new things go in a new “adapter module”:
(module
(import “libc” (module $LIBC …))
(instance $libc (instantiate $LIBC))
(module $CODE
(import “libc” (instance $LIBC …))
…� (func (export “run”) …)� )
(instance $code (instantiate $CODE� (import “libc” (instance $libc))� ))
(export “run” (func $code “run”))�)
(adapter module
(import “libc” (module $LIBC …))
(instance $libc (instantiate $LIBC))
(instance $code (instantiate $CODE
(import “libc” “malloc” (func $libc “malloc"))
))
(export “run” (func $code “run”))
)
(module $CODE� (import “libc” “malloc” (func …))� …� (func (export “run”) …)� )
The current (core) proposal:
As a layered proposal:
What would Module Linking look like as a new layer?
module
$M0
module
$M2
module
$M1
module
$M4
module
$M3
(adapter module $M0
(module $M1 …)
(adapter module $M2
(module $M3 …)
(module $M4 …)
)�
)
(instance $i1 (instantiate $M1 …))
(instance $i3 (instantiate $M3 …))
(instance $i4.1 (instantiate $M4 …))
(instance $i4.2 (instantiate $M4 …))�
(instance $i2 (instantiate $M2 …))
instance�$i0
instance�$i2
instance�$i1
instance�$i4.1
instance�$i3
instance�$i4.2
at instantiation-time
...
What would Module Linking look like as a new layer?
From a spec document POV (before):
Core
JS API
Web API
browsers
browsers
browsers
node.js
node.js
node.js
native wasm hosts
What would Module Linking look like as a new layer?
From a spec document POV (after):
Core
JS API
Web API
Module
Linking
browsers
browsers
browsers
node.js
node.js
node.js
other language APIs
node.js
node.js
native language
runtimes
native wasm hosts
...
What would Module Linking look like as a new layer?
From an implementation POV:
engine
implements
linker
implements
in-terms-of
Core
JS API
Web API
Module
Linking
browsers
browsers
browsers
node.js
node.js
node.js
other language APIs
node.js
node.js
native language
runtimes
native wasm hosts
What about Interface Types?
What about Interface Types?
Example of: Core + (Module Linking + Interface Types):
(module
(import “libc” “malloc” (func (param i32) (result i32)))
…
(func (export “run”) (param i32 i32) (result i32 i32) …)
)
(adapter module
(import “libc” (module $LIBC …))
(instance $libc (instantiate $LIBC))
(instance $core (instantiate $CORE (import “libc” “malloc” (func $libc “malloc"))))
)
(adapter_func (export “run”) (param string) (result string)
… lower param
call (func $core “run”)
… lift result
)
...
What would ML+IT look like as a new layer?
= MVP + reference-types + bulk-mem + ...
= module linking + interface types
+ what else ??
Does every new idea that “doesn’t belong in core” belong in Module Linking?�
What doesn’t belong in Module Linking?�
This is going to go poorly if we don’t carefully define a scope for Module Linking.
Core
JS API
Web API
Module�Linking
browsers
browsers
browsers
node.js
node.js
node.js
other language APIs
node.js
node.js
native language
runtimes
native wasm hosts
Spectrum of linking dynamism
Module
Linking
(component $C …)
+
instantiate $C : �[ im* ] → [(handle $C)]
+
call_export $C $ex :�[ (handle $C) args* ] → [ results* ]
(module $M …)
+
instantiate $M : �[ im* ] →
[(ref (instanceof $M))]
+
call_export $M $ex :�[ (ref (instanceof $M))� args* ] → [ results* ]
(module $M …)�+
ref.module $M�+
instantiate :�[(ref (module $MT)) im*] →�[(ref (instance IT))]�+
instance.get_export $IT $ex :�[(ref (instance $IT))] →�[(ref (func FT))]
…
+
compile $MT : �[i32 i32] →�[(ref module $MT)]
Full JIT
support
existing
concepts
No JIT
static
linkage
No GC
static
lifecycle
existing
concepts
No JIT
static
linkage
No GC
static
lifecycle
existing
concepts
No JIT
static
linkage
No GC
static
lifecycle
existing
concepts
No JIT
static
linkage
No GC
static
lifecycle
existing
concepts
No JIT
static
linkage
No GC
static
lifecycle
existing
concepts
No JIT
static
linkage
No GC
static
lifecycle
...
Outline
What is the scope of Module Linking?
Module�Linking
= MVP + reference-types + bulk-mem + ...
= module linking + interface types
+ ...
Core
JS API
Web API
Lightweight
Component
Model
browsers
browsers
browsers
node.js
node.js
node.js
other language APIs
node.js
node.js
other language
runtimes
native wasm hosts
What’s a component?
What’s a component?
What’s a component model?
What’s a component model?
What’s a lightweight component model?
Spectrum of granularity and concerns
Language-specific units
Lightweight components
Host-specific units
subsystems
engines
transformers
.a / .so�libraries
ESMs
crates
language actors
lightweight frameworks
left-pad
libc
Boost
Serde
React
MagickWand
zlib
clang
webview
bullet
OpenCV
may contain
1
*
may contain
1
*
containers
(micro-)�services
VMs
exes
Concerns:
Concerns:
Concerns:
direct host execution
direct host execution
direct host execution
What’s a lightweight component model?
actor / container / VM / microservice
component
.??�.wasm
.??�.wasm
.wasm
.??
.??
?? memory
??�ABI
component
.??�.wasm
.??�.wasm
.wasm
.??
.??
?? memory
??�ABI
component
.??�.wasm
.??�.wasm
.wasm
.??
.??
?? memory
??�ABI
component
.??�.wasm
.??�.wasm
.wasm
.??
.??
?? memory
??�ABI
actor / container / VM / microservice
component
.??�.wasm
.??�.wasm
.wasm
.??
.??
?? memory
??�ABI
component
.??�.wasm
.??�.wasm
.wasm
.??
.??
?? memory
??�ABI
component
.??�.wasm
.??�.wasm
.wasm
.??
.??
?? memory
??�ABI
component
.??�.wasm
.??�.wasm
.wasm
.??
.??
?? memory
??�ABI
actor / container / VM / microservice
component
.so�.wasm
.so�.wasm
Lightweight enough to do this ⤴
fast import calls
host�defined
host�defined
...
.wasm�
.o
.o
Linear memory
C�ABI
component
.cmxs�.wasm
.cmxs�.wasm
.wasm
.cma
.cma
GC heap
OCaml�ABI
component
.??�.wasm
.??�.wasm
.wasm
.??
.??
?? memory
??�ABI
component
.??�.wasm
.??�.wasm
.wasm
.??
.??
?? memory
??�ABI
Outline
Background
Host-embedding use cases
...
Composition use cases
...
Composition use cases (future features)
...
Static analyzability use cases
...
Requirements
Requirements
...
Spectrum of linking dynamism
Module
Linking
(component $C …)
+
instantiate $C : �[ im* ] → [(handle $C)]
+
call_export $C $ex :�[ (handle $C) args* ] → [ results* ]
(module $M …)
+
instantiate $M : �[ im* ] →
[(ref (instanceof $M))]
+
call_export $M $ex :�[ (ref (instanceof $M))� args* ] → [ results* ]
(module $M …)�+
ref.module $M�+
instantiate :�[(ref (module $MT)) im*] →�[(ref (instance IT))]�+
instance.get_export $IT $ex :�[(ref (instance $IT))] →�[(ref (func FT))]
…
+
compile $MT : �[i32 i32] →�[(ref module $MT)]
Full JIT
support
existing
concepts
No JIT
static
linkage
No GC
static
lifecycle
existing
concepts
No JIT
static
linkage
No GC
static
lifecycle
existing
concepts
No JIT
static
linkage
No GC
static
lifecycle
existing
concepts
No JIT
static
linkage
No GC
static
lifecycle
existing
concepts
No JIT
static
linkage
No GC
static
lifecycle
existing
concepts
No JIT
static
linkage
No GC
static
lifecycle
May be added (in some form) to core wasm in the future; may be used by individual component bodies; but not baked into the component model
...
In scope for component model
Outline
Proposed next steps
Canonical adapter functions
(adapter module
(import “log” (func $log (param string)))
(adapter_func $adapt_log (param i32 i32)
(call $log (list.lift …))
)
(module $CORE
(import “” “log” (func (param i32 i32))
…
(func (export “run”) (param i32 i32) …)
)
(instance $core (instantiate $CORE� (import “” “log” (func $adapt_log))))
(adapter_func $adapt_run (param string)
(call (func $core “run”) (list.lower …))
)
(export “run” (func $adapt_run))
)
(adapter_func $adapt_run (param string)
canonical export (func $core “run”)
)
(adapter_func $adapt_log (param i32 i32)
canonical import $log
)
In the binary format, there is 0 LEB immediate
so we can later add a memory=gc option
Proposed next steps
...
Developer Preview
...
What about WASI?
WASI spec
wasi-io
wasi-fs
Component Model spec
intertype
canonical ABI
wasi-io (core)
wasi-fs (core)
derive
components
components
core modules
import
components
components
components
import
in-terms-of
via
...
Client code:
wasm32-wasi-component
wasm32-wasi-canonical
Compiler triple:
What about WASI?
WASI spec
wasi-io
wasi-fs
Component Model spec
intertype
canonical ABI
wasi-io (core)
wasi-fs (core)
derive
components
components
core modules
import
components
components
components
import
in-terms-of
via
...
Client code:
...
wasm32-wasi-component
wasm32-wasi-canonical
Compiler triple:
Outline
Discussion + Polls