more optimizations for some break/continue cases

This commit is contained in:
Mihai Bazon
2012-10-18 15:14:57 +03:00
parent 6aa56f92fe
commit afb7faa6fa
5 changed files with 216 additions and 9 deletions

View File

@@ -929,10 +929,10 @@ TreeWalker.prototype = {
} else { } else {
for (var i = stack.length; --i >= 0;) { for (var i = stack.length; --i >= 0;) {
var x = stack[i]; var x = stack[i];
if (x instanceof AST_Switch) return x; if (x instanceof AST_Switch
if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) { || x instanceof AST_For
return (x.body instanceof AST_BlockStatement ? x.body : x); || x instanceof AST_ForIn
} || x instanceof AST_DWLoop) return x;
} }
} }
} }

View File

@@ -186,6 +186,14 @@ merge(Compressor.prototype, {
return false; return false;
}; };
function loop_body(x) {
if (x instanceof AST_Switch) return x;
if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
return (x.body instanceof AST_BlockStatement ? x.body : x);
}
return x;
};
function tighten_body(statements, compressor) { function tighten_body(statements, compressor) {
var CHANGED; var CHANGED;
do { do {
@@ -303,8 +311,13 @@ merge(Compressor.prototype, {
} }
var ab = aborts(stat.body); var ab = aborts(stat.body);
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda) if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|| (ab instanceof AST_Continue && self === compressor.loopcontrol_target(ab.label)))) { || (ab instanceof AST_Continue && self === loop_body(lct))
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
if (ab.label) {
remove(ab.label.thedef.references, ab.label);
}
CHANGED = true; CHANGED = true;
var body = as_statement_array(stat.body).slice(0, -1); var body = as_statement_array(stat.body).slice(0, -1);
stat = stat.clone(); stat = stat.clone();
@@ -320,8 +333,13 @@ merge(Compressor.prototype, {
} }
var ab = aborts(stat.alternative); var ab = aborts(stat.alternative);
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda) if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|| (ab instanceof AST_Continue && self === compressor.loopcontrol_target(ab.label)))) { || (ab instanceof AST_Continue && self === loop_body(lct))
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
if (ab.label) {
remove(ab.label.thedef.references, ab.label);
}
CHANGED = true; CHANGED = true;
stat = stat.clone(); stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, { stat.body = make_node(AST_BlockStatement, stat.body, {
@@ -347,11 +365,26 @@ merge(Compressor.prototype, {
function eliminate_dead_code(statements, compressor) { function eliminate_dead_code(statements, compressor) {
var has_quit = false; var has_quit = false;
var orig = statements.length; var orig = statements.length;
var self = compressor.self();
statements = statements.reduce(function(a, stat){ statements = statements.reduce(function(a, stat){
if (has_quit) { if (has_quit) {
extract_declarations_from_unreachable_code(compressor, stat, a); extract_declarations_from_unreachable_code(compressor, stat, a);
} else { } else {
a.push(stat); if (stat instanceof AST_LoopControl) {
var lct = compressor.loopcontrol_target(stat.label);
if ((stat instanceof AST_Break
&& lct instanceof AST_BlockStatement
&& loop_body(lct) === self) || (stat instanceof AST_Continue
&& loop_body(lct) === self)) {
if (stat.label) {
remove(stat.label.thedef.references, stat.label);
}
} else {
a.push(stat);
}
} else {
a.push(stat);
}
if (aborts(stat)) has_quit = true; if (aborts(stat)) has_quit = true;
} }
return a; return a;
@@ -795,6 +828,10 @@ merge(Compressor.prototype, {
}); });
OPT(AST_LabeledStatement, function(self, compressor){ OPT(AST_LabeledStatement, function(self, compressor){
if (self.body instanceof AST_Break
&& compressor.loopcontrol_target(self.body.label) === self.body) {
return make_node(AST_EmptyStatement, self);
}
return self.label.references.length == 0 ? self.body : self; return self.label.references.length == 0 ? self.body : self;
}); });
@@ -1227,7 +1264,7 @@ merge(Compressor.prototype, {
var last_branch = self.body[self.body.length - 1]; var last_branch = self.body[self.body.length - 1];
if (last_branch) { if (last_branch) {
var stat = last_branch.body[last_branch.body.length - 1]; // last statement var stat = last_branch.body[last_branch.body.length - 1]; // last statement
if (stat instanceof AST_Break && compressor.loopcontrol_target(stat.label) === self) if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
last_branch.body.pop(); last_branch.body.pop();
} }
return self; return self;

View File

@@ -166,6 +166,12 @@ function string_template(text, props) {
}); });
}; };
function remove(array, el) {
for (var i = array.length; --i >= 0;) {
if (array[i] === el) array.splice(i, 1);
}
};
function mergeSort(array, cmp) { function mergeSort(array, cmp) {
if (array.length < 2) return array.slice(); if (array.length < 2) return array.slice();
function merge(a, b) { function merge(a, b) {

163
test/compress/labels.js Normal file
View File

@@ -0,0 +1,163 @@
labels_1: {
options = { if_return: true, conditionals: true, dead_code: true };
input: {
out: {
if (foo) break out;
console.log("bar");
}
};
expect: {
foo || console.log("bar");
}
}
labels_2: {
options = { if_return: true, conditionals: true, dead_code: true };
input: {
out: {
if (foo) print("stuff");
else break out;
console.log("here");
}
};
expect: {
if (foo) {
print("stuff");
console.log("here");
}
}
}
labels_3: {
options = { if_return: true, conditionals: true, dead_code: true };
input: {
for (var i = 0; i < 5; ++i) {
if (i < 3) continue;
console.log(i);
}
};
expect: {
for (var i = 0; i < 5; ++i)
i < 3 || console.log(i);
}
}
labels_4: {
options = { if_return: true, conditionals: true, dead_code: true };
input: {
out: for (var i = 0; i < 5; ++i) {
if (i < 3) continue out;
console.log(i);
}
};
expect: {
for (var i = 0; i < 5; ++i)
i < 3 || console.log(i);
}
}
labels_5: {
options = { if_return: true, conditionals: true, dead_code: true };
// should keep the break-s in the following
input: {
while (foo) {
if (bar) break;
console.log("foo");
}
out: while (foo) {
if (bar) break out;
console.log("foo");
}
};
expect: {
while (foo) {
if (bar) break;
console.log("foo");
}
out: while (foo) {
if (bar) break out;
console.log("foo");
}
}
}
labels_6: {
input: {
out: break out;
};
expect: {}
}
labels_7: {
options = { if_return: true, conditionals: true, dead_code: true };
input: {
while (foo) {
x();
y();
continue;
}
};
expect: {
while (foo) {
x();
y();
}
}
}
labels_8: {
options = { if_return: true, conditionals: true, dead_code: true };
input: {
while (foo) {
x();
y();
break;
}
};
expect: {
while (foo) {
x();
y();
break;
}
}
}
labels_9: {
options = { if_return: true, conditionals: true, dead_code: true };
input: {
out: while (foo) {
x();
y();
continue out;
z();
k();
}
};
expect: {
while (foo) {
x();
y();
}
}
}
labels_10: {
options = { if_return: true, conditionals: true, dead_code: true };
input: {
out: while (foo) {
x();
y();
break out;
z();
k();
}
};
expect: {
out: while (foo) {
x();
y();
break out;
}
}
}

View File

@@ -73,12 +73,13 @@ function run_compress_tests() {
var cmp = new U.Compressor(options, true); var cmp = new U.Compressor(options, true);
var expect = make_code(as_toplevel(test.expect), false); var expect = make_code(as_toplevel(test.expect), false);
var input = as_toplevel(test.input); var input = as_toplevel(test.input);
var input_code = make_code(test.input);
var output = input.transform(cmp); var output = input.transform(cmp);
output.figure_out_scope(); output.figure_out_scope();
output = make_code(output, false); output = make_code(output, false);
if (expect != output) { if (expect != output) {
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", { log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
input: make_code(test.input), input: input_code,
output: output, output: output,
expected: expect expected: expect
}); });