diff options
author | Alon Zakai <alonzakai@gmail.com> | 2012-12-03 18:09:59 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2012-12-07 14:23:22 -0800 |
commit | bd9c3093f95947039d5e33aade6f5df1597ca9d5 (patch) | |
tree | 2dab60733c67ddb5751fde7722ee0d8872cd9f69 | |
parent | 61a8933a8df57a9bca285f8b14ab68fdf9da2c19 (diff) |
add normalize/denormalizeAsm to js optimizer, fix eliminator for asm
-rw-r--r-- | tools/eliminator/asm-eliminator-test-output.js | 9 | ||||
-rw-r--r-- | tools/js-optimizer.js | 126 |
2 files changed, 121 insertions, 14 deletions
diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js index 6b15a0cb..10b881a5 100644 --- a/tools/eliminator/asm-eliminator-test-output.js +++ b/tools/eliminator/asm-eliminator-test-output.js @@ -9,17 +9,14 @@ function __Z11printResultPiS_j($needle, $haystack, $len) { $needle = $needle | 0; $haystack = $haystack | 0; $len = $len | 0; - var $3 = 0; - var __stackBase__ = STACKTOP; + var $3 = 0, __stackBase__ = 0; $3 = _bsearch($needle, $haystack, $len, 4, 2); if (($3 | 0) == 0) { - $puts = _puts(_str | 0); + _puts(_str | 0); STACKTOP = __stackBase__; return; } else { - $7 = $3; - $8 = HEAP32[($7 & 16777215) >> 2] | 0; - $9 = _printf(__str1 | 0, (tempInt = STACKTOP, STACKTOP = STACKTOP + 4 | 0, HEAP32[(tempInt & 16777215) >> 2] = $8, tempInt)); + _printf(__str1 | 0, (tempInt = STACKTOP, STACKTOP = STACKTOP + 4 | 0, HEAP32[(tempInt & 16777215) >> 2] = HEAP32[($3 & 16777215) >> 2] | 0, tempInt)); STACKTOP = __stackBase__; return; } diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 1b9403d6..4f66c555 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -263,6 +263,10 @@ function emptyNode() { // XXX do we need to create new nodes here? can't we reus return ['toplevel', []] } +function isEmptyNode(node) { + return node.length == 2 && node[0] == 'toplevel' && node[1].length == 0; +} + // Passes // Dump the AST. Useful for debugging. For example, @@ -1197,6 +1201,94 @@ function loopOptimizer(ast) { vacuum(ast); } +// asm.js support code - normalize (convert asm.js code to 'normal' JS, without +// annotations, plus explicit metadata) and denormalize (vice versa) +var ASM_INT = 0; +var ASM_DOUBLE = 1; + +function detectAsmCoercion(node) { + // for params, +x vs x|0, for vars, +0 vs 0, so check for "+" + return node[0] == 'unary-prefix' ? ASM_DOUBLE : ASM_INT; +} + +function makeAsmParamCoercion(param, type) { + return type == ASM_INT ? ['binary', '|', ['name', param], ['num', 0]] : ['unary-prefix', '+', ['name', param]]; +} + +function makeAsmVarDef(v, type) { + return [v, type == ASM_INT ? ['num', 0] : ['unary-prefix', '+', ['num', 0]]]; +} + +function normalizeAsm(func) { + //printErr('pre-normalize \n\n' + astToSrc(func) + '\n\n'); + var data = { + params: {}, // ident => ASM_* type + vars: {}, // ident => ASM_* type + }; + // process initial params + var stats = func[3]; + var i = 0; + while (i < stats.length) { + var node = stats[i]; + if (node[0] != 'stat') break; + node = node[1]; + data.params[node[2][1]] = detectAsmCoercion(node[3]); + stats[i] = emptyNode(); + i++; + } + // process initial variable definitions + while (i < stats.length) { + var node = stats[i]; + if (node[0] != 'var') break; + node[1].forEach(function(v) { + data.vars[v[0]] = detectAsmCoercion(v[1]); + v[1] = null; // make an un-assigning var + }); + i++; + } + //printErr('normalized \n\n' + astToSrc(func) + '\n\n'); + return data; +} + +function denormalizeAsm(func, data) { + //printErr('pre-denormalize \n\n' + astToSrc(func) + '\n\n'); + var stats = func[3]; + // Remove var definitions, if any + for (var i = 0; i < stats.length; i++) { + if (stats[i][0] == 'var') { + stats[i] = emptyNode(); + } else { + if (!isEmptyNode(stats[i])) break; + } + } + // each param needs a line; reuse emptyNodes as much as we can + var numParams = 0; + for (var i in data.params) numParams++; + var emptyNodes = 0; + while (emptyNodes < stats.length) { + if (!isEmptyNode(stats[emptyNodes])) break; + emptyNodes++; + } + var neededEmptyNodes = numParams + 1; // params plus one big var + if (neededEmptyNodes > emptyNodes) { + var args = [0, 0]; + for (var i = 0; i < neededEmptyNodes - emptyNodes; i++) args[i+2] = 0; + stats.splice.apply(stats, args); + } + // add param coercions + var next = 0; + func[2].forEach(function(param) { + stats[next++] = ['stat', ['assign', true, ['name', param], makeAsmParamCoercion(param, data.params[param])]]; + }); + // add variable definitions + var varDefs = []; + for (var v in data.vars) { + varDefs.push(makeAsmVarDef(v, data.vars[v])); + } + stats[next] = ['var', varDefs]; + //printErr('denormalized \n\n' + astToSrc(func) + '\n\n'); +} + // Very simple 'registerization', coalescing of variables into a smaller number. // We do not optimize when there are switches, so this pass only makes sense with // relooping. @@ -1205,6 +1297,7 @@ function loopOptimizer(ast) { // closure simple? function registerize(ast, asm) { traverseGeneratedFunctions(ast, function(fun) { + if (asm) var asmData = normalizeAsm(fun); // Replace all var definitions with assignments; we will add var definitions at the top after we registerize // We also mark local variables - i.e., having a var definition var localVars = {}; @@ -1360,14 +1453,24 @@ function registerize(ast, asm) { } }); // Add vars at the beginning - if (nextReg > 1) { - var vars = []; + if (!asm) { + if (nextReg > 1) { + var vars = []; + for (var i = 1; i < nextReg; i++) { + vars.push([fullNames[i]]); + } + getStatements(fun).unshift(['var', vars]); + } + } else { + var finalAsmData = { + params: asmData.params, + vars: {}, + }; for (var i = 1; i < nextReg; i++) { - vars.push([fullNames[i]]); + finalAsmData.vars['r' + i] = ASM_FLOAT; // XXX split into register classes } - getStatements(fun).unshift(['var', vars]); + denormalizeAsm(fun, finalAsmData); } - //printErr(fun[1] + ': saved ' + saved + ' / ' + (saved + nextReg - 1) + ' vars through registerization'); // not totally accurate }); } @@ -1415,6 +1518,7 @@ var ABORTING_ELIMINATOR_SCAN_NODES = set('new', 'object', 'function', 'defun', ' function eliminate(ast, memSafe, asm) { // Find variables that have a single use, and if they can be eliminated, do so traverseGeneratedFunctions(ast, function(func, type) { + if (asm) var asmData = normalizeAsm(func); //printErr('eliminate in ' + func[1]); // First, find the potentially eliminatable functions: that have one definition and one use @@ -1430,7 +1534,6 @@ function eliminate(ast, memSafe, asm) { locals[func[2][i]] = true; } } - var defaultDefs = asm ? -1 : 0; // in asm.js we have an extra formal def which we can ignore // examine body and note locals traverse(func, function(node, type) { if (type === 'var') { @@ -1440,7 +1543,7 @@ function eliminate(ast, memSafe, asm) { var name = node1i[0]; var value = node1i[1]; if (value) { - if (!(name in definitions)) definitions[name] = defaultDefs; + if (!(name in definitions)) definitions[name] = 0; definitions[name]++; if (!values[name]) values[name] = value; } @@ -1455,7 +1558,7 @@ function eliminate(ast, memSafe, asm) { var target = node[2]; if (target[0] == 'name') { var name = target[1]; - if (!(name in definitions)) definitions[name] = defaultDefs; + if (!(name in definitions)) definitions[name] = 0; definitions[name]++; if (!uses[name]) uses[name] = 0; if (!values[name]) values[name] = node[3]; @@ -1827,6 +1930,13 @@ function eliminate(ast, memSafe, asm) { } } }); + + if (asm) { + for (var v in varsToRemove) { + delete asmData.vars[v]; + } + denormalizeAsm(func, asmData); + } }); // A class for optimizing expressions. We know that it is legitimate to collapse |