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