@markw65/monkeyc-optimizer 1.1.14 → 1.1.15
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/README.md +11 -0
- package/build/api.cjs +165 -146
- package/build/optimizer.cjs +154 -142
- package/build/sdk-util.cjs +7915 -7196
- package/build/src/optimizer-types.d.ts +1 -0
- package/build/src/readprg/bytecode.d.ts +7 -0
- package/build/src/readprg/dce.d.ts +1 -1
- package/build/src/readprg/opcodes.d.ts +3 -0
- package/build/src/readprg/sharing.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -726,3 +726,14 @@ Bug Fixes
|
|
|
726
726
|
- Remove stores to dead locals
|
|
727
727
|
- Remove side-effect free code that produces an unused result
|
|
728
728
|
- Optimize shift left by constant to multiply, since the bytecode is smaller (this seems to be a bug in the garmin tools; they consider shift left and shift right to have an 8-bit argument, but its always zero, and the simulator and devices treat it as a one byte shift instruction, followed by a one byte nop).
|
|
729
|
+
|
|
730
|
+
### 1.1.15
|
|
731
|
+
|
|
732
|
+
- Post build optimizer improvements
|
|
733
|
+
|
|
734
|
+
- Simplify LogicalExpressions. This generally saves 3 bytes per `&&` or `||`, and also makes them faster
|
|
735
|
+
- Adds a simple code sharing pass. If multiple code paths converge to the same point (or leave the function via return) and they end with the same sequence of bytecode, they're merged into one.
|
|
736
|
+
- Flips branch-true to branch-false or vice versa if the fall through block has multiple predecessors, and the target block has just one. This often leads to better control flow, reducing the number of "goto" bytecodes required.
|
|
737
|
+
|
|
738
|
+
- Source to Source Optimizer improvements
|
|
739
|
+
- Adds an `Iterate Optimizer` option that causes the optimizer to keep re-running until it finds nothing to remove. Defaults to false.
|
package/build/api.cjs
CHANGED
|
@@ -3234,33 +3234,6 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
3234
3234
|
return result;
|
|
3235
3235
|
};
|
|
3236
3236
|
const topLocals = () => state.localsStack[state.localsStack.length - 1];
|
|
3237
|
-
/*
|
|
3238
|
-
* Might this function be called from somewhere, including
|
|
3239
|
-
* callbacks from the api (eg getSettingsView, etc).
|
|
3240
|
-
*/
|
|
3241
|
-
const maybeCalled = (func) => {
|
|
3242
|
-
if (!func.body) {
|
|
3243
|
-
// this is an api.mir function. It can be called
|
|
3244
|
-
return true;
|
|
3245
|
-
}
|
|
3246
|
-
if (hasProperty(state.exposed, func.id.name))
|
|
3247
|
-
return true;
|
|
3248
|
-
if (func.attrs &&
|
|
3249
|
-
func.attrs.attributes &&
|
|
3250
|
-
func.attrs.attributes.elements.some((attr) => {
|
|
3251
|
-
if (attr.type !== "UnaryExpression")
|
|
3252
|
-
return false;
|
|
3253
|
-
if (attr.argument.type !== "Identifier")
|
|
3254
|
-
return false;
|
|
3255
|
-
return attr.argument.name === "test";
|
|
3256
|
-
})) {
|
|
3257
|
-
return true;
|
|
3258
|
-
}
|
|
3259
|
-
if (hasProperty(state.calledFunctions, func.id.name)) {
|
|
3260
|
-
return (state.calledFunctions[func.id.name].find((f) => f === func) !== null);
|
|
3261
|
-
}
|
|
3262
|
-
return false;
|
|
3263
|
-
};
|
|
3264
3237
|
/*
|
|
3265
3238
|
* Does elm (a class) have a maybeCalled function called name,
|
|
3266
3239
|
* anywhere in its superClass chain.
|
|
@@ -3270,7 +3243,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
3270
3243
|
elm.superClass.some((sc) => (hasProperty(sc.decls, name) &&
|
|
3271
3244
|
sc.decls[name].some((f) => isStateNode(f) &&
|
|
3272
3245
|
f.type === "FunctionDeclaration" &&
|
|
3273
|
-
maybeCalled(f.node))) ||
|
|
3246
|
+
maybeCalled(state, f.node))) ||
|
|
3274
3247
|
(sc.superClass && checkInherited(sc, name))));
|
|
3275
3248
|
const renamer = (idnode) => {
|
|
3276
3249
|
const ident = idnode.type === "Identifier" ? idnode : idnode.left;
|
|
@@ -3471,7 +3444,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
3471
3444
|
}
|
|
3472
3445
|
istate = is;
|
|
3473
3446
|
}
|
|
3474
|
-
if (parent.type === "ClassDeclaration" && !maybeCalled(node)) {
|
|
3447
|
+
if (parent.type === "ClassDeclaration" && !maybeCalled(state, node)) {
|
|
3475
3448
|
let used = false;
|
|
3476
3449
|
if (node.id.name === "initialize") {
|
|
3477
3450
|
used = true;
|
|
@@ -3673,15 +3646,31 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
3673
3646
|
Object.values(fnMap).forEach((f) => {
|
|
3674
3647
|
collectNamespaces(f.ast, state);
|
|
3675
3648
|
});
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3649
|
+
const cleanupAll = () => Object.values(fnMap).reduce((changes, f) => {
|
|
3650
|
+
traverseAst(f.ast, undefined, (node) => {
|
|
3651
|
+
const ret = cleanup(state, node, f.ast);
|
|
3652
|
+
if (ret === false) {
|
|
3653
|
+
changes = true;
|
|
3654
|
+
state.removeNodeComments(node, f.ast);
|
|
3655
|
+
}
|
|
3656
|
+
else if (ret) {
|
|
3657
|
+
changes = true;
|
|
3658
|
+
}
|
|
3659
|
+
return ret;
|
|
3660
|
+
});
|
|
3661
|
+
return changes;
|
|
3662
|
+
}, false);
|
|
3663
|
+
do {
|
|
3664
|
+
state.usedByName = {};
|
|
3665
|
+
state.calledFunctions = {};
|
|
3666
|
+
state.exposed = state.nextExposed;
|
|
3667
|
+
state.nextExposed = {};
|
|
3668
|
+
Object.values(fnMap).forEach((f) => {
|
|
3669
|
+
collectNamespaces(f.ast, state);
|
|
3670
|
+
});
|
|
3671
|
+
state.exposed = state.nextExposed;
|
|
3672
|
+
state.nextExposed = {};
|
|
3673
|
+
} while (cleanupAll() && state.config?.iterateOptimizer);
|
|
3685
3674
|
delete state.pre;
|
|
3686
3675
|
delete state.post;
|
|
3687
3676
|
if (state.config?.minimizeModules ?? true) {
|
|
@@ -3690,108 +3679,7 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
3690
3679
|
});
|
|
3691
3680
|
}
|
|
3692
3681
|
Object.values(state.allFunctions).forEach((fns) => fns.forEach((fn) => sizeBasedPRE(state, fn)));
|
|
3693
|
-
|
|
3694
|
-
switch (node.type) {
|
|
3695
|
-
case "ThisExpression":
|
|
3696
|
-
node.text = "self";
|
|
3697
|
-
break;
|
|
3698
|
-
case "EnumStringBody":
|
|
3699
|
-
if (node.members.every((m) => {
|
|
3700
|
-
const name = "name" in m ? m.name : m.id.name;
|
|
3701
|
-
return (hasProperty(state.index, name) &&
|
|
3702
|
-
!hasProperty(state.exposed, name) &&
|
|
3703
|
-
!hasProperty(state.usedByName, name));
|
|
3704
|
-
})) {
|
|
3705
|
-
node.enumType = [
|
|
3706
|
-
...new Set(node.members.map((m) => {
|
|
3707
|
-
if (!("init" in m))
|
|
3708
|
-
return "Number";
|
|
3709
|
-
const [node, type] = getNodeValue(m.init);
|
|
3710
|
-
if (!node) {
|
|
3711
|
-
throw new Error("Failed to get type for eliminated enum");
|
|
3712
|
-
}
|
|
3713
|
-
return type;
|
|
3714
|
-
})),
|
|
3715
|
-
].join(" or ");
|
|
3716
|
-
node.members.splice(0);
|
|
3717
|
-
}
|
|
3718
|
-
break;
|
|
3719
|
-
case "EnumDeclaration":
|
|
3720
|
-
if (!node.body.members.length) {
|
|
3721
|
-
if (!node.id)
|
|
3722
|
-
return false;
|
|
3723
|
-
if (!node.body.enumType) {
|
|
3724
|
-
throw new Error("Missing enumType on optimized enum");
|
|
3725
|
-
}
|
|
3726
|
-
return {
|
|
3727
|
-
type: "TypedefDeclaration",
|
|
3728
|
-
id: node.id,
|
|
3729
|
-
ts: {
|
|
3730
|
-
type: "UnaryExpression",
|
|
3731
|
-
argument: {
|
|
3732
|
-
type: "TypeSpecList",
|
|
3733
|
-
ts: [
|
|
3734
|
-
node.body.enumType,
|
|
3735
|
-
],
|
|
3736
|
-
},
|
|
3737
|
-
prefix: true,
|
|
3738
|
-
operator: " as",
|
|
3739
|
-
},
|
|
3740
|
-
};
|
|
3741
|
-
}
|
|
3742
|
-
break;
|
|
3743
|
-
case "VariableDeclaration": {
|
|
3744
|
-
node.declarations = node.declarations.filter((d) => {
|
|
3745
|
-
const name = variableDeclarationName(d.id);
|
|
3746
|
-
return (!hasProperty(state.index, name) ||
|
|
3747
|
-
hasProperty(state.exposed, name) ||
|
|
3748
|
-
hasProperty(state.usedByName, name));
|
|
3749
|
-
});
|
|
3750
|
-
if (!node.declarations.length) {
|
|
3751
|
-
return false;
|
|
3752
|
-
}
|
|
3753
|
-
break;
|
|
3754
|
-
}
|
|
3755
|
-
case "ClassElement":
|
|
3756
|
-
if (!node.item) {
|
|
3757
|
-
return false;
|
|
3758
|
-
}
|
|
3759
|
-
break;
|
|
3760
|
-
case "FunctionDeclaration":
|
|
3761
|
-
if (!maybeCalled(node)) {
|
|
3762
|
-
if (node.attrs &&
|
|
3763
|
-
node.attrs.attributes &&
|
|
3764
|
-
node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" && attr.argument.name === "keep")) {
|
|
3765
|
-
break;
|
|
3766
|
-
}
|
|
3767
|
-
return false;
|
|
3768
|
-
}
|
|
3769
|
-
break;
|
|
3770
|
-
case "ClassDeclaration":
|
|
3771
|
-
case "ModuleDeclaration":
|
|
3772
|
-
// none of the attributes means anything on classes and
|
|
3773
|
-
// modules, and the new compiler complains about some
|
|
3774
|
-
// of them. Just drop them all.
|
|
3775
|
-
if (node.attrs && node.attrs.access) {
|
|
3776
|
-
if (node.attrs.attributes) {
|
|
3777
|
-
delete node.attrs.access;
|
|
3778
|
-
}
|
|
3779
|
-
else {
|
|
3780
|
-
delete node.attrs;
|
|
3781
|
-
}
|
|
3782
|
-
}
|
|
3783
|
-
}
|
|
3784
|
-
return null;
|
|
3785
|
-
};
|
|
3786
|
-
Object.entries(fnMap).forEach(([, f]) => {
|
|
3787
|
-
traverseAst(f.ast, undefined, (node) => {
|
|
3788
|
-
const ret = cleanup(node);
|
|
3789
|
-
if (ret === false) {
|
|
3790
|
-
state.removeNodeComments(node, f.ast);
|
|
3791
|
-
}
|
|
3792
|
-
return ret;
|
|
3793
|
-
});
|
|
3794
|
-
});
|
|
3682
|
+
cleanupAll();
|
|
3795
3683
|
config.checkCompilerLookupRules = checkLookupRules;
|
|
3796
3684
|
reportMissingSymbols(state, config);
|
|
3797
3685
|
if (state.inlineDiagnostics) {
|
|
@@ -3817,6 +3705,129 @@ async function optimizeMonkeyC(fnMap, resourcesMap, manifestXML, config) {
|
|
|
3817
3705
|
});
|
|
3818
3706
|
return state.diagnostics;
|
|
3819
3707
|
}
|
|
3708
|
+
/*
|
|
3709
|
+
* Might this function be called from somewhere, including
|
|
3710
|
+
* callbacks from the api (eg getSettingsView, etc).
|
|
3711
|
+
*/
|
|
3712
|
+
function maybeCalled(state, func) {
|
|
3713
|
+
if (!func.body) {
|
|
3714
|
+
// this is an api.mir function. It can be called
|
|
3715
|
+
return true;
|
|
3716
|
+
}
|
|
3717
|
+
if (hasProperty(state.exposed, func.id.name))
|
|
3718
|
+
return true;
|
|
3719
|
+
if (func.attrs &&
|
|
3720
|
+
func.attrs.attributes &&
|
|
3721
|
+
func.attrs.attributes.elements.some((attr) => {
|
|
3722
|
+
if (attr.type !== "UnaryExpression")
|
|
3723
|
+
return false;
|
|
3724
|
+
if (attr.argument.type !== "Identifier")
|
|
3725
|
+
return false;
|
|
3726
|
+
return attr.argument.name === "test";
|
|
3727
|
+
})) {
|
|
3728
|
+
return true;
|
|
3729
|
+
}
|
|
3730
|
+
if (hasProperty(state.calledFunctions, func.id.name)) {
|
|
3731
|
+
return state.calledFunctions[func.id.name].find((f) => f === func) !== null;
|
|
3732
|
+
}
|
|
3733
|
+
return false;
|
|
3734
|
+
}
|
|
3735
|
+
function cleanup(state, node, ast) {
|
|
3736
|
+
switch (node.type) {
|
|
3737
|
+
case "ThisExpression":
|
|
3738
|
+
node.text = "self";
|
|
3739
|
+
break;
|
|
3740
|
+
case "EnumStringBody":
|
|
3741
|
+
if (node.members.every((m) => {
|
|
3742
|
+
const name = "name" in m ? m.name : m.id.name;
|
|
3743
|
+
return (hasProperty(state.index, name) &&
|
|
3744
|
+
!hasProperty(state.exposed, name) &&
|
|
3745
|
+
!hasProperty(state.usedByName, name));
|
|
3746
|
+
})) {
|
|
3747
|
+
node.enumType = [
|
|
3748
|
+
...new Set(node.members.map((m) => {
|
|
3749
|
+
if (!("init" in m))
|
|
3750
|
+
return "Number";
|
|
3751
|
+
const [node, type] = getNodeValue(m.init);
|
|
3752
|
+
if (!node) {
|
|
3753
|
+
throw new Error("Failed to get type for eliminated enum");
|
|
3754
|
+
}
|
|
3755
|
+
return type;
|
|
3756
|
+
})),
|
|
3757
|
+
].join(" or ");
|
|
3758
|
+
node.members.splice(0);
|
|
3759
|
+
}
|
|
3760
|
+
break;
|
|
3761
|
+
case "EnumDeclaration":
|
|
3762
|
+
if (!node.body.members.length) {
|
|
3763
|
+
if (!node.id)
|
|
3764
|
+
return false;
|
|
3765
|
+
if (!node.body.enumType) {
|
|
3766
|
+
throw new Error("Missing enumType on optimized enum");
|
|
3767
|
+
}
|
|
3768
|
+
state.removeNodeComments(node, ast);
|
|
3769
|
+
return withLocDeep({
|
|
3770
|
+
type: "TypedefDeclaration",
|
|
3771
|
+
id: node.id,
|
|
3772
|
+
ts: {
|
|
3773
|
+
type: "UnaryExpression",
|
|
3774
|
+
argument: {
|
|
3775
|
+
type: "TypeSpecList",
|
|
3776
|
+
ts: [
|
|
3777
|
+
node.body.enumType,
|
|
3778
|
+
],
|
|
3779
|
+
},
|
|
3780
|
+
prefix: true,
|
|
3781
|
+
operator: " as",
|
|
3782
|
+
},
|
|
3783
|
+
}, node, node);
|
|
3784
|
+
}
|
|
3785
|
+
break;
|
|
3786
|
+
case "VariableDeclarator": {
|
|
3787
|
+
const name = variableDeclarationName(node.id);
|
|
3788
|
+
return !hasProperty(state.index, name) ||
|
|
3789
|
+
hasProperty(state.exposed, name) ||
|
|
3790
|
+
hasProperty(state.usedByName, name)
|
|
3791
|
+
? null
|
|
3792
|
+
: false;
|
|
3793
|
+
}
|
|
3794
|
+
case "VariableDeclaration": {
|
|
3795
|
+
if (!node.declarations.length) {
|
|
3796
|
+
return false;
|
|
3797
|
+
}
|
|
3798
|
+
break;
|
|
3799
|
+
}
|
|
3800
|
+
case "ClassElement":
|
|
3801
|
+
if (!node.item) {
|
|
3802
|
+
return false;
|
|
3803
|
+
}
|
|
3804
|
+
break;
|
|
3805
|
+
case "FunctionDeclaration":
|
|
3806
|
+
if (!maybeCalled(state, node)) {
|
|
3807
|
+
if (node.attrs &&
|
|
3808
|
+
node.attrs.attributes &&
|
|
3809
|
+
node.attrs.attributes.elements.some((attr) => attr.type === "UnaryExpression" && attr.argument.name === "keep")) {
|
|
3810
|
+
break;
|
|
3811
|
+
}
|
|
3812
|
+
return false;
|
|
3813
|
+
}
|
|
3814
|
+
break;
|
|
3815
|
+
case "ClassDeclaration":
|
|
3816
|
+
case "ModuleDeclaration":
|
|
3817
|
+
// none of the attributes means anything on classes and
|
|
3818
|
+
// modules, and the new compiler complains about some
|
|
3819
|
+
// of them. Just drop them all.
|
|
3820
|
+
if (node.attrs && node.attrs.access) {
|
|
3821
|
+
if (node.attrs.attributes) {
|
|
3822
|
+
delete node.attrs.access;
|
|
3823
|
+
}
|
|
3824
|
+
else {
|
|
3825
|
+
delete node.attrs;
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
return null;
|
|
3830
|
+
}
|
|
3820
3831
|
function optimizeCall(istate, node, context) {
|
|
3821
3832
|
const state = istate.state;
|
|
3822
3833
|
const [name, results] = state.lookupNonlocal(node.callee);
|
|
@@ -12356,6 +12367,14 @@ module.exports = require("./api.cjs");
|
|
|
12356
12367
|
|
|
12357
12368
|
/***/ }),
|
|
12358
12369
|
|
|
12370
|
+
/***/ 7168:
|
|
12371
|
+
/***/ ((module) => {
|
|
12372
|
+
|
|
12373
|
+
"use strict";
|
|
12374
|
+
module.exports = require("./sdk-util.cjs");
|
|
12375
|
+
|
|
12376
|
+
/***/ }),
|
|
12377
|
+
|
|
12359
12378
|
/***/ 6906:
|
|
12360
12379
|
/***/ ((module) => {
|
|
12361
12380
|
|
|
@@ -12515,8 +12534,8 @@ var negative_fixups = __webpack_require__(4499);
|
|
|
12515
12534
|
var optimizer_types = __webpack_require__(9697);
|
|
12516
12535
|
// EXTERNAL MODULE: external "./api.cjs"
|
|
12517
12536
|
var external_api_cjs_ = __webpack_require__(6817);
|
|
12518
|
-
|
|
12519
|
-
|
|
12537
|
+
// EXTERNAL MODULE: external "./sdk-util.cjs"
|
|
12538
|
+
var external_sdk_util_cjs_ = __webpack_require__(7168);
|
|
12520
12539
|
;// CONCATENATED MODULE: ./src/resources.ts
|
|
12521
12540
|
|
|
12522
12541
|
|
|
@@ -12568,7 +12587,7 @@ function visit_resources(elements, parent, v) {
|
|
|
12568
12587
|
error(e);
|
|
12569
12588
|
break;
|
|
12570
12589
|
}
|
|
12571
|
-
visit_resources(
|
|
12590
|
+
visit_resources(external_sdk_util_cjs_.xmlUtil.elementKids(e), e, visitor);
|
|
12572
12591
|
break;
|
|
12573
12592
|
// These are the resources themselves. Some can occur at top level; most
|
|
12574
12593
|
// are restricted to <resources> or one or more of the specific lists above
|
|
@@ -12713,7 +12732,7 @@ function add_resources_to_ast(state, ast, resources, manifestXML) {
|
|
|
12713
12732
|
rez.body.body.push(hiddenRez);
|
|
12714
12733
|
if (barrel === "" &&
|
|
12715
12734
|
manifestXML &&
|
|
12716
|
-
manifestXML.body instanceof
|
|
12735
|
+
manifestXML.body instanceof external_sdk_util_cjs_.xmlUtil.Nodes) {
|
|
12717
12736
|
manifestXML.body
|
|
12718
12737
|
.children("iq:application")
|
|
12719
12738
|
.elements.forEach((e) => add_one_resource(state, manifestXML, rez, e));
|
|
@@ -12856,7 +12875,7 @@ function visit_resource_refs(state, doc, e) {
|
|
|
12856
12875
|
return;
|
|
12857
12876
|
}
|
|
12858
12877
|
};
|
|
12859
|
-
|
|
12878
|
+
external_sdk_util_cjs_.xmlUtil.visit_xml([e], {
|
|
12860
12879
|
pre(node) {
|
|
12861
12880
|
if (node.type !== "element")
|
|
12862
12881
|
return false;
|
|
@@ -13301,7 +13320,7 @@ function checkCompilerVersion(version, sdkVer) {
|
|
|
13301
13320
|
async function getApiMapping(state, resourcesMap, manifestXML) {
|
|
13302
13321
|
// get the path to the currently active sdk
|
|
13303
13322
|
const parser = (prettier_plugin_monkeyc_default()).parsers.monkeyc;
|
|
13304
|
-
const sdk = await (0,
|
|
13323
|
+
const sdk = await (0,external_sdk_util_cjs_.getSdkPath)();
|
|
13305
13324
|
if (state) {
|
|
13306
13325
|
state.sdk = sdk;
|
|
13307
13326
|
const match = state.sdk?.match(/-(\d+\.\d+\.\d+)/);
|