X87: [builtin] Refactor Invoke to deal with any kind of callable.
authorchunyang.dai <chunyang.dai@intel.com>
Thu, 24 Sep 2015 08:58:19 +0000 (01:58 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 24 Sep 2015 08:58:40 +0000 (08:58 +0000)
port 634d1d86d8e98584097c6cb548a68ecf705c44f7 (r30874).

original commit message:

    Now both Execution::Call and Execution::New can deal with any
    kind of target and will raise a proper exception if the target is not
    callable (which is not yet spec compliant for New, as we would
    have to check IsConstructor instead, which we don't have yet).

    Now we no longer need to do any of these weird call/construct
    delegate gymnastics in C++, and we finally have a single true
    bottleneck for Call/Construct abstract operations in the code
    base, with only a few special handlings left in the compilers to
    optimize the JSFunction case.

BUG=

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

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

src/x87/builtins-x87.cc

index 8df1254..f2b42be 100644 (file)
@@ -1516,13 +1516,13 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
   //  -- edi : the target to call (can be any Object).
   // -----------------------------------
 
-  Label non_smi, non_function;
-  __ JumpIfSmi(edi, &non_function);
+  Label non_callable, non_function, non_smi;
+  __ JumpIfSmi(edi, &non_callable);
   __ bind(&non_smi);
-  __ CmpObjectType(edi, JS_FUNCTION_TYPE, edx);
+  __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
   __ j(equal, masm->isolate()->builtins()->CallFunction(),
        RelocInfo::CODE_TARGET);
-  __ CmpInstanceType(edx, JS_FUNCTION_PROXY_TYPE);
+  __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
   __ j(not_equal, &non_function);
 
   // 1. Call to function proxy.
@@ -1534,28 +1534,22 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
   // 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.
+  // Check if target has a [[Call]] internal method.
+  __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), 1 << Map::kIsCallable);
+  __ j(zero, &non_callable, Label::kNear);
   // Overwrite the original receiver with the (original) target.
   __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi);
+  // Let the "call_as_function_delegate" take care of the rest.
+  __ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, edi);
+  __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET);
+
+  // 3. Call to something that is not callable.
+  __ bind(&non_callable);
   {
-    // 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);
+    __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
   }
-  // The delegate is always a regular function.
-  __ AssertFunction(edi);
-  __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET);
 }
 
 
@@ -1591,33 +1585,39 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
   //  -- edi : the constructor to call (can be any Object)
   // -----------------------------------
 
-  Label slow;
-  __ JumpIfSmi(edi, &slow, Label::kNear);
+  Label non_callable, non_function;
+  __ JumpIfSmi(edi, &non_callable);
   __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
   __ j(equal, masm->isolate()->builtins()->ConstructFunction(),
        RelocInfo::CODE_TARGET);
   __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
-  __ j(not_equal, &slow, Label::kNear);
+  __ j(not_equal, &non_function, Label::kNear);
 
+  // 1. Construct of function proxy.
   // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
   __ mov(edi, FieldOperand(edi, JSFunctionProxy::kConstructTrapOffset));
   __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
 
-  __ bind(&slow);
+  // 2. Construct of something else, which might have a [[Construct]] internal
+  // method (if not we raise an exception).
+  __ bind(&non_function);
+  // Check if target has a [[Call]] internal method.
+  // TODO(bmeurer): This shoud use IsConstructor once available.
+  __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), 1 << Map::kIsCallable);
+  __ j(zero, &non_callable, Label::kNear);
+  // Overwrite the original receiver with the (original) target.
+  __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi);
+  // Let the "call_as_constructor_delegate" take care of the rest.
+  __ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, edi);
+  __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET);
+
+  // 3. Construct of something that is not callable.
+  __ bind(&non_callable);
   {
-    // Determine the delegate for the target (if any).
     FrameScope scope(masm, StackFrame::INTERNAL);
-    __ SmiTag(eax);
-    __ Push(eax);
     __ Push(edi);
-    __ CallRuntime(Runtime::kGetConstructorDelegate, 1);
-    __ mov(edi, eax);
-    __ Pop(eax);
-    __ SmiUntag(eax);
+    __ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
   }
-  // The delegate is always a regular function.
-  __ AssertFunction(edi);
-  __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET);
 }