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 |