// Copyright 2014 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_TYPE_FEEDBACK_VECTOR_H_ #define V8_TYPE_FEEDBACK_VECTOR_H_ #include #include "src/base/logging.h" #include "src/elements-kind.h" #include "src/objects.h" #include "src/zone-containers.h" namespace v8 { namespace internal { enum class FeedbackVectorSlotKind { // This kind means that the slot points to the middle of other slot // which occupies more than one feedback vector element. // There must be no such slots in the system. INVALID, CALL_IC, LOAD_IC, LOAD_GLOBAL_IC, KEYED_LOAD_IC, STORE_IC, KEYED_STORE_IC, // This is a general purpose slot that occupies one feedback vector element. GENERAL, KINDS_NUMBER // Last value indicating number of kinds. }; std::ostream& operator<<(std::ostream& os, FeedbackVectorSlotKind kind); template class FeedbackVectorSpecBase { public: inline FeedbackVectorSlot AddSlot(FeedbackVectorSlotKind kind); FeedbackVectorSlot AddCallICSlot() { return AddSlot(FeedbackVectorSlotKind::CALL_IC); } FeedbackVectorSlot AddLoadICSlot() { return AddSlot(FeedbackVectorSlotKind::LOAD_IC); } FeedbackVectorSlot AddLoadGlobalICSlot(Handle name) { This()->append_name(name); return AddSlot(FeedbackVectorSlotKind::LOAD_GLOBAL_IC); } FeedbackVectorSlot AddKeyedLoadICSlot() { return AddSlot(FeedbackVectorSlotKind::KEYED_LOAD_IC); } FeedbackVectorSlot AddStoreICSlot() { return AddSlot(FeedbackVectorSlotKind::STORE_IC); } FeedbackVectorSlot AddKeyedStoreICSlot() { return AddSlot(FeedbackVectorSlotKind::KEYED_STORE_IC); } FeedbackVectorSlot AddGeneralSlot() { return AddSlot(FeedbackVectorSlotKind::GENERAL); } #ifdef OBJECT_PRINT // For gdb debugging. void Print(); #endif // OBJECT_PRINT DECLARE_PRINTER(FeedbackVectorSpec) private: Derived* This() { return static_cast(this); } }; class StaticFeedbackVectorSpec : public FeedbackVectorSpecBase { public: StaticFeedbackVectorSpec() : slot_count_(0), name_count_(0) {} int slots() const { return slot_count_; } FeedbackVectorSlotKind GetKind(int slot) const { DCHECK(slot >= 0 && slot < slot_count_); return kinds_[slot]; } int name_count() const { return name_count_; } Handle GetName(int index) const { DCHECK(index >= 0 && index < name_count_); return names_[index]; } private: friend class FeedbackVectorSpecBase; void append(FeedbackVectorSlotKind kind) { DCHECK(slot_count_ < kMaxLength); kinds_[slot_count_++] = kind; } void append_name(Handle name) { DCHECK(name_count_ < kMaxLength); names_[name_count_++] = name; } static const int kMaxLength = 12; int slot_count_; FeedbackVectorSlotKind kinds_[kMaxLength]; int name_count_; Handle names_[kMaxLength]; }; class FeedbackVectorSpec : public FeedbackVectorSpecBase { public: explicit FeedbackVectorSpec(Zone* zone) : slot_kinds_(zone), names_(zone) { slot_kinds_.reserve(16); names_.reserve(8); } int slots() const { return static_cast(slot_kinds_.size()); } FeedbackVectorSlotKind GetKind(int slot) const { return static_cast(slot_kinds_.at(slot)); } int name_count() const { return static_cast(names_.size()); } Handle GetName(int index) const { return names_.at(index); } private: friend class FeedbackVectorSpecBase; void append(FeedbackVectorSlotKind kind) { slot_kinds_.push_back(static_cast(kind)); } void append_name(Handle name) { names_.push_back(name); } ZoneVector slot_kinds_; ZoneVector> names_; }; // The shape of the TypeFeedbackMetadata is an array with: // 0: slot_count // 1: names table // 2..N: slot kinds packed into a bit vector // class TypeFeedbackMetadata : public FixedArray { public: // Casting. static inline TypeFeedbackMetadata* cast(Object* obj); static const int kSlotsCountIndex = 0; static const int kNamesTableIndex = 1; static const int kReservedIndexCount = 2; static const int kNameTableEntrySize = 2; static const int kNameTableSlotIndex = 0; static const int kNameTableNameIndex = 1; // Returns number of feedback vector elements used by given slot kind. static inline int GetSlotSize(FeedbackVectorSlotKind kind); // Defines if slots of given kind require "name". static inline bool SlotRequiresName(FeedbackVectorSlotKind kind); bool SpecDiffersFrom(const FeedbackVectorSpec* other_spec) const; bool DiffersFrom(const TypeFeedbackMetadata* other_metadata) const; inline bool is_empty() const; // Returns number of slots in the vector. inline int slot_count() const; // Returns slot kind for given slot. FeedbackVectorSlotKind GetKind(FeedbackVectorSlot slot) const; // Returns name for given slot. String* GetName(FeedbackVectorSlot slot) const; template static Handle New(Isolate* isolate, const Spec* spec); #ifdef OBJECT_PRINT // For gdb debugging. void Print(); #endif // OBJECT_PRINT DECLARE_PRINTER(TypeFeedbackMetadata) static const char* Kind2String(FeedbackVectorSlotKind kind); private: static const int kFeedbackVectorSlotKindBits = 4; STATIC_ASSERT(static_cast(FeedbackVectorSlotKind::KINDS_NUMBER) < (1 << kFeedbackVectorSlotKindBits)); void SetKind(FeedbackVectorSlot slot, FeedbackVectorSlotKind kind); typedef BitSetComputer VectorICComputer; DISALLOW_IMPLICIT_CONSTRUCTORS(TypeFeedbackMetadata); }; // The shape of the TypeFeedbackVector is an array with: // 0: feedback metadata // 1: ics_with_types // 2: ics_with_generic_info // 3: feedback slot #0 // ... // 3 + slot_count - 1: feedback slot #(slot_count-1) // class TypeFeedbackVector : public FixedArray { public: // Casting. static inline TypeFeedbackVector* cast(Object* obj); static const int kMetadataIndex = 0; static const int kReservedIndexCount = 1; inline void ComputeCounts(int* with_type_info, int* generic); inline bool is_empty() const; // Returns number of slots in the vector. inline int slot_count() const; inline TypeFeedbackMetadata* metadata() const; // Conversion from a slot to an integer index to the underlying array. static int GetIndex(FeedbackVectorSlot slot) { return kReservedIndexCount + slot.ToInt(); } static int GetIndexFromSpec(const FeedbackVectorSpec* spec, FeedbackVectorSlot slot); // Conversion from an integer index to the underlying array to a slot. static inline FeedbackVectorSlot ToSlot(int index); inline Object* Get(FeedbackVectorSlot slot) const; inline void Set(FeedbackVectorSlot slot, Object* value, WriteBarrierMode mode = UPDATE_WRITE_BARRIER); // Returns slot kind for given slot. FeedbackVectorSlotKind GetKind(FeedbackVectorSlot slot) const; // Returns name corresponding to given slot or an empty string. String* GetName(FeedbackVectorSlot slot) const; static Handle New(Isolate* isolate, Handle metadata); static Handle Copy(Isolate* isolate, Handle vector); #ifdef OBJECT_PRINT // For gdb debugging. void Print(); #endif // OBJECT_PRINT DECLARE_PRINTER(TypeFeedbackVector) // Clears the vector slots. void ClearSlots(SharedFunctionInfo* shared) { ClearSlotsImpl(shared, true); } void ClearSlotsAtGCTime(SharedFunctionInfo* shared) { ClearSlotsImpl(shared, false); } static void ClearAllKeyedStoreICs(Isolate* isolate); void ClearKeyedStoreICs(SharedFunctionInfo* shared); // The object that indicates an uninitialized cache. static inline Handle UninitializedSentinel(Isolate* isolate); // The object that indicates a megamorphic state. static inline Handle MegamorphicSentinel(Isolate* isolate); // The object that indicates a premonomorphic state. static inline Handle PremonomorphicSentinel(Isolate* isolate); // A raw version of the uninitialized sentinel that's safe to read during // garbage collection (e.g., for patching the cache). static inline Symbol* RawUninitializedSentinel(Isolate* isolate); static const int kDummyLoadICSlot = 0; static const int kDummyKeyedLoadICSlot = 2; static const int kDummyStoreICSlot = 4; static const int kDummyKeyedStoreICSlot = 6; static Handle DummyVector(Isolate* isolate); static FeedbackVectorSlot DummySlot(int dummyIndex) { DCHECK(dummyIndex >= 0 && dummyIndex <= kDummyKeyedStoreICSlot); return FeedbackVectorSlot(dummyIndex); } private: void ClearSlotsImpl(SharedFunctionInfo* shared, bool force_clear); DISALLOW_IMPLICIT_CONSTRUCTORS(TypeFeedbackVector); }; // The following asserts protect an optimization in type feedback vector // code that looks into the contents of a slot assuming to find a String, // a Symbol, an AllocationSite, a WeakCell, or a FixedArray. STATIC_ASSERT(WeakCell::kSize >= 2 * kPointerSize); STATIC_ASSERT(WeakCell::kValueOffset == AllocationSite::kTransitionInfoOffset); STATIC_ASSERT(WeakCell::kValueOffset == FixedArray::kLengthOffset); STATIC_ASSERT(WeakCell::kValueOffset == Name::kHashFieldSlot); // Verify that an empty hash field looks like a tagged object, but can't // possibly be confused with a pointer. STATIC_ASSERT((Name::kEmptyHashField & kHeapObjectTag) == kHeapObjectTag); STATIC_ASSERT(Name::kEmptyHashField == 0x3); // Verify that a set hash field will not look like a tagged object. STATIC_ASSERT(Name::kHashNotComputedMask == kHeapObjectTag); class TypeFeedbackMetadataIterator { public: explicit TypeFeedbackMetadataIterator(Handle metadata) : metadata_handle_(metadata), next_slot_(FeedbackVectorSlot(0)), slot_kind_(FeedbackVectorSlotKind::INVALID) {} explicit TypeFeedbackMetadataIterator(TypeFeedbackMetadata* metadata) : metadata_(metadata), next_slot_(FeedbackVectorSlot(0)), slot_kind_(FeedbackVectorSlotKind::INVALID) {} inline bool HasNext() const; inline FeedbackVectorSlot Next(); // Returns slot kind of the last slot returned by Next(). FeedbackVectorSlotKind kind() const { DCHECK_NE(FeedbackVectorSlotKind::INVALID, slot_kind_); DCHECK_NE(FeedbackVectorSlotKind::KINDS_NUMBER, slot_kind_); return slot_kind_; } // Returns entry size of the last slot returned by Next(). inline int entry_size() const; String* name() const { DCHECK(TypeFeedbackMetadata::SlotRequiresName(kind())); return metadata()->GetName(cur_slot_); } private: TypeFeedbackMetadata* metadata() const { return !metadata_handle_.is_null() ? *metadata_handle_ : metadata_; } // The reason for having a handle and a raw pointer to the meta data is // to have a single iterator implementation for both "handlified" and raw // pointer use cases. Handle metadata_handle_; TypeFeedbackMetadata* metadata_; FeedbackVectorSlot cur_slot_; FeedbackVectorSlot next_slot_; FeedbackVectorSlotKind slot_kind_; }; // 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, FeedbackVectorSlot slot) : vector_handle_(vector), vector_(NULL), slot_(slot) {} FeedbackNexus(TypeFeedbackVector* vector, FeedbackVectorSlot slot) : vector_(vector), slot_(slot) {} virtual ~FeedbackNexus() {} Handle vector_handle() const { DCHECK(vector_ == NULL); return vector_handle_; } TypeFeedbackVector* vector() const { return vector_handle_.is_null() ? vector_ : *vector_handle_; } FeedbackVectorSlot slot() const { return slot_; } InlineCacheState ic_state() const { return StateFromFeedback(); } bool IsUninitialized() const { return StateFromFeedback() == UNINITIALIZED; } Map* FindFirstMap() const { MapHandleList maps; ExtractMaps(&maps); if (maps.length() > 0) return *maps.at(0); return NULL; } // TODO(mvstanton): remove FindAllMaps, it didn't survive a code review. void FindAllMaps(MapHandleList* maps) const { ExtractMaps(maps); } virtual InlineCacheState StateFromFeedback() const = 0; virtual int ExtractMaps(MapHandleList* maps) const; virtual MaybeHandle FindHandlerForMap(Handle map) const; virtual bool FindHandlers(CodeHandleList* code_list, int length = -1) const; virtual Name* FindFirstName() const { return NULL; } virtual void ConfigureUninitialized(); virtual void ConfigurePremonomorphic(); virtual void ConfigureMegamorphic(); inline Object* GetFeedback() const; inline Object* GetFeedbackExtra() const; inline Isolate* GetIsolate() const; protected: inline void SetFeedback(Object* feedback, WriteBarrierMode mode = UPDATE_WRITE_BARRIER); inline void SetFeedbackExtra(Object* feedback_extra, WriteBarrierMode mode = UPDATE_WRITE_BARRIER); Handle EnsureArrayOfSize(int length); Handle EnsureExtraArrayOfSize(int length); void InstallHandlers(Handle array, MapHandleList* maps, CodeHandleList* handlers); 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 vector_handle_; TypeFeedbackVector* vector_; FeedbackVectorSlot slot_; }; class CallICNexus final : public FeedbackNexus { public: CallICNexus(Handle vector, FeedbackVectorSlot slot) : FeedbackNexus(vector, slot) { DCHECK_EQ(FeedbackVectorSlotKind::CALL_IC, vector->GetKind(slot)); } CallICNexus(TypeFeedbackVector* vector, FeedbackVectorSlot slot) : FeedbackNexus(vector, slot) { DCHECK_EQ(FeedbackVectorSlotKind::CALL_IC, vector->GetKind(slot)); } void Clear(Code* host); void ConfigureMonomorphicArray(); void ConfigureMonomorphic(Handle function); void ConfigureMegamorphic() final; void ConfigureMegamorphic(int call_count); InlineCacheState StateFromFeedback() const final; int ExtractMaps(MapHandleList* maps) const final { // CallICs don't record map feedback. return 0; } MaybeHandle FindHandlerForMap(Handle map) const final { return MaybeHandle(); } bool FindHandlers(CodeHandleList* code_list, int length = -1) const final { return length == 0; } int ExtractCallCount(); }; class LoadICNexus : public FeedbackNexus { public: LoadICNexus(Handle vector, FeedbackVectorSlot slot) : FeedbackNexus(vector, slot) { DCHECK_EQ(FeedbackVectorSlotKind::LOAD_IC, vector->GetKind(slot)); } explicit LoadICNexus(Isolate* isolate) : FeedbackNexus( TypeFeedbackVector::DummyVector(isolate), FeedbackVectorSlot(TypeFeedbackVector::kDummyLoadICSlot)) {} LoadICNexus(TypeFeedbackVector* vector, FeedbackVectorSlot slot) : FeedbackNexus(vector, slot) { DCHECK_EQ(FeedbackVectorSlotKind::LOAD_IC, vector->GetKind(slot)); } void Clear(Code* host); void ConfigureMonomorphic(Handle receiver_map, Handle handler); void ConfigurePolymorphic(MapHandleList* maps, CodeHandleList* handlers); InlineCacheState StateFromFeedback() const override; }; class LoadGlobalICNexus : public FeedbackNexus { public: LoadGlobalICNexus(Handle vector, FeedbackVectorSlot slot) : FeedbackNexus(vector, slot) { DCHECK_EQ(FeedbackVectorSlotKind::LOAD_GLOBAL_IC, vector->GetKind(slot)); } LoadGlobalICNexus(TypeFeedbackVector* vector, FeedbackVectorSlot slot) : FeedbackNexus(vector, slot) { DCHECK_EQ(FeedbackVectorSlotKind::LOAD_GLOBAL_IC, vector->GetKind(slot)); } int ExtractMaps(MapHandleList* maps) const final { // LoadGlobalICs don't record map feedback. return 0; } MaybeHandle FindHandlerForMap(Handle map) const final { return MaybeHandle(); } bool FindHandlers(CodeHandleList* code_list, int length = -1) const final { return length == 0; } void ConfigureMegamorphic() override { UNREACHABLE(); } void Clear(Code* host); void ConfigureUninitialized() override; void ConfigurePropertyCellMode(Handle cell); void ConfigureHandlerMode(Handle handler); InlineCacheState StateFromFeedback() const override; }; class KeyedLoadICNexus : public FeedbackNexus { public: KeyedLoadICNexus(Handle vector, FeedbackVectorSlot slot) : FeedbackNexus(vector, slot) { DCHECK_EQ(FeedbackVectorSlotKind::KEYED_LOAD_IC, vector->GetKind(slot)); } KeyedLoadICNexus(TypeFeedbackVector* vector, FeedbackVectorSlot slot) : FeedbackNexus(vector, slot) { DCHECK_EQ(FeedbackVectorSlotKind::KEYED_LOAD_IC, vector->GetKind(slot)); } void Clear(Code* host); // name can be a null handle for element loads. void ConfigureMonomorphic(Handle name, Handle receiver_map, Handle handler); // name can be null. void ConfigurePolymorphic(Handle name, MapHandleList* maps, CodeHandleList* handlers); void ConfigureMegamorphicKeyed(IcCheckType property_type); IcCheckType GetKeyType() const; InlineCacheState StateFromFeedback() const override; Name* FindFirstName() const override; }; class StoreICNexus : public FeedbackNexus { public: StoreICNexus(Handle vector, FeedbackVectorSlot slot) : FeedbackNexus(vector, slot) { DCHECK_EQ(FeedbackVectorSlotKind::STORE_IC, vector->GetKind(slot)); } explicit StoreICNexus(Isolate* isolate) : FeedbackNexus( TypeFeedbackVector::DummyVector(isolate), FeedbackVectorSlot(TypeFeedbackVector::kDummyStoreICSlot)) {} StoreICNexus(TypeFeedbackVector* vector, FeedbackVectorSlot slot) : FeedbackNexus(vector, slot) { DCHECK_EQ(FeedbackVectorSlotKind::STORE_IC, vector->GetKind(slot)); } void Clear(Code* host); void ConfigureMonomorphic(Handle receiver_map, Handle handler); void ConfigurePolymorphic(MapHandleList* maps, CodeHandleList* handlers); InlineCacheState StateFromFeedback() const override; }; class KeyedStoreICNexus : public FeedbackNexus { public: KeyedStoreICNexus(Handle vector, FeedbackVectorSlot slot) : FeedbackNexus(vector, slot) { DCHECK_EQ(FeedbackVectorSlotKind::KEYED_STORE_IC, vector->GetKind(slot)); } explicit KeyedStoreICNexus(Isolate* isolate) : FeedbackNexus( TypeFeedbackVector::DummyVector(isolate), FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedStoreICSlot)) {} KeyedStoreICNexus(TypeFeedbackVector* vector, FeedbackVectorSlot slot) : FeedbackNexus(vector, slot) { DCHECK_EQ(FeedbackVectorSlotKind::KEYED_STORE_IC, vector->GetKind(slot)); } void Clear(Code* host); // name can be a null handle for element loads. void ConfigureMonomorphic(Handle name, Handle receiver_map, Handle handler); // name can be null. void ConfigurePolymorphic(Handle name, MapHandleList* maps, CodeHandleList* handlers); void ConfigurePolymorphic(MapHandleList* maps, MapHandleList* transitioned_maps, CodeHandleList* handlers); void ConfigureMegamorphicKeyed(IcCheckType property_type); KeyedAccessStoreMode GetKeyedAccessStoreMode() const; IcCheckType GetKeyType() const; InlineCacheState StateFromFeedback() const override; Name* FindFirstName() const override; }; } // namespace internal } // namespace v8 #endif // V8_TRANSITIONS_H_