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