New snapshot framework. Doesn't work on ARM yet (code targets

are different).  Is able to deserialize the whole heap and run
some stuff.  Not available as the primary snapshot system yet.
Review URL: http://codereview.chromium.org/335009

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3142 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
erik.corry@gmail.com 2009-10-27 11:54:01 +00:00
parent 0695df6eaa
commit fb2317b63b
20 changed files with 960 additions and 46 deletions

View File

@ -452,8 +452,8 @@ class V8EXPORT HandleScope {
void* operator new(size_t size);
void operator delete(void*, size_t);
// This Data class is accessible internally through a typedef in the
// ImplementationUtilities class.
// This Data class is accessible internally as HandleScopeData through a
// typedef in the ImplementationUtilities class.
class V8EXPORT Data {
public:
int extensions;

View File

@ -437,6 +437,11 @@ class Assembler : public Malloced {
INLINE(static Address target_address_at(Address pc));
INLINE(static void set_target_address_at(Address pc, Address target));
// Here we are patching the address in the constant pool, not the actual call
// instruction. The address in the constant pool is the same size as a
// pointer.
static const int kCallTargetSize = kPointerSize;
// Size of an instruction.
static const int kInstrSize = sizeof(Instr);

View File

@ -316,8 +316,11 @@ Genesis* Genesis::current_ = NULL;
void Bootstrapper::Iterate(ObjectVisitor* v) {
natives_cache.Iterate(v);
v->Synchronize("NativesCache");
extensions_cache.Iterate(v);
v->Synchronize("Extensions");
PendingFixups::Iterate(v);
v->Synchronize("PendingFixups");
}

View File

@ -248,6 +248,7 @@ class Variable;
class VariableProxy;
class RelocInfo;
class Deserializer;
class GenericDeserializer; // TODO(erikcorry): Get rid of this.
class MessageLocation;
class ObjectGroup;
class TickSample;
@ -272,7 +273,9 @@ enum AllocationSpace {
LO_SPACE, // Promoted large objects.
FIRST_SPACE = NEW_SPACE,
LAST_SPACE = LO_SPACE
LAST_SPACE = LO_SPACE,
FIRST_PAGED_SPACE = OLD_POINTER_SPACE,
LAST_PAGED_SPACE = CELL_SPACE
};
const int kSpaceTagSize = 3;
const int kSpaceTagMask = (1 << kSpaceTagSize) - 1;

View File

@ -114,6 +114,7 @@ int Heap::mc_count_ = 0;
int Heap::gc_count_ = 0;
int Heap::always_allocate_scope_depth_ = 0;
int Heap::linear_allocation_scope_depth_ = 0;
bool Heap::context_disposed_pending_ = false;
#ifdef DEBUG
@ -3243,60 +3244,53 @@ void Heap::IterateRSet(PagedSpace* space, ObjectSlotCallback copy_object_func) {
}
#ifdef DEBUG
#define SYNCHRONIZE_TAG(tag) v->Synchronize(tag)
#else
#define SYNCHRONIZE_TAG(tag)
#endif
void Heap::IterateRoots(ObjectVisitor* v) {
IterateStrongRoots(v);
v->VisitPointer(reinterpret_cast<Object**>(&roots_[kSymbolTableRootIndex]));
SYNCHRONIZE_TAG("symbol_table");
v->Synchronize("symbol_table");
}
void Heap::IterateStrongRoots(ObjectVisitor* v) {
v->VisitPointers(&roots_[0], &roots_[kStrongRootListLength]);
SYNCHRONIZE_TAG("strong_root_list");
v->Synchronize("strong_root_list");
v->VisitPointer(bit_cast<Object**, String**>(&hidden_symbol_));
SYNCHRONIZE_TAG("symbol");
v->Synchronize("symbol");
Bootstrapper::Iterate(v);
SYNCHRONIZE_TAG("bootstrapper");
v->Synchronize("bootstrapper");
Top::Iterate(v);
SYNCHRONIZE_TAG("top");
v->Synchronize("top");
Relocatable::Iterate(v);
SYNCHRONIZE_TAG("relocatable");
v->Synchronize("relocatable");
#ifdef ENABLE_DEBUGGER_SUPPORT
Debug::Iterate(v);
#endif
SYNCHRONIZE_TAG("debug");
v->Synchronize("debug");
CompilationCache::Iterate(v);
SYNCHRONIZE_TAG("compilationcache");
v->Synchronize("compilationcache");
// Iterate over local handles in handle scopes.
HandleScopeImplementer::Iterate(v);
SYNCHRONIZE_TAG("handlescope");
v->Synchronize("handlescope");
// Iterate over the builtin code objects and code stubs in the heap. Note
// that it is not strictly necessary to iterate over code objects on
// scavenge collections. We still do it here because this same function
// is used by the mark-sweep collector and the deserializer.
Builtins::IterateBuiltins(v);
SYNCHRONIZE_TAG("builtins");
v->Synchronize("builtins");
// Iterate over global handles.
GlobalHandles::IterateRoots(v);
SYNCHRONIZE_TAG("globalhandles");
v->Synchronize("globalhandles");
// Iterate over pointers being held by inactive threads.
ThreadManager::Iterate(v);
SYNCHRONIZE_TAG("threadmanager");
v->Synchronize("threadmanager");
}
#undef SYNCHRONIZE_TAG
// Flag is set when the heap has been configured. The heap can be repeatedly

View File

@ -38,7 +38,13 @@ namespace internal {
// Defines all the roots in Heap.
#define UNCONDITIONAL_STRONG_ROOT_LIST(V) \
/* Cluster the most popular ones in a few cache lines here at the top. */ \
/* Put the byte array map early. We need it to be in place by the time */ \
/* the deserializer hits the next page, since it wants to put a byte */ \
/* array in the unused space at the end of the page. */ \
V(Map, byte_array_map, ByteArrayMap) \
V(Map, one_pointer_filler_map, OnePointerFillerMap) \
V(Map, two_pointer_filler_map, TwoPointerFillerMap) \
/* Cluster the most popular ones in a few cache lines here at the top. */ \
V(Smi, stack_limit, StackLimit) \
V(Object, undefined_value, UndefinedValue) \
V(Object, the_hole_value, TheHoleValue) \
@ -109,7 +115,6 @@ namespace internal {
undetectable_medium_ascii_string_map, \
UndetectableMediumAsciiStringMap) \
V(Map, undetectable_long_ascii_string_map, UndetectableLongAsciiStringMap) \
V(Map, byte_array_map, ByteArrayMap) \
V(Map, pixel_array_map, PixelArrayMap) \
V(Map, external_byte_array_map, ExternalByteArrayMap) \
V(Map, external_unsigned_byte_array_map, ExternalUnsignedByteArrayMap) \
@ -126,8 +131,6 @@ namespace internal {
V(Map, boilerplate_function_map, BoilerplateFunctionMap) \
V(Map, shared_function_info_map, SharedFunctionInfoMap) \
V(Map, proxy_map, ProxyMap) \
V(Map, one_pointer_filler_map, OnePointerFillerMap) \
V(Map, two_pointer_filler_map, TwoPointerFillerMap) \
V(Object, nan_value, NanValue) \
V(Object, minus_zero_value, MinusZeroValue) \
V(String, empty_string, EmptyString) \
@ -305,6 +308,9 @@ class Heap : public AllStatic {
static Address always_allocate_scope_depth_address() {
return reinterpret_cast<Address>(&always_allocate_scope_depth_);
}
static bool linear_allocation() {
return linear_allocation_scope_depth_ != 0;
}
static Address* NewSpaceAllocationTopAddress() {
return new_space_.allocation_top_address();
@ -750,7 +756,7 @@ class Heap : public AllStatic {
static bool Contains(HeapObject* value);
// Checks whether an address/object in a space.
// Currently used by tests and heap verification only.
// Currently used by tests, serialization and heap verification only.
static bool InSpace(Address addr, AllocationSpace space);
static bool InSpace(HeapObject* value, AllocationSpace space);
@ -921,6 +927,7 @@ class Heap : public AllStatic {
static int survived_since_last_expansion_;
static int always_allocate_scope_depth_;
static int linear_allocation_scope_depth_;
static bool context_disposed_pending_;
static const int kMaxMapSpaceSize = 8*MB;
@ -1136,6 +1143,7 @@ class Heap : public AllStatic {
friend class Factory;
friend class DisallowAllocationFailure;
friend class AlwaysAllocateScope;
friend class LinearAllocationScope;
};
@ -1157,6 +1165,19 @@ class AlwaysAllocateScope {
};
class LinearAllocationScope {
public:
LinearAllocationScope() {
Heap::linear_allocation_scope_depth_++;
}
~LinearAllocationScope() {
Heap::linear_allocation_scope_depth_--;
ASSERT(Heap::linear_allocation_scope_depth_ >= 0);
}
};
#ifdef DEBUG
// Visitor class to verify interior pointers that do not have remembered set
// bits. All heap object pointers have to point into the heap to a location

View File

@ -439,6 +439,8 @@ class Assembler : public Malloced {
inline static Address target_address_at(Address pc);
inline static void set_target_address_at(Address pc, Address target);
static const int kCallTargetSize = kPointerSize;
// Distance between the address of the code target in the call instruction
// and the return address
static const int kCallTargetAddressOffset = kPointerSize;

View File

@ -48,6 +48,7 @@ template <typename T, class P>
class List {
public:
List() { Initialize(0); }
INLINE(explicit List(int capacity)) { Initialize(capacity); }
INLINE(~List()) { DeleteData(data_); }

View File

@ -945,6 +945,25 @@ HeapObject* MapWord::ToForwardingAddress() {
}
bool MapWord::IsSerializationAddress() {
return HAS_SMI_TAG(reinterpret_cast<Object*>(value_));
}
MapWord MapWord::FromSerializationAddress(int raw) {
// When the map word is being used as a serialization address we Smi-encode
// the serialization address (which is always a smallish positive integer).
return MapWord(reinterpret_cast<uintptr_t>(Smi::FromInt(raw)));
}
int MapWord::ToSerializationAddress() {
// When the map word is being used as a serialization address we treat the
// map word as a Smi and get the small integer that it encodes.
return reinterpret_cast<Smi*>(value_)->value();
}
bool MapWord::IsMarked() {
return (value_ & kMarkingMask) == 0;
}

View File

@ -1082,6 +1082,15 @@ class MapWord BASE_EMBEDDED {
// View this map word as a forwarding address.
inline HeapObject* ToForwardingAddress();
// True if this map word is a serialization address. This will only be the
// case during a destructive serialization of the heap.
inline bool IsSerializationAddress();
// Create a map word from a serialization address.
static inline MapWord FromSerializationAddress(int raw);
// View this map word as a serialization address.
inline int ToSerializationAddress();
// Marking phase of full collection: the map word of live objects is
// marked, and may be marked as overflowed (eg, the object is live, its
@ -5105,6 +5114,8 @@ class ObjectVisitor BASE_EMBEDDED {
// Intended for serialization/deserialization checking: insert, or
// check for the presence of, a tag at this position in the stream.
virtual void Synchronize(const char* tag) {}
#else
inline void Synchronize(const char* tag) {}
#endif
};

View File

@ -1417,7 +1417,27 @@ void Deserializer::Synchronize(const char* tag) {
#endif
class NoGlobalHandlesChecker : public ObjectVisitor {
public:
virtual void VisitPointers(Object** start, Object** end) {
ASSERT(false);
}
};
class GlobalHandleDestroyer : public ObjectVisitor {
void VisitPointers(Object**start, Object**end) {
while (start < end) {
GlobalHandles::Destroy(start++);
}
}
};
void Deserializer::Deserialize() {
// No global handles.
NoGlobalHandlesChecker checker;
GlobalHandles::IterateRoots(&checker);
// No active threads.
ASSERT_EQ(NULL, ThreadState::FirstInUse());
// No active handles.
@ -1428,6 +1448,10 @@ void Deserializer::Deserialize() {
GetHeader();
Heap::IterateRoots(this);
GetContextStack();
// Any global handles that have been set up by deserialization are leaked
// since noone is keeping track of them. So we discard them now.
GlobalHandleDestroyer destroyer;
GlobalHandles::IterateRoots(&destroyer);
}
@ -1740,4 +1764,486 @@ 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;
}
fullness_[space_index] = new_fullness;
Object* new_allocation = space->AllocateRaw(size);
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)) {
ASSERT(size > Page::kPageSize - Page::kObjectStartOffset);
fullness_[LO_SPACE]++;
LargeObjectSpace* lo_space = Heap::lo_space();
Object* new_allocation;
if (space_index == kLargeData) {
new_allocation = lo_space->AllocateRaw(size);
} else if (space_index == kLargeFixedArray) {
new_allocation = lo_space->AllocateRawFixedArray(size);
} else {
ASSERT(space_index == kLargeCode);
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);
}
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) {
int offset = source_->GetInt();
if (SpaceIsLarge(space)) {
// Large spaces have one object per 'page'.
return HeapObject::FromAddress(
pages_[LO_SPACE][fullness_[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);
}
ASSERT(SpaceIsPaged(space));
int virtual_address = fullness_[space] - offset;
int page_of_pointee = (virtual_address) >> Page::kPageSizeBits;
Address object_address = pages_[space][page_of_pointee] +
(virtual_address & Page::kPageAlignmentMask);
return HeapObject::FromAddress(object_address);
}
void Deserializer2::Deserialize() {
// Don't GC while deserializing - just expand the heap.
AlwaysAllocateScope always_allocate;
// Don't use the free lists while deserializing.
LinearAllocationScope allocate_linearly;
// No active threads.
ASSERT_EQ(NULL, ThreadState::FirstInUse());
// No active handles.
ASSERT(HandleScopeImplementer::instance()->blocks()->is_empty());
ASSERT(external_reference_decoder_ == NULL);
external_reference_decoder_ = new ExternalReferenceDecoder();
Heap::IterateRoots(this);
ASSERT(source_->AtEOF());
delete external_reference_decoder_;
external_reference_decoder_ = NULL;
}
// This is called on the roots. It is the driver of the deserialization
// process.
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);
}
}
}
// This routine writes the new object into the pointer provided and then
// returns true if the new object was in young space and false otherwise.
// The reason for this strange interface is that otherwise the object is
// 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();
int size = source_->GetInt() << kObjectAlignmentBits;
Address address = Allocate(space, size);
*write_back = HeapObject::FromAddress(address);
Object** current = reinterpret_cast<Object**>(address);
Object** limit = current + (size >> kPointerSizeLog2);
while (current < limit) {
DataType data = static_cast<DataType>(source_->Get());
switch (data) {
case SMI_SERIALIZATION:
*current++ = Smi::FromInt(source_->GetInt() - kSmiBias);
break;
case RAW_DATA_SERIALIZATION: {
int size = source_->GetInt();
byte* raw_data_out = reinterpret_cast<byte*>(current);
for (int j = 0; j < size; j++) {
*raw_data_out++ = source_->Get();
}
current = reinterpret_cast<Object**>(raw_data_out);
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) {
Heap::RecordWrite(address,
reinterpret_cast<Address>(current) - address);
}
current++;
break;
}
case CODE_OBJECT_SERIALIZATION: {
Object* new_code_object = NULL;
ReadObject(&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_address_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 BACKREF_SERIALIZATION: {
// Write a backreference to an object we unpacked earlier.
int backref_space = source_->Get();
if (backref_space == NEW_SPACE && space != NEW_SPACE) {
Heap::RecordWrite(address,
reinterpret_cast<Address>(current) - address);
}
*current++ = GetAddress(backref_space);
break;
}
case CODE_BACKREF_SERIALIZATION: {
int backref_space = source_->Get();
// 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));
// Setting a branch/call to previously decoded code object from code.
Address location_of_branch_data = reinterpret_cast<Address>(current);
Assembler::set_target_address_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);
*current++ = reinterpret_cast<Object*>(address);
break;
}
default:
UNREACHABLE();
}
}
ASSERT(current == limit);
return space == NEW_SPACE;
}
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 & 0x7f, "intlastpart");
}
#ifdef DEBUG
void Deserializer2::Synchronize(const char* tag) {
int data = source_->Get();
// If this assert fails then that indicates that you have a mismatch between
// the number of GC roots when serializing and deserializing.
ASSERT(data == SYNCHRONIZE);
do {
int character = source_->Get();
if (character == 0) break;
if (FLAG_debug_serialization) {
PrintF("%c", character);
}
} while (true);
if (FLAG_debug_serialization) {
PrintF("\n");
}
}
void Serializer2::Synchronize(const char* tag) {
sink_->Put(SYNCHRONIZE, tag);
int character;
do {
character = *tag++;
sink_->Put(character, "tagcharacter");
} while (character != 0);
}
#endif
Serializer2::Serializer2(SnapshotByteSink* sink)
: sink_(sink),
current_root_index_(0),
external_reference_encoder_(NULL) {
for (int i = 0; i <= LAST_SPACE; i++) {
fullness_[i] = 0;
}
}
void Serializer2::Serialize() {
// No active threads.
CHECK_EQ(NULL, ThreadState::FirstInUse());
// No active or weak handles.
CHECK(HandleScopeImplementer::instance()->blocks()->is_empty());
CHECK_EQ(0, GlobalHandles::NumberOfWeakHandles());
ASSERT(external_reference_encoder_ == NULL);
external_reference_encoder_ = new ExternalReferenceEncoder();
Heap::IterateRoots(this);
delete external_reference_encoder_;
external_reference_encoder_ = NULL;
}
void Serializer2::VisitPointers(Object** start, Object** end) {
for (Object** current = start; current < end; current++) {
SerializeObject(*current, TAGGED_REPRESENTATION);
}
}
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");
} else {
ASSERT(reference_representation == TAGGED_REPRESENTATION);
sink_->Put(BACKREF_SERIALIZATION, "BackRefSerialization");
}
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();
}
} else {
// Serialize a Smi.
unsigned int value = Smi::cast(o)->value() + kSmiBias;
sink_->Put(SMI_SERIALIZATION, "SmiSerialization");
sink_->PutInt(value, "smi");
}
}
void Serializer2::ObjectSerializer::Serialize() {
int space = Serializer2::SpaceOfObject(object_);
int size = object_->Size();
if (reference_representation_ == TAGGED_REPRESENTATION) {
sink_->Put(OBJECT_SERIALIZATION, "ObjectSerialization");
} else {
ASSERT(reference_representation_ == CODE_TARGET_REPRESENTATION);
sink_->Put(CODE_OBJECT_SERIALIZATION, "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)));
// Serialize the map (first word of the object).
serializer_->SerializeObject(map, TAGGED_REPRESENTATION);
// Serialize the rest of the object.
ASSERT(bytes_processed_so_far_ == 0);
bytes_processed_so_far_ = kPointerSize;
object_->IterateBody(map->instance_type(), size, this);
OutputRawData(object_->address() + size);
}
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);
}
bytes_processed_so_far_ += (end - start) * kPointerSize;
}
void Serializer2::ObjectSerializer::VisitExternalReferences(Address* start,
Address* end) {
Address references_start = reinterpret_cast<Address>(start);
OutputRawData(references_start);
for (Address* current = start; current < end; current++) {
sink_->Put(EXTERNAL_REFERENCE_SERIALIZATION, "External reference");
int reference_id = serializer_->EncodeExternalReference(*current);
sink_->PutInt(reference_id, "reference id");
}
bytes_processed_so_far_ += (end - start) * kPointerSize;
}
void Serializer2::ObjectSerializer::VisitCodeTarget(RelocInfo* rinfo) {
ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
Address target_start = rinfo->target_address_address();
OutputRawData(target_start);
Code* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
serializer_->SerializeObject(target, CODE_TARGET_REPRESENTATION);
bytes_processed_so_far_ += Assembler::kCallTargetSize;
}
void Serializer2::ObjectSerializer::OutputRawData(Address up_to) {
Address object_start = object_->address();
int up_to_offset = up_to - object_start;
int skipped = up_to_offset - bytes_processed_so_far_;
ASSERT(skipped >= 0);
if (skipped != 0) {
sink_->Put(RAW_DATA_SERIALIZATION, "raw data");
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");
}
}
bytes_processed_so_far_ += skipped;
}
int Serializer2::SpaceOfObject(HeapObject* object) {
for (int i = FIRST_SPACE; i <= LAST_SPACE; i++) {
AllocationSpace s = static_cast<AllocationSpace>(i);
if (Heap::InSpace(object, s)) {
if (i == LO_SPACE) {
if (object->IsCode()) {
return kLargeCode;
} else if (object->IsFixedArray()) {
return kLargeFixedArray;
} else {
return kLargeData;
}
}
return i;
}
}
UNREACHABLE();
return 0;
}
int Serializer2::SpaceOfAlreadySerializedObject(HeapObject* object) {
for (int i = FIRST_SPACE; i <= LAST_SPACE; i++) {
AllocationSpace s = static_cast<AllocationSpace>(i);
if (Heap::InSpace(object, s)) {
return i;
}
}
UNREACHABLE();
return 0;
}
int Serializer2::Allocate(int space, int size) {
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.
return fullness_[LO_SPACE]++;
}
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
// 0 - Page::kObjectAreaSize. In practice the pages may not be contiguous
// and allocation does not start at offset 0 in the page, but this scheme
// means the deserializer can get the page number quickly by shifting the
// serialized address.
ASSERT(IsPowerOf2(Page::kPageSize));
int used_in_this_page = (fullness_[space] & (Page::kPageSize - 1));
ASSERT(size <= Page::kObjectAreaSize);
if (used_in_this_page + size > Page::kObjectAreaSize) {
fullness_[space] = RoundUp(fullness_[space], Page::kPageSize);
}
}
int allocation_address = fullness_[space];
fullness_[space] = allocation_address + size;
return allocation_address;
}
} } // namespace v8::internal

View File

@ -262,7 +262,18 @@ class SnapshotReader {
// A Deserializer reads a snapshot and reconstructs the Object graph it defines.
class Deserializer: public ObjectVisitor {
// TODO(erikcorry): Get rid of this superclass when we are using the new
// snapshot code exclusively.
class GenericDeserializer: public ObjectVisitor {
public:
virtual void GetLog() = 0;
virtual void Deserialize() = 0;
};
// TODO(erikcorry): Get rid of this class.
class Deserializer: public GenericDeserializer {
public:
// Create a deserializer. The snapshot is held in str and has size len.
Deserializer(const byte* str, int len);
@ -339,6 +350,223 @@ class Deserializer: public ObjectVisitor {
DISALLOW_COPY_AND_ASSIGN(Deserializer);
};
class SnapshotByteSource {
public:
SnapshotByteSource(const byte* array, int length)
: data_(array), length_(length), position_(0) { }
bool HasMore() { return position_ < length_; }
int Get() {
ASSERT(position_ < length_);
return data_[position_++];
}
int GetInt() {
// A little unwind to catch the really small ints.
int snapshot_byte = Get();
if ((snapshot_byte & 0x80) == 0) {
return snapshot_byte;
}
uintptr_t accumulator = (snapshot_byte & 0x7f) << 7;
while (true) {
snapshot_byte = Get();
if ((snapshot_byte & 0x80) == 0) {
return accumulator | snapshot_byte;
}
accumulator = (accumulator | (snapshot_byte & 0x7f)) << 7;
}
UNREACHABLE();
return accumulator;
}
bool AtEOF() {
return position_ == length_;
}
private:
const byte* data_;
int length_;
int position_;
};
// The SerDes class is a common superclass for Serializer2 and Deserializer2
// which is used to store common constants and methods used by both.
// TODO(erikcorry): This should inherit from ObjectVisitor.
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,
SYNCHRONIZE
};
// 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;
static inline bool SpaceIsLarge(int space) { return space >= kLargeData; }
static inline bool SpaceIsPaged(int space) {
return space >= FIRST_PAGED_SPACE && space <= LAST_PAGED_SPACE;
}
};
// A Deserializer reads a snapshot and reconstructs the Object graph it defines.
class Deserializer2: public SerDes {
public:
// Create a deserializer from a snapshot byte source.
explicit Deserializer2(SnapshotByteSource* source);
virtual ~Deserializer2() { }
// Deserialize the snapshot into an empty heap.
void Deserialize();
void GetLog() { } // TODO(erikcorry): Get rid of this.
#ifdef DEBUG
virtual void Synchronize(const char* tag);
#endif
private:
virtual void VisitPointers(Object** start, Object** end);
virtual void VisitExternalReferences(Address* start, Address* end) {
UNREACHABLE();
}
virtual void VisitRuntimeEntry(RelocInfo* rinfo) {
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);
// Keep track of the pages in the paged spaces.
// (In large object space we are keeping track of individual objects
// rather than pages.) In new space we just need the address of the
// first object and the others will flow from that.
List<Address> pages_[SerDes::kNumberOfSpaces];
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];
DISALLOW_COPY_AND_ASSIGN(Deserializer2);
};
class SnapshotByteSink {
public:
virtual ~SnapshotByteSink() { }
virtual void Put(int byte, const char* description) = 0;
void PutInt(uintptr_t integer, const char* description);
};
class Serializer2 : public SerDes {
public:
explicit Serializer2(SnapshotByteSink* sink);
// Serialize the current state of the heap. This operation destroys the
// heap contents.
void Serialize();
void VisitPointers(Object** start, Object** end);
void GetLog() { } // TODO(erikcorry): Get rid of this.
void Deserialize() { } // TODO(erikcorry): Get rid of this.
#ifdef DEBUG
virtual void Synchronize(const char* tag);
#endif
private:
enum ReferenceRepresentation {
TAGGED_REPRESENTATION, // A tagged object reference.
CODE_TARGET_REPRESENTATION // A reference to first instruction in target.
};
class ObjectSerializer : public ObjectVisitor {
public:
ObjectSerializer(Serializer2* serializer,
Object* o,
SnapshotByteSink* sink,
ReferenceRepresentation representation)
: serializer_(serializer),
object_(HeapObject::cast(o)),
sink_(sink),
reference_representation_(representation),
bytes_processed_so_far_(0) { }
void Serialize();
void VisitPointers(Object** start, Object** end);
void VisitExternalReferences(Address* start, Address* end);
void VisitCodeTarget(RelocInfo* target);
private:
void OutputRawData(Address up_to);
Serializer2* serializer_;
HeapObject* object_;
SnapshotByteSink* sink_;
ReferenceRepresentation reference_representation_;
int bytes_processed_so_far_;
};
void SerializeObject(Object* o, ReferenceRepresentation representation);
void InitializeAllocators();
// This will return the space for an object. If the object is in large
// object space it may return kLargeCode or kLargeFixedArray in order
// to indicate to the deserializer what kind of large object allocation
// to make.
static int SpaceOfObject(HeapObject* object);
// This just returns the space of the object. It will return LO_SPACE
// 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 CurrentAllocationAddress(int space) {
if (SpaceIsLarge(space)) space = LO_SPACE;
return fullness_[space];
}
int EncodeExternalReference(Address addr) {
return external_reference_encoder_->Encode(addr);
}
// 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];
SnapshotByteSink* sink_;
int current_root_index_;
ExternalReferenceEncoder* external_reference_encoder_;
friend class ObjectSerializer;
friend class Deserializer2;
DISALLOW_COPY_AND_ASSIGN(Serializer2);
};
} } // namespace v8::internal
#endif // V8_SERIALIZE_H_

View File

@ -43,6 +43,13 @@ bool Snapshot::Deserialize(const byte* content, int len) {
}
bool Snapshot::Deserialize2(const byte* content, int len) {
SnapshotByteSource source(content, len);
Deserializer2 deserializer(&source);
return V8::Initialize(&deserializer);
}
bool Snapshot::Initialize(const char* snapshot_file) {
if (snapshot_file) {
int len;
@ -58,6 +65,20 @@ bool Snapshot::Initialize(const char* snapshot_file) {
}
bool Snapshot::Initialize2(const char* snapshot_file) {
if (snapshot_file) {
int len;
byte* str = ReadBytes(snapshot_file, &len);
if (!str) return false;
Deserialize2(str, len);
DeleteArray(str);
} else if (size_ > 0) {
Deserialize2(data_, size_);
}
return true;
}
bool Snapshot::WriteToFile(const char* snapshot_file) {
Serializer ser;
ser.Serialize();
@ -72,4 +93,38 @@ bool Snapshot::WriteToFile(const char* snapshot_file) {
}
class FileByteSink : public SnapshotByteSink {
public:
explicit FileByteSink(const char* snapshot_file) {
fp_ = fopen(snapshot_file, "wb");
if (fp_ == NULL) {
PrintF("Unable to write to snapshot file \"%s\"\n", snapshot_file);
exit(1);
}
}
virtual ~FileByteSink() {
if (fp_ != NULL) {
fclose(fp_);
}
}
virtual void Put(int byte, const char* description) {
if (fp_ != NULL) {
fputc(byte, fp_);
}
}
private:
FILE* fp_;
};
bool Snapshot::WriteToFile2(const char* snapshot_file) {
FileByteSink file(snapshot_file);
Serializer2 ser(&file);
ser.Serialize();
return true;
}
} } // namespace v8::internal

View File

@ -37,6 +37,7 @@ class Snapshot {
// NULL, use the internal snapshot instead. Returns false if no snapshot
// could be found.
static bool Initialize(const char* snapshot_file = NULL);
static bool Initialize2(const char* snapshot_file = NULL);
// Returns whether or not the snapshot is enabled.
static bool IsEnabled() { return size_ != 0; }
@ -44,12 +45,14 @@ class Snapshot {
// Write snapshot to the given file. Returns true if snapshot was written
// successfully.
static bool WriteToFile(const char* snapshot_file);
static bool WriteToFile2(const char* snapshot_file);
private:
static const byte data_[];
static int size_;
static bool Deserialize(const byte* content, int len);
static bool Deserialize2(const byte* content, int len);
DISALLOW_IMPLICIT_CONSTRUCTORS(Snapshot);
};

View File

@ -1527,7 +1527,9 @@ void FreeListNode::set_size(int size_in_bytes) {
// correct size.
if (size_in_bytes > ByteArray::kAlignedSize) {
set_map(Heap::raw_unchecked_byte_array_map());
ByteArray::cast(this)->set_length(ByteArray::LengthFor(size_in_bytes));
// Can't use ByteArray::cast because it fails during deserialization.
ByteArray* this_as_byte_array = reinterpret_cast<ByteArray*>(this);
this_as_byte_array->set_length(ByteArray::LengthFor(size_in_bytes));
} else if (size_in_bytes == kPointerSize) {
set_map(Heap::raw_unchecked_one_pointer_filler_map());
} else if (size_in_bytes == 2 * kPointerSize) {
@ -1535,7 +1537,8 @@ void FreeListNode::set_size(int size_in_bytes) {
} else {
UNREACHABLE();
}
ASSERT(Size() == size_in_bytes);
// We would like to ASSERT(Size() == size_in_bytes) but this would fail during
// deserialization because the byte array map is not done yet.
}
@ -1828,13 +1831,16 @@ HeapObject* OldSpace::SlowAllocateRaw(int size_in_bytes) {
return AllocateInNextPage(current_page, size_in_bytes);
}
// There is no next page in this space. Try free list allocation.
int wasted_bytes;
Object* result = free_list_.Allocate(size_in_bytes, &wasted_bytes);
accounting_stats_.WasteBytes(wasted_bytes);
if (!result->IsFailure()) {
accounting_stats_.AllocateBytes(size_in_bytes);
return HeapObject::cast(result);
// There is no next page in this space. Try free list allocation unless that
// is currently forbidden.
if (!Heap::linear_allocation()) {
int wasted_bytes;
Object* result = free_list_.Allocate(size_in_bytes, &wasted_bytes);
accounting_stats_.WasteBytes(wasted_bytes);
if (!result->IsFailure()) {
accounting_stats_.AllocateBytes(size_in_bytes);
return HeapObject::cast(result);
}
}
// Free list allocation failed and there is no next page. Fail if we have
@ -2230,10 +2236,10 @@ HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) {
return AllocateInNextPage(current_page, size_in_bytes);
}
// There is no next page in this space. Try free list allocation.
// The fixed space free list implicitly assumes that all free blocks
// are of the fixed size.
if (size_in_bytes == object_size_in_bytes_) {
// There is no next page in this space. Try free list allocation unless
// that is currently forbidden. The fixed space free list implicitly assumes
// that all free blocks are of the fixed size.
if (!Heap::linear_allocation()) {
Object* result = free_list_.Allocate();
if (!result->IsFailure()) {
accounting_stats_.AllocateBytes(size_in_bytes);

View File

@ -45,7 +45,7 @@ bool V8::has_been_setup_ = false;
bool V8::has_been_disposed_ = false;
bool V8::has_fatal_error_ = false;
bool V8::Initialize(Deserializer *des) {
bool V8::Initialize(GenericDeserializer *des) {
bool create_heap_objects = des == NULL;
if (has_been_disposed_ || has_fatal_error_) return false;
if (IsRunning()) return true;

View File

@ -80,7 +80,7 @@ class V8 : public AllStatic {
// created from scratch. If a non-null Deserializer is given, the
// initial state is created by reading the deserialized data into an
// empty heap.
static bool Initialize(Deserializer* des);
static bool Initialize(GenericDeserializer* des);
static void TearDown();
static bool IsRunning() { return is_running_; }
// To be dead you have to have lived

View File

@ -459,6 +459,8 @@ class Assembler : public Malloced {
static inline Address target_address_at(Address pc);
static inline void set_target_address_at(Address pc, Address target);
inline Handle<Object> code_target_object_handle_at(Address pc);
// Number of bytes taken up by the branch target in the code.
static const int kCallTargetSize = 4; // Use 32-bit displacement.
// Distance between the address of the code target in the call instruction
// and the return address pushed on the stack.
static const int kCallTargetAddressOffset = 4; // Use 32-bit displacement.

View File

@ -47,6 +47,10 @@ test-serialize/DependentTestThatAlwaysFails: FAIL
[ $arch == arm ]
# New serialization doesn't work on ARM yet.
test-serialize/Deserialize2: SKIP
test-serialize/DeserializeAndRunScript2: SKIP
# BUG(113): Test seems flaky on ARM.
test-spaces/LargeObjectSpace: PASS || FAIL

View File

@ -185,6 +185,18 @@ static void Serialize() {
}
static void Serialize2() {
Serializer::Enable();
// We have to create one context. One reason for this is so that the builtins
// can be loaded from v8natives.js and their addresses can be processed. This
// will clear the pending fixups array, which would otherwise contain GC roots
// that would confuse the serialization/deserialization process.
v8::Persistent<v8::Context> env = v8::Context::New();
env.Dispose();
Snapshot::WriteToFile2(FLAG_testing_serialization_file);
}
// Test that the whole heap can be serialized when running from the
// internal snapshot.
// (Smoke test.)
@ -203,6 +215,13 @@ TEST(Serialize) {
}
// Test that the whole heap can be serialized.
TEST(Serialize2) {
v8::V8::Initialize();
Serialize2();
}
// Test that the heap isn't destroyed after a serialization.
TEST(SerializeNondestructive) {
if (Snapshot::IsEnabled()) return;
@ -230,6 +249,11 @@ static void Deserialize() {
}
static void Deserialize2() {
CHECK(Snapshot::Initialize2(FLAG_testing_serialization_file));
}
static void SanityCheck() {
v8::HandleScope scope;
#ifdef DEBUG
@ -251,6 +275,21 @@ DEPENDENT_TEST(Deserialize, Serialize) {
SanityCheck();
}
DEPENDENT_TEST(Deserialize2, Serialize2) {
v8::HandleScope scope;
Deserialize2();
fflush(stdout);
v8::Persistent<v8::Context> env = v8::Context::New();
env->Enter();
SanityCheck();
}
DEPENDENT_TEST(DeserializeAndRunScript, Serialize) {
v8::HandleScope scope;
@ -263,6 +302,21 @@ DEPENDENT_TEST(DeserializeAndRunScript, Serialize) {
}
DEPENDENT_TEST(DeserializeAndRunScript2, Serialize2) {
v8::HandleScope scope;
Deserialize2();
v8::Persistent<v8::Context> env = v8::Context::New();
env->Enter();
const char* c_source = "\"1234\".length";
v8::Local<v8::String> source = v8::String::New(c_source);
v8::Local<v8::Script> script = v8::Script::Compile(source);
CHECK_EQ(4, script->Run()->Int32Value());
}
DEPENDENT_TEST(DeserializeNatives, Serialize) {
v8::HandleScope scope;
@ -288,9 +342,6 @@ DEPENDENT_TEST(DeserializeExtensions, Serialize) {
}
extern "C" void V8_Fatal(const char* file, int line, const char* format, ...);
TEST(TestThatAlwaysSucceeds) {
}