From 51405c5297f29f76fe7af9f29fd4c15d0c655093 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Mon, 18 Sep 2023 08:32:55 +0000 Subject: [PATCH] Bug 1853305 - Part 1: Add AutoSelectGCHeap and use it when creating property iterators r=sfink This class observes nursery collections and encasuplates the logic of switching heap after a specified number of collections occur. Differential Revision: https://phabricator.services.mozilla.com/D188333 --- js/src/gc/GC.h | 29 +++++++++++++++++++++++++++++ js/src/gc/GCAPI.cpp | 29 +++++++++++++++++++++++++++++ js/src/jsnum.cpp | 9 +++++++-- js/src/jsnum.h | 4 ++++ js/src/vm/Iteration.cpp | 8 +++++++- js/src/vm/JSAtomUtils-inl.h | 5 +++-- 6 files changed, 79 insertions(+), 5 deletions(-) diff --git a/js/src/gc/GC.h b/js/src/gc/GC.h index 30dc08d8c1ca..d6c8c70f33a6 100644 --- a/js/src/gc/GC.h +++ b/js/src/gc/GC.h @@ -11,6 +11,7 @@ #ifndef gc_GC_h #define gc_GC_h +#include "gc/AllocKind.h" #include "gc/GCEnum.h" #include "js/GCAPI.h" #include "js/HeapAPI.h" @@ -238,6 +239,34 @@ struct MOZ_RAII AutoDisableCompactingGC { JSContext* cx; }; +/* + * Dynamically select the GC heap to allocate into for a graph of GC things. + * + * Initially |heap()| will return Heap::Default to select nursery allocation, + * but when a specified number of nursery collections have been triggered it + * switches to returning Heap::Tenured. + */ +class MOZ_RAII AutoSelectGCHeap { + public: + explicit AutoSelectGCHeap(JSContext* cx, + size_t allowedNurseryCollections = 0); + ~AutoSelectGCHeap(); + + gc::Heap heap() const { return heap_; } + operator gc::Heap() const { return heap_; } + + void onNurseryCollectionEnd(); + + private: + static void NurseryCollectionCallback(JSContext* cx, + JS::GCNurseryProgress progress, + JS::GCReason reason, void* data); + + JSContext* cx_; + size_t allowedNurseryCollections_; + gc::Heap heap_ = gc::Heap::Default; +}; + } /* namespace js */ #endif /* gc_GC_h */ diff --git a/js/src/gc/GCAPI.cpp b/js/src/gc/GCAPI.cpp index e1d083f49bef..103c8b504996 100644 --- a/js/src/gc/GCAPI.cpp +++ b/js/src/gc/GCAPI.cpp @@ -797,3 +797,32 @@ JS_PUBLIC_API void js::gc::SetPerformanceHint(JSContext* cx, cx->runtime()->gc.setPerformanceHint(hint); } + +AutoSelectGCHeap::AutoSelectGCHeap(JSContext* cx, + size_t allowedNurseryCollections) + : cx_(cx), allowedNurseryCollections_(allowedNurseryCollections) { + JS::AddGCNurseryCollectionCallback(cx, &NurseryCollectionCallback, this); +} + +AutoSelectGCHeap::~AutoSelectGCHeap() { + JS::RemoveGCNurseryCollectionCallback(cx_, &NurseryCollectionCallback, this); +} + +/* static */ +void AutoSelectGCHeap::NurseryCollectionCallback(JSContext* cx, + JS::GCNurseryProgress progress, + JS::GCReason reason, + void* data) { + if (progress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END) { + static_cast(data)->onNurseryCollectionEnd(); + } +} + +void AutoSelectGCHeap::onNurseryCollectionEnd() { + if (allowedNurseryCollections_ != 0) { + allowedNurseryCollections_--; + return; + } + + heap_ = gc::Heap::Tenured; +} diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 4f5d4fa96562..da9c0c1d1e93 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -808,6 +808,12 @@ MOZ_ALWAYS_INLINE static T* BackfillInt32InBuffer(int32_t si, T* buffer, template JSLinearString* js::Int32ToString(JSContext* cx, int32_t si) { + return js::Int32ToStringWithHeap(cx, si, gc::Heap::Default); +} + +template +JSLinearString* js::Int32ToStringWithHeap(JSContext* cx, int32_t si, + gc::Heap heap) { if (JSLinearString* str = LookupInt32ToString(cx, si)) { return str; } @@ -818,8 +824,7 @@ JSLinearString* js::Int32ToString(JSContext* cx, int32_t si) { BackfillInt32InBuffer(si, buffer, std::size(buffer), &length); mozilla::Range chars(start, length); - JSInlineString* str = - NewInlineString(cx, chars, js::gc::Heap::Default); + JSInlineString* str = NewInlineString(cx, chars, heap); if (!str) { return nullptr; } diff --git a/js/src/jsnum.h b/js/src/jsnum.h index 3912fa845e6e..5e0cf439e379 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -53,6 +53,10 @@ frontend::TaggedParserAtomIndex NumberToParserAtom( template extern JSLinearString* Int32ToString(JSContext* cx, int32_t i); +template +extern JSLinearString* Int32ToStringWithHeap(JSContext* cx, int32_t i, + gc::Heap heap); + extern JSLinearString* Int32ToStringPure(JSContext* cx, int32_t i); extern JSString* Int32ToStringWithBase(JSContext* cx, int32_t i, int32_t base); diff --git a/js/src/vm/Iteration.cpp b/js/src/vm/Iteration.cpp index b6f70fa7653d..edc9068985cd 100644 --- a/js/src/vm/Iteration.cpp +++ b/js/src/vm/Iteration.cpp @@ -24,6 +24,7 @@ #include "builtin/Array.h" #include "builtin/SelfHostingDefines.h" #include "ds/Sort.h" +#include "gc/GC.h" #include "gc/GCContext.h" #include "js/ForOfIterator.h" // JS::ForOfIterator #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* @@ -964,9 +965,14 @@ NativeIterator::NativeIterator(JSContext* cx, } MOZ_ASSERT(static_cast(shapesEnd_) == propertyCursor_); + // Allocate any strings in the nursery until the first minor GC. After this + // point they will end up getting tenured anyway because they are reachable + // from |propIter| which will be tenured. + AutoSelectGCHeap gcHeap(cx); + size_t numProps = props.length(); for (size_t i = 0; i < numProps; i++) { - JSLinearString* str = IdToString(cx, props[i]); + JSLinearString* str = IdToString(cx, props[i], gcHeap); if (!str) { *hadError = true; return; diff --git a/js/src/vm/JSAtomUtils-inl.h b/js/src/vm/JSAtomUtils-inl.h index f2266ea4f4d7..8843aa383e5f 100644 --- a/js/src/vm/JSAtomUtils-inl.h +++ b/js/src/vm/JSAtomUtils-inl.h @@ -115,13 +115,14 @@ inline bool IndexToId(JSContext* cx, uint32_t index, MutableHandleId idp) { return IndexToIdSlow(cx, index, idp); } -static MOZ_ALWAYS_INLINE JSLinearString* IdToString(JSContext* cx, jsid id) { +static MOZ_ALWAYS_INLINE JSLinearString* IdToString( + JSContext* cx, jsid id, gc::Heap heap = gc::Heap::Default) { if (id.isString()) { return id.toAtom(); } if (MOZ_LIKELY(id.isInt())) { - return Int32ToString(cx, id.toInt()); + return Int32ToStringWithHeap(cx, id.toInt(), heap); } RootedValue idv(cx, IdToValue(id)); -- 2.11.4.GIT