Bug 975947 - Fix generational GC's interaction with the barrier verifier; r=sfink
authorTerrence Cole <terrence@mozilla.com>
Mon, 24 Feb 2014 15:08:05 -0800
changeset 170783 a278b0807420dabbe59b6785a8fee2c734320f94
parent 170782 f0516583976066555ba08caf87e4f25af7455e18
child 170784 7840db3e63c2c6eaa77ae60bdac6483d9f660936
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewerssfink
bugs975947
milestone30.0a1
Bug 975947 - Fix generational GC's interaction with the barrier verifier; r=sfink
js/public/GCAPI.h
js/src/gc/GCInternals.h
js/src/gc/RootMarking.cpp
js/src/gc/Verifier.cpp
js/src/jsfriendapi.cpp
js/src/shell/js.cpp
js/src/shell/jsheaptools.cpp
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -302,47 +302,32 @@ IncrementalObjectBarrier(JSObject *obj);
  * Returns true if the most recent GC ran incrementally.
  */
 extern JS_FRIEND_API(bool)
 WasIncrementalGC(JSRuntime *rt);
 
 /*
  * Generational GC:
  *
- * Note: Generational GC is not yet enabled by default. The following functions
- *       are non-functional unless SpiderMonkey was configured with
+ * Note: Generational GC is not yet enabled by default. The following class
+ *       is non-functional unless SpiderMonkey was configured with
  *       --enable-gcgenerational.
  */
 
-/*
- * Generational GC is enabled by default. Use this method to disable it.
- */
-extern JS_FRIEND_API(void)
-DisableGenerationalGC(JSRuntime *rt);
-
-/*
- * Generational GC may be re-enabled at runtime.
- */
-extern JS_FRIEND_API(void)
-EnableGenerationalGC(JSRuntime *rt);
-
 /* Ensure that generational GC is disabled within some scope. */
 class JS_FRIEND_API(AutoDisableGenerationalGC)
 {
     JSRuntime *runtime;
+#ifdef JS_GC_ZEAL
+    bool restartVerifier;
+#endif
 
   public:
-    AutoDisableGenerationalGC(JSRuntime *rt)
-      : runtime(rt)
-    {
-        DisableGenerationalGC(rt);
-    }
-    ~AutoDisableGenerationalGC() {
-        EnableGenerationalGC(runtime);
-    }
+    AutoDisableGenerationalGC(JSRuntime *rt);
+    ~AutoDisableGenerationalGC();
 };
 
 /*
  * Returns true if generational allocation and collection is currently enabled
  * on the given runtime.
  */
 extern JS_FRIEND_API(bool)
 IsGenerationalGCEnabled(JSRuntime *rt);
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -121,17 +121,17 @@ class AutoStopVerifyingBarriers
 {
     JSRuntime *runtime;
     bool restartPreVerifier;
     bool restartPostVerifier;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     AutoStopVerifyingBarriers(JSRuntime *rt, bool isShutdown
-                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : runtime(rt)
     {
         restartPreVerifier = !isShutdown && rt->gcVerifyPreData;
         restartPostVerifier = !isShutdown && rt->gcVerifyPostData && JS::IsGenerationalGCEnabled(rt);
         if (rt->gcVerifyPreData)
             EndVerifyPreBarriers(rt);
         if (rt->gcVerifyPostData)
             EndVerifyPostBarriers(rt);
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -258,26 +258,26 @@ MarkWordConservatively(JSTracer *trc, ui
      */
 #ifdef MOZ_VALGRIND
     JS_SILENCE_UNUSED_VALUE_IN_EXPR(VALGRIND_MAKE_MEM_DEFINED(&w, sizeof(w)));
 #endif
 
     MarkIfGCThingWord(trc, w);
 }
 
+#ifndef JSGC_USE_EXACT_ROOTING
 MOZ_ASAN_BLACKLIST
 static void
 MarkRangeConservatively(JSTracer *trc, const uintptr_t *begin, const uintptr_t *end)
 {
     JS_ASSERT(begin <= end);
     for (const uintptr_t *i = begin; i < end; ++i)
         MarkWordConservatively(trc, *i);
 }
 
-#ifndef JSGC_USE_EXACT_ROOTING
 static void
 MarkRangeConservativelyAndSkipIon(JSTracer *trc, JSRuntime *rt, const uintptr_t *begin, const uintptr_t *end)
 {
     const uintptr_t *i = begin;
 
 #if JS_STACK_GROWTH_DIRECTION < 0 && defined(JS_ION) && !defined(JS_ARM_SIMULATOR)
     // Walk only regions in between JIT activations. Note that non-volatile
     // registers are spilled to the stack before the entry frame, ensuring
@@ -338,18 +338,16 @@ MarkConservativeStackRoots(JSTracer *trc
 #endif
 
     JS_ASSERT(stackMin <= stackEnd);
     MarkRangeConservativelyAndSkipIon(trc, rt, stackMin, stackEnd);
     MarkRangeConservatively(trc, cgcd->registerSnapshot.words,
                             ArrayEnd(cgcd->registerSnapshot.words));
 }
 
-#endif /* JSGC_USE_EXACT_ROOTING */
-
 void
 js::MarkStackRangeConservatively(JSTracer *trc, Value *beginv, Value *endv)
 {
     const uintptr_t *begin = beginv->payloadUIntPtr();
     const uintptr_t *end = endv->payloadUIntPtr();
 #ifdef JS_NUNBOX32
     /*
      * With 64-bit jsvals on 32-bit systems, we can optimize a bit by
@@ -358,16 +356,18 @@ js::MarkStackRangeConservatively(JSTrace
     JS_ASSERT(begin <= end);
     for (const uintptr_t *i = begin; i < end; i += sizeof(Value) / sizeof(uintptr_t))
         MarkWordConservatively(trc, *i);
 #else
     MarkRangeConservatively(trc, begin, end);
 #endif
 }
 
+#endif /* JSGC_USE_EXACT_ROOTING */
+
 MOZ_NEVER_INLINE void
 ConservativeGCData::recordStackTop()
 {
     /* Update the native stack pointer if it points to a bigger stack. */
     uintptr_t dummy;
     nativeStackTop = &dummy;
 
     /*
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -363,36 +363,35 @@ typedef HashMap<void *, VerifyNode *, De
  * for the graph. If we run out of memory (i.e., if edgeptr goes beyond term),
  * we just abandon the verification.
  *
  * The nodemap field is a hashtable that maps from the address of the GC thing
  * to the VerifyNode that represents it.
  */
 struct VerifyPreTracer : JSTracer
 {
+    JS::AutoDisableGenerationalGC noggc;
+
     /* The gcNumber when the verification began. */
     uint64_t number;
 
     /* This counts up to gcZealFrequency to decide whether to verify. */
     int count;
 
     /* This graph represents the initial GC "snapshot". */
     VerifyNode *curnode;
     VerifyNode *root;
     char *edgeptr;
     char *term;
     NodeMap nodemap;
 
-    VerifyPreTracer(JSRuntime *rt) : root(nullptr) {
-        JS::DisableGenerationalGC(rt);
-    }
+    VerifyPreTracer(JSRuntime *rt) : noggc(rt), root(nullptr) {}
 
     ~VerifyPreTracer() {
         js_free(root);
-        JS::EnableGenerationalGC(runtime);
     }
 };
 
 /*
  * This function builds up the heap snapshot by adding edges to the current
  * node.
  */
 static void
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -925,38 +925,48 @@ JS::IsIncrementalGCInProgress(JSRuntime 
 }
 
 JS_FRIEND_API(void)
 JS::DisableIncrementalGC(JSRuntime *rt)
 {
     rt->gcIncrementalEnabled = false;
 }
 
-extern JS_FRIEND_API(void)
-JS::DisableGenerationalGC(JSRuntime *rt)
+JS::AutoDisableGenerationalGC::AutoDisableGenerationalGC(JSRuntime *rt)
+  : runtime(rt)
+#ifdef JS_GC_ZEAL
+  , restartVerifier(rt->gcVerifyPostData)
+#endif
 {
 #ifdef JSGC_GENERATIONAL
     if (IsGenerationalGCEnabled(rt)) {
+#ifdef JS_GC_ZEAL
+        if (restartVerifier)
+            gc::EndVerifyPostBarriers(rt);
+#endif
         MinorGC(rt, JS::gcreason::API);
         rt->gcNursery.disable();
         rt->gcStoreBuffer.disable();
     }
 #endif
     ++rt->gcGenerationalDisabled;
 }
 
-extern JS_FRIEND_API(void)
-JS::EnableGenerationalGC(JSRuntime *rt)
+JS::AutoDisableGenerationalGC::~AutoDisableGenerationalGC()
 {
-    JS_ASSERT(rt->gcGenerationalDisabled > 0);
-    --rt->gcGenerationalDisabled;
+    JS_ASSERT(runtime->gcGenerationalDisabled > 0);
+    --runtime->gcGenerationalDisabled;
 #ifdef JSGC_GENERATIONAL
-    if (IsGenerationalGCEnabled(rt)) {
-        rt->gcNursery.enable();
-        rt->gcStoreBuffer.enable();
+    if (runtime->gcGenerationalDisabled == 0) {
+        runtime->gcNursery.enable();
+        runtime->gcStoreBuffer.enable();
+#ifdef JS_GC_ZEAL
+        if (restartVerifier)
+            gc::StartVerifyPostBarriers(runtime);
+#endif
     }
 #endif
 }
 
 extern JS_FRIEND_API(bool)
 JS::IsGenerationalGCEnabled(JSRuntime *rt)
 {
     return rt->gcGenerationalDisabled == 0;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -6198,18 +6198,19 @@ main(int argc, char **argv, char **envp)
     if (!rt)
         return 1;
     gTimeoutFunc = NullValue();
     if (!JS_AddNamedValueRootRT(rt, &gTimeoutFunc, "gTimeoutFunc"))
         return 1;
 
     JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff);
 #ifdef JSGC_GENERATIONAL
+    Maybe<JS::AutoDisableGenerationalGC> noggc;
     if (op.getBoolOption("no-ggc"))
-        JS::DisableGenerationalGC(rt);
+        noggc.construct(rt);
 #endif
 
     size_t availMem = op.getIntOption("available-memory");
     if (availMem > 0)
         JS_SetGCParametersBasedOnAvailableMemory(rt, availMem);
 
     /* Set the initial counter to 1 so the principal will never be destroyed. */
     JSPrincipals shellTrustedPrincipals;
--- a/js/src/shell/jsheaptools.cpp
+++ b/js/src/shell/jsheaptools.cpp
@@ -150,33 +150,31 @@ class HeapReverser : public JSTracer, pu
      * structures describing their incoming edges.
      */
     typedef HashMap<void *, Node, DefaultHasher<void *>, SystemAllocPolicy> Map;
     Map map;
 
     /* Construct a HeapReverser for |context|'s heap. */
     HeapReverser(JSContext *cx)
       : JS::CustomAutoRooter(cx),
+        noggc(JS_GetRuntime(cx)),
         runtime(JS_GetRuntime(cx)),
         parent(nullptr)
     {
         JS_TracerInit(this, runtime, traverseEdgeWithThis);
-        JS::DisableGenerationalGC(runtime);
-    }
-
-    ~HeapReverser() {
-        JS::EnableGenerationalGC(runtime);
     }
 
     bool init() { return map.init(); }
 
     /* Build a reversed map of the heap in |map|. */
     bool reverseHeap();
 
   private:
+    JS::AutoDisableGenerationalGC noggc;
+
     /* A runtime pointer for use by the destructor. */
     JSRuntime *runtime;
 
     /*
      * Return the name of the most recent edge this JSTracer has traversed. The
      * result is allocated with malloc; if we run out of memory, raise an error
      * in this HeapReverser's context and return nullptr.
      *