v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
read-only-serializer.cc
Go to the documentation of this file.
1// Copyright 2018 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
8#include "src/heap/heap-inl.h"
12#include "src/objects/slots.h"
14
15namespace v8 {
16namespace internal {
17
18namespace {
19
20// Preprocess an object to prepare it for serialization.
21class ObjectPreProcessor final {
22 public:
23 explicit ObjectPreProcessor(Isolate* isolate)
24 : isolate_(isolate), extref_encoder_(isolate) {}
25
26#define PRE_PROCESS_TYPE_LIST(V) \
27 V(AccessorInfo) \
28 V(InterceptorInfo) \
29 V(JSExternalObject) \
30 V(FunctionTemplateInfo) \
31 V(Code)
32
33 void PreProcessIfNeeded(Tagged<HeapObject> o) {
34 const InstanceType itype = o->map(isolate_)->instance_type();
35#define V(TYPE) \
36 if (InstanceTypeChecker::Is##TYPE(itype)) { \
37 return PreProcess##TYPE(Cast<TYPE>(o)); \
38 }
40#undef V
41 // If we reach here, no preprocessing is needed for this object.
42 }
43#undef PRE_PROCESS_TYPE_LIST
44
45 private:
46 void EncodeExternalPointerSlot(ExternalPointerSlot slot) {
47 Address value = slot.load(isolate_);
48 EncodeExternalPointerSlot(slot, value);
49 }
50
51 void EncodeExternalPointerSlot(ExternalPointerSlot slot, Address value) {
52 // Note it's possible that `value != slot.load(...)`, e.g. for
53 // AccessorInfo::remove_getter_indirection.
54 ExternalReferenceEncoder::Value encoder_value =
55 extref_encoder_.Encode(value);
56 DCHECK_LT(encoder_value.index(),
58 ro::EncodedExternalReference encoded{encoder_value.is_from_api(),
59 encoder_value.index()};
60 // Constructing no_gc here is not the intended use pattern (instead we
61 // should pass it along the entire callchain); but there's little point of
62 // doing that here - all of the code in this file relies on GC being
63 // disabled, and that's guarded at entry points.
65 slot.ReplaceContentWithIndexForSerialization(no_gc, encoded.ToUint32());
66 }
67 void PreProcessAccessorInfo(Tagged<AccessorInfo> o) {
68 EncodeExternalPointerSlot(
69 o->RawExternalPointerField(AccessorInfo::kMaybeRedirectedGetterOffset,
71 o->getter(isolate_)); // Pass the non-redirected value.
72 EncodeExternalPointerSlot(o->RawExternalPointerField(
73 AccessorInfo::kSetterOffset, kAccessorInfoSetterTag));
74 }
75 void PreProcessInterceptorInfo(Tagged<InterceptorInfo> o) {
76 const bool is_named = o->is_named();
77
78#define PROCESS_FIELD(Name, name) \
79 EncodeExternalPointerSlot(o->RawExternalPointerField( \
80 InterceptorInfo::k##Name##Offset, \
81 is_named ? kApiNamedProperty##Name##CallbackTag \
82 : kApiIndexedProperty##Name##CallbackTag));
83
85#undef PROCESS_FIELD
86 }
87 void PreProcessJSExternalObject(Tagged<JSExternalObject> o) {
88 EncodeExternalPointerSlot(
89 o->RawExternalPointerField(JSExternalObject::kValueOffset,
91 reinterpret_cast<Address>(o->value(isolate_)));
92 }
93 void PreProcessFunctionTemplateInfo(Tagged<FunctionTemplateInfo> o) {
94 EncodeExternalPointerSlot(
95 o->RawExternalPointerField(
96 FunctionTemplateInfo::kMaybeRedirectedCallbackOffset,
98 o->callback(isolate_)); // Pass the non-redirected value.
99 }
100 void PreProcessCode(Tagged<Code> o) {
101 o->ClearInstructionStartForSerialization(isolate_);
102 CHECK(!o->has_source_position_table_or_bytecode_offset_table());
103 CHECK(!o->has_deoptimization_data_or_interpreter_data());
104#ifdef V8_ENABLE_LEAPTIERING
105 CHECK_EQ(o->js_dispatch_handle(), kNullJSDispatchHandle);
106#endif
107 }
108
109 Isolate* const isolate_;
110 ExternalReferenceEncoder extref_encoder_;
111};
112
113struct ReadOnlySegmentForSerialization {
114 ReadOnlySegmentForSerialization(Isolate* isolate,
115 const ReadOnlyPageMetadata* page,
117 ObjectPreProcessor* pre_processor)
118 : page(page),
121 segment_offset(segment_start - page->area_start()),
122 contents(new uint8_t[segment_size]),
124 // .. because tagged_slots records a bit for each slot:
126 // Ensure incoming pointers to this page are representable.
127 CHECK_LT(isolate->read_only_heap()->read_only_space()->IndexOf(page),
129
130 MemCopy(contents.get(), reinterpret_cast<void*>(segment_start),
132 PreProcessSegment(pre_processor);
133 if (!V8_STATIC_ROOTS_BOOL) EncodeTaggedSlots(isolate);
134 }
135
136 void PreProcessSegment(ObjectPreProcessor* pre_processor) {
137 // Iterate the RO page and the contents copy in lockstep, preprocessing
138 // objects as we go along.
139 //
140 // See also ObjectSerializer::OutputRawData.
141 DCHECK_GE(segment_start, page->area_start());
142 const Address segment_end = segment_start + segment_size;
143 ReadOnlyPageObjectIterator it(page, segment_start);
144 for (Tagged<HeapObject> o = it.Next(); !o.is_null(); o = it.Next()) {
145 if (o.address() >= segment_end) break;
146 size_t o_offset = o.ptr() - segment_start;
147 Address o_dst = reinterpret_cast<Address>(contents.get()) + o_offset;
148 pre_processor->PreProcessIfNeeded(
150 }
151 }
152
153 void EncodeTaggedSlots(Isolate* isolate);
154
155 const ReadOnlyPageMetadata* const page;
157 const size_t segment_size;
158 const size_t segment_offset;
159 // The (mutated) off-heap copy of the on-heap segment.
160 std::unique_ptr<uint8_t[]> contents;
161 // The relocation table.
162 ro::BitSet tagged_slots;
163
164 friend class EncodeRelocationsVisitor;
165};
166
167ro::EncodedTagged Encode(Isolate* isolate, Tagged<HeapObject> o) {
168 Address o_address = o.address();
169 MemoryChunkMetadata* chunk = MemoryChunkMetadata::FromAddress(o_address);
170
171 ReadOnlySpace* ro_space = isolate->read_only_heap()->read_only_space();
172 int index = static_cast<int>(ro_space->IndexOf(chunk));
173 uint32_t offset = static_cast<int>(chunk->Offset(o_address));
175
176 return ro::EncodedTagged(index, offset / kTaggedSize);
177}
178
179// If relocations are needed, this class
180// - encodes all tagged slots s.t. valid pointers can be reconstructed during
181// deserialization, and
182// - records the location of all tagged slots in a table.
183class EncodeRelocationsVisitor final : public ObjectVisitor {
184 public:
185 EncodeRelocationsVisitor(Isolate* isolate,
186 ReadOnlySegmentForSerialization* segment)
187 : isolate_(isolate), segment_(segment) {
189 }
190
191 void VisitPointers(Tagged<HeapObject> host, ObjectSlot start,
192 ObjectSlot end) override {
193 VisitPointers(host, MaybeObjectSlot(start), MaybeObjectSlot(end));
194 }
195
196 void VisitPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
197 MaybeObjectSlot end) override {
198 for (MaybeObjectSlot slot = start; slot < end; slot++) {
199 ProcessSlot(slot);
200 }
201 }
202
203 void VisitMapPointer(Tagged<HeapObject> host) override {
204 ProcessSlot(host->RawMaybeWeakField(HeapObject::kMapOffset));
205 }
206
207 // Sanity-checks:
208 void VisitInstructionStreamPointer(Tagged<Code> host,
209 InstructionStreamSlot slot) override {
210 // RO space contains only builtin Code objects.
211 DCHECK(!host->has_instruction_stream());
212 }
213 void VisitCodeTarget(Tagged<InstructionStream>, RelocInfo*) override {
214 UNREACHABLE();
215 }
216 void VisitEmbeddedPointer(Tagged<InstructionStream>, RelocInfo*) override {
217 UNREACHABLE();
218 }
219 void VisitExternalReference(Tagged<InstructionStream>, RelocInfo*) override {
220 UNREACHABLE();
221 }
222 void VisitInternalReference(Tagged<InstructionStream>, RelocInfo*) override {
223 UNREACHABLE();
224 }
225 void VisitOffHeapTarget(Tagged<InstructionStream>, RelocInfo*) override {
226 UNREACHABLE();
227 }
228 void VisitExternalPointer(Tagged<HeapObject>,
229 ExternalPointerSlot slot) override {
230 // This slot was encoded in a previous pass, see EncodeExternalPointerSlot.
231#ifdef DEBUG
232 ExternalPointerSlot slot_in_segment{
233 reinterpret_cast<Address>(segment_->contents.get() +
234 SegmentOffsetOf(slot)),
235 slot.exact_tag()};
236 // Constructing no_gc here is not the intended use pattern (instead we
237 // should pass it along the entire callchain); but there's little point of
238 // doing that here - all of the code in this file relies on GC being
239 // disabled, and that's guarded at entry points.
242 slot_in_segment.GetContentAsIndexAfterDeserialization(no_gc));
243 if (encoded.is_api_reference) {
244 // Can't validate these since we don't know how many entries
245 // api_external_references contains.
246 } else {
248 }
249#endif // DEBUG
250 }
251
252 private:
253 void ProcessSlot(MaybeObjectSlot slot) {
254 Tagged<MaybeObject> o = *slot;
255 if (!o.IsStrongOrWeak()) return; // Smis don't need relocation.
256 DCHECK(o.IsStrong());
257
258 int slot_offset = SegmentOffsetOf(slot);
259 DCHECK(IsAligned(slot_offset, kTaggedSize));
260
261 // Encode:
262 ro::EncodedTagged encoded = Encode(isolate_, o.GetHeapObject());
263 memcpy(segment_->contents.get() + slot_offset, &encoded,
265
266 // Record:
267 segment_->tagged_slots.set(AsSlot(slot_offset));
268 }
269
270 template <class SlotT>
271 int SegmentOffsetOf(SlotT slot) const {
272 Address addr = slot.address();
273 DCHECK_GE(addr, segment_->segment_start);
274 DCHECK_LT(addr, segment_->segment_start + segment_->segment_size);
275 return static_cast<int>(addr - segment_->segment_start);
276 }
277
278 static constexpr int AsSlot(int byte_offset) {
279 return byte_offset / kTaggedSize;
280 }
281
282 Isolate* const isolate_;
283 ReadOnlySegmentForSerialization* const segment_;
284};
285
286void ReadOnlySegmentForSerialization::EncodeTaggedSlots(Isolate* isolate) {
288 EncodeRelocationsVisitor v(isolate, this);
289 PtrComprCageBase cage_base(isolate);
290
291 DCHECK_GE(segment_start, page->area_start());
292 const Address segment_end = segment_start + segment_size;
293 ReadOnlyPageObjectIterator it(page, segment_start,
294 SkipFreeSpaceOrFiller::kNo);
295 for (Tagged<HeapObject> o = it.Next(); !o.is_null(); o = it.Next()) {
296 if (o.address() >= segment_end) break;
297 VisitObject(isolate, o, &v);
298 }
299}
300
301class ReadOnlyHeapImageSerializer {
302 public:
303 struct MemoryRegion {
304 Address start;
305 size_t size;
306 };
307
308 static void Serialize(Isolate* isolate, SnapshotByteSink* sink,
309 const std::vector<MemoryRegion>& unmapped_regions) {
310 ReadOnlyHeapImageSerializer{isolate, sink}.SerializeImpl(unmapped_regions);
311 }
312
313 private:
314 using Bytecode = ro::Bytecode;
315
316 ReadOnlyHeapImageSerializer(Isolate* isolate, SnapshotByteSink* sink)
317 : isolate_(isolate), sink_(sink), pre_processor_(isolate) {}
318
319 void SerializeImpl(const std::vector<MemoryRegion>& unmapped_regions) {
320 DCHECK_EQ(sink_->Position(), 0);
321
322 ReadOnlySpace* ro_space = isolate_->read_only_heap()->read_only_space();
323
324 // Allocate all pages first s.t. the deserializer can easily handle forward
325 // references (e.g.: an object on page i points at an object on page i+1).
326 for (const ReadOnlyPageMetadata* page : ro_space->pages()) {
327 EmitAllocatePage(page, unmapped_regions);
328 }
329
330 // Now write the page contents.
331 for (const ReadOnlyPageMetadata* page : ro_space->pages()) {
332 SerializePage(page, unmapped_regions);
333 }
334
335 EmitReadOnlyRootsTable();
336 sink_->Put(Bytecode::kFinalizeReadOnlySpace, "space end");
337 }
338
339 uint32_t IndexOf(const ReadOnlyPageMetadata* page) {
340 ReadOnlySpace* ro_space = isolate_->read_only_heap()->read_only_space();
341 return static_cast<uint32_t>(ro_space->IndexOf(page));
342 }
343
344 void EmitAllocatePage(const ReadOnlyPageMetadata* page,
345 const std::vector<MemoryRegion>& unmapped_regions) {
347 sink_->Put(Bytecode::kAllocatePageAt, "fixed page begin");
348 } else {
349 sink_->Put(Bytecode::kAllocatePage, "page begin");
350 }
351 sink_->PutUint30(IndexOf(page), "page index");
352 sink_->PutUint30(
353 static_cast<uint32_t>(page->HighWaterMark() - page->area_start()),
354 "area size in bytes");
356 auto page_addr = page->ChunkAddress();
357 sink_->PutUint32(V8HeapCompressionScheme::CompressAny(page_addr),
358 "page start offset");
359 }
360 }
361
362 void SerializePage(const ReadOnlyPageMetadata* page,
363 const std::vector<MemoryRegion>& unmapped_regions) {
364 Address pos = page->area_start();
365
366 // If this page contains unmapped regions split it into multiple segments.
367 for (auto r = unmapped_regions.begin(); r != unmapped_regions.end(); ++r) {
368 // Regions must be sorted and non-overlapping.
369 if (r + 1 != unmapped_regions.end()) {
370 CHECK(r->start < (r + 1)->start);
371 CHECK(r->start + r->size < (r + 1)->start);
372 }
373 if (base::IsInRange(r->start, pos, page->HighWaterMark())) {
374 size_t segment_size = r->start - pos;
375 ReadOnlySegmentForSerialization segment(isolate_, page, pos,
377 EmitSegment(&segment);
378 pos += segment_size + r->size;
379 }
380 }
381
382 // Pages are shrunk, but memory at the end of the area is still
383 // uninitialized and we do not want to include it in the snapshot.
384 size_t segment_size = page->HighWaterMark() - pos;
385 ReadOnlySegmentForSerialization segment(isolate_, page, pos, segment_size,
387 EmitSegment(&segment);
388 }
389
390 void EmitSegment(const ReadOnlySegmentForSerialization* segment) {
391 sink_->Put(Bytecode::kSegment, "segment begin");
392 sink_->PutUint30(IndexOf(segment->page), "page index");
393 sink_->PutUint30(static_cast<uint32_t>(segment->segment_offset),
394 "segment start offset");
395 sink_->PutUint30(static_cast<uint32_t>(segment->segment_size),
396 "segment byte size");
397 sink_->PutRaw(segment->contents.get(),
398 static_cast<int>(segment->segment_size), "page");
400 sink_->Put(Bytecode::kRelocateSegment, "relocate segment");
401 sink_->PutRaw(segment->tagged_slots.data(),
402 static_cast<int>(segment->tagged_slots.size_in_bytes()),
403 "tagged_slots");
404 }
405 }
406
407 void EmitReadOnlyRootsTable() {
408 sink_->Put(Bytecode::kReadOnlyRootsTable, "read only roots table");
410 ReadOnlyRoots roots(isolate_);
411 for (size_t i = 0; i < ReadOnlyRoots::kEntriesCount; i++) {
412 RootIndex rudi = static_cast<RootIndex>(i);
413 Tagged<HeapObject> rudolf = Cast<HeapObject>(roots.object_at(rudi));
414 ro::EncodedTagged encoded = Encode(isolate_, rudolf);
415 sink_->PutUint32(encoded.ToUint32(), "read only roots entry");
416 }
417 }
418 }
419
420 Isolate* const isolate_;
421 SnapshotByteSink* const sink_;
422 ObjectPreProcessor pre_processor_;
423};
424
425std::vector<ReadOnlyHeapImageSerializer::MemoryRegion> GetUnmappedRegions(
426 Isolate* isolate) {
427#ifdef V8_STATIC_ROOTS
428 // WasmNull's payload is aligned to the OS page and consists of
429 // WasmNull::kPayloadSize bytes of unmapped memory. To avoid inflating the
430 // snapshot size and accessing uninitialized and/or unmapped memory, the
431 // serializer skips the padding bytes and the payload.
432 ReadOnlyRoots ro_roots(isolate);
433 Tagged<WasmNull> wasm_null = ro_roots.wasm_null();
434 Tagged<HeapObject> wasm_null_padding = ro_roots.wasm_null_padding();
435 CHECK(IsFreeSpace(wasm_null_padding));
436 Address wasm_null_padding_start =
437 wasm_null_padding.address() + FreeSpace::kHeaderSize;
438 std::vector<ReadOnlyHeapImageSerializer::MemoryRegion> unmapped;
439 if (wasm_null.address() > wasm_null_padding_start) {
440 unmapped.push_back({wasm_null_padding_start,
441 wasm_null.address() - wasm_null_padding_start});
442 }
443 unmapped.push_back({wasm_null->payload(), WasmNull::kPayloadSize});
444 return unmapped;
445#else
446 return {};
447#endif // V8_STATIC_ROOTS
448}
449
450} // namespace
451
455
459
462 ReadOnlyHeapImageSerializer::Serialize(isolate(), &sink_,
463 GetUnmappedRegions(isolate()));
464
465 ReadOnlyHeapObjectIterator it(isolate()->read_only_heap());
466 for (Tagged<HeapObject> o = it.Next(); !o.is_null(); o = it.Next()) {
468 if (v8_flags.serialization_statistics) {
469 CountAllocation(o->map(), o->Size(), SnapshotSpace::kReadOnlyHeap);
470 }
471 }
472}
473
474} // namespace internal
475} // namespace v8
#define INTERCEPTOR_INFO_CALLBACK_LIST(V)
Isolate * isolate_
SourcePosition pos
static constexpr int kMapOffset
static V8_INLINE MemoryChunkMetadata * FromAddress(Address a)
ReadOnlySerializer(Isolate *isolate, Snapshot::SerializerFlags flags)
void CheckRehashability(Tagged< HeapObject > obj)
Isolate * isolate() const
Definition serializer.h:195
void CountAllocation(Tagged< Map > map, int size, SnapshotSpace space)
Definition serializer.cc:67
SnapshotByteSink sink_
Definition serializer.h:323
void OutputStatistics(const char *name)
V8_INLINE constexpr bool is_null() const
Definition tagged.h:502
int start
int end
Isolate * isolate
int32_t offset
int r
Definition mul-fft.cc:298
uintptr_t Address
Definition memory.h:13
constexpr int kTaggedSize
Definition globals.h:542
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
SlotTraits::TObjectSlot ObjectSlot
Definition globals.h:1243
Tagged(T object) -> Tagged< T >
kInterpreterTrampolineOffset Tagged< HeapObject >
SlotTraits::TInstructionStreamSlot InstructionStreamSlot
Definition globals.h:1265
void VisitObject(Isolate *isolate, Tagged< HeapObject > object, ObjectVisitor *visitor)
constexpr JSDispatchHandle kNullJSDispatchHandle(0)
@ kExternalObjectValueTag
@ kFunctionTemplateInfoCallbackTag
V8_EXPORT_PRIVATE FlagValues v8_flags
SlotTraits::TMaybeObjectSlot MaybeObjectSlot
Definition globals.h:1248
void MemCopy(void *dest, const void *src, size_t size)
Definition memcopy.h:124
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
#define PROCESS_FIELD(Name, name)
ReadOnlySegmentForSerialization *const segment_
const size_t segment_size
const Address segment_start
ro::BitSet tagged_slots
#define PRE_PROCESS_TYPE_LIST(V)
ObjectPreProcessor pre_processor_
ExternalReferenceEncoder extref_encoder_
const size_t segment_offset
SnapshotByteSink *const sink_
base::Vector< const char > contents
#define CHECK(condition)
Definition logging.h:124
#define CHECK_LT(lhs, rhs)
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
constexpr bool IsAligned(T value, U alignment)
Definition macros.h:403
static EncodedExternalReference FromUint32(uint32_t v)
#define V8_STATIC_ROOTS_BOOL
Definition v8config.h:1001