v8/src/feedback-vector.cc
Mythri 71c66873d6 [lite] Allocate FeedbackCell arrays for create closures in lite mode
We want to allocate feedback vectors lazily in lite mode. To do that,
we should create closures with the correct feedback cell. This cl
allocates feedback cell arrays to hold these feedback cells in lite mode.
This cl also modifies the compile lazy to builtin to expect these arrays
in the feedback cell.

Drive-by fix: InterpreterEntryTrampoline no longer has argument count in
a register. So updated comments and removed unnecessary push/pop of this
register.

Bug: v8:8394
Change-Id: I10d8ca67cebce61a284f0c80b200e1f0c24577a2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1511274
Reviewed-by: Hannes Payer <hpayer@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Commit-Queue: Mythri Alle <mythria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60189}
2019-03-12 14:28:29 +00:00

1429 lines
49 KiB
C++

// 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/feedback-vector-inl.h"
#include "src/ic/handler-configuration-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/map-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->GetHeapObjectIfStrong(&heap_object)) return false;
if (heap_object->IsString()) {
DCHECK(heap_object->IsInternalizedString());
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();
const int closure_feedback_cell_count =
spec == nullptr ? 0 : spec->closure_feedback_cells();
if (slot_count == 0 && closure_feedback_cell_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, closure_feedback_cell_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::kHasKeyed:
return "HasKeyed";
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::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,
Handle<FixedArray> closure_feedback_cell_array) {
Factory* factory = isolate->factory();
const int slot_count = shared->feedback_metadata()->slot_count();
Handle<FeedbackVector> vector =
factory->NewFeedbackVector(shared, AllocationType::kOld);
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);
vector->set_closure_feedback_cell_array(*closure_feedback_cell_array);
// Ensure we can skip the write barrier
Handle<Object> uninitialized_sentinel = UninitializedSentinel(isolate);
DCHECK_EQ(ReadOnlyRoots(isolate).uninitialized_symbol(),
*uninitialized_sentinel);
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(isolate),
SKIP_WRITE_BARRIER);
break;
case FeedbackSlotKind::kForIn:
case FeedbackSlotKind::kCompareOp:
case FeedbackSlotKind::kBinaryOp:
vector->set(index, Smi::kZero, SKIP_WRITE_BARRIER);
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::kHasKeyed:
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
Handle<FixedArray> FeedbackVector::NewClosureFeedbackCellArray(
Isolate* isolate, Handle<SharedFunctionInfo> shared) {
Factory* factory = isolate->factory();
int num_feedback_cells =
shared->feedback_metadata()->closure_feedback_cell_count();
if (num_feedback_cells == 0) {
return factory->empty_fixed_array();
}
Handle<FixedArray> feedback_cell_array =
factory->NewFixedArray(num_feedback_cells, AllocationType::kOld);
for (int i = 0; i < num_feedback_cells; i++) {
Handle<FeedbackCell> cell =
factory->NewNoClosuresCell(factory->undefined_value());
feedback_cell_array->set(i, *cell);
}
return feedback_cell_array;
}
// 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->IsCleared()) {
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->GetHeapObject(&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()->GetHeapObjectIfStrong(&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()->GetHeapObjectIfStrong(&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(isolate),
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::kHasKeyed:
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::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::kHasKeyed:
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(isolate));
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;
}
Map FeedbackNexus::GetFirstMap() const {
MapHandles maps;
ExtractMaps(&maps);
if (maps.size() > 0) return *maps.at(0);
return Map();
}
InlineCacheState FeedbackNexus::ic_state() const {
Isolate* isolate = GetIsolate();
MaybeObject feedback = GetFeedback();
switch (kind()) {
case FeedbackSlotKind::kLiteral:
if (feedback->IsSmi()) return UNINITIALIZED;
return MONOMORPHIC;
case FeedbackSlotKind::kStoreGlobalSloppy:
case FeedbackSlotKind::kStoreGlobalStrict:
case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
case FeedbackSlotKind::kLoadGlobalInsideTypeof: {
if (feedback->IsSmi()) return MONOMORPHIC;
if (feedback == MaybeObject::FromObject(
*FeedbackVector::PremonomorphicSentinel(isolate))) {
DCHECK(kind() == FeedbackSlotKind::kStoreGlobalSloppy ||
kind() == FeedbackSlotKind::kStoreGlobalStrict);
return PREMONOMORPHIC;
}
DCHECK(feedback->IsWeakOrCleared());
MaybeObject extra = GetFeedbackExtra();
if (!feedback->IsCleared() ||
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:
case FeedbackSlotKind::kHasKeyed: {
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->IsWeakOrCleared()) {
// Don't check if the map is cleared.
return MONOMORPHIC;
}
HeapObject heap_object;
if (feedback->GetHeapObjectIfStrong(&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()) ||
IsKeyedHasICKind(kind()));
Object extra = GetFeedbackExtra()->GetHeapObjectAssumeStrong();
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->IsWeakOrCleared() ||
(feedback->GetHeapObjectIfStrong(&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->IsWeakOrCleared()) {
// 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->IsWeakOrCleared()) {
return MONOMORPHIC;
}
DCHECK(feedback->GetHeapObjectAssumeStrong()->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,
bool immutable) {
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) ||
!ImmutabilityBit::is_valid(immutable)) {
return false;
}
int config = ContextIndexBits::encode(script_context_index) |
SlotIndexBits::encode(context_slot_index) |
ImmutabilityBit::encode(immutable);
SetFeedback(Smi::From31BitPattern(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(GetIsolate()));
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->IsStrongOrWeak()
? maybe_feedback->GetHeapObject()
: HeapObject(),
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->IsCleared() || 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(isolate));
}
break;
case POLYMORPHIC: {
const int kMaxElements =
FLAG_max_polymorphic_map_count * kCloneObjectPolymorphicEntrySize;
Handle<WeakFixedArray> array = Handle<WeakFixedArray>::cast(feedback);
int i = 0;
for (; i < array->length(); i += kCloneObjectPolymorphicEntrySize) {
MaybeObject feedback = array->Get(i);
if (feedback->IsCleared()) 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(isolate));
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()->cast<Object>();
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()->cast<Object>();
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()->cast<Object>();
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()) || IsKeyedHasICKind(kind()));
Isolate* isolate = GetIsolate();
MaybeObject feedback = GetFeedback();
bool is_named_feedback = IsPropertyNameFeedback(feedback);
HeapObject heap_object;
if ((feedback->GetHeapObjectIfStrong(&heap_object) &&
heap_object->IsWeakFixedArray()) ||
is_named_feedback) {
int found = 0;
WeakFixedArray array;
if (is_named_feedback) {
array =
WeakFixedArray::cast(GetFeedbackExtra()->GetHeapObjectAssumeStrong());
} 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)->IsWeakOrCleared());
if (array->Get(i)->GetHeapObjectIfWeak(&heap_object)) {
Map map = Map::cast(heap_object);
maps->push_back(handle(map, isolate));
found++;
}
}
return found;
} else if (feedback->GetHeapObjectIfWeak(&heap_object)) {
Map map = Map::cast(heap_object);
maps->push_back(handle(map, isolate));
return 1;
} else if (feedback->GetHeapObjectIfStrong(&heap_object) &&
heap_object ==
heap_object->GetReadOnlyRoots().premonomorphic_symbol()) {
if (GetFeedbackExtra()->GetHeapObjectIfWeak(&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()) ||
IsKeyedHasICKind(kind()));
MaybeObject feedback = GetFeedback();
Isolate* isolate = GetIsolate();
bool is_named_feedback = IsPropertyNameFeedback(feedback);
HeapObject heap_object;
if ((feedback->GetHeapObjectIfStrong(&heap_object) &&
heap_object->IsWeakFixedArray()) ||
is_named_feedback) {
WeakFixedArray array;
if (is_named_feedback) {
array =
WeakFixedArray::cast(GetFeedbackExtra()->GetHeapObjectAssumeStrong());
} 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)->IsWeakOrCleared());
if (array->Get(i)->GetHeapObjectIfWeak(&heap_object)) {
Map array_map = Map::cast(heap_object);
if (array_map == *map && !array->Get(i + increment - 1)->IsCleared()) {
MaybeObject handler = array->Get(i + increment - 1);
DCHECK(IC::IsHandler(handler));
return handle(handler, isolate);
}
}
}
} else if (feedback->GetHeapObjectIfWeak(&heap_object)) {
Map cell_map = Map::cast(heap_object);
if (cell_map == *map && !GetFeedbackExtra()->IsCleared()) {
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()) || IsKeyedHasICKind(kind()));
MaybeObject feedback = GetFeedback();
Isolate* isolate = GetIsolate();
int count = 0;
bool is_named_feedback = IsPropertyNameFeedback(feedback);
HeapObject heap_object;
if ((feedback->GetHeapObjectIfStrong(&heap_object) &&
heap_object->IsWeakFixedArray()) ||
is_named_feedback) {
WeakFixedArray array;
if (is_named_feedback) {
array =
WeakFixedArray::cast(GetFeedbackExtra()->GetHeapObjectAssumeStrong());
} 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)->IsWeakOrCleared());
if (array->Get(i)->GetHeapObjectIfWeak(&heap_object) &&
!array->Get(i + increment - 1)->IsCleared()) {
MaybeObject handler = array->Get(i + increment - 1);
DCHECK(IC::IsHandler(handler));
code_list->push_back(handle(handler, isolate));
count++;
}
}
} else if (feedback->GetHeapObjectIfWeak(&heap_object)) {
MaybeObject extra = GetFeedbackExtra();
if (!extra->IsCleared()) {
DCHECK(IC::IsHandler(extra));
code_list->push_back(handle(extra, isolate));
count++;
}
}
return count == length;
}
Name FeedbackNexus::GetName() const {
if (IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind()) ||
IsKeyedHasICKind(kind())) {
MaybeObject feedback = GetFeedback();
if (IsPropertyNameFeedback(feedback)) {
return Name::cast(feedback->GetHeapObjectAssumeStrong());
}
}
return Name();
}
KeyedAccessLoadMode FeedbackNexus::GetKeyedAccessLoadMode() const {
DCHECK(IsKeyedLoadICKind(kind()) || IsKeyedHasICKind(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;
}
namespace {
bool BuiltinHasKeyedAccessStoreMode(int builtin_index) {
DCHECK(Builtins::IsBuiltinId(builtin_index));
switch (builtin_index) {
case Builtins::kKeyedStoreIC_SloppyArguments_Standard:
case Builtins::kKeyedStoreIC_SloppyArguments_GrowNoTransitionHandleCOW:
case Builtins::kKeyedStoreIC_SloppyArguments_NoTransitionIgnoreOOB:
case Builtins::kKeyedStoreIC_SloppyArguments_NoTransitionHandleCOW:
case Builtins::kStoreFastElementIC_Standard:
case Builtins::kStoreFastElementIC_GrowNoTransitionHandleCOW:
case Builtins::kStoreFastElementIC_NoTransitionIgnoreOOB:
case Builtins::kStoreFastElementIC_NoTransitionHandleCOW:
case Builtins::kStoreInArrayLiteralIC_Slow_Standard:
case Builtins::kStoreInArrayLiteralIC_Slow_GrowNoTransitionHandleCOW:
case Builtins::kStoreInArrayLiteralIC_Slow_NoTransitionIgnoreOOB:
case Builtins::kStoreInArrayLiteralIC_Slow_NoTransitionHandleCOW:
case Builtins::kKeyedStoreIC_Slow_Standard:
case Builtins::kKeyedStoreIC_Slow_GrowNoTransitionHandleCOW:
case Builtins::kKeyedStoreIC_Slow_NoTransitionIgnoreOOB:
case Builtins::kKeyedStoreIC_Slow_NoTransitionHandleCOW:
case Builtins::kElementsTransitionAndStore_Standard:
case Builtins::kElementsTransitionAndStore_GrowNoTransitionHandleCOW:
case Builtins::kElementsTransitionAndStore_NoTransitionIgnoreOOB:
case Builtins::kElementsTransitionAndStore_NoTransitionHandleCOW:
return true;
default:
return false;
}
UNREACHABLE();
}
KeyedAccessStoreMode KeyedAccessStoreModeForBuiltin(int builtin_index) {
DCHECK(BuiltinHasKeyedAccessStoreMode(builtin_index));
switch (builtin_index) {
case Builtins::kKeyedStoreIC_SloppyArguments_Standard:
case Builtins::kStoreInArrayLiteralIC_Slow_Standard:
case Builtins::kKeyedStoreIC_Slow_Standard:
case Builtins::kStoreFastElementIC_Standard:
case Builtins::kElementsTransitionAndStore_Standard:
return STANDARD_STORE;
case Builtins::kKeyedStoreIC_SloppyArguments_GrowNoTransitionHandleCOW:
case Builtins::kStoreInArrayLiteralIC_Slow_GrowNoTransitionHandleCOW:
case Builtins::kKeyedStoreIC_Slow_GrowNoTransitionHandleCOW:
case Builtins::kStoreFastElementIC_GrowNoTransitionHandleCOW:
case Builtins::kElementsTransitionAndStore_GrowNoTransitionHandleCOW:
return STORE_AND_GROW_NO_TRANSITION_HANDLE_COW;
case Builtins::kKeyedStoreIC_SloppyArguments_NoTransitionIgnoreOOB:
case Builtins::kStoreInArrayLiteralIC_Slow_NoTransitionIgnoreOOB:
case Builtins::kKeyedStoreIC_Slow_NoTransitionIgnoreOOB:
case Builtins::kStoreFastElementIC_NoTransitionIgnoreOOB:
case Builtins::kElementsTransitionAndStore_NoTransitionIgnoreOOB:
return STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS;
case Builtins::kKeyedStoreIC_SloppyArguments_NoTransitionHandleCOW:
case Builtins::kStoreInArrayLiteralIC_Slow_NoTransitionHandleCOW:
case Builtins::kKeyedStoreIC_Slow_NoTransitionHandleCOW:
case Builtins::kStoreFastElementIC_NoTransitionHandleCOW:
case Builtins::kElementsTransitionAndStore_NoTransitionHandleCOW:
return STORE_NO_TRANSITION_HANDLE_COW;
default:
UNREACHABLE();
}
}
} // namespace
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()) {
const int builtin_index = handler->builtin_index();
if (!BuiltinHasKeyedAccessStoreMode(builtin_index)) continue;
mode = KeyedAccessStoreModeForBuiltin(builtin_index);
break;
}
}
return mode;
}
IcCheckType FeedbackNexus::GetKeyType() const {
DCHECK(IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind()) ||
IsStoreInArrayLiteralICKind(kind()) || IsKeyedHasICKind(kind()));
MaybeObject feedback = GetFeedback();
if (feedback == MaybeObject::FromObject(
*FeedbackVector::MegamorphicSentinel(GetIsolate()))) {
return static_cast<IcCheckType>(
Smi::ToInt(GetFeedbackExtra()->cast<Object>()));
}
return IsPropertyNameFeedback(feedback) ? PROPERTY : ELEMENT;
}
BinaryOperationHint FeedbackNexus::GetBinaryOperationFeedback() const {
DCHECK_EQ(kind(), FeedbackSlotKind::kBinaryOp);
int feedback = GetFeedback().ToSmi().value();
return BinaryOperationHintFromFeedback(feedback);
}
CompareOperationHint FeedbackNexus::GetCompareOperationFeedback() const {
DCHECK_EQ(kind(), FeedbackSlotKind::kCompareOp);
int feedback = GetFeedback().ToSmi().value();
return CompareOperationHintFromFeedback(feedback);
}
ForInHint FeedbackNexus::GetForInFeedback() const {
DCHECK_EQ(kind(), FeedbackSlotKind::kForIn);
int feedback = GetFeedback().ToSmi().value();
return ForInHintFromFeedback(feedback);
}
MaybeHandle<JSObject> FeedbackNexus::GetConstructorFeedback() const {
DCHECK_EQ(kind(), FeedbackSlotKind::kInstanceOf);
Isolate* isolate = GetIsolate();
MaybeObject feedback = GetFeedback();
HeapObject heap_object;
if (feedback->GetHeapObjectIfWeak(&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->GetHeapObjectAssumeStrong()),
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->GetHeapObjectAssumeStrong()),
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->GetHeapObjectAssumeStrong()),
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->GetHeapObjectAssumeStrong()),
isolate));
}
void FeedbackNexus::ResetTypeProfile() {
DCHECK(IsTypeProfileKind(kind()));
SetFeedback(*FeedbackVector::UninitializedSentinel(GetIsolate()));
}
} // namespace internal
} // namespace v8