diff options
-rw-r--r-- | tests/test_benchmark.py | 36 | ||||
-rw-r--r-- | tools/js-optimizer.js | 86 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-pre-output.js | 1 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-pre.js | 1 |
4 files changed, 86 insertions, 38 deletions
diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index 821caa6b..16abbfdd 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -408,6 +408,42 @@ class benchmark(RunnerCore): ''' self.do_benchmark('ifs', src, 'ok', reps=TEST_REPS*5) + def test_conditionals(self): + src = r''' + #include <stdio.h> + #include <stdlib.h> + + int main(int argc, char *argv[]) { + int arg = argc > 1 ? argv[1][0] - '0' : 3; + switch(arg) { + case 0: return 0; break; + case 1: arg = 3*75; break; + case 2: arg = 3*625; break; + case 3: arg = 3*1250; break; + case 4: arg = 3*5*1250; break; + case 5: arg = 3*10*1250; break; + default: printf("error: %d\\n", arg); return -1; + } + + int x = 0; + + for (int j = 0; j < 27000; j++) { + for (int i = 0; i < arg; i++) { + if (((x*x+11) % 3 == 0) | ((x*(x+2)+17) % 5 == 0)) { + x += 2; + } else { + x++; + } + } + } + + printf("ok %d\n", x); + + return x; + } + ''' + self.do_benchmark('conditionals', src, 'ok', reps=TEST_REPS*5) + def test_fannkuch(self): src = open(path_from_root('tests', 'fannkuch.cpp'), 'r').read().replace( 'int n = argc > 1 ? atoi(argv[1]) : 0;', diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index d79e8f23..f38ff08c 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -749,54 +749,61 @@ function simplifyExpressions(ast) { }); } + function emitsBoolean(node) { + if (node[0] === 'binary') return node[1] in COMPARE_OPS; + if (node[0] === 'unary-prefix') return node[1] === '!'; + if (node[0] === 'conditional') return true; + return false; + } + // expensive | expensive can be turned into expensive ? 1 : expensive, and // expensive | cheap can be turned into cheap ? 1 : expensive, // so that we can avoid the expensive computation, if it has no side effects. function conditionalize(ast) { var MIN_COST = 7; traverse(ast, function(node, type) { - if (type === 'if' || type === 'while') { - var cond = node[1]; - if (cond[0] === 'binary' && (cond[1] === '|' || cond[1] === '&') && cond[3][0] !== 'num' && cond[2][0] !== 'num') { - // logical operator on two non-numerical values, all inside a condition - var left = cond[2]; - var right = cond[3]; - var leftEffects = hasSideEffects(left); - var rightEffects = hasSideEffects(right); - if (leftEffects && rightEffects) return; // both must execute - // canonicalize with side effects, if any, happening on the left - if (rightEffects) { - if (measureCost(left) < MIN_COST) return; // avoidable code is too cheap + if (node[0] === 'binary' && (node[1] === '|' || node[1] === '&') && node[3][0] !== 'num' && node[2][0] !== 'num') { + // logical operator on two non-numerical values + var left = node[2]; + var right = node[3]; + if (!emitsBoolean(left) || !emitsBoolean(right)) return; + var leftEffects = hasSideEffects(left); + var rightEffects = hasSideEffects(right); + if (leftEffects && rightEffects) return; // both must execute + // canonicalize with side effects, if any, happening on the left + if (rightEffects) { + if (measureCost(left) < MIN_COST) return; // avoidable code is too cheap + var temp = left; + left = right; + right = temp; + } else if (leftEffects) { + if (measureCost(right) < MIN_COST) return; // avoidable code is too cheap + } else { + // no side effects, reorder based on cost estimation + var leftCost = measureCost(left); + var rightCost = measureCost(right); + if (Math.max(leftCost, rightCost) < MIN_COST) return; // avoidable code is too cheap + // canonicalize with expensive code on the right + if (leftCost > rightCost) { var temp = left; left = right; right = temp; - } else if (leftEffects) { - if (measureCost(right) < MIN_COST) return; // avoidable code is too cheap - } else { - // no side effects, reorder based on cost estimation - var leftCost = measureCost(left); - var rightCost = measureCost(right); - if (Math.max(leftCost, rightCost) < MIN_COST) return; // avoidable code is too cheap - // canonicalize with expensive code on the right - if (leftCost > rightCost) { - var temp = left; - left = right; - right = temp; - } - } - // worth it, perform conditionalization - if (cond[1] === '|') { - node[1] = ['conditional', left, ['num', 1], right]; - } else { // & - node[1] = ['conditional', left, right, ['num', 0]]; - } - if (left[0] === 'unary-prefix' && left[1] === '!') { - node[1][1] = flipCondition(left); - var temp = node[1][2]; - node[1][2] = node[1][3]; - node[1][3] = temp; } } + // worth it, perform conditionalization + var ret; + if (node[1] === '|') { + ret = ['conditional', left, ['num', 1], right]; + } else { // & + ret = ['conditional', left, right, ['num', 0]]; + } + if (left[0] === 'unary-prefix' && left[1] === '!') { + ret[1] = flipCondition(left); + var temp = ret[2]; + ret[2] = ret[3]; + ret[3] = temp; + } + return ret; } }); } @@ -3978,7 +3985,10 @@ function measureCost(ast) { var size = 0; traverse(ast, function(node, type) { if (type === 'num' || type === 'unary-prefix') size--; - else if (type === 'binary' && node[3][0] === 'num' && node[3][1] === 0) size--; + else if (type === 'binary') { + if (node[3][0] === 'num' && node[3][1] === 0) size--; + else if (node[1] === '/' || node[1] === '%') size += 2; + } else if (type === 'call' && !callHasSideEffects(node)) size -= 2; else if (type === 'sub') size++; size++; diff --git a/tools/test-js-optimizer-asm-pre-output.js b/tools/test-js-optimizer-asm-pre-output.js index d6a7a04b..31b9cfd7 100644 --- a/tools/test-js-optimizer-asm-pre-output.js +++ b/tools/test-js-optimizer-asm-pre-output.js @@ -596,5 +596,6 @@ function conditionalizeMe() { if (x > 10 | HEAP[20] + 2 > 5) { b(); } + return (((((Math_imul(i6 + 1, i7) | 0) + 17 | 0) % 5 | 0) == 0 ? 1 : ((((Math_imul(i7 + 1, i7) | 0) + 11 | 0) >>> 0) % 3 | 0) == 0) | 0) == 0; } diff --git a/tools/test-js-optimizer-asm-pre.js b/tools/test-js-optimizer-asm-pre.js index f930fc13..2a6ea4a9 100644 --- a/tools/test-js-optimizer-asm-pre.js +++ b/tools/test-js-optimizer-asm-pre.js @@ -608,5 +608,6 @@ function conditionalizeMe() { if (x > 10 | (HEAP[20] + 2) > 5) { b(); } + return ((((Math_imul(i6+1, i7) | 0) + 17 | 0) % 5 | 0 | 0) == 0 | ((((Math_imul(i7+1, i7) | 0) + 11 | 0) >>> 0) % 3 | 0 | 0) == 0 | 0) == 0; } // EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "rett", "ret2t", "retf", "i32_8", "tempDoublePtr", "boxx", "_main", "badf", "badf2", "fcomp", "conditionalizeMe"] |