blob: 3bd7842ffba4df82fcd5dacf8169a4a51155e33a [file] [log] [blame]
// Copyright 2023 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_HANDLES_TRACED_HANDLES_INL_H_
#define V8_HANDLES_TRACED_HANDLES_INL_H_
#include "src/handles/traced-handles.h"
#include "src/heap/heap-layout-inl.h"
#include "src/heap/heap-write-barrier-inl.h"
#include "src/objects/slots-inl.h"
namespace v8::internal {
TracedNode* TracedNodeBlock::AllocateNode() {
DCHECK_NE(used_, capacity_);
DCHECK_NE(first_free_node_, kInvalidFreeListNodeIndex);
auto* node = at(first_free_node_);
first_free_node_ = node->next_free();
used_++;
DCHECK(!node->is_in_use());
return node;
}
std::pair<TracedNodeBlock*, TracedNode*> TracedHandles::AllocateNode() {
if (V8_UNLIKELY(usable_blocks_.empty())) {
RefillUsableNodeBlocks();
}
TracedNodeBlock* block = usable_blocks_.Front();
auto* node = block->AllocateNode();
DCHECK(node->IsMetadataCleared());
if (V8_UNLIKELY(block->IsFull())) {
usable_blocks_.Remove(block);
}
used_nodes_++;
return std::make_pair(block, node);
}
bool TracedHandles::NeedsTrackingInYoungNodes(Tagged<Object> object,
TracedNode* node) const {
DCHECK(!node->is_in_young_list());
return HeapLayout::InYoungGeneration(object);
}
CppHeap* TracedHandles::GetCppHeapIfUnifiedYoungGC(Isolate* isolate) const {
// TODO(v8:13475) Consider removing this check when unified-young-gen becomes
// default.
if (!v8_flags.cppgc_young_generation) return nullptr;
auto* cpp_heap = CppHeap::From(isolate->heap()->cpp_heap());
if (cpp_heap && cpp_heap->generational_gc_supported()) return cpp_heap;
return nullptr;
}
bool TracedHandles::IsCppGCHostOld(CppHeap& cpp_heap, Address host) const {
DCHECK(host);
DCHECK(cpp_heap.generational_gc_supported());
auto* host_ptr = reinterpret_cast<void*>(host);
auto* page = cppgc::internal::BasePage::FromInnerAddress(&cpp_heap, host_ptr);
// TracedReference may be created on stack, in which case assume it's young
// and doesn't need to be remembered, since it'll anyway be scanned.
if (!page) return false;
return !page->ObjectHeaderFromInnerAddress(host_ptr).IsYoung();
}
bool TracedHandles::NeedsToBeRemembered(
Tagged<Object> object, TracedNode* node, Address* slot,
TracedReferenceStoreMode store_mode) const {
DCHECK(!node->has_old_host());
auto* cpp_heap = GetCppHeapIfUnifiedYoungGC(isolate_);
if (!cpp_heap) {
return false;
}
if (store_mode == TracedReferenceStoreMode::kInitializingStore) {
// Don't record initializing stores.
return false;
}
if (is_marking_) {
// If marking is in progress, the marking barrier will be issued later.
return false;
}
if (!HeapLayout::InYoungGeneration(object)) {
return false;
}
return IsCppGCHostOld(*cpp_heap, reinterpret_cast<Address>(slot));
}
// Publishes all internal state to be consumed by other threads.
FullObjectSlot TracedNode::Publish(Tagged<Object> object,
bool needs_young_bit_update,
bool needs_black_allocation,
bool has_old_host, bool is_droppable_value) {
DCHECK(IsMetadataCleared());
flags_ = needs_young_bit_update << IsInYoungList::kShift |
has_old_host << HasOldHost::kShift |
is_droppable_value << IsDroppable::kShift | 1 << IsInUse::kShift;
if (needs_black_allocation) set_markbit();
reinterpret_cast<std::atomic<Address>*>(&object_)->store(
object.ptr(), std::memory_order_release);
return FullObjectSlot(&object_);
}
FullObjectSlot TracedHandles::Create(
Address value, Address* slot, TracedReferenceStoreMode store_mode,
TracedReferenceHandling reference_handling) {
DCHECK_NOT_NULL(slot);
Tagged<Object> object(value);
auto [block, node] = AllocateNode();
const bool needs_young_bit_update = NeedsTrackingInYoungNodes(object, node);
const bool has_old_host = NeedsToBeRemembered(object, node, slot, store_mode);
const bool needs_black_allocation =
is_marking_ && store_mode != TracedReferenceStoreMode::kInitializingStore;
const bool is_droppable =
reference_handling == TracedReferenceHandling::kDroppable;
auto result_slot =
node->Publish(object, needs_young_bit_update, needs_black_allocation,
has_old_host, is_droppable);
// Write barrier and young node tracking may be reordered, so move them below
// `Publish()`.
if (needs_young_bit_update && !block->InYoungList()) {
young_blocks_.PushFront(block);
block->SetInYoungList(true);
DCHECK(block->InYoungList());
}
if (needs_black_allocation) {
WriteBarrier::MarkingFromTracedHandle(object);
}
#ifdef VERIFY_HEAP
if (i::v8_flags.verify_heap) {
Object::ObjectVerify(*result_slot, isolate_);
}
#endif // VERIFY_HEAP
return result_slot;
}
} // namespace v8::internal
#endif // V8_HANDLES_TRACED_HANDLES_INL_H_