Files
bd85549918 wasm2go: a WebAssembly-to-Go ahead-of-time transpiler (#1)
* wasm2go: a WebAssembly-to-Go ahead-of-time transpiler

wasm2go translates a WebAssembly binary into standalone Go source. The
generated package runs the module natively — no interpreter and no
embedded wasm runtime — so it builds and links like ordinary Go code.
This makes it practical to ship a wasm-compiled library (for example a
large C/C++ codebase built with the WASI SDK) inside a Go program
without the startup and memory cost of a wasm engine.

Components:

- internal/wasm — the WebAssembly binary parser.
- internal/codegen — translates a parsed module into Go source through
  the SSA lowering + optimization + emission pipeline.
- internal/ssa — a lightweight SSA IR plus optimization passes
  (const-propagation, DCE, CSE, branch folding, memory promotion).
- transpile — the public library API: Parse, Translate, and the
  Transpile one-shot convenience wrapper.
- cmd/wasm2go — the command-line interface.
- internal/testfixture — shared helper that compiles testdata/*.wat
  to wasm on demand via wat2wasm.

Library API (Options):

- Required: Package, OutputImportPath.
- Optional: BulkExportPrefix (per-export Inv_<svc>_<mt> dispatch for
  modules with uniformly-named exports), EntryExports (narrow DCE
  root set), KeepDeadFuncs (disable DCE for diff/debug),
  PromotionReportPath (write SSA memory-promotion JSON).

Everything else is auto-derived from the input: the SSA pipeline runs
unconditionally; data segments emit as //go:embed sidecars; wasi
imports translate to Go-native syscalls; modules whose total
function-body bytes exceed an internal threshold are emitted as a
chain of //go:linkname-split sub-packages so the Go compiler stays
inside a bounded per-package memory budget.

WASI runtime: full wasi_snapshot_preview1 coverage. Args/environ,
clocks, fd_*, path_* (open/create_directory/unlink_file/
remove_directory/rename/filestat_get/filestat_set_times/link/
symlink/readlink), readdir, pread/pwrite, sync/datasync, poll_oneoff
(clock + fd-event branches), sched_yield, random_get, proc_exit,
proc_raise, sock_accept/recv/send/shutdown — every call routes to the
Go stdlib or golang.org/x/sys/unix equivalent. Per-platform companion
files (unix/linux/darwin/windows) carry the bits that differ across
OSes; the translated module is a normal Go program with the host's
ordinary filesystem and network reach.

Release engineering for the public repository:

- GitHub Actions CI: build, vet, race-enabled tests behind an 85%
  coverage gate, golangci-lint — all driven through make targets.
- Release workflow runs GoReleaser on version tags, including
  deb/rpm/apk packages, with build-provenance attestations recorded
  for every release asset.
- A release-test workflow runs `make release/check` (GoReleaser
  config validation + a publish-free dry-run build) on every PR and
  main push so a broken release is caught before tag time.
- golangci-lint and goreleaser are pinned in tools/go.mod and run
  via `go tool`, so local and CI invocations use the same versions.
- `make install/wat2wasm` provisions the wat2wasm CLI the test suite
  needs, dispatching to the host's package manager (Homebrew on
  macOS, apt/dnf/pacman/apk on Linux).
- Makefile targets for local development.

Test coverage is 85.2% of statements overall.

* README: document the WASI runtime, performance, and library entry points

- Add a "WASI support" subsection explaining the single-file,
  stdlib-only `wasi_snapshot_preview1` implementation and the
  resulting cross-platform reach.
- Add a "Performance" section with the wazero-vs-wasm2go corpus
  numbers (cold start 8.4×, short queries up to 81×, TPC-H scans
  within ±10%).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* ci: align release-test with release.yml — use stable Go

The release-test workflow exercises the same `make release/check`
dry-run that the publish job runs at tag time, so it should pull
the same toolchain. Switch it from `go-version-file: tools/go.mod`
to `go-version: stable` to match release.yml.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-24 13:34:54 +09:00

73 lines
3.2 KiB
WebAssembly Text Format

;; cg_numerics.wat — exercises a broad spread of numeric opcodes the legacy
;; fncompiler must handle: i64/f32/f64 constants, select, popcnt/clz/ctz,
;; rotate, the full comparison family, and int/float conversions. Used to
;; light up decoder.go (readS64/readF32/readF64) and fncompiler helpers.
(module
(func (export "i64const") (result i64)
(i64.const -1234567890123456789))
(func (export "f32const") (result f32)
(f32.const 3.14159))
(func (export "f64const") (result f64)
(f64.const 2.718281828459045))
(func (export "sel") (param i32 i32 i32) (result i32)
(select (local.get 0) (local.get 1) (local.get 2)))
(func (export "popcnt") (param i32) (result i32)
(i32.popcnt (local.get 0)))
(func (export "clz") (param i32) (result i32)
(i32.clz (local.get 0)))
(func (export "ctz") (param i32) (result i32)
(i32.ctz (local.get 0)))
(func (export "popcnt64") (param i64) (result i64)
(i64.popcnt (local.get 0)))
(func (export "rotl") (param i32 i32) (result i32)
(i32.rotl (local.get 0) (local.get 1)))
(func (export "rotr") (param i32 i32) (result i32)
(i32.rotr (local.get 0) (local.get 1)))
(func (export "eqz") (param i32) (result i32)
(i32.eqz (local.get 0)))
(func (export "cmp_eq") (param i32 i32) (result i32)
(i32.eq (local.get 0) (local.get 1)))
(func (export "cmp_ne") (param i32 i32) (result i32)
(i32.ne (local.get 0) (local.get 1)))
(func (export "cmp_le_s") (param i32 i32) (result i32)
(i32.le_s (local.get 0) (local.get 1)))
(func (export "cmp_ge_u") (param i32 i32) (result i32)
(i32.ge_u (local.get 0) (local.get 1)))
(func (export "fadd") (param f64 f64) (result f64)
(f64.add (local.get 0) (local.get 1)))
(func (export "fmul") (param f32 f32) (result f32)
(f32.mul (local.get 0) (local.get 1)))
(func (export "fmin") (param f64 f64) (result f64)
(f64.min (local.get 0) (local.get 1)))
(func (export "fmax") (param f64 f64) (result f64)
(f64.max (local.get 0) (local.get 1)))
(func (export "fabs") (param f64) (result f64)
(f64.abs (local.get 0)))
(func (export "fsqrt") (param f64) (result f64)
(f64.sqrt (local.get 0)))
(func (export "fneg") (param f32) (result f32)
(f32.neg (local.get 0)))
(func (export "fceil") (param f64) (result f64)
(f64.ceil (local.get 0)))
(func (export "ffloor") (param f64) (result f64)
(f64.floor (local.get 0)))
(func (export "i32_to_f64") (param i32) (result f64)
(f64.convert_i32_s (local.get 0)))
(func (export "f64_to_i32") (param f64) (result i32)
(i32.trunc_f64_s (local.get 0)))
(func (export "i32_to_i64_u") (param i32) (result i64)
(i64.extend_i32_u (local.get 0)))
(func (export "i64_to_i32") (param i64) (result i32)
(i32.wrap_i64 (local.get 0)))
(func (export "f32_to_f64") (param f32) (result f64)
(f64.promote_f32 (local.get 0)))
(func (export "f64_to_f32") (param f64) (result f32)
(f32.demote_f64 (local.get 0)))
(func (export "fcmp_lt") (param f64 f64) (result i32)
(f64.lt (local.get 0) (local.get 1)))
(func (export "i64_shifts") (param i64 i64) (result i64)
(i64.shr_u (i64.shl (local.get 0) (local.get 1)) (local.get 1)))
(func (export "reinterp") (param f32) (result i32)
(i32.reinterpret_f32 (local.get 0)))
)