watr
Version:
Light & fast WAT compiler – WebAssembly Text to binary, parse, print, transform
275 lines • 10.5 kB
TypeScript
/**
* Optimize AST.
*
* @param {Array|string} ast - AST or WAT source
* @param {boolean|string|Object} [opts=true] - Optimization options
* @returns {Array} Optimized AST
*
* @example
* optimize(ast) // all optimizations
* optimize(ast, 'treeshake') // only treeshake
* optimize(ast, { fold: true }) // explicit
*/
export default function optimize(ast: any[] | string, opts?: boolean | string | any): any[];
/**
* Recursively count AST nodes — fast size heuristic without compiling.
* @param {any} node
* @returns {number}
*/
export function count(node: any): number;
/**
* Compile AST and measure binary size in bytes.
* @param {Array} ast
* @returns {number}
*/
export function binarySize(ast: any[]): number;
/**
* Remove unused functions, globals, types, tables.
* Keeps exports and their transitive dependencies.
* @param {Array} ast
* @returns {Array}
*/
export function treeshake(ast: any[]): any[];
/**
* Fold constant expressions.
* @param {Array} ast
* @returns {Array}
*/
export function fold(ast: any[]): any[];
/**
* Remove dead code after control flow terminators.
* @param {Array} ast
* @returns {Array}
*/
export function deadcode(ast: any[]): any[];
/**
* Reuse locals of the same type to reduce total local count.
* Basic version: deduplicate unused locals.
* @param {Array} ast
* @returns {Array}
*/
export function localReuse(ast: any[]): any[];
/**
* Remove identity operations.
* @param {Array} ast
* @returns {Array}
*/
export function identity(ast: any[]): any[];
/**
* Strength reduction: replace expensive ops with cheaper equivalents.
* @param {Array} ast
* @returns {Array}
*/
export function strength(ast: any[]): any[];
/**
* Simplify branches with constant conditions.
* @param {Array} ast
* @returns {Array}
*/
export function branch(ast: any[]): any[];
export function propagate(ast: any): any;
/**
* Inline tiny functions (single expression, no locals, no params or simple params).
* @param {Array} ast
* @returns {Array}
*/
export function inline(ast: any[]): any[];
/**
* Inline functions that are called from exactly one place into their lone caller,
* then delete them. Unlike {@link inline} (which duplicates tiny stateless bodies),
* this never duplicates code and never inflates: each inlined function drops a
* function-section entry, a type-section entry (if now unused), and a `call`
* instruction, paying back only a `block`/`local.set` wrapper. This is what
* `wasm-opt -Oz` does — collapsing helper chains down to a couple of functions —
* and it's the bulk of the gap between hand-tuned WASM and naive codegen.
*
* A function `$f` qualifies when it is, all of:
* • named, with named params and locals (numeric indices can't be safely renamed);
* • referenced exactly once across the whole module, by a plain `call` (no
* `return_call`, `ref.func`, `elem`, `export`, or `start` reference, and not
* recursive);
* • single-result or void (a multi-value result can't be modeled as `(block (result …))`);
* • free of numeric (depth-relative) branch labels — those would shift under the
* extra block nesting — and of `return_call*` in its body.
*
* `(call $f a0 a1 …)` becomes
* (block $__inlN (result T)?
* (local.set $__inlN_p0 a0) (local.set $__inlN_p1 a1) … ;; args evaluated once, in order
* …body, params/locals renamed to $__inlN_*, `return X` → `br $__inlN X`…)
* and the renamed params+locals are appended to the caller's `local` decls; the
* body's own block/loop/if labels are renamed too so they can't shadow the caller's.
* Runs to a fixpoint so helper chains fully collapse.
*
* @param {Array} ast
* @returns {Array}
*/
export function inlineOnce(ast: any[]): any[];
/**
* Normalize options to a { passName: bool } map. An explicit object is kept
* as-is (preserving `log`/`verbose`), with any unmentioned pass filled to its
* default; `true` selects the defaults; a string selects only the named
* passes (or all of them via `'all'`).
*
* @param {boolean|string|Object} opts
* @returns {Object}
*/
export function normalize(opts: boolean | string | any): any;
/** Option name → default-on map — the public catalogue of passes. */
export const OPTS: any;
/**
* Remove no-op code: nops, drop of pure expressions, empty branches,
* and select with identical arms.
* @param {Array} ast
* @returns {Array}
*/
export function vacuum(ast: any[]): any[];
/**
* Apply peephole optimizations.
* @param {Array} ast
* @returns {Array}
*/
export function peephole(ast: any[]): any[];
/**
* Replace `global.get` of an immutable, const-initialised global with the
* constant — but only when it doesn't grow the module. A `global.get` costs
* ~2 B; an `i32.const 12345` costs 4 B; an `f64.const` costs 9 B. Naively
* inlining a big constant read from many sites trades a few cheap reads + one
* global decl for many fat immediates — pure bloat (and the node-count size
* guard can't see it: same number of AST nodes). So we only propagate a global
* when `refs·constSize ≤ refs·2 + declSize`; when every read is replaced and
* the global isn't exported, its now-dead decl is dropped here too.
* @param {Array} ast
* @returns {Array}
*/
export function globals(ast: any[]): any[];
/** Match (type.load/store (i32.add ptr (type.const N))) and fold offset */
export function offset(ast: any): any;
/**
* Remove br to a block's own label when it is the last instruction.
* @param {Array} ast
* @returns {Array}
*/
export function unbranch(ast: any[]): any[];
/**
* Collapse the `while`-emit idiom into a single loop.
*
* (block $A
* (loop $B
* (br_if $A (i32.eqz cond)) ;; exit when cond is false
* …body…
* (br $B) ;; continue
* ))
*
* becomes
*
* (loop $B
* (if cond (then …body… (br $B))))
*
* Saves ~3 B per while-loop (drop the outer block framing + the `i32.eqz`,
* trade `br_if`→`if`). Safe only when:
* - the block contains nothing but the loop (plus optional `type` slot),
* - block / loop are void (no result),
* - $A is never targeted from within body (only the head `br_if` uses it).
*
* @param {Array} ast
* @returns {Array}
*/
export function loopify(ast: any[]): any[];
/**
* Strip mutability from globals that are never written.
* Enables globals constant-propagation for more globals.
* @param {Array} ast
* @returns {Array}
*/
export function stripmut(ast: any[]): any[];
/**
* Simplify (if cond (then (br $label))) → (br_if $label cond)
* and (if cond (then) (else (br $label))) → (br_if $label (i32.eqz cond))
* Only when the br is the sole instruction in the arm.
* @param {Array} ast
* @returns {Array}
*/
export function brif(ast: any[]): any[];
/**
* Fold identical trailing code out of if/else arms.
* (if cond (then A X) (else B X)) → (if cond (then A) (else B)) X
* @param {Array} ast
* @returns {Array}
*/
export function foldarms(ast: any[]): any[];
/**
* Eliminate duplicate functions by hashing bodies.
* Keeps the first occurrence and redirects all references to it.
* @param {Array} ast
* @returns {Array}
*/
export function dedupe(ast: any[]): any[];
export function reorder(ast: any): any;
/**
* Merge structurally identical (type ...) definitions.
* Keeps the first occurrence and redirects all references.
* @param {Array} ast
* @returns {Array}
*/
export function dedupTypes(ast: any[]): any[];
/**
* Pack data segments: trim trailing zeros and merge adjacent constant-offset segments.
* @param {Array} ast
* @returns {Array}
*/
export function packData(ast: any[]): any[];
/**
* Minify import module and field names for smaller binaries.
* Only safe when you control the host environment.
* @param {Array} ast
* @returns {Array}
*/
export function minifyImports(ast: any[]): any[];
/**
* Unwrap redundant blocks whose label is never targeted. The block's stack
* effect is determined entirely by its body, so removing the `block`/`end`
* framing is sound as long as no `br` reaches into the block from inside.
*
* Three complementary patterns:
*
* 1. **Block at scope level** (sibling in `func`/`block`/`loop`/`then`/`else`):
* splice body into the parent scope. Works for untyped, `(result T)`-typed,
* or even `(param …)`-typed blocks — in all cases the body produces the
* same net stack effect as the framed block did, at the same position.
* 2. **Result-typed block in expression position** (`(block (result T) expr)`
* as the value of some operand): collapse to `expr` if the body is a
* single value expression. Catches the wrappers jz codegen leaves around
* arena allocations once `propagate` has folded the intermediate
* set/get pairs to a single call.
* 3. **Result-typed block as the sole operand of a void consumer** at scope:
* `(local.set $x (block (result T) stmt* expr))` → splice `stmt*` into
* the parent scope and rewrite the consumer to `(local.set $x expr)`.
* Same shape for `global.set` and `drop`. Cleans up the multi-stmt
* wrappers `inlineOnce` leaves when inlining helpers whose return value
* is fed into a single set/drop.
*
* Pattern 2 runs first (post-order) so patterns 1+3 see cleaned-up parents.
* @param {Array} ast
* @returns {Array}
*/
export function mergeBlocks(ast: any[]): any[];
/**
* Share local slots between same-type locals with non-overlapping live ranges.
* Live range = [first pos, last pos] of any local.get/set/tee, extended over
* any loop containing a reference (so a value read across loop iterations stays
* intact). Greedy slot assignment by start position. Params and unnamed/numeric
* references are left alone; `localReuse` later removes the renamed-away decls.
*
* Soundness: WASM zero-initializes locals at function entry, so a local whose
* first reference (in walk order) is a `local.get` *relies* on that implicit
* zero — coalescing it into a slot whose previous user left a non-zero residue
* would silently change behavior (e.g. a `for (let i=0; …)` loop counter
* inheriting `N*4` from a sibling temp). Such "read-first" locals can still
* serve as a slot's *primary* (the slot then keeps the function's zero start),
* but can never be a donor merged into an existing slot.
* @param {Array} ast
* @returns {Array}
*/
export function coalesceLocals(ast: any[]): any[];
export { count as size };
//# sourceMappingURL=optimize.d.ts.map