Skip to content

ncruces/wasm2go

Repository files navigation

A Wasm to Go translator

The input is a Wasm module, and the output is a single Go source file, with no dependencies beyond the standard library.

To translate a Wasm module to Go use the following command:

wasm2go < input.wasm > output.go

The Go file forms a self contained package, that exports a structure called Module, and a New function to initialize it.

The methods of the Module structure are the Wasm module's exports, whereas imports are interfaces New consumes.

We assume the input Wasm modules can be trusted. At a minimum, you should run Wasm modules through a verifier before attempting to convert an untrusted module.

The current target is a useful subset of Wasm produced by clang, including the following features:

  • bulk memory instructions and reference types;
  • non-trapping float-to-int conversions;
  • sign-extension instructions;
  • multi-value results;
  • 64-bit address space;
  • extended constant expressions.

Generating human readable Go code is a non-goal:

  • Wasm names must be mangled into Go identifiers;
  • Wasm control flow is implemented with goto and labels;
  • Go's distinction between bool and int32 requires spurious control flow and type conversions;
  • Go's untyped numeric literals require explicit type conversions;
  • Go's constant evaluator does not match Wasm semantics, requiring workarounds to avoid constant folding/propagation;
  • float operations require type conversions to avoid being combined;
  • float literals can't represent negative zero, infinities, or NaN;
  • Go forbids unused variables/labels/etc.

Many of these introduce unnecessary verbosity, but they're necessary for semantic correctness.

Judge the output by the assembly generated by the Go compiler, not by how a human would read it.

For little endian CPUs, you can generate faster code by using unsafe. Despite the scary name, the generated code abides by the rules of unsafe, and all memory accesses are bounds checked.

To generate big/little endian Go code:

wasm2go -endian=big < input.wasm > output_big.go
wasm2go -endian=little < input.wasm > output_little.go

Both versions will be guarded by a build tag, so you can add both to your project.

The only other knob is whether to attempt to ensure float operations canonicalize NaNs. This is tested to work on both amd64 and arm64, but is known to be broken on most other architectures.

Usage of wasm2go:
  -endian string
        endianness of the generated code (little or big)
  -nanbox
        whether to try to canonicalize NaNs
  -nohead
        disable the header comment (including build tags)
  -nohost
        disable generating interfaces for imports

About

A Wasm to Go translator

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages