v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
cppheap-pointer-table.cc
Go to the documentation of this file.
1// Copyright 2024 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
6
10
11#ifdef V8_COMPRESS_POINTERS
12
13namespace v8 {
14namespace internal {
15
16// TODO(saelo): Reduce duplication with EPT::SweepAndCompact.
17uint32_t CppHeapPointerTable::SweepAndCompact(Space* space,
18 Counters* counters) {
19 DCHECK(space->BelongsTo(this));
20
21 // Lock the space. Technically this is not necessary since no other thread can
22 // allocate entries at this point, but some of the methods we call on the
23 // space assert that the lock is held.
24 base::MutexGuard guard(&space->mutex_);
25 // Same for the invalidated fields mutex.
26 base::MutexGuard invalidated_fields_guard(&space->invalidated_fields_mutex_);
27
28 // There must not be any entry allocations while the table is being swept as
29 // that would not be safe. Set the freelist to this special marker value to
30 // easily catch any violation of this requirement.
31 space->freelist_head_.store(kEntryAllocationIsForbiddenMarker,
32 std::memory_order_relaxed);
33
34 // When compacting, we can compute the number of unused segments at the end of
35 // the table and skip those during sweeping.
36 uint32_t start_of_evacuation_area =
37 space->start_of_evacuation_area_.load(std::memory_order_relaxed);
38 bool evacuation_was_successful = false;
39 if (space->IsCompacting()) {
40 if (space->CompactingWasAborted()) {
41 // Extract the original start_of_evacuation_area value so that the
42 // DCHECKs below and in TryResolveEvacuationEntryDuringSweeping work.
43 start_of_evacuation_area &= ~Space::kCompactionAbortedMarker;
44 } else {
45 evacuation_was_successful = true;
46 }
47 DCHECK(IsAligned(start_of_evacuation_area, kEntriesPerSegment));
48
49 space->StopCompacting();
50 }
51
52 // Sweep top to bottom and rebuild the freelist from newly dead and
53 // previously freed entries while also clearing the marking bit on live
54 // entries and resolving evacuation entries table when compacting the table.
55 // This way, the freelist ends up sorted by index which already makes the
56 // table somewhat self-compacting and is required for the compaction
57 // algorithm so that evacuated entries are evacuated to the start of a space.
58 // This method must run either on the mutator thread or while the mutator is
59 // stopped.
60 uint32_t current_freelist_head = 0;
61 uint32_t current_freelist_length = 0;
62 auto AddToFreelist = [&](uint32_t entry_index) {
63 at(entry_index).MakeFreelistEntry(current_freelist_head);
64 current_freelist_head = entry_index;
65 current_freelist_length++;
66 };
67
68 std::vector<Segment> segments_to_deallocate;
69 for (auto segment : base::Reversed(space->segments_)) {
70 bool segment_will_be_evacuated =
71 evacuation_was_successful &&
72 segment.first_entry() >= start_of_evacuation_area;
73 // Remember the state of the freelist before this segment in case this
74 // segment turns out to be completely empty and we deallocate it.
75 uint32_t previous_freelist_head = current_freelist_head;
76 uint32_t previous_freelist_length = current_freelist_length;
77
78 // Process every entry in this segment, again going top to bottom.
79 for (uint32_t i = segment.last_entry(); i >= segment.first_entry(); i--) {
80 auto payload = at(i).GetRawPayload();
81 if (payload.ContainsEvacuationEntry()) {
82 // Segments that will be evacuated cannot contain evacuation entries
83 // into which other entries would be evacuated.
84 DCHECK(!segment_will_be_evacuated);
85
86 // An evacuation entry contains the address of the slot that owns the
87 // entry that is to be evacuated.
88 Address handle_location =
89 payload.ExtractEvacuationEntryHandleLocation();
90
91 // The CppHeapPointerTable does not support field invalidation.
92 DCHECK(!space->FieldWasInvalidated(handle_location));
93
94 // Resolve the evacuation entry: take the pointer to the handle from the
95 // evacuation entry, copy the entry to its new location, and finally
96 // update the handle to point to the new entry.
97 //
98 // While we now know that the entry being evacuated is free, we don't
99 // add it to (the start of) the freelist because that would immediately
100 // cause new fragmentation when the next entry is allocated. Instead, we
101 // assume that the segments out of which entries are evacuated will all
102 // be decommitted anyway after this loop, which is usually the case
103 // unless compaction was already aborted during marking.
104 ResolveEvacuationEntryDuringSweeping(
105 i, reinterpret_cast<CppHeapPointerHandle*>(handle_location),
106 start_of_evacuation_area);
107
108 // The entry must now contain a pointer and be unmarked as the entry
109 // that was evacuated must have been processed already (it is in an
110 // evacuated segment, which are processed first as they are at the end
111 // of the space). This will have cleared the marking bit.
112 DCHECK(at(i).GetRawPayload().ContainsPointer());
113 DCHECK(!at(i).GetRawPayload().HasMarkBitSet());
114 } else if (!payload.HasMarkBitSet()) {
115 AddToFreelist(i);
116 } else {
117 auto new_payload = payload;
118 new_payload.ClearMarkBit();
119 at(i).SetRawPayload(new_payload);
120 }
121
122 // We must have resolved all evacuation entries. Otherwise, we'll try to
123 // process them again during the next GC, which would cause problems.
124 DCHECK(!at(i).HasEvacuationEntry());
125 }
126
127 // If a segment is completely empty, or if all live entries will be
128 // evacuated out of it at the end of this loop, free the segment.
129 // Note: for segments that will be evacuated, we could avoid building up a
130 // freelist, but it's probably not worth the effort.
131 uint32_t free_entries = current_freelist_length - previous_freelist_length;
132 bool segment_is_empty = free_entries == kEntriesPerSegment;
133 if (segment_is_empty || segment_will_be_evacuated) {
134 segments_to_deallocate.push_back(segment);
135 // Restore the state of the freelist before this segment.
136 current_freelist_head = previous_freelist_head;
137 current_freelist_length = previous_freelist_length;
138 }
139 }
140
141 // We cannot deallocate the segments during the above loop, so do it now.
142 for (auto segment : segments_to_deallocate) {
143 FreeTableSegment(segment);
144 space->segments_.erase(segment);
145 }
146
147 FreelistHead new_freelist(current_freelist_head, current_freelist_length);
148 space->freelist_head_.store(new_freelist, std::memory_order_release);
149 DCHECK_EQ(space->freelist_length(), current_freelist_length);
150
151 uint32_t num_live_entries = space->capacity() - current_freelist_length;
152 counters->cppheap_pointers_count()->AddSample(num_live_entries);
153 return num_live_entries;
154}
155
156void CppHeapPointerTable::ResolveEvacuationEntryDuringSweeping(
157 uint32_t new_index, CppHeapPointerHandle* handle_location,
158 uint32_t start_of_evacuation_area) {
159 CppHeapPointerHandle old_handle = *handle_location;
160 CHECK(IsValidHandle(old_handle));
161
162 uint32_t old_index = HandleToIndex(old_handle);
163 CppHeapPointerHandle new_handle = IndexToHandle(new_index);
164
165 // The compaction algorithm always moves an entry from the evacuation area to
166 // the front of the table. These DCHECKs verify this invariant.
167 DCHECK_GE(old_index, start_of_evacuation_area);
168 DCHECK_LT(new_index, start_of_evacuation_area);
169 auto& new_entry = at(new_index);
170 at(old_index).Evacuate(new_entry);
171 *handle_location = new_handle;
172}
173
174} // namespace internal
175} // namespace v8
176
177#endif // V8_COMPRESS_POINTERS
auto Reversed(T &t)
Definition iterator.h:105
LockGuard< Mutex > MutexGuard
Definition mutex.h:219
uint32_t CppHeapPointerHandle
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
constexpr bool IsAligned(T value, U alignment)
Definition macros.h:403