1 // Copyright 2011 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 #include "src/heap/store-buffer.h"
6 
7 #include <algorithm>
8 
9 #include "src/counters.h"
10 #include "src/heap/incremental-marking.h"
11 #include "src/heap/store-buffer-inl.h"
12 #include "src/isolate.h"
13 #include "src/objects-inl.h"
14 #include "src/v8.h"
15 
16 namespace v8 {
17 namespace internal {
18 
StoreBuffer(Heap * heap)19 StoreBuffer::StoreBuffer(Heap* heap)
20     : heap_(heap),
21       start_(NULL),
22       limit_(NULL),
23       old_start_(NULL),
24       old_limit_(NULL),
25       old_top_(NULL),
26       old_reserved_limit_(NULL),
27       old_buffer_is_sorted_(false),
28       old_buffer_is_filtered_(false),
29       during_gc_(false),
30       store_buffer_rebuilding_enabled_(false),
31       callback_(NULL),
32       may_move_store_buffer_entries_(true),
33       virtual_memory_(NULL),
34       hash_set_1_(NULL),
35       hash_set_2_(NULL),
36       hash_sets_are_empty_(true) {}
37 
38 
SetUp()39 void StoreBuffer::SetUp() {
40   // Allocate 3x the buffer size, so that we can start the new store buffer
41   // aligned to 2x the size.  This lets us use a bit test to detect the end of
42   // the area.
43   virtual_memory_ = new base::VirtualMemory(kStoreBufferSize * 3);
44   uintptr_t start_as_int =
45       reinterpret_cast<uintptr_t>(virtual_memory_->address());
46   start_ =
47       reinterpret_cast<Address*>(RoundUp(start_as_int, kStoreBufferSize * 2));
48   limit_ = start_ + (kStoreBufferSize / kPointerSize);
49 
50   // Reserve space for the larger old buffer.
51   old_virtual_memory_ =
52       new base::VirtualMemory(kOldStoreBufferLength * kPointerSize);
53   old_top_ = old_start_ =
54       reinterpret_cast<Address*>(old_virtual_memory_->address());
55   // Don't know the alignment requirements of the OS, but it is certainly not
56   // less than 0xfff.
57   CHECK((reinterpret_cast<uintptr_t>(old_start_) & 0xfff) == 0);
58   CHECK(kStoreBufferSize >= base::OS::CommitPageSize());
59   // Initial size of the old buffer is as big as the buffer for new pointers.
60   // This means even if we later fail to enlarge the old buffer due to OOM from
61   // the OS, we will still be able to empty the new pointer buffer into the old
62   // buffer.
63   int initial_length = static_cast<int>(kStoreBufferSize / kPointerSize);
64   CHECK(initial_length > 0);
65   CHECK(initial_length <= kOldStoreBufferLength);
66   old_limit_ = old_start_ + initial_length;
67   old_reserved_limit_ = old_start_ + kOldStoreBufferLength;
68 
69   if (!old_virtual_memory_->Commit(reinterpret_cast<void*>(old_start_),
70                                    (old_limit_ - old_start_) * kPointerSize,
71                                    false)) {
72     V8::FatalProcessOutOfMemory("StoreBuffer::SetUp");
73   }
74 
75   DCHECK(reinterpret_cast<Address>(start_) >= virtual_memory_->address());
76   DCHECK(reinterpret_cast<Address>(limit_) >= virtual_memory_->address());
77   Address* vm_limit = reinterpret_cast<Address*>(
78       reinterpret_cast<char*>(virtual_memory_->address()) +
79       virtual_memory_->size());
80   DCHECK(start_ <= vm_limit);
81   DCHECK(limit_ <= vm_limit);
82   USE(vm_limit);
83   DCHECK((reinterpret_cast<uintptr_t>(limit_) & kStoreBufferOverflowBit) != 0);
84   DCHECK((reinterpret_cast<uintptr_t>(limit_ - 1) & kStoreBufferOverflowBit) ==
85          0);
86 
87   if (!virtual_memory_->Commit(reinterpret_cast<Address>(start_),
88                                kStoreBufferSize,
89                                false)) {  // Not executable.
90     V8::FatalProcessOutOfMemory("StoreBuffer::SetUp");
91   }
92   heap_->set_store_buffer_top(reinterpret_cast<Smi*>(start_));
93 
94   hash_set_1_ = new uintptr_t[kHashSetLength];
95   hash_set_2_ = new uintptr_t[kHashSetLength];
96   hash_sets_are_empty_ = false;
97 
98   ClearFilteringHashSets();
99 }
100 
101 
TearDown()102 void StoreBuffer::TearDown() {
103   delete virtual_memory_;
104   delete old_virtual_memory_;
105   delete[] hash_set_1_;
106   delete[] hash_set_2_;
107   old_start_ = old_top_ = old_limit_ = old_reserved_limit_ = NULL;
108   start_ = limit_ = NULL;
109   heap_->set_store_buffer_top(reinterpret_cast<Smi*>(start_));
110 }
111 
112 
StoreBufferOverflow(Isolate * isolate)113 void StoreBuffer::StoreBufferOverflow(Isolate* isolate) {
114   isolate->heap()->store_buffer()->Compact();
115   isolate->counters()->store_buffer_overflows()->Increment();
116 }
117 
118 
SpaceAvailable(intptr_t space_needed)119 bool StoreBuffer::SpaceAvailable(intptr_t space_needed) {
120   return old_limit_ - old_top_ >= space_needed;
121 }
122 
123 
EnsureSpace(intptr_t space_needed)124 void StoreBuffer::EnsureSpace(intptr_t space_needed) {
125   while (old_limit_ - old_top_ < space_needed &&
126          old_limit_ < old_reserved_limit_) {
127     size_t grow = old_limit_ - old_start_;  // Double size.
128     if (old_virtual_memory_->Commit(reinterpret_cast<void*>(old_limit_),
129                                     grow * kPointerSize, false)) {
130       old_limit_ += grow;
131     } else {
132       break;
133     }
134   }
135 
136   if (SpaceAvailable(space_needed)) return;
137 
138   if (old_buffer_is_filtered_) return;
139   DCHECK(may_move_store_buffer_entries_);
140   Compact();
141 
142   old_buffer_is_filtered_ = true;
143   bool page_has_scan_on_scavenge_flag = false;
144 
145   PointerChunkIterator it(heap_);
146   MemoryChunk* chunk;
147   while ((chunk = it.next()) != NULL) {
148     if (chunk->scan_on_scavenge()) {
149       page_has_scan_on_scavenge_flag = true;
150       break;
151     }
152   }
153 
154   if (page_has_scan_on_scavenge_flag) {
155     Filter(MemoryChunk::SCAN_ON_SCAVENGE);
156   }
157 
158   if (SpaceAvailable(space_needed)) return;
159 
160   // Sample 1 entry in 97 and filter out the pages where we estimate that more
161   // than 1 in 8 pointers are to new space.
162   static const int kSampleFinenesses = 5;
163   static const struct Samples {
164     int prime_sample_step;
165     int threshold;
166   } samples[kSampleFinenesses] = {
167         {97, ((Page::kPageSize / kPointerSize) / 97) / 8},
168         {23, ((Page::kPageSize / kPointerSize) / 23) / 16},
169         {7, ((Page::kPageSize / kPointerSize) / 7) / 32},
170         {3, ((Page::kPageSize / kPointerSize) / 3) / 256},
171         {1, 0}};
172   for (int i = 0; i < kSampleFinenesses; i++) {
173     ExemptPopularPages(samples[i].prime_sample_step, samples[i].threshold);
174     // As a last resort we mark all pages as being exempt from the store buffer.
175     DCHECK(i != (kSampleFinenesses - 1) || old_top_ == old_start_);
176     if (SpaceAvailable(space_needed)) return;
177   }
178   UNREACHABLE();
179 }
180 
181 
182 // Sample the store buffer to see if some pages are taking up a lot of space
183 // in the store buffer.
ExemptPopularPages(int prime_sample_step,int threshold)184 void StoreBuffer::ExemptPopularPages(int prime_sample_step, int threshold) {
185   PointerChunkIterator it(heap_);
186   MemoryChunk* chunk;
187   while ((chunk = it.next()) != NULL) {
188     chunk->set_store_buffer_counter(0);
189   }
190   bool created_new_scan_on_scavenge_pages = false;
191   MemoryChunk* previous_chunk = NULL;
192   for (Address* p = old_start_; p < old_top_; p += prime_sample_step) {
193     Address addr = *p;
194     MemoryChunk* containing_chunk = NULL;
195     if (previous_chunk != NULL && previous_chunk->Contains(addr)) {
196       containing_chunk = previous_chunk;
197     } else {
198       containing_chunk = MemoryChunk::FromAnyPointerAddress(heap_, addr);
199     }
200     int old_counter = containing_chunk->store_buffer_counter();
201     if (old_counter >= threshold) {
202       containing_chunk->set_scan_on_scavenge(true);
203       created_new_scan_on_scavenge_pages = true;
204     }
205     containing_chunk->set_store_buffer_counter(old_counter + 1);
206     previous_chunk = containing_chunk;
207   }
208   if (created_new_scan_on_scavenge_pages) {
209     Filter(MemoryChunk::SCAN_ON_SCAVENGE);
210     heap_->isolate()->CountUsage(
211         v8::Isolate::UseCounterFeature::kStoreBufferOverflow);
212   }
213   old_buffer_is_filtered_ = true;
214 }
215 
216 
Filter(int flag)217 void StoreBuffer::Filter(int flag) {
218   Address* new_top = old_start_;
219   MemoryChunk* previous_chunk = NULL;
220   for (Address* p = old_start_; p < old_top_; p++) {
221     Address addr = *p;
222     MemoryChunk* containing_chunk = NULL;
223     if (previous_chunk != NULL && previous_chunk->Contains(addr)) {
224       containing_chunk = previous_chunk;
225     } else {
226       containing_chunk = MemoryChunk::FromAnyPointerAddress(heap_, addr);
227       previous_chunk = containing_chunk;
228     }
229     if (!containing_chunk->IsFlagSet(flag)) {
230       *new_top++ = addr;
231     }
232   }
233   old_top_ = new_top;
234 
235   // Filtering hash sets are inconsistent with the store buffer after this
236   // operation.
237   ClearFilteringHashSets();
238 }
239 
240 
PrepareForIteration()241 bool StoreBuffer::PrepareForIteration() {
242   Compact();
243   PointerChunkIterator it(heap_);
244   MemoryChunk* chunk;
245   bool page_has_scan_on_scavenge_flag = false;
246   while ((chunk = it.next()) != NULL) {
247     if (chunk->scan_on_scavenge()) {
248       page_has_scan_on_scavenge_flag = true;
249       break;
250     }
251   }
252 
253   if (page_has_scan_on_scavenge_flag) {
254     Filter(MemoryChunk::SCAN_ON_SCAVENGE);
255   }
256 
257   // Filtering hash sets are inconsistent with the store buffer after
258   // iteration.
259   ClearFilteringHashSets();
260 
261   return page_has_scan_on_scavenge_flag;
262 }
263 
264 
ClearFilteringHashSets()265 void StoreBuffer::ClearFilteringHashSets() {
266   if (!hash_sets_are_empty_) {
267     memset(reinterpret_cast<void*>(hash_set_1_), 0,
268            sizeof(uintptr_t) * kHashSetLength);
269     memset(reinterpret_cast<void*>(hash_set_2_), 0,
270            sizeof(uintptr_t) * kHashSetLength);
271     hash_sets_are_empty_ = true;
272   }
273 }
274 
275 
GCPrologue()276 void StoreBuffer::GCPrologue() {
277   ClearFilteringHashSets();
278   during_gc_ = true;
279 }
280 
281 
282 #ifdef VERIFY_HEAP
VerifyPointers(LargeObjectSpace * space)283 void StoreBuffer::VerifyPointers(LargeObjectSpace* space) {
284   LargeObjectIterator it(space);
285   for (HeapObject* object = it.Next(); object != NULL; object = it.Next()) {
286     if (object->IsFixedArray()) {
287       Address slot_address = object->address();
288       Address end = object->address() + object->Size();
289 
290       while (slot_address < end) {
291         HeapObject** slot = reinterpret_cast<HeapObject**>(slot_address);
292         // When we are not in GC the Heap::InNewSpace() predicate
293         // checks that pointers which satisfy predicate point into
294         // the active semispace.
295         Object* object = *slot;
296         heap_->InNewSpace(object);
297         slot_address += kPointerSize;
298       }
299     }
300   }
301 }
302 #endif
303 
304 
Verify()305 void StoreBuffer::Verify() {
306 #ifdef VERIFY_HEAP
307   VerifyPointers(heap_->lo_space());
308 #endif
309 }
310 
311 
GCEpilogue()312 void StoreBuffer::GCEpilogue() {
313   during_gc_ = false;
314 #ifdef VERIFY_HEAP
315   if (FLAG_verify_heap) {
316     Verify();
317   }
318 #endif
319 }
320 
321 
ProcessOldToNewSlot(Address slot_address,ObjectSlotCallback slot_callback)322 void StoreBuffer::ProcessOldToNewSlot(Address slot_address,
323                                       ObjectSlotCallback slot_callback) {
324   Object** slot = reinterpret_cast<Object**>(slot_address);
325   Object* object = *slot;
326 
327   // If the object is not in from space, it must be a duplicate store buffer
328   // entry and the slot was already updated.
329   if (heap_->InFromSpace(object)) {
330     HeapObject* heap_object = reinterpret_cast<HeapObject*>(object);
331     DCHECK(heap_object->IsHeapObject());
332     slot_callback(reinterpret_cast<HeapObject**>(slot), heap_object);
333     object = *slot;
334     // If the object was in from space before and is after executing the
335     // callback in to space, the object is still live.
336     // Unfortunately, we do not know about the slot. It could be in a
337     // just freed free space object.
338     if (heap_->InToSpace(object)) {
339       EnterDirectlyIntoStoreBuffer(reinterpret_cast<Address>(slot));
340     }
341   }
342 }
343 
344 
FindPointersToNewSpaceInRegion(Address start,Address end,ObjectSlotCallback slot_callback)345 void StoreBuffer::FindPointersToNewSpaceInRegion(
346     Address start, Address end, ObjectSlotCallback slot_callback) {
347   for (Address slot_address = start; slot_address < end;
348        slot_address += kPointerSize) {
349     ProcessOldToNewSlot(slot_address, slot_callback);
350   }
351 }
352 
353 
IteratePointersInStoreBuffer(ObjectSlotCallback slot_callback)354 void StoreBuffer::IteratePointersInStoreBuffer(
355     ObjectSlotCallback slot_callback) {
356   Address* limit = old_top_;
357   old_top_ = old_start_;
358   {
359     DontMoveStoreBufferEntriesScope scope(this);
360     for (Address* current = old_start_; current < limit; current++) {
361 #ifdef DEBUG
362       Address* saved_top = old_top_;
363 #endif
364       ProcessOldToNewSlot(*current, slot_callback);
365       DCHECK(old_top_ == saved_top + 1 || old_top_ == saved_top);
366     }
367   }
368 }
369 
370 
ClearInvalidStoreBufferEntries()371 void StoreBuffer::ClearInvalidStoreBufferEntries() {
372   Compact();
373   Address* new_top = old_start_;
374   for (Address* current = old_start_; current < old_top_; current++) {
375     Address addr = *current;
376     Object** slot = reinterpret_cast<Object**>(addr);
377     Object* object = *slot;
378     if (heap_->InNewSpace(object) && object->IsHeapObject()) {
379       // If the target object is not black, the source slot must be part
380       // of a non-black (dead) object.
381       HeapObject* heap_object = HeapObject::cast(object);
382       if (Marking::IsBlack(Marking::MarkBitFrom(heap_object)) &&
383           heap_->mark_compact_collector()->IsSlotInLiveObject(addr)) {
384         *new_top++ = addr;
385       }
386     }
387   }
388   old_top_ = new_top;
389   ClearFilteringHashSets();
390 
391   // Don't scan on scavenge dead large objects.
392   LargeObjectIterator it(heap_->lo_space());
393   for (HeapObject* object = it.Next(); object != NULL; object = it.Next()) {
394     MemoryChunk* chunk = MemoryChunk::FromAddress(object->address());
395     if (chunk->scan_on_scavenge() &&
396         Marking::IsWhite(Marking::MarkBitFrom(object))) {
397       chunk->set_scan_on_scavenge(false);
398     }
399   }
400 }
401 
402 
VerifyValidStoreBufferEntries()403 void StoreBuffer::VerifyValidStoreBufferEntries() {
404   for (Address* current = old_start_; current < old_top_; current++) {
405     Object** slot = reinterpret_cast<Object**>(*current);
406     Object* object = *slot;
407     CHECK(object->IsHeapObject());
408     CHECK(heap_->InNewSpace(object));
409     heap_->mark_compact_collector()->VerifyIsSlotInLiveObject(
410         reinterpret_cast<Address>(slot), HeapObject::cast(object));
411   }
412 }
413 
414 
415 class FindPointersToNewSpaceVisitor final : public ObjectVisitor {
416  public:
FindPointersToNewSpaceVisitor(StoreBuffer * store_buffer,ObjectSlotCallback callback)417   FindPointersToNewSpaceVisitor(StoreBuffer* store_buffer,
418                                 ObjectSlotCallback callback)
419       : store_buffer_(store_buffer), callback_(callback) {}
420 
VisitPointers(Object ** start,Object ** end)421   V8_INLINE void VisitPointers(Object** start, Object** end) override {
422     store_buffer_->FindPointersToNewSpaceInRegion(
423         reinterpret_cast<Address>(start), reinterpret_cast<Address>(end),
424         callback_);
425   }
426 
VisitCodeEntry(Address code_entry_slot)427   V8_INLINE void VisitCodeEntry(Address code_entry_slot) override {}
428 
429  private:
430   StoreBuffer* store_buffer_;
431   ObjectSlotCallback callback_;
432 };
433 
434 
IteratePointersToNewSpace(ObjectSlotCallback slot_callback)435 void StoreBuffer::IteratePointersToNewSpace(ObjectSlotCallback slot_callback) {
436   // We do not sort or remove duplicated entries from the store buffer because
437   // we expect that callback will rebuild the store buffer thus removing
438   // all duplicates and pointers to old space.
439   bool some_pages_to_scan = PrepareForIteration();
440 
441   // TODO(gc): we want to skip slots on evacuation candidates
442   // but we can't simply figure that out from slot address
443   // because slot can belong to a large object.
444   IteratePointersInStoreBuffer(slot_callback);
445 
446   // We are done scanning all the pointers that were in the store buffer, but
447   // there may be some pages marked scan_on_scavenge that have pointers to new
448   // space that are not in the store buffer.  We must scan them now.  As we
449   // scan, the surviving pointers to new space will be added to the store
450   // buffer.  If there are still a lot of pointers to new space then we will
451   // keep the scan_on_scavenge flag on the page and discard the pointers that
452   // were added to the store buffer.  If there are not many pointers to new
453   // space left on the page we will keep the pointers in the store buffer and
454   // remove the flag from the page.
455   if (some_pages_to_scan) {
456     if (callback_ != NULL) {
457       (*callback_)(heap_, NULL, kStoreBufferStartScanningPagesEvent);
458     }
459     PointerChunkIterator it(heap_);
460     MemoryChunk* chunk;
461     FindPointersToNewSpaceVisitor visitor(this, slot_callback);
462     while ((chunk = it.next()) != NULL) {
463       if (chunk->scan_on_scavenge()) {
464         chunk->set_scan_on_scavenge(false);
465         if (callback_ != NULL) {
466           (*callback_)(heap_, chunk, kStoreBufferScanningPageEvent);
467         }
468         if (chunk->owner() == heap_->lo_space()) {
469           LargePage* large_page = reinterpret_cast<LargePage*>(chunk);
470           HeapObject* array = large_page->GetObject();
471           DCHECK(array->IsFixedArray());
472           Address start = array->address();
473           Address end = start + array->Size();
474           FindPointersToNewSpaceInRegion(start, end, slot_callback);
475         } else {
476           Page* page = reinterpret_cast<Page*>(chunk);
477           PagedSpace* owner = reinterpret_cast<PagedSpace*>(page->owner());
478           if (owner == heap_->map_space()) {
479             DCHECK(page->WasSwept());
480             HeapObjectIterator iterator(page);
481             for (HeapObject* heap_object = iterator.Next(); heap_object != NULL;
482                  heap_object = iterator.Next()) {
483               // We skip free space objects.
484               if (!heap_object->IsFiller()) {
485                 DCHECK(heap_object->IsMap());
486                 FindPointersToNewSpaceInRegion(
487                     heap_object->address() + Map::kPointerFieldsBeginOffset,
488                     heap_object->address() + Map::kPointerFieldsEndOffset,
489                     slot_callback);
490               }
491             }
492           } else {
493             if (page->IsFlagSet(Page::COMPACTION_WAS_ABORTED)) {
494               // Aborted pages require iterating using mark bits because they
495               // don't have an iterable object layout before sweeping (which can
496               // only happen later). Note that we can never reach an
497               // aborted page through the scavenger.
498               DCHECK_EQ(heap_->gc_state(), Heap::MARK_COMPACT);
499               heap_->mark_compact_collector()->VisitLiveObjectsBody(page,
500                                                                     &visitor);
501             } else {
502               heap_->mark_compact_collector()
503                   ->SweepOrWaitUntilSweepingCompleted(page);
504               HeapObjectIterator iterator(page);
505               for (HeapObject* heap_object = iterator.Next();
506                    heap_object != nullptr; heap_object = iterator.Next()) {
507                 // We iterate over objects that contain new space pointers only.
508                 heap_object->IterateBody(&visitor);
509               }
510             }
511           }
512         }
513       }
514     }
515     if (callback_ != NULL) {
516       (*callback_)(heap_, NULL, kStoreBufferScanningPageEvent);
517     }
518   }
519 }
520 
521 
Compact()522 void StoreBuffer::Compact() {
523   Address* top = reinterpret_cast<Address*>(heap_->store_buffer_top());
524 
525   if (top == start_) return;
526 
527   // There's no check of the limit in the loop below so we check here for
528   // the worst case (compaction doesn't eliminate any pointers).
529   DCHECK(top <= limit_);
530   heap_->set_store_buffer_top(reinterpret_cast<Smi*>(start_));
531   EnsureSpace(top - start_);
532   DCHECK(may_move_store_buffer_entries_);
533   // Goes through the addresses in the store buffer attempting to remove
534   // duplicates.  In the interest of speed this is a lossy operation.  Some
535   // duplicates will remain.  We have two hash sets with different hash
536   // functions to reduce the number of unnecessary clashes.
537   hash_sets_are_empty_ = false;  // Hash sets are in use.
538   for (Address* current = start_; current < top; current++) {
539     DCHECK(!heap_->code_space()->Contains(*current));
540     uintptr_t int_addr = reinterpret_cast<uintptr_t>(*current);
541     // Shift out the last bits including any tags.
542     int_addr >>= kPointerSizeLog2;
543     // The upper part of an address is basically random because of ASLR and OS
544     // non-determinism, so we use only the bits within a page for hashing to
545     // make v8's behavior (more) deterministic.
546     uintptr_t hash_addr =
547         int_addr & (Page::kPageAlignmentMask >> kPointerSizeLog2);
548     int hash1 = ((hash_addr ^ (hash_addr >> kHashSetLengthLog2)) &
549                  (kHashSetLength - 1));
550     if (hash_set_1_[hash1] == int_addr) continue;
551     uintptr_t hash2 = (hash_addr - (hash_addr >> kHashSetLengthLog2));
552     hash2 ^= hash2 >> (kHashSetLengthLog2 * 2);
553     hash2 &= (kHashSetLength - 1);
554     if (hash_set_2_[hash2] == int_addr) continue;
555     if (hash_set_1_[hash1] == 0) {
556       hash_set_1_[hash1] = int_addr;
557     } else if (hash_set_2_[hash2] == 0) {
558       hash_set_2_[hash2] = int_addr;
559     } else {
560       // Rather than slowing down we just throw away some entries.  This will
561       // cause some duplicates to remain undetected.
562       hash_set_1_[hash1] = int_addr;
563       hash_set_2_[hash2] = 0;
564     }
565     old_buffer_is_sorted_ = false;
566     old_buffer_is_filtered_ = false;
567     *old_top_++ = reinterpret_cast<Address>(int_addr << kPointerSizeLog2);
568     DCHECK(old_top_ <= old_limit_);
569   }
570   heap_->isolate()->counters()->store_buffer_compactions()->Increment();
571 }
572 
573 
Callback(MemoryChunk * page,StoreBufferEvent event)574 void StoreBufferRebuilder::Callback(MemoryChunk* page, StoreBufferEvent event) {
575   if (event == kStoreBufferStartScanningPagesEvent) {
576     start_of_current_page_ = NULL;
577     current_page_ = NULL;
578   } else if (event == kStoreBufferScanningPageEvent) {
579     if (current_page_ != NULL) {
580       // If this page already overflowed the store buffer during this iteration.
581       if (current_page_->scan_on_scavenge()) {
582         // Then we should wipe out the entries that have been added for it.
583         store_buffer_->SetTop(start_of_current_page_);
584       } else if (store_buffer_->Top() - start_of_current_page_ >=
585                  (store_buffer_->Limit() - store_buffer_->Top()) >> 2) {
586         // Did we find too many pointers in the previous page?  The heuristic is
587         // that no page can take more then 1/5 the remaining slots in the store
588         // buffer.
589         current_page_->set_scan_on_scavenge(true);
590         store_buffer_->SetTop(start_of_current_page_);
591       } else {
592         // In this case the page we scanned took a reasonable number of slots in
593         // the store buffer.  It has now been rehabilitated and is no longer
594         // marked scan_on_scavenge.
595         DCHECK(!current_page_->scan_on_scavenge());
596       }
597     }
598     start_of_current_page_ = store_buffer_->Top();
599     current_page_ = page;
600   } else if (event == kStoreBufferFullEvent) {
601     // The current page overflowed the store buffer again.  Wipe out its entries
602     // in the store buffer and mark it scan-on-scavenge again.  This may happen
603     // several times while scanning.
604     if (current_page_ == NULL) {
605       // Store Buffer overflowed while scanning promoted objects.  These are not
606       // in any particular page, though they are likely to be clustered by the
607       // allocation routines.
608       store_buffer_->EnsureSpace(StoreBuffer::kStoreBufferSize / 2);
609     } else {
610       // Store Buffer overflowed while scanning a particular old space page for
611       // pointers to new space.
612       DCHECK(current_page_ == page);
613       DCHECK(page != NULL);
614       current_page_->set_scan_on_scavenge(true);
615       DCHECK(start_of_current_page_ != store_buffer_->Top());
616       store_buffer_->SetTop(start_of_current_page_);
617     }
618   } else {
619     UNREACHABLE();
620   }
621 }
622 
623 }  // namespace internal
624 }  // namespace v8
625