diff options
-rw-r--r-- | tools/js-optimizer.js | 153 | ||||
-rw-r--r-- | tools/test-js-optimizer-output.js | 63 | ||||
-rw-r--r-- | tools/test-js-optimizer.js | 51 |
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() { |