Commit 41f3e782 authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Add support for JS runtime calls.

Adds support for calling JS runtime functions. Also changes the bytecode
array builder to allow calling functions with an invalid argument
register if the call takes no arguments.

Adds the bytecode CallJSRuntime.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#31774}
parent b5c80a31
......@@ -495,6 +495,12 @@ void BytecodeGraphBuilder::VisitCallRuntime(
}
void BytecodeGraphBuilder::VisitCallJSRuntime(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitNew(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
......
......@@ -226,8 +226,12 @@ Node* InterpreterAssembler::BytecodeOperandIdx(int operand_index) {
Node* InterpreterAssembler::BytecodeOperandReg(int operand_index) {
DCHECK_EQ(interpreter::OperandType::kReg8,
interpreter::Bytecodes::GetOperandType(bytecode_, operand_index));
#ifdef DEBUG
interpreter::OperandType operand_type =
interpreter::Bytecodes::GetOperandType(bytecode_, operand_index);
DCHECK(operand_type == interpreter::OperandType::kReg8 ||
operand_type == interpreter::OperandType::kMaybeReg8);
#endif
return BytecodeOperandSignExtended(operand_index);
}
......
......@@ -782,6 +782,10 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
BytecodeArrayBuilder& BytecodeArrayBuilder::New(Register constructor,
Register first_arg,
size_t arg_count) {
if (!first_arg.is_valid()) {
DCHECK_EQ(0, arg_count);
first_arg = Register(0);
}
DCHECK(FitsInIdx8Operand(arg_count));
Output(Bytecode::kNew, constructor.ToOperand(), first_arg.ToOperand(),
static_cast<uint8_t>(arg_count));
......@@ -793,12 +797,27 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
Runtime::FunctionId function_id, Register first_arg, size_t arg_count) {
DCHECK(FitsInIdx16Operand(function_id));
DCHECK(FitsInIdx8Operand(arg_count));
if (!first_arg.is_valid()) {
DCHECK_EQ(0, arg_count);
first_arg = Register(0);
}
Output(Bytecode::kCallRuntime, static_cast<uint16_t>(function_id),
first_arg.ToOperand(), static_cast<uint8_t>(arg_count));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime(int context_index,
Register receiver,
size_t arg_count) {
DCHECK(FitsInIdx16Operand(context_index));
DCHECK(FitsInIdx8Operand(arg_count));
Output(Bytecode::kCallJSRuntime, static_cast<uint16_t>(context_index),
receiver.ToOperand(), static_cast<uint8_t>(arg_count));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Delete(Register object,
LanguageMode language_mode) {
Output(BytecodeForDelete(language_mode), object.ToOperand());
......@@ -910,6 +929,11 @@ bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
case OperandType::kImm8:
case OperandType::kIdx8:
return static_cast<uint8_t>(operand_value) == operand_value;
case OperandType::kMaybeReg8:
if (operand_value == 0) {
return true;
}
// Fall-through to kReg8 case.
case OperandType::kReg8: {
Register reg = Register::FromOperand(static_cast<uint8_t>(operand_value));
if (reg.is_function_context() || reg.is_function_closure()) {
......
......@@ -150,6 +150,12 @@ class BytecodeArrayBuilder {
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
Register first_arg, size_t arg_count);
// Call the JS runtime function with |context_index|. The the receiver should
// be in |receiver| and all subsequent arguments should be in registers
// <receiver + 1> to <receiver + 1 + arg_count>.
BytecodeArrayBuilder& CallJSRuntime(int context_index, Register receiver,
size_t arg_count);
// Operators (register holds the lhs value, accumulator holds the rhs value).
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg,
Strength strength);
......
......@@ -76,7 +76,11 @@ int BytecodeArrayIterator::GetIndexOperand(int operand_index) const {
Register BytecodeArrayIterator::GetRegisterOperand(int operand_index) const {
uint32_t operand = GetRawOperand(operand_index, OperandType::kReg8);
OperandType operand_type =
Bytecodes::GetOperandType(current_bytecode(), operand_index);
DCHECK(operand_type == OperandType::kReg8 ||
operand_type == OperandType::kMaybeReg8);
uint32_t operand = GetRawOperand(operand_index, operand_type);
return Register::FromOperand(operand);
}
......
......@@ -1475,6 +1475,10 @@ void BytecodeGenerator::VisitProperty(Property* expr) {
Register BytecodeGenerator::VisitArguments(ZoneList<Expression*>* args) {
if (args->length() == 0) {
return Register();
}
// Visit arguments and place in a contiguous block of temporary
// registers. Return the first temporary register corresponding to
// the first argument.
......@@ -1554,10 +1558,8 @@ void BytecodeGenerator::VisitCall(Call* expr) {
// Evaluate all arguments to the function call and store in sequential
// registers.
if (args->length() > 0) {
Register arg = VisitArguments(args);
CHECK(arg.index() == receiver.index() + 1);
}
Register arg = VisitArguments(args);
CHECK(args->length() == 0 || arg.index() == receiver.index() + 1);
// TODO(rmcilroy): Deal with possible direct eval here?
// TODO(rmcilroy): Use CallIC to allow call type feedback.
......@@ -1572,40 +1574,33 @@ void BytecodeGenerator::VisitCallNew(CallNew* expr) {
builder()->StoreAccumulatorInRegister(constructor);
ZoneList<Expression*>* args = expr->arguments();
if (args->length() > 0) {
Register first_arg = VisitArguments(args);
builder()->New(constructor, first_arg, args->length());
} else {
// The second argument here will be ignored as there are zero
// arguments. Using the constructor register avoids avoid
// allocating a temporary just to fill the operands.
builder()->New(constructor, constructor, 0);
}
Register first_arg = VisitArguments(args);
builder()->New(constructor, first_arg, args->length());
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
Register receiver;
if (expr->is_jsruntime()) {
UNIMPLEMENTED();
// Allocate a register for the receiver and load it with undefined.
execution_result()->PrepareForConsecutiveAllocations(args->length() + 1);
receiver = execution_result()->NextConsecutiveRegister();
builder()->LoadUndefined().StoreAccumulatorInRegister(receiver);
}
// TODO(rmcilroy): support multiple return values.
DCHECK_LE(expr->function()->result_size, 1);
Runtime::FunctionId function_id = expr->function()->function_id;
// Evaluate all arguments to the runtime call.
ZoneList<Expression*>* args = expr->arguments();
Register first_arg;
if (args->length() > 0) {
first_arg = VisitArguments(args);
Register first_arg = VisitArguments(args);
if (expr->is_jsruntime()) {
DCHECK(args->length() == 0 || first_arg.index() == receiver.index() + 1);
builder()->CallJSRuntime(expr->context_index(), receiver, args->length());
} else {
// Allocation here is just to fullfil the requirement that there
// is a register operand for the start of the arguments though
// there are zero when this is generated.
first_arg = execution_result()->NewRegister();
// TODO(rmcilroy): support multiple return values.
DCHECK_LE(expr->function()->result_size, 1);
Runtime::FunctionId function_id = expr->function()->function_id;
builder()->CallRuntime(function_id, first_arg, args->length());
}
builder()->CallRuntime(function_id, first_arg, args->length());
execution_result()->SetResultInAccumulator();
}
......
......@@ -231,7 +231,8 @@ std::ostream& Bytecodes::Decode(std::ostream& os, const uint8_t* bytecode_start,
case interpreter::OperandType::kImm8:
os << "#" << static_cast<int>(static_cast<int8_t>(*operand_start));
break;
case interpreter::OperandType::kReg8: {
case interpreter::OperandType::kReg8:
case interpreter::OperandType::kMaybeReg8: {
Register reg = Register::FromOperand(*operand_start);
if (reg.is_function_context()) {
os << "<context>";
......
......@@ -16,18 +16,19 @@ namespace internal {
namespace interpreter {
// The list of operand types used by bytecodes.
#define OPERAND_TYPE_LIST(V) \
\
/* None operand. */ \
V(None, OperandSize::kNone) \
\
/* Byte operands. */ \
V(Count8, OperandSize::kByte) \
V(Imm8, OperandSize::kByte) \
V(Idx8, OperandSize::kByte) \
V(Reg8, OperandSize::kByte) \
\
/* Short operands. */ \
#define OPERAND_TYPE_LIST(V) \
\
/* None operand. */ \
V(None, OperandSize::kNone) \
\
/* Byte operands. */ \
V(Count8, OperandSize::kByte) \
V(Imm8, OperandSize::kByte) \
V(Idx8, OperandSize::kByte) \
V(Reg8, OperandSize::kByte) \
V(MaybeReg8, OperandSize::kByte) \
\
/* Short operands. */ \
V(Idx16, OperandSize::kShort)
// The list of bytecodes which are interpreted by the interpreter.
......@@ -121,11 +122,13 @@ namespace interpreter {
\
/* Call operations */ \
V(Call, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8) \
V(CallRuntime, OperandType::kIdx16, OperandType::kReg8, \
V(CallRuntime, OperandType::kIdx16, OperandType::kMaybeReg8, \
OperandType::kCount8) \
V(CallJSRuntime, OperandType::kIdx16, OperandType::kReg8, \
OperandType::kCount8) \
\
/* New operator */ \
V(New, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8) \
V(New, OperandType::kReg8, OperandType::kMaybeReg8, OperandType::kCount8) \
\
/* Test Operators */ \
V(TestEqual, OperandType::kReg8) \
......
......@@ -914,6 +914,30 @@ void Interpreter::DoCallRuntime(compiler::InterpreterAssembler* assembler) {
}
// CallJSRuntime <context_index> <receiver> <arg_count>
//
// Call the JS runtime function that has the |context_index| with the receiver
// in register |receiver| and |arg_count| arguments in subsequent registers.
void Interpreter::DoCallJSRuntime(compiler::InterpreterAssembler* assembler) {
Node* context_index = __ BytecodeOperandIdx(0);
Node* receiver_reg = __ BytecodeOperandReg(1);
Node* first_arg = __ RegisterLocation(receiver_reg);
Node* args_count = __ BytecodeOperandCount(2);
// Get the function to call from the native context.
Node* context = __ GetContext();
Node* global = __ LoadContextSlot(context, Context::GLOBAL_OBJECT_INDEX);
Node* native_context =
__ LoadObjectField(global, JSGlobalObject::kNativeContextOffset);
Node* function = __ LoadContextSlot(native_context, context_index);
// Call the function.
Node* result = __ CallJS(function, first_arg, args_count);
__ SetAccumulator(result);
__ Dispatch();
}
// New <constructor> <arg_count>
//
// Call operator new with |constructor| and the first argument in
......
......@@ -1547,10 +1547,10 @@ TEST(CallRuntime) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<int> snippets[] = {
ExpectedSnippet<InstanceType> snippets[] = {
{
"function f() { %TheHole() }\nf()",
1 * kPointerSize,
0,
1,
7,
{
......@@ -1585,6 +1585,24 @@ TEST(CallRuntime) {
B(Return) //
},
},
{
"function f() { return %spread_iterable([1]) }\nf()",
2 * kPointerSize,
1,
16,
{
B(LdaUndefined), //
B(Star), R(0), //
B(LdaConstant), U8(0), //
B(CreateArrayLiteral), U8(0), U8(3), //
B(Star), R(1), //
B(CallJSRuntime), U16(Context::SPREAD_ITERABLE_INDEX), R(0), //
U8(1), //
B(Return), //
},
1,
{InstanceType::FIXED_ARRAY_TYPE},
},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
......
......@@ -317,6 +317,7 @@ TARGET_TEST_F(InterpreterAssemblerTest, BytecodeOperand) {
EXPECT_THAT(m.BytecodeOperandImm(i),
m.IsBytecodeOperandSignExtended(offset));
break;
case interpreter::OperandType::kMaybeReg8:
case interpreter::OperandType::kReg8:
EXPECT_THAT(m.BytecodeOperandReg(i),
m.IsBytecodeOperandSignExtended(offset));
......
......@@ -99,8 +99,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.CreateObjectLiteral(0, 0);
// Call operations.
builder.Call(reg, reg, 0);
builder.CallRuntime(Runtime::kIsArray, reg, 1);
builder.Call(reg, reg, 0)
.CallRuntime(Runtime::kIsArray, reg, 1)
.CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, reg, 1);
// Emit binary operator invocations.
builder.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment