Bug 1149526 - Rework HeapPtr lifetime checks using TLS r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 09 Apr 2015 18:08:54 +0100
changeset 257323 909e17a7edb74332a40d25c7b666a454786b4405
parent 257322 e1fc8574a62e864d5e2a0c605f0fe621bbfbfd88
child 257324 3629ce36619109fae7be228a6df9658f0a1ab40d
push id8007
push userraliiev@mozilla.com
push dateMon, 11 May 2015 19:23:16 +0000
treeherdermozilla-aurora@e2ce1aac996e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1149526
milestone40.0a1
Bug 1149526 - Rework HeapPtr lifetime checks using TLS r=terrence
js/src/asmjs/AsmJSModule.h
js/src/gc/Barrier.cpp
js/src/gc/Barrier.h
js/src/gc/GCInternals.h
js/src/gc/Zone.cpp
js/src/gc/Zone.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsweakmap.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -843,17 +843,17 @@ class AsmJSModule
     ScriptSource *                        scriptSource_;
     PropertyName *                        globalArgumentName_;
     PropertyName *                        importArgumentName_;
     PropertyName *                        bufferArgumentName_;
     uint8_t *                             code_;
     uint8_t *                             interruptExit_;
     uint8_t *                             outOfBoundsExit_;
     StaticLinkData                        staticLinkData_;
-    HeapPtrArrayBufferObjectMaybeShared   maybeHeap_;
+    RelocatablePtrArrayBufferObjectMaybeShared maybeHeap_;
     AsmJSModule **                        prevLinked_;
     AsmJSModule *                         nextLinked_;
     bool                                  dynamicallyLinked_;
     bool                                  loadedFromCache_;
     bool                                  profilingEnabled_;
     bool                                  interrupted_;
 
     void restoreHeapToInitialState(ArrayBufferObjectMaybeShared* maybePrevBuffer);
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -62,63 +62,20 @@ RuntimeFromMainThreadIsHeapMajorCollecti
 }
 
 bool
 CurrentThreadIsIonCompiling()
 {
     return TlsPerThreadData.get()->ionCompiling;
 }
 
-static bool
-GCIsSweepingOnMainThread(JSRuntime* rt, Zone* zone)
-{
-    return rt->isHeapMajorCollecting() && rt->gc.state() == SWEEP &&
-        (zone->isGCSweeping() || rt->isAtomsZone(zone));
-}
-
-static bool
-GCIsSweepingOnBackgroundThread(JSRuntime* rt, Zone* zone)
-{
-    return rt->gc.isBackgroundSweeping() &&
-        (zone->isGCBackgroundSweeping() || rt->isAtomsZone(zone));
-}
-
-static bool
-ThingMayHaveDifferentRuntime(TenuredCell* cell)
+bool
+CurrentThreadIsGCSweeping()
 {
-    // Some GC things may be associated with another runtime.
-    AllocKind kind = cell->getAllocKind();
-    if (kind == AllocKind::STRING)
-        return static_cast<const JSString*>(cell)->isPermanentAtom();
-    else if (kind == AllocKind::SYMBOL)
-        return static_cast<const JS::Symbol*>(cell)->isWellKnownSymbol();
-
-    return false;
-}
-
-void
-CheckGCIsSweepingZone(gc::Cell* cell)
-{
-    MOZ_ASSERT(!IsInsideNursery(cell));
-    TenuredCell* tenured = &cell->asTenured();
-    if (ThingMayHaveDifferentRuntime(tenured))
-        return;
-
-    Zone* zone = tenured->zoneFromAnyThread();
-    JSRuntime* rt = zone->runtimeFromAnyThread();
-    if (CurrentThreadCanAccessRuntime(rt)) {
-        // We're on the main thread.
-        MOZ_ASSERT(GCIsSweepingOnMainThread(rt, zone));
-    } else {
-        // We're not on the main thread, so we're either on a helper thread run
-        // while the GC is active on the main thread or we are background
-        // sweeping.
-        MOZ_ASSERT(GCIsSweepingOnMainThread(rt, zone) ||
-                   GCIsSweepingOnBackgroundThread(rt, zone));
-    }
+    return js::TlsPerThreadData.get()->gcSweeping;
 }
 
 #endif // DEBUG
 
 bool
 StringIsPermanentAtom(JSString* str)
 {
     return str->isPermanentAtom();
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -187,16 +187,19 @@ namespace jit {
 class JitCode;
 }
 
 #ifdef DEBUG
 // Barriers can't be triggered during backend Ion compilation, which may run on
 // a helper thread.
 bool
 CurrentThreadIsIonCompiling();
+
+bool
+CurrentThreadIsGCSweeping();
 #endif
 
 bool
 StringIsPermanentAtom(JSString* str);
 
 namespace gc {
 
 template <typename T> struct MapTypeToTraceKind {};
@@ -282,44 +285,32 @@ ZoneOfIdFromAnyThread(const jsid& id)
 {
     MOZ_ASSERT(JSID_IS_GCTHING(id));
     return js::gc::TenuredCell::fromPointer(JSID_TO_GCTHING(id).asCell())->zoneFromAnyThread();
 }
 
 void
 ValueReadBarrier(const Value& value);
 
-#ifdef DEBUG
-void
-CheckGCIsSweepingZone(gc::Cell* cell);
-#endif
-
 template <typename T>
 struct InternalGCMethods {};
 
 template <typename T>
 struct InternalGCMethods<T*>
 {
     static bool isMarkable(T* v) { return v != nullptr; }
 
     static void preBarrier(T* v) { T::writeBarrierPre(v); }
     static void preBarrier(Zone* zone, T* v) { T::writeBarrierPre(zone, v); }
 
     static void postBarrier(T** vp) { T::writeBarrierPost(*vp, vp); }
     static void postBarrierRelocate(T** vp) { T::writeBarrierPostRelocate(*vp, vp); }
     static void postBarrierRemove(T** vp) { T::writeBarrierPostRemove(*vp, vp); }
 
     static void readBarrier(T* v) { T::readBarrier(v); }
-
-#ifdef DEBUG
-    static void checkGCIsSweeping(T* v) {
-        if (v)
-            CheckGCIsSweepingZone(v);
-    }
-#endif
 };
 
 template <>
 struct InternalGCMethods<Value>
 {
     static JSRuntime* runtimeFromAnyThread(const Value& v) {
         MOZ_ASSERT(v.isMarkable());
         return static_cast<js::gc::Cell*>(v.toGCThing())->runtimeFromAnyThread();
@@ -387,23 +378,16 @@ struct InternalGCMethods<Value>
         MOZ_ASSERT(vp->isMarkable());
         MOZ_ASSERT(!CurrentThreadIsIonCompiling());
         JSRuntime* rt = static_cast<js::gc::Cell*>(vp->toGCThing())->runtimeFromAnyThread();
         JS::shadow::Runtime* shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
         shadowRuntime->gcStoreBufferPtr()->removeRelocatableValueFromAnyThread(vp);
     }
 
     static void readBarrier(const Value& v) { ValueReadBarrier(v); }
-
-#ifdef DEBUG
-    static void checkGCIsSweeping(const Value& v) {
-        if (v.isMarkable())
-            CheckGCIsSweepingZone(v.toGCThing());
-    }
-#endif
 };
 
 template <>
 struct InternalGCMethods<jsid>
 {
     static bool isMarkable(jsid id) { return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id); }
 
     static void preBarrier(jsid id) {
@@ -417,23 +401,16 @@ struct InternalGCMethods<jsid>
             }
         }
     }
     static void preBarrier(Zone* zone, jsid id) { preBarrier(id); }
 
     static void postBarrier(jsid* idp) {}
     static void postBarrierRelocate(jsid* idp) {}
     static void postBarrierRemove(jsid* idp) {}
-
-#ifdef DEBUG
-    static void checkGCIsSweeping(jsid id) {
-        if (JSID_IS_GCTHING(id))
-            CheckGCIsSweepingZone(JSID_TO_GCTHING(id).asCell());
-    }
-#endif
 };
 
 template <typename T>
 class BarrieredBaseMixins {};
 
 /*
  * Base class for barriered pointer types.
  */
@@ -467,20 +444,16 @@ class BarrieredBase : public BarrieredBa
 
     /* For users who need to manually barrier the raw types. */
     static void writeBarrierPre(const T& v) { InternalGCMethods<T>::preBarrier(v); }
     static void writeBarrierPost(const T& v, T* vp) { InternalGCMethods<T>::postBarrier(vp); }
 
   protected:
     void pre() { InternalGCMethods<T>::preBarrier(value); }
     void pre(Zone* zone) { InternalGCMethods<T>::preBarrier(zone, value); }
-
-#ifdef DEBUG
-    void checkGCIsSweeping() { InternalGCMethods<T>::checkGCIsSweeping(value); }
-#endif
 };
 
 template <>
 class BarrieredBaseMixins<JS::Value> : public ValueOperations<BarrieredBase<JS::Value> >
 {
     friend class ValueOperations<BarrieredBase<JS::Value> >;
     const JS::Value * extract() const {
         return static_cast<const BarrieredBase<JS::Value>*>(this)->unsafeGet();
@@ -537,36 +510,31 @@ class PreBarriered : public BarrieredBas
 template <class T>
 class HeapPtr : public BarrieredBase<T>
 {
   public:
     HeapPtr() : BarrieredBase<T>(GCMethods<T>::initial()) {}
     explicit HeapPtr(T v) : BarrieredBase<T>(v) { post(); }
     explicit HeapPtr(const HeapPtr<T>& v) : BarrieredBase<T>(v) { post(); }
 #ifdef DEBUG
-    ~HeapPtr() { this->checkGCIsSweeping(); }
+    ~HeapPtr() {
+        MOZ_ASSERT(CurrentThreadIsGCSweeping());
+    }
 #endif
 
     void init(T v) {
         this->value = v;
         post();
     }
 
     DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T);
 
   protected:
     void post() { InternalGCMethods<T>::postBarrier(&this->value); }
 
-    /* Make this friend so it can access pre() and post(). */
-    template <class T1, class T2>
-    friend inline void
-    BarrieredSetPair(Zone* zone,
-                     HeapPtr<T1*>& v1, T1* val1,
-                     HeapPtr<T2*>& v2, T2* val2);
-
   private:
     void set(const T& v) {
         this->pre();
         this->value = v;
         post();
     }
 
     /*
@@ -686,36 +654,16 @@ class RelocatablePtr : public BarrieredB
 
 /*
  * This is a hack for RegExpStatics::updateFromMatch. It allows us to do two
  * barriers with only one branch to check if we're in an incremental GC.
  */
 template <class T1, class T2>
 static inline void
 BarrieredSetPair(Zone* zone,
-                 HeapPtr<T1*>& v1, T1* val1,
-                 HeapPtr<T2*>& v2, T2* val2)
-{
-    if (T1::needWriteBarrierPre(zone)) {
-        v1.pre();
-        v2.pre();
-    }
-    v1.unsafeSet(val1);
-    v2.unsafeSet(val2);
-    v1.post();
-    v2.post();
-}
-
-/*
- * This is a hack for RegExpStatics::updateFromMatch. It allows us to do two
- * barriers with only one branch to check if we're in an incremental GC.
- */
-template <class T1, class T2>
-static inline void
-BarrieredSetPair(Zone* zone,
                  RelocatablePtr<T1*>& v1, T1* val1,
                  RelocatablePtr<T2*>& v2, T2* val2)
 {
     if (T1::needWriteBarrierPre(zone)) {
         v1.pre();
         v2.pre();
     }
     v1.postBarrieredSet(val1);
@@ -836,16 +784,17 @@ typedef RelocatablePtr<JSScript*> Reloca
 typedef RelocatablePtr<NativeObject*> RelocatablePtrNativeObject;
 typedef RelocatablePtr<NestedScopeObject*> RelocatablePtrNestedScopeObject;
 typedef RelocatablePtr<Shape*> RelocatablePtrShape;
 typedef RelocatablePtr<ObjectGroup*> RelocatablePtrObjectGroup;
 typedef RelocatablePtr<jit::JitCode*> RelocatablePtrJitCode;
 typedef RelocatablePtr<JSLinearString*> RelocatablePtrLinearString;
 typedef RelocatablePtr<JSString*> RelocatablePtrString;
 typedef RelocatablePtr<JSAtom*> RelocatablePtrAtom;
+typedef RelocatablePtr<ArrayBufferObjectMaybeShared*> RelocatablePtrArrayBufferObjectMaybeShared;
 
 typedef HeapPtr<NativeObject*> HeapPtrNativeObject;
 typedef HeapPtr<ArrayObject*> HeapPtrArrayObject;
 typedef HeapPtr<ArrayBufferObjectMaybeShared*> HeapPtrArrayBufferObjectMaybeShared;
 typedef HeapPtr<ArrayBufferObject*> HeapPtrArrayBufferObject;
 typedef HeapPtr<BaseShape*> HeapPtrBaseShape;
 typedef HeapPtr<JSAtom*> HeapPtrAtom;
 typedef HeapPtr<JSFlatString*> HeapPtrFlatString;
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -167,12 +167,37 @@ class AutoMaybeStartBackgroundAllocation
     }
 
     ~AutoMaybeStartBackgroundAllocation() {
         if (runtime)
             runtime->gc.startBackgroundAllocTaskIfIdle();
     }
 };
 
+// In debug builds, set/unset the GC sweeping flag for the current thread.
+struct AutoSetThreadIsSweeping
+{
+#ifdef DEBUG
+    explicit AutoSetThreadIsSweeping(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
+      : threadData_(js::TlsPerThreadData.get())
+    {
+        MOZ_ASSERT(!threadData_->gcSweeping);
+        threadData_->gcSweeping = true;
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    ~AutoSetThreadIsSweeping() {
+        MOZ_ASSERT(threadData_->gcSweeping);
+        threadData_->gcSweeping = false;
+    }
+
+  private:
+    PerThreadData* threadData_;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+#else
+    AutoSetThreadIsSweeping() {}
+#endif
+};
+
 } /* namespace gc */
 } /* namespace js */
 
 #endif /* gc_GCInternals_h */
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -34,19 +34,16 @@ JS::Zone::Zone(JSRuntime* rt)
     data(nullptr),
     isSystem(false),
     usedByExclusiveThread(false),
     active(false),
     jitZone_(nullptr),
     gcState_(NoGC),
     gcScheduled_(false),
     gcPreserveCode_(false),
-#ifdef DEBUG
-    gcBackgroundSweeping_(false),
-#endif
     jitUsingBarriers_(false),
     listNext_(NotOnList)
 {
     /* Ensure that there are no vtables to mess us up here. */
     MOZ_ASSERT(reinterpret_cast<JS::shadow::Zone*>(this) ==
                static_cast<JS::shadow::Zone*>(this));
 
     threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables, rt->gc.schedulingState);
@@ -268,25 +265,16 @@ Zone::notifyObservingDebuggers()
         if (!dbgs)
             continue;
 
         for (GlobalObject::DebuggerVector::Range r = dbgs->all(); !r.empty(); r.popFront())
             r.front()->debuggeeIsBeingCollected();
     }
 }
 
-#ifdef DEBUG
-void
-Zone::setGCBackgroundSweeping(bool newState)
-{
-    MOZ_ASSERT(gcBackgroundSweeping_ != newState);
-    gcBackgroundSweeping_ = newState;
-}
-#endif
-
 JS::Zone*
 js::ZoneOfValue(const JS::Value& value)
 {
     MOZ_ASSERT(value.isMarkable());
     if (value.isObject())
         return value.toObject().zone();
     return js::gc::TenuredCell::fromPointer(value.toGCThing())->zone();
 }
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -221,19 +221,16 @@ struct Zone : public JS::shadow::Zone,
 
     js::jit::JitZone* getJitZone(JSContext* cx) { return jitZone_ ? jitZone_ : createJitZone(cx); }
     js::jit::JitZone* jitZone() { return jitZone_; }
 
 #ifdef DEBUG
     // For testing purposes, return the index of the zone group which this zone
     // was swept in in the last GC.
     unsigned lastZoneGroupIndex() { return gcLastZoneGroupIndex; }
-
-    void setGCBackgroundSweeping(bool newState);
-    bool isGCBackgroundSweeping() { return gcBackgroundSweeping_; }
 #endif
 
   private:
     void sweepBreakpoints(js::FreeOp* fop);
     void sweepCompartments(js::FreeOp* fop, bool keepAtleastOne, bool lastGC);
 
     js::jit::JitZone* createJitZone(JSContext* cx);
 
@@ -298,19 +295,16 @@ struct Zone : public JS::shadow::Zone,
     mozilla::DebugOnly<unsigned> gcLastZoneGroupIndex;
 
   private:
     js::jit::JitZone* jitZone_;
 
     GCState gcState_;
     bool gcScheduled_;
     bool gcPreserveCode_;
-#ifdef DEBUG
-    bool gcBackgroundSweeping_;
-#endif
     bool jitUsingBarriers_;
 
     // Allow zones to be linked into a list
     friend class js::gc::ZoneList;
     static Zone * const NotOnList;
     Zone* listNext_;
     bool isOnList() const;
     Zone* nextZone() const;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3203,51 +3203,29 @@ GCRuntime::expireChunksAndArenas(bool sh
         AutoUnlockGC unlock(lock);
         FreeChunkPool(rt, toFree);
     }
 
     if (shouldShrink)
         decommitArenas(lock);
 }
 
-// In debug builds, set/unset the background sweeping flag on the zone.
-struct AutoSetZoneBackgroundSweeping
-{
-#ifdef DEBUG
-    explicit AutoSetZoneBackgroundSweeping(Zone* zone)
-      : zone_(zone)
-    {
-        zone_->setGCBackgroundSweeping(true);
-    }
-
-    ~AutoSetZoneBackgroundSweeping() {
-        zone_->setGCBackgroundSweeping(false);
-    }
-
-  private:
-    Zone* zone_;
-#else
-    AutoSetZoneBackgroundSweeping(Zone* zone) {}
-#endif
-};
-
 void
 GCRuntime::sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks, ThreadType threadType)
 {
     freeBlocks.freeAll();
 
     if (zones.isEmpty())
         return;
 
     // We must finalize thing kinds in the order specified by BackgroundFinalizePhases.
     ArenaHeader* emptyArenas = nullptr;
     FreeOp fop(rt, threadType);
     for (unsigned phase = 0 ; phase < ArrayLength(BackgroundFinalizePhases) ; ++phase) {
         for (Zone* zone = zones.front(); zone; zone = zone->nextZone()) {
-            AutoSetZoneBackgroundSweeping zbs(zone);
             for (unsigned index = 0 ; index < BackgroundFinalizePhases[phase].length ; ++index) {
                 AllocKind kind = BackgroundFinalizePhases[phase].kinds[index];
                 ArenaHeader* arenas = zone->arenas.arenaListsToSweep[kind];
                 if (arenas)
                     ArenaLists::backgroundFinalize(&fop, arenas, &emptyArenas);
             }
         }
     }
@@ -3477,16 +3455,18 @@ void
 GCHelperState::doSweep(AutoLockGC& lock)
 {
     // The main thread may call queueZonesForBackgroundSweep() or
     // ShrinkGCBuffers() while this is running so we must check there is no more
     // work to do before exiting.
 
     do {
         while (!rt->gc.backgroundSweepZones.isEmpty()) {
+            AutoSetThreadIsSweeping threadIsSweeping;
+
             ZoneList zones;
             zones.transferFrom(rt->gc.backgroundSweepZones);
             LifoAlloc freeLifoAlloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
             freeLifoAlloc.transferFrom(&rt->gc.freeLifoAlloc);
 
             AutoUnlockGC unlock(lock);
             rt->gc.sweepBackgroundThings(zones, freeLifoAlloc, BackgroundThread);
         }
@@ -4715,31 +4695,43 @@ GCRuntime::endMarkingZoneGroup()
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
         MOZ_ASSERT(zone->isGCMarkingGray());
         zone->setGCState(Zone::Mark);
     }
     MOZ_ASSERT(marker.isDrained());
     marker.setMarkColorBlack();
 }
 
-#define MAKE_GC_PARALLEL_TASK(name) \
-    class name : public GCParallelTask {\
-        JSRuntime* runtime;\
-        virtual void run() override;\
-      public:\
-        explicit name (JSRuntime* rt) : runtime(rt) {}\
-    }
-MAKE_GC_PARALLEL_TASK(SweepAtomsTask);
-MAKE_GC_PARALLEL_TASK(SweepInnerViewsTask);
-MAKE_GC_PARALLEL_TASK(SweepCCWrappersTask);
-MAKE_GC_PARALLEL_TASK(SweepBaseShapesTask);
-MAKE_GC_PARALLEL_TASK(SweepInitialShapesTask);
-MAKE_GC_PARALLEL_TASK(SweepObjectGroupsTask);
-MAKE_GC_PARALLEL_TASK(SweepRegExpsTask);
-MAKE_GC_PARALLEL_TASK(SweepMiscTask);
+class GCSweepTask : public GCParallelTask
+{
+    virtual void runFromHelperThread() override {
+        AutoSetThreadIsSweeping threadIsSweeping;
+        GCParallelTask::runFromHelperThread();
+    }
+  protected:
+    JSRuntime* runtime;
+  public:
+    explicit GCSweepTask(JSRuntime* rt) : runtime(rt) {}
+};
+
+#define MAKE_GC_SWEEP_TASK(name)                                              \
+    class name : public GCSweepTask {                                         \
+        virtual void run() override;                                          \
+      public:                                                                 \
+        explicit name (JSRuntime* rt) : GCSweepTask(rt) {}                    \
+    }
+MAKE_GC_SWEEP_TASK(SweepAtomsTask);
+MAKE_GC_SWEEP_TASK(SweepInnerViewsTask);
+MAKE_GC_SWEEP_TASK(SweepCCWrappersTask);
+MAKE_GC_SWEEP_TASK(SweepBaseShapesTask);
+MAKE_GC_SWEEP_TASK(SweepInitialShapesTask);
+MAKE_GC_SWEEP_TASK(SweepObjectGroupsTask);
+MAKE_GC_SWEEP_TASK(SweepRegExpsTask);
+MAKE_GC_SWEEP_TASK(SweepMiscTask);
+#undef MAKE_GC_SWEEP_TASK
 
 /* virtual */ void
 SweepAtomsTask::run()
 {
     runtime->sweepAtoms();
 }
 
 /* virtual */ void
@@ -5023,16 +5015,18 @@ GCRuntime::beginSweepPhase(bool destroyi
      *
      * Finalize as we sweep, outside of lock but with rt->isHeapBusy()
      * true so that any attempt to allocate a GC-thing from a finalizer will
      * fail, rather than nest badly and leave the unmarked newborn to be swept.
      */
 
     MOZ_ASSERT(!abortSweepAfterCurrentGroup);
 
+    AutoSetThreadIsSweeping threadIsSweeping;
+
     releaseHeldRelocatedArenas();
 
     computeNonIncrementalMarkingForValidation();
 
     gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP);
 
     sweepOnBackgroundThread =
         !destroyingRuntime && !TraceEnabled() && CanUseExtraThreads();
@@ -5125,16 +5119,18 @@ SweepArenaList(ArenaHeader** arenasToSwe
     }
 
     return true;
 }
 
 GCRuntime::IncrementalProgress
 GCRuntime::sweepPhase(SliceBudget& sliceBudget)
 {
+    AutoSetThreadIsSweeping threadIsSweeping;
+
     gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP);
     FreeOp fop(rt);
 
     if (drainMarkStack(sliceBudget, gcstats::PHASE_SWEEP_MARK) == NotFinished)
         return NotFinished;
 
 
     for (;;) {
@@ -5230,16 +5226,18 @@ GCRuntime::sweepPhase(SliceBudget& slice
         endMarkingZoneGroup();
         beginSweepingZoneGroup();
     }
 }
 
 void
 GCRuntime::endSweepPhase(bool destroyingRuntime)
 {
+    AutoSetThreadIsSweeping threadIsSweeping;
+
     gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP);
     FreeOp fop(rt);
 
     MOZ_ASSERT_IF(destroyingRuntime, !sweepOnBackgroundThread);
 
     /*
      * Recalculate whether GC was full or not as this may have changed due to
      * newly created zones.  Can only change from full to not full.
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1021,17 +1021,17 @@ class GCParallelTask
     }
 
     // Check if a task is actively running.
     bool isRunning() const;
 
     // This should be friended to HelperThread, but cannot be because it
     // would introduce several circular dependencies.
   public:
-    void runFromHelperThread();
+    virtual void runFromHelperThread();
 };
 
 struct GCChunkHasher {
     typedef gc::Chunk* Lookup;
 
     /*
      * Strip zeros for better distribution after multiplying by the golden
      * ratio.
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -87,17 +87,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.
-    HeapPtrObject memberOf;
+    RelocatablePtrObject 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/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -76,16 +76,17 @@ PerThreadData::PerThreadData(JSRuntime* 
 #ifdef JS_TRACE_LOGGING
     traceLogger(nullptr),
 #endif
     autoFlushICache_(nullptr),
     dtoaState(nullptr),
     suppressGC(0),
 #ifdef DEBUG
     ionCompiling(false),
+    gcSweeping(false),
 #endif
     activeCompilations(0)
 {}
 
 PerThreadData::~PerThreadData()
 {
     if (dtoaState)
         DestroyDtoaState(dtoaState);
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -515,16 +515,19 @@ class PerThreadData : public PerThreadDa
      * extremely dangerous and should only be used when in an OOM situation or
      * in non-exposed debugging facilities.
      */
     int32_t suppressGC;
 
 #ifdef DEBUG
     // Whether this thread is actively Ion compiling.
     bool ionCompiling;
+
+    // Whether this thread is currently sweeping GC things.
+    bool gcSweeping;
 #endif
 
     // Number of active bytecode compilation on this thread.
     unsigned activeCompilations;
 
     explicit PerThreadData(JSRuntime* runtime);
     ~PerThreadData();