Documentation
¶
Overview ¶
Package emit holds the backend-neutral analyses that an SSA-driven code emitter needs: usage counting, hoisting decisions, phi staging, and the trivial SSA-only predicates those decisions rely on.
Anything Go-syntax specific (Go-AST construction, identifier names, type spellings) stays in internal/codegen. Per-architecture assembly generation is the internal/gcasm backend (which captures gc's `-S` output and transforms it). emit is the analysis layer they share.
Index ¶
- func CollectHoistedRefs(v *ssa.Value, hoist map[ssa.ValueID]bool, out map[ssa.ValueID]bool)
- func ComputeHoist(f *ssa.Func, usage map[ssa.ValueID]int) map[ssa.ValueID]bool
- func ComputeStagedPhis(f *ssa.Func, hoist map[ssa.ValueID]bool) map[ssa.ValueID]bool
- func ComputeValueUsage(f *ssa.Func) map[ssa.ValueID]int
- func IsLoadOp(op ssa.Op) bool
- func IsNarrowingStore(v *ssa.Value) bool
- func IsScalarType(t ssa.Type) bool
- func IsVoidAtomicStore(v *ssa.Value) bool
- type Driver
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CollectHoistedRefs ¶
CollectHoistedRefs walks v and gathers the IDs of every hoisted value the emitted expression for v would reference. A hoisted value is emitted by name, so recursion stops there; a non-hoisted value is inlined, so its operands are walked.
func ComputeHoist ¶
ComputeHoist decides which values need a hoisted local. A value is hoisted when:
- it is a phi (assigned on predecessor edges, read elsewhere);
- it is a side-effecting value that yields a usable scalar (a call returning a value) — must be emitted once, not re-inlined;
- it is a memory load — the IR has no Mem token, so a load relies on statement order; inlining a single-use load could float it past an intervening store;
- it is referenced two or more times.
Params are never hoisted (they are bound to the parameter names / argument slots provided by the calling convention).
Additionally, the value-side argument of every narrowing store is force-hoisted. The Go emitter renders narrowing stores as `*(*uint8)(...) = uint8(vN)` and the truncating cast must be a runtime conversion — an inlined constant operand would trip Go's compile-time constant-overflow check. Force-hoisting args[1] guarantees the operand is always a typed local variable. The asm emitter does not face the constant-overflow constraint, but hoisting these values is harmless there (and keeps the two backends' hoist sets identical, which simplifies golden tests).
func ComputeStagedPhis ¶
ComputeStagedPhis returns the phis whose edge-copies must go through a parallel-copy staging temp. A phi P needs staging iff, on some incoming edge, the right-hand side of P (or of a sibling phi assigned on the same edge) reads a phi that is itself a target on that edge — the loop-back-edge swap hazard. if/else merges, where the only target on an edge is the merge phi itself and the RHS comes from outside, never trip this and get a plain direct copy.
func ComputeValueUsage ¶
ComputeValueUsage returns a map ValueID → number of times the value is referenced as an argument (across all blocks) plus once per Block Control that references it. Used to decide when to hoist a value into a local variable vs inline it at the use site.
func IsLoadOp ¶
IsLoadOp reports whether op is a memory-or-global read that must be preserved in statement order. Loads are not flagged HasSideEffect (they don't write state) but must still be ordered relative to stores/calls; ComputeHoist force-hoists them so they emit as in-order statements.
Note: a similar predicate exists in internal/ssa for the memory-classification pass. That one tracks only true linear-memory loads (no OpGlobalGet) because globals don't participate in the memory model it analyzes. This predicate, used by the hoist decision, must include OpGlobalGet because globals are observable across calls and the emitter relies on statement-order semantics to keep them sound.
func IsNarrowingStore ¶
IsNarrowingStore reports whether v is a memory store that writes strictly fewer bits than its value argument carries. Such stores need their value operand hoisted into a typed local so a backend that lowers the narrowing as a typed cast (e.g. the Go backend's `uint8(vN)`) sees a runtime conversion rather than a literal that would trigger compile-time overflow checks.
func IsScalarType ¶
IsScalarType reports whether t is an emittable scalar value type (a real Go value, not the Mem state token, Tuple, or Invalid).
func IsVoidAtomicStore ¶ added in v0.4.9
IsVoidAtomicStore reports whether v is an OpAtomicCall atomic store. The lowering never pushes a store's result (wasm atomic stores leave nothing on the operand stack), so the value only ever appears in statement position. It must NOT be hoisted: the Go emitter renders the full-width forms as sync/atomic Store intrinsics, which yield no value, so a hoisted `vN = <store>` would not compile. (The remaining sub-word store helpers still return a dummy scalar; discarding it in an expression statement is fine.)
Types ¶
type Driver ¶
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)
}
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.