Commit c212d10d authored by Leszek Swirski's avatar Leszek Swirski Committed by Commit Bot

[serializer] Introduce HandleScopes for object contents

Create a HandleScope when serializing an object's contents, to reduce
the number of live handles during serialization. There's only a couple
of cases where these handles have to outlive the serialized contents,
and for these cases we introduce GlobalHandleVector or similar manual
strong root mechanisms.

In particular, backrefs don't actually need to exist as a handle vector
(the object addresses are already referred to by the reference map's
IdentityMap), except for DCHECKs, so this becomes a DEBUG-only global
handle vector.

To support this manual strong-rooting, the HotObjectList is split up
into a strong-rooted find-only class in Serializer, and a Handle
vector in Deserializer.

Bug: chromium:1075999
Change-Id: I586eeeb543e3f6c934c168961b068f2c34e72456
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2449980Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70411}
parent 4ddb0347
......@@ -371,6 +371,18 @@ class Recorder;
} \
} while (false)
#define WHILE_WITH_HANDLE_SCOPE(isolate, limit_check, body) \
do { \
Isolate* for_with_handle_isolate = isolate; \
while (limit_check) { \
HandleScope loop_scope(for_with_handle_isolate); \
for (int for_with_handle_it = 0; \
limit_check && for_with_handle_it < 1024; ++for_with_handle_it) { \
body \
} \
} \
} while (false)
#define FIELD_ACCESSOR(type, name) \
inline void set_##name(type v) { name##_ = v; } \
inline type name() const { return name##_; }
......
......@@ -10,12 +10,12 @@
#include <utility>
#include <vector>
#include "include/v8.h"
#include "include/v8-profiler.h"
#include "src/utils/utils.h"
#include "include/v8.h"
#include "src/handles/handles.h"
#include "src/heap/heap.h"
#include "src/objects/objects.h"
#include "src/utils/utils.h"
namespace v8 {
namespace internal {
......@@ -323,6 +323,52 @@ class EternalHandles final {
DISALLOW_COPY_AND_ASSIGN(EternalHandles);
};
// A vector of global Handles which automatically manages the backing of those
// Handles as a vector of strong-rooted addresses. Handles returned by the
// vector are valid as long as they are present in the vector.
template <typename T>
class GlobalHandleVector {
public:
class Iterator {
public:
explicit Iterator(
std::vector<Address, StrongRootBlockAllocator>::iterator it)
: it_(it) {}
Iterator& operator++() {
++it_;
return *this;
}
Handle<T> operator*() { return Handle<T>(&*it_); }
bool operator!=(Iterator& that) { return it_ != that.it_; }
private:
std::vector<Address, StrongRootBlockAllocator>::iterator it_;
};
explicit GlobalHandleVector(Heap* heap)
: locations_(StrongRootBlockAllocator(heap)) {}
Handle<T> operator[](size_t i) { return Handle<T>(&locations_[i]); }
size_t size() const { return locations_.size(); }
bool empty() const { return locations_.empty(); }
void Push(T val) { locations_.push_back(val.ptr()); }
// Handles into the GlobalHandleVector become invalid when they are removed,
// so "pop" returns a raw object rather than a handle.
T Pop() {
T obj = T::cast(Object(locations_.back()));
locations_.pop_back();
return obj;
}
Iterator begin() { return Iterator(locations_.begin()); }
Iterator end() { return Iterator(locations_.end()); }
private:
std::vector<Address, StrongRootBlockAllocator> locations_;
};
} // namespace internal
} // namespace v8
......
......@@ -248,7 +248,7 @@ class HandleScope {
// Limit for number of handles with --check-handle-count. This is
// large enough to compile natives and pass unit tests with some
// slack for future changes to natives.
static const int kCheckHandleThreshold = 42 * 1024;
static const int kCheckHandleThreshold = 30 * 1024;
private:
Isolate* isolate_;
......
......@@ -6746,5 +6746,41 @@ void Heap::IncrementObjectCounters() {
}
#endif // DEBUG
// StrongRootBlocks are allocated as a block of addresses, prefixed with a
// StrongRootsEntry pointer:
//
// | StrongRootsEntry*
// | Address 1
// | ...
// | Address N
//
// The allocate method registers the range "Address 1" to "Address N" with the
// heap as a strong root array, saves that entry in StrongRootsEntry*, and
// returns a pointer to Address 1.
Address* StrongRootBlockAllocator::allocate(size_t n) {
void* block = malloc(sizeof(StrongRootsEntry*) + n * sizeof(Address));
StrongRootsEntry** header = reinterpret_cast<StrongRootsEntry**>(block);
Address* ret = reinterpret_cast<Address*>(reinterpret_cast<char*>(block) +
sizeof(StrongRootsEntry*));
memset(ret, kNullAddress, n * sizeof(Address));
*header =
heap_->RegisterStrongRoots(FullObjectSlot(ret), FullObjectSlot(ret + n));
return ret;
}
void StrongRootBlockAllocator::deallocate(Address* p, size_t n) noexcept {
// The allocate method returns a pointer to Address 1, so the deallocate
// method has to offset that pointer back by sizeof(StrongRootsEntry*).
void* block = reinterpret_cast<char*>(p) - sizeof(StrongRootsEntry*);
StrongRootsEntry** header = reinterpret_cast<StrongRootsEntry**>(block);
heap_->UnregisterStrongRoots(*header);
free(block);
}
} // namespace internal
} // namespace v8
......@@ -2653,6 +2653,32 @@ T ForwardingAddress(T heap_obj) {
}
}
// Address block allocator compatible with standard containers which registers
// its allocated range as strong roots.
class StrongRootBlockAllocator {
public:
using pointer = Address*;
using const_pointer = const Address*;
using reference = Address&;
using const_reference = const Address&;
using value_type = Address;
using size_type = size_t;
using difference_type = ptrdiff_t;
template <class U>
struct rebind {
STATIC_ASSERT((std::is_same<Address, U>::value));
using other = StrongRootBlockAllocator;
};
explicit StrongRootBlockAllocator(Heap* heap) : heap_(heap) {}
Address* allocate(size_t n);
void deallocate(Address* p, size_t n) noexcept;
private:
Heap* heap_;
};
} // namespace internal
} // namespace v8
......
......@@ -195,8 +195,7 @@ bool ContextSerializer::ShouldBeInTheStartupObjectCache(HeapObject o) {
return o.IsName() || o.IsSharedFunctionInfo() || o.IsHeapNumber() ||
o.IsCode() || o.IsScopeInfo() || o.IsAccessorInfo() ||
o.IsTemplateInfo() || o.IsClassPositions() ||
o.map() == ReadOnlyRoots(startup_serializer_->isolate())
.fixed_cow_array_map();
o.map() == ReadOnlyRoots(isolate()).fixed_cow_array_map();
}
namespace {
......
......@@ -1124,11 +1124,11 @@ HeapObject Deserializer::Allocate(SnapshotSpace space, int size,
}
#endif
HeapObject obj = isolate_->heap()->AllocateRawWith<Heap::kRetryOrFail>(
HeapObject obj = isolate()->heap()->AllocateRawWith<Heap::kRetryOrFail>(
size, SpaceToType(space), AllocationOrigin::kRuntime, alignment);
#ifdef DEBUG
previous_allocation_obj_ = handle(obj, isolate_);
previous_allocation_obj_ = handle(obj, isolate());
previous_allocation_size_ = size;
#endif
......
......@@ -130,6 +130,34 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
private:
class RelocInfoVisitor;
// 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
// to create new handles. Note that this depends on those Handles staying
// valid as long as the HotObjectsList is alive.
class HotObjectsList {
public:
HotObjectsList() = default;
void Add(Handle<HeapObject> object) {
circular_queue_[index_] = object;
index_ = (index_ + 1) & kSizeMask;
}
Handle<HeapObject> Get(int index) {
DCHECK(!circular_queue_[index].is_null());
return circular_queue_[index];
}
private:
static const int kSize = kHotObjectCount;
static const int kSizeMask = kSize - 1;
STATIC_ASSERT(base::bits::IsPowerOfTwo(kSize));
Handle<HeapObject> circular_queue_[kSize];
int index_ = 0;
DISALLOW_COPY_AND_ASSIGN(HotObjectsList);
};
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override;
......@@ -184,6 +212,7 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
SnapshotByteSource source_;
uint32_t magic_number_;
HotObjectsList hot_objects_;
std::vector<Handle<Map>> new_maps_;
std::vector<Handle<AllocationSite>> new_allocation_sites_;
std::vector<Handle<Code>> new_code_objects_;
......
......@@ -39,22 +39,17 @@ bool SerializerDeserializer::CanBeDeferred(HeapObject o) {
return !o.IsMap() && !o.IsInternalizedString();
}
void SerializerDeserializer::RestoreExternalReferenceRedirectors(
Isolate* isolate, const std::vector<Handle<AccessorInfo>>& accessor_infos) {
void SerializerDeserializer::RestoreExternalReferenceRedirector(
Isolate* isolate, Handle<AccessorInfo> accessor_info) {
// Restore wiped accessor infos.
for (Handle<AccessorInfo> info : accessor_infos) {
Foreign::cast(info->js_getter())
.set_foreign_address(isolate, info->redirected_getter());
}
Foreign::cast(accessor_info->js_getter())
.set_foreign_address(isolate, accessor_info->redirected_getter());
}
void SerializerDeserializer::RestoreExternalReferenceRedirectors(
Isolate* isolate,
const std::vector<Handle<CallHandlerInfo>>& call_handler_infos) {
for (Handle<CallHandlerInfo> info : call_handler_infos) {
Foreign::cast(info->js_callback())
.set_foreign_address(isolate, info->redirected_callback());
}
void SerializerDeserializer::RestoreExternalReferenceRedirector(
Isolate* isolate, Handle<CallHandlerInfo> call_handler_info) {
Foreign::cast(call_handler_info->js_callback())
.set_foreign_address(isolate, call_handler_info->redirected_callback());
}
} // namespace internal
......
......@@ -23,51 +23,12 @@ class SerializerDeserializer : public RootVisitor {
static void Iterate(Isolate* isolate, RootVisitor* visitor);
protected:
class HotObjectsList {
public:
HotObjectsList() = default;
void Add(Handle<HeapObject> object) {
circular_queue_[index_] = object;
index_ = (index_ + 1) & kSizeMask;
}
Handle<HeapObject> Get(int index) {
DCHECK(!circular_queue_[index].is_null());
return circular_queue_[index];
}
static const int kNotFound = -1;
int Find(HeapObject object) {
DCHECK(!AllowGarbageCollection::IsAllowed());
for (int i = 0; i < kSize; i++) {
if (!circular_queue_[i].is_null() && *circular_queue_[i] == object) {
return i;
}
}
return kNotFound;
}
static const int kSize = 8;
private:
STATIC_ASSERT(base::bits::IsPowerOfTwo(kSize));
static const int kSizeMask = kSize - 1;
Handle<HeapObject> circular_queue_[kSize];
int index_ = 0;
DISALLOW_COPY_AND_ASSIGN(HotObjectsList);
};
static bool CanBeDeferred(HeapObject o);
void RestoreExternalReferenceRedirectors(
Isolate* isolate,
const std::vector<Handle<AccessorInfo>>& accessor_infos);
void RestoreExternalReferenceRedirectors(
Isolate* isolate,
const std::vector<Handle<CallHandlerInfo>>& call_handler_infos);
void RestoreExternalReferenceRedirector(Isolate* isolate,
Handle<AccessorInfo> accessor_info);
void RestoreExternalReferenceRedirector(
Isolate* isolate, Handle<CallHandlerInfo> call_handler_info);
// clang-format off
#define UNUSED_SERIALIZER_BYTE_CODES(V) \
......@@ -116,7 +77,6 @@ class SerializerDeserializer : public RootVisitor {
// 8 hot (recently seen or back-referenced) objects with optional skip.
static const int kHotObjectCount = 8;
STATIC_ASSERT(kHotObjectCount == HotObjectsList::kSize);
enum Bytecode : byte {
//
......@@ -298,9 +258,6 @@ class SerializerDeserializer : public RootVisitor {
// This backing store reference value represents nullptr values during
// serialization/deserialization.
static const uint32_t kNullRefSentinel = 0;
// ---------- member variable ----------
HotObjectsList hot_objects_;
};
} // namespace internal
......
This diff is collapsed.
......@@ -10,6 +10,7 @@
#include "src/codegen/external-reference-encoder.h"
#include "src/common/assert-scope.h"
#include "src/execution/isolate.h"
#include "src/handles/global-handles.h"
#include "src/logging/log.h"
#include "src/objects/objects.h"
#include "src/snapshot/embedded/embedded-data.h"
......@@ -250,7 +251,7 @@ class Serializer : public SerializerDeserializer {
void QueueDeferredObject(Handle<HeapObject> obj) {
DCHECK_NULL(reference_map_.LookupReference(obj));
deferred_objects_.push_back(obj);
deferred_objects_.Push(*obj);
}
// Register that the the given object shouldn't be immediately serialized, but
......@@ -266,8 +267,8 @@ class Serializer : public SerializerDeserializer {
void CountAllocation(Map map, int size, SnapshotSpace space);
#ifdef DEBUG
void PushStack(Handle<HeapObject> o) { stack_.push_back(o); }
void PopStack() { stack_.pop_back(); }
void PushStack(Handle<HeapObject> o) { stack_.Push(*o); }
void PopStack() { stack_.Pop(); }
void PrintStack();
void PrintStack(std::ostream&);
#endif // DEBUG
......@@ -284,21 +285,63 @@ class Serializer : public SerializerDeserializer {
return (flags_ & Snapshot::kAllowActiveIsolateForTesting) != 0;
}
std::vector<Handle<HeapObject>> back_refs_;
private:
// A circular queue of hot objects. This is added to in the same order as in
// Deserializer::HotObjectsList, but this stores the objects as an array of
// raw addresses that are considered strong roots. This allows objects to be
// added to the list without having to extend their handle's lifetime.
//
// We should never allow this class to return Handles to objects in the queue,
// as the object in the queue may change if kSize other objects are added to
// the queue during that Handle's lifetime.
class HotObjectsList {
public:
explicit HotObjectsList(Heap* heap);
~HotObjectsList();
void Add(HeapObject object) {
circular_queue_[index_] = object.ptr();
index_ = (index_ + 1) & kSizeMask;
}
static const int kNotFound = -1;
int Find(HeapObject object) {
DCHECK(!AllowGarbageCollection::IsAllowed());
for (int i = 0; i < kSize; i++) {
if (circular_queue_[i] == object.ptr()) {
return i;
}
}
return kNotFound;
}
private:
static const int kSize = kHotObjectCount;
static const int kSizeMask = kSize - 1;
STATIC_ASSERT(base::bits::IsPowerOfTwo(kSize));
Heap* heap_;
StrongRootsEntry* strong_roots_entry_;
Address circular_queue_[kSize] = {kNullAddress};
int index_ = 0;
DISALLOW_COPY_AND_ASSIGN(HotObjectsList);
};
// Disallow GC during serialization.
// TODO(leszeks, v8:10815): Remove this constraint.
DISALLOW_HEAP_ALLOCATION(no_gc)
Isolate* isolate_;
HotObjectsList hot_objects_;
SerializerReferenceMap reference_map_;
ExternalReferenceEncoder external_reference_encoder_;
RootIndexMap root_index_map_;
std::unique_ptr<CodeAddressMap> code_address_map_;
std::vector<byte> code_buffer_;
std::vector<Handle<HeapObject>>
GlobalHandleVector<HeapObject>
deferred_objects_; // To handle stack overflow.
int num_back_refs_ = 0;
// Objects which have started being serialized, but haven't yet been allocated
// with the allocator, are considered "pending". References to them don't have
......@@ -334,7 +377,8 @@ class Serializer : public SerializerDeserializer {
#endif // OBJECT_PRINT
#ifdef DEBUG
std::vector<Handle<HeapObject>> stack_;
GlobalHandleVector<HeapObject> back_refs_;
GlobalHandleVector<HeapObject> stack_;
#endif // DEBUG
DISALLOW_COPY_AND_ASSIGN(Serializer);
......@@ -346,7 +390,8 @@ class Serializer::ObjectSerializer : public ObjectVisitor {
public:
ObjectSerializer(Serializer* serializer, Handle<HeapObject> obj,
SnapshotByteSink* sink)
: serializer_(serializer),
: isolate_(serializer->isolate()),
serializer_(serializer),
object_(obj),
sink_(sink),
bytes_processed_so_far_(0) {
......@@ -375,6 +420,8 @@ class Serializer::ObjectSerializer : public ObjectVisitor {
void VisitRuntimeEntry(Code host, RelocInfo* reloc) override;
void VisitOffHeapTarget(Code host, RelocInfo* target) override;
Isolate* isolate() { return isolate_; }
private:
class RelocInfoObjectPreSerializer;
......@@ -393,6 +440,7 @@ class Serializer::ObjectSerializer : public ObjectVisitor {
void SerializeExternalString();
void SerializeExternalStringAsSequentialString();
Isolate* isolate_;
Serializer* serializer_;
Handle<HeapObject> object_;
SnapshotByteSink* sink_;
......
......@@ -37,8 +37,12 @@ void StartupDeserializer::DeserializeIntoIsolate() {
isolate()->heap()->IterateWeakRoots(
this, base::EnumSet<SkipRoot>{SkipRoot::kUnserializable});
DeserializeDeferredObjects();
RestoreExternalReferenceRedirectors(isolate(), accessor_infos());
RestoreExternalReferenceRedirectors(isolate(), call_handler_infos());
for (Handle<AccessorInfo> info : accessor_infos()) {
RestoreExternalReferenceRedirector(isolate(), info);
}
for (Handle<CallHandlerInfo> info : call_handler_infos()) {
RestoreExternalReferenceRedirector(isolate(), info);
}
// Flush the instruction cache for the entire code-space. Must happen after
// builtins deserialization.
......
......@@ -66,13 +66,19 @@ StartupSerializer::StartupSerializer(Isolate* isolate,
Snapshot::SerializerFlags flags,
ReadOnlySerializer* read_only_serializer)
: RootsSerializer(isolate, flags, RootIndex::kFirstStrongRoot),
read_only_serializer_(read_only_serializer) {
read_only_serializer_(read_only_serializer),
accessor_infos_(isolate->heap()),
call_handler_infos_(isolate->heap()) {
InitializeCodeAddressMap();
}
StartupSerializer::~StartupSerializer() {
RestoreExternalReferenceRedirectors(isolate(), accessor_infos_);
RestoreExternalReferenceRedirectors(isolate(), call_handler_infos_);
for (Handle<AccessorInfo> info : accessor_infos_) {
RestoreExternalReferenceRedirector(isolate(), info);
}
for (Handle<CallHandlerInfo> info : call_handler_infos_) {
RestoreExternalReferenceRedirector(isolate(), info);
}
OutputStatistics("StartupSerializer");
}
......@@ -144,14 +150,14 @@ void StartupSerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
Foreign::cast(info->getter()).foreign_address(isolate());
Foreign::cast(info->js_getter())
.set_foreign_address(isolate(), original_address);
accessor_infos_.push_back(info);
accessor_infos_.Push(*info);
} else if (use_simulator && obj->IsCallHandlerInfo()) {
Handle<CallHandlerInfo> info = Handle<CallHandlerInfo>::cast(obj);
Address original_address =
Foreign::cast(info->callback()).foreign_address(isolate());
Foreign::cast(info->js_callback())
.set_foreign_address(isolate(), original_address);
call_handler_infos_.push_back(info);
call_handler_infos_.Push(*info);
} else if (obj->IsScript() && Handle<Script>::cast(obj)->IsUserJavaScript()) {
Handle<Script>::cast(obj)->set_context_data(
ReadOnlyRoots(isolate()).uninitialized_symbol());
......
......@@ -7,6 +7,7 @@
#include <unordered_set>
#include "src/handles/global-handles.h"
#include "src/snapshot/roots-serializer.h"
namespace v8 {
......@@ -51,8 +52,8 @@ class V8_EXPORT_PRIVATE StartupSerializer : public RootsSerializer {
void SerializeStringTable(StringTable* string_table);
ReadOnlySerializer* read_only_serializer_;
std::vector<Handle<AccessorInfo>> accessor_infos_;
std::vector<Handle<CallHandlerInfo>> call_handler_infos_;
GlobalHandleVector<AccessorInfo> accessor_infos_;
GlobalHandleVector<CallHandlerInfo> call_handler_infos_;
DISALLOW_COPY_AND_ASSIGN(StartupSerializer);
};
......
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