aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/test_benchmark.py36
-rw-r--r--tools/js-optimizer.js86
-rw-r--r--tools/test-js-optimizer-asm-pre-output.js1
-rw-r--r--tools/test-js-optimizer-asm-pre.js1
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"]