11cb0ef41Sopenharmony_ci// Copyright 2020 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ci#include "src/heap/cppgc/object-allocator.h" 61cb0ef41Sopenharmony_ci 71cb0ef41Sopenharmony_ci#include "include/cppgc/allocation.h" 81cb0ef41Sopenharmony_ci#include "src/base/logging.h" 91cb0ef41Sopenharmony_ci#include "src/base/macros.h" 101cb0ef41Sopenharmony_ci#include "src/heap/cppgc/free-list.h" 111cb0ef41Sopenharmony_ci#include "src/heap/cppgc/globals.h" 121cb0ef41Sopenharmony_ci#include "src/heap/cppgc/heap-object-header.h" 131cb0ef41Sopenharmony_ci#include "src/heap/cppgc/heap-page.h" 141cb0ef41Sopenharmony_ci#include "src/heap/cppgc/heap-space.h" 151cb0ef41Sopenharmony_ci#include "src/heap/cppgc/heap-visitor.h" 161cb0ef41Sopenharmony_ci#include "src/heap/cppgc/heap.h" 171cb0ef41Sopenharmony_ci#include "src/heap/cppgc/memory.h" 181cb0ef41Sopenharmony_ci#include "src/heap/cppgc/object-start-bitmap.h" 191cb0ef41Sopenharmony_ci#include "src/heap/cppgc/page-memory.h" 201cb0ef41Sopenharmony_ci#include "src/heap/cppgc/prefinalizer-handler.h" 211cb0ef41Sopenharmony_ci#include "src/heap/cppgc/stats-collector.h" 221cb0ef41Sopenharmony_ci#include "src/heap/cppgc/sweeper.h" 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_cinamespace cppgc { 251cb0ef41Sopenharmony_cinamespace internal { 261cb0ef41Sopenharmony_ci 271cb0ef41Sopenharmony_cinamespace { 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_civoid MarkRangeAsYoung(BasePage* page, Address begin, Address end) { 301cb0ef41Sopenharmony_ci#if defined(CPPGC_YOUNG_GENERATION) 311cb0ef41Sopenharmony_ci DCHECK_LT(begin, end); 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_ci static constexpr auto kEntrySize = AgeTable::kCardSizeInBytes; 341cb0ef41Sopenharmony_ci 351cb0ef41Sopenharmony_ci const uintptr_t offset_begin = CagedHeap::OffsetFromAddress(begin); 361cb0ef41Sopenharmony_ci const uintptr_t offset_end = CagedHeap::OffsetFromAddress(end); 371cb0ef41Sopenharmony_ci 381cb0ef41Sopenharmony_ci const uintptr_t young_offset_begin = (begin == page->PayloadStart()) 391cb0ef41Sopenharmony_ci ? RoundDown(offset_begin, kEntrySize) 401cb0ef41Sopenharmony_ci : RoundUp(offset_begin, kEntrySize); 411cb0ef41Sopenharmony_ci const uintptr_t young_offset_end = (end == page->PayloadEnd()) 421cb0ef41Sopenharmony_ci ? RoundUp(offset_end, kEntrySize) 431cb0ef41Sopenharmony_ci : RoundDown(offset_end, kEntrySize); 441cb0ef41Sopenharmony_ci 451cb0ef41Sopenharmony_ci auto& age_table = page->heap().caged_heap().local_data().age_table; 461cb0ef41Sopenharmony_ci for (auto offset = young_offset_begin; offset < young_offset_end; 471cb0ef41Sopenharmony_ci offset += AgeTable::kCardSizeInBytes) { 481cb0ef41Sopenharmony_ci age_table.SetAge(offset, AgeTable::Age::kYoung); 491cb0ef41Sopenharmony_ci } 501cb0ef41Sopenharmony_ci 511cb0ef41Sopenharmony_ci // Set to kUnknown the first and the last regions of the newly allocated 521cb0ef41Sopenharmony_ci // linear buffer. 531cb0ef41Sopenharmony_ci if (begin != page->PayloadStart() && !IsAligned(offset_begin, kEntrySize)) 541cb0ef41Sopenharmony_ci age_table.SetAge(offset_begin, AgeTable::Age::kMixed); 551cb0ef41Sopenharmony_ci if (end != page->PayloadEnd() && !IsAligned(offset_end, kEntrySize)) 561cb0ef41Sopenharmony_ci age_table.SetAge(offset_end, AgeTable::Age::kMixed); 571cb0ef41Sopenharmony_ci#endif 581cb0ef41Sopenharmony_ci} 591cb0ef41Sopenharmony_ci 601cb0ef41Sopenharmony_civoid AddToFreeList(NormalPageSpace& space, Address start, size_t size) { 611cb0ef41Sopenharmony_ci // No need for SetMemoryInaccessible() as LAB memory is retrieved as free 621cb0ef41Sopenharmony_ci // inaccessible memory. 631cb0ef41Sopenharmony_ci space.free_list().Add({start, size}); 641cb0ef41Sopenharmony_ci // Concurrent marking may be running while the LAB is set up next to a live 651cb0ef41Sopenharmony_ci // object sharing the same cell in the bitmap. 661cb0ef41Sopenharmony_ci NormalPage::From(BasePage::FromPayload(start)) 671cb0ef41Sopenharmony_ci ->object_start_bitmap() 681cb0ef41Sopenharmony_ci .SetBit<AccessMode::kAtomic>(start); 691cb0ef41Sopenharmony_ci} 701cb0ef41Sopenharmony_ci 711cb0ef41Sopenharmony_civoid ReplaceLinearAllocationBuffer(NormalPageSpace& space, 721cb0ef41Sopenharmony_ci StatsCollector& stats_collector, 731cb0ef41Sopenharmony_ci Address new_buffer, size_t new_size) { 741cb0ef41Sopenharmony_ci auto& lab = space.linear_allocation_buffer(); 751cb0ef41Sopenharmony_ci if (lab.size()) { 761cb0ef41Sopenharmony_ci AddToFreeList(space, lab.start(), lab.size()); 771cb0ef41Sopenharmony_ci stats_collector.NotifyExplicitFree(lab.size()); 781cb0ef41Sopenharmony_ci } 791cb0ef41Sopenharmony_ci 801cb0ef41Sopenharmony_ci lab.Set(new_buffer, new_size); 811cb0ef41Sopenharmony_ci if (new_size) { 821cb0ef41Sopenharmony_ci DCHECK_NOT_NULL(new_buffer); 831cb0ef41Sopenharmony_ci stats_collector.NotifyAllocation(new_size); 841cb0ef41Sopenharmony_ci auto* page = NormalPage::From(BasePage::FromPayload(new_buffer)); 851cb0ef41Sopenharmony_ci // Concurrent marking may be running while the LAB is set up next to a live 861cb0ef41Sopenharmony_ci // object sharing the same cell in the bitmap. 871cb0ef41Sopenharmony_ci page->object_start_bitmap().ClearBit<AccessMode::kAtomic>(new_buffer); 881cb0ef41Sopenharmony_ci MarkRangeAsYoung(page, new_buffer, new_buffer + new_size); 891cb0ef41Sopenharmony_ci } 901cb0ef41Sopenharmony_ci} 911cb0ef41Sopenharmony_ci 921cb0ef41Sopenharmony_civoid* AllocateLargeObject(PageBackend& page_backend, LargePageSpace& space, 931cb0ef41Sopenharmony_ci StatsCollector& stats_collector, size_t size, 941cb0ef41Sopenharmony_ci GCInfoIndex gcinfo) { 951cb0ef41Sopenharmony_ci LargePage* page = LargePage::Create(page_backend, space, size); 961cb0ef41Sopenharmony_ci space.AddPage(page); 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_ci auto* header = new (page->ObjectHeader()) 991cb0ef41Sopenharmony_ci HeapObjectHeader(HeapObjectHeader::kLargeObjectSizeInHeader, gcinfo); 1001cb0ef41Sopenharmony_ci 1011cb0ef41Sopenharmony_ci stats_collector.NotifyAllocation(size); 1021cb0ef41Sopenharmony_ci MarkRangeAsYoung(page, page->PayloadStart(), page->PayloadEnd()); 1031cb0ef41Sopenharmony_ci 1041cb0ef41Sopenharmony_ci return header->ObjectStart(); 1051cb0ef41Sopenharmony_ci} 1061cb0ef41Sopenharmony_ci 1071cb0ef41Sopenharmony_ci} // namespace 1081cb0ef41Sopenharmony_ci 1091cb0ef41Sopenharmony_ciconstexpr size_t ObjectAllocator::kSmallestSpaceSize; 1101cb0ef41Sopenharmony_ci 1111cb0ef41Sopenharmony_ciObjectAllocator::ObjectAllocator(RawHeap& heap, PageBackend& page_backend, 1121cb0ef41Sopenharmony_ci StatsCollector& stats_collector, 1131cb0ef41Sopenharmony_ci PreFinalizerHandler& prefinalizer_handler) 1141cb0ef41Sopenharmony_ci : raw_heap_(heap), 1151cb0ef41Sopenharmony_ci page_backend_(page_backend), 1161cb0ef41Sopenharmony_ci stats_collector_(stats_collector), 1171cb0ef41Sopenharmony_ci prefinalizer_handler_(prefinalizer_handler) {} 1181cb0ef41Sopenharmony_ci 1191cb0ef41Sopenharmony_civoid* ObjectAllocator::OutOfLineAllocate(NormalPageSpace& space, size_t size, 1201cb0ef41Sopenharmony_ci AlignVal alignment, 1211cb0ef41Sopenharmony_ci GCInfoIndex gcinfo) { 1221cb0ef41Sopenharmony_ci void* memory = OutOfLineAllocateImpl(space, size, alignment, gcinfo); 1231cb0ef41Sopenharmony_ci stats_collector_.NotifySafePointForConservativeCollection(); 1241cb0ef41Sopenharmony_ci if (prefinalizer_handler_.IsInvokingPreFinalizers()) { 1251cb0ef41Sopenharmony_ci // Objects allocated during pre finalizers should be allocated as black 1261cb0ef41Sopenharmony_ci // since marking is already done. Atomics are not needed because there is 1271cb0ef41Sopenharmony_ci // no concurrent marking in the background. 1281cb0ef41Sopenharmony_ci HeapObjectHeader::FromObject(memory).MarkNonAtomic(); 1291cb0ef41Sopenharmony_ci // Resetting the allocation buffer forces all further allocations in pre 1301cb0ef41Sopenharmony_ci // finalizers to go through this slow path. 1311cb0ef41Sopenharmony_ci ReplaceLinearAllocationBuffer(space, stats_collector_, nullptr, 0); 1321cb0ef41Sopenharmony_ci prefinalizer_handler_.NotifyAllocationInPrefinalizer(size); 1331cb0ef41Sopenharmony_ci } 1341cb0ef41Sopenharmony_ci return memory; 1351cb0ef41Sopenharmony_ci} 1361cb0ef41Sopenharmony_ci 1371cb0ef41Sopenharmony_civoid* ObjectAllocator::OutOfLineAllocateImpl(NormalPageSpace& space, 1381cb0ef41Sopenharmony_ci size_t size, AlignVal alignment, 1391cb0ef41Sopenharmony_ci GCInfoIndex gcinfo) { 1401cb0ef41Sopenharmony_ci DCHECK_EQ(0, size & kAllocationMask); 1411cb0ef41Sopenharmony_ci DCHECK_LE(kFreeListEntrySize, size); 1421cb0ef41Sopenharmony_ci // Out-of-line allocation allows for checking this is all situations. 1431cb0ef41Sopenharmony_ci CHECK(!in_disallow_gc_scope()); 1441cb0ef41Sopenharmony_ci 1451cb0ef41Sopenharmony_ci // If this allocation is big enough, allocate a large object. 1461cb0ef41Sopenharmony_ci if (size >= kLargeObjectSizeThreshold) { 1471cb0ef41Sopenharmony_ci auto& large_space = LargePageSpace::From( 1481cb0ef41Sopenharmony_ci *raw_heap_.Space(RawHeap::RegularSpaceType::kLarge)); 1491cb0ef41Sopenharmony_ci // LargePage has a natural alignment that already satisfies 1501cb0ef41Sopenharmony_ci // `kMaxSupportedAlignment`. 1511cb0ef41Sopenharmony_ci return AllocateLargeObject(page_backend_, large_space, stats_collector_, 1521cb0ef41Sopenharmony_ci size, gcinfo); 1531cb0ef41Sopenharmony_ci } 1541cb0ef41Sopenharmony_ci 1551cb0ef41Sopenharmony_ci size_t request_size = size; 1561cb0ef41Sopenharmony_ci // Adjust size to be able to accommodate alignment. 1571cb0ef41Sopenharmony_ci const size_t dynamic_alignment = static_cast<size_t>(alignment); 1581cb0ef41Sopenharmony_ci if (dynamic_alignment != kAllocationGranularity) { 1591cb0ef41Sopenharmony_ci CHECK_EQ(2 * sizeof(HeapObjectHeader), dynamic_alignment); 1601cb0ef41Sopenharmony_ci request_size += kAllocationGranularity; 1611cb0ef41Sopenharmony_ci } 1621cb0ef41Sopenharmony_ci 1631cb0ef41Sopenharmony_ci RefillLinearAllocationBuffer(space, request_size); 1641cb0ef41Sopenharmony_ci 1651cb0ef41Sopenharmony_ci // The allocation must succeed, as we just refilled the LAB. 1661cb0ef41Sopenharmony_ci void* result = (dynamic_alignment == kAllocationGranularity) 1671cb0ef41Sopenharmony_ci ? AllocateObjectOnSpace(space, size, gcinfo) 1681cb0ef41Sopenharmony_ci : AllocateObjectOnSpace(space, size, alignment, gcinfo); 1691cb0ef41Sopenharmony_ci CHECK(result); 1701cb0ef41Sopenharmony_ci return result; 1711cb0ef41Sopenharmony_ci} 1721cb0ef41Sopenharmony_ci 1731cb0ef41Sopenharmony_civoid ObjectAllocator::RefillLinearAllocationBuffer(NormalPageSpace& space, 1741cb0ef41Sopenharmony_ci size_t size) { 1751cb0ef41Sopenharmony_ci // Try to allocate from the freelist. 1761cb0ef41Sopenharmony_ci if (RefillLinearAllocationBufferFromFreeList(space, size)) return; 1771cb0ef41Sopenharmony_ci 1781cb0ef41Sopenharmony_ci // Lazily sweep pages of this heap until we find a freed area for this 1791cb0ef41Sopenharmony_ci // allocation or we finish sweeping all pages of this heap. 1801cb0ef41Sopenharmony_ci Sweeper& sweeper = raw_heap_.heap()->sweeper(); 1811cb0ef41Sopenharmony_ci // TODO(chromium:1056170): Investigate whether this should be a loop which 1821cb0ef41Sopenharmony_ci // would result in more agressive re-use of memory at the expense of 1831cb0ef41Sopenharmony_ci // potentially larger allocation time. 1841cb0ef41Sopenharmony_ci if (sweeper.SweepForAllocationIfRunning(&space, size)) { 1851cb0ef41Sopenharmony_ci // Sweeper found a block of at least `size` bytes. Allocation from the 1861cb0ef41Sopenharmony_ci // free list may still fail as actual buckets are not exhaustively 1871cb0ef41Sopenharmony_ci // searched for a suitable block. Instead, buckets are tested from larger 1881cb0ef41Sopenharmony_ci // sizes that are guaranteed to fit the block to smaller bucket sizes that 1891cb0ef41Sopenharmony_ci // may only potentially fit the block. For the bucket that may exactly fit 1901cb0ef41Sopenharmony_ci // the allocation of `size` bytes (no overallocation), only the first 1911cb0ef41Sopenharmony_ci // entry is checked. 1921cb0ef41Sopenharmony_ci if (RefillLinearAllocationBufferFromFreeList(space, size)) return; 1931cb0ef41Sopenharmony_ci } 1941cb0ef41Sopenharmony_ci 1951cb0ef41Sopenharmony_ci sweeper.FinishIfRunning(); 1961cb0ef41Sopenharmony_ci // TODO(chromium:1056170): Make use of the synchronously freed memory. 1971cb0ef41Sopenharmony_ci 1981cb0ef41Sopenharmony_ci auto* new_page = NormalPage::Create(page_backend_, space); 1991cb0ef41Sopenharmony_ci space.AddPage(new_page); 2001cb0ef41Sopenharmony_ci 2011cb0ef41Sopenharmony_ci // Set linear allocation buffer to new page. 2021cb0ef41Sopenharmony_ci ReplaceLinearAllocationBuffer(space, stats_collector_, 2031cb0ef41Sopenharmony_ci new_page->PayloadStart(), 2041cb0ef41Sopenharmony_ci new_page->PayloadSize()); 2051cb0ef41Sopenharmony_ci} 2061cb0ef41Sopenharmony_ci 2071cb0ef41Sopenharmony_cibool ObjectAllocator::RefillLinearAllocationBufferFromFreeList( 2081cb0ef41Sopenharmony_ci NormalPageSpace& space, size_t size) { 2091cb0ef41Sopenharmony_ci const FreeList::Block entry = space.free_list().Allocate(size); 2101cb0ef41Sopenharmony_ci if (!entry.address) return false; 2111cb0ef41Sopenharmony_ci 2121cb0ef41Sopenharmony_ci // Assume discarded memory on that page is now zero. 2131cb0ef41Sopenharmony_ci auto& page = *NormalPage::From(BasePage::FromPayload(entry.address)); 2141cb0ef41Sopenharmony_ci if (page.discarded_memory()) { 2151cb0ef41Sopenharmony_ci stats_collector_.DecrementDiscardedMemory(page.discarded_memory()); 2161cb0ef41Sopenharmony_ci page.ResetDiscardedMemory(); 2171cb0ef41Sopenharmony_ci } 2181cb0ef41Sopenharmony_ci 2191cb0ef41Sopenharmony_ci ReplaceLinearAllocationBuffer( 2201cb0ef41Sopenharmony_ci space, stats_collector_, static_cast<Address>(entry.address), entry.size); 2211cb0ef41Sopenharmony_ci return true; 2221cb0ef41Sopenharmony_ci} 2231cb0ef41Sopenharmony_ci 2241cb0ef41Sopenharmony_civoid ObjectAllocator::ResetLinearAllocationBuffers() { 2251cb0ef41Sopenharmony_ci class Resetter : public HeapVisitor<Resetter> { 2261cb0ef41Sopenharmony_ci public: 2271cb0ef41Sopenharmony_ci explicit Resetter(StatsCollector& stats) : stats_collector_(stats) {} 2281cb0ef41Sopenharmony_ci 2291cb0ef41Sopenharmony_ci bool VisitLargePageSpace(LargePageSpace&) { return true; } 2301cb0ef41Sopenharmony_ci 2311cb0ef41Sopenharmony_ci bool VisitNormalPageSpace(NormalPageSpace& space) { 2321cb0ef41Sopenharmony_ci ReplaceLinearAllocationBuffer(space, stats_collector_, nullptr, 0); 2331cb0ef41Sopenharmony_ci return true; 2341cb0ef41Sopenharmony_ci } 2351cb0ef41Sopenharmony_ci 2361cb0ef41Sopenharmony_ci private: 2371cb0ef41Sopenharmony_ci StatsCollector& stats_collector_; 2381cb0ef41Sopenharmony_ci } visitor(stats_collector_); 2391cb0ef41Sopenharmony_ci 2401cb0ef41Sopenharmony_ci visitor.Traverse(raw_heap_); 2411cb0ef41Sopenharmony_ci} 2421cb0ef41Sopenharmony_ci 2431cb0ef41Sopenharmony_civoid ObjectAllocator::Terminate() { 2441cb0ef41Sopenharmony_ci ResetLinearAllocationBuffers(); 2451cb0ef41Sopenharmony_ci} 2461cb0ef41Sopenharmony_ci 2471cb0ef41Sopenharmony_cibool ObjectAllocator::in_disallow_gc_scope() const { 2481cb0ef41Sopenharmony_ci return raw_heap_.heap()->in_disallow_gc_scope(); 2491cb0ef41Sopenharmony_ci} 2501cb0ef41Sopenharmony_ci 2511cb0ef41Sopenharmony_ci} // namespace internal 2521cb0ef41Sopenharmony_ci} // namespace cppgc 253