ia32: Fuse map and type checks in call ICs for API functions.
authorvitalyr@chromium.org <vitalyr@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 9 Feb 2010 16:14:14 +0000 (16:14 +0000)
committervitalyr@chromium.org <vitalyr@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 9 Feb 2010 16:14:14 +0000 (16:14 +0000)
This uses the fact that if a map stayed the same then the object
still passes the type check. A new builtin is added to handle the
API call in this case.

Review URL: http://codereview.chromium.org/573003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3825 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

13 files changed:
src/arm/stub-cache-arm.cc
src/builtins.cc
src/builtins.h
src/ia32/macro-assembler-ia32.cc
src/ia32/macro-assembler-ia32.h
src/ia32/stub-cache-ia32.cc
src/macro-assembler.h
src/stub-cache.h
src/top.cc
src/top.h
src/v8-counters.h
src/x64/stub-cache-x64.cc
test/cctest/test-api.cc

index 44a754e..521ecc6 100644 (file)
@@ -737,7 +737,11 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
                                        Register holder_reg,
                                        Register scratch,
                                        String* name,
+                                       int save_at_depth,
                                        Label* miss) {
+  // TODO(602): support object saving.
+  ASSERT(save_at_depth == kInvalidProtoDepth);
+
   // Check that the maps haven't changed.
   Register result =
       masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, miss);
index a49fd60..18afe91 100644 (file)
@@ -474,6 +474,76 @@ BUILTIN(HandleApiCallConstruct) {
 }
 
 
+#ifdef DEBUG
+
+static void VerifyTypeCheck(Handle<JSObject> object,
+                            Handle<JSFunction> function) {
+  FunctionTemplateInfo* info =
+      FunctionTemplateInfo::cast(function->shared()->function_data());
+  if (info->signature()->IsUndefined()) return;
+  SignatureInfo* signature = SignatureInfo::cast(info->signature());
+  Object* receiver_type = signature->receiver();
+  if (receiver_type->IsUndefined()) return;
+  FunctionTemplateInfo* type = FunctionTemplateInfo::cast(receiver_type);
+  ASSERT(object->IsInstanceOf(type));
+}
+
+#endif
+
+
+BUILTIN(FastHandleApiCall) {
+  ASSERT(!CalledAsConstructor());
+  const bool is_construct = false;
+
+  // We expect four more arguments: function, callback, call data, and holder.
+  const int args_length = args.length() - 4;
+  ASSERT(args_length >= 0);
+
+  Handle<JSFunction> function = args.at<JSFunction>(args_length);
+  Object* callback_obj = args[args_length + 1];
+  Handle<Object> data_handle = args.at<Object>(args_length + 2);
+  Handle<JSObject> checked_holder = args.at<JSObject>(args_length + 3);
+
+#ifdef DEBUG
+  VerifyTypeCheck(checked_holder, function);
+#endif
+
+  v8::Local<v8::Object> holder = v8::Utils::ToLocal(checked_holder);
+  v8::Local<v8::Function> callee = v8::Utils::ToLocal(function);
+  v8::InvocationCallback callback =
+      v8::ToCData<v8::InvocationCallback>(callback_obj);
+  v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle);
+
+  v8::Arguments new_args = v8::ImplementationUtilities::NewArguments(
+      data,
+      holder,
+      callee,
+      is_construct,
+      reinterpret_cast<void**>(&args[0] - 1),
+      args_length - 1);
+
+  HandleScope scope;
+  Object* result;
+  v8::Handle<v8::Value> value;
+  {
+    // Leaving JavaScript.
+    VMState state(EXTERNAL);
+#ifdef ENABLE_LOGGING_AND_PROFILING
+    state.set_external_callback(v8::ToCData<Address>(callback_obj));
+#endif
+    value = callback(new_args);
+  }
+  if (value.IsEmpty()) {
+    result = Heap::undefined_value();
+  } else {
+    result = *reinterpret_cast<Object**>(*value);
+  }
+
+  RETURN_IF_SCHEDULED_EXCEPTION();
+  return result;
+}
+
+
 // Helper function to handle calls to non-function objects created through the
 // API. The object can be called as either a constructor (using new) or just as
 // a function (without new).
index 655478e..d7b3e2b 100644 (file)
@@ -50,6 +50,7 @@ enum BuiltinExtraArguments {
   V(ArrayPop, NO_EXTRA_ARGUMENTS)                                   \
                                                                     \
   V(HandleApiCall, NEEDS_CALLED_FUNCTION)                           \
+  V(FastHandleApiCall, NO_EXTRA_ARGUMENTS)                          \
   V(HandleApiCallConstruct, NEEDS_CALLED_FUNCTION)                  \
   V(HandleApiCallAsFunction, NO_EXTRA_ARGUMENTS)                    \
   V(HandleApiCallAsConstructor, NO_EXTRA_ARGUMENTS)
index 97457f8..7cb9eef 100644 (file)
@@ -554,6 +554,7 @@ void MacroAssembler::PopTryHandler() {
 Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
                                    JSObject* holder, Register holder_reg,
                                    Register scratch,
+                                   int save_at_depth,
                                    Label* miss) {
   // Make sure there's no overlap between scratch and the other
   // registers.
@@ -561,7 +562,11 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
 
   // Keep track of the current object in register reg.
   Register reg = object_reg;
-  int depth = 1;
+  int depth = 0;
+
+  if (save_at_depth == depth) {
+    mov(Operand(esp, kPointerSize), object_reg);
+  }
 
   // Check the maps in the prototype chain.
   // Traverse the prototype chain from the object and do map checks.
@@ -593,7 +598,6 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
       // to it in the code. Load it from the map.
       reg = holder_reg;  // from now the object is in holder_reg
       mov(reg, FieldOperand(scratch, Map::kPrototypeOffset));
-
     } else {
       // Check the map of the current object.
       cmp(FieldOperand(reg, HeapObject::kMapOffset),
@@ -611,6 +615,10 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
       mov(reg, Handle<JSObject>(prototype));
     }
 
+    if (save_at_depth == depth) {
+      mov(Operand(esp, kPointerSize), reg);
+    }
+
     // Go to the next object in the prototype chain.
     object = prototype;
   }
@@ -621,7 +629,7 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
   j(not_equal, miss, not_taken);
 
   // Log the check depth.
-  LOG(IntEvent("check-maps-depth", depth));
+  LOG(IntEvent("check-maps-depth", depth + 1));
 
   // Perform security check for access to the global object and return
   // the holder register.
index d104b54..f0abb02 100644 (file)
@@ -194,9 +194,14 @@ class MacroAssembler: public Assembler {
   // clobbered if it the same as the holder register. The function
   // returns a register containing the holder - either object_reg or
   // holder_reg.
+  // The function can optionally (when save_at_depth !=
+  // kInvalidProtoDepth) save the object at the given depth by moving
+  // it to [esp + kPointerSize].
   Register CheckMaps(JSObject* object, Register object_reg,
                      JSObject* holder, Register holder_reg,
-                     Register scratch, Label* miss);
+                     Register scratch,
+                     int save_at_depth,
+                     Label* miss);
 
   // Generate code for checking access rights - used for security checks
   // on access to global objects across environments. The holder register
index 87a4297..efd0d41 100644 (file)
@@ -481,88 +481,353 @@ class LoadInterceptorCompiler BASE_EMBEDDED {
 };
 
 
+// Holds information about possible function call optimizations.
+class CallOptimization BASE_EMBEDDED {
+ public:
+  explicit CallOptimization(LookupResult* lookup)
+    : constant_function_(NULL),
+      is_simple_api_call_(false),
+      expected_receiver_type_(NULL),
+      api_call_info_(NULL) {
+    if (!lookup->IsValid() || !lookup->IsCacheable()) return;
+
+    // We only optimize constant function calls.
+    if (lookup->type() != CONSTANT_FUNCTION) return;
+
+    Initialize(lookup->GetConstantFunction());
+  }
+
+  explicit CallOptimization(JSFunction* function) {
+    Initialize(function);
+  }
+
+  bool is_constant_call() const {
+    return constant_function_ != NULL;
+  }
+
+  JSFunction* constant_function() const {
+    ASSERT(constant_function_ != NULL);
+    return constant_function_;
+  }
+
+  bool is_simple_api_call() const {
+    return is_simple_api_call_;
+  }
+
+  FunctionTemplateInfo* expected_receiver_type() const {
+    ASSERT(is_simple_api_call_);
+    return expected_receiver_type_;
+  }
+
+  CallHandlerInfo* api_call_info() const {
+    ASSERT(is_simple_api_call_);
+    return api_call_info_;
+  }
+
+  // Returns the depth of the object having the expected type in the
+  // prototype chain between the two arguments.
+  int GetPrototypeDepthOfExpectedType(JSObject* object,
+                                      JSObject* holder) const {
+    ASSERT(is_simple_api_call_);
+    if (expected_receiver_type_ == NULL) return 0;
+    int depth = 0;
+    while (object != holder) {
+      if (object->IsInstanceOf(expected_receiver_type_)) return depth;
+      object = JSObject::cast(object->GetPrototype());
+      ++depth;
+    }
+    if (holder->IsInstanceOf(expected_receiver_type_)) return depth;
+    return kInvalidProtoDepth;
+  }
+
+ private:
+  void Initialize(JSFunction* function) {
+    if (!function->is_compiled()) return;
+
+    constant_function_ = function;
+    is_simple_api_call_ = false;
+
+    AnalyzePossibleApiFunction(function);
+  }
+
+  // Determines whether the given function can be called using the
+  // fast api call builtin.
+  void AnalyzePossibleApiFunction(JSFunction* function) {
+    SharedFunctionInfo* sfi = function->shared();
+    if (sfi->function_data()->IsUndefined()) return;
+    FunctionTemplateInfo* info =
+        FunctionTemplateInfo::cast(sfi->function_data());
+
+    // Require a C++ callback.
+    if (info->call_code()->IsUndefined()) return;
+    api_call_info_ = CallHandlerInfo::cast(info->call_code());
+
+    // Accept signatures that either have no restrictions at all or
+    // only have restrictions on the receiver.
+    if (!info->signature()->IsUndefined()) {
+      SignatureInfo* signature = SignatureInfo::cast(info->signature());
+      if (!signature->args()->IsUndefined()) return;
+      if (!signature->receiver()->IsUndefined()) {
+        expected_receiver_type_ =
+            FunctionTemplateInfo::cast(signature->receiver());
+      }
+    }
+
+    is_simple_api_call_ = true;
+  }
+
+  JSFunction* constant_function_;
+  bool is_simple_api_call_;
+  FunctionTemplateInfo* expected_receiver_type_;
+  CallHandlerInfo* api_call_info_;
+};
+
+
+// Reserves space for the extra arguments to FastHandleApiCall in the
+// caller's frame.
+//
+// These arguments are set by CheckPrototypes and GenerateFastApiCall.
+static void ReserveSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
+  // ----------- S t a t e -------------
+  //  -- esp[0] : return address
+  //  -- esp[4] : last argument in the internal frame of the caller
+  // -----------------------------------
+  __ pop(scratch);
+  __ push(Immediate(Smi::FromInt(0)));
+  __ push(Immediate(Smi::FromInt(0)));
+  __ push(Immediate(Smi::FromInt(0)));
+  __ push(Immediate(Smi::FromInt(0)));
+  __ push(scratch);
+}
+
+
+// Undoes the effects of ReserveSpaceForFastApiCall.
+static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
+  // ----------- S t a t e -------------
+  //  -- esp[0]  : return address
+  //  -- esp[4]  : last fast api call extra argument
+  //  -- ...
+  //  -- esp[16] : first fast api call extra argument
+  //  -- esp[20] : last argument in the internal frame
+  // -----------------------------------
+  __ pop(scratch);
+  __ add(Operand(esp), Immediate(kPointerSize * 4));
+  __ push(scratch);
+}
+
+
+// Generates call to FastHandleApiCall builtin.
+static void GenerateFastApiCall(MacroAssembler* masm,
+                                const CallOptimization& optimization,
+                                int argc) {
+  // ----------- S t a t e -------------
+  //  -- esp[0]              : return address
+  //  -- esp[4]              : object passing the type check
+  //                           (last fast api call extra argument,
+  //                            set by CheckPrototypes)
+  //  -- esp[8]              : api call data
+  //  -- esp[12]             : api callback
+  //  -- esp[16]             : api function
+  //                           (first fast api call extra argument)
+  //  -- esp[20]             : last argument
+  //  -- ...
+  //  -- esp[(argc + 5) * 4] : first argument
+  //  -- esp[(argc + 6) * 4] : receiver
+  // -----------------------------------
+
+  // Get the function and setup the context.
+  JSFunction* function = optimization.constant_function();
+  __ mov(edi, Immediate(Handle<JSFunction>(function)));
+  __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+
+  // Pass the additional arguments FastHandleApiCall expects.
+  __ mov(Operand(esp, 4 * kPointerSize), edi);
+  bool info_loaded = false;
+  Object* callback = optimization.api_call_info()->callback();
+  if (Heap::InNewSpace(callback)) {
+    info_loaded = true;
+    __ mov(ecx, Handle<CallHandlerInfo>(optimization.api_call_info()));
+    __ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kCallbackOffset));
+    __ mov(Operand(esp, 3 * kPointerSize), ebx);
+  } else {
+    __ mov(Operand(esp, 3 * kPointerSize), Immediate(Handle<Object>(callback)));
+  }
+  Object* call_data = optimization.api_call_info()->data();
+  if (Heap::InNewSpace(call_data)) {
+    if (!info_loaded) {
+      __ mov(ecx, Handle<CallHandlerInfo>(optimization.api_call_info()));
+    }
+    __ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kDataOffset));
+    __ mov(Operand(esp, 2 * kPointerSize), ebx);
+  } else {
+    __ mov(Operand(esp, 2 * kPointerSize),
+           Immediate(Handle<Object>(call_data)));
+  }
+
+  // Set the number of arguments.
+  __ mov(eax, Immediate(argc + 4));
+
+  // Jump to the fast api call builtin (tail call).
+  Handle<Code> code = Handle<Code>(
+      Builtins::builtin(Builtins::FastHandleApiCall));
+  ParameterCount expected(0);
+  __ InvokeCode(code, expected, expected,
+                RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+}
+
+
 class CallInterceptorCompiler BASE_EMBEDDED {
  public:
-  CallInterceptorCompiler(const ParameterCount& arguments, Register name)
-      : arguments_(arguments), argc_(arguments.immediate()), name_(name) {}
+  CallInterceptorCompiler(StubCompiler* stub_compiler,
+                          const ParameterCount& arguments,
+                          Register name)
+      : stub_compiler_(stub_compiler),
+        arguments_(arguments),
+        argc_(arguments.immediate()),
+        name_(name) {}
+
+  void Compile(MacroAssembler* masm,
+               JSObject* object,
+               JSObject* holder,
+               String* name,
+               LookupResult* lookup,
+               Register receiver,
+               Register scratch1,
+               Register scratch2,
+               Label* miss) {
+    ASSERT(holder->HasNamedInterceptor());
+    ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined());
+
+    // Check that the receiver isn't a smi.
+    __ test(receiver, Immediate(kSmiTagMask));
+    __ j(zero, miss, not_taken);
+
+    CallOptimization optimization(lookup);
+
+    if (optimization.is_constant_call() &&
+        !Top::CanHaveSpecialFunctions(holder)) {
+      CompileCacheable(masm,
+                       object,
+                       receiver,
+                       scratch1,
+                       scratch2,
+                       holder,
+                       lookup,
+                       name,
+                       optimization,
+                       miss);
+    } else {
+      CompileRegular(masm,
+                     object,
+                     receiver,
+                     scratch1,
+                     scratch2,
+                     name,
+                     holder,
+                     miss);
+    }
+  }
 
+ private:
   void CompileCacheable(MacroAssembler* masm,
-                        StubCompiler* stub_compiler,
+                        JSObject* object,
                         Register receiver,
-                        Register holder,
                         Register scratch1,
                         Register scratch2,
                         JSObject* holder_obj,
                         LookupResult* lookup,
                         String* name,
+                        const CallOptimization& optimization,
                         Label* miss_label) {
-    JSFunction* function = 0;
-    bool optimize = false;
-    // So far the most popular case for failed interceptor is
-    // CONSTANT_FUNCTION sitting below.
-    if (lookup->type() == CONSTANT_FUNCTION) {
-      function = lookup->GetConstantFunction();
-      // JSArray holder is a special case for call constant function
-      // (see the corresponding code).
-      if (function->is_compiled() && !holder_obj->IsJSArray()) {
-        optimize = true;
+    ASSERT(optimization.is_constant_call());
+
+    int depth1 = kInvalidProtoDepth;
+    int depth2 = kInvalidProtoDepth;
+    bool can_do_fast_api_call = false;
+    if (optimization.is_simple_api_call() &&
+        !lookup->holder()->IsGlobalObject()) {
+      depth1 = optimization.GetPrototypeDepthOfExpectedType(object, holder_obj);
+      if (depth1 == kInvalidProtoDepth) {
+        depth2 = optimization.GetPrototypeDepthOfExpectedType(holder_obj,
+                                                              lookup->holder());
       }
+      can_do_fast_api_call = (depth1 != kInvalidProtoDepth) ||
+                             (depth2 != kInvalidProtoDepth);
     }
 
-    if (!optimize) {
-      CompileRegular(masm, receiver, holder, scratch2, holder_obj, miss_label);
-      return;
+    __ IncrementCounter(&Counters::call_const_interceptor, 1);
+
+    if (can_do_fast_api_call) {
+      __ IncrementCounter(&Counters::call_const_interceptor_fast_api, 1);
+      ReserveSpaceForFastApiCall(masm, scratch1);
     }
 
-    __ EnterInternalFrame();
-    __ push(holder);  // Save the holder.
-    __ push(name_);  // Save the name.
+    Label miss_cleanup;
+    Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label;
+    Register holder =
+        stub_compiler_->CheckPrototypes(object, receiver, holder_obj,
+                                        scratch1, scratch2, name,
+                                        depth1, miss);
 
-    CompileCallLoadPropertyWithInterceptor(masm,
-                                           receiver,
-                                           holder,
-                                           name_,
-                                           holder_obj);
+    Label regular_invoke;
+    LoadWithInterceptor(masm, receiver, holder, holder_obj, &regular_invoke);
 
-    __ pop(name_);  // Restore the name.
-    __ pop(receiver);  // Restore the holder.
-    __ LeaveInternalFrame();
+    // Generate code for the failed interceptor case.
+
+    // Check the lookup is still valid.
+    stub_compiler_->CheckPrototypes(holder_obj, receiver,
+                                    lookup->holder(),
+                                    scratch1, scratch2, name,
+                                    depth2, miss);
 
-    __ cmp(eax, Factory::no_interceptor_result_sentinel());
-    Label invoke;
-    __ j(not_equal, &invoke);
-
-    stub_compiler->CheckPrototypes(holder_obj, receiver,
-                                   lookup->holder(), scratch1,
-                                   scratch2,
-                                   name,
-                                   miss_label);
     if (lookup->holder()->IsGlobalObject()) {
+      ASSERT(!can_do_fast_api_call);
       __ mov(edx, Operand(esp, (argc_ + 1) * kPointerSize));
       __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
       __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edx);
     }
 
-    ASSERT(function->is_compiled());
-    // Get the function and setup the context.
-    __ mov(edi, Immediate(Handle<JSFunction>(function)));
-    __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+    if (can_do_fast_api_call) {
+      GenerateFastApiCall(masm, optimization, argc_);
+    } else {
+      // Get the function and setup the context.
+      JSFunction* function = optimization.constant_function();
+      __ mov(edi, Immediate(Handle<JSFunction>(function)));
+      __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+
+      // Jump to the cached code (tail call).
+      ASSERT(function->is_compiled());
+      Handle<Code> code(function->code());
+      ParameterCount expected(function->shared()->formal_parameter_count());
+      __ InvokeCode(code, expected, arguments_,
+                    RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+    }
 
-    // Jump to the cached code (tail call).
-    Handle<Code> code(function->code());
-    ParameterCount expected(function->shared()->formal_parameter_count());
-    __ InvokeCode(code, expected, arguments_,
-                  RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+    if (can_do_fast_api_call) {
+      __ bind(&miss_cleanup);
+      FreeSpaceForFastApiCall(masm, scratch1);
+      __ jmp(miss_label);
+    }
 
-    __ bind(&invoke);
+    __ bind(&regular_invoke);
+    if (can_do_fast_api_call) {
+      FreeSpaceForFastApiCall(masm, scratch1);
+    }
   }
 
   void CompileRegular(MacroAssembler* masm,
+                      JSObject* object,
                       Register receiver,
-                      Register holder,
-                      Register scratch,
+                      Register scratch1,
+                      Register scratch2,
+                      String* name,
                       JSObject* holder_obj,
                       Label* miss_label) {
+    Register holder =
+        stub_compiler_->CheckPrototypes(object, receiver, holder_obj,
+                                        scratch1, scratch2, name,
+                                        miss_label);
+
     __ EnterInternalFrame();
     // Save the name_ register across the call.
     __ push(name_);
@@ -586,9 +851,32 @@ class CallInterceptorCompiler BASE_EMBEDDED {
     __ LeaveInternalFrame();
   }
 
- private:
+  void LoadWithInterceptor(MacroAssembler* masm,
+                           Register receiver,
+                           Register holder,
+                           JSObject* holder_obj,
+                           Label* interceptor_succeeded) {
+    __ EnterInternalFrame();
+    __ push(holder);  // Save the holder.
+    __ push(name_);  // Save the name.
+
+    CompileCallLoadPropertyWithInterceptor(masm,
+                                           receiver,
+                                           holder,
+                                           name_,
+                                           holder_obj);
+
+    __ pop(name_);  // Restore the name.
+    __ pop(receiver);  // Restore the holder.
+    __ LeaveInternalFrame();
+
+    __ cmp(eax, Factory::no_interceptor_result_sentinel());
+    __ j(not_equal, interceptor_succeeded);
+  }
+
+  StubCompiler* stub_compiler_;
   const ParameterCount& arguments_;
-  int argc_;
+  const int argc_;
   Register name_;
 };
 
@@ -698,10 +986,12 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
                                        Register holder_reg,
                                        Register scratch,
                                        String* name,
+                                       int push_at_depth,
                                        Label* miss) {
   // Check that the maps haven't changed.
   Register result =
-      masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, miss);
+      masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch,
+                        push_at_depth, miss);
 
   // If we've skipped any global objects, it's not enough to verify
   // that their maps haven't changed.
@@ -723,7 +1013,7 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
     object = JSObject::cast(object->GetPrototype());
   }
 
-  // Return the register containin the holder.
+  // Return the register containing the holder.
   return result;
 }
 
@@ -976,15 +1266,31 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
   // unless we're doing a receiver map check.
   ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK);
 
+  CallOptimization optimization(function);
+  int depth = kInvalidProtoDepth;
+
   switch (check) {
     case RECEIVER_MAP_CHECK:
+      __ IncrementCounter(&Counters::call_const, 1);
+
+      if (optimization.is_simple_api_call() && !object->IsGlobalObject()) {
+        depth = optimization.GetPrototypeDepthOfExpectedType(
+            JSObject::cast(object), holder);
+      }
+
+      if (depth != kInvalidProtoDepth) {
+        __ IncrementCounter(&Counters::call_const_fast_api, 1);
+        ReserveSpaceForFastApiCall(masm(), eax);
+      }
+
       // Check that the maps haven't changed.
       CheckPrototypes(JSObject::cast(object), edx, holder,
-                      ebx, eax, name, &miss);
+                      ebx, eax, name, depth, &miss);
 
       // Patch the receiver on the stack with the global proxy if
       // necessary.
       if (object->IsGlobalObject()) {
+        ASSERT(depth == kInvalidProtoDepth);
         __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
         __ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
       }
@@ -1069,19 +1375,26 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
       UNREACHABLE();
   }
 
-  // Get the function and setup the context.
-  __ mov(edi, Immediate(Handle<JSFunction>(function)));
-  __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+  if (depth != kInvalidProtoDepth) {
+    GenerateFastApiCall(masm(), optimization, argc);
+  } else {
+    // Get the function and setup the context.
+    __ mov(edi, Immediate(Handle<JSFunction>(function)));
+    __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
 
-  // Jump to the cached code (tail call).
-  ASSERT(function->is_compiled());
-  Handle<Code> code(function->code());
-  ParameterCount expected(function->shared()->formal_parameter_count());
-  __ InvokeCode(code, expected, arguments(),
-                RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+    // Jump to the cached code (tail call).
+    ASSERT(function->is_compiled());
+    Handle<Code> code(function->code());
+    ParameterCount expected(function->shared()->formal_parameter_count());
+    __ InvokeCode(code, expected, arguments(),
+                  RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+  }
 
   // Handle call cache miss.
   __ bind(&miss);
+  if (depth != kInvalidProtoDepth) {
+    FreeSpaceForFastApiCall(masm(), eax);
+  }
   Handle<Code> ic = ComputeCallMiss(arguments().immediate());
   __ jmp(ic, RelocInfo::CODE_TARGET);
 
@@ -1115,18 +1428,16 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object,
   // Get the receiver from the stack.
   __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
 
-  CallInterceptorCompiler compiler(arguments(), ecx);
-  CompileLoadInterceptor(&compiler,
-                         this,
-                         masm(),
-                         JSObject::cast(object),
-                         holder,
-                         name,
-                         &lookup,
-                         edx,
-                         ebx,
-                         edi,
-                         &miss);
+  CallInterceptorCompiler compiler(this, arguments(), ecx);
+  compiler.Compile(masm(),
+                   JSObject::cast(object),
+                   holder,
+                   name,
+                   &lookup,
+                   edx,
+                   ebx,
+                   edi,
+                   &miss);
 
   // Restore receiver.
   __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
index e33148c..81e5bf7 100644 (file)
@@ -61,6 +61,8 @@ enum AllocationFlags {
   RESULT_CONTAINS_TOP = 1 << 1
 };
 
+// Invalid depth in prototype chain.
+const int kInvalidProtoDepth = -1;
 
 #if V8_TARGET_ARCH_IA32
 #include "assembler.h"
index 8eeef02..72cf40f 100644 (file)
@@ -381,12 +381,25 @@ class StubCompiler BASE_EMBEDDED {
 
   // Check the integrity of the prototype chain to make sure that the
   // current IC is still valid.
+
+  Register CheckPrototypes(JSObject* object,
+                           Register object_reg,
+                           JSObject* holder,
+                           Register holder_reg,
+                           Register scratch,
+                           String* name,
+                           Label* miss) {
+    return CheckPrototypes(object, object_reg, holder, holder_reg, scratch,
+                           name, kInvalidProtoDepth, miss);
+  }
+
   Register CheckPrototypes(JSObject* object,
                            Register object_reg,
                            JSObject* holder,
                            Register holder_reg,
                            Register scratch,
                            String* name,
+                           int save_at_depth,
                            Label* miss);
 
  protected:
index 0274838..b9db4be 100644 (file)
@@ -949,10 +949,15 @@ Handle<Context> Top::GetCallingGlobalContext() {
 }
 
 
+bool Top::CanHaveSpecialFunctions(JSObject* object) {
+  return object->IsJSArray();
+}
+
+
 Object* Top::LookupSpecialFunction(JSObject* receiver,
                                    JSObject* prototype,
                                    JSFunction* function) {
-  if (receiver->IsJSArray()) {
+  if (CanHaveSpecialFunctions(receiver)) {
     FixedArray* table = context()->global_context()->special_function_table();
     for (int index = 0; index < table->length(); index +=3) {
       if ((prototype == table->get(index)) &&
index 8780844..ddc73ba 100644 (file)
--- a/src/top.h
+++ b/src/top.h
@@ -342,6 +342,7 @@ class Top {
     return Handle<JSBuiltinsObject>(thread_local_.context_->builtins());
   }
 
+  static bool CanHaveSpecialFunctions(JSObject* object);
   static Object* LookupSpecialFunction(JSObject* receiver,
                                        JSObject* prototype,
                                        JSFunction* value);
index 7397c30..3d5e845 100644 (file)
@@ -100,69 +100,73 @@ namespace internal {
   SC(total_full_codegen_source_size, V8.TotalFullCodegenSourceSize)
 
 
-#define STATS_COUNTER_LIST_2(SC)                                    \
-  /* Number of code stubs. */                                       \
-  SC(code_stubs, V8.CodeStubs)                                      \
-  /* Amount of stub code. */                                        \
-  SC(total_stubs_code_size, V8.TotalStubsCodeSize)                  \
-  /* Amount of (JS) compiled code. */                               \
-  SC(total_compiled_code_size, V8.TotalCompiledCodeSize)            \
-  SC(gc_compactor_caused_by_request, V8.GCCompactorCausedByRequest) \
-  SC(gc_compactor_caused_by_promoted_data,                          \
-     V8.GCCompactorCausedByPromotedData)                            \
-  SC(gc_compactor_caused_by_oldspace_exhaustion,                    \
-     V8.GCCompactorCausedByOldspaceExhaustion)                      \
-  SC(gc_compactor_caused_by_weak_handles,                           \
-     V8.GCCompactorCausedByWeakHandles)                             \
-  SC(gc_last_resort_from_js, V8.GCLastResortFromJS)                 \
-  SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles)       \
-  /* How is the generic keyed-load stub used? */                    \
-  SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi)                \
-  SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol)          \
-  SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow)              \
-  SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow) \
-  /* Count how much the monomorphic keyed-load stubs are hit. */    \
-  SC(keyed_load_function_prototype, V8.KeyedLoadFunctionPrototype)  \
-  SC(keyed_load_string_length, V8.KeyedLoadStringLength)            \
-  SC(keyed_load_array_length, V8.KeyedLoadArrayLength)              \
-  SC(keyed_load_constant_function, V8.KeyedLoadConstantFunction)    \
-  SC(keyed_load_field, V8.KeyedLoadField)                           \
-  SC(keyed_load_callback, V8.KeyedLoadCallback)                     \
-  SC(keyed_load_interceptor, V8.KeyedLoadInterceptor)               \
-  SC(keyed_load_inline, V8.KeyedLoadInline)                         \
-  SC(keyed_load_inline_miss, V8.KeyedLoadInlineMiss)                \
-  SC(named_load_inline, V8.NamedLoadInline)                         \
-  SC(named_load_inline_miss, V8.NamedLoadInlineMiss)                \
-  SC(named_load_global_inline, V8.NamedLoadGlobalInline)            \
-  SC(named_load_global_inline_miss, V8.NamedLoadGlobalInlineMiss)   \
-  SC(keyed_store_field, V8.KeyedStoreField)                         \
-  SC(keyed_store_inline, V8.KeyedStoreInline)                       \
-  SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss)              \
-  SC(named_store_global_inline, V8.NamedStoreGlobalInline)          \
-  SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
-  SC(call_global_inline, V8.CallGlobalInline)                       \
-  SC(call_global_inline_miss, V8.CallGlobalInlineMiss)              \
-  SC(constructed_objects, V8.ConstructedObjects)                    \
-  SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime)     \
-  SC(constructed_objects_stub, V8.ConstructedObjectsStub)           \
-  SC(array_function_runtime, V8.ArrayFunctionRuntime)               \
-  SC(array_function_native, V8.ArrayFunctionNative)                 \
-  SC(for_in, V8.ForIn)                                              \
-  SC(enum_cache_hits, V8.EnumCacheHits)                             \
-  SC(enum_cache_misses, V8.EnumCacheMisses)                         \
-  SC(reloc_info_count, V8.RelocInfoCount)                           \
-  SC(reloc_info_size, V8.RelocInfoSize)                             \
-  SC(zone_segment_bytes, V8.ZoneSegmentBytes)                       \
-  SC(compute_entry_frame, V8.ComputeEntryFrame)                     \
-  SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls)          \
-  SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \
-  SC(string_add_runtime, V8.StringAddRuntime)                       \
-  SC(string_add_native, V8.StringAddNative)                         \
-  SC(sub_string_runtime, V8.SubStringRuntime)                       \
-  SC(sub_string_native, V8.SubStringNative)                         \
-  SC(string_compare_native, V8.StringCompareNative)                 \
-  SC(string_compare_runtime, V8.StringCompareRuntime)               \
-  SC(regexp_entry_runtime, V8.RegExpEntryRuntime)                   \
+#define STATS_COUNTER_LIST_2(SC)                                      \
+  /* Number of code stubs. */                                         \
+  SC(code_stubs, V8.CodeStubs)                                        \
+  /* Amount of stub code. */                                          \
+  SC(total_stubs_code_size, V8.TotalStubsCodeSize)                    \
+  /* Amount of (JS) compiled code. */                                 \
+  SC(total_compiled_code_size, V8.TotalCompiledCodeSize)              \
+  SC(gc_compactor_caused_by_request, V8.GCCompactorCausedByRequest)   \
+  SC(gc_compactor_caused_by_promoted_data,                            \
+     V8.GCCompactorCausedByPromotedData)                              \
+  SC(gc_compactor_caused_by_oldspace_exhaustion,                      \
+     V8.GCCompactorCausedByOldspaceExhaustion)                        \
+  SC(gc_compactor_caused_by_weak_handles,                             \
+     V8.GCCompactorCausedByWeakHandles)                               \
+  SC(gc_last_resort_from_js, V8.GCLastResortFromJS)                   \
+  SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles)         \
+  /* How is the generic keyed-load stub used? */                      \
+  SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi)                  \
+  SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol)            \
+  SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow)                \
+  SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow)   \
+  /* Count how much the monomorphic keyed-load stubs are hit. */      \
+  SC(keyed_load_function_prototype, V8.KeyedLoadFunctionPrototype)    \
+  SC(keyed_load_string_length, V8.KeyedLoadStringLength)              \
+  SC(keyed_load_array_length, V8.KeyedLoadArrayLength)                \
+  SC(keyed_load_constant_function, V8.KeyedLoadConstantFunction)      \
+  SC(keyed_load_field, V8.KeyedLoadField)                             \
+  SC(keyed_load_callback, V8.KeyedLoadCallback)                       \
+  SC(keyed_load_interceptor, V8.KeyedLoadInterceptor)                 \
+  SC(keyed_load_inline, V8.KeyedLoadInline)                           \
+  SC(keyed_load_inline_miss, V8.KeyedLoadInlineMiss)                  \
+  SC(named_load_inline, V8.NamedLoadInline)                           \
+  SC(named_load_inline_miss, V8.NamedLoadInlineMiss)                  \
+  SC(named_load_global_inline, V8.NamedLoadGlobalInline)              \
+  SC(named_load_global_inline_miss, V8.NamedLoadGlobalInlineMiss)     \
+  SC(keyed_store_field, V8.KeyedStoreField)                           \
+  SC(keyed_store_inline, V8.KeyedStoreInline)                         \
+  SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss)                \
+  SC(named_store_global_inline, V8.NamedStoreGlobalInline)            \
+  SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss)   \
+  SC(call_const, V8.CallConst)                                        \
+  SC(call_const_fast_api, V8.CallConstFastApi)                        \
+  SC(call_const_interceptor, V8.CallConstInterceptor)                 \
+  SC(call_const_interceptor_fast_api, V8.CallConstInterceptorFastApi) \
+  SC(call_global_inline, V8.CallGlobalInline)                         \
+  SC(call_global_inline_miss, V8.CallGlobalInlineMiss)                \
+  SC(constructed_objects, V8.ConstructedObjects)                      \
+  SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime)       \
+  SC(constructed_objects_stub, V8.ConstructedObjectsStub)             \
+  SC(array_function_runtime, V8.ArrayFunctionRuntime)                 \
+  SC(array_function_native, V8.ArrayFunctionNative)                   \
+  SC(for_in, V8.ForIn)                                                \
+  SC(enum_cache_hits, V8.EnumCacheHits)                               \
+  SC(enum_cache_misses, V8.EnumCacheMisses)                           \
+  SC(reloc_info_count, V8.RelocInfoCount)                             \
+  SC(reloc_info_size, V8.RelocInfoSize)                               \
+  SC(zone_segment_bytes, V8.ZoneSegmentBytes)                         \
+  SC(compute_entry_frame, V8.ComputeEntryFrame)                       \
+  SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls)            \
+  SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs)   \
+  SC(string_add_runtime, V8.StringAddRuntime)                         \
+  SC(string_add_native, V8.StringAddNative)                           \
+  SC(sub_string_runtime, V8.SubStringRuntime)                         \
+  SC(sub_string_native, V8.SubStringNative)                           \
+  SC(string_compare_native, V8.StringCompareNative)                   \
+  SC(string_compare_runtime, V8.StringCompareRuntime)                 \
+  SC(regexp_entry_runtime, V8.RegExpEntryRuntime)                     \
   SC(regexp_entry_native, V8.RegExpEntryNative)
 
 // This file contains all the v8 counters that are in use.
index 1c3fd36..be8641c 100644 (file)
@@ -1669,7 +1669,11 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
                                        Register holder_reg,
                                        Register scratch,
                                        String* name,
+                                       int save_at_depth,
                                        Label* miss) {
+  // TODO(602): support object saving.
+  ASSERT(save_at_depth == kInvalidProtoDepth);
+
   // Check that the maps haven't changed.
   Register result =
       __ CheckMaps(object, object_reg, holder, holder_reg, scratch, miss);
index 4aa0280..bd543ec 100644 (file)
@@ -5890,6 +5890,294 @@ THREADED_TEST(InterceptorCallICCachedFromGlobal) {
   CHECK_EQ(239 * 10, value->Int32Value());
 }
 
+static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
+                                                  const AccessorInfo& info) {
+  ApiTestFuzzer::Fuzz();
+  int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
+  ++(*call_count);
+  if ((*call_count) % 20 == 0) {
+    v8::internal::Heap::CollectAllGarbage(true);
+  }
+  return v8::Handle<Value>();
+}
+
+static v8::Handle<Value> FastApiCallback_TrivialSignature(
+    const v8::Arguments& args) {
+  ApiTestFuzzer::Fuzz();
+  CHECK_EQ(args.This(), args.Holder());
+  CHECK(args.Data()->Equals(v8_str("method_data")));
+  return v8::Integer::New(args[0]->Int32Value() + 1);
+}
+
+static v8::Handle<Value> FastApiCallback_SimpleSignature(
+    const v8::Arguments& args) {
+  ApiTestFuzzer::Fuzz();
+  CHECK_EQ(args.This()->GetPrototype(), args.Holder());
+  CHECK(args.Data()->Equals(v8_str("method_data")));
+  // Note, we're using HasRealNamedProperty instead of Has to avoid
+  // invoking the interceptor again.
+  CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
+  return v8::Integer::New(args[0]->Int32Value() + 1);
+}
+
+// Helper to maximize the odds of object moving.
+static void GenerateSomeGarbage() {
+  CompileRun(
+      "var garbage;"
+      "for (var i = 0; i < 1000; i++) {"
+      "  garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
+      "}"
+      "garbage = undefined;");
+}
+
+THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
+  int interceptor_call_count = 0;
+  v8::HandleScope scope;
+  v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+  v8::Handle<v8::FunctionTemplate> method_templ =
+      v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
+                                v8_str("method_data"),
+                                v8::Handle<v8::Signature>());
+  v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+  proto_templ->Set(v8_str("method"), method_templ);
+  v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+  templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
+                                 NULL, NULL, NULL, NULL,
+                                 v8::External::Wrap(&interceptor_call_count));
+  LocalContext context;
+  v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+  GenerateSomeGarbage();
+  context->Global()->Set(v8_str("o"), fun->NewInstance());
+  v8::Handle<Value> value = CompileRun(
+      "var result = 0;"
+      "for (var i = 0; i < 100; i++) {"
+      "  result = o.method(41);"
+      "}");
+  CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
+  CHECK_EQ(100, interceptor_call_count);
+}
+
+THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
+  int interceptor_call_count = 0;
+  v8::HandleScope scope;
+  v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+  v8::Handle<v8::FunctionTemplate> method_templ =
+      v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+                                v8_str("method_data"),
+                                v8::Signature::New(fun_templ));
+  v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+  proto_templ->Set(v8_str("method"), method_templ);
+  v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+  templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
+                                 NULL, NULL, NULL, NULL,
+                                 v8::External::Wrap(&interceptor_call_count));
+  LocalContext context;
+  v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+  GenerateSomeGarbage();
+  context->Global()->Set(v8_str("o"), fun->NewInstance());
+  v8::Handle<Value> value = CompileRun(
+      "o.foo = 17;"
+      "var receiver = {};"
+      "receiver.__proto__ = o;"
+      "var result = 0;"
+      "for (var i = 0; i < 100; i++) {"
+      "  result = receiver.method(41);"
+      "}");
+  CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
+  CHECK_EQ(100, interceptor_call_count);
+}
+
+THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
+  int interceptor_call_count = 0;
+  v8::HandleScope scope;
+  v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+  v8::Handle<v8::FunctionTemplate> method_templ =
+      v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+                                v8_str("method_data"),
+                                v8::Signature::New(fun_templ));
+  v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+  proto_templ->Set(v8_str("method"), method_templ);
+  v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+  templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
+                                 NULL, NULL, NULL, NULL,
+                                 v8::External::Wrap(&interceptor_call_count));
+  LocalContext context;
+  v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+  GenerateSomeGarbage();
+  context->Global()->Set(v8_str("o"), fun->NewInstance());
+  v8::Handle<Value> value = CompileRun(
+      "o.foo = 17;"
+      "var receiver = {};"
+      "receiver.__proto__ = o;"
+      "var result = 0;"
+      "var saved_result = 0;"
+      "for (var i = 0; i < 100; i++) {"
+      "  result = receiver.method(41);"
+      "  if (i == 50) {"
+      "    saved_result = result;"
+      "    receiver = {method: function(x) { return x - 1 }};"
+      "  }"
+      "}");
+  CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
+  CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
+  CHECK_GE(interceptor_call_count, 50);
+}
+
+THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
+  int interceptor_call_count = 0;
+  v8::HandleScope scope;
+  v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+  v8::Handle<v8::FunctionTemplate> method_templ =
+      v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+                                v8_str("method_data"),
+                                v8::Signature::New(fun_templ));
+  v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+  proto_templ->Set(v8_str("method"), method_templ);
+  v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+  templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
+                                 NULL, NULL, NULL, NULL,
+                                 v8::External::Wrap(&interceptor_call_count));
+  LocalContext context;
+  v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+  GenerateSomeGarbage();
+  context->Global()->Set(v8_str("o"), fun->NewInstance());
+  v8::Handle<Value> value = CompileRun(
+      "o.foo = 17;"
+      "var receiver = {};"
+      "receiver.__proto__ = o;"
+      "var result = 0;"
+      "var saved_result = 0;"
+      "for (var i = 0; i < 100; i++) {"
+      "  result = receiver.method(41);"
+      "  if (i == 50) {"
+      "    saved_result = result;"
+      "    o.method = function(x) { return x - 1 };"
+      "  }"
+      "}");
+  CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
+  CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
+  CHECK_GE(interceptor_call_count, 50);
+}
+
+THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
+  int interceptor_call_count = 0;
+  v8::HandleScope scope;
+  v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+  v8::Handle<v8::FunctionTemplate> method_templ =
+      v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+                                v8_str("method_data"),
+                                v8::Signature::New(fun_templ));
+  v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+  proto_templ->Set(v8_str("method"), method_templ);
+  v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+  templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
+                                 NULL, NULL, NULL, NULL,
+                                 v8::External::Wrap(&interceptor_call_count));
+  LocalContext context;
+  v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+  GenerateSomeGarbage();
+  context->Global()->Set(v8_str("o"), fun->NewInstance());
+  v8::TryCatch try_catch;
+  v8::Handle<Value> value = CompileRun(
+      "o.foo = 17;"
+      "var receiver = {};"
+      "receiver.__proto__ = o;"
+      "var result = 0;"
+      "var saved_result = 0;"
+      "for (var i = 0; i < 100; i++) {"
+      "  result = receiver.method(41);"
+      "  if (i == 50) {"
+      "    saved_result = result;"
+      "    receiver = {method: receiver.method};"
+      "  }"
+      "}");
+  CHECK(try_catch.HasCaught());
+  CHECK_EQ(v8_str("TypeError: Illegal invocation"),
+           try_catch.Exception()->ToString());
+  CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
+  CHECK_GE(interceptor_call_count, 50);
+}
+
+THREADED_TEST(CallICFastApi_TrivialSignature) {
+  v8::HandleScope scope;
+  v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+  v8::Handle<v8::FunctionTemplate> method_templ =
+      v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
+                                v8_str("method_data"),
+                                v8::Handle<v8::Signature>());
+  v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+  proto_templ->Set(v8_str("method"), method_templ);
+  v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+  LocalContext context;
+  v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+  GenerateSomeGarbage();
+  context->Global()->Set(v8_str("o"), fun->NewInstance());
+  v8::Handle<Value> value = CompileRun(
+      "var result = 0;"
+      "for (var i = 0; i < 100; i++) {"
+      "  result = o.method(41);"
+      "}");
+
+  CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+THREADED_TEST(CallICFastApi_SimpleSignature) {
+  v8::HandleScope scope;
+  v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+  v8::Handle<v8::FunctionTemplate> method_templ =
+      v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+                                v8_str("method_data"),
+                                v8::Signature::New(fun_templ));
+  v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+  proto_templ->Set(v8_str("method"), method_templ);
+  v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+  LocalContext context;
+  v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+  GenerateSomeGarbage();
+  context->Global()->Set(v8_str("o"), fun->NewInstance());
+  v8::Handle<Value> value = CompileRun(
+      "o.foo = 17;"
+      "var receiver = {};"
+      "receiver.__proto__ = o;"
+      "var result = 0;"
+      "for (var i = 0; i < 100; i++) {"
+      "  result = receiver.method(41);"
+      "}");
+
+  CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+THREADED_TEST(CallICFastApi_SimpleSignature_Miss) {
+  v8::HandleScope scope;
+  v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+  v8::Handle<v8::FunctionTemplate> method_templ =
+      v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+                                v8_str("method_data"),
+                                v8::Signature::New(fun_templ));
+  v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+  proto_templ->Set(v8_str("method"), method_templ);
+  v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+  LocalContext context;
+  v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+  GenerateSomeGarbage();
+  context->Global()->Set(v8_str("o"), fun->NewInstance());
+  v8::Handle<Value> value = CompileRun(
+      "o.foo = 17;"
+      "var receiver = {};"
+      "receiver.__proto__ = o;"
+      "var result = 0;"
+      "var saved_result = 0;"
+      "for (var i = 0; i < 100; i++) {"
+      "  result = receiver.method(41);"
+      "  if (i == 50) {"
+      "    saved_result = result;"
+      "    receiver = {method: function(x) { return x - 1 }};"
+      "  }"
+      "}");
+  CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
+  CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
+}
+
 
 static int interceptor_call_count = 0;