From d53de0590a88a7ccacc2b37278b98d6cbab8048c Mon Sep 17 00:00:00 2001 From: "verwaest@chromium.org" Date: Mon, 13 Aug 2012 08:43:16 +0000 Subject: [PATCH] Swapped transition array and descriptor array. Now a map points to a transition array which contains the descriptor array. The descriptor array is now immutable. The next step is to share the descriptor array with all back-pointed maps as long as there is a single line of extension. Maps that require a descriptor array but don't need transitions will still need a pseudo-empty transition array to contain the descriptor array. Review URL: https://chromiumcodereview.appspot.com/10816005 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12298 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/macro-assembler-arm.cc | 17 +-- src/bootstrapper.cc | 23 ++-- src/factory.cc | 5 +- src/heap.cc | 9 +- src/ia32/macro-assembler-ia32.cc | 13 +- src/mark-compact.cc | 98 +++------------ src/mark-compact.h | 1 - src/objects-inl.h | 197 +++++++++---------------------- src/objects.cc | 121 ++++++++----------- src/objects.h | 88 +++++--------- src/profile-generator.cc | 3 +- src/runtime.cc | 4 +- src/transitions-inl.h | 39 +++++- src/transitions.cc | 7 +- src/transitions.h | 32 +++-- src/x64/macro-assembler-x64.cc | 13 +- test/cctest/test-alloc.cc | 2 +- 17 files changed, 261 insertions(+), 411 deletions(-) diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 54fb33724..cc7bcdb3b 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -3691,15 +3691,16 @@ void MacroAssembler::ClampDoubleToUint8(Register result_reg, void MacroAssembler::LoadInstanceDescriptors(Register map, Register descriptors, Register scratch) { - ldr(descriptors, - FieldMemOperand(map, Map::kInstanceDescriptorsOrBackPointerOffset)); + Register temp = descriptors; + ldr(temp, FieldMemOperand(map, Map::kTransitionsOrBackPointerOffset)); Label ok, fail; - CheckMap(descriptors, + CheckMap(temp, scratch, isolate()->factory()->fixed_array_map(), &fail, DONT_DO_SMI_CHECK); + ldr(descriptors, FieldMemOperand(temp, TransitionArray::kDescriptorsOffset)); jmp(&ok); bind(&fail); mov(descriptors, Operand(FACTORY->empty_descriptor_array())); @@ -3712,9 +3713,6 @@ void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) { // Preload a couple of values used in the loop. Register empty_fixed_array_value = r6; LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); - Register empty_descriptor_array_value = r7; - LoadRoot(empty_descriptor_array_value, - Heap::kEmptyDescriptorArrayRootIndex); mov(r1, r0); bind(&next); @@ -3728,7 +3726,7 @@ void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) { // check for an enum cache. Leave the map in r2 for the subsequent // prototype load. ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - ldr(r3, FieldMemOperand(r2, Map::kInstanceDescriptorsOrBackPointerOffset)); + ldr(r3, FieldMemOperand(r2, Map::kTransitionsOrBackPointerOffset)); CheckMap(r3, r7, @@ -3736,6 +3734,11 @@ void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) { call_runtime, DONT_DO_SMI_CHECK); + LoadRoot(r7, Heap::kEmptyDescriptorArrayRootIndex); + ldr(r3, FieldMemOperand(r3, TransitionArray::kDescriptorsOffset)); + cmp(r3, r7); + b(eq, call_runtime); + // Check that there is an enum cache in the non-empty instance // descriptors (r3). This is the case if the next enumeration // index field does not contain a smi. diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 4300e7329..b09db4610 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -399,7 +399,7 @@ void Genesis::SetFunctionInstanceDescriptor( } PropertyAttributes attribs = static_cast( DONT_ENUM | DONT_DELETE | READ_ONLY); - map->set_instance_descriptors(*descriptors); + Map::SetDescriptors(map, descriptors); { // Add length. CallbacksDescriptor d(*factory()->length_symbol(), *length, attribs); @@ -540,7 +540,7 @@ void Genesis::SetStrictFunctionInstanceDescriptor( } PropertyAttributes attribs = static_cast( DONT_ENUM | DONT_DELETE); - map->set_instance_descriptors(*descriptors); + Map::SetDescriptors(map, descriptors); { // Add length. CallbacksDescriptor d(*factory()->length_symbol(), *length, attribs); @@ -868,13 +868,14 @@ bool Genesis::InitializeGlobal(Handle inner_global, // is 1. array_function->shared()->set_length(1); + Handle initial_map(array_function->initial_map()); Handle array_descriptors(factory->NewDescriptorArray(1)); DescriptorArray::WhitenessWitness witness(*array_descriptors); Handle array_length(factory->NewForeign(&Accessors::ArrayLength)); PropertyAttributes attribs = static_cast( DONT_ENUM | DONT_DELETE); - array_function->initial_map()->set_instance_descriptors(*array_descriptors); + Map::SetDescriptors(initial_map, array_descriptors); { // Add length. CallbacksDescriptor d(*factory->length_symbol(), *array_length, attribs); @@ -922,7 +923,7 @@ bool Genesis::InitializeGlobal(Handle inner_global, factory->NewForeign(&Accessors::StringLength)); PropertyAttributes attribs = static_cast( DONT_ENUM | DONT_DELETE | READ_ONLY); - string_map->set_instance_descriptors(*string_descriptors); + Map::SetDescriptors(string_map, string_descriptors); { // Add length. CallbacksDescriptor d(*factory->length_symbol(), *string_length, attribs); @@ -958,7 +959,7 @@ bool Genesis::InitializeGlobal(Handle inner_global, static_cast(DONT_ENUM | DONT_DELETE | READ_ONLY); Handle descriptors = factory->NewDescriptorArray(5); DescriptorArray::WhitenessWitness witness(*descriptors); - initial_map->set_instance_descriptors(*descriptors); + Map::SetDescriptors(initial_map, descriptors); { // ECMA-262, section 15.10.7.1. @@ -1142,7 +1143,7 @@ bool Genesis::InitializeGlobal(Handle inner_global, // Create the descriptor array for the arguments object. Handle descriptors = factory->NewDescriptorArray(3); DescriptorArray::WhitenessWitness witness(*descriptors); - map->set_instance_descriptors(*descriptors); + Map::SetDescriptors(map, descriptors); { // length FieldDescriptor d(*factory->length_symbol(), 0, DONT_ENUM); @@ -1528,7 +1529,7 @@ bool Genesis::InstallNatives() { factory()->NewForeign(&Accessors::ScriptEvalFromFunctionName)); PropertyAttributes attribs = static_cast(DONT_ENUM | DONT_DELETE | READ_ONLY); - script_map->set_instance_descriptors(*script_descriptors); + Map::SetDescriptors(script_map, script_descriptors); { CallbacksDescriptor d( @@ -1653,14 +1654,14 @@ bool Genesis::InstallNatives() { // elements in InternalArrays can be set to non-Smi values without going // through a common bottleneck that would make the SMI_ONLY -> FAST_ELEMENT // transition easy to trap. Moreover, they rarely are smi-only. - MaybeObject* maybe_map = - array_function->initial_map()->Copy(DescriptorArray::MAY_BE_SHARED); + MaybeObject* maybe_map = array_function->initial_map()->Copy(); Map* new_map; if (!maybe_map->To(&new_map)) return false; new_map->set_elements_kind(FAST_HOLEY_ELEMENTS); array_function->set_initial_map(new_map); // Make "length" magic on instances. + Handle initial_map(array_function->initial_map()); Handle array_descriptors(factory()->NewDescriptorArray(1)); DescriptorArray::WhitenessWitness witness(*array_descriptors); @@ -1668,7 +1669,7 @@ bool Genesis::InstallNatives() { &Accessors::ArrayLength)); PropertyAttributes attribs = static_cast( DONT_ENUM | DONT_DELETE); - array_function->initial_map()->set_instance_descriptors(*array_descriptors); + Map::SetDescriptors(initial_map, array_descriptors); { // Add length. CallbacksDescriptor d( @@ -1763,7 +1764,7 @@ bool Genesis::InstallNatives() { Handle reresult_descriptors = factory()->NewDescriptorArray(3); DescriptorArray::WhitenessWitness witness(*reresult_descriptors); - initial_map->set_instance_descriptors(*reresult_descriptors); + Map::SetDescriptors(initial_map, reresult_descriptors); { JSFunction* array_function = global_context()->array_function(); diff --git a/src/factory.cc b/src/factory.cc index a4a2be24f..913b75601 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -115,8 +115,7 @@ Handle Factory::NewObjectHashTable(int at_least_space_for) { Handle Factory::NewDescriptorArray(int number_of_descriptors) { ASSERT(0 <= number_of_descriptors); CALL_HEAP_FUNCTION(isolate(), - DescriptorArray::Allocate(number_of_descriptors, - DescriptorArray::MAY_BE_SHARED), + DescriptorArray::Allocate(number_of_descriptors), DescriptorArray); } @@ -497,7 +496,7 @@ Handle Factory::CopyMap(Handle src, Handle Factory::CopyMap(Handle src) { - CALL_HEAP_FUNCTION(isolate(), src->Copy(DescriptorArray::MAY_BE_SHARED), Map); + CALL_HEAP_FUNCTION(isolate(), src->Copy(), Map); } diff --git a/src/heap.cc b/src/heap.cc index 8cc1dea87..8fc025338 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -3730,8 +3730,7 @@ MaybeObject* Heap::AllocateFunctionPrototype(JSFunction* function) { // constructors. Map* new_map; ASSERT(object_function->has_initial_map()); - MaybeObject* maybe_map = - object_function->initial_map()->Copy(DescriptorArray::MAY_BE_SHARED); + MaybeObject* maybe_map = object_function->initial_map()->Copy(); if (!maybe_map->To(&new_map)) return maybe_map; Object* prototype; @@ -3875,8 +3874,7 @@ MaybeObject* Heap::AllocateInitialMap(JSFunction* fun) { fun->shared()->ForbidInlineConstructor(); } else { DescriptorArray* descriptors; - MaybeObject* maybe_descriptors = - DescriptorArray::Allocate(count, DescriptorArray::MAY_BE_SHARED); + MaybeObject* maybe_descriptors = DescriptorArray::Allocate(count); if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors; DescriptorArray::WhitenessWitness witness(descriptors); @@ -3895,7 +3893,8 @@ MaybeObject* Heap::AllocateInitialMap(JSFunction* fun) { if (HasDuplicates(descriptors)) { fun->shared()->ForbidInlineConstructor(); } else { - map->InitializeDescriptors(descriptors); + MaybeObject* maybe_failure = map->InitializeDescriptors(descriptors); + if (maybe_failure->IsFailure()) return maybe_failure; map->set_pre_allocated_property_fields(count); map->set_unused_property_fields(in_object_properties - count); } diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index de3e2ce6d..7d621a34f 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -2519,14 +2519,15 @@ void MacroAssembler::Abort(const char* msg) { void MacroAssembler::LoadInstanceDescriptors(Register map, Register descriptors) { - mov(descriptors, FieldOperand(map, - Map::kInstanceDescriptorsOrBackPointerOffset)); + Register temp = descriptors; + mov(temp, FieldOperand(map, Map::kTransitionsOrBackPointerOffset)); Label ok, fail; - CheckMap(descriptors, + CheckMap(temp, isolate()->factory()->fixed_array_map(), &fail, DONT_DO_SMI_CHECK); + mov(descriptors, FieldOperand(temp, TransitionArray::kDescriptorsOffset)); jmp(&ok); bind(&fail); mov(descriptors, isolate()->factory()->empty_descriptor_array()); @@ -2893,12 +2894,16 @@ void MacroAssembler::CheckEnumCache(Label* call_runtime) { // check for an enum cache. Leave the map in ebx for the subsequent // prototype load. mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset)); - mov(edx, FieldOperand(ebx, Map::kInstanceDescriptorsOrBackPointerOffset)); + mov(edx, FieldOperand(ebx, Map::kTransitionsOrBackPointerOffset)); CheckMap(edx, isolate()->factory()->fixed_array_map(), call_runtime, DONT_DO_SMI_CHECK); + mov(edx, FieldOperand(edx, TransitionArray::kDescriptorsOffset)); + cmp(edx, isolate()->factory()->empty_descriptor_array()); + j(equal, call_runtime); + // Check that there is an enum cache in the non-empty instance // descriptors (edx). This is the case if the next enumeration // index field does not contain a smi. diff --git a/src/mark-compact.cc b/src/mark-compact.cc index 26b01ae01..b3a0bf7df 100644 --- a/src/mark-compact.cc +++ b/src/mark-compact.cc @@ -1906,28 +1906,26 @@ template void Marker::MarkMapContents(Map* map); template void Marker::MarkMapContents(Map* map) { // Make sure that the back pointer stored either in the map itself or inside - // its prototype transitions array is marked. Treat pointers in the descriptor - // array as weak and also mark that array to prevent visiting it later. + // its transitions array is marked. Treat pointers in the transitions array as + // weak and also mark that array to prevent visiting it later. base_marker()->MarkObjectAndPush(HeapObject::cast(map->GetBackPointer())); - Object** descriptor_array_slot = - HeapObject::RawField(map, Map::kInstanceDescriptorsOrBackPointerOffset); - Object* descriptor_array = *descriptor_array_slot; - if (descriptor_array->IsDescriptorArray()) { - MarkDescriptorArray(reinterpret_cast(descriptor_array)); + Object** transitions_slot = + HeapObject::RawField(map, Map::kTransitionsOrBackPointerOffset); + Object* transitions = *transitions_slot; + if (transitions->IsTransitionArray()) { + MarkTransitionArray(reinterpret_cast(transitions)); } else { // Already marked by marking map->GetBackPointer(). - ASSERT(descriptor_array->IsMap() || descriptor_array->IsUndefined()); + ASSERT(transitions->IsMap() || transitions->IsUndefined()); } - // Mark the Object* fields of the Map. Since the descriptor array has been - // marked already, it is fine that one of these fields contains a pointer - // to it. But make sure to skip back pointer. - STATIC_ASSERT(Map::kPointerFieldsEndOffset == - Map::kBitField3Offset + kPointerSize); + // Mark the Object* fields of the Map. Since the transitions array has been + // marked already, it is fine that one of these fields contains a pointer to + // it. Object** start_slot = HeapObject::RawField(map, Map::kPointerFieldsBeginOffset); - Object** end_slot = HeapObject::RawField(map, Map::kBitField3Offset); + Object** end_slot = HeapObject::RawField(map, Map::kPointerFieldsEndOffset); for (Object** slot = start_slot; slot < end_slot; slot++) { Object* obj = *slot; if (!obj->NonFailureIsHeapObject()) continue; @@ -1937,78 +1935,16 @@ void Marker::MarkMapContents(Map* map) { } -template -void Marker::MarkDescriptorArray(DescriptorArray* descriptors) { - // Empty descriptor array is marked as a root before any maps are marked. - ASSERT(descriptors != descriptors->GetHeap()->empty_descriptor_array()); - - if (!base_marker()->MarkObjectWithoutPush(descriptors)) return; - Object** descriptor_start = descriptors->data_start(); - - // Since the descriptor array itself is not pushed for scanning, all fields - // that point to objects manually have to be pushed, marked, and their slots - // recorded. - if (descriptors->HasEnumCache()) { - Object** enum_cache_slot = descriptors->GetEnumCacheSlot(); - Object* enum_cache = *enum_cache_slot; - base_marker()->MarkObjectAndPush( - reinterpret_cast(enum_cache)); - mark_compact_collector()->RecordSlot(descriptor_start, - enum_cache_slot, - enum_cache); - } - - if (descriptors->HasTransitionArray()) { - Object** transitions_slot = descriptors->GetTransitionsSlot(); - Object* transitions = *transitions_slot; - mark_compact_collector()->RecordSlot(descriptor_start, - transitions_slot, - transitions); - MarkTransitionArray(reinterpret_cast(transitions)); - } - - // If the descriptor contains a transition (value is a Map), we don't mark the - // value as live. It might be removed by ClearNonLiveTransitions later. - for (int i = 0; i < descriptors->number_of_descriptors(); ++i) { - Object** key_slot = descriptors->GetKeySlot(i); - Object* key = *key_slot; - if (key->IsHeapObject()) { - base_marker()->MarkObjectAndPush(HeapObject::cast(key)); - mark_compact_collector()->RecordSlot(descriptor_start, key_slot, key); - } - - Object** value_slot = descriptors->GetValueSlot(i); - if (!(*value_slot)->IsHeapObject()) continue; - HeapObject* value = HeapObject::cast(*value_slot); - - mark_compact_collector()->RecordSlot(descriptor_start, - value_slot, - value); - - PropertyDetails details(descriptors->GetDetails(i)); - - switch (details.type()) { - case NORMAL: - case FIELD: - case CONSTANT_FUNCTION: - case HANDLER: - case INTERCEPTOR: - case CALLBACKS: - base_marker()->MarkObjectAndPush(value); - break; - case TRANSITION: - case NONEXISTENT: - UNREACHABLE(); - break; - } - } -} - template void Marker::MarkTransitionArray(TransitionArray* transitions) { if (!base_marker()->MarkObjectWithoutPush(transitions)) return; Object** transitions_start = transitions->data_start(); + DescriptorArray* descriptors = transitions->descriptors(); + base_marker()->MarkObjectAndPush(descriptors); + mark_compact_collector()->RecordSlot( + transitions_start, transitions->GetDescriptorsSlot(), descriptors); + if (transitions->HasPrototypeTransitions()) { // Mark prototype transitions array but don't push it into marking stack. // This will make references from it weak. We will clean dead prototype diff --git a/src/mark-compact.h b/src/mark-compact.h index f4915519a..015438062 100644 --- a/src/mark-compact.h +++ b/src/mark-compact.h @@ -394,7 +394,6 @@ template class Marker { // Mark pointers in a Map and its DescriptorArray together, possibly // treating transitions or back pointers weak. void MarkMapContents(Map* map); - void MarkDescriptorArray(DescriptorArray* descriptors); void MarkTransitionArray(TransitionArray* transitions); private: diff --git a/src/objects-inl.h b/src/objects-inl.h index 3bffe241f..68358ef8f 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -1887,30 +1887,6 @@ bool DescriptorArray::IsEmpty() { } -bool DescriptorArray::MayContainTransitions() { - return !IsEmpty(); -} - - -bool DescriptorArray::HasTransitionArray() { - return MayContainTransitions() && !get(kTransitionsIndex)->IsSmi(); -} - - -Object* DescriptorArray::back_pointer_storage() { - return READ_FIELD(this, kBackPointerStorageOffset); -} - - -void DescriptorArray::set_back_pointer_storage(Object* value, - WriteBarrierMode mode) { - ASSERT(length() > kBackPointerStorageIndex); - Heap* heap = GetHeap(); - WRITE_FIELD(this, kBackPointerStorageOffset, value); - CONDITIONAL_WRITE_BARRIER(heap, this, kBackPointerStorageOffset, value, mode); -} - - void DescriptorArray::NoIncrementalWriteBarrierSwap(FixedArray* array, int first, int second) { @@ -2000,27 +1976,6 @@ int DescriptorArray::SearchWithCache(String* name) { } -TransitionArray* DescriptorArray::transitions() { - ASSERT(MayContainTransitions()); - Object* array = get(kTransitionsIndex); - return TransitionArray::cast(array); -} - - -void DescriptorArray::ClearTransitions() { - WRITE_FIELD(this, kTransitionsOffset, Smi::FromInt(0)); -} - - -void DescriptorArray::set_transitions(TransitionArray* transitions_array, - WriteBarrierMode mode) { - Heap* heap = GetHeap(); - WRITE_FIELD(this, kTransitionsOffset, transitions_array); - CONDITIONAL_WRITE_BARRIER( - heap, this, kTransitionsOffset, transitions_array, mode); -} - - Object** DescriptorArray::GetKeySlot(int descriptor_number) { ASSERT(descriptor_number < number_of_descriptors()); return HeapObject::RawField( @@ -3470,43 +3425,36 @@ void Map::set_prototype(Object* value, WriteBarrierMode mode) { DescriptorArray* Map::instance_descriptors() { - Object* object = READ_FIELD(this, kInstanceDescriptorsOrBackPointerOffset); - if (!object->IsDescriptorArray()) { - ASSERT(object->IsMap() || object->IsUndefined()); - return GetHeap()->empty_descriptor_array(); - } else { - return DescriptorArray::cast(object); - } + if (!HasTransitionArray()) return GetHeap()->empty_descriptor_array(); + return transitions()->descriptors(); } -void Map::set_instance_descriptors(DescriptorArray* value, - WriteBarrierMode mode) { - Heap* heap = GetHeap(); - - if (value == heap->empty_descriptor_array()) { - ClearDescriptorArray(heap, mode); - return; - } +// If the descriptor is using the empty transition array, install a new empty +// transition array that will have place for an element transition. +static MaybeObject* EnsureHasTransitionArray(Map* map) { + if (map->HasTransitionArray()) return map; - Object* object = READ_FIELD(this, kInstanceDescriptorsOrBackPointerOffset); + TransitionArray* transitions; + MaybeObject* maybe_transitions = TransitionArray::Allocate(0); + if (!maybe_transitions->To(&transitions)) return maybe_transitions; + map->set_transitions(transitions); + return transitions; +} - if (object->IsDescriptorArray()) { - value->set_back_pointer_storage( - DescriptorArray::cast(object)->back_pointer_storage()); - } else { - ASSERT(object->IsMap() || object->IsUndefined()); - value->set_back_pointer_storage(object); - } +MaybeObject* Map::SetDescriptors(DescriptorArray* value, + WriteBarrierMode mode) { ASSERT(!is_shared()); - WRITE_FIELD(this, kInstanceDescriptorsOrBackPointerOffset, value); - CONDITIONAL_WRITE_BARRIER( - heap, this, kInstanceDescriptorsOrBackPointerOffset, value, mode); + MaybeObject* maybe_failure = EnsureHasTransitionArray(this); + if (maybe_failure->IsFailure()) return maybe_failure; + + transitions()->set_descriptors(value, mode); + return this; } -void Map::InitializeDescriptors(DescriptorArray* descriptors) { +MaybeObject* Map::InitializeDescriptors(DescriptorArray* descriptors) { int len = descriptors->number_of_descriptors(); ASSERT(len <= DescriptorArray::kMaxNumberOfDescriptors); SLOW_ASSERT(descriptors->IsSortedNoDuplicates()); @@ -3526,36 +3474,37 @@ void Map::InitializeDescriptors(DescriptorArray* descriptors) { } #endif - set_instance_descriptors(descriptors); + MaybeObject* maybe_failure = SetDescriptors(descriptors); + if (maybe_failure->IsFailure()) return maybe_failure; for (int i = 0; i < len; ++i) { if (descriptors->GetDetails(i).index() == len) { SetLastAdded(i); - break; + return this; } } - ASSERT((len == 0 && LastAdded() == kNoneAdded) || - len == descriptors->GetDetails(LastAdded()).index()); + ASSERT(len == 0 && LastAdded() == kNoneAdded); + return this; } SMI_ACCESSORS(Map, bit_field3, kBitField3Offset) -void Map::ClearDescriptorArray(Heap* heap, WriteBarrierMode mode) { +void Map::ClearTransitions(Heap* heap, WriteBarrierMode mode) { Object* back_pointer = GetBackPointer(); #ifdef DEBUG - Object* object = READ_FIELD(this, kInstanceDescriptorsOrBackPointerOffset); - if (object->IsDescriptorArray()) { + Object* object = READ_FIELD(this, kTransitionsOrBackPointerOffset); + if (object->IsTransitionArray()) { ZapTransitions(); } else { ASSERT(object->IsMap() || object->IsUndefined()); } #endif - WRITE_FIELD(this, kInstanceDescriptorsOrBackPointerOffset, back_pointer); + WRITE_FIELD(this, kTransitionsOrBackPointerOffset, back_pointer); CONDITIONAL_WRITE_BARRIER( - heap, this, kInstanceDescriptorsOrBackPointerOffset, back_pointer, mode); + heap, this, kTransitionsOrBackPointerOffset, back_pointer, mode); } @@ -3569,9 +3518,9 @@ void Map::AppendDescriptor(Descriptor* desc, Object* Map::GetBackPointer() { - Object* object = READ_FIELD(this, kInstanceDescriptorsOrBackPointerOffset); + Object* object = READ_FIELD(this, kTransitionsOrBackPointerOffset); if (object->IsDescriptorArray()) { - return DescriptorArray::cast(object)->back_pointer_storage(); + return TransitionArray::cast(object)->back_pointer_storage(); } else { ASSERT(object->IsMap() || object->IsUndefined()); return object; @@ -3585,7 +3534,8 @@ bool Map::HasElementsTransition() { bool Map::HasTransitionArray() { - return instance_descriptors()->HasTransitionArray(); + Object* object = READ_FIELD(this, kTransitionsOrBackPointerOffset); + return object->IsTransitionArray(); } @@ -3613,35 +3563,6 @@ void Map::SetTransition(int transition_index, Map* target) { } -// If the map is using the empty descriptor array, install a new empty -// descriptor array that will contain an elements transition. -static MaybeObject* AllowTransitions(Map* map) { - if (map->instance_descriptors()->MayContainTransitions()) return map; - DescriptorArray* descriptors; - MaybeObject* maybe_descriptors = - DescriptorArray::Allocate(0, DescriptorArray::CANNOT_BE_SHARED); - if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors; - map->set_instance_descriptors(descriptors); - return descriptors; -} - - -// If the descriptor is using the empty transition array, install a new empty -// transition array that will have place for an element transition. -static MaybeObject* EnsureHasTransitionArray(Map* map) { - if (map->HasTransitionArray()) return map; - - AllowTransitions(map); - - TransitionArray* transitions; - MaybeObject* maybe_transitions = TransitionArray::Allocate(0); - if (!maybe_transitions->To(&transitions)) return maybe_transitions; - MaybeObject* added_transitions = map->set_transitions(transitions); - if (added_transitions->IsFailure()) return added_transitions; - return transitions; -} - - MaybeObject* Map::set_elements_transition_map(Map* transitioned_map) { MaybeObject* allow_elements = EnsureHasTransitionArray(this); if (allow_elements->IsFailure()) return allow_elements; @@ -3679,40 +3600,32 @@ bool Map::HasPrototypeTransitions() { TransitionArray* Map::transitions() { - return instance_descriptors()->transitions(); -} - - -void Map::ClearTransitions(Heap* heap, WriteBarrierMode mode) { -#ifdef DEBUG - ZapTransitions(); -#endif - DescriptorArray* descriptors = instance_descriptors(); - if (descriptors->number_of_descriptors() == 0) { - ClearDescriptorArray(heap, mode); - } else { - descriptors->ClearTransitions(); - } + ASSERT(HasTransitionArray()); + Object* object = READ_FIELD(this, kTransitionsOrBackPointerOffset); + return TransitionArray::cast(object); } -MaybeObject* Map::set_transitions(TransitionArray* transitions_array) { - MaybeObject* allow_transitions = AllowTransitions(this); - if (allow_transitions->IsFailure()) return allow_transitions; +void Map::set_transitions(TransitionArray* transition_array, + WriteBarrierMode mode) { + transition_array->set_descriptors(instance_descriptors()); + transition_array->set_back_pointer_storage(GetBackPointer()); #ifdef DEBUG if (HasTransitionArray()) { - ASSERT(transitions() != transitions_array); + ASSERT(transitions() != transition_array); ZapTransitions(); } #endif - instance_descriptors()->set_transitions(transitions_array); - return this; + + WRITE_FIELD(this, kTransitionsOrBackPointerOffset, transition_array); + CONDITIONAL_WRITE_BARRIER( + GetHeap(), this, kTransitionsOrBackPointerOffset, transition_array, mode); } void Map::init_back_pointer(Object* undefined) { ASSERT(undefined->IsUndefined()); - WRITE_FIELD(this, kInstanceDescriptorsOrBackPointerOffset, undefined); + WRITE_FIELD(this, kTransitionsOrBackPointerOffset, undefined); } @@ -3720,13 +3633,13 @@ void Map::SetBackPointer(Object* value, WriteBarrierMode mode) { ASSERT(instance_type() >= FIRST_JS_RECEIVER_TYPE); ASSERT((value->IsUndefined() && GetBackPointer()->IsMap()) || (value->IsMap() && GetBackPointer()->IsUndefined())); - Object* object = READ_FIELD(this, kInstanceDescriptorsOrBackPointerOffset); - if (object->IsDescriptorArray()) { - DescriptorArray::cast(object)->set_back_pointer_storage(value); + Object* object = READ_FIELD(this, kTransitionsOrBackPointerOffset); + if (object->IsTransitionArray()) { + TransitionArray::cast(object)->set_back_pointer_storage(value); } else { - WRITE_FIELD(this, kInstanceDescriptorsOrBackPointerOffset, value); + WRITE_FIELD(this, kTransitionsOrBackPointerOffset, value); CONDITIONAL_WRITE_BARRIER( - GetHeap(), this, kInstanceDescriptorsOrBackPointerOffset, value, mode); + GetHeap(), this, kTransitionsOrBackPointerOffset, value, mode); } } @@ -3734,10 +3647,8 @@ void Map::SetBackPointer(Object* value, WriteBarrierMode mode) { // Can either be Smi (no transitions), normal transition array, or a transition // array with the header overwritten as a Smi (thus iterating). TransitionArray* Map::unchecked_transition_array() { - ASSERT(HasTransitionArray()); - Object* object = *HeapObject::RawField(instance_descriptors(), - DescriptorArray::kTransitionsOffset); - ASSERT(!object->IsSmi()); + Object* object = *HeapObject::RawField(this, + Map::kTransitionsOrBackPointerOffset); TransitionArray* transition_array = static_cast(object); return transition_array; } diff --git a/src/objects.cc b/src/objects.cc index 749a35297..106769133 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -2192,7 +2192,7 @@ void Map::CopyAppendCallbackDescriptors(Handle map, // After this point the GC is not allowed to run anymore until the map is in a // consistent state again, i.e., all the descriptors are appended and the // descriptor array is trimmed to the right size. - map->set_instance_descriptors(*result); + Map::SetDescriptors(map, result); // Fill in new callback descriptors. Process the callbacks from // back to front so that the last callback with a given name takes @@ -2211,7 +2211,7 @@ void Map::CopyAppendCallbackDescriptors(Handle map, int new_number_of_descriptors = map->NumberOfSetDescriptors(); // Reinstall the original descriptor array if no new elements were added. if (new_number_of_descriptors == descriptor_count) { - map->set_instance_descriptors(*array); + Map::SetDescriptors(map, array); return; } @@ -4141,7 +4141,7 @@ MaybeObject* JSObject::PreventExtensions() { // Do a map transition, other objects with this map may still // be extensible. Map* new_map; - MaybeObject* maybe = map()->Copy(DescriptorArray::MAY_BE_SHARED); + MaybeObject* maybe = map()->Copy(); if (!maybe->To(&new_map)) return maybe; new_map->set_is_extensible(false); @@ -4178,6 +4178,13 @@ bool JSReceiver::IsSimpleEnum() { } +void Map::SetDescriptors(Handle map, + Handle descriptors) { + Isolate* isolate = map->GetIsolate(); + CALL_HEAP_FUNCTION_VOID(isolate, map->SetDescriptors(*descriptors)); +} + + int Map::NumberOfDescribedProperties(PropertyAttributes filter) { int result = 0; DescriptorArray* descs = instance_descriptors(); @@ -4918,7 +4925,8 @@ MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors, } else { ASSERT(descriptors->GetDetails(last_added).index() == descriptors->number_of_descriptors()); - result->set_instance_descriptors(descriptors); + MaybeObject* maybe_failure = result->SetDescriptors(descriptors); + if (maybe_failure->IsFailure()) return maybe_failure; result->SetLastAdded(last_added); } @@ -4927,9 +4935,7 @@ MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors, MaybeObject* maybe_transitions = AddTransition(name, result); if (!maybe_transitions->To(&transitions)) return maybe_transitions; - MaybeObject* maybe_set = set_transitions(transitions); - if (maybe_set->IsFailure()) return maybe_set; - + set_transitions(transitions); result->SetBackPointer(this); } @@ -4940,7 +4946,7 @@ MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors, MaybeObject* Map::CopyAsElementsKind(ElementsKind kind, TransitionFlag flag) { // Create a new free-floating map only if we are not allowed to store it. Map* new_map = NULL; - MaybeObject* maybe_new_map = Copy(DescriptorArray::MAY_BE_SHARED); + MaybeObject* maybe_new_map = Copy(); if (!maybe_new_map->To(&new_map)) return maybe_new_map; new_map->set_elements_kind(kind); @@ -4972,25 +4978,17 @@ MaybeObject* Map::CopyWithPreallocatedFieldDescriptors() { // array describing these properties. ASSERT(constructor()->IsJSFunction()); JSFunction* ctor = JSFunction::cast(constructor()); - Map* initial_map = ctor->initial_map(); - DescriptorArray* initial_descriptors = initial_map->instance_descriptors(); - DescriptorArray* descriptors; - MaybeObject* maybe_descriptors = - initial_descriptors->Copy(DescriptorArray::MAY_BE_SHARED); - if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors; + Map* map = ctor->initial_map(); + DescriptorArray* descriptors = map->instance_descriptors(); - int last_added = initial_map->LastAdded(); + int last_added = map->LastAdded(); return CopyReplaceDescriptors(descriptors, NULL, last_added, OMIT_TRANSITION); } -MaybeObject* Map::Copy(DescriptorArray::SharedMode shared_mode) { - DescriptorArray* source_descriptors = instance_descriptors(); - DescriptorArray* descriptors; - MaybeObject* maybe_descriptors = source_descriptors->Copy(shared_mode); - if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors; - +MaybeObject* Map::Copy() { + DescriptorArray* descriptors = instance_descriptors(); int last_added = LastAdded(); return CopyReplaceDescriptors(descriptors, NULL, last_added, OMIT_TRANSITION); @@ -5017,8 +5015,7 @@ MaybeObject* Map::CopyAddDescriptor(Descriptor* descriptor, int new_size = old_size + 1; DescriptorArray* new_descriptors; - MaybeObject* maybe_descriptors = - DescriptorArray::Allocate(new_size, DescriptorArray::MAY_BE_SHARED); + MaybeObject* maybe_descriptors = DescriptorArray::Allocate(new_size); if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; FixedArray::WhitenessWitness witness(new_descriptors); @@ -5079,8 +5076,7 @@ MaybeObject* Map::CopyReplaceDescriptor(Descriptor* descriptor, ASSERT(key == descriptors->GetKey(insertion_index)); DescriptorArray* new_descriptors; - MaybeObject* maybe_descriptors = - DescriptorArray::Allocate(size, DescriptorArray::MAY_BE_SHARED); + MaybeObject* maybe_descriptors = DescriptorArray::Allocate(size); if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; FixedArray::WhitenessWitness witness(new_descriptors); @@ -5301,26 +5297,28 @@ class TraversableMap : public Map { // If we have an unvisited child map, return that one and advance. If we have // none, return NULL and reset any destroyed FixedArray maps. TraversableMap* ChildIteratorNext() { - if (HasTransitionArray()) { - TransitionArray* transition_array = unchecked_transition_array(); - - if (transition_array->HasPrototypeTransitions()) { - HeapObject* proto_transitions = - transition_array->UncheckedPrototypeTransitions(); - IntrusivePrototypeTransitionIterator proto_iterator(proto_transitions); - if (proto_iterator.IsIterating()) { - Map* next = proto_iterator.Next(); - if (next != NULL) return static_cast(next); - } - } + TransitionArray* transition_array = unchecked_transition_array(); + if (!transition_array->map()->IsSmi() && + !transition_array->IsTransitionArray()) { + return NULL; + } - IntrusiveMapTransitionIterator transition_iterator(transition_array); - if (transition_iterator.IsIterating()) { - Map* next = transition_iterator.Next(); + if (transition_array->HasPrototypeTransitions()) { + HeapObject* proto_transitions = + transition_array->UncheckedPrototypeTransitions(); + IntrusivePrototypeTransitionIterator proto_iterator(proto_transitions); + if (proto_iterator.IsIterating()) { + Map* next = proto_iterator.Next(); if (next != NULL) return static_cast(next); } } + IntrusiveMapTransitionIterator transition_iterator(transition_array); + if (transition_iterator.IsIterating()) { + Map* next = transition_iterator.Next(); + if (next != NULL) return static_cast(next); + } + return NULL; } }; @@ -5870,21 +5868,17 @@ bool FixedArray::IsEqualTo(FixedArray* other) { #endif -MaybeObject* DescriptorArray::Allocate(int number_of_descriptors, - SharedMode shared_mode) { +MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) { Heap* heap = Isolate::Current()->heap(); // Do not use DescriptorArray::cast on incomplete object. FixedArray* result; - if (number_of_descriptors == 0 && shared_mode == MAY_BE_SHARED) { - return heap->empty_descriptor_array(); - } + if (number_of_descriptors == 0) return heap->empty_descriptor_array(); // Allocate the array of keys. MaybeObject* maybe_array = heap->AllocateFixedArray(LengthFor(number_of_descriptors)); if (!maybe_array->To(&result)) return maybe_array; result->set(kEnumCacheIndex, Smi::FromInt(0)); - result->set(kTransitionsIndex, Smi::FromInt(0)); return result; } @@ -5921,25 +5915,6 @@ void DescriptorArray::CopyFrom(int dst_index, } -MaybeObject* DescriptorArray::Copy(SharedMode shared_mode) { - // Allocate the new descriptor array. - int number_of_descriptors = this->number_of_descriptors(); - DescriptorArray* new_descriptors; - MaybeObject* maybe_result = Allocate(number_of_descriptors, shared_mode); - if (!maybe_result->To(&new_descriptors)) return maybe_result; - - // Copy the content. - if (number_of_descriptors > 0) { - FixedArray::WhitenessWitness witness(new_descriptors); - for (int i = 0; i < number_of_descriptors; i++) { - new_descriptors->CopyFrom(i, this, i, witness); - } - } - - return new_descriptors; -} - - // We need the whiteness witness since sort will reshuffle the entries in the // descriptor array. If the descriptor array were to be black, the shuffling // would move a slot that was already recorded as pointing into an evacuation @@ -7292,7 +7267,8 @@ void Map::ClearNonLiveTransitions(Heap* heap) { // the transition array from the map. if (transition_index == 0 && !t->HasElementsTransition() && - !t->HasPrototypeTransitions()) { + !t->HasPrototypeTransitions() && + t->descriptors()->IsEmpty()) { return ClearTransitions(heap); } @@ -7534,8 +7510,7 @@ MaybeObject* JSFunction::SetInstancePrototype(Object* value) { // If the function has allocated the initial map // replace it with a copy containing the new prototype. Map* new_map; - MaybeObject* maybe_new_map = - initial_map()->Copy(DescriptorArray::MAY_BE_SHARED); + MaybeObject* maybe_new_map = initial_map()->Copy(); if (!maybe_new_map->To(&new_map)) return maybe_new_map; new_map->set_prototype(value); MaybeObject* maybe_object = set_initial_map_and_cache_transitions(new_map); @@ -7564,7 +7539,7 @@ MaybeObject* JSFunction::SetPrototype(Object* value) { // Remove map transitions because they point to maps with a // different prototype. Map* new_map; - MaybeObject* maybe_new_map = map()->Copy(DescriptorArray::MAY_BE_SHARED); + MaybeObject* maybe_new_map = map()->Copy(); if (!maybe_new_map->To(&new_map)) return maybe_new_map; Heap* heap = new_map->GetHeap(); @@ -8859,7 +8834,7 @@ MaybeObject* JSReceiver::SetPrototype(Object* value, Map* new_map = map->GetPrototypeTransition(value); if (new_map == NULL) { - MaybeObject* maybe_new_map = map->Copy(DescriptorArray::MAY_BE_SHARED); + MaybeObject* maybe_new_map = map->Copy(); if (!maybe_new_map->To(&new_map)) return maybe_new_map; MaybeObject* maybe_new_cache = @@ -12594,8 +12569,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( // Allocate the instance descriptor. DescriptorArray* descriptors; MaybeObject* maybe_descriptors = - DescriptorArray::Allocate(instance_descriptor_length, - DescriptorArray::MAY_BE_SHARED); + DescriptorArray::Allocate(instance_descriptor_length); if (!maybe_descriptors->To(&descriptors)) { return maybe_descriptors; } @@ -12667,8 +12641,9 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( descriptors->Sort(witness); + MaybeObject* maybe_failure = new_map->InitializeDescriptors(descriptors); + if (maybe_failure->IsFailure()) return maybe_failure; new_map->set_unused_property_fields(unused_property_fields); - new_map->InitializeDescriptors(descriptors); // Transform the object. obj->set_map(new_map); diff --git a/src/objects.h b/src/objects.h index 0b3143aba..b7f3132e7 100644 --- a/src/objects.h +++ b/src/objects.h @@ -84,6 +84,7 @@ // - Context // - JSFunctionResultCache // - ScopeInfo +// - TransitionArray // - FixedDoubleArray // - ExternalArray // - ExternalPixelArray @@ -2459,32 +2460,21 @@ class FixedDoubleArray: public FixedArrayBase { // DescriptorArrays are fixed arrays used to hold instance descriptors. // The format of the these objects is: -// TODO(1399): It should be possible to make room for bit_field3 in the map -// without overloading the instance descriptors field in the map -// (and storing it in the DescriptorArray when the map has one). -// [0]: storage for bit_field3 for Map owning this object (Smi) -// [1]: point to a fixed array with (value, detail) pairs. -// [2]: next enumeration index (Smi), or pointer to small fixed array: -// [0]: next enumeration index (Smi) -// [1]: pointer to fixed array with enum cache -// [3]: first key +// [0]: Either Smi(0) if uninitialized, or a pointer to small fixed array: +// [0]: pointer to fixed array with enum cache +// [1]: either Smi(0) or pointer to fixed array with indices +// [1]: first key // [length() - kDescriptorSize]: last key -// class DescriptorArray: public FixedArray { public: // Returns true for both shared empty_descriptor_array and for smis, which the // map uses to encode additional bit fields when the descriptor array is not // yet used. inline bool IsEmpty(); - inline bool MayContainTransitions(); - inline bool HasTransitionArray(); - - DECL_ACCESSORS(transitions, TransitionArray) - inline void ClearTransitions(); // Returns the number of descriptors in the array. int number_of_descriptors() { - ASSERT(MayContainTransitions() || IsEmpty()); + ASSERT(length() >= kFirstIndex || IsEmpty()); int len = length(); return len <= kFirstIndex ? 0 : (len - kFirstIndex) / kDescriptorSize; } @@ -2508,13 +2498,6 @@ class DescriptorArray: public FixedArray { kEnumCacheOffset); } - Object** GetTransitionsSlot() { - return HeapObject::RawField(reinterpret_cast(this), - kTransitionsOffset); - } - - DECL_ACCESSORS(back_pointer_storage, Object) - // Initialize or change the enum cache, // using the supplied storage for the small "bridge". void SetEnumCache(FixedArray* bridge_storage, @@ -2552,17 +2535,6 @@ class DescriptorArray: public FixedArray { int src_index, const WhitenessWitness&); - // Indicates whether the search function should expect a sorted or an unsorted - // descriptor array as input. - enum SharedMode { - MAY_BE_SHARED, - CANNOT_BE_SHARED - }; - - // Return a copy of the array with all transitions and null descriptors - // removed. Return a Failure object in case of an allocation failure. - MUST_USE_RESULT MaybeObject* Copy(SharedMode shared_mode); - // Sort the instance descriptors by the hash codes of their keys. void Sort(const WhitenessWitness&); @@ -2578,8 +2550,7 @@ class DescriptorArray: public FixedArray { // Allocates a DescriptorArray, but returns the singleton // empty descriptor array object if number_of_descriptors is 0. - MUST_USE_RESULT static MaybeObject* Allocate(int number_of_descriptors, - SharedMode shared_mode); + MUST_USE_RESULT static MaybeObject* Allocate(int number_of_descriptors); // Casting. static inline DescriptorArray* cast(Object* obj); @@ -2587,10 +2558,8 @@ class DescriptorArray: public FixedArray { // Constant for denoting key was not found. static const int kNotFound = -1; - static const int kBackPointerStorageIndex = 0; - static const int kEnumCacheIndex = 1; - static const int kTransitionsIndex = 2; - static const int kFirstIndex = 3; + static const int kEnumCacheIndex = 0; + static const int kFirstIndex = 1; // The length of the "bridge" to the enum cache. static const int kEnumCacheBridgeLength = 2; @@ -2598,11 +2567,8 @@ class DescriptorArray: public FixedArray { static const int kEnumCacheBridgeIndicesCacheIndex = 1; // Layout description. - static const int kBackPointerStorageOffset = FixedArray::kHeaderSize; - static const int kEnumCacheOffset = kBackPointerStorageOffset + - kPointerSize; - static const int kTransitionsOffset = kEnumCacheOffset + kPointerSize; - static const int kFirstOffset = kTransitionsOffset + kPointerSize; + static const int kEnumCacheOffset = FixedArray::kHeaderSize; + static const int kFirstOffset = kEnumCacheOffset + kPointerSize; // Layout description for the bridge array. static const int kEnumCacheBridgeCacheOffset = FixedArray::kHeaderSize; @@ -4821,11 +4787,9 @@ class Map: public HeapObject { inline Map* elements_transition_map(); MUST_USE_RESULT inline MaybeObject* set_elements_transition_map( Map* transitioned_map); - inline TransitionArray* transitions(); inline void SetTransition(int index, Map* target); MUST_USE_RESULT inline MaybeObject* AddTransition(String* key, Map* target); - MUST_USE_RESULT inline MaybeObject* set_transitions( - TransitionArray* transitions); + DECL_ACCESSORS(transitions, TransitionArray) inline void ClearTransitions(Heap* heap, WriteBarrierMode mode = UPDATE_WRITE_BARRIER); @@ -4862,12 +4826,14 @@ class Map: public HeapObject { inline JSFunction* unchecked_constructor(); // [instance descriptors]: describes the object. - DECL_ACCESSORS(instance_descriptors, DescriptorArray) - inline void InitializeDescriptors(DescriptorArray* descriptors); - - // Should only be called to clear a descriptor array that was only used to - // store transitions and does not contain any live transitions anymore. - inline void ClearDescriptorArray(Heap* heap, WriteBarrierMode mode); + inline DescriptorArray* instance_descriptors(); + MUST_USE_RESULT inline MaybeObject* SetDescriptors( + DescriptorArray* descriptors, + WriteBarrierMode mode = UPDATE_WRITE_BARRIER); + static void SetDescriptors(Handle map, + Handle descriptors); + MUST_USE_RESULT inline MaybeObject* InitializeDescriptors( + DescriptorArray* descriptors); // [stub cache]: contains stubs compiled for this map. DECL_ACCESSORS(code_cache, Object) @@ -4973,7 +4939,7 @@ class Map: public HeapObject { // Returns a copy of the map, with all transitions dropped from the // instance descriptors. - MUST_USE_RESULT MaybeObject* Copy(DescriptorArray::SharedMode shared_mode); + MUST_USE_RESULT MaybeObject* Copy(); // Returns the property index for name (only valid for FAST MODE). int PropertyIndexFor(String* name); @@ -5095,14 +5061,14 @@ class Map: public HeapObject { static const int kInstanceAttributesOffset = kInstanceSizesOffset + kIntSize; static const int kPrototypeOffset = kInstanceAttributesOffset + kIntSize; static const int kConstructorOffset = kPrototypeOffset + kPointerSize; - // Storage for instance descriptors is overloaded to also contain additional - // map flags when unused (bit_field3). When the map has instance descriptors, - // the flags are transferred to the instance descriptor array and accessed - // through an extra indirection. - static const int kInstanceDescriptorsOrBackPointerOffset = + // Storage for the transition array is overloaded to directly contain a back + // pointer if unused. When the map has transitions, the back pointer is + // transferred to the transition array and accessed through an extra + // indirection. + static const int kTransitionsOrBackPointerOffset = kConstructorOffset + kPointerSize; static const int kCodeCacheOffset = - kInstanceDescriptorsOrBackPointerOffset + kPointerSize; + kTransitionsOrBackPointerOffset + kPointerSize; static const int kBitField3Offset = kCodeCacheOffset + kPointerSize; static const int kPadStart = kBitField3Offset + kPointerSize; static const int kSize = MAP_POINTER_ALIGN(kPadStart); diff --git a/src/profile-generator.cc b/src/profile-generator.cc index 9a91ad670..1a4139642 100644 --- a/src/profile-generator.cc +++ b/src/profile-generator.cc @@ -2009,9 +2009,10 @@ void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) { Map::kConstructorOffset); if (!map->instance_descriptors()->IsEmpty()) { TagObject(map->instance_descriptors(), "(map descriptors)"); + // TODO(verwaest): Check what to do here. SetInternalReference(map, entry, "descriptors", map->instance_descriptors(), - Map::kInstanceDescriptorsOrBackPointerOffset); + Map::kTransitionsOrBackPointerOffset); } SetInternalReference(map, entry, "code_cache", map->code_cache(), diff --git a/src/runtime.cc b/src/runtime.cc index 16d2c76f2..146607def 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -1294,7 +1294,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DisableAccessChecks) { if (needs_access_checks) { // Copy map so it won't interfere constructor's initial map. Map* new_map; - MaybeObject* maybe_new_map = old_map->Copy(DescriptorArray::MAY_BE_SHARED); + MaybeObject* maybe_new_map = old_map->Copy(); if (!maybe_new_map->To(&new_map)) return maybe_new_map; new_map->set_is_access_check_needed(false); @@ -1311,7 +1311,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_EnableAccessChecks) { if (!old_map->is_access_check_needed()) { // Copy map so it won't interfere constructor's initial map. Map* new_map; - MaybeObject* maybe_new_map = old_map->Copy(DescriptorArray::MAY_BE_SHARED); + MaybeObject* maybe_new_map = old_map->Copy(); if (!maybe_new_map->To(&new_map)) return maybe_new_map; new_map->set_is_access_check_needed(true); diff --git a/src/transitions-inl.h b/src/transitions-inl.h index 6716c717a..0fb5fbeac 100644 --- a/src/transitions-inl.h +++ b/src/transitions-inl.h @@ -82,6 +82,40 @@ void TransitionArray::set_elements_transition(Map* transition_map, } +DescriptorArray* TransitionArray::descriptors() { + return DescriptorArray::cast(get(kDescriptorsIndex)); +} + + +void TransitionArray::set_descriptors(DescriptorArray* descriptors, + WriteBarrierMode mode) { + Heap* heap = GetHeap(); + WRITE_FIELD(this, kDescriptorsOffset, descriptors); + CONDITIONAL_WRITE_BARRIER( + heap, this, kDescriptorsOffset, descriptors, mode); +} + + +Object** TransitionArray::GetDescriptorsSlot() { + return HeapObject::RawField(reinterpret_cast(this), + kDescriptorsOffset); +} + + +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() { Object* prototype_transitions = get(kPrototypeTransitionsIndex); return prototype_transitions != Smi::FromInt(0); @@ -95,9 +129,8 @@ FixedArray* TransitionArray::GetPrototypeTransitions() { HeapObject* TransitionArray::UncheckedPrototypeTransitions() { - Object* prototype_transitions = get(kPrototypeTransitionsIndex); - if (prototype_transitions == Smi::FromInt(0)) return NULL; - return reinterpret_cast(prototype_transitions); + ASSERT(HasPrototypeTransitions()); + return reinterpret_cast(get(kPrototypeTransitionsIndex)); } diff --git a/src/transitions.cc b/src/transitions.cc index 5dda18edd..6f8b2fec5 100644 --- a/src/transitions.cc +++ b/src/transitions.cc @@ -39,10 +39,9 @@ MaybeObject* TransitionArray::Allocate(int number_of_transitions) { Heap* heap = Isolate::Current()->heap(); // Use FixedArray to not use DescriptorArray::cast on incomplete object. FixedArray* array; - { MaybeObject* maybe_array = - heap->AllocateFixedArray(ToKeyIndex(number_of_transitions)); - if (!maybe_array->To(&array)) return maybe_array; - } + MaybeObject* maybe_array = + heap->AllocateFixedArray(ToKeyIndex(number_of_transitions)); + if (!maybe_array->To(&array)) return maybe_array; array->set(kElementsTransitionIndex, Smi::FromInt(0)); array->set(kPrototypeTransitionsIndex, Smi::FromInt(0)); diff --git a/src/transitions.h b/src/transitions.h index 57f32535a..d0d761b5d 100644 --- a/src/transitions.h +++ b/src/transitions.h @@ -41,8 +41,11 @@ namespace internal { // TransitionArrays are fixed arrays used to hold map transitions for property, // constant, and element changes. // The format of the these objects is: -// [0] Elements transition -// [1] First transition +// [0] Descriptor array +// [1] Undefined or back pointer map +// [2] Smi(0) or elements transition map +// [3] Smi(0) or fixed array of prototype transitions +// [4] First transition // [length() - kTransitionSize] Last transition class TransitionArray: public FixedArray { public: @@ -63,6 +66,16 @@ class TransitionArray: public FixedArray { inline bool HasElementsTransition(); inline void ClearElementsTransition(); + inline DescriptorArray* descriptors(); + inline void set_descriptors(DescriptorArray* descriptors, + WriteBarrierMode mode = UPDATE_WRITE_BARRIER); + inline Object** GetDescriptorsSlot(); + + inline Object* back_pointer_storage(); + inline void set_back_pointer_storage( + Object* back_pointer, + WriteBarrierMode mode = UPDATE_WRITE_BARRIER); + inline FixedArray* GetPrototypeTransitions(); inline void SetPrototypeTransitions( FixedArray* prototype_transitions, @@ -106,15 +119,20 @@ class TransitionArray: public FixedArray { // Constant for denoting key was not found. static const int kNotFound = -1; - static const int kElementsTransitionIndex = 0; - static const int kPrototypeTransitionsIndex = 1; - static const int kFirstIndex = 2; + static const int kDescriptorsIndex = 0; + static const int kBackPointerStorageIndex = 1; + static const int kElementsTransitionIndex = 2; + static const int kPrototypeTransitionsIndex = 3; + static const int kFirstIndex = 4; // Layout transition array header. - static const int kElementsTransitionOffset = FixedArray::kHeaderSize; + static const int kDescriptorsOffset = FixedArray::kHeaderSize; + static const int kBackPointerStorageOffset = kDescriptorsOffset + + kPointerSize; + static const int kElementsTransitionOffset = kBackPointerStorageOffset + + kPointerSize; static const int kPrototypeTransitionsOffset = kElementsTransitionOffset + kPointerSize; - static const int kFirstOffset = kPrototypeTransitionsOffset + kPointerSize; // Layout of map transition. static const int kTransitionKey = 0; diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index 9ec16a1f1..2e554b166 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -2860,14 +2860,15 @@ void MacroAssembler::ClampDoubleToUint8(XMMRegister input_reg, void MacroAssembler::LoadInstanceDescriptors(Register map, Register descriptors) { - movq(descriptors, FieldOperand(map, - Map::kInstanceDescriptorsOrBackPointerOffset)); + Register temp = descriptors; + movq(temp, FieldOperand(map, Map::kTransitionsOrBackPointerOffset)); Label ok, fail; - CheckMap(descriptors, + CheckMap(temp, isolate()->factory()->fixed_array_map(), &fail, DONT_DO_SMI_CHECK); + movq(descriptors, FieldOperand(temp, TransitionArray::kDescriptorsOffset)); jmp(&ok); bind(&fail); Move(descriptors, isolate()->factory()->empty_descriptor_array()); @@ -4471,13 +4472,17 @@ void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) { // check for an enum cache. Leave the map in rbx for the subsequent // prototype load. movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset)); - movq(rdx, FieldOperand(rbx, Map::kInstanceDescriptorsOrBackPointerOffset)); + movq(rdx, FieldOperand(rbx, Map::kTransitionsOrBackPointerOffset)); CheckMap(rdx, isolate()->factory()->fixed_array_map(), call_runtime, DONT_DO_SMI_CHECK); + movq(rdx, FieldOperand(rdx, TransitionArray::kDescriptorsOffset)); + cmpq(rdx, empty_descriptor_array_value); + j(equal, call_runtime); + // Check that there is an enum cache in the non-empty instance // descriptors (rdx). This is the case if the next enumeration // index field does not contain a smi. diff --git a/test/cctest/test-alloc.cc b/test/cctest/test-alloc.cc index f64ed1af1..50e60da27 100644 --- a/test/cctest/test-alloc.cc +++ b/test/cctest/test-alloc.cc @@ -158,7 +158,7 @@ TEST(StressJS) { Handle new_descriptors = FACTORY->NewDescriptorArray(1); v8::internal::DescriptorArray::WhitenessWitness witness(*new_descriptors); - map->set_instance_descriptors(*new_descriptors); + v8::internal::Map::SetDescriptors(map, new_descriptors); CallbacksDescriptor d(*name, *foreign, -- 2.34.1