diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index f672d4908e..d474260b01 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1077,6 +1077,8 @@ LInstruction* LChunkBuilder::DoTest(HTest* instr) { } else if (v->IsTypeofIs()) { HTypeofIs* typeof_is = HTypeofIs::cast(v); return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value())); + } else if (v->IsIsConstructCall()) { + return new LIsConstructCallAndBranch(TempRegister()); } else { if (v->IsConstant()) { if (HConstant::cast(v)->handle()->IsTrue()) { @@ -1890,6 +1892,12 @@ LInstruction* LChunkBuilder::DoTypeofIs(HTypeofIs* instr) { return DefineSameAsFirst(new LTypeofIs(UseRegister(instr->value()))); } + +LInstruction* LChunkBuilder::DoIsConstructCall(HIsConstructCall* instr) { + return DefineAsRegister(new LIsConstructCall()); +} + + LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) { HEnvironment* env = current_block_->last_environment(); ASSERT(env != NULL); diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index a076c80c75..5194026914 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -153,6 +153,8 @@ class LCodeGen; V(Typeof) \ V(TypeofIs) \ V(TypeofIsAndBranch) \ + V(IsConstructCall) \ + V(IsConstructCallAndBranch) \ V(UnaryMathOperation) \ V(UnknownOSRValue) \ V(ValueOf) @@ -1716,6 +1718,24 @@ class LTypeofIsAndBranch: public LControlInstruction<1, 0> { }; +class LIsConstructCall: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(IsConstructCall, "is-construct-call") + DECLARE_HYDROGEN_ACCESSOR(IsConstructCall) +}; + + +class LIsConstructCallAndBranch: public LControlInstruction<0, 1> { + public: + explicit LIsConstructCallAndBranch(LOperand* temp) { + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch, + "is-construct-call-and-branch") +}; + + class LDeleteProperty: public LTemplateInstruction<1, 2, 0> { public: LDeleteProperty(LOperand* obj, LOperand* key) { diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 855ed461b5..7617b4bbb9 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -3778,6 +3778,55 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, } +void LCodeGen::DoIsConstructCall(LIsConstructCall* instr) { + Register result = ToRegister(instr->result()); + Label true_label; + Label false_label; + Label done; + + EmitIsConstructCall(result, scratch0()); + __ b(eq, &true_label); + + __ LoadRoot(result, Heap::kFalseValueRootIndex); + __ b(&done); + + + __ bind(&true_label); + __ LoadRoot(result, Heap::kTrueValueRootIndex); + + __ bind(&done); +} + + +void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) { + Register temp1 = ToRegister(instr->TempAt(0)); + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + EmitIsConstructCall(temp1, scratch0()); + EmitBranch(true_block, false_block, eq); +} + + +void LCodeGen::EmitIsConstructCall(Register temp1, Register temp2) { + ASSERT(!temp1.is(temp2)); + // Get the frame pointer for the calling frame. + __ ldr(temp1, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); + + // Skip the arguments adaptor frame if it exists. + Label check_frame_marker; + __ ldr(temp2, MemOperand(temp1, StandardFrameConstants::kContextOffset)); + __ cmp(temp2, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + __ b(ne, &check_frame_marker); + __ ldr(temp1, MemOperand(temp1, StandardFrameConstants::kCallerFPOffset)); + + // Check the marker in the calling frame. + __ bind(&check_frame_marker); + __ ldr(temp1, MemOperand(temp1, StandardFrameConstants::kMarkerOffset)); + __ cmp(temp1, Operand(Smi::FromInt(StackFrame::CONSTRUCT))); +} + + void LCodeGen::DoLazyBailout(LLazyBailout* instr) { // No code for lazy bailout instruction. Used to capture environment after a // call for populating the safepoint data with deoptimization data. diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h index 3f7fe4519b..7bc6689f76 100644 --- a/src/arm/lithium-codegen-arm.h +++ b/src/arm/lithium-codegen-arm.h @@ -264,6 +264,10 @@ class LCodeGen BASE_EMBEDDED { Label* is_not_object, Label* is_object); + // Emits optimized code for %_IsConstructCall(). + // Caller should branch on equal condition. + void EmitIsConstructCall(Register temp1, Register temp2); + LChunk* const chunk_; MacroAssembler* const masm_; CompilationInfo* const info_; diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index f1093a004c..10e1c975ca 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -113,6 +113,7 @@ class LChunkBuilder; V(IsNull) \ V(IsObject) \ V(IsSmi) \ + V(IsConstructCall) \ V(HasInstanceType) \ V(HasCachedArrayIndex) \ V(JSArrayLength) \ @@ -2179,6 +2180,22 @@ class HIsSmi: public HUnaryPredicate { }; +class HIsConstructCall: public HInstruction { + public: + HIsConstructCall() { + set_representation(Representation::Tagged()); + SetFlag(kUseGVN); + } + + virtual bool EmitAtUses() const { return uses()->length() <= 1; } + + DECLARE_CONCRETE_INSTRUCTION(IsConstructCall, "is_construct_call") + + protected: + virtual bool DataEquals(HValue* other) const { return true; } +}; + + class HHasInstanceType: public HUnaryPredicate { public: HHasInstanceType(HValue* value, InstanceType type) diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 4044f7fff1..dd12831c5f 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -5185,9 +5185,10 @@ void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf( } - // Support for construct call checks. +// Support for construct call checks. void HGraphBuilder::GenerateIsConstructCall(int argument_count, int ast_id) { - BAILOUT("inlined runtime function: IsConstructCall"); + ASSERT(argument_count == 0); + ast_context()->ReturnInstruction(new HIsConstructCall, ast_id); } diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 0e959217e0..50ed1048ba 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -3587,6 +3587,53 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, } +void LCodeGen::DoIsConstructCall(LIsConstructCall* instr) { + Register result = ToRegister(instr->result()); + NearLabel true_label; + NearLabel false_label; + NearLabel done; + + EmitIsConstructCall(result); + __ j(equal, &true_label); + + __ mov(result, Factory::false_value()); + __ jmp(&done); + + __ bind(&true_label); + __ mov(result, Factory::true_value()); + + __ bind(&done); +} + + +void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) { + Register temp = ToRegister(instr->TempAt(0)); + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + EmitIsConstructCall(temp); + EmitBranch(true_block, false_block, equal); +} + + +void LCodeGen::EmitIsConstructCall(Register temp) { + // Get the frame pointer for the calling frame. + __ mov(temp, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); + + // Skip the arguments adaptor frame if it exists. + NearLabel check_frame_marker; + __ cmp(Operand(temp, StandardFrameConstants::kContextOffset), + Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + __ j(not_equal, &check_frame_marker); + __ mov(temp, Operand(temp, StandardFrameConstants::kCallerFPOffset)); + + // Check the marker in the calling frame. + __ bind(&check_frame_marker); + __ cmp(Operand(temp, StandardFrameConstants::kMarkerOffset), + Immediate(Smi::FromInt(StackFrame::CONSTRUCT))); +} + + void LCodeGen::DoLazyBailout(LLazyBailout* instr) { // No code for lazy bailout instruction. Used to capture environment after a // call for populating the safepoint data with deoptimization data. diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h index f0379c02e3..3782208336 100644 --- a/src/ia32/lithium-codegen-ia32.h +++ b/src/ia32/lithium-codegen-ia32.h @@ -229,6 +229,11 @@ class LCodeGen BASE_EMBEDDED { Label* is_not_object, Label* is_object); + // Emits optimized code for %_IsConstructCall(). + // Caller should branch on equal condition. + void EmitIsConstructCall(Register temp); + + LChunk* const chunk_; MacroAssembler* const masm_; CompilationInfo* const info_; diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index b31c4eba18..9ec2faf972 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -1098,6 +1098,8 @@ LInstruction* LChunkBuilder::DoTest(HTest* instr) { } else if (v->IsTypeofIs()) { HTypeofIs* typeof_is = HTypeofIs::cast(v); return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value())); + } else if (v->IsIsConstructCall()) { + return new LIsConstructCallAndBranch(TempRegister()); } else { if (v->IsConstant()) { if (HConstant::cast(v)->handle()->IsTrue()) { @@ -1925,6 +1927,12 @@ LInstruction* LChunkBuilder::DoTypeofIs(HTypeofIs* instr) { return DefineSameAsFirst(new LTypeofIs(UseRegister(instr->value()))); } + +LInstruction* LChunkBuilder::DoIsConstructCall(HIsConstructCall* instr) { + return DefineAsRegister(new LIsConstructCall); +} + + LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) { HEnvironment* env = current_block_->last_environment(); ASSERT(env != NULL); diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index f643dac9f6..e471274095 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -112,6 +112,8 @@ class LCodeGen; V(IsObjectAndBranch) \ V(IsSmi) \ V(IsSmiAndBranch) \ + V(IsConstructCall) \ + V(IsConstructCallAndBranch) \ V(JSArrayLength) \ V(Label) \ V(LazyBailout) \ @@ -755,6 +757,24 @@ class LHasCachedArrayIndexAndBranch: public LControlInstruction<1, 0> { }; +class LIsConstructCall: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(IsConstructCall, "is-construct-call") + DECLARE_HYDROGEN_ACCESSOR(IsConstructCall) +}; + + +class LIsConstructCallAndBranch: public LControlInstruction<0, 1> { + public: + explicit LIsConstructCallAndBranch(LOperand* temp) { + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch, + "is-construct-call-and-branch") +}; + + class LClassOfTest: public LTemplateInstruction<1, 1, 1> { public: LClassOfTest(LOperand* value, LOperand* temp) { diff --git a/src/runtime.cc b/src/runtime.cc index 4994378cd3..3e38d388d8 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -6681,28 +6681,50 @@ static MaybeObject* Runtime_NewClosure(Arguments args) { return *result; } + static MaybeObject* Runtime_NewObjectFromBound(Arguments args) { HandleScope scope; ASSERT(args.length() == 2); + // First argument is a function to use as a constructor. CONVERT_ARG_CHECKED(JSFunction, function, 0); - CONVERT_ARG_CHECKED(JSArray, params, 1); - RUNTIME_ASSERT(params->HasFastElements()); - FixedArray* fixed = FixedArray::cast(params->elements()); + // Second argument is either null or an array of bound arguments. + FixedArray* bound_args = NULL; + int bound_argc = 0; + if (!args[1]->IsNull()) { + CONVERT_ARG_CHECKED(JSArray, params, 1); + RUNTIME_ASSERT(params->HasFastElements()); + bound_args = FixedArray::cast(params->elements()); + bound_argc = Smi::cast(params->length())->value(); + } - int fixed_length = Smi::cast(params->length())->value(); - SmartPointer param_data(NewArray(fixed_length)); - for (int i = 0; i < fixed_length; i++) { - Handle val = Handle(fixed->get(i)); + // Find frame containing arguments passed to the caller. + JavaScriptFrameIterator it; + JavaScriptFrame* frame = it.frame(); + ASSERT(!frame->is_optimized()); + it.AdvanceToArgumentsFrame(); + frame = it.frame(); + int argc = frame->GetProvidedParametersCount(); + + // Prepend bound arguments to caller's arguments. + int total_argc = bound_argc + argc; + SmartPointer param_data(NewArray(total_argc)); + for (int i = 0; i < bound_argc; i++) { + Handle val = Handle(bound_args->get(i)); param_data[i] = val.location(); } + for (int i = 0; i < argc; i++) { + Handle val = Handle(frame->GetParameter(i)); + param_data[bound_argc + i] = val.location(); + } bool exception = false; - Handle result = Execution::New( - function, fixed_length, *param_data, &exception); + Handle result = + Execution::New(function, total_argc, *param_data, &exception); if (exception) { return Failure::Exception(); } + ASSERT(!result.is_null()); return *result; } diff --git a/src/v8natives.js b/src/v8natives.js index b0fb5bf171..83b00b0f13 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -1153,33 +1153,49 @@ function FunctionBind(this_arg) { // Length is 1. } // this_arg is not an argument that should be bound. var argc_bound = (%_ArgumentsLength() || 1) - 1; - if (argc_bound > 0) { + var fn = this; + if (argc_bound == 0) { + var result = function() { + if (%_IsConstructCall()) { + // %NewObjectFromBound implicitly uses arguments passed to this + // function. We do not pass the arguments object explicitly to avoid + // materializing it and guarantee that this function will be optimized. + return %NewObjectFromBound(fn, null); + } + + return fn.apply(this_arg, arguments); + }; + } else { var bound_args = new $Array(argc_bound); for(var i = 0; i < argc_bound; i++) { bound_args[i] = %_Arguments(i+1); } + + var result = function() { + // If this is a construct call we use a special runtime method + // to generate the actual object using the bound function. + if (%_IsConstructCall()) { + // %NewObjectFromBound implicitly uses arguments passed to this + // function. We do not pass the arguments object explicitly to avoid + // materializing it and guarantee that this function will be optimized. + return %NewObjectFromBound(fn, bound_args); + } + + // Combine the args we got from the bind call with the args + // given as argument to the invocation. + var argc = %_ArgumentsLength(); + var args = new $Array(argc + argc_bound); + // Add bound arguments. + for (var i = 0; i < argc_bound; i++) { + args[i] = bound_args[i]; + } + // Add arguments from call. + for (var i = 0; i < argc; i++) { + args[argc_bound + i] = %_Arguments(i); + } + return fn.apply(this_arg, args); + }; } - var fn = this; - var result = function() { - // Combine the args we got from the bind call with the args - // given as argument to the invocation. - var argc = %_ArgumentsLength(); - var args = new $Array(argc + argc_bound); - // Add bound arguments. - for (var i = 0; i < argc_bound; i++) { - args[i] = bound_args[i]; - } - // Add arguments from call. - for (var i = 0; i < argc; i++) { - args[argc_bound + i] = %_Arguments(i); - } - // If this is a construct call we use a special runtime method - // to generate the actual object using the bound function. - if (%_IsConstructCall()) { - return %NewObjectFromBound(fn, args); - } - return fn.apply(this_arg, args); - }; // We already have caller and arguments properties on functions, // which are non-configurable. It therefore makes no sence to diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 69b5441ec0..31b812cc8a 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -2284,6 +2284,54 @@ void LCodeGen::DoTypeofIs(LTypeofIs* instr) { } +void LCodeGen::DoIsConstructCall(LIsConstructCall* instr) { + Register result = ToRegister(instr->result()); + NearLabel true_label; + NearLabel false_label; + NearLabel done; + + EmitIsConstructCall(result); + __ j(equal, &true_label); + + __ LoadRoot(result, Heap::kFalseValueRootIndex); + __ jmp(&done); + + __ bind(&true_label); + __ LoadRoot(result, Heap::kTrueValueRootIndex); + + + __ bind(&done); +} + + +void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) { + Register temp = ToRegister(instr->TempAt(0)); + int true_block = chunk_->LookupDestination(instr->true_block_id()); + int false_block = chunk_->LookupDestination(instr->false_block_id()); + + EmitIsConstructCall(temp); + EmitBranch(true_block, false_block, equal); +} + + +void LCodeGen::EmitIsConstructCall(Register temp) { + // Get the frame pointer for the calling frame. + __ movq(temp, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); + + // Skip the arguments adaptor frame if it exists. + NearLabel check_frame_marker; + __ SmiCompare(Operand(temp, StandardFrameConstants::kContextOffset), + Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); + __ j(not_equal, &check_frame_marker); + __ movq(temp, Operand(rax, StandardFrameConstants::kCallerFPOffset)); + + // Check the marker in the calling frame. + __ bind(&check_frame_marker); + __ SmiCompare(Operand(temp, StandardFrameConstants::kMarkerOffset), + Smi::FromInt(StackFrame::CONSTRUCT)); +} + + void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) { Register input = ToRegister(instr->InputAt(0)); int true_block = chunk_->LookupDestination(instr->true_block_id()); diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h index cbcc5c8b0c..6f8f06e345 100644 --- a/src/x64/lithium-codegen-x64.h +++ b/src/x64/lithium-codegen-x64.h @@ -221,6 +221,10 @@ class LCodeGen BASE_EMBEDDED { Label* is_not_object, Label* is_object); + // Emits optimized code for %_IsConstructCall(). + // Caller should branch on equal condition. + void EmitIsConstructCall(Register temp); + LChunk* const chunk_; MacroAssembler* const masm_; CompilationInfo* const info_; diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index 64c2ba63e1..b6e0a44cf6 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -1082,6 +1082,8 @@ LInstruction* LChunkBuilder::DoTest(HTest* instr) { } else if (v->IsTypeofIs()) { HTypeofIs* typeof_is = HTypeofIs::cast(v); return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value())); + } else if (v->IsIsConstructCall()) { + return new LIsConstructCallAndBranch(TempRegister()); } else { if (v->IsConstant()) { if (HConstant::cast(v)->handle()->IsTrue()) { @@ -1775,6 +1777,12 @@ LInstruction* LChunkBuilder::DoTypeofIs(HTypeofIs* instr) { return NULL; } + +LInstruction* LChunkBuilder::DoIsConstructCall(HIsConstructCall* instr) { + return DefineAsRegister(new LIsConstructCall); +} + + LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) { HEnvironment* env = current_block_->last_environment(); ASSERT(env != NULL); diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index 64d141e335..72027b2727 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -259,6 +259,8 @@ class LCodeGen; V(Typeof) \ V(TypeofIs) \ V(TypeofIsAndBranch) \ + V(IsConstructCall) \ + V(IsConstructCallAndBranch) \ V(UnaryMathOperation) \ V(UnknownOSRValue) \ V(ValueOf) @@ -1763,6 +1765,24 @@ class LTypeofIsAndBranch: public LControlInstruction<1, 0> { }; +class LIsConstructCall: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(IsConstructCall, "is-construct-call") + DECLARE_HYDROGEN_ACCESSOR(IsConstructCall) +}; + + +class LIsConstructCallAndBranch: public LControlInstruction<0, 1> { + public: + explicit LIsConstructCallAndBranch(LOperand* temp) { + temps_[0] = temp; + } + + DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch, + "is-construct-call-and-branch") +}; + + class LDeleteProperty: public LTemplateInstruction<1, 2, 0> { public: LDeleteProperty(LOperand* obj, LOperand* key) {