diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 5f9789009af8..ce04ab43dc73 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -464,9 +464,9 @@ namespace JS { D(FULL_CELL_PTR_STR_BUFFER, 28) \ D(TOO_MUCH_JIT_CODE, 29) \ D(FULL_CELL_PTR_BIGINT_BUFFER, 30) \ + D(INIT_SELF_HOSTING, 31) \ \ /* These are reserved for future use. */ \ - D(RESERVED7, 31) \ D(RESERVED8, 32) \ \ /* Reasons from Firefox */ \ diff --git a/js/src/gc/Allocator.cpp b/js/src/gc/Allocator.cpp index 6f04952be726..037166d35ea3 100644 --- a/js/src/gc/Allocator.cpp +++ b/js/src/gc/Allocator.cpp @@ -411,6 +411,8 @@ bool GCRuntime::checkAllocatorState(JSContext* cx, AllocKind kind) { kind == AllocKind::SCOPE); MOZ_ASSERT_IF(!cx->zone()->isAtomsZone(), kind != AllocKind::ATOM && kind != AllocKind::FAT_INLINE_ATOM); + MOZ_ASSERT_IF(cx->zone()->isSelfHostingZone(), + !rt->parentRuntime && !selfHostingZoneFrozen); MOZ_ASSERT(!JS::RuntimeHeapIsBusy()); #endif diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp index bf8100f3d814..166a8de8832c 100644 --- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -1258,6 +1258,21 @@ bool GCRuntime::init(uint32_t maxbytes) { return true; } +void GCRuntime::freezeSelfHostingZone() { + MOZ_ASSERT(!selfHostingZoneFrozen); + MOZ_ASSERT(!isIncrementalGCInProgress()); + + for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) { + MOZ_ASSERT(!zone->isGCScheduled()); + if (zone->isSelfHostingZone()) { + zone->scheduleGC(); + } + } + + gc(GC_SHRINK, JS::GCReason::INIT_SELF_HOSTING); + selfHostingZoneFrozen = true; +} + void GCRuntime::finish() { // Wait for nursery background free to end and disable it to release memory. if (nursery().isEnabled()) { @@ -1704,8 +1719,16 @@ AutoDisableCompactingGC::~AutoDisableCompactingGC() { --cx->compactingDisabledCount; } -static bool CanRelocateZone(Zone* zone) { - return !zone->isAtomsZone() && !zone->isSelfHostingZone(); +bool GCRuntime::canRelocateZone(Zone* zone) const { + if (zone->isAtomsZone()) { + return false; + } + + if (zone->isSelfHostingZone() && selfHostingZoneFrozen) { + return false; + } + + return true; } Arena* ArenaList::removeRemainingArenas(Arena** arenap) { @@ -2018,7 +2041,7 @@ bool GCRuntime::relocateArenas(Zone* zone, JS::GCReason reason, gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::COMPACT_MOVE); MOZ_ASSERT(!zone->isPreservingCode()); - MOZ_ASSERT(CanRelocateZone(zone)); + MOZ_ASSERT(canRelocateZone(zone)); js::CancelOffThreadIonCompile(rt, JS::Zone::Compact); @@ -3841,7 +3864,7 @@ bool GCRuntime::prepareZonesForCollection(JS::GCReason reason, MOZ_ASSERT(zone->canCollect()); any = true; zone->changeGCState(Zone::NoGC, Zone::MarkBlackOnly); - } else { + } else if (zone->canCollect()) { *isFullOut = false; } @@ -3911,7 +3934,7 @@ void GCRuntime::relazifyFunctionsForShrinkingGC() { void GCRuntime::purgeShapeCachesForShrinkingGC() { gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::PURGE_SHAPE_CACHES); for (GCZonesIter zone(this); !zone.done(); zone.next()) { - if (!CanRelocateZone(zone) || zone->keepShapeCaches()) { + if (!canRelocateZone(zone) || zone->keepShapeCaches()) { continue; } for (auto baseShape = zone->cellIterUnsafe(); !baseShape.done(); @@ -3927,7 +3950,7 @@ void GCRuntime::purgeSourceURLsForShrinkingGC() { gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::PURGE_SOURCE_URLS); for (GCZonesIter zone(this); !zone.done(); zone.next()) { // URLs are not tracked for realms in the system zone. - if (!CanRelocateZone(zone) || zone->isSystem) { + if (!canRelocateZone(zone) || zone->isSystem) { continue; } for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) { @@ -6127,7 +6150,7 @@ void GCRuntime::beginCompactPhase() { MOZ_ASSERT(zonesToMaybeCompact.ref().isEmpty()); for (GCZonesIter zone(this); !zone.done(); zone.next()) { - if (CanRelocateZone(zone)) { + if (canRelocateZone(zone)) { zonesToMaybeCompact.ref().append(zone); } } diff --git a/js/src/gc/GC.h b/js/src/gc/GC.h index 1292de03355b..c6f14c06f22d 100644 --- a/js/src/gc/GC.h +++ b/js/src/gc/GC.h @@ -137,6 +137,8 @@ void FinishGC(JSContext* cx, JS::GCReason = JS::GCReason::FINISH_GC); */ void MergeRealms(JS::Realm* source, JS::Realm* target); +void CollectSelfHostingZone(JSContext* cx); + enum VerifierType { PreBarrierVerifier }; #ifdef JS_GC_ZEAL diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 55d1e91cbcac..c0d932b5b11a 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -261,6 +261,9 @@ class GCRuntime { JS::HeapState heapState() const { return heapState_; } + void freezeSelfHostingZone(); + bool isSelfHostingZoneFrozen() const { return selfHostingZoneFrozen; } + inline bool hasZealMode(ZealMode mode); inline void clearZealMode(ZealMode mode); inline bool upcomingZealousGC(); @@ -734,6 +737,7 @@ class GCRuntime { void endCompactPhase(); void sweepTypesAfterCompacting(Zone* zone); void sweepZoneAfterCompacting(MovingTracer* trc, Zone* zone); + bool canRelocateZone(Zone* zone) const; MOZ_MUST_USE bool relocateArenas(Zone* zone, JS::GCReason reason, Arena*& relocatedListOut, SliceBudget& sliceBudget); @@ -862,6 +866,12 @@ class GCRuntime { mozilla::recordreplay::Behavior::DontPreserve> numActiveZoneIters; + /* + * The self hosting zone is collected once after initialization. We don't + * allow allocation after this point and we don't collect it again. + */ + WriteOnceData selfHostingZoneFrozen; + /* During shutdown, the GC needs to clean up every possible object. */ MainThreadData cleanUpEverything; diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index b784671c7e47..14e19befc62a 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -202,7 +202,6 @@ bool Zone::init(bool isSystemArg) { } void Zone::setNeedsIncrementalBarrier(bool needs) { - MOZ_ASSERT_IF(needs, canCollect()); needsIncrementalBarrier_ = needs; } @@ -498,6 +497,11 @@ bool Zone::canCollect() { return !runtimeFromAnyThread()->hasHelperThreadZones(); } + // We don't collect the self hosting zone after it has been initialized. + if (isSelfHostingZone()) { + return !runtimeFromAnyThread()->gc.isSelfHostingZoneFrozen(); + } + // Zones that will be or are currently used by other threads cannot be // collected. return !createdForHelperThread(); diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index c6019a1efc15..4418365ecc11 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -276,7 +276,7 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase { void changeGCState(GCState prev, GCState next) { MOZ_ASSERT(RuntimeHeapIsBusy()); MOZ_ASSERT(gcState() == prev); - MOZ_ASSERT_IF(next != NoGC, canCollect()); + MOZ_ASSERT(canCollect()); gcState_ = next; } diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 40f3b2947277..bdec328124c7 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -2769,6 +2769,10 @@ bool JSRuntime::initSelfHosting(JSContext* cx) { return false; } + // Garbage collect the self hosting zone once when it is created. It should + // not be modified after this point. + cx->runtime()->gc.freezeSelfHostingZone(); + return true; }