watr 4.5.3 → 4.6.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/dist/watr.js +565 -44
- 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/optimize.js +670 -49
- package/types/src/optimize.d.ts +107 -6
- package/types/src/optimize.d.ts.map +1 -1
package/dist/watr.js
CHANGED
|
@@ -1459,10 +1459,10 @@ function compile(nodes) {
|
|
|
1459
1459
|
if (imported) ctx.import.push([...imported, [kind, ...node]]), node = null;
|
|
1460
1460
|
items.push(node);
|
|
1461
1461
|
});
|
|
1462
|
-
const bin = (kind,
|
|
1462
|
+
const bin = (kind, count = true) => {
|
|
1463
1463
|
const items = ctx[kind].filter(Boolean).map((item) => build[kind](item, ctx)).filter(Boolean);
|
|
1464
1464
|
if (kind === SECTION.custom) return items.flatMap((content) => [kind, ...vec(content)]);
|
|
1465
|
-
return !items.length ? [] : [kind, ...vec(
|
|
1465
|
+
return !items.length ? [] : [kind, ...vec(count ? vec(items) : items.flat())];
|
|
1466
1466
|
};
|
|
1467
1467
|
const binMeta = () => {
|
|
1468
1468
|
const sections = [];
|
|
@@ -1869,16 +1869,16 @@ var IMM = {
|
|
|
1869
1869
|
isId(n[0]) && (c.block[n.shift()] = c.block.length + 1);
|
|
1870
1870
|
let blocktype2 = n.shift();
|
|
1871
1871
|
let result = !blocktype2 ? [TYPE.void] : blocktype2[0] === "result" ? reftype(blocktype2[1], c) : uleb(id(blocktype2[1], c.type));
|
|
1872
|
-
let catches = [],
|
|
1872
|
+
let catches = [], count = 0;
|
|
1873
1873
|
while (n[0]?.[0] === "catch" || n[0]?.[0] === "catch_ref" || n[0]?.[0] === "catch_all" || n[0]?.[0] === "catch_all_ref") {
|
|
1874
1874
|
let clause = n.shift();
|
|
1875
1875
|
let kind = clause[0] === "catch" ? 0 : clause[0] === "catch_ref" ? 1 : clause[0] === "catch_all" ? 2 : 3;
|
|
1876
1876
|
if (kind <= 1) catches.push(kind, ...uleb(id(clause[1], c.tag)), ...uleb(blockid(clause[2], c.block)));
|
|
1877
1877
|
else catches.push(kind, ...uleb(blockid(clause[1], c.block)));
|
|
1878
|
-
|
|
1878
|
+
count++;
|
|
1879
1879
|
}
|
|
1880
1880
|
c.block.push(1);
|
|
1881
|
-
return [...result, ...uleb(
|
|
1881
|
+
return [...result, ...uleb(count), ...catches];
|
|
1882
1882
|
},
|
|
1883
1883
|
end: (_n, c) => (c.block.pop(), []),
|
|
1884
1884
|
call_indirect: (n, c) => {
|
|
@@ -1886,9 +1886,9 @@ var IMM = {
|
|
|
1886
1886
|
return [...uleb(id(idx, c.type)), ...uleb(id(t, c.table))];
|
|
1887
1887
|
},
|
|
1888
1888
|
br_table: (n, c) => {
|
|
1889
|
-
let labels = [],
|
|
1890
|
-
while (n[0] && (!isNaN(n[0]) || isId(n[0]))) labels.push(...uleb(blockid(n.shift(), c.block))),
|
|
1891
|
-
return [...uleb(
|
|
1889
|
+
let labels = [], count = 0;
|
|
1890
|
+
while (n[0] && (!isNaN(n[0]) || isId(n[0]))) labels.push(...uleb(blockid(n.shift(), c.block))), count++;
|
|
1891
|
+
return [...uleb(count - 1), ...labels];
|
|
1892
1892
|
},
|
|
1893
1893
|
select: (n, c) => {
|
|
1894
1894
|
let r = n.shift() || [];
|
|
@@ -3064,12 +3064,18 @@ var OPTS = {
|
|
|
3064
3064
|
// strength reduction (x * 2 → x << 1)
|
|
3065
3065
|
branch: true,
|
|
3066
3066
|
// simplify constant branches
|
|
3067
|
-
propagate:
|
|
3068
|
-
//
|
|
3067
|
+
propagate: true,
|
|
3068
|
+
// forward-propagate single-use locals & tiny consts (never inflates)
|
|
3069
3069
|
inline: false,
|
|
3070
3070
|
// inline tiny functions — can duplicate bodies
|
|
3071
|
+
inlineOnce: true,
|
|
3072
|
+
// inline single-call functions into their lone caller (never duplicates)
|
|
3071
3073
|
vacuum: true,
|
|
3072
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
|
|
3073
3079
|
peephole: true,
|
|
3074
3080
|
// x-x→0, x&0→0, etc.
|
|
3075
3081
|
globals: true,
|
|
@@ -3078,6 +3084,8 @@ var OPTS = {
|
|
|
3078
3084
|
// fold add+const into load/store offset
|
|
3079
3085
|
unbranch: true,
|
|
3080
3086
|
// remove redundant br at end of own block
|
|
3087
|
+
loopify: true,
|
|
3088
|
+
// collapse block+loop+brif while-idiom into loop+if
|
|
3081
3089
|
stripmut: true,
|
|
3082
3090
|
// strip mut from never-written globals
|
|
3083
3091
|
brif: true,
|
|
@@ -3096,11 +3104,12 @@ var OPTS = {
|
|
|
3096
3104
|
// shorten import names — enable only when you control the host
|
|
3097
3105
|
};
|
|
3098
3106
|
var ALL2 = Object.keys(OPTS);
|
|
3099
|
-
var
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3107
|
+
var binarySize = (ast) => {
|
|
3108
|
+
try {
|
|
3109
|
+
return compile(ast).length;
|
|
3110
|
+
} catch {
|
|
3111
|
+
return Infinity;
|
|
3112
|
+
}
|
|
3104
3113
|
};
|
|
3105
3114
|
var equal = (a, b) => {
|
|
3106
3115
|
if (a === b) return true;
|
|
@@ -3305,6 +3314,28 @@ var treeshake = (ast) => {
|
|
|
3305
3314
|
return result;
|
|
3306
3315
|
};
|
|
3307
3316
|
var roundEven = (x) => x - Math.floor(x) !== 0.5 ? Math.round(x) : 2 * Math.round(x / 2);
|
|
3317
|
+
var _rb8 = new ArrayBuffer(8);
|
|
3318
|
+
var _rf64 = new Float64Array(_rb8);
|
|
3319
|
+
var _ri64 = new BigInt64Array(_rb8);
|
|
3320
|
+
var _rb4 = new ArrayBuffer(4);
|
|
3321
|
+
var _rf32 = new Float32Array(_rb4);
|
|
3322
|
+
var _ri32 = new Int32Array(_rb4);
|
|
3323
|
+
var i64FromF64 = (x) => {
|
|
3324
|
+
_rf64[0] = x;
|
|
3325
|
+
return _ri64[0];
|
|
3326
|
+
};
|
|
3327
|
+
var f64FromI64 = (x) => {
|
|
3328
|
+
_ri64[0] = BigInt.asIntN(64, x);
|
|
3329
|
+
return _rf64[0];
|
|
3330
|
+
};
|
|
3331
|
+
var i32FromF32 = (x) => {
|
|
3332
|
+
_rf32[0] = x;
|
|
3333
|
+
return _ri32[0];
|
|
3334
|
+
};
|
|
3335
|
+
var f32FromI32 = (x) => {
|
|
3336
|
+
_ri32[0] = x | 0;
|
|
3337
|
+
return _rf32[0];
|
|
3338
|
+
};
|
|
3308
3339
|
var i32c = (fn) => (a, b) => fn(a, b) ? 1 : 0;
|
|
3309
3340
|
var u32c = (fn) => (a, b) => fn(a >>> 0, b >>> 0) ? 1 : 0;
|
|
3310
3341
|
var i64c = (fn) => (a, b) => fn(a, b) ? 1 : 0;
|
|
@@ -3408,7 +3439,23 @@ var FOLDABLE = {
|
|
|
3408
3439
|
"f64.ceil": [Math.ceil, "f64"],
|
|
3409
3440
|
"f64.floor": [Math.floor, "f64"],
|
|
3410
3441
|
"f64.trunc": [Math.trunc, "f64"],
|
|
3411
|
-
"f64.nearest": [roundEven, "f64"]
|
|
3442
|
+
"f64.nearest": [roundEven, "f64"],
|
|
3443
|
+
// Bit-exact reinterprets (preserve NaN payloads)
|
|
3444
|
+
"i32.reinterpret_f32": [i32FromF32, "i32"],
|
|
3445
|
+
"f32.reinterpret_i32": [f32FromI32, "f32"],
|
|
3446
|
+
"i64.reinterpret_f64": [i64FromF64, "i64"],
|
|
3447
|
+
"f64.reinterpret_i64": [f64FromI64, "f64"],
|
|
3448
|
+
// Numeric conversions (value-preserving where representable)
|
|
3449
|
+
"f32.convert_i32_s": [(a) => Math.fround(a | 0), "f32"],
|
|
3450
|
+
"f32.convert_i32_u": [(a) => Math.fround(a >>> 0), "f32"],
|
|
3451
|
+
"f32.convert_i64_s": [(a) => Math.fround(Number(BigInt.asIntN(64, a))), "f32"],
|
|
3452
|
+
"f32.convert_i64_u": [(a) => Math.fround(Number(BigInt.asUintN(64, a))), "f32"],
|
|
3453
|
+
"f64.convert_i32_s": [(a) => a | 0, "f64"],
|
|
3454
|
+
"f64.convert_i32_u": [(a) => a >>> 0, "f64"],
|
|
3455
|
+
"f64.convert_i64_s": [(a) => Number(BigInt.asIntN(64, a)), "f64"],
|
|
3456
|
+
"f64.convert_i64_u": [(a) => Number(BigInt.asUintN(64, a)), "f64"],
|
|
3457
|
+
"f32.demote_f64": [(a) => Math.fround(a), "f32"],
|
|
3458
|
+
"f64.promote_f32": [(a) => Math.fround(a), "f64"]
|
|
3412
3459
|
};
|
|
3413
3460
|
var getConst = (node) => {
|
|
3414
3461
|
if (!Array.isArray(node) || node.length !== 2) return null;
|
|
@@ -3736,7 +3783,29 @@ var countLocalUses = (node) => {
|
|
|
3736
3783
|
});
|
|
3737
3784
|
return counts;
|
|
3738
3785
|
};
|
|
3739
|
-
var
|
|
3786
|
+
var isTinyConst = (node) => {
|
|
3787
|
+
const c = getConst(node);
|
|
3788
|
+
if (!c) return false;
|
|
3789
|
+
if (c.type === "i32") {
|
|
3790
|
+
const v = c.value | 0;
|
|
3791
|
+
return v >= -64 && v <= 63;
|
|
3792
|
+
}
|
|
3793
|
+
if (c.type === "i64") {
|
|
3794
|
+
const v = typeof c.value === "bigint" ? c.value : BigInt(c.value);
|
|
3795
|
+
return v >= -64n && v <= 63n;
|
|
3796
|
+
}
|
|
3797
|
+
return false;
|
|
3798
|
+
};
|
|
3799
|
+
var canSubst = (k) => k.pure && k.singleUse || isTinyConst(k.val);
|
|
3800
|
+
var purgeRefs = (known, name2) => {
|
|
3801
|
+
for (const [key, tracked] of known) {
|
|
3802
|
+
let refs = false;
|
|
3803
|
+
walk2(tracked.val, (n) => {
|
|
3804
|
+
if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.tee") && n[1] === name2) refs = true;
|
|
3805
|
+
});
|
|
3806
|
+
if (refs) known.delete(key);
|
|
3807
|
+
}
|
|
3808
|
+
};
|
|
3740
3809
|
var substGets = (node, known) => walkPost2(node, (n) => {
|
|
3741
3810
|
if (!Array.isArray(n) || n[0] !== "local.get" || n.length !== 2) return;
|
|
3742
3811
|
const k = typeof n[1] === "string" && known.get(n[1]);
|
|
@@ -3754,6 +3823,7 @@ var forwardPropagate = (funcNode, params, useCounts) => {
|
|
|
3754
3823
|
if ((op === "local.set" || op === "local.tee") && instr2.length === 3 && typeof instr2[1] === "string") {
|
|
3755
3824
|
substGets(instr2[2], known);
|
|
3756
3825
|
const uses = getUseCount(instr2[1]);
|
|
3826
|
+
purgeRefs(known, instr2[1]);
|
|
3757
3827
|
known.set(instr2[1], {
|
|
3758
3828
|
val: instr2[2],
|
|
3759
3829
|
pure: isPure(instr2[2]),
|
|
@@ -3780,8 +3850,10 @@ var forwardPropagate = (funcNode, params, useCounts) => {
|
|
|
3780
3850
|
substGets(instr2, known);
|
|
3781
3851
|
if (!equal(prev, instr2)) changed = true;
|
|
3782
3852
|
walk2(instr2, (n) => {
|
|
3783
|
-
if (Array.isArray(n) && (n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string")
|
|
3853
|
+
if (Array.isArray(n) && (n[0] === "local.set" || n[0] === "local.tee") && typeof n[1] === "string") {
|
|
3784
3854
|
known.delete(n[1]);
|
|
3855
|
+
purgeRefs(known, n[1]);
|
|
3856
|
+
}
|
|
3785
3857
|
});
|
|
3786
3858
|
}
|
|
3787
3859
|
}
|
|
@@ -3840,19 +3912,27 @@ var eliminateDeadStores = (funcNode, params, useCounts) => {
|
|
|
3840
3912
|
}
|
|
3841
3913
|
return changed;
|
|
3842
3914
|
};
|
|
3915
|
+
var isScopeNode = (n) => Array.isArray(n) && (n[0] === "func" || n[0] === "block" || n[0] === "loop" || n[0] === "then" || n[0] === "else");
|
|
3843
3916
|
var propagate = (ast) => {
|
|
3844
3917
|
walk2(ast, (funcNode) => {
|
|
3845
3918
|
if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
|
|
3846
3919
|
const params = /* @__PURE__ */ new Set();
|
|
3847
3920
|
for (const sub of funcNode)
|
|
3848
3921
|
if (Array.isArray(sub) && sub[0] === "param" && typeof sub[1] === "string") params.add(sub[1]);
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
if (
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3922
|
+
const scopes = [];
|
|
3923
|
+
walkPost2(funcNode, (n) => {
|
|
3924
|
+
if (isScopeNode(n)) scopes.push(n);
|
|
3925
|
+
});
|
|
3926
|
+
for (let round = 0; round < 6; round++) {
|
|
3927
|
+
const useCounts = countLocalUses(funcNode);
|
|
3928
|
+
let progressed = false;
|
|
3929
|
+
for (const scope of scopes) {
|
|
3930
|
+
if (forwardPropagate(scope, params, useCounts)) progressed = true;
|
|
3931
|
+
if (eliminateSetGetPairs(scope, params, useCounts)) progressed = true;
|
|
3932
|
+
if (createLocalTees(scope, params, useCounts)) progressed = true;
|
|
3933
|
+
if (eliminateDeadStores(scope, params, useCounts)) progressed = true;
|
|
3934
|
+
}
|
|
3935
|
+
if (!progressed) break;
|
|
3856
3936
|
}
|
|
3857
3937
|
});
|
|
3858
3938
|
return ast;
|
|
@@ -3927,6 +4007,336 @@ var inline = (ast) => {
|
|
|
3927
4007
|
});
|
|
3928
4008
|
return ast;
|
|
3929
4009
|
};
|
|
4010
|
+
var inlineUid = 0;
|
|
4011
|
+
var inlineOnce = (ast) => {
|
|
4012
|
+
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4013
|
+
const HEAD = /* @__PURE__ */ new Set(["export", "type", "param", "result", "local"]);
|
|
4014
|
+
const bodyStart = (fn) => {
|
|
4015
|
+
let i = 2;
|
|
4016
|
+
while (i < fn.length && (typeof fn[i] === "string" || Array.isArray(fn[i]) && HEAD.has(fn[i][0]))) i++;
|
|
4017
|
+
return i;
|
|
4018
|
+
};
|
|
4019
|
+
const isBranch = (op) => op === "br" || op === "br_if" || op === "br_table";
|
|
4020
|
+
const unsafe = (n) => {
|
|
4021
|
+
if (!Array.isArray(n)) return false;
|
|
4022
|
+
const op = n[0];
|
|
4023
|
+
if (op === "return_call" || op === "return_call_indirect" || op === "return_call_ref") return true;
|
|
4024
|
+
if (op === "try" || op === "try_table" || op === "delegate" || op === "rethrow") return true;
|
|
4025
|
+
if (isBranch(op)) {
|
|
4026
|
+
for (let i = 1; i < n.length; i++) if (typeof n[i] === "number" || typeof n[i] === "string" && /^\d+$/.test(n[i])) return true;
|
|
4027
|
+
}
|
|
4028
|
+
for (let i = 1; i < n.length; i++) if (unsafe(n[i])) return true;
|
|
4029
|
+
return false;
|
|
4030
|
+
};
|
|
4031
|
+
const callsSelf = (n, name2) => {
|
|
4032
|
+
if (!Array.isArray(n)) return false;
|
|
4033
|
+
if ((n[0] === "call" || n[0] === "return_call") && n[1] === name2) return true;
|
|
4034
|
+
for (let i = 1; i < n.length; i++) if (callsSelf(n[i], name2)) return true;
|
|
4035
|
+
return false;
|
|
4036
|
+
};
|
|
4037
|
+
const collectPinned = (n, pinned) => {
|
|
4038
|
+
if (!Array.isArray(n)) return;
|
|
4039
|
+
const op = n[0];
|
|
4040
|
+
if (op === "export" && Array.isArray(n[2]) && n[2][0] === "func" && typeof n[2][1] === "string") pinned.add(n[2][1]);
|
|
4041
|
+
else if (op === "start" && typeof n[1] === "string") pinned.add(n[1]);
|
|
4042
|
+
else if (op === "ref.func" && typeof n[1] === "string") pinned.add(n[1]);
|
|
4043
|
+
else if (op === "elem") {
|
|
4044
|
+
for (const c of n) if (typeof c === "string" && c[0] === "$") pinned.add(c);
|
|
4045
|
+
}
|
|
4046
|
+
for (const c of n) collectPinned(c, pinned);
|
|
4047
|
+
};
|
|
4048
|
+
for (let round = 0; round < 16; round++) {
|
|
4049
|
+
const funcs = ast.filter((n) => Array.isArray(n) && n[0] === "func");
|
|
4050
|
+
const funcByName = /* @__PURE__ */ new Map();
|
|
4051
|
+
for (const n of funcs) if (typeof n[1] === "string") funcByName.set(n[1], n);
|
|
4052
|
+
const callRefs = /* @__PURE__ */ new Map(), otherRef = /* @__PURE__ */ new Set();
|
|
4053
|
+
const countRefs = (n) => {
|
|
4054
|
+
if (!Array.isArray(n)) return;
|
|
4055
|
+
const op = n[0];
|
|
4056
|
+
if (op === "call" && typeof n[1] === "string") callRefs.set(n[1], (callRefs.get(n[1]) || 0) + 1);
|
|
4057
|
+
else if (op === "return_call" && typeof n[1] === "string") otherRef.add(n[1]);
|
|
4058
|
+
for (let i = 1; i < n.length; i++) countRefs(n[i]);
|
|
4059
|
+
};
|
|
4060
|
+
countRefs(ast);
|
|
4061
|
+
const pinned = /* @__PURE__ */ new Set();
|
|
4062
|
+
for (const n of ast) if (!Array.isArray(n) || n[0] !== "func") collectPinned(n, pinned);
|
|
4063
|
+
let calleeName = null;
|
|
4064
|
+
for (const [name2, fn] of funcByName) {
|
|
4065
|
+
if (pinned.has(name2) || otherRef.has(name2)) continue;
|
|
4066
|
+
if (callRefs.get(name2) !== 1) continue;
|
|
4067
|
+
if (callsSelf(fn, name2)) continue;
|
|
4068
|
+
let ok = true, nResult = 0;
|
|
4069
|
+
for (let i = 2; i < fn.length; i++) {
|
|
4070
|
+
const c = fn[i];
|
|
4071
|
+
if (typeof c === "string") continue;
|
|
4072
|
+
if (!Array.isArray(c)) {
|
|
4073
|
+
ok = false;
|
|
4074
|
+
break;
|
|
4075
|
+
}
|
|
4076
|
+
if (c[0] === "param" || c[0] === "local") {
|
|
4077
|
+
if (typeof c[1] !== "string" || c[1][0] !== "$") {
|
|
4078
|
+
ok = false;
|
|
4079
|
+
break;
|
|
4080
|
+
}
|
|
4081
|
+
} else if (c[0] === "result") nResult += c.length - 1;
|
|
4082
|
+
else if (c[0] === "export") {
|
|
4083
|
+
ok = false;
|
|
4084
|
+
break;
|
|
4085
|
+
} else if (c[0] === "type") continue;
|
|
4086
|
+
else break;
|
|
4087
|
+
}
|
|
4088
|
+
if (!ok || nResult > 1) continue;
|
|
4089
|
+
let bad = false;
|
|
4090
|
+
for (let i = bodyStart(fn); i < fn.length; i++) if (unsafe(fn[i])) {
|
|
4091
|
+
bad = true;
|
|
4092
|
+
break;
|
|
4093
|
+
}
|
|
4094
|
+
if (bad) continue;
|
|
4095
|
+
calleeName = name2;
|
|
4096
|
+
break;
|
|
4097
|
+
}
|
|
4098
|
+
if (!calleeName) break;
|
|
4099
|
+
const callee = funcByName.get(calleeName);
|
|
4100
|
+
const params = [], locals = [];
|
|
4101
|
+
let resultType = null;
|
|
4102
|
+
for (let i = 2; i < callee.length; i++) {
|
|
4103
|
+
const c = callee[i];
|
|
4104
|
+
if (typeof c === "string" || !Array.isArray(c)) continue;
|
|
4105
|
+
if (c[0] === "param") params.push({ name: c[1], type: c[2] });
|
|
4106
|
+
else if (c[0] === "result") {
|
|
4107
|
+
if (c.length > 1) resultType = c[1];
|
|
4108
|
+
} else if (c[0] === "local") locals.push({ name: c[1], type: c[2] });
|
|
4109
|
+
else if (c[0] === "export" || c[0] === "type") continue;
|
|
4110
|
+
else break;
|
|
4111
|
+
}
|
|
4112
|
+
const cBody = callee.slice(bodyStart(callee));
|
|
4113
|
+
const uid2 = ++inlineUid;
|
|
4114
|
+
const exit = `$__inl${uid2}`;
|
|
4115
|
+
const rename = /* @__PURE__ */ new Map();
|
|
4116
|
+
for (const p of params) rename.set(p.name, `$__inl${uid2}_${p.name.slice(1)}`);
|
|
4117
|
+
for (const l of locals) rename.set(l.name, `$__inl${uid2}_${l.name.slice(1)}`);
|
|
4118
|
+
const isBlockLabel = (op) => op === "block" || op === "loop" || op === "if";
|
|
4119
|
+
const labelRename = /* @__PURE__ */ new Map();
|
|
4120
|
+
const collectLabels = (n) => {
|
|
4121
|
+
if (!Array.isArray(n)) return;
|
|
4122
|
+
if (isBlockLabel(n[0]) && typeof n[1] === "string" && n[1][0] === "$" && !labelRename.has(n[1]))
|
|
4123
|
+
labelRename.set(n[1], `$__inl${uid2}L_${n[1].slice(1)}`);
|
|
4124
|
+
for (let i = 1; i < n.length; i++) collectLabels(n[i]);
|
|
4125
|
+
};
|
|
4126
|
+
for (const n of cBody) collectLabels(n);
|
|
4127
|
+
const sub = (n) => {
|
|
4128
|
+
if (!Array.isArray(n)) return n;
|
|
4129
|
+
const op = n[0];
|
|
4130
|
+
if ((op === "local.get" || op === "local.set" || op === "local.tee") && typeof n[1] === "string" && rename.has(n[1]))
|
|
4131
|
+
return [op, rename.get(n[1]), ...n.slice(2).map(sub)];
|
|
4132
|
+
if (op === "return") return ["br", exit, ...n.slice(1).map(sub)];
|
|
4133
|
+
if (isBlockLabel(op) && typeof n[1] === "string" && labelRename.has(n[1]))
|
|
4134
|
+
return [op, labelRename.get(n[1]), ...n.slice(2).map(sub)];
|
|
4135
|
+
if (isBranch(op)) return [op, ...n.slice(1).map((c) => typeof c === "string" && labelRename.has(c) ? labelRename.get(c) : sub(c))];
|
|
4136
|
+
return n.map((c, i) => i === 0 ? c : sub(c));
|
|
4137
|
+
};
|
|
4138
|
+
let done = false;
|
|
4139
|
+
for (const fn of funcs) {
|
|
4140
|
+
if (fn === callee || done) continue;
|
|
4141
|
+
const start = bodyStart(fn);
|
|
4142
|
+
for (let i = start; i < fn.length; i++) {
|
|
4143
|
+
const replaced = walkPost2(fn[i], (n) => {
|
|
4144
|
+
if (done || !Array.isArray(n) || n[0] !== "call" || n[1] !== calleeName) return;
|
|
4145
|
+
const args = n.slice(2);
|
|
4146
|
+
if (args.length !== params.length) return;
|
|
4147
|
+
const setup = params.map((p, k) => ["local.set", rename.get(p.name), args[k]]);
|
|
4148
|
+
const inner = cBody.map(sub);
|
|
4149
|
+
done = true;
|
|
4150
|
+
return resultType ? ["block", exit, ["result", resultType], ...setup, ...inner] : ["block", exit, ...setup, ...inner];
|
|
4151
|
+
});
|
|
4152
|
+
if (replaced !== fn[i]) fn[i] = replaced;
|
|
4153
|
+
if (done) {
|
|
4154
|
+
const decls = [...params, ...locals].map((p) => ["local", rename.get(p.name), p.type]);
|
|
4155
|
+
if (decls.length) fn.splice(bodyStart(fn), 0, ...decls);
|
|
4156
|
+
break;
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
if (done) break;
|
|
4160
|
+
}
|
|
4161
|
+
if (!done) break;
|
|
4162
|
+
const idx = ast.indexOf(callee);
|
|
4163
|
+
if (idx >= 0) ast.splice(idx, 1);
|
|
4164
|
+
}
|
|
4165
|
+
return ast;
|
|
4166
|
+
};
|
|
4167
|
+
var targetsLabel = (body, label) => {
|
|
4168
|
+
let found = false;
|
|
4169
|
+
const search = (n, shadowed) => {
|
|
4170
|
+
if (found || !Array.isArray(n)) return;
|
|
4171
|
+
const op = n[0];
|
|
4172
|
+
let inner = shadowed;
|
|
4173
|
+
if ((op === "block" || op === "loop") && typeof n[1] === "string" && n[1] === label) inner = true;
|
|
4174
|
+
if (!shadowed) {
|
|
4175
|
+
if (op === "br" || op === "br_if" || op === "br_on_null" || op === "br_on_non_null" || op === "br_on_cast" || op === "br_on_cast_fail") {
|
|
4176
|
+
if (n[1] === label) {
|
|
4177
|
+
found = true;
|
|
4178
|
+
return;
|
|
4179
|
+
}
|
|
4180
|
+
} else if (op === "br_table") {
|
|
4181
|
+
for (let j = 1; j < n.length; j++) {
|
|
4182
|
+
if (typeof n[j] === "string") {
|
|
4183
|
+
if (n[j] === label) {
|
|
4184
|
+
found = true;
|
|
4185
|
+
return;
|
|
4186
|
+
}
|
|
4187
|
+
} else break;
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
for (let i = 1; i < n.length; i++) search(n[i], inner);
|
|
4192
|
+
};
|
|
4193
|
+
for (const node of body) search(node, false);
|
|
4194
|
+
return found;
|
|
4195
|
+
};
|
|
4196
|
+
var mergeBlocks = (ast) => {
|
|
4197
|
+
walkPost2(ast, (node) => {
|
|
4198
|
+
if (!Array.isArray(node) || node[0] !== "block") return;
|
|
4199
|
+
let bi = 1, label = null;
|
|
4200
|
+
if (typeof node[1] === "string" && node[1][0] === "$") {
|
|
4201
|
+
label = node[1];
|
|
4202
|
+
bi = 2;
|
|
4203
|
+
}
|
|
4204
|
+
let hasResult = false;
|
|
4205
|
+
while (bi < node.length) {
|
|
4206
|
+
const c = node[bi];
|
|
4207
|
+
if (Array.isArray(c) && (c[0] === "param" || c[0] === "type")) {
|
|
4208
|
+
bi++;
|
|
4209
|
+
continue;
|
|
4210
|
+
}
|
|
4211
|
+
if (Array.isArray(c) && c[0] === "result") {
|
|
4212
|
+
hasResult = true;
|
|
4213
|
+
bi++;
|
|
4214
|
+
continue;
|
|
4215
|
+
}
|
|
4216
|
+
break;
|
|
4217
|
+
}
|
|
4218
|
+
const body = node.slice(bi);
|
|
4219
|
+
if (!hasResult || body.length !== 1) return;
|
|
4220
|
+
const only = body[0];
|
|
4221
|
+
if (!Array.isArray(only)) return;
|
|
4222
|
+
if (label && targetsLabel(body, label)) return;
|
|
4223
|
+
node.length = 0;
|
|
4224
|
+
for (const tok of only) node.push(tok);
|
|
4225
|
+
});
|
|
4226
|
+
walk2(ast, (node) => {
|
|
4227
|
+
if (!isScopeNode(node)) return;
|
|
4228
|
+
let i = 1;
|
|
4229
|
+
while (i < node.length) {
|
|
4230
|
+
const child = node[i];
|
|
4231
|
+
if (!Array.isArray(child) || child[0] !== "block") {
|
|
4232
|
+
i++;
|
|
4233
|
+
continue;
|
|
4234
|
+
}
|
|
4235
|
+
let bi = 1, label = null;
|
|
4236
|
+
if (typeof child[1] === "string" && child[1][0] === "$") {
|
|
4237
|
+
label = child[1];
|
|
4238
|
+
bi = 2;
|
|
4239
|
+
}
|
|
4240
|
+
while (bi < child.length) {
|
|
4241
|
+
const c = child[bi];
|
|
4242
|
+
if (Array.isArray(c) && (c[0] === "param" || c[0] === "result" || c[0] === "type")) {
|
|
4243
|
+
bi++;
|
|
4244
|
+
continue;
|
|
4245
|
+
}
|
|
4246
|
+
break;
|
|
4247
|
+
}
|
|
4248
|
+
const body = child.slice(bi);
|
|
4249
|
+
if (label && targetsLabel(body, label)) {
|
|
4250
|
+
i++;
|
|
4251
|
+
continue;
|
|
4252
|
+
}
|
|
4253
|
+
node.splice(i, 1, ...body);
|
|
4254
|
+
i += body.length;
|
|
4255
|
+
}
|
|
4256
|
+
});
|
|
4257
|
+
return ast;
|
|
4258
|
+
};
|
|
4259
|
+
var coalesceLocals = (ast) => {
|
|
4260
|
+
walk2(ast, (funcNode) => {
|
|
4261
|
+
if (!Array.isArray(funcNode) || funcNode[0] !== "func") return;
|
|
4262
|
+
const decls = /* @__PURE__ */ new Map();
|
|
4263
|
+
for (const sub of funcNode) {
|
|
4264
|
+
if (Array.isArray(sub) && sub[0] === "local" && typeof sub[1] === "string" && sub[1][0] === "$" && typeof sub[2] === "string") {
|
|
4265
|
+
decls.set(sub[1], sub[2]);
|
|
4266
|
+
}
|
|
4267
|
+
}
|
|
4268
|
+
if (decls.size < 2) return;
|
|
4269
|
+
const uses = /* @__PURE__ */ new Map();
|
|
4270
|
+
const loopStack = [];
|
|
4271
|
+
let pos = 0, abort = false, condDepth = 0;
|
|
4272
|
+
const visit = (n) => {
|
|
4273
|
+
if (abort || !Array.isArray(n)) return;
|
|
4274
|
+
const op = n[0];
|
|
4275
|
+
const isLoop = op === "loop";
|
|
4276
|
+
if (isLoop) loopStack.push({ start: pos, end: pos });
|
|
4277
|
+
const isSet = op === "local.set" || op === "local.tee";
|
|
4278
|
+
if (isSet || op === "local.get") {
|
|
4279
|
+
const name2 = n[1];
|
|
4280
|
+
if (typeof name2 !== "string" || name2[0] !== "$") {
|
|
4281
|
+
abort = true;
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4284
|
+
if (isSet) for (let i = 2; i < n.length; i++) visit(n[i]);
|
|
4285
|
+
const here = pos++;
|
|
4286
|
+
if (decls.has(name2)) {
|
|
4287
|
+
let u = uses.get(name2);
|
|
4288
|
+
if (!u) {
|
|
4289
|
+
u = { start: here, end: here, firstOp: op, firstCond: condDepth > 0, loops: /* @__PURE__ */ new Set() };
|
|
4290
|
+
uses.set(name2, u);
|
|
4291
|
+
}
|
|
4292
|
+
if (here > u.end) u.end = here;
|
|
4293
|
+
for (const ls of loopStack) u.loops.add(ls);
|
|
4294
|
+
}
|
|
4295
|
+
} else {
|
|
4296
|
+
pos++;
|
|
4297
|
+
const isIf = op === "if";
|
|
4298
|
+
for (let i = 1; i < n.length; i++) {
|
|
4299
|
+
const c = n[i];
|
|
4300
|
+
const cond = isIf && Array.isArray(c) && (c[0] === "then" || c[0] === "else");
|
|
4301
|
+
if (cond) condDepth++;
|
|
4302
|
+
visit(c);
|
|
4303
|
+
if (cond) condDepth--;
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
if (isLoop) {
|
|
4307
|
+
const ls = loopStack.pop();
|
|
4308
|
+
ls.end = pos;
|
|
4309
|
+
}
|
|
4310
|
+
};
|
|
4311
|
+
visit(funcNode);
|
|
4312
|
+
if (abort) return;
|
|
4313
|
+
for (const u of uses.values()) {
|
|
4314
|
+
for (const ls of u.loops) {
|
|
4315
|
+
if (ls.start < u.start) u.start = ls.start;
|
|
4316
|
+
if (ls.end > u.end) u.end = ls.end;
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
const ordered = [...uses.entries()].sort((a, b) => a[1].start - b[1].start);
|
|
4320
|
+
const rename = /* @__PURE__ */ new Map();
|
|
4321
|
+
const slots = [];
|
|
4322
|
+
for (const [name2, range] of ordered) {
|
|
4323
|
+
const readsZero = range.firstOp === "local.get" || range.firstCond;
|
|
4324
|
+
const type = decls.get(name2);
|
|
4325
|
+
const slot = readsZero ? null : slots.find((s) => s.type === type && s.end < range.start);
|
|
4326
|
+
if (slot) {
|
|
4327
|
+
rename.set(name2, slot.primary);
|
|
4328
|
+
if (range.end > slot.end) slot.end = range.end;
|
|
4329
|
+
} else slots.push({ primary: name2, type, end: range.end });
|
|
4330
|
+
}
|
|
4331
|
+
if (rename.size === 0) return;
|
|
4332
|
+
walk2(funcNode, (n) => {
|
|
4333
|
+
if (Array.isArray(n) && (n[0] === "local.get" || n[0] === "local.set" || n[0] === "local.tee") && rename.has(n[1])) {
|
|
4334
|
+
n[1] = rename.get(n[1]);
|
|
4335
|
+
}
|
|
4336
|
+
});
|
|
4337
|
+
});
|
|
4338
|
+
return ast;
|
|
4339
|
+
};
|
|
3930
4340
|
var vacuum = (ast) => {
|
|
3931
4341
|
return walkPost2(ast, (node) => {
|
|
3932
4342
|
if (!Array.isArray(node)) return;
|
|
@@ -4035,33 +4445,82 @@ var peephole = (ast) => {
|
|
|
4035
4445
|
if (result !== null) return result;
|
|
4036
4446
|
});
|
|
4037
4447
|
};
|
|
4448
|
+
var slebSize = (v) => {
|
|
4449
|
+
let x = typeof v === "bigint" ? v : BigInt(Math.trunc(Number(v) || 0));
|
|
4450
|
+
let n = 1;
|
|
4451
|
+
while (true) {
|
|
4452
|
+
const b = x & 0x7fn;
|
|
4453
|
+
x >>= 7n;
|
|
4454
|
+
if (x === 0n && (b & 0x40n) === 0n || x === -1n && (b & 0x40n) !== 0n) return n;
|
|
4455
|
+
n++;
|
|
4456
|
+
}
|
|
4457
|
+
};
|
|
4458
|
+
var constInstrSize = (node) => {
|
|
4459
|
+
if (!Array.isArray(node)) return 4;
|
|
4460
|
+
switch (node[0]) {
|
|
4461
|
+
case "i32.const":
|
|
4462
|
+
case "i64.const":
|
|
4463
|
+
return 1 + slebSize(node[1]);
|
|
4464
|
+
case "f32.const":
|
|
4465
|
+
return 5;
|
|
4466
|
+
case "f64.const":
|
|
4467
|
+
return 9;
|
|
4468
|
+
case "v128.const":
|
|
4469
|
+
return 18;
|
|
4470
|
+
default:
|
|
4471
|
+
return 4;
|
|
4472
|
+
}
|
|
4473
|
+
};
|
|
4474
|
+
var GLOBAL_GET_SIZE = 2;
|
|
4038
4475
|
var globals = (ast) => {
|
|
4039
4476
|
if (!Array.isArray(ast) || ast[0] !== "module") return ast;
|
|
4040
4477
|
const constGlobals = /* @__PURE__ */ new Map();
|
|
4041
|
-
const
|
|
4478
|
+
const exported = /* @__PURE__ */ new Set();
|
|
4042
4479
|
for (const node of ast.slice(1)) {
|
|
4043
|
-
if (!Array.isArray(node)
|
|
4480
|
+
if (!Array.isArray(node)) continue;
|
|
4481
|
+
if (node[0] === "export" && Array.isArray(node[2]) && node[2][0] === "global" && typeof node[2][1] === "string") {
|
|
4482
|
+
exported.add(node[2][1]);
|
|
4483
|
+
continue;
|
|
4484
|
+
}
|
|
4485
|
+
if (node[0] !== "global") continue;
|
|
4044
4486
|
const name2 = typeof node[1] === "string" && node[1][0] === "$" ? node[1] : null;
|
|
4045
4487
|
if (!name2) continue;
|
|
4046
|
-
|
|
4047
|
-
const
|
|
4048
|
-
const typeSlot = hasName ? node[2] : node[1];
|
|
4488
|
+
if (node.some((c) => Array.isArray(c) && c[0] === "export")) exported.add(name2);
|
|
4489
|
+
const typeSlot = node[2];
|
|
4049
4490
|
if (Array.isArray(typeSlot) && typeSlot[0] === "mut") continue;
|
|
4050
|
-
|
|
4491
|
+
if (Array.isArray(typeSlot) && typeSlot[0] === "import") continue;
|
|
4492
|
+
const init = node[3];
|
|
4051
4493
|
if (getConst(init)) constGlobals.set(name2, init);
|
|
4052
4494
|
}
|
|
4495
|
+
if (constGlobals.size === 0) return ast;
|
|
4496
|
+
const reads = /* @__PURE__ */ new Map();
|
|
4053
4497
|
walk2(ast, (n) => {
|
|
4054
|
-
if (!Array.isArray(n)
|
|
4498
|
+
if (!Array.isArray(n)) return;
|
|
4055
4499
|
const ref = n[1];
|
|
4056
|
-
if (typeof ref
|
|
4500
|
+
if (typeof ref !== "string" || ref[0] !== "$") return;
|
|
4501
|
+
if (n[0] === "global.set") constGlobals.delete(ref);
|
|
4502
|
+
else if (n[0] === "global.get") reads.set(ref, (reads.get(ref) || 0) + 1);
|
|
4057
4503
|
});
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4504
|
+
const propagate2 = /* @__PURE__ */ new Set();
|
|
4505
|
+
for (const [name2, init] of constGlobals) {
|
|
4506
|
+
const r = reads.get(name2) || 0;
|
|
4507
|
+
if (r === 0) continue;
|
|
4508
|
+
const cs = constInstrSize(init);
|
|
4509
|
+
const declSize = cs + 2;
|
|
4510
|
+
const before = r * GLOBAL_GET_SIZE + declSize;
|
|
4511
|
+
const after = r * cs + (exported.has(name2) ? declSize : 0);
|
|
4512
|
+
if (after <= before) propagate2.add(name2);
|
|
4513
|
+
}
|
|
4514
|
+
if (propagate2.size === 0) return ast;
|
|
4515
|
+
walkPost2(ast, (node) => {
|
|
4061
4516
|
if (!Array.isArray(node) || node[0] !== "global.get" || node.length !== 2) return;
|
|
4062
|
-
|
|
4063
|
-
if (constGlobals.has(ref)) return clone2(constGlobals.get(ref));
|
|
4517
|
+
if (propagate2.has(node[1])) return clone2(constGlobals.get(node[1]));
|
|
4064
4518
|
});
|
|
4519
|
+
for (let i = ast.length - 1; i >= 1; i--) {
|
|
4520
|
+
const n = ast[i];
|
|
4521
|
+
if (Array.isArray(n) && n[0] === "global" && typeof n[1] === "string" && propagate2.has(n[1]) && !exported.has(n[1])) ast.splice(i, 1);
|
|
4522
|
+
}
|
|
4523
|
+
return ast;
|
|
4065
4524
|
};
|
|
4066
4525
|
var offset = (ast) => {
|
|
4067
4526
|
return walkPost2(ast, (node) => {
|
|
@@ -4141,8 +4600,65 @@ var unbranch = (ast) => {
|
|
|
4141
4600
|
if (lastIdx < 0) return;
|
|
4142
4601
|
const last = node[lastIdx];
|
|
4143
4602
|
if (Array.isArray(last) && last[0] === "br" && last[1] === label) {
|
|
4144
|
-
node.splice(lastIdx, 1);
|
|
4603
|
+
node.splice(lastIdx, 1, ...last.slice(2));
|
|
4604
|
+
}
|
|
4605
|
+
});
|
|
4606
|
+
return ast;
|
|
4607
|
+
};
|
|
4608
|
+
var loopify = (ast) => {
|
|
4609
|
+
walk2(ast, (node) => {
|
|
4610
|
+
if (!Array.isArray(node) || node[0] !== "block") return;
|
|
4611
|
+
let bi = 1, label = null;
|
|
4612
|
+
if (typeof node[1] === "string" && node[1][0] === "$") {
|
|
4613
|
+
label = node[1];
|
|
4614
|
+
bi = 2;
|
|
4615
|
+
}
|
|
4616
|
+
if (!label) return;
|
|
4617
|
+
while (bi < node.length) {
|
|
4618
|
+
const c = node[bi];
|
|
4619
|
+
if (Array.isArray(c) && c[0] === "type") {
|
|
4620
|
+
bi++;
|
|
4621
|
+
continue;
|
|
4622
|
+
}
|
|
4623
|
+
if (Array.isArray(c) && (c[0] === "param" || c[0] === "result")) return;
|
|
4624
|
+
break;
|
|
4625
|
+
}
|
|
4626
|
+
if (node.length - bi !== 1) return;
|
|
4627
|
+
const loop = node[bi];
|
|
4628
|
+
if (!Array.isArray(loop) || loop[0] !== "loop") return;
|
|
4629
|
+
let li = 1, loopLabel = null;
|
|
4630
|
+
if (typeof loop[1] === "string" && loop[1][0] === "$") {
|
|
4631
|
+
loopLabel = loop[1];
|
|
4632
|
+
li = 2;
|
|
4633
|
+
}
|
|
4634
|
+
const loopHeader = [];
|
|
4635
|
+
while (li < loop.length) {
|
|
4636
|
+
const c = loop[li];
|
|
4637
|
+
if (Array.isArray(c) && c[0] === "type") {
|
|
4638
|
+
loopHeader.push(c);
|
|
4639
|
+
li++;
|
|
4640
|
+
continue;
|
|
4641
|
+
}
|
|
4642
|
+
if (Array.isArray(c) && (c[0] === "param" || c[0] === "result")) return;
|
|
4643
|
+
break;
|
|
4145
4644
|
}
|
|
4645
|
+
const body = loop.slice(li);
|
|
4646
|
+
if (body.length < 2) return;
|
|
4647
|
+
const head = body[0];
|
|
4648
|
+
const tail = body[body.length - 1];
|
|
4649
|
+
if (!Array.isArray(head) || head[0] !== "br_if" || head[1] !== label || head.length !== 3) return;
|
|
4650
|
+
if (!Array.isArray(tail) || tail[0] !== "br" || tail[1] !== loopLabel || tail.length !== 2) return;
|
|
4651
|
+
const inner = body.slice(1, -1);
|
|
4652
|
+
if (targetsLabel(inner, label)) return;
|
|
4653
|
+
let cond = head[2];
|
|
4654
|
+
if (Array.isArray(cond) && cond[0] === "i32.eqz" && cond.length === 2) cond = cond[1];
|
|
4655
|
+
else cond = ["i32.eqz", cond];
|
|
4656
|
+
const newLoop = ["loop"];
|
|
4657
|
+
if (loopLabel) newLoop.push(loopLabel);
|
|
4658
|
+
for (const h of loopHeader) newLoop.push(h);
|
|
4659
|
+
newLoop.push(["if", cond, ["then", ...inner, tail]]);
|
|
4660
|
+
node.length = 0;
|
|
4661
|
+
for (const tok of newLoop) node.push(tok);
|
|
4146
4662
|
});
|
|
4147
4663
|
return ast;
|
|
4148
4664
|
};
|
|
@@ -4599,7 +5115,7 @@ function optimize(ast, opts = true) {
|
|
|
4599
5115
|
let beforeRound = null;
|
|
4600
5116
|
for (let round = 0; round < 3; round++) {
|
|
4601
5117
|
beforeRound = clone2(ast);
|
|
4602
|
-
const sizeBefore =
|
|
5118
|
+
const sizeBefore = binarySize(ast);
|
|
4603
5119
|
if (opts.stripmut) ast = stripmut(ast);
|
|
4604
5120
|
if (opts.globals) ast = globals(ast);
|
|
4605
5121
|
if (opts.fold) ast = fold(ast);
|
|
@@ -4608,13 +5124,17 @@ function optimize(ast, opts = true) {
|
|
|
4608
5124
|
if (opts.strength) ast = strength(ast);
|
|
4609
5125
|
if (opts.branch) ast = branch(ast);
|
|
4610
5126
|
if (opts.propagate) ast = propagate(ast);
|
|
5127
|
+
if (opts.inlineOnce) ast = inlineOnce(ast);
|
|
4611
5128
|
if (opts.inline) ast = inline(ast);
|
|
4612
5129
|
if (opts.offset) ast = offset(ast);
|
|
4613
5130
|
if (opts.unbranch) ast = unbranch(ast);
|
|
5131
|
+
if (opts.loopify) ast = loopify(ast);
|
|
4614
5132
|
if (opts.brif) ast = brif(ast);
|
|
4615
5133
|
if (opts.foldarms) ast = foldarms(ast);
|
|
4616
5134
|
if (opts.deadcode) ast = deadcode(ast);
|
|
4617
5135
|
if (opts.vacuum) ast = vacuum(ast);
|
|
5136
|
+
if (opts.mergeBlocks) ast = mergeBlocks(ast);
|
|
5137
|
+
if (opts.coalesce) ast = coalesceLocals(ast);
|
|
4618
5138
|
if (opts.locals) ast = localReuse(ast);
|
|
4619
5139
|
if (opts.dedupe) ast = dedupe(ast);
|
|
4620
5140
|
if (opts.dedupTypes) ast = dedupTypes(ast);
|
|
@@ -4622,14 +5142,15 @@ function optimize(ast, opts = true) {
|
|
|
4622
5142
|
if (opts.reorder) ast = reorder(ast);
|
|
4623
5143
|
if (opts.treeshake) ast = treeshake(ast);
|
|
4624
5144
|
if (opts.minifyImports) ast = minifyImports(ast);
|
|
4625
|
-
|
|
5145
|
+
if (opts.propagate && (opts.inlineOnce || opts.inline)) ast = propagate(ast);
|
|
5146
|
+
const sizeAfter = binarySize(ast);
|
|
4626
5147
|
const delta = sizeAfter - sizeBefore;
|
|
4627
5148
|
if (verbose || delta !== 0) {
|
|
4628
|
-
log(` round ${round + 1}: ${delta > 0 ? "+" : ""}${delta}
|
|
5149
|
+
log(` round ${round + 1}: ${delta > 0 ? "+" : ""}${delta} bytes`, delta);
|
|
4629
5150
|
}
|
|
4630
|
-
const tolerance = strictGuard ? 0 :
|
|
5151
|
+
const tolerance = strictGuard ? 0 : 16;
|
|
4631
5152
|
if (delta > tolerance) {
|
|
4632
|
-
if (verbose) log(` \u26A0 round ${round + 1} inflated by ${delta}, reverting`, delta);
|
|
5153
|
+
if (verbose) log(` \u26A0 round ${round + 1} inflated by ${delta} bytes, reverting`, delta);
|
|
4633
5154
|
ast = beforeRound;
|
|
4634
5155
|
break;
|
|
4635
5156
|
}
|