%PDF- %PDF-
| Direktori : /home2/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/ |
| Current File : //home2/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/marking-visitor-inl.h |
// Copyright 2019 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_HEAP_MARKING_VISITOR_INL_H_
#define V8_HEAP_MARKING_VISITOR_INL_H_
#include "src/common/globals.h"
#include "src/heap/ephemeron-remembered-set.h"
#include "src/heap/marking-state-inl.h"
#include "src/heap/marking-visitor.h"
#include "src/heap/marking-worklist-inl.h"
#include "src/heap/objects-visiting-inl.h"
#include "src/heap/objects-visiting.h"
#include "src/heap/pretenuring-handler-inl.h"
#include "src/heap/progress-bar.h"
#include "src/heap/spaces.h"
#include "src/objects/descriptor-array.h"
#include "src/objects/object-macros.h"
#include "src/objects/objects.h"
#include "src/objects/property-details.h"
#include "src/objects/smi.h"
#include "src/objects/string.h"
#include "src/sandbox/external-pointer-inl.h"
namespace v8 {
namespace internal {
// ===========================================================================
// Visiting strong and weak pointers =========================================
// ===========================================================================
template <typename ConcreteVisitor>
void MarkingVisitorBase<ConcreteVisitor>::MarkObject(
Tagged<HeapObject> host, Tagged<HeapObject> object) {
DCHECK(ReadOnlyHeap::Contains(object) || heap_->Contains(object));
SynchronizePageAccess(object);
concrete_visitor()->AddStrongReferenceForReferenceSummarizer(host, object);
if (concrete_visitor()->TryMark(object)) {
local_marking_worklists_->Push(object);
if (V8_UNLIKELY(concrete_visitor()->retaining_path_mode() ==
TraceRetainingPathMode::kEnabled)) {
heap_->AddRetainer(host, object);
}
}
}
// class template arguments
template <typename ConcreteVisitor>
// method template arguments
template <typename THeapObjectSlot>
void MarkingVisitorBase<ConcreteVisitor>::ProcessStrongHeapObject(
Tagged<HeapObject> host, THeapObjectSlot slot,
Tagged<HeapObject> heap_object) {
SynchronizePageAccess(heap_object);
if (!ShouldMarkObject(heap_object)) return;
MarkObject(host, heap_object);
concrete_visitor()->RecordSlot(host, slot, heap_object);
}
// class template arguments
template <typename ConcreteVisitor>
// method template arguments
template <typename THeapObjectSlot>
void MarkingVisitorBase<ConcreteVisitor>::ProcessWeakHeapObject(
Tagged<HeapObject> host, THeapObjectSlot slot,
Tagged<HeapObject> heap_object) {
SynchronizePageAccess(heap_object);
if (!ShouldMarkObject(heap_object)) return;
if (concrete_visitor()->IsMarked(heap_object)) {
// Weak references with live values are directly processed here to
// reduce the processing time of weak cells during the main GC
// pause.
concrete_visitor()->RecordSlot(host, slot, heap_object);
} else {
// If we do not know about liveness of the value, we have to process
// the reference when we know the liveness of the whole transitive
// closure.
local_weak_objects_->weak_references_local.Push(std::make_pair(host, slot));
concrete_visitor()->AddWeakReferenceForReferenceSummarizer(host,
heap_object);
}
}
// class template arguments
template <typename ConcreteVisitor>
// method template arguments
template <typename TSlot>
V8_INLINE void MarkingVisitorBase<ConcreteVisitor>::VisitPointersImpl(
Tagged<HeapObject> host, TSlot start, TSlot end) {
using THeapObjectSlot = typename TSlot::THeapObjectSlot;
for (TSlot slot = start; slot < end; ++slot) {
typename TSlot::TObject object =
slot.Relaxed_Load(ObjectVisitorWithCageBases::cage_base());
Tagged<HeapObject> heap_object;
if (object.GetHeapObjectIfStrong(&heap_object)) {
// If the reference changes concurrently from strong to weak, the write
// barrier will treat the weak reference as strong, so we won't miss the
// weak reference.
ProcessStrongHeapObject(host, THeapObjectSlot(slot), heap_object);
} else if (TSlot::kCanBeWeak && object.GetHeapObjectIfWeak(&heap_object)) {
ProcessWeakHeapObject(host, THeapObjectSlot(slot), heap_object);
}
}
}
template <typename ConcreteVisitor>
V8_INLINE void
MarkingVisitorBase<ConcreteVisitor>::VisitInstructionStreamPointerImpl(
Tagged<Code> host, InstructionStreamSlot slot) {
Tagged<Object> object =
slot.Relaxed_Load(ObjectVisitorWithCageBases::code_cage_base());
Tagged<HeapObject> heap_object;
if (object.GetHeapObjectIfStrong(&heap_object)) {
// If the reference changes concurrently from strong to weak, the write
// barrier will treat the weak reference as strong, so we won't miss the
// weak reference.
ProcessStrongHeapObject(host, HeapObjectSlot(slot), heap_object);
}
}
template <typename ConcreteVisitor>
void MarkingVisitorBase<ConcreteVisitor>::VisitEmbeddedPointer(
Tagged<InstructionStream> host, RelocInfo* rinfo) {
DCHECK(RelocInfo::IsEmbeddedObjectMode(rinfo->rmode()));
Tagged<HeapObject> object =
rinfo->target_object(ObjectVisitorWithCageBases::cage_base());
if (!ShouldMarkObject(object)) return;
if (!concrete_visitor()->IsMarked(object)) {
Tagged<Code> code = Code::unchecked_cast(host->raw_code(kAcquireLoad));
if (code->IsWeakObject(object)) {
local_weak_objects_->weak_objects_in_code_local.Push(
std::make_pair(object, code));
concrete_visitor()->AddWeakReferenceForReferenceSummarizer(host, object);
} else {
MarkObject(host, object);
}
}
concrete_visitor()->RecordRelocSlot(host, rinfo, object);
}
template <typename ConcreteVisitor>
void MarkingVisitorBase<ConcreteVisitor>::VisitCodeTarget(
Tagged<InstructionStream> host, RelocInfo* rinfo) {
DCHECK(RelocInfo::IsCodeTargetMode(rinfo->rmode()));
Tagged<InstructionStream> target =
InstructionStream::FromTargetAddress(rinfo->target_address());
if (!ShouldMarkObject(target)) return;
MarkObject(host, target);
concrete_visitor()->RecordRelocSlot(host, rinfo, target);
}
template <typename ConcreteVisitor>
void MarkingVisitorBase<ConcreteVisitor>::VisitExternalPointer(
Tagged<HeapObject> host, ExternalPointerSlot slot) {
#ifdef V8_ENABLE_SANDBOX
DCHECK_NE(slot.tag(), kExternalPointerNullTag);
ExternalPointerHandle handle = slot.Relaxed_LoadHandle();
ExternalPointerTable* table = IsSharedExternalPointerType(slot.tag())
? shared_external_pointer_table_
: external_pointer_table_;
ExternalPointerTable::Space* space = IsSharedExternalPointerType(slot.tag())
? shared_external_pointer_space_
: heap_->external_pointer_space();
table->Mark(space, handle, slot.address());
#endif // V8_ENABLE_SANDBOX
}
template <typename ConcreteVisitor>
void MarkingVisitorBase<ConcreteVisitor>::VisitIndirectPointer(
Tagged<HeapObject> host, IndirectPointerSlot slot,
IndirectPointerMode mode) {
#ifdef V8_ENABLE_SANDBOX
if (mode == IndirectPointerMode::kStrong) {
// Load the referenced object (if the slot is initialized) and mark it as
// alive if necessary. Indirect pointers never have to be added to a
// remembered set because the referenced object will update the pointer
// table entry when it is relocated.
Tagged<Object> value = slot.Relaxed_Load(heap_->isolate());
if (IsHeapObject(value)) {
Tagged<HeapObject> obj = HeapObject::cast(value);
SynchronizePageAccess(obj);
if (ShouldMarkObject(obj)) {
MarkObject(host, obj);
}
}
}
#else
UNREACHABLE();
#endif
}
template <typename ConcreteVisitor>
void MarkingVisitorBase<ConcreteVisitor>::VisitIndirectPointerTableEntry(
Tagged<HeapObject> host, IndirectPointerSlot slot) {
#ifdef V8_ENABLE_SANDBOX
IndirectPointerHandle handle = slot.Relaxed_LoadHandle();
if (slot.tag() == kCodeIndirectPointerTag) {
CodePointerTable* table = GetProcessWideCodePointerTable();
CodePointerTable::Space* space = heap_->code_pointer_space();
table->Mark(space, handle);
return;
}
IndirectPointerTable* table = indirect_pointer_table_;
IndirectPointerTable::Space* space = heap_->indirect_pointer_space();
table->Mark(space, handle);
#else
UNREACHABLE();
#endif
}
// ===========================================================================
// Object participating in bytecode flushing =================================
// ===========================================================================
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitBytecodeArray(
Tagged<Map> map, Tagged<BytecodeArray> object) {
int size = BytecodeArray::BodyDescriptor::SizeOf(map, object);
this->VisitMapPointer(object);
BytecodeArray::BodyDescriptor::IterateBody(map, object, size, this);
return size;
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitJSFunction(
Tagged<Map> map, Tagged<JSFunction> js_function) {
int size = concrete_visitor()->VisitJSObjectSubclass(map, js_function);
if (ShouldFlushBaselineCode(js_function)) {
DCHECK(IsBaselineCodeFlushingEnabled(code_flush_mode_));
local_weak_objects_->baseline_flushing_candidates_local.Push(js_function);
} else {
#ifdef V8_ENABLE_SANDBOX
VisitIndirectPointer(js_function,
js_function->RawIndirectPointerField(
JSFunction::kCodeOffset, kCodeIndirectPointerTag),
IndirectPointerMode::kStrong);
#else
VisitPointer(js_function, js_function->RawField(JSFunction::kCodeOffset));
#endif // V8_ENABLE_SANDBOX
// TODO(mythria): Consider updating the check for ShouldFlushBaselineCode to
// also include cases where there is old bytecode even when there is no
// baseline code and remove this check here.
if (IsByteCodeFlushingEnabled(code_flush_mode_) &&
js_function->NeedsResetDueToFlushedBytecode()) {
local_weak_objects_->flushed_js_functions_local.Push(js_function);
}
}
return size;
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitSharedFunctionInfo(
Tagged<Map> map, Tagged<SharedFunctionInfo> shared_info) {
int size = SharedFunctionInfo::BodyDescriptor::SizeOf(map, shared_info);
this->VisitMapPointer(shared_info);
SharedFunctionInfo::BodyDescriptor::IterateBody(map, shared_info, size, this);
const bool can_flush_bytecode = HasBytecodeArrayForFlushing(shared_info);
// We found a BytecodeArray that can be flushed. Increment the age of the SFI.
if (can_flush_bytecode && !should_keep_ages_unchanged_) {
MakeOlder(shared_info);
}
if (!can_flush_bytecode || !ShouldFlushCode(shared_info)) {
// If the SharedFunctionInfo doesn't have old bytecode visit the function
// data strongly.
VisitPointer(shared_info, shared_info->RawField(
SharedFunctionInfo::kFunctionDataOffset));
} else if (!IsByteCodeFlushingEnabled(code_flush_mode_)) {
// If bytecode flushing is disabled but baseline code flushing is enabled
// then we have to visit the bytecode but not the baseline code.
DCHECK(IsBaselineCodeFlushingEnabled(code_flush_mode_));
Tagged<Code> baseline_code =
Code::cast(shared_info->function_data(kAcquireLoad));
// Visit the bytecode hanging off baseline code.
VisitPointer(baseline_code,
baseline_code->RawField(
Code::kDeoptimizationDataOrInterpreterDataOffset));
local_weak_objects_->code_flushing_candidates_local.Push(shared_info);
} else {
// In other cases, record as a flushing candidate since we have old
// bytecode.
local_weak_objects_->code_flushing_candidates_local.Push(shared_info);
}
return size;
}
template <typename ConcreteVisitor>
bool MarkingVisitorBase<ConcreteVisitor>::HasBytecodeArrayForFlushing(
Tagged<SharedFunctionInfo> sfi) const {
if (IsFlushingDisabled(code_flush_mode_)) return false;
// TODO(rmcilroy): Enable bytecode flushing for resumable functions.
if (IsResumableFunction(sfi->kind()) || !sfi->allows_lazy_compilation()) {
return false;
}
// Get a snapshot of the function data field, and if it is a bytecode array,
// check if it is old. Note, this is done this way since this function can be
// called by the concurrent marker.
Tagged<Object> data = sfi->function_data(kAcquireLoad);
if (IsCode(data)) {
Tagged<Code> baseline_code = Code::cast(data);
DCHECK_EQ(baseline_code->kind(), CodeKind::BASELINE);
// If baseline code flushing isn't enabled and we have baseline data on SFI
// we cannot flush baseline / bytecode.
if (!IsBaselineCodeFlushingEnabled(code_flush_mode_)) return false;
data = baseline_code->bytecode_or_interpreter_data();
} else if (!IsByteCodeFlushingEnabled(code_flush_mode_)) {
// If bytecode flushing isn't enabled and there is no baseline code there is
// nothing to flush.
return false;
}
return IsBytecodeArray(data);
}
template <typename ConcreteVisitor>
bool MarkingVisitorBase<ConcreteVisitor>::ShouldFlushCode(
Tagged<SharedFunctionInfo> sfi) const {
return IsStressFlushingEnabled(code_flush_mode_) || IsOld(sfi);
}
template <typename ConcreteVisitor>
bool MarkingVisitorBase<ConcreteVisitor>::IsOld(
Tagged<SharedFunctionInfo> sfi) const {
if (v8_flags.flush_code_based_on_time) {
return sfi->age() >= v8_flags.bytecode_old_time;
} else if (v8_flags.flush_code_based_on_tab_visibility) {
return isolate_in_background_ ||
V8_UNLIKELY(sfi->age() == SharedFunctionInfo::kMaxAge);
} else {
return sfi->age() >= v8_flags.bytecode_old_age;
}
}
template <typename ConcreteVisitor>
void MarkingVisitorBase<ConcreteVisitor>::MakeOlder(
Tagged<SharedFunctionInfo> sfi) const {
if (v8_flags.flush_code_based_on_time) {
DCHECK_NE(code_flushing_increase_, 0);
uint16_t current_age;
uint16_t updated_age;
do {
current_age = sfi->age();
// When the age is 0, it was reset by the function prologue in
// Ignition/Sparkplug. But that might have been some time after the last
// full GC. So in this case we don't increment the value like we normally
// would but just set the age to 1. All non-0 values can be incremented as
// expected (we add the number of seconds since the last GC) as they were
// definitely last executed before the last full GC.
updated_age = current_age == 0
? 1
: SaturateAdd(current_age, code_flushing_increase_);
} while (sfi->CompareExchangeAge(current_age, updated_age) != current_age);
} else if (v8_flags.flush_code_based_on_tab_visibility) {
// No need to increment age.
} else {
uint16_t age = sfi->age();
if (age < v8_flags.bytecode_old_age) {
sfi->CompareExchangeAge(age, age + 1);
}
DCHECK_LE(sfi->age(), v8_flags.bytecode_old_age);
}
}
template <typename ConcreteVisitor>
bool MarkingVisitorBase<ConcreteVisitor>::ShouldFlushBaselineCode(
Tagged<JSFunction> js_function) const {
if (!IsBaselineCodeFlushingEnabled(code_flush_mode_)) return false;
// Do a raw read for shared and code fields here since this function may be
// called on a concurrent thread. JSFunction itself should be fully
// initialized here but the SharedFunctionInfo, InstructionStream objects may
// not be initialized. We read using acquire loads to defend against that.
Tagged<Object> maybe_shared =
ACQUIRE_READ_FIELD(js_function, JSFunction::kSharedFunctionInfoOffset);
if (!IsSharedFunctionInfo(maybe_shared)) return false;
// See crbug.com/v8/11972 for more details on acquire / release semantics for
// code field. We don't use release stores when copying code pointers from
// SFI / FV to JSFunction but it is safe in practice.
Tagged<Object> maybe_code = js_function->raw_code(kAcquireLoad);
#ifdef THREAD_SANITIZER
// This is needed because TSAN does not process the memory fence
// emitted after page initialization.
BasicMemoryChunk::FromAddress(maybe_code.ptr())->SynchronizedHeapLoad();
#endif
if (!IsCode(maybe_code)) return false;
Tagged<Code> code = Code::cast(maybe_code);
if (code->kind() != CodeKind::BASELINE) return false;
Tagged<SharedFunctionInfo> shared = SharedFunctionInfo::cast(maybe_shared);
return HasBytecodeArrayForFlushing(shared) && ShouldFlushCode(shared);
}
// ===========================================================================
// Fixed arrays that need incremental processing and can be left-trimmed =====
// ===========================================================================
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitFixedArrayWithProgressBar(
Tagged<Map> map, Tagged<FixedArray> object, ProgressBar& progress_bar) {
const int kProgressBarScanningChunk = kMaxRegularHeapObjectSize;
static_assert(kMaxRegularHeapObjectSize % kTaggedSize == 0);
DCHECK(concrete_visitor()->IsMarked(object));
int size = FixedArray::BodyDescriptor::SizeOf(map, object);
size_t current_progress_bar = progress_bar.Value();
int start = static_cast<int>(current_progress_bar);
if (start == 0) {
this->VisitMapPointer(object);
start = FixedArray::BodyDescriptor::kStartOffset;
}
int end = std::min(size, start + kProgressBarScanningChunk);
if (start < end) {
VisitPointers(object, object->RawField(start), object->RawField(end));
bool success = progress_bar.TrySetNewValue(current_progress_bar, end);
CHECK(success);
if (end < size) {
// The object can be pushed back onto the marking worklist only after
// progress bar was updated.
DCHECK(ShouldMarkObject(object));
local_marking_worklists_->Push(object);
}
}
return end - start;
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitFixedArrayRegularly(
Tagged<Map> map, Tagged<FixedArray> object) {
int size = FixedArray::BodyDescriptor::SizeOf(map, object);
concrete_visitor()
->template VisitMapPointerIfNeeded<VisitorId::kVisitFixedArray>(object);
FixedArray::BodyDescriptor::IterateBody(map, object, size,
concrete_visitor());
return size;
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitFixedArray(
Tagged<Map> map, Tagged<FixedArray> object) {
ProgressBar& progress_bar =
MemoryChunk::FromHeapObject(object)->ProgressBar();
return concrete_visitor()->CanUpdateValuesInHeap() && progress_bar.IsEnabled()
? VisitFixedArrayWithProgressBar(map, object, progress_bar)
: VisitFixedArrayRegularly(map, object);
}
// ===========================================================================
// Objects participating in embedder tracing =================================
// ===========================================================================
template <typename ConcreteVisitor>
template <typename T>
inline int MarkingVisitorBase<ConcreteVisitor>::
VisitEmbedderTracingSubClassNoEmbedderTracing(Tagged<Map> map,
Tagged<T> object) {
return concrete_visitor()->VisitJSObjectSubclass(map, object);
}
template <typename ConcreteVisitor>
template <typename T>
inline int MarkingVisitorBase<ConcreteVisitor>::
VisitEmbedderTracingSubClassWithEmbedderTracing(Tagged<Map> map,
Tagged<T> object) {
const bool requires_snapshot =
local_marking_worklists_->SupportsExtractWrapper();
MarkingWorklists::Local::WrapperSnapshot wrapper_snapshot;
const bool valid_snapshot =
requires_snapshot &&
local_marking_worklists_->ExtractWrapper(map, object, wrapper_snapshot);
const int size = concrete_visitor()->VisitJSObjectSubclass(map, object);
if (size && valid_snapshot) {
local_marking_worklists_->PushExtractedWrapper(wrapper_snapshot);
}
return size;
}
template <typename ConcreteVisitor>
template <typename T>
int MarkingVisitorBase<ConcreteVisitor>::VisitEmbedderTracingSubclass(
Tagged<Map> map, Tagged<T> object) {
DCHECK(object->MayHaveEmbedderFields());
if (V8_LIKELY(trace_embedder_fields_)) {
return VisitEmbedderTracingSubClassWithEmbedderTracing(map, object);
}
return VisitEmbedderTracingSubClassNoEmbedderTracing(map, object);
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitJSApiObject(
Tagged<Map> map, Tagged<JSObject> object) {
return VisitEmbedderTracingSubclass(map, object);
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitJSArrayBuffer(
Tagged<Map> map, Tagged<JSArrayBuffer> object) {
object->MarkExtension();
return VisitEmbedderTracingSubclass(map, object);
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitJSDataViewOrRabGsabDataView(
Tagged<Map> map, Tagged<JSDataViewOrRabGsabDataView> object) {
return VisitEmbedderTracingSubclass(map, object);
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitJSTypedArray(
Tagged<Map> map, Tagged<JSTypedArray> object) {
return VisitEmbedderTracingSubclass(map, object);
}
// ===========================================================================
// Weak JavaScript objects ===================================================
// ===========================================================================
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitEphemeronHashTable(
Tagged<Map> map, Tagged<EphemeronHashTable> table) {
local_weak_objects_->ephemeron_hash_tables_local.Push(table);
for (InternalIndex i : table->IterateEntries()) {
ObjectSlot key_slot =
table->RawFieldOfElementAt(EphemeronHashTable::EntryToIndex(i));
Tagged<HeapObject> key = HeapObject::cast(table->KeyAt(i));
SynchronizePageAccess(key);
concrete_visitor()->RecordSlot(table, key_slot, key);
concrete_visitor()->AddWeakReferenceForReferenceSummarizer(table, key);
ObjectSlot value_slot =
table->RawFieldOfElementAt(EphemeronHashTable::EntryToValueIndex(i));
// Objects in the shared heap are prohibited from being used as keys in
// WeakMaps and WeakSets and therefore cannot be ephemeron keys. See also
// MarkCompactCollector::ProcessEphemeron.
DCHECK(!key.InWritableSharedSpace());
if (key.InReadOnlySpace() || concrete_visitor()->IsMarked(key)) {
VisitPointer(table, value_slot);
} else {
Tagged<Object> value_obj = table->ValueAt(i);
if (IsHeapObject(value_obj)) {
Tagged<HeapObject> value = HeapObject::cast(value_obj);
SynchronizePageAccess(value);
concrete_visitor()->RecordSlot(table, value_slot, value);
concrete_visitor()->AddWeakReferenceForReferenceSummarizer(table,
value);
if (!ShouldMarkObject(value)) continue;
// Revisit ephemerons with both key and value unreachable at end
// of concurrent marking cycle.
if (concrete_visitor()->IsUnmarked(value)) {
local_weak_objects_->discovered_ephemerons_local.Push(
Ephemeron{key, value});
}
}
}
}
return table->SizeFromMap(map);
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitJSWeakRef(
Tagged<Map> map, Tagged<JSWeakRef> weak_ref) {
int size = concrete_visitor()->VisitJSObjectSubclass(map, weak_ref);
if (size == 0) return 0;
if (IsHeapObject(weak_ref->target())) {
Tagged<HeapObject> target = HeapObject::cast(weak_ref->target());
SynchronizePageAccess(target);
if (target.InReadOnlySpace() || concrete_visitor()->IsMarked(target)) {
// Record the slot inside the JSWeakRef, since the
// VisitJSObjectSubclass above didn't visit it.
ObjectSlot slot = weak_ref->RawField(JSWeakRef::kTargetOffset);
concrete_visitor()->RecordSlot(weak_ref, slot, target);
} else {
// JSWeakRef points to a potentially dead object. We have to process
// them when we know the liveness of the whole transitive closure.
local_weak_objects_->js_weak_refs_local.Push(weak_ref);
concrete_visitor()->AddWeakReferenceForReferenceSummarizer(weak_ref,
target);
}
}
return size;
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitWeakCell(
Tagged<Map> map, Tagged<WeakCell> weak_cell) {
int size = WeakCell::BodyDescriptor::SizeOf(map, weak_cell);
this->VisitMapPointer(weak_cell);
WeakCell::BodyDescriptor::IterateBody(map, weak_cell, size, this);
Tagged<HeapObject> target = weak_cell->relaxed_target();
Tagged<HeapObject> unregister_token = weak_cell->relaxed_unregister_token();
SynchronizePageAccess(target);
SynchronizePageAccess(unregister_token);
if ((target.InReadOnlySpace() || concrete_visitor()->IsMarked(target)) &&
(unregister_token.InReadOnlySpace() ||
concrete_visitor()->IsMarked(unregister_token))) {
// Record the slots inside the WeakCell, since the IterateBody above
// didn't visit it.
ObjectSlot slot = weak_cell->RawField(WeakCell::kTargetOffset);
concrete_visitor()->RecordSlot(weak_cell, slot, target);
slot = weak_cell->RawField(WeakCell::kUnregisterTokenOffset);
concrete_visitor()->RecordSlot(weak_cell, slot, unregister_token);
} else {
// WeakCell points to a potentially dead object or a dead unregister
// token. We have to process them when we know the liveness of the whole
// transitive closure.
local_weak_objects_->weak_cells_local.Push(weak_cell);
concrete_visitor()->AddWeakReferenceForReferenceSummarizer(weak_cell,
target);
concrete_visitor()->AddWeakReferenceForReferenceSummarizer(
weak_cell, unregister_token);
}
return size;
}
// ===========================================================================
// Custom weakness in descriptor arrays and transition arrays ================
// ===========================================================================
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitDescriptorArrayStrongly(
Tagged<Map> map, Tagged<DescriptorArray> array) {
this->VisitMapPointer(array);
int size = DescriptorArray::BodyDescriptor::SizeOf(map, array);
VisitPointers(array, array->GetFirstPointerSlot(),
array->GetDescriptorSlot(0));
VisitPointers(array, MaybeObjectSlot(array->GetDescriptorSlot(0)),
MaybeObjectSlot(
array->GetDescriptorSlot(array->number_of_descriptors())));
return size;
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitDescriptorArray(
Tagged<Map> map, Tagged<DescriptorArray> array) {
if (!concrete_visitor()->CanUpdateValuesInHeap()) {
// If we cannot update the values in the heap, we just treat the array
// strongly.
return VisitDescriptorArrayStrongly(map, array);
}
// The markbit is not used anymore. This is different from a checked
// transition in that the array is re-added to the worklist and thus there's
// many invocations of this transition. All cases (roots, marking via map,
// write barrier) are handled here as they all update the state accordingly.
const auto [start, end] =
DescriptorArrayMarkingState::AcquireDescriptorRangeToMark(
mark_compact_epoch_, array);
if (start != end) {
DCHECK_LT(start, end);
VisitPointers(array, MaybeObjectSlot(array->GetDescriptorSlot(start)),
MaybeObjectSlot(array->GetDescriptorSlot(end)));
if (start == 0) {
// We are processing the object the first time. Visit the header and
// return a size for accounting.
int size = DescriptorArray::BodyDescriptor::SizeOf(map, array);
VisitPointers(array, array->GetFirstPointerSlot(),
array->GetDescriptorSlot(0));
concrete_visitor()
->template VisitMapPointerIfNeeded<VisitorId::kVisitDescriptorArray>(
array);
return size;
}
}
return 0;
}
template <typename ConcreteVisitor>
void MarkingVisitorBase<ConcreteVisitor>::VisitDescriptorsForMap(
Tagged<Map> map) {
if (!concrete_visitor()->CanUpdateValuesInHeap() || !map->CanTransition())
return;
// Maps that can transition share their descriptor arrays and require
// special visiting logic to avoid memory leaks.
// Since descriptor arrays are potentially shared, ensure that only the
// descriptors that belong to this map are marked. The first time a
// non-empty descriptor array is marked, its header is also visited. The
// slot holding the descriptor array will be implicitly recorded when the
// pointer fields of this map are visited.
Tagged<Object> maybe_descriptors =
TaggedField<Object, Map::kInstanceDescriptorsOffset>::Acquire_Load(
heap_->isolate(), map);
// If the descriptors are a Smi, then this Map is in the process of being
// deserialized, and doesn't yet have an initialized descriptor field.
if (IsSmi(maybe_descriptors)) {
DCHECK_EQ(maybe_descriptors, Smi::uninitialized_deserialization_value());
return;
}
Tagged<DescriptorArray> descriptors =
DescriptorArray::cast(maybe_descriptors);
// Synchronize reading of page flags for tsan.
SynchronizePageAccess(descriptors);
// Normal processing of descriptor arrays through the pointers iteration that
// follows this call:
// - Array in read only space;
// - StrongDescriptor array;
if (descriptors.InReadOnlySpace() || IsStrongDescriptorArray(descriptors)) {
return;
}
const int number_of_own_descriptors = map->NumberOfOwnDescriptors();
if (number_of_own_descriptors) {
// It is possible that the concurrent marker observes the
// number_of_own_descriptors out of sync with the descriptors. In that
// case the marking write barrier for the descriptor array will ensure
// that all required descriptors are marked. The concurrent marker
// just should avoid crashing in that case. That's why we need the
// std::min<int>() below.
const auto descriptors_to_mark = std::min<int>(
number_of_own_descriptors, descriptors->number_of_descriptors());
concrete_visitor()->TryMark(descriptors);
if (DescriptorArrayMarkingState::TryUpdateIndicesToMark(
mark_compact_epoch_, descriptors, descriptors_to_mark)) {
local_marking_worklists_->Push(descriptors);
}
}
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitMap(Tagged<Map> meta_map,
Tagged<Map> map) {
int size = Map::BodyDescriptor::SizeOf(meta_map, map);
VisitDescriptorsForMap(map);
// Mark the pointer fields of the Map. If there is a transitions array, it has
// been marked already, so it is fine that one of these fields contains a
// pointer to it.
Map::BodyDescriptor::IterateBody(meta_map, map, size, this);
return size;
}
template <typename ConcreteVisitor>
int MarkingVisitorBase<ConcreteVisitor>::VisitTransitionArray(
Tagged<Map> map, Tagged<TransitionArray> array) {
this->VisitMapPointer(array);
int size = TransitionArray::BodyDescriptor::SizeOf(map, array);
TransitionArray::BodyDescriptor::IterateBody(map, array, size, this);
local_weak_objects_->transition_arrays_local.Push(array);
return size;
}
} // namespace internal
} // namespace v8
#endif // V8_HEAP_MARKING_VISITOR_INL_H_