diff options
-rw-r--r-- | tests/test_other.py | 3 | ||||
-rw-r--r-- | tools/js-optimizer.js | 106 | ||||
-rw-r--r-- | tools/test-js-optimizer-si-output.js | 34 | ||||
-rw-r--r-- | tools/test-js-optimizer-si.js | 52 |
4 files changed, 164 insertions, 31 deletions
diff --git a/tests/test_other.py b/tests/test_other.py index 4a62384c..0c597e9f 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -2591,6 +2591,7 @@ int main() main = src[src.find('function _main'):src.find('\n}', src.find('function _main'))] actual_ifs = main.count('if (') assert ifs == actual_ifs, main + ' : ' + str([ifs, actual_ifs]) + #print main test(r''' #include <stdio.h> @@ -2617,5 +2618,5 @@ int main() } return 0; } - ''', [8, 6, 6]) + ''', [8, 5, 5]) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index e57bab0f..c38b51d5 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -838,38 +838,86 @@ function simplifyExpressions(ast) { function simplifyIfs(ast) { - traverse(ast, function(node, type) { - // simplify if (x) { if (y) { .. } } to if (x ? y : 0) { .. } - if (type === 'if') { - var body = node[2]; - // recurse to handle chains - while (body[0] === 'block') { - var stats = body[1]; - if (stats.length === 0) break; - var other = stats[stats.length-1]; - if (other[0] !== 'if') break; - // we can handle elses, but must be fully identical - if (!astCompare(node[3], other[3])) break; - if (stats.length > 1) { - // try to commaify - turn everything between the ifs into a comma operator inside the second if - var ok = true; - for (var i = 0; i < stats.length-1; i++) { - var curr = stats[i]; - if (curr[0] === 'stat') curr = curr[1]; - if (!(curr[0] in COMMABLE)) ok = false; - } - if (!ok) break; - for (var i = stats.length-2; i >= 0; i--) { - var curr = stats[i]; - if (curr[0] === 'stat') curr = curr[1]; - other[1] = ['seq', curr, other[1]]; + traverseGeneratedFunctions(ast, function(func) { + var simplifiedAnElse = false; + + traverse(func, function(node, type) { + // simplify if (x) { if (y) { .. } } to if (x ? y : 0) { .. } + if (type === 'if') { + var body = node[2]; + // recurse to handle chains + while (body[0] === 'block') { + var stats = body[1]; + if (stats.length === 0) break; + var other = stats[stats.length-1]; + if (other[0] !== 'if') break; + // we can handle elses, but must be fully identical + if (!astCompare(node[3], other[3])) break; + if (stats.length > 1) { + // try to commaify - turn everything between the ifs into a comma operator inside the second if + var ok = true; + for (var i = 0; i < stats.length-1; i++) { + var curr = stats[i]; + if (curr[0] === 'stat') curr = curr[1]; + if (!(curr[0] in COMMABLE)) ok = false; + } + if (!ok) break; + for (var i = stats.length-2; i >= 0; i--) { + var curr = stats[i]; + if (curr[0] === 'stat') curr = curr[1]; + other[1] = ['seq', curr, other[1]]; + } + stats = body[1] = [other]; } - stats = body[1] = [other]; + if (stats.length !== 1) break; + if (node[3]) simplifiedAnElse = true; + node[1] = ['conditional', node[1], other[1], ['num', 0]]; + body = node[2] = other[2]; } - if (stats.length !== 1) break; - node[1] = ['conditional', node[1], other[1], ['num', 0]]; - body = node[2] = other[2]; } + }); + + if (simplifiedAnElse) { + // there may be fusing opportunities + traverse(func, function(node, type) { + var stats = getStatements(node); + if (stats) { + for (var i = 0; i < stats.length-1; i++) { + var pre = stats[i]; + var post = stats[i+1]; + if (pre[0] === 'if' && pre[3] && post[0] === 'if' && !post[3]) { + var postCond = post[1]; + if (postCond[0] === 'binary' && postCond[1] === '==' && + postCond[2][0] === 'binary' && postCond[2][1] === '|' && + postCond[2][2][0] === 'name' && postCond[2][2][1] === 'label' && + postCond[2][3][0] === 'num' && postCond[2][3][1] === 0 && + postCond[3][0] === 'num') { + var postValue = postCond[3][1]; + var preElse = pre[3]; + if (preElse[0] === 'block' && preElse[1] && preElse[1].length === 1) { + var preStat = preElse[1][0]; + if (preStat[0] === 'stat' && preStat[1][0] === 'assign' && + preStat[1][1] === true && preStat[1][2][0] === 'name' && preStat[1][2][1] === 'label' && + preStat[1][3][0] === 'num' && preStat[1][3][1] === postValue) { + // Conditions match, just need to make sure the post clears label + if (post[2][0] === 'block' && post[2][1] && post[2][1].length > 0) { + var postStat = post[2][1][0]; + if (postStat[0] === 'stat' && postStat[1][0] === 'assign' && + postStat[1][1] === true && postStat[1][2][0] === 'name' && postStat[1][2][1] === 'label' && + postStat[1][3][0] === 'num' && postStat[1][3][1] === 0) { + // Everything lines up, do it + pre[3] = post[2]; + pre[3][1].splice(0, 1); // remove the label clearing + stats.splice(i+1, 1); // remove the post entirely + } + } + } + } + } + } + } + } + }); } }); } diff --git a/tools/test-js-optimizer-si-output.js b/tools/test-js-optimizer-si-output.js index 97d7fe83..a6e8f53e 100644 --- a/tools/test-js-optimizer-si-output.js +++ b/tools/test-js-optimizer-si-output.js @@ -103,5 +103,39 @@ function a() { } else { label = 5; } + fuseElses(); + if (x ? y : 0) { + f(); + } else { + a(); + } + if (x ? y : 0) { + f(); + } else { + label = 5; + } + if ((label | 0) == 6) { + label = 0; + a(); + } + if (x ? y : 0) { + f(); + } else { + label = 5; + } + if ((label | 0) == 5) { + a(); + } +} +function b() { + if (x) { + a(); + } else { + label = 5; + } + if ((label | 0) == 5) { + label = 0; + a(); + } } diff --git a/tools/test-js-optimizer-si.js b/tools/test-js-optimizer-si.js index def2b5d9..78683d6c 100644 --- a/tools/test-js-optimizer-si.js +++ b/tools/test-js-optimizer-si.js @@ -125,5 +125,55 @@ function a() { } else { label = 5; } + fuseElses(); + if (x) { + if (y) { + f(); + } else { + label = 5; + } + } else { + label = 5; + } + if ((label|0) == 5) { + label = 0; + a(); + } + if (x) { + if (y) { + f(); + } else { + label = 5; + } + } else { + label = 5; + } + if ((label|0) == 6) { + label = 0; + a(); + } + if (x) { + if (y) { + f(); + } else { + label = 5; + } + } else { + label = 5; + } + if ((label|0) == 5) { + a(); + } +} +function b() { + if (x) { // will not be fused, since we did not eliminate with elses + a(); + } else { + label = 5; + } + if ((label|0) == 5) { + label = 0; + a(); + } } - +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b"] |