aboutsummaryrefslogtreecommitdiff
path: root/tools/js-optimizer.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/js-optimizer.js')
-rw-r--r--tools/js-optimizer.js39
1 files changed, 31 insertions, 8 deletions
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 03d7fe7f..d3121bc8 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -128,10 +128,8 @@ load('utility.js');
// Utilities
-var FUNCTION = set('defun', 'function');
var LOOP = set('do', 'while', 'for');
var LOOP_FLOW = set('break', 'continue');
-var ASSIGN_OR_ALTER = set('assign', 'unary-postfix', 'unary-prefix');
var CONTROL_FLOW = set('do', 'while', 'for', 'if', 'switch');
var NAME_OR_NUM = set('name', 'num');
var ASSOCIATIVE_BINARIES = set('+', '*', '|', '&', '^');
@@ -145,10 +143,7 @@ var COMMABLE = set('assign', 'binary', 'unary-prefix', 'unary-postfix', 'name',
var FUNCTIONS_THAT_ALWAYS_THROW = set('abort', '___resumeException', '___cxa_throw', '___cxa_rethrow');
-var NULL_NODE = ['name', 'null'];
var UNDEFINED_NODE = ['unary-prefix', 'void', ['num', 0]];
-var TRUE_NODE = ['unary-prefix', '!', ['num', 0]];
-var FALSE_NODE = ['unary-prefix', '!', ['num', 1]];
var GENERATED_FUNCTIONS_MARKER = '// EMSCRIPTEN_GENERATED_FUNCTIONS';
var generatedFunctions = false; // whether we have received only generated functions
@@ -5183,8 +5178,11 @@ function fixDotZero(js) {
}
function asmLastOpts(ast) {
+ var statsStack = [];
traverseGeneratedFunctions(ast, function(fun) {
traverse(fun, function(node, type) {
+ var stats = getStatements(node);
+ if (stats) statsStack.push(stats);
if (type === 'while' && node[1][0] === 'num' && node[1][1] === 1 && node[2][0] === 'block' && node[2].length == 2) {
// This is at the end of the pipeline, we can assume all other optimizations are done, and we modify loops
// into shapes that might confuse other passes
@@ -5192,15 +5190,28 @@ function asmLastOpts(ast) {
// while (1) { .. if (..) { break } } ==> do { .. } while(..)
var stats = node[2][1];
var last = stats[stats.length-1];
- if (last && last[0] === 'if' && !last[3] && last[2][0] === 'block' && last[2][1][0] && last[2][1][0][0] === 'break' && !last[2][1][0][1]) {
+ if (last && last[0] === 'if' && !last[3] && last[2][0] === 'block' && last[2][1][0]) {
+ var lastStats = last[2][1];
+ var lastNum = lastStats.length;
+ var lastLast = lastStats[lastNum-1];
+ if (!(lastLast[0] === 'break' && !lastLast[1])) return;// if not a simple break, dangerous
+ for (var i = 0; i < lastNum; i++) {
+ if (lastStats[i][0] !== 'stat' && lastStats[i][0] !== 'break') return; // something dangerous
+ }
+ // ok, a bunch of statements ending in a break
var abort = false;
var stack = 0;
+ var breaks = 0;
traverse(stats, function(node, type) {
- if (type == 'continue') {
- if (stack == 0 || node[1]) { // abort if labeled (we do not analyze labels here yet), or a continue directly on us
+ if (type === 'continue') {
+ if (stack === 0 || node[1]) { // abort if labeled (we do not analyze labels here yet), or a continue directly on us
abort = true;
return true;
}
+ } else if (type === 'break') {
+ if (stack === 0 || node[1]) { // relevant if labeled (we do not analyze labels here yet), or a break directly on us
+ breaks++;
+ }
} else if (type in LOOP) {
stack++;
}
@@ -5210,6 +5221,15 @@ function asmLastOpts(ast) {
}
});
if (abort) return;
+ assert(breaks > 0);
+ if (lastStats.length > 1 && breaks !== 1) return; // if we have code aside from the break, we can only move it out if there is just one break
+ // start to optimize
+ if (lastStats.length > 1) {
+ var parent = statsStack[statsStack.length-1];
+ var me = parent.indexOf(node);
+ if (me < 0) return; // not always directly on a stats, could be in a label for example
+ parent.splice.apply(parent, [me+1, 0].concat(lastStats.slice(0, lastStats.length-1)));
+ }
var conditionToBreak = last[1];
stats.pop();
node[0] = 'do';
@@ -5238,6 +5258,9 @@ function asmLastOpts(ast) {
}
}
}
+ }, function(node, type) {
+ var stats = getStatements(node);
+ if (stats) statsStack.pop();
});
});
}