| Index: src/x64/stub-cache-x64.cc
|
| diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc
|
| index 94f5efaaa98eeaa6b9625a726f89fb271d033511..918b72f494b3a8eba900131c49de10e3ca652f23 100644
|
| --- a/src/x64/stub-cache-x64.cc
|
| +++ b/src/x64/stub-cache-x64.cc
|
| @@ -564,12 +564,14 @@ static void ReserveSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
|
| // -- rsp[0] : return address
|
| // -- rsp[8] : last argument in the internal frame of the caller
|
| // -----------------------------------
|
| - __ pop(scratch);
|
| - __ Push(Smi::FromInt(0));
|
| - __ Push(Smi::FromInt(0));
|
| - __ Push(Smi::FromInt(0));
|
| - __ Push(Smi::FromInt(0));
|
| - __ push(scratch);
|
| + __ movq(scratch, Operand(rsp, 0));
|
| + __ subq(rsp, Immediate(4 * kPointerSize));
|
| + __ movq(Operand(rsp, 0), scratch);
|
| + __ Move(scratch, Smi::FromInt(0));
|
| + __ movq(Operand(rsp, 1 * kPointerSize), scratch);
|
| + __ movq(Operand(rsp, 2 * kPointerSize), scratch);
|
| + __ movq(Operand(rsp, 3 * kPointerSize), scratch);
|
| + __ movq(Operand(rsp, 4 * kPointerSize), scratch);
|
| }
|
|
|
|
|
| @@ -582,9 +584,9 @@ static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
|
| // -- rsp[32] : first fast api call extra argument
|
| // -- rsp[40] : last argument in the internal frame
|
| // -----------------------------------
|
| - __ pop(scratch);
|
| - __ Drop(4);
|
| - __ push(scratch);
|
| + __ movq(scratch, Operand(rsp, 0));
|
| + __ movq(Operand(rsp, 4 * kPointerSize), scratch);
|
| + __ addq(rsp, Immediate(kPointerSize * 4));
|
| }
|
|
|
|
|
| @@ -853,129 +855,6 @@ static Object* GenerateCheckPropertyCell(MacroAssembler* masm,
|
|
|
| #define __ ACCESS_MASM((masm()))
|
|
|
| -
|
| -Object* CallStubCompiler::CompileArrayPushCall(Object* object,
|
| - JSObject* holder,
|
| - JSFunction* function,
|
| - String* name,
|
| - CheckType check) {
|
| - // ----------- S t a t e -------------
|
| - // rcx : function name
|
| - // rsp[0] : return address
|
| - // rsp[8] : argument argc
|
| - // rsp[16] : argument argc - 1
|
| - // ...
|
| - // rsp[argc * 8] : argument 1
|
| - // rsp[(argc + 1) * 8] : argument 0 = receiver
|
| - // -----------------------------------
|
| -
|
| - // If object is not an array, bail out to regular call.
|
| - if (!object->IsJSArray()) {
|
| - return Heap::undefined_value();
|
| - }
|
| -
|
| - // TODO(639): faster implementation.
|
| - ASSERT(check == RECEIVER_MAP_CHECK);
|
| -
|
| - Label miss;
|
| -
|
| - // Get the receiver from the stack.
|
| - const int argc = arguments().immediate();
|
| - __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
|
| -
|
| - // Check that the receiver isn't a smi.
|
| - __ JumpIfSmi(rdx, &miss);
|
| -
|
| - // Check that the maps haven't changed.
|
| - CheckPrototypes(JSObject::cast(object), rdx, holder,
|
| - rbx, rax, name, &miss);
|
| -
|
| - // Patch the receiver on the stack with the global proxy if
|
| - // necessary.
|
| - if (object->IsGlobalObject()) {
|
| - __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
|
| - __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
|
| - }
|
| -
|
| - __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush),
|
| - argc + 1,
|
| - 1);
|
| -
|
| - // Handle call cache miss.
|
| - __ bind(&miss);
|
| - Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
| - __ Jump(ic, RelocInfo::CODE_TARGET);
|
| -
|
| - // Return the generated code.
|
| - String* function_name = NULL;
|
| - if (function->shared()->name()->IsString()) {
|
| - function_name = String::cast(function->shared()->name());
|
| - }
|
| - return GetCode(CONSTANT_FUNCTION, function_name);
|
| -}
|
| -
|
| -
|
| -Object* CallStubCompiler::CompileArrayPopCall(Object* object,
|
| - JSObject* holder,
|
| - JSFunction* function,
|
| - String* name,
|
| - CheckType check) {
|
| - // ----------- S t a t e -------------
|
| - // rcx : function name
|
| - // rsp[0] : return address
|
| - // rsp[8] : argument argc
|
| - // rsp[16] : argument argc - 1
|
| - // ...
|
| - // rsp[argc * 8] : argument 1
|
| - // rsp[(argc + 1) * 8] : argument 0 = receiver
|
| - // -----------------------------------
|
| -
|
| - // If object is not an array, bail out to regular call.
|
| - if (!object->IsJSArray()) {
|
| - return Heap::undefined_value();
|
| - }
|
| -
|
| - // TODO(642): faster implementation.
|
| - ASSERT(check == RECEIVER_MAP_CHECK);
|
| -
|
| - Label miss;
|
| -
|
| - // Get the receiver from the stack.
|
| - const int argc = arguments().immediate();
|
| - __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
|
| -
|
| - // Check that the receiver isn't a smi.
|
| - __ JumpIfSmi(rdx, &miss);
|
| -
|
| - // Check that the maps haven't changed.
|
| - CheckPrototypes(JSObject::cast(object), rdx, holder,
|
| - rbx, rax, name, &miss);
|
| -
|
| - // Patch the receiver on the stack with the global proxy if
|
| - // necessary.
|
| - if (object->IsGlobalObject()) {
|
| - __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
|
| - __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
|
| - }
|
| -
|
| - __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop),
|
| - argc + 1,
|
| - 1);
|
| -
|
| - // Handle call cache miss.
|
| - __ bind(&miss);
|
| - Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
| - __ Jump(ic, RelocInfo::CODE_TARGET);
|
| -
|
| - // Return the generated code.
|
| - String* function_name = NULL;
|
| - if (function->shared()->name()->IsString()) {
|
| - function_name = String::cast(function->shared()->name());
|
| - }
|
| - return GetCode(CONSTANT_FUNCTION, function_name);
|
| -}
|
| -
|
| -
|
| Object* CallStubCompiler::CompileCallConstant(Object* object,
|
| JSObject* holder,
|
| JSFunction* function,
|
| @@ -1190,6 +1069,257 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
|
| }
|
|
|
|
|
| +Object* CallStubCompiler::CompileArrayPushCall(Object* object,
|
| + JSObject* holder,
|
| + JSFunction* function,
|
| + String* name,
|
| + CheckType check) {
|
| + // ----------- S t a t e -------------
|
| + // -- rcx : name
|
| + // -- rsp[0] : return address
|
| + // -- rsp[(argc - n) * 8] : arg[n] (zero-based)
|
| + // -- ...
|
| + // -- rsp[(argc + 1) * 8] : receiver
|
| + // -----------------------------------
|
| + ASSERT(check == RECEIVER_MAP_CHECK);
|
| +
|
| + // If object is not an array, bail out to regular call.
|
| + if (!object->IsJSArray()) {
|
| + return Heap::undefined_value();
|
| + }
|
| +
|
| + Label miss;
|
| +
|
| + // Get the receiver from the stack.
|
| + const int argc = arguments().immediate();
|
| + __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
|
| +
|
| + // Check that the receiver isn't a smi.
|
| + __ JumpIfSmi(rdx, &miss);
|
| +
|
| + CheckPrototypes(JSObject::cast(object),
|
| + rdx,
|
| + holder,
|
| + rbx,
|
| + rax,
|
| + name,
|
| + &miss);
|
| +
|
| + if (argc == 0) {
|
| + // Noop, return the length.
|
| + __ movq(rax, FieldOperand(rdx, JSArray::kLengthOffset));
|
| + __ ret((argc + 1) * kPointerSize);
|
| + } else {
|
| + // Get the elements array of the object.
|
| + __ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset));
|
| +
|
| + // Check that the elements are in fast mode (not dictionary).
|
| + __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset),
|
| + Factory::fixed_array_map());
|
| + __ j(not_equal, &miss);
|
| +
|
| + if (argc == 1) { // Otherwise fall through to call builtin.
|
| + Label call_builtin, exit, with_rset_update, attempt_to_grow_elements;
|
| +
|
| + // Get the array's length into rax and calculate new length.
|
| + __ movq(rax, FieldOperand(rdx, JSArray::kLengthOffset));
|
| + STATIC_ASSERT(FixedArray::kMaxLength < Smi::kMaxValue);
|
| + __ SmiAddConstant(rax, rax, Smi::FromInt(argc));
|
| +
|
| + // Get the element's length into rcx.
|
| + __ movl(rcx, FieldOperand(rbx, FixedArray::kLengthOffset));
|
| + __ Integer32ToSmi(rcx, rcx);
|
| +
|
| + // Check if we could survive without allocation.
|
| + __ SmiCompare(rax, rcx);
|
| + __ j(greater, &attempt_to_grow_elements);
|
| +
|
| + // Save new length.
|
| + __ movq(FieldOperand(rdx, JSArray::kLengthOffset), rax);
|
| +
|
| + // Push the element.
|
| + __ movq(rcx, Operand(rsp, argc * kPointerSize));
|
| + SmiIndex index =
|
| + masm()->SmiToIndex(kScratchRegister, rax, times_pointer_size);
|
| + __ lea(rdx, FieldOperand(rbx,
|
| + index.reg, index.scale,
|
| + FixedArray::kHeaderSize - argc * kPointerSize));
|
| + __ movq(Operand(rdx, 0), rcx);
|
| +
|
| + // Check if value is a smi.
|
| + __ JumpIfNotSmi(rcx, &with_rset_update);
|
| +
|
| + __ bind(&exit);
|
| + __ ret((argc + 1) * kPointerSize);
|
| +
|
| + __ bind(&with_rset_update);
|
| +
|
| + __ InNewSpace(rbx, rcx, equal, &exit);
|
| +
|
| + RecordWriteStub stub(rbx, rdx, rcx);
|
| + __ CallStub(&stub);
|
| + __ ret((argc + 1) * kPointerSize);
|
| +
|
| + __ bind(&attempt_to_grow_elements);
|
| + ExternalReference new_space_allocation_top =
|
| + ExternalReference::new_space_allocation_top_address();
|
| + ExternalReference new_space_allocation_limit =
|
| + ExternalReference::new_space_allocation_limit_address();
|
| +
|
| + const int kAllocationDelta = 4;
|
| + // Load top.
|
| + __ movq(rcx, new_space_allocation_top);
|
| + __ movq(rcx, Operand(rcx, 0));
|
| +
|
| + // Check if it's the end of elements.
|
| + index = masm()->SmiToIndex(kScratchRegister, rax, times_pointer_size);
|
| + __ lea(rdx, FieldOperand(rbx,
|
| + index.reg, index.scale,
|
| + FixedArray::kHeaderSize - argc * kPointerSize));
|
| + __ cmpq(rdx, rcx);
|
| + __ j(not_equal, &call_builtin);
|
| + __ addq(rcx, Immediate(kAllocationDelta * kPointerSize));
|
| + __ movq(kScratchRegister, new_space_allocation_limit);
|
| + __ cmpq(rcx, Operand(kScratchRegister, 0));
|
| + __ j(above, &call_builtin);
|
| +
|
| + // We fit and could grow elements.
|
| + __ movq(kScratchRegister, new_space_allocation_top);
|
| + __ movq(Operand(kScratchRegister, 0), rcx);
|
| + __ movq(rcx, Operand(rsp, argc * kPointerSize));
|
| +
|
| + // Push the argument...
|
| + __ movq(Operand(rdx, 0), rcx);
|
| + // ... and fill the rest with holes.
|
| + __ Move(kScratchRegister, Factory::the_hole_value());
|
| + for (int i = 1; i < kAllocationDelta; i++) {
|
| + __ movq(Operand(rdx, i * kPointerSize), kScratchRegister);
|
| + }
|
| +
|
| + // Restore receiver to rdx as finish sequence assumes it's here.
|
| + __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
|
| +
|
| + // Increment element's and array's sizes.
|
| + __ addq(FieldOperand(rbx, FixedArray::kLengthOffset),
|
| + Immediate(kAllocationDelta));
|
| + __ movq(FieldOperand(rdx, JSArray::kLengthOffset), rax);
|
| +
|
| + // Elements are in new space, so no remembered set updates are necessary.
|
| + __ ret((argc + 1) * kPointerSize);
|
| +
|
| + __ bind(&call_builtin);
|
| + }
|
| +
|
| + __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush),
|
| + argc + 1,
|
| + 1);
|
| + }
|
| +
|
| + __ bind(&miss);
|
| +
|
| + Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
| + __ jmp(ic, RelocInfo::CODE_TARGET);
|
| +
|
| + // Return the generated code.
|
| + String* function_name = NULL;
|
| + if (function->shared()->name()->IsString()) {
|
| + function_name = String::cast(function->shared()->name());
|
| + }
|
| + return GetCode(CONSTANT_FUNCTION, function_name);
|
| +}
|
| +
|
| +
|
| +Object* CallStubCompiler::CompileArrayPopCall(Object* object,
|
| + JSObject* holder,
|
| + JSFunction* function,
|
| + String* name,
|
| + CheckType check) {
|
| + // ----------- S t a t e -------------
|
| + // -- ecx : name
|
| + // -- esp[0] : return address
|
| + // -- esp[(argc - n) * 4] : arg[n] (zero-based)
|
| + // -- ...
|
| + // -- esp[(argc + 1) * 4] : receiver
|
| + // -----------------------------------
|
| + ASSERT(check == RECEIVER_MAP_CHECK);
|
| +
|
| + // If object is not an array, bail out to regular call.
|
| + if (!object->IsJSArray()) {
|
| + return Heap::undefined_value();
|
| + }
|
| +
|
| + Label miss, return_undefined, call_builtin;
|
| +
|
| + // Get the receiver from the stack.
|
| + const int argc = arguments().immediate();
|
| + __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
|
| +
|
| + // Check that the receiver isn't a smi.
|
| + __ JumpIfSmi(rdx, &miss);
|
| +
|
| + CheckPrototypes(JSObject::cast(object), rdx,
|
| + holder, rbx,
|
| + rax, name, &miss);
|
| +
|
| + // Get the elements array of the object.
|
| + __ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset));
|
| +
|
| + // Check that the elements are in fast mode (not dictionary).
|
| + __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), Factory::fixed_array_map());
|
| + __ j(not_equal, &miss);
|
| +
|
| + // Get the array's length into rcx and calculate new length.
|
| + __ movq(rcx, FieldOperand(rdx, JSArray::kLengthOffset));
|
| + __ SmiSubConstant(rcx, rcx, Smi::FromInt(1));
|
| + __ SmiTest(rcx);
|
| + __ j(negative, &return_undefined);
|
| +
|
| + // Get the last element.
|
| + __ Move(r9, Factory::the_hole_value());
|
| + SmiIndex index =
|
| + masm()->SmiToIndex(r8, rcx, times_pointer_size);
|
| + __ movq(rax, FieldOperand(rbx,
|
| + index.reg, index.scale,
|
| + FixedArray::kHeaderSize));
|
| + // Check if element is already the hole.
|
| + __ cmpq(rax, r9);
|
| + __ j(equal, &call_builtin);
|
| +
|
| + // Set the array's length.
|
| + __ movq(FieldOperand(rdx, JSArray::kLengthOffset), rcx);
|
| +
|
| + // Fill with the hole and return original value..
|
| + __ movq(FieldOperand(rbx,
|
| + index.reg, index.scale,
|
| + FixedArray::kHeaderSize),
|
| + r9);
|
| + __ ret((argc + 1) * kPointerSize);
|
| +
|
| + __ bind(&return_undefined);
|
| +
|
| + __ Move(rax, Factory::undefined_value());
|
| + __ ret((argc + 1) * kPointerSize);
|
| +
|
| + __ bind(&call_builtin);
|
| + __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop),
|
| + argc + 1,
|
| + 1);
|
| + __ bind(&miss);
|
| +
|
| + Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
| + __ jmp(ic, RelocInfo::CODE_TARGET);
|
| +
|
| + // Return the generated code.
|
| + String* function_name = NULL;
|
| + if (function->shared()->name()->IsString()) {
|
| + function_name = String::cast(function->shared()->name());
|
| + }
|
| + return GetCode(CONSTANT_FUNCTION, function_name);
|
| +}
|
| +
|
| +
|
| +
|
| +
|
| Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
|
| JSObject* holder,
|
| String* name) {
|
| @@ -2043,8 +2173,13 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
|
| Label* miss) {
|
| // Check that the maps haven't changed.
|
| Register result =
|
| - __ CheckMaps(object, object_reg, holder, holder_reg, scratch,
|
| - save_at_depth, miss);
|
| + masm()->CheckMaps(object,
|
| + object_reg,
|
| + holder,
|
| + holder_reg,
|
| + scratch,
|
| + save_at_depth,
|
| + miss);
|
|
|
| // If we've skipped any global objects, it's not enough to verify
|
| // that their maps haven't changed. We also need to check that the
|
|
|