Index: src/ic.cc |
diff --git a/src/ic.cc b/src/ic.cc |
index e312a853874f83d9e5952b6376331a0831f96d8a..c4498f9bbf3379eb164e2b78438db96ce033fa48 100644 |
--- a/src/ic.cc |
+++ b/src/ic.cc |
@@ -23,7 +23,8 @@ char IC::TransitionMarkFromState(IC::State state) { |
case UNINITIALIZED: return '0'; |
case PREMONOMORPHIC: return '.'; |
case MONOMORPHIC: return '1'; |
- case MONOMORPHIC_PROTOTYPE_FAILURE: return '^'; |
+ case PROTOTYPE_FAILURE: |
+ return '^'; |
case POLYMORPHIC: return 'P'; |
case MEGAMORPHIC: return 'N'; |
case GENERIC: return 'G'; |
@@ -236,60 +237,40 @@ static void LookupForRead(Handle<Object> object, |
bool IC::TryRemoveInvalidPrototypeDependentStub(Handle<Object> receiver, |
Handle<String> name) { |
- if (!IsNameCompatibleWithMonomorphicPrototypeFailure(name)) return false; |
- |
- InlineCacheHolderFlag cache_holder = |
- Code::ExtractCacheHolderFromFlags(target()->flags()); |
- |
- switch (cache_holder) { |
- case OWN_MAP: |
- // The stub was generated for JSObject but called for non-JSObject. |
- // IC::GetCodeCacheHolder is not applicable. |
- if (!receiver->IsJSObject()) return false; |
- break; |
- case PROTOTYPE_MAP: |
- // IC::GetCodeCacheHolder is not applicable. |
- PrototypeIterator iter(isolate(), receiver); |
- if (iter.IsAtEnd()) return false; |
- break; |
+ if (!IsNameCompatibleWithPrototypeFailure(name)) return false; |
+ Handle<Map> receiver_map = TypeToMap(*receiver_type(), isolate()); |
+ maybe_handler_ = target()->FindHandlerForMap(*receiver_map); |
+ |
+ // The current map wasn't handled yet. There's no reason to stay monomorphic, |
+ // *unless* we're moving from a deprecated map to its replacement, or |
+ // to a more general elements kind. |
+ // TODO(verwaest): Check if the current map is actually what the old map |
+ // would transition to. |
+ if (maybe_handler_.is_null()) { |
+ if (!receiver_map->IsJSObjectMap()) return false; |
+ Map* first_map = FirstTargetMap(); |
+ if (first_map == NULL) return false; |
+ Handle<Map> old_map(first_map); |
+ if (old_map->is_deprecated()) return true; |
+ if (IsMoreGeneralElementsKindTransition(old_map->elements_kind(), |
+ receiver_map->elements_kind())) { |
+ return true; |
+ } |
+ return false; |
} |
- Handle<Map> map( |
- IC::GetCodeCacheHolder(isolate(), *receiver, cache_holder)->map()); |
- |
- // Decide whether the inline cache failed because of changes to the |
- // receiver itself or changes to one of its prototypes. |
- // |
- // If there are changes to the receiver itself, the map of the |
- // receiver will have changed and the current target will not be in |
- // the receiver map's code cache. Therefore, if the current target |
- // is in the receiver map's code cache, the inline cache failed due |
- // to prototype check failure. |
- int index = map->IndexInCodeCache(*name, *target()); |
- if (index >= 0) { |
- map->RemoveFromCodeCache(*name, *target(), index); |
- // Handlers are stored in addition to the ICs on the map. Remove those, too. |
- TryRemoveInvalidHandlers(map, name); |
- return true; |
- } |
+ CacheHolderFlag flag; |
+ Handle<Map> ic_holder_map( |
+ GetICCacheHolder(*receiver_type(), isolate(), &flag)); |
- // The stub is not in the cache. We've ruled out all other kinds of failure |
- // except for proptotype chain changes, a deprecated map, a map that's |
- // different from the one that the stub expects, elements kind changes, or a |
- // constant global property that will become mutable. Threat all those |
- // situations as prototype failures (stay monomorphic if possible). |
- |
- // If the IC is shared between multiple receivers (slow dictionary mode), then |
- // the map cannot be deprecated and the stub invalidated. |
- if (cache_holder == OWN_MAP) { |
- Map* old_map = FirstTargetMap(); |
- if (old_map == *map) return true; |
- if (old_map != NULL) { |
- if (old_map->is_deprecated()) return true; |
- if (IsMoreGeneralElementsKindTransition(old_map->elements_kind(), |
- map->elements_kind())) { |
- return true; |
- } |
+ ASSERT(flag != kCacheOnReceiver || receiver->IsJSObject()); |
+ ASSERT(flag != kCacheOnPrototype || !receiver->IsJSReceiver()); |
+ ASSERT(flag != kCacheOnPrototypeReceiverIsDictionary); |
+ |
+ if (state() == MONOMORPHIC) { |
+ int index = ic_holder_map->IndexInCodeCache(*name, *target()); |
+ if (index >= 0) { |
+ ic_holder_map->RemoveFromCodeCache(*name, *target(), index); |
} |
} |
@@ -302,25 +283,11 @@ bool IC::TryRemoveInvalidPrototypeDependentStub(Handle<Object> receiver, |
return cell->type()->IsConstant(); |
} |
- return false; |
-} |
- |
- |
-void IC::TryRemoveInvalidHandlers(Handle<Map> map, Handle<String> name) { |
- CodeHandleList handlers; |
- target()->FindHandlers(&handlers); |
- for (int i = 0; i < handlers.length(); i++) { |
- Handle<Code> handler = handlers.at(i); |
- int index = map->IndexInCodeCache(*name, *handler); |
- if (index >= 0) { |
- map->RemoveFromCodeCache(*name, *handler, index); |
- return; |
- } |
- } |
+ return true; |
} |
-bool IC::IsNameCompatibleWithMonomorphicPrototypeFailure(Handle<Object> name) { |
+bool IC::IsNameCompatibleWithPrototypeFailure(Handle<Object> name) { |
if (target()->is_keyed_stub()) { |
// Determine whether the failure is due to a name failure. |
if (!name->IsName()) return false; |
@@ -333,23 +300,17 @@ bool IC::IsNameCompatibleWithMonomorphicPrototypeFailure(Handle<Object> name) { |
void IC::UpdateState(Handle<Object> receiver, Handle<Object> name) { |
+ receiver_type_ = CurrentTypeOf(receiver, isolate()); |
if (!name->IsString()) return; |
- if (state() != MONOMORPHIC) { |
- if (state() == POLYMORPHIC && receiver->IsHeapObject()) { |
- TryRemoveInvalidHandlers( |
- handle(Handle<HeapObject>::cast(receiver)->map()), |
- Handle<String>::cast(name)); |
- } |
- return; |
- } |
+ if (state() != MONOMORPHIC && state() != POLYMORPHIC) return; |
if (receiver->IsUndefined() || receiver->IsNull()) return; |
// Remove the target from the code cache if it became invalid |
// because of changes in the prototype chain to avoid hitting it |
// again. |
- if (TryRemoveInvalidPrototypeDependentStub( |
- receiver, Handle<String>::cast(name)) && |
- TryMarkMonomorphicPrototypeFailure(name)) { |
+ if (TryRemoveInvalidPrototypeDependentStub(receiver, |
+ Handle<String>::cast(name))) { |
+ MarkPrototypeFailure(name); |
return; |
} |
@@ -688,10 +649,10 @@ static bool AddOneReceiverMapIfMissing(MapHandleList* receiver_maps, |
} |
-bool IC::UpdatePolymorphicIC(Handle<HeapType> type, |
- Handle<String> name, |
- Handle<Code> code) { |
+bool IC::UpdatePolymorphicIC(Handle<String> name, Handle<Code> code) { |
if (!code->is_handler()) return false; |
+ if (target()->is_keyed_stub() && state() != PROTOTYPE_FAILURE) return false; |
+ Handle<HeapType> type = receiver_type(); |
TypeHandleList types; |
CodeHandleList handlers; |
@@ -728,18 +689,25 @@ bool IC::UpdatePolymorphicIC(Handle<HeapType> type, |
if (!target()->FindHandlers(&handlers, types.length())) return false; |
number_of_valid_types++; |
- if (handler_to_overwrite >= 0) { |
- handlers.Set(handler_to_overwrite, code); |
- if (!type->NowIs(types.at(handler_to_overwrite))) { |
- types.Set(handler_to_overwrite, type); |
- } |
+ if (number_of_valid_types > 1 && target()->is_keyed_stub()) return false; |
+ Handle<Code> ic; |
+ if (number_of_valid_types == 1) { |
+ ic = isolate()->stub_cache()->ComputeMonomorphicIC(kind(), name, type, code, |
+ extra_ic_state()); |
} else { |
- types.Add(type); |
- handlers.Add(code); |
+ if (handler_to_overwrite >= 0) { |
+ handlers.Set(handler_to_overwrite, code); |
+ if (!type->NowIs(types.at(handler_to_overwrite))) { |
+ types.Set(handler_to_overwrite, type); |
+ } |
+ } else { |
+ types.Add(type); |
+ handlers.Add(code); |
+ } |
+ ic = isolate()->stub_cache()->ComputePolymorphicIC( |
+ kind(), &types, &handlers, number_of_valid_types, name, |
+ extra_ic_state()); |
} |
- |
- Handle<Code> ic = isolate()->stub_cache()->ComputePolymorphicIC( |
- kind(), &types, &handlers, number_of_valid_types, name, extra_ic_state()); |
set_target(*ic); |
return true; |
} |
@@ -787,12 +755,10 @@ template |
Handle<HeapType> IC::MapToType<HeapType>(Handle<Map> map, Isolate* region); |
-void IC::UpdateMonomorphicIC(Handle<HeapType> type, |
- Handle<Code> handler, |
- Handle<String> name) { |
+void IC::UpdateMonomorphicIC(Handle<Code> handler, Handle<String> name) { |
if (!handler->is_handler()) return set_target(*handler); |
Handle<Code> ic = isolate()->stub_cache()->ComputeMonomorphicIC( |
- kind(), name, type, handler, extra_ic_state()); |
+ kind(), name, receiver_type(), handler, extra_ic_state()); |
set_target(*ic); |
} |
@@ -823,19 +789,17 @@ bool IC::IsTransitionOfMonomorphicTarget(Map* source_map, Map* target_map) { |
} |
-void IC::PatchCache(Handle<HeapType> type, |
- Handle<String> name, |
- Handle<Code> code) { |
+void IC::PatchCache(Handle<String> name, Handle<Code> code) { |
switch (state()) { |
case UNINITIALIZED: |
case PREMONOMORPHIC: |
- case MONOMORPHIC_PROTOTYPE_FAILURE: |
- UpdateMonomorphicIC(type, code, name); |
+ UpdateMonomorphicIC(code, name); |
break; |
- case MONOMORPHIC: // Fall through. |
+ case PROTOTYPE_FAILURE: |
+ case MONOMORPHIC: |
case POLYMORPHIC: |
- if (!target()->is_keyed_stub()) { |
- if (UpdatePolymorphicIC(type, name, code)) break; |
+ if (!target()->is_keyed_stub() || state() == PROTOTYPE_FAILURE) { |
+ if (UpdatePolymorphicIC(name, code)) break; |
CopyICToMegamorphicCache(name); |
} |
if (FLAG_compiled_keyed_generic_loads && (kind() == Code::LOAD_IC)) { |
@@ -845,7 +809,7 @@ void IC::PatchCache(Handle<HeapType> type, |
set_target(*megamorphic_stub()); |
// Fall through. |
case MEGAMORPHIC: |
- UpdateMegamorphicCache(*type, *name, *code); |
+ UpdateMegamorphicCache(*receiver_type(), *name, *code); |
break; |
case DEBUG_STUB: |
break; |
@@ -901,14 +865,16 @@ void LoadIC::UpdateCaches(LookupResult* lookup, |
return; |
} |
- Handle<HeapType> type = CurrentTypeOf(object, isolate()); |
Handle<Code> code; |
if (!lookup->IsCacheable()) { |
// Bail out if the result is not cacheable. |
code = slow_stub(); |
} else if (!lookup->IsProperty()) { |
if (kind() == Code::LOAD_IC) { |
- code = isolate()->stub_cache()->ComputeLoadNonexistent(name, type); |
+ code = isolate()->stub_cache()->ComputeLoadNonexistent(name, |
+ receiver_type()); |
+ // TODO(jkummerow/verwaest): Introduce a builtin that handles this case. |
+ if (code.is_null()) code = slow_stub(); |
} else { |
code = slow_stub(); |
} |
@@ -916,7 +882,7 @@ void LoadIC::UpdateCaches(LookupResult* lookup, |
code = ComputeHandler(lookup, object, name); |
} |
- PatchCache(type, name, code); |
+ PatchCache(name, code); |
TRACE_IC("LoadIC", name); |
} |
@@ -933,33 +899,50 @@ Handle<Code> IC::ComputeHandler(LookupResult* lookup, |
Handle<Object> object, |
Handle<String> name, |
Handle<Object> value) { |
- InlineCacheHolderFlag cache_holder = GetCodeCacheForObject(*object); |
- Handle<HeapObject> stub_holder(GetCodeCacheHolder( |
- isolate(), *object, cache_holder)); |
+ bool receiver_is_holder = lookup->ReceiverIsHolder(object); |
+ CacheHolderFlag flag; |
+ Handle<Map> stub_holder_map = IC::GetHandlerCacheHolder( |
+ *receiver_type(), receiver_is_holder, isolate(), &flag); |
Handle<Code> code = isolate()->stub_cache()->FindHandler( |
- name, handle(stub_holder->map()), kind(), cache_holder, |
+ name, stub_holder_map, kind(), flag, |
lookup->holder()->HasFastProperties() ? Code::FAST : Code::NORMAL); |
+ // Use the cached value if it exists, and if it is different from the |
+ // handler that just missed. |
if (!code.is_null()) { |
- return code; |
+ if (!maybe_handler_.is_null() && |
+ !maybe_handler_.ToHandleChecked().is_identical_to(code)) { |
+ return code; |
+ } |
+ if (maybe_handler_.is_null()) { |
+ // maybe_handler_ is only populated for MONOMORPHIC and POLYMORPHIC ICs. |
+ // In MEGAMORPHIC case, check if the handler in the megamorphic stub |
+ // cache (which just missed) is different from the cached handler. |
+ if (state() == MEGAMORPHIC && object->IsHeapObject()) { |
+ Map* map = Handle<HeapObject>::cast(object)->map(); |
+ Code* megamorphic_cached_code = |
+ isolate()->stub_cache()->Get(*name, map, code->flags()); |
+ if (megamorphic_cached_code != *code) return code; |
+ } else { |
+ return code; |
+ } |
+ } |
} |
- code = CompileHandler(lookup, object, name, value, cache_holder); |
+ code = CompileHandler(lookup, object, name, value, flag); |
ASSERT(code->is_handler()); |
if (code->type() != Code::NORMAL) { |
- HeapObject::UpdateMapCodeCache(stub_holder, name, code); |
+ Map::UpdateCodeCache(stub_holder_map, name, code); |
} |
return code; |
} |
-Handle<Code> LoadIC::CompileHandler(LookupResult* lookup, |
- Handle<Object> object, |
- Handle<String> name, |
- Handle<Object> unused, |
- InlineCacheHolderFlag cache_holder) { |
+Handle<Code> LoadIC::CompileHandler(LookupResult* lookup, Handle<Object> object, |
+ Handle<String> name, Handle<Object> unused, |
+ CacheHolderFlag cache_holder) { |
if (object->IsString() && |
String::Equals(isolate()->factory()->length_string(), name)) { |
FieldIndex index = FieldIndex::ForInObjectOffset(String::kLengthOffset); |
@@ -977,14 +960,15 @@ Handle<Code> LoadIC::CompileHandler(LookupResult* lookup, |
} |
} |
- Handle<HeapType> type = CurrentTypeOf(object, isolate()); |
+ Handle<HeapType> type = receiver_type(); |
Handle<JSObject> holder(lookup->holder()); |
+ bool receiver_is_holder = object.is_identical_to(holder); |
LoadStubCompiler compiler(isolate(), kNoExtraICState, cache_holder, kind()); |
switch (lookup->type()) { |
case FIELD: { |
FieldIndex field = lookup->GetFieldIndex(); |
- if (object.is_identical_to(holder)) { |
+ if (receiver_is_holder) { |
return SimpleFieldLoad(field); |
} |
return compiler.CompileLoadField( |
@@ -1003,24 +987,23 @@ Handle<Code> LoadIC::CompileHandler(LookupResult* lookup, |
Handle<Code> code = compiler.CompileLoadGlobal( |
type, global, cell, name, lookup->IsDontDelete()); |
// TODO(verwaest): Move caching of these NORMAL stubs outside as well. |
- Handle<HeapObject> stub_holder(GetCodeCacheHolder( |
- isolate(), *object, cache_holder)); |
- HeapObject::UpdateMapCodeCache(stub_holder, name, code); |
+ CacheHolderFlag flag; |
+ Handle<Map> stub_holder_map = |
+ GetHandlerCacheHolder(*type, receiver_is_holder, isolate(), &flag); |
+ Map::UpdateCodeCache(stub_holder_map, name, code); |
return code; |
} |
// There is only one shared stub for loading normalized |
// properties. It does not traverse the prototype chain, so the |
// property must be found in the object for the stub to be |
// applicable. |
- if (!object.is_identical_to(holder)) break; |
+ if (!receiver_is_holder) break; |
return isolate()->builtins()->LoadIC_Normal(); |
case CALLBACKS: { |
// Use simple field loads for some well-known callback properties. |
- if (object->IsJSObject()) { |
+ if (receiver_is_holder) { |
+ ASSERT(object->IsJSObject()); |
Handle<JSObject> receiver = Handle<JSObject>::cast(object); |
- Handle<Map> map(receiver->map()); |
- Handle<HeapType> type = IC::MapToType<HeapType>( |
- handle(receiver->map()), isolate()); |
int object_offset; |
if (Accessors::IsJSObjectFieldAccessor<HeapType>( |
type, name, &object_offset)) { |
@@ -1270,7 +1253,9 @@ static bool LookupForWrite(Handle<JSObject> receiver, |
// entirely by the migration above. |
receiver->map()->LookupTransition(*holder, *name, lookup); |
if (!lookup->IsTransition()) return false; |
- return ic->TryMarkMonomorphicPrototypeFailure(name); |
+ if (!ic->IsNameCompatibleWithPrototypeFailure(name)) return false; |
+ ic->MarkPrototypeFailure(name); |
+ return true; |
} |
return true; |
@@ -1416,18 +1401,18 @@ void StoreIC::UpdateCaches(LookupResult* lookup, |
Handle<Code> code = ComputeHandler(lookup, receiver, name, value); |
- PatchCache(CurrentTypeOf(receiver, isolate()), name, code); |
+ PatchCache(name, code); |
TRACE_IC("StoreIC", name); |
} |
Handle<Code> StoreIC::CompileHandler(LookupResult* lookup, |
- Handle<Object> object, |
- Handle<String> name, |
+ Handle<Object> object, Handle<String> name, |
Handle<Object> value, |
- InlineCacheHolderFlag cache_holder) { |
+ CacheHolderFlag cache_holder) { |
if (object->IsAccessCheckNeeded()) return slow_stub(); |
- ASSERT(cache_holder == OWN_MAP); |
+ ASSERT(cache_holder == kCacheOnReceiver || lookup->type() == CALLBACKS || |
+ (object->IsJSGlobalProxy() && lookup->holder()->IsJSGlobalObject())); |
// This is currently guaranteed by checks in StoreIC::Store. |
Handle<JSObject> receiver = Handle<JSObject>::cast(object); |