remembered-set.cc 6.02 KB
Newer Older
1 2 3 4 5 6 7 8
// Copyright 2022 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/cppgc/remembered-set.h"

#include <algorithm>

9
#include "include/cppgc/member.h"
10
#include "include/cppgc/visitor.h"
11
#include "src/heap/cppgc/heap-base.h"
12 13 14 15 16 17 18 19 20 21
#include "src/heap/cppgc/heap-object-header.h"
#include "src/heap/cppgc/heap-page.h"
#include "src/heap/cppgc/marking-state.h"

namespace cppgc {
namespace internal {

namespace {

// Visit remembered set that was recorded in the generational barrier.
22
void VisitRememberedSlots(const std::set<void*>& slots, const HeapBase& heap,
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
                          MutatorMarkingState& mutator_marking_state) {
  for (void* slot : slots) {
    // Slot must always point to a valid, not freed object.
    auto& slot_header = BasePage::FromInnerAddress(&heap, slot)
                            ->ObjectHeaderFromInnerAddress(slot);
    // The age checking in the generational barrier is imprecise, since a card
    // may have mixed young/old objects. Check here precisely if the object is
    // old.
    if (slot_header.IsYoung()) continue;
    // The design of young generation requires collections to be executed at the
    // top level (with the guarantee that no objects are currently being in
    // construction). This can be ensured by running young GCs from safe points
    // or by reintroducing nested allocation scopes that avoid finalization.
    DCHECK(!slot_header.template IsInConstruction<AccessMode::kNonAtomic>());

38 39 40 41 42
#if defined(CPPGC_POINTER_COMPRESSION)
    // Transform slot.
    void* value =
        CompressedPointer::Decompress(*reinterpret_cast<uint32_t*>(slot));
#else   // !defined(CPPGC_POINTER_COMPRESSION)
43
    void* value = *reinterpret_cast<void**>(slot);
44 45
#endif  // !defined(CPPGC_POINTER_COMPRESSION)

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
    // Slot could be updated to nullptr or kSentinelPointer by the mutator.
    if (value == kSentinelPointer || value == nullptr) continue;

#if DEBUG
    // Check that the slot can not point to a freed object.
    HeapObjectHeader& header =
        BasePage::FromPayload(value)->ObjectHeaderFromInnerAddress(value);
    DCHECK(!header.IsFree());
#endif

    mutator_marking_state.DynamicallyMarkAddress(static_cast<Address>(value));
  }
}

// Visits source objects that were recorded in the generational barrier for
// slots.
void VisitRememberedSourceObjects(
63 64
    const std::set<HeapObjectHeader*>& remembered_source_objects,
    Visitor& visitor) {
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
  for (HeapObjectHeader* source_hoh : remembered_source_objects) {
    DCHECK(source_hoh);
    // The age checking in the generational barrier is imprecise, since a card
    // may have mixed young/old objects. Check here precisely if the object is
    // old.
    if (source_hoh->IsYoung()) continue;
    // The design of young generation requires collections to be executed at the
    // top level (with the guarantee that no objects are currently being in
    // construction). This can be ensured by running young GCs from safe points
    // or by reintroducing nested allocation scopes that avoid finalization.
    DCHECK(!source_hoh->template IsInConstruction<AccessMode::kNonAtomic>());

    const TraceCallback trace_callback =
        GlobalGCInfoTable::GCInfoFromIndex(source_hoh->GetGCInfoIndex()).trace;

    // Process eagerly to avoid reaccounting.
    trace_callback(&visitor, source_hoh->ObjectStart());
  }
}

}  // namespace

void OldToNewRememberedSet::AddSlot(void* slot) {
88
  DCHECK(heap_.generational_gc_supported());
89 90 91 92
  remembered_slots_.insert(slot);
}

void OldToNewRememberedSet::AddSourceObject(HeapObjectHeader& hoh) {
93
  DCHECK(heap_.generational_gc_supported());
94 95 96
  remembered_source_objects_.insert(&hoh);
}

97
void OldToNewRememberedSet::AddWeakCallback(WeakCallbackItem item) {
98
  DCHECK(heap_.generational_gc_supported());
99 100 101
  // TODO(1029379): WeakCallbacks are also executed for weak collections.
  // Consider splitting weak-callbacks in custom weak callbacks and ones for
  // collections.
102 103 104
  remembered_weak_callbacks_.insert(item);
}

105 106
void OldToNewRememberedSet::InvalidateRememberedSlotsInRange(void* begin,
                                                             void* end) {
107
  DCHECK(heap_.generational_gc_supported());
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
  // TODO(1029379): The 2 binary walks can be optimized with a custom algorithm.
  auto from = remembered_slots_.lower_bound(begin),
       to = remembered_slots_.lower_bound(end);
  remembered_slots_.erase(from, to);
#if defined(ENABLE_SLOW_DCHECKS)
  // Check that no remembered slots are referring to the freed area.
  DCHECK(std::none_of(remembered_slots_.begin(), remembered_slots_.end(),
                      [begin, end](void* slot) {
                        void* value = *reinterpret_cast<void**>(slot);
                        return begin <= value && value < end;
                      }));
#endif  // defined(ENABLE_SLOW_DCHECKS)
}

void OldToNewRememberedSet::InvalidateRememberedSourceObject(
    HeapObjectHeader& header) {
124
  DCHECK(heap_.generational_gc_supported());
125 126 127 128 129
  remembered_source_objects_.erase(&header);
}

void OldToNewRememberedSet::Visit(Visitor& visitor,
                                  MutatorMarkingState& marking_state) {
130
  DCHECK(heap_.generational_gc_supported());
131 132 133 134
  VisitRememberedSlots(remembered_slots_, heap_, marking_state);
  VisitRememberedSourceObjects(remembered_source_objects_, visitor);
}

135
void OldToNewRememberedSet::ExecuteCustomCallbacks(LivenessBroker broker) {
136
  DCHECK(heap_.generational_gc_supported());
137 138 139 140 141 142
  for (const auto& callback : remembered_weak_callbacks_) {
    callback.callback(broker, callback.parameter);
  }
}

void OldToNewRememberedSet::ReleaseCustomCallbacks() {
143
  DCHECK(heap_.generational_gc_supported());
144 145 146
  remembered_weak_callbacks_.clear();
}

147
void OldToNewRememberedSet::Reset() {
148
  DCHECK(heap_.generational_gc_supported());
149 150 151 152
  remembered_slots_.clear();
  remembered_source_objects_.clear();
}

153 154 155 156 157
bool OldToNewRememberedSet::IsEmpty() const {
  return remembered_slots_.empty() && remembered_source_objects_.empty() &&
         remembered_weak_callbacks_.empty();
}

158 159
}  // namespace internal
}  // namespace cppgc