From 7557dc5a70e333ba10eb370fe6e7b7a31760d891 Mon Sep 17 00:00:00 2001 From: oth Date: Thu, 15 Oct 2015 09:46:16 -0700 Subject: [PATCH] [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} --- src/arm/builtins-arm.cc | 47 +++++++++--- src/arm/interface-descriptors-arm.cc | 12 ++++ src/arm64/builtins-arm64.cc | 39 +++++++++- src/arm64/interface-descriptors-arm64.cc | 12 ++++ src/builtins.h | 2 + src/code-factory.cc | 7 ++ src/code-factory.h | 1 + src/compiler/bytecode-graph-builder.cc | 6 ++ src/compiler/interpreter-assembler.cc | 20 ++++++ src/compiler/interpreter-assembler.h | 13 +++- src/ia32/builtins-ia32.cc | 71 +++++++++++++++--- src/ia32/interface-descriptors-ia32.cc | 12 ++++ src/interface-descriptors.h | 9 +++ src/interpreter/bytecode-array-builder.cc | 10 +++ src/interpreter/bytecode-array-builder.h | 8 ++- src/interpreter/bytecode-generator.cc | 67 ++++++++++++----- src/interpreter/bytecode-generator.h | 2 + src/interpreter/bytecodes.h | 5 +- src/interpreter/interpreter.cc | 19 +++++ src/mips/builtins-mips.cc | 32 +++++++++ src/mips/interface-descriptors-mips.cc | 12 ++++ src/mips64/builtins-mips64.cc | 32 +++++++++ src/mips64/interface-descriptors-mips64.cc | 12 ++++ src/ppc/builtins-ppc.cc | 36 ++++++++++ src/ppc/interface-descriptors-ppc.cc | 12 ++++ src/ppc/macro-assembler-ppc.cc | 3 +- src/x64/builtins-x64.cc | 61 +++++++++++++--- src/x64/interface-descriptors-x64.cc | 12 ++++ .../interpreter/test-bytecode-generator.cc | 72 ++++++++++++++++++- test/cctest/interpreter/test-interpreter.cc | 59 +++++++++++++++ .../bytecode-array-builder-unittest.cc | 3 + 31 files changed, 655 insertions(+), 53 deletions(-) diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 554baa2f44..fd94243cdd 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -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); diff --git a/src/arm/interface-descriptors-arm.cc b/src/arm/interface-descriptors-arm.cc index 48a9790583..cf02577d5d 100644 --- a/src/arm/interface-descriptors-arm.cc +++ b/src/arm/interface-descriptors-arm.cc @@ -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[] = { diff --git a/src/arm64/builtins-arm64.cc b/src/arm64/builtins-arm64.cc index 471aeb5b9f..d6bd29a3c3 100644 --- a/src/arm64/builtins-arm64.cc +++ b/src/arm64/builtins-arm64.cc @@ -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 ------------- diff --git a/src/arm64/interface-descriptors-arm64.cc b/src/arm64/interface-descriptors-arm64.cc index 39df17a8b8..eba696fa74 100644 --- a/src/arm64/interface-descriptors-arm64.cc +++ b/src/arm64/interface-descriptors-arm64.cc @@ -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[] = { diff --git a/src/builtins.h b/src/builtins.h index 48b831fc23..4681dc8b44 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -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( \ diff --git a/src/code-factory.cc b/src/code-factory.cc index 671c74b206..0f363c7637 100644 --- a/src/code-factory.cc +++ b/src/code-factory.cc @@ -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. diff --git a/src/code-factory.h b/src/code-factory.h index c7cc31603c..aa5abd0689 100644 --- a/src/code-factory.h +++ b/src/code-factory.h @@ -98,6 +98,7 @@ class CodeFactory final { CallFunctionFlags flags); static Callable InterpreterPushArgsAndCall(Isolate* isolate); + static Callable InterpreterPushArgsAndConstruct(Isolate* isolate); static Callable InterpreterCEntry(Isolate* isolate); }; diff --git a/src/compiler/bytecode-graph-builder.cc b/src/compiler/bytecode-graph-builder.cc index b0af5749fe..c8e3356610 100644 --- a/src/compiler/bytecode-graph-builder.cc +++ b/src/compiler/bytecode-graph-builder.cc @@ -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)); diff --git a/src/compiler/interpreter-assembler.cc b/src/compiler/interpreter-assembler.cc index 0ba6176f6b..47c24a4cee 100644 --- a/src/compiler/interpreter-assembler.cc +++ b/src/compiler/interpreter-assembler.cc @@ -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(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; diff --git a/src/compiler/interpreter-assembler.h b/src/compiler/interpreter-assembler.h index 11f43ccf26..e49f9e919d 100644 --- a/src/compiler/interpreter-assembler.h +++ b/src/compiler/interpreter-assembler.h @@ -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. diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 4156123f28..16081779af 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -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. diff --git a/src/ia32/interface-descriptors-ia32.cc b/src/ia32/interface-descriptors-ia32.cc index 8208262cad..3916074941 100644 --- a/src/ia32/interface-descriptors-ia32.cc +++ b/src/ia32/interface-descriptors-ia32.cc @@ -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[] = { diff --git a/src/interface-descriptors.h b/src/interface-descriptors.h index b08e841978..04d8e508b5 100644 --- a/src/interface-descriptors.h +++ b/src/interface-descriptors.h @@ -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) diff --git a/src/interpreter/bytecode-array-builder.cc b/src/interpreter/bytecode-array-builder.cc index abee9f7804..e53c493307 100644 --- a/src/interpreter/bytecode-array-builder.cc +++ b/src/interpreter/bytecode-array-builder.cc @@ -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(arg_count)); + return *this; +} + + BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime( Runtime::FunctionId function_id, Register first_arg, size_t arg_count) { DCHECK(FitsInIdx16Operand(function_id)); diff --git a/src/interpreter/bytecode-array-builder.h b/src/interpreter/bytecode-array-builder.h index 5f90f9ec7f..ba28c76b25 100644 --- a/src/interpreter/bytecode-array-builder.h +++ b/src/interpreter/bytecode-array-builder.h @@ -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 // to . 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); diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc index a4efcce1da..34c2425d87 100644 --- a/src/interpreter/bytecode-generator.cc +++ b/src/interpreter/bytecode-generator.cc @@ -933,6 +933,25 @@ void BytecodeGenerator::VisitProperty(Property* expr) { } +Register BytecodeGenerator::VisitArguments( + ZoneList* 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(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* 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* 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* 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* 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()); } diff --git a/src/interpreter/bytecode-generator.h b/src/interpreter/bytecode-generator.h index 6ea4cd75ae..cc596fc1e5 100644 --- a/src/interpreter/bytecode-generator.h +++ b/src/interpreter/bytecode-generator.h @@ -36,6 +36,8 @@ class BytecodeGenerator : public AstVisitor { DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); + Register VisitArguments(ZoneList* arguments, + TemporaryRegisterScope* caller_scope); void VisitArithmeticExpression(BinaryOperation* binop); void VisitCommaExpression(BinaryOperation* binop); void VisitLogicalOrExpression(BinaryOperation* binop); diff --git a/src/interpreter/bytecodes.h b/src/interpreter/bytecodes.h index f98882a5dd..e2ca01df0d 100644 --- a/src/interpreter/bytecodes.h +++ b/src/interpreter/bytecodes.h @@ -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) \ diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index a6e9317e0f..30a7aab4d0 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -537,6 +537,25 @@ void Interpreter::DoCallRuntime(compiler::InterpreterAssembler* assembler) { } +// New +// +// 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 // // Test if the value in the register equals the accumulator. diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc index 2e6904380a..70970a82f9 100644 --- a/src/mips/builtins-mips.cc +++ b/src/mips/builtins-mips.cc @@ -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); diff --git a/src/mips/interface-descriptors-mips.cc b/src/mips/interface-descriptors-mips.cc index 326d391af4..024f72c31c 100644 --- a/src/mips/interface-descriptors-mips.cc +++ b/src/mips/interface-descriptors-mips.cc @@ -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[] = { diff --git a/src/mips64/builtins-mips64.cc b/src/mips64/builtins-mips64.cc index 99701a0d85..b00ba88da4 100644 --- a/src/mips64/builtins-mips64.cc +++ b/src/mips64/builtins-mips64.cc @@ -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); diff --git a/src/mips64/interface-descriptors-mips64.cc b/src/mips64/interface-descriptors-mips64.cc index 192c7d3b11..2fb7a7c16e 100644 --- a/src/mips64/interface-descriptors-mips64.cc +++ b/src/mips64/interface-descriptors-mips64.cc @@ -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[] = { diff --git a/src/ppc/builtins-ppc.cc b/src/ppc/builtins-ppc.cc index cdbb1c24a0..3d188a6e98 100644 --- a/src/ppc/builtins-ppc.cc +++ b/src/ppc/builtins-ppc.cc @@ -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); diff --git a/src/ppc/interface-descriptors-ppc.cc b/src/ppc/interface-descriptors-ppc.cc index 4286f8bf6d..9e1d8861dc 100644 --- a/src/ppc/interface-descriptors-ppc.cc +++ b/src/ppc/interface-descriptors-ppc.cc @@ -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[] = { diff --git a/src/ppc/macro-assembler-ppc.cc b/src/ppc/macro-assembler-ppc.cc index f597574d9e..51e2092f13 100644 --- a/src/ppc/macro-assembler-ppc.cc +++ b/src/ppc/macro-assembler-ppc.cc @@ -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); diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 03f6fa2fd5..2e3adb73c5 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -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); diff --git a/src/x64/interface-descriptors-x64.cc b/src/x64/interface-descriptors-x64.cc index c323a2d5db..39c8d51f6b 100644 --- a/src/x64/interface-descriptors-x64.cc +++ b/src/x64/interface-descriptors-x64.cc @@ -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[] = { diff --git a/test/cctest/interpreter/test-bytecode-generator.cc b/test/cctest/interpreter/test-bytecode-generator.cc index 9dcfb5b019..33c5e50c63 100644 --- a/test/cctest/interpreter/test-bytecode-generator.cc +++ b/test/cctest/interpreter/test-bytecode-generator.cc @@ -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 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 bytecode_array = + helper.MakeBytecode(snippets[i].code_snippet, "f"); + CheckBytecodeArrayEqual(snippets[i], bytecode_array, true); + } +} + } // namespace interpreter } // namespace internal } // namespace v8 diff --git a/test/cctest/interpreter/test-interpreter.cc b/test/cctest/interpreter/test-interpreter.cc index 87379bc736..cc265e2ba6 100644 --- a/test/cctest/interpreter/test-interpreter.cc +++ b/test/cctest/interpreter/test-interpreter.cc @@ -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 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 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 return_val = callable().ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(15)); +} + + TEST(InterpreterComma) { HandleAndZoneScope handles; i::Isolate* isolate = handles.main_isolate(); diff --git a/test/unittests/interpreter/bytecode-array-builder-unittest.cc b/test/unittests/interpreter/bytecode-array-builder-unittest.cc index 16dd47d1d1..c3252cfb05 100644 --- a/test/unittests/interpreter/bytecode-array-builder-unittest.cc +++ b/test/unittests/interpreter/bytecode-array-builder-unittest.cc @@ -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)