Bug 1380030 - Remove color constants from public API and replace with an internal MarkColor enum r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 12 Jul 2017 18:31:55 +0100
changeset 607723 933aa2989b9af7f7746c5b45b41c5ff3ce52cd20
parent 607722 04364ec85017d7646b11b1ed1f3e1cf61a59075b
child 607724 872e9c8fec61fbc0596db21b3002472dea32d864
push id68095
push userbmo:rbarker@mozilla.com
push dateWed, 12 Jul 2017 20:01:47 +0000
reviewerssfink
bugs1380030
milestone56.0a1
Bug 1380030 - Remove color constants from public API and replace with an internal MarkColor enum r=sfink
js/public/HeapAPI.h
js/src/gc/Heap.h
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/jsapi-tests/testGCGrayMarking.cpp
js/src/jsapi-tests/testGCHeapPostBarriers.cpp
js/src/jsgc.cpp
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -53,26 +53,20 @@ const size_t ChunkMarkBitmapBits = 12902
 const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
 const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t);
 const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize;
 const size_t ArenaZoneOffset = sizeof(size_t);
 const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) +
                                sizeof(size_t) + sizeof(uintptr_t);
 
 /*
- * Live objects are marked black. How many other additional colors are available
- * depends on the size of the GCThing. Objects marked gray are eligible for
- * cycle collection.
- */
-static const uint32_t BLACK = 0;
-static const uint32_t GRAY = 1;
-
-/*
- * Two bits determine the mark color as follows:
- *    BlackBit      GrayOrBlackBit   color
+ * Live objects are marked black or gray. Everything reachable from a JS root is
+ * marked black. Objects marked gray are eligible for cycle collection.
+ *
+ *    BlackBit:     GrayOrBlackBit:  Color:
  *       0               0           white
  *       0               1           gray
  *       1               0           black
  *       1               1           black
  */
 enum class ColorBit : uint32_t
 {
     BlackBit = 0,
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -237,16 +237,23 @@ FOR_EACH_ALLOCKIND(EXPAND_ELEMENT)
 
 /*
  * This must be an upper bound, but we do not need the least upper bound, so
  * we just exclude non-background objects.
  */
 static const size_t MAX_BACKGROUND_FINALIZE_KINDS =
     size_t(AllocKind::LIMIT) - size_t(AllocKind::OBJECT_LIMIT) / 2;
 
+/* Mark colors to pass to markIfUnmarked. */
+enum class MarkColor : uint32_t
+{
+    Black = 0,
+    Gray
+};
+
 class TenuredCell;
 
 // A GC cell is the base class for all GC things.
 struct Cell
 {
   public:
     MOZ_ALWAYS_INLINE bool isTenured() const { return !IsInsideNursery(this); }
     MOZ_ALWAYS_INLINE const TenuredCell& asTenured() const;
@@ -294,17 +301,17 @@ class TenuredCell : public Cell
     static MOZ_ALWAYS_INLINE const TenuredCell* fromPointer(const void* ptr);
 
     // Mark bit management.
     MOZ_ALWAYS_INLINE bool isMarkedAny() const;
     MOZ_ALWAYS_INLINE bool isMarkedBlack() const;
     MOZ_ALWAYS_INLINE bool isMarkedGray() const;
 
     // The return value indicates if the cell went from unmarked to marked.
-    MOZ_ALWAYS_INLINE bool markIfUnmarked(uint32_t color = BLACK) const;
+    MOZ_ALWAYS_INLINE bool markIfUnmarked(MarkColor color = MarkColor::Black) const;
     MOZ_ALWAYS_INLINE void markBlack() const;
     MOZ_ALWAYS_INLINE void copyMarkBitsFrom(const TenuredCell* src);
 
     // Access to the arena.
     inline Arena* arena() const;
     inline AllocKind getAllocKind() const;
     inline JS::TraceKind getTraceKind() const;
     inline JS::Zone* zone() const;
@@ -892,76 +899,87 @@ const size_t ChunkBytesAvailable = Chunk
 const size_t ArenasPerChunk = ChunkBytesAvailable / BytesPerArenaWithHeader;
 
 #ifdef JS_GC_SMALL_CHUNK_SIZE
 static_assert(ArenasPerChunk == 62, "Do not accidentally change our heap's density.");
 #else
 static_assert(ArenasPerChunk == 252, "Do not accidentally change our heap's density.");
 #endif
 
+static inline void
+AssertValidColorBit(const TenuredCell* thing, ColorBit colorBit)
+{
+#ifdef DEBUG
+    Arena* arena = thing->arena();
+    MOZ_ASSERT(unsigned(colorBit) < arena->getThingSize() / CellBytesPerMarkBit);
+#endif
+}
+
 /* A chunk bitmap contains enough mark bits for all the cells in a chunk. */
 struct ChunkBitmap
 {
     volatile uintptr_t bitmap[ArenaBitmapWords * ArenasPerChunk];
 
   public:
     ChunkBitmap() { }
 
-    MOZ_ALWAYS_INLINE void getMarkWordAndMask(const Cell* cell, ColorBit colorBit,
+    MOZ_ALWAYS_INLINE void getMarkWordAndMask(const TenuredCell* cell, ColorBit colorBit,
                                               uintptr_t** wordp, uintptr_t* maskp)
     {
         detail::GetGCThingMarkWordAndMask(uintptr_t(cell), colorBit, wordp, maskp);
     }
 
-    MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool markBit(const Cell* cell, ColorBit colorBit) {
+    MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool markBit(const TenuredCell* cell, ColorBit colorBit) {
+        AssertValidColorBit(cell, colorBit);
         uintptr_t* word, mask;
         getMarkWordAndMask(cell, colorBit, &word, &mask);
         return *word & mask;
     }
 
-    MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarkedAny(const Cell* cell) {
+    MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarkedAny(const TenuredCell* cell) {
         return markBit(cell, ColorBit::BlackBit) || markBit(cell, ColorBit::GrayOrBlackBit);
     }
 
-    MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarkedBlack(const Cell* cell) {
+    MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarkedBlack(const TenuredCell* cell) {
         return markBit(cell, ColorBit::BlackBit);
     }
 
-    MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarkedGray(const Cell* cell) {
+    MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarkedGray(const TenuredCell* cell) {
         return !markBit(cell, ColorBit::BlackBit) && markBit(cell, ColorBit::GrayOrBlackBit);
     }
 
     // The return value indicates if the cell went from unmarked to marked.
-    MOZ_ALWAYS_INLINE bool markIfUnmarked(const Cell* cell, uint32_t color) {
+    MOZ_ALWAYS_INLINE bool markIfUnmarked(const TenuredCell* cell, MarkColor color) {
         uintptr_t* word, mask;
         getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask);
         if (*word & mask)
             return false;
-        if (color == BLACK) {
+        if (color == MarkColor::Black) {
             *word |= mask;
         } else {
             /*
              * We use getMarkWordAndMask to recalculate both mask and word as
              * doing just mask << color may overflow the mask.
              */
             getMarkWordAndMask(cell, ColorBit::GrayOrBlackBit, &word, &mask);
             if (*word & mask)
                 return false;
             *word |= mask;
         }
         return true;
     }
 
-    MOZ_ALWAYS_INLINE void markBlack(const Cell* cell) {
+    MOZ_ALWAYS_INLINE void markBlack(const TenuredCell* cell) {
         uintptr_t* word, mask;
         getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask);
         *word |= mask;
     }
 
-    MOZ_ALWAYS_INLINE void copyMarkBit(Cell* dst, const TenuredCell* src, ColorBit colorBit) {
+    MOZ_ALWAYS_INLINE void copyMarkBit(TenuredCell* dst, const TenuredCell* src,
+                                       ColorBit colorBit) {
         uintptr_t* srcWord, srcMask;
         getMarkWordAndMask(src, colorBit, &srcWord, &srcMask);
 
         uintptr_t* dstWord, dstMask;
         getMarkWordAndMask(dst, colorBit, &dstWord, &dstMask);
 
         *dstWord &= ~dstMask;
         if (*srcWord & srcMask)
@@ -974,17 +992,18 @@ struct ChunkBitmap
 
     uintptr_t* arenaBits(Arena* arena) {
         static_assert(ArenaBitmapBits == ArenaBitmapWords * JS_BITS_PER_WORD,
                       "We assume that the part of the bitmap corresponding to the arena "
                       "has the exact number of words so we do not need to deal with a word "
                       "that covers bits from two arenas.");
 
         uintptr_t* word, unused;
-        getMarkWordAndMask(reinterpret_cast<Cell*>(arena->address()), ColorBit::BlackBit, &word, &unused);
+        getMarkWordAndMask(reinterpret_cast<TenuredCell*>(arena->address()),
+                           ColorBit::BlackBit, &word, &unused);
         return word;
     }
 };
 
 static_assert(ArenaBitmapBytes * ArenasPerChunk == sizeof(ChunkBitmap),
               "Ensure our ChunkBitmap actually covers all arenas.");
 static_assert(js::gc::ChunkMarkBitmapBits == ArenaBitmapBits * ArenasPerChunk,
               "Ensure that the mark bitmap has the right number of bits.");
@@ -1150,25 +1169,16 @@ Arena::checkAddress() const
 }
 
 inline Chunk*
 Arena::chunk() const
 {
     return Chunk::fromAddress(address());
 }
 
-static void
-AssertValidColor(const TenuredCell* thing, uint32_t color)
-{
-#ifdef DEBUG
-    Arena* arena = thing->arena();
-    MOZ_ASSERT(color < arena->getThingSize() / CellBytesPerMarkBit);
-#endif
-}
-
 MOZ_ALWAYS_INLINE const TenuredCell&
 Cell::asTenured() const
 {
     MOZ_ASSERT(isTenured());
     return *static_cast<const TenuredCell*>(this);
 }
 
 MOZ_ALWAYS_INLINE TenuredCell&
@@ -1284,19 +1294,18 @@ TenuredCell::isMarkedBlack() const
 bool
 TenuredCell::isMarkedGray() const
 {
     MOZ_ASSERT(arena()->allocated());
     return chunk()->bitmap.isMarkedGray(this);
 }
 
 bool
-TenuredCell::markIfUnmarked(uint32_t color /* = BLACK */) const
+TenuredCell::markIfUnmarked(MarkColor color /* = Black */) const
 {
-    AssertValidColor(this, color);
     return chunk()->bitmap.markIfUnmarked(this, color);
 }
 
 void
 TenuredCell::markBlack() const
 {
     chunk()->bitmap.markBlack(this);
 }
@@ -1449,18 +1458,18 @@ static const int32_t ChunkLocationOffset
     int32_t(gc::ChunkLocationOffset) - int32_t(gc::ChunkMask);
 
 } /* namespace gc */
 
 namespace debug {
 
 // Utility functions meant to be called from an interactive debugger.
 enum class MarkInfo : int {
-    BLACK = js::gc::BLACK,
-    GRAY = js::gc::GRAY,
+    BLACK = 0,
+    GRAY = 1,
     UNMARKED = -1,
     NURSERY = -2,
 };
 
 // Get the mark color for a cell, in a way easily usable from a debugger.
 MOZ_NEVER_INLINE MarkInfo
 GetMarkInfo(js::gc::Cell* cell);
 
@@ -1484,17 +1493,17 @@ GetMarkInfo(js::gc::Cell* cell);
 // execution if the $mask bit happened to already be set. gdb does not expose
 // enough information to restrict the watchpoint to just a single bit.
 
 // Return the address of the word containing the mark bits for the given cell,
 // or nullptr if the cell is in the nursery.
 MOZ_NEVER_INLINE uintptr_t*
 GetMarkWordAddress(js::gc::Cell* cell);
 
-// Return the mask for the given cell and color, or 0 if the cell is in the
+// Return the mask for the given cell and color bit, or 0 if the cell is in the
 // nursery.
 MOZ_NEVER_INLINE uintptr_t
-GetMarkMask(js::gc::Cell* cell, uint32_t color);
+GetMarkMask(js::gc::Cell* cell, uint32_t colorBit);
 
 } /* namespace debug */
 } /* namespace js */
 
 #endif /* gc_Heap_h */
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -239,17 +239,17 @@ js::CheckTracedThing(JSTracer* trc, T* t
 
     MOZ_ASSERT_IF(zone->requireGCTracer(), isGcMarkingTracer || IsBufferGrayRootsTracer(trc));
 
     if (isGcMarkingTracer) {
         GCMarker* gcMarker = GCMarker::fromTracer(trc);
         MOZ_ASSERT_IF(gcMarker->shouldCheckCompartments(),
                       zone->isCollecting() || zone->isAtomsZone());
 
-        MOZ_ASSERT_IF(gcMarker->markColor() == GRAY,
+        MOZ_ASSERT_IF(gcMarker->markColor() == MarkColor::Gray,
                       !zone->isGCMarkingBlack() || zone->isAtomsZone());
 
         MOZ_ASSERT(!(zone->isGCSweeping() || zone->isGCFinished() || zone->isGCCompacting()));
     }
 
     /*
      * Try to assert that the thing is allocated.
      *
@@ -290,27 +290,26 @@ JS_FOR_EACH_TRACEKIND(IMPL_CHECK_TRACED_
 } // namespace js
 
 static bool
 ShouldTraceCrossCompartment(JSTracer* trc, JSObject* src, Cell* cell)
 {
     if (!trc->isMarkingTracer())
         return true;
 
-    uint32_t color = GCMarker::fromTracer(trc)->markColor();
-    MOZ_ASSERT(color == BLACK || color == GRAY);
+    MarkColor color = GCMarker::fromTracer(trc)->markColor();
 
     if (!cell->isTenured()) {
-        MOZ_ASSERT(color == BLACK);
+        MOZ_ASSERT(color == MarkColor::Black);
         return false;
     }
     TenuredCell& tenured = cell->asTenured();
 
     JS::Zone* zone = tenured.zone();
-    if (color == BLACK) {
+    if (color == MarkColor::Black) {
         /*
          * Having black->gray edges violates our promise to the cycle
          * collector. This can happen if we're collecting a compartment and it
          * has an edge to an uncollected compartment: it's possible that the
          * source and destination of the cross-compartment edge should be gray,
          * but the source was marked black by the write barrier.
          */
         if (tenured.isMarkedGray()) {
@@ -614,17 +613,17 @@ js::TraceProcessGlobalRoot(JSTracer* trc
     // We have to mark permanent atoms and well-known symbols through a special
     // method because the default DoMarking implementation automatically skips
     // them. Fortunately, atoms (permanent and non) cannot refer to other GC
     // things so they do not need to go through the mark stack and may simply
     // be marked directly.  Moreover, well-known symbols can refer only to
     // permanent atoms, so likewise require no subsquent marking.
     CheckTracedThing(trc, *ConvertToBase(&thing));
     if (trc->isMarkingTracer())
-        thing->markIfUnmarked(gc::BLACK);
+        thing->markIfUnmarked(gc::MarkColor::Black);
     else
         DoCallback(trc->asCallbackTracer(), ConvertToBase(&thing), name);
 }
 template void js::TraceProcessGlobalRoot<JSAtom>(JSTracer*, JSAtom*, const char*);
 template void js::TraceProcessGlobalRoot<JS::Symbol>(JSTracer*, JS::Symbol*, const char*);
 
 // A typed functor adaptor for TraceRoot.
 struct TraceRootFunctor {
@@ -978,17 +977,17 @@ js::GCMarker::traverseEdge(S source, con
 template <typename T>
 bool
 js::GCMarker::mark(T* thing)
 {
     AssertShouldMarkInZone(thing);
     MOZ_ASSERT(!IsInsideNursery(gc::TenuredCell::fromPointer(thing)));
     return gc::ParticipatesInCC<T>::value
            ? gc::TenuredCell::fromPointer(thing)->markIfUnmarked(markColor())
-           : gc::TenuredCell::fromPointer(thing)->markIfUnmarked(gc::BLACK);
+           : gc::TenuredCell::fromPointer(thing)->markIfUnmarked(gc::MarkColor::Black);
 }
 
 
 /*** Inline, Eager GC Marking *********************************************************************/
 
 // Each of the eager, inline marking paths is directly preceeded by the
 // out-of-line, generic tracing code for comparison. Both paths must end up
 // traversing equivalent subgraphs.
@@ -1057,18 +1056,18 @@ Shape::traceChildren(JSTracer* trc)
     if (hasGetterObject())
         TraceManuallyBarrieredEdge(trc, &asAccessorShape().getterObj, "getter");
     if (hasSetterObject())
         TraceManuallyBarrieredEdge(trc, &asAccessorShape().setterObj, "setter");
 }
 inline void
 js::GCMarker::eagerlyMarkChildren(Shape* shape)
 {
-    MOZ_ASSERT_IF(markColor() == GRAY, shape->isMarkedGray());
-    MOZ_ASSERT_IF(markColor() == BLACK, shape->isMarkedBlack());
+    MOZ_ASSERT_IF(markColor() == MarkColor::Gray, shape->isMarkedGray());
+    MOZ_ASSERT_IF(markColor() == MarkColor::Black, shape->isMarkedBlack());
 
     do {
         // Special case: if a base shape has a shape table then all its pointers
         // must point to this shape or an anscestor.  Since these pointers will
         // be traced by this loop they do not need to be traced here as well.
         BaseShape* base = shape->base();
         CheckTraversedEdge(shape, base);
         if (mark(base)) {
@@ -2338,17 +2337,17 @@ MarkStackIter::saveValueArray(NativeObje
  * ExpandWeakMaps: the GC is recomputing the liveness of WeakMap entries by
  * expanding each live WeakMap into its constituent key->value edges, a table
  * of which will be consulted in a later phase whenever marking a potential
  * key.
  */
 GCMarker::GCMarker(JSRuntime* rt)
   : JSTracer(rt, JSTracer::TracerKindTag::Marking, ExpandWeakMaps),
     stack(size_t(-1)),
-    color(BLACK),
+    color(MarkColor::Black),
     unmarkedArenaStackTop(nullptr)
 #ifdef DEBUG
   , markLaterArenas(0)
   , started(false)
   , strictCompartmentChecking(false)
 #endif
 {
 }
@@ -2361,17 +2360,17 @@ GCMarker::init(JSGCMode gcMode)
 
 void
 GCMarker::start()
 {
 #ifdef DEBUG
     MOZ_ASSERT(!started);
     started = true;
 #endif
-    color = BLACK;
+    color = MarkColor::Black;
     linearWeakMarkingDisabled_ = false;
 
     MOZ_ASSERT(!unmarkedArenaStackTop);
     MOZ_ASSERT(markLaterArenas == 0);
 }
 
 void
 GCMarker::stop()
@@ -2393,17 +2392,17 @@ GCMarker::stop()
         if (!zone->gcWeakKeys().clear())
             oomUnsafe.crash("clearing weak keys in GCMarker::stop()");
     }
 }
 
 void
 GCMarker::reset()
 {
-    color = BLACK;
+    color = MarkColor::Black;
 
     stack.reset();
     MOZ_ASSERT(isMarkStackEmpty());
 
     while (unmarkedArenaStackTop) {
         Arena* arena = unmarkedArenaStackTop;
         MOZ_ASSERT(arena->hasDelayedMarking);
         MOZ_ASSERT(markLaterArenas);
@@ -2435,18 +2434,18 @@ GCMarker::pushValueArray(JSObject* obj, 
     checkZone(obj);
     if (!stack.push(obj, start, end))
         delayMarkingChildren(obj);
 }
 
 void
 GCMarker::repush(JSObject* obj)
 {
-    MOZ_ASSERT_IF(markColor() == GRAY, gc::TenuredCell::fromPointer(obj)->isMarkedGray());
-    MOZ_ASSERT_IF(markColor() == BLACK, gc::TenuredCell::fromPointer(obj)->isMarkedBlack());
+    MOZ_ASSERT_IF(markColor() == MarkColor::Gray, gc::TenuredCell::fromPointer(obj)->isMarkedGray());
+    MOZ_ASSERT_IF(markColor() == MarkColor::Black, gc::TenuredCell::fromPointer(obj)->isMarkedBlack());
     pushTaggedPtr(obj);
 }
 
 void
 GCMarker::enterWeakMarkingMode()
 {
     MOZ_ASSERT(tag_ == TracerKindTag::Marking);
     if (linearWeakMarkingDisabled_)
@@ -3460,24 +3459,24 @@ GetMarkWordAddress(Cell* cell)
 
     uintptr_t* wordp;
     uintptr_t mask;
     js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), ColorBit::BlackBit, &wordp, &mask);
     return wordp;
 }
 
 uintptr_t
-GetMarkMask(Cell* cell, uint32_t color)
+GetMarkMask(Cell* cell, uint32_t colorBit)
 {
-    MOZ_ASSERT(color == 0 || color == 1);
+    MOZ_ASSERT(colorBit == 0 || colorBit == 1);
 
     if (!cell->isTenured())
         return 0;
 
-    ColorBit bit = color == 0 ? ColorBit::BlackBit : ColorBit::GrayOrBlackBit;
+    ColorBit bit = colorBit == 0 ? ColorBit::BlackBit : ColorBit::GrayOrBlackBit;
     uintptr_t* wordp;
     uintptr_t mask;
     js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), bit, &wordp, &mask);
     return mask;
 }
 
 }
 }
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -260,25 +260,25 @@ class GCMarker : public JSTracer
      * Care must be taken changing the mark color from gray to black. The cycle
      * collector depends on the invariant that there are no black to gray edges
      * in the GC heap. This invariant lets the CC not trace through black
      * objects. If this invariant is violated, the cycle collector may free
      * objects that are still reachable.
      */
     void setMarkColorGray() {
         MOZ_ASSERT(isDrained());
-        MOZ_ASSERT(color == gc::BLACK);
-        color = gc::GRAY;
+        MOZ_ASSERT(color == gc::MarkColor::Black);
+        color = gc::MarkColor::Gray;
     }
     void setMarkColorBlack() {
         MOZ_ASSERT(isDrained());
-        MOZ_ASSERT(color == gc::GRAY);
-        color = gc::BLACK;
+        MOZ_ASSERT(color == gc::MarkColor::Gray);
+        color = gc::MarkColor::Black;
     }
-    uint32_t markColor() const { return color; }
+    gc::MarkColor markColor() const { return color; }
 
     void enterWeakMarkingMode();
     void leaveWeakMarkingMode();
     void abortLinearWeakMarking() {
         leaveWeakMarkingMode();
         linearWeakMarkingDisabled_ = true;
     }
 
@@ -361,17 +361,17 @@ class GCMarker : public JSTracer
                                         HeapSlot** vpp, HeapSlot** endp);
     void saveValueRanges();
     inline void processMarkStackTop(SliceBudget& budget);
 
     /* The mark stack. Pointers in this stack are "gray" in the GC sense. */
     gc::MarkStack stack;
 
     /* The color is only applied to objects and functions. */
-    ActiveThreadData<uint32_t> color;
+    ActiveThreadData<gc::MarkColor> color;
 
     /* Pointer to the top of the stack of arenas we are delaying marking on. */
     ActiveThreadData<js::gc::Arena*> unmarkedArenaStackTop;
 
     /*
      * If the weakKeys table OOMs, disable the linear algorithm and fall back
      * to iterating until the next GC.
      */
--- a/js/src/jsapi-tests/testGCGrayMarking.cpp
+++ b/js/src/jsapi-tests/testGCGrayMarking.cpp
@@ -585,42 +585,42 @@ TestGrayUnmarking()
     const size_t length = 2000;
 
     JSObject* chain = AllocObjectChain(length);
     CHECK(chain);
 
     RootedObject blackRoot(cx, chain);
     JS_GC(cx);
     size_t count;
-    CHECK(IterateObjectChain(chain, ColorCheckFunctor(BLACK, &count)));
+    CHECK(IterateObjectChain(chain, ColorCheckFunctor(MarkColor::Black, &count)));
     CHECK(count == length);
 
     blackRoot = nullptr;
     grayRoots.grayRoot1 = chain;
     JS_GC(cx);
     CHECK(cx->runtime()->gc.areGrayBitsValid());
-    CHECK(IterateObjectChain(chain, ColorCheckFunctor(GRAY, &count)));
+    CHECK(IterateObjectChain(chain, ColorCheckFunctor(MarkColor::Gray, &count)));
     CHECK(count == length);
 
     JS::ExposeObjectToActiveJS(chain);
     CHECK(cx->runtime()->gc.areGrayBitsValid());
-    CHECK(IterateObjectChain(chain, ColorCheckFunctor(BLACK, &count)));
+    CHECK(IterateObjectChain(chain, ColorCheckFunctor(MarkColor::Black, &count)));
     CHECK(count == length);
 
     grayRoots.grayRoot1 = nullptr;
 
     return true;
 }
 
 struct ColorCheckFunctor
 {
-    uint32_t color;
+    MarkColor color;
     size_t& count;
 
-    ColorCheckFunctor(uint32_t colorArg, size_t* countArg)
+    ColorCheckFunctor(MarkColor colorArg, size_t* countArg)
       : color(colorArg), count(*countArg)
     {
         count = 0;
     }
 
     bool operator()(JSObject* obj) {
         if (!CheckCellColor(obj, color))
             return false;
@@ -630,17 +630,17 @@ struct ColorCheckFunctor
             return false;
 
         Shape* shape = nobj.shape();
         if (!CheckCellColor(shape, color))
             return false;
 
         // Shapes and symbols are never marked gray.
         jsid id = shape->propid();
-        if (JSID_IS_GCTHING(id) && !CheckCellColor(JSID_TO_GCTHING(id).asCell(), BLACK))
+        if (JSID_IS_GCTHING(id) && !CheckCellColor(JSID_TO_GCTHING(id).asCell(), MarkColor::Black))
             return false;
 
         count++;
         return true;
     }
 };
 
 JS::PersistentRootedObject global1;
@@ -825,23 +825,23 @@ IsMarkedGray(Cell* cell)
 {
     TenuredCell* tc = &cell->asTenured();
     bool isGray = tc->isMarkedGray();
     MOZ_ASSERT_IF(isGray, tc->isMarkedAny());
     return isGray;
 }
 
 static bool
-CheckCellColor(Cell* cell, uint32_t color)
+CheckCellColor(Cell* cell, MarkColor color)
 {
-    MOZ_ASSERT(color == BLACK || color == GRAY);
-    if (color == BLACK && !IsMarkedBlack(cell)) {
+    MOZ_ASSERT(color == MarkColor::Black || color == MarkColor::Gray);
+    if (color == MarkColor::Black && !IsMarkedBlack(cell)) {
         printf("Found non-black cell: %p\n", cell);
         return false;
-    } else if (color == GRAY && !IsMarkedGray(cell)) {
+    } else if (color == MarkColor::Gray && !IsMarkedGray(cell)) {
         printf("Found non-gray cell: %p\n", cell);
         return false;
     }
 
     return true;
 }
 
 void
--- a/js/src/jsapi-tests/testGCHeapPostBarriers.cpp
+++ b/js/src/jsapi-tests/testGCHeapPostBarriers.cpp
@@ -207,18 +207,18 @@ BEGIN_TEST(testUnbarrieredEquality)
     const JSObject* constobj = robj;
     const JSObject* constobj2 = robj2;
 
     // Make them gray. We will make sure they stay gray. (For most reads, the
     // barrier will unmark gray.)
     using namespace js::gc;
     TenuredCell* cell = &obj->asTenured();
     TenuredCell* cell2 = &obj2->asTenured();
-    cell->markIfUnmarked(GRAY);
-    cell2->markIfUnmarked(GRAY);
+    cell->markIfUnmarked(MarkColor::Gray);
+    cell2->markIfUnmarked(MarkColor::Gray);
     MOZ_ASSERT(cell->isMarkedGray());
     MOZ_ASSERT(cell2->isMarkedGray());
 
     {
         JS::Heap<JSObject*> heap(obj);
         JS::Heap<JSObject*> heap2(obj2);
         CHECK(TestWrapper(obj, obj2, heap, heap2));
         CHECK(TestWrapper(constobj, constobj2, heap, heap2));
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4466,17 +4466,17 @@ js::gc::MarkingValidator::validate()
                 continue;
             if (arena->allocatedDuringIncremental)
                 continue;
 
             AllocKind kind = arena->getAllocKind();
             uintptr_t thing = arena->thingsStart();
             uintptr_t end = arena->thingsEnd();
             while (thing < end) {
-                Cell* cell = (Cell*)thing;
+                auto cell = reinterpret_cast<TenuredCell*>(thing);
 
                 /*
                  * If a non-incremental GC wouldn't have collected a cell, then
                  * an incremental GC won't collect it.
                  */
                 if (bitmap->isMarkedAny(cell))
                     MOZ_RELEASE_ASSERT(incBitmap->isMarkedAny(cell));
 
@@ -4836,41 +4836,41 @@ js::DelayCrossCompartmentGrayMarking(JSO
             found = true;
         obj = NextIncomingCrossCompartmentPointer(obj, false);
     }
     MOZ_ASSERT(found);
 #endif
 }
 
 static void
-MarkIncomingCrossCompartmentPointers(JSRuntime* rt, const uint32_t color)
-{
-    MOZ_ASSERT(color == BLACK || color == GRAY);
+MarkIncomingCrossCompartmentPointers(JSRuntime* rt, MarkColor color)
+{
+    MOZ_ASSERT(color == MarkColor::Black || color == MarkColor::Gray);
 
     static const gcstats::PhaseKind statsPhases[] = {
         gcstats::PhaseKind::SWEEP_MARK_INCOMING_BLACK,
         gcstats::PhaseKind::SWEEP_MARK_INCOMING_GRAY
     };
-    gcstats::AutoPhase ap1(rt->gc.stats(), statsPhases[color]);
-
-    bool unlinkList = color == GRAY;
+    gcstats::AutoPhase ap1(rt->gc.stats(), statsPhases[unsigned(color)]);
+
+    bool unlinkList = color == MarkColor::Gray;
 
     for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
-        MOZ_ASSERT_IF(color == GRAY, c->zone()->isGCMarkingGray());
-        MOZ_ASSERT_IF(color == BLACK, c->zone()->isGCMarkingBlack());
+        MOZ_ASSERT_IF(color == MarkColor::Gray, c->zone()->isGCMarkingGray());
+        MOZ_ASSERT_IF(color == MarkColor::Black, c->zone()->isGCMarkingBlack());
         MOZ_ASSERT_IF(c->gcIncomingGrayPointers, IsGrayListObject(c->gcIncomingGrayPointers));
 
         for (JSObject* src = c->gcIncomingGrayPointers;
              src;
              src = NextIncomingCrossCompartmentPointer(src, unlinkList))
         {
             JSObject* dst = CrossCompartmentPointerReferent(src);
             MOZ_ASSERT(dst->compartment() == c);
 
-            if (color == GRAY) {
+            if (color == MarkColor::Gray) {
                 if (IsMarkedUnbarriered(rt, &src) && src->asTenured().isMarkedGray())
                     TraceManuallyBarrieredEdge(&rt->gc.marker, &dst,
                                                "cross-compartment gray pointer");
             } else {
                 if (IsMarkedUnbarriered(rt, &src) && !src->asTenured().isMarkedGray())
                     TraceManuallyBarrieredEdge(&rt->gc.marker, &dst,
                                                "cross-compartment black pointer");
             }
@@ -4971,33 +4971,33 @@ GCRuntime::endMarkingSweepGroup()
 {
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_MARK);
 
     /*
      * Mark any incoming black pointers from previously swept compartments
      * whose referents are not marked. This can occur when gray cells become
      * black by the action of UnmarkGray.
      */
-    MarkIncomingCrossCompartmentPointers(rt, BLACK);
+    MarkIncomingCrossCompartmentPointers(rt, MarkColor::Black);
     markWeakReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_WEAK);
 
     /*
      * Change state of current group to MarkGray to restrict marking to this
      * group.  Note that there may be pointers to the atoms compartment, and
      * these will be marked through, as they are not marked with
      * MarkCrossCompartmentXXX.
      */
     for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) {
         MOZ_ASSERT(zone->isGCMarkingBlack());
         zone->setGCState(Zone::MarkGray);
     }
     marker.setMarkColorGray();
 
     /* Mark incoming gray pointers from previously swept compartments. */
-    MarkIncomingCrossCompartmentPointers(rt, GRAY);
+    MarkIncomingCrossCompartmentPointers(rt, MarkColor::Gray);
 
     /* Mark gray roots and mark transitively inside the current compartment group. */
     markGrayReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_GRAY);
     markWeakReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_GRAY_WEAK);
 
     /* Restore marking state. */
     for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) {
         MOZ_ASSERT(zone->isGCMarkingGray());