diff options
author | Alon Zakai <alonzakai@gmail.com> | 2011-11-23 18:23:52 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2011-11-23 18:23:52 -0800 |
commit | 309659d7b214828841540dd14de217b2058ce96e (patch) | |
tree | 36dde00cef133c162b63070f4ac4d3587efa174f | |
parent | 8d030228a3a428f2450d4d77651d04347a1211e9 (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.js | 31 | ||||
-rw-r--r-- | src/jsifier.js | 12 | ||||
-rw-r--r-- | src/utility.js | 4 | ||||
-rw-r--r-- | tools/js-optimizer.js | 52 | ||||
-rw-r--r-- | tools/test-js-optimizer-output.js | 4 |
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) { |