Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| 11 // with the distribution. | 11 // with the distribution. |
| 12 // * Neither the name of Google Inc. nor the names of its | 12 // * Neither the name of Google Inc. nor the names of its |
| 13 // contributors may be used to endorse or promote products derived | 13 // contributors may be used to endorse or promote products derived |
| 14 // from this software without specific prior written permission. | 14 // from this software without specific prior written permission. |
| 15 // | 15 // |
| 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | 27 |
| 28 #include "v8-counters.h" | 28 #include "v8.h" |
| 29 | |
| 29 #include "store-buffer.h" | 30 #include "store-buffer.h" |
| 30 #include "store-buffer-inl.h" | 31 #include "store-buffer-inl.h" |
| 32 #include "v8-counters.h" | |
| 31 | 33 |
| 32 namespace v8 { | 34 namespace v8 { |
| 33 namespace internal { | 35 namespace internal { |
| 34 | 36 |
| 35 Address* StoreBuffer::start_ = NULL; | 37 Address* StoreBuffer::start_ = NULL; |
| 36 Address* StoreBuffer::limit_ = NULL; | 38 Address* StoreBuffer::limit_ = NULL; |
| 37 Address* StoreBuffer::old_start_ = NULL; | 39 Address* StoreBuffer::old_start_ = NULL; |
| 38 Address* StoreBuffer::old_limit_ = NULL; | 40 Address* StoreBuffer::old_limit_ = NULL; |
| 39 Address* StoreBuffer::old_top_ = NULL; | 41 Address* StoreBuffer::old_top_ = NULL; |
| 40 uintptr_t* StoreBuffer::hash_map_1_ = NULL; | 42 uintptr_t* StoreBuffer::hash_map_1_ = NULL; |
| 41 uintptr_t* StoreBuffer::hash_map_2_ = NULL; | 43 uintptr_t* StoreBuffer::hash_map_2_ = NULL; |
| 42 VirtualMemory* StoreBuffer::virtual_memory_ = NULL; | 44 VirtualMemory* StoreBuffer::virtual_memory_ = NULL; |
| 43 StoreBuffer::StoreBufferMode StoreBuffer::store_buffer_mode_ = | |
| 44 kStoreBufferFunctional; | |
| 45 bool StoreBuffer::old_buffer_is_sorted_ = false; | 45 bool StoreBuffer::old_buffer_is_sorted_ = false; |
| 46 bool StoreBuffer::old_buffer_is_filtered_ = false; | |
| 46 bool StoreBuffer::during_gc_ = false; | 47 bool StoreBuffer::during_gc_ = false; |
| 48 bool StoreBuffer::may_move_store_buffer_entries_ = true; | |
| 47 bool StoreBuffer::store_buffer_rebuilding_enabled_ = false; | 49 bool StoreBuffer::store_buffer_rebuilding_enabled_ = false; |
| 48 bool StoreBuffer::may_move_store_buffer_entries_ = true; | 50 StoreBufferCallback StoreBuffer::callback_ = NULL; |
| 49 | 51 |
| 50 void StoreBuffer::Setup() { | 52 void StoreBuffer::Setup() { |
| 51 virtual_memory_ = new VirtualMemory(kStoreBufferSize * 3); | 53 virtual_memory_ = new VirtualMemory(kStoreBufferSize * 3); |
| 52 uintptr_t start_as_int = | 54 uintptr_t start_as_int = |
| 53 reinterpret_cast<uintptr_t>(virtual_memory_->address()); | 55 reinterpret_cast<uintptr_t>(virtual_memory_->address()); |
| 54 start_ = | 56 start_ = |
| 55 reinterpret_cast<Address*>(RoundUp(start_as_int, kStoreBufferSize * 2)); | 57 reinterpret_cast<Address*>(RoundUp(start_as_int, kStoreBufferSize * 2)); |
| 56 limit_ = start_ + (kStoreBufferSize / sizeof(*start_)); | 58 limit_ = start_ + (kStoreBufferSize / sizeof(*start_)); |
| 57 | 59 |
| 58 old_top_ = old_start_ = new Address[kOldStoreBufferLength]; | 60 old_top_ = old_start_ = new Address[kOldStoreBufferLength]; |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 135 if (Heap::InNewSpace(*reinterpret_cast<Object**>(current))) { | 137 if (Heap::InNewSpace(*reinterpret_cast<Object**>(current))) { |
| 136 *write++ = current; | 138 *write++ = current; |
| 137 } | 139 } |
| 138 } | 140 } |
| 139 previous = current; | 141 previous = current; |
| 140 } | 142 } |
| 141 old_top_ = write; | 143 old_top_ = write; |
| 142 } | 144 } |
| 143 | 145 |
| 144 | 146 |
| 147 void StoreBuffer::HandleFullness() { | |
| 148 if (old_buffer_is_filtered_) return; | |
| 149 ASSERT(may_move_store_buffer_entries_); | |
| 150 Compact(); | |
| 151 | |
| 152 old_buffer_is_filtered_ = true; | |
| 153 bool page_has_scan_on_scavenge_flag = false; | |
| 154 | |
| 155 PointerChunkIterator it; | |
| 156 MemoryChunk* chunk; | |
| 157 while ((chunk = it.next()) != NULL) { | |
| 158 if (chunk->scan_on_scavenge()) page_has_scan_on_scavenge_flag = true; | |
| 159 } | |
| 160 | |
| 161 if (page_has_scan_on_scavenge_flag) { | |
| 162 FilterScanOnScavengeEntries(); | |
| 163 } | |
| 164 | |
| 165 // If filtering out the entries from scan_on_scavenge pages got us down to | |
| 166 // less than half full, then we are satisfied with that. | |
| 167 if (old_limit_ - old_top_ > old_top_ - old_start_) return; | |
| 168 | |
| 169 // Sample 1 entry in 97 and filter out the pages where we estimate that more | |
| 170 // than 1 in 8 pointers are to new space. | |
| 171 ExemptPopularPages(97, ((Page::kPageSize / kPointerSize) / 97) / 8); | |
|
Vyacheslav Egorov (Chromium)
2011/03/28 15:13:19
this looks like a repeatable pattern.
loop with ar
Erik Corry
2011/03/28 15:56:07
Done.
| |
| 172 if (old_limit_ - old_top_ > old_top_ - old_start_) return; | |
| 173 | |
| 174 // Sample more finely and reduce the threshold for exempting pages from the | |
| 175 // store buffer. | |
| 176 ExemptPopularPages(23, ((Page::kPageSize / kPointerSize) / 23) / 16); | |
| 177 if (old_limit_ - old_top_ > old_top_ - old_start_) return; | |
| 178 ExemptPopularPages(7, ((Page::kPageSize / kPointerSize) / 7) / 32); | |
| 179 if (old_limit_ - old_top_ > old_top_ - old_start_) return; | |
| 180 ExemptPopularPages(3, ((Page::kPageSize / kPointerSize) / 3) / 256); | |
| 181 if (old_limit_ - old_top_ > old_top_ - old_start_) return; | |
| 182 | |
| 183 // As a last resort we mark all pages as being exempt from the store buffer. | |
| 184 ExemptPopularPages(1, 0); | |
| 185 ASSERT(old_top_ == old_start_); | |
| 186 } | |
| 187 | |
| 188 | |
| 189 // Sample the store buffer to see if some pages are taking up a lot of space | |
| 190 // in the store buffer. | |
| 191 void StoreBuffer::ExemptPopularPages(int prime_sample_step, int threshold) { | |
| 192 PointerChunkIterator it; | |
| 193 MemoryChunk* chunk; | |
| 194 while ((chunk = it.next()) != NULL) { | |
| 195 chunk->set_store_buffer_counter(0); | |
| 196 } | |
| 197 bool created_new_scan_on_scavenge_pages = false; | |
| 198 MemoryChunk* previous_chunk = NULL; | |
| 199 for (Address* p = old_start_; p < old_top_; p += prime_sample_step) { | |
| 200 Address addr = *p; | |
| 201 MemoryChunk* containing_chunk = NULL; | |
| 202 if (previous_chunk != NULL && previous_chunk->Contains(addr)) { | |
| 203 containing_chunk = previous_chunk; | |
| 204 } else { | |
| 205 containing_chunk = MemoryChunk::FromAnyPointerAddress(addr); | |
| 206 } | |
| 207 int old_counter = containing_chunk->store_buffer_counter(); | |
| 208 if (old_counter == threshold) { | |
| 209 containing_chunk->set_scan_on_scavenge(true); | |
| 210 created_new_scan_on_scavenge_pages = true; | |
| 211 } | |
| 212 containing_chunk->set_store_buffer_counter(old_counter + 1); | |
| 213 previous_chunk = containing_chunk; | |
| 214 } | |
| 215 if (created_new_scan_on_scavenge_pages) { | |
| 216 FilterScanOnScavengeEntries(); | |
| 217 } | |
| 218 old_buffer_is_filtered_ = true; | |
| 219 } | |
| 220 | |
| 221 | |
| 222 void StoreBuffer::FilterScanOnScavengeEntries() { | |
| 223 Address* new_top = old_start_; | |
| 224 MemoryChunk* previous_chunk = NULL; | |
| 225 for (Address* p = old_start_; p < old_top_; p++) { | |
| 226 Address addr = *p; | |
| 227 MemoryChunk* containing_chunk = NULL; | |
| 228 if (previous_chunk != NULL && previous_chunk->Contains(addr)) { | |
| 229 containing_chunk = previous_chunk; | |
| 230 } else { | |
| 231 containing_chunk = MemoryChunk::FromAnyPointerAddress(addr); | |
|
Vyacheslav Egorov (Chromium)
2011/03/28 15:13:19
This code can be pretty slow on certain patterns.
Erik Corry
2011/03/28 15:56:07
Yes.
| |
| 232 previous_chunk = containing_chunk; | |
| 233 } | |
| 234 if (!containing_chunk->scan_on_scavenge()) { | |
| 235 *new_top++ = addr; | |
| 236 } | |
| 237 } | |
| 238 old_top_ = new_top; | |
| 239 } | |
| 240 | |
| 241 | |
| 145 void StoreBuffer::SortUniq() { | 242 void StoreBuffer::SortUniq() { |
| 146 Compact(); | 243 Compact(); |
| 147 if (old_buffer_is_sorted_) return; | 244 if (old_buffer_is_sorted_) return; |
| 148 if (store_buffer_mode() == kStoreBufferDisabled) { | |
| 149 old_top_ = old_start_; | |
| 150 return; | |
| 151 } | |
| 152 ZapHashTables(); | 245 ZapHashTables(); |
| 153 qsort(reinterpret_cast<void*>(old_start_), | 246 qsort(reinterpret_cast<void*>(old_start_), |
| 154 old_top_ - old_start_, | 247 old_top_ - old_start_, |
| 155 sizeof(*old_top_), | 248 sizeof(*old_top_), |
| 156 &CompareAddresses); | 249 &CompareAddresses); |
| 157 Uniq(); | 250 Uniq(); |
| 158 | 251 |
| 159 old_buffer_is_sorted_ = true; | 252 old_buffer_is_sorted_ = true; |
| 160 } | 253 } |
| 161 | 254 |
| 162 | 255 |
| 163 void StoreBuffer::PrepareForIteration() { | 256 bool StoreBuffer::PrepareForIteration() { |
| 164 Compact(); | 257 Compact(); |
| 165 if (store_buffer_mode() == kStoreBufferDisabled) { | 258 PointerChunkIterator it; |
| 166 old_top_ = old_start_; | 259 MemoryChunk* chunk; |
| 167 return; | 260 bool page_has_scan_on_scavenge_flag = false; |
| 261 while ((chunk = it.next()) != NULL) { | |
| 262 if (chunk->scan_on_scavenge()) page_has_scan_on_scavenge_flag = true; | |
| 263 } | |
| 264 | |
| 265 if (page_has_scan_on_scavenge_flag) { | |
| 266 FilterScanOnScavengeEntries(); | |
| 168 } | 267 } |
| 169 ZapHashTables(); | 268 ZapHashTables(); |
| 269 return page_has_scan_on_scavenge_flag; | |
| 170 } | 270 } |
| 171 | 271 |
| 172 | 272 |
| 173 #ifdef DEBUG | 273 #ifdef DEBUG |
| 174 void StoreBuffer::Clean() { | 274 void StoreBuffer::Clean() { |
| 175 if (store_buffer_mode() == kStoreBufferDisabled) { | |
| 176 old_top_ = old_start_; // Just clear the cache. | |
| 177 return; | |
| 178 } | |
| 179 ZapHashTables(); | 275 ZapHashTables(); |
| 180 Uniq(); // Also removes things that no longer point to new space. | 276 Uniq(); // Also removes things that no longer point to new space. |
| 181 CheckForFullBuffer(); | 277 CheckForFullBuffer(); |
| 182 } | 278 } |
| 183 | 279 |
| 184 | 280 |
| 185 static bool Zapped(char* start, int size) { | 281 static bool Zapped(char* start, int size) { |
| 186 for (int i = 0; i < size; i++) { | 282 for (int i = 0; i < size; i++) { |
| 187 if (start[i] != 0) return false; | 283 if (start[i] != 0) return false; |
| 188 } | 284 } |
| 189 return true; | 285 return true; |
| 190 } | 286 } |
| 191 | 287 |
| 192 | 288 |
| 193 bool StoreBuffer::HashTablesAreZapped() { | 289 bool StoreBuffer::HashTablesAreZapped() { |
| 194 return Zapped(reinterpret_cast<char*>(hash_map_1_), | 290 return Zapped(reinterpret_cast<char*>(hash_map_1_), |
| 195 sizeof(uintptr_t) * kHashMapLength) && | 291 sizeof(uintptr_t) * kHashMapLength) && |
| 196 Zapped(reinterpret_cast<char*>(hash_map_2_), | 292 Zapped(reinterpret_cast<char*>(hash_map_2_), |
| 197 sizeof(uintptr_t) * kHashMapLength); | 293 sizeof(uintptr_t) * kHashMapLength); |
| 198 } | 294 } |
| 199 | 295 |
| 200 | 296 |
| 201 static Address* in_store_buffer_1_element_cache = NULL; | 297 static Address* in_store_buffer_1_element_cache = NULL; |
| 202 | 298 |
| 203 | 299 |
| 204 bool StoreBuffer::CellIsInStoreBuffer(Address cell_address) { | 300 bool StoreBuffer::CellIsInStoreBuffer(Address cell_address) { |
| 205 if (!FLAG_enable_slow_asserts) return true; | 301 if (!FLAG_enable_slow_asserts) return true; |
| 206 if (store_buffer_mode() != kStoreBufferFunctional) return true; | |
| 207 if (in_store_buffer_1_element_cache != NULL && | 302 if (in_store_buffer_1_element_cache != NULL && |
| 208 *in_store_buffer_1_element_cache == cell_address) { | 303 *in_store_buffer_1_element_cache == cell_address) { |
| 209 return true; | 304 return true; |
| 210 } | 305 } |
| 211 Address* top = reinterpret_cast<Address*>(Heap::store_buffer_top()); | 306 Address* top = reinterpret_cast<Address*>(Heap::store_buffer_top()); |
| 212 for (Address* current = top - 1; current >= start_; current--) { | 307 for (Address* current = top - 1; current >= start_; current--) { |
| 213 if (*current == cell_address) { | 308 if (*current == cell_address) { |
| 214 in_store_buffer_1_element_cache = current; | 309 in_store_buffer_1_element_cache = current; |
| 215 return true; | 310 return true; |
| 216 } | 311 } |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 235 sizeof(uintptr_t) * kHashMapLength); | 330 sizeof(uintptr_t) * kHashMapLength); |
| 236 } | 331 } |
| 237 | 332 |
| 238 | 333 |
| 239 void StoreBuffer::GCPrologue(GCType type, GCCallbackFlags flags) { | 334 void StoreBuffer::GCPrologue(GCType type, GCCallbackFlags flags) { |
| 240 ZapHashTables(); | 335 ZapHashTables(); |
| 241 during_gc_ = true; | 336 during_gc_ = true; |
| 242 } | 337 } |
| 243 | 338 |
| 244 | 339 |
| 245 void StoreBuffer::Verify() { | 340 void StoreBuffer::Verify() { |
|
Vyacheslav Egorov (Chromium)
2011/03/28 15:13:19
I think we should have a way to verify store-buffe
Erik Corry
2011/03/28 15:56:07
I will consider this for another change.
| |
| 246 #ifdef DEBUG | |
| 247 if (FLAG_verify_heap && | |
| 248 StoreBuffer::store_buffer_mode() == kStoreBufferFunctional) { | |
| 249 Heap::OldPointerSpaceCheckStoreBuffer(Heap::WATERMARK_SHOULD_BE_VALID); | |
| 250 Heap::MapSpaceCheckStoreBuffer(Heap::WATERMARK_SHOULD_BE_VALID); | |
| 251 Heap::LargeObjectSpaceCheckStoreBuffer(); | |
| 252 } | |
| 253 #endif | |
| 254 } | 341 } |
| 255 | 342 |
| 256 | 343 |
| 257 void StoreBuffer::GCEpilogue(GCType type, GCCallbackFlags flags) { | 344 void StoreBuffer::GCEpilogue(GCType type, GCCallbackFlags flags) { |
| 258 during_gc_ = false; | 345 during_gc_ = false; |
| 259 if (store_buffer_mode() == kStoreBufferBeingRebuilt) { | |
| 260 set_store_buffer_mode(kStoreBufferFunctional); | |
| 261 } | |
| 262 Verify(); | 346 Verify(); |
| 263 } | 347 } |
| 264 | 348 |
| 265 | 349 |
| 266 void StoreBuffer::IteratePointersToNewSpace(ObjectSlotCallback callback) { | 350 void StoreBuffer::IteratePointersToNewSpace(ObjectSlotCallback callback) { |
| 267 if (store_buffer_mode() == kStoreBufferFunctional) { | 351 // We do not sort or remove duplicated entries from the store buffer because |
| 268 // We do not sort or remove duplicated entries from the store buffer because | 352 // we expect that callback will rebuild the store buffer thus removing |
| 269 // we expect that callback will rebuild the store buffer thus removing | 353 // all duplicates and pointers to old space. |
| 270 // all duplicates and pointers to old space. | 354 bool some_pages_to_scan = PrepareForIteration(); |
| 271 PrepareForIteration(); | 355 |
| 356 Address* limit = old_top_; | |
| 357 old_top_ = old_start_; | |
| 358 { | |
| 359 DontMoveStoreBufferEntriesScope scope; | |
| 360 for (Address* current = old_start_; current < limit; current++) { | |
| 361 #ifdef DEBUG | |
| 362 Address* saved_top = old_top_; | |
| 363 #endif | |
| 364 Object** cell = reinterpret_cast<Object**>(*current); | |
| 365 Object* object = *cell; | |
| 366 // May be invalid if object is not in new space. | |
| 367 HeapObject* heap_object = reinterpret_cast<HeapObject*>(object); | |
| 368 if (Heap::InFromSpace(object)) { | |
| 369 callback(reinterpret_cast<HeapObject**>(cell), heap_object); | |
| 370 } | |
| 371 ASSERT(old_top_ == saved_top + 1 || old_top_ == saved_top); | |
| 372 } | |
| 272 } | 373 } |
| 273 if (store_buffer_mode() != kStoreBufferFunctional) { | 374 // We are done scanning all the pointers that were in the store buffer, but |
| 274 old_top_ = old_start_; | 375 // there may be some pages marked scan_on_scavenge that have pointers to new |
| 275 ZapHashTables(); | 376 // space that are not in the store buffer. We must scan them now. As we |
| 276 Heap::public_set_store_buffer_top(start_); | 377 // scan, the surviving pointers to new space will be added to the store |
| 277 set_store_buffer_mode(kStoreBufferBeingRebuilt); | 378 // buffer. If there are still a lot of pointers to new space then we will |
| 278 Heap::IteratePointers(Heap::old_pointer_space(), | 379 // keep the scan_on_scavenge flag on the page and discard the pointers that |
| 279 &Heap::IteratePointersToNewSpace, | 380 // were added to the store buffer. If there are not many pointers to new |
| 280 callback, | 381 // space left on the page we will keep the pointers in the store buffer and |
| 281 Heap::WATERMARK_SHOULD_BE_VALID); | 382 // remove the flag from the page. |
| 282 | 383 if (some_pages_to_scan) { |
| 283 Heap::IteratePointers(Heap::map_space(), | 384 if (callback_ != NULL) { |
| 284 &Heap::IteratePointersFromMapsToNewSpace, | 385 (*callback_)(NULL, kStoreBufferStartScanningPagesEvent); |
| 285 callback, | 386 } |
| 286 Heap::WATERMARK_SHOULD_BE_VALID); | 387 PointerChunkIterator it; |
| 287 | 388 MemoryChunk* chunk; |
| 288 Heap::lo_space()->IteratePointersToNewSpace(callback); | 389 while ((chunk = it.next()) != NULL) { |
| 289 } else { | 390 if (chunk->scan_on_scavenge()) { |
| 290 Address* limit = old_top_; | 391 if (callback_ != NULL) { |
| 291 old_top_ = old_start_; | 392 (*callback_)(chunk, kStoreBufferScanningPageEvent); |
| 292 { | |
| 293 DontMoveStoreBufferEntriesScope scope; | |
| 294 for (Address* current = old_start_; current < limit; current++) { | |
| 295 #ifdef DEBUG | |
| 296 Address* saved_top = old_top_; | |
| 297 #endif | |
| 298 Object** cell = reinterpret_cast<Object**>(*current); | |
| 299 Object* object = *cell; | |
| 300 // May be invalid if object is not in new space. | |
| 301 HeapObject* heap_object = reinterpret_cast<HeapObject*>(object); | |
| 302 if (Heap::InFromSpace(object)) { | |
| 303 callback(reinterpret_cast<HeapObject**>(cell), heap_object); | |
| 304 } | 393 } |
| 305 ASSERT(old_top_ == saved_top + 1 || old_top_ == saved_top); | 394 if (chunk->owner() == Heap::lo_space()) { |
| 395 LargePage* large_page = reinterpret_cast<LargePage*>(chunk); | |
| 396 HeapObject* array = large_page->GetObject(); | |
| 397 if (array->IsFixedArray()) { | |
|
Vyacheslav Egorov (Chromium)
2011/03/28 15:13:19
maybe you should assert that array is actually a f
Erik Corry
2011/03/28 15:56:07
Done.
| |
| 398 Address start = array->address(); | |
| 399 Address object_end = start + array->Size(); | |
| 400 Heap::IteratePointersToNewSpace(start, object_end, callback); | |
| 401 } | |
| 402 } else { | |
| 403 Page* page = reinterpret_cast<Page*>(chunk); | |
| 404 Heap::IteratePointersOnPage( | |
| 405 reinterpret_cast<PagedSpace*>(page->owner()), | |
| 406 &Heap::IteratePointersToNewSpace, | |
| 407 callback, | |
| 408 page); | |
| 409 } | |
| 306 } | 410 } |
| 307 } | 411 } |
| 412 (*callback_)(NULL, kStoreBufferScanningPageEvent); | |
| 308 } | 413 } |
| 309 } | 414 } |
| 310 | 415 |
| 311 | 416 |
| 312 void StoreBuffer::Compact() { | 417 void StoreBuffer::Compact() { |
| 313 Address* top = reinterpret_cast<Address*>(Heap::store_buffer_top()); | 418 Address* top = reinterpret_cast<Address*>(Heap::store_buffer_top()); |
| 314 | 419 |
| 315 if (top == start_) return; | 420 if (top == start_) return; |
| 316 | 421 |
| 317 // There's no check of the limit in the loop below so we check here for | 422 // There's no check of the limit in the loop below so we check here for |
| 318 // the worst case (compaction doesn't eliminate any pointers). | 423 // the worst case (compaction doesn't eliminate any pointers). |
| 319 ASSERT(top <= limit_); | 424 ASSERT(top <= limit_); |
| 320 Heap::public_set_store_buffer_top(start_); | 425 Heap::public_set_store_buffer_top(start_); |
| 321 if (top - start_ > old_limit_ - old_top_) { | 426 if (top - start_ > old_limit_ - old_top_) { |
| 322 CheckForFullBuffer(); | 427 HandleFullness(); |
| 323 } | 428 } |
| 324 if (store_buffer_mode() == kStoreBufferDisabled) return; | |
| 325 ASSERT(may_move_store_buffer_entries_); | 429 ASSERT(may_move_store_buffer_entries_); |
| 326 // Goes through the addresses in the store buffer attempting to remove | 430 // Goes through the addresses in the store buffer attempting to remove |
| 327 // duplicates. In the interest of speed this is a lossy operation. Some | 431 // duplicates. In the interest of speed this is a lossy operation. Some |
| 328 // duplicates will remain. We have two hash tables with different hash | 432 // duplicates will remain. We have two hash tables with different hash |
| 329 // functions to reduce the number of unnecessary clashes. | 433 // functions to reduce the number of unnecessary clashes. |
| 330 for (Address* current = start_; current < top; current++) { | 434 for (Address* current = start_; current < top; current++) { |
| 435 ASSERT(!Heap::cell_space()->Contains(*current)); | |
| 436 ASSERT(!Heap::code_space()->Contains(*current)); | |
| 437 ASSERT(!Heap::old_data_space()->Contains(*current)); | |
| 331 uintptr_t int_addr = reinterpret_cast<uintptr_t>(*current); | 438 uintptr_t int_addr = reinterpret_cast<uintptr_t>(*current); |
| 332 // Shift out the last bits including any tags. | 439 // Shift out the last bits including any tags. |
| 333 int_addr >>= kPointerSizeLog2; | 440 int_addr >>= kPointerSizeLog2; |
| 334 int hash1 = | 441 int hash1 = |
| 335 ((int_addr ^ (int_addr >> kHashMapLengthLog2)) & (kHashMapLength - 1)); | 442 ((int_addr ^ (int_addr >> kHashMapLengthLog2)) & (kHashMapLength - 1)); |
| 336 if (hash_map_1_[hash1] == int_addr) continue; | 443 if (hash_map_1_[hash1] == int_addr) continue; |
| 337 int hash2 = | 444 int hash2 = |
| 338 ((int_addr - (int_addr >> kHashMapLengthLog2)) & (kHashMapLength - 1)); | 445 ((int_addr - (int_addr >> kHashMapLengthLog2)) & (kHashMapLength - 1)); |
| 339 hash2 ^= hash2 >> (kHashMapLengthLog2 * 2); | 446 hash2 ^= hash2 >> (kHashMapLengthLog2 * 2); |
| 340 if (hash_map_2_[hash2] == int_addr) continue; | 447 if (hash_map_2_[hash2] == int_addr) continue; |
| 341 if (hash_map_1_[hash1] == 0) { | 448 if (hash_map_1_[hash1] == 0) { |
| 342 hash_map_1_[hash1] = int_addr; | 449 hash_map_1_[hash1] = int_addr; |
| 343 } else if (hash_map_2_[hash2] == 0) { | 450 } else if (hash_map_2_[hash2] == 0) { |
| 344 hash_map_2_[hash2] = int_addr; | 451 hash_map_2_[hash2] = int_addr; |
| 345 } else { | 452 } else { |
| 346 // Rather than slowing down we just throw away some entries. This will | 453 // Rather than slowing down we just throw away some entries. This will |
| 347 // cause some duplicates to remain undetected. | 454 // cause some duplicates to remain undetected. |
| 348 hash_map_1_[hash1] = int_addr; | 455 hash_map_1_[hash1] = int_addr; |
| 349 hash_map_2_[hash2] = 0; | 456 hash_map_2_[hash2] = 0; |
| 350 } | 457 } |
| 351 old_buffer_is_sorted_ = false; | 458 old_buffer_is_sorted_ = false; |
| 459 old_buffer_is_filtered_ = false; | |
| 352 *old_top_++ = reinterpret_cast<Address>(int_addr << kPointerSizeLog2); | 460 *old_top_++ = reinterpret_cast<Address>(int_addr << kPointerSizeLog2); |
| 353 ASSERT(old_top_ <= old_limit_); | 461 ASSERT(old_top_ <= old_limit_); |
| 354 } | 462 } |
| 355 Counters::store_buffer_compactions.Increment(); | 463 Counters::store_buffer_compactions.Increment(); |
| 356 CheckForFullBuffer(); | 464 CheckForFullBuffer(); |
| 357 } | 465 } |
| 358 | 466 |
| 359 | 467 |
| 360 void StoreBuffer::CheckForFullBuffer() { | 468 void StoreBuffer::CheckForFullBuffer() { |
| 361 if (old_limit_ - old_top_ < kStoreBufferSize * 2) { | 469 if (old_limit_ - old_top_ < kStoreBufferSize * 2) { |
| 362 // After compression we don't have enough space that we can be sure that | 470 HandleFullness(); |
| 363 // the next two compressions will have enough space in the buffer. We | |
| 364 // start by trying a more agressive compression. If this frees up at least | |
| 365 // half the space then we can keep going, otherwise it is time to brake. | |
| 366 if (!during_gc_) { | |
| 367 SortUniq(); | |
| 368 } | |
| 369 if (old_limit_ - old_top_ > old_top_ - old_start_) { | |
| 370 return; | |
| 371 } | |
| 372 // TODO(gc): Set an interrupt to do a GC on the next back edge. | |
| 373 // TODO(gc): Allocate the rest of new space to force a GC on the next | |
| 374 // allocation. | |
| 375 // TODO(gc): Make the disabling of the store buffer dependendent on | |
| 376 // those two measures failing: | |
| 377 // After compression not enough space was freed up in the store buffer. We | |
| 378 // might as well stop sorting and trying to eliminate duplicates. | |
| 379 Counters::store_buffer_overflows.Increment(); | |
| 380 set_store_buffer_mode(kStoreBufferDisabled); | |
| 381 } | 471 } |
| 382 } | 472 } |
| 383 | 473 |
| 384 } } // namespace v8::internal | 474 } } // namespace v8::internal |
| OLD | NEW |