Commit 233ea0ee authored by jkummerow's avatar jkummerow Committed by Commit bot

Reland: Simplify and compact transitions storage

Original issue: https://codereview.chromium.org/980573002/

Simple transitions are now stored in a map's "transitions" field (as a WeakCell wrapping the target map); full TransitionArrays are used when that's not sufficient.
To encapsulate these storage format implementation details, functions for manipulating and querying transitions have been refactored to be static functions on the TransitionArray class, and take maps as inputs.

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

Cr-Commit-Position: refs/heads/master@{#27044}
parent d8416f55
......@@ -1286,28 +1286,31 @@ void V8HeapExplorer::ExtractContextReferences(int entry, Context* context) {
void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
if (map->HasTransitionArray()) {
TransitionArray* transitions = map->transitions();
Object* raw_transitions = map->raw_transitions();
if (TransitionArray::IsFullTransitionArray(raw_transitions)) {
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
int transitions_entry = GetEntry(transitions)->index();
if (FLAG_collect_maps && map->CanTransition()) {
if (!transitions->IsSimpleTransition()) {
if (transitions->HasPrototypeTransitions()) {
FixedArray* prototype_transitions =
transitions->GetPrototypeTransitions();
MarkAsWeakContainer(prototype_transitions);
TagObject(prototype_transitions, "(prototype transitions");
SetInternalReference(transitions, transitions_entry,
"prototype_transitions", prototype_transitions);
}
// TODO(alph): transitions keys are strong links.
MarkAsWeakContainer(transitions);
if (transitions->HasPrototypeTransitions()) {
FixedArray* prototype_transitions =
transitions->GetPrototypeTransitions();
MarkAsWeakContainer(prototype_transitions);
TagObject(prototype_transitions, "(prototype transitions");
SetInternalReference(transitions, transitions_entry,
"prototype_transitions", prototype_transitions);
}
// TODO(alph): transitions keys are strong links.
MarkAsWeakContainer(transitions);
}
TagObject(transitions, "(transition array)");
SetInternalReference(map, entry, "transitions", transitions,
Map::kTransitionsOffset);
} else if (TransitionArray::IsSimpleTransition(raw_transitions)) {
TagObject(raw_transitions, "(transition)");
SetInternalReference(map, entry, "transition", raw_transitions,
Map::kTransitionsOffset);
}
DescriptorArray* descriptors = map->instance_descriptors();
TagObject(descriptors, "(map descriptors)");
......
......@@ -2477,7 +2477,7 @@ AllocationResult Heap::AllocateMap(InstanceType instance_type,
map->set_dependent_code(DependentCode::cast(empty_fixed_array()),
SKIP_WRITE_BARRIER);
map->set_weak_cell_cache(Smi::FromInt(0));
map->init_transitions(undefined_value());
map->set_raw_transitions(Smi::FromInt(0));
map->set_unused_property_fields(0);
map->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
......@@ -2611,7 +2611,7 @@ bool Heap::CreateInitialMaps() {
// Fix the instance_descriptors for the existing maps.
meta_map()->set_code_cache(empty_fixed_array());
meta_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
meta_map()->init_transitions(undefined_value());
meta_map()->set_raw_transitions(Smi::FromInt(0));
meta_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
meta_map()->set_layout_descriptor(LayoutDescriptor::FastPointerLayout());
......@@ -2620,7 +2620,7 @@ bool Heap::CreateInitialMaps() {
fixed_array_map()->set_code_cache(empty_fixed_array());
fixed_array_map()->set_dependent_code(
DependentCode::cast(empty_fixed_array()));
fixed_array_map()->init_transitions(undefined_value());
fixed_array_map()->set_raw_transitions(Smi::FromInt(0));
fixed_array_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
fixed_array_map()->set_layout_descriptor(
......@@ -2629,7 +2629,7 @@ bool Heap::CreateInitialMaps() {
undefined_map()->set_code_cache(empty_fixed_array());
undefined_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
undefined_map()->init_transitions(undefined_value());
undefined_map()->set_raw_transitions(Smi::FromInt(0));
undefined_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
undefined_map()->set_layout_descriptor(
......@@ -2638,7 +2638,7 @@ bool Heap::CreateInitialMaps() {
null_map()->set_code_cache(empty_fixed_array());
null_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
null_map()->init_transitions(undefined_value());
null_map()->set_raw_transitions(Smi::FromInt(0));
null_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
null_map()->set_layout_descriptor(LayoutDescriptor::FastPointerLayout());
......@@ -2647,7 +2647,7 @@ bool Heap::CreateInitialMaps() {
constant_pool_array_map()->set_code_cache(empty_fixed_array());
constant_pool_array_map()->set_dependent_code(
DependentCode::cast(empty_fixed_array()));
constant_pool_array_map()->init_transitions(undefined_value());
constant_pool_array_map()->set_raw_transitions(Smi::FromInt(0));
constant_pool_array_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
constant_pool_array_map()->set_layout_descriptor(
......
......@@ -1470,8 +1470,9 @@ class MarkCompactMarkingVisitor::ObjectStatsTracker<
heap->RecordFixedArraySubTypeStats(DESCRIPTOR_ARRAY_SUB_TYPE,
fixed_array_size);
}
if (map_obj->HasTransitionArray()) {
int fixed_array_size = map_obj->transitions()->Size();
if (TransitionArray::IsFullTransitionArray(map_obj->raw_transitions())) {
int fixed_array_size =
TransitionArray::cast(map_obj->raw_transitions())->Size();
heap->RecordFixedArraySubTypeStats(TRANSITION_ARRAY_SUB_TYPE,
fixed_array_size);
}
......@@ -2406,10 +2407,12 @@ void MarkCompactCollector::ClearNonLiveReferences() {
void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) {
int number_of_transitions = map->NumberOfProtoTransitions();
FixedArray* prototype_transitions = map->GetPrototypeTransitions();
FixedArray* prototype_transitions =
TransitionArray::GetPrototypeTransitions(map);
int number_of_transitions =
TransitionArray::NumberOfPrototypeTransitions(prototype_transitions);
const int header = Map::kProtoTransitionHeaderSize;
const int header = TransitionArray::kProtoTransitionHeaderSize;
int new_number_of_transitions = 0;
for (int i = 0; i < number_of_transitions; i++) {
Object* cached_map = prototype_transitions->get(header + i);
......@@ -2423,7 +2426,8 @@ void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) {
}
if (new_number_of_transitions != number_of_transitions) {
map->SetNumberOfProtoTransitions(new_number_of_transitions);
TransitionArray::SetNumberOfPrototypeTransitions(prototype_transitions,
new_number_of_transitions);
}
// Fill slots that became free with undefined value.
......@@ -2444,7 +2448,7 @@ void MarkCompactCollector::ClearNonLiveMapTransitions(Map* map,
bool current_is_alive = map_mark.Get();
bool parent_is_alive = Marking::MarkBitFrom(parent).Get();
if (!current_is_alive && parent_is_alive) {
ClearMapTransitions(parent);
ClearMapTransitions(parent, map);
}
}
......@@ -2458,28 +2462,43 @@ bool MarkCompactCollector::ClearMapBackPointer(Map* target) {
}
void MarkCompactCollector::ClearMapTransitions(Map* map) {
// If there are no transitions to be cleared, return.
// TODO(verwaest) Should be an assert, otherwise back pointers are not
// properly cleared.
if (!map->HasTransitionArray()) return;
void MarkCompactCollector::ClearMapTransitions(Map* map, Map* dead_transition) {
Object* transitions = map->raw_transitions();
int num_transitions = TransitionArray::NumberOfTransitions(transitions);
TransitionArray* t = map->transitions();
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
DescriptorArray* descriptors = map->instance_descriptors();
// A previously existing simple transition (stored in a WeakCell) may have
// been cleared. Clear the useless cell pointer, and take ownership
// of the descriptor array.
if (transitions->IsWeakCell() && WeakCell::cast(transitions)->cleared()) {
map->set_raw_transitions(Smi::FromInt(0));
}
if (num_transitions == 0 &&
descriptors == dead_transition->instance_descriptors() &&
number_of_own_descriptors > 0) {
TrimDescriptorArray(map, descriptors, number_of_own_descriptors);
DCHECK(descriptors->number_of_descriptors() == number_of_own_descriptors);
map->set_owns_descriptors(true);
return;
}
int transition_index = 0;
DescriptorArray* descriptors = map->instance_descriptors();
bool descriptors_owner_died = false;
// Compact all live descriptors to the left.
for (int i = 0; i < t->number_of_transitions(); ++i) {
Map* target = t->GetTarget(i);
for (int i = 0; i < num_transitions; ++i) {
Map* target = TransitionArray::GetTarget(transitions, i);
if (ClearMapBackPointer(target)) {
if (target->instance_descriptors() == descriptors) {
descriptors_owner_died = true;
}
} else {
if (i != transition_index) {
DCHECK(TransitionArray::IsFullTransitionArray(transitions));
TransitionArray* t = TransitionArray::cast(transitions);
Name* key = t->GetKey(i);
t->SetKey(transition_index, key);
Object** key_slot = t->GetKeySlot(transition_index);
......@@ -2494,9 +2513,7 @@ void MarkCompactCollector::ClearMapTransitions(Map* map) {
// If there are no transitions to be cleared, return.
// TODO(verwaest) Should be an assert, otherwise back pointers are not
// properly cleared.
if (transition_index == t->number_of_transitions()) return;
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
if (transition_index == num_transitions) return;
if (descriptors_owner_died) {
if (number_of_own_descriptors > 0) {
......@@ -2512,14 +2529,17 @@ void MarkCompactCollector::ClearMapTransitions(Map* map) {
// such that number_of_transitions() == 0. If this assumption changes,
// TransitionArray::Insert() will need to deal with the case that a transition
// array disappeared during GC.
int trim = t->number_of_transitions_storage() - transition_index;
int trim = TransitionArray::Capacity(transitions) - transition_index;
if (trim > 0) {
// Non-full-TransitionArray cases can never reach this point.
DCHECK(TransitionArray::IsFullTransitionArray(transitions));
TransitionArray* t = TransitionArray::cast(transitions);
heap_->RightTrimFixedArray<Heap::FROM_GC>(
t, t->IsSimpleTransition() ? trim
: trim * TransitionArray::kTransitionSize);
t, trim * TransitionArray::kTransitionSize);
t->SetNumberOfTransitions(transition_index);
// The map still has a full transition array.
DCHECK(TransitionArray::IsFullTransitionArray(map->raw_transitions()));
}
DCHECK(map->HasTransitionArray());
}
......@@ -4231,7 +4251,7 @@ void MarkCompactCollector::SweepSpaces() {
EvacuateNewSpaceAndCandidates();
// ClearNonLiveTransitions depends on precise sweeping of map space to
// ClearNonLiveReferences depends on precise sweeping of map space to
// detect whether unmarked map became dead in this collection or in one
// of the previous ones.
{
......
......@@ -812,7 +812,7 @@ class MarkCompactCollector {
void ClearNonLiveReferences();
void ClearNonLivePrototypeTransitions(Map* map);
void ClearNonLiveMapTransitions(Map* map, MarkBit map_mark);
void ClearMapTransitions(Map* map);
void ClearMapTransitions(Map* map, Map* dead_transition);
bool ClearMapBackPointer(Map* map);
void TrimDescriptorArray(Map* map, DescriptorArray* descriptors,
int number_of_own_descriptors);
......
......@@ -584,18 +584,13 @@ void StaticMarkingVisitor<StaticVisitor>::VisitJSDataView(Map* map,
template <typename StaticVisitor>
void StaticMarkingVisitor<StaticVisitor>::MarkMapContents(Heap* heap,
Map* map) {
// Make sure that the back pointer stored either in the map itself or
// inside its transitions array is marked. Skip recording the back
// pointer slot since map space is not compacted.
StaticVisitor::MarkObject(heap, HeapObject::cast(map->GetBackPointer()));
// Treat pointers in the transitions array as weak and also mark that
// array to prevent visiting it later. Skip recording the transition
// array slot, since it will be implicitly recorded when the pointer
// fields of this map are visited.
if (map->HasTransitionArray()) {
TransitionArray* transitions = map->transitions();
MarkTransitionArray(heap, transitions);
Object* raw_transitions = map->raw_transitions();
if (TransitionArray::IsSimpleTransition(raw_transitions)) {
StaticVisitor::VisitPointer(
heap, HeapObject::RawField(map, Map::kTransitionsOffset));
}
if (TransitionArray::IsFullTransitionArray(raw_transitions)) {
MarkTransitionArray(heap, TransitionArray::cast(raw_transitions));
}
// Since descriptor arrays are potentially shared, ensure that only the
......@@ -631,20 +626,18 @@ void StaticMarkingVisitor<StaticVisitor>::MarkTransitionArray(
Heap* heap, TransitionArray* transitions) {
if (!StaticVisitor::MarkObjectWithoutPush(heap, transitions)) return;
// Simple transitions do not have keys nor prototype transitions.
if (transitions->IsSimpleTransition()) return;
if (transitions->HasPrototypeTransitions()) {
// Mark prototype transitions array but do not push it onto marking
// stack, this will make references from it weak. We will clean dead
// prototype transitions in ClearNonLiveTransitions.
// prototype transitions in ClearNonLiveReferences.
Object** slot = transitions->GetPrototypeTransitionsSlot();
HeapObject* obj = HeapObject::cast(*slot);
heap->mark_compact_collector()->RecordSlot(slot, slot, obj);
StaticVisitor::MarkObjectWithoutPush(heap, obj);
}
for (int i = 0; i < transitions->number_of_transitions(); ++i) {
int num_transitions = TransitionArray::NumberOfTransitions(transitions);
for (int i = 0; i < num_transitions; ++i) {
StaticVisitor::VisitPointer(heap, transitions->GetKeySlot(i));
}
}
......
......@@ -2553,10 +2553,11 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
number_ = number;
}
void LookupTransition(Map* map, Name* name, PropertyAttributes attributes) {
int transition_index = map->SearchTransition(kData, name, attributes);
if (transition_index == TransitionArray::kNotFound) return NotFound();
Map* target =
TransitionArray::SearchTransition(map, kData, name, attributes);
if (target == NULL) return NotFound();
lookup_type_ = TRANSITION_TYPE;
transition_ = handle(map->GetTransition(transition_index));
transition_ = handle(target);
number_ = transition_->LastAdded();
details_ = transition_->instance_descriptors()->GetDetails(number_);
}
......
......@@ -360,19 +360,19 @@ Handle<Object> JsonParser<seq_one_byte>::ParseJsonObject() {
bool follow_expected = false;
Handle<Map> target;
if (seq_one_byte) {
key = Map::ExpectedTransitionKey(map);
key = TransitionArray::ExpectedTransitionKey(map);
follow_expected = !key.is_null() && ParseJsonString(key);
}
// If the expected transition hits, follow it.
if (follow_expected) {
target = Map::ExpectedTransitionTarget(map);
target = TransitionArray::ExpectedTransitionTarget(map);
} else {
// If the expected transition failed, parse an internalized string and
// try to find a matching transition.
key = ParseJsonInternalizedString();
if (key.is_null()) return ReportUnexpectedCharacter();
target = Map::FindTransitionToField(map, key);
target = TransitionArray::FindTransitionToField(map, key);
// If a transition was found, follow it and continue.
transitioning = !target.is_null();
}
......
......@@ -320,10 +320,8 @@ void Map::MapVerify() {
VerifyHeapPointer(prototype());
VerifyHeapPointer(instance_descriptors());
SLOW_DCHECK(instance_descriptors()->IsSortedNoDuplicates());
if (HasTransitionArray()) {
SLOW_DCHECK(transitions()->IsSortedNoDuplicates());
SLOW_DCHECK(transitions()->IsConsistentWithBackPointers(this));
}
SLOW_DCHECK(TransitionArray::IsSortedNoDuplicates(this));
SLOW_DCHECK(TransitionArray::IsConsistentWithBackPointers(this));
// TODO(ishell): turn it back to SLOW_DCHECK.
CHECK(!FLAG_unbox_double_fields ||
layout_descriptor()->IsConsistentWithMap(this));
......@@ -344,7 +342,6 @@ void Map::VerifyOmittedMapChecks() {
if (!FLAG_omit_map_checks_for_leaf_maps) return;
if (!is_stable() ||
is_deprecated() ||
HasTransitionArray() ||
is_dictionary_map()) {
CHECK_EQ(0, dependent_code()->number_of_entries(
DependentCode::kPrototypeCheckGroup));
......@@ -1208,14 +1205,28 @@ bool TransitionArray::IsSortedNoDuplicates(int valid_entries) {
}
// static
bool TransitionArray::IsSortedNoDuplicates(Map* map) {
Object* raw_transitions = map->raw_transitions();
if (IsFullTransitionArray(raw_transitions)) {
return TransitionArray::cast(raw_transitions)->IsSortedNoDuplicates();
}
// Simple and non-existent transitions are always sorted.
return true;
}
static bool CheckOneBackPointer(Map* current_map, Object* target) {
return !target->IsMap() || Map::cast(target)->GetBackPointer() == current_map;
}
bool TransitionArray::IsConsistentWithBackPointers(Map* current_map) {
for (int i = 0; i < number_of_transitions(); ++i) {
if (!CheckOneBackPointer(current_map, GetTarget(i))) return false;
// static
bool TransitionArray::IsConsistentWithBackPointers(Map* map) {
Object* transitions = map->raw_transitions();
for (int i = 0; i < TransitionArray::NumberOfTransitions(transitions); ++i) {
Map* target = TransitionArray::GetTarget(transitions, i);
if (!CheckOneBackPointer(map, target)) return false;
}
return true;
}
......
......@@ -1880,41 +1880,6 @@ void JSObject::initialize_elements() {
}
Handle<String> Map::ExpectedTransitionKey(Handle<Map> map) {
DisallowHeapAllocation no_gc;
if (!map->HasTransitionArray()) return Handle<String>::null();
TransitionArray* transitions = map->transitions();
if (!transitions->IsSimpleTransition()) return Handle<String>::null();
int transition = TransitionArray::kSimpleTransitionIndex;
PropertyDetails details = transitions->GetTargetDetails(transition);
Name* name = transitions->GetKey(transition);
if (details.type() != DATA) return Handle<String>::null();
if (details.attributes() != NONE) return Handle<String>::null();
if (!name->IsString()) return Handle<String>::null();
return Handle<String>(String::cast(name));
}
Handle<Map> Map::ExpectedTransitionTarget(Handle<Map> map) {
DCHECK(!ExpectedTransitionKey(map).is_null());
return Handle<Map>(map->transitions()->GetTarget(
TransitionArray::kSimpleTransitionIndex));
}
Handle<Map> Map::FindTransitionToField(Handle<Map> map, Handle<Name> key) {
DisallowHeapAllocation no_allocation;
if (!map->HasTransitionArray()) return Handle<Map>::null();
TransitionArray* transitions = map->transitions();
int transition = transitions->Search(kData, *key, NONE);
if (transition == TransitionArray::kNotFound) return Handle<Map>::null();
PropertyDetails details = transitions->GetTargetDetails(transition);
if (details.type() != DATA) return Handle<Map>::null();
DCHECK_EQ(NONE, details.attributes());
return Handle<Map>(transitions->GetTarget(transition));
}
ACCESSORS(Oddball, to_string, String, kToStringOffset)
ACCESSORS(Oddball, to_number, Object, kToNumberOffset)
......@@ -3098,7 +3063,7 @@ FixedArrayBase* Map::GetInitialElements() {
return empty_array;
} else if (has_fixed_typed_array_elements()) {
FixedTypedArrayBase* empty_array =
GetHeap()->EmptyFixedTypedArrayForMap(this);
GetHeap()->EmptyFixedTypedArrayForMap(this);
DCHECK(!GetHeap()->InNewSpace(empty_array));
return empty_array;
} else {
......@@ -5317,22 +5282,6 @@ void Map::set_prototype(Object* value, WriteBarrierMode mode) {
}
// If the descriptor is using the empty transition array, install a new empty
// transition array that will have place for an element transition.
static void EnsureHasTransitionArray(Handle<Map> map) {
Handle<TransitionArray> transitions;
if (!map->HasTransitionArray()) {
transitions = TransitionArray::Allocate(map->GetIsolate(), 0);
transitions->set_back_pointer_storage(map->GetBackPointer());
} else if (!map->transitions()->IsFullTransitionArray()) {
transitions = TransitionArray::ExtendToFullTransitionArray(map);
} else {
return;
}
map->set_transitions(*transitions);
}
LayoutDescriptor* Map::layout_descriptor_gc_safe() {
Object* layout_desc = READ_FIELD(this, kLayoutDecriptorOffset);
return LayoutDescriptor::cast_gc_safe(layout_desc);
......@@ -5435,127 +5384,13 @@ Object* Map::GetBackPointer() {
}
bool Map::HasElementsTransition() {
return HasTransitionArray() && transitions()->HasElementsTransition();
}
bool Map::HasTransitionArray() const {
Object* object = READ_FIELD(this, kTransitionsOffset);
return object->IsTransitionArray();
}
Map* Map::elements_transition_map() {
int index =
transitions()->SearchSpecial(GetHeap()->elements_transition_symbol());
return transitions()->GetTarget(index);
}
bool Map::CanHaveMoreTransitions() {
if (!HasTransitionArray()) return true;
return transitions()->number_of_transitions() <
TransitionArray::kMaxNumberOfTransitions;
}
Map* Map::GetTransition(int transition_index) {
return transitions()->GetTarget(transition_index);
}
int Map::SearchSpecialTransition(Symbol* name) {
if (HasTransitionArray()) {
return transitions()->SearchSpecial(name);
}
return TransitionArray::kNotFound;
}
int Map::SearchTransition(PropertyKind kind, Name* name,
PropertyAttributes attributes) {
if (HasTransitionArray()) {
return transitions()->Search(kind, name, attributes);
}
return TransitionArray::kNotFound;
}
FixedArray* Map::GetPrototypeTransitions() {
if (!HasTransitionArray()) return GetHeap()->empty_fixed_array();
if (!transitions()->HasPrototypeTransitions()) {
return GetHeap()->empty_fixed_array();
}
return transitions()->GetPrototypeTransitions();
}
void Map::SetPrototypeTransitions(
Handle<Map> map, Handle<FixedArray> proto_transitions) {
EnsureHasTransitionArray(map);
int old_number_of_transitions = map->NumberOfProtoTransitions();
if (Heap::ShouldZapGarbage() && map->HasPrototypeTransitions()) {
DCHECK(map->GetPrototypeTransitions() != *proto_transitions);
map->ZapPrototypeTransitions();
}
map->transitions()->SetPrototypeTransitions(*proto_transitions);
map->SetNumberOfProtoTransitions(old_number_of_transitions);
}
bool Map::HasPrototypeTransitions() {
return HasTransitionArray() && transitions()->HasPrototypeTransitions();
}
TransitionArray* Map::transitions() const {
DCHECK(HasTransitionArray());
Object* object = READ_FIELD(this, kTransitionsOffset);
return TransitionArray::cast(object);
Map* Map::ElementsTransitionMap() {
return TransitionArray::SearchSpecial(
this, GetHeap()->elements_transition_symbol());
}
void Map::set_transitions(TransitionArray* transition_array,
WriteBarrierMode mode) {
// 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.
if (HasTransitionArray()) {
#ifdef DEBUG
for (int i = 0; i < transitions()->number_of_transitions(); i++) {
Map* target = transitions()->GetTarget(i);
if (target->instance_descriptors() == instance_descriptors()) {
Name* key = transitions()->GetKey(i);
int new_target_index;
if (TransitionArray::IsSpecialTransition(key)) {
new_target_index = transition_array->SearchSpecial(Symbol::cast(key));
} else {
PropertyDetails details =
TransitionArray::GetTargetDetails(key, target);
new_target_index = transition_array->Search(details.kind(), key,
details.attributes());
}
DCHECK_NE(TransitionArray::kNotFound, new_target_index);
DCHECK_EQ(target, transition_array->GetTarget(new_target_index));
}
}
#endif
DCHECK(transitions() != transition_array);
ZapTransitions();
}
WRITE_FIELD(this, kTransitionsOffset, transition_array);
CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kTransitionsOffset,
transition_array, mode);
}
void Map::init_transitions(Object* undefined) {
DCHECK(undefined->IsUndefined());
WRITE_FIELD(this, kTransitionsOffset, undefined);
}
ACCESSORS(Map, raw_transitions, Object, kTransitionsOffset)
void Map::SetBackPointer(Object* value, WriteBarrierMode mode) {
......@@ -5574,6 +5409,7 @@ ACCESSORS(Map, weak_cell_cache, Object, kWeakCellCacheOffset)
ACCESSORS(Map, constructor_or_backpointer, Object,
kConstructorOrBackPointerOffset)
Object* Map::GetConstructor() const {
Object* maybe_constructor = constructor_or_backpointer();
// Follow any back pointers.
......@@ -5583,12 +5419,15 @@ Object* Map::GetConstructor() const {
}
return maybe_constructor;
}
void Map::SetConstructor(Object* constructor, WriteBarrierMode mode) {
// Never overwrite a back pointer with a constructor.
DCHECK(!constructor_or_backpointer()->IsMap());
set_constructor_or_backpointer(constructor, mode);
}
ACCESSORS(JSFunction, shared, SharedFunctionInfo, kSharedFunctionInfoOffset)
ACCESSORS(JSFunction, literals_or_bindings, FixedArray, kLiteralsOffset)
ACCESSORS(JSFunction, next_function_link, Object, kNextFunctionLinkOffset)
......
......@@ -432,8 +432,9 @@ void Map::MapPrint(std::ostream& os) { // NOLINT
if (FLAG_unbox_double_fields) {
os << "\n - layout descriptor: " << Brief(layout_descriptor());
}
if (HasTransitionArray()) {
os << "\n - transitions: " << Brief(transitions());
if (TransitionArray::NumberOfTransitions(raw_transitions()) > 0) {
os << "\n - transitions: ";
TransitionArray::PrintTransitions(os, raw_transitions());
}
os << "\n - prototype: " << Brief(prototype());
os << "\n - constructor: " << Brief(GetConstructor());
......@@ -1138,19 +1139,20 @@ void DescriptorArray::PrintDescriptors(std::ostream& os) { // NOLINT
void TransitionArray::Print() {
OFStream os(stdout);
this->PrintTransitions(os);
TransitionArray::PrintTransitions(os, this);
os << std::flush;
}
void TransitionArray::PrintTransitions(std::ostream& os,
void TransitionArray::PrintTransitions(std::ostream& os, Object* transitions,
bool print_header) { // NOLINT
int num_transitions = NumberOfTransitions(transitions);
if (print_header) {
os << "Transition array " << number_of_transitions() << "\n";
os << "Transition array " << num_transitions << "\n";
}
for (int i = 0; i < number_of_transitions(); i++) {
Name* key = GetKey(i);
Map* target = GetTarget(i);
for (int i = 0; i < num_transitions; i++) {
Name* key = GetKey(transitions, i);
Map* target = GetTarget(transitions, i);
os << " ";
#ifdef OBJECT_PRINT
key->NamePrint(os);
......@@ -1158,16 +1160,17 @@ void TransitionArray::PrintTransitions(std::ostream& os,
key->ShortPrint(os);
#endif
os << ": ";
if (key == GetHeap()->nonextensible_symbol()) {
Heap* heap = key->GetHeap();
if (key == heap->nonextensible_symbol()) {
os << " (transition to non-extensible)";
} else if (key == GetHeap()->sealed_symbol()) {
} else if (key == heap->sealed_symbol()) {
os << " (transition to sealed)";
} else if (key == GetHeap()->frozen_symbol()) {
} else if (key == heap->frozen_symbol()) {
os << " (transition to frozen)";
} else if (key == GetHeap()->elements_transition_symbol()) {
} else if (key == heap->elements_transition_symbol()) {
os << " (transition to " << ElementsKindToString(target->elements_kind())
<< ")";
} else if (key == GetHeap()->observed_symbol()) {
} else if (key == heap->observed_symbol()) {
os << " (transition to Object.observe)";
} else {
PropertyDetails details = GetTargetDetails(key, target);
......@@ -1177,7 +1180,9 @@ void TransitionArray::PrintTransitions(std::ostream& os,
}
os << (details.kind() == kData ? "data" : "accessor");
if (details.location() == kDescriptor) {
os << " " << Brief(GetTargetValue(i));
Object* value =
target->instance_descriptors()->GetValue(target->LastAdded());
os << " " << Brief(value);
}
os << "), attrs: " << details.attributes();
}
......@@ -1187,8 +1192,7 @@ void TransitionArray::PrintTransitions(std::ostream& os,
void JSObject::PrintTransitions(std::ostream& os) { // NOLINT
if (!map()->HasTransitionArray()) return;
map()->transitions()->PrintTransitions(os, false);
TransitionArray::PrintTransitions(os, map()->raw_transitions());
}
#endif // defined(DEBUG) || defined(OBJECT_PRINT)
} } // namespace v8::internal
This diff is collapsed.
......@@ -5892,26 +5892,14 @@ class Map: public HeapObject {
// map with DICTIONARY_ELEMENTS was found in the prototype chain.
bool DictionaryElementsInPrototypeChainOnly();
inline bool HasTransitionArray() const;
inline bool HasElementsTransition();
inline Map* elements_transition_map();
inline Map* ElementsTransitionMap();
inline Map* GetTransition(int transition_index);
inline int SearchSpecialTransition(Symbol* name);
inline int SearchTransition(PropertyKind kind, Name* name,
PropertyAttributes attributes);
inline FixedArrayBase* GetInitialElements();
DECL_ACCESSORS(transitions, TransitionArray)
inline void init_transitions(Object* undefined);
static inline Handle<String> ExpectedTransitionKey(Handle<Map> map);
static inline Handle<Map> ExpectedTransitionTarget(Handle<Map> map);
// Try to follow an existing transition to a field with attributes NONE. The
// return value indicates whether the transition was successful.
static inline Handle<Map> FindTransitionToField(Handle<Map> map,
Handle<Name> key);
// [raw_transitions]: Provides access to the transitions storage field.
// Don't call set_raw_transitions() directly to overwrite transitions, use
// the TransitionArray::ReplaceTransitions() wrapper instead!
DECL_ACCESSORS(raw_transitions, Object)
Map* FindRootMap();
Map* FindFieldOwner(int descriptor);
......@@ -5985,6 +5973,8 @@ class Map: public HeapObject {
inline Object* GetConstructor() const;
inline void SetConstructor(Object* constructor,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
// [back pointer]: points back to the parent map from which a transition
// leads to this map. The field overlaps with the constructor (see above).
inline Object* GetBackPointer();
inline void SetBackPointer(Object* value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
......@@ -6017,38 +6007,8 @@ class Map: public HeapObject {
// [weak cell cache]: cache that stores a weak cell pointing to this map.
DECL_ACCESSORS(weak_cell_cache, Object)
// [prototype transitions]: cache of prototype transitions.
// Prototype transition is a transition that happens
// when we change object's prototype to a new one.
// Cache format:
// 0: finger - index of the first free cell in the cache
// 1 + i: target map
inline FixedArray* GetPrototypeTransitions();
inline bool HasPrototypeTransitions();
static const int kProtoTransitionNumberOfEntriesOffset = 0;
static const int kProtoTransitionHeaderSize = 1;
inline int NumberOfProtoTransitions() {
FixedArray* cache = GetPrototypeTransitions();
if (cache->length() == 0) return 0;
return
Smi::cast(cache->get(kProtoTransitionNumberOfEntriesOffset))->value();
}
inline void SetNumberOfProtoTransitions(int value) {
FixedArray* cache = GetPrototypeTransitions();
DCHECK(cache->length() != 0);
cache->set(kProtoTransitionNumberOfEntriesOffset, Smi::FromInt(value));
}
inline PropertyDetails GetLastDescriptorDetails();
// The size of transition arrays are limited so they do not end up in large
// object space. Otherwise ClearNonLiveTransitions would leak memory while
// applying in-place right trimming.
inline bool CanHaveMoreTransitions();
int LastAdded() {
int number_of_own_descriptors = NumberOfOwnDescriptors();
DCHECK(number_of_own_descriptors > 0);
......@@ -6216,11 +6176,6 @@ class Map: public HeapObject {
// Removes a code object from the code cache at the given index.
void RemoveFromCodeCache(Name* name, Code* code, int index);
// Set all map transitions from this map to dead maps to null. Also clear
// back pointers in transition targets so that we do not process this map
// again while following back pointers.
void ClearNonLiveTransitions(Heap* heap);
// Computes a hash value for this map, to be used in HashTables and such.
int Hash();
......@@ -6286,18 +6241,6 @@ class Map: public HeapObject {
inline int visitor_id();
inline void set_visitor_id(int visitor_id);
typedef void (*TraverseCallback)(Map* map, void* data);
void TraverseTransitionTree(TraverseCallback callback, void* data);
// When you set the prototype of an object using the __proto__ accessor you
// need a new map for the object (the prototype is stored in the map). In
// order not to multiply maps unnecessarily we store these as transitions in
// the original map. That way we can transition to the same map if the same
// prototype is set, rather than creating a new map every time. The
// transitions are in the form of a map where the keys are prototype objects
// and the values are the maps they transition to.
static const int kMaxCachedPrototypeTransitions = 256;
static Handle<Map> TransitionToPrototype(Handle<Map> map,
Handle<Object> prototype,
PrototypeOptimizationMode mode);
......@@ -6311,6 +6254,10 @@ class Map: public HeapObject {
static const int kPrototypeOffset = kBitField3Offset + kPointerSize;
static const int kConstructorOrBackPointerOffset =
kPrototypeOffset + kPointerSize;
// When there is only one transition, it is stored directly in this field;
// otherwise a transition array is used.
// For prototype maps, this slot is used to store a pointer to the prototype
// object using this map.
static const int kTransitionsOffset =
kConstructorOrBackPointerOffset + kPointerSize;
static const int kDescriptorsOffset = kTransitionsOffset + kPointerSize;
......@@ -6453,15 +6400,6 @@ class Map: public HeapObject {
static Handle<Map> TransitionElementsToSlow(Handle<Map> object,
ElementsKind to_kind);
// Zaps the contents of backing data structures. Note that the
// heap verifier (i.e. VerifyMarkingVisitor) relies on zapping of objects
// holding weak references when incremental marking is used, because it also
// iterates over objects that are otherwise unreachable.
// In general we only want to call these functions in release mode when
// heap verification is turned on.
void ZapPrototypeTransitions();
void ZapTransitions();
void DeprecateTransitionTree();
bool DeprecateTarget(PropertyKind kind, Name* key,
PropertyAttributes attributes,
......@@ -6490,16 +6428,6 @@ class Map: public HeapObject {
HeapType* old_field_type,
HeapType* new_field_type);
static inline void SetPrototypeTransitions(
Handle<Map> map,
Handle<FixedArray> prototype_transitions);
static Handle<Map> GetPrototypeTransition(Handle<Map> map,
Handle<Object> prototype);
static Handle<Map> PutPrototypeTransition(Handle<Map> map,
Handle<Object> prototype,
Handle<Map> target_map);
static const int kFastPropertiesSoftLimit = 12;
static const int kMaxFastProperties = 128;
......
......@@ -11,55 +11,19 @@ namespace v8 {
namespace internal {
#define FIELD_ADDR(p, offset) \
(reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
#define WRITE_FIELD(p, offset, value) \
(*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)
#define CONDITIONAL_WRITE_BARRIER(heap, object, offset, value, mode) \
if (mode == UPDATE_WRITE_BARRIER) { \
heap->incremental_marking()->RecordWrite( \
object, HeapObject::RawField(object, offset), value); \
if (heap->InNewSpace(value)) { \
heap->RecordWrite(object->address(), offset); \
} \
}
TransitionArray* TransitionArray::cast(Object* object) {
DCHECK(object->IsTransitionArray());
return reinterpret_cast<TransitionArray*>(object);
}
bool TransitionArray::HasElementsTransition() {
return SearchSpecial(GetHeap()->elements_transition_symbol()) != kNotFound;
}
Object* TransitionArray::back_pointer_storage() {
return get(kBackPointerStorageIndex);
}
void TransitionArray::set_back_pointer_storage(Object* back_pointer,
WriteBarrierMode mode) {
Heap* heap = GetHeap();
WRITE_FIELD(this, kBackPointerStorageOffset, back_pointer);
CONDITIONAL_WRITE_BARRIER(
heap, this, kBackPointerStorageOffset, back_pointer, mode);
}
bool TransitionArray::HasPrototypeTransitions() {
return IsFullTransitionArray() &&
get(kPrototypeTransitionsIndex) != Smi::FromInt(0);
return get(kPrototypeTransitionsIndex) != Smi::FromInt(0);
}
FixedArray* TransitionArray::GetPrototypeTransitions() {
DCHECK(IsFullTransitionArray());
DCHECK(HasPrototypeTransitions()); // Callers must check first.
Object* prototype_transitions = get(kPrototypeTransitionsIndex);
return FixedArray::cast(prototype_transitions);
}
......@@ -67,88 +31,68 @@ FixedArray* TransitionArray::GetPrototypeTransitions() {
void TransitionArray::SetPrototypeTransitions(FixedArray* transitions,
WriteBarrierMode mode) {
DCHECK(IsFullTransitionArray());
DCHECK(transitions->IsFixedArray());
Heap* heap = GetHeap();
WRITE_FIELD(this, kPrototypeTransitionsOffset, transitions);
CONDITIONAL_WRITE_BARRIER(
heap, this, kPrototypeTransitionsOffset, transitions, mode);
set(kPrototypeTransitionsIndex, transitions, mode);
}
Object** TransitionArray::GetPrototypeTransitionsSlot() {
return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
kPrototypeTransitionsOffset);
return RawFieldOfElementAt(kPrototypeTransitionsIndex);
}
Object** TransitionArray::GetKeySlot(int transition_number) {
DCHECK(!IsSimpleTransition());
DCHECK(transition_number < number_of_transitions());
return RawFieldOfElementAt(ToKeyIndex(transition_number));
}
Name* TransitionArray::GetKey(int transition_number) {
if (IsSimpleTransition()) {
Map* target = GetTarget(kSimpleTransitionIndex);
int descriptor = target->LastAdded();
Name* key = target->instance_descriptors()->GetKey(descriptor);
return key;
}
DCHECK(transition_number < number_of_transitions());
return Name::cast(get(ToKeyIndex(transition_number)));
}
Name* TransitionArray::GetKey(Object* raw_transitions, int transition_number) {
if (IsSimpleTransition(raw_transitions)) {
DCHECK(transition_number == 0);
return GetSimpleTransitionKey(GetSimpleTransition(raw_transitions));
}
DCHECK(IsFullTransitionArray(raw_transitions));
return TransitionArray::cast(raw_transitions)->GetKey(transition_number);
}
void TransitionArray::SetKey(int transition_number, Name* key) {
DCHECK(!IsSimpleTransition());
DCHECK(transition_number < number_of_transitions());
set(ToKeyIndex(transition_number), key);
}
Map* TransitionArray::GetTarget(int transition_number) {
if (IsSimpleTransition()) {
DCHECK(transition_number == kSimpleTransitionIndex);
return Map::cast(get(kSimpleTransitionTarget));
}
DCHECK(transition_number < number_of_transitions());
return Map::cast(get(ToTargetIndex(transition_number)));
}
void TransitionArray::SetTarget(int transition_number, Map* value) {
if (IsSimpleTransition()) {
DCHECK(transition_number == kSimpleTransitionIndex);
return set(kSimpleTransitionTarget, value);
Map* TransitionArray::GetTarget(Object* raw_transitions,
int transition_number) {
if (IsSimpleTransition(raw_transitions)) {
DCHECK(transition_number == 0);
return GetSimpleTransition(raw_transitions);
}
DCHECK(transition_number < number_of_transitions());
set(ToTargetIndex(transition_number), value);
}
PropertyDetails TransitionArray::GetTargetDetails(int transition_number) {
Map* map = GetTarget(transition_number);
return map->GetLastDescriptorDetails();
DCHECK(IsFullTransitionArray(raw_transitions));
return TransitionArray::cast(raw_transitions)->GetTarget(transition_number);
}
Object* TransitionArray::GetTargetValue(int transition_number) {
Map* map = GetTarget(transition_number);
return map->instance_descriptors()->GetValue(map->LastAdded());
void TransitionArray::SetTarget(int transition_number, Map* value) {
DCHECK(transition_number < number_of_transitions());
set(ToTargetIndex(transition_number), value);
}
int TransitionArray::SearchName(Name* name, int* out_insertion_index) {
if (IsSimpleTransition()) {
Name* key = GetKey(kSimpleTransitionIndex);
if (key->Equals(name)) return kSimpleTransitionIndex;
if (out_insertion_index != NULL) {
*out_insertion_index = key->Hash() > name->Hash() ? 0 : 1;
}
return kNotFound;
}
return internal::Search<ALL_ENTRIES>(this, name, 0, out_insertion_index);
}
......@@ -225,19 +169,10 @@ void TransitionArray::NoIncrementalWriteBarrierSet(int transition_number,
void TransitionArray::SetNumberOfTransitions(int number_of_transitions) {
if (IsFullTransitionArray()) {
DCHECK(number_of_transitions <= number_of_transitions_storage());
WRITE_FIELD(this, kTransitionLengthOffset,
Smi::FromInt(number_of_transitions));
}
DCHECK(number_of_transitions <= Capacity(this));
set(kTransitionLengthIndex, Smi::FromInt(number_of_transitions));
}
#undef FIELD_ADDR
#undef WRITE_FIELD
#undef CONDITIONAL_WRITE_BARRIER
} } // namespace v8::internal
#endif // V8_TRANSITIONS_INL_H_
This diff is collapsed.
This diff is collapsed.
......@@ -2142,6 +2142,12 @@ TEST(InstanceOfStubWriteBarrier) {
}
static int NumberOfProtoTransitions(Map* map) {
return TransitionArray::NumberOfPrototypeTransitions(
TransitionArray::GetPrototypeTransitions(map));
}
TEST(PrototypeTransitionClearing) {
if (FLAG_never_compact) return;
CcTest::InitializeVM();
......@@ -2154,7 +2160,7 @@ TEST(PrototypeTransitionClearing) {
v8::Utils::OpenHandle(
*v8::Handle<v8::Object>::Cast(
CcTest::global()->Get(v8_str("base"))));
int initialTransitions = baseObject->map()->NumberOfProtoTransitions();
int initialTransitions = NumberOfProtoTransitions(baseObject->map());
CompileRun(
"var live = [];"
......@@ -2167,16 +2173,17 @@ TEST(PrototypeTransitionClearing) {
// Verify that only dead prototype transitions are cleared.
CHECK_EQ(initialTransitions + 10,
baseObject->map()->NumberOfProtoTransitions());
NumberOfProtoTransitions(baseObject->map()));
CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
const int transitions = 10 - 3;
CHECK_EQ(initialTransitions + transitions,
baseObject->map()->NumberOfProtoTransitions());
NumberOfProtoTransitions(baseObject->map()));
// Verify that prototype transitions array was compacted.
FixedArray* trans = baseObject->map()->GetPrototypeTransitions();
FixedArray* trans =
TransitionArray::GetPrototypeTransitions(baseObject->map());
for (int i = initialTransitions; i < initialTransitions + transitions; i++) {
int j = Map::kProtoTransitionHeaderSize + i;
int j = TransitionArray::kProtoTransitionHeaderSize + i;
CHECK(trans->get(j)->IsMap());
}
......@@ -2194,7 +2201,7 @@ TEST(PrototypeTransitionClearing) {
i::FLAG_always_compact = true;
Handle<Map> map(baseObject->map());
CHECK(!space->LastPage()->Contains(
map->GetPrototypeTransitions()->address()));
TransitionArray::GetPrototypeTransitions(*map)->address()));
CHECK(space->LastPage()->Contains(prototype->address()));
}
......@@ -2877,7 +2884,7 @@ TEST(OptimizedAllocationArrayLiterals) {
static int CountMapTransitions(Map* map) {
return map->transitions()->number_of_transitions();
return TransitionArray::NumberOfTransitions(map->raw_transitions());
}
......@@ -3058,7 +3065,7 @@ TEST(TransitionArraySimpleToFull) {
CompileRun("o = new F;"
"root = new F");
root = GetByName("root");
DCHECK(root->map()->transitions()->IsSimpleTransition());
DCHECK(TransitionArray::IsSimpleTransition(root->map()->raw_transitions()));
AddPropertyTo(2, root, "happy");
// Count number of live transitions after marking. Note that one transition
......
......@@ -342,9 +342,10 @@ class Expectations {
SetDataField(property_index, attributes, representation, heap_type);
Handle<String> name = MakeName("prop", property_index);
int t = map->SearchTransition(kData, *name, attributes);
CHECK_NE(TransitionArray::kNotFound, t);
return handle(map->GetTransition(t));
Map* target =
TransitionArray::SearchTransition(*map, kData, *name, attributes);
CHECK(target != NULL);
return handle(target);
}
Handle<Map> AddAccessorConstant(Handle<Map> map,
......@@ -1477,9 +1478,10 @@ TEST(ReconfigurePropertySplitMapTransitionsOverflow) {
}
Handle<String> name = MakeName("prop", i);
int t = map2->SearchTransition(kData, *name, NONE);
CHECK_NE(TransitionArray::kNotFound, t);
map2 = handle(map2->GetTransition(t));
Map* target =
TransitionArray::SearchTransition(*map2, kData, *name, NONE);
CHECK(target != NULL);
map2 = handle(target);
}
map2 = Map::ReconfigureProperty(map2, kSplitProp, kData, NONE,
......@@ -1499,12 +1501,12 @@ TEST(ReconfigurePropertySplitMapTransitionsOverflow) {
// Fill in transition tree of |map2| so that it can't have more transitions.
for (int i = 0; i < TransitionArray::kMaxNumberOfTransitions; i++) {
CHECK(map2->CanHaveMoreTransitions());
CHECK(TransitionArray::CanHaveMoreTransitions(map2));
Handle<String> name = MakeName("foo", i);
Map::CopyWithField(map2, name, any_type, NONE, Representation::Smi(),
INSERT_TRANSITION).ToHandleChecked();
}
CHECK(!map2->CanHaveMoreTransitions());
CHECK(!TransitionArray::CanHaveMoreTransitions(map2));
// Try to update |map|, since there is no place for propX transition at |map2|
// |map| should become "copy-generalized".
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment