2015-08-03 10:42:16 +00:00
|
|
|
// Copyright 2014 the V8 project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
#include "src/v8.h"
|
|
|
|
|
|
|
|
#include "src/interpreter/bytecode-array-builder.h"
|
2015-09-24 15:20:47 +00:00
|
|
|
#include "src/interpreter/bytecode-array-iterator.h"
|
2015-08-13 11:27:54 +00:00
|
|
|
#include "test/unittests/test-utils.h"
|
2015-08-03 10:42:16 +00:00
|
|
|
|
2015-08-13 11:27:54 +00:00
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace interpreter {
|
2015-08-03 10:42:16 +00:00
|
|
|
|
2015-08-28 15:40:52 +00:00
|
|
|
class BytecodeArrayBuilderTest : public TestWithIsolateAndZone {
|
2015-08-13 11:27:54 +00:00
|
|
|
public:
|
|
|
|
BytecodeArrayBuilderTest() {}
|
|
|
|
~BytecodeArrayBuilderTest() override {}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
2015-08-28 15:40:52 +00:00
|
|
|
BytecodeArrayBuilder builder(isolate(), zone());
|
2015-08-03 10:42:16 +00:00
|
|
|
|
|
|
|
builder.set_locals_count(1);
|
2015-10-16 15:29:07 +00:00
|
|
|
builder.set_context_count(1);
|
2015-08-27 10:32:26 +00:00
|
|
|
builder.set_parameter_count(0);
|
2015-08-03 10:42:16 +00:00
|
|
|
CHECK_EQ(builder.locals_count(), 1);
|
2015-10-16 15:29:07 +00:00
|
|
|
CHECK_EQ(builder.context_count(), 1);
|
|
|
|
CHECK_EQ(builder.fixed_register_count(), 2);
|
2015-08-03 10:42:16 +00:00
|
|
|
|
|
|
|
// Emit constant loads.
|
|
|
|
builder.LoadLiteral(Smi::FromInt(0))
|
|
|
|
.LoadLiteral(Smi::FromInt(8))
|
2015-08-28 15:40:52 +00:00
|
|
|
.LoadLiteral(Smi::FromInt(10000000))
|
2015-08-03 10:42:16 +00:00
|
|
|
.LoadUndefined()
|
|
|
|
.LoadNull()
|
|
|
|
.LoadTheHole()
|
|
|
|
.LoadTrue()
|
|
|
|
.LoadFalse();
|
|
|
|
|
|
|
|
// Emit accumulator transfers.
|
2015-08-13 11:27:54 +00:00
|
|
|
Register reg(0);
|
|
|
|
builder.LoadAccumulatorWithRegister(reg).StoreAccumulatorInRegister(reg);
|
2015-08-03 10:42:16 +00:00
|
|
|
|
2015-10-22 14:55:32 +00:00
|
|
|
// Emit global load / store operations.
|
|
|
|
builder.LoadGlobal(0, 1, LanguageMode::SLOPPY)
|
|
|
|
.LoadGlobal(0, 1, LanguageMode::STRICT)
|
|
|
|
.StoreGlobal(0, 1, LanguageMode::SLOPPY)
|
|
|
|
.StoreGlobal(0, 1, LanguageMode::STRICT);
|
|
|
|
|
2015-10-13 13:09:48 +00:00
|
|
|
// Emit context operations.
|
|
|
|
builder.PushContext(reg);
|
|
|
|
builder.PopContext(reg);
|
2015-10-07 21:18:58 +00:00
|
|
|
builder.LoadContextSlot(reg, 1);
|
2015-10-16 15:29:07 +00:00
|
|
|
builder.StoreContextSlot(reg, 1);
|
2015-09-24 11:48:22 +00:00
|
|
|
|
2015-09-09 15:46:04 +00:00
|
|
|
// Emit load / store property operations.
|
2015-10-22 14:55:32 +00:00
|
|
|
builder.LoadNamedProperty(reg, 0, 0, LanguageMode::SLOPPY)
|
2015-09-09 15:46:04 +00:00
|
|
|
.LoadKeyedProperty(reg, 0, LanguageMode::SLOPPY)
|
2015-10-22 14:55:32 +00:00
|
|
|
.StoreNamedProperty(reg, 0, 0, LanguageMode::SLOPPY)
|
2015-10-07 07:54:07 +00:00
|
|
|
.StoreKeyedProperty(reg, reg, 0, LanguageMode::SLOPPY)
|
2015-10-22 14:55:32 +00:00
|
|
|
.LoadNamedProperty(reg, 0, 0, LanguageMode::STRICT)
|
2015-10-07 07:54:07 +00:00
|
|
|
.LoadKeyedProperty(reg, 0, LanguageMode::STRICT)
|
2015-10-22 14:55:32 +00:00
|
|
|
.StoreNamedProperty(reg, 0, 0, LanguageMode::STRICT)
|
2015-10-14 13:19:49 +00:00
|
|
|
.StoreKeyedProperty(reg, reg, 0, LanguageMode::STRICT);
|
2015-09-02 13:03:06 +00:00
|
|
|
|
2015-10-13 09:39:55 +00:00
|
|
|
// Emit closure operations.
|
|
|
|
builder.CreateClosure(NOT_TENURED);
|
|
|
|
|
2015-10-22 21:41:47 +00:00
|
|
|
// Emit argument creation operations.
|
|
|
|
builder.CreateArguments(CreateArgumentsType::kMappedArguments)
|
|
|
|
.CreateArguments(CreateArgumentsType::kUnmappedArguments);
|
|
|
|
|
2015-10-13 14:00:40 +00:00
|
|
|
// Emit literal creation operations
|
2015-10-16 16:14:23 +00:00
|
|
|
builder.CreateRegExpLiteral(0, reg)
|
|
|
|
.CreateArrayLiteral(0, 0)
|
2015-10-14 10:10:01 +00:00
|
|
|
.CreateObjectLiteral(0, 0);
|
2015-10-13 14:00:40 +00:00
|
|
|
|
2015-09-14 10:05:18 +00:00
|
|
|
// Call operations.
|
|
|
|
builder.Call(reg, reg, 0);
|
2015-10-02 18:13:41 +00:00
|
|
|
builder.CallRuntime(Runtime::kIsArray, reg, 1);
|
2015-09-14 10:05:18 +00:00
|
|
|
|
2015-09-24 15:20:47 +00:00
|
|
|
// Emit binary operator invocations.
|
2015-10-07 07:54:07 +00:00
|
|
|
builder.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
|
|
|
|
.BinaryOperation(Token::Value::SUB, reg, Strength::WEAK)
|
|
|
|
.BinaryOperation(Token::Value::MUL, reg, Strength::WEAK)
|
|
|
|
.BinaryOperation(Token::Value::DIV, reg, Strength::WEAK)
|
|
|
|
.BinaryOperation(Token::Value::MOD, reg, Strength::WEAK);
|
2015-08-03 10:42:16 +00:00
|
|
|
|
2015-10-12 13:35:57 +00:00
|
|
|
// Emit bitwise operator invocations
|
|
|
|
builder.BinaryOperation(Token::Value::BIT_OR, reg, Strength::WEAK)
|
|
|
|
.BinaryOperation(Token::Value::BIT_XOR, reg, Strength::WEAK)
|
|
|
|
.BinaryOperation(Token::Value::BIT_AND, reg, Strength::WEAK);
|
|
|
|
|
2015-10-12 10:45:19 +00:00
|
|
|
// Emit shift operator invocations
|
|
|
|
builder.BinaryOperation(Token::Value::SHL, reg, Strength::WEAK)
|
|
|
|
.BinaryOperation(Token::Value::SAR, reg, Strength::WEAK)
|
|
|
|
.BinaryOperation(Token::Value::SHR, reg, Strength::WEAK);
|
|
|
|
|
2015-10-22 20:40:20 +00:00
|
|
|
// Emit count operatior invocations
|
|
|
|
builder.CountOperation(Token::Value::ADD, Strength::WEAK)
|
|
|
|
.CountOperation(Token::Value::SUB, Strength::WEAK);
|
|
|
|
|
2015-10-06 14:15:09 +00:00
|
|
|
// Emit unary operator invocations.
|
|
|
|
builder.LogicalNot().TypeOf();
|
|
|
|
|
2015-10-28 09:49:20 +00:00
|
|
|
// Emit delete
|
|
|
|
builder.Delete(reg, LanguageMode::SLOPPY).Delete(reg, LanguageMode::STRICT);
|
2015-10-22 20:40:20 +00:00
|
|
|
|
2015-10-15 16:46:16 +00:00
|
|
|
// Emit new.
|
|
|
|
builder.New(reg, reg, 0);
|
|
|
|
|
2015-09-24 15:20:47 +00:00
|
|
|
// Emit test operator invocations.
|
2015-10-07 07:54:07 +00:00
|
|
|
builder.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
|
|
|
|
.CompareOperation(Token::Value::NE, reg, Strength::WEAK)
|
|
|
|
.CompareOperation(Token::Value::EQ_STRICT, reg, Strength::WEAK)
|
|
|
|
.CompareOperation(Token::Value::NE_STRICT, reg, Strength::WEAK)
|
|
|
|
.CompareOperation(Token::Value::LT, reg, Strength::WEAK)
|
|
|
|
.CompareOperation(Token::Value::GT, reg, Strength::WEAK)
|
|
|
|
.CompareOperation(Token::Value::LTE, reg, Strength::WEAK)
|
|
|
|
.CompareOperation(Token::Value::GTE, reg, Strength::WEAK)
|
|
|
|
.CompareOperation(Token::Value::INSTANCEOF, reg, Strength::WEAK)
|
|
|
|
.CompareOperation(Token::Value::IN, reg, Strength::WEAK);
|
2015-09-24 15:20:47 +00:00
|
|
|
|
|
|
|
// Emit cast operator invocations.
|
2015-10-22 20:40:20 +00:00
|
|
|
builder.CastAccumulatorToNumber()
|
2015-10-14 10:10:01 +00:00
|
|
|
.CastAccumulatorToBoolean()
|
2015-10-29 12:06:00 +00:00
|
|
|
.CastAccumulatorToJSObject()
|
2015-10-14 10:10:01 +00:00
|
|
|
.CastAccumulatorToName();
|
2015-09-24 15:20:47 +00:00
|
|
|
|
2015-08-03 10:42:16 +00:00
|
|
|
// Emit control flow. Return must be the last instruction.
|
2015-09-24 15:20:47 +00:00
|
|
|
BytecodeLabel start;
|
|
|
|
builder.Bind(&start);
|
|
|
|
// Short jumps with Imm8 operands
|
2015-10-15 09:11:40 +00:00
|
|
|
builder.Jump(&start)
|
|
|
|
.JumpIfTrue(&start)
|
|
|
|
.JumpIfFalse(&start)
|
|
|
|
.JumpIfToBooleanTrue(&start)
|
2015-10-29 12:06:00 +00:00
|
|
|
.JumpIfToBooleanFalse(&start)
|
|
|
|
.JumpIfNull(&start)
|
|
|
|
.JumpIfUndefined(&start);
|
|
|
|
|
2015-09-24 15:20:47 +00:00
|
|
|
// Insert dummy ops to force longer jumps
|
|
|
|
for (int i = 0; i < 128; i++) {
|
|
|
|
builder.LoadTrue();
|
|
|
|
}
|
|
|
|
// Longer jumps requiring Constant operand
|
2015-10-15 09:11:40 +00:00
|
|
|
builder.Jump(&start)
|
|
|
|
.JumpIfTrue(&start)
|
|
|
|
.JumpIfFalse(&start)
|
|
|
|
.JumpIfToBooleanTrue(&start)
|
2015-10-29 12:06:00 +00:00
|
|
|
.JumpIfToBooleanFalse(&start)
|
|
|
|
.JumpIfNull(&start)
|
|
|
|
.JumpIfUndefined(&start);
|
2015-10-19 10:59:00 +00:00
|
|
|
|
|
|
|
builder.EnterBlock()
|
|
|
|
.Throw()
|
|
|
|
.LeaveBlock();
|
|
|
|
|
2015-10-29 12:06:00 +00:00
|
|
|
builder.ForInPrepare(reg).ForInDone(reg).ForInNext(reg, reg);
|
|
|
|
|
2015-08-03 10:42:16 +00:00
|
|
|
builder.Return();
|
|
|
|
|
|
|
|
// Generate BytecodeArray.
|
|
|
|
Handle<BytecodeArray> the_array = builder.ToBytecodeArray();
|
2015-10-16 15:29:07 +00:00
|
|
|
CHECK_EQ(the_array->frame_size(),
|
|
|
|
builder.fixed_register_count() * kPointerSize);
|
2015-08-03 10:42:16 +00:00
|
|
|
|
|
|
|
// Build scorecard of bytecodes encountered in the BytecodeArray.
|
|
|
|
std::vector<int> scorecard(Bytecodes::ToByte(Bytecode::kLast) + 1);
|
|
|
|
Bytecode final_bytecode = Bytecode::kLdaZero;
|
2015-10-01 17:22:58 +00:00
|
|
|
int i = 0;
|
|
|
|
while (i < the_array->length()) {
|
2015-08-03 10:42:16 +00:00
|
|
|
uint8_t code = the_array->get(i);
|
|
|
|
scorecard[code] += 1;
|
|
|
|
final_bytecode = Bytecodes::FromByte(code);
|
2015-10-01 17:22:58 +00:00
|
|
|
i += Bytecodes::Size(Bytecodes::FromByte(code));
|
2015-08-03 10:42:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check return occurs at the end and only once in the BytecodeArray.
|
|
|
|
CHECK_EQ(final_bytecode, Bytecode::kReturn);
|
|
|
|
CHECK_EQ(scorecard[Bytecodes::ToByte(final_bytecode)], 1);
|
|
|
|
|
|
|
|
#define CHECK_BYTECODE_PRESENT(Name, ...) \
|
|
|
|
/* Check Bytecode is marked in scorecard */ \
|
|
|
|
CHECK_GE(scorecard[Bytecodes::ToByte(Bytecode::k##Name)], 1);
|
|
|
|
BYTECODE_LIST(CHECK_BYTECODE_PRESENT)
|
|
|
|
#undef CHECK_BYTECODE_PRESENT
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-13 11:27:54 +00:00
|
|
|
TEST_F(BytecodeArrayBuilderTest, FrameSizesLookGood) {
|
2015-08-03 20:38:57 +00:00
|
|
|
for (int locals = 0; locals < 5; locals++) {
|
2015-10-16 15:29:07 +00:00
|
|
|
for (int contexts = 0; contexts < 4; contexts++) {
|
|
|
|
for (int temps = 0; temps < 3; temps++) {
|
|
|
|
BytecodeArrayBuilder builder(isolate(), zone());
|
|
|
|
builder.set_parameter_count(0);
|
|
|
|
builder.set_locals_count(locals);
|
|
|
|
builder.set_context_count(contexts);
|
|
|
|
|
|
|
|
TemporaryRegisterScope temporaries(&builder);
|
|
|
|
for (int i = 0; i < temps; i++) {
|
2015-10-21 15:28:55 +00:00
|
|
|
builder.StoreAccumulatorInRegister(temporaries.NewRegister());
|
2015-10-16 15:29:07 +00:00
|
|
|
}
|
2015-10-21 15:28:55 +00:00
|
|
|
builder.Return();
|
2015-10-16 15:29:07 +00:00
|
|
|
|
|
|
|
Handle<BytecodeArray> the_array = builder.ToBytecodeArray();
|
|
|
|
int total_registers = locals + contexts + temps;
|
|
|
|
CHECK_EQ(the_array->frame_size(), total_registers * kPointerSize);
|
2015-08-03 10:42:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-13 11:27:54 +00:00
|
|
|
TEST_F(BytecodeArrayBuilderTest, TemporariesRecycled) {
|
2015-08-28 15:40:52 +00:00
|
|
|
BytecodeArrayBuilder builder(isolate(), zone());
|
2015-08-27 10:32:26 +00:00
|
|
|
builder.set_parameter_count(0);
|
2015-08-03 10:42:16 +00:00
|
|
|
builder.set_locals_count(0);
|
2015-10-16 15:29:07 +00:00
|
|
|
builder.set_context_count(0);
|
2015-08-03 10:42:16 +00:00
|
|
|
builder.Return();
|
|
|
|
|
|
|
|
int first;
|
|
|
|
{
|
|
|
|
TemporaryRegisterScope temporaries(&builder);
|
2015-08-13 11:27:54 +00:00
|
|
|
first = temporaries.NewRegister().index();
|
2015-08-03 10:42:16 +00:00
|
|
|
temporaries.NewRegister();
|
|
|
|
temporaries.NewRegister();
|
|
|
|
temporaries.NewRegister();
|
|
|
|
}
|
|
|
|
|
|
|
|
int second;
|
|
|
|
{
|
|
|
|
TemporaryRegisterScope temporaries(&builder);
|
2015-08-13 11:27:54 +00:00
|
|
|
second = temporaries.NewRegister().index();
|
2015-08-03 10:42:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CHECK_EQ(first, second);
|
|
|
|
}
|
2015-08-13 11:27:54 +00:00
|
|
|
|
2015-08-13 17:11:25 +00:00
|
|
|
|
|
|
|
TEST_F(BytecodeArrayBuilderTest, RegisterValues) {
|
|
|
|
int index = 1;
|
|
|
|
uint8_t operand = static_cast<uint8_t>(-index);
|
|
|
|
|
|
|
|
Register the_register(index);
|
|
|
|
CHECK_EQ(the_register.index(), index);
|
|
|
|
|
|
|
|
int actual_operand = the_register.ToOperand();
|
|
|
|
CHECK_EQ(actual_operand, operand);
|
|
|
|
|
|
|
|
int actual_index = Register::FromOperand(actual_operand).index();
|
|
|
|
CHECK_EQ(actual_index, index);
|
|
|
|
}
|
|
|
|
|
2015-08-27 10:32:26 +00:00
|
|
|
|
|
|
|
TEST_F(BytecodeArrayBuilderTest, Parameters) {
|
2015-08-28 15:40:52 +00:00
|
|
|
BytecodeArrayBuilder builder(isolate(), zone());
|
2015-08-27 10:32:26 +00:00
|
|
|
builder.set_parameter_count(10);
|
|
|
|
builder.set_locals_count(0);
|
2015-10-16 15:29:07 +00:00
|
|
|
builder.set_context_count(0);
|
2015-08-27 10:32:26 +00:00
|
|
|
|
|
|
|
Register param0(builder.Parameter(0));
|
|
|
|
Register param9(builder.Parameter(9));
|
|
|
|
CHECK_EQ(param9.index() - param0.index(), 9);
|
|
|
|
}
|
|
|
|
|
2015-08-28 15:40:52 +00:00
|
|
|
|
2015-10-21 15:28:55 +00:00
|
|
|
TEST_F(BytecodeArrayBuilderTest, RegisterType) {
|
|
|
|
BytecodeArrayBuilder builder(isolate(), zone());
|
|
|
|
builder.set_parameter_count(10);
|
|
|
|
builder.set_locals_count(3);
|
|
|
|
builder.set_context_count(0);
|
|
|
|
|
|
|
|
TemporaryRegisterScope temporary_register_scope(&builder);
|
|
|
|
Register temp0 = temporary_register_scope.NewRegister();
|
|
|
|
Register param0(builder.Parameter(0));
|
|
|
|
Register param9(builder.Parameter(9));
|
|
|
|
Register temp1 = temporary_register_scope.NewRegister();
|
|
|
|
Register reg0(0);
|
|
|
|
Register reg1(1);
|
|
|
|
Register reg2(2);
|
|
|
|
Register temp2 = temporary_register_scope.NewRegister();
|
|
|
|
CHECK_EQ(builder.RegisterIsParameterOrLocal(temp0), false);
|
|
|
|
CHECK_EQ(builder.RegisterIsParameterOrLocal(temp1), false);
|
|
|
|
CHECK_EQ(builder.RegisterIsParameterOrLocal(temp2), false);
|
|
|
|
CHECK_EQ(builder.RegisterIsParameterOrLocal(param0), true);
|
|
|
|
CHECK_EQ(builder.RegisterIsParameterOrLocal(param9), true);
|
|
|
|
CHECK_EQ(builder.RegisterIsParameterOrLocal(reg0), true);
|
|
|
|
CHECK_EQ(builder.RegisterIsParameterOrLocal(reg1), true);
|
|
|
|
CHECK_EQ(builder.RegisterIsParameterOrLocal(reg2), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-28 15:40:52 +00:00
|
|
|
TEST_F(BytecodeArrayBuilderTest, Constants) {
|
|
|
|
BytecodeArrayBuilder builder(isolate(), zone());
|
|
|
|
builder.set_parameter_count(0);
|
|
|
|
builder.set_locals_count(0);
|
2015-10-16 15:29:07 +00:00
|
|
|
builder.set_context_count(0);
|
2015-08-28 15:40:52 +00:00
|
|
|
|
|
|
|
Factory* factory = isolate()->factory();
|
|
|
|
Handle<HeapObject> heap_num_1 = factory->NewHeapNumber(3.14);
|
|
|
|
Handle<HeapObject> heap_num_2 = factory->NewHeapNumber(5.2);
|
|
|
|
Handle<Object> large_smi(Smi::FromInt(0x12345678), isolate());
|
|
|
|
Handle<HeapObject> heap_num_2_copy(*heap_num_2);
|
|
|
|
builder.LoadLiteral(heap_num_1)
|
|
|
|
.LoadLiteral(heap_num_2)
|
|
|
|
.LoadLiteral(large_smi)
|
|
|
|
.LoadLiteral(heap_num_1)
|
|
|
|
.LoadLiteral(heap_num_1)
|
|
|
|
.LoadLiteral(heap_num_2_copy);
|
|
|
|
|
|
|
|
Handle<BytecodeArray> array = builder.ToBytecodeArray();
|
|
|
|
// Should only have one entry for each identical constant.
|
|
|
|
CHECK_EQ(array->constant_pool()->length(), 3);
|
|
|
|
}
|
|
|
|
|
2015-09-24 15:20:47 +00:00
|
|
|
|
|
|
|
TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
|
|
|
|
static const int kFarJumpDistance = 256;
|
|
|
|
|
|
|
|
BytecodeArrayBuilder builder(isolate(), zone());
|
|
|
|
builder.set_parameter_count(0);
|
|
|
|
builder.set_locals_count(0);
|
2015-10-16 15:29:07 +00:00
|
|
|
builder.set_context_count(0);
|
2015-09-24 15:20:47 +00:00
|
|
|
|
|
|
|
BytecodeLabel far0, far1, far2;
|
|
|
|
BytecodeLabel near0, near1, near2;
|
|
|
|
|
|
|
|
builder.Jump(&near0)
|
|
|
|
.JumpIfTrue(&near1)
|
|
|
|
.JumpIfFalse(&near2)
|
|
|
|
.Bind(&near0)
|
|
|
|
.Bind(&near1)
|
|
|
|
.Bind(&near2)
|
|
|
|
.Jump(&far0)
|
|
|
|
.JumpIfTrue(&far1)
|
|
|
|
.JumpIfFalse(&far2);
|
|
|
|
for (int i = 0; i < kFarJumpDistance - 6; i++) {
|
|
|
|
builder.LoadUndefined();
|
|
|
|
}
|
|
|
|
builder.Bind(&far0).Bind(&far1).Bind(&far2);
|
|
|
|
builder.Return();
|
|
|
|
|
|
|
|
Handle<BytecodeArray> array = builder.ToBytecodeArray();
|
|
|
|
DCHECK_EQ(array->length(), 12 + kFarJumpDistance - 6 + 1);
|
|
|
|
|
|
|
|
BytecodeArrayIterator iterator(array);
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), 6);
|
2015-09-24 15:20:47 +00:00
|
|
|
iterator.Advance();
|
|
|
|
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrue);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), 4);
|
2015-09-24 15:20:47 +00:00
|
|
|
iterator.Advance();
|
|
|
|
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalse);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), 2);
|
2015-09-24 15:20:47 +00:00
|
|
|
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();
|
|
|
|
|
|
|
|
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));
|
|
|
|
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));
|
|
|
|
iterator.Advance();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
|
|
|
|
BytecodeArrayBuilder builder(isolate(), zone());
|
|
|
|
builder.set_parameter_count(0);
|
|
|
|
builder.set_locals_count(0);
|
2015-10-16 15:29:07 +00:00
|
|
|
builder.set_context_count(0);
|
2015-09-24 15:20:47 +00:00
|
|
|
|
|
|
|
BytecodeLabel label0, label1, label2;
|
|
|
|
builder.Bind(&label0)
|
|
|
|
.Jump(&label0)
|
|
|
|
.Bind(&label1)
|
|
|
|
.JumpIfTrue(&label1)
|
|
|
|
.Bind(&label2)
|
|
|
|
.JumpIfFalse(&label2);
|
|
|
|
for (int i = 0; i < 64; i++) {
|
|
|
|
builder.Jump(&label2);
|
|
|
|
}
|
|
|
|
builder.JumpIfFalse(&label2);
|
|
|
|
builder.JumpIfTrue(&label1);
|
|
|
|
builder.Jump(&label0);
|
|
|
|
builder.Return();
|
|
|
|
|
|
|
|
Handle<BytecodeArray> array = builder.ToBytecodeArray();
|
|
|
|
BytecodeArrayIterator iterator(array);
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), 0);
|
2015-09-24 15:20:47 +00:00
|
|
|
iterator.Advance();
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrue);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), 0);
|
2015-09-24 15:20:47 +00:00
|
|
|
iterator.Advance();
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalse);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), 0);
|
2015-09-24 15:20:47 +00:00
|
|
|
iterator.Advance();
|
|
|
|
for (int i = 0; i < 64; i++) {
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), -i * 2 - 2);
|
2015-09-24 15:20:47 +00:00
|
|
|
iterator.Advance();
|
|
|
|
}
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalseConstant);
|
|
|
|
CHECK_EQ(Smi::cast(*iterator.GetConstantForIndexOperand(0))->value(), -130);
|
|
|
|
iterator.Advance();
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrueConstant);
|
|
|
|
CHECK_EQ(Smi::cast(*iterator.GetConstantForIndexOperand(0))->value(), -134);
|
|
|
|
iterator.Advance();
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpConstant);
|
|
|
|
CHECK_EQ(Smi::cast(*iterator.GetConstantForIndexOperand(0))->value(), -138);
|
|
|
|
iterator.Advance();
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
|
|
|
|
iterator.Advance();
|
|
|
|
CHECK(iterator.done());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST_F(BytecodeArrayBuilderTest, LabelReuse) {
|
|
|
|
BytecodeArrayBuilder builder(isolate(), zone());
|
|
|
|
builder.set_parameter_count(0);
|
|
|
|
builder.set_locals_count(0);
|
2015-10-16 15:29:07 +00:00
|
|
|
builder.set_context_count(0);
|
2015-09-24 15:20:47 +00:00
|
|
|
|
|
|
|
// Labels can only have 1 forward reference, but
|
|
|
|
// can be referred to mulitple times once bound.
|
|
|
|
BytecodeLabel label;
|
|
|
|
|
|
|
|
builder.Jump(&label).Bind(&label).Jump(&label).Jump(&label).Return();
|
|
|
|
|
|
|
|
Handle<BytecodeArray> array = builder.ToBytecodeArray();
|
|
|
|
BytecodeArrayIterator iterator(array);
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), 2);
|
2015-09-24 15:20:47 +00:00
|
|
|
iterator.Advance();
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), 0);
|
2015-09-24 15:20:47 +00:00
|
|
|
iterator.Advance();
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), -2);
|
2015-09-24 15:20:47 +00:00
|
|
|
iterator.Advance();
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
|
|
|
|
iterator.Advance();
|
|
|
|
CHECK(iterator.done());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST_F(BytecodeArrayBuilderTest, LabelAddressReuse) {
|
|
|
|
static const int kRepeats = 3;
|
|
|
|
|
|
|
|
BytecodeArrayBuilder builder(isolate(), zone());
|
|
|
|
builder.set_parameter_count(0);
|
|
|
|
builder.set_locals_count(0);
|
2015-10-16 15:29:07 +00:00
|
|
|
builder.set_context_count(0);
|
2015-09-24 15:20:47 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < kRepeats; i++) {
|
|
|
|
BytecodeLabel label;
|
|
|
|
builder.Jump(&label).Bind(&label).Jump(&label).Jump(&label);
|
|
|
|
}
|
|
|
|
|
|
|
|
builder.Return();
|
|
|
|
|
|
|
|
Handle<BytecodeArray> array = builder.ToBytecodeArray();
|
|
|
|
BytecodeArrayIterator iterator(array);
|
|
|
|
for (int i = 0; i < kRepeats; i++) {
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), 2);
|
2015-09-24 15:20:47 +00:00
|
|
|
iterator.Advance();
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), 0);
|
2015-09-24 15:20:47 +00:00
|
|
|
iterator.Advance();
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
|
2015-10-01 17:22:58 +00:00
|
|
|
CHECK_EQ(iterator.GetImmediateOperand(0), -2);
|
2015-09-24 15:20:47 +00:00
|
|
|
iterator.Advance();
|
|
|
|
}
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
|
|
|
|
iterator.Advance();
|
|
|
|
CHECK(iterator.done());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-01 17:22:58 +00:00
|
|
|
TEST_F(BytecodeArrayBuilderTest, ToBoolean) {
|
|
|
|
BytecodeArrayBuilder builder(isolate(), zone());
|
|
|
|
builder.set_parameter_count(0);
|
|
|
|
builder.set_locals_count(0);
|
2015-10-16 15:29:07 +00:00
|
|
|
builder.set_context_count(0);
|
2015-10-01 17:22:58 +00:00
|
|
|
|
|
|
|
// Check ToBoolean emitted at start of block.
|
|
|
|
builder.EnterBlock().CastAccumulatorToBoolean();
|
|
|
|
|
|
|
|
// Check ToBoolean emitted preceding bytecode is non-boolean.
|
|
|
|
builder.LoadNull().CastAccumulatorToBoolean();
|
|
|
|
|
|
|
|
// Check ToBoolean omitted if preceding bytecode is boolean.
|
|
|
|
builder.LoadFalse().CastAccumulatorToBoolean();
|
|
|
|
|
|
|
|
// Check ToBoolean emitted if it is at the start of the next block.
|
|
|
|
builder.LoadFalse()
|
|
|
|
.LeaveBlock()
|
|
|
|
.EnterBlock()
|
|
|
|
.CastAccumulatorToBoolean()
|
|
|
|
.LeaveBlock();
|
|
|
|
|
|
|
|
builder.Return();
|
|
|
|
|
|
|
|
Handle<BytecodeArray> array = builder.ToBytecodeArray();
|
|
|
|
BytecodeArrayIterator iterator(array);
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kToBoolean);
|
|
|
|
iterator.Advance();
|
|
|
|
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaNull);
|
|
|
|
iterator.Advance();
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kToBoolean);
|
|
|
|
iterator.Advance();
|
|
|
|
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaFalse);
|
|
|
|
iterator.Advance();
|
|
|
|
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaFalse);
|
|
|
|
iterator.Advance();
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kToBoolean);
|
|
|
|
iterator.Advance();
|
|
|
|
|
|
|
|
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
|
|
|
|
iterator.Advance();
|
|
|
|
CHECK(iterator.done());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-13 11:27:54 +00:00
|
|
|
} // namespace interpreter
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|