Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(837)

Unified Diff: src/vm/snapshot.cc

Issue 2067483002: More compact snapshots. (Closed) Base URL: git@github.com:dartino/sdk.git@master
Patch Set: Feedback Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/vm/snapshot.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/vm/snapshot.cc
diff --git a/src/vm/snapshot.cc b/src/vm/snapshot.cc
index b9729166e78f663785010f17e8d3af57057d605d..4b79b7928b73e707e4fa2d0a8ff698833f84c2ac 100644
--- a/src/vm/snapshot.cc
+++ b/src/vm/snapshot.cc
@@ -19,6 +19,8 @@
namespace dartino {
+class SnapshotOracle;
+
void FixByteCodes(uint8* bcp, uword size, int old_shift, int new_shift);
class ReaderVisitor : public PointerVisitor {
@@ -206,8 +208,7 @@ void SnapshotReader::BuildLocationMap(Program* program, word total_floats,
break;
}
case kSnapshotRaw: {
- word x = (b & 7) + kSnapshotBias;
- ASSERT(x >= 0);
+ word x = ArgumentStart(b, w);
while (w-- != 0) {
x <<= 8;
x |= snapshot_[position++];
@@ -399,7 +400,7 @@ word SnapshotReader::ReadWord() {
} else {
uint8 opcode = OpCode(b);
int w = ArgumentBytes(b);
- word x = (b & 7) + kSnapshotBias;
+ word x = ArgumentStart(b, w);
for (int i = 0; i < w; i++) {
x <<= 8;
x |= ReadByte();
@@ -570,15 +571,19 @@ void FixByteCodes(uint8* bcp, uword size, int old_shift, int new_shift) {
class ObjectWriter : public PointerVisitor {
public:
ObjectWriter(SnapshotWriter* writer, PortableAddressMap* map,
+ SnapshotOracle* pointer_oracle, SnapshotOracle* smi_oracle,
HeapObject* current = NULL)
: writer_(writer),
address_map_(map),
+ pointer_oracle_(pointer_oracle),
+ smi_oracle_(smi_oracle),
current_(current == NULL ? 0 : reinterpret_cast<uint8*>(
current->address())) {}
virtual void VisitClass(Object** p) {
ASSERT(current_ == reinterpret_cast<uint8*>(p));
- WriteWord(*p, reinterpret_cast<uword>(p));
+ ASSERT(!(*p)->IsSmi());
+ WriteWord(*p);
current_ += kWordSize;
}
@@ -587,7 +592,7 @@ class ObjectWriter : public PointerVisitor {
uint8* block_start = reinterpret_cast<uint8*>(start);
ASSERT(current_ == 0 || block_start == current_);
for (Object** p = start; p < end; p++) {
- WriteWord(*p, reinterpret_cast<uword>(p));
+ WriteWord(*p);
}
if (current_ != NULL) current_ = reinterpret_cast<uint8*>(end);
}
@@ -659,119 +664,73 @@ class ObjectWriter : public PointerVisitor {
writer_->WriteBytes(start, size);
}
- int32 abs(int32 x) {
- if (x < 0) return -x;
- return x;
- }
+ void WriteInteger(word i);
- void WriteInteger(word i) {
- // Large Smis must be purged from the heap before we serialize, since
- // they can't be represented on small devices.
- ASSERT(i <= 0x7fffffff);
- ASSERT(i >= -0x7fffffff - 1);
- int32 offset = i - writer_->recent_smi();
- if (offset < -2 || abs(i) < abs(offset)) {
- WriteOpcode(kSnapshotSmi, i);
- } else {
- WriteOpcode(kSnapshotRecentSmi, offset);
- writer_->set_recent_smi(i);
- }
- }
-
- void WriteWord(Object* object, uword from = 0) {
- if (object->IsSmi()) {
- WriteInteger(reinterpret_cast<word>(object));
- return;
- }
-
- HeapObject* heap_object = HeapObject::cast(object);
- uword ideal = address_map_->IdealizedAddress(heap_object) -
- address_map_->doubles_size();
- int popular_index = writer_->PopularityIndex(heap_object);
- if (popular_index != -1) {
- ASSERT(popular_index < kSnapshotNumberOfPopularObjects &&
- popular_index >= 0);
- ASSERT(kSnapshotPopular == 0);
- ASSERT((kSnapshotPopularMask & kSnapshotPopular) == 0);
- ASSERT((kSnapshotPopularMask & (kSnapshotNumberOfPopularObjects - 1)) ==
- 0);
- writer_->WriteByte(popular_index);
- } else {
- // If there's a register that we can use that gives a 1-byte encoding,
- // then just use that.
- for (int i = 0; i < 2; i++) {
- word offset = ideal - writer_->recent(i);
- if (OpcodeLength(offset / kIdealWordSize) == 1) {
- WriteOpcode(kSnapshotRecentPointer + i, offset / kIdealWordSize);
- writer_->set_recent(i, ideal);
- return;
- }
- }
- int reg = -1;
- word offset0 = ideal - writer_->recent(0);
- word offset1 = ideal - writer_->recent(1);
- // For each encoding length, check if there is a register that we can
- // pick that will give that length. If there are two, pick the least
- // recently used.
- for (int goal = 2; goal <= 5; goal++) {
- word offset0 = ideal - writer_->recent(0);
- word offset1 = ideal - writer_->recent(1);
- if (OpcodeLength(offset0 / kIdealWordSize) == goal) {
- if (OpcodeLength(offset1 / kIdealWordSize) == goal) {
- if (writer_->lru(0) < writer_->lru(1)) {
- reg = 0;
- } else {
- reg = 1;
- }
- } else {
- reg = 0;
- }
- break;
- } else if (OpcodeLength(offset1 / kIdealWordSize) == goal) {
- reg = 1;
- break;
- }
- }
- word offset = reg ? offset1 : offset0;
- ASSERT(reg != -1);
- WriteOpcode(kSnapshotRecentPointer + reg, offset / kIdealWordSize);
- writer_->set_recent(reg, ideal);
- }
- }
+ void WriteWord(Object* object);
- int OpcodeLength(word offset) {
+ static int OpcodeLength(word offset) {
int w = 0;
- // We can't code for a w with 3, so we keep going to 4 if we hit that one.
- while (offset < kSnapshotBias || offset > 7 + kSnapshotBias || w == 3) {
+ int first_byte_bits = 4;
+ while (offset < kSnapshotBias ||
+ offset >= (1 << first_byte_bits) + kSnapshotBias) {
w++;
offset >>= 8; // Signed shift.
+ first_byte_bits--;
+ if (first_byte_bits == 0) {
+ ASSERT(w == 4);
+ ASSERT(offset <= 0 && offset >= kSnapshotBias);
+ return 5;
+ }
}
- ASSERT(w >= 0 && w <= 4 && w != 3);
+ ASSERT(w <= 4);
return w + 1;
}
void WriteOpcode(int opcode, word offset) {
- int w = 0;
- uint8 bytes[4] = {0, 0, 0, 0};
- // We can't code for a w with 3, so we keep going to 4 if we hit that one.
- while (offset < kSnapshotBias || offset > 7 + kSnapshotBias || w == 3) {
- w++;
- bytes[3] = bytes[2];
- bytes[2] = bytes[1];
- bytes[1] = bytes[0];
- bytes[0] = offset;
- offset >>= 8; // Signed shift.
+ int w = OpcodeLength(offset) - 1;
+ // bytecode has the opcode in the top 3 bits.
+ uint8 opcode_byte = opcode << 5;
+ // Move to 64 bit even on 32 bit platforms.
+ int64 x = offset;
+ int64 bias = -kSnapshotBias;
+ // We can do a shift of 32 on bias because it is a 64 bit value.
+ x += bias << (8 * w);
+ ASSERT(x >= 0 && (x >> 33) == 0); // Unsigned 33 bit value now.
+ // We can encode 4 bits for a 0-byte instruction. For each byte
+ // we add to the instruction we lose one bit in the initial word
+ // so we gain only 7 bits per extra byte. However for the 5-byte
+ // encoding where w == 4 we have an extra bit.
+ int64 one = 1; // Value that can legally be shifted by 32.
+ if (offset > 0 && w == 4) {
+ ASSERT(x == (x & ((one << 33) - 1)));
+ } else {
+ ASSERT(x == (x & ((one << (4 + 7 * w)) - 1)));
+ }
+ // The lower part of the bytecode has a unary encoding of the width
+ // and the first few bits of the biased offset.
+ // w == 0: 0xxxx
+ // w == 1: 10xxx
+ // w == 2: 110xx
+ // w == 3: 1110x
+ // w == 4: 1111x
+ uint8 width_indicator = 0x1f;
+ // Zap the low bits (the xs and the 0s).
+ width_indicator &= ~((1 << (5 - w)) - 1);
+ // The width indicator and the top part of the x may not overlap.
+ uint8 offset_top_part = x >> (8 * w);
+ ASSERT((width_indicator & offset_top_part) == 0);
+
+ writer_->WriteByte(opcode_byte | width_indicator | offset_top_part);
+ for (int i = w - 1; i >= 0; i--) {
+ writer_->WriteByte(x >> (8 * i));
}
- ASSERT(w >= 0 && w <= 4 && w != 3);
- uint8 opcode_byte = (opcode << 5) | ((w == 4 ? 3 : w) << 3) |
- ((offset - kSnapshotBias) & 7);
- writer_->WriteByte(opcode_byte);
- writer_->WriteBytes(bytes, w);
}
private:
SnapshotWriter* writer_;
PortableAddressMap* address_map_;
+ SnapshotOracle* pointer_oracle_;
+ SnapshotOracle* smi_oracle_;
// This is the position in the current object we are serializing. If we
// are outputting roots, which are not inside any object, then it is null.
// Only used for asserts, to ensure we don't skip part of the object
@@ -779,14 +738,209 @@ class ObjectWriter : public PointerVisitor {
uint8* current_;
};
+// The oracle tells the snapshot encoder at each step, which register to use,
+// using knowledge of the future pointers that the encoder will soon encounter.
+// Like all good oracles, it does this by cheating: It runs through the
+// pointers first, and remembers the order they arrived last time. In the first
+// pass it keeps track of all possible decisions for the last kStatesLog2
+// pointers. When a new pointer arrives, it discards half the possibilities, by
+// making a decision on how to encode the pointer we had, kStatesLog2 calls
+// ago. Then it doubles the number of possibilities by picking either register
+// 0 or register 1. Similarly in the smi mode it does this for Smis, with the
+// difference that here one of the registers is locked to zero (ie the choice
+// is between register-relative and absolute encoding).
+class SnapshotOracle : public PointerVisitor {
+ public:
+ SnapshotOracle(bool smi_mode, PortableAddressMap* map, SnapshotWriter* writer)
+ : smi_mode_(smi_mode), writer_(writer), map_(map) {
+ for (int i = 0; i < kStates; i++) {
+ costs_[i] = 0;
+ regs_[0][i] = 0;
+ regs_[1][i] = 0;
+ }
+ }
+
+ virtual void VisitInteger(uword slot) {
+ if (!smi_mode_) return;
+ word i = *reinterpret_cast<word*>(slot);
+ SimulateInput(i);
+ }
+
+ virtual void VisitLiteralInteger(int32 i) {
+ if (!smi_mode_) return;
+ SimulateInput(i);
+ }
+
+ virtual void VisitClass(Object** slot) { VisitBlock(slot, slot + 1); }
+
+ virtual void VisitBlock(Object** start, Object** end) {
+ for (Object** p = start; p < end; p++) {
+ Object* o = *p;
+ if (o->IsSmi()) {
+ if (!smi_mode_) continue;
+ SimulateInput(static_cast<int>(reinterpret_cast<word>(o)));
+ } else {
+ if (smi_mode_) continue;
+ int addr = map_->IdealizedAddress(o);
+ int popular_index = writer_->PopularityIndex(HeapObject::cast(o));
+ if (popular_index == -1) {
+ SimulateInput(addr);
+ }
+ }
+ }
+ }
+
+ void WrapUp() {
+ int bogus = kStatesLog2 - unused_;
+ for (int i = 0; i < bogus; i++) {
+ // Add enough bogus entries to the list of pointers to be encoded. These
+ // have the effect of forcing the decision to be made on all pointers up
+ // to the last real one.
+ SimulateInput(0);
+ }
+ }
+
+ void SimulateInput(int addr) {
+ int best_cost = 1000000000;
+ int best_reg = -1;
+ for (int i = 0; i < kStates; i++) {
+ if (costs_[i] >= best_cost) continue; // Optimization.
+ for (int reg = 0; reg < 2; reg++) {
+ int diff = addr - regs_[reg][i];
+ int cost = ObjectWriter::OpcodeLength(diff >> (smi_mode_ ? 0 : 2));
+ if (costs_[i] + cost < best_cost) {
+ best_reg = ((i >> (kStatesLog2 - 1)) & 1);
+ best_cost = costs_[i] + cost;
+ }
+ }
+ }
+ // Oldest choice is in the most significant bit.
+ // Find a decision that is now made, using the oldest possibility.
+ int decision = best_reg;
+ if (unused_ == 0) {
+ script_.PushBack(decision);
+ } else {
+ unused_--;
+ }
+ for (int i = 0; i < kStatesLog2 - 1; i++) ideals_[i] = ideals_[i + 1];
+ ideals_[kStatesLog2 - 1] = addr;
+ if (decision == 0) {
+ // Keep the left-hand-side entries (the lower half of the leaf arrays),
+ // and spread them out.
+ for (int i = kStates - 1; i >= 0; i--) {
+ int j = i >> 1;
+ costs_[i] = costs_[j];
+ regs_[0][i] = regs_[0][j];
+ regs_[1][i] = regs_[1][j];
+ }
+ } else {
+ // Keep the right-hand-side entries (the upper half of the leaf arrays),
+ // and spread them out.
+ for (int i = 0; i < kStates; i++) {
+ int j = kStates / 2 + (i >> 1);
+ costs_[i] = costs_[j];
+ regs_[0][i] = regs_[0][j];
+ regs_[1][i] = regs_[1][j];
+ }
+ }
+ for (int i = 0; i < kStates; i++) {
+ int reg = (i & 1);
+ int diff = addr - regs_[reg][i];
+ int cost = ObjectWriter::OpcodeLength(diff >> (smi_mode_ ? 0 : 2));
+ costs_[i] += cost;
+ if (!smi_mode_ || reg == 1) regs_[reg][i] = addr;
+ }
+ }
+
+ int Consult() { return script_[oracular_pronouncements_++]; }
+
+ void DoneConsulting() { ASSERT(oracular_pronouncements_ == script_.size()); }
+
+ private:
+ static const int kStatesLog2 = 6;
+ static const int kStates = 1 << kStatesLog2;
+ bool smi_mode_;
+ SnapshotWriter* writer_;
+ // We have a binary tree of k recent possible choices for choosing snapshot
+ // byte codes. The arrays represent the 2^k leaves, and the bits of the
+ // indices represent the potential choices made. For each leaf we record the
+ // cost (in bytes added to the snapshot) and the values of the two registers.
+ int costs_[kStates];
+ int regs_[2][kStates];
+ int unused_ = kStatesLog2;
+ size_t oracular_pronouncements_ = 0;
+ int ideals_[kStatesLog2];
+ Vector<int> script_;
+ PortableAddressMap* map_;
+};
+
+class SnapshotDecisionObjectVisitor : public HeapObjectVisitor {
+ public:
+ explicit SnapshotDecisionObjectVisitor(SnapshotOracle* oracle)
+ : oracle_(oracle) {}
+
+ virtual uword Visit(HeapObject* object) {
+ object->IterateEverything(oracle_);
+ return object->Size();
+ }
+
+ private:
+ SnapshotOracle* oracle_;
+};
+
+void ObjectWriter::WriteInteger(word i) {
+ // Large Smis must be purged from the heap before we serialize, since
+ // they can't be represented on small devices.
+ ASSERT(i <= 0x7fffffff);
+ ASSERT(i >= -0x7fffffff - 1);
+ int reg = smi_oracle_->Consult();
+ if (reg == 0) {
+ WriteOpcode(kSnapshotSmi, i);
+ } else {
+ int32 offset = i - writer_->recent_smi();
+ WriteOpcode(kSnapshotRecentSmi, offset);
+ writer_->set_recent_smi(i);
+ }
+}
+
+void ObjectWriter::WriteWord(Object* object) {
+ if (object->IsSmi()) {
+ WriteInteger(reinterpret_cast<word>(object));
+ return;
+ }
+
+ HeapObject* heap_object = HeapObject::cast(object);
+ uword ideal = address_map_->IdealizedAddress(heap_object) -
+ address_map_->doubles_size();
+ int popular_index = writer_->PopularityIndex(heap_object);
+ if (popular_index != -1) {
+ ASSERT(popular_index < kSnapshotNumberOfPopularObjects &&
+ popular_index >= 0);
+ ASSERT(kSnapshotPopular == 0);
+ ASSERT((kSnapshotPopularMask & kSnapshotPopular) == 0);
+ ASSERT((kSnapshotPopularMask & (kSnapshotNumberOfPopularObjects - 1)) == 0);
+ writer_->WriteByte(popular_index);
+ } else {
+ int reg = pointer_oracle_->Consult();
+ int offset = ideal - writer_->recent(reg);
+ WriteOpcode(kSnapshotRecentPointer + reg, offset / kIdealWordSize);
+ writer_->set_recent(reg, ideal);
+ }
+}
+
void SnapshotWriter::WriteSectionBoundary(ObjectWriter* writer) {
writer->WriteOpcode(kSnapshotRaw, -1);
}
class SnapshotWriterVisitor : public HeapObjectVisitor {
public:
- SnapshotWriterVisitor(PortableAddressMap* map, SnapshotWriter* writer)
- : address_map_(map), writer_(writer) {}
+ SnapshotWriterVisitor(PortableAddressMap* map, SnapshotWriter* writer,
+ SnapshotOracle* pointer_oracle,
+ SnapshotOracle* smi_oracle)
+ : address_map_(map),
+ writer_(writer),
+ pointer_oracle_(pointer_oracle),
+ smi_oracle_(smi_oracle) {}
virtual uword Visit(HeapObject* object) {
ASSERT(!object->IsStack());
@@ -794,10 +948,15 @@ class SnapshotWriterVisitor : public HeapObjectVisitor {
if (object->IsDouble()) {
ASSERT(doubles_mode_);
writer_->WriteDouble(Double::cast(object)->value());
+ // We have to consume one oracular pronouncement (for the class
+ // field of the double) to keep the oracle in sync with the
+ // writer.
+ pointer_oracle_->Consult();
return size;
}
doubles_mode_ = false;
- ObjectWriter object_writer(writer_, address_map_, object);
+ ObjectWriter object_writer(writer_, address_map_, pointer_oracle_,
+ smi_oracle_, object);
object->IterateEverything(&object_writer);
object_writer.End(object->address() + size);
return size;
@@ -807,6 +966,8 @@ class SnapshotWriterVisitor : public HeapObjectVisitor {
bool doubles_mode_ = true;
PortableAddressMap* address_map_;
SnapshotWriter* writer_;
+ SnapshotOracle* pointer_oracle_;
+ SnapshotOracle* smi_oracle_;
};
void PopularityCounter::VisitClass(Object** slot) {
@@ -886,7 +1047,24 @@ List<uint8> SnapshotWriter::WriteProgram(Program* program) {
WriteSize(portable_addresses.total_size().ComputeSizeInBytes(
kSmallPointerBigFloat));
- ObjectWriter object_writer(this, &portable_addresses);
+ SnapshotOracle pointer_oracle(false, &portable_addresses, this);
+ emitting_popular_list_ = true;
+ popularity_counter_.VisitMostPopular(&pointer_oracle);
+ emitting_popular_list_ = false;
+ program->IterateRootsIgnoringSession(&pointer_oracle);
+ SnapshotDecisionObjectVisitor sdov(&pointer_oracle);
+ program->heap()->IterateObjects(&sdov);
+ pointer_oracle.WrapUp();
+
+ SnapshotOracle smi_oracle(true, NULL, this);
+ popularity_counter_.VisitMostPopular(&smi_oracle);
+ program->IterateRootsIgnoringSession(&smi_oracle);
+ SnapshotDecisionObjectVisitor sdov2(&smi_oracle);
+ program->heap()->IterateObjects(&sdov2);
+ smi_oracle.WrapUp();
+
+ ObjectWriter object_writer(this, &portable_addresses, &pointer_oracle,
+ &smi_oracle);
WriteSectionBoundary(&object_writer);
emitting_popular_list_ = true;
@@ -897,11 +1075,15 @@ List<uint8> SnapshotWriter::WriteProgram(Program* program) {
program->IterateRootsIgnoringSession(&object_writer);
WriteSectionBoundary(&object_writer);
- SnapshotWriterVisitor writer_visitor(&portable_addresses, this);
+ SnapshotWriterVisitor writer_visitor(&portable_addresses, this,
+ &pointer_oracle, &smi_oracle);
program->heap()->IterateObjects(&writer_visitor);
WriteSectionBoundary(&object_writer);
+ smi_oracle.DoneConsulting();
+ pointer_oracle.DoneConsulting();
+
List<uint8> result = snapshot_.Sublist(0, position_);
program->set_snapshot_hash(ComputeSnapshotHash(result));
« no previous file with comments | « src/vm/snapshot.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698