mirror of
https://github.com/hanzoai/wasm2go.git
synced 2026-10-30 15:06:48 +00:00
* 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]>
230 lines
15 KiB
WebAssembly Text Format
230 lines
15 KiB
WebAssembly Text Format
;; cg_dispatchif.wat - br_table dispatcher; each case has an if whose
|
|
;; branches each contain a br to the shared epilogue, exercising
|
|
;; compound-statement exit-goto inlining (replaceExitGotos) in dispatch_split.
|
|
(module
|
|
(global $out (mut i32) (i32.const 0))
|
|
(func (export "dispatch") (param $sel i32) (param $x i32)
|
|
(local $acc i32)
|
|
(block $epi
|
|
(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 35 (local.get $sel))
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 10)) (br $epi))
|
|
(else (global.set $out (i32.const 11)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 20)) (br $epi))
|
|
(else (global.set $out (i32.const 21)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 30)) (br $epi))
|
|
(else (global.set $out (i32.const 31)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 40)) (br $epi))
|
|
(else (global.set $out (i32.const 41)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 50)) (br $epi))
|
|
(else (global.set $out (i32.const 51)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 60)) (br $epi))
|
|
(else (global.set $out (i32.const 61)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 70)) (br $epi))
|
|
(else (global.set $out (i32.const 71)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 80)) (br $epi))
|
|
(else (global.set $out (i32.const 81)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 90)) (br $epi))
|
|
(else (global.set $out (i32.const 91)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 100)) (br $epi))
|
|
(else (global.set $out (i32.const 101)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 110)) (br $epi))
|
|
(else (global.set $out (i32.const 111)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 120)) (br $epi))
|
|
(else (global.set $out (i32.const 121)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 130)) (br $epi))
|
|
(else (global.set $out (i32.const 131)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 140)) (br $epi))
|
|
(else (global.set $out (i32.const 141)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 150)) (br $epi))
|
|
(else (global.set $out (i32.const 151)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 160)) (br $epi))
|
|
(else (global.set $out (i32.const 161)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 170)) (br $epi))
|
|
(else (global.set $out (i32.const 171)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 180)) (br $epi))
|
|
(else (global.set $out (i32.const 181)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 190)) (br $epi))
|
|
(else (global.set $out (i32.const 191)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 200)) (br $epi))
|
|
(else (global.set $out (i32.const 201)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 210)) (br $epi))
|
|
(else (global.set $out (i32.const 211)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 220)) (br $epi))
|
|
(else (global.set $out (i32.const 221)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 230)) (br $epi))
|
|
(else (global.set $out (i32.const 231)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 240)) (br $epi))
|
|
(else (global.set $out (i32.const 241)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 250)) (br $epi))
|
|
(else (global.set $out (i32.const 251)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 260)) (br $epi))
|
|
(else (global.set $out (i32.const 261)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 270)) (br $epi))
|
|
(else (global.set $out (i32.const 271)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 280)) (br $epi))
|
|
(else (global.set $out (i32.const 281)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 290)) (br $epi))
|
|
(else (global.set $out (i32.const 291)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 300)) (br $epi))
|
|
(else (global.set $out (i32.const 301)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 310)) (br $epi))
|
|
(else (global.set $out (i32.const 311)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 320)) (br $epi))
|
|
(else (global.set $out (i32.const 321)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 330)) (br $epi))
|
|
(else (global.set $out (i32.const 331)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 340)) (br $epi))
|
|
(else (global.set $out (i32.const 341)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 350)) (br $epi))
|
|
(else (global.set $out (i32.const 351)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(if (i32.gt_s (local.get $x) (i32.const 0))
|
|
(then (global.set $out (i32.const 360)) (br $epi))
|
|
(else (global.set $out (i32.const 361)) (br $epi)))
|
|
(br $epi)
|
|
)
|
|
(local.set $acc (i32.add (local.get $acc) (i32.const 1))))
|
|
(func (export "get_out") (result i32) (global.get $out))
|
|
)
|