From 24807a6d8f657b27e0ae6b73e73fa987a0672480 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 5 Jun 2013 18:37:01 -0700 Subject: [PATCH 01/14] simplify infinite while loops with a break at the end into a do-while with a condition --- tools/js-optimizer.js | 16 ++++++++++++++++ tools/test-js-optimizer-regs-output.js | 5 +++++ tools/test-js-optimizer-regs.js | 10 +++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 09791150bf405..57c9cf79521f8 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -968,6 +968,7 @@ function simplifyNotComps(ast) { } } }); + return ast; } function simplifyExpressionsPost(ast) { @@ -1804,6 +1805,21 @@ function registerize(ast) { } denormalizeAsm(fun, finalAsmData); } + + // Post-registerize optimizations. This is near the end of the pipeline, we can assume all other optimizations except for minification are done + traverse(fun, function(node, type) { + if (type == 'while' && node[1][0] == 'num' && node[1][1] == 1 && node[2][0] == 'block') { + // while (1) { .. if (..) { break } } ==> do { .. } while(..) + var stats = node[2][1]; + var last = stats[stats.length-1]; + if (last[0] == 'if' && !last[3] && last[2][0] == 'block' && last[2][1][0][0] == 'break' && !last[2][1][0][1]) { + var conditionToBreak = last[1]; + stats.pop(); + node[0] = 'do'; + node[1] = simplifyNotComps(['unary-prefix', '!', conditionToBreak]); + } + } + }); }); } diff --git a/tools/test-js-optimizer-regs-output.js b/tools/test-js-optimizer-regs-output.js index fe7de5fb836f7..c1c9ec8794e01 100644 --- a/tools/test-js-optimizer-regs-output.js +++ b/tools/test-js-optimizer-regs-output.js @@ -229,4 +229,9 @@ function __ZN14NetworkAddressC1EPKcti(r1) { __ZN14NetworkAddressC2EPKcti(r1); return; } +function looop() { + do { + do_it(); + } while (!condition()); +} diff --git a/tools/test-js-optimizer-regs.js b/tools/test-js-optimizer-regs.js index 3013e518851a2..b38760cc99eee 100644 --- a/tools/test-js-optimizer-regs.js +++ b/tools/test-js-optimizer-regs.js @@ -234,4 +234,12 @@ function __ZN14NetworkAddressC1EPKcti($this) { __ZN14NetworkAddressC2EPKcti($this); return; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test", "primes", "atomic", "fcntl_open", "ex", "switchey", "__ZN14NetworkAddressC1EPKcti"] +function looop() { + while (1) { + do_it(); + if (condition()) { + break; + } + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test", "primes", "atomic", "fcntl_open", "ex", "switchey", "__ZN14NetworkAddressC1EPKcti", "looop"] From b970a019bfcf5413dec74f944a25cef633927f1c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 6 Jun 2013 17:16:30 -0700 Subject: [PATCH 02/14] move asm loop optimization into last phase --- tests/runner.py | 4 +-- tools/js-optimizer.js | 36 +++++++++++++--------- tools/test-js-optimizer-asm-last-output.js | 5 +++ tools/test-js-optimizer-asm-last.js | 11 ++++++- tools/test-js-optimizer-regs-output.js | 5 --- tools/test-js-optimizer-regs.js | 10 +----- 6 files changed, 39 insertions(+), 32 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index 53eb56feeefb3..b348565713392 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -13095,7 +13095,7 @@ def test_enet(self): Building.COMPILER_TEST_OPTS = [] - TEST_REPS = 2 + TEST_REPS = 1 TOTAL_TESTS = 8 # standard arguments for timing: @@ -13160,7 +13160,7 @@ def do_benchmark(self, name, src, expected_output='FAIL', args=[], emcc_args=[], '-O2', '-s', 'DOUBLE_MODE=0', '-s', 'PRECISE_I64_MATH=0', '--llvm-lto', '1', '--memory-init-file', '0', '-s', 'TOTAL_MEMORY=128*1024*1024', - '--closure', '1', + '--closure', '1', '-g', '-o', final_filename] + shared_args + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate() assert os.path.exists(final_filename), 'Failed to compile file: ' + output[0] diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 57c9cf79521f8..df71ab8640b3e 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1805,21 +1805,6 @@ function registerize(ast) { } denormalizeAsm(fun, finalAsmData); } - - // Post-registerize optimizations. This is near the end of the pipeline, we can assume all other optimizations except for minification are done - traverse(fun, function(node, type) { - if (type == 'while' && node[1][0] == 'num' && node[1][1] == 1 && node[2][0] == 'block') { - // while (1) { .. if (..) { break } } ==> do { .. } while(..) - var stats = node[2][1]; - var last = stats[stats.length-1]; - if (last[0] == 'if' && !last[3] && last[2][0] == 'block' && last[2][1][0][0] == 'break' && !last[2][1][0][1]) { - var conditionToBreak = last[1]; - stats.pop(); - node[0] = 'do'; - node[1] = simplifyNotComps(['unary-prefix', '!', conditionToBreak]); - } - } - }); }); } @@ -2471,6 +2456,26 @@ function fixDotZero(js) { }); } +function asmLoopOptimizer(ast) { + traverseGeneratedFunctions(ast, function(fun) { + // This is at the end of the pipeline, we can assume all other optimizations are done, and we modify loops + // into shapes that might confuse other passes + traverse(fun, function(node, type) { + if (type == 'while' && node[1][0] == 'num' && node[1][1] == 1 && node[2][0] == 'block') { + // while (1) { .. if (..) { break } } ==> do { .. } while(..) + var stats = node[2][1]; + var last = stats[stats.length-1]; + if (last[0] == 'if' && !last[3] && last[2][0] == 'block' && last[2][1][0][0] == 'break' && !last[2][1][0][1]) { + var conditionToBreak = last[1]; + stats.pop(); + node[0] = 'do'; + node[1] = simplifyNotComps(['unary-prefix', '!', conditionToBreak]); + } + } + }); + }); +} + // Passes table var compress = false, printMetadata = true, asm = false, last = false; @@ -2514,6 +2519,7 @@ arguments_.slice(1).forEach(function(arg) { passes[arg](ast); }); if (asm && last) { + asmLoopOptimizer(ast); prepDotZero(ast); } var js = astToSrc(ast, compress), old; diff --git a/tools/test-js-optimizer-asm-last-output.js b/tools/test-js-optimizer-asm-last-output.js index c10cc6b0c4e53..02605db88fbbf 100644 --- a/tools/test-js-optimizer-asm-last-output.js +++ b/tools/test-js-optimizer-asm-last-output.js @@ -32,4 +32,9 @@ function finall(x) { a = -0xde0b6b000000000; return 12.0e10; } +function looop() { + do { + do_it(); + } while (!condition()); +} diff --git a/tools/test-js-optimizer-asm-last.js b/tools/test-js-optimizer-asm-last.js index 6e97b6875c6e0..d3dffcae2ed5f 100644 --- a/tools/test-js-optimizer-asm-last.js +++ b/tools/test-js-optimizer-asm-last.js @@ -32,4 +32,13 @@ function finall(x) { a = -0xde0b6b000000000; return +12e10; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["finall"] +function looop() { + while (1) { + do_it(); + if (condition()) { + break; + } + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["finall", "looop"] + diff --git a/tools/test-js-optimizer-regs-output.js b/tools/test-js-optimizer-regs-output.js index c1c9ec8794e01..fe7de5fb836f7 100644 --- a/tools/test-js-optimizer-regs-output.js +++ b/tools/test-js-optimizer-regs-output.js @@ -229,9 +229,4 @@ function __ZN14NetworkAddressC1EPKcti(r1) { __ZN14NetworkAddressC2EPKcti(r1); return; } -function looop() { - do { - do_it(); - } while (!condition()); -} diff --git a/tools/test-js-optimizer-regs.js b/tools/test-js-optimizer-regs.js index b38760cc99eee..3013e518851a2 100644 --- a/tools/test-js-optimizer-regs.js +++ b/tools/test-js-optimizer-regs.js @@ -234,12 +234,4 @@ function __ZN14NetworkAddressC1EPKcti($this) { __ZN14NetworkAddressC2EPKcti($this); return; } -function looop() { - while (1) { - do_it(); - if (condition()) { - break; - } - } -} -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test", "primes", "atomic", "fcntl_open", "ex", "switchey", "__ZN14NetworkAddressC1EPKcti", "looop"] +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test", "primes", "atomic", "fcntl_open", "ex", "switchey", "__ZN14NetworkAddressC1EPKcti"] From 2c51c1eeb9b761e99ae37a5fcd1cdc40b121f746 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 6 Jun 2013 19:46:05 -0700 Subject: [PATCH 03/14] eliminate loop helper variables --- tests/runner.py | 4 +- .../eliminator/asm-eliminator-test-output.js | 70 +++++++++++++++ tools/eliminator/asm-eliminator-test.js | 79 ++++++++++++++++- tools/js-optimizer.js | 85 ++++++++++++++++++- 4 files changed, 234 insertions(+), 4 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index b348565713392..53eb56feeefb3 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -13095,7 +13095,7 @@ def test_enet(self): Building.COMPILER_TEST_OPTS = [] - TEST_REPS = 1 + TEST_REPS = 2 TOTAL_TESTS = 8 # standard arguments for timing: @@ -13160,7 +13160,7 @@ def do_benchmark(self, name, src, expected_output='FAIL', args=[], emcc_args=[], '-O2', '-s', 'DOUBLE_MODE=0', '-s', 'PRECISE_I64_MATH=0', '--llvm-lto', '1', '--memory-init-file', '0', '-s', 'TOTAL_MEMORY=128*1024*1024', - '--closure', '1', '-g', + '--closure', '1', '-o', final_filename] + shared_args + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate() assert os.path.exists(final_filename), 'Failed to compile file: ' + output[0] diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js index 41a2c2b606086..8cd115413dbc7 100644 --- a/tools/eliminator/asm-eliminator-test-output.js +++ b/tools/eliminator/asm-eliminator-test-output.js @@ -4961,4 +4961,74 @@ function _java_nio_charset_Charset_forNameInternal___java_lang_String($n1) { break; } } +function looop2() { + var i = 0; + while (1) { + do_it(); + i = i + 1 | 0; + if (condition(i)) { + break; + } + } +} +function looop3() { + var i = 0; + while (1) { + do_it(); + i = i + 1 | 0; + if (!condition(i)) { + break; + } + } +} +function looop4() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = i + 1 | 0; + f(i, helper); + if (condition()) { + i = helper; + } else { + break; + } + } +} +function looop4b() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = i + 1 | 0; + g(helper); + if (condition(i)) { + i = helper; + } else { + break; + } + } +} +function looop5() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = i + 1 | 0; + if (condition(helper)) { + i = helper; + } else { + break; + } + } + moar(i); +} +function looop6() { + var i = 0; + while (1) { + do_it(); + i = i + 1 | 0; + if (!condition(i)) { + break; + } + } + moar(i); +} diff --git a/tools/eliminator/asm-eliminator-test.js b/tools/eliminator/asm-eliminator-test.js index 9b9cd3f728b1b..6f881654e1c6c 100644 --- a/tools/eliminator/asm-eliminator-test.js +++ b/tools/eliminator/asm-eliminator-test.js @@ -6678,5 +6678,82 @@ function _java_nio_charset_Charset_forNameInternal___java_lang_String($n1) { __THREW__ = threwValue = 0; break; } -}// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String"] +} +function looop2() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = (i + 1)|0; + if (condition(helper)) { + break; + } else { + i = helper; + } + } +} +function looop3() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = (i + 1)|0; + if (condition(helper)) { + i = helper; + } else { + break; + } + } +} +function looop4() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = (i + 1)|0; + f(i, helper); // i is used, cannot optimize! + if (condition()) { + i = helper; + } else { + break; + } + } +} +function looop4b() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = (i + 1)|0; + g(helper); + if (condition(i)) { // i is used, cannot optimize! + i = helper; + } else { + break; + } + } +} +function looop5() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = (i + 1)|0; + if (condition(helper)) { + i = helper; + } else { + break; + } + } + moar(i); // i is still needed, cannot optimize! +} +function looop6() { + var i = 0, helper = 0; + while (1) { + do_it(); + helper = (i + 1)|0; + if (condition(helper)) { + i = helper; + } else { + break; + } + } + moar(helper); // this is cool +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6"] diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index df71ab8640b3e..a3a58f2baa64b 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1972,7 +1972,7 @@ function eliminate(ast, memSafe) { //printErr('locals: ' + JSON.stringify(locals)); //printErr('varsToRemove: ' + JSON.stringify(varsToRemove)); //printErr('varsToTryToRemove: ' + JSON.stringify(varsToTryToRemove)); - definitions = values = null; + values = null; //printErr('potentials: ' + JSON.stringify(potentials)); // We can now proceed through the function. In each list of statements, we try to eliminate var tracked = {}; @@ -2327,6 +2327,8 @@ function eliminate(ast, memSafe) { //printErr('delete StatBlock'); }); + var seenUses = {}, helperReplacements = {}; // for looper-helper optimization + // clean up vars //printErr('cleaning up ' + JSON.stringify(varsToRemove)); traverse(func, function(node, type) { @@ -2338,6 +2340,87 @@ function eliminate(ast, memSafe) { node[1] = []; } } + }, function(node, type) { + if (type == 'name') { + var name = node[1]; + if (name in helperReplacements) { + node[1] = helperReplacements[name]; + return; // no need to track this anymore, we can't loop-optimize more than once + } + // track how many uses we saw. we need to know when a variable is no longer used (hence we run this in the post) + if (!(name in seenUses)) { + seenUses[name] = 1; + } else { + seenUses[name]++; + } + } else if (type == 'while') { + // try to remove loop helper variables specifically + var stats = node[2][1]; + var last = stats[stats.length-1]; + if (last[0] == 'if' && last[2][0] == 'block' && last[3] && last[3][0] == 'block') { + var ifTrue = last[2]; + var ifFalse = last[3]; + var flip = false; + if (ifFalse[1][0][0] == 'break') { // canonicalize break in the if + var temp = ifFalse; + ifFalse = ifTrue; + ifTrue = temp; + flip = true; + } + if (ifTrue[1][0][0] == 'break' && !ifTrue[1][1] && ifFalse[1].length == 1 && ifFalse[1][0][0] == 'stat' && ifFalse[1][0][1][0] == 'assign') { + var assign = ifFalse[1][0][1]; + if (assign[1] === true && assign[2][0] == 'name' && assign[3][0] == 'name') { + var looper = assign[2][1]; + var helper = assign[3][1]; + if (definitions[helper] == 1 && seenUses[looper] == uses[looper] + 1 && // +1, because uses does not count the definition + !helperReplacements[helper] && !helperReplacements[looper]) { + // the remaining issue is whether looper is used after the assignment to helper and before the last line (where we assign to it) + var found = -1; + for (var i = stats.length-2; i >= 0; i--) { + var curr = stats[i]; + if (curr[0] == 'stat' && curr[1][0] == 'assign') { + var currAssign = curr[1]; + if (currAssign[1] === true && currAssign[2][0] == 'name') { + var to = currAssign[2][1]; + if (to == helper) { + found = i; + break; + } + } + } + } + if (found >= 0) { + var looperUsed = false; + for (var i = found+1; i < stats.length && !looperUsed; i++) { + var curr = i < stats.length-1 ? stats[i] : last[1]; // on the last line, just look in the condition + traverse(curr, function(node, type) { + if (type == 'name' && node[1] == looper) { + looperUsed = true; + return true; + } + }); + } + if (!looperUsed) { + // hurrah! this is safe to do + varsToRemove[helper] = 2; + traverse(node, function(node, type) { // replace all appearances of helper with looper + if (type == 'name' && node[1] == helper) node[1] = looper; + }); + helperReplacements[helper] = looper; // replace all future appearances of helper with looper + helperReplacements[looper] = looper; // avoid any further attempts to optimize looper in this manner (seenUses is wrong anyhow, too) + // simplify the if. we remove the if branch, leaving only the else + if (flip) { + last[1] = simplifyNotComps(['unary-prefix', '!', last[1]]); + last[2] = last[3]; + } + last.pop(); + } + } + } + } + } + } + } }); if (asm) { From 57ea5f4e7110310733e81194082bda168a463ae4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 6 Jun 2013 20:55:42 -0700 Subject: [PATCH 04/14] better erroring in SDL_Delay; issue #1255 --- src/library_sdl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library_sdl.js b/src/library_sdl.js index e8419536928a0..1c8ac52d9799a 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -868,7 +868,7 @@ var LibrarySDL = { }, SDL_Delay: function(delay) { - throw 'SDL_Delay called! Potential infinite loop, quitting. ' + new Error().stack; + abort('SDL_Delay called! Potential infinite loop, quitting.'); }, SDL_WM_SetCaption: function(title, icon) { From 5f2ccad0a399276de4e8426fcd1449e256499815 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 6 Jun 2013 21:12:20 -0700 Subject: [PATCH 05/14] track number of uses properly for loop variable removal --- .../eliminator/asm-eliminator-test-output.js | 17 +++++++++++++++++ tools/eliminator/asm-eliminator-test.js | 19 ++++++++++++++++++- tools/js-optimizer.js | 9 ++++++++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js index 8cd115413dbc7..9b874ba63b434 100644 --- a/tools/eliminator/asm-eliminator-test-output.js +++ b/tools/eliminator/asm-eliminator-test-output.js @@ -5031,4 +5031,21 @@ function looop6() { } moar(i); } +function looop7() { + var $old_0_i107_i = 0, $current_0_i108_i = 0, $696 = 0; + $old_0_i107_i = $draw_left_i; + while (1) { + $current_0_i108_i = HEAP32[$old_0_i107_i >> 2] | 0; + if (($current_0_i108_i | 0) == 0) { + break; + } + $696 = $current_0_i108_i + 4 | 0; + if (($current_0_i108_i | 0) == ($P_3207_i | 0)) { + break; + } else { + $old_0_i107_i = $696; + } + } + HEAP32[$old_0_i107_i >> 2] = HEAP32[$696 >> 2] | 0; +} diff --git a/tools/eliminator/asm-eliminator-test.js b/tools/eliminator/asm-eliminator-test.js index 6f881654e1c6c..9524bde234b9a 100644 --- a/tools/eliminator/asm-eliminator-test.js +++ b/tools/eliminator/asm-eliminator-test.js @@ -6755,5 +6755,22 @@ function looop6() { } moar(helper); // this is cool } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6"] +function looop7() { + var $old_0_i107_i = 0, $current_0_i108_i = 0, $696 = 0; + $old_0_i107_i = $draw_left_i; + while (1) { + $current_0_i108_i = HEAP32[$old_0_i107_i >> 2] | 0; + if (($current_0_i108_i | 0) == 0) { + break; + } + $696 = $current_0_i108_i + 4 | 0; + if (($current_0_i108_i | 0) == ($P_3207_i | 0)) { + break; + } else { + $old_0_i107_i = $696; + } + } + HEAP32[$old_0_i107_i >> 2] = HEAP32[$696 >> 2] | 0; +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7"] diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index a3a58f2baa64b..67ce2b3d0a498 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1854,6 +1854,7 @@ function eliminate(ast, memSafe) { // First, find the potentially eliminatable functions: that have one definition and one use var definitions = {}; var uses = {}; + var namings = {}; var values = {}; var locals = {}; var varsToRemove = {}; // variables being removed, that we can eliminate all 'var x;' of (this refers to 'var' nodes we should remove) @@ -1896,6 +1897,8 @@ function eliminate(ast, memSafe) { if (!values[name]) values[name] = node[3]; if (node[1] === true) { // not +=, -= etc., just = uses[name]--; // because the name node will show up by itself in the previous case + if (!namings[name]) namings[name] = 0; + namings[name]++; // offset it here, this tracks the total times we are named } } } else if (type == 'switch') { @@ -1903,6 +1906,10 @@ function eliminate(ast, memSafe) { } }); + for (var used in uses) { + namings[used] = (namings[used] || 0) + uses[used]; + } + // we cannot eliminate variables if there is a switch if (hasSwitch && !asm) return; @@ -2372,7 +2379,7 @@ function eliminate(ast, memSafe) { if (assign[1] === true && assign[2][0] == 'name' && assign[3][0] == 'name') { var looper = assign[2][1]; var helper = assign[3][1]; - if (definitions[helper] == 1 && seenUses[looper] == uses[looper] + 1 && // +1, because uses does not count the definition + if (definitions[helper] == 1 && seenUses[looper] == namings[looper] && !helperReplacements[helper] && !helperReplacements[looper]) { // the remaining issue is whether looper is used after the assignment to helper and before the last line (where we assign to it) var found = -1; From 32dc04d8d17fb7cc4122d692c8fa1ba661a33803 Mon Sep 17 00:00:00 2001 From: Jez Ng Date: Thu, 6 Jun 2013 10:27:17 -0700 Subject: [PATCH 06/14] Make lrint more correct. Closes #1265. --- src/library.js | 3 ++- tests/runner.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/library.js b/src/library.js index 5779327f293ae..2578fda44512e 100644 --- a/src/library.js +++ b/src/library.js @@ -5732,7 +5732,8 @@ LibraryManager.library = { llround: 'round', llroundf: 'round', rint: function(x) { - return (x > 0) ? -Math.round(-x) : Math.round(x); + if (Math.abs(x % 1) !== 0.5) return Math.round(x); + return x + x % 2 + ((x < 0) ? 1 : -1); }, rintf: 'rint', lrint: 'rint', diff --git a/tests/runner.py b/tests/runner.py index 53eb56feeefb3..5e8cbeea7af2a 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -1957,6 +1957,39 @@ def test_frexp(self): 1.000000=1.000000*2^0 -1.000000=-1.000000*2^0''') + def test_rounding(self): + src = ''' + #include + #include + + int main() + { + printf("%.1f ", round(1.4)); + printf("%.1f ", round(1.6)); + printf("%.1f ", round(-1.4)); + printf("%.1f ", round(-1.6)); + + printf("%.1f ", round(1.5)); + printf("%.1f ", round(2.5)); + printf("%.1f ", round(-1.5)); + printf("%.1f ", round(-2.5)); + + printf("%ld ", lrint(1.4)); + printf("%ld ", lrint(1.6)); + printf("%ld ", lrint(-1.4)); + printf("%ld ", lrint(-1.6)); + + printf("%ld ", lrint(1.5)); + printf("%ld ", lrint(2.5)); + printf("%ld ", lrint(-1.5)); + printf("%ld ", lrint(-2.5)); + + return 0; + } + ''' + self.do_run(src, "1.0 2.0 -1.0 -2.0 2.0 3.0 -2.0 -3.0 " + "1 2 -1 -2 2 2 -2 -2") + def test_getgep(self): # Generated code includes getelementptr (getelementptr, 0, 1), i.e., GEP as the first param to GEP src = ''' From bd7db31b42e5f445a0d36bc5cf79e119a4f90fdf Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 6 Jun 2013 21:55:47 -0700 Subject: [PATCH 07/14] add missing return values in safeRequestAnimationFrame,safeSetTimeout,safeSetInterval --- src/library_browser.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/library_browser.js b/src/library_browser.js index a690286bf83e2..9800fedf355a9 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -371,17 +371,17 @@ mergeInto(LibraryManager.library, { // abort-aware versions safeRequestAnimationFrame: function(func) { - Browser.requestAnimationFrame(function() { + return Browser.requestAnimationFrame(function() { if (!ABORT) func(); }); }, safeSetTimeout: function(func, timeout) { - setTimeout(function() { + return setTimeout(function() { if (!ABORT) func(); }, timeout); }, safeSetInterval: function(func, timeout) { - setInterval(function() { + return setInterval(function() { if (!ABORT) func(); }, timeout); }, From 265dd5a4e28ebd280dafb5febca1b556abc473e8 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 7 Jun 2013 09:40:48 -0700 Subject: [PATCH 08/14] handle empty loops in new loop optimizations; fixes #1270 --- tools/eliminator/asm-eliminator-test-output.js | 1 + tools/eliminator/asm-eliminator-test.js | 3 +++ tools/js-optimizer.js | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js index 9b874ba63b434..a7eebe25493fd 100644 --- a/tools/eliminator/asm-eliminator-test-output.js +++ b/tools/eliminator/asm-eliminator-test-output.js @@ -5047,5 +5047,6 @@ function looop7() { } } HEAP32[$old_0_i107_i >> 2] = HEAP32[$696 >> 2] | 0; + while (1) {} } diff --git a/tools/eliminator/asm-eliminator-test.js b/tools/eliminator/asm-eliminator-test.js index 9524bde234b9a..22b6ddecf823e 100644 --- a/tools/eliminator/asm-eliminator-test.js +++ b/tools/eliminator/asm-eliminator-test.js @@ -6771,6 +6771,9 @@ function looop7() { } } HEAP32[$old_0_i107_i >> 2] = HEAP32[$696 >> 2] | 0; + // empty loop + while (1) { + } } // EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7"] diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 67ce2b3d0a498..2b05aeb324bd8 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2364,7 +2364,7 @@ function eliminate(ast, memSafe) { // try to remove loop helper variables specifically var stats = node[2][1]; var last = stats[stats.length-1]; - if (last[0] == 'if' && last[2][0] == 'block' && last[3] && last[3][0] == 'block') { + if (last && last[0] == 'if' && last[2][0] == 'block' && last[3] && last[3][0] == 'block') { var ifTrue = last[2]; var ifFalse = last[3]; var flip = false; @@ -2555,7 +2555,7 @@ function asmLoopOptimizer(ast) { // while (1) { .. if (..) { break } } ==> do { .. } while(..) var stats = node[2][1]; var last = stats[stats.length-1]; - if (last[0] == 'if' && !last[3] && last[2][0] == 'block' && last[2][1][0][0] == 'break' && !last[2][1][0][1]) { + if (last && last[0] == 'if' && !last[3] && last[2][0] == 'block' && last[2][1][0][0] == 'break' && !last[2][1][0][1]) { var conditionToBreak = last[1]; stats.pop(); node[0] = 'do'; From 523ed62b3c68ef4dc04fb2918ae8b1b2f8e2bce1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 7 Jun 2013 10:02:56 -0700 Subject: [PATCH 09/14] optimize multiple loop variables together --- .../eliminator/asm-eliminator-test-output.js | 45 ++++++++ tools/eliminator/asm-eliminator-test.js | 50 ++++++++- tools/js-optimizer.js | 103 ++++++++++-------- 3 files changed, 153 insertions(+), 45 deletions(-) diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js index a7eebe25493fd..52c91ce1a4652 100644 --- a/tools/eliminator/asm-eliminator-test-output.js +++ b/tools/eliminator/asm-eliminator-test-output.js @@ -5049,4 +5049,49 @@ function looop7() { HEAP32[$old_0_i107_i >> 2] = HEAP32[$696 >> 2] | 0; while (1) {} } +function multiloop($n_0, $35) { + $n_0 = $n_0 | 0; + $35 = $35 | 0; + var $p_0 = 0, $41 = 0; + $n_0 = $35; + $p_0 = (HEAP32[$15 >> 2] | 0) + ($35 << 1) | 0; + while (1) { + $p_0 = $p_0 - 2 | 0; + $41 = HEAPU16[$p_0 >> 1] | 0; + if ($41 >>> 0 < $2 >>> 0) { + $_off0 = 0; + } else { + $_off0 = $41 - $2 & 65535; + } + HEAP16[$p_0 >> 1] = $_off0; + $n_0 = $n_0 - 1 | 0; + if (($n_0 | 0) == 0) { + break; + } + } +} +function multiloop2($n_0, $35) { + $n_0 = $n_0 | 0; + $35 = $35 | 0; + var $p_0 = 0, $39 = 0, $41 = 0, $46 = 0; + $n_0 = $35; + $p_0 = (HEAP32[$15 >> 2] | 0) + ($35 << 1) | 0; + while (1) { + $39 = $p_0 - 2 | 0; + $41 = HEAPU16[$39 >> 1] | 0; + if ($41 >>> 0 < $2 >>> 0) { + $_off0 = 0; + } else { + $_off0 = $41 - $2 & 65535; + } + HEAP16[$39 >> 1] = $p_0; + $46 = $n_0 - 1 | 0; + if (($46 | 0) == 0) { + break; + } else { + $n_0 = $46; + $p_0 = $39; + } + } +} diff --git a/tools/eliminator/asm-eliminator-test.js b/tools/eliminator/asm-eliminator-test.js index 22b6ddecf823e..e01a89ea4fca0 100644 --- a/tools/eliminator/asm-eliminator-test.js +++ b/tools/eliminator/asm-eliminator-test.js @@ -6775,5 +6775,53 @@ function looop7() { while (1) { } } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7"] +function multiloop($n_0, $35) { + $n_0 = $n_0 | 0; + $35 = $35 | 0; + var $p_0 = 0, $39 = 0, $41 = 0, $46 = 0; + $n_0 = $35; + $p_0 = (HEAP32[$15 >> 2] | 0) + ($35 << 1) | 0; + while (1) { + $39 = $p_0 - 2 | 0; + $41 = HEAPU16[$39 >> 1] | 0; + if ($41 >>> 0 < $2 >>> 0) { + $_off0 = 0; + } else { + $_off0 = $41 - $2 & 65535; + } + HEAP16[$39 >> 1] = $_off0; + $46 = $n_0 - 1 | 0; + if (($46 | 0) == 0) { + break; + } else { + $n_0 = $46; + $p_0 = $39; + } + } +} +function multiloop2($n_0, $35) { + $n_0 = $n_0 | 0; + $35 = $35 | 0; + var $p_0 = 0, $39 = 0, $41 = 0, $46 = 0; + $n_0 = $35; + $p_0 = (HEAP32[$15 >> 2] | 0) + ($35 << 1) | 0; + while (1) { + $39 = $p_0 - 2 | 0; + $41 = HEAPU16[$39 >> 1] | 0; + if ($41 >>> 0 < $2 >>> 0) { + $_off0 = 0; + } else { + $_off0 = $41 - $2 & 65535; + } + HEAP16[$39 >> 1] = $p_0; // cannot optimize one, so none + $46 = $n_0 - 1 | 0; + if (($46 | 0) == 0) { + break; + } else { + $n_0 = $46; + $p_0 = $39; + } + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7", "multiloop", "multiloop2"] diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 2b05aeb324bd8..8e8151009c501 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2374,57 +2374,72 @@ function eliminate(ast, memSafe) { ifTrue = temp; flip = true; } - if (ifTrue[1][0][0] == 'break' && !ifTrue[1][1] && ifFalse[1].length == 1 && ifFalse[1][0][0] == 'stat' && ifFalse[1][0][1][0] == 'assign') { - var assign = ifFalse[1][0][1]; - if (assign[1] === true && assign[2][0] == 'name' && assign[3][0] == 'name') { - var looper = assign[2][1]; - var helper = assign[3][1]; - if (definitions[helper] == 1 && seenUses[looper] == namings[looper] && - !helperReplacements[helper] && !helperReplacements[looper]) { - // the remaining issue is whether looper is used after the assignment to helper and before the last line (where we assign to it) - var found = -1; - for (var i = stats.length-2; i >= 0; i--) { - var curr = stats[i]; - if (curr[0] == 'stat' && curr[1][0] == 'assign') { - var currAssign = curr[1]; - if (currAssign[1] === true && currAssign[2][0] == 'name') { - var to = currAssign[2][1]; - if (to == helper) { - found = i; - break; - } - } + if (ifTrue[1][0][0] == 'break' && !ifTrue[1][1]) { + var assigns = ifFalse[1]; + var loopers = [], helpers = []; + for (var i = 0; i < assigns.length; i++) { + if (assigns[i][0] == 'stat' && assigns[i][1][0] == 'assign') { + var assign = assigns[i][1]; + if (assign[1] === true && assign[2][0] == 'name' && assign[3][0] == 'name') { + var looper = assign[2][1]; + var helper = assign[3][1]; + if (definitions[helper] == 1 && seenUses[looper] == namings[looper] && + !helperReplacements[helper] && !helperReplacements[looper]) { + loopers.push(looper); + helpers.push(helper); } } - if (found >= 0) { - var looperUsed = false; - for (var i = found+1; i < stats.length && !looperUsed; i++) { - var curr = i < stats.length-1 ? stats[i] : last[1]; // on the last line, just look in the condition - traverse(curr, function(node, type) { - if (type == 'name' && node[1] == looper) { - looperUsed = true; - return true; - } - }); - } - if (!looperUsed) { - // hurrah! this is safe to do - varsToRemove[helper] = 2; - traverse(node, function(node, type) { // replace all appearances of helper with looper - if (type == 'name' && node[1] == helper) node[1] = looper; - }); - helperReplacements[helper] = looper; // replace all future appearances of helper with looper - helperReplacements[looper] = looper; // avoid any further attempts to optimize looper in this manner (seenUses is wrong anyhow, too) - // simplify the if. we remove the if branch, leaving only the else - if (flip) { - last[1] = simplifyNotComps(['unary-prefix', '!', last[1]]); - last[2] = last[3]; + } + } + if (loopers.length < assigns.length) return; // TODO: handle the case where can can just eliminate one. (we can't optimize the break, but we can remove the var at least) + for (var l = 0; l < loopers.length; l++) { + var looper = loopers[l]; + var helper = helpers[l]; + // the remaining issue is whether loopers are used after the assignment to helper and before the last line (where we assign to it) + var found = -1; + for (var i = stats.length-2; i >= 0; i--) { + var curr = stats[i]; + if (curr[0] == 'stat' && curr[1][0] == 'assign') { + var currAssign = curr[1]; + if (currAssign[1] === true && currAssign[2][0] == 'name') { + var to = currAssign[2][1]; + if (to == helper) { + found = i; + break; } - last.pop(); } } } + if (found < 0) return; + var looperUsed = false; + for (var i = found+1; i < stats.length && !looperUsed; i++) { + var curr = i < stats.length-1 ? stats[i] : last[1]; // on the last line, just look in the condition + traverse(curr, function(node, type) { + if (type == 'name' && node[1] == looper) { + looperUsed = true; + return true; + } + }); + } + if (looperUsed) return; + } + // hurrah! this is safe to do + for (var l = 0; l < loopers.length; l++) { + var looper = loopers[l]; + var helper = helpers[l]; + varsToRemove[helper] = 2; + traverse(node, function(node, type) { // replace all appearances of helper with looper + if (type == 'name' && node[1] == helper) node[1] = looper; + }); + helperReplacements[helper] = looper; // replace all future appearances of helper with looper + helperReplacements[looper] = looper; // avoid any further attempts to optimize looper in this manner (seenUses is wrong anyhow, too) + } + // simplify the if. we remove the if branch, leaving only the else + if (flip) { + last[1] = simplifyNotComps(['unary-prefix', '!', last[1]]); + last[2] = last[3]; } + last.pop(); } } } From 20abfa85d57e796ab5fdf744643c7d3de08ae454 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 7 Jun 2013 10:12:51 -0700 Subject: [PATCH 10/14] allow building only in test runner --- tests/runner.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/runner.py b/tests/runner.py index 5e8cbeea7af2a..9214ae2d520c4 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -13146,6 +13146,9 @@ def test_enet(self): class benchmark(RunnerCore): def print_stats(self, times, native_times, last=False, reps=TEST_REPS): + if reps == 0: + print '(no reps)' + return mean = sum(times)/len(times) squared_times = map(lambda x: x*x, times) mean_of_squared = sum(squared_times)/len(times) From 574437453c4512eaedea5bcd4a74f8a29e6a37ba Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 7 Jun 2013 10:45:38 -0700 Subject: [PATCH 11/14] properly simplify not comps in loop suffixes and elsewhere --- tools/js-optimizer.js | 46 +++++++++++----------- tools/test-js-optimizer-asm-last-output.js | 3 ++ tools/test-js-optimizer-asm-last.js | 6 +++ 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 8e8151009c501..5f65033252a31 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -950,25 +950,27 @@ function optimizeShiftsAggressive(ast) { // we then get // if (!(x < 5)) // or such. Simplifying these saves space and time. -function simplifyNotComps(ast) { - traverse(ast, function(node, type) { - if (type == 'unary-prefix' && node[1] == '!' && node[2][0] == 'binary') { - if (node[2][1] == '<') { - return ['binary', '>=', node[2][2], node[2][3]]; - } else if (node[2][1] == '>') { - return ['binary', '<=', node[2][2], node[2][3]]; - } else if (node[2][1] == '==') { - return ['binary', '!=', node[2][2], node[2][3]]; - } else if (node[2][1] == '!=') { - return ['binary', '==', node[2][2], node[2][3]]; - } else if (node[2][1] == '===') { - return ['binary', '!==', node[2][2], node[2][3]]; - } else if (node[2][1] == '!==') { - return ['binary', '===', node[2][2], node[2][3]]; - } +function simplifyNotCompsDirect(node) { + if (node[0] == 'unary-prefix' && node[1] == '!' && node[2][0] == 'binary') { + if (node[2][1] == '<') { + return ['binary', '>=', node[2][2], node[2][3]]; + } else if (node[2][1] == '>') { + return ['binary', '<=', node[2][2], node[2][3]]; + } else if (node[2][1] == '==') { + return ['binary', '!=', node[2][2], node[2][3]]; + } else if (node[2][1] == '!=') { + return ['binary', '==', node[2][2], node[2][3]]; + } else if (node[2][1] == '===') { + return ['binary', '!==', node[2][2], node[2][3]]; + } else if (node[2][1] == '!==') { + return ['binary', '===', node[2][2], node[2][3]]; } - }); - return ast; + } + return node; +} + +function simplifyNotComps(ast) { + traverse(ast, simplifyNotCompsDirect); } function simplifyExpressionsPost(ast) { @@ -1217,8 +1219,7 @@ function hoistMultiples(ast) { var temp = node[3]; node[3] = node[2]; node[2] = temp; - node[1] = ['unary-prefix', '!', node[1]]; - simplifyNotComps(node[1]); // bake the ! into the expression + node[1] = simplifyNotCompsDirect(['unary-prefix', '!', node[1]]); stat1 = node[2][1]; stat2 = node[3][1]; } @@ -2436,7 +2437,7 @@ function eliminate(ast, memSafe) { } // simplify the if. we remove the if branch, leaving only the else if (flip) { - last[1] = simplifyNotComps(['unary-prefix', '!', last[1]]); + last[1] = simplifyNotCompsDirect(['unary-prefix', '!', last[1]]); last[2] = last[3]; } last.pop(); @@ -2574,7 +2575,8 @@ function asmLoopOptimizer(ast) { var conditionToBreak = last[1]; stats.pop(); node[0] = 'do'; - node[1] = simplifyNotComps(['unary-prefix', '!', conditionToBreak]); + node[1] = simplifyNotCompsDirect(['unary-prefix', '!', conditionToBreak]); + return node; } } }); diff --git a/tools/test-js-optimizer-asm-last-output.js b/tools/test-js-optimizer-asm-last-output.js index 02605db88fbbf..051f950dfd150 100644 --- a/tools/test-js-optimizer-asm-last-output.js +++ b/tools/test-js-optimizer-asm-last-output.js @@ -36,5 +36,8 @@ function looop() { do { do_it(); } while (!condition()); + do { + do_it(); + } while (a <= b); } diff --git a/tools/test-js-optimizer-asm-last.js b/tools/test-js-optimizer-asm-last.js index d3dffcae2ed5f..9ca323207300f 100644 --- a/tools/test-js-optimizer-asm-last.js +++ b/tools/test-js-optimizer-asm-last.js @@ -39,6 +39,12 @@ function looop() { break; } } + while (1) { + do_it(); + if (a > b) { + break; + } + } } // EMSCRIPTEN_GENERATED_FUNCTIONS: ["finall", "looop"] From 6467d20032135ec016301d8bf03b80e92cdb2ad1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 7 Jun 2013 10:58:17 -0700 Subject: [PATCH 12/14] remove unneeded check --- tools/js-optimizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 5f65033252a31..7681cf8d39e83 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2375,7 +2375,7 @@ function eliminate(ast, memSafe) { ifTrue = temp; flip = true; } - if (ifTrue[1][0][0] == 'break' && !ifTrue[1][1]) { + if (ifTrue[1][0][0] == 'break') { var assigns = ifFalse[1]; var loopers = [], helpers = []; for (var i = 0; i < assigns.length; i++) { From c6d56fb9da9eb2ecafadec1a951c98db17092b18 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 7 Jun 2013 11:12:05 -0700 Subject: [PATCH 13/14] optimize out double not --- tools/js-optimizer.js | 30 ++++++++++++---------- tools/test-js-optimizer-asm-last-output.js | 3 +++ tools/test-js-optimizer-asm-last.js | 6 +++++ 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 7681cf8d39e83..7e1d374fa08f0 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -951,19 +951,23 @@ function optimizeShiftsAggressive(ast) { // if (!(x < 5)) // or such. Simplifying these saves space and time. function simplifyNotCompsDirect(node) { - if (node[0] == 'unary-prefix' && node[1] == '!' && node[2][0] == 'binary') { - if (node[2][1] == '<') { - return ['binary', '>=', node[2][2], node[2][3]]; - } else if (node[2][1] == '>') { - return ['binary', '<=', node[2][2], node[2][3]]; - } else if (node[2][1] == '==') { - return ['binary', '!=', node[2][2], node[2][3]]; - } else if (node[2][1] == '!=') { - return ['binary', '==', node[2][2], node[2][3]]; - } else if (node[2][1] == '===') { - return ['binary', '!==', node[2][2], node[2][3]]; - } else if (node[2][1] == '!==') { - return ['binary', '===', node[2][2], node[2][3]]; + if (node[0] == 'unary-prefix' && node[1] == '!') { + if (node[2][0] == 'binary') { + if (node[2][1] == '<') { + return ['binary', '>=', node[2][2], node[2][3]]; + } else if (node[2][1] == '>') { + return ['binary', '<=', node[2][2], node[2][3]]; + } else if (node[2][1] == '==') { + return ['binary', '!=', node[2][2], node[2][3]]; + } else if (node[2][1] == '!=') { + return ['binary', '==', node[2][2], node[2][3]]; + } else if (node[2][1] == '===') { + return ['binary', '!==', node[2][2], node[2][3]]; + } else if (node[2][1] == '!==') { + return ['binary', '===', node[2][2], node[2][3]]; + } + } else if (node[2][0] == 'unary-prefix' && node[2][1] == '!') { + return node[2][2]; } } return node; diff --git a/tools/test-js-optimizer-asm-last-output.js b/tools/test-js-optimizer-asm-last-output.js index 051f950dfd150..1dacd0cd38618 100644 --- a/tools/test-js-optimizer-asm-last-output.js +++ b/tools/test-js-optimizer-asm-last-output.js @@ -39,5 +39,8 @@ function looop() { do { do_it(); } while (a <= b); + do { + do_it(); + } while (x()); } diff --git a/tools/test-js-optimizer-asm-last.js b/tools/test-js-optimizer-asm-last.js index 9ca323207300f..e83822f82038f 100644 --- a/tools/test-js-optimizer-asm-last.js +++ b/tools/test-js-optimizer-asm-last.js @@ -45,6 +45,12 @@ function looop() { break; } } + while (1) { + do_it(); + if (!x()) { + break; + } + } } // EMSCRIPTEN_GENERATED_FUNCTIONS: ["finall", "looop"] From 507d73ae7b6964031c3090e9330fb50c009df7c1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 7 Jun 2013 14:00:12 -0700 Subject: [PATCH 14/14] remove break labels more aggresively, with a refined natural flow analysis --- src/relooper/Relooper.cpp | 39 ++- src/relooper/Relooper.h | 1 + src/relooper/test.cpp | 27 ++ src/relooper/test.txt | 17 + src/relooper/test3.txt | 4 +- src/relooper/test_inf.txt | 648 +++++++++++++++++++------------------- tools/shared.py | 2 +- 7 files changed, 403 insertions(+), 335 deletions(-) diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp index 1939831025fad..8a6e18b8e5296 100644 --- a/src/relooper/Relooper.cpp +++ b/src/relooper/Relooper.cpp @@ -70,7 +70,7 @@ int Indenter::CurrIndent = 0; // Branch -Branch::Branch(const char *ConditionInit, const char *CodeInit) : Ancestor(NULL), Labeled(false) { +Branch::Branch(const char *ConditionInit, const char *CodeInit) : Ancestor(NULL), Labeled(true) { Condition = ConditionInit ? strdup(ConditionInit) : NULL; Code = CodeInit ? strdup(CodeInit) : NULL; } @@ -951,10 +951,28 @@ void Relooper::Calculate(Block *Entry) { }); } + void FindNaturals(Shape *Root, Shape *Otherwise=NULL) { + if (Root->Next) { + Root->Natural = Root->Next; + FindNaturals(Root->Next, Otherwise); + } else { + Root->Natural = Otherwise; + } + + SHAPE_SWITCH(Root, { + }, { + for (BlockShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) { + FindNaturals(iter->second, Root->Natural); + } + }, { + FindNaturals(Loop->Inner, Loop->Inner); + }); + } + // Remove unneeded breaks and continues. // A flow operation is trivially unneeded if the shape we naturally get to by normal code // execution is the same as the flow forces us to. - void RemoveUnneededFlows(Shape *Root, Shape *Natural=NULL) { + void RemoveUnneededFlows(Shape *Root, Shape *Natural=NULL, LoopShape *LastLoop=NULL) { BlockSet NaturalBlocks; FollowNaturalFlow(Natural, NaturalBlocks); Shape *Next = Root; @@ -976,16 +994,22 @@ void Relooper::Calculate(Block *Entry) { if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) { Multiple->NeedLoop--; } + } else if (Details->Type == Branch::Break && LastLoop && LastLoop->Natural == Details->Ancestor->Natural) { + // it is important to simplify breaks, as simpler breaks enable other optimizations + Details->Labeled = false; + if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) { + Multiple->NeedLoop--; + } } } } }, { for (BlockShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) { - RemoveUnneededFlows(iter->second, Multiple->Next); + RemoveUnneededFlows(iter->second, Multiple->Next, Multiple->NeedLoop ? NULL : LastLoop); } Next = Multiple->Next; }, { - RemoveUnneededFlows(Loop->Inner, Loop->Inner); + RemoveUnneededFlows(Loop->Inner, Loop->Inner, Loop); Next = Loop->Next; }); } @@ -1016,7 +1040,7 @@ void Relooper::Calculate(Block *Entry) { Branch *Details = iter->second; if (Details->Type != Branch::Direct) { assert(LoopStack.size() > 0); - if (Details->Ancestor != LoopStack.top()) { + if (Details->Ancestor != LoopStack.top() && Details->Labeled) { LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor); Labeled->Labeled = true; Details->Labeled = true; @@ -1054,6 +1078,7 @@ void Relooper::Calculate(Block *Entry) { } void Process(Shape *Root) { + FindNaturals(Root); RemoveUnneededFlows(Root); FindLabeledLoops(Root); } @@ -1101,6 +1126,10 @@ void Debugging::Dump(BlockSet &Blocks, const char *prefix) { void Debugging::Dump(Shape *S, const char *prefix) { if (prefix) printf("%s ", prefix); + if (!S) { + printf(" (null)\n"); + return; + } printf(" %d ", S->Id); SHAPE_SWITCH(S, { printf("<< Simple with block %d\n", Simple->Inner->Id); diff --git a/src/relooper/Relooper.h b/src/relooper/Relooper.h index 34b6db08fdd77..fe56a133a9a32 100644 --- a/src/relooper/Relooper.h +++ b/src/relooper/Relooper.h @@ -101,6 +101,7 @@ class LoopShape; struct Shape { int Id; // A unique identifier. Used to identify loops, labels are Lx where x is the Id. Shape *Next; // The shape that will appear in the code right after this one + Shape *Natural; // The shape that control flow gets to naturally (if there is Next, then this is Next) enum ShapeType { Simple, diff --git a/src/relooper/test.cpp b/src/relooper/test.cpp index 7da990b5550a1..b2d500d7b5aba 100644 --- a/src/relooper/test.cpp +++ b/src/relooper/test.cpp @@ -231,5 +231,32 @@ int main() { puts(buffer); } + + if (1) { + Relooper::SetOutputBuffer(buffer, sizeof(buffer)); + + printf("\n\n-- conditional loop --\n\n"); + + Block *b_a = new Block("// block A\n"); + Block *b_b = new Block("// block B\n"); + Block *b_c = new Block("// block C\n"); + + b_a->AddBranchTo(b_b, "shouldLoop()"); + b_a->AddBranchTo(b_c, NULL); + + b_b->AddBranchTo(b_b, "moarLoop()"); + b_b->AddBranchTo(b_c, NULL); + + Relooper r; + r.AddBlock(b_a); + r.AddBlock(b_b); + r.AddBlock(b_c); + + r.Calculate(b_a); + printf("\n\n"); + r.Render(); + + puts(buffer); + } } diff --git a/src/relooper/test.txt b/src/relooper/test.txt index d657c6af7a2bc..b35355928f9a3 100644 --- a/src/relooper/test.txt +++ b/src/relooper/test.txt @@ -136,3 +136,20 @@ while(1) { // block F } + + +-- conditional loop -- + + + +// block A +if (shouldLoop()) { + while(1) { + // block B + if (!(moarLoop())) { + break; + } + } +} +// block C + diff --git a/src/relooper/test3.txt b/src/relooper/test3.txt index 696542ef98610..6049ee48fabd1 100644 --- a/src/relooper/test3.txt +++ b/src/relooper/test3.txt @@ -9,7 +9,7 @@ do { } } while(0); LBB3 -L5: do { +do { if (LBB3 -> LBB4) { LBB4 if (!(LBB4 -> LBB5)) { @@ -18,7 +18,7 @@ L5: do { while(1) { LBB5 if (LBB5 -> LBB6) { - break L5; + break; } } } diff --git a/src/relooper/test_inf.txt b/src/relooper/test_inf.txt index 379d20836d9a8..1dff59bba0e77 100644 --- a/src/relooper/test_inf.txt +++ b/src/relooper/test_inf.txt @@ -5,35 +5,33 @@ if (uint(i4) >= uint(i5)) { code 1 } code 3 -L5: do { - if (!(i2 == 0)) { - code 4 - while(1) { - code 5 - if (uint(i6) >= uint(i7)) { - code 7 - } else { - code 6 - } - code 8 - if (uint(i6) >= uint(i7)) { - code 10 - } else { - code 9 - } - code 11 - if (uint(i5) >= uint(i6)) { - code 13 - } else { - code 12 - } - code 14 - if (!(i2 != 0)) { - break L5; - } +if (!(i2 == 0)) { + code 4 + while(1) { + code 5 + if (uint(i6) >= uint(i7)) { + code 7 + } else { + code 6 + } + code 8 + if (uint(i6) >= uint(i7)) { + code 10 + } else { + code 9 + } + code 11 + if (uint(i5) >= uint(i6)) { + code 13 + } else { + code 12 + } + code 14 + if (!(i2 != 0)) { + break; } } -} while(0); +} code 15 if (uint(i4) >= uint(i5)) { code 17 @@ -41,179 +39,177 @@ if (uint(i4) >= uint(i5)) { code 16 } code 18 -L26: do { - if (!(i2 == 0)) { - code 19 - while(1) { - code 20 - if (uint(i5) >= uint(i6)) { - code 22 - } else { - code 21 - } - code 23 - if (uint(i5) >= uint(i6)) { - code 25 - } else { - code 24 - } - code 26 - if (uint(i5) >= uint(i6)) { - code 28 - } else { - code 27 - } - code 29 - if (uint(i5) >= uint(i6)) { - code 31 - } else { - code 30 - } - code 32 - if (uint(i5) >= uint(i6)) { - code 34 - } else { - code 33 - } - code 35 - if (uint(i5) >= uint(i6)) { - code 37 - } else { - code 36 - } - code 38 - if (uint(i5) >= uint(i6)) { - code 40 - } else { - code 39 - } - code 41 - if (uint(i5) >= uint(i6)) { - code 43 - } else { - code 42 - } - code 44 - if (uint(i5) >= uint(i6)) { - code 46 - } else { - code 45 - } - code 47 - if (uint(i5) >= uint(i6)) { - code 49 - } else { - code 48 - } - code 50 - if (uint(i5) >= uint(i6)) { - code 52 - } else { - code 51 - } - code 53 - if (uint(i5) >= uint(i6)) { - code 55 - } else { - code 54 - } - code 56 - if (uint(i5) >= uint(i6)) { - code 58 - } else { - code 57 - } - code 59 - if (uint(i5) >= uint(i6)) { - code 61 - } else { - code 60 - } - code 62 - if (uint(i5) >= uint(i6)) { - code 64 - } else { - code 63 - } - code 65 - if (uint(i5) >= uint(i6)) { - code 67 - } else { - code 66 - } - code 68 - if (uint(i5) >= uint(i6)) { - code 70 - } else { - code 69 - } - code 71 - if (uint(i5) >= uint(i6)) { - code 73 - } else { - code 72 - } - code 74 - if (uint(i5) >= uint(i6)) { - code 76 - } else { - code 75 - } - code 77 - if (uint(i5) >= uint(i6)) { - code 79 - } else { - code 78 - } - code 80 - if (uint(i5) >= uint(i6)) { - code 82 - } else { - code 81 - } - code 83 - if (uint(i5) >= uint(i6)) { - code 85 - } else { - code 84 - } - code 86 - if (uint(i5) >= uint(i6)) { - code 88 - } else { - code 87 - } - code 89 - if (uint(i5) >= uint(i6)) { - code 91 - } else { - code 90 - } - code 92 - if (uint(i5) >= uint(i6)) { - code 94 - } else { - code 93 - } - code 95 - if (uint(i5) >= uint(i6)) { - code 97 - } else { - code 96 - } - code 98 - if (uint(i5) >= uint(i6)) { - code 100 - } else { - code 99 - } - code 101 - if (!(i2 != 0)) { - break L26; - } +if (!(i2 == 0)) { + code 19 + while(1) { + code 20 + if (uint(i5) >= uint(i6)) { + code 22 + } else { + code 21 + } + code 23 + if (uint(i5) >= uint(i6)) { + code 25 + } else { + code 24 + } + code 26 + if (uint(i5) >= uint(i6)) { + code 28 + } else { + code 27 + } + code 29 + if (uint(i5) >= uint(i6)) { + code 31 + } else { + code 30 + } + code 32 + if (uint(i5) >= uint(i6)) { + code 34 + } else { + code 33 + } + code 35 + if (uint(i5) >= uint(i6)) { + code 37 + } else { + code 36 + } + code 38 + if (uint(i5) >= uint(i6)) { + code 40 + } else { + code 39 + } + code 41 + if (uint(i5) >= uint(i6)) { + code 43 + } else { + code 42 + } + code 44 + if (uint(i5) >= uint(i6)) { + code 46 + } else { + code 45 + } + code 47 + if (uint(i5) >= uint(i6)) { + code 49 + } else { + code 48 + } + code 50 + if (uint(i5) >= uint(i6)) { + code 52 + } else { + code 51 + } + code 53 + if (uint(i5) >= uint(i6)) { + code 55 + } else { + code 54 + } + code 56 + if (uint(i5) >= uint(i6)) { + code 58 + } else { + code 57 + } + code 59 + if (uint(i5) >= uint(i6)) { + code 61 + } else { + code 60 + } + code 62 + if (uint(i5) >= uint(i6)) { + code 64 + } else { + code 63 + } + code 65 + if (uint(i5) >= uint(i6)) { + code 67 + } else { + code 66 + } + code 68 + if (uint(i5) >= uint(i6)) { + code 70 + } else { + code 69 + } + code 71 + if (uint(i5) >= uint(i6)) { + code 73 + } else { + code 72 + } + code 74 + if (uint(i5) >= uint(i6)) { + code 76 + } else { + code 75 + } + code 77 + if (uint(i5) >= uint(i6)) { + code 79 + } else { + code 78 + } + code 80 + if (uint(i5) >= uint(i6)) { + code 82 + } else { + code 81 + } + code 83 + if (uint(i5) >= uint(i6)) { + code 85 + } else { + code 84 + } + code 86 + if (uint(i5) >= uint(i6)) { + code 88 + } else { + code 87 + } + code 89 + if (uint(i5) >= uint(i6)) { + code 91 + } else { + code 90 + } + code 92 + if (uint(i5) >= uint(i6)) { + code 94 + } else { + code 93 + } + code 95 + if (uint(i5) >= uint(i6)) { + code 97 + } else { + code 96 + } + code 98 + if (uint(i5) >= uint(i6)) { + code 100 + } else { + code 99 + } + code 101 + if (!(i2 != 0)) { + break; } } -} while(0); +} code 102 if (uint(i4) >= uint(i5)) { code 104 @@ -221,137 +217,135 @@ if (uint(i4) >= uint(i5)) { code 103 } code 105 -L143: do { - if (!(i2 == 0)) { - code 106 - while(1) { - code 107 - if (uint(i5) >= uint(i6)) { - code 109 - } else { - code 108 - } - code 110 - if (uint(i5) >= uint(i6)) { - code 112 - } else { - code 111 - } - code 113 - if (uint(i5) >= uint(i6)) { - code 115 - } else { - code 114 - } - code 116 - if (uint(i5) >= uint(i6)) { - code 118 - } else { - code 117 - } - code 119 - if (uint(i5) >= uint(i6)) { - code 121 - } else { - code 120 - } - code 122 - if (uint(i5) >= uint(i6)) { - code 124 - } else { - code 123 - } - code 125 - if (uint(i5) >= uint(i6)) { - code 127 - } else { - code 126 - } - code 128 - if (uint(i5) >= uint(i6)) { - code 130 - } else { - code 129 - } - code 131 - if (uint(i5) >= uint(i6)) { - code 133 - } else { - code 132 - } - code 134 - if (uint(i5) >= uint(i6)) { - code 136 - } else { - code 135 - } - code 137 - if (uint(i5) >= uint(i6)) { - code 139 - } else { - code 138 - } - code 140 - if (uint(i5) >= uint(i6)) { - code 142 - } else { - code 141 - } - code 143 - if (uint(i5) >= uint(i6)) { - code 145 - } else { - code 144 - } - code 146 - if (uint(i5) >= uint(i6)) { - code 148 - } else { - code 147 - } - code 149 - if (uint(i5) >= uint(i6)) { - code 151 - } else { - code 150 - } - code 152 - if (uint(i5) >= uint(i6)) { - code 154 - } else { - code 153 - } - code 155 - if (uint(i5) >= uint(i6)) { - code 157 - } else { - code 156 - } - code 158 - if (uint(i5) >= uint(i6)) { - code 160 - } else { - code 159 - } - code 161 - if (uint(i5) >= uint(i6)) { - code 163 - } else { - code 162 - } - code 164 - if (uint(i5) >= uint(i6)) { - code 166 - } else { - code 165 - } - code 167 - if (!(i2 != 0)) { - break L143; - } +if (!(i2 == 0)) { + code 106 + while(1) { + code 107 + if (uint(i5) >= uint(i6)) { + code 109 + } else { + code 108 + } + code 110 + if (uint(i5) >= uint(i6)) { + code 112 + } else { + code 111 + } + code 113 + if (uint(i5) >= uint(i6)) { + code 115 + } else { + code 114 + } + code 116 + if (uint(i5) >= uint(i6)) { + code 118 + } else { + code 117 + } + code 119 + if (uint(i5) >= uint(i6)) { + code 121 + } else { + code 120 + } + code 122 + if (uint(i5) >= uint(i6)) { + code 124 + } else { + code 123 + } + code 125 + if (uint(i5) >= uint(i6)) { + code 127 + } else { + code 126 + } + code 128 + if (uint(i5) >= uint(i6)) { + code 130 + } else { + code 129 + } + code 131 + if (uint(i5) >= uint(i6)) { + code 133 + } else { + code 132 + } + code 134 + if (uint(i5) >= uint(i6)) { + code 136 + } else { + code 135 + } + code 137 + if (uint(i5) >= uint(i6)) { + code 139 + } else { + code 138 + } + code 140 + if (uint(i5) >= uint(i6)) { + code 142 + } else { + code 141 + } + code 143 + if (uint(i5) >= uint(i6)) { + code 145 + } else { + code 144 + } + code 146 + if (uint(i5) >= uint(i6)) { + code 148 + } else { + code 147 + } + code 149 + if (uint(i5) >= uint(i6)) { + code 151 + } else { + code 150 + } + code 152 + if (uint(i5) >= uint(i6)) { + code 154 + } else { + code 153 + } + code 155 + if (uint(i5) >= uint(i6)) { + code 157 + } else { + code 156 + } + code 158 + if (uint(i5) >= uint(i6)) { + code 160 + } else { + code 159 + } + code 161 + if (uint(i5) >= uint(i6)) { + code 163 + } else { + code 162 + } + code 164 + if (uint(i5) >= uint(i6)) { + code 166 + } else { + code 165 + } + code 167 + if (!(i2 != 0)) { + break; } } -} while(0); +} code 168 if (uint(i4) >= uint(i5)) { code 170 diff --git a/tools/shared.py b/tools/shared.py index 4225be264e6fb..8a172d9cac994 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -295,7 +295,7 @@ def check_node_version(): # we re-check sanity when the settings are changed) # We also re-check sanity and clear the cache when the version changes -EMSCRIPTEN_VERSION = '1.4.8' +EMSCRIPTEN_VERSION = '1.4.9' def generate_sanity(): return EMSCRIPTEN_VERSION + '|' + get_llvm_target()