Bug 988486 - Move verifier functions into GCRuntime r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 19 May 2014 11:09:55 +0100
changeset 183746 0ad2c38c664983201911bb4167b588121e51001a
parent 183745 31984278765e18f3faa7bc5fa5966884e08eed17
child 183747 2020f15e4e4584cc03622a10550a26efef3e84ed
push id26801
push userkwierso@gmail.com
push dateMon, 19 May 2014 19:35:37 +0000
treeherdermozilla-central@6b52f777d2ac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs988486
milestone32.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 988486 - Move verifier functions into GCRuntime r=terrence
js/public/GCAPI.h
js/src/gc/GCInternals.h
js/src/gc/GCRuntime.h
js/src/gc/Verifier.cpp
js/src/jsfriendapi.cpp
js/src/jsgc.cpp
js/src/jsgcinlines.h
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -8,16 +8,22 @@
 #define js_GCAPI_h
 
 #include "mozilla/NullPtr.h"
 
 #include "js/HeapAPI.h"
 #include "js/RootingAPI.h"
 #include "js/Value.h"
 
+namespace js {
+namespace gc {
+class GCRuntime;
+}
+}
+
 typedef enum JSGCMode {
     /* Perform only global GCs. */
     JSGC_MODE_GLOBAL = 0,
 
     /* Perform per-compartment GCs until too much garbage has accumulated. */
     JSGC_MODE_COMPARTMENT = 1,
 
     /*
@@ -324,17 +330,17 @@ WasIncrementalGC(JSRuntime *rt);
  * Note: Generational GC is not yet enabled by default. The following class
  *       is non-functional unless SpiderMonkey was configured with
  *       --enable-gcgenerational.
  */
 
 /* Ensure that generational GC is disabled within some scope. */
 class JS_FRIEND_API(AutoDisableGenerationalGC)
 {
-    JSRuntime *runtime;
+    js::gc::GCRuntime *gc;
 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
     bool restartVerifier;
 #endif
 
   public:
     AutoDisableGenerationalGC(JSRuntime *rt);
     ~AutoDisableGenerationalGC();
 };
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -86,57 +86,40 @@ class IncrementalSafety
         return reason_;
     }
 };
 
 IncrementalSafety
 IsIncrementalGCSafe(JSRuntime *rt);
 
 #ifdef JS_GC_ZEAL
-void
-StartVerifyPreBarriers(JSRuntime *rt);
-
-void
-EndVerifyPreBarriers(JSRuntime *rt);
-
-void
-StartVerifyPostBarriers(JSRuntime *rt);
-
-void
-EndVerifyPostBarriers(JSRuntime *rt);
-
-void
-FinishVerifier(JSRuntime *rt);
 
 class AutoStopVerifyingBarriers
 {
-    JSRuntime *runtime;
+    GCRuntime *gc;
     bool restartPreVerifier;
     bool restartPostVerifier;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     AutoStopVerifyingBarriers(JSRuntime *rt, bool isShutdown
                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : runtime(rt)
+      : gc(&rt->gc)
     {
-        restartPreVerifier = !isShutdown && rt->gc.verifyPreData;
-        restartPostVerifier = !isShutdown && rt->gc.verifyPostData && JS::IsGenerationalGCEnabled(rt);
-        if (rt->gc.verifyPreData)
-            EndVerifyPreBarriers(rt);
-        if (rt->gc.verifyPostData)
-            EndVerifyPostBarriers(rt);
+        restartPreVerifier = gc->endVerifyPreBarriers() && !isShutdown;
+        restartPostVerifier = gc->endVerifyPostBarriers() && !isShutdown &&
+            JS::IsGenerationalGCEnabled(rt);
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     ~AutoStopVerifyingBarriers() {
         if (restartPreVerifier)
-            StartVerifyPreBarriers(runtime);
+            gc->startVerifyPreBarriers();
         if (restartPostVerifier)
-            StartVerifyPostBarriers(runtime);
+            gc->startVerifyPostBarriers();
     }
 };
 #else
 struct AutoStopVerifyingBarriers
 {
     AutoStopVerifyingBarriers(JSRuntime *, bool) {}
 };
 #endif /* JS_GC_ZEAL */
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -126,16 +126,23 @@ class GCRuntime
     void gcIfNeeded(JSContext *cx);
     void collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
                  JS::gcreason::Reason reason);
     void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis);
     void runDebugGC();
 
     void markRuntime(JSTracer *trc, bool useSavedRoots = false);
 
+#ifdef JS_GC_ZEAL
+    void verifyPreBarriers();
+    void verifyPostBarriers();
+    void maybeVerifyPreBarriers(bool always);
+    void maybeVerifyPostBarriers(bool always);
+#endif
+
   public:
     // Internal public interface
     void recordNativeStackTop();
     void notifyRequestEnd() { conservativeGC.updateForRequestEnd(); }
     bool isBackgroundSweeping() { return helperThread.sweeping(); }
     void waitBackgroundSweepEnd() { helperThread.waitBackgroundSweepEnd(); }
     void waitBackgroundSweepOrAllocEnd() { helperThread.waitBackgroundSweepOrAllocEnd(); }
     void startBackgroundShrink() { helperThread.startBackgroundShrink(); }
@@ -174,16 +181,28 @@ class GCRuntime
     void allowAlloc() {
         JS_ASSERT(!isAllocAllowed());
         --noGCOrAllocationCheck;
     }
 #endif
 
     void setAlwaysPreserveCode() { alwaysPreserveCode = true; }
 
+    bool isGenerationalGCEnabled() { return generationalDisabled == 0; }
+    void disableGenerationalGC();
+    void enableGenerationalGC();
+
+#ifdef JS_GC_ZEAL
+    void startVerifyPreBarriers();
+    bool endVerifyPreBarriers();
+    void startVerifyPostBarriers();
+    bool endVerifyPostBarriers();
+    void finishVerifier();
+#endif
+
   private:
     // For ArenaLists::allocateFromArenaInline()
     friend class ArenaLists;
     Chunk *pickChunk(Zone *zone);
 
     inline bool wantBackgroundAllocation() const;
 
     bool initGCZeal();
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -164,41 +164,41 @@ NextNode(VerifyNode *node)
     if (node->count == 0)
         return (VerifyNode *)((char *)node + sizeof(VerifyNode) - sizeof(EdgeValue));
     else
         return (VerifyNode *)((char *)node + sizeof(VerifyNode) +
                              sizeof(EdgeValue)*(node->count - 1));
 }
 
 void
-gc::StartVerifyPreBarriers(JSRuntime *rt)
+gc::GCRuntime::startVerifyPreBarriers()
 {
-    if (rt->gc.verifyPreData || rt->gc.incrementalState != NO_INCREMENTAL)
+    if (verifyPreData || incrementalState != NO_INCREMENTAL)
         return;
 
     /*
      * The post barrier verifier requires the storebuffer to be enabled, but the
      * pre barrier verifier disables it as part of disabling GGC.  Don't allow
      * starting the pre barrier verifier if the post barrier verifier is already
      * running.
      */
-    if (rt->gc.verifyPostData)
+    if (verifyPostData)
         return;
 
     MinorGC(rt, JS::gcreason::EVICT_NURSERY);
 
     AutoPrepareForTracing prep(rt, WithAtoms);
 
     if (!IsIncrementalGCSafe(rt))
         return;
 
-    for (GCChunkSet::Range r(rt->gc.chunkSet.all()); !r.empty(); r.popFront())
+    for (GCChunkSet::Range r(chunkSet.all()); !r.empty(); r.popFront())
         r.front()->bitmap.clear();
 
-    rt->gc.number++;
+    number++;
 
     VerifyPreTracer *trc = js_new<VerifyPreTracer>(rt, JSTraceCallback(nullptr));
     if (!trc)
         return;
 
     /*
      * Passing a function pointer directly to js_new trips a compiler bug in
      * MSVC. Work around by filling the pointer after allocating with nullptr.
@@ -214,20 +214,20 @@ gc::StartVerifyPreBarriers(JSRuntime *rt
 
     if (!trc->nodemap.init())
         goto oom;
 
     /* Create the root node. */
     trc->curnode = MakeNode(trc, nullptr, JSGCTraceKind(0));
 
     /* We want MarkRuntime to save the roots to gcSavedRoots. */
-    rt->gc.incrementalState = MARK_ROOTS;
+    incrementalState = MARK_ROOTS;
 
     /* Make all the roots be edges emanating from the root node. */
-    rt->gc.markRuntime(trc);
+    markRuntime(trc);
 
     VerifyNode *node;
     node = trc->curnode;
     if (trc->edgeptr == trc->term)
         goto oom;
 
     /* For each edge, make a node for it if one doesn't already exist. */
     while ((char *)node < trc->edgeptr) {
@@ -240,33 +240,33 @@ gc::StartVerifyPreBarriers(JSRuntime *rt
             }
             if (trc->edgeptr == trc->term)
                 goto oom;
         }
 
         node = NextNode(node);
     }
 
-    rt->gc.verifyPreData = trc;
-    rt->gc.incrementalState = MARK;
-    rt->gc.marker.start();
+    verifyPreData = trc;
+    incrementalState = MARK;
+    marker.start();
 
     rt->setNeedsBarrier(true);
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         PurgeJITCaches(zone);
         zone->setNeedsBarrier(true, Zone::UpdateIon);
         zone->allocator.arenas.purge();
     }
 
     return;
 
 oom:
-    rt->gc.incrementalState = NO_INCREMENTAL;
+    incrementalState = NO_INCREMENTAL;
     js_delete(trc);
-    rt->gc.verifyPreData = nullptr;
+    verifyPreData = nullptr;
 }
 
 static bool
 IsMarkedOrAllocated(Cell *cell)
 {
     return cell->isMarked() || cell->arenaHeader()->allocatedDuringIncremental;
 }
 
@@ -311,49 +311,49 @@ AssertMarkedOrAllocated(const EdgeValue 
     char msgbuf[1024];
     const char *label = edge.label;
 
     JS_snprintf(msgbuf, sizeof(msgbuf), "[barrier verifier] Unmarked edge: %s", label);
     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
     MOZ_CRASH();
 }
 
-void
-gc::EndVerifyPreBarriers(JSRuntime *rt)
+bool
+gc::GCRuntime::endVerifyPreBarriers()
 {
+    VerifyPreTracer *trc = (VerifyPreTracer *)verifyPreData;
+
+    if (!trc)
+        return false;
+
     JS_ASSERT(!JS::IsGenerationalGCEnabled(rt));
 
     AutoPrepareForTracing prep(rt, SkipAtoms);
 
-    VerifyPreTracer *trc = (VerifyPreTracer *)rt->gc.verifyPreData;
-
-    if (!trc)
-        return;
-
     bool compartmentCreated = false;
 
     /* We need to disable barriers before tracing, which may invoke barriers. */
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         if (!zone->needsBarrier())
             compartmentCreated = true;
 
         zone->setNeedsBarrier(false, Zone::UpdateIon);
         PurgeJITCaches(zone);
     }
     rt->setNeedsBarrier(false);
 
     /*
      * We need to bump gcNumber so that the methodjit knows that jitcode has
      * been discarded.
      */
-    JS_ASSERT(trc->number == rt->gc.number);
-    rt->gc.number++;
+    JS_ASSERT(trc->number == number);
+    number++;
 
-    rt->gc.verifyPreData = nullptr;
-    rt->gc.incrementalState = NO_INCREMENTAL;
+    verifyPreData = nullptr;
+    incrementalState = NO_INCREMENTAL;
 
     if (!compartmentCreated && IsIncrementalGCSafe(rt)) {
         trc->setTraceCallback(CheckEdge);
 
         /* Start after the roots. */
         VerifyNode *node = NextNode(trc->root);
         while ((char *)node < trc->edgeptr) {
             trc->curnode = node;
@@ -363,20 +363,21 @@ gc::EndVerifyPreBarriers(JSRuntime *rt)
                 for (uint32_t i = 0; i < node->count; i++)
                     AssertMarkedOrAllocated(node->edges[i]);
             }
 
             node = NextNode(node);
         }
     }
 
-    rt->gc.marker.reset();
-    rt->gc.marker.stop();
+    marker.reset();
+    marker.stop();
 
     js_delete(trc);
+    return true;
 }
 
 /*** Post-Barrier Verifyier ***/
 
 struct VerifyPostTracer : JSTracer
 {
     /* The gcNumber when the verification began. */
     uint64_t number;
@@ -394,34 +395,34 @@ struct VerifyPostTracer : JSTracer
 };
 
 /*
  * The post-barrier verifier runs the full store buffer and a fake nursery when
  * running and when it stops, walks the full heap to ensure that all the
  * important edges were inserted into the storebuffer.
  */
 void
-gc::StartVerifyPostBarriers(JSRuntime *rt)
+gc::GCRuntime::startVerifyPostBarriers()
 {
 #ifdef JSGC_GENERATIONAL
-    if (rt->gc.verifyPostData ||
-        rt->gc.incrementalState != NO_INCREMENTAL)
+    if (verifyPostData ||
+        incrementalState != NO_INCREMENTAL)
     {
         return;
     }
 
     MinorGC(rt, JS::gcreason::EVICT_NURSERY);
 
-    rt->gc.number++;
+    number++;
 
     VerifyPostTracer *trc = js_new<VerifyPostTracer>(rt, JSTraceCallback(nullptr));
     if (!trc)
         return;
 
-    rt->gc.verifyPostData = trc;
+    verifyPostData = trc;
 #endif
 }
 
 #ifdef JSGC_GENERATIONAL
 void
 PostVerifierCollectStoreBufferEdges(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
 {
     VerifyPostTracer *trc = (VerifyPostTracer *)jstrc;
@@ -480,132 +481,138 @@ PostVerifierVisitEdge(JSTracer *jstrc, v
      * location will be set correctly in these cases.
      */
     void **loc = trc->tracingLocation(thingp);
 
     AssertStoreBufferContainsEdge(trc->edges, loc, dst);
 }
 #endif
 
-void
-js::gc::EndVerifyPostBarriers(JSRuntime *rt)
+bool
+js::gc::GCRuntime::endVerifyPostBarriers()
 {
 #ifdef JSGC_GENERATIONAL
+    VerifyPostTracer *trc = (VerifyPostTracer *)verifyPostData;
+    if (!trc)
+        return false;
+
     VerifyPostTracer::EdgeSet edges;
     AutoPrepareForTracing prep(rt, SkipAtoms);
 
-    VerifyPostTracer *trc = (VerifyPostTracer *)rt->gc.verifyPostData;
-
     /* Visit every entry in the store buffer and put the edges in a hash set. */
     trc->setTraceCallback(PostVerifierCollectStoreBufferEdges);
     if (!edges.init())
         goto oom;
     trc->edges = &edges;
-    rt->gc.storeBuffer.markAll(trc);
+    storeBuffer.markAll(trc);
 
     /* Walk the heap to find any edges not the the |edges| set. */
     trc->setTraceCallback(PostVerifierVisitEdge);
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
         for (size_t kind = 0; kind < FINALIZE_LIMIT; ++kind) {
             for (ZoneCellIterUnderGC cells(zone, AllocKind(kind)); !cells.done(); cells.next()) {
                 Cell *src = cells.getCell();
                 JS_TraceChildren(trc, src, MapAllocToTraceKind(AllocKind(kind)));
             }
         }
     }
 
 oom:
     js_delete(trc);
-    rt->gc.verifyPostData = nullptr;
+    verifyPostData = nullptr;
+    return true;
+#else
+    return false;
 #endif
 }
 
 /*** Barrier Verifier Scheduling ***/
 
-static void
-VerifyPreBarriers(JSRuntime *rt)
+void
+gc::GCRuntime::verifyPreBarriers()
 {
-    if (rt->gc.verifyPreData)
-        EndVerifyPreBarriers(rt);
+    if (verifyPreData)
+        endVerifyPreBarriers();
     else
-        StartVerifyPreBarriers(rt);
+        startVerifyPreBarriers();
 }
 
-static void
-VerifyPostBarriers(JSRuntime *rt)
+void
+gc::GCRuntime::verifyPostBarriers()
 {
-    if (rt->gc.verifyPostData)
-        EndVerifyPostBarriers(rt);
+    if (verifyPostData)
+        endVerifyPostBarriers();
     else
-        StartVerifyPostBarriers(rt);
+        startVerifyPostBarriers();
 }
 
 void
 gc::VerifyBarriers(JSRuntime *rt, VerifierType type)
 {
     if (type == PreBarrierVerifier)
-        VerifyPreBarriers(rt);
+        rt->gc.verifyPreBarriers();
     else
-        VerifyPostBarriers(rt);
+        rt->gc.verifyPostBarriers();
 }
 
-static void
-MaybeVerifyPreBarriers(JSRuntime *rt, bool always)
+void
+gc::GCRuntime::maybeVerifyPreBarriers(bool always)
 {
-    if (rt->gcZeal() != ZealVerifierPreValue)
+    if (zealMode != ZealVerifierPreValue)
         return;
 
     if (rt->mainThread.suppressGC)
         return;
 
-    if (VerifyPreTracer *trc = (VerifyPreTracer *)rt->gc.verifyPreData) {
-        if (++trc->count < rt->gc.zealFrequency && !always)
+    if (VerifyPreTracer *trc = (VerifyPreTracer *)verifyPreData) {
+        if (++trc->count < zealFrequency && !always)
             return;
 
-        EndVerifyPreBarriers(rt);
+        endVerifyPreBarriers();
     }
 
-    StartVerifyPreBarriers(rt);
+    startVerifyPreBarriers();
 }
 
-static void
-MaybeVerifyPostBarriers(JSRuntime *rt, bool always)
+void
+gc::GCRuntime::maybeVerifyPostBarriers(bool always)
 {
 #ifdef JSGC_GENERATIONAL
-    if (rt->gcZeal() != ZealVerifierPostValue)
+    if (zealMode != ZealVerifierPostValue)
         return;
 
-    if (rt->mainThread.suppressGC || !rt->gc.storeBuffer.isEnabled())
+    if (rt->mainThread.suppressGC || !storeBuffer.isEnabled())
         return;
 
-    if (VerifyPostTracer *trc = (VerifyPostTracer *)rt->gc.verifyPostData) {
-        if (++trc->count < rt->gc.zealFrequency && !always)
+    if (VerifyPostTracer *trc = (VerifyPostTracer *)verifyPostData) {
+        if (++trc->count < zealFrequency && !always)
             return;
 
-        EndVerifyPostBarriers(rt);
+        endVerifyPostBarriers();
     }
-    StartVerifyPostBarriers(rt);
+    startVerifyPostBarriers();
 #endif
 }
 
 void
 js::gc::MaybeVerifyBarriers(JSContext *cx, bool always)
 {
-    MaybeVerifyPreBarriers(cx->runtime(), always);
-    MaybeVerifyPostBarriers(cx->runtime(), always);
+    GCRuntime *gc = &cx->runtime()->gc;
+    gc->maybeVerifyPreBarriers(always);
+    gc->maybeVerifyPostBarriers(always);
 }
 
 void
-js::gc::FinishVerifier(JSRuntime *rt)
+js::gc::GCRuntime::finishVerifier()
 {
-    if (VerifyPreTracer *trc = (VerifyPreTracer *)rt->gc.verifyPreData) {
+    if (VerifyPreTracer *trc = (VerifyPreTracer *)verifyPreData) {
         js_delete(trc);
-        rt->gc.verifyPreData = nullptr;
+        verifyPreData = nullptr;
     }
 #ifdef JSGC_GENERATIONAL
-    if (VerifyPostTracer *trc = (VerifyPostTracer *)rt->gc.verifyPostData) {
+    if (VerifyPostTracer *trc = (VerifyPostTracer *)verifyPostData) {
         js_delete(trc);
-        rt->gc.verifyPostData = nullptr;
+        verifyPostData = nullptr;
     }
 #endif
 }
 
 #endif /* JS_GC_ZEAL */
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -923,55 +923,42 @@ JS::IsIncrementalGCInProgress(JSRuntime 
 
 JS_FRIEND_API(void)
 JS::DisableIncrementalGC(JSRuntime *rt)
 {
     rt->gc.incrementalEnabled = false;
 }
 
 JS::AutoDisableGenerationalGC::AutoDisableGenerationalGC(JSRuntime *rt)
-  : runtime(rt)
+  : gc(&rt->gc)
 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
-  , restartVerifier(rt->gc.verifyPostData)
+  , restartVerifier(false)
 #endif
 {
-#ifdef JSGC_GENERATIONAL
-    if (IsGenerationalGCEnabled(rt)) {
-#ifdef JS_GC_ZEAL
-        if (restartVerifier)
-            gc::EndVerifyPostBarriers(rt);
+#if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
+    restartVerifier = gc->endVerifyPostBarriers();
 #endif
-        MinorGC(rt, JS::gcreason::API);
-        rt->gc.nursery.disable();
-        rt->gc.storeBuffer.disable();
-    }
-#endif
-    ++rt->gc.generationalDisabled;
+    gc->disableGenerationalGC();
 }
 
 JS::AutoDisableGenerationalGC::~AutoDisableGenerationalGC()
 {
-    JS_ASSERT(runtime->gc.generationalDisabled > 0);
-    --runtime->gc.generationalDisabled;
-#ifdef JSGC_GENERATIONAL
-    if (runtime->gc.generationalDisabled == 0) {
-        runtime->gc.nursery.enable();
-        runtime->gc.storeBuffer.enable();
-#ifdef JS_GC_ZEAL
-        if (restartVerifier)
-            gc::StartVerifyPostBarriers(runtime);
-#endif
+    gc->enableGenerationalGC();
+#if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
+    if (restartVerifier) {
+        JS_ASSERT(gc->isGenerationalGCEnabled());
+        gc->startVerifyPostBarriers();
     }
 #endif
 }
 
 extern JS_FRIEND_API(bool)
 JS::IsGenerationalGCEnabled(JSRuntime *rt)
 {
-    return rt->gc.generationalDisabled == 0;
+    return rt->gc.isGenerationalGCEnabled();
 }
 
 JS_FRIEND_API(bool)
 JS::IsIncrementalBarrierNeeded(JSRuntime *rt)
 {
     return rt->gc.incrementalState == gc::MARK && !rt->isHeapBusy();
 }
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1239,17 +1239,17 @@ GCRuntime::finish()
     /*
      * Wait until the background finalization stops and the helper thread
      * shuts down before we forcefully release any remaining GC memory.
      */
     helperThread.finish();
 
 #ifdef JS_GC_ZEAL
     /* Free memory associated with GC verification. */
-    FinishVerifier(rt);
+    finishVerifier();
 #endif
 
     /* Delete all remaining zones. */
     if (rt->gcInitialized) {
         for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
             for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
                 js_delete(comp.get());
             js_delete(zone.get());
@@ -5003,16 +5003,42 @@ GCRuntime::minorGC(JSContext *cx, JS::gc
         if (pretenureTypes[i]->canPreTenure())
             pretenureTypes[i]->setShouldPreTenure(cx);
     }
     JS_ASSERT_IF(!rt->mainThread.suppressGC, nursery.isEmpty());
 #endif
 }
 
 void
+GCRuntime::disableGenerationalGC()
+{
+#ifdef JSGC_GENERATIONAL
+    if (isGenerationalGCEnabled()) {
+        minorGC(JS::gcreason::API);
+        nursery.disable();
+        storeBuffer.disable();
+    }
+#endif
+    ++rt->gc.generationalDisabled;
+}
+
+void
+GCRuntime::enableGenerationalGC()
+{
+    JS_ASSERT(generationalDisabled > 0);
+    --generationalDisabled;
+#ifdef JSGC_GENERATIONAL
+    if (generationalDisabled == 0) {
+        nursery.enable();
+        storeBuffer.enable();
+    }
+#endif
+}
+
+void
 js::gc::GCIfNeeded(JSContext *cx)
 {
     cx->runtime()->gc.gcIfNeeded(cx);
 }
 
 void
 GCRuntime::gcIfNeeded(JSContext *cx)
 {
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -306,17 +306,17 @@ class ZoneCellIterUnderGC : public ZoneC
 #ifdef JSGC_GENERATIONAL
         JS_ASSERT(zone->runtimeFromAnyThread()->gc.nursery.isEmpty());
 #endif
         JS_ASSERT(zone->runtimeFromAnyThread()->isHeapBusy());
         init(zone, kind);
     }
 };
 
-/* In debug builds, assert that no allocation occurs while it is live. */
+/* In debug builds, assert that no allocation occurs. */
 class AutoAssertNoAlloc
 {
 #ifdef JS_DEBUG
     GCRuntime *gc;
 
   public:
     AutoAssertNoAlloc() : gc(nullptr) {}
     AutoAssertNoAlloc(JSRuntime *rt) : gc(nullptr) {
@@ -330,16 +330,17 @@ class AutoAssertNoAlloc
     ~AutoAssertNoAlloc() {
         if (gc)
             gc->allowAlloc();
     }
 #else
   public:
     AutoAssertNoAlloc() {}
     AutoAssertNoAlloc(JSRuntime *) {}
+    void disallowAlloc(JSRuntime *rt) {}
 #endif
 };
 
 class ZoneCellIter : public ZoneCellIterImpl
 {
     AutoAssertNoAlloc noAlloc;
     ArenaLists *lists;
     AllocKind kind;