| Index: src/heap/spaces.h | 
| diff --git a/src/heap/spaces.h b/src/heap/spaces.h | 
| index 8ac8103f17d98f5b2ce52b73a176b41b5829b02b..98cc365b81d1200bd6b364c41220210493e66fcf 100644 | 
| --- a/src/heap/spaces.h | 
| +++ b/src/heap/spaces.h | 
| @@ -27,6 +27,7 @@ class FreeList; | 
| class Isolate; | 
| class MemoryAllocator; | 
| class MemoryChunk; | 
| +class Page; | 
| class PagedSpace; | 
| class SemiSpace; | 
| class SkipList; | 
| @@ -287,6 +288,113 @@ class Bitmap { | 
| } | 
| }; | 
|  | 
| +enum FreeListCategoryType { | 
| +  kTiniest, | 
| +  kTiny, | 
| +  kSmall, | 
| +  kMedium, | 
| +  kLarge, | 
| +  kHuge, | 
| + | 
| +  kFirstCategory = kTiniest, | 
| +  kLastCategory = kHuge, | 
| +  kNumberOfCategories = kLastCategory + 1, | 
| +  kInvalidCategory | 
| +}; | 
| + | 
| +enum FreeMode { kLinkCategory, kDoNotLinkCategory }; | 
| + | 
| +// A free list category maintains a linked list of free memory blocks. | 
| +class FreeListCategory { | 
| + public: | 
| +  static const int kSize = kIntSize +      // FreeListCategoryType type_ | 
| +                           kIntSize +      // int available_ | 
| +                           kPointerSize +  // FreeSpace* top_ | 
| +                           kPointerSize +  // FreeListCategory* prev_ | 
| +                           kPointerSize;   // FreeListCategory* next_ | 
| + | 
| +  FreeListCategory() | 
| +      : type_(kInvalidCategory), | 
| +        available_(0), | 
| +        top_(nullptr), | 
| +        prev_(nullptr), | 
| +        next_(nullptr) {} | 
| + | 
| +  void Initialize(FreeListCategoryType type) { | 
| +    type_ = type; | 
| +    available_ = 0; | 
| +    top_ = nullptr; | 
| +    prev_ = nullptr; | 
| +    next_ = nullptr; | 
| +  } | 
| + | 
| +  void Invalidate(); | 
| + | 
| +  void Reset(); | 
| + | 
| +  void ResetStats() { Reset(); } | 
| + | 
| +  void RepairFreeList(Heap* heap); | 
| + | 
| +  // Relinks the category into the currently owning free list. Requires that the | 
| +  // category is currently unlinked. | 
| +  void Relink(); | 
| + | 
| +  bool Free(FreeSpace* node, int size_in_bytes, FreeMode mode); | 
| + | 
| +  // Picks a node from the list and stores its size in |node_size|. Returns | 
| +  // nullptr if the category is empty. | 
| +  FreeSpace* PickNodeFromList(int* node_size); | 
| + | 
| +  // Performs a single try to pick a node of at least |minimum_size| from the | 
| +  // category. Stores the actual size in |node_size|. Returns nullptr if no | 
| +  // node is found. | 
| +  FreeSpace* TryPickNodeFromList(int minimum_size, int* node_size); | 
| + | 
| +  // Picks a node of at least |minimum_size| from the category. Stores the | 
| +  // actual size in |node_size|. Returns nullptr if no node is found. | 
| +  FreeSpace* SearchForNodeInList(int minimum_size, int* node_size); | 
| + | 
| +  inline FreeList* owner(); | 
| +  inline bool is_linked(); | 
| +  bool is_empty() { return top() == nullptr; } | 
| +  int available() const { return available_; } | 
| + | 
| +#ifdef DEBUG | 
| +  intptr_t SumFreeList(); | 
| +  int FreeListLength(); | 
| +#endif | 
| + | 
| + private: | 
| +  // For debug builds we accurately compute free lists lengths up until | 
| +  // {kVeryLongFreeList} by manually walking the list. | 
| +  static const int kVeryLongFreeList = 500; | 
| + | 
| +  inline Page* page(); | 
| + | 
| +  FreeSpace* top() { return top_; } | 
| +  void set_top(FreeSpace* top) { top_ = top; } | 
| +  FreeListCategory* prev() { return prev_; } | 
| +  void set_prev(FreeListCategory* prev) { prev_ = prev; } | 
| +  FreeListCategory* next() { return next_; } | 
| +  void set_next(FreeListCategory* next) { next_ = next; } | 
| + | 
| +  // |type_|: The type of this free list category. | 
| +  FreeListCategoryType type_; | 
| + | 
| +  // |available_|: Total available bytes in all blocks of this free list | 
| +  // category. | 
| +  int available_; | 
| + | 
| +  // |top_|: Points to the top FreeSpace* in the free list category. | 
| +  FreeSpace* top_; | 
| + | 
| +  FreeListCategory* prev_; | 
| +  FreeListCategory* next_; | 
| + | 
| +  friend class FreeList; | 
| +  friend class PagedSpace; | 
| +}; | 
|  | 
| // MemoryChunk represents a memory region owned by a specific space. | 
| // It is divided into the header and the body. Chunk start is always | 
| @@ -397,7 +505,9 @@ class MemoryChunk { | 
| + kPointerSize      // base::AtomicWord concurrent_sweeping_ | 
| + 2 * kPointerSize  // AtomicNumber free-list statistics | 
| + kPointerSize      // AtomicValue next_chunk_ | 
| -      + kPointerSize;     // AtomicValue prev_chunk_ | 
| +      + kPointerSize      // AtomicValue prev_chunk_ | 
| +      // FreeListCategory categories_[kNumberOfCategories] | 
| +      + FreeListCategory::kSize * kNumberOfCategories; | 
|  | 
| // We add some more space to the computed header size to amount for missing | 
| // alignment requirements in our computation. | 
| @@ -577,19 +687,6 @@ class MemoryChunk { | 
| return !IsEvacuationCandidate() && !IsFlagSet(NEVER_ALLOCATE_ON_PAGE); | 
| } | 
|  | 
| -  void MarkEvacuationCandidate() { | 
| -    DCHECK(!IsFlagSet(NEVER_EVACUATE)); | 
| -    DCHECK_NULL(old_to_old_slots_); | 
| -    DCHECK_NULL(typed_old_to_old_slots_); | 
| -    SetFlag(EVACUATION_CANDIDATE); | 
| -  } | 
| - | 
| -  void ClearEvacuationCandidate() { | 
| -    DCHECK_NULL(old_to_old_slots_); | 
| -    DCHECK_NULL(typed_old_to_old_slots_); | 
| -    ClearFlag(EVACUATION_CANDIDATE); | 
| -  } | 
| - | 
| bool ShouldSkipEvacuationSlotRecording() { | 
| return (flags_ & kSkipEvacuationSlotsRecordingMask) != 0; | 
| } | 
| @@ -699,6 +796,8 @@ class MemoryChunk { | 
| // prev_chunk_ holds a pointer of type MemoryChunk | 
| AtomicValue<MemoryChunk*> prev_chunk_; | 
|  | 
| +  FreeListCategory categories_[kNumberOfCategories]; | 
| + | 
| private: | 
| void InitializeReservedMemory() { reservation_.Reset(); } | 
|  | 
| @@ -706,19 +805,6 @@ class MemoryChunk { | 
| friend class MemoryChunkValidator; | 
| }; | 
|  | 
| -enum FreeListCategoryType { | 
| -  kTiniest, | 
| -  kTiny, | 
| -  kSmall, | 
| -  kMedium, | 
| -  kLarge, | 
| -  kHuge, | 
| - | 
| -  kFirstCategory = kTiniest, | 
| -  kLastCategory = kHuge, | 
| -  kNumberOfCategories = kLastCategory + 1 | 
| -}; | 
| - | 
| // ----------------------------------------------------------------------------- | 
| // A page is a memory chunk of a size 1MB. Large object pages may be larger. | 
| // | 
| @@ -822,6 +908,17 @@ class Page : public MemoryChunk { | 
| available_in_free_list()); | 
| } | 
|  | 
| +  template <typename Callback> | 
| +  inline void ForAllFreeListCategories(Callback callback) { | 
| +    for (int i = kFirstCategory; i < kNumberOfCategories; i++) { | 
| +      callback(&categories_[i]); | 
| +    } | 
| +  } | 
| + | 
| +  FreeListCategory* free_list_category(FreeListCategoryType type) { | 
| +    return &categories_[type]; | 
| +  } | 
| + | 
| #define FRAGMENTATION_STATS_ACCESSORS(type, name)        \ | 
| type name() { return name##_.Value(); }                \ | 
| void set_##name(type name) { name##_.SetValue(name); } \ | 
| @@ -836,6 +933,13 @@ class Page : public MemoryChunk { | 
| void Print(); | 
| #endif  // DEBUG | 
|  | 
| +  inline void MarkNeverAllocateForTesting(); | 
| +  inline void MarkEvacuationCandidate(); | 
| +  inline void ClearEvacuationCandidate(); | 
| + | 
| + private: | 
| +  inline void InitializeFreeListCategories(); | 
| + | 
| friend class MemoryAllocator; | 
| }; | 
|  | 
| @@ -1475,12 +1579,6 @@ class AllocationStats BASE_EMBEDDED { | 
|  | 
| void ClearSize() { size_ = capacity_; } | 
|  | 
| -  // Reset the allocation statistics (i.e., available = capacity with no wasted | 
| -  // or allocated bytes). | 
| -  void Reset() { | 
| -    size_ = 0; | 
| -  } | 
| - | 
| // Accessors for the allocation statistics. | 
| intptr_t Capacity() { return capacity_; } | 
| intptr_t MaxCapacity() { return max_capacity_; } | 
| @@ -1507,13 +1605,13 @@ class AllocationStats BASE_EMBEDDED { | 
| void ShrinkSpace(int size_in_bytes) { | 
| capacity_ -= size_in_bytes; | 
| size_ -= size_in_bytes; | 
| -    CHECK(size_ >= 0); | 
| +    CHECK_GE(size_, 0); | 
| } | 
|  | 
| // Allocate from available bytes (available -> size). | 
| void AllocateBytes(intptr_t size_in_bytes) { | 
| size_ += size_in_bytes; | 
| -    CHECK(size_ >= 0); | 
| +    CHECK_GE(size_, 0); | 
| } | 
|  | 
| // Free allocated bytes, making them available (size -> available). | 
| @@ -1552,80 +1650,6 @@ class AllocationStats BASE_EMBEDDED { | 
| intptr_t size_; | 
| }; | 
|  | 
| - | 
| -// A free list category maintains a linked list of free memory blocks. | 
| -class FreeListCategory { | 
| - public: | 
| -  FreeListCategory() : top_(nullptr), end_(nullptr), available_(0) {} | 
| - | 
| -  void Initialize(FreeList* owner, FreeListCategoryType type) { | 
| -    owner_ = owner; | 
| -    type_ = type; | 
| -  } | 
| - | 
| -  // Concatenates {category} into {this}. | 
| -  // | 
| -  // Note: Thread-safe. | 
| -  intptr_t Concatenate(FreeListCategory* category); | 
| - | 
| -  void Reset(); | 
| - | 
| -  void Free(FreeSpace* node, int size_in_bytes); | 
| - | 
| -  // Pick a node from the list. | 
| -  FreeSpace* PickNodeFromList(int* node_size); | 
| - | 
| -  // Pick a node from the list and compare it against {size_in_bytes}. If the | 
| -  // node's size is greater or equal return the node and null otherwise. | 
| -  FreeSpace* PickNodeFromList(int size_in_bytes, int* node_size); | 
| - | 
| -  // Search for a node of size {size_in_bytes}. | 
| -  FreeSpace* SearchForNodeInList(int size_in_bytes, int* node_size); | 
| - | 
| -  intptr_t EvictFreeListItemsInList(Page* p); | 
| -  bool ContainsPageFreeListItemsInList(Page* p); | 
| - | 
| -  void RepairFreeList(Heap* heap); | 
| - | 
| -  bool IsEmpty() { return top() == nullptr; } | 
| - | 
| -  FreeList* owner() { return owner_; } | 
| -  int available() const { return available_; } | 
| - | 
| -#ifdef DEBUG | 
| -  intptr_t SumFreeList(); | 
| -  int FreeListLength(); | 
| -  bool IsVeryLong(); | 
| -#endif | 
| - | 
| - private: | 
| -  // For debug builds we accurately compute free lists lengths up until | 
| -  // {kVeryLongFreeList} by manually walking the list. | 
| -  static const int kVeryLongFreeList = 500; | 
| - | 
| -  FreeSpace* top() { return top_.Value(); } | 
| -  void set_top(FreeSpace* top) { top_.SetValue(top); } | 
| - | 
| -  FreeSpace* end() const { return end_; } | 
| -  void set_end(FreeSpace* end) { end_ = end; } | 
| - | 
| -  // |type_|: The type of this free list category. | 
| -  FreeListCategoryType type_; | 
| - | 
| -  // |top_|: Points to the top FreeSpace* in the free list category. | 
| -  AtomicValue<FreeSpace*> top_; | 
| - | 
| -  // |end_|: Points to the end FreeSpace* in the free list category. | 
| -  FreeSpace* end_; | 
| - | 
| -  // |available_|: Total available bytes in all blocks of this free list | 
| -  //   category. | 
| -  int available_; | 
| - | 
| -  // |owner_|: The owning free list of this category. | 
| -  FreeList* owner_; | 
| -}; | 
| - | 
| // A free list maintaining free blocks of memory. The free list is organized in | 
| // a way to encourage objects allocated around the same time to be near each | 
| // other. The normal way to allocate is intended to be by bumping a 'top' | 
| @@ -1670,19 +1694,13 @@ class FreeList { | 
|  | 
| explicit FreeList(PagedSpace* owner); | 
|  | 
| -  // The method concatenates {other} into {this} and returns the added bytes, | 
| -  // including waste. | 
| -  // | 
| -  // Note: Thread-safe. | 
| -  intptr_t Concatenate(FreeList* other); | 
| - | 
| // Adds a node on the free list. The block of size {size_in_bytes} starting | 
| // at {start} is placed on the free list. The return value is the number of | 
| // bytes that were not added to the free list, because they freed memory block | 
| // was too small. Bookkeeping information will be written to the block, i.e., | 
| // its contents will be destroyed. The start address should be word aligned, | 
| // and the size should be a non-zero multiple of the word size. | 
| -  int Free(Address start, int size_in_bytes); | 
| +  int Free(Address start, int size_in_bytes, FreeMode mode); | 
|  | 
| // Allocate a block of size {size_in_bytes} from the free list. The block is | 
| // unitialized. A failure is returned if no block is available. The size | 
| @@ -1692,49 +1710,83 @@ class FreeList { | 
| // Clear the free list. | 
| void Reset(); | 
|  | 
| -  void ResetStats() { wasted_bytes_ = 0; } | 
| +  void ResetStats() { | 
| +    wasted_bytes_.SetValue(0); | 
| +    ForAllFreeListCategories( | 
| +        [](FreeListCategory* category) { category->ResetStats(); }); | 
| +  } | 
|  | 
| // Return the number of bytes available on the free list. | 
| intptr_t Available() { | 
| intptr_t available = 0; | 
| -    for (int i = kFirstCategory; i < kNumberOfCategories; i++) { | 
| -      available += category_[i].available(); | 
| -    } | 
| +    ForAllFreeListCategories([&available](FreeListCategory* category) { | 
| +      available += category->available(); | 
| +    }); | 
| return available; | 
| } | 
|  | 
| -  // The method tries to find a {FreeSpace} node of at least {size_in_bytes} | 
| -  // size in the free list category exactly matching the size. If no suitable | 
| -  // node could be found, the method falls back to retrieving a {FreeSpace} | 
| -  // from the large or huge free list category. | 
| -  // | 
| -  // Can be used concurrently. | 
| -  MUST_USE_RESULT FreeSpace* TryRemoveMemory(intptr_t hint_size_in_bytes); | 
| - | 
| bool IsEmpty() { | 
| -    for (int i = kFirstCategory; i < kNumberOfCategories; i++) { | 
| -      if (!category_[i].IsEmpty()) return false; | 
| -    } | 
| -    return true; | 
| +    bool empty = true; | 
| +    ForAllFreeListCategories([&empty](FreeListCategory* category) { | 
| +      if (!category->is_empty()) empty = false; | 
| +    }); | 
| +    return empty; | 
| } | 
|  | 
| // Used after booting the VM. | 
| void RepairLists(Heap* heap); | 
|  | 
| -  intptr_t EvictFreeListItems(Page* p); | 
| -  bool ContainsPageFreeListItems(Page* p); | 
| +  intptr_t EvictFreeListItems(Page* page); | 
| +  bool ContainsPageFreeListItems(Page* page); | 
|  | 
| PagedSpace* owner() { return owner_; } | 
| -  intptr_t wasted_bytes() { return wasted_bytes_; } | 
| -  base::Mutex* mutex() { return &mutex_; } | 
| +  intptr_t wasted_bytes() { return wasted_bytes_.Value(); } | 
| + | 
| +  template <typename Callback> | 
| +  void ForAllFreeListCategories(FreeListCategoryType type, Callback callback) { | 
| +    FreeListCategory* current = categories_[type]; | 
| +    while (current != nullptr) { | 
| +      FreeListCategory* next = current->next(); | 
| +      callback(current); | 
| +      current = next; | 
| +    } | 
| +  } | 
| + | 
| +  template <typename Callback> | 
| +  void ForAllFreeListCategories(Callback callback) { | 
| +    for (int i = kFirstCategory; i < kNumberOfCategories; i++) { | 
| +      ForAllFreeListCategories(static_cast<FreeListCategoryType>(i), callback); | 
| +    } | 
| +  } | 
| + | 
| +  bool AddCategory(FreeListCategory* category); | 
| +  void RemoveCategory(FreeListCategory* category); | 
| +  void PrintCategories(FreeListCategoryType type); | 
|  | 
| #ifdef DEBUG | 
| -  void Zap(); | 
| intptr_t SumFreeLists(); | 
| bool IsVeryLong(); | 
| #endif | 
|  | 
| private: | 
| +  class FreeListCategoryIterator { | 
| +   public: | 
| +    FreeListCategoryIterator(FreeList* free_list, FreeListCategoryType type) | 
| +        : current_(free_list->categories_[type]) {} | 
| + | 
| +    bool HasNext() { return current_ != nullptr; } | 
| + | 
| +    FreeListCategory* Next() { | 
| +      DCHECK(HasNext()); | 
| +      FreeListCategory* tmp = current_; | 
| +      current_ = current_->next(); | 
| +      return tmp; | 
| +    } | 
| + | 
| +   private: | 
| +    FreeListCategory* current_; | 
| +  }; | 
| + | 
| // The size range of blocks, in bytes. | 
| static const int kMinBlockSize = 3 * kPointerSize; | 
| static const int kMaxBlockSize = Page::kAllocatableMemory; | 
| @@ -1750,11 +1802,19 @@ class FreeList { | 
| static const int kLargeAllocationMax = kMediumListMax; | 
|  | 
| FreeSpace* FindNodeFor(int size_in_bytes, int* node_size); | 
| -  FreeSpace* FindNodeIn(FreeListCategoryType category, int* node_size); | 
|  | 
| -  FreeListCategory* GetFreeListCategory(FreeListCategoryType category) { | 
| -    return &category_[category]; | 
| -  } | 
| +  // Walks all available categories for a given |type| and tries to retrieve | 
| +  // a node. Returns nullptr if the category is empty. | 
| +  FreeSpace* FindNodeIn(FreeListCategoryType type, int* node_size); | 
| + | 
| +  // Tries to retrieve a node from the first category in a given |type|. | 
| +  // Returns nullptr if the category is empty. | 
| +  FreeSpace* TryFindNodeIn(FreeListCategoryType type, int* node_size, | 
| +                           int minimum_size); | 
| + | 
| +  // Searches a given |type| for a node of at least |minimum_size|. | 
| +  FreeSpace* SearchForNodeInList(FreeListCategoryType type, int* node_size, | 
| +                                 int minimum_size); | 
|  | 
| FreeListCategoryType SelectFreeListCategoryType(size_t size_in_bytes) { | 
| if (size_in_bytes <= kTiniestListMax) { | 
| @@ -1784,10 +1844,13 @@ class FreeList { | 
| return kHuge; | 
| } | 
|  | 
| +  FreeListCategory* top(FreeListCategoryType type) { return categories_[type]; } | 
| + | 
| PagedSpace* owner_; | 
| -  base::Mutex mutex_; | 
| -  intptr_t wasted_bytes_; | 
| -  FreeListCategory category_[kNumberOfCategories]; | 
| +  AtomicNumber<intptr_t> wasted_bytes_; | 
| +  FreeListCategory* categories_[kNumberOfCategories]; | 
| + | 
| +  friend class FreeListCategory; | 
|  | 
| DISALLOW_IMPLICIT_CONSTRUCTORS(FreeList); | 
| }; | 
| @@ -1889,7 +1952,6 @@ class LocalAllocationBuffer { | 
| AllocationInfo allocation_info_; | 
| }; | 
|  | 
| - | 
| class PagedSpace : public Space { | 
| public: | 
| static const intptr_t kCompactionMemoryWanted = 500 * KB; | 
| @@ -1946,11 +2008,6 @@ class PagedSpace : public Space { | 
| ResetFreeListStatistics(); | 
| } | 
|  | 
| -  // Increases the number of available bytes of that space. | 
| -  void AddToAccountingStats(intptr_t bytes) { | 
| -    accounting_stats_.DeallocateBytes(bytes); | 
| -  } | 
| - | 
| // Available bytes without growing.  These are the bytes on the free list. | 
| // The bytes in the linear allocation area are not included in this total | 
| // because updating the stats would slow down allocation.  New pages are | 
| @@ -2006,11 +2063,16 @@ class PagedSpace : public Space { | 
| // If add_to_freelist is false then just accounting stats are updated and | 
| // no attempt to add area to free list is made. | 
| int Free(Address start, int size_in_bytes) { | 
| -    int wasted = free_list_.Free(start, size_in_bytes); | 
| +    int wasted = free_list_.Free(start, size_in_bytes, kLinkCategory); | 
| accounting_stats_.DeallocateBytes(size_in_bytes); | 
| return size_in_bytes - wasted; | 
| } | 
|  | 
| +  int UnaccountedFree(Address start, int size_in_bytes) { | 
| +    int wasted = free_list_.Free(start, size_in_bytes, kDoNotLinkCategory); | 
| +    return size_in_bytes - wasted; | 
| +  } | 
| + | 
| void ResetFreeList() { free_list_.Reset(); } | 
|  | 
| // Set space allocation info. | 
| @@ -2035,7 +2097,7 @@ class PagedSpace : public Space { | 
| void IncreaseCapacity(int size); | 
|  | 
| // Releases an unused page and shrinks the space. | 
| -  void ReleasePage(Page* page, bool evict_free_list_items); | 
| +  void ReleasePage(Page* page); | 
|  | 
| // The dummy page that anchors the linked list of pages. | 
| Page* anchor() { return &anchor_; } | 
| @@ -2091,17 +2153,18 @@ class PagedSpace : public Space { | 
| // sweeper. | 
| virtual void RefillFreeList(); | 
|  | 
| - protected: | 
| -  void AddMemory(Address start, intptr_t size); | 
| +  FreeList* free_list() { return &free_list_; } | 
|  | 
| -  void MoveOverFreeMemory(PagedSpace* other); | 
| +  base::Mutex* mutex() { return &space_mutex_; } | 
|  | 
| +  inline void UnlinkFreeListCategories(Page* page); | 
| +  inline intptr_t RelinkFreeListCategories(Page* page); | 
| + | 
| + protected: | 
| // PagedSpaces that should be included in snapshots have different, i.e., | 
| // smaller, initial pages. | 
| virtual bool snapshotable() { return true; } | 
|  | 
| -  FreeList* free_list() { return &free_list_; } | 
| - | 
| bool HasPages() { return anchor_.next_page() != &anchor_; } | 
|  | 
| // Cleans up the space, frees all pages in this space except those belonging | 
| @@ -2799,8 +2862,6 @@ class CompactionSpace : public PagedSpace { | 
|  | 
| bool is_local() override { return true; } | 
|  | 
| -  void RefillFreeList() override; | 
| - | 
| protected: | 
| // The space is temporary and not included in any snapshots. | 
| bool snapshotable() override { return false; } | 
|  |