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:
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(