1// Copyright 2011 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
30#include "liveobjectlist-inl.h"
31#include "macro-assembler.h"
32#include "mark-compact.h"
33#include "platform.h"
34
35namespace v8 {
36namespace internal {
37
38
39// ----------------------------------------------------------------------------
40// HeapObjectIterator
41
42HeapObjectIterator::HeapObjectIterator(PagedSpace* space) {
43  // You can't actually iterate over the anchor page.  It is not a real page,
44  // just an anchor for the double linked page list.  Initialize as if we have
45  // reached the end of the anchor page, then the first iteration will move on
46  // to the first page.
47  Initialize(space,
48             NULL,
49             NULL,
50             kAllPagesInSpace,
51             NULL);
52}
53
54
55HeapObjectIterator::HeapObjectIterator(PagedSpace* space,
56                                       HeapObjectCallback size_func) {
57  // You can't actually iterate over the anchor page.  It is not a real page,
58  // just an anchor for the double linked page list.  Initialize the current
59  // address and end as NULL, then the first iteration will move on
60  // to the first page.
61  Initialize(space,
62             NULL,
63             NULL,
64             kAllPagesInSpace,
65             size_func);
66}
67
68
69HeapObjectIterator::HeapObjectIterator(Page* page,
70                                       HeapObjectCallback size_func) {
71  Space* owner = page->owner();
72  ASSERT(owner == HEAP->old_pointer_space() ||
73         owner == HEAP->old_data_space() ||
74         owner == HEAP->map_space() ||
75         owner == HEAP->cell_space() ||
76         owner == HEAP->code_space());
77  Initialize(reinterpret_cast<PagedSpace*>(owner),
78             page->area_start(),
79             page->area_end(),
80             kOnePageOnly,
81             size_func);
82  ASSERT(page->WasSweptPrecisely());
83}
84
85
86void HeapObjectIterator::Initialize(PagedSpace* space,
87                                    Address cur, Address end,
88                                    HeapObjectIterator::PageMode mode,
89                                    HeapObjectCallback size_f) {
90  // Check that we actually can iterate this space.
91  ASSERT(!space->was_swept_conservatively());
92
93  space_ = space;
94  cur_addr_ = cur;
95  cur_end_ = end;
96  page_mode_ = mode;
97  size_func_ = size_f;
98}
99
100
101// We have hit the end of the page and should advance to the next block of
102// objects.  This happens at the end of the page.
103bool HeapObjectIterator::AdvanceToNextPage() {
104  ASSERT(cur_addr_ == cur_end_);
105  if (page_mode_ == kOnePageOnly) return false;
106  Page* cur_page;
107  if (cur_addr_ == NULL) {
108    cur_page = space_->anchor();
109  } else {
110    cur_page = Page::FromAddress(cur_addr_ - 1);
111    ASSERT(cur_addr_ == cur_page->area_end());
112  }
113  cur_page = cur_page->next_page();
114  if (cur_page == space_->anchor()) return false;
115  cur_addr_ = cur_page->area_start();
116  cur_end_ = cur_page->area_end();
117  ASSERT(cur_page->WasSweptPrecisely());
118  return true;
119}
120
121
122// -----------------------------------------------------------------------------
123// CodeRange
124
125
126CodeRange::CodeRange(Isolate* isolate)
127    : isolate_(isolate),
128      code_range_(NULL),
129      free_list_(0),
130      allocation_list_(0),
131      current_allocation_block_index_(0) {
132}
133
134
135bool CodeRange::SetUp(const size_t requested) {
136  ASSERT(code_range_ == NULL);
137
138  code_range_ = new VirtualMemory(requested);
139  CHECK(code_range_ != NULL);
140  if (!code_range_->IsReserved()) {
141    delete code_range_;
142    code_range_ = NULL;
143    return false;
144  }
145
146  // We are sure that we have mapped a block of requested addresses.
147  ASSERT(code_range_->size() == requested);
148  LOG(isolate_, NewEvent("CodeRange", code_range_->address(), requested));
149  Address base = reinterpret_cast<Address>(code_range_->address());
150  Address aligned_base =
151      RoundUp(reinterpret_cast<Address>(code_range_->address()),
152              MemoryChunk::kAlignment);
153  size_t size = code_range_->size() - (aligned_base - base);
154  allocation_list_.Add(FreeBlock(aligned_base, size));
155  current_allocation_block_index_ = 0;
156  return true;
157}
158
159
160int CodeRange::CompareFreeBlockAddress(const FreeBlock* left,
161                                       const FreeBlock* right) {
162  // The entire point of CodeRange is that the difference between two
163  // addresses in the range can be represented as a signed 32-bit int,
164  // so the cast is semantically correct.
165  return static_cast<int>(left->start - right->start);
166}
167
168
169void CodeRange::GetNextAllocationBlock(size_t requested) {
170  for (current_allocation_block_index_++;
171       current_allocation_block_index_ < allocation_list_.length();
172       current_allocation_block_index_++) {
173    if (requested <= allocation_list_[current_allocation_block_index_].size) {
174      return;  // Found a large enough allocation block.
175    }
176  }
177
178  // Sort and merge the free blocks on the free list and the allocation list.
179  free_list_.AddAll(allocation_list_);
180  allocation_list_.Clear();
181  free_list_.Sort(&CompareFreeBlockAddress);
182  for (int i = 0; i < free_list_.length();) {
183    FreeBlock merged = free_list_[i];
184    i++;
185    // Add adjacent free blocks to the current merged block.
186    while (i < free_list_.length() &&
187           free_list_[i].start == merged.start + merged.size) {
188      merged.size += free_list_[i].size;
189      i++;
190    }
191    if (merged.size > 0) {
192      allocation_list_.Add(merged);
193    }
194  }
195  free_list_.Clear();
196
197  for (current_allocation_block_index_ = 0;
198       current_allocation_block_index_ < allocation_list_.length();
199       current_allocation_block_index_++) {
200    if (requested <= allocation_list_[current_allocation_block_index_].size) {
201      return;  // Found a large enough allocation block.
202    }
203  }
204
205  // Code range is full or too fragmented.
206  V8::FatalProcessOutOfMemory("CodeRange::GetNextAllocationBlock");
207}
208
209
210
211Address CodeRange::AllocateRawMemory(const size_t requested,
212                                     size_t* allocated) {
213  ASSERT(current_allocation_block_index_ < allocation_list_.length());
214  if (requested > allocation_list_[current_allocation_block_index_].size) {
215    // Find an allocation block large enough.  This function call may
216    // call V8::FatalProcessOutOfMemory if it cannot find a large enough block.
217    GetNextAllocationBlock(requested);
218  }
219  // Commit the requested memory at the start of the current allocation block.
220  size_t aligned_requested = RoundUp(requested, MemoryChunk::kAlignment);
221  FreeBlock current = allocation_list_[current_allocation_block_index_];
222  if (aligned_requested >= (current.size - Page::kPageSize)) {
223    // Don't leave a small free block, useless for a large object or chunk.
224    *allocated = current.size;
225  } else {
226    *allocated = aligned_requested;
227  }
228  ASSERT(*allocated <= current.size);
229  ASSERT(IsAddressAligned(current.start, MemoryChunk::kAlignment));
230  if (!MemoryAllocator::CommitCodePage(code_range_,
231                                       current.start,
232                                       *allocated)) {
233    *allocated = 0;
234    return NULL;
235  }
236  allocation_list_[current_allocation_block_index_].start += *allocated;
237  allocation_list_[current_allocation_block_index_].size -= *allocated;
238  if (*allocated == current.size) {
239    GetNextAllocationBlock(0);  // This block is used up, get the next one.
240  }
241  return current.start;
242}
243
244
245void CodeRange::FreeRawMemory(Address address, size_t length) {
246  ASSERT(IsAddressAligned(address, MemoryChunk::kAlignment));
247  free_list_.Add(FreeBlock(address, length));
248  code_range_->Uncommit(address, length);
249}
250
251
252void CodeRange::TearDown() {
253    delete code_range_;  // Frees all memory in the virtual memory range.
254    code_range_ = NULL;
255    free_list_.Free();
256    allocation_list_.Free();
257}
258
259
260// -----------------------------------------------------------------------------
261// MemoryAllocator
262//
263
264MemoryAllocator::MemoryAllocator(Isolate* isolate)
265    : isolate_(isolate),
266      capacity_(0),
267      capacity_executable_(0),
268      size_(0),
269      size_executable_(0) {
270}
271
272
273bool MemoryAllocator::SetUp(intptr_t capacity, intptr_t capacity_executable) {
274  capacity_ = RoundUp(capacity, Page::kPageSize);
275  capacity_executable_ = RoundUp(capacity_executable, Page::kPageSize);
276  ASSERT_GE(capacity_, capacity_executable_);
277
278  size_ = 0;
279  size_executable_ = 0;
280
281  return true;
282}
283
284
285void MemoryAllocator::TearDown() {
286  // Check that spaces were torn down before MemoryAllocator.
287  ASSERT(size_ == 0);
288  // TODO(gc) this will be true again when we fix FreeMemory.
289  // ASSERT(size_executable_ == 0);
290  capacity_ = 0;
291  capacity_executable_ = 0;
292}
293
294
295void MemoryAllocator::FreeMemory(VirtualMemory* reservation,
296                                 Executability executable) {
297  // TODO(gc) make code_range part of memory allocator?
298  ASSERT(reservation->IsReserved());
299  size_t size = reservation->size();
300  ASSERT(size_ >= size);
301  size_ -= size;
302
303  isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size));
304
305  if (executable == EXECUTABLE) {
306    ASSERT(size_executable_ >= size);
307    size_executable_ -= size;
308  }
309  // Code which is part of the code-range does not have its own VirtualMemory.
310  ASSERT(!isolate_->code_range()->contains(
311      static_cast<Address>(reservation->address())));
312  ASSERT(executable == NOT_EXECUTABLE || !isolate_->code_range()->exists());
313  reservation->Release();
314}
315
316
317void MemoryAllocator::FreeMemory(Address base,
318                                 size_t size,
319                                 Executability executable) {
320  // TODO(gc) make code_range part of memory allocator?
321  ASSERT(size_ >= size);
322  size_ -= size;
323
324  isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size));
325
326  if (executable == EXECUTABLE) {
327    ASSERT(size_executable_ >= size);
328    size_executable_ -= size;
329  }
330  if (isolate_->code_range()->contains(static_cast<Address>(base))) {
331    ASSERT(executable == EXECUTABLE);
332    isolate_->code_range()->FreeRawMemory(base, size);
333  } else {
334    ASSERT(executable == NOT_EXECUTABLE || !isolate_->code_range()->exists());
335    bool result = VirtualMemory::ReleaseRegion(base, size);
336    USE(result);
337    ASSERT(result);
338  }
339}
340
341
342Address MemoryAllocator::ReserveAlignedMemory(size_t size,
343                                              size_t alignment,
344                                              VirtualMemory* controller) {
345  VirtualMemory reservation(size, alignment);
346
347  if (!reservation.IsReserved()) return NULL;
348  size_ += reservation.size();
349  Address base = RoundUp(static_cast<Address>(reservation.address()),
350                         alignment);
351  controller->TakeControl(&reservation);
352  return base;
353}
354
355
356Address MemoryAllocator::AllocateAlignedMemory(size_t size,
357                                               size_t alignment,
358                                               Executability executable,
359                                               VirtualMemory* controller) {
360  VirtualMemory reservation;
361  Address base = ReserveAlignedMemory(size, alignment, &reservation);
362  if (base == NULL) return NULL;
363
364  if (executable == EXECUTABLE) {
365    if (!CommitCodePage(&reservation, base, size)) {
366      base = NULL;
367    }
368  } else {
369    if (!reservation.Commit(base, size, false)) {
370      base = NULL;
371    }
372  }
373
374  if (base == NULL) {
375    // Failed to commit the body. Release the mapping and any partially
376    // commited regions inside it.
377    reservation.Release();
378    return NULL;
379  }
380
381  controller->TakeControl(&reservation);
382  return base;
383}
384
385
386void Page::InitializeAsAnchor(PagedSpace* owner) {
387  set_owner(owner);
388  set_prev_page(this);
389  set_next_page(this);
390}
391
392
393NewSpacePage* NewSpacePage::Initialize(Heap* heap,
394                                       Address start,
395                                       SemiSpace* semi_space) {
396  Address area_start = start + NewSpacePage::kObjectStartOffset;
397  Address area_end = start + Page::kPageSize;
398
399  MemoryChunk* chunk = MemoryChunk::Initialize(heap,
400                                               start,
401                                               Page::kPageSize,
402                                               area_start,
403                                               area_end,
404                                               NOT_EXECUTABLE,
405                                               semi_space);
406  chunk->set_next_chunk(NULL);
407  chunk->set_prev_chunk(NULL);
408  chunk->initialize_scan_on_scavenge(true);
409  bool in_to_space = (semi_space->id() != kFromSpace);
410  chunk->SetFlag(in_to_space ? MemoryChunk::IN_TO_SPACE
411                             : MemoryChunk::IN_FROM_SPACE);
412  ASSERT(!chunk->IsFlagSet(in_to_space ? MemoryChunk::IN_FROM_SPACE
413                                       : MemoryChunk::IN_TO_SPACE));
414  NewSpacePage* page = static_cast<NewSpacePage*>(chunk);
415  heap->incremental_marking()->SetNewSpacePageFlags(page);
416  return page;
417}
418
419
420void NewSpacePage::InitializeAsAnchor(SemiSpace* semi_space) {
421  set_owner(semi_space);
422  set_next_chunk(this);
423  set_prev_chunk(this);
424  // Flags marks this invalid page as not being in new-space.
425  // All real new-space pages will be in new-space.
426  SetFlags(0, ~0);
427}
428
429
430MemoryChunk* MemoryChunk::Initialize(Heap* heap,
431                                     Address base,
432                                     size_t size,
433                                     Address area_start,
434                                     Address area_end,
435                                     Executability executable,
436                                     Space* owner) {
437  MemoryChunk* chunk = FromAddress(base);
438
439  ASSERT(base == chunk->address());
440
441  chunk->heap_ = heap;
442  chunk->size_ = size;
443  chunk->area_start_ = area_start;
444  chunk->area_end_ = area_end;
445  chunk->flags_ = 0;
446  chunk->set_owner(owner);
447  chunk->InitializeReservedMemory();
448  chunk->slots_buffer_ = NULL;
449  chunk->skip_list_ = NULL;
450  chunk->ResetLiveBytes();
451  Bitmap::Clear(chunk);
452  chunk->initialize_scan_on_scavenge(false);
453  chunk->SetFlag(WAS_SWEPT_PRECISELY);
454
455  ASSERT(OFFSET_OF(MemoryChunk, flags_) == kFlagsOffset);
456  ASSERT(OFFSET_OF(MemoryChunk, live_byte_count_) == kLiveBytesOffset);
457
458  if (executable == EXECUTABLE) {
459    chunk->SetFlag(IS_EXECUTABLE);
460  }
461
462  if (owner == heap->old_data_space()) {
463    chunk->SetFlag(CONTAINS_ONLY_DATA);
464  }
465
466  return chunk;
467}
468
469
470void MemoryChunk::InsertAfter(MemoryChunk* other) {
471  next_chunk_ = other->next_chunk_;
472  prev_chunk_ = other;
473  other->next_chunk_->prev_chunk_ = this;
474  other->next_chunk_ = this;
475}
476
477
478void MemoryChunk::Unlink() {
479  if (!InNewSpace() && IsFlagSet(SCAN_ON_SCAVENGE)) {
480    heap_->decrement_scan_on_scavenge_pages();
481    ClearFlag(SCAN_ON_SCAVENGE);
482  }
483  next_chunk_->prev_chunk_ = prev_chunk_;
484  prev_chunk_->next_chunk_ = next_chunk_;
485  prev_chunk_ = NULL;
486  next_chunk_ = NULL;
487}
488
489
490MemoryChunk* MemoryAllocator::AllocateChunk(intptr_t body_size,
491                                            Executability executable,
492                                            Space* owner) {
493  size_t chunk_size;
494  Heap* heap = isolate_->heap();
495  Address base = NULL;
496  VirtualMemory reservation;
497  Address area_start = NULL;
498  Address area_end = NULL;
499  if (executable == EXECUTABLE) {
500    chunk_size = RoundUp(CodePageAreaStartOffset() + body_size,
501                         OS::CommitPageSize()) + CodePageGuardSize();
502
503    // Check executable memory limit.
504    if (size_executable_ + chunk_size > capacity_executable_) {
505      LOG(isolate_,
506          StringEvent("MemoryAllocator::AllocateRawMemory",
507                      "V8 Executable Allocation capacity exceeded"));
508      return NULL;
509    }
510
511    // Allocate executable memory either from code range or from the
512    // OS.
513    if (isolate_->code_range()->exists()) {
514      base = isolate_->code_range()->AllocateRawMemory(chunk_size, &chunk_size);
515      ASSERT(IsAligned(reinterpret_cast<intptr_t>(base),
516                       MemoryChunk::kAlignment));
517      if (base == NULL) return NULL;
518      size_ += chunk_size;
519      // Update executable memory size.
520      size_executable_ += chunk_size;
521    } else {
522      base = AllocateAlignedMemory(chunk_size,
523                                   MemoryChunk::kAlignment,
524                                   executable,
525                                   &reservation);
526      if (base == NULL) return NULL;
527      // Update executable memory size.
528      size_executable_ += reservation.size();
529    }
530
531#ifdef DEBUG
532    ZapBlock(base, CodePageGuardStartOffset());
533    ZapBlock(base + CodePageAreaStartOffset(), body_size);
534#endif
535    area_start = base + CodePageAreaStartOffset();
536    area_end = area_start + body_size;
537  } else {
538    chunk_size = MemoryChunk::kObjectStartOffset + body_size;
539    base = AllocateAlignedMemory(chunk_size,
540                                 MemoryChunk::kAlignment,
541                                 executable,
542                                 &reservation);
543
544    if (base == NULL) return NULL;
545
546#ifdef DEBUG
547    ZapBlock(base, chunk_size);
548#endif
549
550    area_start = base + Page::kObjectStartOffset;
551    area_end = base + chunk_size;
552  }
553
554  isolate_->counters()->memory_allocated()->
555      Increment(static_cast<int>(chunk_size));
556
557  LOG(isolate_, NewEvent("MemoryChunk", base, chunk_size));
558  if (owner != NULL) {
559    ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity());
560    PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size);
561  }
562
563  MemoryChunk* result = MemoryChunk::Initialize(heap,
564                                                base,
565                                                chunk_size,
566                                                area_start,
567                                                area_end,
568                                                executable,
569                                                owner);
570  result->set_reserved_memory(&reservation);
571  return result;
572}
573
574
575Page* MemoryAllocator::AllocatePage(PagedSpace* owner,
576                                    Executability executable) {
577  MemoryChunk* chunk = AllocateChunk(owner->AreaSize(),
578                                     executable,
579                                     owner);
580
581  if (chunk == NULL) return NULL;
582
583  return Page::Initialize(isolate_->heap(), chunk, executable, owner);
584}
585
586
587LargePage* MemoryAllocator::AllocateLargePage(intptr_t object_size,
588                                              Executability executable,
589                                              Space* owner) {
590  MemoryChunk* chunk = AllocateChunk(object_size, executable, owner);
591  if (chunk == NULL) return NULL;
592  return LargePage::Initialize(isolate_->heap(), chunk);
593}
594
595
596void MemoryAllocator::Free(MemoryChunk* chunk) {
597  LOG(isolate_, DeleteEvent("MemoryChunk", chunk));
598  if (chunk->owner() != NULL) {
599    ObjectSpace space =
600        static_cast<ObjectSpace>(1 << chunk->owner()->identity());
601    PerformAllocationCallback(space, kAllocationActionFree, chunk->size());
602  }
603
604  isolate_->heap()->RememberUnmappedPage(
605      reinterpret_cast<Address>(chunk), chunk->IsEvacuationCandidate());
606
607  delete chunk->slots_buffer();
608  delete chunk->skip_list();
609
610  VirtualMemory* reservation = chunk->reserved_memory();
611  if (reservation->IsReserved()) {
612    FreeMemory(reservation, chunk->executable());
613  } else {
614    FreeMemory(chunk->address(),
615               chunk->size(),
616               chunk->executable());
617  }
618}
619
620
621bool MemoryAllocator::CommitBlock(Address start,
622                                  size_t size,
623                                  Executability executable) {
624  if (!VirtualMemory::CommitRegion(start, size, executable)) return false;
625#ifdef DEBUG
626  ZapBlock(start, size);
627#endif
628  isolate_->counters()->memory_allocated()->Increment(static_cast<int>(size));
629  return true;
630}
631
632
633bool MemoryAllocator::UncommitBlock(Address start, size_t size) {
634  if (!VirtualMemory::UncommitRegion(start, size)) return false;
635  isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size));
636  return true;
637}
638
639
640void MemoryAllocator::ZapBlock(Address start, size_t size) {
641  for (size_t s = 0; s + kPointerSize <= size; s += kPointerSize) {
642    Memory::Address_at(start + s) = kZapValue;
643  }
644}
645
646
647void MemoryAllocator::PerformAllocationCallback(ObjectSpace space,
648                                                AllocationAction action,
649                                                size_t size) {
650  for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
651    MemoryAllocationCallbackRegistration registration =
652      memory_allocation_callbacks_[i];
653    if ((registration.space & space) == space &&
654        (registration.action & action) == action)
655      registration.callback(space, action, static_cast<int>(size));
656  }
657}
658
659
660bool MemoryAllocator::MemoryAllocationCallbackRegistered(
661    MemoryAllocationCallback callback) {
662  for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
663    if (memory_allocation_callbacks_[i].callback == callback) return true;
664  }
665  return false;
666}
667
668
669void MemoryAllocator::AddMemoryAllocationCallback(
670    MemoryAllocationCallback callback,
671    ObjectSpace space,
672    AllocationAction action) {
673  ASSERT(callback != NULL);
674  MemoryAllocationCallbackRegistration registration(callback, space, action);
675  ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback));
676  return memory_allocation_callbacks_.Add(registration);
677}
678
679
680void MemoryAllocator::RemoveMemoryAllocationCallback(
681     MemoryAllocationCallback callback) {
682  ASSERT(callback != NULL);
683  for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
684    if (memory_allocation_callbacks_[i].callback == callback) {
685      memory_allocation_callbacks_.Remove(i);
686      return;
687    }
688  }
689  UNREACHABLE();
690}
691
692
693#ifdef DEBUG
694void MemoryAllocator::ReportStatistics() {
695  float pct = static_cast<float>(capacity_ - size_) / capacity_;
696  PrintF("  capacity: %" V8_PTR_PREFIX "d"
697             ", used: %" V8_PTR_PREFIX "d"
698             ", available: %%%d\n\n",
699         capacity_, size_, static_cast<int>(pct*100));
700}
701#endif
702
703
704int MemoryAllocator::CodePageGuardStartOffset() {
705  // We are guarding code pages: the first OS page after the header
706  // will be protected as non-writable.
707  return RoundUp(Page::kObjectStartOffset, OS::CommitPageSize());
708}
709
710
711int MemoryAllocator::CodePageGuardSize() {
712  return static_cast<int>(OS::CommitPageSize());
713}
714
715
716int MemoryAllocator::CodePageAreaStartOffset() {
717  // We are guarding code pages: the first OS page after the header
718  // will be protected as non-writable.
719  return CodePageGuardStartOffset() + CodePageGuardSize();
720}
721
722
723int MemoryAllocator::CodePageAreaEndOffset() {
724  // We are guarding code pages: the last OS page will be protected as
725  // non-writable.
726  return Page::kPageSize - static_cast<int>(OS::CommitPageSize());
727}
728
729
730bool MemoryAllocator::CommitCodePage(VirtualMemory* vm,
731                                     Address start,
732                                     size_t size) {
733  // Commit page header (not executable).
734  if (!vm->Commit(start,
735                  CodePageGuardStartOffset(),
736                  false)) {
737    return false;
738  }
739
740  // Create guard page after the header.
741  if (!vm->Guard(start + CodePageGuardStartOffset())) {
742    return false;
743  }
744
745  // Commit page body (executable).
746  size_t area_size = size - CodePageAreaStartOffset() - CodePageGuardSize();
747  if (!vm->Commit(start + CodePageAreaStartOffset(),
748                  area_size,
749                  true)) {
750    return false;
751  }
752
753  // Create guard page after the allocatable area.
754  if (!vm->Guard(start + CodePageAreaStartOffset() + area_size)) {
755    return false;
756  }
757
758  return true;
759}
760
761
762// -----------------------------------------------------------------------------
763// MemoryChunk implementation
764
765void MemoryChunk::IncrementLiveBytesFromMutator(Address address, int by) {
766  MemoryChunk* chunk = MemoryChunk::FromAddress(address);
767  if (!chunk->InNewSpace() && !static_cast<Page*>(chunk)->WasSwept()) {
768    static_cast<PagedSpace*>(chunk->owner())->IncrementUnsweptFreeBytes(-by);
769  }
770  chunk->IncrementLiveBytes(by);
771}
772
773// -----------------------------------------------------------------------------
774// PagedSpace implementation
775
776PagedSpace::PagedSpace(Heap* heap,
777                       intptr_t max_capacity,
778                       AllocationSpace id,
779                       Executability executable)
780    : Space(heap, id, executable),
781      free_list_(this),
782      was_swept_conservatively_(false),
783      first_unswept_page_(Page::FromAddress(NULL)),
784      unswept_free_bytes_(0) {
785  if (id == CODE_SPACE) {
786    area_size_ = heap->isolate()->memory_allocator()->
787        CodePageAreaSize();
788  } else {
789    area_size_ = Page::kPageSize - Page::kObjectStartOffset;
790  }
791  max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize)
792      * AreaSize();
793  accounting_stats_.Clear();
794
795  allocation_info_.top = NULL;
796  allocation_info_.limit = NULL;
797
798  anchor_.InitializeAsAnchor(this);
799}
800
801
802bool PagedSpace::SetUp() {
803  return true;
804}
805
806
807bool PagedSpace::HasBeenSetUp() {
808  return true;
809}
810
811
812void PagedSpace::TearDown() {
813  PageIterator iterator(this);
814  while (iterator.has_next()) {
815    heap()->isolate()->memory_allocator()->Free(iterator.next());
816  }
817  anchor_.set_next_page(&anchor_);
818  anchor_.set_prev_page(&anchor_);
819  accounting_stats_.Clear();
820}
821
822
823MaybeObject* PagedSpace::FindObject(Address addr) {
824  // Note: this function can only be called on precisely swept spaces.
825  ASSERT(!heap()->mark_compact_collector()->in_use());
826
827  if (!Contains(addr)) return Failure::Exception();
828
829  Page* p = Page::FromAddress(addr);
830  HeapObjectIterator it(p, NULL);
831  for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) {
832    Address cur = obj->address();
833    Address next = cur + obj->Size();
834    if ((cur <= addr) && (addr < next)) return obj;
835  }
836
837  UNREACHABLE();
838  return Failure::Exception();
839}
840
841bool PagedSpace::CanExpand() {
842  ASSERT(max_capacity_ % AreaSize() == 0);
843  ASSERT(Capacity() % AreaSize() == 0);
844
845  if (Capacity() == max_capacity_) return false;
846
847  ASSERT(Capacity() < max_capacity_);
848
849  // Are we going to exceed capacity for this space?
850  if ((Capacity() + Page::kPageSize) > max_capacity_) return false;
851
852  return true;
853}
854
855bool PagedSpace::Expand() {
856  if (!CanExpand()) return false;
857
858  Page* p = heap()->isolate()->memory_allocator()->
859      AllocatePage(this, executable());
860  if (p == NULL) return false;
861
862  ASSERT(Capacity() <= max_capacity_);
863
864  p->InsertAfter(anchor_.prev_page());
865
866  return true;
867}
868
869
870int PagedSpace::CountTotalPages() {
871  PageIterator it(this);
872  int count = 0;
873  while (it.has_next()) {
874    it.next();
875    count++;
876  }
877  return count;
878}
879
880
881void PagedSpace::ReleasePage(Page* page) {
882  ASSERT(page->LiveBytes() == 0);
883  ASSERT(AreaSize() == page->area_size());
884
885  // Adjust list of unswept pages if the page is the head of the list.
886  if (first_unswept_page_ == page) {
887    first_unswept_page_ = page->next_page();
888    if (first_unswept_page_ == anchor()) {
889      first_unswept_page_ = Page::FromAddress(NULL);
890    }
891  }
892
893  if (page->WasSwept()) {
894    intptr_t size = free_list_.EvictFreeListItems(page);
895    accounting_stats_.AllocateBytes(size);
896    ASSERT_EQ(AreaSize(), static_cast<int>(size));
897  } else {
898    DecreaseUnsweptFreeBytes(page);
899  }
900
901  if (Page::FromAllocationTop(allocation_info_.top) == page) {
902    allocation_info_.top = allocation_info_.limit = NULL;
903  }
904
905  page->Unlink();
906  if (page->IsFlagSet(MemoryChunk::CONTAINS_ONLY_DATA)) {
907    heap()->isolate()->memory_allocator()->Free(page);
908  } else {
909    heap()->QueueMemoryChunkForFree(page);
910  }
911
912  ASSERT(Capacity() > 0);
913  ASSERT(Capacity() % AreaSize() == 0);
914  accounting_stats_.ShrinkSpace(AreaSize());
915}
916
917
918void PagedSpace::ReleaseAllUnusedPages() {
919  PageIterator it(this);
920  while (it.has_next()) {
921    Page* page = it.next();
922    if (!page->WasSwept()) {
923      if (page->LiveBytes() == 0) ReleasePage(page);
924    } else {
925      HeapObject* obj = HeapObject::FromAddress(page->area_start());
926      if (obj->IsFreeSpace() &&
927          FreeSpace::cast(obj)->size() == AreaSize()) {
928        // Sometimes we allocate memory from free list but don't
929        // immediately initialize it (e.g. see PagedSpace::ReserveSpace
930        // called from Heap::ReserveSpace that can cause GC before
931        // reserved space is actually initialized).
932        // Thus we can't simply assume that obj represents a valid
933        // node still owned by a free list
934        // Instead we should verify that the page is fully covered
935        // by free list items.
936        FreeList::SizeStats sizes;
937        free_list_.CountFreeListItems(page, &sizes);
938        if (sizes.Total() == AreaSize()) {
939          ReleasePage(page);
940        }
941      }
942    }
943  }
944  heap()->FreeQueuedChunks();
945}
946
947
948#ifdef DEBUG
949void PagedSpace::Print() { }
950#endif
951
952
953#ifdef DEBUG
954void PagedSpace::Verify(ObjectVisitor* visitor) {
955  // We can only iterate over the pages if they were swept precisely.
956  if (was_swept_conservatively_) return;
957
958  bool allocation_pointer_found_in_space =
959      (allocation_info_.top == allocation_info_.limit);
960  PageIterator page_iterator(this);
961  while (page_iterator.has_next()) {
962    Page* page = page_iterator.next();
963    ASSERT(page->owner() == this);
964    if (page == Page::FromAllocationTop(allocation_info_.top)) {
965      allocation_pointer_found_in_space = true;
966    }
967    ASSERT(page->WasSweptPrecisely());
968    HeapObjectIterator it(page, NULL);
969    Address end_of_previous_object = page->area_start();
970    Address top = page->area_end();
971    int black_size = 0;
972    for (HeapObject* object = it.Next(); object != NULL; object = it.Next()) {
973      ASSERT(end_of_previous_object <= object->address());
974
975      // The first word should be a map, and we expect all map pointers to
976      // be in map space.
977      Map* map = object->map();
978      ASSERT(map->IsMap());
979      ASSERT(heap()->map_space()->Contains(map));
980
981      // Perform space-specific object verification.
982      VerifyObject(object);
983
984      // The object itself should look OK.
985      object->Verify();
986
987      // All the interior pointers should be contained in the heap.
988      int size = object->Size();
989      object->IterateBody(map->instance_type(), size, visitor);
990      if (Marking::IsBlack(Marking::MarkBitFrom(object))) {
991        black_size += size;
992      }
993
994      ASSERT(object->address() + size <= top);
995      end_of_previous_object = object->address() + size;
996    }
997    ASSERT_LE(black_size, page->LiveBytes());
998  }
999  ASSERT(allocation_pointer_found_in_space);
1000}
1001#endif
1002
1003
1004// -----------------------------------------------------------------------------
1005// NewSpace implementation
1006
1007
1008bool NewSpace::SetUp(int reserved_semispace_capacity,
1009                     int maximum_semispace_capacity) {
1010  // Set up new space based on the preallocated memory block defined by
1011  // start and size. The provided space is divided into two semi-spaces.
1012  // To support fast containment testing in the new space, the size of
1013  // this chunk must be a power of two and it must be aligned to its size.
1014  int initial_semispace_capacity = heap()->InitialSemiSpaceSize();
1015
1016  size_t size = 2 * reserved_semispace_capacity;
1017  Address base =
1018      heap()->isolate()->memory_allocator()->ReserveAlignedMemory(
1019          size, size, &reservation_);
1020  if (base == NULL) return false;
1021
1022  chunk_base_ = base;
1023  chunk_size_ = static_cast<uintptr_t>(size);
1024  LOG(heap()->isolate(), NewEvent("InitialChunk", chunk_base_, chunk_size_));
1025
1026  ASSERT(initial_semispace_capacity <= maximum_semispace_capacity);
1027  ASSERT(IsPowerOf2(maximum_semispace_capacity));
1028
1029  // Allocate and set up the histogram arrays if necessary.
1030  allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
1031  promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
1032
1033#define SET_NAME(name) allocated_histogram_[name].set_name(#name); \
1034                       promoted_histogram_[name].set_name(#name);
1035  INSTANCE_TYPE_LIST(SET_NAME)
1036#undef SET_NAME
1037
1038  ASSERT(reserved_semispace_capacity == heap()->ReservedSemiSpaceSize());
1039  ASSERT(static_cast<intptr_t>(chunk_size_) >=
1040         2 * heap()->ReservedSemiSpaceSize());
1041  ASSERT(IsAddressAligned(chunk_base_, 2 * reserved_semispace_capacity, 0));
1042
1043  to_space_.SetUp(chunk_base_,
1044                  initial_semispace_capacity,
1045                  maximum_semispace_capacity);
1046  from_space_.SetUp(chunk_base_ + reserved_semispace_capacity,
1047                    initial_semispace_capacity,
1048                    maximum_semispace_capacity);
1049  if (!to_space_.Commit()) {
1050    return false;
1051  }
1052
1053  start_ = chunk_base_;
1054  address_mask_ = ~(2 * reserved_semispace_capacity - 1);
1055  object_mask_ = address_mask_ | kHeapObjectTagMask;
1056  object_expected_ = reinterpret_cast<uintptr_t>(start_) | kHeapObjectTag;
1057
1058  ResetAllocationInfo();
1059
1060  return true;
1061}
1062
1063
1064void NewSpace::TearDown() {
1065  if (allocated_histogram_) {
1066    DeleteArray(allocated_histogram_);
1067    allocated_histogram_ = NULL;
1068  }
1069  if (promoted_histogram_) {
1070    DeleteArray(promoted_histogram_);
1071    promoted_histogram_ = NULL;
1072  }
1073
1074  start_ = NULL;
1075  allocation_info_.top = NULL;
1076  allocation_info_.limit = NULL;
1077
1078  to_space_.TearDown();
1079  from_space_.TearDown();
1080
1081  LOG(heap()->isolate(), DeleteEvent("InitialChunk", chunk_base_));
1082
1083  ASSERT(reservation_.IsReserved());
1084  heap()->isolate()->memory_allocator()->FreeMemory(&reservation_,
1085                                                    NOT_EXECUTABLE);
1086  chunk_base_ = NULL;
1087  chunk_size_ = 0;
1088}
1089
1090
1091void NewSpace::Flip() {
1092  SemiSpace::Swap(&from_space_, &to_space_);
1093}
1094
1095
1096void NewSpace::Grow() {
1097  // Double the semispace size but only up to maximum capacity.
1098  ASSERT(Capacity() < MaximumCapacity());
1099  int new_capacity = Min(MaximumCapacity(), 2 * static_cast<int>(Capacity()));
1100  if (to_space_.GrowTo(new_capacity)) {
1101    // Only grow from space if we managed to grow to-space.
1102    if (!from_space_.GrowTo(new_capacity)) {
1103      // If we managed to grow to-space but couldn't grow from-space,
1104      // attempt to shrink to-space.
1105      if (!to_space_.ShrinkTo(from_space_.Capacity())) {
1106        // We are in an inconsistent state because we could not
1107        // commit/uncommit memory from new space.
1108        V8::FatalProcessOutOfMemory("Failed to grow new space.");
1109      }
1110    }
1111  }
1112  ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1113}
1114
1115
1116void NewSpace::Shrink() {
1117  int new_capacity = Max(InitialCapacity(), 2 * SizeAsInt());
1118  int rounded_new_capacity = RoundUp(new_capacity, Page::kPageSize);
1119  if (rounded_new_capacity < Capacity() &&
1120      to_space_.ShrinkTo(rounded_new_capacity))  {
1121    // Only shrink from-space if we managed to shrink to-space.
1122    from_space_.Reset();
1123    if (!from_space_.ShrinkTo(rounded_new_capacity)) {
1124      // If we managed to shrink to-space but couldn't shrink from
1125      // space, attempt to grow to-space again.
1126      if (!to_space_.GrowTo(from_space_.Capacity())) {
1127        // We are in an inconsistent state because we could not
1128        // commit/uncommit memory from new space.
1129        V8::FatalProcessOutOfMemory("Failed to shrink new space.");
1130      }
1131    }
1132  }
1133  allocation_info_.limit = to_space_.page_high();
1134  ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1135}
1136
1137
1138void NewSpace::UpdateAllocationInfo() {
1139  allocation_info_.top = to_space_.page_low();
1140  allocation_info_.limit = to_space_.page_high();
1141
1142  // Lower limit during incremental marking.
1143  if (heap()->incremental_marking()->IsMarking() &&
1144      inline_allocation_limit_step() != 0) {
1145    Address new_limit =
1146        allocation_info_.top + inline_allocation_limit_step();
1147    allocation_info_.limit = Min(new_limit, allocation_info_.limit);
1148  }
1149  ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1150}
1151
1152
1153void NewSpace::ResetAllocationInfo() {
1154  to_space_.Reset();
1155  UpdateAllocationInfo();
1156  pages_used_ = 0;
1157  // Clear all mark-bits in the to-space.
1158  NewSpacePageIterator it(&to_space_);
1159  while (it.has_next()) {
1160    Bitmap::Clear(it.next());
1161  }
1162}
1163
1164
1165bool NewSpace::AddFreshPage() {
1166  Address top = allocation_info_.top;
1167  if (NewSpacePage::IsAtStart(top)) {
1168    // The current page is already empty. Don't try to make another.
1169
1170    // We should only get here if someone asks to allocate more
1171    // than what can be stored in a single page.
1172    // TODO(gc): Change the limit on new-space allocation to prevent this
1173    // from happening (all such allocations should go directly to LOSpace).
1174    return false;
1175  }
1176  if (!to_space_.AdvancePage()) {
1177    // Failed to get a new page in to-space.
1178    return false;
1179  }
1180
1181  // Clear remainder of current page.
1182  Address limit = NewSpacePage::FromLimit(top)->area_end();
1183  if (heap()->gc_state() == Heap::SCAVENGE) {
1184    heap()->promotion_queue()->SetNewLimit(limit);
1185    heap()->promotion_queue()->ActivateGuardIfOnTheSamePage();
1186  }
1187
1188  int remaining_in_page = static_cast<int>(limit - top);
1189  heap()->CreateFillerObjectAt(top, remaining_in_page);
1190  pages_used_++;
1191  UpdateAllocationInfo();
1192
1193  return true;
1194}
1195
1196
1197MaybeObject* NewSpace::SlowAllocateRaw(int size_in_bytes) {
1198  Address old_top = allocation_info_.top;
1199  Address new_top = old_top + size_in_bytes;
1200  Address high = to_space_.page_high();
1201  if (allocation_info_.limit < high) {
1202    // Incremental marking has lowered the limit to get a
1203    // chance to do a step.
1204    allocation_info_.limit = Min(
1205        allocation_info_.limit + inline_allocation_limit_step_,
1206        high);
1207    int bytes_allocated = static_cast<int>(new_top - top_on_previous_step_);
1208    heap()->incremental_marking()->Step(
1209        bytes_allocated, IncrementalMarking::GC_VIA_STACK_GUARD);
1210    top_on_previous_step_ = new_top;
1211    return AllocateRaw(size_in_bytes);
1212  } else if (AddFreshPage()) {
1213    // Switched to new page. Try allocating again.
1214    int bytes_allocated = static_cast<int>(old_top - top_on_previous_step_);
1215    heap()->incremental_marking()->Step(
1216        bytes_allocated, IncrementalMarking::GC_VIA_STACK_GUARD);
1217    top_on_previous_step_ = to_space_.page_low();
1218    return AllocateRaw(size_in_bytes);
1219  } else {
1220    return Failure::RetryAfterGC();
1221  }
1222}
1223
1224
1225#ifdef DEBUG
1226// We do not use the SemiSpaceIterator because verification doesn't assume
1227// that it works (it depends on the invariants we are checking).
1228void NewSpace::Verify() {
1229  // The allocation pointer should be in the space or at the very end.
1230  ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1231
1232  // There should be objects packed in from the low address up to the
1233  // allocation pointer.
1234  Address current = to_space_.first_page()->area_start();
1235  CHECK_EQ(current, to_space_.space_start());
1236
1237  while (current != top()) {
1238    if (!NewSpacePage::IsAtEnd(current)) {
1239      // The allocation pointer should not be in the middle of an object.
1240      CHECK(!NewSpacePage::FromLimit(current)->ContainsLimit(top()) ||
1241            current < top());
1242
1243      HeapObject* object = HeapObject::FromAddress(current);
1244
1245      // The first word should be a map, and we expect all map pointers to
1246      // be in map space.
1247      Map* map = object->map();
1248      CHECK(map->IsMap());
1249      CHECK(heap()->map_space()->Contains(map));
1250
1251      // The object should not be code or a map.
1252      CHECK(!object->IsMap());
1253      CHECK(!object->IsCode());
1254
1255      // The object itself should look OK.
1256      object->Verify();
1257
1258      // All the interior pointers should be contained in the heap.
1259      VerifyPointersVisitor visitor;
1260      int size = object->Size();
1261      object->IterateBody(map->instance_type(), size, &visitor);
1262
1263      current += size;
1264    } else {
1265      // At end of page, switch to next page.
1266      NewSpacePage* page = NewSpacePage::FromLimit(current)->next_page();
1267      // Next page should be valid.
1268      CHECK(!page->is_anchor());
1269      current = page->area_start();
1270    }
1271  }
1272
1273  // Check semi-spaces.
1274  ASSERT_EQ(from_space_.id(), kFromSpace);
1275  ASSERT_EQ(to_space_.id(), kToSpace);
1276  from_space_.Verify();
1277  to_space_.Verify();
1278}
1279#endif
1280
1281// -----------------------------------------------------------------------------
1282// SemiSpace implementation
1283
1284void SemiSpace::SetUp(Address start,
1285                      int initial_capacity,
1286                      int maximum_capacity) {
1287  // Creates a space in the young generation. The constructor does not
1288  // allocate memory from the OS.  A SemiSpace is given a contiguous chunk of
1289  // memory of size 'capacity' when set up, and does not grow or shrink
1290  // otherwise.  In the mark-compact collector, the memory region of the from
1291  // space is used as the marking stack. It requires contiguous memory
1292  // addresses.
1293  ASSERT(maximum_capacity >= Page::kPageSize);
1294  initial_capacity_ = RoundDown(initial_capacity, Page::kPageSize);
1295  capacity_ = initial_capacity;
1296  maximum_capacity_ = RoundDown(maximum_capacity, Page::kPageSize);
1297  committed_ = false;
1298  start_ = start;
1299  address_mask_ = ~(maximum_capacity - 1);
1300  object_mask_ = address_mask_ | kHeapObjectTagMask;
1301  object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag;
1302  age_mark_ = start_;
1303}
1304
1305
1306void SemiSpace::TearDown() {
1307  start_ = NULL;
1308  capacity_ = 0;
1309}
1310
1311
1312bool SemiSpace::Commit() {
1313  ASSERT(!is_committed());
1314  int pages = capacity_ / Page::kPageSize;
1315  Address end = start_ + maximum_capacity_;
1316  Address start = end - pages * Page::kPageSize;
1317  if (!heap()->isolate()->memory_allocator()->CommitBlock(start,
1318                                                          capacity_,
1319                                                          executable())) {
1320    return false;
1321  }
1322
1323  NewSpacePage* page = anchor();
1324  for (int i = 1; i <= pages; i++) {
1325    NewSpacePage* new_page =
1326      NewSpacePage::Initialize(heap(), end - i * Page::kPageSize, this);
1327    new_page->InsertAfter(page);
1328    page = new_page;
1329  }
1330
1331  committed_ = true;
1332  Reset();
1333  return true;
1334}
1335
1336
1337bool SemiSpace::Uncommit() {
1338  ASSERT(is_committed());
1339  Address start = start_ + maximum_capacity_ - capacity_;
1340  if (!heap()->isolate()->memory_allocator()->UncommitBlock(start, capacity_)) {
1341    return false;
1342  }
1343  anchor()->set_next_page(anchor());
1344  anchor()->set_prev_page(anchor());
1345
1346  committed_ = false;
1347  return true;
1348}
1349
1350
1351bool SemiSpace::GrowTo(int new_capacity) {
1352  if (!is_committed()) {
1353    if (!Commit()) return false;
1354  }
1355  ASSERT((new_capacity & Page::kPageAlignmentMask) == 0);
1356  ASSERT(new_capacity <= maximum_capacity_);
1357  ASSERT(new_capacity > capacity_);
1358  int pages_before = capacity_ / Page::kPageSize;
1359  int pages_after = new_capacity / Page::kPageSize;
1360
1361  Address end = start_ + maximum_capacity_;
1362  Address start = end - new_capacity;
1363  size_t delta = new_capacity - capacity_;
1364
1365  ASSERT(IsAligned(delta, OS::AllocateAlignment()));
1366  if (!heap()->isolate()->memory_allocator()->CommitBlock(
1367      start, delta, executable())) {
1368    return false;
1369  }
1370  capacity_ = new_capacity;
1371  NewSpacePage* last_page = anchor()->prev_page();
1372  ASSERT(last_page != anchor());
1373  for (int i = pages_before + 1; i <= pages_after; i++) {
1374    Address page_address = end - i * Page::kPageSize;
1375    NewSpacePage* new_page = NewSpacePage::Initialize(heap(),
1376                                                      page_address,
1377                                                      this);
1378    new_page->InsertAfter(last_page);
1379    Bitmap::Clear(new_page);
1380    // Duplicate the flags that was set on the old page.
1381    new_page->SetFlags(last_page->GetFlags(),
1382                       NewSpacePage::kCopyOnFlipFlagsMask);
1383    last_page = new_page;
1384  }
1385  return true;
1386}
1387
1388
1389bool SemiSpace::ShrinkTo(int new_capacity) {
1390  ASSERT((new_capacity & Page::kPageAlignmentMask) == 0);
1391  ASSERT(new_capacity >= initial_capacity_);
1392  ASSERT(new_capacity < capacity_);
1393  if (is_committed()) {
1394    // Semispaces grow backwards from the end of their allocated capacity,
1395    // so we find the before and after start addresses relative to the
1396    // end of the space.
1397    Address space_end = start_ + maximum_capacity_;
1398    Address old_start = space_end - capacity_;
1399    size_t delta = capacity_ - new_capacity;
1400    ASSERT(IsAligned(delta, OS::AllocateAlignment()));
1401
1402    MemoryAllocator* allocator = heap()->isolate()->memory_allocator();
1403    if (!allocator->UncommitBlock(old_start, delta)) {
1404      return false;
1405    }
1406
1407    int pages_after = new_capacity / Page::kPageSize;
1408    NewSpacePage* new_last_page =
1409        NewSpacePage::FromAddress(space_end - pages_after * Page::kPageSize);
1410    new_last_page->set_next_page(anchor());
1411    anchor()->set_prev_page(new_last_page);
1412    ASSERT((current_page_ <= first_page()) && (current_page_ >= new_last_page));
1413  }
1414
1415  capacity_ = new_capacity;
1416
1417  return true;
1418}
1419
1420
1421void SemiSpace::FlipPages(intptr_t flags, intptr_t mask) {
1422  anchor_.set_owner(this);
1423  // Fixup back-pointers to anchor. Address of anchor changes
1424  // when we swap.
1425  anchor_.prev_page()->set_next_page(&anchor_);
1426  anchor_.next_page()->set_prev_page(&anchor_);
1427
1428  bool becomes_to_space = (id_ == kFromSpace);
1429  id_ = becomes_to_space ? kToSpace : kFromSpace;
1430  NewSpacePage* page = anchor_.next_page();
1431  while (page != &anchor_) {
1432    page->set_owner(this);
1433    page->SetFlags(flags, mask);
1434    if (becomes_to_space) {
1435      page->ClearFlag(MemoryChunk::IN_FROM_SPACE);
1436      page->SetFlag(MemoryChunk::IN_TO_SPACE);
1437      page->ClearFlag(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK);
1438      page->ResetLiveBytes();
1439    } else {
1440      page->SetFlag(MemoryChunk::IN_FROM_SPACE);
1441      page->ClearFlag(MemoryChunk::IN_TO_SPACE);
1442    }
1443    ASSERT(page->IsFlagSet(MemoryChunk::SCAN_ON_SCAVENGE));
1444    ASSERT(page->IsFlagSet(MemoryChunk::IN_TO_SPACE) ||
1445           page->IsFlagSet(MemoryChunk::IN_FROM_SPACE));
1446    page = page->next_page();
1447  }
1448}
1449
1450
1451void SemiSpace::Reset() {
1452  ASSERT(anchor_.next_page() != &anchor_);
1453  current_page_ = anchor_.next_page();
1454}
1455
1456
1457void SemiSpace::Swap(SemiSpace* from, SemiSpace* to) {
1458  // We won't be swapping semispaces without data in them.
1459  ASSERT(from->anchor_.next_page() != &from->anchor_);
1460  ASSERT(to->anchor_.next_page() != &to->anchor_);
1461
1462  // Swap bits.
1463  SemiSpace tmp = *from;
1464  *from = *to;
1465  *to = tmp;
1466
1467  // Fixup back-pointers to the page list anchor now that its address
1468  // has changed.
1469  // Swap to/from-space bits on pages.
1470  // Copy GC flags from old active space (from-space) to new (to-space).
1471  intptr_t flags = from->current_page()->GetFlags();
1472  to->FlipPages(flags, NewSpacePage::kCopyOnFlipFlagsMask);
1473
1474  from->FlipPages(0, 0);
1475}
1476
1477
1478void SemiSpace::set_age_mark(Address mark) {
1479  ASSERT(NewSpacePage::FromLimit(mark)->semi_space() == this);
1480  age_mark_ = mark;
1481  // Mark all pages up to the one containing mark.
1482  NewSpacePageIterator it(space_start(), mark);
1483  while (it.has_next()) {
1484    it.next()->SetFlag(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK);
1485  }
1486}
1487
1488
1489#ifdef DEBUG
1490void SemiSpace::Print() { }
1491
1492
1493void SemiSpace::Verify() {
1494  bool is_from_space = (id_ == kFromSpace);
1495  NewSpacePage* page = anchor_.next_page();
1496  CHECK(anchor_.semi_space() == this);
1497  while (page != &anchor_) {
1498    CHECK(page->semi_space() == this);
1499    CHECK(page->InNewSpace());
1500    CHECK(page->IsFlagSet(is_from_space ? MemoryChunk::IN_FROM_SPACE
1501                                        : MemoryChunk::IN_TO_SPACE));
1502    CHECK(!page->IsFlagSet(is_from_space ? MemoryChunk::IN_TO_SPACE
1503                                         : MemoryChunk::IN_FROM_SPACE));
1504    CHECK(page->IsFlagSet(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING));
1505    if (!is_from_space) {
1506      // The pointers-from-here-are-interesting flag isn't updated dynamically
1507      // on from-space pages, so it might be out of sync with the marking state.
1508      if (page->heap()->incremental_marking()->IsMarking()) {
1509        CHECK(page->IsFlagSet(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING));
1510      } else {
1511        CHECK(!page->IsFlagSet(
1512            MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING));
1513      }
1514      // TODO(gc): Check that the live_bytes_count_ field matches the
1515      // black marking on the page (if we make it match in new-space).
1516    }
1517    CHECK(page->IsFlagSet(MemoryChunk::SCAN_ON_SCAVENGE));
1518    CHECK(page->prev_page()->next_page() == page);
1519    page = page->next_page();
1520  }
1521}
1522
1523
1524void SemiSpace::AssertValidRange(Address start, Address end) {
1525  // Addresses belong to same semi-space
1526  NewSpacePage* page = NewSpacePage::FromLimit(start);
1527  NewSpacePage* end_page = NewSpacePage::FromLimit(end);
1528  SemiSpace* space = page->semi_space();
1529  CHECK_EQ(space, end_page->semi_space());
1530  // Start address is before end address, either on same page,
1531  // or end address is on a later page in the linked list of
1532  // semi-space pages.
1533  if (page == end_page) {
1534    CHECK(start <= end);
1535  } else {
1536    while (page != end_page) {
1537      page = page->next_page();
1538      CHECK_NE(page, space->anchor());
1539    }
1540  }
1541}
1542#endif
1543
1544
1545// -----------------------------------------------------------------------------
1546// SemiSpaceIterator implementation.
1547SemiSpaceIterator::SemiSpaceIterator(NewSpace* space) {
1548  Initialize(space->bottom(), space->top(), NULL);
1549}
1550
1551
1552SemiSpaceIterator::SemiSpaceIterator(NewSpace* space,
1553                                     HeapObjectCallback size_func) {
1554  Initialize(space->bottom(), space->top(), size_func);
1555}
1556
1557
1558SemiSpaceIterator::SemiSpaceIterator(NewSpace* space, Address start) {
1559  Initialize(start, space->top(), NULL);
1560}
1561
1562
1563SemiSpaceIterator::SemiSpaceIterator(Address from, Address to) {
1564  Initialize(from, to, NULL);
1565}
1566
1567
1568void SemiSpaceIterator::Initialize(Address start,
1569                                   Address end,
1570                                   HeapObjectCallback size_func) {
1571  SemiSpace::AssertValidRange(start, end);
1572  current_ = start;
1573  limit_ = end;
1574  size_func_ = size_func;
1575}
1576
1577
1578#ifdef DEBUG
1579// heap_histograms is shared, always clear it before using it.
1580static void ClearHistograms() {
1581  Isolate* isolate = Isolate::Current();
1582  // We reset the name each time, though it hasn't changed.
1583#define DEF_TYPE_NAME(name) isolate->heap_histograms()[name].set_name(#name);
1584  INSTANCE_TYPE_LIST(DEF_TYPE_NAME)
1585#undef DEF_TYPE_NAME
1586
1587#define CLEAR_HISTOGRAM(name) isolate->heap_histograms()[name].clear();
1588  INSTANCE_TYPE_LIST(CLEAR_HISTOGRAM)
1589#undef CLEAR_HISTOGRAM
1590
1591  isolate->js_spill_information()->Clear();
1592}
1593
1594
1595static void ClearCodeKindStatistics() {
1596  Isolate* isolate = Isolate::Current();
1597  for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) {
1598    isolate->code_kind_statistics()[i] = 0;
1599  }
1600}
1601
1602
1603static void ReportCodeKindStatistics() {
1604  Isolate* isolate = Isolate::Current();
1605  const char* table[Code::NUMBER_OF_KINDS] = { NULL };
1606
1607#define CASE(name)                            \
1608  case Code::name: table[Code::name] = #name; \
1609  break
1610
1611  for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) {
1612    switch (static_cast<Code::Kind>(i)) {
1613      CASE(FUNCTION);
1614      CASE(OPTIMIZED_FUNCTION);
1615      CASE(STUB);
1616      CASE(BUILTIN);
1617      CASE(LOAD_IC);
1618      CASE(KEYED_LOAD_IC);
1619      CASE(STORE_IC);
1620      CASE(KEYED_STORE_IC);
1621      CASE(CALL_IC);
1622      CASE(KEYED_CALL_IC);
1623      CASE(UNARY_OP_IC);
1624      CASE(BINARY_OP_IC);
1625      CASE(COMPARE_IC);
1626      CASE(TO_BOOLEAN_IC);
1627    }
1628  }
1629
1630#undef CASE
1631
1632  PrintF("\n   Code kind histograms: \n");
1633  for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) {
1634    if (isolate->code_kind_statistics()[i] > 0) {
1635      PrintF("     %-20s: %10d bytes\n", table[i],
1636          isolate->code_kind_statistics()[i]);
1637    }
1638  }
1639  PrintF("\n");
1640}
1641
1642
1643static int CollectHistogramInfo(HeapObject* obj) {
1644  Isolate* isolate = Isolate::Current();
1645  InstanceType type = obj->map()->instance_type();
1646  ASSERT(0 <= type && type <= LAST_TYPE);
1647  ASSERT(isolate->heap_histograms()[type].name() != NULL);
1648  isolate->heap_histograms()[type].increment_number(1);
1649  isolate->heap_histograms()[type].increment_bytes(obj->Size());
1650
1651  if (FLAG_collect_heap_spill_statistics && obj->IsJSObject()) {
1652    JSObject::cast(obj)->IncrementSpillStatistics(
1653        isolate->js_spill_information());
1654  }
1655
1656  return obj->Size();
1657}
1658
1659
1660static void ReportHistogram(bool print_spill) {
1661  Isolate* isolate = Isolate::Current();
1662  PrintF("\n  Object Histogram:\n");
1663  for (int i = 0; i <= LAST_TYPE; i++) {
1664    if (isolate->heap_histograms()[i].number() > 0) {
1665      PrintF("    %-34s%10d (%10d bytes)\n",
1666             isolate->heap_histograms()[i].name(),
1667             isolate->heap_histograms()[i].number(),
1668             isolate->heap_histograms()[i].bytes());
1669    }
1670  }
1671  PrintF("\n");
1672
1673  // Summarize string types.
1674  int string_number = 0;
1675  int string_bytes = 0;
1676#define INCREMENT(type, size, name, camel_name)      \
1677    string_number += isolate->heap_histograms()[type].number(); \
1678    string_bytes += isolate->heap_histograms()[type].bytes();
1679  STRING_TYPE_LIST(INCREMENT)
1680#undef INCREMENT
1681  if (string_number > 0) {
1682    PrintF("    %-34s%10d (%10d bytes)\n\n", "STRING_TYPE", string_number,
1683           string_bytes);
1684  }
1685
1686  if (FLAG_collect_heap_spill_statistics && print_spill) {
1687    isolate->js_spill_information()->Print();
1688  }
1689}
1690#endif  // DEBUG
1691
1692
1693// Support for statistics gathering for --heap-stats and --log-gc.
1694void NewSpace::ClearHistograms() {
1695  for (int i = 0; i <= LAST_TYPE; i++) {
1696    allocated_histogram_[i].clear();
1697    promoted_histogram_[i].clear();
1698  }
1699}
1700
1701// Because the copying collector does not touch garbage objects, we iterate
1702// the new space before a collection to get a histogram of allocated objects.
1703// This only happens when --log-gc flag is set.
1704void NewSpace::CollectStatistics() {
1705  ClearHistograms();
1706  SemiSpaceIterator it(this);
1707  for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next())
1708    RecordAllocation(obj);
1709}
1710
1711
1712static void DoReportStatistics(Isolate* isolate,
1713                               HistogramInfo* info, const char* description) {
1714  LOG(isolate, HeapSampleBeginEvent("NewSpace", description));
1715  // Lump all the string types together.
1716  int string_number = 0;
1717  int string_bytes = 0;
1718#define INCREMENT(type, size, name, camel_name)       \
1719    string_number += info[type].number();             \
1720    string_bytes += info[type].bytes();
1721  STRING_TYPE_LIST(INCREMENT)
1722#undef INCREMENT
1723  if (string_number > 0) {
1724    LOG(isolate,
1725        HeapSampleItemEvent("STRING_TYPE", string_number, string_bytes));
1726  }
1727
1728  // Then do the other types.
1729  for (int i = FIRST_NONSTRING_TYPE; i <= LAST_TYPE; ++i) {
1730    if (info[i].number() > 0) {
1731      LOG(isolate,
1732          HeapSampleItemEvent(info[i].name(), info[i].number(),
1733                              info[i].bytes()));
1734    }
1735  }
1736  LOG(isolate, HeapSampleEndEvent("NewSpace", description));
1737}
1738
1739
1740void NewSpace::ReportStatistics() {
1741#ifdef DEBUG
1742  if (FLAG_heap_stats) {
1743    float pct = static_cast<float>(Available()) / Capacity();
1744    PrintF("  capacity: %" V8_PTR_PREFIX "d"
1745               ", available: %" V8_PTR_PREFIX "d, %%%d\n",
1746           Capacity(), Available(), static_cast<int>(pct*100));
1747    PrintF("\n  Object Histogram:\n");
1748    for (int i = 0; i <= LAST_TYPE; i++) {
1749      if (allocated_histogram_[i].number() > 0) {
1750        PrintF("    %-34s%10d (%10d bytes)\n",
1751               allocated_histogram_[i].name(),
1752               allocated_histogram_[i].number(),
1753               allocated_histogram_[i].bytes());
1754      }
1755    }
1756    PrintF("\n");
1757  }
1758#endif  // DEBUG
1759
1760  if (FLAG_log_gc) {
1761    Isolate* isolate = ISOLATE;
1762    DoReportStatistics(isolate, allocated_histogram_, "allocated");
1763    DoReportStatistics(isolate, promoted_histogram_, "promoted");
1764  }
1765}
1766
1767
1768void NewSpace::RecordAllocation(HeapObject* obj) {
1769  InstanceType type = obj->map()->instance_type();
1770  ASSERT(0 <= type && type <= LAST_TYPE);
1771  allocated_histogram_[type].increment_number(1);
1772  allocated_histogram_[type].increment_bytes(obj->Size());
1773}
1774
1775
1776void NewSpace::RecordPromotion(HeapObject* obj) {
1777  InstanceType type = obj->map()->instance_type();
1778  ASSERT(0 <= type && type <= LAST_TYPE);
1779  promoted_histogram_[type].increment_number(1);
1780  promoted_histogram_[type].increment_bytes(obj->Size());
1781}
1782
1783// -----------------------------------------------------------------------------
1784// Free lists for old object spaces implementation
1785
1786void FreeListNode::set_size(Heap* heap, int size_in_bytes) {
1787  ASSERT(size_in_bytes > 0);
1788  ASSERT(IsAligned(size_in_bytes, kPointerSize));
1789
1790  // We write a map and possibly size information to the block.  If the block
1791  // is big enough to be a FreeSpace with at least one extra word (the next
1792  // pointer), we set its map to be the free space map and its size to an
1793  // appropriate array length for the desired size from HeapObject::Size().
1794  // If the block is too small (eg, one or two words), to hold both a size
1795  // field and a next pointer, we give it a filler map that gives it the
1796  // correct size.
1797  if (size_in_bytes > FreeSpace::kHeaderSize) {
1798    set_map_no_write_barrier(heap->raw_unchecked_free_space_map());
1799    // Can't use FreeSpace::cast because it fails during deserialization.
1800    FreeSpace* this_as_free_space = reinterpret_cast<FreeSpace*>(this);
1801    this_as_free_space->set_size(size_in_bytes);
1802  } else if (size_in_bytes == kPointerSize) {
1803    set_map_no_write_barrier(heap->raw_unchecked_one_pointer_filler_map());
1804  } else if (size_in_bytes == 2 * kPointerSize) {
1805    set_map_no_write_barrier(heap->raw_unchecked_two_pointer_filler_map());
1806  } else {
1807    UNREACHABLE();
1808  }
1809  // We would like to ASSERT(Size() == size_in_bytes) but this would fail during
1810  // deserialization because the free space map is not done yet.
1811}
1812
1813
1814FreeListNode* FreeListNode::next() {
1815  ASSERT(IsFreeListNode(this));
1816  if (map() == HEAP->raw_unchecked_free_space_map()) {
1817    ASSERT(map() == NULL || Size() >= kNextOffset + kPointerSize);
1818    return reinterpret_cast<FreeListNode*>(
1819        Memory::Address_at(address() + kNextOffset));
1820  } else {
1821    return reinterpret_cast<FreeListNode*>(
1822        Memory::Address_at(address() + kPointerSize));
1823  }
1824}
1825
1826
1827FreeListNode** FreeListNode::next_address() {
1828  ASSERT(IsFreeListNode(this));
1829  if (map() == HEAP->raw_unchecked_free_space_map()) {
1830    ASSERT(Size() >= kNextOffset + kPointerSize);
1831    return reinterpret_cast<FreeListNode**>(address() + kNextOffset);
1832  } else {
1833    return reinterpret_cast<FreeListNode**>(address() + kPointerSize);
1834  }
1835}
1836
1837
1838void FreeListNode::set_next(FreeListNode* next) {
1839  ASSERT(IsFreeListNode(this));
1840  // While we are booting the VM the free space map will actually be null.  So
1841  // we have to make sure that we don't try to use it for anything at that
1842  // stage.
1843  if (map() == HEAP->raw_unchecked_free_space_map()) {
1844    ASSERT(map() == NULL || Size() >= kNextOffset + kPointerSize);
1845    Memory::Address_at(address() + kNextOffset) =
1846        reinterpret_cast<Address>(next);
1847  } else {
1848    Memory::Address_at(address() + kPointerSize) =
1849        reinterpret_cast<Address>(next);
1850  }
1851}
1852
1853
1854FreeList::FreeList(PagedSpace* owner)
1855    : owner_(owner), heap_(owner->heap()) {
1856  Reset();
1857}
1858
1859
1860void FreeList::Reset() {
1861  available_ = 0;
1862  small_list_ = NULL;
1863  medium_list_ = NULL;
1864  large_list_ = NULL;
1865  huge_list_ = NULL;
1866}
1867
1868
1869int FreeList::Free(Address start, int size_in_bytes) {
1870  if (size_in_bytes == 0) return 0;
1871  FreeListNode* node = FreeListNode::FromAddress(start);
1872  node->set_size(heap_, size_in_bytes);
1873
1874  // Early return to drop too-small blocks on the floor.
1875  if (size_in_bytes < kSmallListMin) return size_in_bytes;
1876
1877  // Insert other blocks at the head of a free list of the appropriate
1878  // magnitude.
1879  if (size_in_bytes <= kSmallListMax) {
1880    node->set_next(small_list_);
1881    small_list_ = node;
1882  } else if (size_in_bytes <= kMediumListMax) {
1883    node->set_next(medium_list_);
1884    medium_list_ = node;
1885  } else if (size_in_bytes <= kLargeListMax) {
1886    node->set_next(large_list_);
1887    large_list_ = node;
1888  } else {
1889    node->set_next(huge_list_);
1890    huge_list_ = node;
1891  }
1892  available_ += size_in_bytes;
1893  ASSERT(IsVeryLong() || available_ == SumFreeLists());
1894  return 0;
1895}
1896
1897
1898FreeListNode* FreeList::PickNodeFromList(FreeListNode** list, int* node_size) {
1899  FreeListNode* node = *list;
1900
1901  if (node == NULL) return NULL;
1902
1903  while (node != NULL &&
1904         Page::FromAddress(node->address())->IsEvacuationCandidate()) {
1905    available_ -= node->Size();
1906    node = node->next();
1907  }
1908
1909  if (node != NULL) {
1910    *node_size = node->Size();
1911    *list = node->next();
1912  } else {
1913    *list = NULL;
1914  }
1915
1916  return node;
1917}
1918
1919
1920FreeListNode* FreeList::FindNodeFor(int size_in_bytes, int* node_size) {
1921  FreeListNode* node = NULL;
1922
1923  if (size_in_bytes <= kSmallAllocationMax) {
1924    node = PickNodeFromList(&small_list_, node_size);
1925    if (node != NULL) return node;
1926  }
1927
1928  if (size_in_bytes <= kMediumAllocationMax) {
1929    node = PickNodeFromList(&medium_list_, node_size);
1930    if (node != NULL) return node;
1931  }
1932
1933  if (size_in_bytes <= kLargeAllocationMax) {
1934    node = PickNodeFromList(&large_list_, node_size);
1935    if (node != NULL) return node;
1936  }
1937
1938  for (FreeListNode** cur = &huge_list_;
1939       *cur != NULL;
1940       cur = (*cur)->next_address()) {
1941    FreeListNode* cur_node = *cur;
1942    while (cur_node != NULL &&
1943           Page::FromAddress(cur_node->address())->IsEvacuationCandidate()) {
1944      available_ -= reinterpret_cast<FreeSpace*>(cur_node)->Size();
1945      cur_node = cur_node->next();
1946    }
1947
1948    *cur = cur_node;
1949    if (cur_node == NULL) break;
1950
1951    ASSERT((*cur)->map() == HEAP->raw_unchecked_free_space_map());
1952    FreeSpace* cur_as_free_space = reinterpret_cast<FreeSpace*>(*cur);
1953    int size = cur_as_free_space->Size();
1954    if (size >= size_in_bytes) {
1955      // Large enough node found.  Unlink it from the list.
1956      node = *cur;
1957      *node_size = size;
1958      *cur = node->next();
1959      break;
1960    }
1961  }
1962
1963  return node;
1964}
1965
1966
1967// Allocation on the old space free list.  If it succeeds then a new linear
1968// allocation space has been set up with the top and limit of the space.  If
1969// the allocation fails then NULL is returned, and the caller can perform a GC
1970// or allocate a new page before retrying.
1971HeapObject* FreeList::Allocate(int size_in_bytes) {
1972  ASSERT(0 < size_in_bytes);
1973  ASSERT(size_in_bytes <= kMaxBlockSize);
1974  ASSERT(IsAligned(size_in_bytes, kPointerSize));
1975  // Don't free list allocate if there is linear space available.
1976  ASSERT(owner_->limit() - owner_->top() < size_in_bytes);
1977
1978  int new_node_size = 0;
1979  FreeListNode* new_node = FindNodeFor(size_in_bytes, &new_node_size);
1980  if (new_node == NULL) return NULL;
1981
1982  available_ -= new_node_size;
1983  ASSERT(IsVeryLong() || available_ == SumFreeLists());
1984
1985  int bytes_left = new_node_size - size_in_bytes;
1986  ASSERT(bytes_left >= 0);
1987
1988  int old_linear_size = static_cast<int>(owner_->limit() - owner_->top());
1989  // Mark the old linear allocation area with a free space map so it can be
1990  // skipped when scanning the heap.  This also puts it back in the free list
1991  // if it is big enough.
1992  owner_->Free(owner_->top(), old_linear_size);
1993
1994#ifdef DEBUG
1995  for (int i = 0; i < size_in_bytes / kPointerSize; i++) {
1996    reinterpret_cast<Object**>(new_node->address())[i] = Smi::FromInt(0);
1997  }
1998#endif
1999
2000  owner_->heap()->incremental_marking()->OldSpaceStep(
2001      size_in_bytes - old_linear_size);
2002
2003  // The old-space-step might have finished sweeping and restarted marking.
2004  // Verify that it did not turn the page of the new node into an evacuation
2005  // candidate.
2006  ASSERT(!MarkCompactCollector::IsOnEvacuationCandidate(new_node));
2007
2008  const int kThreshold = IncrementalMarking::kAllocatedThreshold;
2009
2010  // Memory in the linear allocation area is counted as allocated.  We may free
2011  // a little of this again immediately - see below.
2012  owner_->Allocate(new_node_size);
2013
2014  if (bytes_left > kThreshold &&
2015      owner_->heap()->incremental_marking()->IsMarkingIncomplete() &&
2016      FLAG_incremental_marking_steps) {
2017    int linear_size = owner_->RoundSizeDownToObjectAlignment(kThreshold);
2018    // We don't want to give too large linear areas to the allocator while
2019    // incremental marking is going on, because we won't check again whether
2020    // we want to do another increment until the linear area is used up.
2021    owner_->Free(new_node->address() + size_in_bytes + linear_size,
2022                 new_node_size - size_in_bytes - linear_size);
2023    owner_->SetTop(new_node->address() + size_in_bytes,
2024                   new_node->address() + size_in_bytes + linear_size);
2025  } else if (bytes_left > 0) {
2026    // Normally we give the rest of the node to the allocator as its new
2027    // linear allocation area.
2028    owner_->SetTop(new_node->address() + size_in_bytes,
2029                   new_node->address() + new_node_size);
2030  } else {
2031    // TODO(gc) Try not freeing linear allocation region when bytes_left
2032    // are zero.
2033    owner_->SetTop(NULL, NULL);
2034  }
2035
2036  return new_node;
2037}
2038
2039
2040static intptr_t CountFreeListItemsInList(FreeListNode* n, Page* p) {
2041  intptr_t sum = 0;
2042  while (n != NULL) {
2043    if (Page::FromAddress(n->address()) == p) {
2044      FreeSpace* free_space = reinterpret_cast<FreeSpace*>(n);
2045      sum += free_space->Size();
2046    }
2047    n = n->next();
2048  }
2049  return sum;
2050}
2051
2052
2053void FreeList::CountFreeListItems(Page* p, SizeStats* sizes) {
2054  sizes->huge_size_ = CountFreeListItemsInList(huge_list_, p);
2055  if (sizes->huge_size_ < p->area_size()) {
2056    sizes->small_size_ = CountFreeListItemsInList(small_list_, p);
2057    sizes->medium_size_ = CountFreeListItemsInList(medium_list_, p);
2058    sizes->large_size_ = CountFreeListItemsInList(large_list_, p);
2059  } else {
2060    sizes->small_size_ = 0;
2061    sizes->medium_size_ = 0;
2062    sizes->large_size_ = 0;
2063  }
2064}
2065
2066
2067static intptr_t EvictFreeListItemsInList(FreeListNode** n, Page* p) {
2068  intptr_t sum = 0;
2069  while (*n != NULL) {
2070    if (Page::FromAddress((*n)->address()) == p) {
2071      FreeSpace* free_space = reinterpret_cast<FreeSpace*>(*n);
2072      sum += free_space->Size();
2073      *n = (*n)->next();
2074    } else {
2075      n = (*n)->next_address();
2076    }
2077  }
2078  return sum;
2079}
2080
2081
2082intptr_t FreeList::EvictFreeListItems(Page* p) {
2083  intptr_t sum = EvictFreeListItemsInList(&huge_list_, p);
2084
2085  if (sum < p->area_size()) {
2086    sum += EvictFreeListItemsInList(&small_list_, p) +
2087        EvictFreeListItemsInList(&medium_list_, p) +
2088        EvictFreeListItemsInList(&large_list_, p);
2089  }
2090
2091  available_ -= static_cast<int>(sum);
2092
2093  return sum;
2094}
2095
2096
2097#ifdef DEBUG
2098intptr_t FreeList::SumFreeList(FreeListNode* cur) {
2099  intptr_t sum = 0;
2100  while (cur != NULL) {
2101    ASSERT(cur->map() == HEAP->raw_unchecked_free_space_map());
2102    FreeSpace* cur_as_free_space = reinterpret_cast<FreeSpace*>(cur);
2103    sum += cur_as_free_space->Size();
2104    cur = cur->next();
2105  }
2106  return sum;
2107}
2108
2109
2110static const int kVeryLongFreeList = 500;
2111
2112
2113int FreeList::FreeListLength(FreeListNode* cur) {
2114  int length = 0;
2115  while (cur != NULL) {
2116    length++;
2117    cur = cur->next();
2118    if (length == kVeryLongFreeList) return length;
2119  }
2120  return length;
2121}
2122
2123
2124bool FreeList::IsVeryLong() {
2125  if (FreeListLength(small_list_) == kVeryLongFreeList) return  true;
2126  if (FreeListLength(medium_list_) == kVeryLongFreeList) return  true;
2127  if (FreeListLength(large_list_) == kVeryLongFreeList) return  true;
2128  if (FreeListLength(huge_list_) == kVeryLongFreeList) return  true;
2129  return false;
2130}
2131
2132
2133// This can take a very long time because it is linear in the number of entries
2134// on the free list, so it should not be called if FreeListLength returns
2135// kVeryLongFreeList.
2136intptr_t FreeList::SumFreeLists() {
2137  intptr_t sum = SumFreeList(small_list_);
2138  sum += SumFreeList(medium_list_);
2139  sum += SumFreeList(large_list_);
2140  sum += SumFreeList(huge_list_);
2141  return sum;
2142}
2143#endif
2144
2145
2146// -----------------------------------------------------------------------------
2147// OldSpace implementation
2148
2149bool NewSpace::ReserveSpace(int bytes) {
2150  // We can't reliably unpack a partial snapshot that needs more new space
2151  // space than the minimum NewSpace size.  The limit can be set lower than
2152  // the end of new space either because there is more space on the next page
2153  // or because we have lowered the limit in order to get periodic incremental
2154  // marking.  The most reliable way to ensure that there is linear space is
2155  // to do the allocation, then rewind the limit.
2156  ASSERT(bytes <= InitialCapacity());
2157  MaybeObject* maybe = AllocateRaw(bytes);
2158  Object* object = NULL;
2159  if (!maybe->ToObject(&object)) return false;
2160  HeapObject* allocation = HeapObject::cast(object);
2161  Address top = allocation_info_.top;
2162  if ((top - bytes) == allocation->address()) {
2163    allocation_info_.top = allocation->address();
2164    return true;
2165  }
2166  // There may be a borderline case here where the allocation succeeded, but
2167  // the limit and top have moved on to a new page.  In that case we try again.
2168  return ReserveSpace(bytes);
2169}
2170
2171
2172void PagedSpace::PrepareForMarkCompact() {
2173  // We don't have a linear allocation area while sweeping.  It will be restored
2174  // on the first allocation after the sweep.
2175  // Mark the old linear allocation area with a free space map so it can be
2176  // skipped when scanning the heap.
2177  int old_linear_size = static_cast<int>(limit() - top());
2178  Free(top(), old_linear_size);
2179  SetTop(NULL, NULL);
2180
2181  // Stop lazy sweeping and clear marking bits for unswept pages.
2182  if (first_unswept_page_ != NULL) {
2183    Page* p = first_unswept_page_;
2184    do {
2185      // Do not use ShouldBeSweptLazily predicate here.
2186      // New evacuation candidates were selected but they still have
2187      // to be swept before collection starts.
2188      if (!p->WasSwept()) {
2189        Bitmap::Clear(p);
2190        if (FLAG_gc_verbose) {
2191          PrintF("Sweeping 0x%" V8PRIxPTR " lazily abandoned.\n",
2192                 reinterpret_cast<intptr_t>(p));
2193        }
2194      }
2195      p = p->next_page();
2196    } while (p != anchor());
2197  }
2198  first_unswept_page_ = Page::FromAddress(NULL);
2199  unswept_free_bytes_ = 0;
2200
2201  // Clear the free list before a full GC---it will be rebuilt afterward.
2202  free_list_.Reset();
2203}
2204
2205
2206bool PagedSpace::ReserveSpace(int size_in_bytes) {
2207  ASSERT(size_in_bytes <= AreaSize());
2208  ASSERT(size_in_bytes == RoundSizeDownToObjectAlignment(size_in_bytes));
2209  Address current_top = allocation_info_.top;
2210  Address new_top = current_top + size_in_bytes;
2211  if (new_top <= allocation_info_.limit) return true;
2212
2213  HeapObject* new_area = free_list_.Allocate(size_in_bytes);
2214  if (new_area == NULL) new_area = SlowAllocateRaw(size_in_bytes);
2215  if (new_area == NULL) return false;
2216
2217  int old_linear_size = static_cast<int>(limit() - top());
2218  // Mark the old linear allocation area with a free space so it can be
2219  // skipped when scanning the heap.  This also puts it back in the free list
2220  // if it is big enough.
2221  Free(top(), old_linear_size);
2222
2223  SetTop(new_area->address(), new_area->address() + size_in_bytes);
2224  Allocate(size_in_bytes);
2225  return true;
2226}
2227
2228
2229// You have to call this last, since the implementation from PagedSpace
2230// doesn't know that memory was 'promised' to large object space.
2231bool LargeObjectSpace::ReserveSpace(int bytes) {
2232  return heap()->OldGenerationCapacityAvailable() >= bytes &&
2233         (!heap()->incremental_marking()->IsStopped() ||
2234           heap()->OldGenerationSpaceAvailable() >= bytes);
2235}
2236
2237
2238bool PagedSpace::AdvanceSweeper(intptr_t bytes_to_sweep) {
2239  if (IsSweepingComplete()) return true;
2240
2241  intptr_t freed_bytes = 0;
2242  Page* p = first_unswept_page_;
2243  do {
2244    Page* next_page = p->next_page();
2245    if (ShouldBeSweptLazily(p)) {
2246      if (FLAG_gc_verbose) {
2247        PrintF("Sweeping 0x%" V8PRIxPTR " lazily advanced.\n",
2248               reinterpret_cast<intptr_t>(p));
2249      }
2250      DecreaseUnsweptFreeBytes(p);
2251      freed_bytes += MarkCompactCollector::SweepConservatively(this, p);
2252    }
2253    p = next_page;
2254  } while (p != anchor() && freed_bytes < bytes_to_sweep);
2255
2256  if (p == anchor()) {
2257    first_unswept_page_ = Page::FromAddress(NULL);
2258  } else {
2259    first_unswept_page_ = p;
2260  }
2261
2262  heap()->LowerOldGenLimits(freed_bytes);
2263
2264  heap()->FreeQueuedChunks();
2265
2266  return IsSweepingComplete();
2267}
2268
2269
2270void PagedSpace::EvictEvacuationCandidatesFromFreeLists() {
2271  if (allocation_info_.top >= allocation_info_.limit) return;
2272
2273  if (Page::FromAllocationTop(allocation_info_.top)->IsEvacuationCandidate()) {
2274    // Create filler object to keep page iterable if it was iterable.
2275    int remaining =
2276        static_cast<int>(allocation_info_.limit - allocation_info_.top);
2277    heap()->CreateFillerObjectAt(allocation_info_.top, remaining);
2278
2279    allocation_info_.top = NULL;
2280    allocation_info_.limit = NULL;
2281  }
2282}
2283
2284
2285HeapObject* PagedSpace::SlowAllocateRaw(int size_in_bytes) {
2286  // Allocation in this space has failed.
2287
2288  // If there are unswept pages advance lazy sweeper then sweep one page before
2289  // allocating a new page.
2290  if (first_unswept_page_->is_valid()) {
2291    AdvanceSweeper(size_in_bytes);
2292
2293    // Retry the free list allocation.
2294    HeapObject* object = free_list_.Allocate(size_in_bytes);
2295    if (object != NULL) return object;
2296  }
2297
2298  // Free list allocation failed and there is no next page.  Fail if we have
2299  // hit the old generation size limit that should cause a garbage
2300  // collection.
2301  if (!heap()->always_allocate() &&
2302      heap()->OldGenerationAllocationLimitReached()) {
2303    return NULL;
2304  }
2305
2306  // Try to expand the space and allocate in the new next page.
2307  if (Expand()) {
2308    return free_list_.Allocate(size_in_bytes);
2309  }
2310
2311  // Last ditch, sweep all the remaining pages to try to find space.  This may
2312  // cause a pause.
2313  if (!IsSweepingComplete()) {
2314    AdvanceSweeper(kMaxInt);
2315
2316    // Retry the free list allocation.
2317    HeapObject* object = free_list_.Allocate(size_in_bytes);
2318    if (object != NULL) return object;
2319  }
2320
2321  // Finally, fail.
2322  return NULL;
2323}
2324
2325
2326#ifdef DEBUG
2327void PagedSpace::ReportCodeStatistics() {
2328  Isolate* isolate = Isolate::Current();
2329  CommentStatistic* comments_statistics =
2330      isolate->paged_space_comments_statistics();
2331  ReportCodeKindStatistics();
2332  PrintF("Code comment statistics (\"   [ comment-txt   :    size/   "
2333         "count  (average)\"):\n");
2334  for (int i = 0; i <= CommentStatistic::kMaxComments; i++) {
2335    const CommentStatistic& cs = comments_statistics[i];
2336    if (cs.size > 0) {
2337      PrintF("   %-30s: %10d/%6d     (%d)\n", cs.comment, cs.size, cs.count,
2338             cs.size/cs.count);
2339    }
2340  }
2341  PrintF("\n");
2342}
2343
2344
2345void PagedSpace::ResetCodeStatistics() {
2346  Isolate* isolate = Isolate::Current();
2347  CommentStatistic* comments_statistics =
2348      isolate->paged_space_comments_statistics();
2349  ClearCodeKindStatistics();
2350  for (int i = 0; i < CommentStatistic::kMaxComments; i++) {
2351    comments_statistics[i].Clear();
2352  }
2353  comments_statistics[CommentStatistic::kMaxComments].comment = "Unknown";
2354  comments_statistics[CommentStatistic::kMaxComments].size = 0;
2355  comments_statistics[CommentStatistic::kMaxComments].count = 0;
2356}
2357
2358
2359// Adds comment to 'comment_statistics' table. Performance OK as long as
2360// 'kMaxComments' is small
2361static void EnterComment(Isolate* isolate, const char* comment, int delta) {
2362  CommentStatistic* comments_statistics =
2363      isolate->paged_space_comments_statistics();
2364  // Do not count empty comments
2365  if (delta <= 0) return;
2366  CommentStatistic* cs = &comments_statistics[CommentStatistic::kMaxComments];
2367  // Search for a free or matching entry in 'comments_statistics': 'cs'
2368  // points to result.
2369  for (int i = 0; i < CommentStatistic::kMaxComments; i++) {
2370    if (comments_statistics[i].comment == NULL) {
2371      cs = &comments_statistics[i];
2372      cs->comment = comment;
2373      break;
2374    } else if (strcmp(comments_statistics[i].comment, comment) == 0) {
2375      cs = &comments_statistics[i];
2376      break;
2377    }
2378  }
2379  // Update entry for 'comment'
2380  cs->size += delta;
2381  cs->count += 1;
2382}
2383
2384
2385// Call for each nested comment start (start marked with '[ xxx', end marked
2386// with ']'.  RelocIterator 'it' must point to a comment reloc info.
2387static void CollectCommentStatistics(Isolate* isolate, RelocIterator* it) {
2388  ASSERT(!it->done());
2389  ASSERT(it->rinfo()->rmode() == RelocInfo::COMMENT);
2390  const char* tmp = reinterpret_cast<const char*>(it->rinfo()->data());
2391  if (tmp[0] != '[') {
2392    // Not a nested comment; skip
2393    return;
2394  }
2395
2396  // Search for end of nested comment or a new nested comment
2397  const char* const comment_txt =
2398      reinterpret_cast<const char*>(it->rinfo()->data());
2399  const byte* prev_pc = it->rinfo()->pc();
2400  int flat_delta = 0;
2401  it->next();
2402  while (true) {
2403    // All nested comments must be terminated properly, and therefore exit
2404    // from loop.
2405    ASSERT(!it->done());
2406    if (it->rinfo()->rmode() == RelocInfo::COMMENT) {
2407      const char* const txt =
2408          reinterpret_cast<const char*>(it->rinfo()->data());
2409      flat_delta += static_cast<int>(it->rinfo()->pc() - prev_pc);
2410      if (txt[0] == ']') break;  // End of nested  comment
2411      // A new comment
2412      CollectCommentStatistics(isolate, it);
2413      // Skip code that was covered with previous comment
2414      prev_pc = it->rinfo()->pc();
2415    }
2416    it->next();
2417  }
2418  EnterComment(isolate, comment_txt, flat_delta);
2419}
2420
2421
2422// Collects code size statistics:
2423// - by code kind
2424// - by code comment
2425void PagedSpace::CollectCodeStatistics() {
2426  Isolate* isolate = heap()->isolate();
2427  HeapObjectIterator obj_it(this);
2428  for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) {
2429    if (obj->IsCode()) {
2430      Code* code = Code::cast(obj);
2431      isolate->code_kind_statistics()[code->kind()] += code->Size();
2432      RelocIterator it(code);
2433      int delta = 0;
2434      const byte* prev_pc = code->instruction_start();
2435      while (!it.done()) {
2436        if (it.rinfo()->rmode() == RelocInfo::COMMENT) {
2437          delta += static_cast<int>(it.rinfo()->pc() - prev_pc);
2438          CollectCommentStatistics(isolate, &it);
2439          prev_pc = it.rinfo()->pc();
2440        }
2441        it.next();
2442      }
2443
2444      ASSERT(code->instruction_start() <= prev_pc &&
2445             prev_pc <= code->instruction_end());
2446      delta += static_cast<int>(code->instruction_end() - prev_pc);
2447      EnterComment(isolate, "NoComment", delta);
2448    }
2449  }
2450}
2451
2452
2453void PagedSpace::ReportStatistics() {
2454  int pct = static_cast<int>(Available() * 100 / Capacity());
2455  PrintF("  capacity: %" V8_PTR_PREFIX "d"
2456             ", waste: %" V8_PTR_PREFIX "d"
2457             ", available: %" V8_PTR_PREFIX "d, %%%d\n",
2458         Capacity(), Waste(), Available(), pct);
2459
2460  if (was_swept_conservatively_) return;
2461  ClearHistograms();
2462  HeapObjectIterator obj_it(this);
2463  for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next())
2464    CollectHistogramInfo(obj);
2465  ReportHistogram(true);
2466}
2467#endif
2468
2469// -----------------------------------------------------------------------------
2470// FixedSpace implementation
2471
2472void FixedSpace::PrepareForMarkCompact() {
2473  // Call prepare of the super class.
2474  PagedSpace::PrepareForMarkCompact();
2475
2476  // During a non-compacting collection, everything below the linear
2477  // allocation pointer except wasted top-of-page blocks is considered
2478  // allocated and we will rediscover available bytes during the
2479  // collection.
2480  accounting_stats_.AllocateBytes(free_list_.available());
2481
2482  // Clear the free list before a full GC---it will be rebuilt afterward.
2483  free_list_.Reset();
2484}
2485
2486
2487// -----------------------------------------------------------------------------
2488// MapSpace implementation
2489
2490#ifdef DEBUG
2491void MapSpace::VerifyObject(HeapObject* object) {
2492  // The object should be a map or a free-list node.
2493  ASSERT(object->IsMap() || object->IsFreeSpace());
2494}
2495#endif
2496
2497
2498// -----------------------------------------------------------------------------
2499// GlobalPropertyCellSpace implementation
2500
2501#ifdef DEBUG
2502void CellSpace::VerifyObject(HeapObject* object) {
2503  // The object should be a global object property cell or a free-list node.
2504  ASSERT(object->IsJSGlobalPropertyCell() ||
2505         object->map() == heap()->two_pointer_filler_map());
2506}
2507#endif
2508
2509
2510// -----------------------------------------------------------------------------
2511// LargeObjectIterator
2512
2513LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) {
2514  current_ = space->first_page_;
2515  size_func_ = NULL;
2516}
2517
2518
2519LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space,
2520                                         HeapObjectCallback size_func) {
2521  current_ = space->first_page_;
2522  size_func_ = size_func;
2523}
2524
2525
2526HeapObject* LargeObjectIterator::Next() {
2527  if (current_ == NULL) return NULL;
2528
2529  HeapObject* object = current_->GetObject();
2530  current_ = current_->next_page();
2531  return object;
2532}
2533
2534
2535// -----------------------------------------------------------------------------
2536// LargeObjectSpace
2537static bool ComparePointers(void* key1, void* key2) {
2538    return key1 == key2;
2539}
2540
2541
2542LargeObjectSpace::LargeObjectSpace(Heap* heap,
2543                                   intptr_t max_capacity,
2544                                   AllocationSpace id)
2545    : Space(heap, id, NOT_EXECUTABLE),  // Managed on a per-allocation basis
2546      max_capacity_(max_capacity),
2547      first_page_(NULL),
2548      size_(0),
2549      page_count_(0),
2550      objects_size_(0),
2551      chunk_map_(ComparePointers, 1024) {}
2552
2553
2554bool LargeObjectSpace::SetUp() {
2555  first_page_ = NULL;
2556  size_ = 0;
2557  page_count_ = 0;
2558  objects_size_ = 0;
2559  chunk_map_.Clear();
2560  return true;
2561}
2562
2563
2564void LargeObjectSpace::TearDown() {
2565  while (first_page_ != NULL) {
2566    LargePage* page = first_page_;
2567    first_page_ = first_page_->next_page();
2568    LOG(heap()->isolate(), DeleteEvent("LargeObjectChunk", page->address()));
2569
2570    ObjectSpace space = static_cast<ObjectSpace>(1 << identity());
2571    heap()->isolate()->memory_allocator()->PerformAllocationCallback(
2572        space, kAllocationActionFree, page->size());
2573    heap()->isolate()->memory_allocator()->Free(page);
2574  }
2575  SetUp();
2576}
2577
2578
2579MaybeObject* LargeObjectSpace::AllocateRaw(int object_size,
2580                                           Executability executable) {
2581  // Check if we want to force a GC before growing the old space further.
2582  // If so, fail the allocation.
2583  if (!heap()->always_allocate() &&
2584      heap()->OldGenerationAllocationLimitReached()) {
2585    return Failure::RetryAfterGC(identity());
2586  }
2587
2588  if (Size() + object_size > max_capacity_) {
2589    return Failure::RetryAfterGC(identity());
2590  }
2591
2592  LargePage* page = heap()->isolate()->memory_allocator()->
2593      AllocateLargePage(object_size, executable, this);
2594  if (page == NULL) return Failure::RetryAfterGC(identity());
2595  ASSERT(page->area_size() >= object_size);
2596
2597  size_ += static_cast<int>(page->size());
2598  objects_size_ += object_size;
2599  page_count_++;
2600  page->set_next_page(first_page_);
2601  first_page_ = page;
2602
2603  // Register all MemoryChunk::kAlignment-aligned chunks covered by
2604  // this large page in the chunk map.
2605  uintptr_t base = reinterpret_cast<uintptr_t>(page) / MemoryChunk::kAlignment;
2606  uintptr_t limit = base + (page->size() - 1) / MemoryChunk::kAlignment;
2607  for (uintptr_t key = base; key <= limit; key++) {
2608    HashMap::Entry* entry = chunk_map_.Lookup(reinterpret_cast<void*>(key),
2609                                              static_cast<uint32_t>(key),
2610                                              true);
2611    ASSERT(entry != NULL);
2612    entry->value = page;
2613  }
2614
2615  HeapObject* object = page->GetObject();
2616
2617#ifdef DEBUG
2618  // Make the object consistent so the heap can be vefified in OldSpaceStep.
2619  reinterpret_cast<Object**>(object->address())[0] =
2620      heap()->fixed_array_map();
2621  reinterpret_cast<Object**>(object->address())[1] = Smi::FromInt(0);
2622#endif
2623
2624  heap()->incremental_marking()->OldSpaceStep(object_size);
2625  return object;
2626}
2627
2628
2629// GC support
2630MaybeObject* LargeObjectSpace::FindObject(Address a) {
2631  LargePage* page = FindPage(a);
2632  if (page != NULL) {
2633    return page->GetObject();
2634  }
2635  return Failure::Exception();
2636}
2637
2638
2639LargePage* LargeObjectSpace::FindPage(Address a) {
2640  uintptr_t key = reinterpret_cast<uintptr_t>(a) / MemoryChunk::kAlignment;
2641  HashMap::Entry* e = chunk_map_.Lookup(reinterpret_cast<void*>(key),
2642                                        static_cast<uint32_t>(key),
2643                                        false);
2644  if (e != NULL) {
2645    ASSERT(e->value != NULL);
2646    LargePage* page = reinterpret_cast<LargePage*>(e->value);
2647    ASSERT(page->is_valid());
2648    if (page->Contains(a)) {
2649      return page;
2650    }
2651  }
2652  return NULL;
2653}
2654
2655
2656void LargeObjectSpace::FreeUnmarkedObjects() {
2657  LargePage* previous = NULL;
2658  LargePage* current = first_page_;
2659  while (current != NULL) {
2660    HeapObject* object = current->GetObject();
2661    // Can this large page contain pointers to non-trivial objects.  No other
2662    // pointer object is this big.
2663    bool is_pointer_object = object->IsFixedArray();
2664    MarkBit mark_bit = Marking::MarkBitFrom(object);
2665    if (mark_bit.Get()) {
2666      mark_bit.Clear();
2667      MemoryChunk::IncrementLiveBytesFromGC(object->address(), -object->Size());
2668      previous = current;
2669      current = current->next_page();
2670    } else {
2671      LargePage* page = current;
2672      // Cut the chunk out from the chunk list.
2673      current = current->next_page();
2674      if (previous == NULL) {
2675        first_page_ = current;
2676      } else {
2677        previous->set_next_page(current);
2678      }
2679
2680      // Free the chunk.
2681      heap()->mark_compact_collector()->ReportDeleteIfNeeded(
2682          object, heap()->isolate());
2683      size_ -= static_cast<int>(page->size());
2684      objects_size_ -= object->Size();
2685      page_count_--;
2686
2687      // Remove entries belonging to this page.
2688      // Use variable alignment to help pass length check (<= 80 characters)
2689      // of single line in tools/presubmit.py.
2690      const intptr_t alignment = MemoryChunk::kAlignment;
2691      uintptr_t base = reinterpret_cast<uintptr_t>(page)/alignment;
2692      uintptr_t limit = base + (page->size()-1)/alignment;
2693      for (uintptr_t key = base; key <= limit; key++) {
2694        chunk_map_.Remove(reinterpret_cast<void*>(key),
2695                          static_cast<uint32_t>(key));
2696      }
2697
2698      if (is_pointer_object) {
2699        heap()->QueueMemoryChunkForFree(page);
2700      } else {
2701        heap()->isolate()->memory_allocator()->Free(page);
2702      }
2703    }
2704  }
2705  heap()->FreeQueuedChunks();
2706}
2707
2708
2709bool LargeObjectSpace::Contains(HeapObject* object) {
2710  Address address = object->address();
2711  MemoryChunk* chunk = MemoryChunk::FromAddress(address);
2712
2713  bool owned = (chunk->owner() == this);
2714
2715  SLOW_ASSERT(!owned || !FindObject(address)->IsFailure());
2716
2717  return owned;
2718}
2719
2720
2721#ifdef DEBUG
2722// We do not assume that the large object iterator works, because it depends
2723// on the invariants we are checking during verification.
2724void LargeObjectSpace::Verify() {
2725  for (LargePage* chunk = first_page_;
2726       chunk != NULL;
2727       chunk = chunk->next_page()) {
2728    // Each chunk contains an object that starts at the large object page's
2729    // object area start.
2730    HeapObject* object = chunk->GetObject();
2731    Page* page = Page::FromAddress(object->address());
2732    ASSERT(object->address() == page->area_start());
2733
2734    // The first word should be a map, and we expect all map pointers to be
2735    // in map space.
2736    Map* map = object->map();
2737    ASSERT(map->IsMap());
2738    ASSERT(heap()->map_space()->Contains(map));
2739
2740    // We have only code, sequential strings, external strings
2741    // (sequential strings that have been morphed into external
2742    // strings), fixed arrays, and byte arrays in large object space.
2743    ASSERT(object->IsCode() || object->IsSeqString() ||
2744           object->IsExternalString() || object->IsFixedArray() ||
2745           object->IsFixedDoubleArray() || object->IsByteArray());
2746
2747    // The object itself should look OK.
2748    object->Verify();
2749
2750    // Byte arrays and strings don't have interior pointers.
2751    if (object->IsCode()) {
2752      VerifyPointersVisitor code_visitor;
2753      object->IterateBody(map->instance_type(),
2754                          object->Size(),
2755                          &code_visitor);
2756    } else if (object->IsFixedArray()) {
2757      FixedArray* array = FixedArray::cast(object);
2758      for (int j = 0; j < array->length(); j++) {
2759        Object* element = array->get(j);
2760        if (element->IsHeapObject()) {
2761          HeapObject* element_object = HeapObject::cast(element);
2762          ASSERT(heap()->Contains(element_object));
2763          ASSERT(element_object->map()->IsMap());
2764        }
2765      }
2766    }
2767  }
2768}
2769
2770
2771void LargeObjectSpace::Print() {
2772  LargeObjectIterator it(this);
2773  for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) {
2774    obj->Print();
2775  }
2776}
2777
2778
2779void LargeObjectSpace::ReportStatistics() {
2780  PrintF("  size: %" V8_PTR_PREFIX "d\n", size_);
2781  int num_objects = 0;
2782  ClearHistograms();
2783  LargeObjectIterator it(this);
2784  for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) {
2785    num_objects++;
2786    CollectHistogramInfo(obj);
2787  }
2788
2789  PrintF("  number of objects %d, "
2790         "size of objects %" V8_PTR_PREFIX "d\n", num_objects, objects_size_);
2791  if (num_objects > 0) ReportHistogram(false);
2792}
2793
2794
2795void LargeObjectSpace::CollectCodeStatistics() {
2796  Isolate* isolate = heap()->isolate();
2797  LargeObjectIterator obj_it(this);
2798  for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) {
2799    if (obj->IsCode()) {
2800      Code* code = Code::cast(obj);
2801      isolate->code_kind_statistics()[code->kind()] += code->Size();
2802    }
2803  }
2804}
2805
2806
2807void Page::Print() {
2808  // Make a best-effort to print the objects in the page.
2809  PrintF("Page@%p in %s\n",
2810         this->address(),
2811         AllocationSpaceName(this->owner()->identity()));
2812  printf(" --------------------------------------\n");
2813  HeapObjectIterator objects(this, heap()->GcSafeSizeOfOldObjectFunction());
2814  unsigned mark_size = 0;
2815  for (HeapObject* object = objects.Next();
2816       object != NULL;
2817       object = objects.Next()) {
2818    bool is_marked = Marking::MarkBitFrom(object).Get();
2819    PrintF(" %c ", (is_marked ? '!' : ' '));  // Indent a little.
2820    if (is_marked) {
2821      mark_size += heap()->GcSafeSizeOfOldObjectFunction()(object);
2822    }
2823    object->ShortPrint();
2824    PrintF("\n");
2825  }
2826  printf(" --------------------------------------\n");
2827  printf(" Marked: %x, LiveCount: %x\n", mark_size, LiveBytes());
2828}
2829
2830#endif  // DEBUG
2831
2832} }  // namespace v8::internal
2833