Bug 1332597 - IsMarked should return true for things owned by a different runtime. r=sfink, a=jcristau
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 23 Jan 2017 17:31:23 +0000
changeset 377965 602af8c8550cce482cd53260ccf98e0bc7337449
parent 377964 55d70a7681c3ced07e5d5301f30a15a24b8f301e
child 377966 d4a9c3ed6ed9db6ba9038574c833a7d198ea2385
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink, jcristau
bugs1332597
milestone53.0a2
Bug 1332597 - IsMarked should return true for things owned by a different runtime. r=sfink, a=jcristau
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/jit/Ion.cpp
js/src/jit/IonCode.h
js/src/jit/JitcodeMap.cpp
js/src/jit/JitcodeMap.h
js/src/jsapi-tests/testIntern.cpp
js/src/jsgc.cpp
js/src/jswatchpoint.cpp
js/src/jsweakmap.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/RegExpObject.cpp
js/src/vm/TypeInference.cpp
js/src/vm/TypeInference.h
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -172,19 +172,19 @@ template <> bool ThingIsPermanentAtomOrW
     return name->isPermanent();
 }
 template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JS::Symbol>(JS::Symbol* sym) {
     return sym->isWellKnownSymbol();
 }
 
 template <typename T>
 static inline bool
-IsOwnedByOtherRuntime(JSTracer* trc, T thing)
+IsOwnedByOtherRuntime(JSRuntime* rt, T thing)
 {
-    bool other = thing->runtimeFromAnyThread() != trc->runtime();
+    bool other = thing->runtimeFromAnyThread() != rt;
     MOZ_ASSERT_IF(other,
                   ThingIsPermanentAtomOrWellKnownSymbol(thing) ||
                   thing->zoneFromAnyThread()->isSelfHostingZone());
     return other;
 }
 
 template<typename T>
 void
@@ -205,17 +205,17 @@ js::CheckTracedThing(JSTracer* trc, T* t
         return;
 
     MOZ_ASSERT_IF(!IsMovingTracer(trc) && !trc->isTenuringTracer(), !IsForwarded(thing));
 
     /*
      * Permanent atoms and things in the self-hosting zone are not associated
      * with this runtime, but will be ignored during marking.
      */
-    if (IsOwnedByOtherRuntime(trc, thing))
+    if (IsOwnedByOtherRuntime(trc->runtime(), thing))
         return;
 
     Zone* zone = thing->zoneFromAnyThread();
     JSRuntime* rt = trc->runtime();
 
     MOZ_ASSERT_IF(!IsMovingTracer(trc), CurrentThreadCanAccessZone(zone));
     MOZ_ASSERT_IF(!IsMovingTracer(trc), CurrentThreadCanAccessRuntime(rt));
 
@@ -749,29 +749,29 @@ GCMarker::markImplicitEdges(T* thing)
 
 } // namespace js
 
 template <typename T>
 static inline bool
 MustSkipMarking(GCMarker* gcmarker, T thing)
 {
     // Don't trace things that are owned by another runtime.
-    if (IsOwnedByOtherRuntime(gcmarker, thing))
+    if (IsOwnedByOtherRuntime(gcmarker->runtime(), thing))
         return true;
 
     // Don't mark things outside a zone if we are in a per-zone GC.
     return !thing->zone()->isGCMarking();
 }
 
 template <>
 bool
 MustSkipMarking<JSObject*>(GCMarker* gcmarker, JSObject* obj)
 {
     // Don't trace things that are owned by another runtime.
-    if (IsOwnedByOtherRuntime(gcmarker, obj))
+    if (IsOwnedByOtherRuntime(gcmarker->runtime(), obj))
         return true;
 
     // We may mark a Nursery thing outside the context of the
     // MinorCollectionTracer because of a pre-barrier. The pre-barrier is not
     // needed in this case because we perform a minor collection before each
     // incremental slice.
     if (IsInsideNursery(obj))
         return true;
@@ -815,17 +815,17 @@ NoteWeakEdge(GCMarker* gcmarker, T** thi
 {
     // Do per-type marking precondition checks.
     if (MustSkipMarking(gcmarker, *thingp))
         return;
 
     CheckTracedThing(gcmarker, *thingp);
 
     // If the target is already marked, there's no need to store the edge.
-    if (IsMarkedUnbarriered(thingp))
+    if (IsMarkedUnbarriered(gcmarker->runtime(), thingp))
         return;
 
     gcmarker->noteWeakEdge(thingp);
 }
 
 template <typename T>
 void
 NoteWeakEdge(GCMarker* gcmarker, T* thingp)
@@ -2690,47 +2690,52 @@ IsMarkedInternalCommon(T* thingp)
         return true;
     if (zone->isGCCompacting() && IsForwarded(*thingp))
         *thingp = Forwarded(*thingp);
     return (*thingp)->asTenured().isMarked();
 }
 
 template <typename T>
 static bool
-IsMarkedInternal(T** thingp)
+IsMarkedInternal(JSRuntime* rt, T** thingp)
 {
+    if (IsOwnedByOtherRuntime(rt, *thingp))
+        return true;
+
     return IsMarkedInternalCommon(thingp);
 }
 
 template <>
 /* static */ bool
-IsMarkedInternal(JSObject** thingp)
+IsMarkedInternal(JSRuntime* rt, JSObject** thingp)
 {
+    if (IsOwnedByOtherRuntime(rt, *thingp))
+        return true;
+
     if (IsInsideNursery(*thingp)) {
-        JSRuntime* rt = (*thingp)->runtimeFromAnyThread();
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
         return rt->gc.nursery.getForwardedPointer(thingp);
     }
     return IsMarkedInternalCommon(thingp);
 }
 
 template <typename S>
 struct IsMarkedFunctor : public IdentityDefaultAdaptor<S> {
-    template <typename T> S operator()(T* t, bool* rv) {
-        *rv = IsMarkedInternal(&t);
+    template <typename T> S operator()(T* t, JSRuntime* rt, bool* rv) {
+        *rv = IsMarkedInternal(rt, &t);
         return js::gc::RewrapTaggedPointer<S, T>::wrap(t);
     }
 };
 
 template <typename T>
 static bool
-IsMarkedInternal(T* thingp)
+IsMarkedInternal(JSRuntime* rt, T* thingp)
 {
     bool rv = true;
-    *thingp = DispatchTyped(IsMarkedFunctor<T>(), *thingp, &rv);
+    *thingp = DispatchTyped(IsMarkedFunctor<T>(), *thingp, rt, &rv);
     return rv;
 }
 
 bool
 js::gc::IsAboutToBeFinalizedDuringSweep(TenuredCell& tenured)
 {
     MOZ_ASSERT(!IsInsideNursery(&tenured));
     MOZ_ASSERT(tenured.zoneFromAnyThread()->isGCSweeping());
@@ -2785,26 +2790,26 @@ IsAboutToBeFinalizedInternal(T* thingp)
     return rv;
 }
 
 namespace js {
 namespace gc {
 
 template <typename T>
 bool
-IsMarkedUnbarriered(T* thingp)
+IsMarkedUnbarriered(JSRuntime* rt, T* thingp)
 {
-    return IsMarkedInternal(ConvertToBase(thingp));
+    return IsMarkedInternal(rt, ConvertToBase(thingp));
 }
 
 template <typename T>
 bool
-IsMarked(WriteBarrieredBase<T>* thingp)
+IsMarked(JSRuntime* rt, WriteBarrieredBase<T>* thingp)
 {
-    return IsMarkedInternal(ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
+    return IsMarkedInternal(rt, ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
 }
 
 template <typename T>
 bool
 IsAboutToBeFinalizedUnbarriered(T* thingp)
 {
     return IsAboutToBeFinalizedInternal(ConvertToBase(thingp));
 }
@@ -2827,18 +2832,18 @@ template <typename T>
 JS_PUBLIC_API(bool)
 EdgeNeedsSweep(JS::Heap<T>* thingp)
 {
     return IsAboutToBeFinalizedInternal(ConvertToBase(thingp->unsafeGet()));
 }
 
 // Instantiate a copy of the Tracing templates for each derived type.
 #define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \
-    template bool IsMarkedUnbarriered<type>(type*); \
-    template bool IsMarked<type>(WriteBarrieredBase<type>*); \
+    template bool IsMarkedUnbarriered<type>(JSRuntime*, type*);                \
+    template bool IsMarked<type>(JSRuntime*, WriteBarrieredBase<type>*); \
     template bool IsAboutToBeFinalizedUnbarriered<type>(type*); \
     template bool IsAboutToBeFinalized<type>(WriteBarrieredBase<type>*); \
     template bool IsAboutToBeFinalized<type>(ReadBarrieredBase<type>*);
 #define INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS(type) \
     template JS_PUBLIC_API(bool) EdgeNeedsSweep<type>(JS::Heap<type>*);
 FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS)
 FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS)
 FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS)
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -372,23 +372,29 @@ namespace gc {
 
 /*** Special Cases ***/
 
 void
 PushArena(GCMarker* gcmarker, Arena* arena);
 
 /*** Liveness ***/
 
+// Report whether a thing has been marked.  Things which are in zones that are
+// not currently being collected or are owned by another runtime are always
+// reported as being marked.
 template <typename T>
 bool
-IsMarkedUnbarriered(T* thingp);
+IsMarkedUnbarriered(JSRuntime* rt, T* thingp);
 
+// Report whether a thing has been marked.  Things which are in zones that are
+// not currently being collected or are owned by another runtime are always
+// reported as being marked.
 template <typename T>
 bool
-IsMarked(WriteBarrieredBase<T>* thingp);
+IsMarked(JSRuntime* rt, WriteBarrieredBase<T>* thingp);
 
 template <typename T>
 bool
 IsAboutToBeFinalizedUnbarriered(T* thingp);
 
 template <typename T>
 bool
 IsAboutToBeFinalized(WriteBarrieredBase<T>* thingp);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -668,26 +668,27 @@ JitCompartment::sweep(FreeOp* fop, JSCom
         baselineCallReturnAddrs_[1] = nullptr;
 
     // Similarly for the ICGetProp_Fallback stub.
     if (!stubCodes_->lookup(ICGetProp_Fallback::Compiler::BASELINE_KEY))
         baselineGetPropReturnAddr_ = nullptr;
     if (!stubCodes_->lookup(ICSetProp_Fallback::Compiler::BASELINE_KEY))
         baselineSetPropReturnAddr_ = nullptr;
 
-    if (stringConcatStub_ && !IsMarkedUnbarriered(&stringConcatStub_))
+    JSRuntime* rt = fop->runtime();
+    if (stringConcatStub_ && !IsMarkedUnbarriered(rt, &stringConcatStub_))
         stringConcatStub_ = nullptr;
 
-    if (regExpMatcherStub_ && !IsMarkedUnbarriered(&regExpMatcherStub_))
+    if (regExpMatcherStub_ && !IsMarkedUnbarriered(rt, &regExpMatcherStub_))
         regExpMatcherStub_ = nullptr;
 
-    if (regExpSearcherStub_ && !IsMarkedUnbarriered(&regExpSearcherStub_))
+    if (regExpSearcherStub_ && !IsMarkedUnbarriered(rt, &regExpSearcherStub_))
         regExpSearcherStub_ = nullptr;
 
-    if (regExpTesterStub_ && !IsMarkedUnbarriered(&regExpTesterStub_))
+    if (regExpTesterStub_ && !IsMarkedUnbarriered(rt, &regExpTesterStub_))
         regExpTesterStub_ = nullptr;
 
     for (ReadBarrieredObject& obj : simdTemplateObjects_) {
         if (obj && IsAboutToBeFinalized(&obj))
             obj.set(nullptr);
     }
 }
 
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -791,17 +791,17 @@ struct AutoFlushICache
     explicit AutoFlushICache(const char* nonce, bool inhibit=false);
 };
 
 } // namespace jit
 
 namespace gc {
 
 inline bool
-IsMarked(const jit::VMFunction*)
+IsMarked(JSRuntime* rt, const jit::VMFunction*)
 {
     // VMFunction are only static objects which are used by WeakMaps as keys.
     // It is considered as a root object which is always marked.
     return true;
 }
 
 } // namespace gc
 
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -719,17 +719,17 @@ JitcodeGlobalTable::setAllEntriesAsExpir
     AutoSuppressProfilerSampling suppressSampling(rt);
     for (Range r(*this); !r.empty(); r.popFront())
         r.front()->setAsExpired();
 }
 
 struct Unconditionally
 {
     template <typename T>
-    static bool ShouldTrace(T* thingp) { return true; }
+    static bool ShouldTrace(JSRuntime* rt, T* thingp) { return true; }
 };
 
 void
 JitcodeGlobalTable::trace(JSTracer* trc)
 {
     // Trace all entries unconditionally. This is done during minor collection
     // to tenure and update object pointers.
 
@@ -738,23 +738,23 @@ JitcodeGlobalTable::trace(JSTracer* trc)
     AutoSuppressProfilerSampling suppressSampling(trc->runtime());
     for (Range r(*this); !r.empty(); r.popFront())
         r.front()->trace<Unconditionally>(trc);
 }
 
 struct IfUnmarked
 {
     template <typename T>
-    static bool ShouldTrace(T* thingp) { return !IsMarkedUnbarriered(thingp); }
+    static bool ShouldTrace(JSRuntime* rt, T* thingp) { return !IsMarkedUnbarriered(rt, thingp); }
 };
 
 template <>
-bool IfUnmarked::ShouldTrace<TypeSet::Type>(TypeSet::Type* type)
+bool IfUnmarked::ShouldTrace<TypeSet::Type>(JSRuntime* rt, TypeSet::Type* type)
 {
-    return !TypeSet::IsTypeMarked(type);
+    return !TypeSet::IsTypeMarked(rt, type);
 }
 
 bool
 JitcodeGlobalTable::markIteratively(GCMarker* marker)
 {
     // JitcodeGlobalTable must keep entries that are in the sampler buffer
     // alive. This conditionality is akin to holding the entries weakly.
     //
@@ -795,17 +795,17 @@ JitcodeGlobalTable::markIteratively(GCMa
         // generation, and conditionally mark the rest of the entry if its
         // JitCode is not already marked. This conditional marking ensures
         // that so long as the JitCode *may* be sampled, we keep any
         // information that may be handed out to the sampler, like tracked
         // types used by optimizations and scripts used for pc to line number
         // mapping, alive as well.
         if (!entry->isSampled(gen, lapCount)) {
             entry->setAsExpired();
-            if (!entry->baseEntry().isJitcodeMarkedFromAnyThread())
+            if (!entry->baseEntry().isJitcodeMarkedFromAnyThread(marker->runtime()))
                 continue;
         }
 
         // The table is runtime-wide. Not all zones may be participating in
         // the GC.
         if (!entry->zone()->isCollecting() || entry->zone()->isGCFinished())
             continue;
 
@@ -831,89 +831,90 @@ JitcodeGlobalTable::sweep(JSRuntime* rt)
             entry->sweepChildren(rt);
     }
 }
 
 template <class ShouldTraceProvider>
 bool
 JitcodeGlobalEntry::BaseEntry::traceJitcode(JSTracer* trc)
 {
-    if (ShouldTraceProvider::ShouldTrace(&jitcode_)) {
+    if (ShouldTraceProvider::ShouldTrace(trc->runtime(), &jitcode_)) {
         TraceManuallyBarrieredEdge(trc, &jitcode_, "jitcodglobaltable-baseentry-jitcode");
         return true;
     }
     return false;
 }
 
 bool
-JitcodeGlobalEntry::BaseEntry::isJitcodeMarkedFromAnyThread()
+JitcodeGlobalEntry::BaseEntry::isJitcodeMarkedFromAnyThread(JSRuntime* rt)
 {
-    return IsMarkedUnbarriered(&jitcode_) ||
+    return IsMarkedUnbarriered(rt, &jitcode_) ||
            jitcode_->arena()->allocatedDuringIncremental;
 }
 
 bool
 JitcodeGlobalEntry::BaseEntry::isJitcodeAboutToBeFinalized()
 {
     return IsAboutToBeFinalizedUnbarriered(&jitcode_);
 }
 
 template <class ShouldTraceProvider>
 bool
 JitcodeGlobalEntry::BaselineEntry::trace(JSTracer* trc)
 {
-    if (ShouldTraceProvider::ShouldTrace(&script_)) {
+    if (ShouldTraceProvider::ShouldTrace(trc->runtime(), &script_)) {
         TraceManuallyBarrieredEdge(trc, &script_, "jitcodeglobaltable-baselineentry-script");
         return true;
     }
     return false;
 }
 
 void
 JitcodeGlobalEntry::BaselineEntry::sweepChildren()
 {
     MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&script_));
 }
 
 bool
-JitcodeGlobalEntry::BaselineEntry::isMarkedFromAnyThread()
+JitcodeGlobalEntry::BaselineEntry::isMarkedFromAnyThread(JSRuntime* rt)
 {
-    return IsMarkedUnbarriered(&script_) ||
+    return IsMarkedUnbarriered(rt, &script_) ||
            script_->arena()->allocatedDuringIncremental;
 }
 
 template <class ShouldTraceProvider>
 bool
 JitcodeGlobalEntry::IonEntry::trace(JSTracer* trc)
 {
     bool tracedAny = false;
 
+    JSRuntime* rt = trc->runtime();
     for (unsigned i = 0; i < numScripts(); i++) {
-        if (ShouldTraceProvider::ShouldTrace(&sizedScriptList()->pairs[i].script)) {
+        if (ShouldTraceProvider::ShouldTrace(rt, &sizedScriptList()->pairs[i].script)) {
             TraceManuallyBarrieredEdge(trc, &sizedScriptList()->pairs[i].script,
                                        "jitcodeglobaltable-ionentry-script");
             tracedAny = true;
         }
     }
 
     if (!optsAllTypes_)
         return tracedAny;
 
     for (IonTrackedTypeWithAddendum* iter = optsAllTypes_->begin();
          iter != optsAllTypes_->end(); iter++)
     {
-        if (ShouldTraceProvider::ShouldTrace(&iter->type)) {
+        if (ShouldTraceProvider::ShouldTrace(rt, &iter->type)) {
             iter->type.trace(trc);
             tracedAny = true;
         }
-        if (iter->hasAllocationSite() && ShouldTraceProvider::ShouldTrace(&iter->script)) {
+        if (iter->hasAllocationSite() && ShouldTraceProvider::ShouldTrace(rt, &iter->script)) {
             TraceManuallyBarrieredEdge(trc, &iter->script,
                                        "jitcodeglobaltable-ionentry-type-addendum-script");
             tracedAny = true;
-        } else if (iter->hasConstructor() && ShouldTraceProvider::ShouldTrace(&iter->constructor)) {
+        } else if (iter->hasConstructor() && ShouldTraceProvider::ShouldTrace(rt, &iter->constructor)) {
             TraceManuallyBarrieredEdge(trc, &iter->constructor,
                                        "jitcodeglobaltable-ionentry-type-addendum-constructor");
             tracedAny = true;
         }
     }
 
     return tracedAny;
 }
@@ -936,33 +937,33 @@ JitcodeGlobalEntry::IonEntry::sweepChild
         if (iter->hasAllocationSite())
             MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&iter->script));
         else if (iter->hasConstructor())
             MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&iter->constructor));
     }
 }
 
 bool
-JitcodeGlobalEntry::IonEntry::isMarkedFromAnyThread()
+JitcodeGlobalEntry::IonEntry::isMarkedFromAnyThread(JSRuntime* rt)
 {
     for (unsigned i = 0; i < numScripts(); i++) {
-        if (!IsMarkedUnbarriered(&sizedScriptList()->pairs[i].script) &&
+        if (!IsMarkedUnbarriered(rt, &sizedScriptList()->pairs[i].script) &&
             !sizedScriptList()->pairs[i].script->arena()->allocatedDuringIncremental)
         {
             return false;
         }
     }
 
     if (!optsAllTypes_)
         return true;
 
     for (IonTrackedTypeWithAddendum* iter = optsAllTypes_->begin();
          iter != optsAllTypes_->end(); iter++)
     {
-        if (!TypeSet::IsTypeMarked(&iter->type) &&
+        if (!TypeSet::IsTypeMarked(rt, &iter->type) &&
             !TypeSet::IsTypeAllocatedDuringIncremental(iter->type))
         {
             return false;
         }
     }
 
     return true;
 }
--- a/js/src/jit/JitcodeMap.h
+++ b/js/src/jit/JitcodeMap.h
@@ -203,17 +203,17 @@ class JitcodeGlobalEntry
         bool endsAbovePointer(void* ptr) const {
             return ((uint8_t*)nativeEndAddr()) > ((uint8_t*) ptr);
         }
         bool containsPointer(void* ptr) const {
             return startsBelowPointer(ptr) && endsAbovePointer(ptr);
         }
 
         template <class ShouldTraceProvider> bool traceJitcode(JSTracer* trc);
-        bool isJitcodeMarkedFromAnyThread();
+        bool isJitcodeMarkedFromAnyThread(JSRuntime* rt);
         bool isJitcodeAboutToBeFinalized();
     };
 
     struct IonEntry : public BaseEntry
     {
         // regionTable_ points to the start of the region table within the
         // packed map for compile represented by this entry.  Since the
         // region table occurs at the tail of the memory region, this pointer
@@ -365,17 +365,17 @@ class JitcodeGlobalEntry
 
         void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index,
                                         JS::ForEachTrackedOptimizationAttemptOp& op);
         void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index,
                                          IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op);
 
         template <class ShouldTraceProvider> bool trace(JSTracer* trc);
         void sweepChildren();
-        bool isMarkedFromAnyThread();
+        bool isMarkedFromAnyThread(JSRuntime* rt);
     };
 
     struct BaselineEntry : public BaseEntry
     {
         JSScript* script_;
         const char* str_;
 
         // Last location that caused Ion to abort compilation and the reason
@@ -423,17 +423,17 @@ class JitcodeGlobalEntry
         uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
                                  uint32_t maxResults) const;
 
         void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr,
                                          JSScript** script, jsbytecode** pc) const;
 
         template <class ShouldTraceProvider> bool trace(JSTracer* trc);
         void sweepChildren();
-        bool isMarkedFromAnyThread();
+        bool isMarkedFromAnyThread(JSRuntime* rt);
     };
 
     struct IonCacheEntry : public BaseEntry
     {
         void* rejoinAddr_;
         JS::TrackedOutcome trackedOutcome_;
 
         void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr,
@@ -946,23 +946,23 @@ class JitcodeGlobalEntry
           case Dummy:
             break;
           default:
             MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
         }
     }
 
     bool isMarkedFromAnyThread(JSRuntime* rt) {
-        if (!baseEntry().isJitcodeMarkedFromAnyThread())
+        if (!baseEntry().isJitcodeMarkedFromAnyThread(rt))
             return false;
         switch (kind()) {
           case Ion:
-            return ionEntry().isMarkedFromAnyThread();
+            return ionEntry().isMarkedFromAnyThread(rt);
           case Baseline:
-            return baselineEntry().isMarkedFromAnyThread();
+            return baselineEntry().isMarkedFromAnyThread(rt);
           case IonCache:
             return ionCacheEntry().isMarkedFromAnyThread(rt);
           case Dummy:
             break;
           default:
             MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
         }
         return true;
--- a/js/src/jsapi-tests/testIntern.cpp
+++ b/js/src/jsapi-tests/testIntern.cpp
@@ -38,11 +38,11 @@ BEGIN_TEST(testPinAcrossGC)
     CHECK(sw.strOk);
     return true;
 }
 
 static void
 FinalizeCallback(JSFreeOp* fop, JSFinalizeStatus status, bool isZoneGC, void* data)
 {
     if (status == JSFINALIZE_GROUP_START)
-        sw.strOk = js::gc::IsMarkedUnbarriered(&sw.str);
+        sw.strOk = js::gc::IsMarkedUnbarriered(fop->runtime(), &sw.str);
 }
 END_TEST(testPinAcrossGC)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4665,21 +4665,21 @@ MarkIncomingCrossCompartmentPointers(JSR
         for (JSObject* src = c->gcIncomingGrayPointers;
              src;
              src = NextIncomingCrossCompartmentPointer(src, unlinkList))
         {
             JSObject* dst = CrossCompartmentPointerReferent(src);
             MOZ_ASSERT(dst->compartment() == c);
 
             if (color == GRAY) {
-                if (IsMarkedUnbarriered(&src) && src->asTenured().isMarked(GRAY))
+                if (IsMarkedUnbarriered(rt, &src) && src->asTenured().isMarked(GRAY))
                     TraceManuallyBarrieredEdge(&rt->gc.marker, &dst,
                                                "cross-compartment gray pointer");
             } else {
-                if (IsMarkedUnbarriered(&src) && !src->asTenured().isMarked(GRAY))
+                if (IsMarkedUnbarriered(rt, &src) && !src->asTenured().isMarked(GRAY))
                     TraceManuallyBarrieredEdge(&rt->gc.marker, &dst,
                                                "cross-compartment black pointer");
             }
         }
 
         if (unlinkList)
             c->gcIncomingGrayPointers = nullptr;
     }
--- a/js/src/jswatchpoint.cpp
+++ b/js/src/jswatchpoint.cpp
@@ -149,30 +149,30 @@ bool
 WatchpointMap::markIteratively(GCMarker* marker)
 {
     bool marked = false;
     for (Map::Enum e(map); !e.empty(); e.popFront()) {
         Map::Entry& entry = e.front();
         JSObject* priorKeyObj = entry.key().object;
         jsid priorKeyId(entry.key().id.get());
         bool objectIsLive =
-            IsMarked(const_cast<PreBarrieredObject*>(&entry.key().object));
+            IsMarked(marker->runtime(), const_cast<PreBarrieredObject*>(&entry.key().object));
         if (objectIsLive || entry.value().held) {
             if (!objectIsLive) {
                 TraceEdge(marker, const_cast<PreBarrieredObject*>(&entry.key().object),
                            "held Watchpoint object");
                 marked = true;
             }
 
             MOZ_ASSERT(JSID_IS_STRING(priorKeyId) ||
                        JSID_IS_INT(priorKeyId) ||
                        JSID_IS_SYMBOL(priorKeyId));
             TraceEdge(marker, const_cast<PreBarrieredId*>(&entry.key().id), "WatchKey::id");
 
-            if (entry.value().closure && !IsMarked(&entry.value().closure)) {
+            if (entry.value().closure && !IsMarked(marker->runtime(), &entry.value().closure)) {
                 TraceEdge(marker, &entry.value().closure, "Watchpoint::closure");
                 marked = true;
             }
 
             /* We will sweep this entry in sweepAll if !objectIsLive. */
             if (priorKeyObj != entry.key().object || priorKeyId != entry.key().id)
                 e.rekeyFront(WatchKey(entry.key().object, entry.key().id));
         }
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -183,17 +183,17 @@ class WeakMap : public HashMap<Key, Valu
         // If this cast fails, then you're instantiating the WeakMap with a
         // Lookup that can't be constructed from a Cell*. The WeakKeyTable
         // mechanism is indexed with a GCCellPtr, so that won't work.
         Ptr p = Base::lookup(static_cast<Lookup>(origKey.asCell()));
         MOZ_ASSERT(p.found());
 
         Key key(p->key());
         MOZ_ASSERT((markedCell == extractUnbarriered(key)) || (markedCell == getDelegate(key)));
-        if (gc::IsMarked(&key)) {
+        if (gc::IsMarked(marker->runtime(), &key)) {
             TraceEdge(marker, &p->value(), "ephemeron value");
         } else if (keyNeedsMark(key)) {
             TraceEdge(marker, &p->value(), "WeakMap ephemeron value");
             TraceEdge(marker, &key, "proxy-preserved WeakMap ephemeron key");
             MOZ_ASSERT(key == p->key()); // No moving
         }
         key.unsafeSet(nullptr); // Prevent destructor from running barriers.
     }
@@ -243,25 +243,25 @@ class WeakMap : public HashMap<Key, Valu
 
     bool markIteratively(GCMarker* marker) override {
         MOZ_ASSERT(marked);
 
         bool markedAny = false;
 
         for (Enum e(*this); !e.empty(); e.popFront()) {
             // If the entry is live, ensure its key and value are marked.
-            bool keyIsMarked = gc::IsMarked(&e.front().mutableKey());
+            bool keyIsMarked = gc::IsMarked(marker->runtime(), &e.front().mutableKey());
             if (!keyIsMarked && keyNeedsMark(e.front().key())) {
                 TraceEdge(marker, &e.front().mutableKey(), "proxy-preserved WeakMap entry key");
                 keyIsMarked = true;
                 markedAny = true;
             }
 
             if (keyIsMarked) {
-                if (!gc::IsMarked(&e.front().value())) {
+                if (!gc::IsMarked(marker->runtime(), &e.front().value())) {
                     TraceEdge(marker, &e.front().value(), "WeakMap entry value");
                     markedAny = true;
                 }
             } else if (marker->isWeakMarkingTracer()) {
                 // Entry is not yet known to be live. Record this weakmap and
                 // the lookup key in the list of weak keys. Also record the
                 // delegate, if any, because marking the delegate also marks
                 // the entry.
@@ -290,17 +290,17 @@ class WeakMap : public HashMap<Key, Valu
     void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); }
 
     bool keyNeedsMark(JSObject* key) const {
         JSObject* delegate = getDelegate(key);
         /*
          * Check if the delegate is marked with any color to properly handle
          * gray marking when the key's delegate is black and the map is gray.
          */
-        return delegate && gc::IsMarkedUnbarriered(&delegate);
+        return delegate && gc::IsMarkedUnbarriered(zone->runtimeFromMainThread(), &delegate);
     }
 
     bool keyNeedsMark(JSScript* script) const {
         return false;
     }
 
     bool findZoneEdges() override {
         // This is overridden by ObjectValueMap.
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -801,32 +801,32 @@ JSObject*
 Debugger::getHook(Hook hook) const
 {
     MOZ_ASSERT(hook >= 0 && hook < HookCount);
     const Value& v = object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + hook);
     return v.isUndefined() ? nullptr : &v.toObject();
 }
 
 bool
-Debugger::hasAnyLiveHooks() const
+Debugger::hasAnyLiveHooks(JSRuntime* rt) const
 {
     if (!enabled)
         return false;
 
     if (getHook(OnDebuggerStatement) ||
         getHook(OnExceptionUnwind) ||
         getHook(OnNewScript) ||
         getHook(OnEnterFrame))
     {
         return true;
     }
 
     /* If any breakpoints are in live scripts, return true. */
     for (Breakpoint* bp = firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
-        if (IsMarkedUnbarriered(&bp->site->script))
+        if (IsMarkedUnbarriered(rt, &bp->site->script))
             return true;
     }
 
     for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
         NativeObject* frameObj = r.front().value();
         if (!frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() ||
             !frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined())
             return true;
@@ -2999,17 +2999,17 @@ Debugger::markIteratively(GCMarker* mark
     /*
      * Find all Debugger objects in danger of GC. This code is a little
      * convoluted since the easiest way to find them is via their debuggees.
      */
     JSRuntime* rt = marker->runtime();
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         if (c->isDebuggee()) {
             GlobalObject* global = c->unsafeUnbarrieredMaybeGlobal();
-            if (!IsMarkedUnbarriered(&global))
+            if (!IsMarkedUnbarriered(rt, &global))
                 continue;
 
             /*
              * Every debuggee has at least one debugger, so in this case
              * getDebuggers can't return nullptr.
              */
             const GlobalObject::DebuggerVector* debuggers = global->getDebuggers();
             MOZ_ASSERT(debuggers);
@@ -3021,36 +3021,36 @@ Debugger::markIteratively(GCMarker* mark
                  *   - dbg is actually in a compartment that is being marked
                  *   - it isn't already marked
                  *   - it actually has hooks that might be called
                  */
                 GCPtrNativeObject& dbgobj = dbg->toJSObjectRef();
                 if (!dbgobj->zone()->isGCMarking())
                     continue;
 
-                bool dbgMarked = IsMarked(&dbgobj);
-                if (!dbgMarked && dbg->hasAnyLiveHooks()) {
+                bool dbgMarked = IsMarked(rt, &dbgobj);
+                if (!dbgMarked && dbg->hasAnyLiveHooks(rt)) {
                     /*
                      * obj could be reachable only via its live, enabled
                      * debugger hooks, which may yet be called.
                      */
                     TraceEdge(marker, &dbgobj, "enabled Debugger");
                     markedAny = true;
                     dbgMarked = true;
                 }
 
                 if (dbgMarked) {
                     /* Search for breakpoints to mark. */
                     for (Breakpoint* bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
-                        if (IsMarkedUnbarriered(&bp->site->script)) {
+                        if (IsMarkedUnbarriered(rt, &bp->site->script)) {
                             /*
                              * The debugger and the script are both live.
                              * Therefore the breakpoint handler is live.
                              */
-                            if (!IsMarked(&bp->getHandlerRef())) {
+                            if (!IsMarked(rt, &bp->getHandlerRef())) {
                                 TraceEdge(marker, &bp->getHandlerRef(), "breakpoint handler");
                                 markedAny = true;
                             }
                         }
                     }
                 }
             }
         }
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -692,17 +692,17 @@ class Debugger : private mozilla::Linked
 
     static bool hookObservesAllExecution(Hook which);
 
     MOZ_MUST_USE bool updateObservesAllExecutionOnDebuggees(JSContext* cx, IsObserving observing);
     MOZ_MUST_USE bool updateObservesCoverageOnDebuggees(JSContext* cx, IsObserving observing);
     void updateObservesAsmJSOnDebuggees(IsObserving observing);
 
     JSObject* getHook(Hook hook) const;
-    bool hasAnyLiveHooks() const;
+    bool hasAnyLiveHooks(JSRuntime* rt) const;
 
     static MOZ_MUST_USE bool slowPathCheckNoExecute(JSContext* cx, HandleScript script);
     static JSTrapStatus slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame);
     static MOZ_MUST_USE bool slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame,
                                                   jsbytecode* pc, bool ok);
     static JSTrapStatus slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
     static JSTrapStatus slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
     static void slowPathOnNewScript(JSContext* cx, HandleScript script);
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -1270,17 +1270,17 @@ RegExpShared::needsSweep(JSRuntime* rt)
     // being subsequently cleared. This can happen if a GC is restarted while
     // in progress (i.e. performing a full GC in the middle of an incremental
     // GC) or if a RegExpShared referenced via the 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 = marked() && IsMarked(&source);
+    bool keep = marked() && IsMarked(rt, &source);
     for (size_t i = 0; i < ArrayLength(compilationArray); i++) {
         RegExpShared::RegExpCompilation& compilation = compilationArray[i];
         if (compilation.jitCode && gc::IsAboutToBeFinalized(&compilation.jitCode))
             keep = false;
     }
 
     MOZ_ASSERT(rt->isHeapMajorCollecting());
     if (keep || rt->gc.isHeapCompacting()) {
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -785,26 +785,26 @@ TypeSet::readBarrier(const TypeSet* type
                 (void) key->singleton();
             else
                 (void) key->group();
         }
     }
 }
 
 /* static */ bool
-TypeSet::IsTypeMarked(TypeSet::Type* v)
+TypeSet::IsTypeMarked(JSRuntime* rt, TypeSet::Type* v)
 {
     bool rv;
     if (v->isSingletonUnchecked()) {
         JSObject* obj = v->singletonNoBarrier();
-        rv = IsMarkedUnbarriered(&obj);
+        rv = IsMarkedUnbarriered(rt, &obj);
         *v = TypeSet::ObjectType(obj);
     } else if (v->isGroupUnchecked()) {
         ObjectGroup* group = v->groupNoBarrier();
-        rv = IsMarkedUnbarriered(&group);
+        rv = IsMarkedUnbarriered(rt, &group);
         *v = TypeSet::ObjectType(group);
     } else {
         rv = true;
     }
     return rv;
 }
 
 /* static */ bool
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -524,17 +524,17 @@ class TypeSet
 
     static inline bool IsUntrackedValue(const Value& val);
 
     // Get the type of a possibly optimized out or uninitialized let value.
     // This generally only happens on unconditional type monitors on bailing
     // out of Ion, such as for argument and local types.
     static inline Type GetMaybeUntrackedValueType(const Value& val);
 
-    static bool IsTypeMarked(Type* v);
+    static bool IsTypeMarked(JSRuntime* rt, Type* v);
     static bool IsTypeAllocatedDuringIncremental(Type v);
     static bool IsTypeAboutToBeFinalized(Type* v);
 } JS_HAZ_GC_POINTER;
 
 /*
  * A constraint which listens to additions to a type set and propagates those
  * changes to other type sets.
  */