Commit 948a973d authored by jkummerow's avatar jkummerow Committed by Commit bot

Revert of Simplify and compact transitions storage (patchset #4 id:80001 of...

Revert of Simplify and compact transitions storage (patchset #4 id:80001 of https://codereview.chromium.org/980573002/)

Reason for revert:
x64 test failures

Original issue's description:
> Simplify and compact transitions storage
>
> 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.
>
> Committed: https://crrev.com/45fbef7f2252fce10634931cb103ccc1fc95ae6a
> Cr-Commit-Position: refs/heads/master@{#27029}

TBR=verwaest@chromium.org,ulan@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

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

Cr-Commit-Position: refs/heads/master@{#27030}
parent 45fbef7f
......@@ -1286,31 +1286,28 @@ void V8HeapExplorer::ExtractContextReferences(int entry, Context* context) {
void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
Object* raw_transitions = map->raw_transitions();
if (TransitionArray::IsFullTransitionArray(raw_transitions)) {
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
if (map->HasTransitionArray()) {
TransitionArray* transitions = map->transitions();
int transitions_entry = GetEntry(transitions)->index();
if (FLAG_collect_maps && map->CanTransition()) {
if (transitions->HasPrototypeTransitions()) {
FixedArray* prototype_transitions =
transitions->GetPrototypeTransitions();
MarkAsWeakContainer(prototype_transitions);
TagObject(prototype_transitions, "(prototype transitions");
SetInternalReference(transitions, transitions_entry,
"prototype_transitions", prototype_transitions);
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);
}
// 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)");
......
......@@ -2476,7 +2476,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->set_raw_transitions(Smi::FromInt(0));
map->init_transitions(undefined_value());
map->set_unused_property_fields(0);
map->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
......@@ -2610,7 +2610,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()->set_raw_transitions(Smi::FromInt(0));
meta_map()->init_transitions(undefined_value());
meta_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
meta_map()->set_layout_descriptor(LayoutDescriptor::FastPointerLayout());
......@@ -2619,7 +2619,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()->set_raw_transitions(Smi::FromInt(0));
fixed_array_map()->init_transitions(undefined_value());
fixed_array_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
fixed_array_map()->set_layout_descriptor(
......@@ -2628,7 +2628,7 @@ bool Heap::CreateInitialMaps() {
undefined_map()->set_code_cache(empty_fixed_array());
undefined_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
undefined_map()->set_raw_transitions(Smi::FromInt(0));
undefined_map()->init_transitions(undefined_value());
undefined_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
undefined_map()->set_layout_descriptor(
......@@ -2637,7 +2637,7 @@ bool Heap::CreateInitialMaps() {
null_map()->set_code_cache(empty_fixed_array());
null_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
null_map()->set_raw_transitions(Smi::FromInt(0));
null_map()->init_transitions(undefined_value());
null_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
null_map()->set_layout_descriptor(LayoutDescriptor::FastPointerLayout());
......@@ -2646,7 +2646,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()->set_raw_transitions(Smi::FromInt(0));
constant_pool_array_map()->init_transitions(undefined_value());
constant_pool_array_map()->set_instance_descriptors(empty_descriptor_array());
if (FLAG_unbox_double_fields) {
constant_pool_array_map()->set_layout_descriptor(
......
......@@ -1470,9 +1470,8 @@ class MarkCompactMarkingVisitor::ObjectStatsTracker<
heap->RecordFixedArraySubTypeStats(DESCRIPTOR_ARRAY_SUB_TYPE,
fixed_array_size);
}
if (TransitionArray::IsFullTransitionArray(map_obj->raw_transitions())) {
int fixed_array_size =
TransitionArray::cast(map_obj->raw_transitions())->Size();
if (map_obj->HasTransitionArray()) {
int fixed_array_size = map_obj->transitions()->Size();
heap->RecordFixedArraySubTypeStats(TRANSITION_ARRAY_SUB_TYPE,
fixed_array_size);
}
......@@ -2347,12 +2346,10 @@ void MarkCompactCollector::ClearNonLiveReferences() {
void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) {
FixedArray* prototype_transitions =
TransitionArray::GetPrototypeTransitions(map);
int number_of_transitions =
TransitionArray::NumberOfPrototypeTransitions(prototype_transitions);
int number_of_transitions = map->NumberOfProtoTransitions();
FixedArray* prototype_transitions = map->GetPrototypeTransitions();
const int header = TransitionArray::kProtoTransitionHeaderSize;
const int header = Map::kProtoTransitionHeaderSize;
int new_number_of_transitions = 0;
for (int i = 0; i < number_of_transitions; i++) {
Object* cached_map = prototype_transitions->get(header + i);
......@@ -2366,8 +2363,7 @@ void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) {
}
if (new_number_of_transitions != number_of_transitions) {
TransitionArray::SetNumberOfPrototypeTransitions(prototype_transitions,
new_number_of_transitions);
map->SetNumberOfProtoTransitions(new_number_of_transitions);
}
// Fill slots that became free with undefined value.
......@@ -2388,7 +2384,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, map);
ClearMapTransitions(parent);
}
}
......@@ -2402,43 +2398,28 @@ bool MarkCompactCollector::ClearMapBackPointer(Map* target) {
}
void MarkCompactCollector::ClearMapTransitions(Map* map, Map* dead_transition) {
Object* transitions = map->raw_transitions();
int num_transitions = TransitionArray::NumberOfTransitions(transitions);
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;
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;
}
TransitionArray* t = map->transitions();
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 < num_transitions; ++i) {
Map* target = TransitionArray::GetTarget(transitions, i);
for (int i = 0; i < t->number_of_transitions(); ++i) {
Map* target = t->GetTarget(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);
......@@ -2453,7 +2434,9 @@ void MarkCompactCollector::ClearMapTransitions(Map* map, Map* dead_transition) {
// 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 == num_transitions) return;
if (transition_index == t->number_of_transitions()) return;
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
if (descriptors_owner_died) {
if (number_of_own_descriptors > 0) {
......@@ -2469,17 +2452,14 @@ void MarkCompactCollector::ClearMapTransitions(Map* map, Map* dead_transition) {
// 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 = TransitionArray::Capacity(transitions) - transition_index;
int trim = t->number_of_transitions_storage() - 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, trim * TransitionArray::kTransitionSize);
t, t->IsSimpleTransition() ? trim
: trim * TransitionArray::kTransitionSize);
t->SetNumberOfTransitions(transition_index);
// The map still has a full transition array.
DCHECK(TransitionArray::IsFullTransitionArray(map->raw_transitions()));
}
DCHECK(map->HasTransitionArray());
}
......@@ -4191,7 +4171,7 @@ void MarkCompactCollector::SweepSpaces() {
EvacuateNewSpaceAndCandidates();
// ClearNonLiveReferences depends on precise sweeping of map space to
// ClearNonLiveTransitions depends on precise sweeping of map space to
// detect whether unmarked map became dead in this collection or in one
// of the previous ones.
{
......
......@@ -808,7 +808,7 @@ class MarkCompactCollector {
void ClearNonLiveReferences();
void ClearNonLivePrototypeTransitions(Map* map);
void ClearNonLiveMapTransitions(Map* map, MarkBit map_mark);
void ClearMapTransitions(Map* map, Map* dead_transition);
void ClearMapTransitions(Map* map);
bool ClearMapBackPointer(Map* map);
void TrimDescriptorArray(Map* map, DescriptorArray* descriptors,
int number_of_own_descriptors);
......
......@@ -584,13 +584,18 @@ void StaticMarkingVisitor<StaticVisitor>::VisitJSDataView(Map* map,
template <typename StaticVisitor>
void StaticMarkingVisitor<StaticVisitor>::MarkMapContents(Heap* heap,
Map* map) {
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));
// 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);
}
// Since descriptor arrays are potentially shared, ensure that only the
......@@ -626,18 +631,20 @@ 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 ClearNonLiveReferences.
// prototype transitions in ClearNonLiveTransitions.
Object** slot = transitions->GetPrototypeTransitionsSlot();
HeapObject* obj = HeapObject::cast(*slot);
heap->mark_compact_collector()->RecordSlot(slot, slot, obj);
StaticVisitor::MarkObjectWithoutPush(heap, obj);
}
int num_transitions = TransitionArray::NumberOfTransitions(transitions);
for (int i = 0; i < num_transitions; ++i) {
for (int i = 0; i < transitions->number_of_transitions(); ++i) {
StaticVisitor::VisitPointer(heap, transitions->GetKeySlot(i));
}
}
......
......@@ -2553,11 +2553,10 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
number_ = number;
}
void LookupTransition(Map* map, Name* name, PropertyAttributes attributes) {
Map* target =
TransitionArray::SearchTransition(map, kData, name, attributes);
if (target == NULL) return NotFound();
int transition_index = map->SearchTransition(kData, name, attributes);
if (transition_index == TransitionArray::kNotFound) return NotFound();
lookup_type_ = TRANSITION_TYPE;
transition_ = handle(target);
transition_ = handle(map->GetTransition(transition_index));
number_ = transition_->LastAdded();
details_ = transition_->instance_descriptors()->GetDetails(number_);
}
......
......@@ -359,19 +359,19 @@ Handle<Object> JsonParser<seq_one_byte>::ParseJsonObject() {
bool follow_expected = false;
Handle<Map> target;
if (seq_one_byte) {
key = TransitionArray::ExpectedTransitionKey(map);
key = Map::ExpectedTransitionKey(map);
follow_expected = !key.is_null() && ParseJsonString(key);
}
// If the expected transition hits, follow it.
if (follow_expected) {
target = TransitionArray::ExpectedTransitionTarget(map);
target = Map::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 = TransitionArray::FindTransitionToField(map, key);
target = Map::FindTransitionToField(map, key);
// If a transition was found, follow it and continue.
transitioning = !target.is_null();
}
......
......@@ -320,8 +320,10 @@ void Map::MapVerify() {
VerifyHeapPointer(prototype());
VerifyHeapPointer(instance_descriptors());
SLOW_DCHECK(instance_descriptors()->IsSortedNoDuplicates());
SLOW_DCHECK(TransitionArray::IsSortedNoDuplicates(this));
SLOW_DCHECK(TransitionArray::IsConsistentWithBackPointers(this));
if (HasTransitionArray()) {
SLOW_DCHECK(transitions()->IsSortedNoDuplicates());
SLOW_DCHECK(transitions()->IsConsistentWithBackPointers(this));
}
// TODO(ishell): turn it back to SLOW_DCHECK.
CHECK(!FLAG_unbox_double_fields ||
layout_descriptor()->IsConsistentWithMap(this));
......@@ -342,6 +344,7 @@ 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));
......@@ -1205,28 +1208,14 @@ 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;
}
// 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;
bool TransitionArray::IsConsistentWithBackPointers(Map* current_map) {
for (int i = 0; i < number_of_transitions(); ++i) {
if (!CheckOneBackPointer(current_map, GetTarget(i))) return false;
}
return true;
}
......
......@@ -1877,6 +1877,41 @@ 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)
......@@ -3002,7 +3037,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 {
......@@ -5220,6 +5255,22 @@ 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);
......@@ -5322,13 +5373,127 @@ Object* Map::GetBackPointer() {
}
Map* Map::ElementsTransitionMap() {
return TransitionArray::SearchSpecial(
this, GetHeap()->elements_transition_symbol());
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);
}
ACCESSORS(Map, raw_transitions, Object, kTransitionsOffset)
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);
}
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);
}
void Map::SetBackPointer(Object* value, WriteBarrierMode mode) {
......@@ -5347,7 +5512,6 @@ 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.
......@@ -5357,15 +5521,12 @@ 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,9 +432,8 @@ void Map::MapPrint(std::ostream& os) { // NOLINT
if (FLAG_unbox_double_fields) {
os << "\n - layout descriptor: " << Brief(layout_descriptor());
}
if (TransitionArray::NumberOfTransitions(raw_transitions()) > 0) {
os << "\n - transitions: ";
TransitionArray::PrintTransitions(os, raw_transitions());
if (HasTransitionArray()) {
os << "\n - transitions: " << Brief(transitions());
}
os << "\n - prototype: " << Brief(prototype());
os << "\n - constructor: " << Brief(GetConstructor());
......@@ -1139,20 +1138,19 @@ void DescriptorArray::PrintDescriptors(std::ostream& os) { // NOLINT
void TransitionArray::Print() {
OFStream os(stdout);
TransitionArray::PrintTransitions(os, this);
this->PrintTransitions(os);
os << std::flush;
}
void TransitionArray::PrintTransitions(std::ostream& os, Object* transitions,
void TransitionArray::PrintTransitions(std::ostream& os,
bool print_header) { // NOLINT
int num_transitions = NumberOfTransitions(transitions);
if (print_header) {
os << "Transition array " << num_transitions << "\n";
os << "Transition array " << number_of_transitions() << "\n";
}
for (int i = 0; i < num_transitions; i++) {
Name* key = GetKey(transitions, i);
Map* target = GetTarget(transitions, i);
for (int i = 0; i < number_of_transitions(); i++) {
Name* key = GetKey(i);
Map* target = GetTarget(i);
os << " ";
#ifdef OBJECT_PRINT
key->NamePrint(os);
......@@ -1160,17 +1158,16 @@ void TransitionArray::PrintTransitions(std::ostream& os, Object* transitions,
key->ShortPrint(os);
#endif
os << ": ";
Heap* heap = key->GetHeap();
if (key == heap->nonextensible_symbol()) {
if (key == GetHeap()->nonextensible_symbol()) {
os << " (transition to non-extensible)";
} else if (key == heap->sealed_symbol()) {
} else if (key == GetHeap()->sealed_symbol()) {
os << " (transition to sealed)";
} else if (key == heap->frozen_symbol()) {
} else if (key == GetHeap()->frozen_symbol()) {
os << " (transition to frozen)";
} else if (key == heap->elements_transition_symbol()) {
} else if (key == GetHeap()->elements_transition_symbol()) {
os << " (transition to " << ElementsKindToString(target->elements_kind())
<< ")";
} else if (key == heap->observed_symbol()) {
} else if (key == GetHeap()->observed_symbol()) {
os << " (transition to Object.observe)";
} else {
PropertyDetails details = GetTargetDetails(key, target);
......@@ -1180,9 +1177,7 @@ void TransitionArray::PrintTransitions(std::ostream& os, Object* transitions,
}
os << (details.kind() == kData ? "data" : "accessor");
if (details.location() == kDescriptor) {
Object* value =
target->instance_descriptors()->GetValue(target->LastAdded());
os << " " << Brief(value);
os << " " << Brief(GetTargetValue(i));
}
os << "), attrs: " << details.attributes();
}
......@@ -1192,7 +1187,8 @@ void TransitionArray::PrintTransitions(std::ostream& os, Object* transitions,
void JSObject::PrintTransitions(std::ostream& os) { // NOLINT
TransitionArray::PrintTransitions(os, map()->raw_transitions());
if (!map()->HasTransitionArray()) return;
map()->transitions()->PrintTransitions(os, false);
}
#endif // defined(DEBUG) || defined(OBJECT_PRINT)
} } // namespace v8::internal
This diff is collapsed.
......@@ -5869,11 +5869,26 @@ class Map: public HeapObject {
// map with DICTIONARY_ELEMENTS was found in the prototype chain.
bool DictionaryElementsInPrototypeChainOnly();
inline Map* ElementsTransitionMap();
inline bool HasTransitionArray() const;
inline bool HasElementsTransition();
inline Map* elements_transition_map();
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(raw_transitions, Object)
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);
Map* FindRootMap();
Map* FindFieldOwner(int descriptor);
......@@ -5947,8 +5962,6 @@ 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);
......@@ -5981,8 +5994,38 @@ 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);
......@@ -6150,6 +6193,11 @@ 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();
......@@ -6215,6 +6263,18 @@ 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);
......@@ -6228,10 +6288,6 @@ 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;
......@@ -6374,6 +6430,15 @@ 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,
......@@ -6402,6 +6467,16 @@ 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,19 +11,55 @@ 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 get(kPrototypeTransitionsIndex) != Smi::FromInt(0);
return IsFullTransitionArray() &&
get(kPrototypeTransitionsIndex) != Smi::FromInt(0);
}
FixedArray* TransitionArray::GetPrototypeTransitions() {
DCHECK(HasPrototypeTransitions()); // Callers must check first.
DCHECK(IsFullTransitionArray());
Object* prototype_transitions = get(kPrototypeTransitionsIndex);
return FixedArray::cast(prototype_transitions);
}
......@@ -31,68 +67,88 @@ FixedArray* TransitionArray::GetPrototypeTransitions() {
void TransitionArray::SetPrototypeTransitions(FixedArray* transitions,
WriteBarrierMode mode) {
DCHECK(IsFullTransitionArray());
DCHECK(transitions->IsFixedArray());
set(kPrototypeTransitionsIndex, transitions, mode);
Heap* heap = GetHeap();
WRITE_FIELD(this, kPrototypeTransitionsOffset, transitions);
CONDITIONAL_WRITE_BARRIER(
heap, this, kPrototypeTransitionsOffset, transitions, mode);
}
Object** TransitionArray::GetPrototypeTransitionsSlot() {
return RawFieldOfElementAt(kPrototypeTransitionsIndex);
return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
kPrototypeTransitionsOffset);
}
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)));
}
Map* TransitionArray::GetTarget(Object* raw_transitions,
int transition_number) {
if (IsSimpleTransition(raw_transitions)) {
DCHECK(transition_number == 0);
return GetSimpleTransition(raw_transitions);
void TransitionArray::SetTarget(int transition_number, Map* value) {
if (IsSimpleTransition()) {
DCHECK(transition_number == kSimpleTransitionIndex);
return set(kSimpleTransitionTarget, value);
}
DCHECK(IsFullTransitionArray(raw_transitions));
return TransitionArray::cast(raw_transitions)->GetTarget(transition_number);
DCHECK(transition_number < number_of_transitions());
set(ToTargetIndex(transition_number), value);
}
void TransitionArray::SetTarget(int transition_number, Map* value) {
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();
}
Object* TransitionArray::GetTargetValue(int transition_number) {
Map* map = GetTarget(transition_number);
return map->instance_descriptors()->GetValue(map->LastAdded());
}
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);
}
......@@ -169,10 +225,19 @@ void TransitionArray::NoIncrementalWriteBarrierSet(int transition_number,
void TransitionArray::SetNumberOfTransitions(int number_of_transitions) {
DCHECK(number_of_transitions <= Capacity(this));
set(kTransitionLengthIndex, Smi::FromInt(number_of_transitions));
if (IsFullTransitionArray()) {
DCHECK(number_of_transitions <= number_of_transitions_storage());
WRITE_FIELD(this, kTransitionLengthOffset,
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.
......@@ -2141,12 +2141,6 @@ TEST(InstanceOfStubWriteBarrier) {
}
static int NumberOfProtoTransitions(Map* map) {
return TransitionArray::NumberOfPrototypeTransitions(
TransitionArray::GetPrototypeTransitions(map));
}
TEST(PrototypeTransitionClearing) {
if (FLAG_never_compact) return;
CcTest::InitializeVM();
......@@ -2159,7 +2153,7 @@ TEST(PrototypeTransitionClearing) {
v8::Utils::OpenHandle(
*v8::Handle<v8::Object>::Cast(
CcTest::global()->Get(v8_str("base"))));
int initialTransitions = NumberOfProtoTransitions(baseObject->map());
int initialTransitions = baseObject->map()->NumberOfProtoTransitions();
CompileRun(
"var live = [];"
......@@ -2172,17 +2166,16 @@ TEST(PrototypeTransitionClearing) {
// Verify that only dead prototype transitions are cleared.
CHECK_EQ(initialTransitions + 10,
NumberOfProtoTransitions(baseObject->map()));
baseObject->map()->NumberOfProtoTransitions());
CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
const int transitions = 10 - 3;
CHECK_EQ(initialTransitions + transitions,
NumberOfProtoTransitions(baseObject->map()));
baseObject->map()->NumberOfProtoTransitions());
// Verify that prototype transitions array was compacted.
FixedArray* trans =
TransitionArray::GetPrototypeTransitions(baseObject->map());
FixedArray* trans = baseObject->map()->GetPrototypeTransitions();
for (int i = initialTransitions; i < initialTransitions + transitions; i++) {
int j = TransitionArray::kProtoTransitionHeaderSize + i;
int j = Map::kProtoTransitionHeaderSize + i;
CHECK(trans->get(j)->IsMap());
}
......@@ -2200,7 +2193,7 @@ TEST(PrototypeTransitionClearing) {
i::FLAG_always_compact = true;
Handle<Map> map(baseObject->map());
CHECK(!space->LastPage()->Contains(
TransitionArray::GetPrototypeTransitions(*map)->address()));
map->GetPrototypeTransitions()->address()));
CHECK(space->LastPage()->Contains(prototype->address()));
}
......@@ -2883,7 +2876,7 @@ TEST(OptimizedAllocationArrayLiterals) {
static int CountMapTransitions(Map* map) {
return TransitionArray::NumberOfTransitions(map->raw_transitions());
return map->transitions()->number_of_transitions();
}
......@@ -3062,7 +3055,7 @@ TEST(TransitionArraySimpleToFull) {
CompileRun("o = new F;"
"root = new F");
root = GetByName("root");
DCHECK(TransitionArray::IsSimpleTransition(root->map()->raw_transitions()));
DCHECK(root->map()->transitions()->IsSimpleTransition());
AddPropertyTo(2, root, "happy");
// Count number of live transitions after marking. Note that one transition
......
......@@ -342,10 +342,9 @@ class Expectations {
SetDataField(property_index, attributes, representation, heap_type);
Handle<String> name = MakeName("prop", property_index);
Map* target =
TransitionArray::SearchTransition(*map, kData, *name, attributes);
CHECK(target != NULL);
return handle(target);
int t = map->SearchTransition(kData, *name, attributes);
CHECK_NE(TransitionArray::kNotFound, t);
return handle(map->GetTransition(t));
}
Handle<Map> AddAccessorConstant(Handle<Map> map,
......@@ -1478,10 +1477,9 @@ TEST(ReconfigurePropertySplitMapTransitionsOverflow) {
}
Handle<String> name = MakeName("prop", i);
Map* target =
TransitionArray::SearchTransition(*map2, kData, *name, NONE);
CHECK(target != NULL);
map2 = handle(target);
int t = map2->SearchTransition(kData, *name, NONE);
CHECK_NE(TransitionArray::kNotFound, t);
map2 = handle(map2->GetTransition(t));
}
map2 = Map::ReconfigureProperty(map2, kSplitProp, kData, NONE,
......@@ -1501,12 +1499,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(TransitionArray::CanHaveMoreTransitions(map2));
CHECK(map2->CanHaveMoreTransitions());
Handle<String> name = MakeName("foo", i);
Map::CopyWithField(map2, name, any_type, NONE, Representation::Smi(),
INSERT_TRANSITION).ToHandleChecked();
}
CHECK(!TransitionArray::CanHaveMoreTransitions(map2));
CHECK(!map2->CanHaveMoreTransitions());
// 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