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