Commit 1a063d94 authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Add support for calling runtime functions which return a pair.

Adds support for calling runtime functions which return a pair of
values. Adds the bytecode CallRuntimePair. Also adds support to TurboFan
for calling stubs which return multiple values.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#33181}
parent d006f617
......@@ -359,11 +359,10 @@ Callable CodeFactory::InterpreterPushArgsAndConstruct(Isolate* isolate) {
// static
Callable CodeFactory::InterpreterCEntry(Isolate* isolate) {
// TODO(rmcilroy): Deal with runtime functions that return two values.
Callable CodeFactory::InterpreterCEntry(Isolate* isolate, int result_size) {
// Note: If we ever use fpregs in the interpreter then we will need to
// save fpregs too.
CEntryStub stub(isolate, 1, kDontSaveFPRegs, kArgvInRegister);
CEntryStub stub(isolate, result_size, kDontSaveFPRegs, kArgvInRegister);
return Callable(stub.GetCode(), InterpreterCEntryDescriptor(isolate));
}
......
......@@ -110,7 +110,7 @@ class CodeFactory final {
static Callable InterpreterPushArgsAndCall(Isolate* isolate);
static Callable InterpreterPushArgsAndConstruct(Isolate* isolate);
static Callable InterpreterCEntry(Isolate* isolate);
static Callable InterpreterCEntry(Isolate* isolate, int result_size = 1);
};
} // namespace internal
......
......@@ -1199,6 +1199,12 @@ void BytecodeGraphBuilder::VisitCallRuntime(
}
void BytecodeGraphBuilder::VisitCallRuntimeForPair(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
Node* BytecodeGraphBuilder::ProcessCallNewArguments(
const Operator* call_new_op, interpreter::Register callee,
interpreter::Register first_arg, size_t arity) {
......
......@@ -148,6 +148,12 @@ Node* InterpreterAssembler::StoreRegister(Node* value, Node* reg_index) {
}
Node* InterpreterAssembler::NextRegister(Node* reg_index) {
// Register indexes are negative, so the next index is minus one.
return IntPtrAdd(reg_index, Int32Constant(-1));
}
Node* InterpreterAssembler::BytecodeOperand(int operand_index) {
DCHECK_LT(operand_index, interpreter::Bytecodes::NumberOfOperands(bytecode_));
DCHECK_EQ(interpreter::OperandSize::kByte,
......@@ -297,6 +303,7 @@ Node* InterpreterAssembler::BytecodeOperandIdx(int operand_index) {
Node* InterpreterAssembler::BytecodeOperandReg(int operand_index) {
switch (interpreter::Bytecodes::GetOperandType(bytecode_, operand_index)) {
case interpreter::OperandType::kReg8:
case interpreter::OperandType::kRegPair8:
case interpreter::OperandType::kMaybeReg8:
DCHECK_EQ(
interpreter::OperandSize::kByte,
......@@ -555,11 +562,11 @@ Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor,
Node* InterpreterAssembler::CallRuntime(Node* function_id, Node* first_arg,
Node* arg_count) {
Callable callable = CodeFactory::InterpreterCEntry(isolate());
Node* arg_count, int result_size) {
Callable callable = CodeFactory::InterpreterCEntry(isolate(), result_size);
CallDescriptor* descriptor = Linkage::GetStubCallDescriptor(
isolate(), zone(), callable.descriptor(), 0, CallDescriptor::kNoFlags);
isolate(), zone(), callable.descriptor(), 0, CallDescriptor::kNoFlags,
Operator::kNoProperties, MachineType::AnyTagged(), result_size);
Node* code_target = HeapConstant(callable.code());
// Get the function entry from the function id.
......
......@@ -67,6 +67,9 @@ class InterpreterAssembler {
Node* StoreRegister(Node* value, interpreter::Register reg);
Node* StoreRegister(Node* value, Node* reg_index);
// Returns the next consecutive register.
Node* NextRegister(Node* reg_index);
// Returns the location in memory of the register |reg_index| in the
// interpreter register file.
Node* RegisterLocation(Node* reg_index);
......@@ -130,7 +133,8 @@ class InterpreterAssembler {
Node* arg2, Node* arg3, Node* arg4, Node* arg5);
// Call runtime function.
Node* CallRuntime(Node* function_id, Node* first_arg, Node* arg_count);
Node* CallRuntime(Node* function_id, Node* first_arg, Node* arg_count,
int return_size = 1);
Node* CallRuntime(Runtime::FunctionId function_id, Node* arg1);
Node* CallRuntime(Runtime::FunctionId function_id, Node* arg1, Node* arg2);
Node* CallRuntime(Runtime::FunctionId function_id, Node* arg1, Node* arg2,
......
......@@ -430,21 +430,28 @@ CallDescriptor* Linkage::GetInterpreterDispatchDescriptor(Zone* zone) {
CallDescriptor* Linkage::GetStubCallDescriptor(
Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor,
int stack_parameter_count, CallDescriptor::Flags flags,
Operator::Properties properties, MachineType return_type) {
Operator::Properties properties, MachineType return_type,
size_t return_count) {
const int register_parameter_count = descriptor.GetRegisterParameterCount();
const int js_parameter_count =
register_parameter_count + stack_parameter_count;
const int context_count = 1;
const size_t return_count = 1;
const size_t parameter_count =
static_cast<size_t>(js_parameter_count + context_count);
LocationSignature::Builder locations(zone, return_count, parameter_count);
MachineSignature::Builder types(zone, return_count, parameter_count);
// Add return location.
locations.AddReturn(regloc(kReturnRegister0));
types.AddReturn(return_type);
// Add returns.
if (locations.return_count_ > 0) {
locations.AddReturn(regloc(kReturnRegister0));
}
if (locations.return_count_ > 1) {
locations.AddReturn(regloc(kReturnRegister1));
}
for (size_t i = 0; i < return_count; i++) {
types.AddReturn(return_type);
}
// Add parameters in registers and on the stack.
for (int i = 0; i < js_parameter_count; i++) {
......
......@@ -319,7 +319,8 @@ class Linkage : public ZoneObject {
Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor,
int stack_parameter_count, CallDescriptor::Flags flags,
Operator::Properties properties = Operator::kNoProperties,
MachineType return_type = MachineType::AnyTagged());
MachineType return_type = MachineType::AnyTagged(),
size_t return_count = 1);
// Creates a call descriptor for simplified C calls that is appropriate
// for the host platform. This simplified calling convention only supports
......
......@@ -1055,6 +1055,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::New(Register constructor,
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
Runtime::FunctionId function_id, Register first_arg, size_t arg_count) {
DCHECK_EQ(1, Runtime::FunctionForId(function_id)->result_size);
DCHECK(FitsInIdx16Operand(function_id));
DCHECK(FitsInIdx8Operand(arg_count));
if (!first_arg.is_valid()) {
......@@ -1067,6 +1068,23 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntimeForPair(
Runtime::FunctionId function_id, Register first_arg, size_t arg_count,
Register first_return) {
DCHECK_EQ(2, Runtime::FunctionForId(function_id)->result_size);
DCHECK(FitsInIdx16Operand(function_id));
DCHECK(FitsInIdx8Operand(arg_count));
if (!first_arg.is_valid()) {
DCHECK_EQ(0u, arg_count);
first_arg = Register(0);
}
Output(Bytecode::kCallRuntimeForPair, static_cast<uint16_t>(function_id),
first_arg.ToOperand(), static_cast<uint8_t>(arg_count),
first_return.ToOperand());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime(int context_index,
Register receiver,
size_t arg_count) {
......@@ -1196,6 +1214,21 @@ bool BytecodeArrayBuilder::TemporaryRegisterIsLive(Register reg) const {
}
bool BytecodeArrayBuilder::RegisterIsValid(Register reg) const {
if (reg.is_function_context() || reg.is_function_closure() ||
reg.is_new_target()) {
return true;
} else if (reg.is_parameter()) {
int parameter_index = reg.ToParameterIndex(parameter_count_);
return parameter_index >= 0 && parameter_index < parameter_count_;
} else if (reg.index() < fixed_register_count()) {
return true;
} else {
return TemporaryRegisterIsLive(reg);
}
}
bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
uint32_t operand_value) const {
OperandType operand_type = Bytecodes::GetOperandType(bytecode, operand_index);
......@@ -1214,38 +1247,22 @@ bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
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() ||
reg.is_new_target()) {
return true;
} else if (reg.is_parameter()) {
int parameter_index = reg.ToParameterIndex(parameter_count_);
return parameter_index >= 0 && parameter_index < parameter_count_;
} else if (reg.index() < fixed_register_count()) {
return true;
} else {
return TemporaryRegisterIsLive(reg);
}
case OperandType::kReg8:
return RegisterIsValid(
Register::FromOperand(static_cast<uint8_t>(operand_value)));
case OperandType::kRegPair8: {
Register reg0 =
Register::FromOperand(static_cast<uint8_t>(operand_value));
Register reg1 = Register(reg0.index() + 1);
return RegisterIsValid(reg0) && RegisterIsValid(reg1);
}
case OperandType::kReg16: {
case OperandType::kReg16:
if (bytecode != Bytecode::kExchange &&
bytecode != Bytecode::kExchangeWide) {
return false;
}
Register reg =
Register::FromWideOperand(static_cast<uint16_t>(operand_value));
if (reg.is_function_context() || reg.is_function_closure() ||
reg.is_new_target()) {
return false;
} else if (reg.is_parameter()) {
return false;
} else if (reg.index() < fixed_register_count()) {
return true;
} else {
return TemporaryRegisterIsLive(reg);
}
}
return RegisterIsValid(
Register::FromWideOperand(static_cast<uint16_t>(operand_value)));
}
UNREACHABLE();
return false;
......
......@@ -167,6 +167,14 @@ class BytecodeArrayBuilder final {
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
Register first_arg, size_t arg_count);
// Call the runtime function with |function_id| that returns a pair of values.
// The first argument should be in |first_arg| and all subsequent arguments
// should be in registers <first_arg + 1> to <first_arg + 1 + arg_count>. The
// return values will be returned in <first_return> and <first_return + 1>.
BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id,
Register first_arg, size_t arg_count,
Register first_return);
// 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>.
......@@ -293,6 +301,8 @@ class BytecodeArrayBuilder final {
bool NeedToBooleanCast();
bool IsRegisterInAccumulator(Register reg);
bool RegisterIsValid(Register reg) const;
// Temporary register management.
int BorrowTemporaryRegister();
int BorrowTemporaryRegisterNotInRange(int start_index, int end_index);
......
......@@ -88,6 +88,7 @@ Register BytecodeArrayIterator::GetRegisterOperand(int operand_index) const {
OperandType operand_type =
Bytecodes::GetOperandType(current_bytecode(), operand_index);
DCHECK(operand_type == OperandType::kReg8 ||
operand_type == OperandType::kRegPair8 ||
operand_type == OperandType::kMaybeReg8 ||
operand_type == OperandType::kReg16);
uint32_t operand = GetRawOperand(operand_index, operand_type);
......
......@@ -1690,8 +1690,6 @@ void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
DCHECK(args->length() == 0 || first_arg.index() == receiver.index() + 1);
builder()->CallJSRuntime(expr->context_index(), receiver, args->length());
} else {
// 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());
}
......
......@@ -293,6 +293,17 @@ std::ostream& Bytecodes::Decode(std::ostream& os, const uint8_t* bytecode_start,
}
break;
}
case interpreter::OperandType::kRegPair8: {
Register reg = Register::FromOperand(*operand_start);
if (reg.is_parameter()) {
int parameter_index = reg.ToParameterIndex(parameter_count);
DCHECK_NE(parameter_index, 0);
os << "a" << parameter_index - 1 << "-" << parameter_index;
} else {
os << "r" << reg.index() << "-" << reg.index() + 1;
}
break;
}
case interpreter::OperandType::kReg16: {
Register reg =
Register::FromWideOperand(ReadUnalignedUInt16(operand_start));
......
......@@ -27,6 +27,7 @@ namespace interpreter {
V(Idx8, OperandSize::kByte) \
V(MaybeReg8, OperandSize::kByte) \
V(Reg8, OperandSize::kByte) \
V(RegPair8, OperandSize::kByte) \
\
/* Short operands. */ \
V(Count16, OperandSize::kShort) \
......@@ -145,6 +146,8 @@ namespace interpreter {
OperandType::kIdx16) \
V(CallRuntime, OperandType::kIdx16, OperandType::kMaybeReg8, \
OperandType::kCount8) \
V(CallRuntimeForPair, OperandType::kIdx16, OperandType::kMaybeReg8, \
OperandType::kCount8, OperandType::kRegPair8) \
V(CallJSRuntime, OperandType::kIdx16, OperandType::kReg8, \
OperandType::kCount8) \
\
......
......@@ -1086,6 +1086,33 @@ void Interpreter::DoCallRuntime(compiler::InterpreterAssembler* assembler) {
}
// CallRuntimeForPair <function_id> <first_arg> <arg_count> <first_return>
//
// Call the runtime function |function_id| which returns a pair, with the
// first argument in register |first_arg| and |arg_count| arguments in
// subsequent registers. Returns the result in <first_return> and
// <first_return + 1>
void Interpreter::DoCallRuntimeForPair(
compiler::InterpreterAssembler* assembler) {
// Call the runtime function.
Node* function_id = __ BytecodeOperandIdx(0);
Node* first_arg_reg = __ BytecodeOperandReg(1);
Node* first_arg = __ RegisterLocation(first_arg_reg);
Node* args_count = __ BytecodeOperandCount(2);
Node* result_pair = __ CallRuntime(function_id, first_arg, args_count, 2);
// Store the results in <first_return> and <first_return + 1>
Node* first_return_reg = __ BytecodeOperandReg(3);
Node* second_return_reg = __ NextRegister(first_return_reg);
Node* result0 = __ Projection(0, result_pair);
Node* result1 = __ Projection(1, result_pair);
__ StoreRegister(result0, first_return_reg);
__ StoreRegister(result1, second_return_reg);
__ Dispatch();
}
// CallJSRuntime <context_index> <receiver> <arg_count>
//
// Call the JS runtime function that has the |context_index| with the receiver
......
......@@ -350,6 +350,7 @@ TARGET_TEST_F(InterpreterAssemblerTest, BytecodeOperand) {
break;
case interpreter::OperandType::kMaybeReg8:
case interpreter::OperandType::kReg8:
case interpreter::OperandType::kRegPair8:
EXPECT_THAT(m.BytecodeOperandReg(i),
m.IsBytecodeOperandSignExtended(offset));
break;
......@@ -592,29 +593,33 @@ TARGET_TEST_F(InterpreterAssemblerTest, CallRuntime2) {
TARGET_TEST_F(InterpreterAssemblerTest, CallRuntime) {
const int kResultSizes[] = {1, 2};
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
Callable builtin = CodeFactory::InterpreterCEntry(isolate());
Node* function_id = m.Int32Constant(0);
Node* first_arg = m.Int32Constant(1);
Node* arg_count = m.Int32Constant(2);
Matcher<Node*> function_table = IsExternalConstant(
ExternalReference::runtime_function_table_address(isolate()));
Matcher<Node*> function = IsIntPtrAdd(
function_table,
IsInt32Mul(function_id, IsInt32Constant(sizeof(Runtime::Function))));
Matcher<Node*> function_entry =
m.IsLoad(MachineType::Pointer(), function,
IsInt32Constant(offsetof(Runtime::Function, entry)));
Node* call_runtime = m.CallRuntime(function_id, first_arg, arg_count);
EXPECT_THAT(
call_runtime,
IsCall(_, IsHeapConstant(builtin.code()), arg_count, first_arg,
function_entry,
IsParameter(Linkage::kInterpreterContextParameter), _, _));
TRACED_FOREACH(int, result_size, kResultSizes) {
InterpreterAssemblerForTest m(this, bytecode);
Callable builtin = CodeFactory::InterpreterCEntry(isolate(), result_size);
Node* function_id = m.Int32Constant(0);
Node* first_arg = m.Int32Constant(1);
Node* arg_count = m.Int32Constant(2);
Matcher<Node*> function_table = IsExternalConstant(
ExternalReference::runtime_function_table_address(isolate()));
Matcher<Node*> function = IsIntPtrAdd(
function_table,
IsInt32Mul(function_id, IsInt32Constant(sizeof(Runtime::Function))));
Matcher<Node*> function_entry =
m.IsLoad(MachineType::Pointer(), function,
IsInt32Constant(offsetof(Runtime::Function, entry)));
Node* call_runtime =
m.CallRuntime(function_id, first_arg, arg_count, result_size);
EXPECT_THAT(
call_runtime,
IsCall(_, IsHeapConstant(builtin.code()), arg_count, first_arg,
function_entry,
IsParameter(Linkage::kInterpreterContextParameter), _, _));
}
}
}
......
......@@ -109,6 +109,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
builder.Call(reg, reg, 0, 0)
.Call(reg, reg, 0, 1024)
.CallRuntime(Runtime::kIsArray, reg, 1)
.CallRuntimeForPair(Runtime::kLoadLookupSlot, reg, 1, reg)
.CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, reg, 1);
// Emit binary operator invocations.
......
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