Index: src/x64/builtins-x64.cc |
diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc |
index 8482023f361c1effe6e0211cdff1bf8be051b592..de30646d92c2967ecb0bdb58e637f2997fb22e0d 100644 |
--- a/src/x64/builtins-x64.cc |
+++ b/src/x64/builtins-x64.cc |
@@ -185,14 +185,14 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { |
void Builtins::Generate_FunctionCall(MacroAssembler* masm) { |
// Stack Layout: |
- // rsp: return address |
- // +1: Argument n |
- // +2: Argument n-1 |
+ // rsp[0]: Return address |
+ // rsp[1]: Argument n |
+ // rsp[2]: Argument n-1 |
// ... |
- // +n: Argument 1 = receiver |
- // +n+1: Argument 0 = function to call |
+ // rsp[n]: Argument 1 |
+ // rsp[n+1]: Receiver (function to call) |
// |
- // rax contains the number of arguments, n, not counting the function. |
+ // rax contains the number of arguments, n, not counting the receiver. |
// |
// 1. Make sure we have at least one argument. |
{ Label done; |
@@ -205,31 +205,26 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { |
__ bind(&done); |
} |
- // 2. Get the function to call from the stack. |
- { Label done, non_function, function; |
+ // 2. Get the function to call (passed as receiver) from the stack, check |
+ // if it is a function. |
+ Label non_function; |
+ { Label function; |
// The function to call is at position n+1 on the stack. |
- __ movq(rdi, Operand(rsp, rax, times_pointer_size, +1 * kPointerSize)); |
+ __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize)); |
__ JumpIfSmi(rdi, &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. |
+ __ j(not_equal, &non_function); |
__ 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)); |
+ // 3a. Patch the first argument if necessary when calling a function. |
+ Label shift_arguments; |
+ { Label convert_to_object, use_global_receiver, patch_receiver; |
+ // Change context eagerly in case we need the global receiver. |
+ __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); |
- __ JumpIfSmi(rbx, &call_to_object); |
+ __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0)); |
+ __ JumpIfSmi(rbx, &convert_to_object); |
__ CompareRoot(rbx, Heap::kNullValueRootIndex); |
__ j(equal, &use_global_receiver); |
@@ -237,31 +232,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { |
__ j(equal, &use_global_receiver); |
__ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx); |
- __ j(below, &call_to_object); |
+ __ j(below, &convert_to_object); |
__ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE); |
- __ j(below_equal, &done); |
- |
- __ bind(&call_to_object); |
- __ EnterInternalFrame(); // preserves rax, rbx, rdi |
+ __ j(below_equal, &shift_arguments); |
- // Store the arguments count on the stack (smi tagged). |
+ __ bind(&convert_to_object); |
+ __ EnterInternalFrame(); // In order to preserve argument count. |
__ Integer32ToSmi(rax, rax); |
__ 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); |
__ SmiToInteger32(rax, rax); |
- |
__ LeaveInternalFrame(); |
+ // Restore the function to rdi. |
+ __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize)); |
__ jmp(&patch_receiver); |
- // Use the global receiver object from the called function as the 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; |
@@ -273,41 +265,47 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { |
__ bind(&patch_receiver); |
__ movq(Operand(rsp, rax, times_pointer_size, 0), rbx); |
- __ bind(&done); |
+ __ jmp(&shift_arguments); |
} |
- // 4. Check that the function really is a function. |
- { Label real_function; |
- __ testq(rdi, rdi); |
- __ j(not_zero, &real_function); |
- __ xor_(rbx, rbx); |
- // CALL_NON_FUNCTION will expect to find the non-function callee on the |
- // expression stack of the caller. Transfer it from receiver to the |
- // caller's expression stack (and make the first argument the receiver |
- // for CALL_NON_FUNCTION) by decrementing the argument count. |
- __ decq(rax); |
- __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION); |
- __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), |
- RelocInfo::CODE_TARGET); |
- __ bind(&real_function); |
- } |
+ // 3b. Patch the first argument when calling a non-function. The |
+ // CALL_NON_FUNCTION builtin expects the non-function callee as |
+ // receiver, so overwrite the first argument which will ultimately |
+ // become the receiver. |
+ __ bind(&non_function); |
+ __ movq(Operand(rsp, rax, times_pointer_size, 0), rdi); |
+ __ xor_(rdi, rdi); |
- // 5. Shift arguments and return address one slot down on the stack |
- // (overwriting the receiver). |
+ // 4. Shift arguments and return address one slot down on the stack |
+ // (overwriting the original receiver). Adjust argument count to make |
+ // the original first argument the new receiver. |
+ __ bind(&shift_arguments); |
{ Label loop; |
__ movq(rcx, rax); |
__ 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_sign, &loop); |
+ __ j(not_sign, &loop); // While non-negative (to copy return address). |
__ pop(rbx); // Discard copy of return address. |
__ decq(rax); // One fewer argument (first argument is new receiver). |
} |
- // 6. Get the code to call from the function and check that the number of |
- // expected arguments matches what we're providing. |
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin. |
+ { Label function; |
+ __ testq(rdi, rdi); |
+ __ j(not_zero, &function); |
+ __ xor_(rbx, rbx); |
+ __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION); |
+ __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), |
+ RelocInfo::CODE_TARGET); |
+ __ bind(&function); |
+ } |
+ |
+ // 5b. Get the code to call from the function and check that the number of |
+ // expected arguments matches what we're providing. If so, jump |
+ // (tail-call) to the code in register edx without checking arguments. |
__ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); |
__ movsxlq(rbx, |
FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset)); |
@@ -318,7 +316,6 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { |
Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), |
RelocInfo::CODE_TARGET); |
- // 7. Jump (tail-call) to the code in register edx without checking arguments. |
ParameterCount expected(0); |
__ InvokeCode(rdx, expected, expected, JUMP_FUNCTION); |
} |
@@ -905,7 +902,11 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { |
// edi: called object |
// eax: number of arguments |
__ bind(&non_function_call); |
- // Set expected number of arguments to zero (not changing eax). |
+ // CALL_NON_FUNCTION expects the non-function constructor as receiver |
+ // (instead of the original receiver from the call site). The receiver is |
+ // stack element argc+1. |
+ __ movq(Operand(rsp, rax, times_pointer_size, kPointerSize), rdi); |
+ // Set expected number of arguments to zero (not changing rax). |
__ movq(rbx, Immediate(0)); |
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); |
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), |