[heap] Add read-only heap sharing behind a flag
Piggybacking of splitting heap setup, this change adds a shared read-only heap and a flag to enable it. Also makes CallOnce use std::function instead of a raw function pointer so the CL can use lambdas with CallOnce. Bug: v8:7464 Change-Id: I9a97fb1baa6badca39a7381de3fd9e01f5969340 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1518180 Reviewed-by: Hannes Payer <hpayer@chromium.org> Reviewed-by: Dan Elphick <delphick@chromium.org> Commit-Queue: Maciej Goszczycki <goszczycki@google.com> Cr-Commit-Position: refs/heads/master@{#60241}
This commit is contained in:
parent
04bb707e52
commit
e3aad1c84b
9
BUILD.gn
9
BUILD.gn
@ -171,6 +171,9 @@ declare_args() {
|
||||
# setting the "check_v8_header_includes" gclient variable to run a
|
||||
# specific hook).
|
||||
v8_check_header_includes = false
|
||||
|
||||
# Enable sharing read-only space across isolates.
|
||||
v8_enable_shared_ro_heap = false
|
||||
}
|
||||
|
||||
# We reuse the snapshot toolchain for building torque and other generators to
|
||||
@ -214,6 +217,9 @@ assert(!v8_enable_lite_mode || v8_enable_embedded_builtins,
|
||||
assert(!v8_enable_lite_mode || v8_use_snapshot,
|
||||
"Lite mode requires a snapshot build")
|
||||
|
||||
assert(v8_use_snapshot || !v8_enable_shared_ro_heap,
|
||||
"Nosnapshot builds are not supported with shared read-only heap enabled")
|
||||
|
||||
v8_random_seed = "314159265"
|
||||
v8_toolset_for_shell = "host"
|
||||
|
||||
@ -404,6 +410,9 @@ config("features") {
|
||||
if (v8_use_siphash) {
|
||||
defines += [ "V8_USE_SIPHASH" ]
|
||||
}
|
||||
if (v8_enable_shared_ro_heap) {
|
||||
defines += [ "V8_SHARED_RO_HEAP" ]
|
||||
}
|
||||
}
|
||||
|
||||
config("toolchain") {
|
||||
|
@ -74,7 +74,6 @@ enum : uint8_t {
|
||||
ONCE_STATE_DONE = 2
|
||||
};
|
||||
|
||||
typedef void (*NoArgFunction)();
|
||||
typedef void (*PointerArgFunction)(void* arg);
|
||||
|
||||
template <typename T>
|
||||
@ -85,13 +84,12 @@ struct OneArgFunction {
|
||||
V8_BASE_EXPORT void CallOnceImpl(OnceType* once,
|
||||
std::function<void()> init_func);
|
||||
|
||||
inline void CallOnce(OnceType* once, NoArgFunction init_func) {
|
||||
inline void CallOnce(OnceType* once, std::function<void()> init_func) {
|
||||
if (once->load(std::memory_order_acquire) != ONCE_STATE_DONE) {
|
||||
CallOnceImpl(once, init_func);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename Arg>
|
||||
inline void CallOnce(OnceType* once,
|
||||
typename OneArgFunction<Arg*>::type init_func, Arg* arg) {
|
||||
|
@ -3550,14 +3550,21 @@ const char* Heap::GarbageCollectionReasonToString(
|
||||
}
|
||||
|
||||
bool Heap::Contains(HeapObject value) {
|
||||
// Check RO_SPACE first because IsOutsideAllocatedSpace cannot account for a
|
||||
// shared RO_SPACE.
|
||||
// TODO(goszczycki): Exclude read-only space. Use ReadOnlyHeap::Contains where
|
||||
// appropriate.
|
||||
if (read_only_space_ != nullptr && read_only_space_->Contains(value)) {
|
||||
return true;
|
||||
}
|
||||
if (memory_allocator()->IsOutsideAllocatedSpace(value->address())) {
|
||||
return false;
|
||||
}
|
||||
return HasBeenSetUp() &&
|
||||
(new_space_->ToSpaceContains(value) || old_space_->Contains(value) ||
|
||||
code_space_->Contains(value) || map_space_->Contains(value) ||
|
||||
lo_space_->Contains(value) || read_only_space_->Contains(value) ||
|
||||
code_lo_space_->Contains(value) || new_lo_space_->Contains(value));
|
||||
lo_space_->Contains(value) || code_lo_space_->Contains(value) ||
|
||||
new_lo_space_->Contains(value));
|
||||
}
|
||||
|
||||
bool Heap::InSpace(HeapObject value, AllocationSpace space) {
|
||||
@ -4550,8 +4557,9 @@ void Heap::SetUp() {
|
||||
}
|
||||
|
||||
void Heap::SetUpFromReadOnlyHeap(ReadOnlyHeap* ro_heap) {
|
||||
DCHECK_NULL(read_only_space_);
|
||||
DCHECK_NOT_NULL(ro_heap);
|
||||
DCHECK_IMPLIES(read_only_space_ != nullptr,
|
||||
read_only_space_ == ro_heap->read_only_space());
|
||||
read_only_heap_ = ro_heap;
|
||||
space_[RO_SPACE] = read_only_space_ = ro_heap->read_only_space();
|
||||
}
|
||||
|
@ -4,29 +4,72 @@
|
||||
|
||||
#include "src/heap/read-only-heap.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "src/base/once.h"
|
||||
#include "src/heap/heap-inl.h"
|
||||
#include "src/heap/spaces.h"
|
||||
#include "src/snapshot/read-only-deserializer.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
#ifdef V8_SHARED_RO_HEAP
|
||||
V8_DECLARE_ONCE(setup_ro_heap_once);
|
||||
ReadOnlyHeap* shared_ro_heap = nullptr;
|
||||
#endif
|
||||
|
||||
// static
|
||||
void ReadOnlyHeap::SetUp(Isolate* isolate, ReadOnlyDeserializer* des) {
|
||||
#ifdef V8_SHARED_RO_HEAP
|
||||
void* isolate_ro_roots = reinterpret_cast<void*>(
|
||||
isolate->roots_table().read_only_roots_begin().address());
|
||||
base::CallOnce(&setup_ro_heap_once, [isolate, des, isolate_ro_roots]() {
|
||||
shared_ro_heap = Init(isolate, des);
|
||||
if (des != nullptr) {
|
||||
std::memcpy(shared_ro_heap->read_only_roots_, isolate_ro_roots,
|
||||
kEntriesCount * sizeof(Address));
|
||||
}
|
||||
});
|
||||
|
||||
isolate->heap()->SetUpFromReadOnlyHeap(shared_ro_heap);
|
||||
if (des != nullptr) {
|
||||
std::memcpy(isolate_ro_roots, shared_ro_heap->read_only_roots_,
|
||||
kEntriesCount * sizeof(Address));
|
||||
}
|
||||
#else
|
||||
Init(isolate, des);
|
||||
#endif // V8_SHARED_RO_HEAP
|
||||
}
|
||||
|
||||
void ReadOnlyHeap::OnCreateHeapObjectsComplete() {
|
||||
DCHECK(!deserializing_);
|
||||
#ifdef V8_SHARED_RO_HEAP
|
||||
read_only_space_->Forget();
|
||||
#endif
|
||||
read_only_space_->MarkAsReadOnly();
|
||||
}
|
||||
|
||||
// static
|
||||
ReadOnlyHeap* ReadOnlyHeap::Init(Isolate* isolate, ReadOnlyDeserializer* des) {
|
||||
auto* ro_heap = new ReadOnlyHeap(new ReadOnlySpace(isolate->heap()));
|
||||
isolate->heap()->SetUpFromReadOnlyHeap(ro_heap);
|
||||
if (des != nullptr) {
|
||||
des->DeserializeInto(isolate);
|
||||
ro_heap->deserializing_ = true;
|
||||
#ifdef V8_SHARED_RO_HEAP
|
||||
ro_heap->read_only_space_->Forget();
|
||||
#endif
|
||||
ro_heap->read_only_space_->MarkAsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlyHeap::OnCreateHeapObjectsComplete() {
|
||||
read_only_space_->MarkAsReadOnly();
|
||||
return ro_heap;
|
||||
}
|
||||
|
||||
void ReadOnlyHeap::OnHeapTearDown() {
|
||||
#ifndef V8_SHARED_RO_HEAP
|
||||
delete read_only_space_;
|
||||
delete this;
|
||||
#endif
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "src/heap/heap.h"
|
||||
#include "src/objects.h"
|
||||
#include "src/roots.h"
|
||||
#include "src/snapshot/read-only-deserializer.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -18,16 +17,19 @@ class ReadOnlySpace;
|
||||
class ReadOnlyDeserializer;
|
||||
|
||||
// This class transparently manages read-only space, roots and cache creation
|
||||
// and destruction. Eventually this will allow sharing these artifacts between
|
||||
// isolates.
|
||||
// and destruction.
|
||||
class ReadOnlyHeap final {
|
||||
public:
|
||||
static constexpr size_t kEntriesCount =
|
||||
static_cast<size_t>(RootIndex::kReadOnlyRootsCount);
|
||||
|
||||
// If necessary create read-only heap and initialize its artifacts (if the
|
||||
// deserializer is provided).
|
||||
// TODO(goszczycki): Ideally we'd create this without needing a heap.
|
||||
static void SetUp(Isolate* isolate, ReadOnlyDeserializer* des);
|
||||
// Indicate that all read-only space objects have been created and will not
|
||||
// be written to.
|
||||
// be written to. This is not thread safe, and should really only be used as
|
||||
// part of mksnapshot or when read-only heap sharing is disabled.
|
||||
void OnCreateHeapObjectsComplete();
|
||||
// Indicate that the current isolate no longer requires the read-only heap and
|
||||
// it may be safely disposed of.
|
||||
@ -42,9 +44,16 @@ class ReadOnlyHeap final {
|
||||
ReadOnlySpace* read_only_space() const { return read_only_space_; }
|
||||
|
||||
private:
|
||||
static ReadOnlyHeap* Init(Isolate* isolate, ReadOnlyDeserializer* des);
|
||||
|
||||
bool deserializing_ = false;
|
||||
ReadOnlySpace* read_only_space_ = nullptr;
|
||||
std::vector<Object> read_only_object_cache_;
|
||||
|
||||
#ifdef V8_SHARED_RO_HEAP
|
||||
Address read_only_roots_[kEntriesCount];
|
||||
#endif
|
||||
|
||||
explicit ReadOnlyHeap(ReadOnlySpace* ro_space) : read_only_space_(ro_space) {}
|
||||
DISALLOW_COPY_AND_ASSIGN(ReadOnlyHeap);
|
||||
};
|
||||
|
@ -3305,6 +3305,12 @@ void ReadOnlyPage::MakeHeaderRelocatable() {
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlySpace::Forget() {
|
||||
for (Page* p : *this) {
|
||||
heap()->memory_allocator()->PreFreeMemory(p);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlySpace::SetPermissionsForPages(PageAllocator::Permission access) {
|
||||
MemoryAllocator* memory_allocator = heap()->memory_allocator();
|
||||
for (Page* p : *this) {
|
||||
|
@ -2992,6 +2992,9 @@ class ReadOnlySpace : public PagedSpace {
|
||||
|
||||
void ClearStringPaddingIfNeeded();
|
||||
void MarkAsReadOnly();
|
||||
// Make the heap forget the space for memory bookkeeping purposes
|
||||
// (e.g. prevent space's memory from registering as leaked).
|
||||
void Forget();
|
||||
|
||||
// During boot the free_space_map is created, and afterwards we may need
|
||||
// to write it into the free list nodes that were already created.
|
||||
|
@ -497,6 +497,7 @@ class RootsTable {
|
||||
friend class Isolate;
|
||||
friend class Heap;
|
||||
friend class Factory;
|
||||
friend class ReadOnlyHeap;
|
||||
friend class ReadOnlyRoots;
|
||||
friend class RootsSerializer;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user