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 +1138 -383
- package/dist/watr.min.js +6 -6
- package/dist/watr.wasm +0 -0
- package/package.json +8 -4
- package/src/compile.js +93 -26
- package/src/optimize.js +1358 -395
- package/types/src/compile.d.ts +5 -1
- package/types/src/compile.d.ts.map +1 -1
- package/types/src/optimize.d.ts +34 -20
- package/types/src/optimize.d.ts.map +1 -1
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
|
|
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
|
-
|
|
1500
|
+
sections2.push(0, ...vec([...name2, ...content]));
|
|
1501
1501
|
}
|
|
1502
|
-
return
|
|
1502
|
+
return sections2;
|
|
1503
1503
|
};
|
|
1504
1504
|
const globalSection = bin(SECTION.global);
|
|
1505
1505
|
const elemSection = bin(SECTION.elem);
|
|
1506
|
-
const
|
|
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
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
3345
|
+
return "0x" + _hex8(_ru32[1]) + _hex8(_ru32[0]);
|
|
3247
3346
|
};
|
|
3248
|
-
var f64FromI64 = (
|
|
3249
|
-
|
|
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(
|
|
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) =>
|
|
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
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
"i64.
|
|
3325
|
-
"i64.
|
|
3326
|
-
"i64.
|
|
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 ===
|
|
3336
|
-
"i64.extend_i32_s": (a) =>
|
|
3337
|
-
"i64.extend_i32_u": (a) =>
|
|
3338
|
-
"i64.extend8_s": (a) =>
|
|
3339
|
-
|
|
3340
|
-
|
|
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
|
-
|
|
3373
|
-
|
|
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) =>
|
|
3377
|
-
"f64.convert_i64_u": (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
|
-
|
|
3384
|
-
|
|
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" ?
|
|
3397
|
-
if (op === "i64.const") return { type: "i64", value:
|
|
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
|
-
|
|
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")
|
|
3412
|
-
|
|
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
|
|
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 (
|
|
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(
|
|
3589
|
+
if (r === null || r === void 0) return;
|
|
3590
|
+
return makeConst(resultType2(node[0]), r);
|
|
3426
3591
|
}
|
|
3427
|
-
if (
|
|
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(
|
|
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(
|
|
3611
|
+
"i64.add": commutativeIdentity(ZERO64),
|
|
3447
3612
|
// x - 0 → x
|
|
3448
3613
|
"i32.sub": rightIdentity(0),
|
|
3449
|
-
"i64.sub": rightIdentity(
|
|
3614
|
+
"i64.sub": rightIdentity(ZERO64),
|
|
3450
3615
|
// x * 1 → x, 1 * x → x
|
|
3451
3616
|
"i32.mul": commutativeIdentity(1),
|
|
3452
|
-
"i64.mul": commutativeIdentity(
|
|
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(
|
|
3457
|
-
"i64.div_u": rightIdentity(
|
|
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(
|
|
3625
|
+
"i64.and": commutativeIdentity(NEG164),
|
|
3461
3626
|
// x | 0 → x, 0 | x → x
|
|
3462
3627
|
"i32.or": commutativeIdentity(0),
|
|
3463
|
-
"i64.or": commutativeIdentity(
|
|
3628
|
+
"i64.or": commutativeIdentity(ZERO64),
|
|
3464
3629
|
// x ^ 0 → x, 0 ^ x → x
|
|
3465
3630
|
"i32.xor": commutativeIdentity(0),
|
|
3466
|
-
"i64.xor": commutativeIdentity(
|
|
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(
|
|
3472
|
-
"i64.shr_s": rightIdentity(
|
|
3473
|
-
"i64.shr_u": rightIdentity(
|
|
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
|
|
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
|
|
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 (
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
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 (
|
|
3525
|
-
|
|
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 (
|
|
3538
|
-
return ["i64.and", a, ["i64.const",
|
|
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
|
|
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 !==
|
|
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 ===
|
|
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 ===
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
3733
|
-
return v
|
|
4080
|
+
const v = BigInt(c.value);
|
|
4081
|
+
return v <= 63n || v >= 0xffffffffffffffc0n;
|
|
3734
4082
|
}
|
|
3735
4083
|
return false;
|
|
3736
4084
|
};
|
|
3737
|
-
var
|
|
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
|
-
|
|
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
|
|
4140
|
+
if (k && canSubst(k)) return clone2(k.val);
|
|
3783
4141
|
return node;
|
|
3784
4142
|
}
|
|
3785
4143
|
let inner = known;
|
|
3786
|
-
if (op
|
|
4144
|
+
if (isBranchScope(op)) {
|
|
3787
4145
|
let cloned = null;
|
|
3788
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
4225
|
+
const prev = clone2(instr2);
|
|
3864
4226
|
substGets(instr2, known);
|
|
3865
4227
|
if (!equal(prev, instr2)) changed = true;
|
|
3866
|
-
|
|
3867
|
-
if (Array.isArray(n)
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
4329
|
+
walkPost2(funcNode, (n) => {
|
|
3948
4330
|
if (isScopeNode(n)) scopes.push(n);
|
|
3949
4331
|
});
|
|
3950
|
-
for (let round = 0; 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
|
|
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
|
|
3967
|
-
for (
|
|
3968
|
-
|
|
3969
|
-
const
|
|
3970
|
-
if (
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
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 (
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
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
|
|
4034
|
-
var inlineOnce = (ast) => {
|
|
4569
|
+
var devirt = (ast) => {
|
|
4035
4570
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4036
|
-
const
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
const
|
|
4043
|
-
|
|
4044
|
-
if (
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
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
|
-
|
|
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
|
|
4055
|
-
|
|
4056
|
-
if ((n
|
|
4057
|
-
|
|
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
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
if (
|
|
4075
|
-
|
|
4076
|
-
if (
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
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
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
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
|
-
|
|
4095
|
-
|
|
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
|
|
4098
|
-
|
|
4099
|
-
const
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
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 (
|
|
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 =
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
4461
|
-
|
|
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 (
|
|
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)
|
|
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": (
|
|
4493
|
-
"i64.sub": (
|
|
4494
|
-
"i32.xor": (
|
|
4495
|
-
"i64.xor": (
|
|
4496
|
-
"i32.eq": (
|
|
4497
|
-
"i64.eq": (
|
|
4498
|
-
"i32.ne": (
|
|
4499
|
-
"i64.ne": (
|
|
4500
|
-
"i32.lt_s": (
|
|
4501
|
-
"i32.lt_u": (
|
|
4502
|
-
"i32.gt_s": (
|
|
4503
|
-
"i32.gt_u": (
|
|
4504
|
-
"i32.le_s": (
|
|
4505
|
-
"i32.le_u": (
|
|
4506
|
-
"i32.ge_s": (
|
|
4507
|
-
"i32.ge_u": (
|
|
4508
|
-
"i64.lt_s": (
|
|
4509
|
-
"i64.lt_u": (
|
|
4510
|
-
"i64.gt_s": (
|
|
4511
|
-
"i64.gt_u": (
|
|
4512
|
-
"i64.le_s": (
|
|
4513
|
-
"i64.le_u": (
|
|
4514
|
-
"i64.ge_s": (
|
|
4515
|
-
"i64.ge_u": (
|
|
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
|
-
|
|
4519
|
-
if (
|
|
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
|
-
|
|
4524
|
-
if (
|
|
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
|
-
|
|
4530
|
-
if (
|
|
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
|
-
|
|
4536
|
-
if (
|
|
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
|
-
|
|
4542
|
-
if (
|
|
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
|
-
|
|
4548
|
-
if (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4988
|
-
const inner = str2.slice(1, -1);
|
|
5684
|
+
if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return [];
|
|
4989
5685
|
const bytes = [];
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
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
|
-
|
|
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
|
|
5709
|
+
return bytes;
|
|
5010
5710
|
};
|
|
5011
|
-
var encodeDataString = (bytes) => {
|
|
5711
|
+
var encodeDataString = (bytes, end) => {
|
|
5012
5712
|
let str2 = '"';
|
|
5013
|
-
for (let i = 0; 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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
6118
|
+
ast = walk3(ast, (node) => {
|
|
5364
6119
|
if (node === PUA) {
|
|
5365
6120
|
const value = values[idx++];
|
|
5366
6121
|
if (typeof value === "function") {
|