OLD | NEW |
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef V8_REMEMBERED_SET_H | 5 #ifndef V8_REMEMBERED_SET_H |
6 #define V8_REMEMBERED_SET_H | 6 #define V8_REMEMBERED_SET_H |
7 | 7 |
8 #include "src/assembler.h" | 8 #include "src/assembler.h" |
9 #include "src/heap/heap.h" | 9 #include "src/heap/heap.h" |
10 #include "src/heap/slot-set.h" | 10 #include "src/heap/slot-set.h" |
11 #include "src/heap/spaces.h" | 11 #include "src/heap/spaces.h" |
12 | 12 |
13 namespace v8 { | 13 namespace v8 { |
14 namespace internal { | 14 namespace internal { |
15 | 15 |
16 enum PointerDirection { OLD_TO_OLD, OLD_TO_NEW }; | |
17 | |
18 // TODO(ulan): Investigate performance of de-templatizing this class. | 16 // TODO(ulan): Investigate performance of de-templatizing this class. |
19 template <PointerDirection direction> | 17 template <RememberedSetType type> |
20 class RememberedSet : public AllStatic { | 18 class RememberedSet : public AllStatic { |
21 public: | 19 public: |
22 // Given a page and a slot in that page, this function adds the slot to the | 20 // Given a page and a slot in that page, this function adds the slot to the |
23 // remembered set. | 21 // remembered set. |
24 static void Insert(MemoryChunk* chunk, Address slot_addr) { | 22 static void Insert(MemoryChunk* chunk, Address slot_addr) { |
25 DCHECK(chunk->Contains(slot_addr)); | 23 DCHECK(chunk->Contains(slot_addr)); |
26 SlotSet* slot_set = GetSlotSet(chunk); | 24 SlotSet* slot_set = chunk->slot_set<type>(); |
27 if (slot_set == nullptr) { | 25 if (slot_set == nullptr) { |
28 slot_set = AllocateSlotSet(chunk); | 26 slot_set = chunk->AllocateSlotSet<type>(); |
29 } | 27 } |
30 uintptr_t offset = slot_addr - chunk->address(); | 28 uintptr_t offset = slot_addr - chunk->address(); |
31 slot_set[offset / Page::kPageSize].Insert(offset % Page::kPageSize); | 29 slot_set[offset / Page::kPageSize].Insert(offset % Page::kPageSize); |
32 } | 30 } |
33 | 31 |
34 // Given a page and a slot in that page, this function returns true if | 32 // Given a page and a slot in that page, this function returns true if |
35 // the remembered set contains the slot. | 33 // the remembered set contains the slot. |
36 static bool Contains(MemoryChunk* chunk, Address slot_addr) { | 34 static bool Contains(MemoryChunk* chunk, Address slot_addr) { |
37 DCHECK(chunk->Contains(slot_addr)); | 35 DCHECK(chunk->Contains(slot_addr)); |
38 SlotSet* slot_set = GetSlotSet(chunk); | 36 SlotSet* slot_set = chunk->slot_set<type>(); |
39 if (slot_set == nullptr) { | 37 if (slot_set == nullptr) { |
40 return false; | 38 return false; |
41 } | 39 } |
42 uintptr_t offset = slot_addr - chunk->address(); | 40 uintptr_t offset = slot_addr - chunk->address(); |
43 return slot_set[offset / Page::kPageSize].Contains(offset % | 41 return slot_set[offset / Page::kPageSize].Contains(offset % |
44 Page::kPageSize); | 42 Page::kPageSize); |
45 } | 43 } |
46 | 44 |
47 // Given a page and a slot in that page, this function removes the slot from | 45 // Given a page and a slot in that page, this function removes the slot from |
48 // the remembered set. | 46 // the remembered set. |
49 // If the slot was never added, then the function does nothing. | 47 // If the slot was never added, then the function does nothing. |
50 static void Remove(MemoryChunk* chunk, Address slot_addr) { | 48 static void Remove(MemoryChunk* chunk, Address slot_addr) { |
51 DCHECK(chunk->Contains(slot_addr)); | 49 DCHECK(chunk->Contains(slot_addr)); |
52 SlotSet* slot_set = GetSlotSet(chunk); | 50 SlotSet* slot_set = chunk->slot_set<type>(); |
53 if (slot_set != nullptr) { | 51 if (slot_set != nullptr) { |
54 uintptr_t offset = slot_addr - chunk->address(); | 52 uintptr_t offset = slot_addr - chunk->address(); |
55 slot_set[offset / Page::kPageSize].Remove(offset % Page::kPageSize); | 53 slot_set[offset / Page::kPageSize].Remove(offset % Page::kPageSize); |
56 } | 54 } |
57 } | 55 } |
58 | 56 |
59 // Given a page and a range of slots in that page, this function removes the | 57 // Given a page and a range of slots in that page, this function removes the |
60 // slots from the remembered set. | 58 // slots from the remembered set. |
61 static void RemoveRange(MemoryChunk* chunk, Address start, Address end, | 59 static void RemoveRange(MemoryChunk* chunk, Address start, Address end, |
62 SlotSet::EmptyBucketMode mode) { | 60 SlotSet::EmptyBucketMode mode) { |
63 SlotSet* slot_set = GetSlotSet(chunk); | 61 SlotSet* slot_set = chunk->slot_set<type>(); |
64 if (slot_set != nullptr) { | 62 if (slot_set != nullptr) { |
65 uintptr_t start_offset = start - chunk->address(); | 63 uintptr_t start_offset = start - chunk->address(); |
66 uintptr_t end_offset = end - chunk->address(); | 64 uintptr_t end_offset = end - chunk->address(); |
67 DCHECK_LT(start_offset, end_offset); | 65 DCHECK_LT(start_offset, end_offset); |
68 if (end_offset < static_cast<uintptr_t>(Page::kPageSize)) { | 66 if (end_offset < static_cast<uintptr_t>(Page::kPageSize)) { |
69 slot_set->RemoveRange(static_cast<int>(start_offset), | 67 slot_set->RemoveRange(static_cast<int>(start_offset), |
70 static_cast<int>(end_offset), mode); | 68 static_cast<int>(end_offset), mode); |
71 } else { | 69 } else { |
72 // The large page has multiple slot sets. | 70 // The large page has multiple slot sets. |
73 // Compute slot set indicies for the range [start_offset, end_offset). | 71 // Compute slot set indicies for the range [start_offset, end_offset). |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
105 heap, [callback](MemoryChunk* chunk) { Iterate(chunk, callback); }); | 103 heap, [callback](MemoryChunk* chunk) { Iterate(chunk, callback); }); |
106 } | 104 } |
107 | 105 |
108 // Iterates over all memory chunks that contains non-empty slot sets. | 106 // Iterates over all memory chunks that contains non-empty slot sets. |
109 // The callback should take (MemoryChunk* chunk) and return void. | 107 // The callback should take (MemoryChunk* chunk) and return void. |
110 template <typename Callback> | 108 template <typename Callback> |
111 static void IterateMemoryChunks(Heap* heap, Callback callback) { | 109 static void IterateMemoryChunks(Heap* heap, Callback callback) { |
112 MemoryChunkIterator it(heap); | 110 MemoryChunkIterator it(heap); |
113 MemoryChunk* chunk; | 111 MemoryChunk* chunk; |
114 while ((chunk = it.next()) != nullptr) { | 112 while ((chunk = it.next()) != nullptr) { |
115 SlotSet* slots = GetSlotSet(chunk); | 113 SlotSet* slots = chunk->slot_set<type>(); |
116 TypedSlotSet* typed_slots = GetTypedSlotSet(chunk); | 114 TypedSlotSet* typed_slots = chunk->typed_slot_set<type>(); |
117 if (slots != nullptr || typed_slots != nullptr) { | 115 if (slots != nullptr || typed_slots != nullptr) { |
118 callback(chunk); | 116 callback(chunk); |
119 } | 117 } |
120 } | 118 } |
121 } | 119 } |
122 | 120 |
123 // Iterates and filters the remembered set in the given memory chunk with | 121 // Iterates and filters the remembered set in the given memory chunk with |
124 // the given callback. The callback should take (Address slot) and return | 122 // the given callback. The callback should take (Address slot) and return |
125 // SlotCallbackResult. | 123 // SlotCallbackResult. |
126 template <typename Callback> | 124 template <typename Callback> |
127 static void Iterate(MemoryChunk* chunk, Callback callback) { | 125 static void Iterate(MemoryChunk* chunk, Callback callback) { |
128 SlotSet* slots = GetSlotSet(chunk); | 126 SlotSet* slots = chunk->slot_set<type>(); |
129 if (slots != nullptr) { | 127 if (slots != nullptr) { |
130 size_t pages = (chunk->size() + Page::kPageSize - 1) / Page::kPageSize; | 128 size_t pages = (chunk->size() + Page::kPageSize - 1) / Page::kPageSize; |
131 int new_count = 0; | 129 int new_count = 0; |
132 for (size_t page = 0; page < pages; page++) { | 130 for (size_t page = 0; page < pages; page++) { |
133 new_count += | 131 new_count += |
134 slots[page].Iterate(callback, SlotSet::PREFREE_EMPTY_BUCKETS); | 132 slots[page].Iterate(callback, SlotSet::PREFREE_EMPTY_BUCKETS); |
135 } | 133 } |
136 // Only old-to-old slot sets are released eagerly. Old-new-slot sets are | 134 // Only old-to-old slot sets are released eagerly. Old-new-slot sets are |
137 // released by the sweeper threads. | 135 // released by the sweeper threads. |
138 if (direction == OLD_TO_OLD && new_count == 0) { | 136 if (type == OLD_TO_OLD && new_count == 0) { |
139 chunk->ReleaseOldToOldSlots(); | 137 chunk->ReleaseSlotSet<OLD_TO_OLD>(); |
140 } | 138 } |
141 } | 139 } |
142 } | 140 } |
143 | 141 |
144 // Given a page and a typed slot in that page, this function adds the slot | 142 // Given a page and a typed slot in that page, this function adds the slot |
145 // to the remembered set. | 143 // to the remembered set. |
146 static void InsertTyped(Page* page, Address host_addr, SlotType slot_type, | 144 static void InsertTyped(Page* page, Address host_addr, SlotType slot_type, |
147 Address slot_addr) { | 145 Address slot_addr) { |
148 TypedSlotSet* slot_set = GetTypedSlotSet(page); | 146 TypedSlotSet* slot_set = page->typed_slot_set<type>(); |
149 if (slot_set == nullptr) { | 147 if (slot_set == nullptr) { |
150 AllocateTypedSlotSet(page); | 148 slot_set = page->AllocateTypedSlotSet<type>(); |
151 slot_set = GetTypedSlotSet(page); | |
152 } | 149 } |
153 if (host_addr == nullptr) { | 150 if (host_addr == nullptr) { |
154 host_addr = page->address(); | 151 host_addr = page->address(); |
155 } | 152 } |
156 uintptr_t offset = slot_addr - page->address(); | 153 uintptr_t offset = slot_addr - page->address(); |
157 uintptr_t host_offset = host_addr - page->address(); | 154 uintptr_t host_offset = host_addr - page->address(); |
158 DCHECK_LT(offset, static_cast<uintptr_t>(TypedSlotSet::kMaxOffset)); | 155 DCHECK_LT(offset, static_cast<uintptr_t>(TypedSlotSet::kMaxOffset)); |
159 DCHECK_LT(host_offset, static_cast<uintptr_t>(TypedSlotSet::kMaxOffset)); | 156 DCHECK_LT(host_offset, static_cast<uintptr_t>(TypedSlotSet::kMaxOffset)); |
160 slot_set->Insert(slot_type, static_cast<uint32_t>(host_offset), | 157 slot_set->Insert(slot_type, static_cast<uint32_t>(host_offset), |
161 static_cast<uint32_t>(offset)); | 158 static_cast<uint32_t>(offset)); |
162 } | 159 } |
163 | 160 |
164 // Given a page and a range of typed slots in that page, this function removes | 161 // Given a page and a range of typed slots in that page, this function removes |
165 // the slots from the remembered set. | 162 // the slots from the remembered set. |
166 static void RemoveRangeTyped(MemoryChunk* page, Address start, Address end) { | 163 static void RemoveRangeTyped(MemoryChunk* page, Address start, Address end) { |
167 TypedSlotSet* slots = GetTypedSlotSet(page); | 164 TypedSlotSet* slots = page->typed_slot_set<type>(); |
168 if (slots != nullptr) { | 165 if (slots != nullptr) { |
169 slots->Iterate( | 166 slots->Iterate( |
170 [start, end](SlotType slot_type, Address host_addr, | 167 [start, end](SlotType slot_type, Address host_addr, |
171 Address slot_addr) { | 168 Address slot_addr) { |
172 return start <= slot_addr && slot_addr < end ? REMOVE_SLOT | 169 return start <= slot_addr && slot_addr < end ? REMOVE_SLOT |
173 : KEEP_SLOT; | 170 : KEEP_SLOT; |
174 }, | 171 }, |
175 TypedSlotSet::PREFREE_EMPTY_CHUNKS); | 172 TypedSlotSet::PREFREE_EMPTY_CHUNKS); |
176 } | 173 } |
177 } | 174 } |
178 | 175 |
179 // Iterates and filters the remembered set with the given callback. | 176 // Iterates and filters the remembered set with the given callback. |
180 // The callback should take (SlotType slot_type, SlotAddress slot) and return | 177 // The callback should take (SlotType slot_type, SlotAddress slot) and return |
181 // SlotCallbackResult. | 178 // SlotCallbackResult. |
182 template <typename Callback> | 179 template <typename Callback> |
183 static void IterateTyped(Heap* heap, Callback callback) { | 180 static void IterateTyped(Heap* heap, Callback callback) { |
184 IterateMemoryChunks(heap, [callback](MemoryChunk* chunk) { | 181 IterateMemoryChunks(heap, [callback](MemoryChunk* chunk) { |
185 IterateTyped(chunk, callback); | 182 IterateTyped(chunk, callback); |
186 }); | 183 }); |
187 } | 184 } |
188 | 185 |
189 // Iterates and filters typed old to old pointers in the given memory chunk | 186 // Iterates and filters typed old to old pointers in the given memory chunk |
190 // with the given callback. The callback should take (SlotType slot_type, | 187 // with the given callback. The callback should take (SlotType slot_type, |
191 // Address slot_addr) and return SlotCallbackResult. | 188 // Address slot_addr) and return SlotCallbackResult. |
192 template <typename Callback> | 189 template <typename Callback> |
193 static void IterateTyped(MemoryChunk* chunk, Callback callback) { | 190 static void IterateTyped(MemoryChunk* chunk, Callback callback) { |
194 TypedSlotSet* slots = GetTypedSlotSet(chunk); | 191 TypedSlotSet* slots = chunk->typed_slot_set<type>(); |
195 if (slots != nullptr) { | 192 if (slots != nullptr) { |
196 int new_count = slots->Iterate(callback, TypedSlotSet::KEEP_EMPTY_CHUNKS); | 193 int new_count = slots->Iterate(callback, TypedSlotSet::KEEP_EMPTY_CHUNKS); |
197 if (new_count == 0) { | 194 if (new_count == 0) { |
198 ReleaseTypedSlotSet(chunk); | 195 chunk->ReleaseTypedSlotSet<type>(); |
199 } | 196 } |
200 } | 197 } |
201 } | 198 } |
202 | 199 |
203 // Clear all old to old slots from the remembered set. | 200 // Clear all old to old slots from the remembered set. |
204 static void ClearAll(Heap* heap) { | 201 static void ClearAll(Heap* heap) { |
205 STATIC_ASSERT(direction == OLD_TO_OLD); | 202 STATIC_ASSERT(type == OLD_TO_OLD); |
206 MemoryChunkIterator it(heap); | 203 MemoryChunkIterator it(heap); |
207 MemoryChunk* chunk; | 204 MemoryChunk* chunk; |
208 while ((chunk = it.next()) != nullptr) { | 205 while ((chunk = it.next()) != nullptr) { |
209 chunk->ReleaseOldToOldSlots(); | 206 chunk->ReleaseSlotSet<OLD_TO_OLD>(); |
210 chunk->ReleaseTypedOldToOldSlots(); | 207 chunk->ReleaseTypedSlotSet<OLD_TO_OLD>(); |
211 } | 208 } |
212 } | 209 } |
213 | 210 |
214 // Eliminates all stale slots from the remembered set, i.e. | 211 // Eliminates all stale slots from the remembered set, i.e. |
215 // slots that are not part of live objects anymore. This method must be | 212 // slots that are not part of live objects anymore. This method must be |
216 // called after marking, when the whole transitive closure is known and | 213 // called after marking, when the whole transitive closure is known and |
217 // must be called before sweeping when mark bits are still intact. | 214 // must be called before sweeping when mark bits are still intact. |
218 static void ClearInvalidTypedSlots(Heap* heap, MemoryChunk* chunk); | 215 static void ClearInvalidTypedSlots(Heap* heap, MemoryChunk* chunk); |
219 | 216 |
220 private: | 217 private: |
221 static SlotSet* GetSlotSet(MemoryChunk* chunk) { | |
222 if (direction == OLD_TO_OLD) { | |
223 return chunk->old_to_old_slots(); | |
224 } else { | |
225 return chunk->old_to_new_slots(); | |
226 } | |
227 } | |
228 | |
229 static TypedSlotSet* GetTypedSlotSet(MemoryChunk* chunk) { | |
230 if (direction == OLD_TO_OLD) { | |
231 return chunk->typed_old_to_old_slots(); | |
232 } else { | |
233 return chunk->typed_old_to_new_slots(); | |
234 } | |
235 } | |
236 | |
237 static void ReleaseTypedSlotSet(MemoryChunk* chunk) { | |
238 if (direction == OLD_TO_OLD) { | |
239 chunk->ReleaseTypedOldToOldSlots(); | |
240 } | |
241 } | |
242 | |
243 static SlotSet* AllocateSlotSet(MemoryChunk* chunk) { | |
244 if (direction == OLD_TO_OLD) { | |
245 chunk->AllocateOldToOldSlots(); | |
246 return chunk->old_to_old_slots(); | |
247 } else { | |
248 chunk->AllocateOldToNewSlots(); | |
249 return chunk->old_to_new_slots(); | |
250 } | |
251 } | |
252 | |
253 static TypedSlotSet* AllocateTypedSlotSet(MemoryChunk* chunk) { | |
254 if (direction == OLD_TO_OLD) { | |
255 chunk->AllocateTypedOldToOldSlots(); | |
256 return chunk->typed_old_to_old_slots(); | |
257 } else { | |
258 chunk->AllocateTypedOldToNewSlots(); | |
259 return chunk->typed_old_to_new_slots(); | |
260 } | |
261 } | |
262 | |
263 static bool IsValidSlot(Heap* heap, MemoryChunk* chunk, Object** slot); | 218 static bool IsValidSlot(Heap* heap, MemoryChunk* chunk, Object** slot); |
264 }; | 219 }; |
265 | 220 |
266 class UpdateTypedSlotHelper { | 221 class UpdateTypedSlotHelper { |
267 public: | 222 public: |
268 // Updates a cell slot using an untyped slot callback. | 223 // Updates a cell slot using an untyped slot callback. |
269 // The callback accepts Object** and returns SlotCallbackResult. | 224 // The callback accepts Object** and returns SlotCallbackResult. |
270 template <typename Callback> | 225 template <typename Callback> |
271 static SlotCallbackResult UpdateCell(RelocInfo* rinfo, Callback callback) { | 226 static SlotCallbackResult UpdateCell(RelocInfo* rinfo, Callback callback) { |
272 DCHECK(rinfo->rmode() == RelocInfo::CELL); | 227 DCHECK(rinfo->rmode() == RelocInfo::CELL); |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
392 return DEBUG_TARGET_SLOT; | 347 return DEBUG_TARGET_SLOT; |
393 } | 348 } |
394 UNREACHABLE(); | 349 UNREACHABLE(); |
395 return CLEARED_SLOT; | 350 return CLEARED_SLOT; |
396 } | 351 } |
397 | 352 |
398 } // namespace internal | 353 } // namespace internal |
399 } // namespace v8 | 354 } // namespace v8 |
400 | 355 |
401 #endif // V8_REMEMBERED_SET_H | 356 #endif // V8_REMEMBERED_SET_H |
OLD | NEW |