aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tools/js-optimizer.js153
-rw-r--r--tools/test-js-optimizer-output.js63
-rw-r--r--tools/test-js-optimizer.js51
3 files changed, 196 insertions, 71 deletions
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index f9b2cc1c..ab509f14 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -667,36 +667,44 @@ function optimizeShiftsAggressive(ast) {
optimizeShiftsInternal(ast, false);
}
-function simplifyExpressionsPost(ast) {
- // We often have branchings that are simplified so one end vanishes, and
- // we then get
- // if (!(x < 5))
- // or such. Simplifying these saves space and time.
- function simplifyNotComps(ast) {
- traverse(ast, function(node, type) {
- if (type == 'unary-prefix' && node[1] == '!' && node[2][0] == 'binary') {
- if (node[2][1] == '<') {
- return ['binary', '>=', node[2][2], node[2][3]];
- } else if (node[2][1] == '>') {
- return ['binary', '<=', node[2][2], node[2][3]];
- } else if (node[2][1] == '==') {
- return ['binary', '!=', node[2][2], node[2][3]];
- } else if (node[2][1] == '!=') {
- return ['binary', '==', node[2][2], node[2][3]];
- } else if (node[2][1] == '===') {
- return ['binary', '!==', node[2][2], node[2][3]];
- } else if (node[2][1] == '!==') {
- return ['binary', '===', node[2][2], node[2][3]];
- }
+// We often have branchings that are simplified so one end vanishes, and
+// we then get
+// if (!(x < 5))
+// or such. Simplifying these saves space and time.
+function simplifyNotComps(ast) {
+ traverse(ast, function(node, type) {
+ if (type == 'unary-prefix' && node[1] == '!' && node[2][0] == 'binary') {
+ if (node[2][1] == '<') {
+ return ['binary', '>=', node[2][2], node[2][3]];
+ } else if (node[2][1] == '>') {
+ return ['binary', '<=', node[2][2], node[2][3]];
+ } else if (node[2][1] == '==') {
+ return ['binary', '!=', node[2][2], node[2][3]];
+ } else if (node[2][1] == '!=') {
+ return ['binary', '==', node[2][2], node[2][3]];
+ } else if (node[2][1] == '===') {
+ return ['binary', '!==', node[2][2], node[2][3]];
+ } else if (node[2][1] == '!==') {
+ return ['binary', '===', node[2][2], node[2][3]];
}
- });
- }
-
- // Go
+ }
+ });
+}
+function simplifyExpressionsPost(ast) {
simplifyNotComps(ast);
}
+function hasSideEffects(node) { // this is 99% incomplete and wrong! It just works on __label__ == X and number literals
+ if (node[0] == 'num') return false;
+ if (node[0] == 'binary' && (node[1] == '==' || node[1] == '!=') && node[2][0] == 'name' &&
+ node[3][0] == 'num') {
+ return false;
+ } else {
+ return true;
+ }
+}
+
// Clear out empty ifs and blocks, and redundant blocks/stats and so forth
function vacuum(ast) {
function isEmpty(node) {
@@ -729,18 +737,9 @@ function vacuum(ast) {
}
var type = node[0];
if (type == 'defun' && isGenerated(node[1])) {
+ simplifyNotComps(node);
traverse(node, function(node, type) {
- if (type == 'if') {
- if (((node[2][0] == 'block' && (!node[2][1] || node[2][1].length == 0)) ||
- jsonCompare(node[2], emptyNode())) && !node[3]) {
- more = true;
- return emptyNode();
- } else if (node[3] && isEmpty(node[3])) {
- more = true;
- node[3] = null;
- return node;
- }
- } else if (type == 'block' && node[1] && node[1].length == 1 && node[1][0][0] == 'block') {
+ if (type == 'block' && node[1] && node[1].length == 1 && node[1][0][0] == 'block') {
more = true;
return node[1][0];
} else if (type == 'stat' && node[1][0] == 'block') {
@@ -762,9 +761,25 @@ function vacuum(ast) {
} else if (type == 'label' && jsonCompare(node[2], emptyNode())) {
more = true;
return emptyNode();
- } else if (type == 'if' && isEmpty(node[3])) { // empty else clauses
- node[3] = null;
- return node;
+ } else if (type == 'if') {
+ var empty2 = isEmpty(node[2]), empty3 = isEmpty(node[3]), has3 = node.length == 4;
+ if (!empty2 && empty3 && has3) { // empty else clauses
+ more = true;
+ return node.slice(0, 3);
+ } else if (empty2 && !empty3) { // empty if blocks
+ more = true;
+ return ['if', ['unary-prefix', '!', node[1]], node[3]];
+ } else if (empty2 && empty3) {
+ more = true;
+ if (hasSideEffects(node[1])) {
+ return ['stat', node[1]];
+ } else {
+ return emptyNode();
+ }
+ }
+ } else if (type == 'do' && isEmpty(node[2]) && !hasSideEffects(node[1])) {
+ more = true;
+ return emptyNode();
}
});
}
@@ -772,6 +787,16 @@ function vacuum(ast) {
}
}
+function getStatements(node) {
+ if (node[0] == 'defun') {
+ return node[3];
+ } else if (node[0] == 'block') {
+ return node[1];
+ } else {
+ return null;
+ }
+}
+
// Multiple blocks from the relooper are, in general, implemented by
// if (__label__ == x) { } else if ..
// and branching into them by
@@ -781,12 +806,7 @@ function hoistMultiples(ast) {
ast[1].forEach(function(node, i) {
if (!(node[0] == 'defun' && isGenerated(node[1]))) return;
traverse(node, function(node, type) {
- var statements = null;
- if (type == 'defun') {
- statements = node[3];
- } else if (type == 'block') {
- statements = node[1];
- }
+ var statements = getStatements(node);
if (!statements) return;
var modified = false;
for (var i = 0; i < statements.length-1; i++) {
@@ -833,11 +853,11 @@ function hoistMultiples(ast) {
if (!found && preType == 'assign' && preNode[2][0] == 'name' && preNode[2][1] == '__label__') {
assert(preNode[3][0] == 'num');
if (preNode[3][1] == labelNum) {
- // That's it! Hoist away
+ // That's it! Hoist away. We can also throw away the __label__ setting as its goal has already been achieved
found = true;
modifiedI = true;
postInner[2] = ['block', []];
- return ['block', [preNode].concat(labelBlock[1])];
+ return labelBlock;
}
}
});
@@ -850,6 +870,45 @@ function hoistMultiples(ast) {
}
if (modified) return node;
});
+
+ // After hoisting in this function, it is safe to remove { __label__ = x; } blocks, because
+ // if they were leading to the next code right after them, they would be hoisted, and if they
+ // are going to some other place entirely, they would break or continue. The only risky
+ // situation is if the code after us is a multiple, in which case we might be checking for
+ // this label inside it (or in a later multiple, even)
+ function tryEliminate(node) {
+ if (node[0] == 'if') {
+ if (tryEliminate(node[2])) node[2] = emptyNode();
+ if (node[3] && tryEliminate(node[3])) node[3] = emptyNode();
+ } else {
+ if (node[0] == 'block' && node[1] && node[1].length == 1) {
+ var subNode = node[1][0];
+ if (subNode[0] == 'stat' && subNode[1][0] == 'assign' && subNode[1][2][0] == 'name' &&
+ subNode[1][2][1] == '__label__' && subNode[1][3][0] == 'num') {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ function getActualStatement(node) { // find the actual active statement, ignoring a label and one-time do loop
+ if (node[0] == 'label') node = node[2];
+ if (node[0] == 'do') node = node[2];
+ if (node[0] == 'block' && node[1].length == 1) node = node[1][0];
+ return node;
+ }
+ vacuum([0, [node]]);
+ traverse(node, function(node, type) {
+ var statements = getStatements(node);
+ if (!statements) return;
+ for (var i = 0; i < statements.length-1; i++) {
+ var curr = getActualStatement(statements[i]);
+ var next = statements[i+1];
+ if (curr[0] == 'if' && next[0] != 'if' && next[0] != 'label' && next[0] != 'do' && next[0] != 'while') {
+ tryEliminate(curr);
+ }
+ }
+ });
});
vacuum(ast);
diff --git a/tools/test-js-optimizer-output.js b/tools/test-js-optimizer-output.js
index 00a624c4..778684de 100644
--- a/tools/test-js-optimizer-output.js
+++ b/tools/test-js-optimizer-output.js
@@ -99,62 +99,80 @@ function maths() {
}
function hoisting() {
if ($i < $N) {
- __label__ = 2;
callOther();
}
pause(1);
$for_body3$$for_end$5 : do {
if ($i < $N) {
- __label__ = 2;
while (true) {
break $for_body3$$for_end$5;
}
callOther();
- } else {
- __label__ = 3;
}
} while (0);
pause(2);
do {
if ($i < $N) {
- __label__ = 2;
if (callOther()) break;
- } else {
- __label__ = 3;
}
} while (0);
pause(3);
if ($i < $N) {
- __label__ = 2;
callOther();
- } else {
- __label__ = 3;
}
pause(4);
if ($i < $N) {
- __label__ = 2;
callOther();
} else {
- __label__ = 3;
somethingElse();
}
pause(5);
if ($i < $N) {
__label__ = 2;
} else {
- __label__ = 3;
somethingElse();
}
if (__label__ == 55) {
callOther();
}
pause(6);
- if ($i < $N) {
- __label__ = 2;
- } else {
- __label__ = 3;
+ if ($i >= $N) {
somethingElse();
}
+ pause(7);
+ while (1) {
+ if ($i < $N) {
+ somethingElse();
+ } else {
+ __label__ = 3;
+ break;
+ }
+ if ($i < $N) {
+ somethingElse();
+ }
+ nothing();
+ }
+ pause(8);
+ var $cmp95 = $69 == -1;
+ do {
+ if ($cmp95) {
+ if (!$cmp103) {
+ __label__ = 38;
+ break;
+ }
+ if (!$cmp106) {
+ __label__ = 38;
+ break;
+ }
+ __label__ = 39;
+ break;
+ } else {
+ __label__ = 38;
+ }
+ } while (0);
+ if (__label__ == 38) {
+ var $79 = $_pr6;
+ }
}
var FS = {
absolutePath: (function(relative, base) {
@@ -184,7 +202,6 @@ function demangle($cmp) {
if ($cmp) {
__label__ = 3;
} else {
- __label__ = 1;
if (something()) {
__label__ = 3;
break;
@@ -200,10 +217,7 @@ function demangle($cmp) {
function lua() {
while (1) {
do {
- if ($14) {
- __label__ = 3;
- } else {
- __label__ = 4;
+ if (!$14) {
var $17 = $i;
var $18 = $3;
var $19 = $18 + ($17 << 2) | 0;
@@ -218,12 +232,15 @@ function lua() {
pause();
if ($1435 == 0) {
__label__ = 176;
- } else if ($1435 == 1) {} else {
+ cheez();
+ } else if ($1435 != 1) {
__label__ = 180;
+ cheez();
}
pause();
if ($1435 == 0) {
__label__ = 176;
+ cheez();
}
}
function moreLabels() {
diff --git a/tools/test-js-optimizer.js b/tools/test-js-optimizer.js
index 961cf491..0b7d7702 100644
--- a/tools/test-js-optimizer.js
+++ b/tools/test-js-optimizer.js
@@ -106,7 +106,6 @@ function hoisting() {
if (__label__ == 2) {
callOther();
}
-// ok /*
pause(1);
if ($i < $N) {
__label__ = 2;
@@ -170,6 +169,53 @@ function hoisting() {
if (__label__ == 3) {
somethingElse();
}
+ pause(7);
+ free: while (1) {
+ if ($i < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3; // this cannot be removed!
+ break;
+ }
+ if (__label__ == 2) {
+ somethingElse();
+ }
+ if ($i < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3; // this can be removed!
+ }
+ if (__label__ == 2) {
+ somethingElse();
+ }
+ nothing();
+ }
+ pause(8);
+ var $cmp95 = $69 == -1;
+ if ($cmp95) {
+ __label__ = 35;
+ } else {
+ __label__ = 38;
+ }
+ $if_then96$$if_end110thread_pre_split$48 : do {
+ if (__label__ == 35) {
+ if (!$cmp103) {
+ __label__ = 38;
+ break $if_then96$$if_end110thread_pre_split$48;
+ }
+ if (!$cmp106) {
+ __label__ = 38;
+ break $if_then96$$if_end110thread_pre_split$48;
+ }
+ __label__ = 39;
+ break $if_then96$$if_end110thread_pre_split$48;
+ }
+ } while (0);
+ $if_end110$$if_end110thread_pre_split$52 : do {
+ if (__label__ == 38) {
+ var $79 = $_pr6;
+ }
+ } while (0);
}
var FS = {
absolutePath: function(relative, base) { // Don't touch this!
@@ -243,12 +289,15 @@ function lua() {
pause();
if ($1435 == 0) {
__label__ = 176;
+ cheez();
} else if ($1435 == 1) {} else {
__label__ = 180;
+ cheez();
}
pause();
if ($1435 == 0) {
__label__ = 176;
+ cheez();
} else if ($1435 == 1) {}
}
function moreLabels() {