• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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