[Interpreter] Removes unnecessary jumps and dead code from If and loops.

Adds an optimization to not emit unnecessary jumps and dead code in If,
For, While, and do-while statments. When the value of condition is known
at compile time, the code is emitted only for the paths that can be taken.
For example, when the condition is known to be true in an if statmenet
only then block is generated.

BUG=v8:4280
LOG=N

Review URL: https://codereview.chromium.org/1414193006

Cr-Commit-Position: refs/heads/master@{#31715}
This commit is contained in:
mythria 2015-11-02 07:24:25 -08:00 committed by Commit bot
parent 1df7377477
commit 77c19034f4
3 changed files with 335 additions and 119 deletions

View File

@ -519,23 +519,28 @@ void BytecodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) {
void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
// TODO(oth): Spot easy cases where there code would not need to
// emit the then block or the else block, e.g. condition is
// obviously true/1/false/0.
BytecodeLabel else_label, end_label;
VisitForAccumulatorValue(stmt->condition());
builder()->JumpIfFalse(&else_label);
Visit(stmt->then_statement());
if (stmt->HasElseStatement()) {
builder()->Jump(&end_label);
builder()->Bind(&else_label);
Visit(stmt->else_statement());
if (stmt->condition()->ToBooleanIsTrue()) {
// Generate only then block.
Visit(stmt->then_statement());
} else if (stmt->condition()->ToBooleanIsFalse()) {
// Generate only else block if it exists.
if (stmt->HasElseStatement()) {
Visit(stmt->else_statement());
}
} else {
builder()->Bind(&else_label);
VisitForAccumulatorValue(stmt->condition());
builder()->JumpIfFalse(&else_label);
Visit(stmt->then_statement());
if (stmt->HasElseStatement()) {
builder()->Jump(&end_label);
builder()->Bind(&else_label);
Visit(stmt->else_statement());
} else {
builder()->Bind(&else_label);
}
builder()->Bind(&end_label);
}
builder()->Bind(&end_label);
}
@ -624,15 +629,26 @@ void BytecodeGenerator::VisitCaseClause(CaseClause* clause) {
void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
LoopBuilder loop_builder(builder());
ControlScopeForIteration execution_control(this, stmt, &loop_builder);
BytecodeLabel body_label, condition_label, done_label;
builder()->Bind(&body_label);
Visit(stmt->body());
builder()->Bind(&condition_label);
VisitForAccumulatorValue(stmt->cond());
builder()->JumpIfTrue(&body_label);
builder()->Bind(&done_label);
if (stmt->cond()->ToBooleanIsFalse()) {
Visit(stmt->body());
// Bind condition_label and done_label for processing continue and break.
builder()->Bind(&condition_label);
builder()->Bind(&done_label);
} else {
builder()->Bind(&body_label);
Visit(stmt->body());
builder()->Bind(&condition_label);
if (stmt->cond()->ToBooleanIsTrue()) {
builder()->Jump(&body_label);
} else {
VisitForAccumulatorValue(stmt->cond());
builder()->JumpIfTrue(&body_label);
}
builder()->Bind(&done_label);
}
loop_builder.SetBreakTarget(done_label);
loop_builder.SetContinueTarget(condition_label);
}
@ -643,12 +659,24 @@ void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
ControlScopeForIteration execution_control(this, stmt, &loop_builder);
BytecodeLabel body_label, condition_label, done_label;
builder()->Jump(&condition_label);
if (stmt->cond()->ToBooleanIsFalse()) {
// If the condition is false there is no need to generating the loop.
return;
}
if (!stmt->cond()->ToBooleanIsTrue()) {
builder()->Jump(&condition_label);
}
builder()->Bind(&body_label);
Visit(stmt->body());
builder()->Bind(&condition_label);
VisitForAccumulatorValue(stmt->cond());
builder()->JumpIfTrue(&body_label);
if (stmt->cond()->ToBooleanIsTrue()) {
builder()->Jump(&body_label);
} else {
VisitForAccumulatorValue(stmt->cond());
builder()->JumpIfTrue(&body_label);
}
builder()->Bind(&done_label);
loop_builder.SetBreakTarget(done_label);
@ -664,8 +692,14 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
Visit(stmt->init());
}
if (stmt->cond() && stmt->cond()->ToBooleanIsFalse()) {
// If the condition is known to be false there is no need to generate
// body, next or condition blocks. Init block should be generated.
return;
}
BytecodeLabel body_label, condition_label, next_label, done_label;
if (stmt->cond() != nullptr) {
if (stmt->cond() && !stmt->cond()->ToBooleanIsTrue()) {
builder()->Jump(&condition_label);
}
builder()->Bind(&body_label);
@ -674,7 +708,7 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
if (stmt->next() != nullptr) {
Visit(stmt->next());
}
if (stmt->cond()) {
if (stmt->cond() && !stmt->cond()->ToBooleanIsTrue()) {
builder()->Bind(&condition_label);
VisitForAccumulatorValue(stmt->cond());
builder()->JumpIfTrue(&body_label);

View File

@ -1605,47 +1605,39 @@ TEST(IfConditions) {
{"function f() { if (0) { return 1; } else { return -1; } } f()",
0,
1,
13,
{B(LdaZero), //
B(JumpIfToBooleanFalse), U8(7), //
B(LdaSmi8), U8(1), //
B(Return), //
B(Jump), U8(5), //
B(LdaSmi8), U8(-1), //
B(Return), //
B(LdaUndefined), //
B(Return)}, //
5,
{B(LdaSmi8), U8(-1), //
B(Return), //
B(LdaUndefined), // TODO(mythria) redundant return statement
B(Return)}, // could be eliminated.
0,
{unused, unused, unused, unused, unused, unused}},
{"function f() { if ('lucky') { return 1; } else { return -1; } } f();",
0,
1,
14,
{B(LdaConstant), U8(0), //
B(JumpIfToBooleanFalse), U8(7), //
B(LdaSmi8), U8(1), //
B(Return), //
B(Jump), U8(5), //
B(LdaSmi8), U8(-1), //
B(Return), //
B(LdaUndefined), //
B(Return)}, //
1,
{helper.factory()->NewStringFromStaticChars("lucky"), unused, unused,
unused, unused, unused}},
5,
{B(LdaSmi8), U8(1), //
B(Return), //
B(LdaUndefined), // TODO(mythria) redundant return statement
B(Return)}, // could be eliminated.
0,
{unused, unused, unused, unused, unused, unused}},
{"function f() { if (false) { return 1; } else { return -1; } } f();",
0,
1,
13,
{B(LdaFalse), //
B(JumpIfFalse), U8(7), //
B(LdaSmi8), U8(1), //
B(Return), //
B(Jump), U8(5), //
B(LdaSmi8), U8(-1), //
B(Return), //
B(LdaUndefined), //
B(Return)}, //
5,
{B(LdaSmi8), U8(-1), //
B(Return), //
B(LdaUndefined), // TODO(mythria) redundant return statement
B(Return)}, // could be eliminated.
0,
{unused, unused, unused, unused, unused, unused}},
{"function f() { if (false) { return 1; } } f();",
0,
1,
2,
{B(LdaUndefined), //
B(Return)},
0,
{unused, unused, unused, unused, unused, unused}},
{"function f(a) { if (a <= 0) { return 200; } else { return -200; } }"
@ -1777,6 +1769,32 @@ TEST(IfConditions) {
#undef IF_CONDITION_RETURN
0,
{unused, unused, unused, unused, unused, unused}},
{"function f() {"
" var a = 0;"
" if (a) {"
" return 20;"
"} else {"
" return -20;}"
"};"
"f();",
1 * kPointerSize,
1,
17,
{
B(LdaZero), //
B(Star), R(0), //
B(Ldar), R(0), //
B(JumpIfToBooleanFalse), U8(7), //
B(LdaSmi8), U8(20), //
B(Return), //
B(Jump), U8(5), //
B(LdaSmi8), U8(-20), //
B(Return), //
B(LdaUndefined), //
B(Return)
},
0,
{unused, unused, unused, unused, unused, unused}}
};
for (size_t i = 0; i < arraysize(snippets); i++) {
@ -1952,38 +1970,36 @@ TEST(BasicLoops) {
"return i;",
1 * kPointerSize,
1,
56,
53,
{
B(LdaZero), //
B(Star), R(0), //
B(Jump), U8(47), //
B(LdaZero), //
B(TestLessThan), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(40), //
B(LdaSmi8), U8(3), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(35), //
B(LdaSmi8), U8(4), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(27), //
B(LdaSmi8), U8(10), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(16), //
B(LdaSmi8), U8(5), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(11), //
B(LdaSmi8), U8(1), //
B(Add), R(0), //
B(Star), R(0), //
B(LdaTrue), //
B(JumpIfTrue), U8(-46), //
B(Ldar), R(0), //
B(Return), //
B(LdaZero), //
B(Star), R(0), //
B(LdaZero), //
B(TestLessThan), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(40), //
B(LdaSmi8), U8(3), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(34), //
B(LdaSmi8), U8(4), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(26), //
B(LdaSmi8), U8(10), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(16), //
B(LdaSmi8), U8(5), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(10), //
B(LdaSmi8), U8(1), //
B(Add), R(0), //
B(Star), R(0), //
B(Jump), U8(-45), //
B(Ldar), R(0), //
B(Return), //
},
0},
{"var x = 0; var y = 1;"
@ -2086,11 +2102,10 @@ TEST(BasicLoops) {
"return i;",
1 * kPointerSize,
1,
41,
38,
{
B(LdaZero), //
B(Star), R(0), //
B(Jump), U8(32), //
B(Jump), U8(16), //
B(LdaSmi8), U8(2), //
B(TestEqual), R(0), //
@ -2105,9 +2120,8 @@ TEST(BasicLoops) {
B(LdaSmi8), U8(1), //
B(Add), R(0), //
B(Star), R(0), //
B(Jump), U8(5), //
B(LdaTrue), //
B(JumpIfTrue), U8(-31), //
B(Jump), U8(4), //
B(Jump), U8(-30), //
B(Ldar), R(0), //
B(Return), //
},
@ -2139,8 +2153,8 @@ TEST(BasicLoops) {
B(Ldar), R(1), //
B(Return), //
},
0},
{"var x = 10;"
0},
{"var x = 10;"
"var y = 1;"
"do {"
" y = y * 12;"
@ -2166,16 +2180,16 @@ TEST(BasicLoops) {
B(Ldar), R(1), //
B(Return), //
},
0},
{"var y = 1;"
"for (var x = 10; x; --x) {"
" y = y * 12;"
"}"
"return y;",
2 * kPointerSize,
1,
29,
{
0},
{"var y = 1;"
"for (var x = 10; x; --x) {"
" y = y * 12;"
"}"
"return y;",
2 * kPointerSize,
1,
29,
{
B(LdaSmi8), U8(1), //
B(Star), R(0), //
B(LdaSmi8), U8(10), //
@ -2192,8 +2206,140 @@ TEST(BasicLoops) {
B(JumpIfToBooleanTrue), U8(-14), //
B(Ldar), R(0), //
B(Return), //
},
0}};
},
0},
{"var x = 0; var y = 1;"
"do {"
" y = y * 10;"
" if (x == 5) break;"
" x = x + 1;"
" if (x == 6) continue;"
"} while (false);"
"return y;",
2 * kPointerSize,
1,
38,
{
B(LdaZero), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(LdaSmi8), U8(10), //
B(Mul), R(1), //
B(Star), R(1), //
B(LdaSmi8), U8(5), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(16), //
B(LdaSmi8), U8(1), //
B(Add), R(0), //
B(Star), R(0), //
B(LdaSmi8), U8(6), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(2), //
B(Ldar), R(1), //
B(Return), //
},
0},
{"var x = 0; var y = 1;"
"do {"
" y = y * 10;"
" if (x == 5) break;"
" x = x + 1;"
" if (x == 6) continue;"
"} while (true);"
"return y;",
2 * kPointerSize,
1,
40,
{
B(LdaZero), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(LdaSmi8), U8(10), //
B(Mul), R(1), //
B(Star), R(1), //
B(LdaSmi8), U8(5), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(18), //
B(LdaSmi8), U8(1), //
B(Add), R(0), //
B(Star), R(0), //
B(LdaSmi8), U8(6), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(2), //
B(Jump), U8(-28), //
B(Ldar), R(1), //
B(Return), //
},
0},
{"var x = 0;"
"while(false) {"
" x = x + 1;"
"};"
"return x;",
1 * kPointerSize,
1,
6,
{
B(LdaZero), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Return), //
},
0},
{"var x = 0;"
"for( var i = 0; false; i++) {"
" x = x + 1;"
"};"
"return x;",
2 * kPointerSize,
1,
9,
{
B(LdaZero), //
B(Star), R(0), //
B(LdaZero), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Return), //
},
0},
{"var x = 0;"
"for( var i = 0; true; ++i) {"
" x = x + 1;"
" if (x == 20) break;"
"};"
"return x;",
2 * kPointerSize,
1,
31,
{
B(LdaZero), //
B(Star), R(0), //
B(LdaZero), //
B(Star), R(1), //
B(LdaSmi8), U8(1), //
B(Add), R(0), //
B(Star), R(0), //
B(LdaSmi8), U8(20), //
B(TestEqual), R(0), //
B(JumpIfFalse), U8(4), //
B(Jump), U8(10), //
B(Ldar), R(1), //
B(ToNumber), //
B(Inc), //
B(Star), R(1), //
B(Jump), U8(-20), //
B(Ldar), R(0), //
B(Return), //
},
0},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
@ -3294,17 +3440,15 @@ TEST(Throw) {
{"if ('test') { throw 'Error'; };",
0,
1,
9,
5,
{
B(LdaConstant), U8(0), //
B(JumpIfToBooleanFalse), U8(5), //
B(LdaConstant), U8(1), //
B(Throw), //
B(LdaUndefined), //
B(Return), //
B(LdaConstant), U8(0), //
B(Throw), //
B(LdaUndefined), //
B(Return), //
},
2,
{"test", "Error"}},
1,
{"Error"}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {

View File

@ -2577,7 +2577,45 @@ TEST(InterpreterBasicLoops) {
" b *= 2;\n"
"}\n"
"return b;",
factory->NewHeapNumber(1024))};
factory->NewHeapNumber(1024)),
std::make_pair("var a = 10; var b = 1;\n"
"while (false) {\n"
" b = b * 2;\n"
" a = a - 1;\n"
"}\n"
"return b;\n",
Handle<Object>(Smi::FromInt(1), isolate)),
std::make_pair("var a = 10; var b = 1;\n"
"while (true) {\n"
" b = b * 2;\n"
" a = a - 1;\n"
" if (a == 0) break;"
" continue;"
"}\n"
"return b;\n",
factory->NewHeapNumber(1024)),
std::make_pair("var a = 10; var b = 1;\n"
"do {\n"
" b = b * 2;\n"
" a = a - 1;\n"
" if (a == 0) break;"
"} while(true);\n"
"return b;\n",
factory->NewHeapNumber(1024)),
std::make_pair("var a = 10; var b = 1;\n"
"do {\n"
" b = b * 2;\n"
" a = a - 1;\n"
" if (a == 0) break;"
"} while(false);\n"
"return b;\n",
Handle<Object>(Smi::FromInt(2), isolate)),
std::make_pair("var a = 10; var b = 1;\n"
"for ( a = 1, b = 30; false; ) {\n"
" b = b * 2;\n"
"}\n"
"return b;\n",
Handle<Object>(Smi::FromInt(30), isolate))};
for (size_t i = 0; i < arraysize(loops); i++) {
std::string source(InterpreterTester::SourceForBody(loops[i].first));