Index: src/x64/builtins-x64.cc |
=================================================================== |
--- src/x64/builtins-x64.cc (revision 2286) |
+++ src/x64/builtins-x64.cc (working copy) |
@@ -168,14 +168,292 @@ |
} |
-void Builtins::Generate_FunctionApply(MacroAssembler* masm) { |
- masm->int3(); // UNIMPLEMENTED. |
- masm->movq(kScratchRegister, Immediate(0xBEFA)); // Debugging aid. |
+void Builtins::Generate_FunctionCall(MacroAssembler* masm) { |
+ // Stack Layout: |
+ // rsp: return address |
+ // +1: Argument n |
+ // +2: Argument n-1 |
+ // ... |
+ // +n: Argument 1 = receiver |
+ // +n+1: Argument 0 = function to call |
+ // |
+ // rax contains the number of arguments, n, not counting the function. |
+ // |
+ // 1. Make sure we have at least one argument. |
+ { Label done; |
+ __ testq(rax, rax); |
+ __ j(not_zero, &done); |
+ __ pop(rbx); |
+ __ Push(Factory::undefined_value()); |
+ __ push(rbx); |
+ __ incq(rax); |
+ __ bind(&done); |
+ } |
+ |
+ // 2. Get the function to call from the stack. |
+ { Label done, non_function, function; |
+ // The function to call is at position n+1 on the stack. |
+ __ movq(rdi, Operand(rsp, rax, times_pointer_size, +1 * kPointerSize)); |
+ __ testl(rdi, Immediate(kSmiTagMask)); |
+ __ j(zero, &non_function); |
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); |
+ __ j(equal, &function); |
+ |
+ // Non-function called: Clear the function to force exception. |
+ __ bind(&non_function); |
+ __ xor_(rdi, rdi); |
+ __ jmp(&done); |
+ |
+ // Function called: Change context eagerly to get the right global object. |
+ __ bind(&function); |
+ __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); |
+ |
+ __ bind(&done); |
+ } |
+ |
+ // 3. Make sure first argument is an object; convert if necessary. |
+ { Label call_to_object, use_global_receiver, patch_receiver, done; |
+ __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0)); |
+ |
+ __ testl(rbx, Immediate(kSmiTagMask)); |
+ __ j(zero, &call_to_object); |
+ |
+ __ Cmp(rbx, Factory::null_value()); |
+ __ j(equal, &use_global_receiver); |
+ __ Cmp(rbx, Factory::undefined_value()); |
+ __ j(equal, &use_global_receiver); |
+ |
+ __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx); |
+ __ j(below, &call_to_object); |
+ __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE); |
+ __ j(below_equal, &done); |
+ |
+ __ bind(&call_to_object); |
+ __ EnterInternalFrame(); // preserves rax, rbx, rdi |
+ |
+ // Store the arguments count on the stack (smi tagged). |
+ ASSERT(kSmiTag == 0); |
+ __ shl(rax, Immediate(kSmiTagSize)); |
+ __ push(rax); |
+ |
+ __ push(rdi); // save edi across the call |
+ __ push(rbx); |
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); |
+ __ movq(rbx, rax); |
+ __ pop(rdi); // restore edi after the call |
+ |
+ // Get the arguments count and untag it. |
+ __ pop(rax); |
+ __ shr(rax, Immediate(kSmiTagSize)); |
+ |
+ __ LeaveInternalFrame(); |
+ __ jmp(&patch_receiver); |
+ |
+ // Use the global receiver object from the called function as the receiver. |
+ __ bind(&use_global_receiver); |
+ const int kGlobalIndex = |
+ Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; |
+ __ movq(rbx, FieldOperand(rsi, kGlobalIndex)); |
+ __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); |
+ |
+ __ bind(&patch_receiver); |
+ __ movq(Operand(rsp, rax, times_pointer_size, 0), rbx); |
+ |
+ __ bind(&done); |
+ } |
+ |
+ // 4. Shift stuff one slot down the stack. |
+ { Label loop; |
+ __ lea(rcx, Operand(rax, +1)); // +1 ~ copy receiver too |
+ __ bind(&loop); |
+ __ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0)); |
+ __ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx); |
+ __ decq(rcx); |
+ __ j(not_zero, &loop); |
+ } |
+ |
+ // 5. Remove TOS (copy of last arguments), but keep return address. |
+ __ pop(rbx); |
+ __ pop(rcx); |
+ __ push(rbx); |
+ __ decq(rax); |
+ |
+ // 6. Check that function really was a function and get the code to |
+ // call from the function and check that the number of expected |
+ // arguments matches what we're providing. |
+ { Label invoke, trampoline; |
+ __ testq(rdi, rdi); |
+ __ j(not_zero, &invoke); |
+ __ xor_(rbx, rbx); |
+ __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION); |
+ __ bind(&trampoline); |
+ __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), |
+ RelocInfo::CODE_TARGET); |
+ |
+ __ bind(&invoke); |
+ __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); |
+ __ movsxlq(rbx, |
+ FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset)); |
+ __ movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset)); |
+ __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize)); |
+ __ cmpq(rax, rbx); |
+ __ j(not_equal, &trampoline); |
+ } |
+ |
+ // 7. Jump (tail-call) to the code in register edx without checking arguments. |
+ ParameterCount expected(0); |
+ __ InvokeCode(rdx, expected, expected, JUMP_FUNCTION); |
} |
-void Builtins::Generate_FunctionCall(MacroAssembler* masm) { |
- masm->int3(); // UNIMPLEMENTED. |
- masm->movq(kScratchRegister, Immediate(0xBEFC)); // Debugging aid. |
+ |
+void Builtins::Generate_FunctionApply(MacroAssembler* masm) { |
+ // Stack at entry: |
+ // rsp: return address |
+ // rsp+8: arguments |
+ // rsp+16: receiver ("this") |
+ // rsp+24: function |
+ __ EnterInternalFrame(); |
+ // Stack frame: |
+ // rbp: Old base pointer |
+ // rbp[1]: return address |
+ // rbp[2]: function arguments |
+ // rbp[3]: receiver |
+ // rbp[4]: function |
+ static const int kArgumentsOffset = 2 * kPointerSize; |
+ static const int kReceiverOffset = 3 * kPointerSize; |
+ static const int kFunctionOffset = 4 * kPointerSize; |
+ __ push(Operand(rbp, kFunctionOffset)); |
+ __ push(Operand(rbp, kArgumentsOffset)); |
+ __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); |
+ |
+ if (FLAG_check_stack) { |
+ // We need to catch preemptions right here, otherwise an unlucky preemption |
+ // could show up as a failed apply. |
+ Label retry_preemption; |
+ Label no_preemption; |
+ __ bind(&retry_preemption); |
+ ExternalReference stack_guard_limit = |
+ ExternalReference::address_of_stack_guard_limit(); |
+ __ movq(kScratchRegister, stack_guard_limit); |
+ __ movq(rcx, rsp); |
+ __ subq(rcx, Operand(kScratchRegister, 0)); |
+ // rcx contains the difference between the stack limit and the stack top. |
+ // We use it below to check that there is enough room for the arguments. |
+ __ j(above, &no_preemption); |
+ |
+ // Preemption! |
+ // Because runtime functions always remove the receiver from the stack, we |
+ // have to fake one to avoid underflowing the stack. |
+ __ push(rax); |
+ __ push(Immediate(Smi::FromInt(0))); |
+ |
+ // Do call to runtime routine. |
+ __ CallRuntime(Runtime::kStackGuard, 1); |
+ __ pop(rax); |
+ __ jmp(&retry_preemption); |
+ |
+ __ bind(&no_preemption); |
+ |
+ Label okay; |
+ // Make rdx the space we need for the array when it is unrolled onto the |
+ // stack. |
+ __ movq(rdx, rax); |
+ __ shl(rdx, Immediate(kPointerSizeLog2 - kSmiTagSize)); |
+ __ cmpq(rcx, rdx); |
+ __ j(greater, &okay); |
+ |
+ // Too bad: Out of stack space. |
+ __ push(Operand(rbp, kFunctionOffset)); |
+ __ push(rax); |
+ __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); |
+ __ bind(&okay); |
+ } |
+ |
+ // Push current index and limit. |
+ const int kLimitOffset = |
+ StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize; |
+ const int kIndexOffset = kLimitOffset - 1 * kPointerSize; |
+ __ push(rax); // limit |
+ __ push(Immediate(0)); // index |
+ |
+ // Change context eagerly to get the right global object if |
+ // necessary. |
+ __ movq(rdi, Operand(rbp, kFunctionOffset)); |
+ __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); |
+ |
+ // Compute the receiver. |
+ Label call_to_object, use_global_receiver, push_receiver; |
+ __ movq(rbx, Operand(rbp, kReceiverOffset)); |
+ __ testl(rbx, Immediate(kSmiTagMask)); |
+ __ j(zero, &call_to_object); |
+ __ Cmp(rbx, Factory::null_value()); |
+ __ j(equal, &use_global_receiver); |
+ __ Cmp(rbx, Factory::undefined_value()); |
+ __ j(equal, &use_global_receiver); |
+ |
+ // If given receiver is already a JavaScript object then there's no |
+ // reason for converting it. |
+ __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx); |
+ __ j(less, &call_to_object); |
+ __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE); |
+ __ j(less_equal, &push_receiver); |
+ |
+ // Convert the receiver to an object. |
+ __ bind(&call_to_object); |
+ __ push(rbx); |
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); |
+ __ movq(rbx, rax); |
+ __ jmp(&push_receiver); |
+ |
+ // Use the current global receiver object as the receiver. |
+ __ bind(&use_global_receiver); |
+ const int kGlobalOffset = |
+ Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; |
+ __ movq(rbx, FieldOperand(rsi, kGlobalOffset)); |
+ __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); |
+ |
+ // Push the receiver. |
+ __ bind(&push_receiver); |
+ __ push(rbx); |
+ |
+ // Copy all arguments from the array to the stack. |
+ Label entry, loop; |
+ __ movq(rax, Operand(rbp, kIndexOffset)); |
+ __ jmp(&entry); |
+ __ bind(&loop); |
+ __ movq(rcx, Operand(rbp, kArgumentsOffset)); // load arguments |
+ __ push(rcx); |
+ __ push(rax); |
+ |
+ // Use inline caching to speed up access to arguments. |
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); |
+ __ Call(ic, RelocInfo::CODE_TARGET); |
+ // It is important that we do not have a test instruction after the |
+ // call. A test instruction after the call is used to indicate that |
+ // we have generated an inline version of the keyed load. In this |
+ // case, we know that we are not generating a test instruction next. |
+ |
+ // Remove IC arguments from the stack and push the nth argument. |
+ __ addq(rsp, Immediate(2 * kPointerSize)); |
+ __ push(rax); |
+ |
+ // Update the index on the stack and in register rax. |
+ __ movq(rax, Operand(rbp, kIndexOffset)); |
+ __ addq(rax, Immediate(Smi::FromInt(1))); |
+ __ movq(Operand(rbp, kIndexOffset), rax); |
+ |
+ __ bind(&entry); |
+ __ cmpq(rax, Operand(rbp, kLimitOffset)); |
+ __ j(not_equal, &loop); |
+ |
+ // Invoke the function. |
+ ParameterCount actual(rax); |
+ __ shr(rax, Immediate(kSmiTagSize)); |
+ __ movq(rdi, Operand(rbp, kFunctionOffset)); |
+ __ InvokeFunction(rdi, actual, CALL_FUNCTION); |
+ |
+ __ LeaveInternalFrame(); |
+ __ ret(3 * kPointerSize); // remove function, receiver, and arguments |
} |