sigildocs

Direct WASM optimization levels

Direct WASM uses the existing SIGIL_OPT_LEVEL / build configuration opt-level setting, which is part of compile identity. The accepted values are exactly 0, 1 and 2; absent or invalid environment values select O0. Release build configurations already select O2 and keep that default. Higher configuration optimization settings are clamped to O2 by the build system.

These are separate resolution layers. Direct compiler invocation with no SIGIL_OPT_LEVEL, or with SIGIL_OPT_LEVEL=3, keeps O0 and its existing output. The environment parser never clamps. A build config with optimize: 3 clamps to O2, and a release config omitting optimize: defaults to O2; the build then pins SIGIL_OPT_LEVEL=2 for compilation. This preserves existing cache identity and direct compiler defaults.

The implemented MIR and emitter additions are:

LevelAdditional transformations
O0None; preserve the existing lowering and emission.
O1Scalar block-argument copy propagation; identical-edge CFG cleanup; adjacent SSA-local set/get fusion into local.tee; identical-edge branch peephole.
O2O1 plus small straight-line chain dispatcher removal and physical local reuse.

The existing large-chain path remains available at every level. Small chains must pass the same proof: follow the entry through every block exactly once, with unconditional edges and a return or tail call at the end. Sparse block identities and physical enumeration order do not determine execution order. Branches, loops and unvisited blocks refuse the chain path.

Local reuse changes only the WASM local backing an SSA value. MIR value IDs, root-slot IDs and continuation capture identities remain unchanged. Lifetimes include explicit operands, root sets, closure procedures, remainder captures and terminator roots. A local is released after the final instruction using it; an emitter may read an operand again after writing its result. Direct or closure calls without an explicit safepoint retain distinct locals because their conservative root publication can include otherwise dead SSA names. Newly eligible small chains containing a call with more than eight arguments also retain distinct locals. The checked spill corridor proves the consumed result's root slot from its SSA local identity, so this protocol requires an identity local map. Dispatcher removal remains available for these chains.

The tee peephole requires exact byte adjacency and matching physical locals. Any intervening instruction, including root stores, calls, checkpoints and control boundaries, prevents fusion. Branch folding requires equal targets and equal parallel-copy arguments on both edges. The condition producer is still emitted.

Scalar copy propagation requires a reachable, non-entry block with one incoming unconditional edge from a dominating predecessor. Neither block may contain a call or safepoint. Only scalar parameters used within that block can be removed; boxed values, root entries, closure captures, remainder captures, continuation bodies and remainder bodies are excluded. A deterministic alias map rewrites uses and removes matching edge arguments. Changed functions compact their value IDs, composing the original CPS identity map used by continuation proofs.

MIR CFG cleanup folds only conditional edges with identical targets and arguments. It retains all condition-producing instructions. Both MIR passes run after scalar representation lowering and before final compaction, remainder-reuse checks, frame metadata and safepoint liveness; the resulting MIR is validated. The emitter branch peephole also handles callers emitting pre-built MIR directly.