Bug 1135985 - Introduce a new, strongly-typed tracing path; r=jonco, r=sfink
☠☠ backed out by 99415fbccf83 ☠ ☠
authorTerrence Cole <terrence@mozilla.com>
Thu, 26 Feb 2015 14:15:26 -0800
changeset 266631 853e3ad56dadff940baea514c999b9fb055755fb
parent 266611 bcb0b42f9068ad85685a47c00680018a9fb20258
child 266632 6bbd529bd995bc89ac9b064bf33b81d3ce6cd342
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco, sfink
bugs1135985
milestone39.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 1135985 - Introduce a new, strongly-typed tracing path; r=jonco, r=sfink
js/public/TracingAPI.h
js/src/gc/Marking.cpp
js/src/gc/Marking.h
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -114,25 +114,27 @@ class JS_PUBLIC_API(JSTracer)
         debugPrintIndex_ = index;
     }
 
     void setTracingIndex(const char *name, size_t index) {
         setTracingDetails(nullptr, (void *)name, index);
     }
 
     void setTracingName(const char *name) {
-        setTracingDetails(nullptr, (void *)name, size_t(-1));
+        setTracingDetails(nullptr, (void *)name, InvalidIndex);
     }
 
     // Remove the currently set tracing details.
     void clearTracingDetails() {
         debugPrinter_ = nullptr;
         debugPrintArg_ = nullptr;
     }
 
+    const static size_t InvalidIndex = size_t(-1);
+
     // Return true if tracing details are currently set.
     bool hasTracingDetails() const;
 
     // Get the string set with the most recent call to setTracingName or return
     // fallback if a name printer function has been installed.
     const char *tracingName(const char *fallback) const;
 
     // Build a description of this edge in the heap graph. This call may invoke
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -2,16 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gc/Marking.h"
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/IntegerRange.h"
+#include "mozilla/TypeTraits.h"
 
 #include "jsprf.h"
 
 #include "gc/GCInternals.h"
 #include "jit/IonCode.h"
 #include "js/SliceBudget.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/ArrayObject.h"
@@ -26,16 +28,19 @@
 
 #include "gc/Nursery-inl.h"
 #include "vm/String-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::DebugOnly;
+using mozilla::IsBaseOf;
+using mozilla::IsSame;
+using mozilla::MakeRange;
 
 void * const js::NullPtr::constNullValue = nullptr;
 
 JS_PUBLIC_DATA(void * const) JS::NullPtr::constNullValue = nullptr;
 
 /*
  * There are two mostly separate mark paths. The first is a fast path used
  * internally in the GC. The second is a slow path used for root marking and
@@ -142,62 +147,71 @@ IsThingPoisoned(T *thing)
 
 static GCMarker *
 AsGCMarker(JSTracer *trc)
 {
     MOZ_ASSERT(trc->isMarkingTracer());
     return static_cast<GCMarker *>(trc);
 }
 
-template <typename T> bool ThingIsPermanentAtom(T *thing) { return false; }
-template <> bool ThingIsPermanentAtom<JSString>(JSString *str) { return str->isPermanentAtom(); }
-template <> bool ThingIsPermanentAtom<JSFlatString>(JSFlatString *str) { return str->isPermanentAtom(); }
-template <> bool ThingIsPermanentAtom<JSLinearString>(JSLinearString *str) { return str->isPermanentAtom(); }
-template <> bool ThingIsPermanentAtom<JSAtom>(JSAtom *atom) { return atom->isPermanent(); }
-template <> bool ThingIsPermanentAtom<PropertyName>(PropertyName *name) { return name->isPermanent(); }
-template <> bool ThingIsPermanentAtom<JS::Symbol>(JS::Symbol *sym) { return sym->isWellKnownSymbol(); }
+template <typename T> bool ThingIsPermanentAtomOrWellKnownSymbol(T *thing) { return false; }
+template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JSString>(JSString *str) {
+    return str->isPermanentAtom();
+}
+template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JSFlatString>(JSFlatString *str) {
+    return str->isPermanentAtom();
+}
+template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JSLinearString>(JSLinearString *str) {
+    return str->isPermanentAtom();
+}
+template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JSAtom>(JSAtom *atom) {
+    return atom->isPermanent();
+}
+template <> bool ThingIsPermanentAtomOrWellKnownSymbol<PropertyName>(PropertyName *name) {
+    return name->isPermanent();
+}
+template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JS::Symbol>(JS::Symbol *sym) {
+    return sym->isWellKnownSymbol();
+}
 
 template<typename T>
 static inline void
-CheckMarkedThing(JSTracer *trc, T **thingp)
+CheckMarkedThing(JSTracer *trc, T thing)
 {
 #ifdef DEBUG
     MOZ_ASSERT(trc);
-    MOZ_ASSERT(thingp);
-
-    T *thing = *thingp;
-    MOZ_ASSERT(*thingp);
+    MOZ_ASSERT(thing);
 
     thing = MaybeForwarded(thing);
 
     /* This function uses data that's not available in the nursery. */
     if (IsInsideNursery(thing))
         return;
 
     MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc) && !Nursery::IsMinorCollectionTracer(trc),
-                  !IsForwarded(*thingp));
+                  !IsForwarded(thing));
 
     /*
      * Permanent atoms are not associated with this runtime, but will be ignored
      * during marking.
      */
-    if (ThingIsPermanentAtom(thing))
+    if (ThingIsPermanentAtomOrWellKnownSymbol(thing))
         return;
 
     Zone *zone = thing->zoneFromAnyThread();
     JSRuntime *rt = trc->runtime();
 
     MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc), CurrentThreadCanAccessZone(zone));
     MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc), CurrentThreadCanAccessRuntime(rt));
 
     MOZ_ASSERT(zone->runtimeFromAnyThread() == trc->runtime());
-    MOZ_ASSERT(trc->hasTracingDetails());
 
     MOZ_ASSERT(thing->isAligned());
-    MOZ_ASSERT(MapTypeToTraceKind<T>::kind == GetGCThingTraceKind(thing));
+    MOZ_ASSERT(MapTypeToTraceKind<typename mozilla::RemovePointer<T>::Type>::kind ==
+               GetGCThingTraceKind(thing));
 
     /*
      * Do not check IsMarkingTracer directly -- it should only be used in paths
      * where we cannot be the gray buffering tracer.
      */
     bool isGcMarkingTracer = trc->isMarkingTracer();
 
     MOZ_ASSERT_IF(zone->requireGCTracer(), isGcMarkingTracer || IsBufferingGrayRoots(trc));
@@ -221,16 +235,47 @@ CheckMarkedThing(JSTracer *trc, T **thin
      * background sweeping may be running and concurrently modifiying the free
      * list.
      */
     MOZ_ASSERT_IF(IsThingPoisoned(thing) && rt->isHeapBusy() && !rt->gc.isBackgroundSweeping(),
                   !InFreeList(thing->asTenured().arenaHeader(), thing));
 #endif
 }
 
+template<>
+void
+CheckMarkedThing<Value>(JSTracer *trc, Value val)
+{
+#ifdef DEBUG
+    if (val.isString())
+        CheckMarkedThing(trc, val.toString());
+    else if (val.isObject())
+        CheckMarkedThing(trc, &val.toObject());
+    else if (val.isSymbol())
+        CheckMarkedThing(trc, val.toSymbol());
+#endif
+}
+
+template <>
+void
+CheckMarkedThing<jsid>(JSTracer *trc, jsid id)
+{
+#ifdef DEBUG
+    if (JSID_IS_STRING(id))
+        CheckMarkedThing(trc, JSID_TO_STRING(id));
+    else if (JSID_IS_SYMBOL(id))
+        CheckMarkedThing(trc, JSID_TO_SYMBOL(id));
+#endif
+}
+
+#define JS_ROOT_MARKING_ASSERT(trc) \
+    MOZ_ASSERT_IF(trc->isMarkingTracer(), \
+                  trc->runtime()->gc.state() == NO_INCREMENTAL || \
+                  trc->runtime()->gc.state() == MARK_ROOTS);
+
 /*
  * We only set the maybeAlive flag for objects and scripts. It's assumed that,
  * if a compartment is alive, then it will have at least some live object or
  * script it in. Even if we get this wrong, the worst that will happen is that
  * scheduledForDestruction will be set on the compartment, which will cause some
  * extra GC activity to try to free the compartment.
  */
 template<typename T>
@@ -255,39 +300,355 @@ SetMaybeAliveFlag(NativeObject *thing)
 
 template<>
 void
 SetMaybeAliveFlag(JSScript *thing)
 {
     thing->compartment()->maybeAlive = true;
 }
 
+#define FOR_EACH_GC_LAYOUT(D) \
+    D(Object, JSObject) \
+    D(String, JSString) \
+    D(Symbol, JS::Symbol) \
+    D(Script, JSScript) \
+    D(Shape, js::Shape) \
+    D(BaseShape, js::BaseShape) \
+    D(JitCode, js::jit::JitCode) \
+    D(LazyScript, js::LazyScript) \
+    D(ObjectGroup, js::ObjectGroup)
+
+// A C++ version of JSGCTraceKind
+enum class TraceKind {
+#define NAMES(name, _) name,
+FOR_EACH_GC_LAYOUT(NAMES)
+#undef NAMES
+};
+
+#define FOR_EACH_GC_POINTER_TYPE(D) \
+    D(BaseShape *) \
+    D(UnownedBaseShape *) \
+    D(jit::JitCode *) \
+    D(NativeObject *) \
+    D(ArrayObject *) \
+    D(ArgumentsObject *) \
+    D(ArrayBufferObject *) \
+    D(ArrayBufferObjectMaybeShared *) \
+    D(ArrayBufferViewObject *) \
+    D(DebugScopeObject *) \
+    D(GlobalObject *) \
+    D(JSObject *) \
+    D(JSFunction *) \
+    D(NestedScopeObject *) \
+    D(PlainObject *) \
+    D(SavedFrame *) \
+    D(ScopeObject *) \
+    D(SharedArrayBufferObject *) \
+    D(SharedTypedArrayObject *) \
+    D(JSScript *) \
+    D(LazyScript *) \
+    D(Shape *) \
+    D(JSAtom *) \
+    D(JSString *) \
+    D(JSFlatString *) \
+    D(JSLinearString *) \
+    D(PropertyName *) \
+    D(JS::Symbol *) \
+    D(js::ObjectGroup *) \
+    D(Value) \
+    D(jsid)
+
+// The second parameter to BaseGCType is derived automatically based on T. The
+// relation here is that for any T, the TraceKind will automatically,
+// statically select the correct Cell layout for marking. Below, we instantiate
+// each override with a declaration of the most derived layout type.
+//
+// Usage:
+//   BaseGCType<T>::type
+//
+// Examples:
+//   BaseGCType<JSFunction>::type => JSObject
+//   BaseGCType<UnownedBaseShape>::type => BaseShape
+//   etc.
+template <typename T,
+          TraceKind = IsBaseOf<JSObject, T>::value     ? TraceKind::Object
+                    : IsBaseOf<JSString, T>::value     ? TraceKind::String
+                    : IsBaseOf<JS::Symbol, T>::value   ? TraceKind::Symbol
+                    : IsBaseOf<JSScript, T>::value     ? TraceKind::Script
+                    : IsBaseOf<Shape, T>::value        ? TraceKind::Shape
+                    : IsBaseOf<BaseShape, T>::value    ? TraceKind::BaseShape
+                    : IsBaseOf<jit::JitCode, T>::value ? TraceKind::JitCode
+                    : IsBaseOf<LazyScript, T>::value   ? TraceKind::LazyScript
+                    :                                    TraceKind::ObjectGroup>
+struct BaseGCType;
+#define IMPL_BASE_GC_TYPE(name, type_) \
+    template <typename T> struct BaseGCType<T, TraceKind:: name> { typedef type_ type; };
+FOR_EACH_GC_LAYOUT(IMPL_BASE_GC_TYPE);
+#undef IMPL_BASE_GC_TYPE
+
+// Our barrier templates are parameterized on the pointer types so that we can
+// share the definitions with Value and jsid. Thus, we need to strip the
+// pointer before sending the type to BaseGCType and re-add it on the other
+// side. As such:
+template <typename T> struct PtrBaseGCType {};
+template <> struct PtrBaseGCType<Value> { typedef Value type; };
+template <> struct PtrBaseGCType<jsid> { typedef jsid type; };
+template <typename T> struct PtrBaseGCType<T *> { typedef typename BaseGCType<T>::type *type; };
+
+template <typename T> void DispatchToTracer(JSTracer *trc, T *thingp, const char *name, size_t i);
+template <typename T> void DoTracing(JS::CallbackTracer *trc, T *thingp, const char *name, size_t i);
+template <typename T> void DoMarking(GCMarker *gcmarker, T thing);
+
+template <typename T>
+void
+js::TraceEdge(JSTracer *trc, BarrieredBase<T> *thingp, const char *name)
+{
+    auto layout = reinterpret_cast<typename PtrBaseGCType<T>::type *>(thingp->unsafeGet());
+    DispatchToTracer(trc, layout, name, JSTracer::InvalidIndex);
+}
+
+template <typename T>
+void
+js::TraceManuallyBarrieredEdge(JSTracer *trc, T *thingp, const char *name)
+{
+    auto layout = reinterpret_cast<typename PtrBaseGCType<T>::type *>(thingp);
+    DispatchToTracer(trc, layout, name, JSTracer::InvalidIndex);
+}
+
+template <typename T>
+void
+js::TraceRoot(JSTracer *trc, T *thingp, const char *name)
+{
+    JS_ROOT_MARKING_ASSERT(trc);
+    auto layout = reinterpret_cast<typename PtrBaseGCType<T>::type *>(thingp);
+    DispatchToTracer(trc, layout, name, JSTracer::InvalidIndex);
+}
+
+template <typename T>
+void
+js::TraceRange(JSTracer *trc, size_t len, BarrieredBase<T> *thingp, const char *name)
+{
+    for (auto i : MakeRange(len)) {
+        auto layout = reinterpret_cast<typename PtrBaseGCType<T>::type *>(&thingp[i]);
+        DispatchToTracer(trc, layout, name, i);
+    }
+}
+
+template <typename T>
+void
+js::TraceRootRange(JSTracer *trc, size_t len, T *thingp, const char *name)
+{
+    JS_ROOT_MARKING_ASSERT(trc);
+    for (auto i : MakeRange(len)) {
+        auto layout = reinterpret_cast<typename PtrBaseGCType<T>::type *>(&thingp[i]);
+        DispatchToTracer(trc, layout, name, i);
+    }
+}
+
+// Instantiate a copy of the Tracing templates for each derived type.
+#define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \
+    template void js::TraceEdge<type>(JSTracer *, BarrieredBase<type> *, const char *); \
+    template void js::TraceManuallyBarrieredEdge<type>(JSTracer *, type *, const char *); \
+    template void js::TraceRoot<type>(JSTracer *, type *, const char *); \
+    template void js::TraceRange<type>(JSTracer *, size_t, BarrieredBase<type> *, const char *); \
+    template void js::TraceRootRange<type>(JSTracer *, size_t, type *, const char *);
+FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS)
+#undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
+
+// This method is responsible for dynamic dispatch to the real tracer
+// implementation. Consider replacing this choke point with virtual dispatch:
+// a sufficiently smart C++ compiler may be able to devirtualize some paths.
+template <typename T>
+void
+DispatchToTracer(JSTracer *trc, T *thingp, const char *name, size_t i)
+{
+#define IS_SAME_TYPE_OR(name, type) mozilla::IsSame<type *, T>::value ||
+    static_assert(
+            FOR_EACH_GC_LAYOUT(IS_SAME_TYPE_OR)
+            mozilla::IsSame<T, JS::Value>::value ||
+            mozilla::IsSame<T, jsid>::value,
+            "Only the base cell layout types are allowed into marking/tracing internals");
+#undef IS_SAME_TYPE_OR
+    CheckMarkedThing(trc, *thingp);
+
+    if (trc->isMarkingTracer())
+        return DoMarking(static_cast<GCMarker*>(trc), *thingp);
+    return DoTracing(static_cast<JS::CallbackTracer*>(trc), thingp, name, i);
+}
+
+template <typename T>
+static inline bool
+MustSkipMarking(T thing)
+{
+    // Don't mark things outside a zone if we are in a per-zone GC.
+    return !thing->zone()->isGCMarking();
+}
+
+template <>
+bool
+MustSkipMarking<JSObject*>(JSObject *obj)
+{
+    // 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;
+
+    // Don't mark things outside a zone if we are in a per-zone GC. It is
+    // faster to check our own arena header, which we can do since we know that
+    // the object is tenured.
+    return !TenuredCell::fromPointer(obj)->zone()->isGCMarking();
+}
+
+template <>
+bool
+MustSkipMarking<JSString*>(JSString *str)
+{
+    // Don't mark permanent atoms, as they may be associated with another
+    // runtime. Note that PushMarkStack() also checks this, but we need to not
+    // run the isGCMarking test from off-main-thread, so have to check it here
+    // too.
+    return str->isPermanentAtom() ||
+           !str->zone()->isGCMarking();
+}
+
+template <>
+bool
+MustSkipMarking<JS::Symbol*>(JS::Symbol *sym)
+{
+    // As for JSString, don't touch a globally owned well-known symbol from
+    // off-main-thread.
+    return sym->isWellKnownSymbol() ||
+           !sym->zone()->isGCMarking();
+}
+
+template <typename T>
+void
+DoMarking(GCMarker *gcmarker, T thing)
+{
+    // Do per-type marking precondition checks.
+    if (MustSkipMarking(thing))
+        return;
+
+    PushMarkStack(gcmarker, thing);
+
+    // Mark the compartment as live.
+    SetMaybeAliveFlag(thing);
+}
+
+template <>
+void
+DoMarking<Value>(GCMarker *gcmarker, Value val)
+{
+    if (val.isString())
+        DoMarking(gcmarker, val.toString());
+    else if (val.isObject())
+        DoMarking(gcmarker, &val.toObject());
+    else if (val.isSymbol())
+        DoMarking(gcmarker, val.toSymbol());
+    else
+        gcmarker->clearTracingDetails();
+}
+
+template <>
+void
+DoMarking<jsid>(GCMarker *gcmarker, jsid id)
+{
+    if (JSID_IS_STRING(id))
+        DoMarking(gcmarker, JSID_TO_STRING(id));
+    else if (JSID_IS_SYMBOL(id))
+        DoMarking(gcmarker, JSID_TO_SYMBOL(id));
+    else
+        gcmarker->clearTracingDetails();
+}
+
+template <typename T>
+void
+DoTracing(JS::CallbackTracer *trc, T *thingp, const char *name, size_t i)
+{
+    JSGCTraceKind kind = MapTypeToTraceKind<typename mozilla::RemovePointer<T>::Type>::kind;
+    trc->setTracingIndex(name, i);
+    trc->invoke((void **)thingp, kind);
+    trc->unsetTracingLocation();
+}
+
+template <>
+void
+DoTracing<Value>(JS::CallbackTracer *trc, Value *vp, const char *name, size_t i)
+{
+    if (vp->isObject()) {
+        JSObject *prior = &vp->toObject();
+        JSObject *obj = prior;
+        DoTracing(trc, &obj, name, i);
+        if (obj != prior)
+            vp->setObjectOrNull(obj);
+    } else if (vp->isString()) {
+        JSString *prior = vp->toString();
+        JSString *str = prior;
+        DoTracing(trc, &str, name, i);
+        if (str != prior)
+            vp->setString(str);
+    } else if (vp->isSymbol()) {
+        JS::Symbol *prior = vp->toSymbol();
+        JS::Symbol *sym = prior;
+        DoTracing(trc, &sym, name, i);
+        if (sym != prior)
+            vp->setSymbol(sym);
+    } else {
+        /* Unset realLocation manually if we do not call MarkInternal. */
+        trc->unsetTracingLocation();
+    }
+}
+
+template <>
+void
+DoTracing<jsid>(JS::CallbackTracer *trc, jsid *idp, const char *name, size_t i)
+{
+    if (JSID_IS_STRING(*idp)) {
+        JSString *prior = JSID_TO_STRING(*idp);
+        JSString *str = prior;
+        DoTracing(trc, &str, name, i);
+        if (str != prior)
+            *idp = NON_INTEGER_ATOM_TO_JSID(reinterpret_cast<JSAtom *>(str));
+    } else if (JSID_IS_SYMBOL(*idp)) {
+        JS::Symbol *prior = JSID_TO_SYMBOL(*idp);
+        JS::Symbol *sym = prior;
+        DoTracing(trc, &sym, name, i);
+        if (sym != prior)
+            *idp = SYMBOL_TO_JSID(sym);
+    } else {
+        /* Unset realLocation manually if we do not call MarkInternal. */
+        trc->unsetTracingLocation();
+    }
+}
+
 template<typename T>
 static void
 MarkInternal(JSTracer *trc, T **thingp)
 {
-    CheckMarkedThing(trc, thingp);
     T *thing = *thingp;
+    CheckMarkedThing(trc, thing);
 
     if (trc->isMarkingTracer()) {
         /*
          * 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(thing))
             return;
 
         /*
          * Don't mark permanent atoms, as they may be associated with another
          * runtime. Note that PushMarkStack() also checks this, but the tests
          * and maybeAlive write below should only be done on the main thread.
          */
-        if (ThingIsPermanentAtom(thing))
+        if (ThingIsPermanentAtomOrWellKnownSymbol(thing))
             return;
 
         /*
          * Don't mark things outside a compartment if we are in a
          * per-compartment GC.
          */
         if (!thing->zone()->isGCMarking())
             return;
@@ -297,21 +658,16 @@ MarkInternal(JSTracer *trc, T **thingp)
     } else {
         trc->asCallbackTracer()->invoke((void **)thingp, MapTypeToTraceKind<T>::kind);
         trc->unsetTracingLocation();
     }
 
     trc->clearTracingDetails();
 }
 
-#define JS_ROOT_MARKING_ASSERT(trc) \
-    MOZ_ASSERT_IF(trc->isMarkingTracer(), \
-                  trc->runtime()->gc.state() == NO_INCREMENTAL || \
-                  trc->runtime()->gc.state() == MARK_ROOTS);
-
 namespace js {
 namespace gc {
 
 template <typename T>
 void
 MarkUnbarriered(JSTracer *trc, T **thingp, const char *name)
 {
     trc->setTracingName(name);
@@ -328,17 +684,17 @@ Mark(JSTracer *trc, BarrieredBase<T*> *t
 
 void
 MarkPermanentAtom(JSTracer *trc, JSAtom *atom, const char *name)
 {
     trc->setTracingName(name);
 
     MOZ_ASSERT(atom->isPermanent());
 
-    CheckMarkedThing(trc, &atom);
+    CheckMarkedThing(trc, atom);
 
     if (trc->isMarkingTracer()) {
         // Atoms do not refer to other GC things so don't need to go on the mark stack.
         // Additionally, PushMarkStack will ignore permanent atoms.
         atom->markIfUnmarked();
     } else {
         void *thing = atom;
         trc->asCallbackTracer()->invoke(&thing, JSTRACE_STRING);
@@ -353,17 +709,17 @@ void
 MarkWellKnownSymbol(JSTracer *trc, JS::Symbol *sym)
 {
     if (!sym)
         return;
 
     trc->setTracingName("wellKnownSymbols");
 
     MOZ_ASSERT(sym->isWellKnownSymbol());
-    CheckMarkedThing(trc, &sym);
+    CheckMarkedThing(trc, sym);
     if (trc->isMarkingTracer()) {
         // Permanent atoms are marked before well-known symbols.
         MOZ_ASSERT(sym->description()->isMarked());
         sym->markIfUnmarked();
     } else {
         void *thing = sym;
         trc->asCallbackTracer()->invoke(&thing, JSTRACE_SYMBOL);
         MOZ_ASSERT(thing == sym);
@@ -412,17 +768,17 @@ MarkRootRange(JSTracer *trc, size_t len,
 
 namespace js {
 namespace gc {
 
 template <typename T>
 static bool
 IsMarked(T **thingp)
 {
-    MOZ_ASSERT_IF(!ThingIsPermanentAtom(*thingp),
+    MOZ_ASSERT_IF(!ThingIsPermanentAtomOrWellKnownSymbol(*thingp),
                   CurrentThreadCanAccessRuntime((*thingp)->runtimeFromMainThread()));
     return IsMarkedFromAnyThread(thingp);
 }
 
 template <typename T>
 static bool
 IsMarkedFromAnyThread(T **thingp)
 {
@@ -442,33 +798,33 @@ IsMarkedFromAnyThread(T **thingp)
         *thingp = Forwarded(*thingp);
     return (*thingp)->asTenured().isMarked();
 }
 
 template <typename T>
 static bool
 IsAboutToBeFinalized(T **thingp)
 {
-    MOZ_ASSERT_IF(!ThingIsPermanentAtom(*thingp),
+    MOZ_ASSERT_IF(!ThingIsPermanentAtomOrWellKnownSymbol(*thingp),
                   CurrentThreadCanAccessRuntime((*thingp)->runtimeFromMainThread()));
     return IsAboutToBeFinalizedFromAnyThread(thingp);
 }
 
 template <typename T>
 static bool
 IsAboutToBeFinalizedFromAnyThread(T **thingp)
 {
     MOZ_ASSERT(thingp);
     MOZ_ASSERT(*thingp);
 
     T *thing = *thingp;
     JSRuntime *rt = thing->runtimeFromAnyThread();
 
     /* Permanent atoms are never finalized by non-owning runtimes. */
-    if (ThingIsPermanentAtom(thing) && !TlsPerThreadData.get()->associatedWith(rt))
+    if (ThingIsPermanentAtomOrWellKnownSymbol(thing) && !TlsPerThreadData.get()->associatedWith(rt))
         return false;
 
     Nursery &nursery = rt->gc.nursery;
     MOZ_ASSERT_IF(!rt->isHeapMinorCollecting(), !IsInsideNursery(thing));
     if (rt->isHeapMinorCollecting()) {
         if (IsInsideNursery(thing))
             return !nursery.getForwardedPointer(thingp);
         return false;
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -32,16 +32,38 @@ class UnownedBaseShape;
 template<class> class HeapPtr;
 
 namespace jit {
 class JitCode;
 struct IonScript;
 struct VMFunction;
 }
 
+/*** Tracing ***/
+
+template <typename T>
+void
+TraceEdge(JSTracer *trc, BarrieredBase<T> *thingp, const char *name);
+
+template <typename T>
+void
+TraceRoot(JSTracer *trc, T *thingp, const char *name);
+
+template <typename T>
+void
+TraceManuallyBarrieredEdge(JSTracer *trc, T *thingp, const char *name);
+
+template <typename T>
+void
+TraceRange(JSTracer *trc, size_t len, BarrieredBase<T> *thingp, const char *name);
+
+template <typename T>
+void
+TraceRootRange(JSTracer *trc, size_t len, T *thingp, const char *name);
+
 namespace gc {
 
 /*** Object Marking ***/
 
 /*
  * These functions expose marking functionality for all of the different GC
  * thing kinds. For each GC thing, there are several variants. As an example,
  * these are the variants generated for JSObject. They are listed from most to