* Do a GC in mksnapshot to get rid of some extraneous junk.
authorerik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 3 Nov 2009 21:00:43 +0000 (21:00 +0000)
committererik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 3 Nov 2009 21:00:43 +0000 (21:00 +0000)
* Make snapshot more compact by coding the tag and the space
in one byte.  Contract some common sequences to one byte.
* Use back references only within one page.  Index from the
start of the space otherwise.
* Serialize Smis as raw data rather than int-encoding them.
This takes a little more space but is faster.
Review URL: http://codereview.chromium.org/341079

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

src/mksnapshot.cc
src/objects.h
src/serialize.cc
src/serialize.h

index 083d11f..f9dbd5c 100644 (file)
@@ -194,6 +194,9 @@ int main2(int argc, char** argv) {
   context.Dispose();
   CppByteSink sink(argv[1]);
   i::Serializer2 ser(&sink);
+  // This results in a somewhat smaller snapshot, probably because it gets rid
+  // of some things that are cached between garbage collections.
+  i::Heap::CollectAllGarbage(true);
   ser.Serialize();
   return 0;
 }
index a8cbbfc..232cabf 100644 (file)
@@ -2910,7 +2910,8 @@ class Code: public HeapObject {
   void CodeVerify();
 #endif
   // Code entry points are aligned to 32 bytes.
-  static const int kCodeAlignment = 32;
+  static const int kCodeAlignmentBits = 5;
+  static const int kCodeAlignment = 1 << kCodeAlignmentBits;
   static const int kCodeAlignmentMask = kCodeAlignment - 1;
 
   // Layout description.
index 398ccc7..3c333d0 100644 (file)
@@ -1767,55 +1767,31 @@ Object* Deserializer::Resolve(Address encoded) {
 Deserializer2::Deserializer2(SnapshotByteSource* source)
     : source_(source),
       external_reference_decoder_(NULL) {
-  for (int i = 0; i <= LAST_SPACE; i++) {
-    fullness_[i] = 0;
-  }
 }
 
 
 // This routine both allocates a new object, and also keeps
 // track of where objects have been allocated so that we can
 // fix back references when deserializing.
-Address Deserializer2::Allocate(int space_index, int size) {
-  HeapObject* new_object;
-  int old_fullness = CurrentAllocationAddress(space_index);
-  // When we start a new page we need to record its location.
-  bool record_page = (old_fullness == 0);
-  if (SpaceIsPaged(space_index)) {
-    PagedSpace* space;
-    switch (space_index) {
-      case OLD_DATA_SPACE: space = Heap::old_data_space(); break;
-      case OLD_POINTER_SPACE: space = Heap::old_pointer_space(); break;
-      case MAP_SPACE: space = Heap::map_space(); break;
-      case CODE_SPACE: space = Heap::code_space(); break;
-      case CELL_SPACE: space = Heap::cell_space(); break;
-      default: UNREACHABLE(); space = NULL; break;
-    }
-    ASSERT(size <= Page::kPageSize - Page::kObjectStartOffset);
-    int current_page = old_fullness >> Page::kPageSizeBits;
-    int new_fullness = old_fullness + size;
-    int new_page = new_fullness >> Page::kPageSizeBits;
-    // What is our new position within the current page.
-    int intra_page_offset = new_fullness - current_page * Page::kPageSize;
-    if (intra_page_offset > Page::kPageSize - Page::kObjectStartOffset) {
-      // This object will not fit in a page and we have to move to the next.
-      new_page = current_page + 1;
-      old_fullness = new_page << Page::kPageSizeBits;
-      new_fullness = old_fullness + size;
-      record_page = true;
+Address Deserializer2::Allocate(int space_index, Space* space, int size) {
+  Address address;
+  if (!SpaceIsLarge(space_index)) {
+    ASSERT(!SpaceIsPaged(space_index) ||
+           size <= Page::kPageSize - Page::kObjectStartOffset);
+    Object* new_allocation;
+    if (space_index == NEW_SPACE) {
+      new_allocation = reinterpret_cast<NewSpace*>(space)->AllocateRaw(size);
+    } else {
+      new_allocation = reinterpret_cast<PagedSpace*>(space)->AllocateRaw(size);
     }
-    fullness_[space_index] = new_fullness;
-    Object* new_allocation = space->AllocateRaw(size);
-    new_object = HeapObject::cast(new_allocation);
+    HeapObject* new_object = HeapObject::cast(new_allocation);
     ASSERT(!new_object->IsFailure());
-    ASSERT((reinterpret_cast<intptr_t>(new_object->address()) &
-            Page::kPageAlignmentMask) ==
-           (old_fullness & Page::kPageAlignmentMask) +
-            Page::kObjectStartOffset);
-  } else if (SpaceIsLarge(space_index)) {
+    address = new_object->address();
+    high_water_[space_index] = address + size;
+  } else {
+    ASSERT(SpaceIsLarge(space_index));
     ASSERT(size > Page::kPageSize - Page::kObjectStartOffset);
-    fullness_[LO_SPACE]++;
-    LargeObjectSpace* lo_space = Heap::lo_space();
+    LargeObjectSpace* lo_space = reinterpret_cast<LargeObjectSpace*>(space);
     Object* new_allocation;
     if (space_index == kLargeData) {
       new_allocation = lo_space->AllocateRaw(size);
@@ -1826,45 +1802,43 @@ Address Deserializer2::Allocate(int space_index, int size) {
       new_allocation = lo_space->AllocateRawCode(size);
     }
     ASSERT(!new_allocation->IsFailure());
-    new_object = HeapObject::cast(new_allocation);
-    record_page = true;
-    // The page recording below records all large objects in the same space.
-    space_index = LO_SPACE;
-  } else {
-    ASSERT(space_index == NEW_SPACE);
-    Object* new_allocation = Heap::new_space()->AllocateRaw(size);
-    fullness_[space_index] += size;
-    ASSERT(!new_allocation->IsFailure());
-    new_object = HeapObject::cast(new_allocation);
-  }
-  Address address = new_object->address();
-  if (record_page) {
-    pages_[space_index].Add(address);
+    HeapObject* new_object = HeapObject::cast(new_allocation);
+    // Record all large objects in the same space.
+    address = new_object->address();
+    high_water_[LO_SPACE] = address + size;
   }
+  last_object_address_ = address;
   return address;
 }
 
 
 // This returns the address of an object that has been described in the
 // snapshot as being offset bytes back in a particular space.
-HeapObject* Deserializer2::GetAddress(int space) {
+HeapObject* Deserializer2::GetAddressFromEnd(int space) {
+  int offset = source_->GetInt();
+  ASSERT(!SpaceIsLarge(space));
+  offset <<= kObjectAlignmentBits;
+  return HeapObject::FromAddress(high_water_[space] - offset);
+}
+
+
+// This returns the address of an object that has been described in the
+// snapshot as being offset bytes into a particular space.
+HeapObject* Deserializer2::GetAddressFromStart(int space) {
   int offset = source_->GetInt();
   if (SpaceIsLarge(space)) {
     // Large spaces have one object per 'page'.
-    return HeapObject::FromAddress(
-        pages_[LO_SPACE][fullness_[LO_SPACE] - offset]);
+    return HeapObject::FromAddress(pages_[LO_SPACE][offset]);
   }
   offset <<= kObjectAlignmentBits;
   if (space == NEW_SPACE) {
     // New space has only one space - numbered 0.
-    return HeapObject::FromAddress(
-        pages_[space][0] + fullness_[space] - offset);
+    return HeapObject::FromAddress(pages_[space][0] + offset);
   }
   ASSERT(SpaceIsPaged(space));
-  int virtual_address = fullness_[space] - offset;
-  int page_of_pointee = (virtual_address) >> Page::kPageSizeBits;
+  int page_of_pointee = offset >> Page::kPageSizeBits;
   Address object_address = pages_[space][page_of_pointee] +
-                           (virtual_address & Page::kPageAlignmentMask);
+                           (offset & Page::kPageAlignmentMask);
   return HeapObject::FromAddress(object_address);
 }
 
@@ -1888,20 +1862,11 @@ void Deserializer2::Deserialize() {
 
 
 // This is called on the roots.  It is the driver of the deserialization
-// process.
+// process.  It is also called on the body of each function.
 void Deserializer2::VisitPointers(Object** start, Object** end) {
-  for (Object** current = start; current < end; current++) {
-    DataType data = static_cast<DataType>(source_->Get());
-    if (data == SMI_SERIALIZATION) {
-      *current = Smi::FromInt(source_->GetInt() - kSmiBias);
-    } else if (data == BACKREF_SERIALIZATION) {
-      int space = source_->Get();
-      *current = GetAddress(space);
-    } else {
-      ASSERT(data == OBJECT_SERIALIZATION);
-      ReadObject(current);
-    }
-  }
+  // The space must be new space.  Any other space would cause ReadChunk to try
+  // to update the remembered using NULL as the address.
+  ReadChunk(start, end, NEW_SPACE, NULL);
 }
 
 
@@ -1911,19 +1876,46 @@ void Deserializer2::VisitPointers(Object** start, Object** end) {
 // written very late, which means the ByteArray map is not set up by the
 // time we need to use it to mark the space at the end of a page free (by
 // making it into a byte array).
-bool Deserializer2::ReadObject(Object** write_back) {
-  int space = source_->Get();
+void Deserializer2::ReadObject(int space_number,
+                               Space* space,
+                               Object** write_back) {
   int size = source_->GetInt() << kObjectAlignmentBits;
-  Address address = Allocate(space, size);
+  Address address = Allocate(space_number, space, size);
   *write_back = HeapObject::FromAddress(address);
   Object** current = reinterpret_cast<Object**>(address);
   Object** limit = current + (size >> kPointerSizeLog2);
+  ReadChunk(current, limit, space_number, address);
+}
+
+
+#define ONE_CASE_PER_SPACE(base_tag)   \
+  case (base_tag) + NEW_SPACE:         /* NOLINT */ \
+  case (base_tag) + OLD_POINTER_SPACE: /* NOLINT */ \
+  case (base_tag) + OLD_DATA_SPACE:    /* NOLINT */ \
+  case (base_tag) + CODE_SPACE:        /* NOLINT */ \
+  case (base_tag) + MAP_SPACE:         /* NOLINT */ \
+  case (base_tag) + CELL_SPACE:        /* NOLINT */ \
+  case (base_tag) + kLargeData:        /* NOLINT */ \
+  case (base_tag) + kLargeCode:        /* NOLINT */ \
+  case (base_tag) + kLargeFixedArray:  /* NOLINT */
+
+
+void Deserializer2::ReadChunk(Object** current,
+                              Object** limit,
+                              int space,
+                              Address address) {
   while (current < limit) {
-    DataType data = static_cast<DataType>(source_->Get());
+    int data = source_->Get();
     switch (data) {
-      case SMI_SERIALIZATION:
-        *current++ = Smi::FromInt(source_->GetInt() - kSmiBias);
-        break;
+#define RAW_CASE(index, size)                                      \
+      case RAW_DATA_SERIALIZATION + index: {                       \
+        byte* raw_data_out = reinterpret_cast<byte*>(current);     \
+        source_->CopyRaw(raw_data_out, size);                      \
+        current = reinterpret_cast<Object**>(raw_data_out + size); \
+        break;                                                     \
+      }
+      COMMON_RAW_LENGTHS(RAW_CASE)
+#undef RAW_CASE
       case RAW_DATA_SERIALIZATION: {
         int size = source_->GetInt();
         byte* raw_data_out = reinterpret_cast<byte*>(current);
@@ -1931,19 +1923,54 @@ bool Deserializer2::ReadObject(Object** write_back) {
         current = reinterpret_cast<Object**>(raw_data_out + size);
         break;
       }
-      case OBJECT_SERIALIZATION: {
-        // Recurse to unpack an object that is forward-referenced from here.
-        bool in_new_space = ReadObject(current);
-        if (in_new_space && space != NEW_SPACE) {
+      case OBJECT_SERIALIZATION + NEW_SPACE: {
+        ReadObject(NEW_SPACE, Heap::new_space(), current);
+        if (space != NEW_SPACE) {
           Heap::RecordWrite(address,
                             reinterpret_cast<Address>(current) - address);
         }
         current++;
         break;
       }
-      case CODE_OBJECT_SERIALIZATION: {
+      case OBJECT_SERIALIZATION + OLD_DATA_SPACE:
+        ReadObject(OLD_DATA_SPACE, Heap::old_data_space(), current++);
+        break;
+      case OBJECT_SERIALIZATION + OLD_POINTER_SPACE:
+        ReadObject(OLD_POINTER_SPACE, Heap::old_pointer_space(), current++);
+        break;
+      case OBJECT_SERIALIZATION + MAP_SPACE:
+        ReadObject(MAP_SPACE, Heap::map_space(), current++);
+        break;
+      case OBJECT_SERIALIZATION + CODE_SPACE:
+        ReadObject(CODE_SPACE, Heap::code_space(), current++);
+        break;
+      case OBJECT_SERIALIZATION + CELL_SPACE:
+        ReadObject(CELL_SPACE, Heap::cell_space(), current++);
+        break;
+      case OBJECT_SERIALIZATION + kLargeData:
+        ReadObject(kLargeData, Heap::lo_space(), current++);
+        break;
+      case OBJECT_SERIALIZATION + kLargeCode:
+        ReadObject(kLargeCode, Heap::lo_space(), current++);
+        break;
+      case OBJECT_SERIALIZATION + kLargeFixedArray:
+        ReadObject(kLargeFixedArray, Heap::lo_space(), current++);
+        break;
+      case CODE_OBJECT_SERIALIZATION + kLargeCode: {
+        Object* new_code_object = NULL;
+        ReadObject(kLargeCode, Heap::lo_space(), &new_code_object);
+        Code* code_object = reinterpret_cast<Code*>(new_code_object);
+        // Setting a branch/call to another code object from code.
+        Address location_of_branch_data = reinterpret_cast<Address>(current);
+        Assembler::set_target_at(location_of_branch_data,
+                                 code_object->instruction_start());
+        location_of_branch_data += Assembler::kCallTargetSize;
+        current = reinterpret_cast<Object**>(location_of_branch_data);
+        break;
+      }
+      case CODE_OBJECT_SERIALIZATION + CODE_SPACE: {
         Object* new_code_object = NULL;
-        ReadObject(&new_code_object);
+        ReadObject(CODE_SPACE, Heap::code_space(), &new_code_object);
         Code* code_object = reinterpret_cast<Code*>(new_code_object);
         // Setting a branch/call to another code object from code.
         Address location_of_branch_data = reinterpret_cast<Address>(current);
@@ -1953,21 +1980,42 @@ bool Deserializer2::ReadObject(Object** write_back) {
         current = reinterpret_cast<Object**>(location_of_branch_data);
         break;
       }
-      case BACKREF_SERIALIZATION: {
+      ONE_CASE_PER_SPACE(BACKREF_SERIALIZATION) {
         // Write a backreference to an object we unpacked earlier.
-        int backref_space = source_->Get();
+        int backref_space = (data & kSpaceMask);
         if (backref_space == NEW_SPACE && space != NEW_SPACE) {
           Heap::RecordWrite(address,
                             reinterpret_cast<Address>(current) - address);
         }
-        *current++ = GetAddress(backref_space);
+        *current++ = GetAddressFromEnd(backref_space);
         break;
       }
-      case CODE_BACKREF_SERIALIZATION: {
-        int backref_space = source_->Get();
+      ONE_CASE_PER_SPACE(REFERENCE_SERIALIZATION) {
+        // Write a reference to an object we unpacked earlier.
+        int reference_space = (data & kSpaceMask);
+        if (reference_space == NEW_SPACE && space != NEW_SPACE) {
+          Heap::RecordWrite(address,
+                            reinterpret_cast<Address>(current) - address);
+        }
+        *current++ = GetAddressFromStart(reference_space);
+        break;
+      }
+#define COMMON_REFS_CASE(index, reference_space, address)                      \
+      case REFERENCE_SERIALIZATION + index: {                                  \
+        ASSERT(SpaceIsPaged(reference_space));                                 \
+        Address object_address =                                               \
+            pages_[reference_space][0] + (address << kObjectAlignmentBits);    \
+        *current++ = HeapObject::FromAddress(object_address);                  \
+        break;                                                                 \
+      }
+      COMMON_REFERENCE_PATTERNS(COMMON_REFS_CASE)
+#undef COMMON_REFS_CASE
+      ONE_CASE_PER_SPACE(CODE_BACKREF_SERIALIZATION) {
+        int backref_space = (data & kSpaceMask);
         // Can't use Code::cast because heap is not set up yet and assertions
         // will fail.
-        Code* code_object = reinterpret_cast<Code*>(GetAddress(backref_space));
+        Code* code_object =
+            reinterpret_cast<Code*>(GetAddressFromEnd(backref_space));
         // Setting a branch/call to previously decoded code object from code.
         Address location_of_branch_data = reinterpret_cast<Address>(current);
         Assembler::set_target_at(location_of_branch_data,
@@ -1975,7 +2023,21 @@ bool Deserializer2::ReadObject(Object** write_back) {
         location_of_branch_data += Assembler::kCallTargetSize;
         current = reinterpret_cast<Object**>(location_of_branch_data);
         break;
-        }
+      }
+      ONE_CASE_PER_SPACE(CODE_REFERENCE_SERIALIZATION) {
+        int backref_space = (data & kSpaceMask);
+        // Can't use Code::cast because heap is not set up yet and assertions
+        // will fail.
+        Code* code_object =
+            reinterpret_cast<Code*>(GetAddressFromStart(backref_space));
+        // Setting a branch/call to previously decoded code object from code.
+        Address location_of_branch_data = reinterpret_cast<Address>(current);
+        Assembler::set_target_at(location_of_branch_data,
+                                 code_object->instruction_start());
+        location_of_branch_data += Assembler::kCallTargetSize;
+        current = reinterpret_cast<Object**>(location_of_branch_data);
+        break;
+      }
       case EXTERNAL_REFERENCE_SERIALIZATION: {
         int reference_id = source_->GetInt();
         Address address = external_reference_decoder_->Decode(reference_id);
@@ -1991,12 +2053,16 @@ bool Deserializer2::ReadObject(Object** write_back) {
         current = reinterpret_cast<Object**>(location_of_branch_data);
         break;
       }
+      case START_NEW_PAGE_SERIALIZATION: {
+        int space = source_->Get();
+        pages_[space].Add(last_object_address_);
+        break;
+      }
       default:
         UNREACHABLE();
     }
   }
   ASSERT(current == limit);
-  return space == NEW_SPACE;
 }
 
 
@@ -2004,10 +2070,10 @@ void SnapshotByteSink::PutInt(uintptr_t integer, const char* description) {
   const int max_shift = ((kPointerSize * kBitsPerByte) / 7) * 7;
   for (int shift = max_shift; shift > 0; shift -= 7) {
     if (integer >= 1u << shift) {
-      Put(((integer >> shift) & 0x7f) | 0x80, "intpart");
+      Put(((integer >> shift) & 0x7f) | 0x80, "IntPart");
     }
   }
-  Put(integer & 0x7f, "intlastpart");
+  PutSection(integer & 0x7f, "IntLastPart");
 }
 
 #ifdef DEBUG
@@ -2035,7 +2101,7 @@ void Serializer2::Synchronize(const char* tag) {
   int character;
   do {
     character = *tag++;
-    sink_->Put(character, "tagcharacter");
+    sink_->PutSection(character, "TagCharacter");
   } while (character != 0);
 }
 
@@ -2067,7 +2133,15 @@ void Serializer2::Serialize() {
 
 void Serializer2::VisitPointers(Object** start, Object** end) {
   for (Object** current = start; current < end; current++) {
-    SerializeObject(*current, TAGGED_REPRESENTATION);
+    if ((*current)->IsSmi()) {
+      sink_->Put(RAW_DATA_SERIALIZATION, "RawData");
+      sink_->PutInt(kPointerSize, "length");
+      for (int i = 0; i < kPointerSize; i++) {
+        sink_->Put(reinterpret_cast<byte*>(current)[i], "Byte");
+      }
+    } else {
+      SerializeObject(*current, TAGGED_REPRESENTATION);
+    }
   }
 }
 
@@ -2075,59 +2149,89 @@ void Serializer2::VisitPointers(Object** start, Object** end) {
 void Serializer2::SerializeObject(
     Object* o,
     ReferenceRepresentation reference_representation) {
-  if (o->IsHeapObject()) {
-    HeapObject* heap_object = HeapObject::cast(o);
-    MapWord map_word = heap_object->map_word();
-    if (map_word.IsSerializationAddress()) {
-      int space = SpaceOfAlreadySerializedObject(heap_object);
-      int offset =
-          CurrentAllocationAddress(space) - map_word.ToSerializationAddress();
-      // If we are actually dealing with real offsets (and not a numbering of
-      // all objects) then we should shift out the bits that are always 0.
-      if (!SpaceIsLarge(space)) offset >>= kObjectAlignmentBits;
-      if (reference_representation == CODE_TARGET_REPRESENTATION) {
-        sink_->Put(CODE_BACKREF_SERIALIZATION, "BackRefCodeSerialization");
+  ASSERT(o->IsHeapObject());
+  HeapObject* heap_object = HeapObject::cast(o);
+  MapWord map_word = heap_object->map_word();
+  if (map_word.IsSerializationAddress()) {
+    int space = SpaceOfAlreadySerializedObject(heap_object);
+    int address = map_word.ToSerializationAddress();
+    int offset = CurrentAllocationAddress(space) - address;
+    bool from_start = true;
+    if (SpaceIsPaged(space)) {
+      if ((CurrentAllocationAddress(space) >> Page::kPageSizeBits) ==
+          (address >> Page::kPageSizeBits)) {
+        from_start = false;
+        address = offset;
+      }
+    } else if (space == NEW_SPACE) {
+      if (offset < address) {
+        from_start = false;
+        address = offset;
+      }
+    }
+    // If we are actually dealing with real offsets (and not a numbering of
+    // all objects) then we should shift out the bits that are always 0.
+    if (!SpaceIsLarge(space)) address >>= kObjectAlignmentBits;
+    if (reference_representation == CODE_TARGET_REPRESENTATION) {
+      if (from_start) {
+        sink_->Put(CODE_REFERENCE_SERIALIZATION + space, "RefCodeSer");
+        sink_->PutInt(address, "address");
       } else {
-        ASSERT(reference_representation == TAGGED_REPRESENTATION);
-        sink_->Put(BACKREF_SERIALIZATION, "BackRefSerialization");
+        sink_->Put(CODE_BACKREF_SERIALIZATION + space, "BackRefCodeSer");
+        sink_->PutInt(address, "address");
       }
-      sink_->Put(space, "space");
-      sink_->PutInt(offset, "offset");
     } else {
-      // Object has not yet been serialized.  Serialize it here.
-      ObjectSerializer serializer(this,
-                                  heap_object,
-                                  sink_,
-                                  reference_representation);
-      serializer.Serialize();
+      ASSERT(reference_representation == TAGGED_REPRESENTATION);
+      if (from_start) {
+#define COMMON_REFS_CASE(tag, common_space, common_offset)                 \
+        if (space == common_space && address == common_offset) {           \
+          sink_->PutSection(tag + REFERENCE_SERIALIZATION, "RefSer");      \
+        } else  /* NOLINT */
+        COMMON_REFERENCE_PATTERNS(COMMON_REFS_CASE)
+#undef COMMON_REFS_CASE
+        {  /* NOLINT */
+          sink_->Put(REFERENCE_SERIALIZATION + space, "RefSer");
+          sink_->PutInt(address, "address");
+        }
+      } else {
+        sink_->Put(BACKREF_SERIALIZATION + space, "BackRefSer");
+        sink_->PutInt(address, "address");
+      }
     }
   } else {
-    // Serialize a Smi.
-    unsigned int value = Smi::cast(o)->value() + kSmiBias;
-    sink_->Put(SMI_SERIALIZATION, "SmiSerialization");
-    sink_->PutInt(value, "smi");
+    // Object has not yet been serialized.  Serialize it here.
+    ObjectSerializer serializer(this,
+                                heap_object,
+                                sink_,
+                                reference_representation);
+    serializer.Serialize();
   }
 }
 
 
+
 void Serializer2::ObjectSerializer::Serialize() {
   int space = Serializer2::SpaceOfObject(object_);
   int size = object_->Size();
 
   if (reference_representation_ == TAGGED_REPRESENTATION) {
-    sink_->Put(OBJECT_SERIALIZATION, "ObjectSerialization");
+    sink_->Put(OBJECT_SERIALIZATION + space, "ObjectSerialization");
   } else {
     ASSERT(reference_representation_ == CODE_TARGET_REPRESENTATION);
-    sink_->Put(CODE_OBJECT_SERIALIZATION, "ObjectSerialization");
+    sink_->Put(CODE_OBJECT_SERIALIZATION + space, "ObjectSerialization");
   }
-  sink_->Put(space, "space");
   sink_->PutInt(size >> kObjectAlignmentBits, "Size in words");
 
   // Get the map before overwriting it.
   Map* map = object_->map();
   // Mark this object as already serialized.
-  object_->set_map_word(
-      MapWord::FromSerializationAddress(serializer_->Allocate(space, size)));
+  bool start_new_page;
+  object_->set_map_word(MapWord::FromSerializationAddress(
+      serializer_->Allocate(space, size, &start_new_page)));
+  if (start_new_page) {
+    sink_->Put(START_NEW_PAGE_SERIALIZATION, "NewPage");
+    sink_->PutSection(space, "NewPageSpace");
+  }
 
   // Serialize the map (first word of the object).
   serializer_->SerializeObject(map, TAGGED_REPRESENTATION);
@@ -2142,13 +2246,17 @@ void Serializer2::ObjectSerializer::Serialize() {
 
 void Serializer2::ObjectSerializer::VisitPointers(Object** start,
                                                   Object** end) {
-  Address pointers_start = reinterpret_cast<Address>(start);
-  OutputRawData(pointers_start);
-
-  for (Object** current = start; current < end; current++) {
-    serializer_->SerializeObject(*current, TAGGED_REPRESENTATION);
+  Object** current = start;
+  while (current < end) {
+    while (current < end && (*current)->IsSmi()) current++;
+    OutputRawData(reinterpret_cast<Address>(current));
+
+    while (current < end && !(*current)->IsSmi()) {
+      serializer_->SerializeObject(*current, TAGGED_REPRESENTATION);
+      bytes_processed_so_far_ += kPointerSize;
+      current++;
+    }
   }
-  bytes_processed_so_far_ += (end - start) * kPointerSize;
 }
 
 
@@ -2158,7 +2266,7 @@ void Serializer2::ObjectSerializer::VisitExternalReferences(Address* start,
   OutputRawData(references_start);
 
   for (Address* current = start; current < end; current++) {
-    sink_->Put(EXTERNAL_REFERENCE_SERIALIZATION, "External reference");
+    sink_->Put(EXTERNAL_REFERENCE_SERIALIZATION, "ExternalReference");
     int reference_id = serializer_->EncodeExternalReference(*current);
     sink_->PutInt(reference_id, "reference id");
   }
@@ -2172,7 +2280,7 @@ void Serializer2::ObjectSerializer::VisitRuntimeEntry(RelocInfo* rinfo) {
   Address target = rinfo->target_address();
   uint32_t encoding = serializer_->EncodeExternalReference(target);
   CHECK(target == NULL ? encoding == 0 : encoding != 0);
-  sink_->Put(EXTERNAL_BRANCH_TARGET_SERIALIZATION, "External reference");
+  sink_->Put(EXTERNAL_BRANCH_TARGET_SERIALIZATION, "ExternalReference");
   sink_->PutInt(encoding, "reference id");
   bytes_processed_so_far_ += Assembler::kExternalTargetSize;
 }
@@ -2196,11 +2304,20 @@ void Serializer2::ObjectSerializer::OutputRawData(Address up_to) {
   // locations in a non-ascending order.  Luckily that doesn't happen.
   ASSERT(skipped >= 0);
   if (skipped != 0) {
-    sink_->Put(RAW_DATA_SERIALIZATION, "raw data");
-    sink_->PutInt(skipped, "length");
+    Address base = object_start + bytes_processed_so_far_;
+#define RAW_CASE(index, length)                                                \
+    if (skipped == length) {                                                   \
+      sink_->PutSection(RAW_DATA_SERIALIZATION + index, "RawDataFixed");       \
+    } else  /* NOLINT */
+    COMMON_RAW_LENGTHS(RAW_CASE)
+#undef RAW_CASE
+    {  /* NOLINT */
+      sink_->Put(RAW_DATA_SERIALIZATION, "RawData");
+      sink_->PutInt(skipped, "length");
+    }
     for (int i = 0; i < skipped; i++) {
-      unsigned int data = object_start[bytes_processed_so_far_ + i];
-      sink_->Put(data, "byte");
+      unsigned int data = base[i];
+      sink_->PutSection(data, "Byte");
     }
     bytes_processed_so_far_ += skipped;
   }
@@ -2240,13 +2357,18 @@ int Serializer2::SpaceOfAlreadySerializedObject(HeapObject* object) {
 }
 
 
-int Serializer2::Allocate(int space, int size) {
+int Serializer2::Allocate(int space, int size, bool* new_page) {
   ASSERT(space >= 0 && space < kNumberOfSpaces);
   if (SpaceIsLarge(space)) {
     // In large object space we merely number the objects instead of trying to
     // determine some sort of address.
+    *new_page = true;
     return fullness_[LO_SPACE]++;
   }
+  *new_page = false;
+  if (fullness_[space] == 0) {
+    *new_page = true;
+  }
   if (SpaceIsPaged(space)) {
     // Paged spaces are a little special.  We encode their addresses as if the
     // pages were all contiguous and each page were filled up in the range
@@ -2258,6 +2380,7 @@ int Serializer2::Allocate(int space, int size) {
     int used_in_this_page = (fullness_[space] & (Page::kPageSize - 1));
     ASSERT(size <= Page::kObjectAreaSize);
     if (used_in_this_page + size > Page::kObjectAreaSize) {
+      *new_page = true;
       fullness_[space] = RoundUp(fullness_[space], Page::kPageSize);
     }
   }
index 5966cbc..d634a80 100644 (file)
@@ -396,6 +396,34 @@ class SnapshotByteSource {
   int position_;
 };
 
+// It is very common to have a reference to the object at word 10 in space 2,
+// the object at word 5 in space 2 and the object at word 28 in space 4.  This
+// only works for objects in the first page of a space.
+#define COMMON_REFERENCE_PATTERNS(f)                              \
+  f(kNumberOfSpaces, 2, 10)                                       \
+  f(kNumberOfSpaces + 1, 2, 5)                                    \
+  f(kNumberOfSpaces + 2, 4, 28)                                   \
+  f(kNumberOfSpaces + 3, 2, 21)                                   \
+  f(kNumberOfSpaces + 4, 2, 98)                                   \
+  f(kNumberOfSpaces + 5, 2, 67)                                   \
+  f(kNumberOfSpaces + 6, 4, 132)
+
+#define COMMON_RAW_LENGTHS(f)        \
+  f(1, 1)  \
+  f(2, 2)  \
+  f(3, 3)  \
+  f(4, 4)  \
+  f(5, 5)  \
+  f(6, 6)  \
+  f(7, 7)  \
+  f(8, 8)  \
+  f(9, 12)  \
+  f(10, 16) \
+  f(11, 20) \
+  f(12, 24) \
+  f(13, 28) \
+  f(14, 32) \
+  f(15, 36)
 
 // The SerDes class is a common superclass for Serializer2 and Deserializer2
 // which is used to store common constants and methods used by both.
@@ -403,25 +431,37 @@ class SnapshotByteSource {
 class SerDes: public GenericDeserializer {
  protected:
   enum DataType {
-    SMI_SERIALIZATION,
-    RAW_DATA_SERIALIZATION,
-    OBJECT_SERIALIZATION,
-    CODE_OBJECT_SERIALIZATION,
-    BACKREF_SERIALIZATION,
-    CODE_BACKREF_SERIALIZATION,
-    EXTERNAL_REFERENCE_SERIALIZATION,
-    EXTERNAL_BRANCH_TARGET_SERIALIZATION,
-    SYNCHRONIZE
+    RAW_DATA_SERIALIZATION = 0,
+    // And 15 common raw lengths.
+    OBJECT_SERIALIZATION = 16,
+    // One variant per space.
+    CODE_OBJECT_SERIALIZATION = 25,
+    // One per space (only code spaces in use).
+    EXTERNAL_REFERENCE_SERIALIZATION = 34,
+    EXTERNAL_BRANCH_TARGET_SERIALIZATION = 35,
+    SYNCHRONIZE = 36,
+    START_NEW_PAGE_SERIALIZATION = 37,
+    // Free: 38-47.
+    BACKREF_SERIALIZATION = 48,
+    // One per space, must be kSpaceMask aligned.
+    // Free: 57-63.
+    REFERENCE_SERIALIZATION = 64,
+    // One per space and common references.  Must be kSpaceMask aligned.
+    CODE_BACKREF_SERIALIZATION = 80,
+    // One per space, must be kSpaceMask aligned.
+    // Free: 89-95.
+    CODE_REFERENCE_SERIALIZATION = 96
+    // One per space, must be kSpaceMask aligned.
+    // Free: 105-255.
   };
-  // Our Smi encoding is much more efficient for small positive integers than it
-  // is for negative numbers so we add a bias before encoding and subtract it
-  // after encoding so that popular small negative Smis are efficiently encoded.
-  static const int kSmiBias = 16;
   static const int kLargeData = LAST_SPACE;
   static const int kLargeCode = kLargeData + 1;
   static const int kLargeFixedArray = kLargeCode + 1;
   static const int kNumberOfSpaces = kLargeFixedArray + 1;
 
+  // A bitmask for getting the space out of an instruction.
+  static const int kSpaceMask = 15;
+
   static inline bool SpaceIsLarge(int space) { return space >= kLargeData; }
   static inline bool SpaceIsPaged(int space) {
     return space >= FIRST_PAGED_SPACE && space <= LAST_PAGED_SPACE;
@@ -456,17 +496,11 @@ class Deserializer2: public SerDes {
     UNREACHABLE();
   }
 
-  int CurrentAllocationAddress(int space) {
-    // The three different kinds of large objects have different tags in the
-    // snapshot so the deserializer knows which kind of object to allocate,
-    // but they share a fullness_ entry.
-    if (SpaceIsLarge(space)) space = LO_SPACE;
-    return fullness_[space];
-  }
-
-  HeapObject* GetAddress(int space);
-  Address Allocate(int space, int size);
-  bool ReadObject(Object** write_back);
+  void ReadChunk(Object** start, Object** end, int space, Address address);
+  HeapObject* GetAddressFromStart(int space);
+  inline HeapObject* GetAddressFromEnd(int space);
+  Address Allocate(int space_number, Space* space, int size);
+  void ReadObject(int space_number, Space* space, Object** write_back);
 
   // Keep track of the pages in the paged spaces.
   // (In large object space we are keeping track of individual objects
@@ -476,11 +510,13 @@ class Deserializer2: public SerDes {
 
   SnapshotByteSource* source_;
   ExternalReferenceDecoder* external_reference_decoder_;
-  // Keep track of the fullness of each space in order to generate
-  // relative addresses for back references.  Large objects are
-  // just numbered sequentially since relative addresses make no
-  // sense in large object space.
-  int fullness_[LAST_SPACE + 1];
+  // This is the address of the next object that will be allocated in each
+  // space.  It is used to calculate the addresses of back-references.
+  Address high_water_[LAST_SPACE + 1];
+  // This is the address of the most recent object that was allocated.  It
+  // is used to set the location of the new page when we encounter a
+  // START_NEW_PAGE_SERIALIZATION tag.
+  Address last_object_address_;
 
   DISALLOW_COPY_AND_ASSIGN(Deserializer2);
 };
@@ -490,6 +526,9 @@ class SnapshotByteSink {
  public:
   virtual ~SnapshotByteSink() { }
   virtual void Put(int byte, const char* description) = 0;
+  virtual void PutSection(int byte, const char* description) {
+    Put(byte, description);
+  }
   void PutInt(uintptr_t integer, const char* description);
 };
 
@@ -550,7 +589,7 @@ class Serializer2 : public SerDes {
   // for all large objects since you can't check the type of the object
   // once the map has been used for the serialization address.
   static int SpaceOfAlreadySerializedObject(HeapObject* object);
-  int Allocate(int space, int size);
+  int Allocate(int space, int size, bool* new_page_started);
   int CurrentAllocationAddress(int space) {
     if (SpaceIsLarge(space)) space = LO_SPACE;
     return fullness_[space];