From 526b3f2cac7e7bb87d4314cc0a326d53e9f875af Mon Sep 17 00:00:00 2001 From: "mstarzinger@chromium.org" Date: Wed, 9 May 2012 07:29:18 +0000 Subject: [PATCH] Implement explicit back pointers in transition tree. This makes back pointers in the map transition tree explicit by having accurate back pointers throughout the lifetime of maps instead of establishing and destroying back pointers before and after each marking phase. This is a prerequisite for being able to clear map transitions during incremental marking. R=vegorov@chromium.org BUG=v8:1465 Review URL: https://chromiumcodereview.appspot.com/10381053 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11528 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/heap.cc | 8 ++-- src/mark-compact.cc | 85 +++++++++------------------------ src/mark-compact.h | 7 --- src/objects-debug.cc | 57 ++++++++++++++++++++++ src/objects-inl.h | 60 ++++++++++++++++++++++-- src/objects.cc | 120 ++++++++++++----------------------------------- src/objects.h | 52 ++++++++++---------- src/profile-generator.cc | 14 ++++-- 8 files changed, 207 insertions(+), 196 deletions(-) diff --git a/src/heap.cc b/src/heap.cc index ad28c1e..0035fd5 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -2020,7 +2020,7 @@ MaybeObject* Heap::AllocateMap(InstanceType instance_type, map->set_pre_allocated_property_fields(0); map->init_instance_descriptors(); map->set_code_cache(empty_fixed_array(), SKIP_WRITE_BARRIER); - map->set_prototype_transitions(empty_fixed_array(), SKIP_WRITE_BARRIER); + map->init_prototype_transitions(undefined_value()); map->set_unused_property_fields(0); map->set_bit_field(0); map->set_bit_field2(1 << Map::kIsExtensible); @@ -2159,15 +2159,15 @@ bool Heap::CreateInitialMaps() { // Fix the instance_descriptors for the existing maps. meta_map()->init_instance_descriptors(); meta_map()->set_code_cache(empty_fixed_array()); - meta_map()->set_prototype_transitions(empty_fixed_array()); + meta_map()->init_prototype_transitions(undefined_value()); fixed_array_map()->init_instance_descriptors(); fixed_array_map()->set_code_cache(empty_fixed_array()); - fixed_array_map()->set_prototype_transitions(empty_fixed_array()); + fixed_array_map()->init_prototype_transitions(undefined_value()); oddball_map()->init_instance_descriptors(); oddball_map()->set_code_cache(empty_fixed_array()); - oddball_map()->set_prototype_transitions(empty_fixed_array()); + oddball_map()->init_prototype_transitions(undefined_value()); // Fix prototype object for existing maps. meta_map()->set_prototype(null_value()); diff --git a/src/mark-compact.cc b/src/mark-compact.cc index 5746d23..0aa1192 100644 --- a/src/mark-compact.cc +++ b/src/mark-compact.cc @@ -680,7 +680,6 @@ void MarkCompactCollector::Prepare(GCTracer* tracer) { ASSERT(!FLAG_never_compact || !FLAG_always_compact); - if (collect_maps_) CreateBackPointers(); #ifdef ENABLE_GDB_JIT_INTERFACE if (FLAG_gdbjit) { // If GDBJIT interface is active disable compaction. @@ -1816,13 +1815,19 @@ void MarkCompactCollector::ProcessNewlyMarkedObject(HeapObject* object) { void MarkCompactCollector::MarkMapContents(Map* map) { // Mark prototype transitions array but don't push it into marking stack. // This will make references from it weak. We will clean dead prototype - // transitions in ClearNonLiveTransitions. - FixedArray* prototype_transitions = map->prototype_transitions(); - MarkBit mark = Marking::MarkBitFrom(prototype_transitions); - if (!mark.Get()) { - mark.Set(); - MemoryChunk::IncrementLiveBytesFromGC(prototype_transitions->address(), - prototype_transitions->Size()); + // transitions in ClearNonLiveTransitions. But make sure that back pointers + // stored inside prototype transitions arrays are marked. + Object* raw_proto_transitions = map->unchecked_prototype_transitions(); + if (raw_proto_transitions->IsFixedArray()) { + FixedArray* prototype_transitions = FixedArray::cast(raw_proto_transitions); + MarkBit mark = Marking::MarkBitFrom(prototype_transitions); + if (!mark.Get()) { + mark.Set(); + MemoryChunk::IncrementLiveBytesFromGC(prototype_transitions->address(), + prototype_transitions->Size()); + MarkObjectAndPush(HeapObject::cast( + prototype_transitions->get(Map::kProtoTransitionBackPointerOffset))); + } } Object** raw_descriptor_array_slot = @@ -1921,23 +1926,6 @@ void MarkCompactCollector::MarkDescriptorArray( } -void MarkCompactCollector::CreateBackPointers() { - HeapObjectIterator iterator(heap()->map_space()); - for (HeapObject* next_object = iterator.Next(); - next_object != NULL; next_object = iterator.Next()) { - if (next_object->IsMap()) { // Could also be FreeSpace object on free list. - Map* map = Map::cast(next_object); - STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); - if (map->instance_type() >= FIRST_JS_RECEIVER_TYPE) { - map->CreateBackPointers(); - } else { - ASSERT(map->instance_descriptors() == heap()->empty_descriptor_array()); - } - } - } -} - - // Fill the marking stack with overflowed objects returned by the given // iterator. Stop when the marking stack is filled or the end of the space // is reached, whichever comes first. @@ -2461,15 +2449,8 @@ void MarkCompactCollector::ReattachInitialMaps() { void MarkCompactCollector::ClearNonLiveTransitions() { HeapObjectIterator map_iterator(heap()->map_space()); // Iterate over the map space, setting map transitions that go from - // a marked map to an unmarked map to null transitions. At the same time, - // set all the prototype fields of maps back to their original value, - // dropping the back pointers temporarily stored in the prototype field. - // Setting the prototype field requires following the linked list of - // back pointers, reversing them all at once. This allows us to find - // those maps with map transitions that need to be nulled, and only - // scan the descriptor arrays of those maps, not all maps. - // All of these actions are carried out only on maps of JSObjects - // and related subtypes. + // a marked map to an unmarked map to null transitions. This action + // is carried out only on maps of JSObjects and related subtypes. for (HeapObject* obj = map_iterator.Next(); obj != NULL; obj = map_iterator.Next()) { Map* map = reinterpret_cast(obj); @@ -2545,36 +2526,16 @@ void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) { void MarkCompactCollector::ClearNonLiveMapTransitions(Map* map, MarkBit map_mark) { - // Follow the chain of back pointers to find the prototype. - Object* real_prototype = map; - while (real_prototype->IsMap()) { - real_prototype = Map::cast(real_prototype)->prototype(); - ASSERT(real_prototype->IsHeapObject()); - } + Object* potential_parent = map->GetBackPointer(); + if (!potential_parent->IsMap()) return; + Map* parent = Map::cast(potential_parent); - // Follow back pointers, setting them to prototype, clearing map transitions - // when necessary. - Map* current = map; + // Follow back pointer, check whether we are dealing with a map transition + // from a live map to a dead path and in case clear transitions of parent. bool current_is_alive = map_mark.Get(); - bool on_dead_path = !current_is_alive; - while (current->IsMap()) { - Object* next = current->prototype(); - // There should never be a dead map above a live map. - ASSERT(on_dead_path || current_is_alive); - - // A live map above a dead map indicates a dead transition. This test will - // always be false on the first iteration. - if (on_dead_path && current_is_alive) { - on_dead_path = false; - current->ClearNonLiveTransitions(heap(), real_prototype); - } - - Object** slot = HeapObject::RawField(current, Map::kPrototypeOffset); - *slot = real_prototype; - if (current_is_alive) RecordSlot(slot, slot, real_prototype); - - current = reinterpret_cast(next); - current_is_alive = Marking::MarkBitFrom(current).Get(); + bool parent_is_alive = Marking::MarkBitFrom(parent).Get(); + if (!current_is_alive && parent_is_alive) { + parent->ClearNonLiveTransitions(heap()); } } diff --git a/src/mark-compact.h b/src/mark-compact.h index f8488bb..6420a21 100644 --- a/src/mark-compact.h +++ b/src/mark-compact.h @@ -642,13 +642,6 @@ class MarkCompactCollector { void ProcessNewlyMarkedObject(HeapObject* obj); - // Creates back pointers for all map transitions, stores them in - // the prototype field. The original prototype pointers are restored - // in ClearNonLiveTransitions(). All JSObject maps - // connected by map transitions have the same prototype object, which - // is why we can use this field temporarily for back pointers. - void CreateBackPointers(); - // Mark a Map and its DescriptorArray together, skipping transitions. void MarkMapContents(Map* map); void MarkAccessorPairSlot(HeapObject* accessors, int offset); diff --git a/src/objects-debug.cc b/src/objects-debug.cc index cd2ccf8..3bfb74d 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -303,6 +303,8 @@ void Map::MapVerify() { instance_size() < HEAP->Capacity())); VerifyHeapPointer(prototype()); VerifyHeapPointer(instance_descriptors()); + SLOW_ASSERT(instance_descriptors()->IsSortedNoDuplicates()); + SLOW_ASSERT(instance_descriptors()->IsConsistentWithBackPointers(this)); } @@ -894,6 +896,61 @@ bool DescriptorArray::IsSortedNoDuplicates() { } +static bool CheckOneBackPointer(Map* current_map, Object* target) { + return !target->IsMap() || Map::cast(target)->GetBackPointer() == current_map; +} + + +bool DescriptorArray::IsConsistentWithBackPointers(Map* current_map) { + for (int i = 0; i < number_of_descriptors(); ++i) { + switch (GetType(i)) { + case MAP_TRANSITION: + case CONSTANT_TRANSITION: + if (!CheckOneBackPointer(current_map, GetValue(i))) { + return false; + } + break; + case ELEMENTS_TRANSITION: { + Object* object = GetValue(i); + if (!CheckOneBackPointer(current_map, object)) { + return false; + } + if (object->IsFixedArray()) { + FixedArray* array = FixedArray::cast(object); + for (int i = 0; i < array->length(); ++i) { + if (!CheckOneBackPointer(current_map, array->get(i))) { + return false; + } + } + } + break; + } + case CALLBACKS: { + Object* object = GetValue(i); + if (object->IsAccessorPair()) { + AccessorPair* accessors = AccessorPair::cast(object); + if (!CheckOneBackPointer(current_map, accessors->getter())) { + return false; + } + if (!CheckOneBackPointer(current_map, accessors->setter())) { + return false; + } + } + break; + } + case NORMAL: + case FIELD: + case CONSTANT_FUNCTION: + case HANDLER: + case INTERCEPTOR: + case NULL_DESCRIPTOR: + break; + } + } + return true; +} + + void JSFunctionResultCache::JSFunctionResultCacheVerify() { JSFunction::cast(get(kFactoryIndex))->Verify(); diff --git a/src/objects-inl.h b/src/objects-inl.h index 3aa3942..eb1586a 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -3407,14 +3407,66 @@ void Map::set_bit_field3(int value) { } -FixedArray* Map::unchecked_prototype_transitions() { - return reinterpret_cast( - READ_FIELD(this, kPrototypeTransitionsOffset)); +Object* Map::GetBackPointer() { + Object* object = READ_FIELD(this, kPrototypeTransitionsOrBackPointerOffset); + if (object->IsFixedArray()) { + return FixedArray::cast(object)->get(kProtoTransitionBackPointerOffset); + } else { + return object; + } +} + + +void Map::SetBackPointer(Object* value, WriteBarrierMode mode) { + Heap* heap = GetHeap(); + ASSERT(instance_type() >= FIRST_JS_RECEIVER_TYPE); + ASSERT((value->IsUndefined() && GetBackPointer()->IsMap()) || + (value->IsMap() && GetBackPointer()->IsUndefined())); + Object* object = READ_FIELD(this, kPrototypeTransitionsOrBackPointerOffset); + if (object->IsFixedArray()) { + FixedArray::cast(object)->set( + kProtoTransitionBackPointerOffset, value, mode); + } else { + WRITE_FIELD(this, kPrototypeTransitionsOrBackPointerOffset, value); + CONDITIONAL_WRITE_BARRIER( + heap, this, kPrototypeTransitionsOrBackPointerOffset, value, mode); + } +} + + +FixedArray* Map::prototype_transitions() { + Object* object = READ_FIELD(this, kPrototypeTransitionsOrBackPointerOffset); + if (object->IsFixedArray()) { + return FixedArray::cast(object); + } else { + return GetHeap()->empty_fixed_array(); + } +} + + +void Map::set_prototype_transitions(FixedArray* value, WriteBarrierMode mode) { + Heap* heap = GetHeap(); + ASSERT(value != heap->empty_fixed_array()); + value->set(kProtoTransitionBackPointerOffset, GetBackPointer()); + WRITE_FIELD(this, kPrototypeTransitionsOrBackPointerOffset, value); + CONDITIONAL_WRITE_BARRIER( + heap, this, kPrototypeTransitionsOrBackPointerOffset, value, mode); +} + + +void Map::init_prototype_transitions(Object* undefined) { + ASSERT(undefined->IsUndefined()); + WRITE_FIELD(this, kPrototypeTransitionsOrBackPointerOffset, undefined); +} + + +HeapObject* Map::unchecked_prototype_transitions() { + Object* object = READ_FIELD(this, kPrototypeTransitionsOrBackPointerOffset); + return reinterpret_cast(object); } ACCESSORS(Map, code_cache, Object, kCodeCacheOffset) -ACCESSORS(Map, prototype_transitions, FixedArray, kPrototypeTransitionsOffset) ACCESSORS(Map, constructor, Object, kConstructorOffset) ACCESSORS(JSFunction, shared, SharedFunctionInfo, kSharedFunctionInfoOffset) diff --git a/src/objects.cc b/src/objects.cc index 970fc24..cde43f9 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1604,6 +1604,7 @@ MaybeObject* JSObject::AddFastProperty(String* name, // We have now allocated all the necessary objects. // All the changes can be applied at once, so they are atomic. map()->set_instance_descriptors(old_descriptors); + new_map->SetBackPointer(map()); new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors)); set_map(new_map); return FastPropertyAtPut(index, value); @@ -1664,6 +1665,7 @@ MaybeObject* JSObject::AddConstantFunctionProperty( } } old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors)); + Map::cast(new_map)->SetBackPointer(old_map); return function; } @@ -1824,6 +1826,7 @@ MaybeObject* JSObject::ConvertDescriptorToFieldAndMapTransition( } } old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors)); + map()->SetBackPointer(old_map); return result; } @@ -2408,6 +2411,7 @@ MaybeObject* Map::AddElementsTransition(ElementsKind elements_kind, return maybe_new_descriptors; } set_instance_descriptors(DescriptorArray::cast(new_descriptors)); + transitioned_map->SetBackPointer(this); return this; } @@ -4653,6 +4657,7 @@ static MaybeObject* CreateFreshAccessor(JSObject* obj, // step 6: everything went well so far, so we make our changes visible obj->set_map(map2); map1->set_instance_descriptors(descriptors1); + map2->SetBackPointer(map1); return obj; } @@ -4705,6 +4710,7 @@ static MaybeObject* NewCallbackTransition(JSObject* obj, // step 4: everything went well so far, so we make our changes visible obj->set_map(map3); accessors2->set(component, map3); + map3->SetBackPointer(map2); return obj; } @@ -5137,7 +5143,7 @@ class IntrusiveMapTransitionIterator { // underlying array while it is running. class IntrusivePrototypeTransitionIterator { public: - explicit IntrusivePrototypeTransitionIterator(FixedArray* proto_trans) + explicit IntrusivePrototypeTransitionIterator(HeapObject* proto_trans) : proto_trans_(proto_trans) { } void Start() { @@ -5162,7 +5168,7 @@ class IntrusivePrototypeTransitionIterator { private: bool HasTransitions() { - return proto_trans_->length() >= Map::kProtoTransitionHeaderSize; + return proto_trans_->map()->IsSmi() || proto_trans_->IsFixedArray(); } Object** Header() { @@ -5170,12 +5176,16 @@ class IntrusivePrototypeTransitionIterator { } int NumberOfTransitions() { - Object* num = proto_trans_->get(Map::kProtoTransitionNumberOfEntriesOffset); + ASSERT(HasTransitions()); + FixedArray* proto_trans = reinterpret_cast(proto_trans_); + Object* num = proto_trans->get(Map::kProtoTransitionNumberOfEntriesOffset); return Smi::cast(num)->value(); } Map* GetTransition(int transitionNumber) { - return Map::cast(proto_trans_->get(IndexFor(transitionNumber))); + ASSERT(HasTransitions()); + FixedArray* proto_trans = reinterpret_cast(proto_trans_); + return Map::cast(proto_trans->get(IndexFor(transitionNumber))); } int IndexFor(int transitionNumber) { @@ -5184,7 +5194,7 @@ class IntrusivePrototypeTransitionIterator { transitionNumber * Map::kProtoTransitionElementsPerEntry; } - FixedArray* proto_trans_; + HeapObject* proto_trans_; }; @@ -7346,85 +7356,23 @@ void String::PrintOn(FILE* file) { } -void Map::CreateOneBackPointer(Object* transition_target) { - if (!transition_target->IsMap()) return; - Map* target = Map::cast(transition_target); -#ifdef DEBUG - // Verify target. - Object* source_prototype = prototype(); - Object* target_prototype = target->prototype(); - ASSERT(source_prototype->IsJSReceiver() || - source_prototype->IsMap() || - source_prototype->IsNull()); - ASSERT(target_prototype->IsJSReceiver() || - target_prototype->IsNull()); - ASSERT(source_prototype->IsMap() || - source_prototype == target_prototype); -#endif - // Point target back to source. set_prototype() will not let us set - // the prototype to a map, as we do here. - *RawField(target, kPrototypeOffset) = this; -} - - -void Map::CreateBackPointers() { - DescriptorArray* descriptors = instance_descriptors(); - for (int i = 0; i < descriptors->number_of_descriptors(); i++) { - switch (descriptors->GetType(i)) { - case MAP_TRANSITION: - case CONSTANT_TRANSITION: - CreateOneBackPointer(descriptors->GetValue(i)); - break; - case ELEMENTS_TRANSITION: { - Object* object = descriptors->GetValue(i); - if (object->IsMap()) { - CreateOneBackPointer(object); - } else { - FixedArray* array = FixedArray::cast(object); - for (int i = 0; i < array->length(); ++i) { - CreateOneBackPointer(array->get(i)); - } - } - break; - } - case CALLBACKS: { - Object* object = descriptors->GetValue(i); - if (object->IsAccessorPair()) { - AccessorPair* accessors = AccessorPair::cast(object); - CreateOneBackPointer(accessors->getter()); - CreateOneBackPointer(accessors->setter()); - } - break; - } - case NORMAL: - case FIELD: - case CONSTANT_FUNCTION: - case HANDLER: - case INTERCEPTOR: - case NULL_DESCRIPTOR: - break; - } - } -} - - -bool Map::RestoreOneBackPointer(Object* object, - Object* real_prototype, - bool* keep_entry) { - if (!object->IsMap()) return false; - Map* map = Map::cast(object); +// Clear a possible back pointer in case the transition leads to a dead map. +// Return true in case a back pointer has been cleared and false otherwise. +// Set *keep_entry to true when a live map transition has been found. +static bool ClearBackPointer(Heap* heap, Object* target, bool* keep_entry) { + if (!target->IsMap()) return false; + Map* map = Map::cast(target); if (Marking::MarkBitFrom(map).Get()) { *keep_entry = true; return false; + } else { + map->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER); + return true; } - ASSERT(map->prototype() == this || map->prototype() == real_prototype); - // Getter prototype() is read-only, set_prototype() has side effects. - *RawField(map, Map::kPrototypeOffset) = real_prototype; - return true; } -void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) { +void Map::ClearNonLiveTransitions(Heap* heap) { DescriptorArray* d = DescriptorArray::cast( *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset)); if (d->IsEmpty()) return; @@ -7437,24 +7385,22 @@ void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) { // If the pair (value, details) is a map transition, check if the target is // live. If not, null the descriptor. Also drop the back pointer for that // map transition, so that this map is not reached again by following a back - // pointer from a non-live object. + // pointer from that non-live map. bool keep_entry = false; PropertyDetails details(Smi::cast(contents->get(i + 1))); switch (details.type()) { case MAP_TRANSITION: case CONSTANT_TRANSITION: - RestoreOneBackPointer(contents->get(i), real_prototype, &keep_entry); + ClearBackPointer(heap, contents->get(i), &keep_entry); break; case ELEMENTS_TRANSITION: { Object* object = contents->get(i); if (object->IsMap()) { - RestoreOneBackPointer(object, real_prototype, &keep_entry); + ClearBackPointer(heap, object, &keep_entry); } else { FixedArray* array = FixedArray::cast(object); for (int j = 0; j < array->length(); ++j) { - if (RestoreOneBackPointer(array->get(j), - real_prototype, - &keep_entry)) { + if (ClearBackPointer(heap, array->get(j), &keep_entry)) { array->set_undefined(j); } } @@ -7465,14 +7411,10 @@ void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) { Object* object = contents->get(i); if (object->IsAccessorPair()) { AccessorPair* accessors = AccessorPair::cast(object); - if (RestoreOneBackPointer(accessors->getter(), - real_prototype, - &keep_entry)) { + if (ClearBackPointer(heap, accessors->getter(), &keep_entry)) { accessors->set_getter(heap->the_hole_value()); } - if (RestoreOneBackPointer(accessors->setter(), - real_prototype, - &keep_entry)) { + if (ClearBackPointer(heap, accessors->setter(), &keep_entry)) { accessors->set_setter(heap->the_hole_value()); } } else { diff --git a/src/objects.h b/src/objects.h index 6ced390..e11cbdc 100644 --- a/src/objects.h +++ b/src/objects.h @@ -2609,6 +2609,9 @@ class DescriptorArray: public FixedArray { // Is the descriptor array sorted and without duplicates? bool IsSortedNoDuplicates(); + // Is the descriptor array consistent with the back pointers in targets? + bool IsConsistentWithBackPointers(Map* current_map); + // Are two DescriptorArrays equal? bool IsEqualTo(DescriptorArray* other); #endif @@ -4719,19 +4722,30 @@ class Map: public HeapObject { // [stub cache]: contains stubs compiled for this map. DECL_ACCESSORS(code_cache, Object) + // [back pointer]: points back to the parent map from which a transition + // leads to this map. The field overlaps with prototype transitions and the + // back pointer will be moved into the prototype transitions array if + // required. + inline Object* GetBackPointer(); + inline void SetBackPointer(Object* value, + WriteBarrierMode mode = UPDATE_WRITE_BARRIER); + // [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 + 2 * i: prototype - // 2 + 2 * i: target map + // 1: back pointer that overlaps with prototype transitions field. + // 2 + 2 * i: prototype + // 3 + 2 * i: target map DECL_ACCESSORS(prototype_transitions, FixedArray) - inline FixedArray* unchecked_prototype_transitions(); + inline void init_prototype_transitions(Object* undefined); + inline HeapObject* unchecked_prototype_transitions(); - static const int kProtoTransitionHeaderSize = 1; + static const int kProtoTransitionHeaderSize = 2; static const int kProtoTransitionNumberOfEntriesOffset = 0; + static const int kProtoTransitionBackPointerOffset = 1; static const int kProtoTransitionElementsPerEntry = 2; static const int kProtoTransitionPrototypeOffset = 0; static const int kProtoTransitionMapOffset = 1; @@ -4803,25 +4817,10 @@ class Map: public HeapObject { // Removes a code object from the code cache at the given index. void RemoveFromCodeCache(String* name, Code* code, int index); - // For every transition in this map, makes the transition's - // target's prototype pointer point back to this map. - // This is undone in MarkCompactCollector::ClearNonLiveTransitions(). - void CreateBackPointers(); - - void CreateOneBackPointer(Object* transition_target); - - // Set all map transitions from this map to dead maps to null. - // Also, restore the original prototype on the targets of these - // transitions, so that we do not process this map again while - // following back pointers. - void ClearNonLiveTransitions(Heap* heap, Object* real_prototype); - - // Restore a possible back pointer in the prototype field of object. - // Return true in that case and false otherwise. Set *keep_entry to - // true when a live map transition has been found. - bool RestoreOneBackPointer(Object* object, - Object* real_prototype, - bool* keep_entry); + // 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(); @@ -4903,16 +4902,17 @@ class Map: public HeapObject { kConstructorOffset + kPointerSize; static const int kCodeCacheOffset = kInstanceDescriptorsOrBitField3Offset + kPointerSize; - static const int kPrototypeTransitionsOffset = + static const int kPrototypeTransitionsOrBackPointerOffset = kCodeCacheOffset + kPointerSize; - static const int kPadStart = kPrototypeTransitionsOffset + kPointerSize; + static const int kPadStart = + kPrototypeTransitionsOrBackPointerOffset + kPointerSize; static const int kSize = MAP_POINTER_ALIGN(kPadStart); // Layout of pointer fields. Heap iteration code relies on them // being continuously allocated. static const int kPointerFieldsBeginOffset = Map::kPrototypeOffset; static const int kPointerFieldsEndOffset = - Map::kPrototypeTransitionsOffset + kPointerSize; + kPrototypeTransitionsOrBackPointerOffset + kPointerSize; // Byte offsets within kInstanceSizesOffset. static const int kInstanceSizeOffset = kInstanceSizesOffset + 0; diff --git a/src/profile-generator.cc b/src/profile-generator.cc index c91e83b..821b1d0 100644 --- a/src/profile-generator.cc +++ b/src/profile-generator.cc @@ -2146,10 +2146,16 @@ void V8HeapExplorer::ExtractMapReferences(HeapEntry* entry, Map* map) { "descriptors", map->instance_descriptors(), Map::kInstanceDescriptorsOrBitField3Offset); } - TagObject(map->prototype_transitions(), "(prototype transitions)"); - SetInternalReference(map, entry, - "prototype_transitions", map->prototype_transitions(), - Map::kPrototypeTransitionsOffset); + if (map->unchecked_prototype_transitions()->IsFixedArray()) { + TagObject(map->prototype_transitions(), "(prototype transitions)"); + SetInternalReference(map, entry, + "prototype_transitions", map->prototype_transitions(), + Map::kPrototypeTransitionsOrBackPointerOffset); + } else { + SetInternalReference(map, entry, + "back_pointer", map->GetBackPointer(), + Map::kPrototypeTransitionsOrBackPointerOffset); + } SetInternalReference(map, entry, "code_cache", map->code_cache(), Map::kCodeCacheOffset); -- 2.7.4