watr 4.6.9 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/watr.js +1141 -385
- 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 +1362 -387
- package/types/src/compile.d.ts +5 -1
- package/types/src/compile.d.ts.map +1 -1
- package/types/src/optimize.d.ts +28 -46
- 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,464 @@ var propagate = (ast) => {
|
|
|
3955
4337
|
if (eliminateSetGetPairs(scope, params, useCounts)) progressed = true;
|
|
3956
4338
|
if (createLocalTees(scope, params, useCounts)) progressed = true;
|
|
3957
4339
|
if (eliminateDeadStores(scope, params, useCounts)) progressed = true;
|
|
4340
|
+
if (eliminateAdjacentDeadStores(scope, params)) progressed = true;
|
|
3958
4341
|
}
|
|
3959
4342
|
if (!progressed) break;
|
|
3960
4343
|
}
|
|
3961
4344
|
});
|
|
3962
4345
|
return ast;
|
|
3963
4346
|
};
|
|
3964
|
-
var
|
|
4347
|
+
var inlineUid = 0;
|
|
4348
|
+
var INL_HEAD = /* @__PURE__ */ new Set(["export", "type", "param", "result", "local"]);
|
|
4349
|
+
var inlBodyStart = (fn) => {
|
|
4350
|
+
let i = 2;
|
|
4351
|
+
while (i < fn.length && (typeof fn[i] === "string" || Array.isArray(fn[i]) && INL_HEAD.has(fn[i][0]))) i++;
|
|
4352
|
+
return i;
|
|
4353
|
+
};
|
|
4354
|
+
var inlIsBranch = (op) => op === "br" || op === "br_if" || op === "br_table";
|
|
4355
|
+
var inlUnsafe = (n) => {
|
|
4356
|
+
if (!Array.isArray(n)) return false;
|
|
4357
|
+
const op = n[0];
|
|
4358
|
+
if (op === "return_call" || op === "return_call_indirect" || op === "return_call_ref") return true;
|
|
4359
|
+
if (op === "try" || op === "try_table" || op === "delegate" || op === "rethrow") return true;
|
|
4360
|
+
if (inlIsBranch(op)) {
|
|
4361
|
+
for (let i = 1; i < n.length; i++) if (typeof n[i] === "number" || typeof n[i] === "string" && /^\d+$/.test(n[i])) return true;
|
|
4362
|
+
}
|
|
4363
|
+
for (let i = 1; i < n.length; i++) if (inlUnsafe(n[i])) return true;
|
|
4364
|
+
return false;
|
|
4365
|
+
};
|
|
4366
|
+
var inlCallsSelf = (n, name2) => {
|
|
4367
|
+
if (!Array.isArray(n)) return false;
|
|
4368
|
+
if ((n[0] === "call" || n[0] === "return_call") && n[1] === name2) return true;
|
|
4369
|
+
for (let i = 1; i < n.length; i++) if (inlCallsSelf(n[i], name2)) return true;
|
|
4370
|
+
return false;
|
|
4371
|
+
};
|
|
4372
|
+
var inlZeroFor = (t) => {
|
|
4373
|
+
if (t === "i32") return ["i32.const", 0];
|
|
4374
|
+
if (t === "i64") return ["i64.const", 0];
|
|
4375
|
+
if (t === "f32") return ["f32.const", 0];
|
|
4376
|
+
if (t === "f64") return ["f64.const", 0];
|
|
4377
|
+
if (t === "v128") return ["v128.const", "i64x2", "0", "0"];
|
|
4378
|
+
return null;
|
|
4379
|
+
};
|
|
4380
|
+
var inlNeedsReset = (body, name2) => {
|
|
4381
|
+
let seen = false, conditional = false, depth = 0;
|
|
4382
|
+
const visit = (n) => {
|
|
4383
|
+
if (seen || !Array.isArray(n)) return;
|
|
4384
|
+
const op = n[0];
|
|
4385
|
+
const isSet = op === "local.set" || op === "local.tee";
|
|
4386
|
+
if ((isSet || op === "local.get") && n[1] === name2) {
|
|
4387
|
+
if (isSet) for (let i = 2; i < n.length && !seen; i++) visit(n[i]);
|
|
4388
|
+
if (seen) return;
|
|
4389
|
+
seen = true;
|
|
4390
|
+
if (op === "local.get" || depth > 0) conditional = true;
|
|
4391
|
+
return;
|
|
4392
|
+
}
|
|
4393
|
+
const isIf = op === "if";
|
|
4394
|
+
for (let i = 1; i < n.length && !seen; i++) {
|
|
4395
|
+
const c = n[i];
|
|
4396
|
+
const cond = isIf && Array.isArray(c) && (c[0] === "then" || c[0] === "else");
|
|
4397
|
+
if (cond) depth++;
|
|
4398
|
+
visit(c);
|
|
4399
|
+
if (cond) depth--;
|
|
4400
|
+
}
|
|
4401
|
+
};
|
|
4402
|
+
for (const n of body) {
|
|
4403
|
+
if (seen) break;
|
|
4404
|
+
visit(n);
|
|
4405
|
+
}
|
|
4406
|
+
if (!seen) return false;
|
|
4407
|
+
return conditional;
|
|
4408
|
+
};
|
|
4409
|
+
var inlCollectPinned = (n, pinned) => {
|
|
4410
|
+
if (!Array.isArray(n)) return;
|
|
4411
|
+
const op = n[0];
|
|
4412
|
+
if (op === "export" && Array.isArray(n[2]) && n[2][0] === "func" && typeof n[2][1] === "string") pinned.add(n[2][1]);
|
|
4413
|
+
else if (op === "start" && typeof n[1] === "string") pinned.add(n[1]);
|
|
4414
|
+
else if (op === "ref.func" && typeof n[1] === "string") pinned.add(n[1]);
|
|
4415
|
+
else if (op === "elem") {
|
|
4416
|
+
for (const c of n) if (typeof c === "string" && c[0] === "$") pinned.add(c);
|
|
4417
|
+
}
|
|
4418
|
+
for (const c of n) inlCollectPinned(c, pinned);
|
|
4419
|
+
};
|
|
4420
|
+
var inlBuildPinned = (ast) => {
|
|
4421
|
+
const pinned = /* @__PURE__ */ new Set();
|
|
4422
|
+
for (const n of ast) if (!Array.isArray(n) || n[0] !== "func") inlCollectPinned(n, pinned);
|
|
4423
|
+
return pinned;
|
|
4424
|
+
};
|
|
4425
|
+
var inlParse = (fn) => {
|
|
4426
|
+
const params = [], locals = [];
|
|
4427
|
+
let inlResult = null, ok = true, nResult = 0;
|
|
4428
|
+
for (let i = 2; i < fn.length; i++) {
|
|
4429
|
+
const c = fn[i];
|
|
4430
|
+
if (typeof c === "string") continue;
|
|
4431
|
+
if (!Array.isArray(c)) {
|
|
4432
|
+
ok = false;
|
|
4433
|
+
break;
|
|
4434
|
+
}
|
|
4435
|
+
if (c[0] === "param") {
|
|
4436
|
+
if (typeof c[1] !== "string" || c[1][0] !== "$") {
|
|
4437
|
+
ok = false;
|
|
4438
|
+
break;
|
|
4439
|
+
}
|
|
4440
|
+
params.push({ name: c[1], type: c[2] });
|
|
4441
|
+
} else if (c[0] === "local") {
|
|
4442
|
+
if (typeof c[1] !== "string" || c[1][0] !== "$" || !inlZeroFor(c[2])) {
|
|
4443
|
+
ok = false;
|
|
4444
|
+
break;
|
|
4445
|
+
}
|
|
4446
|
+
locals.push({ name: c[1], type: c[2] });
|
|
4447
|
+
} else if (c[0] === "result") {
|
|
4448
|
+
nResult += c.length - 1;
|
|
4449
|
+
if (c.length > 1) inlResult = c[1];
|
|
4450
|
+
} else if (c[0] === "export") {
|
|
4451
|
+
ok = false;
|
|
4452
|
+
break;
|
|
4453
|
+
} else if (c[0] === "type") continue;
|
|
4454
|
+
else break;
|
|
4455
|
+
}
|
|
4456
|
+
if (nResult > 1) ok = false;
|
|
4457
|
+
return ok ? { params, locals, inlResult } : null;
|
|
4458
|
+
};
|
|
4459
|
+
var inlBodySize = (fn) => {
|
|
4460
|
+
let n = 0;
|
|
4461
|
+
const count = (x) => {
|
|
4462
|
+
if (!Array.isArray(x)) return;
|
|
4463
|
+
n++;
|
|
4464
|
+
for (let i = 1; i < x.length; i++) count(x[i]);
|
|
4465
|
+
};
|
|
4466
|
+
for (let i = inlBodyStart(fn); i < fn.length; i++) count(fn[i]);
|
|
4467
|
+
return n;
|
|
4468
|
+
};
|
|
4469
|
+
var buildInline = (params, locals, inlResult, cBody, args) => {
|
|
4470
|
+
const uid2 = ++inlineUid;
|
|
4471
|
+
const exit = `$__inl${uid2}`;
|
|
4472
|
+
const rename = /* @__PURE__ */ new Map();
|
|
4473
|
+
for (const p of params) rename.set(p.name, `$__inl${uid2}_${p.name.slice(1)}`);
|
|
4474
|
+
for (const l of locals) rename.set(l.name, `$__inl${uid2}_${l.name.slice(1)}`);
|
|
4475
|
+
const labelRename = /* @__PURE__ */ new Map();
|
|
4476
|
+
const collectLabels = (n) => {
|
|
4477
|
+
if (!Array.isArray(n)) return;
|
|
4478
|
+
if (isBranchScope(n[0]) && typeof n[1] === "string" && n[1][0] === "$" && !labelRename.has(n[1]))
|
|
4479
|
+
labelRename.set(n[1], `$__inl${uid2}L_${n[1].slice(1)}`);
|
|
4480
|
+
for (let i = 1; i < n.length; i++) collectLabels(n[i]);
|
|
4481
|
+
};
|
|
4482
|
+
for (const n of cBody) collectLabels(n);
|
|
4483
|
+
const sub = (n) => {
|
|
4484
|
+
if (!Array.isArray(n)) return n;
|
|
4485
|
+
const op = n[0];
|
|
4486
|
+
if ((op === "local.get" || op === "local.set" || op === "local.tee") && typeof n[1] === "string" && rename.has(n[1]))
|
|
4487
|
+
return [op, rename.get(n[1]), ...n.slice(2).map(sub)];
|
|
4488
|
+
if (op === "return") return ["br", exit, ...n.slice(1).map(sub)];
|
|
4489
|
+
if (isBranchScope(op) && typeof n[1] === "string" && labelRename.has(n[1]))
|
|
4490
|
+
return [op, labelRename.get(n[1]), ...n.slice(2).map(sub)];
|
|
4491
|
+
if (inlIsBranch(op)) return [op, ...n.slice(1).map((c) => typeof c === "string" && labelRename.has(c) ? labelRename.get(c) : sub(c))];
|
|
4492
|
+
return n.map((c, i) => i === 0 ? c : sub(c));
|
|
4493
|
+
};
|
|
4494
|
+
const setup = params.map((p, k) => ["local.set", rename.get(p.name), args[k]]);
|
|
4495
|
+
const resets = locals.filter((l) => inlNeedsReset(cBody, l.name)).map((l) => ["local.set", rename.get(l.name), inlZeroFor(l.type)]);
|
|
4496
|
+
const inner = cBody.map(sub);
|
|
4497
|
+
const block = inlResult ? ["block", exit, ["result", inlResult], ...setup, ...resets, ...inner] : ["block", exit, ...setup, ...resets, ...inner];
|
|
4498
|
+
const decls = [...params, ...locals].map((p) => ["local", rename.get(p.name), p.type]);
|
|
4499
|
+
return { block, decls };
|
|
4500
|
+
};
|
|
4501
|
+
var INLINE_MAX_NODES = 90;
|
|
4502
|
+
var isV128SimdHelper = (params, inlResult) => inlResult === "v128" && params.length > 0 && params.every((p) => p.type === "v128");
|
|
4503
|
+
var inline = (ast, { simdOnly = false } = {}) => {
|
|
3965
4504
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
3966
|
-
const
|
|
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) || SIMD_PROTECTED.has(name2)) continue;
|
|
4524
|
+
if (!(callRefs.get(name2) >= 1)) continue;
|
|
4525
|
+
if (inlBodySize(fn) > INLINE_MAX_NODES) continue;
|
|
4526
|
+
if (inlCallsSelf(fn, name2)) continue;
|
|
4527
|
+
const p = inlParse(fn);
|
|
4528
|
+
if (!p) continue;
|
|
4529
|
+
if (simdOnly && !isV128SimdHelper(p.params, p.inlResult)) continue;
|
|
4530
|
+
let bad = false;
|
|
4531
|
+
for (let i = inlBodyStart(fn); i < fn.length; i++) if (inlUnsafe(fn[i])) {
|
|
4532
|
+
bad = true;
|
|
4533
|
+
break;
|
|
3991
4534
|
}
|
|
4535
|
+
if (bad) continue;
|
|
4536
|
+
calleeName = name2;
|
|
4537
|
+
parsed = p;
|
|
4538
|
+
break;
|
|
3992
4539
|
}
|
|
3993
|
-
if (
|
|
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 SIMD_PROTECTED = /* @__PURE__ */ new Set(["$math.sin_core", "$math.cos_core", "$math.sin", "$math.cos", "$math.pow", "$math.atan2", "$math.hypot", "$math.log"]);
|
|
4793
|
+
var inlineOnce = (ast) => {
|
|
4794
|
+
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4795
|
+
const bodyStart = inlBodyStart, callsSelf = inlCallsSelf, unsafe = inlUnsafe, isBranch = inlIsBranch;
|
|
4796
|
+
const zeroFor = inlZeroFor, needsReset = inlNeedsReset;
|
|
4797
|
+
for (let round = 0; round < MAX_INLINE_ROUNDS; round++) {
|
|
4109
4798
|
const funcs = ast.filter((n) => Array.isArray(n) && n[0] === "func");
|
|
4110
4799
|
const funcByName = /* @__PURE__ */ new Map();
|
|
4111
4800
|
for (const n of funcs) if (typeof n[1] === "string") funcByName.set(n[1], n);
|
|
@@ -4118,12 +4807,12 @@ var inlineOnce = (ast) => {
|
|
|
4118
4807
|
for (let i = 1; i < n.length; i++) countRefs(n[i]);
|
|
4119
4808
|
};
|
|
4120
4809
|
countRefs(ast);
|
|
4121
|
-
const pinned =
|
|
4122
|
-
for (const n of ast) if (!Array.isArray(n) || n[0] !== "func") collectPinned(n, pinned);
|
|
4810
|
+
const pinned = inlBuildPinned(ast);
|
|
4123
4811
|
let calleeName = null;
|
|
4124
4812
|
for (const [name2, fn] of funcByName) {
|
|
4125
4813
|
if (pinned.has(name2) || otherRef.has(name2)) continue;
|
|
4126
4814
|
if (callRefs.get(name2) !== 1) continue;
|
|
4815
|
+
if (SIMD_PROTECTED.has(name2)) continue;
|
|
4127
4816
|
if (callsSelf(fn, name2)) continue;
|
|
4128
4817
|
let ok = true, nResult = 0;
|
|
4129
4818
|
for (let i = 2; i < fn.length; i++) {
|
|
@@ -4179,11 +4868,10 @@ var inlineOnce = (ast) => {
|
|
|
4179
4868
|
const rename = /* @__PURE__ */ new Map();
|
|
4180
4869
|
for (const p of params) rename.set(p.name, `$__inl${uid2}_${p.name.slice(1)}`);
|
|
4181
4870
|
for (const l of locals) rename.set(l.name, `$__inl${uid2}_${l.name.slice(1)}`);
|
|
4182
|
-
const isBlockLabel = (op) => op === "block" || op === "loop" || op === "if";
|
|
4183
4871
|
const labelRename = /* @__PURE__ */ new Map();
|
|
4184
4872
|
const collectLabels = (n) => {
|
|
4185
4873
|
if (!Array.isArray(n)) return;
|
|
4186
|
-
if (
|
|
4874
|
+
if (isBranchScope(n[0]) && typeof n[1] === "string" && n[1][0] === "$" && !labelRename.has(n[1]))
|
|
4187
4875
|
labelRename.set(n[1], `$__inl${uid2}L_${n[1].slice(1)}`);
|
|
4188
4876
|
for (let i = 1; i < n.length; i++) collectLabels(n[i]);
|
|
4189
4877
|
};
|
|
@@ -4194,7 +4882,7 @@ var inlineOnce = (ast) => {
|
|
|
4194
4882
|
if ((op === "local.get" || op === "local.set" || op === "local.tee") && typeof n[1] === "string" && rename.has(n[1]))
|
|
4195
4883
|
return [op, rename.get(n[1]), ...n.slice(2).map(sub)];
|
|
4196
4884
|
if (op === "return") return ["br", exit, ...n.slice(1).map(sub)];
|
|
4197
|
-
if (
|
|
4885
|
+
if (isBranchScope(op) && typeof n[1] === "string" && labelRename.has(n[1]))
|
|
4198
4886
|
return [op, labelRename.get(n[1]), ...n.slice(2).map(sub)];
|
|
4199
4887
|
if (isBranch(op)) return [op, ...n.slice(1).map((c) => typeof c === "string" && labelRename.has(c) ? labelRename.get(c) : sub(c))];
|
|
4200
4888
|
return n.map((c, i) => i === 0 ? c : sub(c));
|
|
@@ -4204,7 +4892,7 @@ var inlineOnce = (ast) => {
|
|
|
4204
4892
|
if (fn === callee || done) continue;
|
|
4205
4893
|
const start = bodyStart(fn);
|
|
4206
4894
|
for (let i = start; i < fn.length; i++) {
|
|
4207
|
-
const replaced =
|
|
4895
|
+
const replaced = walkPost2(fn[i], (n) => {
|
|
4208
4896
|
if (done || !Array.isArray(n) || n[0] !== "call" || n[1] !== calleeName) return;
|
|
4209
4897
|
const args = n.slice(2);
|
|
4210
4898
|
if (args.length !== params.length) return;
|
|
@@ -4269,7 +4957,7 @@ var targetsLabel = (body, label) => {
|
|
|
4269
4957
|
return found;
|
|
4270
4958
|
};
|
|
4271
4959
|
var mergeBlocks = (ast) => {
|
|
4272
|
-
|
|
4960
|
+
walkPost2(ast, (node) => {
|
|
4273
4961
|
if (!Array.isArray(node) || node[0] !== "block") return;
|
|
4274
4962
|
let bi = 1, label = null;
|
|
4275
4963
|
if (typeof node[1] === "string" && node[1][0] === "$") {
|
|
@@ -4298,7 +4986,7 @@ var mergeBlocks = (ast) => {
|
|
|
4298
4986
|
node.length = 0;
|
|
4299
4987
|
for (const tok of only) node.push(tok);
|
|
4300
4988
|
});
|
|
4301
|
-
|
|
4989
|
+
walk2(ast, (node) => {
|
|
4302
4990
|
if (!isScopeNode(node)) return;
|
|
4303
4991
|
let i = 1;
|
|
4304
4992
|
while (i < node.length) {
|
|
@@ -4372,7 +5060,7 @@ var mergeBlocks = (ast) => {
|
|
|
4372
5060
|
return ast;
|
|
4373
5061
|
};
|
|
4374
5062
|
var coalesceLocals = (ast) => {
|
|
4375
|
-
|
|
5063
|
+
walk2(ast, (funcNode) => {
|
|
4376
5064
|
if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
|
|
4377
5065
|
const decls = /* @__PURE__ */ new Map();
|
|
4378
5066
|
for (const sub of funcNode) {
|
|
@@ -4444,7 +5132,7 @@ var coalesceLocals = (ast) => {
|
|
|
4444
5132
|
} else slots.push({ primary: name2, type, end: range.end });
|
|
4445
5133
|
}
|
|
4446
5134
|
if (rename.size === 0) return;
|
|
4447
|
-
|
|
5135
|
+
walk2(funcNode, (n) => {
|
|
4448
5136
|
if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.set" || n[0] === "local.tee") && rename.has(n[1])) {
|
|
4449
5137
|
n[1] = rename.get(n[1]);
|
|
4450
5138
|
}
|
|
@@ -4453,14 +5141,17 @@ var coalesceLocals = (ast) => {
|
|
|
4453
5141
|
return ast;
|
|
4454
5142
|
};
|
|
4455
5143
|
var vacuum = (ast) => {
|
|
4456
|
-
return
|
|
5144
|
+
return walkPost2(ast, (node) => {
|
|
4457
5145
|
if (!Array.isArray(node)) return;
|
|
4458
5146
|
const op = node[0];
|
|
4459
5147
|
if (op === "nop") return ["nop"];
|
|
4460
|
-
if (op === "drop" && node.length === 2
|
|
4461
|
-
|
|
5148
|
+
if (op === "drop" && node.length === 2) {
|
|
5149
|
+
const eff = dropEffects(node[1]);
|
|
5150
|
+
if (eff.length === 0) return ["nop"];
|
|
5151
|
+
if (eff.length === 1) return eff[0];
|
|
5152
|
+
return ["block", ...eff];
|
|
4462
5153
|
}
|
|
4463
|
-
if (op === "select" && node.length >= 4 && equal(node[1], node[2])) return node[1];
|
|
5154
|
+
if (op === "select" && node.length >= 4 && equal(node[1], node[2]) && isPure(node[3])) return node[1];
|
|
4464
5155
|
if (op === "if") {
|
|
4465
5156
|
const { cond, thenBranch, elseBranch } = parseIf(node);
|
|
4466
5157
|
const thenEmpty = !thenBranch || thenBranch.length <= 1;
|
|
@@ -4470,14 +5161,19 @@ var vacuum = (ast) => {
|
|
|
4470
5161
|
return node.filter((c) => c !== elseBranch);
|
|
4471
5162
|
}
|
|
4472
5163
|
}
|
|
4473
|
-
if (
|
|
5164
|
+
if (isScopeNode(node)) {
|
|
4474
5165
|
const cleaned = [op];
|
|
4475
5166
|
for (let i = 1; i < node.length; i++) {
|
|
4476
5167
|
const child = node[i];
|
|
4477
5168
|
if (child === "nop" || Array.isArray(child) && child[0] === "nop") continue;
|
|
4478
5169
|
const next = node[i + 1];
|
|
4479
5170
|
const isDrop = next === "drop" || Array.isArray(next) && next[0] === "drop" && next.length === 1;
|
|
4480
|
-
if (Array.isArray(child) && isPure(child)
|
|
5171
|
+
if (Array.isArray(child) && isDrop && isPure(child)) {
|
|
5172
|
+
i++;
|
|
5173
|
+
continue;
|
|
5174
|
+
}
|
|
5175
|
+
if (Array.isArray(child) && isDrop && child[0] === "local.tee" && child.length === 3) {
|
|
5176
|
+
cleaned.push(["local.set", child[1], child[2]]);
|
|
4481
5177
|
i++;
|
|
4482
5178
|
continue;
|
|
4483
5179
|
}
|
|
@@ -4487,72 +5183,73 @@ var vacuum = (ast) => {
|
|
|
4487
5183
|
}
|
|
4488
5184
|
});
|
|
4489
5185
|
};
|
|
5186
|
+
var selfFold = (val) => (a, b) => equal(a, b) && isPure(a) ? val : null;
|
|
4490
5187
|
var PEEPHOLE = {
|
|
4491
|
-
// Self-cancelling / tautological binary ops
|
|
4492
|
-
"i32.sub": (
|
|
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
|
|
5188
|
+
// Self-cancelling / tautological binary ops — drop both (equal) operands.
|
|
5189
|
+
"i32.sub": selfFold(["i32.const", 0]),
|
|
5190
|
+
"i64.sub": selfFold(["i64.const", 0]),
|
|
5191
|
+
"i32.xor": selfFold(["i32.const", 0]),
|
|
5192
|
+
"i64.xor": selfFold(["i64.const", 0]),
|
|
5193
|
+
"i32.eq": selfFold(["i32.const", 1]),
|
|
5194
|
+
"i64.eq": selfFold(["i32.const", 1]),
|
|
5195
|
+
"i32.ne": selfFold(["i32.const", 0]),
|
|
5196
|
+
"i64.ne": selfFold(["i32.const", 0]),
|
|
5197
|
+
"i32.lt_s": selfFold(["i32.const", 0]),
|
|
5198
|
+
"i32.lt_u": selfFold(["i32.const", 0]),
|
|
5199
|
+
"i32.gt_s": selfFold(["i32.const", 0]),
|
|
5200
|
+
"i32.gt_u": selfFold(["i32.const", 0]),
|
|
5201
|
+
"i32.le_s": selfFold(["i32.const", 1]),
|
|
5202
|
+
"i32.le_u": selfFold(["i32.const", 1]),
|
|
5203
|
+
"i32.ge_s": selfFold(["i32.const", 1]),
|
|
5204
|
+
"i32.ge_u": selfFold(["i32.const", 1]),
|
|
5205
|
+
"i64.lt_s": selfFold(["i32.const", 0]),
|
|
5206
|
+
"i64.lt_u": selfFold(["i32.const", 0]),
|
|
5207
|
+
"i64.gt_s": selfFold(["i32.const", 0]),
|
|
5208
|
+
"i64.gt_u": selfFold(["i32.const", 0]),
|
|
5209
|
+
"i64.le_s": selfFold(["i32.const", 1]),
|
|
5210
|
+
"i64.le_u": selfFold(["i32.const", 1]),
|
|
5211
|
+
"i64.ge_s": selfFold(["i32.const", 1]),
|
|
5212
|
+
"i64.ge_u": selfFold(["i32.const", 1]),
|
|
5213
|
+
// Zero/all-bits absorption — drops the NON-const operand, so guard its purity.
|
|
4517
5214
|
"i32.mul": (a, b) => {
|
|
4518
|
-
|
|
4519
|
-
if (
|
|
5215
|
+
if (getConst(b)?.value === 0 && isPure(a)) return ["i32.const", 0];
|
|
5216
|
+
if (getConst(a)?.value === 0 && isPure(b)) return ["i32.const", 0];
|
|
4520
5217
|
return null;
|
|
4521
5218
|
},
|
|
4522
5219
|
"i64.mul": (a, b) => {
|
|
4523
|
-
|
|
4524
|
-
if (
|
|
5220
|
+
if (getConst(b)?.value === ZERO64 && isPure(a)) return ["i64.const", 0];
|
|
5221
|
+
if (getConst(a)?.value === ZERO64 && isPure(b)) return ["i64.const", 0];
|
|
4525
5222
|
return null;
|
|
4526
5223
|
},
|
|
4527
5224
|
"i32.and": (a, b) => {
|
|
4528
|
-
if (equal(a, b)) return a;
|
|
4529
|
-
|
|
4530
|
-
if (
|
|
5225
|
+
if (equal(a, b) && isPure(b)) return a;
|
|
5226
|
+
if (getConst(b)?.value === 0 && isPure(a)) return ["i32.const", 0];
|
|
5227
|
+
if (getConst(a)?.value === 0 && isPure(b)) return ["i32.const", 0];
|
|
4531
5228
|
return null;
|
|
4532
5229
|
},
|
|
4533
5230
|
"i64.and": (a, b) => {
|
|
4534
|
-
if (equal(a, b)) return a;
|
|
4535
|
-
|
|
4536
|
-
if (
|
|
5231
|
+
if (equal(a, b) && isPure(b)) return a;
|
|
5232
|
+
if (getConst(b)?.value === ZERO64 && isPure(a)) return ["i64.const", 0];
|
|
5233
|
+
if (getConst(a)?.value === ZERO64 && isPure(b)) return ["i64.const", 0];
|
|
4537
5234
|
return null;
|
|
4538
5235
|
},
|
|
4539
5236
|
"i32.or": (a, b) => {
|
|
4540
|
-
if (equal(a, b)) return a;
|
|
4541
|
-
|
|
4542
|
-
if (
|
|
5237
|
+
if (equal(a, b) && isPure(b)) return a;
|
|
5238
|
+
if (getConst(b)?.value === -1 && isPure(a)) return ["i32.const", -1];
|
|
5239
|
+
if (getConst(a)?.value === -1 && isPure(b)) return ["i32.const", -1];
|
|
4543
5240
|
return null;
|
|
4544
5241
|
},
|
|
4545
5242
|
"i64.or": (a, b) => {
|
|
4546
|
-
if (equal(a, b)) return a;
|
|
4547
|
-
|
|
4548
|
-
if (
|
|
5243
|
+
if (equal(a, b) && isPure(b)) return a;
|
|
5244
|
+
if (getConst(b)?.value === NEG164 && isPure(a)) return ["i64.const", -1];
|
|
5245
|
+
if (getConst(a)?.value === NEG164 && isPure(b)) return ["i64.const", -1];
|
|
4549
5246
|
return null;
|
|
4550
5247
|
},
|
|
4551
5248
|
// (local.set $x (local.get $x)) → nop
|
|
4552
5249
|
"local.set": (a, b) => Array.isArray(b) && b[0] === "local.get" && b[1] === a ? ["nop"] : null
|
|
4553
5250
|
};
|
|
4554
5251
|
var peephole = (ast) => {
|
|
4555
|
-
return
|
|
5252
|
+
return walkPost2(ast, (node) => {
|
|
4556
5253
|
if (!Array.isArray(node) || node.length !== 3) return;
|
|
4557
5254
|
const fn = PEEPHOLE[node[0]];
|
|
4558
5255
|
if (!fn) return;
|
|
@@ -4561,7 +5258,8 @@ var peephole = (ast) => {
|
|
|
4561
5258
|
});
|
|
4562
5259
|
};
|
|
4563
5260
|
var slebSize = (v) => {
|
|
4564
|
-
let x = typeof v === "bigint" ? v : BigInt(Math.trunc(Number(v) || 0));
|
|
5261
|
+
let x = typeof v === "bigint" ? v : typeof v === "string" ? BigInt(v.replaceAll("_", "")) : BigInt(Math.trunc(Number(v) || 0));
|
|
5262
|
+
if (x > 0x7fffffffffffffffn) x = x - 0x8000000000000000n - 0x8000000000000000n;
|
|
4565
5263
|
let n = 1;
|
|
4566
5264
|
while (true) {
|
|
4567
5265
|
const b = x & 0x7fn;
|
|
@@ -4609,7 +5307,7 @@ var globals = (ast) => {
|
|
|
4609
5307
|
}
|
|
4610
5308
|
if (constGlobals.size === 0) return ast;
|
|
4611
5309
|
const reads = /* @__PURE__ */ new Map();
|
|
4612
|
-
|
|
5310
|
+
walk2(ast, (n) => {
|
|
4613
5311
|
if (!Array.isArray(n)) return;
|
|
4614
5312
|
const ref = n[1];
|
|
4615
5313
|
if (typeof ref !== "string" || ref[0] !== "$") return;
|
|
@@ -4627,9 +5325,9 @@ var globals = (ast) => {
|
|
|
4627
5325
|
if (after <= before) propagate2.add(name2);
|
|
4628
5326
|
}
|
|
4629
5327
|
if (propagate2.size === 0) return ast;
|
|
4630
|
-
|
|
5328
|
+
walkPost2(ast, (node) => {
|
|
4631
5329
|
if (!Array.isArray(node) || node[0] !== "global.get" || node.length !== 2) return;
|
|
4632
|
-
if (propagate2.has(node[1])) return
|
|
5330
|
+
if (propagate2.has(node[1])) return clone2(constGlobals.get(node[1]));
|
|
4633
5331
|
});
|
|
4634
5332
|
for (let i = ast.length - 1; i >= 1; i--) {
|
|
4635
5333
|
const n = ast[i];
|
|
@@ -4638,7 +5336,7 @@ var globals = (ast) => {
|
|
|
4638
5336
|
return ast;
|
|
4639
5337
|
};
|
|
4640
5338
|
var offset = (ast) => {
|
|
4641
|
-
return
|
|
5339
|
+
return walkPost2(ast, (node) => {
|
|
4642
5340
|
if (!Array.isArray(node)) return;
|
|
4643
5341
|
const op = node[0];
|
|
4644
5342
|
if (typeof op !== "string" || !op.endsWith("load") && !op.endsWith("store")) return;
|
|
@@ -4689,7 +5387,7 @@ var offset = (ast) => {
|
|
|
4689
5387
|
});
|
|
4690
5388
|
};
|
|
4691
5389
|
var unbranch = (ast) => {
|
|
4692
|
-
|
|
5390
|
+
walk2(ast, (node) => {
|
|
4693
5391
|
if (!Array.isArray(node)) return;
|
|
4694
5392
|
const op = node[0];
|
|
4695
5393
|
if (op !== "block") return;
|
|
@@ -4721,7 +5419,7 @@ var unbranch = (ast) => {
|
|
|
4721
5419
|
return ast;
|
|
4722
5420
|
};
|
|
4723
5421
|
var loopify = (ast) => {
|
|
4724
|
-
|
|
5422
|
+
walk2(ast, (node) => {
|
|
4725
5423
|
if (!Array.isArray(node) || node[0] !== "block") return;
|
|
4726
5424
|
let bi = 1, label = null;
|
|
4727
5425
|
if (typeof node[1] === "string" && node[1][0] === "$") {
|
|
@@ -4780,10 +5478,10 @@ var loopify = (ast) => {
|
|
|
4780
5478
|
var stripmut = (ast) => {
|
|
4781
5479
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4782
5480
|
const written = /* @__PURE__ */ new Set();
|
|
4783
|
-
|
|
5481
|
+
walk2(ast, (n) => {
|
|
4784
5482
|
if (Array.isArray(n) && n[0] === "global.set" && typeof n[1] === "string") written.add(n[1]);
|
|
4785
5483
|
});
|
|
4786
|
-
return
|
|
5484
|
+
return walkPost2(ast, (node) => {
|
|
4787
5485
|
if (!Array.isArray(node) || node[0] !== "global") return;
|
|
4788
5486
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
4789
5487
|
if (!name2 || written.has(name2)) return;
|
|
@@ -4797,7 +5495,7 @@ var stripmut = (ast) => {
|
|
|
4797
5495
|
});
|
|
4798
5496
|
};
|
|
4799
5497
|
var brif = (ast) => {
|
|
4800
|
-
return
|
|
5498
|
+
return walkPost2(ast, (node) => {
|
|
4801
5499
|
if (!Array.isArray(node) || node[0] !== "if") return;
|
|
4802
5500
|
const { cond, thenBranch, elseBranch } = parseIf(node);
|
|
4803
5501
|
const thenEmpty = !thenBranch || thenBranch.length <= 1;
|
|
@@ -4813,7 +5511,7 @@ var brif = (ast) => {
|
|
|
4813
5511
|
});
|
|
4814
5512
|
};
|
|
4815
5513
|
var foldarms = (ast) => {
|
|
4816
|
-
return
|
|
5514
|
+
return walkPost2(ast, (node) => {
|
|
4817
5515
|
if (!Array.isArray(node) || node[0] !== "if") return;
|
|
4818
5516
|
const { thenBranch, elseBranch } = parseIf(node);
|
|
4819
5517
|
if (!thenBranch || !elseBranch) return;
|
|
@@ -4886,10 +5584,10 @@ var dedupe = (ast) => {
|
|
|
4886
5584
|
if (!name2) continue;
|
|
4887
5585
|
const localNames = /* @__PURE__ */ new Set();
|
|
4888
5586
|
if (typeof node[1] === "string" && node[1][0] === "$") localNames.add(node[1]);
|
|
4889
|
-
|
|
5587
|
+
walk2(node, (n) => {
|
|
4890
5588
|
if (!Array.isArray(n) || typeof n[1] !== "string" || n[1][0] !== "$") return;
|
|
4891
5589
|
const op = n[0];
|
|
4892
|
-
if (op === "param" || op === "local" || op
|
|
5590
|
+
if (op === "param" || op === "local" || isBranchScope(op)) {
|
|
4893
5591
|
localNames.add(n[1]);
|
|
4894
5592
|
}
|
|
4895
5593
|
});
|
|
@@ -4901,7 +5599,7 @@ var dedupe = (ast) => {
|
|
|
4901
5599
|
}
|
|
4902
5600
|
}
|
|
4903
5601
|
if (redirects.size === 0) return ast;
|
|
4904
|
-
|
|
5602
|
+
walkPost2(ast, (node) => {
|
|
4905
5603
|
if (!Array.isArray(node)) return;
|
|
4906
5604
|
const op = node[0];
|
|
4907
5605
|
if ((op === "call" || op === "return_call") && redirects.has(node[1])) {
|
|
@@ -4948,7 +5646,7 @@ var dedupTypes = (ast) => {
|
|
|
4948
5646
|
if (name2 && redirects.has(name2)) ast.splice(i, 1);
|
|
4949
5647
|
}
|
|
4950
5648
|
}
|
|
4951
|
-
|
|
5649
|
+
walkPost2(ast, (node) => {
|
|
4952
5650
|
if (!Array.isArray(node)) return;
|
|
4953
5651
|
const op = node[0];
|
|
4954
5652
|
if (op === "func") {
|
|
@@ -4984,39 +5682,39 @@ var dedupTypes = (ast) => {
|
|
|
4984
5682
|
return ast;
|
|
4985
5683
|
};
|
|
4986
5684
|
var parseDataString = (str2) => {
|
|
4987
|
-
if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return
|
|
4988
|
-
const inner = str2.slice(1, -1);
|
|
5685
|
+
if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return [];
|
|
4989
5686
|
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));
|
|
5687
|
+
const hexv = (c) => c >= 48 && c <= 57 ? c - 48 : c >= 97 && c <= 102 ? c - 87 : c >= 65 && c <= 70 ? c - 55 : -1;
|
|
5688
|
+
const end = str2.length - 1;
|
|
5689
|
+
for (let i = 1; i < end; i++) {
|
|
5690
|
+
const c = str2.charCodeAt(i);
|
|
5691
|
+
if (c !== 92) {
|
|
5692
|
+
bytes.push(c);
|
|
5693
|
+
continue;
|
|
5694
|
+
}
|
|
5695
|
+
const n = str2.charCodeAt(++i);
|
|
5696
|
+
if (n === 120 || n === 88) {
|
|
5697
|
+
bytes.push(hexv(str2.charCodeAt(i + 1)) << 4 | hexv(str2.charCodeAt(i + 2)));
|
|
5698
|
+
i += 2;
|
|
5005
5699
|
} else {
|
|
5006
|
-
|
|
5700
|
+
const h1 = hexv(n), h2 = i + 1 < end ? hexv(str2.charCodeAt(i + 1)) : -1;
|
|
5701
|
+
if (h1 >= 0 && h2 >= 0) {
|
|
5702
|
+
bytes.push(h1 << 4 | h2);
|
|
5703
|
+
i++;
|
|
5704
|
+
} else if (n === 110) bytes.push(10);
|
|
5705
|
+
else if (n === 116) bytes.push(9);
|
|
5706
|
+
else if (n === 114) bytes.push(13);
|
|
5707
|
+
else bytes.push(n);
|
|
5007
5708
|
}
|
|
5008
5709
|
}
|
|
5009
|
-
return
|
|
5710
|
+
return bytes;
|
|
5010
5711
|
};
|
|
5011
|
-
var encodeDataString = (bytes) => {
|
|
5712
|
+
var encodeDataString = (bytes, end) => {
|
|
5012
5713
|
let str2 = '"';
|
|
5013
|
-
for (let i = 0; i <
|
|
5714
|
+
for (let i = 0; i < end; i++) {
|
|
5014
5715
|
const b = bytes[i];
|
|
5015
|
-
if (b >= 32 && b < 127 && b !== 34 && b !== 92)
|
|
5016
|
-
|
|
5017
|
-
} else {
|
|
5018
|
-
str2 += "\\" + b.toString(16).padStart(2, "0");
|
|
5019
|
-
}
|
|
5716
|
+
if (b >= 32 && b < 127 && b !== 34 && b !== 92) str2 += String.fromCharCode(b);
|
|
5717
|
+
else str2 += "\\" + b.toString(16).padStart(2, "0");
|
|
5020
5718
|
}
|
|
5021
5719
|
return str2 + '"';
|
|
5022
5720
|
};
|
|
@@ -5024,7 +5722,8 @@ var trimTrailingZeros = (items) => {
|
|
|
5024
5722
|
const bytes = [];
|
|
5025
5723
|
for (const item of items) {
|
|
5026
5724
|
if (typeof item === "string") {
|
|
5027
|
-
|
|
5725
|
+
const chunk = parseDataString(item);
|
|
5726
|
+
for (let i = 0; i < chunk.length; i++) bytes.push(chunk[i]);
|
|
5028
5727
|
} else if (Array.isArray(item) && item[0] === "i8") {
|
|
5029
5728
|
for (let i = 1; i < item.length; i++) bytes.push(Number(item[i]) & 255);
|
|
5030
5729
|
} else {
|
|
@@ -5035,7 +5734,7 @@ var trimTrailingZeros = (items) => {
|
|
|
5035
5734
|
while (end > 0 && bytes[end - 1] === 0) end--;
|
|
5036
5735
|
if (end === bytes.length) return items;
|
|
5037
5736
|
if (end === 0) return [];
|
|
5038
|
-
return [encodeDataString(
|
|
5737
|
+
return [encodeDataString(bytes, end)];
|
|
5039
5738
|
};
|
|
5040
5739
|
var getDataOffset = (node) => {
|
|
5041
5740
|
let idx = 1;
|
|
@@ -5083,11 +5782,9 @@ var mergeDataSegments = (a, b) => {
|
|
|
5083
5782
|
if (aContent.length === 1 && bContent.length === 1 && typeof aContent[0] === "string" && typeof bContent[0] === "string") {
|
|
5084
5783
|
const aBytes = parseDataString(aContent[0]);
|
|
5085
5784
|
const bBytes = parseDataString(bContent[0]);
|
|
5086
|
-
|
|
5087
|
-
merged.set(aBytes);
|
|
5088
|
-
merged.set(bBytes, aBytes.length);
|
|
5785
|
+
for (let i = 0; i < bBytes.length; i++) aBytes.push(bBytes[i]);
|
|
5089
5786
|
a.length = aIdx;
|
|
5090
|
-
a.push(encodeDataString(
|
|
5787
|
+
a.push(encodeDataString(aBytes, aBytes.length));
|
|
5091
5788
|
return true;
|
|
5092
5789
|
}
|
|
5093
5790
|
a.length = aIdx;
|
|
@@ -5175,7 +5872,7 @@ var minifyImports = (ast) => {
|
|
|
5175
5872
|
};
|
|
5176
5873
|
var reorderSafe = (ast) => {
|
|
5177
5874
|
let safe = true;
|
|
5178
|
-
|
|
5875
|
+
walk2(ast, (n) => {
|
|
5179
5876
|
if (!safe || !Array.isArray(n)) return;
|
|
5180
5877
|
const op = n[0];
|
|
5181
5878
|
if (op === "func" && (typeof n[1] !== "string" || n[1][0] !== "$")) safe = false;
|
|
@@ -5200,7 +5897,7 @@ var reorder = (ast) => {
|
|
|
5200
5897
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
5201
5898
|
if (!reorderSafe(ast)) return ast;
|
|
5202
5899
|
const callCounts = /* @__PURE__ */ new Map();
|
|
5203
|
-
|
|
5900
|
+
walk2(ast, (n) => {
|
|
5204
5901
|
if (!Array.isArray(n)) return;
|
|
5205
5902
|
if (n[0] === "call" || n[0] === "return_call") {
|
|
5206
5903
|
callCounts.set(n[1], (callCounts.get(n[1]) || 0) + 1);
|
|
@@ -5222,12 +5919,14 @@ var reorder = (ast) => {
|
|
|
5222
5919
|
var PASSES = [
|
|
5223
5920
|
["stripmut", stripmut, true, "strip mut from never-written globals"],
|
|
5224
5921
|
["globals", globals, true, "propagate immutable global constants"],
|
|
5922
|
+
["guardRefine", guardRefine, false, "fold NaN-box tag reads under dominating tag guards (jz NaN-box-specific; opt-in)"],
|
|
5225
5923
|
["fold", fold, true, "constant folding"],
|
|
5226
5924
|
["identity", identity, true, "remove identity ops (x + 0 \u2192 x)"],
|
|
5227
5925
|
["peephole", peephole, true, "x-x\u21920, x&0\u21920, etc."],
|
|
5228
5926
|
["strength", strength, true, "strength reduction (x * 2 \u2192 x << 1)"],
|
|
5229
5927
|
["branch", branch, true, "simplify constant branches"],
|
|
5230
5928
|
["propagate", propagate, true, "forward-propagate single-use locals & tiny consts (never inflates)"],
|
|
5929
|
+
["devirt", devirt, false, "call_indirect with a constant or known closure-const index \u2192 direct/guarded calls \u2014 grows bytes for speed"],
|
|
5231
5930
|
["inlineOnce", inlineOnce, true, "inline single-call functions into their lone caller (never duplicates)"],
|
|
5232
5931
|
["inline", inline, false, "inline tiny functions \u2014 can duplicate bodies"],
|
|
5233
5932
|
["offset", offset, true, "fold add+const into load/store offset"],
|
|
@@ -5260,6 +5959,42 @@ var normalize3 = (opts) => {
|
|
|
5260
5959
|
for (const p of PASSES) m[p[0]] = set ? set.has("all") || set.has(p[0]) : p[2];
|
|
5261
5960
|
return m;
|
|
5262
5961
|
};
|
|
5962
|
+
var mayInline = (ast) => {
|
|
5963
|
+
if (!Array.isArray(ast)) return false;
|
|
5964
|
+
const callRefs = /* @__PURE__ */ new Map(), pinned = /* @__PURE__ */ new Set(), other = /* @__PURE__ */ new Set();
|
|
5965
|
+
const scan = (n) => {
|
|
5966
|
+
if (!Array.isArray(n)) return;
|
|
5967
|
+
const op = n[0];
|
|
5968
|
+
if (op === "call" && typeof n[1] === "string") callRefs.set(n[1], (callRefs.get(n[1]) || 0) + 1);
|
|
5969
|
+
else if (op === "return_call" && typeof n[1] === "string") other.add(n[1]);
|
|
5970
|
+
else if (op === "ref.func" && typeof n[1] === "string") pinned.add(n[1]);
|
|
5971
|
+
else if (op === "export" && Array.isArray(n[2]) && n[2][0] === "func" && typeof n[2][1] === "string") pinned.add(n[2][1]);
|
|
5972
|
+
else if (op === "start" && typeof n[1] === "string") pinned.add(n[1]);
|
|
5973
|
+
else if (op === "elem") {
|
|
5974
|
+
for (const c of n) if (typeof c === "string" && c[0] === "$") pinned.add(c);
|
|
5975
|
+
}
|
|
5976
|
+
for (let i = 1; i < n.length; i++) scan(n[i]);
|
|
5977
|
+
};
|
|
5978
|
+
scan(ast);
|
|
5979
|
+
for (const n of ast) {
|
|
5980
|
+
if (!Array.isArray(n) || n[0] !== "func" || typeof n[1] !== "string") continue;
|
|
5981
|
+
const name2 = n[1];
|
|
5982
|
+
if (callRefs.get(name2) !== 1 || pinned.has(name2) || other.has(name2)) continue;
|
|
5983
|
+
if (n.some((c) => Array.isArray(c) && c[0] === "export")) continue;
|
|
5984
|
+
return true;
|
|
5985
|
+
}
|
|
5986
|
+
return false;
|
|
5987
|
+
};
|
|
5988
|
+
var START_HEAD = /* @__PURE__ */ new Set(["export", "type", "param", "result", "local"]);
|
|
5989
|
+
function pruneEmptyStart(ast) {
|
|
5990
|
+
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
5991
|
+
const fn = ast.find((n) => Array.isArray(n) && n[0] === "func" && n[1] === "$__start");
|
|
5992
|
+
if (!fn) return ast;
|
|
5993
|
+
let b = 2;
|
|
5994
|
+
while (b < fn.length && Array.isArray(fn[b]) && START_HEAD.has(fn[b][0])) b++;
|
|
5995
|
+
if (b < fn.length) return ast;
|
|
5996
|
+
return ast.filter((n) => !(Array.isArray(n) && (n[0] === "func" && n[1] === "$__start" || n[0] === "start" && n[1] === "$__start")));
|
|
5997
|
+
}
|
|
5263
5998
|
function optimize(ast, opts = true) {
|
|
5264
5999
|
if (typeof ast === "string") ast = parse_default(ast);
|
|
5265
6000
|
const strictGuard = opts === true;
|
|
@@ -5267,27 +6002,48 @@ function optimize(ast, opts = true) {
|
|
|
5267
6002
|
const log = opts.log ? (msg, delta) => opts.log(msg, delta) : () => {
|
|
5268
6003
|
};
|
|
5269
6004
|
const verbose = opts.verbose || opts.log;
|
|
5270
|
-
ast =
|
|
6005
|
+
ast = clone2(ast);
|
|
6006
|
+
const runInline = (a) => {
|
|
6007
|
+
if (!opts.inline) return a;
|
|
6008
|
+
a = inline(a, { simdOnly: opts.inline === "simd" });
|
|
6009
|
+
if (opts.propagate) a = propagate(a);
|
|
6010
|
+
if (opts.mergeBlocks) a = mergeBlocks(a);
|
|
6011
|
+
if (opts.vacuum) a = vacuum(a);
|
|
6012
|
+
if (opts.coalesceLocals) a = coalesceLocals(a);
|
|
6013
|
+
return a;
|
|
6014
|
+
};
|
|
6015
|
+
const finish = (a) => {
|
|
6016
|
+
a = runInline(a);
|
|
6017
|
+
return pruneEmptyStart(opts.devirt ? devirt(a) : a);
|
|
6018
|
+
};
|
|
6019
|
+
if (!((opts.inlineOnce || opts.inline) && mayInline(ast))) {
|
|
6020
|
+
for (let round = 0; round < 3; round++) {
|
|
6021
|
+
const beforeRound2 = clone2(ast);
|
|
6022
|
+
for (const [key, fn] of PASSES) if (opts[key] && key !== "inlineOnce" && key !== "inline" && key !== "devirt") ast = fn(ast);
|
|
6023
|
+
if (equal(beforeRound2, ast)) break;
|
|
6024
|
+
if (verbose) log(` round ${round + 1} applied`);
|
|
6025
|
+
}
|
|
6026
|
+
return finish(ast);
|
|
6027
|
+
}
|
|
5271
6028
|
let beforeRound = null;
|
|
6029
|
+
let sizeBefore = binarySize(ast);
|
|
5272
6030
|
for (let round = 0; round < 3; round++) {
|
|
5273
|
-
beforeRound =
|
|
5274
|
-
const
|
|
5275
|
-
for (const [key, fn] of PASSES) if (opts[key]) ast = fn(ast);
|
|
6031
|
+
beforeRound = clone2(ast);
|
|
6032
|
+
for (const [key, fn] of PASSES) if (opts[key] && key !== "devirt" && key !== "inline") ast = fn(ast);
|
|
5276
6033
|
if (opts.propagate && (opts.inlineOnce || opts.inline)) ast = propagate(ast);
|
|
6034
|
+
if (equal(beforeRound, ast)) break;
|
|
5277
6035
|
const sizeAfter = binarySize(ast);
|
|
5278
6036
|
const delta = sizeAfter - sizeBefore;
|
|
5279
|
-
if (verbose || delta !== 0) {
|
|
5280
|
-
log(` round ${round + 1}: ${delta > 0 ? "+" : ""}${delta} bytes`, delta);
|
|
5281
|
-
}
|
|
6037
|
+
if (verbose || delta !== 0) log(` round ${round + 1}: ${delta > 0 ? "+" : ""}${delta} bytes`, delta);
|
|
5282
6038
|
const tolerance = strictGuard ? 0 : 16;
|
|
5283
6039
|
if (delta > tolerance) {
|
|
5284
6040
|
if (verbose) log(` \u26A0 round ${round + 1} inflated by ${delta} bytes, reverting`, delta);
|
|
5285
6041
|
ast = beforeRound;
|
|
5286
6042
|
break;
|
|
5287
6043
|
}
|
|
5288
|
-
|
|
6044
|
+
sizeBefore = sizeAfter;
|
|
5289
6045
|
}
|
|
5290
|
-
return ast;
|
|
6046
|
+
return finish(ast);
|
|
5291
6047
|
}
|
|
5292
6048
|
|
|
5293
6049
|
// src/template.js
|
|
@@ -5304,11 +6060,11 @@ var exprType = (node, ctx = {}) => {
|
|
|
5304
6060
|
if (op === "call" && ctx.funcs?.[args[0]]) return ctx.funcs[args[0]].result?.[0];
|
|
5305
6061
|
return null;
|
|
5306
6062
|
};
|
|
5307
|
-
function
|
|
6063
|
+
function walk3(node, fn) {
|
|
5308
6064
|
node = fn(node);
|
|
5309
6065
|
if (Array.isArray(node)) {
|
|
5310
6066
|
for (let i = 0; i < node.length; i++) {
|
|
5311
|
-
let child =
|
|
6067
|
+
let child = walk3(node[i], fn);
|
|
5312
6068
|
if (child?._splice) node.splice(i, 1, ...child), i += child.length - 1;
|
|
5313
6069
|
else node[i] = child;
|
|
5314
6070
|
}
|
|
@@ -5318,7 +6074,7 @@ function walk2(node, fn) {
|
|
|
5318
6074
|
function inferImports(ast, funcs) {
|
|
5319
6075
|
const imports = [];
|
|
5320
6076
|
const importMap = /* @__PURE__ */ new Map();
|
|
5321
|
-
|
|
6077
|
+
walk3(ast, (node) => {
|
|
5322
6078
|
if (!Array.isArray(node)) return node;
|
|
5323
6079
|
if (node[0] === "call" && typeof node[1] === "function") {
|
|
5324
6080
|
const fn = node[1];
|
|
@@ -5359,7 +6115,7 @@ function compile2(backend2, source, values) {
|
|
|
5359
6115
|
let ast = parse(src);
|
|
5360
6116
|
const funcsToImport = [];
|
|
5361
6117
|
let idx = 0;
|
|
5362
|
-
ast =
|
|
6118
|
+
ast = walk3(ast, (node) => {
|
|
5363
6119
|
if (node === PUA) {
|
|
5364
6120
|
const value = values[idx++];
|
|
5365
6121
|
if (typeof value === "function") {
|