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