watr 4.7.2 → 5.0.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 +54 -3876
- package/dist/watr.min.js +6 -6
- package/dist/watr.wasm +0 -0
- package/package.json +11 -1
- package/readme.md +9 -6
- package/src/template.js +23 -5
- package/types/src/template.d.ts.map +1 -1
- package/types/watr.d.ts +4 -5
- package/types/watr.d.ts.map +1 -1
- package/watr.js +10 -7
package/dist/watr.js
CHANGED
|
@@ -56,17 +56,6 @@ var str = (s) => {
|
|
|
56
56
|
return bytes;
|
|
57
57
|
};
|
|
58
58
|
var unescape = (s) => tdec.decode(new Uint8Array(str(s)));
|
|
59
|
-
var clone = (node) => Array.isArray(node) ? node.map(clone) : node;
|
|
60
|
-
var walk = (node, fn, parent, idx) => {
|
|
61
|
-
fn(node, parent, idx);
|
|
62
|
-
if (Array.isArray(node)) for (let i = 0; i < node.length; i++) walk(node[i], fn, node, i);
|
|
63
|
-
};
|
|
64
|
-
var walkPost = (node, fn, parent, idx) => {
|
|
65
|
-
if (Array.isArray(node)) for (let i = 0; i < node.length; i++) walkPost(node[i], fn, node, i);
|
|
66
|
-
const result = fn(node, parent, idx);
|
|
67
|
-
if (result !== void 0 && parent) parent[idx] = result;
|
|
68
|
-
return result !== void 0 ? result : node;
|
|
69
|
-
};
|
|
70
59
|
|
|
71
60
|
// src/encode.js
|
|
72
61
|
var uleb = (n, buffer = []) => {
|
|
@@ -1598,9 +1587,6 @@ function assemble(nodes, sizeOnly) {
|
|
|
1598
1587
|
function compile(nodes) {
|
|
1599
1588
|
return assemble(nodes);
|
|
1600
1589
|
}
|
|
1601
|
-
function size(nodes) {
|
|
1602
|
-
return assemble(nodes, true);
|
|
1603
|
-
}
|
|
1604
1590
|
var isIdx = (n) => n?.[0] === "$" || !isNaN(n);
|
|
1605
1591
|
var isId = (n) => n?.[0] === "$";
|
|
1606
1592
|
var isMemParam = (n) => n?.[0] === "a" || n?.[0] === "o";
|
|
@@ -1810,7 +1796,7 @@ var build = [
|
|
|
1810
1796
|
// (elem (table idx)? (offset expr)|(expr) elem*) - active
|
|
1811
1797
|
// ref: https://webassembly.github.io/spec/core/binary/modules.html#element-section
|
|
1812
1798
|
(parts, ctx) => {
|
|
1813
|
-
let passive = 0, declare = 0, elexpr = 0, nofunc = 0, tabidx,
|
|
1799
|
+
let passive = 0, declare = 0, elexpr = 0, nofunc = 0, tabidx, offset, rt;
|
|
1814
1800
|
if (parts[0] === "declare") parts.shift(), declare = 1;
|
|
1815
1801
|
if (parts[0]?.[0] === "table") {
|
|
1816
1802
|
[, tabidx] = parts.shift();
|
|
@@ -1819,9 +1805,9 @@ var build = [
|
|
|
1819
1805
|
tabidx = id(parts.shift(), ctx.table);
|
|
1820
1806
|
}
|
|
1821
1807
|
if (parts[0]?.[0] === "offset" || Array.isArray(parts[0]) && parts[0][0] !== "item" && !parts[0][0].startsWith("ref")) {
|
|
1822
|
-
|
|
1823
|
-
if (
|
|
1824
|
-
|
|
1808
|
+
offset = parts.shift();
|
|
1809
|
+
if (offset[0] === "offset") [, offset] = offset;
|
|
1810
|
+
offset = expr(offset, ctx);
|
|
1825
1811
|
} else if (!declare) passive = 1;
|
|
1826
1812
|
if (TYPE[parts[0]] || parts[0]?.[0] === "ref") rt = reftype(parts.shift(), ctx);
|
|
1827
1813
|
else if (parts[0] === "func") rt = [TYPE[parts.shift()]];
|
|
@@ -1837,19 +1823,19 @@ var build = [
|
|
|
1837
1823
|
return [
|
|
1838
1824
|
mode,
|
|
1839
1825
|
...// 0b000 e:expr y*:vec(funcidx) | type=(ref func), init ((ref.func y)end)*, active (table=0,offset=e)
|
|
1840
|
-
mode === 0 ?
|
|
1826
|
+
mode === 0 ? offset : (
|
|
1841
1827
|
// 0b001 et:elkind y*:vec(funcidx) | type=0x00, init ((ref.func y)end)*, passive
|
|
1842
1828
|
mode === 1 ? [0] : (
|
|
1843
1829
|
// 0b010 x:tabidx e:expr et:elkind y*:vec(funcidx) | type=0x00, init ((ref.func y)end)*, active (table=x,offset=e)
|
|
1844
|
-
mode === 2 ? [...uleb(tabidx || 0), ...
|
|
1830
|
+
mode === 2 ? [...uleb(tabidx || 0), ...offset, 0] : (
|
|
1845
1831
|
// 0b011 et:elkind y*:vec(funcidx) | type=0x00, init ((ref.func y)end)*, passive declare
|
|
1846
1832
|
mode === 3 ? [0] : (
|
|
1847
1833
|
// 0b100 e:expr el*:vec(expr) | type=(ref null func), init el*, active (table=0, offset=e)
|
|
1848
|
-
mode === 4 ?
|
|
1834
|
+
mode === 4 ? offset : (
|
|
1849
1835
|
// 0b101 et:reftype el*:vec(expr) | type=et, init el*, passive
|
|
1850
1836
|
mode === 5 ? rt : (
|
|
1851
1837
|
// 0b110 x:tabidx e:expr et:reftype el*:vec(expr) | type=et, init el*, active (table=x, offset=e)
|
|
1852
|
-
mode === 6 ? [...uleb(tabidx || 0), ...
|
|
1838
|
+
mode === 6 ? [...uleb(tabidx || 0), ...offset, ...rt] : (
|
|
1853
1839
|
// 0b111 et:reftype el*:vec(expr) | type=et, init el*, passive declare
|
|
1854
1840
|
rt
|
|
1855
1841
|
)
|
|
@@ -1910,7 +1896,7 @@ var build = [
|
|
|
1910
1896
|
// (data (global.get $x) "\aa" "\bb"?)
|
|
1911
1897
|
// (data (i8 1 2 3) ...) numeric values (WAT numeric values, Phase 2)
|
|
1912
1898
|
(inits, ctx) => {
|
|
1913
|
-
let
|
|
1899
|
+
let offset, memidx = 0;
|
|
1914
1900
|
if (inits[0]?.[0] === "memory") {
|
|
1915
1901
|
[, memidx] = inits.shift();
|
|
1916
1902
|
memidx = id(memidx, ctx.memory);
|
|
@@ -1918,15 +1904,15 @@ var build = [
|
|
|
1918
1904
|
memidx = id(inits.shift(), ctx.memory);
|
|
1919
1905
|
}
|
|
1920
1906
|
if (Array.isArray(inits[0]) && typeof inits[0]?.[0] === "string") {
|
|
1921
|
-
|
|
1922
|
-
if (
|
|
1923
|
-
|
|
1907
|
+
offset = inits.shift();
|
|
1908
|
+
if (offset[0] === "offset") [, offset] = offset;
|
|
1909
|
+
offset ?? err("Bad offset", offset);
|
|
1924
1910
|
}
|
|
1925
1911
|
return [
|
|
1926
1912
|
...// active: 2, x=memidx, e=expr
|
|
1927
|
-
memidx ? [2, ...uleb(memidx), ...expr(
|
|
1913
|
+
memidx ? [2, ...uleb(memidx), ...expr(offset, ctx)] : (
|
|
1928
1914
|
// active: 0, e=expr
|
|
1929
|
-
|
|
1915
|
+
offset ? [0, ...expr(offset, ctx)] : (
|
|
1930
1916
|
// passive: 1
|
|
1931
1917
|
[1]
|
|
1932
1918
|
)
|
|
@@ -2142,7 +2128,7 @@ var instr = (nodes, ctx) => {
|
|
|
2142
2128
|
};
|
|
2143
2129
|
var ulebSize = (n) => uleb(n).length;
|
|
2144
2130
|
var instrSize = (nodes, ctx) => {
|
|
2145
|
-
let
|
|
2131
|
+
let size = 0, meta = [];
|
|
2146
2132
|
while (nodes?.length) {
|
|
2147
2133
|
let op = nodes.shift();
|
|
2148
2134
|
if (op?.[0] === "@metadata") {
|
|
@@ -2157,12 +2143,12 @@ var instrSize = (nodes, ctx) => {
|
|
|
2157
2143
|
let n = opc.length;
|
|
2158
2144
|
if (HANDLER[op]) n += HANDLER[op](nodes, ctx, op).length;
|
|
2159
2145
|
if (meta.length) {
|
|
2160
|
-
for (const [type, data] of meta) (ctx.meta[type] ??= []).push([
|
|
2146
|
+
for (const [type, data] of meta) (ctx.meta[type] ??= []).push([size, data]);
|
|
2161
2147
|
meta = [];
|
|
2162
2148
|
}
|
|
2163
|
-
|
|
2149
|
+
size += n;
|
|
2164
2150
|
}
|
|
2165
|
-
return
|
|
2151
|
+
return size + 1;
|
|
2166
2152
|
};
|
|
2167
2153
|
var codeItemSize = (body, ctx) => {
|
|
2168
2154
|
let [typeidx, param] = body.shift();
|
|
@@ -2200,12 +2186,12 @@ var expr = (node, ctx) => instr(normalize([node], ctx), ctx);
|
|
|
2200
2186
|
var id = (nm, list, n) => (n = isId(nm) ? list[nm] : +nm, n in list ? n : err(`Unknown ${list.name} ${nm}`));
|
|
2201
2187
|
var blockid = (nm, block, i) => (i = isId(nm) ? block.length - block[nm] : +nm, isNaN(i) || i > block.length ? err(`Bad label ${nm}`) : i);
|
|
2202
2188
|
var memarg = (args) => {
|
|
2203
|
-
let align2,
|
|
2204
|
-
while (isMemParam(args[0])) [k, v] = args.shift().split("="), k === "offset" ?
|
|
2205
|
-
if (
|
|
2189
|
+
let align2, offset, k, v;
|
|
2190
|
+
while (isMemParam(args[0])) [k, v] = args.shift().split("="), k === "offset" ? offset = +v : k === "align" ? align2 = +v : err(`Unknown param ${k}=${v}`);
|
|
2191
|
+
if (offset < 0 || offset > 4294967295) err(`Bad offset ${offset}`);
|
|
2206
2192
|
if (align2 <= 0 || align2 > 4294967295) err(`Bad align ${align2}`);
|
|
2207
2193
|
if (align2) (align2 = Math.log2(align2)) % 1 && err(`Bad align ${align2}`);
|
|
2208
|
-
return [align2,
|
|
2194
|
+
return [align2, offset];
|
|
2209
2195
|
};
|
|
2210
2196
|
var memargEnc = (nodes, op, memIdx = 0) => {
|
|
2211
2197
|
const [a, o] = memarg(nodes), alignVal = (a ?? align(op)) | (memIdx && 64);
|
|
@@ -2326,3846 +2312,40 @@ function print(tree, options = {}) {
|
|
|
2326
2312
|
}
|
|
2327
2313
|
}
|
|
2328
2314
|
|
|
2329
|
-
// src/
|
|
2330
|
-
var
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
var genId = (prefix) => `$__${prefix}${uid++}`;
|
|
2341
|
-
var funcref = (ast, ctx) => {
|
|
2342
|
-
const refs = /* @__PURE__ */ new Set();
|
|
2343
|
-
walk(ast, (node) => {
|
|
2344
|
-
if (Array.isArray(node) && node[0] === "ref.func") refs.add(node[1]);
|
|
2345
|
-
});
|
|
2346
|
-
if (!refs.size) return ast;
|
|
2347
|
-
const tableId = genId("fntbl");
|
|
2348
|
-
const refList = [...refs];
|
|
2349
|
-
const refIdx = Object.fromEntries(refList.map((r, i) => [r, i]));
|
|
2350
|
-
const funcs = findNodes(ast, "func");
|
|
2351
|
-
const insertPos = funcs.length ? funcs[0].idx : ast[0] === "module" ? 1 : 0;
|
|
2352
|
-
insert(ast, insertPos, ["table", tableId, "funcref", ["elem", ...refList]]);
|
|
2353
|
-
const funcSigs = {};
|
|
2354
|
-
walk(ast, (node) => {
|
|
2355
|
-
if (!Array.isArray(node) || node[0] !== "func") return;
|
|
2356
|
-
const id2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
2357
|
-
if (!id2) return;
|
|
2358
|
-
const params = [], results = [];
|
|
2359
|
-
for (const part of node) {
|
|
2360
|
-
if (Array.isArray(part) && part[0] === "param") {
|
|
2361
|
-
for (let i = 1; i < part.length; i++) if (part[i][0] !== "$") params.push(part[i]);
|
|
2362
|
-
}
|
|
2363
|
-
if (Array.isArray(part) && part[0] === "result") {
|
|
2364
|
-
for (let i = 1; i < part.length; i++) results.push(part[i]);
|
|
2365
|
-
}
|
|
2366
|
-
}
|
|
2367
|
-
funcSigs[id2] = { params, results };
|
|
2368
|
-
});
|
|
2369
|
-
walkPost(ast, (node, parent, idx) => {
|
|
2370
|
-
if (!Array.isArray(node) || !parent) return;
|
|
2371
|
-
if (node[0] === "ref.func" && refIdx[node[1]] !== void 0) {
|
|
2372
|
-
parent[idx] = ["i32.const", refIdx[node[1]]];
|
|
2373
|
-
}
|
|
2374
|
-
if (node[0] === "call_ref") {
|
|
2375
|
-
const typeRef = node[1];
|
|
2376
|
-
const args = node.slice(2);
|
|
2377
|
-
parent[idx] = ["call_indirect", tableId, ["type", typeRef], ...args];
|
|
2378
|
-
}
|
|
2379
|
-
if (node[0] === "return_call_ref") {
|
|
2380
|
-
const typeRef = node[1];
|
|
2381
|
-
const args = node.slice(2);
|
|
2382
|
-
parent[idx] = ["return_call_indirect", tableId, ["type", typeRef], ...args];
|
|
2383
|
-
}
|
|
2384
|
-
});
|
|
2385
|
-
return ast;
|
|
2386
|
-
};
|
|
2387
|
-
var SIGN_EXT_SHIFTS = {
|
|
2388
|
-
"i32.extend8_s": ["i32", 24],
|
|
2389
|
-
"i32.extend16_s": ["i32", 16],
|
|
2390
|
-
"i64.extend8_s": ["i64", 56n],
|
|
2391
|
-
"i64.extend16_s": ["i64", 48n],
|
|
2392
|
-
"i64.extend32_s": ["i64", 32n]
|
|
2393
|
-
};
|
|
2394
|
-
var sign_ext = (ast, ctx) => {
|
|
2395
|
-
walkPost(ast, (node, parent, idx) => {
|
|
2396
|
-
if (!Array.isArray(node) || !parent) return;
|
|
2397
|
-
const info = SIGN_EXT_SHIFTS[node[0]];
|
|
2398
|
-
if (!info) return;
|
|
2399
|
-
const [type, shift] = info;
|
|
2400
|
-
const arg = node.slice(1);
|
|
2401
|
-
parent[idx] = [
|
|
2402
|
-
`${type}.shr_s`,
|
|
2403
|
-
[`${type}.shl`, ...arg, [`${type}.const`, shift]],
|
|
2404
|
-
[`${type}.const`, shift]
|
|
2405
|
-
];
|
|
2406
|
-
});
|
|
2407
|
-
return ast;
|
|
2408
|
-
};
|
|
2409
|
-
var TRUNC_SAT_INFO = {
|
|
2410
|
-
"i32.trunc_sat_f32_s": { itype: "i32", ftype: "f32", signed: true, min: -2147483648, max: 2147483647 },
|
|
2411
|
-
"i32.trunc_sat_f32_u": { itype: "i32", ftype: "f32", signed: false, min: 0, max: 4294967295 },
|
|
2412
|
-
"i32.trunc_sat_f64_s": { itype: "i32", ftype: "f64", signed: true, min: -2147483648, max: 2147483647 },
|
|
2413
|
-
"i32.trunc_sat_f64_u": { itype: "i32", ftype: "f64", signed: false, min: 0, max: 4294967295 },
|
|
2414
|
-
"i64.trunc_sat_f32_s": { itype: "i64", ftype: "f32", signed: true, min: -9223372036854775808n, max: 9223372036854775807n },
|
|
2415
|
-
"i64.trunc_sat_f32_u": { itype: "i64", ftype: "f32", signed: false, min: 0n, max: 18446744073709551615n },
|
|
2416
|
-
"i64.trunc_sat_f64_s": { itype: "i64", ftype: "f64", signed: true, min: -9223372036854775808n, max: 9223372036854775807n },
|
|
2417
|
-
"i64.trunc_sat_f64_u": { itype: "i64", ftype: "f64", signed: false, min: 0n, max: 18446744073709551615n }
|
|
2418
|
-
};
|
|
2419
|
-
var nontrapping = (ast, ctx) => {
|
|
2420
|
-
const used = /* @__PURE__ */ new Set();
|
|
2421
|
-
walk(ast, (node) => {
|
|
2422
|
-
if (Array.isArray(node) && TRUNC_SAT_INFO[node[0]]) used.add(node[0]);
|
|
2423
|
-
});
|
|
2424
|
-
if (!used.size) return ast;
|
|
2425
|
-
const helpers = {};
|
|
2426
|
-
for (const op of used) {
|
|
2427
|
-
const { itype, ftype, signed, min, max } = TRUNC_SAT_INFO[op];
|
|
2428
|
-
const id2 = genId(`trunc_${itype}_${ftype}_${signed ? "s" : "u"}`);
|
|
2429
|
-
helpers[op] = id2;
|
|
2430
|
-
const truncOp = `${itype}.trunc_${ftype}_${signed ? "s" : "u"}`;
|
|
2431
|
-
const zero = itype === "i64" ? 0n : 0;
|
|
2432
|
-
const helper = [
|
|
2433
|
-
"func",
|
|
2434
|
-
id2,
|
|
2435
|
-
["param", "$v", ftype],
|
|
2436
|
-
["result", itype],
|
|
2437
|
-
// NaN check: if v != v return 0
|
|
2438
|
-
[
|
|
2439
|
-
"if",
|
|
2440
|
-
["result", itype],
|
|
2441
|
-
[`${ftype}.ne`, ["local.get", "$v"], ["local.get", "$v"]],
|
|
2442
|
-
["then", [`${itype}.const`, zero]],
|
|
2443
|
-
[
|
|
2444
|
-
"else",
|
|
2445
|
-
// Below min check
|
|
2446
|
-
[
|
|
2447
|
-
"if",
|
|
2448
|
-
["result", itype],
|
|
2449
|
-
[`${ftype}.lt`, ["local.get", "$v"], [`${ftype}.const`, typeof min === "bigint" ? Number(min) : min]],
|
|
2450
|
-
["then", [`${itype}.const`, min]],
|
|
2451
|
-
[
|
|
2452
|
-
"else",
|
|
2453
|
-
// Above max check
|
|
2454
|
-
[
|
|
2455
|
-
"if",
|
|
2456
|
-
["result", itype],
|
|
2457
|
-
[`${ftype}.gt`, ["local.get", "$v"], [`${ftype}.const`, typeof max === "bigint" ? Number(max) : max]],
|
|
2458
|
-
["then", [`${itype}.const`, max]],
|
|
2459
|
-
["else", [truncOp, ["local.get", "$v"]]]
|
|
2460
|
-
]
|
|
2461
|
-
]
|
|
2462
|
-
]
|
|
2463
|
-
]
|
|
2464
|
-
]
|
|
2465
|
-
];
|
|
2466
|
-
ast.push(helper);
|
|
2467
|
-
}
|
|
2468
|
-
walkPost(ast, (node, parent, idx) => {
|
|
2469
|
-
if (!Array.isArray(node) || !parent) return;
|
|
2470
|
-
if (helpers[node[0]]) {
|
|
2471
|
-
parent[idx] = ["call", helpers[node[0]], ...node.slice(1)];
|
|
2472
|
-
}
|
|
2473
|
-
});
|
|
2474
|
-
return ast;
|
|
2475
|
-
};
|
|
2476
|
-
var bulk_memory = (ast, ctx) => {
|
|
2477
|
-
const needsCopy = /* @__PURE__ */ new Set(), needsFill = /* @__PURE__ */ new Set();
|
|
2478
|
-
walk(ast, (node) => {
|
|
2479
|
-
if (!Array.isArray(node)) return;
|
|
2480
|
-
if (node[0] === "memory.copy") {
|
|
2481
|
-
const m1 = typeof node[1] === "number" ? node[1] : 0;
|
|
2482
|
-
const m2 = typeof node[2] === "number" ? node[2] : 0;
|
|
2483
|
-
needsCopy.add(`${m1}_${m2}`);
|
|
2484
|
-
}
|
|
2485
|
-
if (node[0] === "memory.fill") {
|
|
2486
|
-
const m = typeof node[1] === "number" ? node[1] : 0;
|
|
2487
|
-
needsFill.add(m);
|
|
2488
|
-
}
|
|
2489
|
-
});
|
|
2490
|
-
const copyHelpers = {}, fillHelpers = {};
|
|
2491
|
-
for (const key of needsCopy) {
|
|
2492
|
-
const [m1, m2] = key.split("_").map(Number);
|
|
2493
|
-
const id2 = genId(`memcpy${key === "0_0" ? "" : "_" + key}`);
|
|
2494
|
-
copyHelpers[key] = id2;
|
|
2495
|
-
const store = m1 ? ["i32.store8", m1] : ["i32.store8"];
|
|
2496
|
-
const load = m2 ? ["i32.load8_u", m2] : ["i32.load8_u"];
|
|
2497
|
-
ast.push([
|
|
2498
|
-
"func",
|
|
2499
|
-
id2,
|
|
2500
|
-
["param", "$dst", "i32"],
|
|
2501
|
-
["param", "$src", "i32"],
|
|
2502
|
-
["param", "$len", "i32"],
|
|
2503
|
-
["local", "$i", "i32"],
|
|
2504
|
-
[
|
|
2505
|
-
"block",
|
|
2506
|
-
"$done",
|
|
2507
|
-
[
|
|
2508
|
-
"loop",
|
|
2509
|
-
"$loop",
|
|
2510
|
-
["br_if", "$done", ["i32.ge_u", ["local.get", "$i"], ["local.get", "$len"]]],
|
|
2511
|
-
[
|
|
2512
|
-
...store,
|
|
2513
|
-
["i32.add", ["local.get", "$dst"], ["local.get", "$i"]],
|
|
2514
|
-
[...load, ["i32.add", ["local.get", "$src"], ["local.get", "$i"]]]
|
|
2515
|
-
],
|
|
2516
|
-
["local.set", "$i", ["i32.add", ["local.get", "$i"], ["i32.const", 1]]],
|
|
2517
|
-
["br", "$loop"]
|
|
2518
|
-
]
|
|
2519
|
-
]
|
|
2520
|
-
]);
|
|
2521
|
-
}
|
|
2522
|
-
for (const m of needsFill) {
|
|
2523
|
-
const id2 = genId(`memset${m === 0 ? "" : "_" + m}`);
|
|
2524
|
-
fillHelpers[m] = id2;
|
|
2525
|
-
const store = m ? ["i32.store8", m] : ["i32.store8"];
|
|
2526
|
-
ast.push([
|
|
2527
|
-
"func",
|
|
2528
|
-
id2,
|
|
2529
|
-
["param", "$dst", "i32"],
|
|
2530
|
-
["param", "$val", "i32"],
|
|
2531
|
-
["param", "$len", "i32"],
|
|
2532
|
-
["local", "$i", "i32"],
|
|
2533
|
-
[
|
|
2534
|
-
"block",
|
|
2535
|
-
"$done",
|
|
2536
|
-
[
|
|
2537
|
-
"loop",
|
|
2538
|
-
"$loop",
|
|
2539
|
-
["br_if", "$done", ["i32.ge_u", ["local.get", "$i"], ["local.get", "$len"]]],
|
|
2540
|
-
[
|
|
2541
|
-
...store,
|
|
2542
|
-
["i32.add", ["local.get", "$dst"], ["local.get", "$i"]],
|
|
2543
|
-
["local.get", "$val"]
|
|
2544
|
-
],
|
|
2545
|
-
["local.set", "$i", ["i32.add", ["local.get", "$i"], ["i32.const", 1]]],
|
|
2546
|
-
["br", "$loop"]
|
|
2547
|
-
]
|
|
2548
|
-
]
|
|
2549
|
-
]);
|
|
2315
|
+
// src/template.js
|
|
2316
|
+
var PUA = "\uE000";
|
|
2317
|
+
function applyTransform(fn, name2, ast, opt) {
|
|
2318
|
+
if (typeof fn !== "function")
|
|
2319
|
+
throw Error(`watr: '${name2}' is not bundled in this entry \u2014 import it from 'watr/${name2}' and compose: compile(${name2}(src))`);
|
|
2320
|
+
return fn(ast, opt);
|
|
2321
|
+
}
|
|
2322
|
+
var exprType = (node, ctx = {}) => {
|
|
2323
|
+
if (!Array.isArray(node)) {
|
|
2324
|
+
if (typeof node === "string" && node[0] === "$" && ctx.locals?.[node]) return ctx.locals[node];
|
|
2325
|
+
return null;
|
|
2550
2326
|
}
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
parent[idx] = ["call", copyHelpers[`${m1}_${m2}`], ...args];
|
|
2558
|
-
}
|
|
2559
|
-
if (node[0] === "memory.fill") {
|
|
2560
|
-
const m = typeof node[1] === "number" ? node[1] : 0;
|
|
2561
|
-
const args = node.filter((n) => Array.isArray(n) || typeof n === "string" && n[0] === "$");
|
|
2562
|
-
parent[idx] = ["call", fillHelpers[m], ...args];
|
|
2563
|
-
}
|
|
2564
|
-
});
|
|
2565
|
-
return ast;
|
|
2566
|
-
};
|
|
2567
|
-
var return_call_transform = (ast, ctx) => {
|
|
2568
|
-
let hasAnyTailCall = false;
|
|
2569
|
-
walk(ast, (node) => {
|
|
2570
|
-
if (Array.isArray(node) && (node[0] === "return_call" || node[0] === "return_call_indirect")) {
|
|
2571
|
-
hasAnyTailCall = true;
|
|
2572
|
-
}
|
|
2573
|
-
});
|
|
2574
|
-
if (!hasAnyTailCall) return ast;
|
|
2575
|
-
walkPost(ast, (node, parent, idx) => {
|
|
2576
|
-
if (!Array.isArray(node) || !parent) return;
|
|
2577
|
-
if (node[0] === "return_call") {
|
|
2578
|
-
parent[idx] = ["return", ["call", ...node.slice(1)]];
|
|
2579
|
-
}
|
|
2580
|
-
if (node[0] === "return_call_indirect") {
|
|
2581
|
-
parent[idx] = ["return", ["call_indirect", ...node.slice(1)]];
|
|
2582
|
-
}
|
|
2583
|
-
});
|
|
2584
|
-
return ast;
|
|
2585
|
-
};
|
|
2586
|
-
var i31ref = (ast, ctx) => {
|
|
2587
|
-
walkPost(ast, (node, parent, idx) => {
|
|
2588
|
-
if (!Array.isArray(node) || !parent) return;
|
|
2589
|
-
if (node[0] === "ref.i31") {
|
|
2590
|
-
parent[idx] = ["i32.and", ...node.slice(1), ["i32.const", 2147483647]];
|
|
2591
|
-
}
|
|
2592
|
-
if (node[0] === "i31.get_u") {
|
|
2593
|
-
parent[idx] = node.length > 1 ? node[1] : ["drop"];
|
|
2594
|
-
}
|
|
2595
|
-
if (node[0] === "i31.get_s") {
|
|
2596
|
-
const arg = node.slice(1);
|
|
2597
|
-
parent[idx] = ["i32.shr_s", ["i32.shl", ...arg, ["i32.const", 1]], ["i32.const", 1]];
|
|
2598
|
-
}
|
|
2599
|
-
});
|
|
2600
|
-
return ast;
|
|
2601
|
-
};
|
|
2602
|
-
var extended_const = (ast, ctx) => {
|
|
2603
|
-
const globals2 = {};
|
|
2604
|
-
walk(ast, (node) => {
|
|
2605
|
-
if (!Array.isArray(node) || node[0] !== "global") return;
|
|
2606
|
-
const id2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
2607
|
-
if (!id2) return;
|
|
2608
|
-
for (let i = node.length - 1; i >= 0; i--) {
|
|
2609
|
-
const init = node[i];
|
|
2610
|
-
if (!Array.isArray(init)) continue;
|
|
2611
|
-
if (init[0] === "i32.const" || init[0] === "i64.const" || init[0] === "f32.const" || init[0] === "f64.const") {
|
|
2612
|
-
globals2[id2] = { type: init[0].split(".")[0], value: init[1] };
|
|
2613
|
-
break;
|
|
2614
|
-
}
|
|
2615
|
-
}
|
|
2616
|
-
});
|
|
2617
|
-
const evalConst = (node) => {
|
|
2618
|
-
if (!Array.isArray(node)) return node;
|
|
2619
|
-
const op = node[0];
|
|
2620
|
-
if (op === "global.get" && globals2[node[1]]) {
|
|
2621
|
-
const g = globals2[node[1]];
|
|
2622
|
-
return [`${g.type}.const`, g.value];
|
|
2623
|
-
}
|
|
2624
|
-
if (op === "i32.add" || op === "i64.add") {
|
|
2625
|
-
const a = evalConst(node[1]), b = evalConst(node[2]);
|
|
2626
|
-
if (a && b && a[0]?.endsWith(".const") && b[0]?.endsWith(".const")) {
|
|
2627
|
-
const type = op.split(".")[0];
|
|
2628
|
-
const va = type === "i64" ? BigInt(a[1]) : Number(a[1]);
|
|
2629
|
-
const vb = type === "i64" ? BigInt(b[1]) : Number(b[1]);
|
|
2630
|
-
return [`${type}.const`, va + vb];
|
|
2631
|
-
}
|
|
2632
|
-
}
|
|
2633
|
-
if (op === "i32.sub" || op === "i64.sub") {
|
|
2634
|
-
const a = evalConst(node[1]), b = evalConst(node[2]);
|
|
2635
|
-
if (a && b && a[0]?.endsWith(".const") && b[0]?.endsWith(".const")) {
|
|
2636
|
-
const type = op.split(".")[0];
|
|
2637
|
-
const va = type === "i64" ? BigInt(a[1]) : Number(a[1]);
|
|
2638
|
-
const vb = type === "i64" ? BigInt(b[1]) : Number(b[1]);
|
|
2639
|
-
return [`${type}.const`, va - vb];
|
|
2640
|
-
}
|
|
2641
|
-
}
|
|
2642
|
-
if (op === "i32.mul" || op === "i64.mul") {
|
|
2643
|
-
const a = evalConst(node[1]), b = evalConst(node[2]);
|
|
2644
|
-
if (a && b && a[0]?.endsWith(".const") && b[0]?.endsWith(".const")) {
|
|
2645
|
-
const type = op.split(".")[0];
|
|
2646
|
-
const va = type === "i64" ? BigInt(a[1]) : Number(a[1]);
|
|
2647
|
-
const vb = type === "i64" ? BigInt(b[1]) : Number(b[1]);
|
|
2648
|
-
return [`${type}.const`, va * vb];
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
return node;
|
|
2652
|
-
};
|
|
2653
|
-
walkPost(ast, (node, parent, idx) => {
|
|
2654
|
-
if (!Array.isArray(node) || node[0] !== "global" || !parent) return;
|
|
2655
|
-
for (let i = 2; i < node.length; i++) {
|
|
2656
|
-
if (Array.isArray(node[i])) {
|
|
2657
|
-
const evaluated = evalConst(node[i]);
|
|
2658
|
-
if (evaluated !== node[i]) node[i] = evaluated;
|
|
2659
|
-
}
|
|
2660
|
-
}
|
|
2661
|
-
});
|
|
2662
|
-
return ast;
|
|
2327
|
+
const [op, ...args] = node;
|
|
2328
|
+
const rt = resultType(op);
|
|
2329
|
+
if (rt) return rt;
|
|
2330
|
+
if (op === "local.get" && ctx.locals?.[args[0]]) return ctx.locals[args[0]];
|
|
2331
|
+
if (op === "call" && ctx.funcs?.[args[0]]) return ctx.funcs[args[0]].result?.[0];
|
|
2332
|
+
return null;
|
|
2663
2333
|
};
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
walk(ast, (node) => {
|
|
2668
|
-
if (!Array.isArray(node) || node[0] !== "func") return;
|
|
2669
|
-
const id2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
2670
|
-
const results = [];
|
|
2671
|
-
for (const part of node) {
|
|
2672
|
-
if (Array.isArray(part) && part[0] === "result") {
|
|
2673
|
-
for (let i = 1; i < part.length; i++) results.push(part[i]);
|
|
2674
|
-
}
|
|
2675
|
-
}
|
|
2676
|
-
if (results.length > 1 && id2) {
|
|
2677
|
-
multiResultFuncs.set(id2, results);
|
|
2678
|
-
}
|
|
2679
|
-
});
|
|
2680
|
-
if (!multiResultFuncs.size) return ast;
|
|
2681
|
-
const maxReturns = Math.max(...[...multiResultFuncs.values()].map((r) => r.length));
|
|
2682
|
-
const globalsByType = {};
|
|
2683
|
-
for (const [id2, results] of multiResultFuncs) {
|
|
2684
|
-
for (let i = 1; i < results.length; i++) {
|
|
2685
|
-
const type = results[i];
|
|
2686
|
-
if (!globalsByType[type]) globalsByType[type] = [];
|
|
2687
|
-
if (globalsByType[type].length < i) {
|
|
2688
|
-
const gid = genId(`ret_${type}_${globalsByType[type].length}`);
|
|
2689
|
-
globalsByType[type].push(gid);
|
|
2690
|
-
returnGlobals.push(["global", gid, ["mut", type], [`${type}.const`, type === "i64" ? 0n : 0]]);
|
|
2691
|
-
}
|
|
2692
|
-
}
|
|
2693
|
-
}
|
|
2694
|
-
const insertPos = ast[0] === "module" ? 1 : 0;
|
|
2695
|
-
for (const g of returnGlobals.reverse()) {
|
|
2696
|
-
insert(ast, insertPos, g);
|
|
2697
|
-
}
|
|
2698
|
-
walkPost(ast, (node, parent, idx) => {
|
|
2699
|
-
if (!Array.isArray(node) || node[0] !== "func") return;
|
|
2700
|
-
const id2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
2701
|
-
if (!id2 || !multiResultFuncs.has(id2)) return;
|
|
2702
|
-
const results = multiResultFuncs.get(id2);
|
|
2334
|
+
function walk(node, fn) {
|
|
2335
|
+
node = fn(node);
|
|
2336
|
+
if (Array.isArray(node)) {
|
|
2703
2337
|
for (let i = 0; i < node.length; i++) {
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
}
|
|
2708
|
-
}
|
|
2709
|
-
});
|
|
2710
|
-
return ast;
|
|
2711
|
-
};
|
|
2712
|
-
var TYPE_SIZES = { i32: 4, i64: 8, f32: 4, f64: 8 };
|
|
2713
|
-
var gc = (ast, ctx) => {
|
|
2714
|
-
const types = /* @__PURE__ */ new Map();
|
|
2715
|
-
const typeIndices = /* @__PURE__ */ new Map();
|
|
2716
|
-
let typeIdx = 1;
|
|
2717
|
-
walk(ast, (node) => {
|
|
2718
|
-
if (!Array.isArray(node) || node[0] !== "type") return;
|
|
2719
|
-
const id2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
2720
|
-
if (!id2) return;
|
|
2721
|
-
for (const def of node) {
|
|
2722
|
-
if (!Array.isArray(def)) continue;
|
|
2723
|
-
if (def[0] === "struct") {
|
|
2724
|
-
const fields = [];
|
|
2725
|
-
for (const f of def) {
|
|
2726
|
-
if (Array.isArray(f) && f[0] === "field") {
|
|
2727
|
-
const fname = typeof f[1] === "string" && f[1][0] === "$" ? f[1] : null;
|
|
2728
|
-
const ftype = fname ? f[2] : f[1];
|
|
2729
|
-
const actualType = Array.isArray(ftype) && ftype[0] === "mut" ? ftype[1] : ftype;
|
|
2730
|
-
fields.push({ name: fname, type: actualType });
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
|
-
types.set(id2, { kind: "struct", fields });
|
|
2734
|
-
typeIndices.set(id2, typeIdx++);
|
|
2735
|
-
}
|
|
2736
|
-
if (def[0] === "array") {
|
|
2737
|
-
const elemDef = def[1];
|
|
2738
|
-
const elemType = Array.isArray(elemDef) && elemDef[0] === "mut" ? elemDef[1] : elemDef;
|
|
2739
|
-
types.set(id2, { kind: "array", elemType });
|
|
2740
|
-
typeIndices.set(id2, typeIdx++);
|
|
2741
|
-
}
|
|
2742
|
-
}
|
|
2743
|
-
});
|
|
2744
|
-
if (!types.size) return ast;
|
|
2745
|
-
const hasMemory = findNodes(ast, "memory").length > 0;
|
|
2746
|
-
const allocId = genId("alloc");
|
|
2747
|
-
const heapPtrId = genId("heap_ptr");
|
|
2748
|
-
const insertPos = ast[0] === "module" ? 1 : 0;
|
|
2749
|
-
if (!hasMemory) {
|
|
2750
|
-
insert(ast, insertPos, ["memory", 1]);
|
|
2751
|
-
}
|
|
2752
|
-
insert(ast, insertPos + 1, ["global", heapPtrId, ["mut", "i32"], ["i32.const", 1024]]);
|
|
2753
|
-
const allocFunc = [
|
|
2754
|
-
"func",
|
|
2755
|
-
allocId,
|
|
2756
|
-
["param", "$size", "i32"],
|
|
2757
|
-
["result", "i32"],
|
|
2758
|
-
["local", "$ptr", "i32"],
|
|
2759
|
-
["local.set", "$ptr", ["global.get", heapPtrId]],
|
|
2760
|
-
["global.set", heapPtrId, ["i32.add", ["global.get", heapPtrId], ["local.get", "$size"]]],
|
|
2761
|
-
["local.get", "$ptr"]
|
|
2762
|
-
];
|
|
2763
|
-
ast.push(allocFunc);
|
|
2764
|
-
const structSize = (typeDef) => {
|
|
2765
|
-
let size2 = 4;
|
|
2766
|
-
for (const f of typeDef.fields) {
|
|
2767
|
-
size2 += TYPE_SIZES[f.type] || 4;
|
|
2768
|
-
}
|
|
2769
|
-
return size2;
|
|
2770
|
-
};
|
|
2771
|
-
const fieldOffset = (typeDef, fieldIdx) => {
|
|
2772
|
-
let offset2 = 4;
|
|
2773
|
-
for (let i = 0; i < fieldIdx; i++) {
|
|
2774
|
-
offset2 += TYPE_SIZES[typeDef.fields[i].type] || 4;
|
|
2775
|
-
}
|
|
2776
|
-
return offset2;
|
|
2777
|
-
};
|
|
2778
|
-
const findFieldIdx = (typeDef, fieldName) => {
|
|
2779
|
-
for (let i = 0; i < typeDef.fields.length; i++) {
|
|
2780
|
-
if (typeDef.fields[i].name === fieldName) return i;
|
|
2781
|
-
}
|
|
2782
|
-
return -1;
|
|
2783
|
-
};
|
|
2784
|
-
let localCounter = 0;
|
|
2785
|
-
const genLocal = () => `$__gc_tmp${localCounter++}`;
|
|
2786
|
-
walk(ast, (node) => {
|
|
2787
|
-
if (!Array.isArray(node) || node[0] !== "func") return;
|
|
2788
|
-
let needsStructPtr = false;
|
|
2789
|
-
let needsArrayPtr = false;
|
|
2790
|
-
let needsArrayLen = false;
|
|
2791
|
-
let needsArrayIdx = false;
|
|
2792
|
-
walk(node, (n) => {
|
|
2793
|
-
if (!Array.isArray(n)) return;
|
|
2794
|
-
if (n[0] === "struct.new" || n[0] === "struct.new_default") needsStructPtr = true;
|
|
2795
|
-
if (n[0] === "array.new" || n[0] === "array.new_default") {
|
|
2796
|
-
needsArrayPtr = true;
|
|
2797
|
-
needsArrayLen = true;
|
|
2798
|
-
needsArrayIdx = true;
|
|
2799
|
-
}
|
|
2800
|
-
});
|
|
2801
|
-
if (!needsStructPtr && !needsArrayPtr) return;
|
|
2802
|
-
let insertIdx = 1;
|
|
2803
|
-
for (let i = 1; i < node.length; i++) {
|
|
2804
|
-
const item = node[i];
|
|
2805
|
-
if (Array.isArray(item) && (item[0] === "param" || item[0] === "result" || item[0] === "local" || item[0] === "export" || item[0] === "type")) {
|
|
2806
|
-
insertIdx = i + 1;
|
|
2807
|
-
} else if (typeof item === "string" && item[0] === "$") {
|
|
2808
|
-
insertIdx = i + 1;
|
|
2809
|
-
} else if (!Array.isArray(item)) {
|
|
2810
|
-
insertIdx = i + 1;
|
|
2811
|
-
} else {
|
|
2812
|
-
break;
|
|
2813
|
-
}
|
|
2814
|
-
}
|
|
2815
|
-
if (needsStructPtr) node.splice(insertIdx++, 0, ["local", "$__gc_ptr", "i32"]);
|
|
2816
|
-
if (needsArrayPtr) node.splice(insertIdx++, 0, ["local", "$__gc_aptr", "i32"]);
|
|
2817
|
-
if (needsArrayLen) node.splice(insertIdx++, 0, ["local", "$__gc_alen", "i32"]);
|
|
2818
|
-
if (needsArrayIdx) node.splice(insertIdx++, 0, ["local", "$__gc_aidx", "i32"]);
|
|
2819
|
-
});
|
|
2820
|
-
walkPost(ast, (node, parent, idx) => {
|
|
2821
|
-
if (!Array.isArray(node) || !parent) return;
|
|
2822
|
-
if (node[0] === "struct.new" || node[0] === "struct.new_default") {
|
|
2823
|
-
const typeId = node[1];
|
|
2824
|
-
const typeDef = types.get(typeId);
|
|
2825
|
-
if (!typeDef || typeDef.kind !== "struct") return;
|
|
2826
|
-
const size2 = structSize(typeDef);
|
|
2827
|
-
const typeTag = typeIndices.get(typeId);
|
|
2828
|
-
const args = node.slice(2);
|
|
2829
|
-
const ptrLocal = "$__gc_ptr";
|
|
2830
|
-
const stores = [
|
|
2831
|
-
["local.set", ptrLocal, ["call", allocId, ["i32.const", size2]]],
|
|
2832
|
-
["i32.store", ["local.get", ptrLocal], ["i32.const", typeTag]]
|
|
2833
|
-
// store type tag
|
|
2834
|
-
];
|
|
2835
|
-
if (node[0] === "struct.new") {
|
|
2836
|
-
for (let i = 0; i < typeDef.fields.length; i++) {
|
|
2837
|
-
const f = typeDef.fields[i];
|
|
2838
|
-
const offset2 = fieldOffset(typeDef, i);
|
|
2839
|
-
const storeOp = f.type === "i64" ? "i64.store" : f.type === "f32" ? "f32.store" : f.type === "f64" ? "f64.store" : "i32.store";
|
|
2840
|
-
stores.push([storeOp, ["i32.add", ["local.get", ptrLocal], ["i32.const", offset2]], args[i] || [`${f.type}.const`, 0]]);
|
|
2841
|
-
}
|
|
2842
|
-
} else {
|
|
2843
|
-
for (let i = 0; i < typeDef.fields.length; i++) {
|
|
2844
|
-
const f = typeDef.fields[i];
|
|
2845
|
-
const offset2 = fieldOffset(typeDef, i);
|
|
2846
|
-
const storeOp = f.type === "i64" ? "i64.store" : f.type === "f32" ? "f32.store" : f.type === "f64" ? "f64.store" : "i32.store";
|
|
2847
|
-
const zero = f.type === "i64" ? ["i64.const", 0n] : f.type === "f32" ? ["f32.const", 0] : f.type === "f64" ? ["f64.const", 0] : ["i32.const", 0];
|
|
2848
|
-
stores.push([storeOp, ["i32.add", ["local.get", ptrLocal], ["i32.const", offset2]], zero]);
|
|
2849
|
-
}
|
|
2850
|
-
}
|
|
2851
|
-
stores.push(["local.get", ptrLocal]);
|
|
2852
|
-
parent[idx] = ["block", ["result", "i32"], ...stores];
|
|
2853
|
-
}
|
|
2854
|
-
if (node[0] === "struct.get") {
|
|
2855
|
-
const typeId = node[1];
|
|
2856
|
-
const fieldId = node[2];
|
|
2857
|
-
const ref = node[3];
|
|
2858
|
-
const typeDef = types.get(typeId);
|
|
2859
|
-
if (!typeDef || typeDef.kind !== "struct") return;
|
|
2860
|
-
const fieldIdx = typeof fieldId === "string" && fieldId[0] === "$" ? findFieldIdx(typeDef, fieldId) : parseInt(fieldId);
|
|
2861
|
-
if (fieldIdx < 0) return;
|
|
2862
|
-
const f = typeDef.fields[fieldIdx];
|
|
2863
|
-
const offset2 = fieldOffset(typeDef, fieldIdx);
|
|
2864
|
-
const loadOp = f.type === "i64" ? "i64.load" : f.type === "f32" ? "f32.load" : f.type === "f64" ? "f64.load" : "i32.load";
|
|
2865
|
-
parent[idx] = [loadOp, ["i32.add", ref, ["i32.const", offset2]]];
|
|
2866
|
-
}
|
|
2867
|
-
if (node[0] === "struct.set") {
|
|
2868
|
-
const typeId = node[1];
|
|
2869
|
-
const fieldId = node[2];
|
|
2870
|
-
const ref = node[3];
|
|
2871
|
-
const val = node[4];
|
|
2872
|
-
const typeDef = types.get(typeId);
|
|
2873
|
-
if (!typeDef || typeDef.kind !== "struct") return;
|
|
2874
|
-
const fieldIdx = typeof fieldId === "string" && fieldId[0] === "$" ? findFieldIdx(typeDef, fieldId) : parseInt(fieldId);
|
|
2875
|
-
if (fieldIdx < 0) return;
|
|
2876
|
-
const f = typeDef.fields[fieldIdx];
|
|
2877
|
-
const offset2 = fieldOffset(typeDef, fieldIdx);
|
|
2878
|
-
const storeOp = f.type === "i64" ? "i64.store" : f.type === "f32" ? "f32.store" : f.type === "f64" ? "f64.store" : "i32.store";
|
|
2879
|
-
parent[idx] = [storeOp, ["i32.add", ref, ["i32.const", offset2]], val];
|
|
2880
|
-
}
|
|
2881
|
-
if (node[0] === "array.new" || node[0] === "array.new_default") {
|
|
2882
|
-
const typeId = node[1];
|
|
2883
|
-
const typeDef = types.get(typeId);
|
|
2884
|
-
if (!typeDef || typeDef.kind !== "array") return;
|
|
2885
|
-
const typeTag = typeIndices.get(typeId);
|
|
2886
|
-
const elemSize = TYPE_SIZES[typeDef.elemType] || 4;
|
|
2887
|
-
const val = node[0] === "array.new" ? node[2] : null;
|
|
2888
|
-
const len = node[0] === "array.new" ? node[3] : node[2];
|
|
2889
|
-
const ptrLocal = "$__gc_aptr";
|
|
2890
|
-
const lenLocal = "$__gc_alen";
|
|
2891
|
-
const iLocal = "$__gc_aidx";
|
|
2892
|
-
const storeOp = typeDef.elemType === "i64" ? "i64.store" : typeDef.elemType === "f32" ? "f32.store" : typeDef.elemType === "f64" ? "f64.store" : "i32.store";
|
|
2893
|
-
const ops = [
|
|
2894
|
-
["local.set", lenLocal, len],
|
|
2895
|
-
["local.set", ptrLocal, ["call", allocId, ["i32.add", ["i32.const", 8], ["i32.mul", ["local.get", lenLocal], ["i32.const", elemSize]]]]],
|
|
2896
|
-
["i32.store", ["local.get", ptrLocal], ["i32.const", typeTag]],
|
|
2897
|
-
["i32.store", ["i32.add", ["local.get", ptrLocal], ["i32.const", 4]], ["local.get", lenLocal]]
|
|
2898
|
-
];
|
|
2899
|
-
if (val) {
|
|
2900
|
-
ops.push(
|
|
2901
|
-
["local.set", iLocal, ["i32.const", 0]],
|
|
2902
|
-
[
|
|
2903
|
-
"block",
|
|
2904
|
-
"$done",
|
|
2905
|
-
[
|
|
2906
|
-
"loop",
|
|
2907
|
-
"$loop",
|
|
2908
|
-
["br_if", "$done", ["i32.ge_u", ["local.get", iLocal], ["local.get", lenLocal]]],
|
|
2909
|
-
[
|
|
2910
|
-
storeOp,
|
|
2911
|
-
[
|
|
2912
|
-
"i32.add",
|
|
2913
|
-
["i32.add", ["local.get", ptrLocal], ["i32.const", 8]],
|
|
2914
|
-
["i32.mul", ["local.get", iLocal], ["i32.const", elemSize]]
|
|
2915
|
-
],
|
|
2916
|
-
val
|
|
2917
|
-
],
|
|
2918
|
-
["local.set", iLocal, ["i32.add", ["local.get", iLocal], ["i32.const", 1]]],
|
|
2919
|
-
["br", "$loop"]
|
|
2920
|
-
]
|
|
2921
|
-
]
|
|
2922
|
-
);
|
|
2923
|
-
}
|
|
2924
|
-
ops.push(["local.get", ptrLocal]);
|
|
2925
|
-
parent[idx] = ["block", ["result", "i32"], ...ops];
|
|
2926
|
-
}
|
|
2927
|
-
if (node[0] === "array.get") {
|
|
2928
|
-
const typeId = node[1];
|
|
2929
|
-
const ref = node[2];
|
|
2930
|
-
const idx_val = node[3];
|
|
2931
|
-
const typeDef = types.get(typeId);
|
|
2932
|
-
if (!typeDef || typeDef.kind !== "array") return;
|
|
2933
|
-
const elemSize = TYPE_SIZES[typeDef.elemType] || 4;
|
|
2934
|
-
const loadOp = typeDef.elemType === "i64" ? "i64.load" : typeDef.elemType === "f32" ? "f32.load" : typeDef.elemType === "f64" ? "f64.load" : "i32.load";
|
|
2935
|
-
parent[idx] = [loadOp, ["i32.add", ["i32.add", ref, ["i32.const", 8]], ["i32.mul", idx_val, ["i32.const", elemSize]]]];
|
|
2936
|
-
}
|
|
2937
|
-
if (node[0] === "array.set") {
|
|
2938
|
-
const typeId = node[1];
|
|
2939
|
-
const ref = node[2];
|
|
2940
|
-
const idx_val = node[3];
|
|
2941
|
-
const val = node[4];
|
|
2942
|
-
const typeDef = types.get(typeId);
|
|
2943
|
-
if (!typeDef || typeDef.kind !== "array") return;
|
|
2944
|
-
const elemSize = TYPE_SIZES[typeDef.elemType] || 4;
|
|
2945
|
-
const storeOp = typeDef.elemType === "i64" ? "i64.store" : typeDef.elemType === "f32" ? "f32.store" : typeDef.elemType === "f64" ? "f64.store" : "i32.store";
|
|
2946
|
-
parent[idx] = [storeOp, ["i32.add", ["i32.add", ref, ["i32.const", 8]], ["i32.mul", idx_val, ["i32.const", elemSize]]], val];
|
|
2947
|
-
}
|
|
2948
|
-
if (node[0] === "array.len") {
|
|
2949
|
-
const ref = node[1];
|
|
2950
|
-
parent[idx] = ["i32.load", ["i32.add", ref, ["i32.const", 4]]];
|
|
2951
|
-
}
|
|
2952
|
-
});
|
|
2953
|
-
return ast;
|
|
2954
|
-
};
|
|
2955
|
-
var ref_cast = (ast, ctx) => {
|
|
2956
|
-
const typeIndices = /* @__PURE__ */ new Map();
|
|
2957
|
-
let typeIdx = 1;
|
|
2958
|
-
walk(ast, (node) => {
|
|
2959
|
-
if (!Array.isArray(node) || node[0] !== "type") return;
|
|
2960
|
-
const id2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
2961
|
-
if (!id2) return;
|
|
2962
|
-
for (const def of node) {
|
|
2963
|
-
if (Array.isArray(def) && (def[0] === "struct" || def[0] === "array")) {
|
|
2964
|
-
typeIndices.set(id2, typeIdx++);
|
|
2965
|
-
}
|
|
2966
|
-
}
|
|
2967
|
-
});
|
|
2968
|
-
if (!typeIndices.size) return ast;
|
|
2969
|
-
walkPost(ast, (node, parent, idx) => {
|
|
2970
|
-
if (!Array.isArray(node) || !parent) return;
|
|
2971
|
-
if (node[0] === "ref.test") {
|
|
2972
|
-
const reftype2 = node[1];
|
|
2973
|
-
let typeId = null;
|
|
2974
|
-
if (Array.isArray(reftype2) && reftype2[0] === "ref") {
|
|
2975
|
-
typeId = reftype2[1] === "null" ? reftype2[2] : reftype2[1];
|
|
2976
|
-
}
|
|
2977
|
-
const val = node[2];
|
|
2978
|
-
const typeTag = typeIndices.get(typeId);
|
|
2979
|
-
if (typeTag !== void 0) {
|
|
2980
|
-
parent[idx] = [
|
|
2981
|
-
"if",
|
|
2982
|
-
["result", "i32"],
|
|
2983
|
-
["i32.eqz", val],
|
|
2984
|
-
["then", ["i32.const", 0]],
|
|
2985
|
-
// null fails test
|
|
2986
|
-
["else", ["i32.eq", ["i32.load", val], ["i32.const", typeTag]]]
|
|
2987
|
-
];
|
|
2988
|
-
}
|
|
2989
|
-
}
|
|
2990
|
-
if (node[0] === "ref.cast") {
|
|
2991
|
-
const reftype2 = node[1];
|
|
2992
|
-
let typeId = null;
|
|
2993
|
-
let allowNull = false;
|
|
2994
|
-
if (Array.isArray(reftype2) && reftype2[0] === "ref") {
|
|
2995
|
-
if (reftype2[1] === "null") {
|
|
2996
|
-
allowNull = true;
|
|
2997
|
-
typeId = reftype2[2];
|
|
2998
|
-
} else {
|
|
2999
|
-
typeId = reftype2[1];
|
|
3000
|
-
}
|
|
3001
|
-
}
|
|
3002
|
-
const val = node[2];
|
|
3003
|
-
const typeTag = typeIndices.get(typeId);
|
|
3004
|
-
if (typeTag !== void 0) {
|
|
3005
|
-
const checkLocal = genId("cast");
|
|
3006
|
-
if (allowNull) {
|
|
3007
|
-
parent[idx] = [
|
|
3008
|
-
"block",
|
|
3009
|
-
["result", "i32"],
|
|
3010
|
-
["local", checkLocal, "i32"],
|
|
3011
|
-
["local.set", checkLocal, val],
|
|
3012
|
-
[
|
|
3013
|
-
"if",
|
|
3014
|
-
[
|
|
3015
|
-
"i32.and",
|
|
3016
|
-
["i32.ne", ["local.get", checkLocal], ["i32.const", 0]],
|
|
3017
|
-
["i32.ne", ["i32.load", ["local.get", checkLocal]], ["i32.const", typeTag]]
|
|
3018
|
-
],
|
|
3019
|
-
["then", ["unreachable"]]
|
|
3020
|
-
],
|
|
3021
|
-
["local.get", checkLocal]
|
|
3022
|
-
];
|
|
3023
|
-
} else {
|
|
3024
|
-
parent[idx] = [
|
|
3025
|
-
"block",
|
|
3026
|
-
["result", "i32"],
|
|
3027
|
-
["local", checkLocal, "i32"],
|
|
3028
|
-
["local.set", checkLocal, val],
|
|
3029
|
-
[
|
|
3030
|
-
"if",
|
|
3031
|
-
[
|
|
3032
|
-
"i32.or",
|
|
3033
|
-
["i32.eqz", ["local.get", checkLocal]],
|
|
3034
|
-
["i32.ne", ["i32.load", ["local.get", checkLocal]], ["i32.const", typeTag]]
|
|
3035
|
-
],
|
|
3036
|
-
["then", ["unreachable"]]
|
|
3037
|
-
],
|
|
3038
|
-
["local.get", checkLocal]
|
|
3039
|
-
];
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
}
|
|
3043
|
-
if (node[0] === "br_on_cast") {
|
|
3044
|
-
const label = node[1];
|
|
3045
|
-
const fromType = node[2];
|
|
3046
|
-
const toType = node[3];
|
|
3047
|
-
const val = node[4];
|
|
3048
|
-
let typeId = null;
|
|
3049
|
-
if (Array.isArray(toType) && toType[0] === "ref") {
|
|
3050
|
-
typeId = toType[1] === "null" ? toType[2] : toType[1];
|
|
3051
|
-
}
|
|
3052
|
-
const typeTag = typeIndices.get(typeId);
|
|
3053
|
-
if (typeTag !== void 0) {
|
|
3054
|
-
const checkLocal = genId("brcast");
|
|
3055
|
-
parent[idx] = [
|
|
3056
|
-
"block",
|
|
3057
|
-
["result", "i32"],
|
|
3058
|
-
["local", checkLocal, "i32"],
|
|
3059
|
-
["local.set", checkLocal, val],
|
|
3060
|
-
["br_if", label, [
|
|
3061
|
-
"i32.and",
|
|
3062
|
-
["i32.ne", ["local.get", checkLocal], ["i32.const", 0]],
|
|
3063
|
-
["i32.eq", ["i32.load", ["local.get", checkLocal]], ["i32.const", typeTag]]
|
|
3064
|
-
]],
|
|
3065
|
-
["local.get", checkLocal]
|
|
3066
|
-
];
|
|
3067
|
-
}
|
|
3068
|
-
}
|
|
3069
|
-
if (node[0] === "br_on_cast_fail") {
|
|
3070
|
-
const label = node[1];
|
|
3071
|
-
const fromType = node[2];
|
|
3072
|
-
const toType = node[3];
|
|
3073
|
-
const val = node[4];
|
|
3074
|
-
let typeId = null;
|
|
3075
|
-
if (Array.isArray(toType) && toType[0] === "ref") {
|
|
3076
|
-
typeId = toType[1] === "null" ? toType[2] : toType[1];
|
|
3077
|
-
}
|
|
3078
|
-
const typeTag = typeIndices.get(typeId);
|
|
3079
|
-
if (typeTag !== void 0) {
|
|
3080
|
-
const checkLocal = genId("brfail");
|
|
3081
|
-
parent[idx] = [
|
|
3082
|
-
"block",
|
|
3083
|
-
["result", "i32"],
|
|
3084
|
-
["local", checkLocal, "i32"],
|
|
3085
|
-
["local.set", checkLocal, val],
|
|
3086
|
-
["br_if", label, [
|
|
3087
|
-
"i32.or",
|
|
3088
|
-
["i32.eqz", ["local.get", checkLocal]],
|
|
3089
|
-
["i32.ne", ["i32.load", ["local.get", checkLocal]], ["i32.const", typeTag]]
|
|
3090
|
-
]],
|
|
3091
|
-
["local.get", checkLocal]
|
|
3092
|
-
];
|
|
3093
|
-
}
|
|
3094
|
-
}
|
|
3095
|
-
});
|
|
3096
|
-
return ast;
|
|
3097
|
-
};
|
|
3098
|
-
var POLYFILLS = [
|
|
3099
|
-
["funcref", ["ref.func", "call_ref", "return_call_ref"], funcref],
|
|
3100
|
-
["sign_ext", ["i32.extend8_s", "i32.extend16_s", "i64.extend8_s", "i64.extend16_s", "i64.extend32_s"], sign_ext],
|
|
3101
|
-
["nontrapping", [
|
|
3102
|
-
"i32.trunc_sat_f32_s",
|
|
3103
|
-
"i32.trunc_sat_f32_u",
|
|
3104
|
-
"i32.trunc_sat_f64_s",
|
|
3105
|
-
"i32.trunc_sat_f64_u",
|
|
3106
|
-
"i64.trunc_sat_f32_s",
|
|
3107
|
-
"i64.trunc_sat_f32_u",
|
|
3108
|
-
"i64.trunc_sat_f64_s",
|
|
3109
|
-
"i64.trunc_sat_f64_u"
|
|
3110
|
-
], nontrapping],
|
|
3111
|
-
["bulk_memory", ["memory.copy", "memory.fill"], bulk_memory],
|
|
3112
|
-
["return_call", ["return_call", "return_call_indirect"], return_call_transform],
|
|
3113
|
-
["i31ref", ["ref.i31", "i31.get_s", "i31.get_u"], i31ref],
|
|
3114
|
-
["extended_const", ["global.get"], extended_const],
|
|
3115
|
-
// global.get in a const initializer — also detected specially
|
|
3116
|
-
["multi_value", [], multi_value],
|
|
3117
|
-
// functions with >1 result — detected by result count
|
|
3118
|
-
["gc", [
|
|
3119
|
-
"struct.new",
|
|
3120
|
-
"struct.get",
|
|
3121
|
-
"struct.set",
|
|
3122
|
-
"array.new",
|
|
3123
|
-
"array.get",
|
|
3124
|
-
"array.set",
|
|
3125
|
-
"array.len",
|
|
3126
|
-
"struct.new_default",
|
|
3127
|
-
"array.new_default",
|
|
3128
|
-
"array.new_fixed",
|
|
3129
|
-
"array.copy"
|
|
3130
|
-
], gc],
|
|
3131
|
-
["ref_cast", ["ref.test", "ref.cast", "br_on_cast", "br_on_cast_fail"], ref_cast]
|
|
3132
|
-
];
|
|
3133
|
-
var FEATURES = Object.fromEntries(POLYFILLS.map((p) => [p[0], p[1]]));
|
|
3134
|
-
var normalize2 = (opts) => {
|
|
3135
|
-
if (opts === false) return {};
|
|
3136
|
-
if (opts !== true && typeof opts !== "string") return { ...opts };
|
|
3137
|
-
const set = typeof opts === "string" ? new Set(opts.split(/\s+/).filter(Boolean)) : null;
|
|
3138
|
-
const m = {};
|
|
3139
|
-
for (const p of POLYFILLS) m[p[0]] = set ? set.has("all") || set.has(p[0]) : true;
|
|
3140
|
-
return m;
|
|
3141
|
-
};
|
|
3142
|
-
var detect = (ast) => {
|
|
3143
|
-
const used = /* @__PURE__ */ new Set();
|
|
3144
|
-
walk(ast, (node) => {
|
|
3145
|
-
if (typeof node !== "string") return;
|
|
3146
|
-
for (const p of POLYFILLS) {
|
|
3147
|
-
const ops = p[1];
|
|
3148
|
-
if (ops.some((op) => node === op || node.startsWith(op + " "))) used.add(p[0]);
|
|
3149
|
-
}
|
|
3150
|
-
});
|
|
3151
|
-
walk(ast, (node) => {
|
|
3152
|
-
if (!Array.isArray(node) || node[0] !== "global") return;
|
|
3153
|
-
for (const init of node) {
|
|
3154
|
-
if (!Array.isArray(init)) continue;
|
|
3155
|
-
if (init[0] === "i32.add" || init[0] === "i32.sub" || init[0] === "i32.mul" || init[0] === "i64.add" || init[0] === "i64.sub" || init[0] === "i64.mul") {
|
|
3156
|
-
walk(init, (inner) => {
|
|
3157
|
-
if (Array.isArray(inner) && inner[0] === "global.get") used.add("extended_const");
|
|
3158
|
-
});
|
|
3159
|
-
}
|
|
3160
|
-
}
|
|
3161
|
-
});
|
|
3162
|
-
walk(ast, (node) => {
|
|
3163
|
-
if (!Array.isArray(node) || node[0] !== "func") return;
|
|
3164
|
-
let resultCount = 0;
|
|
3165
|
-
for (const part of node) {
|
|
3166
|
-
if (Array.isArray(part) && part[0] === "result") resultCount += part.length - 1;
|
|
2338
|
+
let child = walk(node[i], fn);
|
|
2339
|
+
if (child?._splice) node.splice(i, 1, ...child), i += child.length - 1;
|
|
2340
|
+
else node[i] = child;
|
|
3167
2341
|
}
|
|
3168
|
-
if (resultCount > 1) used.add("multi_value");
|
|
3169
|
-
});
|
|
3170
|
-
return used;
|
|
3171
|
-
};
|
|
3172
|
-
function polyfill(ast, opts = true) {
|
|
3173
|
-
if (typeof ast === "string") ast = parse_default(ast);
|
|
3174
|
-
ast = clone(ast);
|
|
3175
|
-
opts = normalize2(opts);
|
|
3176
|
-
const used = detect(ast);
|
|
3177
|
-
const ctx = { uid: 0 };
|
|
3178
|
-
for (const p of POLYFILLS) {
|
|
3179
|
-
const fn = p[2];
|
|
3180
|
-
if (used.has(p[0]) && opts[p[0]] !== false) ast = fn(ast, ctx);
|
|
3181
2342
|
}
|
|
3182
|
-
return
|
|
3183
|
-
}
|
|
3184
|
-
|
|
3185
|
-
// src/optimize.js
|
|
3186
|
-
var MAX_PROP_ROUNDS = 6;
|
|
3187
|
-
var MAX_INLINE_ROUNDS = 16;
|
|
3188
|
-
var clone2 = (node) => Array.isArray(node) ? node.map(clone2) : node;
|
|
3189
|
-
var walk2 = (node, fn, parent, idx) => {
|
|
3190
|
-
fn(node, parent, idx);
|
|
3191
|
-
if (Array.isArray(node)) for (let i = 0; i < node.length; i++) walk2(node[i], fn, node, i);
|
|
3192
|
-
};
|
|
3193
|
-
var walkPost2 = (node, fn, parent, idx) => {
|
|
3194
|
-
if (Array.isArray(node)) for (let i = 0; i < node.length; i++) walkPost2(node[i], fn, node, i);
|
|
3195
|
-
const result = fn(node, parent, idx);
|
|
3196
|
-
if (result !== void 0 && parent) parent[idx] = result;
|
|
3197
|
-
return result !== void 0 ? result : node;
|
|
3198
|
-
};
|
|
3199
|
-
var resultType2 = (op) => {
|
|
3200
|
-
if (typeof op !== "string") return null;
|
|
3201
|
-
const dot = op.indexOf(".");
|
|
3202
|
-
if (dot < 0) return null;
|
|
3203
|
-
const prefix = op.slice(0, dot);
|
|
3204
|
-
const scalar = prefix === "i32" || prefix === "i64" || prefix === "f32" || prefix === "f64";
|
|
3205
|
-
if (scalar && /^(eqz?|ne|[lg][te])(_[su])?$/.test(op.slice(dot + 1))) return "i32";
|
|
3206
|
-
if (scalar || prefix === "v128") return prefix;
|
|
3207
|
-
if (op === "memory.size" || op === "memory.grow") return "i32";
|
|
3208
|
-
return null;
|
|
3209
|
-
};
|
|
3210
|
-
var binarySize = (ast) => {
|
|
3211
|
-
try {
|
|
3212
|
-
return size(ast);
|
|
3213
|
-
} catch {
|
|
3214
|
-
return Infinity;
|
|
3215
|
-
}
|
|
3216
|
-
};
|
|
3217
|
-
var equal = (a, b) => {
|
|
3218
|
-
if (a === b) return true;
|
|
3219
|
-
if (typeof a !== typeof b) return false;
|
|
3220
|
-
if (typeof a === "bigint") return a === b;
|
|
3221
|
-
if (!Array.isArray(a) || !Array.isArray(b)) return false;
|
|
3222
|
-
if (a.length !== b.length) return false;
|
|
3223
|
-
for (let i = 0; i < a.length; i++) if (!equal(a[i], b[i])) return false;
|
|
3224
|
-
return true;
|
|
3225
|
-
};
|
|
3226
|
-
var parseIf = (node) => {
|
|
3227
|
-
let condIdx = 1;
|
|
3228
|
-
while (condIdx < node.length) {
|
|
3229
|
-
const c = node[condIdx];
|
|
3230
|
-
if (Array.isArray(c) && (c[0] === "then" || c[0] === "else" || c[0] === "result" || c[0] === "param")) {
|
|
3231
|
-
condIdx++;
|
|
3232
|
-
continue;
|
|
3233
|
-
}
|
|
3234
|
-
break;
|
|
3235
|
-
}
|
|
3236
|
-
let thenBranch = null, elseBranch = null;
|
|
3237
|
-
for (let i = condIdx + 1; i < node.length; i++) {
|
|
3238
|
-
const c = node[i];
|
|
3239
|
-
if (!Array.isArray(c)) continue;
|
|
3240
|
-
if (c[0] === "then") thenBranch = c;
|
|
3241
|
-
else if (c[0] === "else") elseBranch = c;
|
|
3242
|
-
}
|
|
3243
|
-
return { condIdx, cond: node[condIdx], thenBranch, elseBranch };
|
|
3244
|
-
};
|
|
3245
|
-
var treeshake = (ast) => {
|
|
3246
|
-
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
3247
|
-
const funcs = /* @__PURE__ */ new Map(), globals2 = /* @__PURE__ */ new Map(), types = /* @__PURE__ */ new Map();
|
|
3248
|
-
const tables = /* @__PURE__ */ new Map(), memories = /* @__PURE__ */ new Map();
|
|
3249
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
3250
|
-
const register = (map, node, idx, isImport = false) => {
|
|
3251
|
-
const named = typeof node[1] === "string" && node[1][0] === "$";
|
|
3252
|
-
const name2 = named ? node[1] : idx;
|
|
3253
|
-
const inlineExported = !isImport && node.some((s) => Array.isArray(s) && s[0] === "export");
|
|
3254
|
-
const entry = { node, idx, used: inlineExported, isImport };
|
|
3255
|
-
map.set(name2, entry);
|
|
3256
|
-
if (named) map.set(idx, entry);
|
|
3257
|
-
nodeMap.set(node, entry);
|
|
3258
|
-
return entry;
|
|
3259
|
-
};
|
|
3260
|
-
let funcIdx = 0, globalIdx = 0, typeIdx = 0, tableIdx = 0, memIdx = 0;
|
|
3261
|
-
const elems = [], data = [], exports = [], starts = [];
|
|
3262
|
-
for (const node of ast.slice(1)) {
|
|
3263
|
-
if (!Array.isArray(node)) continue;
|
|
3264
|
-
const kind = node[0];
|
|
3265
|
-
if (kind === "type") register(types, node, typeIdx++);
|
|
3266
|
-
else if (kind === "func") register(funcs, node, funcIdx++);
|
|
3267
|
-
else if (kind === "global") register(globals2, node, globalIdx++);
|
|
3268
|
-
else if (kind === "table") register(tables, node, tableIdx++);
|
|
3269
|
-
else if (kind === "memory") register(memories, node, memIdx++);
|
|
3270
|
-
else if (kind === "import") {
|
|
3271
|
-
for (const sub of node) {
|
|
3272
|
-
if (!Array.isArray(sub)) continue;
|
|
3273
|
-
if (sub[0] === "func") register(funcs, sub, funcIdx++, true);
|
|
3274
|
-
else if (sub[0] === "global") register(globals2, sub, globalIdx++, true);
|
|
3275
|
-
else if (sub[0] === "table") register(tables, sub, tableIdx++, true);
|
|
3276
|
-
else if (sub[0] === "memory") register(memories, sub, memIdx++, true);
|
|
3277
|
-
}
|
|
3278
|
-
} else if (kind === "export") exports.push(node);
|
|
3279
|
-
else if (kind === "start") starts.push(node);
|
|
3280
|
-
else if (kind === "elem") elems.push(node);
|
|
3281
|
-
else if (kind === "data") data.push(node);
|
|
3282
|
-
}
|
|
3283
|
-
const work = [];
|
|
3284
|
-
const enqueue = (entry) => {
|
|
3285
|
-
if (entry && !entry.scanned) work.push(entry);
|
|
3286
|
-
};
|
|
3287
|
-
const markFunc = (ref) => {
|
|
3288
|
-
const e = funcs.get(ref);
|
|
3289
|
-
if (!e) return;
|
|
3290
|
-
if (!e.used) e.used = true;
|
|
3291
|
-
enqueue(e);
|
|
3292
|
-
};
|
|
3293
|
-
const markGlobal = (ref) => {
|
|
3294
|
-
const e = globals2.get(ref);
|
|
3295
|
-
if (e) e.used = true;
|
|
3296
|
-
};
|
|
3297
|
-
const markTable = (ref) => {
|
|
3298
|
-
const e = tables.get(ref);
|
|
3299
|
-
if (e) e.used = true;
|
|
3300
|
-
};
|
|
3301
|
-
const markMemory = (ref) => {
|
|
3302
|
-
if (typeof ref === "string" && ref[0] !== "$") ref = +ref;
|
|
3303
|
-
const e = memories.get(ref);
|
|
3304
|
-
if (e) e.used = true;
|
|
3305
|
-
};
|
|
3306
|
-
const markType = (ref) => {
|
|
3307
|
-
const e = types.get(ref);
|
|
3308
|
-
if (e) e.used = true;
|
|
3309
|
-
};
|
|
3310
|
-
for (const exp of exports) {
|
|
3311
|
-
for (const sub of exp) {
|
|
3312
|
-
if (!Array.isArray(sub)) continue;
|
|
3313
|
-
const [kind, ref] = sub;
|
|
3314
|
-
if (kind === "func") markFunc(ref);
|
|
3315
|
-
else if (kind === "global") markGlobal(ref);
|
|
3316
|
-
else if (kind === "table") markTable(ref);
|
|
3317
|
-
else if (kind === "memory") markMemory(ref);
|
|
3318
|
-
}
|
|
3319
|
-
}
|
|
3320
|
-
for (const start of starts) {
|
|
3321
|
-
let ref = start[1];
|
|
3322
|
-
if (typeof ref === "string" && ref[0] !== "$") ref = +ref;
|
|
3323
|
-
markFunc(ref);
|
|
3324
|
-
}
|
|
3325
|
-
for (const elem of elems) {
|
|
3326
|
-
walk2(elem, (n) => {
|
|
3327
|
-
if (Array.isArray(n) && n[0] === "ref.func") markFunc(n[1]);
|
|
3328
|
-
else if (typeof n === "string" && n[0] === "$") markFunc(n);
|
|
3329
|
-
});
|
|
3330
|
-
}
|
|
3331
|
-
for (const d of data) {
|
|
3332
|
-
const first = d[1];
|
|
3333
|
-
if (Array.isArray(first) && first[0] === "memory") markMemory(first[1]);
|
|
3334
|
-
else if (typeof first === "string" && first[0] === "$") markMemory(first);
|
|
3335
|
-
else if (Array.isArray(first)) markMemory(0);
|
|
3336
|
-
}
|
|
3337
|
-
for (const m of [funcs, globals2, tables, memories]) for (const e of m.values()) if (e.used) enqueue(e);
|
|
3338
|
-
const hasAnchor = exports.length > 0 || starts.length > 0 || elems.length > 0 || work.length > 0;
|
|
3339
|
-
if (!hasAnchor) {
|
|
3340
|
-
for (const m of [funcs, globals2, tables, memories]) for (const e of m.values()) e.used = true;
|
|
3341
|
-
return ast;
|
|
3342
|
-
}
|
|
3343
|
-
while (work.length) {
|
|
3344
|
-
const entry = work.pop();
|
|
3345
|
-
if (entry.scanned) continue;
|
|
3346
|
-
entry.scanned = true;
|
|
3347
|
-
if (entry.isImport) continue;
|
|
3348
|
-
walk2(entry.node, (n) => {
|
|
3349
|
-
if (!Array.isArray(n)) {
|
|
3350
|
-
if (typeof n === "string" && n[0] === "$") markFunc(n);
|
|
3351
|
-
return;
|
|
3352
|
-
}
|
|
3353
|
-
const [op, ref] = n;
|
|
3354
|
-
if (op === "call" || op === "return_call" || op === "ref.func") markFunc(ref);
|
|
3355
|
-
else if (op === "global.get" || op === "global.set") markGlobal(ref);
|
|
3356
|
-
else if (op === "type") markType(ref);
|
|
3357
|
-
else if (op === "call_indirect" || op === "return_call_indirect") {
|
|
3358
|
-
for (const sub of n) if (typeof sub === "string" && sub[0] === "$") markTable(sub);
|
|
3359
|
-
}
|
|
3360
|
-
if (typeof op === "string" && (op.startsWith("memory.") || op.includes(".load") || op.includes(".store"))) {
|
|
3361
|
-
markMemory(0);
|
|
3362
|
-
}
|
|
3363
|
-
});
|
|
3364
|
-
}
|
|
3365
|
-
const result = ["module"];
|
|
3366
|
-
for (const node of ast.slice(1)) {
|
|
3367
|
-
if (!Array.isArray(node)) {
|
|
3368
|
-
result.push(node);
|
|
3369
|
-
continue;
|
|
3370
|
-
}
|
|
3371
|
-
const kind = node[0];
|
|
3372
|
-
if (kind === "func" || kind === "global" || kind === "type") {
|
|
3373
|
-
if (nodeMap.get(node)?.used) result.push(node);
|
|
3374
|
-
} else if (kind === "import") {
|
|
3375
|
-
let used = false;
|
|
3376
|
-
for (const sub of node) {
|
|
3377
|
-
if (!Array.isArray(sub)) continue;
|
|
3378
|
-
const e = nodeMap.get(sub);
|
|
3379
|
-
if (e?.used) {
|
|
3380
|
-
used = true;
|
|
3381
|
-
break;
|
|
3382
|
-
}
|
|
3383
|
-
}
|
|
3384
|
-
if (used) result.push(node);
|
|
3385
|
-
} else {
|
|
3386
|
-
result.push(node);
|
|
3387
|
-
}
|
|
3388
|
-
}
|
|
3389
|
-
return result;
|
|
3390
|
-
};
|
|
3391
|
-
var roundEven = (x) => x - Math.floor(x) !== 0.5 ? Math.round(x) : 2 * Math.round(x / 2);
|
|
3392
|
-
var _rb8 = new ArrayBuffer(8);
|
|
3393
|
-
var _rf64 = new Float64Array(_rb8);
|
|
3394
|
-
var _ru32 = new Uint32Array(_rb8);
|
|
3395
|
-
var _rb4 = new ArrayBuffer(4);
|
|
3396
|
-
var _rf32 = new Float32Array(_rb4);
|
|
3397
|
-
var _ri32 = new Int32Array(_rb4);
|
|
3398
|
-
var _hex8 = (u) => (u >>> 0).toString(16).padStart(8, "0");
|
|
3399
|
-
var _twosComp16 = (mag) => {
|
|
3400
|
-
let out = "", carry = 1;
|
|
3401
|
-
for (let i = 15; i >= 0; i--) {
|
|
3402
|
-
const d = 15 - parseInt(mag[i], 16) + carry;
|
|
3403
|
-
out = (d & 15).toString(16) + out;
|
|
3404
|
-
carry = d >> 4;
|
|
3405
|
-
}
|
|
3406
|
-
return out;
|
|
3407
|
-
};
|
|
3408
|
-
var _i64Hex16 = (v) => {
|
|
3409
|
-
const h = v.toString(16);
|
|
3410
|
-
return h[0] === "-" ? _twosComp16(h.slice(1).padStart(16, "0")) : h.padStart(16, "0");
|
|
3411
|
-
};
|
|
3412
|
-
var ZERO64 = "0x0000000000000000";
|
|
3413
|
-
var ONE64 = "0x0000000000000001";
|
|
3414
|
-
var NEG164 = "0xffffffffffffffff";
|
|
3415
|
-
var _i64Canon = (val) => {
|
|
3416
|
-
if (typeof val === "string") {
|
|
3417
|
-
const s = val.replaceAll("_", "");
|
|
3418
|
-
if (s.length === 18 && s[1] === "x") return "0x" + s.slice(2).toLowerCase();
|
|
3419
|
-
const neg = s[0] === "-", mag = s[0] === "-" || s[0] === "+" ? s.slice(1) : s;
|
|
3420
|
-
return "0x" + _i64Hex16(neg ? -BigInt(mag) : BigInt(mag));
|
|
3421
|
-
}
|
|
3422
|
-
if (typeof val === "bigint") return "0x" + _i64Hex16(BigInt(String(val)));
|
|
3423
|
-
return "0x" + _i64Hex16(BigInt(Math.trunc(val) || 0));
|
|
3424
|
-
};
|
|
3425
|
-
var _sb = (h) => (parseInt(h[2], 16) ^ 8).toString(16) + h.slice(3);
|
|
3426
|
-
var _i64Lo = (h) => parseInt(h.slice(10), 16) | 0;
|
|
3427
|
-
var _i64HiU = (h) => parseInt(h.slice(2, 10), 16) >>> 0;
|
|
3428
|
-
var _i64Arith = (r) => r == null ? null : "0x" + _i64Hex16(r);
|
|
3429
|
-
var _sgn = (h) => {
|
|
3430
|
-
let v = BigInt(h);
|
|
3431
|
-
if (v > 0x7fffffffffffffffn) v = v - 0x8000000000000000n - 0x8000000000000000n;
|
|
3432
|
-
return v;
|
|
3433
|
-
};
|
|
3434
|
-
var i64FromF64 = (x) => {
|
|
3435
|
-
_rf64[0] = x;
|
|
3436
|
-
return "0x" + _hex8(_ru32[1]) + _hex8(_ru32[0]);
|
|
3437
|
-
};
|
|
3438
|
-
var f64FromI64 = (h) => {
|
|
3439
|
-
_ru32[1] = parseInt(h.slice(2, 10), 16);
|
|
3440
|
-
_ru32[0] = parseInt(h.slice(10), 16);
|
|
3441
|
-
return _rf64[0];
|
|
3442
|
-
};
|
|
3443
|
-
var i32FromF32 = (x) => {
|
|
3444
|
-
_rf32[0] = x;
|
|
3445
|
-
return _ri32[0];
|
|
3446
|
-
};
|
|
3447
|
-
var f32FromI32 = (x) => {
|
|
3448
|
-
_ri32[0] = x | 0;
|
|
3449
|
-
return _rf32[0];
|
|
3450
|
-
};
|
|
3451
|
-
var i32c = (fn) => (a, b) => fn(a, b) ? 1 : 0;
|
|
3452
|
-
var u32c = (fn) => (a, b) => fn(a >>> 0, b >>> 0) ? 1 : 0;
|
|
3453
|
-
var i64c = (fn) => (a, b) => fn(_sb(a), _sb(b)) ? 1 : 0;
|
|
3454
|
-
var u64c = (fn) => (a, b) => fn(a, b) ? 1 : 0;
|
|
3455
|
-
var FOLDABLE = {
|
|
3456
|
-
// i32 arithmetic
|
|
3457
|
-
"i32.add": (a, b) => a + b | 0,
|
|
3458
|
-
"i32.sub": (a, b) => a - b | 0,
|
|
3459
|
-
"i32.mul": (a, b) => Math.imul(a, b),
|
|
3460
|
-
"i32.div_s": (a, b) => b !== 0 ? a / b | 0 : null,
|
|
3461
|
-
"i32.div_u": (a, b) => b !== 0 ? (a >>> 0) / (b >>> 0) | 0 : null,
|
|
3462
|
-
"i32.rem_s": (a, b) => b !== 0 ? a % b | 0 : null,
|
|
3463
|
-
"i32.rem_u": (a, b) => b !== 0 ? (a >>> 0) % (b >>> 0) | 0 : null,
|
|
3464
|
-
"i32.and": (a, b) => a & b,
|
|
3465
|
-
"i32.or": (a, b) => a | b,
|
|
3466
|
-
"i32.xor": (a, b) => a ^ b,
|
|
3467
|
-
"i32.shl": (a, b) => a << (b & 31),
|
|
3468
|
-
"i32.shr_s": (a, b) => a >> (b & 31),
|
|
3469
|
-
"i32.shr_u": (a, b) => a >>> (b & 31),
|
|
3470
|
-
"i32.rotl": (a, b) => {
|
|
3471
|
-
b &= 31;
|
|
3472
|
-
return a << b | a >>> 32 - b | 0;
|
|
3473
|
-
},
|
|
3474
|
-
"i32.rotr": (a, b) => {
|
|
3475
|
-
b &= 31;
|
|
3476
|
-
return a >>> b | a << 32 - b | 0;
|
|
3477
|
-
},
|
|
3478
|
-
"i32.eq": i32c((a, b) => a === b),
|
|
3479
|
-
"i32.ne": i32c((a, b) => a !== b),
|
|
3480
|
-
"i32.lt_s": i32c((a, b) => a < b),
|
|
3481
|
-
"i32.lt_u": u32c((a, b) => a < b),
|
|
3482
|
-
"i32.gt_s": i32c((a, b) => a > b),
|
|
3483
|
-
"i32.gt_u": u32c((a, b) => a > b),
|
|
3484
|
-
"i32.le_s": i32c((a, b) => a <= b),
|
|
3485
|
-
"i32.le_u": u32c((a, b) => a <= b),
|
|
3486
|
-
"i32.ge_s": i32c((a, b) => a >= b),
|
|
3487
|
-
"i32.ge_u": u32c((a, b) => a >= b),
|
|
3488
|
-
"i32.eqz": (a) => a === 0 ? 1 : 0,
|
|
3489
|
-
"i32.clz": (a) => Math.clz32(a),
|
|
3490
|
-
"i32.ctz": (a) => a === 0 ? 32 : 31 - Math.clz32(a & -a),
|
|
3491
|
-
"i32.popcnt": (a) => {
|
|
3492
|
-
let c = 0;
|
|
3493
|
-
while (a) {
|
|
3494
|
-
c += a & 1;
|
|
3495
|
-
a >>>= 1;
|
|
3496
|
-
}
|
|
3497
|
-
return c;
|
|
3498
|
-
},
|
|
3499
|
-
"i32.wrap_i64": (a) => _i64Lo(a),
|
|
3500
|
-
"i32.extend8_s": (a) => a << 24 >> 24,
|
|
3501
|
-
"i32.extend16_s": (a) => a << 16 >> 16,
|
|
3502
|
-
// i64 — hex-string in, hex-string out, BOTH-WORLDS-EXACT arithmetic.
|
|
3503
|
-
// BigInts construct locally (in-expression — kernel kind erasure never
|
|
3504
|
-
// applies), but two further kernel facts shape every folder:
|
|
3505
|
-
// (1) the kernel's BigInt is the mod-2^64 i64 CARRIER: BigInt('0xffff…')
|
|
3506
|
-
// arrives NEGATIVE there, so sign-sensitive ops (>>, /, %, unsigned
|
|
3507
|
-
// division) diverge unless the value is sign-canonicalized first;
|
|
3508
|
-
// (2) BigInt.asIntN/asUintN are unfaithful in-kernel — never used.
|
|
3509
|
-
// Ring ops {+,−,×,&,|,^,<<} are mod-2^64-compatible: compute then mask with
|
|
3510
|
-
// `& 0xffffffffffffffffn` (native: the wrap; kernel: AND with −1 ≡ no-op).
|
|
3511
|
-
// `_sgn` yields the SIGNED value in both worlds (the subtract arm is dead
|
|
3512
|
-
// in-kernel — same dead-arm trick as slebSize). shr_u is pure u32-half
|
|
3513
|
-
// number math. div_u/rem_u fold only below 2^63 (signed==unsigned there);
|
|
3514
|
-
// above, they skip — sound degradation, never a wrong constant.
|
|
3515
|
-
"i64.add": (a, b) => _i64Arith(BigInt(a) + BigInt(b) & 0xffffffffffffffffn),
|
|
3516
|
-
"i64.sub": (a, b) => _i64Arith(BigInt(a) - BigInt(b) & 0xffffffffffffffffn),
|
|
3517
|
-
"i64.mul": (a, b) => _i64Arith(BigInt(a) * BigInt(b) & 0xffffffffffffffffn),
|
|
3518
|
-
"i64.div_s": (a, b) => b !== ZERO64 && !(a === "0x8000000000000000" && b === NEG164) ? _i64Arith(_sgn(a) / _sgn(b) & 0xffffffffffffffffn) : null,
|
|
3519
|
-
"i64.div_u": (a, b) => b !== ZERO64 && !(_i64HiU(a) >>> 31) && !(_i64HiU(b) >>> 31) ? _i64Arith(BigInt(a) / BigInt(b)) : null,
|
|
3520
|
-
"i64.rem_s": (a, b) => b !== ZERO64 ? _i64Arith(_sgn(a) % _sgn(b) & 0xffffffffffffffffn) : null,
|
|
3521
|
-
"i64.rem_u": (a, b) => b !== ZERO64 && !(_i64HiU(a) >>> 31) && !(_i64HiU(b) >>> 31) ? _i64Arith(BigInt(a) % BigInt(b)) : null,
|
|
3522
|
-
"i64.and": (a, b) => _i64Arith(BigInt(a) & BigInt(b) & 0xffffffffffffffffn),
|
|
3523
|
-
"i64.or": (a, b) => _i64Arith((BigInt(a) | BigInt(b)) & 0xffffffffffffffffn),
|
|
3524
|
-
"i64.xor": (a, b) => _i64Arith((BigInt(a) ^ BigInt(b)) & 0xffffffffffffffffn),
|
|
3525
|
-
"i64.shl": (a, b) => _i64Arith(BigInt(a) << (BigInt(b) & 63n) & 0xffffffffffffffffn),
|
|
3526
|
-
"i64.shr_s": (a, b) => _i64Arith(_sgn(a) >> (BigInt(b) & 63n) & 0xffffffffffffffffn),
|
|
3527
|
-
"i64.shr_u": (a, b) => {
|
|
3528
|
-
const s = parseInt(b.slice(10), 16) & 63;
|
|
3529
|
-
const hi = _i64HiU(a), lo = parseInt(a.slice(10), 16) >>> 0;
|
|
3530
|
-
const rh = s >= 32 ? 0 : hi >>> s;
|
|
3531
|
-
const rl = s === 0 ? lo : s >= 32 ? hi >>> s - 32 : (lo >>> s | hi << 32 - s) >>> 0;
|
|
3532
|
-
return "0x" + _hex8(rh) + _hex8(rl);
|
|
3533
|
-
},
|
|
3534
|
-
"i64.eq": (a, b) => a === b ? 1 : 0,
|
|
3535
|
-
"i64.ne": (a, b) => a !== b ? 1 : 0,
|
|
3536
|
-
"i64.lt_s": i64c((a, b) => a < b),
|
|
3537
|
-
"i64.lt_u": u64c((a, b) => a < b),
|
|
3538
|
-
"i64.gt_s": i64c((a, b) => a > b),
|
|
3539
|
-
"i64.gt_u": u64c((a, b) => a > b),
|
|
3540
|
-
"i64.le_s": i64c((a, b) => a <= b),
|
|
3541
|
-
"i64.le_u": u64c((a, b) => a <= b),
|
|
3542
|
-
"i64.ge_s": i64c((a, b) => a >= b),
|
|
3543
|
-
"i64.ge_u": u64c((a, b) => a >= b),
|
|
3544
|
-
"i64.eqz": (a) => a === ZERO64 ? 1 : 0,
|
|
3545
|
-
"i64.extend_i32_s": (a) => "0x" + _hex8(a >> 31) + _hex8(a),
|
|
3546
|
-
"i64.extend_i32_u": (a) => "0x00000000" + _hex8(a),
|
|
3547
|
-
"i64.extend8_s": (a) => {
|
|
3548
|
-
const v = _i64Lo(a) << 24 >> 24;
|
|
3549
|
-
return "0x" + _hex8(v >> 31) + _hex8(v);
|
|
3550
|
-
},
|
|
3551
|
-
"i64.extend16_s": (a) => {
|
|
3552
|
-
const v = _i64Lo(a) << 16 >> 16;
|
|
3553
|
-
return "0x" + _hex8(v >> 31) + _hex8(v);
|
|
3554
|
-
},
|
|
3555
|
-
"i64.extend32_s": (a) => {
|
|
3556
|
-
const v = _i64Lo(a);
|
|
3557
|
-
return "0x" + _hex8(v >> 31) + _hex8(v);
|
|
3558
|
-
},
|
|
3559
|
-
// f32/f64 (NaN/precision-aware via Math.fround)
|
|
3560
|
-
"f32.add": (a, b) => Math.fround(a + b),
|
|
3561
|
-
"f32.sub": (a, b) => Math.fround(a - b),
|
|
3562
|
-
"f32.mul": (a, b) => Math.fround(a * b),
|
|
3563
|
-
"f32.div": (a, b) => Math.fround(a / b),
|
|
3564
|
-
"f32.neg": (a) => Math.fround(-a),
|
|
3565
|
-
"f32.abs": (a) => Math.fround(Math.abs(a)),
|
|
3566
|
-
"f32.sqrt": (a) => Math.fround(Math.sqrt(a)),
|
|
3567
|
-
"f32.ceil": (a) => Math.fround(Math.ceil(a)),
|
|
3568
|
-
"f32.floor": (a) => Math.fround(Math.floor(a)),
|
|
3569
|
-
"f32.trunc": (a) => Math.fround(Math.trunc(a)),
|
|
3570
|
-
"f32.nearest": (a) => Math.fround(roundEven(a)),
|
|
3571
|
-
"f64.add": (a, b) => a + b,
|
|
3572
|
-
"f64.sub": (a, b) => a - b,
|
|
3573
|
-
"f64.mul": (a, b) => a * b,
|
|
3574
|
-
"f64.div": (a, b) => a / b,
|
|
3575
|
-
"f64.neg": (a) => -a,
|
|
3576
|
-
"f64.abs": Math.abs,
|
|
3577
|
-
"f64.sqrt": Math.sqrt,
|
|
3578
|
-
"f64.ceil": Math.ceil,
|
|
3579
|
-
"f64.floor": Math.floor,
|
|
3580
|
-
"f64.trunc": Math.trunc,
|
|
3581
|
-
"f64.nearest": roundEven,
|
|
3582
|
-
// Bit-exact reinterprets (preserve NaN payloads)
|
|
3583
|
-
"i32.reinterpret_f32": i32FromF32,
|
|
3584
|
-
"f32.reinterpret_i32": f32FromI32,
|
|
3585
|
-
"i64.reinterpret_f64": i64FromF64,
|
|
3586
|
-
"f64.reinterpret_i64": f64FromI64,
|
|
3587
|
-
// Numeric conversions (value-preserving where representable)
|
|
3588
|
-
"f32.convert_i32_s": (a) => Math.fround(a | 0),
|
|
3589
|
-
"f32.convert_i32_u": (a) => Math.fround(a >>> 0),
|
|
3590
|
-
// (hi|0)·2^32 + lo is the exact signed value with ONE rounding at the add —
|
|
3591
|
-
// correct f64 conversion semantics, pure number math (kernel-safe).
|
|
3592
|
-
"f32.convert_i64_s": (a) => Math.fround((_i64HiU(a) | 0) * 4294967296 + parseInt(a.slice(10), 16)),
|
|
3593
|
-
"f32.convert_i64_u": (a) => Math.fround(_i64HiU(a) * 4294967296 + parseInt(a.slice(10), 16)),
|
|
3594
|
-
"f64.convert_i32_s": (a) => a | 0,
|
|
3595
|
-
"f64.convert_i32_u": (a) => a >>> 0,
|
|
3596
|
-
"f64.convert_i64_s": (a) => (_i64HiU(a) | 0) * 4294967296 + parseInt(a.slice(10), 16),
|
|
3597
|
-
"f64.convert_i64_u": (a) => _i64HiU(a) * 4294967296 + parseInt(a.slice(10), 16),
|
|
3598
|
-
"f32.demote_f64": (a) => Math.fround(a),
|
|
3599
|
-
"f64.promote_f32": (a) => Math.fround(a)
|
|
3600
|
-
};
|
|
3601
|
-
var _nanBitsHex = (s) => {
|
|
3602
|
-
const i = s?.indexOf?.("nan");
|
|
3603
|
-
if (i < 0 || i == null) return null;
|
|
3604
|
-
const tail = s.slice(i + 4).replaceAll("_", "");
|
|
3605
|
-
const payload = s[i + 3] === ":" && tail !== "canonical" && tail !== "arithmetic" ? BigInt(tail) : 0x8000000000000n;
|
|
3606
|
-
const h = payload.toString(16).padStart(16, "0");
|
|
3607
|
-
const hi = (parseInt(h.slice(0, 8), 16) | 2146435072 | (s[0] === "-" ? 2147483648 : 0)) >>> 0;
|
|
3608
|
-
return "0x" + _hex8(hi) + h.slice(8);
|
|
3609
|
-
};
|
|
3610
|
-
var _parseNanF64 = (s, i = s?.indexOf?.("nan")) => {
|
|
3611
|
-
if (i < 0 || i == null) return null;
|
|
3612
|
-
const tail = s.slice(i + 4).replaceAll("_", "");
|
|
3613
|
-
const payload = s[i + 3] === ":" && tail !== "canonical" && tail !== "arithmetic" ? BigInt(tail) : 0x8000000000000n;
|
|
3614
|
-
const h = payload.toString(16).padStart(16, "0");
|
|
3615
|
-
_ru32[1] = (parseInt(h.slice(0, 8), 16) | 2146435072 | (s[0] === "-" ? 2147483648 : 0)) >>> 0;
|
|
3616
|
-
_ru32[0] = parseInt(h.slice(8), 16);
|
|
3617
|
-
return _rf64[0];
|
|
3618
|
-
};
|
|
3619
|
-
var _parseNanF32 = (s, i = s?.indexOf?.("nan")) => {
|
|
3620
|
-
if (i < 0 || i == null) return null;
|
|
3621
|
-
let tail = s.slice(i + 4).replaceAll("_", ""), bits = s[i + 3] === ":" && tail !== "canonical" && tail !== "arithmetic" ? parseInt(tail) : 4194304;
|
|
3622
|
-
_ri32[0] = bits | 2139095040 | (s[0] === "-" ? 2147483648 : 0) | 0;
|
|
3623
|
-
return _rf32[0];
|
|
3624
|
-
};
|
|
3625
|
-
var getConst = (node) => {
|
|
3626
|
-
if (!Array.isArray(node) || node.length !== 2) return null;
|
|
3627
|
-
const [op, val] = node;
|
|
3628
|
-
if (op === "i32.const") return { type: "i32", value: (typeof val === "string" ? parseInt(val.replaceAll("_", "")) : val) | 0 };
|
|
3629
|
-
if (op === "i64.const") return { type: "i64", value: _i64Canon(val) };
|
|
3630
|
-
if (op === "f32.const") {
|
|
3631
|
-
const n = _parseNanF32(val);
|
|
3632
|
-
return { type: "f32", value: n !== null ? n : Math.fround(Number(val)) };
|
|
3633
|
-
}
|
|
3634
|
-
if (op === "f64.const") {
|
|
3635
|
-
const n = _parseNanF64(val);
|
|
3636
|
-
const v = n !== null ? n : Number(val);
|
|
3637
|
-
return { type: "f64", value: Number.isNaN(v) ? NaN : v };
|
|
3638
|
-
}
|
|
3639
|
-
return null;
|
|
3640
|
-
};
|
|
3641
|
-
var makeConst = (type, value) => {
|
|
3642
|
-
if (type === "i32") return ["i32.const", value | 0];
|
|
3643
|
-
if (type === "i64") return ["i64.const", typeof value === "number" ? value : _i64Canon(value)];
|
|
3644
|
-
if (type === "f32") {
|
|
3645
|
-
const v = Math.fround(value);
|
|
3646
|
-
return ["f32.const", Number.isNaN(v) ? "nan" : v];
|
|
3647
|
-
}
|
|
3648
|
-
if (type === "f64") return ["f64.const", Number.isNaN(value) ? "nan" : value];
|
|
3649
|
-
return null;
|
|
3650
|
-
};
|
|
3651
|
-
var fold = (ast) => {
|
|
3652
|
-
return walkPost2(ast, (node) => {
|
|
3653
|
-
if (!Array.isArray(node)) return;
|
|
3654
|
-
const fn = FOLDABLE[node[0]];
|
|
3655
|
-
if (!fn) return;
|
|
3656
|
-
if (node.length === 2) {
|
|
3657
|
-
if (node[0] === "i64.reinterpret_f64") {
|
|
3658
|
-
const inner = node[1];
|
|
3659
|
-
if (Array.isArray(inner) && inner.length === 2 && inner[0] === "f64.const" && typeof inner[1] === "string") {
|
|
3660
|
-
const bits = _nanBitsHex(inner[1]);
|
|
3661
|
-
if (bits) return ["i64.const", bits];
|
|
3662
|
-
}
|
|
3663
|
-
}
|
|
3664
|
-
if (node[0] === "f64.reinterpret_i64") {
|
|
3665
|
-
const c = getConst(node[1]);
|
|
3666
|
-
if (c && c.type === "i64") {
|
|
3667
|
-
const h = c.value.slice(2);
|
|
3668
|
-
const hi = parseInt(h.slice(0, 8), 16) >>> 0;
|
|
3669
|
-
const lo = parseInt(h.slice(8), 16) >>> 0;
|
|
3670
|
-
const isNaN64 = (hi & 2146435072) === 2146435072 && ((hi & 1048575) !== 0 || lo !== 0);
|
|
3671
|
-
if (isNaN64) return [
|
|
3672
|
-
"f64.const",
|
|
3673
|
-
((hi & 2147483648) !== 0 ? "-" : "") + "nan:0x" + (hi & 1048575).toString(16).padStart(5, "0") + h.slice(8)
|
|
3674
|
-
];
|
|
3675
|
-
}
|
|
3676
|
-
}
|
|
3677
|
-
const a = getConst(node[1]);
|
|
3678
|
-
if (!a) return;
|
|
3679
|
-
const r = fn(a.value);
|
|
3680
|
-
if (r === null || r === void 0) return;
|
|
3681
|
-
return makeConst(resultType2(node[0]), r);
|
|
3682
|
-
}
|
|
3683
|
-
if (node.length === 3) {
|
|
3684
|
-
const a = getConst(node[1]), b = getConst(node[2]);
|
|
3685
|
-
if (!a || !b) return;
|
|
3686
|
-
const r = fn(a.value, b.value);
|
|
3687
|
-
if (r === null || r === void 0) return;
|
|
3688
|
-
return makeConst(resultType2(node[0]), r);
|
|
3689
|
-
}
|
|
3690
|
-
});
|
|
3691
|
-
};
|
|
3692
|
-
var commutativeIdentity = (neutral) => (a, b) => {
|
|
3693
|
-
const ca = getConst(a), cb = getConst(b);
|
|
3694
|
-
if (ca?.value === neutral) return b;
|
|
3695
|
-
if (cb?.value === neutral) return a;
|
|
3696
|
-
return null;
|
|
3697
|
-
};
|
|
3698
|
-
var rightIdentity = (neutral) => (a, b) => getConst(b)?.value === neutral ? a : null;
|
|
3699
|
-
var IDENTITIES = {
|
|
3700
|
-
// x + 0 → x, 0 + x → x
|
|
3701
|
-
"i32.add": commutativeIdentity(0),
|
|
3702
|
-
"i64.add": commutativeIdentity(ZERO64),
|
|
3703
|
-
// x - 0 → x
|
|
3704
|
-
"i32.sub": rightIdentity(0),
|
|
3705
|
-
"i64.sub": rightIdentity(ZERO64),
|
|
3706
|
-
// x * 1 → x, 1 * x → x
|
|
3707
|
-
"i32.mul": commutativeIdentity(1),
|
|
3708
|
-
"i64.mul": commutativeIdentity(ONE64),
|
|
3709
|
-
// x / 1 → x
|
|
3710
|
-
"i32.div_s": rightIdentity(1),
|
|
3711
|
-
"i32.div_u": rightIdentity(1),
|
|
3712
|
-
"i64.div_s": rightIdentity(ONE64),
|
|
3713
|
-
"i64.div_u": rightIdentity(ONE64),
|
|
3714
|
-
// x & -1 → x, -1 & x → x (all bits set)
|
|
3715
|
-
"i32.and": commutativeIdentity(-1),
|
|
3716
|
-
"i64.and": commutativeIdentity(NEG164),
|
|
3717
|
-
// x | 0 → x, 0 | x → x
|
|
3718
|
-
"i32.or": commutativeIdentity(0),
|
|
3719
|
-
"i64.or": commutativeIdentity(ZERO64),
|
|
3720
|
-
// x ^ 0 → x, 0 ^ x → x
|
|
3721
|
-
"i32.xor": commutativeIdentity(0),
|
|
3722
|
-
"i64.xor": commutativeIdentity(ZERO64),
|
|
3723
|
-
// x << 0 → x, x >> 0 → x
|
|
3724
|
-
"i32.shl": rightIdentity(0),
|
|
3725
|
-
"i32.shr_s": rightIdentity(0),
|
|
3726
|
-
"i32.shr_u": rightIdentity(0),
|
|
3727
|
-
"i64.shl": rightIdentity(ZERO64),
|
|
3728
|
-
"i64.shr_s": rightIdentity(ZERO64),
|
|
3729
|
-
"i64.shr_u": rightIdentity(ZERO64)
|
|
3730
|
-
// f + 0 → x (careful with -0.0, skip for floats)
|
|
3731
|
-
// f * 1 → x (careful with NaN, skip for floats)
|
|
3732
|
-
};
|
|
3733
|
-
var identity = (ast) => {
|
|
3734
|
-
return walkPost2(ast, (node) => {
|
|
3735
|
-
if (!Array.isArray(node) || node.length !== 3) return;
|
|
3736
|
-
const fn = IDENTITIES[node[0]];
|
|
3737
|
-
if (!fn) return;
|
|
3738
|
-
const result = fn(node[1], node[2]);
|
|
3739
|
-
if (result === null) return;
|
|
3740
|
-
return result;
|
|
3741
|
-
});
|
|
3742
|
-
};
|
|
3743
|
-
var strength = (ast) => {
|
|
3744
|
-
return walkPost2(ast, (node) => {
|
|
3745
|
-
if (!Array.isArray(node) || node.length !== 3) return;
|
|
3746
|
-
const [op, a, b] = node;
|
|
3747
|
-
if (op === "i32.mul") {
|
|
3748
|
-
const cb = getConst(b);
|
|
3749
|
-
if (cb && cb.value > 0 && (cb.value & cb.value - 1) === 0) {
|
|
3750
|
-
const shift = Math.log2(cb.value);
|
|
3751
|
-
if (Number.isInteger(shift)) return ["i32.shl", a, ["i32.const", shift]];
|
|
3752
|
-
}
|
|
3753
|
-
const ca = getConst(a);
|
|
3754
|
-
if (ca && ca.value > 0 && (ca.value & ca.value - 1) === 0) {
|
|
3755
|
-
const shift = Math.log2(ca.value);
|
|
3756
|
-
if (Number.isInteger(shift)) return ["i32.shl", b, ["i32.const", shift]];
|
|
3757
|
-
}
|
|
3758
|
-
}
|
|
3759
|
-
if (op === "i64.mul") {
|
|
3760
|
-
const cb = getConst(b), vb = cb ? BigInt(cb.value) : null;
|
|
3761
|
-
if (vb != null && vb > 0n && (vb & vb - 1n) === 0n)
|
|
3762
|
-
return ["i64.shl", a, ["i64.const", vb.toString(2).length - 1]];
|
|
3763
|
-
const ca = getConst(a), va = ca ? BigInt(ca.value) : null;
|
|
3764
|
-
if (va != null && va > 0n && (va & va - 1n) === 0n)
|
|
3765
|
-
return ["i64.shl", b, ["i64.const", va.toString(2).length - 1]];
|
|
3766
|
-
}
|
|
3767
|
-
if (op === "i32.div_u") {
|
|
3768
|
-
const cb = getConst(b);
|
|
3769
|
-
if (cb && cb.value > 0 && (cb.value & cb.value - 1) === 0) {
|
|
3770
|
-
const shift = Math.log2(cb.value);
|
|
3771
|
-
if (Number.isInteger(shift)) return ["i32.shr_u", a, ["i32.const", shift]];
|
|
3772
|
-
}
|
|
3773
|
-
}
|
|
3774
|
-
if (op === "i64.div_u") {
|
|
3775
|
-
const cb = getConst(b), vb = cb ? BigInt(cb.value) : null;
|
|
3776
|
-
if (vb != null && vb > 0n && (vb & vb - 1n) === 0n)
|
|
3777
|
-
return ["i64.shr_u", a, ["i64.const", vb.toString(2).length - 1]];
|
|
3778
|
-
}
|
|
3779
|
-
if (op === "i32.rem_u") {
|
|
3780
|
-
const cb = getConst(b);
|
|
3781
|
-
if (cb && cb.value > 0 && (cb.value & cb.value - 1) === 0) {
|
|
3782
|
-
return ["i32.and", a, ["i32.const", cb.value - 1]];
|
|
3783
|
-
}
|
|
3784
|
-
}
|
|
3785
|
-
if (op === "i64.rem_u") {
|
|
3786
|
-
const cb = getConst(b), vb = cb ? BigInt(cb.value) : null;
|
|
3787
|
-
if (vb != null && vb > 0n && (vb & vb - 1n) === 0n)
|
|
3788
|
-
return ["i64.and", a, ["i64.const", "0x" + _i64Hex16(vb - 1n)]];
|
|
3789
|
-
}
|
|
3790
|
-
});
|
|
3791
|
-
};
|
|
3792
|
-
var branch = (ast) => {
|
|
3793
|
-
return walkPost2(ast, (node) => {
|
|
3794
|
-
if (!Array.isArray(node)) return;
|
|
3795
|
-
const op = node[0];
|
|
3796
|
-
if (op === "if") {
|
|
3797
|
-
const { condIdx, cond, thenBranch, elseBranch } = parseIf(node);
|
|
3798
|
-
const c = getConst(cond);
|
|
3799
|
-
if (!c) return;
|
|
3800
|
-
const taken = c.value !== 0 && c.value !== ZERO64 ? thenBranch : elseBranch;
|
|
3801
|
-
if (taken && taken.length > 1) {
|
|
3802
|
-
const contents = taken.slice(1);
|
|
3803
|
-
const blockType = node.slice(1, condIdx).filter((p) => Array.isArray(p) && (p[0] === "result" || p[0] === "param"));
|
|
3804
|
-
if (blockType.length) return ["block", ...blockType, ...contents];
|
|
3805
|
-
return contents.length === 1 ? contents[0] : ["block", ...contents];
|
|
3806
|
-
}
|
|
3807
|
-
return ["nop"];
|
|
3808
|
-
}
|
|
3809
|
-
if (op === "br_if" && node.length >= 3) {
|
|
3810
|
-
const cond = node[node.length - 1];
|
|
3811
|
-
const c = getConst(cond);
|
|
3812
|
-
if (!c) return;
|
|
3813
|
-
if (c.value === 0 || c.value === ZERO64) return ["nop"];
|
|
3814
|
-
return ["br", node[1]];
|
|
3815
|
-
}
|
|
3816
|
-
if (op === "select" && node.length >= 4) {
|
|
3817
|
-
const cond = node[node.length - 1];
|
|
3818
|
-
const c = getConst(cond);
|
|
3819
|
-
if (!c) return;
|
|
3820
|
-
if (c.value === 0 || c.value === ZERO64) return node[2];
|
|
3821
|
-
return node[1];
|
|
3822
|
-
}
|
|
3823
|
-
});
|
|
3824
|
-
};
|
|
3825
|
-
var guardRefine = (ast) => {
|
|
3826
|
-
if (Array.isArray(ast)) {
|
|
3827
|
-
for (const node of ast) if (Array.isArray(node) && node[0] === "func") refineGuards(node);
|
|
3828
|
-
}
|
|
3829
|
-
return ast;
|
|
3830
|
-
};
|
|
3831
|
-
var EMPTY_SET = /* @__PURE__ */ new Set();
|
|
3832
|
-
var refineGuards = (fn) => {
|
|
3833
|
-
const ptrAlias = /* @__PURE__ */ new Map();
|
|
3834
|
-
const tagAlias = /* @__PURE__ */ new Map();
|
|
3835
|
-
const eqFact = /* @__PURE__ */ new Map();
|
|
3836
|
-
const neFact = /* @__PURE__ */ new Map();
|
|
3837
|
-
const intVal = (n) => {
|
|
3838
|
-
if (!Array.isArray(n) || n.length !== 2 || n[0] !== "i32.const" && n[0] !== "i64.const") return null;
|
|
3839
|
-
const v = typeof n[1] === "string" ? Number(n[1].replaceAll("_", "")) : Number(n[1]);
|
|
3840
|
-
return Number.isFinite(v) ? v : null;
|
|
3841
|
-
};
|
|
3842
|
-
const i32Val = (n) => Array.isArray(n) && n[0] === "i32.const" ? intVal(n) : null;
|
|
3843
|
-
const ptrSrc = (n) => {
|
|
3844
|
-
if (!Array.isArray(n)) return null;
|
|
3845
|
-
if (n[0] === "i64.reinterpret_f64" && Array.isArray(n[1]) && n[1][0] === "local.get" && typeof n[1][1] === "string") return n[1][1];
|
|
3846
|
-
if (n[0] === "local.get" && typeof n[1] === "string") return ptrAlias.get(n[1]) ?? null;
|
|
3847
|
-
return null;
|
|
3848
|
-
};
|
|
3849
|
-
const tagSrc = (n) => {
|
|
3850
|
-
if (!Array.isArray(n)) return null;
|
|
3851
|
-
const op = n[0];
|
|
3852
|
-
if (op === "local.get" && typeof n[1] === "string") return tagAlias.get(n[1]) ?? null;
|
|
3853
|
-
if (op === "call" && n[1] === "$__ptr_type" && n.length === 3) return ptrSrc(n[2]);
|
|
3854
|
-
const shifted = (m) => Array.isArray(m) && m[0] === "i64.shr_u" && intVal(m[2]) === 47 ? ptrSrc(m[1]) : null;
|
|
3855
|
-
if (op === "i32.and" && n.length === 3) {
|
|
3856
|
-
const [a, b] = i32Val(n[2]) === 15 ? [n[1], null] : i32Val(n[1]) === 15 ? [n[2], null] : [null, null];
|
|
3857
|
-
if (a && Array.isArray(a) && a[0] === "i32.wrap_i64") return shifted(a[1]);
|
|
3858
|
-
}
|
|
3859
|
-
if (op === "i32.wrap_i64" && Array.isArray(n[1]) && n[1][0] === "i64.and") {
|
|
3860
|
-
const m = n[1];
|
|
3861
|
-
if (intVal(m[2]) === 15) return shifted(m[1]);
|
|
3862
|
-
if (intVal(m[1]) === 15) return shifted(m[2]);
|
|
3863
|
-
}
|
|
3864
|
-
return null;
|
|
3865
|
-
};
|
|
3866
|
-
const killLocal = (name2) => {
|
|
3867
|
-
ptrAlias.delete(name2);
|
|
3868
|
-
tagAlias.delete(name2);
|
|
3869
|
-
eqFact.delete(name2);
|
|
3870
|
-
neFact.delete(name2);
|
|
3871
|
-
for (const [p, x] of ptrAlias) if (x === name2) ptrAlias.delete(p);
|
|
3872
|
-
for (const [t, x] of tagAlias) if (x === name2) tagAlias.delete(t);
|
|
3873
|
-
};
|
|
3874
|
-
const writesMemo = /* @__PURE__ */ new Map();
|
|
3875
|
-
const writesOf = (n) => {
|
|
3876
|
-
if (!Array.isArray(n)) return EMPTY_SET;
|
|
3877
|
-
let s = writesMemo.get(n);
|
|
3878
|
-
if (s) return s;
|
|
3879
|
-
s = /* @__PURE__ */ new Set();
|
|
3880
|
-
if ((n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string") s.add(n[1]);
|
|
3881
|
-
for (let i = 1; i < n.length; i++) for (const w of writesOf(n[i])) s.add(w);
|
|
3882
|
-
writesMemo.set(n, s);
|
|
3883
|
-
return s;
|
|
3884
|
-
};
|
|
3885
|
-
const snap = () => [new Map(eqFact), new Map([...neFact].map(([k, s]) => [k, new Set(s)])), new Map(ptrAlias), new Map(tagAlias)];
|
|
3886
|
-
const reset = (m, src) => {
|
|
3887
|
-
m.clear();
|
|
3888
|
-
for (const [k, v] of src) m.set(k, v);
|
|
3889
|
-
};
|
|
3890
|
-
const restore = ([e, n, p, t]) => {
|
|
3891
|
-
reset(eqFact, e);
|
|
3892
|
-
reset(neFact, n);
|
|
3893
|
-
reset(ptrAlias, p);
|
|
3894
|
-
reset(tagAlias, t);
|
|
3895
|
-
};
|
|
3896
|
-
const condFacts = (cond, sense, out) => {
|
|
3897
|
-
if (!Array.isArray(cond)) return out;
|
|
3898
|
-
const op = cond[0];
|
|
3899
|
-
if (op === "i32.eqz") return condFacts(cond[1], !sense, out);
|
|
3900
|
-
if (op === "i32.and" && sense && cond.length === 3) {
|
|
3901
|
-
condFacts(cond[1], true, out);
|
|
3902
|
-
condFacts(cond[2], true, out);
|
|
3903
|
-
return out;
|
|
3904
|
-
}
|
|
3905
|
-
if (op === "i32.or" && !sense && cond.length === 3) {
|
|
3906
|
-
condFacts(cond[1], false, out);
|
|
3907
|
-
condFacts(cond[2], false, out);
|
|
3908
|
-
return out;
|
|
3909
|
-
}
|
|
3910
|
-
if ((op === "i32.eq" || op === "i32.ne") && cond.length === 3) {
|
|
3911
|
-
let x2 = tagSrc(cond[1]), k = i32Val(cond[2]);
|
|
3912
|
-
if (x2 == null || k == null) {
|
|
3913
|
-
x2 = tagSrc(cond[2]);
|
|
3914
|
-
k = i32Val(cond[1]);
|
|
3915
|
-
}
|
|
3916
|
-
if (x2 != null && k != null) out.push({ x: x2, k, eq: op === "i32.eq" === sense });
|
|
3917
|
-
return out;
|
|
3918
|
-
}
|
|
3919
|
-
const x = tagSrc(cond);
|
|
3920
|
-
if (x != null) out.push({ x, k: 0, eq: !sense });
|
|
3921
|
-
return out;
|
|
3922
|
-
};
|
|
3923
|
-
const addFacts = (fs) => {
|
|
3924
|
-
for (const { x, k, eq } of fs) {
|
|
3925
|
-
if (eq) eqFact.set(x, k);
|
|
3926
|
-
else {
|
|
3927
|
-
let s = neFact.get(x);
|
|
3928
|
-
if (!s) neFact.set(x, s = /* @__PURE__ */ new Set());
|
|
3929
|
-
s.add(k);
|
|
3930
|
-
}
|
|
3931
|
-
}
|
|
3932
|
-
};
|
|
3933
|
-
const walkSeq = (node, parent, idx) => {
|
|
3934
|
-
if (!Array.isArray(node)) return;
|
|
3935
|
-
const op = node[0];
|
|
3936
|
-
if (op === "local.set" || op === "local.tee") {
|
|
3937
|
-
if (Array.isArray(node[2])) walkSeq(node[2], node, 2);
|
|
3938
|
-
const name2 = node[1];
|
|
3939
|
-
if (typeof name2 !== "string") return;
|
|
3940
|
-
killLocal(name2);
|
|
3941
|
-
const v = node[2];
|
|
3942
|
-
if (Array.isArray(v)) {
|
|
3943
|
-
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]);
|
|
3944
|
-
else if (v[0] === "local.get" && typeof v[1] === "string" && ptrAlias.has(v[1])) ptrAlias.set(name2, ptrAlias.get(v[1]));
|
|
3945
|
-
else {
|
|
3946
|
-
const tx2 = tagSrc(v);
|
|
3947
|
-
if (tx2 != null) tagAlias.set(name2, tx2);
|
|
3948
|
-
}
|
|
3949
|
-
}
|
|
3950
|
-
return;
|
|
3951
|
-
}
|
|
3952
|
-
if (op === "if") {
|
|
3953
|
-
const { condIdx } = parseIf(node);
|
|
3954
|
-
if (Array.isArray(node[condIdx])) walkSeq(node[condIdx], node, condIdx);
|
|
3955
|
-
const cond = node[condIdx];
|
|
3956
|
-
const { thenBranch, elseBranch } = parseIf(node);
|
|
3957
|
-
const writes = writesOf(node);
|
|
3958
|
-
const pre = snap();
|
|
3959
|
-
addFacts(condFacts(cond, true, []));
|
|
3960
|
-
if (thenBranch) for (let i = 1; i < thenBranch.length; i++) walkSeq(thenBranch[i], thenBranch, i);
|
|
3961
|
-
restore(pre);
|
|
3962
|
-
addFacts(condFacts(cond, false, []));
|
|
3963
|
-
if (elseBranch) for (let i = 1; i < elseBranch.length; i++) walkSeq(elseBranch[i], elseBranch, i);
|
|
3964
|
-
restore(pre);
|
|
3965
|
-
for (const w of writes) killLocal(w);
|
|
3966
|
-
return;
|
|
3967
|
-
}
|
|
3968
|
-
if (op === "loop") {
|
|
3969
|
-
const writes = writesOf(node);
|
|
3970
|
-
for (const w of writes) killLocal(w);
|
|
3971
|
-
for (let i = 1; i < node.length; i++) walkSeq(node[i], node, i);
|
|
3972
|
-
for (const w of writes) killLocal(w);
|
|
3973
|
-
return;
|
|
3974
|
-
}
|
|
3975
|
-
if (op === "block") {
|
|
3976
|
-
for (let i = 1; i < node.length; i++) walkSeq(node[i], node, i);
|
|
3977
|
-
for (const w of writesOf(node)) killLocal(w);
|
|
3978
|
-
return;
|
|
3979
|
-
}
|
|
3980
|
-
const tx = tagSrc(node);
|
|
3981
|
-
if (tx != null && eqFact.has(tx) && parent) {
|
|
3982
|
-
parent[idx] = ["i32.const", eqFact.get(tx)];
|
|
3983
|
-
return;
|
|
3984
|
-
}
|
|
3985
|
-
if ((op === "i32.eq" || op === "i32.ne") && node.length === 3) {
|
|
3986
|
-
let x = tagSrc(node[1]), k = i32Val(node[2]);
|
|
3987
|
-
if (x == null || k == null) {
|
|
3988
|
-
x = tagSrc(node[2]);
|
|
3989
|
-
k = i32Val(node[1]);
|
|
3990
|
-
}
|
|
3991
|
-
if (x != null && k != null && neFact.get(x)?.has(k) && parent) {
|
|
3992
|
-
parent[idx] = ["i32.const", op === "i32.eq" ? 0 : 1];
|
|
3993
|
-
return;
|
|
3994
|
-
}
|
|
3995
|
-
}
|
|
3996
|
-
for (let i = 1; i < node.length; i++) walkSeq(node[i], node, i);
|
|
3997
|
-
};
|
|
3998
|
-
for (let i = 1; i < fn.length; i++) walkSeq(fn[i], fn, i);
|
|
3999
|
-
};
|
|
4000
|
-
var TERMINATORS = /* @__PURE__ */ new Set(["unreachable", "return", "br", "br_table"]);
|
|
4001
|
-
var deadcode = (ast) => {
|
|
4002
|
-
walk2(ast, (node) => {
|
|
4003
|
-
if (!Array.isArray(node)) return;
|
|
4004
|
-
const kind = node[0];
|
|
4005
|
-
if (kind === "func" || kind === "block" || kind === "loop") {
|
|
4006
|
-
eliminateDeadInBlock(node);
|
|
4007
|
-
}
|
|
4008
|
-
if (kind === "if") {
|
|
4009
|
-
for (let i = 1; i < node.length; i++) {
|
|
4010
|
-
if (Array.isArray(node[i]) && (node[i][0] === "then" || node[i][0] === "else")) {
|
|
4011
|
-
eliminateDeadInBlock(node[i]);
|
|
4012
|
-
}
|
|
4013
|
-
}
|
|
4014
|
-
}
|
|
4015
|
-
});
|
|
4016
|
-
return ast;
|
|
4017
|
-
};
|
|
4018
|
-
var eliminateDeadInBlock = (block) => {
|
|
4019
|
-
let terminated = false;
|
|
4020
|
-
let firstTerminator = -1;
|
|
4021
|
-
for (let i = 1; i < block.length; i++) {
|
|
4022
|
-
const node = block[i];
|
|
4023
|
-
if (Array.isArray(node)) {
|
|
4024
|
-
const op = node[0];
|
|
4025
|
-
if (op === "param" || op === "result" || op === "local" || op === "type" || op === "export") continue;
|
|
4026
|
-
if (terminated) {
|
|
4027
|
-
if (firstTerminator === -1) firstTerminator = i;
|
|
4028
|
-
}
|
|
4029
|
-
if (TERMINATORS.has(op)) {
|
|
4030
|
-
terminated = true;
|
|
4031
|
-
firstTerminator = i + 1;
|
|
4032
|
-
}
|
|
4033
|
-
} else if (typeof node === "string") {
|
|
4034
|
-
if (terminated) {
|
|
4035
|
-
if (firstTerminator === -1) firstTerminator = i;
|
|
4036
|
-
}
|
|
4037
|
-
if (TERMINATORS.has(node)) {
|
|
4038
|
-
terminated = true;
|
|
4039
|
-
firstTerminator = i + 1;
|
|
4040
|
-
}
|
|
4041
|
-
}
|
|
4042
|
-
}
|
|
4043
|
-
if (firstTerminator > 0 && firstTerminator < block.length) {
|
|
4044
|
-
block.splice(firstTerminator);
|
|
4045
|
-
}
|
|
4046
|
-
};
|
|
4047
|
-
var localReuse = (ast) => {
|
|
4048
|
-
walk2(ast, (node) => {
|
|
4049
|
-
if (!Array.isArray(node) || node[0] !== "func") return;
|
|
4050
|
-
const localDecls = [];
|
|
4051
|
-
const localTypes = /* @__PURE__ */ new Map();
|
|
4052
|
-
const usedLocals = /* @__PURE__ */ new Set();
|
|
4053
|
-
for (let i = 1; i < node.length; i++) {
|
|
4054
|
-
const sub = node[i];
|
|
4055
|
-
if (!Array.isArray(sub)) continue;
|
|
4056
|
-
if (sub[0] === "local") {
|
|
4057
|
-
localDecls.push({ node: sub, idx: i });
|
|
4058
|
-
if (typeof sub[1] === "string" && sub[1][0] === "$") {
|
|
4059
|
-
localTypes.set(sub[1], sub[2]);
|
|
4060
|
-
}
|
|
4061
|
-
}
|
|
4062
|
-
if (sub[0] === "param") {
|
|
4063
|
-
if (typeof sub[1] === "string" && sub[1][0] === "$") {
|
|
4064
|
-
localTypes.set(sub[1], sub[2]);
|
|
4065
|
-
usedLocals.add(sub[1]);
|
|
4066
|
-
}
|
|
4067
|
-
}
|
|
4068
|
-
}
|
|
4069
|
-
walk2(node, (n) => {
|
|
4070
|
-
if (!Array.isArray(n)) return;
|
|
4071
|
-
const op = n[0];
|
|
4072
|
-
if (op === "local.get" || op === "local.set" || op === "local.tee") {
|
|
4073
|
-
const ref = n[1];
|
|
4074
|
-
if (typeof ref === "string") usedLocals.add(ref);
|
|
4075
|
-
}
|
|
4076
|
-
});
|
|
4077
|
-
for (let i = localDecls.length - 1; i >= 0; i--) {
|
|
4078
|
-
const { idx, node: decl } = localDecls[i];
|
|
4079
|
-
const name2 = typeof decl[1] === "string" && decl[1][0] === "$" ? decl[1] : null;
|
|
4080
|
-
if (name2 && !usedLocals.has(name2)) {
|
|
4081
|
-
node.splice(idx, 1);
|
|
4082
|
-
}
|
|
4083
|
-
}
|
|
4084
|
-
});
|
|
4085
|
-
return ast;
|
|
4086
|
-
};
|
|
4087
|
-
var IMPURE_OPS = /* @__PURE__ */ new Set([
|
|
4088
|
-
"call",
|
|
4089
|
-
"call_indirect",
|
|
4090
|
-
"return_call",
|
|
4091
|
-
"return_call_indirect",
|
|
4092
|
-
"table.set",
|
|
4093
|
-
"table.grow",
|
|
4094
|
-
"table.fill",
|
|
4095
|
-
"table.copy",
|
|
4096
|
-
"table.init",
|
|
4097
|
-
"struct.set",
|
|
4098
|
-
"struct.new",
|
|
4099
|
-
"array.set",
|
|
4100
|
-
"array.new",
|
|
4101
|
-
"array.new_fixed",
|
|
4102
|
-
"array.new_data",
|
|
4103
|
-
"array.new_elem",
|
|
4104
|
-
"array.init_data",
|
|
4105
|
-
"array.init_elem",
|
|
4106
|
-
"ref.i31",
|
|
4107
|
-
"global.set",
|
|
4108
|
-
"local.set",
|
|
4109
|
-
"local.tee",
|
|
4110
|
-
"unreachable",
|
|
4111
|
-
"return",
|
|
4112
|
-
"br",
|
|
4113
|
-
"br_if",
|
|
4114
|
-
"br_table",
|
|
4115
|
-
"br_on_null",
|
|
4116
|
-
"br_on_non_null",
|
|
4117
|
-
"br_on_cast",
|
|
4118
|
-
"br_on_cast_fail",
|
|
4119
|
-
"throw",
|
|
4120
|
-
"rethrow",
|
|
4121
|
-
"throw_ref",
|
|
4122
|
-
"try_table",
|
|
4123
|
-
"data.drop",
|
|
4124
|
-
"elem.drop"
|
|
4125
|
-
]);
|
|
4126
|
-
var IMPURE_SUBSTRINGS = [".store", "memory.", ".atomic."];
|
|
4127
|
-
var isPure = (node) => {
|
|
4128
|
-
if (!Array.isArray(node)) return true;
|
|
4129
|
-
const op = node[0];
|
|
4130
|
-
if (typeof op !== "string") return false;
|
|
4131
|
-
if (IMPURE_OPS.has(op)) return false;
|
|
4132
|
-
for (const sub of IMPURE_SUBSTRINGS) if (op.includes(sub)) return false;
|
|
4133
|
-
for (let i = 1; i < node.length; i++) if (Array.isArray(node[i]) && !isPure(node[i])) return false;
|
|
4134
|
-
return true;
|
|
4135
|
-
};
|
|
4136
|
-
var STRUCTURED_OPS = /* @__PURE__ */ new Set(["if", "then", "else", "block", "loop", "try"]);
|
|
4137
|
-
var isEagerValueOp = (op) => typeof op === "string" && !IMPURE_OPS.has(op) && !STRUCTURED_OPS.has(op) && !IMPURE_SUBSTRINGS.some((s) => op.includes(s));
|
|
4138
|
-
var dropEffects = (node) => {
|
|
4139
|
-
if (!Array.isArray(node) || isPure(node)) return [];
|
|
4140
|
-
const op = node[0];
|
|
4141
|
-
if (op === "local.tee" && node.length === 3) return [["local.set", node[1], node[2]]];
|
|
4142
|
-
if (isEagerValueOp(op)) {
|
|
4143
|
-
const eff = [];
|
|
4144
|
-
for (let i = 1; i < node.length; i++) eff.push(...dropEffects(node[i]));
|
|
4145
|
-
return eff;
|
|
4146
|
-
}
|
|
4147
|
-
return [["drop", node]];
|
|
4148
|
-
};
|
|
4149
|
-
var countLocalUses = (node) => {
|
|
4150
|
-
const counts = /* @__PURE__ */ new Map();
|
|
4151
|
-
const ensure = (name2) => {
|
|
4152
|
-
if (!counts.has(name2)) counts.set(name2, { gets: 0, sets: 0, tees: 0 });
|
|
4153
|
-
return counts.get(name2);
|
|
4154
|
-
};
|
|
4155
|
-
walk2(node, (n) => {
|
|
4156
|
-
if (!Array.isArray(n) || n.length < 2 || typeof n[1] !== "string") return;
|
|
4157
|
-
if (n[0] === "local.get") ensure(n[1]).gets++;
|
|
4158
|
-
else if (n[0] === "local.set") ensure(n[1]).sets++;
|
|
4159
|
-
else if (n[0] === "local.tee") ensure(n[1]).tees++;
|
|
4160
|
-
});
|
|
4161
|
-
return counts;
|
|
4162
|
-
};
|
|
4163
|
-
var isTinyConst = (node) => {
|
|
4164
|
-
const c = getConst(node);
|
|
4165
|
-
if (!c) return false;
|
|
4166
|
-
if (c.type === "i32") {
|
|
4167
|
-
const v = c.value | 0;
|
|
4168
|
-
return v >= -64 && v <= 63;
|
|
4169
|
-
}
|
|
4170
|
-
if (c.type === "i64") {
|
|
4171
|
-
const v = BigInt(c.value);
|
|
4172
|
-
return v <= 63n || v >= 0xffffffffffffffc0n;
|
|
4173
|
-
}
|
|
4174
|
-
return false;
|
|
4175
|
-
};
|
|
4176
|
-
var isLocalCopy = (val, dest) => Array.isArray(val) && val[0] === "local.get" && val.length === 2 && typeof val[1] === "string" && val[1] !== dest;
|
|
4177
|
-
var canSubst = (k) => k.pure && k.singleUse || isTinyConst(k.val) || k.copy;
|
|
4178
|
-
var purgeRefs = (known, name2) => {
|
|
4179
|
-
for (const [key, tracked] of known) {
|
|
4180
|
-
let refs = false;
|
|
4181
|
-
walk2(tracked.val, (n) => {
|
|
4182
|
-
if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.tee") && n[1] === name2) refs = true;
|
|
4183
|
-
});
|
|
4184
|
-
if (refs) known.delete(key);
|
|
4185
|
-
}
|
|
4186
|
-
};
|
|
4187
|
-
var purgeGlobalRefs = (known, name2) => {
|
|
4188
|
-
for (const [key, tracked] of known) {
|
|
4189
|
-
let refs = false;
|
|
4190
|
-
walk2(tracked.val, (n) => {
|
|
4191
|
-
if (Array.isArray(n) && n[0] === "global.get" && n[1] === name2) refs = true;
|
|
4192
|
-
});
|
|
4193
|
-
if (refs) known.delete(key);
|
|
4194
|
-
}
|
|
4195
|
-
};
|
|
4196
|
-
var readsMemory = (node) => {
|
|
4197
|
-
if (!Array.isArray(node)) return false;
|
|
4198
|
-
const op = node[0];
|
|
4199
|
-
if (typeof op === "string") {
|
|
4200
|
-
if (op.includes(".load") || op === "memory.copy" || op === "memory.size") return true;
|
|
4201
|
-
}
|
|
4202
|
-
for (let i = 1; i < node.length; i++) if (readsMemory(node[i])) return true;
|
|
4203
|
-
return false;
|
|
4204
|
-
};
|
|
4205
|
-
var readsCallableState = (node) => {
|
|
4206
|
-
if (!Array.isArray(node)) return false;
|
|
4207
|
-
const op = node[0];
|
|
4208
|
-
if (typeof op === "string") {
|
|
4209
|
-
if (op === "global.get" || op === "table.get" || op === "table.size") return true;
|
|
4210
|
-
if (op === "call" || op === "call_indirect" || op === "return_call" || op === "return_call_indirect") return true;
|
|
4211
|
-
if (op.includes(".load") || op === "memory.copy" || op === "memory.size") return true;
|
|
4212
|
-
}
|
|
4213
|
-
for (let i = 1; i < node.length; i++) if (readsCallableState(node[i])) return true;
|
|
4214
|
-
return false;
|
|
4215
|
-
};
|
|
4216
|
-
var writesMemory = (node) => {
|
|
4217
|
-
if (!Array.isArray(node)) return false;
|
|
4218
|
-
const op = node[0];
|
|
4219
|
-
if (typeof op === "string") {
|
|
4220
|
-
if (op.endsWith(".store") || op === "memory.copy" || op === "memory.fill" || op === "memory.init") return true;
|
|
4221
|
-
if (op.includes(".atomic.") && !op.endsWith(".load")) return true;
|
|
4222
|
-
}
|
|
4223
|
-
for (let i = 1; i < node.length; i++) if (writesMemory(node[i])) return true;
|
|
4224
|
-
return false;
|
|
4225
|
-
};
|
|
4226
|
-
var substGets = (node, known) => {
|
|
4227
|
-
if (!Array.isArray(node)) return node;
|
|
4228
|
-
const op = node[0];
|
|
4229
|
-
if (op === "local.get" && node.length === 2) {
|
|
4230
|
-
const k = typeof node[1] === "string" && known.get(node[1]);
|
|
4231
|
-
if (k && canSubst(k)) return clone2(k.val);
|
|
4232
|
-
return node;
|
|
4233
|
-
}
|
|
4234
|
-
let inner = known;
|
|
4235
|
-
if (isBranchScope(op)) {
|
|
4236
|
-
let cloned = null;
|
|
4237
|
-
walk2(node, (n) => {
|
|
4238
|
-
if (!Array.isArray(n)) return;
|
|
4239
|
-
if ((n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string" && known.has(n[1])) {
|
|
4240
|
-
if (!cloned) cloned = new Map(known);
|
|
4241
|
-
cloned.delete(n[1]);
|
|
4242
|
-
}
|
|
4243
|
-
});
|
|
4244
|
-
if (cloned) inner = cloned;
|
|
4245
|
-
}
|
|
4246
|
-
for (let i = 1; i < node.length; i++) {
|
|
4247
|
-
const r = substGets(node[i], inner);
|
|
4248
|
-
if (r !== node[i]) node[i] = r;
|
|
4249
|
-
if (i + 1 < node.length && Array.isArray(node[i])) {
|
|
4250
|
-
walk2(node[i], (n) => {
|
|
4251
|
-
if (!Array.isArray(n)) return;
|
|
4252
|
-
if ((n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string") {
|
|
4253
|
-
if (inner === known) inner = new Map(known);
|
|
4254
|
-
inner.delete(n[1]);
|
|
4255
|
-
purgeRefs(inner, n[1]);
|
|
4256
|
-
} else if (n[0] === "global.set" && typeof n[1] === "string") {
|
|
4257
|
-
if (inner === known) inner = new Map(known);
|
|
4258
|
-
purgeGlobalRefs(inner, n[1]);
|
|
4259
|
-
}
|
|
4260
|
-
});
|
|
4261
|
-
}
|
|
4262
|
-
}
|
|
4263
|
-
return node;
|
|
4264
|
-
};
|
|
4265
|
-
var forwardPropagate = (funcNode, params, useCounts) => {
|
|
4266
|
-
let changed = false;
|
|
4267
|
-
const getUseCount = (name2) => useCounts.get(name2) || { gets: 0, sets: 0, tees: 0 };
|
|
4268
|
-
const known = /* @__PURE__ */ new Map();
|
|
4269
|
-
for (let i = 1; i < funcNode.length; i++) {
|
|
4270
|
-
const instr2 = funcNode[i];
|
|
4271
|
-
if (!Array.isArray(instr2)) continue;
|
|
4272
|
-
const op = instr2[0];
|
|
4273
|
-
if (op === "param" || op === "result" || op === "local" || op === "type" || op === "export") continue;
|
|
4274
|
-
if ((op === "local.set" || op === "local.tee") && instr2.length === 3 && typeof instr2[1] === "string") {
|
|
4275
|
-
const sr = substGets(instr2[2], known);
|
|
4276
|
-
if (sr !== instr2[2]) {
|
|
4277
|
-
instr2[2] = sr;
|
|
4278
|
-
changed = true;
|
|
4279
|
-
}
|
|
4280
|
-
walk2(instr2[2], (n) => {
|
|
4281
|
-
if (!Array.isArray(n)) return;
|
|
4282
|
-
if ((n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string") {
|
|
4283
|
-
known.delete(n[1]);
|
|
4284
|
-
purgeRefs(known, n[1]);
|
|
4285
|
-
} else if (n[0] === "global.set" && typeof n[1] === "string") purgeGlobalRefs(known, n[1]);
|
|
4286
|
-
});
|
|
4287
|
-
const uses = getUseCount(instr2[1]);
|
|
4288
|
-
purgeRefs(known, instr2[1]);
|
|
4289
|
-
if (writesMemory(instr2[2])) {
|
|
4290
|
-
for (const [key, tracked] of known) if (tracked.readsMem) known.delete(key);
|
|
4291
|
-
}
|
|
4292
|
-
known.set(instr2[1], {
|
|
4293
|
-
val: instr2[2],
|
|
4294
|
-
pure: isPure(instr2[2]),
|
|
4295
|
-
readsMem: readsMemory(instr2[2]),
|
|
4296
|
-
singleUse: uses.gets <= 1 && uses.sets <= 1 && uses.tees === 0,
|
|
4297
|
-
copy: isLocalCopy(instr2[2], instr2[1])
|
|
4298
|
-
});
|
|
4299
|
-
continue;
|
|
4300
|
-
}
|
|
4301
|
-
if (isBranchScope(op)) known.clear();
|
|
4302
|
-
if (op === "call" || op === "call_indirect" || op === "return_call" || op === "return_call_indirect") {
|
|
4303
|
-
for (const [key, tracked] of known) if (readsCallableState(tracked.val)) known.delete(key);
|
|
4304
|
-
}
|
|
4305
|
-
if (op === "local.get" && instr2.length === 2 && typeof instr2[1] === "string") {
|
|
4306
|
-
const tracked = known.get(instr2[1]);
|
|
4307
|
-
if (tracked && canSubst(tracked)) {
|
|
4308
|
-
const replacement = clone2(tracked.val);
|
|
4309
|
-
instr2.length = 0;
|
|
4310
|
-
instr2.push(...Array.isArray(replacement) ? replacement : [replacement]);
|
|
4311
|
-
changed = true;
|
|
4312
|
-
continue;
|
|
4313
|
-
}
|
|
4314
|
-
}
|
|
4315
|
-
if (op !== "block" && op !== "loop" && op !== "if") {
|
|
4316
|
-
const prev = clone2(instr2);
|
|
4317
|
-
substGets(instr2, known);
|
|
4318
|
-
if (!equal(prev, instr2)) changed = true;
|
|
4319
|
-
walk2(instr2, (n) => {
|
|
4320
|
-
if (!Array.isArray(n)) return;
|
|
4321
|
-
if ((n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string") {
|
|
4322
|
-
known.delete(n[1]);
|
|
4323
|
-
purgeRefs(known, n[1]);
|
|
4324
|
-
} else if (n[0] === "global.set" && typeof n[1] === "string") purgeGlobalRefs(known, n[1]);
|
|
4325
|
-
});
|
|
4326
|
-
if (writesMemory(instr2)) {
|
|
4327
|
-
for (const [key, tracked] of known) if (tracked.readsMem) known.delete(key);
|
|
4328
|
-
}
|
|
4329
|
-
}
|
|
4330
|
-
}
|
|
4331
|
-
return changed;
|
|
4332
|
-
};
|
|
4333
|
-
var eliminateSetGetPairs = (funcNode, params, useCounts) => {
|
|
4334
|
-
let changed = false;
|
|
4335
|
-
for (let i = 1; i < funcNode.length - 1; i++) {
|
|
4336
|
-
const setNode = funcNode[i];
|
|
4337
|
-
const getNode = funcNode[i + 1];
|
|
4338
|
-
if (!Array.isArray(setNode) || setNode[0] !== "local.set" || setNode.length !== 3) continue;
|
|
4339
|
-
if (!Array.isArray(getNode) || getNode[0] !== "local.get" || getNode.length !== 2) continue;
|
|
4340
|
-
const name2 = setNode[1];
|
|
4341
|
-
if (getNode[1] !== name2 || params.has(name2)) continue;
|
|
4342
|
-
const uses = useCounts.get(name2) || { gets: 0, sets: 0, tees: 0 };
|
|
4343
|
-
if (uses.sets !== 1 || uses.gets !== 1 || uses.tees !== 0) continue;
|
|
4344
|
-
const expr2 = clone2(setNode[2]);
|
|
4345
|
-
funcNode.splice(i, 2, ...Array.isArray(expr2) ? [expr2] : [expr2]);
|
|
4346
|
-
changed = true;
|
|
4347
|
-
i--;
|
|
4348
|
-
}
|
|
4349
|
-
return changed;
|
|
4350
|
-
};
|
|
4351
|
-
var createLocalTees = (funcNode, params, useCounts) => {
|
|
4352
|
-
let changed = false;
|
|
4353
|
-
for (let i = 1; i < funcNode.length - 1; i++) {
|
|
4354
|
-
const setNode = funcNode[i];
|
|
4355
|
-
const getNode = funcNode[i + 1];
|
|
4356
|
-
if (!Array.isArray(setNode) || setNode[0] !== "local.set" || setNode.length !== 3) continue;
|
|
4357
|
-
if (!Array.isArray(getNode) || getNode[0] !== "local.get" || getNode.length !== 2) continue;
|
|
4358
|
-
const name2 = setNode[1];
|
|
4359
|
-
if (getNode[1] !== name2 || params.has(name2)) continue;
|
|
4360
|
-
const uses = useCounts.get(name2) || { gets: 0, sets: 0, tees: 0 };
|
|
4361
|
-
if (uses.sets + uses.gets + uses.tees <= 2) continue;
|
|
4362
|
-
funcNode.splice(i, 2, ["local.tee", name2, clone2(setNode[2])]);
|
|
4363
|
-
changed = true;
|
|
4364
|
-
}
|
|
4365
|
-
return changed;
|
|
4366
|
-
};
|
|
4367
|
-
var eliminateDeadStores = (funcNode, params, useCounts) => {
|
|
4368
|
-
let changed = false;
|
|
4369
|
-
const getPostUseCount = (name2) => useCounts.get(name2) || { gets: 0, sets: 0, tees: 0 };
|
|
4370
|
-
for (let i = funcNode.length - 1; i >= 1; i--) {
|
|
4371
|
-
const sub = funcNode[i];
|
|
4372
|
-
if (!Array.isArray(sub)) continue;
|
|
4373
|
-
const name2 = typeof sub[1] === "string" ? sub[1] : null;
|
|
4374
|
-
if (!name2 || params.has(name2)) continue;
|
|
4375
|
-
const uses = getPostUseCount(name2);
|
|
4376
|
-
if (sub[0] === "local.set" && uses.gets === 0 && uses.tees === 0) {
|
|
4377
|
-
if (sub.length === 3) {
|
|
4378
|
-
if (isPure(sub[2])) {
|
|
4379
|
-
funcNode.splice(i, 1);
|
|
4380
|
-
changed = true;
|
|
4381
|
-
}
|
|
4382
|
-
} else if (sub.length === 2) {
|
|
4383
|
-
funcNode[i] = ["drop"];
|
|
4384
|
-
changed = true;
|
|
4385
|
-
}
|
|
4386
|
-
} else if (sub[0] === "local" && name2[0] === "$" && uses.gets === 0 && uses.sets === 0 && uses.tees === 0) {
|
|
4387
|
-
funcNode.splice(i, 1);
|
|
4388
|
-
changed = true;
|
|
4389
|
-
}
|
|
4390
|
-
}
|
|
4391
|
-
return changed;
|
|
4392
|
-
};
|
|
4393
|
-
var eliminateAdjacentDeadStores = (funcNode, params) => {
|
|
4394
|
-
let changed = false;
|
|
4395
|
-
for (let i = 1; i < funcNode.length - 1; i++) {
|
|
4396
|
-
const a = funcNode[i], b = funcNode[i + 1];
|
|
4397
|
-
if (!Array.isArray(a) || a[0] !== "local.set" || a.length !== 3) continue;
|
|
4398
|
-
if (!Array.isArray(b) || b[0] !== "local.set" && b[0] !== "local.tee" || b.length !== 3 || b[1] !== a[1]) continue;
|
|
4399
|
-
if (params.has(a[1]) || !isPure(a[2])) continue;
|
|
4400
|
-
let reads = false;
|
|
4401
|
-
walk2(b[2], (n) => {
|
|
4402
|
-
if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.tee") && n[1] === a[1]) reads = true;
|
|
4403
|
-
});
|
|
4404
|
-
if (reads) continue;
|
|
4405
|
-
funcNode.splice(i, 1);
|
|
4406
|
-
changed = true;
|
|
4407
|
-
i--;
|
|
4408
|
-
}
|
|
4409
|
-
return changed;
|
|
4410
|
-
};
|
|
4411
|
-
var isScopeNode = (n) => Array.isArray(n) && (n[0] === "func" || n[0] === "block" || n[0] === "loop" || n[0] === "then" || n[0] === "else");
|
|
4412
|
-
var isBranchScope = (op) => op === "block" || op === "loop" || op === "if";
|
|
4413
|
-
var propagate = (ast) => {
|
|
4414
|
-
walk2(ast, (funcNode) => {
|
|
4415
|
-
if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
|
|
4416
|
-
const params = /* @__PURE__ */ new Set();
|
|
4417
|
-
for (const sub of funcNode)
|
|
4418
|
-
if (Array.isArray(sub) && sub[0] === "param" && typeof sub[1] === "string") params.add(sub[1]);
|
|
4419
|
-
const scopes = [];
|
|
4420
|
-
walkPost2(funcNode, (n) => {
|
|
4421
|
-
if (isScopeNode(n)) scopes.push(n);
|
|
4422
|
-
});
|
|
4423
|
-
for (let round = 0; round < MAX_PROP_ROUNDS; round++) {
|
|
4424
|
-
const useCounts = countLocalUses(funcNode);
|
|
4425
|
-
let progressed = false;
|
|
4426
|
-
for (const scope of scopes) {
|
|
4427
|
-
if (forwardPropagate(scope, params, useCounts)) progressed = true;
|
|
4428
|
-
if (eliminateSetGetPairs(scope, params, useCounts)) progressed = true;
|
|
4429
|
-
if (createLocalTees(scope, params, useCounts)) progressed = true;
|
|
4430
|
-
if (eliminateDeadStores(scope, params, useCounts)) progressed = true;
|
|
4431
|
-
if (eliminateAdjacentDeadStores(scope, params)) progressed = true;
|
|
4432
|
-
}
|
|
4433
|
-
if (!progressed) break;
|
|
4434
|
-
}
|
|
4435
|
-
});
|
|
4436
|
-
return ast;
|
|
4437
|
-
};
|
|
4438
|
-
var inlineUid = 0;
|
|
4439
|
-
var INL_HEAD = /* @__PURE__ */ new Set(["export", "type", "param", "result", "local"]);
|
|
4440
|
-
var inlBodyStart = (fn) => {
|
|
4441
|
-
let i = 2;
|
|
4442
|
-
while (i < fn.length && (typeof fn[i] === "string" || Array.isArray(fn[i]) && INL_HEAD.has(fn[i][0]))) i++;
|
|
4443
|
-
return i;
|
|
4444
|
-
};
|
|
4445
|
-
var inlIsBranch = (op) => op === "br" || op === "br_if" || op === "br_table";
|
|
4446
|
-
var inlUnsafe = (n) => {
|
|
4447
|
-
if (!Array.isArray(n)) return false;
|
|
4448
|
-
const op = n[0];
|
|
4449
|
-
if (op === "return_call" || op === "return_call_indirect" || op === "return_call_ref") return true;
|
|
4450
|
-
if (op === "try" || op === "try_table" || op === "delegate" || op === "rethrow") return true;
|
|
4451
|
-
if (inlIsBranch(op)) {
|
|
4452
|
-
for (let i = 1; i < n.length; i++) if (typeof n[i] === "number" || typeof n[i] === "string" && /^\d+$/.test(n[i])) return true;
|
|
4453
|
-
}
|
|
4454
|
-
for (let i = 1; i < n.length; i++) if (inlUnsafe(n[i])) return true;
|
|
4455
|
-
return false;
|
|
4456
|
-
};
|
|
4457
|
-
var inlCallsSelf = (n, name2) => {
|
|
4458
|
-
if (!Array.isArray(n)) return false;
|
|
4459
|
-
if ((n[0] === "call" || n[0] === "return_call") && n[1] === name2) return true;
|
|
4460
|
-
for (let i = 1; i < n.length; i++) if (inlCallsSelf(n[i], name2)) return true;
|
|
4461
|
-
return false;
|
|
4462
|
-
};
|
|
4463
|
-
var inlZeroFor = (t) => {
|
|
4464
|
-
if (t === "i32") return ["i32.const", 0];
|
|
4465
|
-
if (t === "i64") return ["i64.const", 0];
|
|
4466
|
-
if (t === "f32") return ["f32.const", 0];
|
|
4467
|
-
if (t === "f64") return ["f64.const", 0];
|
|
4468
|
-
if (t === "v128") return ["v128.const", "i64x2", "0", "0"];
|
|
4469
|
-
return null;
|
|
4470
|
-
};
|
|
4471
|
-
var inlNeedsReset = (body, name2) => {
|
|
4472
|
-
let seen = false, conditional = false, depth = 0;
|
|
4473
|
-
const visit = (n) => {
|
|
4474
|
-
if (seen || !Array.isArray(n)) return;
|
|
4475
|
-
const op = n[0];
|
|
4476
|
-
const isSet = op === "local.set" || op === "local.tee";
|
|
4477
|
-
if ((isSet || op === "local.get") && n[1] === name2) {
|
|
4478
|
-
if (isSet) for (let i = 2; i < n.length && !seen; i++) visit(n[i]);
|
|
4479
|
-
if (seen) return;
|
|
4480
|
-
seen = true;
|
|
4481
|
-
if (op === "local.get" || depth > 0) conditional = true;
|
|
4482
|
-
return;
|
|
4483
|
-
}
|
|
4484
|
-
const isIf = op === "if";
|
|
4485
|
-
for (let i = 1; i < n.length && !seen; i++) {
|
|
4486
|
-
const c = n[i];
|
|
4487
|
-
const cond = isIf && Array.isArray(c) && (c[0] === "then" || c[0] === "else");
|
|
4488
|
-
if (cond) depth++;
|
|
4489
|
-
visit(c);
|
|
4490
|
-
if (cond) depth--;
|
|
4491
|
-
}
|
|
4492
|
-
};
|
|
4493
|
-
for (const n of body) {
|
|
4494
|
-
if (seen) break;
|
|
4495
|
-
visit(n);
|
|
4496
|
-
}
|
|
4497
|
-
if (!seen) return false;
|
|
4498
|
-
return conditional;
|
|
4499
|
-
};
|
|
4500
|
-
var inlCollectPinned = (n, pinned) => {
|
|
4501
|
-
if (!Array.isArray(n)) return;
|
|
4502
|
-
const op = n[0];
|
|
4503
|
-
if (op === "export" && Array.isArray(n[2]) && n[2][0] === "func" && typeof n[2][1] === "string") pinned.add(n[2][1]);
|
|
4504
|
-
else if (op === "start" && typeof n[1] === "string") pinned.add(n[1]);
|
|
4505
|
-
else if (op === "ref.func" && typeof n[1] === "string") pinned.add(n[1]);
|
|
4506
|
-
else if (op === "elem") {
|
|
4507
|
-
for (const c of n) if (typeof c === "string" && c[0] === "$") pinned.add(c);
|
|
4508
|
-
}
|
|
4509
|
-
for (const c of n) inlCollectPinned(c, pinned);
|
|
4510
|
-
};
|
|
4511
|
-
var inlBuildPinned = (ast) => {
|
|
4512
|
-
const pinned = /* @__PURE__ */ new Set();
|
|
4513
|
-
for (const n of ast) if (!Array.isArray(n) || n[0] !== "func") inlCollectPinned(n, pinned);
|
|
4514
|
-
return pinned;
|
|
4515
|
-
};
|
|
4516
|
-
var inlParse = (fn) => {
|
|
4517
|
-
const params = [], locals = [];
|
|
4518
|
-
let inlResult = null, ok = true, nResult = 0;
|
|
4519
|
-
for (let i = 2; i < fn.length; i++) {
|
|
4520
|
-
const c = fn[i];
|
|
4521
|
-
if (typeof c === "string") continue;
|
|
4522
|
-
if (!Array.isArray(c)) {
|
|
4523
|
-
ok = false;
|
|
4524
|
-
break;
|
|
4525
|
-
}
|
|
4526
|
-
if (c[0] === "param") {
|
|
4527
|
-
if (typeof c[1] !== "string" || c[1][0] !== "$") {
|
|
4528
|
-
ok = false;
|
|
4529
|
-
break;
|
|
4530
|
-
}
|
|
4531
|
-
params.push({ name: c[1], type: c[2] });
|
|
4532
|
-
} else if (c[0] === "local") {
|
|
4533
|
-
if (typeof c[1] !== "string" || c[1][0] !== "$" || !inlZeroFor(c[2])) {
|
|
4534
|
-
ok = false;
|
|
4535
|
-
break;
|
|
4536
|
-
}
|
|
4537
|
-
locals.push({ name: c[1], type: c[2] });
|
|
4538
|
-
} else if (c[0] === "result") {
|
|
4539
|
-
nResult += c.length - 1;
|
|
4540
|
-
if (c.length > 1) inlResult = c[1];
|
|
4541
|
-
} else if (c[0] === "export") {
|
|
4542
|
-
ok = false;
|
|
4543
|
-
break;
|
|
4544
|
-
} else if (c[0] === "type") continue;
|
|
4545
|
-
else break;
|
|
4546
|
-
}
|
|
4547
|
-
if (nResult > 1) ok = false;
|
|
4548
|
-
return ok ? { params, locals, inlResult } : null;
|
|
4549
|
-
};
|
|
4550
|
-
var inlBodySize = (fn) => {
|
|
4551
|
-
let n = 0;
|
|
4552
|
-
const count = (x) => {
|
|
4553
|
-
if (!Array.isArray(x)) return;
|
|
4554
|
-
n++;
|
|
4555
|
-
for (let i = 1; i < x.length; i++) count(x[i]);
|
|
4556
|
-
};
|
|
4557
|
-
for (let i = inlBodyStart(fn); i < fn.length; i++) count(fn[i]);
|
|
4558
|
-
return n;
|
|
4559
|
-
};
|
|
4560
|
-
var buildInline = (params, locals, inlResult, cBody, args) => {
|
|
4561
|
-
const uid2 = ++inlineUid;
|
|
4562
|
-
const exit = `$__inl${uid2}`;
|
|
4563
|
-
const rename = /* @__PURE__ */ new Map();
|
|
4564
|
-
for (const p of params) rename.set(p.name, `$__inl${uid2}_${p.name.slice(1)}`);
|
|
4565
|
-
for (const l of locals) rename.set(l.name, `$__inl${uid2}_${l.name.slice(1)}`);
|
|
4566
|
-
const labelRename = /* @__PURE__ */ new Map();
|
|
4567
|
-
const collectLabels = (n) => {
|
|
4568
|
-
if (!Array.isArray(n)) return;
|
|
4569
|
-
if (isBranchScope(n[0]) && typeof n[1] === "string" && n[1][0] === "$" && !labelRename.has(n[1]))
|
|
4570
|
-
labelRename.set(n[1], `$__inl${uid2}L_${n[1].slice(1)}`);
|
|
4571
|
-
for (let i = 1; i < n.length; i++) collectLabels(n[i]);
|
|
4572
|
-
};
|
|
4573
|
-
for (const n of cBody) collectLabels(n);
|
|
4574
|
-
const sub = (n) => {
|
|
4575
|
-
if (!Array.isArray(n)) return n;
|
|
4576
|
-
const op = n[0];
|
|
4577
|
-
if ((op === "local.get" || op === "local.set" || op === "local.tee") && typeof n[1] === "string" && rename.has(n[1]))
|
|
4578
|
-
return [op, rename.get(n[1]), ...n.slice(2).map(sub)];
|
|
4579
|
-
if (op === "return") return ["br", exit, ...n.slice(1).map(sub)];
|
|
4580
|
-
if (isBranchScope(op) && typeof n[1] === "string" && labelRename.has(n[1]))
|
|
4581
|
-
return [op, labelRename.get(n[1]), ...n.slice(2).map(sub)];
|
|
4582
|
-
if (inlIsBranch(op)) return [op, ...n.slice(1).map((c) => typeof c === "string" && labelRename.has(c) ? labelRename.get(c) : sub(c))];
|
|
4583
|
-
return n.map((c, i) => i === 0 ? c : sub(c));
|
|
4584
|
-
};
|
|
4585
|
-
const setup = params.map((p, k) => ["local.set", rename.get(p.name), args[k]]);
|
|
4586
|
-
const resets = locals.filter((l) => inlNeedsReset(cBody, l.name)).map((l) => ["local.set", rename.get(l.name), inlZeroFor(l.type)]);
|
|
4587
|
-
const inner = cBody.map(sub);
|
|
4588
|
-
const block = inlResult ? ["block", exit, ["result", inlResult], ...setup, ...resets, ...inner] : ["block", exit, ...setup, ...resets, ...inner];
|
|
4589
|
-
const decls = [...params, ...locals].map((p) => ["local", rename.get(p.name), p.type]);
|
|
4590
|
-
return { block, decls };
|
|
4591
|
-
};
|
|
4592
|
-
var INLINE_MAX_NODES = 90;
|
|
4593
|
-
var isV128SimdHelper = (params, inlResult) => inlResult === "v128" && params.length > 0 && params.every((p) => p.type === "v128");
|
|
4594
|
-
var inline = (ast, { simdOnly = false, pin = EMPTY_SET } = {}) => {
|
|
4595
|
-
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4596
|
-
const skip = /* @__PURE__ */ new Set();
|
|
4597
|
-
for (let round = 0; round < MAX_INLINE_ROUNDS; round++) {
|
|
4598
|
-
const funcs = ast.filter((n) => Array.isArray(n) && n[0] === "func");
|
|
4599
|
-
const funcByName = /* @__PURE__ */ new Map();
|
|
4600
|
-
for (const n of funcs) if (typeof n[1] === "string") funcByName.set(n[1], n);
|
|
4601
|
-
const callRefs = /* @__PURE__ */ new Map(), otherRef = /* @__PURE__ */ new Set();
|
|
4602
|
-
const countRefs = (n) => {
|
|
4603
|
-
if (!Array.isArray(n)) return;
|
|
4604
|
-
const op = n[0];
|
|
4605
|
-
if (op === "call" && typeof n[1] === "string") callRefs.set(n[1], (callRefs.get(n[1]) || 0) + 1);
|
|
4606
|
-
else if (op === "return_call" && typeof n[1] === "string") otherRef.add(n[1]);
|
|
4607
|
-
for (let i = 1; i < n.length; i++) countRefs(n[i]);
|
|
4608
|
-
};
|
|
4609
|
-
countRefs(ast);
|
|
4610
|
-
const pinned = /* @__PURE__ */ new Set();
|
|
4611
|
-
for (const n of ast) if (!Array.isArray(n) || n[0] !== "func") inlCollectPinned(n, pinned);
|
|
4612
|
-
let calleeName = null, parsed = null;
|
|
4613
|
-
for (const [name2, fn] of funcByName) {
|
|
4614
|
-
if (skip.has(name2) || pinned.has(name2) || otherRef.has(name2) || pin.has(name2)) continue;
|
|
4615
|
-
if (!(callRefs.get(name2) >= 1)) continue;
|
|
4616
|
-
if (inlBodySize(fn) > INLINE_MAX_NODES) continue;
|
|
4617
|
-
if (inlCallsSelf(fn, name2)) continue;
|
|
4618
|
-
const p = inlParse(fn);
|
|
4619
|
-
if (!p) continue;
|
|
4620
|
-
if (simdOnly && !isV128SimdHelper(p.params, p.inlResult)) continue;
|
|
4621
|
-
let bad = false;
|
|
4622
|
-
for (let i = inlBodyStart(fn); i < fn.length; i++) if (inlUnsafe(fn[i])) {
|
|
4623
|
-
bad = true;
|
|
4624
|
-
break;
|
|
4625
|
-
}
|
|
4626
|
-
if (bad) continue;
|
|
4627
|
-
calleeName = name2;
|
|
4628
|
-
parsed = p;
|
|
4629
|
-
break;
|
|
4630
|
-
}
|
|
4631
|
-
if (!calleeName) break;
|
|
4632
|
-
const callee = funcByName.get(calleeName);
|
|
4633
|
-
const { params, locals, inlResult } = parsed;
|
|
4634
|
-
const cBody = callee.slice(inlBodyStart(callee));
|
|
4635
|
-
const expected = callRefs.get(calleeName) || 0;
|
|
4636
|
-
let replaced = 0;
|
|
4637
|
-
for (const fn of funcs) {
|
|
4638
|
-
if (fn === callee) continue;
|
|
4639
|
-
const addDecls = [];
|
|
4640
|
-
for (let i = inlBodyStart(fn); i < fn.length; i++) {
|
|
4641
|
-
fn[i] = walkPost2(fn[i], (n) => {
|
|
4642
|
-
if (!Array.isArray(n) || n[0] !== "call" || n[1] !== calleeName) return;
|
|
4643
|
-
const args = n.slice(2);
|
|
4644
|
-
if (args.length !== params.length) return;
|
|
4645
|
-
const { block, decls } = buildInline(params, locals, inlResult, cBody, args);
|
|
4646
|
-
addDecls.push(...decls);
|
|
4647
|
-
replaced++;
|
|
4648
|
-
return block;
|
|
4649
|
-
});
|
|
4650
|
-
}
|
|
4651
|
-
if (addDecls.length) fn.splice(inlBodyStart(fn), 0, ...addDecls);
|
|
4652
|
-
}
|
|
4653
|
-
if (replaced === expected) {
|
|
4654
|
-
const idx = ast.indexOf(callee);
|
|
4655
|
-
if (idx >= 0) ast.splice(idx, 1);
|
|
4656
|
-
} else skip.add(calleeName);
|
|
4657
|
-
}
|
|
4658
|
-
return ast;
|
|
4659
|
-
};
|
|
4660
|
-
var devirt = (ast) => {
|
|
4661
|
-
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4662
|
-
const slots = /* @__PURE__ */ new Map(), typeDefs = /* @__PURE__ */ new Map(), funcsByName = /* @__PURE__ */ new Map(), allFuncs = [];
|
|
4663
|
-
let tableMutated = false;
|
|
4664
|
-
walk2(ast, (n) => {
|
|
4665
|
-
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;
|
|
4666
|
-
});
|
|
4667
|
-
if (tableMutated) return ast;
|
|
4668
|
-
for (const node of ast.slice(1)) {
|
|
4669
|
-
if (!Array.isArray(node)) continue;
|
|
4670
|
-
if (node[0] === "elem") {
|
|
4671
|
-
const off = node[1];
|
|
4672
|
-
if (!Array.isArray(off) || off[0] !== "i32.const") return ast;
|
|
4673
|
-
let base = Number(off[1]);
|
|
4674
|
-
for (let i = 2; i < node.length; i++)
|
|
4675
|
-
if (typeof node[i] === "string" && node[i][0] === "$") slots.set(base++, node[i]);
|
|
4676
|
-
} else if (node[0] === "type" && typeof node[1] === "string") typeDefs.set(node[1], node[2]);
|
|
4677
|
-
else if (node[0] === "func") {
|
|
4678
|
-
allFuncs.push(node);
|
|
4679
|
-
if (typeof node[1] === "string") funcsByName.set(node[1], node);
|
|
4680
|
-
}
|
|
4681
|
-
}
|
|
4682
|
-
if (!slots.size) return ast;
|
|
4683
|
-
const globalCands = /* @__PURE__ */ new Map();
|
|
4684
|
-
const isC64 = (n, hex) => Array.isArray(n) && n[0] === "i64.const" && _i64Canon(n[1]) === hex;
|
|
4685
|
-
const MASK15 = "0x0000000000007fff", SHIFT32 = "0x0000000000000020";
|
|
4686
|
-
const boxConsts = (v, out) => {
|
|
4687
|
-
if (!Array.isArray(v)) return false;
|
|
4688
|
-
if (v[0] === "i64.const") {
|
|
4689
|
-
out.push(v);
|
|
4690
|
-
return true;
|
|
4691
|
-
}
|
|
4692
|
-
if (v[0] === "f64.const" && typeof v[1] === "string" && v[1].startsWith("nan:")) {
|
|
4693
|
-
out.push(["i64.const", _i64Canon(v[1].slice(4))]);
|
|
4694
|
-
return true;
|
|
4695
|
-
}
|
|
4696
|
-
if (v[0] === "f64.reinterpret_i64" && v.length === 2) return boxConsts(v[1], out);
|
|
4697
|
-
if (v[0] === "select" && v.length === 4) return boxConsts(v[1], out) && boxConsts(v[2], out);
|
|
4698
|
-
return false;
|
|
4699
|
-
};
|
|
4700
|
-
const globalWrites = /* @__PURE__ */ new Map();
|
|
4701
|
-
walk2(ast, (n) => {
|
|
4702
|
-
if (!Array.isArray(n) || n[0] !== "global.set" || typeof n[1] !== "string") return;
|
|
4703
|
-
if (!globalWrites.has(n[1])) globalWrites.set(n[1], []);
|
|
4704
|
-
globalWrites.get(n[1]).push(n[2]);
|
|
4705
|
-
});
|
|
4706
|
-
const candLeaves = (v, consts, reads) => {
|
|
4707
|
-
if (!Array.isArray(v)) return false;
|
|
4708
|
-
if (v[0] === "i64.const") {
|
|
4709
|
-
consts.push(v);
|
|
4710
|
-
return true;
|
|
4711
|
-
}
|
|
4712
|
-
if (v[0] === "f64.const" && typeof v[1] === "string" && v[1].startsWith("nan:")) {
|
|
4713
|
-
consts.push(["i64.const", _i64Canon(v[1].slice(4))]);
|
|
4714
|
-
return true;
|
|
4715
|
-
}
|
|
4716
|
-
if ((v[0] === "f64.reinterpret_i64" || v[0] === "i64.reinterpret_f64") && v.length === 2)
|
|
4717
|
-
return candLeaves(v[1], consts, reads);
|
|
4718
|
-
if (v[0] === "global.get" && typeof v[1] === "string") {
|
|
4719
|
-
reads.push(v[1]);
|
|
4720
|
-
return true;
|
|
4721
|
-
}
|
|
4722
|
-
if (v[0] === "local.get" || v[0] === "local.tee") {
|
|
4723
|
-
return v[0] === "local.tee" && v.length === 3 ? candLeaves(v[2], consts, reads) : false;
|
|
4724
|
-
}
|
|
4725
|
-
if (v[0] === "select" && v.length === 4)
|
|
4726
|
-
return candLeaves(v[1], consts, reads) && candLeaves(v[2], consts, reads);
|
|
4727
|
-
if (v[0] === "if") {
|
|
4728
|
-
let ok = true, seenArm = false;
|
|
4729
|
-
for (let i = 1; i < v.length; i++) {
|
|
4730
|
-
const p = v[i];
|
|
4731
|
-
if (!Array.isArray(p)) continue;
|
|
4732
|
-
if (p[0] === "then" || p[0] === "else") {
|
|
4733
|
-
seenArm = true;
|
|
4734
|
-
if (p.length !== 2 || !candLeaves(p[1], consts, reads)) ok = false;
|
|
4735
|
-
}
|
|
4736
|
-
}
|
|
4737
|
-
return ok && seenArm;
|
|
4738
|
-
}
|
|
4739
|
-
return false;
|
|
4740
|
-
};
|
|
4741
|
-
const writeFacts = /* @__PURE__ */ new Map();
|
|
4742
|
-
for (const [g, ws] of globalWrites) {
|
|
4743
|
-
let consts = [], reads = [], ok = true;
|
|
4744
|
-
for (const w of ws) if (!candLeaves(w, consts, reads)) {
|
|
4745
|
-
ok = false;
|
|
4746
|
-
break;
|
|
4747
|
-
}
|
|
4748
|
-
writeFacts.set(g, ok ? { consts, reads } : null);
|
|
4749
|
-
}
|
|
4750
|
-
let changed = true;
|
|
4751
|
-
const resolved = /* @__PURE__ */ new Map();
|
|
4752
|
-
while (changed) {
|
|
4753
|
-
changed = false;
|
|
4754
|
-
for (const [g, f] of writeFacts) {
|
|
4755
|
-
if (resolved.get(g) === null) continue;
|
|
4756
|
-
if (f === null) {
|
|
4757
|
-
if (resolved.get(g) !== null) {
|
|
4758
|
-
resolved.set(g, null);
|
|
4759
|
-
changed = true;
|
|
4760
|
-
}
|
|
4761
|
-
continue;
|
|
4762
|
-
}
|
|
4763
|
-
const m = resolved.get(g) || /* @__PURE__ */ new Map();
|
|
4764
|
-
const before = m.size;
|
|
4765
|
-
let poisoned = false;
|
|
4766
|
-
for (const c of f.consts) m.set(_i64Canon(c[1]), c);
|
|
4767
|
-
for (const r of f.reads) {
|
|
4768
|
-
if (writeFacts.get(r) === null || resolved.get(r) === null) {
|
|
4769
|
-
poisoned = true;
|
|
4770
|
-
break;
|
|
4771
|
-
}
|
|
4772
|
-
const rm = resolved.get(r);
|
|
4773
|
-
if (rm) for (const [hex, c] of rm) m.set(hex, c);
|
|
4774
|
-
}
|
|
4775
|
-
if (poisoned) {
|
|
4776
|
-
resolved.set(g, null);
|
|
4777
|
-
changed = true;
|
|
4778
|
-
continue;
|
|
4779
|
-
}
|
|
4780
|
-
if (!resolved.has(g) || m.size !== before) {
|
|
4781
|
-
resolved.set(g, m);
|
|
4782
|
-
changed = true;
|
|
4783
|
-
}
|
|
4784
|
-
}
|
|
4785
|
-
}
|
|
4786
|
-
for (const [g, m] of resolved) globalCands.set(g, m);
|
|
4787
|
-
const matchSlotOfLocal = (e) => {
|
|
4788
|
-
if (!Array.isArray(e) || e[0] !== "i32.wrap_i64") return null;
|
|
4789
|
-
const a = e[1];
|
|
4790
|
-
if (!Array.isArray(a) || a[0] !== "i64.and") return null;
|
|
4791
|
-
let sh = a[1], mk = a[2];
|
|
4792
|
-
if (!isC64(mk, MASK15)) {
|
|
4793
|
-
sh = a[2];
|
|
4794
|
-
mk = a[1];
|
|
4795
|
-
}
|
|
4796
|
-
if (!isC64(mk, MASK15) || !Array.isArray(sh) || sh[0] !== "i64.shr_u" || !isC64(sh[2], SHIFT32)) return null;
|
|
4797
|
-
const ri = sh[1];
|
|
4798
|
-
if (!Array.isArray(ri) || ri[0] !== "i64.reinterpret_f64") return null;
|
|
4799
|
-
const leaf = ri[1];
|
|
4800
|
-
if (Array.isArray(leaf) && leaf[0] === "local.get" && typeof leaf[1] === "string") return { local: leaf[1] };
|
|
4801
|
-
if (Array.isArray(leaf) && leaf[0] === "global.get" && typeof leaf[1] === "string") return { global: leaf[1] };
|
|
4802
|
-
return null;
|
|
4803
|
-
};
|
|
4804
|
-
const tokSig = (parts) => {
|
|
4805
|
-
const ps = [], rs = [];
|
|
4806
|
-
for (const p of parts) {
|
|
4807
|
-
if (!Array.isArray(p)) continue;
|
|
4808
|
-
if (p[0] === "param") {
|
|
4809
|
-
for (const t of p.slice(1)) if (typeof t === "string" && t[0] !== "$") ps.push(t);
|
|
4810
|
-
} else if (p[0] === "result") rs.push(...p.slice(1));
|
|
4811
|
-
}
|
|
4812
|
-
return ps.join(",") + "->" + rs.join(",");
|
|
4813
|
-
};
|
|
4814
|
-
for (const fn of allFuncs) {
|
|
4815
|
-
const cands = /* @__PURE__ */ new Map();
|
|
4816
|
-
for (const part of fn)
|
|
4817
|
-
if (Array.isArray(part) && part[0] === "param" && typeof part[1] === "string") cands.set(part[1], null);
|
|
4818
|
-
walk2(fn, (n) => {
|
|
4819
|
-
if (!Array.isArray(n) || n[0] !== "local.set" && n[0] !== "local.tee" || typeof n[1] !== "string") return;
|
|
4820
|
-
if (cands.get(n[1]) === null) return;
|
|
4821
|
-
const out = [];
|
|
4822
|
-
if (boxConsts(n[2], out)) {
|
|
4823
|
-
const m = cands.get(n[1]) || /* @__PURE__ */ new Map();
|
|
4824
|
-
for (const c of out) m.set(_i64Canon(c[1]), c);
|
|
4825
|
-
cands.set(n[1], m);
|
|
4826
|
-
} else if (Array.isArray(n[2]) && n[2][0] === "global.get" && typeof n[2][1] === "string" && globalCands.get(n[2][1])) {
|
|
4827
|
-
const g = globalCands.get(n[2][1]);
|
|
4828
|
-
const m = cands.get(n[1]) || /* @__PURE__ */ new Map();
|
|
4829
|
-
for (const [hex, c] of g) m.set(hex, c);
|
|
4830
|
-
cands.set(n[1], m);
|
|
4831
|
-
} else cands.set(n[1], null);
|
|
4832
|
-
});
|
|
4833
|
-
walkPost2(fn, (n, parent) => {
|
|
4834
|
-
if (!Array.isArray(n) || n[0] !== "call_indirect") return;
|
|
4835
|
-
if (parent && parent[0] === "else") return;
|
|
4836
|
-
const typeUse = Array.isArray(n[1]) && n[1][0] === "type" ? n[1] : null;
|
|
4837
|
-
if (!typeUse) return;
|
|
4838
|
-
const sig = typeDefs.get(typeUse[1]);
|
|
4839
|
-
const callSig = Array.isArray(sig) ? tokSig(sig.slice(1)) : null;
|
|
4840
|
-
if (callSig == null) return;
|
|
4841
|
-
const results = [];
|
|
4842
|
-
for (const s of sig.slice(1)) if (Array.isArray(s) && s[0] === "result") results.push(...s.slice(1));
|
|
4843
|
-
const args = n.slice(2, -1);
|
|
4844
|
-
const idx = n[n.length - 1];
|
|
4845
|
-
const sigOk = (name2) => {
|
|
4846
|
-
const target = funcsByName.get(name2);
|
|
4847
|
-
if (!target) return false;
|
|
4848
|
-
const tu = target.find((p) => Array.isArray(p) && p[0] === "type");
|
|
4849
|
-
if (tu) return tu[1] === typeUse[1] || typeDefs.get(tu[1]) && tokSig(typeDefs.get(tu[1]).slice(1)) === callSig;
|
|
4850
|
-
return tokSig(target.slice(2)) === callSig;
|
|
4851
|
-
};
|
|
4852
|
-
if (Array.isArray(idx) && idx[0] === "i32.const") {
|
|
4853
|
-
const name2 = slots.get(Number(idx[1]));
|
|
4854
|
-
return name2 && sigOk(name2) ? ["call", name2, ...args] : void 0;
|
|
4855
|
-
}
|
|
4856
|
-
const f = matchSlotOfLocal(idx);
|
|
4857
|
-
if (!f) return;
|
|
4858
|
-
const m = f.local != null ? cands.get(f.local) : globalCands.get(f.global);
|
|
4859
|
-
if (!m || m.size === 0 || m.size > 4) return;
|
|
4860
|
-
const arms = [];
|
|
4861
|
-
for (const cNode of m.values()) {
|
|
4862
|
-
const name2 = slots.get(_i64HiU(_i64Canon(cNode[1])) & 32767);
|
|
4863
|
-
if (!name2 || !sigOk(name2)) return;
|
|
4864
|
-
arms.push([cNode, name2]);
|
|
4865
|
-
}
|
|
4866
|
-
const readBack = f.local != null ? ["local.get", f.local] : ["global.get", f.global];
|
|
4867
|
-
let out = n;
|
|
4868
|
-
for (let i = arms.length - 1; i >= 0; i--) {
|
|
4869
|
-
const [cNode, name2] = arms[i];
|
|
4870
|
-
out = [
|
|
4871
|
-
"if",
|
|
4872
|
-
...results.length ? [["result", ...results]] : [],
|
|
4873
|
-
["i64.eq", ["i64.reinterpret_f64", clone2(readBack)], clone2(cNode)],
|
|
4874
|
-
["then", ["call", name2, ...args.map(clone2)]],
|
|
4875
|
-
["else", out]
|
|
4876
|
-
];
|
|
4877
|
-
}
|
|
4878
|
-
return out;
|
|
4879
|
-
});
|
|
4880
|
-
}
|
|
4881
|
-
return ast;
|
|
4882
|
-
};
|
|
4883
|
-
var inlineOnce = (ast, { pin = EMPTY_SET } = {}) => {
|
|
4884
|
-
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4885
|
-
const bodyStart = inlBodyStart, callsSelf = inlCallsSelf, unsafe = inlUnsafe, isBranch = inlIsBranch;
|
|
4886
|
-
const zeroFor = inlZeroFor, needsReset = inlNeedsReset;
|
|
4887
|
-
for (let round = 0; round < MAX_INLINE_ROUNDS; round++) {
|
|
4888
|
-
const funcs = ast.filter((n) => Array.isArray(n) && n[0] === "func");
|
|
4889
|
-
const funcByName = /* @__PURE__ */ new Map();
|
|
4890
|
-
for (const n of funcs) if (typeof n[1] === "string") funcByName.set(n[1], n);
|
|
4891
|
-
const callRefs = /* @__PURE__ */ new Map(), otherRef = /* @__PURE__ */ new Set();
|
|
4892
|
-
const countRefs = (n) => {
|
|
4893
|
-
if (!Array.isArray(n)) return;
|
|
4894
|
-
const op = n[0];
|
|
4895
|
-
if (op === "call" && typeof n[1] === "string") callRefs.set(n[1], (callRefs.get(n[1]) || 0) + 1);
|
|
4896
|
-
else if (op === "return_call" && typeof n[1] === "string") otherRef.add(n[1]);
|
|
4897
|
-
for (let i = 1; i < n.length; i++) countRefs(n[i]);
|
|
4898
|
-
};
|
|
4899
|
-
countRefs(ast);
|
|
4900
|
-
const pinned = inlBuildPinned(ast);
|
|
4901
|
-
let calleeName = null;
|
|
4902
|
-
for (const [name2, fn] of funcByName) {
|
|
4903
|
-
if (pinned.has(name2) || otherRef.has(name2)) continue;
|
|
4904
|
-
if (callRefs.get(name2) !== 1) continue;
|
|
4905
|
-
if (pin.has(name2)) continue;
|
|
4906
|
-
if (callsSelf(fn, name2)) continue;
|
|
4907
|
-
let ok = true, nResult = 0;
|
|
4908
|
-
for (let i = 2; i < fn.length; i++) {
|
|
4909
|
-
const c = fn[i];
|
|
4910
|
-
if (typeof c === "string") continue;
|
|
4911
|
-
if (!Array.isArray(c)) {
|
|
4912
|
-
ok = false;
|
|
4913
|
-
break;
|
|
4914
|
-
}
|
|
4915
|
-
if (c[0] === "param" || c[0] === "local") {
|
|
4916
|
-
if (typeof c[1] !== "string" || c[1][0] !== "$") {
|
|
4917
|
-
ok = false;
|
|
4918
|
-
break;
|
|
4919
|
-
}
|
|
4920
|
-
if (c[0] === "local" && !zeroFor(c[2])) {
|
|
4921
|
-
ok = false;
|
|
4922
|
-
break;
|
|
4923
|
-
}
|
|
4924
|
-
} else if (c[0] === "result") nResult += c.length - 1;
|
|
4925
|
-
else if (c[0] === "export") {
|
|
4926
|
-
ok = false;
|
|
4927
|
-
break;
|
|
4928
|
-
} else if (c[0] === "type") continue;
|
|
4929
|
-
else break;
|
|
4930
|
-
}
|
|
4931
|
-
if (!ok || nResult > 1) continue;
|
|
4932
|
-
let bad = false;
|
|
4933
|
-
for (let i = bodyStart(fn); i < fn.length; i++) if (unsafe(fn[i])) {
|
|
4934
|
-
bad = true;
|
|
4935
|
-
break;
|
|
4936
|
-
}
|
|
4937
|
-
if (bad) continue;
|
|
4938
|
-
calleeName = name2;
|
|
4939
|
-
break;
|
|
4940
|
-
}
|
|
4941
|
-
if (!calleeName) break;
|
|
4942
|
-
const callee = funcByName.get(calleeName);
|
|
4943
|
-
const params = [], locals = [];
|
|
4944
|
-
let inlResult = null;
|
|
4945
|
-
for (let i = 2; i < callee.length; i++) {
|
|
4946
|
-
const c = callee[i];
|
|
4947
|
-
if (typeof c === "string" || !Array.isArray(c)) continue;
|
|
4948
|
-
if (c[0] === "param") params.push({ name: c[1], type: c[2] });
|
|
4949
|
-
else if (c[0] === "result") {
|
|
4950
|
-
if (c.length > 1) inlResult = c[1];
|
|
4951
|
-
} else if (c[0] === "local") locals.push({ name: c[1], type: c[2] });
|
|
4952
|
-
else if (c[0] === "export" || c[0] === "type") continue;
|
|
4953
|
-
else break;
|
|
4954
|
-
}
|
|
4955
|
-
const cBody = callee.slice(bodyStart(callee));
|
|
4956
|
-
const uid2 = ++inlineUid;
|
|
4957
|
-
const exit = `$__inl${uid2}`;
|
|
4958
|
-
const rename = /* @__PURE__ */ new Map();
|
|
4959
|
-
for (const p of params) rename.set(p.name, `$__inl${uid2}_${p.name.slice(1)}`);
|
|
4960
|
-
for (const l of locals) rename.set(l.name, `$__inl${uid2}_${l.name.slice(1)}`);
|
|
4961
|
-
const labelRename = /* @__PURE__ */ new Map();
|
|
4962
|
-
const collectLabels = (n) => {
|
|
4963
|
-
if (!Array.isArray(n)) return;
|
|
4964
|
-
if (isBranchScope(n[0]) && typeof n[1] === "string" && n[1][0] === "$" && !labelRename.has(n[1]))
|
|
4965
|
-
labelRename.set(n[1], `$__inl${uid2}L_${n[1].slice(1)}`);
|
|
4966
|
-
for (let i = 1; i < n.length; i++) collectLabels(n[i]);
|
|
4967
|
-
};
|
|
4968
|
-
for (const n of cBody) collectLabels(n);
|
|
4969
|
-
const sub = (n) => {
|
|
4970
|
-
if (!Array.isArray(n)) return n;
|
|
4971
|
-
const op = n[0];
|
|
4972
|
-
if ((op === "local.get" || op === "local.set" || op === "local.tee") && typeof n[1] === "string" && rename.has(n[1]))
|
|
4973
|
-
return [op, rename.get(n[1]), ...n.slice(2).map(sub)];
|
|
4974
|
-
if (op === "return") return ["br", exit, ...n.slice(1).map(sub)];
|
|
4975
|
-
if (isBranchScope(op) && typeof n[1] === "string" && labelRename.has(n[1]))
|
|
4976
|
-
return [op, labelRename.get(n[1]), ...n.slice(2).map(sub)];
|
|
4977
|
-
if (isBranch(op)) return [op, ...n.slice(1).map((c) => typeof c === "string" && labelRename.has(c) ? labelRename.get(c) : sub(c))];
|
|
4978
|
-
return n.map((c, i) => i === 0 ? c : sub(c));
|
|
4979
|
-
};
|
|
4980
|
-
let done = false;
|
|
4981
|
-
for (const fn of funcs) {
|
|
4982
|
-
if (fn === callee || done) continue;
|
|
4983
|
-
const start = bodyStart(fn);
|
|
4984
|
-
for (let i = start; i < fn.length; i++) {
|
|
4985
|
-
const replaced = walkPost2(fn[i], (n) => {
|
|
4986
|
-
if (done || !Array.isArray(n) || n[0] !== "call" || n[1] !== calleeName) return;
|
|
4987
|
-
const args = n.slice(2);
|
|
4988
|
-
if (args.length !== params.length) return;
|
|
4989
|
-
const setup = params.map((p, k) => ["local.set", rename.get(p.name), args[k]]);
|
|
4990
|
-
const resets = locals.filter((l) => needsReset(cBody, l.name)).map((l) => ["local.set", rename.get(l.name), zeroFor(l.type)]);
|
|
4991
|
-
const inner = cBody.map(sub);
|
|
4992
|
-
done = true;
|
|
4993
|
-
return inlResult ? ["block", exit, ["result", inlResult], ...setup, ...resets, ...inner] : ["block", exit, ...setup, ...resets, ...inner];
|
|
4994
|
-
});
|
|
4995
|
-
if (replaced !== fn[i]) fn[i] = replaced;
|
|
4996
|
-
if (done) {
|
|
4997
|
-
const decls = [...params, ...locals].map((p) => ["local", rename.get(p.name), p.type]);
|
|
4998
|
-
if (decls.length) fn.splice(bodyStart(fn), 0, ...decls);
|
|
4999
|
-
break;
|
|
5000
|
-
}
|
|
5001
|
-
}
|
|
5002
|
-
if (done) break;
|
|
5003
|
-
}
|
|
5004
|
-
if (!done) break;
|
|
5005
|
-
const idx = ast.indexOf(callee);
|
|
5006
|
-
if (idx >= 0) ast.splice(idx, 1);
|
|
5007
|
-
}
|
|
5008
|
-
return ast;
|
|
5009
|
-
};
|
|
5010
|
-
var targetsLabel = (body, label) => {
|
|
5011
|
-
let found = false;
|
|
5012
|
-
const search = (n, shadowed) => {
|
|
5013
|
-
if (found || !Array.isArray(n)) return;
|
|
5014
|
-
const op = n[0];
|
|
5015
|
-
let inner = shadowed;
|
|
5016
|
-
if ((op === "block" || op === "loop") && typeof n[1] === "string" && n[1] === label) inner = true;
|
|
5017
|
-
if (!shadowed) {
|
|
5018
|
-
if (op === "br" || op === "br_if" || op === "br_on_null" || op === "br_on_non_null" || op === "br_on_cast" || op === "br_on_cast_fail") {
|
|
5019
|
-
if (n[1] === label) {
|
|
5020
|
-
found = true;
|
|
5021
|
-
return;
|
|
5022
|
-
}
|
|
5023
|
-
} else if (op === "br_table") {
|
|
5024
|
-
for (let j = 1; j < n.length; j++) {
|
|
5025
|
-
if (typeof n[j] === "string") {
|
|
5026
|
-
if (n[j] === label) {
|
|
5027
|
-
found = true;
|
|
5028
|
-
return;
|
|
5029
|
-
}
|
|
5030
|
-
} else break;
|
|
5031
|
-
}
|
|
5032
|
-
} else if (op === "catch" || op === "catch_ref") {
|
|
5033
|
-
if (n[2] === label) {
|
|
5034
|
-
found = true;
|
|
5035
|
-
return;
|
|
5036
|
-
}
|
|
5037
|
-
} else if (op === "catch_all" || op === "catch_all_ref") {
|
|
5038
|
-
if (n[1] === label) {
|
|
5039
|
-
found = true;
|
|
5040
|
-
return;
|
|
5041
|
-
}
|
|
5042
|
-
}
|
|
5043
|
-
}
|
|
5044
|
-
for (let i = 1; i < n.length; i++) search(n[i], inner);
|
|
5045
|
-
};
|
|
5046
|
-
for (const node of body) search(node, false);
|
|
5047
|
-
return found;
|
|
5048
|
-
};
|
|
5049
|
-
var mergeBlocks = (ast) => {
|
|
5050
|
-
walkPost2(ast, (node) => {
|
|
5051
|
-
if (!Array.isArray(node) || node[0] !== "block") return;
|
|
5052
|
-
let bi = 1, label = null;
|
|
5053
|
-
if (typeof node[1] === "string" && node[1][0] === "$") {
|
|
5054
|
-
label = node[1];
|
|
5055
|
-
bi = 2;
|
|
5056
|
-
}
|
|
5057
|
-
let hasResult = false;
|
|
5058
|
-
while (bi < node.length) {
|
|
5059
|
-
const c = node[bi];
|
|
5060
|
-
if (Array.isArray(c) && (c[0] === "param" || c[0] === "type")) {
|
|
5061
|
-
bi++;
|
|
5062
|
-
continue;
|
|
5063
|
-
}
|
|
5064
|
-
if (Array.isArray(c) && c[0] === "result") {
|
|
5065
|
-
hasResult = true;
|
|
5066
|
-
bi++;
|
|
5067
|
-
continue;
|
|
5068
|
-
}
|
|
5069
|
-
break;
|
|
5070
|
-
}
|
|
5071
|
-
const body = node.slice(bi);
|
|
5072
|
-
if (!hasResult || body.length !== 1) return;
|
|
5073
|
-
const only = body[0];
|
|
5074
|
-
if (!Array.isArray(only)) return;
|
|
5075
|
-
if (label && targetsLabel(body, label)) return;
|
|
5076
|
-
node.length = 0;
|
|
5077
|
-
for (const tok of only) node.push(tok);
|
|
5078
|
-
});
|
|
5079
|
-
walk2(ast, (node) => {
|
|
5080
|
-
if (!isScopeNode(node)) return;
|
|
5081
|
-
let i = 1;
|
|
5082
|
-
while (i < node.length) {
|
|
5083
|
-
const child = node[i];
|
|
5084
|
-
if (!Array.isArray(child)) {
|
|
5085
|
-
i++;
|
|
5086
|
-
continue;
|
|
5087
|
-
}
|
|
5088
|
-
{
|
|
5089
|
-
const cop = child[0];
|
|
5090
|
-
const oi = cop === "local.set" || cop === "global.set" ? 2 : cop === "drop" ? 1 : -1;
|
|
5091
|
-
if (oi >= 0 && child.length === oi + 1) {
|
|
5092
|
-
const operand = child[oi];
|
|
5093
|
-
if (Array.isArray(operand) && operand[0] === "block") {
|
|
5094
|
-
let bi2 = 1, label2 = null;
|
|
5095
|
-
if (typeof operand[1] === "string" && operand[1][0] === "$") {
|
|
5096
|
-
label2 = operand[1];
|
|
5097
|
-
bi2 = 2;
|
|
5098
|
-
}
|
|
5099
|
-
let hasResult = false;
|
|
5100
|
-
while (bi2 < operand.length) {
|
|
5101
|
-
const c = operand[bi2];
|
|
5102
|
-
if (Array.isArray(c) && (c[0] === "param" || c[0] === "type")) {
|
|
5103
|
-
bi2++;
|
|
5104
|
-
continue;
|
|
5105
|
-
}
|
|
5106
|
-
if (Array.isArray(c) && c[0] === "result") {
|
|
5107
|
-
hasResult = true;
|
|
5108
|
-
bi2++;
|
|
5109
|
-
continue;
|
|
5110
|
-
}
|
|
5111
|
-
break;
|
|
5112
|
-
}
|
|
5113
|
-
const body2 = hasResult ? operand.slice(bi2) : null;
|
|
5114
|
-
if (body2 && body2.length >= 2 && !(label2 && targetsLabel(body2, label2))) {
|
|
5115
|
-
const expr2 = body2[body2.length - 1];
|
|
5116
|
-
const setup = body2.slice(0, -1);
|
|
5117
|
-
child[oi] = expr2;
|
|
5118
|
-
node.splice(i, 1, ...setup, child);
|
|
5119
|
-
continue;
|
|
5120
|
-
}
|
|
5121
|
-
}
|
|
5122
|
-
}
|
|
5123
|
-
}
|
|
5124
|
-
if (child[0] !== "block") {
|
|
5125
|
-
i++;
|
|
5126
|
-
continue;
|
|
5127
|
-
}
|
|
5128
|
-
let bi = 1, label = null;
|
|
5129
|
-
if (typeof child[1] === "string" && child[1][0] === "$") {
|
|
5130
|
-
label = child[1];
|
|
5131
|
-
bi = 2;
|
|
5132
|
-
}
|
|
5133
|
-
while (bi < child.length) {
|
|
5134
|
-
const c = child[bi];
|
|
5135
|
-
if (Array.isArray(c) && (c[0] === "param" || c[0] === "result" || c[0] === "type")) {
|
|
5136
|
-
bi++;
|
|
5137
|
-
continue;
|
|
5138
|
-
}
|
|
5139
|
-
break;
|
|
5140
|
-
}
|
|
5141
|
-
const body = child.slice(bi);
|
|
5142
|
-
if (label && targetsLabel(body, label)) {
|
|
5143
|
-
i++;
|
|
5144
|
-
continue;
|
|
5145
|
-
}
|
|
5146
|
-
node.splice(i, 1, ...body);
|
|
5147
|
-
i += body.length;
|
|
5148
|
-
}
|
|
5149
|
-
});
|
|
5150
|
-
return ast;
|
|
5151
|
-
};
|
|
5152
|
-
var coalesceLocals = (ast) => {
|
|
5153
|
-
walk2(ast, (funcNode) => {
|
|
5154
|
-
if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
|
|
5155
|
-
const decls = /* @__PURE__ */ new Map();
|
|
5156
|
-
for (const sub of funcNode) {
|
|
5157
|
-
if (Array.isArray(sub) && sub[0] === "local" && typeof sub[1] === "string" && sub[1][0] === "$" && typeof sub[2] === "string") {
|
|
5158
|
-
decls.set(sub[1], sub[2]);
|
|
5159
|
-
}
|
|
5160
|
-
}
|
|
5161
|
-
if (decls.size < 2) return;
|
|
5162
|
-
const uses = /* @__PURE__ */ new Map();
|
|
5163
|
-
const loopStack = [];
|
|
5164
|
-
let pos = 0, abort = false, condDepth = 0;
|
|
5165
|
-
const visit = (n) => {
|
|
5166
|
-
if (abort || !Array.isArray(n)) return;
|
|
5167
|
-
const op = n[0];
|
|
5168
|
-
const isLoop = op === "loop";
|
|
5169
|
-
if (isLoop) loopStack.push({ start: pos, end: pos });
|
|
5170
|
-
const isSet = op === "local.set" || op === "local.tee";
|
|
5171
|
-
if (isSet || op === "local.get") {
|
|
5172
|
-
const name2 = n[1];
|
|
5173
|
-
if (typeof name2 !== "string" || name2[0] !== "$") {
|
|
5174
|
-
abort = true;
|
|
5175
|
-
return;
|
|
5176
|
-
}
|
|
5177
|
-
if (isSet) for (let i = 2; i < n.length; i++) visit(n[i]);
|
|
5178
|
-
const here = pos++;
|
|
5179
|
-
if (decls.has(name2)) {
|
|
5180
|
-
let u = uses.get(name2);
|
|
5181
|
-
if (!u) {
|
|
5182
|
-
u = { start: here, end: here, firstOp: op, firstCond: condDepth > 0, loops: /* @__PURE__ */ new Set() };
|
|
5183
|
-
uses.set(name2, u);
|
|
5184
|
-
}
|
|
5185
|
-
if (here > u.end) u.end = here;
|
|
5186
|
-
for (const ls of loopStack) u.loops.add(ls);
|
|
5187
|
-
}
|
|
5188
|
-
} else {
|
|
5189
|
-
pos++;
|
|
5190
|
-
const isIf = op === "if";
|
|
5191
|
-
for (let i = 1; i < n.length; i++) {
|
|
5192
|
-
const c = n[i];
|
|
5193
|
-
const cond = isIf && Array.isArray(c) && (c[0] === "then" || c[0] === "else");
|
|
5194
|
-
if (cond) condDepth++;
|
|
5195
|
-
visit(c);
|
|
5196
|
-
if (cond) condDepth--;
|
|
5197
|
-
}
|
|
5198
|
-
}
|
|
5199
|
-
if (isLoop) {
|
|
5200
|
-
const ls = loopStack.pop();
|
|
5201
|
-
ls.end = pos;
|
|
5202
|
-
}
|
|
5203
|
-
};
|
|
5204
|
-
visit(funcNode);
|
|
5205
|
-
if (abort) return;
|
|
5206
|
-
for (const u of uses.values()) {
|
|
5207
|
-
for (const ls of u.loops) {
|
|
5208
|
-
if (ls.start < u.start) u.start = ls.start;
|
|
5209
|
-
if (ls.end > u.end) u.end = ls.end;
|
|
5210
|
-
}
|
|
5211
|
-
}
|
|
5212
|
-
const ordered = [...uses.entries()].sort((a, b) => a[1].start - b[1].start);
|
|
5213
|
-
const rename = /* @__PURE__ */ new Map();
|
|
5214
|
-
const slots = [];
|
|
5215
|
-
for (const [name2, range] of ordered) {
|
|
5216
|
-
const readsZero = range.firstOp === "local.get" || range.firstCond;
|
|
5217
|
-
const type = decls.get(name2);
|
|
5218
|
-
const slot = readsZero ? null : slots.find((s) => s.type === type && s.end < range.start);
|
|
5219
|
-
if (slot) {
|
|
5220
|
-
rename.set(name2, slot.primary);
|
|
5221
|
-
if (range.end > slot.end) slot.end = range.end;
|
|
5222
|
-
} else slots.push({ primary: name2, type, end: range.end });
|
|
5223
|
-
}
|
|
5224
|
-
if (rename.size === 0) return;
|
|
5225
|
-
walk2(funcNode, (n) => {
|
|
5226
|
-
if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.set" || n[0] === "local.tee") && rename.has(n[1])) {
|
|
5227
|
-
n[1] = rename.get(n[1]);
|
|
5228
|
-
}
|
|
5229
|
-
});
|
|
5230
|
-
});
|
|
5231
|
-
return ast;
|
|
5232
|
-
};
|
|
5233
|
-
var vacuum = (ast) => {
|
|
5234
|
-
return walkPost2(ast, (node) => {
|
|
5235
|
-
if (!Array.isArray(node)) return;
|
|
5236
|
-
const op = node[0];
|
|
5237
|
-
if (op === "nop") return ["nop"];
|
|
5238
|
-
if (op === "drop" && node.length === 2) {
|
|
5239
|
-
const eff = dropEffects(node[1]);
|
|
5240
|
-
if (eff.length === 0) return ["nop"];
|
|
5241
|
-
if (eff.length === 1) return eff[0];
|
|
5242
|
-
return ["block", ...eff];
|
|
5243
|
-
}
|
|
5244
|
-
if (op === "select" && node.length >= 4 && equal(node[1], node[2]) && isPure(node[3])) return node[1];
|
|
5245
|
-
if (op === "if") {
|
|
5246
|
-
const { cond, thenBranch, elseBranch } = parseIf(node);
|
|
5247
|
-
const thenEmpty = !thenBranch || thenBranch.length <= 1;
|
|
5248
|
-
const elseEmpty = !elseBranch || elseBranch.length <= 1;
|
|
5249
|
-
if (thenEmpty && elseEmpty) return isPure(cond) ? ["nop"] : ["drop", cond];
|
|
5250
|
-
if (elseBranch && elseEmpty && !thenEmpty) {
|
|
5251
|
-
return node.filter((c) => c !== elseBranch);
|
|
5252
|
-
}
|
|
5253
|
-
}
|
|
5254
|
-
if (isScopeNode(node)) {
|
|
5255
|
-
const cleaned = [op];
|
|
5256
|
-
for (let i = 1; i < node.length; i++) {
|
|
5257
|
-
const child = node[i];
|
|
5258
|
-
if (child === "nop" || Array.isArray(child) && child[0] === "nop") continue;
|
|
5259
|
-
const next = node[i + 1];
|
|
5260
|
-
const isDrop = next === "drop" || Array.isArray(next) && next[0] === "drop" && next.length === 1;
|
|
5261
|
-
if (Array.isArray(child) && isDrop && isPure(child)) {
|
|
5262
|
-
i++;
|
|
5263
|
-
continue;
|
|
5264
|
-
}
|
|
5265
|
-
if (Array.isArray(child) && isDrop && child[0] === "local.tee" && child.length === 3) {
|
|
5266
|
-
cleaned.push(["local.set", child[1], child[2]]);
|
|
5267
|
-
i++;
|
|
5268
|
-
continue;
|
|
5269
|
-
}
|
|
5270
|
-
cleaned.push(child);
|
|
5271
|
-
}
|
|
5272
|
-
if (cleaned.length !== node.length) return cleaned;
|
|
5273
|
-
}
|
|
5274
|
-
});
|
|
5275
|
-
};
|
|
5276
|
-
var selfFold = (val) => (a, b) => equal(a, b) && isPure(a) ? val : null;
|
|
5277
|
-
var PEEPHOLE = {
|
|
5278
|
-
// Self-cancelling / tautological binary ops — drop both (equal) operands.
|
|
5279
|
-
"i32.sub": selfFold(["i32.const", 0]),
|
|
5280
|
-
"i64.sub": selfFold(["i64.const", 0]),
|
|
5281
|
-
"i32.xor": selfFold(["i32.const", 0]),
|
|
5282
|
-
"i64.xor": selfFold(["i64.const", 0]),
|
|
5283
|
-
"i32.eq": selfFold(["i32.const", 1]),
|
|
5284
|
-
"i64.eq": selfFold(["i32.const", 1]),
|
|
5285
|
-
"i32.ne": selfFold(["i32.const", 0]),
|
|
5286
|
-
"i64.ne": selfFold(["i32.const", 0]),
|
|
5287
|
-
"i32.lt_s": selfFold(["i32.const", 0]),
|
|
5288
|
-
"i32.lt_u": selfFold(["i32.const", 0]),
|
|
5289
|
-
"i32.gt_s": selfFold(["i32.const", 0]),
|
|
5290
|
-
"i32.gt_u": selfFold(["i32.const", 0]),
|
|
5291
|
-
"i32.le_s": selfFold(["i32.const", 1]),
|
|
5292
|
-
"i32.le_u": selfFold(["i32.const", 1]),
|
|
5293
|
-
"i32.ge_s": selfFold(["i32.const", 1]),
|
|
5294
|
-
"i32.ge_u": selfFold(["i32.const", 1]),
|
|
5295
|
-
"i64.lt_s": selfFold(["i32.const", 0]),
|
|
5296
|
-
"i64.lt_u": selfFold(["i32.const", 0]),
|
|
5297
|
-
"i64.gt_s": selfFold(["i32.const", 0]),
|
|
5298
|
-
"i64.gt_u": selfFold(["i32.const", 0]),
|
|
5299
|
-
"i64.le_s": selfFold(["i32.const", 1]),
|
|
5300
|
-
"i64.le_u": selfFold(["i32.const", 1]),
|
|
5301
|
-
"i64.ge_s": selfFold(["i32.const", 1]),
|
|
5302
|
-
"i64.ge_u": selfFold(["i32.const", 1]),
|
|
5303
|
-
// Zero/all-bits absorption — drops the NON-const operand, so guard its purity.
|
|
5304
|
-
"i32.mul": (a, b) => {
|
|
5305
|
-
if (getConst(b)?.value === 0 && isPure(a)) return ["i32.const", 0];
|
|
5306
|
-
if (getConst(a)?.value === 0 && isPure(b)) return ["i32.const", 0];
|
|
5307
|
-
return null;
|
|
5308
|
-
},
|
|
5309
|
-
"i64.mul": (a, b) => {
|
|
5310
|
-
if (getConst(b)?.value === ZERO64 && isPure(a)) return ["i64.const", 0];
|
|
5311
|
-
if (getConst(a)?.value === ZERO64 && isPure(b)) return ["i64.const", 0];
|
|
5312
|
-
return null;
|
|
5313
|
-
},
|
|
5314
|
-
"i32.and": (a, b) => {
|
|
5315
|
-
if (equal(a, b) && isPure(b)) return a;
|
|
5316
|
-
if (getConst(b)?.value === 0 && isPure(a)) return ["i32.const", 0];
|
|
5317
|
-
if (getConst(a)?.value === 0 && isPure(b)) return ["i32.const", 0];
|
|
5318
|
-
return null;
|
|
5319
|
-
},
|
|
5320
|
-
"i64.and": (a, b) => {
|
|
5321
|
-
if (equal(a, b) && isPure(b)) return a;
|
|
5322
|
-
if (getConst(b)?.value === ZERO64 && isPure(a)) return ["i64.const", 0];
|
|
5323
|
-
if (getConst(a)?.value === ZERO64 && isPure(b)) return ["i64.const", 0];
|
|
5324
|
-
return null;
|
|
5325
|
-
},
|
|
5326
|
-
"i32.or": (a, b) => {
|
|
5327
|
-
if (equal(a, b) && isPure(b)) return a;
|
|
5328
|
-
if (getConst(b)?.value === -1 && isPure(a)) return ["i32.const", -1];
|
|
5329
|
-
if (getConst(a)?.value === -1 && isPure(b)) return ["i32.const", -1];
|
|
5330
|
-
return null;
|
|
5331
|
-
},
|
|
5332
|
-
"i64.or": (a, b) => {
|
|
5333
|
-
if (equal(a, b) && isPure(b)) return a;
|
|
5334
|
-
if (getConst(b)?.value === NEG164 && isPure(a)) return ["i64.const", -1];
|
|
5335
|
-
if (getConst(a)?.value === NEG164 && isPure(b)) return ["i64.const", -1];
|
|
5336
|
-
return null;
|
|
5337
|
-
},
|
|
5338
|
-
// (local.set $x (local.get $x)) → nop
|
|
5339
|
-
"local.set": (a, b) => Array.isArray(b) && b[0] === "local.get" && b[1] === a ? ["nop"] : null
|
|
5340
|
-
};
|
|
5341
|
-
var peephole = (ast) => {
|
|
5342
|
-
return walkPost2(ast, (node) => {
|
|
5343
|
-
if (!Array.isArray(node) || node.length !== 3) return;
|
|
5344
|
-
const fn = PEEPHOLE[node[0]];
|
|
5345
|
-
if (!fn) return;
|
|
5346
|
-
const result = fn(node[1], node[2]);
|
|
5347
|
-
if (result !== null) return result;
|
|
5348
|
-
});
|
|
5349
|
-
};
|
|
5350
|
-
var slebSize = (v) => {
|
|
5351
|
-
let x = typeof v === "bigint" ? v : typeof v === "string" ? BigInt(v.replaceAll("_", "")) : BigInt(Math.trunc(Number(v) || 0));
|
|
5352
|
-
if (x > 0x7fffffffffffffffn) x = x - 0x8000000000000000n - 0x8000000000000000n;
|
|
5353
|
-
let n = 1;
|
|
5354
|
-
while (true) {
|
|
5355
|
-
const b = x & 0x7fn;
|
|
5356
|
-
x >>= 7n;
|
|
5357
|
-
if (x === 0n && (b & 0x40n) === 0n || x === -1n && (b & 0x40n) !== 0n) return n;
|
|
5358
|
-
n++;
|
|
5359
|
-
}
|
|
5360
|
-
};
|
|
5361
|
-
var constInstrSize = (node) => {
|
|
5362
|
-
if (!Array.isArray(node)) return 4;
|
|
5363
|
-
switch (node[0]) {
|
|
5364
|
-
case "i32.const":
|
|
5365
|
-
case "i64.const":
|
|
5366
|
-
return 1 + slebSize(node[1]);
|
|
5367
|
-
case "f32.const":
|
|
5368
|
-
return 5;
|
|
5369
|
-
case "f64.const":
|
|
5370
|
-
return 9;
|
|
5371
|
-
case "v128.const":
|
|
5372
|
-
return 18;
|
|
5373
|
-
default:
|
|
5374
|
-
return 4;
|
|
5375
|
-
}
|
|
5376
|
-
};
|
|
5377
|
-
var GLOBAL_GET_SIZE = 2;
|
|
5378
|
-
var globals = (ast) => {
|
|
5379
|
-
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
5380
|
-
const constGlobals = /* @__PURE__ */ new Map();
|
|
5381
|
-
const exported = /* @__PURE__ */ new Set();
|
|
5382
|
-
for (const node of ast.slice(1)) {
|
|
5383
|
-
if (!Array.isArray(node)) continue;
|
|
5384
|
-
if (node[0] === "export" && Array.isArray(node[2]) && node[2][0] === "global" && typeof node[2][1] === "string") {
|
|
5385
|
-
exported.add(node[2][1]);
|
|
5386
|
-
continue;
|
|
5387
|
-
}
|
|
5388
|
-
if (node[0] !== "global") continue;
|
|
5389
|
-
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
5390
|
-
if (!name2) continue;
|
|
5391
|
-
if (node.some((c) => Array.isArray(c) && c[0] === "export")) exported.add(name2);
|
|
5392
|
-
const typeSlot = node[2];
|
|
5393
|
-
if (Array.isArray(typeSlot) && typeSlot[0] === "mut") continue;
|
|
5394
|
-
if (Array.isArray(typeSlot) && typeSlot[0] === "import") continue;
|
|
5395
|
-
const init = node[3];
|
|
5396
|
-
if (getConst(init)) constGlobals.set(name2, init);
|
|
5397
|
-
}
|
|
5398
|
-
if (constGlobals.size === 0) return ast;
|
|
5399
|
-
const reads = /* @__PURE__ */ new Map();
|
|
5400
|
-
walk2(ast, (n) => {
|
|
5401
|
-
if (!Array.isArray(n)) return;
|
|
5402
|
-
const ref = n[1];
|
|
5403
|
-
if (typeof ref !== "string" || ref[0] !== "$") return;
|
|
5404
|
-
if (n[0] === "global.set") constGlobals.delete(ref);
|
|
5405
|
-
else if (n[0] === "global.get") reads.set(ref, (reads.get(ref) || 0) + 1);
|
|
5406
|
-
});
|
|
5407
|
-
const propagate2 = /* @__PURE__ */ new Set();
|
|
5408
|
-
for (const [name2, init] of constGlobals) {
|
|
5409
|
-
const r = reads.get(name2) || 0;
|
|
5410
|
-
if (r === 0) continue;
|
|
5411
|
-
const cs = constInstrSize(init);
|
|
5412
|
-
const declSize = cs + 2;
|
|
5413
|
-
const before = r * GLOBAL_GET_SIZE + declSize;
|
|
5414
|
-
const after = r * cs + (exported.has(name2) ? declSize : 0);
|
|
5415
|
-
if (after <= before) propagate2.add(name2);
|
|
5416
|
-
}
|
|
5417
|
-
if (propagate2.size === 0) return ast;
|
|
5418
|
-
walkPost2(ast, (node) => {
|
|
5419
|
-
if (!Array.isArray(node) || node[0] !== "global.get" || node.length !== 2) return;
|
|
5420
|
-
if (propagate2.has(node[1])) return clone2(constGlobals.get(node[1]));
|
|
5421
|
-
});
|
|
5422
|
-
for (let i = ast.length - 1; i >= 1; i--) {
|
|
5423
|
-
const n = ast[i];
|
|
5424
|
-
if (Array.isArray(n) && n[0] === "global" && typeof n[1] === "string" && propagate2.has(n[1]) && !exported.has(n[1])) ast.splice(i, 1);
|
|
5425
|
-
}
|
|
5426
|
-
return ast;
|
|
5427
|
-
};
|
|
5428
|
-
var offset = (ast) => {
|
|
5429
|
-
return walkPost2(ast, (node) => {
|
|
5430
|
-
if (!Array.isArray(node)) return;
|
|
5431
|
-
const op = node[0];
|
|
5432
|
-
if (typeof op !== "string" || !op.endsWith("load") && !op.endsWith("store")) return;
|
|
5433
|
-
const isStore = op.endsWith("store");
|
|
5434
|
-
let currentOffset = 0;
|
|
5435
|
-
let memIdx = null;
|
|
5436
|
-
let argStart = 1;
|
|
5437
|
-
if (typeof node[1] === "string" && (node[1][0] === "$" || !isNaN(node[1]))) {
|
|
5438
|
-
memIdx = node[1];
|
|
5439
|
-
argStart = 2;
|
|
5440
|
-
}
|
|
5441
|
-
while (argStart < node.length && typeof node[argStart] === "string" && (node[argStart].startsWith("offset=") || node[argStart].startsWith("align="))) {
|
|
5442
|
-
if (node[argStart].startsWith("offset=")) {
|
|
5443
|
-
currentOffset = +node[argStart].slice(7);
|
|
5444
|
-
}
|
|
5445
|
-
argStart++;
|
|
5446
|
-
}
|
|
5447
|
-
const ptrIdx = isStore ? node.length - 2 : node.length - 1;
|
|
5448
|
-
const valIdx = isStore ? node.length - 1 : -1;
|
|
5449
|
-
if (ptrIdx < argStart) return;
|
|
5450
|
-
const ptr = node[ptrIdx];
|
|
5451
|
-
if (!Array.isArray(ptr) || ptr[0] !== "i32.add" || ptr.length !== 3) return;
|
|
5452
|
-
const a = ptr[1], b = ptr[2];
|
|
5453
|
-
const ca = getConst(a), cb = getConst(b);
|
|
5454
|
-
let base = null, addend = null;
|
|
5455
|
-
if (ca && ca.type === "i32") {
|
|
5456
|
-
addend = ca.value;
|
|
5457
|
-
base = b;
|
|
5458
|
-
} else if (cb && cb.type === "i32") {
|
|
5459
|
-
addend = cb.value;
|
|
5460
|
-
base = a;
|
|
5461
|
-
}
|
|
5462
|
-
if (base === null || addend === null) return;
|
|
5463
|
-
const newOffset = currentOffset + addend;
|
|
5464
|
-
const newNode = [op];
|
|
5465
|
-
if (memIdx !== null) newNode.push(memIdx);
|
|
5466
|
-
newNode.push(`offset=${newOffset}`);
|
|
5467
|
-
let alignParam = null;
|
|
5468
|
-
for (let i = argStart; i < ptrIdx; i++) {
|
|
5469
|
-
if (typeof node[i] === "string" && node[i].startsWith("align=")) {
|
|
5470
|
-
alignParam = node[i];
|
|
5471
|
-
}
|
|
5472
|
-
}
|
|
5473
|
-
if (alignParam) newNode.push(alignParam);
|
|
5474
|
-
newNode.push(base);
|
|
5475
|
-
if (isStore) newNode.push(node[valIdx]);
|
|
5476
|
-
return newNode;
|
|
5477
|
-
});
|
|
5478
|
-
};
|
|
5479
|
-
var unbranch = (ast) => {
|
|
5480
|
-
walk2(ast, (node) => {
|
|
5481
|
-
if (!Array.isArray(node)) return;
|
|
5482
|
-
const op = node[0];
|
|
5483
|
-
if (op !== "block") return;
|
|
5484
|
-
let labelIdx = 1;
|
|
5485
|
-
let label = null;
|
|
5486
|
-
if (typeof node[1] === "string" && node[1][0] === "$") {
|
|
5487
|
-
label = node[1];
|
|
5488
|
-
labelIdx = 2;
|
|
5489
|
-
}
|
|
5490
|
-
if (!label) return;
|
|
5491
|
-
let lastIdx = -1;
|
|
5492
|
-
for (let i = node.length - 1; i >= labelIdx; i--) {
|
|
5493
|
-
const child = node[i];
|
|
5494
|
-
if (!Array.isArray(child)) {
|
|
5495
|
-
if (child !== "nop" && child !== "end") lastIdx = i;
|
|
5496
|
-
continue;
|
|
5497
|
-
}
|
|
5498
|
-
const cop = child[0];
|
|
5499
|
-
if (cop === "param" || cop === "result" || cop === "local" || cop === "type" || cop === "export") continue;
|
|
5500
|
-
lastIdx = i;
|
|
5501
|
-
break;
|
|
5502
|
-
}
|
|
5503
|
-
if (lastIdx < 0) return;
|
|
5504
|
-
const last = node[lastIdx];
|
|
5505
|
-
if (Array.isArray(last) && last[0] === "br" && last[1] === label) {
|
|
5506
|
-
node.splice(lastIdx, 1, ...last.slice(2));
|
|
5507
|
-
}
|
|
5508
|
-
});
|
|
5509
|
-
return ast;
|
|
5510
|
-
};
|
|
5511
|
-
var loopify = (ast) => {
|
|
5512
|
-
walk2(ast, (node) => {
|
|
5513
|
-
if (!Array.isArray(node) || node[0] !== "block") return;
|
|
5514
|
-
let bi = 1, label = null;
|
|
5515
|
-
if (typeof node[1] === "string" && node[1][0] === "$") {
|
|
5516
|
-
label = node[1];
|
|
5517
|
-
bi = 2;
|
|
5518
|
-
}
|
|
5519
|
-
if (!label) return;
|
|
5520
|
-
while (bi < node.length) {
|
|
5521
|
-
const c = node[bi];
|
|
5522
|
-
if (Array.isArray(c) && c[0] === "type") {
|
|
5523
|
-
bi++;
|
|
5524
|
-
continue;
|
|
5525
|
-
}
|
|
5526
|
-
if (Array.isArray(c) && (c[0] === "param" || c[0] === "result")) return;
|
|
5527
|
-
break;
|
|
5528
|
-
}
|
|
5529
|
-
if (node.length - bi !== 1) return;
|
|
5530
|
-
const loop = node[bi];
|
|
5531
|
-
if (!Array.isArray(loop) || loop[0] !== "loop") return;
|
|
5532
|
-
let li = 1, loopLabel = null;
|
|
5533
|
-
if (typeof loop[1] === "string" && loop[1][0] === "$") {
|
|
5534
|
-
loopLabel = loop[1];
|
|
5535
|
-
li = 2;
|
|
5536
|
-
}
|
|
5537
|
-
const loopHeader = [];
|
|
5538
|
-
while (li < loop.length) {
|
|
5539
|
-
const c = loop[li];
|
|
5540
|
-
if (Array.isArray(c) && c[0] === "type") {
|
|
5541
|
-
loopHeader.push(c);
|
|
5542
|
-
li++;
|
|
5543
|
-
continue;
|
|
5544
|
-
}
|
|
5545
|
-
if (Array.isArray(c) && (c[0] === "param" || c[0] === "result")) return;
|
|
5546
|
-
break;
|
|
5547
|
-
}
|
|
5548
|
-
const body = loop.slice(li);
|
|
5549
|
-
if (body.length < 2) return;
|
|
5550
|
-
const head = body[0];
|
|
5551
|
-
const tail = body[body.length - 1];
|
|
5552
|
-
if (!Array.isArray(head) || head[0] !== "br_if" || head[1] !== label || head.length !== 3) return;
|
|
5553
|
-
if (!Array.isArray(tail) || tail[0] !== "br" || tail[1] !== loopLabel || tail.length !== 2) return;
|
|
5554
|
-
const inner = body.slice(1, -1);
|
|
5555
|
-
if (targetsLabel(inner, label)) return;
|
|
5556
|
-
let cond = head[2];
|
|
5557
|
-
if (Array.isArray(cond) && cond[0] === "i32.eqz" && cond.length === 2) cond = cond[1];
|
|
5558
|
-
else cond = ["i32.eqz", cond];
|
|
5559
|
-
const newLoop = ["loop"];
|
|
5560
|
-
if (loopLabel) newLoop.push(loopLabel);
|
|
5561
|
-
for (const h of loopHeader) newLoop.push(h);
|
|
5562
|
-
newLoop.push(["if", cond, ["then", ...inner, tail]]);
|
|
5563
|
-
node.length = 0;
|
|
5564
|
-
for (const tok of newLoop) node.push(tok);
|
|
5565
|
-
});
|
|
5566
|
-
return ast;
|
|
5567
|
-
};
|
|
5568
|
-
var stripmut = (ast) => {
|
|
5569
|
-
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
5570
|
-
const written = /* @__PURE__ */ new Set();
|
|
5571
|
-
walk2(ast, (n) => {
|
|
5572
|
-
if (Array.isArray(n) && n[0] === "global.set" && typeof n[1] === "string") written.add(n[1]);
|
|
5573
|
-
});
|
|
5574
|
-
return walkPost2(ast, (node) => {
|
|
5575
|
-
if (!Array.isArray(node) || node[0] !== "global") return;
|
|
5576
|
-
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
5577
|
-
if (!name2 || written.has(name2)) return;
|
|
5578
|
-
const hasName = typeof node[1] === "string" && node[1][0] === "$";
|
|
5579
|
-
const typeSlot = hasName ? node[2] : node[1];
|
|
5580
|
-
if (Array.isArray(typeSlot) && typeSlot[0] === "mut") {
|
|
5581
|
-
const newNode = [...node];
|
|
5582
|
-
newNode[hasName ? 2 : 1] = typeSlot[1];
|
|
5583
|
-
return newNode;
|
|
5584
|
-
}
|
|
5585
|
-
});
|
|
5586
|
-
};
|
|
5587
|
-
var brif = (ast) => {
|
|
5588
|
-
return walkPost2(ast, (node) => {
|
|
5589
|
-
if (!Array.isArray(node) || node[0] !== "if") return;
|
|
5590
|
-
const { cond, thenBranch, elseBranch } = parseIf(node);
|
|
5591
|
-
const thenEmpty = !thenBranch || thenBranch.length <= 1;
|
|
5592
|
-
const elseEmpty = !elseBranch || elseBranch.length <= 1;
|
|
5593
|
-
if (!thenEmpty && elseEmpty && thenBranch.length === 2) {
|
|
5594
|
-
const t = thenBranch[1];
|
|
5595
|
-
if (Array.isArray(t) && t[0] === "br" && t.length === 2) return ["br_if", t[1], cond];
|
|
5596
|
-
}
|
|
5597
|
-
if (thenEmpty && !elseEmpty && elseBranch.length === 2) {
|
|
5598
|
-
const e = elseBranch[1];
|
|
5599
|
-
if (Array.isArray(e) && e[0] === "br" && e.length === 2) return ["br_if", e[1], ["i32.eqz", cond]];
|
|
5600
|
-
}
|
|
5601
|
-
});
|
|
5602
|
-
};
|
|
5603
|
-
var foldarms = (ast) => {
|
|
5604
|
-
return walkPost2(ast, (node) => {
|
|
5605
|
-
if (!Array.isArray(node) || node[0] !== "if") return;
|
|
5606
|
-
const { thenBranch, elseBranch } = parseIf(node);
|
|
5607
|
-
if (!thenBranch || !elseBranch) return;
|
|
5608
|
-
if (thenBranch.length <= 1 || elseBranch.length <= 1) return;
|
|
5609
|
-
const hasResult = node.some((c) => Array.isArray(c) && c[0] === "result");
|
|
5610
|
-
if (!hasResult) return;
|
|
5611
|
-
let common = 0;
|
|
5612
|
-
const minLen = Math.min(thenBranch.length, elseBranch.length);
|
|
5613
|
-
for (let i = 1; i < minLen; i++) {
|
|
5614
|
-
if (!equal(thenBranch[thenBranch.length - i], elseBranch[elseBranch.length - i])) break;
|
|
5615
|
-
common++;
|
|
5616
|
-
}
|
|
5617
|
-
if (common === 0) return;
|
|
5618
|
-
const hoisted = thenBranch.slice(thenBranch.length - common);
|
|
5619
|
-
const newThen = thenBranch.slice(0, thenBranch.length - common);
|
|
5620
|
-
const newElse = elseBranch.slice(0, elseBranch.length - common);
|
|
5621
|
-
const block = ["block"];
|
|
5622
|
-
for (let i = 1; i < node.length; i++) {
|
|
5623
|
-
const c = node[i];
|
|
5624
|
-
if (Array.isArray(c) && (c[0] === "then" || c[0] === "else")) break;
|
|
5625
|
-
if (Array.isArray(c) && (c[0] === "result" || c[0] === "type")) block.push(c);
|
|
5626
|
-
}
|
|
5627
|
-
const newIf = ["if"];
|
|
5628
|
-
for (let i = 1; i < node.length; i++) {
|
|
5629
|
-
const c = node[i];
|
|
5630
|
-
if (Array.isArray(c) && (c[0] === "then" || c[0] === "else")) break;
|
|
5631
|
-
if (Array.isArray(c) && (c[0] === "result" || c[0] === "type")) continue;
|
|
5632
|
-
newIf.push(c);
|
|
5633
|
-
}
|
|
5634
|
-
newIf.push(newThen.length > 1 ? newThen : ["then"]);
|
|
5635
|
-
newIf.push(newElse.length > 1 ? newElse : ["else"]);
|
|
5636
|
-
block.push(newIf, ...hoisted);
|
|
5637
|
-
return block;
|
|
5638
|
-
});
|
|
5639
|
-
};
|
|
5640
|
-
var hashFunc = (node, localNames) => {
|
|
5641
|
-
const parts = [];
|
|
5642
|
-
const stack = [node];
|
|
5643
|
-
while (stack.length) {
|
|
5644
|
-
const v = stack.pop();
|
|
5645
|
-
if (Array.isArray(v)) {
|
|
5646
|
-
stack.push("|");
|
|
5647
|
-
for (let i = v.length - 1; i >= 0; i--) stack.push(v[i]);
|
|
5648
|
-
stack.push("[");
|
|
5649
|
-
} else if (typeof v === "string") {
|
|
5650
|
-
parts.push(localNames.has(v) ? "$__L" : v);
|
|
5651
|
-
} else if (typeof v === "bigint") {
|
|
5652
|
-
parts.push(v.toString() + "n");
|
|
5653
|
-
} else if (typeof v === "number") {
|
|
5654
|
-
parts.push(v.toString());
|
|
5655
|
-
} else if (v === null) {
|
|
5656
|
-
parts.push("null");
|
|
5657
|
-
} else if (v === true) {
|
|
5658
|
-
parts.push("t");
|
|
5659
|
-
} else if (v === false) {
|
|
5660
|
-
parts.push("f");
|
|
5661
|
-
} else {
|
|
5662
|
-
parts.push(String(v));
|
|
5663
|
-
}
|
|
5664
|
-
}
|
|
5665
|
-
return parts.join(",");
|
|
5666
|
-
};
|
|
5667
|
-
var dedupe = (ast) => {
|
|
5668
|
-
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
5669
|
-
const signatures = /* @__PURE__ */ new Map();
|
|
5670
|
-
const redirects = /* @__PURE__ */ new Map();
|
|
5671
|
-
for (const node of ast.slice(1)) {
|
|
5672
|
-
if (!Array.isArray(node) || node[0] !== "func") continue;
|
|
5673
|
-
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
5674
|
-
if (!name2) continue;
|
|
5675
|
-
const localNames = /* @__PURE__ */ new Set();
|
|
5676
|
-
if (typeof node[1] === "string" && node[1][0] === "$") localNames.add(node[1]);
|
|
5677
|
-
walk2(node, (n) => {
|
|
5678
|
-
if (!Array.isArray(n) || typeof n[1] !== "string" || n[1][0] !== "$") return;
|
|
5679
|
-
const op = n[0];
|
|
5680
|
-
if (op === "param" || op === "local" || isBranchScope(op)) {
|
|
5681
|
-
localNames.add(n[1]);
|
|
5682
|
-
}
|
|
5683
|
-
});
|
|
5684
|
-
const hash = hashFunc(node, localNames);
|
|
5685
|
-
if (signatures.has(hash)) {
|
|
5686
|
-
redirects.set(name2, signatures.get(hash));
|
|
5687
|
-
} else {
|
|
5688
|
-
signatures.set(hash, name2);
|
|
5689
|
-
}
|
|
5690
|
-
}
|
|
5691
|
-
if (redirects.size === 0) return ast;
|
|
5692
|
-
walkPost2(ast, (node) => {
|
|
5693
|
-
if (!Array.isArray(node)) return;
|
|
5694
|
-
const op = node[0];
|
|
5695
|
-
if ((op === "call" || op === "return_call") && redirects.has(node[1])) {
|
|
5696
|
-
return [op, redirects.get(node[1]), ...node.slice(2)];
|
|
5697
|
-
}
|
|
5698
|
-
if (op === "ref.func" && redirects.has(node[1])) {
|
|
5699
|
-
return ["ref.func", redirects.get(node[1])];
|
|
5700
|
-
}
|
|
5701
|
-
if (op === "elem") {
|
|
5702
|
-
const funcs = node[node.length - 1];
|
|
5703
|
-
if (Array.isArray(funcs)) {
|
|
5704
|
-
return [...node.slice(0, -1), funcs.map((f) => redirects.get(f) || f)];
|
|
5705
|
-
}
|
|
5706
|
-
}
|
|
5707
|
-
if (op === "call_indirect" && node.length >= 3) {
|
|
5708
|
-
const typeRef = node[1];
|
|
5709
|
-
if (typeof typeRef === "string" && redirects.has(typeRef)) {
|
|
5710
|
-
return ["call_indirect", redirects.get(typeRef), ...node.slice(2)];
|
|
5711
|
-
}
|
|
5712
|
-
}
|
|
5713
|
-
});
|
|
5714
|
-
return ast;
|
|
5715
|
-
};
|
|
5716
|
-
var dedupTypes = (ast) => {
|
|
5717
|
-
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
5718
|
-
const signatures = /* @__PURE__ */ new Map();
|
|
5719
|
-
const redirects = /* @__PURE__ */ new Map();
|
|
5720
|
-
for (const node of ast.slice(1)) {
|
|
5721
|
-
if (!Array.isArray(node) || node[0] !== "type") continue;
|
|
5722
|
-
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
5723
|
-
if (!name2) continue;
|
|
5724
|
-
const hash = hashFunc(node, /* @__PURE__ */ new Set([name2]));
|
|
5725
|
-
if (signatures.has(hash)) {
|
|
5726
|
-
redirects.set(name2, signatures.get(hash));
|
|
5727
|
-
} else {
|
|
5728
|
-
signatures.set(hash, name2);
|
|
5729
|
-
}
|
|
5730
|
-
}
|
|
5731
|
-
if (redirects.size === 0) return ast;
|
|
5732
|
-
for (let i = ast.length - 1; i >= 0; i--) {
|
|
5733
|
-
const node = ast[i];
|
|
5734
|
-
if (Array.isArray(node) && node[0] === "type") {
|
|
5735
|
-
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
5736
|
-
if (name2 && redirects.has(name2)) ast.splice(i, 1);
|
|
5737
|
-
}
|
|
5738
|
-
}
|
|
5739
|
-
walkPost2(ast, (node) => {
|
|
5740
|
-
if (!Array.isArray(node)) return;
|
|
5741
|
-
const op = node[0];
|
|
5742
|
-
if (op === "func") {
|
|
5743
|
-
for (let i = 1; i < node.length; i++) {
|
|
5744
|
-
const sub = node[i];
|
|
5745
|
-
if (Array.isArray(sub) && sub[0] === "type" && typeof sub[1] === "string" && redirects.has(sub[1])) {
|
|
5746
|
-
node[i] = ["type", redirects.get(sub[1])];
|
|
5747
|
-
}
|
|
5748
|
-
}
|
|
5749
|
-
}
|
|
5750
|
-
if (op === "import") {
|
|
5751
|
-
for (let i = 1; i < node.length; i++) {
|
|
5752
|
-
const sub = node[i];
|
|
5753
|
-
if (Array.isArray(sub)) {
|
|
5754
|
-
for (let j = 1; j < sub.length; j++) {
|
|
5755
|
-
const inner = sub[j];
|
|
5756
|
-
if (Array.isArray(inner) && inner[0] === "type" && typeof inner[1] === "string" && redirects.has(inner[1])) {
|
|
5757
|
-
sub[j] = ["type", redirects.get(inner[1])];
|
|
5758
|
-
}
|
|
5759
|
-
}
|
|
5760
|
-
}
|
|
5761
|
-
}
|
|
5762
|
-
}
|
|
5763
|
-
if (op === "call_indirect" || op === "return_call_indirect") {
|
|
5764
|
-
if (typeof node[1] === "string" && redirects.has(node[1])) {
|
|
5765
|
-
return [op, redirects.get(node[1]), ...node.slice(2)];
|
|
5766
|
-
}
|
|
5767
|
-
if (Array.isArray(node[1]) && node[1][0] === "type" && typeof node[1][1] === "string" && redirects.has(node[1][1])) {
|
|
5768
|
-
return [op, ["type", redirects.get(node[1][1])], ...node.slice(2)];
|
|
5769
|
-
}
|
|
5770
|
-
}
|
|
5771
|
-
});
|
|
5772
|
-
return ast;
|
|
5773
|
-
};
|
|
5774
|
-
var parseDataString = (str2) => {
|
|
5775
|
-
if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return [];
|
|
5776
|
-
const bytes = [];
|
|
5777
|
-
const hexv = (c) => c >= 48 && c <= 57 ? c - 48 : c >= 97 && c <= 102 ? c - 87 : c >= 65 && c <= 70 ? c - 55 : -1;
|
|
5778
|
-
const end = str2.length - 1;
|
|
5779
|
-
for (let i = 1; i < end; i++) {
|
|
5780
|
-
const c = str2.charCodeAt(i);
|
|
5781
|
-
if (c !== 92) {
|
|
5782
|
-
bytes.push(c);
|
|
5783
|
-
continue;
|
|
5784
|
-
}
|
|
5785
|
-
const n = str2.charCodeAt(++i);
|
|
5786
|
-
if (n === 120 || n === 88) {
|
|
5787
|
-
bytes.push(hexv(str2.charCodeAt(i + 1)) << 4 | hexv(str2.charCodeAt(i + 2)));
|
|
5788
|
-
i += 2;
|
|
5789
|
-
} else {
|
|
5790
|
-
const h1 = hexv(n), h2 = i + 1 < end ? hexv(str2.charCodeAt(i + 1)) : -1;
|
|
5791
|
-
if (h1 >= 0 && h2 >= 0) {
|
|
5792
|
-
bytes.push(h1 << 4 | h2);
|
|
5793
|
-
i++;
|
|
5794
|
-
} else if (n === 110) bytes.push(10);
|
|
5795
|
-
else if (n === 116) bytes.push(9);
|
|
5796
|
-
else if (n === 114) bytes.push(13);
|
|
5797
|
-
else bytes.push(n);
|
|
5798
|
-
}
|
|
5799
|
-
}
|
|
5800
|
-
return bytes;
|
|
5801
|
-
};
|
|
5802
|
-
var encodeDataString = (bytes, end) => {
|
|
5803
|
-
let str2 = '"';
|
|
5804
|
-
for (let i = 0; i < end; i++) {
|
|
5805
|
-
const b = bytes[i];
|
|
5806
|
-
if (b >= 32 && b < 127 && b !== 34 && b !== 92) str2 += String.fromCharCode(b);
|
|
5807
|
-
else str2 += "\\" + b.toString(16).padStart(2, "0");
|
|
5808
|
-
}
|
|
5809
|
-
return str2 + '"';
|
|
5810
|
-
};
|
|
5811
|
-
var trimTrailingZeros = (items) => {
|
|
5812
|
-
const bytes = [];
|
|
5813
|
-
for (const item of items) {
|
|
5814
|
-
if (typeof item === "string") {
|
|
5815
|
-
const chunk = parseDataString(item);
|
|
5816
|
-
for (let i = 0; i < chunk.length; i++) bytes.push(chunk[i]);
|
|
5817
|
-
} else if (Array.isArray(item) && item[0] === "i8") {
|
|
5818
|
-
for (let i = 1; i < item.length; i++) bytes.push(Number(item[i]) & 255);
|
|
5819
|
-
} else {
|
|
5820
|
-
return items;
|
|
5821
|
-
}
|
|
5822
|
-
}
|
|
5823
|
-
let end = bytes.length;
|
|
5824
|
-
while (end > 0 && bytes[end - 1] === 0) end--;
|
|
5825
|
-
if (end === bytes.length) return items;
|
|
5826
|
-
if (end === 0) return [];
|
|
5827
|
-
return [encodeDataString(bytes, end)];
|
|
5828
|
-
};
|
|
5829
|
-
var getDataOffset = (node) => {
|
|
5830
|
-
let idx = 1;
|
|
5831
|
-
if (typeof node[idx] === "string" && node[idx][0] === "$") idx++;
|
|
5832
|
-
if (Array.isArray(node[idx]) && node[idx][0] === "memory") {
|
|
5833
|
-
const mem = node[idx][1];
|
|
5834
|
-
idx++;
|
|
5835
|
-
const off2 = node[idx];
|
|
5836
|
-
if (Array.isArray(off2) && (off2[0] === "i32.const" || off2[0] === "i64.const")) {
|
|
5837
|
-
return { memidx: mem, offset: Number(off2[1]) };
|
|
5838
|
-
}
|
|
5839
|
-
return null;
|
|
5840
|
-
}
|
|
5841
|
-
const off = node[idx];
|
|
5842
|
-
if (Array.isArray(off) && (off[0] === "i32.const" || off[0] === "i64.const")) {
|
|
5843
|
-
return { memidx: 0, offset: Number(off[1]) };
|
|
5844
|
-
}
|
|
5845
|
-
return null;
|
|
5846
|
-
};
|
|
5847
|
-
var getDataLength = (node) => {
|
|
5848
|
-
let idx = 1;
|
|
5849
|
-
if (typeof node[idx] === "string" && node[idx][0] === "$") idx++;
|
|
5850
|
-
if (Array.isArray(node[idx]) && node[idx][0] === "memory") idx++;
|
|
5851
|
-
if (Array.isArray(node[idx]) && typeof node[idx][0] === "string" && !node[idx][0].startsWith('"')) idx++;
|
|
5852
|
-
let len = 0;
|
|
5853
|
-
for (let i = idx; i < node.length; i++) {
|
|
5854
|
-
const item = node[i];
|
|
5855
|
-
if (typeof item === "string") len += parseDataString(item).length;
|
|
5856
|
-
else if (Array.isArray(item) && item[0] === "i8") len += item.length - 1;
|
|
5857
|
-
else return null;
|
|
5858
|
-
}
|
|
5859
|
-
return len;
|
|
5860
|
-
};
|
|
5861
|
-
var mergeDataSegments = (a, b) => {
|
|
5862
|
-
let aIdx = 1;
|
|
5863
|
-
if (typeof a[aIdx] === "string" && a[aIdx][0] === "$") aIdx++;
|
|
5864
|
-
if (Array.isArray(a[aIdx]) && a[aIdx][0] === "memory") aIdx++;
|
|
5865
|
-
if (Array.isArray(a[aIdx]) && typeof a[aIdx][0] === "string" && !a[aIdx][0].startsWith('"')) aIdx++;
|
|
5866
|
-
let bIdx = 1;
|
|
5867
|
-
if (typeof b[bIdx] === "string" && b[bIdx][0] === "$") bIdx++;
|
|
5868
|
-
if (Array.isArray(b[bIdx]) && b[bIdx][0] === "memory") bIdx++;
|
|
5869
|
-
if (Array.isArray(b[bIdx]) && typeof b[bIdx][0] === "string" && !b[bIdx][0].startsWith('"')) bIdx++;
|
|
5870
|
-
const aContent = a.slice(aIdx);
|
|
5871
|
-
const bContent = b.slice(bIdx);
|
|
5872
|
-
if (aContent.length === 1 && bContent.length === 1 && typeof aContent[0] === "string" && typeof bContent[0] === "string") {
|
|
5873
|
-
const aBytes = parseDataString(aContent[0]);
|
|
5874
|
-
const bBytes = parseDataString(bContent[0]);
|
|
5875
|
-
for (let i = 0; i < bBytes.length; i++) aBytes.push(bBytes[i]);
|
|
5876
|
-
a.length = aIdx;
|
|
5877
|
-
a.push(encodeDataString(aBytes, aBytes.length));
|
|
5878
|
-
return true;
|
|
5879
|
-
}
|
|
5880
|
-
a.length = aIdx;
|
|
5881
|
-
a.push(...aContent, ...bContent);
|
|
5882
|
-
return true;
|
|
5883
|
-
};
|
|
5884
|
-
var packData = (ast) => {
|
|
5885
|
-
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
5886
|
-
for (const node of ast) {
|
|
5887
|
-
if (!Array.isArray(node) || node[0] !== "data") continue;
|
|
5888
|
-
let contentStart = 1;
|
|
5889
|
-
if (typeof node[1] === "string" && node[1][0] === "$") contentStart = 2;
|
|
5890
|
-
if (contentStart < node.length && Array.isArray(node[contentStart]) && typeof node[contentStart][0] === "string" && !node[contentStart][0].startsWith('"')) {
|
|
5891
|
-
contentStart++;
|
|
5892
|
-
}
|
|
5893
|
-
const content = node.slice(contentStart);
|
|
5894
|
-
if (content.length === 0) continue;
|
|
5895
|
-
const trimmed = trimTrailingZeros(content);
|
|
5896
|
-
if (trimmed.length !== content.length || trimmed.length > 0 && trimmed[0] !== content[0]) {
|
|
5897
|
-
node.length = contentStart;
|
|
5898
|
-
node.push(...trimmed);
|
|
5899
|
-
}
|
|
5900
|
-
}
|
|
5901
|
-
const dataNodes = [];
|
|
5902
|
-
for (let i = 0; i < ast.length; i++) {
|
|
5903
|
-
const node = ast[i];
|
|
5904
|
-
if (Array.isArray(node) && node[0] === "data") {
|
|
5905
|
-
const info = getDataOffset(node);
|
|
5906
|
-
if (info) {
|
|
5907
|
-
const len = getDataLength(node);
|
|
5908
|
-
if (len !== null) dataNodes.push({ ...info, node, index: i, len });
|
|
5909
|
-
}
|
|
5910
|
-
}
|
|
5911
|
-
}
|
|
5912
|
-
dataNodes.sort((a, b) => {
|
|
5913
|
-
const ma = String(a.memidx), mb = String(b.memidx);
|
|
5914
|
-
if (ma !== mb) return ma.localeCompare(mb);
|
|
5915
|
-
return a.offset - b.offset;
|
|
5916
|
-
});
|
|
5917
|
-
const toRemove = /* @__PURE__ */ new Set();
|
|
5918
|
-
for (let i = 0; i < dataNodes.length - 1; i++) {
|
|
5919
|
-
const a = dataNodes[i];
|
|
5920
|
-
const b = dataNodes[i + 1];
|
|
5921
|
-
if (toRemove.has(a.index) || String(a.memidx) !== String(b.memidx)) continue;
|
|
5922
|
-
if (a.offset + a.len !== b.offset) continue;
|
|
5923
|
-
if (mergeDataSegments(a.node, b.node)) {
|
|
5924
|
-
toRemove.add(b.index);
|
|
5925
|
-
a.len = getDataLength(a.node);
|
|
5926
|
-
}
|
|
5927
|
-
}
|
|
5928
|
-
if (toRemove.size > 0) {
|
|
5929
|
-
ast = ast.filter((_, i) => !toRemove.has(i));
|
|
5930
|
-
}
|
|
5931
|
-
return ast;
|
|
5932
|
-
};
|
|
5933
|
-
var makeShortener = () => {
|
|
5934
|
-
const map = /* @__PURE__ */ new Map();
|
|
5935
|
-
let n = 0;
|
|
5936
|
-
return (name2) => {
|
|
5937
|
-
if (!map.has(name2)) {
|
|
5938
|
-
let id2 = "", x = n++;
|
|
5939
|
-
do {
|
|
5940
|
-
id2 = String.fromCharCode(97 + x % 26) + id2;
|
|
5941
|
-
x = Math.floor(x / 26) - 1;
|
|
5942
|
-
} while (x >= 0);
|
|
5943
|
-
map.set(name2, id2 || "a");
|
|
5944
|
-
}
|
|
5945
|
-
return map.get(name2);
|
|
5946
|
-
};
|
|
5947
|
-
};
|
|
5948
|
-
var minifyImports = (ast) => {
|
|
5949
|
-
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
5950
|
-
const shortMod = makeShortener();
|
|
5951
|
-
const shortField = makeShortener();
|
|
5952
|
-
for (const node of ast) {
|
|
5953
|
-
if (!Array.isArray(node) || node[0] !== "import") continue;
|
|
5954
|
-
if (typeof node[1] === "string" && node[1][0] === '"') {
|
|
5955
|
-
node[1] = '"' + shortMod(node[1].slice(1, -1)) + '"';
|
|
5956
|
-
}
|
|
5957
|
-
if (typeof node[2] === "string" && node[2][0] === '"') {
|
|
5958
|
-
node[2] = '"' + shortField(node[2].slice(1, -1)) + '"';
|
|
5959
|
-
}
|
|
5960
|
-
}
|
|
5961
|
-
return ast;
|
|
5962
|
-
};
|
|
5963
|
-
var reorderSafe = (ast) => {
|
|
5964
|
-
let safe = true;
|
|
5965
|
-
walk2(ast, (n) => {
|
|
5966
|
-
if (!safe || !Array.isArray(n)) return;
|
|
5967
|
-
const op = n[0];
|
|
5968
|
-
if (op === "func" && (typeof n[1] !== "string" || n[1][0] !== "$")) safe = false;
|
|
5969
|
-
else if ((op === "call" || op === "return_call" || op === "ref.func") && (typeof n[1] !== "string" || n[1][0] !== "$")) safe = false;
|
|
5970
|
-
else if (op === "start" && (typeof n[1] !== "string" || n[1][0] !== "$")) safe = false;
|
|
5971
|
-
else if (op === "elem") {
|
|
5972
|
-
for (const sub of n) {
|
|
5973
|
-
if (typeof sub === "string" && sub[0] !== "$" && /^\d/.test(sub)) {
|
|
5974
|
-
safe = false;
|
|
5975
|
-
break;
|
|
5976
|
-
}
|
|
5977
|
-
if (Array.isArray(sub) && sub[0] === "ref.func" && (typeof sub[1] !== "string" || sub[1][0] !== "$")) {
|
|
5978
|
-
safe = false;
|
|
5979
|
-
break;
|
|
5980
|
-
}
|
|
5981
|
-
}
|
|
5982
|
-
}
|
|
5983
|
-
});
|
|
5984
|
-
return safe;
|
|
5985
|
-
};
|
|
5986
|
-
var reorder = (ast) => {
|
|
5987
|
-
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
5988
|
-
if (!reorderSafe(ast)) return ast;
|
|
5989
|
-
const callCounts = /* @__PURE__ */ new Map();
|
|
5990
|
-
walk2(ast, (n) => {
|
|
5991
|
-
if (!Array.isArray(n)) return;
|
|
5992
|
-
if (n[0] === "call" || n[0] === "return_call") {
|
|
5993
|
-
callCounts.set(n[1], (callCounts.get(n[1]) || 0) + 1);
|
|
5994
|
-
}
|
|
5995
|
-
});
|
|
5996
|
-
const imports = [], funcs = [], others = [];
|
|
5997
|
-
for (const node of ast.slice(1)) {
|
|
5998
|
-
if (!Array.isArray(node)) {
|
|
5999
|
-
others.push(node);
|
|
6000
|
-
continue;
|
|
6001
|
-
}
|
|
6002
|
-
if (node[0] === "import") imports.push(node);
|
|
6003
|
-
else if (node[0] === "func") funcs.push(node);
|
|
6004
|
-
else others.push(node);
|
|
6005
|
-
}
|
|
6006
|
-
funcs.sort((a, b) => (callCounts.get(b[1]) || 0) - (callCounts.get(a[1]) || 0));
|
|
6007
|
-
return ["module", ...imports, ...funcs, ...others];
|
|
6008
|
-
};
|
|
6009
|
-
var PASSES = [
|
|
6010
|
-
["stripmut", stripmut, true, "strip mut from never-written globals"],
|
|
6011
|
-
["globals", globals, true, "propagate immutable global constants"],
|
|
6012
|
-
["guardRefine", guardRefine, false, "fold NaN-box tag reads under dominating tag guards (jz NaN-box-specific; opt-in)"],
|
|
6013
|
-
["fold", fold, true, "constant folding"],
|
|
6014
|
-
["identity", identity, true, "remove identity ops (x + 0 \u2192 x)"],
|
|
6015
|
-
["peephole", peephole, true, "x-x\u21920, x&0\u21920, etc."],
|
|
6016
|
-
["strength", strength, true, "strength reduction (x * 2 \u2192 x << 1)"],
|
|
6017
|
-
["branch", branch, true, "simplify constant branches"],
|
|
6018
|
-
["propagate", propagate, true, "forward-propagate single-use locals & tiny consts (never inflates)"],
|
|
6019
|
-
["devirt", devirt, false, "call_indirect with a constant or known closure-const index \u2192 direct/guarded calls \u2014 grows bytes for speed"],
|
|
6020
|
-
["inlineOnce", inlineOnce, true, "inline single-call functions into their lone caller (never duplicates)"],
|
|
6021
|
-
["inline", inline, false, "inline tiny functions \u2014 can duplicate bodies"],
|
|
6022
|
-
["offset", offset, true, "fold add+const into load/store offset"],
|
|
6023
|
-
["unbranch", unbranch, true, "remove redundant br at end of own block"],
|
|
6024
|
-
["loopify", loopify, true, "collapse block+loop+brif while-idiom into loop+if"],
|
|
6025
|
-
["brif", brif, true, "if-then-br \u2192 br_if"],
|
|
6026
|
-
["foldarms", foldarms, false, "merge identical trailing if arms \u2014 can add block wrapper"],
|
|
6027
|
-
["deadcode", deadcode, true, "eliminate dead code after unreachable/br/return"],
|
|
6028
|
-
["vacuum", vacuum, true, "remove nops, drop-of-pure, empty branches"],
|
|
6029
|
-
["mergeBlocks", mergeBlocks, true, "unwrap `(block $L \u2026)` whose label is never targeted"],
|
|
6030
|
-
["coalesce", coalesceLocals, true, "share local slots between same-type non-overlapping locals"],
|
|
6031
|
-
["locals", localReuse, true, "remove unused locals"],
|
|
6032
|
-
["dedupe", dedupe, true, "eliminate duplicate functions"],
|
|
6033
|
-
["dedupTypes", dedupTypes, true, "merge identical type definitions"],
|
|
6034
|
-
["packData", packData, true, "trim trailing zeros, merge adjacent data segments"],
|
|
6035
|
-
["reorder", reorder, false, "put hot functions first \u2014 no AST reduction"],
|
|
6036
|
-
["treeshake", treeshake, true, "remove unused funcs/globals/types/tables"],
|
|
6037
|
-
["minifyImports", minifyImports, false, "shorten import names \u2014 enable only when you control the host"]
|
|
6038
|
-
];
|
|
6039
|
-
var OPTS = Object.fromEntries(PASSES.map((p) => [p[0], p[2]]));
|
|
6040
|
-
var normalize3 = (opts) => {
|
|
6041
|
-
if (opts === false) return {};
|
|
6042
|
-
if (opts !== true && typeof opts !== "string") {
|
|
6043
|
-
const m2 = { ...opts };
|
|
6044
|
-
for (const p of PASSES) if (m2[p[0]] === void 0) m2[p[0]] = p[2];
|
|
6045
|
-
return m2;
|
|
6046
|
-
}
|
|
6047
|
-
const set = typeof opts === "string" ? new Set(opts.split(/\s+/).filter(Boolean)) : null;
|
|
6048
|
-
const m = {};
|
|
6049
|
-
for (const p of PASSES) m[p[0]] = set ? set.has("all") || set.has(p[0]) : p[2];
|
|
6050
|
-
return m;
|
|
6051
|
-
};
|
|
6052
|
-
var mayInline = (ast) => {
|
|
6053
|
-
if (!Array.isArray(ast)) return false;
|
|
6054
|
-
const callRefs = /* @__PURE__ */ new Map(), pinned = /* @__PURE__ */ new Set(), other = /* @__PURE__ */ new Set();
|
|
6055
|
-
const scan = (n) => {
|
|
6056
|
-
if (!Array.isArray(n)) return;
|
|
6057
|
-
const op = n[0];
|
|
6058
|
-
if (op === "call" && typeof n[1] === "string") callRefs.set(n[1], (callRefs.get(n[1]) || 0) + 1);
|
|
6059
|
-
else if (op === "return_call" && typeof n[1] === "string") other.add(n[1]);
|
|
6060
|
-
else if (op === "ref.func" && typeof n[1] === "string") pinned.add(n[1]);
|
|
6061
|
-
else if (op === "export" && Array.isArray(n[2]) && n[2][0] === "func" && typeof n[2][1] === "string") pinned.add(n[2][1]);
|
|
6062
|
-
else if (op === "start" && typeof n[1] === "string") pinned.add(n[1]);
|
|
6063
|
-
else if (op === "elem") {
|
|
6064
|
-
for (const c of n) if (typeof c === "string" && c[0] === "$") pinned.add(c);
|
|
6065
|
-
}
|
|
6066
|
-
for (let i = 1; i < n.length; i++) scan(n[i]);
|
|
6067
|
-
};
|
|
6068
|
-
scan(ast);
|
|
6069
|
-
for (const n of ast) {
|
|
6070
|
-
if (!Array.isArray(n) || n[0] !== "func" || typeof n[1] !== "string") continue;
|
|
6071
|
-
const name2 = n[1];
|
|
6072
|
-
if (callRefs.get(name2) !== 1 || pinned.has(name2) || other.has(name2)) continue;
|
|
6073
|
-
if (n.some((c) => Array.isArray(c) && c[0] === "export")) continue;
|
|
6074
|
-
return true;
|
|
6075
|
-
}
|
|
6076
|
-
return false;
|
|
6077
|
-
};
|
|
6078
|
-
var START_HEAD = /* @__PURE__ */ new Set(["export", "type", "param", "result", "local"]);
|
|
6079
|
-
function pruneEmptyStart(ast) {
|
|
6080
|
-
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
6081
|
-
const fn = ast.find((n) => Array.isArray(n) && n[0] === "func" && n[1] === "$__start");
|
|
6082
|
-
if (!fn) return ast;
|
|
6083
|
-
let b = 2;
|
|
6084
|
-
while (b < fn.length && Array.isArray(fn[b]) && START_HEAD.has(fn[b][0])) b++;
|
|
6085
|
-
if (b < fn.length) return ast;
|
|
6086
|
-
return ast.filter((n) => !(Array.isArray(n) && (n[0] === "func" && n[1] === "$__start" || n[0] === "start" && n[1] === "$__start")));
|
|
6087
|
-
}
|
|
6088
|
-
function optimize(ast, opts = true) {
|
|
6089
|
-
if (typeof ast === "string") ast = parse_default(ast);
|
|
6090
|
-
const strictGuard = opts === true;
|
|
6091
|
-
opts = normalize3(opts);
|
|
6092
|
-
opts.pin = opts.pin instanceof Set ? opts.pin : new Set(opts.pin || []);
|
|
6093
|
-
const log = opts.log ? (msg, delta) => opts.log(msg, delta) : () => {
|
|
6094
|
-
};
|
|
6095
|
-
const verbose = opts.verbose || opts.log;
|
|
6096
|
-
ast = clone2(ast);
|
|
6097
|
-
const runInline = (a) => {
|
|
6098
|
-
if (!opts.inline) return a;
|
|
6099
|
-
a = inline(a, { simdOnly: opts.inline === "simd", pin: opts.pin });
|
|
6100
|
-
if (opts.propagate) a = propagate(a);
|
|
6101
|
-
if (opts.mergeBlocks) a = mergeBlocks(a);
|
|
6102
|
-
if (opts.vacuum) a = vacuum(a);
|
|
6103
|
-
if (opts.coalesceLocals) a = coalesceLocals(a);
|
|
6104
|
-
return a;
|
|
6105
|
-
};
|
|
6106
|
-
const finish = (a) => {
|
|
6107
|
-
a = runInline(a);
|
|
6108
|
-
return pruneEmptyStart(opts.devirt ? devirt(a) : a);
|
|
6109
|
-
};
|
|
6110
|
-
if (!((opts.inlineOnce || opts.inline) && mayInline(ast))) {
|
|
6111
|
-
for (let round = 0; round < 3; round++) {
|
|
6112
|
-
const beforeRound2 = clone2(ast);
|
|
6113
|
-
for (const [key, fn] of PASSES) if (opts[key] && key !== "inlineOnce" && key !== "inline" && key !== "devirt") ast = fn(ast, opts);
|
|
6114
|
-
if (equal(beforeRound2, ast)) break;
|
|
6115
|
-
if (verbose) log(` round ${round + 1} applied`);
|
|
6116
|
-
}
|
|
6117
|
-
return finish(ast);
|
|
6118
|
-
}
|
|
6119
|
-
let beforeRound = null;
|
|
6120
|
-
let sizeBefore = binarySize(ast);
|
|
6121
|
-
for (let round = 0; round < 3; round++) {
|
|
6122
|
-
beforeRound = clone2(ast);
|
|
6123
|
-
for (const [key, fn] of PASSES) if (opts[key] && key !== "devirt" && key !== "inline") ast = fn(ast, opts);
|
|
6124
|
-
if (opts.propagate && (opts.inlineOnce || opts.inline)) ast = propagate(ast);
|
|
6125
|
-
if (equal(beforeRound, ast)) break;
|
|
6126
|
-
const sizeAfter = binarySize(ast);
|
|
6127
|
-
const delta = sizeAfter - sizeBefore;
|
|
6128
|
-
if (verbose || delta !== 0) log(` round ${round + 1}: ${delta > 0 ? "+" : ""}${delta} bytes`, delta);
|
|
6129
|
-
const tolerance = strictGuard ? 0 : 16;
|
|
6130
|
-
if (delta > tolerance) {
|
|
6131
|
-
if (verbose) log(` \u26A0 round ${round + 1} inflated by ${delta} bytes, reverting`, delta);
|
|
6132
|
-
ast = beforeRound;
|
|
6133
|
-
break;
|
|
6134
|
-
}
|
|
6135
|
-
sizeBefore = sizeAfter;
|
|
6136
|
-
}
|
|
6137
|
-
return finish(ast);
|
|
6138
|
-
}
|
|
6139
|
-
|
|
6140
|
-
// src/template.js
|
|
6141
|
-
var PUA = "\uE000";
|
|
6142
|
-
var exprType = (node, ctx = {}) => {
|
|
6143
|
-
if (!Array.isArray(node)) {
|
|
6144
|
-
if (typeof node === "string" && node[0] === "$" && ctx.locals?.[node]) return ctx.locals[node];
|
|
6145
|
-
return null;
|
|
6146
|
-
}
|
|
6147
|
-
const [op, ...args] = node;
|
|
6148
|
-
const rt = resultType(op);
|
|
6149
|
-
if (rt) return rt;
|
|
6150
|
-
if (op === "local.get" && ctx.locals?.[args[0]]) return ctx.locals[args[0]];
|
|
6151
|
-
if (op === "call" && ctx.funcs?.[args[0]]) return ctx.funcs[args[0]].result?.[0];
|
|
6152
|
-
return null;
|
|
6153
|
-
};
|
|
6154
|
-
function walk3(node, fn) {
|
|
6155
|
-
node = fn(node);
|
|
6156
|
-
if (Array.isArray(node)) {
|
|
6157
|
-
for (let i = 0; i < node.length; i++) {
|
|
6158
|
-
let child = walk3(node[i], fn);
|
|
6159
|
-
if (child?._splice) node.splice(i, 1, ...child), i += child.length - 1;
|
|
6160
|
-
else node[i] = child;
|
|
6161
|
-
}
|
|
6162
|
-
}
|
|
6163
|
-
return node;
|
|
2343
|
+
return node;
|
|
6164
2344
|
}
|
|
6165
2345
|
function inferImports(ast, funcs) {
|
|
6166
2346
|
const imports = [];
|
|
6167
2347
|
const importMap = /* @__PURE__ */ new Map();
|
|
6168
|
-
|
|
2348
|
+
walk(ast, (node) => {
|
|
6169
2349
|
if (!Array.isArray(node)) return node;
|
|
6170
2350
|
if (node[0] === "call" && typeof node[1] === "function") {
|
|
6171
2351
|
const fn = node[1];
|
|
@@ -6193,7 +2373,7 @@ function genImports(imports) {
|
|
|
6193
2373
|
);
|
|
6194
2374
|
}
|
|
6195
2375
|
function compile2(backend2, source, values) {
|
|
6196
|
-
const { parse, compile: emit, optimize
|
|
2376
|
+
const { parse, compile: emit, optimize, polyfill } = backend2;
|
|
6197
2377
|
let opts = {};
|
|
6198
2378
|
if (!Array.isArray(source) && values.length && typeof values[values.length - 1] === "object" && values[values.length - 1] !== null && !values[values.length - 1].byteLength) {
|
|
6199
2379
|
opts = values.pop();
|
|
@@ -6206,7 +2386,7 @@ function compile2(backend2, source, values) {
|
|
|
6206
2386
|
let ast = parse(src);
|
|
6207
2387
|
const funcsToImport = [];
|
|
6208
2388
|
let idx = 0;
|
|
6209
|
-
ast =
|
|
2389
|
+
ast = walk(ast, (node) => {
|
|
6210
2390
|
if (node === PUA) {
|
|
6211
2391
|
const value = values[idx++];
|
|
6212
2392
|
if (typeof value === "function") {
|
|
@@ -6244,16 +2424,16 @@ function compile2(backend2, source, values) {
|
|
|
6244
2424
|
}
|
|
6245
2425
|
}
|
|
6246
2426
|
}
|
|
6247
|
-
if (opts.polyfill) ast =
|
|
6248
|
-
if (opts.optimize) ast =
|
|
2427
|
+
if (opts.polyfill) ast = applyTransform(polyfill, "polyfill", ast, opts.polyfill);
|
|
2428
|
+
if (opts.optimize) ast = applyTransform(optimize, "optimize", ast, opts.optimize);
|
|
6249
2429
|
const binary = emit(ast);
|
|
6250
2430
|
if (importObjs) binary._imports = importObjs;
|
|
6251
2431
|
return binary;
|
|
6252
2432
|
}
|
|
6253
2433
|
if (opts.polyfill || opts.optimize) {
|
|
6254
2434
|
let ast = typeof source === "string" ? parse(source) : source;
|
|
6255
|
-
if (opts.polyfill) ast =
|
|
6256
|
-
if (opts.optimize) ast =
|
|
2435
|
+
if (opts.polyfill) ast = applyTransform(polyfill, "polyfill", ast, opts.polyfill);
|
|
2436
|
+
if (opts.optimize) ast = applyTransform(optimize, "optimize", ast, opts.optimize);
|
|
6257
2437
|
return emit(ast);
|
|
6258
2438
|
}
|
|
6259
2439
|
return emit(source);
|
|
@@ -6266,7 +2446,7 @@ function watr(backend2, source, values) {
|
|
|
6266
2446
|
}
|
|
6267
2447
|
|
|
6268
2448
|
// watr.js
|
|
6269
|
-
var backend = { parse: parse_default, compile
|
|
2449
|
+
var backend = { parse: parse_default, compile };
|
|
6270
2450
|
function compile3(source, ...values) {
|
|
6271
2451
|
return compile2(backend, source, values);
|
|
6272
2452
|
}
|
|
@@ -6277,9 +2457,7 @@ var watr_default = watr2;
|
|
|
6277
2457
|
export {
|
|
6278
2458
|
compile3 as compile,
|
|
6279
2459
|
watr_default as default,
|
|
6280
|
-
optimize,
|
|
6281
2460
|
parse_default as parse,
|
|
6282
|
-
polyfill,
|
|
6283
2461
|
print,
|
|
6284
2462
|
watr2 as watr
|
|
6285
2463
|
};
|