1// Copyright 2022 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/heap/cppgc/remembered-set.h" 6 7#include <algorithm> 8 9#include "include/cppgc/visitor.h" 10#include "src/heap/cppgc/heap-object-header.h" 11#include "src/heap/cppgc/heap-page.h" 12#include "src/heap/cppgc/marking-state.h" 13 14namespace cppgc { 15namespace internal { 16 17namespace { 18 19// Visit remembered set that was recorded in the generational barrier. 20void VisitRememberedSlots(const std::set<void*>& slots, const HeapBase& heap, 21 MutatorMarkingState& mutator_marking_state) { 22 for (void* slot : slots) { 23 // Slot must always point to a valid, not freed object. 24 auto& slot_header = BasePage::FromInnerAddress(&heap, slot) 25 ->ObjectHeaderFromInnerAddress(slot); 26 // The age checking in the generational barrier is imprecise, since a card 27 // may have mixed young/old objects. Check here precisely if the object is 28 // old. 29 if (slot_header.IsYoung()) continue; 30 // The design of young generation requires collections to be executed at the 31 // top level (with the guarantee that no objects are currently being in 32 // construction). This can be ensured by running young GCs from safe points 33 // or by reintroducing nested allocation scopes that avoid finalization. 34 DCHECK(!slot_header.template IsInConstruction<AccessMode::kNonAtomic>()); 35 36 void* value = *reinterpret_cast<void**>(slot); 37 // Slot could be updated to nullptr or kSentinelPointer by the mutator. 38 if (value == kSentinelPointer || value == nullptr) continue; 39 40#if DEBUG 41 // Check that the slot can not point to a freed object. 42 HeapObjectHeader& header = 43 BasePage::FromPayload(value)->ObjectHeaderFromInnerAddress(value); 44 DCHECK(!header.IsFree()); 45#endif 46 47 mutator_marking_state.DynamicallyMarkAddress(static_cast<Address>(value)); 48 } 49} 50 51// Visits source objects that were recorded in the generational barrier for 52// slots. 53void VisitRememberedSourceObjects( 54 const std::set<HeapObjectHeader*>& remembered_source_objects, 55 Visitor& visitor) { 56 for (HeapObjectHeader* source_hoh : remembered_source_objects) { 57 DCHECK(source_hoh); 58 // The age checking in the generational barrier is imprecise, since a card 59 // may have mixed young/old objects. Check here precisely if the object is 60 // old. 61 if (source_hoh->IsYoung()) continue; 62 // The design of young generation requires collections to be executed at the 63 // top level (with the guarantee that no objects are currently being in 64 // construction). This can be ensured by running young GCs from safe points 65 // or by reintroducing nested allocation scopes that avoid finalization. 66 DCHECK(!source_hoh->template IsInConstruction<AccessMode::kNonAtomic>()); 67 68 const TraceCallback trace_callback = 69 GlobalGCInfoTable::GCInfoFromIndex(source_hoh->GetGCInfoIndex()).trace; 70 71 // Process eagerly to avoid reaccounting. 72 trace_callback(&visitor, source_hoh->ObjectStart()); 73 } 74} 75 76} // namespace 77 78void OldToNewRememberedSet::AddSlot(void* slot) { 79 remembered_slots_.insert(slot); 80} 81 82void OldToNewRememberedSet::AddSourceObject(HeapObjectHeader& hoh) { 83 remembered_source_objects_.insert(&hoh); 84} 85 86void OldToNewRememberedSet::AddWeakCallback(WeakCallbackItem item) { 87 // TODO(1029379): WeakCallbacks are also executed for weak collections. 88 // Consider splitting weak-callbacks in custom weak callbacks and ones for 89 // collections. 90 remembered_weak_callbacks_.insert(item); 91} 92 93void OldToNewRememberedSet::InvalidateRememberedSlotsInRange(void* begin, 94 void* end) { 95 // TODO(1029379): The 2 binary walks can be optimized with a custom algorithm. 96 auto from = remembered_slots_.lower_bound(begin), 97 to = remembered_slots_.lower_bound(end); 98 remembered_slots_.erase(from, to); 99#if defined(ENABLE_SLOW_DCHECKS) 100 // Check that no remembered slots are referring to the freed area. 101 DCHECK(std::none_of(remembered_slots_.begin(), remembered_slots_.end(), 102 [begin, end](void* slot) { 103 void* value = *reinterpret_cast<void**>(slot); 104 return begin <= value && value < end; 105 })); 106#endif // defined(ENABLE_SLOW_DCHECKS) 107} 108 109void OldToNewRememberedSet::InvalidateRememberedSourceObject( 110 HeapObjectHeader& header) { 111 remembered_source_objects_.erase(&header); 112} 113 114void OldToNewRememberedSet::Visit(Visitor& visitor, 115 MutatorMarkingState& marking_state) { 116 VisitRememberedSlots(remembered_slots_, heap_, marking_state); 117 VisitRememberedSourceObjects(remembered_source_objects_, visitor); 118} 119 120void OldToNewRememberedSet::ExecuteCustomCallbacks(LivenessBroker broker) { 121 for (const auto& callback : remembered_weak_callbacks_) { 122 callback.callback(broker, callback.parameter); 123 } 124} 125 126void OldToNewRememberedSet::ReleaseCustomCallbacks() { 127 remembered_weak_callbacks_.clear(); 128} 129 130void OldToNewRememberedSet::Reset() { 131 remembered_slots_.clear(); 132 remembered_source_objects_.clear(); 133} 134 135} // namespace internal 136} // namespace cppgc 137