From 8dd16259287f58f9273002717ec4d27e97127719 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:43:14 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- js/src/gc/Marking.cpp | 175 ++++++++++++++++++-------------------------------- 1 file changed, 64 insertions(+), 111 deletions(-) (limited to 'js/src/gc/Marking.cpp') diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 6b8742c980..6bec46940f 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -331,12 +331,20 @@ static bool ShouldTraceCrossCompartment(JSTracer* trc, JSObject* src, #ifdef DEBUG -inline void js::gc::AssertShouldMarkInZone(GCMarker* marker, Cell* thing) { - if (!thing->isMarkedBlack()) { - Zone* zone = thing->zone(); - MOZ_ASSERT(zone->isAtomsZone() || - zone->shouldMarkInZone(marker->markColor())); +template +void js::gc::AssertShouldMarkInZone(GCMarker* marker, T* thing) { + if (thing->isMarkedBlack()) { + return; } + + // Allow marking marking atoms if we're not collected the atoms zone, except + // for symbols which may entrain other GC things if they're used as weakmap + // keys. + bool allowAtoms = !std::is_same_v; + + Zone* zone = thing->zone(); + MOZ_ASSERT(zone->shouldMarkInZone(marker->markColor()) || + (allowAtoms && zone->isAtomsZone())); } void js::gc::AssertRootMarkingPhase(JSTracer* trc) { @@ -718,26 +726,6 @@ void js::gc::TraceRangeInternal(JSTracer* trc, size_t len, T* vec, namespace js { -using HasNoImplicitEdgesType = bool; - -template -struct ImplicitEdgeHolderType { - using Type = HasNoImplicitEdgesType; -}; - -// For now, we only handle JSObject* and BaseScript* keys, but the linear time -// algorithm can be easily extended by adding in more types here, then making -// GCMarker::traverse call markImplicitEdges. -template <> -struct ImplicitEdgeHolderType { - using Type = JSObject*; -}; - -template <> -struct ImplicitEdgeHolderType { - using Type = BaseScript*; -}; - void GCMarker::markEphemeronEdges(EphemeronEdgeVector& edges, gc::MarkColor srcColor) { // This is called as part of GC weak marking or by barriers outside of GC. @@ -771,86 +759,21 @@ void GCMarker::markEphemeronEdges(EphemeronEdgeVector& edges, } } -// 'delegate' is no longer the delegate of 'key'. -void GCMarker::severWeakDelegate(JSObject* key, JSObject* delegate) { - MOZ_ASSERT(CurrentThreadIsMainThread()); - - JS::Zone* zone = delegate->zone(); - MOZ_ASSERT(zone->needsIncrementalBarrier()); - - auto* p = zone->gcEphemeronEdges(delegate).get(delegate); - if (!p) { - return; - } - - // We are losing 3 edges here: key -> delegate, delegate -> key, and - // -> value. Maintain snapshot-at-beginning (hereafter, - // S-A-B) by conservatively assuming the delegate will end up black and - // marking through the latter 2 edges. - // - // Note that this does not fully give S-A-B: - // - // 1. If the map is gray, then the value will only be marked gray here even - // though the map could later be discovered to be black. - // - // 2. If the map has not yet been marked, we won't have any entries to mark - // here in the first place. - // - // 3. We're not marking the delegate, since that would cause eg nukeAllCCWs - // to keep everything alive for another collection. - // - // We can't even assume that the delegate passed in here is live, because we - // could have gotten here from nukeAllCCWs, which iterates over all CCWs - // including dead ones. - // - // This is ok because S-A-B is only needed to prevent the case where an - // unmarked object is removed from the graph and then re-inserted where it is - // reachable only by things that have already been marked. None of the 3 - // target objects will be re-inserted anywhere as a result of this action. - - EphemeronEdgeVector& edges = p->value; - MOZ_ASSERT(markColor() == MarkColor::Black); - markEphemeronEdges(edges, MarkColor::Black); -} - -// 'delegate' is now the delegate of 'key'. Update weakmap marking state. -void GCMarker::restoreWeakDelegate(JSObject* key, JSObject* delegate) { - MOZ_ASSERT(CurrentThreadIsMainThread()); - - MOZ_ASSERT(key->zone()->needsIncrementalBarrier()); - - if (!delegate->zone()->needsIncrementalBarrier()) { - // Normally we should not have added the key -> value edge if the delegate - // zone is not marking (because the delegate would have been seen as black, - // so we would mark the key immediately instead). But if there wasn't a - // delegate (the key was nuked), then we won't have consulted it. So we - // can't do the same assertion as above. - // - // Specifically, the sequence would be: - // 1. Nuke the key. - // 2. Start the incremental GC. - // 3. Mark the WeakMap. Insert a key->value edge with a DeadObjectProxy key. - // 4. Un-nuke the key with a delegate in a nonmarking Zone. - // - // The result is an ephemeron edge (from to value, but stored - // as key to value) involving a key with a delegate in a nonmarking Zone, - // something that ordinarily would not happen. - return; - } +template +struct TypeCanHaveImplicitEdges : std::false_type {}; +template <> +struct TypeCanHaveImplicitEdges : std::true_type {}; +template <> +struct TypeCanHaveImplicitEdges : std::true_type {}; +template <> +struct TypeCanHaveImplicitEdges : std::true_type {}; - auto* p = key->zone()->gcEphemeronEdges(key).get(key); - if (!p) { +template +void GCMarker::markImplicitEdges(T* markedThing) { + if constexpr (!TypeCanHaveImplicitEdges::value) { return; } - // Similar to severWeakDelegate above, mark through the key -> value edge. - EphemeronEdgeVector& edges = p->value; - MOZ_ASSERT(markColor() == MarkColor::Black); - markEphemeronEdges(edges, MarkColor::Black); -} - -template -void GCMarker::markImplicitEdgesHelper(T markedThing) { if (!isWeakMarking()) { return; } @@ -859,30 +782,34 @@ void GCMarker::markImplicitEdgesHelper(T markedThing) { MOZ_ASSERT(zone->isGCMarking()); MOZ_ASSERT(!zone->isGCSweeping()); - auto p = zone->gcEphemeronEdges().get(markedThing); + auto& ephemeronTable = zone->gcEphemeronEdges(); + auto* p = ephemeronTable.get(markedThing); if (!p) { return; } + EphemeronEdgeVector& edges = p->value; // markedThing might be a key in a debugger weakmap, which can end up marking // values that are in a different compartment. AutoClearTracingSource acts(tracer()); - CellColor thingColor = gc::detail::GetEffectiveColor(this, markedThing); - markEphemeronEdges(edges, AsMarkColor(thingColor)); -} + MarkColor thingColor = markColor(); + MOZ_ASSERT(CellColor(thingColor) == + gc::detail::GetEffectiveColor(this, markedThing)); -template <> -void GCMarker::markImplicitEdgesHelper(HasNoImplicitEdgesType) {} + markEphemeronEdges(edges, thingColor); -template -void GCMarker::markImplicitEdges(T* thing) { - markImplicitEdgesHelper::Type>(thing); + if (edges.empty()) { + ephemeronTable.remove(p); + } } template void GCMarker::markImplicitEdges(JSObject*); template void GCMarker::markImplicitEdges(BaseScript*); +#ifdef NIGHTLY_BUILD +template void GCMarker::markImplicitEdges(JS::Symbol*); +#endif } // namespace js @@ -959,6 +886,9 @@ static void TraceEdgeForBarrier(GCMarker* gcmarker, TenuredCell* thing, MOZ_ASSERT(ShouldMark(gcmarker, thing)); CheckTracedThing(gcmarker->tracer(), thing); AutoClearTracingSource acts(gcmarker->tracer()); +#ifdef DEBUG + AutoSetThreadIsMarking threadIsMarking; +#endif // DEBUG gcmarker->markAndTraverse(thing); }); } @@ -1100,6 +1030,11 @@ void GCMarker::traverse(GetterSetter* thing) { } template void GCMarker::traverse(JS::Symbol* thing) { +#ifdef NIGHTLY_BUILD + if constexpr (bool(opts & MarkingOptions::MarkImplicitEdges)) { + markImplicitEdges(thing); + } +#endif traceChildren(thing); } template @@ -1253,6 +1188,13 @@ bool js::GCMarker::mark(T* thing) { return false; } + // Don't mark symbols if we're not collecting the atoms zone. + if constexpr (std::is_same_v) { + if (!thing->zone()->isGCMarkingOrVerifyingPreBarriers()) { + return false; + } + } + AssertShouldMarkInZone(this, thing); MarkColor color = @@ -1465,7 +1407,10 @@ void GCMarker::updateRangesAtStartOfSlice() { for (MarkStackIter iter(stack); !iter.done(); iter.next()) { if (iter.isSlotsOrElementsRange()) { MarkStack::SlotsOrElementsRange& range = iter.slotsOrElementsRange(); - if (range.kind() == SlotsOrElementsKind::Elements) { + JSObject* obj = range.ptr().asRangeObject(); + if (!obj->is()) { + range.setEmpty(); + } else if (range.kind() == SlotsOrElementsKind::Elements) { NativeObject* obj = &range.ptr().asRangeObject()->as(); size_t index = range.start(); size_t numShifted = obj->getElementsHeader()->numShiftedElements(); @@ -1736,6 +1681,8 @@ inline MarkStack::TaggedPtr::TaggedPtr(Tag tag, Cell* ptr) assertValid(); } +inline uintptr_t MarkStack::TaggedPtr::asBits() const { return bits; } + inline uintptr_t MarkStack::TaggedPtr::tagUnchecked() const { return bits & TagMask; } @@ -1801,6 +1748,12 @@ inline void MarkStack::SlotsOrElementsRange::setStart(size_t newStart) { MOZ_ASSERT(start() == newStart); } +inline void MarkStack::SlotsOrElementsRange::setEmpty() { + TaggedPtr entry = TaggedPtr(ObjectTag, ptr().asRangeObject()); + ptr_ = entry; + startAndKind_ = entry.asBits(); +} + inline MarkStack::TaggedPtr MarkStack::SlotsOrElementsRange::ptr() const { return ptr_; } -- cgit v1.2.3