Commit 595609fb authored by Leszek Swirski's avatar Leszek Swirski Committed by Commit Bot

[offthread] Add off thread deserialization

Add a new OffThreadObjectDeserializer, which can deserialize a snapshot
into an OffThreadIsolate.

This involves templating the Deserializer base class on Isolate, and
amending OffThreadHeap to be able to create Reservations same as the
main-thread Heap can. Various off-thread incompatible methods are
stubbed out as UNREACHABLE in OffThreadIsolate overloads.

There is currently no API entry into the off-thread deserialization, but
under --stress-background-compile it now runs the CodeDeserializer (i.e.
code cache deserialization) in a background thread.

Bug: chromium:1075999

Change-Id: I2453f51ae31df4d4b6aa94b0804a9d6d3a03781e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2172741
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67799}
parent 4dabba12
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_EXECUTION_LOCAL_ISOLATE_WRAPPER_INL_H_
#define V8_EXECUTION_LOCAL_ISOLATE_WRAPPER_INL_H_
#include "src/execution/isolate.h"
#include "src/execution/local-isolate-wrapper.h"
#include "src/execution/off-thread-isolate.h"
#include "src/heap/heap-inl.h"
#include "src/heap/off-thread-heap.h"
#include "src/logging/log.h"
#include "src/logging/off-thread-logger.h"
namespace v8 {
namespace internal {
class HeapMethodCaller {
public:
explicit HeapMethodCaller(LocalHeapWrapper* heap) : heap_(heap) {}
ReadOnlySpace* read_only_space() {
return heap_->is_off_thread() ? heap_->off_thread()->read_only_space()
: heap_->main_thread()->read_only_space();
}
void OnAllocationEvent(HeapObject obj, int size) {
return heap_->is_off_thread()
? heap_->off_thread()->OnAllocationEvent(obj, size)
: heap_->main_thread()->OnAllocationEvent(obj, size);
}
bool Contains(HeapObject obj) {
return heap_->is_off_thread() ? heap_->off_thread()->Contains(obj)
: heap_->main_thread()->Contains(obj);
}
private:
LocalHeapWrapper* heap_;
};
class LoggerMethodCaller {
public:
explicit LoggerMethodCaller(LocalLoggerWrapper* logger) : logger_(logger) {}
bool is_logging() const {
return logger_->is_off_thread() ? logger_->off_thread()->is_logging()
: logger_->main_thread()->is_logging();
}
void ScriptEvent(Logger::ScriptEventType type, int script_id) {
return logger_->is_off_thread()
? logger_->off_thread()->ScriptEvent(type, script_id)
: logger_->main_thread()->ScriptEvent(type, script_id);
}
void ScriptDetails(Script script) {
return logger_->is_off_thread()
? logger_->off_thread()->ScriptDetails(script)
: logger_->main_thread()->ScriptDetails(script);
}
private:
LocalLoggerWrapper* logger_;
};
class IsolateMethodCaller {
public:
explicit IsolateMethodCaller(LocalIsolateWrapper* isolate)
: isolate_(isolate) {}
LocalLoggerWrapper logger() {
return isolate_->is_off_thread()
? LocalLoggerWrapper(isolate_->off_thread()->logger())
: LocalLoggerWrapper(isolate_->main_thread()->logger());
}
LocalHeapWrapper heap() {
return isolate_->is_off_thread()
? LocalHeapWrapper(isolate_->off_thread()->heap())
: LocalHeapWrapper(isolate_->main_thread()->heap());
}
ReadOnlyHeap* read_only_heap() {
return isolate_->is_off_thread()
? isolate_->off_thread()->read_only_heap()
: isolate_->main_thread()->read_only_heap();
}
Object root(RootIndex index) {
return isolate_->is_off_thread() ? isolate_->off_thread()->root(index)
: isolate_->main_thread()->root(index);
}
int GetNextScriptId() {
return isolate_->is_off_thread()
? isolate_->off_thread()->GetNextScriptId()
: isolate_->main_thread()->GetNextScriptId();
}
private:
LocalIsolateWrapper* isolate_;
};
// Helper wrapper for HandleScope behaviour with a LocalIsolateWrapper.
class LocalHandleScopeWrapper {
public:
explicit LocalHandleScopeWrapper(LocalIsolateWrapper local_isolate)
: is_off_thread_(local_isolate.is_off_thread()) {
if (is_off_thread_) {
new (off_thread()) OffThreadHandleScope(local_isolate.off_thread());
} else {
new (main_thread()) HandleScope(local_isolate.main_thread());
}
}
~LocalHandleScopeWrapper() {
if (is_off_thread_) {
off_thread()->~OffThreadHandleScope();
} else {
main_thread()->~HandleScope();
}
}
template <typename T>
Handle<T> CloseAndEscape(Handle<T> handle) {
if (is_off_thread_) {
return off_thread()->CloseAndEscape(handle);
} else {
return main_thread()->CloseAndEscape(handle);
}
}
private:
HandleScope* main_thread() {
return reinterpret_cast<HandleScope*>(&scope_storage_);
}
OffThreadHandleScope* off_thread() {
return reinterpret_cast<OffThreadHandleScope*>(&scope_storage_);
}
std::aligned_union_t<0, HandleScope, OffThreadHandleScope> scope_storage_;
bool is_off_thread_;
};
} // namespace internal
} // namespace v8
#endif // V8_EXECUTION_LOCAL_ISOLATE_WRAPPER_INL_H_
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_EXECUTION_LOCAL_ISOLATE_WRAPPER_H_
#define V8_EXECUTION_LOCAL_ISOLATE_WRAPPER_H_
#include "src/utils/pointer-with-payload.h"
namespace v8 {
namespace internal {
// LocalWrapperBase is the base-class for wrapper classes around a main-thread
// and off-thread type, e.g. Isolate and OffThreadIsolate, and a bit stating
// which of the two the wrapper wraps.
//
// The shared methods are defined on MethodCaller, which will dispatch to the
// right type depending on the state of the wrapper. The reason for a separate
// MethodCaller is to
//
// a) Move the method definitions into an -inl.h so that this header can have
// minimal dependencies, and
// b) To allow the type methods to be called with operator-> (e.g.
// isolate_wrapper->heap()), while forcing the wrapper methods to be called
// with a dot (e.g. isolate_wrapper.is_main_thread()).
template <typename MainThreadType, typename OffThreadType,
typename MethodCaller>
class LocalWrapperBase {
public:
// Helper for returning a MethodCaller* by value from operator->.
class MethodCallerRef {
public:
MethodCaller* operator->() { return &caller_; }
private:
friend class LocalWrapperBase;
explicit MethodCallerRef(LocalWrapperBase* wrapper) : caller_(wrapper) {}
MethodCaller caller_;
};
explicit LocalWrapperBase(std::nullptr_t) : pointer_and_tag_(nullptr) {}
explicit LocalWrapperBase(MainThreadType* pointer)
: pointer_and_tag_(pointer, false) {}
explicit LocalWrapperBase(OffThreadType* pointer)
: pointer_and_tag_(pointer, true) {}
MainThreadType* main_thread() {
DCHECK(is_main_thread());
return static_cast<MainThreadType*>(
pointer_and_tag_.GetPointerWithKnownPayload(false));
}
OffThreadType* off_thread() {
DCHECK(is_off_thread());
return static_cast<OffThreadType*>(
pointer_and_tag_.GetPointerWithKnownPayload(true));
}
bool is_main_thread() const {
return !is_null() && !pointer_and_tag_.GetPayload();
}
bool is_off_thread() const {
return !is_null() && pointer_and_tag_.GetPayload();
}
bool is_null() const { return pointer_and_tag_.GetPointer() == nullptr; }
// Access the methods via wrapper->Method.
MethodCallerRef operator->() { return MethodCallerRef(this); }
private:
PointerWithPayload<void, bool, 1> pointer_and_tag_;
};
using LocalHeapWrapper =
LocalWrapperBase<class Heap, class OffThreadHeap, class HeapMethodCaller>;
using LocalLoggerWrapper = LocalWrapperBase<class Logger, class OffThreadLogger,
class LoggerMethodCaller>;
using LocalIsolateWrapper =
LocalWrapperBase<class Isolate, class OffThreadIsolate,
class IsolateMethodCaller>;
} // namespace internal
} // namespace v8
#endif // V8_EXECUTION_LOCAL_ISOLATE_WRAPPER_H_
......@@ -15,6 +15,14 @@ namespace internal {
Address OffThreadIsolate::isolate_root() const {
return isolate_->isolate_root();
}
ReadOnlyHeap* OffThreadIsolate::read_only_heap() {
return isolate_->read_only_heap();
}
Object OffThreadIsolate::root(RootIndex index) {
DCHECK(RootsTable::IsImmortalImmovable(index));
return isolate_->root(index);
}
} // namespace internal
} // namespace v8
......
......@@ -86,6 +86,8 @@ class V8_EXPORT_PRIVATE OffThreadIsolate final
OffThreadHeap* heap() { return &heap_; }
inline Address isolate_root() const;
inline ReadOnlyHeap* read_only_heap();
inline Object root(RootIndex index);
v8::internal::OffThreadFactory* factory() {
// Upcast to the privately inherited base-class using c-style casts to avoid
......
......@@ -6,6 +6,7 @@
#define V8_HANDLES_HANDLES_INL_H_
#include "src/execution/isolate.h"
#include "src/execution/local-isolate-wrapper.h"
#include "src/execution/off-thread-isolate.h"
#include "src/handles/handles.h"
#include "src/handles/local-handles-inl.h"
......@@ -66,19 +67,11 @@ V8_INLINE Handle<T> handle(T object, LocalHeap* local_heap) {
return Handle<T>(object, local_heap);
}
// Convenience overloads for when we already have a Handle, but want
// either a Handle or an Handle.
template <typename T>
V8_INLINE Handle<T> handle(Handle<T> handle, Isolate* isolate) {
return handle;
}
template <typename T>
V8_INLINE Handle<T> handle(Handle<T> handle, OffThreadIsolate* isolate) {
return Handle<T>(*handle);
}
template <typename T>
V8_INLINE Handle<T> handle(Handle<T> handle, LocalHeap* local_heap) {
return Handle<T>(*handle, local_heap);
V8_INLINE Handle<T> handle(T object, LocalIsolateWrapper local_isolate) {
return local_isolate.is_off_thread()
? handle(object, local_isolate.off_thread())
: handle(object, local_isolate.main_thread());
}
template <typename T>
......
......@@ -199,6 +199,11 @@ class HandleScope {
explicit inline HandleScope(Isolate* isolate);
inline HandleScope(HandleScope&& other) V8_NOEXCEPT;
// Allow placement new.
void* operator new(size_t size, void* storage) {
return ::operator new(size, storage);
}
inline ~HandleScope();
inline HandleScope& operator=(HandleScope&& other) V8_NOEXCEPT;
......@@ -235,8 +240,8 @@ class HandleScope {
private:
// Prevent heap allocation or illegal handle scopes.
void* operator new(size_t size);
void operator delete(void* size_t);
void* operator new(size_t size) = delete;
void operator delete(void* size_t) = delete;
Isolate* isolate_;
Address* prev_next_;
......
......@@ -4,10 +4,12 @@
#include "src/heap/off-thread-heap.h"
#include "src/common/globals.h"
#include "src/heap/spaces-inl.h"
#include "src/heap/spaces.h"
#include "src/objects/objects-body-descriptors-inl.h"
#include "src/roots/roots.h"
#include "src/snapshot/references.h"
// Has to be the last include (doesn't have include guards)
#include "src/objects/object-macros.h"
......@@ -17,6 +19,10 @@ namespace internal {
OffThreadHeap::OffThreadHeap(Heap* heap) : space_(heap), lo_space_(heap) {}
bool OffThreadHeap::Contains(HeapObject obj) {
return space_.Contains(obj) || lo_space_.Contains(obj);
}
class OffThreadHeap::StringSlotCollectingVisitor : public ObjectVisitor {
public:
void VisitPointers(HeapObject host, ObjectSlot start,
......@@ -223,7 +229,37 @@ HeapObject OffThreadHeap::AllocateRaw(int size, AllocationType allocation,
} else {
result = space_.AllocateRaw(size, alignment);
}
return result.ToObjectChecked();
HeapObject obj = result.ToObjectChecked();
OnAllocationEvent(obj, size);
return obj;
}
bool OffThreadHeap::ReserveSpace(Heap::Reservation* reservations) {
#ifdef DEBUG
for (int space = FIRST_SPACE;
space < static_cast<int>(SnapshotSpace::kNumberOfHeapSpaces); space++) {
if (space == OLD_SPACE || space == LO_SPACE) continue;
Heap::Reservation* reservation = &reservations[space];
DCHECK_EQ(reservation->size(), 1);
DCHECK_EQ(reservation->at(0).size, 0);
}
#endif
for (auto& chunk : reservations[OLD_SPACE]) {
int size = chunk.size;
AllocationResult allocation = space_.AllocateRawUnaligned(size);
HeapObject free_space = allocation.ToObjectChecked();
// Mark with a free list node, in case we have a GC before
// deserializing.
Address free_space_address = free_space.address();
CreateFillerObjectAt(free_space_address, size,
ClearFreedMemoryMode::kDontClearFreedMemory);
chunk.start = free_space_address;
chunk.end = free_space_address + size;
}
return true;
}
HeapObject OffThreadHeap::CreateFillerObjectAt(
......
......@@ -6,9 +6,11 @@
#define V8_HEAP_OFF_THREAD_HEAP_H_
#include <vector>
#include "src/common/globals.h"
#include "src/heap/large-spaces.h"
#include "src/heap/spaces.h"
#include "src/objects/heap-object.h"
namespace v8 {
namespace internal {
......@@ -21,6 +23,19 @@ class V8_EXPORT_PRIVATE OffThreadHeap {
AllocationAlignment alignment = kWordAligned);
void AddToScriptList(Handle<Script> shared);
void OnAllocationEvent(HeapObject obj, int size) {
// TODO(leszeks): Do something here.
}
ReadOnlySpace* read_only_space() const {
// Access the main-thread heap via the spaces.
return space_.heap()->read_only_space();
}
bool Contains(HeapObject obj);
bool ReserveSpace(Heap::Reservation* reservations);
HeapObject CreateFillerObjectAt(Address addr, int size,
ClearFreedMemoryMode clear_memory_mode);
......@@ -28,6 +43,8 @@ class V8_EXPORT_PRIVATE OffThreadHeap {
void Publish(Heap* heap);
private:
friend class DeserializerAllocator;
class StringSlotCollectingVisitor;
struct RelativeSlot {
......
......@@ -72,13 +72,13 @@ class Ticker;
#undef LOG
#define LOG(isolate, Call) \
do { \
auto* logger = (isolate)->logger(); \
auto&& logger = (isolate)->logger(); \
if (logger->is_logging()) logger->Call; \
} while (false)
#define LOG_CODE_EVENT(isolate, Call) \
do { \
auto* logger = (isolate)->logger(); \
auto&& logger = (isolate)->logger(); \
if (logger->is_listening_to_code_events()) logger->Call; \
} while (false)
......
......@@ -19,6 +19,7 @@ class OffThreadLogger {
void ScriptEvent(Logger::ScriptEventType type, int script_id) {
UNREACHABLE();
}
void ScriptDetails(Script script) { UNREACHABLE(); }
void CodeLinePosInfoRecordEvent(Address code_start,
ByteArray source_position_table) {
UNREACHABLE();
......
......@@ -191,7 +191,7 @@ class HeapObject : public Object {
bool CanBeRehashed() const;
// Rehash the object based on the layout inferred from its map.
void RehashBasedOnMap(ReadOnlyRoots root);
void RehashBasedOnMap(LocalIsolateWrapper isolate);
// Layout description.
#define HEAP_OBJECT_FIELDS(V) \
......
......@@ -6,6 +6,7 @@
#define V8_OBJECTS_MAYBE_OBJECT_INL_H_
#include "src/common/ptr-compr-inl.h"
#include "src/execution/local-isolate-wrapper.h"
#include "src/objects/maybe-object.h"
#include "src/objects/smi-inl.h"
#include "src/objects/tagged-impl-inl.h"
......@@ -88,6 +89,13 @@ HeapObjectReference HeapObjectReference::ClearedValue(
return HeapObjectReference(raw_value);
}
// static
HeapObjectReference HeapObjectReference::ClearedValue(
LocalIsolateWrapper isolate) {
return isolate.is_off_thread() ? ClearedValue(isolate.off_thread())
: ClearedValue(isolate.main_thread());
}
template <typename THeapObjectSlot>
void HeapObjectReference::Update(THeapObjectSlot slot, HeapObject value) {
static_assert(std::is_same<THeapObjectSlot, FullHeapObjectSlot>::value ||
......
......@@ -5,6 +5,7 @@
#ifndef V8_OBJECTS_MAYBE_OBJECT_H_
#define V8_OBJECTS_MAYBE_OBJECT_H_
#include "src/execution/local-isolate-wrapper.h"
#include "src/objects/tagged-impl.h"
namespace v8 {
......@@ -52,6 +53,9 @@ class HeapObjectReference : public MaybeObject {
V8_INLINE static HeapObjectReference ClearedValue(
const OffThreadIsolate* isolate);
V8_INLINE static HeapObjectReference ClearedValue(
LocalIsolateWrapper isolate);
template <typename THeapObjectSlot>
V8_INLINE static void Update(THeapObjectSlot slot, HeapObject value);
};
......
......@@ -2354,7 +2354,8 @@ bool HeapObject::CanBeRehashed() const {
return false;
}
void HeapObject::RehashBasedOnMap(ReadOnlyRoots roots) {
void HeapObject::RehashBasedOnMap(LocalIsolateWrapper isolate) {
ReadOnlyRoots roots = ReadOnlyRoots(isolate);
switch (map().instance_type()) {
case HASH_TABLE_TYPE:
UNREACHABLE();
......
......@@ -5,9 +5,8 @@
#ifndef V8_ROOTS_ROOTS_INL_H_
#define V8_ROOTS_ROOTS_INL_H_
#include "src/roots/roots.h"
#include "src/execution/isolate.h"
#include "src/execution/local-isolate-wrapper.h"
#include "src/execution/off-thread-isolate.h"
#include "src/handles/handles.h"
#include "src/heap/read-only-heap.h"
......@@ -23,6 +22,7 @@
#include "src/objects/scope-info.h"
#include "src/objects/slots.h"
#include "src/objects/string.h"
#include "src/roots/roots.h"
namespace v8 {
namespace internal {
......@@ -72,7 +72,14 @@ ReadOnlyRoots::ReadOnlyRoots(Isolate* isolate)
ReadOnlyRoots::ReadOnlyRoots(OffThreadIsolate* isolate)
: ReadOnlyRoots(isolate->factory()->read_only_roots()) {}
ReadOnlyRoots::ReadOnlyRoots(Address* ro_roots) : read_only_roots_(ro_roots) {}
ReadOnlyRoots::ReadOnlyRoots(LocalHeapWrapper heap)
: ReadOnlyRoots(heap.is_off_thread() ? ReadOnlyRoots(heap.off_thread())
: ReadOnlyRoots(heap.main_thread())) {}
ReadOnlyRoots::ReadOnlyRoots(LocalIsolateWrapper isolate)
: ReadOnlyRoots(isolate.is_off_thread()
? ReadOnlyRoots(isolate.off_thread())
: ReadOnlyRoots(isolate.main_thread())) {}
// We use unchecked_cast below because we trust our read-only roots to
// have the right type, and to avoid the heavy #includes that would be
......
......@@ -8,6 +8,7 @@
#include "src/base/macros.h"
#include "src/builtins/accessors.h"
#include "src/common/globals.h"
#include "src/execution/local-isolate-wrapper.h"
#include "src/handles/handles.h"
#include "src/init/heap-symbols.h"
#include "src/objects/objects-definitions.h"
......@@ -527,6 +528,8 @@ class ReadOnlyRoots {
V8_INLINE explicit ReadOnlyRoots(OffThreadHeap* heap);
V8_INLINE explicit ReadOnlyRoots(Isolate* isolate);
V8_INLINE explicit ReadOnlyRoots(OffThreadIsolate* isolate);
V8_INLINE explicit ReadOnlyRoots(LocalIsolateWrapper wrapper);
V8_INLINE explicit ReadOnlyRoots(LocalHeapWrapper wrapper);
#define ROOT_ACCESSOR(Type, name, CamelName) \
V8_INLINE class Type name() const; \
......@@ -553,13 +556,15 @@ class ReadOnlyRoots {
#undef ROOT_TYPE_CHECK
#endif
V8_INLINE explicit ReadOnlyRoots(Address* ro_roots);
V8_INLINE explicit ReadOnlyRoots(Address* ro_roots)
: read_only_roots_(ro_roots) {}
V8_INLINE Address* GetLocation(RootIndex root_index) const;
Address* read_only_roots_;
friend class ReadOnlyHeap;
friend class DeserializerAllocator;
};
} // namespace internal
......
......@@ -4,9 +4,12 @@
#include "src/snapshot/code-serializer.h"
#include "src/base/platform/platform.h"
#include "src/codegen/macro-assembler.h"
#include "src/common/globals.h"
#include "src/debug/debug.h"
#include "src/heap/heap-inl.h"
#include "src/heap/off-thread-factory-inl.h"
#include "src/logging/counters.h"
#include "src/logging/log.h"
#include "src/objects/objects-inl.h"
......@@ -259,6 +262,39 @@ void CreateInterpreterDataForDeserializedCode(Isolate* isolate,
}
#endif // V8_TARGET_ARCH_ARM
namespace {
class StressOffThreadDeserializeThread final : public base::Thread {
public:
explicit StressOffThreadDeserializeThread(
OffThreadIsolate* off_thread_isolate, const SerializedCodeData* scd)
: Thread(
base::Thread::Options("StressOffThreadDeserializeThread", 2 * MB)),
off_thread_isolate_(off_thread_isolate),
scd_(scd) {}
MaybeHandle<SharedFunctionInfo> maybe_result() const {
return maybe_result_.ToHandle();
}
void Run() final {
off_thread_isolate_->PinToCurrentThread();
MaybeHandle<SharedFunctionInfo> off_thread_maybe_result =
ObjectDeserializer::DeserializeSharedFunctionInfoOffThread(
off_thread_isolate_, scd_,
off_thread_isolate_->factory()->empty_string());
maybe_result_ =
off_thread_isolate_->TransferHandle(off_thread_maybe_result);
}
private:
OffThreadIsolate* off_thread_isolate_;
const SerializedCodeData* scd_;
OffThreadTransferMaybeHandle<SharedFunctionInfo> maybe_result_;
};
} // namespace
MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
Isolate* isolate, ScriptData* cached_data, Handle<String> source,
ScriptOriginOptions origin_options) {
......@@ -281,8 +317,29 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
}
// Deserialize.
MaybeHandle<SharedFunctionInfo> maybe_result =
ObjectDeserializer::DeserializeSharedFunctionInfo(isolate, &scd, source);
MaybeHandle<SharedFunctionInfo> maybe_result;
if (FLAG_stress_background_compile) {
Zone zone(isolate->allocator(), "Deserialize");
OffThreadIsolate off_thread_isolate(isolate, &zone);
StressOffThreadDeserializeThread thread(&off_thread_isolate, &scd);
CHECK(thread.Start());
thread.Join();
off_thread_isolate.FinishOffThread();
off_thread_isolate.Publish(isolate);
maybe_result = thread.maybe_result();
// Fix-up result script source.
Handle<SharedFunctionInfo> result;
if (maybe_result.ToHandle(&result)) {
Script::cast(result->script()).set_source(*source);
}
} else {
maybe_result = ObjectDeserializer::DeserializeSharedFunctionInfo(
isolate, &scd, source);
}
Handle<SharedFunctionInfo> result;
if (!maybe_result.ToHandle(&result)) {
......@@ -356,7 +413,6 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
return scope.CloseAndEscape(result);
}
SerializedCodeData::SerializedCodeData(const std::vector<byte>* payload,
const CodeSerializer* cs) {
DisallowHeapAllocation no_gc;
......
......@@ -6,10 +6,17 @@
#include "src/heap/heap-inl.h" // crbug.com/v8/8499
#include "src/heap/memory-chunk.h"
#include "src/roots/roots.h"
namespace v8 {
namespace internal {
void DeserializerAllocator::Initialize(LocalHeapWrapper heap) {
heap_ = heap;
roots_ = heap.is_off_thread() ? ReadOnlyRoots(heap.off_thread())
: ReadOnlyRoots(heap.main_thread());
}
// We know the space requirements before deserialization and can
// pre-allocate that reserved space. During deserialization, all we need
// to do is to bump up the pointer for each space in the reserved
......@@ -24,12 +31,18 @@ namespace internal {
Address DeserializerAllocator::AllocateRaw(SnapshotSpace space, int size) {
const int space_number = static_cast<int>(space);
if (space == SnapshotSpace::kLargeObject) {
AlwaysAllocateScope scope(heap_);
// Note that we currently do not support deserialization of large code
// objects.
OldLargeObjectSpace* lo_space = heap_->lo_space();
AllocationResult result = lo_space->AllocateRaw(size);
HeapObject obj = result.ToObjectChecked();
HeapObject obj;
if (heap_.is_off_thread()) {
obj = heap_.off_thread()->lo_space_.AllocateRaw(size).ToObjectChecked();
} else {
Heap* heap = heap_.main_thread();
AlwaysAllocateScope scope(heap);
OldLargeObjectSpace* lo_space = heap->lo_space();
AllocationResult result = lo_space->AllocateRaw(size);
obj = result.ToObjectChecked();
}
deserialized_large_objects_.push_back(obj);
return obj.address();
} else if (space == SnapshotSpace::kMap) {
......@@ -82,11 +95,10 @@ Address DeserializerAllocator::Allocate(SnapshotSpace space, int size) {
// If one of the following assertions fails, then we are deserializing an
// aligned object when the filler maps have not been deserialized yet.
// We require filler maps as padding to align the object.
DCHECK(ReadOnlyRoots(heap_).free_space_map().IsMap());
DCHECK(ReadOnlyRoots(heap_).one_pointer_filler_map().IsMap());
DCHECK(ReadOnlyRoots(heap_).two_pointer_filler_map().IsMap());
obj = Heap::AlignWithFiller(ReadOnlyRoots(heap_), obj, size, reserved,
next_alignment_);
DCHECK(roots_.free_space_map().IsMap());
DCHECK(roots_.one_pointer_filler_map().IsMap());
DCHECK(roots_.two_pointer_filler_map().IsMap());
obj = Heap::AlignWithFiller(roots_, obj, size, reserved, next_alignment_);
address = obj.address();
next_alignment_ = kWordAligned;
return address;
......@@ -109,6 +121,7 @@ void DeserializerAllocator::MoveToNextChunk(SnapshotSpace space) {
}
HeapObject DeserializerAllocator::GetMap(uint32_t index) {
DCHECK(!heap_.is_off_thread());
DCHECK_LT(index, next_map_index_);
return HeapObject::FromAddress(allocated_maps_[index]);
}
......@@ -156,10 +169,16 @@ bool DeserializerAllocator::ReserveSpace() {
}
#endif // DEBUG
DCHECK(allocated_maps_.empty());
// TODO(v8:7464): Allocate using the off-heap ReadOnlySpace here once
// implemented.
if (!heap_->ReserveSpace(reservations_, &allocated_maps_)) {
return false;
if (heap_.is_off_thread()) {
if (!heap_.off_thread()->ReserveSpace(reservations_)) {
return false;
}
} else {
// TODO(v8:7464): Allocate using the off-heap ReadOnlySpace here once
// implemented.
if (!heap_.main_thread()->ReserveSpace(reservations_, &allocated_maps_)) {
return false;
}
}
for (int i = 0; i < kNumberOfPreallocatedSpaces; i++) {
high_water_[i] = reservations_[i][0].start;
......@@ -181,7 +200,8 @@ bool DeserializerAllocator::ReservationsAreFullyUsed() const {
}
void DeserializerAllocator::RegisterDeserializedObjectsForBlackAllocation() {
heap_->RegisterDeserializedObjectsForBlackAllocation(
DCHECK(!heap_.is_off_thread());
heap_.main_thread()->RegisterDeserializedObjectsForBlackAllocation(
reservations_, deserialized_large_objects_, allocated_maps_);
}
......
......@@ -6,8 +6,10 @@
#define V8_SNAPSHOT_DESERIALIZER_ALLOCATOR_H_
#include "src/common/globals.h"
#include "src/execution/local-isolate-wrapper.h"
#include "src/heap/heap.h"
#include "src/objects/heap-object.h"
#include "src/roots/roots.h"
#include "src/snapshot/references.h"
#include "src/snapshot/snapshot-data.h"
......@@ -16,12 +18,13 @@ namespace internal {
class Deserializer;
class StartupDeserializer;
class OffThreadHeap;
class DeserializerAllocator final {
public:
DeserializerAllocator() = default;
void Initialize(Heap* heap) { heap_ = heap; }
void Initialize(LocalHeapWrapper heap);
// ------- Allocation Methods -------
// Methods related to memory allocation during deserialization.
......@@ -99,7 +102,9 @@ class DeserializerAllocator final {
// back-references.
std::vector<HeapObject> deserialized_large_objects_;
Heap* heap_;
// ReadOnlyRoots and heap are null until Initialize is called.
LocalHeapWrapper heap_ = LocalHeapWrapper(nullptr);
ReadOnlyRoots roots_ = ReadOnlyRoots(static_cast<Address*>(nullptr));
DISALLOW_COPY_AND_ASSIGN(DeserializerAllocator);
};
......
This diff is collapsed.
......@@ -8,6 +8,7 @@
#include <utility>
#include <vector>
#include "src/execution/local-isolate-wrapper.h"
#include "src/objects/allocation-site.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/backing-store.h"
......@@ -47,7 +48,7 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
// Create a deserializer from a snapshot byte source.
template <class Data>
Deserializer(Data* data, bool deserializing_user_code)
: isolate_(nullptr),
: local_isolate_(nullptr),
source_(data->Payload()),
magic_number_(data->GetMagicNumber()),
deserializing_user_code_(deserializing_user_code),
......@@ -58,7 +59,10 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
backing_stores_.push_back({});
}
void Initialize(Isolate* isolate);
void Initialize(Isolate* isolate) {
Initialize(LocalIsolateWrapper(isolate));
}
void Initialize(LocalIsolateWrapper isolate);
void DeserializeDeferredObjects();
// Create Log events for newly deserialized objects.
......@@ -80,7 +84,11 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
CHECK_EQ(new_off_heap_array_buffers().size(), 0);
}
Isolate* isolate() const { return isolate_; }
LocalIsolateWrapper local_isolate() const { return local_isolate_; }
Isolate* isolate() const { return local_isolate().main_thread(); }
bool is_main_thread() const { return local_isolate().is_main_thread(); }
bool is_off_thread() const { return local_isolate().is_off_thread(); }
SnapshotByteSource* source() { return &source_; }
const std::vector<AllocationSite>& new_allocation_sites() const {
return new_allocation_sites_;
......@@ -117,9 +125,6 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
void Rehash();
// Cached current isolate.
Isolate* isolate_;
private:
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override;
......@@ -148,9 +153,8 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
// Returns the new value of {current}.
template <typename TSlot, Bytecode bytecode,
SnapshotSpace space_number_if_any>
inline TSlot ReadDataCase(Isolate* isolate, TSlot current,
Address current_object_address, byte data,
bool write_barrier_needed);
inline TSlot ReadDataCase(TSlot current, Address current_object_address,
byte data, bool write_barrier_needed);
// A helper function for ReadData for reading external references.
inline Address ReadExternalReferenceCase();
......@@ -175,6 +179,9 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
// Special handling for serialized code like hooking up internalized strings.
HeapObject PostProcessNewObject(HeapObject obj, SnapshotSpace space);
// Cached current isolate.
LocalIsolateWrapper local_isolate_;
// Objects from the attached object descriptions in the serialized user code.
std::vector<Handle<HeapObject>> attached_objects_;
......
......@@ -6,6 +6,7 @@
#include "src/codegen/assembler-inl.h"
#include "src/execution/isolate.h"
#include "src/execution/local-isolate-wrapper-inl.h"
#include "src/heap/heap-inl.h"
#include "src/objects/allocation-site-inl.h"
#include "src/objects/objects.h"
......@@ -26,75 +27,91 @@ ObjectDeserializer::DeserializeSharedFunctionInfo(
d.AddAttachedObject(source);
Handle<HeapObject> result;
return d.Deserialize(isolate).ToHandle(&result)
return d.Deserialize(LocalIsolateWrapper(isolate)).ToHandle(&result)
? Handle<SharedFunctionInfo>::cast(result)
: MaybeHandle<SharedFunctionInfo>();
}
MaybeHandle<HeapObject> ObjectDeserializer::Deserialize(Isolate* isolate) {
Initialize(isolate);
MaybeHandle<SharedFunctionInfo>
ObjectDeserializer::DeserializeSharedFunctionInfoOffThread(
OffThreadIsolate* isolate, const SerializedCodeData* data,
Handle<String> source) {
DCHECK(ReadOnlyHeap::Contains(*source) || Heap::InOffThreadSpace(*source));
ObjectDeserializer d(data);
d.AddAttachedObject(source);
Handle<HeapObject> result;
return d.Deserialize(LocalIsolateWrapper(isolate)).ToHandle(&result)
? Handle<SharedFunctionInfo>::cast(result)
: MaybeHandle<SharedFunctionInfo>();
}
MaybeHandle<HeapObject> ObjectDeserializer::Deserialize(
LocalIsolateWrapper local_isolate) {
Initialize(local_isolate);
if (!allocator()->ReserveSpace()) return MaybeHandle<HeapObject>();
DCHECK(deserializing_user_code());
HandleScope scope(isolate);
LocalHandleScopeWrapper scope(local_isolate);
Handle<HeapObject> result;
{
DisallowHeapAllocation no_gc;
Object root;
VisitRootPointer(Root::kStartupObjectCache, nullptr, FullObjectSlot(&root));
DeserializeDeferredObjects();
FlushICache();
LinkAllocationSites();
LogNewMapEvents();
result = handle(HeapObject::cast(root), isolate);
CHECK(new_code_objects().empty());
if (is_main_thread()) {
LinkAllocationSites();
LogNewMapEvents();
}
result = handle(HeapObject::cast(root), local_isolate);
Rehash();
allocator()->RegisterDeserializedObjectsForBlackAllocation();
if (is_main_thread()) {
allocator()->RegisterDeserializedObjectsForBlackAllocation();
}
}
CommitPostProcessedObjects();
return scope.CloseAndEscape(result);
}
void ObjectDeserializer::FlushICache() {
DCHECK(deserializing_user_code());
for (Code code : new_code_objects()) {
// Record all references to embedded objects in the new code object.
#ifndef V8_DISABLE_WRITE_BARRIERS
WriteBarrierForCode(code);
#endif
FlushInstructionCache(code.raw_instruction_start(),
code.raw_instruction_size());
}
}
void ObjectDeserializer::CommitPostProcessedObjects() {
CHECK_LE(new_internalized_strings().size(), kMaxInt);
StringTable::EnsureCapacityForDeserialization(
isolate(), static_cast<int>(new_internalized_strings().size()));
for (Handle<String> string : new_internalized_strings()) {
DisallowHeapAllocation no_gc;
StringTableInsertionKey key(*string);
StringTable::AddKeyNoResize(isolate(), &key);
if (is_main_thread()) {
CHECK_LE(new_internalized_strings().size(), kMaxInt);
StringTable::EnsureCapacityForDeserialization(
isolate(), static_cast<int>(new_internalized_strings().size()));
for (Handle<String> string : new_internalized_strings()) {
DisallowHeapAllocation no_gc;
StringTableInsertionKey key(*string);
StringTable::AddKeyNoResize(isolate(), &key);
}
for (Handle<JSArrayBuffer> buffer : new_off_heap_array_buffers()) {
uint32_t store_index = buffer->GetBackingStoreRefForDeserialization();
auto bs = backing_store(store_index);
SharedFlag shared =
bs && bs->is_shared() ? SharedFlag::kShared : SharedFlag::kNotShared;
buffer->Setup(shared, bs);
}
} else {
CHECK_EQ(new_internalized_strings().size(), 0);
CHECK_EQ(new_off_heap_array_buffers().size(), 0);
}
Heap* heap = isolate()->heap();
Factory* factory = isolate()->factory();
for (Handle<Script> script : new_scripts()) {
// Assign a new script id to avoid collision.
script->set_id(isolate()->GetNextScriptId());
script->set_id(local_isolate()->GetNextScriptId());
LogScriptEvents(*script);
// Add script to list.
Handle<WeakArrayList> list = factory->script_list();
list = WeakArrayList::AddToEnd(isolate(), list,
MaybeObjectHandle::Weak(script));
heap->SetRootScriptList(*list);
}
for (Handle<JSArrayBuffer> buffer : new_off_heap_array_buffers()) {
uint32_t store_index = buffer->GetBackingStoreRefForDeserialization();
auto bs = backing_store(store_index);
SharedFlag shared =
bs && bs->is_shared() ? SharedFlag::kShared : SharedFlag::kNotShared;
buffer->Setup(shared, bs);
if (is_main_thread()) {
Handle<WeakArrayList> list = isolate()->factory()->script_list();
list = WeakArrayList::AddToEnd(isolate(), list,
MaybeObjectHandle::Weak(script));
isolate()->heap()->SetRootScriptList(*list);
} else {
local_isolate().off_thread()->heap()->AddToScriptList(script);
}
}
}
......
......@@ -18,14 +18,16 @@ class ObjectDeserializer final : public Deserializer {
public:
static MaybeHandle<SharedFunctionInfo> DeserializeSharedFunctionInfo(
Isolate* isolate, const SerializedCodeData* data, Handle<String> source);
static MaybeHandle<SharedFunctionInfo> DeserializeSharedFunctionInfoOffThread(
OffThreadIsolate* isolate, const SerializedCodeData* data,
Handle<String> source);
private:
explicit ObjectDeserializer(const SerializedCodeData* data);
// Deserialize an object graph. Fail gracefully.
MaybeHandle<HeapObject> Deserialize(Isolate* isolate);
MaybeHandle<HeapObject> Deserialize(LocalIsolateWrapper isolate);
void FlushICache();
void LinkAllocationSites();
void CommitPostProcessedObjects();
};
......
......@@ -55,7 +55,7 @@ void ReadOnlyDeserializer::DeserializeInto(Isolate* isolate) {
}
if (FLAG_rehash_snapshot && can_rehash()) {
isolate_->heap()->InitializeHashSeed();
isolate->heap()->InitializeHashSeed();
Rehash();
}
}
......
......@@ -74,7 +74,7 @@ void StartupDeserializer::DeserializeInto(Isolate* isolate) {
}
void StartupDeserializer::LogNewMapEvents() {
if (FLAG_trace_maps) LOG(isolate_, LogAllMaps());
if (FLAG_trace_maps) LOG(isolate(), LogAllMaps());
}
void StartupDeserializer::FlushICache() {
......
......@@ -20,6 +20,12 @@ struct PointerWithPayloadTraits {
alignof(PointerType) >= 8 ? 3 : alignof(PointerType) >= 4 ? 2 : 1;
};
// Assume void* has the same payloads as void**, under the assumption that it's
// used for classes that contain at least one pointer.
template <>
struct PointerWithPayloadTraits<void> : public PointerWithPayloadTraits<void*> {
};
// PointerWithPayload combines a PointerType* an a small PayloadType into
// one. The bits of the storage type get packed into the lower bits of the
// pointer that are free due to alignment. The user needs to specify how many
......@@ -42,7 +48,8 @@ class PointerWithPayload {
"Ptr does not have sufficient alignment for the selected amount of "
"storage bits.");
static constexpr uintptr_t kPayloadMask = (uintptr_t{1} << kAvailBits) - 1;
static constexpr uintptr_t kPayloadMask =
(uintptr_t{1} << NumPayloadBits) - 1;
static constexpr uintptr_t kPointerMask = ~kPayloadMask;
public:
......@@ -68,6 +75,13 @@ class PointerWithPayload {
return reinterpret_cast<PointerType*>(pointer_ & kPointerMask);
}
// An optimized version of GetPointer for when we know the payload value.
V8_INLINE PointerType* GetPointerWithKnownPayload(PayloadType payload) const {
DCHECK_EQ(GetPayload(), payload);
return reinterpret_cast<PointerType*>(pointer_ -
static_cast<uintptr_t>(payload));
}
V8_INLINE PointerType* operator->() const { return GetPointer(); }
V8_INLINE void update(PointerType* new_pointer, PayloadType new_payload) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment