watr 4.3.4 → 4.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/watr.js CHANGED
@@ -1278,7 +1278,7 @@ function compile(nodes) {
1278
1278
  const bin = (kind, count = true) => {
1279
1279
  const items = ctx[kind].filter(Boolean).map((item) => build[kind](item, ctx)).filter(Boolean);
1280
1280
  if (kind === SECTION.custom) return items.flatMap((content) => [kind, ...vec(content)]);
1281
- return !items.length ? [] : [kind, ...vec(count ? vec(items) : items)];
1281
+ return !items.length ? [] : [kind, ...vec(count ? vec(items) : items.flat())];
1282
1282
  };
1283
1283
  const binMeta = () => {
1284
1284
  const sections = [];
@@ -1529,7 +1529,7 @@ var build = [
1529
1529
  // (elem (table idx)? (offset expr)|(expr) elem*) - active
1530
1530
  // ref: https://webassembly.github.io/spec/core/binary/modules.html#element-section
1531
1531
  (parts, ctx) => {
1532
- let passive = 0, declare = 0, elexpr = 0, nofunc = 0, tabidx, offset, rt;
1532
+ let passive = 0, declare = 0, elexpr = 0, nofunc = 0, tabidx, offset2, rt;
1533
1533
  if (parts[0] === "declare") parts.shift(), declare = 1;
1534
1534
  if (parts[0]?.[0] === "table") {
1535
1535
  [, tabidx] = parts.shift();
@@ -1538,9 +1538,9 @@ var build = [
1538
1538
  tabidx = id(parts.shift(), ctx.table);
1539
1539
  }
1540
1540
  if (parts[0]?.[0] === "offset" || Array.isArray(parts[0]) && parts[0][0] !== "item" && !parts[0][0].startsWith("ref")) {
1541
- offset = parts.shift();
1542
- if (offset[0] === "offset") [, offset] = offset;
1543
- offset = expr(offset, ctx);
1541
+ offset2 = parts.shift();
1542
+ if (offset2[0] === "offset") [, offset2] = offset2;
1543
+ offset2 = expr(offset2, ctx);
1544
1544
  } else if (!declare) passive = 1;
1545
1545
  if (TYPE[parts[0]] || parts[0]?.[0] === "ref") rt = reftype(parts.shift(), ctx);
1546
1546
  else if (parts[0] === "func") rt = [TYPE[parts.shift()]];
@@ -1556,19 +1556,19 @@ var build = [
1556
1556
  return [
1557
1557
  mode,
1558
1558
  ...// 0b000 e:expr y*:vec(funcidx) | type=(ref func), init ((ref.func y)end)*, active (table=0,offset=e)
1559
- mode === 0 ? offset : (
1559
+ mode === 0 ? offset2 : (
1560
1560
  // 0b001 et:elkind y*:vec(funcidx) | type=0x00, init ((ref.func y)end)*, passive
1561
1561
  mode === 1 ? [0] : (
1562
1562
  // 0b010 x:tabidx e:expr et:elkind y*:vec(funcidx) | type=0x00, init ((ref.func y)end)*, active (table=x,offset=e)
1563
- mode === 2 ? [...uleb(tabidx || 0), ...offset, 0] : (
1563
+ mode === 2 ? [...uleb(tabidx || 0), ...offset2, 0] : (
1564
1564
  // 0b011 et:elkind y*:vec(funcidx) | type=0x00, init ((ref.func y)end)*, passive declare
1565
1565
  mode === 3 ? [0] : (
1566
1566
  // 0b100 e:expr el*:vec(expr) | type=(ref null func), init el*, active (table=0, offset=e)
1567
- mode === 4 ? offset : (
1567
+ mode === 4 ? offset2 : (
1568
1568
  // 0b101 et:reftype el*:vec(expr) | type=et, init el*, passive
1569
1569
  mode === 5 ? rt : (
1570
1570
  // 0b110 x:tabidx e:expr et:reftype el*:vec(expr) | type=et, init el*, active (table=x, offset=e)
1571
- mode === 6 ? [...uleb(tabidx || 0), ...offset, ...rt] : (
1571
+ mode === 6 ? [...uleb(tabidx || 0), ...offset2, ...rt] : (
1572
1572
  // 0b111 et:reftype el*:vec(expr) | type=et, init el*, passive declare
1573
1573
  rt
1574
1574
  )
@@ -1623,7 +1623,7 @@ var build = [
1623
1623
  // (data (global.get $x) "\aa" "\bb"?)
1624
1624
  // (data (i8 1 2 3) ...) numeric values (WAT numeric values, Phase 2)
1625
1625
  (inits, ctx) => {
1626
- let offset, memidx = 0;
1626
+ let offset2, memidx = 0;
1627
1627
  if (inits[0]?.[0] === "memory") {
1628
1628
  [, memidx] = inits.shift();
1629
1629
  memidx = id(memidx, ctx.memory);
@@ -1631,15 +1631,15 @@ var build = [
1631
1631
  memidx = id(inits.shift(), ctx.memory);
1632
1632
  }
1633
1633
  if (Array.isArray(inits[0]) && typeof inits[0]?.[0] === "string") {
1634
- offset = inits.shift();
1635
- if (offset[0] === "offset") [, offset] = offset;
1636
- offset ?? err("Bad offset", offset);
1634
+ offset2 = inits.shift();
1635
+ if (offset2[0] === "offset") [, offset2] = offset2;
1636
+ offset2 ?? err("Bad offset", offset2);
1637
1637
  }
1638
1638
  return [
1639
1639
  ...// active: 2, x=memidx, e=expr
1640
- memidx ? [2, ...uleb(memidx), ...expr(offset, ctx)] : (
1640
+ memidx ? [2, ...uleb(memidx), ...expr(offset2, ctx)] : (
1641
1641
  // active: 0, e=expr
1642
- offset ? [0, ...expr(offset, ctx)] : (
1642
+ offset2 ? [0, ...expr(offset2, ctx)] : (
1643
1643
  // passive: 1
1644
1644
  [1]
1645
1645
  )
@@ -1854,12 +1854,12 @@ var expr = (node, ctx) => instr(normalize([node], ctx), ctx);
1854
1854
  var id = (nm, list, n) => (n = isId(nm) ? list[nm] : +nm, n in list ? n : err(`Unknown ${list.name} ${nm}`));
1855
1855
  var blockid = (nm, block, i) => (i = isId(nm) ? block.length - block[nm] : +nm, isNaN(i) || i > block.length ? err(`Bad label ${nm}`) : i);
1856
1856
  var memarg = (args) => {
1857
- let align2, offset, k, v;
1858
- while (isMemParam(args[0])) [k, v] = args.shift().split("="), k === "offset" ? offset = +v : k === "align" ? align2 = +v : err(`Unknown param ${k}=${v}`);
1859
- if (offset < 0 || offset > 4294967295) err(`Bad offset ${offset}`);
1857
+ let align2, offset2, k, v;
1858
+ while (isMemParam(args[0])) [k, v] = args.shift().split("="), k === "offset" ? offset2 = +v : k === "align" ? align2 = +v : err(`Unknown param ${k}=${v}`);
1859
+ if (offset2 < 0 || offset2 > 4294967295) err(`Bad offset ${offset2}`);
1860
1860
  if (align2 <= 0 || align2 > 4294967295) err(`Bad align ${align2}`);
1861
1861
  if (align2) (align2 = Math.log2(align2)) % 1 && err(`Bad align ${align2}`);
1862
- return [align2, offset];
1862
+ return [align2, offset2];
1863
1863
  };
1864
1864
  var memargEnc = (nodes, op, memIdx = 0) => {
1865
1865
  const [a, o] = memarg(nodes), alignVal = (a ?? align(op)) | (memIdx && 64);
@@ -2345,7 +2345,7 @@ var i31ref = (ast, ctx) => {
2345
2345
  };
2346
2346
  transforms.i31ref = i31ref;
2347
2347
  var extended_const = (ast, ctx) => {
2348
- const globals = {};
2348
+ const globals2 = {};
2349
2349
  walk(ast, (node) => {
2350
2350
  if (!Array.isArray(node) || node[0] !== "global") return;
2351
2351
  const id2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
@@ -2354,7 +2354,7 @@ var extended_const = (ast, ctx) => {
2354
2354
  const init = node[i];
2355
2355
  if (!Array.isArray(init)) continue;
2356
2356
  if (init[0] === "i32.const" || init[0] === "i64.const" || init[0] === "f32.const" || init[0] === "f64.const") {
2357
- globals[id2] = { type: init[0].split(".")[0], value: init[1] };
2357
+ globals2[id2] = { type: init[0].split(".")[0], value: init[1] };
2358
2358
  break;
2359
2359
  }
2360
2360
  }
@@ -2362,8 +2362,8 @@ var extended_const = (ast, ctx) => {
2362
2362
  const evalConst = (node) => {
2363
2363
  if (!Array.isArray(node)) return node;
2364
2364
  const op = node[0];
2365
- if (op === "global.get" && globals[node[1]]) {
2366
- const g = globals[node[1]];
2365
+ if (op === "global.get" && globals2[node[1]]) {
2366
+ const g = globals2[node[1]];
2367
2367
  return [`${g.type}.const`, g.value];
2368
2368
  }
2369
2369
  if (op === "i32.add" || op === "i64.add") {
@@ -2516,11 +2516,11 @@ var gc = (ast, ctx) => {
2516
2516
  return size;
2517
2517
  };
2518
2518
  const fieldOffset = (typeDef, fieldIdx) => {
2519
- let offset = 4;
2519
+ let offset2 = 4;
2520
2520
  for (let i = 0; i < fieldIdx; i++) {
2521
- offset += TYPE_SIZES[typeDef.fields[i].type] || 4;
2521
+ offset2 += TYPE_SIZES[typeDef.fields[i].type] || 4;
2522
2522
  }
2523
- return offset;
2523
+ return offset2;
2524
2524
  };
2525
2525
  const findFieldIdx = (typeDef, fieldName) => {
2526
2526
  for (let i = 0; i < typeDef.fields.length; i++) {
@@ -2582,17 +2582,17 @@ var gc = (ast, ctx) => {
2582
2582
  if (node[0] === "struct.new") {
2583
2583
  for (let i = 0; i < typeDef.fields.length; i++) {
2584
2584
  const f = typeDef.fields[i];
2585
- const offset = fieldOffset(typeDef, i);
2585
+ const offset2 = fieldOffset(typeDef, i);
2586
2586
  const storeOp = f.type === "i64" ? "i64.store" : f.type === "f32" ? "f32.store" : f.type === "f64" ? "f64.store" : "i32.store";
2587
- stores.push([storeOp, ["i32.add", ["local.get", ptrLocal], ["i32.const", offset]], args[i] || [`${f.type}.const`, 0]]);
2587
+ stores.push([storeOp, ["i32.add", ["local.get", ptrLocal], ["i32.const", offset2]], args[i] || [`${f.type}.const`, 0]]);
2588
2588
  }
2589
2589
  } else {
2590
2590
  for (let i = 0; i < typeDef.fields.length; i++) {
2591
2591
  const f = typeDef.fields[i];
2592
- const offset = fieldOffset(typeDef, i);
2592
+ const offset2 = fieldOffset(typeDef, i);
2593
2593
  const storeOp = f.type === "i64" ? "i64.store" : f.type === "f32" ? "f32.store" : f.type === "f64" ? "f64.store" : "i32.store";
2594
2594
  const zero = f.type === "i64" ? ["i64.const", 0n] : f.type === "f32" ? ["f32.const", 0] : f.type === "f64" ? ["f64.const", 0] : ["i32.const", 0];
2595
- stores.push([storeOp, ["i32.add", ["local.get", ptrLocal], ["i32.const", offset]], zero]);
2595
+ stores.push([storeOp, ["i32.add", ["local.get", ptrLocal], ["i32.const", offset2]], zero]);
2596
2596
  }
2597
2597
  }
2598
2598
  stores.push(["local.get", ptrLocal]);
@@ -2607,9 +2607,9 @@ var gc = (ast, ctx) => {
2607
2607
  const fieldIdx = typeof fieldId === "string" && fieldId[0] === "$" ? findFieldIdx(typeDef, fieldId) : parseInt(fieldId);
2608
2608
  if (fieldIdx < 0) return;
2609
2609
  const f = typeDef.fields[fieldIdx];
2610
- const offset = fieldOffset(typeDef, fieldIdx);
2610
+ const offset2 = fieldOffset(typeDef, fieldIdx);
2611
2611
  const loadOp = f.type === "i64" ? "i64.load" : f.type === "f32" ? "f32.load" : f.type === "f64" ? "f64.load" : "i32.load";
2612
- parent[idx] = [loadOp, ["i32.add", ref, ["i32.const", offset]]];
2612
+ parent[idx] = [loadOp, ["i32.add", ref, ["i32.const", offset2]]];
2613
2613
  }
2614
2614
  if (node[0] === "struct.set") {
2615
2615
  const typeId = node[1];
@@ -2621,9 +2621,9 @@ var gc = (ast, ctx) => {
2621
2621
  const fieldIdx = typeof fieldId === "string" && fieldId[0] === "$" ? findFieldIdx(typeDef, fieldId) : parseInt(fieldId);
2622
2622
  if (fieldIdx < 0) return;
2623
2623
  const f = typeDef.fields[fieldIdx];
2624
- const offset = fieldOffset(typeDef, fieldIdx);
2624
+ const offset2 = fieldOffset(typeDef, fieldIdx);
2625
2625
  const storeOp = f.type === "i64" ? "i64.store" : f.type === "f32" ? "f32.store" : f.type === "f64" ? "f64.store" : "i32.store";
2626
- parent[idx] = [storeOp, ["i32.add", ref, ["i32.const", offset]], val];
2626
+ parent[idx] = [storeOp, ["i32.add", ref, ["i32.const", offset2]], val];
2627
2627
  }
2628
2628
  if (node[0] === "array.new" || node[0] === "array.new_default") {
2629
2629
  const typeId = node[1];
@@ -2876,10 +2876,47 @@ var OPTS = {
2876
2876
  // simplify constant branches
2877
2877
  propagate: true,
2878
2878
  // constant propagation through locals
2879
- inline: true
2879
+ inline: true,
2880
2880
  // inline tiny functions
2881
+ vacuum: true,
2882
+ // remove nops, drop-of-pure, empty branches
2883
+ peephole: true,
2884
+ // x-x→0, x&0→0, etc.
2885
+ globals: true,
2886
+ // propagate immutable global constants
2887
+ offset: true,
2888
+ // fold add+const into load/store offset
2889
+ unbranch: true,
2890
+ // remove redundant br at end of own block
2891
+ stripmut: true,
2892
+ // strip mut from never-written globals
2893
+ brif: true,
2894
+ // if-then-br → br_if
2895
+ foldarms: true,
2896
+ // merge identical trailing if arms
2897
+ // minify: true, // NOTE: disabled — renaming $ids has no binary-size effect
2898
+ // without a names section, and risks local-name collisions.
2899
+ dedupe: true,
2900
+ // eliminate duplicate functions
2901
+ reorder: true,
2902
+ // put hot functions first for smaller LEBs
2903
+ dedupTypes: true,
2904
+ // merge identical type definitions
2905
+ packData: true,
2906
+ // trim trailing zeros, merge adjacent data segments
2907
+ minifyImports: false
2908
+ // shorten import names — enable only when you control the host
2881
2909
  };
2882
2910
  var ALL2 = Object.keys(OPTS);
2911
+ var equal = (a, b) => {
2912
+ if (a === b) return true;
2913
+ if (typeof a !== typeof b) return false;
2914
+ if (typeof a === "bigint") return a === b;
2915
+ if (!Array.isArray(a) || !Array.isArray(b)) return false;
2916
+ if (a.length !== b.length) return false;
2917
+ for (let i = 0; i < a.length; i++) if (!equal(a[i], b[i])) return false;
2918
+ return true;
2919
+ };
2883
2920
  var normalize3 = (opts) => {
2884
2921
  if (opts === true) return { ...OPTS };
2885
2922
  if (opts === false) return {};
@@ -2910,157 +2947,134 @@ var walkPost2 = (node, fn, parent, idx) => {
2910
2947
  const result = fn(node, parent, idx);
2911
2948
  return result !== void 0 ? result : node;
2912
2949
  };
2950
+ var parseIf = (node) => {
2951
+ let condIdx = 1;
2952
+ while (condIdx < node.length) {
2953
+ const c = node[condIdx];
2954
+ if (Array.isArray(c) && (c[0] === "then" || c[0] === "else" || c[0] === "result" || c[0] === "param")) {
2955
+ condIdx++;
2956
+ continue;
2957
+ }
2958
+ break;
2959
+ }
2960
+ let thenBranch = null, elseBranch = null;
2961
+ for (let i = condIdx + 1; i < node.length; i++) {
2962
+ const c = node[i];
2963
+ if (!Array.isArray(c)) continue;
2964
+ if (c[0] === "then") thenBranch = c;
2965
+ else if (c[0] === "else") elseBranch = c;
2966
+ }
2967
+ return { condIdx, cond: node[condIdx], thenBranch, elseBranch };
2968
+ };
2913
2969
  var treeshake = (ast) => {
2914
2970
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
2915
- const funcs = /* @__PURE__ */ new Map();
2916
- const globals = /* @__PURE__ */ new Map();
2917
- const types = /* @__PURE__ */ new Map();
2918
- const tables = /* @__PURE__ */ new Map();
2919
- const memories = /* @__PURE__ */ new Map();
2920
- const exports = [];
2921
- const starts = [];
2922
- let funcIdx = 0, globalIdx = 0, typeIdx = 0, tableIdx = 0, memIdx = 0, importFuncIdx = 0;
2971
+ const funcs = /* @__PURE__ */ new Map(), globals2 = /* @__PURE__ */ new Map(), types = /* @__PURE__ */ new Map();
2972
+ const tables = /* @__PURE__ */ new Map(), memories = /* @__PURE__ */ new Map();
2973
+ const nodeMap = /* @__PURE__ */ new Map();
2974
+ const register = (map, node, idx, isImport = false) => {
2975
+ const named = typeof node[1] === "string" && node[1][0] === "$";
2976
+ const name2 = named ? node[1] : idx;
2977
+ const inlineExported = !isImport && node.some((s) => Array.isArray(s) && s[0] === "export");
2978
+ const entry = { node, idx, used: inlineExported, isImport };
2979
+ map.set(name2, entry);
2980
+ if (named) map.set(idx, entry);
2981
+ nodeMap.set(node, entry);
2982
+ return entry;
2983
+ };
2984
+ let funcIdx = 0, globalIdx = 0, typeIdx = 0, tableIdx = 0, memIdx = 0;
2985
+ const elems = [], exports = [], starts = [];
2923
2986
  for (const node of ast.slice(1)) {
2924
2987
  if (!Array.isArray(node)) continue;
2925
2988
  const kind = node[0];
2926
- if (kind === "type") {
2927
- const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : typeIdx;
2928
- types.set(name2, { node, idx: typeIdx, used: false });
2929
- if (typeof name2 === "string") types.set(typeIdx, types.get(name2));
2930
- typeIdx++;
2931
- } else if (kind === "func") {
2932
- const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : funcIdx;
2933
- const hasInlineExport = node.some((sub) => Array.isArray(sub) && sub[0] === "export");
2934
- funcs.set(name2, { node, idx: funcIdx, used: hasInlineExport });
2935
- if (typeof name2 === "string") funcs.set(funcIdx, funcs.get(name2));
2936
- funcIdx++;
2937
- } else if (kind === "global") {
2938
- const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : globalIdx;
2939
- const hasInlineExport = node.some((sub) => Array.isArray(sub) && sub[0] === "export");
2940
- globals.set(name2, { node, idx: globalIdx, used: hasInlineExport });
2941
- if (typeof name2 === "string") globals.set(globalIdx, globals.get(name2));
2942
- globalIdx++;
2943
- } else if (kind === "table") {
2944
- const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : tableIdx;
2945
- const hasInlineExport = node.some((sub) => Array.isArray(sub) && sub[0] === "export");
2946
- tables.set(name2, { node, idx: tableIdx, used: hasInlineExport });
2947
- if (typeof name2 === "string") tables.set(tableIdx, tables.get(name2));
2948
- tableIdx++;
2949
- } else if (kind === "memory") {
2950
- const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : memIdx;
2951
- const hasInlineExport = node.some((sub) => Array.isArray(sub) && sub[0] === "export");
2952
- memories.set(name2, { node, idx: memIdx, used: hasInlineExport });
2953
- if (typeof name2 === "string") memories.set(memIdx, memories.get(name2));
2954
- memIdx++;
2955
- } else if (kind === "import") {
2989
+ if (kind === "type") register(types, node, typeIdx++);
2990
+ else if (kind === "func") register(funcs, node, funcIdx++);
2991
+ else if (kind === "global") register(globals2, node, globalIdx++);
2992
+ else if (kind === "table") register(tables, node, tableIdx++);
2993
+ else if (kind === "memory") register(memories, node, memIdx++);
2994
+ else if (kind === "import") {
2956
2995
  for (const sub of node) {
2957
- if (Array.isArray(sub) && sub[0] === "func") {
2958
- const name2 = typeof sub[1] === "string" && sub[1][0] === "$" ? sub[1] : importFuncIdx;
2959
- funcs.set(name2, { node, idx: importFuncIdx, used: true, isImport: true });
2960
- if (typeof name2 === "string") funcs.set(importFuncIdx, funcs.get(name2));
2961
- importFuncIdx++;
2962
- funcIdx++;
2963
- }
2996
+ if (!Array.isArray(sub)) continue;
2997
+ if (sub[0] === "func") register(funcs, sub, funcIdx++, true);
2998
+ else if (sub[0] === "global") register(globals2, sub, globalIdx++, true);
2999
+ else if (sub[0] === "table") register(tables, sub, tableIdx++, true);
3000
+ else if (sub[0] === "memory") register(memories, sub, memIdx++, true);
2964
3001
  }
2965
- } else if (kind === "export") {
2966
- exports.push(node);
2967
- } else if (kind === "start") {
2968
- starts.push(node);
2969
- }
3002
+ } else if (kind === "export") exports.push(node);
3003
+ else if (kind === "start") starts.push(node);
3004
+ else if (kind === "elem") elems.push(node);
2970
3005
  }
3006
+ const work = [];
3007
+ const enqueue = (entry) => {
3008
+ if (entry && !entry.scanned) work.push(entry);
3009
+ };
3010
+ const markFunc = (ref) => {
3011
+ const e = funcs.get(ref);
3012
+ if (!e) return;
3013
+ if (!e.used) e.used = true;
3014
+ enqueue(e);
3015
+ };
3016
+ const markGlobal = (ref) => {
3017
+ const e = globals2.get(ref);
3018
+ if (e) e.used = true;
3019
+ };
3020
+ const markTable = (ref) => {
3021
+ const e = tables.get(ref);
3022
+ if (e) e.used = true;
3023
+ };
3024
+ const markMemory = (ref) => {
3025
+ const e = memories.get(ref);
3026
+ if (e) e.used = true;
3027
+ };
3028
+ const markType = (ref) => {
3029
+ const e = types.get(ref);
3030
+ if (e) e.used = true;
3031
+ };
2971
3032
  for (const exp of exports) {
2972
3033
  for (const sub of exp) {
2973
3034
  if (!Array.isArray(sub)) continue;
2974
3035
  const [kind, ref] = sub;
2975
- if (kind === "func" && funcs.has(ref)) funcs.get(ref).used = true;
2976
- else if (kind === "global" && globals.has(ref)) globals.get(ref).used = true;
2977
- else if (kind === "table" && tables.has(ref)) tables.get(ref).used = true;
2978
- else if (kind === "memory" && memories.has(ref)) memories.get(ref).used = true;
3036
+ if (kind === "func") markFunc(ref);
3037
+ else if (kind === "global") markGlobal(ref);
3038
+ else if (kind === "table") markTable(ref);
3039
+ else if (kind === "memory") markMemory(ref);
2979
3040
  }
2980
3041
  }
2981
3042
  for (const start of starts) {
2982
3043
  let ref = start[1];
2983
3044
  if (typeof ref === "string" && ref[0] !== "$") ref = +ref;
2984
- if (funcs.has(ref)) funcs.get(ref).used = true;
3045
+ markFunc(ref);
2985
3046
  }
2986
- let hasExports = exports.length > 0 || starts.length > 0;
2987
- if (!hasExports) {
2988
- for (const [, entry] of funcs) if (entry.used) {
2989
- hasExports = true;
2990
- break;
2991
- }
2992
- if (!hasExports) {
2993
- for (const [, entry] of globals) if (entry.used) {
2994
- hasExports = true;
2995
- break;
2996
- }
2997
- }
2998
- if (!hasExports) {
2999
- for (const [, entry] of tables) if (entry.used) {
3000
- hasExports = true;
3001
- break;
3002
- }
3003
- }
3004
- if (!hasExports) {
3005
- for (const [, entry] of memories) if (entry.used) {
3006
- hasExports = true;
3007
- break;
3008
- }
3009
- }
3047
+ for (const elem of elems) {
3048
+ walk2(elem, (n) => {
3049
+ if (Array.isArray(n) && n[0] === "ref.func") markFunc(n[1]);
3050
+ else if (typeof n === "string" && n[0] === "$") markFunc(n);
3051
+ });
3010
3052
  }
3011
- if (!hasExports) {
3012
- for (const [, entry] of funcs) entry.used = true;
3013
- for (const [, entry] of globals) entry.used = true;
3014
- for (const [, entry] of tables) entry.used = true;
3015
- for (const [, entry] of memories) entry.used = true;
3053
+ for (const m of [funcs, globals2, tables, memories]) for (const e of m.values()) if (e.used) enqueue(e);
3054
+ const hasAnchor = exports.length > 0 || starts.length > 0 || elems.length > 0 || work.length > 0;
3055
+ if (!hasAnchor) {
3056
+ for (const m of [funcs, globals2, tables, memories]) for (const e of m.values()) e.used = true;
3057
+ return ast;
3016
3058
  }
3017
- for (const node of ast.slice(1)) {
3018
- if (!Array.isArray(node) || node[0] !== "elem") continue;
3019
- walk2(node, (n) => {
3020
- if (Array.isArray(n) && n[0] === "ref.func") {
3021
- const ref = n[1];
3022
- if (funcs.has(ref)) funcs.get(ref).used = true;
3059
+ while (work.length) {
3060
+ const entry = work.pop();
3061
+ if (entry.scanned) continue;
3062
+ entry.scanned = true;
3063
+ if (entry.isImport) continue;
3064
+ walk2(entry.node, (n) => {
3065
+ if (!Array.isArray(n)) {
3066
+ if (typeof n === "string" && n[0] === "$") markFunc(n);
3067
+ return;
3068
+ }
3069
+ const [op, ref] = n;
3070
+ if (op === "call" || op === "return_call" || op === "ref.func") markFunc(ref);
3071
+ else if (op === "global.get" || op === "global.set") markGlobal(ref);
3072
+ else if (op === "type") markType(ref);
3073
+ else if (op === "call_indirect" || op === "return_call_indirect") {
3074
+ for (const sub of n) if (typeof sub === "string" && sub[0] === "$") markTable(sub);
3023
3075
  }
3024
- if (typeof n === "string" && n[0] === "$" && funcs.has(n)) funcs.get(n).used = true;
3025
3076
  });
3026
3077
  }
3027
- let changed = true;
3028
- while (changed) {
3029
- changed = false;
3030
- for (const [, entry] of funcs) {
3031
- if (!entry.used || entry.isImport) continue;
3032
- walk2(entry.node, (n) => {
3033
- if (!Array.isArray(n)) {
3034
- if (typeof n === "string" && n[0] === "$" && funcs.has(n) && !funcs.get(n).used) {
3035
- funcs.get(n).used = true;
3036
- changed = true;
3037
- }
3038
- return;
3039
- }
3040
- const [op, ref] = n;
3041
- if ((op === "call" || op === "return_call" || op === "ref.func") && funcs.has(ref) && !funcs.get(ref).used) {
3042
- funcs.get(ref).used = true;
3043
- changed = true;
3044
- }
3045
- if ((op === "global.get" || op === "global.set") && globals.has(ref) && !globals.get(ref).used) {
3046
- globals.get(ref).used = true;
3047
- changed = true;
3048
- }
3049
- if (op === "call_indirect" || op === "return_call_indirect") {
3050
- for (const sub of n) {
3051
- if (typeof sub === "string" && sub[0] === "$" && tables.has(sub) && !tables.get(sub).used) {
3052
- tables.get(sub).used = true;
3053
- changed = true;
3054
- }
3055
- }
3056
- }
3057
- if (op === "type" && types.has(ref) && !types.get(ref).used) {
3058
- types.get(ref).used = true;
3059
- changed = true;
3060
- }
3061
- });
3062
- }
3063
- }
3064
3078
  const result = ["module"];
3065
3079
  for (const node of ast.slice(1)) {
3066
3080
  if (!Array.isArray(node)) {
@@ -3068,16 +3082,19 @@ var treeshake = (ast) => {
3068
3082
  continue;
3069
3083
  }
3070
3084
  const kind = node[0];
3071
- if (kind === "func") {
3072
- const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
3073
- const entry = name2 ? funcs.get(name2) : [...funcs.values()].find((e) => e.node === node);
3074
- if (entry?.used) result.push(node);
3075
- } else if (kind === "global") {
3076
- const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
3077
- const entry = name2 ? globals.get(name2) : [...globals.values()].find((e) => e.node === node);
3078
- if (entry?.used) result.push(node);
3079
- } else if (kind === "type") {
3080
- result.push(node);
3085
+ if (kind === "func" || kind === "global" || kind === "type") {
3086
+ if (nodeMap.get(node)?.used) result.push(node);
3087
+ } else if (kind === "import") {
3088
+ let used = false;
3089
+ for (const sub of node) {
3090
+ if (!Array.isArray(sub)) continue;
3091
+ const e = nodeMap.get(sub);
3092
+ if (e?.used) {
3093
+ used = true;
3094
+ break;
3095
+ }
3096
+ }
3097
+ if (used) result.push(node);
3081
3098
  } else {
3082
3099
  result.push(node);
3083
3100
  }
@@ -3085,101 +3102,110 @@ var treeshake = (ast) => {
3085
3102
  return result;
3086
3103
  };
3087
3104
  var roundEven = (x) => x - Math.floor(x) !== 0.5 ? Math.round(x) : 2 * Math.round(x / 2);
3105
+ var i32c = (fn) => (a, b) => fn(a, b) ? 1 : 0;
3106
+ var u32c = (fn) => (a, b) => fn(a >>> 0, b >>> 0) ? 1 : 0;
3107
+ var i64c = (fn) => (a, b) => fn(a, b) ? 1 : 0;
3108
+ var u64c = (fn) => (a, b) => fn(BigInt.asUintN(64, a), BigInt.asUintN(64, b)) ? 1 : 0;
3088
3109
  var FOLDABLE = {
3089
- // i32
3090
- "i32.add": (a, b) => a + b | 0,
3091
- "i32.sub": (a, b) => a - b | 0,
3092
- "i32.mul": (a, b) => Math.imul(a, b),
3093
- "i32.div_s": (a, b) => b !== 0 ? a / b | 0 : null,
3094
- "i32.div_u": (a, b) => b !== 0 ? (a >>> 0) / (b >>> 0) | 0 : null,
3095
- "i32.rem_s": (a, b) => b !== 0 ? a % b | 0 : null,
3096
- "i32.rem_u": (a, b) => b !== 0 ? (a >>> 0) % (b >>> 0) | 0 : null,
3097
- "i32.and": (a, b) => a & b,
3098
- "i32.or": (a, b) => a | b,
3099
- "i32.xor": (a, b) => a ^ b,
3100
- "i32.shl": (a, b) => a << (b & 31),
3101
- "i32.shr_s": (a, b) => a >> (b & 31),
3102
- "i32.shr_u": (a, b) => a >>> (b & 31),
3103
- "i32.rotl": (a, b) => {
3110
+ // i32 arithmetic
3111
+ "i32.add": [(a, b) => a + b | 0, "i32"],
3112
+ "i32.sub": [(a, b) => a - b | 0, "i32"],
3113
+ "i32.mul": [(a, b) => Math.imul(a, b), "i32"],
3114
+ "i32.div_s": [(a, b) => b !== 0 ? a / b | 0 : null, "i32"],
3115
+ "i32.div_u": [(a, b) => b !== 0 ? (a >>> 0) / (b >>> 0) | 0 : null, "i32"],
3116
+ "i32.rem_s": [(a, b) => b !== 0 ? a % b | 0 : null, "i32"],
3117
+ "i32.rem_u": [(a, b) => b !== 0 ? (a >>> 0) % (b >>> 0) | 0 : null, "i32"],
3118
+ "i32.and": [(a, b) => a & b, "i32"],
3119
+ "i32.or": [(a, b) => a | b, "i32"],
3120
+ "i32.xor": [(a, b) => a ^ b, "i32"],
3121
+ "i32.shl": [(a, b) => a << (b & 31), "i32"],
3122
+ "i32.shr_s": [(a, b) => a >> (b & 31), "i32"],
3123
+ "i32.shr_u": [(a, b) => a >>> (b & 31), "i32"],
3124
+ "i32.rotl": [(a, b) => {
3104
3125
  b &= 31;
3105
3126
  return a << b | a >>> 32 - b | 0;
3106
- },
3107
- "i32.rotr": (a, b) => {
3127
+ }, "i32"],
3128
+ "i32.rotr": [(a, b) => {
3108
3129
  b &= 31;
3109
3130
  return a >>> b | a << 32 - b | 0;
3110
- },
3111
- "i32.eq": (a, b) => a === b ? 1 : 0,
3112
- "i32.ne": (a, b) => a !== b ? 1 : 0,
3113
- "i32.lt_s": (a, b) => a < b ? 1 : 0,
3114
- "i32.lt_u": (a, b) => a >>> 0 < b >>> 0 ? 1 : 0,
3115
- "i32.gt_s": (a, b) => a > b ? 1 : 0,
3116
- "i32.gt_u": (a, b) => a >>> 0 > b >>> 0 ? 1 : 0,
3117
- "i32.le_s": (a, b) => a <= b ? 1 : 0,
3118
- "i32.le_u": (a, b) => a >>> 0 <= b >>> 0 ? 1 : 0,
3119
- "i32.ge_s": (a, b) => a >= b ? 1 : 0,
3120
- "i32.ge_u": (a, b) => a >>> 0 >= b >>> 0 ? 1 : 0,
3121
- "i32.eqz": (a) => a === 0 ? 1 : 0,
3122
- "i32.clz": (a) => Math.clz32(a),
3123
- "i32.ctz": (a) => a === 0 ? 32 : 31 - Math.clz32(a & -a),
3124
- "i32.popcnt": (a) => {
3131
+ }, "i32"],
3132
+ "i32.eq": [i32c((a, b) => a === b), "i32"],
3133
+ "i32.ne": [i32c((a, b) => a !== b), "i32"],
3134
+ "i32.lt_s": [i32c((a, b) => a < b), "i32"],
3135
+ "i32.lt_u": [u32c((a, b) => a < b), "i32"],
3136
+ "i32.gt_s": [i32c((a, b) => a > b), "i32"],
3137
+ "i32.gt_u": [u32c((a, b) => a > b), "i32"],
3138
+ "i32.le_s": [i32c((a, b) => a <= b), "i32"],
3139
+ "i32.le_u": [u32c((a, b) => a <= b), "i32"],
3140
+ "i32.ge_s": [i32c((a, b) => a >= b), "i32"],
3141
+ "i32.ge_u": [u32c((a, b) => a >= b), "i32"],
3142
+ "i32.eqz": [(a) => a === 0 ? 1 : 0, "i32"],
3143
+ "i32.clz": [(a) => Math.clz32(a), "i32"],
3144
+ "i32.ctz": [(a) => a === 0 ? 32 : 31 - Math.clz32(a & -a), "i32"],
3145
+ "i32.popcnt": [(a) => {
3125
3146
  let c = 0;
3126
3147
  while (a) {
3127
3148
  c += a & 1;
3128
3149
  a >>>= 1;
3129
3150
  }
3130
3151
  return c;
3131
- },
3132
- "i32.wrap_i64": (a) => Number(BigInt.asIntN(32, a)),
3152
+ }, "i32"],
3153
+ "i32.wrap_i64": [(a) => Number(BigInt.asIntN(32, a)), "i32"],
3154
+ "i32.extend8_s": [(a) => a << 24 >> 24, "i32"],
3155
+ "i32.extend16_s": [(a) => a << 16 >> 16, "i32"],
3133
3156
  // i64 (using BigInt)
3134
- "i64.add": (a, b) => BigInt.asIntN(64, a + b),
3135
- "i64.sub": (a, b) => BigInt.asIntN(64, a - b),
3136
- "i64.mul": (a, b) => BigInt.asIntN(64, a * b),
3137
- "i64.div_s": (a, b) => b !== 0n ? BigInt.asIntN(64, a / b) : null,
3138
- "i64.div_u": (a, b) => b !== 0n ? BigInt.asUintN(64, BigInt.asUintN(64, a) / BigInt.asUintN(64, b)) : null,
3139
- "i64.rem_s": (a, b) => b !== 0n ? BigInt.asIntN(64, a % b) : null,
3140
- "i64.rem_u": (a, b) => b !== 0n ? BigInt.asUintN(64, BigInt.asUintN(64, a) % BigInt.asUintN(64, b)) : null,
3141
- "i64.and": (a, b) => BigInt.asIntN(64, a & b),
3142
- "i64.or": (a, b) => BigInt.asIntN(64, a | b),
3143
- "i64.xor": (a, b) => BigInt.asIntN(64, a ^ b),
3144
- "i64.shl": (a, b) => BigInt.asIntN(64, a << (b & 63n)),
3145
- "i64.shr_s": (a, b) => BigInt.asIntN(64, a >> (b & 63n)),
3146
- "i64.shr_u": (a, b) => BigInt.asUintN(64, BigInt.asUintN(64, a) >> (b & 63n)),
3147
- "i64.eq": (a, b) => a === b ? 1 : 0,
3148
- "i64.ne": (a, b) => a !== b ? 1 : 0,
3149
- "i64.lt_s": (a, b) => a < b ? 1 : 0,
3150
- "i64.lt_u": (a, b) => BigInt.asUintN(64, a) < BigInt.asUintN(64, b) ? 1 : 0,
3151
- "i64.gt_s": (a, b) => a > b ? 1 : 0,
3152
- "i64.gt_u": (a, b) => BigInt.asUintN(64, a) > BigInt.asUintN(64, b) ? 1 : 0,
3153
- "i64.le_s": (a, b) => a <= b ? 1 : 0,
3154
- "i64.le_u": (a, b) => BigInt.asUintN(64, a) <= BigInt.asUintN(64, b) ? 1 : 0,
3155
- "i64.ge_s": (a, b) => a >= b ? 1 : 0,
3156
- "i64.ge_u": (a, b) => BigInt.asUintN(64, a) >= BigInt.asUintN(64, b) ? 1 : 0,
3157
- "i64.eqz": (a) => a === 0n ? 1 : 0,
3158
- "i64.extend_i32_s": (a) => BigInt(a),
3159
- "i64.extend_i32_u": (a) => BigInt(a >>> 0),
3160
- // f32/f64 - be careful with NaN/precision
3161
- "f32.add": (a, b) => Math.fround(a + b),
3162
- "f32.sub": (a, b) => Math.fround(a - b),
3163
- "f32.mul": (a, b) => Math.fround(a * b),
3164
- "f32.div": (a, b) => Math.fround(a / b),
3165
- "f32.neg": (a) => Math.fround(-a),
3166
- "f32.abs": (a) => Math.fround(Math.abs(a)),
3167
- "f32.sqrt": (a) => Math.fround(Math.sqrt(a)),
3168
- "f32.ceil": (a) => Math.fround(Math.ceil(a)),
3169
- "f32.floor": (a) => Math.fround(Math.floor(a)),
3170
- "f32.trunc": (a) => Math.fround(Math.trunc(a)),
3171
- "f32.nearest": (a) => Math.fround(roundEven(a)),
3172
- "f64.add": (a, b) => a + b,
3173
- "f64.sub": (a, b) => a - b,
3174
- "f64.mul": (a, b) => a * b,
3175
- "f64.div": (a, b) => a / b,
3176
- "f64.neg": (a) => -a,
3177
- "f64.abs": (a) => Math.abs(a),
3178
- "f64.sqrt": (a) => Math.sqrt(a),
3179
- "f64.ceil": (a) => Math.ceil(a),
3180
- "f64.floor": (a) => Math.floor(a),
3181
- "f64.trunc": (a) => Math.trunc(a),
3182
- "f64.nearest": roundEven
3157
+ "i64.add": [(a, b) => BigInt.asIntN(64, a + b), "i64"],
3158
+ "i64.sub": [(a, b) => BigInt.asIntN(64, a - b), "i64"],
3159
+ "i64.mul": [(a, b) => BigInt.asIntN(64, a * b), "i64"],
3160
+ "i64.div_s": [(a, b) => b !== 0n ? BigInt.asIntN(64, a / b) : null, "i64"],
3161
+ "i64.div_u": [(a, b) => b !== 0n ? BigInt.asUintN(64, BigInt.asUintN(64, a) / BigInt.asUintN(64, b)) : null, "i64"],
3162
+ "i64.rem_s": [(a, b) => b !== 0n ? BigInt.asIntN(64, a % b) : null, "i64"],
3163
+ "i64.rem_u": [(a, b) => b !== 0n ? BigInt.asUintN(64, BigInt.asUintN(64, a) % BigInt.asUintN(64, b)) : null, "i64"],
3164
+ "i64.and": [(a, b) => BigInt.asIntN(64, a & b), "i64"],
3165
+ "i64.or": [(a, b) => BigInt.asIntN(64, a | b), "i64"],
3166
+ "i64.xor": [(a, b) => BigInt.asIntN(64, a ^ b), "i64"],
3167
+ "i64.shl": [(a, b) => BigInt.asIntN(64, a << (b & 63n)), "i64"],
3168
+ "i64.shr_s": [(a, b) => BigInt.asIntN(64, a >> (b & 63n)), "i64"],
3169
+ "i64.shr_u": [(a, b) => BigInt.asUintN(64, BigInt.asUintN(64, a) >> (b & 63n)), "i64"],
3170
+ "i64.eq": [i64c((a, b) => a === b), "i32"],
3171
+ "i64.ne": [i64c((a, b) => a !== b), "i32"],
3172
+ "i64.lt_s": [i64c((a, b) => a < b), "i32"],
3173
+ "i64.lt_u": [u64c((a, b) => a < b), "i32"],
3174
+ "i64.gt_s": [i64c((a, b) => a > b), "i32"],
3175
+ "i64.gt_u": [u64c((a, b) => a > b), "i32"],
3176
+ "i64.le_s": [i64c((a, b) => a <= b), "i32"],
3177
+ "i64.le_u": [u64c((a, b) => a <= b), "i32"],
3178
+ "i64.ge_s": [i64c((a, b) => a >= b), "i32"],
3179
+ "i64.ge_u": [u64c((a, b) => a >= b), "i32"],
3180
+ "i64.eqz": [(a) => a === 0n ? 1 : 0, "i32"],
3181
+ "i64.extend_i32_s": [(a) => BigInt(a), "i64"],
3182
+ "i64.extend_i32_u": [(a) => BigInt(a >>> 0), "i64"],
3183
+ "i64.extend8_s": [(a) => BigInt.asIntN(64, BigInt.asIntN(8, a)), "i64"],
3184
+ "i64.extend16_s": [(a) => BigInt.asIntN(64, BigInt.asIntN(16, a)), "i64"],
3185
+ "i64.extend32_s": [(a) => BigInt.asIntN(64, BigInt.asIntN(32, a)), "i64"],
3186
+ // f32/f64 (NaN/precision-aware via Math.fround)
3187
+ "f32.add": [(a, b) => Math.fround(a + b), "f32"],
3188
+ "f32.sub": [(a, b) => Math.fround(a - b), "f32"],
3189
+ "f32.mul": [(a, b) => Math.fround(a * b), "f32"],
3190
+ "f32.div": [(a, b) => Math.fround(a / b), "f32"],
3191
+ "f32.neg": [(a) => Math.fround(-a), "f32"],
3192
+ "f32.abs": [(a) => Math.fround(Math.abs(a)), "f32"],
3193
+ "f32.sqrt": [(a) => Math.fround(Math.sqrt(a)), "f32"],
3194
+ "f32.ceil": [(a) => Math.fround(Math.ceil(a)), "f32"],
3195
+ "f32.floor": [(a) => Math.fround(Math.floor(a)), "f32"],
3196
+ "f32.trunc": [(a) => Math.fround(Math.trunc(a)), "f32"],
3197
+ "f32.nearest": [(a) => Math.fround(roundEven(a)), "f32"],
3198
+ "f64.add": [(a, b) => a + b, "f64"],
3199
+ "f64.sub": [(a, b) => a - b, "f64"],
3200
+ "f64.mul": [(a, b) => a * b, "f64"],
3201
+ "f64.div": [(a, b) => a / b, "f64"],
3202
+ "f64.neg": [(a) => -a, "f64"],
3203
+ "f64.abs": [Math.abs, "f64"],
3204
+ "f64.sqrt": [Math.sqrt, "f64"],
3205
+ "f64.ceil": [Math.ceil, "f64"],
3206
+ "f64.floor": [Math.floor, "f64"],
3207
+ "f64.trunc": [Math.trunc, "f64"],
3208
+ "f64.nearest": [roundEven, "f64"]
3183
3209
  };
3184
3210
  var getConst = (node) => {
3185
3211
  if (!Array.isArray(node) || node.length !== 2) return null;
@@ -3200,110 +3226,63 @@ var makeConst = (type, value) => {
3200
3226
  var fold = (ast) => {
3201
3227
  return walkPost2(clone2(ast), (node) => {
3202
3228
  if (!Array.isArray(node)) return;
3203
- const op = node[0];
3204
- const fn = FOLDABLE[op];
3205
- if (!fn) return;
3229
+ const entry = FOLDABLE[node[0]];
3230
+ if (!entry) return;
3231
+ const [fn, t] = entry;
3206
3232
  if (fn.length === 1 && node.length === 2) {
3207
3233
  const a = getConst(node[1]);
3208
3234
  if (!a) return;
3209
- const result = fn(a.value);
3210
- if (result === null) return;
3211
- const resultType = op.startsWith("i64.") && !op.includes("eqz") ? "i64" : op.startsWith("f32.") ? "f32" : op.startsWith("f64.") ? "f64" : "i32";
3212
- return makeConst(resultType, result);
3235
+ const r = fn(a.value);
3236
+ if (r === null) return;
3237
+ return makeConst(t, r);
3213
3238
  }
3214
3239
  if (fn.length === 2 && node.length === 3) {
3215
- const a = getConst(node[1]);
3216
- const b = getConst(node[2]);
3240
+ const a = getConst(node[1]), b = getConst(node[2]);
3217
3241
  if (!a || !b) return;
3218
- const result = fn(a.value, b.value);
3219
- if (result === null) return;
3220
- const isCompare = /\.(eq|ne|[lg][te])/.test(op);
3221
- const resultType = isCompare ? "i32" : op.startsWith("i64.") ? "i64" : op.startsWith("f32.") ? "f32" : op.startsWith("f64.") ? "f64" : "i32";
3222
- return makeConst(resultType, result);
3242
+ const r = fn(a.value, b.value);
3243
+ if (r === null) return;
3244
+ return makeConst(t, r);
3223
3245
  }
3224
3246
  });
3225
3247
  };
3248
+ var commutativeIdentity = (neutral) => (a, b) => {
3249
+ const ca = getConst(a), cb = getConst(b);
3250
+ if (ca?.value === neutral) return b;
3251
+ if (cb?.value === neutral) return a;
3252
+ return null;
3253
+ };
3254
+ var rightIdentity = (neutral) => (a, b) => getConst(b)?.value === neutral ? a : null;
3226
3255
  var IDENTITIES = {
3227
3256
  // x + 0 → x, 0 + x → x
3228
- "i32.add": (a, b) => {
3229
- const ca = getConst(a), cb = getConst(b);
3230
- if (ca?.value === 0) return b;
3231
- if (cb?.value === 0) return a;
3232
- return null;
3233
- },
3234
- "i64.add": (a, b) => {
3235
- const ca = getConst(a), cb = getConst(b);
3236
- if (ca?.value === 0n) return b;
3237
- if (cb?.value === 0n) return a;
3238
- return null;
3239
- },
3257
+ "i32.add": commutativeIdentity(0),
3258
+ "i64.add": commutativeIdentity(0n),
3240
3259
  // x - 0 → x
3241
- "i32.sub": (a, b) => getConst(b)?.value === 0 ? a : null,
3242
- "i64.sub": (a, b) => getConst(b)?.value === 0n ? a : null,
3260
+ "i32.sub": rightIdentity(0),
3261
+ "i64.sub": rightIdentity(0n),
3243
3262
  // x * 1 → x, 1 * x → x
3244
- "i32.mul": (a, b) => {
3245
- const ca = getConst(a), cb = getConst(b);
3246
- if (ca?.value === 1) return b;
3247
- if (cb?.value === 1) return a;
3248
- return null;
3249
- },
3250
- "i64.mul": (a, b) => {
3251
- const ca = getConst(a), cb = getConst(b);
3252
- if (ca?.value === 1n) return b;
3253
- if (cb?.value === 1n) return a;
3254
- return null;
3255
- },
3263
+ "i32.mul": commutativeIdentity(1),
3264
+ "i64.mul": commutativeIdentity(1n),
3256
3265
  // x / 1 → x
3257
- "i32.div_s": (a, b) => getConst(b)?.value === 1 ? a : null,
3258
- "i32.div_u": (a, b) => getConst(b)?.value === 1 ? a : null,
3259
- "i64.div_s": (a, b) => getConst(b)?.value === 1n ? a : null,
3260
- "i64.div_u": (a, b) => getConst(b)?.value === 1n ? a : null,
3266
+ "i32.div_s": rightIdentity(1),
3267
+ "i32.div_u": rightIdentity(1),
3268
+ "i64.div_s": rightIdentity(1n),
3269
+ "i64.div_u": rightIdentity(1n),
3261
3270
  // x & -1 → x, -1 & x → x (all bits set)
3262
- "i32.and": (a, b) => {
3263
- const ca = getConst(a), cb = getConst(b);
3264
- if (ca?.value === -1) return b;
3265
- if (cb?.value === -1) return a;
3266
- return null;
3267
- },
3268
- "i64.and": (a, b) => {
3269
- const ca = getConst(a), cb = getConst(b);
3270
- if (ca?.value === -1n) return b;
3271
- if (cb?.value === -1n) return a;
3272
- return null;
3273
- },
3271
+ "i32.and": commutativeIdentity(-1),
3272
+ "i64.and": commutativeIdentity(-1n),
3274
3273
  // x | 0 → x, 0 | x → x
3275
- "i32.or": (a, b) => {
3276
- const ca = getConst(a), cb = getConst(b);
3277
- if (ca?.value === 0) return b;
3278
- if (cb?.value === 0) return a;
3279
- return null;
3280
- },
3281
- "i64.or": (a, b) => {
3282
- const ca = getConst(a), cb = getConst(b);
3283
- if (ca?.value === 0n) return b;
3284
- if (cb?.value === 0n) return a;
3285
- return null;
3286
- },
3274
+ "i32.or": commutativeIdentity(0),
3275
+ "i64.or": commutativeIdentity(0n),
3287
3276
  // x ^ 0 → x, 0 ^ x → x
3288
- "i32.xor": (a, b) => {
3289
- const ca = getConst(a), cb = getConst(b);
3290
- if (ca?.value === 0) return b;
3291
- if (cb?.value === 0) return a;
3292
- return null;
3293
- },
3294
- "i64.xor": (a, b) => {
3295
- const ca = getConst(a), cb = getConst(b);
3296
- if (ca?.value === 0n) return b;
3297
- if (cb?.value === 0n) return a;
3298
- return null;
3299
- },
3277
+ "i32.xor": commutativeIdentity(0),
3278
+ "i64.xor": commutativeIdentity(0n),
3300
3279
  // x << 0 → x, x >> 0 → x
3301
- "i32.shl": (a, b) => getConst(b)?.value === 0 ? a : null,
3302
- "i32.shr_s": (a, b) => getConst(b)?.value === 0 ? a : null,
3303
- "i32.shr_u": (a, b) => getConst(b)?.value === 0 ? a : null,
3304
- "i64.shl": (a, b) => getConst(b)?.value === 0n ? a : null,
3305
- "i64.shr_s": (a, b) => getConst(b)?.value === 0n ? a : null,
3306
- "i64.shr_u": (a, b) => getConst(b)?.value === 0n ? a : null
3280
+ "i32.shl": rightIdentity(0),
3281
+ "i32.shr_s": rightIdentity(0),
3282
+ "i32.shr_u": rightIdentity(0),
3283
+ "i64.shl": rightIdentity(0n),
3284
+ "i64.shr_s": rightIdentity(0n),
3285
+ "i64.shr_u": rightIdentity(0n)
3307
3286
  // f + 0 → x (careful with -0.0, skip for floats)
3308
3287
  // f * 1 → x (careful with NaN, skip for floats)
3309
3288
  };
@@ -3378,41 +3357,15 @@ var branch = (ast) => {
3378
3357
  if (!Array.isArray(node)) return;
3379
3358
  const op = node[0];
3380
3359
  if (op === "if") {
3381
- let condIdx = 1;
3382
- while (condIdx < node.length) {
3383
- const child = node[condIdx];
3384
- if (Array.isArray(child) && (child[0] === "then" || child[0] === "else" || child[0] === "result" || child[0] === "param")) {
3385
- condIdx++;
3386
- continue;
3387
- }
3388
- break;
3389
- }
3390
- const cond = node[condIdx];
3360
+ const { cond, thenBranch, elseBranch } = parseIf(node);
3391
3361
  const c = getConst(cond);
3392
3362
  if (!c) return;
3393
- let thenBranch = null, elseBranch = null;
3394
- for (let i = condIdx + 1; i < node.length; i++) {
3395
- const child = node[i];
3396
- if (Array.isArray(child)) {
3397
- if (child[0] === "then") thenBranch = child;
3398
- else if (child[0] === "else") elseBranch = child;
3399
- }
3400
- }
3401
- if (c.value !== 0 && c.value !== 0n) {
3402
- if (thenBranch && thenBranch.length > 1) {
3403
- const contents = thenBranch.slice(1);
3404
- if (contents.length === 1) return contents[0];
3405
- return ["block", ...contents];
3406
- }
3407
- return ["nop"];
3408
- } else {
3409
- if (elseBranch && elseBranch.length > 1) {
3410
- const contents = elseBranch.slice(1);
3411
- if (contents.length === 1) return contents[0];
3412
- return ["block", ...contents];
3413
- }
3414
- return ["nop"];
3363
+ const taken = c.value !== 0 && c.value !== 0n ? thenBranch : elseBranch;
3364
+ if (taken && taken.length > 1) {
3365
+ const contents = taken.slice(1);
3366
+ return contents.length === 1 ? contents[0] : ["block", ...contents];
3415
3367
  }
3368
+ return ["nop"];
3416
3369
  }
3417
3370
  if (op === "br_if" && node.length >= 3) {
3418
3371
  const cond = node[node.length - 1];
@@ -3519,13 +3472,52 @@ var localReuse = (ast) => {
3519
3472
  });
3520
3473
  return result;
3521
3474
  };
3475
+ var IMPURE_OPS = /* @__PURE__ */ new Set([
3476
+ "call",
3477
+ "call_indirect",
3478
+ "return_call",
3479
+ "return_call_indirect",
3480
+ "table.set",
3481
+ "table.grow",
3482
+ "table.fill",
3483
+ "table.copy",
3484
+ "table.init",
3485
+ "struct.set",
3486
+ "struct.new",
3487
+ "array.set",
3488
+ "array.new",
3489
+ "array.new_fixed",
3490
+ "array.new_data",
3491
+ "array.new_elem",
3492
+ "array.init_data",
3493
+ "array.init_elem",
3494
+ "ref.i31",
3495
+ "global.set",
3496
+ "local.set",
3497
+ "local.tee",
3498
+ "unreachable",
3499
+ "return",
3500
+ "br",
3501
+ "br_if",
3502
+ "br_table",
3503
+ "br_on_null",
3504
+ "br_on_non_null",
3505
+ "br_on_cast",
3506
+ "br_on_cast_fail",
3507
+ "throw",
3508
+ "rethrow",
3509
+ "throw_ref",
3510
+ "try_table",
3511
+ "data.drop",
3512
+ "elem.drop"
3513
+ ]);
3514
+ var IMPURE_SUBSTRINGS = [".store", "memory.", ".atomic."];
3522
3515
  var isPure = (node) => {
3523
3516
  if (!Array.isArray(node)) return true;
3524
3517
  const op = node[0];
3525
3518
  if (typeof op !== "string") return false;
3526
- if (op === "call" || op === "call_indirect" || op === "return_call" || op === "return_call_indirect") return false;
3527
- if (op.includes(".store") || op.includes(".load") || op.includes("memory.")) return false;
3528
- if (op === "global.set") return false;
3519
+ if (IMPURE_OPS.has(op)) return false;
3520
+ for (const sub of IMPURE_SUBSTRINGS) if (op.includes(sub)) return false;
3529
3521
  for (let i = 1; i < node.length; i++) if (Array.isArray(node[i]) && !isPure(node[i])) return false;
3530
3522
  return true;
3531
3523
  };
@@ -3549,6 +3541,100 @@ var substGets = (node, known) => walkPost2(node, (n) => {
3549
3541
  const k = typeof n[1] === "string" && known.get(n[1]);
3550
3542
  if (k && canSubst(k)) return clone2(k.val);
3551
3543
  });
3544
+ var forwardPropagate = (funcNode, params, useCounts) => {
3545
+ let changed = false;
3546
+ const getUseCount = (name2) => useCounts.get(name2) || { gets: 0, sets: 0, tees: 0 };
3547
+ const known = /* @__PURE__ */ new Map();
3548
+ for (let i = 1; i < funcNode.length; i++) {
3549
+ const instr2 = funcNode[i];
3550
+ if (!Array.isArray(instr2)) continue;
3551
+ const op = instr2[0];
3552
+ if (op === "param" || op === "result" || op === "local" || op === "type" || op === "export") continue;
3553
+ if (op === "local.set" && instr2.length === 3 && typeof instr2[1] === "string") {
3554
+ substGets(instr2[2], known);
3555
+ const uses = getUseCount(instr2[1]);
3556
+ known.set(instr2[1], {
3557
+ val: instr2[2],
3558
+ pure: isPure(instr2[2]),
3559
+ singleUse: uses.gets <= 1 && uses.sets <= 1 && uses.tees === 0
3560
+ });
3561
+ continue;
3562
+ }
3563
+ if (op === "block" || op === "loop" || op === "if") known.clear();
3564
+ if (op === "call" || op === "call_indirect" || op === "return_call" || op === "return_call_indirect") {
3565
+ for (const [key, tracked] of known) if (!getConst(tracked.val)) known.delete(key);
3566
+ }
3567
+ if (op === "local.get" && instr2.length === 2 && typeof instr2[1] === "string") {
3568
+ const tracked = known.get(instr2[1]);
3569
+ if (tracked && canSubst(tracked)) {
3570
+ const replacement = clone2(tracked.val);
3571
+ instr2.length = 0;
3572
+ instr2.push(...Array.isArray(replacement) ? replacement : [replacement]);
3573
+ changed = true;
3574
+ continue;
3575
+ }
3576
+ }
3577
+ if (op !== "block" && op !== "loop" && op !== "if") {
3578
+ const prev = clone2(instr2);
3579
+ substGets(instr2, known);
3580
+ if (!equal(prev, instr2)) changed = true;
3581
+ }
3582
+ }
3583
+ return changed;
3584
+ };
3585
+ var eliminateSetGetPairs = (funcNode, params, useCounts) => {
3586
+ let changed = false;
3587
+ for (let i = 1; i < funcNode.length - 1; i++) {
3588
+ const setNode = funcNode[i];
3589
+ const getNode = funcNode[i + 1];
3590
+ if (!Array.isArray(setNode) || setNode[0] !== "local.set" || setNode.length !== 3) continue;
3591
+ if (!Array.isArray(getNode) || getNode[0] !== "local.get" || getNode.length !== 2) continue;
3592
+ const name2 = setNode[1];
3593
+ if (getNode[1] !== name2 || params.has(name2)) continue;
3594
+ const uses = useCounts.get(name2) || { gets: 0, sets: 0, tees: 0 };
3595
+ if (uses.sets !== 1 || uses.gets !== 1 || uses.tees !== 0) continue;
3596
+ const expr2 = clone2(setNode[2]);
3597
+ funcNode.splice(i, 2, ...Array.isArray(expr2) ? [expr2] : [expr2]);
3598
+ changed = true;
3599
+ i--;
3600
+ }
3601
+ return changed;
3602
+ };
3603
+ var createLocalTees = (funcNode, params, useCounts) => {
3604
+ let changed = false;
3605
+ for (let i = 1; i < funcNode.length - 1; i++) {
3606
+ const setNode = funcNode[i];
3607
+ const getNode = funcNode[i + 1];
3608
+ if (!Array.isArray(setNode) || setNode[0] !== "local.set" || setNode.length !== 3) continue;
3609
+ if (!Array.isArray(getNode) || getNode[0] !== "local.get" || getNode.length !== 2) continue;
3610
+ const name2 = setNode[1];
3611
+ if (getNode[1] !== name2 || params.has(name2)) continue;
3612
+ const uses = useCounts.get(name2) || { gets: 0, sets: 0, tees: 0 };
3613
+ if (uses.sets + uses.gets + uses.tees <= 2) continue;
3614
+ funcNode.splice(i, 2, ["local.tee", name2, clone2(setNode[2])]);
3615
+ changed = true;
3616
+ }
3617
+ return changed;
3618
+ };
3619
+ var eliminateDeadStores = (funcNode, params, useCounts) => {
3620
+ let changed = false;
3621
+ const getPostUseCount = (name2) => useCounts.get(name2) || { gets: 0, sets: 0, tees: 0 };
3622
+ for (let i = funcNode.length - 1; i >= 1; i--) {
3623
+ const sub = funcNode[i];
3624
+ if (!Array.isArray(sub)) continue;
3625
+ const name2 = typeof sub[1] === "string" ? sub[1] : null;
3626
+ if (!name2 || params.has(name2)) continue;
3627
+ const uses = getPostUseCount(name2);
3628
+ if (sub[0] === "local.set" && uses.gets === 0 && uses.tees === 0 && isPure(sub[2])) {
3629
+ funcNode.splice(i, 1);
3630
+ changed = true;
3631
+ } else if (sub[0] === "local" && name2[0] === "$" && uses.gets === 0 && uses.sets === 0 && uses.tees === 0) {
3632
+ funcNode.splice(i, 1);
3633
+ changed = true;
3634
+ }
3635
+ }
3636
+ return changed;
3637
+ };
3552
3638
  var propagate = (ast) => {
3553
3639
  const result = clone2(ast);
3554
3640
  walk2(result, (funcNode) => {
@@ -3558,60 +3644,10 @@ var propagate = (ast) => {
3558
3644
  if (Array.isArray(sub) && sub[0] === "param" && typeof sub[1] === "string") params.add(sub[1]);
3559
3645
  for (let pass = 0; pass < 4; pass++) {
3560
3646
  let changed = false;
3561
- const uses = countLocalUses(funcNode);
3562
- const getUses = (name2) => uses.get(name2) || { gets: 0, sets: 0, tees: 0 };
3563
- const known = /* @__PURE__ */ new Map();
3564
- for (let i = 1; i < funcNode.length; i++) {
3565
- const instr2 = funcNode[i];
3566
- if (!Array.isArray(instr2)) continue;
3567
- const op = instr2[0];
3568
- if (op === "param" || op === "result" || op === "local" || op === "type" || op === "export") continue;
3569
- if (op === "local.set" && instr2.length === 3 && typeof instr2[1] === "string") {
3570
- substGets(instr2[2], known);
3571
- const u = getUses(instr2[1]);
3572
- known.set(instr2[1], {
3573
- val: instr2[2],
3574
- pure: isPure(instr2[2]),
3575
- singleUse: u.gets <= 1 && u.sets <= 1 && u.tees === 0
3576
- });
3577
- continue;
3578
- }
3579
- if (op === "block" || op === "loop" || op === "if") known.clear();
3580
- if (op === "call" || op === "call_indirect" || op === "return_call" || op === "return_call_indirect") {
3581
- for (const [k, v] of known) if (!getConst(v.val)) known.delete(k);
3582
- }
3583
- if (op === "local.get" && instr2.length === 2 && typeof instr2[1] === "string") {
3584
- const k = known.get(instr2[1]);
3585
- if (k && canSubst(k)) {
3586
- const r = clone2(k.val);
3587
- instr2.length = 0;
3588
- instr2.push(...Array.isArray(r) ? r : [r]);
3589
- changed = true;
3590
- continue;
3591
- }
3592
- }
3593
- if (op !== "block" && op !== "loop" && op !== "if") {
3594
- const prev = JSON.stringify(instr2);
3595
- substGets(instr2, known);
3596
- if (JSON.stringify(instr2) !== prev) changed = true;
3597
- }
3598
- }
3599
- const postUses = countLocalUses(funcNode);
3600
- const pu = (name2) => postUses.get(name2) || { gets: 0, sets: 0, tees: 0 };
3601
- for (let i = funcNode.length - 1; i >= 1; i--) {
3602
- const sub = funcNode[i];
3603
- if (!Array.isArray(sub)) continue;
3604
- const name2 = typeof sub[1] === "string" ? sub[1] : null;
3605
- if (!name2 || params.has(name2)) continue;
3606
- const u = pu(name2);
3607
- if (sub[0] === "local.set" && u.gets === 0 && u.tees === 0 && isPure(sub[2])) {
3608
- funcNode.splice(i, 1);
3609
- changed = true;
3610
- } else if (sub[0] === "local" && name2[0] === "$" && u.gets === 0 && u.sets === 0 && u.tees === 0) {
3611
- funcNode.splice(i, 1);
3612
- changed = true;
3613
- }
3614
- }
3647
+ if (forwardPropagate(funcNode, params, countLocalUses(funcNode))) changed = true;
3648
+ if (eliminateSetGetPairs(funcNode, params, countLocalUses(funcNode))) changed = true;
3649
+ if (createLocalTees(funcNode, params, countLocalUses(funcNode))) changed = true;
3650
+ if (eliminateDeadStores(funcNode, params, countLocalUses(funcNode))) changed = true;
3615
3651
  if (!changed) break;
3616
3652
  }
3617
3653
  });
@@ -3647,7 +3683,7 @@ var inline = (ast) => {
3647
3683
  body.push(sub);
3648
3684
  }
3649
3685
  }
3650
- if (params && !hasLocals && !hasExport && params.length <= 2 && body.length === 1) {
3686
+ if (params && !hasLocals && !hasExport && params.length <= 4 && body.length === 1) {
3651
3687
  const paramNames = new Set(params.map((p) => p.name));
3652
3688
  let mutatesParam = false;
3653
3689
  walk2(body[0], (n) => {
@@ -3684,19 +3720,704 @@ var inline = (ast) => {
3684
3720
  });
3685
3721
  return result;
3686
3722
  };
3723
+ var vacuum = (ast) => {
3724
+ return walkPost2(clone2(ast), (node) => {
3725
+ if (!Array.isArray(node)) return;
3726
+ const op = node[0];
3727
+ if (op === "nop") return ["nop"];
3728
+ if (op === "drop" && node.length === 2 && isPure(node[1])) {
3729
+ return ["nop"];
3730
+ }
3731
+ if (op === "select" && node.length >= 4 && equal(node[1], node[2])) return node[1];
3732
+ if (op === "if") {
3733
+ const { cond, thenBranch, elseBranch } = parseIf(node);
3734
+ const thenEmpty = !thenBranch || thenBranch.length <= 1;
3735
+ const elseEmpty = !elseBranch || elseBranch.length <= 1;
3736
+ if (thenEmpty && elseEmpty) return isPure(cond) ? ["nop"] : ["drop", cond];
3737
+ if (elseBranch && elseEmpty && !thenEmpty) {
3738
+ return node.filter((c) => c !== elseBranch);
3739
+ }
3740
+ }
3741
+ if (op === "func" || op === "block" || op === "loop" || op === "then" || op === "else") {
3742
+ const cleaned = [op];
3743
+ for (let i = 1; i < node.length; i++) {
3744
+ const child = node[i];
3745
+ if (child === "nop" || Array.isArray(child) && child[0] === "nop") continue;
3746
+ const next = node[i + 1];
3747
+ const isDrop = next === "drop" || Array.isArray(next) && next[0] === "drop" && next.length === 1;
3748
+ if (Array.isArray(child) && isPure(child) && isDrop) {
3749
+ i++;
3750
+ continue;
3751
+ }
3752
+ cleaned.push(child);
3753
+ }
3754
+ if (cleaned.length !== node.length) return cleaned;
3755
+ }
3756
+ });
3757
+ };
3758
+ var PEEPHOLE = {
3759
+ // Self-cancelling / tautological binary ops
3760
+ "i32.sub": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
3761
+ "i64.sub": (a, b) => equal(a, b) ? ["i64.const", 0n] : null,
3762
+ "i32.xor": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
3763
+ "i64.xor": (a, b) => equal(a, b) ? ["i64.const", 0n] : null,
3764
+ "i32.and": (a, b) => equal(a, b) ? a : null,
3765
+ "i64.and": (a, b) => equal(a, b) ? a : null,
3766
+ "i32.or": (a, b) => equal(a, b) ? a : null,
3767
+ "i64.or": (a, b) => equal(a, b) ? a : null,
3768
+ "i32.eq": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
3769
+ "i64.eq": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
3770
+ "i32.ne": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
3771
+ "i64.ne": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
3772
+ "i32.lt_s": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
3773
+ "i32.lt_u": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
3774
+ "i32.gt_s": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
3775
+ "i32.gt_u": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
3776
+ "i32.le_s": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
3777
+ "i32.le_u": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
3778
+ "i32.ge_s": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
3779
+ "i32.ge_u": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
3780
+ "i64.lt_s": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
3781
+ "i64.lt_u": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
3782
+ "i64.gt_s": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
3783
+ "i64.gt_u": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
3784
+ "i64.le_s": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
3785
+ "i64.le_u": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
3786
+ "i64.ge_s": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
3787
+ "i64.ge_u": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
3788
+ // Zero/all-bits absorption
3789
+ "i32.mul": (a, b) => {
3790
+ const ca = getConst(a), cb = getConst(b);
3791
+ if (ca?.value === 0 || cb?.value === 0) return ["i32.const", 0];
3792
+ return null;
3793
+ },
3794
+ "i64.mul": (a, b) => {
3795
+ const ca = getConst(a), cb = getConst(b);
3796
+ if (ca?.value === 0n || cb?.value === 0n) return ["i64.const", 0n];
3797
+ return null;
3798
+ },
3799
+ "i32.and": (a, b) => {
3800
+ const ca = getConst(a), cb = getConst(b);
3801
+ if (ca?.value === 0 || cb?.value === 0) return ["i32.const", 0];
3802
+ return null;
3803
+ },
3804
+ "i64.and": (a, b) => {
3805
+ const ca = getConst(a), cb = getConst(b);
3806
+ if (ca?.value === 0n || cb?.value === 0n) return ["i64.const", 0n];
3807
+ return null;
3808
+ },
3809
+ "i32.or": (a, b) => {
3810
+ const ca = getConst(a), cb = getConst(b);
3811
+ if (ca?.value === -1 || cb?.value === -1) return ["i32.const", -1];
3812
+ return null;
3813
+ },
3814
+ "i64.or": (a, b) => {
3815
+ const ca = getConst(a), cb = getConst(b);
3816
+ if (ca?.value === -1n || cb?.value === -1n) return ["i64.const", -1n];
3817
+ return null;
3818
+ },
3819
+ // (local.set $x (local.get $x)) → nop
3820
+ "local.set": (a, b) => Array.isArray(b) && b[0] === "local.get" && b[1] === a ? ["nop"] : null
3821
+ };
3822
+ var peephole = (ast) => {
3823
+ return walkPost2(clone2(ast), (node) => {
3824
+ if (!Array.isArray(node) || node.length !== 3) return;
3825
+ const fn = PEEPHOLE[node[0]];
3826
+ if (!fn) return;
3827
+ const result = fn(node[1], node[2]);
3828
+ if (result !== null) return result;
3829
+ });
3830
+ };
3831
+ var globals = (ast) => {
3832
+ if (!Array.isArray(ast) || ast[0] !== "module") return ast;
3833
+ const result = clone2(ast);
3834
+ const constGlobals = /* @__PURE__ */ new Map();
3835
+ const mutableGlobals = /* @__PURE__ */ new Set();
3836
+ for (const node of result.slice(1)) {
3837
+ if (!Array.isArray(node) || node[0] !== "global") continue;
3838
+ const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
3839
+ if (!name2) continue;
3840
+ const hasName = typeof node[1] === "string" && node[1][0] === "$";
3841
+ const initIdx = hasName ? 3 : 2;
3842
+ const typeSlot = hasName ? node[2] : node[1];
3843
+ if (Array.isArray(typeSlot) && typeSlot[0] === "mut") continue;
3844
+ const init = node[initIdx];
3845
+ if (getConst(init)) constGlobals.set(name2, init);
3846
+ }
3847
+ walk2(result, (n) => {
3848
+ if (!Array.isArray(n) || n[0] !== "global.set") return;
3849
+ const ref = n[1];
3850
+ if (typeof ref === "string" && ref[0] === "$") mutableGlobals.add(ref);
3851
+ });
3852
+ for (const name2 of mutableGlobals) constGlobals.delete(name2);
3853
+ if (constGlobals.size === 0) return result;
3854
+ return walkPost2(result, (node) => {
3855
+ if (!Array.isArray(node) || node[0] !== "global.get" || node.length !== 2) return;
3856
+ const ref = node[1];
3857
+ if (constGlobals.has(ref)) return clone2(constGlobals.get(ref));
3858
+ });
3859
+ };
3860
+ var offset = (ast) => {
3861
+ return walkPost2(clone2(ast), (node) => {
3862
+ if (!Array.isArray(node)) return;
3863
+ const op = node[0];
3864
+ if (typeof op !== "string" || !op.endsWith("load") && !op.endsWith("store")) return;
3865
+ const isStore = op.endsWith("store");
3866
+ let currentOffset = 0;
3867
+ let memIdx = null;
3868
+ let argStart = 1;
3869
+ if (typeof node[1] === "string" && (node[1][0] === "$" || !isNaN(node[1]))) {
3870
+ memIdx = node[1];
3871
+ argStart = 2;
3872
+ }
3873
+ while (argStart < node.length && typeof node[argStart] === "string" && (node[argStart].startsWith("offset=") || node[argStart].startsWith("align="))) {
3874
+ if (node[argStart].startsWith("offset=")) {
3875
+ currentOffset = +node[argStart].slice(7);
3876
+ }
3877
+ argStart++;
3878
+ }
3879
+ const ptrIdx = isStore ? node.length - 2 : node.length - 1;
3880
+ const valIdx = isStore ? node.length - 1 : -1;
3881
+ if (ptrIdx < argStart) return;
3882
+ const ptr = node[ptrIdx];
3883
+ if (!Array.isArray(ptr) || ptr[0] !== "i32.add" || ptr.length !== 3) return;
3884
+ const a = ptr[1], b = ptr[2];
3885
+ const ca = getConst(a), cb = getConst(b);
3886
+ let base = null, addend = null;
3887
+ if (ca && ca.type === "i32") {
3888
+ addend = ca.value;
3889
+ base = b;
3890
+ } else if (cb && cb.type === "i32") {
3891
+ addend = cb.value;
3892
+ base = a;
3893
+ }
3894
+ if (base === null || addend === null) return;
3895
+ const newOffset = currentOffset + addend;
3896
+ const newNode = [op];
3897
+ if (memIdx !== null) newNode.push(memIdx);
3898
+ newNode.push(`offset=${newOffset}`);
3899
+ let alignParam = null;
3900
+ for (let i = argStart; i < ptrIdx; i++) {
3901
+ if (typeof node[i] === "string" && node[i].startsWith("align=")) {
3902
+ alignParam = node[i];
3903
+ }
3904
+ }
3905
+ if (alignParam) newNode.push(alignParam);
3906
+ newNode.push(base);
3907
+ if (isStore) newNode.push(node[valIdx]);
3908
+ return newNode;
3909
+ });
3910
+ };
3911
+ var unbranch = (ast) => {
3912
+ const result = clone2(ast);
3913
+ walk2(result, (node) => {
3914
+ if (!Array.isArray(node)) return;
3915
+ const op = node[0];
3916
+ if (op !== "block" && op !== "loop") return;
3917
+ let labelIdx = 1;
3918
+ let label = null;
3919
+ if (typeof node[1] === "string" && node[1][0] === "$") {
3920
+ label = node[1];
3921
+ labelIdx = 2;
3922
+ }
3923
+ if (!label) return;
3924
+ let lastIdx = -1;
3925
+ for (let i = node.length - 1; i >= labelIdx; i--) {
3926
+ const child = node[i];
3927
+ if (!Array.isArray(child)) {
3928
+ if (child !== "nop" && child !== "end") lastIdx = i;
3929
+ continue;
3930
+ }
3931
+ const cop = child[0];
3932
+ if (cop === "param" || cop === "result" || cop === "local" || cop === "type" || cop === "export") continue;
3933
+ lastIdx = i;
3934
+ break;
3935
+ }
3936
+ if (lastIdx < 0) return;
3937
+ const last = node[lastIdx];
3938
+ if (Array.isArray(last) && last[0] === "br" && last[1] === label) {
3939
+ node.splice(lastIdx, 1);
3940
+ }
3941
+ });
3942
+ return result;
3943
+ };
3944
+ var stripmut = (ast) => {
3945
+ if (!Array.isArray(ast) || ast[0] !== "module") return ast;
3946
+ const result = clone2(ast);
3947
+ const written = /* @__PURE__ */ new Set();
3948
+ walk2(result, (n) => {
3949
+ if (Array.isArray(n) && n[0] === "global.set" && typeof n[1] === "string") written.add(n[1]);
3950
+ });
3951
+ return walkPost2(result, (node) => {
3952
+ if (!Array.isArray(node) || node[0] !== "global") return;
3953
+ const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
3954
+ if (!name2 || written.has(name2)) return;
3955
+ const hasName = typeof node[1] === "string" && node[1][0] === "$";
3956
+ const typeSlot = hasName ? node[2] : node[1];
3957
+ if (Array.isArray(typeSlot) && typeSlot[0] === "mut") {
3958
+ const newNode = [...node];
3959
+ newNode[hasName ? 2 : 1] = typeSlot[1];
3960
+ return newNode;
3961
+ }
3962
+ });
3963
+ };
3964
+ var brif = (ast) => {
3965
+ return walkPost2(clone2(ast), (node) => {
3966
+ if (!Array.isArray(node) || node[0] !== "if") return;
3967
+ const { cond, thenBranch, elseBranch } = parseIf(node);
3968
+ const thenEmpty = !thenBranch || thenBranch.length <= 1;
3969
+ const elseEmpty = !elseBranch || elseBranch.length <= 1;
3970
+ if (!thenEmpty && elseEmpty && thenBranch.length === 2) {
3971
+ const t = thenBranch[1];
3972
+ if (Array.isArray(t) && t[0] === "br" && t.length === 2) return ["br_if", t[1], cond];
3973
+ }
3974
+ if (thenEmpty && !elseEmpty && elseBranch.length === 2) {
3975
+ const e = elseBranch[1];
3976
+ if (Array.isArray(e) && e[0] === "br" && e.length === 2) return ["br_if", e[1], ["i32.eqz", cond]];
3977
+ }
3978
+ });
3979
+ };
3980
+ var foldarms = (ast) => {
3981
+ return walkPost2(clone2(ast), (node) => {
3982
+ if (!Array.isArray(node) || node[0] !== "if") return;
3983
+ const { thenBranch, elseBranch } = parseIf(node);
3984
+ if (!thenBranch || !elseBranch) return;
3985
+ if (thenBranch.length <= 1 || elseBranch.length <= 1) return;
3986
+ const hasResult = node.some((c) => Array.isArray(c) && c[0] === "result");
3987
+ if (!hasResult) return;
3988
+ let common = 0;
3989
+ const minLen = Math.min(thenBranch.length, elseBranch.length);
3990
+ for (let i = 1; i < minLen; i++) {
3991
+ if (!equal(thenBranch[thenBranch.length - i], elseBranch[elseBranch.length - i])) break;
3992
+ common++;
3993
+ }
3994
+ if (common === 0) return;
3995
+ const hoisted = thenBranch.slice(thenBranch.length - common);
3996
+ const newThen = thenBranch.slice(0, thenBranch.length - common);
3997
+ const newElse = elseBranch.slice(0, elseBranch.length - common);
3998
+ const block = ["block"];
3999
+ for (let i = 1; i < node.length; i++) {
4000
+ const c = node[i];
4001
+ if (Array.isArray(c) && (c[0] === "then" || c[0] === "else")) break;
4002
+ if (Array.isArray(c) && (c[0] === "result" || c[0] === "type")) block.push(c);
4003
+ }
4004
+ const newIf = ["if"];
4005
+ for (let i = 1; i < node.length; i++) {
4006
+ const c = node[i];
4007
+ if (Array.isArray(c) && (c[0] === "then" || c[0] === "else")) break;
4008
+ newIf.push(c);
4009
+ }
4010
+ newIf.push(newThen.length > 1 ? newThen : ["then"]);
4011
+ newIf.push(newElse.length > 1 ? newElse : ["else"]);
4012
+ block.push(newIf, ...hoisted);
4013
+ return block;
4014
+ });
4015
+ };
4016
+ var hashFunc = (node, localNames) => {
4017
+ const parts = [];
4018
+ const stack = [node];
4019
+ while (stack.length) {
4020
+ const v = stack.pop();
4021
+ if (Array.isArray(v)) {
4022
+ stack.push("|");
4023
+ for (let i = v.length - 1; i >= 0; i--) stack.push(v[i]);
4024
+ stack.push("[");
4025
+ } else if (typeof v === "string") {
4026
+ parts.push(localNames.has(v) ? "$__L" : v);
4027
+ } else if (typeof v === "bigint") {
4028
+ parts.push(v.toString() + "n");
4029
+ } else if (typeof v === "number") {
4030
+ parts.push(v.toString());
4031
+ } else if (v === null) {
4032
+ parts.push("null");
4033
+ } else if (v === true) {
4034
+ parts.push("t");
4035
+ } else if (v === false) {
4036
+ parts.push("f");
4037
+ } else {
4038
+ parts.push(String(v));
4039
+ }
4040
+ }
4041
+ return parts.join(",");
4042
+ };
4043
+ var dedupe = (ast) => {
4044
+ if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4045
+ const result = clone2(ast);
4046
+ const signatures = /* @__PURE__ */ new Map();
4047
+ const redirects = /* @__PURE__ */ new Map();
4048
+ for (const node of result.slice(1)) {
4049
+ if (!Array.isArray(node) || node[0] !== "func") continue;
4050
+ const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4051
+ if (!name2) continue;
4052
+ const localNames = /* @__PURE__ */ new Set();
4053
+ if (typeof node[1] === "string" && node[1][0] === "$") localNames.add(node[1]);
4054
+ walk2(node, (n) => {
4055
+ if (!Array.isArray(n) || typeof n[1] !== "string" || n[1][0] !== "$") return;
4056
+ const op = n[0];
4057
+ if (op === "param" || op === "local" || op === "block" || op === "loop" || op === "if") {
4058
+ localNames.add(n[1]);
4059
+ }
4060
+ });
4061
+ const hash = hashFunc(node, localNames);
4062
+ if (signatures.has(hash)) {
4063
+ redirects.set(name2, signatures.get(hash));
4064
+ } else {
4065
+ signatures.set(hash, name2);
4066
+ }
4067
+ }
4068
+ if (redirects.size === 0) return result;
4069
+ walkPost2(result, (node) => {
4070
+ if (!Array.isArray(node)) return;
4071
+ const op = node[0];
4072
+ if ((op === "call" || op === "return_call") && redirects.has(node[1])) {
4073
+ return [op, redirects.get(node[1]), ...node.slice(2)];
4074
+ }
4075
+ if (op === "ref.func" && redirects.has(node[1])) {
4076
+ return ["ref.func", redirects.get(node[1])];
4077
+ }
4078
+ if (op === "elem") {
4079
+ const funcs = node[node.length - 1];
4080
+ if (Array.isArray(funcs)) {
4081
+ return [...node.slice(0, -1), funcs.map((f) => redirects.get(f) || f)];
4082
+ }
4083
+ }
4084
+ if (op === "call_indirect" && node.length >= 3) {
4085
+ const typeRef = node[1];
4086
+ if (typeof typeRef === "string" && redirects.has(typeRef)) {
4087
+ return ["call_indirect", redirects.get(typeRef), ...node.slice(2)];
4088
+ }
4089
+ }
4090
+ });
4091
+ return result;
4092
+ };
4093
+ var dedupTypes = (ast) => {
4094
+ if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4095
+ const result = clone2(ast);
4096
+ const signatures = /* @__PURE__ */ new Map();
4097
+ const redirects = /* @__PURE__ */ new Map();
4098
+ for (const node of result.slice(1)) {
4099
+ if (!Array.isArray(node) || node[0] !== "type") continue;
4100
+ const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4101
+ if (!name2) continue;
4102
+ const hash = hashFunc(node, /* @__PURE__ */ new Set([name2]));
4103
+ if (signatures.has(hash)) {
4104
+ redirects.set(name2, signatures.get(hash));
4105
+ } else {
4106
+ signatures.set(hash, name2);
4107
+ }
4108
+ }
4109
+ if (redirects.size === 0) return result;
4110
+ for (let i = result.length - 1; i >= 0; i--) {
4111
+ const node = result[i];
4112
+ if (Array.isArray(node) && node[0] === "type") {
4113
+ const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4114
+ if (name2 && redirects.has(name2)) result.splice(i, 1);
4115
+ }
4116
+ }
4117
+ walkPost2(result, (node) => {
4118
+ if (!Array.isArray(node)) return;
4119
+ const op = node[0];
4120
+ if (op === "func") {
4121
+ for (let i = 1; i < node.length; i++) {
4122
+ const sub = node[i];
4123
+ if (Array.isArray(sub) && sub[0] === "type" && typeof sub[1] === "string" && redirects.has(sub[1])) {
4124
+ node[i] = ["type", redirects.get(sub[1])];
4125
+ }
4126
+ }
4127
+ }
4128
+ if (op === "import") {
4129
+ for (let i = 1; i < node.length; i++) {
4130
+ const sub = node[i];
4131
+ if (Array.isArray(sub)) {
4132
+ for (let j = 1; j < sub.length; j++) {
4133
+ const inner = sub[j];
4134
+ if (Array.isArray(inner) && inner[0] === "type" && typeof inner[1] === "string" && redirects.has(inner[1])) {
4135
+ sub[j] = ["type", redirects.get(inner[1])];
4136
+ }
4137
+ }
4138
+ }
4139
+ }
4140
+ }
4141
+ if (op === "call_indirect" || op === "return_call_indirect") {
4142
+ if (typeof node[1] === "string" && redirects.has(node[1])) {
4143
+ return [op, redirects.get(node[1]), ...node.slice(2)];
4144
+ }
4145
+ if (Array.isArray(node[1]) && node[1][0] === "type" && typeof node[1][1] === "string" && redirects.has(node[1][1])) {
4146
+ return [op, ["type", redirects.get(node[1][1])], ...node.slice(2)];
4147
+ }
4148
+ }
4149
+ });
4150
+ return result;
4151
+ };
4152
+ var parseDataString = (str2) => {
4153
+ if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return new Uint8Array();
4154
+ const inner = str2.slice(1, -1);
4155
+ const bytes = [];
4156
+ for (let i = 0; i < inner.length; i++) {
4157
+ if (inner[i] === "\\") {
4158
+ const next = inner[++i];
4159
+ if (next === "x" || next === "X") {
4160
+ bytes.push(parseInt(inner.slice(i + 1, i + 3), 16));
4161
+ i += 2;
4162
+ } else if (/[0-9a-fA-F]/.test(next) && /[0-9a-fA-F]/.test(inner[i + 1])) {
4163
+ bytes.push(parseInt(inner.slice(i, i + 2), 16));
4164
+ i++;
4165
+ } else if (next === "n") bytes.push(10);
4166
+ else if (next === "t") bytes.push(9);
4167
+ else if (next === "r") bytes.push(13);
4168
+ else if (next === "\\") bytes.push(92);
4169
+ else if (next === '"') bytes.push(34);
4170
+ else bytes.push(next.charCodeAt(0));
4171
+ } else {
4172
+ bytes.push(inner.charCodeAt(i));
4173
+ }
4174
+ }
4175
+ return new Uint8Array(bytes);
4176
+ };
4177
+ var encodeDataString = (bytes) => {
4178
+ let str2 = '"';
4179
+ for (let i = 0; i < bytes.length; i++) {
4180
+ const b = bytes[i];
4181
+ if (b >= 32 && b < 127 && b !== 34 && b !== 92) {
4182
+ str2 += String.fromCharCode(b);
4183
+ } else {
4184
+ str2 += "\\" + b.toString(16).padStart(2, "0");
4185
+ }
4186
+ }
4187
+ return str2 + '"';
4188
+ };
4189
+ var trimTrailingZeros = (items) => {
4190
+ const bytes = [];
4191
+ for (const item of items) {
4192
+ if (typeof item === "string") {
4193
+ bytes.push(...parseDataString(item));
4194
+ } else if (Array.isArray(item) && item[0] === "i8") {
4195
+ for (let i = 1; i < item.length; i++) bytes.push(Number(item[i]) & 255);
4196
+ } else {
4197
+ return items;
4198
+ }
4199
+ }
4200
+ let end = bytes.length;
4201
+ while (end > 0 && bytes[end - 1] === 0) end--;
4202
+ if (end === bytes.length) return items;
4203
+ if (end === 0) return [];
4204
+ return [encodeDataString(new Uint8Array(bytes.slice(0, end)))];
4205
+ };
4206
+ var getDataOffset = (node) => {
4207
+ let idx = 1;
4208
+ if (typeof node[idx] === "string" && node[idx][0] === "$") idx++;
4209
+ if (Array.isArray(node[idx]) && node[idx][0] === "memory") {
4210
+ const mem = node[idx][1];
4211
+ idx++;
4212
+ const off2 = node[idx];
4213
+ if (Array.isArray(off2) && (off2[0] === "i32.const" || off2[0] === "i64.const")) {
4214
+ return { memidx: mem, offset: Number(off2[1]) };
4215
+ }
4216
+ return null;
4217
+ }
4218
+ const off = node[idx];
4219
+ if (Array.isArray(off) && (off[0] === "i32.const" || off[0] === "i64.const")) {
4220
+ return { memidx: 0, offset: Number(off[1]) };
4221
+ }
4222
+ return null;
4223
+ };
4224
+ var getDataLength = (node) => {
4225
+ let idx = 1;
4226
+ if (typeof node[idx] === "string" && node[idx][0] === "$") idx++;
4227
+ if (Array.isArray(node[idx]) && node[idx][0] === "memory") idx++;
4228
+ if (Array.isArray(node[idx]) && typeof node[idx][0] === "string" && !node[idx][0].startsWith('"')) idx++;
4229
+ let len = 0;
4230
+ for (let i = idx; i < node.length; i++) {
4231
+ const item = node[i];
4232
+ if (typeof item === "string") len += parseDataString(item).length;
4233
+ else if (Array.isArray(item) && item[0] === "i8") len += item.length - 1;
4234
+ else return null;
4235
+ }
4236
+ return len;
4237
+ };
4238
+ var mergeDataSegments = (a, b) => {
4239
+ let aIdx = 1;
4240
+ if (typeof a[aIdx] === "string" && a[aIdx][0] === "$") aIdx++;
4241
+ if (Array.isArray(a[aIdx]) && a[aIdx][0] === "memory") aIdx++;
4242
+ if (Array.isArray(a[aIdx]) && typeof a[aIdx][0] === "string" && !a[aIdx][0].startsWith('"')) aIdx++;
4243
+ let bIdx = 1;
4244
+ if (typeof b[bIdx] === "string" && b[bIdx][0] === "$") bIdx++;
4245
+ if (Array.isArray(b[bIdx]) && b[bIdx][0] === "memory") bIdx++;
4246
+ if (Array.isArray(b[bIdx]) && typeof b[bIdx][0] === "string" && !b[bIdx][0].startsWith('"')) bIdx++;
4247
+ const aContent = a.slice(aIdx);
4248
+ const bContent = b.slice(bIdx);
4249
+ if (aContent.length === 1 && bContent.length === 1 && typeof aContent[0] === "string" && typeof bContent[0] === "string") {
4250
+ const aBytes = parseDataString(aContent[0]);
4251
+ const bBytes = parseDataString(bContent[0]);
4252
+ const merged = new Uint8Array(aBytes.length + bBytes.length);
4253
+ merged.set(aBytes);
4254
+ merged.set(bBytes, aBytes.length);
4255
+ a.length = aIdx;
4256
+ a.push(encodeDataString(merged));
4257
+ return true;
4258
+ }
4259
+ a.length = aIdx;
4260
+ a.push(...aContent, ...bContent);
4261
+ return true;
4262
+ };
4263
+ var packData = (ast) => {
4264
+ if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4265
+ let result = clone2(ast);
4266
+ for (const node of result) {
4267
+ if (!Array.isArray(node) || node[0] !== "data") continue;
4268
+ let contentStart = 1;
4269
+ if (typeof node[1] === "string" && node[1][0] === "$") contentStart = 2;
4270
+ if (contentStart < node.length && Array.isArray(node[contentStart]) && typeof node[contentStart][0] === "string" && !node[contentStart][0].startsWith('"')) {
4271
+ contentStart++;
4272
+ }
4273
+ const content = node.slice(contentStart);
4274
+ if (content.length === 0) continue;
4275
+ const trimmed = trimTrailingZeros(content);
4276
+ if (trimmed.length !== content.length || trimmed.length > 0 && trimmed[0] !== content[0]) {
4277
+ node.length = contentStart;
4278
+ node.push(...trimmed);
4279
+ }
4280
+ }
4281
+ const dataNodes = [];
4282
+ for (let i = 0; i < result.length; i++) {
4283
+ const node = result[i];
4284
+ if (Array.isArray(node) && node[0] === "data") {
4285
+ const info = getDataOffset(node);
4286
+ if (info) {
4287
+ const len = getDataLength(node);
4288
+ if (len !== null) dataNodes.push({ ...info, node, index: i, len });
4289
+ }
4290
+ }
4291
+ }
4292
+ dataNodes.sort((a, b) => {
4293
+ const ma = String(a.memidx), mb = String(b.memidx);
4294
+ if (ma !== mb) return ma.localeCompare(mb);
4295
+ return a.offset - b.offset;
4296
+ });
4297
+ const toRemove = /* @__PURE__ */ new Set();
4298
+ for (let i = 0; i < dataNodes.length - 1; i++) {
4299
+ const a = dataNodes[i];
4300
+ const b = dataNodes[i + 1];
4301
+ if (toRemove.has(a.index) || String(a.memidx) !== String(b.memidx)) continue;
4302
+ if (a.offset + a.len !== b.offset) continue;
4303
+ if (mergeDataSegments(a.node, b.node)) {
4304
+ toRemove.add(b.index);
4305
+ a.len = getDataLength(a.node);
4306
+ }
4307
+ }
4308
+ if (toRemove.size > 0) {
4309
+ result = result.filter((_, i) => !toRemove.has(i));
4310
+ }
4311
+ return result;
4312
+ };
4313
+ var makeShortener = () => {
4314
+ const map = /* @__PURE__ */ new Map();
4315
+ let n = 0;
4316
+ return (name2) => {
4317
+ if (!map.has(name2)) {
4318
+ let id2 = "", x = n++;
4319
+ do {
4320
+ id2 = String.fromCharCode(97 + x % 26) + id2;
4321
+ x = Math.floor(x / 26) - 1;
4322
+ } while (x >= 0);
4323
+ map.set(name2, id2 || "a");
4324
+ }
4325
+ return map.get(name2);
4326
+ };
4327
+ };
4328
+ var minifyImports = (ast) => {
4329
+ if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4330
+ const result = clone2(ast);
4331
+ const shortMod = makeShortener();
4332
+ const shortField = makeShortener();
4333
+ for (const node of result) {
4334
+ if (!Array.isArray(node) || node[0] !== "import") continue;
4335
+ if (typeof node[1] === "string" && node[1][0] === '"') {
4336
+ node[1] = '"' + shortMod(node[1].slice(1, -1)) + '"';
4337
+ }
4338
+ if (typeof node[2] === "string" && node[2][0] === '"') {
4339
+ node[2] = '"' + shortField(node[2].slice(1, -1)) + '"';
4340
+ }
4341
+ }
4342
+ return result;
4343
+ };
4344
+ var reorderSafe = (ast) => {
4345
+ let safe = true;
4346
+ walk2(ast, (n) => {
4347
+ if (!safe || !Array.isArray(n)) return;
4348
+ const op = n[0];
4349
+ if (op === "func" && (typeof n[1] !== "string" || n[1][0] !== "$")) safe = false;
4350
+ else if ((op === "call" || op === "return_call" || op === "ref.func") && (typeof n[1] !== "string" || n[1][0] !== "$")) safe = false;
4351
+ else if (op === "start" && (typeof n[1] !== "string" || n[1][0] !== "$")) safe = false;
4352
+ else if (op === "elem") {
4353
+ for (const sub of n) {
4354
+ if (typeof sub === "string" && sub[0] !== "$" && /^\d/.test(sub)) {
4355
+ safe = false;
4356
+ break;
4357
+ }
4358
+ if (Array.isArray(sub) && sub[0] === "ref.func" && (typeof sub[1] !== "string" || sub[1][0] !== "$")) {
4359
+ safe = false;
4360
+ break;
4361
+ }
4362
+ }
4363
+ }
4364
+ });
4365
+ return safe;
4366
+ };
4367
+ var reorder = (ast) => {
4368
+ if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4369
+ if (!reorderSafe(ast)) return ast;
4370
+ const result = clone2(ast);
4371
+ const callCounts = /* @__PURE__ */ new Map();
4372
+ walk2(result, (n) => {
4373
+ if (!Array.isArray(n)) return;
4374
+ if (n[0] === "call" || n[0] === "return_call") {
4375
+ callCounts.set(n[1], (callCounts.get(n[1]) || 0) + 1);
4376
+ }
4377
+ });
4378
+ const imports = [], funcs = [], others = [];
4379
+ for (const node of result.slice(1)) {
4380
+ if (!Array.isArray(node)) {
4381
+ others.push(node);
4382
+ continue;
4383
+ }
4384
+ if (node[0] === "import") imports.push(node);
4385
+ else if (node[0] === "func") funcs.push(node);
4386
+ else others.push(node);
4387
+ }
4388
+ funcs.sort((a, b) => (callCounts.get(b[1]) || 0) - (callCounts.get(a[1]) || 0));
4389
+ return ["module", ...imports, ...funcs, ...others];
4390
+ };
3687
4391
  function optimize(ast, opts = true) {
3688
4392
  if (typeof ast === "string") ast = parse_default(ast);
3689
4393
  ast = clone2(ast);
3690
4394
  opts = normalize3(opts);
3691
- if (opts.fold) ast = fold(ast);
3692
- if (opts.identity) ast = identity(ast);
3693
- if (opts.strength) ast = strength(ast);
3694
- if (opts.branch) ast = branch(ast);
3695
- if (opts.propagate) ast = propagate(ast);
3696
- if (opts.inline) ast = inline(ast);
3697
- if (opts.deadcode) ast = deadcode(ast);
3698
- if (opts.locals) ast = localReuse(ast);
3699
- if (opts.treeshake) ast = treeshake(ast);
4395
+ for (let round = 0; round < 6; round++) {
4396
+ const before = ast;
4397
+ if (opts.stripmut) ast = stripmut(ast);
4398
+ if (opts.globals) ast = globals(ast);
4399
+ if (opts.fold) ast = fold(ast);
4400
+ if (opts.identity) ast = identity(ast);
4401
+ if (opts.peephole) ast = peephole(ast);
4402
+ if (opts.strength) ast = strength(ast);
4403
+ if (opts.branch) ast = branch(ast);
4404
+ if (opts.propagate) ast = propagate(ast);
4405
+ if (opts.inline) ast = inline(ast);
4406
+ if (opts.offset) ast = offset(ast);
4407
+ if (opts.unbranch) ast = unbranch(ast);
4408
+ if (opts.brif) ast = brif(ast);
4409
+ if (opts.foldarms) ast = foldarms(ast);
4410
+ if (opts.deadcode) ast = deadcode(ast);
4411
+ if (opts.vacuum) ast = vacuum(ast);
4412
+ if (opts.locals) ast = localReuse(ast);
4413
+ if (opts.dedupe) ast = dedupe(ast);
4414
+ if (opts.dedupTypes) ast = dedupTypes(ast);
4415
+ if (opts.packData) ast = packData(ast);
4416
+ if (opts.reorder) ast = reorder(ast);
4417
+ if (opts.treeshake) ast = treeshake(ast);
4418
+ if (opts.minifyImports) ast = minifyImports(ast);
4419
+ if (equal(before, ast)) break;
4420
+ }
3700
4421
  return ast;
3701
4422
  }
3702
4423