Bug 650161 - Fix shell test failures caused by compacting GC r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 17 Sep 2014 15:35:11 +0100
changeset 206211 44134599b738e7f22cb9f9741233f8380ca2aef5
parent 206210 9737b23a2790891c6b851646a0e731b9a9e380e8
child 206212 0e6e9d2913800479c5fcc98d2c0fcf1094ac6c8a
push id27516
push userryanvm@gmail.com
push dateFri, 19 Sep 2014 17:54:48 +0000
treeherdermozilla-central@b00bdb144e06 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs650161
milestone35.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 650161 - Fix shell test failures caused by compacting GC r=terrence
js/src/gc/GCInternals.h
js/src/gc/Heap.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsobjinlines.h
js/src/jsweakmap.cpp
js/src/jsweakmap.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -133,17 +133,17 @@ struct AutoStopVerifyingBarriers
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 CheckHashTablesAfterMovingGC(JSRuntime *rt);
 #endif
 
 #ifdef JSGC_COMPACTING
 struct MovingTracer : JSTracer {
-    MovingTracer(JSRuntime *rt) : JSTracer(rt, Visit, TraceWeakMapValues) {}
+    MovingTracer(JSRuntime *rt) : JSTracer(rt, Visit, TraceWeakMapKeysValues) {}
 
     static void Visit(JSTracer *jstrc, void **thingp, JSGCTraceKind kind);
     static void Sweep(JSTracer *jstrc);
     static bool IsMovingTracer(JSTracer *trc) {
         return trc->callback == Visit;
     }
 };
 #endif
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -619,16 +619,18 @@ struct ArenaHeader : public JS::shadow::
 
     inline ArenaHeader *getNextDelayedMarking() const;
     inline void setNextDelayedMarking(ArenaHeader *aheader);
     inline void unsetDelayedMarking();
 
     inline ArenaHeader *getNextAllocDuringSweep() const;
     inline void setNextAllocDuringSweep(ArenaHeader *aheader);
     inline void unsetAllocDuringSweep();
+
+    void unmarkAll();
 };
 
 struct Arena
 {
     /*
      * Layout of an arena:
      * An arena is 4K in size and 4K-aligned. It starts with the ArenaHeader
      * descriptor followed by some pad bytes. The remainder of the arena is
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -450,16 +450,23 @@ ArenaHeader::checkSynchronizedWithFreeLi
     /*
      * Here this arena has free things, FreeList::lists[thingKind] is not
      * empty and also points to this arena. Thus they must be the same.
      */
     JS_ASSERT(freeList->isSameNonEmptySpan(firstSpan));
 }
 #endif
 
+void
+ArenaHeader::unmarkAll()
+{
+    uintptr_t *word = chunk()->bitmap.arenaBits(this);
+    memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t));
+}
+
 /* static */ void
 Arena::staticAsserts()
 {
     static_assert(JS_ARRAY_LENGTH(ThingSizes) == FINALIZE_LIMIT, "We have defined all thing sizes.");
     static_assert(JS_ARRAY_LENGTH(FirstThingOffsets) == FINALIZE_LIMIT, "We have defined all offsets.");
 }
 
 void
@@ -2487,16 +2494,19 @@ GCRuntime::releaseRelocatedArenas(ArenaH
     }
 #endif
 
     unsigned count = 0;
     while (relocatedList) {
         ArenaHeader *aheader = relocatedList;
         relocatedList = relocatedList->next;
 
+        // Clear the mark bits
+        aheader->unmarkAll();
+
         // Mark arena as empty
         AllocKind thingKind = aheader->getAllocKind();
         size_t thingSize = aheader->getThingSize();
         Arena *arena = aheader->getArena();
         FreeSpan fullSpan;
         fullSpan.initFinal(arena->thingsStart(thingKind), arena->thingsEnd() - thingSize, thingSize);
         aheader->setFirstFreeSpan(&fullSpan);
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -690,20 +690,18 @@ class ArenaLists
         return true;
     }
 
     void unmarkAll() {
         for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
             /* The background finalization must have stopped at this point. */
             JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE ||
                       backgroundFinalizeState[i] == BFS_JUST_FINISHED);
-            for (ArenaHeader *aheader = arenaLists[i].head(); aheader; aheader = aheader->next) {
-                uintptr_t *word = aheader->chunk()->bitmap.arenaBits(aheader);
-                memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t));
-            }
+            for (ArenaHeader *aheader = arenaLists[i].head(); aheader; aheader = aheader->next)
+                aheader->unmarkAll();
         }
     }
 
     bool doneBackgroundFinalize(AllocKind kind) const {
         return backgroundFinalizeState[kind] == BFS_DONE ||
                backgroundFinalizeState[kind] == BFS_JUST_FINISHED;
     }
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -658,16 +658,22 @@ JSObject::finish(js::FreeOp *fop)
                 // so that other objects can access these elements while they
                 // are themselves finalized.
                 fop->freeLater(elements);
             }
         } else {
             fop->free_(elements);
         }
     }
+
+    // It's possible that unreachable shapes may be marked whose listp points
+    // into this object. In case this happens, null out the shape's pointer here
+    // so that a moving GC will not try to access the dead object.
+    if (shape_->listp == &shape_)
+        shape_->listp = nullptr;
 }
 
 /* static */ inline bool
 JSObject::hasProperty(JSContext *cx, js::HandleObject obj,
                       js::HandleId id, bool *foundp)
 {
     JS::RootedObject pobj(cx);
     js::RootedShape prop(cx);
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -66,18 +66,22 @@ WeakMapBase::unmarkCompartment(JSCompart
 {
     for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
         m->marked = false;
 }
 
 void
 WeakMapBase::markAll(JSCompartment *c, JSTracer *tracer)
 {
-    for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
-        m->markIteratively(tracer);
+    JS_ASSERT(tracer->eagerlyTraceWeakMaps() != DoNotTraceWeakMaps);
+    for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next) {
+        m->trace(tracer);
+        if (m->memberOf)
+            gc::MarkObject(tracer, &m->memberOf, "memberOf");
+    }
 }
 
 bool
 WeakMapBase::markCompartmentIteratively(JSCompartment *c, JSTracer *tracer)
 {
     bool markedAny = false;
     for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next) {
         if (m->marked && m->markIteratively(tracer))
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -86,17 +86,17 @@ class WeakMapBase {
     virtual void nonMarkingTraceValues(JSTracer *tracer) = 0;
     virtual bool markIteratively(JSTracer *tracer) = 0;
     virtual bool findZoneEdges() = 0;
     virtual void sweep() = 0;
     virtual void traceMappings(WeakMapTracer *tracer) = 0;
     virtual void finish() = 0;
 
     // Object that this weak map is part of, if any.
-    JSObject *memberOf;
+    HeapPtrObject memberOf;
 
     // Compartment that this weak map is part of.
     JSCompartment *compartment;
 
     // Link in a list of all WeakMaps in a compartment, headed by
     // JSCompartment::gcWeakMapList. The last element of the list has nullptr as
     // its next. Maps not in the list have WeakMapNotInList as their next.
     WeakMapBase *next;
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -767,20 +767,23 @@ RegExpCompartment::sweep(JSRuntime *rt)
         // stack is traced but is not in a zone being collected.
         //
         // Because of this we only treat the marked_ bit as a hint, and destroy
         // the RegExpShared if it was accidentally marked earlier but wasn't
         // marked by the current trace.
         bool keep = shared->marked() && !IsStringAboutToBeFinalized(shared->source.unsafeGet());
         for (size_t i = 0; i < ArrayLength(shared->compilationArray); i++) {
             RegExpShared::RegExpCompilation &compilation = shared->compilationArray[i];
-            if (keep && compilation.jitCode)
-                keep = !IsJitCodeAboutToBeFinalized(compilation.jitCode.unsafeGet());
+            if (compilation.jitCode &&
+                IsJitCodeAboutToBeFinalized(compilation.jitCode.unsafeGet()))
+            {
+                keep = false;
+            }
         }
-        if (keep) {
+        if (keep || rt->isHeapCompacting()) {
             shared->clearMarked();
         } else {
             js_delete(shared);
             e.removeFront();
         }
     }
 
     if (matchResultTemplateObject_ &&
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -194,17 +194,17 @@ class RegExpShared
     bool isCompiled() const {
         return isCompiled(Normal, true) || isCompiled(Normal, false)
             || isCompiled(MatchOnly, true) || isCompiled(MatchOnly, false);
     }
 
     void trace(JSTracer *trc);
 
     bool marked() const { return marked_; }
-    void clearMarked() { JS_ASSERT(marked_); marked_ = false; }
+    void clearMarked() { marked_ = false; }
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 };
 
 /*
  * Extend the lifetime of a given RegExpShared to at least the lifetime of
  * the guard object. See Regular Expression comment at the top.
  */