Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Unified Diff: src/ic.cc

Issue 400523007: Cache IC handlers on the prototype's map if possible (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: addressed comment Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/ic.h ('k') | src/ic-inl.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
« no previous file with comments | « src/ic.h ('k') | src/ic-inl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698