Files
wasm2go/testdata/cg_bigdispatch.wat
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

177 lines
9.8 KiB
WebAssembly Text Format

;; cg_bigdispatch.wat - large br_table dispatcher (void result) to trigger
;; dispatch_split.go. n nested blocks; br_table picks one; each end stores a
;; distinct value into a global, then returns. Void result so the dispatcher
;; split heuristic (which only fires on result-less funcs) can engage.
(module
(global $out (mut i32) (i32.const 0))
(func (export "dispatch") (param $sel i32)
(local $acc i32)
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(block
(br_table 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 (local.get $sel))
)
(global.set $out (i32.const 100))
(return)
)
(global.set $out (i32.const 200))
(return)
)
(global.set $out (i32.const 300))
(return)
)
(global.set $out (i32.const 400))
(return)
)
(global.set $out (i32.const 500))
(return)
)
(global.set $out (i32.const 600))
(return)
)
(global.set $out (i32.const 700))
(return)
)
(global.set $out (i32.const 800))
(return)
)
(global.set $out (i32.const 900))
(return)
)
(global.set $out (i32.const 1000))
(return)
)
(global.set $out (i32.const 1100))
(return)
)
(global.set $out (i32.const 1200))
(return)
)
(global.set $out (i32.const 1300))
(return)
)
(global.set $out (i32.const 1400))
(return)
)
(global.set $out (i32.const 1500))
(return)
)
(global.set $out (i32.const 1600))
(return)
)
(global.set $out (i32.const 1700))
(return)
)
(global.set $out (i32.const 1800))
(return)
)
(global.set $out (i32.const 1900))
(return)
)
(global.set $out (i32.const 2000))
(return)
)
(global.set $out (i32.const 2100))
(return)
)
(global.set $out (i32.const 2200))
(return)
)
(global.set $out (i32.const 2300))
(return)
)
(global.set $out (i32.const 2400))
(return)
)
(global.set $out (i32.const 2500))
(return)
)
(global.set $out (i32.const 2600))
(return)
)
(global.set $out (i32.const 2700))
(return)
)
(global.set $out (i32.const 2800))
(return)
)
(global.set $out (i32.const 2900))
(return)
)
(global.set $out (i32.const 3000))
(return)
)
(global.set $out (i32.const 3100))
(return)
)
(global.set $out (i32.const 3200))
(return)
)
(global.set $out (i32.const 3300))
(return)
)
(global.set $out (i32.const 3400))
(return)
)
(global.set $out (i32.const 3500))
(return)
)
(global.set $out (i32.const 3600))
(return)
)
(global.set $out (i32.const 3700))
(return)
)
(global.set $out (i32.const 3800))
(return)
)
(global.set $out (i32.const 3900))
(return)
)
(global.set $out (i32.const 4000))
(return)
)
(global.set $out (i32.const 4100))
(return)
)
(func (export "get_out") (result i32) (global.get $out))
)