| // 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/feedback-vector.h" | |
| #include "src/code-stubs.h" | |
| #include "src/feedback-vector-inl.h" | |
| #include "src/ic/ic-inl.h" | |
| #include "src/objects.h" | |
| #include "src/objects/data-handler-inl.h" | |
| #include "src/objects/hash-table-inl.h" | |
| #include "src/objects/object-macros.h" | |
| namespace v8 { | |
| namespace internal { | |
| FeedbackSlot FeedbackVectorSpec::AddSlot(FeedbackSlotKind kind) { | |
| int slot = slots(); | |
| int entries_per_slot = FeedbackMetadata::GetSlotSize(kind); | |
| append(kind); | |
| for (int i = 1; i < entries_per_slot; i++) { | |
| append(FeedbackSlotKind::kInvalid); | |
| } | |
| return FeedbackSlot(slot); | |
| } | |
| FeedbackSlot FeedbackVectorSpec::AddTypeProfileSlot() { | |
| FeedbackSlot slot = AddSlot(FeedbackSlotKind::kTypeProfile); | |
| CHECK_EQ(FeedbackVectorSpec::kTypeProfileSlotIndex, | |
| FeedbackVector::GetIndex(slot)); | |
| return slot; | |
| } | |
| bool FeedbackVectorSpec::HasTypeProfileSlot() const { | |
| FeedbackSlot slot = | |
| FeedbackVector::ToSlot(FeedbackVectorSpec::kTypeProfileSlotIndex); | |
| if (slots() <= slot.ToInt()) { | |
| return false; | |
| } | |
| return GetKind(slot) == FeedbackSlotKind::kTypeProfile; | |
| } | |
| static bool IsPropertyNameFeedback(MaybeObject* feedback) { | |
| HeapObject* heap_object; | |
| if (!feedback->ToStrongHeapObject(&heap_object)) return false; | |
| if (heap_object->IsString()) return true; | |
| if (!heap_object->IsSymbol()) return false; | |
| Symbol* symbol = Symbol::cast(heap_object); | |
| ReadOnlyRoots roots = symbol->GetReadOnlyRoots(); | |
| return symbol != roots.uninitialized_symbol() && | |
| symbol != roots.premonomorphic_symbol() && | |
| symbol != roots.megamorphic_symbol(); | |
| } | |
| std::ostream& operator<<(std::ostream& os, FeedbackSlotKind kind) { | |
| return os << FeedbackMetadata::Kind2String(kind); | |
| } | |
| FeedbackSlotKind FeedbackMetadata::GetKind(FeedbackSlot slot) const { | |
| int index = VectorICComputer::index(0, slot.ToInt()); | |
| int data = get(index); | |
| return VectorICComputer::decode(data, slot.ToInt()); | |
| } | |
| void FeedbackMetadata::SetKind(FeedbackSlot slot, FeedbackSlotKind kind) { | |
| int index = VectorICComputer::index(0, slot.ToInt()); | |
| int data = get(index); | |
| int new_data = VectorICComputer::encode(data, slot.ToInt(), kind); | |
| set(index, new_data); | |
| } | |
| // static | |
| Handle<FeedbackMetadata> FeedbackMetadata::New(Isolate* isolate, | |
| const FeedbackVectorSpec* spec) { | |
| Factory* factory = isolate->factory(); | |
| const int slot_count = spec == nullptr ? 0 : spec->slots(); | |
| if (slot_count == 0) { | |
| return factory->empty_feedback_metadata(); | |
| } | |
| #ifdef DEBUG | |
| for (int i = 0; i < slot_count;) { | |
| DCHECK(spec); | |
| FeedbackSlotKind kind = spec->GetKind(FeedbackSlot(i)); | |
| int entry_size = FeedbackMetadata::GetSlotSize(kind); | |
| for (int j = 1; j < entry_size; j++) { | |
| FeedbackSlotKind kind = spec->GetKind(FeedbackSlot(i + j)); | |
| DCHECK_EQ(FeedbackSlotKind::kInvalid, kind); | |
| } | |
| i += entry_size; | |
| } | |
| #endif | |
| Handle<FeedbackMetadata> metadata = factory->NewFeedbackMetadata(slot_count); | |
| // Initialize the slots. The raw data section has already been pre-zeroed in | |
| // NewFeedbackMetadata. | |
| for (int i = 0; i < slot_count; i++) { | |
| DCHECK(spec); | |
| FeedbackSlot slot(i); | |
| FeedbackSlotKind kind = spec->GetKind(slot); | |
| metadata->SetKind(slot, kind); | |
| } | |
| return metadata; | |
| } | |
| bool FeedbackMetadata::SpecDiffersFrom( | |
| const FeedbackVectorSpec* other_spec) const { | |
| if (other_spec->slots() != slot_count()) { | |
| return true; | |
| } | |
| int slots = slot_count(); | |
| for (int i = 0; i < slots;) { | |
| FeedbackSlot slot(i); | |
| FeedbackSlotKind kind = GetKind(slot); | |
| int entry_size = FeedbackMetadata::GetSlotSize(kind); | |
| if (kind != other_spec->GetKind(slot)) { | |
| return true; | |
| } | |
| i += entry_size; | |
| } | |
| return false; | |
| } | |
| const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) { | |
| switch (kind) { | |
| case FeedbackSlotKind::kInvalid: | |
| return "Invalid"; | |
| case FeedbackSlotKind::kCall: | |
| return "Call"; | |
| case FeedbackSlotKind::kLoadProperty: | |
| return "LoadProperty"; | |
| case FeedbackSlotKind::kLoadGlobalInsideTypeof: | |
| return "LoadGlobalInsideTypeof"; | |
| case FeedbackSlotKind::kLoadGlobalNotInsideTypeof: | |
| return "LoadGlobalNotInsideTypeof"; | |
| case FeedbackSlotKind::kLoadKeyed: | |
| return "LoadKeyed"; | |
| case FeedbackSlotKind::kStoreNamedSloppy: | |
| return "StoreNamedSloppy"; | |
| case FeedbackSlotKind::kStoreNamedStrict: | |
| return "StoreNamedStrict"; | |
| case FeedbackSlotKind::kStoreOwnNamed: | |
| return "StoreOwnNamed"; | |
| case FeedbackSlotKind::kStoreGlobalSloppy: | |
| return "StoreGlobalSloppy"; | |
| case FeedbackSlotKind::kStoreGlobalStrict: | |
| return "StoreGlobalStrict"; | |
| case FeedbackSlotKind::kStoreKeyedSloppy: | |
| return "StoreKeyedSloppy"; | |
| case FeedbackSlotKind::kStoreKeyedStrict: | |
| return "StoreKeyedStrict"; | |
| case FeedbackSlotKind::kStoreInArrayLiteral: | |
| return "StoreInArrayLiteral"; | |
| case FeedbackSlotKind::kBinaryOp: | |
| return "BinaryOp"; | |
| case FeedbackSlotKind::kCompareOp: | |
| return "CompareOp"; | |
| case FeedbackSlotKind::kStoreDataPropertyInLiteral: | |
| return "StoreDataPropertyInLiteral"; | |
| case FeedbackSlotKind::kCreateClosure: | |
| return "kCreateClosure"; | |
| case FeedbackSlotKind::kLiteral: | |
| return "Literal"; | |
| case FeedbackSlotKind::kTypeProfile: | |
| return "TypeProfile"; | |
| case FeedbackSlotKind::kForIn: | |
| return "ForIn"; | |
| case FeedbackSlotKind::kInstanceOf: | |
| return "InstanceOf"; | |
| case FeedbackSlotKind::kCloneObject: | |
| return "CloneObject"; | |
| case FeedbackSlotKind::kKindsNumber: | |
| break; | |
| } | |
| UNREACHABLE(); | |
| } | |
| bool FeedbackMetadata::HasTypeProfileSlot() const { | |
| FeedbackSlot slot = | |
| FeedbackVector::ToSlot(FeedbackVectorSpec::kTypeProfileSlotIndex); | |
| return slot.ToInt() < slot_count() && | |
| GetKind(slot) == FeedbackSlotKind::kTypeProfile; | |
| } | |
| FeedbackSlotKind FeedbackVector::GetKind(FeedbackSlot slot) const { | |
| DCHECK(!is_empty()); | |
| return metadata()->GetKind(slot); | |
| } | |
| FeedbackSlot FeedbackVector::GetTypeProfileSlot() const { | |
| DCHECK(metadata()->HasTypeProfileSlot()); | |
| FeedbackSlot slot = | |
| FeedbackVector::ToSlot(FeedbackVectorSpec::kTypeProfileSlotIndex); | |
| DCHECK_EQ(FeedbackSlotKind::kTypeProfile, GetKind(slot)); | |
| return slot; | |
| } | |
| // static | |
| Handle<FeedbackVector> FeedbackVector::New(Isolate* isolate, | |
| Handle<SharedFunctionInfo> shared) { | |
| Factory* factory = isolate->factory(); | |
| const int slot_count = shared->feedback_metadata()->slot_count(); | |
| Handle<FeedbackVector> vector = factory->NewFeedbackVector(shared, TENURED); | |
| DCHECK_EQ(vector->length(), slot_count); | |
| DCHECK_EQ(vector->shared_function_info(), *shared); | |
| DCHECK_EQ( | |
| vector->optimized_code_weak_or_smi(), | |
| MaybeObject::FromSmi(Smi::FromEnum( | |
| FLAG_log_function_events ? OptimizationMarker::kLogFirstExecution | |
| : OptimizationMarker::kNone))); | |
| DCHECK_EQ(vector->invocation_count(), 0); | |
| DCHECK_EQ(vector->profiler_ticks(), 0); | |
| DCHECK_EQ(vector->deopt_count(), 0); | |
| // Ensure we can skip the write barrier | |
| Handle<Object> uninitialized_sentinel = UninitializedSentinel(isolate); | |
| DCHECK_EQ(ReadOnlyRoots(isolate).uninitialized_symbol(), | |
| *uninitialized_sentinel); | |
| Handle<Oddball> undefined_value = factory->undefined_value(); | |
| for (int i = 0; i < slot_count;) { | |
| FeedbackSlot slot(i); | |
| FeedbackSlotKind kind = shared->feedback_metadata()->GetKind(slot); | |
| int index = FeedbackVector::GetIndex(slot); | |
| int entry_size = FeedbackMetadata::GetSlotSize(kind); | |
| Object* extra_value = *uninitialized_sentinel; | |
| switch (kind) { | |
| case FeedbackSlotKind::kLoadGlobalInsideTypeof: | |
| case FeedbackSlotKind::kLoadGlobalNotInsideTypeof: | |
| case FeedbackSlotKind::kStoreGlobalSloppy: | |
| case FeedbackSlotKind::kStoreGlobalStrict: | |
| vector->set(index, HeapObjectReference::ClearedValue(), | |
| SKIP_WRITE_BARRIER); | |
| break; | |
| case FeedbackSlotKind::kForIn: | |
| case FeedbackSlotKind::kCompareOp: | |
| case FeedbackSlotKind::kBinaryOp: | |
| vector->set(index, Smi::kZero, SKIP_WRITE_BARRIER); | |
| break; | |
| case FeedbackSlotKind::kCreateClosure: { | |
| Handle<FeedbackCell> cell = factory->NewNoClosuresCell(undefined_value); | |
| vector->set(index, *cell); | |
| break; | |
| } | |
| case FeedbackSlotKind::kLiteral: | |
| vector->set(index, Smi::kZero, SKIP_WRITE_BARRIER); | |
| break; | |
| case FeedbackSlotKind::kCall: | |
| vector->set(index, *uninitialized_sentinel, SKIP_WRITE_BARRIER); | |
| extra_value = Smi::kZero; | |
| break; | |
| case FeedbackSlotKind::kCloneObject: | |
| case FeedbackSlotKind::kLoadProperty: | |
| case FeedbackSlotKind::kLoadKeyed: | |
| case FeedbackSlotKind::kStoreNamedSloppy: | |
| case FeedbackSlotKind::kStoreNamedStrict: | |
| case FeedbackSlotKind::kStoreOwnNamed: | |
| case FeedbackSlotKind::kStoreKeyedSloppy: | |
| case FeedbackSlotKind::kStoreKeyedStrict: | |
| case FeedbackSlotKind::kStoreInArrayLiteral: | |
| case FeedbackSlotKind::kStoreDataPropertyInLiteral: | |
| case FeedbackSlotKind::kTypeProfile: | |
| case FeedbackSlotKind::kInstanceOf: | |
| vector->set(index, *uninitialized_sentinel, SKIP_WRITE_BARRIER); | |
| break; | |
| case FeedbackSlotKind::kInvalid: | |
| case FeedbackSlotKind::kKindsNumber: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| for (int j = 1; j < entry_size; j++) { | |
| vector->set(index + j, extra_value, SKIP_WRITE_BARRIER); | |
| } | |
| i += entry_size; | |
| } | |
| Handle<FeedbackVector> result = Handle<FeedbackVector>::cast(vector); | |
| if (!isolate->is_best_effort_code_coverage() || | |
| isolate->is_collecting_type_profile()) { | |
| AddToVectorsForProfilingTools(isolate, result); | |
| } | |
| return result; | |
| } | |
| // static | |
| void FeedbackVector::AddToVectorsForProfilingTools( | |
| Isolate* isolate, Handle<FeedbackVector> vector) { | |
| DCHECK(!isolate->is_best_effort_code_coverage() || | |
| isolate->is_collecting_type_profile()); | |
| if (!vector->shared_function_info()->IsSubjectToDebugging()) return; | |
| Handle<ArrayList> list = Handle<ArrayList>::cast( | |
| isolate->factory()->feedback_vectors_for_profiling_tools()); | |
| list = ArrayList::Add(isolate, list, vector); | |
| isolate->SetFeedbackVectorsForProfilingTools(*list); | |
| } | |
| // static | |
| void FeedbackVector::SetOptimizedCode(Handle<FeedbackVector> vector, | |
| Handle<Code> code) { | |
| DCHECK_EQ(code->kind(), Code::OPTIMIZED_FUNCTION); | |
| vector->set_optimized_code_weak_or_smi(HeapObjectReference::Weak(*code)); | |
| } | |
| void FeedbackVector::ClearOptimizedCode() { | |
| DCHECK(has_optimized_code()); | |
| SetOptimizationMarker(OptimizationMarker::kNone); | |
| } | |
| void FeedbackVector::ClearOptimizationMarker() { | |
| DCHECK(!has_optimized_code()); | |
| SetOptimizationMarker(OptimizationMarker::kNone); | |
| } | |
| void FeedbackVector::SetOptimizationMarker(OptimizationMarker marker) { | |
| set_optimized_code_weak_or_smi(MaybeObject::FromSmi(Smi::FromEnum(marker))); | |
| } | |
| void FeedbackVector::EvictOptimizedCodeMarkedForDeoptimization( | |
| SharedFunctionInfo* shared, const char* reason) { | |
| MaybeObject* slot = optimized_code_weak_or_smi(); | |
| if (slot->IsSmi()) { | |
| return; | |
| } | |
| if (slot->IsClearedWeakHeapObject()) { | |
| ClearOptimizationMarker(); | |
| return; | |
| } | |
| Code* code = Code::cast(slot->GetHeapObject()); | |
| if (code->marked_for_deoptimization()) { | |
| if (FLAG_trace_deopt) { | |
| PrintF("[evicting optimizing code marked for deoptimization (%s) for ", | |
| reason); | |
| shared->ShortPrint(); | |
| PrintF("]\n"); | |
| } | |
| if (!code->deopt_already_counted()) { | |
| increment_deopt_count(); | |
| code->set_deopt_already_counted(true); | |
| } | |
| ClearOptimizedCode(); | |
| } | |
| } | |
| bool FeedbackVector::ClearSlots(Isolate* isolate) { | |
| MaybeObject* uninitialized_sentinel = MaybeObject::FromObject( | |
| FeedbackVector::RawUninitializedSentinel(isolate)); | |
| bool feedback_updated = false; | |
| FeedbackMetadataIterator iter(metadata()); | |
| while (iter.HasNext()) { | |
| FeedbackSlot slot = iter.Next(); | |
| MaybeObject* obj = Get(slot); | |
| if (obj != uninitialized_sentinel) { | |
| FeedbackNexus nexus(this, slot); | |
| feedback_updated |= nexus.Clear(); | |
| } | |
| } | |
| return feedback_updated; | |
| } | |
| void FeedbackVector::AssertNoLegacyTypes(MaybeObject* object) { | |
| #ifdef DEBUG | |
| HeapObject* heap_object; | |
| if (object->ToStrongOrWeakHeapObject(&heap_object)) { | |
| // Instead of FixedArray, the Feedback and the Extra should contain | |
| // WeakFixedArrays. The only allowed FixedArray subtype is HashTable. | |
| DCHECK_IMPLIES(heap_object->IsFixedArray(), heap_object->IsHashTable()); | |
| } | |
| #endif | |
| } | |
| Handle<WeakFixedArray> FeedbackNexus::EnsureArrayOfSize(int length) { | |
| Isolate* isolate = GetIsolate(); | |
| HeapObject* heap_object; | |
| if (GetFeedback()->ToStrongHeapObject(&heap_object) && | |
| heap_object->IsWeakFixedArray() && | |
| WeakFixedArray::cast(heap_object)->length() == length) { | |
| return handle(WeakFixedArray::cast(heap_object), isolate); | |
| } | |
| Handle<WeakFixedArray> array = isolate->factory()->NewWeakFixedArray(length); | |
| SetFeedback(*array); | |
| return array; | |
| } | |
| Handle<WeakFixedArray> FeedbackNexus::EnsureExtraArrayOfSize(int length) { | |
| Isolate* isolate = GetIsolate(); | |
| HeapObject* heap_object; | |
| if (GetFeedbackExtra()->ToStrongHeapObject(&heap_object) && | |
| heap_object->IsWeakFixedArray() && | |
| WeakFixedArray::cast(heap_object)->length() == length) { | |
| return handle(WeakFixedArray::cast(heap_object), isolate); | |
| } | |
| Handle<WeakFixedArray> array = isolate->factory()->NewWeakFixedArray(length); | |
| SetFeedbackExtra(*array); | |
| return array; | |
| } | |
| void FeedbackNexus::ConfigureUninitialized() { | |
| Isolate* isolate = GetIsolate(); | |
| switch (kind()) { | |
| case FeedbackSlotKind::kStoreGlobalSloppy: | |
| case FeedbackSlotKind::kStoreGlobalStrict: | |
| case FeedbackSlotKind::kLoadGlobalNotInsideTypeof: | |
| case FeedbackSlotKind::kLoadGlobalInsideTypeof: { | |
| SetFeedback(HeapObjectReference::ClearedValue(), SKIP_WRITE_BARRIER); | |
| SetFeedbackExtra(*FeedbackVector::UninitializedSentinel(isolate), | |
| SKIP_WRITE_BARRIER); | |
| break; | |
| } | |
| case FeedbackSlotKind::kCloneObject: | |
| case FeedbackSlotKind::kCall: { | |
| SetFeedback(*FeedbackVector::UninitializedSentinel(isolate), | |
| SKIP_WRITE_BARRIER); | |
| SetFeedbackExtra(Smi::kZero, SKIP_WRITE_BARRIER); | |
| break; | |
| } | |
| case FeedbackSlotKind::kInstanceOf: { | |
| SetFeedback(*FeedbackVector::UninitializedSentinel(isolate), | |
| SKIP_WRITE_BARRIER); | |
| break; | |
| } | |
| case FeedbackSlotKind::kStoreNamedSloppy: | |
| case FeedbackSlotKind::kStoreNamedStrict: | |
| case FeedbackSlotKind::kStoreKeyedSloppy: | |
| case FeedbackSlotKind::kStoreKeyedStrict: | |
| case FeedbackSlotKind::kStoreInArrayLiteral: | |
| case FeedbackSlotKind::kStoreOwnNamed: | |
| case FeedbackSlotKind::kLoadProperty: | |
| case FeedbackSlotKind::kLoadKeyed: | |
| case FeedbackSlotKind::kStoreDataPropertyInLiteral: { | |
| SetFeedback(*FeedbackVector::UninitializedSentinel(isolate), | |
| SKIP_WRITE_BARRIER); | |
| SetFeedbackExtra(*FeedbackVector::UninitializedSentinel(isolate), | |
| SKIP_WRITE_BARRIER); | |
| break; | |
| } | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| bool FeedbackNexus::Clear() { | |
| bool feedback_updated = false; | |
| switch (kind()) { | |
| case FeedbackSlotKind::kCreateClosure: | |
| case FeedbackSlotKind::kTypeProfile: | |
| // We don't clear these kinds ever. | |
| break; | |
| case FeedbackSlotKind::kCompareOp: | |
| case FeedbackSlotKind::kForIn: | |
| case FeedbackSlotKind::kBinaryOp: | |
| // We don't clear these, either. | |
| break; | |
| case FeedbackSlotKind::kLiteral: | |
| SetFeedback(Smi::kZero, SKIP_WRITE_BARRIER); | |
| feedback_updated = true; | |
| break; | |
| case FeedbackSlotKind::kStoreNamedSloppy: | |
| case FeedbackSlotKind::kStoreNamedStrict: | |
| case FeedbackSlotKind::kStoreKeyedSloppy: | |
| case FeedbackSlotKind::kStoreKeyedStrict: | |
| case FeedbackSlotKind::kStoreInArrayLiteral: | |
| case FeedbackSlotKind::kStoreOwnNamed: | |
| case FeedbackSlotKind::kLoadProperty: | |
| case FeedbackSlotKind::kLoadKeyed: | |
| case FeedbackSlotKind::kStoreGlobalSloppy: | |
| case FeedbackSlotKind::kStoreGlobalStrict: | |
| case FeedbackSlotKind::kLoadGlobalNotInsideTypeof: | |
| case FeedbackSlotKind::kLoadGlobalInsideTypeof: | |
| case FeedbackSlotKind::kCall: | |
| case FeedbackSlotKind::kInstanceOf: | |
| case FeedbackSlotKind::kStoreDataPropertyInLiteral: | |
| case FeedbackSlotKind::kCloneObject: | |
| if (!IsCleared()) { | |
| ConfigureUninitialized(); | |
| feedback_updated = true; | |
| } | |
| break; | |
| case FeedbackSlotKind::kInvalid: | |
| case FeedbackSlotKind::kKindsNumber: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| return feedback_updated; | |
| } | |
| void FeedbackNexus::ConfigurePremonomorphic(Handle<Map> receiver_map) { | |
| SetFeedback(*FeedbackVector::PremonomorphicSentinel(GetIsolate()), | |
| SKIP_WRITE_BARRIER); | |
| SetFeedbackExtra(HeapObjectReference::Weak(*receiver_map)); | |
| } | |
| bool FeedbackNexus::ConfigureMegamorphic() { | |
| DisallowHeapAllocation no_gc; | |
| Isolate* isolate = GetIsolate(); | |
| MaybeObject* sentinel = | |
| MaybeObject::FromObject(*FeedbackVector::MegamorphicSentinel(isolate)); | |
| if (GetFeedback() != sentinel) { | |
| SetFeedback(sentinel, SKIP_WRITE_BARRIER); | |
| SetFeedbackExtra(HeapObjectReference::ClearedValue()); | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool FeedbackNexus::ConfigureMegamorphic(IcCheckType property_type) { | |
| DisallowHeapAllocation no_gc; | |
| Isolate* isolate = GetIsolate(); | |
| bool changed = false; | |
| MaybeObject* sentinel = | |
| MaybeObject::FromObject(*FeedbackVector::MegamorphicSentinel(isolate)); | |
| if (GetFeedback() != sentinel) { | |
| SetFeedback(sentinel, SKIP_WRITE_BARRIER); | |
| changed = true; | |
| } | |
| Smi* extra = Smi::FromInt(static_cast<int>(property_type)); | |
| if (changed || GetFeedbackExtra() != MaybeObject::FromSmi(extra)) { | |
| SetFeedbackExtra(extra, SKIP_WRITE_BARRIER); | |
| changed = true; | |
| } | |
| return changed; | |
| } | |
| InlineCacheState FeedbackNexus::StateFromFeedback() const { | |
| Isolate* isolate = GetIsolate(); | |
| MaybeObject* feedback = GetFeedback(); | |
| switch (kind()) { | |
| case FeedbackSlotKind::kCreateClosure: | |
| case FeedbackSlotKind::kLiteral: | |
| // CreateClosure and literal slots don't have a notion of state. | |
| UNREACHABLE(); | |
| break; | |
| case FeedbackSlotKind::kStoreGlobalSloppy: | |
| case FeedbackSlotKind::kStoreGlobalStrict: | |
| case FeedbackSlotKind::kLoadGlobalNotInsideTypeof: | |
| case FeedbackSlotKind::kLoadGlobalInsideTypeof: { | |
| if (feedback->IsSmi()) return MONOMORPHIC; | |
| DCHECK(feedback->IsWeakOrClearedHeapObject()); | |
| MaybeObject* extra = GetFeedbackExtra(); | |
| if (!feedback->IsClearedWeakHeapObject() || | |
| extra != MaybeObject::FromObject( | |
| *FeedbackVector::UninitializedSentinel(isolate))) { | |
| return MONOMORPHIC; | |
| } | |
| return UNINITIALIZED; | |
| } | |
| case FeedbackSlotKind::kStoreNamedSloppy: | |
| case FeedbackSlotKind::kStoreNamedStrict: | |
| case FeedbackSlotKind::kStoreKeyedSloppy: | |
| case FeedbackSlotKind::kStoreKeyedStrict: | |
| case FeedbackSlotKind::kStoreInArrayLiteral: | |
| case FeedbackSlotKind::kStoreOwnNamed: | |
| case FeedbackSlotKind::kLoadProperty: | |
| case FeedbackSlotKind::kLoadKeyed: { | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::UninitializedSentinel(isolate))) { | |
| return UNINITIALIZED; | |
| } | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::MegamorphicSentinel(isolate))) { | |
| return MEGAMORPHIC; | |
| } | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::PremonomorphicSentinel(isolate))) { | |
| return PREMONOMORPHIC; | |
| } | |
| if (feedback->IsWeakOrClearedHeapObject()) { | |
| // Don't check if the map is cleared. | |
| return MONOMORPHIC; | |
| } | |
| HeapObject* heap_object; | |
| if (feedback->ToStrongHeapObject(&heap_object)) { | |
| if (heap_object->IsWeakFixedArray()) { | |
| // Determine state purely by our structure, don't check if the maps | |
| // are cleared. | |
| return POLYMORPHIC; | |
| } | |
| if (heap_object->IsName()) { | |
| DCHECK(IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind())); | |
| Object* extra = GetFeedbackExtra()->ToStrongHeapObject(); | |
| WeakFixedArray* extra_array = WeakFixedArray::cast(extra); | |
| return extra_array->length() > 2 ? POLYMORPHIC : MONOMORPHIC; | |
| } | |
| } | |
| UNREACHABLE(); | |
| } | |
| case FeedbackSlotKind::kCall: { | |
| HeapObject* heap_object; | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::MegamorphicSentinel(isolate))) { | |
| return GENERIC; | |
| } else if (feedback->IsWeakOrClearedHeapObject() || | |
| (feedback->ToStrongHeapObject(&heap_object) && | |
| heap_object->IsAllocationSite())) { | |
| return MONOMORPHIC; | |
| } | |
| CHECK_EQ(feedback, MaybeObject::FromObject( | |
| *FeedbackVector::UninitializedSentinel(isolate))); | |
| return UNINITIALIZED; | |
| } | |
| case FeedbackSlotKind::kBinaryOp: { | |
| BinaryOperationHint hint = GetBinaryOperationFeedback(); | |
| if (hint == BinaryOperationHint::kNone) { | |
| return UNINITIALIZED; | |
| } else if (hint == BinaryOperationHint::kAny) { | |
| return GENERIC; | |
| } | |
| return MONOMORPHIC; | |
| } | |
| case FeedbackSlotKind::kCompareOp: { | |
| CompareOperationHint hint = GetCompareOperationFeedback(); | |
| if (hint == CompareOperationHint::kNone) { | |
| return UNINITIALIZED; | |
| } else if (hint == CompareOperationHint::kAny) { | |
| return GENERIC; | |
| } | |
| return MONOMORPHIC; | |
| } | |
| case FeedbackSlotKind::kForIn: { | |
| ForInHint hint = GetForInFeedback(); | |
| if (hint == ForInHint::kNone) { | |
| return UNINITIALIZED; | |
| } else if (hint == ForInHint::kAny) { | |
| return GENERIC; | |
| } | |
| return MONOMORPHIC; | |
| } | |
| case FeedbackSlotKind::kInstanceOf: { | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::UninitializedSentinel(isolate))) { | |
| return UNINITIALIZED; | |
| } else if (feedback == | |
| MaybeObject::FromObject( | |
| *FeedbackVector::MegamorphicSentinel(isolate))) { | |
| return MEGAMORPHIC; | |
| } | |
| return MONOMORPHIC; | |
| } | |
| case FeedbackSlotKind::kStoreDataPropertyInLiteral: { | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::UninitializedSentinel(isolate))) { | |
| return UNINITIALIZED; | |
| } else if (feedback->IsWeakOrClearedHeapObject()) { | |
| // Don't check if the map is cleared. | |
| return MONOMORPHIC; | |
| } | |
| return MEGAMORPHIC; | |
| } | |
| case FeedbackSlotKind::kTypeProfile: { | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::UninitializedSentinel(isolate))) { | |
| return UNINITIALIZED; | |
| } | |
| return MONOMORPHIC; | |
| } | |
| case FeedbackSlotKind::kCloneObject: { | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::UninitializedSentinel(isolate))) { | |
| return UNINITIALIZED; | |
| } | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::MegamorphicSentinel(isolate))) { | |
| return MEGAMORPHIC; | |
| } | |
| if (feedback->IsWeakOrClearedHeapObject()) { | |
| return MONOMORPHIC; | |
| } | |
| DCHECK(feedback->ToStrongHeapObject()->IsWeakFixedArray()); | |
| return POLYMORPHIC; | |
| } | |
| case FeedbackSlotKind::kInvalid: | |
| case FeedbackSlotKind::kKindsNumber: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| return UNINITIALIZED; | |
| } | |
| void FeedbackNexus::ConfigurePropertyCellMode(Handle<PropertyCell> cell) { | |
| DCHECK(IsGlobalICKind(kind())); | |
| Isolate* isolate = GetIsolate(); | |
| SetFeedback(HeapObjectReference::Weak(*cell)); | |
| SetFeedbackExtra(*FeedbackVector::UninitializedSentinel(isolate), | |
| SKIP_WRITE_BARRIER); | |
| } | |
| bool FeedbackNexus::ConfigureLexicalVarMode(int script_context_index, | |
| int context_slot_index) { | |
| DCHECK(IsGlobalICKind(kind())); | |
| DCHECK_LE(0, script_context_index); | |
| DCHECK_LE(0, context_slot_index); | |
| if (!ContextIndexBits::is_valid(script_context_index) || | |
| !SlotIndexBits::is_valid(context_slot_index)) { | |
| return false; | |
| } | |
| int config = ContextIndexBits::encode(script_context_index) | | |
| SlotIndexBits::encode(context_slot_index); | |
| SetFeedback(Smi::FromInt(config)); | |
| Isolate* isolate = GetIsolate(); | |
| SetFeedbackExtra(*FeedbackVector::UninitializedSentinel(isolate), | |
| SKIP_WRITE_BARRIER); | |
| return true; | |
| } | |
| void FeedbackNexus::ConfigureHandlerMode(const MaybeObjectHandle& handler) { | |
| DCHECK(IsGlobalICKind(kind())); | |
| DCHECK(IC::IsHandler(*handler)); | |
| SetFeedback(HeapObjectReference::ClearedValue()); | |
| SetFeedbackExtra(*handler); | |
| } | |
| void FeedbackNexus::ConfigureCloneObject(Handle<Map> source_map, | |
| Handle<Map> result_map) { | |
| Isolate* isolate = GetIsolate(); | |
| MaybeObject* maybe_feedback = GetFeedback(); | |
| Handle<HeapObject> feedback(maybe_feedback->IsStrongOrWeakHeapObject() | |
| ? maybe_feedback->GetHeapObject() | |
| : nullptr, | |
| isolate); | |
| switch (ic_state()) { | |
| case UNINITIALIZED: | |
| // Cache the first map seen which meets the fast case requirements. | |
| SetFeedback(HeapObjectReference::Weak(*source_map)); | |
| SetFeedbackExtra(*result_map); | |
| break; | |
| case MONOMORPHIC: | |
| if (maybe_feedback->IsClearedWeakHeapObject() || | |
| feedback.is_identical_to(source_map) || | |
| Map::cast(*feedback)->is_deprecated()) { | |
| // Remain in MONOMORPHIC state if previous feedback has been collected. | |
| SetFeedback(HeapObjectReference::Weak(*source_map)); | |
| SetFeedbackExtra(*result_map); | |
| } else { | |
| // Transition to POLYMORPHIC. | |
| Handle<WeakFixedArray> array = | |
| EnsureArrayOfSize(2 * kCloneObjectPolymorphicEntrySize); | |
| array->Set(0, maybe_feedback); | |
| array->Set(1, GetFeedbackExtra()); | |
| array->Set(2, HeapObjectReference::Weak(*source_map)); | |
| array->Set(3, MaybeObject::FromObject(*result_map)); | |
| SetFeedbackExtra(HeapObjectReference::ClearedValue()); | |
| } | |
| break; | |
| case POLYMORPHIC: { | |
| static constexpr int kMaxElements = | |
| IC::kMaxPolymorphicMapCount * kCloneObjectPolymorphicEntrySize; | |
| Handle<WeakFixedArray> array = Handle<WeakFixedArray>::cast(feedback); | |
| int i = 0; | |
| for (; i < array->length(); i += kCloneObjectPolymorphicEntrySize) { | |
| MaybeObject* feedback = array->Get(i); | |
| if (feedback->IsClearedWeakHeapObject()) break; | |
| Handle<Map> cached_map(Map::cast(feedback->GetHeapObject()), isolate); | |
| if (cached_map.is_identical_to(source_map) || | |
| cached_map->is_deprecated()) | |
| break; | |
| } | |
| if (i >= array->length()) { | |
| if (i == kMaxElements) { | |
| // Transition to MEGAMORPHIC. | |
| MaybeObject* sentinel = MaybeObject::FromObject( | |
| *FeedbackVector::MegamorphicSentinel(isolate)); | |
| SetFeedback(sentinel, SKIP_WRITE_BARRIER); | |
| SetFeedbackExtra(HeapObjectReference::ClearedValue()); | |
| break; | |
| } | |
| // Grow polymorphic feedback array. | |
| Handle<WeakFixedArray> new_array = EnsureArrayOfSize( | |
| array->length() + kCloneObjectPolymorphicEntrySize); | |
| for (int j = 0; j < array->length(); ++j) { | |
| new_array->Set(j, array->Get(j)); | |
| } | |
| array = new_array; | |
| } | |
| array->Set(i, HeapObjectReference::Weak(*source_map)); | |
| array->Set(i + 1, MaybeObject::FromObject(*result_map)); | |
| break; | |
| } | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| int FeedbackNexus::GetCallCount() { | |
| DCHECK(IsCallICKind(kind())); | |
| Object* call_count = GetFeedbackExtra()->ToObject(); | |
| CHECK(call_count->IsSmi()); | |
| uint32_t value = static_cast<uint32_t>(Smi::ToInt(call_count)); | |
| return CallCountField::decode(value); | |
| } | |
| void FeedbackNexus::SetSpeculationMode(SpeculationMode mode) { | |
| DCHECK(IsCallICKind(kind())); | |
| Object* call_count = GetFeedbackExtra()->ToObject(); | |
| CHECK(call_count->IsSmi()); | |
| uint32_t count = static_cast<uint32_t>(Smi::ToInt(call_count)); | |
| uint32_t value = CallCountField::encode(CallCountField::decode(count)); | |
| int result = static_cast<int>(value | SpeculationModeField::encode(mode)); | |
| SetFeedbackExtra(Smi::FromInt(result), SKIP_WRITE_BARRIER); | |
| } | |
| SpeculationMode FeedbackNexus::GetSpeculationMode() { | |
| DCHECK(IsCallICKind(kind())); | |
| Object* call_count = GetFeedbackExtra()->ToObject(); | |
| CHECK(call_count->IsSmi()); | |
| uint32_t value = static_cast<uint32_t>(Smi::ToInt(call_count)); | |
| return SpeculationModeField::decode(value); | |
| } | |
| float FeedbackNexus::ComputeCallFrequency() { | |
| DCHECK(IsCallICKind(kind())); | |
| double const invocation_count = vector()->invocation_count(); | |
| double const call_count = GetCallCount(); | |
| if (invocation_count == 0) { | |
| // Prevent division by 0. | |
| return 0.0f; | |
| } | |
| return static_cast<float>(call_count / invocation_count); | |
| } | |
| void FeedbackNexus::ConfigureMonomorphic(Handle<Name> name, | |
| Handle<Map> receiver_map, | |
| const MaybeObjectHandle& handler) { | |
| DCHECK(handler.is_null() || IC::IsHandler(*handler)); | |
| if (kind() == FeedbackSlotKind::kStoreDataPropertyInLiteral) { | |
| SetFeedback(HeapObjectReference::Weak(*receiver_map)); | |
| SetFeedbackExtra(*name); | |
| } else { | |
| if (name.is_null()) { | |
| SetFeedback(HeapObjectReference::Weak(*receiver_map)); | |
| SetFeedbackExtra(*handler); | |
| } else { | |
| Handle<WeakFixedArray> array = EnsureExtraArrayOfSize(2); | |
| SetFeedback(*name); | |
| array->Set(0, HeapObjectReference::Weak(*receiver_map)); | |
| array->Set(1, *handler); | |
| } | |
| } | |
| } | |
| void FeedbackNexus::ConfigurePolymorphic(Handle<Name> name, | |
| MapHandles const& maps, | |
| MaybeObjectHandles* handlers) { | |
| DCHECK_EQ(handlers->size(), maps.size()); | |
| int receiver_count = static_cast<int>(maps.size()); | |
| DCHECK_GT(receiver_count, 1); | |
| Handle<WeakFixedArray> array; | |
| if (name.is_null()) { | |
| array = EnsureArrayOfSize(receiver_count * 2); | |
| SetFeedbackExtra(*FeedbackVector::UninitializedSentinel(GetIsolate()), | |
| SKIP_WRITE_BARRIER); | |
| } else { | |
| array = EnsureExtraArrayOfSize(receiver_count * 2); | |
| SetFeedback(*name); | |
| } | |
| for (int current = 0; current < receiver_count; ++current) { | |
| Handle<Map> map = maps[current]; | |
| array->Set(current * 2, HeapObjectReference::Weak(*map)); | |
| DCHECK(IC::IsHandler(*handlers->at(current))); | |
| array->Set(current * 2 + 1, *handlers->at(current)); | |
| } | |
| } | |
| int FeedbackNexus::ExtractMaps(MapHandles* maps) const { | |
| DCHECK(IsLoadICKind(kind()) || IsStoreICKind(kind()) || | |
| IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) || | |
| IsStoreOwnICKind(kind()) || IsStoreDataPropertyInLiteralKind(kind()) || | |
| IsStoreInArrayLiteralICKind(kind())); | |
| Isolate* isolate = GetIsolate(); | |
| MaybeObject* feedback = GetFeedback(); | |
| bool is_named_feedback = IsPropertyNameFeedback(feedback); | |
| HeapObject* heap_object; | |
| if ((feedback->ToStrongHeapObject(&heap_object) && | |
| heap_object->IsWeakFixedArray()) || | |
| is_named_feedback) { | |
| int found = 0; | |
| WeakFixedArray* array; | |
| if (is_named_feedback) { | |
| array = WeakFixedArray::cast(GetFeedbackExtra()->ToStrongHeapObject()); | |
| } else { | |
| array = WeakFixedArray::cast(heap_object); | |
| } | |
| const int increment = 2; | |
| HeapObject* heap_object; | |
| for (int i = 0; i < array->length(); i += increment) { | |
| DCHECK(array->Get(i)->IsWeakOrClearedHeapObject()); | |
| if (array->Get(i)->ToWeakHeapObject(&heap_object)) { | |
| Map* map = Map::cast(heap_object); | |
| maps->push_back(handle(map, isolate)); | |
| found++; | |
| } | |
| } | |
| return found; | |
| } else if (feedback->ToWeakHeapObject(&heap_object)) { | |
| Map* map = Map::cast(heap_object); | |
| maps->push_back(handle(map, isolate)); | |
| return 1; | |
| } else if (feedback->ToStrongHeapObject(&heap_object) && | |
| heap_object == | |
| heap_object->GetReadOnlyRoots().premonomorphic_symbol()) { | |
| if (GetFeedbackExtra()->ToWeakHeapObject(&heap_object)) { | |
| Map* map = Map::cast(heap_object); | |
| maps->push_back(handle(map, isolate)); | |
| return 1; | |
| } | |
| } | |
| return 0; | |
| } | |
| MaybeObjectHandle FeedbackNexus::FindHandlerForMap(Handle<Map> map) const { | |
| DCHECK(IsLoadICKind(kind()) || IsStoreICKind(kind()) || | |
| IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) || | |
| IsStoreOwnICKind(kind()) || IsStoreDataPropertyInLiteralKind(kind())); | |
| MaybeObject* feedback = GetFeedback(); | |
| Isolate* isolate = GetIsolate(); | |
| bool is_named_feedback = IsPropertyNameFeedback(feedback); | |
| HeapObject* heap_object; | |
| if ((feedback->ToStrongHeapObject(&heap_object) && | |
| heap_object->IsWeakFixedArray()) || | |
| is_named_feedback) { | |
| WeakFixedArray* array; | |
| if (is_named_feedback) { | |
| array = WeakFixedArray::cast(GetFeedbackExtra()->ToStrongHeapObject()); | |
| } else { | |
| array = WeakFixedArray::cast(heap_object); | |
| } | |
| const int increment = 2; | |
| HeapObject* heap_object; | |
| for (int i = 0; i < array->length(); i += increment) { | |
| DCHECK(array->Get(i)->IsWeakOrClearedHeapObject()); | |
| if (array->Get(i)->ToWeakHeapObject(&heap_object)) { | |
| Map* array_map = Map::cast(heap_object); | |
| if (array_map == *map && | |
| !array->Get(i + increment - 1)->IsClearedWeakHeapObject()) { | |
| MaybeObject* handler = array->Get(i + increment - 1); | |
| DCHECK(IC::IsHandler(handler)); | |
| return handle(handler, isolate); | |
| } | |
| } | |
| } | |
| } else if (feedback->ToWeakHeapObject(&heap_object)) { | |
| Map* cell_map = Map::cast(heap_object); | |
| if (cell_map == *map && !GetFeedbackExtra()->IsClearedWeakHeapObject()) { | |
| MaybeObject* handler = GetFeedbackExtra(); | |
| DCHECK(IC::IsHandler(handler)); | |
| return handle(handler, isolate); | |
| } | |
| } | |
| return MaybeObjectHandle(); | |
| } | |
| bool FeedbackNexus::FindHandlers(MaybeObjectHandles* code_list, | |
| int length) const { | |
| DCHECK(IsLoadICKind(kind()) || IsStoreICKind(kind()) || | |
| IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) || | |
| IsStoreOwnICKind(kind()) || IsStoreDataPropertyInLiteralKind(kind()) || | |
| IsStoreInArrayLiteralICKind(kind())); | |
| MaybeObject* feedback = GetFeedback(); | |
| Isolate* isolate = GetIsolate(); | |
| int count = 0; | |
| bool is_named_feedback = IsPropertyNameFeedback(feedback); | |
| HeapObject* heap_object; | |
| if ((feedback->ToStrongHeapObject(&heap_object) && | |
| heap_object->IsWeakFixedArray()) || | |
| is_named_feedback) { | |
| WeakFixedArray* array; | |
| if (is_named_feedback) { | |
| array = WeakFixedArray::cast(GetFeedbackExtra()->ToStrongHeapObject()); | |
| } else { | |
| array = WeakFixedArray::cast(heap_object); | |
| } | |
| const int increment = 2; | |
| HeapObject* heap_object; | |
| for (int i = 0; i < array->length(); i += increment) { | |
| // Be sure to skip handlers whose maps have been cleared. | |
| DCHECK(array->Get(i)->IsWeakOrClearedHeapObject()); | |
| if (array->Get(i)->ToWeakHeapObject(&heap_object) && | |
| !array->Get(i + increment - 1)->IsClearedWeakHeapObject()) { | |
| MaybeObject* handler = array->Get(i + increment - 1); | |
| DCHECK(IC::IsHandler(handler)); | |
| code_list->push_back(handle(handler, isolate)); | |
| count++; | |
| } | |
| } | |
| } else if (feedback->ToWeakHeapObject(&heap_object)) { | |
| MaybeObject* extra = GetFeedbackExtra(); | |
| if (!extra->IsClearedWeakHeapObject()) { | |
| DCHECK(IC::IsHandler(extra)); | |
| code_list->push_back(handle(extra, isolate)); | |
| count++; | |
| } | |
| } | |
| return count == length; | |
| } | |
| Name* FeedbackNexus::FindFirstName() const { | |
| if (IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind())) { | |
| MaybeObject* feedback = GetFeedback(); | |
| if (IsPropertyNameFeedback(feedback)) { | |
| return Name::cast(feedback->ToStrongHeapObject()); | |
| } | |
| } | |
| return nullptr; | |
| } | |
| KeyedAccessLoadMode FeedbackNexus::GetKeyedAccessLoadMode() const { | |
| DCHECK(IsKeyedLoadICKind(kind())); | |
| MapHandles maps; | |
| MaybeObjectHandles handlers; | |
| if (GetKeyType() == PROPERTY) return STANDARD_LOAD; | |
| ExtractMaps(&maps); | |
| FindHandlers(&handlers, static_cast<int>(maps.size())); | |
| for (MaybeObjectHandle const& handler : handlers) { | |
| KeyedAccessLoadMode mode = LoadHandler::GetKeyedAccessLoadMode(*handler); | |
| if (mode != STANDARD_LOAD) return mode; | |
| } | |
| return STANDARD_LOAD; | |
| } | |
| KeyedAccessStoreMode FeedbackNexus::GetKeyedAccessStoreMode() const { | |
| DCHECK(IsKeyedStoreICKind(kind()) || IsStoreInArrayLiteralICKind(kind())); | |
| KeyedAccessStoreMode mode = STANDARD_STORE; | |
| MapHandles maps; | |
| MaybeObjectHandles handlers; | |
| if (GetKeyType() == PROPERTY) return mode; | |
| ExtractMaps(&maps); | |
| FindHandlers(&handlers, static_cast<int>(maps.size())); | |
| for (const MaybeObjectHandle& maybe_code_handler : handlers) { | |
| // The first handler that isn't the slow handler will have the bits we need. | |
| Handle<Code> handler; | |
| if (maybe_code_handler.object()->IsStoreHandler()) { | |
| Handle<StoreHandler> data_handler = | |
| Handle<StoreHandler>::cast(maybe_code_handler.object()); | |
| handler = handle(Code::cast(data_handler->smi_handler()), | |
| vector()->GetIsolate()); | |
| } else if (maybe_code_handler.object()->IsSmi()) { | |
| // Skip proxy handlers. | |
| DCHECK_EQ(*(maybe_code_handler.object()), | |
| *StoreHandler::StoreProxy(GetIsolate())); | |
| continue; | |
| } else { | |
| // Element store without prototype chain check. | |
| handler = Handle<Code>::cast(maybe_code_handler.object()); | |
| if (handler->is_builtin()) continue; | |
| } | |
| CodeStub::Major major_key = CodeStub::MajorKeyFromKey(handler->stub_key()); | |
| uint32_t minor_key = CodeStub::MinorKeyFromKey(handler->stub_key()); | |
| CHECK(major_key == CodeStub::KeyedStoreSloppyArguments || | |
| major_key == CodeStub::StoreFastElement || | |
| major_key == CodeStub::StoreSlowElement || | |
| major_key == CodeStub::StoreInArrayLiteralSlow || | |
| major_key == CodeStub::ElementsTransitionAndStore || | |
| major_key == CodeStub::NoCache); | |
| if (major_key != CodeStub::NoCache) { | |
| mode = CommonStoreModeBits::decode(minor_key); | |
| break; | |
| } | |
| } | |
| return mode; | |
| } | |
| IcCheckType FeedbackNexus::GetKeyType() const { | |
| DCHECK(IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind()) || | |
| IsStoreInArrayLiteralICKind(kind())); | |
| MaybeObject* feedback = GetFeedback(); | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::MegamorphicSentinel(GetIsolate()))) { | |
| return static_cast<IcCheckType>(Smi::ToInt(GetFeedbackExtra()->ToObject())); | |
| } | |
| return IsPropertyNameFeedback(feedback) ? PROPERTY : ELEMENT; | |
| } | |
| BinaryOperationHint FeedbackNexus::GetBinaryOperationFeedback() const { | |
| DCHECK_EQ(kind(), FeedbackSlotKind::kBinaryOp); | |
| int feedback = Smi::ToInt(GetFeedback()->ToSmi()); | |
| return BinaryOperationHintFromFeedback(feedback); | |
| } | |
| CompareOperationHint FeedbackNexus::GetCompareOperationFeedback() const { | |
| DCHECK_EQ(kind(), FeedbackSlotKind::kCompareOp); | |
| int feedback = Smi::ToInt(GetFeedback()->ToSmi()); | |
| return CompareOperationHintFromFeedback(feedback); | |
| } | |
| ForInHint FeedbackNexus::GetForInFeedback() const { | |
| DCHECK_EQ(kind(), FeedbackSlotKind::kForIn); | |
| int feedback = Smi::ToInt(GetFeedback()->ToSmi()); | |
| return ForInHintFromFeedback(feedback); | |
| } | |
| Handle<FeedbackCell> FeedbackNexus::GetFeedbackCell() const { | |
| DCHECK_EQ(FeedbackSlotKind::kCreateClosure, kind()); | |
| return handle(FeedbackCell::cast(GetFeedback()->ToObject()), | |
| vector()->GetIsolate()); | |
| } | |
| MaybeHandle<JSObject> FeedbackNexus::GetConstructorFeedback() const { | |
| DCHECK_EQ(kind(), FeedbackSlotKind::kInstanceOf); | |
| Isolate* isolate = GetIsolate(); | |
| MaybeObject* feedback = GetFeedback(); | |
| HeapObject* heap_object; | |
| if (feedback->ToWeakHeapObject(&heap_object)) { | |
| return handle(JSObject::cast(heap_object), isolate); | |
| } | |
| return MaybeHandle<JSObject>(); | |
| } | |
| namespace { | |
| bool InList(Handle<ArrayList> types, Handle<String> type) { | |
| for (int i = 0; i < types->Length(); i++) { | |
| Object* obj = types->Get(i); | |
| if (String::cast(obj)->Equals(*type)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| } // anonymous namespace | |
| void FeedbackNexus::Collect(Handle<String> type, int position) { | |
| DCHECK(IsTypeProfileKind(kind())); | |
| DCHECK_GE(position, 0); | |
| Isolate* isolate = GetIsolate(); | |
| MaybeObject* const feedback = GetFeedback(); | |
| // Map source position to collection of types | |
| Handle<SimpleNumberDictionary> types; | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::UninitializedSentinel(isolate))) { | |
| types = SimpleNumberDictionary::New(isolate, 1); | |
| } else { | |
| types = handle(SimpleNumberDictionary::cast(feedback->ToStrongHeapObject()), | |
| isolate); | |
| } | |
| Handle<ArrayList> position_specific_types; | |
| int entry = types->FindEntry(isolate, position); | |
| if (entry == SimpleNumberDictionary::kNotFound) { | |
| position_specific_types = ArrayList::New(isolate, 1); | |
| types = SimpleNumberDictionary::Set( | |
| isolate, types, position, | |
| ArrayList::Add(isolate, position_specific_types, type)); | |
| } else { | |
| DCHECK(types->ValueAt(entry)->IsArrayList()); | |
| position_specific_types = | |
| handle(ArrayList::cast(types->ValueAt(entry)), isolate); | |
| if (!InList(position_specific_types, type)) { // Add type | |
| types = SimpleNumberDictionary::Set( | |
| isolate, types, position, | |
| ArrayList::Add(isolate, position_specific_types, type)); | |
| } | |
| } | |
| SetFeedback(*types); | |
| } | |
| std::vector<int> FeedbackNexus::GetSourcePositions() const { | |
| DCHECK(IsTypeProfileKind(kind())); | |
| std::vector<int> source_positions; | |
| Isolate* isolate = GetIsolate(); | |
| MaybeObject* const feedback = GetFeedback(); | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::UninitializedSentinel(isolate))) { | |
| return source_positions; | |
| } | |
| Handle<SimpleNumberDictionary> types( | |
| SimpleNumberDictionary::cast(feedback->ToStrongHeapObject()), isolate); | |
| for (int index = SimpleNumberDictionary::kElementsStartIndex; | |
| index < types->length(); index += SimpleNumberDictionary::kEntrySize) { | |
| int key_index = index + SimpleNumberDictionary::kEntryKeyIndex; | |
| Object* key = types->get(key_index); | |
| if (key->IsSmi()) { | |
| int position = Smi::cast(key)->value(); | |
| source_positions.push_back(position); | |
| } | |
| } | |
| return source_positions; | |
| } | |
| std::vector<Handle<String>> FeedbackNexus::GetTypesForSourcePositions( | |
| uint32_t position) const { | |
| DCHECK(IsTypeProfileKind(kind())); | |
| Isolate* isolate = GetIsolate(); | |
| MaybeObject* const feedback = GetFeedback(); | |
| std::vector<Handle<String>> types_for_position; | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::UninitializedSentinel(isolate))) { | |
| return types_for_position; | |
| } | |
| Handle<SimpleNumberDictionary> types( | |
| SimpleNumberDictionary::cast(feedback->ToStrongHeapObject()), isolate); | |
| int entry = types->FindEntry(isolate, position); | |
| if (entry == SimpleNumberDictionary::kNotFound) { | |
| return types_for_position; | |
| } | |
| DCHECK(types->ValueAt(entry)->IsArrayList()); | |
| Handle<ArrayList> position_specific_types = | |
| Handle<ArrayList>(ArrayList::cast(types->ValueAt(entry)), isolate); | |
| for (int i = 0; i < position_specific_types->Length(); i++) { | |
| Object* t = position_specific_types->Get(i); | |
| types_for_position.push_back(Handle<String>(String::cast(t), isolate)); | |
| } | |
| return types_for_position; | |
| } | |
| namespace { | |
| Handle<JSObject> ConvertToJSObject(Isolate* isolate, | |
| Handle<SimpleNumberDictionary> feedback) { | |
| Handle<JSObject> type_profile = | |
| isolate->factory()->NewJSObject(isolate->object_function()); | |
| for (int index = SimpleNumberDictionary::kElementsStartIndex; | |
| index < feedback->length(); | |
| index += SimpleNumberDictionary::kEntrySize) { | |
| int key_index = index + SimpleNumberDictionary::kEntryKeyIndex; | |
| Object* key = feedback->get(key_index); | |
| if (key->IsSmi()) { | |
| int value_index = index + SimpleNumberDictionary::kEntryValueIndex; | |
| Handle<ArrayList> position_specific_types( | |
| ArrayList::cast(feedback->get(value_index)), isolate); | |
| int position = Smi::ToInt(key); | |
| JSObject::AddDataElement( | |
| type_profile, position, | |
| isolate->factory()->NewJSArrayWithElements( | |
| ArrayList::Elements(isolate, position_specific_types)), | |
| PropertyAttributes::NONE); | |
| } | |
| } | |
| return type_profile; | |
| } | |
| } // namespace | |
| JSObject* FeedbackNexus::GetTypeProfile() const { | |
| DCHECK(IsTypeProfileKind(kind())); | |
| Isolate* isolate = GetIsolate(); | |
| MaybeObject* const feedback = GetFeedback(); | |
| if (feedback == MaybeObject::FromObject( | |
| *FeedbackVector::UninitializedSentinel(isolate))) { | |
| return *isolate->factory()->NewJSObject(isolate->object_function()); | |
| } | |
| return *ConvertToJSObject( | |
| isolate, | |
| handle(SimpleNumberDictionary::cast(feedback->ToStrongHeapObject()), | |
| isolate)); | |
| } | |
| void FeedbackNexus::ResetTypeProfile() { | |
| DCHECK(IsTypeProfileKind(kind())); | |
| SetFeedback(*FeedbackVector::UninitializedSentinel(GetIsolate())); | |
| } | |
| } // namespace internal | |
| } // namespace v8 |