// 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. #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" namespace v8 { namespace internal { // static TypeFeedbackVector::VectorICKind TypeFeedbackVector::FromCodeKind( Code::Kind kind) { switch (kind) { case Code::CALL_IC: return KindCallIC; case Code::LOAD_IC: return KindLoadIC; case Code::KEYED_LOAD_IC: return KindKeyedLoadIC; default: // Shouldn't get here. UNREACHABLE(); } return KindUnused; } // static Code::Kind TypeFeedbackVector::FromVectorICKind(VectorICKind kind) { switch (kind) { case KindCallIC: return Code::CALL_IC; case KindLoadIC: return Code::LOAD_IC; case KindKeyedLoadIC: return Code::KEYED_LOAD_IC; case KindUnused: break; } // Sentinel for no information. return Code::NUMBER_OF_KINDS; } Code::Kind TypeFeedbackVector::GetKind(FeedbackVectorICSlot slot) const { if (!FLAG_vector_ics) { // We only have CALL_ICs return Code::CALL_IC; } int index = VectorICComputer::index(kReservedIndexCount, slot.ToInt()); int data = Smi::cast(get(index))->value(); VectorICKind b = VectorICComputer::decode(data, slot.ToInt()); return FromVectorICKind(b); } void TypeFeedbackVector::SetKind(FeedbackVectorICSlot slot, Code::Kind kind) { if (!FLAG_vector_ics) { // Nothing to do if we only have CALL_ICs return; } VectorICKind b = FromCodeKind(kind); int index = VectorICComputer::index(kReservedIndexCount, slot.ToInt()); int data = Smi::cast(get(index))->value(); int new_data = VectorICComputer::encode(data, slot.ToInt(), b); set(index, Smi::FromInt(new_data)); } // static Handle<TypeFeedbackVector> TypeFeedbackVector::Allocate( Isolate* isolate, const FeedbackVectorSpec& spec) { const int slot_count = spec.slots(); const int ic_slot_count = spec.ic_slots(); const int index_count = FLAG_vector_ics ? VectorICComputer::word_count(ic_slot_count) : 0; const int length = slot_count + ic_slot_count + index_count + kReservedIndexCount; if (length == kReservedIndexCount) { return Handle<TypeFeedbackVector>::cast( isolate->factory()->empty_fixed_array()); } Handle<FixedArray> array = isolate->factory()->NewFixedArray(length, TENURED); if (ic_slot_count > 0) { array->set(kFirstICSlotIndex, Smi::FromInt(slot_count + index_count + kReservedIndexCount)); } else { array->set(kFirstICSlotIndex, Smi::FromInt(length)); } array->set(kWithTypesIndex, Smi::FromInt(0)); array->set(kGenericCountIndex, Smi::FromInt(0)); // Fill the indexes with zeros. for (int i = 0; i < index_count; i++) { array->set(kReservedIndexCount + i, Smi::FromInt(0)); } // Ensure we can skip the write barrier Handle<Object> uninitialized_sentinel = UninitializedSentinel(isolate); DCHECK_EQ(isolate->heap()->uninitialized_symbol(), *uninitialized_sentinel); for (int i = kReservedIndexCount + index_count; i < length; i++) { array->set(i, *uninitialized_sentinel, SKIP_WRITE_BARRIER); } Handle<TypeFeedbackVector> vector = Handle<TypeFeedbackVector>::cast(array); if (FLAG_vector_ics) { for (int i = 0; i < ic_slot_count; i++) { vector->SetKind(FeedbackVectorICSlot(i), spec.GetKind(i)); } } return vector; } // static Handle<TypeFeedbackVector> TypeFeedbackVector::Copy( Isolate* isolate, Handle<TypeFeedbackVector> vector) { Handle<TypeFeedbackVector> result; result = Handle<TypeFeedbackVector>::cast( isolate->factory()->CopyFixedArray(Handle<FixedArray>::cast(vector))); return result; } // This logic is copied from // StaticMarkingVisitor<StaticVisitor>::VisitCodeTarget. // TODO(mvstanton): with weak handling of all vector ics, this logic should // actually be completely eliminated and we no longer need to clear the // vector ICs. static bool ClearLogic(Heap* heap, int ic_age, Code::Kind kind, InlineCacheState state) { if (FLAG_cleanup_code_caches_at_gc && (kind == Code::CALL_IC || heap->flush_monomorphic_ics() || // TODO(mvstanton): is this ic_age granular enough? it comes from // the SharedFunctionInfo which may change on a different schedule // than ic targets. // ic_age != heap->global_ic_age() || // is_invalidated_weak_stub || heap->isolate()->serializer_enabled())) { return true; } return false; } void TypeFeedbackVector::ClearSlots(SharedFunctionInfo* shared) { int slots = Slots(); Isolate* isolate = GetIsolate(); Object* uninitialized_sentinel = TypeFeedbackVector::RawUninitializedSentinel(isolate->heap()); for (int i = 0; i < slots; i++) { FeedbackVectorSlot slot(i); Object* obj = Get(slot); if (obj->IsHeapObject()) { InstanceType instance_type = HeapObject::cast(obj)->map()->instance_type(); // AllocationSites are exempt from clearing. They don't store Maps // or Code pointers which can cause memory leaks if not cleared // regularly. if (instance_type != ALLOCATION_SITE_TYPE) { Set(slot, uninitialized_sentinel, SKIP_WRITE_BARRIER); } } } slots = ICSlots(); if (slots == 0) return; // Now clear vector-based ICs. // Try and pass the containing code (the "host"). Heap* heap = isolate->heap(); Code* host = shared->code(); // I'm not sure yet if this ic age is the correct one. int ic_age = shared->ic_age(); for (int i = 0; i < slots; i++) { FeedbackVectorICSlot slot(i); Object* obj = Get(slot); if (obj != uninitialized_sentinel) { Code::Kind kind = GetKind(slot); if (kind == Code::CALL_IC) { CallICNexus nexus(this, slot); if (ClearLogic(heap, ic_age, kind, nexus.StateFromFeedback())) { nexus.Clear(host); } } else if (kind == Code::LOAD_IC) { LoadICNexus nexus(this, slot); if (ClearLogic(heap, ic_age, kind, nexus.StateFromFeedback())) { nexus.Clear(host); } } else if (kind == Code::KEYED_LOAD_IC) { KeyedLoadICNexus nexus(this, slot); if (ClearLogic(heap, ic_age, kind, nexus.StateFromFeedback())) { nexus.Clear(host); } } } } } 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(); Handle<FixedArray> array = handle(FixedArray::cast(GetFeedback()), isolate); 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); Handle<WeakCell> cell = Map::WeakCellForMap(map); array->set(start_index + (current * 2), *cell); array->set(start_index + (current * 2 + 1), *handlers->at(current)); } } InlineCacheState LoadICNexus::StateFromFeedback() const { Isolate* isolate = GetIsolate(); Object* feedback = GetFeedback(); if (feedback == *vector()->UninitializedSentinel(isolate)) { return UNINITIALIZED; } else if (feedback == *vector()->MegamorphicSentinel(isolate)) { return MEGAMORPHIC; } else if (feedback == *vector()->PremonomorphicSentinel(isolate)) { return PREMONOMORPHIC; } else if (feedback->IsFixedArray()) { // Determine state purely by our structure, don't check if the maps are // cleared. FixedArray* array = FixedArray::cast(feedback); int length = array->length(); DCHECK(length >= 2); return length == 2 ? MONOMORPHIC : POLYMORPHIC; } return UNINITIALIZED; } InlineCacheState KeyedLoadICNexus::StateFromFeedback() const { Isolate* isolate = GetIsolate(); Object* feedback = GetFeedback(); if (feedback == *vector()->UninitializedSentinel(isolate)) { return UNINITIALIZED; } else if (feedback == *vector()->PremonomorphicSentinel(isolate)) { return PREMONOMORPHIC; } else if (feedback == *vector()->GenericSentinel(isolate)) { return GENERIC; } else if (feedback->IsFixedArray()) { // Determine state purely by our structure, don't check if the maps are // cleared. FixedArray* array = FixedArray::cast(feedback); int length = array->length(); DCHECK(length >= 3); return length == 3 ? MONOMORPHIC : POLYMORPHIC; } return UNINITIALIZED; } InlineCacheState CallICNexus::StateFromFeedback() const { Isolate* isolate = GetIsolate(); Object* feedback = GetFeedback(); if (feedback == *vector()->MegamorphicSentinel(isolate)) { return GENERIC; } else if (feedback->IsAllocationSite() || feedback->IsJSFunction()) { return MONOMORPHIC; } CHECK(feedback == *vector()->UninitializedSentinel(isolate)); return UNINITIALIZED; } void CallICNexus::Clear(Code* host) { CallIC::Clear(GetIsolate(), host, this); } 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); } void KeyedLoadICNexus::ConfigureGeneric() { SetFeedback(*vector()->GenericSentinel(GetIsolate()), SKIP_WRITE_BARRIER); } void LoadICNexus::ConfigureMegamorphic() { SetFeedback(*vector()->MegamorphicSentinel(GetIsolate()), SKIP_WRITE_BARRIER); } void LoadICNexus::ConfigurePremonomorphic() { SetFeedback(*vector()->PremonomorphicSentinel(GetIsolate()), SKIP_WRITE_BARRIER); } void KeyedLoadICNexus::ConfigurePremonomorphic() { SetFeedback(*vector()->PremonomorphicSentinel(GetIsolate()), SKIP_WRITE_BARRIER); } void LoadICNexus::ConfigureMonomorphic(Handle<HeapType> type, Handle<Code> handler) { Handle<FixedArray> array = EnsureArrayOfSize(2); Handle<Map> receiver_map = IC::TypeToMap(*type, GetIsolate()); Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map); array->set(0, *cell); array->set(1, *handler); } void KeyedLoadICNexus::ConfigureMonomorphic(Handle<Name> name, Handle<HeapType> type, Handle<Code> handler) { Handle<FixedArray> array = EnsureArrayOfSize(3); Handle<Map> receiver_map = IC::TypeToMap(*type, GetIsolate()); if (name.is_null()) { array->set(0, Smi::FromInt(0)); } else { array->set(0, *name); } Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map); array->set(1, *cell); array->set(2, *handler); } void LoadICNexus::ConfigurePolymorphic(TypeHandleList* types, CodeHandleList* handlers) { int receiver_count = types->length(); EnsureArrayOfSize(receiver_count * 2); InstallHandlers(0, types, handlers); } void KeyedLoadICNexus::ConfigurePolymorphic(Handle<Name> name, TypeHandleList* types, CodeHandleList* handlers) { int receiver_count = types->length(); Handle<FixedArray> array = EnsureArrayOfSize(1 + receiver_count * 2); if (name.is_null()) { array->set(0, Smi::FromInt(0)); } else { array->set(0, *name); } InstallHandlers(1, types, handlers); } int FeedbackNexus::ExtractMaps(int start_index, MapHandleList* maps) const { Isolate* isolate = GetIsolate(); Object* feedback = GetFeedback(); if (feedback->IsFixedArray()) { int found = 0; 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) { WeakCell* cell = WeakCell::cast(array->get(i)); if (!cell->cleared()) { Map* map = Map::cast(cell->value()); maps->Add(handle(map, isolate)); found++; } } return found; } 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) { WeakCell* cell = WeakCell::cast(array->get(i)); if (!cell->cleared()) { Map* array_map = Map::cast(cell->value()); 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, ... ]. Be sure to skip handlers whose maps // have been cleared. DCHECK(array->length() >= (2 + start_index)); for (int i = start_index; i < array->length(); i += 2) { WeakCell* cell = WeakCell::cast(array->get(i)); if (!cell->cleared()) { Code* code = Code::cast(array->get(i + 1)); DCHECK(code->kind() == Code::HANDLER); code_list->Add(handle(code)); count++; } } } return count == length; } int LoadICNexus::ExtractMaps(MapHandleList* maps) const { return FeedbackNexus::ExtractMaps(0, maps); } void LoadICNexus::Clear(Code* host) { LoadIC::Clear(GetIsolate(), host, this); } void KeyedLoadICNexus::Clear(Code* host) { KeyedLoadIC::Clear(GetIsolate(), host, this); } int KeyedLoadICNexus::ExtractMaps(MapHandleList* maps) const { return FeedbackNexus::ExtractMaps(1, maps); } MaybeHandle<Code> LoadICNexus::FindHandlerForMap(Handle<Map> map) const { return FeedbackNexus::FindHandlerForMap(0, map); } MaybeHandle<Code> KeyedLoadICNexus::FindHandlerForMap(Handle<Map> map) const { return FeedbackNexus::FindHandlerForMap(1, map); } bool LoadICNexus::FindHandlers(CodeHandleList* code_list, int length) const { return FeedbackNexus::FindHandlers(0, code_list, length); } bool KeyedLoadICNexus::FindHandlers(CodeHandleList* code_list, int length) const { return FeedbackNexus::FindHandlers(1, code_list, length); } Name* KeyedLoadICNexus::FindFirstName() const { Object* feedback = GetFeedback(); if (feedback->IsFixedArray()) { FixedArray* array = FixedArray::cast(feedback); DCHECK(array->length() >= 3); Object* name = array->get(0); if (name->IsName()) return Name::cast(name); } return NULL; } } } // namespace v8::internal