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