Commit Graph

12 Commits

Author SHA1 Message Date
Takeshi Yoneda
0ca7ee1340 Use same CompiledCode for import replacement (#478)
This commit allows CompiledCode to be re-used regardless of
the existence of import replacement configs for instantiation.

In order to achieve this, we introduce ModuleID, which is sha256
checksum calculated on source bytes, as a key for module compilation
cache. Previously, we used*wasm.Module as keys for caches which
differ before/after import replacement.

Signed-off-by: Takeshi Yoneda takeshi@tetrate.io
2022-04-18 20:07:28 +09:00
Takeshi Yoneda
d2905d480c Make CompileModule actually compile (#469)
This commit makes it possible for functions to be compiled before instantiation.
Notably, this adds CompileModule method on Engine interface where we pass
wasm.Module (which is the decoded module) to engines, and engines compile
all the module functions and caches them keyed on *wasm.Module.

In order to achieve that, this stops the compiled native code from embedding typeID
which is assigned for all the function types in a store.

Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
2022-04-18 18:14:58 +09:00
Takeshi Yoneda
bd328a3355 Add compilation cache functionality and Close on CompiledCode. (#457)
Thanks to #454, now the compiled binary (code segment) can be reused for
multiple module instances originating from the same source (wasm.Module).

This commit introduces the caching mechanism on engine where it caches
compiled functions keyed on `wasm.Module`. As a result, this allows us to
do the fast module instantiation from the same *CompiledCode.

In order to release the cache properly, this also adds `Close` method 
on CompiledCode.

Here's some bench result for instantiating multiple modules from the same CompiledCode:

```
name                           old time/op    new time/op    delta
Initialization/interpreter-32    2.84ms ± 3%    0.06ms ± 1%  -97.73%  (p=0.008 n=5+5)
Initialization/jit-32            10.7ms ±18%     0.1ms ± 1%  -99.52%  (p=0.008 n=5+5)

name                           old alloc/op   new alloc/op   delta
Initialization/interpreter-32    1.25MB ± 0%    0.15MB ± 0%  -88.41%  (p=0.008 n=5+5)
Initialization/jit-32            4.46MB ± 0%    0.15MB ± 0%  -96.69%  (p=0.008 n=5+5)

name                           old allocs/op  new allocs/op  delta
Initialization/interpreter-32     35.2k ± 0%      0.3k ± 0%  -99.29%  (p=0.008 n=5+5)
Initialization/jit-32             94.1k ± 0%      0.2k ± 0%  -99.74%  (p=0.008 n=5+5)
```


Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
2022-04-14 11:33:10 +09:00
Crypt Keeper
c3ff16d596 Supports functions with multiple results (multi-value) (#446)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
2022-04-13 09:22:39 +08:00
Crypt Keeper
fb0b311844 Consistently uses LEB128 signed encoding for global constants (#443)
Global constants can be defined in wasm or in ModuleBuilder. In either
case, they end up being decoded and interpreted during instantiation.
This chooses signed encoding to avoid surprises. A more comprehensive
explanation was added to RATIONALE.md, but the motivation was a global
100 coming out negative.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-04-06 09:50:47 +08:00
Crypt Keeper
f5598c9a8e Fixes global numeric types to have max of signed encoding (#442)
This adjusts towards the exiting code which used int32/64 instead of
uint32/64. The reason is that the spec indicates intepretation as signed
numbers, which affects the maximum value.

See https://www.w3.org/TR/wasm-core-1/#value-types%E2%91%A2

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-04-06 06:35:31 +08:00
Crypt Keeper
b1cffcc58e Adds ModuleBuilder.ExportGlobalXXX to configure constants (#441)
See https://github.com/summerwind/the-art-of-webassembly-go/issues/1

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-04-05 15:41:19 +08:00
Crypt Keeper
9345a89bea Adds ModuleBuilder.ExportMemory and ExportMemoryWithMax (#440)
This adds functions to configure memory with ModuleBuilder. This uses
two functions, ExportMemory and ExportMemoryWithMax, as working with
uint32 pointers is awkward.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-04-05 13:12:43 +08:00
Crypt Keeper
2664b1eb62 Simplifies API per feedback (#427)
During #425, @neilalexander gave constructive feedback that the API is
both moving fast, and not good enough yet. This attempts to reduce the
incidental complexity at the cost of a little conflation.

### odd presence of `wasm` and `wasi` packages -> `api` package

We had public API packages in wasm and wasi, which helped us avoid
leaking too many internals as public. That these had names that look
like there should be implementations in them cause unnecessary
confusion. This squashes both into one package "api" which has no
package collission with anything.

We've long struggled with the poorly specified and non-uniformly
implemented WASI specification. Trying to bring visibility to its
constraints knowing they are routinely invalid taints our API for no
good reason. This removes all `WASI` commands for a default to invoke
the function `_start` if it exists. In doing so, there's only one path
to start a module.

Moreover, this puts all wasi code in a top-level package "wasi" as it
isn't re-imported by any internal types.

### Reuse of Module for pre and post instantiation to `Binary` -> `Module`

Module is defined by WebAssembly in many phases, from decoded to
instantiated. However, using the same noun in multiple packages is very
confusing. We at one point tried a name "DecodedModule" or
"InstantiatedModule", but this is a fools errand. By deviating slightly
from the spec we can make it unambiguous what a module is.

This make a result of compilation a `Binary`, retaining `Module` for an
instantiated one. In doing so, there's no longer any name conflicts
whatsoever.

### Confusion about config -> `ModuleConfig`

Also caused by splitting wasm into wasm+wasi is configuration. This
conflates both into the same type `ModuleConfig` as it is simpler than
trying to explain a "will never be finished" api of wasi snapshot-01 in
routine use of WebAssembly. In other words, this further moves WASI out
of the foreground as it has been nothing but burden.

```diff
--- a/README.md
+++ b/README.md
@@ -49,8 +49,8 @@ For example, here's how you can allow WebAssembly modules to read
-wm, err := r.InstantiateModule(wazero.WASISnapshotPreview1())
-defer wm.Close()
+wm, err := wasi.InstantiateSnapshotPreview1(r)
+defer wm.Close()

-sysConfig := wazero.NewSysConfig().WithFS(os.DirFS("/work/home"))
-module, err := wazero.StartWASICommandWithConfig(r, compiled, sysConfig)
+config := wazero.ModuleConfig().WithFS(os.DirFS("/work/home"))
+module, err := r.InstantiateModule(binary, config)
 defer module.Close()
 ...
```
2022-04-02 06:42:36 +08:00
Crypt Keeper
59617a24c8 wasi: renames WASIConfig to SysConfig and makes stdio defaults safer (#396)
This introduces `SysConfig` to replace `WASIConfig` and formalize documentation around system calls.

The only incompatible change planned after this is to switch from wasi.FS to fs.FS

Implementation Notes:

Defaulting to os.Stdin os.Stdout and os.Stderr doesn't make sense for
the same reasons as why we don't propagate ENV or ARGV: it violates
sand-boxing. Moreover, these are worse as they prevent concurrency and
can also lead to console overload if accidentally not overridden.

This also changes default stdin to read EOF as that is safer than reading
from os.DevNull, which can run the host out of file descriptors.

Finally, this removes "WithPreopens" for "WithFS" and "WithWorkDirFS",
to focus on the intended result. Similar Docker, if the WorkDir isn't set, it
defaults to the same as root.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-03-23 12:58:55 +08:00
Crypt Keeper
2114dfc492 Renames Runtime.DecodeModule to CompileModule (#351)
The best way to reduce performance impact of instantiating a module
multiple times is lowering it to wazero's IR or even generating
assembly. This renames `DecodeModule` to `CompileModule` to allow that
sort of side-effect to be obvious even if it isn't currently
implemented.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-03-09 13:59:19 +08:00
Crypt Keeper
50d9fa58a1 Runtime.NewModule -> InstantiateModule and adds ModuleBuilder (#349)
This reverts `Runtime.NewModule` back to `InstantiateModule` as it calls
more attention to the registration aspect of it, and also makes a chain
of `NewXX` more clear. This is particularly helpful as this change
introduces `ModuleBuilder` which is created by `NewModuleBuilder`.

`ModuleBuilder` is a way to define a WebAssembly 1.0 (20191205) in Go.
The first iteration allows setting the module name and exported
functions. The next PR will add globals.

Ex. Below defines and instantiates a module named "env" with one function:

```go
hello := func() {
	fmt.Fprintln(stdout, "hello!")
}
_, err := r.NewModuleBuilder("env").ExportFunction("hello", hello).InstantiateModule()
```

If the same module may be instantiated multiple times, it is more efficient to separate steps. Ex.

```go
env, err := r.NewModuleBuilder("env").ExportFunction("get_random_string", getRandomString).Build()

_, err := r.InstantiateModule(env.WithName("env.1"))
_, err := r.InstantiateModule(env.WithName("env.2"))
```

Note: Builder methods do not return errors, to allow chaining. Any validation errors are deferred until Build.
Note: Insertion order is not retained. Anything defined by this builder is sorted lexicographically on Build.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-03-09 10:39:13 +08:00