Bug 1633425 - Use offsets to delimit arrays in IonScript. r=jandem
authorTed Campbell <tcampbell@mozilla.com>
Tue, 28 Apr 2020 08:22:01 +0000
changeset 526487 5d3704bf89d5abe274c7587afbacf59d92439723
parent 526486 6158f69d21c7e5cdba5d2d6847aba2d4074fa7c1
child 526488 33897701906977158dece6031ec204b2bd532627
push id114291
push usertcampbell@mozilla.com
push dateTue, 28 Apr 2020 13:58:09 +0000
treeherderautoland@338977019069 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1633425
milestone77.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1633425 - Use offsets to delimit arrays in IonScript. r=jandem Use helper methods to access the size and length of each trailing array. We shave 40 bytes off each IonScript by using the difference between two arrays to compute size rather than storing a dedicated size field. Differential Revision: https://phabricator.services.mozilla.com/D72675
js/src/jit/Ion.cpp
js/src/jit/IonCode.h
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -612,16 +612,24 @@ IonScript* IonScript::New(JSContext* cx,
                           size_t safepointsSize,
                           OptimizationLevel optimizationLevel) {
   if (snapshotsListSize >= MAX_BUFFER_SIZE ||
       (bailoutEntries >= MAX_BUFFER_SIZE / sizeof(uint32_t))) {
     ReportOutOfMemory(cx);
     return nullptr;
   }
 
+  // Verify the hardcoded sizes in header are accurate.
+  static_assert(SizeOf_OsiIndex == sizeof(OsiIndex),
+                "IonScript has wrong size for OsiIndex");
+  static_assert(SizeOf_SafepointIndex == sizeof(SafepointIndex),
+                "IonScript has wrong size for SafepointIndex");
+  static_assert(SizeOf_SnapshotOffset == sizeof(SnapshotOffset),
+                "IonScript has wrong size for SnapshotOffset");
+
   CheckedInt<Offset> allocSize = sizeof(IonScript);
   allocSize += CheckedInt<Offset>(constants) * sizeof(Value);
   allocSize += CheckedInt<Offset>(runtimeSize);
   allocSize += CheckedInt<Offset>(osiIndices) * sizeof(OsiIndex);
   allocSize += CheckedInt<Offset>(safepointIndices) * sizeof(SafepointIndex);
   allocSize += CheckedInt<Offset>(bailoutEntries) * sizeof(SnapshotOffset);
   allocSize += CheckedInt<Offset>(icEntries) * sizeof(uint32_t);
   allocSize += CheckedInt<Offset>(safepointsSize);
@@ -640,61 +648,64 @@ IonScript* IonScript::New(JSContext* cx,
     return nullptr;
   }
   IonScript* script = new (raw) IonScript(
       compilationId, frameSlots, argumentSlots, frameSize, optimizationLevel);
 
   Offset offsetCursor = sizeof(IonScript);
 
   MOZ_ASSERT(offsetCursor % alignof(Value) == 0);
-  script->constantTable_ = offsetCursor;
-  script->constantEntries_ = constants;
+  script->constantTableOffset_ = offsetCursor;
   offsetCursor += constants * sizeof(Value);
 
   MOZ_ASSERT(offsetCursor % alignof(uint64_t) == 0);
-  script->runtimeData_ = offsetCursor;
-  script->runtimeSize_ = runtimeSize;
+  script->runtimeDataOffset_ = offsetCursor;
   offsetCursor += runtimeSize;
 
   MOZ_ASSERT(offsetCursor % alignof(OsiIndex) == 0);
   script->osiIndexOffset_ = offsetCursor;
-  script->osiIndexEntries_ = osiIndices;
   offsetCursor += osiIndices * sizeof(OsiIndex);
 
   MOZ_ASSERT(offsetCursor % alignof(SafepointIndex) == 0);
   script->safepointIndexOffset_ = offsetCursor;
-  script->safepointIndexEntries_ = safepointIndices;
   offsetCursor += safepointIndices * sizeof(SafepointIndex);
 
   MOZ_ASSERT(offsetCursor % alignof(SnapshotOffset) == 0);
-  script->bailoutTable_ = offsetCursor;
-  script->bailoutEntries_ = bailoutEntries;
+  script->bailoutTableOffset_ = offsetCursor;
   offsetCursor += bailoutEntries * sizeof(SnapshotOffset);
 
   MOZ_ASSERT(offsetCursor % alignof(uint32_t) == 0);
-  script->icIndex_ = offsetCursor;
-  script->icEntries_ = icEntries;
+  script->icIndexOffset_ = offsetCursor;
   offsetCursor += icEntries * sizeof(uint32_t);
 
-  script->safepointsStart_ = offsetCursor;
-  script->safepointsSize_ = safepointsSize;
+  script->safepointsOffset_ = offsetCursor;
   offsetCursor += safepointsSize;
 
-  script->snapshots_ = offsetCursor;
-  script->snapshotsListSize_ = snapshotsListSize;
-  script->snapshotsRVATableSize_ = snapshotsRVATableSize;
+  script->snapshotsOffset_ = offsetCursor;
   offsetCursor += snapshotsListSize;
+
+  script->rvaTableOffset_ = offsetCursor;
   offsetCursor += snapshotsRVATableSize;
 
-  script->recovers_ = offsetCursor;
-  script->recoversSize_ = recoversSize;
+  script->recoversOffset_ = offsetCursor;
   offsetCursor += recoversSize;
 
   script->allocBytes_ = offsetCursor;
-  MOZ_ASSERT(offsetCursor == allocSize.value());
+
+  MOZ_ASSERT(script->numConstants() == constants);
+  MOZ_ASSERT(script->runtimeSize() == runtimeSize);
+  MOZ_ASSERT(script->numOsiIndices() == osiIndices);
+  MOZ_ASSERT(script->numSafepointIndices() == safepointIndices);
+  MOZ_ASSERT(script->numBailoutEntries() == bailoutEntries);
+  MOZ_ASSERT(script->numICs() == icEntries);
+  MOZ_ASSERT(script->safepointsSize() == safepointsSize);
+  MOZ_ASSERT(script->snapshotsListSize() == snapshotsListSize);
+  MOZ_ASSERT(script->snapshotsRVATableSize() == snapshotsRVATableSize);
+  MOZ_ASSERT(script->recoversSize() == recoversSize);
+  MOZ_ASSERT(script->endOffset() == offsetCursor);
 
   return script;
 }
 
 void IonScript::trace(JSTracer* trc) {
   if (method_) {
     TraceEdge(trc, &method_, "method");
   }
@@ -712,81 +723,84 @@ void IonScript::trace(JSTracer* trc) {
 /* static */
 void IonScript::writeBarrierPre(Zone* zone, IonScript* ionScript) {
   if (zone->needsIncrementalBarrier()) {
     ionScript->trace(zone->barrierTracer());
   }
 }
 
 void IonScript::copySnapshots(const SnapshotWriter* writer) {
-  MOZ_ASSERT(writer->listSize() == snapshotsListSize_);
-  memcpy((uint8_t*)this + snapshots_, writer->listBuffer(), snapshotsListSize_);
-
-  MOZ_ASSERT(snapshotsRVATableSize_);
-  MOZ_ASSERT(writer->RVATableSize() == snapshotsRVATableSize_);
-  memcpy((uint8_t*)this + snapshots_ + snapshotsListSize_,
-         writer->RVATableBuffer(), snapshotsRVATableSize_);
+  MOZ_ASSERT(writer->listSize() == snapshotsListSize());
+  memcpy(offsetToPointer<uint8_t>(snapshotsOffset()), writer->listBuffer(),
+         snapshotsListSize());
+
+  MOZ_ASSERT(snapshotsRVATableSize());
+  MOZ_ASSERT(writer->RVATableSize() == snapshotsRVATableSize());
+  memcpy(offsetToPointer<uint8_t>(rvaTableOffset()), writer->RVATableBuffer(),
+         snapshotsRVATableSize());
 }
 
 void IonScript::copyRecovers(const RecoverWriter* writer) {
-  MOZ_ASSERT(writer->size() == recoversSize_);
-  memcpy((uint8_t*)this + recovers_, writer->buffer(), recoversSize_);
+  MOZ_ASSERT(writer->size() == recoversSize());
+  memcpy(offsetToPointer<uint8_t>(recoversOffset()), writer->buffer(),
+         recoversSize());
 }
 
 void IonScript::copySafepoints(const SafepointWriter* writer) {
-  MOZ_ASSERT(writer->size() == safepointsSize_);
-  memcpy((uint8_t*)this + safepointsStart_, writer->buffer(), safepointsSize_);
+  MOZ_ASSERT(writer->size() == safepointsSize());
+  memcpy(offsetToPointer<uint8_t>(safepointsOffset()), writer->buffer(),
+         safepointsSize());
 }
 
 void IonScript::copyBailoutTable(const SnapshotOffset* table) {
-  memcpy(bailoutTable(), table, bailoutEntries_ * sizeof(uint32_t));
+  memcpy(bailoutTable(), table, numBailoutEntries() * sizeof(SnapshotOffset));
 }
 
 void IonScript::copyConstants(const Value* vp) {
-  for (size_t i = 0; i < constantEntries_; i++) {
+  for (size_t i = 0; i < numConstants(); i++) {
     constants()[i].init(vp[i]);
   }
 }
 
 void IonScript::copySafepointIndices(const CodegenSafepointIndex* si) {
   // Convert CodegenSafepointIndex to more compact form.
   SafepointIndex* table = safepointIndices();
-  for (size_t i = 0; i < safepointIndexEntries_; ++i) {
+  for (size_t i = 0; i < numSafepointIndices(); ++i) {
     table[i] = SafepointIndex(si[i]);
   }
 }
 
 void IonScript::copyOsiIndices(const OsiIndex* oi) {
-  memcpy(osiIndices(), oi, osiIndexEntries_ * sizeof(OsiIndex));
+  memcpy(osiIndices(), oi, numOsiIndices() * sizeof(OsiIndex));
 }
 
 void IonScript::copyRuntimeData(const uint8_t* data) {
   memcpy(runtimeData(), data, runtimeSize());
 }
 
 void IonScript::copyICEntries(const uint32_t* icEntries) {
   memcpy(icIndex(), icEntries, numICs() * sizeof(uint32_t));
 
   // Update the codeRaw_ field in the ICs now that we know the code address.
   for (size_t i = 0; i < numICs(); i++) {
     getICFromIndex(i).resetCodeRaw(this);
   }
 }
 
 const SafepointIndex* IonScript::getSafepointIndex(uint32_t disp) const {
-  MOZ_ASSERT(safepointIndexEntries_ > 0);
+  MOZ_ASSERT(numSafepointIndices() > 0);
 
   const SafepointIndex* table = safepointIndices();
-  if (safepointIndexEntries_ == 1) {
+  if (numSafepointIndices() == 1) {
     MOZ_ASSERT(disp == table[0].displacement());
     return &table[0];
   }
 
   size_t minEntry = 0;
-  size_t maxEntry = safepointIndexEntries_ - 1;
+  size_t maxEntry = numSafepointIndices() - 1;
   uint32_t min = table[minEntry].displacement();
   uint32_t max = table[maxEntry].displacement();
 
   // Raise if the element is not in the list.
   MOZ_ASSERT(min <= disp && disp <= max);
 
   // Approximate the location of the FrameInfo.
   size_t guess = (disp - min) * (maxEntry - minEntry) / (max - min) + minEntry;
@@ -817,17 +831,17 @@ const SafepointIndex* IonScript::getSafe
       }
     }
   }
 
   MOZ_CRASH("displacement not found.");
 }
 
 const OsiIndex* IonScript::getOsiIndex(uint32_t disp) const {
-  const OsiIndex* end = osiIndices() + osiIndexEntries_;
+  const OsiIndex* end = osiIndices() + numOsiIndices();
   for (const OsiIndex* it = osiIndices(); it != end; ++it) {
     if (it->returnPointDisplacement() == disp) {
       return it;
     }
   }
 
   MOZ_CRASH("Failed to find OSI point return address");
 }
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -170,57 +170,31 @@ class IonIC;
 //    uint8_t[]             snapshotsRVATable()
 //    uint8_t[]             recovers()
 //
 // Note: These are arranged in order of descending alignment requirements to
 // avoid the need for padding. The `runtimeData` uses uint64_t alignement due to
 // its use of mozilla::AlignedStorage2.
 struct alignas(8) IonScript final : public TrailingArray {
  private:
-  // Table of constants referenced in snapshots. (JS::Value alignment)
-  uint32_t constantTable_ = 0;
-  uint32_t constantEntries_ = 0;
-
-  // IonIC data structures. (uint64_t alignment)
-  uint32_t runtimeData_ = 0;
-  uint32_t runtimeSize_ = 0;
-
-  // Map OSI-point displacement to snapshot.
-  uint32_t osiIndexOffset_ = 0;
-  uint32_t osiIndexEntries_ = 0;
-
-  // Map code displacement to safepoint / OSI-patch-delta.
-  uint32_t safepointIndexOffset_ = 0;
-  uint32_t safepointIndexEntries_ = 0;
-
-  // Table mapping bailout IDs to snapshot offsets.
-  uint32_t bailoutTable_ = 0;
-  uint32_t bailoutEntries_ = 0;
+  // Offset (in bytes) from `this` to the start of each trailing array. Each
+  // array ends where following one begins. There is no implicit padding (except
+  // possible at very end).
+  Offset constantTableOffset_ = 0;  // JS::Value aligned
+  Offset runtimeDataOffset_ = 0;    // uint64_t aligned
+  Offset osiIndexOffset_ = 0;
+  Offset safepointIndexOffset_ = 0;
+  Offset bailoutTableOffset_ = 0;
+  Offset icIndexOffset_ = 0;
+  Offset safepointsOffset_ = 0;
+  Offset snapshotsOffset_ = 0;
+  Offset rvaTableOffset_ = 0;
+  Offset recoversOffset_ = 0;
+  Offset allocBytes_ = 0;
 
-  // Offset into `runtimeData` for each (variable-length) IonIC.
-  uint32_t icIndex_ = 0;
-  uint32_t icEntries_ = 0;
-
-  // Safepoint table as a CompactBuffer.
-  uint32_t safepointsStart_ = 0;
-  uint32_t safepointsSize_ = 0;
-
-  // Snapshot and RValueAllocation tables as CompactBuffers.
-  uint32_t snapshots_ = 0;
-  uint32_t snapshotsListSize_ = 0;
-  uint32_t snapshotsRVATableSize_ = 0;
-
-  // Recover instruction table as a CompactBuffer.
-  uint32_t recovers_ = 0;
-  uint32_t recoversSize_ = 0;
-
-  // The size of this allocation.
-  uint32_t allocBytes_ = 0;
-
- private:
   // Code pointer containing the actual method.
   HeapPtrJitCode method_ = nullptr;
 
   // Entrypoint for OSR, or nullptr.
   jsbytecode* osrPc_ = nullptr;
 
   // Offset to OSR entrypoint from method_->raw(), or 0.
   uint32_t osrEntryOffset_ = 0;
@@ -268,43 +242,141 @@ struct alignas(8) IonScript final : publ
 
   // Number of times we tried to enter this script via OSR but failed due to
   // a LOOPENTRY pc other than osrPc_.
   uint32_t osrPcMismatchCounter_ = 0;
 
   // TraceLogger events that are baked into the IonScript.
   TraceLoggerEventVector traceLoggerEvents_;
 
+  // End of fields.
+
  private:
   inline uint8_t* bottomBuffer() { return reinterpret_cast<uint8_t*>(this); }
   inline const uint8_t* bottomBuffer() const {
     return reinterpret_cast<const uint8_t*>(this);
   }
 
+  // Layout helpers
+  Offset constantTableOffset() const { return constantTableOffset_; }
+  Offset runtimeDataOffset() const { return runtimeDataOffset_; }
+  Offset osiIndexOffset() const { return osiIndexOffset_; }
+  Offset safepointIndexOffset() const { return safepointIndexOffset_; }
+  Offset bailoutTableOffset() const { return bailoutTableOffset_; }
+  Offset icIndexOffset() const { return icIndexOffset_; }
+  Offset safepointsOffset() const { return safepointsOffset_; }
+  Offset snapshotsOffset() const { return snapshotsOffset_; }
+  Offset rvaTableOffset() const { return rvaTableOffset_; }
+  Offset recoversOffset() const { return recoversOffset_; }
+  Offset endOffset() const { return allocBytes_; }
+
+  // Hardcode size of incomplete types. These are verified in Ion.cpp.
+  static constexpr size_t SizeOf_OsiIndex = 2 * sizeof(uint32_t);
+  static constexpr size_t SizeOf_SafepointIndex = 2 * sizeof(uint32_t);
+  static constexpr size_t SizeOf_SnapshotOffset = sizeof(uint32_t);
+
  public:
-  SnapshotOffset* bailoutTable() {
-    return (SnapshotOffset*)&bottomBuffer()[bailoutTable_];
-  }
+  //
+  // Table of constants referenced in snapshots. (JS::Value alignment)
+  //
   PreBarrieredValue* constants() {
     // Nursery constants are manually barriered in CodeGenerator::link() so a
     // post barrier is not required..
-    return (PreBarrieredValue*)&bottomBuffer()[constantTable_];
+    return offsetToPointer<PreBarrieredValue>(constantTableOffset());
+  }
+  size_t numConstants() const {
+    return (runtimeDataOffset() - constantTableOffset()) /
+           sizeof(PreBarrieredValue);
+  }
+
+  //
+  // IonIC data structures. (uint64_t alignment)
+  //
+  uint8_t* runtimeData() {
+    return offsetToPointer<uint8_t>(runtimeDataOffset());
+  }
+  size_t runtimeSize() const {
+    return (osiIndexOffset() - runtimeDataOffset()) / sizeof(uint8_t);
+  }
+
+  //
+  // Map OSI-point displacement to snapshot.
+  //
+  OsiIndex* osiIndices() { return offsetToPointer<OsiIndex>(osiIndexOffset()); }
+  const OsiIndex* osiIndices() const {
+    return offsetToPointer<OsiIndex>(osiIndexOffset());
+  }
+  size_t numOsiIndices() const {
+    return (safepointIndexOffset() - osiIndexOffset()) / SizeOf_OsiIndex;
+  }
+
+  //
+  // Map code displacement to safepoint / OSI-patch-delta.
+  //
+  SafepointIndex* safepointIndices() {
+    return offsetToPointer<SafepointIndex>(safepointIndexOffset());
   }
   const SafepointIndex* safepointIndices() const {
-    return const_cast<IonScript*>(this)->safepointIndices();
+    return offsetToPointer<SafepointIndex>(safepointIndexOffset());
+  }
+  size_t numSafepointIndices() const {
+    return (bailoutTableOffset() - safepointIndexOffset()) /
+           SizeOf_SafepointIndex;
   }
-  SafepointIndex* safepointIndices() {
-    return (SafepointIndex*)&bottomBuffer()[safepointIndexOffset_];
+
+  //
+  // Table mapping bailout IDs to snapshot offsets.
+  //
+  SnapshotOffset* bailoutTable() {
+    return offsetToPointer<SnapshotOffset>(bailoutTableOffset());
+  }
+  size_t numBailoutEntries() const {
+    return (icIndexOffset() - bailoutTableOffset()) / SizeOf_SnapshotOffset;
+  }
+
+  //
+  // Offset into `runtimeData` for each (variable-length) IonIC.
+  //
+  uint32_t* icIndex() { return offsetToPointer<uint32_t>(icIndexOffset()); }
+  size_t numICs() const {
+    return (safepointsOffset() - icIndexOffset()) / sizeof(uint32_t);
   }
-  const OsiIndex* osiIndices() const {
-    return const_cast<IonScript*>(this)->osiIndices();
+
+  //
+  // Safepoint table as a CompactBuffer.
+  //
+  const uint8_t* safepoints() const {
+    return offsetToPointer<uint8_t>(safepointsOffset());
+  }
+  size_t safepointsSize() const {
+    return (snapshotsOffset() - safepointsOffset()) / sizeof(uint8_t);
+  }
+
+  //
+  // Snapshot and RValueAllocation tables as CompactBuffers.
+  //
+  const uint8_t* snapshots() const {
+    return offsetToPointer<uint8_t>(snapshotsOffset());
   }
-  OsiIndex* osiIndices() { return (OsiIndex*)&bottomBuffer()[osiIndexOffset_]; }
-  uint32_t* icIndex() { return (uint32_t*)&bottomBuffer()[icIndex_]; }
-  uint8_t* runtimeData() { return &bottomBuffer()[runtimeData_]; }
+  size_t snapshotsListSize() const {
+    return (rvaTableOffset() - snapshotsOffset()) / sizeof(uint8_t);
+  }
+  size_t snapshotsRVATableSize() const {
+    return (recoversOffset() - rvaTableOffset()) / sizeof(uint8_t);
+  }
+
+  //
+  // Recover instruction table as a CompactBuffer.
+  //
+  const uint8_t* recovers() const {
+    return offsetToPointer<uint8_t>(recoversOffset());
+  }
+  size_t recoversSize() const {
+    return (endOffset() - recoversOffset()) / sizeof(uint8_t);
+  }
 
  private:
   IonScript(IonCompilationId compilationId, uint32_t frameSlots,
             uint32_t argumentSlots, uint32_t frameSize,
             OptimizationLevel optimizationLevel);
 
  public:
   static IonScript* New(JSContext* cx, IonCompilationId compilationId,
@@ -391,63 +463,47 @@ struct alignas(8) IonScript final : publ
   }
   bool hasProfilingInstrumentation() const {
     return hasProfilingInstrumentation_;
   }
   MOZ_MUST_USE bool addTraceLoggerEvent(TraceLoggerEvent& event) {
     MOZ_ASSERT(event.hasTextId());
     return traceLoggerEvents_.append(std::move(event));
   }
-  const uint8_t* snapshots() const {
-    return reinterpret_cast<const uint8_t*>(this) + snapshots_;
-  }
-  size_t snapshotsListSize() const { return snapshotsListSize_; }
-  size_t snapshotsRVATableSize() const { return snapshotsRVATableSize_; }
-  const uint8_t* recovers() const {
-    return reinterpret_cast<const uint8_t*>(this) + recovers_;
-  }
-  size_t recoversSize() const { return recoversSize_; }
-  const uint8_t* safepoints() const {
-    return reinterpret_cast<const uint8_t*>(this) + safepointsStart_;
-  }
-  size_t safepointsSize() const { return safepointsSize_; }
   size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     return mallocSizeOf(this);
   }
   PreBarrieredValue& getConstant(size_t index) {
     MOZ_ASSERT(index < numConstants());
     return constants()[index];
   }
-  size_t numConstants() const { return constantEntries_; }
   uint32_t frameSlots() const { return frameSlots_; }
   uint32_t argumentSlots() const { return argumentSlots_; }
   uint32_t frameSize() const { return frameSize_; }
   SnapshotOffset bailoutToSnapshot(uint32_t bailoutId) {
-    MOZ_ASSERT(bailoutId < bailoutEntries_);
+    MOZ_ASSERT(bailoutId < numBailoutEntries());
     return bailoutTable()[bailoutId];
   }
   const SafepointIndex* getSafepointIndex(uint32_t disp) const;
   const SafepointIndex* getSafepointIndex(uint8_t* retAddr) const {
     MOZ_ASSERT(containsCodeAddress(retAddr));
     return getSafepointIndex(retAddr - method()->raw());
   }
   const OsiIndex* getOsiIndex(uint32_t disp) const;
   const OsiIndex* getOsiIndex(uint8_t* retAddr) const;
 
   IonIC& getICFromIndex(uint32_t index) {
-    MOZ_ASSERT(index < icEntries_);
+    MOZ_ASSERT(index < numICs());
     uint32_t offset = icIndex()[index];
     return getIC(offset);
   }
   inline IonIC& getIC(uint32_t offset) {
-    MOZ_ASSERT(offset < runtimeSize_);
-    return *(IonIC*)&runtimeData()[offset];
+    MOZ_ASSERT(offset < runtimeSize());
+    return *reinterpret_cast<IonIC*>(runtimeData() + offset);
   }
-  size_t numICs() const { return icEntries_; }
-  size_t runtimeSize() const { return runtimeSize_; }
   void purgeICs(Zone* zone);
   void copySnapshots(const SnapshotWriter* writer);
   void copyRecovers(const RecoverWriter* writer);
   void copyBailoutTable(const SnapshotOffset* table);
   void copyConstants(const Value* vp);
   void copySafepointIndices(const CodegenSafepointIndex* firstSafepointIndex);
   void copyOsiIndices(const OsiIndex* firstOsiIndex);
   void copyRuntimeData(const uint8_t* data);