[Interpreter] Add implementations of arithmetic binary op bytecodes.
Adds implementations and tests for the following bytecodes: - Add - Sub - Mul - Div - Mod Also adds the Mod bytecode and adds support to BytecodeGenerator and BytecodeArrayBuilder to enable it's use. The current bytecodes always call through to the JS builtins. This also adds LoadObjectField and CallJSBuiltin operators to the InterpreterAssembler. BUG=v8:4280 LOG=N Review URL: https://codereview.chromium.org/1300813005 Cr-Commit-Position: refs/heads/master@{#30352}
This commit is contained in:
parent
cfdcc874a9
commit
b5502099b7
@ -187,12 +187,54 @@ Node* InterpreterAssembler::SmiUntag(Node* value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Node* InterpreterAssembler::LoadObjectField(Node* object, int offset) {
|
||||||
|
return raw_assembler_->Load(kMachAnyTagged, object,
|
||||||
|
IntPtrConstant(offset - kHeapObjectTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Node* InterpreterAssembler::LoadContextSlot(int slot_index) {
|
Node* InterpreterAssembler::LoadContextSlot(int slot_index) {
|
||||||
return raw_assembler_->Load(kMachAnyTagged, ContextTaggedPointer(),
|
return raw_assembler_->Load(kMachAnyTagged, ContextTaggedPointer(),
|
||||||
IntPtrConstant(Context::SlotOffset(slot_index)));
|
IntPtrConstant(Context::SlotOffset(slot_index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Node* InterpreterAssembler::CallJSBuiltin(Builtins::JavaScript builtin,
|
||||||
|
Node* receiver, Node** js_args,
|
||||||
|
int js_arg_count) {
|
||||||
|
Node* global_object = LoadContextSlot(Context::GLOBAL_OBJECT_INDEX);
|
||||||
|
Node* builtins_object =
|
||||||
|
LoadObjectField(global_object, GlobalObject::kBuiltinsOffset);
|
||||||
|
Node* function = LoadObjectField(
|
||||||
|
builtins_object, JSBuiltinsObject::OffsetOfFunctionWithId(builtin));
|
||||||
|
Node* context = LoadObjectField(function, JSFunction::kContextOffset);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
Node** args = zone()->NewArray<Node*>(js_arg_count + 2);
|
||||||
|
args[index++] = receiver;
|
||||||
|
for (int i = 0; i < js_arg_count; i++) {
|
||||||
|
args[index++] = js_args[i];
|
||||||
|
}
|
||||||
|
args[index++] = context;
|
||||||
|
|
||||||
|
CallDescriptor* descriptor = Linkage::GetJSCallDescriptor(
|
||||||
|
zone(), false, js_arg_count + 1, CallDescriptor::kNoFlags);
|
||||||
|
return raw_assembler_->CallN(descriptor, function, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Node* InterpreterAssembler::CallJSBuiltin(Builtins::JavaScript builtin,
|
||||||
|
Node* receiver) {
|
||||||
|
return CallJSBuiltin(builtin, receiver, nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Node* InterpreterAssembler::CallJSBuiltin(Builtins::JavaScript builtin,
|
||||||
|
Node* receiver, Node* arg1) {
|
||||||
|
return CallJSBuiltin(builtin, receiver, &arg1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void InterpreterAssembler::Return() {
|
void InterpreterAssembler::Return() {
|
||||||
Node* exit_trampoline_code_object =
|
Node* exit_trampoline_code_object =
|
||||||
HeapConstant(Unique<HeapObject>::CreateImmovable(
|
HeapConstant(Unique<HeapObject>::CreateImmovable(
|
||||||
@ -204,10 +246,14 @@ void InterpreterAssembler::Return() {
|
|||||||
STATIC_ASSERT(3 == Linkage::kInterpreterBytecodeArrayParameter);
|
STATIC_ASSERT(3 == Linkage::kInterpreterBytecodeArrayParameter);
|
||||||
STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter);
|
STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter);
|
||||||
STATIC_ASSERT(5 == Linkage::kInterpreterContextParameter);
|
STATIC_ASSERT(5 == Linkage::kInterpreterContextParameter);
|
||||||
Node* tail_call = raw_assembler_->TailCallInterpreterDispatch(
|
Node* args[] = { GetAccumulator(),
|
||||||
call_descriptor(), exit_trampoline_code_object, GetAccumulator(),
|
RegisterFileRawPointer(),
|
||||||
RegisterFileRawPointer(), BytecodeOffset(), BytecodeArrayTaggedPointer(),
|
BytecodeOffset(),
|
||||||
DispatchTableRawPointer(), ContextTaggedPointer());
|
BytecodeArrayTaggedPointer(),
|
||||||
|
DispatchTableRawPointer(),
|
||||||
|
ContextTaggedPointer() };
|
||||||
|
Node* tail_call = raw_assembler_->TailCallN(
|
||||||
|
call_descriptor(), exit_trampoline_code_object, args);
|
||||||
// This should always be the end node.
|
// This should always be the end node.
|
||||||
SetEndInput(tail_call);
|
SetEndInput(tail_call);
|
||||||
}
|
}
|
||||||
@ -237,11 +283,14 @@ void InterpreterAssembler::Dispatch() {
|
|||||||
STATIC_ASSERT(3 == Linkage::kInterpreterBytecodeArrayParameter);
|
STATIC_ASSERT(3 == Linkage::kInterpreterBytecodeArrayParameter);
|
||||||
STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter);
|
STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter);
|
||||||
STATIC_ASSERT(5 == Linkage::kInterpreterContextParameter);
|
STATIC_ASSERT(5 == Linkage::kInterpreterContextParameter);
|
||||||
Node* tail_call = raw_assembler_->TailCallInterpreterDispatch(
|
Node* args[] = { GetAccumulator(),
|
||||||
call_descriptor(), target_code_object, GetAccumulator(),
|
RegisterFileRawPointer(),
|
||||||
RegisterFileRawPointer(), new_bytecode_offset,
|
new_bytecode_offset,
|
||||||
BytecodeArrayTaggedPointer(), DispatchTableRawPointer(),
|
BytecodeArrayTaggedPointer(),
|
||||||
ContextTaggedPointer());
|
DispatchTableRawPointer(),
|
||||||
|
ContextTaggedPointer() };
|
||||||
|
Node* tail_call =
|
||||||
|
raw_assembler_->TailCallN(call_descriptor(), target_code_object, args);
|
||||||
// This should always be the end node.
|
// This should always be the end node.
|
||||||
SetEndInput(tail_call);
|
SetEndInput(tail_call);
|
||||||
}
|
}
|
||||||
@ -278,6 +327,8 @@ Schedule* InterpreterAssembler::schedule() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zone* InterpreterAssembler::zone() { return raw_assembler_->zone(); }
|
||||||
|
|
||||||
|
|
||||||
} // namespace interpreter
|
} // namespace interpreter
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
// Do not include anything from src/compiler here!
|
// Do not include anything from src/compiler here!
|
||||||
#include "src/allocation.h"
|
#include "src/allocation.h"
|
||||||
#include "src/base/smart-pointers.h"
|
#include "src/base/smart-pointers.h"
|
||||||
|
#include "src/builtins.h"
|
||||||
#include "src/frames.h"
|
#include "src/frames.h"
|
||||||
#include "src/interpreter/bytecodes.h"
|
#include "src/interpreter/bytecodes.h"
|
||||||
#include "src/unique.h"
|
#include "src/unique.h"
|
||||||
@ -61,9 +62,16 @@ class InterpreterAssembler {
|
|||||||
Node* SmiTag(Node* value);
|
Node* SmiTag(Node* value);
|
||||||
Node* SmiUntag(Node* value);
|
Node* SmiUntag(Node* value);
|
||||||
|
|
||||||
|
// Load a field from an object on the heap.
|
||||||
|
Node* LoadObjectField(Node* object, int offset);
|
||||||
|
|
||||||
// Load |slot_index| from the current context.
|
// Load |slot_index| from the current context.
|
||||||
Node* LoadContextSlot(int slot_index);
|
Node* LoadContextSlot(int slot_index);
|
||||||
|
|
||||||
|
// Call JS builtin.
|
||||||
|
Node* CallJSBuiltin(Builtins::JavaScript builtin, Node* receiver);
|
||||||
|
Node* CallJSBuiltin(Builtins::JavaScript builtin, Node* receiver, Node* arg1);
|
||||||
|
|
||||||
// Returns from the function.
|
// Returns from the function.
|
||||||
void Return();
|
void Return();
|
||||||
|
|
||||||
@ -97,6 +105,9 @@ class InterpreterAssembler {
|
|||||||
Node* BytecodeOperand(int operand_index);
|
Node* BytecodeOperand(int operand_index);
|
||||||
Node* BytecodeOperandSignExtended(int operand_index);
|
Node* BytecodeOperandSignExtended(int operand_index);
|
||||||
|
|
||||||
|
Node* CallJSBuiltin(Builtins::JavaScript builtin, Node* receiver,
|
||||||
|
Node** js_args, int js_arg_count);
|
||||||
|
|
||||||
// Returns BytecodeOffset() advanced by delta bytecodes. Note: this does not
|
// Returns BytecodeOffset() advanced by delta bytecodes. Note: this does not
|
||||||
// update BytecodeOffset() itself.
|
// update BytecodeOffset() itself.
|
||||||
Node* Advance(int delta);
|
Node* Advance(int delta);
|
||||||
@ -107,6 +118,7 @@ class InterpreterAssembler {
|
|||||||
// Private helpers which delegate to RawMachineAssembler.
|
// Private helpers which delegate to RawMachineAssembler.
|
||||||
Isolate* isolate();
|
Isolate* isolate();
|
||||||
Schedule* schedule();
|
Schedule* schedule();
|
||||||
|
Zone* zone();
|
||||||
|
|
||||||
interpreter::Bytecode bytecode_;
|
interpreter::Bytecode bytecode_;
|
||||||
base::SmartPointer<RawMachineAssembler> raw_assembler_;
|
base::SmartPointer<RawMachineAssembler> raw_assembler_;
|
||||||
|
@ -104,7 +104,8 @@ Node* RawMachineAssembler::CallN(CallDescriptor* desc, Node* function,
|
|||||||
Node** args) {
|
Node** args) {
|
||||||
int param_count =
|
int param_count =
|
||||||
static_cast<int>(desc->GetMachineSignature()->parameter_count());
|
static_cast<int>(desc->GetMachineSignature()->parameter_count());
|
||||||
Node** buffer = zone()->NewArray<Node*>(param_count + 3);
|
int input_count = param_count + 3;
|
||||||
|
Node** buffer = zone()->NewArray<Node*>(input_count);
|
||||||
int index = 0;
|
int index = 0;
|
||||||
buffer[index++] = function;
|
buffer[index++] = function;
|
||||||
for (int i = 0; i < param_count; i++) {
|
for (int i = 0; i < param_count; i++) {
|
||||||
@ -112,12 +113,54 @@ Node* RawMachineAssembler::CallN(CallDescriptor* desc, Node* function,
|
|||||||
}
|
}
|
||||||
buffer[index++] = graph()->start();
|
buffer[index++] = graph()->start();
|
||||||
buffer[index++] = graph()->start();
|
buffer[index++] = graph()->start();
|
||||||
Node* call = graph()->NewNode(common()->Call(desc), param_count + 3, buffer);
|
Node* call = graph()->NewNode(common()->Call(desc), input_count, buffer);
|
||||||
schedule()->AddNode(CurrentBlock(), call);
|
schedule()->AddNode(CurrentBlock(), call);
|
||||||
return call;
|
return call;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Node* RawMachineAssembler::CallNWithFrameState(CallDescriptor* desc,
|
||||||
|
Node* function, Node** args,
|
||||||
|
Node* frame_state) {
|
||||||
|
DCHECK(desc->NeedsFrameState());
|
||||||
|
int param_count =
|
||||||
|
static_cast<int>(desc->GetMachineSignature()->parameter_count());
|
||||||
|
int input_count = param_count + 4;
|
||||||
|
Node** buffer = zone()->NewArray<Node*>(input_count);
|
||||||
|
int index = 0;
|
||||||
|
buffer[index++] = function;
|
||||||
|
for (int i = 0; i < param_count; i++) {
|
||||||
|
buffer[index++] = args[i];
|
||||||
|
}
|
||||||
|
buffer[index++] = frame_state;
|
||||||
|
buffer[index++] = graph()->start();
|
||||||
|
buffer[index++] = graph()->start();
|
||||||
|
Node* call = graph()->NewNode(common()->Call(desc), input_count, buffer);
|
||||||
|
schedule()->AddNode(CurrentBlock(), call);
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Node* RawMachineAssembler::TailCallN(CallDescriptor* desc, Node* function,
|
||||||
|
Node** args) {
|
||||||
|
int param_count =
|
||||||
|
static_cast<int>(desc->GetMachineSignature()->parameter_count());
|
||||||
|
int input_count = param_count + 3;
|
||||||
|
Node** buffer = zone()->NewArray<Node*>(input_count);
|
||||||
|
int index = 0;
|
||||||
|
buffer[index++] = function;
|
||||||
|
for (int i = 0; i < param_count; i++) {
|
||||||
|
buffer[index++] = args[i];
|
||||||
|
}
|
||||||
|
buffer[index++] = graph()->start();
|
||||||
|
buffer[index++] = graph()->start();
|
||||||
|
Node* tail_call =
|
||||||
|
graph()->NewNode(common()->TailCall(desc), input_count, buffer);
|
||||||
|
schedule()->AddTailCall(CurrentBlock(), tail_call);
|
||||||
|
return tail_call;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Node* RawMachineAssembler::CallFunctionStub0(Node* function, Node* receiver,
|
Node* RawMachineAssembler::CallFunctionStub0(Node* function, Node* receiver,
|
||||||
Node* context, Node* frame_state,
|
Node* context, Node* frame_state,
|
||||||
CallFunctionFlags flags) {
|
CallFunctionFlags flags) {
|
||||||
@ -134,18 +177,6 @@ Node* RawMachineAssembler::CallFunctionStub0(Node* function, Node* receiver,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Node* RawMachineAssembler::CallJS0(Node* function, Node* receiver,
|
|
||||||
Node* context, Node* frame_state) {
|
|
||||||
CallDescriptor* descriptor = Linkage::GetJSCallDescriptor(
|
|
||||||
zone(), false, 1, CallDescriptor::kNeedsFrameState);
|
|
||||||
Node* call =
|
|
||||||
graph()->NewNode(common()->Call(descriptor), function, receiver, context,
|
|
||||||
frame_state, graph()->start(), graph()->start());
|
|
||||||
schedule()->AddNode(CurrentBlock(), call);
|
|
||||||
return call;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Node* RawMachineAssembler::CallRuntime1(Runtime::FunctionId function,
|
Node* RawMachineAssembler::CallRuntime1(Runtime::FunctionId function,
|
||||||
Node* arg0, Node* context,
|
Node* arg0, Node* context,
|
||||||
Node* frame_state) {
|
Node* frame_state) {
|
||||||
@ -249,17 +280,6 @@ Node* RawMachineAssembler::CallCFunction8(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Node* RawMachineAssembler::TailCallInterpreterDispatch(
|
|
||||||
const CallDescriptor* call_descriptor, Node* target, Node* arg1, Node* arg2,
|
|
||||||
Node* arg3, Node* arg4, Node* arg5, Node* arg6) {
|
|
||||||
Node* tail_call = graph()->NewNode(common()->TailCall(call_descriptor),
|
|
||||||
target, arg1, arg2, arg3, arg4, arg5, arg6,
|
|
||||||
graph()->start(), graph()->start());
|
|
||||||
schedule()->AddTailCall(CurrentBlock(), tail_call);
|
|
||||||
return tail_call;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void RawMachineAssembler::Bind(Label* label) {
|
void RawMachineAssembler::Bind(Label* label) {
|
||||||
DCHECK(current_block_ == nullptr);
|
DCHECK(current_block_ == nullptr);
|
||||||
DCHECK(!label->bound_);
|
DCHECK(!label->bound_);
|
||||||
|
@ -481,13 +481,14 @@ class RawMachineAssembler {
|
|||||||
|
|
||||||
// Call a given call descriptor and the given arguments.
|
// Call a given call descriptor and the given arguments.
|
||||||
Node* CallN(CallDescriptor* desc, Node* function, Node** args);
|
Node* CallN(CallDescriptor* desc, Node* function, Node** args);
|
||||||
|
// Call a given call descriptor and the given arguments and frame-state.
|
||||||
|
Node* CallNWithFrameState(CallDescriptor* desc, Node* function, Node** args,
|
||||||
|
Node* frame_state);
|
||||||
|
// Tail call the given call descriptor and the given arguments.
|
||||||
|
Node* TailCallN(CallDescriptor* call_descriptor, Node* function, Node** args);
|
||||||
// Call through CallFunctionStub with lazy deopt and frame-state.
|
// Call through CallFunctionStub with lazy deopt and frame-state.
|
||||||
Node* CallFunctionStub0(Node* function, Node* receiver, Node* context,
|
Node* CallFunctionStub0(Node* function, Node* receiver, Node* context,
|
||||||
Node* frame_state, CallFunctionFlags flags);
|
Node* frame_state, CallFunctionFlags flags);
|
||||||
// Call to a JS function with zero arguments.
|
|
||||||
Node* CallJS0(Node* function, Node* receiver, Node* context,
|
|
||||||
Node* frame_state);
|
|
||||||
// Call to a runtime function with zero arguments.
|
// Call to a runtime function with zero arguments.
|
||||||
Node* CallRuntime1(Runtime::FunctionId function, Node* arg0, Node* context,
|
Node* CallRuntime1(Runtime::FunctionId function, Node* arg0, Node* context,
|
||||||
Node* frame_state);
|
Node* frame_state);
|
||||||
@ -508,10 +509,6 @@ class RawMachineAssembler {
|
|||||||
MachineType arg7_type, Node* function, Node* arg0,
|
MachineType arg7_type, Node* function, Node* arg0,
|
||||||
Node* arg1, Node* arg2, Node* arg3, Node* arg4,
|
Node* arg1, Node* arg2, Node* arg3, Node* arg4,
|
||||||
Node* arg5, Node* arg6, Node* arg7);
|
Node* arg5, Node* arg6, Node* arg7);
|
||||||
Node* TailCallInterpreterDispatch(const CallDescriptor* call_descriptor,
|
|
||||||
Node* target, Node* arg1, Node* arg2,
|
|
||||||
Node* arg3, Node* arg4, Node* arg5,
|
|
||||||
Node* arg6);
|
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// The following utility methods deal with control flow, hence might switch
|
// The following utility methods deal with control flow, hence might switch
|
||||||
|
@ -193,6 +193,8 @@ Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) {
|
|||||||
return Bytecode::kMul;
|
return Bytecode::kMul;
|
||||||
case Token::Value::DIV:
|
case Token::Value::DIV:
|
||||||
return Bytecode::kDiv;
|
return Bytecode::kDiv;
|
||||||
|
case Token::Value::MOD:
|
||||||
|
return Bytecode::kMod;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
return static_cast<Bytecode>(-1);
|
return static_cast<Bytecode>(-1);
|
||||||
|
@ -42,6 +42,7 @@ namespace interpreter {
|
|||||||
V(Sub, OperandType::kReg) \
|
V(Sub, OperandType::kReg) \
|
||||||
V(Mul, OperandType::kReg) \
|
V(Mul, OperandType::kReg) \
|
||||||
V(Div, OperandType::kReg) \
|
V(Div, OperandType::kReg) \
|
||||||
|
V(Mod, OperandType::kReg) \
|
||||||
\
|
\
|
||||||
/* Control Flow */ \
|
/* Control Flow */ \
|
||||||
V(Return, OperandType::kNone)
|
V(Return, OperandType::kNone)
|
||||||
|
@ -165,7 +165,8 @@ void Interpreter::DoLdaFalse(compiler::InterpreterAssembler* assembler) {
|
|||||||
//
|
//
|
||||||
// Load accumulator with value from register <src>.
|
// Load accumulator with value from register <src>.
|
||||||
void Interpreter::DoLdar(compiler::InterpreterAssembler* assembler) {
|
void Interpreter::DoLdar(compiler::InterpreterAssembler* assembler) {
|
||||||
Node* value = __ LoadRegister(__ BytecodeOperandReg(0));
|
Node* reg_index = __ BytecodeOperandReg(0);
|
||||||
|
Node* value = __ LoadRegister(reg_index);
|
||||||
__ SetAccumulator(value);
|
__ SetAccumulator(value);
|
||||||
__ Dispatch();
|
__ Dispatch();
|
||||||
}
|
}
|
||||||
@ -182,12 +183,24 @@ void Interpreter::DoStar(compiler::InterpreterAssembler* assembler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Interpreter::DoBinaryOp(Builtins::JavaScript binop_builtin,
|
||||||
|
compiler::InterpreterAssembler* assembler) {
|
||||||
|
// TODO(rmcilroy): Call ICs which back-patch bytecode with type specialized
|
||||||
|
// operations, instead of calling builtins directly.
|
||||||
|
Node* reg_index = __ BytecodeOperandReg(0);
|
||||||
|
Node* lhs = __ LoadRegister(reg_index);
|
||||||
|
Node* rhs = __ GetAccumulator();
|
||||||
|
Node* result = __ CallJSBuiltin(binop_builtin, lhs, rhs);
|
||||||
|
__ SetAccumulator(result);
|
||||||
|
__ Dispatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Add <src>
|
// Add <src>
|
||||||
//
|
//
|
||||||
// Add register <src> to accumulator.
|
// Add register <src> to accumulator.
|
||||||
void Interpreter::DoAdd(compiler::InterpreterAssembler* assembler) {
|
void Interpreter::DoAdd(compiler::InterpreterAssembler* assembler) {
|
||||||
// TODO(rmcilroy) Implement.
|
DoBinaryOp(Builtins::ADD, assembler);
|
||||||
__ Dispatch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -195,8 +208,7 @@ void Interpreter::DoAdd(compiler::InterpreterAssembler* assembler) {
|
|||||||
//
|
//
|
||||||
// Subtract register <src> from accumulator.
|
// Subtract register <src> from accumulator.
|
||||||
void Interpreter::DoSub(compiler::InterpreterAssembler* assembler) {
|
void Interpreter::DoSub(compiler::InterpreterAssembler* assembler) {
|
||||||
// TODO(rmcilroy) Implement.
|
DoBinaryOp(Builtins::SUB, assembler);
|
||||||
__ Dispatch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -204,8 +216,7 @@ void Interpreter::DoSub(compiler::InterpreterAssembler* assembler) {
|
|||||||
//
|
//
|
||||||
// Multiply accumulator by register <src>.
|
// Multiply accumulator by register <src>.
|
||||||
void Interpreter::DoMul(compiler::InterpreterAssembler* assembler) {
|
void Interpreter::DoMul(compiler::InterpreterAssembler* assembler) {
|
||||||
// TODO(rmcilroy) Implement add register to accumulator.
|
DoBinaryOp(Builtins::MUL, assembler);
|
||||||
__ Dispatch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -213,8 +224,15 @@ void Interpreter::DoMul(compiler::InterpreterAssembler* assembler) {
|
|||||||
//
|
//
|
||||||
// Divide register <src> by accumulator.
|
// Divide register <src> by accumulator.
|
||||||
void Interpreter::DoDiv(compiler::InterpreterAssembler* assembler) {
|
void Interpreter::DoDiv(compiler::InterpreterAssembler* assembler) {
|
||||||
// TODO(rmcilroy) Implement.
|
DoBinaryOp(Builtins::DIV, assembler);
|
||||||
__ Dispatch();
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Mod <src>
|
||||||
|
//
|
||||||
|
// Modulo register <src> by accumulator.
|
||||||
|
void Interpreter::DoMod(compiler::InterpreterAssembler* assembler) {
|
||||||
|
DoBinaryOp(Builtins::MOD, assembler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
// Do not include anything from src/interpreter other than
|
// Do not include anything from src/interpreter other than
|
||||||
// src/interpreter/bytecodes.h here!
|
// src/interpreter/bytecodes.h here!
|
||||||
#include "src/base/macros.h"
|
#include "src/base/macros.h"
|
||||||
|
#include "src/builtins.h"
|
||||||
#include "src/interpreter/bytecodes.h"
|
#include "src/interpreter/bytecodes.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
@ -46,6 +47,10 @@ class Interpreter {
|
|||||||
BYTECODE_LIST(DECLARE_BYTECODE_HANDLER_GENERATOR)
|
BYTECODE_LIST(DECLARE_BYTECODE_HANDLER_GENERATOR)
|
||||||
#undef DECLARE_BYTECODE_HANDLER_GENERATOR
|
#undef DECLARE_BYTECODE_HANDLER_GENERATOR
|
||||||
|
|
||||||
|
// Generates code to perform the binary operations via |binop_builtin|.
|
||||||
|
void DoBinaryOp(Builtins::JavaScript binop_builtin,
|
||||||
|
compiler::InterpreterAssembler* assembler);
|
||||||
|
|
||||||
bool IsInterpreterTableInitialized(Handle<FixedArray> handler_table);
|
bool IsInterpreterTableInitialized(Handle<FixedArray> handler_table);
|
||||||
|
|
||||||
Isolate* isolate_;
|
Isolate* isolate_;
|
||||||
|
@ -69,6 +69,7 @@ using v8::internal::BytecodeArray;
|
|||||||
using v8::internal::Handle;
|
using v8::internal::Handle;
|
||||||
using v8::internal::Object;
|
using v8::internal::Object;
|
||||||
using v8::internal::Smi;
|
using v8::internal::Smi;
|
||||||
|
using v8::internal::Token;
|
||||||
using namespace v8::internal::interpreter;
|
using namespace v8::internal::interpreter;
|
||||||
|
|
||||||
TEST(TestInterpreterReturn) {
|
TEST(TestInterpreterReturn) {
|
||||||
@ -206,3 +207,104 @@ TEST(TestInterpreterLoadStoreRegisters) {
|
|||||||
CHECK(return_val.is_identical_to(true_value));
|
CHECK(return_val.is_identical_to(true_value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(TestInterpreterAdd) {
|
||||||
|
InitializedHandleScope handles;
|
||||||
|
// TODO(rmcilroy): Do add tests for heap numbers and strings once we support
|
||||||
|
// them.
|
||||||
|
BytecodeArrayBuilder builder(handles.main_isolate());
|
||||||
|
builder.set_locals_count(1);
|
||||||
|
Register reg(0);
|
||||||
|
builder.LoadLiteral(Smi::FromInt(1))
|
||||||
|
.StoreAccumulatorInRegister(reg)
|
||||||
|
.LoadLiteral(Smi::FromInt(2))
|
||||||
|
.BinaryOperation(Token::Value::ADD, reg)
|
||||||
|
.Return();
|
||||||
|
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
|
||||||
|
|
||||||
|
InterpreterTester tester(handles.main_isolate(), bytecode_array);
|
||||||
|
InterpreterCallable callable(tester.GetCallable());
|
||||||
|
Handle<Object> return_val = callable().ToHandleChecked();
|
||||||
|
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(TestInterpreterSub) {
|
||||||
|
InitializedHandleScope handles;
|
||||||
|
// TODO(rmcilroy): Do add tests for heap numbers once we support them.
|
||||||
|
BytecodeArrayBuilder builder(handles.main_isolate());
|
||||||
|
builder.set_locals_count(1);
|
||||||
|
Register reg(0);
|
||||||
|
builder.LoadLiteral(Smi::FromInt(5))
|
||||||
|
.StoreAccumulatorInRegister(reg)
|
||||||
|
.LoadLiteral(Smi::FromInt(31))
|
||||||
|
.BinaryOperation(Token::Value::SUB, reg)
|
||||||
|
.Return();
|
||||||
|
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
|
||||||
|
|
||||||
|
InterpreterTester tester(handles.main_isolate(), bytecode_array);
|
||||||
|
InterpreterCallable callable(tester.GetCallable());
|
||||||
|
Handle<Object> return_val = callable().ToHandleChecked();
|
||||||
|
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(-26));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(TestInterpreterMul) {
|
||||||
|
InitializedHandleScope handles;
|
||||||
|
// TODO(rmcilroy): Do add tests for heap numbers once we support them.
|
||||||
|
BytecodeArrayBuilder builder(handles.main_isolate());
|
||||||
|
builder.set_locals_count(1);
|
||||||
|
Register reg(0);
|
||||||
|
builder.LoadLiteral(Smi::FromInt(111))
|
||||||
|
.StoreAccumulatorInRegister(reg)
|
||||||
|
.LoadLiteral(Smi::FromInt(6))
|
||||||
|
.BinaryOperation(Token::Value::MUL, reg)
|
||||||
|
.Return();
|
||||||
|
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
|
||||||
|
|
||||||
|
InterpreterTester tester(handles.main_isolate(), bytecode_array);
|
||||||
|
InterpreterCallable callable(tester.GetCallable());
|
||||||
|
Handle<Object> return_val = callable().ToHandleChecked();
|
||||||
|
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(666));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(TestInterpreterDiv) {
|
||||||
|
InitializedHandleScope handles;
|
||||||
|
// TODO(rmcilroy): Do add tests for heap numbers once we support them.
|
||||||
|
BytecodeArrayBuilder builder(handles.main_isolate());
|
||||||
|
builder.set_locals_count(1);
|
||||||
|
Register reg(0);
|
||||||
|
builder.LoadLiteral(Smi::FromInt(-20))
|
||||||
|
.StoreAccumulatorInRegister(reg)
|
||||||
|
.LoadLiteral(Smi::FromInt(5))
|
||||||
|
.BinaryOperation(Token::Value::DIV, reg)
|
||||||
|
.Return();
|
||||||
|
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
|
||||||
|
|
||||||
|
InterpreterTester tester(handles.main_isolate(), bytecode_array);
|
||||||
|
InterpreterCallable callable(tester.GetCallable());
|
||||||
|
Handle<Object> return_val = callable().ToHandleChecked();
|
||||||
|
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(-4));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(TestInterpreterMod) {
|
||||||
|
InitializedHandleScope handles;
|
||||||
|
// TODO(rmcilroy): Do add tests for heap numbers once we support them.
|
||||||
|
BytecodeArrayBuilder builder(handles.main_isolate());
|
||||||
|
builder.set_locals_count(1);
|
||||||
|
Register reg(0);
|
||||||
|
builder.LoadLiteral(Smi::FromInt(121))
|
||||||
|
.StoreAccumulatorInRegister(reg)
|
||||||
|
.LoadLiteral(Smi::FromInt(100))
|
||||||
|
.BinaryOperation(Token::Value::MOD, reg)
|
||||||
|
.Return();
|
||||||
|
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
|
||||||
|
|
||||||
|
InterpreterTester tester(handles.main_isolate(), bytecode_array);
|
||||||
|
InterpreterCallable callable(tester.GetCallable());
|
||||||
|
Handle<Object> return_val = callable().ToHandleChecked();
|
||||||
|
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(21));
|
||||||
|
}
|
||||||
|
@ -366,6 +366,9 @@ TARGET_TEST_F(InstructionSelectorTest, CallJSFunctionWithDeopt) {
|
|||||||
ZoneVector<MachineType> int32_type(1, kMachInt32, zone());
|
ZoneVector<MachineType> int32_type(1, kMachInt32, zone());
|
||||||
ZoneVector<MachineType> empty_types(zone());
|
ZoneVector<MachineType> empty_types(zone());
|
||||||
|
|
||||||
|
CallDescriptor* descriptor = Linkage::GetJSCallDescriptor(
|
||||||
|
zone(), false, 1, CallDescriptor::kNeedsFrameState);
|
||||||
|
|
||||||
Node* parameters =
|
Node* parameters =
|
||||||
m.NewNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(1));
|
m.NewNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(1));
|
||||||
Node* locals = m.NewNode(m.common()->TypedStateValues(&empty_types));
|
Node* locals = m.NewNode(m.common()->TypedStateValues(&empty_types));
|
||||||
@ -377,7 +380,9 @@ TARGET_TEST_F(InstructionSelectorTest, CallJSFunctionWithDeopt) {
|
|||||||
m.GetFrameStateFunctionInfo(1, 0)),
|
m.GetFrameStateFunctionInfo(1, 0)),
|
||||||
parameters, locals, stack, context_dummy, function_node,
|
parameters, locals, stack, context_dummy, function_node,
|
||||||
m.UndefinedConstant());
|
m.UndefinedConstant());
|
||||||
Node* call = m.CallJS0(function_node, receiver, context, state_node);
|
Node* args[] = {receiver, context};
|
||||||
|
Node* call =
|
||||||
|
m.CallNWithFrameState(descriptor, function_node, args, state_node);
|
||||||
m.Return(call);
|
m.Return(call);
|
||||||
|
|
||||||
Stream s = m.Build(kAllExceptNopInstructions);
|
Stream s = m.Build(kAllExceptNopInstructions);
|
||||||
|
@ -277,6 +277,52 @@ TARGET_TEST_F(InterpreterAssemblerTest, LoadContextSlot) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TARGET_TEST_F(InterpreterAssemblerTest, LoadObjectField) {
|
||||||
|
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
|
||||||
|
InterpreterAssemblerForTest m(this, bytecode);
|
||||||
|
Node* object = m.IntPtrConstant(0xdeadbeef);
|
||||||
|
int offset = 16;
|
||||||
|
Node* load_field = m.LoadObjectField(object, offset);
|
||||||
|
EXPECT_THAT(load_field,
|
||||||
|
m.IsLoad(kMachAnyTagged, object,
|
||||||
|
IsIntPtrConstant(offset - kHeapObjectTag)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TARGET_TEST_F(InterpreterAssemblerTest, CallJSBuiltin) {
|
||||||
|
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
|
||||||
|
InterpreterAssemblerForTest m(this, bytecode);
|
||||||
|
Node* receiver = m.IntPtrConstant(1234);
|
||||||
|
Node* call_js_builtin_0 = m.CallJSBuiltin(Builtins::SUB, receiver);
|
||||||
|
|
||||||
|
Matcher<Node*> load_globals_matcher = m.IsLoad(
|
||||||
|
kMachAnyTagged, IsParameter(Linkage::kInterpreterContextParameter),
|
||||||
|
IsIntPtrConstant(Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
|
||||||
|
Matcher<Node*> load_builtins_matcher = m.IsLoad(
|
||||||
|
kMachAnyTagged, load_globals_matcher,
|
||||||
|
IsIntPtrConstant(GlobalObject::kBuiltinsOffset - kHeapObjectTag));
|
||||||
|
Matcher<Node*> function_matcher =
|
||||||
|
m.IsLoad(kMachAnyTagged, load_builtins_matcher,
|
||||||
|
IsIntPtrConstant(
|
||||||
|
JSBuiltinsObject::OffsetOfFunctionWithId(Builtins::SUB) -
|
||||||
|
kHeapObjectTag));
|
||||||
|
Matcher<Node*> context_matcher =
|
||||||
|
m.IsLoad(kMachAnyTagged, function_matcher,
|
||||||
|
IsIntPtrConstant(JSFunction::kContextOffset - kHeapObjectTag));
|
||||||
|
EXPECT_THAT(call_js_builtin_0,
|
||||||
|
IsCall(_, function_matcher, receiver, context_matcher,
|
||||||
|
m.graph()->start(), m.graph()->start()));
|
||||||
|
|
||||||
|
Node* arg1 = m.Int32Constant(0xabcd);
|
||||||
|
Node* call_js_builtin_1 = m.CallJSBuiltin(Builtins::SUB, receiver, arg1);
|
||||||
|
EXPECT_THAT(call_js_builtin_1,
|
||||||
|
IsCall(_, function_matcher, receiver, arg1, context_matcher,
|
||||||
|
m.graph()->start(), m.graph()->start()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -1627,6 +1627,38 @@ Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
|
||||||
|
const Matcher<Node*>& value0_matcher,
|
||||||
|
const Matcher<Node*>& value1_matcher,
|
||||||
|
const Matcher<Node*>& value2_matcher,
|
||||||
|
const Matcher<Node*>& effect_matcher,
|
||||||
|
const Matcher<Node*>& control_matcher) {
|
||||||
|
std::vector<Matcher<Node*>> value_matchers;
|
||||||
|
value_matchers.push_back(value0_matcher);
|
||||||
|
value_matchers.push_back(value1_matcher);
|
||||||
|
value_matchers.push_back(value2_matcher);
|
||||||
|
return MakeMatcher(new IsCallMatcher(descriptor_matcher, value_matchers,
|
||||||
|
effect_matcher, control_matcher));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
|
||||||
|
const Matcher<Node*>& value0_matcher,
|
||||||
|
const Matcher<Node*>& value1_matcher,
|
||||||
|
const Matcher<Node*>& value2_matcher,
|
||||||
|
const Matcher<Node*>& value3_matcher,
|
||||||
|
const Matcher<Node*>& effect_matcher,
|
||||||
|
const Matcher<Node*>& control_matcher) {
|
||||||
|
std::vector<Matcher<Node*>> value_matchers;
|
||||||
|
value_matchers.push_back(value0_matcher);
|
||||||
|
value_matchers.push_back(value1_matcher);
|
||||||
|
value_matchers.push_back(value2_matcher);
|
||||||
|
value_matchers.push_back(value3_matcher);
|
||||||
|
return MakeMatcher(new IsCallMatcher(descriptor_matcher, value_matchers,
|
||||||
|
effect_matcher, control_matcher));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
|
Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
|
||||||
const Matcher<Node*>& value0_matcher,
|
const Matcher<Node*>& value0_matcher,
|
||||||
const Matcher<Node*>& value1_matcher,
|
const Matcher<Node*>& value1_matcher,
|
||||||
|
@ -105,6 +105,19 @@ Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
|
|||||||
const Matcher<Node*>& value1_matcher,
|
const Matcher<Node*>& value1_matcher,
|
||||||
const Matcher<Node*>& effect_matcher,
|
const Matcher<Node*>& effect_matcher,
|
||||||
const Matcher<Node*>& control_matcher);
|
const Matcher<Node*>& control_matcher);
|
||||||
|
Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
|
||||||
|
const Matcher<Node*>& value0_matcher,
|
||||||
|
const Matcher<Node*>& value1_matcher,
|
||||||
|
const Matcher<Node*>& value2_matcher,
|
||||||
|
const Matcher<Node*>& effect_matcher,
|
||||||
|
const Matcher<Node*>& control_matcher);
|
||||||
|
Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
|
||||||
|
const Matcher<Node*>& value0_matcher,
|
||||||
|
const Matcher<Node*>& value1_matcher,
|
||||||
|
const Matcher<Node*>& value2_matcher,
|
||||||
|
const Matcher<Node*>& value3_matcher,
|
||||||
|
const Matcher<Node*>& effect_matcher,
|
||||||
|
const Matcher<Node*>& control_matcher);
|
||||||
Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
|
Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
|
||||||
const Matcher<Node*>& value0_matcher,
|
const Matcher<Node*>& value0_matcher,
|
||||||
const Matcher<Node*>& value1_matcher,
|
const Matcher<Node*>& value1_matcher,
|
||||||
|
@ -41,7 +41,8 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
|||||||
builder.BinaryOperation(Token::Value::ADD, reg)
|
builder.BinaryOperation(Token::Value::ADD, reg)
|
||||||
.BinaryOperation(Token::Value::SUB, reg)
|
.BinaryOperation(Token::Value::SUB, reg)
|
||||||
.BinaryOperation(Token::Value::MUL, reg)
|
.BinaryOperation(Token::Value::MUL, reg)
|
||||||
.BinaryOperation(Token::Value::DIV, reg);
|
.BinaryOperation(Token::Value::DIV, reg)
|
||||||
|
.BinaryOperation(Token::Value::MOD, reg);
|
||||||
|
|
||||||
// Emit control flow. Return must be the last instruction.
|
// Emit control flow. Return must be the last instruction.
|
||||||
builder.Return();
|
builder.Return();
|
||||||
|
Loading…
Reference in New Issue
Block a user