| 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));
|
|
|