From 27ad136bdb01b31e789646252d43a2da03b98d48 Mon Sep 17 00:00:00 2001 From: "mvstanton@chromium.org" Date: Tue, 28 Oct 2014 14:50:18 +0000 Subject: [PATCH] Introduce FeedbackNexus for vector-based ics. A FeedbackNexus is the combination of a feedback vector, a slot(s) in the vector, along with methods to query and manipulate that information in a type-correct way. A CallIC will have a CallICNexus, a LoadIC a LoadICNexus, etc., reflecting the fact that different types of ICs configure their data in unique ways. This CL limits itself to introducing and using the nexus type only for CallICs. A follow-up will use them for Load and KeyedLoadICs for the case when the --vector-ics flag is turned on. The notion of a Nexus is also embedded at the lowest level of the IC class. This makes sense because more ICs should become vector-based in the future. This CL is based on https://codereview.chromium.org/679073002/ which should land first. BUG= R=ishell@chromium.org Review URL: https://codereview.chromium.org/680883004 Cr-Commit-Position: refs/heads/master@{#24945} git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24945 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/ic/ic-inl.h | 20 +--- src/ic/ic-state.cc | 10 +- src/ic/ic-state.h | 3 +- src/ic/ic.cc | 170 ++++++++++++++-------------- src/ic/ic.h | 72 ++++++++---- src/type-feedback-vector.cc | 136 +++++++++++++++++++++- src/type-feedback-vector.h | 99 ++++++++++++++++ test/cctest/test-feedback-vector.cc | 48 +++++++- 8 files changed, 420 insertions(+), 138 deletions(-) diff --git a/src/ic/ic-inl.h b/src/ic/ic-inl.h index 91b8955fd4..22f66d0409 100644 --- a/src/ic/ic-inl.h +++ b/src/ic/ic-inl.h @@ -208,30 +208,12 @@ Handle IC::GetICCacheHolder(HeapType* type, Isolate* isolate, } -inline Code* CallIC::get_host() { +inline Code* IC::get_host() { return isolate() ->inner_pointer_to_code_cache() ->GetCacheEntry(address()) ->code; } - - -// static -IC::State CallIC::FeedbackToState(Isolate* isolate, TypeFeedbackVector* vector, - FeedbackVectorICSlot slot) { - IC::State state = UNINITIALIZED; - Object* feedback = vector->Get(slot); - - if (feedback == *TypeFeedbackVector::MegamorphicSentinel(isolate)) { - state = GENERIC; - } else if (feedback->IsAllocationSite() || feedback->IsJSFunction()) { - state = MONOMORPHIC; - } else { - CHECK(feedback == *TypeFeedbackVector::UninitializedSentinel(isolate)); - } - - return state; -} } } // namespace v8::internal diff --git a/src/ic/ic-state.cc b/src/ic/ic-state.cc index 5a17bc8688..18ea7f3105 100644 --- a/src/ic/ic-state.cc +++ b/src/ic/ic-state.cc @@ -18,12 +18,18 @@ void ICUtility::Clear(Isolate* isolate, Address address, // static +template void ICUtility::Clear(Isolate* isolate, Code::Kind kind, Code* host, - TypeFeedbackVector* vector, FeedbackVectorICSlot slot) { - IC::Clear(isolate, kind, host, vector, slot); + Nexus* nexus) { + IC::Clear(isolate, kind, host, nexus); } +// Force instantiation of template instances for vector-based IC clearing. +template void ICUtility::Clear(Isolate*, Code::Kind, Code*, + CallICNexus*); + + CallICState::CallICState(ExtraICState extra_ic_state) : argc_(ArgcBits::decode(extra_ic_state)), call_type_(CallTypeBits::decode(extra_ic_state)) {} diff --git a/src/ic/ic-state.h b/src/ic/ic-state.h index a8cfefd104..9bb877a383 100644 --- a/src/ic/ic-state.h +++ b/src/ic/ic-state.h @@ -20,8 +20,9 @@ class ICUtility : public AllStatic { static void Clear(Isolate* isolate, Address address, ConstantPoolArray* constant_pool); // Clear a vector-based inline cache to initial state. + template static void Clear(Isolate* isolate, Code::Kind kind, Code* host, - TypeFeedbackVector* vector, FeedbackVectorICSlot slot); + Nexus* nexus); }; diff --git a/src/ic/ic.cc b/src/ic/ic.cc index e934a89a44..d0c0edb8f7 100644 --- a/src/ic/ic.cc +++ b/src/ic/ic.cc @@ -89,8 +89,8 @@ const char* GetTransitionMarkModifier(KeyedAccessStoreMode mode) { void IC::TraceIC(const char* type, Handle name) { if (FLAG_trace_ic) { - Code* new_target = raw_target(); - State new_state = new_target->ic_state(); + State new_state = + UseVector() ? nexus()->StateFromFeedback() : raw_target()->ic_state(); TraceIC(type, name, state(), new_state); } } @@ -134,12 +134,16 @@ void IC::TraceIC(const char* type, Handle name, State old_state, } } -#define TRACE_IC(type, name) TraceIC(type, name) -#define TRACE_VECTOR_IC(type, name, old_state, new_state) \ - TraceIC(type, name, old_state, new_state) -IC::IC(FrameDepth depth, Isolate* isolate) - : isolate_(isolate), target_set_(false), target_maps_set_(false) { +#define TRACE_IC(type, name) TraceIC(type, name) + + +IC::IC(FrameDepth depth, Isolate* isolate, FeedbackNexus* nexus, + bool for_queries_only) + : isolate_(isolate), + target_set_(false), + target_maps_set_(false), + nexus_(nexus) { // To improve the performance of the (much used) IC code, we unfold a few // levels of the stack frame iteration code. This yields a ~35% speedup when // running DeltaBlue and a ~25% speedup of gbemu with the '--nouse-ic' flag. @@ -178,8 +182,10 @@ IC::IC(FrameDepth depth, Isolate* isolate) } pc_address_ = StackFrame::ResolveReturnAddressLocation(pc_address); target_ = handle(raw_target(), isolate); - state_ = target_->ic_state(); kind_ = target_->kind(); + state_ = (!for_queries_only && UseVector()) ? nexus->StateFromFeedback() + : target_->ic_state(); + old_state_ = state_; extra_ic_state_ = target_->extra_ic_state(); } @@ -419,6 +425,30 @@ void IC::OnTypeFeedbackChanged(Isolate* isolate, Address address, } +// static +void IC::OnTypeFeedbackChanged(Isolate* isolate, Code* host, + TypeFeedbackVector* vector, State old_state, + State new_state) { + if (host->kind() != Code::FUNCTION) return; + + if (FLAG_type_info_threshold > 0) { + int polymorphic_delta = 0; // "Polymorphic" here includes monomorphic. + int generic_delta = 0; // "Generic" here includes megamorphic. + ComputeTypeInfoCountDelta(old_state, new_state, &polymorphic_delta, + &generic_delta); + vector->change_ic_with_type_info_count(polymorphic_delta); + vector->change_ic_generic_count(generic_delta); + } + TypeFeedbackInfo* info = TypeFeedbackInfo::cast(host->type_feedback_info()); + info->change_own_type_change_checksum(); + host->set_profiler_ticks(0); + isolate->runtime_profiler()->NotifyICChanged(); + // TODO(2029): When an optimized function is patched, it would + // be nice to propagate the corresponding type information to its + // unoptimized version for the benefit of later inlining. +} + + void IC::PostPatching(Address address, Code* target, Code* old_target) { // Type vector based ICs update these statistics at a different time because // they don't always patch on state change. @@ -507,17 +537,21 @@ void IC::Clear(Isolate* isolate, Address address, } -void IC::Clear(Isolate* isolate, Code::Kind kind, Code* host, - TypeFeedbackVector* vector, FeedbackVectorICSlot slot) { +template +void IC::Clear(Isolate* isolate, Code::Kind kind, Code* host, Nexus* nexus) { switch (kind) { case Code::CALL_IC: - return CallIC::Clear(isolate, host, vector, slot); + return CallIC::Clear(isolate, host, nexus); default: UNREACHABLE(); } } +// Force instantiation of template instances for vector-based IC clearing. +template void IC::Clear(Isolate*, Code::Kind, Code*, CallICNexus*); + + void KeyedLoadIC::Clear(Isolate* isolate, Address address, Code* target, ConstantPoolArray* constant_pool) { if (IsCleared(target)) return; @@ -529,18 +563,15 @@ void KeyedLoadIC::Clear(Isolate* isolate, Address address, Code* target, } -void CallIC::Clear(Isolate* isolate, Code* host, TypeFeedbackVector* vector, - FeedbackVectorICSlot slot) { - DCHECK(vector != NULL && !slot.IsInvalid()); - Object* feedback = vector->Get(slot); +void CallIC::Clear(Isolate* isolate, Code* host, CallICNexus* nexus) { // Determine our state. - State state = FeedbackToState(isolate, vector, slot); + Object* feedback = nexus->vector()->Get(nexus->slot()); + State state = nexus->StateFromFeedback(); if (state != UNINITIALIZED && !feedback->IsAllocationSite()) { - vector->Set(slot, isolate->heap()->uninitialized_symbol(), - SKIP_WRITE_BARRIER); + nexus->ConfigureUninitialized(); // The change in state must be processed. - OnTypeFeedbackChanged(isolate, host, vector, state, UNINITIALIZED); + OnTypeFeedbackChanged(isolate, host, nexus->vector(), state, UNINITIALIZED); } } @@ -1946,34 +1977,8 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, } -// static -void CallIC::OnTypeFeedbackChanged(Isolate* isolate, Code* host, - TypeFeedbackVector* vector, State old_state, - State new_state) { - if (host->kind() != Code::FUNCTION) return; - - if (FLAG_type_info_threshold > 0) { - int polymorphic_delta = 0; // "Polymorphic" here includes monomorphic. - int generic_delta = 0; // "Generic" here includes megamorphic. - ComputeTypeInfoCountDelta(old_state, new_state, &polymorphic_delta, - &generic_delta); - vector->change_ic_with_type_info_count(polymorphic_delta); - vector->change_ic_generic_count(generic_delta); - } - TypeFeedbackInfo* info = TypeFeedbackInfo::cast(host->type_feedback_info()); - info->change_own_type_change_checksum(); - host->set_profiler_ticks(0); - isolate->runtime_profiler()->NotifyICChanged(); - // TODO(2029): When an optimized function is patched, it would - // be nice to propagate the corresponding type information to its - // unoptimized version for the benefit of later inlining. -} - - bool CallIC::DoCustomHandler(Handle receiver, Handle function, - Handle vector, - FeedbackVectorICSlot slot, - const CallICState& state) { + const CallICState& callic_state) { DCHECK(FLAG_use_ic && function->IsJSFunction()); // Are we the array function? @@ -1981,42 +1986,33 @@ bool CallIC::DoCustomHandler(Handle receiver, Handle function, Handle(isolate()->native_context()->array_function()); if (array_function.is_identical_to(Handle::cast(function))) { // Alter the slot. - IC::State old_state = FeedbackToState(isolate(), *vector, slot); - Object* feedback = vector->Get(slot); - if (!feedback->IsAllocationSite()) { - Handle new_site = - isolate()->factory()->NewAllocationSite(); - vector->Set(slot, *new_site); - } + CallICNexus* nexus = casted_nexus(); + nexus->ConfigureMonomorphicArray(); - CallIC_ArrayStub stub(isolate(), state); + CallIC_ArrayStub stub(isolate(), callic_state); set_target(*stub.GetCode()); Handle name; if (array_function->shared()->name()->IsString()) { name = Handle(String::cast(array_function->shared()->name()), isolate()); } - - IC::State new_state = FeedbackToState(isolate(), *vector, slot); - OnTypeFeedbackChanged(isolate(), get_host(), *vector, old_state, new_state); - TRACE_VECTOR_IC("CallIC (custom handler)", name, old_state, new_state); + TRACE_IC("CallIC", name); + OnTypeFeedbackChanged(isolate(), get_host(), nexus->vector(), state(), + MONOMORPHIC); return true; } return false; } -void CallIC::PatchMegamorphic(Handle function, - Handle vector, - FeedbackVectorICSlot slot) { - CallICState state(target()->extra_ic_state()); - IC::State old_state = FeedbackToState(isolate(), *vector, slot); +void CallIC::PatchMegamorphic(Handle function) { + CallICState callic_state(target()->extra_ic_state()); // We are going generic. - vector->Set(slot, *TypeFeedbackVector::MegamorphicSentinel(isolate()), - SKIP_WRITE_BARRIER); + CallICNexus* nexus = casted_nexus(); + nexus->ConfigureGeneric(); - CallICStub stub(isolate(), state); + CallICStub stub(isolate(), callic_state); Handle code = stub.GetCode(); set_target(*code); @@ -2026,27 +2022,24 @@ void CallIC::PatchMegamorphic(Handle function, name = handle(js_function->shared()->name(), isolate()); } - IC::State new_state = FeedbackToState(isolate(), *vector, slot); - OnTypeFeedbackChanged(isolate(), get_host(), *vector, old_state, new_state); - TRACE_VECTOR_IC("CallIC", name, old_state, new_state); + TRACE_IC("CallIC", name); + OnTypeFeedbackChanged(isolate(), get_host(), nexus->vector(), state(), + GENERIC); } -void CallIC::HandleMiss(Handle receiver, Handle function, - Handle vector, - FeedbackVectorICSlot slot) { - CallICState state(target()->extra_ic_state()); - IC::State old_state = FeedbackToState(isolate(), *vector, slot); +void CallIC::HandleMiss(Handle receiver, Handle function) { + CallICState callic_state(target()->extra_ic_state()); Handle name = isolate()->factory()->empty_string(); - Object* feedback = vector->Get(slot); + CallICNexus* nexus = casted_nexus(); + Object* feedback = nexus->GetFeedback(); // Hand-coded MISS handling is easier if CallIC slots don't contain smis. DCHECK(!feedback->IsSmi()); if (feedback->IsJSFunction() || !function->IsJSFunction()) { // We are going generic. - vector->Set(slot, *TypeFeedbackVector::MegamorphicSentinel(isolate()), - SKIP_WRITE_BARRIER); + nexus->ConfigureGeneric(); } else { // The feedback is either uninitialized or an allocation site. // It might be an allocation site because if we re-compile the full code @@ -2058,12 +2051,11 @@ void CallIC::HandleMiss(Handle receiver, Handle function, feedback->IsAllocationSite()); // Do we want to install a custom handler? - if (FLAG_use_ic && - DoCustomHandler(receiver, function, vector, slot, state)) { + if (FLAG_use_ic && DoCustomHandler(receiver, function, callic_state)) { return; } - vector->Set(slot, *function); + nexus->ConfigureMonomorphic(Handle::cast(function)); } if (function->IsJSFunction()) { @@ -2071,9 +2063,9 @@ void CallIC::HandleMiss(Handle receiver, Handle function, name = handle(js_function->shared()->name(), isolate()); } - IC::State new_state = FeedbackToState(isolate(), *vector, slot); - OnTypeFeedbackChanged(isolate(), get_host(), *vector, old_state, new_state); - TRACE_VECTOR_IC("CallIC", name, old_state, new_state); + IC::State new_state = nexus->StateFromFeedback(); + OnTypeFeedbackChanged(isolate(), get_host(), *vector(), state(), new_state); + TRACE_IC("CallIC", name); } @@ -2089,13 +2081,14 @@ RUNTIME_FUNCTION(CallIC_Miss) { TimerEventScope timer(isolate); HandleScope scope(isolate); DCHECK(args.length() == 4); - CallIC ic(isolate); Handle receiver = args.at(0); Handle function = args.at(1); Handle vector = args.at(2); Handle slot = args.at(3); FeedbackVectorICSlot vector_slot = vector->ToICSlot(slot->value()); - ic.HandleMiss(receiver, function, vector, vector_slot); + CallICNexus nexus(vector, vector_slot); + CallIC ic(isolate, &nexus); + ic.HandleMiss(receiver, function); return *function; } @@ -2104,13 +2097,14 @@ RUNTIME_FUNCTION(CallIC_Customization_Miss) { TimerEventScope timer(isolate); HandleScope scope(isolate); DCHECK(args.length() == 4); - // A miss on a custom call ic always results in going megamorphic. - CallIC ic(isolate); Handle function = args.at(1); Handle vector = args.at(2); Handle slot = args.at(3); FeedbackVectorICSlot vector_slot = vector->ToICSlot(slot->value()); - ic.PatchMegamorphic(function, vector, vector_slot); + CallICNexus nexus(vector, vector_slot); + // A miss on a custom call ic always results in going megamorphic. + CallIC ic(isolate, &nexus); + ic.PatchMegamorphic(function); return *function; } diff --git a/src/ic/ic.h b/src/ic/ic.h index 0da2cd0069..c50b43e0b5 100644 --- a/src/ic/ic.h +++ b/src/ic/ic.h @@ -59,7 +59,8 @@ class IC { // Construct the IC structure with the given number of extra // JavaScript frames on the stack. - IC(FrameDepth depth, Isolate* isolate); + IC(FrameDepth depth, Isolate* isolate, FeedbackNexus* nexus = NULL, + bool for_queries_only = false); virtual ~IC() {} State state() const { return state_; } @@ -71,6 +72,7 @@ class IC { bool IsNameCompatibleWithPrototypeFailure(Handle name); void MarkPrototypeFailure(Handle name) { DCHECK(IsNameCompatibleWithPrototypeFailure(name)); + old_state_ = state_; state_ = PROTOTYPE_FAILURE; } @@ -87,8 +89,9 @@ class IC { ConstantPoolArray* constant_pool); // Clear the vector-based inline cache to initial state. + template static void Clear(Isolate* isolate, Code::Kind kind, Code* host, - TypeFeedbackVector* vector, FeedbackVectorICSlot slot); + Nexus* nexus); #ifdef DEBUG bool IsLoadStub() const { @@ -117,6 +120,11 @@ class IC { return state == UNINITIALIZED || state == PREMONOMORPHIC; } + static bool IsCleared(FeedbackNexus* nexus) { + InlineCacheState state = nexus->StateFromFeedback(); + return state == UNINITIALIZED || state == PREMONOMORPHIC; + } + // Utility functions to convert maps to types and back. There are two special // cases: // - The heap_number_map is used as a marker which includes heap numbers as @@ -149,6 +157,15 @@ class IC { inline void set_target(Code* code); bool is_target_set() { return target_set_; } + bool UseVector() const { + bool use = (FLAG_vector_ics && + (kind() == Code::LOAD_IC || kind() == Code::KEYED_LOAD_IC)) || + kind() == Code::CALL_IC; + // If we are supposed to use the nexus, verify the nexus is non-null. + DCHECK(!use || nexus_ != NULL); + return use; + } + char TransitionMarkFromState(IC::State state); void TraceIC(const char* type, Handle name); void TraceIC(const char* type, Handle name, State old_state, @@ -166,6 +183,10 @@ class IC { static void OnTypeFeedbackChanged(Isolate* isolate, Address address, State old_state, State new_state, bool target_remains_ic_stub); + // As a vector-based IC, type feedback must be updated differently. + static void OnTypeFeedbackChanged(Isolate* isolate, Code* host, + TypeFeedbackVector* vector, State old_state, + State new_state); static void PostPatching(Address address, Code* target, Code* old_target); // Compute the handler either by compiling or by retrieving a cached version. @@ -229,6 +250,20 @@ class IC { inline void UpdateTarget(); + Handle vector() const { return nexus()->vector_handle(); } + FeedbackVectorICSlot slot() const { return nexus()->slot(); } + State saved_state() const { + return state() == PROTOTYPE_FAILURE ? old_state_ : state(); + } + + template + NexusClass* casted_nexus() { + return static_cast(nexus_); + } + FeedbackNexus* nexus() const { return nexus_; } + + inline Code* get_host(); + private: inline Code* raw_target() const; inline ConstantPoolArray* constant_pool() const; @@ -263,6 +298,7 @@ class IC { // The original code target that missed. Handle target_; bool target_set_; + State old_state_; // For saving if we marked as prototype failure. State state_; Code::Kind kind_; Handle receiver_type_; @@ -272,6 +308,8 @@ class IC { MapHandleList target_maps_; bool target_maps_set_; + FeedbackNexus* nexus_; + DISALLOW_IMPLICIT_CONSTRUCTORS(IC); }; @@ -295,38 +333,24 @@ class IC_Utility { class CallIC : public IC { public: - explicit CallIC(Isolate* isolate) : IC(EXTRA_CALL_FRAME, isolate) {} + CallIC(Isolate* isolate, CallICNexus* nexus) + : IC(EXTRA_CALL_FRAME, isolate, nexus) { + DCHECK(nexus != NULL); + } - void PatchMegamorphic(Handle function, - Handle vector, - FeedbackVectorICSlot slot); + void PatchMegamorphic(Handle function); - void HandleMiss(Handle receiver, Handle function, - Handle vector, FeedbackVectorICSlot slot); + void HandleMiss(Handle receiver, Handle function); // Returns true if a custom handler was installed. bool DoCustomHandler(Handle receiver, Handle function, - Handle vector, - FeedbackVectorICSlot slot, const CallICState& state); + const CallICState& callic_state); // Code generator routines. static Handle initialize_stub(Isolate* isolate, int argc, CallICState::CallType call_type); - static void Clear(Isolate* isolate, Code* host, TypeFeedbackVector* vector, - FeedbackVectorICSlot slot); - - private: - static inline IC::State FeedbackToState(Isolate* isolate, - TypeFeedbackVector* vector, - FeedbackVectorICSlot slot); - - inline Code* get_host(); - - // As a vector-based IC, type feedback must be updated differently. - static void OnTypeFeedbackChanged(Isolate* isolate, Code* host, - TypeFeedbackVector* vector, State old_state, - State new_state); + static void Clear(Isolate* isolate, Code* host, CallICNexus* nexus); }; diff --git a/src/type-feedback-vector.cc b/src/type-feedback-vector.cc index c0078c5001..676290c83d 100644 --- a/src/type-feedback-vector.cc +++ b/src/type-feedback-vector.cc @@ -4,6 +4,7 @@ #include "src/v8.h" +#include "src/ic/ic.h" #include "src/ic/ic-state.h" #include "src/objects.h" #include "src/type-feedback-vector-inl.h" @@ -151,9 +152,142 @@ void TypeFeedbackVector::ClearSlots(SharedFunctionInfo* shared) { FeedbackVectorICSlot slot(i); Object* obj = Get(slot); if (obj != uninitialized_sentinel) { - ICUtility::Clear(isolate, Code::CALL_IC, host, this, slot); + // TODO(mvstanton): To make this code work with --vector-ics, + // additional Nexus types must be created. + DCHECK(!FLAG_vector_ics); + DCHECK(GetKind(slot) == Code::CALL_IC); + CallICNexus nexus(this, slot); + ICUtility::Clear(isolate, Code::CALL_IC, host, &nexus); } } } + + +Handle FeedbackNexus::EnsureArrayOfSize(int length) { + Isolate* isolate = GetIsolate(); + Handle feedback = handle(GetFeedback(), isolate); + if (!feedback->IsFixedArray() || + FixedArray::cast(*feedback)->length() != length) { + Handle array = isolate->factory()->NewFixedArray(length); + SetFeedback(*array); + return array; + } + return Handle::cast(feedback); +} + + +void FeedbackNexus::InstallHandlers(int start_index, TypeHandleList* types, + CodeHandleList* handlers) { + Isolate* isolate = GetIsolate(); + FixedArray* array = FixedArray::cast(GetFeedback()); + int receiver_count = types->length(); + for (int current = 0; current < receiver_count; ++current) { + Handle type = types->at(current); + Handle map = IC::TypeToMap(*type, isolate); + array->set(start_index + (current * 2), *map); + array->set(start_index + (current * 2 + 1), *handlers->at(current)); + } +} + + +InlineCacheState CallICNexus::StateFromFeedback() const { + Isolate* isolate = GetIsolate(); + InlineCacheState state = UNINITIALIZED; + Object* feedback = GetFeedback(); + + if (feedback == *vector()->MegamorphicSentinel(isolate)) { + state = GENERIC; + } else if (feedback->IsAllocationSite() || feedback->IsJSFunction()) { + state = MONOMORPHIC; + } else { + CHECK(feedback == *vector()->UninitializedSentinel(isolate)); + } + + return state; +} + + +void CallICNexus::ConfigureGeneric() { + SetFeedback(*vector()->MegamorphicSentinel(GetIsolate()), SKIP_WRITE_BARRIER); +} + + +void CallICNexus::ConfigureMonomorphicArray() { + Object* feedback = GetFeedback(); + if (!feedback->IsAllocationSite()) { + Handle new_site = + GetIsolate()->factory()->NewAllocationSite(); + SetFeedback(*new_site); + } +} + + +void CallICNexus::ConfigureUninitialized() { + SetFeedback(*vector()->UninitializedSentinel(GetIsolate()), + SKIP_WRITE_BARRIER); +} + + +void CallICNexus::ConfigureMonomorphic(Handle function) { + SetFeedback(*function); +} + + +int FeedbackNexus::ExtractMaps(int start_index, MapHandleList* maps) const { + Isolate* isolate = GetIsolate(); + Object* feedback = GetFeedback(); + if (feedback->IsFixedArray()) { + FixedArray* array = FixedArray::cast(feedback); + // The array should be of the form [], then + // [map, handler, map, handler, ... ] + DCHECK(array->length() >= (2 + start_index)); + for (int i = start_index; i < array->length(); i += 2) { + Map* map = Map::cast(array->get(i)); + maps->Add(handle(map, isolate)); + } + return (array->length() - start_index) / 2; + } + + return 0; +} + + +MaybeHandle FeedbackNexus::FindHandlerForMap(int start_index, + Handle map) const { + Object* feedback = GetFeedback(); + if (feedback->IsFixedArray()) { + FixedArray* array = FixedArray::cast(feedback); + for (int i = start_index; i < array->length(); i += 2) { + Map* array_map = Map::cast(array->get(i)); + if (array_map == *map) { + Code* code = Code::cast(array->get(i + 1)); + DCHECK(code->kind() == Code::HANDLER); + return handle(code); + } + } + } + + return MaybeHandle(); +} + + +bool FeedbackNexus::FindHandlers(int start_index, CodeHandleList* code_list, + int length) const { + Object* feedback = GetFeedback(); + int count = 0; + if (feedback->IsFixedArray()) { + FixedArray* array = FixedArray::cast(feedback); + // The array should be of the form [], then + // [map, handler, map, handler, ... ] + DCHECK(array->length() >= (2 + start_index)); + for (int i = start_index; i < array->length(); i += 2) { + Code* code = Code::cast(array->get(i + 1)); + DCHECK(code->kind() == Code::HANDLER); + code_list->Add(handle(code)); + count++; + } + } + return count == length; +} } } // namespace v8::internal diff --git a/src/type-feedback-vector.h b/src/type-feedback-vector.h index 61463b0394..d993b245d8 100644 --- a/src/type-feedback-vector.h +++ b/src/type-feedback-vector.h @@ -173,6 +173,105 @@ class TypeFeedbackVector : public FixedArray { DISALLOW_IMPLICIT_CONSTRUCTORS(TypeFeedbackVector); }; + + +// A FeedbackNexus is the combination of a TypeFeedbackVector and a slot. +// Derived classes customize the update and retrieval of feedback. +class FeedbackNexus { + public: + FeedbackNexus(Handle vector, FeedbackVectorICSlot slot) + : vector_handle_(vector), use_handle_(true), slot_(slot) {} + FeedbackNexus(TypeFeedbackVector* vector, FeedbackVectorICSlot slot) + : vector_(vector), use_handle_(false), slot_(slot) {} + virtual ~FeedbackNexus() {} + + Handle vector_handle() const { + DCHECK(use_handle_); + return vector_handle_; + } + TypeFeedbackVector* vector() const { + return use_handle_ ? *vector_handle_ : vector_; + } + FeedbackVectorICSlot slot() const { return slot_; } + + InlineCacheState ic_state() const { return StateFromFeedback(); } + Map* FindFirstMap() const { + MapHandleList maps; + ExtractMaps(&maps); + if (maps.length() > 0) return *maps.at(0); + return NULL; + } + + virtual InlineCacheState StateFromFeedback() const = 0; + virtual int ExtractMaps(MapHandleList* maps) const = 0; + virtual MaybeHandle FindHandlerForMap(Handle map) const = 0; + virtual bool FindHandlers(CodeHandleList* code_list, int length = -1) const { + return length == 0; + } + virtual Name* FindFirstName() const { return NULL; } + + Object* GetFeedback() const { return vector()->Get(slot()); } + + protected: + Isolate* GetIsolate() const { return vector()->GetIsolate(); } + + void SetFeedback(Object* feedback, + WriteBarrierMode mode = UPDATE_WRITE_BARRIER) { + vector()->Set(slot(), feedback, mode); + } + + Handle EnsureArrayOfSize(int length); + void InstallHandlers(int start_index, TypeHandleList* types, + CodeHandleList* handlers); + int ExtractMaps(int start_index, MapHandleList* maps) const; + MaybeHandle FindHandlerForMap(int start_index, Handle map) const; + bool FindHandlers(int start_index, CodeHandleList* code_list, + int length) const; + + private: + // The reason for the union is that we can use handles during IC miss, + // but not during GC when we clear ICs. If you have a handle to the + // vector that is better because more operations can be done, like + // allocation. + union { + Handle vector_handle_; + TypeFeedbackVector* vector_; + }; + bool use_handle_; + FeedbackVectorICSlot slot_; +}; + + +class CallICNexus : public FeedbackNexus { + public: + CallICNexus(Handle vector, FeedbackVectorICSlot slot) + : FeedbackNexus(vector, slot) { + DCHECK(vector->GetKind(slot) == Code::CALL_IC); + } + CallICNexus(TypeFeedbackVector* vector, FeedbackVectorICSlot slot) + : FeedbackNexus(vector, slot) { + DCHECK(vector->GetKind(slot) == Code::CALL_IC); + } + + void ConfigureUninitialized(); + void ConfigureGeneric(); + void ConfigureMonomorphicArray(); + void ConfigureMonomorphic(Handle function); + + virtual InlineCacheState StateFromFeedback() const OVERRIDE; + + virtual int ExtractMaps(MapHandleList* maps) const OVERRIDE { + // CallICs don't record map feedback. + return 0; + } + virtual MaybeHandle FindHandlerForMap(Handle map) const OVERRIDE { + return MaybeHandle(); + } + virtual bool FindHandlers(CodeHandleList* code_list, + int length = -1) const OVERRIDE { + return length == 0; + } +}; } } // namespace v8::internal diff --git a/test/cctest/test-feedback-vector.cc b/test/cctest/test-feedback-vector.cc index 79c6ea2668..28a15f2029 100644 --- a/test/cctest/test-feedback-vector.cc +++ b/test/cctest/test-feedback-vector.cc @@ -86,12 +86,13 @@ TEST(VectorICMetadata) { // Set metadata. for (int i = 0; i < 30; i++) { Code::Kind kind; - if (i % 3 == 0) + if (i % 3 == 0) { kind = Code::CALL_IC; - else if (i % 3 == 1) + } else if (i % 3 == 1) { kind = Code::LOAD_IC; - else + } else { kind = Code::KEYED_LOAD_IC; + } vector->SetKind(FeedbackVectorICSlot(i), kind); } @@ -197,4 +198,45 @@ TEST(VectorICProfilerStatistics) { CHECK( feedback_vector->Get(FeedbackVectorICSlot(ic_slot))->IsAllocationSite()); } + + +TEST(VectorCallICStates) { + if (i::FLAG_always_opt) return; + CcTest::InitializeVM(); + LocalContext context; + v8::HandleScope scope(context->GetIsolate()); + Isolate* isolate = CcTest::i_isolate(); + Heap* heap = isolate->heap(); + + // Make sure function f has a call that uses a type feedback slot. + CompileRun( + "function foo() { return 17; }" + "function f(a) { a(); } f(foo);"); + Handle f = v8::Utils::OpenHandle( + *v8::Handle::Cast(CcTest::global()->Get(v8_str("f")))); + // There should be one IC. + Handle feedback_vector = + Handle(f->shared()->feedback_vector(), isolate); + FeedbackVectorICSlot slot(FLAG_vector_ics ? 1 : 0); + CallICNexus nexus(feedback_vector, slot); + CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback()); + // CallIC doesn't return map feedback. + CHECK_EQ(NULL, nexus.FindFirstMap()); + + CompileRun("f(function() { return 16; })"); + CHECK_EQ(GENERIC, nexus.StateFromFeedback()); + + // After a collection, state should be reset to UNINITIALIZED. + heap->CollectAllGarbage(i::Heap::kNoGCFlags); + CHECK_EQ(UNINITIALIZED, nexus.StateFromFeedback()); + + // Array is special. It will remain monomorphic across gcs and it contains an + // AllocationSite. + CompileRun("f(Array)"); + CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback()); + CHECK(feedback_vector->Get(FeedbackVectorICSlot(slot))->IsAllocationSite()); + + heap->CollectAllGarbage(i::Heap::kNoGCFlags); + CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback()); +} }