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/heap/heap.h" | 9 #include "src/heap/heap.h" |
9 #include "src/heap/slot-set.h" | 10 #include "src/heap/slot-set.h" |
10 #include "src/heap/spaces.h" | 11 #include "src/heap/spaces.h" |
11 | 12 |
12 namespace v8 { | 13 namespace v8 { |
13 namespace internal { | 14 namespace internal { |
14 | 15 |
15 enum PointerDirection { OLD_TO_OLD, OLD_TO_NEW }; | 16 enum PointerDirection { OLD_TO_OLD, OLD_TO_NEW }; |
16 | 17 |
| 18 // TODO(ulan): Investigate performance of de-templatizing this class. |
17 template <PointerDirection direction> | 19 template <PointerDirection direction> |
18 class RememberedSet { | 20 class RememberedSet { |
19 public: | 21 public: |
20 // Given a page and a slot in that page, this function adds the slot to the | 22 // Given a page and a slot in that page, this function adds the slot to the |
21 // remembered set. | 23 // remembered set. |
22 static void Insert(Page* page, Address slot_addr) { | 24 static void Insert(Page* page, Address slot_addr) { |
23 DCHECK(page->Contains(slot_addr)); | 25 DCHECK(page->Contains(slot_addr)); |
24 SlotSet* slot_set = GetSlotSet(page); | 26 SlotSet* slot_set = GetSlotSet(page); |
25 if (slot_set == nullptr) { | 27 if (slot_set == nullptr) { |
26 slot_set = AllocateSlotSet(page); | 28 slot_set = AllocateSlotSet(page); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 template <typename Callback> | 62 template <typename Callback> |
61 static void Iterate(Heap* heap, Callback callback) { | 63 static void Iterate(Heap* heap, Callback callback) { |
62 IterateMemoryChunks( | 64 IterateMemoryChunks( |
63 heap, [callback](MemoryChunk* chunk) { Iterate(chunk, callback); }); | 65 heap, [callback](MemoryChunk* chunk) { Iterate(chunk, callback); }); |
64 } | 66 } |
65 | 67 |
66 // Iterates over all memory chunks that contains non-empty slot sets. | 68 // Iterates over all memory chunks that contains non-empty slot sets. |
67 // The callback should take (MemoryChunk* chunk) and return void. | 69 // The callback should take (MemoryChunk* chunk) and return void. |
68 template <typename Callback> | 70 template <typename Callback> |
69 static void IterateMemoryChunks(Heap* heap, Callback callback) { | 71 static void IterateMemoryChunks(Heap* heap, Callback callback) { |
70 MemoryChunkIterator it(heap, direction == OLD_TO_OLD | 72 MemoryChunkIterator it(heap); |
71 ? MemoryChunkIterator::ALL | |
72 : MemoryChunkIterator::ALL_BUT_CODE_SPACE); | |
73 MemoryChunk* chunk; | 73 MemoryChunk* chunk; |
74 while ((chunk = it.next()) != nullptr) { | 74 while ((chunk = it.next()) != nullptr) { |
75 SlotSet* slots = GetSlotSet(chunk); | 75 SlotSet* slots = GetSlotSet(chunk); |
76 TypedSlotSet* typed_slots = GetTypedSlotSet(chunk); | 76 TypedSlotSet* typed_slots = GetTypedSlotSet(chunk); |
77 if (slots != nullptr || typed_slots != nullptr) { | 77 if (slots != nullptr || typed_slots != nullptr) { |
78 callback(chunk); | 78 callback(chunk); |
79 } | 79 } |
80 } | 80 } |
81 } | 81 } |
82 | 82 |
(...skipping 11 matching lines...) Expand all Loading... |
94 } | 94 } |
95 if (new_count == 0) { | 95 if (new_count == 0) { |
96 ReleaseSlotSet(chunk); | 96 ReleaseSlotSet(chunk); |
97 } | 97 } |
98 } | 98 } |
99 } | 99 } |
100 | 100 |
101 // Given a page and a typed slot in that page, this function adds the slot | 101 // Given a page and a typed slot in that page, this function adds the slot |
102 // to the remembered set. | 102 // to the remembered set. |
103 static void InsertTyped(Page* page, SlotType slot_type, Address slot_addr) { | 103 static void InsertTyped(Page* page, SlotType slot_type, Address slot_addr) { |
104 STATIC_ASSERT(direction == OLD_TO_OLD); | 104 TypedSlotSet* slot_set = GetTypedSlotSet(page); |
105 TypedSlotSet* slot_set = page->typed_old_to_old_slots(); | |
106 if (slot_set == nullptr) { | 105 if (slot_set == nullptr) { |
107 page->AllocateTypedOldToOldSlots(); | 106 AllocateTypedSlotSet(page); |
108 slot_set = page->typed_old_to_old_slots(); | 107 slot_set = GetTypedSlotSet(page); |
109 } | 108 } |
110 uintptr_t offset = slot_addr - page->address(); | 109 uintptr_t offset = slot_addr - page->address(); |
111 DCHECK_LT(offset, static_cast<uintptr_t>(TypedSlotSet::kMaxOffset)); | 110 DCHECK_LT(offset, static_cast<uintptr_t>(TypedSlotSet::kMaxOffset)); |
112 slot_set->Insert(slot_type, static_cast<uint32_t>(offset)); | 111 slot_set->Insert(slot_type, static_cast<uint32_t>(offset)); |
113 } | 112 } |
114 | 113 |
115 // Given a page and a range of typed slots in that page, this function removes | 114 // Given a page and a range of typed slots in that page, this function removes |
116 // the slots from the remembered set. | 115 // the slots from the remembered set. |
117 static void RemoveRangeTyped(Page* page, Address start, Address end) { | 116 static void RemoveRangeTyped(Page* page, Address start, Address end) { |
118 TypedSlotSet* slots = page->typed_old_to_old_slots(); | 117 TypedSlotSet* slots = GetTypedSlotSet(page); |
119 if (slots != nullptr) { | 118 if (slots != nullptr) { |
120 slots->Iterate([start, end](SlotType slot_type, Address slot_addr) { | 119 slots->Iterate([start, end](SlotType slot_type, Address slot_addr) { |
121 return start <= slot_addr && slot_addr < end ? REMOVE_SLOT : KEEP_SLOT; | 120 return start <= slot_addr && slot_addr < end ? REMOVE_SLOT : KEEP_SLOT; |
122 }); | 121 }); |
123 } | 122 } |
124 } | 123 } |
125 | 124 |
| 125 // Iterates and filters the remembered set with the given callback. |
| 126 // The callback should take (SlotType slot_type, SlotAddress slot) and return |
| 127 // SlotCallbackResult. |
| 128 template <typename Callback> |
| 129 static void IterateTyped(Heap* heap, Callback callback) { |
| 130 IterateMemoryChunks(heap, [callback](MemoryChunk* chunk) { |
| 131 IterateTyped(chunk, callback); |
| 132 }); |
| 133 } |
| 134 |
126 // Iterates and filters typed old to old pointers in the given memory chunk | 135 // Iterates and filters typed old to old pointers in the given memory chunk |
127 // with the given callback. The callback should take (SlotType slot_type, | 136 // with the given callback. The callback should take (SlotType slot_type, |
128 // Address slot_addr) and return SlotCallbackResult. | 137 // Address slot_addr) and return SlotCallbackResult. |
129 template <typename Callback> | 138 template <typename Callback> |
130 static void IterateTyped(MemoryChunk* chunk, Callback callback) { | 139 static void IterateTyped(MemoryChunk* chunk, Callback callback) { |
131 TypedSlotSet* slots = chunk->typed_old_to_old_slots(); | 140 TypedSlotSet* slots = GetTypedSlotSet(chunk); |
132 if (slots != nullptr) { | 141 if (slots != nullptr) { |
133 int new_count = slots->Iterate(callback); | 142 int new_count = slots->Iterate(callback); |
134 if (new_count == 0) { | 143 if (new_count == 0) { |
| 144 ReleaseTypedSlotSet(chunk); |
135 chunk->ReleaseTypedOldToOldSlots(); | 145 chunk->ReleaseTypedOldToOldSlots(); |
136 } | 146 } |
137 } | 147 } |
138 } | 148 } |
139 | 149 |
140 // Clear all old to old slots from the remembered set. | 150 // Clear all old to old slots from the remembered set. |
141 static void ClearAll(Heap* heap) { | 151 static void ClearAll(Heap* heap) { |
142 STATIC_ASSERT(direction == OLD_TO_OLD); | 152 STATIC_ASSERT(direction == OLD_TO_OLD); |
143 MemoryChunkIterator it(heap, MemoryChunkIterator::ALL); | 153 MemoryChunkIterator it(heap); |
144 MemoryChunk* chunk; | 154 MemoryChunk* chunk; |
145 while ((chunk = it.next()) != nullptr) { | 155 while ((chunk = it.next()) != nullptr) { |
146 chunk->ReleaseOldToOldSlots(); | 156 chunk->ReleaseOldToOldSlots(); |
147 chunk->ReleaseTypedOldToOldSlots(); | 157 chunk->ReleaseTypedOldToOldSlots(); |
148 } | 158 } |
149 } | 159 } |
150 | 160 |
151 // Eliminates all stale slots from the remembered set, i.e. | 161 // Eliminates all stale slots from the remembered set, i.e. |
152 // slots that are not part of live objects anymore. This method must be | 162 // slots that are not part of live objects anymore. This method must be |
153 // called after marking, when the whole transitive closure is known and | 163 // called after marking, when the whole transitive closure is known and |
154 // must be called before sweeping when mark bits are still intact. | 164 // must be called before sweeping when mark bits are still intact. |
155 static void ClearInvalidSlots(Heap* heap); | 165 static void ClearInvalidSlots(Heap* heap); |
156 | 166 |
157 static void VerifyValidSlots(Heap* heap); | 167 static void VerifyValidSlots(Heap* heap); |
158 | 168 |
159 private: | 169 private: |
160 static SlotSet* GetSlotSet(MemoryChunk* chunk) { | 170 static SlotSet* GetSlotSet(MemoryChunk* chunk) { |
161 if (direction == OLD_TO_OLD) { | 171 if (direction == OLD_TO_OLD) { |
162 return chunk->old_to_old_slots(); | 172 return chunk->old_to_old_slots(); |
163 } else { | 173 } else { |
164 return chunk->old_to_new_slots(); | 174 return chunk->old_to_new_slots(); |
165 } | 175 } |
166 } | 176 } |
167 | 177 |
168 static TypedSlotSet* GetTypedSlotSet(MemoryChunk* chunk) { | 178 static TypedSlotSet* GetTypedSlotSet(MemoryChunk* chunk) { |
169 if (direction == OLD_TO_OLD) { | 179 if (direction == OLD_TO_OLD) { |
170 return chunk->typed_old_to_old_slots(); | 180 return chunk->typed_old_to_old_slots(); |
171 } else { | 181 } else { |
172 return nullptr; | 182 return chunk->typed_old_to_new_slots(); |
173 } | 183 } |
174 } | 184 } |
175 | 185 |
176 static void ReleaseSlotSet(MemoryChunk* chunk) { | 186 static void ReleaseSlotSet(MemoryChunk* chunk) { |
177 if (direction == OLD_TO_OLD) { | 187 if (direction == OLD_TO_OLD) { |
178 chunk->ReleaseOldToOldSlots(); | 188 chunk->ReleaseOldToOldSlots(); |
179 } else { | 189 } else { |
180 chunk->ReleaseOldToNewSlots(); | 190 chunk->ReleaseOldToNewSlots(); |
181 } | 191 } |
182 } | 192 } |
183 | 193 |
| 194 static void ReleaseTypedSlotSet(MemoryChunk* chunk) { |
| 195 if (direction == OLD_TO_OLD) { |
| 196 chunk->ReleaseTypedOldToOldSlots(); |
| 197 } else { |
| 198 chunk->ReleaseTypedOldToNewSlots(); |
| 199 } |
| 200 } |
| 201 |
184 static SlotSet* AllocateSlotSet(MemoryChunk* chunk) { | 202 static SlotSet* AllocateSlotSet(MemoryChunk* chunk) { |
185 if (direction == OLD_TO_OLD) { | 203 if (direction == OLD_TO_OLD) { |
186 chunk->AllocateOldToOldSlots(); | 204 chunk->AllocateOldToOldSlots(); |
187 return chunk->old_to_old_slots(); | 205 return chunk->old_to_old_slots(); |
188 } else { | 206 } else { |
189 chunk->AllocateOldToNewSlots(); | 207 chunk->AllocateOldToNewSlots(); |
190 return chunk->old_to_new_slots(); | 208 return chunk->old_to_new_slots(); |
191 } | 209 } |
192 } | 210 } |
193 | 211 |
| 212 static TypedSlotSet* AllocateTypedSlotSet(MemoryChunk* chunk) { |
| 213 if (direction == OLD_TO_OLD) { |
| 214 chunk->AllocateTypedOldToOldSlots(); |
| 215 return chunk->typed_old_to_old_slots(); |
| 216 } else { |
| 217 chunk->AllocateTypedOldToNewSlots(); |
| 218 return chunk->typed_old_to_new_slots(); |
| 219 } |
| 220 } |
| 221 |
194 static bool IsValidSlot(Heap* heap, MemoryChunk* chunk, Object** slot); | 222 static bool IsValidSlot(Heap* heap, MemoryChunk* chunk, Object** slot); |
195 }; | 223 }; |
196 | 224 |
| 225 class UpdateTypedSlotHelper { |
| 226 public: |
| 227 // Updates a cell slot using an untyped slot callback. |
| 228 // The callback accepts (Heap*, Object**) and returns SlotCallbackResult. |
| 229 template <typename Callback> |
| 230 static SlotCallbackResult UpdateCell(RelocInfo* rinfo, Callback callback) { |
| 231 DCHECK(rinfo->rmode() == RelocInfo::CELL); |
| 232 Object* cell = rinfo->target_cell(); |
| 233 Object* old_cell = cell; |
| 234 SlotCallbackResult result = callback(&cell); |
| 235 if (cell != old_cell) { |
| 236 rinfo->set_target_cell(reinterpret_cast<Cell*>(cell)); |
| 237 } |
| 238 return result; |
| 239 } |
| 240 |
| 241 // Updates a code entry slot using an untyped slot callback. |
| 242 // The callback accepts (Heap*, Object**) and returns SlotCallbackResult. |
| 243 template <typename Callback> |
| 244 static SlotCallbackResult UpdateCodeEntry(Address entry_address, |
| 245 Callback callback) { |
| 246 Object* code = Code::GetObjectFromEntryAddress(entry_address); |
| 247 Object* old_code = code; |
| 248 SlotCallbackResult result = callback(&code); |
| 249 if (code != old_code) { |
| 250 Memory::Address_at(entry_address) = |
| 251 reinterpret_cast<Code*>(code)->entry(); |
| 252 } |
| 253 return result; |
| 254 } |
| 255 |
| 256 // Updates a code target slot using an untyped slot callback. |
| 257 // The callback accepts (Heap*, Object**) and returns SlotCallbackResult. |
| 258 template <typename Callback> |
| 259 static SlotCallbackResult UpdateCodeTarget(RelocInfo* rinfo, |
| 260 Callback callback) { |
| 261 DCHECK(RelocInfo::IsCodeTarget(rinfo->rmode())); |
| 262 Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); |
| 263 Object* old_target = target; |
| 264 SlotCallbackResult result = callback(&target); |
| 265 if (target != old_target) { |
| 266 rinfo->set_target_address(Code::cast(target)->instruction_start()); |
| 267 } |
| 268 return result; |
| 269 } |
| 270 |
| 271 // Updates an embedded pointer slot using an untyped slot callback. |
| 272 // The callback accepts (Heap*, Object**) and returns SlotCallbackResult. |
| 273 template <typename Callback> |
| 274 static SlotCallbackResult UpdateEmbeddedPointer(RelocInfo* rinfo, |
| 275 Callback callback) { |
| 276 DCHECK(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT); |
| 277 Object* target = rinfo->target_object(); |
| 278 Object* old_target = target; |
| 279 SlotCallbackResult result = callback(&target); |
| 280 if (target != old_target) { |
| 281 rinfo->set_target_object(target); |
| 282 } |
| 283 return result; |
| 284 } |
| 285 |
| 286 // Updates a debug target slot using an untyped slot callback. |
| 287 // The callback accepts (Heap*, Object**) and returns SlotCallbackResult. |
| 288 template <typename Callback> |
| 289 static SlotCallbackResult UpdateDebugTarget(RelocInfo* rinfo, |
| 290 Callback callback) { |
| 291 DCHECK(RelocInfo::IsDebugBreakSlot(rinfo->rmode()) && |
| 292 rinfo->IsPatchedDebugBreakSlotSequence()); |
| 293 Object* target = |
| 294 Code::GetCodeFromTargetAddress(rinfo->debug_call_address()); |
| 295 SlotCallbackResult result = callback(&target); |
| 296 rinfo->set_debug_call_address(Code::cast(target)->instruction_start()); |
| 297 return result; |
| 298 } |
| 299 |
| 300 // Updates a typed slot using an untyped slot callback. |
| 301 // The callback accepts (Heap*, Object**) and returns SlotCallbackResult. |
| 302 template <typename Callback> |
| 303 static SlotCallbackResult UpdateTypedSlot(Isolate* isolate, |
| 304 SlotType slot_type, Address addr, |
| 305 Callback callback) { |
| 306 switch (slot_type) { |
| 307 case CODE_TARGET_SLOT: { |
| 308 RelocInfo rinfo(isolate, addr, RelocInfo::CODE_TARGET, 0, NULL); |
| 309 return UpdateCodeTarget(&rinfo, callback); |
| 310 } |
| 311 case CELL_TARGET_SLOT: { |
| 312 RelocInfo rinfo(isolate, addr, RelocInfo::CELL, 0, NULL); |
| 313 return UpdateCell(&rinfo, callback); |
| 314 } |
| 315 case CODE_ENTRY_SLOT: { |
| 316 return UpdateCodeEntry(addr, callback); |
| 317 } |
| 318 case DEBUG_TARGET_SLOT: { |
| 319 RelocInfo rinfo(isolate, addr, RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION, |
| 320 0, NULL); |
| 321 if (rinfo.IsPatchedDebugBreakSlotSequence()) { |
| 322 return UpdateDebugTarget(&rinfo, callback); |
| 323 } |
| 324 return REMOVE_SLOT; |
| 325 } |
| 326 case EMBEDDED_OBJECT_SLOT: { |
| 327 RelocInfo rinfo(isolate, addr, RelocInfo::EMBEDDED_OBJECT, 0, NULL); |
| 328 return UpdateEmbeddedPointer(&rinfo, callback); |
| 329 } |
| 330 case OBJECT_SLOT: { |
| 331 return callback(reinterpret_cast<Object**>(addr)); |
| 332 } |
| 333 case NUMBER_OF_SLOT_TYPES: |
| 334 break; |
| 335 } |
| 336 UNREACHABLE(); |
| 337 return REMOVE_SLOT; |
| 338 } |
| 339 }; |
| 340 |
197 } // namespace internal | 341 } // namespace internal |
198 } // namespace v8 | 342 } // namespace v8 |
199 | 343 |
200 #endif // V8_REMEMBERED_SET_H | 344 #endif // V8_REMEMBERED_SET_H |
OLD | NEW |