Chromium Code Reviews| Index: src/ic.cc |
| diff --git a/src/ic.cc b/src/ic.cc |
| index 080c7bf1b723f87b1449d4d9da58567ddf6ea9fb..0e715d708f7523e16857f7b8eb3ee7061065eb51 100644 |
| --- a/src/ic.cc |
| +++ b/src/ic.cc |
| @@ -182,13 +182,13 @@ Address IC::OriginalCodeAddress() const { |
| static bool TryRemoveInvalidPrototypeDependentStub(Code* target, |
| Object* receiver, |
| Object* name) { |
| - // If the code is NORMAL, it handles dictionary mode objects. Such stubs do |
| - // not check maps, but do positive/negative lookups. |
| - if (target->type() != Code::NORMAL) { |
| - Map* map = target->FindFirstMap(); |
| - if (map != NULL && map->is_deprecated()) { |
| - return true; |
| - } |
| + if (target->is_keyed_load_stub() || |
| + target->is_keyed_call_stub() || |
| + target->is_keyed_store_stub()) { |
| + // Determine whether the failure is due to a name failure. |
| + if (!name->IsName()) return false; |
| + Name* stub_name = target->FindFirstName(); |
| + if (Name::cast(name) != stub_name) return false; |
| } |
| InlineCacheHolderFlag cache_holder = |
| @@ -217,10 +217,30 @@ static bool TryRemoveInvalidPrototypeDependentStub(Code* target, |
| int index = map->IndexInCodeCache(name, target); |
| if (index >= 0) { |
| map->RemoveFromCodeCache(String::cast(name), target, index); |
| + // For loads, handlers are stored in addition to the ICs on the map. Remove |
| + // those, too. |
| + if (target->is_load_stub() || target->is_keyed_load_stub()) { |
| + Code* handler = target->FindFirstCode(); |
| + index = map->IndexInCodeCache(name, handler); |
| + if (index >= 0) { |
| + map->RemoveFromCodeCache(String::cast(name), handler, index); |
| + } |
| + } |
| return true; |
| } |
| - return false; |
| + // 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) return false; |
| + |
| + // The stub is not in the cache. We've ruled out all other kinds of failure |
| + // except for proptotype chain changes, a deprecated map, or a map that's |
| + // different from the one that the stub expects. If the map hasn't changed, |
| + // assume it's a prototype failure. Treat deprecated maps in the same way as |
| + // prototype failures (stay monomorphic if possible). |
| + Map* old_map = target->FindFirstMap(); |
| + if (old_map == NULL) return false; |
| + return old_map == map || old_map->is_deprecated(); |
| } |
| @@ -230,22 +250,13 @@ IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) { |
| if (state != MONOMORPHIC || !name->IsString()) return state; |
| if (receiver->IsUndefined() || receiver->IsNull()) return state; |
| - // 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 || |
| - kind == Code::KEYED_CALL_IC) { |
| - return MONOMORPHIC; |
| - } |
| - |
| // Remove the target from the code cache if it became invalid |
| // because of changes in the prototype chain to avoid hitting it |
| // again. |
| // Call stubs handle this later to allow extra IC state |
| // transitions. |
| - if (kind != Code::CALL_IC && |
| + if (kind != Code::CALL_IC && kind != Code::KEYED_CALL_IC && |
| TryRemoveInvalidPrototypeDependentStub(target, receiver, name)) { |
| return MONOMORPHIC_PROTOTYPE_FAILURE; |
| } |
| @@ -724,8 +735,7 @@ void CallICBase::UpdateCaches(LookupResult* lookup, |
| TryUpdateExtraICState(lookup, object, &extra_ic_state)) { |
| code = ComputeMonomorphicStub(lookup, state, extra_ic_state, |
| object, name); |
| - } else if (kind_ == Code::CALL_IC && |
| - TryRemoveInvalidPrototypeDependentStub(target(), |
| + } else if (TryRemoveInvalidPrototypeDependentStub(target(), |
| *object, |
| *name)) { |
| state = MONOMORPHIC_PROTOTYPE_FAILURE; |
| @@ -748,15 +758,7 @@ void CallICBase::UpdateCaches(LookupResult* lookup, |
| case UNINITIALIZED: |
| case MONOMORPHIC_PROTOTYPE_FAILURE: |
| case PREMONOMORPHIC: |
| - set_target(*code); |
| - break; |
| case MONOMORPHIC: |
| - if (code->ic_state() != MONOMORPHIC) { |
| - Map* map = target()->FindFirstMap(); |
| - if (map != NULL) { |
| - UpdateMegamorphicCache(map, *name, target()); |
| - } |
| - } |
| set_target(*code); |
| break; |
| case MEGAMORPHIC: { |
| @@ -986,14 +988,25 @@ bool IC::UpdatePolymorphicIC(State state, |
| CodeHandleList handlers; |
| int number_of_valid_maps; |
| + int overwrite_handler = -1; |
|
danno
2013/05/02 15:24:28
nit: handler_to_overwrite?
|
| + Handle<Map> new_receiver_map(receiver->map()); |
| { |
| AssertNoAllocation no_gc; |
| target()->FindAllMaps(&receiver_maps); |
| int number_of_maps = receiver_maps.length(); |
| number_of_valid_maps = number_of_maps; |
| + |
| for (int i = 0; i < number_of_maps; i++) { |
| - if (receiver_maps.at(i)->is_deprecated()) { |
| + Handle<Map> map = receiver_maps.at(i); |
| + // Filter out deprecated maps to ensure its instances get migrated. |
| + if (map->is_deprecated()) { |
| + number_of_valid_maps--; |
| + // If the receiver map is already in the polymorphic IC, this indicates |
| + // there was a prototoype chain failure. In that case, just overwrite the |
| + // handler. |
| + } else if (map.is_identical_to(new_receiver_map)) { |
| number_of_valid_maps--; |
| + overwrite_handler = i; |
| } |
| } |
| @@ -1007,14 +1020,16 @@ bool IC::UpdatePolymorphicIC(State state, |
| target()->FindAllCode(&handlers, receiver_maps.length()); |
| } |
| - if (!AddOneReceiverMapIfMissing(&receiver_maps, |
| - Handle<Map>(receiver->map()))) { |
| - return false; |
| + number_of_valid_maps++; |
| + if (overwrite_handler >= 0) { |
| + handlers.InsertAt(overwrite_handler, code); |
| + } else { |
| + receiver_maps.Add(new_receiver_map); |
| + handlers.Add(code); |
| } |
| - handlers.Add(code); |
| Handle<Code> ic = isolate()->stub_cache()->ComputePolymorphicIC( |
| - &receiver_maps, &handlers, number_of_valid_maps + 1, name); |
| + &receiver_maps, &handlers, number_of_valid_maps, name); |
| set_target(*ic); |
| return true; |
| } |
| @@ -1101,38 +1116,9 @@ void IC::PatchCache(State state, |
| if (UpdatePolymorphicIC(state, strict_mode, receiver, name, code)) { |
| break; |
| } |
| - } |
| - if (target()->type() != Code::NORMAL) { |
| - if (target()->is_load_stub()) { |
| + |
| + if (target()->type() != Code::NORMAL) { |
| CopyICToMegamorphicCache(name); |
| - } else if (target()->is_store_stub()) { |
| - // Ensure that the IC stays monomorphic when replacing a monomorphic |
| - // IC for a deprecated map. |
| - // TODO(verwaest): Remove this code once polymorphic store ICs are |
| - // implemented. Updating the polymorphic IC will keep it monomorphic |
| - // by filtering deprecated maps. |
| - MapHandleList maps; |
| - Code* handler = target(); |
| - handler->FindAllMaps(&maps); |
| - for (int i = 0; i < Min(1, maps.length()); i++) { |
| - if (maps.at(i)->is_deprecated()) { |
| - UpdateMonomorphicIC(receiver, code, name); |
| - return; |
| - } |
| - } |
| - if (maps.length() > 0) { |
| - if (receiver->map() == *maps.at(0)) { |
| - UpdateMonomorphicIC(receiver, code, name); |
| - return; |
| - } |
| - UpdateMegamorphicCache(*maps.at(0), *name, handler); |
| - } |
| - } else { |
| - Code* handler = target(); |
| - Map* map = handler->FindFirstMap(); |
| - if (map != NULL) { |
| - UpdateMegamorphicCache(map, *name, handler); |
| - } |
| } |
| } |
| @@ -1235,7 +1221,7 @@ void LoadIC::UpdateCaches(LookupResult* lookup, |
| } |
| -void IC::UpdateMegamorphicCache(Map* map, String* name, Code* code) { |
| +void IC::UpdateMegamorphicCache(Map* map, Name* name, Code* code) { |
| // Cache code holding map should be consistent with |
| // GenerateMonomorphicCacheProbe. |
| isolate()->stub_cache()->Set(name, map, code); |
| @@ -1497,7 +1483,8 @@ Handle<Code> KeyedLoadIC::ComputeLoadHandler(LookupResult* lookup, |
| static bool LookupForWrite(Handle<JSObject> receiver, |
| Handle<String> name, |
| Handle<Object> value, |
| - LookupResult* lookup) { |
| + LookupResult* lookup, |
| + IC::State* state) { |
| Handle<JSObject> holder = receiver; |
| receiver->Lookup(*name, lookup); |
| if (lookup->IsFound()) { |
| @@ -1534,7 +1521,21 @@ static bool LookupForWrite(Handle<JSObject> receiver, |
| PropertyDetails target_details = |
| lookup->GetTransitionDetails(receiver->map()); |
| if (target_details.IsReadOnly()) return false; |
| - return value->FitsRepresentation(target_details.representation()); |
| + |
| + // If the value that's being stored does not fit in the field that the |
| + // instance would transition to, create a new transition that fits the value. |
| + // This has to be done before generating the IC, since that IC will embed the |
| + // transition target. |
| + // Ensure the instance and its map were migrated before trying to update the |
| + // transition target. |
| + ASSERT(!receiver->map()->is_deprecated()); |
| + if (!value->FitsRepresentation(target_details.representation())) { |
| + Handle<Map> target(lookup->GetTransitionMapFromMap(receiver->map())); |
| + Map::GeneralizeRepresentation( |
| + target, target->LastAdded(), value->OptimalRepresentation()); |
| + *state = MONOMORPHIC_PROTOTYPE_FAILURE; |
| + } |
| + return true; |
| } |
| @@ -1618,7 +1619,7 @@ MaybeObject* StoreIC::Store(State state, |
| } |
| LookupResult lookup(isolate()); |
| - if (LookupForWrite(receiver, name, value, &lookup)) { |
| + if (LookupForWrite(receiver, name, value, &lookup, &state)) { |
| if (FLAG_use_ic) { |
| UpdateCaches(&lookup, state, strict_mode, receiver, name, value); |
| } |