[offthread] Template deserializer on Isolate

Make the deserializer class templated on Isolate/LocalIsolate. This
allows the ObjectSerializer to be split into a main-thread and offthread
variant, with the latter taking a LocalIsolate.

Eventually, we probably want to anyway split off the code-cache de/serializer
to a separate implementation (for various reasons), and this the only one that
wants off-thread finalization, and at this point the deserializer can revert
back to being un-templated, used only for bootstrapping. However, this is the
simplest way, for now, to enable off-thread deserialization.

Bug: chromium:1075999
Change-Id: I49c0d2c5409f0aa58183673785296756c3714f22
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2562254
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75834}
This commit is contained in:
Leszek Swirski 2021-07-20 14:51:23 +02:00 committed by V8 LUCI CQ
parent 7179c71e12
commit e24fa91327
31 changed files with 503 additions and 215 deletions

View File

@ -1768,6 +1768,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
return main_thread_local_isolate_.get();
}
Isolate* AsIsolate() { return this; }
LocalIsolate* AsLocalIsolate() { return main_thread_local_isolate(); }
LocalHeap* main_thread_local_heap();

View File

@ -22,6 +22,11 @@ Object LocalIsolate::root(RootIndex index) const {
return isolate_->root(index);
}
Handle<Object> LocalIsolate::root_handle(RootIndex index) const {
DCHECK(RootsTable::IsImmortalImmovable(index));
return isolate_->root_handle(index);
}
} // namespace internal
} // namespace v8

View File

@ -26,6 +26,13 @@ LocalIsolate::LocalIsolate(Isolate* isolate, ThreadKind kind,
LocalIsolate::~LocalIsolate() = default;
void LocalIsolate::RegisterDeserializerStarted() {
return isolate_->RegisterDeserializerStarted();
}
void LocalIsolate::RegisterDeserializerFinished() {
return isolate_->RegisterDeserializerFinished();
}
int LocalIsolate::GetNextScriptId() { return isolate_->GetNextScriptId(); }
#if V8_SFI_HAS_UNIQUE_ID

View File

@ -48,11 +48,14 @@ class V8_EXPORT_PRIVATE LocalIsolate final : private HiddenLocalFactory {
OFFSET_OF(LocalIsolate, heap_));
}
bool is_main_thread() { return heap()->is_main_thread(); }
LocalHeap* heap() { return &heap_; }
inline Address cage_base() const;
inline ReadOnlyHeap* read_only_heap() const;
inline Object root(RootIndex index) const;
inline Handle<Object> root_handle(RootIndex index) const;
StringTable* string_table() const { return isolate_->string_table(); }
base::SharedMutex* internalized_string_access() {
@ -67,6 +70,9 @@ class V8_EXPORT_PRIVATE LocalIsolate final : private HiddenLocalFactory {
bool has_pending_exception() const { return false; }
void RegisterDeserializerStarted();
void RegisterDeserializerFinished();
template <typename T>
Handle<T> Throw(Handle<Object> exception) {
UNREACHABLE();
@ -89,6 +95,12 @@ class V8_EXPORT_PRIVATE LocalIsolate final : private HiddenLocalFactory {
bool is_main_thread() const { return heap_.is_main_thread(); }
// AsIsolate is only allowed on the main-thread.
Isolate* AsIsolate() {
DCHECK(is_main_thread());
DCHECK_EQ(ThreadId::Current(), isolate_->thread_id());
return isolate_;
}
LocalIsolate* AsLocalIsolate() { return this; }
private:

View File

@ -335,14 +335,11 @@ HeapObject Heap::AllocateRawWith(int size, AllocationType allocation,
UNREACHABLE();
}
Address Heap::DeserializerAllocate(AllocationType type, int size_in_bytes) {
if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) {
AllocationResult allocation = tp_heap_->Allocate(
size_in_bytes, type, AllocationAlignment::kDoubleAligned);
return allocation.ToObjectChecked().ptr();
} else {
UNIMPLEMENTED(); // unimplemented
}
Address Heap::AllocateRawOrFail(int size, AllocationType allocation,
AllocationOrigin origin,
AllocationAlignment alignment) {
return AllocateRawWith<kRetryOrFail>(size, allocation, origin, alignment)
.address();
}
void Heap::OnAllocationEvent(HeapObject object, int size_in_bytes) {

View File

@ -567,11 +567,6 @@ class Heap {
V8_EXPORT_PRIVATE static bool IsLargeObject(HeapObject object);
// This method supports the deserialization allocator. All allocations
// are word-aligned. The method should never fail to allocate since the
// total space requirements of the deserializer are known at build time.
inline Address DeserializerAllocate(AllocationType type, int size_in_bytes);
// Trim the given array from the left. Note that this relocates the object
// start and hence is only valid if there is only a single reference to it.
V8_EXPORT_PRIVATE FixedArrayBase LeftTrimFixedArray(FixedArrayBase obj,
@ -2105,6 +2100,12 @@ class Heap {
AllocationOrigin origin = AllocationOrigin::kRuntime,
AllocationAlignment alignment = kWordAligned);
// Call AllocateRawWith with kRetryOrFail. Matches the method in LocalHeap.
V8_WARN_UNUSED_RESULT inline Address AllocateRawOrFail(
int size, AllocationType allocation,
AllocationOrigin origin = AllocationOrigin::kRuntime,
AllocationAlignment alignment = kWordAligned);
// This method will try to perform an allocation of a given size of a given
// AllocationType. If the allocation fails, a regular full garbage collection
// is triggered and the allocation is retried. This is performed multiple
@ -2527,6 +2528,7 @@ class Heap {
// The allocator interface.
friend class Factory;
template <typename IsolateT>
friend class Deserializer;
// The Isolate constructs us.
@ -2580,8 +2582,6 @@ class V8_NODISCARD AlwaysAllocateScope {
private:
friend class AlwaysAllocateScopeForTesting;
friend class Deserializer;
friend class DeserializerAllocator;
friend class Evacuator;
friend class Heap;
friend class Isolate;

View File

@ -10,6 +10,7 @@
#include "src/common/assert-scope.h"
#include "src/handles/persistent-handles.h"
#include "src/heap/concurrent-allocator-inl.h"
#include "src/heap/heap.h"
#include "src/heap/local-heap.h"
namespace v8 {
@ -53,6 +54,13 @@ Address LocalHeap::AllocateRawOrFail(int object_size, AllocationType type,
alignment);
}
void LocalHeap::CreateFillerObjectAt(Address addr, int size,
ClearRecordedSlots clear_slots_mode) {
DCHECK_EQ(clear_slots_mode, ClearRecordedSlots::kNo);
heap()->CreateFillerObjectAtBackground(
addr, size, ClearFreedMemoryMode::kDontClearFreedMemory);
}
} // namespace internal
} // namespace v8

View File

@ -130,7 +130,14 @@ class V8_EXPORT_PRIVATE LocalHeap {
AllocationOrigin origin = AllocationOrigin::kRuntime,
AllocationAlignment alignment = kWordAligned);
inline void CreateFillerObjectAt(Address addr, int size,
ClearRecordedSlots clear_slots_mode);
bool is_main_thread() const { return is_main_thread_; }
bool deserialization_complete() const {
return heap_->deserialization_complete();
}
ReadOnlySpace* read_only_space() { return heap_->read_only_space(); }
// Requests GC and blocks until the collection finishes.
bool TryPerformCollection();

View File

@ -5,6 +5,7 @@
#include "src/logging/local-logger.h"
#include "src/execution/isolate.h"
#include "src/objects/map.h"
namespace v8 {
namespace internal {
@ -27,5 +28,9 @@ void LocalLogger::CodeLinePosInfoRecordEvent(Address code_start,
logger_->CodeLinePosInfoRecordEvent(code_start, source_position_table);
}
void LocalLogger::MapCreate(Map map) { logger_->MapCreate(map); }
void LocalLogger::MapDetails(Map map) { logger_->MapDetails(map); }
} // namespace internal
} // namespace v8

View File

@ -25,6 +25,9 @@ class LocalLogger {
void CodeLinePosInfoRecordEvent(Address code_start,
ByteArray source_position_table);
void MapCreate(Map map);
void MapDetails(Map map);
private:
Logger* logger_;
bool is_logging_;

View File

@ -195,7 +195,8 @@ class HeapObject : public Object {
bool CanBeRehashed() const;
// Rehash the object based on the layout inferred from its map.
void RehashBasedOnMap(Isolate* isolate);
template <typename IsolateT>
void RehashBasedOnMap(IsolateT* isolate);
// Layout description.
#define HEAP_OBJECT_FIELDS(V) \

View File

@ -364,6 +364,7 @@ class JSTypedArray
#endif
private:
template <typename IsolateT>
friend class Deserializer;
friend class Factory;

View File

@ -10,6 +10,7 @@
#include "src/objects/map-inl.h"
#include "src/objects/name.h"
#include "src/objects/primitive-heap-object-inl.h"
#include "src/objects/string-inl.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
@ -97,6 +98,14 @@ uint32_t Name::EnsureHash() {
return String::cast(*this).ComputeAndSetHash();
}
uint32_t Name::EnsureHash(const SharedStringAccessGuardIfNeeded& access_guard) {
// Fast case: has hash code already been computed?
uint32_t field = raw_hash_field();
if (IsHashFieldComputed(field)) return field >> kHashShift;
// Slow case: compute hash code and set it. Has to be a string.
return String::cast(*this).ComputeAndSetHash(access_guard);
}
uint32_t Name::hash() const {
uint32_t field = raw_hash_field();
DCHECK(IsHashFieldComputed(field));

View File

@ -18,6 +18,8 @@ namespace internal {
#include "torque-generated/src/objects/name-tq.inc"
class SharedStringAccessGuardIfNeeded;
// The Name abstract class captures anything that can be used as a property
// name, i.e., strings and symbols. All names store a hash value.
class Name : public TorqueGeneratedName<Name, PrimitiveHeapObject> {
@ -27,7 +29,11 @@ class Name : public TorqueGeneratedName<Name, PrimitiveHeapObject> {
// Returns a hash value used for the property table. Ensures that the hash
// value is computed.
//
// The overload without SharedStringAccessGuardIfNeeded can only be called on
// the main thread.
inline uint32_t EnsureHash();
inline uint32_t EnsureHash(const SharedStringAccessGuardIfNeeded&);
// Returns a hash value used for the property table (same as Hash()), assumes
// the hash is already computed.

View File

@ -2363,7 +2363,8 @@ bool HeapObject::CanBeRehashed() const {
}
}
void HeapObject::RehashBasedOnMap(Isolate* isolate) {
template <typename IsolateT>
void HeapObject::RehashBasedOnMap(IsolateT* isolate) {
switch (map().instance_type()) {
case HASH_TABLE_TYPE:
UNREACHABLE();
@ -2399,11 +2400,11 @@ void HeapObject::RehashBasedOnMap(Isolate* isolate) {
case ORDERED_HASH_SET_TYPE:
UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them.
case JS_MAP_TYPE: {
JSMap::cast(*this).Rehash(isolate);
JSMap::cast(*this).Rehash(isolate->AsIsolate());
break;
}
case JS_SET_TYPE: {
JSSet::cast(*this).Rehash(isolate);
JSSet::cast(*this).Rehash(isolate->AsIsolate());
break;
}
case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
@ -2419,6 +2420,8 @@ void HeapObject::RehashBasedOnMap(Isolate* isolate) {
UNREACHABLE();
}
}
template void HeapObject::RehashBasedOnMap(Isolate* isolate);
template void HeapObject::RehashBasedOnMap(LocalIsolate* isolate);
bool HeapObject::IsExternal(Isolate* isolate) const {
return map().FindRootMap(isolate) == isolate->heap()->external_map();

View File

@ -9,17 +9,19 @@
namespace v8 {
namespace internal {
void StringComparator::State::Init(String string) {
ConsString cons_string = String::VisitFlat(this, string);
void StringComparator::State::Init(
String string, const SharedStringAccessGuardIfNeeded& access_guard) {
ConsString cons_string = String::VisitFlat(this, string, 0, access_guard);
iter_.Reset(cons_string);
if (!cons_string.is_null()) {
int offset;
string = iter_.Next(&offset);
String::VisitFlat(this, string, offset);
String::VisitFlat(this, string, offset, access_guard);
}
}
void StringComparator::State::Advance(int consumed) {
void StringComparator::State::Advance(
int consumed, const SharedStringAccessGuardIfNeeded& access_guard) {
DCHECK(consumed <= length_);
// Still in buffer.
if (length_ != consumed) {
@ -36,13 +38,15 @@ void StringComparator::State::Advance(int consumed) {
String next = iter_.Next(&offset);
DCHECK_EQ(0, offset);
DCHECK(!next.is_null());
String::VisitFlat(this, next);
String::VisitFlat(this, next, 0, access_guard);
}
bool StringComparator::Equals(String string_1, String string_2) {
bool StringComparator::Equals(
String string_1, String string_2,
const SharedStringAccessGuardIfNeeded& access_guard) {
int length = string_1.length();
state_1_.Init(string_1);
state_2_.Init(string_2);
state_1_.Init(string_1, access_guard);
state_2_.Init(string_2, access_guard);
while (true) {
int to_check = std::min(state_1_.length_, state_2_.length_);
DCHECK(to_check > 0 && to_check <= length);
@ -65,8 +69,8 @@ bool StringComparator::Equals(String string_1, String string_2) {
length -= to_check;
// Exit condition. Strings are equal.
if (length == 0) return true;
state_1_.Advance(to_check);
state_2_.Advance(to_check);
state_1_.Advance(to_check, access_guard);
state_2_.Advance(to_check, access_guard);
}
}

View File

@ -20,7 +20,8 @@ class StringComparator {
State(const State&) = delete;
State& operator=(const State&) = delete;
void Init(String string);
void Init(String string,
const SharedStringAccessGuardIfNeeded& access_guard);
inline void VisitOneByteString(const uint8_t* chars, int length) {
is_one_byte_ = true;
@ -34,7 +35,8 @@ class StringComparator {
length_ = length;
}
void Advance(int consumed);
void Advance(int consumed,
const SharedStringAccessGuardIfNeeded& access_guard);
ConsStringIterator iter_;
bool is_one_byte_;
@ -57,7 +59,8 @@ class StringComparator {
return CompareCharsEqual(a, b, to_check);
}
bool Equals(String string_1, String string_2);
bool Equals(String string_1, String string_2,
const SharedStringAccessGuardIfNeeded& access_guard);
private:
State state_1_;

View File

@ -510,6 +510,8 @@ template Handle<String> StringTable::LookupKey(LocalIsolate* isolate,
template Handle<String> StringTable::LookupKey(Isolate* isolate,
StringTableInsertionKey* key);
template Handle<String> StringTable::LookupKey(LocalIsolate* isolate,
StringTableInsertionKey* key);
StringTable::Data* StringTable::EnsureCapacity(PtrComprCageBase cage_base,
int additional_elements) {

View File

@ -10,6 +10,8 @@
#include "src/execution/thread-id.h"
#include "src/handles/handles-inl.h"
#include "src/heap/heap-inl.h"
#include "src/heap/local-factory-inl.h"
#include "src/heap/local-heap-inl.h"
#include "src/heap/memory-chunk.h"
#include "src/heap/read-only-heap.h"
#include "src/numbers/conversions.h"
@ -93,26 +95,32 @@ void MigrateExternalStringResource(Isolate* isolate, ExternalString from,
}
}
void MigrateExternalString(Isolate* isolate, String string,
String internalized) {
if (internalized.IsExternalOneByteString()) {
MigrateExternalStringResource(isolate, ExternalString::cast(string),
ExternalOneByteString::cast(internalized));
} else if (internalized.IsExternalTwoByteString()) {
MigrateExternalStringResource(isolate, ExternalString::cast(string),
ExternalTwoByteString::cast(internalized));
} else {
// If the external string is duped into an existing non-external
// internalized string, free its resource (it's about to be rewritten
// into a ThinString below).
isolate->heap()->FinalizeExternalString(string);
}
}
} // namespace
void String::MakeThin(Isolate* isolate, String internalized) {
template <typename IsolateT>
void String::MakeThin(IsolateT* isolate, String internalized) {
DisallowGarbageCollection no_gc;
DCHECK_NE(*this, internalized);
DCHECK(internalized.IsInternalizedString());
if (this->IsExternalString()) {
if (internalized.IsExternalOneByteString()) {
MigrateExternalStringResource(isolate, ExternalString::cast(*this),
ExternalOneByteString::cast(internalized));
} else if (internalized.IsExternalTwoByteString()) {
MigrateExternalStringResource(isolate, ExternalString::cast(*this),
ExternalTwoByteString::cast(internalized));
} else {
// If the external string is duped into an existing non-external
// internalized string, free its resource (it's about to be rewritten
// into a ThinString below).
isolate->heap()->FinalizeExternalString(*this);
}
MigrateExternalString(isolate->AsIsolate(), *this, internalized);
}
bool has_pointers = StringShape(*this).IsIndirect();
@ -131,9 +139,8 @@ void String::MakeThin(Isolate* isolate, String internalized) {
Address thin_end = thin.address() + ThinString::kSize;
int size_delta = old_size - ThinString::kSize;
if (size_delta != 0) {
Heap* heap = isolate->heap();
if (!heap->IsLargeObject(thin)) {
heap->CreateFillerObjectAt(
if (!Heap::IsLargeObject(thin)) {
isolate->heap()->CreateFillerObjectAt(
thin_end, size_delta,
has_pointers ? ClearRecordedSlots::kYes : ClearRecordedSlots::kNo);
} else {
@ -144,6 +151,9 @@ void String::MakeThin(Isolate* isolate, String internalized) {
}
}
template void String::MakeThin(Isolate* isolate, String internalized);
template void String::MakeThin(LocalIsolate* isolate, String internalized);
bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
// Disallow garbage collection to avoid possible GC vs string access deadlock.
DisallowGarbageCollection no_gc;
@ -633,6 +643,7 @@ std::unique_ptr<char[]> String::ToCString(AllowNullsFlag allow_nulls,
return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
}
// static
template <typename sinkchar>
void String::WriteToFlat(String source, sinkchar* sink, int from, int to) {
DCHECK(!SharedStringAccessGuardIfNeeded::IsNeeded(source));
@ -640,6 +651,7 @@ void String::WriteToFlat(String source, sinkchar* sink, int from, int to) {
SharedStringAccessGuardIfNeeded::NotNeeded());
}
// static
template <typename sinkchar>
void String::WriteToFlat(String source, sinkchar* sink, int from, int to,
const SharedStringAccessGuardIfNeeded& access_guard) {
@ -794,6 +806,13 @@ template Handle<FixedArray> String::CalculateLineEnds(LocalIsolate* isolate,
bool include_ending_line);
bool String::SlowEquals(String other) const {
DCHECK(!SharedStringAccessGuardIfNeeded::IsNeeded(*this));
DCHECK(!SharedStringAccessGuardIfNeeded::IsNeeded(other));
return SlowEquals(other, SharedStringAccessGuardIfNeeded::NotNeeded());
}
bool String::SlowEquals(
String other, const SharedStringAccessGuardIfNeeded& access_guard) const {
DisallowGarbageCollection no_gc;
// Fast check: negative check with lengths.
int len = length();
@ -833,16 +852,18 @@ bool String::SlowEquals(String other) const {
// We know the strings are both non-empty. Compare the first chars
// before we try to flatten the strings.
if (this->Get(0) != other.Get(0)) return false;
if (this->Get(0, access_guard) != other.Get(0, access_guard)) return false;
if (IsSeqOneByteString() && other.IsSeqOneByteString()) {
const uint8_t* str1 = SeqOneByteString::cast(*this).GetChars(no_gc);
const uint8_t* str2 = SeqOneByteString::cast(other).GetChars(no_gc);
const uint8_t* str1 =
SeqOneByteString::cast(*this).GetChars(no_gc, access_guard);
const uint8_t* str2 =
SeqOneByteString::cast(other).GetChars(no_gc, access_guard);
return CompareCharsEqual(str1, str2, len);
}
StringComparator comparator;
return comparator.Equals(*this, other);
return comparator.Equals(*this, other, access_guard);
}
// static
@ -1326,7 +1347,8 @@ bool String::HasOneBytePrefix(base::Vector<const char> str) {
namespace {
template <typename Char>
uint32_t HashString(String string, size_t start, int length, uint64_t seed) {
uint32_t HashString(String string, size_t start, int length, uint64_t seed,
const SharedStringAccessGuardIfNeeded& access_guard) {
DisallowGarbageCollection no_gc;
if (length > String::kMaxHashCalcLength) {
@ -1340,10 +1362,10 @@ uint32_t HashString(String string, size_t start, int length, uint64_t seed) {
DCHECK_EQ(0, start);
DCHECK(!string.IsFlat());
buffer.reset(new Char[length]);
String::WriteToFlat(string, buffer.get(), 0, length);
String::WriteToFlat(string, buffer.get(), 0, length, access_guard);
chars = buffer.get();
} else {
chars = string.GetChars<Char>(no_gc) + start;
chars = string.GetChars<Char>(no_gc, access_guard) + start;
}
return StringHasher::HashSequentialString<Char>(chars, length, seed);
@ -1352,6 +1374,11 @@ uint32_t HashString(String string, size_t start, int length, uint64_t seed) {
} // namespace
uint32_t String::ComputeAndSetHash() {
DCHECK(!SharedStringAccessGuardIfNeeded::IsNeeded(*this));
return ComputeAndSetHash(SharedStringAccessGuardIfNeeded::NotNeeded());
}
uint32_t String::ComputeAndSetHash(
const SharedStringAccessGuardIfNeeded& access_guard) {
DisallowGarbageCollection no_gc;
// Should only be called if hash code has not yet been computed.
DCHECK(!HasHashCode());
@ -1377,8 +1404,8 @@ uint32_t String::ComputeAndSetHash() {
}
uint32_t raw_hash_field =
string.IsOneByteRepresentation()
? HashString<uint8_t>(string, start, length(), seed)
: HashString<uint16_t>(string, start, length(), seed);
? HashString<uint8_t>(string, start, length(), seed, access_guard)
: HashString<uint16_t>(string, start, length(), seed, access_guard);
set_raw_hash_field(raw_hash_field);
// Check the hash code is there.

View File

@ -172,7 +172,8 @@ class String : public TorqueGeneratedString<String, Name> {
friend class IterableSubString;
};
void MakeThin(Isolate* isolate, String canonical);
template <typename IsolateT>
void MakeThin(IsolateT* isolate, String canonical);
template <typename Char>
V8_INLINE base::Vector<const Char> GetCharVector(
@ -570,6 +571,8 @@ class String : public TorqueGeneratedString<String, Name> {
// Slow case of String::Equals. This implementation works on any strings
// but it is most efficient on strings that are almost flat.
V8_EXPORT_PRIVATE bool SlowEquals(String other) const;
V8_EXPORT_PRIVATE bool SlowEquals(
String other, const SharedStringAccessGuardIfNeeded&) const;
V8_EXPORT_PRIVATE static bool SlowEquals(Isolate* isolate, Handle<String> one,
Handle<String> two);
@ -580,6 +583,8 @@ class String : public TorqueGeneratedString<String, Name> {
// Compute and set the hash code.
V8_EXPORT_PRIVATE uint32_t ComputeAndSetHash();
V8_EXPORT_PRIVATE uint32_t
ComputeAndSetHash(const SharedStringAccessGuardIfNeeded&);
TQ_OBJECT_CONSTRUCTORS(String)
};

View File

@ -207,7 +207,8 @@ Handle<SwissNameDictionary> SwissNameDictionary::Shrink(
// storing it somewhere in the main table or the meta table, for those
// SwissNameDictionaries that we know will be in-place rehashed, most notably
// those stored in the snapshot.
void SwissNameDictionary::Rehash(Isolate* isolate) {
template <typename IsolateT>
void SwissNameDictionary::Rehash(IsolateT* isolate) {
DisallowHeapAllocation no_gc;
struct Entry {
@ -307,6 +308,10 @@ template V8_EXPORT_PRIVATE Handle<SwissNameDictionary>
SwissNameDictionary::Rehash(Isolate* isolate, Handle<SwissNameDictionary> table,
int new_capacity);
template V8_EXPORT_PRIVATE void SwissNameDictionary::Rehash(
LocalIsolate* isolate);
template V8_EXPORT_PRIVATE void SwissNameDictionary::Rehash(Isolate* isolate);
constexpr int SwissNameDictionary::kInitialCapacity;
constexpr int SwissNameDictionary::kGroupWidth;

View File

@ -133,7 +133,8 @@ class V8_EXPORT_PRIVATE SwissNameDictionary : public HeapObject {
static Handle<SwissNameDictionary> Rehash(IsolateT* isolate,
Handle<SwissNameDictionary> table,
int new_capacity);
void Rehash(Isolate* isolate);
template <typename IsolateT>
void Rehash(IsolateT* isolate);
inline void SetHash(int hash);
inline int Hash();

View File

@ -4,12 +4,16 @@
#include "src/snapshot/code-serializer.h"
#include <memory>
#include "src/base/platform/platform.h"
#include "src/codegen/macro-assembler.h"
#include "src/common/globals.h"
#include "src/debug/debug.h"
#include "src/handles/persistent-handles.h"
#include "src/heap/heap-inl.h"
#include "src/heap/local-factory-inl.h"
#include "src/heap/parked-scope.h"
#include "src/logging/counters.h"
#include "src/logging/log.h"
#include "src/logging/runtime-call-stats-scope.h"
@ -277,18 +281,39 @@ class StressOffThreadDeserializeThread final : public base::Thread {
void Run() final {
LocalIsolate local_isolate(isolate_, ThreadKind::kBackground);
UnparkedScope unparked_scope(&local_isolate);
LocalHandleScope handle_scope(&local_isolate);
MaybeHandle<SharedFunctionInfo> local_maybe_result =
ObjectDeserializer::DeserializeSharedFunctionInfoOffThread(
&local_isolate, scd_, local_isolate.factory()->empty_string());
OffThreadObjectDeserializer::DeserializeSharedFunctionInfo(
&local_isolate, scd_, &scripts_);
maybe_result_ =
local_isolate.heap()->NewPersistentMaybeHandle(local_maybe_result);
persistent_handles_ = local_isolate.heap()->DetachPersistentHandles();
}
void Finalize(Isolate* isolate) {
Handle<WeakArrayList> list = isolate->factory()->script_list();
for (Handle<Script> script : scripts_) {
DCHECK(persistent_handles_->Contains(script.location()));
list = WeakArrayList::AddToEnd(isolate, list,
MaybeObjectHandle::Weak(script));
}
isolate->heap()->SetRootScriptList(*list);
Handle<SharedFunctionInfo> result;
if (maybe_result_.ToHandle(&result)) {
maybe_result_ = handle(*result, isolate);
}
}
private:
Isolate* isolate_;
const SerializedCodeData* scd_;
MaybeHandle<SharedFunctionInfo> maybe_result_;
std::vector<Handle<Script>> scripts_;
std::unique_ptr<PersistentHandles> persistent_handles_;
};
} // namespace
@ -315,12 +340,12 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
// Deserialize.
MaybeHandle<SharedFunctionInfo> maybe_result;
// TODO(leszeks): Add LocalHeap support to deserializer
if (false && FLAG_stress_background_compile) {
if (FLAG_stress_background_compile) {
StressOffThreadDeserializeThread thread(isolate, &scd);
CHECK(thread.Start());
thread.Join();
thread.Finalize(isolate);
maybe_result = thread.maybe_result();
// Fix-up result script source.

View File

@ -17,7 +17,8 @@ class Isolate;
// Deserializes the context-dependent object graph rooted at a given object.
// The ContextDeserializer is not expected to deserialize any code objects.
class V8_EXPORT_PRIVATE ContextDeserializer final : public Deserializer {
class V8_EXPORT_PRIVATE ContextDeserializer final
: public Deserializer<Isolate> {
public:
static MaybeHandle<Context> DeserializeContext(
Isolate* isolate, const SnapshotData* data, bool can_rehash,

View File

@ -14,10 +14,14 @@
#include "src/heap/heap-inl.h"
#include "src/heap/heap-write-barrier-inl.h"
#include "src/heap/heap-write-barrier.h"
#include "src/heap/heap.h"
#include "src/heap/local-heap-inl.h"
#include "src/heap/read-only-heap.h"
#include "src/interpreter/interpreter.h"
#include "src/logging/local-logger.h"
#include "src/logging/log.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/backing-store.h"
#include "src/objects/cell-inl.h"
#include "src/objects/embedder-data-array-inl.h"
#include "src/objects/hash-table.h"
@ -149,9 +153,10 @@ class SlotAccessorForRootSlots {
// A SlotAccessor for creating a Handle, which saves a Handle allocation when
// a Handle already exists.
template <typename IsolateT>
class SlotAccessorForHandle {
public:
SlotAccessorForHandle(Handle<HeapObject>* handle, Isolate* isolate)
SlotAccessorForHandle(Handle<HeapObject>* handle, IsolateT* isolate)
: handle_(handle), isolate_(isolate) {}
MaybeObjectSlot slot() const { UNREACHABLE(); }
@ -185,36 +190,62 @@ class SlotAccessorForHandle {
private:
Handle<HeapObject>* handle_;
Isolate* isolate_;
IsolateT* isolate_;
};
template <typename IsolateT>
template <typename TSlot>
int Deserializer::WriteAddress(TSlot dest, Address value) {
int Deserializer<IsolateT>::WriteAddress(TSlot dest, Address value) {
DCHECK(!next_reference_is_weak_);
memcpy(dest.ToVoidPtr(), &value, kSystemPointerSize);
STATIC_ASSERT(IsAligned(kSystemPointerSize, TSlot::kSlotDataSize));
return (kSystemPointerSize / TSlot::kSlotDataSize);
}
template <typename IsolateT>
template <typename TSlot>
int Deserializer::WriteExternalPointer(TSlot dest, Address value,
ExternalPointerTag tag) {
int Deserializer<IsolateT>::WriteExternalPointer(TSlot dest, Address value,
ExternalPointerTag tag) {
DCHECK(!next_reference_is_weak_);
InitExternalPointerField(dest.address(), isolate(), value, tag);
InitExternalPointerField(dest.address(), main_thread_isolate(), value, tag);
STATIC_ASSERT(IsAligned(kExternalPointerSize, TSlot::kSlotDataSize));
return (kExternalPointerSize / TSlot::kSlotDataSize);
}
Deserializer::Deserializer(Isolate* isolate, base::Vector<const byte> payload,
uint32_t magic_number, bool deserializing_user_code,
bool can_rehash)
namespace {
#ifdef DEBUG
int GetNumApiReferences(Isolate* isolate) {
int num_api_references = 0;
// The read-only deserializer is run by read-only heap set-up before the
// heap is fully set up. External reference table relies on a few parts of
// this set-up (like old-space), so it may be uninitialized at this point.
if (isolate->isolate_data()->external_reference_table()->is_initialized()) {
// Count the number of external references registered through the API.
if (isolate->api_external_references() != nullptr) {
while (isolate->api_external_references()[num_api_references] != 0) {
num_api_references++;
}
}
}
return num_api_references;
}
int GetNumApiReferences(LocalIsolate* isolate) { return 0; }
#endif
} // namespace
template <typename IsolateT>
Deserializer<IsolateT>::Deserializer(IsolateT* isolate,
base::Vector<const byte> payload,
uint32_t magic_number,
bool deserializing_user_code,
bool can_rehash)
: isolate_(isolate),
source_(payload),
magic_number_(magic_number),
deserializing_user_code_(deserializing_user_code),
can_rehash_(can_rehash) {
DCHECK_NOT_NULL(isolate);
isolate_->RegisterDeserializerStarted();
isolate->RegisterDeserializerStarted();
// We start the indices here at 1, so that we can distinguish between an
// actual index and a nullptr (serialized as kNullRefSentinel) in a
@ -223,30 +254,21 @@ Deserializer::Deserializer(Isolate* isolate, base::Vector<const byte> payload,
backing_stores_.push_back({});
#ifdef DEBUG
num_api_references_ = 0;
// The read-only deserializer is run by read-only heap set-up before the
// heap is fully set up. External reference table relies on a few parts of
// this set-up (like old-space), so it may be uninitialized at this point.
if (isolate->isolate_data()->external_reference_table()->is_initialized()) {
// Count the number of external references registered through the API.
if (isolate->api_external_references() != nullptr) {
while (isolate->api_external_references()[num_api_references_] != 0) {
num_api_references_++;
}
}
}
num_api_references_ = GetNumApiReferences(isolate);
#endif // DEBUG
CHECK_EQ(magic_number_, SerializedData::kMagicNumber);
}
void Deserializer::Rehash() {
template <typename IsolateT>
void Deserializer<IsolateT>::Rehash() {
DCHECK(can_rehash() || deserializing_user_code());
for (Handle<HeapObject> item : to_rehash_) {
item->RehashBasedOnMap(isolate());
}
}
Deserializer::~Deserializer() {
template <typename IsolateT>
Deserializer<IsolateT>::~Deserializer() {
#ifdef DEBUG
// Do not perform checks if we aborted deserialization.
if (source_.position() == 0) return;
@ -261,24 +283,30 @@ Deserializer::~Deserializer() {
// This is called on the roots. It is the driver of the deserialization
// process. It is also called on the body of each function.
void Deserializer::VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) {
template <typename IsolateT>
void Deserializer<IsolateT>::VisitRootPointers(Root root,
const char* description,
FullObjectSlot start,
FullObjectSlot end) {
ReadData(FullMaybeObjectSlot(start), FullMaybeObjectSlot(end));
}
void Deserializer::Synchronize(VisitorSynchronization::SyncTag tag) {
template <typename IsolateT>
void Deserializer<IsolateT>::Synchronize(VisitorSynchronization::SyncTag tag) {
static const byte expected = kSynchronize;
CHECK_EQ(expected, source_.Get());
}
void Deserializer::DeserializeDeferredObjects() {
template <typename IsolateT>
void Deserializer<IsolateT>::DeserializeDeferredObjects() {
for (int code = source_.Get(); code != kSynchronize; code = source_.Get()) {
SnapshotSpace space = NewObject::Decode(code);
ReadObject(space);
}
}
void Deserializer::LogNewMapEvents() {
template <typename IsolateT>
void Deserializer<IsolateT>::LogNewMapEvents() {
DisallowGarbageCollection no_gc;
for (Handle<Map> map : new_maps_) {
DCHECK(FLAG_log_maps);
@ -287,7 +315,8 @@ void Deserializer::LogNewMapEvents() {
}
}
void Deserializer::WeakenDescriptorArrays() {
template <typename IsolateT>
void Deserializer<IsolateT>::WeakenDescriptorArrays() {
DisallowGarbageCollection no_gc;
for (Handle<DescriptorArray> descriptor_array : new_descriptor_arrays_) {
DCHECK(descriptor_array->IsStrongDescriptorArray());
@ -297,36 +326,66 @@ void Deserializer::WeakenDescriptorArrays() {
}
}
void Deserializer::LogScriptEvents(Script script) {
template <typename IsolateT>
void Deserializer<IsolateT>::LogScriptEvents(Script script) {
DisallowGarbageCollection no_gc;
LOG(isolate(),
ScriptEvent(Logger::ScriptEventType::kDeserialize, script.id()));
LOG(isolate(), ScriptDetails(script));
}
StringTableInsertionKey::StringTableInsertionKey(Handle<String> string)
: StringTableKey(ComputeRawHashField(*string), string->length()),
namespace {
template <typename IsolateT>
uint32_t ComputeRawHashField(IsolateT* isolate, String string) {
// Make sure raw_hash_field() is computed.
string.EnsureHash(SharedStringAccessGuardIfNeeded(isolate));
return string.raw_hash_field();
}
} // namespace
StringTableInsertionKey::StringTableInsertionKey(Isolate* isolate,
Handle<String> string)
: StringTableKey(ComputeRawHashField(isolate, *string), string->length()),
string_(string) {
DCHECK(string->IsInternalizedString());
}
bool StringTableInsertionKey::IsMatch(Isolate* isolate, String string) {
StringTableInsertionKey::StringTableInsertionKey(LocalIsolate* isolate,
Handle<String> string)
: StringTableKey(ComputeRawHashField(isolate, *string), string->length()),
string_(string) {
DCHECK(string->IsInternalizedString());
}
template <typename IsolateT>
bool StringTableInsertionKey::IsMatch(IsolateT* isolate, String string) {
// We want to compare the content of two strings here.
return string_->SlowEquals(string);
return string_->SlowEquals(string, SharedStringAccessGuardIfNeeded(isolate));
}
template bool StringTableInsertionKey::IsMatch(Isolate* isolate, String string);
template bool StringTableInsertionKey::IsMatch(LocalIsolate* isolate,
String string);
namespace {
void PostProcessExternalString(Handle<ExternalString> string,
Isolate* isolate) {
uint32_t index = string->GetResourceRefForDeserialization();
Address address =
static_cast<Address>(isolate->api_external_references()[index]);
string->AllocateExternalPointerEntries(isolate);
string->set_address_as_resource(isolate, address);
isolate->heap()->UpdateExternalString(*string, 0,
string->ExternalPayloadSize());
isolate->heap()->RegisterExternalString(*string);
}
Handle<String> StringTableInsertionKey::AsHandle(Isolate* isolate) {
return string_;
}
} // namespace
uint32_t StringTableInsertionKey::ComputeRawHashField(String string) {
// Make sure raw_hash_field() is computed.
string.EnsureHash();
return string.raw_hash_field();
}
void Deserializer::PostProcessNewObject(Handle<Map> map, Handle<HeapObject> obj,
SnapshotSpace space) {
template <typename IsolateT>
void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map,
Handle<HeapObject> obj,
SnapshotSpace space) {
DCHECK_EQ(*map, obj->map());
DisallowGarbageCollection no_gc;
InstanceType instance_type = map->instance_type();
@ -349,15 +408,20 @@ void Deserializer::PostProcessNewObject(Handle<Map> map, Handle<HeapObject> obj,
if (deserializing_user_code()) {
if (InstanceTypeChecker::IsInternalizedString(instance_type)) {
// Canonicalize the internalized string. If it already exists in the
// string table, set it to forward to the existing one.
// string table, patch the deserialized string handle to point to the
// existing one.
// TODO(leszeks): This handle patching is ugly, consider adding an
// explicit internalized string bytecode.
Handle<String> string = Handle<String>::cast(obj);
StringTableInsertionKey key(string);
StringTableInsertionKey key(isolate(), string);
Handle<String> result =
isolate()->string_table()->LookupKey(isolate(), &key);
if (*result != *string) {
string->MakeThin(isolate(), *result);
// Invalidate the original string.
isolate()->heap()->CreateFillerObjectAt(
string->address(), string->Size(), ClearRecordedSlots::kNo);
// Mutate the given object handle so that the backreference entry is
// also updated.
obj.PatchValue(*result);
@ -388,8 +452,8 @@ void Deserializer::PostProcessNewObject(Handle<Map> map, Handle<HeapObject> obj,
} else if (V8_EXTERNAL_CODE_SPACE_BOOL &&
InstanceTypeChecker::IsCodeDataContainer(instance_type)) {
auto code_data_container = Handle<CodeDataContainer>::cast(obj);
code_data_container->AllocateExternalPointerEntries(isolate());
code_data_container->UpdateCodeEntryPoint(isolate(),
code_data_container->AllocateExternalPointerEntries(main_thread_isolate());
code_data_container->UpdateCodeEntryPoint(main_thread_isolate(),
code_data_container->code());
} else if (InstanceTypeChecker::IsMap(instance_type)) {
if (FLAG_log_maps) {
@ -406,15 +470,8 @@ void Deserializer::PostProcessNewObject(Handle<Map> map, Handle<HeapObject> obj,
call_handler_infos_.push_back(Handle<CallHandlerInfo>::cast(obj));
#endif
} else if (InstanceTypeChecker::IsExternalString(instance_type)) {
Handle<ExternalString> string = Handle<ExternalString>::cast(obj);
uint32_t index = string->GetResourceRefForDeserialization();
Address address =
static_cast<Address>(isolate()->api_external_references()[index]);
string->AllocateExternalPointerEntries(isolate());
string->set_address_as_resource(isolate(), address);
isolate()->heap()->UpdateExternalString(*string, 0,
string->ExternalPayloadSize());
isolate()->heap()->RegisterExternalString(*string);
PostProcessExternalString(Handle<ExternalString>::cast(obj),
main_thread_isolate());
} else if (InstanceTypeChecker::IsJSDataView(instance_type)) {
Handle<JSDataView> data_view = Handle<JSDataView>::cast(obj);
JSArrayBuffer buffer = JSArrayBuffer::cast(data_view->buffer());
@ -426,18 +483,18 @@ void Deserializer::PostProcessNewObject(Handle<Map> map, Handle<HeapObject> obj,
// a numbered reference to an already deserialized backing store.
backing_store = backing_stores_[store_index]->buffer_start();
}
data_view->AllocateExternalPointerEntries(isolate());
data_view->AllocateExternalPointerEntries(main_thread_isolate());
data_view->set_data_pointer(
isolate(),
main_thread_isolate(),
reinterpret_cast<uint8_t*>(backing_store) + data_view->byte_offset());
} else if (InstanceTypeChecker::IsJSTypedArray(instance_type)) {
Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(obj);
// Fixup typed array pointers.
if (typed_array->is_on_heap()) {
Address raw_external_pointer = typed_array->external_pointer_raw();
typed_array->AllocateExternalPointerEntries(isolate());
typed_array->AllocateExternalPointerEntries(main_thread_isolate());
typed_array->SetOnHeapDataPtr(
isolate(), HeapObject::cast(typed_array->base_pointer()),
main_thread_isolate(), HeapObject::cast(typed_array->base_pointer()),
raw_external_pointer);
} else {
// Serializer writes backing store ref as a DataPtr() value.
@ -447,8 +504,8 @@ void Deserializer::PostProcessNewObject(Handle<Map> map, Handle<HeapObject> obj,
auto start = backing_store
? reinterpret_cast<byte*>(backing_store->buffer_start())
: nullptr;
typed_array->AllocateExternalPointerEntries(isolate());
typed_array->SetOffHeapDataPtr(isolate(), start,
typed_array->AllocateExternalPointerEntries(main_thread_isolate());
typed_array->SetOffHeapDataPtr(main_thread_isolate(), start,
typed_array->byte_offset());
}
} else if (InstanceTypeChecker::IsJSArrayBuffer(instance_type)) {
@ -457,8 +514,8 @@ void Deserializer::PostProcessNewObject(Handle<Map> map, Handle<HeapObject> obj,
if (buffer->GetBackingStoreRefForDeserialization() != kNullRefSentinel) {
new_off_heap_array_buffers_.push_back(buffer);
} else {
buffer->AllocateExternalPointerEntries(isolate());
buffer->set_backing_store(isolate(), nullptr);
buffer->AllocateExternalPointerEntries(main_thread_isolate());
buffer->set_backing_store(main_thread_isolate(), nullptr);
}
} else if (InstanceTypeChecker::IsBytecodeArray(instance_type)) {
// TODO(mythria): Remove these once we store the default values for these
@ -471,7 +528,7 @@ void Deserializer::PostProcessNewObject(Handle<Map> map, Handle<HeapObject> obj,
new_descriptor_arrays_.push_back(descriptors);
} else if (InstanceTypeChecker::IsNativeContext(instance_type)) {
Handle<NativeContext> context = Handle<NativeContext>::cast(obj);
context->AllocateExternalPointerEntries(isolate());
context->AllocateExternalPointerEntries(main_thread_isolate());
}
// Check alignment.
@ -479,7 +536,8 @@ void Deserializer::PostProcessNewObject(Handle<Map> map, Handle<HeapObject> obj,
HeapObject::RequiredAlignment(*map)));
}
HeapObjectReferenceType Deserializer::GetAndResetNextReferenceType() {
template <typename IsolateT>
HeapObjectReferenceType Deserializer<IsolateT>::GetAndResetNextReferenceType() {
HeapObjectReferenceType type = next_reference_is_weak_
? HeapObjectReferenceType::WEAK
: HeapObjectReferenceType::STRONG;
@ -487,7 +545,8 @@ HeapObjectReferenceType Deserializer::GetAndResetNextReferenceType() {
return type;
}
Handle<HeapObject> Deserializer::GetBackReferencedObject() {
template <typename IsolateT>
Handle<HeapObject> Deserializer<IsolateT>::GetBackReferencedObject() {
Handle<HeapObject> obj = back_refs_[source_.GetInt()];
// We don't allow ThinStrings in backreferences -- if internalization produces
@ -499,15 +558,17 @@ Handle<HeapObject> Deserializer::GetBackReferencedObject() {
return obj;
}
Handle<HeapObject> Deserializer::ReadObject() {
template <typename IsolateT>
Handle<HeapObject> Deserializer<IsolateT>::ReadObject() {
Handle<HeapObject> ret;
CHECK_EQ(ReadSingleBytecodeData(source_.Get(),
SlotAccessorForHandle(&ret, isolate())),
CHECK_EQ(ReadSingleBytecodeData(
source_.Get(), SlotAccessorForHandle<IsolateT>(&ret, isolate())),
1);
return ret;
}
Handle<HeapObject> Deserializer::ReadObject(SnapshotSpace space) {
template <typename IsolateT>
Handle<HeapObject> Deserializer<IsolateT>::ReadObject(SnapshotSpace space) {
const int size_in_tagged = source_.GetInt();
const int size_in_bytes = size_in_tagged * kTaggedSize;
@ -555,8 +616,8 @@ Handle<HeapObject> Deserializer::ReadObject(SnapshotSpace space) {
JSObject js_obj = JSObject::cast(raw_obj);
for (int i = 0; i < js_obj.GetEmbedderFieldCount(); ++i) {
void* pointer;
CHECK(EmbedderDataSlot(js_obj, i).ToAlignedPointerSafe(isolate(),
&pointer));
CHECK(EmbedderDataSlot(js_obj, i).ToAlignedPointerSafe(
main_thread_isolate(), &pointer));
CHECK_NULL(pointer);
}
} else if (raw_obj.IsEmbedderDataArray()) {
@ -565,7 +626,7 @@ Handle<HeapObject> Deserializer::ReadObject(SnapshotSpace space) {
EmbedderDataSlot end(array, array.length());
for (EmbedderDataSlot slot = start; slot < end; ++slot) {
void* pointer;
CHECK(slot.ToAlignedPointerSafe(isolate(), &pointer));
CHECK(slot.ToAlignedPointerSafe(main_thread_isolate(), &pointer));
CHECK_NULL(pointer);
}
}
@ -577,8 +638,6 @@ Handle<HeapObject> Deserializer::ReadObject(SnapshotSpace space) {
ReadData(obj, 1, size_in_tagged);
PostProcessNewObject(map, obj, space);
DCHECK(!obj->IsThinString(isolate()));
#ifdef DEBUG
if (obj->IsCode()) {
DCHECK(space == SnapshotSpace::kCode ||
@ -591,7 +650,8 @@ Handle<HeapObject> Deserializer::ReadObject(SnapshotSpace space) {
return obj;
}
Handle<HeapObject> Deserializer::ReadMetaMap() {
template <typename IsolateT>
Handle<HeapObject> Deserializer<IsolateT>::ReadMetaMap() {
const SnapshotSpace space = SnapshotSpace::kReadOnlyHeap;
const int size_in_bytes = Map::kSize;
const int size_in_tagged = size_in_bytes / kTaggedSize;
@ -613,12 +673,20 @@ Handle<HeapObject> Deserializer::ReadMetaMap() {
return obj;
}
class Deserializer::RelocInfoVisitor {
class DeserializerRelocInfoVisitor {
public:
RelocInfoVisitor(Deserializer* deserializer,
const std::vector<Handle<HeapObject>>* objects)
DeserializerRelocInfoVisitor(Deserializer<Isolate>* deserializer,
const std::vector<Handle<HeapObject>>* objects)
: deserializer_(deserializer), objects_(objects), current_object_(0) {}
~RelocInfoVisitor() { DCHECK_EQ(current_object_, objects_->size()); }
DeserializerRelocInfoVisitor(Deserializer<LocalIsolate>* deserializer,
const std::vector<Handle<HeapObject>>* objects) {
UNREACHABLE();
}
~DeserializerRelocInfoVisitor() {
DCHECK_EQ(current_object_, objects_->size());
}
void VisitCodeTarget(Code host, RelocInfo* rinfo);
void VisitEmbeddedPointer(Code host, RelocInfo* rinfo);
@ -631,34 +699,34 @@ class Deserializer::RelocInfoVisitor {
Isolate* isolate() { return deserializer_->isolate(); }
SnapshotByteSource& source() { return deserializer_->source_; }
Deserializer* deserializer_;
Deserializer<Isolate>* deserializer_;
const std::vector<Handle<HeapObject>>* objects_;
int current_object_;
};
void Deserializer::RelocInfoVisitor::VisitCodeTarget(Code host,
RelocInfo* rinfo) {
void DeserializerRelocInfoVisitor::VisitCodeTarget(Code host,
RelocInfo* rinfo) {
HeapObject object = *objects_->at(current_object_++);
rinfo->set_target_address(Code::cast(object).raw_instruction_start());
}
void Deserializer::RelocInfoVisitor::VisitEmbeddedPointer(Code host,
RelocInfo* rinfo) {
void DeserializerRelocInfoVisitor::VisitEmbeddedPointer(Code host,
RelocInfo* rinfo) {
HeapObject object = *objects_->at(current_object_++);
// Embedded object reference must be a strong one.
rinfo->set_target_object(isolate()->heap(), object);
}
void Deserializer::RelocInfoVisitor::VisitRuntimeEntry(Code host,
RelocInfo* rinfo) {
void DeserializerRelocInfoVisitor::VisitRuntimeEntry(Code host,
RelocInfo* rinfo) {
// We no longer serialize code that contains runtime entries.
UNREACHABLE();
}
void Deserializer::RelocInfoVisitor::VisitExternalReference(Code host,
RelocInfo* rinfo) {
void DeserializerRelocInfoVisitor::VisitExternalReference(Code host,
RelocInfo* rinfo) {
byte data = source().Get();
CHECK_EQ(data, kExternalReference);
CHECK_EQ(data, Deserializer<Isolate>::kExternalReference);
Address address = deserializer_->ReadExternalReferenceCase();
@ -671,10 +739,10 @@ void Deserializer::RelocInfoVisitor::VisitExternalReference(Code host,
}
}
void Deserializer::RelocInfoVisitor::VisitInternalReference(Code host,
RelocInfo* rinfo) {
void DeserializerRelocInfoVisitor::VisitInternalReference(Code host,
RelocInfo* rinfo) {
byte data = source().Get();
CHECK_EQ(data, kInternalReference);
CHECK_EQ(data, Deserializer<Isolate>::kInternalReference);
// Internal reference target is encoded as an offset from code entry.
int target_offset = source().GetInt();
@ -689,10 +757,10 @@ void Deserializer::RelocInfoVisitor::VisitInternalReference(Code host,
rinfo->pc(), target, rinfo->rmode());
}
void Deserializer::RelocInfoVisitor::VisitOffHeapTarget(Code host,
RelocInfo* rinfo) {
void DeserializerRelocInfoVisitor::VisitOffHeapTarget(Code host,
RelocInfo* rinfo) {
byte data = source().Get();
CHECK_EQ(data, kOffHeapTarget);
CHECK_EQ(data, Deserializer<Isolate>::kOffHeapTarget);
Builtin builtin = Builtins::FromInt(source().GetInt());
@ -711,9 +779,10 @@ void Deserializer::RelocInfoVisitor::VisitOffHeapTarget(Code host,
}
}
template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer::ReadRepeatedObject(SlotAccessor slot_accessor,
int repeat_count) {
int Deserializer<IsolateT>::ReadRepeatedObject(SlotAccessor slot_accessor,
int repeat_count) {
CHECK_LE(2, repeat_count);
Handle<HeapObject> heap_object = ReadObject();
@ -765,8 +834,10 @@ constexpr byte VerifyBytecodeCount(byte bytecode) {
: case SpaceEncoder<bytecode>::Encode(SnapshotSpace::kMap) \
: case SpaceEncoder<bytecode>::Encode(SnapshotSpace::kReadOnlyHeap)
void Deserializer::ReadData(Handle<HeapObject> object, int start_slot_index,
int end_slot_index) {
template <typename IsolateT>
void Deserializer<IsolateT>::ReadData(Handle<HeapObject> object,
int start_slot_index,
int end_slot_index) {
int current = start_slot_index;
while (current < end_slot_index) {
byte data = source_.Get();
@ -776,8 +847,9 @@ void Deserializer::ReadData(Handle<HeapObject> object, int start_slot_index,
CHECK_EQ(current, end_slot_index);
}
void Deserializer::ReadData(FullMaybeObjectSlot start,
FullMaybeObjectSlot end) {
template <typename IsolateT>
void Deserializer<IsolateT>::ReadData(FullMaybeObjectSlot start,
FullMaybeObjectSlot end) {
FullMaybeObjectSlot current = start;
while (current < end) {
byte data = source_.Get();
@ -786,9 +858,10 @@ void Deserializer::ReadData(FullMaybeObjectSlot start,
CHECK_EQ(current, end);
}
template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer::ReadSingleBytecodeData(byte data,
SlotAccessor slot_accessor) {
int Deserializer<IsolateT>::ReadSingleBytecodeData(byte data,
SlotAccessor slot_accessor) {
using TSlot = decltype(slot_accessor.slot());
switch (data) {
@ -841,8 +914,8 @@ int Deserializer::ReadSingleBytecodeData(byte data,
int cache_index = source_.GetInt();
// TODO(leszeks): Could we use the address of the startup_object_cache
// entry as a Handle backing?
HeapObject heap_object =
HeapObject::cast(isolate()->startup_object_cache()->at(cache_index));
HeapObject heap_object = HeapObject::cast(
main_thread_isolate()->startup_object_cache()->at(cache_index));
return slot_accessor.Write(heap_object, GetAndResetNextReferenceType());
}
@ -989,7 +1062,7 @@ int Deserializer::ReadSingleBytecodeData(byte data,
DisallowGarbageCollection no_gc;
Code code = Code::cast(*slot_accessor.object());
RelocInfoVisitor visitor(this, &preserialized_objects);
DeserializerRelocInfoVisitor visitor(this, &preserialized_objects);
for (RelocIterator it(code, Code::BodyDescriptor::kRelocModeMask);
!it.done(); it.next()) {
it.rinfo()->Visit(&visitor);
@ -1007,11 +1080,10 @@ int Deserializer::ReadSingleBytecodeData(byte data,
}
case kOffHeapBackingStore: {
AlwaysAllocateScope scope(isolate()->heap());
int byte_length = source_.GetInt();
std::unique_ptr<BackingStore> backing_store =
BackingStore::Allocate(isolate(), byte_length, SharedFlag::kNotShared,
InitializedFlag::kUninitialized);
std::unique_ptr<BackingStore> backing_store = BackingStore::Allocate(
main_thread_isolate(), byte_length, SharedFlag::kNotShared,
InitializedFlag::kUninitialized);
CHECK_NOT_NULL(backing_store);
source_.CopyRaw(backing_store->buffer_start(), byte_length);
backing_stores_.push_back(std::move(backing_store));
@ -1022,11 +1094,11 @@ int Deserializer::ReadSingleBytecodeData(byte data,
case kApiReference: {
uint32_t reference_id = static_cast<uint32_t>(source_.GetInt());
Address address;
if (isolate()->api_external_references()) {
if (main_thread_isolate()->api_external_references()) {
DCHECK_WITH_MSG(reference_id < num_api_references_,
"too few external references provided through the API");
address = static_cast<Address>(
isolate()->api_external_references()[reference_id]);
main_thread_isolate()->api_external_references()[reference_id]);
} else {
address = reinterpret_cast<Address>(NoExternalReferencesCallback);
}
@ -1117,9 +1189,11 @@ int Deserializer::ReadSingleBytecodeData(byte data,
#undef CASE_R2
#undef CASE_R1
Address Deserializer::ReadExternalReferenceCase() {
template <typename IsolateT>
Address Deserializer<IsolateT>::ReadExternalReferenceCase() {
uint32_t reference_id = static_cast<uint32_t>(source_.GetInt());
return isolate()->external_reference_table()->address(reference_id);
return main_thread_isolate()->external_reference_table()->address(
reference_id);
}
namespace {
@ -1137,8 +1211,9 @@ AllocationType SpaceToType(SnapshotSpace space) {
}
} // namespace
HeapObject Deserializer::Allocate(SnapshotSpace space, int size,
AllocationAlignment alignment) {
template <typename IsolateT>
HeapObject Deserializer<IsolateT>::Allocate(SnapshotSpace space, int size,
AllocationAlignment alignment) {
#ifdef DEBUG
if (!previous_allocation_obj_.is_null()) {
// Make sure that the previous object is initialized sufficiently to
@ -1148,8 +1223,8 @@ HeapObject Deserializer::Allocate(SnapshotSpace space, int size,
}
#endif
HeapObject obj = isolate()->heap()->AllocateRawWith<Heap::kRetryOrFail>(
size, SpaceToType(space), AllocationOrigin::kRuntime, alignment);
HeapObject obj = HeapObject::FromAddress(isolate()->heap()->AllocateRawOrFail(
size, SpaceToType(space), AllocationOrigin::kRuntime, alignment));
#ifdef DEBUG
previous_allocation_obj_ = handle(obj, isolate());
@ -1159,5 +1234,9 @@ HeapObject Deserializer::Allocate(SnapshotSpace space, int size,
return obj;
}
template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Deserializer<Isolate>;
template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
Deserializer<LocalIsolate>;
} // namespace internal
} // namespace v8

View File

@ -8,7 +8,10 @@
#include <utility>
#include <vector>
#include "src/base/macros.h"
#include "src/base/optional.h"
#include "src/common/globals.h"
#include "src/execution/local-isolate.h"
#include "src/objects/allocation-site.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/backing-store.h"
@ -39,7 +42,8 @@ class Object;
#endif
// A Deserializer reads a snapshot and reconstructs the Object graph it defines.
class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
template <typename IsolateT>
class Deserializer : public SerializerDeserializer {
public:
~Deserializer() override;
Deserializer(const Deserializer&) = delete;
@ -49,7 +53,7 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
protected:
// Create a deserializer from a snapshot byte source.
Deserializer(Isolate* isolate, base::Vector<const byte> payload,
Deserializer(IsolateT* isolate, base::Vector<const byte> payload,
uint32_t magic_number, bool deserializing_user_code,
bool can_rehash);
@ -79,7 +83,9 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
CHECK_EQ(new_off_heap_array_buffers().size(), 0);
}
Isolate* isolate() const { return isolate_; }
IsolateT* isolate() const { return isolate_; }
Isolate* main_thread_isolate() const { return isolate_->AsIsolate(); }
SnapshotByteSource* source() { return &source_; }
const std::vector<Handle<AllocationSite>>& new_allocation_sites() const {
@ -120,7 +126,7 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
Handle<HeapObject> ReadObject();
private:
class RelocInfoVisitor;
friend class DeserializerRelocInfoVisitor;
// A circular queue of hot objects. This is added to in the same order as in
// Serializer::HotObjectsList, but this stores the objects as a vector of
// existing handles. This allows us to add Handles to the queue without having
@ -196,7 +202,7 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
AllocationAlignment alignment);
// Cached current isolate.
Isolate* isolate_;
IsolateT* isolate_;
// Objects from the attached object descriptions in the serialized user code.
std::vector<Handle<HeapObject>> attached_objects_;
@ -253,19 +259,27 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
#endif // DEBUG
};
extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
Deserializer<Isolate>;
extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
Deserializer<LocalIsolate>;
// Used to insert a deserialized internalized string into the string table.
class StringTableInsertionKey final : public StringTableKey {
public:
explicit StringTableInsertionKey(Handle<String> string);
explicit StringTableInsertionKey(Isolate* isolate, Handle<String> string);
explicit StringTableInsertionKey(LocalIsolate* isolate,
Handle<String> string);
bool IsMatch(Isolate* isolate, String string);
template <typename IsolateT>
bool IsMatch(IsolateT* isolate, String string);
V8_WARN_UNUSED_RESULT Handle<String> AsHandle(Isolate* isolate);
V8_WARN_UNUSED_RESULT Handle<String> AsHandle(LocalIsolate* isolate);
template <typename IsolateT>
V8_WARN_UNUSED_RESULT Handle<String> AsHandle(IsolateT* isolate) {
return string_;
}
private:
uint32_t ComputeRawHashField(String string);
Handle<String> string_;
DISALLOW_GARBAGE_COLLECTION(no_gc)
};

View File

@ -7,6 +7,7 @@
#include "src/codegen/assembler-inl.h"
#include "src/execution/isolate.h"
#include "src/heap/heap-inl.h"
#include "src/heap/local-factory-inl.h"
#include "src/objects/allocation-site-inl.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/objects.h"
@ -34,14 +35,6 @@ ObjectDeserializer::DeserializeSharedFunctionInfo(
: MaybeHandle<SharedFunctionInfo>();
}
MaybeHandle<SharedFunctionInfo>
ObjectDeserializer::DeserializeSharedFunctionInfoOffThread(
LocalIsolate* isolate, const SerializedCodeData* data,
Handle<String> source) {
// TODO(leszeks): Add LocalHeap support to deserializer
UNREACHABLE();
}
MaybeHandle<HeapObject> ObjectDeserializer::Deserialize() {
DCHECK(deserializing_user_code());
HandleScope scope(isolate());
@ -102,5 +95,56 @@ void ObjectDeserializer::LinkAllocationSites() {
}
}
OffThreadObjectDeserializer::OffThreadObjectDeserializer(
LocalIsolate* isolate, const SerializedCodeData* data)
: Deserializer(isolate, data->Payload(), data->GetMagicNumber(), true,
false) {}
MaybeHandle<SharedFunctionInfo>
OffThreadObjectDeserializer::DeserializeSharedFunctionInfo(
LocalIsolate* isolate, const SerializedCodeData* data,
std::vector<Handle<Script>>* deserialized_scripts) {
OffThreadObjectDeserializer d(isolate, data);
// Attach the empty string as the source.
d.AddAttachedObject(isolate->factory()->empty_string());
Handle<HeapObject> result;
if (!d.Deserialize(deserialized_scripts).ToHandle(&result)) {
return MaybeHandle<SharedFunctionInfo>();
}
return Handle<SharedFunctionInfo>::cast(result);
}
MaybeHandle<HeapObject> OffThreadObjectDeserializer::Deserialize(
std::vector<Handle<Script>>* deserialized_scripts) {
DCHECK(deserializing_user_code());
LocalHandleScope scope(isolate());
Handle<HeapObject> result;
{
result = ReadObject();
DeserializeDeferredObjects();
CHECK(new_code_objects().empty());
CHECK(new_allocation_sites().empty());
CHECK(new_maps().empty());
WeakenDescriptorArrays();
}
Rehash();
CHECK(new_off_heap_array_buffers().empty());
// TODO(leszeks): Figure out a better way of dealing with scripts.
CHECK_EQ(new_scripts().size(), 1);
for (Handle<Script> script : new_scripts()) {
// Assign a new script id to avoid collision.
script->set_id(isolate()->GetNextScriptId());
LogScriptEvents(*script);
deserialized_scripts->push_back(
isolate()->heap()->NewPersistentHandle(script));
}
return scope.CloseAndEscape(result);
}
} // namespace internal
} // namespace v8

View File

@ -14,13 +14,10 @@ class SerializedCodeData;
class SharedFunctionInfo;
// Deserializes the object graph rooted at a given object.
class ObjectDeserializer final : public Deserializer {
class ObjectDeserializer final : public Deserializer<Isolate> {
public:
static MaybeHandle<SharedFunctionInfo> DeserializeSharedFunctionInfo(
Isolate* isolate, const SerializedCodeData* data, Handle<String> source);
static MaybeHandle<SharedFunctionInfo> DeserializeSharedFunctionInfoOffThread(
LocalIsolate* isolate, const SerializedCodeData* data,
Handle<String> source);
private:
explicit ObjectDeserializer(Isolate* isolate, const SerializedCodeData* data);
@ -32,6 +29,22 @@ class ObjectDeserializer final : public Deserializer {
void CommitPostProcessedObjects();
};
// Deserializes the object graph rooted at a given object.
class OffThreadObjectDeserializer final : public Deserializer<LocalIsolate> {
public:
static MaybeHandle<SharedFunctionInfo> DeserializeSharedFunctionInfo(
LocalIsolate* isolate, const SerializedCodeData* data,
std::vector<Handle<Script>>* deserialized_scripts);
private:
explicit OffThreadObjectDeserializer(LocalIsolate* isolate,
const SerializedCodeData* data);
// Deserialize an object graph. Fail gracefully.
MaybeHandle<HeapObject> Deserialize(
std::vector<Handle<Script>>* deserialized_scripts);
};
} // namespace internal
} // namespace v8

View File

@ -14,7 +14,7 @@ namespace internal {
// Deserializes the read-only blob, creating the read-only roots and the
// Read-only object cache used by the other deserializers.
class ReadOnlyDeserializer final : public Deserializer {
class ReadOnlyDeserializer final : public Deserializer<Isolate> {
public:
explicit ReadOnlyDeserializer(Isolate* isolate, const SnapshotData* data,
bool can_rehash)

View File

@ -85,7 +85,7 @@ void StartupDeserializer::DeserializeStringTable() {
// TODO(leszeks): Consider pre-sizing the string table.
for (int i = 0; i < string_table_size; ++i) {
Handle<String> string = Handle<String>::cast(ReadObject());
StringTableInsertionKey key(string);
StringTableInsertionKey key(isolate(), string);
Handle<String> result =
isolate()->string_table()->LookupKey(isolate(), &key);
USE(result);

View File

@ -13,7 +13,7 @@ namespace v8 {
namespace internal {
// Initializes an isolate with context-independent data from a given snapshot.
class StartupDeserializer final : public Deserializer {
class StartupDeserializer final : public Deserializer<Isolate> {
public:
explicit StartupDeserializer(Isolate* isolate,
const SnapshotData* startup_data,