|
| 1 | +# Release Wasm size optimization |
| 2 | + |
| 3 | +Release compilation runs a bounded size optimizer after emission. Debug and hot |
| 4 | +reload bypass all optimizer scans. The objective is a smaller complete Wasm |
| 5 | +file; execution-speed optimization remains the engine's job. Binaryen is not a |
| 6 | +compiler dependency. |
| 7 | + |
| 8 | +## Promoted pipeline |
| 9 | + |
| 10 | +Two cleanup sweeps simplify integer instructions, fold adjacent integer |
| 11 | +constants, remove unreachable code and unused locals, simplify structured |
| 12 | +control flow, and share identical return sequences. The second sweep handles |
| 13 | +patterns exposed by the first. Compilation does not iterate to a fixed point, |
| 14 | +and it stops early when a sweep does not shrink the module. |
| 15 | + |
| 16 | +Function sharing then merges bodies that differ only in integer constants. |
| 17 | +Each original function remains a small wrapper passing its constants to a |
| 18 | +shared helper. Existing indices, signatures, exports and references remain |
| 19 | +intact. Reports retain the original functions and add the shared helpers. |
| 20 | + |
| 21 | +Every cleanup body and complete module must shrink. Function-sharing costs |
| 22 | +include wrappers, helper bodies, new signatures/declarations, and section |
| 23 | +encoding overhead. The implementation preserves integer overflow and traps, |
| 24 | +does not fold floating-point operations, and retains effects of discarded |
| 25 | +values. Control-flow changes remap branch targets. Shared return sequences |
| 26 | +exclude bodies with non-defaultable locals; function sharing excludes tail |
| 27 | +calls and exception/continuation constructs. The tests cover those boundaries, |
| 28 | +GC/reference values, multi-value returns, large indices, and observable effects. |
| 29 | + |
| 30 | +## Selection from the experimental branch |
| 31 | + |
| 32 | +The promotion was measured against `master` at `1b65a40` and the full pipeline |
| 33 | +at `3ecf397` on `experiment/wasm-size-optimizations`. The experiment and its |
| 34 | +Binaryen comparisons remain on that branch. Commit `3f3c9be` records a |
| 35 | +reproducible comparison with individual passes disabled. |
| 36 | + |
| 37 | +| Real script | Previous master | Promoted Release | Saved | Reduction | |
| 38 | +| --- | ---: | ---: | ---: | ---: | |
| 39 | +| Minish Cap | 35,111 | 32,648 | 2,463 | 7.0% | |
| 40 | +| Lunistice | 31,413 | 29,163 | 2,250 | 7.2% | |
| 41 | +| Neon White | 5,073 | 4,768 | 305 | 6.0% | |
| 42 | +| A Hat in Time | 50,205 | 48,536 | 1,669 | 3.3% | |
| 43 | + |
| 44 | +The selected passes retain 98.2% / 98.4% of the full experimental savings on |
| 45 | +Minish Cap / Lunistice. Inlining and temporary sinking stay experimental: |
| 46 | +together, they save only another 46 / 36 bytes compared with this pipeline. |
| 47 | +Temporary sinking alone contributes 36 / 17 bytes in the two-sweep configuration. |
| 48 | +The remaining size benefit does not justify promoting that additional analysis |
| 49 | +and maintenance surface yet. The disabled propagation experiment also stays off |
| 50 | +master. Redundant null rewriting is omitted because direct emitter fixes already |
| 51 | +provide its savings in both profiles. |
| 52 | + |
| 53 | +Integer constant folding stays because it saves 113 bytes on Minish Cap and |
| 54 | +fits into the existing instruction scan. A second cleanup sweep saves another |
| 55 | +282 / 41 bytes on the primary scripts when measured with temporary sinking |
| 56 | +enabled; it exposes useful simplifications without introducing another analysis. |
| 57 | +Pass savings interact and must not be added together as independent totals. |
| 58 | + |
| 59 | +## Validation and reproduction |
| 60 | + |
| 61 | +The promoted pipeline has focused instruction/control/function-sharing tests |
| 62 | +and a source-level behavioral test covering evaluation order, GC values, |
| 63 | +fallible results and suspension. The real-script regression checks module |
| 64 | +validation, size reduction, unchanged original function indices and report |
| 65 | +metadata, and identical output with or without requesting a report. |
| 66 | + |
| 67 | +The opt-in nine-fixture corpus records complete module bytes and section payload |
| 68 | +sizes, validates baseline and optimized modules, and checks Debug equivalence. |
| 69 | +DWARF `.debug_info` variable entries already have nondeterministic ordering in |
| 70 | +unoptimized builds, so that comparison excludes only this section; executable |
| 71 | +code, names, source maps and line tables must match. A smaller deterministic |
| 72 | +fixture also checks the entire Debug binary byte for byte. No Debug emitter |
| 73 | +changes are part of this promotion. |
| 74 | + |
| 75 | +Validation passes: 494 library, 696 compiler, 20 binary and four example tests; |
| 76 | +Clippy; formatting/diff checks; and the browser compiler's wasm32 target check. |
| 77 | +All 176 maintained modules validate, all 211 runtime scenarios and the |
| 78 | +Debug/Release profile check pass. The size-corpus runner additionally passes |
| 79 | +36 behavioral invocations comparing unoptimized and promoted output. |
| 80 | + |
| 81 | +```powershell |
| 82 | +$env:CARGO_PROFILE_DEV_DEBUG = '0' |
| 83 | +$env:CARGO_PROFILE_TEST_DEBUG = '0' |
| 84 | +$env:CARGO_INCREMENTAL = '0' |
| 85 | +cargo test --lib --test compiler --bin splitc --bin splitls --examples --offline |
| 86 | +cargo clippy --all-targets --offline -- -D warnings |
| 87 | +cargo check -p splitscript-vscode-wasm --target wasm32-unknown-unknown --offline |
| 88 | +cargo test --lib write_size_corpus --offline -- --ignored --nocapture |
| 89 | +node scripts/wasm-size-runtime.mjs |
| 90 | +cargo test --profile max-opt --lib measure_optimization_overhead --offline -- --ignored --nocapture |
| 91 | +``` |
| 92 | + |
| 93 | +Artifacts stay under `target/size-check`. Timing uses seven warmed, alternating |
| 94 | +samples per mode, excludes parsing/type checking, and includes lowering and |
| 95 | +emission. The maintained runtime runner compares baseline and optimized output |
| 96 | +through the existing behavioral scenarios rather than checking bytes alone. |
0 commit comments