| Index: src/ic.cc
|
| diff --git a/src/ic.cc b/src/ic.cc
|
| index 080c7bf1b723f87b1449d4d9da58567ddf6ea9fb..83677bdbcca48abe77358cde401c493920216943 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 handler_to_overwrite = -1;
|
| + 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--;
|
| + handler_to_overwrite = 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 (handler_to_overwrite >= 0) {
|
| + handlers.InsertAt(handler_to_overwrite, 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);
|
| }
|
|
|