blob: c51d9877f1c27c4e31fcdb02316a36c1883e2c92 [file] [log] [blame]
// 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