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_HEAP_REMEMBERED_SET_H_ 6 #define V8_HEAP_REMEMBERED_SET_H_ 7 8 #include <memory> 9 10 #include "src/base/bounds.h" 11 #include "src/base/memory.h" 12 #include "src/codegen/reloc-info.h" 13 #include "src/common/globals.h" 14 #include "src/heap/base/worklist.h" 15 #include "src/heap/heap.h" 16 #include "src/heap/memory-chunk.h" 17 #include "src/heap/paged-spaces.h" 18 #include "src/heap/slot-set.h" 19 #include "src/heap/spaces.h" 20 21 namespace v8 { 22 namespace internal { 23 24 enum RememberedSetIterationMode { SYNCHRONIZED, NON_SYNCHRONIZED }; 25 26 class RememberedSetOperations { 27 public: 28 // Given a page and a slot in that page, this function adds the slot to the 29 // remembered set. 30 template <AccessMode access_mode> Insert(SlotSet * slot_set,MemoryChunk * chunk,Address slot_addr)31 static void Insert(SlotSet* slot_set, MemoryChunk* chunk, Address slot_addr) { 32 DCHECK(chunk->Contains(slot_addr)); 33 uintptr_t offset = slot_addr - chunk->address(); 34 slot_set->Insert<access_mode>(offset); 35 } 36 37 template <typename Callback> Iterate(SlotSet * slot_set,MemoryChunk * chunk,Callback callback,SlotSet::EmptyBucketMode mode)38 static int Iterate(SlotSet* slot_set, MemoryChunk* chunk, Callback callback, 39 SlotSet::EmptyBucketMode mode) { 40 int slots = 0; 41 if (slot_set != nullptr) { 42 slots += slot_set->Iterate(chunk->address(), 0, chunk->buckets(), 43 callback, mode); 44 } 45 return slots; 46 } 47 Remove(SlotSet * slot_set,MemoryChunk * chunk,Address slot_addr)48 static void Remove(SlotSet* slot_set, MemoryChunk* chunk, Address slot_addr) { 49 if (slot_set != nullptr) { 50 uintptr_t offset = slot_addr - chunk->address(); 51 slot_set->Remove(offset); 52 } 53 } 54 RemoveRange(SlotSet * slot_set,MemoryChunk * chunk,Address start,Address end,SlotSet::EmptyBucketMode mode)55 static void RemoveRange(SlotSet* slot_set, MemoryChunk* chunk, Address start, 56 Address end, SlotSet::EmptyBucketMode mode) { 57 if (slot_set != nullptr) { 58 uintptr_t start_offset = start - chunk->address(); 59 uintptr_t end_offset = end - chunk->address(); 60 DCHECK_LT(start_offset, end_offset); 61 slot_set->RemoveRange(static_cast<int>(start_offset), 62 static_cast<int>(end_offset), chunk->buckets(), 63 mode); 64 } 65 } 66 CheckNoneInRange(SlotSet * slot_set,MemoryChunk * chunk,Address start,Address end)67 static void CheckNoneInRange(SlotSet* slot_set, MemoryChunk* chunk, 68 Address start, Address end) { 69 if (slot_set != nullptr) { 70 size_t start_bucket = SlotSet::BucketForSlot(start - chunk->address()); 71 // Both 'end' and 'end_bucket' are exclusive limits, so do some index 72 // juggling to make sure we get the right bucket even if the end address 73 // is at the start of a bucket. 74 size_t end_bucket = 75 SlotSet::BucketForSlot(end - chunk->address() - kTaggedSize) + 1; 76 slot_set->Iterate( 77 chunk->address(), start_bucket, end_bucket, 78 [start, end](MaybeObjectSlot slot) { 79 CHECK(!base::IsInRange(slot.address(), start, end + 1)); 80 return KEEP_SLOT; 81 }, 82 SlotSet::KEEP_EMPTY_BUCKETS); 83 } 84 } 85 }; 86 87 template <RememberedSetType type> 88 class RememberedSet : public AllStatic { 89 public: 90 // Given a page and a slot in that page, this function adds the slot to the 91 // remembered set. 92 template <AccessMode access_mode> Insert(MemoryChunk * chunk,Address slot_addr)93 static void Insert(MemoryChunk* chunk, Address slot_addr) { 94 DCHECK(chunk->Contains(slot_addr)); 95 SlotSet* slot_set = chunk->slot_set<type, access_mode>(); 96 if (slot_set == nullptr) { 97 slot_set = chunk->AllocateSlotSet<type>(); 98 } 99 RememberedSetOperations::Insert<access_mode>(slot_set, chunk, slot_addr); 100 } 101 102 // Given a page and a slot in that page, this function returns true if 103 // the remembered set contains the slot. Contains(MemoryChunk * chunk,Address slot_addr)104 static bool Contains(MemoryChunk* chunk, Address slot_addr) { 105 DCHECK(chunk->Contains(slot_addr)); 106 SlotSet* slot_set = chunk->slot_set<type>(); 107 if (slot_set == nullptr) { 108 return false; 109 } 110 uintptr_t offset = slot_addr - chunk->address(); 111 return slot_set->Contains(offset); 112 } 113 CheckNoneInRange(MemoryChunk * chunk,Address start,Address end)114 static void CheckNoneInRange(MemoryChunk* chunk, Address start, Address end) { 115 SlotSet* slot_set = chunk->slot_set<type>(); 116 RememberedSetOperations::CheckNoneInRange(slot_set, chunk, start, end); 117 } 118 119 // Given a page and a slot in that page, this function removes the slot from 120 // the remembered set. 121 // If the slot was never added, then the function does nothing. Remove(MemoryChunk * chunk,Address slot_addr)122 static void Remove(MemoryChunk* chunk, Address slot_addr) { 123 DCHECK(chunk->Contains(slot_addr)); 124 SlotSet* slot_set = chunk->slot_set<type>(); 125 RememberedSetOperations::Remove(slot_set, chunk, slot_addr); 126 } 127 128 // Given a page and a range of slots in that page, this function removes the 129 // slots from the remembered set. RemoveRange(MemoryChunk * chunk,Address start,Address end,SlotSet::EmptyBucketMode mode)130 static void RemoveRange(MemoryChunk* chunk, Address start, Address end, 131 SlotSet::EmptyBucketMode mode) { 132 SlotSet* slot_set = chunk->slot_set<type>(); 133 RememberedSetOperations::RemoveRange(slot_set, chunk, start, end, mode); 134 } 135 136 // Iterates and filters the remembered set with the given callback. 137 // The callback should take (Address slot) and return SlotCallbackResult. 138 template <typename Callback> Iterate(Heap * heap,RememberedSetIterationMode mode,Callback callback)139 static void Iterate(Heap* heap, RememberedSetIterationMode mode, 140 Callback callback) { 141 IterateMemoryChunks(heap, [mode, callback](MemoryChunk* chunk) { 142 if (mode == SYNCHRONIZED) chunk->mutex()->Lock(); 143 Iterate(chunk, callback); 144 if (mode == SYNCHRONIZED) chunk->mutex()->Unlock(); 145 }); 146 } 147 148 // Iterates over all memory chunks that contains non-empty slot sets. 149 // The callback should take (MemoryChunk* chunk) and return void. 150 template <typename Callback> IterateMemoryChunks(Heap * heap,Callback callback)151 static void IterateMemoryChunks(Heap* heap, Callback callback) { 152 OldGenerationMemoryChunkIterator it(heap); 153 MemoryChunk* chunk; 154 while ((chunk = it.next()) != nullptr) { 155 SlotSet* slot_set = chunk->slot_set<type>(); 156 TypedSlotSet* typed_slot_set = chunk->typed_slot_set<type>(); 157 if (slot_set != nullptr || typed_slot_set != nullptr || 158 chunk->invalidated_slots<type>() != nullptr) { 159 callback(chunk); 160 } 161 } 162 } 163 164 // Iterates and filters the remembered set in the given memory chunk with 165 // the given callback. The callback should take (Address slot) and return 166 // SlotCallbackResult. 167 // 168 // Notice that |mode| can only be of FREE* or PREFREE* if there are no other 169 // threads concurrently inserting slots. 170 template <typename Callback> Iterate(MemoryChunk * chunk,Callback callback,SlotSet::EmptyBucketMode mode)171 static int Iterate(MemoryChunk* chunk, Callback callback, 172 SlotSet::EmptyBucketMode mode) { 173 SlotSet* slot_set = chunk->slot_set<type>(); 174 return RememberedSetOperations::Iterate(slot_set, chunk, callback, mode); 175 } 176 177 template <typename Callback> IterateAndTrackEmptyBuckets(MemoryChunk * chunk,Callback callback,::heap::base::Worklist<MemoryChunk *,64>::Local * empty_chunks)178 static int IterateAndTrackEmptyBuckets( 179 MemoryChunk* chunk, Callback callback, 180 ::heap::base::Worklist<MemoryChunk*, 64>::Local* empty_chunks) { 181 SlotSet* slot_set = chunk->slot_set<type>(); 182 int slots = 0; 183 if (slot_set != nullptr) { 184 PossiblyEmptyBuckets* possibly_empty_buckets = 185 chunk->possibly_empty_buckets(); 186 slots += slot_set->IterateAndTrackEmptyBuckets(chunk->address(), 0, 187 chunk->buckets(), callback, 188 possibly_empty_buckets); 189 if (!possibly_empty_buckets->IsEmpty()) empty_chunks->Push(chunk); 190 } 191 return slots; 192 } 193 FreeEmptyBuckets(MemoryChunk * chunk)194 static void FreeEmptyBuckets(MemoryChunk* chunk) { 195 DCHECK(type == OLD_TO_NEW); 196 SlotSet* slot_set = chunk->slot_set<type>(); 197 if (slot_set != nullptr && slot_set->FreeEmptyBuckets(chunk->buckets())) { 198 chunk->ReleaseSlotSet<type>(); 199 } 200 } 201 CheckPossiblyEmptyBuckets(MemoryChunk * chunk)202 static bool CheckPossiblyEmptyBuckets(MemoryChunk* chunk) { 203 DCHECK(type == OLD_TO_NEW); 204 SlotSet* slot_set = chunk->slot_set<type, AccessMode::NON_ATOMIC>(); 205 if (slot_set != nullptr && 206 slot_set->CheckPossiblyEmptyBuckets(chunk->buckets(), 207 chunk->possibly_empty_buckets())) { 208 chunk->ReleaseSlotSet<type>(); 209 return true; 210 } 211 212 return false; 213 } 214 215 // Given a page and a typed slot in that page, this function adds the slot 216 // to the remembered set. InsertTyped(MemoryChunk * memory_chunk,SlotType slot_type,uint32_t offset)217 static void InsertTyped(MemoryChunk* memory_chunk, SlotType slot_type, 218 uint32_t offset) { 219 TypedSlotSet* slot_set = memory_chunk->typed_slot_set<type>(); 220 if (slot_set == nullptr) { 221 slot_set = memory_chunk->AllocateTypedSlotSet<type>(); 222 } 223 slot_set->Insert(slot_type, offset); 224 } 225 MergeTyped(MemoryChunk * page,std::unique_ptr<TypedSlots> other)226 static void MergeTyped(MemoryChunk* page, std::unique_ptr<TypedSlots> other) { 227 TypedSlotSet* slot_set = page->typed_slot_set<type>(); 228 if (slot_set == nullptr) { 229 slot_set = page->AllocateTypedSlotSet<type>(); 230 } 231 slot_set->Merge(other.get()); 232 } 233 234 // Given a page and a range of typed slots in that page, this function removes 235 // the slots from the remembered set. RemoveRangeTyped(MemoryChunk * page,Address start,Address end)236 static void RemoveRangeTyped(MemoryChunk* page, Address start, Address end) { 237 TypedSlotSet* slot_set = page->typed_slot_set<type>(); 238 if (slot_set != nullptr) { 239 slot_set->Iterate( 240 [=](SlotType slot_type, Address slot_addr) { 241 return start <= slot_addr && slot_addr < end ? REMOVE_SLOT 242 : KEEP_SLOT; 243 }, 244 TypedSlotSet::FREE_EMPTY_CHUNKS); 245 } 246 } 247 248 // Iterates and filters the remembered set with the given callback. 249 // The callback should take (SlotType slot_type, Address addr) and return 250 // SlotCallbackResult. 251 template <typename Callback> IterateTyped(Heap * heap,RememberedSetIterationMode mode,Callback callback)252 static void IterateTyped(Heap* heap, RememberedSetIterationMode mode, 253 Callback callback) { 254 IterateMemoryChunks(heap, [mode, callback](MemoryChunk* chunk) { 255 if (mode == SYNCHRONIZED) chunk->mutex()->Lock(); 256 IterateTyped(chunk, callback); 257 if (mode == SYNCHRONIZED) chunk->mutex()->Unlock(); 258 }); 259 } 260 261 // Iterates and filters typed pointers in the given memory chunk with the 262 // given callback. The callback should take (SlotType slot_type, Address addr) 263 // and return SlotCallbackResult. 264 template <typename Callback> IterateTyped(MemoryChunk * chunk,Callback callback)265 static void IterateTyped(MemoryChunk* chunk, Callback callback) { 266 TypedSlotSet* slot_set = chunk->typed_slot_set<type>(); 267 if (slot_set != nullptr) { 268 int new_count = 269 slot_set->Iterate(callback, TypedSlotSet::KEEP_EMPTY_CHUNKS); 270 if (new_count == 0) { 271 chunk->ReleaseTypedSlotSet<type>(); 272 } 273 } 274 } 275 276 // Clear all old to old slots from the remembered set. ClearAll(Heap * heap)277 static void ClearAll(Heap* heap) { 278 STATIC_ASSERT(type == OLD_TO_OLD || type == OLD_TO_CODE); 279 OldGenerationMemoryChunkIterator it(heap); 280 MemoryChunk* chunk; 281 while ((chunk = it.next()) != nullptr) { 282 chunk->ReleaseSlotSet<OLD_TO_OLD>(); 283 if (V8_EXTERNAL_CODE_SPACE_BOOL) { 284 chunk->ReleaseSlotSet<OLD_TO_CODE>(); 285 } 286 chunk->ReleaseTypedSlotSet<OLD_TO_OLD>(); 287 chunk->ReleaseInvalidatedSlots<OLD_TO_OLD>(); 288 } 289 } 290 }; 291 292 class UpdateTypedSlotHelper { 293 public: 294 // Updates a typed slot using an untyped slot callback where |addr| depending 295 // on slot type represents either address for respective RelocInfo or address 296 // of the uncompressed constant pool entry. 297 // The callback accepts FullMaybeObjectSlot and returns SlotCallbackResult. 298 template <typename Callback> 299 static SlotCallbackResult UpdateTypedSlot(Heap* heap, SlotType slot_type, 300 Address addr, Callback callback); 301 302 private: 303 // Updates a code entry slot using an untyped slot callback. 304 // The callback accepts FullMaybeObjectSlot and returns SlotCallbackResult. 305 template <typename Callback> UpdateCodeEntry(Address entry_address,Callback callback)306 static SlotCallbackResult UpdateCodeEntry(Address entry_address, 307 Callback callback) { 308 Code code = Code::GetObjectFromEntryAddress(entry_address); 309 Code old_code = code; 310 SlotCallbackResult result = callback(FullMaybeObjectSlot(&code)); 311 DCHECK(!HasWeakHeapObjectTag(code)); 312 if (code != old_code) { 313 base::Memory<Address>(entry_address) = code.entry(); 314 } 315 return result; 316 } 317 318 // Updates a code target slot using an untyped slot callback. 319 // The callback accepts FullMaybeObjectSlot and returns SlotCallbackResult. 320 template <typename Callback> UpdateCodeTarget(RelocInfo * rinfo,Callback callback)321 static SlotCallbackResult UpdateCodeTarget(RelocInfo* rinfo, 322 Callback callback) { 323 DCHECK(RelocInfo::IsCodeTargetMode(rinfo->rmode())); 324 Code old_target = Code::GetCodeFromTargetAddress(rinfo->target_address()); 325 Code new_target = old_target; 326 SlotCallbackResult result = callback(FullMaybeObjectSlot(&new_target)); 327 DCHECK(!HasWeakHeapObjectTag(new_target)); 328 if (new_target != old_target) { 329 rinfo->set_target_address(Code::cast(new_target).raw_instruction_start()); 330 } 331 return result; 332 } 333 334 // Updates an embedded pointer slot using an untyped slot callback. 335 // The callback accepts FullMaybeObjectSlot and returns SlotCallbackResult. 336 template <typename Callback> UpdateEmbeddedPointer(Heap * heap,RelocInfo * rinfo,Callback callback)337 static SlotCallbackResult UpdateEmbeddedPointer(Heap* heap, RelocInfo* rinfo, 338 Callback callback) { 339 DCHECK(RelocInfo::IsEmbeddedObjectMode(rinfo->rmode())); 340 HeapObject old_target = rinfo->target_object(heap->isolate()); 341 HeapObject new_target = old_target; 342 SlotCallbackResult result = callback(FullMaybeObjectSlot(&new_target)); 343 DCHECK(!HasWeakHeapObjectTag(new_target)); 344 if (new_target != old_target) { 345 rinfo->set_target_object(heap, HeapObject::cast(new_target)); 346 } 347 return result; 348 } 349 }; 350 351 } // namespace internal 352 } // namespace v8 353 354 #endif // V8_HEAP_REMEMBERED_SET_H_ 355