Home | History | Annotate | Download | only in src
      1 // Copyright 2011 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 #include "v8.h"
     29 
     30 #include "store-buffer.h"
     31 #include "store-buffer-inl.h"
     32 #include "v8-counters.h"
     33 
     34 namespace v8 {
     35 namespace internal {
     36 
     37 StoreBuffer::StoreBuffer(Heap* heap)
     38     : heap_(heap),
     39       start_(NULL),
     40       limit_(NULL),
     41       old_start_(NULL),
     42       old_limit_(NULL),
     43       old_top_(NULL),
     44       old_reserved_limit_(NULL),
     45       old_buffer_is_sorted_(false),
     46       old_buffer_is_filtered_(false),
     47       during_gc_(false),
     48       store_buffer_rebuilding_enabled_(false),
     49       callback_(NULL),
     50       may_move_store_buffer_entries_(true),
     51       virtual_memory_(NULL),
     52       hash_set_1_(NULL),
     53       hash_set_2_(NULL),
     54       hash_sets_are_empty_(true) {
     55 }
     56 
     57 
     58 void StoreBuffer::SetUp() {
     59   virtual_memory_ = new VirtualMemory(kStoreBufferSize * 3);
     60   uintptr_t start_as_int =
     61       reinterpret_cast<uintptr_t>(virtual_memory_->address());
     62   start_ =
     63       reinterpret_cast<Address*>(RoundUp(start_as_int, kStoreBufferSize * 2));
     64   limit_ = start_ + (kStoreBufferSize / kPointerSize);
     65 
     66   old_virtual_memory_ =
     67       new VirtualMemory(kOldStoreBufferLength * kPointerSize);
     68   old_top_ = old_start_ =
     69       reinterpret_cast<Address*>(old_virtual_memory_->address());
     70   // Don't know the alignment requirements of the OS, but it is certainly not
     71   // less than 0xfff.
     72   ASSERT((reinterpret_cast<uintptr_t>(old_start_) & 0xfff) == 0);
     73   int initial_length = static_cast<int>(OS::CommitPageSize() / kPointerSize);
     74   ASSERT(initial_length > 0);
     75   ASSERT(initial_length <= kOldStoreBufferLength);
     76   old_limit_ = old_start_ + initial_length;
     77   old_reserved_limit_ = old_start_ + kOldStoreBufferLength;
     78 
     79   CHECK(old_virtual_memory_->Commit(
     80             reinterpret_cast<void*>(old_start_),
     81             (old_limit_ - old_start_) * kPointerSize,
     82             false));
     83 
     84   ASSERT(reinterpret_cast<Address>(start_) >= virtual_memory_->address());
     85   ASSERT(reinterpret_cast<Address>(limit_) >= virtual_memory_->address());
     86   Address* vm_limit = reinterpret_cast<Address*>(
     87       reinterpret_cast<char*>(virtual_memory_->address()) +
     88           virtual_memory_->size());
     89   ASSERT(start_ <= vm_limit);
     90   ASSERT(limit_ <= vm_limit);
     91   USE(vm_limit);
     92   ASSERT((reinterpret_cast<uintptr_t>(limit_) & kStoreBufferOverflowBit) != 0);
     93   ASSERT((reinterpret_cast<uintptr_t>(limit_ - 1) & kStoreBufferOverflowBit) ==
     94          0);
     95 
     96   CHECK(virtual_memory_->Commit(reinterpret_cast<Address>(start_),
     97                                 kStoreBufferSize,
     98                                 false));  // Not executable.
     99   heap_->public_set_store_buffer_top(start_);
    100 
    101   hash_set_1_ = new uintptr_t[kHashSetLength];
    102   hash_set_2_ = new uintptr_t[kHashSetLength];
    103   hash_sets_are_empty_ = false;
    104 
    105   ClearFilteringHashSets();
    106 }
    107 
    108 
    109 void StoreBuffer::TearDown() {
    110   delete virtual_memory_;
    111   delete old_virtual_memory_;
    112   delete[] hash_set_1_;
    113   delete[] hash_set_2_;
    114   old_start_ = old_top_ = old_limit_ = old_reserved_limit_ = NULL;
    115   start_ = limit_ = NULL;
    116   heap_->public_set_store_buffer_top(start_);
    117 }
    118 
    119 
    120 void StoreBuffer::StoreBufferOverflow(Isolate* isolate) {
    121   isolate->heap()->store_buffer()->Compact();
    122 }
    123 
    124 
    125 #if V8_TARGET_ARCH_X64
    126 static int CompareAddresses(const void* void_a, const void* void_b) {
    127   intptr_t a =
    128       reinterpret_cast<intptr_t>(*reinterpret_cast<const Address*>(void_a));
    129   intptr_t b =
    130       reinterpret_cast<intptr_t>(*reinterpret_cast<const Address*>(void_b));
    131   // Unfortunately if int is smaller than intptr_t there is no branch-free
    132   // way to return a number with the same sign as the difference between the
    133   // pointers.
    134   if (a == b) return 0;
    135   if (a < b) return -1;
    136   ASSERT(a > b);
    137   return 1;
    138 }
    139 #else
    140 static int CompareAddresses(const void* void_a, const void* void_b) {
    141   intptr_t a =
    142       reinterpret_cast<intptr_t>(*reinterpret_cast<const Address*>(void_a));
    143   intptr_t b =
    144       reinterpret_cast<intptr_t>(*reinterpret_cast<const Address*>(void_b));
    145   ASSERT(sizeof(1) == sizeof(a));
    146   // Shift down to avoid wraparound.
    147   return (a >> kPointerSizeLog2) - (b >> kPointerSizeLog2);
    148 }
    149 #endif
    150 
    151 
    152 void StoreBuffer::Uniq() {
    153   // Remove adjacent duplicates and cells that do not point at new space.
    154   Address previous = NULL;
    155   Address* write = old_start_;
    156   ASSERT(may_move_store_buffer_entries_);
    157   for (Address* read = old_start_; read < old_top_; read++) {
    158     Address current = *read;
    159     if (current != previous) {
    160       if (heap_->InNewSpace(*reinterpret_cast<Object**>(current))) {
    161         *write++ = current;
    162       }
    163     }
    164     previous = current;
    165   }
    166   old_top_ = write;
    167 }
    168 
    169 
    170 void StoreBuffer::EnsureSpace(intptr_t space_needed) {
    171   while (old_limit_ - old_top_ < space_needed &&
    172          old_limit_ < old_reserved_limit_) {
    173     size_t grow = old_limit_ - old_start_;  // Double size.
    174     CHECK(old_virtual_memory_->Commit(reinterpret_cast<void*>(old_limit_),
    175                                       grow * kPointerSize,
    176                                       false));
    177     old_limit_ += grow;
    178   }
    179 
    180   if (old_limit_ - old_top_ >= space_needed) return;
    181 
    182   if (old_buffer_is_filtered_) return;
    183   ASSERT(may_move_store_buffer_entries_);
    184   Compact();
    185 
    186   old_buffer_is_filtered_ = true;
    187   bool page_has_scan_on_scavenge_flag = false;
    188 
    189   PointerChunkIterator it(heap_);
    190   MemoryChunk* chunk;
    191   while ((chunk = it.next()) != NULL) {
    192     if (chunk->scan_on_scavenge()) page_has_scan_on_scavenge_flag = true;
    193   }
    194 
    195   if (page_has_scan_on_scavenge_flag) {
    196     Filter(MemoryChunk::SCAN_ON_SCAVENGE);
    197   }
    198 
    199   // If filtering out the entries from scan_on_scavenge pages got us down to
    200   // less than half full, then we are satisfied with that.
    201   if (old_limit_ - old_top_ > old_top_ - old_start_) return;
    202 
    203   // Sample 1 entry in 97 and filter out the pages where we estimate that more
    204   // than 1 in 8 pointers are to new space.
    205   static const int kSampleFinenesses = 5;
    206   static const struct Samples {
    207     int prime_sample_step;
    208     int threshold;
    209   } samples[kSampleFinenesses] =  {
    210     { 97, ((Page::kPageSize / kPointerSize) / 97) / 8 },
    211     { 23, ((Page::kPageSize / kPointerSize) / 23) / 16 },
    212     { 7, ((Page::kPageSize / kPointerSize) / 7) / 32 },
    213     { 3, ((Page::kPageSize / kPointerSize) / 3) / 256 },
    214     { 1, 0}
    215   };
    216   for (int i = kSampleFinenesses - 1; i >= 0; i--) {
    217     ExemptPopularPages(samples[i].prime_sample_step, samples[i].threshold);
    218     // As a last resort we mark all pages as being exempt from the store buffer.
    219     ASSERT(i != 0 || old_top_ == old_start_);
    220     if (old_limit_ - old_top_ > old_top_ - old_start_) return;
    221   }
    222   UNREACHABLE();
    223 }
    224 
    225 
    226 // Sample the store buffer to see if some pages are taking up a lot of space
    227 // in the store buffer.
    228 void StoreBuffer::ExemptPopularPages(int prime_sample_step, int threshold) {
    229   PointerChunkIterator it(heap_);
    230   MemoryChunk* chunk;
    231   while ((chunk = it.next()) != NULL) {
    232     chunk->set_store_buffer_counter(0);
    233   }
    234   bool created_new_scan_on_scavenge_pages = false;
    235   MemoryChunk* previous_chunk = NULL;
    236   for (Address* p = old_start_; p < old_top_; p += prime_sample_step) {
    237     Address addr = *p;
    238     MemoryChunk* containing_chunk = NULL;
    239     if (previous_chunk != NULL && previous_chunk->Contains(addr)) {
    240       containing_chunk = previous_chunk;
    241     } else {
    242       containing_chunk = MemoryChunk::FromAnyPointerAddress(addr);
    243     }
    244     int old_counter = containing_chunk->store_buffer_counter();
    245     if (old_counter == threshold) {
    246       containing_chunk->set_scan_on_scavenge(true);
    247       created_new_scan_on_scavenge_pages = true;
    248     }
    249     containing_chunk->set_store_buffer_counter(old_counter + 1);
    250     previous_chunk = containing_chunk;
    251   }
    252   if (created_new_scan_on_scavenge_pages) {
    253     Filter(MemoryChunk::SCAN_ON_SCAVENGE);
    254   }
    255   old_buffer_is_filtered_ = true;
    256 }
    257 
    258 
    259 void StoreBuffer::Filter(int flag) {
    260   Address* new_top = old_start_;
    261   MemoryChunk* previous_chunk = NULL;
    262   for (Address* p = old_start_; p < old_top_; p++) {
    263     Address addr = *p;
    264     MemoryChunk* containing_chunk = NULL;
    265     if (previous_chunk != NULL && previous_chunk->Contains(addr)) {
    266       containing_chunk = previous_chunk;
    267     } else {
    268       containing_chunk = MemoryChunk::FromAnyPointerAddress(addr);
    269       previous_chunk = containing_chunk;
    270     }
    271     if (!containing_chunk->IsFlagSet(flag)) {
    272       *new_top++ = addr;
    273     }
    274   }
    275   old_top_ = new_top;
    276 
    277   // Filtering hash sets are inconsistent with the store buffer after this
    278   // operation.
    279   ClearFilteringHashSets();
    280 }
    281 
    282 
    283 void StoreBuffer::SortUniq() {
    284   Compact();
    285   if (old_buffer_is_sorted_) return;
    286   qsort(reinterpret_cast<void*>(old_start_),
    287         old_top_ - old_start_,
    288         sizeof(*old_top_),
    289         &CompareAddresses);
    290   Uniq();
    291 
    292   old_buffer_is_sorted_ = true;
    293 
    294   // Filtering hash sets are inconsistent with the store buffer after this
    295   // operation.
    296   ClearFilteringHashSets();
    297 }
    298 
    299 
    300 bool StoreBuffer::PrepareForIteration() {
    301   Compact();
    302   PointerChunkIterator it(heap_);
    303   MemoryChunk* chunk;
    304   bool page_has_scan_on_scavenge_flag = false;
    305   while ((chunk = it.next()) != NULL) {
    306     if (chunk->scan_on_scavenge()) page_has_scan_on_scavenge_flag = true;
    307   }
    308 
    309   if (page_has_scan_on_scavenge_flag) {
    310     Filter(MemoryChunk::SCAN_ON_SCAVENGE);
    311   }
    312 
    313   // Filtering hash sets are inconsistent with the store buffer after
    314   // iteration.
    315   ClearFilteringHashSets();
    316 
    317   return page_has_scan_on_scavenge_flag;
    318 }
    319 
    320 
    321 #ifdef DEBUG
    322 void StoreBuffer::Clean() {
    323   ClearFilteringHashSets();
    324   Uniq();  // Also removes things that no longer point to new space.
    325   CheckForFullBuffer();
    326 }
    327 
    328 
    329 static Address* in_store_buffer_1_element_cache = NULL;
    330 
    331 
    332 bool StoreBuffer::CellIsInStoreBuffer(Address cell_address) {
    333   if (!FLAG_enable_slow_asserts) return true;
    334   if (in_store_buffer_1_element_cache != NULL &&
    335       *in_store_buffer_1_element_cache == cell_address) {
    336     return true;
    337   }
    338   Address* top = reinterpret_cast<Address*>(heap_->store_buffer_top());
    339   for (Address* current = top - 1; current >= start_; current--) {
    340     if (*current == cell_address) {
    341       in_store_buffer_1_element_cache = current;
    342       return true;
    343     }
    344   }
    345   for (Address* current = old_top_ - 1; current >= old_start_; current--) {
    346     if (*current == cell_address) {
    347       in_store_buffer_1_element_cache = current;
    348       return true;
    349     }
    350   }
    351   return false;
    352 }
    353 #endif
    354 
    355 
    356 void StoreBuffer::ClearFilteringHashSets() {
    357   if (!hash_sets_are_empty_) {
    358     memset(reinterpret_cast<void*>(hash_set_1_),
    359            0,
    360            sizeof(uintptr_t) * kHashSetLength);
    361     memset(reinterpret_cast<void*>(hash_set_2_),
    362            0,
    363            sizeof(uintptr_t) * kHashSetLength);
    364     hash_sets_are_empty_ = true;
    365   }
    366 }
    367 
    368 
    369 void StoreBuffer::GCPrologue() {
    370   ClearFilteringHashSets();
    371   during_gc_ = true;
    372 }
    373 
    374 
    375 #ifdef DEBUG
    376 static void DummyScavengePointer(HeapObject** p, HeapObject* o) {
    377   // Do nothing.
    378 }
    379 
    380 
    381 void StoreBuffer::VerifyPointers(PagedSpace* space,
    382                                  RegionCallback region_callback) {
    383   PageIterator it(space);
    384 
    385   while (it.has_next()) {
    386     Page* page = it.next();
    387     FindPointersToNewSpaceOnPage(
    388         reinterpret_cast<PagedSpace*>(page->owner()),
    389         page,
    390         region_callback,
    391         &DummyScavengePointer);
    392   }
    393 }
    394 
    395 
    396 void StoreBuffer::VerifyPointers(LargeObjectSpace* space) {
    397   LargeObjectIterator it(space);
    398   for (HeapObject* object = it.Next(); object != NULL; object = it.Next()) {
    399     if (object->IsFixedArray()) {
    400       Address slot_address = object->address();
    401       Address end = object->address() + object->Size();
    402 
    403       while (slot_address < end) {
    404         HeapObject** slot = reinterpret_cast<HeapObject**>(slot_address);
    405         // When we are not in GC the Heap::InNewSpace() predicate
    406         // checks that pointers which satisfy predicate point into
    407         // the active semispace.
    408         heap_->InNewSpace(*slot);
    409         slot_address += kPointerSize;
    410       }
    411     }
    412   }
    413 }
    414 #endif
    415 
    416 
    417 void StoreBuffer::Verify() {
    418 #ifdef DEBUG
    419   VerifyPointers(heap_->old_pointer_space(),
    420                  &StoreBuffer::FindPointersToNewSpaceInRegion);
    421   VerifyPointers(heap_->map_space(),
    422                  &StoreBuffer::FindPointersToNewSpaceInMapsRegion);
    423   VerifyPointers(heap_->lo_space());
    424 #endif
    425 }
    426 
    427 
    428 void StoreBuffer::GCEpilogue() {
    429   during_gc_ = false;
    430   if (FLAG_verify_heap) {
    431     Verify();
    432   }
    433 }
    434 
    435 
    436 void StoreBuffer::FindPointersToNewSpaceInRegion(
    437     Address start, Address end, ObjectSlotCallback slot_callback) {
    438   for (Address slot_address = start;
    439        slot_address < end;
    440        slot_address += kPointerSize) {
    441     Object** slot = reinterpret_cast<Object**>(slot_address);
    442     if (heap_->InNewSpace(*slot)) {
    443       HeapObject* object = reinterpret_cast<HeapObject*>(*slot);
    444       ASSERT(object->IsHeapObject());
    445       slot_callback(reinterpret_cast<HeapObject**>(slot), object);
    446       if (heap_->InNewSpace(*slot)) {
    447         EnterDirectlyIntoStoreBuffer(slot_address);
    448       }
    449     }
    450   }
    451 }
    452 
    453 
    454 // Compute start address of the first map following given addr.
    455 static inline Address MapStartAlign(Address addr) {
    456   Address page = Page::FromAddress(addr)->area_start();
    457   return page + (((addr - page) + (Map::kSize - 1)) / Map::kSize * Map::kSize);
    458 }
    459 
    460 
    461 // Compute end address of the first map preceding given addr.
    462 static inline Address MapEndAlign(Address addr) {
    463   Address page = Page::FromAllocationTop(addr)->area_start();
    464   return page + ((addr - page) / Map::kSize * Map::kSize);
    465 }
    466 
    467 
    468 void StoreBuffer::FindPointersToNewSpaceInMaps(
    469     Address start,
    470     Address end,
    471     ObjectSlotCallback slot_callback) {
    472   ASSERT(MapStartAlign(start) == start);
    473   ASSERT(MapEndAlign(end) == end);
    474 
    475   Address map_address = start;
    476   while (map_address < end) {
    477     ASSERT(!heap_->InNewSpace(Memory::Object_at(map_address)));
    478     ASSERT(Memory::Object_at(map_address)->IsMap());
    479 
    480     Address pointer_fields_start = map_address + Map::kPointerFieldsBeginOffset;
    481     Address pointer_fields_end = map_address + Map::kPointerFieldsEndOffset;
    482 
    483     FindPointersToNewSpaceInRegion(pointer_fields_start,
    484                                    pointer_fields_end,
    485                                    slot_callback);
    486     map_address += Map::kSize;
    487   }
    488 }
    489 
    490 
    491 void StoreBuffer::FindPointersToNewSpaceInMapsRegion(
    492     Address start,
    493     Address end,
    494     ObjectSlotCallback slot_callback) {
    495   Address map_aligned_start = MapStartAlign(start);
    496   Address map_aligned_end   = MapEndAlign(end);
    497 
    498   ASSERT(map_aligned_start == start);
    499   ASSERT(map_aligned_end == end);
    500 
    501   FindPointersToNewSpaceInMaps(map_aligned_start,
    502                                map_aligned_end,
    503                                slot_callback);
    504 }
    505 
    506 
    507 // This function iterates over all the pointers in a paged space in the heap,
    508 // looking for pointers into new space.  Within the pages there may be dead
    509 // objects that have not been overwritten by free spaces or fillers because of
    510 // lazy sweeping.  These dead objects may not contain pointers to new space.
    511 // The garbage areas that have been swept properly (these will normally be the
    512 // large ones) will be marked with free space and filler map words.  In
    513 // addition any area that has never been used at all for object allocation must
    514 // be marked with a free space or filler.  Because the free space and filler
    515 // maps do not move we can always recognize these even after a compaction.
    516 // Normal objects like FixedArrays and JSObjects should not contain references
    517 // to these maps.  The special garbage section (see comment in spaces.h) is
    518 // skipped since it can contain absolutely anything.  Any objects that are
    519 // allocated during iteration may or may not be visited by the iteration, but
    520 // they will not be partially visited.
    521 void StoreBuffer::FindPointersToNewSpaceOnPage(
    522     PagedSpace* space,
    523     Page* page,
    524     RegionCallback region_callback,
    525     ObjectSlotCallback slot_callback) {
    526   Address visitable_start = page->area_start();
    527   Address end_of_page = page->area_end();
    528 
    529   Address visitable_end = visitable_start;
    530 
    531   Object* free_space_map = heap_->free_space_map();
    532   Object* two_pointer_filler_map = heap_->two_pointer_filler_map();
    533 
    534   while (visitable_end < end_of_page) {
    535     Object* o = *reinterpret_cast<Object**>(visitable_end);
    536     // Skip fillers but not things that look like fillers in the special
    537     // garbage section which can contain anything.
    538     if (o == free_space_map ||
    539         o == two_pointer_filler_map ||
    540         (visitable_end == space->top() && visitable_end != space->limit())) {
    541       if (visitable_start != visitable_end) {
    542         // After calling this the special garbage section may have moved.
    543         (this->*region_callback)(visitable_start,
    544                                  visitable_end,
    545                                  slot_callback);
    546         if (visitable_end >= space->top() && visitable_end < space->limit()) {
    547           visitable_end = space->limit();
    548           visitable_start = visitable_end;
    549           continue;
    550         }
    551       }
    552       if (visitable_end == space->top() && visitable_end != space->limit()) {
    553         visitable_start = visitable_end = space->limit();
    554       } else {
    555         // At this point we are either at the start of a filler or we are at
    556         // the point where the space->top() used to be before the
    557         // visit_pointer_region call above.  Either way we can skip the
    558         // object at the current spot:  We don't promise to visit objects
    559         // allocated during heap traversal, and if space->top() moved then it
    560         // must be because an object was allocated at this point.
    561         visitable_start =
    562             visitable_end + HeapObject::FromAddress(visitable_end)->Size();
    563         visitable_end = visitable_start;
    564       }
    565     } else {
    566       ASSERT(o != free_space_map);
    567       ASSERT(o != two_pointer_filler_map);
    568       ASSERT(visitable_end < space->top() || visitable_end >= space->limit());
    569       visitable_end += kPointerSize;
    570     }
    571   }
    572   ASSERT(visitable_end == end_of_page);
    573   if (visitable_start != visitable_end) {
    574     (this->*region_callback)(visitable_start,
    575                              visitable_end,
    576                              slot_callback);
    577   }
    578 }
    579 
    580 
    581 void StoreBuffer::IteratePointersInStoreBuffer(
    582     ObjectSlotCallback slot_callback) {
    583   Address* limit = old_top_;
    584   old_top_ = old_start_;
    585   {
    586     DontMoveStoreBufferEntriesScope scope(this);
    587     for (Address* current = old_start_; current < limit; current++) {
    588 #ifdef DEBUG
    589       Address* saved_top = old_top_;
    590 #endif
    591       Object** slot = reinterpret_cast<Object**>(*current);
    592       Object* object = *slot;
    593       if (heap_->InFromSpace(object)) {
    594         HeapObject* heap_object = reinterpret_cast<HeapObject*>(object);
    595         slot_callback(reinterpret_cast<HeapObject**>(slot), heap_object);
    596         if (heap_->InNewSpace(*slot)) {
    597           EnterDirectlyIntoStoreBuffer(reinterpret_cast<Address>(slot));
    598         }
    599       }
    600       ASSERT(old_top_ == saved_top + 1 || old_top_ == saved_top);
    601     }
    602   }
    603 }
    604 
    605 
    606 void StoreBuffer::IteratePointersToNewSpace(ObjectSlotCallback slot_callback) {
    607   // We do not sort or remove duplicated entries from the store buffer because
    608   // we expect that callback will rebuild the store buffer thus removing
    609   // all duplicates and pointers to old space.
    610   bool some_pages_to_scan = PrepareForIteration();
    611 
    612   // TODO(gc): we want to skip slots on evacuation candidates
    613   // but we can't simply figure that out from slot address
    614   // because slot can belong to a large object.
    615   IteratePointersInStoreBuffer(slot_callback);
    616 
    617   // We are done scanning all the pointers that were in the store buffer, but
    618   // there may be some pages marked scan_on_scavenge that have pointers to new
    619   // space that are not in the store buffer.  We must scan them now.  As we
    620   // scan, the surviving pointers to new space will be added to the store
    621   // buffer.  If there are still a lot of pointers to new space then we will
    622   // keep the scan_on_scavenge flag on the page and discard the pointers that
    623   // were added to the store buffer.  If there are not many pointers to new
    624   // space left on the page we will keep the pointers in the store buffer and
    625   // remove the flag from the page.
    626   if (some_pages_to_scan) {
    627     if (callback_ != NULL) {
    628       (*callback_)(heap_, NULL, kStoreBufferStartScanningPagesEvent);
    629     }
    630     PointerChunkIterator it(heap_);
    631     MemoryChunk* chunk;
    632     while ((chunk = it.next()) != NULL) {
    633       if (chunk->scan_on_scavenge()) {
    634         chunk->set_scan_on_scavenge(false);
    635         if (callback_ != NULL) {
    636           (*callback_)(heap_, chunk, kStoreBufferScanningPageEvent);
    637         }
    638         if (chunk->owner() == heap_->lo_space()) {
    639           LargePage* large_page = reinterpret_cast<LargePage*>(chunk);
    640           HeapObject* array = large_page->GetObject();
    641           ASSERT(array->IsFixedArray());
    642           Address start = array->address();
    643           Address end = start + array->Size();
    644           FindPointersToNewSpaceInRegion(start, end, slot_callback);
    645         } else {
    646           Page* page = reinterpret_cast<Page*>(chunk);
    647           PagedSpace* owner = reinterpret_cast<PagedSpace*>(page->owner());
    648           FindPointersToNewSpaceOnPage(
    649               owner,
    650               page,
    651               (owner == heap_->map_space() ?
    652                  &StoreBuffer::FindPointersToNewSpaceInMapsRegion :
    653                  &StoreBuffer::FindPointersToNewSpaceInRegion),
    654               slot_callback);
    655         }
    656       }
    657     }
    658     if (callback_ != NULL) {
    659       (*callback_)(heap_, NULL, kStoreBufferScanningPageEvent);
    660     }
    661   }
    662 }
    663 
    664 
    665 void StoreBuffer::Compact() {
    666   Address* top = reinterpret_cast<Address*>(heap_->store_buffer_top());
    667 
    668   if (top == start_) return;
    669 
    670   // There's no check of the limit in the loop below so we check here for
    671   // the worst case (compaction doesn't eliminate any pointers).
    672   ASSERT(top <= limit_);
    673   heap_->public_set_store_buffer_top(start_);
    674   EnsureSpace(top - start_);
    675   ASSERT(may_move_store_buffer_entries_);
    676   // Goes through the addresses in the store buffer attempting to remove
    677   // duplicates.  In the interest of speed this is a lossy operation.  Some
    678   // duplicates will remain.  We have two hash sets with different hash
    679   // functions to reduce the number of unnecessary clashes.
    680   hash_sets_are_empty_ = false;  // Hash sets are in use.
    681   for (Address* current = start_; current < top; current++) {
    682     ASSERT(!heap_->cell_space()->Contains(*current));
    683     ASSERT(!heap_->code_space()->Contains(*current));
    684     ASSERT(!heap_->old_data_space()->Contains(*current));
    685     uintptr_t int_addr = reinterpret_cast<uintptr_t>(*current);
    686     // Shift out the last bits including any tags.
    687     int_addr >>= kPointerSizeLog2;
    688     int hash1 =
    689         ((int_addr ^ (int_addr >> kHashSetLengthLog2)) & (kHashSetLength - 1));
    690     if (hash_set_1_[hash1] == int_addr) continue;
    691     uintptr_t hash2 = (int_addr - (int_addr >> kHashSetLengthLog2));
    692     hash2 ^= hash2 >> (kHashSetLengthLog2 * 2);
    693     hash2 &= (kHashSetLength - 1);
    694     if (hash_set_2_[hash2] == int_addr) continue;
    695     if (hash_set_1_[hash1] == 0) {
    696       hash_set_1_[hash1] = int_addr;
    697     } else if (hash_set_2_[hash2] == 0) {
    698       hash_set_2_[hash2] = int_addr;
    699     } else {
    700       // Rather than slowing down we just throw away some entries.  This will
    701       // cause some duplicates to remain undetected.
    702       hash_set_1_[hash1] = int_addr;
    703       hash_set_2_[hash2] = 0;
    704     }
    705     old_buffer_is_sorted_ = false;
    706     old_buffer_is_filtered_ = false;
    707     *old_top_++ = reinterpret_cast<Address>(int_addr << kPointerSizeLog2);
    708     ASSERT(old_top_ <= old_limit_);
    709   }
    710   heap_->isolate()->counters()->store_buffer_compactions()->Increment();
    711   CheckForFullBuffer();
    712 }
    713 
    714 
    715 void StoreBuffer::CheckForFullBuffer() {
    716   EnsureSpace(kStoreBufferSize * 2);
    717 }
    718 
    719 } }  // namespace v8::internal
    720