summaryrefslogtreecommitdiffstats
path: root/js/src/gc/StoreBuffer.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/gc/StoreBuffer.cpp
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/gc/StoreBuffer.cpp')
-rw-r--r--js/src/gc/StoreBuffer.cpp275
1 files changed, 275 insertions, 0 deletions
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<LifoAlloc>(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<LifoAlloc>(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<unsigned>();
+ BufferableRef* edge = e.read<BufferableRef>(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_<ArenaCellSet>(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<JSObject*>::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<StoreBuffer::ValueEdge>;
+template struct StoreBuffer::MonoTypeBuffer<StoreBuffer::SlotsEdge>;
+template struct StoreBuffer::MonoTypeBuffer<StoreBuffer::WasmAnyRefEdge>;
+
+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);
+}