From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- js/src/gc/StoreBuffer.cpp | 275 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 js/src/gc/StoreBuffer.cpp (limited to 'js/src/gc/StoreBuffer.cpp') diff --git a/js/src/gc/StoreBuffer.cpp b/js/src/gc/StoreBuffer.cpp new file mode 100644 index 0000000000..d637eb62b1 --- /dev/null +++ b/js/src/gc/StoreBuffer.cpp @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 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/StoreBuffer-inl.h" + +#include "mozilla/Assertions.h" + +#include "gc/Statistics.h" +#include "vm/MutexIDs.h" +#include "vm/Runtime.h" + +using namespace js; +using namespace js::gc; + +#ifdef DEBUG +void StoreBuffer::checkAccess() const { + // The GC runs tasks that may access the storebuffer in parallel and so must + // take a lock. The mutator may only access the storebuffer from the main + // thread. + if (runtime_->heapState() != JS::HeapState::Idle) { + MOZ_ASSERT(!CurrentThreadIsGCMarking()); + runtime_->gc.assertCurrentThreadHasLockedStoreBuffer(); + } else { + MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_)); + } +} +#endif + +bool StoreBuffer::WholeCellBuffer::init() { + MOZ_ASSERT(!stringHead_); + MOZ_ASSERT(!nonStringHead_); + if (!storage_) { + storage_ = MakeUnique(LifoAllocBlockSize); + // This prevents LifoAlloc::Enum from crashing with a release + // assertion if we ever allocate one entry larger than + // LifoAllocBlockSize. + if (storage_) { + storage_->disableOversize(); + } + } + clear(); + return bool(storage_); +} + +bool StoreBuffer::GenericBuffer::init() { + if (!storage_) { + storage_ = MakeUnique(LifoAllocBlockSize); + } + clear(); + return bool(storage_); +} + +void StoreBuffer::GenericBuffer::trace(JSTracer* trc, StoreBuffer* owner) { + mozilla::ReentrancyGuard g(*owner); + MOZ_ASSERT(owner->isEnabled()); + if (!storage_) { + return; + } + + for (LifoAlloc::Enum e(*storage_); !e.empty();) { + unsigned size = *e.read(); + BufferableRef* edge = e.read(size); + edge->trace(trc); + } +} + +StoreBuffer::StoreBuffer(JSRuntime* rt) + : runtime_(rt), + nursery_(rt->gc.nursery()), + aboutToOverflow_(false), + enabled_(false), + mayHavePointersToDeadCells_(false) +#ifdef DEBUG + , + mEntered(false), + markingNondeduplicatable(false) +#endif +{ +} + +StoreBuffer::StoreBuffer(StoreBuffer&& other) + : bufferVal(std::move(other.bufferVal)), + bufStrCell(std::move(other.bufStrCell)), + bufBigIntCell(std::move(other.bufBigIntCell)), + bufObjCell(std::move(other.bufObjCell)), + bufferSlot(std::move(other.bufferSlot)), + bufferWasmAnyRef(std::move(other.bufferWasmAnyRef)), + bufferWholeCell(std::move(other.bufferWholeCell)), + bufferGeneric(std::move(other.bufferGeneric)), + runtime_(other.runtime_), + nursery_(other.nursery_), + aboutToOverflow_(other.aboutToOverflow_), + enabled_(other.enabled_), + mayHavePointersToDeadCells_(other.mayHavePointersToDeadCells_) +#ifdef DEBUG + , + mEntered(other.mEntered), + markingNondeduplicatable(other.markingNondeduplicatable) +#endif +{ + MOZ_ASSERT(enabled_); + MOZ_ASSERT(!mEntered); + MOZ_ASSERT(!markingNondeduplicatable); + other.disable(); +} + +StoreBuffer& StoreBuffer::operator=(StoreBuffer&& other) { + if (&other != this) { + this->~StoreBuffer(); + new (this) StoreBuffer(std::move(other)); + } + return *this; +} + +void StoreBuffer::checkEmpty() const { MOZ_ASSERT(isEmpty()); } + +bool StoreBuffer::isEmpty() const { + return bufferVal.isEmpty() && bufStrCell.isEmpty() && + bufBigIntCell.isEmpty() && bufObjCell.isEmpty() && + bufferSlot.isEmpty() && bufferWasmAnyRef.isEmpty() && + bufferWholeCell.isEmpty() && bufferGeneric.isEmpty(); +} + +bool StoreBuffer::enable() { + if (enabled_) { + return true; + } + + checkEmpty(); + + if (!bufferWholeCell.init() || !bufferGeneric.init()) { + return false; + } + + enabled_ = true; + return true; +} + +void StoreBuffer::disable() { + checkEmpty(); + + if (!enabled_) { + return; + } + + aboutToOverflow_ = false; + + enabled_ = false; +} + +void StoreBuffer::clear() { + if (!enabled_) { + return; + } + + aboutToOverflow_ = false; + mayHavePointersToDeadCells_ = false; + + bufferVal.clear(); + bufStrCell.clear(); + bufBigIntCell.clear(); + bufObjCell.clear(); + bufferSlot.clear(); + bufferWasmAnyRef.clear(); + bufferWholeCell.clear(); + bufferGeneric.clear(); +} + +void StoreBuffer::setAboutToOverflow(JS::GCReason reason) { + if (!aboutToOverflow_) { + aboutToOverflow_ = true; + runtime_->gc.stats().count(gcstats::COUNT_STOREBUFFER_OVERFLOW); + } + nursery_.requestMinorGC(reason); +} + +void StoreBuffer::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, + JS::GCSizes* sizes) { + sizes->storeBufferVals += bufferVal.sizeOfExcludingThis(mallocSizeOf); + sizes->storeBufferCells += bufStrCell.sizeOfExcludingThis(mallocSizeOf) + + bufBigIntCell.sizeOfExcludingThis(mallocSizeOf) + + bufObjCell.sizeOfExcludingThis(mallocSizeOf); + sizes->storeBufferSlots += bufferSlot.sizeOfExcludingThis(mallocSizeOf); + sizes->storeBufferWasmAnyRefs += + bufferWasmAnyRef.sizeOfExcludingThis(mallocSizeOf); + sizes->storeBufferWholeCells += + bufferWholeCell.sizeOfExcludingThis(mallocSizeOf); + sizes->storeBufferGenerics += bufferGeneric.sizeOfExcludingThis(mallocSizeOf); +} + +ArenaCellSet ArenaCellSet::Empty; + +ArenaCellSet::ArenaCellSet(Arena* arena, ArenaCellSet* next) + : arena(arena), + next(next) +#ifdef DEBUG + , + minorGCNumberAtCreation( + arena->zone->runtimeFromMainThread()->gc.minorGCCount()) +#endif +{ + MOZ_ASSERT(arena); + MOZ_ASSERT(bits.isAllClear()); +} + +ArenaCellSet* StoreBuffer::WholeCellBuffer::allocateCellSet(Arena* arena) { + Zone* zone = arena->zone; + JSRuntime* rt = zone->runtimeFromMainThread(); + if (!rt->gc.nursery().isEnabled()) { + return nullptr; + } + + // Maintain separate lists for strings and non-strings, so that all buffered + // string whole cells will be processed before anything else (to prevent them + // from being deduplicated when their chars are used by a tenured string.) + bool isString = + MapAllocToTraceKind(arena->getAllocKind()) == JS::TraceKind::String; + + AutoEnterOOMUnsafeRegion oomUnsafe; + ArenaCellSet*& head = isString ? stringHead_ : nonStringHead_; + auto* cells = storage_->new_(arena, head); + if (!cells) { + oomUnsafe.crash("Failed to allocate ArenaCellSet"); + } + + arena->bufferedCells() = cells; + head = cells; + + if (isAboutToOverflow()) { + rt->gc.storeBuffer().setAboutToOverflow( + JS::GCReason::FULL_WHOLE_CELL_BUFFER); + } + + return cells; +} + +void gc::CellHeaderPostWriteBarrier(JSObject** ptr, JSObject* prev, + JSObject* next) { + InternalBarrierMethods::postBarrier(ptr, prev, next); +} + +void StoreBuffer::WholeCellBuffer::clear() { + for (auto** headPtr : {&stringHead_, &nonStringHead_}) { + for (auto* set = *headPtr; set; set = set->next) { + set->arena->bufferedCells() = &ArenaCellSet::Empty; + } + *headPtr = nullptr; + } + + if (storage_) { + storage_->used() ? storage_->releaseAll() : storage_->freeAll(); + } + + last_ = nullptr; +} + +template struct StoreBuffer::MonoTypeBuffer; +template struct StoreBuffer::MonoTypeBuffer; +template struct StoreBuffer::MonoTypeBuffer; + +void js::gc::PostWriteBarrierCell(Cell* cell, Cell* prev, Cell* next) { + if (!next || !cell->isTenured()) { + return; + } + + StoreBuffer* buffer = next->storeBuffer(); + if (!buffer || (prev && prev->storeBuffer())) { + return; + } + + buffer->putWholeCell(cell); +} -- cgit v1.2.3