Files
9d2559e75d asmgen: SSA-based dual-arch asm emit (amd64 + arm64) (#7)
Replace the legacy direct-opcode compiler with an SSA pipeline that
emits per-function amd64.s and arm64.s for every wasm function. A
pure-Go body is also emitted (build-constrained on other GOOS/GOARCH)
and shares the same New() / exported-method surface, so callers never
see the path distinction.

Asm pipeline
- SSA always on; unsupported wasm features are now hard errors.
  Translate now drives Parse -> SSA build -> passes (Simplify, DCE,
  CSE) -> arch-specific emit.
- Per-arch block-local linear-scan regalloc with slot reuse and
  m-pointer caching (the *Module pointer is pinned in a reserved
  register across the function body — R11 on amd64, R4 on arm64 —
  so per-access reloads from the FP frame are gone).
- Cross-block stack allocation via interference-graph slot sharing
  reduces the typical per-function frame to 8-16 bytes.
- Peephole + branch fusion: drop dead spills, elide empty else
  fall-through, fold constant displacement into indexed load/store,
  fuse AND+NE+BlockIf into BTL+JCC.
- A new cross-block linear-scan allocator with Belady eviction
  lives in internal/regalloc/ and is gated behind
  WASM2GO_NEWREGALLOC for measurement.

Output shape
- Bulk-dispatch exports: when -bulk-export-prefix matches at least
  one export, each <prefix><svc>_<mt> becomes a standalone
  Inv_<svc>_<mt> function with inlined trap-recovery (defer +
  recover + mutable-global snapshot/restore). The Go linker drops
  whichever Inv_* the consumer never calls.
- Cross-package asm CALL: when every byte of an import path maps to
  a plan-9 identifier rune ("/" -> U+2215, "." -> U+00B7), the CALL
  target emits the direct cross-package symbol; otherwise asm falls
  back to a per-chunk Go-body trampoline.
- Multi-package layout for large modules: bodies split across
  p0..p<N> with go:linkname bridges in alias.go and per-chunk
  amd64.s / arm64.s sidecars. Single-file mode remains supported
  for modules under the internal multi-file threshold.

Coverage and tests
- internal/asmgen, internal/codegen, internal/regalloc, internal/ssa,
  internal/wasm pass with -race. Total cross-package statement
  coverage 85.8%, above the enforced 85% threshold.
- A new TestRecoverTrapsAreSurfacedAsErrors end-to-end test
  (testdata/cg_recover.wat) pins the Inv_<svc>_<mt> trap-recovery
  contract: wasm traps surface as errors (no Go panic), mutable
  globals restore to wrapper-entry values, and 5000 trap+recover
  cycles leak no goroutines and keep the Go heap delta under 1 MiB
  after a forced GC.

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
2026-06-10 10:54:22 +09:00

27 lines
905 B
WebAssembly Text Format

;; hello.wat — minimal wasi_snapshot_preview1 fd_write smoke test.
;;
;; Exports `say_hello`: writes "hello from wasi\n" to fd 1 (stdout).
;; Used by wasm2go to verify the native wasip1 codegen.
(module
(import "wasi_snapshot_preview1" "fd_write"
(func $fd_write (param i32 i32 i32 i32) (result i32)))
(memory (export "memory") 1)
;; "hello from wasi\n" — 16 bytes — at memory offset 16.
(data (i32.const 16) "hello from wasi\n")
;; iovec at offset 0:
;; [0..4) = buf pointer = 16
;; [4..8) = buf length = 16
(data (i32.const 0) "\10\00\00\00") ;; little-endian 16
(data (i32.const 4) "\10\00\00\00") ;; little-endian 16
(func (export "say_hello") (result i32)
(call $fd_write
(i32.const 1) ;; fd = 1 (stdout)
(i32.const 0) ;; iovs pointer
(i32.const 1) ;; iovs length (1 entry)
(i32.const 32))) ;; nwritten output pointer
)