[Intepreter] BytecodeArrayBuilder and accumulator based bytecodes.

The BytecodeArrayBuilder has responsibility for emitting the BytecodeArray. It will be used by the AST walker.

Bytecode now uses an accumulator plus registers rather being pure register based.

Update BytecodeArray::Disassemble to print operand information.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#29970}
This commit is contained in:
oth 2015-08-03 03:42:16 -07:00 committed by Commit bot
parent 2da7214d98
commit 6ab1f70e12
8 changed files with 571 additions and 15 deletions

View File

@ -0,0 +1,220 @@
// Copyright 2015 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/interpreter/bytecode-array-builder.h"
namespace v8 {
namespace internal {
namespace interpreter {
BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate)
: isolate_(isolate),
bytecode_generated_(false),
local_register_count_(-1),
temporary_register_count_(0),
temporary_register_next_(0) {}
void BytecodeArrayBuilder::set_locals_count(int number_of_locals) {
local_register_count_ = number_of_locals;
temporary_register_next_ = local_register_count_;
}
int BytecodeArrayBuilder::locals_count() const { return local_register_count_; }
Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray() {
DCHECK_EQ(bytecode_generated_, false);
DCHECK_GE(local_register_count_, 0);
int bytecode_size = static_cast<int>(bytecodes_.size());
int register_count = local_register_count_ + temporary_register_count_;
int frame_size = register_count * kPointerSize;
Handle<BytecodeArray> output = isolate_->factory()->NewBytecodeArray(
bytecode_size, &bytecodes_.front(), frame_size);
bytecode_generated_ = true;
return output;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value binop,
int reg) {
Output(BytecodeForBinaryOperation(binop), reg);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(
v8::internal::Smi* smi) {
int32_t raw_smi = smi->value();
if (raw_smi == 0) {
Output(Bytecode::kLdaZero);
} else if (raw_smi > -128 && raw_smi <= 128) {
Output(Bytecode::kLdaSmi8, static_cast<uint8_t>(raw_smi));
} else {
// TODO(oth): Put Smi in constant pool.
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadUndefined() {
Output(Bytecode::kLdaUndefined);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNull() {
Output(Bytecode::kLdaNull);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadTheHole() {
Output(Bytecode::kLdaTheHole);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadTrue() {
Output(Bytecode::kLdaTrue);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadFalse() {
Output(Bytecode::kLdaFalse);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister(
int reg) {
Output(Bytecode::kLdar, reg);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
int reg) {
Output(Bytecode::kStar, reg);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Return() {
Output(Bytecode::kReturn);
return *this;
}
int BytecodeArrayBuilder::BorrowTemporaryRegister() {
DCHECK_GE(local_register_count_, 0);
int temporary_register = temporary_register_next_++;
int count = temporary_register_next_ - local_register_count_;
if (count > temporary_register_count_) {
temporary_register_count_ = count;
}
return temporary_register;
}
void BytecodeArrayBuilder::ReturnTemporaryRegister(int reg) {
DCHECK_EQ(reg, temporary_register_next_ - 1);
temporary_register_next_ = reg;
}
bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
uint8_t operand_value) const {
OperandType operand_type = Bytecodes::GetOperandType(bytecode, operand_index);
switch (operand_type) {
case OperandType::kNone:
return false;
case OperandType::kImm8:
return true;
case OperandType::kReg:
return operand_value < temporary_register_next_;
}
UNREACHABLE();
return false;
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint8_t operand0,
uint8_t operand1, uint8_t operand2) {
DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 3);
DCHECK(OperandIsValid(bytecode, 0, operand0) &&
OperandIsValid(bytecode, 1, operand1) &&
OperandIsValid(bytecode, 2, operand2));
bytecodes_.push_back(Bytecodes::ToByte(bytecode));
bytecodes_.push_back(operand0);
bytecodes_.push_back(operand1);
bytecodes_.push_back(operand2);
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint8_t operand0,
uint8_t operand1) {
DCHECK(Bytecodes::NumberOfOperands(bytecode) == 2);
DCHECK(OperandIsValid(bytecode, 0, operand0) &&
OperandIsValid(bytecode, 1, operand1));
bytecodes_.push_back(Bytecodes::ToByte(bytecode));
bytecodes_.push_back(operand0);
bytecodes_.push_back(operand1);
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint8_t operand0) {
DCHECK(Bytecodes::NumberOfOperands(bytecode) == 1);
DCHECK(OperandIsValid(bytecode, 0, operand0));
bytecodes_.push_back(Bytecodes::ToByte(bytecode));
bytecodes_.push_back(operand0);
}
void BytecodeArrayBuilder::Output(Bytecode bytecode) {
DCHECK(Bytecodes::NumberOfOperands(bytecode) == 0);
bytecodes_.push_back(Bytecodes::ToByte(bytecode));
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) {
switch (op) {
case Token::Value::ADD:
return Bytecode::kAdd;
case Token::Value::SUB:
return Bytecode::kSub;
case Token::Value::MUL:
return Bytecode::kMul;
case Token::Value::DIV:
return Bytecode::kDiv;
default:
UNIMPLEMENTED();
return static_cast<Bytecode>(-1);
}
}
TemporaryRegisterScope::TemporaryRegisterScope(BytecodeArrayBuilder* builder)
: builder_(builder), count_(0), register_(-1) {}
TemporaryRegisterScope::~TemporaryRegisterScope() {
while (count_-- != 0) {
builder_->ReturnTemporaryRegister(register_--);
}
}
int TemporaryRegisterScope::NewRegister() {
count_++;
register_ = builder_->BorrowTemporaryRegister();
return register_;
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,97 @@
// Copyright 2015 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.
#ifndef V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_
#define V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_
#include <vector>
#include "src/ast.h"
#include "src/interpreter/bytecodes.h"
namespace v8 {
namespace internal {
class Isolate;
namespace interpreter {
class BytecodeArrayBuilder {
public:
explicit BytecodeArrayBuilder(Isolate* isolate);
Handle<BytecodeArray> ToBytecodeArray();
// Set number of locals required for bytecode array.
void set_locals_count(int number_of_locals);
int locals_count() const;
// Constant loads to accumulator
BytecodeArrayBuilder& LoadLiteral(v8::internal::Smi* value);
BytecodeArrayBuilder& LoadUndefined();
BytecodeArrayBuilder& LoadNull();
BytecodeArrayBuilder& LoadTheHole();
BytecodeArrayBuilder& LoadTrue();
BytecodeArrayBuilder& LoadFalse();
// Register-accumulator transfers
BytecodeArrayBuilder& LoadAccumulatorWithRegister(int reg);
BytecodeArrayBuilder& StoreAccumulatorInRegister(int reg);
// Operators
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, int reg);
// Flow Control
BytecodeArrayBuilder& Return();
private:
static Bytecode BytecodeForBinaryOperation(Token::Value op);
void Output(Bytecode bytecode, uint8_t r0, uint8_t r1, uint8_t r2);
void Output(Bytecode bytecode, uint8_t r0, uint8_t r1);
void Output(Bytecode bytecode, uint8_t r0);
void Output(Bytecode bytecode);
bool OperandIsValid(Bytecode bytecode, int operand_index,
uint8_t operand_value) const;
int BorrowTemporaryRegister();
void ReturnTemporaryRegister(int reg);
Isolate* isolate_;
std::vector<uint8_t> bytecodes_;
bool bytecode_generated_;
int local_register_count_;
int temporary_register_count_;
int temporary_register_next_;
friend class TemporaryRegisterScope;
DISALLOW_IMPLICIT_CONSTRUCTORS(BytecodeArrayBuilder);
};
// A stack-allocated class than allows the instantiator to allocate
// temporary registers that are cleaned up when scope is closed.
class TemporaryRegisterScope {
public:
explicit TemporaryRegisterScope(BytecodeArrayBuilder* builder);
~TemporaryRegisterScope();
int NewRegister();
private:
void* operator new(size_t size);
void operator delete(void* p);
BytecodeArrayBuilder* builder_;
int count_;
int register_;
DISALLOW_COPY_AND_ASSIGN(TemporaryRegisterScope);
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_

View File

@ -22,9 +22,28 @@ namespace interpreter {
V(Reg)
// The list of bytecodes which are interpreted by the interpreter.
#define BYTECODE_LIST(V) \
V(LoadLiteral0, OperandType::kReg) \
V(LoadSmi8, OperandType::kReg, OperandType::kImm8) \
#define BYTECODE_LIST(V) \
\
/* Loading the accumulator */ \
V(LdaZero, OperandType::kNone) \
V(LdaSmi8, OperandType::kImm8) \
V(LdaUndefined, OperandType::kNone) \
V(LdaNull, OperandType::kNone) \
V(LdaTheHole, OperandType::kNone) \
V(LdaTrue, OperandType::kNone) \
V(LdaFalse, OperandType::kNone) \
\
/* Register-accumulator transfers */ \
V(Ldar, OperandType::kReg) \
V(Star, OperandType::kReg) \
\
/* Binary Operators */ \
V(Add, OperandType::kReg) \
V(Sub, OperandType::kReg) \
V(Mul, OperandType::kReg) \
V(Div, OperandType::kReg) \
\
/* Control Flow */ \
V(Return, OperandType::kNone)

View File

@ -48,21 +48,107 @@ void Interpreter::Initialize(bool create_heap_objects) {
}
// LoadLiteral0 <dst>
// LdaZero
//
// Load literal '0' into the destination register.
void Interpreter::DoLoadLiteral0(compiler::InterpreterAssembler* assembler) {
Node* register_index = __ BytecodeOperand(0);
__ StoreRegister(__ NumberConstant(0), register_index);
__ Dispatch();
// Load literal '0' into the accumulator.
void Interpreter::DoLdaZero(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement.
}
// LoadSmi8 <dst>, <imm8>
// LdaSmi8 <imm8>
//
// Load an 8-bit integer literal into destination register as a Smi.
void Interpreter::DoLoadSmi8(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Convert an 8-bit integer to a Smi.
// Load an 8-bit integer literal into the accumulator as a Smi.
void Interpreter::DoLdaSmi8(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement 8-bit integer to SMI promotion.
}
// LdaUndefined
//
// Load Undefined into the accumulator.
void Interpreter::DoLdaUndefined(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement.
}
// LdaNull
//
// Load Null into the accumulator.
void Interpreter::DoLdaNull(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement.
}
// LdaTheHole
//
// Load TheHole into the accumulator.
void Interpreter::DoLdaTheHole(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement.
}
// LdaTrue
//
// Load True into the accumulator.
void Interpreter::DoLdaTrue(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement.
}
// LdaFalse
//
// Load False into the accumulator.
void Interpreter::DoLdaFalse(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement.
}
// Ldar <src>
//
// Load accumulator with value from register <src>.
void Interpreter::DoLdar(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement.
}
// Star <dst>
//
// Store accumulator to register <dst>.
void Interpreter::DoStar(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement.
}
// Add <src>
//
// Add register <src> to accumulator.
void Interpreter::DoAdd(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement.
}
// Sub <src>
//
// Subtract register <src> from accumulator.
void Interpreter::DoSub(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement.
}
// Mul <src>
//
// Multiply accumulator by register <src>.
void Interpreter::DoMul(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement add register to accumulator.
}
// Div <src>
//
// Divide register <src> by accumulator.
void Interpreter::DoDiv(compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy) Implement.
}

View File

@ -11615,7 +11615,6 @@ void BytecodeArray::Disassemble(std::ostream& os) {
for (int i = 0; i < this->length(); i += bytes) {
interpreter::Bytecode bytecode = interpreter::Bytecodes::FromByte(get(i));
bytes = interpreter::Bytecodes::Size(bytecode);
SNPrintF(buf, "%p : ", GetFirstBytecodeAddress() + i);
os << buf.start();
for (int j = 0; j < bytes; j++) {
@ -11625,7 +11624,27 @@ void BytecodeArray::Disassemble(std::ostream& os) {
for (int j = bytes; j < interpreter::Bytecodes::MaximumSize(); j++) {
os << " ";
}
os << bytecode << "\n";
os << bytecode << " ";
for (int j = 1; j < bytes; j++) {
interpreter::OperandType op_type =
interpreter::Bytecodes::GetOperandType(bytecode, j - 1);
uint8_t operand = get(i + j);
switch (op_type) {
case interpreter::OperandType::kImm8:
os << "#" << static_cast<int>(operand);
break;
case interpreter::OperandType::kReg:
os << "r" << static_cast<int>(operand);
break;
case interpreter::OperandType::kNone:
UNREACHABLE();
break;
}
if (j + 1 < bytes) {
os << ", ";
}
}
os << "\n";
}
}

View File

@ -85,6 +85,7 @@
'gay-fixed.cc',
'gay-precision.cc',
'gay-shortest.cc',
'interpreter/test-bytecode-array-builder.cc',
'print-extension.cc',
'profiler-extension.cc',
'test-accessors.cc',

View File

@ -0,0 +1,112 @@
// 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"
#include "test/cctest/cctest.h"
using namespace v8::internal;
using namespace v8::internal::interpreter;
TEST(AllBytecodesGenerated) {
InitializedHandleScope handle_scope;
BytecodeArrayBuilder builder(handle_scope.main_isolate());
builder.set_locals_count(1);
CHECK_EQ(builder.locals_count(), 1);
// Emit constant loads.
builder.LoadLiteral(Smi::FromInt(0))
.LoadLiteral(Smi::FromInt(8))
.LoadUndefined()
.LoadNull()
.LoadTheHole()
.LoadTrue()
.LoadFalse();
// Emit accumulator transfers.
builder.LoadAccumulatorWithRegister(0).StoreAccumulatorInRegister(0);
// Emit binary operators invocations.
builder.BinaryOperation(Token::Value::ADD, 0)
.BinaryOperation(Token::Value::SUB, 0)
.BinaryOperation(Token::Value::MUL, 0)
.BinaryOperation(Token::Value::DIV, 0);
// Emit control flow. Return must be the last instruction.
builder.Return();
// Generate BytecodeArray.
Handle<BytecodeArray> the_array = builder.ToBytecodeArray();
CHECK_EQ(the_array->frame_size(), builder.locals_count() * kPointerSize);
// Build scorecard of bytecodes encountered in the BytecodeArray.
std::vector<int> scorecard(Bytecodes::ToByte(Bytecode::kLast) + 1);
Bytecode final_bytecode = Bytecode::kLdaZero;
for (int i = 0; i < the_array->length(); i++) {
uint8_t code = the_array->get(i);
scorecard[code] += 1;
int operands = Bytecodes::NumberOfOperands(Bytecodes::FromByte(code));
CHECK_LE(operands, Bytecodes::MaximumNumberOfOperands());
final_bytecode = Bytecodes::FromByte(code);
i += operands;
}
// 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
}
TEST(FrameSizesLookGood) {
for (int locals = 1; locals < 5; locals++) {
for (int temps = 0; temps < 3; temps++) {
InitializedHandleScope handle_scope;
BytecodeArrayBuilder builder(handle_scope.main_isolate());
builder.set_locals_count(locals);
builder.Return();
TemporaryRegisterScope temporaries(&builder);
for (int i = 0; i < temps; i++) {
temporaries.NewRegister();
}
Handle<BytecodeArray> the_array = builder.ToBytecodeArray();
int total_registers = locals + temps;
CHECK_EQ(the_array->frame_size(), total_registers * kPointerSize);
}
}
}
TEST(TemporariesRecycled) {
InitializedHandleScope handle_scope;
BytecodeArrayBuilder builder(handle_scope.main_isolate());
builder.set_locals_count(0);
builder.Return();
int first;
{
TemporaryRegisterScope temporaries(&builder);
first = temporaries.NewRegister();
temporaries.NewRegister();
temporaries.NewRegister();
temporaries.NewRegister();
}
int second;
{
TemporaryRegisterScope temporaries(&builder);
second = temporaries.NewRegister();
}
CHECK_EQ(first, second);
}

View File

@ -794,6 +794,8 @@
'../../src/interpreter-irregexp.h',
'../../src/interpreter/bytecodes.cc',
'../../src/interpreter/bytecodes.h',
'../../src/interpreter/bytecode-array-builder.cc',
'../../src/interpreter/bytecode-array-builder.h',
'../../src/interpreter/interpreter.cc',
'../../src/interpreter/interpreter.h',
'../../src/isolate.cc',