[Interpreter] Support for operator new.

This change add a new bytecode for operator new and implements it using
the Construct() builtin.

BUG=v8:4280
LOG=N

Committed: https://crrev.com/8e4f9963d53913eab7fbd2f61a5733d8dc2169e7
Cr-Commit-Position: refs/heads/master@{#31293}

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

Cr-Commit-Position: refs/heads/master@{#31312}
This commit is contained in:
oth 2015-10-15 09:46:16 -07:00 committed by Commit bot
parent 87aab49a42
commit 7557dc5a70
31 changed files with 655 additions and 53 deletions

View File

@ -976,6 +976,19 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
}
static void Generate_InterpreterPushArgs(MacroAssembler* masm, Register index,
Register limit, Register scratch) {
Label loop_header, loop_check;
__ b(al, &loop_check);
__ bind(&loop_header);
__ ldr(scratch, MemOperand(index, -kPointerSize, PostIndex));
__ push(scratch);
__ bind(&loop_check);
__ cmp(index, limit);
__ b(gt, &loop_header);
}
// static
void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
// ----------- S t a t e -------------
@ -984,6 +997,7 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
// 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.
@ -991,20 +1005,37 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
__ 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);
Generate_InterpreterPushArgs(masm, r2, r3, r4);
// Call the target.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
}
// static
void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r0 : argument count (not including receiver)
// -- r3 : original constructor
// -- r1 : constructor to call
// -- r2 : address of the first argument
// -----------------------------------
// Find the address of the last argument.
__ mov(r4, Operand(r0, LSL, kPointerSizeLog2));
__ sub(r4, r2, r4);
// Push a slot for the receiver to be constructed.
__ push(r0);
// Push the arguments.
Generate_InterpreterPushArgs(masm, r2, r4, r5);
// Call the constructor with r0, r1, and r3 unmodified.
__ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CONSTRUCT_CALL);
}
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
GenerateTailCallToReturnedCode(masm);

View File

@ -420,6 +420,18 @@ void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
}
void InterpreterPushArgsAndConstructDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
r0, // argument count (not including receiver)
r3, // original constructor
r1, // constructor to call
r2 // address of the first argument
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {

View File

@ -708,7 +708,6 @@ void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
ParameterCount actual(x0);
__ InvokeFunction(x1, actual, CALL_FUNCTION, NullCallWrapper());
// Restore the context from the frame.
// x0: result
// jssp[0]: number of arguments (smi-tagged)
@ -1769,6 +1768,7 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
// 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.
@ -1793,6 +1793,43 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
}
// static
void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- x0 : argument count (not including receiver)
// -- x3 : original constructor
// -- x1 : constructor to call
// -- x2 : address of the first argument
// -----------------------------------
// Find the address of the last argument.
__ add(x5, x0, Operand(1)); // Add one for receiver (to be constructed).
__ lsl(x5, x5, kPointerSizeLog2);
// Set stack pointer and where to stop.
__ Mov(x6, jssp);
__ Claim(x5, 1);
__ sub(x4, x6, x5);
// Push a slot for the receiver.
__ Str(xzr, MemOperand(x6, -kPointerSize, PreIndex));
Label loop_header, loop_check;
// Push the arguments.
__ B(&loop_check);
__ Bind(&loop_header);
// TODO(rmcilroy): Push two at a time once we ensure we keep stack aligned.
__ Ldr(x5, MemOperand(x2, -kPointerSize, PostIndex));
__ Str(x5, MemOperand(x6, -kPointerSize, PreIndex));
__ Bind(&loop_check);
__ Cmp(x6, x4);
__ B(gt, &loop_header);
// Call the constructor with x0, x1, and x3 unmodified.
__ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CONSTRUCT_CALL);
}
void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
ASM_LOCATION("Builtins::Generate_ArgumentsAdaptorTrampoline");
// ----------- S t a t e -------------

View File

@ -449,6 +449,18 @@ void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
}
void InterpreterPushArgsAndConstructDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
x0, // argument count (not including receiver)
x3, // original constructor
x1, // constructor to call
x2 // address of the first argument
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {

View File

@ -104,6 +104,7 @@ enum BuiltinExtraArguments {
V(InterpreterEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterExitTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterPushArgsAndCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterPushArgsAndConstruct, BUILTIN, UNINITIALIZED, kNoExtraICState) \
\
V(LoadIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(KeyedLoadIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
@ -308,6 +309,7 @@ class Builtins {
static void Generate_InterpreterEntryTrampoline(MacroAssembler* masm);
static void Generate_InterpreterExitTrampoline(MacroAssembler* masm);
static void Generate_InterpreterPushArgsAndCall(MacroAssembler* masm);
static void Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm);
#define DECLARE_CODE_AGE_BUILTIN_GENERATOR(C) \
static void Generate_Make##C##CodeYoungAgainEvenMarking( \

View File

@ -275,6 +275,13 @@ Callable CodeFactory::InterpreterPushArgsAndCall(Isolate* isolate) {
}
// static
Callable CodeFactory::InterpreterPushArgsAndConstruct(Isolate* isolate) {
return Callable(isolate->builtins()->InterpreterPushArgsAndConstruct(),
InterpreterPushArgsAndConstructDescriptor(isolate));
}
// static
Callable CodeFactory::InterpreterCEntry(Isolate* isolate) {
// TODO(rmcilroy): Deal with runtime functions that return two values.

View File

@ -98,6 +98,7 @@ class CodeFactory final {
CallFunctionFlags flags);
static Callable InterpreterPushArgsAndCall(Isolate* isolate);
static Callable InterpreterPushArgsAndConstruct(Isolate* isolate);
static Callable InterpreterCEntry(Isolate* isolate);
};

View File

@ -357,6 +357,12 @@ void BytecodeGraphBuilder::VisitCallRuntime(
}
void BytecodeGraphBuilder::VisitNew(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::BuildBinaryOp(
const Operator* js_op, const interpreter::BytecodeArrayIterator& iterator) {
Node* left = environment()->LookupRegister(iterator.GetRegisterOperand(0));

View File

@ -317,6 +317,26 @@ Node* InterpreterAssembler::LoadTypeFeedbackVector() {
}
Node* InterpreterAssembler::CallConstruct(Node* original_constructor,
Node* constructor, Node* first_arg,
Node* arg_count) {
Callable callable = CodeFactory::InterpreterPushArgsAndConstruct(isolate());
CallDescriptor* descriptor = Linkage::GetStubCallDescriptor(
isolate(), zone(), callable.descriptor(), 0, CallDescriptor::kNoFlags);
Node* code_target = HeapConstant(callable.code());
Node** args = zone()->NewArray<Node*>(5);
args[0] = arg_count;
args[1] = original_constructor;
args[2] = constructor;
args[3] = first_arg;
args[4] = GetContext();
return CallN(descriptor, code_target, args);
}
Node* InterpreterAssembler::CallN(CallDescriptor* descriptor, Node* code_target,
Node** args) {
Node* stack_pointer_before_call = nullptr;

View File

@ -101,8 +101,17 @@ 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|.
// Call constructor |constructor| with |arg_count| arguments (not
// including receiver) and the first argument located at
// |first_arg|. The |original_constructor| is the same as the
// |constructor| for the new keyword, but differs for the super
// keyword.
Node* CallConstruct(Node* original_constructor, Node* constructor,
Node* first_arg, Node* arg_count);
// Call JSFunction or Callable |function| with |arg_count|
// arguments (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.

View File

@ -724,6 +724,24 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
}
static void Generate_InterpreterPushArgs(MacroAssembler* masm,
Register array_limit) {
// ----------- S t a t e -------------
// -- ebx : Pointer to the last argument in the args array.
// -- array_limit : Pointer to one before the first argument in the
// args array.
// -----------------------------------
Label loop_header, loop_check;
__ jmp(&loop_check);
__ bind(&loop_header);
__ Push(Operand(ebx, 0));
__ sub(ebx, Immediate(kPointerSize));
__ bind(&loop_check);
__ cmp(ebx, array_limit);
__ j(greater, &loop_header, Label::kNear);
}
// static
void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
// ----------- S t a t e -------------
@ -732,6 +750,7 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
// 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);
@ -743,15 +762,7 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
__ 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);
Generate_InterpreterPushArgs(masm, ecx);
// Call the target.
__ Push(edx); // Re-push return address.
@ -759,13 +770,53 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
}
// static
void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : the number of arguments (not including the receiver)
// -- edx : the original constructor
// -- edi : the constructor
// -- 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.
// -----------------------------------
// Save number of arguments on the stack below where arguments are going
// to be pushed.
__ mov(ecx, eax);
__ neg(ecx);
__ mov(Operand(esp, ecx, times_pointer_size, -kPointerSize), eax);
__ mov(eax, ecx);
// Pop return address to allow tail-call after pushing arguments.
__ Pop(ecx);
// Find the address of the last argument.
__ shl(eax, kPointerSizeLog2);
__ add(eax, ebx);
// Push padding for receiver.
__ Push(Immediate(0));
Generate_InterpreterPushArgs(masm, eax);
// Restore number of arguments from slot on stack.
__ mov(eax, Operand(esp, -kPointerSize));
// Re-push return address.
__ Push(ecx);
// Call the constructor with unmodified eax, edi, ebi values.
__ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CONSTRUCT_CALL);
}
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
GenerateTailCallToReturnedCode(masm);
}
static void CallCompileOptimized(MacroAssembler* masm, bool concurrent) {
FrameScope scope(masm, StackFrame::INTERNAL);
// Push a copy of the function.

View File

@ -401,6 +401,18 @@ void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
}
void InterpreterPushArgsAndConstructDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
eax, // argument count (not including receiver)
edx, // original constructor
edi, // constructor
ebx, // address of first argument
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {

View File

@ -71,6 +71,7 @@ class PlatformInterfaceDescriptor;
V(MathRoundVariantCallFromUnoptimizedCode) \
V(MathRoundVariantCallFromOptimizedCode) \
V(InterpreterPushArgsAndCall) \
V(InterpreterPushArgsAndConstruct) \
V(InterpreterCEntry)
@ -716,6 +717,14 @@ class InterpreterPushArgsAndCallDescriptor : public CallInterfaceDescriptor {
};
class InterpreterPushArgsAndConstructDescriptor
: public CallInterfaceDescriptor {
public:
DECLARE_DESCRIPTOR(InterpreterPushArgsAndConstructDescriptor,
CallInterfaceDescriptor)
};
class InterpreterCEntryDescriptor : public CallInterfaceDescriptor {
public:
DECLARE_DESCRIPTOR(InterpreterCEntryDescriptor, CallInterfaceDescriptor)

View File

@ -599,6 +599,16 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
}
BytecodeArrayBuilder& BytecodeArrayBuilder::New(Register constructor,
Register first_arg,
size_t arg_count) {
DCHECK(FitsInIdx8Operand(arg_count));
Output(Bytecode::kNew, constructor.ToOperand(), first_arg.ToOperand(),
static_cast<uint8_t>(arg_count));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
Runtime::FunctionId function_id, Register first_arg, size_t arg_count) {
DCHECK(FitsInIdx16Operand(function_id));

View File

@ -100,13 +100,19 @@ class BytecodeArrayBuilder {
BytecodeArrayBuilder& Call(Register callable, Register receiver,
size_t arg_count);
// Call the new operator. The |constructor| register is followed by
// |arg_count| consecutive registers containing arguments to be
// applied to the constructor.
BytecodeArrayBuilder& New(Register constructor, Register first_arg,
size_t arg_count);
// Call the runtime function with |function_id|. 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>.
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
Register first_arg, size_t arg_count);
// Operators (register == lhs, accumulator = rhs).
// Operators (register holds the lhs value, accumulator holds the rhs value).
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg,
Strength strength);

View File

@ -933,6 +933,25 @@ void BytecodeGenerator::VisitProperty(Property* expr) {
}
Register BytecodeGenerator::VisitArguments(
ZoneList<Expression*>* args, TemporaryRegisterScope* register_scope) {
// Visit arguments and place in a contiguous block of temporary registers.
// Return the first temporary register corresponding to the first argument.
DCHECK_GT(args->length(), 0);
Register first_arg = register_scope->NewRegister();
Visit(args->at(0));
builder()->StoreAccumulatorInRegister(first_arg);
for (int i = 1; i < static_cast<int>(args->length()); i++) {
Register ith_arg = register_scope->NewRegister();
Visit(args->at(i));
builder()->StoreAccumulatorInRegister(ith_arg);
DCHECK(ith_arg.index() - i == first_arg.index());
}
return first_arg;
}
void BytecodeGenerator::VisitCall(Call* expr) {
Expression* callee_expr = expr->expression();
Call::CallType call_type = expr->GetCallType(isolate());
@ -980,11 +999,9 @@ void BytecodeGenerator::VisitCall(Call* expr) {
// 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);
if (args->length() > 0) {
Register first_arg = VisitArguments(args, &temporary_register_scope);
CHECK_EQ(first_arg.index(), receiver.index() + 1);
}
// TODO(rmcilroy): Deal with possible direct eval here?
@ -993,7 +1010,22 @@ void BytecodeGenerator::VisitCall(Call* expr) {
}
void BytecodeGenerator::VisitCallNew(CallNew* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitCallNew(CallNew* expr) {
TemporaryRegisterScope temporary_register_scope(builder());
Register constructor = temporary_register_scope.NewRegister();
Visit(expr->expression());
builder()->StoreAccumulatorInRegister(constructor);
ZoneList<Expression*>* args = expr->arguments();
if (args->length() > 0) {
Register first_arg = VisitArguments(args, &temporary_register_scope);
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);
}
}
void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
@ -1002,22 +1034,21 @@ void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
}
// Evaluate all arguments to the runtime call.
ZoneList<Expression*>* args = expr->arguments();
TemporaryRegisterScope temporary_register_scope(builder());
// Ensure we always have a valid first_arg register even if there are no
// arguments to pass.
Register first_arg = temporary_register_scope.NewRegister();
for (int i = 0; i < args->length(); ++i) {
Register arg =
(i == 0) ? first_arg : temporary_register_scope.NewRegister();
Visit(args->at(i));
DCHECK_EQ(arg.index() - i, first_arg.index());
builder()->StoreAccumulatorInRegister(arg);
}
TemporaryRegisterScope temporary_register_scope(&builder_);
// TODO(rmcilroy): support multiple return values.
DCHECK_LE(expr->function()->result_size, 1);
Runtime::FunctionId function_id = expr->function()->function_id;
ZoneList<Expression*>* args = expr->arguments();
Register first_arg;
if (args->length() > 0) {
first_arg = VisitArguments(args, &temporary_register_scope);
} 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 = temporary_register_scope.NewRegister();
}
builder()->CallRuntime(function_id, first_arg, args->length());
}

View File

@ -36,6 +36,8 @@ class BytecodeGenerator : public AstVisitor {
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
Register VisitArguments(ZoneList<Expression*>* arguments,
TemporaryRegisterScope* caller_scope);
void VisitArithmeticExpression(BinaryOperation* binop);
void VisitCommaExpression(BinaryOperation* binop);
void VisitLogicalOrExpression(BinaryOperation* binop);

View File

@ -89,11 +89,14 @@ namespace interpreter {
V(LogicalNot, OperandType::kNone) \
V(TypeOf, OperandType::kNone) \
\
/* Call operations. */ \
/* Call operations */ \
V(Call, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8) \
V(CallRuntime, OperandType::kIdx16, OperandType::kReg8, \
OperandType::kCount8) \
\
/* New operator */ \
V(New, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8) \
\
/* Test Operators */ \
V(TestEqual, OperandType::kReg8) \
V(TestNotEqual, OperandType::kReg8) \

View File

@ -537,6 +537,25 @@ void Interpreter::DoCallRuntime(compiler::InterpreterAssembler* assembler) {
}
// New <constructor> <arg_count>
//
// Call operator new with |constructor| and the first argument in
// register |first_arg| and |arg_count| arguments in subsequent
//
void Interpreter::DoNew(compiler::InterpreterAssembler* assembler) {
Callable ic = CodeFactory::InterpreterPushArgsAndConstruct(isolate_);
Node* constructor_index = __ BytecodeOperandReg8(0);
Node* constructor = __ LoadRegister(constructor_index);
Node* first_arg_reg = __ BytecodeOperandReg8(1);
Node* first_arg = __ RegisterLocation(first_arg_reg);
Node* args_count = __ BytecodeOperandCount8(2);
Node* result =
__ CallConstruct(constructor, constructor, first_arg, args_count);
__ SetAccumulator(result);
__ Dispatch();
}
// TestEqual <src>
//
// Test if the value in the <src> register equals the accumulator.

View File

@ -986,6 +986,7 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
// 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.
@ -1007,6 +1008,37 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
}
// static
void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- a0 : argument count (not including receiver)
// -- a3 : original constructor
// -- a1 : constructor to call
// -- a2 : address of the first argument
// -----------------------------------
// Find the address of the last argument.
__ sll(t0, a0, kPointerSizeLog2);
__ Subu(t0, a2, Operand(t0));
// Push a slot for the receiver.
__ push(zero_reg);
// Push the arguments.
Label loop_header, loop_check;
__ Branch(&loop_check);
__ bind(&loop_header);
__ lw(t1, MemOperand(a2));
__ Addu(a2, a2, Operand(-kPointerSize));
__ push(t1);
__ bind(&loop_check);
__ Branch(&loop_header, gt, a2, Operand(t0));
// Call the constructor with a0, a1, and a3 unmodified.
__ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CONSTRUCT_CALL);
}
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
GenerateTailCallToReturnedCode(masm);

View File

@ -395,6 +395,18 @@ void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
}
void InterpreterPushArgsAndConstructDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
a0, // argument count (not including receiver)
a3, // original constructor
a1, // constructor to call
a2 // address of the first argument
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {

View File

@ -982,6 +982,7 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
// 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.
@ -1003,6 +1004,37 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
}
// static
void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- a0 : argument count (not including receiver)
// -- a3 : original constructor
// -- a1 : constructor to call
// -- a2 : address of the first argument
// -----------------------------------
// Find the address of the last argument.
__ dsll(t0, a0, kPointerSizeLog2);
__ Dsubu(t0, a2, Operand(t0));
// Push a slot for the receiver.
__ push(zero_reg);
// Push the arguments.
Label loop_header, loop_check;
__ Branch(&loop_check);
__ bind(&loop_header);
__ ld(t1, MemOperand(a2));
__ Daddu(a2, a2, Operand(-kPointerSize));
__ push(t1);
__ bind(&loop_check);
__ Branch(&loop_header, gt, a2, Operand(t0));
// Call the constructor with a0, a1, and a3 unmodified.
__ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CONSTRUCT_CALL);
}
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
GenerateTailCallToReturnedCode(masm);

View File

@ -395,6 +395,18 @@ void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
}
void InterpreterPushArgsAndConstructDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
a0, // argument count (not including receiver)
a3, // original constructor
a1, // constructor to call
a2 // address of the first argument
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {

View File

@ -983,6 +983,7 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
// arguments should be consecutive above this, in the same order as
// they are to be pushed onto the stack.
// -- r4 : the target to call (can be any Object).
// -----------------------------------
// Calculate number of arguments (add one for receiver).
__ addi(r6, r3, Operand(1));
@ -1001,6 +1002,41 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
}
// static
void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r3 : argument count (not including receiver)
// -- r6 : original constructor
// -- r4 : constructor to call
// -- r5 : address of the first argument
// -----------------------------------
// Calculate number of arguments.
__ mr(r7, r3);
// Push a slot for the receiver.
__ push(r3);
// Skip pushing arguments if none.
Label loop_end;
__ cmpi(r7, Operand(0));
__ beq(&loop_end);
// Push the arguments
Label loop;
__ addi(r5, r5, Operand(kPointerSize)); // Bias up for LoadPU
__ mtctr(r7);
__ bind(&loop);
__ LoadPU(r7, MemOperand(r5, -kPointerSize));
__ push(r7);
__ bdnz(&loop);
__ bind(&loop_end);
// Call the constructor with r3, r4, and r6 unmodified.
__ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CONSTRUCT_CALL);
}
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
GenerateTailCallToReturnedCode(masm);

View File

@ -394,6 +394,18 @@ void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
}
void InterpreterPushArgsAndConstructDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
r3, // argument count (not including receiver)
r6, // original constructor
r4, // constructor to call
r5 // address of the first argument
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {

View File

@ -49,7 +49,8 @@ void MacroAssembler::Jump(intptr_t target, RelocInfo::Mode rmode,
if (cond != al) b(NegateCondition(cond), &skip, cr);
DCHECK(rmode == RelocInfo::CODE_TARGET || rmode == RelocInfo::RUNTIME_ENTRY);
DCHECK(rmode == RelocInfo::CODE_TARGET || rmode == RelocInfo::RUNTIME_ENTRY ||
rmode == RelocInfo::CONSTRUCT_CALL);
mov(ip, Operand(target, rmode));
mtctr(ip);

View File

@ -785,21 +785,21 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
}
// static
void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
static void Generate_InterpreterPushArgs(MacroAssembler* masm,
bool push_receiver) {
// ----------- 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.
if (push_receiver) {
__ addp(rcx, Immediate(1)); // Add one for receiver.
}
__ shlp(rcx, Immediate(kPointerSizeLog2));
__ negp(rcx);
__ addp(rcx, rbx);
@ -813,13 +813,58 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
__ bind(&loop_check);
__ cmpp(rbx, rcx);
__ j(greater, &loop_header, Label::kNear);
}
// static
void Builtins::Generate_InterpreterPushArgsAndCall(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.
__ PopReturnAddressTo(kScratchRegister);
Generate_InterpreterPushArgs(masm, true);
// Call the target.
__ Push(rdx); // Re-push return address.
__ PushReturnAddressFrom(kScratchRegister); // Re-push return address.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
}
// static
void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : the number of arguments (not including the receiver)
// -- rdx : the original constructor (either the same as the constructor or
// the JSFunction on which new was invoked initially)
// -- rdi : the constructor to call (can be any Object)
// -- 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.
// -----------------------------------
// Pop return address to allow tail-call after pushing arguments.
__ PopReturnAddressTo(kScratchRegister);
// Push slot for the receiver to be constructed.
__ Push(Immediate(0));
Generate_InterpreterPushArgs(masm, false);
// Push return address in preparation for the tail-call.
__ PushReturnAddressFrom(kScratchRegister);
// Call the constructor (rax, rdx, rdi passed on).
__ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CONSTRUCT_CALL);
}
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
GenerateTailCallToReturnedCode(masm);

View File

@ -395,6 +395,18 @@ void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
}
void InterpreterPushArgsAndConstructDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
rax, // argument count (not including receiver)
rdx, // original constructor
rdi, // constructor
rbx, // address of first argument
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {

View File

@ -1015,14 +1015,14 @@ TEST(PropertyCall) {
B(LoadICSloppy), R(1), U8(vector->GetIndex(slot2)), //
B(Star), R(0), //
B(Ldar), R(helper.kLastParamIndex), //
B(Star), R(2), //
B(Star), R(3), //
B(Ldar), R(helper.kLastParamIndex), //
B(Add), R(2), //
B(Add), R(3), //
B(Star), R(2), //
B(Ldar), R(helper.kLastParamIndex), //
B(Star), R(3), //
B(Call), R(0), R(1), U8(2), //
B(Return) //
B(Return), //
},
1,
{"func"}}};
@ -2670,6 +2670,72 @@ TEST(TryFinally) {
}
}
TEST(CallNew) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<InstanceType> snippets[] = {
{"function bar() { this.value = 0; }\n"
"function f() { return new bar(); }\n"
"f()",
kPointerSize,
1,
9,
{
B(LdaGlobal), _, //
B(Star), R(0), //
B(New), R(0), R(0), U8(0), //
B(Return), //
},
0},
{"function bar(x) { this.value = 18; this.x = x;}\n"
"function f() { return new bar(3); }\n"
"f()",
2 * kPointerSize,
1,
13,
{
B(LdaGlobal), _, //
B(Star), R(0), //
B(LdaSmi8), U8(3), //
B(Star), R(1), //
B(New), R(0), R(1), U8(1), //
B(Return), //
},
0},
{"function bar(w, x, y, z) {\n"
" this.value = 18;\n"
" this.x = x;\n"
" this.y = y;\n"
" this.z = z;\n"
"}\n"
"function f() { return new bar(3, 4, 5); }\n"
"f()",
4 * kPointerSize,
1,
21,
{
B(LdaGlobal), _, //
B(Star), R(0), //
B(LdaSmi8), U8(3), //
B(Star), R(1), //
B(LdaSmi8), U8(4), //
B(Star), R(2), //
B(LdaSmi8), U8(5), //
B(Star), R(3), //
B(New), R(0), R(1), U8(3), //
B(Return), //
},
0}};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -1728,6 +1728,65 @@ TEST(InterpreterObjectLiterals) {
}
TEST(InterpreterConstruct) {
HandleAndZoneScope handles;
std::string source(
"function counter() { this.count = 0; }\n"
"function " +
InterpreterTester::function_name() +
"() {\n"
" var c = new counter();\n"
" return c.count;\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(0));
}
TEST(InterpreterConstructWithArgument) {
HandleAndZoneScope handles;
std::string source(
"function counter(arg0) { this.count = 17; this.x = arg0; }\n"
"function " +
InterpreterTester::function_name() +
"() {\n"
" var c = new counter(3);\n"
" return c.x;\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(3));
}
TEST(InterpreterConstructWithArguments) {
HandleAndZoneScope handles;
std::string source(
"function counter(arg0, arg1) {\n"
" this.count = 7; this.x = arg0; this.y = arg1;\n"
"}\n"
"function " +
InterpreterTester::function_name() +
"() {\n"
" var c = new counter(3, 5);\n"
" return c.count + c.x + c.y;\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(15));
}
TEST(InterpreterComma) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();

View File

@ -90,6 +90,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Emit unary operator invocations.
builder.LogicalNot().TypeOf();
// Emit new.
builder.New(reg, reg, 0);
// Emit test operator invocations.
builder.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
.CompareOperation(Token::Value::NE, reg, Strength::WEAK)