This commit implements the v128.const, i32x4.add and i64x2.add in
interpreter mode and this adds support for the vector value types in the
locals and globals.
Notably, the vector type values can be passed and returned by exported functions
as well as host functions via two-uint64 encodings as described in #484 (comment).
Note: implementation of these instructions on JIT will be done in subsequent PR.
part of #484
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit enables WebAssembly 2.0 Core Specification tests.
In order to pass the tests, this fixes several places mostly on the
validation logic.
Note that SIMD instructions are not implemented yet.
part of #484
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Co-authored-by: Crypt Keeper <64215+codefromthecrypt@users.noreply.github.com>
This commit completes the reference-types proposal implementation.
Notably, this adds support for
* `ref.is_null`, `ref.func`, `ref.is_null` instructions
* `table.get`, `table.set`, `table.grow`, `table.size` and `table.fill` instructions
* `Externref` and `Funcref` types (including invocation via uint64 encoding).
part of #484
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
By testing the side-effects of Memory.Read, we ensure users who control
the underlying memory capacity can use the returned slice for
write-through access to Wasm addressible memory. Notably, this allows a
shared fixed length data structure to exist with a pointer on the Go
side and a memory offset on the Wasm side.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
Co-authored-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit adds support for multiple tables per module.
Notably, if the WithFeatureReferenceTypes is enabled,
call_indirect, table.init and table.copy instructions
can reference non-zero indexed tables.
part of #484
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This adds an experimental package to expose two work-in-progress
features:
* FunctionListener - for tracing etc.
* Sys - to control random number generators
Both the functionality and the names of the features above are
not stable. However, this should help those who can tolerate drift a
means to test things out.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
Co-authored-by: Anuraag Agrawal <anuraaga@gmail.com>
This commit implements the rest of the unimplemented instructions in the
bulk-memory-operations proposal.
Notably, this adds support for table.init, table.copy and elem.drop
instructions toggled by FeatureBulkMemoryOperations.
Given that, now wazero has the complete support for the bulk-memory-operations
proposal as described in https://github.com/WebAssembly/spec/blob/main/proposals/bulk-memory-operations/Overview.mdfixes#321
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This prepares for exposing operations like Memory.Grow while keeping the
ability to trace what did that, by adding a `context.Context` initial
parameter. This adds this to all API methods that mutate or return
mutated data.
Before, we made a change to trace functions and general lifecycle
commands, but we missed this part. Ex. We track functions, but can't
track what closed the module, changed memory or a mutable constant.
Changing to do this now is not only more consistent, but helps us
optimize at least the interpreter to help users identify otherwise
opaque code that can cause harm. This is critical before we add more
functions that can cause harm, such as Memory.Grow.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This is an API breaking change that does a few things:
* Stop encouraging practice that can break context propagation:
* Stops caching `context.Context` in `wazero.RuntimeConfig`
* Stops caching `context.Context` in `api.Module`
* Fixes context propagation in function calls:
* Changes `api.Function`'s arg0 from `api.Module` to `context.Context`
* Adds `context.Context` parameter in instantiation (propagates to
.start)
* Allows context propagation for heavy operations like compile:
* Adds `context.Context` as the initial parameter of `CompileModule`
The design we had earlier was a good start, but this is the only way to
ensure coherence when users start correlating or tracing. While adding a
`context.Context` parameter may seem difficult, wazero is a low-level
library and WebAssembly is notoriously difficult to troubleshoot. In
other words, it will be easier to explain to users to pass (even nil) as
the context parameter vs try to figure out things without coherent
context.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This centralizes logic that gets params and results in and out of
Go-defined functions. This allows us to refactor and optimize easier as
well ensure value coersion is the same regardless of the engine type.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
Co-authored-by: Takeshi Yoneda <takeshi@tetrate.io>
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
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>
This implements our own assertion library so that we can remove the
testify dependency. I've changed a lot of call sites so that we only
have to maintain a minimal amount of assertions.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
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>
This starts the process of removing all dependencies from wazero, by
isolating all assertions we use into a single file. This allows us to
port those assertions as we have time, and when twitchy is gone, the
project literally has no dependencies except go!
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This moves the responsibility to close a module from the engine to the
context. The reason for this is that the engine is what defines the
function. When a module is closed, it is often from an imported host
function. If it is on the module engine, it is easy to accidentally
close an imported module.
This refactors the WASI tests also, to ensure they aren't cheating too
much. This allows us to know for example "proc_exit" works without too
much trouble.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This adds tests that pass without changing deferred error handling.
There are some tests that don't pass even without deferred error
handling. I'll add those in a separate PR.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
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()
...
```
This changes the way the JIT engine is tested by focusing on two areas:
* Did NewModuleEngine set a finalizer for each compiled function
correctly?
* Did ModuleEngine.Close remove its compiled function correctly?
A later change will do parallel tests for both engines and won't have to
at the same time check how finalizers work.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This deduplicates Engine tests so that there's less chance of copy/paste
errors and less requirement to use ad-hoc tests which are outside the
relevant source tree.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
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>
The prior design had a problem, where multiple imports of WASI would end
up having different file descriptors for the same file. Moreover, there
was no means to close any of these when a module importing WASI was
closed.
This moves all existing functionality to a new type SystemContext, which
is owned by the importing module, similar to how it owns its memory.
While this PR doesn't fix some problems such as unclosed files, the code
is now organized in a way it can be, and these issues will be resolved
by #394.
In order to fix scope, `WASISnapshotPreview1WithConfig` had to be
removed.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This enforces all ElementSegment rules prior to creating a new module
engine. This allows flexibility and less errors inside that function.
This also uses interface as table element to pass tests with -race
Signed-off-by: Adrian Cole <adrian@tetrate.io>
Co-authored-by: Takeshi Yoneda <takeshi@tetrate.io>
Before, if there was an unexpected panic on munmap of a code section,
we'd only see "invalid argument" and have to guess why. This propagates
context and stubs out the finalizer so we can test a better error, like:
"jit: failed to munmap code segment for test.function[0]: invalid argument"
This also attempts to improve troubleshooting time by making all module
instantiation that used empty module name, use the test name instead.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
StartWASICommandWithConfig is like StartWASICommand, except you can override configuration based on the importing
module. For example, you can use this to define different args depending on the importing module.
```go
// Initialize base configuration:
r := wazero.NewRuntime()
config := wazero.NewWASIConfig().WithStdout(buf)
wasi, _ := r.NewHostModule(wazero.WASISnapshotPreview1WithConfig(config))
decoded, _ := r.CompileModule(source)
// Assign configuration only when ready to instantiate.
module, _ := StartWASICommandWithConfig(r, decoded, config.WithArgs("rotate", "angle=90", "dir=cw"))
```
This also changes the config to be immutable since it may be reused many
times. Instead of assigning fields directly, use `WithXXX`.
Finally, this changes the examples to encourage using `.Close()`
Fixes#364
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit adds Module.Close API, which can be used to indicate that
the module will no longer be used and setting finalizers for the allocated
resources. Notably, this will set finalizers on compiledFunction to deallocate
mapped memory regions as well as removing them from the store-wide
storage. As a result, users can re-instantiate a module for the same name
while having the safety -- Calling Module.Close is safe even when there are
outstanding function calls as we never explicitly deallocate but indirectly
do that via Goruntine's finalizers.
resolves: #293
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit fixes a few bugs in engine.Release, and backfills
unittests. Notably, moduleEngines must not release the imported
functions.
Signed-off-by: Takeshi Yoneda takeshi@tetrate.io
This commit adds ModuleEngine interface which is used to
make calls and created per-module instance. Notably, this
enables us to remove the necessity for store to holds
FunctionInstances and FunctionIndex.
This is a follow-up from #342
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Co-authored-by: Adrian Cole <adrian@tetrate.io>
This converges host-defined modules with Wasm defined modules by
introducing a custom section for host-defined functions. The net result
are far less types and consistent initialization.
* HostModule is removed for Module
* HostFunction is removed for Function
* ModuleContext is removed for Module
Note: One impact of this is that the low-level API no longer accepts a
go context (context.Context), rather a `wasm.Module` which the function
is called in context of. This meant exposing `wasm.Module.WithContext`
to override the default.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This makes sure that concurrent use of Instantiate and
ReleaseModuleInstance is goroutine-safe.
Note that it is still not safe to expose Release in the public API
as we haven't taken into account the outstanding calls. That
would be addressed in the follow-up commit.
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Co-authored-by: Adrian Cole <adrian@tetrate.io>
Allows users to do the following to enable sign-extension-ops:
```
r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfig().WithFeatureSignExtensionOps(true))
```
Resolves#66
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Co-authored-by: Adrian Cole <adrian@tetrate.io>
This flattens `FunctionInstance.FunctionType` into `Type` and `TypeID`
fields, where the former is known prior to instantiation. This helps
pave a way for integration between Wasm declared and host defined
modules.
This also clarifies that `FunctionInstance.String` was used as a lookup
key, by renaming and caching its impl. While at it, I renamed "null" to
"v" in its output as I had been using v for void noticing others were
doing that also. Moreover, null is easy to misunderstand as a bug.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This adds `RuntimeConfig.WithFeatureMutableGlobal(enabled bool)`, which
allows disabling of mutable globals. When disabled, any attempt to add a
mutable global, either explicitly or implicitly via decoding wasm will
fail.
To support this, there's a new `Features` bitflag that can allow up to
63 feature toggles without passing structs.
While here, I fixed a significant performance problem in decoding
binary:
Before
```
BenchmarkCodecExample/binary.DecodeModule-16 184243 5623 ns/op 3848 B/op 184 allocs/op
```
Now
```
BenchmarkCodecExample/binary.DecodeModule-16 294084 3520 ns/op 2176 B/op 91 allocs/op
```
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit refactors store.go and adds store.ReleaseModuleInstance.
Notably, this removes the "rollback" functions in InstantiateModule
which we had used to rollback the mutated state when we encounter
the initialization failure. Instead, we separate out the validation from
initialization and migrate most of the validation logics into module.go
As for ReleaseModuleInstance, that is necessary to complete #293,
will be leveraged to make it possible for store to be used for long-running
processes and environment.
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
MVP was a term used by WebAssembly insiders when ramping up to the 1.0
spec. While these folks still use that term it is confusing and
unnecessary way to qualify a W3C version. Here are some of the problems:
* MVP does not match a W3C published URL
* MVP does not match a git tag on the spec repo
* MVP was a work in progress, so there are text that say "not in MVP"
which ended up in 1.0 (as MVP became more than it was).
* MVP is jargon to people who don't know that stands for Minimum Viable Product.
This stops this practice and instead uses the W3C 1.0 Draft version
instead: 20191205
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit introduces virtualMachine type in each engine, which holds
the context for each engine.Call invocation. Previously, Engines were holding
value stacks and callframe stacks, and therefore it was impossible to execute
multiple functions in a single store concurrently as each of them were trying to
manipulate these stacks in a single engine (tied with each store).
As a side effect, we are no longer able to track "recursive call" and engines cannot
judge if the current invocation is the recursive call or not (previously it was possible
by tracking the current callframe stack's state before invoking any function).
Therefore, if the recursive call traps, now only the most recent store.Call returns error,
and the panic doesn't propagate and unwind all the way back to the original store.Call.
But this is in line with other runtimes behavior, so I hope this is fine. If the necessity comes up,
we will revisit this "how to propagate panic on recursive function call" problem.
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This adds a function to get an exported memory by name, allowing end
users to check ahead of time if memory writes might fail. This also
renames memory.len to size to as that's what the spec calls it.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This adds `StoreConfig.Context` to centralize assignment of the initial
context used implicitly by the WebAssembly 1.0 (MVP) start function and
also the WASI snapshot-01 "_start" exported function. This also
backfills tests and comments around propagation.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This adds this interface `wasm.Store` which gives access to functions in
a store without leaking an API to change the store. This is primarily to
support configuration use cases where post-initialization, there's no
need or desire to mutate the store. This also backfills codecs needed to
handle float results.
Signed-off-by: Adrian Cole <adrian@tetrate.io>
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>