Chromium Code Reviews| 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()); |
|
Toon Verwaest
2014/07/18 13:12:23
Probably should move this closer to where we get t
Jakob Kummerow
2014/07/18 13:47:52
Ack. The difficulty is that where maybe_handler_ i
|
| + 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); |
|
Toon Verwaest
2014/07/18 13:12:24
Nice :)
Jakob Kummerow
2014/07/18 13:47:52
:-)
|
| } |
| 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); |