X87: [builtins] Unify the various versions of [[Call]] with a Call builtin.
authorchunyang.dai <chunyang.dai@intel.com>
Thu, 10 Sep 2015 05:41:33 +0000 (22:41 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 10 Sep 2015 05:41:51 +0000 (05:41 +0000)
port ccbb4ff00f1d8f32fd9227cd7aba1723791e5744 (r30629)

original commit message:

    The new Call and CallFunction builtins supersede the current
    CallFunctionStub (and CallIC magic) and will be the single bottleneck
    for all calling, including the currently special Function.prototype.call
    and Function.prototype.apply builtins, which had handwritten (and
    not fully compliant) versions of CallFunctionStub, and also the
    CallIC(s), which where also slightly different.

    This also reduces the overhead for API function calls, which is still
    unnecessary high, but let's do that step-by-step.

    This also fixes a bunch of cases where the implicit ToObject for
    sloppy receivers was done in the wrong context (in the caller
    context instead of the callee context), which basically meant
    that we allowed cross context access to %ObjectPrototype%.

BUG=

Review URL: https://codereview.chromium.org/1332703002

Cr-Commit-Position: refs/heads/master@{#30668}

src/x87/assembler-x87.cc
src/x87/assembler-x87.h
src/x87/builtins-x87.cc
src/x87/code-stubs-x87.cc
src/x87/macro-assembler-x87.cc
src/x87/macro-assembler-x87.h

index 5b31e38437e72ecf80dd36281560c8436f74d086..323d2434f6750471e96db2b1180fe0a33d7a892f 100644 (file)
@@ -1458,12 +1458,12 @@ void Assembler::j(Condition cc, byte* entry, RelocInfo::Mode rmode) {
 }
 
 
-void Assembler::j(Condition cc, Handle<Code> code) {
+void Assembler::j(Condition cc, Handle<Code> code, RelocInfo::Mode rmode) {
   EnsureSpace ensure_space(this);
   // 0000 1111 1000 tttn #32-bit disp
   EMIT(0x0F);
   EMIT(0x80 | cc);
-  emit(code, RelocInfo::CODE_TARGET);
+  emit(code, rmode);
 }
 
 
index 6d631785d689f28cfc34de27b5afc40a78e96ecb..1f454bcd9023275bc8a8e0e7ccaeaf10a119b33b 100644 (file)
@@ -842,7 +842,8 @@ class Assembler : public AssemblerBase {
          Label* L,
          Label::Distance distance = Label::kFar);
   void j(Condition cc, byte* entry, RelocInfo::Mode rmode);
-  void j(Condition cc, Handle<Code> code);
+  void j(Condition cc, Handle<Code> code,
+         RelocInfo::Mode rmode = RelocInfo::CODE_TARGET);
 
   // Floating-point operations
   void fld(int i);
index 74d71c00f9c0e1e080c6da9c1ce9079dfb39383f..f955c811d8c7c7a0e95e53717535b590a9df0a24 100644 (file)
@@ -956,161 +956,50 @@ void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) {
 }
 
 
+// static
 void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
-  Factory* factory = masm->isolate()->factory();
-
+  // Stack Layout:
+  // esp[0]           : Return address
+  // esp[8]           : Argument n
+  // esp[16]          : Argument n-1
+  //  ...
+  // esp[8 * n]       : Argument 1
+  // esp[8 * (n + 1)] : Receiver (callable to call)
+  //
+  // eax contains the number of arguments, n, not counting the receiver.
+  //
   // 1. Make sure we have at least one argument.
-  { Label done;
+  {
+    Label done;
     __ test(eax, eax);
-    __ j(not_zero, &done);
-    __ pop(ebx);
-    __ push(Immediate(factory->undefined_value()));
-    __ push(ebx);
+    __ j(not_zero, &done, Label::kNear);
+    __ PopReturnAddressTo(ebx);
+    __ PushRoot(Heap::kUndefinedValueRootIndex);
+    __ PushReturnAddressFrom(ebx);
     __ inc(eax);
     __ bind(&done);
   }
 
-  // 2. Get the function to call (passed as receiver) from the stack, check
-  //    if it is a function.
-  Label slow, non_function;
-  // 1 ~ return address.
-  __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
-  __ JumpIfSmi(edi, &non_function);
-  __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
-  __ j(not_equal, &slow);
-
-
-  // 3a. Patch the first argument if necessary when calling a function.
-  Label shift_arguments;
-  __ Move(edx, Immediate(0));  // indicate regular JS_FUNCTION
-  { Label convert_to_object, use_global_proxy, patch_receiver;
-    // Change context eagerly in case we need the global receiver.
-    __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
-
-    // Do not transform the receiver for strict mode functions.
-    __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
-    __ test_b(FieldOperand(ebx, SharedFunctionInfo::kStrictModeByteOffset),
-              1 << SharedFunctionInfo::kStrictModeBitWithinByte);
-    __ j(not_equal, &shift_arguments);
-
-    // Do not transform the receiver for natives (shared already in ebx).
-    __ test_b(FieldOperand(ebx, SharedFunctionInfo::kNativeByteOffset),
-              1 << SharedFunctionInfo::kNativeBitWithinByte);
-    __ j(not_equal, &shift_arguments);
-
-    // Compute the receiver in sloppy mode.
-    __ mov(ebx, Operand(esp, eax, times_4, 0));  // First argument.
-
-    // Call ToObject on the receiver if it is not an object, or use the
-    // global object if it is null or undefined.
-    __ JumpIfSmi(ebx, &convert_to_object);
-    __ cmp(ebx, factory->null_value());
-    __ j(equal, &use_global_proxy);
-    __ cmp(ebx, factory->undefined_value());
-    __ j(equal, &use_global_proxy);
-    STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
-    __ CmpObjectType(ebx, FIRST_SPEC_OBJECT_TYPE, ecx);
-    __ j(above_equal, &shift_arguments);
-
-    __ bind(&convert_to_object);
-
-    { // In order to preserve argument count.
-      FrameScope scope(masm, StackFrame::INTERNAL);
-      __ SmiTag(eax);
-      __ push(eax);
-
-      __ mov(eax, ebx);
-      ToObjectStub stub(masm->isolate());
-      __ CallStub(&stub);
-      __ mov(ebx, eax);
-      __ Move(edx, Immediate(0));  // restore
-
-      __ pop(eax);
-      __ SmiUntag(eax);
-    }
-
-    // Restore the function to edi.
-    __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
-    __ jmp(&patch_receiver);
-
-    __ bind(&use_global_proxy);
-    __ mov(ebx,
-           Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
-    __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalProxyOffset));
-
-    __ bind(&patch_receiver);
-    __ mov(Operand(esp, eax, times_4, 0), ebx);
-
-    __ jmp(&shift_arguments);
-  }
-
-  // 3b. Check for function proxy.
-  __ bind(&slow);
-  __ Move(edx, Immediate(1));  // indicate function proxy
-  __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
-  __ j(equal, &shift_arguments);
-  __ bind(&non_function);
-  __ Move(edx, Immediate(2));  // indicate non-function
-
-  // 3c. 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.
-  __ mov(Operand(esp, eax, times_4, 0), edi);
+  // 2. Get the callable to call (passed as receiver) from the stack.
+  __ mov(edi, Operand(esp, eax, times_pointer_size, kPointerSize));
 
-  // 4. Shift arguments and return address one slot down on the stack
+  // 3. 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;
+  {
+    Label loop;
     __ mov(ecx, eax);
     __ bind(&loop);
-    __ mov(ebx, Operand(esp, ecx, times_4, 0));
-    __ mov(Operand(esp, ecx, times_4, kPointerSize), ebx);
+    __ mov(ebx, Operand(esp, ecx, times_pointer_size, 0));
+    __ mov(Operand(esp, ecx, times_pointer_size, kPointerSize), ebx);
     __ dec(ecx);
     __ j(not_sign, &loop);  // While non-negative (to copy return address).
-    __ pop(ebx);  // Discard copy of return address.
+    __ pop(ebx);            // Discard copy of return address.
     __ dec(eax);  // One fewer argument (first argument is new receiver).
   }
 
-  // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin,
-  //     or a function proxy via CALL_FUNCTION_PROXY.
-  { Label function, non_proxy;
-    __ test(edx, edx);
-    __ j(zero, &function);
-    __ Move(ebx, Immediate(0));
-    __ cmp(edx, Immediate(1));
-    __ j(not_equal, &non_proxy);
-
-    __ pop(edx);   // return address
-    __ push(edi);  // re-add proxy object as additional argument
-    __ push(edx);
-    __ inc(eax);
-    __ GetBuiltinEntry(edx, Context::CALL_FUNCTION_PROXY_BUILTIN_INDEX);
-    __ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
-           RelocInfo::CODE_TARGET);
-
-    __ bind(&non_proxy);
-    __ GetBuiltinEntry(edx, Context::CALL_NON_FUNCTION_BUILTIN_INDEX);
-    __ jmp(masm->isolate()->builtins()->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.
-  __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
-  __ mov(ebx,
-         FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
-  __ mov(edx, FieldOperand(edi, JSFunction::kCodeEntryOffset));
-  __ SmiUntag(ebx);
-  __ cmp(eax, ebx);
-  __ j(not_equal,
-       masm->isolate()->builtins()->ArgumentsAdaptorTrampoline());
-
-  ParameterCount expected(0);
-  __ InvokeCode(edx, expected, expected, JUMP_FUNCTION, NullCallWrapper());
+  // 4. Call the callable.
+  __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
 }
 
 
@@ -1185,7 +1074,7 @@ static void Generate_ApplyHelper(MacroAssembler* masm, bool targetIsArgument) {
     static const int kReceiverOffset = kArgumentsOffset + kPointerSize;
     static const int kFunctionOffset = kReceiverOffset + kPointerSize;
 
-    __ push(Operand(ebp, kFunctionOffset));  // push this
+    __ push(Operand(ebp, kFunctionOffset));   // push this
     __ push(Operand(ebp, kArgumentsOffset));  // push arguments
     if (targetIsArgument) {
       __ InvokeBuiltin(Context::REFLECT_APPLY_PREPARE_BUILTIN_INDEX,
@@ -1200,87 +1089,18 @@ static void Generate_ApplyHelper(MacroAssembler* masm, bool targetIsArgument) {
     const int kLimitOffset =
         StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize;
     const int kIndexOffset = kLimitOffset - 1 * kPointerSize;
-    __ push(eax);  // limit
-    __ push(Immediate(0));  // index
-
-    // Get the receiver.
-    __ mov(ebx, Operand(ebp, kReceiverOffset));
-
-    // Check that the function is a JS function (otherwise it must be a proxy).
-    Label push_receiver, use_global_proxy;
-    __ mov(edi, Operand(ebp, kFunctionOffset));
-    __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
-    __ j(not_equal, &push_receiver);
-
-    // Change context eagerly to get the right global object if necessary.
-    __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
-
-    // Compute the receiver.
-    // Do not transform the receiver for strict mode functions.
-    Label call_to_object;
-    __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
-    __ test_b(FieldOperand(ecx, SharedFunctionInfo::kStrictModeByteOffset),
-              1 << SharedFunctionInfo::kStrictModeBitWithinByte);
-    __ j(not_equal, &push_receiver);
-
-    Factory* factory = masm->isolate()->factory();
-
-    // Do not transform the receiver for natives (shared already in ecx).
-    __ test_b(FieldOperand(ecx, SharedFunctionInfo::kNativeByteOffset),
-              1 << SharedFunctionInfo::kNativeBitWithinByte);
-    __ j(not_equal, &push_receiver);
-
-    // Compute the receiver in sloppy mode.
-    // Call ToObject on the receiver if it is not an object, or use the
-    // global object if it is null or undefined.
-    __ JumpIfSmi(ebx, &call_to_object);
-    __ cmp(ebx, factory->null_value());
-    __ j(equal, &use_global_proxy);
-    __ cmp(ebx, factory->undefined_value());
-    __ j(equal, &use_global_proxy);
-    STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
-    __ CmpObjectType(ebx, FIRST_SPEC_OBJECT_TYPE, ecx);
-    __ j(above_equal, &push_receiver);
-
-    __ bind(&call_to_object);
-    __ mov(eax, ebx);
-    ToObjectStub stub(masm->isolate());
-    __ CallStub(&stub);
-    __ mov(ebx, eax);
-    __ jmp(&push_receiver);
-
-    __ bind(&use_global_proxy);
-    __ mov(ebx,
-           Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
-    __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalProxyOffset));
-
-    // Push the receiver.
-    __ bind(&push_receiver);
-    __ push(ebx);
+    __ Push(eax);                            // limit
+    __ Push(Immediate(0));                   // index
+    __ Push(Operand(ebp, kReceiverOffset));  // receiver
 
     // Loop over the arguments array, pushing each value to the stack
-    Generate_PushAppliedArguments(
-        masm, kArgumentsOffset, kIndexOffset, kLimitOffset);
+    Generate_PushAppliedArguments(masm, kArgumentsOffset, kIndexOffset,
+                                  kLimitOffset);
 
-    // Call the function.
-    Label call_proxy;
-    ParameterCount actual(eax);
+    // Call the callable.
+    // TODO(bmeurer): This should be a tail call according to ES6.
     __ mov(edi, Operand(ebp, kFunctionOffset));
-    __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
-    __ j(not_equal, &call_proxy);
-    __ InvokeFunction(edi, actual, CALL_FUNCTION, NullCallWrapper());
-
-    frame_scope.GenerateLeaveFrame();
-    __ ret(kStackSize * kPointerSize);  // remove this, receiver, and arguments
-
-    // Call the function proxy.
-    __ bind(&call_proxy);
-    __ push(edi);  // add function proxy as last argument
-    __ inc(eax);
-    __ Move(ebx, Immediate(0));
-    __ GetBuiltinEntry(edx, Context::CALL_FUNCTION_PROXY_BUILTIN_INDEX);
-    __ call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
-            RelocInfo::CODE_TARGET);
+    __ Call(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
 
     // Leave internal frame.
   }
@@ -1617,6 +1437,146 @@ static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
 }
 
 
+// static
+void Builtins::Generate_CallFunction(MacroAssembler* masm) {
+  // ----------- S t a t e -------------
+  //  -- eax : the number of arguments (not including the receiver)
+  //  -- edi : the function to call (checked to be a JSFunction)
+  // -----------------------------------
+
+  Label convert, convert_global_proxy, convert_to_object, done_convert;
+  __ AssertFunction(edi);
+  // TODO(bmeurer): Throw a TypeError if function's [[FunctionKind]] internal
+  // slot is "classConstructor".
+  // Enter the context of the function; ToObject has to run in the function
+  // context, and we also need to take the global proxy from the function
+  // context in case of conversion.
+  // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList)
+  STATIC_ASSERT(SharedFunctionInfo::kNativeByteOffset ==
+                SharedFunctionInfo::kStrictModeByteOffset);
+  __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+  __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+  // We need to convert the receiver for non-native sloppy mode functions.
+  __ test_b(FieldOperand(edx, SharedFunctionInfo::kNativeByteOffset),
+            (1 << SharedFunctionInfo::kNativeBitWithinByte) |
+                (1 << SharedFunctionInfo::kStrictModeBitWithinByte));
+  __ j(not_zero, &done_convert);
+  {
+    __ mov(ecx, Operand(esp, eax, times_pointer_size, kPointerSize));
+
+    // ----------- S t a t e -------------
+    //  -- eax : the number of arguments (not including the receiver)
+    //  -- ecx : the receiver
+    //  -- edx : the shared function info.
+    //  -- edi : the function to call (checked to be a JSFunction)
+    //  -- esi : the function context.
+    // -----------------------------------
+
+    Label convert_receiver;
+    __ JumpIfSmi(ecx, &convert_to_object, Label::kNear);
+    STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
+    __ CmpObjectType(ecx, FIRST_JS_RECEIVER_TYPE, ebx);
+    __ j(above_equal, &done_convert);
+    __ JumpIfRoot(ecx, Heap::kUndefinedValueRootIndex, &convert_global_proxy,
+                  Label::kNear);
+    __ JumpIfNotRoot(ecx, Heap::kNullValueRootIndex, &convert_to_object,
+                     Label::kNear);
+    __ bind(&convert_global_proxy);
+    {
+      // Patch receiver to global proxy.
+      __ LoadGlobalProxy(ecx);
+    }
+    __ jmp(&convert_receiver);
+    __ bind(&convert_to_object);
+    {
+      // Convert receiver using ToObject.
+      // TODO(bmeurer): Inline the allocation here to avoid building the frame
+      // in the fast case? (fall back to AllocateInNewSpace?)
+      FrameScope scope(masm, StackFrame::INTERNAL);
+      __ SmiTag(eax);
+      __ Push(eax);
+      __ Push(edi);
+      __ mov(eax, ecx);
+      ToObjectStub stub(masm->isolate());
+      __ CallStub(&stub);
+      __ mov(ecx, eax);
+      __ Pop(edi);
+      __ Pop(eax);
+      __ SmiUntag(eax);
+    }
+    __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+    __ bind(&convert_receiver);
+    __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), ecx);
+  }
+  __ bind(&done_convert);
+
+  // ----------- S t a t e -------------
+  //  -- eax : the number of arguments (not including the receiver)
+  //  -- edx : the shared function info.
+  //  -- edi : the function to call (checked to be a JSFunction)
+  //  -- esi : the function context.
+  // -----------------------------------
+
+  __ mov(ebx,
+         FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
+  __ SmiUntag(ebx);
+  ParameterCount actual(eax);
+  ParameterCount expected(ebx);
+  __ InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset), expected,
+                actual, JUMP_FUNCTION, NullCallWrapper());
+}
+
+
+// static
+void Builtins::Generate_Call(MacroAssembler* masm) {
+  // ----------- S t a t e -------------
+  //  -- eax : the number of arguments (not including the receiver)
+  //  -- edi : the target to call (can be any Object).
+  // -----------------------------------
+
+  Label non_smi, non_function;
+  __ JumpIfSmi(edi, &non_function);
+  __ bind(&non_smi);
+  __ CmpObjectType(edi, JS_FUNCTION_TYPE, edx);
+  __ j(equal, masm->isolate()->builtins()->CallFunction(),
+       RelocInfo::CODE_TARGET);
+  __ CmpInstanceType(edx, JS_FUNCTION_PROXY_TYPE);
+  __ j(not_equal, &non_function);
+
+  // 1. Call to function proxy.
+  // TODO(neis): This doesn't match the ES6 spec for [[Call]] on proxies.
+  __ mov(edi, FieldOperand(edi, JSFunctionProxy::kCallTrapOffset));
+  __ AssertNotSmi(edi);
+  __ jmp(&non_smi);
+
+  // 2. Call to something else, which might have a [[Call]] internal method (if
+  // not we raise an exception).
+  __ bind(&non_function);
+  // TODO(bmeurer): I wonder why we prefer to have slow API calls? This could
+  // be awesome instead; i.e. a trivial improvement would be to call into the
+  // runtime and just deal with the API function there instead of returning a
+  // delegate from a runtime call that just jumps back to the runtime once
+  // called. Or, bonus points, call directly into the C API function here, as
+  // we do in some Crankshaft fast cases.
+  // Overwrite the original receiver with the (original) target.
+  __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi);
+  {
+    // Determine the delegate for the target (if any).
+    FrameScope scope(masm, StackFrame::INTERNAL);
+    __ SmiTag(eax);
+    __ Push(eax);
+    __ Push(edi);
+    __ CallRuntime(Runtime::kGetFunctionDelegate, 1);
+    __ mov(edi, eax);
+    __ Pop(eax);
+    __ SmiUntag(eax);
+  }
+  // The delegate is always a regular function.
+  __ AssertFunction(edi);
+  __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET);
+}
+
+
 void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
   // ----------- S t a t e -------------
   //  -- eax : actual number of arguments
index 897d4fa2e01124c555849b8b27c88956b934a859..77d6e9989a9d127f8df99cc0b3bef111cfdfccbe 100644 (file)
@@ -1746,33 +1746,9 @@ static void EmitContinueIfStrictOrNative(MacroAssembler* masm, Label* cont) {
 }
 
 
-static void EmitSlowCase(Isolate* isolate,
-                         MacroAssembler* masm,
-                         int argc,
-                         Label* non_function) {
-  // Check for function proxy.
-  __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
-  __ j(not_equal, non_function);
-  __ pop(ecx);
-  __ push(edi);  // put proxy as additional argument under return address
-  __ push(ecx);
-  __ Move(eax, Immediate(argc + 1));
-  __ Move(ebx, Immediate(0));
-  __ GetBuiltinEntry(edx, Context::CALL_FUNCTION_PROXY_BUILTIN_INDEX);
-  {
-    Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
-    __ jmp(adaptor, RelocInfo::CODE_TARGET);
-  }
-
-  // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
-  // of the original receiver from the call site).
-  __ bind(non_function);
-  __ mov(Operand(esp, (argc + 1) * kPointerSize), edi);
-  __ Move(eax, Immediate(argc));
-  __ Move(ebx, Immediate(0));
-  __ GetBuiltinEntry(edx, Context::CALL_NON_FUNCTION_BUILTIN_INDEX);
-  Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
-  __ jmp(adaptor, RelocInfo::CODE_TARGET);
+static void EmitSlowCase(Isolate* isolate, MacroAssembler* masm, int argc) {
+  __ Set(eax, argc);
+  __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
 }
 
 
@@ -1793,11 +1769,11 @@ static void CallFunctionNoFeedback(MacroAssembler* masm,
                                    int argc, bool needs_checks,
                                    bool call_as_method) {
   // edi : the function to call
-  Label slow, non_function, wrap, cont;
+  Label slow, wrap, cont;
 
   if (needs_checks) {
     // Check that the function really is a JavaScript function.
-    __ JumpIfSmi(edi, &non_function);
+    __ JumpIfSmi(edi, &slow);
 
     // Goto slow case if we do not have a function.
     __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
@@ -1832,8 +1808,7 @@ static void CallFunctionNoFeedback(MacroAssembler* masm,
   if (needs_checks) {
     // Slow-case: Non-function called.
     __ bind(&slow);
-    // (non_function is bound in EmitSlowCase)
-    EmitSlowCase(masm->isolate(), masm, argc, &non_function);
+    EmitSlowCase(masm->isolate(), masm, argc);
   }
 
   if (call_as_method) {
@@ -1977,13 +1952,8 @@ void CallIC_ArrayStub::Generate(MacroAssembler* masm) {
   GenerateMiss(masm);
 
   // The slow case, we need this no matter what to complete a call after a miss.
-  CallFunctionNoFeedback(masm,
-                         arg_count(),
-                         true,
-                         CallAsMethod());
-
-  // Unreachable.
-  __ int3();
+  __ Set(eax, arg_count());
+  __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
 }
 
 
@@ -1997,7 +1967,7 @@ void CallICStub::Generate(MacroAssembler* masm) {
   const int generic_offset =
       FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex);
   Label extra_checks_or_miss, slow_start;
-  Label slow, non_function, wrap, cont;
+  Label slow, wrap, cont;
   Label have_js_function;
   int argc = arg_count();
   ParameterCount actual(argc);
@@ -2050,7 +2020,7 @@ void CallICStub::Generate(MacroAssembler* masm) {
   __ InvokeFunction(edi, actual, JUMP_FUNCTION, NullCallWrapper());
 
   __ bind(&slow);
-  EmitSlowCase(isolate, masm, argc, &non_function);
+  EmitSlowCase(isolate, masm, argc);
 
   if (CallAsMethod()) {
     __ bind(&wrap);
@@ -2131,7 +2101,7 @@ void CallICStub::Generate(MacroAssembler* masm) {
   __ bind(&slow_start);
 
   // Check that the function really is a JavaScript function.
-  __ JumpIfSmi(edi, &non_function);
+  __ JumpIfSmi(edi, &slow);
 
   // Goto slow case if we do not have a function.
   __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
index d2b1742623195775e182b76cfa1ead3ffde8f462..090fc6ec19fbd076aa3cc22e93a33710202d6a05 100644 (file)
@@ -116,6 +116,12 @@ void MacroAssembler::CompareRoot(const Operand& with,
 }
 
 
+void MacroAssembler::PushRoot(Heap::RootListIndex index) {
+  DCHECK(isolate()->heap()->RootCanBeTreatedAsConstant(index));
+  Push(isolate()->heap()->root_handle(index));
+}
+
+
 void MacroAssembler::InNewSpace(
     Register object,
     Register scratch,
@@ -776,6 +782,18 @@ void MacroAssembler::AssertName(Register object) {
 }
 
 
+void MacroAssembler::AssertFunction(Register object) {
+  if (emit_debug_code()) {
+    test(object, Immediate(kSmiTagMask));
+    Check(not_equal, kOperandIsASmiAndNotAFunction);
+    Push(object);
+    CmpObjectType(object, JS_FUNCTION_TYPE, object);
+    Pop(object);
+    Check(equal, kOperandIsNotAFunction);
+  }
+}
+
+
 void MacroAssembler::AssertUndefinedOrAllocationSite(Register object) {
   if (emit_debug_code()) {
     Label done_checking;
@@ -2062,6 +2080,12 @@ void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
 }
 
 
+void MacroAssembler::LoadGlobalProxy(Register dst) {
+  mov(dst, GlobalObjectOperand());
+  mov(dst, FieldOperand(dst, GlobalObject::kGlobalProxyOffset));
+}
+
+
 void MacroAssembler::LoadTransitionedArrayMapConditional(
     ElementsKind expected_kind,
     ElementsKind transitioned_kind,
index 26a721a16f40978b4929107c09cd416307f3d57e..97dbf532dd1403d417d1cb37a8dad2a44c0aadf5 100644 (file)
@@ -71,6 +71,16 @@ class MacroAssembler: public Assembler {
   void Load(Register dst, const Operand& src, Representation r);
   void Store(Register src, const Operand& dst, Representation r);
 
+  // Load a register with a long value as efficiently as possible.
+  void Set(Register dst, int32_t x) {
+    if (x == 0) {
+      xor_(dst, dst);
+    } else {
+      mov(dst, Immediate(x));
+    }
+  }
+  void Set(const Operand& dst, int32_t x) { mov(dst, Immediate(x)); }
+
   // Operations on roots in the root-array.
   void LoadRoot(Register destination, Heap::RootListIndex index);
   void StoreRoot(Register source, Register scratch, Heap::RootListIndex index);
@@ -79,6 +89,22 @@ class MacroAssembler: public Assembler {
   // and not in new space).
   void CompareRoot(Register with, Heap::RootListIndex index);
   void CompareRoot(const Operand& with, Heap::RootListIndex index);
+  void PushRoot(Heap::RootListIndex index);
+
+  // Compare the object in a register to a value and jump if they are equal.
+  void JumpIfRoot(Register with, Heap::RootListIndex index, Label* if_equal,
+                  Label::Distance if_equal_distance = Label::kNear) {
+    CompareRoot(with, index);
+    j(equal, if_equal, if_equal_distance);
+  }
+
+  // Compare the object in a register to a value and jump if they are not equal.
+  void JumpIfNotRoot(Register with, Heap::RootListIndex index,
+                     Label* if_not_equal,
+                     Label::Distance if_not_equal_distance = Label::kNear) {
+    CompareRoot(with, index);
+    j(not_equal, if_not_equal, if_not_equal_distance);
+  }
 
   // ---------------------------------------------------------------------------
   // GC Support
@@ -240,6 +266,9 @@ class MacroAssembler: public Assembler {
   // Find the function context up the context chain.
   void LoadContext(Register dst, int context_chain_length);
 
+  // Load the global proxy from the current context.
+  void LoadGlobalProxy(Register dst);
+
   // Conditionally load the cached Array transitioned map of type
   // transitioned_kind from the native context if the map in register
   // map_in_out is the cached Array map in the native context of
@@ -530,6 +559,9 @@ class MacroAssembler: public Assembler {
   // Abort execution if argument is not a name, enabled via --debug-code.
   void AssertName(Register object);
 
+  // Abort execution if argument is not a JSFunction, enabled via --debug-code.
+  void AssertFunction(Register object);
+
   // Abort execution if argument is not undefined or an AllocationSite, enabled
   // via --debug-code.
   void AssertUndefinedOrAllocationSite(Register object);
@@ -766,8 +798,14 @@ class MacroAssembler: public Assembler {
   void Drop(int element_count);
 
   void Call(Label* target) { call(target); }
+  void Call(Handle<Code> target, RelocInfo::Mode rmode) { call(target, rmode); }
+  void Jump(Handle<Code> target, RelocInfo::Mode rmode) { jmp(target, rmode); }
   void Push(Register src) { push(src); }
+  void Push(const Operand& src) { push(src); }
+  void Push(Immediate value) { push(value); }
   void Pop(Register dst) { pop(dst); }
+  void PushReturnAddressFrom(Register src) { push(src); }
+  void PopReturnAddressTo(Register dst) { pop(dst); }
 
   void Lzcnt(Register dst, Register src) { Lzcnt(dst, Operand(src)); }
   void Lzcnt(Register dst, const Operand& src);