diff options
Diffstat (limited to 'tools/js-optimizer.js')
| -rw-r--r-- | tools/js-optimizer.js | 109 | 
1 files changed, 77 insertions, 32 deletions
| diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index ff6aee67..9a5104bf 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -801,7 +801,10 @@ function simplifyExpressions(ast) {  //  HEAP[x >> 2]  // very often. We can in some cases do the shift on the variable itself when it is set,  // to greatly reduce the number of shift operations. -// TODO: when shifting a variable, if there are other uses, keep an unshifted version too, to prevent slowdowns? +// XXX this optimization is deprecated and currently invalid: does not handle overflows +//     or non-aligned (round numbers, x >> 2 is a multiple of 4). Both are ok to assume +//     for pointers (undefined behavior otherwise), but invalid in general, and we do +//     no sufficiently-well distinguish the cases.  function optimizeShiftsInternal(ast, conservative) {    var MAX_SHIFTS = 3;    traverseGeneratedFunctions(ast, function(fun) { @@ -1233,7 +1236,7 @@ function vacuum(ast) {          }        } break;        case 'label': { -        if (node[2][0] === 'toplevel' && (!node[2][1] || node[2][1].length === 0)) { +        if (node[2] && node[2][0] === 'toplevel' && (!node[2][1] || node[2][1].length === 0)) {            return emptyNode();          }        } break; @@ -3001,7 +3004,7 @@ function outline(ast) {    // Try to flatten out code as much as possible, to make outlining more feasible.    function flatten(func, asmData) { -    var minSize = sizeToOutline; +    var minSize = extraInfo.sizeToOutline/4;      var helperId = 0;      function getHelper() {        while (1) { @@ -3023,58 +3026,90 @@ function outline(ast) {            if (ignore.indexOf(node) >= 0) continue;            var type = node[0];            if (measureSize(node) >= minSize) { -            if (type === 'if' && node[3]) { +            if ((type === 'if' && node[3]) || type === 'switch') { +              var isIf = type === 'if';                var reps = [];                var helper = getHelper();                // clear helper                reps.push(['stat', ['assign', true, ['name', helper], ['num', 1]]]); // 1 means continue in ifs                // gather parts -              var parts = []; -              var curr = node; -              while (1) { -                parts.push({ condition: curr[1], body: curr[2] }); -                curr = curr[3]; -                if (!curr) break; -                if (curr[0] != 'if') { -                  parts.push({ condition: null, body: curr }); -                  break; +              var parts; +              if (isIf) { +                parts = []; +                var curr = node; +                while (1) { +                  parts.push({ condition: curr[1], body: curr[2] }); +                  curr = curr[3]; +                  if (!curr) break; +                  if (curr[0] != 'if') { +                    parts.push({ condition: null, body: curr }); +                    break; +                  }                  } +              } else { // switch +                var switchVar = getHelper(); // switch var could be an expression +                reps.push(['stat', ['assign', true, ['name', switchVar], node[1]]]); +                parts = node[2].map(function(case_) { +                  return { condition: case_[0], body: case_[1] }; +                });                }                // chunkify. Each chunk is a chain of if-elses, with the new overhead just on entry and exit                var chunks = [];                var currSize = 0;                var currChunk = []; +              var force = false; // when we hit a case X: that falls through, we force inclusion of everything until a full case                parts.forEach(function(part) {                  var size = (part.condition ? measureSize(part.condition) : 0) + measureSize(part.body) + 5; // add constant for overhead of extra code                  assert(size > 0); -                if (size + currSize >= minSize && currSize) { +                if (size + currSize >= minSize && currSize && !force) {                    chunks.push(currChunk);                    currChunk = [];                    currSize = 0;                  }                  currChunk.push(part);                  currSize += size; +                if (!isIf) { +                  var last = part.body; +                  last = last[stats.length-1]; +                  if (last && last[0] === 'block') last = last[1][last[1].length-1]; +                  if (last && last[0] === 'stat') last = last[1]; +                  force = !last || last[0] !== 'break'; +                }                });                assert(currSize);                chunks.push(currChunk);                // generate flattened code                chunks.forEach(function(chunk) {                  var pre = ['stat', ['assign', true, ['name', helper], ['num', 0]]]; -                var chain = null, tail = null; -                chunk.forEach(function(part) { -                  // add to chain -                  var contents = makeIf(part.condition || ['num', 1], part.body[1]); -                  if (chain) { -                    tail[3] = contents; -                  } else { -                    chain = contents; -                    ignore.push(contents); +                if (isIf) { +                  var chain = null, tail = null; +                  chunk.forEach(function(part) { +                    // add to chain +                    var contents = makeIf(part.condition || ['num', 1], part.body[1]); +                    if (chain) { +                      tail[3] = contents; +                    } else { +                      chain = contents; +                      ignore.push(contents); +                    } +                    tail = contents; +                  }); +                  // if none of the ifs were entered, in the final else note that we need to continue +                  tail[3] = ['block', [['stat', ['assign', true, ['name', helper], ['num', 1]]]]]; +                  reps.push(makeIf(['name', helper], [pre, chain])); +                } else { // switch +                  var hasDefault; +                  var s = makeSwitch(['binary', '|', ['name', switchVar], ['num', 0]], chunk.map(function(part) { +                    hasDefault = hasDefault || part.condition === null; +                    return [part.condition, part.body]; +                  })); +                  // if no default, add one where we note that we need to continue +                  if (!hasDefault) { +                    s[2].push([null, [['block', [['stat', ['assign', true, ['name', helper], ['num', 1]]]]]]]);                    } -                  tail = contents; -                }); -                // if none of the ifs were entered, in the final else note that we need to continue -                tail[3] = ['block', [['stat', ['assign', true, ['name', helper], ['num', 1]]]]]; -                reps.push(makeIf(['name', helper], [pre, chain])); +                  ignore.push(s); +                  reps.push(makeIf(['name', helper], [pre, s])); +                }                });                // replace code and update i                stats.splice.apply(stats, [i, 1].concat(reps)); @@ -3088,6 +3123,8 @@ function outline(ast) {      });    } +  var maxTotalOutlinings = Infinity; // debugging tool +    // Prepares information for spilling of local variables    function analyzeFunction(func, asmData) {      var stack = []; // list of variables, each gets 8 bytes @@ -3107,7 +3144,7 @@ function outline(ast) {      // The control variables are zeroed out when calling an outlined function, and after using      // the value after they return.      var size = measureSize(func); -    asmData.maxOutlinings = Math.round(3*size/extraInfo.sizeToOutline); +    asmData.maxOutlinings = Math.min(Math.round(3*size/extraInfo.sizeToOutline), maxTotalOutlinings);      asmData.intendedPieces = Math.ceil(size/extraInfo.sizeToOutline);      asmData.totalStackSize = stackSize + (stack.length + 2*asmData.maxOutlinings)*8;      asmData.controlStackPos = function(i) { return stackSize + (stack.length + i)*8 }; @@ -3437,6 +3474,7 @@ function outline(ast) {        asmData.splitCounter--;        return [];      } +    maxTotalOutlinings--;      for (var v in owned) {        if (v != 'sp') delete asmData.vars[v]; // parent does not need these anymore      } @@ -3475,6 +3513,9 @@ function outline(ast) {          }        }      } +    function done() { +      return asmData.splitCounter >= asmData.maxOutlinings || measureSize(func) <= extraInfo.sizeToOutline; +    }      while (1) {        i--;        calcMinIndex(); // TODO: optimize @@ -3530,7 +3571,7 @@ function outline(ast) {          if (ret.length > pre) {            // we outlined recursively, reset our state here            //printErr('successful outline in recursion ' + func[1] + ' due to recursive in level ' + level); -          if (measureSize(func) <= extraInfo.sizeToOutline) break; +          if (done()) break;            end = i-1;            sizeSeen = 0;            canRestart = true; @@ -3570,7 +3611,7 @@ function outline(ast) {          if (newFuncs.length) {            ret.push.apply(ret, newFuncs);          } -        if (measureSize(func) <= extraInfo.sizeToOutline) break; +        if (done()) break;          sizeSeen = 0;          end = i-1;          canRestart = true; @@ -3590,6 +3631,8 @@ function outline(ast) {    var funcs = ast[1]; +  var maxTotalFunctions = Infinity; // debugging tool +    var more = true;    while (more) {      more = false; @@ -3597,9 +3640,11 @@ function outline(ast) {      var newFuncs = [];      funcs.forEach(function(func) { +      vacuum(func); // clear out empty nodes that affect code size        var asmData = normalizeAsm(func);        var size = measureSize(func); -      if (size >= extraInfo.sizeToOutline) { +      if (size >= extraInfo.sizeToOutline && maxTotalFunctions > 0) { +        maxTotalFunctions--;          aggressiveVariableElimination(func, asmData);          flatten(func, asmData);          analyzeFunction(func, asmData); | 
