aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2011-11-23 18:23:52 -0800
committerAlon Zakai <alonzakai@gmail.com>2011-11-23 18:23:52 -0800
commit309659d7b214828841540dd14de217b2058ce96e (patch)
tree36dde00cef133c162b63070f4ac4d3587efa174f
parent8d030228a3a428f2450d4d77651d04347a1211e9 (diff)
replace compiler tricks for one-time loop removal and label removal with a proper pass in the js optimizer
-rw-r--r--src/analyzer.js31
-rw-r--r--src/jsifier.js12
-rw-r--r--src/utility.js4
-rw-r--r--tools/js-optimizer.js52
-rw-r--r--tools/test-js-optimizer-output.js4
5 files changed, 48 insertions, 55 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 7245a583..9e827cd1 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -1172,37 +1172,7 @@ function analyzer(data) {
replaceLabelLabels(block.labels, set('BJSET|*|' + block.willGetTo), 'BNOPP');
replaceLabelLabels(block.labels, set('BCONT|*|' + block.willGetTo), 'BNOPP');
replaceLabelLabels(block.labels, set('BREAK|*|' + block.willGetTo), 'BNOPP');
- } else if (block.type === 'multiple') {
- // Check if the one-time loop (that allows breaking out) is actually needed
- if (replaceLabelLabels(block.labels, set('BREAK|' + block.id + '|*')).length === 0) {
- block.loopless = true;
- }
- }
- }
-
- // Checks whether we actually need labels. We return whether we have a loop nested inside us.
- function optimizeOutUnneededLabels(block) {
- if (!block) return false;
-
- dprint('relooping', "// optimizing (2) block: " + block.type + ' : ' + block.entries);
-
- var containLoop = sum(recurseBlock(block, optimizeOutUnneededLabels)) > 0;
-
- if (block.type === 'emulated') {
- return containLoop;
- } else if (block.type === 'multiple') {
- // TODO: Apply the same optimization below for 'reloop', to looped multiples
- return containLoop || !block.loopless;
- } else if (block.type === 'reloop') {
- if (!containLoop) {
- block.needBlockId = false;
-
- replaceLabelLabels(block.labels, set('BCONT|' + block.id + '|*'), 'BCONT||*');
- replaceLabelLabels(block.labels, set('BREAK|' + block.id + '|*'), 'BREAK||*');
- }
- return true;
}
- return assert(false);
}
// TODO: Parallelize
@@ -1210,7 +1180,6 @@ function analyzer(data) {
dprint('relooping', "// loopOptimizing function: " + func.ident);
exploreBlockEndings(func.block);
optimizeBlockEndings(func.block);
- optimizeOutUnneededLabels(func.block);
});
return finish();
}
diff --git a/src/jsifier.js b/src/jsifier.js
index 49bac511..2869d059 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -495,16 +495,14 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
}
ret += '\n';
} else if (block.type == 'reloop') {
- ret += indent + (block.needBlockId ? block.id + ': ' : '') + 'while(1) { ' + (SHOW_LABELS ? ' /* ' + block.entries + + ' */' : '') + '\n';
+ ret += indent + block.id + ': while(1) { ' + (SHOW_LABELS ? ' /* ' + block.entries + + ' */' : '') + '\n';
ret += walkBlock(block.inner, indent + ' ');
ret += indent + '}\n';
} else if (block.type == 'multiple') {
var first = true;
var multipleIdent = '';
- if (!block.loopless) {
- ret += indent + (block.needBlockId ? block.id + ': ' : '') + 'do { \n';
- multipleIdent = ' ';
- }
+ ret += indent + block.id + ': do { \n';
+ multipleIdent = ' ';
// TODO: Find out cases where the final if/case is not needed - where we know we must be in a specific label at that point
var SWITCH_IN_MULTIPLE = 0; // This appears to never be worth it, for no amount of labels
if (SWITCH_IN_MULTIPLE && block.entryLabels.length >= 2) {
@@ -523,9 +521,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
first = false;
});
}
- if (!block.loopless) {
- ret += indent + '} while(0);\n';
- }
+ ret += indent + '} while(0);\n';
} else {
throw "Walked into an invalid block type: " + block.type;
}
diff --git a/src/utility.js b/src/utility.js
index ba97a399..f0439ea7 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -135,6 +135,10 @@ function sumStringy(x) {
return x.reduce(function(a,b) { return a+b }, '');
}
+function filterTruthy(x) {
+ return x.filter(function(y) { return !!y });
+}
+
function loopOn(array, func) {
for (var i = 0; i < array.length; i++) {
func(i, array[i]);
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index d1078471..869ca77b 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -46,6 +46,18 @@ function isGenerated(ident) {
return ident in generatedFunctions;
}
+function srcToAst(src) {
+ return uglify.parser.parse(src);
+}
+
+function astToSrc(ast) {
+ return uglify.uglify.gen_code(ast, {
+ ascii_only: true,
+ beautify: true,
+ indent_level: 2
+ });
+}
+
// Traverses a JavaScript syntax tree rooted at the given node calling the given
// callback for each node.
// @arg node: The root of the AST.
@@ -267,9 +279,9 @@ function simplifyNotComps(ast) {
}
function loopOptimizer(ast) {
- // We generate loops and one-time loops (do while(0)) with labels. It is often
- // possible to eliminate those labels.
- function loopLabelOptimizer(ast) {
+ // Remove unneeded labels and one-time (do while(0)) loops. It is convenient to do these both at once.
+ function passTwo(ast) {
+ var neededDos = [];
// Find unneeded labels
traverseGenerated(ast, function(node, type, stack) {
if (type == 'label' && node[2][0] in LOOP) {
@@ -294,6 +306,8 @@ function loopOptimizer(ast) {
var ident = node[1];
var plus = '+' + ident;
if (lastLabel && (ident == lastLabel[1] || plus == lastLabel[1])) {
+ // If this is a 'do' loop, this break means we actually need it.
+ neededDos.push(lastLoop);
// We don't need the control flow command to have a label - it's referring to the current loop
return [node[0]];
} else {
@@ -305,7 +319,7 @@ function loopOptimizer(ast) {
}
}
}, null, []);
- // Remove those labels
+ // Remove unneeded labels
traverseGenerated(ast, function(node, type) {
if (type == 'label' && node[1][0] == '+') {
var ident = node[1].substr(1);
@@ -318,13 +332,27 @@ function loopOptimizer(ast) {
return node[2]; // Remove the label itself on the loop
}
});
+ // Remove unneeded one-time loops. We need such loops if (1) they have a label, or (2) they have a direct break so they are in neededDos.
+ // First, add all labeled loops of this nature to neededDos
+ traverseGenerated(ast, function(node, type) {
+ if (type == 'label' && node[2][0] == 'do') {
+ neededDos.push(node[2]);
+ }
+ });
+ // Remove unneeded dos, we know who they are now
+ traverseGenerated(ast, function(node, type) {
+ if (type == 'do' && neededDos.indexOf(node) < 0) {
+ assert(jsonCompare(node[1], ['num', 0]), 'Trying to remove a one-time do loop that is not one of our generated ones.;');
+ return node[2];
+ }
+ });
}
- // Eliminate unneeded breaks and continues, when we would get to that place anyhow
- function loopMotionOptimizer(ast) {} // TODO
+ // Go
- loopLabelOptimizer(ast);
- loopMotionOptimizer(ast);
+ // TODO: pass 1: Removal of unneeded continues, breaks if they get us to where we are already going. That will
+ // help the next pass.
+ passTwo(ast);
}
// Passes table
@@ -340,7 +368,7 @@ var passes = {
// Main
var src = fs.readFileSync('/dev/stdin').toString();
-var ast = uglify.parser.parse(src);
+var ast = srcToAst(src);
//printErr(JSON.stringify(ast)); throw 1;
var metadata = src.split('\n').filter(function(line) { return line.indexOf('EMSCRIPTEN_GENERATED_FUNCTIONS') >= 0 })[0];
//assert(metadata, 'Must have EMSCRIPTEN_GENERATED_FUNCTIONS metadata');
@@ -359,10 +387,6 @@ arguments.forEach(function(arg) {
});
*/
-print(uglify.uglify.gen_code(ast, {
- ascii_only: true,
- beautify: true,
- indent_level: 2
-}));
+print(astToSrc(ast));
if (metadata) print(metadata + '\n');
diff --git a/tools/test-js-optimizer-output.js b/tools/test-js-optimizer-output.js
index 3148fb55..b165aea8 100644
--- a/tools/test-js-optimizer-output.js
+++ b/tools/test-js-optimizer-output.js
@@ -68,9 +68,9 @@ function loopy() {
something();
} while (0);
next();
- do {
+ {
something();
- } while (0);
+ }
}
function ignoreLoopy() {
b$for_cond$4 : while (1) {