diff options
| author | Alon Zakai <alonzakai@gmail.com> | 2012-01-01 21:00:34 -0800 | 
|---|---|---|
| committer | Alon Zakai <alonzakai@gmail.com> | 2012-01-01 21:00:34 -0800 | 
| commit | a914110dac09b399d2022440f441a0847b7f745d (patch) | |
| tree | 5b94419002fad3ca288e6cf7c7a72a47cf89b568 /tools/js-optimizer.js | |
| parent | b724d8ba0b0d5c6c5950d9b0e4ae28e29d279e57 (diff) | |
remove __label__ settings in hoisted blocks and if we are sure the label setting is unimportant because the next code after us is not a check for the label
Diffstat (limited to 'tools/js-optimizer.js')
| -rw-r--r-- | tools/js-optimizer.js | 153 | 
1 files changed, 106 insertions, 47 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);  | 
