diff options
author | Michaël Zasso <targos@protonmail.com> | 2017-02-14 11:27:26 +0100 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2017-02-22 15:55:42 +0100 |
commit | 7a77daf24344db7942e34c962b0f1ee729ab7af5 (patch) | |
tree | e7cbe7bf4e2f4b802a8f5bc18336c546cd6a0d7f /deps/v8/src/ic/ic.cc | |
parent | 5f08871ee93ea739148cc49e0f7679e33c70295a (diff) | |
download | node-new-7a77daf24344db7942e34c962b0f1ee729ab7af5.tar.gz |
deps: update V8 to 5.6.326.55
PR-URL: https://github.com/nodejs/node/pull/10992
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Diffstat (limited to 'deps/v8/src/ic/ic.cc')
-rw-r--r-- | deps/v8/src/ic/ic.cc | 657 |
1 files changed, 499 insertions, 158 deletions
diff --git a/deps/v8/src/ic/ic.cc b/deps/v8/src/ic/ic.cc index 0e751bd358..7e0cefdca9 100644 --- a/deps/v8/src/ic/ic.cc +++ b/deps/v8/src/ic/ic.cc @@ -4,6 +4,8 @@ #include "src/ic/ic.h" +#include <iostream> + #include "src/accessors.h" #include "src/api-arguments-inl.h" #include "src/api.h" @@ -16,6 +18,7 @@ #include "src/frames-inl.h" #include "src/ic/call-optimization.h" #include "src/ic/handler-compiler.h" +#include "src/ic/handler-configuration-inl.h" #include "src/ic/ic-compiler.h" #include "src/ic/ic-inl.h" #include "src/ic/stub-cache.h" @@ -98,38 +101,51 @@ void IC::TraceIC(const char* type, Handle<Object> name) { void IC::TraceIC(const char* type, Handle<Object> name, State old_state, State new_state) { - if (FLAG_trace_ic) { - PrintF("[%s%s in ", is_keyed() ? "Keyed" : "", type); - - // TODO(jkummerow): Add support for "apply". The logic is roughly: - // marker = [fp_ + kMarkerOffset]; - // if marker is smi and marker.value == INTERNAL and - // the frame's code == builtin(Builtins::kFunctionApply): - // then print "apply from" and advance one frame - - Object* maybe_function = - Memory::Object_at(fp_ + JavaScriptFrameConstants::kFunctionOffset); - if (maybe_function->IsJSFunction()) { - JSFunction* function = JSFunction::cast(maybe_function); - JavaScriptFrame::PrintFunctionAndOffset(function, function->code(), pc(), - stdout, true); + if (!FLAG_trace_ic) return; + PrintF("[%s%s in ", is_keyed() ? "Keyed" : "", type); + + // TODO(jkummerow): Add support for "apply". The logic is roughly: + // marker = [fp_ + kMarkerOffset]; + // if marker is smi and marker.value == INTERNAL and + // the frame's code == builtin(Builtins::kFunctionApply): + // then print "apply from" and advance one frame + + Object* maybe_function = + Memory::Object_at(fp_ + JavaScriptFrameConstants::kFunctionOffset); + if (maybe_function->IsJSFunction()) { + JSFunction* function = JSFunction::cast(maybe_function); + int code_offset = 0; + if (function->IsInterpreted()) { + code_offset = InterpretedFrame::GetBytecodeOffset(fp()); + } else { + code_offset = + static_cast<int>(pc() - function->code()->instruction_start()); } + JavaScriptFrame::PrintFunctionAndOffset(function, function->abstract_code(), + code_offset, stdout, true); + } - const char* modifier = ""; - if (kind() == Code::KEYED_STORE_IC) { - KeyedAccessStoreMode mode = - casted_nexus<KeyedStoreICNexus>()->GetKeyedAccessStoreMode(); - modifier = GetTransitionMarkModifier(mode); - } - void* map = nullptr; - if (!receiver_map().is_null()) { - map = reinterpret_cast<void*>(*receiver_map()); - } - PrintF(" (%c->%c%s) map=%p ", TransitionMarkFromState(old_state), - TransitionMarkFromState(new_state), modifier, map); - name->ShortPrint(stdout); - PrintF("]\n"); + const char* modifier = ""; + if (kind() == Code::KEYED_STORE_IC) { + KeyedAccessStoreMode mode = + casted_nexus<KeyedStoreICNexus>()->GetKeyedAccessStoreMode(); + modifier = GetTransitionMarkModifier(mode); } + Map* map = nullptr; + if (!receiver_map().is_null()) { + map = *receiver_map(); + } + PrintF(" (%c->%c%s) map=(%p", TransitionMarkFromState(old_state), + TransitionMarkFromState(new_state), modifier, + reinterpret_cast<void*>(map)); + if (map != nullptr) { + PrintF(" dict=%u own=%u type=", map->is_dictionary_map(), + map->NumberOfOwnDescriptors()); + std::cout << map->instance_type(); + } + PrintF(") "); + name->ShortPrint(stdout); + PrintF("]\n"); } @@ -171,6 +187,16 @@ IC::IC(FrameDepth depth, Isolate* isolate, FeedbackNexus* nexus) StackFrame* frame = it.frame(); DCHECK(fp == frame->fp() && pc_address == frame->pc_address()); #endif + // For interpreted functions, some bytecode handlers construct a + // frame. We have to skip the constructed frame to find the interpreted + // function's frame. Check if the there is an additional frame, and if there + // is skip this frame. However, the pc should not be updated. The call to + // ICs happen from bytecode handlers. + Object* frame_type = + Memory::Object_at(fp + TypedFrameConstants::kFrameTypeOffset); + if (frame_type == Smi::FromInt(StackFrame::STUB)) { + fp = Memory::Address_at(fp + TypedFrameConstants::kCallerFPOffset); + } fp_ = fp; if (FLAG_enable_embedded_constant_pool) { constant_pool_address_ = constant_pool; @@ -224,11 +250,6 @@ SharedFunctionInfo* IC::GetSharedFunctionInfo() const { // corresponding to the frame. StackFrameIterator it(isolate()); while (it.frame()->fp() != this->fp()) it.Advance(); - if (FLAG_ignition && it.frame()->type() == StackFrame::STUB) { - // Advance over bytecode handler frame. - // TODO(rmcilroy): Remove this once bytecode handlers don't need a frame. - it.Advance(); - } JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame()); // Find the function on the stack and both the active code for the // function and the original code. @@ -504,19 +525,6 @@ void CompareIC::Clear(Isolate* isolate, Address address, Code* target, PatchInlinedSmiCode(isolate, address, DISABLE_INLINED_SMI_CHECK); } - -// static -Handle<Code> KeyedLoadIC::ChooseMegamorphicStub(Isolate* isolate, - ExtraICState extra_state) { - // TODO(ishell): remove extra_ic_state - if (FLAG_compiled_keyed_generic_loads) { - return KeyedLoadGenericStub(isolate).GetCode(); - } else { - return isolate->builtins()->KeyedLoadIC_Megamorphic(); - } -} - - static bool MigrateDeprecated(Handle<Object> object) { if (!object->IsJSObject()) return false; Handle<JSObject> receiver = Handle<JSObject>::cast(object); @@ -562,11 +570,11 @@ void IC::ConfigureVectorState(Handle<Name> name, Handle<Map> map, nexus->ConfigureMonomorphic(name, map, handler); } else if (kind() == Code::STORE_IC) { StoreICNexus* nexus = casted_nexus<StoreICNexus>(); - nexus->ConfigureMonomorphic(map, Handle<Code>::cast(handler)); + nexus->ConfigureMonomorphic(map, handler); } else { DCHECK(kind() == Code::KEYED_STORE_IC); KeyedStoreICNexus* nexus = casted_nexus<KeyedStoreICNexus>(); - nexus->ConfigureMonomorphic(name, map, Handle<Code>::cast(handler)); + nexus->ConfigureMonomorphic(name, map, handler); } vector_set_ = true; @@ -691,11 +699,8 @@ static bool AddOneReceiverMapIfMissing(MapHandleList* receiver_maps, return true; } -bool IC::UpdatePolymorphicIC(Handle<Name> name, Handle<Object> code) { - DCHECK(code->IsSmi() || code->IsCode()); - if (!code->IsSmi() && !Code::cast(*code)->is_handler()) { - return false; - } +bool IC::UpdatePolymorphicIC(Handle<Name> name, Handle<Object> handler) { + DCHECK(IsHandler(*handler)); if (is_keyed() && state() != RECOMPUTE_HANDLER) return false; Handle<Map> map = receiver_map(); MapHandleList maps; @@ -735,16 +740,16 @@ bool IC::UpdatePolymorphicIC(Handle<Name> name, Handle<Object> code) { number_of_valid_maps++; if (number_of_valid_maps > 1 && is_keyed()) return false; if (number_of_valid_maps == 1) { - ConfigureVectorState(name, receiver_map(), code); + ConfigureVectorState(name, receiver_map(), handler); } else { if (handler_to_overwrite >= 0) { - handlers.Set(handler_to_overwrite, code); + handlers.Set(handler_to_overwrite, handler); if (!map.is_identical_to(maps.at(handler_to_overwrite))) { maps.Set(handler_to_overwrite, map); } } else { maps.Add(map); - handlers.Add(code); + handlers.Add(handler); } ConfigureVectorState(name, &maps, &handlers); @@ -754,8 +759,7 @@ bool IC::UpdatePolymorphicIC(Handle<Name> name, Handle<Object> code) { } void IC::UpdateMonomorphicIC(Handle<Object> handler, Handle<Name> name) { - DCHECK(handler->IsSmi() || - (handler->IsCode() && Handle<Code>::cast(handler)->is_handler())); + DCHECK(IsHandler(*handler)); ConfigureVectorState(name, receiver_map(), handler); } @@ -786,24 +790,28 @@ bool IC::IsTransitionOfMonomorphicTarget(Map* source_map, Map* target_map) { return transitioned_map == target_map; } -void IC::PatchCache(Handle<Name> name, Handle<Object> code) { - DCHECK(code->IsCode() || (code->IsSmi() && (kind() == Code::LOAD_IC || - kind() == Code::KEYED_LOAD_IC))); +void IC::PatchCache(Handle<Name> name, Handle<Object> handler) { + DCHECK(IsHandler(*handler)); + // Currently only LoadIC and KeyedLoadIC support non-code handlers. + DCHECK_IMPLIES(!handler->IsCode(), kind() == Code::LOAD_IC || + kind() == Code::KEYED_LOAD_IC || + kind() == Code::STORE_IC || + kind() == Code::KEYED_STORE_IC); switch (state()) { case UNINITIALIZED: case PREMONOMORPHIC: - UpdateMonomorphicIC(code, name); + UpdateMonomorphicIC(handler, name); break; case RECOMPUTE_HANDLER: case MONOMORPHIC: if (kind() == Code::LOAD_GLOBAL_IC) { - UpdateMonomorphicIC(code, name); + UpdateMonomorphicIC(handler, name); break; } // Fall through. case POLYMORPHIC: if (!is_keyed() || state() == RECOMPUTE_HANDLER) { - if (UpdatePolymorphicIC(name, code)) break; + if (UpdatePolymorphicIC(name, handler)) break; // For keyed stubs, we can't know whether old handlers were for the // same key. CopyICToMegamorphicCache(name); @@ -812,7 +820,7 @@ void IC::PatchCache(Handle<Name> name, Handle<Object> code) { ConfigureVectorState(MEGAMORPHIC, name); // Fall through. case MEGAMORPHIC: - UpdateMegamorphicCache(*receiver_map(), *name, *code); + UpdateMegamorphicCache(*receiver_map(), *name, *handler); // Indicate that we've handled this case. DCHECK(UseVector()); vector_set_ = true; @@ -825,6 +833,7 @@ void IC::PatchCache(Handle<Name> name, Handle<Object> code) { Handle<Code> KeyedStoreIC::ChooseMegamorphicStub(Isolate* isolate, ExtraICState extra_state) { + DCHECK(!FLAG_tf_store_ic_stub); LanguageMode mode = StoreICState::GetLanguageMode(extra_state); return is_strict(mode) ? isolate->builtins()->KeyedStoreIC_Megamorphic_Strict() @@ -833,13 +842,186 @@ Handle<Code> KeyedStoreIC::ChooseMegamorphicStub(Isolate* isolate, Handle<Object> LoadIC::SimpleFieldLoad(FieldIndex index) { if (FLAG_tf_load_ic_stub) { - return handle(Smi::FromInt(index.GetLoadByFieldOffset()), isolate()); + TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldDH); + return LoadHandler::LoadField(isolate(), index); } TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldStub); LoadFieldStub stub(isolate(), index); return stub.GetCode(); } +namespace { + +template <bool fill_array = true> +int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map, + Handle<JSObject> holder, Handle<Name> name, + Handle<FixedArray> array, int first_index) { + DCHECK(holder.is_null() || holder->HasFastProperties()); + + // We don't encode the requirement to check access rights because we already + // passed the access check for current native context and the access + // can't be revoked. + + HandleScope scope(isolate); + int checks_count = 0; + + if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap()) { + // The validity cell check for primitive and global proxy receivers does + // not guarantee that certain native context ever had access to other + // native context. However, a handler created for one native context could + // be used in other native context through the megamorphic stub cache. + // So we record the original native context to which this handler + // corresponds. + if (fill_array) { + Handle<Context> native_context = isolate->native_context(); + array->set(LoadHandler::kFirstPrototypeIndex + checks_count, + native_context->self_weak_cell()); + } + checks_count++; + + } else if (receiver_map->IsJSGlobalObjectMap()) { + if (fill_array) { + Handle<JSGlobalObject> global = isolate->global_object(); + Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell( + global, name, PropertyCellType::kInvalidated); + DCHECK(cell->value()->IsTheHole(isolate)); + Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell); + array->set(LoadHandler::kFirstPrototypeIndex + checks_count, *weak_cell); + } + checks_count++; + } + + // Create/count entries for each global or dictionary prototype appeared in + // the prototype chain contains from receiver till holder. + PrototypeIterator::WhereToEnd end = name->IsPrivate() + ? PrototypeIterator::END_AT_NON_HIDDEN + : PrototypeIterator::END_AT_NULL; + for (PrototypeIterator iter(receiver_map, end); !iter.IsAtEnd(); + iter.Advance()) { + Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter); + if (holder.is_identical_to(current)) break; + Handle<Map> current_map(current->map(), isolate); + + if (current_map->IsJSGlobalObjectMap()) { + if (fill_array) { + Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(current); + Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell( + global, name, PropertyCellType::kInvalidated); + DCHECK(cell->value()->IsTheHole(isolate)); + Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell); + array->set(first_index + checks_count, *weak_cell); + } + checks_count++; + + } else if (current_map->is_dictionary_map()) { + DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. + if (fill_array) { + DCHECK_EQ(NameDictionary::kNotFound, + current->property_dictionary()->FindEntry(name)); + Handle<WeakCell> weak_cell = + Map::GetOrCreatePrototypeWeakCell(current, isolate); + array->set(first_index + checks_count, *weak_cell); + } + checks_count++; + } + } + return checks_count; +} + +// Returns 0 if the validity cell check is enough to ensure that the +// prototype chain from |receiver_map| till |holder| did not change. +// If the |holder| is an empty handle then the full prototype chain is +// checked. +// Returns -1 if the handler has to be compiled or the number of prototype +// checks otherwise. +int GetPrototypeCheckCount(Isolate* isolate, Handle<Map> receiver_map, + Handle<JSObject> holder, Handle<Name> name) { + return InitPrototypeChecks<false>(isolate, receiver_map, holder, name, + Handle<FixedArray>(), 0); +} + +} // namespace + +Handle<Object> LoadIC::LoadFromPrototype(Handle<Map> receiver_map, + Handle<JSObject> holder, + Handle<Name> name, + Handle<Object> smi_handler) { + int checks_count = + GetPrototypeCheckCount(isolate(), receiver_map, holder, name); + DCHECK_LE(0, checks_count); + + if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap()) { + DCHECK(!receiver_map->is_dictionary_map()); + DCHECK_LE(1, checks_count); // For native context. + smi_handler = + LoadHandler::EnableAccessCheckOnReceiver(isolate(), smi_handler); + } else if (receiver_map->is_dictionary_map() && + !receiver_map->IsJSGlobalObjectMap()) { + smi_handler = + LoadHandler::EnableNegativeLookupOnReceiver(isolate(), smi_handler); + } + + Handle<Cell> validity_cell = + Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate()); + DCHECK(!validity_cell.is_null()); + + Handle<WeakCell> holder_cell = + Map::GetOrCreatePrototypeWeakCell(holder, isolate()); + + if (checks_count == 0) { + return isolate()->factory()->NewTuple3(holder_cell, smi_handler, + validity_cell); + } + Handle<FixedArray> handler_array(isolate()->factory()->NewFixedArray( + LoadHandler::kFirstPrototypeIndex + checks_count, TENURED)); + handler_array->set(LoadHandler::kSmiHandlerIndex, *smi_handler); + handler_array->set(LoadHandler::kValidityCellIndex, *validity_cell); + handler_array->set(LoadHandler::kHolderCellIndex, *holder_cell); + InitPrototypeChecks(isolate(), receiver_map, holder, name, handler_array, + LoadHandler::kFirstPrototypeIndex); + return handler_array; +} + +Handle<Object> LoadIC::LoadNonExistent(Handle<Map> receiver_map, + Handle<Name> name) { + Handle<JSObject> holder; // null handle + int checks_count = + GetPrototypeCheckCount(isolate(), receiver_map, holder, name); + DCHECK_LE(0, checks_count); + + bool do_negative_lookup_on_receiver = + receiver_map->is_dictionary_map() && !receiver_map->IsJSGlobalObjectMap(); + Handle<Object> smi_handler = + LoadHandler::LoadNonExistent(isolate(), do_negative_lookup_on_receiver); + + if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap()) { + DCHECK(!receiver_map->is_dictionary_map()); + DCHECK_LE(1, checks_count); // For native context. + smi_handler = + LoadHandler::EnableAccessCheckOnReceiver(isolate(), smi_handler); + } + + Handle<Object> validity_cell = + Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate()); + if (validity_cell.is_null()) { + DCHECK_EQ(0, checks_count); + validity_cell = handle(Smi::FromInt(0), isolate()); + } + + Factory* factory = isolate()->factory(); + if (checks_count == 0) { + return factory->NewTuple3(factory->null_value(), smi_handler, + validity_cell); + } + Handle<FixedArray> handler_array(factory->NewFixedArray( + LoadHandler::kFirstPrototypeIndex + checks_count, TENURED)); + handler_array->set(LoadHandler::kSmiHandlerIndex, *smi_handler); + handler_array->set(LoadHandler::kValidityCellIndex, *validity_cell); + handler_array->set(LoadHandler::kHolderCellIndex, *factory->null_value()); + InitPrototypeChecks(isolate(), receiver_map, holder, name, handler_array, + LoadHandler::kFirstPrototypeIndex); + return handler_array; +} bool IsCompatibleReceiver(LookupIterator* lookup, Handle<Map> receiver_map) { DCHECK(lookup->state() == LookupIterator::ACCESSOR); @@ -884,6 +1066,7 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) { if (state() == UNINITIALIZED && kind() != Code::LOAD_GLOBAL_IC) { // This is the first time we execute this inline cache. Set the target to // the pre monomorphic stub to delay setting the monomorphic state. + TRACE_HANDLER_STATS(isolate(), LoadIC_Premonomorphic); ConfigureVectorState(PREMONOMORPHIC, Handle<Object>()); TRACE_IC("LoadIC", lookup->name()); return; @@ -894,7 +1077,10 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) { lookup->state() == LookupIterator::ACCESS_CHECK) { code = slow_stub(); } else if (!lookup->IsFound()) { - if (kind() == Code::LOAD_IC || kind() == Code::LOAD_GLOBAL_IC) { + if (kind() == Code::LOAD_IC) { + TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonexistentDH); + code = LoadNonExistent(receiver_map(), lookup->name()); + } else if (kind() == Code::LOAD_GLOBAL_IC) { code = NamedLoadHandlerCompiler::ComputeLoadNonexistent(lookup->name(), receiver_map()); // TODO(jkummerow/verwaest): Introduce a builtin that handles this case. @@ -964,30 +1150,80 @@ StubCache* IC::stub_cache() { return nullptr; } -void IC::UpdateMegamorphicCache(Map* map, Name* name, Object* code) { - if (code->IsSmi()) { - // TODO(jkummerow): Support Smis in the code cache. - Handle<Map> map_handle(map, isolate()); - Handle<Name> name_handle(name, isolate()); - FieldIndex index = - FieldIndex::ForLoadByFieldOffset(map, Smi::cast(code)->value()); - TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldStub); - LoadFieldStub stub(isolate(), index); - Code* handler = *stub.GetCode(); - stub_cache()->Set(*name_handle, *map_handle, handler); - return; +void IC::UpdateMegamorphicCache(Map* map, Name* name, Object* handler) { + stub_cache()->Set(name, map, handler); +} + +void IC::TraceHandlerCacheHitStats(LookupIterator* lookup) { + if (!FLAG_runtime_call_stats) return; + + if (kind() == Code::LOAD_IC || kind() == Code::LOAD_GLOBAL_IC || + kind() == Code::KEYED_LOAD_IC) { + switch (lookup->state()) { + case LookupIterator::ACCESS_CHECK: + TRACE_HANDLER_STATS(isolate(), LoadIC_HandlerCacheHit_AccessCheck); + break; + case LookupIterator::INTEGER_INDEXED_EXOTIC: + TRACE_HANDLER_STATS(isolate(), LoadIC_HandlerCacheHit_Exotic); + break; + case LookupIterator::INTERCEPTOR: + TRACE_HANDLER_STATS(isolate(), LoadIC_HandlerCacheHit_Interceptor); + break; + case LookupIterator::JSPROXY: + TRACE_HANDLER_STATS(isolate(), LoadIC_HandlerCacheHit_JSProxy); + break; + case LookupIterator::NOT_FOUND: + TRACE_HANDLER_STATS(isolate(), LoadIC_HandlerCacheHit_NonExistent); + break; + case LookupIterator::ACCESSOR: + TRACE_HANDLER_STATS(isolate(), LoadIC_HandlerCacheHit_Accessor); + break; + case LookupIterator::DATA: + TRACE_HANDLER_STATS(isolate(), LoadIC_HandlerCacheHit_Data); + break; + case LookupIterator::TRANSITION: + TRACE_HANDLER_STATS(isolate(), LoadIC_HandlerCacheHit_Transition); + break; + } + } else if (kind() == Code::STORE_IC || kind() == Code::KEYED_STORE_IC) { + switch (lookup->state()) { + case LookupIterator::ACCESS_CHECK: + TRACE_HANDLER_STATS(isolate(), StoreIC_HandlerCacheHit_AccessCheck); + break; + case LookupIterator::INTEGER_INDEXED_EXOTIC: + TRACE_HANDLER_STATS(isolate(), StoreIC_HandlerCacheHit_Exotic); + break; + case LookupIterator::INTERCEPTOR: + TRACE_HANDLER_STATS(isolate(), StoreIC_HandlerCacheHit_Interceptor); + break; + case LookupIterator::JSPROXY: + TRACE_HANDLER_STATS(isolate(), StoreIC_HandlerCacheHit_JSProxy); + break; + case LookupIterator::NOT_FOUND: + TRACE_HANDLER_STATS(isolate(), StoreIC_HandlerCacheHit_NonExistent); + break; + case LookupIterator::ACCESSOR: + TRACE_HANDLER_STATS(isolate(), StoreIC_HandlerCacheHit_Accessor); + break; + case LookupIterator::DATA: + TRACE_HANDLER_STATS(isolate(), StoreIC_HandlerCacheHit_Data); + break; + case LookupIterator::TRANSITION: + TRACE_HANDLER_STATS(isolate(), StoreIC_HandlerCacheHit_Transition); + break; + } + } else { + TRACE_HANDLER_STATS(isolate(), IC_HandlerCacheHit); } - DCHECK(code->IsCode()); - stub_cache()->Set(name, map, Code::cast(code)); } Handle<Object> IC::ComputeHandler(LookupIterator* lookup, Handle<Object> value) { // Try to find a globally shared handler stub. - Handle<Object> handler_or_index = GetMapIndependentHandler(lookup); - if (!handler_or_index.is_null()) { - DCHECK(handler_or_index->IsCode() || handler_or_index->IsSmi()); - return handler_or_index; + Handle<Object> shared_handler = GetMapIndependentHandler(lookup); + if (!shared_handler.is_null()) { + DCHECK(IC::IsHandler(*shared_handler)); + return shared_handler; } // Otherwise check the map's handler cache for a map-specific handler, and @@ -1007,16 +1243,16 @@ Handle<Object> IC::ComputeHandler(LookupIterator* lookup, stub_holder_map = receiver_map(); } - Handle<Code> code = PropertyHandlerCompiler::Find( + Handle<Object> handler = PropertyHandlerCompiler::Find( lookup->name(), stub_holder_map, kind(), flag); // Use the cached value if it exists, and if it is different from the // handler that just missed. - if (!code.is_null()) { - Handle<Object> handler; - if (maybe_handler_.ToHandle(&handler)) { - if (!handler.is_identical_to(code)) { - TRACE_HANDLER_STATS(isolate(), IC_HandlerCacheHit); - return code; + if (!handler.is_null()) { + Handle<Object> current_handler; + if (maybe_handler_.ToHandle(¤t_handler)) { + if (!current_handler.is_identical_to(handler)) { + TraceHandlerCacheHitStats(lookup); + return handler; } } else { // maybe_handler_ is only populated for MONOMORPHIC and POLYMORPHIC ICs. @@ -1024,24 +1260,27 @@ Handle<Object> IC::ComputeHandler(LookupIterator* lookup, // cache (which just missed) is different from the cached handler. if (state() == MEGAMORPHIC && lookup->GetReceiver()->IsHeapObject()) { Map* map = Handle<HeapObject>::cast(lookup->GetReceiver())->map(); - Code* megamorphic_cached_code = stub_cache()->Get(*lookup->name(), map); - if (megamorphic_cached_code != *code) { - TRACE_HANDLER_STATS(isolate(), IC_HandlerCacheHit); - return code; + Object* megamorphic_cached_handler = + stub_cache()->Get(*lookup->name(), map); + if (megamorphic_cached_handler != *handler) { + TraceHandlerCacheHitStats(lookup); + return handler; } } else { - TRACE_HANDLER_STATS(isolate(), IC_HandlerCacheHit); - return code; + TraceHandlerCacheHitStats(lookup); + return handler; } } } - code = CompileHandler(lookup, value, flag); - DCHECK(code->is_handler()); - DCHECK(Code::ExtractCacheHolderFromFlags(code->flags()) == flag); - Map::UpdateCodeCache(stub_holder_map, lookup->name(), code); - - return code; + handler = CompileHandler(lookup, value, flag); + DCHECK(IC::IsHandler(*handler)); + if (handler->IsCode()) { + Handle<Code> code = Handle<Code>::cast(handler); + DCHECK_EQ(Code::ExtractCacheHolderFromFlags(code->flags()), flag); + Map::UpdateCodeCache(stub_holder_map, lookup->name(), code); + } + return handler; } Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) { @@ -1111,17 +1350,33 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) { } // Ruled out by IsCompatibleReceiver() above. DCHECK(AccessorInfo::IsCompatibleReceiverMap(isolate(), info, map)); - if (!holder->HasFastProperties()) return slow_stub(); - if (receiver_is_holder) { - TRACE_HANDLER_STATS(isolate(), LoadIC_LoadApiGetterStub); - int index = lookup->GetAccessorIndex(); - LoadApiGetterStub stub(isolate(), true, index); - return stub.GetCode(); - } - if (info->is_sloppy() && !receiver->IsJSReceiver()) { + if (!holder->HasFastProperties() || + (info->is_sloppy() && !receiver->IsJSReceiver())) { + DCHECK(!holder->HasFastProperties() || !receiver_is_holder); TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub); return slow_stub(); } + if (FLAG_tf_load_ic_stub) { + Handle<Object> smi_handler = LoadHandler::LoadApiGetter( + isolate(), lookup->GetAccessorIndex()); + if (receiver_is_holder) { + TRACE_HANDLER_STATS(isolate(), LoadIC_LoadApiGetterDH); + return smi_handler; + } + if (kind() != Code::LOAD_GLOBAL_IC) { + TRACE_HANDLER_STATS(isolate(), + LoadIC_LoadApiGetterFromPrototypeDH); + return LoadFromPrototype(map, holder, lookup->name(), + smi_handler); + } + } else { + if (receiver_is_holder) { + TRACE_HANDLER_STATS(isolate(), LoadIC_LoadApiGetterStub); + int index = lookup->GetAccessorIndex(); + LoadApiGetterStub stub(isolate(), true, index); + return stub.GetCode(); + } + } break; // Custom-compiled handler. } } @@ -1153,18 +1408,36 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) { // -------------- Fields -------------- if (lookup->property_details().type() == DATA) { FieldIndex field = lookup->GetFieldIndex(); + Handle<Object> smi_handler = SimpleFieldLoad(field); if (receiver_is_holder) { - return SimpleFieldLoad(field); + return smi_handler; + } + if (FLAG_tf_load_ic_stub && kind() != Code::LOAD_GLOBAL_IC) { + TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldFromPrototypeDH); + return LoadFromPrototype(map, holder, lookup->name(), smi_handler); } break; // Custom-compiled handler. } // -------------- Constant properties -------------- DCHECK(lookup->property_details().type() == DATA_CONSTANT); - if (receiver_is_holder) { - TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstantStub); - LoadConstantStub stub(isolate(), lookup->GetConstantIndex()); - return stub.GetCode(); + if (FLAG_tf_load_ic_stub) { + Handle<Object> smi_handler = + LoadHandler::LoadConstant(isolate(), lookup->GetConstantIndex()); + if (receiver_is_holder) { + TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstantDH); + return smi_handler; + } + if (kind() != Code::LOAD_GLOBAL_IC) { + TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstantFromPrototypeDH); + return LoadFromPrototype(map, holder, lookup->name(), smi_handler); + } + } else { + if (receiver_is_holder) { + TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstantStub); + LoadConstantStub stub(isolate(), lookup->GetConstantIndex()); + return stub.GetCode(); + } } break; // Custom-compiled handler. } @@ -1182,9 +1455,9 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) { return Handle<Code>::null(); } -Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup, - Handle<Object> unused, - CacheHolderFlag cache_holder) { +Handle<Object> LoadIC::CompileHandler(LookupIterator* lookup, + Handle<Object> unused, + CacheHolderFlag cache_holder) { Handle<JSObject> holder = lookup->GetHolder<JSObject>(); #ifdef DEBUG // Only used by DCHECKs below. @@ -1229,6 +1502,10 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup, DCHECK(IsCompatibleReceiver(lookup, map)); Handle<Object> accessors = lookup->GetAccessors(); if (accessors->IsAccessorPair()) { + if (lookup->TryLookupCachedProperty()) { + DCHECK_EQ(LookupIterator::DATA, lookup->state()); + return ComputeHandler(lookup); + } DCHECK(holder->HasFastProperties()); DCHECK(!GetSharedFunctionInfo()->HasDebugInfo()); Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(), @@ -1421,7 +1698,9 @@ MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object, if ((object->IsJSObject() && key->IsSmi()) || (object->IsString() && key->IsNumber())) { UpdateLoadElement(Handle<HeapObject>::cast(object)); - TRACE_IC("LoadIC", key); + if (is_vector_set()) { + TRACE_IC("LoadIC", key); + } } } @@ -1580,6 +1859,7 @@ void StoreIC::UpdateCaches(LookupIterator* lookup, Handle<Object> value, if (state() == UNINITIALIZED) { // This is the first time we execute this inline cache. Set the target to // the pre monomorphic stub to delay setting the monomorphic state. + TRACE_HANDLER_STATS(isolate(), StoreIC_Premonomorphic); ConfigureVectorState(PREMONOMORPHIC, Handle<Object>()); TRACE_IC("StoreIC", lookup->name()); return; @@ -1589,13 +1869,72 @@ void StoreIC::UpdateCaches(LookupIterator* lookup, Handle<Object> value, if (!use_ic) { TRACE_GENERIC_IC(isolate(), "StoreIC", "LookupForWrite said 'false'"); } - Handle<Code> code = - use_ic ? Handle<Code>::cast(ComputeHandler(lookup, value)) : slow_stub(); + Handle<Object> handler = use_ic ? ComputeHandler(lookup, value) + : Handle<Object>::cast(slow_stub()); - PatchCache(lookup->name(), code); + PatchCache(lookup->name(), handler); TRACE_IC("StoreIC", lookup->name()); } +Handle<Object> StoreIC::StoreTransition(Handle<Map> receiver_map, + Handle<JSObject> holder, + Handle<Map> transition, + Handle<Name> name) { + int descriptor = transition->LastAdded(); + Handle<DescriptorArray> descriptors(transition->instance_descriptors()); + PropertyDetails details = descriptors->GetDetails(descriptor); + Representation representation = details.representation(); + DCHECK(!representation.IsNone()); + + // Declarative handlers don't support access checks. + DCHECK(!transition->is_access_check_needed()); + + Handle<Object> smi_handler; + if (details.type() == DATA_CONSTANT) { + smi_handler = StoreHandler::TransitionToConstant(isolate(), descriptor); + + } else { + DCHECK_EQ(DATA, details.type()); + bool extend_storage = + Map::cast(transition->GetBackPointer())->unused_property_fields() == 0; + + FieldIndex index = FieldIndex::ForDescriptor(*transition, descriptor); + smi_handler = StoreHandler::TransitionToField( + isolate(), descriptor, index, representation, extend_storage); + } + // |holder| is either a receiver if the property is non-existent or + // one of the prototypes. + DCHECK(!holder.is_null()); + bool is_nonexistent = holder->map() == transition->GetBackPointer(); + if (is_nonexistent) holder = Handle<JSObject>::null(); + + int checks_count = + GetPrototypeCheckCount(isolate(), receiver_map, holder, name); + DCHECK_LE(0, checks_count); + DCHECK(!receiver_map->IsJSGlobalObjectMap()); + + Handle<Object> validity_cell = + Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate()); + if (validity_cell.is_null()) { + DCHECK_EQ(0, checks_count); + validity_cell = handle(Smi::FromInt(0), isolate()); + } + + Handle<WeakCell> transition_cell = Map::WeakCellForMap(transition); + + Factory* factory = isolate()->factory(); + if (checks_count == 0) { + return factory->NewTuple3(transition_cell, smi_handler, validity_cell); + } + Handle<FixedArray> handler_array(factory->NewFixedArray( + StoreHandler::kFirstPrototypeIndex + checks_count, TENURED)); + handler_array->set(StoreHandler::kSmiHandlerIndex, *smi_handler); + handler_array->set(StoreHandler::kValidityCellIndex, *validity_cell); + handler_array->set(StoreHandler::kTransitionCellIndex, *transition_cell); + InitPrototypeChecks(isolate(), receiver_map, holder, name, handler_array, + StoreHandler::kFirstPrototypeIndex); + return handler_array; +} static Handle<Code> PropertyCellStoreHandler( Isolate* isolate, Handle<JSObject> receiver, Handle<JSGlobalObject> holder, @@ -1632,8 +1971,13 @@ Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) { TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); return slow_stub(); } - DCHECK(lookup->IsCacheableTransition()); + if (FLAG_tf_store_ic_stub) { + Handle<Map> transition = lookup->transition_map(); + TRACE_HANDLER_STATS(isolate(), StoreIC_StoreTransitionDH); + return StoreTransition(receiver_map(), holder, transition, + lookup->name()); + } break; // Custom-compiled handler. } @@ -1711,17 +2055,25 @@ Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) { // -------------- Fields -------------- if (lookup->property_details().type() == DATA) { - bool use_stub = true; - if (lookup->representation().IsHeapObject()) { - // Only use a generic stub if no types need to be tracked. - Handle<FieldType> field_type = lookup->GetFieldType(); - use_stub = !field_type->IsClass(); - } - if (use_stub) { - TRACE_HANDLER_STATS(isolate(), StoreIC_StoreFieldStub); - StoreFieldStub stub(isolate(), lookup->GetFieldIndex(), - lookup->representation()); - return stub.GetCode(); + if (FLAG_tf_store_ic_stub) { + TRACE_HANDLER_STATS(isolate(), StoreIC_StoreFieldDH); + int descriptor = lookup->GetFieldDescriptorIndex(); + FieldIndex index = lookup->GetFieldIndex(); + return StoreHandler::StoreField(isolate(), descriptor, index, + lookup->representation()); + } else { + bool use_stub = true; + if (lookup->representation().IsHeapObject()) { + // Only use a generic stub if no types need to be tracked. + Handle<FieldType> field_type = lookup->GetFieldType(); + use_stub = !field_type->IsClass(); + } + if (use_stub) { + TRACE_HANDLER_STATS(isolate(), StoreIC_StoreFieldStub); + StoreFieldStub stub(isolate(), lookup->GetFieldIndex(), + lookup->representation()); + return stub.GetCode(); + } } break; // Custom-compiled handler. } @@ -1742,9 +2094,9 @@ Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) { return Handle<Code>::null(); } -Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup, - Handle<Object> value, - CacheHolderFlag cache_holder) { +Handle<Object> StoreIC::CompileHandler(LookupIterator* lookup, + Handle<Object> value, + CacheHolderFlag cache_holder) { DCHECK_NE(LookupIterator::JSPROXY, lookup->state()); // This is currently guaranteed by checks in StoreIC::Store. @@ -1765,6 +2117,7 @@ Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup, cell->set_value(isolate()->heap()->the_hole_value()); return code; } + DCHECK(!FLAG_tf_store_ic_stub); Handle<Map> transition = lookup->transition_map(); // Currently not handled by CompileStoreTransition. DCHECK(holder->HasFastProperties()); @@ -1836,6 +2189,7 @@ Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup, // -------------- Fields -------------- if (lookup->property_details().type() == DATA) { + DCHECK(!FLAG_tf_store_ic_stub); #ifdef DEBUG bool use_stub = true; if (lookup->representation().IsHeapObject()) { @@ -1981,7 +2335,6 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map, } } - TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_Polymorphic); MapHandleList transitioned_maps(target_receiver_maps.length()); CodeHandleList handlers(target_receiver_maps.length()); PropertyICCompiler::ComputeKeyedStorePolymorphicHandlers( @@ -2241,7 +2594,6 @@ void CallIC::HandleMiss(Handle<Object> function) { // Used from ic-<arch>.cc. RUNTIME_FUNCTION(Runtime_CallIC_Miss) { - TimerEventScope<TimerEventIcMiss> timer(isolate); HandleScope scope(isolate); DCHECK_EQ(3, args.length()); // Runtime functions don't follow the IC's calling convention. @@ -2258,7 +2610,6 @@ RUNTIME_FUNCTION(Runtime_CallIC_Miss) { // Used from ic-<arch>.cc. RUNTIME_FUNCTION(Runtime_LoadIC_Miss) { - TimerEventScope<TimerEventIcMiss> timer(isolate); HandleScope scope(isolate); DCHECK_EQ(4, args.length()); // Runtime functions don't follow the IC's calling convention. @@ -2279,7 +2630,7 @@ RUNTIME_FUNCTION(Runtime_LoadIC_Miss) { } else if (kind == FeedbackVectorSlotKind::LOAD_GLOBAL_IC) { Handle<Name> key(vector->GetName(vector_slot), isolate); - DCHECK_NE(*key, *isolate->factory()->empty_string()); + DCHECK_NE(*key, isolate->heap()->empty_string()); DCHECK_EQ(*isolate->global_object(), *receiver); LoadGlobalICNexus nexus(vector, vector_slot); LoadGlobalIC ic(IC::NO_EXTRA_FRAME, isolate, &nexus); @@ -2298,7 +2649,6 @@ RUNTIME_FUNCTION(Runtime_LoadIC_Miss) { // Used from ic-<arch>.cc. RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Miss) { - TimerEventScope<TimerEventIcMiss> timer(isolate); HandleScope scope(isolate); DCHECK_EQ(2, args.length()); // Runtime functions don't follow the IC's calling convention. @@ -2309,7 +2659,7 @@ RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Miss) { DCHECK_EQ(FeedbackVectorSlotKind::LOAD_GLOBAL_IC, vector->GetKind(vector_slot)); Handle<String> name(vector->GetName(vector_slot), isolate); - DCHECK_NE(*name, *isolate->factory()->empty_string()); + DCHECK_NE(*name, isolate->heap()->empty_string()); LoadGlobalICNexus nexus(vector, vector_slot); LoadGlobalIC ic(IC::NO_EXTRA_FRAME, isolate, &nexus); @@ -2330,7 +2680,7 @@ RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Slow) { DCHECK_EQ(FeedbackVectorSlotKind::LOAD_GLOBAL_IC, vector->GetKind(vector_slot)); Handle<String> name(vector->GetName(vector_slot), isolate); - DCHECK_NE(*name, *isolate->factory()->empty_string()); + DCHECK_NE(*name, isolate->heap()->empty_string()); Handle<JSGlobalObject> global = isolate->global_object(); @@ -2343,7 +2693,7 @@ RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Slow) { script_contexts, lookup_result.context_index); Handle<Object> result = FixedArray::get(*script_context, lookup_result.slot_index, isolate); - if (*result == *isolate->factory()->the_hole_value()) { + if (*result == isolate->heap()->the_hole_value()) { THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewReferenceError(MessageTemplate::kNotDefined, name)); } @@ -2370,7 +2720,6 @@ RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Slow) { // Used from ic-<arch>.cc RUNTIME_FUNCTION(Runtime_KeyedLoadIC_Miss) { - TimerEventScope<TimerEventIcMiss> timer(isolate); HandleScope scope(isolate); DCHECK_EQ(4, args.length()); // Runtime functions don't follow the IC's calling convention. @@ -2387,7 +2736,6 @@ RUNTIME_FUNCTION(Runtime_KeyedLoadIC_Miss) { RUNTIME_FUNCTION(Runtime_KeyedLoadIC_MissFromStubFailure) { - TimerEventScope<TimerEventIcMiss> timer(isolate); HandleScope scope(isolate); typedef LoadWithVectorDescriptor Descriptor; DCHECK_EQ(Descriptor::kParameterCount, args.length()); @@ -2406,7 +2754,6 @@ RUNTIME_FUNCTION(Runtime_KeyedLoadIC_MissFromStubFailure) { // Used from ic-<arch>.cc. RUNTIME_FUNCTION(Runtime_StoreIC_Miss) { - TimerEventScope<TimerEventIcMiss> timer(isolate); HandleScope scope(isolate); DCHECK_EQ(5, args.length()); // Runtime functions don't follow the IC's calling convention. @@ -2434,7 +2781,6 @@ RUNTIME_FUNCTION(Runtime_StoreIC_Miss) { // Used from ic-<arch>.cc. RUNTIME_FUNCTION(Runtime_KeyedStoreIC_Miss) { - TimerEventScope<TimerEventIcMiss> timer(isolate); HandleScope scope(isolate); DCHECK_EQ(5, args.length()); // Runtime functions don't follow the IC's calling convention. @@ -2470,7 +2816,6 @@ RUNTIME_FUNCTION(Runtime_KeyedStoreIC_Slow) { RUNTIME_FUNCTION(Runtime_ElementsTransitionAndStoreIC_Miss) { - TimerEventScope<TimerEventIcMiss> timer(isolate); HandleScope scope(isolate); // Runtime functions don't follow the IC's calling convention. Handle<Object> object = args.at<Object>(0); @@ -2609,7 +2954,6 @@ MaybeHandle<Object> BinaryOpIC::Transition( RUNTIME_FUNCTION(Runtime_BinaryOpIC_Miss) { - TimerEventScope<TimerEventIcMiss> timer(isolate); HandleScope scope(isolate); DCHECK_EQ(2, args.length()); typedef BinaryOpDescriptor Descriptor; @@ -2622,7 +2966,6 @@ RUNTIME_FUNCTION(Runtime_BinaryOpIC_Miss) { RUNTIME_FUNCTION(Runtime_BinaryOpIC_MissWithAllocationSite) { - TimerEventScope<TimerEventIcMiss> timer(isolate); HandleScope scope(isolate); DCHECK_EQ(3, args.length()); typedef BinaryOpWithAllocationSiteDescriptor Descriptor; @@ -2686,7 +3029,6 @@ Code* CompareIC::UpdateCaches(Handle<Object> x, Handle<Object> y) { // Used from CompareICStub::GenerateMiss in code-stubs-<arch>.cc. RUNTIME_FUNCTION(Runtime_CompareIC_Miss) { - TimerEventScope<TimerEventIcMiss> timer(isolate); HandleScope scope(isolate); DCHECK(args.length() == 3); CompareIC ic(isolate, static_cast<Token::Value>(args.smi_at(2))); @@ -2711,7 +3053,6 @@ Handle<Object> ToBooleanIC::ToBoolean(Handle<Object> object) { RUNTIME_FUNCTION(Runtime_ToBooleanIC_Miss) { - TimerEventScope<TimerEventIcMiss> timer(isolate); DCHECK(args.length() == 1); HandleScope scope(isolate); Handle<Object> object = args.at<Object>(0); @@ -2729,7 +3070,7 @@ RUNTIME_FUNCTION(Runtime_StoreCallbackProperty) { CONVERT_LANGUAGE_MODE_ARG_CHECKED(language_mode, 5); HandleScope scope(isolate); - if (FLAG_runtime_call_stats) { + if (V8_UNLIKELY(FLAG_runtime_stats)) { RETURN_RESULT_OR_FAILURE( isolate, Runtime::SetObjectProperty(isolate, receiver, name, value, language_mode)); |