tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit a39d39fa905d5b9468a5fc3d0bde9f13aa76726c
parent 1884530df58f24261c97eb68fece5a7bab56e36e
Author: Jon Coppeard <jcoppeard@mozilla.com>
Date:   Mon,  6 Oct 2025 09:51:10 +0000

Bug 1978645 - Part 3: Remove malloc block cache and nursery trailer tracking r=jseward

Differential Revision: https://phabricator.services.mozilla.com/D265785

Diffstat:
Mjs/public/GCAPI.h | 2+-
Djs/src/ds/PointerAndUint7.h | 125-------------------------------------------------------------------------------
Mjs/src/gc/GCEnum.h | 1-
Djs/src/gc/MallocedBlockCache.cpp | 96-------------------------------------------------------------------------------
Djs/src/gc/MallocedBlockCache.h | 179-------------------------------------------------------------------------------
Mjs/src/gc/Nursery-inl.h | 31-------------------------------
Mjs/src/gc/Nursery.cpp | 135-------------------------------------------------------------------------------
Mjs/src/gc/Nursery.h | 59-----------------------------------------------------------
Mjs/src/gc/moz.build | 1-
Mjs/src/util/Poison.h | 1-
Mjs/src/vm/Runtime.cpp | 5-----
11 files changed, 1 insertion(+), 634 deletions(-)

diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h @@ -656,7 +656,7 @@ namespace JS { D(FULL_CELL_PTR_STR_BUFFER, 28) \ D(TOO_MUCH_JIT_CODE, 29) \ D(FULL_CELL_PTR_BIGINT_BUFFER, 30) \ - D(NURSERY_TRAILERS, 31) \ + D(UNUSED4, 31) \ D(NURSERY_MALLOC_BUFFERS, 32) \ \ /* \ diff --git a/js/src/ds/PointerAndUint7.h b/js/src/ds/PointerAndUint7.h @@ -1,125 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sw=2 et tw=80: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef gc_PointerAndUint7_h -#define gc_PointerAndUint7_h - -#include "mozilla/Assertions.h" - -#include <stdint.h> - -namespace js { - -// A class that can store an address and a 7-bit unsigned integer in 64 bits, -// even on a 64-bit target. -// -// On 64-bit targets, it assumes that all supported target architectures -// contain at most 57 significant bits in their addresses, and that the valid -// address space is split evenly between addresses increasing from 0--(64)--0 -// and addresses decreasing from 1--(64)--1. -// -// The 57-significant-bit constraint comes from Intel's 5-level paging scheme -// as introduced in the Ice Lake processor line, circa late 2019; see -// https://en.wikipedia.org/wiki/Intel_5-level_paging. Prior to that, Intel -// required only 48 significant bits. AArch64 requires 52 significant bits, -// as of the ARMv8.2 LVA (Large Virtual Addressing) extension, and so is less -// constraining than Intel. -// -// In any case, NaN-boxing of pointers in JS::Value gives us a pretty hard -// requirement that we can store pointers in 47 bits. So that constraint will -// break before the 57-bit constraint here breaks. See SMDOC in -// js/public/Value.h. -// -// On 32-bit targets, both components are stored unmodified in the upper and -// lower 32-bit chunks of the value, and there are no constraints on the -// component values. - -#ifdef JS_64BIT - -// The implementation for 64-bit targets. -class PointerAndUint7 final { - // The representation is: the lowest 57 bits of the pointer are stored in - // the top 57 bits of val_, and the Uint7 is stored in the bottom 7 bits. - // Hence recovering the pointer is 7-bit signed shift right of val_, and - // recovering the UInt7 is an AND with 127. In both cases, that's a single - // machine instruction. - uint64_t val_; - - static const uint8_t SHIFT_PTR = 7; - static const uint64_t MASK_UINT7 = (uint64_t(1) << SHIFT_PTR) - 1; - - static inline bool isRepresentablePtr(void* ptr) { - // We require that the top 7 bits (bits 63:57) are the same as bit 56. - // That will be the case iff, when we signedly shift `ptr` right by 56 - // bits, the value is all zeroes or all ones. - int64_t s = int64_t(ptr); - // s should be bbbb'bbbb'X--(56)--X, for b = 0 or 1, and X can be anything - s >>= (64 - SHIFT_PTR - 1); // 56 - // s should be 0--(64)--0 or 1--(64)--1 - uint64_t u = uint64_t(s); - // Note, this addition can overflow, intentionally. - u += 1; - // u should be 0--(64)--0 or 0--(63)--01 - return u <= uint64_t(1); - } - static inline bool isRepresentableUint7(uint32_t uint7) { - return uint7 <= MASK_UINT7; - } - - public: - inline PointerAndUint7() : val_(0) {} - inline PointerAndUint7(void* ptr, uint32_t uint7) - : val_((uint64_t(ptr) << SHIFT_PTR) | (uint64_t(uint7 & MASK_UINT7))) { - MOZ_ASSERT(isRepresentablePtr(ptr)); - MOZ_ASSERT(isRepresentableUint7(uint7)); - } - inline void* pointer() const { return (void*)(int64_t(val_) >> SHIFT_PTR); } - inline uint32_t uint7() const { return uint32_t(val_ & MASK_UINT7); } -}; - -static_assert(sizeof(void*) == 8); -// "int64_t really is signed" -static_assert(((int64_t(1) << 63) >> 63) == int64_t(0xFFFFFFFFFFFFFFFFULL)); - -#else - -// The implementation for 32-bit targets. -class PointerAndUint7 final { - // The representation places the pointer in the upper 32 bits of val_ and - // the Uint7 in the lower 32 bits. This is represented using a single - // 64-bit field in the hope of increasing the chance that the class will be - // passed around in a register-pair rather than through memory. - uint64_t val_; - - static const uint8_t SHIFT_PTR = 32; - static const uint64_t MASK_UINT7 = (uint64_t(1) << 7) - 1; - - static inline bool isRepresentableUint7(uint32_t uint7) { - return uint7 <= MASK_UINT7; - } - - public: - inline PointerAndUint7() : val_(0) {} - inline PointerAndUint7(void* ptr, uint32_t uint7) - : val_((uint64_t(uint32_t(ptr)) << SHIFT_PTR) | - (uint64_t(uint7) & MASK_UINT7)) { - MOZ_ASSERT(isRepresentableUint7(uint7)); - } - inline void* pointer() const { return (void*)(int32_t(val_ >> SHIFT_PTR)); } - inline uint32_t uint7() const { return uint32_t(val_ & MASK_UINT7); } -}; - -static_assert(sizeof(void*) == 4); - -#endif // JS_64BIT - -// We require this for both 32- and 64-bit targets. -static_assert(sizeof(PointerAndUint7) == 8); - -} // namespace js - -#endif // gc_PointerAndUint7_h diff --git a/js/src/gc/GCEnum.h b/js/src/gc/GCEnum.h @@ -160,7 +160,6 @@ enum class GCAbortReason { _(XDRBufferElements) \ _(GlobalObjectData) \ _(ProxyExternalValueArray) \ - _(WasmTrailerBlock) \ _(GraphLoadingStateRecord) #define JS_FOR_EACH_MEMORY_USE(_) \ diff --git a/js/src/gc/MallocedBlockCache.cpp b/js/src/gc/MallocedBlockCache.cpp @@ -1,96 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sw=2 et tw=80: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "gc/MallocedBlockCache.h" -#include "mozilla/MemoryChecking.h" - -using js::PointerAndUint7; -using js::gc::MallocedBlockCache; - -MallocedBlockCache::~MallocedBlockCache() { clear(); } - -// This is the fallback path for MallocedBlockCache::alloc. Do not call this -// directly, since it doesn't handle all cases by itself. See ::alloc for -// further comments. -PointerAndUint7 MallocedBlockCache::allocSlow(size_t size) { - // We're never expected to handle zero-sized blocks. - MOZ_ASSERT(size > 0); - - size = js::RoundUp(size, STEP); - size_t i = size / STEP; - MOZ_ASSERT(i > 0); - - // Too large to cache; go straight to js_malloc. - if (MOZ_UNLIKELY(i >= NUM_LISTS)) { - void* p = js_malloc(size); - // If p is nullptr, that fact is carried into the PointerAndUint7, and the - // caller is expected to check that. - return PointerAndUint7(p, OVERSIZE_BLOCK_LIST_ID); - } - - // The block is of cacheable size, but we expect the relevant list to be - // empty, because ::alloc will have handled the case where it wasn't. - MOZ_ASSERT(i >= 1 && i < NUM_LISTS); - // Check that i is the right list - MOZ_ASSERT(i * STEP == size); - MOZ_RELEASE_ASSERT(lists[i].empty()); - - // And so we have to hand the request off to js_malloc. - void* p = js_malloc(size); - if (MOZ_UNLIKELY(!p)) { - return PointerAndUint7(nullptr, 0); // OOM - } - return PointerAndUint7(p, i); -} - -void MallocedBlockCache::preen(double percentOfBlocksToDiscard) { - MOZ_ASSERT(percentOfBlocksToDiscard >= 0.0 && - percentOfBlocksToDiscard <= 100.0); - MOZ_ASSERT(lists[OVERSIZE_BLOCK_LIST_ID].empty()); - for (size_t listID = 1; listID < NUM_LISTS; listID++) { - MallocedBlockVector& list = lists[listID]; - size_t numToFree = - size_t(float(list.length()) * (percentOfBlocksToDiscard / 100.0)); - MOZ_RELEASE_ASSERT(numToFree <= list.length()); - while (numToFree > 0) { - void* block = list.popCopy(); - MOZ_ASSERT(block); - js_free(block); - numToFree--; - } - } -} - -void MallocedBlockCache::clear() { - MOZ_ASSERT(lists[OVERSIZE_BLOCK_LIST_ID].empty()); - for (size_t i = 1; i < NUM_LISTS; i++) { - MallocedBlockVector& list = lists[i]; - for (void*& block : list) { - MOZ_ASSERT(block); - js_free(block); - block = nullptr; // for safety - } - list.clear(); - } -} - -size_t MallocedBlockCache::sizeOfExcludingThis( - mozilla::MallocSizeOf mallocSizeOf) const { - MOZ_ASSERT(lists[OVERSIZE_BLOCK_LIST_ID].empty()); - size_t nBytes = 0; - for (const MallocedBlockVector& list : lists) { - nBytes += list.sizeOfExcludingThis(mallocSizeOf); - // The payload size of each block in `list` is the same. Hence, we could - // possibly do better here (measure once and multiply by the length) if we - // believe that the metadata size for each block is also the same. - for (void* block : list) { - MOZ_ASSERT(block); - nBytes += mallocSizeOf(block); - } - } - return nBytes; -} diff --git a/js/src/gc/MallocedBlockCache.h b/js/src/gc/MallocedBlockCache.h @@ -1,179 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sw=2 et tw=80: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef gc_MallocedBlockCache_h -#define gc_MallocedBlockCache_h - -#include "ds/PointerAndUint7.h" -#include "js/AllocPolicy.h" -#include "js/Vector.h" -#include "util/Poison.h" - -namespace js { -namespace gc { - -// MallocedBlockCache implements a lightweight wrapper around js_malloc/js_free. -// -// Blocks are requested by ::alloc and must be returned with ::free, at which -// point the cache may decide to hold on to the block rather than hand it back -// to js_free. Subsequent ::alloc calls may be satisfied from the cached -// blocks rather than calling js_malloc. The mechanism is designed to be much -// cheaper than calling js_malloc/js_free directly. One consequence is that -// there is no locking; it is essential therefore to use each cache only from -// a single thread. -// -// The intended use is for lightweight management of OOL (malloc'd) storage -// associated with WasmStructObject and WasmArrayObject. The mechanism is -// general and potentially has other uses. Blocks of size STEP * NUM_LISTS -// and larger are never cached, though. -// -// Request sizes are rounded up to a multiple of STEP. There are NUM_LISTS-1 -// free lists, with a "List ID" indicating the multiple of STEP stored on the -// list. So for example, blocks of size 3 * STEP (after rounding up) are -// stored on the list with ID 3. List ID 0 indicates blocks which are too -// large to live on any freelist. With the default settings, this gives -// separate freelists for blocks of size 16, 32, 48, .. 496. Blocks of size -// zero are not supported, and `lists[0]` will always be empty. -// -// Allocation of a block produces not only the block's address but also its -// List ID. When freeing, both values must be presented, because there is -// otherwise no way for ::free to know the size of the original allocation, -// and hence which freelist it should go on. For this reason, the ::alloc and -// ::free methods produce and take a `PointerAndUint7`, not a `void*`. -// -// Resizing of blocks is not supported. - -class MallocedBlockCache { - public: - static const size_t STEP = 16; - - static const size_t NUM_LISTS = 32; - // This limitation exists because allocation returns a PointerAndUint7, and - // a List-ID value (viz, 0 .. NUM_LISTS-1) is stored in the Uint7 part. - static_assert(NUM_LISTS <= (1 << 7)); - - // list[0] must always remain empty. List ID 0 indicates a block which - // cannot participate in the freelist machinery because it is too large. - // - // list[i], for 1 <= i < NUM_LISTS, holds blocks of size i * STEP only. - // All requests are rounded up to multiple of SIZE. - // - // We do not expect to be required to issue or accept blocks of size zero. - static const size_t OVERSIZE_BLOCK_LIST_ID = 0; - using MallocedBlockVector = Vector<void*, 0, SystemAllocPolicy>; - - MallocedBlockVector lists[NUM_LISTS]; - - ~MallocedBlockCache(); - - static inline size_t listIDForSize(size_t size); - - // Allocation and freeing. Use `alloc` to allocate. `allocSlow` is - // `alloc`s fallback path. Do not call it directly, since it doesn't handle - // all cases by itself. - [[nodiscard]] inline PointerAndUint7 alloc(size_t size); - [[nodiscard]] MOZ_NEVER_INLINE PointerAndUint7 allocSlow(size_t size); - - inline void free(PointerAndUint7 blockAndListID); - - // Allows users to gradually hand blocks back to js_free, so as to avoid - // space leaks in long-running scenarios. The specified percentage of - // blocks in each list is discarded. - void preen(double percentOfBlocksToDiscard); - - // Return all blocks in the cache to js_free. - void clear(); - - size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; -}; - -/* static */ -inline size_t MallocedBlockCache::listIDForSize(size_t size) { - // Figure out which free list can give us a block of size `size`, after it - // has been rounded up to a multiple of `step`. - // - // Example mapping for STEP = 16 and NUM_LISTS = 8, after rounding up: - // 0 never holds any blocks (denotes "too large") - // 1 holds blocks of size 16 - // 2 holds blocks of size 32 - // 3 holds blocks of size 48 - // 4 holds blocks of size 64 - // 5 holds blocks of size 80 - // 6 holds blocks of size 96 - // 7 holds blocks of size 112 - // - // For a request of size n: - // * if n == 0, fail - // * else - // round n up to a multiple of STEP - // let i = n / STEP - // if i >= NUM_LISTS - // alloc direct from js_malloc, and return listID = 0 - // if lists[i] is nonempty, use lists[i] and return listID = i. - // else - // let p = js_malloc(n) - // return p and listID = i. - - // We're never expected to handle zero-sized blocks. - MOZ_ASSERT(size > 0); - - size = js::RoundUp(size, STEP); - size_t i = size / STEP; - MOZ_ASSERT(i > 0); - - if (i >= NUM_LISTS) { - return OVERSIZE_BLOCK_LIST_ID; - } - - return i; -} - -inline PointerAndUint7 MallocedBlockCache::alloc(size_t size) { - size_t i = listIDForSize(size); - MOZ_ASSERT(i < NUM_LISTS); - - // Fast path: try to pull a block from the relevant list. - if (MOZ_LIKELY( - i != OVERSIZE_BLOCK_LIST_ID && // "block is small enough to cache" - !lists[i].empty())) { // "a cached block is available" - // Check that i is the right list - MOZ_ASSERT(i * STEP == js::RoundUp(size, STEP)); - void* block = lists[i].popCopy(); - return PointerAndUint7(block, i); - } - - // Fallback path for all other cases. - return allocSlow(size); -} - -inline void MallocedBlockCache::free(PointerAndUint7 blockAndListID) { - // This is a whole lot simpler than the ::alloc case, since we are given the - // listId and don't have to compute it (not that we have any way to). - void* block = blockAndListID.pointer(); - uint32_t listID = blockAndListID.uint7(); - MOZ_ASSERT(block); - MOZ_ASSERT(listID < NUM_LISTS); - if (MOZ_UNLIKELY(listID == OVERSIZE_BLOCK_LIST_ID)) { - // It was too large for recycling; go straight to js_free. - js_free(block); - return; - } - - // Put it back on list `listId`, first poisoning it for safety. - memset(block, JS_NOTINUSE_TRAILER_PATTERN, listID * STEP); - MOZ_MAKE_MEM_UNDEFINED(block, listID * STEP); - if (MOZ_UNLIKELY(!lists[listID].append(block))) { - // OOM'd while doing admin. Hand it off to js_free and forget about the - // OOM. - js_free(block); - } -} - -} // namespace gc -} // namespace js - -#endif // gc_MallocedBlockCache_h diff --git a/js/src/gc/Nursery-inl.h b/js/src/gc/Nursery-inl.h @@ -270,37 +270,6 @@ inline void* js::Nursery::tryAllocate(size_t size) { return ptr; } -inline bool js::Nursery::registerTrailer(PointerAndUint7 blockAndListID, - size_t nBytes) { - MOZ_ASSERT(toSpace.trailersAdded_.length() == - toSpace.trailersRemoved_.length()); - MOZ_ASSERT(nBytes > 0); - if (MOZ_UNLIKELY(!toSpace.trailersAdded_.append(blockAndListID))) { - return false; - } - if (MOZ_UNLIKELY(!toSpace.trailersRemoved_.append(nullptr))) { - toSpace.trailersAdded_.popBack(); - return false; - } - - // This is a clone of the logic in ::registerMallocedBuffer. It may be - // that some other heuristic is better, once we know more about the - // typical behaviour of wasm-GC applications. - toSpace.trailerBytes_ += nBytes; - if (MOZ_UNLIKELY(toSpace.trailerBytes_ > capacity() * 8)) { - requestMinorGC(JS::GCReason::NURSERY_TRAILERS); - } - return true; -} - -inline void js::Nursery::unregisterTrailer(void* block) { - // Unlike removeMallocedBuffer this is only called during minor GC. - MOZ_ASSERT(fromSpace.trailersRemovedUsed_ < - fromSpace.trailersRemoved_.length()); - fromSpace.trailersRemoved_[fromSpace.trailersRemovedUsed_] = block; - fromSpace.trailersRemovedUsed_++; -} - inline void* js::Nursery::allocateBuffer(Zone* zone, js::gc::Cell* owner, size_t nbytes, size_t maxNurserySize) { MOZ_ASSERT(owner); diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp @@ -1517,114 +1517,6 @@ void js::Nursery::printDeduplicationData(js::StringStats& prev, } } -void js::Nursery::freeTrailerBlocks(JS::GCOptions options, - JS::GCReason reason) { - fromSpace.freeTrailerBlocks(mallocedBlockCache_); - - if (options == JS::GCOptions::Shrink || gc::IsOOMReason(reason)) { - mallocedBlockCache_.clear(); - return; - } - - // Discard blocks from the cache at 0.05% per megabyte of nursery capacity, - // that is, 0.8% of blocks for a 16-megabyte nursery. This allows the cache - // to gradually discard unneeded blocks in long running applications. - mallocedBlockCache_.preen(0.05 * double(capacity()) / (1024.0 * 1024.0)); -} - -void js::Nursery::Space::freeTrailerBlocks( - MallocedBlockCache& mallocedBlockCache) { - // This routine frees those blocks denoted by the set - // - // trailersAdded_ (all of it) - // - trailersRemoved_ (entries with index below trailersRemovedUsed_) - // - // For each block, places it back on the nursery's small-malloced-block pool - // by calling mallocedBlockCache.free. - - MOZ_ASSERT(trailersAdded_.length() == trailersRemoved_.length()); - MOZ_ASSERT(trailersRemovedUsed_ <= trailersRemoved_.length()); - - // Sort the removed entries. - std::sort(trailersRemoved_.begin(), - trailersRemoved_.begin() + trailersRemovedUsed_, - [](const void* block1, const void* block2) { - return uintptr_t(block1) < uintptr_t(block2); - }); - - // Use one of two schemes to enumerate the set subtraction. - if (trailersRemovedUsed_ < 1000) { - // If the number of removed items is relatively small, it isn't worth the - // cost of sorting `trailersAdded_`. Instead, walk through the vector in - // whatever order it is and use binary search to establish whether each - // item is present in trailersRemoved_[0 .. trailersRemovedUsed_ - 1]. - const size_t nAdded = trailersAdded_.length(); - for (size_t i = 0; i < nAdded; i++) { - const PointerAndUint7 block = trailersAdded_[i]; - const void* blockPointer = block.pointer(); - if (!std::binary_search(trailersRemoved_.begin(), - trailersRemoved_.begin() + trailersRemovedUsed_, - blockPointer)) { - mallocedBlockCache.free(block); - } - } - } else { - // The general case, which is algorithmically safer for large inputs. - // Sort the added entries, and then walk through both them and the removed - // entries in lockstep. - std::sort(trailersAdded_.begin(), trailersAdded_.end(), - [](const PointerAndUint7& block1, const PointerAndUint7& block2) { - return uintptr_t(block1.pointer()) < - uintptr_t(block2.pointer()); - }); - // Enumerate the set subtraction. This is somewhat simplified by the fact - // that all elements of the removed set must also be present in the added - // set. (the "inclusion property"). - const size_t nAdded = trailersAdded_.length(); - const size_t nRemoved = trailersRemovedUsed_; - size_t iAdded; - size_t iRemoved = 0; - for (iAdded = 0; iAdded < nAdded; iAdded++) { - if (iRemoved == nRemoved) { - // We've run out of items to skip, so move on to the next loop. - break; - } - const PointerAndUint7 blockAdded = trailersAdded_[iAdded]; - const void* blockRemoved = trailersRemoved_[iRemoved]; - if (blockAdded.pointer() < blockRemoved) { - mallocedBlockCache.free(blockAdded); - continue; - } - // If this doesn't hold - // (that is, if `blockAdded.pointer() > blockRemoved`), - // then the abovementioned inclusion property doesn't hold. - MOZ_RELEASE_ASSERT(blockAdded.pointer() == blockRemoved); - iRemoved++; - } - MOZ_ASSERT(iRemoved == nRemoved); - // We've used up the removed set, so now finish up the remainder of the - // added set. - for (/*keep going*/; iAdded < nAdded; iAdded++) { - const PointerAndUint7 block = trailersAdded_[iAdded]; - mallocedBlockCache.free(block); - } - } - - // And empty out both sets, but preserve the underlying storage. - trailersAdded_.clear(); - trailersRemoved_.clear(); - trailersRemovedUsed_ = 0; - trailerBytes_ = 0; -} - -size_t Nursery::sizeOfTrailerBlockSets( - mozilla::MallocSizeOf mallocSizeOf) const { - MOZ_ASSERT(fromSpace.trailersAdded_.empty()); - MOZ_ASSERT(fromSpace.trailersRemoved_.empty()); - return toSpace.trailersAdded_.sizeOfExcludingThis(mallocSizeOf) + - toSpace.trailersRemoved_.sizeOfExcludingThis(mallocSizeOf); -} - js::Nursery::CollectionResult js::Nursery::doCollection(AutoGCSession& session, JS::GCOptions options, JS::GCReason reason) { @@ -1717,12 +1609,6 @@ js::Nursery::CollectionResult js::Nursery::doCollection(AutoGCSession& session, fromSpace.mallocedBufferBytes = 0; endProfile(ProfileKey::FreeMallocedBuffers); - // Give trailer blocks associated with non-tenured Wasm{Struct,Array}Objects - // back to our `mallocedBlockCache_`. - startProfile(ProfileKey::FreeTrailerBlocks); - freeTrailerBlocks(options, reason); - endProfile(ProfileKey::FreeTrailerBlocks); - startProfile(ProfileKey::ClearNursery); clear(); endProfile(ProfileKey::ClearNursery); @@ -1962,27 +1848,6 @@ void js::Nursery::trackMallocedBufferOnPromotion(void* buffer, gc::Cell* owner, } } -void js::Nursery::trackTrailerOnPromotion(void* buffer, gc::Cell* owner, - size_t nbytes, size_t overhead, - MemoryUse use) { - MOZ_ASSERT(!isInside(buffer)); - unregisterTrailer(buffer); - - if (owner->isTenured()) { - // If we tenured the owner then account for the memory. - AddCellMemory(owner, nbytes + overhead, use); - return; - } - - // Otherwise add it to the nursery's new buffer list. - PointerAndUint7 blockAndListID(buffer, - MallocedBlockCache::listIDForSize(nbytes)); - AutoEnterOOMUnsafeRegion oomUnsafe; - if (!registerTrailer(blockAndListID, nbytes)) { - oomUnsafe.crash("Nursery::trackTrailerOnPromotion"); - } -} - Nursery::WasBufferMoved js::Nursery::maybeMoveRawBufferOnPromotion( void** bufferp, gc::Cell* owner, size_t nbytes) { bool nurseryOwned = IsInsideNursery(owner); diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h @@ -19,7 +19,6 @@ #include "gc/GCEnum.h" #include "gc/GCProbes.h" #include "gc/Heap.h" -#include "gc/MallocedBlockCache.h" #include "gc/Pretenuring.h" #include "js/AllocPolicy.h" #include "js/Class.h" @@ -50,7 +49,6 @@ _(Sweep, "sweep") \ _(UpdateJitActivations, "updtIn") \ _(FreeMallocedBuffers, "frSlts") \ - _(FreeTrailerBlocks, "frTrBs") \ _(ClearNursery, "clear") \ _(PurgeStringToAtomCache, "pStoA") \ _(Pretenure, "pretnr") @@ -268,36 +266,6 @@ class Nursery { size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const; - // Wasm "trailer" (C++-heap-allocated) blocks. - // - // All involved blocks are allocated/deallocated via this nursery's - // `mallocedBlockCache_`. Hence we must store both the block address and - // its freelist ID, wrapped up in a PointerAndUint7. - // - // Trailer blocks registered here are added to `trailersAdded_`. Those that - // are later deregistered as a result of `obj_moved` calls that indicate - // tenuring, should be added to `trailersRemoved_`. - // - // Unfortunately ::unregisterTrailer cannot be allowed to OOM. To get - // around this we rely on the observation that all deregistered blocks - // should previously have been registered, so the deregistered set can never - // be larger than the registered set. Hence ::registerTrailer effectively - // preallocates space in `trailersRemoved_` so as to ensure that, in the - // worst case, all registered blocks can be handed to ::unregisterTrailer - // without needing to resize `trailersRemoved_` in ::unregisterTrailer. - // - // The downside is that most of the space in `trailersRemoved_` is wasted in - // the case where there are few blocks deregistered. This is unfortunate - // but it's hard to see how to avoid it. - // - // At the end of a minor collection, all blocks in the set `trailersAdded_ - - // trailersRemoved_[0 .. trailersRemovedUsed_ - 1]` are handed back to the - // `mallocedBlockCache_`. - [[nodiscard]] inline bool registerTrailer(PointerAndUint7 blockAndListID, - size_t nBytes); - inline void unregisterTrailer(void* block); - size_t sizeOfTrailerBlockSets(mozilla::MallocSizeOf mallocSizeOf) const; - size_t totalCapacity() const; size_t totalCommitted() const; @@ -375,18 +343,10 @@ class Nursery { void trackMallocedBufferOnPromotion(void* buffer, gc::Cell* owner, size_t nbytes, MemoryUse use); void trackBufferOnPromotion(void* buffer, gc::Cell* owner, size_t nbytes); - void trackTrailerOnPromotion(void* buffer, gc::Cell* owner, size_t nbytes, - size_t overhead, MemoryUse use); // Round a size in bytes to the nearest valid nursery size. static size_t roundSize(size_t size); - // The malloc'd block cache. - gc::MallocedBlockCache& mallocedBlockCache() { return mallocedBlockCache_; } - size_t sizeOfMallocedBlockCache(mozilla::MallocSizeOf mallocSizeOf) const { - return mallocedBlockCache_.sizeOfExcludingThis(mallocSizeOf); - } - inline void addMallocedBufferBytes(size_t nbytes); inline void removeMallocedBufferBytes(size_t nbytes); @@ -446,8 +406,6 @@ class Nursery { // Must only be called if the previousGC data is initialised. double calcPromotionRate(bool* validForTenuring) const; - void freeTrailerBlocks(JS::GCOptions options, JS::GCReason reason); - NurseryChunk& chunk(unsigned index) const { return *toSpace.chunks_[index]; } // Set the allocation position to the start of a chunk. This sets @@ -609,13 +567,6 @@ class Nursery { BufferSet mallocedBuffers; size_t mallocedBufferBytes = 0; - // Wasm "trailer" (C++-heap-allocated) blocks. See comments above on - // ::registerTrailer and ::unregisterTrailer. - Vector<PointerAndUint7, 0, SystemAllocPolicy> trailersAdded_; - Vector<void*, 0, SystemAllocPolicy> trailersRemoved_; - size_t trailersRemovedUsed_ = 0; - size_t trailerBytes_ = 0; - gc::ChunkKind kind; explicit Space(gc::ChunkKind kind); @@ -637,7 +588,6 @@ class Nursery { bool commitSubChunkRegion(size_t oldCapacity, size_t newCapacity); void decommitSubChunkRegion(Nursery* nursery, size_t oldCapacity, size_t newCapacity); - void freeTrailerBlocks(gc::MallocedBlockCache& mallocedBlockCache); #ifdef DEBUG void checkKind(gc::ChunkKind expected) const; @@ -770,15 +720,6 @@ class Nursery { UniquePtr<NurserySweepTask> sweepTask; UniquePtr<NurseryDecommitTask> decommitTask; - // A cache of small C++-heap allocated blocks associated with this Nursery. - // This provided so as to provide cheap allocation/deallocation of - // out-of-line storage areas as used by WasmStructObject and - // WasmArrayObject, although the mechanism is general and not specific to - // these object types. Regarding lifetimes, because the cache holds only - // blocks that are not currently in use, it can be flushed at any point with - // no correctness impact, only a performance impact. - gc::MallocedBlockCache mallocedBlockCache_; - // Whether the previous collection tenured everything. This may be false if // semispace is in use. bool tenuredEverything; diff --git a/js/src/gc/moz.build b/js/src/gc/moz.build @@ -35,7 +35,6 @@ UNIFIED_SOURCES += [ "GCAPI.cpp", "GCParallelTask.cpp", "Heap.cpp", - "MallocedBlockCache.cpp", "Marking.cpp", "Nursery.cpp", "ParallelMarking.cpp", diff --git a/js/src/util/Poison.h b/js/src/util/Poison.h @@ -57,7 +57,6 @@ static MOZ_ALWAYS_INLINE void PodSet(T* aDst, const T& aSrc, size_t aNElem) { const uint8_t JS_FRESH_NURSERY_PATTERN = 0x2F; const uint8_t JS_SWEPT_NURSERY_PATTERN = 0x2B; const uint8_t JS_ALLOCATED_NURSERY_PATTERN = 0x2D; -const uint8_t JS_NOTINUSE_TRAILER_PATTERN = 0x43; const uint8_t JS_FRESH_TENURED_PATTERN = 0x4F; const uint8_t JS_MOVED_TENURED_PATTERN = 0x49; const uint8_t JS_SWEPT_TENURED_PATTERN = 0x4B; diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp @@ -335,11 +335,6 @@ void JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, gc.nursery().sizeOfMallocedBuffers(mallocSizeOf); gc.storeBuffer().addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc); - rtSizes->gc.nurseryMallocedBlockCache += - gc.nursery().sizeOfMallocedBlockCache(mallocSizeOf); - rtSizes->gc.nurseryTrailerBlockSets += - gc.nursery().sizeOfTrailerBlockSets(mallocSizeOf); - if (isMainRuntime()) { rtSizes->sharedImmutableStringsCache += js::SharedImmutableStringsCache::getSingleton().sizeOfExcludingThis(