watr 4.1.0 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/watr.js +29 -6
- package/dist/watr.js +829 -17
- package/dist/watr.min.js +5 -5
- package/package.json +1 -1
- package/readme.md +13 -38
- package/src/compile.js +20 -10
- package/src/optimize.js +1184 -0
- package/src/parse.js +2 -2
- package/src/util.js +4 -4
- package/types/src/compile.d.ts.map +1 -1
- package/types/src/optimize.d.ts +88 -0
- package/types/src/optimize.d.ts.map +1 -0
- package/types/src/util.d.ts +3 -3
- package/types/src/util.d.ts.map +1 -1
- package/types/watr.d.ts +6 -3
- package/types/watr.d.ts.map +1 -1
- package/watr.js +12 -6
package/dist/watr.js
CHANGED
|
@@ -18,7 +18,7 @@ __export(encode_exports, {
|
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
// src/util.js
|
|
21
|
-
var err = (text, pos = err.
|
|
21
|
+
var err = (text, pos = err.loc) => {
|
|
22
22
|
if (pos != null && err.src) {
|
|
23
23
|
let line = 1, col = 1;
|
|
24
24
|
for (let i = 0; i < pos && i < err.src.length; i++) {
|
|
@@ -951,7 +951,7 @@ var parse_default = (str2) => {
|
|
|
951
951
|
let i = 0, level = [], buf = "", q = 0, depth = 0;
|
|
952
952
|
const commit = () => buf && (level.push(buf), buf = "");
|
|
953
953
|
const parseLevel = (pos) => {
|
|
954
|
-
level.
|
|
954
|
+
level.loc = pos;
|
|
955
955
|
for (let c, root, p; i < str2.length; ) {
|
|
956
956
|
c = str2.charCodeAt(i);
|
|
957
957
|
if (q === 34) buf += str2[i++], c === 92 ? buf += str2[i++] : c === 34 && (commit(), q = 0);
|
|
@@ -996,14 +996,14 @@ var cleanup = (node, result) => !Array.isArray(node) ? typeof node !== "string"
|
|
|
996
996
|
) : (
|
|
997
997
|
// remove annotations like (@name ...) except @custom and @metadata.code.*
|
|
998
998
|
node[0]?.[0] === "@" && node[0] !== "@custom" && !node[0]?.startsWith?.("@metadata.code.") ? null : (
|
|
999
|
-
// unwrap single-element array containing module (after removing comments), preserve .
|
|
1000
|
-
(result = node.map(cleanup).filter((n) => n != null), result.
|
|
999
|
+
// unwrap single-element array containing module (after removing comments), preserve .loc
|
|
1000
|
+
(result = node.map(cleanup).filter((n) => n != null), result.loc = node.loc, result.length === 1 && result[0]?.[0] === "module" ? result[0] : result)
|
|
1001
1001
|
)
|
|
1002
1002
|
);
|
|
1003
1003
|
function compile(nodes) {
|
|
1004
1004
|
if (typeof nodes === "string") err.src = nodes, nodes = parse_default(nodes) || [];
|
|
1005
1005
|
else err.src = "";
|
|
1006
|
-
err.
|
|
1006
|
+
err.loc = 0;
|
|
1007
1007
|
nodes = cleanup(nodes) || [];
|
|
1008
1008
|
let idx = 0;
|
|
1009
1009
|
if (nodes[0] === "module") idx++, isId(nodes[idx]) && idx++;
|
|
@@ -1015,12 +1015,25 @@ function compile(nodes) {
|
|
|
1015
1015
|
ctx.metadata = {};
|
|
1016
1016
|
nodes.slice(idx).filter((n) => {
|
|
1017
1017
|
if (!Array.isArray(n)) {
|
|
1018
|
-
let pos = err.
|
|
1019
|
-
|
|
1018
|
+
let pos = err.loc, src = err.src, c;
|
|
1019
|
+
while ((pos = src.indexOf(n, pos)) >= 0) {
|
|
1020
|
+
c = src.charCodeAt(pos - 1);
|
|
1021
|
+
if (pos > 0 && (c > 47 && c < 58 || c > 64 && c < 91 || c > 96 && c < 123 || c === 95 || c === 36)) {
|
|
1022
|
+
pos++;
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
c = src.charCodeAt(pos + n.length);
|
|
1026
|
+
if (c > 47 && c < 58 || c > 64 && c < 91 || c > 96 && c < 123 || c === 95) {
|
|
1027
|
+
pos++;
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
break;
|
|
1031
|
+
}
|
|
1032
|
+
if (pos >= 0) err.loc = pos;
|
|
1020
1033
|
err(`Unexpected token ${n}`);
|
|
1021
1034
|
}
|
|
1022
1035
|
let [kind, ...node] = n;
|
|
1023
|
-
err.
|
|
1036
|
+
err.loc = n.loc;
|
|
1024
1037
|
if (kind === "@custom") {
|
|
1025
1038
|
ctx.custom.push(node);
|
|
1026
1039
|
} else if (kind === "rec") {
|
|
@@ -1037,7 +1050,7 @@ function compile(nodes) {
|
|
|
1037
1050
|
else return true;
|
|
1038
1051
|
}).forEach((n) => {
|
|
1039
1052
|
let [kind, ...node] = n;
|
|
1040
|
-
err.
|
|
1053
|
+
err.loc = n.loc;
|
|
1041
1054
|
let imported;
|
|
1042
1055
|
if (kind === "import") [kind, ...node] = (imported = node).pop();
|
|
1043
1056
|
let items = ctx[kind];
|
|
@@ -1147,7 +1160,7 @@ function normalize(nodes, ctx) {
|
|
|
1147
1160
|
} else if ((node.startsWith("memory.") || node.endsWith("load") || node.endsWith("store")) && isIdx(nodes[0])) out.push(nodes.shift());
|
|
1148
1161
|
} else if (Array.isArray(node)) {
|
|
1149
1162
|
const op = node[0];
|
|
1150
|
-
node.
|
|
1163
|
+
node.loc != null && (err.loc = node.loc);
|
|
1151
1164
|
if (op?.startsWith?.("@metadata.code.")) {
|
|
1152
1165
|
let type = op.slice(15);
|
|
1153
1166
|
out.push(["@metadata", type, node[1]]);
|
|
@@ -1561,7 +1574,7 @@ var instr = (nodes, ctx) => {
|
|
|
1561
1574
|
continue;
|
|
1562
1575
|
}
|
|
1563
1576
|
if (Array.isArray(op)) {
|
|
1564
|
-
op.
|
|
1577
|
+
op.loc != null && (err.loc = op.loc);
|
|
1565
1578
|
err(`Unknown instruction ${op[0]}`);
|
|
1566
1579
|
}
|
|
1567
1580
|
let [...bytes] = INSTR[op] || err(`Unknown instruction ${op}`);
|
|
@@ -2566,6 +2579,801 @@ function polyfill(ast, opts = true) {
|
|
|
2566
2579
|
return ast;
|
|
2567
2580
|
}
|
|
2568
2581
|
|
|
2582
|
+
// src/optimize.js
|
|
2583
|
+
var OPTS = {
|
|
2584
|
+
treeshake: true,
|
|
2585
|
+
// remove unused funcs/globals/types/tables
|
|
2586
|
+
fold: true,
|
|
2587
|
+
// constant folding
|
|
2588
|
+
deadcode: true,
|
|
2589
|
+
// eliminate dead code after unreachable/br/return
|
|
2590
|
+
locals: true,
|
|
2591
|
+
// remove unused locals
|
|
2592
|
+
identity: true,
|
|
2593
|
+
// remove identity ops (x + 0 → x)
|
|
2594
|
+
strength: true,
|
|
2595
|
+
// strength reduction (x * 2 → x << 1)
|
|
2596
|
+
branch: true,
|
|
2597
|
+
// simplify constant branches
|
|
2598
|
+
propagate: true,
|
|
2599
|
+
// constant propagation through locals
|
|
2600
|
+
inline: true
|
|
2601
|
+
// inline tiny functions
|
|
2602
|
+
};
|
|
2603
|
+
var ALL2 = Object.keys(OPTS);
|
|
2604
|
+
var normalize3 = (opts) => {
|
|
2605
|
+
if (opts === true) return { ...OPTS };
|
|
2606
|
+
if (opts === false) return {};
|
|
2607
|
+
if (typeof opts === "string") {
|
|
2608
|
+
const set = new Set(opts.split(/\s+/).filter(Boolean));
|
|
2609
|
+
if (set.size === 1 && ALL2.includes([...set][0])) {
|
|
2610
|
+
return Object.fromEntries(ALL2.map((f) => [f, set.has(f)]));
|
|
2611
|
+
}
|
|
2612
|
+
return Object.fromEntries(ALL2.map((f) => [f, set.has(f) || set.has("all")]));
|
|
2613
|
+
}
|
|
2614
|
+
return { ...OPTS, ...opts };
|
|
2615
|
+
};
|
|
2616
|
+
var clone2 = (node) => {
|
|
2617
|
+
if (!Array.isArray(node)) return node;
|
|
2618
|
+
return node.map(clone2);
|
|
2619
|
+
};
|
|
2620
|
+
var walk2 = (node, fn, parent, idx) => {
|
|
2621
|
+
fn(node, parent, idx);
|
|
2622
|
+
if (Array.isArray(node)) for (let i = 0; i < node.length; i++) walk2(node[i], fn, node, i);
|
|
2623
|
+
};
|
|
2624
|
+
var walkPost2 = (node, fn, parent, idx) => {
|
|
2625
|
+
if (Array.isArray(node)) {
|
|
2626
|
+
for (let i = 0; i < node.length; i++) {
|
|
2627
|
+
const result2 = walkPost2(node[i], fn, node, i);
|
|
2628
|
+
if (result2 !== void 0) node[i] = result2;
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
const result = fn(node, parent, idx);
|
|
2632
|
+
return result !== void 0 ? result : node;
|
|
2633
|
+
};
|
|
2634
|
+
var treeshake = (ast) => {
|
|
2635
|
+
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
2636
|
+
const funcs = /* @__PURE__ */ new Map();
|
|
2637
|
+
const globals = /* @__PURE__ */ new Map();
|
|
2638
|
+
const types = /* @__PURE__ */ new Map();
|
|
2639
|
+
const tables = /* @__PURE__ */ new Map();
|
|
2640
|
+
const memories = /* @__PURE__ */ new Map();
|
|
2641
|
+
const exports = [];
|
|
2642
|
+
const starts = [];
|
|
2643
|
+
let funcIdx = 0, globalIdx = 0, typeIdx = 0, tableIdx = 0, memIdx = 0, importFuncIdx = 0;
|
|
2644
|
+
for (const node of ast.slice(1)) {
|
|
2645
|
+
if (!Array.isArray(node)) continue;
|
|
2646
|
+
const kind = node[0];
|
|
2647
|
+
if (kind === "type") {
|
|
2648
|
+
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : typeIdx;
|
|
2649
|
+
types.set(name2, { node, idx: typeIdx, used: false });
|
|
2650
|
+
if (typeof name2 === "string") types.set(typeIdx, types.get(name2));
|
|
2651
|
+
typeIdx++;
|
|
2652
|
+
} else if (kind === "func") {
|
|
2653
|
+
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : funcIdx;
|
|
2654
|
+
const hasInlineExport = node.some((sub) => Array.isArray(sub) && sub[0] === "export");
|
|
2655
|
+
funcs.set(name2, { node, idx: funcIdx, used: hasInlineExport });
|
|
2656
|
+
if (typeof name2 === "string") funcs.set(funcIdx, funcs.get(name2));
|
|
2657
|
+
funcIdx++;
|
|
2658
|
+
} else if (kind === "global") {
|
|
2659
|
+
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : globalIdx;
|
|
2660
|
+
const hasInlineExport = node.some((sub) => Array.isArray(sub) && sub[0] === "export");
|
|
2661
|
+
globals.set(name2, { node, idx: globalIdx, used: hasInlineExport });
|
|
2662
|
+
if (typeof name2 === "string") globals.set(globalIdx, globals.get(name2));
|
|
2663
|
+
globalIdx++;
|
|
2664
|
+
} else if (kind === "table") {
|
|
2665
|
+
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : tableIdx;
|
|
2666
|
+
const hasInlineExport = node.some((sub) => Array.isArray(sub) && sub[0] === "export");
|
|
2667
|
+
tables.set(name2, { node, idx: tableIdx, used: hasInlineExport });
|
|
2668
|
+
if (typeof name2 === "string") tables.set(tableIdx, tables.get(name2));
|
|
2669
|
+
tableIdx++;
|
|
2670
|
+
} else if (kind === "memory") {
|
|
2671
|
+
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : memIdx;
|
|
2672
|
+
const hasInlineExport = node.some((sub) => Array.isArray(sub) && sub[0] === "export");
|
|
2673
|
+
memories.set(name2, { node, idx: memIdx, used: hasInlineExport });
|
|
2674
|
+
if (typeof name2 === "string") memories.set(memIdx, memories.get(name2));
|
|
2675
|
+
memIdx++;
|
|
2676
|
+
} else if (kind === "import") {
|
|
2677
|
+
for (const sub of node) {
|
|
2678
|
+
if (Array.isArray(sub) && sub[0] === "func") {
|
|
2679
|
+
const name2 = typeof sub[1] === "string" && sub[1][0] === "$" ? sub[1] : importFuncIdx;
|
|
2680
|
+
funcs.set(name2, { node, idx: importFuncIdx, used: true, isImport: true });
|
|
2681
|
+
if (typeof name2 === "string") funcs.set(importFuncIdx, funcs.get(name2));
|
|
2682
|
+
importFuncIdx++;
|
|
2683
|
+
funcIdx++;
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
} else if (kind === "export") {
|
|
2687
|
+
exports.push(node);
|
|
2688
|
+
} else if (kind === "start") {
|
|
2689
|
+
starts.push(node);
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
for (const exp of exports) {
|
|
2693
|
+
for (const sub of exp) {
|
|
2694
|
+
if (!Array.isArray(sub)) continue;
|
|
2695
|
+
const [kind, ref] = sub;
|
|
2696
|
+
if (kind === "func" && funcs.has(ref)) funcs.get(ref).used = true;
|
|
2697
|
+
else if (kind === "global" && globals.has(ref)) globals.get(ref).used = true;
|
|
2698
|
+
else if (kind === "table" && tables.has(ref)) tables.get(ref).used = true;
|
|
2699
|
+
else if (kind === "memory" && memories.has(ref)) memories.get(ref).used = true;
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
for (const start of starts) {
|
|
2703
|
+
let ref = start[1];
|
|
2704
|
+
if (typeof ref === "string" && ref[0] !== "$") ref = +ref;
|
|
2705
|
+
if (funcs.has(ref)) funcs.get(ref).used = true;
|
|
2706
|
+
}
|
|
2707
|
+
let hasExports = exports.length > 0 || starts.length > 0;
|
|
2708
|
+
if (!hasExports) {
|
|
2709
|
+
for (const [, entry] of funcs) if (entry.used) {
|
|
2710
|
+
hasExports = true;
|
|
2711
|
+
break;
|
|
2712
|
+
}
|
|
2713
|
+
if (!hasExports) {
|
|
2714
|
+
for (const [, entry] of globals) if (entry.used) {
|
|
2715
|
+
hasExports = true;
|
|
2716
|
+
break;
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
if (!hasExports) {
|
|
2720
|
+
for (const [, entry] of tables) if (entry.used) {
|
|
2721
|
+
hasExports = true;
|
|
2722
|
+
break;
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
if (!hasExports) {
|
|
2726
|
+
for (const [, entry] of memories) if (entry.used) {
|
|
2727
|
+
hasExports = true;
|
|
2728
|
+
break;
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
if (!hasExports) {
|
|
2733
|
+
for (const [, entry] of funcs) entry.used = true;
|
|
2734
|
+
for (const [, entry] of globals) entry.used = true;
|
|
2735
|
+
for (const [, entry] of tables) entry.used = true;
|
|
2736
|
+
for (const [, entry] of memories) entry.used = true;
|
|
2737
|
+
}
|
|
2738
|
+
for (const node of ast.slice(1)) {
|
|
2739
|
+
if (!Array.isArray(node) || node[0] !== "elem") continue;
|
|
2740
|
+
walk2(node, (n) => {
|
|
2741
|
+
if (Array.isArray(n) && n[0] === "ref.func") {
|
|
2742
|
+
const ref = n[1];
|
|
2743
|
+
if (funcs.has(ref)) funcs.get(ref).used = true;
|
|
2744
|
+
}
|
|
2745
|
+
if (typeof n === "string" && n[0] === "$" && funcs.has(n)) funcs.get(n).used = true;
|
|
2746
|
+
});
|
|
2747
|
+
}
|
|
2748
|
+
let changed = true;
|
|
2749
|
+
while (changed) {
|
|
2750
|
+
changed = false;
|
|
2751
|
+
for (const [, entry] of funcs) {
|
|
2752
|
+
if (!entry.used || entry.isImport) continue;
|
|
2753
|
+
walk2(entry.node, (n) => {
|
|
2754
|
+
if (!Array.isArray(n)) {
|
|
2755
|
+
if (typeof n === "string" && n[0] === "$" && funcs.has(n) && !funcs.get(n).used) {
|
|
2756
|
+
funcs.get(n).used = true;
|
|
2757
|
+
changed = true;
|
|
2758
|
+
}
|
|
2759
|
+
return;
|
|
2760
|
+
}
|
|
2761
|
+
const [op, ref] = n;
|
|
2762
|
+
if ((op === "call" || op === "return_call" || op === "ref.func") && funcs.has(ref) && !funcs.get(ref).used) {
|
|
2763
|
+
funcs.get(ref).used = true;
|
|
2764
|
+
changed = true;
|
|
2765
|
+
}
|
|
2766
|
+
if ((op === "global.get" || op === "global.set") && globals.has(ref) && !globals.get(ref).used) {
|
|
2767
|
+
globals.get(ref).used = true;
|
|
2768
|
+
changed = true;
|
|
2769
|
+
}
|
|
2770
|
+
if (op === "call_indirect" || op === "return_call_indirect") {
|
|
2771
|
+
for (const sub of n) {
|
|
2772
|
+
if (typeof sub === "string" && sub[0] === "$" && tables.has(sub) && !tables.get(sub).used) {
|
|
2773
|
+
tables.get(sub).used = true;
|
|
2774
|
+
changed = true;
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
if (op === "type" && types.has(ref) && !types.get(ref).used) {
|
|
2779
|
+
types.get(ref).used = true;
|
|
2780
|
+
changed = true;
|
|
2781
|
+
}
|
|
2782
|
+
});
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
const result = ["module"];
|
|
2786
|
+
for (const node of ast.slice(1)) {
|
|
2787
|
+
if (!Array.isArray(node)) {
|
|
2788
|
+
result.push(node);
|
|
2789
|
+
continue;
|
|
2790
|
+
}
|
|
2791
|
+
const kind = node[0];
|
|
2792
|
+
if (kind === "func") {
|
|
2793
|
+
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
2794
|
+
const entry = name2 ? funcs.get(name2) : [...funcs.values()].find((e) => e.node === node);
|
|
2795
|
+
if (entry?.used) result.push(node);
|
|
2796
|
+
} else if (kind === "global") {
|
|
2797
|
+
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
2798
|
+
const entry = name2 ? globals.get(name2) : [...globals.values()].find((e) => e.node === node);
|
|
2799
|
+
if (entry?.used) result.push(node);
|
|
2800
|
+
} else if (kind === "type") {
|
|
2801
|
+
result.push(node);
|
|
2802
|
+
} else {
|
|
2803
|
+
result.push(node);
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
return result;
|
|
2807
|
+
};
|
|
2808
|
+
var FOLDABLE = {
|
|
2809
|
+
// i32
|
|
2810
|
+
"i32.add": (a, b) => a + b | 0,
|
|
2811
|
+
"i32.sub": (a, b) => a - b | 0,
|
|
2812
|
+
"i32.mul": (a, b) => Math.imul(a, b),
|
|
2813
|
+
"i32.div_s": (a, b) => b !== 0 ? a / b | 0 : null,
|
|
2814
|
+
"i32.div_u": (a, b) => b !== 0 ? (a >>> 0) / (b >>> 0) | 0 : null,
|
|
2815
|
+
"i32.rem_s": (a, b) => b !== 0 ? a % b | 0 : null,
|
|
2816
|
+
"i32.rem_u": (a, b) => b !== 0 ? (a >>> 0) % (b >>> 0) | 0 : null,
|
|
2817
|
+
"i32.and": (a, b) => a & b,
|
|
2818
|
+
"i32.or": (a, b) => a | b,
|
|
2819
|
+
"i32.xor": (a, b) => a ^ b,
|
|
2820
|
+
"i32.shl": (a, b) => a << (b & 31),
|
|
2821
|
+
"i32.shr_s": (a, b) => a >> (b & 31),
|
|
2822
|
+
"i32.shr_u": (a, b) => a >>> (b & 31),
|
|
2823
|
+
"i32.rotl": (a, b) => {
|
|
2824
|
+
b &= 31;
|
|
2825
|
+
return a << b | a >>> 32 - b | 0;
|
|
2826
|
+
},
|
|
2827
|
+
"i32.rotr": (a, b) => {
|
|
2828
|
+
b &= 31;
|
|
2829
|
+
return a >>> b | a << 32 - b | 0;
|
|
2830
|
+
},
|
|
2831
|
+
"i32.eq": (a, b) => a === b ? 1 : 0,
|
|
2832
|
+
"i32.ne": (a, b) => a !== b ? 1 : 0,
|
|
2833
|
+
"i32.lt_s": (a, b) => a < b ? 1 : 0,
|
|
2834
|
+
"i32.lt_u": (a, b) => a >>> 0 < b >>> 0 ? 1 : 0,
|
|
2835
|
+
"i32.gt_s": (a, b) => a > b ? 1 : 0,
|
|
2836
|
+
"i32.gt_u": (a, b) => a >>> 0 > b >>> 0 ? 1 : 0,
|
|
2837
|
+
"i32.le_s": (a, b) => a <= b ? 1 : 0,
|
|
2838
|
+
"i32.le_u": (a, b) => a >>> 0 <= b >>> 0 ? 1 : 0,
|
|
2839
|
+
"i32.ge_s": (a, b) => a >= b ? 1 : 0,
|
|
2840
|
+
"i32.ge_u": (a, b) => a >>> 0 >= b >>> 0 ? 1 : 0,
|
|
2841
|
+
"i32.eqz": (a) => a === 0 ? 1 : 0,
|
|
2842
|
+
"i32.clz": (a) => Math.clz32(a),
|
|
2843
|
+
"i32.ctz": (a) => a === 0 ? 32 : 31 - Math.clz32(a & -a),
|
|
2844
|
+
"i32.popcnt": (a) => {
|
|
2845
|
+
let c = 0;
|
|
2846
|
+
while (a) {
|
|
2847
|
+
c += a & 1;
|
|
2848
|
+
a >>>= 1;
|
|
2849
|
+
}
|
|
2850
|
+
return c;
|
|
2851
|
+
},
|
|
2852
|
+
"i32.wrap_i64": (a) => Number(BigInt.asIntN(32, a)),
|
|
2853
|
+
// i64 (using BigInt)
|
|
2854
|
+
"i64.add": (a, b) => BigInt.asIntN(64, a + b),
|
|
2855
|
+
"i64.sub": (a, b) => BigInt.asIntN(64, a - b),
|
|
2856
|
+
"i64.mul": (a, b) => BigInt.asIntN(64, a * b),
|
|
2857
|
+
"i64.div_s": (a, b) => b !== 0n ? BigInt.asIntN(64, a / b) : null,
|
|
2858
|
+
"i64.div_u": (a, b) => b !== 0n ? BigInt.asUintN(64, BigInt.asUintN(64, a) / BigInt.asUintN(64, b)) : null,
|
|
2859
|
+
"i64.rem_s": (a, b) => b !== 0n ? BigInt.asIntN(64, a % b) : null,
|
|
2860
|
+
"i64.rem_u": (a, b) => b !== 0n ? BigInt.asUintN(64, BigInt.asUintN(64, a) % BigInt.asUintN(64, b)) : null,
|
|
2861
|
+
"i64.and": (a, b) => BigInt.asIntN(64, a & b),
|
|
2862
|
+
"i64.or": (a, b) => BigInt.asIntN(64, a | b),
|
|
2863
|
+
"i64.xor": (a, b) => BigInt.asIntN(64, a ^ b),
|
|
2864
|
+
"i64.shl": (a, b) => BigInt.asIntN(64, a << (b & 63n)),
|
|
2865
|
+
"i64.shr_s": (a, b) => BigInt.asIntN(64, a >> (b & 63n)),
|
|
2866
|
+
"i64.shr_u": (a, b) => BigInt.asUintN(64, BigInt.asUintN(64, a) >> (b & 63n)),
|
|
2867
|
+
"i64.eq": (a, b) => a === b ? 1 : 0,
|
|
2868
|
+
"i64.ne": (a, b) => a !== b ? 1 : 0,
|
|
2869
|
+
"i64.lt_s": (a, b) => a < b ? 1 : 0,
|
|
2870
|
+
"i64.lt_u": (a, b) => BigInt.asUintN(64, a) < BigInt.asUintN(64, b) ? 1 : 0,
|
|
2871
|
+
"i64.gt_s": (a, b) => a > b ? 1 : 0,
|
|
2872
|
+
"i64.gt_u": (a, b) => BigInt.asUintN(64, a) > BigInt.asUintN(64, b) ? 1 : 0,
|
|
2873
|
+
"i64.le_s": (a, b) => a <= b ? 1 : 0,
|
|
2874
|
+
"i64.le_u": (a, b) => BigInt.asUintN(64, a) <= BigInt.asUintN(64, b) ? 1 : 0,
|
|
2875
|
+
"i64.ge_s": (a, b) => a >= b ? 1 : 0,
|
|
2876
|
+
"i64.ge_u": (a, b) => BigInt.asUintN(64, a) >= BigInt.asUintN(64, b) ? 1 : 0,
|
|
2877
|
+
"i64.eqz": (a) => a === 0n ? 1 : 0,
|
|
2878
|
+
"i64.extend_i32_s": (a) => BigInt(a),
|
|
2879
|
+
"i64.extend_i32_u": (a) => BigInt(a >>> 0),
|
|
2880
|
+
// f32/f64 - be careful with NaN/precision
|
|
2881
|
+
"f32.add": (a, b) => Math.fround(a + b),
|
|
2882
|
+
"f32.sub": (a, b) => Math.fround(a - b),
|
|
2883
|
+
"f32.mul": (a, b) => Math.fround(a * b),
|
|
2884
|
+
"f32.div": (a, b) => Math.fround(a / b),
|
|
2885
|
+
"f32.neg": (a) => Math.fround(-a),
|
|
2886
|
+
"f32.abs": (a) => Math.fround(Math.abs(a)),
|
|
2887
|
+
"f32.sqrt": (a) => Math.fround(Math.sqrt(a)),
|
|
2888
|
+
"f32.ceil": (a) => Math.fround(Math.ceil(a)),
|
|
2889
|
+
"f32.floor": (a) => Math.fround(Math.floor(a)),
|
|
2890
|
+
"f32.trunc": (a) => Math.fround(Math.trunc(a)),
|
|
2891
|
+
"f32.nearest": (a) => Math.fround(Math.round(a)),
|
|
2892
|
+
"f64.add": (a, b) => a + b,
|
|
2893
|
+
"f64.sub": (a, b) => a - b,
|
|
2894
|
+
"f64.mul": (a, b) => a * b,
|
|
2895
|
+
"f64.div": (a, b) => a / b,
|
|
2896
|
+
"f64.neg": (a) => -a,
|
|
2897
|
+
"f64.abs": (a) => Math.abs(a),
|
|
2898
|
+
"f64.sqrt": (a) => Math.sqrt(a),
|
|
2899
|
+
"f64.ceil": (a) => Math.ceil(a),
|
|
2900
|
+
"f64.floor": (a) => Math.floor(a),
|
|
2901
|
+
"f64.trunc": (a) => Math.trunc(a),
|
|
2902
|
+
"f64.nearest": (a) => Math.round(a)
|
|
2903
|
+
};
|
|
2904
|
+
var getConst = (node) => {
|
|
2905
|
+
if (!Array.isArray(node) || node.length !== 2) return null;
|
|
2906
|
+
const [op, val] = node;
|
|
2907
|
+
if (op === "i32.const") return { type: "i32", value: Number(val) | 0 };
|
|
2908
|
+
if (op === "i64.const") return { type: "i64", value: BigInt(val) };
|
|
2909
|
+
if (op === "f32.const") return { type: "f32", value: Math.fround(Number(val)) };
|
|
2910
|
+
if (op === "f64.const") return { type: "f64", value: Number(val) };
|
|
2911
|
+
return null;
|
|
2912
|
+
};
|
|
2913
|
+
var makeConst = (type, value) => {
|
|
2914
|
+
if (type === "i32") return ["i32.const", value | 0];
|
|
2915
|
+
if (type === "i64") return ["i64.const", value];
|
|
2916
|
+
if (type === "f32") return ["f32.const", Math.fround(value)];
|
|
2917
|
+
if (type === "f64") return ["f64.const", value];
|
|
2918
|
+
return null;
|
|
2919
|
+
};
|
|
2920
|
+
var fold = (ast) => {
|
|
2921
|
+
return walkPost2(clone2(ast), (node) => {
|
|
2922
|
+
if (!Array.isArray(node)) return;
|
|
2923
|
+
const op = node[0];
|
|
2924
|
+
const fn = FOLDABLE[op];
|
|
2925
|
+
if (!fn) return;
|
|
2926
|
+
if (fn.length === 1 && node.length === 2) {
|
|
2927
|
+
const a = getConst(node[1]);
|
|
2928
|
+
if (!a) return;
|
|
2929
|
+
const result = fn(a.value);
|
|
2930
|
+
if (result === null) return;
|
|
2931
|
+
const resultType = op.startsWith("i64.") && !op.includes("eqz") ? "i64" : op.startsWith("f32.") ? "f32" : op.startsWith("f64.") ? "f64" : "i32";
|
|
2932
|
+
return makeConst(resultType, result);
|
|
2933
|
+
}
|
|
2934
|
+
if (fn.length === 2 && node.length === 3) {
|
|
2935
|
+
const a = getConst(node[1]);
|
|
2936
|
+
const b = getConst(node[2]);
|
|
2937
|
+
if (!a || !b) return;
|
|
2938
|
+
const result = fn(a.value, b.value);
|
|
2939
|
+
if (result === null) return;
|
|
2940
|
+
const isCompare = /\.(eq|ne|[lg][te])/.test(op);
|
|
2941
|
+
const resultType = isCompare ? "i32" : op.startsWith("i64.") ? "i64" : op.startsWith("f32.") ? "f32" : op.startsWith("f64.") ? "f64" : "i32";
|
|
2942
|
+
return makeConst(resultType, result);
|
|
2943
|
+
}
|
|
2944
|
+
});
|
|
2945
|
+
};
|
|
2946
|
+
var IDENTITIES = {
|
|
2947
|
+
// x + 0 → x, 0 + x → x
|
|
2948
|
+
"i32.add": (a, b) => {
|
|
2949
|
+
const ca = getConst(a), cb = getConst(b);
|
|
2950
|
+
if (ca?.value === 0) return b;
|
|
2951
|
+
if (cb?.value === 0) return a;
|
|
2952
|
+
return null;
|
|
2953
|
+
},
|
|
2954
|
+
"i64.add": (a, b) => {
|
|
2955
|
+
const ca = getConst(a), cb = getConst(b);
|
|
2956
|
+
if (ca?.value === 0n) return b;
|
|
2957
|
+
if (cb?.value === 0n) return a;
|
|
2958
|
+
return null;
|
|
2959
|
+
},
|
|
2960
|
+
// x - 0 → x
|
|
2961
|
+
"i32.sub": (a, b) => getConst(b)?.value === 0 ? a : null,
|
|
2962
|
+
"i64.sub": (a, b) => getConst(b)?.value === 0n ? a : null,
|
|
2963
|
+
// x * 1 → x, 1 * x → x
|
|
2964
|
+
"i32.mul": (a, b) => {
|
|
2965
|
+
const ca = getConst(a), cb = getConst(b);
|
|
2966
|
+
if (ca?.value === 1) return b;
|
|
2967
|
+
if (cb?.value === 1) return a;
|
|
2968
|
+
return null;
|
|
2969
|
+
},
|
|
2970
|
+
"i64.mul": (a, b) => {
|
|
2971
|
+
const ca = getConst(a), cb = getConst(b);
|
|
2972
|
+
if (ca?.value === 1n) return b;
|
|
2973
|
+
if (cb?.value === 1n) return a;
|
|
2974
|
+
return null;
|
|
2975
|
+
},
|
|
2976
|
+
// x / 1 → x
|
|
2977
|
+
"i32.div_s": (a, b) => getConst(b)?.value === 1 ? a : null,
|
|
2978
|
+
"i32.div_u": (a, b) => getConst(b)?.value === 1 ? a : null,
|
|
2979
|
+
"i64.div_s": (a, b) => getConst(b)?.value === 1n ? a : null,
|
|
2980
|
+
"i64.div_u": (a, b) => getConst(b)?.value === 1n ? a : null,
|
|
2981
|
+
// x & -1 → x, -1 & x → x (all bits set)
|
|
2982
|
+
"i32.and": (a, b) => {
|
|
2983
|
+
const ca = getConst(a), cb = getConst(b);
|
|
2984
|
+
if (ca?.value === -1) return b;
|
|
2985
|
+
if (cb?.value === -1) return a;
|
|
2986
|
+
return null;
|
|
2987
|
+
},
|
|
2988
|
+
"i64.and": (a, b) => {
|
|
2989
|
+
const ca = getConst(a), cb = getConst(b);
|
|
2990
|
+
if (ca?.value === -1n) return b;
|
|
2991
|
+
if (cb?.value === -1n) return a;
|
|
2992
|
+
return null;
|
|
2993
|
+
},
|
|
2994
|
+
// x | 0 → x, 0 | x → x
|
|
2995
|
+
"i32.or": (a, b) => {
|
|
2996
|
+
const ca = getConst(a), cb = getConst(b);
|
|
2997
|
+
if (ca?.value === 0) return b;
|
|
2998
|
+
if (cb?.value === 0) return a;
|
|
2999
|
+
return null;
|
|
3000
|
+
},
|
|
3001
|
+
"i64.or": (a, b) => {
|
|
3002
|
+
const ca = getConst(a), cb = getConst(b);
|
|
3003
|
+
if (ca?.value === 0n) return b;
|
|
3004
|
+
if (cb?.value === 0n) return a;
|
|
3005
|
+
return null;
|
|
3006
|
+
},
|
|
3007
|
+
// x ^ 0 → x, 0 ^ x → x
|
|
3008
|
+
"i32.xor": (a, b) => {
|
|
3009
|
+
const ca = getConst(a), cb = getConst(b);
|
|
3010
|
+
if (ca?.value === 0) return b;
|
|
3011
|
+
if (cb?.value === 0) return a;
|
|
3012
|
+
return null;
|
|
3013
|
+
},
|
|
3014
|
+
"i64.xor": (a, b) => {
|
|
3015
|
+
const ca = getConst(a), cb = getConst(b);
|
|
3016
|
+
if (ca?.value === 0n) return b;
|
|
3017
|
+
if (cb?.value === 0n) return a;
|
|
3018
|
+
return null;
|
|
3019
|
+
},
|
|
3020
|
+
// x << 0 → x, x >> 0 → x
|
|
3021
|
+
"i32.shl": (a, b) => getConst(b)?.value === 0 ? a : null,
|
|
3022
|
+
"i32.shr_s": (a, b) => getConst(b)?.value === 0 ? a : null,
|
|
3023
|
+
"i32.shr_u": (a, b) => getConst(b)?.value === 0 ? a : null,
|
|
3024
|
+
"i64.shl": (a, b) => getConst(b)?.value === 0n ? a : null,
|
|
3025
|
+
"i64.shr_s": (a, b) => getConst(b)?.value === 0n ? a : null,
|
|
3026
|
+
"i64.shr_u": (a, b) => getConst(b)?.value === 0n ? a : null
|
|
3027
|
+
// f + 0 → x (careful with -0.0, skip for floats)
|
|
3028
|
+
// f * 1 → x (careful with NaN, skip for floats)
|
|
3029
|
+
};
|
|
3030
|
+
var identity = (ast) => {
|
|
3031
|
+
return walkPost2(clone2(ast), (node) => {
|
|
3032
|
+
if (!Array.isArray(node) || node.length !== 3) return;
|
|
3033
|
+
const fn = IDENTITIES[node[0]];
|
|
3034
|
+
if (!fn) return;
|
|
3035
|
+
const result = fn(node[1], node[2]);
|
|
3036
|
+
if (result === null) return;
|
|
3037
|
+
return result;
|
|
3038
|
+
});
|
|
3039
|
+
};
|
|
3040
|
+
var strength = (ast) => {
|
|
3041
|
+
return walkPost2(clone2(ast), (node) => {
|
|
3042
|
+
if (!Array.isArray(node) || node.length !== 3) return;
|
|
3043
|
+
const [op, a, b] = node;
|
|
3044
|
+
if (op === "i32.mul") {
|
|
3045
|
+
const cb = getConst(b);
|
|
3046
|
+
if (cb && cb.value > 0 && (cb.value & cb.value - 1) === 0) {
|
|
3047
|
+
const shift = Math.log2(cb.value);
|
|
3048
|
+
if (Number.isInteger(shift)) return ["i32.shl", a, ["i32.const", shift]];
|
|
3049
|
+
}
|
|
3050
|
+
const ca = getConst(a);
|
|
3051
|
+
if (ca && ca.value > 0 && (ca.value & ca.value - 1) === 0) {
|
|
3052
|
+
const shift = Math.log2(ca.value);
|
|
3053
|
+
if (Number.isInteger(shift)) return ["i32.shl", b, ["i32.const", shift]];
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
if (op === "i64.mul") {
|
|
3057
|
+
const cb = getConst(b);
|
|
3058
|
+
if (cb && cb.value > 0n && (cb.value & cb.value - 1n) === 0n) {
|
|
3059
|
+
const shift = BigInt(cb.value.toString(2).length - 1);
|
|
3060
|
+
return ["i64.shl", a, ["i64.const", shift]];
|
|
3061
|
+
}
|
|
3062
|
+
const ca = getConst(a);
|
|
3063
|
+
if (ca && ca.value > 0n && (ca.value & ca.value - 1n) === 0n) {
|
|
3064
|
+
const shift = BigInt(ca.value.toString(2).length - 1);
|
|
3065
|
+
return ["i64.shl", b, ["i64.const", shift]];
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
if (op === "i32.div_u") {
|
|
3069
|
+
const cb = getConst(b);
|
|
3070
|
+
if (cb && cb.value > 0 && (cb.value & cb.value - 1) === 0) {
|
|
3071
|
+
const shift = Math.log2(cb.value);
|
|
3072
|
+
if (Number.isInteger(shift)) return ["i32.shr_u", a, ["i32.const", shift]];
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
if (op === "i64.div_u") {
|
|
3076
|
+
const cb = getConst(b);
|
|
3077
|
+
if (cb && cb.value > 0n && (cb.value & cb.value - 1n) === 0n) {
|
|
3078
|
+
const shift = BigInt(cb.value.toString(2).length - 1);
|
|
3079
|
+
return ["i64.shr_u", a, ["i64.const", shift]];
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
if (op === "i32.rem_u") {
|
|
3083
|
+
const cb = getConst(b);
|
|
3084
|
+
if (cb && cb.value > 0 && (cb.value & cb.value - 1) === 0) {
|
|
3085
|
+
return ["i32.and", a, ["i32.const", cb.value - 1]];
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
if (op === "i64.rem_u") {
|
|
3089
|
+
const cb = getConst(b);
|
|
3090
|
+
if (cb && cb.value > 0n && (cb.value & cb.value - 1n) === 0n) {
|
|
3091
|
+
return ["i64.and", a, ["i64.const", cb.value - 1n]];
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
});
|
|
3095
|
+
};
|
|
3096
|
+
var branch = (ast) => {
|
|
3097
|
+
return walkPost2(clone2(ast), (node) => {
|
|
3098
|
+
if (!Array.isArray(node)) return;
|
|
3099
|
+
const op = node[0];
|
|
3100
|
+
if (op === "if") {
|
|
3101
|
+
let condIdx = 1;
|
|
3102
|
+
while (condIdx < node.length) {
|
|
3103
|
+
const child = node[condIdx];
|
|
3104
|
+
if (Array.isArray(child) && (child[0] === "then" || child[0] === "else" || child[0] === "result" || child[0] === "param")) {
|
|
3105
|
+
condIdx++;
|
|
3106
|
+
continue;
|
|
3107
|
+
}
|
|
3108
|
+
break;
|
|
3109
|
+
}
|
|
3110
|
+
const cond = node[condIdx];
|
|
3111
|
+
const c = getConst(cond);
|
|
3112
|
+
if (!c) return;
|
|
3113
|
+
let thenBranch = null, elseBranch = null;
|
|
3114
|
+
for (let i = condIdx + 1; i < node.length; i++) {
|
|
3115
|
+
const child = node[i];
|
|
3116
|
+
if (Array.isArray(child)) {
|
|
3117
|
+
if (child[0] === "then") thenBranch = child;
|
|
3118
|
+
else if (child[0] === "else") elseBranch = child;
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
if (c.value !== 0 && c.value !== 0n) {
|
|
3122
|
+
if (thenBranch && thenBranch.length > 1) {
|
|
3123
|
+
const contents = thenBranch.slice(1);
|
|
3124
|
+
if (contents.length === 1) return contents[0];
|
|
3125
|
+
return ["block", ...contents];
|
|
3126
|
+
}
|
|
3127
|
+
return ["nop"];
|
|
3128
|
+
} else {
|
|
3129
|
+
if (elseBranch && elseBranch.length > 1) {
|
|
3130
|
+
const contents = elseBranch.slice(1);
|
|
3131
|
+
if (contents.length === 1) return contents[0];
|
|
3132
|
+
return ["block", ...contents];
|
|
3133
|
+
}
|
|
3134
|
+
return ["nop"];
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
if (op === "br_if" && node.length >= 3) {
|
|
3138
|
+
const cond = node[node.length - 1];
|
|
3139
|
+
const c = getConst(cond);
|
|
3140
|
+
if (!c) return;
|
|
3141
|
+
if (c.value === 0 || c.value === 0n) return ["nop"];
|
|
3142
|
+
return ["br", node[1]];
|
|
3143
|
+
}
|
|
3144
|
+
if (op === "select" && node.length >= 4) {
|
|
3145
|
+
const cond = node[node.length - 1];
|
|
3146
|
+
const c = getConst(cond);
|
|
3147
|
+
if (!c) return;
|
|
3148
|
+
if (c.value === 0 || c.value === 0n) return node[2];
|
|
3149
|
+
return node[1];
|
|
3150
|
+
}
|
|
3151
|
+
});
|
|
3152
|
+
};
|
|
3153
|
+
var TERMINATORS = /* @__PURE__ */ new Set(["unreachable", "return", "br", "br_table"]);
|
|
3154
|
+
var deadcode = (ast) => {
|
|
3155
|
+
const result = clone2(ast);
|
|
3156
|
+
walk2(result, (node) => {
|
|
3157
|
+
if (!Array.isArray(node)) return;
|
|
3158
|
+
const kind = node[0];
|
|
3159
|
+
if (kind === "func" || kind === "block" || kind === "loop") {
|
|
3160
|
+
eliminateDeadInBlock(node);
|
|
3161
|
+
}
|
|
3162
|
+
if (kind === "if") {
|
|
3163
|
+
for (let i = 1; i < node.length; i++) {
|
|
3164
|
+
if (Array.isArray(node[i]) && (node[i][0] === "then" || node[i][0] === "else")) {
|
|
3165
|
+
eliminateDeadInBlock(node[i]);
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
});
|
|
3170
|
+
return result;
|
|
3171
|
+
};
|
|
3172
|
+
var eliminateDeadInBlock = (block) => {
|
|
3173
|
+
let terminated = false;
|
|
3174
|
+
let firstTerminator = -1;
|
|
3175
|
+
for (let i = 1; i < block.length; i++) {
|
|
3176
|
+
const node = block[i];
|
|
3177
|
+
if (Array.isArray(node)) {
|
|
3178
|
+
const op = node[0];
|
|
3179
|
+
if (op === "param" || op === "result" || op === "local" || op === "type" || op === "export") continue;
|
|
3180
|
+
if (terminated) {
|
|
3181
|
+
if (firstTerminator === -1) firstTerminator = i;
|
|
3182
|
+
}
|
|
3183
|
+
if (TERMINATORS.has(op)) {
|
|
3184
|
+
terminated = true;
|
|
3185
|
+
firstTerminator = i + 1;
|
|
3186
|
+
}
|
|
3187
|
+
} else if (typeof node === "string") {
|
|
3188
|
+
if (terminated) {
|
|
3189
|
+
if (firstTerminator === -1) firstTerminator = i;
|
|
3190
|
+
}
|
|
3191
|
+
if (TERMINATORS.has(node)) {
|
|
3192
|
+
terminated = true;
|
|
3193
|
+
firstTerminator = i + 1;
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
if (firstTerminator > 0 && firstTerminator < block.length) {
|
|
3198
|
+
block.splice(firstTerminator);
|
|
3199
|
+
}
|
|
3200
|
+
};
|
|
3201
|
+
var localReuse = (ast) => {
|
|
3202
|
+
const result = clone2(ast);
|
|
3203
|
+
walk2(result, (node) => {
|
|
3204
|
+
if (!Array.isArray(node) || node[0] !== "func") return;
|
|
3205
|
+
const localDecls = [];
|
|
3206
|
+
const localTypes = /* @__PURE__ */ new Map();
|
|
3207
|
+
const usedLocals = /* @__PURE__ */ new Set();
|
|
3208
|
+
for (let i = 1; i < node.length; i++) {
|
|
3209
|
+
const sub = node[i];
|
|
3210
|
+
if (!Array.isArray(sub)) continue;
|
|
3211
|
+
if (sub[0] === "local") {
|
|
3212
|
+
localDecls.push({ idx: i, node: sub });
|
|
3213
|
+
if (typeof sub[1] === "string" && sub[1][0] === "$") {
|
|
3214
|
+
localTypes.set(sub[1], sub[2]);
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
if (sub[0] === "param") {
|
|
3218
|
+
if (typeof sub[1] === "string" && sub[1][0] === "$") {
|
|
3219
|
+
localTypes.set(sub[1], sub[2]);
|
|
3220
|
+
usedLocals.add(sub[1]);
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
walk2(node, (n) => {
|
|
3225
|
+
if (!Array.isArray(n)) return;
|
|
3226
|
+
const op = n[0];
|
|
3227
|
+
if (op === "local.get" || op === "local.set" || op === "local.tee") {
|
|
3228
|
+
const ref = n[1];
|
|
3229
|
+
if (typeof ref === "string") usedLocals.add(ref);
|
|
3230
|
+
}
|
|
3231
|
+
});
|
|
3232
|
+
for (let i = localDecls.length - 1; i >= 0; i--) {
|
|
3233
|
+
const { idx, node: decl } = localDecls[i];
|
|
3234
|
+
const name2 = typeof decl[1] === "string" && decl[1][0] === "$" ? decl[1] : null;
|
|
3235
|
+
if (name2 && !usedLocals.has(name2)) {
|
|
3236
|
+
node.splice(idx, 1);
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
});
|
|
3240
|
+
return result;
|
|
3241
|
+
};
|
|
3242
|
+
var propagate = (ast) => {
|
|
3243
|
+
const result = clone2(ast);
|
|
3244
|
+
walk2(result, (node) => {
|
|
3245
|
+
if (!Array.isArray(node) || node[0] !== "func") return;
|
|
3246
|
+
const constLocals = /* @__PURE__ */ new Map();
|
|
3247
|
+
const processBlock = (block, startIdx = 1) => {
|
|
3248
|
+
for (let i = startIdx; i < block.length; i++) {
|
|
3249
|
+
const instr2 = block[i];
|
|
3250
|
+
if (!Array.isArray(instr2)) continue;
|
|
3251
|
+
const op = instr2[0];
|
|
3252
|
+
if (op === "local.set" && instr2.length === 3) {
|
|
3253
|
+
const local = instr2[1];
|
|
3254
|
+
const val = instr2[2];
|
|
3255
|
+
const c = getConst(val);
|
|
3256
|
+
if (c && typeof local === "string") {
|
|
3257
|
+
constLocals.set(local, val);
|
|
3258
|
+
} else if (typeof local === "string") {
|
|
3259
|
+
constLocals.delete(local);
|
|
3260
|
+
}
|
|
3261
|
+
} else if (op === "local.tee" && instr2.length === 3) {
|
|
3262
|
+
const local = instr2[1];
|
|
3263
|
+
const val = instr2[2];
|
|
3264
|
+
const c = getConst(val);
|
|
3265
|
+
if (c && typeof local === "string") {
|
|
3266
|
+
constLocals.set(local, val);
|
|
3267
|
+
} else if (typeof local === "string") {
|
|
3268
|
+
constLocals.delete(local);
|
|
3269
|
+
}
|
|
3270
|
+
} else if (op === "local.get" && instr2.length === 2) {
|
|
3271
|
+
const local = instr2[1];
|
|
3272
|
+
if (typeof local === "string" && constLocals.has(local)) {
|
|
3273
|
+
const constVal = constLocals.get(local);
|
|
3274
|
+
instr2.length = 0;
|
|
3275
|
+
instr2.push(...clone2(constVal));
|
|
3276
|
+
}
|
|
3277
|
+
} else if (op === "block" || op === "loop" || op === "if" || op === "call" || op === "call_indirect") {
|
|
3278
|
+
constLocals.clear();
|
|
3279
|
+
}
|
|
3280
|
+
walkPost2(instr2, (n) => {
|
|
3281
|
+
if (!Array.isArray(n) || n[0] !== "local.get" || n.length !== 2) return;
|
|
3282
|
+
const local = n[1];
|
|
3283
|
+
if (typeof local === "string" && constLocals.has(local)) {
|
|
3284
|
+
const constVal = constLocals.get(local);
|
|
3285
|
+
return clone2(constVal);
|
|
3286
|
+
}
|
|
3287
|
+
});
|
|
3288
|
+
}
|
|
3289
|
+
};
|
|
3290
|
+
processBlock(node);
|
|
3291
|
+
});
|
|
3292
|
+
return result;
|
|
3293
|
+
};
|
|
3294
|
+
var inline = (ast) => {
|
|
3295
|
+
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
3296
|
+
const result = clone2(ast);
|
|
3297
|
+
const inlinable = /* @__PURE__ */ new Map();
|
|
3298
|
+
for (const node of result.slice(1)) {
|
|
3299
|
+
if (!Array.isArray(node) || node[0] !== "func") continue;
|
|
3300
|
+
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
3301
|
+
if (!name2) continue;
|
|
3302
|
+
let params = [];
|
|
3303
|
+
let body = [];
|
|
3304
|
+
let hasLocals = false;
|
|
3305
|
+
let hasExport = false;
|
|
3306
|
+
for (let i = 1; i < node.length; i++) {
|
|
3307
|
+
const sub = node[i];
|
|
3308
|
+
if (!Array.isArray(sub)) continue;
|
|
3309
|
+
if (sub[0] === "param") {
|
|
3310
|
+
if (typeof sub[1] === "string" && sub[1][0] === "$") {
|
|
3311
|
+
params.push({ name: sub[1], type: sub[2] });
|
|
3312
|
+
} else {
|
|
3313
|
+
params = null;
|
|
3314
|
+
break;
|
|
3315
|
+
}
|
|
3316
|
+
} else if (sub[0] === "local") {
|
|
3317
|
+
hasLocals = true;
|
|
3318
|
+
} else if (sub[0] === "export") {
|
|
3319
|
+
hasExport = true;
|
|
3320
|
+
} else if (sub[0] !== "result" && sub[0] !== "type") {
|
|
3321
|
+
body.push(sub);
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
if (params && !hasLocals && !hasExport && params.length <= 2 && body.length === 1) {
|
|
3325
|
+
const paramNames = new Set(params.map((p) => p.name));
|
|
3326
|
+
let mutatesParam = false;
|
|
3327
|
+
walk2(body[0], (n) => {
|
|
3328
|
+
if (!Array.isArray(n)) return;
|
|
3329
|
+
if ((n[0] === "local.set" || n[0] === "local.tee") && paramNames.has(n[1])) {
|
|
3330
|
+
mutatesParam = true;
|
|
3331
|
+
}
|
|
3332
|
+
});
|
|
3333
|
+
if (!mutatesParam) {
|
|
3334
|
+
inlinable.set(name2, { body: body[0], params });
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
if (inlinable.size === 0) return result;
|
|
3339
|
+
walkPost2(result, (node) => {
|
|
3340
|
+
if (!Array.isArray(node) || node[0] !== "call") return;
|
|
3341
|
+
const fname = node[1];
|
|
3342
|
+
if (!inlinable.has(fname)) return;
|
|
3343
|
+
const { body, params } = inlinable.get(fname);
|
|
3344
|
+
const args = node.slice(2);
|
|
3345
|
+
if (params.length === 0) {
|
|
3346
|
+
return clone2(body);
|
|
3347
|
+
}
|
|
3348
|
+
const substituted = clone2(body);
|
|
3349
|
+
walkPost2(substituted, (n) => {
|
|
3350
|
+
if (!Array.isArray(n) || n[0] !== "local.get") return;
|
|
3351
|
+
const local = n[1];
|
|
3352
|
+
const paramIdx = params.findIndex((p) => p.name === local);
|
|
3353
|
+
if (paramIdx !== -1 && args[paramIdx]) {
|
|
3354
|
+
return clone2(args[paramIdx]);
|
|
3355
|
+
}
|
|
3356
|
+
});
|
|
3357
|
+
return substituted;
|
|
3358
|
+
});
|
|
3359
|
+
return result;
|
|
3360
|
+
};
|
|
3361
|
+
function optimize(ast, opts = true) {
|
|
3362
|
+
if (typeof ast === "string") ast = parse_default(ast);
|
|
3363
|
+
ast = clone2(ast);
|
|
3364
|
+
opts = normalize3(opts);
|
|
3365
|
+
if (opts.fold) ast = fold(ast);
|
|
3366
|
+
if (opts.identity) ast = identity(ast);
|
|
3367
|
+
if (opts.strength) ast = strength(ast);
|
|
3368
|
+
if (opts.branch) ast = branch(ast);
|
|
3369
|
+
if (opts.propagate) ast = propagate(ast);
|
|
3370
|
+
if (opts.inline) ast = inline(ast);
|
|
3371
|
+
if (opts.deadcode) ast = deadcode(ast);
|
|
3372
|
+
if (opts.locals) ast = localReuse(ast);
|
|
3373
|
+
if (opts.treeshake) ast = treeshake(ast);
|
|
3374
|
+
return ast;
|
|
3375
|
+
}
|
|
3376
|
+
|
|
2569
3377
|
// watr.js
|
|
2570
3378
|
var PUA = "\uE000";
|
|
2571
3379
|
var instrType = (op) => {
|
|
@@ -2587,11 +3395,11 @@ var exprType = (node, ctx = {}) => {
|
|
|
2587
3395
|
if (op === "call" && ctx.funcs?.[args[0]]) return ctx.funcs[args[0]].result?.[0];
|
|
2588
3396
|
return null;
|
|
2589
3397
|
};
|
|
2590
|
-
function
|
|
3398
|
+
function walk3(node, fn) {
|
|
2591
3399
|
node = fn(node);
|
|
2592
3400
|
if (Array.isArray(node)) {
|
|
2593
3401
|
for (let i = 0; i < node.length; i++) {
|
|
2594
|
-
let child =
|
|
3402
|
+
let child = walk3(node[i], fn);
|
|
2595
3403
|
if (child?._splice) node.splice(i, 1, ...child), i += child.length - 1;
|
|
2596
3404
|
else node[i] = child;
|
|
2597
3405
|
}
|
|
@@ -2601,7 +3409,7 @@ function walk2(node, fn) {
|
|
|
2601
3409
|
function inferImports(ast, funcs) {
|
|
2602
3410
|
const imports = [];
|
|
2603
3411
|
const importMap = /* @__PURE__ */ new Map();
|
|
2604
|
-
|
|
3412
|
+
walk3(ast, (node) => {
|
|
2605
3413
|
if (!Array.isArray(node)) return node;
|
|
2606
3414
|
if (node[0] === "call" && typeof node[1] === "function") {
|
|
2607
3415
|
const fn = node[1];
|
|
@@ -2641,7 +3449,7 @@ function compile2(source, ...values) {
|
|
|
2641
3449
|
let ast = parse_default(src);
|
|
2642
3450
|
const funcsToImport = [];
|
|
2643
3451
|
let idx = 0;
|
|
2644
|
-
ast =
|
|
3452
|
+
ast = walk3(ast, (node) => {
|
|
2645
3453
|
if (node === PUA) {
|
|
2646
3454
|
const value = values[idx++];
|
|
2647
3455
|
if (typeof value === "function") {
|
|
@@ -2679,12 +3487,15 @@ function compile2(source, ...values) {
|
|
|
2679
3487
|
}
|
|
2680
3488
|
}
|
|
2681
3489
|
if (opts.polyfill) ast = polyfill(ast, opts.polyfill);
|
|
3490
|
+
if (opts.optimize) ast = optimize(ast, opts.optimize);
|
|
2682
3491
|
const binary = compile(ast);
|
|
2683
3492
|
if (importObjs) binary._imports = importObjs;
|
|
2684
3493
|
return binary;
|
|
2685
3494
|
}
|
|
2686
|
-
if (opts.polyfill) {
|
|
2687
|
-
|
|
3495
|
+
if (opts.polyfill || opts.optimize) {
|
|
3496
|
+
let ast = typeof source === "string" ? parse_default(source) : source;
|
|
3497
|
+
if (opts.polyfill) ast = polyfill(ast, opts.polyfill);
|
|
3498
|
+
if (opts.optimize) ast = optimize(ast, opts.optimize);
|
|
2688
3499
|
return compile(ast);
|
|
2689
3500
|
}
|
|
2690
3501
|
return compile(source);
|
|
@@ -2699,6 +3510,7 @@ var watr_default = watr;
|
|
|
2699
3510
|
export {
|
|
2700
3511
|
compile2 as compile,
|
|
2701
3512
|
watr_default as default,
|
|
3513
|
+
optimize,
|
|
2702
3514
|
parse_default as parse,
|
|
2703
3515
|
polyfill,
|
|
2704
3516
|
print,
|