v8/src/transitions.cc
hpayer ad51e8b1e8 [heap] Black allocation.
When black allocation is active, all objects allocated in old space are allocated black. Important: With that change, you cannot assume anymore that new objects are white right after their allocation. Currently, black allocation is enabled when incremental marking is started.

This feature can be turned off via flag: --noblack-allocation

BUG=chromium:561449
LOG=n

Review URL: https://codereview.chromium.org/1420423009

Cr-Commit-Position: refs/heads/master@{#34743}
2016-03-14 14:03:01 +00:00

554 lines
20 KiB
C++

// Copyright 2012 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/transitions.h"
#include "src/objects-inl.h"
#include "src/transitions-inl.h"
#include "src/utils.h"
namespace v8 {
namespace internal {
// static
void TransitionArray::Insert(Handle<Map> map, Handle<Name> name,
Handle<Map> target, SimpleTransitionFlag flag) {
Isolate* isolate = map->GetIsolate();
target->SetBackPointer(*map);
// If the map doesn't have any transitions at all yet, install the new one.
if (CanStoreSimpleTransition(map->raw_transitions())) {
if (flag == SIMPLE_PROPERTY_TRANSITION) {
Handle<WeakCell> cell = Map::WeakCellForMap(target);
ReplaceTransitions(map, *cell);
return;
}
// If the flag requires a full TransitionArray, allocate one.
Handle<TransitionArray> result = Allocate(isolate, 0, 1);
ReplaceTransitions(map, *result);
}
bool is_special_transition = flag == SPECIAL_TRANSITION;
// If the map has a simple transition, check if it should be overwritten.
if (IsSimpleTransition(map->raw_transitions())) {
Map* old_target = GetSimpleTransition(map->raw_transitions());
Name* key = GetSimpleTransitionKey(old_target);
PropertyDetails old_details = GetSimpleTargetDetails(old_target);
PropertyDetails new_details = is_special_transition
? PropertyDetails::Empty()
: GetTargetDetails(*name, *target);
if (flag == SIMPLE_PROPERTY_TRANSITION && key->Equals(*name) &&
old_details.kind() == new_details.kind() &&
old_details.attributes() == new_details.attributes()) {
Handle<WeakCell> cell = Map::WeakCellForMap(target);
ReplaceTransitions(map, *cell);
return;
}
// Otherwise allocate a full TransitionArray with slack for a new entry.
Handle<TransitionArray> result = Allocate(isolate, 1, 1);
// Re-read existing data; the allocation might have caused it to be cleared.
if (IsSimpleTransition(map->raw_transitions())) {
old_target = GetSimpleTransition(map->raw_transitions());
result->Set(0, GetSimpleTransitionKey(old_target), old_target);
} else {
result->SetNumberOfTransitions(0);
}
ReplaceTransitions(map, *result);
}
// At this point, we know that the map has a full TransitionArray.
DCHECK(IsFullTransitionArray(map->raw_transitions()));
int number_of_transitions = 0;
int new_nof = 0;
int insertion_index = kNotFound;
DCHECK_EQ(is_special_transition, IsSpecialTransition(*name));
PropertyDetails details = is_special_transition
? PropertyDetails::Empty()
: GetTargetDetails(*name, *target);
{
DisallowHeapAllocation no_gc;
TransitionArray* array = TransitionArray::cast(map->raw_transitions());
number_of_transitions = array->number_of_transitions();
new_nof = number_of_transitions;
int index =
is_special_transition
? array->SearchSpecial(Symbol::cast(*name), &insertion_index)
: array->Search(details.kind(), *name, details.attributes(),
&insertion_index);
// If an existing entry was found, overwrite it and return.
if (index != kNotFound) {
array->SetTarget(index, *target);
return;
}
++new_nof;
CHECK(new_nof <= kMaxNumberOfTransitions);
DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions);
// If there is enough capacity, insert new entry into the existing array.
if (new_nof <= Capacity(array)) {
array->SetNumberOfTransitions(new_nof);
for (index = number_of_transitions; index > insertion_index; --index) {
array->SetKey(index, array->GetKey(index - 1));
array->SetTarget(index, array->GetTarget(index - 1));
}
array->SetKey(index, *name);
array->SetTarget(index, *target);
SLOW_DCHECK(array->IsSortedNoDuplicates());
return;
}
}
// We're gonna need a bigger TransitionArray.
Handle<TransitionArray> result = Allocate(
map->GetIsolate(), new_nof,
Map::SlackForArraySize(number_of_transitions, kMaxNumberOfTransitions));
// The map's transition array may have shrunk during the allocation above as
// it was weakly traversed, though it is guaranteed not to disappear. Trim the
// result copy if needed, and recompute variables.
DCHECK(IsFullTransitionArray(map->raw_transitions()));
DisallowHeapAllocation no_gc;
TransitionArray* array = TransitionArray::cast(map->raw_transitions());
if (array->number_of_transitions() != number_of_transitions) {
DCHECK(array->number_of_transitions() < number_of_transitions);
number_of_transitions = array->number_of_transitions();
new_nof = number_of_transitions;
insertion_index = kNotFound;
int index =
is_special_transition
? array->SearchSpecial(Symbol::cast(*name), &insertion_index)
: array->Search(details.kind(), *name, details.attributes(),
&insertion_index);
if (index == kNotFound) {
++new_nof;
} else {
insertion_index = index;
}
DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions);
result->Shrink(ToKeyIndex(new_nof));
result->SetNumberOfTransitions(new_nof);
}
if (array->HasPrototypeTransitions()) {
result->SetPrototypeTransitions(array->GetPrototypeTransitions());
}
DCHECK_NE(kNotFound, insertion_index);
for (int i = 0; i < insertion_index; ++i) {
result->Set(i, array->GetKey(i), array->GetTarget(i));
}
result->Set(insertion_index, *name, *target);
for (int i = insertion_index; i < number_of_transitions; ++i) {
result->Set(i + 1, array->GetKey(i), array->GetTarget(i));
}
SLOW_DCHECK(result->IsSortedNoDuplicates());
ReplaceTransitions(map, *result);
}
// static
Map* TransitionArray::SearchTransition(Map* map, PropertyKind kind, Name* name,
PropertyAttributes attributes) {
DCHECK(name->IsUniqueName());
Object* raw_transitions = map->raw_transitions();
if (IsSimpleTransition(raw_transitions)) {
Map* target = GetSimpleTransition(raw_transitions);
Name* key = GetSimpleTransitionKey(target);
if (key != name) return nullptr;
PropertyDetails details = GetSimpleTargetDetails(target);
if (details.attributes() != attributes) return nullptr;
if (details.kind() != kind) return nullptr;
return target;
}
if (IsFullTransitionArray(raw_transitions)) {
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
int transition = transitions->Search(kind, name, attributes);
if (transition == kNotFound) return nullptr;
return transitions->GetTarget(transition);
}
return NULL;
}
// static
Map* TransitionArray::SearchSpecial(Map* map, Symbol* name) {
Object* raw_transitions = map->raw_transitions();
if (IsFullTransitionArray(raw_transitions)) {
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
int transition = transitions->SearchSpecial(name);
if (transition == kNotFound) return NULL;
return transitions->GetTarget(transition);
}
return NULL;
}
// static
Handle<Map> TransitionArray::FindTransitionToField(Handle<Map> map,
Handle<Name> name) {
DCHECK(name->IsUniqueName());
DisallowHeapAllocation no_gc;
Map* target = SearchTransition(*map, kData, *name, NONE);
if (target == NULL) return Handle<Map>::null();
PropertyDetails details = target->GetLastDescriptorDetails();
DCHECK_EQ(NONE, details.attributes());
if (details.type() != DATA) return Handle<Map>::null();
return Handle<Map>(target);
}
// static
Handle<String> TransitionArray::ExpectedTransitionKey(Handle<Map> map) {
DisallowHeapAllocation no_gc;
Object* raw_transition = map->raw_transitions();
if (!IsSimpleTransition(raw_transition)) return Handle<String>::null();
Map* target = GetSimpleTransition(raw_transition);
PropertyDetails details = GetSimpleTargetDetails(target);
if (details.type() != DATA) return Handle<String>::null();
if (details.attributes() != NONE) return Handle<String>::null();
Name* name = GetSimpleTransitionKey(target);
if (!name->IsString()) return Handle<String>::null();
return Handle<String>(String::cast(name));
}
// static
bool TransitionArray::CanHaveMoreTransitions(Handle<Map> map) {
if (map->is_dictionary_map()) return false;
Object* raw_transitions = map->raw_transitions();
if (IsFullTransitionArray(raw_transitions)) {
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
return transitions->number_of_transitions() < kMaxNumberOfTransitions;
}
return true;
}
// static
bool TransitionArray::CompactPrototypeTransitionArray(FixedArray* array) {
const int header = kProtoTransitionHeaderSize;
int number_of_transitions = NumberOfPrototypeTransitions(array);
if (number_of_transitions == 0) {
// Empty array cannot be compacted.
return false;
}
int new_number_of_transitions = 0;
for (int i = 0; i < number_of_transitions; i++) {
Object* cell = array->get(header + i);
if (!WeakCell::cast(cell)->cleared()) {
if (new_number_of_transitions != i) {
array->set(header + new_number_of_transitions, cell);
}
new_number_of_transitions++;
}
}
// Fill slots that became free with undefined value.
for (int i = new_number_of_transitions; i < number_of_transitions; i++) {
array->set_undefined(header + i);
}
if (number_of_transitions != new_number_of_transitions) {
SetNumberOfPrototypeTransitions(array, new_number_of_transitions);
}
return new_number_of_transitions < number_of_transitions;
}
// static
Handle<FixedArray> TransitionArray::GrowPrototypeTransitionArray(
Handle<FixedArray> array, int new_capacity, Isolate* isolate) {
// Grow array by factor 2 up to MaxCachedPrototypeTransitions.
int capacity = array->length() - kProtoTransitionHeaderSize;
new_capacity = Min(kMaxCachedPrototypeTransitions, new_capacity);
DCHECK_GT(new_capacity, capacity);
int grow_by = new_capacity - capacity;
array = isolate->factory()->CopyFixedArrayAndGrow(array, grow_by, TENURED);
if (capacity < 0) {
// There was no prototype transitions array before, so the size
// couldn't be copied. Initialize it explicitly.
SetNumberOfPrototypeTransitions(*array, 0);
}
return array;
}
// static
int TransitionArray::NumberOfPrototypeTransitionsForTest(Map* map) {
FixedArray* transitions = GetPrototypeTransitions(map);
CompactPrototypeTransitionArray(transitions);
return TransitionArray::NumberOfPrototypeTransitions(transitions);
}
// static
void TransitionArray::PutPrototypeTransition(Handle<Map> map,
Handle<Object> prototype,
Handle<Map> target_map) {
DCHECK(HeapObject::cast(*prototype)->map()->IsMap());
// Don't cache prototype transition if this map is either shared, or a map of
// a prototype.
if (map->is_prototype_map()) return;
if (map->is_dictionary_map() || !FLAG_cache_prototype_transitions) return;
const int header = kProtoTransitionHeaderSize;
Handle<WeakCell> target_cell = Map::WeakCellForMap(target_map);
Handle<FixedArray> cache(GetPrototypeTransitions(*map));
int capacity = cache->length() - header;
int transitions = NumberOfPrototypeTransitions(*cache) + 1;
if (transitions > capacity) {
// Grow the array if compacting it doesn't free space.
if (!CompactPrototypeTransitionArray(*cache)) {
if (capacity == kMaxCachedPrototypeTransitions) return;
cache = GrowPrototypeTransitionArray(cache, 2 * transitions,
map->GetIsolate());
SetPrototypeTransitions(map, cache);
}
}
// Reload number of transitions as they might have been compacted.
int last = NumberOfPrototypeTransitions(*cache);
int entry = header + last;
cache->set(entry, *target_cell);
SetNumberOfPrototypeTransitions(*cache, last + 1);
}
// static
Handle<Map> TransitionArray::GetPrototypeTransition(Handle<Map> map,
Handle<Object> prototype) {
DisallowHeapAllocation no_gc;
FixedArray* cache = GetPrototypeTransitions(*map);
int number_of_transitions = NumberOfPrototypeTransitions(cache);
for (int i = 0; i < number_of_transitions; i++) {
WeakCell* target_cell =
WeakCell::cast(cache->get(kProtoTransitionHeaderSize + i));
if (!target_cell->cleared() &&
Map::cast(target_cell->value())->prototype() == *prototype) {
return handle(Map::cast(target_cell->value()));
}
}
return Handle<Map>();
}
// static
FixedArray* TransitionArray::GetPrototypeTransitions(Map* map) {
Object* raw_transitions = map->raw_transitions();
Heap* heap = map->GetHeap();
if (!IsFullTransitionArray(raw_transitions)) {
return heap->empty_fixed_array();
}
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
if (!transitions->HasPrototypeTransitions()) {
return heap->empty_fixed_array();
}
return transitions->GetPrototypeTransitions();
}
// static
void TransitionArray::SetNumberOfPrototypeTransitions(
FixedArray* proto_transitions, int value) {
DCHECK(proto_transitions->length() != 0);
proto_transitions->set(kProtoTransitionNumberOfEntriesOffset,
Smi::FromInt(value));
}
// static
int TransitionArray::NumberOfTransitions(Object* raw_transitions) {
if (CanStoreSimpleTransition(raw_transitions)) return 0;
if (IsSimpleTransition(raw_transitions)) return 1;
// Prototype maps don't have transitions.
if (raw_transitions->IsPrototypeInfo()) return 0;
DCHECK(IsFullTransitionArray(raw_transitions));
return TransitionArray::cast(raw_transitions)->number_of_transitions();
}
// static
int TransitionArray::Capacity(Object* raw_transitions) {
if (!IsFullTransitionArray(raw_transitions)) return 1;
TransitionArray* t = TransitionArray::cast(raw_transitions);
if (t->length() <= kFirstIndex) return 0;
return (t->length() - kFirstIndex) / kTransitionSize;
}
// Private static helper functions.
Handle<TransitionArray> TransitionArray::Allocate(Isolate* isolate,
int number_of_transitions,
int slack) {
Handle<FixedArray> array = isolate->factory()->NewTransitionArray(
LengthFor(number_of_transitions + slack));
array->set(kPrototypeTransitionsIndex, Smi::FromInt(0));
array->set(kTransitionLengthIndex, Smi::FromInt(number_of_transitions));
return Handle<TransitionArray>::cast(array);
}
// static
void TransitionArray::ZapTransitionArray(TransitionArray* transitions) {
// Do not zap the next link that is used by GC.
STATIC_ASSERT(kNextLinkIndex + 1 == kPrototypeTransitionsIndex);
MemsetPointer(transitions->data_start() + kPrototypeTransitionsIndex,
transitions->GetHeap()->the_hole_value(),
transitions->length() - kPrototypeTransitionsIndex);
transitions->SetNumberOfTransitions(0);
}
void TransitionArray::ReplaceTransitions(Handle<Map> map,
Object* new_transitions) {
Object* raw_transitions = map->raw_transitions();
if (IsFullTransitionArray(raw_transitions)) {
TransitionArray* old_transitions = TransitionArray::cast(raw_transitions);
#ifdef DEBUG
CheckNewTransitionsAreConsistent(map, old_transitions, new_transitions);
DCHECK(old_transitions != new_transitions);
#endif
// Transition arrays are not shared. When one is replaced, it should not
// keep referenced objects alive, so we zap it.
// When there is another reference to the array somewhere (e.g. a handle),
// not zapping turns from a waste of memory into a source of crashes.
ZapTransitionArray(old_transitions);
}
map->set_raw_transitions(new_transitions);
}
void TransitionArray::SetPrototypeTransitions(
Handle<Map> map, Handle<FixedArray> proto_transitions) {
EnsureHasFullTransitionArray(map);
TransitionArray* transitions = TransitionArray::cast(map->raw_transitions());
transitions->SetPrototypeTransitions(*proto_transitions);
}
void TransitionArray::EnsureHasFullTransitionArray(Handle<Map> map) {
Object* raw_transitions = map->raw_transitions();
if (IsFullTransitionArray(raw_transitions)) return;
int nof = IsSimpleTransition(raw_transitions) ? 1 : 0;
Handle<TransitionArray> result = Allocate(map->GetIsolate(), nof);
DisallowHeapAllocation no_gc;
// Reload pointer after the allocation that just happened.
raw_transitions = map->raw_transitions();
int new_nof = IsSimpleTransition(raw_transitions) ? 1 : 0;
if (new_nof != nof) {
DCHECK(new_nof == 0);
result->Shrink(ToKeyIndex(0));
result->SetNumberOfTransitions(0);
} else if (nof == 1) {
Map* target = GetSimpleTransition(raw_transitions);
Name* key = GetSimpleTransitionKey(target);
result->Set(0, key, target);
}
ReplaceTransitions(map, *result);
}
void TransitionArray::TraverseTransitionTreeInternal(Map* map,
TraverseCallback callback,
void* data) {
Object* raw_transitions = map->raw_transitions();
if (IsFullTransitionArray(raw_transitions)) {
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
if (transitions->HasPrototypeTransitions()) {
FixedArray* proto_trans = transitions->GetPrototypeTransitions();
for (int i = 0; i < NumberOfPrototypeTransitions(proto_trans); ++i) {
int index = TransitionArray::kProtoTransitionHeaderSize + i;
WeakCell* cell = WeakCell::cast(proto_trans->get(index));
if (!cell->cleared()) {
TraverseTransitionTreeInternal(Map::cast(cell->value()), callback,
data);
}
}
}
for (int i = 0; i < transitions->number_of_transitions(); ++i) {
TraverseTransitionTreeInternal(transitions->GetTarget(i), callback, data);
}
} else if (IsSimpleTransition(raw_transitions)) {
TraverseTransitionTreeInternal(GetSimpleTransition(raw_transitions),
callback, data);
}
callback(map, data);
}
#ifdef DEBUG
void TransitionArray::CheckNewTransitionsAreConsistent(
Handle<Map> map, TransitionArray* old_transitions, Object* transitions) {
// This function only handles full transition arrays.
DCHECK(IsFullTransitionArray(transitions));
TransitionArray* new_transitions = TransitionArray::cast(transitions);
for (int i = 0; i < old_transitions->number_of_transitions(); i++) {
Map* target = old_transitions->GetTarget(i);
if (target->instance_descriptors() == map->instance_descriptors()) {
Name* key = old_transitions->GetKey(i);
int new_target_index;
if (TransitionArray::IsSpecialTransition(key)) {
new_target_index = new_transitions->SearchSpecial(Symbol::cast(key));
} else {
PropertyDetails details =
TransitionArray::GetTargetDetails(key, target);
new_target_index =
new_transitions->Search(details.kind(), key, details.attributes());
}
DCHECK_NE(TransitionArray::kNotFound, new_target_index);
DCHECK_EQ(target, new_transitions->GetTarget(new_target_index));
}
}
}
#endif
// Private non-static helper functions (operating on full transition arrays).
int TransitionArray::SearchDetails(int transition, PropertyKind kind,
PropertyAttributes attributes,
int* out_insertion_index) {
int nof_transitions = number_of_transitions();
DCHECK(transition < nof_transitions);
Name* key = GetKey(transition);
for (; transition < nof_transitions && GetKey(transition) == key;
transition++) {
Map* target = GetTarget(transition);
PropertyDetails target_details = GetTargetDetails(key, target);
int cmp = CompareDetails(kind, attributes, target_details.kind(),
target_details.attributes());
if (cmp == 0) {
return transition;
} else if (cmp < 0) {
break;
}
}
if (out_insertion_index != NULL) *out_insertion_index = transition;
return kNotFound;
}
int TransitionArray::Search(PropertyKind kind, Name* name,
PropertyAttributes attributes,
int* out_insertion_index) {
int transition = SearchName(name, out_insertion_index);
if (transition == kNotFound) return kNotFound;
return SearchDetails(transition, kind, attributes, out_insertion_index);
}
} // namespace internal
} // namespace v8