optimization for if/break as first statement in a loop body

for(...; x; ...) if (y) break; → for(...; x&&!y; ...);

similarly for `while` and some combinations (i.e. the `break` appears in the
`else` clause, etc.)
This commit is contained in:
Mihai Bazon
2012-11-07 18:57:51 +02:00
parent 5a7e54cf72
commit 1a5fd3e052
2 changed files with 171 additions and 1 deletions

View File

@@ -1118,11 +1118,57 @@ merge(Compressor.prototype, {
return self;
});
function if_break_in_loop(self, compressor) {
function drop_it(rest) {
rest = as_statement_array(rest);
if (self.body instanceof AST_BlockStatement) {
self.body = self.body.clone();
self.body.body = rest.concat(self.body.body.slice(1));
self.body = self.body.transform(compressor);
} else {
self.body = make_node(AST_BlockStatement, self.body, {
body: rest
}).transform(compressor);
}
if_break_in_loop(self, compressor);
}
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
if (first instanceof AST_If) {
if (first.body instanceof AST_Break
&& compressor.loopcontrol_target(first.body.label) === self) {
if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, {
left: self.condition,
operator: "&&",
right: first.condition.negate(compressor),
});
} else {
self.condition = first.condition.negate(compressor);
}
drop_it(first.alternative);
}
else if (first.alternative instanceof AST_Break
&& compressor.loopcontrol_target(first.alternative.label) === self) {
if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, {
left: self.condition,
operator: "&&",
right: first.condition,
});
} else {
self.condition = first.condition;
}
drop_it(first.body);
}
}
};
OPT(AST_While, function(self, compressor) {
if (!compressor.option("loops")) return self;
self = AST_DWLoop.prototype.optimize.call(self, compressor);
if (self instanceof AST_While) {
self = make_node(AST_For, self, self);
if_break_in_loop(self, compressor);
self = make_node(AST_For, self, self).transform(compressor);
}
return self;
});
@@ -1151,6 +1197,7 @@ merge(Compressor.prototype, {
}
}
}
if_break_in_loop(self, compressor);
return self;
});

123
test/compress/loops.js Normal file
View File

@@ -0,0 +1,123 @@
while_becomes_for: {
options = { loops: true };
input: {
while (foo()) bar();
}
expect: {
for (; foo(); ) bar();
}
}
drop_if_break_1: {
options = { loops: true };
input: {
for (;;)
if (foo()) break;
}
expect: {
for (; !foo(););
}
}
drop_if_break_2: {
options = { loops: true };
input: {
for (;bar();)
if (foo()) break;
}
expect: {
for (; bar() && !foo(););
}
}
drop_if_break_3: {
options = { loops: true };
input: {
for (;bar();) {
if (foo()) break;
stuff1();
stuff2();
}
}
expect: {
for (; bar() && !foo();) {
stuff1();
stuff2();
}
}
}
drop_if_break_4: {
options = { loops: true, sequences: true };
input: {
for (;bar();) {
x();
y();
if (foo()) break;
z();
k();
}
}
expect: {
for (; bar() && (x(), y(), !foo());) z(), k();
}
}
drop_if_else_break_1: {
options = { loops: true };
input: {
for (;;) if (foo()) bar(); else break;
}
expect: {
for (; foo(); ) bar();
}
}
drop_if_else_break_2: {
options = { loops: true };
input: {
for (;bar();) {
if (foo()) baz();
else break;
}
}
expect: {
for (; bar() && foo();) baz();
}
}
drop_if_else_break_3: {
options = { loops: true };
input: {
for (;bar();) {
if (foo()) baz();
else break;
stuff1();
stuff2();
}
}
expect: {
for (; bar() && foo();) {
baz();
stuff1();
stuff2();
}
}
}
drop_if_else_break_4: {
options = { loops: true, sequences: true };
input: {
for (;bar();) {
x();
y();
if (foo()) baz();
else break;
z();
k();
}
}
expect: {
for (; bar() && (x(), y(), foo());) baz(), z(), k();
}
}