summaryrefslogtreecommitdiffstats
path: root/js/src/gc/Tenuring.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/gc/Tenuring.cpp')
-rw-r--r--js/src/gc/Tenuring.cpp710
1 files changed, 457 insertions, 253 deletions
diff --git a/js/src/gc/Tenuring.cpp b/js/src/gc/Tenuring.cpp
index a9506cfa14..d38a374599 100644
--- a/js/src/gc/Tenuring.cpp
+++ b/js/src/gc/Tenuring.cpp
@@ -18,6 +18,7 @@
#include "gc/Pretenuring.h"
#include "gc/Zone.h"
#include "jit/JitCode.h"
+#include "js/TypeDecls.h"
#include "proxy/Proxy.h"
#include "vm/BigIntType.h"
#include "vm/JSScript.h"
@@ -44,104 +45,120 @@ using mozilla::PodCopy;
constexpr size_t MAX_DEDUPLICATABLE_STRING_LENGTH = 500;
-TenuringTracer::TenuringTracer(JSRuntime* rt, Nursery* nursery)
+TenuringTracer::TenuringTracer(JSRuntime* rt, Nursery* nursery,
+ bool tenureEverything)
: JSTracer(rt, JS::TracerKind::Tenuring,
JS::WeakMapTraceAction::TraceKeysAndValues),
- nursery_(*nursery) {
+ nursery_(*nursery),
+ tenureEverything(tenureEverything) {
stringDeDupSet.emplace();
}
-size_t TenuringTracer::getTenuredSize() const {
- return tenuredSize + tenuredCells * sizeof(NurseryCellHeader);
+size_t TenuringTracer::getPromotedSize() const {
+ return promotedSize + promotedCells * sizeof(NurseryCellHeader);
}
-size_t TenuringTracer::getTenuredCells() const { return tenuredCells; }
-
-static inline void UpdateAllocSiteOnTenure(Cell* cell) {
- AllocSite* site = NurseryCellHeader::from(cell)->allocSite();
- site->incTenuredCount();
-}
+size_t TenuringTracer::getPromotedCells() const { return promotedCells; }
void TenuringTracer::onObjectEdge(JSObject** objp, const char* name) {
JSObject* obj = *objp;
- if (!IsInsideNursery(obj)) {
+ if (!nursery_.inCollectedRegion(obj)) {
+ MOZ_ASSERT(!obj->isForwarded());
return;
}
+ *objp = promoteOrForward(obj);
+ MOZ_ASSERT(!(*objp)->isForwarded());
+}
+
+JSObject* TenuringTracer::promoteOrForward(JSObject* obj) {
+ MOZ_ASSERT(nursery_.inCollectedRegion(obj));
+
if (obj->isForwarded()) {
const gc::RelocationOverlay* overlay = gc::RelocationOverlay::fromCell(obj);
- *objp = static_cast<JSObject*>(overlay->forwardingAddress());
- return;
+ obj = static_cast<JSObject*>(overlay->forwardingAddress());
+ if (IsInsideNursery(obj)) {
+ promotedToNursery = true;
+ }
+ return obj;
}
- onNonForwardedNurseryObjectEdge(objp);
+ return onNonForwardedNurseryObject(obj);
}
-void TenuringTracer::onNonForwardedNurseryObjectEdge(JSObject** objp) {
- JSObject* obj = *objp;
+JSObject* TenuringTracer::onNonForwardedNurseryObject(JSObject* obj) {
MOZ_ASSERT(IsInsideNursery(obj));
MOZ_ASSERT(!obj->isForwarded());
- UpdateAllocSiteOnTenure(obj);
-
- // Take a fast path for tenuring a plain object which is by far the most
+ // Take a fast path for promoting a plain object as this is by far the most
// common case.
if (obj->is<PlainObject>()) {
- *objp = movePlainObjectToTenured(&obj->as<PlainObject>());
- return;
+ return promotePlainObject(&obj->as<PlainObject>());
}
- *objp = moveToTenuredSlow(obj);
+ return promoteObjectSlow(obj);
}
void TenuringTracer::onStringEdge(JSString** strp, const char* name) {
JSString* str = *strp;
- if (!IsInsideNursery(str)) {
+ if (!nursery_.inCollectedRegion(str)) {
return;
}
+ *strp = promoteOrForward(str);
+}
+
+JSString* TenuringTracer::promoteOrForward(JSString* str) {
+ MOZ_ASSERT(nursery_.inCollectedRegion(str));
+
if (str->isForwarded()) {
const gc::RelocationOverlay* overlay = gc::RelocationOverlay::fromCell(str);
- *strp = static_cast<JSString*>(overlay->forwardingAddress());
- return;
+ str = static_cast<JSString*>(overlay->forwardingAddress());
+ if (IsInsideNursery(str)) {
+ promotedToNursery = true;
+ }
+ return str;
}
- onNonForwardedNurseryStringEdge(strp);
+ return onNonForwardedNurseryString(str);
}
-void TenuringTracer::onNonForwardedNurseryStringEdge(JSString** strp) {
- JSString* str = *strp;
+JSString* TenuringTracer::onNonForwardedNurseryString(JSString* str) {
MOZ_ASSERT(IsInsideNursery(str));
MOZ_ASSERT(!str->isForwarded());
- UpdateAllocSiteOnTenure(str);
-
- *strp = moveToTenured(str);
+ return promoteString(str);
}
void TenuringTracer::onBigIntEdge(JS::BigInt** bip, const char* name) {
JS::BigInt* bi = *bip;
- if (!IsInsideNursery(bi)) {
+ if (!nursery_.inCollectedRegion(bi)) {
return;
}
+ *bip = promoteOrForward(bi);
+}
+
+JS::BigInt* TenuringTracer::promoteOrForward(JS::BigInt* bi) {
+ MOZ_ASSERT(nursery_.inCollectedRegion(bi));
+
if (bi->isForwarded()) {
const gc::RelocationOverlay* overlay = gc::RelocationOverlay::fromCell(bi);
- *bip = static_cast<JS::BigInt*>(overlay->forwardingAddress());
- return;
+ bi = static_cast<JS::BigInt*>(overlay->forwardingAddress());
+ if (IsInsideNursery(bi)) {
+ promotedToNursery = true;
+ }
+ return bi;
}
- onNonForwardedNurseryBigIntEdge(bip);
+ return onNonForwardedNurseryBigInt(bi);
}
-void TenuringTracer::onNonForwardedNurseryBigIntEdge(JS::BigInt** bip) {
- JS::BigInt* bi = *bip;
+JS::BigInt* TenuringTracer::onNonForwardedNurseryBigInt(JS::BigInt* bi) {
MOZ_ASSERT(IsInsideNursery(bi));
MOZ_ASSERT(!bi->isForwarded());
- UpdateAllocSiteOnTenure(bi);
-
- *bip = moveToTenured(bi);
+ return promoteBigInt(bi);
}
void TenuringTracer::onSymbolEdge(JS::Symbol** symp, const char* name) {}
@@ -156,7 +173,7 @@ void TenuringTracer::onJitCodeEdge(jit::JitCode** codep, const char* name) {}
void TenuringTracer::onScopeEdge(Scope** scopep, const char* name) {}
void TenuringTracer::traverse(JS::Value* thingp) {
- MOZ_ASSERT(!nursery().isInside(thingp));
+ MOZ_ASSERT(!nursery().inCollectedRegion(thingp));
Value value = *thingp;
CheckTracedThing(this, value);
@@ -166,66 +183,71 @@ void TenuringTracer::traverse(JS::Value* thingp) {
}
Cell* cell = value.toGCThing();
- if (!IsInsideNursery(cell)) {
+ if (!nursery_.inCollectedRegion(cell)) {
return;
}
if (cell->isForwarded()) {
const gc::RelocationOverlay* overlay =
gc::RelocationOverlay::fromCell(cell);
- thingp->changeGCThingPayload(overlay->forwardingAddress());
+ Cell* target = overlay->forwardingAddress();
+ thingp->changeGCThingPayload(target);
+ if (IsInsideNursery(target)) {
+ promotedToNursery = true;
+ }
return;
}
// We only care about a few kinds of GC thing here and this generates much
// tighter code than using MapGCThingTyped.
if (value.isObject()) {
- JSObject* obj = &value.toObject();
- onNonForwardedNurseryObjectEdge(&obj);
+ JSObject* obj = onNonForwardedNurseryObject(&value.toObject());
MOZ_ASSERT(obj != &value.toObject());
*thingp = JS::ObjectValue(*obj);
return;
}
#ifdef ENABLE_RECORD_TUPLE
if (value.isExtendedPrimitive()) {
- JSObject* obj = &value.toExtendedPrimitive();
- onNonForwardedNurseryObjectEdge(&obj);
+ JSObject* obj = onNonForwardedNurseryObject(&value.toExtendedPrimitive());
MOZ_ASSERT(obj != &value.toExtendedPrimitive());
*thingp = JS::ExtendedPrimitiveValue(*obj);
return;
}
#endif
if (value.isString()) {
- JSString* str = value.toString();
- onNonForwardedNurseryStringEdge(&str);
+ JSString* str = onNonForwardedNurseryString(value.toString());
MOZ_ASSERT(str != value.toString());
*thingp = JS::StringValue(str);
return;
}
MOZ_ASSERT(value.isBigInt());
- JS::BigInt* bi = value.toBigInt();
- onNonForwardedNurseryBigIntEdge(&bi);
+ JS::BigInt* bi = onNonForwardedNurseryBigInt(value.toBigInt());
MOZ_ASSERT(bi != value.toBigInt());
*thingp = JS::BigIntValue(bi);
}
void TenuringTracer::traverse(wasm::AnyRef* thingp) {
- MOZ_ASSERT(!nursery().isInside(thingp));
+ MOZ_ASSERT(!nursery().inCollectedRegion(thingp));
wasm::AnyRef value = *thingp;
CheckTracedThing(this, value);
+ Cell* cell = value.toGCThing();
+ if (!nursery_.inCollectedRegion(cell)) {
+ return;
+ }
+
wasm::AnyRef post = wasm::AnyRef::invalid();
switch (value.kind()) {
case wasm::AnyRefKind::Object: {
- JSObject* obj = &value.toJSObject();
- onObjectEdge(&obj, "value");
+ JSObject* obj = promoteOrForward(&value.toJSObject());
+ MOZ_ASSERT(obj != &value.toJSObject());
post = wasm::AnyRef::fromJSObject(*obj);
break;
}
case wasm::AnyRefKind::String: {
- JSString* str = value.toJSString();
- onStringEdge(&str, "string");
+ JSString* str = promoteOrForward(value.toJSString());
+ MOZ_ASSERT(str != value.toJSString());
post = wasm::AnyRef::fromJSString(str);
break;
}
@@ -236,11 +258,20 @@ void TenuringTracer::traverse(wasm::AnyRef* thingp) {
}
}
- if (post != value) {
- *thingp = post;
- }
+ *thingp = post;
}
+class MOZ_RAII TenuringTracer::AutoPromotedAnyToNursery {
+ public:
+ explicit AutoPromotedAnyToNursery(TenuringTracer& trc) : trc_(trc) {
+ trc.promotedToNursery = false;
+ }
+ explicit operator bool() const { return trc_.promotedToNursery; }
+
+ private:
+ TenuringTracer& trc_;
+};
+
template <typename T>
void js::gc::StoreBuffer::MonoTypeBuffer<T>::trace(TenuringTracer& mover,
StoreBuffer* owner) {
@@ -279,6 +310,8 @@ void js::gc::StoreBuffer::SlotsEdge::trace(TenuringTracer& mover) const {
MOZ_ASSERT(!IsInsideNursery(obj), "obj shouldn't live in nursery.");
+ TenuringTracer::AutoPromotedAnyToNursery promotedToNursery(mover);
+
if (kind() == ElementKind) {
uint32_t initLen = obj->getDenseInitializedLength();
uint32_t numShifted = obj->getElementsHeader()->numShiftedElements();
@@ -289,99 +322,60 @@ void js::gc::StoreBuffer::SlotsEdge::trace(TenuringTracer& mover) const {
clampedEnd = numShifted < clampedEnd ? clampedEnd - numShifted : 0;
clampedEnd = std::min(clampedEnd, initLen);
MOZ_ASSERT(clampedStart <= clampedEnd);
- mover.traceSlots(
- static_cast<HeapSlot*>(obj->getDenseElements() + clampedStart)
- ->unbarrieredAddress(),
- clampedEnd - clampedStart);
+ auto* slotStart =
+ static_cast<HeapSlot*>(obj->getDenseElements() + clampedStart);
+ uint32_t nslots = clampedEnd - clampedStart;
+ mover.traceObjectElements(slotStart->unbarrieredAddress(), nslots);
} else {
uint32_t start = std::min(start_, obj->slotSpan());
uint32_t end = std::min(start_ + count_, obj->slotSpan());
MOZ_ASSERT(start <= end);
mover.traceObjectSlots(obj, start, end);
}
+
+ if (promotedToNursery) {
+ mover.runtime()->gc.storeBuffer().putSlot(obj, kind(), start_, count_);
+ }
}
static inline void TraceWholeCell(TenuringTracer& mover, JSObject* object) {
- MOZ_ASSERT_IF(object->storeBuffer(),
- !object->storeBuffer()->markingNondeduplicatable);
mover.traceObject(object);
}
-// Non-deduplicatable marking is necessary because of the following 2 reasons:
+// Return whether the string needs to be swept.
//
-// 1. Tenured string chars cannot be updated:
+// We can break down the relevant dependency chains as follows:
//
-// If any of the tenured string's bases were deduplicated during tenuring,
-// the tenured string's chars pointer would need to be adjusted. This would
-// then require updating any other tenured strings that are dependent on the
-// first tenured string, and we have no way to find them without scanning
-// the entire tenured heap.
+// T -> T2 : will not be swept, but safe because T2.chars is fixed.
+// T -> N1 -> ... -> T2 : safe because T2.chars is fixed
+// T -> N1 -> ... -> N2 : update T.chars += tenured(N2).chars - N2.chars
//
-// 2. Tenured string cannot store its nursery base or base's chars:
+// Collapse the base chain down to simply T -> T2 or T -> N2. The pointer update
+// will happen during sweeping.
//
-// Tenured strings have no place to stash a pointer to their nursery base or
-// its chars. You need to be able to traverse any dependent string's chain
-// of bases up to a nursery "root base" that points to the malloced chars
-// that the dependent strings started out pointing to, so that you can
-// calculate the offset of any dependent string and update the ptr+offset if
-// the root base gets deduplicated to a different allocation. Tenured
-// strings in this base chain will stop you from reaching the nursery
-// version of the root base; you can only get to the tenured version, and it
-// has no place to store the original chars pointer.
-static inline void PreventDeduplicationOfReachableStrings(JSString* str) {
- MOZ_ASSERT(str->isTenured());
- MOZ_ASSERT(!str->isForwarded());
-
- JSLinearString* baseOrRelocOverlay = str->nurseryBaseOrRelocOverlay();
-
- // Walk along the chain of dependent strings' base string pointers
- // to mark them all non-deduplicatable.
- while (true) {
- // baseOrRelocOverlay can be one of the three cases:
- // 1. forwarded nursery string:
- // The forwarded string still retains the flag that can tell whether
- // this string is a dependent string with a base. Its
- // StringRelocationOverlay holds a saved pointer to its base in the
- // nursery.
- // 2. not yet forwarded nursery string:
- // Retrieve the base field directly from the string.
- // 3. tenured string:
- // The nursery base chain ends here, so stop traversing.
- if (baseOrRelocOverlay->isForwarded()) {
- JSLinearString* tenuredBase = Forwarded(baseOrRelocOverlay);
- if (!tenuredBase->hasBase()) {
- break;
- }
- baseOrRelocOverlay = StringRelocationOverlay::fromCell(baseOrRelocOverlay)
- ->savedNurseryBaseOrRelocOverlay();
- } else {
- JSLinearString* base = baseOrRelocOverlay;
- if (base->isTenured()) {
- break;
- }
- if (base->isDeduplicatable()) {
- base->setNonDeduplicatable();
- }
- if (!base->hasBase()) {
- break;
- }
- baseOrRelocOverlay = base->nurseryBaseOrRelocOverlay();
- }
- }
-}
-
-static inline void TraceWholeCell(TenuringTracer& mover, JSString* str) {
- MOZ_ASSERT_IF(str->storeBuffer(),
- str->storeBuffer()->markingNondeduplicatable);
-
- // Mark all strings reachable from the tenured string `str` as
- // non-deduplicatable. These strings are the bases of the tenured dependent
- // string.
+// Note that in cases like T -> N1 -> T2 -> T3 -> N2, both T -> N1 and T3 -> N2
+// will be processed by the whole cell buffer (or rather, only T and T3 will
+// be in the store buffer). The order that these strings are
+// visited does not matter because the nursery bases are left alone until
+// sweeping.
+static inline bool TraceWholeCell(TenuringTracer& mover, JSString* str) {
if (str->hasBase()) {
- PreventDeduplicationOfReachableStrings(str);
+ // For tenured dependent strings -> nursery string edges, sweep the
+ // (tenured) strings at the end of nursery marking to update chars pointers
+ // that were in the nursery. Rather than updating the base pointer to point
+ // directly to the tenured version of itself, we will leave it pointing at
+ // the nursery Cell (which will become a StringRelocationOverlay during the
+ // minor GC.)
+ JSLinearString* base = str->nurseryBaseOrRelocOverlay();
+ if (IsInsideNursery(base)) {
+ str->traceBaseFromStoreBuffer(&mover);
+ return IsInsideNursery(str->nurseryBaseOrRelocOverlay());
+ }
}
str->traceChildren(&mover);
+
+ return false;
}
static inline void TraceWholeCell(TenuringTracer& mover, BaseScript* script) {
@@ -394,70 +388,173 @@ static inline void TraceWholeCell(TenuringTracer& mover,
}
template <typename T>
-static void TraceBufferedCells(TenuringTracer& mover, Arena* arena,
- ArenaCellSet* cells) {
+bool TenuringTracer::traceBufferedCells(Arena* arena, ArenaCellSet* cells) {
for (size_t i = 0; i < MaxArenaCellIndex; i += cells->BitsPerWord) {
ArenaCellSet::WordT bitset = cells->getWord(i / cells->BitsPerWord);
while (bitset) {
size_t bit = i + js::detail::CountTrailingZeroes(bitset);
+ bitset &= bitset - 1; // Clear the low bit.
+
auto cell =
reinterpret_cast<T*>(uintptr_t(arena) + ArenaCellIndexBytes * bit);
- TraceWholeCell(mover, cell);
- bitset &= bitset - 1; // Clear the low bit.
+
+ TenuringTracer::AutoPromotedAnyToNursery promotedToNursery(*this);
+
+ TraceWholeCell(*this, cell);
+
+ if (promotedToNursery) {
+ runtime()->gc.storeBuffer().putWholeCell(cell);
+ }
}
}
+
+ return false;
}
-void ArenaCellSet::trace(TenuringTracer& mover) {
- for (ArenaCellSet* cells = this; cells; cells = cells->next) {
+template <>
+bool TenuringTracer::traceBufferedCells<JSString>(Arena* arena,
+ ArenaCellSet* cells) {
+ bool needsSweep = false;
+ for (size_t i = 0; i < MaxArenaCellIndex; i += cells->BitsPerWord) {
+ ArenaCellSet::WordT bitset = cells->getWord(i / cells->BitsPerWord);
+ ArenaCellSet::WordT tosweep = bitset;
+ while (bitset) {
+ size_t bit = i + js::detail::CountTrailingZeroes(bitset);
+ auto* cell = reinterpret_cast<JSString*>(uintptr_t(arena) +
+ ArenaCellIndexBytes * bit);
+ TenuringTracer::AutoPromotedAnyToNursery promotedToNursery(*this);
+ bool needsSweep = TraceWholeCell(*this, cell);
+ if (promotedToNursery) {
+ runtime()->gc.storeBuffer().putWholeCell(cell);
+ }
+ ArenaCellSet::WordT mask = bitset - 1;
+ bitset &= mask;
+ if (!needsSweep) {
+ tosweep &= mask;
+ }
+ }
+
+ cells->setWord(i / cells->BitsPerWord, tosweep);
+ if (tosweep) {
+ needsSweep = true;
+ }
+ }
+
+ return needsSweep;
+}
+
+ArenaCellSet* ArenaCellSet::trace(TenuringTracer& mover) {
+ ArenaCellSet* head = nullptr;
+
+ ArenaCellSet* cells = this;
+ while (cells) {
cells->check();
Arena* arena = cells->arena;
arena->bufferedCells() = &ArenaCellSet::Empty;
JS::TraceKind kind = MapAllocToTraceKind(arena->getAllocKind());
+ bool needsSweep;
switch (kind) {
case JS::TraceKind::Object:
- TraceBufferedCells<JSObject>(mover, arena, cells);
+ needsSweep = mover.traceBufferedCells<JSObject>(arena, cells);
break;
case JS::TraceKind::String:
- TraceBufferedCells<JSString>(mover, arena, cells);
+ needsSweep = mover.traceBufferedCells<JSString>(arena, cells);
break;
case JS::TraceKind::Script:
- TraceBufferedCells<BaseScript>(mover, arena, cells);
+ needsSweep = mover.traceBufferedCells<BaseScript>(arena, cells);
break;
case JS::TraceKind::JitCode:
- TraceBufferedCells<jit::JitCode>(mover, arena, cells);
+ needsSweep = mover.traceBufferedCells<jit::JitCode>(arena, cells);
break;
default:
MOZ_CRASH("Unexpected trace kind");
}
+
+ ArenaCellSet* next = cells->next;
+ if (needsSweep) {
+ cells->next = head;
+ head = cells;
+ }
+
+ cells = next;
}
+
+ return head;
}
void js::gc::StoreBuffer::WholeCellBuffer::trace(TenuringTracer& mover,
StoreBuffer* owner) {
MOZ_ASSERT(owner->isEnabled());
-#ifdef DEBUG
- // Verify that all string whole cells are traced first before any other
- // strings are visited for any reason.
- MOZ_ASSERT(!owner->markingNondeduplicatable);
- owner->markingNondeduplicatable = true;
-#endif
- // Trace all of the strings to mark the non-deduplicatable bits, then trace
- // all other whole cells.
- if (stringHead_) {
- stringHead_->trace(mover);
+ if (head_) {
+ head_ = head_->trace(mover);
}
-#ifdef DEBUG
- owner->markingNondeduplicatable = false;
-#endif
- if (nonStringHead_) {
- nonStringHead_->trace(mover);
+}
+
+// Sweep a tenured dependent string with a nursery base. The base chain will
+// have been collapsed to a single link before this string was added to the
+// sweep set, so only the simple case of a tenured dependent string with a
+// nursery base needs to be considered.
+template <typename CharT>
+void JSDependentString::sweepTypedAfterMinorGC() {
+ MOZ_ASSERT(isTenured());
+ MOZ_ASSERT(IsInsideNursery(nurseryBaseOrRelocOverlay()));
+
+ JSLinearString* base = nurseryBaseOrRelocOverlay();
+ MOZ_ASSERT(IsInsideNursery(base));
+ MOZ_ASSERT(!Forwarded(base)->hasBase(), "base chain should be collapsed");
+ MOZ_ASSERT(base->isForwarded(), "root base should be kept alive");
+ auto* baseOverlay = js::gc::StringRelocationOverlay::fromCell(base);
+ const CharT* oldBaseChars = baseOverlay->savedNurseryChars<CharT>();
+
+ // We have the base's original chars pointer and its current chars pointer.
+ // Update our chars pointer, which is an offset from the original base
+ // chars, and make it point to the same offset within the root's chars.
+ // (Most of the time, the base chars didn't move and so this has no
+ // effect.)
+ const CharT* oldChars = JSString::nonInlineCharsRaw<CharT>();
+ size_t offset = oldChars - oldBaseChars;
+ JSLinearString* tenuredBase = Forwarded(base);
+ MOZ_ASSERT(offset < tenuredBase->length());
+
+ const CharT* newBaseChars = tenuredBase->JSString::nonInlineCharsRaw<CharT>();
+ relocateNonInlineChars(newBaseChars, offset);
+
+ d.s.u3.base = tenuredBase;
+}
+
+inline void JSDependentString::sweepAfterMinorGC() {
+ if (hasTwoByteChars()) {
+ sweepTypedAfterMinorGC<char16_t>();
+ } else {
+ sweepTypedAfterMinorGC<JS::Latin1Char>();
+ }
+}
+
+static void SweepDependentStrings(Arena* arena, ArenaCellSet* cells) {
+ for (size_t i = 0; i < MaxArenaCellIndex; i += cells->BitsPerWord) {
+ ArenaCellSet::WordT bitset = cells->getWord(i / cells->BitsPerWord);
+ while (bitset) {
+ size_t bit = i + js::detail::CountTrailingZeroes(bitset);
+ auto* str = reinterpret_cast<JSString*>(uintptr_t(arena) +
+ ArenaCellIndexBytes * bit);
+ MOZ_ASSERT(str->isTenured());
+ str->asDependent().sweepAfterMinorGC();
+ bitset &= bitset - 1; // Clear the low bit.
+ }
}
+}
- stringHead_ = nonStringHead_ = nullptr;
+void ArenaCellSet::sweepDependentStrings() {
+ for (ArenaCellSet* cells = this; cells; cells = cells->next) {
+ Arena* arena = cells->arena;
+ arena->bufferedCells() = &ArenaCellSet::Empty;
+ MOZ_ASSERT(MapAllocToTraceKind(arena->getAllocKind()) ==
+ JS::TraceKind::String);
+ SweepDependentStrings(arena, cells);
+ }
}
template <typename T>
@@ -481,18 +578,42 @@ void js::gc::StoreBuffer::CellPtrEdge<T>::trace(TenuringTracer& mover) const {
edge, JS::TraceKind::String));
}
- DispatchToOnEdge(&mover, edge, "CellPtrEdge");
+ if (!mover.nursery().inCollectedRegion(thing)) {
+ return;
+ }
+
+ *edge = mover.promoteOrForward(thing);
+
+ if (IsInsideNursery(*edge)) {
+ mover.runtime()->gc.storeBuffer().putCell(edge);
+ }
}
void js::gc::StoreBuffer::ValueEdge::trace(TenuringTracer& mover) const {
- if (deref()) {
- mover.traverse(edge);
+ if (!isGCThing()) {
+ return;
+ }
+
+ TenuringTracer::AutoPromotedAnyToNursery promotedToNursery(mover);
+
+ mover.traverse(edge);
+
+ if (promotedToNursery) {
+ mover.runtime()->gc.storeBuffer().putValue(edge);
}
}
void js::gc::StoreBuffer::WasmAnyRefEdge::trace(TenuringTracer& mover) const {
- if (deref()) {
- mover.traverse(edge);
+ if (!isGCThing()) {
+ return;
+ }
+
+ TenuringTracer::AutoPromotedAnyToNursery promotedToNursery(mover);
+
+ mover.traverse(edge);
+
+ if (promotedToNursery) {
+ mover.runtime()->gc.storeBuffer().putWasmAnyRef(edge);
}
}
@@ -513,7 +634,7 @@ void js::gc::TenuringTracer::traceObject(JSObject* obj) {
if (!nobj->hasEmptyElements()) {
HeapSlotArray elements = nobj->getDenseElements();
Value* elems = elements.begin()->unbarrieredAddress();
- traceSlots(elems, elems + nobj->getDenseInitializedLength());
+ traceObjectElements(elems, nobj->getDenseInitializedLength());
}
traceObjectSlots(nobj, 0, nobj->slotSpan());
@@ -527,16 +648,17 @@ void js::gc::TenuringTracer::traceObjectSlots(NativeObject* nobj,
nobj->forEachSlotRange(start, end, traceRange);
}
+void js::gc::TenuringTracer::traceObjectElements(JS::Value* vp,
+ uint32_t count) {
+ traceSlots(vp, vp + count);
+}
+
void js::gc::TenuringTracer::traceSlots(Value* vp, Value* end) {
for (; vp != end; ++vp) {
traverse(vp);
}
}
-inline void js::gc::TenuringTracer::traceSlots(JS::Value* vp, uint32_t nslots) {
- traceSlots(vp, vp + nslots);
-}
-
void js::gc::TenuringTracer::traceString(JSString* str) {
str->traceChildren(this);
}
@@ -564,25 +686,73 @@ inline void js::gc::TenuringTracer::insertIntoObjectFixupList(
}
template <typename T>
-inline T* js::gc::TenuringTracer::allocTenured(Zone* zone, AllocKind kind) {
- return static_cast<T*>(static_cast<Cell*>(AllocateCellInGC(zone, kind)));
+inline T* js::gc::TenuringTracer::alloc(Zone* zone, AllocKind kind, Cell* src) {
+ AllocSite* site = NurseryCellHeader::from(src)->allocSite();
+ site->incPromotedCount();
+
+ void* ptr = allocCell<T::TraceKind>(zone, kind, site, src);
+ auto* cell = reinterpret_cast<T*>(ptr);
+ if (IsInsideNursery(cell)) {
+ MOZ_ASSERT(!nursery().inCollectedRegion(cell));
+ promotedToNursery = true;
+ }
+
+ return cell;
}
-JSString* js::gc::TenuringTracer::allocTenuredString(JSString* src, Zone* zone,
- AllocKind dstKind) {
- JSString* dst = allocTenured<JSString>(zone, dstKind);
- tenuredSize += moveStringToTenured(dst, src, dstKind);
- tenuredCells++;
+template <JS::TraceKind traceKind>
+void* js::gc::TenuringTracer::allocCell(Zone* zone, AllocKind allocKind,
+ AllocSite* site, Cell* src) {
+ MOZ_ASSERT(zone == src->zone());
+
+ if (!shouldTenure(zone, traceKind, src)) {
+ // Allocations from the optimized alloc site continue to use that site,
+ // otherwise a special promoted alloc site it used.
+ if (site->kind() != AllocSite::Kind::Optimized) {
+ site = &zone->pretenuring.promotedAllocSite(traceKind);
+ }
+
+ size_t thingSize = Arena::thingSize(allocKind);
+ void* ptr = nursery_.tryAllocateCell(site, thingSize, traceKind);
+ if (MOZ_LIKELY(ptr)) {
+ return ptr;
+ }
+
+ JSContext* cx = runtime()->mainContextFromOwnThread();
+ ptr = CellAllocator::RetryNurseryAlloc<NoGC>(cx, traceKind, allocKind,
+ thingSize, site);
+ if (MOZ_LIKELY(ptr)) {
+ return ptr;
+ }
+
+ // The nursery is full. This is unlikely but can happen. Fall through to
+ // the tenured allocation path.
+ }
+
+ return AllocateTenuredCellInGC(zone, allocKind);
+}
+
+JSString* js::gc::TenuringTracer::allocString(JSString* src, Zone* zone,
+ AllocKind dstKind) {
+ JSString* dst = alloc<JSString>(zone, dstKind, src);
+ promotedSize += moveString(dst, src, dstKind);
+ promotedCells++;
return dst;
}
-JSObject* js::gc::TenuringTracer::moveToTenuredSlow(JSObject* src) {
+bool js::gc::TenuringTracer::shouldTenure(Zone* zone, JS::TraceKind traceKind,
+ Cell* cell) {
+ return tenureEverything || !zone->allocKindInNursery(traceKind) ||
+ nursery_.shouldTenure(cell);
+}
+
+JSObject* js::gc::TenuringTracer::promoteObjectSlow(JSObject* src) {
MOZ_ASSERT(IsInsideNursery(src));
MOZ_ASSERT(!src->is<PlainObject>());
AllocKind dstKind = src->allocKindForTenure(nursery());
- auto* dst = allocTenured<JSObject>(src->nurseryZone(), dstKind);
+ auto* dst = alloc<JSObject>(src->nurseryZone(), dstKind, src);
size_t srcSize = Arena::thingSize(dstKind);
@@ -590,8 +760,8 @@ JSObject* js::gc::TenuringTracer::moveToTenuredSlow(JSObject* src) {
// and dst. We deal with this by copying elements manually, possibly
// re-inlining them if there is adequate room inline in dst.
//
- // For Arrays and Tuples we're reducing tenuredSize to the smaller srcSize
- // because moveElementsToTenured() accounts for all Array or Tuple elements,
+ // For Arrays and Tuples we're reducing promotedSize to the smaller srcSize
+ // because moveElements() accounts for all Array or Tuple elements,
// even if they are inlined.
if (src->is<FixedLengthTypedArrayObject>()) {
auto* tarray = &src->as<FixedLengthTypedArrayObject>();
@@ -613,8 +783,8 @@ JSObject* js::gc::TenuringTracer::moveToTenuredSlow(JSObject* src) {
srcSize = sizeof(NativeObject);
}
- tenuredSize += srcSize;
- tenuredCells++;
+ promotedSize += srcSize;
+ promotedCells++;
// Copy the Cell contents.
MOZ_ASSERT(OffsetFromChunkStart(src) >= sizeof(ChunkBase));
@@ -625,8 +795,8 @@ JSObject* js::gc::TenuringTracer::moveToTenuredSlow(JSObject* src) {
if (src->is<NativeObject>()) {
NativeObject* ndst = &dst->as<NativeObject>();
NativeObject* nsrc = &src->as<NativeObject>();
- tenuredSize += moveSlotsToTenured(ndst, nsrc);
- tenuredSize += moveElementsToTenured(ndst, nsrc, dstKind);
+ promotedSize += moveSlots(ndst, nsrc);
+ promotedSize += moveElements(ndst, nsrc, dstKind);
}
JSObjectMovedOp op = dst->getClass()->extObjectMovedOp();
@@ -634,7 +804,7 @@ JSObject* js::gc::TenuringTracer::moveToTenuredSlow(JSObject* src) {
if (op) {
// Tell the hazard analysis that the object moved hook can't GC.
JS::AutoSuppressGCAnalysis nogc;
- tenuredSize += op(dst, src);
+ promotedSize += op(dst, src);
} else {
MOZ_ASSERT_IF(src->getClass()->hasFinalize(),
CanNurseryAllocateFinalizedClass(src->getClass()));
@@ -647,18 +817,17 @@ JSObject* js::gc::TenuringTracer::moveToTenuredSlow(JSObject* src) {
return dst;
}
-inline JSObject* js::gc::TenuringTracer::movePlainObjectToTenured(
- PlainObject* src) {
- // Fast path version of moveToTenuredSlow() for specialized for PlainObject.
+inline JSObject* js::gc::TenuringTracer::promotePlainObject(PlainObject* src) {
+ // Fast path version of promoteObjectSlow() for specialized for PlainObject.
MOZ_ASSERT(IsInsideNursery(src));
AllocKind dstKind = src->allocKindForTenure();
- auto* dst = allocTenured<PlainObject>(src->nurseryZone(), dstKind);
+ auto* dst = alloc<PlainObject>(src->nurseryZone(), dstKind, src);
size_t srcSize = Arena::thingSize(dstKind);
- tenuredSize += srcSize;
- tenuredCells++;
+ promotedSize += srcSize;
+ promotedCells++;
// Copy the Cell contents.
MOZ_ASSERT(OffsetFromChunkStart(src) >= sizeof(ChunkBase));
@@ -666,8 +835,8 @@ inline JSObject* js::gc::TenuringTracer::movePlainObjectToTenured(
js_memcpy(dst, src, srcSize);
// Move the slots and elements.
- tenuredSize += moveSlotsToTenured(dst, src);
- tenuredSize += moveElementsToTenured(dst, src, dstKind);
+ promotedSize += moveSlots(dst, src);
+ promotedSize += moveElements(dst, src, dstKind);
MOZ_ASSERT(!dst->getClass()->extObjectMovedOp());
@@ -678,8 +847,7 @@ inline JSObject* js::gc::TenuringTracer::movePlainObjectToTenured(
return dst;
}
-size_t js::gc::TenuringTracer::moveSlotsToTenured(NativeObject* dst,
- NativeObject* src) {
+size_t js::gc::TenuringTracer::moveSlots(NativeObject* dst, NativeObject* src) {
/* Fixed slots have already been copied over. */
if (!src->hasDynamicSlots()) {
return 0;
@@ -702,9 +870,9 @@ size_t js::gc::TenuringTracer::moveSlotsToTenured(NativeObject* dst,
return allocSize;
}
-size_t js::gc::TenuringTracer::moveElementsToTenured(NativeObject* dst,
- NativeObject* src,
- AllocKind dstKind) {
+size_t js::gc::TenuringTracer::moveElements(NativeObject* dst,
+ NativeObject* src,
+ AllocKind dstKind) {
if (src->hasEmptyElements()) {
return 0;
}
@@ -751,7 +919,7 @@ inline void js::gc::TenuringTracer::insertIntoStringFixupList(
stringHead = entry;
}
-JSString* js::gc::TenuringTracer::moveToTenured(JSString* src) {
+JSString* js::gc::TenuringTracer::promoteString(JSString* src) {
MOZ_ASSERT(IsInsideNursery(src));
MOZ_ASSERT(!src->isExternal());
@@ -762,29 +930,32 @@ JSString* js::gc::TenuringTracer::moveToTenured(JSString* src) {
// the atom. Don't do this for dependent strings because they're more
// complicated. See StringRelocationOverlay and DeduplicationStringHasher
// comments.
+ MOZ_ASSERT(!src->isAtom());
if (src->isLinear() && src->inStringToAtomCache() &&
src->isDeduplicatable() && !src->hasBase()) {
JSLinearString* linear = &src->asLinear();
JSAtom* atom = runtime()->caches().stringToAtomCache.lookupInMap(linear);
- MOZ_ASSERT(atom, "Why was the cache purged before minor GC?");
-
- // Only deduplicate if both strings have the same encoding, to not confuse
- // dependent strings.
- if (src->hasTwoByteChars() == atom->hasTwoByteChars()) {
- // The StringToAtomCache isn't used for inline strings (due to the minimum
- // length) so canOwnDependentChars must be true for both src and atom.
- // This means if there are dependent strings floating around using str's
- // chars, they will be able to use the chars from the atom.
- static_assert(StringToAtomCache::MinStringLength >
- JSFatInlineString::MAX_LENGTH_LATIN1);
- static_assert(StringToAtomCache::MinStringLength >
- JSFatInlineString::MAX_LENGTH_TWO_BYTE);
- MOZ_ASSERT(src->canOwnDependentChars());
- MOZ_ASSERT(atom->canOwnDependentChars());
-
- StringRelocationOverlay::forwardCell(src, atom);
- gcprobes::PromoteToTenured(src, atom);
- return atom;
+ // The string will not be present in the cache if it was previously promoted
+ // to the second nursery generation.
+ if (atom) {
+ // Only deduplicate if both strings have the same encoding, to not confuse
+ // dependent strings.
+ if (src->hasTwoByteChars() == atom->hasTwoByteChars()) {
+ // The StringToAtomCache isn't used for inline strings (due to the
+ // minimum length) so canOwnDependentChars must be true for both src and
+ // atom. This means if there are dependent strings floating around using
+ // str's chars, they will be able to use the chars from the atom.
+ static_assert(StringToAtomCache::MinStringLength >
+ JSFatInlineString::MAX_LENGTH_LATIN1);
+ static_assert(StringToAtomCache::MinStringLength >
+ JSFatInlineString::MAX_LENGTH_TWO_BYTE);
+ MOZ_ASSERT(src->canOwnDependentChars());
+ MOZ_ASSERT(atom->canOwnDependentChars());
+
+ StringRelocationOverlay::forwardCell(src, atom);
+ gcprobes::PromoteToTenured(src, atom);
+ return atom;
+ }
}
}
@@ -798,9 +969,12 @@ JSString* js::gc::TenuringTracer::moveToTenured(JSString* src) {
// 3. It is deduplicatable:
// The JSString NON_DEDUP_BIT flag is unset.
// 4. It matches an entry in stringDeDupSet.
+ // 5. It is moved to the tenured heap.
- if (src->length() < MAX_DEDUPLICATABLE_STRING_LENGTH && src->isLinear() &&
+ if (shouldTenure(zone, JS::TraceKind::String, src) &&
+ src->length() < MAX_DEDUPLICATABLE_STRING_LENGTH && src->isLinear() &&
src->isDeduplicatable() && stringDeDupSet.isSome()) {
+ src->clearBitsOnTenure();
auto p = stringDeDupSet->lookupForAdd(src);
if (p) {
// Deduplicate to the looked-up string!
@@ -811,7 +985,7 @@ JSString* js::gc::TenuringTracer::moveToTenured(JSString* src) {
return dst;
}
- dst = allocTenuredString(src, zone, dstKind);
+ dst = allocString(src, zone, dstKind);
using DedupHasher [[maybe_unused]] = DeduplicationStringHasher<JSString*>;
MOZ_ASSERT(DedupHasher::hash(src) == DedupHasher::hash(dst),
@@ -823,14 +997,17 @@ JSString* js::gc::TenuringTracer::moveToTenured(JSString* src) {
stringDeDupSet.reset();
}
} else {
- dst = allocTenuredString(src, zone, dstKind);
- dst->clearNonDeduplicatable();
+ dst = allocString(src, zone, dstKind);
+ if (dst->isTenured()) {
+ src->clearBitsOnTenure();
+ dst->clearBitsOnTenure();
+ }
}
zone->stringStats.ref().noteTenured(src->allocSize());
auto* overlay = StringRelocationOverlay::forwardCell(src, dst);
- MOZ_ASSERT(dst->isDeduplicatable());
+ MOZ_ASSERT_IF(dst->isTenured() && dst->isLinear(), dst->isDeduplicatable());
if (dst->hasBase() || dst->isRope()) {
// dst or one of its leaves might have a base that will be deduplicated.
@@ -874,6 +1051,9 @@ void js::gc::TenuringTracer::relocateDependentStringChars(
tenuredDependentStr->relocateNonInlineChars<const CharT*>(
tenuredRootBase->nonInlineChars<CharT>(nogc), *offset);
tenuredDependentStr->setBase(tenuredRootBase);
+ if (tenuredDependentStr->isTenured() && !tenuredRootBase->isTenured()) {
+ runtime()->gc.storeBuffer().putWholeCell(tenuredDependentStr);
+ }
return;
}
@@ -889,7 +1069,7 @@ void js::gc::TenuringTracer::relocateDependentStringChars(
// The root base can be in either the nursery or the tenured heap.
// dependentStr chars needs to be relocated after traceString if the
// root base is in the nursery.
- if (!(*rootBase)->isTenured()) {
+ if (nursery().inCollectedRegion(*rootBase)) {
*rootBaseNotYetForwarded = true;
const CharT* rootBaseChars = (*rootBase)->nonInlineChars<CharT>(nogc);
*offset = dependentStrChars - rootBaseChars;
@@ -906,15 +1086,15 @@ void js::gc::TenuringTracer::relocateDependentStringChars(
}
}
-JS::BigInt* js::gc::TenuringTracer::moveToTenured(JS::BigInt* src) {
+JS::BigInt* js::gc::TenuringTracer::promoteBigInt(JS::BigInt* src) {
MOZ_ASSERT(IsInsideNursery(src));
AllocKind dstKind = src->getAllocKind();
Zone* zone = src->nurseryZone();
- JS::BigInt* dst = allocTenured<JS::BigInt>(zone, dstKind);
- tenuredSize += moveBigIntToTenured(dst, src, dstKind);
- tenuredCells++;
+ JS::BigInt* dst = alloc<JS::BigInt>(zone, dstKind, src);
+ promotedSize += moveBigInt(dst, src, dstKind);
+ promotedCells++;
RelocationOverlay::forwardCell(src, dst);
@@ -924,41 +1104,57 @@ JS::BigInt* js::gc::TenuringTracer::moveToTenured(JS::BigInt* src) {
void js::gc::TenuringTracer::collectToObjectFixedPoint() {
while (RelocationOverlay* p = objHead) {
+ MOZ_ASSERT(nursery().inCollectedRegion(p));
objHead = objHead->next();
auto* obj = static_cast<JSObject*>(p->forwardingAddress());
+
+ MOZ_ASSERT_IF(IsInsideNursery(obj), !nursery().inCollectedRegion(obj));
+
+ AutoPromotedAnyToNursery promotedAnyToNursery(*this);
traceObject(obj);
+ if (obj->isTenured() && promotedAnyToNursery) {
+ runtime()->gc.storeBuffer().putWholeCell(obj);
+ }
}
}
void js::gc::TenuringTracer::collectToStringFixedPoint() {
while (StringRelocationOverlay* p = stringHead) {
+ MOZ_ASSERT(nursery().inCollectedRegion(p));
stringHead = stringHead->next();
- auto* tenuredStr = static_cast<JSString*>(p->forwardingAddress());
+ auto* str = static_cast<JSString*>(p->forwardingAddress());
+ MOZ_ASSERT_IF(IsInsideNursery(str), !nursery().inCollectedRegion(str));
+
// To ensure the NON_DEDUP_BIT was reset properly.
- MOZ_ASSERT(tenuredStr->isDeduplicatable());
+ MOZ_ASSERT(!str->isAtom());
+ MOZ_ASSERT_IF(str->isTenured() && str->isLinear(), str->isDeduplicatable());
// The nursery root base might not be forwarded before
- // traceString(tenuredStr). traceString(tenuredStr) will forward the root
+ // traceString(str). traceString(str) will forward the root
// base if that's the case. Dependent string chars needs to be relocated
// after traceString if root base was not forwarded.
size_t offset = 0;
bool rootBaseNotYetForwarded = false;
JSLinearString* rootBase = nullptr;
- if (tenuredStr->isDependent()) {
- if (tenuredStr->hasTwoByteChars()) {
+ if (str->isDependent()) {
+ if (str->hasTwoByteChars()) {
relocateDependentStringChars<char16_t>(
- &tenuredStr->asDependent(), p->savedNurseryBaseOrRelocOverlay(),
- &offset, &rootBaseNotYetForwarded, &rootBase);
+ &str->asDependent(), p->savedNurseryBaseOrRelocOverlay(), &offset,
+ &rootBaseNotYetForwarded, &rootBase);
} else {
relocateDependentStringChars<JS::Latin1Char>(
- &tenuredStr->asDependent(), p->savedNurseryBaseOrRelocOverlay(),
- &offset, &rootBaseNotYetForwarded, &rootBase);
+ &str->asDependent(), p->savedNurseryBaseOrRelocOverlay(), &offset,
+ &rootBaseNotYetForwarded, &rootBase);
}
}
- traceString(tenuredStr);
+ AutoPromotedAnyToNursery promotedAnyToNursery(*this);
+ traceString(str);
+ if (str->isTenured() && promotedAnyToNursery) {
+ runtime()->gc.storeBuffer().putWholeCell(str);
+ }
if (rootBaseNotYetForwarded) {
MOZ_ASSERT(rootBase->isForwarded(),
@@ -968,25 +1164,34 @@ void js::gc::TenuringTracer::collectToStringFixedPoint() {
JSLinearString* tenuredRootBase = Forwarded(rootBase);
MOZ_ASSERT(offset < tenuredRootBase->length());
- if (tenuredStr->hasTwoByteChars()) {
- tenuredStr->asDependent().relocateNonInlineChars<const char16_t*>(
+ if (str->hasTwoByteChars()) {
+ str->asDependent().relocateNonInlineChars<const char16_t*>(
tenuredRootBase->twoByteChars(nogc), offset);
} else {
- tenuredStr->asDependent().relocateNonInlineChars<const JS::Latin1Char*>(
+ str->asDependent().relocateNonInlineChars<const JS::Latin1Char*>(
tenuredRootBase->latin1Chars(nogc), offset);
}
- tenuredStr->setBase(tenuredRootBase);
+
+ str->setBase(tenuredRootBase);
+ if (str->isTenured() && !tenuredRootBase->isTenured()) {
+ runtime()->gc.storeBuffer().putWholeCell(str);
+ }
+ }
+
+ if (str->hasBase()) {
+ MOZ_ASSERT(!str->base()->isForwarded());
+ MOZ_ASSERT_IF(!str->base()->isTenured(),
+ !nursery().inCollectedRegion(str->base()));
}
}
}
-size_t js::gc::TenuringTracer::moveStringToTenured(JSString* dst, JSString* src,
- AllocKind dstKind) {
+size_t js::gc::TenuringTracer::moveString(JSString* dst, JSString* src,
+ AllocKind dstKind) {
size_t size = Arena::thingSize(dstKind);
- // At the moment, strings always have the same AllocKind between src and
- // dst. This may change in the future.
- MOZ_ASSERT(dst->asTenured().getAllocKind() == src->getAllocKind());
+ MOZ_ASSERT_IF(dst->isTenured(),
+ dst->asTenured().getAllocKind() == src->getAllocKind());
// Copy the Cell contents.
MOZ_ASSERT(OffsetToChunkEnd(src) >= size);
@@ -999,7 +1204,8 @@ size_t js::gc::TenuringTracer::moveStringToTenured(JSString* dst, JSString* src,
if (src->ownsMallocedChars()) {
void* chars = src->asLinear().nonInlineCharsRaw();
nursery().removeMallocedBufferDuringMinorGC(chars);
- AddCellMemory(dst, dst->asLinear().allocSize(), MemoryUse::StringContents);
+ nursery().trackMallocedBufferOnPromotion(
+ chars, dst, dst->asLinear().allocSize(), MemoryUse::StringContents);
return size;
}
@@ -1016,14 +1222,12 @@ size_t js::gc::TenuringTracer::moveStringToTenured(JSString* dst, JSString* src,
return size;
}
-size_t js::gc::TenuringTracer::moveBigIntToTenured(JS::BigInt* dst,
- JS::BigInt* src,
- AllocKind dstKind) {
+size_t js::gc::TenuringTracer::moveBigInt(JS::BigInt* dst, JS::BigInt* src,
+ AllocKind dstKind) {
size_t size = Arena::thingSize(dstKind);
- // At the moment, BigInts always have the same AllocKind between src and
- // dst. This may change in the future.
- MOZ_ASSERT(dst->asTenured().getAllocKind() == src->getAllocKind());
+ MOZ_ASSERT_IF(dst->isTenured(),
+ dst->asTenured().getAllocKind() == src->getAllocKind());
// Copy the Cell contents.
MOZ_ASSERT(OffsetToChunkEnd(src) >= size);