watr 4.5.1 → 4.6.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 +543 -110
- package/dist/watr.min.js +6 -6
- package/dist/watr.wasm +0 -0
- package/package.json +8 -3
- package/readme.md +1 -1
- package/src/compile.js +9 -2
- package/src/const.js +2 -2
- package/src/encode.js +33 -12
- package/src/optimize.js +634 -122
- package/types/src/const.d.ts +2 -0
- package/types/src/encode.d.ts.map +1 -1
- package/types/src/optimize.d.ts +81 -6
- package/types/src/optimize.d.ts.map +1 -1
package/dist/watr.js
CHANGED
|
@@ -140,16 +140,29 @@ var _f64 = new Float64Array(_buf);
|
|
|
140
140
|
var _i64 = new BigInt64Array(_buf);
|
|
141
141
|
i64.parse = (n) => {
|
|
142
142
|
n = cleanInt(n);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
const neg = n[0] === "-";
|
|
144
|
+
const body = neg ? n.slice(1) : n;
|
|
145
|
+
let max;
|
|
146
|
+
if (body[0] === "0" && (body[1] === "x" || body[1] === "X")) {
|
|
147
|
+
const hex = body.slice(2).replace(/^0+/, "") || "0";
|
|
148
|
+
max = neg ? "8000000000000000" : "ffffffffffffffff";
|
|
149
|
+
if (hex.length > 16 || hex.length === 16 && hex.toLowerCase() > max) err(`i64 constant out of range`);
|
|
150
|
+
} else {
|
|
151
|
+
const dec = body.replace(/^0+/, "") || "0";
|
|
152
|
+
max = neg ? "9223372036854775808" : "18446744073709551615";
|
|
153
|
+
if (dec.length > max.length || dec.length === max.length && dec > max) err(`i64 constant out of range`);
|
|
154
|
+
}
|
|
155
|
+
let bi = BigInt(body);
|
|
156
|
+
if (neg) bi = 0n - bi;
|
|
157
|
+
_i64[0] = bi;
|
|
146
158
|
return _i64[0];
|
|
147
159
|
};
|
|
148
160
|
var F32_SIGN = 2147483648;
|
|
149
161
|
var F32_NAN = 2139095040;
|
|
162
|
+
var F32_QUIET = 4194304;
|
|
150
163
|
function f32(input, value, idx) {
|
|
151
|
-
if (typeof input === "string" &&
|
|
152
|
-
value = i32.parse(input.slice(idx + 4));
|
|
164
|
+
if (typeof input === "string" && (idx = input.indexOf("nan")) >= 0) {
|
|
165
|
+
value = input[idx + 3] === ":" ? i32.parse(input.slice(idx + 4)) : F32_QUIET;
|
|
153
166
|
value |= F32_NAN;
|
|
154
167
|
if (input[0] === "-") value |= F32_SIGN;
|
|
155
168
|
_i32[0] = value;
|
|
@@ -161,9 +174,10 @@ function f32(input, value, idx) {
|
|
|
161
174
|
}
|
|
162
175
|
var F64_SIGN = 0x8000000000000000n;
|
|
163
176
|
var F64_NAN = 0x7ff0000000000000n;
|
|
177
|
+
var F64_QUIET = 0x8000000000000n;
|
|
164
178
|
function f64(input, value, idx) {
|
|
165
|
-
if (typeof input === "string" &&
|
|
166
|
-
value = i64.parse(input.slice(idx + 4));
|
|
179
|
+
if (typeof input === "string" && (idx = input.indexOf("nan")) >= 0) {
|
|
180
|
+
value = input[idx + 3] === ":" ? i64.parse(input.slice(idx + 4)) : F64_QUIET;
|
|
167
181
|
value |= F64_NAN;
|
|
168
182
|
if (input[0] === "-") value |= F64_SIGN;
|
|
169
183
|
_i64[0] = value;
|
|
@@ -182,8 +196,11 @@ f64.parse = (input, max = Number.MAX_VALUE) => {
|
|
|
182
196
|
let [sig, exp = "0"] = input.split(/p/i);
|
|
183
197
|
let [int, fract = ""] = sig.split(".");
|
|
184
198
|
let flen = fract.length ?? 0;
|
|
185
|
-
let intVal =
|
|
186
|
-
|
|
199
|
+
let intVal = 0;
|
|
200
|
+
for (let i = int.length - 1; i >= 2; i--) {
|
|
201
|
+
let digit = parseInt(int[i], 16);
|
|
202
|
+
intVal += digit * 16 ** (int.length - 1 - i);
|
|
203
|
+
}
|
|
187
204
|
let fractVal = fract ? parseInt("0x" + fract) / 16 ** flen : 0;
|
|
188
205
|
exp = parseInt(exp, 10);
|
|
189
206
|
let value = sign * (intVal + fractVal) * 2 ** exp;
|
|
@@ -497,9 +514,9 @@ var INSTR = [
|
|
|
497
514
|
"array.init_data typeidx_dataidx",
|
|
498
515
|
"array.init_elem typeidx_elemidx",
|
|
499
516
|
"ref.test reftype",
|
|
500
|
-
"",
|
|
517
|
+
"ref.test_null reftype",
|
|
501
518
|
"ref.cast reftype",
|
|
502
|
-
"",
|
|
519
|
+
"ref.cast_null reftype",
|
|
503
520
|
"br_on_cast reftype2",
|
|
504
521
|
"br_on_cast_fail reftype2",
|
|
505
522
|
"any.convert_extern",
|
|
@@ -1244,6 +1261,7 @@ var TYPE = {
|
|
|
1244
1261
|
i31: 108,
|
|
1245
1262
|
struct: 107,
|
|
1246
1263
|
array: 106,
|
|
1264
|
+
data: 107,
|
|
1247
1265
|
cont: 104,
|
|
1248
1266
|
nocont: 117,
|
|
1249
1267
|
// stack switching (Phase 3)
|
|
@@ -1522,7 +1540,7 @@ function normalize(nodes, ctx) {
|
|
|
1522
1540
|
ctx.datacount && (ctx.datacount[0] = true);
|
|
1523
1541
|
} else if ((node.startsWith("memory.") || node.endsWith("load") || node.endsWith("store")) && isIdx(nodes[0])) out.push(nodes.shift());
|
|
1524
1542
|
} else if (Array.isArray(node)) {
|
|
1525
|
-
|
|
1543
|
+
let op = node[0];
|
|
1526
1544
|
node.loc != null && (err.loc = node.loc);
|
|
1527
1545
|
if (op?.startsWith?.("@metadata.code.")) {
|
|
1528
1546
|
let type = op.slice(15);
|
|
@@ -1556,6 +1574,12 @@ function normalize(nodes, ctx) {
|
|
|
1556
1574
|
out.push(parts.shift());
|
|
1557
1575
|
}
|
|
1558
1576
|
out.push(...normalize(parts, ctx), "end");
|
|
1577
|
+
} else if (op === "ref.test" || op === "ref.cast") {
|
|
1578
|
+
const type = parts[0];
|
|
1579
|
+
const isNullable = !Array.isArray(type) || type[1] === "null" || type[0] !== "ref";
|
|
1580
|
+
if (isNullable) op += "_null";
|
|
1581
|
+
out.push(...normalize(parts.slice(1), ctx), op, type);
|
|
1582
|
+
nodes.unshift(...out.splice(out.length - 2));
|
|
1559
1583
|
} else {
|
|
1560
1584
|
const imm = [];
|
|
1561
1585
|
while (parts.length && (!Array.isArray(parts[0]) || parts[0].valueOf !== Array.prototype.valueOf || "type,param,result,ref,exact,on".includes(parts[0][0]))) imm.push(parts.shift());
|
|
@@ -2006,7 +2030,7 @@ var instr = (nodes, ctx) => {
|
|
|
2006
2030
|
let [...bytes] = INSTR[op] || err(`Unknown instruction ${op}`);
|
|
2007
2031
|
if (HANDLER[op]) {
|
|
2008
2032
|
if (op === "select" && nodes[0]?.length) bytes[0]++;
|
|
2009
|
-
else if (HANDLER[op] === IMM.reftype && (nodes[0][1] === "null" || nodes[0][0] !== "ref")) {
|
|
2033
|
+
else if (HANDLER[op] === IMM.reftype && !op.endsWith("_null") && (nodes[0][1] === "null" || nodes[0][0] !== "ref")) {
|
|
2010
2034
|
bytes[bytes.length - 1]++;
|
|
2011
2035
|
}
|
|
2012
2036
|
bytes.push(...HANDLER[op](nodes, ctx, op));
|
|
@@ -3041,11 +3065,17 @@ var OPTS = {
|
|
|
3041
3065
|
branch: true,
|
|
3042
3066
|
// simplify constant branches
|
|
3043
3067
|
propagate: true,
|
|
3044
|
-
//
|
|
3045
|
-
inline:
|
|
3046
|
-
// inline tiny functions
|
|
3068
|
+
// forward-propagate single-use locals & tiny consts (never inflates)
|
|
3069
|
+
inline: false,
|
|
3070
|
+
// inline tiny functions — can duplicate bodies
|
|
3071
|
+
inlineOnce: true,
|
|
3072
|
+
// inline single-call functions into their lone caller (never duplicates)
|
|
3047
3073
|
vacuum: true,
|
|
3048
3074
|
// remove nops, drop-of-pure, empty branches
|
|
3075
|
+
mergeBlocks: true,
|
|
3076
|
+
// unwrap `(block $L …)` whose label is never targeted
|
|
3077
|
+
coalesce: true,
|
|
3078
|
+
// share local slots between same-type non-overlapping locals
|
|
3049
3079
|
peephole: true,
|
|
3050
3080
|
// x-x→0, x&0→0, etc.
|
|
3051
3081
|
globals: true,
|
|
@@ -3058,14 +3088,12 @@ var OPTS = {
|
|
|
3058
3088
|
// strip mut from never-written globals
|
|
3059
3089
|
brif: true,
|
|
3060
3090
|
// if-then-br → br_if
|
|
3061
|
-
foldarms:
|
|
3062
|
-
// merge identical trailing if arms
|
|
3063
|
-
// minify: true, // NOTE: disabled — renaming $ids has no binary-size effect
|
|
3064
|
-
// without a names section, and risks local-name collisions.
|
|
3091
|
+
foldarms: false,
|
|
3092
|
+
// merge identical trailing if arms — can add block wrapper
|
|
3065
3093
|
dedupe: true,
|
|
3066
3094
|
// eliminate duplicate functions
|
|
3067
|
-
reorder:
|
|
3068
|
-
// put hot functions first
|
|
3095
|
+
reorder: false,
|
|
3096
|
+
// put hot functions first — no AST reduction
|
|
3069
3097
|
dedupTypes: true,
|
|
3070
3098
|
// merge identical type definitions
|
|
3071
3099
|
packData: true,
|
|
@@ -3074,6 +3102,13 @@ var OPTS = {
|
|
|
3074
3102
|
// shorten import names — enable only when you control the host
|
|
3075
3103
|
};
|
|
3076
3104
|
var ALL2 = Object.keys(OPTS);
|
|
3105
|
+
var binarySize = (ast) => {
|
|
3106
|
+
try {
|
|
3107
|
+
return compile(ast).length;
|
|
3108
|
+
} catch {
|
|
3109
|
+
return Infinity;
|
|
3110
|
+
}
|
|
3111
|
+
};
|
|
3077
3112
|
var equal = (a, b) => {
|
|
3078
3113
|
if (a === b) return true;
|
|
3079
3114
|
if (typeof a !== typeof b) return false;
|
|
@@ -3088,10 +3123,8 @@ var normalize3 = (opts) => {
|
|
|
3088
3123
|
if (opts === false) return {};
|
|
3089
3124
|
if (typeof opts === "string") {
|
|
3090
3125
|
const set = new Set(opts.split(/\s+/).filter(Boolean));
|
|
3091
|
-
if (set.
|
|
3092
|
-
|
|
3093
|
-
}
|
|
3094
|
-
return Object.fromEntries(ALL2.map((f) => [f, set.has(f) || set.has("all")]));
|
|
3126
|
+
if (set.has("all")) return Object.fromEntries(ALL2.map((f) => [f, true]));
|
|
3127
|
+
return Object.fromEntries(ALL2.map((f) => [f, set.has(f)]));
|
|
3095
3128
|
}
|
|
3096
3129
|
return { ...OPTS, ...opts };
|
|
3097
3130
|
};
|
|
@@ -3401,7 +3434,7 @@ var makeConst = (type, value) => {
|
|
|
3401
3434
|
return null;
|
|
3402
3435
|
};
|
|
3403
3436
|
var fold = (ast) => {
|
|
3404
|
-
return walkPost2(
|
|
3437
|
+
return walkPost2(ast, (node) => {
|
|
3405
3438
|
if (!Array.isArray(node)) return;
|
|
3406
3439
|
const entry = FOLDABLE[node[0]];
|
|
3407
3440
|
if (!entry) return;
|
|
@@ -3464,7 +3497,7 @@ var IDENTITIES = {
|
|
|
3464
3497
|
// f * 1 → x (careful with NaN, skip for floats)
|
|
3465
3498
|
};
|
|
3466
3499
|
var identity = (ast) => {
|
|
3467
|
-
return walkPost2(
|
|
3500
|
+
return walkPost2(ast, (node) => {
|
|
3468
3501
|
if (!Array.isArray(node) || node.length !== 3) return;
|
|
3469
3502
|
const fn = IDENTITIES[node[0]];
|
|
3470
3503
|
if (!fn) return;
|
|
@@ -3474,7 +3507,7 @@ var identity = (ast) => {
|
|
|
3474
3507
|
});
|
|
3475
3508
|
};
|
|
3476
3509
|
var strength = (ast) => {
|
|
3477
|
-
return walkPost2(
|
|
3510
|
+
return walkPost2(ast, (node) => {
|
|
3478
3511
|
if (!Array.isArray(node) || node.length !== 3) return;
|
|
3479
3512
|
const [op, a, b] = node;
|
|
3480
3513
|
if (op === "i32.mul") {
|
|
@@ -3530,7 +3563,7 @@ var strength = (ast) => {
|
|
|
3530
3563
|
});
|
|
3531
3564
|
};
|
|
3532
3565
|
var branch = (ast) => {
|
|
3533
|
-
return walkPost2(
|
|
3566
|
+
return walkPost2(ast, (node) => {
|
|
3534
3567
|
if (!Array.isArray(node)) return;
|
|
3535
3568
|
const op = node[0];
|
|
3536
3569
|
if (op === "if") {
|
|
@@ -3562,8 +3595,7 @@ var branch = (ast) => {
|
|
|
3562
3595
|
};
|
|
3563
3596
|
var TERMINATORS = /* @__PURE__ */ new Set(["unreachable", "return", "br", "br_table"]);
|
|
3564
3597
|
var deadcode = (ast) => {
|
|
3565
|
-
|
|
3566
|
-
walk2(result, (node) => {
|
|
3598
|
+
walk2(ast, (node) => {
|
|
3567
3599
|
if (!Array.isArray(node)) return;
|
|
3568
3600
|
const kind = node[0];
|
|
3569
3601
|
if (kind === "func" || kind === "block" || kind === "loop") {
|
|
@@ -3577,7 +3609,7 @@ var deadcode = (ast) => {
|
|
|
3577
3609
|
}
|
|
3578
3610
|
}
|
|
3579
3611
|
});
|
|
3580
|
-
return
|
|
3612
|
+
return ast;
|
|
3581
3613
|
};
|
|
3582
3614
|
var eliminateDeadInBlock = (block) => {
|
|
3583
3615
|
let terminated = false;
|
|
@@ -3609,8 +3641,7 @@ var eliminateDeadInBlock = (block) => {
|
|
|
3609
3641
|
}
|
|
3610
3642
|
};
|
|
3611
3643
|
var localReuse = (ast) => {
|
|
3612
|
-
|
|
3613
|
-
walk2(result, (node) => {
|
|
3644
|
+
walk2(ast, (node) => {
|
|
3614
3645
|
if (!Array.isArray(node) || node[0] !== "func") return;
|
|
3615
3646
|
const localDecls = [];
|
|
3616
3647
|
const localTypes = /* @__PURE__ */ new Map();
|
|
@@ -3647,7 +3678,7 @@ var localReuse = (ast) => {
|
|
|
3647
3678
|
}
|
|
3648
3679
|
}
|
|
3649
3680
|
});
|
|
3650
|
-
return
|
|
3681
|
+
return ast;
|
|
3651
3682
|
};
|
|
3652
3683
|
var IMPURE_OPS = /* @__PURE__ */ new Set([
|
|
3653
3684
|
"call",
|
|
@@ -3712,7 +3743,29 @@ var countLocalUses = (node) => {
|
|
|
3712
3743
|
});
|
|
3713
3744
|
return counts;
|
|
3714
3745
|
};
|
|
3715
|
-
var
|
|
3746
|
+
var isTinyConst = (node) => {
|
|
3747
|
+
const c = getConst(node);
|
|
3748
|
+
if (!c) return false;
|
|
3749
|
+
if (c.type === "i32") {
|
|
3750
|
+
const v = c.value | 0;
|
|
3751
|
+
return v >= -64 && v <= 63;
|
|
3752
|
+
}
|
|
3753
|
+
if (c.type === "i64") {
|
|
3754
|
+
const v = typeof c.value === "bigint" ? c.value : BigInt(c.value);
|
|
3755
|
+
return v >= -64n && v <= 63n;
|
|
3756
|
+
}
|
|
3757
|
+
return false;
|
|
3758
|
+
};
|
|
3759
|
+
var canSubst = (k) => k.pure && k.singleUse || isTinyConst(k.val);
|
|
3760
|
+
var purgeRefs = (known, name2) => {
|
|
3761
|
+
for (const [key, tracked] of known) {
|
|
3762
|
+
let refs = false;
|
|
3763
|
+
walk2(tracked.val, (n) => {
|
|
3764
|
+
if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.tee") && n[1] === name2) refs = true;
|
|
3765
|
+
});
|
|
3766
|
+
if (refs) known.delete(key);
|
|
3767
|
+
}
|
|
3768
|
+
};
|
|
3716
3769
|
var substGets = (node, known) => walkPost2(node, (n) => {
|
|
3717
3770
|
if (!Array.isArray(n) || n[0] !== "local.get" || n.length !== 2) return;
|
|
3718
3771
|
const k = typeof n[1] === "string" && known.get(n[1]);
|
|
@@ -3727,9 +3780,10 @@ var forwardPropagate = (funcNode, params, useCounts) => {
|
|
|
3727
3780
|
if (!Array.isArray(instr2)) continue;
|
|
3728
3781
|
const op = instr2[0];
|
|
3729
3782
|
if (op === "param" || op === "result" || op === "local" || op === "type" || op === "export") continue;
|
|
3730
|
-
if (op === "local.set" && instr2.length === 3 && typeof instr2[1] === "string") {
|
|
3783
|
+
if ((op === "local.set" || op === "local.tee") && instr2.length === 3 && typeof instr2[1] === "string") {
|
|
3731
3784
|
substGets(instr2[2], known);
|
|
3732
3785
|
const uses = getUseCount(instr2[1]);
|
|
3786
|
+
purgeRefs(known, instr2[1]);
|
|
3733
3787
|
known.set(instr2[1], {
|
|
3734
3788
|
val: instr2[2],
|
|
3735
3789
|
pure: isPure(instr2[2]),
|
|
@@ -3755,6 +3809,12 @@ var forwardPropagate = (funcNode, params, useCounts) => {
|
|
|
3755
3809
|
const prev = clone2(instr2);
|
|
3756
3810
|
substGets(instr2, known);
|
|
3757
3811
|
if (!equal(prev, instr2)) changed = true;
|
|
3812
|
+
walk2(instr2, (n) => {
|
|
3813
|
+
if (Array.isArray(n) && (n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string") {
|
|
3814
|
+
known.delete(n[1]);
|
|
3815
|
+
purgeRefs(known, n[1]);
|
|
3816
|
+
}
|
|
3817
|
+
});
|
|
3758
3818
|
}
|
|
3759
3819
|
}
|
|
3760
3820
|
return changed;
|
|
@@ -3812,29 +3872,35 @@ var eliminateDeadStores = (funcNode, params, useCounts) => {
|
|
|
3812
3872
|
}
|
|
3813
3873
|
return changed;
|
|
3814
3874
|
};
|
|
3875
|
+
var isScopeNode = (n) => Array.isArray(n) && (n[0] === "func" || n[0] === "block" || n[0] === "loop" || n[0] === "then" || n[0] === "else");
|
|
3815
3876
|
var propagate = (ast) => {
|
|
3816
|
-
|
|
3817
|
-
walk2(result, (funcNode) => {
|
|
3877
|
+
walk2(ast, (funcNode) => {
|
|
3818
3878
|
if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
|
|
3819
3879
|
const params = /* @__PURE__ */ new Set();
|
|
3820
3880
|
for (const sub of funcNode)
|
|
3821
3881
|
if (Array.isArray(sub) && sub[0] === "param" && typeof sub[1] === "string") params.add(sub[1]);
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
if (
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3882
|
+
const scopes = [];
|
|
3883
|
+
walkPost2(funcNode, (n) => {
|
|
3884
|
+
if (isScopeNode(n)) scopes.push(n);
|
|
3885
|
+
});
|
|
3886
|
+
for (let round = 0; round < 6; round++) {
|
|
3887
|
+
const useCounts = countLocalUses(funcNode);
|
|
3888
|
+
let progressed = false;
|
|
3889
|
+
for (const scope of scopes) {
|
|
3890
|
+
if (forwardPropagate(scope, params, useCounts)) progressed = true;
|
|
3891
|
+
if (eliminateSetGetPairs(scope, params, useCounts)) progressed = true;
|
|
3892
|
+
if (createLocalTees(scope, params, useCounts)) progressed = true;
|
|
3893
|
+
if (eliminateDeadStores(scope, params, useCounts)) progressed = true;
|
|
3894
|
+
}
|
|
3895
|
+
if (!progressed) break;
|
|
3829
3896
|
}
|
|
3830
3897
|
});
|
|
3831
|
-
return
|
|
3898
|
+
return ast;
|
|
3832
3899
|
};
|
|
3833
3900
|
var inline = (ast) => {
|
|
3834
3901
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
3835
|
-
const result = clone2(ast);
|
|
3836
3902
|
const inlinable = /* @__PURE__ */ new Map();
|
|
3837
|
-
for (const node of
|
|
3903
|
+
for (const node of ast.slice(1)) {
|
|
3838
3904
|
if (!Array.isArray(node) || node[0] !== "func") continue;
|
|
3839
3905
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
3840
3906
|
if (!name2) continue;
|
|
@@ -3878,8 +3944,8 @@ var inline = (ast) => {
|
|
|
3878
3944
|
}
|
|
3879
3945
|
}
|
|
3880
3946
|
}
|
|
3881
|
-
if (inlinable.size === 0) return
|
|
3882
|
-
walkPost2(
|
|
3947
|
+
if (inlinable.size === 0) return ast;
|
|
3948
|
+
walkPost2(ast, (node) => {
|
|
3883
3949
|
if (!Array.isArray(node) || node[0] !== "call") return;
|
|
3884
3950
|
const fname = node[1];
|
|
3885
3951
|
if (!inlinable.has(fname)) return;
|
|
@@ -3899,10 +3965,315 @@ var inline = (ast) => {
|
|
|
3899
3965
|
});
|
|
3900
3966
|
return substituted;
|
|
3901
3967
|
});
|
|
3902
|
-
return
|
|
3968
|
+
return ast;
|
|
3969
|
+
};
|
|
3970
|
+
var inlineUid = 0;
|
|
3971
|
+
var inlineOnce = (ast) => {
|
|
3972
|
+
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
3973
|
+
const HEAD = /* @__PURE__ */ new Set(["export", "type", "param", "result", "local"]);
|
|
3974
|
+
const bodyStart = (fn) => {
|
|
3975
|
+
let i = 2;
|
|
3976
|
+
while (i < fn.length && (typeof fn[i] === "string" || Array.isArray(fn[i]) && HEAD.has(fn[i][0]))) i++;
|
|
3977
|
+
return i;
|
|
3978
|
+
};
|
|
3979
|
+
const isBranch = (op) => op === "br" || op === "br_if" || op === "br_table";
|
|
3980
|
+
const unsafe = (n) => {
|
|
3981
|
+
if (!Array.isArray(n)) return false;
|
|
3982
|
+
const op = n[0];
|
|
3983
|
+
if (op === "return_call" || op === "return_call_indirect" || op === "return_call_ref") return true;
|
|
3984
|
+
if (op === "try" || op === "try_table" || op === "delegate" || op === "rethrow") return true;
|
|
3985
|
+
if (isBranch(op)) {
|
|
3986
|
+
for (let i = 1; i < n.length; i++) if (typeof n[i] === "number" || typeof n[i] === "string" && /^\d+$/.test(n[i])) return true;
|
|
3987
|
+
}
|
|
3988
|
+
for (let i = 1; i < n.length; i++) if (unsafe(n[i])) return true;
|
|
3989
|
+
return false;
|
|
3990
|
+
};
|
|
3991
|
+
const callsSelf = (n, name2) => {
|
|
3992
|
+
if (!Array.isArray(n)) return false;
|
|
3993
|
+
if ((n[0] === "call" || n[0] === "return_call") && n[1] === name2) return true;
|
|
3994
|
+
for (let i = 1; i < n.length; i++) if (callsSelf(n[i], name2)) return true;
|
|
3995
|
+
return false;
|
|
3996
|
+
};
|
|
3997
|
+
const collectPinned = (n, pinned) => {
|
|
3998
|
+
if (!Array.isArray(n)) return;
|
|
3999
|
+
const op = n[0];
|
|
4000
|
+
if (op === "export" && Array.isArray(n[2]) && n[2][0] === "func" && typeof n[2][1] === "string") pinned.add(n[2][1]);
|
|
4001
|
+
else if (op === "start" && typeof n[1] === "string") pinned.add(n[1]);
|
|
4002
|
+
else if (op === "ref.func" && typeof n[1] === "string") pinned.add(n[1]);
|
|
4003
|
+
else if (op === "elem") {
|
|
4004
|
+
for (const c of n) if (typeof c === "string" && c[0] === "$") pinned.add(c);
|
|
4005
|
+
}
|
|
4006
|
+
for (const c of n) collectPinned(c, pinned);
|
|
4007
|
+
};
|
|
4008
|
+
for (let round = 0; round < 16; round++) {
|
|
4009
|
+
const funcs = ast.filter((n) => Array.isArray(n) && n[0] === "func");
|
|
4010
|
+
const funcByName = /* @__PURE__ */ new Map();
|
|
4011
|
+
for (const n of funcs) if (typeof n[1] === "string") funcByName.set(n[1], n);
|
|
4012
|
+
const callRefs = /* @__PURE__ */ new Map(), otherRef = /* @__PURE__ */ new Set();
|
|
4013
|
+
const countRefs = (n) => {
|
|
4014
|
+
if (!Array.isArray(n)) return;
|
|
4015
|
+
const op = n[0];
|
|
4016
|
+
if (op === "call" && typeof n[1] === "string") callRefs.set(n[1], (callRefs.get(n[1]) || 0) + 1);
|
|
4017
|
+
else if (op === "return_call" && typeof n[1] === "string") otherRef.add(n[1]);
|
|
4018
|
+
for (let i = 1; i < n.length; i++) countRefs(n[i]);
|
|
4019
|
+
};
|
|
4020
|
+
countRefs(ast);
|
|
4021
|
+
const pinned = /* @__PURE__ */ new Set();
|
|
4022
|
+
for (const n of ast) if (!Array.isArray(n) || n[0] !== "func") collectPinned(n, pinned);
|
|
4023
|
+
let calleeName = null;
|
|
4024
|
+
for (const [name2, fn] of funcByName) {
|
|
4025
|
+
if (pinned.has(name2) || otherRef.has(name2)) continue;
|
|
4026
|
+
if (callRefs.get(name2) !== 1) continue;
|
|
4027
|
+
if (callsSelf(fn, name2)) continue;
|
|
4028
|
+
let ok = true, nResult = 0;
|
|
4029
|
+
for (let i = 2; i < fn.length; i++) {
|
|
4030
|
+
const c = fn[i];
|
|
4031
|
+
if (typeof c === "string") continue;
|
|
4032
|
+
if (!Array.isArray(c)) {
|
|
4033
|
+
ok = false;
|
|
4034
|
+
break;
|
|
4035
|
+
}
|
|
4036
|
+
if (c[0] === "param" || c[0] === "local") {
|
|
4037
|
+
if (typeof c[1] !== "string" || c[1][0] !== "$") {
|
|
4038
|
+
ok = false;
|
|
4039
|
+
break;
|
|
4040
|
+
}
|
|
4041
|
+
} else if (c[0] === "result") nResult += c.length - 1;
|
|
4042
|
+
else if (c[0] === "export") {
|
|
4043
|
+
ok = false;
|
|
4044
|
+
break;
|
|
4045
|
+
} else if (c[0] === "type") continue;
|
|
4046
|
+
else break;
|
|
4047
|
+
}
|
|
4048
|
+
if (!ok || nResult > 1) continue;
|
|
4049
|
+
let bad = false;
|
|
4050
|
+
for (let i = bodyStart(fn); i < fn.length; i++) if (unsafe(fn[i])) {
|
|
4051
|
+
bad = true;
|
|
4052
|
+
break;
|
|
4053
|
+
}
|
|
4054
|
+
if (bad) continue;
|
|
4055
|
+
calleeName = name2;
|
|
4056
|
+
break;
|
|
4057
|
+
}
|
|
4058
|
+
if (!calleeName) break;
|
|
4059
|
+
const callee = funcByName.get(calleeName);
|
|
4060
|
+
const params = [], locals = [];
|
|
4061
|
+
let resultType = null;
|
|
4062
|
+
for (let i = 2; i < callee.length; i++) {
|
|
4063
|
+
const c = callee[i];
|
|
4064
|
+
if (typeof c === "string" || !Array.isArray(c)) continue;
|
|
4065
|
+
if (c[0] === "param") params.push({ name: c[1], type: c[2] });
|
|
4066
|
+
else if (c[0] === "result") {
|
|
4067
|
+
if (c.length > 1) resultType = c[1];
|
|
4068
|
+
} else if (c[0] === "local") locals.push({ name: c[1], type: c[2] });
|
|
4069
|
+
else if (c[0] === "export" || c[0] === "type") continue;
|
|
4070
|
+
else break;
|
|
4071
|
+
}
|
|
4072
|
+
const cBody = callee.slice(bodyStart(callee));
|
|
4073
|
+
const uid2 = ++inlineUid;
|
|
4074
|
+
const exit = `$__inl${uid2}`;
|
|
4075
|
+
const rename = /* @__PURE__ */ new Map();
|
|
4076
|
+
for (const p of params) rename.set(p.name, `$__inl${uid2}_${p.name.slice(1)}`);
|
|
4077
|
+
for (const l of locals) rename.set(l.name, `$__inl${uid2}_${l.name.slice(1)}`);
|
|
4078
|
+
const isBlockLabel = (op) => op === "block" || op === "loop" || op === "if";
|
|
4079
|
+
const labelRename = /* @__PURE__ */ new Map();
|
|
4080
|
+
const collectLabels = (n) => {
|
|
4081
|
+
if (!Array.isArray(n)) return;
|
|
4082
|
+
if (isBlockLabel(n[0]) && typeof n[1] === "string" && n[1][0] === "$" && !labelRename.has(n[1]))
|
|
4083
|
+
labelRename.set(n[1], `$__inl${uid2}L_${n[1].slice(1)}`);
|
|
4084
|
+
for (let i = 1; i < n.length; i++) collectLabels(n[i]);
|
|
4085
|
+
};
|
|
4086
|
+
for (const n of cBody) collectLabels(n);
|
|
4087
|
+
const sub = (n) => {
|
|
4088
|
+
if (!Array.isArray(n)) return n;
|
|
4089
|
+
const op = n[0];
|
|
4090
|
+
if ((op === "local.get" || op === "local.set" || op === "local.tee") && typeof n[1] === "string" && rename.has(n[1]))
|
|
4091
|
+
return [op, rename.get(n[1]), ...n.slice(2).map(sub)];
|
|
4092
|
+
if (op === "return") return ["br", exit, ...n.slice(1).map(sub)];
|
|
4093
|
+
if (isBlockLabel(op) && typeof n[1] === "string" && labelRename.has(n[1]))
|
|
4094
|
+
return [op, labelRename.get(n[1]), ...n.slice(2).map(sub)];
|
|
4095
|
+
if (isBranch(op)) return [op, ...n.slice(1).map((c) => typeof c === "string" && labelRename.has(c) ? labelRename.get(c) : sub(c))];
|
|
4096
|
+
return n.map((c, i) => i === 0 ? c : sub(c));
|
|
4097
|
+
};
|
|
4098
|
+
let done = false;
|
|
4099
|
+
for (const fn of funcs) {
|
|
4100
|
+
if (fn === callee || done) continue;
|
|
4101
|
+
const start = bodyStart(fn);
|
|
4102
|
+
for (let i = start; i < fn.length; i++) {
|
|
4103
|
+
const replaced = walkPost2(fn[i], (n) => {
|
|
4104
|
+
if (done || !Array.isArray(n) || n[0] !== "call" || n[1] !== calleeName) return;
|
|
4105
|
+
const args = n.slice(2);
|
|
4106
|
+
if (args.length !== params.length) return;
|
|
4107
|
+
const setup = params.map((p, k) => ["local.set", rename.get(p.name), args[k]]);
|
|
4108
|
+
const inner = cBody.map(sub);
|
|
4109
|
+
done = true;
|
|
4110
|
+
return resultType ? ["block", exit, ["result", resultType], ...setup, ...inner] : ["block", exit, ...setup, ...inner];
|
|
4111
|
+
});
|
|
4112
|
+
if (replaced !== fn[i]) fn[i] = replaced;
|
|
4113
|
+
if (done) {
|
|
4114
|
+
const decls = [...params, ...locals].map((p) => ["local", rename.get(p.name), p.type]);
|
|
4115
|
+
if (decls.length) fn.splice(bodyStart(fn), 0, ...decls);
|
|
4116
|
+
break;
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
if (done) break;
|
|
4120
|
+
}
|
|
4121
|
+
if (!done) break;
|
|
4122
|
+
const idx = ast.indexOf(callee);
|
|
4123
|
+
if (idx >= 0) ast.splice(idx, 1);
|
|
4124
|
+
}
|
|
4125
|
+
return ast;
|
|
4126
|
+
};
|
|
4127
|
+
var targetsLabel = (body, label) => {
|
|
4128
|
+
let found = false;
|
|
4129
|
+
const search = (n, shadowed) => {
|
|
4130
|
+
if (found || !Array.isArray(n)) return;
|
|
4131
|
+
const op = n[0];
|
|
4132
|
+
let inner = shadowed;
|
|
4133
|
+
if ((op === "block" || op === "loop") && typeof n[1] === "string" && n[1] === label) inner = true;
|
|
4134
|
+
if (!shadowed) {
|
|
4135
|
+
if (op === "br" || op === "br_if" || op === "br_on_null" || op === "br_on_non_null" || op === "br_on_cast" || op === "br_on_cast_fail") {
|
|
4136
|
+
if (n[1] === label) {
|
|
4137
|
+
found = true;
|
|
4138
|
+
return;
|
|
4139
|
+
}
|
|
4140
|
+
} else if (op === "br_table") {
|
|
4141
|
+
for (let j = 1; j < n.length; j++) {
|
|
4142
|
+
if (typeof n[j] === "string") {
|
|
4143
|
+
if (n[j] === label) {
|
|
4144
|
+
found = true;
|
|
4145
|
+
return;
|
|
4146
|
+
}
|
|
4147
|
+
} else break;
|
|
4148
|
+
}
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
for (let i = 1; i < n.length; i++) search(n[i], inner);
|
|
4152
|
+
};
|
|
4153
|
+
for (const node of body) search(node, false);
|
|
4154
|
+
return found;
|
|
4155
|
+
};
|
|
4156
|
+
var mergeBlocks = (ast) => {
|
|
4157
|
+
walk2(ast, (node) => {
|
|
4158
|
+
if (!isScopeNode(node)) return;
|
|
4159
|
+
let i = 1;
|
|
4160
|
+
while (i < node.length) {
|
|
4161
|
+
const child = node[i];
|
|
4162
|
+
if (!Array.isArray(child) || child[0] !== "block") {
|
|
4163
|
+
i++;
|
|
4164
|
+
continue;
|
|
4165
|
+
}
|
|
4166
|
+
let bi = 1, label = null;
|
|
4167
|
+
if (typeof child[1] === "string" && child[1][0] === "$") {
|
|
4168
|
+
label = child[1];
|
|
4169
|
+
bi = 2;
|
|
4170
|
+
}
|
|
4171
|
+
let typed = false;
|
|
4172
|
+
for (let j = bi; j < child.length; j++) {
|
|
4173
|
+
const c = child[j];
|
|
4174
|
+
if (Array.isArray(c) && (c[0] === "param" || c[0] === "result" || c[0] === "type")) {
|
|
4175
|
+
typed = true;
|
|
4176
|
+
break;
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
if (typed) {
|
|
4180
|
+
i++;
|
|
4181
|
+
continue;
|
|
4182
|
+
}
|
|
4183
|
+
const body = child.slice(bi);
|
|
4184
|
+
if (label && targetsLabel(body, label)) {
|
|
4185
|
+
i++;
|
|
4186
|
+
continue;
|
|
4187
|
+
}
|
|
4188
|
+
node.splice(i, 1, ...body);
|
|
4189
|
+
i += body.length;
|
|
4190
|
+
}
|
|
4191
|
+
});
|
|
4192
|
+
return ast;
|
|
4193
|
+
};
|
|
4194
|
+
var coalesceLocals = (ast) => {
|
|
4195
|
+
walk2(ast, (funcNode) => {
|
|
4196
|
+
if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
|
|
4197
|
+
const decls = /* @__PURE__ */ new Map();
|
|
4198
|
+
for (const sub of funcNode) {
|
|
4199
|
+
if (Array.isArray(sub) && sub[0] === "local" && typeof sub[1] === "string" && sub[1][0] === "$" && typeof sub[2] === "string") {
|
|
4200
|
+
decls.set(sub[1], sub[2]);
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
if (decls.size < 2) return;
|
|
4204
|
+
const uses = /* @__PURE__ */ new Map();
|
|
4205
|
+
const loopStack = [];
|
|
4206
|
+
let pos = 0, abort = false, condDepth = 0;
|
|
4207
|
+
const visit = (n) => {
|
|
4208
|
+
if (abort || !Array.isArray(n)) return;
|
|
4209
|
+
const op = n[0];
|
|
4210
|
+
const isLoop = op === "loop";
|
|
4211
|
+
if (isLoop) loopStack.push({ start: pos, end: pos });
|
|
4212
|
+
const isSet = op === "local.set" || op === "local.tee";
|
|
4213
|
+
if (isSet || op === "local.get") {
|
|
4214
|
+
const name2 = n[1];
|
|
4215
|
+
if (typeof name2 !== "string" || name2[0] !== "$") {
|
|
4216
|
+
abort = true;
|
|
4217
|
+
return;
|
|
4218
|
+
}
|
|
4219
|
+
if (isSet) for (let i = 2; i < n.length; i++) visit(n[i]);
|
|
4220
|
+
const here = pos++;
|
|
4221
|
+
if (decls.has(name2)) {
|
|
4222
|
+
let u = uses.get(name2);
|
|
4223
|
+
if (!u) {
|
|
4224
|
+
u = { start: here, end: here, firstOp: op, firstCond: condDepth > 0, loops: /* @__PURE__ */ new Set() };
|
|
4225
|
+
uses.set(name2, u);
|
|
4226
|
+
}
|
|
4227
|
+
if (here > u.end) u.end = here;
|
|
4228
|
+
for (const ls of loopStack) u.loops.add(ls);
|
|
4229
|
+
}
|
|
4230
|
+
} else {
|
|
4231
|
+
pos++;
|
|
4232
|
+
const isIf = op === "if";
|
|
4233
|
+
for (let i = 1; i < n.length; i++) {
|
|
4234
|
+
const c = n[i];
|
|
4235
|
+
const cond = isIf && Array.isArray(c) && (c[0] === "then" || c[0] === "else");
|
|
4236
|
+
if (cond) condDepth++;
|
|
4237
|
+
visit(c);
|
|
4238
|
+
if (cond) condDepth--;
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
if (isLoop) {
|
|
4242
|
+
const ls = loopStack.pop();
|
|
4243
|
+
ls.end = pos;
|
|
4244
|
+
}
|
|
4245
|
+
};
|
|
4246
|
+
visit(funcNode);
|
|
4247
|
+
if (abort) return;
|
|
4248
|
+
for (const u of uses.values()) {
|
|
4249
|
+
for (const ls of u.loops) {
|
|
4250
|
+
if (ls.start < u.start) u.start = ls.start;
|
|
4251
|
+
if (ls.end > u.end) u.end = ls.end;
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
const ordered = [...uses.entries()].sort((a, b) => a[1].start - b[1].start);
|
|
4255
|
+
const rename = /* @__PURE__ */ new Map();
|
|
4256
|
+
const slots = [];
|
|
4257
|
+
for (const [name2, range] of ordered) {
|
|
4258
|
+
const readsZero = range.firstOp === "local.get" || range.firstCond;
|
|
4259
|
+
const type = decls.get(name2);
|
|
4260
|
+
const slot = readsZero ? null : slots.find((s) => s.type === type && s.end < range.start);
|
|
4261
|
+
if (slot) {
|
|
4262
|
+
rename.set(name2, slot.primary);
|
|
4263
|
+
if (range.end > slot.end) slot.end = range.end;
|
|
4264
|
+
} else slots.push({ primary: name2, type, end: range.end });
|
|
4265
|
+
}
|
|
4266
|
+
if (rename.size === 0) return;
|
|
4267
|
+
walk2(funcNode, (n) => {
|
|
4268
|
+
if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.set" || n[0] === "local.tee") && rename.has(n[1])) {
|
|
4269
|
+
n[1] = rename.get(n[1]);
|
|
4270
|
+
}
|
|
4271
|
+
});
|
|
4272
|
+
});
|
|
4273
|
+
return ast;
|
|
3903
4274
|
};
|
|
3904
4275
|
var vacuum = (ast) => {
|
|
3905
|
-
return walkPost2(
|
|
4276
|
+
return walkPost2(ast, (node) => {
|
|
3906
4277
|
if (!Array.isArray(node)) return;
|
|
3907
4278
|
const op = node[0];
|
|
3908
4279
|
if (op === "nop") return ["nop"];
|
|
@@ -4001,7 +4372,7 @@ var PEEPHOLE = {
|
|
|
4001
4372
|
"local.set": (a, b) => Array.isArray(b) && b[0] === "local.get" && b[1] === a ? ["nop"] : null
|
|
4002
4373
|
};
|
|
4003
4374
|
var peephole = (ast) => {
|
|
4004
|
-
return walkPost2(
|
|
4375
|
+
return walkPost2(ast, (node) => {
|
|
4005
4376
|
if (!Array.isArray(node) || node.length !== 3) return;
|
|
4006
4377
|
const fn = PEEPHOLE[node[0]];
|
|
4007
4378
|
if (!fn) return;
|
|
@@ -4009,37 +4380,85 @@ var peephole = (ast) => {
|
|
|
4009
4380
|
if (result !== null) return result;
|
|
4010
4381
|
});
|
|
4011
4382
|
};
|
|
4383
|
+
var slebSize = (v) => {
|
|
4384
|
+
let x = typeof v === "bigint" ? v : BigInt(Math.trunc(Number(v) || 0));
|
|
4385
|
+
let n = 1;
|
|
4386
|
+
while (true) {
|
|
4387
|
+
const b = x & 0x7fn;
|
|
4388
|
+
x >>= 7n;
|
|
4389
|
+
if (x === 0n && (b & 0x40n) === 0n || x === -1n && (b & 0x40n) !== 0n) return n;
|
|
4390
|
+
n++;
|
|
4391
|
+
}
|
|
4392
|
+
};
|
|
4393
|
+
var constInstrSize = (node) => {
|
|
4394
|
+
if (!Array.isArray(node)) return 4;
|
|
4395
|
+
switch (node[0]) {
|
|
4396
|
+
case "i32.const":
|
|
4397
|
+
case "i64.const":
|
|
4398
|
+
return 1 + slebSize(node[1]);
|
|
4399
|
+
case "f32.const":
|
|
4400
|
+
return 5;
|
|
4401
|
+
case "f64.const":
|
|
4402
|
+
return 9;
|
|
4403
|
+
case "v128.const":
|
|
4404
|
+
return 18;
|
|
4405
|
+
default:
|
|
4406
|
+
return 4;
|
|
4407
|
+
}
|
|
4408
|
+
};
|
|
4409
|
+
var GLOBAL_GET_SIZE = 2;
|
|
4012
4410
|
var globals = (ast) => {
|
|
4013
4411
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4014
|
-
const result = clone2(ast);
|
|
4015
4412
|
const constGlobals = /* @__PURE__ */ new Map();
|
|
4016
|
-
const
|
|
4017
|
-
for (const node of
|
|
4018
|
-
if (!Array.isArray(node)
|
|
4413
|
+
const exported = /* @__PURE__ */ new Set();
|
|
4414
|
+
for (const node of ast.slice(1)) {
|
|
4415
|
+
if (!Array.isArray(node)) continue;
|
|
4416
|
+
if (node[0] === "export" && Array.isArray(node[2]) && node[2][0] === "global" && typeof node[2][1] === "string") {
|
|
4417
|
+
exported.add(node[2][1]);
|
|
4418
|
+
continue;
|
|
4419
|
+
}
|
|
4420
|
+
if (node[0] !== "global") continue;
|
|
4019
4421
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
4020
4422
|
if (!name2) continue;
|
|
4021
|
-
|
|
4022
|
-
const
|
|
4023
|
-
const typeSlot = hasName ? node[2] : node[1];
|
|
4423
|
+
if (node.some((c) => Array.isArray(c) && c[0] === "export")) exported.add(name2);
|
|
4424
|
+
const typeSlot = node[2];
|
|
4024
4425
|
if (Array.isArray(typeSlot) && typeSlot[0] === "mut") continue;
|
|
4025
|
-
|
|
4426
|
+
if (Array.isArray(typeSlot) && typeSlot[0] === "import") continue;
|
|
4427
|
+
const init = node[3];
|
|
4026
4428
|
if (getConst(init)) constGlobals.set(name2, init);
|
|
4027
4429
|
}
|
|
4028
|
-
|
|
4029
|
-
|
|
4430
|
+
if (constGlobals.size === 0) return ast;
|
|
4431
|
+
const reads = /* @__PURE__ */ new Map();
|
|
4432
|
+
walk2(ast, (n) => {
|
|
4433
|
+
if (!Array.isArray(n)) return;
|
|
4030
4434
|
const ref = n[1];
|
|
4031
|
-
if (typeof ref
|
|
4435
|
+
if (typeof ref !== "string" || ref[0] !== "$") return;
|
|
4436
|
+
if (n[0] === "global.set") constGlobals.delete(ref);
|
|
4437
|
+
else if (n[0] === "global.get") reads.set(ref, (reads.get(ref) || 0) + 1);
|
|
4032
4438
|
});
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4439
|
+
const propagate2 = /* @__PURE__ */ new Set();
|
|
4440
|
+
for (const [name2, init] of constGlobals) {
|
|
4441
|
+
const r = reads.get(name2) || 0;
|
|
4442
|
+
if (r === 0) continue;
|
|
4443
|
+
const cs = constInstrSize(init);
|
|
4444
|
+
const declSize = cs + 2;
|
|
4445
|
+
const before = r * GLOBAL_GET_SIZE + declSize;
|
|
4446
|
+
const after = r * cs + (exported.has(name2) ? declSize : 0);
|
|
4447
|
+
if (after <= before) propagate2.add(name2);
|
|
4448
|
+
}
|
|
4449
|
+
if (propagate2.size === 0) return ast;
|
|
4450
|
+
walkPost2(ast, (node) => {
|
|
4036
4451
|
if (!Array.isArray(node) || node[0] !== "global.get" || node.length !== 2) return;
|
|
4037
|
-
|
|
4038
|
-
if (constGlobals.has(ref)) return clone2(constGlobals.get(ref));
|
|
4452
|
+
if (propagate2.has(node[1])) return clone2(constGlobals.get(node[1]));
|
|
4039
4453
|
});
|
|
4454
|
+
for (let i = ast.length - 1; i >= 1; i--) {
|
|
4455
|
+
const n = ast[i];
|
|
4456
|
+
if (Array.isArray(n) && n[0] === "global" && typeof n[1] === "string" && propagate2.has(n[1]) && !exported.has(n[1])) ast.splice(i, 1);
|
|
4457
|
+
}
|
|
4458
|
+
return ast;
|
|
4040
4459
|
};
|
|
4041
4460
|
var offset = (ast) => {
|
|
4042
|
-
return walkPost2(
|
|
4461
|
+
return walkPost2(ast, (node) => {
|
|
4043
4462
|
if (!Array.isArray(node)) return;
|
|
4044
4463
|
const op = node[0];
|
|
4045
4464
|
if (typeof op !== "string" || !op.endsWith("load") && !op.endsWith("store")) return;
|
|
@@ -4090,8 +4509,7 @@ var offset = (ast) => {
|
|
|
4090
4509
|
});
|
|
4091
4510
|
};
|
|
4092
4511
|
var unbranch = (ast) => {
|
|
4093
|
-
|
|
4094
|
-
walk2(result, (node) => {
|
|
4512
|
+
walk2(ast, (node) => {
|
|
4095
4513
|
if (!Array.isArray(node)) return;
|
|
4096
4514
|
const op = node[0];
|
|
4097
4515
|
if (op !== "block") return;
|
|
@@ -4117,19 +4535,18 @@ var unbranch = (ast) => {
|
|
|
4117
4535
|
if (lastIdx < 0) return;
|
|
4118
4536
|
const last = node[lastIdx];
|
|
4119
4537
|
if (Array.isArray(last) && last[0] === "br" && last[1] === label) {
|
|
4120
|
-
node.splice(lastIdx, 1);
|
|
4538
|
+
node.splice(lastIdx, 1, ...last.slice(2));
|
|
4121
4539
|
}
|
|
4122
4540
|
});
|
|
4123
|
-
return
|
|
4541
|
+
return ast;
|
|
4124
4542
|
};
|
|
4125
4543
|
var stripmut = (ast) => {
|
|
4126
4544
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4127
|
-
const result = clone2(ast);
|
|
4128
4545
|
const written = /* @__PURE__ */ new Set();
|
|
4129
|
-
walk2(
|
|
4546
|
+
walk2(ast, (n) => {
|
|
4130
4547
|
if (Array.isArray(n) && n[0] === "global.set" && typeof n[1] === "string") written.add(n[1]);
|
|
4131
4548
|
});
|
|
4132
|
-
return walkPost2(
|
|
4549
|
+
return walkPost2(ast, (node) => {
|
|
4133
4550
|
if (!Array.isArray(node) || node[0] !== "global") return;
|
|
4134
4551
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
4135
4552
|
if (!name2 || written.has(name2)) return;
|
|
@@ -4143,7 +4560,7 @@ var stripmut = (ast) => {
|
|
|
4143
4560
|
});
|
|
4144
4561
|
};
|
|
4145
4562
|
var brif = (ast) => {
|
|
4146
|
-
return walkPost2(
|
|
4563
|
+
return walkPost2(ast, (node) => {
|
|
4147
4564
|
if (!Array.isArray(node) || node[0] !== "if") return;
|
|
4148
4565
|
const { cond, thenBranch, elseBranch } = parseIf(node);
|
|
4149
4566
|
const thenEmpty = !thenBranch || thenBranch.length <= 1;
|
|
@@ -4159,7 +4576,7 @@ var brif = (ast) => {
|
|
|
4159
4576
|
});
|
|
4160
4577
|
};
|
|
4161
4578
|
var foldarms = (ast) => {
|
|
4162
|
-
return walkPost2(
|
|
4579
|
+
return walkPost2(ast, (node) => {
|
|
4163
4580
|
if (!Array.isArray(node) || node[0] !== "if") return;
|
|
4164
4581
|
const { thenBranch, elseBranch } = parseIf(node);
|
|
4165
4582
|
if (!thenBranch || !elseBranch) return;
|
|
@@ -4224,10 +4641,9 @@ var hashFunc = (node, localNames) => {
|
|
|
4224
4641
|
};
|
|
4225
4642
|
var dedupe = (ast) => {
|
|
4226
4643
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4227
|
-
const result = clone2(ast);
|
|
4228
4644
|
const signatures = /* @__PURE__ */ new Map();
|
|
4229
4645
|
const redirects = /* @__PURE__ */ new Map();
|
|
4230
|
-
for (const node of
|
|
4646
|
+
for (const node of ast.slice(1)) {
|
|
4231
4647
|
if (!Array.isArray(node) || node[0] !== "func") continue;
|
|
4232
4648
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
4233
4649
|
if (!name2) continue;
|
|
@@ -4247,8 +4663,8 @@ var dedupe = (ast) => {
|
|
|
4247
4663
|
signatures.set(hash, name2);
|
|
4248
4664
|
}
|
|
4249
4665
|
}
|
|
4250
|
-
if (redirects.size === 0) return
|
|
4251
|
-
walkPost2(
|
|
4666
|
+
if (redirects.size === 0) return ast;
|
|
4667
|
+
walkPost2(ast, (node) => {
|
|
4252
4668
|
if (!Array.isArray(node)) return;
|
|
4253
4669
|
const op = node[0];
|
|
4254
4670
|
if ((op === "call" || op === "return_call") && redirects.has(node[1])) {
|
|
@@ -4270,14 +4686,13 @@ var dedupe = (ast) => {
|
|
|
4270
4686
|
}
|
|
4271
4687
|
}
|
|
4272
4688
|
});
|
|
4273
|
-
return
|
|
4689
|
+
return ast;
|
|
4274
4690
|
};
|
|
4275
4691
|
var dedupTypes = (ast) => {
|
|
4276
4692
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4277
|
-
const result = clone2(ast);
|
|
4278
4693
|
const signatures = /* @__PURE__ */ new Map();
|
|
4279
4694
|
const redirects = /* @__PURE__ */ new Map();
|
|
4280
|
-
for (const node of
|
|
4695
|
+
for (const node of ast.slice(1)) {
|
|
4281
4696
|
if (!Array.isArray(node) || node[0] !== "type") continue;
|
|
4282
4697
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
4283
4698
|
if (!name2) continue;
|
|
@@ -4288,15 +4703,15 @@ var dedupTypes = (ast) => {
|
|
|
4288
4703
|
signatures.set(hash, name2);
|
|
4289
4704
|
}
|
|
4290
4705
|
}
|
|
4291
|
-
if (redirects.size === 0) return
|
|
4292
|
-
for (let i =
|
|
4293
|
-
const node =
|
|
4706
|
+
if (redirects.size === 0) return ast;
|
|
4707
|
+
for (let i = ast.length - 1; i >= 0; i--) {
|
|
4708
|
+
const node = ast[i];
|
|
4294
4709
|
if (Array.isArray(node) && node[0] === "type") {
|
|
4295
4710
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
4296
|
-
if (name2 && redirects.has(name2))
|
|
4711
|
+
if (name2 && redirects.has(name2)) ast.splice(i, 1);
|
|
4297
4712
|
}
|
|
4298
4713
|
}
|
|
4299
|
-
walkPost2(
|
|
4714
|
+
walkPost2(ast, (node) => {
|
|
4300
4715
|
if (!Array.isArray(node)) return;
|
|
4301
4716
|
const op = node[0];
|
|
4302
4717
|
if (op === "func") {
|
|
@@ -4329,7 +4744,7 @@ var dedupTypes = (ast) => {
|
|
|
4329
4744
|
}
|
|
4330
4745
|
}
|
|
4331
4746
|
});
|
|
4332
|
-
return
|
|
4747
|
+
return ast;
|
|
4333
4748
|
};
|
|
4334
4749
|
var parseDataString = (str2) => {
|
|
4335
4750
|
if (typeof str2 !== "string" || str2.length < 2 || str2[0] !== '"') return new Uint8Array();
|
|
@@ -4444,8 +4859,7 @@ var mergeDataSegments = (a, b) => {
|
|
|
4444
4859
|
};
|
|
4445
4860
|
var packData = (ast) => {
|
|
4446
4861
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4447
|
-
|
|
4448
|
-
for (const node of result) {
|
|
4862
|
+
for (const node of ast) {
|
|
4449
4863
|
if (!Array.isArray(node) || node[0] !== "data") continue;
|
|
4450
4864
|
let contentStart = 1;
|
|
4451
4865
|
if (typeof node[1] === "string" && node[1][0] === "$") contentStart = 2;
|
|
@@ -4461,8 +4875,8 @@ var packData = (ast) => {
|
|
|
4461
4875
|
}
|
|
4462
4876
|
}
|
|
4463
4877
|
const dataNodes = [];
|
|
4464
|
-
for (let i = 0; i <
|
|
4465
|
-
const node =
|
|
4878
|
+
for (let i = 0; i < ast.length; i++) {
|
|
4879
|
+
const node = ast[i];
|
|
4466
4880
|
if (Array.isArray(node) && node[0] === "data") {
|
|
4467
4881
|
const info = getDataOffset(node);
|
|
4468
4882
|
if (info) {
|
|
@@ -4488,9 +4902,9 @@ var packData = (ast) => {
|
|
|
4488
4902
|
}
|
|
4489
4903
|
}
|
|
4490
4904
|
if (toRemove.size > 0) {
|
|
4491
|
-
|
|
4905
|
+
ast = ast.filter((_, i) => !toRemove.has(i));
|
|
4492
4906
|
}
|
|
4493
|
-
return
|
|
4907
|
+
return ast;
|
|
4494
4908
|
};
|
|
4495
4909
|
var makeShortener = () => {
|
|
4496
4910
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -4509,10 +4923,9 @@ var makeShortener = () => {
|
|
|
4509
4923
|
};
|
|
4510
4924
|
var minifyImports = (ast) => {
|
|
4511
4925
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4512
|
-
const result = clone2(ast);
|
|
4513
4926
|
const shortMod = makeShortener();
|
|
4514
4927
|
const shortField = makeShortener();
|
|
4515
|
-
for (const node of
|
|
4928
|
+
for (const node of ast) {
|
|
4516
4929
|
if (!Array.isArray(node) || node[0] !== "import") continue;
|
|
4517
4930
|
if (typeof node[1] === "string" && node[1][0] === '"') {
|
|
4518
4931
|
node[1] = '"' + shortMod(node[1].slice(1, -1)) + '"';
|
|
@@ -4521,7 +4934,7 @@ var minifyImports = (ast) => {
|
|
|
4521
4934
|
node[2] = '"' + shortField(node[2].slice(1, -1)) + '"';
|
|
4522
4935
|
}
|
|
4523
4936
|
}
|
|
4524
|
-
return
|
|
4937
|
+
return ast;
|
|
4525
4938
|
};
|
|
4526
4939
|
var reorderSafe = (ast) => {
|
|
4527
4940
|
let safe = true;
|
|
@@ -4549,16 +4962,15 @@ var reorderSafe = (ast) => {
|
|
|
4549
4962
|
var reorder = (ast) => {
|
|
4550
4963
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4551
4964
|
if (!reorderSafe(ast)) return ast;
|
|
4552
|
-
const result = clone2(ast);
|
|
4553
4965
|
const callCounts = /* @__PURE__ */ new Map();
|
|
4554
|
-
walk2(
|
|
4966
|
+
walk2(ast, (n) => {
|
|
4555
4967
|
if (!Array.isArray(n)) return;
|
|
4556
4968
|
if (n[0] === "call" || n[0] === "return_call") {
|
|
4557
4969
|
callCounts.set(n[1], (callCounts.get(n[1]) || 0) + 1);
|
|
4558
4970
|
}
|
|
4559
4971
|
});
|
|
4560
4972
|
const imports = [], funcs = [], others = [];
|
|
4561
|
-
for (const node of
|
|
4973
|
+
for (const node of ast.slice(1)) {
|
|
4562
4974
|
if (!Array.isArray(node)) {
|
|
4563
4975
|
others.push(node);
|
|
4564
4976
|
continue;
|
|
@@ -4572,10 +4984,16 @@ var reorder = (ast) => {
|
|
|
4572
4984
|
};
|
|
4573
4985
|
function optimize(ast, opts = true) {
|
|
4574
4986
|
if (typeof ast === "string") ast = parse_default(ast);
|
|
4575
|
-
|
|
4987
|
+
const strictGuard = opts === true;
|
|
4576
4988
|
opts = normalize3(opts);
|
|
4577
|
-
|
|
4578
|
-
|
|
4989
|
+
const log = opts.log ? (msg, delta) => opts.log(msg, delta) : () => {
|
|
4990
|
+
};
|
|
4991
|
+
const verbose = opts.verbose || opts.log;
|
|
4992
|
+
ast = clone2(ast);
|
|
4993
|
+
let beforeRound = null;
|
|
4994
|
+
for (let round = 0; round < 3; round++) {
|
|
4995
|
+
beforeRound = clone2(ast);
|
|
4996
|
+
const sizeBefore = binarySize(ast);
|
|
4579
4997
|
if (opts.stripmut) ast = stripmut(ast);
|
|
4580
4998
|
if (opts.globals) ast = globals(ast);
|
|
4581
4999
|
if (opts.fold) ast = fold(ast);
|
|
@@ -4584,6 +5002,7 @@ function optimize(ast, opts = true) {
|
|
|
4584
5002
|
if (opts.strength) ast = strength(ast);
|
|
4585
5003
|
if (opts.branch) ast = branch(ast);
|
|
4586
5004
|
if (opts.propagate) ast = propagate(ast);
|
|
5005
|
+
if (opts.inlineOnce) ast = inlineOnce(ast);
|
|
4587
5006
|
if (opts.inline) ast = inline(ast);
|
|
4588
5007
|
if (opts.offset) ast = offset(ast);
|
|
4589
5008
|
if (opts.unbranch) ast = unbranch(ast);
|
|
@@ -4591,6 +5010,8 @@ function optimize(ast, opts = true) {
|
|
|
4591
5010
|
if (opts.foldarms) ast = foldarms(ast);
|
|
4592
5011
|
if (opts.deadcode) ast = deadcode(ast);
|
|
4593
5012
|
if (opts.vacuum) ast = vacuum(ast);
|
|
5013
|
+
if (opts.mergeBlocks) ast = mergeBlocks(ast);
|
|
5014
|
+
if (opts.coalesce) ast = coalesceLocals(ast);
|
|
4594
5015
|
if (opts.locals) ast = localReuse(ast);
|
|
4595
5016
|
if (opts.dedupe) ast = dedupe(ast);
|
|
4596
5017
|
if (opts.dedupTypes) ast = dedupTypes(ast);
|
|
@@ -4598,7 +5019,19 @@ function optimize(ast, opts = true) {
|
|
|
4598
5019
|
if (opts.reorder) ast = reorder(ast);
|
|
4599
5020
|
if (opts.treeshake) ast = treeshake(ast);
|
|
4600
5021
|
if (opts.minifyImports) ast = minifyImports(ast);
|
|
4601
|
-
if (
|
|
5022
|
+
if (opts.propagate && (opts.inlineOnce || opts.inline)) ast = propagate(ast);
|
|
5023
|
+
const sizeAfter = binarySize(ast);
|
|
5024
|
+
const delta = sizeAfter - sizeBefore;
|
|
5025
|
+
if (verbose || delta !== 0) {
|
|
5026
|
+
log(` round ${round + 1}: ${delta > 0 ? "+" : ""}${delta} bytes`, delta);
|
|
5027
|
+
}
|
|
5028
|
+
const tolerance = strictGuard ? 0 : 16;
|
|
5029
|
+
if (delta > tolerance) {
|
|
5030
|
+
if (verbose) log(` \u26A0 round ${round + 1} inflated by ${delta} bytes, reverting`, delta);
|
|
5031
|
+
ast = beforeRound;
|
|
5032
|
+
break;
|
|
5033
|
+
}
|
|
5034
|
+
if (equal(beforeRound, ast)) break;
|
|
4602
5035
|
}
|
|
4603
5036
|
return ast;
|
|
4604
5037
|
}
|