Reduce space usage of simple transitions and descriptors holders.
authorverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 19 Sep 2012 09:54:10 +0000 (09:54 +0000)
committerverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 19 Sep 2012 09:54:10 +0000 (09:54 +0000)
Review URL: https://chromiumcodereview.appspot.com/10915260

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

src/objects-inl.h
src/objects-visiting-inl.h
src/objects.cc
src/objects.h
src/transitions-inl.h
src/transitions.cc
src/transitions.h

index c5ea5dc2c4856e3a7f6782a6d9655a659b7fb9b9..411aeecefe24efa727cb19ee4b4ee11c983a67d5 100644 (file)
@@ -3560,17 +3560,31 @@ DescriptorArray* Map::instance_descriptors() {
 }
 
 
+enum TransitionsKind { DESCRIPTORS_HOLDER, FULL_TRANSITION_ARRAY };
+
+
 // 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;
-
+static MaybeObject* EnsureHasTransitionArray(Map* map, TransitionsKind kind) {
   TransitionArray* transitions;
-  JSGlobalPropertyCell* pointer = map->RetrieveDescriptorsPointer();
-  MaybeObject* maybe_transitions = TransitionArray::Allocate(0, pointer);
-  if (!maybe_transitions->To(&transitions)) return maybe_transitions;
-
-  transitions->set_back_pointer_storage(map->GetBackPointer());
+  MaybeObject* maybe_transitions;
+  if (map->HasTransitionArray()) {
+    if (kind != FULL_TRANSITION_ARRAY ||
+        map->transitions()->IsFullTransitionArray()) {
+      return map;
+    }
+    maybe_transitions = map->transitions()->ExtendToFullTransitionArray();
+    if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+  } else {
+    JSGlobalPropertyCell* pointer = map->RetrieveDescriptorsPointer();
+    if (kind == FULL_TRANSITION_ARRAY) {
+      maybe_transitions = TransitionArray::Allocate(0, pointer);
+    } else {
+      maybe_transitions = TransitionArray::AllocateDescriptorsHolder(pointer);
+    }
+    if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+    transitions->set_back_pointer_storage(map->GetBackPointer());
+  }
   map->set_transitions(transitions);
   return transitions;
 }
@@ -3578,7 +3592,8 @@ static MaybeObject* EnsureHasTransitionArray(Map* map) {
 
 MaybeObject* Map::SetDescriptors(DescriptorArray* value) {
   ASSERT(!is_shared());
-  MaybeObject* maybe_failure = EnsureHasTransitionArray(this);
+  MaybeObject* maybe_failure =
+      EnsureHasTransitionArray(this, DESCRIPTORS_HOLDER);
   if (maybe_failure->IsFailure()) return maybe_failure;
 
   ASSERT(NumberOfOwnDescriptors() <= value->number_of_descriptors());
@@ -3688,11 +3703,13 @@ JSGlobalPropertyCell* Map::RetrieveDescriptorsPointer() {
 }
 
 
-MaybeObject* Map::AddTransition(String* key, Map* target) {
+MaybeObject* Map::AddTransition(String* key,
+                                Map* target,
+                                SimpleTransitionFlag flag) {
   if (HasTransitionArray()) return transitions()->CopyInsert(key, target);
   JSGlobalPropertyCell* descriptors_pointer = RetrieveDescriptorsPointer();
   return TransitionArray::NewWith(
-      key, target, descriptors_pointer, GetBackPointer());
+      flag, key, target, descriptors_pointer, GetBackPointer());
 }
 
 
@@ -3707,7 +3724,8 @@ Map* Map::GetTransition(int transition_index) {
 
 
 MaybeObject* Map::set_elements_transition_map(Map* transitioned_map) {
-  MaybeObject* allow_elements = EnsureHasTransitionArray(this);
+  MaybeObject* allow_elements =
+      EnsureHasTransitionArray(this, FULL_TRANSITION_ARRAY);
   if (allow_elements->IsFailure()) return allow_elements;
   transitions()->set_elements_transition(transitioned_map);
   return this;
@@ -3724,7 +3742,8 @@ FixedArray* Map::GetPrototypeTransitions() {
 
 
 MaybeObject* Map::SetPrototypeTransitions(FixedArray* proto_transitions) {
-  MaybeObject* allow_prototype = EnsureHasTransitionArray(this);
+  MaybeObject* allow_prototype =
+      EnsureHasTransitionArray(this, FULL_TRANSITION_ARRAY);
   if (allow_prototype->IsFailure()) return allow_prototype;
 #ifdef DEBUG
   if (HasPrototypeTransitions()) {
index 5d1bc63b73d061137e071661ffefc463a4933674..5d33e2ef98e66754715066102e3f7ea7131782c5 100644 (file)
@@ -330,6 +330,9 @@ void StaticMarkingVisitor<StaticVisitor>::MarkTransitionArray(
   // is not compacted and descriptors are referenced through a cell.
   StaticVisitor::MarkObject(heap, transitions->descriptors_pointer());
 
+  // Simple transitions do not have keys nor prototype transitions.
+  if (transitions->IsSimpleTransition()) return;
+
   if (transitions->HasPrototypeTransitions()) {
     // Mark prototype transitions array but do not push it onto marking
     // stack, this will make references from it weak. We will clean dead
index db2c1eb75d5cfd40c7c7dfce590c96f4df9b13d0..d7fa18ff0ba22c068476683534d30830cee06c9c 100644 (file)
@@ -5000,7 +5000,8 @@ MaybeObject* Map::ShareDescriptor(Descriptor* descriptor) {
   String* name = descriptor->GetKey();
 
   TransitionArray* transitions;
-  MaybeObject* maybe_transitions = AddTransition(name, result);
+  MaybeObject* maybe_transitions =
+      AddTransition(name, result, SIMPLE_TRANSITION);
   if (!maybe_transitions->To(&transitions)) return maybe_transitions;
 
   DescriptorArray* descriptors = instance_descriptors();
@@ -5051,7 +5052,8 @@ MaybeObject* Map::ShareDescriptor(Descriptor* descriptor) {
 
 MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors,
                                          String* name,
-                                         TransitionFlag flag) {
+                                         TransitionFlag flag,
+                                         int descriptor_index) {
   ASSERT(descriptors->IsSortedNoDuplicates());
 
   Map* result;
@@ -5068,7 +5070,11 @@ MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors,
 
   if (flag == INSERT_TRANSITION && CanHaveMoreTransitions()) {
     TransitionArray* transitions;
-    MaybeObject* maybe_transitions = AddTransition(name, result);
+    SimpleTransitionFlag simple_flag =
+        (descriptor_index == descriptors->number_of_descriptors() - 1)
+         ? SIMPLE_TRANSITION
+         : FULL_TRANSITION;
+    MaybeObject* maybe_transitions = AddTransition(name, result, simple_flag);
     if (!maybe_transitions->To(&transitions)) return maybe_transitions;
 
     if (descriptors->IsEmpty()) {
@@ -5173,7 +5179,7 @@ MaybeObject* Map::CopyWithPreallocatedFieldDescriptors() {
       descriptors->CopyUpTo(number_of_own_descriptors);
   if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
 
-  return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION);
+  return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION, 0);
 }
 
 
@@ -5185,7 +5191,7 @@ MaybeObject* Map::Copy() {
       descriptors->CopyUpTo(number_of_own_descriptors);
   if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
 
-  return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION);
+  return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION, 0);
 }
 
 
@@ -5227,8 +5233,9 @@ MaybeObject* Map::CopyAddDescriptor(Descriptor* descriptor,
   }
 
   String* key = descriptor->GetKey();
+  int insertion_index = new_descriptors->number_of_descriptors() - 1;
 
-  return CopyReplaceDescriptors(new_descriptors, key, flag);
+  return CopyReplaceDescriptors(new_descriptors, key, flag, insertion_index);
 }
 
 
@@ -5304,7 +5311,7 @@ MaybeObject* Map::CopyReplaceDescriptor(Descriptor* descriptor,
   // Re-sort if descriptors were removed.
   if (new_size != descriptors->length()) new_descriptors->Sort();
 
-  return CopyReplaceDescriptors(new_descriptors, key, flag);
+  return CopyReplaceDescriptors(new_descriptors, key, flag, insertion_index);
 }
 
 
@@ -7554,8 +7561,8 @@ void Map::ClearNonLiveTransitions(Heap* heap) {
 
   int trim = t->number_of_transitions() - transition_index;
   if (trim > 0) {
-    RightTrimFixedArray<FROM_GC>(
-        heap, t, trim * TransitionArray::kTransitionSize);
+    RightTrimFixedArray<FROM_GC>(heap, t, t->IsSimpleTransition()
+        ? trim : trim * TransitionArray::kTransitionSize);
   }
 }
 
index 5fa275ef25beec5ac6b406c66d1d26240cbd49df..141db175e97706461bf4d5491c4bd45e8656e4e7 100644 (file)
@@ -177,6 +177,16 @@ enum TransitionFlag {
   OMIT_TRANSITION
 };
 
+
+// Indicates whether the transition is simple: the target map of the transition
+// either extends the current map with a new property, or it modifies the
+// property that was added last to the current map.
+enum SimpleTransitionFlag {
+  SIMPLE_TRANSITION,
+  FULL_TRANSITION
+};
+
+
 // Indicates whether we are only interested in the descriptors of a particular
 // map, or in all descriptors in the descriptor array.
 enum DescriptorFlag {
@@ -4836,7 +4846,9 @@ class Map: public HeapObject {
       Map* transitioned_map);
   inline void SetTransition(int transition_index, Map* target);
   inline Map* GetTransition(int transition_index);
-  MUST_USE_RESULT inline MaybeObject* AddTransition(String* key, Map* target);
+  MUST_USE_RESULT inline MaybeObject* AddTransition(String* key,
+                                                    Map* target,
+                                                    SimpleTransitionFlag flag);
   DECL_ACCESSORS(transitions, TransitionArray)
   inline void ClearTransitions(Heap* heap,
                                WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
@@ -4987,7 +4999,8 @@ class Map: public HeapObject {
   MUST_USE_RESULT MaybeObject* CopyReplaceDescriptors(
       DescriptorArray* descriptors,
       String* name,
-      TransitionFlag flag);
+      TransitionFlag flag,
+      int descriptor_index);
   MUST_USE_RESULT MaybeObject* ShareDescriptor(Descriptor* descriptor);
   MUST_USE_RESULT MaybeObject* CopyAddDescriptor(Descriptor* descriptor,
                                                  TransitionFlag flag);
index 5030616e4ea4ecc21bcf1092f80c753e0f849eb8..17c42edce2e50daf51a7a9c212cf33e4cde30807 100644 (file)
@@ -69,12 +69,14 @@ void TransitionArray::ClearElementsTransition() {
 
 
 bool TransitionArray::HasElementsTransition() {
-  return get(kElementsTransitionIndex) != Smi::FromInt(0);
+  return IsFullTransitionArray() &&
+      get(kElementsTransitionIndex) != Smi::FromInt(0);
 }
 
 
 void TransitionArray::set_elements_transition(Map* transition_map,
                                               WriteBarrierMode mode) {
+  ASSERT(IsFullTransitionArray());
   Heap* heap = GetHeap();
   WRITE_FIELD(this, kElementsTransitionOffset, transition_map);
   CONDITIONAL_WRITE_BARRIER(
@@ -121,12 +123,13 @@ void TransitionArray::set_back_pointer_storage(Object* back_pointer,
 
 
 bool TransitionArray::HasPrototypeTransitions() {
-  Object* prototype_transitions = get(kPrototypeTransitionsIndex);
-  return prototype_transitions != Smi::FromInt(0);
+  return IsFullTransitionArray() &&
+      get(kPrototypeTransitionsIndex) != Smi::FromInt(0);
 }
 
 
 FixedArray* TransitionArray::GetPrototypeTransitions() {
+  ASSERT(IsFullTransitionArray());
   Object* prototype_transitions = get(kPrototypeTransitionsIndex);
   return FixedArray::cast(prototype_transitions);
 }
@@ -140,7 +143,7 @@ HeapObject* TransitionArray::UncheckedPrototypeTransitions() {
 
 void TransitionArray::SetPrototypeTransitions(FixedArray* transitions,
                                               WriteBarrierMode mode) {
-  ASSERT(this != NULL);
+  ASSERT(IsFullTransitionArray());
   ASSERT(transitions->IsFixedArray());
   Heap* heap = GetHeap();
   WRITE_FIELD(this, kPrototypeTransitionsOffset, transitions);
@@ -156,6 +159,7 @@ Object** TransitionArray::GetPrototypeTransitionsSlot() {
 
 
 Object** TransitionArray::GetKeySlot(int transition_number) {
+  ASSERT(!IsSimpleTransition());
   ASSERT(transition_number < number_of_transitions());
   return HeapObject::RawField(
       reinterpret_cast<HeapObject*>(this),
@@ -164,24 +168,39 @@ Object** TransitionArray::GetKeySlot(int transition_number) {
 
 
 String* TransitionArray::GetKey(int transition_number) {
+  if (IsSimpleTransition()) {
+    Map* target = GetTarget(kSimpleTransitionIndex);
+    int descriptor = target->LastAdded();
+    String* key = target->instance_descriptors()->GetKey(descriptor);
+    return key;
+  }
   ASSERT(transition_number < number_of_transitions());
   return String::cast(get(ToKeyIndex(transition_number)));
 }
 
 
 void TransitionArray::SetKey(int transition_number, String* key) {
+  ASSERT(!IsSimpleTransition());
   ASSERT(transition_number < number_of_transitions());
   set(ToKeyIndex(transition_number), key);
 }
 
 
 Map* TransitionArray::GetTarget(int transition_number) {
+  if (IsSimpleTransition()) {
+    ASSERT(transition_number == kSimpleTransitionIndex);
+    return Map::cast(get(kSimpleTransitionTarget));
+  }
   ASSERT(transition_number < number_of_transitions());
   return Map::cast(get(ToTargetIndex(transition_number)));
 }
 
 
 void TransitionArray::SetTarget(int transition_number, Map* value) {
+  if (IsSimpleTransition()) {
+    ASSERT(transition_number == kSimpleTransitionIndex);
+    return set(kSimpleTransitionTarget, value);
+  }
   ASSERT(transition_number < number_of_transitions());
   set(ToTargetIndex(transition_number), value);
 }
index 199fcc2c9838fb0691bbedd4c00e1dd7bd27bb3e..7233f7346e1a7239b3aee241c9b47d596d34a7a5 100644 (file)
@@ -35,8 +35,8 @@ namespace v8 {
 namespace internal {
 
 
-MaybeObject* TransitionArray::Allocate(int number_of_transitions,
-                                       JSGlobalPropertyCell* descriptors_cell) {
+static MaybeObject* AllocateRaw(int length,
+                                JSGlobalPropertyCell* descriptors_cell) {
   Heap* heap = Isolate::Current()->heap();
 
   if (descriptors_cell == NULL) {
@@ -45,13 +45,22 @@ MaybeObject* TransitionArray::Allocate(int number_of_transitions,
     if (!maybe_cell->To(&descriptors_cell)) return maybe_cell;
   }
 
-  // Use FixedArray to not use DescriptorArray::cast on incomplete object.
+  // Use FixedArray to not use TransitionArray::cast on incomplete object.
   FixedArray* array;
-  MaybeObject* maybe_array =
-      heap->AllocateFixedArray(ToKeyIndex(number_of_transitions));
+  MaybeObject* maybe_array = heap->AllocateFixedArray(length);
   if (!maybe_array->To(&array)) return maybe_array;
 
-  array->set(kDescriptorsPointerIndex, descriptors_cell);
+  array->set(TransitionArray::kDescriptorsPointerIndex, descriptors_cell);
+  return array;
+}
+
+
+MaybeObject* TransitionArray::Allocate(int number_of_transitions,
+                                       JSGlobalPropertyCell* descriptors_cell) {
+  FixedArray* array;
+  MaybeObject* maybe_array =
+      AllocateRaw(ToKeyIndex(number_of_transitions), descriptors_cell);
+  if (!maybe_array->To(&array)) return maybe_array;
   array->set(kElementsTransitionIndex, Smi::FromInt(0));
   array->set(kPrototypeTransitionsIndex, Smi::FromInt(0));
   return array;
@@ -72,18 +81,46 @@ static bool InsertionPointFound(String* key1, String* key2) {
 }
 
 
-MaybeObject* TransitionArray::NewWith(
-    String* name,
-    Map* target,
-    JSGlobalPropertyCell* descriptors_pointer,
-    Object* back_pointer) {
+MaybeObject* TransitionArray::NewWith(SimpleTransitionFlag flag,
+                                      String* key,
+                                      Map* target,
+                                      JSGlobalPropertyCell* descriptors_pointer,
+                                      Object* back_pointer) {
+  TransitionArray* result;
+  MaybeObject* maybe_result;
+
+  if (flag == SIMPLE_TRANSITION) {
+    maybe_result = AllocateRaw(kSimpleTransitionSize, descriptors_pointer);
+    if (!maybe_result->To(&result)) return maybe_result;
+    result->set(kSimpleTransitionTarget, target);
+  } else {
+    maybe_result = Allocate(1, descriptors_pointer);
+    if (!maybe_result->To(&result)) return maybe_result;
+    result->NoIncrementalWriteBarrierSet(0, key, target);
+  }
+  result->set_back_pointer_storage(back_pointer);
+  return result;
+}
+
+
+MaybeObject* TransitionArray::AllocateDescriptorsHolder(
+    JSGlobalPropertyCell* descriptors_pointer) {
+  return AllocateRaw(kDescriptorsHolderSize, descriptors_pointer);
+}
+
+
+MaybeObject* TransitionArray::ExtendToFullTransitionArray() {
+  ASSERT(!IsFullTransitionArray());
+  int nof = number_of_transitions();
   TransitionArray* result;
+  MaybeObject* maybe_result = Allocate(nof, descriptors_pointer());
+  if (!maybe_result->To(&result)) return maybe_result;
 
-  MaybeObject* maybe_array = TransitionArray::Allocate(1, descriptors_pointer);
-  if (!maybe_array->To(&result)) return maybe_array;
+  if (nof == 1) {
+    result->NoIncrementalWriteBarrierCopyFrom(this, kSimpleTransitionIndex, 0);
+  }
 
-  result->NoIncrementalWriteBarrierSet(0, name, target);
-  result->set_back_pointer_storage(back_pointer);
+  result->set_back_pointer_storage(back_pointer_storage());
   return result;
 }
 
index b87b9730303aa32274885c2b6309cdc1679833e4..52a043ce096d837db9e49ef1a214d4591f78bf9c 100644 (file)
@@ -91,7 +91,7 @@ class TransitionArray: public FixedArray {
 
   // Returns the number of transitions in the array.
   int number_of_transitions() {
-    ASSERT(length() >= kFirstIndex);
+    if (IsSimpleTransition()) return 1;
     int len = length();
     return len <= kFirstIndex ? 0 : (len - kFirstIndex) / kTransitionSize;
   }
@@ -100,11 +100,17 @@ class TransitionArray: public FixedArray {
 
   // Allocate a new transition array with a single entry.
   static MUST_USE_RESULT MaybeObject* NewWith(
-      String* name,
+      SimpleTransitionFlag flag,
+      String* key,
       Map* target,
       JSGlobalPropertyCell* descriptor_pointer,
       Object* back_pointer);
 
+  static MUST_USE_RESULT MaybeObject* AllocateDescriptorsHolder(
+      JSGlobalPropertyCell* descriptor_pointer);
+
+  MUST_USE_RESULT MaybeObject* ExtendToFullTransitionArray();
+
   // Copy the transition array, inserting a new transition.
   // TODO(verwaest): This should not cause an existing transition to be
   // overwritten.
@@ -123,6 +129,10 @@ class TransitionArray: public FixedArray {
       int number_of_transitions,
       JSGlobalPropertyCell* descriptors_cell);
 
+  bool IsDescriptorsHolder() { return length() == kDescriptorsHolderSize; }
+  bool IsSimpleTransition() { return length() == kSimpleTransitionSize; }
+  bool IsFullTransitionArray() { return length() >= kFirstIndex; }
+
   // Casting.
   static inline TransitionArray* cast(Object* obj);
 
@@ -131,20 +141,30 @@ class TransitionArray: public FixedArray {
 
   static const int kDescriptorsPointerIndex = 0;
   static const int kBackPointerStorageIndex = 1;
+  static const int kDescriptorsHolderSize = 2;
+
+  // Layout for full transition arrays.
   static const int kElementsTransitionIndex = 2;
   static const int kPrototypeTransitionsIndex = 3;
   static const int kFirstIndex = 4;
 
-  // Layout transition array header.
+  // Layout for simple transition arrays.
+  static const int kSimpleTransitionTarget = 2;
+  static const int kSimpleTransitionSize = 3;
+  static const int kSimpleTransitionIndex = 0;
+  STATIC_ASSERT(kSimpleTransitionIndex != kNotFound);
+
   static const int kDescriptorsPointerOffset = FixedArray::kHeaderSize;
   static const int kBackPointerStorageOffset = kDescriptorsPointerOffset +
                                                kPointerSize;
+
+  // Layout for the full transition array header.
   static const int kElementsTransitionOffset = kBackPointerStorageOffset +
                                                kPointerSize;
   static const int kPrototypeTransitionsOffset = kElementsTransitionOffset +
                                                  kPointerSize;
 
-  // Layout of map transition.
+  // Layout of map transition entries in full transition arrays.
   static const int kTransitionKey = 0;
   static const int kTransitionTarget = 1;
   static const int kTransitionSize = 2;