Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: src/spaces.cc

Issue 5987005: Refactor MemoryAllocator to allow big normal pages (Closed) Base URL: https://v8.googlecode.com/svn/branches/experimental/gc
Patch Set: Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2006-2010 the V8 project authors. All rights reserved. 1 // Copyright 2006-2010 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
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 119
120 // ----------------------------------------------------------------------------- 120 // -----------------------------------------------------------------------------
121 // PageIterator 121 // PageIterator
122 122
123 PageIterator::PageIterator(PagedSpace* space, Mode mode) : space_(space) { 123 PageIterator::PageIterator(PagedSpace* space, Mode mode) : space_(space) {
124 prev_page_ = NULL; 124 prev_page_ = NULL;
125 switch (mode) { 125 switch (mode) {
126 case PAGES_IN_USE: 126 case PAGES_IN_USE:
127 stop_page_ = space->AllocationTopPage(); 127 stop_page_ = space->AllocationTopPage();
128 break; 128 break;
129 case PAGES_USED_BY_MC:
130 stop_page_ = space->MCRelocationTopPage();
131 break;
132 case ALL_PAGES: 129 case ALL_PAGES:
133 #ifdef DEBUG 130 #ifdef DEBUG
134 // Verify that the cached last page in the space is actually the 131 // Verify that the cached last page in the space is actually the
135 // last page. 132 // last page.
136 for (Page* p = space->first_page_; p->is_valid(); p = p->next_page()) { 133 for (Page* p = space->first_page_; p->is_valid(); p = p->next_page()) {
137 if (!p->next_page()->is_valid()) { 134 if (!p->next_page()->is_valid()) {
138 ASSERT(space->last_page_ == p); 135 ASSERT(space->last_page_ == p);
139 } 136 }
140 } 137 }
141 #endif 138 #endif
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
226 223
227 224
228 void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) { 225 void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) {
229 ASSERT(current_allocation_block_index_ < allocation_list_.length()); 226 ASSERT(current_allocation_block_index_ < allocation_list_.length());
230 if (requested > allocation_list_[current_allocation_block_index_].size) { 227 if (requested > allocation_list_[current_allocation_block_index_].size) {
231 // Find an allocation block large enough. This function call may 228 // Find an allocation block large enough. This function call may
232 // call V8::FatalProcessOutOfMemory if it cannot find a large enough block. 229 // call V8::FatalProcessOutOfMemory if it cannot find a large enough block.
233 GetNextAllocationBlock(requested); 230 GetNextAllocationBlock(requested);
234 } 231 }
235 // Commit the requested memory at the start of the current allocation block. 232 // Commit the requested memory at the start of the current allocation block.
236 *allocated = RoundUp(requested, Page::kPageSize); 233 *allocated = RoundUp(requested, MemoryChunk::kAlignment);
237 FreeBlock current = allocation_list_[current_allocation_block_index_]; 234 FreeBlock current = allocation_list_[current_allocation_block_index_];
238 if (*allocated >= current.size - Page::kPageSize) { 235 if (*allocated >= current.size - Page::kPageSize) {
239 // Don't leave a small free block, useless for a large object or chunk. 236 // Don't leave a small free block, useless for a large object or chunk.
240 *allocated = current.size; 237 *allocated = current.size;
241 } 238 }
242 ASSERT(*allocated <= current.size); 239 ASSERT(*allocated <= current.size);
240 ASSERT(IsAligned(static_cast<intptr_t>(*allocated), MemoryChunk::kAlignment));
241 ASSERT(IsAligned(OffsetFrom(current.start), MemoryChunk::kAlignment));
243 if (!code_range_->Commit(current.start, *allocated, true)) { 242 if (!code_range_->Commit(current.start, *allocated, true)) {
244 *allocated = 0; 243 *allocated = 0;
245 return NULL; 244 return NULL;
246 } 245 }
247 allocation_list_[current_allocation_block_index_].start += *allocated; 246 allocation_list_[current_allocation_block_index_].start += *allocated;
248 allocation_list_[current_allocation_block_index_].size -= *allocated; 247 allocation_list_[current_allocation_block_index_].size -= *allocated;
249 if (*allocated == current.size) { 248 if (*allocated == current.size) {
250 GetNextAllocationBlock(0); // This block is used up, get the next one. 249 GetNextAllocationBlock(0); // This block is used up, get the next one.
251 } 250 }
252 return current.start; 251 return current.start;
253 } 252 }
254 253
255 254
256 void CodeRange::FreeRawMemory(void* address, size_t length) { 255 void CodeRange::FreeRawMemory(void* address, size_t length) {
256 ASSERT(IsAligned(reinterpret_cast<intptr_t>(address),
257 MemoryChunk::kAlignment));
258 ASSERT(IsAligned(static_cast<intptr_t>(length), MemoryChunk::kAlignment));
257 free_list_.Add(FreeBlock(address, length)); 259 free_list_.Add(FreeBlock(address, length));
258 code_range_->Uncommit(address, length); 260 code_range_->Uncommit(address, length);
259 } 261 }
260 262
261 263
262 void CodeRange::TearDown() { 264 void CodeRange::TearDown() {
263 delete code_range_; // Frees all memory in the virtual memory range. 265 delete code_range_; // Frees all memory in the virtual memory range.
264 code_range_ = NULL; 266 code_range_ = NULL;
265 free_list_.Free(); 267 free_list_.Free();
266 allocation_list_.Free(); 268 allocation_list_.Free();
267 } 269 }
268 270
269 271
270 // ----------------------------------------------------------------------------- 272 // -----------------------------------------------------------------------------
271 // MemoryAllocator 273 // MemoryAllocator
272 // 274 //
273 intptr_t MemoryAllocator::capacity_ = 0; 275 size_t MemoryAllocator::capacity_ = 0;
274 intptr_t MemoryAllocator::capacity_executable_ = 0; 276 size_t MemoryAllocator::capacity_executable_ = 0;
275 intptr_t MemoryAllocator::size_ = 0; 277 size_t MemoryAllocator::size_ = 0;
276 intptr_t MemoryAllocator::size_executable_ = 0; 278 size_t MemoryAllocator::size_executable_ = 0;
277 279
278 List<MemoryAllocator::MemoryAllocationCallbackRegistration> 280 List<MemoryAllocator::MemoryAllocationCallbackRegistration>
279 MemoryAllocator::memory_allocation_callbacks_; 281 MemoryAllocator::memory_allocation_callbacks_;
280 282
281 VirtualMemory* MemoryAllocator::initial_chunk_ = NULL;
282
283 // 270 is an estimate based on the static default heap size of a pair of 256K
284 // semispaces and a 64M old generation.
285 const int kEstimatedNumberOfChunks = 270;
286 List<MemoryAllocator::ChunkInfo> MemoryAllocator::chunks_(
287 kEstimatedNumberOfChunks);
288 List<int> MemoryAllocator::free_chunk_ids_(kEstimatedNumberOfChunks);
289 int MemoryAllocator::max_nof_chunks_ = 0;
290 int MemoryAllocator::top_ = 0;
291
292
293 void MemoryAllocator::Push(int free_chunk_id) {
294 ASSERT(max_nof_chunks_ > 0);
295 ASSERT(top_ < max_nof_chunks_);
296 free_chunk_ids_[top_++] = free_chunk_id;
297 }
298
299
300 int MemoryAllocator::Pop() {
301 ASSERT(top_ > 0);
302 return free_chunk_ids_[--top_];
303 }
304
305
306 bool MemoryAllocator::Setup(intptr_t capacity, intptr_t capacity_executable) { 283 bool MemoryAllocator::Setup(intptr_t capacity, intptr_t capacity_executable) {
307 capacity_ = RoundUp(capacity, Page::kPageSize); 284 capacity_ = RoundUp(capacity, Page::kPageSize);
308 capacity_executable_ = RoundUp(capacity_executable, Page::kPageSize); 285 capacity_executable_ = RoundUp(capacity_executable, Page::kPageSize);
309 ASSERT_GE(capacity_, capacity_executable_); 286 ASSERT_GE(capacity_, capacity_executable_);
310 287
311 // Over-estimate the size of chunks_ array. It assumes the expansion of old
312 // space is always in the unit of a chunk (kChunkSize) except the last
313 // expansion.
314 //
315 // Due to alignment, allocated space might be one page less than required
316 // number (kPagesPerChunk) of pages for old spaces.
317 //
318 // Reserve two chunk ids for semispaces, one for map space, one for old
319 // space, and one for code space.
320 max_nof_chunks_ =
321 static_cast<int>((capacity_ / (kChunkSize - Page::kPageSize))) + 5;
322 if (max_nof_chunks_ > kMaxNofChunks) return false;
323
324 size_ = 0; 288 size_ = 0;
325 size_executable_ = 0; 289 size_executable_ = 0;
326 ChunkInfo info; // uninitialized element. 290
327 for (int i = max_nof_chunks_ - 1; i >= 0; i--) {
328 chunks_.Add(info);
329 free_chunk_ids_.Add(i);
330 }
331 top_ = max_nof_chunks_;
332 return true; 291 return true;
333 } 292 }
334 293
335 294
336 bool MemoryAllocator::SafeIsInAPageChunk(Address addr) {
337 return InInitialChunk(addr) || InAllocatedChunks(addr);
338 }
339
340
341 void MemoryAllocator::TearDown() { 295 void MemoryAllocator::TearDown() {
342 for (int i = 0; i < max_nof_chunks_; i++) {
343 if (chunks_[i].address() != NULL) DeleteChunk(i);
344 }
345 chunks_.Clear();
346 free_chunk_ids_.Clear();
347
348 if (initial_chunk_ != NULL) {
349 LOG(DeleteEvent("InitialChunk", initial_chunk_->address()));
350 delete initial_chunk_;
351 initial_chunk_ = NULL;
352 }
353
354 FreeChunkTables(&chunk_table_[0],
355 kChunkTableTopLevelEntries,
356 kChunkTableLevels);
357
358 ASSERT(top_ == max_nof_chunks_); // all chunks are free
359 top_ = 0;
360 capacity_ = 0; 296 capacity_ = 0;
361 capacity_executable_ = 0; 297 capacity_executable_ = 0;
362 size_ = 0; 298 size_ = 0;
363 max_nof_chunks_ = 0; 299 size_executable_ = 0;
364 } 300 }
365 301
366 302
367 void MemoryAllocator::FreeChunkTables(uintptr_t* array, int len, int level) { 303 void MemoryAllocator::FreeMemory(void* base,
368 for (int i = 0; i < len; i++) { 304 size_t size,
369 if (array[i] != kUnusedChunkTableEntry) { 305 Executability executable) {
370 uintptr_t* subarray = reinterpret_cast<uintptr_t*>(array[i]); 306 if (CodeRange::contains(static_cast<Address>(base))) {
371 if (level > 1) { 307 ASSERT(executable == EXECUTABLE);
372 array[i] = kUnusedChunkTableEntry; 308 CodeRange::FreeRawMemory(base, size);
373 FreeChunkTables(subarray, 1 << kChunkTableBitsPerLevel, level - 1); 309 } else {
374 } else { 310 ASSERT(executable == NOT_EXECUTABLE || !CodeRange::exists());
375 array[i] = kUnusedChunkTableEntry; 311 VirtualMemory::ReleaseRegion(base, size);
376 } 312 }
377 delete[] subarray; 313
378 } 314 Counters::memory_allocated.Decrement(static_cast<int>(size));
379 } 315
380 } 316 ASSERT(size_ >= size);
381 317 size_ -= size;
382 318
383 void* MemoryAllocator::AllocateRawMemory(const size_t requested, 319 if (executable == EXECUTABLE) {
384 size_t* allocated, 320 ASSERT(size_executable_ >= size);
385 Executability executable) { 321 size_executable_ -= size;
386 if (size_ + static_cast<size_t>(requested) > static_cast<size_t>(capacity_)) { 322 }
323 }
324
325
326 void* MemoryAllocator::ReserveAlignedMemory(const size_t requested,
Erik Corry 2010/12/22 13:48:27 Shouldn't this return an Address?
Vyacheslav Egorov (Chromium) 2010/12/22 19:55:07 Done. I eradicated void* usages.
327 size_t alignment,
328 size_t* allocated_size) {
329 ASSERT(IsAligned(alignment, OS::AllocateAlignment()));
330 if (size_ + requested > capacity_) return false;
331
332 size_t allocated = RoundUp(requested + alignment, OS::AllocateAlignment());
333
334 uintptr_t base = reinterpret_cast<intptr_t>(
335 VirtualMemory::ReserveRegion(allocated));
336
337 uintptr_t end = base + allocated;
338
339 if (base == 0) return NULL;
340
341 uintptr_t aligned_base = RoundUp(base, alignment);
342
343 ASSERT(aligned_base + requested <= base + allocated);
344
345 // The difference between re-aligned base address and base address is
346 // multiple of OS::AllocateAlignment().
347 if (aligned_base != base) {
348 ASSERT(aligned_base > base);
349 // TODO(gc) check result of operation?
350 VirtualMemory::ReleaseRegion(reinterpret_cast<void*>(base),
351 aligned_base - base);
352 allocated -= (aligned_base - base);
353 base = aligned_base;
354 }
355
356 ASSERT(base + allocated == end);
357
358 uintptr_t requested_end = base + requested;
359 uintptr_t aligned_requested_end =
360 RoundUp(requested_end, OS::AllocateAlignment());
361
362 if (aligned_requested_end < end) {
363 // TODO(gc) check result of operation?
364 VirtualMemory::ReleaseRegion(reinterpret_cast<void*>(aligned_requested_end),
365 end - aligned_requested_end);
366 allocated = aligned_requested_end - base;
367 }
368
369 size_ += allocated;
370 *allocated_size = allocated;
371 return reinterpret_cast<void*>(base);
372 }
373
374
375 void* MemoryAllocator::AllocateAlignedMemory(const size_t requested,
376 size_t alignment,
377 Executability executable,
378 size_t* allocated_size) {
379 void* base =
380 ReserveAlignedMemory(requested, Page::kPageSize, allocated_size);
381
382 if (base == NULL) return NULL;
383
384 if (!VirtualMemory::CommitRegion(base,
385 *allocated_size,
386 executable == EXECUTABLE)) {
387 VirtualMemory::ReleaseRegion(base, *allocated_size);
388 size_ -= *allocated_size;
387 return NULL; 389 return NULL;
388 } 390 }
389 391
390 void* mem; 392 return base;
393 }
394
395
396 MemoryChunk* MemoryAllocator::AllocateChunk(intptr_t body_size,
397 Executability executable,
398 Space* owner) {
399 size_t chunk_size = MemoryChunk::kBodyOffset + body_size;
400 void* base = NULL;
391 if (executable == EXECUTABLE) { 401 if (executable == EXECUTABLE) {
392 // Check executable memory limit. 402 // Check executable memory limit.
393 if (size_executable_ + requested > 403 if (size_executable_ + chunk_size > capacity_executable_) {
394 static_cast<size_t>(capacity_executable_)) {
395 LOG(StringEvent("MemoryAllocator::AllocateRawMemory", 404 LOG(StringEvent("MemoryAllocator::AllocateRawMemory",
396 "V8 Executable Allocation capacity exceeded")); 405 "V8 Executable Allocation capacity exceeded"));
397 return NULL; 406 return NULL;
398 } 407 }
408
399 // Allocate executable memory either from code range or from the 409 // Allocate executable memory either from code range or from the
400 // OS. 410 // OS.
401 if (CodeRange::exists()) { 411 if (CodeRange::exists()) {
402 mem = CodeRange::AllocateRawMemory(requested, allocated); 412 base = CodeRange::AllocateRawMemory(chunk_size, &chunk_size);
413 ASSERT(IsAligned(reinterpret_cast<intptr_t>(base),
414 MemoryChunk::kAlignment));
415 size_ += chunk_size;
403 } else { 416 } else {
404 mem = OS::Allocate(requested, allocated, true); 417 base = AllocateAlignedMemory(chunk_size,
418 MemoryChunk::kAlignment,
419 executable,
420 &chunk_size);
405 } 421 }
422
423 if (base == NULL) return NULL;
424
406 // Update executable memory size. 425 // Update executable memory size.
407 size_executable_ += static_cast<int>(*allocated); 426 size_executable_ += chunk_size;
408 } else { 427 } else {
409 mem = OS::Allocate(requested, allocated, false); 428 base = AllocateAlignedMemory(chunk_size,
410 } 429 MemoryChunk::kAlignment,
411 int alloced = static_cast<int>(*allocated); 430 executable,
412 size_ += alloced; 431 &chunk_size);
432
433 if (base == NULL) return NULL;
434 }
413 435
414 #ifdef DEBUG 436 #ifdef DEBUG
415 ZapBlock(reinterpret_cast<Address>(mem), alloced); 437 ZapBlock(reinterpret_cast<Address>(base), chunk_size);
416 #endif 438 #endif
417 Counters::memory_allocated.Increment(alloced); 439 Counters::memory_allocated.Increment(chunk_size);
418 return mem; 440
419 } 441 LOG(NewEvent("MemoryChunk", base, chunk_size));
420 442 if (owner != NULL) {
421 443 ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity());
422 void MemoryAllocator::FreeRawMemory(void* mem, 444 PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size);
423 size_t length, 445 }
446
447 return MemoryChunk::Initialize(reinterpret_cast<Address>(base),
448 chunk_size,
449 executable,
450 owner);
451 }
452
453
454 Page* MemoryAllocator::AllocatePage(PagedSpace* owner,
424 Executability executable) { 455 Executability executable) {
425 #ifdef DEBUG 456 MemoryChunk* chunk = AllocateChunk(Page::kObjectAreaSize, executable, owner);
426 ZapBlock(reinterpret_cast<Address>(mem), length); 457
427 #endif 458 if (chunk == NULL) return NULL;
428 if (CodeRange::contains(static_cast<Address>(mem))) { 459
429 CodeRange::FreeRawMemory(mem, length); 460 return Page::Initialize(chunk);
430 } else { 461 }
431 OS::Free(mem, length); 462
432 } 463
433 Counters::memory_allocated.Decrement(static_cast<int>(length)); 464 LargePage* MemoryAllocator::AllocateLargePage(intptr_t object_size,
434 size_ -= static_cast<int>(length); 465 Executability executable,
435 if (executable == EXECUTABLE) size_executable_ -= static_cast<int>(length); 466 Space* owner) {
436 467 MemoryChunk* chunk = AllocateChunk(object_size, executable, owner);
437 ASSERT(size_ >= 0); 468 if (chunk == NULL) return NULL;
438 ASSERT(size_executable_ >= 0); 469 return LargePage::Initialize(chunk);
439 } 470 }
440 471
441 472
442 void MemoryAllocator::PerformAllocationCallback(ObjectSpace space, 473 void MemoryAllocator::Free(MemoryChunk* chunk) {
443 AllocationAction action, 474 LOG(DeleteEvent("MemoryChunk", chunk));
444 size_t size) { 475 if (chunk->owner() != NULL) {
445 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { 476 ObjectSpace space =
446 MemoryAllocationCallbackRegistration registration = 477 static_cast<ObjectSpace>(1 << chunk->owner()->identity());
447 memory_allocation_callbacks_[i]; 478 PerformAllocationCallback(space, kAllocationActionFree, chunk->size());
448 if ((registration.space & space) == space && 479 }
449 (registration.action & action) == action) 480
450 registration.callback(space, action, static_cast<int>(size)); 481 FreeMemory(reinterpret_cast<void*>(chunk->address()),
451 } 482 chunk->size(),
452 } 483 chunk->executable());
453 484 }
454 485
455 bool MemoryAllocator::MemoryAllocationCallbackRegistered( 486
456 MemoryAllocationCallback callback) { 487 bool MemoryAllocator::CommitBlock(Address start,
457 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { 488 size_t size,
458 if (memory_allocation_callbacks_[i].callback == callback) return true; 489 Executability executable) {
459 } 490 if (!VirtualMemory::CommitRegion(start, size, executable)) return false;
460 return false;
461 }
462
463
464 void MemoryAllocator::AddMemoryAllocationCallback(
465 MemoryAllocationCallback callback,
466 ObjectSpace space,
467 AllocationAction action) {
468 ASSERT(callback != NULL);
469 MemoryAllocationCallbackRegistration registration(callback, space, action);
470 ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback));
471 return memory_allocation_callbacks_.Add(registration);
472 }
473
474
475 void MemoryAllocator::RemoveMemoryAllocationCallback(
476 MemoryAllocationCallback callback) {
477 ASSERT(callback != NULL);
478 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
479 if (memory_allocation_callbacks_[i].callback == callback) {
480 memory_allocation_callbacks_.Remove(i);
481 return;
482 }
483 }
484 UNREACHABLE();
485 }
486
487 void* MemoryAllocator::ReserveInitialChunk(const size_t requested) {
488 ASSERT(initial_chunk_ == NULL);
489
490 initial_chunk_ = new VirtualMemory(requested);
491 CHECK(initial_chunk_ != NULL);
492 if (!initial_chunk_->IsReserved()) {
493 delete initial_chunk_;
494 initial_chunk_ = NULL;
495 return NULL;
496 }
497
498 // We are sure that we have mapped a block of requested addresses.
499 ASSERT(initial_chunk_->size() == requested);
500 LOG(NewEvent("InitialChunk", initial_chunk_->address(), requested));
501 size_ += static_cast<int>(requested);
502 return initial_chunk_->address();
503 }
504
505
506 static int PagesInChunk(Address start, size_t size) {
507 // The first page starts on the first page-aligned address from start onward
508 // and the last page ends on the last page-aligned address before
509 // start+size. Page::kPageSize is a power of two so we can divide by
510 // shifting.
511 return static_cast<int>((RoundDown(start + size, Page::kPageSize)
512 - RoundUp(start, Page::kPageSize)) >> kPageSizeBits);
513 }
514
515
516 Page* MemoryAllocator::AllocatePages(int requested_pages,
517 int* allocated_pages,
518 PagedSpace* owner) {
519 if (requested_pages <= 0) return Page::FromAddress(NULL);
520 size_t chunk_size = requested_pages * Page::kPageSize;
521
522 void* chunk = AllocateRawMemory(chunk_size, &chunk_size, owner->executable());
523 if (chunk == NULL) return Page::FromAddress(NULL);
524 LOG(NewEvent("PagedChunk", chunk, chunk_size));
525
526 *allocated_pages = PagesInChunk(static_cast<Address>(chunk), chunk_size);
527 // We may 'lose' a page due to alignment.
528 ASSERT(*allocated_pages >= kPagesPerChunk - 1);
529 if (*allocated_pages == 0) {
530 FreeRawMemory(chunk, chunk_size, owner->executable());
531 LOG(DeleteEvent("PagedChunk", chunk));
532 return Page::FromAddress(NULL);
533 }
534
535 int chunk_id = Pop();
536 chunks_[chunk_id].init(static_cast<Address>(chunk), chunk_size, owner);
537
538 ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity());
539 PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size);
540 Page* new_pages = InitializePagesInChunk(chunk_id, *allocated_pages, owner);
541
542 AddToAllocatedChunks(static_cast<Address>(chunk), chunk_size);
543
544 return new_pages;
545 }
546
547
548 Page* MemoryAllocator::CommitPages(Address start, size_t size,
549 PagedSpace* owner, int* num_pages) {
550 ASSERT(start != NULL);
551 *num_pages = PagesInChunk(start, size);
552 ASSERT(*num_pages > 0);
553 ASSERT(initial_chunk_ != NULL);
554 ASSERT(InInitialChunk(start));
555 ASSERT(InInitialChunk(start + size - 1));
556 if (!initial_chunk_->Commit(start, size, owner->executable() == EXECUTABLE)) {
557 return Page::FromAddress(NULL);
558 }
559 #ifdef DEBUG 491 #ifdef DEBUG
560 ZapBlock(start, size); 492 ZapBlock(start, size);
561 #endif 493 #endif
562 Counters::memory_allocated.Increment(static_cast<int>(size));
563
564 // So long as we correctly overestimated the number of chunks we should not
565 // run out of chunk ids.
566 CHECK(!OutOfChunkIds());
567 int chunk_id = Pop();
568 chunks_[chunk_id].init(start, size, owner);
569 return InitializePagesInChunk(chunk_id, *num_pages, owner);
570 }
571
572
573 bool MemoryAllocator::CommitBlock(Address start,
574 size_t size,
575 Executability executable) {
576 ASSERT(start != NULL);
577 ASSERT(size > 0);
578 ASSERT(initial_chunk_ != NULL);
579 ASSERT(InInitialChunk(start));
580 ASSERT(InInitialChunk(start + size - 1));
581
582 if (!initial_chunk_->Commit(start, size, executable)) return false;
583 #ifdef DEBUG
584 ZapBlock(start, size);
585 #endif
586 Counters::memory_allocated.Increment(static_cast<int>(size)); 494 Counters::memory_allocated.Increment(static_cast<int>(size));
587 return true; 495 return true;
588 } 496 }
589 497
590 498
591 bool MemoryAllocator::UncommitBlock(Address start, size_t size) { 499 bool MemoryAllocator::UncommitBlock(Address start, size_t size) {
592 ASSERT(start != NULL); 500 if (!VirtualMemory::UncommitRegion(start, size)) return false;
593 ASSERT(size > 0);
594 ASSERT(initial_chunk_ != NULL);
595 ASSERT(InInitialChunk(start));
596 ASSERT(InInitialChunk(start + size - 1));
597
598 if (!initial_chunk_->Uncommit(start, size)) return false;
599 Counters::memory_allocated.Decrement(static_cast<int>(size)); 501 Counters::memory_allocated.Decrement(static_cast<int>(size));
600 return true; 502 return true;
601 } 503 }
602 504
603 505
604 void MemoryAllocator::ZapBlock(Address start, size_t size) { 506 void MemoryAllocator::ZapBlock(Address start, size_t size) {
605 for (size_t s = 0; s + kPointerSize <= size; s += kPointerSize) { 507 for (size_t s = 0; s + kPointerSize <= size; s += kPointerSize) {
606 Memory::Address_at(start + s) = kZapValue; 508 Memory::Address_at(start + s) = kZapValue;
607 } 509 }
608 } 510 }
609 511
610 512
611 Page* MemoryAllocator::InitializePagesInChunk(int chunk_id, int pages_in_chunk, 513 void MemoryAllocator::PerformAllocationCallback(ObjectSpace space,
612 PagedSpace* owner) { 514 AllocationAction action,
613 ASSERT(IsValidChunk(chunk_id)); 515 size_t size) {
614 ASSERT(pages_in_chunk > 0); 516 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
615 517 MemoryAllocationCallbackRegistration registration =
616 Address chunk_start = chunks_[chunk_id].address(); 518 memory_allocation_callbacks_[i];
617 519 if ((registration.space & space) == space &&
618 Address low = RoundUp(chunk_start, Page::kPageSize); 520 (registration.action & action) == action)
619 521 registration.callback(space, action, static_cast<int>(size));
620 #ifdef DEBUG
621 size_t chunk_size = chunks_[chunk_id].size();
622 Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize);
623 ASSERT(pages_in_chunk <=
624 ((OffsetFrom(high) - OffsetFrom(low)) / Page::kPageSize));
625 #endif
626
627 Address page_addr = low;
628 for (int i = 0; i < pages_in_chunk; i++) {
629 Page* p = Page::FromAddress(page_addr);
630 p->opaque_header = OffsetFrom(page_addr + Page::kPageSize) | chunk_id;
631 p->InvalidateWatermark(true);
632 p->SetIsLargeObjectPage(false);
633 p->SetAllocationWatermark(p->ObjectAreaStart());
634 p->SetCachedAllocationWatermark(p->ObjectAreaStart());
635 page_addr += Page::kPageSize;
636 }
637
638 // Set the next page of the last page to 0.
639 Page* last_page = Page::FromAddress(page_addr - Page::kPageSize);
640 last_page->opaque_header = OffsetFrom(0) | chunk_id;
641
642 return Page::FromAddress(low);
643 }
644
645
646 Page* MemoryAllocator::FreePages(Page* p) {
647 if (!p->is_valid()) return p;
648
649 // Find the first page in the same chunk as 'p'
650 Page* first_page = FindFirstPageInSameChunk(p);
651 Page* page_to_return = Page::FromAddress(NULL);
652
653 if (p != first_page) {
654 // Find the last page in the same chunk as 'prev'.
655 Page* last_page = FindLastPageInSameChunk(p);
656 first_page = GetNextPage(last_page); // first page in next chunk
657
658 // set the next_page of last_page to NULL
659 SetNextPage(last_page, Page::FromAddress(NULL));
660 page_to_return = p; // return 'p' when exiting
661 }
662
663 while (first_page->is_valid()) {
664 int chunk_id = GetChunkId(first_page);
665 ASSERT(IsValidChunk(chunk_id));
666
667 // Find the first page of the next chunk before deleting this chunk.
668 first_page = GetNextPage(FindLastPageInSameChunk(first_page));
669
670 // Free the current chunk.
671 DeleteChunk(chunk_id);
672 }
673
674 return page_to_return;
675 }
676
677
678 void MemoryAllocator::FreeAllPages(PagedSpace* space) {
679 for (int i = 0, length = chunks_.length(); i < length; i++) {
680 if (chunks_[i].owner() == space) {
681 DeleteChunk(i);
682 }
683 } 522 }
684 } 523 }
685 524
686 525
687 void MemoryAllocator::DeleteChunk(int chunk_id) { 526 bool MemoryAllocator::MemoryAllocationCallbackRegistered(
688 ASSERT(IsValidChunk(chunk_id)); 527 MemoryAllocationCallback callback) {
689 528 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
690 ChunkInfo& c = chunks_[chunk_id]; 529 if (memory_allocation_callbacks_[i].callback == callback) return true;
691
692 // We cannot free a chunk contained in the initial chunk because it was not
693 // allocated with AllocateRawMemory. Instead we uncommit the virtual
694 // memory.
695 if (InInitialChunk(c.address())) {
696 // TODO(1240712): VirtualMemory::Uncommit has a return value which
697 // is ignored here.
698 initial_chunk_->Uncommit(c.address(), c.size());
699 Counters::memory_allocated.Decrement(static_cast<int>(c.size()));
700 } else {
701 RemoveFromAllocatedChunks(c.address(), c.size());
702 LOG(DeleteEvent("PagedChunk", c.address()));
703 ObjectSpace space = static_cast<ObjectSpace>(1 << c.owner()->identity());
704 size_t size = c.size();
705 FreeRawMemory(c.address(), size, c.executable());
706 PerformAllocationCallback(space, kAllocationActionFree, size);
707 } 530 }
708 c.init(NULL, 0, NULL); 531 return false;
709 Push(chunk_id);
710 } 532 }
711 533
712 534
713 Page* MemoryAllocator::FindFirstPageInSameChunk(Page* p) { 535 void MemoryAllocator::AddMemoryAllocationCallback(
714 int chunk_id = GetChunkId(p); 536 MemoryAllocationCallback callback,
715 ASSERT(IsValidChunk(chunk_id)); 537 ObjectSpace space,
716 538 AllocationAction action) {
717 Address low = RoundUp(chunks_[chunk_id].address(), Page::kPageSize); 539 ASSERT(callback != NULL);
718 return Page::FromAddress(low); 540 MemoryAllocationCallbackRegistration registration(callback, space, action);
541 ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback));
542 return memory_allocation_callbacks_.Add(registration);
719 } 543 }
720 544
721 545
722 Page* MemoryAllocator::FindLastPageInSameChunk(Page* p) { 546 void MemoryAllocator::RemoveMemoryAllocationCallback(
723 int chunk_id = GetChunkId(p); 547 MemoryAllocationCallback callback) {
724 ASSERT(IsValidChunk(chunk_id)); 548 ASSERT(callback != NULL);
725 549 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
726 Address chunk_start = chunks_[chunk_id].address(); 550 if (memory_allocation_callbacks_[i].callback == callback) {
727 size_t chunk_size = chunks_[chunk_id].size(); 551 memory_allocation_callbacks_.Remove(i);
728 552 return;
729 Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize); 553 }
730 ASSERT(chunk_start <= p->address() && p->address() < high); 554 }
731 555 UNREACHABLE();
732 return Page::FromAddress(high - Page::kPageSize);
733 } 556 }
734 557
735 558
736 #ifdef DEBUG 559 #ifdef DEBUG
737 void MemoryAllocator::ReportStatistics() { 560 void MemoryAllocator::ReportStatistics() {
738 float pct = static_cast<float>(capacity_ - size_) / capacity_; 561 float pct = static_cast<float>(capacity_ - size_) / capacity_;
739 PrintF(" capacity: %" V8_PTR_PREFIX "d" 562 PrintF(" capacity: %" V8_PTR_PREFIX "d"
740 ", used: %" V8_PTR_PREFIX "d" 563 ", used: %" V8_PTR_PREFIX "d"
741 ", available: %%%d\n\n", 564 ", available: %%%d\n\n",
742 capacity_, size_, static_cast<int>(pct*100)); 565 capacity_, size_, static_cast<int>(pct*100));
743 } 566 }
744 #endif 567 #endif
745 568
746
747 void MemoryAllocator::RelinkPageListInChunkOrder(PagedSpace* space,
748 Page** first_page,
749 Page** last_page,
750 Page** last_page_in_use) {
751 Page* first = NULL;
752 Page* last = NULL;
753
754 for (int i = 0, length = chunks_.length(); i < length; i++) {
755 ChunkInfo& chunk = chunks_[i];
756
757 if (chunk.owner() == space) {
758 if (first == NULL) {
759 Address low = RoundUp(chunk.address(), Page::kPageSize);
760 first = Page::FromAddress(low);
761 }
762 last = RelinkPagesInChunk(i,
763 chunk.address(),
764 chunk.size(),
765 last,
766 last_page_in_use);
767 }
768 }
769
770 if (first_page != NULL) {
771 *first_page = first;
772 }
773
774 if (last_page != NULL) {
775 *last_page = last;
776 }
777 }
778
779
780 Page* MemoryAllocator::RelinkPagesInChunk(int chunk_id,
781 Address chunk_start,
782 size_t chunk_size,
783 Page* prev,
784 Page** last_page_in_use) {
785 Address page_addr = RoundUp(chunk_start, Page::kPageSize);
786 int pages_in_chunk = PagesInChunk(chunk_start, chunk_size);
787
788 if (prev->is_valid()) {
789 SetNextPage(prev, Page::FromAddress(page_addr));
790 }
791
792 for (int i = 0; i < pages_in_chunk; i++) {
793 Page* p = Page::FromAddress(page_addr);
794 p->opaque_header = OffsetFrom(page_addr + Page::kPageSize) | chunk_id;
795 page_addr += Page::kPageSize;
796
797 p->InvalidateWatermark(true);
798 if (p->WasInUseBeforeMC()) {
799 *last_page_in_use = p;
800 }
801 }
802
803 // Set the next page of the last page to 0.
804 Page* last_page = Page::FromAddress(page_addr - Page::kPageSize);
805 last_page->opaque_header = OffsetFrom(0) | chunk_id;
806
807 if (last_page->WasInUseBeforeMC()) {
808 *last_page_in_use = last_page;
809 }
810
811 return last_page;
812 }
813
814
815 void MemoryAllocator::AddToAllocatedChunks(Address addr, intptr_t size) {
816 ASSERT(size == kChunkSize);
817 uintptr_t int_address = reinterpret_cast<uintptr_t>(addr);
818 AddChunkUsingAddress(int_address, int_address);
819 AddChunkUsingAddress(int_address, int_address + size - 1);
820 }
821
822
823 void MemoryAllocator::AddChunkUsingAddress(uintptr_t chunk_start,
824 uintptr_t chunk_index_base) {
825 uintptr_t* fine_grained = AllocatedChunksFinder(
826 chunk_table_,
827 chunk_index_base,
828 kChunkSizeLog2 + (kChunkTableLevels - 1) * kChunkTableBitsPerLevel,
829 kCreateTablesAsNeeded);
830 int index = FineGrainedIndexForAddress(chunk_index_base);
831 if (fine_grained[index] != kUnusedChunkTableEntry) index++;
832 ASSERT(fine_grained[index] == kUnusedChunkTableEntry);
833 fine_grained[index] = chunk_start;
834 }
835
836
837 void MemoryAllocator::RemoveFromAllocatedChunks(Address addr, intptr_t size) {
838 ASSERT(size == kChunkSize);
839 uintptr_t int_address = reinterpret_cast<uintptr_t>(addr);
840 RemoveChunkFoundUsingAddress(int_address, int_address);
841 RemoveChunkFoundUsingAddress(int_address, int_address + size - 1);
842 }
843
844
845 void MemoryAllocator::RemoveChunkFoundUsingAddress(
846 uintptr_t chunk_start,
847 uintptr_t chunk_index_base) {
848 uintptr_t* fine_grained = AllocatedChunksFinder(
849 chunk_table_,
850 chunk_index_base,
851 kChunkSizeLog2 + (kChunkTableLevels - 1) * kChunkTableBitsPerLevel,
852 kDontCreateTables);
853 // Can't remove an entry that's not there.
854 ASSERT(fine_grained != kUnusedChunkTableEntry);
855 int index = FineGrainedIndexForAddress(chunk_index_base);
856 ASSERT(fine_grained[index] != kUnusedChunkTableEntry);
857 if (fine_grained[index] != chunk_start) {
858 index++;
859 ASSERT(fine_grained[index] == chunk_start);
860 fine_grained[index] = kUnusedChunkTableEntry;
861 } else {
862 // If only one of the entries is used it must be the first, since
863 // InAllocatedChunks relies on that. Move things around so that this is
864 // the case.
865 fine_grained[index] = fine_grained[index + 1];
866 fine_grained[index + 1] = kUnusedChunkTableEntry;
867 }
868 }
869
870
871 bool MemoryAllocator::InAllocatedChunks(Address addr) {
872 uintptr_t int_address = reinterpret_cast<uintptr_t>(addr);
873 uintptr_t* fine_grained = AllocatedChunksFinder(
874 chunk_table_,
875 int_address,
876 kChunkSizeLog2 + (kChunkTableLevels - 1) * kChunkTableBitsPerLevel,
877 kDontCreateTables);
878 if (fine_grained == NULL) return false;
879 int index = FineGrainedIndexForAddress(int_address);
880 if (fine_grained[index] == kUnusedChunkTableEntry) return false;
881 uintptr_t entry = fine_grained[index];
882 if (entry <= int_address && entry + kChunkSize > int_address) return true;
883 index++;
884 if (fine_grained[index] == kUnusedChunkTableEntry) return false;
885 entry = fine_grained[index];
886 if (entry <= int_address && entry + kChunkSize > int_address) return true;
887 return false;
888 }
889
890
891 uintptr_t* MemoryAllocator::AllocatedChunksFinder(
892 uintptr_t* table,
893 uintptr_t address,
894 int bit_position,
895 CreateTables create_as_needed) {
896 if (bit_position == kChunkSizeLog2) {
897 return table;
898 }
899 ASSERT(bit_position >= kChunkSizeLog2 + kChunkTableBitsPerLevel);
900 int index =
901 ((address >> bit_position) &
902 ((V8_INTPTR_C(1) << kChunkTableBitsPerLevel) - 1));
903 uintptr_t more_fine_grained_address =
904 address & ((V8_INTPTR_C(1) << bit_position) - 1);
905 ASSERT((table == chunk_table_ && index < kChunkTableTopLevelEntries) ||
906 (table != chunk_table_ && index < 1 << kChunkTableBitsPerLevel));
907 uintptr_t* more_fine_grained_table =
908 reinterpret_cast<uintptr_t*>(table[index]);
909 if (more_fine_grained_table == kUnusedChunkTableEntry) {
910 if (create_as_needed == kDontCreateTables) return NULL;
911 int words_needed = 1 << kChunkTableBitsPerLevel;
912 if (bit_position == kChunkTableBitsPerLevel + kChunkSizeLog2) {
913 words_needed =
914 (1 << kChunkTableBitsPerLevel) * kChunkTableFineGrainedWordsPerEntry;
915 }
916 more_fine_grained_table = new uintptr_t[words_needed];
917 for (int i = 0; i < words_needed; i++) {
918 more_fine_grained_table[i] = kUnusedChunkTableEntry;
919 }
920 table[index] = reinterpret_cast<uintptr_t>(more_fine_grained_table);
921 }
922 return AllocatedChunksFinder(
923 more_fine_grained_table,
924 more_fine_grained_address,
925 bit_position - kChunkTableBitsPerLevel,
926 create_as_needed);
927 }
928
929
930 uintptr_t MemoryAllocator::chunk_table_[kChunkTableTopLevelEntries];
931
932
933 // ----------------------------------------------------------------------------- 569 // -----------------------------------------------------------------------------
934 // PagedSpace implementation 570 // PagedSpace implementation
935 571
936 PagedSpace::PagedSpace(intptr_t max_capacity, 572 PagedSpace::PagedSpace(intptr_t max_capacity,
937 AllocationSpace id, 573 AllocationSpace id,
938 Executability executable) 574 Executability executable)
939 : Space(id, executable) { 575 : Space(id, executable) {
940 max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize) 576 max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize)
941 * Page::kObjectAreaSize; 577 * Page::kObjectAreaSize;
942 accounting_stats_.Clear(); 578 accounting_stats_.Clear();
943 579
944 allocation_info_.top = NULL; 580 allocation_info_.top = NULL;
945 allocation_info_.limit = NULL; 581 allocation_info_.limit = NULL;
946
947 mc_forwarding_info_.top = NULL;
948 mc_forwarding_info_.limit = NULL;
949 } 582 }
950 583
951 584
952 bool PagedSpace::Setup(Address start, size_t size) { 585 bool PagedSpace::Setup() {
953 if (HasBeenSetup()) return false; 586 if (HasBeenSetup()) return false;
954 587
955 int num_pages = 0; 588 // Maximum space capacity can not be less than single page size.
956 // Try to use the virtual memory range passed to us. If it is too small to 589 if (max_capacity_ < Page::kPageSize) return false;
957 // contain at least one page, ignore it and allocate instead. 590
958 int pages_in_chunk = PagesInChunk(start, size); 591 first_page_ = MemoryAllocator::AllocatePage(this, executable());
959 if (pages_in_chunk > 0) { 592 if (!first_page_->is_valid()) return false;
960 first_page_ = MemoryAllocator::CommitPages(RoundUp(start, Page::kPageSize),
961 Page::kPageSize * pages_in_chunk,
962 this, &num_pages);
963 } else {
964 int requested_pages =
965 Min(MemoryAllocator::kPagesPerChunk,
966 static_cast<int>(max_capacity_ / Page::kObjectAreaSize));
967 first_page_ =
968 MemoryAllocator::AllocatePages(requested_pages, &num_pages, this);
969 if (!first_page_->is_valid()) return false;
970 }
971 593
972 // We are sure that the first page is valid and that we have at least one 594 // We are sure that the first page is valid and that we have at least one
973 // page. 595 // page.
974 ASSERT(first_page_->is_valid()); 596 accounting_stats_.ExpandSpace(Page::kObjectAreaSize);
975 ASSERT(num_pages > 0);
976 accounting_stats_.ExpandSpace(num_pages * Page::kObjectAreaSize);
977 ASSERT(Capacity() <= max_capacity_); 597 ASSERT(Capacity() <= max_capacity_);
978 598
979 // Sequentially clear region marks in the newly allocated 599 last_page_ = first_page_;
980 // pages and cache the current last page in the space. 600 ASSERT(!last_page_->next_page()->is_valid());
981 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) {
982 p->SetRegionMarks(Page::kAllRegionsCleanMarks);
983 last_page_ = p;
984 }
985 601
986 // Use first_page_ for allocation. 602 // Use first_page_ for allocation.
987 SetAllocationInfo(&allocation_info_, first_page_); 603 SetAllocationInfo(&allocation_info_, first_page_);
988 604
989 page_list_is_chunk_ordered_ = true;
990
991 return true; 605 return true;
992 } 606 }
993 607
994 608
995 bool PagedSpace::HasBeenSetup() { 609 bool PagedSpace::HasBeenSetup() {
996 return (Capacity() > 0); 610 return (Capacity() > 0);
997 } 611 }
998 612
999 613
1000 void PagedSpace::TearDown() { 614 void PagedSpace::TearDown() {
1001 MemoryAllocator::FreeAllPages(this); 615 Page* next = NULL;
616 for (Page* p = first_page_; p->is_valid(); p = next) {
617 next = p->next_page();
618 MemoryAllocator::Free(p);
619 }
1002 first_page_ = NULL; 620 first_page_ = NULL;
621 last_page_ = NULL;
1003 accounting_stats_.Clear(); 622 accounting_stats_.Clear();
1004 } 623 }
1005 624
1006 625
1007 #ifdef ENABLE_HEAP_PROTECTION 626 #ifdef ENABLE_HEAP_PROTECTION
1008 627
1009 void PagedSpace::Protect() { 628 void PagedSpace::Protect() {
1010 Page* page = first_page_; 629 Page* page = first_page_;
1011 while (page->is_valid()) { 630 while (page->is_valid()) {
1012 MemoryAllocator::ProtectChunkFromPage(page); 631 MemoryAllocator::ProtectChunkFromPage(page);
1013 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page(); 632 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page();
1014 } 633 }
1015 } 634 }
1016 635
1017 636
1018 void PagedSpace::Unprotect() { 637 void PagedSpace::Unprotect() {
1019 Page* page = first_page_; 638 Page* page = first_page_;
1020 while (page->is_valid()) { 639 while (page->is_valid()) {
1021 MemoryAllocator::UnprotectChunkFromPage(page); 640 MemoryAllocator::UnprotectChunkFromPage(page);
1022 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page(); 641 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page();
1023 } 642 }
1024 } 643 }
1025 644
1026 #endif 645 #endif
1027 646
1028 647
1029 void PagedSpace::MarkAllPagesClean() {
1030 PageIterator it(this, PageIterator::ALL_PAGES);
1031 while (it.has_next()) {
1032 it.next()->SetRegionMarks(Page::kAllRegionsCleanMarks);
1033 }
1034 }
1035
1036
1037 MaybeObject* PagedSpace::FindObject(Address addr) { 648 MaybeObject* PagedSpace::FindObject(Address addr) {
1038 // Note: this function can only be called before or after mark-compact GC 649 // Note: this function can only be called before or after mark-compact GC
1039 // because it accesses map pointers. 650 // because it accesses map pointers.
1040 ASSERT(!MarkCompactCollector::in_use()); 651 ASSERT(!MarkCompactCollector::in_use());
1041 652
1042 if (!Contains(addr)) return Failure::Exception(); 653 if (!Contains(addr)) return Failure::Exception();
1043 654
1044 Page* p = Page::FromAddress(addr); 655 Page* p = Page::FromAddress(addr);
1045 ASSERT(IsUsed(p)); 656 ASSERT(IsUsed(p));
1046 Address cur = p->ObjectAreaStart(); 657 Address cur = p->ObjectAreaStart();
(...skipping 19 matching lines...) Expand all
1066 } 677 }
1067 678
1068 679
1069 void PagedSpace::SetAllocationInfo(AllocationInfo* alloc_info, Page* p) { 680 void PagedSpace::SetAllocationInfo(AllocationInfo* alloc_info, Page* p) {
1070 alloc_info->top = p->ObjectAreaStart(); 681 alloc_info->top = p->ObjectAreaStart();
1071 alloc_info->limit = p->ObjectAreaEnd(); 682 alloc_info->limit = p->ObjectAreaEnd();
1072 ASSERT(alloc_info->VerifyPagedAllocation()); 683 ASSERT(alloc_info->VerifyPagedAllocation());
1073 } 684 }
1074 685
1075 686
1076 void PagedSpace::MCResetRelocationInfo() { 687 bool PagedSpace::Expand() {
1077 // Set page indexes.
1078 int i = 0;
1079 PageIterator it(this, PageIterator::ALL_PAGES);
1080 while (it.has_next()) {
1081 Page* p = it.next();
1082 p->mc_page_index = i++;
1083 }
1084
1085 // Set mc_forwarding_info_ to the first page in the space.
1086 SetAllocationInfo(&mc_forwarding_info_, first_page_);
1087 // All the bytes in the space are 'available'. We will rediscover
1088 // allocated and wasted bytes during GC.
1089 accounting_stats_.Reset();
1090 }
1091
1092
1093 int PagedSpace::MCSpaceOffsetForAddress(Address addr) {
1094 #ifdef DEBUG
1095 // The Contains function considers the address at the beginning of a
1096 // page in the page, MCSpaceOffsetForAddress considers it is in the
1097 // previous page.
1098 if (Page::IsAlignedToPageSize(addr)) {
1099 ASSERT(Contains(addr - kPointerSize));
1100 } else {
1101 ASSERT(Contains(addr));
1102 }
1103 #endif
1104
1105 // If addr is at the end of a page, it belongs to previous page
1106 Page* p = Page::IsAlignedToPageSize(addr)
1107 ? Page::FromAllocationTop(addr)
1108 : Page::FromAddress(addr);
1109 int index = p->mc_page_index;
1110 return (index * Page::kPageSize) + p->Offset(addr);
1111 }
1112
1113
1114 // Slow case for reallocating and promoting objects during a compacting
1115 // collection. This function is not space-specific.
1116 HeapObject* PagedSpace::SlowMCAllocateRaw(int size_in_bytes) {
1117 Page* current_page = TopPageOf(mc_forwarding_info_);
1118 if (!current_page->next_page()->is_valid()) {
1119 if (!Expand(current_page)) {
1120 return NULL;
1121 }
1122 }
1123
1124 // There are surely more pages in the space now.
1125 ASSERT(current_page->next_page()->is_valid());
1126 // We do not add the top of page block for current page to the space's
1127 // free list---the block may contain live objects so we cannot write
1128 // bookkeeping information to it. Instead, we will recover top of page
1129 // blocks when we move objects to their new locations.
1130 //
1131 // We do however write the allocation pointer to the page. The encoding
1132 // of forwarding addresses is as an offset in terms of live bytes, so we
1133 // need quick access to the allocation top of each page to decode
1134 // forwarding addresses.
1135 current_page->SetAllocationWatermark(mc_forwarding_info_.top);
1136 current_page->next_page()->InvalidateWatermark(true);
1137 SetAllocationInfo(&mc_forwarding_info_, current_page->next_page());
1138 return AllocateLinearly(&mc_forwarding_info_, size_in_bytes);
1139 }
1140
1141
1142 bool PagedSpace::Expand(Page* last_page) {
1143 ASSERT(max_capacity_ % Page::kObjectAreaSize == 0); 688 ASSERT(max_capacity_ % Page::kObjectAreaSize == 0);
1144 ASSERT(Capacity() % Page::kObjectAreaSize == 0); 689 ASSERT(Capacity() % Page::kObjectAreaSize == 0);
1145 690
1146 if (Capacity() == max_capacity_) return false; 691 if (Capacity() == max_capacity_) return false;
1147 692
1148 ASSERT(Capacity() < max_capacity_); 693 ASSERT(Capacity() < max_capacity_);
1149 // Last page must be valid and its next page is invalid. 694 // Last page must be valid and its next page is invalid.
1150 ASSERT(last_page->is_valid() && !last_page->next_page()->is_valid()); 695 ASSERT(last_page_->is_valid() && !last_page_->next_page()->is_valid());
1151 696
1152 int available_pages = 697 // We are going to exceed capacity for this space.
1153 static_cast<int>((max_capacity_ - Capacity()) / Page::kObjectAreaSize); 698 if ((Capacity() + Page::kPageSize) > max_capacity_) return false;
1154 // We don't want to have to handle small chunks near the end so if there are
1155 // not kPagesPerChunk pages available without exceeding the max capacity then
1156 // act as if memory has run out.
1157 if (available_pages < MemoryAllocator::kPagesPerChunk) return false;
1158 699
1159 int desired_pages = Min(available_pages, MemoryAllocator::kPagesPerChunk); 700 Page* p = MemoryAllocator::AllocatePage(this, executable());
1160 Page* p = MemoryAllocator::AllocatePages(desired_pages, &desired_pages, this);
1161 if (!p->is_valid()) return false; 701 if (!p->is_valid()) return false;
1162 702
1163 accounting_stats_.ExpandSpace(desired_pages * Page::kObjectAreaSize); 703 accounting_stats_.ExpandSpace(Page::kObjectAreaSize);
1164 ASSERT(Capacity() <= max_capacity_); 704 ASSERT(Capacity() <= max_capacity_);
1165 705
1166 MemoryAllocator::SetNextPage(last_page, p); 706 last_page_->set_next_page(p);
707 last_page_ = p;
1167 708
1168 // Sequentially clear region marks of new pages and and cache the 709 ASSERT(!last_page_->next_page()->is_valid());
1169 // new last page in the space.
1170 while (p->is_valid()) {
1171 p->SetRegionMarks(Page::kAllRegionsCleanMarks);
1172 last_page_ = p;
1173 p = p->next_page();
1174 }
1175 710
1176 return true; 711 return true;
1177 } 712 }
1178 713
1179 714
1180 #ifdef DEBUG 715 #ifdef DEBUG
1181 int PagedSpace::CountTotalPages() { 716 int PagedSpace::CountTotalPages() {
1182 int count = 0; 717 int count = 0;
1183 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) { 718 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) {
1184 count++; 719 count++;
1185 } 720 }
1186 return count; 721 return count;
1187 } 722 }
1188 #endif 723 #endif
1189 724
1190 725
1191 void PagedSpace::Shrink() { 726 void PagedSpace::Shrink() {
1192 if (!page_list_is_chunk_ordered_) {
1193 // We can't shrink space if pages is not chunk-ordered
1194 // (see comment for class MemoryAllocator for definition).
1195 return;
1196 }
1197
1198 // Release half of free pages.
1199 Page* top_page = AllocationTopPage(); 727 Page* top_page = AllocationTopPage();
1200 ASSERT(top_page->is_valid()); 728 ASSERT(top_page->is_valid());
1201 729
1202 // Count the number of pages we would like to free. 730 // TODO(gc) release half of pages?
1203 int pages_to_free = 0; 731 if (top_page->next_page()->is_valid()) {
1204 for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) { 732 int pages_freed = 0;
1205 pages_to_free++; 733 Page* page = top_page->next_page();
734 Page* next_page;
735 while (page->is_valid()) {
736 next_page = page->next_page();
737 MemoryAllocator::Free(page);
738 pages_freed++;
739 page = next_page;
740 }
741 top_page->set_next_page(Page::FromAddress(NULL));
742 last_page_ = top_page;
743
744 accounting_stats_.ShrinkSpace(pages_freed * Page::kObjectAreaSize);
745 ASSERT(Capacity() == CountTotalPages() * Page::kObjectAreaSize);
1206 } 746 }
1207
1208 // Free pages after top_page.
1209 Page* p = MemoryAllocator::FreePages(top_page->next_page());
1210 MemoryAllocator::SetNextPage(top_page, p);
1211
1212 // Find out how many pages we failed to free and update last_page_.
1213 // Please note pages can only be freed in whole chunks.
1214 last_page_ = top_page;
1215 for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) {
1216 pages_to_free--;
1217 last_page_ = p;
1218 }
1219
1220 accounting_stats_.ShrinkSpace(pages_to_free * Page::kObjectAreaSize);
1221 ASSERT(Capacity() == CountTotalPages() * Page::kObjectAreaSize);
1222 } 747 }
1223 748
1224 749
1225 bool PagedSpace::EnsureCapacity(int capacity) { 750 bool PagedSpace::EnsureCapacity(int capacity) {
1226 if (Capacity() >= capacity) return true; 751 while (Capacity() < capacity) {
1227 752 // Expand the space until it has the required capacity or expansion fails.
1228 // Start from the allocation top and loop to the last page in the space. 753 if (!Expand()) return false;
1229 Page* last_page = AllocationTopPage();
1230 Page* next_page = last_page->next_page();
1231 while (next_page->is_valid()) {
1232 last_page = MemoryAllocator::FindLastPageInSameChunk(next_page);
1233 next_page = last_page->next_page();
1234 } 754 }
1235
1236 // Expand the space until it has the required capacity or expansion fails.
1237 do {
1238 if (!Expand(last_page)) return false;
1239 ASSERT(last_page->next_page()->is_valid());
1240 last_page =
1241 MemoryAllocator::FindLastPageInSameChunk(last_page->next_page());
1242 } while (Capacity() < capacity);
1243
1244 return true; 755 return true;
1245 } 756 }
1246 757
1247 758
1248 #ifdef DEBUG 759 #ifdef DEBUG
1249 void PagedSpace::Print() { } 760 void PagedSpace::Print() { }
1250 #endif 761 #endif
1251 762
1252 763
1253 #ifdef DEBUG 764 #ifdef DEBUG
1254 // We do not assume that the PageIterator works, because it depends on the 765 // We do not assume that the PageIterator works, because it depends on the
1255 // invariants we are checking during verification. 766 // invariants we are checking during verification.
1256 void PagedSpace::Verify(ObjectVisitor* visitor) { 767 void PagedSpace::Verify(ObjectVisitor* visitor) {
1257 // The allocation pointer should be valid, and it should be in a page in the 768 // The allocation pointer should be valid, and it should be in a page in the
1258 // space. 769 // space.
1259 ASSERT(allocation_info_.VerifyPagedAllocation()); 770 ASSERT(allocation_info_.VerifyPagedAllocation());
1260 Page* top_page = Page::FromAllocationTop(allocation_info_.top); 771 Page* top_page = Page::FromAllocationTop(allocation_info_.top);
1261 ASSERT(MemoryAllocator::IsPageInSpace(top_page, this));
1262 772
1263 // Loop over all the pages. 773 // Loop over all the pages.
1264 bool above_allocation_top = false; 774 bool above_allocation_top = false;
1265 Page* current_page = first_page_; 775 Page* current_page = first_page_;
1266 while (current_page->is_valid()) { 776 while (current_page->is_valid()) {
1267 if (above_allocation_top) { 777 if (above_allocation_top) {
1268 // We don't care what's above the allocation top. 778 // We don't care what's above the allocation top.
1269 } else { 779 } else {
1270 Address top = current_page->AllocationTop(); 780 Address top = current_page->AllocationTop();
1271 if (current_page == top_page) { 781 if (current_page == top_page) {
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
1307 current_page = current_page->next_page(); 817 current_page = current_page->next_page();
1308 } 818 }
1309 } 819 }
1310 #endif 820 #endif
1311 821
1312 822
1313 // ----------------------------------------------------------------------------- 823 // -----------------------------------------------------------------------------
1314 // NewSpace implementation 824 // NewSpace implementation
1315 825
1316 826
1317 bool NewSpace::Setup(Address start, int size) { 827 bool NewSpace::Setup(int maximum_semispace_capacity) {
1318 // Setup new space based on the preallocated memory block defined by 828 // Setup new space based on the preallocated memory block defined by
1319 // start and size. The provided space is divided into two semi-spaces. 829 // start and size. The provided space is divided into two semi-spaces.
1320 // To support fast containment testing in the new space, the size of 830 // To support fast containment testing in the new space, the size of
1321 // this chunk must be a power of two and it must be aligned to its size. 831 // this chunk must be a power of two and it must be aligned to its size.
1322 int initial_semispace_capacity = Heap::InitialSemiSpaceSize(); 832 int initial_semispace_capacity = Heap::InitialSemiSpaceSize();
1323 int maximum_semispace_capacity = Heap::MaxSemiSpaceSize(); 833
834 size_t size = 0;
835 void* base =
836 MemoryAllocator::ReserveAlignedMemory(2*maximum_semispace_capacity,
837 2*maximum_semispace_capacity,
838 &size);
839
840 if (base == NULL) return false;
841
842 chunk_base_ = static_cast<Address>(base);
843 chunk_size_ = static_cast<uintptr_t>(size);
844 LOG(NewEvent("InitialChunk", chunk_base_, chunk_size_));
1324 845
1325 ASSERT(initial_semispace_capacity <= maximum_semispace_capacity); 846 ASSERT(initial_semispace_capacity <= maximum_semispace_capacity);
1326 ASSERT(IsPowerOf2(maximum_semispace_capacity)); 847 ASSERT(IsPowerOf2(maximum_semispace_capacity));
1327 848
1328 // Allocate and setup the histogram arrays if necessary. 849 // Allocate and setup the histogram arrays if necessary.
1329 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) 850 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
1330 allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); 851 allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
1331 promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); 852 promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
1332 853
1333 #define SET_NAME(name) allocated_histogram_[name].set_name(#name); \ 854 #define SET_NAME(name) allocated_histogram_[name].set_name(#name); \
1334 promoted_histogram_[name].set_name(#name); 855 promoted_histogram_[name].set_name(#name);
1335 INSTANCE_TYPE_LIST(SET_NAME) 856 INSTANCE_TYPE_LIST(SET_NAME)
1336 #undef SET_NAME 857 #undef SET_NAME
1337 #endif 858 #endif
1338 859
1339 ASSERT(size == 2 * Heap::ReservedSemiSpaceSize()); 860 ASSERT(maximum_semispace_capacity == Heap::ReservedSemiSpaceSize());
1340 ASSERT(IsAddressAligned(start, size, 0)); 861 ASSERT(static_cast<intptr_t>(chunk_size_) >= 2 * Heap::ReservedSemiSpaceSize() );
862 ASSERT(IsAddressAligned(chunk_base_, 2*maximum_semispace_capacity, 0));
Erik Corry 2010/12/22 13:48:27 spaces around '*'
Vyacheslav Egorov (Chromium) 2010/12/22 19:55:07 Done.
1341 863
1342 if (!to_space_.Setup(start, 864 if (!to_space_.Setup(chunk_base_,
1343 initial_semispace_capacity, 865 initial_semispace_capacity,
1344 maximum_semispace_capacity)) { 866 maximum_semispace_capacity)) {
1345 return false; 867 return false;
1346 } 868 }
1347 if (!from_space_.Setup(start + maximum_semispace_capacity, 869 if (!from_space_.Setup(chunk_base_ + maximum_semispace_capacity,
1348 initial_semispace_capacity, 870 initial_semispace_capacity,
1349 maximum_semispace_capacity)) { 871 maximum_semispace_capacity)) {
1350 return false; 872 return false;
1351 } 873 }
1352 874
1353 start_ = start; 875 start_ = chunk_base_;
1354 address_mask_ = ~(size - 1); 876 address_mask_ = ~(2*maximum_semispace_capacity - 1);
Erik Corry 2010/12/22 13:48:27 and here and a few other places
1355 object_mask_ = address_mask_ | kHeapObjectTagMask; 877 object_mask_ = address_mask_ | kHeapObjectTagMask;
1356 object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag; 878 object_expected_ = reinterpret_cast<uintptr_t>(start_) | kHeapObjectTag;
1357 879
1358 allocation_info_.top = to_space_.low(); 880 allocation_info_.top = to_space_.low();
1359 allocation_info_.limit = to_space_.high(); 881 allocation_info_.limit = to_space_.high();
1360 mc_forwarding_info_.top = NULL;
1361 mc_forwarding_info_.limit = NULL;
1362 882
1363 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 883 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1364 return true; 884 return true;
1365 } 885 }
1366 886
1367 887
1368 void NewSpace::TearDown() { 888 void NewSpace::TearDown() {
1369 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) 889 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
1370 if (allocated_histogram_) { 890 if (allocated_histogram_) {
1371 DeleteArray(allocated_histogram_); 891 DeleteArray(allocated_histogram_);
1372 allocated_histogram_ = NULL; 892 allocated_histogram_ = NULL;
1373 } 893 }
1374 if (promoted_histogram_) { 894 if (promoted_histogram_) {
1375 DeleteArray(promoted_histogram_); 895 DeleteArray(promoted_histogram_);
1376 promoted_histogram_ = NULL; 896 promoted_histogram_ = NULL;
1377 } 897 }
1378 #endif 898 #endif
1379 899
1380 start_ = NULL; 900 start_ = NULL;
1381 allocation_info_.top = NULL; 901 allocation_info_.top = NULL;
1382 allocation_info_.limit = NULL; 902 allocation_info_.limit = NULL;
1383 mc_forwarding_info_.top = NULL;
1384 mc_forwarding_info_.limit = NULL;
1385 903
1386 to_space_.TearDown(); 904 to_space_.TearDown();
1387 from_space_.TearDown(); 905 from_space_.TearDown();
906
907 LOG(DeleteEvent("InitialChunk", chunk_base_));
908 MemoryAllocator::FreeMemory(chunk_base_,
909 static_cast<size_t>(chunk_size_),
910 NOT_EXECUTABLE);
911 chunk_base_ = NULL;
912 chunk_size_ = 0;
1388 } 913 }
1389 914
1390 915
1391 #ifdef ENABLE_HEAP_PROTECTION 916 #ifdef ENABLE_HEAP_PROTECTION
1392 917
1393 void NewSpace::Protect() { 918 void NewSpace::Protect() {
1394 MemoryAllocator::Protect(ToSpaceLow(), Capacity()); 919 MemoryAllocator::Protect(ToSpaceLow(), Capacity());
1395 MemoryAllocator::Protect(FromSpaceLow(), Capacity()); 920 MemoryAllocator::Protect(FromSpaceLow(), Capacity());
1396 } 921 }
1397 922
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
1454 } 979 }
1455 980
1456 981
1457 void NewSpace::ResetAllocationInfo() { 982 void NewSpace::ResetAllocationInfo() {
1458 allocation_info_.top = to_space_.low(); 983 allocation_info_.top = to_space_.low();
1459 allocation_info_.limit = to_space_.high(); 984 allocation_info_.limit = to_space_.high();
1460 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 985 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1461 } 986 }
1462 987
1463 988
1464 void NewSpace::MCResetRelocationInfo() {
1465 mc_forwarding_info_.top = from_space_.low();
1466 mc_forwarding_info_.limit = from_space_.high();
1467 ASSERT_SEMISPACE_ALLOCATION_INFO(mc_forwarding_info_, from_space_);
1468 }
1469
1470
1471 void NewSpace::MCCommitRelocationInfo() {
1472 // Assumes that the spaces have been flipped so that mc_forwarding_info_ is
1473 // valid allocation info for the to space.
1474 allocation_info_.top = mc_forwarding_info_.top;
1475 allocation_info_.limit = to_space_.high();
1476 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1477 }
1478
1479
1480 #ifdef DEBUG 989 #ifdef DEBUG
1481 // We do not use the SemispaceIterator because verification doesn't assume 990 // We do not use the SemispaceIterator because verification doesn't assume
1482 // that it works (it depends on the invariants we are checking). 991 // that it works (it depends on the invariants we are checking).
1483 void NewSpace::Verify() { 992 void NewSpace::Verify() {
1484 // The allocation pointer should be in the space or at the very end. 993 // The allocation pointer should be in the space or at the very end.
1485 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 994 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1486 995
1487 // There should be objects packed in from the low address up to the 996 // There should be objects packed in from the low address up to the
1488 // allocation pointer. 997 // allocation pointer.
1489 Address current = to_space_.low(); 998 Address current = to_space_.low();
(...skipping 633 matching lines...) Expand 10 before | Expand all | Expand 10 after
2123 cur_addr = cur_node->next(); 1632 cur_addr = cur_node->next();
2124 cur_node->SetMark(); 1633 cur_node->SetMark();
2125 } 1634 }
2126 } 1635 }
2127 1636
2128 1637
2129 // ----------------------------------------------------------------------------- 1638 // -----------------------------------------------------------------------------
2130 // OldSpace implementation 1639 // OldSpace implementation
2131 1640
2132 void OldSpace::PrepareForMarkCompact(bool will_compact) { 1641 void OldSpace::PrepareForMarkCompact(bool will_compact) {
1642 ASSERT(!will_compact);
2133 // Call prepare of the super class. 1643 // Call prepare of the super class.
2134 PagedSpace::PrepareForMarkCompact(will_compact); 1644 PagedSpace::PrepareForMarkCompact(will_compact);
2135 1645
2136 if (will_compact) { 1646 // During a non-compacting collection, everything below the linear
2137 // Reset relocation info. During a compacting collection, everything in 1647 // allocation pointer is considered allocated (everything above is
2138 // the space is considered 'available' and we will rediscover live data 1648 // available) and we will rediscover available and wasted bytes during
2139 // and waste during the collection. 1649 // the collection.
2140 MCResetRelocationInfo(); 1650 accounting_stats_.AllocateBytes(free_list_.available());
2141 ASSERT(Available() == Capacity()); 1651 accounting_stats_.FillWastedBytes(Waste());
2142 } else {
2143 // During a non-compacting collection, everything below the linear
2144 // allocation pointer is considered allocated (everything above is
2145 // available) and we will rediscover available and wasted bytes during
2146 // the collection.
2147 accounting_stats_.AllocateBytes(free_list_.available());
2148 accounting_stats_.FillWastedBytes(Waste());
2149 }
2150 1652
2151 // Clear the free list before a full GC---it will be rebuilt afterward. 1653 // Clear the free list before a full GC---it will be rebuilt afterward.
2152 free_list_.Reset(); 1654 free_list_.Reset();
2153 } 1655 }
2154 1656
2155 1657
2156 void OldSpace::MCCommitRelocationInfo() {
2157 // Update fast allocation info.
2158 allocation_info_.top = mc_forwarding_info_.top;
2159 allocation_info_.limit = mc_forwarding_info_.limit;
2160 ASSERT(allocation_info_.VerifyPagedAllocation());
2161
2162 // The space is compacted and we haven't yet built free lists or
2163 // wasted any space.
2164 ASSERT(Waste() == 0);
2165 ASSERT(AvailableFree() == 0);
2166
2167 // Build the free list for the space.
2168 int computed_size = 0;
2169 PageIterator it(this, PageIterator::PAGES_USED_BY_MC);
2170 while (it.has_next()) {
2171 Page* p = it.next();
2172 // Space below the relocation pointer is allocated.
2173 computed_size +=
2174 static_cast<int>(p->AllocationWatermark() - p->ObjectAreaStart());
2175 if (it.has_next()) {
2176 // Free the space at the top of the page.
2177 int extra_size =
2178 static_cast<int>(p->ObjectAreaEnd() - p->AllocationWatermark());
2179 if (extra_size > 0) {
2180 int wasted_bytes = free_list_.Free(p->AllocationWatermark(),
2181 extra_size);
2182 // The bytes we have just "freed" to add to the free list were
2183 // already accounted as available.
2184 accounting_stats_.WasteBytes(wasted_bytes);
2185 }
2186 }
2187 }
2188
2189 // Make sure the computed size - based on the used portion of the pages in
2190 // use - matches the size obtained while computing forwarding addresses.
2191 ASSERT(computed_size == Size());
2192 }
2193
2194
2195 bool NewSpace::ReserveSpace(int bytes) { 1658 bool NewSpace::ReserveSpace(int bytes) {
2196 // We can't reliably unpack a partial snapshot that needs more new space 1659 // We can't reliably unpack a partial snapshot that needs more new space
2197 // space than the minimum NewSpace size. 1660 // space than the minimum NewSpace size.
2198 ASSERT(bytes <= InitialCapacity()); 1661 ASSERT(bytes <= InitialCapacity());
2199 Address limit = allocation_info_.limit; 1662 Address limit = allocation_info_.limit;
2200 Address top = allocation_info_.top; 1663 Address top = allocation_info_.top;
2201 return limit - top >= bytes; 1664 return limit - top >= bytes;
2202 } 1665 }
2203 1666
2204 1667
2205 void PagedSpace::FreePages(Page* prev, Page* last) { 1668 void PagedSpace::FreePages(Page* prev, Page* last) {
2206 if (last == AllocationTopPage()) { 1669 if (last == AllocationTopPage()) {
2207 // Pages are already at the end of used pages. 1670 // Pages are already at the end of used pages.
2208 return; 1671 return;
2209 } 1672 }
2210 1673
2211 Page* first = NULL; 1674 Page* first = NULL;
2212 1675
2213 // Remove pages from the list. 1676 // Remove pages from the list.
2214 if (prev == NULL) { 1677 if (prev == NULL) {
2215 first = first_page_; 1678 first = first_page_;
2216 first_page_ = last->next_page(); 1679 first_page_ = last->next_page();
2217 } else { 1680 } else {
2218 first = prev->next_page(); 1681 first = prev->next_page();
2219 MemoryAllocator::SetNextPage(prev, last->next_page()); 1682 prev->set_next_page(last->next_page());
2220 } 1683 }
2221 1684
2222 // Attach it after the last page. 1685 // Attach it after the last page.
2223 MemoryAllocator::SetNextPage(last_page_, first); 1686 last_page_->set_next_page(first);
2224 last_page_ = last; 1687 last_page_ = last;
2225 MemoryAllocator::SetNextPage(last, NULL); 1688 last->set_next_page(NULL);
2226 1689
2227 // Clean them up. 1690 // Clean them up.
2228 do { 1691 do {
2229 first->InvalidateWatermark(true); 1692 first->InvalidateWatermark(true);
2230 first->SetAllocationWatermark(first->ObjectAreaStart()); 1693 first->SetAllocationWatermark(first->ObjectAreaStart());
2231 first->SetCachedAllocationWatermark(first->ObjectAreaStart()); 1694 first->SetCachedAllocationWatermark(first->ObjectAreaStart());
2232 first->SetRegionMarks(Page::kAllRegionsCleanMarks); 1695 first->SetRegionMarks(Page::kAllRegionsCleanMarks);
2233 first = first->next_page(); 1696 first = first->next_page();
2234 } while (first != NULL); 1697 } while (first != NULL);
2235
2236 // Order of pages in this space might no longer be consistent with
2237 // order of pages in chunks.
2238 page_list_is_chunk_ordered_ = false;
2239 }
2240
2241
2242 void PagedSpace::RelinkPageListInChunkOrder(bool deallocate_blocks) {
2243 const bool add_to_freelist = true;
2244
2245 // Mark used and unused pages to properly fill unused pages
2246 // after reordering.
2247 PageIterator all_pages_iterator(this, PageIterator::ALL_PAGES);
2248 Page* last_in_use = AllocationTopPage();
2249 bool in_use = true;
2250
2251 while (all_pages_iterator.has_next()) {
2252 Page* p = all_pages_iterator.next();
2253 p->SetWasInUseBeforeMC(in_use);
2254 if (p == last_in_use) {
2255 // We passed a page containing allocation top. All consequent
2256 // pages are not used.
2257 in_use = false;
2258 }
2259 }
2260
2261 if (page_list_is_chunk_ordered_) return;
2262
2263 Page* new_last_in_use = Page::FromAddress(NULL);
2264 MemoryAllocator::RelinkPageListInChunkOrder(this,
2265 &first_page_,
2266 &last_page_,
2267 &new_last_in_use);
2268 ASSERT(new_last_in_use->is_valid());
2269
2270 if (new_last_in_use != last_in_use) {
2271 // Current allocation top points to a page which is now in the middle
2272 // of page list. We should move allocation top forward to the new last
2273 // used page so various object iterators will continue to work properly.
2274 int size_in_bytes = static_cast<int>(PageAllocationLimit(last_in_use) -
2275 last_in_use->AllocationTop());
2276
2277 last_in_use->SetAllocationWatermark(last_in_use->AllocationTop());
2278 if (size_in_bytes > 0) {
2279 Address start = last_in_use->AllocationTop();
2280 if (deallocate_blocks) {
2281 accounting_stats_.AllocateBytes(size_in_bytes);
2282 DeallocateBlock(start, size_in_bytes, add_to_freelist);
2283 } else {
2284 Heap::CreateFillerObjectAt(start, size_in_bytes);
2285 }
2286 }
2287
2288 // New last in use page was in the middle of the list before
2289 // sorting so it full.
2290 SetTop(new_last_in_use->AllocationTop());
2291
2292 ASSERT(AllocationTopPage() == new_last_in_use);
2293 ASSERT(AllocationTopPage()->WasInUseBeforeMC());
2294 }
2295
2296 PageIterator pages_in_use_iterator(this, PageIterator::PAGES_IN_USE);
2297 while (pages_in_use_iterator.has_next()) {
2298 Page* p = pages_in_use_iterator.next();
2299 if (!p->WasInUseBeforeMC()) {
2300 // Empty page is in the middle of a sequence of used pages.
2301 // Allocate it as a whole and deallocate immediately.
2302 int size_in_bytes = static_cast<int>(PageAllocationLimit(p) -
2303 p->ObjectAreaStart());
2304
2305 p->SetAllocationWatermark(p->ObjectAreaStart());
2306 Address start = p->ObjectAreaStart();
2307 if (deallocate_blocks) {
2308 accounting_stats_.AllocateBytes(size_in_bytes);
2309 DeallocateBlock(start, size_in_bytes, add_to_freelist);
2310 } else {
2311 Heap::CreateFillerObjectAt(start, size_in_bytes);
2312 }
2313 }
2314 }
2315
2316 page_list_is_chunk_ordered_ = true;
2317 } 1698 }
2318 1699
2319 1700
2320 void PagedSpace::PrepareForMarkCompact(bool will_compact) { 1701 void PagedSpace::PrepareForMarkCompact(bool will_compact) {
2321 if (will_compact) { 1702 ASSERT(!will_compact);
2322 RelinkPageListInChunkOrder(false);
2323 }
2324 } 1703 }
2325 1704
2326 1705
2327 bool PagedSpace::ReserveSpace(int bytes) { 1706 bool PagedSpace::ReserveSpace(int bytes) {
2328 Address limit = allocation_info_.limit; 1707 Address limit = allocation_info_.limit;
2329 Address top = allocation_info_.top; 1708 Address top = allocation_info_.top;
2330 if (limit - top >= bytes) return true; 1709 if (limit - top >= bytes) return true;
2331 1710
2332 // There wasn't enough space in the current page. Lets put the rest 1711 // There wasn't enough space in the current page. Lets put the rest
2333 // of the page on the free list and start a fresh page. 1712 // of the page on the free list and start a fresh page.
2334 PutRestOfCurrentPageOnFreeList(TopPageOf(allocation_info_)); 1713 PutRestOfCurrentPageOnFreeList(TopPageOf(allocation_info_));
2335 1714
2336 Page* reserved_page = TopPageOf(allocation_info_); 1715 Page* reserved_page = TopPageOf(allocation_info_);
2337 int bytes_left_to_reserve = bytes; 1716 int bytes_left_to_reserve = bytes;
2338 while (bytes_left_to_reserve > 0) { 1717 while (bytes_left_to_reserve > 0) {
2339 if (!reserved_page->next_page()->is_valid()) { 1718 if (!reserved_page->next_page()->is_valid()) {
2340 if (Heap::OldGenerationAllocationLimitReached()) return false; 1719 if (Heap::OldGenerationAllocationLimitReached()) return false;
2341 Expand(reserved_page); 1720 Expand();
2342 } 1721 }
2343 bytes_left_to_reserve -= Page::kPageSize; 1722 bytes_left_to_reserve -= Page::kPageSize;
2344 reserved_page = reserved_page->next_page(); 1723 reserved_page = reserved_page->next_page();
2345 if (!reserved_page->is_valid()) return false; 1724 if (!reserved_page->is_valid()) return false;
2346 } 1725 }
2347 ASSERT(TopPageOf(allocation_info_)->next_page()->is_valid()); 1726 ASSERT(TopPageOf(allocation_info_)->next_page()->is_valid());
2348 TopPageOf(allocation_info_)->next_page()->InvalidateWatermark(true); 1727 TopPageOf(allocation_info_)->next_page()->InvalidateWatermark(true);
2349 SetAllocationInfo(&allocation_info_, 1728 SetAllocationInfo(&allocation_info_,
2350 TopPageOf(allocation_info_)->next_page()); 1729 TopPageOf(allocation_info_)->next_page());
2351 return true; 1730 return true;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
2400 1779
2401 // Free list allocation failed and there is no next page. Fail if we have 1780 // Free list allocation failed and there is no next page. Fail if we have
2402 // hit the old generation size limit that should cause a garbage 1781 // hit the old generation size limit that should cause a garbage
2403 // collection. 1782 // collection.
2404 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { 1783 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
2405 return NULL; 1784 return NULL;
2406 } 1785 }
2407 1786
2408 // Try to expand the space and allocate in the new next page. 1787 // Try to expand the space and allocate in the new next page.
2409 ASSERT(!current_page->next_page()->is_valid()); 1788 ASSERT(!current_page->next_page()->is_valid());
2410 if (Expand(current_page)) { 1789 if (Expand()) {
2411 return AllocateInNextPage(current_page, size_in_bytes); 1790 return AllocateInNextPage(current_page, size_in_bytes);
2412 } 1791 }
2413 1792
2414 // Finally, fail. 1793 // Finally, fail.
2415 return NULL; 1794 return NULL;
2416 } 1795 }
2417 1796
2418 1797
2419 void OldSpace::PutRestOfCurrentPageOnFreeList(Page* current_page) { 1798 void OldSpace::PutRestOfCurrentPageOnFreeList(Page* current_page) {
2420 current_page->SetAllocationWatermark(allocation_info_.top); 1799 current_page->SetAllocationWatermark(allocation_info_.top);
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
2611 } 1990 }
2612 #endif 1991 #endif
2613 1992
2614 // ----------------------------------------------------------------------------- 1993 // -----------------------------------------------------------------------------
2615 // FixedSpace implementation 1994 // FixedSpace implementation
2616 1995
2617 void FixedSpace::PrepareForMarkCompact(bool will_compact) { 1996 void FixedSpace::PrepareForMarkCompact(bool will_compact) {
2618 // Call prepare of the super class. 1997 // Call prepare of the super class.
2619 PagedSpace::PrepareForMarkCompact(will_compact); 1998 PagedSpace::PrepareForMarkCompact(will_compact);
2620 1999
2621 if (will_compact) { 2000 ASSERT(!will_compact);
2622 // Reset relocation info.
2623 MCResetRelocationInfo();
2624 2001
2625 // During a compacting collection, everything in the space is considered 2002 // During a non-compacting collection, everything below the linear
2626 // 'available' (set by the call to MCResetRelocationInfo) and we will 2003 // allocation pointer except wasted top-of-page blocks is considered
2627 // rediscover live and wasted bytes during the collection. 2004 // allocated and we will rediscover available bytes during the
2628 ASSERT(Available() == Capacity()); 2005 // collection.
2629 } else { 2006 accounting_stats_.AllocateBytes(free_list_.available());
2630 // During a non-compacting collection, everything below the linear
2631 // allocation pointer except wasted top-of-page blocks is considered
2632 // allocated and we will rediscover available bytes during the
2633 // collection.
2634 accounting_stats_.AllocateBytes(free_list_.available());
2635 }
2636 2007
2637 // Clear the free list before a full GC---it will be rebuilt afterward. 2008 // Clear the free list before a full GC---it will be rebuilt afterward.
2638 free_list_.Reset(); 2009 free_list_.Reset();
2639 } 2010 }
2640 2011
2641 2012
2642 void FixedSpace::MCCommitRelocationInfo() {
2643 // Update fast allocation info.
2644 allocation_info_.top = mc_forwarding_info_.top;
2645 allocation_info_.limit = mc_forwarding_info_.limit;
2646 ASSERT(allocation_info_.VerifyPagedAllocation());
2647
2648 // The space is compacted and we haven't yet wasted any space.
2649 ASSERT(Waste() == 0);
2650
2651 // Update allocation_top of each page in use and compute waste.
2652 int computed_size = 0;
2653 PageIterator it(this, PageIterator::PAGES_USED_BY_MC);
2654 while (it.has_next()) {
2655 Page* page = it.next();
2656 Address page_top = page->AllocationTop();
2657 computed_size += static_cast<int>(page_top - page->ObjectAreaStart());
2658 if (it.has_next()) {
2659 accounting_stats_.WasteBytes(
2660 static_cast<int>(page->ObjectAreaEnd() - page_top));
2661 page->SetAllocationWatermark(page_top);
2662 }
2663 }
2664
2665 // Make sure the computed size - based on the used portion of the
2666 // pages in use - matches the size we adjust during allocation.
2667 ASSERT(computed_size == Size());
2668 }
2669
2670
2671 // Slow case for normal allocation. Try in order: (1) allocate in the next 2013 // Slow case for normal allocation. Try in order: (1) allocate in the next
2672 // page in the space, (2) allocate off the space's free list, (3) expand the 2014 // page in the space, (2) allocate off the space's free list, (3) expand the
2673 // space, (4) fail. 2015 // space, (4) fail.
2674 HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) { 2016 HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) {
2675 ASSERT_EQ(object_size_in_bytes_, size_in_bytes); 2017 ASSERT_EQ(object_size_in_bytes_, size_in_bytes);
2676 // Linear allocation in this space has failed. If there is another page 2018 // Linear allocation in this space has failed. If there is another page
2677 // in the space, move to that page and allocate there. This allocation 2019 // in the space, move to that page and allocate there. This allocation
2678 // should succeed. 2020 // should succeed.
2679 Page* current_page = TopPageOf(allocation_info_); 2021 Page* current_page = TopPageOf(allocation_info_);
2680 if (current_page->next_page()->is_valid()) { 2022 if (current_page->next_page()->is_valid()) {
(...skipping 26 matching lines...) Expand all
2707 2049
2708 // Free list allocation failed and there is no next page. Fail if we have 2050 // Free list allocation failed and there is no next page. Fail if we have
2709 // hit the old generation size limit that should cause a garbage 2051 // hit the old generation size limit that should cause a garbage
2710 // collection. 2052 // collection.
2711 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { 2053 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
2712 return NULL; 2054 return NULL;
2713 } 2055 }
2714 2056
2715 // Try to expand the space and allocate in the new next page. 2057 // Try to expand the space and allocate in the new next page.
2716 ASSERT(!current_page->next_page()->is_valid()); 2058 ASSERT(!current_page->next_page()->is_valid());
2717 if (Expand(current_page)) { 2059 if (Expand()) {
2718 return AllocateInNextPage(current_page, size_in_bytes); 2060 return AllocateInNextPage(current_page, size_in_bytes);
2719 } 2061 }
2720 2062
2721 // Finally, fail. 2063 // Finally, fail.
2722 return NULL; 2064 return NULL;
2723 } 2065 }
2724 2066
2725 2067
2726 // Move to the next page (there is assumed to be one) and allocate there. 2068 // Move to the next page (there is assumed to be one) and allocate there.
2727 // The top of page block is always wasted, because it is too small to hold a 2069 // The top of page block is always wasted, because it is too small to hold a
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
2798 ASSERT(object->IsJSGlobalPropertyCell() || 2140 ASSERT(object->IsJSGlobalPropertyCell() ||
2799 object->map() == Heap::two_pointer_filler_map()); 2141 object->map() == Heap::two_pointer_filler_map());
2800 } 2142 }
2801 #endif 2143 #endif
2802 2144
2803 2145
2804 // ----------------------------------------------------------------------------- 2146 // -----------------------------------------------------------------------------
2805 // LargeObjectIterator 2147 // LargeObjectIterator
2806 2148
2807 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) { 2149 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) {
2808 current_ = space->first_chunk_; 2150 current_ = space->first_page_;
2809 size_func_ = NULL; 2151 size_func_ = NULL;
2810 } 2152 }
2811 2153
2812 2154
2813 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space, 2155 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space,
2814 HeapObjectCallback size_func) { 2156 HeapObjectCallback size_func) {
2815 current_ = space->first_chunk_; 2157 current_ = space->first_page_;
2816 size_func_ = size_func; 2158 size_func_ = size_func;
2817 } 2159 }
2818 2160
2819 2161
2820 HeapObject* LargeObjectIterator::next() { 2162 HeapObject* LargeObjectIterator::next() {
2821 if (current_ == NULL) return NULL; 2163 if (current_ == NULL) return NULL;
2822 2164
2823 HeapObject* object = current_->GetObject(); 2165 HeapObject* object = current_->GetObject();
2824 current_ = current_->next(); 2166 current_ = current_->next_page();
2825 return object; 2167 return object;
2826 } 2168 }
2827 2169
2828 2170
2829 // ----------------------------------------------------------------------------- 2171 // -----------------------------------------------------------------------------
2830 // LargeObjectChunk
2831
2832 LargeObjectChunk* LargeObjectChunk::New(int size_in_bytes,
2833 Executability executable) {
2834 size_t requested = ChunkSizeFor(size_in_bytes);
2835 size_t size;
2836 void* mem = MemoryAllocator::AllocateRawMemory(requested, &size, executable);
2837 if (mem == NULL) return NULL;
2838
2839 // The start of the chunk may be overlayed with a page so we have to
2840 // make sure that the page flags fit in the size field.
2841 ASSERT((size & Page::kPageFlagMask) == 0);
2842
2843 LOG(NewEvent("LargeObjectChunk", mem, size));
2844 if (size < requested) {
2845 MemoryAllocator::FreeRawMemory(mem, size, executable);
2846 LOG(DeleteEvent("LargeObjectChunk", mem));
2847 return NULL;
2848 }
2849
2850 ObjectSpace space = (executable == EXECUTABLE)
2851 ? kObjectSpaceCodeSpace
2852 : kObjectSpaceLoSpace;
2853 MemoryAllocator::PerformAllocationCallback(
2854 space, kAllocationActionAllocate, size);
2855
2856 LargeObjectChunk* chunk = reinterpret_cast<LargeObjectChunk*>(mem);
2857 chunk->size_ = size;
2858 return chunk;
2859 }
2860
2861
2862 int LargeObjectChunk::ChunkSizeFor(int size_in_bytes) {
2863 int os_alignment = static_cast<int>(OS::AllocateAlignment());
2864 if (os_alignment < Page::kPageSize) {
2865 size_in_bytes += (Page::kPageSize - os_alignment);
2866 }
2867 return size_in_bytes + Page::kObjectStartOffset;
2868 }
2869
2870 // -----------------------------------------------------------------------------
2871 // LargeObjectSpace 2172 // LargeObjectSpace
2872 2173
2873 LargeObjectSpace::LargeObjectSpace(AllocationSpace id) 2174 LargeObjectSpace::LargeObjectSpace(AllocationSpace id)
2874 : Space(id, NOT_EXECUTABLE), // Managed on a per-allocation basis 2175 : Space(id, NOT_EXECUTABLE), // Managed on a per-allocation basis
2875 first_chunk_(NULL), 2176 first_page_(NULL),
2876 size_(0), 2177 size_(0),
2877 page_count_(0), 2178 page_count_(0),
2878 objects_size_(0) {} 2179 objects_size_(0) {}
2879 2180
2880 2181
2881 bool LargeObjectSpace::Setup() { 2182 bool LargeObjectSpace::Setup() {
2882 first_chunk_ = NULL; 2183 first_page_ = NULL;
2883 size_ = 0; 2184 size_ = 0;
2884 page_count_ = 0; 2185 page_count_ = 0;
2885 objects_size_ = 0; 2186 objects_size_ = 0;
2886 return true; 2187 return true;
2887 } 2188 }
2888 2189
2889 2190
2890 void LargeObjectSpace::TearDown() { 2191 void LargeObjectSpace::TearDown() {
2891 while (first_chunk_ != NULL) { 2192 while (first_page_ != NULL) {
2892 LargeObjectChunk* chunk = first_chunk_; 2193 LargePage* page = first_page_;
2893 first_chunk_ = first_chunk_->next(); 2194 first_page_ = first_page_->next_page();
2894 LOG(DeleteEvent("LargeObjectChunk", chunk->address())); 2195
2895 Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize)); 2196 MemoryAllocator::Free(page);
2896 Executability executable =
2897 page->IsPageExecutable() ? EXECUTABLE : NOT_EXECUTABLE;
2898 ObjectSpace space = kObjectSpaceLoSpace;
2899 if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace;
2900 size_t size = chunk->size();
2901 MemoryAllocator::FreeRawMemory(chunk->address(), size, executable);
2902 MemoryAllocator::PerformAllocationCallback(
2903 space, kAllocationActionFree, size);
2904 } 2197 }
2905 2198
2906 size_ = 0; 2199 size_ = 0;
2907 page_count_ = 0; 2200 page_count_ = 0;
2908 objects_size_ = 0; 2201 objects_size_ = 0;
2909 } 2202 }
2910 2203
2911 2204
2912 #ifdef ENABLE_HEAP_PROTECTION 2205 #ifdef ENABLE_HEAP_PROTECTION
2913 2206
(...skipping 11 matching lines...) Expand all
2925 while (chunk != NULL) { 2218 while (chunk != NULL) {
2926 bool is_code = chunk->GetObject()->IsCode(); 2219 bool is_code = chunk->GetObject()->IsCode();
2927 MemoryAllocator::Unprotect(chunk->address(), chunk->size(), 2220 MemoryAllocator::Unprotect(chunk->address(), chunk->size(),
2928 is_code ? EXECUTABLE : NOT_EXECUTABLE); 2221 is_code ? EXECUTABLE : NOT_EXECUTABLE);
2929 chunk = chunk->next(); 2222 chunk = chunk->next();
2930 } 2223 }
2931 } 2224 }
2932 2225
2933 #endif 2226 #endif
2934 2227
2935 2228 MaybeObject* LargeObjectSpace::AllocateRawInternal(int object_size,
2936 MaybeObject* LargeObjectSpace::AllocateRawInternal(int requested_size,
2937 int object_size,
2938 Executability executable) { 2229 Executability executable) {
2939 ASSERT(0 < object_size && object_size <= requested_size);
2940
2941 // Check if we want to force a GC before growing the old space further. 2230 // Check if we want to force a GC before growing the old space further.
2942 // If so, fail the allocation. 2231 // If so, fail the allocation.
2943 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { 2232 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
2944 return Failure::RetryAfterGC(identity()); 2233 return Failure::RetryAfterGC(identity());
2945 } 2234 }
2946 2235
2947 LargeObjectChunk* chunk = LargeObjectChunk::New(requested_size, executable); 2236 LargePage* page = MemoryAllocator::AllocateLargePage(object_size,
2948 if (chunk == NULL) { 2237 executable,
2949 return Failure::RetryAfterGC(identity()); 2238 this);
2950 } 2239 if (page == NULL) return Failure::RetryAfterGC(identity());
2240 ASSERT(page->body_size() >= object_size);
2951 2241
2952 size_ += static_cast<int>(chunk->size()); 2242 size_ += static_cast<int>(page->size());
2953 objects_size_ += requested_size; 2243 objects_size_ += object_size;
2954 page_count_++; 2244 page_count_++;
2955 chunk->set_next(first_chunk_); 2245 page->set_next_page(first_page_);
2956 first_chunk_ = chunk; 2246 first_page_ = page;
2957 2247
2958 // Initialize page header. 2248 return page->GetObject();
2959 Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize));
2960 Address object_address = page->ObjectAreaStart();
2961
2962 // Clear the low order bit of the second word in the page to flag it as a
2963 // large object page. If the chunk_size happened to be written there, its
2964 // low order bit should already be clear.
2965 page->SetIsLargeObjectPage(true);
2966 page->SetIsPageExecutable(executable);
2967 page->SetRegionMarks(Page::kAllRegionsCleanMarks);
2968 return HeapObject::FromAddress(object_address);
2969 } 2249 }
2970 2250
2971 2251
2972 MaybeObject* LargeObjectSpace::AllocateRawCode(int size_in_bytes) { 2252 MaybeObject* LargeObjectSpace::AllocateRawCode(int size_in_bytes) {
2973 ASSERT(0 < size_in_bytes); 2253 ASSERT(0 < size_in_bytes);
2974 return AllocateRawInternal(size_in_bytes, 2254 return AllocateRawInternal(size_in_bytes, EXECUTABLE);
2975 size_in_bytes,
2976 EXECUTABLE);
2977 } 2255 }
2978 2256
2979 2257
2980 MaybeObject* LargeObjectSpace::AllocateRawFixedArray(int size_in_bytes) { 2258 MaybeObject* LargeObjectSpace::AllocateRawFixedArray(int size_in_bytes) {
2981 ASSERT(0 < size_in_bytes); 2259 ASSERT(0 < size_in_bytes);
2982 return AllocateRawInternal(size_in_bytes, 2260 return AllocateRawInternal(size_in_bytes, NOT_EXECUTABLE);
2983 size_in_bytes,
2984 NOT_EXECUTABLE);
2985 } 2261 }
2986 2262
2987 2263
2988 MaybeObject* LargeObjectSpace::AllocateRaw(int size_in_bytes) { 2264 MaybeObject* LargeObjectSpace::AllocateRaw(int size_in_bytes) {
2989 ASSERT(0 < size_in_bytes); 2265 ASSERT(0 < size_in_bytes);
2990 return AllocateRawInternal(size_in_bytes, 2266 return AllocateRawInternal(size_in_bytes, NOT_EXECUTABLE);
2991 size_in_bytes,
2992 NOT_EXECUTABLE);
2993 } 2267 }
2994 2268
2995 2269
2996 // GC support 2270 // GC support
2997 MaybeObject* LargeObjectSpace::FindObject(Address a) { 2271 MaybeObject* LargeObjectSpace::FindObject(Address a) {
2998 for (LargeObjectChunk* chunk = first_chunk_; 2272 for (LargePage* page = first_page_;
2999 chunk != NULL; 2273 page != NULL;
3000 chunk = chunk->next()) { 2274 page = page->next_page()) {
3001 Address chunk_address = chunk->address(); 2275 Address page_address = page->address();
3002 if (chunk_address <= a && a < chunk_address + chunk->size()) { 2276 if (page_address <= a && a < page_address + page->size()) {
3003 return chunk->GetObject(); 2277 return page->GetObject();
3004 } 2278 }
3005 } 2279 }
3006 return Failure::Exception(); 2280 return Failure::Exception();
3007 } 2281 }
3008 2282
3009 2283
3010 LargeObjectChunk* LargeObjectSpace::FindChunkContainingPc(Address pc) { 2284 LargePage* LargeObjectSpace::FindPageContainingPc(Address pc) {
3011 // TODO(853): Change this implementation to only find executable 2285 // TODO(853): Change this implementation to only find executable
3012 // chunks and use some kind of hash-based approach to speed it up. 2286 // chunks and use some kind of hash-based approach to speed it up.
3013 for (LargeObjectChunk* chunk = first_chunk_; 2287 for (LargePage* chunk = first_page_;
3014 chunk != NULL; 2288 chunk != NULL;
3015 chunk = chunk->next()) { 2289 chunk = chunk->next_page()) {
3016 Address chunk_address = chunk->address(); 2290 Address chunk_address = chunk->address();
3017 if (chunk_address <= pc && pc < chunk_address + chunk->size()) { 2291 if (chunk_address <= pc && pc < chunk_address + chunk->size()) {
3018 return chunk; 2292 return chunk;
3019 } 2293 }
3020 } 2294 }
3021 return NULL; 2295 return NULL;
3022 } 2296 }
3023 2297
3024 2298
3025 void LargeObjectSpace::IterateDirtyRegions(ObjectSlotCallback copy_object) { 2299 void LargeObjectSpace::IterateDirtyRegions(ObjectSlotCallback copy_object) {
3026 LargeObjectIterator it(this); 2300 LargeObjectIterator it(this);
3027 for (HeapObject* object = it.next(); object != NULL; object = it.next()) { 2301 for (HeapObject* object = it.next(); object != NULL; object = it.next()) {
3028 // We only have code, sequential strings, or fixed arrays in large 2302 // We only have code, sequential strings, or fixed arrays in large
3029 // object space, and only fixed arrays can possibly contain pointers to 2303 // object space, and only fixed arrays can possibly contain pointers to
3030 // the young generation. 2304 // the young generation.
3031 if (object->IsFixedArray()) { 2305 if (object->IsFixedArray()) {
3032 Page* page = Page::FromAddress(object->address()); 2306 Page* page = Page::FromAddress(object->address());
3033 uint32_t marks = page->GetRegionMarks(); 2307 uint32_t marks = page->GetRegionMarks();
3034 uint32_t newmarks = Page::kAllRegionsCleanMarks;
3035 2308
3036 if (marks != Page::kAllRegionsCleanMarks) { 2309 // TODO(gc): we can no longer assume that LargePage is bigger than normal
3037 // For a large page a single dirty mark corresponds to several 2310 // page.
3038 // regions (modulo 32). So we treat a large page as a sequence of 2311 ASSERT(marks == Page::kAllRegionsDirtyMarks);
3039 // normal pages of size Page::kPageSize having same dirty marks 2312 USE(marks);
3040 // and subsequently iterate dirty regions on each of these pages.
3041 Address start = object->address();
3042 Address end = page->ObjectAreaEnd();
3043 Address object_end = start + object->Size();
3044 2313
3045 // Iterate regions of the first normal page covering object. 2314 Address start = object->address();
3046 uint32_t first_region_number = page->GetRegionNumberForAddress(start); 2315 Address object_end = start + object->Size();
3047 newmarks |= 2316 Heap::IteratePointersInDirtyRegion(start, object_end, copy_object);
3048 Heap::IterateDirtyRegions(marks >> first_region_number,
3049 start,
3050 end,
3051 &Heap::IteratePointersInDirtyRegion,
3052 copy_object) << first_region_number;
3053
3054 start = end;
3055 end = start + Page::kPageSize;
3056 while (end <= object_end) {
3057 // Iterate next 32 regions.
3058 newmarks |=
3059 Heap::IterateDirtyRegions(marks,
3060 start,
3061 end,
3062 &Heap::IteratePointersInDirtyRegion,
3063 copy_object);
3064 start = end;
3065 end = start + Page::kPageSize;
3066 }
3067
3068 if (start != object_end) {
3069 // Iterate the last piece of an object which is less than
3070 // Page::kPageSize.
3071 newmarks |=
3072 Heap::IterateDirtyRegions(marks,
3073 start,
3074 object_end,
3075 &Heap::IteratePointersInDirtyRegion,
3076 copy_object);
3077 }
3078
3079 page->SetRegionMarks(newmarks);
3080 }
3081 } 2317 }
3082 } 2318 }
3083 } 2319 }
3084 2320
3085 2321
3086 void LargeObjectSpace::FreeUnmarkedObjects() { 2322 void LargeObjectSpace::FreeUnmarkedObjects() {
3087 LargeObjectChunk* previous = NULL; 2323 LargePage* previous = NULL;
3088 LargeObjectChunk* current = first_chunk_; 2324 LargePage* current = first_page_;
3089 while (current != NULL) { 2325 while (current != NULL) {
3090 HeapObject* object = current->GetObject(); 2326 HeapObject* object = current->GetObject();
3091 if (object->IsMarked()) { 2327 if (object->IsMarked()) {
3092 object->ClearMark(); 2328 object->ClearMark();
3093 MarkCompactCollector::tracer()->decrement_marked_count(); 2329 MarkCompactCollector::tracer()->decrement_marked_count();
3094 previous = current; 2330 previous = current;
3095 current = current->next(); 2331 current = current->next_page();
3096 } else { 2332 } else {
3097 Page* page = Page::FromAddress(RoundUp(current->address(), 2333 LargePage* page = current;
3098 Page::kPageSize));
3099 Executability executable =
3100 page->IsPageExecutable() ? EXECUTABLE : NOT_EXECUTABLE;
3101 Address chunk_address = current->address();
3102 size_t chunk_size = current->size();
3103
3104 // Cut the chunk out from the chunk list. 2334 // Cut the chunk out from the chunk list.
3105 current = current->next(); 2335 current = current->next_page();
3106 if (previous == NULL) { 2336 if (previous == NULL) {
3107 first_chunk_ = current; 2337 first_page_ = current;
3108 } else { 2338 } else {
3109 previous->set_next(current); 2339 previous->set_next_page(current);
3110 } 2340 }
3111 2341
3112 // Free the chunk. 2342 // Free the chunk.
3113 MarkCompactCollector::ReportDeleteIfNeeded(object); 2343 MarkCompactCollector::ReportDeleteIfNeeded(object);
3114 size_ -= static_cast<int>(chunk_size); 2344 size_ -= static_cast<int>(page->size());
3115 objects_size_ -= object->Size(); 2345 objects_size_ -= object->Size();
3116 page_count_--; 2346 page_count_--;
3117 ObjectSpace space = kObjectSpaceLoSpace; 2347
3118 if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace; 2348 MemoryAllocator::Free(page);
3119 MemoryAllocator::FreeRawMemory(chunk_address, chunk_size, executable);
3120 MemoryAllocator::PerformAllocationCallback(space, kAllocationActionFree,
3121 size_);
3122 LOG(DeleteEvent("LargeObjectChunk", chunk_address));
3123 } 2349 }
3124 } 2350 }
3125 } 2351 }
3126 2352
3127 2353
3128 bool LargeObjectSpace::Contains(HeapObject* object) { 2354 bool LargeObjectSpace::Contains(HeapObject* object) {
3129 Address address = object->address(); 2355 Address address = object->address();
3130 if (Heap::new_space()->Contains(address)) { 2356 if (Heap::new_space()->Contains(address)) {
3131 return false; 2357 return false;
3132 } 2358 }
3133 Page* page = Page::FromAddress(address); 2359 MemoryChunk* chunk = MemoryChunk::FromAddress(address);
3134 2360
3135 SLOW_ASSERT(!page->IsLargeObjectPage() 2361 bool owned = chunk->owner() == this;
2362
2363 SLOW_ASSERT(!owned
3136 || !FindObject(address)->IsFailure()); 2364 || !FindObject(address)->IsFailure());
3137 2365
3138 return page->IsLargeObjectPage(); 2366 return owned;
3139 } 2367 }
3140 2368
3141 2369
3142 #ifdef DEBUG 2370 #ifdef DEBUG
3143 // We do not assume that the large object iterator works, because it depends 2371 // We do not assume that the large object iterator works, because it depends
3144 // on the invariants we are checking during verification. 2372 // on the invariants we are checking during verification.
3145 void LargeObjectSpace::Verify() { 2373 void LargeObjectSpace::Verify() {
3146 for (LargeObjectChunk* chunk = first_chunk_; 2374 for (LargePage* chunk = first_page_;
3147 chunk != NULL; 2375 chunk != NULL;
3148 chunk = chunk->next()) { 2376 chunk = chunk->next_page()) {
3149 // Each chunk contains an object that starts at the large object page's 2377 // Each chunk contains an object that starts at the large object page's
3150 // object area start. 2378 // object area start.
3151 HeapObject* object = chunk->GetObject(); 2379 HeapObject* object = chunk->GetObject();
3152 Page* page = Page::FromAddress(object->address()); 2380 Page* page = Page::FromAddress(object->address());
3153 ASSERT(object->address() == page->ObjectAreaStart()); 2381 ASSERT(object->address() == page->ObjectAreaStart());
3154 2382
3155 // The first word should be a map, and we expect all map pointers to be 2383 // The first word should be a map, and we expect all map pointers to be
3156 // in map space. 2384 // in map space.
3157 Map* map = object->map(); 2385 Map* map = object->map();
3158 ASSERT(map->IsMap()); 2386 ASSERT(map->IsMap());
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
3228 for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) { 2456 for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) {
3229 if (obj->IsCode()) { 2457 if (obj->IsCode()) {
3230 Code* code = Code::cast(obj); 2458 Code* code = Code::cast(obj);
3231 code_kind_statistics[code->kind()] += code->Size(); 2459 code_kind_statistics[code->kind()] += code->Size();
3232 } 2460 }
3233 } 2461 }
3234 } 2462 }
3235 #endif // DEBUG 2463 #endif // DEBUG
3236 2464
3237 } } // namespace v8::internal 2465 } } // namespace v8::internal
OLDNEW
« src/spaces.h ('K') | « src/spaces.h ('k') | src/spaces-inl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698