Bug 1490042 - Only allow a single AutoClearTypeInferenceStateOnOOM to be active at once r=jandem r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 13 Sep 2018 16:46:51 +0100
changeset 436214 aa3c5d257b1e8ddda72905e728d72d4d57762b7e
parent 436213 084a50d2778ae75429e21dce28e6e547951a05cd
child 436215 2eb3342c3ea47ba0cbfb698f6c5113ac7a89a491
push id34631
push usernerli@mozilla.com
push dateThu, 13 Sep 2018 22:02:04 +0000
treeherdermozilla-central@e923330d5bd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, sfink
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 a single AutoClearTypeInferenceStateOnOOM to be active at once r=jandem r=sfink
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
--- 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, oom);
     }
     for (auto group = zone->cellIter<ObjectGroup>(); !group.done(); group.next()) {
-        AutoSweepObjectGroup sweep(group, &oom);
+        AutoSweepObjectGroup sweep(group, oom);
     }
 
     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)
+SweepThing(JSScript* script, AutoClearTypeInferenceStateOnOOM& oom)
 {
     AutoSweepTypeScript sweep(script, oom);
 }
 
 static void
-SweepThing(ObjectGroup* group, AutoClearTypeInferenceStateOnOOM* oom)
+SweepThing(ObjectGroup* group, AutoClearTypeInferenceStateOnOOM& oom)
 {
     AutoSweepObjectGroup sweep(group, oom);
 }
 
 template <typename T, typename... Args>
 static bool
-SweepArenaList(Arena** arenasToSweep, SliceBudget& sliceBudget, Args... args)
+SweepArenaList(Arena** arenasToSweep, SliceBudget& sliceBudget, Args&&... args)
 {
     while (Arena* arena = *arenasToSweep) {
         for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
-            SweepThing(i.get<T>(), args...);
+            SweepThing(i.get<T>(), std::forward<Args>(args)...);
         }
 
         *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, oom)) {
         return NotFinished;
     }
 
-    if (!SweepArenaList<ObjectGroup>(&al.gcObjectGroupArenasToUpdate.ref(), budget, &oom)) {
+    if (!SweepArenaList<ObjectGroup>(&al.gcObjectGroupArenasToUpdate.ref(), budget, oom)) {
         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
@@ -7292,16 +7292,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
@@ -4372,16 +4372,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
@@ -1790,17 +1790,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);
+                    js::AutoClearTypeInferenceStateOnOOM& oom);
 
     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
@@ -280,17 +280,18 @@ class ObjectGroup : public gc::TenuredCe
     TypeNewScript* newScriptDontCheckGeneration() const {
         if (addendumKind() == Addendum_NewScript) {
             return reinterpret_cast<TypeNewScript*>(addendum_);
         }
         return nullptr;
     }
 
     TypeNewScript* anyNewScript(const AutoSweepObjectGroup& sweep);
-    void detachNewScript(bool writeBarrier, ObjectGroup* replacement);
+    void detachNewScript(bool writeBarrier, ObjectGroup* replacement,
+                         AutoClearTypeInferenceStateOnOOM& oom);
 
     ObjectGroupFlags flagsDontCheckGeneration() const {
         return flags_;
     }
 
   public:
 
     inline ObjectGroupFlags flags(const AutoSweepObjectGroup&);
@@ -468,26 +469,26 @@ class ObjectGroup : public gc::TenuredCe
                                 jsid id, HeapTypeSet* types);
     void addDefiniteProperties(JSContext* cx, Shape* shape);
     bool matchDefiniteProperties(HandleObject obj);
     void markPropertyNonData(JSContext* cx, JSObject* obj, jsid id);
     void markPropertyNonWritable(JSContext* cx, JSObject* obj, jsid id);
     void markStateChange(const AutoSweepObjectGroup& sweep, JSContext* cx);
     void setFlags(const AutoSweepObjectGroup& sweep, JSContext* cx, ObjectGroupFlags flags);
     void markUnknown(const AutoSweepObjectGroup& sweep, JSContext* cx);
-    void maybeClearNewScriptOnOOM();
+    void maybeClearNewScriptOnOOM(AutoClearTypeInferenceStateOnOOM& oom);
     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, AutoClearTypeInferenceStateOnOOM& oom);
 
   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
@@ -281,19 +281,16 @@ struct 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.
-    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;
 
     FreeOp* freeOp;
     Zone* zone;
@@ -1277,18 +1274,30 @@ ObjectGroup::getProperty(const AutoSweep
     }
     if (result) {
         result->types.checkMagic();
     }
     return result;
 }
 
 inline
+AutoSweepObjectGroup::AutoSweepObjectGroup(ObjectGroup* group)
+#ifdef DEBUG
+  : group_(group)
+#endif
+{
+    if (group->needsSweep()) {
+        AutoClearTypeInferenceStateOnOOM oom(group->zone());
+        group->sweep(*this, oom);
+    }
+}
+
+inline
 AutoSweepObjectGroup::AutoSweepObjectGroup(ObjectGroup* group,
-                                           AutoClearTypeInferenceStateOnOOM* oom)
+                                           AutoClearTypeInferenceStateOnOOM& oom)
 #ifdef DEBUG
   : group_(group)
 #endif
 {
     if (group->needsSweep()) {
         group->sweep(*this, oom);
     }
 }
@@ -1298,18 +1307,29 @@ 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()) {
+        AutoClearTypeInferenceStateOnOOM oom(script->zone());
+        script->sweepTypes(*this, oom);
+    }
+}
+
+inline
+AutoSweepTypeScript::AutoSweepTypeScript(JSScript* script, AutoClearTypeInferenceStateOnOOM& oom)
 #ifdef DEBUG
   : script_(script)
 #endif
 {
     if (script->typesNeedsSweep()) {
         script->sweepTypes(*this, oom);
     }
 }
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3297,35 +3297,36 @@ ObjectGroup::anyNewScript(const AutoSwee
     }
     if (maybeUnboxedLayout(sweep)) {
         return unboxedLayout(sweep).newScript();
     }
     return nullptr;
 }
 
 void
-ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement)
+ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement,
+                             AutoClearTypeInferenceStateOnOOM& oom)
 {
     // Clear the TypeNewScript from this ObjectGroup and, if it has been
     // analyzed, remove it from the newObjectGroups table so that it will not be
     // produced by calling 'new' on the associated function anymore.
     // The TypeNewScript is not actually destroyed.
-    AutoSweepObjectGroup sweep(this);
+    AutoSweepObjectGroup sweep(this, oom);
     TypeNewScript* newScript = anyNewScript(sweep);
     MOZ_ASSERT(newScript);
 
     if (newScript->analyzed()) {
         ObjectGroupRealm& objectGroups = ObjectGroupRealm::get(this);
         TaggedProto proto = this->proto();
         if (proto.isObject() && IsForwarded(proto.toObject())) {
             proto = TaggedProto(Forwarded(proto.toObject()));
         }
         JSObject* associated = MaybeForwarded(newScript->function());
         if (replacement) {
-            AutoSweepObjectGroup sweepReplacement(replacement);
+            AutoSweepObjectGroup sweepReplacement(replacement, oom);
             MOZ_ASSERT(replacement->newScript(sweepReplacement)->function() == newScript->function());
             objectGroups.replaceDefaultNewGroup(nullptr, proto, associated, replacement);
         } else {
             objectGroups.removeDefaultNewGroup(nullptr, proto, associated);
         }
     } else {
         MOZ_ASSERT(!replacement);
     }
@@ -3333,59 +3334,60 @@ ObjectGroup::detachNewScript(bool writeB
     if (this->newScript(sweep)) {
         setAddendum(Addendum_None, nullptr, writeBarrier);
     } else {
         unboxedLayout(sweep).setNewScript(nullptr, writeBarrier);
     }
 }
 
 void
-ObjectGroup::maybeClearNewScriptOnOOM()
+ObjectGroup::maybeClearNewScriptOnOOM(AutoClearTypeInferenceStateOnOOM& oom)
 {
     MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
 
     if (!isMarkedAny()) {
         return;
     }
 
-    AutoSweepObjectGroup sweep(this);
+    AutoSweepObjectGroup sweep(this, oom);
     TypeNewScript* newScript = anyNewScript(sweep);
     if (!newScript) {
         return;
     }
 
     addFlags(sweep, OBJECT_FLAG_NEW_SCRIPT_CLEARED);
 
     // This method is called during GC sweeping, so don't trigger pre barriers.
-    detachNewScript(/* writeBarrier = */ false, nullptr);
+    detachNewScript(/* writeBarrier = */ false, nullptr, oom);
 
     js_delete(newScript);
 }
 
 void
 ObjectGroup::clearNewScript(JSContext* cx, ObjectGroup* replacement /* = nullptr*/)
 {
-    AutoSweepObjectGroup sweep(this);
+    AutoClearTypeInferenceStateOnOOM oom(zone());
+    AutoSweepObjectGroup sweep(this, oom);
     TypeNewScript* newScript = anyNewScript(sweep);
     if (!newScript) {
         return;
     }
 
     AutoEnterAnalysis enter(cx);
 
     if (!replacement) {
         // Invalidate any Ion code constructing objects of this type.
         setFlags(sweep, cx, OBJECT_FLAG_NEW_SCRIPT_CLEARED);
 
         // Mark the constructing function as having its 'new' script cleared, so we
         // will not try to construct another one later.
         newScript->function()->setNewScriptCleared();
     }
 
-    detachNewScript(/* writeBarrier = */ true, replacement);
+    detachNewScript(/* writeBarrier = */ true, replacement, oom);
 
     if (!cx->helperThread()) {
         bool found = newScript->rollbackPartiallyInitializedObjects(cx, this);
 
         // If we managed to rollback any partially initialized objects, then
         // any definite properties we added due to analysis of the new script
         // are now invalid, so remove them. If there weren't any partially
         // initialized objects then we don't need to change type information,
@@ -4735,51 +4737,31 @@ 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, AutoClearTypeInferenceStateOnOOM& oom)
 {
     MOZ_ASSERT(generation() != zoneFromAnyThread()->types.generation);
     setGeneration(zone()->types.generation);
 
     AssertGCStateForSweep(zone());
 
-    Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
-    EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
-
     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)) {
             layout->detachFromRealm();
@@ -4844,22 +4826,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(), oom);
                         continue;
                     }
                 }
 
-                oom->setOOM();
+                oom.setOOM();
                 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) {
@@ -4872,40 +4854,37 @@ 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(), oom);
             } else {
-                oom->setOOM();
+                oom.setOOM();
                 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, AutoClearTypeInferenceStateOnOOM& oom)
 {
     MOZ_ASSERT(typesGeneration() != zone()->types.generation);
     setTypesGeneration(zone()->types.generation);
 
     AssertGCStateForSweep(zone());
 
-    Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
-    EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
-
     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++) {
             if (inlinedCompilations[i].shouldSweep(types)) {
@@ -4935,20 +4914,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(), oom);
+    }
+
+    if (oom.hadOOM()) {
         // It's possible we OOM'd while copying freeze constraints, so they
         // need to be regenerated.
         bitFields_.hasFreezeConstraints_ = false;
     }
 }
 
 void
 TypeScript::destroy()
@@ -5026,45 +5005,45 @@ void
 TypeZone::endSweep(JSRuntime* rt)
 {
     sweepReleaseTypes = false;
 
     rt->gc.freeAllLifoBlocksAfterSweeping(&sweepTypeLifoAlloc.ref());
 }
 
 void
-TypeZone::clearAllNewScriptsOnOOM()
+TypeZone::clearAllNewScriptsOnOOM(AutoClearTypeInferenceStateOnOOM& oom)
 {
     for (auto iter = zone()->cellIter<ObjectGroup>(); !iter.done(); iter.next()) {
         ObjectGroup* group = iter;
         if (!IsAboutToBeFinalizedUnbarriered(&group)) {
-            group->maybeClearNewScriptOnOOM();
+            group->maybeClearNewScriptOnOOM(oom);
         }
     }
 }
 
 AutoClearTypeInferenceStateOnOOM::AutoClearTypeInferenceStateOnOOM(Zone* zone)
   : zone(zone), oom(false)
 {
     MOZ_RELEASE_ASSERT(CurrentThreadCanAccessZone(zone));
     MOZ_ASSERT(!TlsContext.get()->inUnsafeCallWithABI);
     zone->types.setSweepingTypes(true);
 }
 
 AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()
 {
-    zone->types.setSweepingTypes(false);
-
     if (oom) {
         JSRuntime* rt = zone->runtimeFromMainThread();
         js::CancelOffThreadIonCompile(rt);
         zone->setPreservingCode(false);
         zone->discardJitCode(rt->defaultFreeOp(), /* discardBaselineCode = */ false);
-        zone->types.clearAllNewScriptsOnOOM();
-    }
+        zone->types.clearAllNewScriptsOnOOM(*this);
+    }
+
+    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
@@ -694,18 +694,18 @@ 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);
+    inline AutoSweepObjectGroup(ObjectGroup* group, AutoClearTypeInferenceStateOnOOM& oom);
 #ifdef DEBUG
     inline ~AutoSweepObjectGroup();
 
     ObjectGroup* group() const {
         return group_;
     }
 #endif
 };
@@ -714,18 +714,18 @@ 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);
+    inline AutoSweepTypeScript(JSScript* script, AutoClearTypeInferenceStateOnOOM& oom);
 #ifdef DEBUG
     inline ~AutoSweepTypeScript();
 
     JSScript* script() const {
         return script_;
     }
 #endif
 };
@@ -1462,17 +1462,17 @@ class TypeZone
 #ifdef JS_CRASH_DIAGNOSTICS
         MOZ_RELEASE_ASSERT(CurrentThreadCanAccessZone(zone_));
 #endif
         return typeLifoAlloc_.ref();
     }
 
     void beginSweep(bool releaseTypes);
     void endSweep(JSRuntime* rt);
-    void clearAllNewScriptsOnOOM();
+    void clearAllNewScriptsOnOOM(AutoClearTypeInferenceStateOnOOM& oom);
 
     /* 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);
 
     void setSweepingTypes(bool sweeping) {