Commit e7fb2339 authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Add support for JS calls.

Adds support for JS calls to the interpreter. In order to support
calls from the interpreter, the PushArgsAndCall builtin is added
which pushes a sequence of arguments onto the stack and calls
builtin::Call.

Adds the Call bytecode.

MIPS port contributed by akos.palfi@imgtec.com in https://codereview.chromium.org/1334873002/

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#30710}
parent d3df2b05
......@@ -1610,6 +1610,35 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
}
// static
void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r0 : the number of arguments (not including the receiver)
// -- r2 : the address of the first argument to be pushed. Subsequent
// arguments should be consecutive above this, in the same order as
// they are to be pushed onto the stack.
// -- r1 : the target to call (can be any Object).
// Find the address of the last argument.
__ add(r3, r0, Operand(1)); // Add one for receiver.
__ mov(r3, Operand(r3, LSL, kPointerSizeLog2));
__ sub(r3, r2, r3);
// Push the arguments.
Label loop_header, loop_check;
__ b(al, &loop_check);
__ bind(&loop_header);
__ ldr(r4, MemOperand(r2, -kPointerSize, PostIndex));
__ push(r4);
__ bind(&loop_check);
__ cmp(r2, r3);
__ b(gt, &loop_header);
// Call the target.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
}
void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r0 : actual number of arguments
......
......@@ -406,6 +406,18 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor::
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void PushArgsAndCallDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
r0, // argument count (including receiver)
r2, // address of first argument
r1 // the target callable to be call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8
......
......@@ -2246,7 +2246,7 @@ void Assembler::debug(const char* message, uint32_t code, Instr params) {
#ifdef USE_SIMULATOR
// Don't generate simulator specific code if we are building a snapshot, which
// might be run on real hardware.
if (!serializer_enabled()) {
// if (!serializer_enabled()) {
// The arguments to the debug marker need to be contiguous in memory, so
// make sure we don't try to emit pools.
BlockPoolsScope scope(this);
......@@ -2266,7 +2266,7 @@ void Assembler::debug(const char* message, uint32_t code, Instr params) {
hlt(kImmExceptionIsUnreachable);
return;
}
// }
// Fall through if Serializer is enabled.
#endif
......
......@@ -1655,6 +1655,38 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
}
// static
void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- x0 : the number of arguments (not including the receiver)
// -- x2 : the address of the first argument to be pushed. Subsequent
// arguments should be consecutive above this, in the same order as
// they are to be pushed onto the stack.
// -- x1 : the target to call (can be any Object).
// Find the address of the last argument.
__ add(x3, x0, Operand(1)); // Add one for receiver.
__ lsl(x3, x3, kPointerSizeLog2);
__ sub(x4, x2, x3);
// Push the arguments.
Label loop_header, loop_check;
__ Mov(x5, jssp);
__ Claim(x3, 1);
__ B(&loop_check);
__ Bind(&loop_header);
// TODO(rmcilroy): Push two at a time once we ensure we keep stack aligned.
__ Ldr(x3, MemOperand(x2, -kPointerSize, PostIndex));
__ Str(x3, MemOperand(x5, -kPointerSize, PreIndex));
__ Bind(&loop_check);
__ Cmp(x2, x4);
__ B(gt, &loop_header);
// Call the target.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
}
void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
ASM_LOCATION("Builtins::Generate_ArgumentsAdaptorTrampoline");
// ----------- S t a t e -------------
......
......@@ -435,6 +435,18 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor::
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void PushArgsAndCallDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
x0, // argument count (including receiver)
x2, // address of first argument
x1 // the target callable to be call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8
......
......@@ -74,6 +74,8 @@ enum BuiltinExtraArguments {
V(CallFunction, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(Call, BUILTIN, UNINITIALIZED, kNoExtraICState) \
\
V(PushArgsAndCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
\
V(InOptimizationQueue, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSConstructStubForDerived, BUILTIN, UNINITIALIZED, kNoExtraICState) \
......@@ -270,6 +272,8 @@ class Builtins {
// ES6 section 7.3.12 Call(F, V, [argumentsList])
static void Generate_Call(MacroAssembler* masm);
static void Generate_PushArgsAndCall(MacroAssembler* masm);
static void Generate_FunctionCall(MacroAssembler* masm);
static void Generate_FunctionApply(MacroAssembler* masm);
static void Generate_ReflectApply(MacroAssembler* masm);
......
......@@ -242,5 +242,12 @@ Callable CodeFactory::CallFunction(Isolate* isolate, int argc,
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::PushArgsAndCall(Isolate* isolate) {
return Callable(isolate->builtins()->PushArgsAndCall(),
PushArgsAndCallDescriptor(isolate));
}
} // namespace internal
} // namespace v8
......@@ -91,6 +91,8 @@ class CodeFactory final {
static Callable CallFunction(Isolate* isolate, int argc,
CallFunctionFlags flags);
static Callable PushArgsAndCall(Isolate* isolate);
};
} // namespace internal
......
......@@ -273,6 +273,12 @@ void BytecodeGraphBuilder::VisitKeyedStoreIC(
}
void BytecodeGraphBuilder::VisitCall(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::BuildBinaryOp(
const Operator* js_op, const interpreter::BytecodeArrayIterator& iterator) {
Node* left = environment()->LookupRegister(iterator.GetRegisterOperand(0));
......
......@@ -6,6 +6,7 @@
#include <ostream>
#include "src/code-factory.h"
#include "src/compiler/graph.h"
#include "src/compiler/instruction-selector.h"
#include "src/compiler/linkage.h"
......@@ -102,7 +103,12 @@ Node* InterpreterAssembler::DispatchTableRawPointer() {
Node* InterpreterAssembler::RegisterFrameOffset(Node* index) {
return raw_assembler_->WordShl(index, Int32Constant(kPointerSizeLog2));
return WordShl(index, kPointerSizeLog2);
}
Node* InterpreterAssembler::RegisterLocation(Node* reg_index) {
return IntPtrAdd(RegisterFileRawPointer(), RegisterFrameOffset(reg_index));
}
......@@ -122,8 +128,7 @@ Node* InterpreterAssembler::BytecodeOperand(int operand_index) {
DCHECK_LT(operand_index, interpreter::Bytecodes::NumberOfOperands(bytecode_));
return raw_assembler_->Load(
kMachUint8, BytecodeArrayTaggedPointer(),
raw_assembler_->IntPtrAdd(BytecodeOffset(),
Int32Constant(1 + operand_index)));
IntPtrAdd(BytecodeOffset(), Int32Constant(1 + operand_index)));
}
......@@ -131,8 +136,7 @@ Node* InterpreterAssembler::BytecodeOperandSignExtended(int operand_index) {
DCHECK_LT(operand_index, interpreter::Bytecodes::NumberOfOperands(bytecode_));
Node* load = raw_assembler_->Load(
kMachInt8, BytecodeArrayTaggedPointer(),
raw_assembler_->IntPtrAdd(BytecodeOffset(),
Int32Constant(1 + operand_index)));
IntPtrAdd(BytecodeOffset(), Int32Constant(1 + operand_index)));
// Ensure that we sign extend to full pointer size
if (kPointerSize == 8) {
load = raw_assembler_->ChangeInt32ToInt64(load);
......@@ -141,6 +145,13 @@ Node* InterpreterAssembler::BytecodeOperandSignExtended(int operand_index) {
}
Node* InterpreterAssembler::BytecodeOperandCount(int operand_index) {
DCHECK_EQ(interpreter::OperandType::kCount,
interpreter::Bytecodes::GetOperandType(bytecode_, operand_index));
return BytecodeOperand(operand_index);
}
Node* InterpreterAssembler::BytecodeOperandImm8(int operand_index) {
DCHECK_EQ(interpreter::OperandType::kImm8,
interpreter::Bytecodes::GetOperandType(bytecode_, operand_index));
......@@ -197,12 +208,27 @@ Node* InterpreterAssembler::SmiUntag(Node* value) {
}
Node* InterpreterAssembler::IntPtrAdd(Node* a, Node* b) {
return raw_assembler_->IntPtrAdd(a, b);
}
Node* InterpreterAssembler::IntPtrSub(Node* a, Node* b) {
return raw_assembler_->IntPtrSub(a, b);
}
Node* InterpreterAssembler::WordShl(Node* value, int shift) {
return raw_assembler_->WordShl(value, Int32Constant(shift));
}
Node* InterpreterAssembler::LoadConstantPoolEntry(Node* index) {
Node* constant_pool = LoadObjectField(BytecodeArrayTaggedPointer(),
BytecodeArray::kConstantPoolOffset);
Node* entry_offset = raw_assembler_->IntPtrAdd(
IntPtrConstant(FixedArray::kHeaderSize - kHeapObjectTag),
raw_assembler_->WordShl(index, Int32Constant(kPointerSizeLog2)));
Node* entry_offset =
IntPtrAdd(IntPtrConstant(FixedArray::kHeaderSize - kHeapObjectTag),
WordShl(index, kPointerSizeLog2));
return raw_assembler_->Load(kMachAnyTagged, constant_pool, entry_offset);
}
......@@ -236,6 +262,24 @@ Node* InterpreterAssembler::LoadTypeFeedbackVector() {
}
Node* InterpreterAssembler::CallJS(Node* function, Node* first_arg,
Node* arg_count) {
Callable builtin = CodeFactory::PushArgsAndCall(isolate());
CallDescriptor* descriptor = Linkage::GetStubCallDescriptor(
isolate(), zone(), builtin.descriptor(), 0, CallDescriptor::kNoFlags);
Node* code_target = HeapConstant(builtin.code());
Node** args = zone()->NewArray<Node*>(4);
args[0] = arg_count;
args[1] = first_arg;
args[2] = function;
args[3] = ContextTaggedPointer();
return raw_assembler_->CallN(descriptor, code_target, args);
}
Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor,
Node* target, Node** args) {
CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
......@@ -302,7 +346,7 @@ void InterpreterAssembler::Return() {
Node* InterpreterAssembler::Advance(int delta) {
return raw_assembler_->IntPtrAdd(BytecodeOffset(), Int32Constant(delta));
return IntPtrAdd(BytecodeOffset(), Int32Constant(delta));
}
......
......@@ -38,7 +38,10 @@ class InterpreterAssembler {
Handle<Code> GenerateCode();
// Returns the Idx immediate for bytecode operand |operand_index| in the
// Returns the count immediate for bytecode operand |operand_index| in the
// current bytecode.
Node* BytecodeOperandCount(int operand_index);
// Returns the index immediate for bytecode operand |operand_index| in the
// current bytecode.
Node* BytecodeOperandIdx(int operand_index);
// Returns the Imm8 immediate for bytecode operand |operand_index| in the
......@@ -56,6 +59,10 @@ class InterpreterAssembler {
Node* LoadRegister(Node* reg_index);
Node* StoreRegister(Node* value, Node* reg_index);
// Returns the location in memory of the register |reg_index| in the
// interpreter register file.
Node* RegisterLocation(Node* reg_index);
// Constants.
Node* Int32Constant(int value);
Node* IntPtrConstant(intptr_t value);
......@@ -66,6 +73,11 @@ class InterpreterAssembler {
Node* SmiTag(Node* value);
Node* SmiUntag(Node* value);
// Basic arithmetic operations.
Node* IntPtrAdd(Node* a, Node* b);
Node* IntPtrSub(Node* a, Node* b);
Node* WordShl(Node* value, int shift);
// Load constant at |index| in the constant pool.
Node* LoadConstantPoolEntry(Node* index);
......@@ -81,6 +93,10 @@ class InterpreterAssembler {
// Load the TypeFeedbackVector for the current function.
Node* LoadTypeFeedbackVector();
// Call JSFunction or Callable |function| with |arg_count| (not including
// receiver) and the first argument located at |first_arg|.
Node* CallJS(Node* function, Node* first_arg, Node* arg_count);
// Call an IC code stub.
Node* CallIC(CallInterfaceDescriptor descriptor, Node* target, Node* arg1,
Node* arg2, Node* arg3, Node* arg4);
......
......@@ -1534,6 +1534,41 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
}
// static
void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : the number of arguments (not including the receiver)
// -- ebx : the address of the first argument to be pushed. Subsequent
// arguments should be consecutive above this, in the same order as
// they are to be pushed onto the stack.
// -- edi : the target to call (can be any Object).
// Pop return address to allow tail-call after pushing arguments.
__ Pop(edx);
// Find the address of the last argument.
__ mov(ecx, eax);
__ add(ecx, Immediate(1)); // Add one for receiver.
__ shl(ecx, kPointerSizeLog2);
__ neg(ecx);
__ add(ecx, ebx);
// Push the arguments.
Label loop_header, loop_check;
__ jmp(&loop_check);
__ bind(&loop_header);
__ Push(Operand(ebx, 0));
__ sub(ebx, Immediate(kPointerSize));
__ bind(&loop_check);
__ cmp(ebx, ecx);
__ j(greater, &loop_header, Label::kNear);
// Call the target.
__ Push(edx); // Re-push return address.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
}
void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : actual number of arguments
......
......@@ -389,6 +389,18 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor::
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void PushArgsAndCallDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
eax, // argument count (including receiver)
ebx, // address of first argument
edi // the target callable to be call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8
......
......@@ -38,6 +38,7 @@ class PlatformInterfaceDescriptor;
V(CallFunctionWithFeedbackAndVector) \
V(CallConstruct) \
V(CallTrampoline) \
V(PushArgsAndCall) \
V(RegExpConstructResult) \
V(TransitionElementsKind) \
V(AllocateHeapNumber) \
......@@ -681,6 +682,12 @@ class GrowArrayElementsDescriptor : public CallInterfaceDescriptor {
static const Register KeyRegister();
};
class PushArgsAndCallDescriptor : public CallInterfaceDescriptor {
public:
DECLARE_DESCRIPTOR(PushArgsAndCallDescriptor, CallInterfaceDescriptor)
};
#undef DECLARE_DESCRIPTOR
......
......@@ -224,6 +224,19 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Return() {
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
Register receiver,
size_t arg_count) {
if (FitsInByteOperand(arg_count)) {
Output(Bytecode::kCall, callable.ToOperand(), receiver.ToOperand(),
static_cast<uint8_t>(arg_count));
} else {
UNIMPLEMENTED();
}
return *this;
}
size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) {
// These constants shouldn't be added to the constant pool, the should use
// specialzed bytecodes instead.
......@@ -267,6 +280,7 @@ bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
switch (operand_type) {
case OperandType::kNone:
return false;
case OperandType::kCount:
case OperandType::kImm8:
case OperandType::kIdx:
return true;
......
......@@ -67,6 +67,13 @@ class BytecodeArrayBuilder {
int feedback_slot,
LanguageMode language_mode);
// Call a JS function. The JSFunction or Callable to be called should be in
// |callable|, the receiver should be in |receiver| and all subsequent
// arguments should be in registers <receiver + 1> to
// <receiver + 1 + arg_count>.
BytecodeArrayBuilder& Call(Register callable, Register receiver,
size_t arg_count);
// Operators.
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg);
......
......@@ -342,27 +342,18 @@ void BytecodeGenerator::VisitYield(Yield* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitThrow(Throw* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitProperty(Property* expr) {
void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* expr) {
LhsKind property_kind = Property::GetAssignType(expr);
FeedbackVectorICSlot slot = expr->PropertyFeedbackSlot();
switch (property_kind) {
case VARIABLE:
UNREACHABLE();
break;
case NAMED_PROPERTY: {
TemporaryRegisterScope temporary_register_scope(&builder_);
Register obj = temporary_register_scope.NewRegister();
Visit(expr->obj());
builder().StoreAccumulatorInRegister(obj);
builder().LoadLiteral(expr->key()->AsLiteral()->AsPropertyName());
builder().LoadNamedProperty(obj, feedback_index(slot), language_mode());
break;
}
case KEYED_PROPERTY: {
TemporaryRegisterScope temporary_register_scope(&builder_);
Register obj = temporary_register_scope.NewRegister();
Visit(expr->obj());
builder().StoreAccumulatorInRegister(obj);
Visit(expr->key());
builder().LoadKeyedProperty(obj, feedback_index(slot), language_mode());
break;
......@@ -374,7 +365,60 @@ void BytecodeGenerator::VisitProperty(Property* expr) {
}
void BytecodeGenerator::VisitCall(Call* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitProperty(Property* expr) {
TemporaryRegisterScope temporary_register_scope(&builder_);
Register obj = temporary_register_scope.NewRegister();
Visit(expr->obj());
builder().StoreAccumulatorInRegister(obj);
VisitPropertyLoad(obj, expr);
}
void BytecodeGenerator::VisitCall(Call* expr) {
Expression* callee_expr = expr->expression();
Call::CallType call_type = expr->GetCallType(isolate());
// Prepare the callee and the receiver to the function call. This depends on
// the semantics of the underlying call type.
TemporaryRegisterScope temporary_register_scope(&builder_);
Register callee = temporary_register_scope.NewRegister();
Register receiver = temporary_register_scope.NewRegister();
switch (call_type) {
case Call::PROPERTY_CALL: {
Property* property = callee_expr->AsProperty();
if (property->IsSuperAccess()) {
UNIMPLEMENTED();
}
Visit(property->obj());
builder().StoreAccumulatorInRegister(receiver);
// Perform a property load of the callee.
VisitPropertyLoad(receiver, property);
builder().StoreAccumulatorInRegister(callee);
break;
}
case Call::GLOBAL_CALL:
case Call::LOOKUP_SLOT_CALL:
case Call::SUPER_CALL:
case Call::POSSIBLY_EVAL_CALL:
case Call::OTHER_CALL:
UNIMPLEMENTED();
}
// Evaluate all arguments to the function call and store in sequential
// registers.
ZoneList<Expression*>* args = expr->arguments();
for (int i = 0; i < args->length(); ++i) {
Visit(args->at(i));
Register arg = temporary_register_scope.NewRegister();
DCHECK(arg.index() - i == receiver.index() + 1);
builder().StoreAccumulatorInRegister(arg);
}
// TODO(rmcilroy): Deal with possible direct eval here?
// TODO(rmcilroy): Use CallIC to allow call type feedback.
builder().Call(callee, receiver, args->length());
}
void BytecodeGenerator::VisitCallNew(CallNew* expr) { UNIMPLEMENTED(); }
......
......@@ -28,6 +28,7 @@ class BytecodeGenerator : public AstVisitor {
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
void VisitArithmeticExpression(BinaryOperation* binop);
void VisitPropertyLoad(Register obj, Property* expr);
inline BytecodeArrayBuilder& builder() { return builder_; }
inline Scope* scope() const { return scope_; }
......
......@@ -126,6 +126,9 @@ std::ostream& Bytecodes::Decode(std::ostream& os, const uint8_t* bytecode_start,
OperandType op_type = GetOperandType(bytecode, i);
uint8_t operand = operands_start[i];
switch (op_type) {
case interpreter::OperandType::kCount:
os << "#" << static_cast<unsigned int>(operand);
break;
case interpreter::OperandType::kIdx:
os << "[" << static_cast<unsigned int>(operand) << "]";
break;
......
......@@ -18,6 +18,7 @@ namespace interpreter {
// The list of operand types used by bytecodes.
#define OPERAND_TYPE_LIST(V) \
V(None) \
V(Count) \
V(Imm8) \
V(Idx) \
V(Reg)
......@@ -54,6 +55,9 @@ namespace interpreter {
V(Div, OperandType::kReg) \
V(Mod, OperandType::kReg) \
\
/* Call operations. */ \
V(Call, OperandType::kReg, OperandType::kReg, OperandType::kCount) \
\
/* Control Flow */ \
V(Return, OperandType::kNone)
......
......@@ -322,6 +322,22 @@ void Interpreter::DoMod(compiler::InterpreterAssembler* assembler) {
}
// Call <receiver> <arg_count>
//
// Call a JS function with receiver and |arg_count| arguments in subsequent
// registers. The JSfunction or Callable to call is in the accumulator.
void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
Node* function_reg = __ BytecodeOperandReg(0);
Node* function = __ LoadRegister(function_reg);
Node* receiver_reg = __ BytecodeOperandReg(1);
Node* first_arg = __ RegisterLocation(receiver_reg);
Node* args_count = __ BytecodeOperandCount(2);
Node* result = __ CallJS(function, first_arg, args_count);
__ SetAccumulator(result);
__ Dispatch();
}
// Return
//
// Return the value in register 0.
......
......@@ -1619,6 +1619,35 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
}
// static
void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- a0 : the number of arguments (not including the receiver)
// -- a2 : the address of the first argument to be pushed. Subsequent
// arguments should be consecutive above this, in the same order as
// they are to be pushed onto the stack.
// -- a1 : the target to call (can be any Object).
// Find the address of the last argument.
__ Addu(a3, a0, Operand(1)); // Add one for receiver.
__ sll(a3, a3, kPointerSizeLog2);
__ Subu(a3, a2, Operand(a3));
// Push the arguments.
Label loop_header, loop_check;
__ Branch(&loop_check);
__ bind(&loop_header);
__ lw(t0, MemOperand(a2));
__ Addu(a2, a2, Operand(-kPointerSize));
__ push(t0);
__ bind(&loop_check);
__ Branch(&loop_header, gt, a2, Operand(a3));
// Call the target.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
}
void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// State setup as expected by MacroAssembler::InvokePrologue.
// ----------- S t a t e -------------
......
......@@ -381,6 +381,18 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor::
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void PushArgsAndCallDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
a0, // argument count (including receiver)
a2, // address of first argument
a1 // the target callable to be call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8
......
......@@ -1616,6 +1616,35 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
}
// static
void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- a0 : the number of arguments (not including the receiver)
// -- a2 : the address of the first argument to be pushed. Subsequent
// arguments should be consecutive above this, in the same order as
// they are to be pushed onto the stack.
// -- a1 : the target to call (can be any Object).
// Find the address of the last argument.
__ Daddu(a3, a0, Operand(1)); // Add one for receiver.
__ dsll(a3, a3, kPointerSizeLog2);
__ Dsubu(a3, a2, Operand(a3));
// Push the arguments.
Label loop_header, loop_check;
__ Branch(&loop_check);
__ bind(&loop_header);
__ ld(a4, MemOperand(a2));
__ Daddu(a2, a2, Operand(-kPointerSize));
__ push(a4);
__ bind(&loop_check);
__ Branch(&loop_header, gt, a2, Operand(a3));
// Call the target.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
}
void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// State setup as expected by MacroAssembler::InvokePrologue.
// ----------- S t a t e -------------
......
......@@ -381,6 +381,18 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor::
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void PushArgsAndCallDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
a0, // argument count (including receiver)
a2, // address of first argument
a1 // the target callable to be call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8
......
......@@ -1722,6 +1722,41 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
}
// static
void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : the number of arguments (not including the receiver)
// -- rbx : the address of the first argument to be pushed. Subsequent
// arguments should be consecutive above this, in the same order as
// they are to be pushed onto the stack.
// -- rdi : the target to call (can be any Object).
// Pop return address to allow tail-call after pushing arguments.
__ Pop(rdx);
// Find the address of the last argument.
__ movp(rcx, rax);
__ addp(rcx, Immediate(1)); // Add one for receiver.
__ shlp(rcx, Immediate(kPointerSizeLog2));
__ negp(rcx);
__ addp(rcx, rbx);
// Push the arguments.
Label loop_header, loop_check;
__ j(always, &loop_check);
__ bind(&loop_header);
__ Push(Operand(rbx, 0));
__ subp(rbx, Immediate(kPointerSize));
__ bind(&loop_check);
__ cmpp(rbx, rcx);
__ j(greater, &loop_header, Label::kNear);
// Call the target.
__ Push(rdx); // Re-push return address.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
}
void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) {
// Lookup the function in the JavaScript frame.
__ movp(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
......
......@@ -381,6 +381,18 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor::
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void PushArgsAndCallDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
rax, // argument count (including receiver)
rbx, // address of first argument
rdi // the target callable to be call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8
......
......@@ -64,7 +64,7 @@ struct ExpectedSnippet {
int frame_size;
int parameter_count;
int bytecode_length;
const uint8_t bytecode[24];
const uint8_t bytecode[32];
int constant_count;
T constants[16];
};
......@@ -338,7 +338,7 @@ TEST(PropertyLoads) {
BytecodeGeneratorHelper helper;
Code::Kind ic_kinds[] = { i::Code::LOAD_IC, i::Code::LOAD_IC };
FeedbackVectorSpec feedback_spec(0, 1, ic_kinds);
FeedbackVectorSpec feedback_spec(0, 2, ic_kinds);
Handle<i::TypeFeedbackVector> vector =
helper.factory()->NewTypeFeedbackVector(&feedback_spec);
......@@ -427,7 +427,7 @@ TEST(PropertyStores) {
BytecodeGeneratorHelper helper;
Code::Kind ic_kinds[] = { i::Code::STORE_IC, i::Code::STORE_IC };
FeedbackVectorSpec feedback_spec(0, 1, ic_kinds);
FeedbackVectorSpec feedback_spec(0, 2, ic_kinds);
Handle<i::TypeFeedbackVector> vector =
helper.factory()->NewTypeFeedbackVector(&feedback_spec);
......@@ -525,6 +525,89 @@ TEST(PropertyStores) {
}
}
#define FUNC_ARG "new (function Obj() { this.func = function() { return; }})()"
TEST(PropertyCall) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
Code::Kind ic_kinds[] = { i::Code::LOAD_IC, i::Code::LOAD_IC };
FeedbackVectorSpec feedback_spec(0, 2, ic_kinds);
Handle<i::TypeFeedbackVector> vector =
helper.factory()->NewTypeFeedbackVector(&feedback_spec);
ExpectedSnippet<const char*> snippets[] = {
{"function f(a) { return a.func(); }\nf(" FUNC_ARG ")",
2 * kPointerSize, 2, 16,
{
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(1),
B(LdaConstant), U8(0),
B(LoadIC), R(1), U8(vector->first_ic_slot_index() + 2),
B(Star), R(0),
B(Call), R(0), R(1), U8(0),
B(Return)
},
1, { "func" }
},
{"function f(a, b, c) { return a.func(b, c); }\nf(" FUNC_ARG ", 1, 2)",
4 * kPointerSize, 4, 24,
{
B(Ldar), R(helper.kLastParamIndex - 2),
B(Star), R(1),
B(LdaConstant), U8(0),
B(LoadIC), R(1), U8(vector->first_ic_slot_index() + 2),
B(Star), R(0),
B(Ldar), R(helper.kLastParamIndex - 1),
B(Star), R(2),
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(3),
B(Call), R(0), R(1), U8(2),
B(Return)
},
1, { "func" }
},
{"function f(a, b) { return a.func(b + b, b); }\nf(" FUNC_ARG ", 1)",
4 * kPointerSize, 3, 30,
{
B(Ldar), R(helper.kLastParamIndex - 1),
B(Star), R(1),
B(LdaConstant), U8(0),
B(LoadIC), R(1), U8(vector->first_ic_slot_index() + 2),
B(Star), R(0),
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(2),
B(Ldar), R(helper.kLastParamIndex),
B(Add), R(2),
B(Star), R(2),
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(3),
B(Call), R(0), R(1), U8(2),
B(Return)
},
1, { "func" }
}
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
ba->length()));
CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count);
for (int j = 0; j < snippets[i].constant_count; j++) {
Handle<String> expected =
helper.factory()->NewStringFromAsciiChecked(snippets[i].constants[j]);
CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected));
}
}
}
} // namespace interpreter
} // namespace internal
} // namespance v8
......@@ -476,7 +476,7 @@ TEST(InterpreterLoadNamedProperty) {
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
i::Code::Kind ic_kinds[] = { i::Code::LOAD_IC };
i::Code::Kind ic_kinds[] = {i::Code::LOAD_IC};
i::FeedbackVectorSpec feedback_spec(0, 1, ic_kinds);
Handle<i::TypeFeedbackVector> vector =
factory->NewTypeFeedbackVector(&feedback_spec);
......@@ -662,3 +662,146 @@ TEST(InterpreterStoreKeyedProperty) {
CHECK(Runtime::GetObjectProperty(isolate, object2, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
}
TEST(InterpreterCall) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
i::Code::Kind ic_kinds[] = { i::Code::LOAD_IC };
i::FeedbackVectorSpec feedback_spec(0, 1, ic_kinds);
Handle<i::TypeFeedbackVector> vector =
factory->NewTypeFeedbackVector(&feedback_spec);
Handle<i::String> name = factory->NewStringFromAsciiChecked("func");
name = factory->string_table()->LookupString(isolate, name);
// Check with no args.
{
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(1);
builder.set_parameter_count(1);
builder.LoadLiteral(name)
.LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(),
i::SLOPPY)
.StoreAccumulatorInRegister(Register(0))
.Call(Register(0), builder.Parameter(0), 0)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject(
"new (function Obj() { this.func = function() { return 0x265; }})()");
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(0x265));
}
// Check that receiver is passed properly.
{
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(1);
builder.set_parameter_count(1);
builder.LoadLiteral(name)
.LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(),
i::SLOPPY)
.StoreAccumulatorInRegister(Register(0))
.Call(Register(0), builder.Parameter(0), 0)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject(
"new (function Obj() {"
" this.val = 1234;"
" this.func = function() { return this.val; };"
"})()");
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(1234));
}
// Check with two parameters (+ receiver).
{
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(4);
builder.set_parameter_count(1);
builder.LoadLiteral(name)
.LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(),
i::SLOPPY)
.StoreAccumulatorInRegister(Register(0))
.LoadAccumulatorWithRegister(builder.Parameter(0))
.StoreAccumulatorInRegister(Register(1))
.LoadLiteral(Smi::FromInt(51))
.StoreAccumulatorInRegister(Register(2))
.LoadLiteral(Smi::FromInt(11))
.StoreAccumulatorInRegister(Register(3))
.Call(Register(0), Register(1), 2)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject(
"new (function Obj() { "
" this.func = function(a, b) { return a - b; }"
"})()");
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK(return_val->SameValue(Smi::FromInt(40)));
}
// Check with 10 parameters (+ receiver).
{
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(12);
builder.set_parameter_count(1);
builder.LoadLiteral(name)
.LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(),
i::SLOPPY)
.StoreAccumulatorInRegister(Register(0))
.LoadAccumulatorWithRegister(builder.Parameter(0))
.StoreAccumulatorInRegister(Register(1))
.LoadLiteral(factory->NewStringFromAsciiChecked("a"))
.StoreAccumulatorInRegister(Register(2))
.LoadLiteral(factory->NewStringFromAsciiChecked("b"))
.StoreAccumulatorInRegister(Register(3))
.LoadLiteral(factory->NewStringFromAsciiChecked("c"))
.StoreAccumulatorInRegister(Register(4))
.LoadLiteral(factory->NewStringFromAsciiChecked("d"))
.StoreAccumulatorInRegister(Register(5))
.LoadLiteral(factory->NewStringFromAsciiChecked("e"))
.StoreAccumulatorInRegister(Register(6))
.LoadLiteral(factory->NewStringFromAsciiChecked("f"))
.StoreAccumulatorInRegister(Register(7))
.LoadLiteral(factory->NewStringFromAsciiChecked("g"))
.StoreAccumulatorInRegister(Register(8))
.LoadLiteral(factory->NewStringFromAsciiChecked("h"))
.StoreAccumulatorInRegister(Register(9))
.LoadLiteral(factory->NewStringFromAsciiChecked("i"))
.StoreAccumulatorInRegister(Register(10))
.LoadLiteral(factory->NewStringFromAsciiChecked("j"))
.StoreAccumulatorInRegister(Register(11))
.Call(Register(0), Register(1), 10)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject(
"new (function Obj() { "
" this.prefix = \"prefix_\";"
" this.func = function(a, b, c, d, e, f, g, h, i, j) {"
" return this.prefix + a + b + c + d + e + f + g + h + i + j;"
" }"
"})()");
Handle<Object> return_val = callable(object).ToHandleChecked();
Handle<i::String> expected =
factory->NewStringFromAsciiChecked("prefix_abcdefghij");
CHECK(i::String::cast(*return_val)->Equals(*expected));
}
}
......@@ -4,6 +4,7 @@
#include "test/unittests/compiler/interpreter-assembler-unittest.h"
#include "src/code-factory.h"
#include "src/compiler/graph.h"
#include "src/compiler/node.h"
#include "src/interface-descriptors.h"
......@@ -185,6 +186,9 @@ TARGET_TEST_F(InterpreterAssemblerTest, BytecodeOperand) {
int number_of_operands = interpreter::Bytecodes::NumberOfOperands(bytecode);
for (int i = 0; i < number_of_operands; i++) {
switch (interpreter::Bytecodes::GetOperandType(bytecode, i)) {
case interpreter::OperandType::kCount:
EXPECT_THAT(m.BytecodeOperandCount(i), m.IsBytecodeOperand(i));
break;
case interpreter::OperandType::kIdx:
EXPECT_THAT(m.BytecodeOperandIdx(i), m.IsBytecodeOperand(i));
break;
......@@ -235,6 +239,20 @@ TARGET_TEST_F(InterpreterAssemblerTest, GetSetAccumulator) {
}
TARGET_TEST_F(InterpreterAssemblerTest, RegisterLocation) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
Node* reg_index_node = m.Int32Constant(44);
Node* reg_location_node = m.RegisterLocation(reg_index_node);
EXPECT_THAT(
reg_location_node,
IsIntPtrAdd(
IsParameter(Linkage::kInterpreterRegisterFileParameter),
IsWordShl(reg_index_node, IsInt32Constant(kPointerSizeLog2))));
}
}
TARGET_TEST_F(InterpreterAssemblerTest, LoadRegister) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
......@@ -277,6 +295,38 @@ TARGET_TEST_F(InterpreterAssemblerTest, SmiTag) {
}
TARGET_TEST_F(InterpreterAssemblerTest, IntPtrAdd) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
Node* a = m.Int32Constant(0);
Node* b = m.Int32Constant(1);
Node* add = m.IntPtrAdd(a, b);
EXPECT_THAT(add, IsIntPtrAdd(a, b));
}
}
TARGET_TEST_F(InterpreterAssemblerTest, IntPtrSub) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
Node* a = m.Int32Constant(0);
Node* b = m.Int32Constant(1);
Node* add = m.IntPtrSub(a, b);
EXPECT_THAT(add, IsIntPtrSub(a, b));
}
}
TARGET_TEST_F(InterpreterAssemblerTest, WordShl) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
Node* a = m.Int32Constant(0);
Node* add = m.WordShl(a, 10);
EXPECT_THAT(add, IsWordShl(a, IsInt32Constant(10)));
}
}
TARGET_TEST_F(InterpreterAssemblerTest, LoadConstantPoolEntry) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
......@@ -358,6 +408,22 @@ TARGET_TEST_F(InterpreterAssemblerTest, CallIC) {
}
TARGET_TEST_F(InterpreterAssemblerTest, CallJS) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
Callable builtin = CodeFactory::PushArgsAndCall(isolate());
Node* function = m.Int32Constant(0);
Node* first_arg = m.Int32Constant(1);
Node* arg_count = m.Int32Constant(2);
Node* call_js = m.CallJS(function, first_arg, arg_count);
EXPECT_THAT(
call_js,
m.IsCall(_, IsHeapConstant(builtin.code()), arg_count, first_arg,
function, IsParameter(Linkage::kInterpreterContextParameter)));
}
}
TARGET_TEST_F(InterpreterAssemblerTest, LoadTypeFeedbackVector) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
......
......@@ -45,6 +45,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.StoreNamedProperty(reg, reg, 0, LanguageMode::SLOPPY)
.StoreKeyedProperty(reg, reg, 0, LanguageMode::SLOPPY);
// Call operations.
builder.Call(reg, reg, 0);
// Emit binary operators invocations.
builder.BinaryOperation(Token::Value::ADD, reg)
.BinaryOperation(Token::Value::SUB, reg)
......
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