From: jkummerow@chromium.org Date: Fri, 18 Jul 2014 13:50:21 +0000 (+0000) Subject: Cache IC handlers on the prototype's map if possible X-Git-Tag: upstream/4.7.83~8153 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1211f606ae7ae299b232033056f510f95242a7d5;p=platform%2Fupstream%2Fv8.git Cache IC handlers on the prototype's map if possible instead of on the receiver's map. Lazily overwrite cached handler if it is identical to the handler that just missed. R=verwaest@chromium.org Review URL: https://codereview.chromium.org/400523007 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22483 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index 5db538d..17c815e 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -276,7 +276,8 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { ASSERT(name.is(r2)); // Probe the stub cache. - Code::Flags flags = Code::ComputeHandlerFlags(Code::LOAD_IC); + Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( + Code::ComputeHandlerFlags(Code::LOAD_IC)); masm->isolate()->stub_cache()->GenerateProbe( masm, flags, receiver, name, r3, r4, r5, r6); @@ -1086,7 +1087,8 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { ASSERT(ValueRegister().is(r0)); // Get the receiver from the stack and probe the stub cache. - Code::Flags flags = Code::ComputeHandlerFlags(Code::STORE_IC); + Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( + Code::ComputeHandlerFlags(Code::STORE_IC)); masm->isolate()->stub_cache()->GenerateProbe( masm, flags, receiver, name, r3, r4, r5, r6); diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 50c3ff1..d9abe0a 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -893,12 +893,15 @@ Register StubCompiler::CheckPrototypes(Handle type, reg = holder_reg; // From now on the object will be in holder_reg. - if (heap()->InNewSpace(*prototype)) { - // The prototype is in new space; we cannot store a reference to it - // in the code. Load it from the map. + // Two possible reasons for loading the prototype from the map: + // (1) Can't store references to new space in code. + // (2) Handler is shared for all receivers with the same prototype + // map (but not necessarily the same prototype instance). + bool load_prototype_from_map = + heap()->InNewSpace(*prototype) || depth == 1; + if (load_prototype_from_map) { __ ldr(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset)); } else { - // The prototype is in old space; load it directly. __ mov(reg, Operand(prototype)); } } diff --git a/src/arm64/ic-arm64.cc b/src/arm64/ic-arm64.cc index bde701c..5be6438 100644 --- a/src/arm64/ic-arm64.cc +++ b/src/arm64/ic-arm64.cc @@ -369,7 +369,8 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { ASSERT(name.is(x2)); // Probe the stub cache. - Code::Flags flags = Code::ComputeHandlerFlags(Code::LOAD_IC); + Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( + Code::ComputeHandlerFlags(Code::LOAD_IC)); masm->isolate()->stub_cache()->GenerateProbe( masm, flags, receiver, name, x3, x4, x5, x6); @@ -1122,7 +1123,8 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { ASSERT(!AreAliased(receiver, name, ValueRegister(), x3, x4, x5, x6)); // Probe the stub cache. - Code::Flags flags = Code::ComputeHandlerFlags(Code::STORE_IC); + Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( + Code::ComputeHandlerFlags(Code::STORE_IC)); masm->isolate()->stub_cache()->GenerateProbe( masm, flags, receiver, name, x3, x4, x5, x6); diff --git a/src/arm64/stub-cache-arm64.cc b/src/arm64/stub-cache-arm64.cc index 0a672bc..14ced59 100644 --- a/src/arm64/stub-cache-arm64.cc +++ b/src/arm64/stub-cache-arm64.cc @@ -823,13 +823,14 @@ Register StubCompiler::CheckPrototypes(Handle type, reg = holder_reg; // From now on the object will be in holder_reg. __ Ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); } else { - bool need_map = (depth != 1 || check == CHECK_ALL_MAPS) || - heap()->InNewSpace(*prototype); - Register map_reg = NoReg; - if (need_map) { - map_reg = scratch1; - __ Ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset)); - } + // Two possible reasons for loading the prototype from the map: + // (1) Can't store references to new space in code. + // (2) Handler is shared for all receivers with the same prototype + // map (but not necessarily the same prototype instance). + bool load_prototype_from_map = + heap()->InNewSpace(*prototype) || depth == 1; + Register map_reg = scratch1; + __ Ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset)); if (depth != 1 || check == CHECK_ALL_MAPS) { __ CheckMap(map_reg, current_map, miss, DONT_DO_SMI_CHECK); @@ -849,12 +850,9 @@ Register StubCompiler::CheckPrototypes(Handle type, reg = holder_reg; // From now on the object will be in holder_reg. - if (heap()->InNewSpace(*prototype)) { - // The prototype is in new space; we cannot store a reference to it - // in the code. Load it from the map. + if (load_prototype_from_map) { __ Ldr(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset)); } else { - // The prototype is in old space; load it directly. __ Mov(reg, Operand(prototype)); } } diff --git a/src/globals.h b/src/globals.h index 871164e..bddd4ef 100644 --- a/src/globals.h +++ b/src/globals.h @@ -420,8 +420,8 @@ enum InlineCacheState { PREMONOMORPHIC, // Has been executed and only one receiver type has been seen. MONOMORPHIC, - // Like MONOMORPHIC but check failed due to prototype. - MONOMORPHIC_PROTOTYPE_FAILURE, + // Check failed due to prototype (or map deprecation). + PROTOTYPE_FAILURE, // Multiple receiver types have been seen. POLYMORPHIC, // Many receiver types have been seen. @@ -449,9 +449,11 @@ enum CallConstructorFlags { }; -enum InlineCacheHolderFlag { - OWN_MAP, // For fast properties objects. - PROTOTYPE_MAP // For slow properties objects (except GlobalObjects). +enum CacheHolderFlag { + kCacheOnPrototype, + kCacheOnPrototypeReceiverIsDictionary, + kCacheOnPrototypeReceiverIsPrimitive, + kCacheOnReceiver }; diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 0e9b95e..c48de4c 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -6124,6 +6124,14 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessAsMonomorphic( } +Handle HOptimizedGraphBuilder::PropertyAccessInfo::map() { + JSFunction* ctor = IC::GetRootConstructor( + type_, current_info()->closure()->context()->native_context()); + if (ctor != NULL) return handle(ctor->initial_map()); + return type_->AsClass()->Map(); +} + + static bool NeedsWrappingFor(Type* type, Handle target) { return type->Is(Type::NumberOrString()) && target->shared()->strict_mode() == SLOPPY && diff --git a/src/hydrogen.h b/src/hydrogen.h index a49fef7..bb93667 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -2466,23 +2466,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { // PropertyAccessInfo is built for types->first(). bool CanAccessAsMonomorphic(SmallMapList* types); - Handle map() { - if (type_->Is(Type::Number())) { - Context* context = current_info()->closure()->context(); - context = context->native_context(); - return handle(context->number_function()->initial_map()); - } else if (type_->Is(Type::Boolean())) { - Context* context = current_info()->closure()->context(); - context = context->native_context(); - return handle(context->boolean_function()->initial_map()); - } else if (type_->Is(Type::String())) { - Context* context = current_info()->closure()->context(); - context = context->native_context(); - return handle(context->string_function()->initial_map()); - } else { - return type_->AsClass()->Map(); - } - } + Handle map(); Type* type() const { return type_; } Handle name() const { return name_; } diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index bfec291..16b8ecf 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -893,7 +893,8 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { ASSERT(name.is(ecx)); // Probe the stub cache. - Code::Flags flags = Code::ComputeHandlerFlags(Code::LOAD_IC); + Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( + Code::ComputeHandlerFlags(Code::LOAD_IC)); masm->isolate()->stub_cache()->GenerateProbe( masm, flags, receiver, name, ebx, eax); @@ -1009,7 +1010,8 @@ void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { // Return address is on the stack. - Code::Flags flags = Code::ComputeHandlerFlags(Code::STORE_IC); + Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( + Code::ComputeHandlerFlags(Code::STORE_IC)); masm->isolate()->stub_cache()->GenerateProbe( masm, flags, ReceiverRegister(), NameRegister(), ebx, no_reg); diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index e02de20..443ed4e 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -823,6 +823,11 @@ Register StubCompiler::CheckPrototypes(Handle type, __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); } else { bool in_new_space = heap()->InNewSpace(*prototype); + // Two possible reasons for loading the prototype from the map: + // (1) Can't store references to new space in code. + // (2) Handler is shared for all receivers with the same prototype + // map (but not necessarily the same prototype instance). + bool load_prototype_from_map = in_new_space || depth == 1; if (depth != 1 || check == CHECK_ALL_MAPS) { __ CheckMap(reg, current_map, miss, DONT_DO_SMI_CHECK); } @@ -838,19 +843,16 @@ Register StubCompiler::CheckPrototypes(Handle type, scratch2, miss); } - if (in_new_space) { + if (load_prototype_from_map) { // Save the map in scratch1 for later. __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); } reg = holder_reg; // From now on the object will be in holder_reg. - if (in_new_space) { - // The prototype is in new space; we cannot store a reference to it - // in the code. Load it from the map. + if (load_prototype_from_map) { __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); } else { - // The prototype is in old space; load it directly. __ mov(reg, prototype); } } diff --git a/src/ic-inl.h b/src/ic-inl.h index 78d42ea..59dfedd 100644 --- a/src/ic-inl.h +++ b/src/ic-inl.h @@ -119,61 +119,55 @@ void IC::SetTargetAtAddress(Address address, } -InlineCacheHolderFlag IC::GetCodeCacheForObject(Object* object) { - if (object->IsJSObject()) return OWN_MAP; - - // If the object is a value, we use the prototype map for the cache. - ASSERT(object->IsString() || object->IsSymbol() || - object->IsNumber() || object->IsBoolean()); - return PROTOTYPE_MAP; -} - - -HeapObject* IC::GetCodeCacheHolder(Isolate* isolate, - Object* object, - InlineCacheHolderFlag holder) { - if (object->IsSmi()) holder = PROTOTYPE_MAP; - PrototypeIterator iter(isolate, object, - holder == OWN_MAP - ? PrototypeIterator::START_AT_RECEIVER - : PrototypeIterator::START_AT_PROTOTYPE); - return HeapObject::cast(iter.GetCurrent()); +template +JSFunction* IC::GetRootConstructor(TypeClass* type, Context* native_context) { + if (type->Is(TypeClass::Boolean())) { + return native_context->boolean_function(); + } else if (type->Is(TypeClass::Number())) { + return native_context->number_function(); + } else if (type->Is(TypeClass::String())) { + return native_context->string_function(); + } else if (type->Is(TypeClass::Symbol())) { + return native_context->symbol_function(); + } else { + return NULL; + } } -InlineCacheHolderFlag IC::GetCodeCacheFlag(HeapType* type) { - if (type->Is(HeapType::Boolean()) || - type->Is(HeapType::Number()) || - type->Is(HeapType::String()) || - type->Is(HeapType::Symbol())) { - return PROTOTYPE_MAP; +Handle IC::GetHandlerCacheHolder(HeapType* type, bool receiver_is_holder, + Isolate* isolate, CacheHolderFlag* flag) { + Handle receiver_map = TypeToMap(type, isolate); + if (receiver_is_holder) { + *flag = kCacheOnReceiver; + return receiver_map; + } + Context* native_context = *isolate->native_context(); + JSFunction* builtin_ctor = GetRootConstructor(type, native_context); + if (builtin_ctor != NULL) { + *flag = kCacheOnPrototypeReceiverIsPrimitive; + return handle(HeapObject::cast(builtin_ctor->instance_prototype())->map()); } - return OWN_MAP; + *flag = receiver_map->is_dictionary_map() + ? kCacheOnPrototypeReceiverIsDictionary + : kCacheOnPrototype; + // Callers must ensure that the prototype is non-null. + return handle(JSObject::cast(receiver_map->prototype())->map()); } -Handle IC::GetCodeCacheHolder(InlineCacheHolderFlag flag, - HeapType* type, - Isolate* isolate) { - if (flag == PROTOTYPE_MAP) { - Context* context = *isolate->native_context(); - JSFunction* constructor; - if (type->Is(HeapType::Boolean())) { - constructor = context->boolean_function(); - } else if (type->Is(HeapType::Number())) { - constructor = context->number_function(); - } else if (type->Is(HeapType::String())) { - constructor = context->string_function(); - } else { - ASSERT(type->Is(HeapType::Symbol())); - constructor = context->symbol_function(); - } - return handle(JSObject::cast(constructor->instance_prototype())->map()); +Handle IC::GetICCacheHolder(HeapType* type, Isolate* isolate, + CacheHolderFlag* flag) { + Context* native_context = *isolate->native_context(); + JSFunction* builtin_ctor = GetRootConstructor(type, native_context); + if (builtin_ctor != NULL) { + *flag = kCacheOnPrototype; + return handle(builtin_ctor->initial_map()); } + *flag = kCacheOnReceiver; return TypeToMap(type, isolate); } - } } // namespace v8::internal #endif // V8_IC_INL_H_ diff --git a/src/ic.cc b/src/ic.cc index ffabaf8..81afa53 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, bool IC::TryRemoveInvalidPrototypeDependentStub(Handle receiver, Handle 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 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 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( - 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 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 receiver, return cell->type()->IsConstant(); } - return false; -} - - -void IC::TryRemoveInvalidHandlers(Handle map, Handle name) { - CodeHandleList handlers; - target()->FindHandlers(&handlers); - for (int i = 0; i < handlers.length(); i++) { - Handle 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 name) { +bool IC::IsNameCompatibleWithPrototypeFailure(Handle 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 name) { void IC::UpdateState(Handle receiver, Handle name) { + receiver_type_ = CurrentTypeOf(receiver, isolate()); if (!name->IsString()) return; - if (state() != MONOMORPHIC) { - if (state() == POLYMORPHIC && receiver->IsHeapObject()) { - TryRemoveInvalidHandlers( - handle(Handle::cast(receiver)->map()), - Handle::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::cast(name)) && - TryMarkMonomorphicPrototypeFailure(name)) { + if (TryRemoveInvalidPrototypeDependentStub(receiver, + Handle::cast(name))) { + MarkPrototypeFailure(name); return; } @@ -688,10 +649,10 @@ static bool AddOneReceiverMapIfMissing(MapHandleList* receiver_maps, } -bool IC::UpdatePolymorphicIC(Handle type, - Handle name, - Handle code) { +bool IC::UpdatePolymorphicIC(Handle name, Handle code) { if (!code->is_handler()) return false; + if (target()->is_keyed_stub() && state() != PROTOTYPE_FAILURE) return false; + Handle type = receiver_type(); TypeHandleList types; CodeHandleList handlers; @@ -728,18 +689,25 @@ bool IC::UpdatePolymorphicIC(Handle 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 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 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 IC::MapToType(Handle map, Isolate* region); -void IC::UpdateMonomorphicIC(Handle type, - Handle handler, - Handle name) { +void IC::UpdateMonomorphicIC(Handle handler, Handle name) { if (!handler->is_handler()) return set_target(*handler); Handle 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 type, - Handle name, - Handle code) { +void IC::PatchCache(Handle name, Handle 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 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 type = CurrentTypeOf(object, isolate()); Handle 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 IC::ComputeHandler(LookupResult* lookup, Handle object, Handle name, Handle value) { - InlineCacheHolderFlag cache_holder = GetCodeCacheForObject(*object); - Handle stub_holder(GetCodeCacheHolder( - isolate(), *object, cache_holder)); + bool receiver_is_holder = lookup->ReceiverIsHolder(object); + CacheHolderFlag flag; + Handle stub_holder_map = IC::GetHandlerCacheHolder( + *receiver_type(), receiver_is_holder, isolate(), &flag); Handle 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::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 LoadIC::CompileHandler(LookupResult* lookup, - Handle object, - Handle name, - Handle unused, - InlineCacheHolderFlag cache_holder) { +Handle LoadIC::CompileHandler(LookupResult* lookup, Handle object, + Handle name, Handle 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 LoadIC::CompileHandler(LookupResult* lookup, } } - Handle type = CurrentTypeOf(object, isolate()); + Handle type = receiver_type(); Handle 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 LoadIC::CompileHandler(LookupResult* lookup, Handle code = compiler.CompileLoadGlobal( type, global, cell, name, lookup->IsDontDelete()); // TODO(verwaest): Move caching of these NORMAL stubs outside as well. - Handle stub_holder(GetCodeCacheHolder( - isolate(), *object, cache_holder)); - HeapObject::UpdateMapCodeCache(stub_holder, name, code); + CacheHolderFlag flag; + Handle 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 receiver = Handle::cast(object); - Handle map(receiver->map()); - Handle type = IC::MapToType( - handle(receiver->map()), isolate()); int object_offset; if (Accessors::IsJSObjectFieldAccessor( type, name, &object_offset)) { @@ -1270,7 +1253,9 @@ static bool LookupForWrite(Handle 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; @@ -1418,18 +1403,18 @@ void StoreIC::UpdateCaches(LookupResult* lookup, Handle code = ComputeHandler(lookup, receiver, name, value); - PatchCache(CurrentTypeOf(receiver, isolate()), name, code); + PatchCache(name, code); TRACE_IC("StoreIC", name); } Handle StoreIC::CompileHandler(LookupResult* lookup, - Handle object, - Handle name, + Handle object, Handle name, Handle 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 receiver = Handle::cast(object); diff --git a/src/ic.h b/src/ic.h index 4647c9c..4e75423 100644 --- a/src/ic.h +++ b/src/ic.h @@ -75,13 +75,10 @@ class IC { // Compute the current IC state based on the target stub, receiver and name. void UpdateState(Handle receiver, Handle name); - bool IsNameCompatibleWithMonomorphicPrototypeFailure(Handle name); - bool TryMarkMonomorphicPrototypeFailure(Handle name) { - if (IsNameCompatibleWithMonomorphicPrototypeFailure(name)) { - state_ = MONOMORPHIC_PROTOTYPE_FAILURE; - return true; - } - return false; + bool IsNameCompatibleWithPrototypeFailure(Handle name); + void MarkPrototypeFailure(Handle name) { + ASSERT(IsNameCompatibleWithPrototypeFailure(name)); + state_ = PROTOTYPE_FAILURE; } // If the stub contains weak maps then this function adds the stub to @@ -111,20 +108,15 @@ class IC { } #endif - // Determines which map must be used for keeping the code stub. - // These methods should not be called with undefined or null. - static inline InlineCacheHolderFlag GetCodeCacheForObject(Object* object); - // TODO(verwaest): This currently returns a HeapObject rather than JSObject* - // since loading the IC for loading the length from strings are stored on - // the string map directly, rather than on the JSObject-typed prototype. - static inline HeapObject* GetCodeCacheHolder(Isolate* isolate, - Object* object, - InlineCacheHolderFlag holder); - - static inline InlineCacheHolderFlag GetCodeCacheFlag(HeapType* type); - static inline Handle GetCodeCacheHolder(InlineCacheHolderFlag flag, - HeapType* type, - Isolate* isolate); + template + static JSFunction* GetRootConstructor(TypeClass* type, + Context* native_context); + static inline Handle GetHandlerCacheHolder(HeapType* type, + bool receiver_is_holder, + Isolate* isolate, + CacheHolderFlag* flag); + static inline Handle GetICCacheHolder(HeapType* type, Isolate* isolate, + CacheHolderFlag* flag); static bool IsCleared(Code* code) { InlineCacheState state = code->ic_state(); @@ -193,28 +185,21 @@ class IC { Handle value = Handle::null()); virtual Handle CompileHandler(LookupResult* lookup, Handle object, - Handle name, - Handle value, - InlineCacheHolderFlag cache_holder) { + Handle name, Handle value, + CacheHolderFlag cache_holder) { UNREACHABLE(); return Handle::null(); } - void UpdateMonomorphicIC(Handle type, - Handle handler, - Handle name); + void UpdateMonomorphicIC(Handle handler, Handle name); - bool UpdatePolymorphicIC(Handle type, - Handle name, - Handle code); + bool UpdatePolymorphicIC(Handle name, Handle code); virtual void UpdateMegamorphicCache(HeapType* type, Name* name, Code* code); void CopyICToMegamorphicCache(Handle name); bool IsTransitionOfMonomorphicTarget(Map* source_map, Map* target_map); - void PatchCache(Handle type, - Handle name, - Handle code); + void PatchCache(Handle name, Handle code); virtual Code::Kind kind() const { UNREACHABLE(); return Code::STUB; @@ -234,13 +219,14 @@ class IC { bool TryRemoveInvalidPrototypeDependentStub(Handle receiver, Handle name); - void TryRemoveInvalidHandlers(Handle map, Handle name); ExtraICState extra_ic_state() const { return extra_ic_state_; } void set_extra_ic_state(ExtraICState state) { extra_ic_state_ = state; } + Handle receiver_type() { return receiver_type_; } + void TargetMaps(MapHandleList* list) { FindTargetMaps(); for (int i = 0; i < target_maps_.length(); i++) { @@ -300,8 +286,10 @@ class IC { // The original code target that missed. Handle target_; - State state_; bool target_set_; + State state_; + Handle receiver_type_; + MaybeHandle maybe_handler_; ExtraICState extra_ic_state_; MapHandleList target_maps_; @@ -476,7 +464,7 @@ class LoadIC: public IC { Handle object, Handle name, Handle unused, - InlineCacheHolderFlag cache_holder); + CacheHolderFlag cache_holder); private: // Stub accessors. @@ -650,9 +638,8 @@ class StoreIC: public IC { Handle value); virtual Handle CompileHandler(LookupResult* lookup, Handle object, - Handle name, - Handle value, - InlineCacheHolderFlag cache_holder); + Handle name, Handle value, + CacheHolderFlag cache_holder); private: void set_target(Code* code) { diff --git a/src/objects-inl.h b/src/objects-inl.h index d348dc0..10fa803 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -4955,11 +4955,9 @@ void Code::set_constant_pool(Object* value) { } -Code::Flags Code::ComputeFlags(Kind kind, - InlineCacheState ic_state, - ExtraICState extra_ic_state, - StubType type, - InlineCacheHolderFlag holder) { +Code::Flags Code::ComputeFlags(Kind kind, InlineCacheState ic_state, + ExtraICState extra_ic_state, StubType type, + CacheHolderFlag holder) { // Compute the bit mask. unsigned int bits = KindField::encode(kind) | ICStateField::encode(ic_state) @@ -4972,15 +4970,14 @@ Code::Flags Code::ComputeFlags(Kind kind, Code::Flags Code::ComputeMonomorphicFlags(Kind kind, ExtraICState extra_ic_state, - InlineCacheHolderFlag holder, + CacheHolderFlag holder, StubType type) { return ComputeFlags(kind, MONOMORPHIC, extra_ic_state, type, holder); } -Code::Flags Code::ComputeHandlerFlags(Kind handler_kind, - StubType type, - InlineCacheHolderFlag holder) { +Code::Flags Code::ComputeHandlerFlags(Kind handler_kind, StubType type, + CacheHolderFlag holder) { return ComputeFlags(Code::HANDLER, MONOMORPHIC, handler_kind, type, holder); } @@ -5005,7 +5002,7 @@ Code::StubType Code::ExtractTypeFromFlags(Flags flags) { } -InlineCacheHolderFlag Code::ExtractCacheHolderFromFlags(Flags flags) { +CacheHolderFlag Code::ExtractCacheHolderFromFlags(Flags flags) { return CacheHolderField::decode(flags); } @@ -5016,6 +5013,12 @@ Code::Flags Code::RemoveTypeFromFlags(Flags flags) { } +Code::Flags Code::RemoveTypeAndHolderFromFlags(Flags flags) { + int bits = flags & ~TypeField::kMask & ~CacheHolderField::kMask; + return static_cast(bits); +} + + Code* Code::GetCodeFromTargetAddress(Address address) { HeapObject* code = HeapObject::FromAddress(address - Code::kHeaderSize); // GetCodeFromTargetAddress might be called when marking objects during mark diff --git a/src/objects.cc b/src/objects.cc index caae614..083f6e0 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -10908,6 +10908,26 @@ bool Code::FindHandlers(CodeHandleList* code_list, int length) { } +MaybeHandle Code::FindHandlerForMap(Map* map) { + ASSERT(is_inline_cache_stub()); + int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | + RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); + bool return_next = false; + for (RelocIterator it(this, mask); !it.done(); it.next()) { + RelocInfo* info = it.rinfo(); + if (info->rmode() == RelocInfo::EMBEDDED_OBJECT) { + Object* object = info->target_object(); + if (object == map) return_next = true; + } else if (return_next) { + Code* code = Code::GetCodeFromTargetAddress(info->target_address()); + ASSERT(code->kind() == Code::HANDLER); + return handle(code); + } + } + return MaybeHandle(); +} + + Name* Code::FindFirstName() { ASSERT(is_inline_cache_stub()); DisallowHeapAllocation no_allocation; @@ -11371,7 +11391,8 @@ const char* Code::ICState2String(InlineCacheState state) { case UNINITIALIZED: return "UNINITIALIZED"; case PREMONOMORPHIC: return "PREMONOMORPHIC"; case MONOMORPHIC: return "MONOMORPHIC"; - case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE"; + case PROTOTYPE_FAILURE: + return "PROTOTYPE_FAILURE"; case POLYMORPHIC: return "POLYMORPHIC"; case MEGAMORPHIC: return "MEGAMORPHIC"; case GENERIC: return "GENERIC"; diff --git a/src/objects.h b/src/objects.h index bbc164f..8084474 100644 --- a/src/objects.h +++ b/src/objects.h @@ -5634,6 +5634,9 @@ class Code: public HeapObject { // enough handlers can be found. bool FindHandlers(CodeHandleList* code_list, int length = -1); + // Find the handler for |map|. + MaybeHandle FindHandlerForMap(Map* map); + // Find the first name in an IC stub. Name* FindFirstName(); @@ -5655,30 +5658,26 @@ class Code: public HeapObject { // Flags operations. static inline Flags ComputeFlags( - Kind kind, - InlineCacheState ic_state = UNINITIALIZED, - ExtraICState extra_ic_state = kNoExtraICState, - StubType type = NORMAL, - InlineCacheHolderFlag holder = OWN_MAP); + Kind kind, InlineCacheState ic_state = UNINITIALIZED, + ExtraICState extra_ic_state = kNoExtraICState, StubType type = NORMAL, + CacheHolderFlag holder = kCacheOnReceiver); static inline Flags ComputeMonomorphicFlags( - Kind kind, - ExtraICState extra_ic_state = kNoExtraICState, - InlineCacheHolderFlag holder = OWN_MAP, - StubType type = NORMAL); + Kind kind, ExtraICState extra_ic_state = kNoExtraICState, + CacheHolderFlag holder = kCacheOnReceiver, StubType type = NORMAL); static inline Flags ComputeHandlerFlags( - Kind handler_kind, - StubType type = NORMAL, - InlineCacheHolderFlag holder = OWN_MAP); + Kind handler_kind, StubType type = NORMAL, + CacheHolderFlag holder = kCacheOnReceiver); static inline InlineCacheState ExtractICStateFromFlags(Flags flags); static inline StubType ExtractTypeFromFlags(Flags flags); + static inline CacheHolderFlag ExtractCacheHolderFromFlags(Flags flags); static inline Kind ExtractKindFromFlags(Flags flags); - static inline InlineCacheHolderFlag ExtractCacheHolderFromFlags(Flags flags); static inline ExtraICState ExtractExtraICStateFromFlags(Flags flags); static inline Flags RemoveTypeFromFlags(Flags flags); + static inline Flags RemoveTypeAndHolderFromFlags(Flags flags); // Convert a target address into a code object. static inline Code* GetCodeFromTargetAddress(Address address); @@ -5844,7 +5843,7 @@ class Code: public HeapObject { // Flags layout. BitField. class ICStateField: public BitField {}; class TypeField: public BitField {}; - class CacheHolderField: public BitField {}; + class CacheHolderField : public BitField {}; class KindField: public BitField {}; // TODO(bmeurer): Bit 10 is available for free use. :-) class ExtraICStateField: public BitFieldFindFieldOwner(number_); } + bool ReceiverIsHolder(Handle receiver) { + if (*receiver == holder()) return true; + if (lookup_type_ == TRANSITION_TYPE) return true; + return false; + } + void Iterate(ObjectVisitor* visitor); private: diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 5612bf2..91cb387 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -33,13 +33,13 @@ void StubCache::Initialize() { } -Code* StubCache::Set(Name* name, Map* map, Code* code) { - // Get the flags from the code. - Code::Flags flags = Code::RemoveTypeFromFlags(code->flags()); +static Code::Flags CommonStubCacheChecks(Name* name, Map* map, + Code::Flags flags, Heap* heap) { + flags = Code::RemoveTypeAndHolderFromFlags(flags); // Validate that the name does not move on scavenge, and that we // can use identity checks instead of structural equality checks. - ASSERT(!heap()->InNewSpace(name)); + ASSERT(!heap->InNewSpace(name)); ASSERT(name->IsUniqueName()); // The state bits are not important to the hash function because @@ -49,8 +49,16 @@ Code* StubCache::Set(Name* name, Map* map, Code* code) { ASSERT(Code::ExtractICStateFromFlags(flags) == MONOMORPHIC); STATIC_ASSERT((Code::ICStateField::kMask & 1) == 1); - // Make sure that the code type is not included in the hash. + // Make sure that the code type and cache holder are not included in the hash. ASSERT(Code::ExtractTypeFromFlags(flags) == 0); + ASSERT(Code::ExtractCacheHolderFromFlags(flags) == 0); + + return flags; +} + + +Code* StubCache::Set(Name* name, Map* map, Code* code) { + Code::Flags flags = CommonStubCacheChecks(name, map, code->flags(), heap()); // Compute the primary entry. int primary_offset = PrimaryOffset(name, flags, map); @@ -61,7 +69,8 @@ Code* StubCache::Set(Name* name, Map* map, Code* code) { // secondary cache before overwriting it. if (old_code != isolate_->builtins()->builtin(Builtins::kIllegal)) { Map* old_map = primary->map; - Code::Flags old_flags = Code::RemoveTypeFromFlags(old_code->flags()); + Code::Flags old_flags = + Code::RemoveTypeAndHolderFromFlags(old_code->flags()); int seed = PrimaryOffset(primary->key, old_flags, old_map); int secondary_offset = SecondaryOffset(primary->key, old_flags, seed); Entry* secondary = entry(secondary_, secondary_offset); @@ -77,11 +86,25 @@ Code* StubCache::Set(Name* name, Map* map, Code* code) { } -Handle StubCache::FindIC(Handle name, - Handle stub_holder, - Code::Kind kind, - ExtraICState extra_state, - InlineCacheHolderFlag cache_holder) { +Code* StubCache::Get(Name* name, Map* map, Code::Flags flags) { + flags = CommonStubCacheChecks(name, map, flags, heap()); + int primary_offset = PrimaryOffset(name, flags, map); + Entry* primary = entry(primary_, primary_offset); + if (primary->key == name && primary->map == map) { + return primary->value; + } + int secondary_offset = SecondaryOffset(name, flags, primary_offset); + Entry* secondary = entry(secondary_, secondary_offset); + if (secondary->key == name && secondary->map == map) { + return secondary->value; + } + return NULL; +} + + +Handle StubCache::FindIC(Handle name, Handle stub_holder, + Code::Kind kind, ExtraICState extra_state, + CacheHolderFlag cache_holder) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder); Handle probe(stub_holder->FindInCodeCache(*name, flags), isolate_); @@ -90,10 +113,9 @@ Handle StubCache::FindIC(Handle name, } -Handle StubCache::FindHandler(Handle name, - Handle stub_holder, +Handle StubCache::FindHandler(Handle name, Handle stub_holder, Code::Kind kind, - InlineCacheHolderFlag cache_holder, + CacheHolderFlag cache_holder, Code::StubType type) { Code::Flags flags = Code::ComputeHandlerFlags(kind, type, cache_holder); @@ -109,16 +131,15 @@ Handle StubCache::ComputeMonomorphicIC( Handle type, Handle handler, ExtraICState extra_ic_state) { - InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); + CacheHolderFlag flag; + Handle stub_holder = IC::GetICCacheHolder(*type, isolate(), &flag); - Handle stub_holder; Handle ic; // There are multiple string maps that all use the same prototype. That // prototype cannot hold multiple handlers, one for each of the string maps, // for a single name. Hence, turn off caching of the IC. bool can_be_cached = !type->Is(HeapType::String()); if (can_be_cached) { - stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); ic = FindIC(name, stub_holder, kind, extra_ic_state, flag); if (!ic.is_null()) return ic; } @@ -147,36 +168,45 @@ Handle StubCache::ComputeMonomorphicIC( Handle StubCache::ComputeLoadNonexistent(Handle name, Handle type) { - InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); - Handle stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); + Handle receiver_map = IC::TypeToMap(*type, isolate()); + if (receiver_map->prototype()->IsNull()) { + // TODO(jkummerow/verwaest): If there is no prototype and the property + // is nonexistent, introduce a builtin to handle this (fast properties + // -> return undefined, dictionary properties -> do negative lookup). + return Handle(); + } + CacheHolderFlag flag; + Handle stub_holder_map = + IC::GetHandlerCacheHolder(*type, false, isolate(), &flag); + // If no dictionary mode objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map and we use // the empty string for the map cache in that case. If there are dictionary // mode objects involved, we need to do negative lookups in the stub and // therefore the stub will be specific to the name. - Handle current_map = stub_holder; - Handle cache_name = current_map->is_dictionary_map() - ? name : Handle::cast(isolate()->factory()->nonexistent_symbol()); - Handle next(current_map->prototype(), isolate()); - Handle last = Handle::null(); - while (!next->IsNull()) { - last = Handle::cast(next); - next = handle(current_map->prototype(), isolate()); - current_map = handle(Handle::cast(next)->map()); + Handle cache_name = + receiver_map->is_dictionary_map() + ? name + : Handle::cast(isolate()->factory()->nonexistent_symbol()); + Handle current_map = stub_holder_map; + Handle last(JSObject::cast(receiver_map->prototype())); + while (true) { if (current_map->is_dictionary_map()) cache_name = name; + if (current_map->prototype()->IsNull()) break; + last = handle(JSObject::cast(current_map->prototype())); + current_map = handle(last->map()); } - // Compile the stub that is either shared for all names or // name specific if there are global objects involved. - Handle handler = FindHandler( - cache_name, stub_holder, Code::LOAD_IC, flag, Code::FAST); + Handle handler = + FindHandler(cache_name, stub_holder_map, Code::LOAD_IC, flag, Code::FAST); if (!handler.is_null()) { return handler; } LoadStubCompiler compiler(isolate_, kNoExtraICState, flag); handler = compiler.CompileLoadNonexistent(type, last, cache_name); - Map::UpdateCodeCache(stub_holder, cache_name, handler); + Map::UpdateCodeCache(stub_holder_map, cache_name, handler); return handler; } @@ -203,8 +233,8 @@ Handle StubCache::ComputeKeyedStoreElement( KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); - Code::Flags flags = Code::ComputeMonomorphicFlags( - Code::KEYED_STORE_IC, extra_state); + Code::Flags flags = + Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || diff --git a/src/stub-cache.h b/src/stub-cache.h index 4687c1c..2a59ecc 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -57,17 +57,13 @@ class StubCache { Handle StubHolder(Handle receiver, Handle holder); - Handle FindIC(Handle name, - Handle stub_holder_map, + Handle FindIC(Handle name, Handle stub_holder_map, Code::Kind kind, ExtraICState extra_state = kNoExtraICState, - InlineCacheHolderFlag cache_holder = OWN_MAP); + CacheHolderFlag cache_holder = kCacheOnReceiver); - Handle FindHandler(Handle name, - Handle map, - Code::Kind kind, - InlineCacheHolderFlag cache_holder, - Code::StubType type); + Handle FindHandler(Handle name, Handle map, Code::Kind kind, + CacheHolderFlag cache_holder, Code::StubType type); Handle ComputeMonomorphicIC(Code::Kind kind, Handle name, @@ -114,6 +110,8 @@ class StubCache { // Update cache for entry hash(name, map). Code* Set(Name* name, Map* map, Code* code); + Code* Get(Name* name, Map* map, Code::Flags flags); + // Clear the lookup table (@ mark compact collection). void Clear(); @@ -415,10 +413,9 @@ enum FrontendCheckType { PERFORM_INITIAL_CHECKS, SKIP_INITIAL_CHECKS }; class BaseLoadStoreStubCompiler: public StubCompiler { public: - BaseLoadStoreStubCompiler(Isolate* isolate, - Code::Kind kind, + BaseLoadStoreStubCompiler(Isolate* isolate, Code::Kind kind, ExtraICState extra_ic_state = kNoExtraICState, - InlineCacheHolderFlag cache_holder = OWN_MAP) + CacheHolderFlag cache_holder = kCacheOnReceiver) : StubCompiler(isolate, extra_ic_state), kind_(kind), cache_holder_(cache_holder) { @@ -499,7 +496,7 @@ class BaseLoadStoreStubCompiler: public StubCompiler { bool IncludesNumberType(TypeHandleList* types); Code::Kind kind_; - InlineCacheHolderFlag cache_holder_; + CacheHolderFlag cache_holder_; Register* registers_; }; @@ -508,10 +505,10 @@ class LoadStubCompiler: public BaseLoadStoreStubCompiler { public: LoadStubCompiler(Isolate* isolate, ExtraICState extra_ic_state = kNoExtraICState, - InlineCacheHolderFlag cache_holder = OWN_MAP, + CacheHolderFlag cache_holder = kCacheOnReceiver, Code::Kind kind = Code::LOAD_IC) - : BaseLoadStoreStubCompiler(isolate, kind, extra_ic_state, - cache_holder) { } + : BaseLoadStoreStubCompiler(isolate, kind, extra_ic_state, cache_holder) { + } virtual ~LoadStubCompiler() { } Handle CompileLoadField(Handle type, @@ -616,9 +613,9 @@ class KeyedLoadStubCompiler: public LoadStubCompiler { public: KeyedLoadStubCompiler(Isolate* isolate, ExtraICState extra_ic_state = kNoExtraICState, - InlineCacheHolderFlag cache_holder = OWN_MAP) + CacheHolderFlag cache_holder = kCacheOnReceiver) : LoadStubCompiler(isolate, extra_ic_state, cache_holder, - Code::KEYED_LOAD_IC) { } + Code::KEYED_LOAD_IC) {} Handle CompileLoadElement(Handle receiver_map); diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index 62585af..61696ce 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -910,7 +910,8 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { ASSERT(name.is(rcx)); // Probe the stub cache. - Code::Flags flags = Code::ComputeHandlerFlags(Code::LOAD_IC); + Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( + Code::ComputeHandlerFlags(Code::LOAD_IC)); masm->isolate()->stub_cache()->GenerateProbe( masm, flags, receiver, name, rbx, rax); @@ -1041,7 +1042,8 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { // The return address is on the stack. // Get the receiver from the stack and probe the stub cache. - Code::Flags flags = Code::ComputeHandlerFlags(Code::STORE_IC); + Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( + Code::ComputeHandlerFlags(Code::STORE_IC)); masm->isolate()->stub_cache()->GenerateProbe( masm, flags, ReceiverRegister(), NameRegister(), rbx, no_reg); diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 74f756a..c552dc6 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -775,7 +775,12 @@ Register StubCompiler::CheckPrototypes(Handle type, __ movp(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); } else { bool in_new_space = heap()->InNewSpace(*prototype); - if (in_new_space) { + // Two possible reasons for loading the prototype from the map: + // (1) Can't store references to new space in code. + // (2) Handler is shared for all receivers with the same prototype + // map (but not necessarily the same prototype instance). + bool load_prototype_from_map = in_new_space || depth == 1; + if (load_prototype_from_map) { // Save the map in scratch1 for later. __ movp(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); } @@ -795,12 +800,9 @@ Register StubCompiler::CheckPrototypes(Handle type, } reg = holder_reg; // From now on the object will be in holder_reg. - if (in_new_space) { - // The prototype is in new space; we cannot store a reference to it - // in the code. Load it from the map. + if (load_prototype_from_map) { __ movp(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); } else { - // The prototype is in old space; load it directly. __ Move(reg, prototype); } }