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