diff options
author | Alon Zakai <alonzakai@gmail.com> | 2013-06-08 09:14:22 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2013-06-08 09:55:41 -0700 |
commit | 430cfefc1612c00c481e5343b3be4ddc514fd415 (patch) | |
tree | b5eaa77c1923c00a400d1972a1509a610e541fe7 | |
parent | 0f382355e385e94f933f9962bf8b7703614b7c16 (diff) |
optimize away bitcasts to variables that will only be used in an assign to the parallel heap type
-rw-r--r-- | tools/js-optimizer.js | 127 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-pre-output.js | 12 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-pre.js | 14 |
3 files changed, 120 insertions, 33 deletions
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index ee056c33..c598fb81 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -405,7 +405,7 @@ function removeUnneededLabelSettings(ast) { function simplifyExpressionsPre(ast) { // Look for (x&A)<<B>>B and replace it with X&A if possible. function simplifySignExtends(ast) { - traverseGenerated(ast, function(node, type) { + traverse(ast, function(node, type) { if (type == 'binary' && node[1] == '>>' && node[3][0] == 'num' && node[2][0] == 'binary' && node[2][1] == '<<' && node[2][3][0] == 'num' && node[3][1] == node[2][3][1]) { var innerNode = node[2][2]; @@ -433,7 +433,7 @@ function simplifyExpressionsPre(ast) { var rerun = true; while (rerun) { rerun = false; - traverseGenerated(ast, function process(node, type, stack) { + traverse(ast, function process(node, type, stack) { if (type == 'binary' && node[1] == '|') { if (node[2][0] == 'num' && node[3][0] == 'num') { return ['num', node[2][1] | node[3][1]]; @@ -481,7 +481,7 @@ function simplifyExpressionsPre(ast) { var hasTempDoublePtr = false; - traverseGenerated(ast, function(node, type) { + traverse(ast, function(node, type) { if (type == 'name') { if (node[1] == 'tempDoublePtr') hasTempDoublePtr = true; } else if (type == 'binary' && node[1] == '&' && node[3][0] == 'num') { @@ -574,10 +574,71 @@ function simplifyExpressionsPre(ast) { } } }); + + // finally, wipe out remaining ones by finding cases where all assignments to X are bitcasts, and all uses are writes to + // the other heap type, then eliminate the bitcast + var bitcastVars = {}; + traverse(ast, function(node, type) { + if (type == 'assign' && node[1] === true && node[2][0] == 'name') { + var value = node[3]; + if (value[0] == 'seq' && value[1][0] == 'assign' && value[1][2][0] == 'sub' && value[1][2][1][0] == 'name' && + (value[1][2][1][1] == 'HEAP32' || value[1][2][1][1] == 'HEAPF32') && + value[1][2][2][0] == 'binary' && value[1][2][2][2][0] == 'name' && value[1][2][2][2][1] == 'tempDoublePtr') { + var name = node[2][1]; + if (!bitcastVars[name]) bitcastVars[name] = { + define_HEAP32: 0, define_HEAPF32: 0, use_HEAP32: 0, use_HEAPF32: 0, bad: false, namings: 0, defines: [], uses: [] + }; + bitcastVars[name]['define_' + value[1][2][1][1]]++; + bitcastVars[name].defines.push(node); + } + } + }); + traverse(ast, function(node, type) { + if (type == 'name' && bitcastVars[node[1]]) { + bitcastVars[node[1]].namings++; + } else if (type == 'assign' && node[1] === true) { + var value = node[3]; + if (value[0] == 'name') { + var name = value[1]; + if (bitcastVars[name]) { + var target = node[2]; + if (target[0] == 'sub' && target[1][0] == 'name' && (target[1][1] == 'HEAP32' || target[1][1] == 'HEAPF32')) { + bitcastVars[name]['use_' + target[1][1]]++; + bitcastVars[name].uses.push(node); + } + } + } + } + }); + var asmData = normalizeAsm(ast); + for (var v in bitcastVars) { + var info = bitcastVars[v]; + // good variables define only one type, use only one type, have definitions and uses, and define as a different type than they use + if (info.define_HEAP32*info.define_HEAPF32 == 0 && info.use_HEAP32*info.use_HEAPF32 == 0 && + info.define_HEAP32+info.define_HEAPF32 > 0 && info.use_HEAP32+info.use_HEAPF32 > 0 && + info.define_HEAP32*info.use_HEAP32 == 0 && info.define_HEAPF32*info.use_HEAPF32 == 0 && + v in asmData.vars && info.namings == info.define_HEAP32+info.define_HEAPF32+info.use_HEAP32+info.use_HEAPF32) { + var correct = info.use_HEAP32 ? 'HEAPF32' : 'HEAP32'; + info.defines.forEach(function(define) { + define[3] = define[3][1][3]; + if (correct == 'HEAP32') { + define[3] = ['binary', '|', define[3], ['num', 0]]; + } else { + define[3] = ['unary-prefix', '+', define[3]]; + } + // do we want a simplifybitops on the new values here? + }); + info.uses.forEach(function(use) { + use[2][1][1] = correct; + }); + asmData.vars[v] = 1 - asmData.vars[v]; + } + } + denormalizeAsm(ast, asmData); } // optimize num >> num, in asm we need this here since we do not run optimizeShifts - traverseGenerated(ast, function(node, type) { + traverse(ast, function(node, type) { if (type == 'binary' && node[1] == '>>' && node[2][0] == 'num' && node[3][0] == 'num') { node[0] = 'num'; node[1] = node[2][1] >> node[3][1]; @@ -593,7 +654,7 @@ function simplifyExpressionsPre(ast) { var rerun = true; while (rerun) { rerun = false; - traverseGenerated(ast, function(node, type) { + traverse(ast, function(node, type) { if (type == 'binary' && node[1] == '+') { if (node[2][0] == 'num' && node[3][0] == 'num') { rerun = true; @@ -616,7 +677,7 @@ function simplifyExpressionsPre(ast) { // if (x == 0) can be if (!x), etc. function simplifyZeroComp(ast) { - traverseGenerated(ast, function(node, type) { + traverse(ast, function(node, type) { var binary; if (type == 'if' && (binary = node[1])[0] == 'binary') { if ((binary[1] == '!=' || binary[1] == '!==') && binary[3][0] == 'num' && binary[3][1] == 0) { @@ -630,40 +691,40 @@ function simplifyExpressionsPre(ast) { }); } - function asmOpts(ast) { + function asmOpts(fun) { // 1. Add final returns when necessary // 2. Remove unneeded coercions on function calls that have no targets (eliminator removed it) - traverseGeneratedFunctions(ast, function(fun) { - var returnType = null; - traverse(fun, function(node, type) { - if (type == 'return' && node[1]) { - returnType = detectAsmCoercion(node[1]); - } else if (type == 'stat') { - var inner = node[1]; - if ((inner[0] == 'binary' && inner[1] in ASSOCIATIVE_BINARIES && inner[2][0] == 'call' && inner[3][0] == 'num') || - (inner[0] == 'unary-prefix' && inner[1] == '+' && inner[2][0] == 'call')) { - node[1] = inner[2]; - } - } - }); - // Add a final return if one is missing. - if (returnType !== null) { - var stats = getStatements(fun); - var last = stats[stats.length-1]; - if (last[0] != 'return') { - var returnValue = ['num', 0]; - if (returnType == ASM_DOUBLE) returnValue = ['unary-prefix', '+', returnValue]; - stats.push(['return', returnValue]); + var returnType = null; + traverse(fun, function(node, type) { + if (type == 'return' && node[1]) { + returnType = detectAsmCoercion(node[1]); + } else if (type == 'stat') { + var inner = node[1]; + if ((inner[0] == 'binary' && inner[1] in ASSOCIATIVE_BINARIES && inner[2][0] == 'call' && inner[3][0] == 'num') || + (inner[0] == 'unary-prefix' && inner[1] == '+' && inner[2][0] == 'call')) { + node[1] = inner[2]; } } }); + // Add a final return if one is missing. + if (returnType !== null) { + var stats = getStatements(fun); + var last = stats[stats.length-1]; + if (last[0] != 'return') { + var returnValue = ['num', 0]; + if (returnType == ASM_DOUBLE) returnValue = ['unary-prefix', '+', returnValue]; + stats.push(['return', returnValue]); + } + } } - simplifySignExtends(ast); - simplifyBitops(ast); - joinAdditions(ast); - // simplifyZeroComp(ast); TODO: investigate performance - if (asm) asmOpts(ast); + traverseGeneratedFunctions(ast, function(func) { + simplifySignExtends(func); + simplifyBitops(func); + joinAdditions(func); + // simplifyZeroComp(func); TODO: investigate performance + if (asm) asmOpts(func); + }); } // In typed arrays mode 2, we can have diff --git a/tools/test-js-optimizer-asm-pre-output.js b/tools/test-js-optimizer-asm-pre-output.js index ae1140b1..3257576e 100644 --- a/tools/test-js-optimizer-asm-pre-output.js +++ b/tools/test-js-optimizer-asm-pre-output.js @@ -112,11 +112,23 @@ function tempDoublePtr($45, $14, $28, $42) { $14 = $14 | 0; $28 = $28 | 0; $42 = $42 | 0; + var unelim = +0, bad = 0, unelim2 = +0; + unelim = +(127.5 * +$14); HEAPF32[$45 >> 2] = ($14 < $28 ? $14 : $28) - $42; HEAP32[$world + 102916 >> 2] = _malloc(192) | 0; f(+HEAPF32[$45 >> 2]); g(HEAP32[$14 >> 2] | 0); $42 = +HEAPF32[$42 >> 2]; ch($42); + HEAPF32[$45 >> 2] = unelim; + moar(); + bad = (HEAPF32[tempDoublePtr >> 2] = 127.5 * +$14, HEAP32[tempDoublePtr >> 2] | 0); + func(); + HEAP32[4] = bad; + HEAP32[5] = bad + 1; + moar(); + unelim2 = 127 + $14 | 0; + func(); + HEAP32[4] = unelim2; } diff --git a/tools/test-js-optimizer-asm-pre.js b/tools/test-js-optimizer-asm-pre.js index d28743c5..efa73218 100644 --- a/tools/test-js-optimizer-asm-pre.js +++ b/tools/test-js-optimizer-asm-pre.js @@ -114,11 +114,25 @@ function tempDoublePtr($45, $14, $28, $42) { $14 = $14 | 0; $28 = $28 | 0; $42 = $42 | 0; + var unelim = 0; // only used as assign to int heap, so can avoid bitcast in definition + var bad = 0; + var unelim2 = 0; // opposite types + unelim = (HEAPF32[tempDoublePtr >> 2] = 127.5 * +$14, HEAP32[tempDoublePtr >> 2] | 0); HEAP32[$45 >> 2] = 0 | (HEAPF32[tempDoublePtr >> 2] = ($14 < $28 ? $14 : $28) - $42, HEAP32[tempDoublePtr >> 2] | 0); HEAP32[$world + 102916 >> 2] = _malloc(192) | 0; f((HEAP32[tempDoublePtr >> 2] = HEAP32[$45 >> 2], +HEAPF32[tempDoublePtr >> 2])); g((HEAPF32[tempDoublePtr >> 2] = HEAPF32[$14 >> 2], HEAP32[tempDoublePtr >> 2] | 0)); $42 = (HEAP32[tempDoublePtr >> 2] = HEAP32[$42 >> 2] | 0, +HEAPF32[tempDoublePtr >> 2]); ch($42); + HEAP32[$45 >> 2] = unelim; + moar(); + bad = (HEAPF32[tempDoublePtr >> 2] = 127.5 * +$14, HEAP32[tempDoublePtr >> 2] | 0); + func(); + HEAP32[4] = bad; + HEAP32[5] = (bad + 1) | 0; + moar(); + unelim2 = (HEAP32[tempDoublePtr >> 2] = 127 + $14, +HEAPF32[tempDoublePtr >> 2]); + func(); + HEAPF32[4] = unelim2; } // EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "rett", "ret2t", "retf", "i32_8", "tempDoublePtr"] |