Bug 1490042 - Only allow one AutoClearTypeInferenceStateOnOOM to be live at a time to fix recursive type sweeping r=jandem
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 20 Sep 2018 10:54:18 +0100
changeset 437423 274c743b3d55f3d84adb4500c3ae3f63ee8fdc5e
parent 437422 f41f9765f63ceb72c0b95da02e3e581dbf730896
child 437424 454f0d7c80a1a7214687ab669b69b719f15197ab
push id34682
push userebalazs@mozilla.com
push dateThu, 20 Sep 2018 13:24:34 +0000
treeherdermozilla-central@8265be8d02b0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1490042
milestone64.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 1490042 - Only allow one AutoClearTypeInferenceStateOnOOM to be live at a time to fix recursive type sweeping r=jandem
js/src/builtin/TestingFunctions.cpp
js/src/gc/GC.cpp
js/src/gc/GCRuntime.h
js/src/jit-test/tests/gc/bug-1490042.js
js/src/jsapi.cpp
js/src/jsapi.h
js/src/vm/JSScript.h
js/src/vm/ObjectGroup.h
js/src/vm/TypeInference-inl.h
js/src/vm/TypeInference.cpp
js/src/vm/TypeInference.h
js/src/vm/TypeSet.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -971,44 +971,77 @@ GCPreserveCode(JSContext* cx, unsigned a
 
     args.rval().setUndefined();
     return true;
 }
 
 #ifdef JS_GC_ZEAL
 
 static bool
+ParseGCZealMode(JSContext* cx, const CallArgs& args, uint8_t* zeal)
+{
+    uint32_t value;
+    if (!ToUint32(cx, args.get(0), &value)) {
+        return false;
+    }
+
+    if (value > uint32_t(gc::ZealMode::Limit)) {
+        JS_ReportErrorASCII(cx, "gczeal argument out of range");
+        return false;
+    }
+
+    *zeal = static_cast<uint8_t>(value);
+    return true;
+}
+
+static bool
 GCZeal(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() > 2) {
         RootedObject callee(cx, &args.callee());
         ReportUsageErrorASCII(cx, callee, "Too many arguments");
         return false;
     }
 
-    uint32_t zeal;
-    if (!ToUint32(cx, args.get(0), &zeal)) {
-        return false;
-    }
-
-    if (zeal > uint32_t(gc::ZealMode::Limit)) {
-        JS_ReportErrorASCII(cx, "gczeal argument out of range");
+    uint8_t zeal;
+    if (!ParseGCZealMode(cx, args, &zeal)) {
         return false;
     }
 
     uint32_t frequency = JS_DEFAULT_ZEAL_FREQ;
     if (args.length() >= 2) {
         if (!ToUint32(cx, args.get(1), &frequency)) {
             return false;
         }
     }
 
-    JS_SetGCZeal(cx, (uint8_t)zeal, frequency);
+    JS_SetGCZeal(cx, zeal, frequency);
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+UnsetGCZeal(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() > 1) {
+        RootedObject callee(cx, &args.callee());
+        ReportUsageErrorASCII(cx, callee, "Too many arguments");
+        return false;
+    }
+
+    uint8_t zeal;
+    if (!ParseGCZealMode(cx, args, &zeal)) {
+        return false;
+    }
+
+    JS_UnsetGCZeal(cx, zeal);
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 ScheduleGC(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -5675,19 +5708,24 @@ JS_FN_HELP("streamsAreEnabled", StreamsA
 "  Reset the value returned by finalizeCount()."),
 
     JS_FN_HELP("gcPreserveCode", GCPreserveCode, 0, 0,
 "gcPreserveCode()",
 "  Preserve JIT code during garbage collections."),
 
 #ifdef JS_GC_ZEAL
     JS_FN_HELP("gczeal", GCZeal, 2, 0,
-"gczeal(level, [N])",
+"gczeal(mode, [frequency])",
 gc::ZealModeHelpText),
 
+    JS_FN_HELP("unsetgczeal", UnsetGCZeal, 2, 0,
+"unsetgczeal(mode)",
+"  Turn off a single zeal mode set with gczeal() and don't finish any ongoing\n"
+"  collection that may be happening."),
+
     JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
 "schedulegc([num | obj | string])",
 "  If num is given, schedule a GC after num allocations.\n"
 "  If obj is given, schedule a GC of obj's zone.\n"
 "  If string is given, schedule a GC of the string's zone if possible.\n"
 "  Returns the number of allocations before the next trigger."),
 
     JS_FN_HELP("selectforgc", SelectForGC, 0, 0,
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -1145,16 +1145,51 @@ GCRuntime::setZeal(uint8_t zeal, uint32_
     } else {
         zealModeBits = 0;
     }
     zealFrequency = frequency;
     nextScheduled = schedule ? frequency : 0;
 }
 
 void
+GCRuntime::unsetZeal(uint8_t zeal)
+{
+    MOZ_ASSERT(zeal <= unsigned(ZealMode::Limit));
+    ZealMode zealMode = ZealMode(zeal);
+
+    if (temporaryAbortIfWasmGc(rt->mainContextFromOwnThread())) {
+        return;
+    }
+
+    if (!hasZealMode(zealMode)) {
+        return;
+    }
+
+    if (verifyPreData) {
+        VerifyBarriers(rt, PreBarrierVerifier);
+    }
+
+    if (zealMode == ZealMode::GenerationalGC) {
+        evictNursery(JS::gcreason::DEBUG_GC);
+        nursery().leaveZealMode();
+    }
+
+    clearZealMode(zealMode);
+
+    if (zealModeBits == 0) {
+        if (isIncrementalGCInProgress()) {
+            finishGC(JS::gcreason::DEBUG_GC);
+        }
+
+        zealFrequency = 0;
+        nextScheduled = 0;
+    }
+}
+
+void
 GCRuntime::setNextScheduled(uint32_t count)
 {
     nextScheduled = count;
 }
 
 using CharRange = mozilla::Range<const char>;
 using CharRangeVector = Vector<CharRange, 0, SystemAllocPolicy>;
 
@@ -2611,20 +2646,20 @@ Zone::prepareForCompacting()
 void
 GCRuntime::sweepTypesAfterCompacting(Zone* zone)
 {
     zone->beginSweepTypes(releaseObservedTypes && !zone->isPreservingCode());
 
     AutoClearTypeInferenceStateOnOOM oom(zone);
 
     for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
-        AutoSweepTypeScript sweep(script, &oom);
+        AutoSweepTypeScript sweep(script);
     }
     for (auto group = zone->cellIter<ObjectGroup>(); !group.done(); group.next()) {
-        AutoSweepObjectGroup sweep(group, &oom);
+        AutoSweepObjectGroup sweep(group);
     }
 
     zone->types.endSweep(rt);
 }
 
 void
 GCRuntime::sweepZoneAfterCompacting(Zone* zone)
 {
@@ -6234,34 +6269,34 @@ static void
 SweepThing(Shape* shape)
 {
     if (!shape->isMarkedAny()) {
         shape->sweep();
     }
 }
 
 static void
-SweepThing(JSScript* script, AutoClearTypeInferenceStateOnOOM* oom)
-{
-    AutoSweepTypeScript sweep(script, oom);
+SweepThing(JSScript* script)
+{
+    AutoSweepTypeScript sweep(script);
 }
 
 static void
-SweepThing(ObjectGroup* group, AutoClearTypeInferenceStateOnOOM* oom)
-{
-    AutoSweepObjectGroup sweep(group, oom);
-}
-
-template <typename T, typename... Args>
+SweepThing(ObjectGroup* group)
+{
+    AutoSweepObjectGroup sweep(group);
+}
+
+template <typename T>
 static bool
-SweepArenaList(Arena** arenasToSweep, SliceBudget& sliceBudget, Args... args)
+SweepArenaList(Arena** arenasToSweep, SliceBudget& sliceBudget)
 {
     while (Arena* arena = *arenasToSweep) {
         for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
-            SweepThing(i.get<T>(), args...);
+            SweepThing(i.get<T>());
         }
 
         *arenasToSweep = (*arenasToSweep)->next;
         AllocKind kind = MapTypeToFinalizeKind<T>::kind;
         sliceBudget.step(Arena::thingsPerArena(kind));
         if (sliceBudget.isOverBudget()) {
             return false;
         }
@@ -6283,21 +6318,21 @@ GCRuntime::sweepTypeInformation(FreeOp* 
 
     gcstats::AutoPhase ap1(stats(), gcstats::PhaseKind::SWEEP_COMPARTMENTS);
     gcstats::AutoPhase ap2(stats(), gcstats::PhaseKind::SWEEP_TYPES);
 
     ArenaLists& al = zone->arenas;
 
     AutoClearTypeInferenceStateOnOOM oom(zone);
 
-    if (!SweepArenaList<JSScript>(&al.gcScriptArenasToUpdate.ref(), budget, &oom)) {
+    if (!SweepArenaList<JSScript>(&al.gcScriptArenasToUpdate.ref(), budget)) {
         return NotFinished;
     }
 
-    if (!SweepArenaList<ObjectGroup>(&al.gcObjectGroupArenasToUpdate.ref(), budget, &oom)) {
+    if (!SweepArenaList<ObjectGroup>(&al.gcObjectGroupArenasToUpdate.ref(), budget)) {
         return NotFinished;
     }
 
     // Finish sweeping type information in the zone.
     {
         gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_TYPES_END);
         zone->types.endSweep(rt);
     }
@@ -8064,18 +8099,17 @@ GCRuntime::collect(bool nonincrementalBy
     // Checks run for each request, even if we do not actually GC.
     checkCanCallAPI();
 
     // Check if we are allowed to GC at this time before proceeding.
     if (!checkIfGCAllowedInCurrentState(reason)) {
         return;
     }
 
-    stats().writeLogMessage("GC starting in state %s",
-        StateName(incrementalState));
+    stats().writeLogMessage("GC starting in state %s", StateName(incrementalState));
 
     AutoTraceLog logGC(TraceLoggerForCurrentThread(), TraceLogger_GC);
     AutoStopVerifyingBarriers av(rt, IsShutdownGC(reason));
     AutoEnqueuePendingParseTasksAfterGC aept(*this);
     AutoScheduleZonesForGC asz(this);
 
     bool repeat;
     do {
@@ -8122,17 +8156,17 @@ GCRuntime::collect(bool nonincrementalBy
     if (hasZealMode(ZealMode::CheckHeapAfterGC)) {
         gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::TRACE_HEAP);
         CheckHeapAfterGC(rt);
     }
     if (hasZealMode(ZealMode::CheckGrayMarking) && !isIncrementalGCInProgress()) {
         MOZ_RELEASE_ASSERT(CheckGrayMarkingState(rt));
     }
 #endif
-    stats().writeLogMessage("GC ending");
+    stats().writeLogMessage("GC ending in state %s", StateName(incrementalState));
 }
 
 js::AutoEnqueuePendingParseTasksAfterGC::~AutoEnqueuePendingParseTasksAfterGC()
 {
     if (!OffThreadParsingMustWaitForGC(gc_.rt)) {
         EnqueuePendingParseTasksAfterGC(gc_.rt);
     }
 }
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -294,16 +294,17 @@ class GCRuntime
     void onOutOfMallocMemory(const AutoLockGC& lock);
 
 #ifdef JS_GC_ZEAL
     const uint32_t* addressOfZealModeBits() {
         return &zealModeBits.refNoCheck();
     }
     void getZealBits(uint32_t* zealBits, uint32_t* frequency, uint32_t* nextScheduled);
     void setZeal(uint8_t zeal, uint32_t frequency);
+    void unsetZeal(uint8_t zeal);
     bool parseAndSetZeal(const char* str);
     void setNextScheduled(uint32_t count);
     void verifyPreBarriers();
     void maybeVerifyPreBarriers(bool always);
     bool selectForMarking(JSObject* object);
     void clearSelectedForMarking();
     void setDeterministic(bool enable);
 #endif
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1490042.js
@@ -0,0 +1,34 @@
+// |jit-test| --no-ion; --no-baseline
+
+if (!('gcstate' in this))
+    quit();
+
+gczeal(0);
+
+// Create a bunch of ObjectGroups with TypeNewScript attached.
+const count = 1000;
+let c = [];
+let a = [];
+for (let i = 0; i < count; i++) {
+    c[i] = function() { this.a = 1; this.b = 0; this.c = 2; };
+    a[i] = new c[i];
+}
+
+// Start an incremental GC and run until we're about to sweep type information.
+assertEq(gcstate(), "NotActive");
+gczeal(20);
+startgc(1);
+
+// Run incremental slices with simulated OOM set up to provoke OOM when sweeping
+// types.
+assertEq(gcstate(), "Sweep");
+gczeal(10);
+unsetgczeal(20);
+while (gcstate() == "Sweep") {
+    oomAfterAllocations(2);
+    gcslice(1);
+    resetOOMFailure();
+}
+
+// Ensure our type information stays alive.
+let x = c.length + a.length;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6406,16 +6406,22 @@ JS_GetGCZealBits(JSContext* cx, uint32_t
 
 JS_PUBLIC_API(void)
 JS_SetGCZeal(JSContext* cx, uint8_t zeal, uint32_t frequency)
 {
     cx->runtime()->gc.setZeal(zeal, frequency);
 }
 
 JS_PUBLIC_API(void)
+JS_UnsetGCZeal(JSContext* cx, uint8_t zeal)
+{
+    cx->runtime()->gc.unsetZeal(zeal);
+}
+
+JS_PUBLIC_API(void)
 JS_ScheduleGC(JSContext* cx, uint32_t count)
 {
     cx->runtime()->gc.setNextScheduled(count);
 }
 #endif
 
 JS_PUBLIC_API(void)
 JS_SetParallelParsingEnabled(JSContext* cx, bool enabled)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4379,16 +4379,19 @@ JS_NewObjectForConstructor(JSContext* cx
 
 extern JS_PUBLIC_API(void)
 JS_GetGCZealBits(JSContext* cx, uint32_t* zealBits, uint32_t* frequency, uint32_t* nextScheduled);
 
 extern JS_PUBLIC_API(void)
 JS_SetGCZeal(JSContext* cx, uint8_t zeal, uint32_t frequency);
 
 extern JS_PUBLIC_API(void)
+JS_UnsetGCZeal(JSContext* cx, uint8_t zeal);
+
+extern JS_PUBLIC_API(void)
 JS_ScheduleGC(JSContext* cx, uint32_t count);
 #endif
 
 extern JS_PUBLIC_API(void)
 JS_SetParallelParsingEnabled(JSContext* cx, bool enabled);
 
 extern JS_PUBLIC_API(void)
 JS_SetOffthreadIonCompilationEnabled(JSContext* cx, bool enabled);
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -1793,18 +1793,17 @@ class JSScript : public js::gc::TenuredC
 
     /* Ensure the script has a TypeScript. */
     inline bool ensureHasTypes(JSContext* cx, js::AutoKeepTypeScripts&);
 
     inline js::TypeScript* types(const js::AutoSweepTypeScript& sweep);
 
     inline bool typesNeedsSweep() const;
 
-    void sweepTypes(const js::AutoSweepTypeScript& sweep,
-                    js::AutoClearTypeInferenceStateOnOOM* oom);
+    void sweepTypes(const js::AutoSweepTypeScript& sweep);
 
     inline js::GlobalObject& global() const;
     js::GlobalObject& uninlinedGlobal() const;
 
     uint32_t bodyScopeIndex() const {
         return bodyScopeIndex_;
     }
 
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -473,17 +473,17 @@ class ObjectGroup : public gc::TenuredCe
     void clearNewScript(JSContext* cx, ObjectGroup* replacement = nullptr);
 
     void print(const AutoSweepObjectGroup& sweep);
 
     inline void clearProperties(const AutoSweepObjectGroup& sweep);
     void traceChildren(JSTracer* trc);
 
     inline bool needsSweep();
-    void sweep(const AutoSweepObjectGroup& sweep, AutoClearTypeInferenceStateOnOOM* oom);
+    void sweep(const AutoSweepObjectGroup& sweep);
 
   private:
     uint32_t generation() {
         return (flags_ & OBJECT_FLAG_GENERATION_MASK) >> OBJECT_FLAG_GENERATION_SHIFT;
     }
 
   public:
     void setGeneration(uint32_t generation) {
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -432,26 +432,27 @@ ObjectGroup::hasUnanalyzedPreliminaryObj
  * Structure for type inference entry point functions. All functions which can
  * change type information must use this, and functions which depend on
  * intermediate types (i.e. JITs) can use this to ensure that intermediate
  * information is not collected and does not change.
  *
  * Ensures that GC cannot occur. Does additional sanity checking that inference
  * is not reentrant and that recompilations occur properly.
  */
-struct AutoEnterAnalysis
+struct MOZ_RAII AutoEnterAnalysis
 {
     // For use when initializing an UnboxedLayout.  The UniquePtr's destructor
     // must run when GC is not suppressed.
     UniquePtr<UnboxedLayout> unboxedLayoutToCleanUp;
 
     // Prevent GC activity in the middle of analysis.
     gc::AutoSuppressGC suppressGC;
 
-    // Allow clearing inference info on OOM during incremental sweeping.
+    // Allow clearing inference info on OOM during incremental sweeping. This is
+    // constructed for the outermost AutoEnterAnalysis on the stack.
     mozilla::Maybe<AutoClearTypeInferenceStateOnOOM> oom;
 
     // Pending recompilations to perform before execution of JIT code can resume.
     RecompileInfoVector pendingRecompiles;
 
     // Prevent us from calling the objectMetadataCallback.
     js::AutoSuppressAllocationMetadataBuilder suppressMetadata;
 
@@ -488,17 +489,17 @@ struct AutoEnterAnalysis
     void init(FreeOp* fop, Zone* zone) {
 #ifdef JS_CRASH_DIAGNOSTICS
         MOZ_RELEASE_ASSERT(CurrentThreadCanAccessZone(zone));
 #endif
         this->freeOp = fop;
         this->zone = zone;
 
         if (!zone->types.activeAnalysis) {
-            MOZ_RELEASE_ASSERT(!zone->types.sweepingTypes);
+            oom.emplace(zone);
             zone->types.activeAnalysis = this;
         }
     }
 };
 
 /////////////////////////////////////////////////////////////////////
 // Interface functions
 /////////////////////////////////////////////////////////////////////
@@ -1437,45 +1438,43 @@ ObjectGroup::getProperty(const AutoSweep
     }
     if (result) {
         result->types.checkMagic();
     }
     return result;
 }
 
 inline
-AutoSweepObjectGroup::AutoSweepObjectGroup(ObjectGroup* group,
-                                           AutoClearTypeInferenceStateOnOOM* oom)
+AutoSweepObjectGroup::AutoSweepObjectGroup(ObjectGroup* group)
 #ifdef DEBUG
   : group_(group)
 #endif
 {
     if (group->needsSweep()) {
-        group->sweep(*this, oom);
+        group->sweep(*this);
     }
 }
 
 #ifdef DEBUG
 inline
 AutoSweepObjectGroup::~AutoSweepObjectGroup()
 {
     // This should still hold.
     MOZ_ASSERT(!group_->needsSweep());
 }
 #endif
 
 inline
-AutoSweepTypeScript::AutoSweepTypeScript(JSScript* script,
-                                         AutoClearTypeInferenceStateOnOOM* oom)
+AutoSweepTypeScript::AutoSweepTypeScript(JSScript* script)
 #ifdef DEBUG
   : script_(script)
 #endif
 {
     if (script->typesNeedsSweep()) {
-        script->sweepTypes(*this, oom);
+        script->sweepTypes(*this);
     }
 }
 
 #ifdef DEBUG
 inline
 AutoSweepTypeScript::~AutoSweepTypeScript()
 {
     // This should still hold.
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -4625,18 +4625,17 @@ AssertGCStateForSweep(Zone* zone)
     MOZ_ASSERT(zone->isGCSweepingOrCompacting());
 
     // IsAboutToBeFinalized doesn't work right on tenured objects when called
     // during a minor collection.
     MOZ_ASSERT(!JS::RuntimeHeapIsMinorCollecting());
 }
 
 void
-ConstraintTypeSet::sweep(const AutoSweepBase& sweep, Zone* zone,
-                         AutoClearTypeInferenceStateOnOOM& oom)
+ConstraintTypeSet::sweep(const AutoSweepBase& sweep, Zone* zone)
 {
     AssertGCStateForSweep(zone);
 
     checkMagic();
 
     /*
      * Purge references to objects that are no longer live. Type sets hold
      * only weak references. For type sets containing more than one object,
@@ -4659,17 +4658,17 @@ ConstraintTypeSet::sweep(const AutoSweep
             }
             if (!IsObjectKeyAboutToBeFinalized(&key)) {
                 ObjectKey** pentry =
                     TypeHashSet::Insert<ObjectKey*, ObjectKey, ObjectKey>
                         (zone->types.typeLifoAlloc(), objectSet, objectCount, key);
                 if (pentry) {
                     *pentry = key;
                 } else {
-                    oom.setOOM();
+                    zone->types.setOOMSweepingTypes();
                     flags |= TYPE_FLAG_ANYOBJECT;
                     clearObjects();
                     objectCount = 0;
                     break;
                 }
             } else if (key->isGroup() &&
                        key->groupNoBarrier()->unknownPropertiesDontCheckGeneration()) {
                 // Object sets containing objects with unknown properties might
@@ -4716,17 +4715,17 @@ ConstraintTypeSet::sweep(const AutoSweep
         MOZ_ASSERT(zone->types.sweepTypeLifoAlloc.ref().contains(constraint));
         TypeConstraint* copy;
         if (constraint->sweep(zone->types, &copy)) {
             if (copy) {
                 MOZ_ASSERT(zone->types.typeLifoAlloc().contains(copy));
                 copy->setNext(constraintList_);
                 constraintList_ = copy;
             } else {
-                oom.setOOM();
+                zone->types.setOOMSweepingTypes();
             }
         }
         TypeConstraint* next = constraint->next();
         JS_POISON(constraint, JS_SWEPT_TI_PATTERN, sizeof(TypeConstraint),
                   MemCheckKind::MakeUndefined);
         constraint = next;
     }
 }
@@ -4739,50 +4738,34 @@ ObjectGroup::clearProperties(const AutoS
     if (zone()->needsIncrementalBarrier()) {
         traceChildren(zone()->barrierTracer());
     }
 
     setBasePropertyCount(sweep, 0);
     propertySet = nullptr;
 }
 
-static void
-EnsureHasAutoClearTypeInferenceStateOnOOM(AutoClearTypeInferenceStateOnOOM*& oom, Zone* zone,
-                                          Maybe<AutoClearTypeInferenceStateOnOOM>& fallback)
-{
-    if (!oom) {
-        if (AutoEnterAnalysis* analysis = zone->types.activeAnalysis) {
-            if (analysis->oom.isNothing()) {
-                analysis->oom.emplace(zone);
-            }
-            oom = analysis->oom.ptr();
-        } else {
-            fallback.emplace(zone);
-            oom = &fallback.ref();
-        }
-    }
-}
-
 /*
  * Before sweeping the arenas themselves, scan all groups in a compartment to
  * fixup weak references: property type sets referencing dead JS and type
  * objects, and singleton JS objects whose type is not referenced elsewhere.
  * This is done either incrementally as part of the sweep, or on demand as type
  * objects are accessed before their contents have been swept.
  */
 void
-ObjectGroup::sweep(const AutoSweepObjectGroup& sweep, AutoClearTypeInferenceStateOnOOM* oom)
+ObjectGroup::sweep(const AutoSweepObjectGroup& sweep)
 {
     MOZ_ASSERT(generation() != zoneFromAnyThread()->types.generation);
     setGeneration(zone()->types.generation);
 
     AssertGCStateForSweep(zone());
 
-    Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
-    EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
+    Maybe<AutoClearTypeInferenceStateOnOOM> clearStateOnOOM;
+    if (!zone()->types.isSweepingTypes())
+        clearStateOnOOM.emplace(zone());
 
     AutoTouchingGrayThings tgt;
 
     if (auto* layout = maybeUnboxedLayout(sweep)) {
         // Remove unboxed layouts that are about to be finalized from the
         // realm wide list while we are still on the main thread.
         ObjectGroup* group = this;
         if (IsAboutToBeFinalizedUnbarriered(&group)) {
@@ -4848,22 +4831,22 @@ ObjectGroup::sweep(const AutoSweepObject
                 Property* newProp = typeLifoAlloc.new_<Property>(*prop);
                 JS_POISON(prop, JS_SWEPT_TI_PATTERN, sizeof(Property),
                           MemCheckKind::MakeUndefined);
                 if (newProp) {
                     Property** pentry = TypeHashSet::Insert<jsid, Property, Property>
                                       (typeLifoAlloc, propertySet, propertyCount, newProp->id);
                     if (pentry) {
                         *pentry = newProp;
-                        newProp->types.sweep(sweep, zone(), *oom);
+                        newProp->types.sweep(sweep, zone());
                         continue;
                     }
                 }
 
-                oom->setOOM();
+                zone()->types.setOOMSweepingTypes();
                 addFlags(sweep, OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
                 clearProperties(sweep);
                 return;
             }
         }
         MOZ_RELEASE_ASSERT(oldPropertyCount == oldPropertiesFound);
         setBasePropertyCount(sweep, propertyCount);
     } else if (propertyCount == 1) {
@@ -4876,39 +4859,40 @@ ObjectGroup::sweep(const AutoSweepObject
             // Skip, as above.
             JS_POISON(prop, JS_SWEPT_TI_PATTERN, sizeof(Property), MemCheckKind::MakeUndefined);
             clearProperties(sweep);
         } else {
             Property* newProp = typeLifoAlloc.new_<Property>(*prop);
             JS_POISON(prop, JS_SWEPT_TI_PATTERN, sizeof(Property), MemCheckKind::MakeUndefined);
             if (newProp) {
                 propertySet = (Property**) newProp;
-                newProp->types.sweep(sweep, zone(), *oom);
+                newProp->types.sweep(sweep, zone());
             } else {
-                oom->setOOM();
+                zone()->types.setOOMSweepingTypes();
                 addFlags(sweep, OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
                 clearProperties(sweep);
                 return;
             }
         }
     } else {
         MOZ_RELEASE_ASSERT(!propertySet);
     }
 }
 
 /* static */ void
-JSScript::sweepTypes(const js::AutoSweepTypeScript& sweep, AutoClearTypeInferenceStateOnOOM* oom)
+JSScript::sweepTypes(const js::AutoSweepTypeScript& sweep)
 {
     MOZ_ASSERT(typesGeneration() != zone()->types.generation);
     setTypesGeneration(zone()->types.generation);
 
     AssertGCStateForSweep(zone());
 
-    Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
-    EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
+    Maybe<AutoClearTypeInferenceStateOnOOM> clearStateOnOOM;
+    if (!zone()->types.isSweepingTypes())
+        clearStateOnOOM.emplace(zone());
 
     TypeZone& types = zone()->types;
 
     // Sweep the inlinedCompilations Vector.
     {
         RecompileInfoVector& inlinedCompilations = types_->inlinedCompilations();
         size_t dest = 0;
         for (size_t i = 0; i < inlinedCompilations.length(); i++) {
@@ -4939,20 +4923,20 @@ JSScript::sweepTypes(const js::AutoSweep
         return;
     }
 
     unsigned num = TypeScript::NumTypeSets(this);
     StackTypeSet* typeArray = types_->typeArray();
 
     // Remove constraints and references to dead objects from stack type sets.
     for (unsigned i = 0; i < num; i++) {
-        typeArray[i].sweep(sweep, zone(), *oom);
-    }
-
-    if (oom->hadOOM()) {
+        typeArray[i].sweep(sweep, zone());
+    }
+
+    if (zone()->types.hadOOMSweepingTypes()) {
         // It's possible we OOM'd while copying freeze constraints, so they
         // need to be regenerated.
         bitFields_.hasFreezeConstraints_ = false;
     }
 }
 
 void
 TypeScript::destroy()
@@ -4995,16 +4979,17 @@ Zone::addSizeOfIncludingThis(mozilla::Ma
 TypeZone::TypeZone(Zone* zone)
   : zone_(zone),
     typeLifoAlloc_(zone, (size_t) TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     currentCompilationId_(zone),
     generation(zone, 0),
     sweepTypeLifoAlloc(zone, (size_t) TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     sweepReleaseTypes(zone, false),
     sweepingTypes(zone, false),
+    oomSweepingTypes(zone, false),
     keepTypeScripts(zone, false),
     activeAnalysis(zone, nullptr)
 {
 }
 
 TypeZone::~TypeZone()
 {
     MOZ_RELEASE_ASSERT(!sweepingTypes);
@@ -5041,34 +5026,34 @@ TypeZone::clearAllNewScriptsOnOOM()
         ObjectGroup* group = iter;
         if (!IsAboutToBeFinalizedUnbarriered(&group)) {
             group->maybeClearNewScriptOnOOM();
         }
     }
 }
 
 AutoClearTypeInferenceStateOnOOM::AutoClearTypeInferenceStateOnOOM(Zone* zone)
-  : zone(zone), oom(false)
+  : zone(zone)
 {
     MOZ_RELEASE_ASSERT(CurrentThreadCanAccessZone(zone));
     MOZ_ASSERT(!TlsContext.get()->inUnsafeCallWithABI);
     zone->types.setSweepingTypes(true);
 }
 
 AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()
 {
-    zone->types.setSweepingTypes(false);
-
-    if (oom) {
+    if (zone->types.hadOOMSweepingTypes()) {
         JSRuntime* rt = zone->runtimeFromMainThread();
         js::CancelOffThreadIonCompile(rt);
         zone->setPreservingCode(false);
         zone->discardJitCode(rt->defaultFreeOp(), /* discardBaselineCode = */ false);
         zone->types.clearAllNewScriptsOnOOM();
     }
+
+    zone->types.setSweepingTypes(false);
 }
 
 #ifdef DEBUG
 void
 TypeScript::printTypes(JSContext* cx, HandleScript script) const
 {
     AutoSweepTypeScript sweep(script);
     MOZ_ASSERT(script->types(sweep) == this);
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -46,31 +46,23 @@ class TempAllocator;
 // so that it stays correct (i.e. overapproximates the possible types in the
 // zone), but constraints might not have been triggered on the deoptimization
 // or even copied over completely. In this case, destroy all JIT code and new
 // script information in the zone, the only things whose correctness depends on
 // the type constraints.
 class AutoClearTypeInferenceStateOnOOM
 {
     Zone* zone;
-    bool oom;
 
     AutoClearTypeInferenceStateOnOOM(const AutoClearTypeInferenceStateOnOOM&) = delete;
     void operator=(const AutoClearTypeInferenceStateOnOOM&) = delete;
 
   public:
     explicit AutoClearTypeInferenceStateOnOOM(Zone* zone);
     ~AutoClearTypeInferenceStateOnOOM();
-
-    void setOOM() {
-        oom = true;
-    }
-    bool hadOOM() const {
-        return oom;
-    }
 };
 
 class MOZ_RAII AutoSweepBase
 {
     // Make sure we don't GC while this class is live since GC might trigger
     // (incremental) sweeping.
     JS::AutoCheckCannotGC nogc;
 };
@@ -79,18 +71,17 @@ class MOZ_RAII AutoSweepBase
 // reference to this class.
 class MOZ_RAII AutoSweepObjectGroup : public AutoSweepBase
 {
 #ifdef DEBUG
     ObjectGroup* group_;
 #endif
 
   public:
-    inline explicit AutoSweepObjectGroup(ObjectGroup* group,
-                                         AutoClearTypeInferenceStateOnOOM* oom = nullptr);
+    inline explicit AutoSweepObjectGroup(ObjectGroup* group);
 #ifdef DEBUG
     inline ~AutoSweepObjectGroup();
 
     ObjectGroup* group() const {
         return group_;
     }
 #endif
 };
@@ -99,18 +90,17 @@ class MOZ_RAII AutoSweepObjectGroup : pu
 // reference to this class.
 class MOZ_RAII AutoSweepTypeScript : public AutoSweepBase
 {
 #ifdef DEBUG
     JSScript* script_;
 #endif
 
   public:
-    inline explicit AutoSweepTypeScript(JSScript* script,
-                                        AutoClearTypeInferenceStateOnOOM* oom = nullptr);
+    inline explicit AutoSweepTypeScript(JSScript* script);
 #ifdef DEBUG
     inline ~AutoSweepTypeScript();
 
     JSScript* script() const {
         return script_;
     }
 #endif
 };
@@ -389,16 +379,17 @@ class TypeZone
     // for the zone.
     ZoneData<LifoAlloc> sweepTypeLifoAlloc;
 
     // During incremental sweeping, whether to try to destroy all type
     // information attached to scripts.
     ZoneData<bool> sweepReleaseTypes;
 
     ZoneData<bool> sweepingTypes;
+    ZoneData<bool> oomSweepingTypes;
 
     ZoneData<bool> keepTypeScripts;
 
     // The topmost AutoEnterAnalysis on the stack, if there is one.
     ZoneData<AutoEnterAnalysis*> activeAnalysis;
 
     explicit TypeZone(JS::Zone* zone);
     ~TypeZone();
@@ -417,19 +408,32 @@ class TypeZone
     void clearAllNewScriptsOnOOM();
 
     /* Mark a script as needing recompilation once inference has finished. */
     void addPendingRecompile(JSContext* cx, const RecompileInfo& info);
     void addPendingRecompile(JSContext* cx, JSScript* script);
 
     void processPendingRecompiles(FreeOp* fop, RecompileInfoVector& recompiles);
 
+    bool isSweepingTypes() const {
+        return sweepingTypes;
+    }
     void setSweepingTypes(bool sweeping) {
         MOZ_RELEASE_ASSERT(sweepingTypes != sweeping);
+        MOZ_ASSERT_IF(sweeping, !oomSweepingTypes);
         sweepingTypes = sweeping;
+        oomSweepingTypes = false;
+    }
+    void setOOMSweepingTypes() {
+        MOZ_ASSERT(sweepingTypes);
+        oomSweepingTypes = true;
+    }
+    bool hadOOMSweepingTypes() {
+        MOZ_ASSERT(sweepingTypes);
+        return oomSweepingTypes;
     }
 
     mozilla::Maybe<IonCompilationId> currentCompilationId() const {
         return currentCompilationId_.ref();
     }
     mozilla::Maybe<IonCompilationId>& currentCompilationIdRef() {
         return currentCompilationId_.ref();
     }
--- a/js/src/vm/TypeSet.h
+++ b/js/src/vm/TypeSet.h
@@ -740,18 +740,17 @@ class ConstraintTypeSet : public TypeSet
 
     // Trigger a post barrier when writing to this set, if necessary.
     // addType(cx, type) takes care of this automatically.
     void postWriteBarrier(JSContext* cx, Type type);
 
     /* Add a new constraint to this set. */
     bool addConstraint(JSContext* cx, TypeConstraint* constraint, bool callExisting = true);
 
-    inline void sweep(const AutoSweepBase& sweep, JS::Zone* zone,
-                      AutoClearTypeInferenceStateOnOOM& oom);
+    inline void sweep(const AutoSweepBase& sweep, JS::Zone* zone);
     inline void trace(JS::Zone* zone, JSTracer* trc);
 };
 
 class StackTypeSet : public ConstraintTypeSet
 {
   public:
 };