Implement map collection for incremental marking.
authormstarzinger@chromium.org <mstarzinger@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 15 May 2012 08:39:25 +0000 (08:39 +0000)
committermstarzinger@chromium.org <mstarzinger@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 15 May 2012 08:39:25 +0000 (08:39 +0000)
This causes map transitions to be treated weakly during incremental
marking and hence allows clearing of non-live transitions. The marking
code is now shared between incremental and non-incremental mode.

R=vegorov@chromium.org
BUG=v8:1465
TEST=cctest/test-heap/Regress1465

Review URL: https://chromiumcodereview.appspot.com/10386046

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11556 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/incremental-marking-inl.h
src/incremental-marking.cc
src/incremental-marking.h
src/mark-compact-inl.h
src/mark-compact.cc
src/mark-compact.h
test/cctest/test-heap.cc

index 5ce003f..2dae6f2 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
@@ -118,13 +118,29 @@ void IncrementalMarking::BlackToGreyAndUnshift(HeapObject* obj,
 
 
 void IncrementalMarking::WhiteToGreyAndPush(HeapObject* obj, MarkBit mark_bit) {
-  WhiteToGrey(obj, mark_bit);
+  Marking::WhiteToGrey(mark_bit);
   marking_deque_.PushGrey(obj);
 }
 
 
-void IncrementalMarking::WhiteToGrey(HeapObject* obj, MarkBit mark_bit) {
-  Marking::WhiteToGrey(mark_bit);
+bool IncrementalMarking::MarkObjectAndPush(HeapObject* obj) {
+  MarkBit mark_bit = Marking::MarkBitFrom(obj);
+  if (!mark_bit.Get()) {
+    WhiteToGreyAndPush(obj, mark_bit);
+    return true;
+  }
+  return false;
+}
+
+
+bool IncrementalMarking::MarkObjectWithoutPush(HeapObject* obj) {
+  MarkBit mark_bit = Marking::MarkBitFrom(obj);
+  if (!mark_bit.Get()) {
+    mark_bit.Set();
+    MemoryChunk::IncrementLiveBytesFromGC(obj->address(), obj->Size());
+    return true;
+  }
+  return false;
 }
 
 
index 5b58c9d..94afffa 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
@@ -42,6 +42,7 @@ IncrementalMarking::IncrementalMarking(Heap* heap)
       state_(STOPPED),
       marking_deque_memory_(NULL),
       marking_deque_memory_committed_(false),
+      marker_(this, heap->mark_compact_collector()),
       steps_count_(0),
       steps_took_(0),
       longest_step_(0.0),
@@ -663,6 +664,22 @@ void IncrementalMarking::Hurry() {
       } else if (map == global_context_map) {
         // Global contexts have weak fields.
         VisitGlobalContext(Context::cast(obj), &marking_visitor);
+      } else if (map->instance_type() == MAP_TYPE) {
+        Map* map = Map::cast(obj);
+        heap_->ClearCacheOnMap(map);
+
+        // When map collection is enabled we have to mark through map's
+        // transitions and back pointers in a special way to make these links
+        // weak.  Only maps for subclasses of JSReceiver can have transitions.
+        STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
+        if (FLAG_collect_maps &&
+            map->instance_type() >= FIRST_JS_RECEIVER_TYPE) {
+          marker_.MarkMapContents(map);
+        } else {
+          marking_visitor.VisitPointers(
+              HeapObject::RawField(map, Map::kPointerFieldsBeginOffset),
+              HeapObject::RawField(map, Map::kPointerFieldsEndOffset));
+        }
       } else {
         obj->Iterate(&marking_visitor);
       }
@@ -807,12 +824,6 @@ void IncrementalMarking::Step(intptr_t allocated_bytes,
       Map* map = obj->map();
       if (map == filler_map) continue;
 
-      if (obj->IsMap()) {
-        Map* map = Map::cast(obj);
-        heap_->ClearCacheOnMap(map);
-      }
-
-
       int size = obj->SizeFromMap(map);
       bytes_to_process -= size;
       MarkBit map_mark_bit = Marking::MarkBitFrom(map);
@@ -830,6 +841,22 @@ void IncrementalMarking::Step(intptr_t allocated_bytes,
         MarkObjectGreyDoNotEnqueue(ctx->normalized_map_cache());
 
         VisitGlobalContext(ctx, &marking_visitor);
+      } else if (map->instance_type() == MAP_TYPE) {
+        Map* map = Map::cast(obj);
+        heap_->ClearCacheOnMap(map);
+
+        // When map collection is enabled we have to mark through map's
+        // transitions and back pointers in a special way to make these links
+        // weak.  Only maps for subclasses of JSReceiver can have transitions.
+        STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
+        if (FLAG_collect_maps &&
+            map->instance_type() >= FIRST_JS_RECEIVER_TYPE) {
+          marker_.MarkMapContents(map);
+        } else {
+          marking_visitor.VisitPointers(
+              HeapObject::RawField(map, Map::kPointerFieldsBeginOffset),
+              HeapObject::RawField(map, Map::kPointerFieldsEndOffset));
+        }
       } else if (map->instance_type() == JS_FUNCTION_TYPE) {
         marking_visitor.VisitPointers(
             HeapObject::RawField(obj, JSFunction::kPropertiesOffset),
index 8cbe6c1..39e8dae 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
@@ -154,8 +154,6 @@ class IncrementalMarking {
 
   inline void WhiteToGreyAndPush(HeapObject* obj, MarkBit mark_bit);
 
-  inline void WhiteToGrey(HeapObject* obj, MarkBit mark_bit);
-
   // Does white->black or keeps gray or black color. Returns true if converting
   // white to black.
   inline bool MarkBlackOrKeepGrey(MarkBit mark_bit) {
@@ -169,6 +167,16 @@ class IncrementalMarking {
     return true;
   }
 
+  // Marks the object grey and pushes it on the marking stack.
+  // Returns true if object needed marking and false otherwise.
+  // This is for incremental marking only.
+  INLINE(bool MarkObjectAndPush(HeapObject* obj));
+
+  // Marks the object black without pushing it on the marking stack.
+  // Returns true if object needed marking and false otherwise.
+  // This is for incremental marking only.
+  INLINE(bool MarkObjectWithoutPush(HeapObject* obj));
+
   inline int steps_count() {
     return steps_count_;
   }
@@ -260,6 +268,7 @@ class IncrementalMarking {
   VirtualMemory* marking_deque_memory_;
   bool marking_deque_memory_committed_;
   MarkingDeque marking_deque_;
+  Marker<IncrementalMarking> marker_;
 
   int steps_count_;
   double steps_took_;
index 43f6b89..2f7e31f 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2011 the V8 project authors. All rights reserved.
+// Copyright 2012 the V8 project authors. All rights reserved.
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
@@ -52,6 +52,15 @@ void MarkCompactCollector::SetFlags(int flags) {
 }
 
 
+bool MarkCompactCollector::MarkObjectAndPush(HeapObject* obj) {
+  if (MarkObjectWithoutPush(obj)) {
+    marking_deque_.PushBlack(obj);
+    return true;
+  }
+  return false;
+}
+
+
 void MarkCompactCollector::MarkObject(HeapObject* obj, MarkBit mark_bit) {
   ASSERT(Marking::MarkBitFrom(obj) == mark_bit);
   if (!mark_bit.Get()) {
@@ -62,16 +71,13 @@ void MarkCompactCollector::MarkObject(HeapObject* obj, MarkBit mark_bit) {
 }
 
 
-bool MarkCompactCollector::MarkObjectWithoutPush(HeapObject* object) {
-  MarkBit mark = Marking::MarkBitFrom(object);
-  bool old_mark = mark.Get();
-  if (!old_mark) SetMark(object, mark);
-  return old_mark;
-}
-
-
-void MarkCompactCollector::MarkObjectAndPush(HeapObject* object) {
-  if (!MarkObjectWithoutPush(object)) marking_deque_.PushBlack(object);
+bool MarkCompactCollector::MarkObjectWithoutPush(HeapObject* obj) {
+  MarkBit mark_bit = Marking::MarkBitFrom(obj);
+  if (!mark_bit.Get()) {
+    SetMark(obj, mark_bit);
+    return true;
+  }
+  return false;
 }
 
 
index 0aa1192..c455564 100644 (file)
@@ -64,13 +64,13 @@ MarkCompactCollector::MarkCompactCollector() :  // NOLINT
       abort_incremental_marking_(false),
       compacting_(false),
       was_marked_incrementally_(false),
-      collect_maps_(FLAG_collect_maps),
       flush_monomorphic_ics_(false),
       tracer_(NULL),
       migration_slots_buffer_(NULL),
       heap_(NULL),
       code_flusher_(NULL),
-      encountered_weak_maps_(NULL) { }
+      encountered_weak_maps_(NULL),
+      marker_(this, this) { }
 
 
 #ifdef DEBUG
@@ -282,7 +282,7 @@ void MarkCompactCollector::CollectGarbage() {
   MarkLiveObjects();
   ASSERT(heap_->incremental_marking()->IsStopped());
 
-  if (collect_maps_) ClearNonLiveTransitions();
+  if (FLAG_collect_maps) ClearNonLiveTransitions();
 
   ClearWeakMaps();
 
@@ -294,7 +294,7 @@ void MarkCompactCollector::CollectGarbage() {
 
   SweepSpaces();
 
-  if (!collect_maps_) ReattachInitialMaps();
+  if (!FLAG_collect_maps) ReattachInitialMaps();
 
   Finish();
 
@@ -658,11 +658,6 @@ void MarkCompactCollector::AbortCompaction() {
 void MarkCompactCollector::Prepare(GCTracer* tracer) {
   was_marked_incrementally_ = heap()->incremental_marking()->IsMarking();
 
-  // Disable collection of maps if incremental marking is enabled.
-  // Map collection algorithm relies on a special map transition tree traversal
-  // order which is not implemented for incremental marking.
-  collect_maps_ = FLAG_collect_maps && !was_marked_incrementally_;
-
   // Monomorphic ICs are preserved when possible, but need to be flushed
   // when they might be keeping a Context alive, or when the heap is about
   // to be serialized.
@@ -1798,11 +1793,11 @@ void MarkCompactCollector::ProcessNewlyMarkedObject(HeapObject* object) {
     heap_->ClearCacheOnMap(map);
 
     // When map collection is enabled we have to mark through map's transitions
-    // in a special way to make transition links weak.
-    // Only maps for subclasses of JSReceiver can have transitions.
+    // in a special way to make transition links weak. Only maps for subclasses
+    // of JSReceiver can have transitions.
     STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
-    if (collect_maps_ && map->instance_type() >= FIRST_JS_RECEIVER_TYPE) {
-      MarkMapContents(map);
+    if (FLAG_collect_maps && map->instance_type() >= FIRST_JS_RECEIVER_TYPE) {
+      marker_.MarkMapContents(map);
     } else {
       marking_deque_.PushBlack(map);
     }
@@ -1812,85 +1807,86 @@ void MarkCompactCollector::ProcessNewlyMarkedObject(HeapObject* object) {
 }
 
 
-void MarkCompactCollector::MarkMapContents(Map* map) {
+// Force instantiation of template instances.
+template void Marker<IncrementalMarking>::MarkMapContents(Map* map);
+template void Marker<MarkCompactCollector>::MarkMapContents(Map* map);
+
+
+template <class T>
+void Marker<T>::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. 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);
+  // transitions in ClearNonLiveTransitions.
+  Object** proto_trans_slot =
+      HeapObject::RawField(map, Map::kPrototypeTransitionsOrBackPointerOffset);
+  HeapObject* prototype_transitions = HeapObject::cast(*proto_trans_slot);
+  if (prototype_transitions->IsFixedArray()) {
+    mark_compact_collector()->RecordSlot(proto_trans_slot,
+                                         proto_trans_slot,
+                                         prototype_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 =
+  // 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.
+  base_marker()->MarkObjectAndPush(HeapObject::cast(map->GetBackPointer()));
+
+  Object** descriptor_array_slot =
       HeapObject::RawField(map, Map::kInstanceDescriptorsOrBitField3Offset);
-  Object* raw_descriptor_array = *raw_descriptor_array_slot;
-  if (!raw_descriptor_array->IsSmi()) {
-    MarkDescriptorArray(
-        reinterpret_cast<DescriptorArray*>(raw_descriptor_array));
+  Object* descriptor_array = *descriptor_array_slot;
+  if (!descriptor_array->IsSmi()) {
+    MarkDescriptorArray(reinterpret_cast<DescriptorArray*>(descriptor_array));
+  }
+
+  // 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 and prototype transitions.
+  STATIC_ASSERT(Map::kPointerFieldsEndOffset ==
+      Map::kPrototypeTransitionsOrBackPointerOffset + kPointerSize);
+  Object** start_slot = HeapObject::RawField(
+      map, Map::kPointerFieldsBeginOffset);
+  Object** end_slot = HeapObject::RawField(
+      map, Map::kPrototypeTransitionsOrBackPointerOffset);
+  for (Object** slot = start_slot; slot < end_slot; slot++) {
+    Object* obj = *slot;
+    if (!obj->NonFailureIsHeapObject()) continue;
+    mark_compact_collector()->RecordSlot(start_slot, slot, obj);
+    base_marker()->MarkObjectAndPush(reinterpret_cast<HeapObject*>(obj));
   }
-
-  // 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.
-  Object** start_slot = HeapObject::RawField(map,
-                                             Map::kPointerFieldsBeginOffset);
-
-  Object** end_slot = HeapObject::RawField(map, Map::kPointerFieldsEndOffset);
-
-  StaticMarkingVisitor::VisitPointers(map->GetHeap(), start_slot, end_slot);
 }
 
 
-void MarkCompactCollector::MarkAccessorPairSlot(HeapObject* accessors,
-                                                int offset) {
-  Object** slot = HeapObject::RawField(accessors, offset);
-  HeapObject* accessor = HeapObject::cast(*slot);
-  if (accessor->IsMap()) return;
-  RecordSlot(slot, slot, accessor);
-  MarkObjectAndPush(accessor);
-}
-
-
-void MarkCompactCollector::MarkDescriptorArray(
-    DescriptorArray* descriptors) {
-  MarkBit descriptors_mark = Marking::MarkBitFrom(descriptors);
-  if (descriptors_mark.Get()) return;
+template <class T>
+void Marker<T>::MarkDescriptorArray(DescriptorArray* descriptors) {
   // Empty descriptor array is marked as a root before any maps are marked.
-  ASSERT(descriptors != heap()->empty_descriptor_array());
-  SetMark(descriptors, descriptors_mark);
+  ASSERT(descriptors != descriptors->GetHeap()->empty_descriptor_array());
 
-  FixedArray* contents = reinterpret_cast<FixedArray*>(
+  // The DescriptorArray contains a pointer to its contents array, but the
+  // contents array will be marked black and hence not be visited again.
+  if (!base_marker()->MarkObjectAndPush(descriptors)) return;
+  FixedArray* contents = FixedArray::cast(
       descriptors->get(DescriptorArray::kContentArrayIndex));
-  ASSERT(contents->IsHeapObject());
-  ASSERT(!IsMarked(contents));
-  ASSERT(contents->IsFixedArray());
   ASSERT(contents->length() >= 2);
-  MarkBit contents_mark = Marking::MarkBitFrom(contents);
-  SetMark(contents, contents_mark);
-  // Contents contains (value, details) pairs.  If the details say that the type
-  // of descriptor is MAP_TRANSITION, CONSTANT_TRANSITION,
-  // EXTERNAL_ARRAY_TRANSITION or NULL_DESCRIPTOR, we don't mark the value as
-  // live.  Only for MAP_TRANSITION, EXTERNAL_ARRAY_TRANSITION and
-  // CONSTANT_TRANSITION is the value an Object* (a Map*).
+  ASSERT(Marking::IsWhite(Marking::MarkBitFrom(contents)));
+  base_marker()->MarkObjectWithoutPush(contents);
+
+  // Contents contains (value, details) pairs.  If the descriptor contains a
+  // transition (value is a Map), we don't mark the value as live.  It might
+  // be set to the NULL_DESCRIPTOR in ClearNonLiveTransitions later.
   for (int i = 0; i < contents->length(); i += 2) {
-    // If the pair (value, details) at index i, i+1 is not
-    // a transition or null descriptor, mark the value.
     PropertyDetails details(Smi::cast(contents->get(i + 1)));
 
     Object** slot = contents->data_start() + i;
     if (!(*slot)->IsHeapObject()) continue;
     HeapObject* value = HeapObject::cast(*slot);
 
-    RecordSlot(slot, slot, *slot);
+    mark_compact_collector()->RecordSlot(slot, slot, *slot);
 
     switch (details.type()) {
       case NORMAL:
@@ -1898,21 +1894,22 @@ void MarkCompactCollector::MarkDescriptorArray(
       case CONSTANT_FUNCTION:
       case HANDLER:
       case INTERCEPTOR:
-        MarkObjectAndPush(value);
+        base_marker()->MarkObjectAndPush(value);
         break;
       case CALLBACKS:
         if (!value->IsAccessorPair()) {
-          MarkObjectAndPush(value);
-        } else if (!MarkObjectWithoutPush(value)) {
-          MarkAccessorPairSlot(value, AccessorPair::kGetterOffset);
-          MarkAccessorPairSlot(value, AccessorPair::kSetterOffset);
+          base_marker()->MarkObjectAndPush(value);
+        } else if (base_marker()->MarkObjectWithoutPush(value)) {
+          AccessorPair* accessors = AccessorPair::cast(value);
+          MarkAccessorPairSlot(accessors, AccessorPair::kGetterOffset);
+          MarkAccessorPairSlot(accessors, AccessorPair::kSetterOffset);
         }
         break;
       case ELEMENTS_TRANSITION:
         // For maps with multiple elements transitions, the transition maps are
         // stored in a FixedArray. Keep the fixed array alive but not the maps
         // that it refers to.
-        if (value->IsFixedArray()) MarkObjectWithoutPush(value);
+        if (value->IsFixedArray()) base_marker()->MarkObjectWithoutPush(value);
         break;
       case MAP_TRANSITION:
       case CONSTANT_TRANSITION:
@@ -1920,9 +1917,16 @@ void MarkCompactCollector::MarkDescriptorArray(
         break;
     }
   }
-  // The DescriptorArray descriptors contains a pointer to its contents array,
-  // but the contents array is already marked.
-  marking_deque_.PushBlack(descriptors);
+}
+
+
+template <class T>
+void Marker<T>::MarkAccessorPairSlot(AccessorPair* accessors, int offset) {
+  Object** slot = HeapObject::RawField(accessors, offset);
+  HeapObject* accessor = HeapObject::cast(*slot);
+  if (accessor->IsMap()) return;
+  mark_compact_collector()->RecordSlot(slot, slot, accessor);
+  base_marker()->MarkObjectAndPush(accessor);
 }
 
 
index 6420a21..dbc2869 100644 (file)
@@ -42,6 +42,7 @@ typedef bool (*IsAliveFunction)(HeapObject* obj, int* size, int* offset);
 // Forward declarations.
 class CodeFlusher;
 class GCTracer;
+class MarkCompactCollector;
 class MarkingVisitor;
 class RootMarkingVisitor;
 
@@ -166,7 +167,6 @@ class Marking {
 
 // ----------------------------------------------------------------------------
 // Marking deque for tracing live objects.
-
 class MarkingDeque {
  public:
   MarkingDeque()
@@ -383,6 +383,34 @@ class SlotsBuffer {
 };
 
 
+// -------------------------------------------------------------------------
+// Marker shared between incremental and non-incremental marking
+template<class BaseMarker> class Marker {
+ public:
+  Marker(BaseMarker* base_marker, MarkCompactCollector* mark_compact_collector)
+      : base_marker_(base_marker),
+        mark_compact_collector_(mark_compact_collector) {}
+
+  // 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 MarkAccessorPairSlot(AccessorPair* accessors, int offset);
+
+ private:
+  BaseMarker* base_marker() {
+    return base_marker_;
+  }
+
+  MarkCompactCollector* mark_compact_collector() {
+    return mark_compact_collector_;
+  }
+
+  BaseMarker* base_marker_;
+  MarkCompactCollector* mark_compact_collector_;
+};
+
+
 // Defined in isolate.h.
 class ThreadLocalTop;
 
@@ -584,8 +612,6 @@ class MarkCompactCollector {
 
   bool was_marked_incrementally_;
 
-  bool collect_maps_;
-
   bool flush_monomorphic_ics_;
 
   // A pointer to the current stack-allocated GC tracer object during a full
@@ -608,12 +634,13 @@ class MarkCompactCollector {
   //
   //   After: Live objects are marked and non-live objects are unmarked.
 
-
   friend class RootMarkingVisitor;
   friend class MarkingVisitor;
   friend class StaticMarkingVisitor;
   friend class CodeMarkingVisitor;
   friend class SharedFunctionInfoMarkingVisitor;
+  friend class Marker<IncrementalMarking>;
+  friend class Marker<MarkCompactCollector>;
 
   // Mark non-optimize code for functions inlined into the given optimized
   // code. This will prevent it from being flushed.
@@ -631,22 +658,25 @@ class MarkCompactCollector {
   void AfterMarking();
 
   // Marks the object black and pushes it on the marking stack.
-  // This is for non-incremental marking.
+  // Returns true if object needed marking and false otherwise.
+  // This is for non-incremental marking only.
+  INLINE(bool MarkObjectAndPush(HeapObject* obj));
+
+  // Marks the object black and pushes it on the marking stack.
+  // This is for non-incremental marking only.
   INLINE(void MarkObject(HeapObject* obj, MarkBit mark_bit));
 
-  INLINE(bool MarkObjectWithoutPush(HeapObject* object));
-  INLINE(void MarkObjectAndPush(HeapObject* value));
+  // Marks the object black without pushing it on the marking stack.
+  // Returns true if object needed marking and false otherwise.
+  // This is for non-incremental marking only.
+  INLINE(bool MarkObjectWithoutPush(HeapObject* obj));
 
-  // Marks the object black.  This is for non-incremental marking.
+  // Marks the object black assuming that it is not yet marked.
+  // This is for non-incremental marking only.
   INLINE(void SetMark(HeapObject* obj, MarkBit mark_bit));
 
   void ProcessNewlyMarkedObject(HeapObject* obj);
 
-  // Mark a Map and its DescriptorArray together, skipping transitions.
-  void MarkMapContents(Map* map);
-  void MarkAccessorPairSlot(HeapObject* accessors, int offset);
-  void MarkDescriptorArray(DescriptorArray* descriptors);
-
   // Mark the heap roots and all objects reachable from them.
   void MarkRoots(RootMarkingVisitor* visitor);
 
@@ -749,6 +779,7 @@ class MarkCompactCollector {
   MarkingDeque marking_deque_;
   CodeFlusher* code_flusher_;
   Object* encountered_weak_maps_;
+  Marker<MarkCompactCollector> marker_;
 
   List<Page*> evacuation_candidates_;
   List<Code*> invalidated_code_;
index 72079dc..81a57d5 100644 (file)
@@ -1735,3 +1735,60 @@ TEST(OptimizedAllocationAlwaysInNewSpace) {
 
   CHECK(HEAP->InNewSpace(*o));
 }
+
+
+static int CountMapTransitions(Map* map) {
+  int result = 0;
+  DescriptorArray* descs = map->instance_descriptors();
+  for (int i = 0; i < descs->number_of_descriptors(); i++) {
+    if (descs->IsTransitionOnly(i)) {
+      result++;
+    }
+  }
+  return result;
+}
+
+
+// Test that map transitions are cleared and maps are collected with
+// incremental marking as well.
+TEST(Regress1465) {
+  i::FLAG_allow_natives_syntax = true;
+  i::FLAG_trace_incremental_marking = true;
+  InitializeVM();
+  v8::HandleScope scope;
+
+  #define TRANSITION_COUNT 256
+  for (int i = 0; i < TRANSITION_COUNT; i++) {
+    EmbeddedVector<char, 32> buffer;
+    OS::SNPrintF(buffer, "var o = {}; o.prop%d = %d;", i);
+    CompileRun(buffer.start());
+  }
+  CompileRun("var root = {};");
+  Handle<JSObject> root =
+      v8::Utils::OpenHandle(
+          *v8::Handle<v8::Object>::Cast(
+              v8::Context::GetCurrent()->Global()->Get(v8_str("root"))));
+
+  // Count number of live transitions before marking.
+  int transitions_before = CountMapTransitions(root->map());
+  CompileRun("%DebugPrint(root);");
+  CHECK_EQ(TRANSITION_COUNT, transitions_before);
+
+  // Go through all incremental marking steps in one swoop.
+  IncrementalMarking* marking = HEAP->incremental_marking();
+  CHECK(marking->IsStopped());
+  marking->Start();
+  CHECK(marking->IsMarking());
+  while (!marking->IsComplete()) {
+    marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
+  }
+  CHECK(marking->IsComplete());
+  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
+  CHECK(marking->IsStopped());
+
+  // Count number of live transitions after marking.  Note that one transition
+  // is left, because 'o' still holds an instance of one transition target.
+  int transitions_after = CountMapTransitions(root->map());
+  CompileRun("%DebugPrint(root);");
+  CHECK_EQ(1, transitions_after);
+}