Extend CallIC to support non-constant names.
authorkaznacheev@chromium.org <kaznacheev@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 7 Jun 2010 08:27:32 +0000 (08:27 +0000)
committerkaznacheev@chromium.org <kaznacheev@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 7 Jun 2010 08:27:32 +0000 (08:27 +0000)
This speeds up constructs like this:
var zz='replace'; '123'[zz]('3','4');

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

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

30 files changed:
src/arm/codegen-arm.h
src/arm/ic-arm.cc
src/arm/stub-cache-arm.cc
src/codegen.cc
src/debug.cc
src/disassembler.cc
src/full-codegen.h
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/ia32/full-codegen-ia32.cc
src/ia32/ic-ia32.cc
src/ia32/stub-cache-ia32.cc
src/ia32/virtual-frame-ia32.cc
src/ia32/virtual-frame-ia32.h
src/ic.cc
src/ic.h
src/log.cc
src/log.h
src/objects-inl.h
src/objects.cc
src/objects.h
src/spaces.cc
src/stub-cache.cc
src/stub-cache.h
src/x64/codegen-x64.h
src/x64/ic-x64.cc
src/x64/stub-cache-x64.cc
test/cctest/test-api.cc
test/cctest/test-debug.cc
test/mjsunit/keyed-call-ic.js [new file with mode: 0644]

index 3c6cec317fc202829f26181ab72614b353ea2f59..6a33667eb411bed3f515d4dd616ef17c805f330f 100644 (file)
@@ -449,6 +449,8 @@ class CodeGenerator: public AstVisitor {
 
   static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop);
 
+  static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop);
+
   // Declare global variables and functions in the given array of
   // name/value pairs.
   void DeclareGlobals(Handle<FixedArray> pairs);
index a7c436d61b3353d07e74febc5827f11835fb6211..5257913d21e437465d25717145aeeea7742d7600 100644 (file)
@@ -496,6 +496,21 @@ void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
 }
 
 
+void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+  UNREACHABLE();
+}
+
+
+void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+  UNREACHABLE();
+}
+
+
+void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+  UNREACHABLE();
+}
+
+
 // Defined in ic.cc.
 Object* LoadIC_Miss(Arguments args);
 
index 72cdc10298a8df43326115674e1c01a88e741855..3992d6c5d097c2115528aebcf072a9d2c9c348e1 100644 (file)
@@ -1019,6 +1019,12 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) {
 }
 
 
+void CallStubCompiler::GenerateMissBranch() {
+  Handle<Code> ic = ComputeCallMiss(arguments().immediate(), kind_);
+  __ Jump(ic, RelocInfo::CODE_TARGET);
+}
+
+
 Object* CallStubCompiler::CompileCallField(JSObject* object,
                                            JSObject* holder,
                                            int index,
@@ -1045,8 +1051,7 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
 
   // Handle call cache miss.
   __ bind(&miss);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ Jump(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(FIELD, name);
@@ -1095,8 +1100,7 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
 
   // Handle call cache miss.
   __ bind(&miss);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ Jump(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(function);
@@ -1145,8 +1149,7 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
 
   // Handle call cache miss.
   __ bind(&miss);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ Jump(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(function);
@@ -1317,8 +1320,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
   }
 
   __ bind(&miss_in_smi_check);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ Jump(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(function);
@@ -1364,8 +1366,7 @@ Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
 
   // Handle call cache miss.
   __ bind(&miss);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ Jump(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(INTERCEPTOR, name);
@@ -1447,8 +1448,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
   // Handle call cache miss.
   __ bind(&miss);
   __ IncrementCounter(&Counters::call_global_inline_miss, 1, r1, r3);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ Jump(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(NORMAL, name);
index f89399a974fc798cd722f53d55e402bd4c55ac7d..ff2fa0395b0ee93e22fcaf371f06cf8d55771e4e 100644 (file)
@@ -254,10 +254,28 @@ Handle<Code> CodeGenerator::ComputeCallInitialize(
     // that it needs so we need to ensure it is generated already.
     ComputeCallInitialize(argc, NOT_IN_LOOP);
   }
-  CALL_HEAP_FUNCTION(StubCache::ComputeCallInitialize(argc, in_loop), Code);
+  CALL_HEAP_FUNCTION(
+      StubCache::ComputeCallInitialize(argc, in_loop, Code::CALL_IC),
+      Code);
 }
 
 
+Handle<Code> CodeGenerator::ComputeKeyedCallInitialize(
+    int argc,
+    InLoopFlag in_loop) {
+  if (in_loop == IN_LOOP) {
+    // Force the creation of the corresponding stub outside loops,
+    // because it may be used when clearing the ICs later - it is
+    // possible for a series of IC transitions to lose the in-loop
+    // information, and the IC clearing code can't generate a stub
+    // that it needs so we need to ensure it is generated already.
+    ComputeKeyedCallInitialize(argc, NOT_IN_LOOP);
+  }
+  CALL_HEAP_FUNCTION(
+      StubCache::ComputeCallInitialize(argc, in_loop, Code::KEYED_CALL_IC),
+      Code);
+}
+
 void CodeGenerator::ProcessDeclarations(ZoneList<Declaration*>* declarations) {
   int length = declarations->length();
   int globals = 0;
index 8cb95efd061a89bd1409f4339e0f4bb6b205eb91..e279ee9eff5bf45aaaa3d71b2cf0562a9151469c 100644 (file)
@@ -62,13 +62,14 @@ static void PrintLn(v8::Local<v8::Value> value) {
 }
 
 
-static Handle<Code> ComputeCallDebugBreak(int argc) {
-  CALL_HEAP_FUNCTION(StubCache::ComputeCallDebugBreak(argc), Code);
+static Handle<Code> ComputeCallDebugBreak(int argc, Code::Kind kind) {
+  CALL_HEAP_FUNCTION(StubCache::ComputeCallDebugBreak(argc, kind), Code);
 }
 
 
-static Handle<Code> ComputeCallDebugPrepareStepIn(int argc) {
-  CALL_HEAP_FUNCTION(StubCache::ComputeCallDebugPrepareStepIn(argc), Code);
+static Handle<Code> ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) {
+  CALL_HEAP_FUNCTION(
+      StubCache::ComputeCallDebugPrepareStepIn(argc, kind), Code);
 }
 
 
@@ -360,13 +361,14 @@ void BreakLocationIterator::PrepareStepIn() {
   // construct call or CallFunction stub call.
   Address target = rinfo()->target_address();
   Handle<Code> code(Code::GetCodeFromTargetAddress(target));
-  if (code->is_call_stub()) {
+  if (code->is_call_stub() || code->is_keyed_call_stub()) {
     // Step in through IC call is handled by the runtime system. Therefore make
     // sure that the any current IC is cleared and the runtime system is
     // called. If the executing code has a debug break at the location change
     // the call in the original code as it is the code there that will be
     // executed in place of the debug break call.
-    Handle<Code> stub = ComputeCallDebugPrepareStepIn(code->arguments_count());
+    Handle<Code> stub = ComputeCallDebugPrepareStepIn(code->arguments_count(),
+                                                      code->kind());
     if (IsDebugBreak()) {
       original_rinfo()->set_target_address(stub->entry());
     } else {
@@ -1187,7 +1189,7 @@ void Debug::PrepareStep(StepAction step_action, int step_count) {
   if (RelocInfo::IsCodeTarget(it.rinfo()->rmode())) {
     Address target = it.rinfo()->target_address();
     Code* code = Code::GetCodeFromTargetAddress(target);
-    if (code->is_call_stub()) {
+    if (code->is_call_stub() || code->is_keyed_call_stub()) {
       is_call_target = true;
     }
     if (code->is_inline_cache_stub()) {
@@ -1373,7 +1375,8 @@ Handle<Code> Debug::FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode) {
   if (code->is_inline_cache_stub()) {
     switch (code->kind()) {
       case Code::CALL_IC:
-        return ComputeCallDebugBreak(code->arguments_count());
+      case Code::KEYED_CALL_IC:
+        return ComputeCallDebugBreak(code->arguments_count(), code->kind());
 
       case Code::LOAD_IC:
         return Handle<Code>(Builtins::builtin(Builtins::LoadIC_DebugBreak));
index 8473cd9f95421498472ecc228a379011615d9ab7..19cb6af728889dfcc903d3f87e21c6e8dd59661e 100644 (file)
@@ -246,7 +246,7 @@ static int DecodeIt(FILE* f,
           if (code->ic_in_loop() == IN_LOOP) {
             out.AddFormatted(", in_loop");
           }
-          if (kind == Code::CALL_IC) {
+          if (kind == Code::CALL_IC || kind == Code::KEYED_CALL_IC) {
             out.AddFormatted(", argc = %d", code->arguments_count());
           }
         } else if (kind == Code::STUB) {
index e9a05be622271cc89e9182a18bdd5028f9a9ea99..ac70d9986eaf302140fc08a8b3b68a22455bf899 100644 (file)
@@ -369,6 +369,7 @@ class FullCodeGenerator: public AstVisitor {
   // Platform-specific code sequences for calls
   void EmitCallWithStub(Call* expr);
   void EmitCallWithIC(Call* expr, Handle<Object> name, RelocInfo::Mode mode);
+  void EmitKeyedCallWithIC(Call* expr, Expression* key, RelocInfo::Mode mode);
 
 
   // Platform-specific code for inline runtime calls.
index 7cd83de18ba3da04f6a6f42fd4d6fffab4314bb5..603f9135ff8f5ec39cd286aebb18e3e7b637fd86 100644 (file)
@@ -5975,18 +5975,31 @@ void CodeGenerator::VisitCall(Call* node) {
         ref.GetValue();
         // Use global object as receiver.
         LoadGlobalReceiver();
+        // Call the function.
+        CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
       } else {
+        // Push the receiver onto the frame.
         Load(property->obj());
-        frame()->Dup();
+
+        // Load the arguments.
+        int arg_count = args->length();
+        for (int i = 0; i < arg_count; i++) {
+          Load(args->at(i));
+          frame_->SpillTop();
+        }
+
+        // Load the name of the function.
         Load(property->key());
-        Result function = EmitKeyedLoad();
-        Result receiver = frame_->Pop();
-        frame_->Push(&function);
-        frame_->Push(&receiver);
-      }
 
-      // Call the function.
-      CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
+        // Call the IC initialization code.
+        CodeForSourcePosition(node->position());
+        Result result =
+            frame_->CallKeyedCallIC(RelocInfo::CODE_TARGET,
+                                    arg_count,
+                                    loop_nesting());
+        frame_->RestoreContextRegister();
+        frame_->Push(&result);
+      }
     }
 
   } else {
index ea182ab9cc9f40b3962062633354119275b66778..b3ddad9d9aba4ad1883e01f85c93ba9e1501bd75 100644 (file)
@@ -594,6 +594,8 @@ class CodeGenerator: public AstVisitor {
 
   static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop);
 
+  static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop);
+
   // Declare global variables and functions in the given array of
   // name/value pairs.
   void DeclareGlobals(Handle<FixedArray> pairs);
index ae64d023c0b926622f7bc6a1d167d91b541d4221..28b766a5729dd7c4b03d1197a7165ab305a850a9 100644 (file)
@@ -1726,6 +1726,29 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
 }
 
 
+void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
+                                            Expression* key,
+                                            RelocInfo::Mode mode) {
+  // Code common for calls using the IC.
+  ZoneList<Expression*>* args = expr->arguments();
+  int arg_count = args->length();
+  for (int i = 0; i < arg_count; i++) {
+    VisitForValue(args->at(i), kStack);
+  }
+  VisitForValue(key, kAccumulator);
+  __ mov(ecx, eax);
+  // Record source position of the IC call.
+  SetSourcePosition(expr->position());
+  InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+  Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(
+      arg_count, in_loop);
+  __ call(ic, mode);
+  // Restore context register.
+  __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+  Apply(context_, eax);
+}
+
+
 void FullCodeGenerator::EmitCallWithStub(Call* expr) {
   // Code common for calls using the call stub.
   ZoneList<Expression*>* args = expr->arguments();
@@ -1815,37 +1838,31 @@ void FullCodeGenerator::VisitCall(Call* expr) {
       VisitForValue(prop->obj(), kStack);
       EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
     } else {
-      // Call to a keyed property, use keyed load IC followed by function
-      // call.
+      // Call to a keyed property.
+      // For a synthetic property use keyed load IC followed by function call,
+      // for a regular property use keyed CallIC.
       VisitForValue(prop->obj(), kStack);
-      VisitForValue(prop->key(), kAccumulator);
-      // Record source code position for IC call.
-      SetSourcePosition(prop->position());
       if (prop->is_synthetic()) {
+        VisitForValue(prop->key(), kAccumulator);
+        // Record source code position for IC call.
+        SetSourcePosition(prop->position());
         __ pop(edx);  // We do not need to keep the receiver.
-      } else {
-        __ mov(edx, Operand(esp, 0));  // Keep receiver, to call function on.
-      }
 
-      Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
-      __ call(ic, RelocInfo::CODE_TARGET);
-      // By emitting a nop we make sure that we do not have a "test eax,..."
-      // instruction after the call it is treated specially by the LoadIC code.
-      __ nop();
-      if (prop->is_synthetic()) {
+        Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+        __ call(ic, RelocInfo::CODE_TARGET);
+        // By emitting a nop we make sure that we do not have a "test eax,..."
+        // instruction after the call as it is treated specially
+        // by the LoadIC code.
+        __ nop();
         // Push result (function).
         __ push(eax);
         // Push Global receiver.
         __ mov(ecx, CodeGenerator::GlobalObject());
         __ push(FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
+        EmitCallWithStub(expr);
       } else {
-        // Pop receiver.
-        __ pop(ebx);
-        // Push result (function).
-        __ push(eax);
-        __ push(ebx);
+        EmitKeyedCallWithIC(expr, prop->key(), RelocInfo::CODE_TARGET);
       }
-      EmitCallWithStub(expr);
     }
   } else {
     // Call to some other expression.  If the expression is an anonymous
index 3aa60baa74c38d5e54a62d5632eef44e9c631a84..0f14a587336ded80c9887a773e150705a77f52b7 100644 (file)
@@ -1044,22 +1044,21 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm,
 // Defined in ic.cc.
 Object* CallIC_Miss(Arguments args);
 
-void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+// The generated code does not accept smi keys.
+// The generated code falls through if both probes miss.
+static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
+                                          int argc,
+                                          Code::Kind kind,
+                                          Label* miss) {
   // ----------- S t a t e -------------
   //  -- ecx                 : name
-  //  -- esp[0]              : return address
-  //  -- esp[(argc - n) * 4] : arg[n] (zero-based)
-  //  -- ...
-  //  -- esp[(argc + 1) * 4] : receiver
+  //  -- edx                 : receiver
   // -----------------------------------
-  Label number, non_number, non_string, boolean, probe, miss;
-
-  // Get the receiver of the function from the stack; 1 ~ return address.
-  __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+  Label number, non_number, non_string, boolean, probe;
 
   // Probe the stub cache.
   Code::Flags flags =
-      Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
+      Code::ComputeFlags(kind, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
   StubCache::GenerateProbe(masm, flags, edx, ecx, ebx, eax);
 
   // If the stub cache probing failed, the receiver might be a value.
@@ -1079,7 +1078,7 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
 
   // Check for string.
   __ bind(&non_number);
-  __ cmp(ebx, FIRST_NONSTRING_TYPE);
+  __ CmpInstanceType(ebx, FIRST_NONSTRING_TYPE);
   __ j(above_equal, &non_string, taken);
   StubCompiler::GenerateLoadGlobalFunctionPrototype(
       masm, Context::STRING_FUNCTION_INDEX, edx);
@@ -1090,7 +1089,7 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
   __ cmp(edx, Factory::true_value());
   __ j(equal, &boolean, not_taken);
   __ cmp(edx, Factory::false_value());
-  __ j(not_equal, &miss, taken);
+  __ j(not_equal, miss, taken);
   __ bind(&boolean);
   StubCompiler::GenerateLoadGlobalFunctionPrototype(
       masm, Context::BOOLEAN_FUNCTION_INDEX, edx);
@@ -1098,10 +1097,6 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
   // Probe the stub cache for the value object.
   __ bind(&probe);
   StubCache::GenerateProbe(masm, flags, edx, ecx, ebx, no_reg);
-
-  // Cache miss: Jump to runtime.
-  __ bind(&miss);
-  GenerateMiss(masm, argc);
 }
 
 
@@ -1141,8 +1136,8 @@ static void GenerateNormalHelper(MacroAssembler* masm,
   __ InvokeFunction(edi, actual, JUMP_FUNCTION);
 }
 
-
-void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+// The generated code never falls through.
+static void GenerateCallNormal(MacroAssembler* masm, int argc, Label* miss) {
   // ----------- S t a t e -------------
   //  -- ecx                 : name
   //  -- esp[0]              : return address
@@ -1150,20 +1145,20 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
   //  -- ...
   //  -- esp[(argc + 1) * 4] : receiver
   // -----------------------------------
-  Label miss, global_object, non_global_object;
+  Label global_object, non_global_object;
 
   // Get the receiver of the function from the stack; 1 ~ return address.
   __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
 
   // Check that the receiver isn't a smi.
   __ test(edx, Immediate(kSmiTagMask));
-  __ j(zero, &miss, not_taken);
+  __ j(zero, miss, not_taken);
 
   // Check that the receiver is a valid JS object.
   __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
   __ movzx_b(eax, FieldOperand(ebx, Map::kInstanceTypeOffset));
   __ cmp(eax, FIRST_JS_OBJECT_TYPE);
-  __ j(below, &miss, not_taken);
+  __ j(below, miss, not_taken);
 
   // If this assert fails, we have to check upper bound too.
   ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
@@ -1179,8 +1174,8 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
   // Check that the global object does not require access checks.
   __ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
             1 << Map::kIsAccessCheckNeeded);
-  __ j(not_equal, &miss, not_taken);
-  GenerateNormalHelper(masm, argc, true, &miss);
+  __ j(not_equal, miss, not_taken);
+  GenerateNormalHelper(masm, argc, true, miss);
 
   // Accessing non-global object: Check for access to global proxy.
   Label global_proxy, invoke;
@@ -1191,22 +1186,18 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
   // require access checks.
   __ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
             1 << Map::kIsAccessCheckNeeded);
-  __ j(not_equal, &miss, not_taken);
+  __ j(not_equal, miss, not_taken);
   __ bind(&invoke);
-  GenerateNormalHelper(masm, argc, false, &miss);
+  GenerateNormalHelper(masm, argc, false, miss);
 
   // Global object proxy access: Check access rights.
   __ bind(&global_proxy);
-  __ CheckAccessGlobalProxy(edx, eax, &miss);
+  __ CheckAccessGlobalProxy(edx, eax, miss);
   __ jmp(&invoke);
-
-  // Cache miss: Jump to runtime.
-  __ bind(&miss);
-  GenerateMiss(masm, argc);
 }
 
 
-void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
   // ----------- S t a t e -------------
   //  -- ecx                 : name
   //  -- esp[0]              : return address
@@ -1228,7 +1219,7 @@ void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
   // Call the entry.
   CEntryStub stub(1);
   __ mov(eax, Immediate(2));
-  __ mov(ebx, Immediate(ExternalReference(IC_Utility(kCallIC_Miss))));
+  __ mov(ebx, Immediate(ExternalReference(IC_Utility(id))));
   __ CallStub(&stub);
 
   // Move result to edi and exit the internal frame.
@@ -1259,6 +1250,106 @@ void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
 }
 
 
+void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+  // ----------- S t a t e -------------
+  //  -- ecx                 : name
+  //  -- esp[0]              : return address
+  //  -- esp[(argc - n) * 4] : arg[n] (zero-based)
+  //  -- ...
+  //  -- esp[(argc + 1) * 4] : receiver
+  // -----------------------------------
+
+  Label miss;
+  // Get the receiver of the function from the stack; 1 ~ return address.
+  __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+  GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, &miss);
+  __ bind(&miss);
+  GenerateMiss(masm, argc);
+}
+
+
+void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+  Label miss;
+  GenerateCallNormal(masm, argc, &miss);
+  __ bind(&miss);
+  GenerateMiss(masm, argc);
+}
+
+
+void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+  GenerateCallMiss(masm, argc, IC::kCallIC_Miss);
+}
+
+
+void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+  // ----------- S t a t e -------------
+  //  -- ecx                 : name
+  //  -- esp[0]              : return address
+  //  -- esp[(argc - n) * 4] : arg[n] (zero-based)
+  //  -- ...
+  //  -- esp[(argc + 1) * 4] : receiver
+  // -----------------------------------
+
+  // Get the receiver of the function from the stack; 1 ~ return address.
+  __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+  Label miss, skip_probe;
+
+  // Do not probe monomorphic cache if a key is a smi.
+  __ test(ecx, Immediate(kSmiTagMask));
+  __ j(equal, &skip_probe, taken);
+
+  GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC, &skip_probe);
+
+  __ bind(&skip_probe);
+
+  __ mov(eax, ecx);
+  __ EnterInternalFrame();
+  __ push(ecx);
+  __ call(Handle<Code>(Builtins::builtin(Builtins::KeyedLoadIC_Generic)),
+          RelocInfo::CODE_TARGET);
+  __ pop(ecx);
+  __ LeaveInternalFrame();
+  __ mov(edi, eax);
+
+  __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+  // Check that the receiver isn't a smi.
+  __ test(edx, Immediate(kSmiTagMask));
+  __ j(zero, &miss, not_taken);
+
+  // Check that the receiver is a valid JS object.
+  __ CmpObjectType(edx, FIRST_JS_OBJECT_TYPE, eax);
+  __ j(below, &miss, not_taken);
+
+  // Check that the value is a JavaScript function.
+  __ test(edi, Immediate(kSmiTagMask));
+  __ j(zero, &miss, not_taken);
+  __ CmpObjectType(edi, JS_FUNCTION_TYPE, eax);
+  __ j(not_equal, &miss, not_taken);
+
+  // Invoke the function.
+  ParameterCount actual(argc);
+  __ InvokeFunction(edi, actual, JUMP_FUNCTION);
+
+  __ bind(&miss);
+  GenerateMiss(masm, argc);
+}
+
+
+void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+  Label miss;
+  GenerateCallNormal(masm, argc, &miss);
+  __ bind(&miss);
+  GenerateMiss(masm, argc);
+}
+
+
+void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+  GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss);
+}
+
+
 // Defined in ic.cc.
 Object* LoadIC_Miss(Arguments args);
 
index 0833daebf63d7f85738aa8842fefb458956e44e1..48d9e674549c08b21db7a3b0fbf454409d00bc7b 100644 (file)
@@ -1040,6 +1040,20 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) {
 }
 
 
+void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
+  if (kind_ == Code::KEYED_CALL_IC) {
+    __ cmp(Operand(ecx), Immediate(Handle<String>(name)));
+    __ j(not_equal, miss, not_taken);
+  }
+}
+
+
+void CallStubCompiler::GenerateMissBranch() {
+  Handle<Code> ic = ComputeCallMiss(arguments().immediate(), kind_);
+  __ jmp(ic, RelocInfo::CODE_TARGET);
+}
+
+
 Object* CallStubCompiler::CompileCallField(JSObject* object,
                                            JSObject* holder,
                                            int index,
@@ -1053,6 +1067,8 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
   // -----------------------------------
   Label miss;
 
+  GenerateNameCheck(name, &miss);
+
   // Get the receiver from the stack.
   const int argc = arguments().immediate();
   __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
@@ -1084,8 +1100,7 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
 
   // Handle call cache miss.
   __ bind(&miss);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ jmp(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(FIELD, name);
@@ -1113,6 +1128,8 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
 
   Label miss;
 
+  GenerateNameCheck(name, &miss);
+
   // Get the receiver from the stack.
   const int argc = arguments().immediate();
   __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
@@ -1230,8 +1247,7 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
   }
 
   __ bind(&miss);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ jmp(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(function);
@@ -1259,6 +1275,8 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
 
   Label miss, return_undefined, call_builtin;
 
+  GenerateNameCheck(name, &miss);
+
   // Get the receiver from the stack.
   const int argc = arguments().immediate();
   __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
@@ -1312,8 +1330,7 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
                                1);
 
   __ bind(&miss);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ jmp(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(function);
@@ -1337,6 +1354,7 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
 
   Label miss;
   Label index_out_of_range;
+  GenerateNameCheck(name, &miss);
 
   // Check that the maps starting from the prototype haven't changed.
   GenerateDirectLoadGlobalFunctionPrototype(masm(),
@@ -1346,7 +1364,7 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
                   ebx, edx, name, &miss);
 
   Register receiver = ebx;
-  Register index = ecx;
+  Register index = edi;
   Register scratch = edx;
   Register result = eax;
   __ mov(receiver, Operand(esp, (argc + 1) * kPointerSize));
@@ -1375,11 +1393,8 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
   __ ret((argc + 1) * kPointerSize);
 
   __ bind(&miss);
-  // Restore function name in ecx.
-  __ Set(ecx, Immediate(Handle<String>(name)));
 
-  Handle<Code> ic = ComputeCallMiss(argc);
-  __ jmp(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(function);
@@ -1404,6 +1419,8 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
   Label miss;
   Label index_out_of_range;
 
+  GenerateNameCheck(name, &miss);
+
   // Check that the maps starting from the prototype haven't changed.
   GenerateDirectLoadGlobalFunctionPrototype(masm(),
                                             Context::STRING_FUNCTION_INDEX,
@@ -1412,7 +1429,7 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
                   ebx, edx, name, &miss);
 
   Register receiver = eax;
-  Register index = ecx;
+  Register index = edi;
   Register scratch1 = ebx;
   Register scratch2 = edx;
   Register result = eax;
@@ -1444,10 +1461,8 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
 
   __ bind(&miss);
   // Restore function name in ecx.
-  __ Set(ecx, Immediate(Handle<String>(name)));
 
-  Handle<Code> ic = ComputeCallMiss(argc);
-  __ jmp(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(function);
@@ -1480,6 +1495,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
 
   Label miss_in_smi_check;
 
+  GenerateNameCheck(name, &miss_in_smi_check);
+
   // Get the receiver from the stack.
   const int argc = arguments().immediate();
   __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
@@ -1599,8 +1616,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
     FreeSpaceForFastApiCall(masm(), eax);
   }
   __ bind(&miss_in_smi_check);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ jmp(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(function);
@@ -1619,6 +1635,8 @@ Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
   // -----------------------------------
   Label miss;
 
+  GenerateNameCheck(name, &miss);
+
   // Get the number of arguments.
   const int argc = arguments().immediate();
 
@@ -1661,8 +1679,7 @@ Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
 
   // Handle load cache miss.
   __ bind(&miss);
-  Handle<Code> ic = ComputeCallMiss(argc);
-  __ jmp(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(INTERCEPTOR, name);
@@ -1683,6 +1700,8 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
   // -----------------------------------
   Label miss;
 
+  GenerateNameCheck(name, &miss);
+
   // Get the number of arguments.
   const int argc = arguments().immediate();
 
@@ -1745,8 +1764,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
   // Handle call cache miss.
   __ bind(&miss);
   __ IncrementCounter(&Counters::call_global_inline_miss, 1);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ jmp(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(NORMAL, name);
index e22df6ec2816a803058266a0a579c41186c9f59a..36774da0dc16b62dc224e87a9be8b67868ea30e4 100644 (file)
@@ -1119,6 +1119,24 @@ Result VirtualFrame::CallCallIC(RelocInfo::Mode mode,
 }
 
 
+Result VirtualFrame::CallKeyedCallIC(RelocInfo::Mode mode,
+                                     int arg_count,
+                                     int loop_nesting) {
+  // Function name, arguments, and receiver are on top of the frame.
+  // The IC expects the name in ecx and the rest on the stack and
+  // drops them all.
+  InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP;
+  Handle<Code> ic = cgen()->ComputeKeyedCallInitialize(arg_count, in_loop);
+  // Spill args, receiver, and function.  The call will drop args and
+  // receiver.
+  Result name = Pop();
+  PrepareForCall(arg_count + 1, arg_count + 1);  // Arguments + receiver.
+  name.ToRegister(ecx);
+  name.Unuse();
+  return RawCallCodeObject(ic, mode);
+}
+
+
 Result VirtualFrame::CallConstructor(int arg_count) {
   // Arguments, receiver, and function are on top of the frame.  The
   // IC expects arg count in eax, function in edi, and the arguments
index 48d0fa24715bcec7854d1ee186c2c9db4801f217..e00626b7d49b613d4a6696855050d4a37afb0337 100644 (file)
@@ -360,6 +360,9 @@ class VirtualFrame: public ZoneObject {
   // include the receiver.
   Result CallCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);
 
+  // Call keyed call IC.  Same calling convention as CallCallIC.
+  Result CallKeyedCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);
+
   // Allocate and call JS function as constructor.  Arguments,
   // receiver (global object), and function are found on top of the
   // frame.  Function is not dropped.  The argument count does not
index 678876df7268a0333ff82179e6b559d3d01c24cf..338f630064bdcf0f89c8b2bd094001e627b4e591 100644 (file)
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -152,11 +152,13 @@ IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) {
   // to prototype check failure.
   int index = map->IndexInCodeCache(name, target);
   if (index >= 0) {
-    // For keyed load/store, the most likely cause of cache failure is
+    // For keyed load/store/call, the most likely cause of cache failure is
     // that the key has changed.  We do not distinguish between
     // prototype and non-prototype failures for keyed access.
     Code::Kind kind = target->kind();
-    if (kind == Code::KEYED_LOAD_IC || kind == Code::KEYED_STORE_IC) {
+    if (kind == Code::KEYED_LOAD_IC ||
+        kind == Code::KEYED_STORE_IC ||
+        kind == Code::KEYED_CALL_IC) {
       return MONOMORPHIC;
     }
 
@@ -196,9 +198,9 @@ RelocInfo::Mode IC::ComputeMode() {
 
 Failure* IC::TypeError(const char* type,
                        Handle<Object> object,
-                       Handle<String> name) {
+                       Handle<Object> key) {
   HandleScope scope;
-  Handle<Object> args[2] = { name, object };
+  Handle<Object> args[2] = { key, object };
   Handle<Object> error = Factory::NewTypeError(type, HandleVector(args, 2));
   return Top::Throw(*error);
 }
@@ -224,6 +226,7 @@ void IC::Clear(Address address) {
     case Code::STORE_IC: return StoreIC::Clear(address, target);
     case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(address, target);
     case Code::CALL_IC: return CallIC::Clear(address, target);
+    case Code::KEYED_CALL_IC:  return KeyedCallIC::Clear(address, target);
     case Code::BINARY_OP_IC: return;  // Clearing these is tricky and does not
                                       // make any performance difference.
     default: UNREACHABLE();
@@ -231,12 +234,13 @@ void IC::Clear(Address address) {
 }
 
 
-void CallIC::Clear(Address address, Code* target) {
+void CallICBase::Clear(Address address, Code* target) {
   State state = target->ic_state();
-  InLoopFlag in_loop = target->ic_in_loop();
   if (state == UNINITIALIZED) return;
   Code* code =
-      StubCache::FindCallInitialize(target->arguments_count(), in_loop);
+      StubCache::FindCallInitialize(target->arguments_count(),
+                                    target->ic_in_loop(),
+                                    target->kind());
   SetTargetAtAddress(address, code);
 }
 
@@ -364,7 +368,7 @@ static void LookupForRead(Object* object,
 }
 
 
-Object* CallIC::TryCallAsFunction(Object* object) {
+Object* CallICBase::TryCallAsFunction(Object* object) {
   HandleScope scope;
   Handle<Object> target(object);
   Handle<Object> delegate = Execution::GetFunctionDelegate(target);
@@ -383,7 +387,7 @@ Object* CallIC::TryCallAsFunction(Object* object) {
   return *delegate;
 }
 
-void CallIC::ReceiverToObject(Handle<Object> object) {
+void CallICBase::ReceiverToObject(Handle<Object> object) {
   HandleScope scope;
   Handle<Object> receiver(object);
 
@@ -396,9 +400,9 @@ void CallIC::ReceiverToObject(Handle<Object> object) {
 }
 
 
-Object* CallIC::LoadFunction(State state,
-                             Handle<Object> object,
-                             Handle<String> name) {
+Object* CallICBase::LoadFunction(State state,
+                                 Handle<Object> object,
+                                 Handle<String> name) {
   // If the object is undefined or null it's illegal to try to get any
   // of its properties; throw a TypeError in that case.
   if (object->IsUndefined() || object->IsNull()) {
@@ -481,7 +485,7 @@ Object* CallIC::LoadFunction(State state,
 }
 
 
-void CallIC::UpdateCaches(LookupResult* lookup,
+void CallICBase::UpdateCaches(LookupResult* lookup,
                           State state,
                           Handle<Object> object,
                           Handle<String> name) {
@@ -497,16 +501,21 @@ void CallIC::UpdateCaches(LookupResult* lookup,
     // This is the first time we execute this inline cache.
     // Set the target to the pre monomorphic stub to delay
     // setting the monomorphic state.
-    code = StubCache::ComputeCallPreMonomorphic(argc, in_loop);
+    code = StubCache::ComputeCallPreMonomorphic(argc, in_loop, kind_);
   } else if (state == MONOMORPHIC) {
-    code = StubCache::ComputeCallMegamorphic(argc, in_loop);
+    code = StubCache::ComputeCallMegamorphic(argc, in_loop, kind_);
   } else {
     // Compute monomorphic stub.
     switch (lookup->type()) {
       case FIELD: {
         int index = lookup->GetFieldIndex();
-        code = StubCache::ComputeCallField(argc, in_loop, *name, *object,
-                                           lookup->holder(), index);
+        code = StubCache::ComputeCallField(argc,
+                                           in_loop,
+                                           kind_,
+                                           *name,
+                                           *object,
+                                           lookup->holder(),
+                                           index);
         break;
       }
       case CONSTANT_FUNCTION: {
@@ -514,8 +523,13 @@ void CallIC::UpdateCaches(LookupResult* lookup,
         // call; used for rewriting to monomorphic state and making sure
         // that the code stub is in the stub cache.
         JSFunction* function = lookup->GetConstantFunction();
-        code = StubCache::ComputeCallConstant(argc, in_loop, *name, *object,
-                                              lookup->holder(), function);
+        code = StubCache::ComputeCallConstant(argc,
+                                              in_loop,
+                                              kind_,
+                                              *name,
+                                              *object,
+                                              lookup->holder(),
+                                              function);
         break;
       }
       case NORMAL: {
@@ -530,6 +544,7 @@ void CallIC::UpdateCaches(LookupResult* lookup,
           JSFunction* function = JSFunction::cast(cell->value());
           code = StubCache::ComputeCallGlobal(argc,
                                               in_loop,
+                                              kind_,
                                               *name,
                                               *receiver,
                                               global,
@@ -541,13 +556,20 @@ void CallIC::UpdateCaches(LookupResult* lookup,
           // property must be found in the receiver for the stub to be
           // applicable.
           if (lookup->holder() != *receiver) return;
-          code = StubCache::ComputeCallNormal(argc, in_loop, *name, *receiver);
+          code = StubCache::ComputeCallNormal(argc,
+                                              in_loop,
+                                              kind_,
+                                              *name,
+                                              *receiver);
         }
         break;
       }
       case INTERCEPTOR: {
         ASSERT(HasInterceptorGetter(lookup->holder()));
-        code = StubCache::ComputeCallInterceptor(argc, *name, *object,
+        code = StubCache::ComputeCallInterceptor(argc,
+                                                 kind_,
+                                                 *name,
+                                                 *object,
                                                  lookup->holder());
         break;
       }
@@ -569,11 +591,44 @@ void CallIC::UpdateCaches(LookupResult* lookup,
   }
 
 #ifdef DEBUG
-  TraceIC("CallIC", name, state, target(), in_loop ? " (in-loop)" : "");
+  TraceIC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC",
+      name, state, target(), in_loop ? " (in-loop)" : "");
 #endif
 }
 
 
+Object* KeyedCallIC::LoadFunction(State state,
+                                  Handle<Object> object,
+                                  Handle<Object> key) {
+  if (key->IsSymbol()) {
+    return CallICBase::LoadFunction(state, object, Handle<String>::cast(key));
+  }
+
+  if (object->IsUndefined() || object->IsNull()) {
+    return TypeError("non_object_property_call", object, key);
+  }
+
+  if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
+    ReceiverToObject(object);
+  } else {
+    if (FLAG_use_ic && state != MEGAMORPHIC && !object->IsAccessCheckNeeded()) {
+      int argc = target()->arguments_count();
+      InLoopFlag in_loop = target()->ic_in_loop();
+      Object* code = StubCache::ComputeCallMegamorphic(
+          argc, in_loop, Code::KEYED_CALL_IC);
+      if (!code->IsFailure()) {
+        set_target(Code::cast(code));
+      }
+    }
+  }
+  Object* result = Runtime::GetObjectProperty(object, key);
+  if (result->IsJSFunction()) return result;
+  result = TryCallAsFunction(result);
+  return result->IsJSFunction() ?
+      result : TypeError("property_not_function", object, key);
+}
+
+
 Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) {
   // If the object is undefined or null it's illegal to try to get any
   // of its properties; throw a TypeError in that case.
@@ -1293,7 +1348,22 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
 // Static IC stub generators.
 //
 
-// Used from ic_<arch>.cc.
+static Object* CompileFunction(Object* result,
+                               Handle<Object> object,
+                               InLoopFlag in_loop) {
+  // Compile now with optimization.
+  HandleScope scope;
+  Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result));
+  if (in_loop == IN_LOOP) {
+    CompileLazyInLoop(function, object, CLEAR_EXCEPTION);
+  } else {
+    CompileLazy(function, object, CLEAR_EXCEPTION);
+  }
+  return *function;
+}
+
+
+// Used from ic-<arch>.cc.
 Object* CallIC_Miss(Arguments args) {
   NoHandleAllocation na;
   ASSERT(args.length() == 2);
@@ -1312,21 +1382,27 @@ Object* CallIC_Miss(Arguments args) {
   if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
     return result;
   }
+  return CompileFunction(result, args.at<Object>(0), ic.target()->ic_in_loop());
+}
 
-  // Compile now with optimization.
-  HandleScope scope;
-  Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result));
-  InLoopFlag in_loop = ic.target()->ic_in_loop();
-  if (in_loop == IN_LOOP) {
-    CompileLazyInLoop(function, args.at<Object>(0), CLEAR_EXCEPTION);
-  } else {
-    CompileLazy(function, args.at<Object>(0), CLEAR_EXCEPTION);
+
+// Used from ic-<arch>.cc.
+Object* KeyedCallIC_Miss(Arguments args) {
+  NoHandleAllocation na;
+  ASSERT(args.length() == 2);
+  KeyedCallIC ic;
+  IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+  Object* result =
+      ic.LoadFunction(state, args.at<Object>(0), args.at<Object>(1));
+
+  if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
+    return result;
   }
-  return *function;
+  return CompileFunction(result, args.at<Object>(0), ic.target()->ic_in_loop());
 }
 
 
-// Used from ic_<arch>.cc.
+// Used from ic-<arch>.cc.
 Object* LoadIC_Miss(Arguments args) {
   NoHandleAllocation na;
   ASSERT(args.length() == 2);
@@ -1336,7 +1412,7 @@ Object* LoadIC_Miss(Arguments args) {
 }
 
 
-// Used from ic_<arch>.cc
+// Used from ic-<arch>.cc
 Object* KeyedLoadIC_Miss(Arguments args) {
   NoHandleAllocation na;
   ASSERT(args.length() == 2);
@@ -1346,7 +1422,7 @@ Object* KeyedLoadIC_Miss(Arguments args) {
 }
 
 
-// Used from ic_<arch>.cc.
+// Used from ic-<arch>.cc.
 Object* StoreIC_Miss(Arguments args) {
   NoHandleAllocation na;
   ASSERT(args.length() == 3);
@@ -1404,7 +1480,7 @@ Object* SharedStoreIC_ExtendStorage(Arguments args) {
 }
 
 
-// Used from ic_<arch>.cc.
+// Used from ic-<arch>.cc.
 Object* KeyedStoreIC_Miss(Arguments args) {
   NoHandleAllocation na;
   ASSERT(args.length() == 3);
index a7ff6e671e01a37225dd6e5277ab43d66574acfb..a9ad28b7fac68e1bf98b470b8dbe55b91fa0612e 100644 (file)
--- a/src/ic.h
+++ b/src/ic.h
@@ -44,6 +44,7 @@ enum DictionaryCheck { CHECK_DICTIONARY, DICTIONARY_CHECK_DONE };
   ICU(LoadIC_Miss)                                    \
   ICU(KeyedLoadIC_Miss)                               \
   ICU(CallIC_Miss)                                    \
+  ICU(KeyedCallIC_Miss)                               \
   ICU(StoreIC_Miss)                                   \
   ICU(StoreIC_ArrayLength)                            \
   ICU(SharedStoreIC_ExtendStorage)                    \
@@ -147,7 +148,7 @@ class IC {
 
   static Failure* TypeError(const char* type,
                             Handle<Object> object,
-                            Handle<String> name);
+                            Handle<Object> key);
   static Failure* ReferenceError(const char* type, Handle<String> name);
 
   // Access the target code for the given IC address.
@@ -184,22 +185,16 @@ class IC_Utility {
 };
 
 
-class CallIC: public IC {
- public:
-  CallIC() : IC(EXTRA_CALL_FRAME) { ASSERT(target()->is_call_stub()); }
+class CallICBase: public IC {
+ protected:
+  explicit CallICBase(Code::Kind kind) : IC(EXTRA_CALL_FRAME), kind_(kind) {}
 
+ public:
   Object* LoadFunction(State state, Handle<Object> object, Handle<String> name);
 
+ protected:
+  Code::Kind kind_;
 
-  // Code generator routines.
-  static void GenerateInitialize(MacroAssembler* masm, int argc) {
-    GenerateMiss(masm, argc);
-  }
-  static void GenerateMiss(MacroAssembler* masm, int argc);
-  static void GenerateMegamorphic(MacroAssembler* masm, int argc);
-  static void GenerateNormal(MacroAssembler* masm, int argc);
-
- private:
   // Update the inline cache and the global stub cache based on the
   // lookup result.
   void UpdateCaches(LookupResult* lookup,
@@ -219,6 +214,38 @@ class CallIC: public IC {
 };
 
 
+class CallIC: public CallICBase {
+ public:
+  CallIC() : CallICBase(Code::CALL_IC) { ASSERT(target()->is_call_stub()); }
+
+  // Code generator routines.
+  static void GenerateInitialize(MacroAssembler* masm, int argc) {
+    GenerateMiss(masm, argc);
+  }
+  static void GenerateMiss(MacroAssembler* masm, int argc);
+  static void GenerateMegamorphic(MacroAssembler* masm, int argc);
+  static void GenerateNormal(MacroAssembler* masm, int argc);
+};
+
+
+class KeyedCallIC: public CallICBase {
+ public:
+  KeyedCallIC() : CallICBase(Code::KEYED_CALL_IC) {
+    ASSERT(target()->is_keyed_call_stub());
+  }
+
+  Object* LoadFunction(State state, Handle<Object> object, Handle<Object> key);
+
+  // Code generator routines.
+  static void GenerateInitialize(MacroAssembler* masm, int argc) {
+    GenerateMiss(masm, argc);
+  }
+  static void GenerateMiss(MacroAssembler* masm, int argc);
+  static void GenerateMegamorphic(MacroAssembler* masm, int argc);
+  static void GenerateNormal(MacroAssembler* masm, int argc);
+};
+
+
 class LoadIC: public IC {
  public:
   LoadIC() : IC(NO_EXTRA_FRAME) { ASSERT(target()->is_load_stub()); }
index f48b3589866f55844b916ddc93dffd1361527fca..ada73cbe66ad8dcd952ecda6aaa3927bffa651a4 100644 (file)
@@ -1295,6 +1295,10 @@ void Logger::LogCodeObject(Object* object) {
         description = "A call IC from the snapshot";
         tag = Logger::CALL_IC_TAG;
         break;
+      case Code::KEYED_CALL_IC:
+        description = "A keyed call IC from the snapshot";
+        tag = Logger::KEYED_CALL_IC_TAG;
+        break;
     }
     PROFILE(CodeCreateEvent(tag, code_object, description));
   }
index a1441ac16317d98f7a2b5cb4620c16c9b8757848..160072dec572c5fa6d782734be377850bdd8fc43 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -106,6 +106,18 @@ class CompressionHelper;
   V(CALL_MISS_TAG,                  "CallMiss",               "cm")       \
   V(CALL_NORMAL_TAG,                "CallNormal",             "cn")       \
   V(CALL_PRE_MONOMORPHIC_TAG,       "CallPreMonomorphic",     "cpm")      \
+  V(KEYED_CALL_DEBUG_BREAK_TAG,     "KeyedCallDebugBreak",    "kcdb")     \
+  V(KEYED_CALL_DEBUG_PREPARE_STEP_IN_TAG,                                 \
+    "KeyedCallDebugPrepareStepIn",                                        \
+    "kcdbsi")                                                             \
+  V(KEYED_CALL_IC_TAG,              "KeyedCallIC",            "kcic")     \
+  V(KEYED_CALL_INITIALIZE_TAG,      "KeyedCallInitialize",    "kci")      \
+  V(KEYED_CALL_MEGAMORPHIC_TAG,     "KeyedCallMegamorphic",   "kcmm")     \
+  V(KEYED_CALL_MISS_TAG,            "KeyedCallMiss",          "kcm")      \
+  V(KEYED_CALL_NORMAL_TAG,          "KeyedCallNormal",        "kcn")      \
+  V(KEYED_CALL_PRE_MONOMORPHIC_TAG,                                       \
+    "KeyedCallPreMonomorphic",                                            \
+    "kcpm")                                                               \
   V(CALLBACK_TAG,                   "Callback",               "cb")       \
   V(EVAL_TAG,                       "Eval",                   "e")        \
   V(FUNCTION_TAG,                   "Function",               "f")        \
index fe33e7ef5110a8ac6aa57e086cc0e5cbb2c603a1..c9fcaa8d649b70e1a3cf817f417118bf4f37494d 100644 (file)
@@ -2196,7 +2196,8 @@ Code::Flags Code::flags() {
 void Code::set_flags(Code::Flags flags) {
   STATIC_ASSERT(Code::NUMBER_OF_KINDS <= (kFlagsKindMask >> kFlagsKindShift)+1);
   // Make sure that all call stubs have an arguments count.
-  ASSERT(ExtractKindFromFlags(flags) != CALL_IC ||
+  ASSERT((ExtractKindFromFlags(flags) != CALL_IC &&
+          ExtractKindFromFlags(flags) != KEYED_CALL_IC) ||
          ExtractArgumentsCountFromFlags(flags) >= 0);
   WRITE_INT_FIELD(this, kFlagsOffset, flags);
 }
@@ -2232,7 +2233,7 @@ PropertyType Code::type() {
 
 
 int Code::arguments_count() {
-  ASSERT(is_call_stub() || kind() == STUB);
+  ASSERT(is_call_stub() || is_keyed_call_stub() || kind() == STUB);
   return ExtractArgumentsCountFromFlags(flags());
 }
 
index 1aa60af4f88a570eb35796d10c25e2f3a2164276..1deacb514096cbe7b3cb3297178414f1e08bf103 100644 (file)
@@ -5402,6 +5402,7 @@ const char* Code::Kind2String(Kind kind) {
     case STORE_IC: return "STORE_IC";
     case KEYED_STORE_IC: return "KEYED_STORE_IC";
     case CALL_IC: return "CALL_IC";
+    case KEYED_CALL_IC: return "KEYED_CALL_IC";
     case BINARY_OP_IC: return "BINARY_OP_IC";
   }
   UNREACHABLE();
index 7de8b2a902e32c5b9cd9d083f9e6c6969388966b..c69540ed1b851bcea62dd438331f07068311ab42 100644 (file)
@@ -2669,6 +2669,7 @@ class Code: public HeapObject {
     LOAD_IC,
     KEYED_LOAD_IC,
     CALL_IC,
+    KEYED_CALL_IC,
     STORE_IC,
     KEYED_STORE_IC,
     BINARY_OP_IC,
@@ -2723,6 +2724,7 @@ class Code: public HeapObject {
   inline bool is_store_stub() { return kind() == STORE_IC; }
   inline bool is_keyed_store_stub() { return kind() == KEYED_STORE_IC; }
   inline bool is_call_stub() { return kind() == CALL_IC; }
+  inline bool is_keyed_call_stub() { return kind() == KEYED_CALL_IC; }
 
   // [major_key]: For kind STUB or BINARY_OP_IC, the major key.
   inline CodeStub::Major major_key();
index 1d868e9ac731d32a7c354bf8b8b8c6098ce322ec..3c495ba54e518de44bfaa5d6a1b5ba8b283b724d 100644 (file)
@@ -1457,6 +1457,7 @@ static void ReportCodeKindStatistics() {
       CASE(STORE_IC);
       CASE(KEYED_STORE_IC);
       CASE(CALL_IC);
+      CASE(KEYED_CALL_IC);
       CASE(BINARY_OP_IC);
     }
   }
index 6ebe495f16830b276ba0a115a1d1efe69a5ee018..397988ae46757bf8da5beaa36241d647b2342169 100644 (file)
@@ -441,9 +441,12 @@ Object* StubCache::ComputeKeyedStoreField(String* name, JSObject* receiver,
   return code;
 }
 
+#define CALL_LOGGER_TAG(kind, type) \
+    (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type)
 
 Object* StubCache::ComputeCallConstant(int argc,
                                        InLoopFlag in_loop,
+                                       Code::Kind kind,
                                        String* name,
                                        Object* object,
                                        JSObject* holder,
@@ -462,7 +465,7 @@ Object* StubCache::ComputeCallConstant(int argc,
   }
 
   Code::Flags flags =
-      Code::ComputeMonomorphicFlags(Code::CALL_IC,
+      Code::ComputeMonomorphicFlags(kind,
                                     CONSTANT_FUNCTION,
                                     in_loop,
                                     argc);
@@ -474,11 +477,12 @@ Object* StubCache::ComputeCallConstant(int argc,
     // caches.
     if (!function->is_compiled()) return Failure::InternalError();
     // Compile the stub - only create stubs for fully compiled functions.
-    CallStubCompiler compiler(argc, in_loop);
+    CallStubCompiler compiler(argc, in_loop, kind);
     code = compiler.CompileCallConstant(object, holder, function, name, check);
     if (code->IsFailure()) return code;
     ASSERT_EQ(flags, Code::cast(code)->flags());
-    PROFILE(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
+    PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG),
+                            Code::cast(code), name));
     Object* result = map->UpdateCodeCache(name, Code::cast(code));
     if (result->IsFailure()) return result;
   }
@@ -488,6 +492,7 @@ Object* StubCache::ComputeCallConstant(int argc,
 
 Object* StubCache::ComputeCallField(int argc,
                                     InLoopFlag in_loop,
+                                    Code::Kind kind,
                                     String* name,
                                     Object* object,
                                     JSObject* holder,
@@ -502,20 +507,21 @@ Object* StubCache::ComputeCallField(int argc,
     object = holder;
   }
 
-  Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
+  Code::Flags flags = Code::ComputeMonomorphicFlags(kind,
                                                     FIELD,
                                                     in_loop,
                                                     argc);
   Object* code = map->FindInCodeCache(name, flags);
   if (code->IsUndefined()) {
-    CallStubCompiler compiler(argc, in_loop);
+    CallStubCompiler compiler(argc, in_loop, kind);
     code = compiler.CompileCallField(JSObject::cast(object),
                                      holder,
                                      index,
                                      name);
     if (code->IsFailure()) return code;
     ASSERT_EQ(flags, Code::cast(code)->flags());
-    PROFILE(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
+    PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG),
+                            Code::cast(code), name));
     Object* result = map->UpdateCodeCache(name, Code::cast(code));
     if (result->IsFailure()) return result;
   }
@@ -524,6 +530,7 @@ Object* StubCache::ComputeCallField(int argc,
 
 
 Object* StubCache::ComputeCallInterceptor(int argc,
+                                          Code::Kind kind,
                                           String* name,
                                           Object* object,
                                           JSObject* holder) {
@@ -539,19 +546,20 @@ Object* StubCache::ComputeCallInterceptor(int argc,
   }
 
   Code::Flags flags =
-      Code::ComputeMonomorphicFlags(Code::CALL_IC,
+      Code::ComputeMonomorphicFlags(kind,
                                     INTERCEPTOR,
                                     NOT_IN_LOOP,
                                     argc);
   Object* code = map->FindInCodeCache(name, flags);
   if (code->IsUndefined()) {
-    CallStubCompiler compiler(argc, NOT_IN_LOOP);
+    CallStubCompiler compiler(argc, NOT_IN_LOOP, kind);
     code = compiler.CompileCallInterceptor(JSObject::cast(object),
                                            holder,
                                            name);
     if (code->IsFailure()) return code;
     ASSERT_EQ(flags, Code::cast(code)->flags());
-    PROFILE(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
+    PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG),
+                            Code::cast(code), name));
     Object* result = map->UpdateCodeCache(name, Code::cast(code));
     if (result->IsFailure()) return result;
   }
@@ -561,9 +569,10 @@ Object* StubCache::ComputeCallInterceptor(int argc,
 
 Object* StubCache::ComputeCallNormal(int argc,
                                      InLoopFlag in_loop,
+                                     Code::Kind kind,
                                      String* name,
                                      JSObject* receiver) {
-  Object* code = ComputeCallNormal(argc, in_loop);
+  Object* code = ComputeCallNormal(argc, in_loop, kind);
   if (code->IsFailure()) return code;
   return Set(name, receiver->map(), Code::cast(code));
 }
@@ -571,13 +580,17 @@ Object* StubCache::ComputeCallNormal(int argc,
 
 Object* StubCache::ComputeCallGlobal(int argc,
                                      InLoopFlag in_loop,
+                                     Code::Kind kind,
                                      String* name,
                                      JSObject* receiver,
                                      GlobalObject* holder,
                                      JSGlobalPropertyCell* cell,
                                      JSFunction* function) {
   Code::Flags flags =
-      Code::ComputeMonomorphicFlags(Code::CALL_IC, NORMAL, in_loop, argc);
+      Code::ComputeMonomorphicFlags(kind,
+                                    NORMAL,
+                                    in_loop,
+                                    argc);
   Object* code = receiver->map()->FindInCodeCache(name, flags);
   if (code->IsUndefined()) {
     // If the function hasn't been compiled yet, we cannot do it now
@@ -585,11 +598,12 @@ Object* StubCache::ComputeCallGlobal(int argc,
     // internal error which will make sure we do not update any
     // caches.
     if (!function->is_compiled()) return Failure::InternalError();
-    CallStubCompiler compiler(argc, in_loop);
+    CallStubCompiler compiler(argc, in_loop, kind);
     code = compiler.CompileCallGlobal(receiver, holder, cell, function, name);
     if (code->IsFailure()) return code;
     ASSERT_EQ(flags, Code::cast(code)->flags());
-    PROFILE(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
+    PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG),
+                            Code::cast(code), name));
     Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
     if (result->IsFailure()) return result;
   }
@@ -637,9 +651,11 @@ static Object* FillCache(Object* code) {
 }
 
 
-Code* StubCache::FindCallInitialize(int argc, InLoopFlag in_loop) {
+Code* StubCache::FindCallInitialize(int argc,
+                                    InLoopFlag in_loop,
+                                    Code::Kind kind) {
   Code::Flags flags =
-      Code::ComputeFlags(Code::CALL_IC, in_loop, UNINITIALIZED, NORMAL, argc);
+      Code::ComputeFlags(kind, in_loop, UNINITIALIZED, NORMAL, argc);
   Object* result = ProbeCache(flags);
   ASSERT(!result->IsUndefined());
   // This might be called during the marking phase of the collector
@@ -648,9 +664,11 @@ Code* StubCache::FindCallInitialize(int argc, InLoopFlag in_loop) {
 }
 
 
-Object* StubCache::ComputeCallInitialize(int argc, InLoopFlag in_loop) {
+Object* StubCache::ComputeCallInitialize(int argc,
+                                         InLoopFlag in_loop,
+                                         Code::Kind kind) {
   Code::Flags flags =
-      Code::ComputeFlags(Code::CALL_IC, in_loop, UNINITIALIZED, NORMAL, argc);
+      Code::ComputeFlags(kind, in_loop, UNINITIALIZED, NORMAL, argc);
   Object* probe = ProbeCache(flags);
   if (!probe->IsUndefined()) return probe;
   StubCompiler compiler;
@@ -658,9 +676,11 @@ Object* StubCache::ComputeCallInitialize(int argc, InLoopFlag in_loop) {
 }
 
 
-Object* StubCache::ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop) {
+Object* StubCache::ComputeCallPreMonomorphic(int argc,
+                                             InLoopFlag in_loop,
+                                             Code::Kind kind) {
   Code::Flags flags =
-      Code::ComputeFlags(Code::CALL_IC, in_loop, PREMONOMORPHIC, NORMAL, argc);
+      Code::ComputeFlags(kind, in_loop, PREMONOMORPHIC, NORMAL, argc);
   Object* probe = ProbeCache(flags);
   if (!probe->IsUndefined()) return probe;
   StubCompiler compiler;
@@ -668,9 +688,11 @@ Object* StubCache::ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop) {
 }
 
 
-Object* StubCache::ComputeCallNormal(int argc, InLoopFlag in_loop) {
+Object* StubCache::ComputeCallNormal(int argc,
+                                     InLoopFlag in_loop,
+                                     Code::Kind kind) {
   Code::Flags flags =
-      Code::ComputeFlags(Code::CALL_IC, in_loop, MONOMORPHIC, NORMAL, argc);
+      Code::ComputeFlags(kind, in_loop, MONOMORPHIC, NORMAL, argc);
   Object* probe = ProbeCache(flags);
   if (!probe->IsUndefined()) return probe;
   StubCompiler compiler;
@@ -678,9 +700,11 @@ Object* StubCache::ComputeCallNormal(int argc, InLoopFlag in_loop) {
 }
 
 
-Object* StubCache::ComputeCallMegamorphic(int argc, InLoopFlag in_loop) {
+Object* StubCache::ComputeCallMegamorphic(int argc,
+                                          InLoopFlag in_loop,
+                                          Code::Kind kind) {
   Code::Flags flags =
-      Code::ComputeFlags(Code::CALL_IC, in_loop, MEGAMORPHIC, NORMAL, argc);
+      Code::ComputeFlags(kind, in_loop, MEGAMORPHIC, NORMAL, argc);
   Object* probe = ProbeCache(flags);
   if (!probe->IsUndefined()) return probe;
   StubCompiler compiler;
@@ -688,9 +712,11 @@ Object* StubCache::ComputeCallMegamorphic(int argc, InLoopFlag in_loop) {
 }
 
 
-Object* StubCache::ComputeCallMiss(int argc) {
-  Code::Flags flags =
-      Code::ComputeFlags(Code::STUB, NOT_IN_LOOP, MEGAMORPHIC, NORMAL, argc);
+Object* StubCache::ComputeCallMiss(int argc, Code::Kind kind) {
+  // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs
+  // and monomorphic stubs are not mixed up together in the stub cache.
+  Code::Flags flags = Code::ComputeFlags(
+     kind, NOT_IN_LOOP, MONOMORPHIC_PROTOTYPE_FAILURE, NORMAL, argc);
   Object* probe = ProbeCache(flags);
   if (!probe->IsUndefined()) return probe;
   StubCompiler compiler;
@@ -699,9 +725,9 @@ Object* StubCache::ComputeCallMiss(int argc) {
 
 
 #ifdef ENABLE_DEBUGGER_SUPPORT
-Object* StubCache::ComputeCallDebugBreak(int argc) {
+Object* StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) {
   Code::Flags flags =
-      Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, DEBUG_BREAK, NORMAL, argc);
+      Code::ComputeFlags(kind, NOT_IN_LOOP, DEBUG_BREAK, NORMAL, argc);
   Object* probe = ProbeCache(flags);
   if (!probe->IsUndefined()) return probe;
   StubCompiler compiler;
@@ -709,9 +735,9 @@ Object* StubCache::ComputeCallDebugBreak(int argc) {
 }
 
 
-Object* StubCache::ComputeCallDebugPrepareStepIn(int argc) {
+Object* StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) {
   Code::Flags flags =
-      Code::ComputeFlags(Code::CALL_IC,
+      Code::ComputeFlags(kind,
                          NOT_IN_LOOP,
                          DEBUG_PREPARE_STEP_IN,
                          NORMAL,
@@ -758,8 +784,8 @@ void StubCache::Clear() {
 
 
 // Support function for computing call IC miss stubs.
-Handle<Code> ComputeCallMiss(int argc) {
-  CALL_HEAP_FUNCTION(StubCache::ComputeCallMiss(argc), Code);
+Handle<Code> ComputeCallMiss(int argc, Code::Kind kind) {
+  CALL_HEAP_FUNCTION(StubCache::ComputeCallMiss(argc, kind), Code);
 }
 
 
@@ -966,13 +992,18 @@ Object* KeyedLoadPropertyWithInterceptor(Arguments args) {
 Object* StubCompiler::CompileCallInitialize(Code::Flags flags) {
   HandleScope scope;
   int argc = Code::ExtractArgumentsCountFromFlags(flags);
-  CallIC::GenerateInitialize(masm(), argc);
+  Code::Kind kind = Code::ExtractKindFromFlags(flags);
+  if (kind == Code::CALL_IC) {
+    CallIC::GenerateInitialize(masm(), argc);
+  } else {
+    KeyedCallIC::GenerateInitialize(masm(), argc);
+  }
   Object* result = GetCodeWithFlags(flags, "CompileCallInitialize");
   if (!result->IsFailure()) {
     Counters::call_initialize_stubs.Increment();
     Code* code = Code::cast(result);
     USE(code);
-    PROFILE(CodeCreateEvent(Logger::CALL_INITIALIZE_TAG,
+    PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG),
                             code, code->arguments_count()));
   }
   return result;
@@ -984,13 +1015,18 @@ Object* StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) {
   int argc = Code::ExtractArgumentsCountFromFlags(flags);
   // The code of the PreMonomorphic stub is the same as the code
   // of the Initialized stub.  They just differ on the code object flags.
-  CallIC::GenerateInitialize(masm(), argc);
+  Code::Kind kind = Code::ExtractKindFromFlags(flags);
+  if (kind == Code::CALL_IC) {
+    CallIC::GenerateInitialize(masm(), argc);
+  } else {
+    KeyedCallIC::GenerateInitialize(masm(), argc);
+  }
   Object* result = GetCodeWithFlags(flags, "CompileCallPreMonomorphic");
   if (!result->IsFailure()) {
     Counters::call_premonomorphic_stubs.Increment();
     Code* code = Code::cast(result);
     USE(code);
-    PROFILE(CodeCreateEvent(Logger::CALL_PRE_MONOMORPHIC_TAG,
+    PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG),
                             code, code->arguments_count()));
   }
   return result;
@@ -1000,13 +1036,18 @@ Object* StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) {
 Object* StubCompiler::CompileCallNormal(Code::Flags flags) {
   HandleScope scope;
   int argc = Code::ExtractArgumentsCountFromFlags(flags);
-  CallIC::GenerateNormal(masm(), argc);
+  Code::Kind kind = Code::ExtractKindFromFlags(flags);
+  if (kind == Code::CALL_IC) {
+    CallIC::GenerateNormal(masm(), argc);
+  } else {
+    KeyedCallIC::GenerateNormal(masm(), argc);
+  }
   Object* result = GetCodeWithFlags(flags, "CompileCallNormal");
   if (!result->IsFailure()) {
     Counters::call_normal_stubs.Increment();
     Code* code = Code::cast(result);
     USE(code);
-    PROFILE(CodeCreateEvent(Logger::CALL_NORMAL_TAG,
+    PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG),
                             code, code->arguments_count()));
   }
   return result;
@@ -1016,13 +1057,19 @@ Object* StubCompiler::CompileCallNormal(Code::Flags flags) {
 Object* StubCompiler::CompileCallMegamorphic(Code::Flags flags) {
   HandleScope scope;
   int argc = Code::ExtractArgumentsCountFromFlags(flags);
-  CallIC::GenerateMegamorphic(masm(), argc);
+  Code::Kind kind = Code::ExtractKindFromFlags(flags);
+  if (kind == Code::CALL_IC) {
+    CallIC::GenerateMegamorphic(masm(), argc);
+  } else {
+    KeyedCallIC::GenerateMegamorphic(masm(), argc);
+  }
+
   Object* result = GetCodeWithFlags(flags, "CompileCallMegamorphic");
   if (!result->IsFailure()) {
     Counters::call_megamorphic_stubs.Increment();
     Code* code = Code::cast(result);
     USE(code);
-    PROFILE(CodeCreateEvent(Logger::CALL_MEGAMORPHIC_TAG,
+    PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG),
                             code, code->arguments_count()));
   }
   return result;
@@ -1032,13 +1079,18 @@ Object* StubCompiler::CompileCallMegamorphic(Code::Flags flags) {
 Object* StubCompiler::CompileCallMiss(Code::Flags flags) {
   HandleScope scope;
   int argc = Code::ExtractArgumentsCountFromFlags(flags);
-  CallIC::GenerateMiss(masm(), argc);
+  Code::Kind kind = Code::ExtractKindFromFlags(flags);
+  if (kind == Code::CALL_IC) {
+    CallIC::GenerateMiss(masm(), argc);
+  } else {
+    KeyedCallIC::GenerateMiss(masm(), argc);
+  }
   Object* result = GetCodeWithFlags(flags, "CompileCallMiss");
   if (!result->IsFailure()) {
     Counters::call_megamorphic_stubs.Increment();
     Code* code = Code::cast(result);
     USE(code);
-    PROFILE(CodeCreateEvent(Logger::CALL_MISS_TAG,
+    PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG),
                             code, code->arguments_count()));
   }
   return result;
@@ -1053,7 +1105,8 @@ Object* StubCompiler::CompileCallDebugBreak(Code::Flags flags) {
   if (!result->IsFailure()) {
     Code* code = Code::cast(result);
     USE(code);
-    PROFILE(CodeCreateEvent(Logger::CALL_DEBUG_BREAK_TAG,
+    Code::Kind kind = Code::ExtractKindFromFlags(flags);
+    PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_DEBUG_BREAK_TAG),
                             code, code->arguments_count()));
   }
   return result;
@@ -1065,18 +1118,26 @@ Object* StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) {
   // Use the same code for the the step in preparations as we do for
   // the miss case.
   int argc = Code::ExtractArgumentsCountFromFlags(flags);
-  CallIC::GenerateMiss(masm(), argc);
+  Code::Kind kind = Code::ExtractKindFromFlags(flags);
+  if (kind == Code::CALL_IC) {
+    CallIC::GenerateMiss(masm(), argc);
+  } else {
+    KeyedCallIC::GenerateMiss(masm(), argc);
+  }
   Object* result = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn");
   if (!result->IsFailure()) {
     Code* code = Code::cast(result);
     USE(code);
-    PROFILE(CodeCreateEvent(Logger::CALL_DEBUG_PREPARE_STEP_IN_TAG,
-                            code, code->arguments_count()));
+    PROFILE(CodeCreateEvent(
+        CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG),
+        code,
+        code->arguments_count()));
   }
   return result;
 }
 #endif
 
+#undef CALL_LOGGER_TAG
 
 Object* StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) {
   // Check for allocation failures during stub compilation.
@@ -1167,7 +1228,7 @@ Object* CallStubCompiler::CompileCustomCall(int generator_id,
 
 Object* CallStubCompiler::GetCode(PropertyType type, String* name) {
   int argc = arguments_.immediate();
-  Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
+  Code::Flags flags = Code::ComputeMonomorphicFlags(kind_,
                                                     type,
                                                     in_loop_,
                                                     argc);
index 68187ffae0214ca5d1d10b0f74972380ad3f29f9..fcfffcfc5d012ed10dd1e93a612e340f67d9db57 100644 (file)
@@ -142,6 +142,7 @@ class StubCache : public AllStatic {
 
   static Object* ComputeCallField(int argc,
                                   InLoopFlag in_loop,
+                                  Code::Kind,
                                   String* name,
                                   Object* object,
                                   JSObject* holder,
@@ -149,6 +150,7 @@ class StubCache : public AllStatic {
 
   static Object* ComputeCallConstant(int argc,
                                      InLoopFlag in_loop,
+                                     Code::Kind,
                                      String* name,
                                      Object* object,
                                      JSObject* holder,
@@ -156,16 +158,19 @@ class StubCache : public AllStatic {
 
   static Object* ComputeCallNormal(int argc,
                                    InLoopFlag in_loop,
+                                   Code::Kind,
                                    String* name,
                                    JSObject* receiver);
 
   static Object* ComputeCallInterceptor(int argc,
+                                        Code::Kind,
                                         String* name,
                                         Object* object,
                                         JSObject* holder);
 
   static Object* ComputeCallGlobal(int argc,
                                    InLoopFlag in_loop,
+                                   Code::Kind,
                                    String* name,
                                    JSObject* receiver,
                                    GlobalObject* holder,
@@ -174,18 +179,33 @@ class StubCache : public AllStatic {
 
   // ---
 
-  static Object* ComputeCallInitialize(int argc, InLoopFlag in_loop);
-  static Object* ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop);
-  static Object* ComputeCallNormal(int argc, InLoopFlag in_loop);
-  static Object* ComputeCallMegamorphic(int argc, InLoopFlag in_loop);
-  static Object* ComputeCallMiss(int argc);
+  static Object* ComputeCallInitialize(int argc,
+                                       InLoopFlag in_loop,
+                                       Code::Kind kind);
+
+  static Object* ComputeCallPreMonomorphic(int argc,
+                                           InLoopFlag in_loop,
+                                           Code::Kind kind);
+
+  static Object* ComputeCallNormal(int argc,
+                                   InLoopFlag in_loop,
+                                   Code::Kind kind);
+
+  static Object* ComputeCallMegamorphic(int argc,
+                                        InLoopFlag in_loop,
+                                        Code::Kind kind);
+
+  static Object* ComputeCallMiss(int argc, Code::Kind kind);
 
   // Finds the Code object stored in the Heap::non_monomorphic_cache().
-  static Code* FindCallInitialize(int argc, InLoopFlag in_loop);
+  static Code* FindCallInitialize(int argc,
+                                  InLoopFlag in_loop,
+                                  Code::Kind kind);
 
 #ifdef ENABLE_DEBUGGER_SUPPORT
-  static Object* ComputeCallDebugBreak(int argc);
-  static Object* ComputeCallDebugPrepareStepIn(int argc);
+  static Object* ComputeCallDebugBreak(int argc, Code::Kind kind);
+
+  static Object* ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind);
 #endif
 
   static Object* ComputeLazyCompile(int argc);
@@ -197,9 +217,6 @@ class StubCache : public AllStatic {
   // Clear the lookup table (@ mark compact collection).
   static void Clear();
 
-  // Functions for generating stubs at startup.
-  static void GenerateMiss(MacroAssembler* masm);
-
   // Generate code for probing the stub cache table.
   // If extra != no_reg it might be used as am extra scratch register.
   static void GenerateProbe(MacroAssembler* masm,
@@ -318,7 +335,7 @@ Object* KeyedLoadPropertyWithInterceptor(Arguments args);
 
 
 // Support function for computing call IC miss stubs.
-Handle<Code> ComputeCallMiss(int argc);
+Handle<Code> ComputeCallMiss(int argc, Code::Kind kind);
 
 
 // The stub compiler compiles stubs for the stub cache.
@@ -594,8 +611,8 @@ class CallStubCompiler: public StubCompiler {
     kNumCallGenerators
   };
 
-  CallStubCompiler(int argc, InLoopFlag in_loop)
-      : arguments_(argc), in_loop_(in_loop) { }
+  CallStubCompiler(int argc, InLoopFlag in_loop, Code::Kind kind)
+      : arguments_(argc), in_loop_(in_loop), kind_(kind) { }
 
   Object* CompileCallField(JSObject* object,
                            JSObject* holder,
@@ -635,6 +652,7 @@ class CallStubCompiler: public StubCompiler {
  private:
   const ParameterCount arguments_;
   const InLoopFlag in_loop_;
+  const Code::Kind kind_;
 
   const ParameterCount& arguments() { return arguments_; }
 
@@ -643,6 +661,10 @@ class CallStubCompiler: public StubCompiler {
   // Convenience function. Calls GetCode above passing
   // CONSTANT_FUNCTION type and the name of the given function.
   Object* GetCode(JSFunction* function);
+
+  void GenerateNameCheck(String* name, Label* miss);
+
+  void GenerateMissBranch();
 };
 
 
index 242667eded44ced965a1557274e2e3948eb5292d..823a2575f137149de17fe7eff9c819612ff73dd3 100644 (file)
@@ -542,6 +542,8 @@ class CodeGenerator: public AstVisitor {
 
   static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop);
 
+  static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop);
+
   // Declare global variables and functions in the given array of
   // name/value pairs.
   void DeclareGlobals(Handle<FixedArray> pairs);
index 21bb7e85f68e9f8c2c9fad5de49d110162f635f4..e7086a08e2a0987f1947d89813467c44971b42d2 100644 (file)
@@ -1330,6 +1330,21 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
 }
 
 
+void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+  UNREACHABLE();
+}
+
+
+void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+  UNREACHABLE();
+}
+
+
+void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+  UNREACHABLE();
+}
+
+
 // The offset from the inlined patch site to the start of the
 // inlined load instruction.
 const int LoadIC::kOffsetToLoadInstruction = 20;
index eb9942cb4976938dd2eb0c317966e5d51b8d3e7b..cc5447052943d6fc7027a7a336193ba7f18d696f 100644 (file)
@@ -706,6 +706,12 @@ static Object* GenerateCheckPropertyCell(MacroAssembler* masm,
 
 #define __ ACCESS_MASM((masm()))
 
+void CallStubCompiler::GenerateMissBranch() {
+  Handle<Code> ic = ComputeCallMiss(arguments().immediate(), kind_);
+  __ Jump(ic, RelocInfo::CODE_TARGET);
+}
+
+
 Object* CallStubCompiler::CompileCallConstant(Object* object,
                                               JSObject* holder,
                                               JSFunction* function,
@@ -853,8 +859,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
 
   // Handle call cache miss.
   __ bind(&miss_in_smi_check);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ Jump(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(function);
@@ -905,8 +910,7 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
 
   // Handle call cache miss.
   __ bind(&miss);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ Jump(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(FIELD, name);
@@ -1060,8 +1064,7 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
 
   __ bind(&miss);
 
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ jmp(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(function);
@@ -1145,8 +1148,7 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
                                1);
   __ bind(&miss);
 
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ jmp(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(function);
@@ -1229,8 +1231,7 @@ Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
 
   // Handle load cache miss.
   __ bind(&miss);
-  Handle<Code> ic = ComputeCallMiss(argc);
-  __ Jump(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(INTERCEPTOR, name);
@@ -1313,8 +1314,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
   // Handle call cache miss.
   __ bind(&miss);
   __ IncrementCounter(&Counters::call_global_inline_miss, 1);
-  Handle<Code> ic = ComputeCallMiss(arguments().immediate());
-  __ Jump(ic, RelocInfo::CODE_TARGET);
+  GenerateMissBranch();
 
   // Return the generated code.
   return GetCode(NORMAL, name);
index 60b991f468e47d72f1c63453fe117c6f40633254..503ff93b03df24c0a7d0c68243c02dbc5c59b52d 100644 (file)
@@ -7078,6 +7078,163 @@ THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
 }
 
 
+v8::Handle<Value> keyed_call_ic_function;
+
+static v8::Handle<Value> InterceptorKeyedCallICGetter(
+    Local<String> name, const AccessorInfo& info) {
+  ApiTestFuzzer::Fuzz();
+  if (v8_str("x")->Equals(name)) {
+    return keyed_call_ic_function;
+  }
+  return v8::Handle<Value>();
+}
+
+
+// Test the case when we stored cacheable lookup into
+// a stub, but the function name changed (to another cacheable function).
+THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
+  templ->SetNamedPropertyHandler(NoBlockGetterX);
+  LocalContext context;
+  context->Global()->Set(v8_str("o"), templ->NewInstance());
+  v8::Handle<Value> value = CompileRun(
+    "proto = new Object();"
+    "proto.y = function(x) { return x + 1; };"
+    "proto.z = function(x) { return x - 1; };"
+    "o.__proto__ = proto;"
+    "var result = 0;"
+    "var method = 'y';"
+    "for (var i = 0; i < 10; i++) {"
+    "  if (i == 5) { method = 'z'; };"
+    "  result += o[method](41);"
+    "}");
+  CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+
+// Test the case when we stored cacheable lookup into
+// a stub, but the function name changed (and the new function is present
+// both before and after the interceptor in the prototype chain).
+THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
+  templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
+  LocalContext context;
+  context->Global()->Set(v8_str("proto1"), templ->NewInstance());
+  keyed_call_ic_function =
+      v8_compile("function f(x) { return x - 1; }; f")->Run();
+  v8::Handle<Value> value = CompileRun(
+    "o = new Object();"
+    "proto2 = new Object();"
+    "o.y = function(x) { return x + 1; };"
+    "proto2.y = function(x) { return x + 2; };"
+    "o.__proto__ = proto1;"
+    "proto1.__proto__ = proto2;"
+    "var result = 0;"
+    "var method = 'x';"
+    "for (var i = 0; i < 10; i++) {"
+    "  if (i == 5) { method = 'y'; };"
+    "  result += o[method](41);"
+    "}");
+  CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+
+// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
+// on the global object.
+THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
+  templ->SetNamedPropertyHandler(NoBlockGetterX);
+  LocalContext context;
+  context->Global()->Set(v8_str("o"), templ->NewInstance());
+  v8::Handle<Value> value = CompileRun(
+    "function inc(x) { return x + 1; };"
+    "inc(1);"
+    "function dec(x) { return x - 1; };"
+    "dec(1);"
+    "o.__proto__ = this;"
+    "this.__proto__.x = inc;"
+    "this.__proto__.y = dec;"
+    "var result = 0;"
+    "var method = 'x';"
+    "for (var i = 0; i < 10; i++) {"
+    "  if (i == 5) { method = 'y'; };"
+    "  result += o[method](41);"
+    "}");
+  CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+
+// Test the case when actual function to call sits on global object.
+THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
+  templ_o->SetNamedPropertyHandler(NoBlockGetterX);
+  LocalContext context;
+  context->Global()->Set(v8_str("o"), templ_o->NewInstance());
+
+  v8::Handle<Value> value = CompileRun(
+    "function len(x) { return x.length; };"
+    "o.__proto__ = this;"
+    "var m = 'parseFloat';"
+    "var result = 0;"
+    "for (var i = 0; i < 10; i++) {"
+    "  if (i == 5) {"
+    "    m = 'len';"
+    "    saved_result = result;"
+    "  };"
+    "  result = o[m]('239');"
+    "}");
+  CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
+  CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
+}
+
+// Test the map transition before the interceptor.
+THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
+  templ_o->SetNamedPropertyHandler(NoBlockGetterX);
+  LocalContext context;
+  context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
+
+  v8::Handle<Value> value = CompileRun(
+    "var o = new Object();"
+    "o.__proto__ = proto;"
+    "o.method = function(x) { return x + 1; };"
+    "var m = 'method';"
+    "var result = 0;"
+    "for (var i = 0; i < 10; i++) {"
+    "  if (i == 5) { o.method = function(x) { return x - 1; }; };"
+    "  result += o[m](41);"
+    "}");
+  CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+
+// Test the map transition after the interceptor.
+THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
+  templ_o->SetNamedPropertyHandler(NoBlockGetterX);
+  LocalContext context;
+  context->Global()->Set(v8_str("o"), templ_o->NewInstance());
+
+  v8::Handle<Value> value = CompileRun(
+    "var proto = new Object();"
+    "o.__proto__ = proto;"
+    "proto.method = function(x) { return x + 1; };"
+    "var m = 'method';"
+    "var result = 0;"
+    "for (var i = 0; i < 10; i++) {"
+    "  if (i == 5) { proto.method = function(x) { return x - 1; }; };"
+    "  result += o[m](41);"
+    "}");
+  CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+
 static int interceptor_call_count = 0;
 
 static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
index 61ecfa179794b70cd9831e8847c4e4ad08ca9dad..612e4fc94b5d6458d79dee49638ca64017a9d573 100644 (file)
@@ -397,8 +397,9 @@ Handle<FixedArray> GetDebuggedFunctions() {
 
 
 static Handle<Code> ComputeCallDebugBreak(int argc) {
-  CALL_HEAP_FUNCTION(v8::internal::StubCache::ComputeCallDebugBreak(argc),
-                     Code);
+  CALL_HEAP_FUNCTION(
+      v8::internal::StubCache::ComputeCallDebugBreak(argc, Code::CALL_IC),
+      Code);
 }
 
 
diff --git a/test/mjsunit/keyed-call-ic.js b/test/mjsunit/keyed-call-ic.js
new file mode 100644 (file)
index 0000000..9d82965
--- /dev/null
@@ -0,0 +1,205 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// A test for keyed call ICs.
+
+var toStringName = 'toString';
+var global = this;
+
+function globalFunction1() {
+  return 'function1';
+}
+
+function globalFunction2() {
+  return 'function2';
+}
+
+assertEquals("[object global]", this[toStringName]());
+assertEquals("[object global]", global[toStringName]());
+
+function testGlobals() {
+  assertEquals("[object global]", this[toStringName]());
+  assertEquals("[object global]", global[toStringName]());
+}
+
+testGlobals();
+
+
+function F() {}
+
+F.prototype.one = function() {return 'one'; }
+F.prototype.two = function() {return 'two'; }
+F.prototype.three = function() {return 'three'; }
+
+var keys =
+    ['one', 'one', 'one',  'one', 'two', 'two', 'one', 'three', 'one', 'two'];
+
+function testKeyTransitions() {
+  var i, key, result, message;
+
+  var f = new F();
+
+  // Custom call generators
+  var array = [];
+  for (i = 0; i != 10; i++) {
+    key = (i < 8) ? 'push' : 'pop';
+    array[key](i);
+  }
+
+  assertEquals(6, array.length);
+  for (i = 0; i != array.length; i++) {
+    assertEquals(i, array[i]);
+  }
+
+  for (i = 0; i != 10; i++) {
+    key = (i < 3) ? 'pop' : 'push';
+    array[key](i);
+  }
+
+  assertEquals(10, array.length);
+  for (i = 0; i != array.length; i++) {
+    assertEquals(i, array[i]);
+  }
+
+  var string = 'ABCDEFGHIJ';
+  for (i = 0; i != 10; i++) {
+    key = ((i < 5) ? 'charAt' : 'charCodeAt');
+    result = string[key](i);
+    message = '\'' + string + '\'[\'' + key + '\'](' + i + ')';
+    if (i < 5) {
+      assertEquals(string.charAt(i), result, message);
+    } else {
+      assertEquals(string.charCodeAt(i), result, message);
+    }
+  }
+
+  for (i = 0; i != 10; i++) {
+    key = ((i < 5) ? 'charCodeAt' : 'charAt');
+    result = string[key](i);
+    message = '\'' + string + '\'[\'' + key + '\'](' + i + ')';
+    if (i < 5) {
+      assertEquals(string.charCodeAt(i), result, message);
+    } else {
+      assertEquals(string.charAt(i), result, message);
+    }
+  }
+
+  // Function is a constant property
+  key = 'one';
+  for (i = 0; i != 10; i++) {
+    assertEquals(key, f[key]());
+    if (i == 5) {
+      key = 'two';  // the name change should case a miss
+    }
+  }
+
+  // Function is a fast property
+  f.field = function() { return 'field'; }
+  key = 'field';
+  for (i = 0; i != 10; i++) {
+    assertEquals(key, f[key]());
+    if (i == 5) {
+      key = 'two';  // the name change should case a miss
+    }
+  }
+
+  // Calling on slow case object
+  f.prop = 0;
+  delete f.prop; // force the object to the slow case
+  f.four = function() { return 'four'; }
+  f.five = function() { return 'five'; }
+
+  key = 'four';
+  for (i = 0; i != 10; i++) {
+    assertEquals(key, f[key]());
+    if (i == 5) {
+      key = 'five';
+    }
+  }
+
+  // Calling on global object
+  key = 'globalFunction1';
+  var expect = 'function1';
+  for (i = 0; i != 10; i++) {
+    assertEquals(expect, global[key]());
+    if (i == 5) {
+      key = 'globalFunction2';
+      expect = 'function2';
+    }
+  }
+}
+
+testKeyTransitions();
+
+function testTypeTransitions() {
+  var f = new F();
+  var s = '';
+  var m = 'one';
+  var i;
+
+  s = '';
+  for (i = 0; i != 10; i++) {
+    if (i == 5)  { F.prototype.one = function() { return '1'; } }
+    s += f[m]();
+  }
+  assertEquals("oneoneoneoneone11111", s);
+
+  s = '';
+  for (i = 0; i != 10; i++) {
+    if (i == 5)  { f.__proto__ = { one: function() { return 'I'; } } }
+    s += f[m]();
+  }
+  assertEquals("11111IIIII", s);
+
+  s = '';
+  for (i = 0; i != 10; i++) {
+    if (i == 5)  { f.one = function() { return 'ONE'; } }
+    s += f[m]();
+  }
+  assertEquals("IIIIIONEONEONEONEONE", s);
+
+  m = 'toString';
+
+  s = '';
+  var obj = { toString: function() { return '2'; } };
+  for (i = 0; i != 10; i++) {
+    if (i == 5)  { obj = "TWO"; }
+    s += obj[m]();
+  }
+  assertEquals("22222TWOTWOTWOTWOTWO", s);
+
+  s = '';
+  obj = { toString: function() { return 'ONE'; } };
+  m = 'toString';
+  for (i = 0; i != 10; i++) {
+    if (i == 5)  { obj = 1; }
+    s += obj[m]();
+  }
+  assertEquals("ONEONEONEONEONE11111", s);
+}
+
+testTypeTransitions();