Skip to content

Commit 2c51c1e

Browse files
committed
eliminate loop helper variables
1 parent b970a01 commit 2c51c1e

File tree

4 files changed

+234
-4
lines changed

4 files changed

+234
-4
lines changed

tests/runner.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13095,7 +13095,7 @@ def test_enet(self):
1309513095

1309613096
Building.COMPILER_TEST_OPTS = []
1309713097

13098-
TEST_REPS = 1
13098+
TEST_REPS = 2
1309913099
TOTAL_TESTS = 8
1310013100

1310113101
# standard arguments for timing:
@@ -13160,7 +13160,7 @@ def do_benchmark(self, name, src, expected_output='FAIL', args=[], emcc_args=[],
1316013160
'-O2', '-s', 'DOUBLE_MODE=0', '-s', 'PRECISE_I64_MATH=0',
1316113161
'--llvm-lto', '1', '--memory-init-file', '0',
1316213162
'-s', 'TOTAL_MEMORY=128*1024*1024',
13163-
'--closure', '1', '-g',
13163+
'--closure', '1',
1316413164
'-o', final_filename] + shared_args + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate()
1316513165
assert os.path.exists(final_filename), 'Failed to compile file: ' + output[0]
1316613166

tools/eliminator/asm-eliminator-test-output.js

+70
Original file line numberDiff line numberDiff line change
@@ -4961,4 +4961,74 @@ function _java_nio_charset_Charset_forNameInternal___java_lang_String($n1) {
49614961
break;
49624962
}
49634963
}
4964+
function looop2() {
4965+
var i = 0;
4966+
while (1) {
4967+
do_it();
4968+
i = i + 1 | 0;
4969+
if (condition(i)) {
4970+
break;
4971+
}
4972+
}
4973+
}
4974+
function looop3() {
4975+
var i = 0;
4976+
while (1) {
4977+
do_it();
4978+
i = i + 1 | 0;
4979+
if (!condition(i)) {
4980+
break;
4981+
}
4982+
}
4983+
}
4984+
function looop4() {
4985+
var i = 0, helper = 0;
4986+
while (1) {
4987+
do_it();
4988+
helper = i + 1 | 0;
4989+
f(i, helper);
4990+
if (condition()) {
4991+
i = helper;
4992+
} else {
4993+
break;
4994+
}
4995+
}
4996+
}
4997+
function looop4b() {
4998+
var i = 0, helper = 0;
4999+
while (1) {
5000+
do_it();
5001+
helper = i + 1 | 0;
5002+
g(helper);
5003+
if (condition(i)) {
5004+
i = helper;
5005+
} else {
5006+
break;
5007+
}
5008+
}
5009+
}
5010+
function looop5() {
5011+
var i = 0, helper = 0;
5012+
while (1) {
5013+
do_it();
5014+
helper = i + 1 | 0;
5015+
if (condition(helper)) {
5016+
i = helper;
5017+
} else {
5018+
break;
5019+
}
5020+
}
5021+
moar(i);
5022+
}
5023+
function looop6() {
5024+
var i = 0;
5025+
while (1) {
5026+
do_it();
5027+
i = i + 1 | 0;
5028+
if (!condition(i)) {
5029+
break;
5030+
}
5031+
}
5032+
moar(i);
5033+
}
49645034

tools/eliminator/asm-eliminator-test.js

+78-1
Original file line numberDiff line numberDiff line change
@@ -6678,5 +6678,82 @@ function _java_nio_charset_Charset_forNameInternal___java_lang_String($n1) {
66786678
__THREW__ = threwValue = 0;
66796679
break;
66806680
}
6681-
}// 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"]
6681+
}
6682+
function looop2() {
6683+
var i = 0, helper = 0;
6684+
while (1) {
6685+
do_it();
6686+
helper = (i + 1)|0;
6687+
if (condition(helper)) {
6688+
break;
6689+
} else {
6690+
i = helper;
6691+
}
6692+
}
6693+
}
6694+
function looop3() {
6695+
var i = 0, helper = 0;
6696+
while (1) {
6697+
do_it();
6698+
helper = (i + 1)|0;
6699+
if (condition(helper)) {
6700+
i = helper;
6701+
} else {
6702+
break;
6703+
}
6704+
}
6705+
}
6706+
function looop4() {
6707+
var i = 0, helper = 0;
6708+
while (1) {
6709+
do_it();
6710+
helper = (i + 1)|0;
6711+
f(i, helper); // i is used, cannot optimize!
6712+
if (condition()) {
6713+
i = helper;
6714+
} else {
6715+
break;
6716+
}
6717+
}
6718+
}
6719+
function looop4b() {
6720+
var i = 0, helper = 0;
6721+
while (1) {
6722+
do_it();
6723+
helper = (i + 1)|0;
6724+
g(helper);
6725+
if (condition(i)) { // i is used, cannot optimize!
6726+
i = helper;
6727+
} else {
6728+
break;
6729+
}
6730+
}
6731+
}
6732+
function looop5() {
6733+
var i = 0, helper = 0;
6734+
while (1) {
6735+
do_it();
6736+
helper = (i + 1)|0;
6737+
if (condition(helper)) {
6738+
i = helper;
6739+
} else {
6740+
break;
6741+
}
6742+
}
6743+
moar(i); // i is still needed, cannot optimize!
6744+
}
6745+
function looop6() {
6746+
var i = 0, helper = 0;
6747+
while (1) {
6748+
do_it();
6749+
helper = (i + 1)|0;
6750+
if (condition(helper)) {
6751+
i = helper;
6752+
} else {
6753+
break;
6754+
}
6755+
}
6756+
moar(helper); // this is cool
6757+
}
6758+
// 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"]
66826759

tools/js-optimizer.js

+84-1
Original file line numberDiff line numberDiff line change
@@ -1972,7 +1972,7 @@ function eliminate(ast, memSafe) {
19721972
//printErr('locals: ' + JSON.stringify(locals));
19731973
//printErr('varsToRemove: ' + JSON.stringify(varsToRemove));
19741974
//printErr('varsToTryToRemove: ' + JSON.stringify(varsToTryToRemove));
1975-
definitions = values = null;
1975+
values = null;
19761976
//printErr('potentials: ' + JSON.stringify(potentials));
19771977
// We can now proceed through the function. In each list of statements, we try to eliminate
19781978
var tracked = {};
@@ -2327,6 +2327,8 @@ function eliminate(ast, memSafe) {
23272327
//printErr('delete StatBlock');
23282328
});
23292329

2330+
var seenUses = {}, helperReplacements = {}; // for looper-helper optimization
2331+
23302332
// clean up vars
23312333
//printErr('cleaning up ' + JSON.stringify(varsToRemove));
23322334
traverse(func, function(node, type) {
@@ -2338,6 +2340,87 @@ function eliminate(ast, memSafe) {
23382340
node[1] = [];
23392341
}
23402342
}
2343+
}, function(node, type) {
2344+
if (type == 'name') {
2345+
var name = node[1];
2346+
if (name in helperReplacements) {
2347+
node[1] = helperReplacements[name];
2348+
return; // no need to track this anymore, we can't loop-optimize more than once
2349+
}
2350+
// track how many uses we saw. we need to know when a variable is no longer used (hence we run this in the post)
2351+
if (!(name in seenUses)) {
2352+
seenUses[name] = 1;
2353+
} else {
2354+
seenUses[name]++;
2355+
}
2356+
} else if (type == 'while') {
2357+
// try to remove loop helper variables specifically
2358+
var stats = node[2][1];
2359+
var last = stats[stats.length-1];
2360+
if (last[0] == 'if' && last[2][0] == 'block' && last[3] && last[3][0] == 'block') {
2361+
var ifTrue = last[2];
2362+
var ifFalse = last[3];
2363+
var flip = false;
2364+
if (ifFalse[1][0][0] == 'break') { // canonicalize break in the if
2365+
var temp = ifFalse;
2366+
ifFalse = ifTrue;
2367+
ifTrue = temp;
2368+
flip = true;
2369+
}
2370+
if (ifTrue[1][0][0] == 'break' && !ifTrue[1][1] && ifFalse[1].length == 1 && ifFalse[1][0][0] == 'stat' && ifFalse[1][0][1][0] == 'assign') {
2371+
var assign = ifFalse[1][0][1];
2372+
if (assign[1] === true && assign[2][0] == 'name' && assign[3][0] == 'name') {
2373+
var looper = assign[2][1];
2374+
var helper = assign[3][1];
2375+
if (definitions[helper] == 1 && seenUses[looper] == uses[looper] + 1 && // +1, because uses does not count the definition
2376+
!helperReplacements[helper] && !helperReplacements[looper]) {
2377+
// the remaining issue is whether looper is used after the assignment to helper and before the last line (where we assign to it)
2378+
var found = -1;
2379+
for (var i = stats.length-2; i >= 0; i--) {
2380+
var curr = stats[i];
2381+
if (curr[0] == 'stat' && curr[1][0] == 'assign') {
2382+
var currAssign = curr[1];
2383+
if (currAssign[1] === true && currAssign[2][0] == 'name') {
2384+
var to = currAssign[2][1];
2385+
if (to == helper) {
2386+
found = i;
2387+
break;
2388+
}
2389+
}
2390+
}
2391+
}
2392+
if (found >= 0) {
2393+
var looperUsed = false;
2394+
for (var i = found+1; i < stats.length && !looperUsed; i++) {
2395+
var curr = i < stats.length-1 ? stats[i] : last[1]; // on the last line, just look in the condition
2396+
traverse(curr, function(node, type) {
2397+
if (type == 'name' && node[1] == looper) {
2398+
looperUsed = true;
2399+
return true;
2400+
}
2401+
});
2402+
}
2403+
if (!looperUsed) {
2404+
// hurrah! this is safe to do
2405+
varsToRemove[helper] = 2;
2406+
traverse(node, function(node, type) { // replace all appearances of helper with looper
2407+
if (type == 'name' && node[1] == helper) node[1] = looper;
2408+
});
2409+
helperReplacements[helper] = looper; // replace all future appearances of helper with looper
2410+
helperReplacements[looper] = looper; // avoid any further attempts to optimize looper in this manner (seenUses is wrong anyhow, too)
2411+
// simplify the if. we remove the if branch, leaving only the else
2412+
if (flip) {
2413+
last[1] = simplifyNotComps(['unary-prefix', '!', last[1]]);
2414+
last[2] = last[3];
2415+
}
2416+
last.pop();
2417+
}
2418+
}
2419+
}
2420+
}
2421+
}
2422+
}
2423+
}
23412424
});
23422425

23432426
if (asm) {

0 commit comments

Comments
 (0)