watr 4.6.10 → 4.7.0

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
@@ -1491,23 +1491,42 @@ function compile(nodes) {
1491
1491
  return !items.length ? [] : [kind, ...vec(count ? vec(items) : items.flat())];
1492
1492
  };
1493
1493
  const binMeta = () => {
1494
- const sections = [];
1494
+ const sections2 = [];
1495
1495
  for (const type in ctx.metadata) {
1496
1496
  const name2 = vec(str(`"metadata.code.${type}"`));
1497
1497
  const content = vec(ctx.metadata[type].map(
1498
1498
  ([funcIdx, instances]) => [...uleb(funcIdx), ...vec(instances.map(([pos, data]) => [...uleb(pos), ...vec(data)]))]
1499
1499
  ));
1500
- sections.push(0, ...vec([...name2, ...content]));
1500
+ sections2.push(0, ...vec([...name2, ...content]));
1501
1501
  }
1502
- return sections;
1502
+ return sections2;
1503
1503
  };
1504
1504
  const globalSection = bin(SECTION.global);
1505
1505
  const elemSection = bin(SECTION.elem);
1506
- const codeSection = bin(SECTION.code);
1506
+ const codeItems = ctx.code.filter(Boolean).map((item) => build[SECTION.code](item, ctx)).filter(Boolean);
1507
+ const codeSection = codeItems.length ? [SECTION.code, ...vec(vec(codeItems))] : [];
1507
1508
  const metaSection = binMeta();
1508
1509
  const dataSection = bin(SECTION.data);
1509
1510
  const stringsSection = ctx.strings.length ? [SECTION.strings, ...vec([0, ...vec(ctx.strings.map((s) => vec(s)))])] : [];
1510
- return Uint8Array.from([
1511
+ const sections = [
1512
+ bin(SECTION.custom),
1513
+ bin(SECTION.type),
1514
+ bin(SECTION.import),
1515
+ bin(SECTION.func),
1516
+ bin(SECTION.table),
1517
+ bin(SECTION.memory),
1518
+ bin(SECTION.tag),
1519
+ stringsSection,
1520
+ globalSection,
1521
+ bin(SECTION.export),
1522
+ bin(SECTION.start, false),
1523
+ elemSection,
1524
+ bin(SECTION.datacount, false),
1525
+ codeSection,
1526
+ metaSection,
1527
+ dataSection
1528
+ ];
1529
+ const wasm = Uint8Array.from([
1511
1530
  0,
1512
1531
  97,
1513
1532
  115,
@@ -1518,23 +1537,34 @@ function compile(nodes) {
1518
1537
  0,
1519
1538
  0,
1520
1539
  // version
1521
- ...bin(SECTION.custom),
1522
- ...bin(SECTION.type),
1523
- ...bin(SECTION.import),
1524
- ...bin(SECTION.func),
1525
- ...bin(SECTION.table),
1526
- ...bin(SECTION.memory),
1527
- ...bin(SECTION.tag),
1528
- ...stringsSection,
1529
- ...globalSection,
1530
- ...bin(SECTION.export),
1531
- ...bin(SECTION.start, false),
1532
- ...elemSection,
1533
- ...bin(SECTION.datacount, false),
1534
- ...codeSection,
1535
- ...metaSection,
1536
- ...dataSection
1540
+ ...sections.flat()
1537
1541
  ]);
1542
+ const md = metadataOffsets();
1543
+ if (md) wasm.metadata = md;
1544
+ return wasm;
1545
+ function metadataOffsets() {
1546
+ let has = false;
1547
+ for (const _ in ctx.metadata) has = true;
1548
+ if (!has) return;
1549
+ const out = {};
1550
+ const codeBase = 8 + sections.slice(0, sections.indexOf(codeSection)).reduce((n, s) => n + s.length, 0);
1551
+ const itemsBase = codeBase + codeSection.length - codeItems.reduce((n, it) => n + it.length, 0);
1552
+ const bodyBase = [];
1553
+ for (let i = 0, off = itemsBase; i < codeItems.length; i++) {
1554
+ bodyBase[i] = off + (ctx.codeSizePrefix?.[i] ?? 0);
1555
+ off += codeItems[i].length;
1556
+ }
1557
+ const importedFuncs = ctx.import.filter((imp) => imp[2][0] === "func").length;
1558
+ for (const type in ctx.metadata) {
1559
+ const entries = [];
1560
+ for (const [funcIdx, instances] of ctx.metadata[type]) {
1561
+ const base = bodyBase[funcIdx - importedFuncs];
1562
+ for (const [pos, data] of instances) entries.push([base + pos, data]);
1563
+ }
1564
+ out[type] = entries.sort((a, b) => a[0] - b[0]);
1565
+ }
1566
+ return out;
1567
+ }
1538
1568
  }
1539
1569
  var isIdx = (n) => n?.[0] === "$" || !isNaN(n);
1540
1570
  var isId = (n) => n?.[0] === "$";
@@ -1828,11 +1858,17 @@ var build = [
1828
1858
  }
1829
1859
  ctx.meta = {};
1830
1860
  const bytes = instr(body, ctx);
1831
- const funcIdx = ctx.import.filter((imp) => imp[2][0] === "func").length + codeIdx;
1832
- for (const type in ctx.meta) ((ctx.metadata ??= {})[type] ??= []).push([funcIdx, ctx.meta[type]]);
1833
1861
  let loctypes = ctx.local.slice(param.length).reduce((a, type) => (type == a[a.length - 1]?.[1] ? a[a.length - 1][0]++ : a.push([1, type]), a), []);
1862
+ const locals = vec(loctypes.map(([n, t]) => [...uleb(n), ...reftype(t, ctx)]));
1863
+ const funcIdx = ctx.import.filter((imp) => imp[2][0] === "func").length + codeIdx;
1864
+ for (const type in ctx.meta) {
1865
+ for (const inst of ctx.meta[type]) inst[0] += locals.length;
1866
+ ((ctx.metadata ??= {})[type] ??= []).push([funcIdx, ctx.meta[type]]);
1867
+ }
1834
1868
  ctx.local = ctx.block = ctx.meta = null;
1835
- return vec([...vec(loctypes.map(([n, t]) => [...uleb(n), ...reftype(t, ctx)])), ...bytes]);
1869
+ const item = vec([...locals, ...bytes]);
1870
+ (ctx.codeSizePrefix ??= [])[codeIdx] = item.length - locals.length - bytes.length;
1871
+ return item;
1836
1872
  },
1837
1873
  // (data (i32.const 0) "\aa" "\bb"?)
1838
1874
  // (data (memory ref) (offset (i32.const 0)) "\aa" "\bb"?)
@@ -2061,7 +2097,10 @@ var instr = (nodes, ctx) => {
2061
2097
  }
2062
2098
  bytes.push(...HANDLER[op](nodes, ctx, op));
2063
2099
  }
2064
- for (const [type, data] of meta) (ctx.meta[type] ??= []).push([out.length, data]);
2100
+ if (meta.length) {
2101
+ for (const [type, data] of meta) (ctx.meta[type] ??= []).push([out.length, data]);
2102
+ meta = [];
2103
+ }
2065
2104
  out.push(...bytes);
2066
2105
  }
2067
2106
  return out.push(11), out;
@@ -3053,6 +3092,30 @@ function polyfill(ast, opts = true) {
3053
3092
  }
3054
3093
 
3055
3094
  // src/optimize.js
3095
+ var MAX_PROP_ROUNDS = 6;
3096
+ var MAX_INLINE_ROUNDS = 16;
3097
+ var clone2 = (node) => Array.isArray(node) ? node.map(clone2) : node;
3098
+ var walk2 = (node, fn, parent, idx) => {
3099
+ fn(node, parent, idx);
3100
+ if (Array.isArray(node)) for (let i = 0; i < node.length; i++) walk2(node[i], fn, node, i);
3101
+ };
3102
+ var walkPost2 = (node, fn, parent, idx) => {
3103
+ if (Array.isArray(node)) for (let i = 0; i < node.length; i++) walkPost2(node[i], fn, node, i);
3104
+ const result = fn(node, parent, idx);
3105
+ if (result !== void 0 && parent) parent[idx] = result;
3106
+ return result !== void 0 ? result : node;
3107
+ };
3108
+ var resultType2 = (op) => {
3109
+ if (typeof op !== "string") return null;
3110
+ const dot = op.indexOf(".");
3111
+ if (dot < 0) return null;
3112
+ const prefix = op.slice(0, dot);
3113
+ const scalar = prefix === "i32" || prefix === "i64" || prefix === "f32" || prefix === "f64";
3114
+ if (scalar && /^(eqz?|ne|[lg][te])(_[su])?$/.test(op.slice(dot + 1))) return "i32";
3115
+ if (scalar || prefix === "v128") return prefix;
3116
+ if (op === "memory.size" || op === "memory.grow") return "i32";
3117
+ return null;
3118
+ };
3056
3119
  var binarySize = (ast) => {
3057
3120
  try {
3058
3121
  return compile(ast).length;
@@ -3169,7 +3232,7 @@ var treeshake = (ast) => {
3169
3232
  markFunc(ref);
3170
3233
  }
3171
3234
  for (const elem of elems) {
3172
- walk(elem, (n) => {
3235
+ walk2(elem, (n) => {
3173
3236
  if (Array.isArray(n) && n[0] === "ref.func") markFunc(n[1]);
3174
3237
  else if (typeof n === "string" && n[0] === "$") markFunc(n);
3175
3238
  });
@@ -3191,7 +3254,7 @@ var treeshake = (ast) => {
3191
3254
  if (entry.scanned) continue;
3192
3255
  entry.scanned = true;
3193
3256
  if (entry.isImport) continue;
3194
- walk(entry.node, (n) => {
3257
+ walk2(entry.node, (n) => {
3195
3258
  if (!Array.isArray(n)) {
3196
3259
  if (typeof n === "string" && n[0] === "$") markFunc(n);
3197
3260
  return;
@@ -3237,16 +3300,53 @@ var treeshake = (ast) => {
3237
3300
  var roundEven = (x) => x - Math.floor(x) !== 0.5 ? Math.round(x) : 2 * Math.round(x / 2);
3238
3301
  var _rb8 = new ArrayBuffer(8);
3239
3302
  var _rf64 = new Float64Array(_rb8);
3240
- var _ri64 = new BigInt64Array(_rb8);
3303
+ var _ru32 = new Uint32Array(_rb8);
3241
3304
  var _rb4 = new ArrayBuffer(4);
3242
3305
  var _rf32 = new Float32Array(_rb4);
3243
3306
  var _ri32 = new Int32Array(_rb4);
3307
+ var _hex8 = (u) => (u >>> 0).toString(16).padStart(8, "0");
3308
+ var _twosComp16 = (mag) => {
3309
+ let out = "", carry = 1;
3310
+ for (let i = 15; i >= 0; i--) {
3311
+ const d = 15 - parseInt(mag[i], 16) + carry;
3312
+ out = (d & 15).toString(16) + out;
3313
+ carry = d >> 4;
3314
+ }
3315
+ return out;
3316
+ };
3317
+ var _i64Hex16 = (v) => {
3318
+ const h = v.toString(16);
3319
+ return h[0] === "-" ? _twosComp16(h.slice(1).padStart(16, "0")) : h.padStart(16, "0");
3320
+ };
3321
+ var ZERO64 = "0x0000000000000000";
3322
+ var ONE64 = "0x0000000000000001";
3323
+ var NEG164 = "0xffffffffffffffff";
3324
+ var _i64Canon = (val) => {
3325
+ if (typeof val === "string") {
3326
+ const s = val.replaceAll("_", "");
3327
+ if (s.length === 18 && s[1] === "x") return "0x" + s.slice(2).toLowerCase();
3328
+ const neg = s[0] === "-", mag = s[0] === "-" || s[0] === "+" ? s.slice(1) : s;
3329
+ return "0x" + _i64Hex16(neg ? -BigInt(mag) : BigInt(mag));
3330
+ }
3331
+ if (typeof val === "bigint") return "0x" + _i64Hex16(BigInt(String(val)));
3332
+ return "0x" + _i64Hex16(BigInt(Math.trunc(val) || 0));
3333
+ };
3334
+ var _sb = (h) => (parseInt(h[2], 16) ^ 8).toString(16) + h.slice(3);
3335
+ var _i64Lo = (h) => parseInt(h.slice(10), 16) | 0;
3336
+ var _i64HiU = (h) => parseInt(h.slice(2, 10), 16) >>> 0;
3337
+ var _i64Arith = (r) => r == null ? null : "0x" + _i64Hex16(r);
3338
+ var _sgn = (h) => {
3339
+ let v = BigInt(h);
3340
+ if (v > 0x7fffffffffffffffn) v = v - 0x8000000000000000n - 0x8000000000000000n;
3341
+ return v;
3342
+ };
3244
3343
  var i64FromF64 = (x) => {
3245
3344
  _rf64[0] = x;
3246
- return _ri64[0];
3345
+ return "0x" + _hex8(_ru32[1]) + _hex8(_ru32[0]);
3247
3346
  };
3248
- var f64FromI64 = (x) => {
3249
- _ri64[0] = BigInt.asIntN(64, x);
3347
+ var f64FromI64 = (h) => {
3348
+ _ru32[1] = parseInt(h.slice(2, 10), 16);
3349
+ _ru32[0] = parseInt(h.slice(10), 16);
3250
3350
  return _rf64[0];
3251
3351
  };
3252
3352
  var i32FromF32 = (x) => {
@@ -3259,8 +3359,8 @@ var f32FromI32 = (x) => {
3259
3359
  };
3260
3360
  var i32c = (fn) => (a, b) => fn(a, b) ? 1 : 0;
3261
3361
  var u32c = (fn) => (a, b) => fn(a >>> 0, b >>> 0) ? 1 : 0;
3262
- var i64c = (fn) => (a, b) => fn(a, b) ? 1 : 0;
3263
- var u64c = (fn) => (a, b) => fn(BigInt.asUintN(64, a), BigInt.asUintN(64, b)) ? 1 : 0;
3362
+ var i64c = (fn) => (a, b) => fn(_sb(a), _sb(b)) ? 1 : 0;
3363
+ var u64c = (fn) => (a, b) => fn(a, b) ? 1 : 0;
3264
3364
  var FOLDABLE = {
3265
3365
  // i32 arithmetic
3266
3366
  "i32.add": (a, b) => a + b | 0,
@@ -3305,25 +3405,43 @@ var FOLDABLE = {
3305
3405
  }
3306
3406
  return c;
3307
3407
  },
3308
- "i32.wrap_i64": (a) => Number(BigInt.asIntN(32, a)),
3408
+ "i32.wrap_i64": (a) => _i64Lo(a),
3309
3409
  "i32.extend8_s": (a) => a << 24 >> 24,
3310
3410
  "i32.extend16_s": (a) => a << 16 >> 16,
3311
- // i64 (using BigInt)
3312
- "i64.add": (a, b) => BigInt.asIntN(64, a + b),
3313
- "i64.sub": (a, b) => BigInt.asIntN(64, a - b),
3314
- "i64.mul": (a, b) => BigInt.asIntN(64, a * b),
3315
- "i64.div_s": (a, b) => b !== 0n ? BigInt.asIntN(64, a / b) : null,
3316
- "i64.div_u": (a, b) => b !== 0n ? BigInt.asUintN(64, BigInt.asUintN(64, a) / BigInt.asUintN(64, b)) : null,
3317
- "i64.rem_s": (a, b) => b !== 0n ? BigInt.asIntN(64, a % b) : null,
3318
- "i64.rem_u": (a, b) => b !== 0n ? BigInt.asUintN(64, BigInt.asUintN(64, a) % BigInt.asUintN(64, b)) : null,
3319
- "i64.and": (a, b) => BigInt.asIntN(64, a & b),
3320
- "i64.or": (a, b) => BigInt.asIntN(64, a | b),
3321
- "i64.xor": (a, b) => BigInt.asIntN(64, a ^ b),
3322
- "i64.shl": (a, b) => BigInt.asIntN(64, a << (b & 63n)),
3323
- "i64.shr_s": (a, b) => BigInt.asIntN(64, a >> (b & 63n)),
3324
- "i64.shr_u": (a, b) => BigInt.asUintN(64, BigInt.asUintN(64, a) >> (b & 63n)),
3325
- "i64.eq": i64c((a, b) => a === b),
3326
- "i64.ne": i64c((a, b) => a !== b),
3411
+ // i64 hex-string in, hex-string out, BOTH-WORLDS-EXACT arithmetic.
3412
+ // BigInts construct locally (in-expression kernel kind erasure never
3413
+ // applies), but two further kernel facts shape every folder:
3414
+ // (1) the kernel's BigInt is the mod-2^64 i64 CARRIER: BigInt('0xffff…')
3415
+ // arrives NEGATIVE there, so sign-sensitive ops (>>, /, %, unsigned
3416
+ // division) diverge unless the value is sign-canonicalized first;
3417
+ // (2) BigInt.asIntN/asUintN are unfaithful in-kernel never used.
3418
+ // Ring ops {+,−,×,&,|,^,<<} are mod-2^64-compatible: compute then mask with
3419
+ // `& 0xffffffffffffffffn` (native: the wrap; kernel: AND with −1 ≡ no-op).
3420
+ // `_sgn` yields the SIGNED value in both worlds (the subtract arm is dead
3421
+ // in-kernel same dead-arm trick as slebSize). shr_u is pure u32-half
3422
+ // number math. div_u/rem_u fold only below 2^63 (signed==unsigned there);
3423
+ // above, they skip — sound degradation, never a wrong constant.
3424
+ "i64.add": (a, b) => _i64Arith(BigInt(a) + BigInt(b) & 0xffffffffffffffffn),
3425
+ "i64.sub": (a, b) => _i64Arith(BigInt(a) - BigInt(b) & 0xffffffffffffffffn),
3426
+ "i64.mul": (a, b) => _i64Arith(BigInt(a) * BigInt(b) & 0xffffffffffffffffn),
3427
+ "i64.div_s": (a, b) => b !== ZERO64 && !(a === "0x8000000000000000" && b === NEG164) ? _i64Arith(_sgn(a) / _sgn(b) & 0xffffffffffffffffn) : null,
3428
+ "i64.div_u": (a, b) => b !== ZERO64 && !(_i64HiU(a) >>> 31) && !(_i64HiU(b) >>> 31) ? _i64Arith(BigInt(a) / BigInt(b)) : null,
3429
+ "i64.rem_s": (a, b) => b !== ZERO64 ? _i64Arith(_sgn(a) % _sgn(b) & 0xffffffffffffffffn) : null,
3430
+ "i64.rem_u": (a, b) => b !== ZERO64 && !(_i64HiU(a) >>> 31) && !(_i64HiU(b) >>> 31) ? _i64Arith(BigInt(a) % BigInt(b)) : null,
3431
+ "i64.and": (a, b) => _i64Arith(BigInt(a) & BigInt(b) & 0xffffffffffffffffn),
3432
+ "i64.or": (a, b) => _i64Arith((BigInt(a) | BigInt(b)) & 0xffffffffffffffffn),
3433
+ "i64.xor": (a, b) => _i64Arith((BigInt(a) ^ BigInt(b)) & 0xffffffffffffffffn),
3434
+ "i64.shl": (a, b) => _i64Arith(BigInt(a) << (BigInt(b) & 63n) & 0xffffffffffffffffn),
3435
+ "i64.shr_s": (a, b) => _i64Arith(_sgn(a) >> (BigInt(b) & 63n) & 0xffffffffffffffffn),
3436
+ "i64.shr_u": (a, b) => {
3437
+ const s = parseInt(b.slice(10), 16) & 63;
3438
+ const hi = _i64HiU(a), lo = parseInt(a.slice(10), 16) >>> 0;
3439
+ const rh = s >= 32 ? 0 : hi >>> s;
3440
+ const rl = s === 0 ? lo : s >= 32 ? hi >>> s - 32 : (lo >>> s | hi << 32 - s) >>> 0;
3441
+ return "0x" + _hex8(rh) + _hex8(rl);
3442
+ },
3443
+ "i64.eq": (a, b) => a === b ? 1 : 0,
3444
+ "i64.ne": (a, b) => a !== b ? 1 : 0,
3327
3445
  "i64.lt_s": i64c((a, b) => a < b),
3328
3446
  "i64.lt_u": u64c((a, b) => a < b),
3329
3447
  "i64.gt_s": i64c((a, b) => a > b),
@@ -3332,12 +3450,21 @@ var FOLDABLE = {
3332
3450
  "i64.le_u": u64c((a, b) => a <= b),
3333
3451
  "i64.ge_s": i64c((a, b) => a >= b),
3334
3452
  "i64.ge_u": u64c((a, b) => a >= b),
3335
- "i64.eqz": (a) => a === 0n ? 1 : 0,
3336
- "i64.extend_i32_s": (a) => BigInt(a),
3337
- "i64.extend_i32_u": (a) => BigInt(a >>> 0),
3338
- "i64.extend8_s": (a) => BigInt.asIntN(64, BigInt.asIntN(8, a)),
3339
- "i64.extend16_s": (a) => BigInt.asIntN(64, BigInt.asIntN(16, a)),
3340
- "i64.extend32_s": (a) => BigInt.asIntN(64, BigInt.asIntN(32, a)),
3453
+ "i64.eqz": (a) => a === ZERO64 ? 1 : 0,
3454
+ "i64.extend_i32_s": (a) => "0x" + _hex8(a >> 31) + _hex8(a),
3455
+ "i64.extend_i32_u": (a) => "0x00000000" + _hex8(a),
3456
+ "i64.extend8_s": (a) => {
3457
+ const v = _i64Lo(a) << 24 >> 24;
3458
+ return "0x" + _hex8(v >> 31) + _hex8(v);
3459
+ },
3460
+ "i64.extend16_s": (a) => {
3461
+ const v = _i64Lo(a) << 16 >> 16;
3462
+ return "0x" + _hex8(v >> 31) + _hex8(v);
3463
+ },
3464
+ "i64.extend32_s": (a) => {
3465
+ const v = _i64Lo(a);
3466
+ return "0x" + _hex8(v >> 31) + _hex8(v);
3467
+ },
3341
3468
  // f32/f64 (NaN/precision-aware via Math.fround)
3342
3469
  "f32.add": (a, b) => Math.fround(a + b),
3343
3470
  "f32.sub": (a, b) => Math.fround(a - b),
@@ -3369,19 +3496,33 @@ var FOLDABLE = {
3369
3496
  // Numeric conversions (value-preserving where representable)
3370
3497
  "f32.convert_i32_s": (a) => Math.fround(a | 0),
3371
3498
  "f32.convert_i32_u": (a) => Math.fround(a >>> 0),
3372
- "f32.convert_i64_s": (a) => Math.fround(Number(BigInt.asIntN(64, a))),
3373
- "f32.convert_i64_u": (a) => Math.fround(Number(BigInt.asUintN(64, a))),
3499
+ // (hi|0)·2^32 + lo is the exact signed value with ONE rounding at the add —
3500
+ // correct f64 conversion semantics, pure number math (kernel-safe).
3501
+ "f32.convert_i64_s": (a) => Math.fround((_i64HiU(a) | 0) * 4294967296 + parseInt(a.slice(10), 16)),
3502
+ "f32.convert_i64_u": (a) => Math.fround(_i64HiU(a) * 4294967296 + parseInt(a.slice(10), 16)),
3374
3503
  "f64.convert_i32_s": (a) => a | 0,
3375
3504
  "f64.convert_i32_u": (a) => a >>> 0,
3376
- "f64.convert_i64_s": (a) => Number(BigInt.asIntN(64, a)),
3377
- "f64.convert_i64_u": (a) => Number(BigInt.asUintN(64, a)),
3505
+ "f64.convert_i64_s": (a) => (_i64HiU(a) | 0) * 4294967296 + parseInt(a.slice(10), 16),
3506
+ "f64.convert_i64_u": (a) => _i64HiU(a) * 4294967296 + parseInt(a.slice(10), 16),
3378
3507
  "f32.demote_f64": (a) => Math.fround(a),
3379
3508
  "f64.promote_f32": (a) => Math.fround(a)
3380
3509
  };
3510
+ var _nanBitsHex = (s) => {
3511
+ const i = s?.indexOf?.("nan");
3512
+ if (i < 0 || i == null) return null;
3513
+ const tail = s.slice(i + 4).replaceAll("_", "");
3514
+ const payload = s[i + 3] === ":" && tail !== "canonical" && tail !== "arithmetic" ? BigInt(tail) : 0x8000000000000n;
3515
+ const h = payload.toString(16).padStart(16, "0");
3516
+ const hi = (parseInt(h.slice(0, 8), 16) | 2146435072 | (s[0] === "-" ? 2147483648 : 0)) >>> 0;
3517
+ return "0x" + _hex8(hi) + h.slice(8);
3518
+ };
3381
3519
  var _parseNanF64 = (s, i = s?.indexOf?.("nan")) => {
3382
3520
  if (i < 0 || i == null) return null;
3383
- let tail = s.slice(i + 4).replaceAll("_", ""), bits = s[i + 3] === ":" && tail !== "canonical" && tail !== "arithmetic" ? BigInt(tail) : 0x8000000000000n;
3384
- _ri64[0] = BigInt.asIntN(64, bits | 0x7ff0000000000000n | (s[0] === "-" ? 1n << 63n : 0n));
3521
+ const tail = s.slice(i + 4).replaceAll("_", "");
3522
+ const payload = s[i + 3] === ":" && tail !== "canonical" && tail !== "arithmetic" ? BigInt(tail) : 0x8000000000000n;
3523
+ const h = payload.toString(16).padStart(16, "0");
3524
+ _ru32[1] = (parseInt(h.slice(0, 8), 16) | 2146435072 | (s[0] === "-" ? 2147483648 : 0)) >>> 0;
3525
+ _ru32[0] = parseInt(h.slice(8), 16);
3385
3526
  return _rf64[0];
3386
3527
  };
3387
3528
  var _parseNanF32 = (s, i = s?.indexOf?.("nan")) => {
@@ -3393,43 +3534,67 @@ var _parseNanF32 = (s, i = s?.indexOf?.("nan")) => {
3393
3534
  var getConst = (node) => {
3394
3535
  if (!Array.isArray(node) || node.length !== 2) return null;
3395
3536
  const [op, val] = node;
3396
- if (op === "i32.const") return { type: "i32", value: (typeof val === "string" ? i32.parse(val) : val) | 0 };
3397
- if (op === "i64.const") return { type: "i64", value: typeof val === "string" ? i64.parse(val) : BigInt(val) };
3537
+ if (op === "i32.const") return { type: "i32", value: (typeof val === "string" ? parseInt(val.replaceAll("_", "")) : val) | 0 };
3538
+ if (op === "i64.const") return { type: "i64", value: _i64Canon(val) };
3398
3539
  if (op === "f32.const") {
3399
3540
  const n = _parseNanF32(val);
3400
3541
  return { type: "f32", value: n !== null ? n : Math.fround(Number(val)) };
3401
3542
  }
3402
3543
  if (op === "f64.const") {
3403
3544
  const n = _parseNanF64(val);
3404
- return { type: "f64", value: n !== null ? n : Number(val) };
3545
+ const v = n !== null ? n : Number(val);
3546
+ return { type: "f64", value: Number.isNaN(v) ? NaN : v };
3405
3547
  }
3406
3548
  return null;
3407
3549
  };
3408
3550
  var makeConst = (type, value) => {
3409
3551
  if (type === "i32") return ["i32.const", value | 0];
3410
- if (type === "i64") return ["i64.const", value];
3411
- if (type === "f32") return ["f32.const", Math.fround(value)];
3412
- if (type === "f64") return ["f64.const", value];
3552
+ if (type === "i64") return ["i64.const", typeof value === "number" ? value : _i64Canon(value)];
3553
+ if (type === "f32") {
3554
+ const v = Math.fround(value);
3555
+ return ["f32.const", Number.isNaN(v) ? "nan" : v];
3556
+ }
3557
+ if (type === "f64") return ["f64.const", Number.isNaN(value) ? "nan" : value];
3413
3558
  return null;
3414
3559
  };
3415
3560
  var fold = (ast) => {
3416
- return walkPost(ast, (node) => {
3561
+ return walkPost2(ast, (node) => {
3417
3562
  if (!Array.isArray(node)) return;
3418
3563
  const fn = FOLDABLE[node[0]];
3419
3564
  if (!fn) return;
3420
- if (fn.length === 1 && node.length === 2) {
3565
+ if (node.length === 2) {
3566
+ if (node[0] === "i64.reinterpret_f64") {
3567
+ const inner = node[1];
3568
+ if (Array.isArray(inner) && inner.length === 2 && inner[0] === "f64.const" && typeof inner[1] === "string") {
3569
+ const bits = _nanBitsHex(inner[1]);
3570
+ if (bits) return ["i64.const", bits];
3571
+ }
3572
+ }
3573
+ if (node[0] === "f64.reinterpret_i64") {
3574
+ const c = getConst(node[1]);
3575
+ if (c && c.type === "i64") {
3576
+ const h = c.value.slice(2);
3577
+ const hi = parseInt(h.slice(0, 8), 16) >>> 0;
3578
+ const lo = parseInt(h.slice(8), 16) >>> 0;
3579
+ const isNaN64 = (hi & 2146435072) === 2146435072 && ((hi & 1048575) !== 0 || lo !== 0);
3580
+ if (isNaN64) return [
3581
+ "f64.const",
3582
+ ((hi & 2147483648) !== 0 ? "-" : "") + "nan:0x" + (hi & 1048575).toString(16).padStart(5, "0") + h.slice(8)
3583
+ ];
3584
+ }
3585
+ }
3421
3586
  const a = getConst(node[1]);
3422
3587
  if (!a) return;
3423
3588
  const r = fn(a.value);
3424
- if (r === null) return;
3425
- return makeConst(resultType(node[0]), r);
3589
+ if (r === null || r === void 0) return;
3590
+ return makeConst(resultType2(node[0]), r);
3426
3591
  }
3427
- if (fn.length === 2 && node.length === 3) {
3592
+ if (node.length === 3) {
3428
3593
  const a = getConst(node[1]), b = getConst(node[2]);
3429
3594
  if (!a || !b) return;
3430
3595
  const r = fn(a.value, b.value);
3431
- if (r === null) return;
3432
- return makeConst(resultType(node[0]), r);
3596
+ if (r === null || r === void 0) return;
3597
+ return makeConst(resultType2(node[0]), r);
3433
3598
  }
3434
3599
  });
3435
3600
  };
@@ -3443,39 +3608,39 @@ var rightIdentity = (neutral) => (a, b) => getConst(b)?.value === neutral ? a :
3443
3608
  var IDENTITIES = {
3444
3609
  // x + 0 → x, 0 + x → x
3445
3610
  "i32.add": commutativeIdentity(0),
3446
- "i64.add": commutativeIdentity(0n),
3611
+ "i64.add": commutativeIdentity(ZERO64),
3447
3612
  // x - 0 → x
3448
3613
  "i32.sub": rightIdentity(0),
3449
- "i64.sub": rightIdentity(0n),
3614
+ "i64.sub": rightIdentity(ZERO64),
3450
3615
  // x * 1 → x, 1 * x → x
3451
3616
  "i32.mul": commutativeIdentity(1),
3452
- "i64.mul": commutativeIdentity(1n),
3617
+ "i64.mul": commutativeIdentity(ONE64),
3453
3618
  // x / 1 → x
3454
3619
  "i32.div_s": rightIdentity(1),
3455
3620
  "i32.div_u": rightIdentity(1),
3456
- "i64.div_s": rightIdentity(1n),
3457
- "i64.div_u": rightIdentity(1n),
3621
+ "i64.div_s": rightIdentity(ONE64),
3622
+ "i64.div_u": rightIdentity(ONE64),
3458
3623
  // x & -1 → x, -1 & x → x (all bits set)
3459
3624
  "i32.and": commutativeIdentity(-1),
3460
- "i64.and": commutativeIdentity(-1n),
3625
+ "i64.and": commutativeIdentity(NEG164),
3461
3626
  // x | 0 → x, 0 | x → x
3462
3627
  "i32.or": commutativeIdentity(0),
3463
- "i64.or": commutativeIdentity(0n),
3628
+ "i64.or": commutativeIdentity(ZERO64),
3464
3629
  // x ^ 0 → x, 0 ^ x → x
3465
3630
  "i32.xor": commutativeIdentity(0),
3466
- "i64.xor": commutativeIdentity(0n),
3631
+ "i64.xor": commutativeIdentity(ZERO64),
3467
3632
  // x << 0 → x, x >> 0 → x
3468
3633
  "i32.shl": rightIdentity(0),
3469
3634
  "i32.shr_s": rightIdentity(0),
3470
3635
  "i32.shr_u": rightIdentity(0),
3471
- "i64.shl": rightIdentity(0n),
3472
- "i64.shr_s": rightIdentity(0n),
3473
- "i64.shr_u": rightIdentity(0n)
3636
+ "i64.shl": rightIdentity(ZERO64),
3637
+ "i64.shr_s": rightIdentity(ZERO64),
3638
+ "i64.shr_u": rightIdentity(ZERO64)
3474
3639
  // f + 0 → x (careful with -0.0, skip for floats)
3475
3640
  // f * 1 → x (careful with NaN, skip for floats)
3476
3641
  };
3477
3642
  var identity = (ast) => {
3478
- return walkPost(ast, (node) => {
3643
+ return walkPost2(ast, (node) => {
3479
3644
  if (!Array.isArray(node) || node.length !== 3) return;
3480
3645
  const fn = IDENTITIES[node[0]];
3481
3646
  if (!fn) return;
@@ -3485,7 +3650,7 @@ var identity = (ast) => {
3485
3650
  });
3486
3651
  };
3487
3652
  var strength = (ast) => {
3488
- return walkPost(ast, (node) => {
3653
+ return walkPost2(ast, (node) => {
3489
3654
  if (!Array.isArray(node) || node.length !== 3) return;
3490
3655
  const [op, a, b] = node;
3491
3656
  if (op === "i32.mul") {
@@ -3501,16 +3666,12 @@ var strength = (ast) => {
3501
3666
  }
3502
3667
  }
3503
3668
  if (op === "i64.mul") {
3504
- const cb = getConst(b);
3505
- if (cb && cb.value > 0n && (cb.value & cb.value - 1n) === 0n) {
3506
- const shift = BigInt(cb.value.toString(2).length - 1);
3507
- return ["i64.shl", a, ["i64.const", shift]];
3508
- }
3509
- const ca = getConst(a);
3510
- if (ca && ca.value > 0n && (ca.value & ca.value - 1n) === 0n) {
3511
- const shift = BigInt(ca.value.toString(2).length - 1);
3512
- return ["i64.shl", b, ["i64.const", shift]];
3513
- }
3669
+ const cb = getConst(b), vb = cb ? BigInt(cb.value) : null;
3670
+ if (vb != null && vb > 0n && (vb & vb - 1n) === 0n)
3671
+ return ["i64.shl", a, ["i64.const", vb.toString(2).length - 1]];
3672
+ const ca = getConst(a), va = ca ? BigInt(ca.value) : null;
3673
+ if (va != null && va > 0n && (va & va - 1n) === 0n)
3674
+ return ["i64.shl", b, ["i64.const", va.toString(2).length - 1]];
3514
3675
  }
3515
3676
  if (op === "i32.div_u") {
3516
3677
  const cb = getConst(b);
@@ -3520,11 +3681,9 @@ var strength = (ast) => {
3520
3681
  }
3521
3682
  }
3522
3683
  if (op === "i64.div_u") {
3523
- const cb = getConst(b);
3524
- if (cb && cb.value > 0n && (cb.value & cb.value - 1n) === 0n) {
3525
- const shift = BigInt(cb.value.toString(2).length - 1);
3526
- return ["i64.shr_u", a, ["i64.const", shift]];
3527
- }
3684
+ const cb = getConst(b), vb = cb ? BigInt(cb.value) : null;
3685
+ if (vb != null && vb > 0n && (vb & vb - 1n) === 0n)
3686
+ return ["i64.shr_u", a, ["i64.const", vb.toString(2).length - 1]];
3528
3687
  }
3529
3688
  if (op === "i32.rem_u") {
3530
3689
  const cb = getConst(b);
@@ -3533,24 +3692,25 @@ var strength = (ast) => {
3533
3692
  }
3534
3693
  }
3535
3694
  if (op === "i64.rem_u") {
3536
- const cb = getConst(b);
3537
- if (cb && cb.value > 0n && (cb.value & cb.value - 1n) === 0n) {
3538
- return ["i64.and", a, ["i64.const", cb.value - 1n]];
3539
- }
3695
+ const cb = getConst(b), vb = cb ? BigInt(cb.value) : null;
3696
+ if (vb != null && vb > 0n && (vb & vb - 1n) === 0n)
3697
+ return ["i64.and", a, ["i64.const", "0x" + _i64Hex16(vb - 1n)]];
3540
3698
  }
3541
3699
  });
3542
3700
  };
3543
3701
  var branch = (ast) => {
3544
- return walkPost(ast, (node) => {
3702
+ return walkPost2(ast, (node) => {
3545
3703
  if (!Array.isArray(node)) return;
3546
3704
  const op = node[0];
3547
3705
  if (op === "if") {
3548
- const { cond, thenBranch, elseBranch } = parseIf(node);
3706
+ const { condIdx, cond, thenBranch, elseBranch } = parseIf(node);
3549
3707
  const c = getConst(cond);
3550
3708
  if (!c) return;
3551
- const taken = c.value !== 0 && c.value !== 0n ? thenBranch : elseBranch;
3709
+ const taken = c.value !== 0 && c.value !== ZERO64 ? thenBranch : elseBranch;
3552
3710
  if (taken && taken.length > 1) {
3553
3711
  const contents = taken.slice(1);
3712
+ const blockType = node.slice(1, condIdx).filter((p) => Array.isArray(p) && (p[0] === "result" || p[0] === "param"));
3713
+ if (blockType.length) return ["block", ...blockType, ...contents];
3554
3714
  return contents.length === 1 ? contents[0] : ["block", ...contents];
3555
3715
  }
3556
3716
  return ["nop"];
@@ -3559,21 +3719,196 @@ var branch = (ast) => {
3559
3719
  const cond = node[node.length - 1];
3560
3720
  const c = getConst(cond);
3561
3721
  if (!c) return;
3562
- if (c.value === 0 || c.value === 0n) return ["nop"];
3722
+ if (c.value === 0 || c.value === ZERO64) return ["nop"];
3563
3723
  return ["br", node[1]];
3564
3724
  }
3565
3725
  if (op === "select" && node.length >= 4) {
3566
3726
  const cond = node[node.length - 1];
3567
3727
  const c = getConst(cond);
3568
3728
  if (!c) return;
3569
- if (c.value === 0 || c.value === 0n) return node[2];
3729
+ if (c.value === 0 || c.value === ZERO64) return node[2];
3570
3730
  return node[1];
3571
3731
  }
3572
3732
  });
3573
3733
  };
3734
+ var guardRefine = (ast) => {
3735
+ if (Array.isArray(ast)) {
3736
+ for (const node of ast) if (Array.isArray(node) && node[0] === "func") refineGuards(node);
3737
+ }
3738
+ return ast;
3739
+ };
3740
+ var EMPTY_SET = /* @__PURE__ */ new Set();
3741
+ var refineGuards = (fn) => {
3742
+ const ptrAlias = /* @__PURE__ */ new Map();
3743
+ const tagAlias = /* @__PURE__ */ new Map();
3744
+ const eqFact = /* @__PURE__ */ new Map();
3745
+ const neFact = /* @__PURE__ */ new Map();
3746
+ const intVal = (n) => {
3747
+ if (!Array.isArray(n) || n.length !== 2 || n[0] !== "i32.const" && n[0] !== "i64.const") return null;
3748
+ const v = typeof n[1] === "string" ? Number(n[1].replaceAll("_", "")) : Number(n[1]);
3749
+ return Number.isFinite(v) ? v : null;
3750
+ };
3751
+ const i32Val = (n) => Array.isArray(n) && n[0] === "i32.const" ? intVal(n) : null;
3752
+ const ptrSrc = (n) => {
3753
+ if (!Array.isArray(n)) return null;
3754
+ if (n[0] === "i64.reinterpret_f64" && Array.isArray(n[1]) && n[1][0] === "local.get" && typeof n[1][1] === "string") return n[1][1];
3755
+ if (n[0] === "local.get" && typeof n[1] === "string") return ptrAlias.get(n[1]) ?? null;
3756
+ return null;
3757
+ };
3758
+ const tagSrc = (n) => {
3759
+ if (!Array.isArray(n)) return null;
3760
+ const op = n[0];
3761
+ if (op === "local.get" && typeof n[1] === "string") return tagAlias.get(n[1]) ?? null;
3762
+ if (op === "call" && n[1] === "$__ptr_type" && n.length === 3) return ptrSrc(n[2]);
3763
+ const shifted = (m) => Array.isArray(m) && m[0] === "i64.shr_u" && intVal(m[2]) === 47 ? ptrSrc(m[1]) : null;
3764
+ if (op === "i32.and" && n.length === 3) {
3765
+ const [a, b] = i32Val(n[2]) === 15 ? [n[1], null] : i32Val(n[1]) === 15 ? [n[2], null] : [null, null];
3766
+ if (a && Array.isArray(a) && a[0] === "i32.wrap_i64") return shifted(a[1]);
3767
+ }
3768
+ if (op === "i32.wrap_i64" && Array.isArray(n[1]) && n[1][0] === "i64.and") {
3769
+ const m = n[1];
3770
+ if (intVal(m[2]) === 15) return shifted(m[1]);
3771
+ if (intVal(m[1]) === 15) return shifted(m[2]);
3772
+ }
3773
+ return null;
3774
+ };
3775
+ const killLocal = (name2) => {
3776
+ ptrAlias.delete(name2);
3777
+ tagAlias.delete(name2);
3778
+ eqFact.delete(name2);
3779
+ neFact.delete(name2);
3780
+ for (const [p, x] of ptrAlias) if (x === name2) ptrAlias.delete(p);
3781
+ for (const [t, x] of tagAlias) if (x === name2) tagAlias.delete(t);
3782
+ };
3783
+ const writesMemo = /* @__PURE__ */ new Map();
3784
+ const writesOf = (n) => {
3785
+ if (!Array.isArray(n)) return EMPTY_SET;
3786
+ let s = writesMemo.get(n);
3787
+ if (s) return s;
3788
+ s = /* @__PURE__ */ new Set();
3789
+ if ((n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string") s.add(n[1]);
3790
+ for (let i = 1; i < n.length; i++) for (const w of writesOf(n[i])) s.add(w);
3791
+ writesMemo.set(n, s);
3792
+ return s;
3793
+ };
3794
+ const snap = () => [new Map(eqFact), new Map([...neFact].map(([k, s]) => [k, new Set(s)])), new Map(ptrAlias), new Map(tagAlias)];
3795
+ const reset = (m, src) => {
3796
+ m.clear();
3797
+ for (const [k, v] of src) m.set(k, v);
3798
+ };
3799
+ const restore = ([e, n, p, t]) => {
3800
+ reset(eqFact, e);
3801
+ reset(neFact, n);
3802
+ reset(ptrAlias, p);
3803
+ reset(tagAlias, t);
3804
+ };
3805
+ const condFacts = (cond, sense, out) => {
3806
+ if (!Array.isArray(cond)) return out;
3807
+ const op = cond[0];
3808
+ if (op === "i32.eqz") return condFacts(cond[1], !sense, out);
3809
+ if (op === "i32.and" && sense && cond.length === 3) {
3810
+ condFacts(cond[1], true, out);
3811
+ condFacts(cond[2], true, out);
3812
+ return out;
3813
+ }
3814
+ if (op === "i32.or" && !sense && cond.length === 3) {
3815
+ condFacts(cond[1], false, out);
3816
+ condFacts(cond[2], false, out);
3817
+ return out;
3818
+ }
3819
+ if ((op === "i32.eq" || op === "i32.ne") && cond.length === 3) {
3820
+ let x2 = tagSrc(cond[1]), k = i32Val(cond[2]);
3821
+ if (x2 == null || k == null) {
3822
+ x2 = tagSrc(cond[2]);
3823
+ k = i32Val(cond[1]);
3824
+ }
3825
+ if (x2 != null && k != null) out.push({ x: x2, k, eq: op === "i32.eq" === sense });
3826
+ return out;
3827
+ }
3828
+ const x = tagSrc(cond);
3829
+ if (x != null) out.push({ x, k: 0, eq: !sense });
3830
+ return out;
3831
+ };
3832
+ const addFacts = (fs) => {
3833
+ for (const { x, k, eq } of fs) {
3834
+ if (eq) eqFact.set(x, k);
3835
+ else {
3836
+ let s = neFact.get(x);
3837
+ if (!s) neFact.set(x, s = /* @__PURE__ */ new Set());
3838
+ s.add(k);
3839
+ }
3840
+ }
3841
+ };
3842
+ const walkSeq = (node, parent, idx) => {
3843
+ if (!Array.isArray(node)) return;
3844
+ const op = node[0];
3845
+ if (op === "local.set" || op === "local.tee") {
3846
+ if (Array.isArray(node[2])) walkSeq(node[2], node, 2);
3847
+ const name2 = node[1];
3848
+ if (typeof name2 !== "string") return;
3849
+ killLocal(name2);
3850
+ const v = node[2];
3851
+ if (Array.isArray(v)) {
3852
+ if (v[0] === "i64.reinterpret_f64" && Array.isArray(v[1]) && v[1][0] === "local.get" && typeof v[1][1] === "string") ptrAlias.set(name2, v[1][1]);
3853
+ else if (v[0] === "local.get" && typeof v[1] === "string" && ptrAlias.has(v[1])) ptrAlias.set(name2, ptrAlias.get(v[1]));
3854
+ else {
3855
+ const tx2 = tagSrc(v);
3856
+ if (tx2 != null) tagAlias.set(name2, tx2);
3857
+ }
3858
+ }
3859
+ return;
3860
+ }
3861
+ if (op === "if") {
3862
+ const { condIdx } = parseIf(node);
3863
+ if (Array.isArray(node[condIdx])) walkSeq(node[condIdx], node, condIdx);
3864
+ const cond = node[condIdx];
3865
+ const { thenBranch, elseBranch } = parseIf(node);
3866
+ const writes = writesOf(node);
3867
+ const pre = snap();
3868
+ addFacts(condFacts(cond, true, []));
3869
+ if (thenBranch) for (let i = 1; i < thenBranch.length; i++) walkSeq(thenBranch[i], thenBranch, i);
3870
+ restore(pre);
3871
+ addFacts(condFacts(cond, false, []));
3872
+ if (elseBranch) for (let i = 1; i < elseBranch.length; i++) walkSeq(elseBranch[i], elseBranch, i);
3873
+ restore(pre);
3874
+ for (const w of writes) killLocal(w);
3875
+ return;
3876
+ }
3877
+ if (op === "loop") {
3878
+ const writes = writesOf(node);
3879
+ for (const w of writes) killLocal(w);
3880
+ for (let i = 1; i < node.length; i++) walkSeq(node[i], node, i);
3881
+ for (const w of writes) killLocal(w);
3882
+ return;
3883
+ }
3884
+ if (op === "block") {
3885
+ for (let i = 1; i < node.length; i++) walkSeq(node[i], node, i);
3886
+ for (const w of writesOf(node)) killLocal(w);
3887
+ return;
3888
+ }
3889
+ const tx = tagSrc(node);
3890
+ if (tx != null && eqFact.has(tx) && parent) {
3891
+ parent[idx] = ["i32.const", eqFact.get(tx)];
3892
+ return;
3893
+ }
3894
+ if ((op === "i32.eq" || op === "i32.ne") && node.length === 3) {
3895
+ let x = tagSrc(node[1]), k = i32Val(node[2]);
3896
+ if (x == null || k == null) {
3897
+ x = tagSrc(node[2]);
3898
+ k = i32Val(node[1]);
3899
+ }
3900
+ if (x != null && k != null && neFact.get(x)?.has(k) && parent) {
3901
+ parent[idx] = ["i32.const", op === "i32.eq" ? 0 : 1];
3902
+ return;
3903
+ }
3904
+ }
3905
+ for (let i = 1; i < node.length; i++) walkSeq(node[i], node, i);
3906
+ };
3907
+ for (let i = 1; i < fn.length; i++) walkSeq(fn[i], fn, i);
3908
+ };
3574
3909
  var TERMINATORS = /* @__PURE__ */ new Set(["unreachable", "return", "br", "br_table"]);
3575
3910
  var deadcode = (ast) => {
3576
- walk(ast, (node) => {
3911
+ walk2(ast, (node) => {
3577
3912
  if (!Array.isArray(node)) return;
3578
3913
  const kind = node[0];
3579
3914
  if (kind === "func" || kind === "block" || kind === "loop") {
@@ -3619,7 +3954,7 @@ var eliminateDeadInBlock = (block) => {
3619
3954
  }
3620
3955
  };
3621
3956
  var localReuse = (ast) => {
3622
- walk(ast, (node) => {
3957
+ walk2(ast, (node) => {
3623
3958
  if (!Array.isArray(node) || node[0] !== "func") return;
3624
3959
  const localDecls = [];
3625
3960
  const localTypes = /* @__PURE__ */ new Map();
@@ -3640,7 +3975,7 @@ var localReuse = (ast) => {
3640
3975
  }
3641
3976
  }
3642
3977
  }
3643
- walk(node, (n) => {
3978
+ walk2(node, (n) => {
3644
3979
  if (!Array.isArray(n)) return;
3645
3980
  const op = n[0];
3646
3981
  if (op === "local.get" || op === "local.set" || op === "local.tee") {
@@ -3707,13 +4042,26 @@ var isPure = (node) => {
3707
4042
  for (let i = 1; i < node.length; i++) if (Array.isArray(node[i]) && !isPure(node[i])) return false;
3708
4043
  return true;
3709
4044
  };
4045
+ var STRUCTURED_OPS = /* @__PURE__ */ new Set(["if", "then", "else", "block", "loop", "try"]);
4046
+ var isEagerValueOp = (op) => typeof op === "string" && !IMPURE_OPS.has(op) && !STRUCTURED_OPS.has(op) && !IMPURE_SUBSTRINGS.some((s) => op.includes(s));
4047
+ var dropEffects = (node) => {
4048
+ if (!Array.isArray(node) || isPure(node)) return [];
4049
+ const op = node[0];
4050
+ if (op === "local.tee" && node.length === 3) return [["local.set", node[1], node[2]]];
4051
+ if (isEagerValueOp(op)) {
4052
+ const eff = [];
4053
+ for (let i = 1; i < node.length; i++) eff.push(...dropEffects(node[i]));
4054
+ return eff;
4055
+ }
4056
+ return [["drop", node]];
4057
+ };
3710
4058
  var countLocalUses = (node) => {
3711
4059
  const counts = /* @__PURE__ */ new Map();
3712
4060
  const ensure = (name2) => {
3713
4061
  if (!counts.has(name2)) counts.set(name2, { gets: 0, sets: 0, tees: 0 });
3714
4062
  return counts.get(name2);
3715
4063
  };
3716
- walk(node, (n) => {
4064
+ walk2(node, (n) => {
3717
4065
  if (!Array.isArray(n) || n.length < 2 || typeof n[1] !== "string") return;
3718
4066
  if (n[0] === "local.get") ensure(n[1]).gets++;
3719
4067
  else if (n[0] === "local.set") ensure(n[1]).sets++;
@@ -3729,21 +4077,31 @@ var isTinyConst = (node) => {
3729
4077
  return v >= -64 && v <= 63;
3730
4078
  }
3731
4079
  if (c.type === "i64") {
3732
- const v = typeof c.value === "bigint" ? c.value : BigInt(c.value);
3733
- return v >= -64n && v <= 63n;
4080
+ const v = BigInt(c.value);
4081
+ return v <= 63n || v >= 0xffffffffffffffc0n;
3734
4082
  }
3735
4083
  return false;
3736
4084
  };
3737
- var canSubst = (k) => k.pure && k.singleUse || isTinyConst(k.val);
4085
+ var isLocalCopy = (val, dest) => Array.isArray(val) && val[0] === "local.get" && val.length === 2 && typeof val[1] === "string" && val[1] !== dest;
4086
+ var canSubst = (k) => k.pure && k.singleUse || isTinyConst(k.val) || k.copy;
3738
4087
  var purgeRefs = (known, name2) => {
3739
4088
  for (const [key, tracked] of known) {
3740
4089
  let refs = false;
3741
- walk(tracked.val, (n) => {
4090
+ walk2(tracked.val, (n) => {
3742
4091
  if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.tee") && n[1] === name2) refs = true;
3743
4092
  });
3744
4093
  if (refs) known.delete(key);
3745
4094
  }
3746
4095
  };
4096
+ var purgeGlobalRefs = (known, name2) => {
4097
+ for (const [key, tracked] of known) {
4098
+ let refs = false;
4099
+ walk2(tracked.val, (n) => {
4100
+ if (Array.isArray(n) && n[0] === "global.get" && n[1] === name2) refs = true;
4101
+ });
4102
+ if (refs) known.delete(key);
4103
+ }
4104
+ };
3747
4105
  var readsMemory = (node) => {
3748
4106
  if (!Array.isArray(node)) return false;
3749
4107
  const op = node[0];
@@ -3779,13 +4137,13 @@ var substGets = (node, known) => {
3779
4137
  const op = node[0];
3780
4138
  if (op === "local.get" && node.length === 2) {
3781
4139
  const k = typeof node[1] === "string" && known.get(node[1]);
3782
- if (k && canSubst(k)) return clone(k.val);
4140
+ if (k && canSubst(k)) return clone2(k.val);
3783
4141
  return node;
3784
4142
  }
3785
4143
  let inner = known;
3786
- if (op === "block" || op === "loop" || op === "if") {
4144
+ if (isBranchScope(op)) {
3787
4145
  let cloned = null;
3788
- walk(node, (n) => {
4146
+ walk2(node, (n) => {
3789
4147
  if (!Array.isArray(n)) return;
3790
4148
  if ((n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string" && known.has(n[1])) {
3791
4149
  if (!cloned) cloned = new Map(known);
@@ -3798,12 +4156,15 @@ var substGets = (node, known) => {
3798
4156
  const r = substGets(node[i], inner);
3799
4157
  if (r !== node[i]) node[i] = r;
3800
4158
  if (i + 1 < node.length && Array.isArray(node[i])) {
3801
- walk(node[i], (n) => {
4159
+ walk2(node[i], (n) => {
3802
4160
  if (!Array.isArray(n)) return;
3803
4161
  if ((n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string") {
3804
4162
  if (inner === known) inner = new Map(known);
3805
4163
  inner.delete(n[1]);
3806
4164
  purgeRefs(inner, n[1]);
4165
+ } else if (n[0] === "global.set" && typeof n[1] === "string") {
4166
+ if (inner === known) inner = new Map(known);
4167
+ purgeGlobalRefs(inner, n[1]);
3807
4168
  }
3808
4169
  });
3809
4170
  }
@@ -3825,12 +4186,12 @@ var forwardPropagate = (funcNode, params, useCounts) => {
3825
4186
  instr2[2] = sr;
3826
4187
  changed = true;
3827
4188
  }
3828
- walk(instr2[2], (n) => {
4189
+ walk2(instr2[2], (n) => {
3829
4190
  if (!Array.isArray(n)) return;
3830
4191
  if ((n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string") {
3831
4192
  known.delete(n[1]);
3832
4193
  purgeRefs(known, n[1]);
3833
- }
4194
+ } else if (n[0] === "global.set" && typeof n[1] === "string") purgeGlobalRefs(known, n[1]);
3834
4195
  });
3835
4196
  const uses = getUseCount(instr2[1]);
3836
4197
  purgeRefs(known, instr2[1]);
@@ -3841,18 +4202,19 @@ var forwardPropagate = (funcNode, params, useCounts) => {
3841
4202
  val: instr2[2],
3842
4203
  pure: isPure(instr2[2]),
3843
4204
  readsMem: readsMemory(instr2[2]),
3844
- singleUse: uses.gets <= 1 && uses.sets <= 1 && uses.tees === 0
4205
+ singleUse: uses.gets <= 1 && uses.sets <= 1 && uses.tees === 0,
4206
+ copy: isLocalCopy(instr2[2], instr2[1])
3845
4207
  });
3846
4208
  continue;
3847
4209
  }
3848
- if (op === "block" || op === "loop" || op === "if") known.clear();
4210
+ if (isBranchScope(op)) known.clear();
3849
4211
  if (op === "call" || op === "call_indirect" || op === "return_call" || op === "return_call_indirect") {
3850
4212
  for (const [key, tracked] of known) if (readsCallableState(tracked.val)) known.delete(key);
3851
4213
  }
3852
4214
  if (op === "local.get" && instr2.length === 2 && typeof instr2[1] === "string") {
3853
4215
  const tracked = known.get(instr2[1]);
3854
4216
  if (tracked && canSubst(tracked)) {
3855
- const replacement = clone(tracked.val);
4217
+ const replacement = clone2(tracked.val);
3856
4218
  instr2.length = 0;
3857
4219
  instr2.push(...Array.isArray(replacement) ? replacement : [replacement]);
3858
4220
  changed = true;
@@ -3860,14 +4222,15 @@ var forwardPropagate = (funcNode, params, useCounts) => {
3860
4222
  }
3861
4223
  }
3862
4224
  if (op !== "block" && op !== "loop" && op !== "if") {
3863
- const prev = clone(instr2);
4225
+ const prev = clone2(instr2);
3864
4226
  substGets(instr2, known);
3865
4227
  if (!equal(prev, instr2)) changed = true;
3866
- walk(instr2, (n) => {
3867
- if (Array.isArray(n) && (n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string") {
4228
+ walk2(instr2, (n) => {
4229
+ if (!Array.isArray(n)) return;
4230
+ if ((n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string") {
3868
4231
  known.delete(n[1]);
3869
4232
  purgeRefs(known, n[1]);
3870
- }
4233
+ } else if (n[0] === "global.set" && typeof n[1] === "string") purgeGlobalRefs(known, n[1]);
3871
4234
  });
3872
4235
  if (writesMemory(instr2)) {
3873
4236
  for (const [key, tracked] of known) if (tracked.readsMem) known.delete(key);
@@ -3887,7 +4250,7 @@ var eliminateSetGetPairs = (funcNode, params, useCounts) => {
3887
4250
  if (getNode[1] !== name2 || params.has(name2)) continue;
3888
4251
  const uses = useCounts.get(name2) || { gets: 0, sets: 0, tees: 0 };
3889
4252
  if (uses.sets !== 1 || uses.gets !== 1 || uses.tees !== 0) continue;
3890
- const expr2 = clone(setNode[2]);
4253
+ const expr2 = clone2(setNode[2]);
3891
4254
  funcNode.splice(i, 2, ...Array.isArray(expr2) ? [expr2] : [expr2]);
3892
4255
  changed = true;
3893
4256
  i--;
@@ -3905,7 +4268,7 @@ var createLocalTees = (funcNode, params, useCounts) => {
3905
4268
  if (getNode[1] !== name2 || params.has(name2)) continue;
3906
4269
  const uses = useCounts.get(name2) || { gets: 0, sets: 0, tees: 0 };
3907
4270
  if (uses.sets + uses.gets + uses.tees <= 2) continue;
3908
- funcNode.splice(i, 2, ["local.tee", name2, clone(setNode[2])]);
4271
+ funcNode.splice(i, 2, ["local.tee", name2, clone2(setNode[2])]);
3909
4272
  changed = true;
3910
4273
  }
3911
4274
  return changed;
@@ -3936,18 +4299,37 @@ var eliminateDeadStores = (funcNode, params, useCounts) => {
3936
4299
  }
3937
4300
  return changed;
3938
4301
  };
4302
+ var eliminateAdjacentDeadStores = (funcNode, params) => {
4303
+ let changed = false;
4304
+ for (let i = 1; i < funcNode.length - 1; i++) {
4305
+ const a = funcNode[i], b = funcNode[i + 1];
4306
+ if (!Array.isArray(a) || a[0] !== "local.set" || a.length !== 3) continue;
4307
+ if (!Array.isArray(b) || b[0] !== "local.set" && b[0] !== "local.tee" || b.length !== 3 || b[1] !== a[1]) continue;
4308
+ if (params.has(a[1]) || !isPure(a[2])) continue;
4309
+ let reads = false;
4310
+ walk2(b[2], (n) => {
4311
+ if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.tee") && n[1] === a[1]) reads = true;
4312
+ });
4313
+ if (reads) continue;
4314
+ funcNode.splice(i, 1);
4315
+ changed = true;
4316
+ i--;
4317
+ }
4318
+ return changed;
4319
+ };
3939
4320
  var isScopeNode = (n) => Array.isArray(n) && (n[0] === "func" || n[0] === "block" || n[0] === "loop" || n[0] === "then" || n[0] === "else");
4321
+ var isBranchScope = (op) => op === "block" || op === "loop" || op === "if";
3940
4322
  var propagate = (ast) => {
3941
- walk(ast, (funcNode) => {
4323
+ walk2(ast, (funcNode) => {
3942
4324
  if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
3943
4325
  const params = /* @__PURE__ */ new Set();
3944
4326
  for (const sub of funcNode)
3945
4327
  if (Array.isArray(sub) && sub[0] === "param" && typeof sub[1] === "string") params.add(sub[1]);
3946
4328
  const scopes = [];
3947
- walkPost(funcNode, (n) => {
4329
+ walkPost2(funcNode, (n) => {
3948
4330
  if (isScopeNode(n)) scopes.push(n);
3949
4331
  });
3950
- for (let round = 0; round < 6; round++) {
4332
+ for (let round = 0; round < MAX_PROP_ROUNDS; round++) {
3951
4333
  const useCounts = countLocalUses(funcNode);
3952
4334
  let progressed = false;
3953
4335
  for (const scope of scopes) {
@@ -3955,157 +4337,464 @@ var propagate = (ast) => {
3955
4337
  if (eliminateSetGetPairs(scope, params, useCounts)) progressed = true;
3956
4338
  if (createLocalTees(scope, params, useCounts)) progressed = true;
3957
4339
  if (eliminateDeadStores(scope, params, useCounts)) progressed = true;
4340
+ if (eliminateAdjacentDeadStores(scope, params)) progressed = true;
3958
4341
  }
3959
4342
  if (!progressed) break;
3960
4343
  }
3961
4344
  });
3962
4345
  return ast;
3963
4346
  };
3964
- var inline = (ast) => {
4347
+ var inlineUid = 0;
4348
+ var INL_HEAD = /* @__PURE__ */ new Set(["export", "type", "param", "result", "local"]);
4349
+ var inlBodyStart = (fn) => {
4350
+ let i = 2;
4351
+ while (i < fn.length && (typeof fn[i] === "string" || Array.isArray(fn[i]) && INL_HEAD.has(fn[i][0]))) i++;
4352
+ return i;
4353
+ };
4354
+ var inlIsBranch = (op) => op === "br" || op === "br_if" || op === "br_table";
4355
+ var inlUnsafe = (n) => {
4356
+ if (!Array.isArray(n)) return false;
4357
+ const op = n[0];
4358
+ if (op === "return_call" || op === "return_call_indirect" || op === "return_call_ref") return true;
4359
+ if (op === "try" || op === "try_table" || op === "delegate" || op === "rethrow") return true;
4360
+ if (inlIsBranch(op)) {
4361
+ for (let i = 1; i < n.length; i++) if (typeof n[i] === "number" || typeof n[i] === "string" && /^\d+$/.test(n[i])) return true;
4362
+ }
4363
+ for (let i = 1; i < n.length; i++) if (inlUnsafe(n[i])) return true;
4364
+ return false;
4365
+ };
4366
+ var inlCallsSelf = (n, name2) => {
4367
+ if (!Array.isArray(n)) return false;
4368
+ if ((n[0] === "call" || n[0] === "return_call") && n[1] === name2) return true;
4369
+ for (let i = 1; i < n.length; i++) if (inlCallsSelf(n[i], name2)) return true;
4370
+ return false;
4371
+ };
4372
+ var inlZeroFor = (t) => {
4373
+ if (t === "i32") return ["i32.const", 0];
4374
+ if (t === "i64") return ["i64.const", 0];
4375
+ if (t === "f32") return ["f32.const", 0];
4376
+ if (t === "f64") return ["f64.const", 0];
4377
+ if (t === "v128") return ["v128.const", "i64x2", "0", "0"];
4378
+ return null;
4379
+ };
4380
+ var inlNeedsReset = (body, name2) => {
4381
+ let seen = false, conditional = false, depth = 0;
4382
+ const visit = (n) => {
4383
+ if (seen || !Array.isArray(n)) return;
4384
+ const op = n[0];
4385
+ const isSet = op === "local.set" || op === "local.tee";
4386
+ if ((isSet || op === "local.get") && n[1] === name2) {
4387
+ if (isSet) for (let i = 2; i < n.length && !seen; i++) visit(n[i]);
4388
+ if (seen) return;
4389
+ seen = true;
4390
+ if (op === "local.get" || depth > 0) conditional = true;
4391
+ return;
4392
+ }
4393
+ const isIf = op === "if";
4394
+ for (let i = 1; i < n.length && !seen; i++) {
4395
+ const c = n[i];
4396
+ const cond = isIf && Array.isArray(c) && (c[0] === "then" || c[0] === "else");
4397
+ if (cond) depth++;
4398
+ visit(c);
4399
+ if (cond) depth--;
4400
+ }
4401
+ };
4402
+ for (const n of body) {
4403
+ if (seen) break;
4404
+ visit(n);
4405
+ }
4406
+ if (!seen) return false;
4407
+ return conditional;
4408
+ };
4409
+ var inlCollectPinned = (n, pinned) => {
4410
+ if (!Array.isArray(n)) return;
4411
+ const op = n[0];
4412
+ if (op === "export" && Array.isArray(n[2]) && n[2][0] === "func" && typeof n[2][1] === "string") pinned.add(n[2][1]);
4413
+ else if (op === "start" && typeof n[1] === "string") pinned.add(n[1]);
4414
+ else if (op === "ref.func" && typeof n[1] === "string") pinned.add(n[1]);
4415
+ else if (op === "elem") {
4416
+ for (const c of n) if (typeof c === "string" && c[0] === "$") pinned.add(c);
4417
+ }
4418
+ for (const c of n) inlCollectPinned(c, pinned);
4419
+ };
4420
+ var inlBuildPinned = (ast) => {
4421
+ const pinned = /* @__PURE__ */ new Set();
4422
+ for (const n of ast) if (!Array.isArray(n) || n[0] !== "func") inlCollectPinned(n, pinned);
4423
+ return pinned;
4424
+ };
4425
+ var inlParse = (fn) => {
4426
+ const params = [], locals = [];
4427
+ let inlResult = null, ok = true, nResult = 0;
4428
+ for (let i = 2; i < fn.length; i++) {
4429
+ const c = fn[i];
4430
+ if (typeof c === "string") continue;
4431
+ if (!Array.isArray(c)) {
4432
+ ok = false;
4433
+ break;
4434
+ }
4435
+ if (c[0] === "param") {
4436
+ if (typeof c[1] !== "string" || c[1][0] !== "$") {
4437
+ ok = false;
4438
+ break;
4439
+ }
4440
+ params.push({ name: c[1], type: c[2] });
4441
+ } else if (c[0] === "local") {
4442
+ if (typeof c[1] !== "string" || c[1][0] !== "$" || !inlZeroFor(c[2])) {
4443
+ ok = false;
4444
+ break;
4445
+ }
4446
+ locals.push({ name: c[1], type: c[2] });
4447
+ } else if (c[0] === "result") {
4448
+ nResult += c.length - 1;
4449
+ if (c.length > 1) inlResult = c[1];
4450
+ } else if (c[0] === "export") {
4451
+ ok = false;
4452
+ break;
4453
+ } else if (c[0] === "type") continue;
4454
+ else break;
4455
+ }
4456
+ if (nResult > 1) ok = false;
4457
+ return ok ? { params, locals, inlResult } : null;
4458
+ };
4459
+ var inlBodySize = (fn) => {
4460
+ let n = 0;
4461
+ const count = (x) => {
4462
+ if (!Array.isArray(x)) return;
4463
+ n++;
4464
+ for (let i = 1; i < x.length; i++) count(x[i]);
4465
+ };
4466
+ for (let i = inlBodyStart(fn); i < fn.length; i++) count(fn[i]);
4467
+ return n;
4468
+ };
4469
+ var buildInline = (params, locals, inlResult, cBody, args) => {
4470
+ const uid2 = ++inlineUid;
4471
+ const exit = `$__inl${uid2}`;
4472
+ const rename = /* @__PURE__ */ new Map();
4473
+ for (const p of params) rename.set(p.name, `$__inl${uid2}_${p.name.slice(1)}`);
4474
+ for (const l of locals) rename.set(l.name, `$__inl${uid2}_${l.name.slice(1)}`);
4475
+ const labelRename = /* @__PURE__ */ new Map();
4476
+ const collectLabels = (n) => {
4477
+ if (!Array.isArray(n)) return;
4478
+ if (isBranchScope(n[0]) && typeof n[1] === "string" && n[1][0] === "$" && !labelRename.has(n[1]))
4479
+ labelRename.set(n[1], `$__inl${uid2}L_${n[1].slice(1)}`);
4480
+ for (let i = 1; i < n.length; i++) collectLabels(n[i]);
4481
+ };
4482
+ for (const n of cBody) collectLabels(n);
4483
+ const sub = (n) => {
4484
+ if (!Array.isArray(n)) return n;
4485
+ const op = n[0];
4486
+ if ((op === "local.get" || op === "local.set" || op === "local.tee") && typeof n[1] === "string" && rename.has(n[1]))
4487
+ return [op, rename.get(n[1]), ...n.slice(2).map(sub)];
4488
+ if (op === "return") return ["br", exit, ...n.slice(1).map(sub)];
4489
+ if (isBranchScope(op) && typeof n[1] === "string" && labelRename.has(n[1]))
4490
+ return [op, labelRename.get(n[1]), ...n.slice(2).map(sub)];
4491
+ if (inlIsBranch(op)) return [op, ...n.slice(1).map((c) => typeof c === "string" && labelRename.has(c) ? labelRename.get(c) : sub(c))];
4492
+ return n.map((c, i) => i === 0 ? c : sub(c));
4493
+ };
4494
+ const setup = params.map((p, k) => ["local.set", rename.get(p.name), args[k]]);
4495
+ const resets = locals.filter((l) => inlNeedsReset(cBody, l.name)).map((l) => ["local.set", rename.get(l.name), inlZeroFor(l.type)]);
4496
+ const inner = cBody.map(sub);
4497
+ const block = inlResult ? ["block", exit, ["result", inlResult], ...setup, ...resets, ...inner] : ["block", exit, ...setup, ...resets, ...inner];
4498
+ const decls = [...params, ...locals].map((p) => ["local", rename.get(p.name), p.type]);
4499
+ return { block, decls };
4500
+ };
4501
+ var INLINE_MAX_NODES = 90;
4502
+ var isV128SimdHelper = (params, inlResult) => inlResult === "v128" && params.length > 0 && params.every((p) => p.type === "v128");
4503
+ var inline = (ast, { simdOnly = false } = {}) => {
3965
4504
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
3966
- const inlinable = /* @__PURE__ */ new Map();
3967
- for (const node of ast.slice(1)) {
3968
- if (!Array.isArray(node) || node[0] !== "func") continue;
3969
- const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
3970
- if (!name2) continue;
3971
- let params = [];
3972
- let body = [];
3973
- let hasLocals = false;
3974
- let hasExport = false;
3975
- for (let i = 1; i < node.length; i++) {
3976
- const sub = node[i];
3977
- if (!Array.isArray(sub)) continue;
3978
- if (sub[0] === "param") {
3979
- if (typeof sub[1] === "string" && sub[1][0] === "$") {
3980
- params.push({ name: sub[1], type: sub[2] });
3981
- } else {
3982
- params = null;
3983
- break;
3984
- }
3985
- } else if (sub[0] === "local") {
3986
- hasLocals = true;
3987
- } else if (sub[0] === "export") {
3988
- hasExport = true;
3989
- } else if (sub[0] !== "result" && sub[0] !== "type") {
3990
- body.push(sub);
4505
+ const skip = /* @__PURE__ */ new Set();
4506
+ for (let round = 0; round < MAX_INLINE_ROUNDS; round++) {
4507
+ const funcs = ast.filter((n) => Array.isArray(n) && n[0] === "func");
4508
+ const funcByName = /* @__PURE__ */ new Map();
4509
+ for (const n of funcs) if (typeof n[1] === "string") funcByName.set(n[1], n);
4510
+ const callRefs = /* @__PURE__ */ new Map(), otherRef = /* @__PURE__ */ new Set();
4511
+ const countRefs = (n) => {
4512
+ if (!Array.isArray(n)) return;
4513
+ const op = n[0];
4514
+ if (op === "call" && typeof n[1] === "string") callRefs.set(n[1], (callRefs.get(n[1]) || 0) + 1);
4515
+ else if (op === "return_call" && typeof n[1] === "string") otherRef.add(n[1]);
4516
+ for (let i = 1; i < n.length; i++) countRefs(n[i]);
4517
+ };
4518
+ countRefs(ast);
4519
+ const pinned = /* @__PURE__ */ new Set();
4520
+ for (const n of ast) if (!Array.isArray(n) || n[0] !== "func") inlCollectPinned(n, pinned);
4521
+ let calleeName = null, parsed = null;
4522
+ for (const [name2, fn] of funcByName) {
4523
+ if (skip.has(name2) || pinned.has(name2) || otherRef.has(name2) || SIMD_PROTECTED.has(name2)) continue;
4524
+ if (!(callRefs.get(name2) >= 1)) continue;
4525
+ if (inlBodySize(fn) > INLINE_MAX_NODES) continue;
4526
+ if (inlCallsSelf(fn, name2)) continue;
4527
+ const p = inlParse(fn);
4528
+ if (!p) continue;
4529
+ if (simdOnly && !isV128SimdHelper(p.params, p.inlResult)) continue;
4530
+ let bad = false;
4531
+ for (let i = inlBodyStart(fn); i < fn.length; i++) if (inlUnsafe(fn[i])) {
4532
+ bad = true;
4533
+ break;
3991
4534
  }
4535
+ if (bad) continue;
4536
+ calleeName = name2;
4537
+ parsed = p;
4538
+ break;
3992
4539
  }
3993
- if (params && !hasLocals && !hasExport && params.length <= 4 && body.length === 1) {
3994
- const paramNames = new Set(params.map((p) => p.name));
3995
- let mutatesParam = false;
3996
- let hasReturn = false;
3997
- walk(body[0], (n) => {
3998
- if (!Array.isArray(n)) return;
3999
- if ((n[0] === "local.set" || n[0] === "local.tee") && paramNames.has(n[1])) {
4000
- mutatesParam = true;
4001
- }
4002
- if (n[0] === "return" || n[0] === "return_call" || n[0] === "return_call_indirect") {
4003
- hasReturn = true;
4004
- }
4005
- });
4006
- if (!mutatesParam && !hasReturn) {
4007
- inlinable.set(name2, { body: body[0], params });
4540
+ if (!calleeName) break;
4541
+ const callee = funcByName.get(calleeName);
4542
+ const { params, locals, inlResult } = parsed;
4543
+ const cBody = callee.slice(inlBodyStart(callee));
4544
+ const expected = callRefs.get(calleeName) || 0;
4545
+ let replaced = 0;
4546
+ for (const fn of funcs) {
4547
+ if (fn === callee) continue;
4548
+ const addDecls = [];
4549
+ for (let i = inlBodyStart(fn); i < fn.length; i++) {
4550
+ fn[i] = walkPost2(fn[i], (n) => {
4551
+ if (!Array.isArray(n) || n[0] !== "call" || n[1] !== calleeName) return;
4552
+ const args = n.slice(2);
4553
+ if (args.length !== params.length) return;
4554
+ const { block, decls } = buildInline(params, locals, inlResult, cBody, args);
4555
+ addDecls.push(...decls);
4556
+ replaced++;
4557
+ return block;
4558
+ });
4008
4559
  }
4560
+ if (addDecls.length) fn.splice(inlBodyStart(fn), 0, ...addDecls);
4009
4561
  }
4562
+ if (replaced === expected) {
4563
+ const idx = ast.indexOf(callee);
4564
+ if (idx >= 0) ast.splice(idx, 1);
4565
+ } else skip.add(calleeName);
4010
4566
  }
4011
- if (inlinable.size === 0) return ast;
4012
- walkPost(ast, (node) => {
4013
- if (!Array.isArray(node) || node[0] !== "call") return;
4014
- const fname = node[1];
4015
- if (!inlinable.has(fname)) return;
4016
- const { body, params } = inlinable.get(fname);
4017
- const args = node.slice(2);
4018
- if (params.length === 0) {
4019
- return clone(body);
4020
- }
4021
- const substituted = walkPost(clone(body), (n) => {
4022
- if (!Array.isArray(n) || n[0] !== "local.get") return;
4023
- const local = n[1];
4024
- const paramIdx = params.findIndex((p) => p.name === local);
4025
- if (paramIdx !== -1 && args[paramIdx]) {
4026
- return clone(args[paramIdx]);
4027
- }
4028
- });
4029
- return substituted;
4030
- });
4031
4567
  return ast;
4032
4568
  };
4033
- var inlineUid = 0;
4034
- var inlineOnce = (ast) => {
4569
+ var devirt = (ast) => {
4035
4570
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4036
- const HEAD = /* @__PURE__ */ new Set(["export", "type", "param", "result", "local"]);
4037
- const bodyStart = (fn) => {
4038
- let i = 2;
4039
- while (i < fn.length && (typeof fn[i] === "string" || Array.isArray(fn[i]) && HEAD.has(fn[i][0]))) i++;
4040
- return i;
4041
- };
4042
- const isBranch = (op) => op === "br" || op === "br_if" || op === "br_table";
4043
- const unsafe = (n) => {
4044
- if (!Array.isArray(n)) return false;
4045
- const op = n[0];
4046
- if (op === "return_call" || op === "return_call_indirect" || op === "return_call_ref") return true;
4047
- if (op === "try" || op === "try_table" || op === "delegate" || op === "rethrow") return true;
4048
- if (isBranch(op)) {
4049
- for (let i = 1; i < n.length; i++) if (typeof n[i] === "number" || typeof n[i] === "string" && /^\d+$/.test(n[i])) return true;
4571
+ const slots = /* @__PURE__ */ new Map(), typeDefs = /* @__PURE__ */ new Map(), funcsByName = /* @__PURE__ */ new Map(), allFuncs = [];
4572
+ let tableMutated = false;
4573
+ walk2(ast, (n) => {
4574
+ if (Array.isArray(n) && typeof n[0] === "string" && (n[0] === "table.set" || n[0] === "table.grow" || n[0] === "table.init" || n[0] === "table.copy" || n[0] === "table.fill")) tableMutated = true;
4575
+ });
4576
+ if (tableMutated) return ast;
4577
+ for (const node of ast.slice(1)) {
4578
+ if (!Array.isArray(node)) continue;
4579
+ if (node[0] === "elem") {
4580
+ const off = node[1];
4581
+ if (!Array.isArray(off) || off[0] !== "i32.const") return ast;
4582
+ let base = Number(off[1]);
4583
+ for (let i = 2; i < node.length; i++)
4584
+ if (typeof node[i] === "string" && node[i][0] === "$") slots.set(base++, node[i]);
4585
+ } else if (node[0] === "type" && typeof node[1] === "string") typeDefs.set(node[1], node[2]);
4586
+ else if (node[0] === "func") {
4587
+ allFuncs.push(node);
4588
+ if (typeof node[1] === "string") funcsByName.set(node[1], node);
4050
4589
  }
4051
- for (let i = 1; i < n.length; i++) if (unsafe(n[i])) return true;
4590
+ }
4591
+ if (!slots.size) return ast;
4592
+ const globalCands = /* @__PURE__ */ new Map();
4593
+ const isC64 = (n, hex) => Array.isArray(n) && n[0] === "i64.const" && _i64Canon(n[1]) === hex;
4594
+ const MASK15 = "0x0000000000007fff", SHIFT32 = "0x0000000000000020";
4595
+ const boxConsts = (v, out) => {
4596
+ if (!Array.isArray(v)) return false;
4597
+ if (v[0] === "i64.const") {
4598
+ out.push(v);
4599
+ return true;
4600
+ }
4601
+ if (v[0] === "f64.const" && typeof v[1] === "string" && v[1].startsWith("nan:")) {
4602
+ out.push(["i64.const", _i64Canon(v[1].slice(4))]);
4603
+ return true;
4604
+ }
4605
+ if (v[0] === "f64.reinterpret_i64" && v.length === 2) return boxConsts(v[1], out);
4606
+ if (v[0] === "select" && v.length === 4) return boxConsts(v[1], out) && boxConsts(v[2], out);
4052
4607
  return false;
4053
4608
  };
4054
- const callsSelf = (n, name2) => {
4055
- if (!Array.isArray(n)) return false;
4056
- if ((n[0] === "call" || n[0] === "return_call") && n[1] === name2) return true;
4057
- for (let i = 1; i < n.length; i++) if (callsSelf(n[i], name2)) return true;
4609
+ const globalWrites = /* @__PURE__ */ new Map();
4610
+ walk2(ast, (n) => {
4611
+ if (!Array.isArray(n) || n[0] !== "global.set" || typeof n[1] !== "string") return;
4612
+ if (!globalWrites.has(n[1])) globalWrites.set(n[1], []);
4613
+ globalWrites.get(n[1]).push(n[2]);
4614
+ });
4615
+ const candLeaves = (v, consts, reads) => {
4616
+ if (!Array.isArray(v)) return false;
4617
+ if (v[0] === "i64.const") {
4618
+ consts.push(v);
4619
+ return true;
4620
+ }
4621
+ if (v[0] === "f64.const" && typeof v[1] === "string" && v[1].startsWith("nan:")) {
4622
+ consts.push(["i64.const", _i64Canon(v[1].slice(4))]);
4623
+ return true;
4624
+ }
4625
+ if ((v[0] === "f64.reinterpret_i64" || v[0] === "i64.reinterpret_f64") && v.length === 2)
4626
+ return candLeaves(v[1], consts, reads);
4627
+ if (v[0] === "global.get" && typeof v[1] === "string") {
4628
+ reads.push(v[1]);
4629
+ return true;
4630
+ }
4631
+ if (v[0] === "local.get" || v[0] === "local.tee") {
4632
+ return v[0] === "local.tee" && v.length === 3 ? candLeaves(v[2], consts, reads) : false;
4633
+ }
4634
+ if (v[0] === "select" && v.length === 4)
4635
+ return candLeaves(v[1], consts, reads) && candLeaves(v[2], consts, reads);
4636
+ if (v[0] === "if") {
4637
+ let ok = true, seenArm = false;
4638
+ for (let i = 1; i < v.length; i++) {
4639
+ const p = v[i];
4640
+ if (!Array.isArray(p)) continue;
4641
+ if (p[0] === "then" || p[0] === "else") {
4642
+ seenArm = true;
4643
+ if (p.length !== 2 || !candLeaves(p[1], consts, reads)) ok = false;
4644
+ }
4645
+ }
4646
+ return ok && seenArm;
4647
+ }
4058
4648
  return false;
4059
4649
  };
4060
- const zeroFor = (t) => {
4061
- if (t === "i32") return ["i32.const", 0];
4062
- if (t === "i64") return ["i64.const", 0n];
4063
- if (t === "f32") return ["f32.const", 0];
4064
- if (t === "f64") return ["f64.const", 0];
4065
- if (t === "v128") return ["v128.const", "i64x2", 0n, 0n];
4066
- return null;
4067
- };
4068
- const needsReset = (body, name2) => {
4069
- let seen = false, conditional = false, depth = 0;
4070
- const visit = (n) => {
4071
- if (seen || !Array.isArray(n)) return;
4072
- const op = n[0];
4073
- const isSet = op === "local.set" || op === "local.tee";
4074
- if ((isSet || op === "local.get") && n[1] === name2) {
4075
- if (isSet) for (let i = 2; i < n.length && !seen; i++) visit(n[i]);
4076
- if (seen) return;
4077
- seen = true;
4078
- if (op === "local.get" || depth > 0) conditional = true;
4079
- return;
4650
+ const writeFacts = /* @__PURE__ */ new Map();
4651
+ for (const [g, ws] of globalWrites) {
4652
+ let consts = [], reads = [], ok = true;
4653
+ for (const w of ws) if (!candLeaves(w, consts, reads)) {
4654
+ ok = false;
4655
+ break;
4656
+ }
4657
+ writeFacts.set(g, ok ? { consts, reads } : null);
4658
+ }
4659
+ let changed = true;
4660
+ const resolved = /* @__PURE__ */ new Map();
4661
+ while (changed) {
4662
+ changed = false;
4663
+ for (const [g, f] of writeFacts) {
4664
+ if (resolved.get(g) === null) continue;
4665
+ if (f === null) {
4666
+ if (resolved.get(g) !== null) {
4667
+ resolved.set(g, null);
4668
+ changed = true;
4669
+ }
4670
+ continue;
4671
+ }
4672
+ const m = resolved.get(g) || /* @__PURE__ */ new Map();
4673
+ const before = m.size;
4674
+ let poisoned = false;
4675
+ for (const c of f.consts) m.set(_i64Canon(c[1]), c);
4676
+ for (const r of f.reads) {
4677
+ if (writeFacts.get(r) === null || resolved.get(r) === null) {
4678
+ poisoned = true;
4679
+ break;
4680
+ }
4681
+ const rm = resolved.get(r);
4682
+ if (rm) for (const [hex, c] of rm) m.set(hex, c);
4080
4683
  }
4081
- const isIf = op === "if";
4082
- for (let i = 1; i < n.length && !seen; i++) {
4083
- const c = n[i];
4084
- const cond = isIf && Array.isArray(c) && (c[0] === "then" || c[0] === "else");
4085
- if (cond) depth++;
4086
- visit(c);
4087
- if (cond) depth--;
4684
+ if (poisoned) {
4685
+ resolved.set(g, null);
4686
+ changed = true;
4687
+ continue;
4688
+ }
4689
+ if (!resolved.has(g) || m.size !== before) {
4690
+ resolved.set(g, m);
4691
+ changed = true;
4088
4692
  }
4089
- };
4090
- for (const n of body) {
4091
- if (seen) break;
4092
- visit(n);
4093
4693
  }
4094
- if (!seen) return false;
4095
- return conditional;
4694
+ }
4695
+ for (const [g, m] of resolved) globalCands.set(g, m);
4696
+ const matchSlotOfLocal = (e) => {
4697
+ if (!Array.isArray(e) || e[0] !== "i32.wrap_i64") return null;
4698
+ const a = e[1];
4699
+ if (!Array.isArray(a) || a[0] !== "i64.and") return null;
4700
+ let sh = a[1], mk = a[2];
4701
+ if (!isC64(mk, MASK15)) {
4702
+ sh = a[2];
4703
+ mk = a[1];
4704
+ }
4705
+ if (!isC64(mk, MASK15) || !Array.isArray(sh) || sh[0] !== "i64.shr_u" || !isC64(sh[2], SHIFT32)) return null;
4706
+ const ri = sh[1];
4707
+ if (!Array.isArray(ri) || ri[0] !== "i64.reinterpret_f64") return null;
4708
+ const leaf = ri[1];
4709
+ if (Array.isArray(leaf) && leaf[0] === "local.get" && typeof leaf[1] === "string") return { local: leaf[1] };
4710
+ if (Array.isArray(leaf) && leaf[0] === "global.get" && typeof leaf[1] === "string") return { global: leaf[1] };
4711
+ return null;
4096
4712
  };
4097
- const collectPinned = (n, pinned) => {
4098
- if (!Array.isArray(n)) return;
4099
- const op = n[0];
4100
- if (op === "export" && Array.isArray(n[2]) && n[2][0] === "func" && typeof n[2][1] === "string") pinned.add(n[2][1]);
4101
- else if (op === "start" && typeof n[1] === "string") pinned.add(n[1]);
4102
- else if (op === "ref.func" && typeof n[1] === "string") pinned.add(n[1]);
4103
- else if (op === "elem") {
4104
- for (const c of n) if (typeof c === "string" && c[0] === "$") pinned.add(c);
4105
- }
4106
- for (const c of n) collectPinned(c, pinned);
4713
+ const tokSig = (parts) => {
4714
+ const ps = [], rs = [];
4715
+ for (const p of parts) {
4716
+ if (!Array.isArray(p)) continue;
4717
+ if (p[0] === "param") {
4718
+ for (const t of p.slice(1)) if (typeof t === "string" && t[0] !== "$") ps.push(t);
4719
+ } else if (p[0] === "result") rs.push(...p.slice(1));
4720
+ }
4721
+ return ps.join(",") + "->" + rs.join(",");
4107
4722
  };
4108
- for (let round = 0; round < 16; round++) {
4723
+ for (const fn of allFuncs) {
4724
+ const cands = /* @__PURE__ */ new Map();
4725
+ for (const part of fn)
4726
+ if (Array.isArray(part) && part[0] === "param" && typeof part[1] === "string") cands.set(part[1], null);
4727
+ walk2(fn, (n) => {
4728
+ if (!Array.isArray(n) || n[0] !== "local.set" && n[0] !== "local.tee" || typeof n[1] !== "string") return;
4729
+ if (cands.get(n[1]) === null) return;
4730
+ const out = [];
4731
+ if (boxConsts(n[2], out)) {
4732
+ const m = cands.get(n[1]) || /* @__PURE__ */ new Map();
4733
+ for (const c of out) m.set(_i64Canon(c[1]), c);
4734
+ cands.set(n[1], m);
4735
+ } else if (Array.isArray(n[2]) && n[2][0] === "global.get" && typeof n[2][1] === "string" && globalCands.get(n[2][1])) {
4736
+ const g = globalCands.get(n[2][1]);
4737
+ const m = cands.get(n[1]) || /* @__PURE__ */ new Map();
4738
+ for (const [hex, c] of g) m.set(hex, c);
4739
+ cands.set(n[1], m);
4740
+ } else cands.set(n[1], null);
4741
+ });
4742
+ walkPost2(fn, (n, parent) => {
4743
+ if (!Array.isArray(n) || n[0] !== "call_indirect") return;
4744
+ if (parent && parent[0] === "else") return;
4745
+ const typeUse = Array.isArray(n[1]) && n[1][0] === "type" ? n[1] : null;
4746
+ if (!typeUse) return;
4747
+ const sig = typeDefs.get(typeUse[1]);
4748
+ const callSig = Array.isArray(sig) ? tokSig(sig.slice(1)) : null;
4749
+ if (callSig == null) return;
4750
+ const results = [];
4751
+ for (const s of sig.slice(1)) if (Array.isArray(s) && s[0] === "result") results.push(...s.slice(1));
4752
+ const args = n.slice(2, -1);
4753
+ const idx = n[n.length - 1];
4754
+ const sigOk = (name2) => {
4755
+ const target = funcsByName.get(name2);
4756
+ if (!target) return false;
4757
+ const tu = target.find((p) => Array.isArray(p) && p[0] === "type");
4758
+ if (tu) return tu[1] === typeUse[1] || typeDefs.get(tu[1]) && tokSig(typeDefs.get(tu[1]).slice(1)) === callSig;
4759
+ return tokSig(target.slice(2)) === callSig;
4760
+ };
4761
+ if (Array.isArray(idx) && idx[0] === "i32.const") {
4762
+ const name2 = slots.get(Number(idx[1]));
4763
+ return name2 && sigOk(name2) ? ["call", name2, ...args] : void 0;
4764
+ }
4765
+ const f = matchSlotOfLocal(idx);
4766
+ if (!f) return;
4767
+ const m = f.local != null ? cands.get(f.local) : globalCands.get(f.global);
4768
+ if (!m || m.size === 0 || m.size > 4) return;
4769
+ const arms = [];
4770
+ for (const cNode of m.values()) {
4771
+ const name2 = slots.get(_i64HiU(_i64Canon(cNode[1])) & 32767);
4772
+ if (!name2 || !sigOk(name2)) return;
4773
+ arms.push([cNode, name2]);
4774
+ }
4775
+ const readBack = f.local != null ? ["local.get", f.local] : ["global.get", f.global];
4776
+ let out = n;
4777
+ for (let i = arms.length - 1; i >= 0; i--) {
4778
+ const [cNode, name2] = arms[i];
4779
+ out = [
4780
+ "if",
4781
+ ...results.length ? [["result", ...results]] : [],
4782
+ ["i64.eq", ["i64.reinterpret_f64", clone2(readBack)], clone2(cNode)],
4783
+ ["then", ["call", name2, ...args.map(clone2)]],
4784
+ ["else", out]
4785
+ ];
4786
+ }
4787
+ return out;
4788
+ });
4789
+ }
4790
+ return ast;
4791
+ };
4792
+ var SIMD_PROTECTED = /* @__PURE__ */ new Set(["$math.sin_core", "$math.cos_core", "$math.sin", "$math.cos", "$math.pow", "$math.atan2", "$math.hypot", "$math.log"]);
4793
+ var inlineOnce = (ast) => {
4794
+ if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4795
+ const bodyStart = inlBodyStart, callsSelf = inlCallsSelf, unsafe = inlUnsafe, isBranch = inlIsBranch;
4796
+ const zeroFor = inlZeroFor, needsReset = inlNeedsReset;
4797
+ for (let round = 0; round < MAX_INLINE_ROUNDS; round++) {
4109
4798
  const funcs = ast.filter((n) => Array.isArray(n) && n[0] === "func");
4110
4799
  const funcByName = /* @__PURE__ */ new Map();
4111
4800
  for (const n of funcs) if (typeof n[1] === "string") funcByName.set(n[1], n);
@@ -4118,12 +4807,12 @@ var inlineOnce = (ast) => {
4118
4807
  for (let i = 1; i < n.length; i++) countRefs(n[i]);
4119
4808
  };
4120
4809
  countRefs(ast);
4121
- const pinned = /* @__PURE__ */ new Set();
4122
- for (const n of ast) if (!Array.isArray(n) || n[0] !== "func") collectPinned(n, pinned);
4810
+ const pinned = inlBuildPinned(ast);
4123
4811
  let calleeName = null;
4124
4812
  for (const [name2, fn] of funcByName) {
4125
4813
  if (pinned.has(name2) || otherRef.has(name2)) continue;
4126
4814
  if (callRefs.get(name2) !== 1) continue;
4815
+ if (SIMD_PROTECTED.has(name2)) continue;
4127
4816
  if (callsSelf(fn, name2)) continue;
4128
4817
  let ok = true, nResult = 0;
4129
4818
  for (let i = 2; i < fn.length; i++) {
@@ -4179,11 +4868,10 @@ var inlineOnce = (ast) => {
4179
4868
  const rename = /* @__PURE__ */ new Map();
4180
4869
  for (const p of params) rename.set(p.name, `$__inl${uid2}_${p.name.slice(1)}`);
4181
4870
  for (const l of locals) rename.set(l.name, `$__inl${uid2}_${l.name.slice(1)}`);
4182
- const isBlockLabel = (op) => op === "block" || op === "loop" || op === "if";
4183
4871
  const labelRename = /* @__PURE__ */ new Map();
4184
4872
  const collectLabels = (n) => {
4185
4873
  if (!Array.isArray(n)) return;
4186
- if (isBlockLabel(n[0]) && typeof n[1] === "string" && n[1][0] === "$" && !labelRename.has(n[1]))
4874
+ if (isBranchScope(n[0]) && typeof n[1] === "string" && n[1][0] === "$" && !labelRename.has(n[1]))
4187
4875
  labelRename.set(n[1], `$__inl${uid2}L_${n[1].slice(1)}`);
4188
4876
  for (let i = 1; i < n.length; i++) collectLabels(n[i]);
4189
4877
  };
@@ -4194,7 +4882,7 @@ var inlineOnce = (ast) => {
4194
4882
  if ((op === "local.get" || op === "local.set" || op === "local.tee") && typeof n[1] === "string" && rename.has(n[1]))
4195
4883
  return [op, rename.get(n[1]), ...n.slice(2).map(sub)];
4196
4884
  if (op === "return") return ["br", exit, ...n.slice(1).map(sub)];
4197
- if (isBlockLabel(op) && typeof n[1] === "string" && labelRename.has(n[1]))
4885
+ if (isBranchScope(op) && typeof n[1] === "string" && labelRename.has(n[1]))
4198
4886
  return [op, labelRename.get(n[1]), ...n.slice(2).map(sub)];
4199
4887
  if (isBranch(op)) return [op, ...n.slice(1).map((c) => typeof c === "string" && labelRename.has(c) ? labelRename.get(c) : sub(c))];
4200
4888
  return n.map((c, i) => i === 0 ? c : sub(c));
@@ -4204,7 +4892,7 @@ var inlineOnce = (ast) => {
4204
4892
  if (fn === callee || done) continue;
4205
4893
  const start = bodyStart(fn);
4206
4894
  for (let i = start; i < fn.length; i++) {
4207
- const replaced = walkPost(fn[i], (n) => {
4895
+ const replaced = walkPost2(fn[i], (n) => {
4208
4896
  if (done || !Array.isArray(n) || n[0] !== "call" || n[1] !== calleeName) return;
4209
4897
  const args = n.slice(2);
4210
4898
  if (args.length !== params.length) return;
@@ -4269,7 +4957,7 @@ var targetsLabel = (body, label) => {
4269
4957
  return found;
4270
4958
  };
4271
4959
  var mergeBlocks = (ast) => {
4272
- walkPost(ast, (node) => {
4960
+ walkPost2(ast, (node) => {
4273
4961
  if (!Array.isArray(node) || node[0] !== "block") return;
4274
4962
  let bi = 1, label = null;
4275
4963
  if (typeof node[1] === "string" && node[1][0] === "$") {
@@ -4298,7 +4986,7 @@ var mergeBlocks = (ast) => {
4298
4986
  node.length = 0;
4299
4987
  for (const tok of only) node.push(tok);
4300
4988
  });
4301
- walk(ast, (node) => {
4989
+ walk2(ast, (node) => {
4302
4990
  if (!isScopeNode(node)) return;
4303
4991
  let i = 1;
4304
4992
  while (i < node.length) {
@@ -4372,7 +5060,7 @@ var mergeBlocks = (ast) => {
4372
5060
  return ast;
4373
5061
  };
4374
5062
  var coalesceLocals = (ast) => {
4375
- walk(ast, (funcNode) => {
5063
+ walk2(ast, (funcNode) => {
4376
5064
  if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
4377
5065
  const decls = /* @__PURE__ */ new Map();
4378
5066
  for (const sub of funcNode) {
@@ -4444,7 +5132,7 @@ var coalesceLocals = (ast) => {
4444
5132
  } else slots.push({ primary: name2, type, end: range.end });
4445
5133
  }
4446
5134
  if (rename.size === 0) return;
4447
- walk(funcNode, (n) => {
5135
+ walk2(funcNode, (n) => {
4448
5136
  if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.set" || n[0] === "local.tee") && rename.has(n[1])) {
4449
5137
  n[1] = rename.get(n[1]);
4450
5138
  }
@@ -4453,14 +5141,17 @@ var coalesceLocals = (ast) => {
4453
5141
  return ast;
4454
5142
  };
4455
5143
  var vacuum = (ast) => {
4456
- return walkPost(ast, (node) => {
5144
+ return walkPost2(ast, (node) => {
4457
5145
  if (!Array.isArray(node)) return;
4458
5146
  const op = node[0];
4459
5147
  if (op === "nop") return ["nop"];
4460
- if (op === "drop" && node.length === 2 && isPure(node[1])) {
4461
- return ["nop"];
5148
+ if (op === "drop" && node.length === 2) {
5149
+ const eff = dropEffects(node[1]);
5150
+ if (eff.length === 0) return ["nop"];
5151
+ if (eff.length === 1) return eff[0];
5152
+ return ["block", ...eff];
4462
5153
  }
4463
- if (op === "select" && node.length >= 4 && equal(node[1], node[2])) return node[1];
5154
+ if (op === "select" && node.length >= 4 && equal(node[1], node[2]) && isPure(node[3])) return node[1];
4464
5155
  if (op === "if") {
4465
5156
  const { cond, thenBranch, elseBranch } = parseIf(node);
4466
5157
  const thenEmpty = !thenBranch || thenBranch.length <= 1;
@@ -4470,14 +5161,19 @@ var vacuum = (ast) => {
4470
5161
  return node.filter((c) => c !== elseBranch);
4471
5162
  }
4472
5163
  }
4473
- if (op === "func" || op === "block" || op === "loop" || op === "then" || op === "else") {
5164
+ if (isScopeNode(node)) {
4474
5165
  const cleaned = [op];
4475
5166
  for (let i = 1; i < node.length; i++) {
4476
5167
  const child = node[i];
4477
5168
  if (child === "nop" || Array.isArray(child) && child[0] === "nop") continue;
4478
5169
  const next = node[i + 1];
4479
5170
  const isDrop = next === "drop" || Array.isArray(next) && next[0] === "drop" && next.length === 1;
4480
- if (Array.isArray(child) && isPure(child) && isDrop) {
5171
+ if (Array.isArray(child) && isDrop && isPure(child)) {
5172
+ i++;
5173
+ continue;
5174
+ }
5175
+ if (Array.isArray(child) && isDrop && child[0] === "local.tee" && child.length === 3) {
5176
+ cleaned.push(["local.set", child[1], child[2]]);
4481
5177
  i++;
4482
5178
  continue;
4483
5179
  }
@@ -4487,72 +5183,73 @@ var vacuum = (ast) => {
4487
5183
  }
4488
5184
  });
4489
5185
  };
5186
+ var selfFold = (val) => (a, b) => equal(a, b) && isPure(a) ? val : null;
4490
5187
  var PEEPHOLE = {
4491
- // Self-cancelling / tautological binary ops
4492
- "i32.sub": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
4493
- "i64.sub": (a, b) => equal(a, b) ? ["i64.const", 0n] : null,
4494
- "i32.xor": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
4495
- "i64.xor": (a, b) => equal(a, b) ? ["i64.const", 0n] : null,
4496
- "i32.eq": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
4497
- "i64.eq": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
4498
- "i32.ne": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
4499
- "i64.ne": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
4500
- "i32.lt_s": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
4501
- "i32.lt_u": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
4502
- "i32.gt_s": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
4503
- "i32.gt_u": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
4504
- "i32.le_s": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
4505
- "i32.le_u": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
4506
- "i32.ge_s": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
4507
- "i32.ge_u": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
4508
- "i64.lt_s": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
4509
- "i64.lt_u": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
4510
- "i64.gt_s": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
4511
- "i64.gt_u": (a, b) => equal(a, b) ? ["i32.const", 0] : null,
4512
- "i64.le_s": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
4513
- "i64.le_u": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
4514
- "i64.ge_s": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
4515
- "i64.ge_u": (a, b) => equal(a, b) ? ["i32.const", 1] : null,
4516
- // Zero/all-bits absorption
5188
+ // Self-cancelling / tautological binary ops — drop both (equal) operands.
5189
+ "i32.sub": selfFold(["i32.const", 0]),
5190
+ "i64.sub": selfFold(["i64.const", 0]),
5191
+ "i32.xor": selfFold(["i32.const", 0]),
5192
+ "i64.xor": selfFold(["i64.const", 0]),
5193
+ "i32.eq": selfFold(["i32.const", 1]),
5194
+ "i64.eq": selfFold(["i32.const", 1]),
5195
+ "i32.ne": selfFold(["i32.const", 0]),
5196
+ "i64.ne": selfFold(["i32.const", 0]),
5197
+ "i32.lt_s": selfFold(["i32.const", 0]),
5198
+ "i32.lt_u": selfFold(["i32.const", 0]),
5199
+ "i32.gt_s": selfFold(["i32.const", 0]),
5200
+ "i32.gt_u": selfFold(["i32.const", 0]),
5201
+ "i32.le_s": selfFold(["i32.const", 1]),
5202
+ "i32.le_u": selfFold(["i32.const", 1]),
5203
+ "i32.ge_s": selfFold(["i32.const", 1]),
5204
+ "i32.ge_u": selfFold(["i32.const", 1]),
5205
+ "i64.lt_s": selfFold(["i32.const", 0]),
5206
+ "i64.lt_u": selfFold(["i32.const", 0]),
5207
+ "i64.gt_s": selfFold(["i32.const", 0]),
5208
+ "i64.gt_u": selfFold(["i32.const", 0]),
5209
+ "i64.le_s": selfFold(["i32.const", 1]),
5210
+ "i64.le_u": selfFold(["i32.const", 1]),
5211
+ "i64.ge_s": selfFold(["i32.const", 1]),
5212
+ "i64.ge_u": selfFold(["i32.const", 1]),
5213
+ // Zero/all-bits absorption — drops the NON-const operand, so guard its purity.
4517
5214
  "i32.mul": (a, b) => {
4518
- const ca = getConst(a), cb = getConst(b);
4519
- if (ca?.value === 0 || cb?.value === 0) return ["i32.const", 0];
5215
+ if (getConst(b)?.value === 0 && isPure(a)) return ["i32.const", 0];
5216
+ if (getConst(a)?.value === 0 && isPure(b)) return ["i32.const", 0];
4520
5217
  return null;
4521
5218
  },
4522
5219
  "i64.mul": (a, b) => {
4523
- const ca = getConst(a), cb = getConst(b);
4524
- if (ca?.value === 0n || cb?.value === 0n) return ["i64.const", 0n];
5220
+ if (getConst(b)?.value === ZERO64 && isPure(a)) return ["i64.const", 0];
5221
+ if (getConst(a)?.value === ZERO64 && isPure(b)) return ["i64.const", 0];
4525
5222
  return null;
4526
5223
  },
4527
5224
  "i32.and": (a, b) => {
4528
- if (equal(a, b)) return a;
4529
- const ca = getConst(a), cb = getConst(b);
4530
- if (ca?.value === 0 || cb?.value === 0) return ["i32.const", 0];
5225
+ if (equal(a, b) && isPure(b)) return a;
5226
+ if (getConst(b)?.value === 0 && isPure(a)) return ["i32.const", 0];
5227
+ if (getConst(a)?.value === 0 && isPure(b)) return ["i32.const", 0];
4531
5228
  return null;
4532
5229
  },
4533
5230
  "i64.and": (a, b) => {
4534
- if (equal(a, b)) return a;
4535
- const ca = getConst(a), cb = getConst(b);
4536
- if (ca?.value === 0n || cb?.value === 0n) return ["i64.const", 0n];
5231
+ if (equal(a, b) && isPure(b)) return a;
5232
+ if (getConst(b)?.value === ZERO64 && isPure(a)) return ["i64.const", 0];
5233
+ if (getConst(a)?.value === ZERO64 && isPure(b)) return ["i64.const", 0];
4537
5234
  return null;
4538
5235
  },
4539
5236
  "i32.or": (a, b) => {
4540
- if (equal(a, b)) return a;
4541
- const ca = getConst(a), cb = getConst(b);
4542
- if (ca?.value === -1 || cb?.value === -1) return ["i32.const", -1];
5237
+ if (equal(a, b) && isPure(b)) return a;
5238
+ if (getConst(b)?.value === -1 && isPure(a)) return ["i32.const", -1];
5239
+ if (getConst(a)?.value === -1 && isPure(b)) return ["i32.const", -1];
4543
5240
  return null;
4544
5241
  },
4545
5242
  "i64.or": (a, b) => {
4546
- if (equal(a, b)) return a;
4547
- const ca = getConst(a), cb = getConst(b);
4548
- if (ca?.value === -1n || cb?.value === -1n) return ["i64.const", -1n];
5243
+ if (equal(a, b) && isPure(b)) return a;
5244
+ if (getConst(b)?.value === NEG164 && isPure(a)) return ["i64.const", -1];
5245
+ if (getConst(a)?.value === NEG164 && isPure(b)) return ["i64.const", -1];
4549
5246
  return null;
4550
5247
  },
4551
5248
  // (local.set $x (local.get $x)) → nop
4552
5249
  "local.set": (a, b) => Array.isArray(b) && b[0] === "local.get" && b[1] === a ? ["nop"] : null
4553
5250
  };
4554
5251
  var peephole = (ast) => {
4555
- return walkPost(ast, (node) => {
5252
+ return walkPost2(ast, (node) => {
4556
5253
  if (!Array.isArray(node) || node.length !== 3) return;
4557
5254
  const fn = PEEPHOLE[node[0]];
4558
5255
  if (!fn) return;
@@ -4561,7 +5258,8 @@ var peephole = (ast) => {
4561
5258
  });
4562
5259
  };
4563
5260
  var slebSize = (v) => {
4564
- let x = typeof v === "bigint" ? v : BigInt(Math.trunc(Number(v) || 0));
5261
+ let x = typeof v === "bigint" ? v : typeof v === "string" ? BigInt(v.replaceAll("_", "")) : BigInt(Math.trunc(Number(v) || 0));
5262
+ if (x > 0x7fffffffffffffffn) x = x - 0x8000000000000000n - 0x8000000000000000n;
4565
5263
  let n = 1;
4566
5264
  while (true) {
4567
5265
  const b = x & 0x7fn;
@@ -4609,7 +5307,7 @@ var globals = (ast) => {
4609
5307
  }
4610
5308
  if (constGlobals.size === 0) return ast;
4611
5309
  const reads = /* @__PURE__ */ new Map();
4612
- walk(ast, (n) => {
5310
+ walk2(ast, (n) => {
4613
5311
  if (!Array.isArray(n)) return;
4614
5312
  const ref = n[1];
4615
5313
  if (typeof ref !== "string" || ref[0] !== "$") return;
@@ -4627,9 +5325,9 @@ var globals = (ast) => {
4627
5325
  if (after <= before) propagate2.add(name2);
4628
5326
  }
4629
5327
  if (propagate2.size === 0) return ast;
4630
- walkPost(ast, (node) => {
5328
+ walkPost2(ast, (node) => {
4631
5329
  if (!Array.isArray(node) || node[0] !== "global.get" || node.length !== 2) return;
4632
- if (propagate2.has(node[1])) return clone(constGlobals.get(node[1]));
5330
+ if (propagate2.has(node[1])) return clone2(constGlobals.get(node[1]));
4633
5331
  });
4634
5332
  for (let i = ast.length - 1; i >= 1; i--) {
4635
5333
  const n = ast[i];
@@ -4638,7 +5336,7 @@ var globals = (ast) => {
4638
5336
  return ast;
4639
5337
  };
4640
5338
  var offset = (ast) => {
4641
- return walkPost(ast, (node) => {
5339
+ return walkPost2(ast, (node) => {
4642
5340
  if (!Array.isArray(node)) return;
4643
5341
  const op = node[0];
4644
5342
  if (typeof op !== "string" || !op.endsWith("load") && !op.endsWith("store")) return;
@@ -4689,7 +5387,7 @@ var offset = (ast) => {
4689
5387
  });
4690
5388
  };
4691
5389
  var unbranch = (ast) => {
4692
- walk(ast, (node) => {
5390
+ walk2(ast, (node) => {
4693
5391
  if (!Array.isArray(node)) return;
4694
5392
  const op = node[0];
4695
5393
  if (op !== "block") return;
@@ -4721,7 +5419,7 @@ var unbranch = (ast) => {
4721
5419
  return ast;
4722
5420
  };
4723
5421
  var loopify = (ast) => {
4724
- walk(ast, (node) => {
5422
+ walk2(ast, (node) => {
4725
5423
  if (!Array.isArray(node) || node[0] !== "block") return;
4726
5424
  let bi = 1, label = null;
4727
5425
  if (typeof node[1] === "string" && node[1][0] === "$") {
@@ -4780,10 +5478,10 @@ var loopify = (ast) => {
4780
5478
  var stripmut = (ast) => {
4781
5479
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4782
5480
  const written = /* @__PURE__ */ new Set();
4783
- walk(ast, (n) => {
5481
+ walk2(ast, (n) => {
4784
5482
  if (Array.isArray(n) && n[0] === "global.set" && typeof n[1] === "string") written.add(n[1]);
4785
5483
  });
4786
- return walkPost(ast, (node) => {
5484
+ return walkPost2(ast, (node) => {
4787
5485
  if (!Array.isArray(node) || node[0] !== "global") return;
4788
5486
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4789
5487
  if (!name2 || written.has(name2)) return;
@@ -4797,7 +5495,7 @@ var stripmut = (ast) => {
4797
5495
  });
4798
5496
  };
4799
5497
  var brif = (ast) => {
4800
- return walkPost(ast, (node) => {
5498
+ return walkPost2(ast, (node) => {
4801
5499
  if (!Array.isArray(node) || node[0] !== "if") return;
4802
5500
  const { cond, thenBranch, elseBranch } = parseIf(node);
4803
5501
  const thenEmpty = !thenBranch || thenBranch.length <= 1;
@@ -4813,7 +5511,7 @@ var brif = (ast) => {
4813
5511
  });
4814
5512
  };
4815
5513
  var foldarms = (ast) => {
4816
- return walkPost(ast, (node) => {
5514
+ return walkPost2(ast, (node) => {
4817
5515
  if (!Array.isArray(node) || node[0] !== "if") return;
4818
5516
  const { thenBranch, elseBranch } = parseIf(node);
4819
5517
  if (!thenBranch || !elseBranch) return;
@@ -4886,10 +5584,10 @@ var dedupe = (ast) => {
4886
5584
  if (!name2) continue;
4887
5585
  const localNames = /* @__PURE__ */ new Set();
4888
5586
  if (typeof node[1] === "string" && node[1][0] === "$") localNames.add(node[1]);
4889
- walk(node, (n) => {
5587
+ walk2(node, (n) => {
4890
5588
  if (!Array.isArray(n) || typeof n[1] !== "string" || n[1][0] !== "$") return;
4891
5589
  const op = n[0];
4892
- if (op === "param" || op === "local" || op === "block" || op === "loop" || op === "if") {
5590
+ if (op === "param" || op === "local" || isBranchScope(op)) {
4893
5591
  localNames.add(n[1]);
4894
5592
  }
4895
5593
  });
@@ -4901,7 +5599,7 @@ var dedupe = (ast) => {
4901
5599
  }
4902
5600
  }
4903
5601
  if (redirects.size === 0) return ast;
4904
- walkPost(ast, (node) => {
5602
+ walkPost2(ast, (node) => {
4905
5603
  if (!Array.isArray(node)) return;
4906
5604
  const op = node[0];
4907
5605
  if ((op === "call" || op === "return_call") && redirects.has(node[1])) {
@@ -4948,7 +5646,7 @@ var dedupTypes = (ast) => {
4948
5646
  if (name2 && redirects.has(name2)) ast.splice(i, 1);
4949
5647
  }
4950
5648
  }
4951
- walkPost(ast, (node) => {
5649
+ walkPost2(ast, (node) => {
4952
5650
  if (!Array.isArray(node)) return;
4953
5651
  const op = node[0];
4954
5652
  if (op === "func") {
@@ -4984,39 +5682,39 @@ var dedupTypes = (ast) => {
4984
5682
  return ast;
4985
5683
  };
4986
5684
  var parseDataString = (str2) => {
4987
- if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return new Uint8Array();
4988
- const inner = str2.slice(1, -1);
5685
+ if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return [];
4989
5686
  const bytes = [];
4990
- for (let i = 0; i < inner.length; i++) {
4991
- if (inner[i] === "\\") {
4992
- const next = inner[++i];
4993
- if (next === "x" || next === "X") {
4994
- bytes.push(parseInt(inner.slice(i + 1, i + 3), 16));
4995
- i += 2;
4996
- } else if (/[0-9a-fA-F]/.test(next) && /[0-9a-fA-F]/.test(inner[i + 1])) {
4997
- bytes.push(parseInt(inner.slice(i, i + 2), 16));
4998
- i++;
4999
- } else if (next === "n") bytes.push(10);
5000
- else if (next === "t") bytes.push(9);
5001
- else if (next === "r") bytes.push(13);
5002
- else if (next === "\\") bytes.push(92);
5003
- else if (next === '"') bytes.push(34);
5004
- else bytes.push(next.charCodeAt(0));
5687
+ const hexv = (c) => c >= 48 && c <= 57 ? c - 48 : c >= 97 && c <= 102 ? c - 87 : c >= 65 && c <= 70 ? c - 55 : -1;
5688
+ const end = str2.length - 1;
5689
+ for (let i = 1; i < end; i++) {
5690
+ const c = str2.charCodeAt(i);
5691
+ if (c !== 92) {
5692
+ bytes.push(c);
5693
+ continue;
5694
+ }
5695
+ const n = str2.charCodeAt(++i);
5696
+ if (n === 120 || n === 88) {
5697
+ bytes.push(hexv(str2.charCodeAt(i + 1)) << 4 | hexv(str2.charCodeAt(i + 2)));
5698
+ i += 2;
5005
5699
  } else {
5006
- bytes.push(inner.charCodeAt(i));
5700
+ const h1 = hexv(n), h2 = i + 1 < end ? hexv(str2.charCodeAt(i + 1)) : -1;
5701
+ if (h1 >= 0 && h2 >= 0) {
5702
+ bytes.push(h1 << 4 | h2);
5703
+ i++;
5704
+ } else if (n === 110) bytes.push(10);
5705
+ else if (n === 116) bytes.push(9);
5706
+ else if (n === 114) bytes.push(13);
5707
+ else bytes.push(n);
5007
5708
  }
5008
5709
  }
5009
- return new Uint8Array(bytes);
5710
+ return bytes;
5010
5711
  };
5011
- var encodeDataString = (bytes) => {
5712
+ var encodeDataString = (bytes, end) => {
5012
5713
  let str2 = '"';
5013
- for (let i = 0; i < bytes.length; i++) {
5714
+ for (let i = 0; i < end; i++) {
5014
5715
  const b = bytes[i];
5015
- if (b >= 32 && b < 127 && b !== 34 && b !== 92) {
5016
- str2 += String.fromCharCode(b);
5017
- } else {
5018
- str2 += "\\" + b.toString(16).padStart(2, "0");
5019
- }
5716
+ if (b >= 32 && b < 127 && b !== 34 && b !== 92) str2 += String.fromCharCode(b);
5717
+ else str2 += "\\" + b.toString(16).padStart(2, "0");
5020
5718
  }
5021
5719
  return str2 + '"';
5022
5720
  };
@@ -5024,7 +5722,8 @@ var trimTrailingZeros = (items) => {
5024
5722
  const bytes = [];
5025
5723
  for (const item of items) {
5026
5724
  if (typeof item === "string") {
5027
- bytes.push(...parseDataString(item));
5725
+ const chunk = parseDataString(item);
5726
+ for (let i = 0; i < chunk.length; i++) bytes.push(chunk[i]);
5028
5727
  } else if (Array.isArray(item) && item[0] === "i8") {
5029
5728
  for (let i = 1; i < item.length; i++) bytes.push(Number(item[i]) & 255);
5030
5729
  } else {
@@ -5035,7 +5734,7 @@ var trimTrailingZeros = (items) => {
5035
5734
  while (end > 0 && bytes[end - 1] === 0) end--;
5036
5735
  if (end === bytes.length) return items;
5037
5736
  if (end === 0) return [];
5038
- return [encodeDataString(new Uint8Array(bytes.slice(0, end)))];
5737
+ return [encodeDataString(bytes, end)];
5039
5738
  };
5040
5739
  var getDataOffset = (node) => {
5041
5740
  let idx = 1;
@@ -5083,11 +5782,9 @@ var mergeDataSegments = (a, b) => {
5083
5782
  if (aContent.length === 1 && bContent.length === 1 && typeof aContent[0] === "string" && typeof bContent[0] === "string") {
5084
5783
  const aBytes = parseDataString(aContent[0]);
5085
5784
  const bBytes = parseDataString(bContent[0]);
5086
- const merged = new Uint8Array(aBytes.length + bBytes.length);
5087
- merged.set(aBytes);
5088
- merged.set(bBytes, aBytes.length);
5785
+ for (let i = 0; i < bBytes.length; i++) aBytes.push(bBytes[i]);
5089
5786
  a.length = aIdx;
5090
- a.push(encodeDataString(merged));
5787
+ a.push(encodeDataString(aBytes, aBytes.length));
5091
5788
  return true;
5092
5789
  }
5093
5790
  a.length = aIdx;
@@ -5175,7 +5872,7 @@ var minifyImports = (ast) => {
5175
5872
  };
5176
5873
  var reorderSafe = (ast) => {
5177
5874
  let safe = true;
5178
- walk(ast, (n) => {
5875
+ walk2(ast, (n) => {
5179
5876
  if (!safe || !Array.isArray(n)) return;
5180
5877
  const op = n[0];
5181
5878
  if (op === "func" && (typeof n[1] !== "string" || n[1][0] !== "$")) safe = false;
@@ -5200,7 +5897,7 @@ var reorder = (ast) => {
5200
5897
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
5201
5898
  if (!reorderSafe(ast)) return ast;
5202
5899
  const callCounts = /* @__PURE__ */ new Map();
5203
- walk(ast, (n) => {
5900
+ walk2(ast, (n) => {
5204
5901
  if (!Array.isArray(n)) return;
5205
5902
  if (n[0] === "call" || n[0] === "return_call") {
5206
5903
  callCounts.set(n[1], (callCounts.get(n[1]) || 0) + 1);
@@ -5222,12 +5919,14 @@ var reorder = (ast) => {
5222
5919
  var PASSES = [
5223
5920
  ["stripmut", stripmut, true, "strip mut from never-written globals"],
5224
5921
  ["globals", globals, true, "propagate immutable global constants"],
5922
+ ["guardRefine", guardRefine, false, "fold NaN-box tag reads under dominating tag guards (jz NaN-box-specific; opt-in)"],
5225
5923
  ["fold", fold, true, "constant folding"],
5226
5924
  ["identity", identity, true, "remove identity ops (x + 0 \u2192 x)"],
5227
5925
  ["peephole", peephole, true, "x-x\u21920, x&0\u21920, etc."],
5228
5926
  ["strength", strength, true, "strength reduction (x * 2 \u2192 x << 1)"],
5229
5927
  ["branch", branch, true, "simplify constant branches"],
5230
5928
  ["propagate", propagate, true, "forward-propagate single-use locals & tiny consts (never inflates)"],
5929
+ ["devirt", devirt, false, "call_indirect with a constant or known closure-const index \u2192 direct/guarded calls \u2014 grows bytes for speed"],
5231
5930
  ["inlineOnce", inlineOnce, true, "inline single-call functions into their lone caller (never duplicates)"],
5232
5931
  ["inline", inline, false, "inline tiny functions \u2014 can duplicate bodies"],
5233
5932
  ["offset", offset, true, "fold add+const into load/store offset"],
@@ -5260,6 +5959,42 @@ var normalize3 = (opts) => {
5260
5959
  for (const p of PASSES) m[p[0]] = set ? set.has("all") || set.has(p[0]) : p[2];
5261
5960
  return m;
5262
5961
  };
5962
+ var mayInline = (ast) => {
5963
+ if (!Array.isArray(ast)) return false;
5964
+ const callRefs = /* @__PURE__ */ new Map(), pinned = /* @__PURE__ */ new Set(), other = /* @__PURE__ */ new Set();
5965
+ const scan = (n) => {
5966
+ if (!Array.isArray(n)) return;
5967
+ const op = n[0];
5968
+ if (op === "call" && typeof n[1] === "string") callRefs.set(n[1], (callRefs.get(n[1]) || 0) + 1);
5969
+ else if (op === "return_call" && typeof n[1] === "string") other.add(n[1]);
5970
+ else if (op === "ref.func" && typeof n[1] === "string") pinned.add(n[1]);
5971
+ else if (op === "export" && Array.isArray(n[2]) && n[2][0] === "func" && typeof n[2][1] === "string") pinned.add(n[2][1]);
5972
+ else if (op === "start" && typeof n[1] === "string") pinned.add(n[1]);
5973
+ else if (op === "elem") {
5974
+ for (const c of n) if (typeof c === "string" && c[0] === "$") pinned.add(c);
5975
+ }
5976
+ for (let i = 1; i < n.length; i++) scan(n[i]);
5977
+ };
5978
+ scan(ast);
5979
+ for (const n of ast) {
5980
+ if (!Array.isArray(n) || n[0] !== "func" || typeof n[1] !== "string") continue;
5981
+ const name2 = n[1];
5982
+ if (callRefs.get(name2) !== 1 || pinned.has(name2) || other.has(name2)) continue;
5983
+ if (n.some((c) => Array.isArray(c) && c[0] === "export")) continue;
5984
+ return true;
5985
+ }
5986
+ return false;
5987
+ };
5988
+ var START_HEAD = /* @__PURE__ */ new Set(["export", "type", "param", "result", "local"]);
5989
+ function pruneEmptyStart(ast) {
5990
+ if (!Array.isArray(ast) || ast[0] !== "module") return ast;
5991
+ const fn = ast.find((n) => Array.isArray(n) && n[0] === "func" && n[1] === "$__start");
5992
+ if (!fn) return ast;
5993
+ let b = 2;
5994
+ while (b < fn.length && Array.isArray(fn[b]) && START_HEAD.has(fn[b][0])) b++;
5995
+ if (b < fn.length) return ast;
5996
+ return ast.filter((n) => !(Array.isArray(n) && (n[0] === "func" && n[1] === "$__start" || n[0] === "start" && n[1] === "$__start")));
5997
+ }
5263
5998
  function optimize(ast, opts = true) {
5264
5999
  if (typeof ast === "string") ast = parse_default(ast);
5265
6000
  const strictGuard = opts === true;
@@ -5267,19 +6002,39 @@ function optimize(ast, opts = true) {
5267
6002
  const log = opts.log ? (msg, delta) => opts.log(msg, delta) : () => {
5268
6003
  };
5269
6004
  const verbose = opts.verbose || opts.log;
5270
- ast = clone(ast);
6005
+ ast = clone2(ast);
6006
+ const runInline = (a) => {
6007
+ if (!opts.inline) return a;
6008
+ a = inline(a, { simdOnly: opts.inline === "simd" });
6009
+ if (opts.propagate) a = propagate(a);
6010
+ if (opts.mergeBlocks) a = mergeBlocks(a);
6011
+ if (opts.vacuum) a = vacuum(a);
6012
+ if (opts.coalesceLocals) a = coalesceLocals(a);
6013
+ return a;
6014
+ };
6015
+ const finish = (a) => {
6016
+ a = runInline(a);
6017
+ return pruneEmptyStart(opts.devirt ? devirt(a) : a);
6018
+ };
6019
+ if (!((opts.inlineOnce || opts.inline) && mayInline(ast))) {
6020
+ for (let round = 0; round < 3; round++) {
6021
+ const beforeRound2 = clone2(ast);
6022
+ for (const [key, fn] of PASSES) if (opts[key] && key !== "inlineOnce" && key !== "inline" && key !== "devirt") ast = fn(ast);
6023
+ if (equal(beforeRound2, ast)) break;
6024
+ if (verbose) log(` round ${round + 1} applied`);
6025
+ }
6026
+ return finish(ast);
6027
+ }
5271
6028
  let beforeRound = null;
5272
6029
  let sizeBefore = binarySize(ast);
5273
6030
  for (let round = 0; round < 3; round++) {
5274
- beforeRound = clone(ast);
5275
- for (const [key, fn] of PASSES) if (opts[key]) ast = fn(ast);
6031
+ beforeRound = clone2(ast);
6032
+ for (const [key, fn] of PASSES) if (opts[key] && key !== "devirt" && key !== "inline") ast = fn(ast);
5276
6033
  if (opts.propagate && (opts.inlineOnce || opts.inline)) ast = propagate(ast);
5277
6034
  if (equal(beforeRound, ast)) break;
5278
6035
  const sizeAfter = binarySize(ast);
5279
6036
  const delta = sizeAfter - sizeBefore;
5280
- if (verbose || delta !== 0) {
5281
- log(` round ${round + 1}: ${delta > 0 ? "+" : ""}${delta} bytes`, delta);
5282
- }
6037
+ if (verbose || delta !== 0) log(` round ${round + 1}: ${delta > 0 ? "+" : ""}${delta} bytes`, delta);
5283
6038
  const tolerance = strictGuard ? 0 : 16;
5284
6039
  if (delta > tolerance) {
5285
6040
  if (verbose) log(` \u26A0 round ${round + 1} inflated by ${delta} bytes, reverting`, delta);
@@ -5288,7 +6043,7 @@ function optimize(ast, opts = true) {
5288
6043
  }
5289
6044
  sizeBefore = sizeAfter;
5290
6045
  }
5291
- return ast;
6046
+ return finish(ast);
5292
6047
  }
5293
6048
 
5294
6049
  // src/template.js
@@ -5305,11 +6060,11 @@ var exprType = (node, ctx = {}) => {
5305
6060
  if (op === "call" && ctx.funcs?.[args[0]]) return ctx.funcs[args[0]].result?.[0];
5306
6061
  return null;
5307
6062
  };
5308
- function walk2(node, fn) {
6063
+ function walk3(node, fn) {
5309
6064
  node = fn(node);
5310
6065
  if (Array.isArray(node)) {
5311
6066
  for (let i = 0; i < node.length; i++) {
5312
- let child = walk2(node[i], fn);
6067
+ let child = walk3(node[i], fn);
5313
6068
  if (child?._splice) node.splice(i, 1, ...child), i += child.length - 1;
5314
6069
  else node[i] = child;
5315
6070
  }
@@ -5319,7 +6074,7 @@ function walk2(node, fn) {
5319
6074
  function inferImports(ast, funcs) {
5320
6075
  const imports = [];
5321
6076
  const importMap = /* @__PURE__ */ new Map();
5322
- walk2(ast, (node) => {
6077
+ walk3(ast, (node) => {
5323
6078
  if (!Array.isArray(node)) return node;
5324
6079
  if (node[0] === "call" && typeof node[1] === "function") {
5325
6080
  const fn = node[1];
@@ -5360,7 +6115,7 @@ function compile2(backend2, source, values) {
5360
6115
  let ast = parse(src);
5361
6116
  const funcsToImport = [];
5362
6117
  let idx = 0;
5363
- ast = walk2(ast, (node) => {
6118
+ ast = walk3(ast, (node) => {
5364
6119
  if (node === PUA) {
5365
6120
  const value = values[idx++];
5366
6121
  if (typeof value === "function") {