[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) {
|
||||
return raw_assembler_->Load(kMachAnyTagged, ContextTaggedPointer(),
|
||||
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() {
|
||||
Node* exit_trampoline_code_object =
|
||||
HeapConstant(Unique<HeapObject>::CreateImmovable(
|
||||
@ -204,10 +246,14 @@ void InterpreterAssembler::Return() {
|
||||
STATIC_ASSERT(3 == Linkage::kInterpreterBytecodeArrayParameter);
|
||||
STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter);
|
||||
STATIC_ASSERT(5 == Linkage::kInterpreterContextParameter);
|
||||
Node* tail_call = raw_assembler_->TailCallInterpreterDispatch(
|
||||
call_descriptor(), exit_trampoline_code_object, GetAccumulator(),
|
||||
RegisterFileRawPointer(), BytecodeOffset(), BytecodeArrayTaggedPointer(),
|
||||
DispatchTableRawPointer(), ContextTaggedPointer());
|
||||
Node* args[] = { GetAccumulator(),
|
||||
RegisterFileRawPointer(),
|
||||
BytecodeOffset(),
|
||||
BytecodeArrayTaggedPointer(),
|
||||
DispatchTableRawPointer(),
|
||||
ContextTaggedPointer() };
|
||||
Node* tail_call = raw_assembler_->TailCallN(
|
||||
call_descriptor(), exit_trampoline_code_object, args);
|
||||
// This should always be the end node.
|
||||
SetEndInput(tail_call);
|
||||
}
|
||||
@ -237,11 +283,14 @@ void InterpreterAssembler::Dispatch() {
|
||||
STATIC_ASSERT(3 == Linkage::kInterpreterBytecodeArrayParameter);
|
||||
STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter);
|
||||
STATIC_ASSERT(5 == Linkage::kInterpreterContextParameter);
|
||||
Node* tail_call = raw_assembler_->TailCallInterpreterDispatch(
|
||||
call_descriptor(), target_code_object, GetAccumulator(),
|
||||
RegisterFileRawPointer(), new_bytecode_offset,
|
||||
BytecodeArrayTaggedPointer(), DispatchTableRawPointer(),
|
||||
ContextTaggedPointer());
|
||||
Node* args[] = { GetAccumulator(),
|
||||
RegisterFileRawPointer(),
|
||||
new_bytecode_offset,
|
||||
BytecodeArrayTaggedPointer(),
|
||||
DispatchTableRawPointer(),
|
||||
ContextTaggedPointer() };
|
||||
Node* tail_call =
|
||||
raw_assembler_->TailCallN(call_descriptor(), target_code_object, args);
|
||||
// This should always be the end node.
|
||||
SetEndInput(tail_call);
|
||||
}
|
||||
@ -278,6 +327,8 @@ Schedule* InterpreterAssembler::schedule() {
|
||||
}
|
||||
|
||||
|
||||
Zone* InterpreterAssembler::zone() { return raw_assembler_->zone(); }
|
||||
|
||||
|
||||
} // namespace interpreter
|
||||
} // namespace internal
|
||||
|
@ -9,6 +9,7 @@
|
||||
// Do not include anything from src/compiler here!
|
||||
#include "src/allocation.h"
|
||||
#include "src/base/smart-pointers.h"
|
||||
#include "src/builtins.h"
|
||||
#include "src/frames.h"
|
||||
#include "src/interpreter/bytecodes.h"
|
||||
#include "src/unique.h"
|
||||
@ -61,9 +62,16 @@ class InterpreterAssembler {
|
||||
Node* SmiTag(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.
|
||||
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.
|
||||
void Return();
|
||||
|
||||
@ -97,6 +105,9 @@ class InterpreterAssembler {
|
||||
Node* BytecodeOperand(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
|
||||
// update BytecodeOffset() itself.
|
||||
Node* Advance(int delta);
|
||||
@ -107,6 +118,7 @@ class InterpreterAssembler {
|
||||
// Private helpers which delegate to RawMachineAssembler.
|
||||
Isolate* isolate();
|
||||
Schedule* schedule();
|
||||
Zone* zone();
|
||||
|
||||
interpreter::Bytecode bytecode_;
|
||||
base::SmartPointer<RawMachineAssembler> raw_assembler_;
|
||||
|
@ -104,7 +104,8 @@ Node* RawMachineAssembler::CallN(CallDescriptor* desc, Node* function,
|
||||
Node** args) {
|
||||
int param_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;
|
||||
buffer[index++] = function;
|
||||
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();
|
||||
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);
|
||||
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* context, Node* frame_state,
|
||||
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* arg0, Node* context,
|
||||
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) {
|
||||
DCHECK(current_block_ == nullptr);
|
||||
DCHECK(!label->bound_);
|
||||
|
@ -481,13 +481,14 @@ class RawMachineAssembler {
|
||||
|
||||
// Call a given call descriptor and the given arguments.
|
||||
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.
|
||||
Node* CallFunctionStub0(Node* function, Node* receiver, Node* context,
|
||||
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.
|
||||
Node* CallRuntime1(Runtime::FunctionId function, Node* arg0, Node* context,
|
||||
Node* frame_state);
|
||||
@ -508,10 +509,6 @@ class RawMachineAssembler {
|
||||
MachineType arg7_type, Node* function, Node* arg0,
|
||||
Node* arg1, Node* arg2, Node* arg3, Node* arg4,
|
||||
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
|
||||
|
@ -193,6 +193,8 @@ Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) {
|
||||
return Bytecode::kMul;
|
||||
case Token::Value::DIV:
|
||||
return Bytecode::kDiv;
|
||||
case Token::Value::MOD:
|
||||
return Bytecode::kMod;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
return static_cast<Bytecode>(-1);
|
||||
|
@ -42,6 +42,7 @@ namespace interpreter {
|
||||
V(Sub, OperandType::kReg) \
|
||||
V(Mul, OperandType::kReg) \
|
||||
V(Div, OperandType::kReg) \
|
||||
V(Mod, OperandType::kReg) \
|
||||
\
|
||||
/* Control Flow */ \
|
||||
V(Return, OperandType::kNone)
|
||||
|
@ -165,7 +165,8 @@ void Interpreter::DoLdaFalse(compiler::InterpreterAssembler* assembler) {
|
||||
//
|
||||
// Load accumulator with value from register <src>.
|
||||
void Interpreter::DoLdar(compiler::InterpreterAssembler* assembler) {
|
||||
Node* value = __ LoadRegister(__ BytecodeOperandReg(0));
|
||||
Node* reg_index = __ BytecodeOperandReg(0);
|
||||
Node* value = __ LoadRegister(reg_index);
|
||||
__ SetAccumulator(value);
|
||||
__ 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 register <src> to accumulator.
|
||||
void Interpreter::DoAdd(compiler::InterpreterAssembler* assembler) {
|
||||
// TODO(rmcilroy) Implement.
|
||||
__ Dispatch();
|
||||
DoBinaryOp(Builtins::ADD, assembler);
|
||||
}
|
||||
|
||||
|
||||
@ -195,8 +208,7 @@ void Interpreter::DoAdd(compiler::InterpreterAssembler* assembler) {
|
||||
//
|
||||
// Subtract register <src> from accumulator.
|
||||
void Interpreter::DoSub(compiler::InterpreterAssembler* assembler) {
|
||||
// TODO(rmcilroy) Implement.
|
||||
__ Dispatch();
|
||||
DoBinaryOp(Builtins::SUB, assembler);
|
||||
}
|
||||
|
||||
|
||||
@ -204,8 +216,7 @@ void Interpreter::DoSub(compiler::InterpreterAssembler* assembler) {
|
||||
//
|
||||
// Multiply accumulator by register <src>.
|
||||
void Interpreter::DoMul(compiler::InterpreterAssembler* assembler) {
|
||||
// TODO(rmcilroy) Implement add register to accumulator.
|
||||
__ Dispatch();
|
||||
DoBinaryOp(Builtins::MUL, assembler);
|
||||
}
|
||||
|
||||
|
||||
@ -213,8 +224,15 @@ void Interpreter::DoMul(compiler::InterpreterAssembler* assembler) {
|
||||
//
|
||||
// Divide register <src> by accumulator.
|
||||
void Interpreter::DoDiv(compiler::InterpreterAssembler* assembler) {
|
||||
// TODO(rmcilroy) Implement.
|
||||
__ Dispatch();
|
||||
DoBinaryOp(Builtins::DIV, assembler);
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
// src/interpreter/bytecodes.h here!
|
||||
#include "src/base/macros.h"
|
||||
#include "src/builtins.h"
|
||||
#include "src/interpreter/bytecodes.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -46,6 +47,10 @@ class Interpreter {
|
||||
BYTECODE_LIST(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);
|
||||
|
||||
Isolate* isolate_;
|
||||
|
@ -69,6 +69,7 @@ using v8::internal::BytecodeArray;
|
||||
using v8::internal::Handle;
|
||||
using v8::internal::Object;
|
||||
using v8::internal::Smi;
|
||||
using v8::internal::Token;
|
||||
using namespace v8::internal::interpreter;
|
||||
|
||||
TEST(TestInterpreterReturn) {
|
||||
@ -206,3 +207,104 @@ TEST(TestInterpreterLoadStoreRegisters) {
|
||||
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> empty_types(zone());
|
||||
|
||||
CallDescriptor* descriptor = Linkage::GetJSCallDescriptor(
|
||||
zone(), false, 1, CallDescriptor::kNeedsFrameState);
|
||||
|
||||
Node* parameters =
|
||||
m.NewNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(1));
|
||||
Node* locals = m.NewNode(m.common()->TypedStateValues(&empty_types));
|
||||
@ -377,7 +380,9 @@ TARGET_TEST_F(InstructionSelectorTest, CallJSFunctionWithDeopt) {
|
||||
m.GetFrameStateFunctionInfo(1, 0)),
|
||||
parameters, locals, stack, context_dummy, function_node,
|
||||
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);
|
||||
|
||||
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 internal
|
||||
} // 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,
|
||||
const Matcher<Node*>& value0_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*>& 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*>& 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,
|
||||
const Matcher<Node*>& value0_matcher,
|
||||
const Matcher<Node*>& value1_matcher,
|
||||
|
@ -41,7 +41,8 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
builder.BinaryOperation(Token::Value::ADD, reg)
|
||||
.BinaryOperation(Token::Value::SUB, 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.
|
||||
builder.Return();
|
||||
|
Loading…
Reference in New Issue
Block a user