[Interpreter] Merges ToBoolean and JumpIfTrue/False bytecodes
Adds an optimization to emit JumpIfToBooleanTrue/False instead of ToBoolean followed by JumpIfTrue/False if the value in the accumulator is not boolean. BUG=v8:4280 LOG=N Review URL: https://codereview.chromium.org/1426913002 Cr-Commit-Position: refs/heads/master@{#31697}
This commit is contained in:
parent
7153c7f48c
commit
e66d4f8736
@ -480,33 +480,43 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) {
|
||||
}
|
||||
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToBoolean() {
|
||||
if (LastBytecodeInSameBlock()) {
|
||||
// If the previous bytecode puts a boolean in the accumulator
|
||||
// there is no need to emit an instruction.
|
||||
switch (Bytecodes::FromByte(bytecodes()->at(last_bytecode_start_))) {
|
||||
case Bytecode::kToBoolean:
|
||||
UNREACHABLE();
|
||||
case Bytecode::kLdaTrue:
|
||||
case Bytecode::kLdaFalse:
|
||||
case Bytecode::kLogicalNot:
|
||||
case Bytecode::kTestEqual:
|
||||
case Bytecode::kTestNotEqual:
|
||||
case Bytecode::kTestEqualStrict:
|
||||
case Bytecode::kTestNotEqualStrict:
|
||||
case Bytecode::kTestLessThan:
|
||||
case Bytecode::kTestLessThanOrEqual:
|
||||
case Bytecode::kTestGreaterThan:
|
||||
case Bytecode::kTestGreaterThanOrEqual:
|
||||
case Bytecode::kTestInstanceOf:
|
||||
case Bytecode::kTestIn:
|
||||
return *this;
|
||||
default:
|
||||
// Fall through to output kToBoolean.
|
||||
break;
|
||||
}
|
||||
bool BytecodeArrayBuilder::NeedToBooleanCast() {
|
||||
if (!LastBytecodeInSameBlock()) {
|
||||
// If the previous bytecode was from a different block return false.
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the previous bytecode puts a boolean in the accumulator return true.
|
||||
switch (Bytecodes::FromByte(bytecodes()->at(last_bytecode_start_))) {
|
||||
case Bytecode::kToBoolean:
|
||||
UNREACHABLE();
|
||||
case Bytecode::kLdaTrue:
|
||||
case Bytecode::kLdaFalse:
|
||||
case Bytecode::kLogicalNot:
|
||||
case Bytecode::kTestEqual:
|
||||
case Bytecode::kTestNotEqual:
|
||||
case Bytecode::kTestEqualStrict:
|
||||
case Bytecode::kTestNotEqualStrict:
|
||||
case Bytecode::kTestLessThan:
|
||||
case Bytecode::kTestLessThanOrEqual:
|
||||
case Bytecode::kTestGreaterThan:
|
||||
case Bytecode::kTestGreaterThanOrEqual:
|
||||
case Bytecode::kTestInstanceOf:
|
||||
case Bytecode::kTestIn:
|
||||
case Bytecode::kForInDone:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToBoolean() {
|
||||
// If the previous bytecode puts a boolean in the accumulator
|
||||
// there is no need to emit an instruction.
|
||||
if (NeedToBooleanCast()) {
|
||||
Output(Bytecode::kToBoolean);
|
||||
}
|
||||
Output(Bytecode::kToBoolean);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -613,8 +623,32 @@ void BytecodeArrayBuilder::PatchJump(
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
Bytecode BytecodeArrayBuilder::GetJumpWithToBoolean(Bytecode jump_bytecode) {
|
||||
switch (jump_bytecode) {
|
||||
case Bytecode::kJump:
|
||||
case Bytecode::kJumpIfNull:
|
||||
case Bytecode::kJumpIfUndefined:
|
||||
return jump_bytecode;
|
||||
case Bytecode::kJumpIfTrue:
|
||||
return Bytecode::kJumpIfToBooleanTrue;
|
||||
case Bytecode::kJumpIfFalse:
|
||||
return Bytecode::kJumpIfToBooleanFalse;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return static_cast<Bytecode>(-1);
|
||||
}
|
||||
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode,
|
||||
BytecodeLabel* label) {
|
||||
// Check if the value in accumulator is boolean, if not choose an
|
||||
// appropriate JumpIfToBoolean bytecode.
|
||||
if (NeedToBooleanCast()) {
|
||||
jump_bytecode = GetJumpWithToBoolean(jump_bytecode);
|
||||
}
|
||||
|
||||
int delta;
|
||||
if (label->is_bound()) {
|
||||
// Label has been bound already so this is a backwards jump.
|
||||
@ -659,18 +693,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfFalse(BytecodeLabel* label) {
|
||||
}
|
||||
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfToBooleanTrue(
|
||||
BytecodeLabel* label) {
|
||||
return OutputJump(Bytecode::kJumpIfToBooleanTrue, label);
|
||||
}
|
||||
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfToBooleanFalse(
|
||||
BytecodeLabel* label) {
|
||||
return OutputJump(Bytecode::kJumpIfToBooleanFalse, label);
|
||||
}
|
||||
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNull(BytecodeLabel* label) {
|
||||
return OutputJump(Bytecode::kJumpIfNull, label);
|
||||
}
|
||||
|
@ -183,11 +183,6 @@ class BytecodeArrayBuilder {
|
||||
BytecodeArrayBuilder& JumpIfFalse(BytecodeLabel* label);
|
||||
BytecodeArrayBuilder& JumpIfNull(BytecodeLabel* label);
|
||||
BytecodeArrayBuilder& JumpIfUndefined(BytecodeLabel* label);
|
||||
// TODO(mythria) The following two functions should be merged into
|
||||
// JumpIfTrue/False. These bytecodes should be automatically chosen rather
|
||||
// than explicitly using them.
|
||||
BytecodeArrayBuilder& JumpIfToBooleanTrue(BytecodeLabel* label);
|
||||
BytecodeArrayBuilder& JumpIfToBooleanFalse(BytecodeLabel* label);
|
||||
|
||||
BytecodeArrayBuilder& Throw();
|
||||
BytecodeArrayBuilder& Return();
|
||||
@ -228,6 +223,7 @@ class BytecodeArrayBuilder {
|
||||
static bool FitsInIdx16Operand(size_t value);
|
||||
|
||||
static Bytecode GetJumpWithConstantOperand(Bytecode jump_with_smi8_operand);
|
||||
static Bytecode GetJumpWithToBoolean(Bytecode jump);
|
||||
|
||||
template <size_t N>
|
||||
INLINE(void Output(Bytecode bytecode, uint32_t(&oprands)[N]));
|
||||
@ -248,6 +244,8 @@ class BytecodeArrayBuilder {
|
||||
uint32_t operand_value) const;
|
||||
bool LastBytecodeInSameBlock() const;
|
||||
|
||||
bool NeedToBooleanCast();
|
||||
|
||||
int BorrowTemporaryRegister();
|
||||
void ReturnTemporaryRegister(int reg_index);
|
||||
int PrepareForConsecutiveTemporaryRegisters(size_t count);
|
||||
|
@ -526,7 +526,6 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
|
||||
BytecodeLabel else_label, end_label;
|
||||
|
||||
VisitForAccumulatorValue(stmt->condition());
|
||||
builder()->CastAccumulatorToBoolean();
|
||||
builder()->JumpIfFalse(&else_label);
|
||||
Visit(stmt->then_statement());
|
||||
if (stmt->HasElseStatement()) {
|
||||
@ -869,7 +868,6 @@ void BytecodeGenerator::VisitConditional(Conditional* expr) {
|
||||
BytecodeLabel else_label, end_label;
|
||||
|
||||
VisitForAccumulatorValue(expr->condition());
|
||||
builder()->CastAccumulatorToBoolean();
|
||||
builder()->JumpIfFalse(&else_label);
|
||||
|
||||
VisitForAccumulatorValue(expr->then_expression());
|
||||
@ -1869,7 +1867,7 @@ void BytecodeGenerator::VisitLogicalOrExpression(BinaryOperation* binop) {
|
||||
} else {
|
||||
BytecodeLabel end_label;
|
||||
VisitForAccumulatorValue(left);
|
||||
builder()->JumpIfToBooleanTrue(&end_label);
|
||||
builder()->JumpIfTrue(&end_label);
|
||||
VisitForAccumulatorValue(right);
|
||||
builder()->Bind(&end_label);
|
||||
}
|
||||
@ -1888,7 +1886,7 @@ void BytecodeGenerator::VisitLogicalAndExpression(BinaryOperation* binop) {
|
||||
} else {
|
||||
BytecodeLabel end_label;
|
||||
VisitForAccumulatorValue(left);
|
||||
builder()->JumpIfToBooleanFalse(&end_label);
|
||||
builder()->JumpIfFalse(&end_label);
|
||||
VisitForAccumulatorValue(right);
|
||||
builder()->Bind(&end_label);
|
||||
}
|
||||
|
@ -415,6 +415,18 @@ TEST(LogicalExpressions) {
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Return)},
|
||||
0},
|
||||
{"var x = 0; return (x == 1) || 3;",
|
||||
1 * kPointerSize,
|
||||
1,
|
||||
12,
|
||||
{B(LdaZero), //
|
||||
B(Star), R(0), //
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(TestEqual), R(0), //
|
||||
B(JumpIfTrue), U8(4), //
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Return)},
|
||||
0},
|
||||
{"var x = 0; return x && 3;",
|
||||
1 * kPointerSize,
|
||||
1,
|
||||
@ -426,6 +438,18 @@ TEST(LogicalExpressions) {
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Return)},
|
||||
0},
|
||||
{"var x = 0; return (x == 0) && 3;",
|
||||
1 * kPointerSize,
|
||||
1,
|
||||
11,
|
||||
{B(LdaZero), //
|
||||
B(Star), R(0), //
|
||||
B(LdaZero), //
|
||||
B(TestEqual), R(0), //
|
||||
B(JumpIfFalse), U8(4), //
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Return)},
|
||||
0},
|
||||
{"var x = 0; return x || (1, 2, 3);",
|
||||
1 * kPointerSize,
|
||||
1,
|
||||
@ -500,6 +524,54 @@ TEST(LogicalExpressions) {
|
||||
B(Return)}, //
|
||||
1,
|
||||
{260, 0, 0, 0}},
|
||||
{"var x = 1; var a = 2, b = 3; return (x > 3) || ("
|
||||
REPEAT_32(SPACE, "a = 1, b = 2, ")
|
||||
"3);",
|
||||
3 * kPointerSize,
|
||||
1,
|
||||
277,
|
||||
{B(LdaSmi8), U8(1), //
|
||||
B(Star), R(0), //
|
||||
B(LdaSmi8), U8(2), //
|
||||
B(Star), R(1), //
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Star), R(2), //
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(TestGreaterThan), R(0), //
|
||||
B(JumpIfTrueConstant), U8(0), //
|
||||
REPEAT_32(COMMA, //
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(Star), R(1), //
|
||||
B(LdaSmi8), U8(2), //
|
||||
B(Star), R(2)), //
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Return)},
|
||||
1,
|
||||
{260, 0, 0, 0}},
|
||||
{"var x = 0; var a = 2, b = 3; return (x < 5) && ("
|
||||
REPEAT_32(SPACE, "a = 1, b = 2, ")
|
||||
"3);",
|
||||
3 * kPointerSize,
|
||||
1,
|
||||
276,
|
||||
{B(LdaZero), //
|
||||
B(Star), R(0), //
|
||||
B(LdaSmi8), U8(2), //
|
||||
B(Star), R(1), //
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Star), R(2), //
|
||||
B(LdaSmi8), U8(5), //
|
||||
B(TestLessThan), R(0), //
|
||||
B(JumpIfFalseConstant), U8(0), //
|
||||
REPEAT_32(COMMA, //
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(Star), R(1), //
|
||||
B(LdaSmi8), U8(2), //
|
||||
B(Star), R(2)), //
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Return)},
|
||||
1,
|
||||
{260, 0, 0, 0}},
|
||||
{"return 0 && 3;",
|
||||
0 * kPointerSize,
|
||||
1,
|
||||
@ -1533,33 +1605,31 @@ TEST(IfConditions) {
|
||||
{"function f() { if (0) { return 1; } else { return -1; } } f()",
|
||||
0,
|
||||
1,
|
||||
14,
|
||||
{B(LdaZero), //
|
||||
B(ToBoolean), //
|
||||
B(JumpIfFalse), U8(7), //
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(Return), //
|
||||
B(Jump), U8(5), //
|
||||
B(LdaSmi8), U8(-1), //
|
||||
B(Return), //
|
||||
B(LdaUndefined), //
|
||||
B(Return)}, //
|
||||
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)}, //
|
||||
0,
|
||||
{unused, unused, unused, unused, unused, unused}},
|
||||
{"function f() { if ('lucky') { return 1; } else { return -1; } } f();",
|
||||
0,
|
||||
1,
|
||||
15,
|
||||
{B(LdaConstant), U8(0), //
|
||||
B(ToBoolean), //
|
||||
B(JumpIfFalse), U8(7), //
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(Return), //
|
||||
B(Jump), U8(5), //
|
||||
B(LdaSmi8), U8(-1), //
|
||||
B(Return), //
|
||||
B(LdaUndefined), //
|
||||
B(Return)}, //
|
||||
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}},
|
||||
@ -1643,6 +1713,36 @@ TEST(IfConditions) {
|
||||
helper.factory()->NewNumberFromInt(200),
|
||||
helper.factory()->NewNumberFromInt(263),
|
||||
helper.factory()->NewNumberFromInt(-200), unused, unused}},
|
||||
{"function f() { var a = 0; var b = 0; if (a) { "
|
||||
REPEAT_32(SPACE, "b = a; a = b; ")
|
||||
" return 200; } else { return -200; } } f()",
|
||||
2 * kPointerSize,
|
||||
1,
|
||||
276,
|
||||
{
|
||||
B(LdaZero), //
|
||||
B(Star), R(0), //
|
||||
B(LdaZero), //
|
||||
B(Star), R(1), //
|
||||
B(Ldar), R(0), //
|
||||
B(JumpIfToBooleanFalseConstant), U8(1), //
|
||||
REPEAT_32(COMMA, //
|
||||
B(Ldar), R(0), //
|
||||
B(Star), R(1), //
|
||||
B(Ldar), R(1), //
|
||||
B(Star), R(0)), //
|
||||
B(LdaConstant), U8(0), //
|
||||
B(Return), //
|
||||
B(Jump), U8(5), //
|
||||
B(LdaConstant), U8(2), //
|
||||
B(Return), //
|
||||
B(LdaUndefined), //
|
||||
B(Return)}, //
|
||||
3,
|
||||
{helper.factory()->NewNumberFromInt(200),
|
||||
helper.factory()->NewNumberFromInt(263),
|
||||
helper.factory()->NewNumberFromInt(-200), unused, unused, unused}},
|
||||
|
||||
{"function f(a, b) {\n"
|
||||
" if (a == b) { return 1; }\n"
|
||||
" if (a === b) { return 1; }\n"
|
||||
@ -2012,7 +2112,88 @@ TEST(BasicLoops) {
|
||||
B(Return), //
|
||||
},
|
||||
0},
|
||||
};
|
||||
{"var x = 10;"
|
||||
"var y = 1;"
|
||||
"while (x) {"
|
||||
" y = y * 12;"
|
||||
" x = x - 1;"
|
||||
"}"
|
||||
"return y;",
|
||||
2 * kPointerSize,
|
||||
1,
|
||||
29,
|
||||
{
|
||||
B(LdaSmi8), U8(10), //
|
||||
B(Star), R(0), //
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(Star), R(1), //
|
||||
B(Jump), U8(14), //
|
||||
B(LdaSmi8), U8(12), //
|
||||
B(Mul), R(1), //
|
||||
B(Star), R(1), //
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(Sub), R(0), //
|
||||
B(Star), R(0), //
|
||||
B(Ldar), R(0), //
|
||||
B(JumpIfToBooleanTrue), U8(-14), //
|
||||
B(Ldar), R(1), //
|
||||
B(Return), //
|
||||
},
|
||||
0},
|
||||
{"var x = 10;"
|
||||
"var y = 1;"
|
||||
"do {"
|
||||
" y = y * 12;"
|
||||
" x = x - 1;"
|
||||
"} while(x);"
|
||||
"return y;",
|
||||
2 * kPointerSize,
|
||||
1,
|
||||
27,
|
||||
{
|
||||
B(LdaSmi8), U8(10), //
|
||||
B(Star), R(0), //
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(Star), R(1), //
|
||||
B(LdaSmi8), U8(12), //
|
||||
B(Mul), R(1), //
|
||||
B(Star), R(1), //
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(Sub), R(0), //
|
||||
B(Star), R(0), //
|
||||
B(Ldar), R(0), //
|
||||
B(JumpIfToBooleanTrue), U8(-14), //
|
||||
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,
|
||||
{
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(Star), R(0), //
|
||||
B(LdaSmi8), U8(10), //
|
||||
B(Star), R(1), //
|
||||
B(Jump), U8(14), //
|
||||
B(LdaSmi8), U8(12), //
|
||||
B(Mul), R(0), //
|
||||
B(Star), R(0), //
|
||||
B(Ldar), R(1), //
|
||||
B(ToNumber), //
|
||||
B(Dec), //
|
||||
B(Star), R(1), //
|
||||
B(Ldar), R(1), //
|
||||
B(JumpIfToBooleanTrue), U8(-14), //
|
||||
B(Ldar), R(0), //
|
||||
B(Return), //
|
||||
},
|
||||
0}};
|
||||
|
||||
for (size_t i = 0; i < arraysize(snippets); i++) {
|
||||
Handle<BytecodeArray> bytecode_array =
|
||||
@ -3113,15 +3294,14 @@ TEST(Throw) {
|
||||
{"if ('test') { throw 'Error'; };",
|
||||
0,
|
||||
1,
|
||||
10,
|
||||
9,
|
||||
{
|
||||
B(LdaConstant), U8(0), //
|
||||
B(ToBoolean), //
|
||||
B(JumpIfFalse), U8(5), //
|
||||
B(LdaConstant), U8(1), //
|
||||
B(Throw), //
|
||||
B(LdaUndefined), //
|
||||
B(Return), //
|
||||
B(LdaConstant), U8(0), //
|
||||
B(JumpIfToBooleanFalse), U8(5), //
|
||||
B(LdaConstant), U8(1), //
|
||||
B(Throw), //
|
||||
B(LdaUndefined), //
|
||||
B(Return), //
|
||||
},
|
||||
2,
|
||||
{"test", "Error"}},
|
||||
@ -4309,33 +4489,30 @@ TEST(Conditional) {
|
||||
{"return 1 ? 2 : 3;",
|
||||
0,
|
||||
1,
|
||||
12,
|
||||
11,
|
||||
{
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(ToBoolean), //
|
||||
B(JumpIfFalse), U8(6), //
|
||||
B(LdaSmi8), U8(2), //
|
||||
B(Jump), U8(4), //
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Return), //
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(JumpIfToBooleanFalse), U8(6), //
|
||||
B(LdaSmi8), U8(2), //
|
||||
B(Jump), U8(4), //
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Return), //
|
||||
}},
|
||||
{"return 1 ? 2 ? 3 : 4 : 5;",
|
||||
0,
|
||||
1,
|
||||
21,
|
||||
19,
|
||||
{
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(ToBoolean), //
|
||||
B(JumpIfFalse), U8(15), //
|
||||
B(LdaSmi8), U8(2), //
|
||||
B(ToBoolean), //
|
||||
B(JumpIfFalse), U8(6), //
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Jump), U8(4), //
|
||||
B(LdaSmi8), U8(4), //
|
||||
B(Jump), U8(4), //
|
||||
B(LdaSmi8), U8(5), //
|
||||
B(Return), //
|
||||
B(LdaSmi8), U8(1), //
|
||||
B(JumpIfToBooleanFalse), U8(14), //
|
||||
B(LdaSmi8), U8(2), //
|
||||
B(JumpIfToBooleanFalse), U8(6), //
|
||||
B(LdaSmi8), U8(3), //
|
||||
B(Jump), U8(4), //
|
||||
B(LdaSmi8), U8(4), //
|
||||
B(Jump), U8(4), //
|
||||
B(LdaSmi8), U8(5), //
|
||||
B(Return), //
|
||||
}},
|
||||
};
|
||||
|
||||
|
@ -2027,7 +2027,9 @@ TEST(InterpreterLogicalOr) {
|
||||
factory->NewStringFromStaticChars("0")),
|
||||
std::make_pair("return 0 || 3.2;\n", factory->NewNumber(3.2)),
|
||||
std::make_pair("return 'a' || 0;\n",
|
||||
factory->NewStringFromStaticChars("a"))};
|
||||
factory->NewStringFromStaticChars("a")),
|
||||
std::make_pair("var a = '0', b = 10; return (a == 0) || b;\n",
|
||||
factory->true_value())};
|
||||
|
||||
for (size_t i = 0; i < arraysize(literals); i++) {
|
||||
std::string source(InterpreterTester::SourceForBody(literals[i].first));
|
||||
@ -2052,14 +2054,15 @@ TEST(InterpreterLogicalAnd) {
|
||||
handle(Smi::FromInt(0), isolate)),
|
||||
std::make_pair("var a = '0', b = 10; return a && b;\n",
|
||||
handle(Smi::FromInt(10), isolate)),
|
||||
std::make_pair("return 0.0 && 3.2;\n",
|
||||
handle(Smi::FromInt(0), isolate)),
|
||||
std::make_pair("return 0.0 && 3.2;\n", handle(Smi::FromInt(0), isolate)),
|
||||
std::make_pair("return 'a' && 'b';\n",
|
||||
factory->NewStringFromStaticChars("b")),
|
||||
std::make_pair("return 'a' && 0 || 'b', 'c';\n",
|
||||
factory->NewStringFromStaticChars("c")),
|
||||
std::make_pair("var x = 1, y = 3; return x && 0 + 1 || y;\n",
|
||||
handle(Smi::FromInt(1), isolate))};
|
||||
handle(Smi::FromInt(1), isolate)),
|
||||
std::make_pair("var x = 1, y = 3; return (x == 1) && (3 == 3) || y;\n",
|
||||
factory->true_value())};
|
||||
|
||||
for (size_t i = 0; i < arraysize(literals); i++) {
|
||||
std::string source(InterpreterTester::SourceForBody(literals[i].first));
|
||||
@ -2529,6 +2532,64 @@ TEST(InterpreterGlobalDelete) {
|
||||
}
|
||||
|
||||
|
||||
TEST(InterpreterBasicLoops) {
|
||||
HandleAndZoneScope handles;
|
||||
i::Isolate* isolate = handles.main_isolate();
|
||||
i::Factory* factory = isolate->factory();
|
||||
|
||||
std::pair<const char*, Handle<Object>> loops[] = {
|
||||
std::make_pair("var a = 10; var b = 1;\n"
|
||||
"while (a) {\n"
|
||||
" b = b * 2;\n"
|
||||
" a = a - 1;\n"
|
||||
"};\n"
|
||||
"return b;\n",
|
||||
factory->NewHeapNumber(1024)),
|
||||
std::make_pair("var a = 1; var b = 1;\n"
|
||||
"do {\n"
|
||||
" b = b * 2;\n"
|
||||
" --a;\n"
|
||||
"} while(a);\n"
|
||||
"return b;\n",
|
||||
handle(Smi::FromInt(2), isolate)),
|
||||
std::make_pair("var b = 1;\n"
|
||||
"for ( var a = 10; a; a--) {\n"
|
||||
" b *= 2;\n"
|
||||
"}\n"
|
||||
"return b;",
|
||||
factory->NewHeapNumber(1024)),
|
||||
std::make_pair("var a = 10; var b = 1;\n"
|
||||
"while (a > 0) {\n"
|
||||
" b = b * 2;\n"
|
||||
" a = a - 1;\n"
|
||||
"};\n"
|
||||
"return b;\n",
|
||||
factory->NewHeapNumber(1024)),
|
||||
std::make_pair("var a = 1; var b = 1;\n"
|
||||
"do {\n"
|
||||
" b = b * 2;\n"
|
||||
" --a;\n"
|
||||
"} while(a);\n"
|
||||
"return b;\n",
|
||||
handle(Smi::FromInt(2), isolate)),
|
||||
std::make_pair("var b = 1;\n"
|
||||
"for ( var a = 10; a > 0; a--) {\n"
|
||||
" b *= 2;\n"
|
||||
"}\n"
|
||||
"return b;",
|
||||
factory->NewHeapNumber(1024))};
|
||||
|
||||
for (size_t i = 0; i < arraysize(loops); i++) {
|
||||
std::string source(InterpreterTester::SourceForBody(loops[i].first));
|
||||
InterpreterTester tester(handles.main_isolate(), source.c_str());
|
||||
auto callable = tester.GetCallable<>();
|
||||
|
||||
Handle<i::Object> return_value = callable().ToHandleChecked();
|
||||
CHECK(return_value->SameValue(*loops[i].second));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(InterpreterForIn) {
|
||||
HandleAndZoneScope handles;
|
||||
|
||||
|
@ -150,25 +150,40 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
builder.Bind(&start);
|
||||
// Short jumps with Imm8 operands
|
||||
builder.Jump(&start)
|
||||
.JumpIfTrue(&start)
|
||||
.JumpIfFalse(&start)
|
||||
.JumpIfToBooleanTrue(&start)
|
||||
.JumpIfToBooleanFalse(&start)
|
||||
.JumpIfNull(&start)
|
||||
.JumpIfUndefined(&start);
|
||||
|
||||
// Perform an operation that returns boolean value to
|
||||
// generate JumpIfTrue/False
|
||||
builder.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
||||
.JumpIfTrue(&start)
|
||||
.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
||||
.JumpIfFalse(&start);
|
||||
// Perform an operation that returns a non-boolean operation to
|
||||
// generate JumpIfToBooleanTrue/False.
|
||||
builder.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
||||
.JumpIfTrue(&start)
|
||||
.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
||||
.JumpIfFalse(&start);
|
||||
// Insert dummy ops to force longer jumps
|
||||
for (int i = 0; i < 128; i++) {
|
||||
builder.LoadTrue();
|
||||
}
|
||||
// Longer jumps requiring Constant operand
|
||||
builder.Jump(&start)
|
||||
.JumpIfTrue(&start)
|
||||
.JumpIfFalse(&start)
|
||||
.JumpIfToBooleanTrue(&start)
|
||||
.JumpIfToBooleanFalse(&start)
|
||||
.JumpIfNull(&start)
|
||||
.JumpIfUndefined(&start);
|
||||
// Perform an operation that returns boolean value to
|
||||
// generate JumpIfTrue/False
|
||||
builder.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
||||
.JumpIfTrue(&start)
|
||||
.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
||||
.JumpIfFalse(&start);
|
||||
// Perform an operation that returns a non-boolean operation to
|
||||
// generate JumpIfToBooleanTrue/False.
|
||||
builder.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
||||
.JumpIfTrue(&start)
|
||||
.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
||||
.JumpIfFalse(&start);
|
||||
|
||||
builder.EnterBlock()
|
||||
.Throw()
|
||||
@ -345,68 +360,115 @@ TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
|
||||
|
||||
BytecodeArrayBuilder builder(isolate(), zone());
|
||||
builder.set_parameter_count(0);
|
||||
builder.set_locals_count(0);
|
||||
builder.set_locals_count(1);
|
||||
builder.set_context_count(0);
|
||||
|
||||
BytecodeLabel far0, far1, far2;
|
||||
BytecodeLabel near0, near1, near2;
|
||||
Register reg(0);
|
||||
BytecodeLabel far0, far1, far2, far3, far4;
|
||||
BytecodeLabel near0, near1, near2, near3, near4;
|
||||
|
||||
builder.Jump(&near0)
|
||||
.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
||||
.JumpIfTrue(&near1)
|
||||
.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
||||
.JumpIfFalse(&near2)
|
||||
.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
||||
.JumpIfTrue(&near3)
|
||||
.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
||||
.JumpIfFalse(&near4)
|
||||
.Bind(&near0)
|
||||
.Bind(&near1)
|
||||
.Bind(&near2)
|
||||
.Bind(&near3)
|
||||
.Bind(&near4)
|
||||
.Jump(&far0)
|
||||
.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
||||
.JumpIfTrue(&far1)
|
||||
.JumpIfFalse(&far2);
|
||||
for (int i = 0; i < kFarJumpDistance - 6; i++) {
|
||||
.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
||||
.JumpIfFalse(&far2)
|
||||
.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
||||
.JumpIfTrue(&far3)
|
||||
.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
||||
.JumpIfFalse(&far4);
|
||||
for (int i = 0; i < kFarJumpDistance - 18; i++) {
|
||||
builder.LoadUndefined();
|
||||
}
|
||||
builder.Bind(&far0).Bind(&far1).Bind(&far2);
|
||||
builder.Bind(&far0).Bind(&far1).Bind(&far2).Bind(&far3).Bind(&far4);
|
||||
builder.Return();
|
||||
|
||||
Handle<BytecodeArray> array = builder.ToBytecodeArray();
|
||||
DCHECK_EQ(array->length(), 12 + kFarJumpDistance - 6 + 1);
|
||||
DCHECK_EQ(array->length(), 36 + kFarJumpDistance - 18 + 1);
|
||||
|
||||
BytecodeArrayIterator iterator(array);
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), 6);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), 18);
|
||||
iterator.Advance();
|
||||
|
||||
// Ignore compare operation.
|
||||
iterator.Advance();
|
||||
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrue);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), 4);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), 14);
|
||||
iterator.Advance();
|
||||
|
||||
// Ignore compare operation.
|
||||
iterator.Advance();
|
||||
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalse);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), 10);
|
||||
iterator.Advance();
|
||||
|
||||
// Ignore add operation.
|
||||
iterator.Advance();
|
||||
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrue);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), 6);
|
||||
iterator.Advance();
|
||||
|
||||
// Ignore add operation.
|
||||
iterator.Advance();
|
||||
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanFalse);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), 2);
|
||||
iterator.Advance();
|
||||
|
||||
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpConstant);
|
||||
CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
|
||||
Smi::FromInt(kFarJumpDistance));
|
||||
CHECK_EQ(
|
||||
array->get(iterator.current_offset() +
|
||||
Smi::cast(*iterator.GetConstantForIndexOperand(0))->value()),
|
||||
Bytecodes::ToByte(Bytecode::kReturn));
|
||||
iterator.Advance();
|
||||
|
||||
// Ignore compare operation.
|
||||
iterator.Advance();
|
||||
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrueConstant);
|
||||
CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
|
||||
Smi::FromInt(kFarJumpDistance - 2));
|
||||
CHECK_EQ(
|
||||
array->get(iterator.current_offset() +
|
||||
Smi::cast(*iterator.GetConstantForIndexOperand(0))->value()),
|
||||
Bytecodes::ToByte(Bytecode::kReturn));
|
||||
Smi::FromInt(kFarJumpDistance - 4));
|
||||
iterator.Advance();
|
||||
|
||||
// Ignore compare operation.
|
||||
iterator.Advance();
|
||||
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalseConstant);
|
||||
CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
|
||||
Smi::FromInt(kFarJumpDistance - 4));
|
||||
CHECK_EQ(
|
||||
array->get(iterator.current_offset() +
|
||||
Smi::cast(*iterator.GetConstantForIndexOperand(0))->value()),
|
||||
Bytecodes::ToByte(Bytecode::kReturn));
|
||||
Smi::FromInt(kFarJumpDistance - 8));
|
||||
iterator.Advance();
|
||||
|
||||
// Ignore add operation.
|
||||
iterator.Advance();
|
||||
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrueConstant);
|
||||
CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
|
||||
Smi::FromInt(kFarJumpDistance - 12));
|
||||
iterator.Advance();
|
||||
|
||||
// Ignore add operation.
|
||||
iterator.Advance();
|
||||
|
||||
CHECK_EQ(iterator.current_bytecode(),
|
||||
Bytecode::kJumpIfToBooleanFalseConstant);
|
||||
CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
|
||||
Smi::FromInt(kFarJumpDistance - 16));
|
||||
iterator.Advance();
|
||||
}
|
||||
|
||||
@ -414,21 +476,36 @@ TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
|
||||
TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
|
||||
BytecodeArrayBuilder builder(isolate(), zone());
|
||||
builder.set_parameter_count(0);
|
||||
builder.set_locals_count(0);
|
||||
builder.set_locals_count(1);
|
||||
builder.set_context_count(0);
|
||||
Register reg(0);
|
||||
|
||||
BytecodeLabel label0, label1, label2;
|
||||
BytecodeLabel label0, label1, label2, label3, label4;
|
||||
builder.Bind(&label0)
|
||||
.Jump(&label0)
|
||||
.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
||||
.Bind(&label1)
|
||||
.JumpIfTrue(&label1)
|
||||
.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
||||
.Bind(&label2)
|
||||
.JumpIfFalse(&label2);
|
||||
.JumpIfFalse(&label2)
|
||||
.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
||||
.Bind(&label3)
|
||||
.JumpIfTrue(&label3)
|
||||
.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
||||
.Bind(&label4)
|
||||
.JumpIfFalse(&label4);
|
||||
for (int i = 0; i < 64; i++) {
|
||||
builder.Jump(&label2);
|
||||
builder.Jump(&label4);
|
||||
}
|
||||
builder.JumpIfFalse(&label2);
|
||||
builder.JumpIfTrue(&label1);
|
||||
builder.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
||||
.JumpIfFalse(&label4);
|
||||
builder.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
||||
.JumpIfTrue(&label3);
|
||||
builder.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
||||
.JumpIfFalse(&label2);
|
||||
builder.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
||||
.JumpIfTrue(&label1);
|
||||
builder.Jump(&label0);
|
||||
builder.Return();
|
||||
|
||||
@ -437,25 +514,54 @@ TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), 0);
|
||||
iterator.Advance();
|
||||
// Ignore compare operation.
|
||||
iterator.Advance();
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrue);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), 0);
|
||||
iterator.Advance();
|
||||
// Ignore compare operation.
|
||||
iterator.Advance();
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalse);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), 0);
|
||||
iterator.Advance();
|
||||
// Ignore binary operation.
|
||||
iterator.Advance();
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrue);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), 0);
|
||||
iterator.Advance();
|
||||
// Ignore binary operation.
|
||||
iterator.Advance();
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanFalse);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), 0);
|
||||
iterator.Advance();
|
||||
for (int i = 0; i < 64; i++) {
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
|
||||
CHECK_EQ(iterator.GetImmediateOperand(0), -i * 2 - 2);
|
||||
iterator.Advance();
|
||||
}
|
||||
// Ignore binary operation.
|
||||
iterator.Advance();
|
||||
CHECK_EQ(iterator.current_bytecode(),
|
||||
Bytecode::kJumpIfToBooleanFalseConstant);
|
||||
CHECK_EQ(Smi::cast(*iterator.GetConstantForIndexOperand(0))->value(), -132);
|
||||
iterator.Advance();
|
||||
// Ignore binary operation.
|
||||
iterator.Advance();
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrueConstant);
|
||||
CHECK_EQ(Smi::cast(*iterator.GetConstantForIndexOperand(0))->value(), -140);
|
||||
iterator.Advance();
|
||||
// Ignore compare operation.
|
||||
iterator.Advance();
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalseConstant);
|
||||
CHECK_EQ(Smi::cast(*iterator.GetConstantForIndexOperand(0))->value(), -130);
|
||||
CHECK_EQ(Smi::cast(*iterator.GetConstantForIndexOperand(0))->value(), -148);
|
||||
iterator.Advance();
|
||||
// Ignore compare operation.
|
||||
iterator.Advance();
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrueConstant);
|
||||
CHECK_EQ(Smi::cast(*iterator.GetConstantForIndexOperand(0))->value(), -134);
|
||||
CHECK_EQ(Smi::cast(*iterator.GetConstantForIndexOperand(0))->value(), -156);
|
||||
iterator.Advance();
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpConstant);
|
||||
CHECK_EQ(Smi::cast(*iterator.GetConstantForIndexOperand(0))->value(), -138);
|
||||
CHECK_EQ(Smi::cast(*iterator.GetConstantForIndexOperand(0))->value(), -162);
|
||||
iterator.Advance();
|
||||
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
|
||||
iterator.Advance();
|
||||
|
Loading…
Reference in New Issue
Block a user