Introduce FeedbackNexus for vector-based ics.
authormvstanton@chromium.org <mvstanton@chromium.org>
Tue, 28 Oct 2014 16:05:08 +0000 (16:05 +0000)
committermvstanton@chromium.org <mvstanton@chromium.org>
Tue, 28 Oct 2014 16:05:45 +0000 (16:05 +0000)
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.

R=ishell@chromium.org

Review URL: https://codereview.chromium.org/683933002

Cr-Commit-Position: refs/heads/master@{#24952}
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24952 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/ic/ic-inl.h
src/ic/ic-state.cc
src/ic/ic-state.h
src/ic/ic.cc
src/ic/ic.h
src/type-feedback-vector.cc
src/type-feedback-vector.h
test/cctest/test-feedback-vector.cc

index 91b8955fd47ca08b7eb2cd6d63aa788cf67efb23..22f66d0409b03d45ce443c95caf897d4cb5d8628 100644 (file)
@@ -208,30 +208,12 @@ Handle<Map> 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
 
index 5a17bc8688c77514d1ed2865675524d31f75ba06..18ea7f310585dd5e81bed79b8ff70402e633e699 100644 (file)
@@ -18,12 +18,18 @@ void ICUtility::Clear(Isolate* isolate, Address address,
 
 
 // static
+template <class Nexus>
 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<Nexus>(isolate, kind, host, nexus);
 }
 
 
+// Force instantiation of template instances for vector-based IC clearing.
+template void ICUtility::Clear<CallICNexus>(Isolate*, Code::Kind, Code*,
+                                            CallICNexus*);
+
+
 CallICState::CallICState(ExtraICState extra_ic_state)
     : argc_(ArgcBits::decode(extra_ic_state)),
       call_type_(CallTypeBits::decode(extra_ic_state)) {}
index a8cfefd10453466405cd8d72940d337f18bc7aa2..9bb877a3835761448072ae65ec68be73e5109d95 100644 (file)
@@ -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 <class Nexus>
   static void Clear(Isolate* isolate, Code::Kind kind, Code* host,
-                    TypeFeedbackVector* vector, FeedbackVectorICSlot slot);
+                    Nexus* nexus);
 };
 
 
index e934a89a446902d1f053f912fa61dcb12fd5813e..d0c0edb8f7d700c1c4bc7a582024878cd13962aa 100644 (file)
@@ -89,8 +89,8 @@ const char* GetTransitionMarkModifier(KeyedAccessStoreMode mode) {
 
 void IC::TraceIC(const char* type, Handle<Object> 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<Object> 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) {
+
+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 <class Nexus>
+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<Object> receiver, Handle<Object> function,
-                             Handle<TypeFeedbackVector> 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<Object> receiver, Handle<Object> function,
       Handle<JSFunction>(isolate()->native_context()->array_function());
   if (array_function.is_identical_to(Handle<JSFunction>::cast(function))) {
     // Alter the slot.
-    IC::State old_state = FeedbackToState(isolate(), *vector, slot);
-    Object* feedback = vector->Get(slot);
-    if (!feedback->IsAllocationSite()) {
-      Handle<AllocationSite> new_site =
-          isolate()->factory()->NewAllocationSite();
-      vector->Set(slot, *new_site);
-    }
+    CallICNexus* nexus = casted_nexus<CallICNexus>();
+    nexus->ConfigureMonomorphicArray();
 
-    CallIC_ArrayStub stub(isolate(), state);
+    CallIC_ArrayStub stub(isolate(), callic_state);
     set_target(*stub.GetCode());
     Handle<String> name;
     if (array_function->shared()->name()->IsString()) {
       name = Handle<String>(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<Object> function,
-                              Handle<TypeFeedbackVector> vector,
-                              FeedbackVectorICSlot slot) {
-  CallICState state(target()->extra_ic_state());
-  IC::State old_state = FeedbackToState(isolate(), *vector, slot);
+void CallIC::PatchMegamorphic(Handle<Object> 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<CallICNexus>();
+  nexus->ConfigureGeneric();
 
-  CallICStub stub(isolate(), state);
+  CallICStub stub(isolate(), callic_state);
   Handle<Code> code = stub.GetCode();
   set_target(*code);
 
@@ -2026,27 +2022,24 @@ void CallIC::PatchMegamorphic(Handle<Object> 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<Object> receiver, Handle<Object> function,
-                        Handle<TypeFeedbackVector> vector,
-                        FeedbackVectorICSlot slot) {
-  CallICState state(target()->extra_ic_state());
-  IC::State old_state = FeedbackToState(isolate(), *vector, slot);
+void CallIC::HandleMiss(Handle<Object> receiver, Handle<Object> function) {
+  CallICState callic_state(target()->extra_ic_state());
   Handle<Object> name = isolate()->factory()->empty_string();
-  Object* feedback = vector->Get(slot);
+  CallICNexus* nexus = casted_nexus<CallICNexus>();
+  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<Object> receiver, Handle<Object> 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<JSFunction>::cast(function));
   }
 
   if (function->IsJSFunction()) {
@@ -2071,9 +2063,9 @@ void CallIC::HandleMiss(Handle<Object> receiver, Handle<Object> 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<TimerEventIcMiss> timer(isolate);
   HandleScope scope(isolate);
   DCHECK(args.length() == 4);
-  CallIC ic(isolate);
   Handle<Object> receiver = args.at<Object>(0);
   Handle<Object> function = args.at<Object>(1);
   Handle<TypeFeedbackVector> vector = args.at<TypeFeedbackVector>(2);
   Handle<Smi> slot = args.at<Smi>(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<TimerEventIcMiss> 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<Object> function = args.at<Object>(1);
   Handle<TypeFeedbackVector> vector = args.at<TypeFeedbackVector>(2);
   Handle<Smi> slot = args.at<Smi>(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;
 }
 
index 0da2cd006937711ed5eac58881f4cc07987d2cf5..c50b43e0b5e6290c4aaad67ea96f0e18e226c374 100644 (file)
@@ -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<Object> name);
   void MarkPrototypeFailure(Handle<Object> 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 <class Nexus>
   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<Object> name);
   void TraceIC(const char* type, Handle<Object> 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<TypeFeedbackVector> vector() const { return nexus()->vector_handle(); }
+  FeedbackVectorICSlot slot() const { return nexus()->slot(); }
+  State saved_state() const {
+    return state() == PROTOTYPE_FAILURE ? old_state_ : state();
+  }
+
+  template <class NexusClass>
+  NexusClass* casted_nexus() {
+    return static_cast<NexusClass*>(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<Code> target_;
   bool target_set_;
+  State old_state_;  // For saving if we marked as prototype failure.
   State state_;
   Code::Kind kind_;
   Handle<HeapType> 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<Object> function,
-                        Handle<TypeFeedbackVector> vector,
-                        FeedbackVectorICSlot slot);
+  void PatchMegamorphic(Handle<Object> function);
 
-  void HandleMiss(Handle<Object> receiver, Handle<Object> function,
-                  Handle<TypeFeedbackVector> vector, FeedbackVectorICSlot slot);
+  void HandleMiss(Handle<Object> receiver, Handle<Object> function);
 
   // Returns true if a custom handler was installed.
   bool DoCustomHandler(Handle<Object> receiver, Handle<Object> function,
-                       Handle<TypeFeedbackVector> vector,
-                       FeedbackVectorICSlot slot, const CallICState& state);
+                       const CallICState& callic_state);
 
   // Code generator routines.
   static Handle<Code> 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);
 };
 
 
index c0078c5001653cdf1eb41592b498b6f884af06c8..676290c83d021fff88102a40c4d65c478fb56c31 100644 (file)
@@ -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<FixedArray> FeedbackNexus::EnsureArrayOfSize(int length) {
+  Isolate* isolate = GetIsolate();
+  Handle<Object> feedback = handle(GetFeedback(), isolate);
+  if (!feedback->IsFixedArray() ||
+      FixedArray::cast(*feedback)->length() != length) {
+    Handle<FixedArray> array = isolate->factory()->NewFixedArray(length);
+    SetFeedback(*array);
+    return array;
+  }
+  return Handle<FixedArray>::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<HeapType> type = types->at(current);
+    Handle<Map> 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<AllocationSite> new_site =
+        GetIsolate()->factory()->NewAllocationSite();
+    SetFeedback(*new_site);
+  }
+}
+
+
+void CallICNexus::ConfigureUninitialized() {
+  SetFeedback(*vector()->UninitializedSentinel(GetIsolate()),
+              SKIP_WRITE_BARRIER);
+}
+
+
+void CallICNexus::ConfigureMonomorphic(Handle<JSFunction> 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 [<optional name>], 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<Code> FeedbackNexus::FindHandlerForMap(int start_index,
+                                                   Handle<Map> 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<Code>();
+}
+
+
+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 [<optional name>], 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
index 61463b0394fc8a5ed631ab8d76430755f9def6cc..de5a7e151266ed2ad32cea574b6b315c022f0211 100644 (file)
@@ -173,6 +173,102 @@ 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<TypeFeedbackVector> vector, FeedbackVectorICSlot slot)
+      : vector_handle_(vector), vector_(NULL), slot_(slot) {}
+  FeedbackNexus(TypeFeedbackVector* vector, FeedbackVectorICSlot slot)
+      : vector_(vector), slot_(slot) {}
+  virtual ~FeedbackNexus() {}
+
+  Handle<TypeFeedbackVector> vector_handle() const {
+    DCHECK(vector_ == NULL);
+    return vector_handle_;
+  }
+  TypeFeedbackVector* vector() const {
+    return vector_handle_.is_null() ? vector_ : *vector_handle_;
+  }
+  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<Code> FindHandlerForMap(Handle<Map> 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<FixedArray> EnsureArrayOfSize(int length);
+  void InstallHandlers(int start_index, TypeHandleList* types,
+                       CodeHandleList* handlers);
+  int ExtractMaps(int start_index, MapHandleList* maps) const;
+  MaybeHandle<Code> FindHandlerForMap(int start_index, Handle<Map> map) const;
+  bool FindHandlers(int start_index, CodeHandleList* code_list,
+                    int length) const;
+
+ private:
+  // The reason for having a vector handle and a raw pointer is that we can and
+  // should 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.
+  Handle<TypeFeedbackVector> vector_handle_;
+  TypeFeedbackVector* vector_;
+  FeedbackVectorICSlot slot_;
+};
+
+
+class CallICNexus : public FeedbackNexus {
+ public:
+  CallICNexus(Handle<TypeFeedbackVector> 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<JSFunction> function);
+
+  virtual InlineCacheState StateFromFeedback() const OVERRIDE;
+
+  virtual int ExtractMaps(MapHandleList* maps) const OVERRIDE {
+    // CallICs don't record map feedback.
+    return 0;
+  }
+  virtual MaybeHandle<Code> FindHandlerForMap(Handle<Map> map) const OVERRIDE {
+    return MaybeHandle<Code>();
+  }
+  virtual bool FindHandlers(CodeHandleList* code_list,
+                            int length = -1) const OVERRIDE {
+    return length == 0;
+  }
+};
 }
 }  // namespace v8::internal
 
index 79c6ea26686c20239dab1b37207564d7ad775953..28a15f2029ef2f100cb5cc5b61c035e8c372b4e5 100644 (file)
@@ -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<JSFunction> f = v8::Utils::OpenHandle(
+      *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
+  // There should be one IC.
+  Handle<TypeFeedbackVector> feedback_vector =
+      Handle<TypeFeedbackVector>(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());
+}
 }