mirror of
https://github.com/hanzoai/wasm2go.git
synced 2026-10-30 15:06:48 +00:00
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]>
74 lines
3.3 KiB
Go
74 lines
3.3 KiB
Go
package emit
|
|
|
|
import "github.com/goccy/wasm2go/internal/wasm"
|
|
|
|
// Driver is the backend-neutral surface that an SSA-driven emitter
|
|
// needs from its host. It captures three categories of dependency:
|
|
//
|
|
// - Module access (Module): the parsed wasm being translated.
|
|
// - Identifier resolution (FieldName, FuncName, FuncRefName,
|
|
// HelperName, ImportMethodName): name spelling rules that depend
|
|
// on the host's packaging strategy (single-package vs
|
|
// multi-package + linkname-split) but not on the output language.
|
|
// - Side-effect registration (UseHelper, UsePackage): the emitter
|
|
// announces which runtime helpers and which Go imports the
|
|
// emitted body relies on so the host can stitch them into the
|
|
// final file's header.
|
|
//
|
|
// Two host implementations are anticipated:
|
|
//
|
|
// - The Go-source codegen translator implements Driver and adds
|
|
// Go-AST-returning sister methods (helperRef, funcRef, ...) that
|
|
// it consumes directly. Those AST methods stay outside Driver
|
|
// because their return types would couple the interface to
|
|
// go/ast and prevent an asm host from satisfying it.
|
|
// - A future asm host will implement Driver and pair it with
|
|
// asm-instruction-emitting sister methods to drive the plan9
|
|
// emitter.
|
|
//
|
|
// New Driver methods are added when they describe genuinely
|
|
// host-agnostic state. Anything language-specific stays on the
|
|
// concrete host type.
|
|
type Driver interface {
|
|
// Module returns the parsed wasm module being translated.
|
|
Module() *wasm.Module
|
|
// MultiPackage reports whether the host is producing the
|
|
// multi-package + linkname-split layout. Identifier-resolution
|
|
// methods consult this to decide between bare and chunk-qualified
|
|
// names; downstream emitters consult it when they need to know
|
|
// whether a cross-chunk hop is on the table.
|
|
MultiPackage() bool
|
|
|
|
// FieldName returns the *Module struct field name (multipkg-aware
|
|
// capitalization). Used wherever the emitter spells out an access
|
|
// like `m.<field>` so the field is reachable from outside the
|
|
// owning package in multi-package mode.
|
|
FieldName(s string) string
|
|
// FuncName returns the generated function's bare identifier for a
|
|
// wasm function index (capitalized in multipkg mode, lowercase
|
|
// otherwise).
|
|
FuncName(funcIdx uint32) string
|
|
// FuncRefName returns the identifier to use when *calling* the
|
|
// function from the current emit context. In multi-package mode
|
|
// this may register a //go:linkname forward so a bare name is
|
|
// safe even across chunks.
|
|
FuncRefName(funcIdx uint32) string
|
|
// HelperName returns the qualified identifier for a runtime
|
|
// helper as it should appear at the call site. In multi-package
|
|
// mode the base-package qualifier is added when the caller lives
|
|
// outside the base package.
|
|
HelperName(name string) string
|
|
// ImportMethodName returns the Go method name for an imported
|
|
// wasm function. Always capitalized so it is reachable from the
|
|
// caller package's host-imports interface.
|
|
ImportMethodName(imp wasm.Import) string
|
|
|
|
// UseHelper records that the named runtime helper is referenced
|
|
// by an emitted body so the host writes its definition into the
|
|
// output file.
|
|
UseHelper(name string)
|
|
// UsePackage records that the given Go import path is referenced
|
|
// so the host adds it to the file's import block.
|
|
UsePackage(pkg string)
|
|
}
|