Bug 1645113 - Don't sweep arenas that were allocated during marking asa they cannot contain any dead cells r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Fri, 12 Jun 2020 08:03:39 +0000
changeset 535276 5742bc131451a5af275ebe5bf99ea77e493f1a94
parent 535275 515bfdb0607ae8b0837360c8624780faeea5f7e2
child 535277 7c9218657abb0b0292366a61d39c1803eb1b6ee5
push id37501
push usernbeleuzu@mozilla.com
push dateSat, 13 Jun 2020 03:21:52 +0000
treeherdermozilla-central@80b6f21783a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1645113
milestone79.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1645113 - Don't sweep arenas that were allocated during marking asa they cannot contain any dead cells r=sfink The patch adds areanas allocated during marking into a separate set of arenas lists, which are not swept but are merged back into the main arena lists at the end of sweeping. We do need to do some sweeping for newly allocated arenas on account of type inference data. I haven't looked too hard into why this is. Differential Revision: https://phabricator.services.mozilla.com/D79334
js/src/gc/Allocator.cpp
js/src/gc/ArenaList-inl.h
js/src/gc/ArenaList.h
js/src/gc/GC-inl.h
js/src/gc/GC.cpp
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -550,18 +550,17 @@ TenuredCell* ArenaLists::refillFreeListA
 
   mozilla::Maybe<AutoLockGCBgAlloc> maybeLock;
 
   // See if we can proceed without taking the GC lock.
   if (concurrentUse(thingKind) != ConcurrentUse::None) {
     maybeLock.emplace(rt);
   }
 
-  ArenaList& al = arenaList(thingKind);
-  Arena* arena = al.takeNextArena();
+  Arena* arena = arenaList(thingKind).takeNextArena();
   if (arena) {
     // Empty arenas should be immediately freed.
     MOZ_ASSERT(!arena->isEmpty());
 
     return freeLists.setArenaAndAllocate(arena, thingKind);
   }
 
   // Parallel threads have their own ArenaLists, but chunks are shared;
@@ -578,20 +577,27 @@ TenuredCell* ArenaLists::refillFreeListA
   // Although our chunk should definitely have enough space for another arena,
   // there are other valid reasons why Chunk::allocateArena() may fail.
   arena = rt->gc.allocateArena(chunk, zone_, thingKind, checkThresholds,
                                maybeLock.ref());
   if (!arena) {
     return nullptr;
   }
 
+  addNewArena(arena, thingKind);
+
+  return freeLists.setArenaAndAllocate(arena, thingKind);
+}
+
+inline void ArenaLists::addNewArena(Arena* arena, AllocKind thingKind) {
+  ArenaList& al = zone_->isGCMarking() ? newArenasInMarkPhase(thingKind)
+                                       : arenaList(thingKind);
+
   MOZ_ASSERT(al.isCursorAtEnd());
   al.insertBeforeCursor(arena);
-
-  return freeLists.setArenaAndAllocate(arena, thingKind);
 }
 
 inline TenuredCell* FreeLists::setArenaAndAllocate(Arena* arena,
                                                    AllocKind kind) {
 #ifdef DEBUG
   auto old = freeLists_[kind];
   if (!old->isEmpty()) {
     old->getArena()->checkNoMarkedFreeCells();
--- a/js/src/gc/ArenaList-inl.h
+++ b/js/src/gc/ArenaList-inl.h
@@ -251,16 +251,21 @@ js::gc::Arena* js::gc::ArenaLists::getFi
 js::gc::Arena* js::gc::ArenaLists::getFirstSweptArena(
     AllocKind thingKind) const {
   if (thingKind != incrementalSweptArenaKind.ref()) {
     return nullptr;
   }
   return incrementalSweptArenas.ref().head();
 }
 
+js::gc::Arena* js::gc::ArenaLists::getFirstNewArenaInMarkPhase(
+    AllocKind thingKind) const {
+  return newArenasInMarkPhase(thingKind).head();
+}
+
 js::gc::Arena* js::gc::ArenaLists::getArenaAfterCursor(
     AllocKind thingKind) const {
   return arenaList(thingKind).arenaAfterCursor();
 }
 
 bool js::gc::ArenaLists::arenaListsAreEmpty() const {
   for (auto i : AllAllocKinds()) {
     /*
@@ -303,16 +308,23 @@ MOZ_ALWAYS_INLINE js::gc::TenuredCell* j
 }
 
 void js::gc::ArenaLists::unmarkPreMarkedFreeCells() {
   for (auto i : AllAllocKinds()) {
     freeLists().unmarkPreMarkedFreeCells(i);
   }
 }
 
+void js::gc::ArenaLists::mergeNewArenasInMarkPhase() {
+  for (auto i : AllAllocKinds()) {
+    arenaList(i).insertListWithCursorAtEnd(newArenasInMarkPhase(i));
+    newArenasInMarkPhase(i).clear();
+  }
+}
+
 void js::gc::ArenaLists::checkEmptyFreeLists() {
   MOZ_ASSERT(freeLists().allEmpty());
 }
 
 void js::gc::ArenaLists::checkEmptyArenaLists() {
 #ifdef DEBUG
   for (auto i : AllAllocKinds()) {
     checkEmptyArenaList(i);
--- a/js/src/gc/ArenaList.h
+++ b/js/src/gc/ArenaList.h
@@ -255,31 +255,37 @@ class ArenaLists {
 
   JS::Zone* zone_;
 
   // Whether this structure can be accessed by other threads.
   UnprotectedData<AllAllocKindArray<ConcurrentUseState>> concurrentUseState_;
 
   ZoneData<FreeLists> freeLists_;
 
+  /* The main list of arenas for each alloc kind. */
   ArenaListData<AllAllocKindArray<ArenaList>> arenaLists_;
 
+  /* For each arena kind, a list of arenas allocated during marking. */
+  ArenaListData<AllAllocKindArray<ArenaList>> newArenasInMarkPhase_;
+
   /* For each arena kind, a list of arenas remaining to be swept. */
   MainThreadOrGCTaskData<AllAllocKindArray<Arena*>> arenasToSweep_;
 
   /* During incremental sweeping, a list of the arenas already swept. */
   ZoneOrGCTaskData<AllocKind> incrementalSweptArenaKind;
   ZoneOrGCTaskData<ArenaList> incrementalSweptArenas;
 
   // Arena lists which have yet to be swept, but need additional foreground
   // processing before they are swept.
   ZoneData<Arena*> gcShapeArenasToUpdate;
   ZoneData<Arena*> gcAccessorShapeArenasToUpdate;
   ZoneData<Arena*> gcScriptArenasToUpdate;
+  ZoneData<Arena*> gcNewScriptArenasToUpdate;
   ZoneData<Arena*> gcObjectGroupArenasToUpdate;
+  ZoneData<Arena*> gcNewObjectGroupArenasToUpdate;
 
   // The list of empty arenas which are collected during the sweep phase and
   // released at the end of sweeping every sweep group.
   ZoneData<Arena*> savedEmptyArenas;
 
  public:
   explicit ArenaLists(JS::Zone* zone);
   ~ArenaLists();
@@ -289,16 +295,17 @@ class ArenaLists {
 
   FreeSpan** addressOfFreeList(AllocKind thingKind) {
     return freeLists_.refNoCheck().addressOfFreeList(thingKind);
   }
 
   inline Arena* getFirstArena(AllocKind thingKind) const;
   inline Arena* getFirstArenaToSweep(AllocKind thingKind) const;
   inline Arena* getFirstSweptArena(AllocKind thingKind) const;
+  inline Arena* getFirstNewArenaInMarkPhase(AllocKind thingKind) const;
   inline Arena* getArenaAfterCursor(AllocKind thingKind) const;
 
   inline bool arenaListsAreEmpty() const;
 
   inline void unmarkAll();
 
   inline bool doneBackgroundFinalize(AllocKind kind) const;
   inline bool needBackgroundFinalizeWait(AllocKind kind) const;
@@ -329,24 +336,34 @@ class ArenaLists {
 
   bool foregroundFinalize(JSFreeOp* fop, AllocKind thingKind,
                           js::SliceBudget& sliceBudget,
                           SortedArenaList& sweepList);
   static void backgroundFinalize(JSFreeOp* fop, Arena* listHead, Arena** empty);
 
   void setParallelAllocEnabled(bool enabled);
 
+  inline void mergeNewArenasInMarkPhase();
+
+  void checkGCStateNotInUse();
   void checkSweepStateNotInUse();
   void checkNoArenasToUpdate();
   void checkNoArenasToUpdateForKind(AllocKind kind);
 
  private:
   ArenaList& arenaList(AllocKind i) { return arenaLists_.ref()[i]; }
   const ArenaList& arenaList(AllocKind i) const { return arenaLists_.ref()[i]; }
 
+  ArenaList& newArenasInMarkPhase(AllocKind i) {
+    return newArenasInMarkPhase_.ref()[i];
+  }
+  const ArenaList& newArenasInMarkPhase(AllocKind i) const {
+    return newArenasInMarkPhase_.ref()[i];
+  }
+
   ConcurrentUseState& concurrentUse(AllocKind i) {
     return concurrentUseState_.ref()[i];
   }
   ConcurrentUse concurrentUse(AllocKind i) const {
     return concurrentUseState_.ref()[i];
   }
 
   Arena*& arenasToSweep(AllocKind i) { return arenasToSweep_.ref()[i]; }
@@ -361,16 +378,18 @@ class ArenaLists {
                                       const FinalizePhase& phase);
   inline void queueForForegroundSweep(AllocKind thingKind);
   inline void queueForBackgroundSweep(AllocKind thingKind);
 
   TenuredCell* refillFreeListAndAllocate(FreeLists& freeLists,
                                          AllocKind thingKind,
                                          ShouldCheckThresholds checkThresholds);
 
+  void addNewArena(Arena* arena, AllocKind thingKind);
+
   friend class GCRuntime;
   friend class js::Nursery;
   friend class js::TenuringTracer;
 };
 
 } /* namespace gc */
 } /* namespace js */
 
--- a/js/src/gc/GC-inl.h
+++ b/js/src/gc/GC-inl.h
@@ -18,67 +18,66 @@
 #include "gc/ArenaList-inl.h"
 
 namespace js {
 namespace gc {
 
 class AutoAssertEmptyNursery;
 
 class ArenaIter {
-  Arena* arena;
-  Arena* unsweptArena;
-  Arena* sweptArena;
-  mozilla::DebugOnly<bool> initialized;
+  static constexpr size_t SourceCount = 4;
+
+  Arena* arena = nullptr;
+  Arena* sources[SourceCount] = {nullptr};
+  size_t index = 0;
+  mozilla::DebugOnly<bool> initialized = false;
 
  public:
-  ArenaIter()
-      : arena(nullptr),
-        unsweptArena(nullptr),
-        sweptArena(nullptr),
-        initialized(false) {}
+  ArenaIter() = default;
 
-  ArenaIter(JS::Zone* zone, AllocKind kind) : initialized(false) {
-    init(zone, kind);
-  }
+  ArenaIter(JS::Zone* zone, AllocKind kind) { init(zone, kind); }
 
   void init(JS::Zone* zone, AllocKind kind) {
     MOZ_ASSERT(!initialized);
     MOZ_ASSERT(zone);
+    sources[0] = zone->arenas.getFirstArena(kind);
+    sources[1] = zone->arenas.getFirstArenaToSweep(kind);
+    sources[2] = zone->arenas.getFirstSweptArena(kind);
+    sources[3] = zone->arenas.getFirstNewArenaInMarkPhase(kind);
     initialized = true;
-    arena = zone->arenas.getFirstArena(kind);
-    unsweptArena = zone->arenas.getFirstArenaToSweep(kind);
-    sweptArena = zone->arenas.getFirstSweptArena(kind);
-    if (!unsweptArena) {
-      unsweptArena = sweptArena;
-      sweptArena = nullptr;
-    }
-    if (!arena) {
-      arena = unsweptArena;
-      unsweptArena = sweptArena;
-      sweptArena = nullptr;
-    }
+    settle();
   }
 
   bool done() const {
     MOZ_ASSERT(initialized);
-    return !arena;
+    return index == SourceCount;
   }
 
   Arena* get() const {
     MOZ_ASSERT(!done());
     return arena;
   }
 
   void next() {
     MOZ_ASSERT(!done());
     arena = arena->next;
     if (!arena) {
-      arena = unsweptArena;
-      unsweptArena = sweptArena;
-      sweptArena = nullptr;
+      index++;
+      settle();
+    }
+  }
+
+ private:
+  void settle() {
+    while (index < SourceCount) {
+      arena = sources[index];
+      if (arena) {
+        break;
+      }
+      index++;
     }
   }
 };
 
 class ArenaCellIter {
   size_t firstThingOffset;
   size_t thingSize;
   Arena* arenaAddr;
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -2673,23 +2673,26 @@ FreeLists::FreeLists() {
     freeLists_[i] = &emptySentinel;
   }
 }
 
 ArenaLists::ArenaLists(Zone* zone)
     : zone_(zone),
       freeLists_(zone),
       arenaLists_(zone),
+      newArenasInMarkPhase_(zone),
       arenasToSweep_(),
       incrementalSweptArenaKind(zone, AllocKind::LIMIT),
       incrementalSweptArenas(zone),
       gcShapeArenasToUpdate(zone, nullptr),
       gcAccessorShapeArenasToUpdate(zone, nullptr),
       gcScriptArenasToUpdate(zone, nullptr),
+      gcNewScriptArenasToUpdate(zone, nullptr),
       gcObjectGroupArenasToUpdate(zone, nullptr),
+      gcNewObjectGroupArenasToUpdate(zone, nullptr),
       savedEmptyArenas(zone, nullptr) {
   for (auto i : AllAllocKinds()) {
     concurrentUse(i) = ConcurrentUse::None;
     arenasToSweep(i) = nullptr;
   }
 }
 
 void ReleaseArenaList(JSRuntime* rt, Arena* arena, const AutoLockGC& lock) {
@@ -2738,28 +2741,28 @@ void ArenaLists::queueForBackgroundSweep
   gcstats::AutoPhase ap(fop->runtime()->gc.stats(), phase.statsPhase);
   for (auto kind : phase.kinds) {
     queueForBackgroundSweep(kind);
   }
 }
 
 inline void ArenaLists::queueForBackgroundSweep(AllocKind thingKind) {
   MOZ_ASSERT(IsBackgroundFinalized(thingKind));
+  MOZ_ASSERT(concurrentUse(thingKind) == ConcurrentUse::None);
 
   ArenaList* al = &arenaList(thingKind);
-  if (al->isEmpty()) {
-    MOZ_ASSERT(concurrentUse(thingKind) == ConcurrentUse::None);
-    return;
-  }
-
-  MOZ_ASSERT(concurrentUse(thingKind) == ConcurrentUse::None);
-
   arenasToSweep(thingKind) = al->head();
-  al->clear();
-  concurrentUse(thingKind) = ConcurrentUse::BackgroundFinalize;
+  arenaList(thingKind).clear();
+
+  if (arenasToSweep(thingKind)) {
+    concurrentUse(thingKind) = ConcurrentUse::BackgroundFinalize;
+  } else {
+    arenaList(thingKind) = newArenasInMarkPhase(thingKind);
+    newArenasInMarkPhase(thingKind).clear();
+  }
 }
 
 /*static*/
 void ArenaLists::backgroundFinalize(JSFreeOp* fop, Arena* listHead,
                                     Arena** empty) {
   MOZ_ASSERT(listHead);
   MOZ_ASSERT(empty);
 
@@ -2775,88 +2778,108 @@ void ArenaLists::backgroundFinalize(JSFr
 
   finalizedSorted.extractEmpty(empty);
 
   // When arenas are queued for background finalization, all arenas are moved to
   // arenasToSweep, leaving the arena list empty. However, new arenas may be
   // allocated before background finalization finishes; now that finalization is
   // complete, we want to merge these lists back together.
   ArenaLists* lists = &zone->arenas;
-  ArenaList* al = &lists->arenaList(thingKind);
+  ArenaList& al = lists->arenaList(thingKind);
 
   // Flatten |finalizedSorted| into a regular ArenaList.
   ArenaList finalized = finalizedSorted.toArenaList();
 
   // We must take the GC lock to be able to safely modify the ArenaList;
   // however, this does not by itself make the changes visible to all threads,
   // as not all threads take the GC lock to read the ArenaLists.
   // That safety is provided by the ReleaseAcquire memory ordering of the
   // background finalize state, which we explicitly set as the final step.
   {
     AutoLockGC lock(lists->runtimeFromAnyThread());
     MOZ_ASSERT(lists->concurrentUse(thingKind) ==
                ConcurrentUse::BackgroundFinalize);
 
     // Join |al| and |finalized| into a single list.
-    *al = finalized.insertListWithCursorAtEnd(*al);
-
+    ArenaList allocatedDuringSweep = al;
+    al = finalized;
+    al.insertListWithCursorAtEnd(lists->newArenasInMarkPhase(thingKind));
+    al.insertListWithCursorAtEnd(allocatedDuringSweep);
+
+    lists->newArenasInMarkPhase(thingKind).clear();
     lists->arenasToSweep(thingKind) = nullptr;
   }
 
   lists->concurrentUse(thingKind) = ConcurrentUse::None;
 }
 
 void ArenaLists::releaseForegroundSweptEmptyArenas() {
   AutoLockGC lock(runtime());
   ReleaseArenaList(runtime(), savedEmptyArenas, lock);
   savedEmptyArenas = nullptr;
 }
 
 void ArenaLists::queueForegroundThingsForSweep() {
   gcShapeArenasToUpdate = arenasToSweep(AllocKind::SHAPE);
   gcAccessorShapeArenasToUpdate = arenasToSweep(AllocKind::ACCESSOR_SHAPE);
   gcObjectGroupArenasToUpdate = arenasToSweep(AllocKind::OBJECT_GROUP);
+  gcNewObjectGroupArenasToUpdate =
+      newArenasInMarkPhase(AllocKind::OBJECT_GROUP).head();
   gcScriptArenasToUpdate = arenasToSweep(AllocKind::SCRIPT);
+  gcNewScriptArenasToUpdate = newArenasInMarkPhase(AllocKind::SCRIPT).head();
+}
+
+void ArenaLists::checkGCStateNotInUse() {
+  // Called before and after collection to check the state is as expected.
+#ifdef DEBUG
+  checkSweepStateNotInUse();
+  for (auto i : AllAllocKinds()) {
+    MOZ_ASSERT(newArenasInMarkPhase(i).isEmpty());
+  }
+#endif
 }
 
 void ArenaLists::checkSweepStateNotInUse() {
-  // Called before and after sweeping to check the sweep state is as expected.
 #ifdef DEBUG
   checkNoArenasToUpdate();
   MOZ_ASSERT(incrementalSweptArenaKind == AllocKind::LIMIT);
   MOZ_ASSERT(incrementalSweptArenas.ref().isEmpty());
   MOZ_ASSERT(!savedEmptyArenas);
   for (auto i : AllAllocKinds()) {
     MOZ_ASSERT(concurrentUse(i) == ConcurrentUse::None);
     MOZ_ASSERT(!arenasToSweep(i));
   }
 #endif
 }
 
 void ArenaLists::checkNoArenasToUpdate() {
   MOZ_ASSERT(!gcShapeArenasToUpdate);
   MOZ_ASSERT(!gcAccessorShapeArenasToUpdate);
   MOZ_ASSERT(!gcScriptArenasToUpdate);
+  MOZ_ASSERT(!gcNewScriptArenasToUpdate);
   MOZ_ASSERT(!gcObjectGroupArenasToUpdate);
+  MOZ_ASSERT(!gcNewObjectGroupArenasToUpdate);
 }
 
 void ArenaLists::checkNoArenasToUpdateForKind(AllocKind kind) {
 #ifdef DEBUG
   switch (kind) {
     case AllocKind::SHAPE:
       MOZ_ASSERT(!gcShapeArenasToUpdate);
       break;
     case AllocKind::ACCESSOR_SHAPE:
       MOZ_ASSERT(!gcShapeArenasToUpdate);
       break;
     case AllocKind::SCRIPT:
       MOZ_ASSERT(!gcScriptArenasToUpdate);
+      MOZ_ASSERT(!gcNewScriptArenasToUpdate);
       break;
     case AllocKind::OBJECT_GROUP:
       MOZ_ASSERT(!gcObjectGroupArenasToUpdate);
+      MOZ_ASSERT(!gcNewObjectGroupArenasToUpdate);
       break;
     default:
       break;
   }
 #endif
 }
 
 TimeStamp SliceBudget::unlimitedDeadline;
@@ -4105,16 +4128,17 @@ bool GCRuntime::beginMarkPhase(JS::GCRea
 
   /*
    * In an incremental GC, clear the area free lists to ensure that subsequent
    * allocations refill them and end up marking new cells back. See
    * arenaAllocatedDuringGC().
    */
   for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     zone->arenas.clearFreeLists();
+    zone->arenas.checkGCStateNotInUse();
   }
 
   marker.start();
   GCMarker* gcmarker = &marker;
   gcmarker->clearMarkCount();
 
   {
     gcstats::AutoPhase ap1(stats(), gcstats::PhaseKind::PREPARE);
@@ -4581,16 +4605,17 @@ void GCRuntime::getNextSweepGroup() {
     joinTask(sweepMarkTask, gcstats::PhaseKind::SWEEP_MARK);
 
     // Abort collection of subsequent sweep groups.
     for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
       MOZ_ASSERT(!zone->gcNextGraphComponent);
       zone->setNeedsIncrementalBarrier(false);
       zone->changeGCState(Zone::MarkBlackOnly, Zone::NoGC);
       zone->arenas.unmarkPreMarkedFreeCells();
+      zone->arenas.mergeNewArenasInMarkPhase();
       zone->gcGrayRoots().Clear();
       zone->clearGCSliceThresholds();
     }
 
     for (SweepGroupCompartmentsIter comp(rt); !comp.done(); comp.next()) {
       ResetGrayList(comp);
     }
 
@@ -5511,20 +5536,16 @@ void GCRuntime::beginSweepPhase(JS::GCRe
   sweepActions->assertFinished();
 }
 
 bool ArenaLists::foregroundFinalize(JSFreeOp* fop, AllocKind thingKind,
                                     SliceBudget& sliceBudget,
                                     SortedArenaList& sweepList) {
   checkNoArenasToUpdateForKind(thingKind);
 
-  if (!arenasToSweep(thingKind) && incrementalSweptArenas.ref().isEmpty()) {
-    return true;
-  }
-
   // Arenas are released for use for new allocations as soon as the finalizers
   // for that allocation kind have run. This means that a cell's finalizer can
   // safely use IsAboutToBeFinalized to check other cells of the same alloc
   // kind, but not of different alloc kinds: the other arena may have already
   // had new objects allocated in it, and since we allocate black,
   // IsAboutToBeFinalized will return false even though the referent we intended
   // to check is long gone.
   if (!FinalizeArenas(fop, &arenasToSweep(thingKind), sweepList, thingKind,
@@ -5535,19 +5556,23 @@ bool ArenaLists::foregroundFinalize(JSFr
   }
 
   // Clear any previous incremental sweep state we may have saved.
   incrementalSweptArenaKind = AllocKind::LIMIT;
   incrementalSweptArenas.ref().clear();
 
   sweepList.extractEmpty(&savedEmptyArenas.ref());
 
-  ArenaList finalized = sweepList.toArenaList();
-  arenaList(thingKind) =
-      finalized.insertListWithCursorAtEnd(arenaList(thingKind));
+  ArenaList& al = arenaList(thingKind);
+  ArenaList allocatedDuringSweep = al;
+  al = sweepList.toArenaList();
+  al.insertListWithCursorAtEnd(newArenasInMarkPhase(thingKind));
+  al.insertListWithCursorAtEnd(allocatedDuringSweep);
+
+  newArenasInMarkPhase(thingKind).clear();
 
   return true;
 }
 
 void js::gc::SweepMarkTask::run() {
   // Time reporting is handled separately for parallel tasks.
   gc->sweepMarkResult =
       gc->markUntilBudgetExhausted(this->budget, GCMarker::DontReportMarkTime);
@@ -5637,21 +5662,31 @@ IncrementalProgress GCRuntime::sweepType
 
   AutoClearTypeInferenceStateOnOOM oom(sweepZone);
 
   if (!SweepArenaList<BaseScript>(fop, &al.gcScriptArenasToUpdate.ref(),
                                   budget)) {
     return NotFinished;
   }
 
+  if (!SweepArenaList<BaseScript>(fop, &al.gcNewScriptArenasToUpdate.ref(),
+                                  budget)) {
+    return NotFinished;
+  }
+
   if (!SweepArenaList<ObjectGroup>(fop, &al.gcObjectGroupArenasToUpdate.ref(),
                                    budget)) {
     return NotFinished;
   }
 
+  if (!SweepArenaList<ObjectGroup>(
+          fop, &al.gcNewObjectGroupArenasToUpdate.ref(), budget)) {
+    return NotFinished;
+  }
+
   // Finish sweeping type information in the zone.
   {
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_TYPES_END);
     sweepZone->types.endSweep(rt);
   }
 
   return Finished;
 }
@@ -6339,17 +6374,17 @@ void GCRuntime::finishCollection() {
 
   {
     AutoLockGC lock(this);
     for (GCZonesIter zone(this); !zone.done(); zone.next()) {
       zone->changeGCState(Zone::Finished, Zone::NoGC);
       zone->clearGCSliceThresholds();
       zone->notifyObservingDebuggers();
       zone->updateGCStartThresholds(*this, invocationKind, lock);
-      zone->arenas.checkSweepStateNotInUse();
+      zone->arenas.checkGCStateNotInUse();
     }
   }
 
   for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     MOZ_ASSERT(!zone->wasGCStarted());
     MOZ_ASSERT(!zone->needsIncrementalBarrier());
     MOZ_ASSERT(!zone->isOnList());
   }
@@ -6432,16 +6467,17 @@ GCRuntime::IncrementalResult GCRuntime::
         ResetGrayList(c);
       }
 
       for (GCZonesIter zone(this); !zone.done(); zone.next()) {
         zone->setNeedsIncrementalBarrier(false);
         zone->changeGCState(Zone::MarkBlackOnly, Zone::NoGC);
         zone->clearGCSliceThresholds();
         zone->arenas.unmarkPreMarkedFreeCells();
+        zone->arenas.mergeNewArenasInMarkPhase();
       }
 
       {
         AutoLockHelperThreadState lock;
         lifoBlocksToFree.ref().freeAll();
       }
 
       lastMarkSlice = false;