[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:
parent
1df7377477
commit
77c19034f4
@ -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);
|
||||
|
@ -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++) {
|
||||
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user