UNPKG

watr

Version:

Light & fast WAT compiler – WebAssembly Text to binary, parse, print, transform

275 lines 10.5 kB
/** * 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