watr 4.6.10 → 4.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/watr.js CHANGED
@@ -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,463 @@ 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, pin = EMPTY_SET } = {}) => {
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) || pin.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;
4080
4671
  }
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--;
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);
4683
+ }
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 inlineOnce = (ast, { pin = EMPTY_SET } = {}) => {
4793
+ if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4794
+ const bodyStart = inlBodyStart, callsSelf = inlCallsSelf, unsafe = inlUnsafe, isBranch = inlIsBranch;
4795
+ const zeroFor = inlZeroFor, needsReset = inlNeedsReset;
4796
+ for (let round = 0; round < MAX_INLINE_ROUNDS; round++) {
4109
4797
  const funcs = ast.filter((n) => Array.isArray(n) && n[0] === "func");
4110
4798
  const funcByName = /* @__PURE__ */ new Map();
4111
4799
  for (const n of funcs) if (typeof n[1] === "string") funcByName.set(n[1], n);
@@ -4118,12 +4806,12 @@ var inlineOnce = (ast) => {
4118
4806
  for (let i = 1; i < n.length; i++) countRefs(n[i]);
4119
4807
  };
4120
4808
  countRefs(ast);
4121
- const pinned = /* @__PURE__ */ new Set();
4122
- for (const n of ast) if (!Array.isArray(n) || n[0] !== "func") collectPinned(n, pinned);
4809
+ const pinned = inlBuildPinned(ast);
4123
4810
  let calleeName = null;
4124
4811
  for (const [name2, fn] of funcByName) {
4125
4812
  if (pinned.has(name2) || otherRef.has(name2)) continue;
4126
4813
  if (callRefs.get(name2) !== 1) continue;
4814
+ if (pin.has(name2)) continue;
4127
4815
  if (callsSelf(fn, name2)) continue;
4128
4816
  let ok = true, nResult = 0;
4129
4817
  for (let i = 2; i < fn.length; i++) {
@@ -4179,11 +4867,10 @@ var inlineOnce = (ast) => {
4179
4867
  const rename = /* @__PURE__ */ new Map();
4180
4868
  for (const p of params) rename.set(p.name, `$__inl${uid2}_${p.name.slice(1)}`);
4181
4869
  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
4870
  const labelRename = /* @__PURE__ */ new Map();
4184
4871
  const collectLabels = (n) => {
4185
4872
  if (!Array.isArray(n)) return;
4186
- if (isBlockLabel(n[0]) && typeof n[1] === "string" && n[1][0] === "$" && !labelRename.has(n[1]))
4873
+ if (isBranchScope(n[0]) && typeof n[1] === "string" && n[1][0] === "$" && !labelRename.has(n[1]))
4187
4874
  labelRename.set(n[1], `$__inl${uid2}L_${n[1].slice(1)}`);
4188
4875
  for (let i = 1; i < n.length; i++) collectLabels(n[i]);
4189
4876
  };
@@ -4194,7 +4881,7 @@ var inlineOnce = (ast) => {
4194
4881
  if ((op === "local.get" || op === "local.set" || op === "local.tee") && typeof n[1] === "string" && rename.has(n[1]))
4195
4882
  return [op, rename.get(n[1]), ...n.slice(2).map(sub)];
4196
4883
  if (op === "return") return ["br", exit, ...n.slice(1).map(sub)];
4197
- if (isBlockLabel(op) && typeof n[1] === "string" && labelRename.has(n[1]))
4884
+ if (isBranchScope(op) && typeof n[1] === "string" && labelRename.has(n[1]))
4198
4885
  return [op, labelRename.get(n[1]), ...n.slice(2).map(sub)];
4199
4886
  if (isBranch(op)) return [op, ...n.slice(1).map((c) => typeof c === "string" && labelRename.has(c) ? labelRename.get(c) : sub(c))];
4200
4887
  return n.map((c, i) => i === 0 ? c : sub(c));
@@ -4204,7 +4891,7 @@ var inlineOnce = (ast) => {
4204
4891
  if (fn === callee || done) continue;
4205
4892
  const start = bodyStart(fn);
4206
4893
  for (let i = start; i < fn.length; i++) {
4207
- const replaced = walkPost(fn[i], (n) => {
4894
+ const replaced = walkPost2(fn[i], (n) => {
4208
4895
  if (done || !Array.isArray(n) || n[0] !== "call" || n[1] !== calleeName) return;
4209
4896
  const args = n.slice(2);
4210
4897
  if (args.length !== params.length) return;
@@ -4269,7 +4956,7 @@ var targetsLabel = (body, label) => {
4269
4956
  return found;
4270
4957
  };
4271
4958
  var mergeBlocks = (ast) => {
4272
- walkPost(ast, (node) => {
4959
+ walkPost2(ast, (node) => {
4273
4960
  if (!Array.isArray(node) || node[0] !== "block") return;
4274
4961
  let bi = 1, label = null;
4275
4962
  if (typeof node[1] === "string" && node[1][0] === "$") {
@@ -4298,7 +4985,7 @@ var mergeBlocks = (ast) => {
4298
4985
  node.length = 0;
4299
4986
  for (const tok of only) node.push(tok);
4300
4987
  });
4301
- walk(ast, (node) => {
4988
+ walk2(ast, (node) => {
4302
4989
  if (!isScopeNode(node)) return;
4303
4990
  let i = 1;
4304
4991
  while (i < node.length) {
@@ -4372,7 +5059,7 @@ var mergeBlocks = (ast) => {
4372
5059
  return ast;
4373
5060
  };
4374
5061
  var coalesceLocals = (ast) => {
4375
- walk(ast, (funcNode) => {
5062
+ walk2(ast, (funcNode) => {
4376
5063
  if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
4377
5064
  const decls = /* @__PURE__ */ new Map();
4378
5065
  for (const sub of funcNode) {
@@ -4444,7 +5131,7 @@ var coalesceLocals = (ast) => {
4444
5131
  } else slots.push({ primary: name2, type, end: range.end });
4445
5132
  }
4446
5133
  if (rename.size === 0) return;
4447
- walk(funcNode, (n) => {
5134
+ walk2(funcNode, (n) => {
4448
5135
  if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.set" || n[0] === "local.tee") && rename.has(n[1])) {
4449
5136
  n[1] = rename.get(n[1]);
4450
5137
  }
@@ -4453,14 +5140,17 @@ var coalesceLocals = (ast) => {
4453
5140
  return ast;
4454
5141
  };
4455
5142
  var vacuum = (ast) => {
4456
- return walkPost(ast, (node) => {
5143
+ return walkPost2(ast, (node) => {
4457
5144
  if (!Array.isArray(node)) return;
4458
5145
  const op = node[0];
4459
5146
  if (op === "nop") return ["nop"];
4460
- if (op === "drop" && node.length === 2 && isPure(node[1])) {
4461
- return ["nop"];
5147
+ if (op === "drop" && node.length === 2) {
5148
+ const eff = dropEffects(node[1]);
5149
+ if (eff.length === 0) return ["nop"];
5150
+ if (eff.length === 1) return eff[0];
5151
+ return ["block", ...eff];
4462
5152
  }
4463
- if (op === "select" && node.length >= 4 && equal(node[1], node[2])) return node[1];
5153
+ if (op === "select" && node.length >= 4 && equal(node[1], node[2]) && isPure(node[3])) return node[1];
4464
5154
  if (op === "if") {
4465
5155
  const { cond, thenBranch, elseBranch } = parseIf(node);
4466
5156
  const thenEmpty = !thenBranch || thenBranch.length <= 1;
@@ -4470,14 +5160,19 @@ var vacuum = (ast) => {
4470
5160
  return node.filter((c) => c !== elseBranch);
4471
5161
  }
4472
5162
  }
4473
- if (op === "func" || op === "block" || op === "loop" || op === "then" || op === "else") {
5163
+ if (isScopeNode(node)) {
4474
5164
  const cleaned = [op];
4475
5165
  for (let i = 1; i < node.length; i++) {
4476
5166
  const child = node[i];
4477
5167
  if (child === "nop" || Array.isArray(child) && child[0] === "nop") continue;
4478
5168
  const next = node[i + 1];
4479
5169
  const isDrop = next === "drop" || Array.isArray(next) && next[0] === "drop" && next.length === 1;
4480
- if (Array.isArray(child) && isPure(child) && isDrop) {
5170
+ if (Array.isArray(child) && isDrop && isPure(child)) {
5171
+ i++;
5172
+ continue;
5173
+ }
5174
+ if (Array.isArray(child) && isDrop && child[0] === "local.tee" && child.length === 3) {
5175
+ cleaned.push(["local.set", child[1], child[2]]);
4481
5176
  i++;
4482
5177
  continue;
4483
5178
  }
@@ -4487,72 +5182,73 @@ var vacuum = (ast) => {
4487
5182
  }
4488
5183
  });
4489
5184
  };
5185
+ var selfFold = (val) => (a, b) => equal(a, b) && isPure(a) ? val : null;
4490
5186
  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
5187
+ // Self-cancelling / tautological binary ops — drop both (equal) operands.
5188
+ "i32.sub": selfFold(["i32.const", 0]),
5189
+ "i64.sub": selfFold(["i64.const", 0]),
5190
+ "i32.xor": selfFold(["i32.const", 0]),
5191
+ "i64.xor": selfFold(["i64.const", 0]),
5192
+ "i32.eq": selfFold(["i32.const", 1]),
5193
+ "i64.eq": selfFold(["i32.const", 1]),
5194
+ "i32.ne": selfFold(["i32.const", 0]),
5195
+ "i64.ne": selfFold(["i32.const", 0]),
5196
+ "i32.lt_s": selfFold(["i32.const", 0]),
5197
+ "i32.lt_u": selfFold(["i32.const", 0]),
5198
+ "i32.gt_s": selfFold(["i32.const", 0]),
5199
+ "i32.gt_u": selfFold(["i32.const", 0]),
5200
+ "i32.le_s": selfFold(["i32.const", 1]),
5201
+ "i32.le_u": selfFold(["i32.const", 1]),
5202
+ "i32.ge_s": selfFold(["i32.const", 1]),
5203
+ "i32.ge_u": selfFold(["i32.const", 1]),
5204
+ "i64.lt_s": selfFold(["i32.const", 0]),
5205
+ "i64.lt_u": selfFold(["i32.const", 0]),
5206
+ "i64.gt_s": selfFold(["i32.const", 0]),
5207
+ "i64.gt_u": selfFold(["i32.const", 0]),
5208
+ "i64.le_s": selfFold(["i32.const", 1]),
5209
+ "i64.le_u": selfFold(["i32.const", 1]),
5210
+ "i64.ge_s": selfFold(["i32.const", 1]),
5211
+ "i64.ge_u": selfFold(["i32.const", 1]),
5212
+ // Zero/all-bits absorption — drops the NON-const operand, so guard its purity.
4517
5213
  "i32.mul": (a, b) => {
4518
- const ca = getConst(a), cb = getConst(b);
4519
- if (ca?.value === 0 || cb?.value === 0) return ["i32.const", 0];
5214
+ if (getConst(b)?.value === 0 && isPure(a)) return ["i32.const", 0];
5215
+ if (getConst(a)?.value === 0 && isPure(b)) return ["i32.const", 0];
4520
5216
  return null;
4521
5217
  },
4522
5218
  "i64.mul": (a, b) => {
4523
- const ca = getConst(a), cb = getConst(b);
4524
- if (ca?.value === 0n || cb?.value === 0n) return ["i64.const", 0n];
5219
+ if (getConst(b)?.value === ZERO64 && isPure(a)) return ["i64.const", 0];
5220
+ if (getConst(a)?.value === ZERO64 && isPure(b)) return ["i64.const", 0];
4525
5221
  return null;
4526
5222
  },
4527
5223
  "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];
5224
+ if (equal(a, b) && isPure(b)) return a;
5225
+ if (getConst(b)?.value === 0 && isPure(a)) return ["i32.const", 0];
5226
+ if (getConst(a)?.value === 0 && isPure(b)) return ["i32.const", 0];
4531
5227
  return null;
4532
5228
  },
4533
5229
  "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];
5230
+ if (equal(a, b) && isPure(b)) return a;
5231
+ if (getConst(b)?.value === ZERO64 && isPure(a)) return ["i64.const", 0];
5232
+ if (getConst(a)?.value === ZERO64 && isPure(b)) return ["i64.const", 0];
4537
5233
  return null;
4538
5234
  },
4539
5235
  "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];
5236
+ if (equal(a, b) && isPure(b)) return a;
5237
+ if (getConst(b)?.value === -1 && isPure(a)) return ["i32.const", -1];
5238
+ if (getConst(a)?.value === -1 && isPure(b)) return ["i32.const", -1];
4543
5239
  return null;
4544
5240
  },
4545
5241
  "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];
5242
+ if (equal(a, b) && isPure(b)) return a;
5243
+ if (getConst(b)?.value === NEG164 && isPure(a)) return ["i64.const", -1];
5244
+ if (getConst(a)?.value === NEG164 && isPure(b)) return ["i64.const", -1];
4549
5245
  return null;
4550
5246
  },
4551
5247
  // (local.set $x (local.get $x)) → nop
4552
5248
  "local.set": (a, b) => Array.isArray(b) && b[0] === "local.get" && b[1] === a ? ["nop"] : null
4553
5249
  };
4554
5250
  var peephole = (ast) => {
4555
- return walkPost(ast, (node) => {
5251
+ return walkPost2(ast, (node) => {
4556
5252
  if (!Array.isArray(node) || node.length !== 3) return;
4557
5253
  const fn = PEEPHOLE[node[0]];
4558
5254
  if (!fn) return;
@@ -4561,7 +5257,8 @@ var peephole = (ast) => {
4561
5257
  });
4562
5258
  };
4563
5259
  var slebSize = (v) => {
4564
- let x = typeof v === "bigint" ? v : BigInt(Math.trunc(Number(v) || 0));
5260
+ let x = typeof v === "bigint" ? v : typeof v === "string" ? BigInt(v.replaceAll("_", "")) : BigInt(Math.trunc(Number(v) || 0));
5261
+ if (x > 0x7fffffffffffffffn) x = x - 0x8000000000000000n - 0x8000000000000000n;
4565
5262
  let n = 1;
4566
5263
  while (true) {
4567
5264
  const b = x & 0x7fn;
@@ -4609,7 +5306,7 @@ var globals = (ast) => {
4609
5306
  }
4610
5307
  if (constGlobals.size === 0) return ast;
4611
5308
  const reads = /* @__PURE__ */ new Map();
4612
- walk(ast, (n) => {
5309
+ walk2(ast, (n) => {
4613
5310
  if (!Array.isArray(n)) return;
4614
5311
  const ref = n[1];
4615
5312
  if (typeof ref !== "string" || ref[0] !== "$") return;
@@ -4627,9 +5324,9 @@ var globals = (ast) => {
4627
5324
  if (after <= before) propagate2.add(name2);
4628
5325
  }
4629
5326
  if (propagate2.size === 0) return ast;
4630
- walkPost(ast, (node) => {
5327
+ walkPost2(ast, (node) => {
4631
5328
  if (!Array.isArray(node) || node[0] !== "global.get" || node.length !== 2) return;
4632
- if (propagate2.has(node[1])) return clone(constGlobals.get(node[1]));
5329
+ if (propagate2.has(node[1])) return clone2(constGlobals.get(node[1]));
4633
5330
  });
4634
5331
  for (let i = ast.length - 1; i >= 1; i--) {
4635
5332
  const n = ast[i];
@@ -4638,7 +5335,7 @@ var globals = (ast) => {
4638
5335
  return ast;
4639
5336
  };
4640
5337
  var offset = (ast) => {
4641
- return walkPost(ast, (node) => {
5338
+ return walkPost2(ast, (node) => {
4642
5339
  if (!Array.isArray(node)) return;
4643
5340
  const op = node[0];
4644
5341
  if (typeof op !== "string" || !op.endsWith("load") && !op.endsWith("store")) return;
@@ -4689,7 +5386,7 @@ var offset = (ast) => {
4689
5386
  });
4690
5387
  };
4691
5388
  var unbranch = (ast) => {
4692
- walk(ast, (node) => {
5389
+ walk2(ast, (node) => {
4693
5390
  if (!Array.isArray(node)) return;
4694
5391
  const op = node[0];
4695
5392
  if (op !== "block") return;
@@ -4721,7 +5418,7 @@ var unbranch = (ast) => {
4721
5418
  return ast;
4722
5419
  };
4723
5420
  var loopify = (ast) => {
4724
- walk(ast, (node) => {
5421
+ walk2(ast, (node) => {
4725
5422
  if (!Array.isArray(node) || node[0] !== "block") return;
4726
5423
  let bi = 1, label = null;
4727
5424
  if (typeof node[1] === "string" && node[1][0] === "$") {
@@ -4780,10 +5477,10 @@ var loopify = (ast) => {
4780
5477
  var stripmut = (ast) => {
4781
5478
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
4782
5479
  const written = /* @__PURE__ */ new Set();
4783
- walk(ast, (n) => {
5480
+ walk2(ast, (n) => {
4784
5481
  if (Array.isArray(n) && n[0] === "global.set" && typeof n[1] === "string") written.add(n[1]);
4785
5482
  });
4786
- return walkPost(ast, (node) => {
5483
+ return walkPost2(ast, (node) => {
4787
5484
  if (!Array.isArray(node) || node[0] !== "global") return;
4788
5485
  const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
4789
5486
  if (!name2 || written.has(name2)) return;
@@ -4797,7 +5494,7 @@ var stripmut = (ast) => {
4797
5494
  });
4798
5495
  };
4799
5496
  var brif = (ast) => {
4800
- return walkPost(ast, (node) => {
5497
+ return walkPost2(ast, (node) => {
4801
5498
  if (!Array.isArray(node) || node[0] !== "if") return;
4802
5499
  const { cond, thenBranch, elseBranch } = parseIf(node);
4803
5500
  const thenEmpty = !thenBranch || thenBranch.length <= 1;
@@ -4813,7 +5510,7 @@ var brif = (ast) => {
4813
5510
  });
4814
5511
  };
4815
5512
  var foldarms = (ast) => {
4816
- return walkPost(ast, (node) => {
5513
+ return walkPost2(ast, (node) => {
4817
5514
  if (!Array.isArray(node) || node[0] !== "if") return;
4818
5515
  const { thenBranch, elseBranch } = parseIf(node);
4819
5516
  if (!thenBranch || !elseBranch) return;
@@ -4886,10 +5583,10 @@ var dedupe = (ast) => {
4886
5583
  if (!name2) continue;
4887
5584
  const localNames = /* @__PURE__ */ new Set();
4888
5585
  if (typeof node[1] === "string" && node[1][0] === "$") localNames.add(node[1]);
4889
- walk(node, (n) => {
5586
+ walk2(node, (n) => {
4890
5587
  if (!Array.isArray(n) || typeof n[1] !== "string" || n[1][0] !== "$") return;
4891
5588
  const op = n[0];
4892
- if (op === "param" || op === "local" || op === "block" || op === "loop" || op === "if") {
5589
+ if (op === "param" || op === "local" || isBranchScope(op)) {
4893
5590
  localNames.add(n[1]);
4894
5591
  }
4895
5592
  });
@@ -4901,7 +5598,7 @@ var dedupe = (ast) => {
4901
5598
  }
4902
5599
  }
4903
5600
  if (redirects.size === 0) return ast;
4904
- walkPost(ast, (node) => {
5601
+ walkPost2(ast, (node) => {
4905
5602
  if (!Array.isArray(node)) return;
4906
5603
  const op = node[0];
4907
5604
  if ((op === "call" || op === "return_call") && redirects.has(node[1])) {
@@ -4948,7 +5645,7 @@ var dedupTypes = (ast) => {
4948
5645
  if (name2 && redirects.has(name2)) ast.splice(i, 1);
4949
5646
  }
4950
5647
  }
4951
- walkPost(ast, (node) => {
5648
+ walkPost2(ast, (node) => {
4952
5649
  if (!Array.isArray(node)) return;
4953
5650
  const op = node[0];
4954
5651
  if (op === "func") {
@@ -4984,39 +5681,39 @@ var dedupTypes = (ast) => {
4984
5681
  return ast;
4985
5682
  };
4986
5683
  var parseDataString = (str2) => {
4987
- if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return new Uint8Array();
4988
- const inner = str2.slice(1, -1);
5684
+ if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return [];
4989
5685
  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));
5686
+ const hexv = (c) => c >= 48 && c <= 57 ? c - 48 : c >= 97 && c <= 102 ? c - 87 : c >= 65 && c <= 70 ? c - 55 : -1;
5687
+ const end = str2.length - 1;
5688
+ for (let i = 1; i < end; i++) {
5689
+ const c = str2.charCodeAt(i);
5690
+ if (c !== 92) {
5691
+ bytes.push(c);
5692
+ continue;
5693
+ }
5694
+ const n = str2.charCodeAt(++i);
5695
+ if (n === 120 || n === 88) {
5696
+ bytes.push(hexv(str2.charCodeAt(i + 1)) << 4 | hexv(str2.charCodeAt(i + 2)));
5697
+ i += 2;
5005
5698
  } else {
5006
- bytes.push(inner.charCodeAt(i));
5699
+ const h1 = hexv(n), h2 = i + 1 < end ? hexv(str2.charCodeAt(i + 1)) : -1;
5700
+ if (h1 >= 0 && h2 >= 0) {
5701
+ bytes.push(h1 << 4 | h2);
5702
+ i++;
5703
+ } else if (n === 110) bytes.push(10);
5704
+ else if (n === 116) bytes.push(9);
5705
+ else if (n === 114) bytes.push(13);
5706
+ else bytes.push(n);
5007
5707
  }
5008
5708
  }
5009
- return new Uint8Array(bytes);
5709
+ return bytes;
5010
5710
  };
5011
- var encodeDataString = (bytes) => {
5711
+ var encodeDataString = (bytes, end) => {
5012
5712
  let str2 = '"';
5013
- for (let i = 0; i < bytes.length; i++) {
5713
+ for (let i = 0; i < end; i++) {
5014
5714
  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
- }
5715
+ if (b >= 32 && b < 127 && b !== 34 && b !== 92) str2 += String.fromCharCode(b);
5716
+ else str2 += "\\" + b.toString(16).padStart(2, "0");
5020
5717
  }
5021
5718
  return str2 + '"';
5022
5719
  };
@@ -5024,7 +5721,8 @@ var trimTrailingZeros = (items) => {
5024
5721
  const bytes = [];
5025
5722
  for (const item of items) {
5026
5723
  if (typeof item === "string") {
5027
- bytes.push(...parseDataString(item));
5724
+ const chunk = parseDataString(item);
5725
+ for (let i = 0; i < chunk.length; i++) bytes.push(chunk[i]);
5028
5726
  } else if (Array.isArray(item) && item[0] === "i8") {
5029
5727
  for (let i = 1; i < item.length; i++) bytes.push(Number(item[i]) & 255);
5030
5728
  } else {
@@ -5035,7 +5733,7 @@ var trimTrailingZeros = (items) => {
5035
5733
  while (end > 0 && bytes[end - 1] === 0) end--;
5036
5734
  if (end === bytes.length) return items;
5037
5735
  if (end === 0) return [];
5038
- return [encodeDataString(new Uint8Array(bytes.slice(0, end)))];
5736
+ return [encodeDataString(bytes, end)];
5039
5737
  };
5040
5738
  var getDataOffset = (node) => {
5041
5739
  let idx = 1;
@@ -5083,11 +5781,9 @@ var mergeDataSegments = (a, b) => {
5083
5781
  if (aContent.length === 1 && bContent.length === 1 && typeof aContent[0] === "string" && typeof bContent[0] === "string") {
5084
5782
  const aBytes = parseDataString(aContent[0]);
5085
5783
  const bBytes = parseDataString(bContent[0]);
5086
- const merged = new Uint8Array(aBytes.length + bBytes.length);
5087
- merged.set(aBytes);
5088
- merged.set(bBytes, aBytes.length);
5784
+ for (let i = 0; i < bBytes.length; i++) aBytes.push(bBytes[i]);
5089
5785
  a.length = aIdx;
5090
- a.push(encodeDataString(merged));
5786
+ a.push(encodeDataString(aBytes, aBytes.length));
5091
5787
  return true;
5092
5788
  }
5093
5789
  a.length = aIdx;
@@ -5175,7 +5871,7 @@ var minifyImports = (ast) => {
5175
5871
  };
5176
5872
  var reorderSafe = (ast) => {
5177
5873
  let safe = true;
5178
- walk(ast, (n) => {
5874
+ walk2(ast, (n) => {
5179
5875
  if (!safe || !Array.isArray(n)) return;
5180
5876
  const op = n[0];
5181
5877
  if (op === "func" && (typeof n[1] !== "string" || n[1][0] !== "$")) safe = false;
@@ -5200,7 +5896,7 @@ var reorder = (ast) => {
5200
5896
  if (!Array.isArray(ast) || ast[0] !== "module") return ast;
5201
5897
  if (!reorderSafe(ast)) return ast;
5202
5898
  const callCounts = /* @__PURE__ */ new Map();
5203
- walk(ast, (n) => {
5899
+ walk2(ast, (n) => {
5204
5900
  if (!Array.isArray(n)) return;
5205
5901
  if (n[0] === "call" || n[0] === "return_call") {
5206
5902
  callCounts.set(n[1], (callCounts.get(n[1]) || 0) + 1);
@@ -5222,12 +5918,14 @@ var reorder = (ast) => {
5222
5918
  var PASSES = [
5223
5919
  ["stripmut", stripmut, true, "strip mut from never-written globals"],
5224
5920
  ["globals", globals, true, "propagate immutable global constants"],
5921
+ ["guardRefine", guardRefine, false, "fold NaN-box tag reads under dominating tag guards (jz NaN-box-specific; opt-in)"],
5225
5922
  ["fold", fold, true, "constant folding"],
5226
5923
  ["identity", identity, true, "remove identity ops (x + 0 \u2192 x)"],
5227
5924
  ["peephole", peephole, true, "x-x\u21920, x&0\u21920, etc."],
5228
5925
  ["strength", strength, true, "strength reduction (x * 2 \u2192 x << 1)"],
5229
5926
  ["branch", branch, true, "simplify constant branches"],
5230
5927
  ["propagate", propagate, true, "forward-propagate single-use locals & tiny consts (never inflates)"],
5928
+ ["devirt", devirt, false, "call_indirect with a constant or known closure-const index \u2192 direct/guarded calls \u2014 grows bytes for speed"],
5231
5929
  ["inlineOnce", inlineOnce, true, "inline single-call functions into their lone caller (never duplicates)"],
5232
5930
  ["inline", inline, false, "inline tiny functions \u2014 can duplicate bodies"],
5233
5931
  ["offset", offset, true, "fold add+const into load/store offset"],
@@ -5260,26 +5958,83 @@ var normalize3 = (opts) => {
5260
5958
  for (const p of PASSES) m[p[0]] = set ? set.has("all") || set.has(p[0]) : p[2];
5261
5959
  return m;
5262
5960
  };
5961
+ var mayInline = (ast) => {
5962
+ if (!Array.isArray(ast)) return false;
5963
+ const callRefs = /* @__PURE__ */ new Map(), pinned = /* @__PURE__ */ new Set(), other = /* @__PURE__ */ new Set();
5964
+ const scan = (n) => {
5965
+ if (!Array.isArray(n)) return;
5966
+ const op = n[0];
5967
+ if (op === "call" && typeof n[1] === "string") callRefs.set(n[1], (callRefs.get(n[1]) || 0) + 1);
5968
+ else if (op === "return_call" && typeof n[1] === "string") other.add(n[1]);
5969
+ else if (op === "ref.func" && typeof n[1] === "string") pinned.add(n[1]);
5970
+ else if (op === "export" && Array.isArray(n[2]) && n[2][0] === "func" && typeof n[2][1] === "string") pinned.add(n[2][1]);
5971
+ else if (op === "start" && typeof n[1] === "string") pinned.add(n[1]);
5972
+ else if (op === "elem") {
5973
+ for (const c of n) if (typeof c === "string" && c[0] === "$") pinned.add(c);
5974
+ }
5975
+ for (let i = 1; i < n.length; i++) scan(n[i]);
5976
+ };
5977
+ scan(ast);
5978
+ for (const n of ast) {
5979
+ if (!Array.isArray(n) || n[0] !== "func" || typeof n[1] !== "string") continue;
5980
+ const name2 = n[1];
5981
+ if (callRefs.get(name2) !== 1 || pinned.has(name2) || other.has(name2)) continue;
5982
+ if (n.some((c) => Array.isArray(c) && c[0] === "export")) continue;
5983
+ return true;
5984
+ }
5985
+ return false;
5986
+ };
5987
+ var START_HEAD = /* @__PURE__ */ new Set(["export", "type", "param", "result", "local"]);
5988
+ function pruneEmptyStart(ast) {
5989
+ if (!Array.isArray(ast) || ast[0] !== "module") return ast;
5990
+ const fn = ast.find((n) => Array.isArray(n) && n[0] === "func" && n[1] === "$__start");
5991
+ if (!fn) return ast;
5992
+ let b = 2;
5993
+ while (b < fn.length && Array.isArray(fn[b]) && START_HEAD.has(fn[b][0])) b++;
5994
+ if (b < fn.length) return ast;
5995
+ return ast.filter((n) => !(Array.isArray(n) && (n[0] === "func" && n[1] === "$__start" || n[0] === "start" && n[1] === "$__start")));
5996
+ }
5263
5997
  function optimize(ast, opts = true) {
5264
5998
  if (typeof ast === "string") ast = parse_default(ast);
5265
5999
  const strictGuard = opts === true;
5266
6000
  opts = normalize3(opts);
6001
+ opts.pin = opts.pin instanceof Set ? opts.pin : new Set(opts.pin || []);
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", pin: opts.pin });
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, opts);
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, opts);
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") {