// Copyright 2020 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. #include "src/heap/marking-barrier.h" #include "src/base/logging.h" #include "src/heap/heap-inl.h" #include "src/heap/heap-write-barrier.h" #include "src/heap/heap.h" #include "src/heap/incremental-marking-inl.h" #include "src/heap/incremental-marking.h" #include "src/heap/mark-compact-inl.h" #include "src/heap/mark-compact.h" #include "src/heap/marking-barrier-inl.h" #include "src/heap/marking-worklist-inl.h" #include "src/heap/marking-worklist.h" #include "src/heap/safepoint.h" #include "src/objects/heap-object.h" #include "src/objects/js-array-buffer.h" namespace v8 { namespace internal { MarkingBarrier::MarkingBarrier(Heap* heap) : heap_(heap), collector_(heap_->mark_compact_collector()), incremental_marking_(heap_->incremental_marking()), worklist_(collector_->marking_worklists()->shared()), marking_state_(heap_->isolate()), is_main_thread_barrier_(true), is_shared_heap_(heap_->IsShared()) {} MarkingBarrier::MarkingBarrier(LocalHeap* local_heap) : heap_(local_heap->heap()), collector_(heap_->mark_compact_collector()), incremental_marking_(nullptr), worklist_(collector_->marking_worklists()->shared()), marking_state_(heap_->isolate()), is_main_thread_barrier_(false), is_shared_heap_(heap_->IsShared()) {} MarkingBarrier::~MarkingBarrier() { DCHECK(worklist_.IsLocalEmpty()); } void MarkingBarrier::Write(HeapObject host, HeapObjectSlot slot, HeapObject value) { DCHECK(IsCurrentMarkingBarrier()); if (MarkValue(host, value)) { if (is_compacting_ && slot.address()) { collector_->RecordSlot(host, slot, value); } } } void MarkingBarrier::WriteWithoutHost(HeapObject value) { DCHECK(is_main_thread_barrier_); if (WhiteToGreyAndPush(value)) { incremental_marking_->RestartIfNotMarking(); if (V8_UNLIKELY(FLAG_track_retaining_path)) { heap_->AddRetainingRoot(Root::kWriteBarrier, value); } } } void MarkingBarrier::Write(Code host, RelocInfo* reloc_info, HeapObject value) { DCHECK(IsCurrentMarkingBarrier()); if (MarkValue(host, value)) { if (is_compacting_) { if (is_main_thread_barrier_) { // An optimization to avoid allocating additional typed slots for the // main thread. collector_->RecordRelocSlot(host, reloc_info, value); } else { RecordRelocSlot(host, reloc_info, value); } } } } void MarkingBarrier::Write(JSArrayBuffer host, ArrayBufferExtension* extension) { DCHECK(IsCurrentMarkingBarrier()); if (!V8_CONCURRENT_MARKING_BOOL && !marking_state_.IsBlack(host)) { // The extension will be marked when the marker visits the host object. return; } extension->Mark(); } void MarkingBarrier::Write(DescriptorArray descriptor_array, int number_of_own_descriptors) { DCHECK(IsCurrentMarkingBarrier()); DCHECK(IsReadOnlyHeapObject(descriptor_array.map())); // The DescriptorArray needs to be marked black here to ensure that slots are // recorded by the Scavenger in case the DescriptorArray is promoted while // incremental marking is running. This is needed as the regular marking // visitor does not re-process any already marked descriptors. If we don't // mark it black here, the Scavenger may promote a DescriptorArray and any // already marked descriptors will not have any slots recorded. if (!marking_state_.IsBlack(descriptor_array)) { marking_state_.WhiteToGrey(descriptor_array); marking_state_.GreyToBlack(descriptor_array); MarkRange(descriptor_array, descriptor_array.GetFirstPointerSlot(), descriptor_array.GetDescriptorSlot(0)); } const int16_t old_marked = descriptor_array.UpdateNumberOfMarkedDescriptors( collector_->epoch(), number_of_own_descriptors); if (old_marked < number_of_own_descriptors) { // This marks the range from [old_marked, number_of_own_descriptors) instead // of registering weak slots which may temporarily hold alive more objects // for the current GC cycle. Weakness is not needed for actual trimming, see // `MarkCompactCollector::TrimDescriptorArray()`. MarkRange(descriptor_array, MaybeObjectSlot(descriptor_array.GetDescriptorSlot(old_marked)), MaybeObjectSlot(descriptor_array.GetDescriptorSlot( number_of_own_descriptors))); } } void MarkingBarrier::RecordRelocSlot(Code host, RelocInfo* rinfo, HeapObject target) { DCHECK(IsCurrentMarkingBarrier()); MarkCompactCollector::RecordRelocSlotInfo info = MarkCompactCollector::PrepareRecordRelocSlot(host, rinfo, target); if (info.should_record) { auto& typed_slots = typed_slots_map_[info.memory_chunk]; if (!typed_slots) { typed_slots.reset(new TypedSlots()); } typed_slots->Insert(info.slot_type, info.offset); } } // static void MarkingBarrier::ActivateAll(Heap* heap, bool is_compacting) { heap->marking_barrier()->Activate(is_compacting); heap->safepoint()->IterateLocalHeaps([is_compacting](LocalHeap* local_heap) { local_heap->marking_barrier()->Activate(is_compacting); }); } // static void MarkingBarrier::DeactivateAll(Heap* heap) { heap->marking_barrier()->Deactivate(); heap->safepoint()->IterateLocalHeaps([](LocalHeap* local_heap) { local_heap->marking_barrier()->Deactivate(); }); } // static void MarkingBarrier::PublishAll(Heap* heap) { heap->marking_barrier()->Publish(); heap->safepoint()->IterateLocalHeaps( [](LocalHeap* local_heap) { local_heap->marking_barrier()->Publish(); }); } void MarkingBarrier::Publish() { if (is_activated_) { worklist_.Publish(); for (auto& it : typed_slots_map_) { MemoryChunk* memory_chunk = it.first; // Access to TypeSlots need to be protected, since LocalHeaps might // publish code in the background thread. base::Optional opt_guard; if (FLAG_concurrent_sparkplug) { opt_guard.emplace(memory_chunk->mutex()); } std::unique_ptr& typed_slots = it.second; RememberedSet::MergeTyped(memory_chunk, std::move(typed_slots)); } typed_slots_map_.clear(); } } void MarkingBarrier::DeactivateSpace(PagedSpace* space) { DCHECK(is_main_thread_barrier_); for (Page* p : *space) { p->SetOldGenerationPageFlags(false); } } void MarkingBarrier::DeactivateSpace(NewSpace* space) { DCHECK(is_main_thread_barrier_); for (Page* p : *space) { p->SetYoungGenerationPageFlags(false); } } void MarkingBarrier::Deactivate() { is_activated_ = false; is_compacting_ = false; if (is_main_thread_barrier_) { DeactivateSpace(heap_->old_space()); DeactivateSpace(heap_->map_space()); DeactivateSpace(heap_->code_space()); DeactivateSpace(heap_->new_space()); for (LargePage* p : *heap_->new_lo_space()) { p->SetYoungGenerationPageFlags(false); DCHECK(p->IsLargePage()); } for (LargePage* p : *heap_->lo_space()) { p->SetOldGenerationPageFlags(false); } for (LargePage* p : *heap_->code_lo_space()) { p->SetOldGenerationPageFlags(false); } } DCHECK(typed_slots_map_.empty()); DCHECK(worklist_.IsLocalEmpty()); } void MarkingBarrier::ActivateSpace(PagedSpace* space) { DCHECK(is_main_thread_barrier_); for (Page* p : *space) { p->SetOldGenerationPageFlags(true); } } void MarkingBarrier::ActivateSpace(NewSpace* space) { DCHECK(is_main_thread_barrier_); for (Page* p : *space) { p->SetYoungGenerationPageFlags(true); } } void MarkingBarrier::Activate(bool is_compacting) { DCHECK(!is_activated_); DCHECK(worklist_.IsLocalEmpty()); is_compacting_ = is_compacting; is_activated_ = true; if (is_main_thread_barrier_) { ActivateSpace(heap_->old_space()); ActivateSpace(heap_->map_space()); ActivateSpace(heap_->code_space()); ActivateSpace(heap_->new_space()); for (LargePage* p : *heap_->new_lo_space()) { p->SetYoungGenerationPageFlags(true); DCHECK(p->IsLargePage()); } for (LargePage* p : *heap_->lo_space()) { p->SetOldGenerationPageFlags(true); } for (LargePage* p : *heap_->code_lo_space()) { p->SetOldGenerationPageFlags(true); } } } bool MarkingBarrier::IsCurrentMarkingBarrier() { return WriteBarrier::CurrentMarkingBarrier(heap_) == this; } } // namespace internal } // namespace v8