| 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)),
|
|
|