In-place shrinking of descriptor arrays with non-live transitions.
authorverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 25 Jun 2012 13:10:54 +0000 (13:10 +0000)
committerverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 25 Jun 2012 13:10:54 +0000 (13:10 +0000)
Instead of overwriting non-live transitions with NULL_DESCRIPTORs, we remove them from the array by compacting the array (shifting live values to the left) and in-place trimming the array. If the final descriptor array contains no live values (only contained transitions which are now all cleared), we move bit_field3 back from the descriptor array to the map. The descriptor array itself will be collected in the next GC.

BUG=
TEST=

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

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

15 files changed:
src/ast.cc
src/bootstrapper.cc
src/factory.cc
src/ic.cc
src/mark-compact.cc
src/objects-debug.cc
src/objects-inl.h
src/objects-printer.cc
src/objects.cc
src/objects.h
src/profile-generator.cc
src/property-details.h
src/property.cc
src/property.h
src/runtime.cc

index b790e25..fb9db7c 100644 (file)
@@ -520,9 +520,11 @@ bool Call::ComputeTarget(Handle<Map> type, Handle<String> name) {
           return false;
         case MAP_TRANSITION:
         case CONSTANT_TRANSITION:
-        case NULL_DESCRIPTOR:
           // Perhaps something interesting is up in the prototype chain...
           break;
+        case NONEXISTENT:
+          UNREACHABLE();
+          break;
       }
     }
     // If we reach the end of the prototype chain, we don't know the target.
index b67deae..e910a43 100644 (file)
@@ -2193,13 +2193,13 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from,
         }
         case MAP_TRANSITION:
         case CONSTANT_TRANSITION:
-        case NULL_DESCRIPTOR:
           // Ignore non-properties.
           break;
         case NORMAL:
           // Do not occur since the from object has fast properties.
         case HANDLER:
         case INTERCEPTOR:
+        case NONEXISTENT:
           // No element in instance descriptors have proxy or interceptor type.
           UNREACHABLE();
           break;
index c33b715..46d4d37 100644 (file)
@@ -922,25 +922,18 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors(
     Handle<Object> descriptors) {
   v8::NeanderArray callbacks(descriptors);
   int nof_callbacks = callbacks.length();
+  int descriptor_count = array->number_of_descriptors();
   Handle<DescriptorArray> result =
-      NewDescriptorArray(array->number_of_descriptors() + nof_callbacks);
-
-  // Number of descriptors added to the result so far.
-  int descriptor_count = 0;
+      NewDescriptorArray(descriptor_count + nof_callbacks);
 
   // Ensure that marking will not progress and change color of objects.
   DescriptorArray::WhitenessWitness witness(*result);
 
   // Copy the descriptors from the array.
-  for (int i = 0; i < array->number_of_descriptors(); i++) {
-    if (!array->IsNullDescriptor(i)) {
-      DescriptorArray::CopyFrom(result, descriptor_count++, array, i, witness);
-    }
+  for (int i = 0; i < descriptor_count; i++) {
+    DescriptorArray::CopyFrom(result, i, array, i, witness);
   }
 
-  // Number of duplicates detected.
-  int duplicates = 0;
-
   // Fill in new callback descriptors.  Process the callbacks from
   // back to front so that the last callback with a given name takes
   // precedence over previously added callbacks with that name.
@@ -956,18 +949,14 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors(
       CallbacksDescriptor desc(*key, *entry, entry->property_attributes());
       result->Set(descriptor_count, &desc, witness);
       descriptor_count++;
-    } else {
-      duplicates++;
     }
   }
 
   // If duplicates were detected, allocate a result of the right size
   // and transfer the elements.
-  if (duplicates > 0) {
-    int number_of_descriptors = result->number_of_descriptors() - duplicates;
-    Handle<DescriptorArray> new_result =
-        NewDescriptorArray(number_of_descriptors);
-    for (int i = 0; i < number_of_descriptors; i++) {
+  if (descriptor_count < result->length()) {
+    Handle<DescriptorArray> new_result = NewDescriptorArray(descriptor_count);
+    for (int i = 0; i < descriptor_count; i++) {
       DescriptorArray::CopyFrom(new_result, i, result, i, witness);
     }
     result = new_result;
index 3d36ead..f080bed 100644 (file)
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -1298,7 +1298,7 @@ void KeyedLoadIC::UpdateCaches(LookupResult* lookup,
 
 static bool StoreICableLookup(LookupResult* lookup) {
   // Bail out if we didn't find a result.
-  if (!lookup->IsFound() || lookup->type() == NULL_DESCRIPTOR) return false;
+  if (!lookup->IsFound()) return false;
 
   // Bail out if inline caching is not allowed.
   if (!lookup->IsCacheable()) return false;
@@ -1434,10 +1434,10 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
                            Handle<Object> value) {
   ASSERT(!receiver->IsJSGlobalProxy());
   ASSERT(StoreICableLookup(lookup));
+  ASSERT(lookup->IsFound());
+
   // These are not cacheable, so we never see such LookupResults here.
   ASSERT(!lookup->IsHandler());
-  // We get only called for properties or transitions, see StoreICableLookup.
-  ASSERT(lookup->type() != NULL_DESCRIPTOR);
 
   // If the property has a non-field type allowing map transitions
   // where there is extra room in the object, we leave the IC in its
@@ -1509,8 +1509,8 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
     case CONSTANT_FUNCTION:
     case CONSTANT_TRANSITION:
       return;
+    case NONEXISTENT:
     case HANDLER:
-    case NULL_DESCRIPTOR:
       UNREACHABLE();
       return;
   }
@@ -1936,10 +1936,10 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
                                 Handle<Object> value) {
   ASSERT(!receiver->IsJSGlobalProxy());
   ASSERT(StoreICableLookup(lookup));
+  ASSERT(lookup->IsFound());
+
   // These are not cacheable, so we never see such LookupResults here.
   ASSERT(!lookup->IsHandler());
-  // We get only called for properties or transitions, see StoreICableLookup.
-  ASSERT(lookup->type() != NULL_DESCRIPTOR);
 
   // If the property has a non-field type allowing map transitions
   // where there is extra room in the object, we leave the IC in its
@@ -1978,7 +1978,7 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
           : generic_stub();
       break;
     case HANDLER:
-    case NULL_DESCRIPTOR:
+    case NONEXISTENT:
       UNREACHABLE();
       return;
   }
index f26d9a5..d0b4ae4 100644 (file)
@@ -1901,8 +1901,7 @@ void Marker<T>::MarkDescriptorArray(DescriptorArray* descriptors) {
   }
 
   // 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.
+  // 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;
@@ -1940,7 +1939,9 @@ void Marker<T>::MarkDescriptorArray(DescriptorArray* descriptors) {
         break;
       case MAP_TRANSITION:
       case CONSTANT_TRANSITION:
-      case NULL_DESCRIPTOR:
+        break;
+      case NONEXISTENT:
+        UNREACHABLE();
         break;
     }
   }
index cefa3f8..74c015b 100644 (file)
@@ -953,7 +953,9 @@ bool DescriptorArray::IsConsistentWithBackPointers(Map* current_map) {
       case CONSTANT_FUNCTION:
       case HANDLER:
       case INTERCEPTOR:
-      case NULL_DESCRIPTOR:
+        break;
+      case NONEXISTENT:
+        UNREACHABLE();
         break;
     }
   }
index 331f76f..b334669 100644 (file)
@@ -1863,7 +1863,7 @@ void FixedArray::set_unchecked(Heap* heap,
 
 void FixedArray::set_null_unchecked(Heap* heap, int index) {
   ASSERT(index >= 0 && index < this->length());
-  ASSERT(!HEAP->InNewSpace(heap->null_value()));
+  ASSERT(!heap->InNewSpace(heap->null_value()));
   WRITE_FIELD(this, kHeaderSize + index * kPointerSize, heap->null_value());
 }
 
@@ -1976,6 +1976,17 @@ String* DescriptorArray::GetKey(int descriptor_number) {
 }
 
 
+void DescriptorArray::SetKeyUnchecked(Heap* heap,
+                                      int descriptor_number,
+                                      String* key) {
+  ASSERT(descriptor_number < number_of_descriptors());
+  set_unchecked(heap,
+                ToKeyIndex(descriptor_number),
+                key,
+                UPDATE_WRITE_BARRIER);
+}
+
+
 Object** DescriptorArray::GetValueSlot(int descriptor_number) {
   ASSERT(descriptor_number < number_of_descriptors());
   return HeapObject::RawField(
@@ -1990,12 +2001,24 @@ Object* DescriptorArray::GetValue(int descriptor_number) {
 }
 
 
-void DescriptorArray::SetNullValueUnchecked(int descriptor_number, Heap* heap) {
+void DescriptorArray::SetNullValueUnchecked(Heap* heap, int descriptor_number) {
   ASSERT(descriptor_number < number_of_descriptors());
   set_null_unchecked(heap, ToValueIndex(descriptor_number));
 }
 
 
+
+void DescriptorArray::SetValueUnchecked(Heap* heap,
+                                        int descriptor_number,
+                                        Object* value) {
+  ASSERT(descriptor_number < number_of_descriptors());
+  set_unchecked(heap,
+                ToValueIndex(descriptor_number),
+                value,
+                UPDATE_WRITE_BARRIER);
+}
+
+
 PropertyDetails DescriptorArray::GetDetails(int descriptor_number) {
   ASSERT(descriptor_number < number_of_descriptors());
   Object* details = get(ToDetailsIndex(descriptor_number));
@@ -2059,19 +2082,16 @@ bool DescriptorArray::IsTransitionOnly(int descriptor_number) {
     case CONSTANT_FUNCTION:
     case HANDLER:
     case INTERCEPTOR:
-    case NULL_DESCRIPTOR:
       return false;
+    case NONEXISTENT:
+      UNREACHABLE();
+      break;
   }
   UNREACHABLE();  // Keep the compiler happy.
   return false;
 }
 
 
-bool DescriptorArray::IsNullDescriptor(int descriptor_number) {
-  return GetType(descriptor_number) == NULL_DESCRIPTOR;
-}
-
-
 void DescriptorArray::Get(int descriptor_number, Descriptor* desc) {
   desc->Init(GetKey(descriptor_number),
              GetValue(descriptor_number),
@@ -3464,10 +3484,23 @@ int Map::bit_field3() {
 }
 
 
+void Map::ClearDescriptorArray() {
+  int bitfield3 = bit_field3();
+#ifdef DEBUG
+  Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
+  if (!object->IsSmi()) {
+    ZapInstanceDescriptors();
+  }
+#endif
+  WRITE_FIELD(this,
+              kInstanceDescriptorsOrBitField3Offset,
+              Smi::FromInt(bitfield3));
+}
+
+
 void Map::set_bit_field3(int value) {
   ASSERT(Smi::IsValid(value));
-  Object* object = READ_FIELD(this,
-                              kInstanceDescriptorsOrBitField3Offset);
+  Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
   if (object->IsSmi()) {
     WRITE_FIELD(this,
                 kInstanceDescriptorsOrBitField3Offset,
index 6714cdc..ac3765e 100644 (file)
@@ -279,12 +279,10 @@ void JSObject::PrintProperties(FILE* out) {
         case CONSTANT_TRANSITION:
           PrintF(out, "(constant transition)\n");
           break;
-        case NULL_DESCRIPTOR:
-          PrintF(out, "(null descriptor)\n");
-          break;
         case NORMAL:  // only in slow mode
         case HANDLER:  // only in lookup results, not in descriptors
         case INTERCEPTOR:  // only in lookup results, not in descriptors
+        case NONEXISTENT:
           UNREACHABLE();
           break;
       }
index d96c111..055bbc4 100644 (file)
@@ -418,7 +418,7 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
       case HANDLER:
       case MAP_TRANSITION:
       case CONSTANT_TRANSITION:
-      case NULL_DESCRIPTOR:
+      case NONEXISTENT:
         UNREACHABLE();
     }
   }
@@ -643,7 +643,9 @@ MaybeObject* Object::GetProperty(Object* receiver,
     }
     case MAP_TRANSITION:
     case CONSTANT_TRANSITION:
-    case NULL_DESCRIPTOR:
+      break;
+    case NONEXISTENT:
+      UNREACHABLE();
       break;
   }
   UNREACHABLE();
@@ -2156,7 +2158,9 @@ MaybeObject* JSObject::SetPropertyViaPrototypes(
       }
       case MAP_TRANSITION:
       case CONSTANT_TRANSITION:
-      case NULL_DESCRIPTOR:
+        break;
+      case NONEXISTENT:
+        UNREACHABLE();
         break;
     }
   }
@@ -2939,9 +2943,8 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
       // FIELD, even if the value is a constant function.
       return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
     }
-    case NULL_DESCRIPTOR:
-      return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
     case HANDLER:
+    case NONEXISTENT:
       UNREACHABLE();
       return value;
   }
@@ -3037,9 +3040,9 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
     case CONSTANT_TRANSITION:
       // Replace with a MAP_TRANSITION to a new map with a FIELD, even
       // if the value is a function.
-    case NULL_DESCRIPTOR:
       return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
     case HANDLER:
+    case NONEXISTENT:
       UNREACHABLE();
   }
   UNREACHABLE();  // keep the compiler happy
@@ -3332,11 +3335,11 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
       }
       case MAP_TRANSITION:
       case CONSTANT_TRANSITION:
-      case NULL_DESCRIPTOR:
       case INTERCEPTOR:
         break;
       case HANDLER:
       case NORMAL:
+      case NONEXISTENT:
         UNREACHABLE();
         break;
     }
@@ -3680,8 +3683,7 @@ MaybeObject* JSObject::GetHiddenPropertiesDictionary(bool create_if_absent) {
             this->FastPropertyAt(descriptors->GetFieldIndex(0));
         return StringDictionary::cast(hidden_store);
       } else {
-        ASSERT(descriptors->GetType(0) == NULL_DESCRIPTOR ||
-               descriptors->GetType(0) == MAP_TRANSITION);
+        ASSERT(descriptors->GetType(0) == MAP_TRANSITION);
       }
     }
   } else {
@@ -3729,8 +3731,7 @@ MaybeObject* JSObject::SetHiddenPropertiesDictionary(
         this->FastPropertyAtPut(descriptors->GetFieldIndex(0), dictionary);
         return this;
       } else {
-        ASSERT(descriptors->GetType(0) == NULL_DESCRIPTOR ||
-               descriptors->GetType(0) == MAP_TRANSITION);
+        ASSERT(descriptors->GetType(0) == MAP_TRANSITION);
       }
     }
   }
@@ -4177,9 +4178,7 @@ int Map::NumberOfDescribedProperties(PropertyAttributes filter) {
 int Map::PropertyIndexFor(String* name) {
   DescriptorArray* descs = instance_descriptors();
   for (int i = 0; i < descs->number_of_descriptors(); i++) {
-    if (name->Equals(descs->GetKey(i)) && !descs->IsNullDescriptor(i)) {
-      return descs->GetFieldIndex(i);
-    }
+    if (name->Equals(descs->GetKey(i))) return descs->GetFieldIndex(i);
   }
   return -1;
 }
@@ -5086,11 +5085,13 @@ class IntrusiveMapTransitionIterator {
         case CONSTANT_FUNCTION:
         case HANDLER:
         case INTERCEPTOR:
-        case NULL_DESCRIPTOR:
           // We definitely have no map transition.
           raw_index += 2;
           ++index;
           break;
+        case NONEXISTENT:
+          UNREACHABLE();
+          break;
       }
     }
     if (index == descriptor_array_->number_of_descriptors()) {
@@ -5880,7 +5881,6 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
   // removing all other transitions is not supported.
   bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
   ASSERT(remove_transitions == !descriptor->ContainsTransition());
-  ASSERT(descriptor->GetDetails().type() != NULL_DESCRIPTOR);
 
   // Ensure the key is a symbol.
   { MaybeObject* maybe_result = descriptor->KeyToSymbol();
@@ -5889,7 +5889,6 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
 
   int new_size = 0;
   for (int i = 0; i < number_of_descriptors(); i++) {
-    if (IsNullDescriptor(i)) continue;
     if (remove_transitions && IsTransitionOnly(i)) continue;
     new_size++;
   }
@@ -5947,8 +5946,7 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
       insertion_index = to_index++;
       if (replacing) from_index++;
     } else {
-      if (!(IsNullDescriptor(from_index) ||
-            (remove_transitions && IsTransitionOnly(from_index)))) {
+      if (!(remove_transitions && IsTransitionOnly(from_index))) {
         MaybeObject* copy_result =
             new_descriptors->CopyFrom(to_index++, this, from_index, witness);
         if (copy_result->IsFailure()) return copy_result;
@@ -6078,8 +6076,7 @@ int DescriptorArray::BinarySearch(String* name, int low, int high) {
   }
 
   for (; low <= limit && GetKey(low)->Hash() == hash; ++low) {
-    if (GetKey(low)->Equals(name) && !IsNullDescriptor(low))
-      return low;
+    if (GetKey(low)->Equals(name)) return low;
   }
 
   return kNotFound;
@@ -6091,9 +6088,7 @@ int DescriptorArray::LinearSearch(SearchMode mode, String* name, int len) {
   for (int number = 0; number < len; number++) {
     String* entry = GetKey(number);
     if (mode == EXPECT_SORTED && entry->Hash() > hash) break;
-    if (name->Equals(entry) && !IsNullDescriptor(number)) {
-      return number;
-    }
+    if (name->Equals(entry)) return number;
   }
   return kNotFound;
 }
@@ -7352,75 +7347,147 @@ static bool ClearBackPointer(Heap* heap, Object* target) {
 }
 
 
-void Map::ClearNonLiveTransitions(Heap* heap) {
-  DescriptorArray* d = DescriptorArray::cast(
-      *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset));
-  if (d->IsEmpty()) return;
-  Map* elements_transition = d->elements_transition_map();
-  if (elements_transition != NULL &&
-      ClearBackPointer(heap, elements_transition)) {
-    d->ClearElementsTransition();
+// This function should only be called from within the GC, since it uses
+// IncrementLiveBytesFromGC. If called from anywhere else, this results in an
+// inconsistent live-bytes count.
+static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) {
+  ASSERT(elms->map() != HEAP->fixed_cow_array_map());
+  // For now this trick is only applied to fixed arrays in new and paged space.
+  // In large object space the object's start must coincide with chunk
+  // and thus the trick is just not applicable.
+  ASSERT(!HEAP->lo_space()->Contains(elms));
+
+  const int len = elms->length();
+
+  ASSERT(to_trim < len);
+
+  Address new_end = elms->address() + FixedArray::SizeFor(len - to_trim);
+
+#ifdef DEBUG
+  // If we are doing a big trim in old space then we zap the space.
+  Object** zap = reinterpret_cast<Object**>(new_end);
+  for (int i = 1; i < to_trim; i++) {
+    *zap++ = Smi::FromInt(0);
   }
-  Smi* NullDescriptorDetails =
-    PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
-  for (int i = 0; i < d->number_of_descriptors(); ++i) {
-    // 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 that non-live map.
-    bool keep_entry = false;
-    PropertyDetails details(d->GetDetails(i));
-    switch (details.type()) {
-      case MAP_TRANSITION:
-      case CONSTANT_TRANSITION:
-        keep_entry = !ClearBackPointer(heap, d->GetValue(i));
-        break;
-      case CALLBACKS: {
-        Object* object = d->GetValue(i);
-        if (object->IsAccessorPair()) {
-          AccessorPair* accessors = AccessorPair::cast(object);
-          Object* getter = accessors->getter();
-          if (getter->IsMap()) {
-            if (ClearBackPointer(heap, getter)) {
-              accessors->set_getter(heap->the_hole_value());
-            } else {
-              keep_entry = true;
-            }
-          } else if (!getter->IsTheHole()) {
-            keep_entry = true;
+#endif
+
+  int size_delta = to_trim * kPointerSize;
+
+  // Technically in new space this write might be omitted (except for
+  // debug mode which iterates through the heap), but to play safer
+  // we still do it.
+  heap->CreateFillerObjectAt(new_end, size_delta);
+
+  elms->set_length(len - to_trim);
+
+  // Maintain marking consistency for IncrementalMarking.
+  if (Marking::IsBlack(Marking::MarkBitFrom(elms))) {
+    MemoryChunk::IncrementLiveBytesFromGC(elms->address(), -size_delta);
+  }
+}
+
+
+// If the descriptor describes a transition to a dead map, the back pointer
+// of this map is cleared and we return true. Otherwise we return false.
+static bool ClearNonLiveTransitionsFromDescriptor(Heap* heap,
+                                                  DescriptorArray* d,
+                                                  int descriptor_index) {
+  // 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 that non-live map.
+  PropertyDetails details(d->GetDetails(descriptor_index));
+  switch (details.type()) {
+    case MAP_TRANSITION:
+    case CONSTANT_TRANSITION:
+      return ClearBackPointer(heap, d->GetValue(descriptor_index));
+    case CALLBACKS: {
+      Object* object = d->GetValue(descriptor_index);
+      if (object->IsAccessorPair()) {
+        bool cleared = true;
+        AccessorPair* accessors = AccessorPair::cast(object);
+        Object* getter = accessors->getter();
+        if (getter->IsMap()) {
+          if (ClearBackPointer(heap, getter)) {
+            accessors->set_getter(heap->the_hole_value());
+          } else {
+            cleared = false;
           }
-          Object* setter = accessors->setter();
-          if (setter->IsMap()) {
-            if (ClearBackPointer(heap, setter)) {
-              accessors->set_setter(heap->the_hole_value());
-            } else {
-              keep_entry = true;
-            }
-          } else if (!setter->IsTheHole()) {
-            keep_entry = true;
+        } else if (!getter->IsTheHole()) {
+          cleared = false;
+        }
+        Object* setter = accessors->setter();
+        if (setter->IsMap()) {
+          if (ClearBackPointer(heap, setter)) {
+            accessors->set_setter(heap->the_hole_value());
+          } else {
+            cleared = false;
           }
-        } else {
-          keep_entry = true;
+        } else if (!setter->IsTheHole()) {
+          cleared = false;
         }
-        break;
+        return cleared;
       }
-      case NORMAL:
-      case FIELD:
-      case CONSTANT_FUNCTION:
-      case HANDLER:
-      case INTERCEPTOR:
-      case NULL_DESCRIPTOR:
-        keep_entry = true;
-        break;
+      return false;
     }
-    // Make sure that an entry containing only dead transitions gets collected.
-    // What we *really* want to do here is removing this entry completely, but
-    // for technical reasons we can't do this, so we zero it out instead.
-    if (!keep_entry) {
-      d->SetDetailsUnchecked(i, NullDescriptorDetails);
-      d->SetNullValueUnchecked(i, heap);
+    case NORMAL:
+    case FIELD:
+    case CONSTANT_FUNCTION:
+    case HANDLER:
+    case INTERCEPTOR:
+      return false;
+    case NONEXISTENT:
+      break;
+  }
+  UNREACHABLE();
+  return true;
+}
+
+
+void Map::ClearNonLiveTransitions(Heap* heap) {
+  Object* array = *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset);
+  // If there are no descriptors to be cleared, return.
+  // TODO(verwaest) Should be an assert, otherwise back pointers are not
+  // properly cleared.
+  if (array->IsSmi()) return;
+  DescriptorArray* d = DescriptorArray::cast(array);
+
+  int descriptor_index = 0;
+  // Compact all live descriptors to the left.
+  for (int i = 0; i < d->number_of_descriptors(); ++i) {
+    if (!ClearNonLiveTransitionsFromDescriptor(heap, d, i)) {
+      if (i != descriptor_index) {
+        d->SetKeyUnchecked(heap, descriptor_index, d->GetKey(i));
+        d->SetDetailsUnchecked(descriptor_index, d->GetDetails(i).AsSmi());
+        d->SetValueUnchecked(heap, descriptor_index, d->GetValue(i));
+      }
+      descriptor_index++;
     }
   }
+
+  Map* elements_transition = d->elements_transition_map();
+  if (elements_transition != NULL &&
+      ClearBackPointer(heap, elements_transition)) {
+    elements_transition = NULL;
+    d->ClearElementsTransition();
+  } else {
+    // If there are no descriptors to be cleared, return.
+    // TODO(verwaest) Should be an assert, otherwise back pointers are not
+    // properly cleared.
+    if (descriptor_index == d->number_of_descriptors()) return;
+  }
+
+  // If the final descriptor array does not contain any live descriptors, remove
+  // the descriptor array from the map.
+  if (descriptor_index == 0 && elements_transition == NULL) {
+    ClearDescriptorArray();
+    return;
+  }
+
+  int trim = d->number_of_descriptors() - descriptor_index;
+  if (trim > 0) {
+    RightTrimFixedArray(heap, d, trim * DescriptorArray::kDescriptorSize);
+  }
 }
 
 
@@ -11261,8 +11328,10 @@ bool StringDictionary::ContainsTransition(int entry) {
     case CONSTANT_FUNCTION:
     case HANDLER:
     case INTERCEPTOR:
-    case NULL_DESCRIPTOR:
       return false;
+    case NONEXISTENT:
+      UNREACHABLE();
+      break;
   }
   UNREACHABLE();  // Keep the compiler happy.
   return false;
index 2b6e2f8..e0f99ba 100644 (file)
@@ -2501,9 +2501,13 @@ class DescriptorArray: public FixedArray {
   // Accessors for fetching instance descriptor at descriptor number.
   inline String* GetKey(int descriptor_number);
   inline Object** GetKeySlot(int descriptor_number);
+  inline void SetKeyUnchecked(Heap* heap, int descriptor_number, String* value);
   inline Object* GetValue(int descriptor_number);
   inline Object** GetValueSlot(int descriptor_number);
-  inline void SetNullValueUnchecked(int descriptor_number, Heap* heap);
+  inline void SetNullValueUnchecked(Heap* heap, int descriptor_number);
+  inline void SetValueUnchecked(Heap* heap,
+                                int descriptor_number,
+                                Object* value);
   inline PropertyDetails GetDetails(int descriptor_number);
   inline void SetDetailsUnchecked(int descriptor_number, Smi* value);
   inline PropertyType GetType(int descriptor_number);
@@ -2513,7 +2517,6 @@ class DescriptorArray: public FixedArray {
   inline AccessorDescriptor* GetCallbacks(int descriptor_number);
   inline bool IsProperty(int descriptor_number);
   inline bool IsTransitionOnly(int descriptor_number);
-  inline bool IsNullDescriptor(int descriptor_number);
 
   // WhitenessWitness is used to prove that a specific descriptor array is white
   // (unmarked), so incremental write barriers can be skipped because the
@@ -4780,6 +4783,10 @@ class Map: public HeapObject {
   // [instance descriptors]: describes the object.
   DECL_ACCESSORS(instance_descriptors, DescriptorArray)
 
+  // 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();
+
   // Sets the instance descriptor array for the map to be an empty descriptor
   // array.
   inline void clear_instance_descriptors();
index a3143be..217046f 100644 (file)
@@ -2220,7 +2220,9 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) {
         case INTERCEPTOR:  // only in lookup results, not in descriptors
         case MAP_TRANSITION:  // we do not care about transitions here...
         case CONSTANT_TRANSITION:
-        case NULL_DESCRIPTOR:  // ... and not about "holes"
+          break;
+        case NONEXISTENT:
+          UNREACHABLE();
           break;
       }
     }
index 27c8b15..e898a7d 100644 (file)
@@ -64,7 +64,8 @@ enum PropertyType {
   // All properties before MAP_TRANSITION are real.
   MAP_TRANSITION            = 6,  // only in fast mode
   CONSTANT_TRANSITION       = 7,  // only in fast mode
-  NULL_DESCRIPTOR           = 8  // only in fast mode
+  // Only used as a marker in LookupResult.
+  NONEXISTENT               = 8
 };
 
 
index 8c69541..3258b8c 100644 (file)
@@ -89,8 +89,8 @@ void LookupResult::Print(FILE* out) {
       GetTransitionMap()->Print(out);
       PrintF(out, "\n");
       break;
-    case NULL_DESCRIPTOR:
-      PrintF(out, " =type = null descriptor\n");
+    case NONEXISTENT:
+      UNREACHABLE();
       break;
   }
 }
@@ -123,8 +123,10 @@ bool Descriptor::ContainsTransition() {
     case CONSTANT_FUNCTION:
     case HANDLER:
     case INTERCEPTOR:
-    case NULL_DESCRIPTOR:
       return false;
+    case NONEXISTENT:
+      UNREACHABLE();
+      break;
   }
   UNREACHABLE();  // Keep the compiler happy.
   return false;
index 5599aa8..76ae0fb 100644 (file)
@@ -173,8 +173,10 @@ bool IsPropertyDescriptor(T* desc) {
     }
     case MAP_TRANSITION:
     case CONSTANT_TRANSITION:
-    case NULL_DESCRIPTOR:
       return false;
+    case NONEXISTENT:
+      UNREACHABLE();
+      break;
   }
   UNREACHABLE();  // keep the compiler happy
   return false;
@@ -189,7 +191,7 @@ class LookupResult BASE_EMBEDDED {
         lookup_type_(NOT_FOUND),
         holder_(NULL),
         cacheable_(true),
-        details_(NONE, NULL_DESCRIPTOR) {
+        details_(NONE, NONEXISTENT) {
     isolate->SetTopLookupResult(this);
   }
 
@@ -237,7 +239,7 @@ class LookupResult BASE_EMBEDDED {
 
   void NotFound() {
     lookup_type_ = NOT_FOUND;
-    details_ = PropertyDetails(NONE, NULL_DESCRIPTOR);
+    details_ = PropertyDetails(NONE, NONEXISTENT);
     holder_ = NULL;
   }
 
index 82206dd..8c7a340 100644 (file)
@@ -10286,9 +10286,9 @@ static MaybeObject* DebugLookupResultValue(Heap* heap,
     case INTERCEPTOR:
     case MAP_TRANSITION:
     case CONSTANT_TRANSITION:
-    case NULL_DESCRIPTOR:
       return heap->undefined_value();
     case HANDLER:
+    case NONEXISTENT:
       UNREACHABLE();
       return heap->undefined_value();
   }