Bug 1460098 (Part 1) - Fix bitrot in GCTrace.cpp r=sfink
authorPaul Bone <pbone@mozilla.com>
Thu, 17 May 2018 14:00:37 +1000
changeset 472905 37dc32d486bd384cb95c085d94b4dbaaf828bcfa
parent 472904 e92d5e5095310ea59e3f787825732722b974c6f7
child 472906 ad64ea702de03e6e27b52ad4887e5fa813776904
push id9374
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:43:20 +0000
treeherdermozilla-beta@160e085dfb0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1460098
milestone62.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 1460098 (Part 1) - Fix bitrot in GCTrace.cpp r=sfink Fixup bitrot by updating code normally not compiled since JS_GC_TRACE is usually false. Move the GC tracing code into a new GCTrace class, this allows ObjectGroup to declare it as a friend so that GCTrace can call private methods there.
js/src/builtin/TypedObject.cpp
js/src/gc/Allocator.cpp
js/src/gc/GC.cpp
js/src/gc/GCTrace.cpp
js/src/gc/GCTrace.h
js/src/gc/GCTraceFormat.h
js/src/gc/Marking.cpp
js/src/gc/Nursery.cpp
js/src/jit/MacroAssembler.cpp
js/src/vm/ArrayObject-inl.h
js/src/vm/Caches-inl.h
js/src/vm/JSFunction-inl.h
js/src/vm/NativeObject-inl.h
js/src/vm/ObjectGroup.h
js/src/vm/ProxyObject.cpp
js/src/vm/TypeInference.cpp
js/src/vm/UnboxedObject.cpp
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -2329,17 +2329,17 @@ TypedObject::create(JSContext* cx, js::g
 
     TypedObject* tobj = static_cast<TypedObject*>(obj);
     tobj->initGroup(group);
     tobj->initShape(shape);
 
     MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
     cx->compartment()->setObjectPendingMetadata(cx, tobj);
 
-    js::gc::TraceCreateObject(tobj);
+    js::gc::gcTracer.traceCreateObject(tobj);
 
     return tobj;
 }
 
 /******************************************************************************
  * Intrinsics
  */
 
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -256,17 +256,17 @@ GCRuntime::tryNewTenuredThing(JSContext*
                 t = tryNewTenuredThing<T, NoGC>(cx, kind, thingSize);
             }
             if (!t)
                 ReportOutOfMemory(cx);
         }
     }
 
     checkIncrementalZoneState(cx, t);
-    TraceTenuredAlloc(t, kind);
+    gcTracer.traceTenuredAlloc(t, kind);
     return t;
 }
 
 template <AllowGC allowGC>
 bool
 GCRuntime::checkAllocatorState(JSContext* cx, AllocKind kind)
 {
     if (allowGC) {
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -589,17 +589,17 @@ Arena::finalize(FreeOp* fop, AllocKind t
                                         thing - thingSize, this);
                 newListTail = newListTail->nextSpanUnchecked(this);
             }
             firstThingOrSuccessorOfLastMarkedThing = thing + thingSize;
             nmarked++;
         } else {
             t->finalize(fop);
             JS_POISON(t, JS_SWEPT_TENURED_PATTERN, thingSize, MemCheckKind::MakeUndefined);
-            TraceTenuredFinalize(t);
+            gcTracer.traceTenuredFinalize(t);
         }
     }
 
     if (nmarked == 0) {
         // Do nothing. The caller will update the arena appropriately.
         MOZ_ASSERT(newListTail == &newListHead);
         JS_EXTRA_POISON(data, JS_SWEPT_TENURED_PATTERN, sizeof(data), MemCheckKind::MakeUndefined);
         return nmarked;
@@ -1289,17 +1289,17 @@ GCRuntime::init(uint32_t maxbytes, uint3
     }
 
 #ifdef JS_GC_ZEAL
     const char* zealSpec = getenv("JS_GC_ZEAL");
     if (zealSpec && zealSpec[0] && !parseAndSetZeal(zealSpec))
         return false;
 #endif
 
-    if (!InitTrace(*this))
+    if (!gcTracer.initTrace(*this))
         return false;
 
     if (!marker.init(mode))
         return false;
 
     if (!initSweepActions())
         return false;
 
@@ -1341,17 +1341,17 @@ GCRuntime::finish()
     }
 
     zones().clear();
 
     FreeChunkPool(fullChunks_.ref());
     FreeChunkPool(availableChunks_.ref());
     FreeChunkPool(emptyChunks_.ref());
 
-    FinishTrace();
+    gcTracer.finishTrace();
 
     nursery().printTotalProfileTimes();
     stats().printTotalProfileTimes();
 }
 
 bool
 GCRuntime::setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock)
 {
@@ -5841,17 +5841,19 @@ GCRuntime::beginSweepPhase(JS::gcreason:
 
     releaseHeldRelocatedArenas();
 
     computeNonIncrementalMarkingForValidation(session);
 
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP);
 
     sweepOnBackgroundThread =
-        reason != JS::gcreason::DESTROY_RUNTIME && !TraceEnabled() && CanUseExtraThreads();
+        reason != JS::gcreason::DESTROY_RUNTIME &&
+        !gcTracer.traceEnabled() &&
+        CanUseExtraThreads();
 
     releaseObservedTypes = shouldReleaseObservedTypes();
 
     AssertNoWrappersInGrayList(rt);
     DropStringWrappers(rt);
 
     groupZonesForSweeping(reason);
 
@@ -7466,28 +7468,28 @@ GCRuntime::gcCycle(bool nonincrementalBy
     auto result = budgetIncrementalGC(nonincrementalByAPI, reason, budget, session);
 
     // If an ongoing incremental GC was reset, we may need to restart.
     if (result == IncrementalResult::Reset) {
         MOZ_ASSERT(!isIncrementalGCInProgress());
         return result;
     }
 
-    TraceMajorGCStart();
+    gcTracer.traceMajorGCStart();
 
     incrementalCollectSlice(budget, reason, session);
 
     chunkAllocationSinceLastGC = false;
 
 #ifdef JS_GC_ZEAL
     /* Keeping these around after a GC is dangerous. */
     clearSelectedForMarking();
 #endif
 
-    TraceMajorGCEnd();
+    gcTracer.traceMajorGCEnd();
 
     return IncrementalResult::Ok;
 }
 
 #ifdef JS_GC_ZEAL
 static bool
 IsDeterministicGCReason(JS::gcreason::Reason reason)
 {
--- a/js/src/gc/GCTrace.cpp
+++ b/js/src/gc/GCTrace.cpp
@@ -6,24 +6,30 @@
 
 #ifdef JS_GC_TRACE
 
 #include "gc/GCTrace.h"
 
 #include <stdio.h>
 #include <string.h>
 
+#include "gc/AllocKind.h"
 #include "gc/GCTraceFormat.h"
-
 #include "js/HashTable.h"
+#include "vm/JSFunction.h"
+#include "vm/JSObject.h"
 
-using namespace js;
-using namespace js::gc;
+#include "gc/ObjectKind-inl.h"
 
-JS_STATIC_ASSERT(AllocKinds == unsigned(AllocKind::LIMIT));
+namespace js {
+namespace gc {
+
+GCTrace gcTracer;
+
+JS_STATIC_ASSERT(NumAllocKinds == unsigned(AllocKind::LIMIT));
 JS_STATIC_ASSERT(LastObjectAllocKind == unsigned(AllocKind::OBJECT_LAST));
 
 static FILE* gcTraceFile = nullptr;
 
 static HashSet<const Class*, DefaultHasher<const Class*>, SystemAllocPolicy> tracedClasses;
 static HashSet<const ObjectGroup*, DefaultHasher<const ObjectGroup*>, SystemAllocPolicy> tracedGroups;
 
 static inline void
@@ -71,180 +77,183 @@ TraceString(const char* string)
             char chars[charsPerWord];
         } data;
         strncpy(data.chars, string + (i * charsPerWord), charsPerWord);
         WriteWord(data.word);
     }
 }
 
 bool
-js::gc::InitTrace(GCRuntime& gc)
+GCTrace::initTrace(GCRuntime& gc)
 {
     /* This currently does not support multiple runtimes. */
     MOZ_ALWAYS_TRUE(!gcTraceFile);
 
     char* filename = getenv("JS_GC_TRACE");
     if (!filename)
         return true;
 
-    if (!tracedClasses.init() || !tracedTypes.init()) {
-        FinishTrace();
+    if (!tracedClasses.init() || !tracedGroups.init()) {
+        finishTrace();
         return false;
     }
 
     gcTraceFile = fopen(filename, "w");
     if (!gcTraceFile) {
-        FinishTrace();
+        finishTrace();
         return false;
     }
 
     TraceEvent(TraceEventInit, 0, TraceFormatVersion);
 
     /* Trace information about thing sizes. */
     for (auto kind : AllAllocKinds())
         TraceEvent(TraceEventThingSize, Arena::thingSize(kind));
 
     return true;
 }
 
 void
-js::gc::FinishTrace()
+GCTrace::finishTrace()
 {
     if (gcTraceFile) {
         fclose(gcTraceFile);
         gcTraceFile = nullptr;
     }
     tracedClasses.finish();
-    tracedTypes.finish();
+    tracedGroups.finish();
 }
 
 bool
-js::gc::TraceEnabled()
+GCTrace::traceEnabled()
 {
     return gcTraceFile != nullptr;
 }
 
 void
-js::gc::TraceNurseryAlloc(Cell* thing, size_t size)
+GCTrace::traceNurseryAlloc(Cell* thing, size_t size)
 {
     if (thing) {
         /* We don't have AllocKind here, but we can work it out from size. */
         unsigned slots = (size - sizeof(JSObject)) / sizeof(JS::Value);
         AllocKind kind = GetBackgroundAllocKind(GetGCObjectKind(slots));
-        TraceEvent(TraceEventNurseryAlloc, uint64_t(thing), kind);
+        TraceEvent(TraceEventNurseryAlloc, uint64_t(thing), uint8_t(kind));
     }
 }
 
 void
-js::gc::TraceNurseryAlloc(Cell* thing, AllocKind kind)
+GCTrace::traceNurseryAlloc(Cell* thing, AllocKind kind)
 {
     if (thing)
-        TraceEvent(TraceEventNurseryAlloc, uint64_t(thing), kind);
+        TraceEvent(TraceEventNurseryAlloc, uint64_t(thing), uint8_t(kind));
 }
 
 void
-js::gc::TraceTenuredAlloc(Cell* thing, AllocKind kind)
+GCTrace::traceTenuredAlloc(Cell* thing, AllocKind kind)
 {
     if (thing)
-        TraceEvent(TraceEventTenuredAlloc, uint64_t(thing), kind);
+        TraceEvent(TraceEventTenuredAlloc, uint64_t(thing), uint8_t(kind));
 }
 
 static void
 MaybeTraceClass(const Class* clasp)
 {
     if (tracedClasses.has(clasp))
         return;
 
     TraceEvent(TraceEventClassInfo, uint64_t(clasp));
     TraceString(clasp->name);
     TraceInt(clasp->flags);
-    TraceInt(clasp->finalize != nullptr);
+    TraceInt(clasp->hasFinalize());
 
     MOZ_ALWAYS_TRUE(tracedClasses.put(clasp));
 }
 
-static void
-MaybeTraceGroup(ObjectGroup* group)
+void
+js::gc::GCTrace::maybeTraceGroup(ObjectGroup* group)
 {
     if (tracedGroups.has(group))
         return;
 
     MaybeTraceClass(group->clasp());
     TraceEvent(TraceEventGroupInfo, uint64_t(group));
     TraceAddress(group->clasp());
-    TraceInt(group->flags());
+    TraceInt(group->flagsDontCheckGeneration());
 
     MOZ_ALWAYS_TRUE(tracedGroups.put(group));
 }
 
 void
-js::gc::TraceTypeNewScript(ObjectGroup* group)
+GCTrace::traceTypeNewScript(ObjectGroup* group)
 {
     const size_t bufLength = 128;
     static char buffer[bufLength];
-    MOZ_ASSERT(group->hasNewScript());
-    JSAtom* funName = group->newScript()->fun->displayAtom();
+
+    JSAtom* funName = group->newScriptDontCheckGeneration()->function()->displayAtom();
     if (!funName)
         return;
 
     size_t length = funName->length();
     MOZ_ALWAYS_TRUE(length < bufLength);
     CopyChars(reinterpret_cast<Latin1Char*>(buffer), *funName);
     buffer[length] = 0;
 
     TraceEvent(TraceEventTypeNewScript, uint64_t(group));
     TraceString(buffer);
 }
 
 void
-js::gc::TraceCreateObject(JSObject* object)
+GCTrace::traceCreateObject(JSObject* object)
 {
     if (!gcTraceFile)
         return;
 
     ObjectGroup* group = object->group();
-    MaybeTraceGroup(group);
+    maybeTraceGroup(group);
     TraceEvent(TraceEventCreateObject, uint64_t(object));
     TraceAddress(group);
 }
 
 void
-js::gc::TraceMinorGCStart()
+GCTrace::traceMinorGCStart()
 {
     TraceEvent(TraceEventMinorGCStart);
 }
 
 void
-js::gc::TracePromoteToTenured(Cell* src, Cell* dst)
+GCTrace::tracePromoteToTenured(Cell* src, Cell* dst)
 {
     TraceEvent(TraceEventPromoteToTenured, uint64_t(src));
     TraceAddress(dst);
 }
 
 void
-js::gc::TraceMinorGCEnd()
+GCTrace::traceMinorGCEnd()
 {
     TraceEvent(TraceEventMinorGCEnd);
 }
 
 void
-js::gc::TraceMajorGCStart()
+GCTrace::traceMajorGCStart()
 {
     TraceEvent(TraceEventMajorGCStart);
 }
 
 void
-js::gc::TraceTenuredFinalize(Cell* thing)
+GCTrace::traceTenuredFinalize(Cell* thing)
 {
     if (!gcTraceFile)
         return;
-    if (thing->tenuredGetAllocKind() == AllocKind::OBJECT_GROUP)
+    if (thing->asTenured().getAllocKind() == AllocKind::OBJECT_GROUP)
         tracedGroups.remove(static_cast<const ObjectGroup*>(thing));
     TraceEvent(TraceEventTenuredFinalize, uint64_t(thing));
 }
 
 void
-js::gc::TraceMajorGCEnd()
+GCTrace::traceMajorGCEnd()
 {
     TraceEvent(TraceEventMajorGCEnd);
 }
 
+} // js
+} // gc
+
 #endif
--- a/js/src/gc/GCTrace.h
+++ b/js/src/gc/GCTrace.h
@@ -10,48 +10,62 @@
 #include "gc/Heap.h"
 
 namespace js {
 
 class ObjectGroup;
 
 namespace gc {
 
+/*
+ * Tracing code is declared within this class, so the class can be a friend of
+ * something and access private details used for tracing.
+ */
+class GCTrace {
+  public:
+    GCTrace() { };
+
 #ifdef JS_GC_TRACE
 
-extern MOZ_MUST_USE bool InitTrace(GCRuntime& gc);
-extern void FinishTrace();
-extern bool TraceEnabled();
-extern void TraceNurseryAlloc(Cell* thing, size_t size);
-extern void TraceNurseryAlloc(Cell* thing, AllocKind kind);
-extern void TraceTenuredAlloc(Cell* thing, AllocKind kind);
-extern void TraceCreateObject(JSObject* object);
-extern void TraceMinorGCStart();
-extern void TracePromoteToTenured(Cell* src, Cell* dst);
-extern void TraceMinorGCEnd();
-extern void TraceMajorGCStart();
-extern void TraceTenuredFinalize(Cell* thing);
-extern void TraceMajorGCEnd();
-extern void TraceTypeNewScript(js::ObjectGroup* group);
+    MOZ_MUST_USE bool initTrace(GCRuntime& gc);
+    void finishTrace();
+    bool traceEnabled();
+    void traceNurseryAlloc(Cell* thing, size_t size);
+    void traceNurseryAlloc(Cell* thing, AllocKind kind);
+    void traceTenuredAlloc(Cell* thing, AllocKind kind);
+    void traceCreateObject(JSObject* object);
+    void traceMinorGCStart();
+    void tracePromoteToTenured(Cell* src, Cell* dst);
+    void traceMinorGCEnd();
+    void traceMajorGCStart();
+    void traceTenuredFinalize(Cell* thing);
+    void traceMajorGCEnd();
+    void traceTypeNewScript(js::ObjectGroup* group);
+
+private:
+    void maybeTraceGroup(ObjectGroup* group);
 
 #else
 
-inline MOZ_MUST_USE bool InitTrace(GCRuntime& gc) { return true; }
-inline void FinishTrace() {}
-inline bool TraceEnabled() { return false; }
-inline void TraceNurseryAlloc(Cell* thing, size_t size) {}
-inline void TraceNurseryAlloc(Cell* thing, AllocKind kind) {}
-inline void TraceTenuredAlloc(Cell* thing, AllocKind kind) {}
-inline void TraceCreateObject(JSObject* object) {}
-inline void TraceMinorGCStart() {}
-inline void TracePromoteToTenured(Cell* src, Cell* dst) {}
-inline void TraceMinorGCEnd() {}
-inline void TraceMajorGCStart() {}
-inline void TraceTenuredFinalize(Cell* thing) {}
-inline void TraceMajorGCEnd() {}
-inline void TraceTypeNewScript(js::ObjectGroup* group) {}
+    MOZ_MUST_USE bool initTrace(GCRuntime& gc) { return true; }
+    void finishTrace() {}
+    bool traceEnabled() { return false; }
+    void traceNurseryAlloc(Cell* thing, size_t size) {}
+    void traceNurseryAlloc(Cell* thing, AllocKind kind) {}
+    void traceTenuredAlloc(Cell* thing, AllocKind kind) {}
+    void traceCreateObject(JSObject* object) {}
+    void traceMinorGCStart() {}
+    void tracePromoteToTenured(Cell* src, Cell* dst) {}
+    void traceMinorGCEnd() {}
+    void traceMajorGCStart() {}
+    void traceTenuredFinalize(Cell* thing) {}
+    void traceMajorGCEnd() {}
+    void traceTypeNewScript(js::ObjectGroup* group) {}
 
 #endif
+}; /* GCTrace */
+
+extern GCTrace gcTracer;
 
 } /* namespace gc */
 } /* namespace js */
 
 #endif
--- a/js/src/gc/GCTraceFormat.h
+++ b/js/src/gc/GCTraceFormat.h
@@ -18,17 +18,17 @@
 
 enum GCTraceEvent {
     // Events
     TraceEventInit,
     TraceEventThingSize,
     TraceEventNurseryAlloc,
     TraceEventTenuredAlloc,
     TraceEventClassInfo,
-    TraceEventTypeInfo,
+    TraceEventGroupInfo,
     TraceEventTypeNewScript,
     TraceEventCreateObject,
     TraceEventMinorGCStart,
     TraceEventPromoteToTenured,
     TraceEventMinorGCEnd,
     TraceEventMajorGCStart,
     TraceEventTenuredFinalize,
     TraceEventMajorGCEnd,
@@ -45,12 +45,12 @@ const unsigned TraceFormatVersion = 1;
 const unsigned TracePayloadBits = 48;
 
 const unsigned TraceExtraShift = 48;
 const unsigned TraceExtraBits = 8;
 
 const unsigned TraceEventShift = 56;
 const unsigned TraceEventBits = 8;
 
-const unsigned AllocKinds = 22;
-const unsigned LastObjectAllocKind = 11;
+const unsigned NumAllocKinds = 29;
+const unsigned LastObjectAllocKind = 13;
 
 #endif
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -3098,17 +3098,17 @@ js::TenuringTracer::moveToTenuredSlow(JS
         MOZ_ASSERT_IF(src->getClass()->hasFinalize(),
                       CanNurseryAllocateFinalizedClass(src->getClass()));
     }
 
     RelocationOverlay* overlay = RelocationOverlay::fromCell(src);
     overlay->forwardTo(dst);
     insertIntoObjectFixupList(overlay);
 
-    TracePromoteToTenured(src, dst);
+    gcTracer.tracePromoteToTenured(src, dst);
     return dst;
 }
 
 inline JSObject*
 js::TenuringTracer::movePlainObjectToTenured(PlainObject* src)
 {
     // Fast path version of moveToTenuredSlow() for specialized for PlainObject.
 
@@ -3130,17 +3130,17 @@ js::TenuringTracer::movePlainObjectToTen
     tenuredSize += moveElementsToTenured(dst, src, dstKind);
 
     MOZ_ASSERT(!dst->getClass()->extObjectMovedOp());
 
     RelocationOverlay* overlay = RelocationOverlay::fromCell(src);
     overlay->forwardTo(dst);
     insertIntoObjectFixupList(overlay);
 
-    TracePromoteToTenured(src, dst);
+    gcTracer.tracePromoteToTenured(src, dst);
     return dst;
 }
 
 size_t
 js::TenuringTracer::moveSlotsToTenured(NativeObject* dst, NativeObject* src)
 {
     /* Fixed slots have already been copied over. */
     if (!src->hasDynamicSlots())
@@ -3243,17 +3243,17 @@ js::TenuringTracer::moveToTenured(JSStri
     }
     JSString* dst = reinterpret_cast<JSString*>(t);
     tenuredSize += moveStringToTenured(dst, src, dstKind);
 
     RelocationOverlay* overlay = RelocationOverlay::fromCell(src);
     overlay->forwardTo(dst);
     insertIntoStringFixupList(overlay);
 
-    TracePromoteToTenured(src, dst);
+    gcTracer.tracePromoteToTenured(src, dst);
     return dst;
 }
 
 void
 js::Nursery::collectToFixedPoint(TenuringTracer& mover, TenureCountCache& tenureCounts)
 {
     for (RelocationOverlay* p = mover.objHead; p; p = p->next()) {
         JSObject* obj = static_cast<JSObject*>(p->forwardingAddress());
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -324,34 +324,34 @@ js::Nursery::allocateObject(JSContext* c
     }
 
     /* Store slots pointer directly in new object. If no dynamic slots were
      * requested, caller must initialize slots_ field itself as needed. We
      * don't know if the caller was a native object or not. */
     if (nDynamicSlots)
         static_cast<NativeObject*>(obj)->initSlots(slots);
 
-    TraceNurseryAlloc(obj, size);
+    gcTracer.traceNurseryAlloc(obj, size);
     return obj;
 }
 
 Cell*
 js::Nursery::allocateString(Zone* zone, size_t size, AllocKind kind)
 {
     /* Ensure there's enough space to replace the contents with a RelocationOverlay. */
     MOZ_ASSERT(size >= sizeof(RelocationOverlay));
 
     size_t allocSize = JS_ROUNDUP(sizeof(StringLayout) - 1 + size, CellAlignBytes);
     auto header = static_cast<StringLayout*>(allocate(allocSize));
     if (!header)
         return nullptr;
     header->zone = zone;
 
     auto cell = reinterpret_cast<Cell*>(&header->cell);
-    TraceNurseryAlloc(cell, kind);
+    gcTracer.traceNurseryAlloc(cell, kind);
     return cell;
 }
 
 void*
 js::Nursery::allocate(size_t size)
 {
     MOZ_ASSERT(isEnabled());
     MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy());
@@ -717,17 +717,17 @@ js::Nursery::collect(JS::gcreason::Reaso
     if (rt->gc.hasZealMode(ZealMode::CheckNursery)) {
         for (auto canary = lastCanary_; canary; canary = canary->next)
             MOZ_ASSERT(canary->magicValue == CanaryMagicValue);
     }
     lastCanary_ = nullptr;
 #endif
 
     rt->gc.stats().beginNurseryCollection(reason);
-    TraceMinorGCStart();
+    gcTracer.traceMinorGCStart();
 
     maybeClearProfileDurations();
     startProfile(ProfileKey::Total);
 
     // The analysis marks TenureCount as not problematic for GC hazards because
     // it is only used here, and ObjectGroup pointers are never
     // nursery-allocated.
     MOZ_ASSERT(!IsNurseryAllocable(AllocKind::OBJECT_GROUP));
@@ -813,17 +813,17 @@ js::Nursery::collect(JS::gcreason::Reaso
     rt->addTelemetry(JS_TELEMETRY_GC_MINOR_US, totalTime.ToMicroseconds());
     rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON, reason);
     if (totalTime.ToMilliseconds() > 1.0)
         rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON_LONG, reason);
     rt->addTelemetry(JS_TELEMETRY_GC_NURSERY_BYTES, sizeOfHeapCommitted());
     rt->addTelemetry(JS_TELEMETRY_GC_PRETENURE_COUNT, pretenureCount);
 
     rt->gc.stats().endNurseryCollection(reason);
-    TraceMinorGCEnd();
+    gcTracer.traceMinorGCEnd();
     timeInChunkAlloc_ = mozilla::TimeDuration();
 
     if (enableProfiling_ && totalTime >= profileThreshold_) {
         rt->gc.stats().maybePrintProfileHeaders();
 
         fprintf(stderr, "MinorGC: %20s %5.1f%% %4u        ",
                 JS::gcreason::ExplainReason(reason),
                 promotionRate * 100,
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -808,17 +808,17 @@ MacroAssembler::storeUnboxedProperty(Bas
                                      const ConstantOrRegister& value, Label* failure);
 
 // Inlined version of gc::CheckAllocatorState that checks the bare essentials
 // and bails for anything that cannot be handled with our jit allocators.
 void
 MacroAssembler::checkAllocatorState(Label* fail)
 {
     // Don't execute the inline path if we are tracing allocations.
-    if (js::gc::TraceEnabled())
+    if (js::gc::gcTracer.traceEnabled())
         jump(fail);
 
 #ifdef JS_GC_ZEAL
     // Don't execute the inline path if gc zeal or tracing are active.
     branch32(Assembler::NotEqual,
              AbsoluteAddress(GetJitContext()->runtime->addressOfGCZealModeBits()), Imm32(0),
              fail);
 #endif
@@ -1359,16 +1359,25 @@ MacroAssembler::initGCSlots(Register obj
         } else {
             fillSlotsWithUndefined(Address(obj, 0), temp, 0, ndynamic);
         }
 
         pop(obj);
     }
 }
 
+#ifdef JS_GC_TRACE
+static void
+TraceCreateObject(JSObject *obj)
+{
+    AutoUnsafeCallWithABI unsafe;
+    js::gc::gcTracer.traceCreateObject(obj);
+}
+#endif
+
 void
 MacroAssembler::initGCThing(Register obj, Register temp, const TemplateObject& templateObj,
                             bool initContents)
 {
     // Fast initialization of an empty object returned by allocateObject().
 
     storePtr(ImmGCPtr(templateObj.group()), Address(obj, JSObject::offsetOfGroup()));
 
@@ -1451,28 +1460,28 @@ MacroAssembler::initGCThing(Register obj
         storePtr(ImmPtr(nullptr), Address(obj, UnboxedPlainObject::offsetOfExpando()));
         if (initContents)
             initUnboxedObjectContents(obj, templateObj.unboxedObjectLayout());
     } else {
         MOZ_CRASH("Unknown object");
     }
 
 #ifdef JS_GC_TRACE
-    RegisterSet regs = RegisterSet::Volatile();
-    PushRegsInMask(regs);
+    AllocatableRegisterSet regs(RegisterSet::Volatile());
+    LiveRegisterSet save(regs.asLiveSet());
+    PushRegsInMask(save);
+
     regs.takeUnchecked(obj);
-    Register temp = regs.takeAnyGeneral();
-
-    setupUnalignedABICall(temp);
+    Register temp2 = regs.takeAnyGeneral();
+
+    setupUnalignedABICall(temp2);
     passABIArg(obj);
-    movePtr(ImmGCPtr(templateObj->type()), temp);
-    passABIArg(temp);
-    callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::gc::TraceCreateObject));
-
-    PopRegsInMask(RegisterSet::Volatile());
+    callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceCreateObject));
+
+    PopRegsInMask(save);
 #endif
 }
 
 void
 MacroAssembler::initUnboxedObjectContents(Register object, const UnboxedLayout& layout)
 {
     // Initialize reference fields of the object, per UnboxedPlainObject::create.
     if (const int32_t* list = layout.traceList()) {
--- a/js/src/vm/ArrayObject-inl.h
+++ b/js/src/vm/ArrayObject-inl.h
@@ -68,17 +68,17 @@ ArrayObject::createArrayInternal(JSConte
 
 /* static */ inline ArrayObject*
 ArrayObject::finishCreateArray(ArrayObject* obj, HandleShape shape, AutoSetNewObjectMetadata& metadata)
 {
     size_t span = shape->slotSpan();
     if (span)
         obj->initializeSlotRange(0, span);
 
-    gc::TraceCreateObject(obj);
+    gc::gcTracer.traceCreateObject(obj);
 
     return obj;
 }
 
 /* static */ inline ArrayObject*
 ArrayObject::createArray(JSContext* cx, gc::AllocKind kind, gc::InitialHeap heap,
                          HandleShape shape, HandleObjectGroup group,
                          uint32_t length, AutoSetNewObjectMetadata& metadata)
--- a/js/src/vm/Caches-inl.h
+++ b/js/src/vm/Caches-inl.h
@@ -71,15 +71,15 @@ NewObjectCache::newObjectFromHit(JSConte
     copyCachedToObject(obj, templateObj, entry->kind);
 
     if (group->clasp()->shouldDelayMetadataBuilder())
         cx->compartment()->setObjectPendingMetadata(cx, obj);
     else
         obj = static_cast<NativeObject*>(SetNewObjectMetadata(cx, obj));
 
     probes::CreateObject(cx, obj);
-    gc::TraceCreateObject(obj);
+    gc::gcTracer.traceCreateObject(obj);
     return obj;
 }
 
 }  /* namespace js */
 
 #endif /* vm_Caches_inl_h */
--- a/js/src/vm/JSFunction-inl.h
+++ b/js/src/vm/JSFunction-inl.h
@@ -139,14 +139,14 @@ JSFunction::create(JSContext* cx, js::gc
     }
 
     MOZ_ASSERT(!clasp->shouldDelayMetadataBuilder(),
                "Function has no extra data hanging off it, that wouldn't be "
                "allocated at this point, that would require delaying the "
                "building of metadata for it");
     fun = SetNewObjectMetadata(cx, fun);
 
-    js::gc::TraceCreateObject(fun);
+    js::gc::gcTracer.traceCreateObject(fun);
 
     return fun;
 }
 
 #endif /* vm_JSFunction_inl_h */
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -552,17 +552,17 @@ NativeObject::create(JSContext* cx, js::
     if (size_t span = shape->slotSpan())
         nobj->initializeSlotRange(0, span);
 
     if (clasp->shouldDelayMetadataBuilder())
         cx->compartment()->setObjectPendingMetadata(cx, nobj);
     else
         nobj = SetNewObjectMetadata(cx, nobj);
 
-    js::gc::TraceCreateObject(nobj);
+    js::gc::gcTracer.traceCreateObject(nobj);
 
     return nobj;
 }
 
 /* static */ inline JS::Result<NativeObject*, JS::OOM&>
 NativeObject::createWithTemplate(JSContext* cx, js::gc::InitialHeap heap,
                                  HandleObject templateObject)
 {
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -6,16 +6,17 @@
 
 #ifndef vm_ObjectGroup_h
 #define vm_ObjectGroup_h
 
 #include "jsfriendapi.h"
 
 #include "ds/IdValuePair.h"
 #include "gc/Barrier.h"
+#include "gc/GCTrace.h"
 #include "js/CharacterEncoding.h"
 #include "js/GCHashTable.h"
 #include "js/TypeDecls.h"
 #include "vm/TaggedProto.h"
 #include "vm/TypeInference.h"
 
 namespace js {
 
@@ -82,16 +83,17 @@ enum NewObjectKind {
  * to the property (whether those uncovered by analysis or those occurring
  * in the VM) will treat these properties like those of any other object group.
  */
 
 /* Type information about an object accessed by a script. */
 class ObjectGroup : public gc::TenuredCell
 {
     friend class gc::GCRuntime;
+    friend class gc::GCTrace;
 
     /* Class shared by objects in this group. */
     const Class* clasp_;
 
     /* Prototype shared by objects in this group. */
     GCPtr<TaggedProto> proto_;
 
     /* Compartment shared by objects in this group. */
--- a/js/src/vm/ProxyObject.cpp
+++ b/js/src/vm/ProxyObject.cpp
@@ -186,17 +186,17 @@ ProxyObject::create(JSContext* cx, const
 
     ProxyObject* pobj = static_cast<ProxyObject*>(obj);
     pobj->initGroup(group);
     pobj->initShape(shape);
 
     MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
     cx->compartment()->setObjectPendingMetadata(cx, pobj);
 
-    js::gc::TraceCreateObject(pobj);
+    js::gc::gcTracer.traceCreateObject(pobj);
 
     if (newKind == SingletonObject) {
         Rooted<ProxyObject*> pobjRoot(cx, pobj);
         if (!JSObject::setSingleton(cx, pobjRoot))
             return cx->alreadyReportedOOM();
         pobj = pobjRoot;
     }
 
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3691,17 +3691,17 @@ TypeNewScript::make(JSContext* cx, Objec
     newScript->function_ = fun;
 
     newScript->preliminaryObjects = group->zone()->new_<PreliminaryObjectArray>();
     if (!newScript->preliminaryObjects)
         return true;
 
     group->setNewScript(newScript.forget());
 
-    gc::TraceTypeNewScript(group);
+    gc::gcTracer.traceTypeNewScript(group);
     return true;
 }
 
 // Make a TypeNewScript with the same initializer list as |newScript| but with
 // a new template object.
 /* static */ TypeNewScript*
 TypeNewScript::makeNativeVersion(JSContext* cx, TypeNewScript* newScript,
                                  PlainObject* templateObject)
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -579,17 +579,17 @@ UnboxedLayout::makeNativeGroup(JSContext
             return false;
 
         TypeNewScript* replacementNewScript =
             TypeNewScript::makeNativeVersion(cx, layout.newScript(), templateObject);
         if (!replacementNewScript)
             return false;
 
         replacementGroup->setNewScript(replacementNewScript);
-        gc::TraceTypeNewScript(replacementGroup);
+        gc::gcTracer.traceTypeNewScript(replacementGroup);
 
         group->clearNewScript(cx, replacementGroup);
     }
 
     // Similarly, if this group is keyed to an allocation site, replace its
     // entry with a new group that has no unboxed layout.
     if (layout.allocationScript()) {
         RootedScript script(cx, layout.allocationScript());
@@ -776,17 +776,17 @@ UnboxedObject::createInternal(JSContext*
         return cx->alreadyReportedOOM();
 
     UnboxedObject* uobj = static_cast<UnboxedObject*>(obj);
     uobj->initGroup(group);
 
     MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
     cx->compartment()->setObjectPendingMetadata(cx, uobj);
 
-    js::gc::TraceCreateObject(uobj);
+    js::gc::gcTracer.traceCreateObject(uobj);
 
     return uobj;
 }
 
 /* static */
 UnboxedPlainObject*
 UnboxedPlainObject::create(JSContext* cx, HandleObjectGroup group, NewObjectKind newKind)
 {