Bug 1380030 - Refactor isMarked() methods into separate methods for each color and any r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 12 Jul 2017 18:31:55 +0100
changeset 607721 e2339ab06af56cb05c0d5b917d6f660952b45a69
parent 607720 9fc4c6aabd8e2603f1a0495574d50b496e836c12
child 607722 04364ec85017d7646b11b1ed1f3e1cf61a59075b
push id68095
push userbmo:rbarker@mozilla.com
push dateWed, 12 Jul 2017 20:01:47 +0000
reviewerssfink
bugs1380030
milestone56.0a1
Bug 1380030 - Refactor isMarked() methods into separate methods for each color and any r=sfink
js/public/HeapAPI.h
js/src/gc/Barrier.cpp
js/src/gc/Heap.h
js/src/gc/Iteration.cpp
js/src/gc/Marking.cpp
js/src/gc/Verifier.cpp
js/src/gc/Zone.cpp
js/src/jsapi-tests/testGCGrayMarking.cpp
js/src/jsapi-tests/testGCHeapPostBarriers.cpp
js/src/jsapi-tests/testGCMarking.cpp
js/src/jsfriendapi.cpp
js/src/jsgc.cpp
js/src/jsscript.cpp
js/src/jsweakmap.cpp
js/src/proxy/Wrapper.cpp
js/src/shell/js.cpp
js/src/vm/Shape.cpp
js/src/vm/TypeInference.cpp
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -62,17 +62,17 @@ const size_t ArenaHeaderSize = sizeof(si
  * 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:
- *   BLACK_BIT   GRAY_OR_BLACK_BIT   color
+ *    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/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -33,17 +33,17 @@ RuntimeFromActiveCooperatingThreadIsHeap
 bool
 IsMarkedBlack(JSObject* obj)
 {
     // Note: we assume conservatively that Nursery things will be live.
     if (!obj->isTenured())
         return true;
 
     gc::TenuredCell& tenured = obj->asTenured();
-    if (tenured.isMarked(gc::BLACK) || tenured.arena()->allocatedDuringIncremental)
+    if (tenured.isMarkedAny() || tenured.arena()->allocatedDuringIncremental)
         return true;
 
     return false;
 }
 
 bool
 HeapSlot::preconditionForSet(NativeObject* owner, Kind kind, uint32_t slot) const
 {
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -247,17 +247,19 @@ 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;
     MOZ_ALWAYS_INLINE TenuredCell& asTenured();
 
-    MOZ_ALWAYS_INLINE bool isMarked(uint32_t color = BLACK) const;
+    MOZ_ALWAYS_INLINE bool isMarkedAny() const;
+    MOZ_ALWAYS_INLINE bool isMarkedBlack() const;
+    MOZ_ALWAYS_INLINE bool isMarkedGray() const;
 
     inline JSRuntime* runtimeFromActiveCooperatingThread() const;
 
     // Note: Unrestricted access to the runtime of a GC thing from an arbitrary
     // thread can easily lead to races. Use this method very carefully.
     inline JSRuntime* runtimeFromAnyThread() const;
 
     // May be overridden by GC thing kinds that have a compartment pointer.
@@ -287,17 +289,20 @@ struct Cell
 class TenuredCell : public Cell
 {
   public:
     // Construct a TenuredCell from a void*, making various sanity assertions.
     static MOZ_ALWAYS_INLINE TenuredCell* fromPointer(void* ptr);
     static MOZ_ALWAYS_INLINE const TenuredCell* fromPointer(const void* ptr);
 
     // Mark bit management.
-    MOZ_ALWAYS_INLINE bool isMarked(uint32_t color = BLACK) const;
+    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 void markBlack() const;
     MOZ_ALWAYS_INLINE void copyMarkBitsFrom(const TenuredCell* src);
 
     // Access to the arena.
     inline Arena* arena() const;
     inline AllocKind getAllocKind() const;
@@ -907,24 +912,26 @@ struct ChunkBitmap
     }
 
     MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool markBit(const Cell* cell, ColorBit colorBit) {
         uintptr_t* word, mask;
         getMarkWordAndMask(cell, colorBit, &word, &mask);
         return *word & mask;
     }
 
-    MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarked(const Cell* cell, uint32_t color) {
-        if (color == BLACK) {
-            return markBit(cell, ColorBit::BlackBit) ||
-                   markBit(cell, ColorBit::GrayOrBlackBit);
-        } else {
-            return !markBit(cell, ColorBit::BlackBit) &&
-                   markBit(cell, ColorBit::GrayOrBlackBit);
-        }
+    MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarkedAny(const Cell* cell) {
+        return markBit(cell, ColorBit::BlackBit) || markBit(cell, ColorBit::GrayOrBlackBit);
+    }
+
+    MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarkedBlack(const Cell* cell) {
+        return markBit(cell, ColorBit::BlackBit);
+    }
+
+    MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarkedGray(const Cell* 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) {
         uintptr_t* word, mask;
         getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask);
         if (*word & mask)
             return false;
@@ -1167,24 +1174,31 @@ Cell::asTenured() const
 MOZ_ALWAYS_INLINE TenuredCell&
 Cell::asTenured()
 {
     MOZ_ASSERT(isTenured());
     return *static_cast<TenuredCell*>(this);
 }
 
 MOZ_ALWAYS_INLINE bool
-Cell::isMarked(uint32_t color) const
+Cell::isMarkedAny() const
+{
+    return !isTenured() || asTenured().isMarkedAny();
+}
+
+MOZ_ALWAYS_INLINE bool
+Cell::isMarkedBlack() const
 {
-    if (color == BLACK) {
-        return !isTenured() || asTenured().isMarked(BLACK);
-    } else {
-        MOZ_ASSERT(color == GRAY);
-        return isTenured() && asTenured().isMarked(GRAY);
-    }
+    return !isTenured() || asTenured().isMarkedBlack();
+}
+
+MOZ_ALWAYS_INLINE bool
+Cell::isMarkedGray() const
+{
+    return isTenured() && asTenured().isMarkedGray();
 }
 
 inline JSRuntime*
 Cell::runtimeFromActiveCooperatingThread() const
 {
     JSRuntime* rt = chunk()->trailer.runtime;
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
     return rt;
@@ -1249,21 +1263,34 @@ TenuredCell::fromPointer(void* ptr)
 /* static */ MOZ_ALWAYS_INLINE const TenuredCell*
 TenuredCell::fromPointer(const void* ptr)
 {
     MOZ_ASSERT(static_cast<const TenuredCell*>(ptr)->isTenured());
     return static_cast<const TenuredCell*>(ptr);
 }
 
 bool
-TenuredCell::isMarked(uint32_t color /* = BLACK */) const
+TenuredCell::isMarkedAny() const
 {
     MOZ_ASSERT(arena()->allocated());
-    AssertValidColor(this, color);
-    return chunk()->bitmap.isMarked(this, color);
+    return chunk()->bitmap.isMarkedAny(this);
+}
+
+bool
+TenuredCell::isMarkedBlack() const
+{
+    MOZ_ASSERT(arena()->allocated());
+    return chunk()->bitmap.isMarkedBlack(this);
+}
+
+bool
+TenuredCell::isMarkedGray() const
+{
+    MOZ_ASSERT(arena()->allocated());
+    return chunk()->bitmap.isMarkedGray(this);
 }
 
 bool
 TenuredCell::markIfUnmarked(uint32_t color /* = BLACK */) const
 {
     AssertValidColor(this, color);
     return chunk()->bitmap.markIfUnmarked(this, color);
 }
@@ -1341,17 +1368,17 @@ TenuredCell::readBarrier(TenuredCell* th
     if (shadowZone->needsIncrementalBarrier()) {
         // Barriers are only enabled on the active thread and are disabled while collecting.
         MOZ_ASSERT(!RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(shadowZone));
         Cell* tmp = thing;
         TraceManuallyBarrieredGenericPointerEdge(shadowZone->barrierTracer(), &tmp, "read barrier");
         MOZ_ASSERT(tmp == thing);
     }
 
-    if (thing->isMarked(GRAY)) {
+    if (thing->isMarkedGray()) {
         // There shouldn't be anything marked grey unless we're on the active thread.
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread()));
         if (!RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(shadowZone))
             UnmarkGrayCellRecursively(thing, thing->getTraceKind());
     }
 }
 
 void
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -102,17 +102,17 @@ js::IterateScripts(JSContext* cx, JSComp
     }
 }
 
 static void
 IterateGrayObjects(Zone* zone, GCThingCallback cellCallback, void* data)
 {
     for (auto kind : ObjectAllocKinds()) {
         for (GrayObjectIter obj(zone, kind); !obj.done(); obj.next()) {
-            if (obj->asTenured().isMarked(GRAY))
+            if (obj->asTenured().isMarkedGray())
                 cellCallback(data, JS::GCCellPtr(obj.get()));
         }
     }
 }
 
 void
 js::IterateGrayObjects(Zone* zone, GCThingCallback cellCallback, void* data)
 {
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -306,31 +306,31 @@ ShouldTraceCrossCompartment(JSTracer* tr
 
     JS::Zone* zone = tenured.zone();
     if (color == 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 conservative scanner.
+         * but the source was marked black by the write barrier.
          */
-        if (tenured.isMarked(GRAY)) {
+        if (tenured.isMarkedGray()) {
             MOZ_ASSERT(!zone->isCollecting());
             trc->runtime()->gc.setFoundBlackGrayEdges(tenured);
         }
         return zone->isGCMarking();
     } else {
         if (zone->isGCMarkingBlack()) {
             /*
              * The destination compartment is being not being marked gray now,
              * but it will be later, so record the cell so it can be marked gray
              * at the appropriate time.
              */
-            if (!tenured.isMarked())
+            if (!tenured.isMarkedAny())
                 DelayCrossCompartmentGrayMarking(src);
             return false;
         }
         return zone->isGCMarkingGray();
     }
 }
 
 static bool
@@ -1057,17 +1057,19 @@ 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(shape->isMarked(this->markColor()));
+    MOZ_ASSERT_IF(markColor() == GRAY, shape->isMarkedGray());
+    MOZ_ASSERT_IF(markColor() == BLACK, shape->isMarkedAny());
+
     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)) {
             MOZ_ASSERT(base->canSkipMarkingShapeTable(shape));
@@ -1110,17 +1112,17 @@ JSString::traceBase(JSTracer* trc)
 {
     MOZ_ASSERT(hasBase());
     TraceManuallyBarrieredEdge(trc, &d.s.u3.base, "base");
 }
 inline void
 js::GCMarker::eagerlyMarkChildren(JSLinearString* linearStr)
 {
     AssertShouldMarkInZone(linearStr);
-    MOZ_ASSERT(linearStr->isMarked());
+    MOZ_ASSERT(linearStr->isMarkedAny());
     MOZ_ASSERT(linearStr->JSString::isLinear());
 
     // Use iterative marking to avoid blowing out the stack.
     while (linearStr->hasBase()) {
         linearStr = linearStr->base();
         MOZ_ASSERT(linearStr->JSString::isLinear());
         if (linearStr->isPermanentAtom())
             break;
@@ -1173,17 +1175,17 @@ js::GCMarker::eagerlyMarkChildren(JSRope
             }
             history[ropeDepth % ROPE_CYCLE_HISTORY] = rope;
         }
 #endif
 
         JS_DIAGNOSTICS_ASSERT(rope->getTraceKind() == JS::TraceKind::String);
         JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope());
         AssertShouldMarkInZone(rope);
-        MOZ_ASSERT(rope->isMarked());
+        MOZ_ASSERT(rope->isMarkedAny());
         JSRope* next = nullptr;
 
         JSString* right = rope->rightChild();
         if (!right->isPermanentAtom() &&
             mark(right))
         {
             if (right->isLinear())
                 eagerlyMarkChildren(&right->asLinear());
@@ -2433,17 +2435,18 @@ GCMarker::pushValueArray(JSObject* obj, 
     checkZone(obj);
     if (!stack.push(obj, start, end))
         delayMarkingChildren(obj);
 }
 
 void
 GCMarker::repush(JSObject* obj)
 {
-    MOZ_ASSERT(gc::TenuredCell::fromPointer(obj)->isMarked(markColor()));
+    MOZ_ASSERT_IF(markColor() == GRAY, gc::TenuredCell::fromPointer(obj)->isMarkedGray());
+    MOZ_ASSERT_IF(markColor() == BLACK, gc::TenuredCell::fromPointer(obj)->isMarkedAny());
     pushTaggedPtr(obj);
 }
 
 void
 GCMarker::enterWeakMarkingMode()
 {
     MOZ_ASSERT(tag_ == TracerKindTag::Marking);
     if (linearWeakMarkingDisabled_)
@@ -2487,17 +2490,17 @@ void
 GCMarker::markDelayedChildren(Arena* arena)
 {
     if (arena->markOverflow) {
         bool always = arena->allocatedDuringIncremental;
         arena->markOverflow = 0;
 
         for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
             TenuredCell* t = i.getCell();
-            if (always || t->isMarked()) {
+            if (always || t->isMarkedAny()) {
                 t->markIfUnmarked();
                 js::TraceChildren(this, t, MapAllocToTraceKind(arena->getAllocKind()));
             }
         }
     } else {
         MOZ_ASSERT(arena->allocatedDuringIncremental);
         PushArena(this, arena);
     }
@@ -3102,17 +3105,17 @@ IsMarkedInternalCommon(T* thingp)
     if (!zone->isCollectingFromAnyThread() || zone->isGCFinished())
         return true;
 
     if (zone->isGCCompacting() && IsForwarded(*thingp)) {
         *thingp = Forwarded(*thingp);
         return true;
     }
 
-    return thing.isMarked() || thing.arena()->allocatedDuringIncremental;
+    return thing.isMarkedAny() || thing.arena()->allocatedDuringIncremental;
 }
 
 template <typename T>
 static bool
 IsMarkedInternal(JSRuntime* rt, T** thingp)
 {
     if (IsOwnedByOtherRuntime(rt, *thingp))
         return true;
@@ -3153,17 +3156,17 @@ IsMarkedInternal(JSRuntime* rt, T* thing
 
 bool
 js::gc::IsAboutToBeFinalizedDuringSweep(TenuredCell& tenured)
 {
     MOZ_ASSERT(!IsInsideNursery(&tenured));
     MOZ_ASSERT(tenured.zoneFromAnyThread()->isGCSweeping());
     if (tenured.arena()->allocatedDuringIncremental)
         return false;
-    return !tenured.isMarked();
+    return !tenured.isMarkedAny();
 }
 
 template <typename T>
 static bool
 IsAboutToBeFinalizedInternal(T** thingp)
 {
     CheckIsMarkedThing(thingp);
     T* thing = *thingp;
@@ -3312,17 +3315,17 @@ FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(I
  *   containers.
  */
 
 #ifdef DEBUG
 struct AssertNonGrayTracer : public JS::CallbackTracer {
     explicit AssertNonGrayTracer(JSRuntime* rt) : JS::CallbackTracer(rt) {}
     void onChild(const JS::GCCellPtr& thing) override {
         MOZ_ASSERT_IF(thing.asCell()->isTenured(),
-                      !thing.asCell()->asTenured().isMarked(js::gc::GRAY));
+                      !thing.asCell()->asTenured().isMarkedGray());
     }
 };
 #endif
 
 class UnmarkGrayTracer : public JS::CallbackTracer
 {
   public:
     // We set weakMapAction to DoNotTraceWeakMaps because the cycle collector
@@ -3360,17 +3363,17 @@ UnmarkGrayTracer::onChild(const JS::GCCe
 #ifdef DEBUG
         AssertNonGrayTracer nongray(runtime());
         TraceChildren(&nongray, cell, thing.kind());
 #endif
         return;
     }
 
     TenuredCell& tenured = cell->asTenured();
-    if (!tenured.isMarked(GRAY))
+    if (!tenured.isMarkedGray())
         return;
 
     tenured.markBlack();
     unmarkedAny = true;
 
     if (!stack.append(thing))
         oom = true;
 }
@@ -3438,19 +3441,19 @@ namespace debug {
 
 MarkInfo
 GetMarkInfo(Cell* rawCell)
 {
     if (!rawCell->isTenured())
         return MarkInfo::NURSERY;
 
     TenuredCell* cell = &rawCell->asTenured();
-    if (cell->isMarked(GRAY))
+    if (cell->isMarkedGray())
         return MarkInfo::GRAY;
-    if (cell->isMarked(BLACK))
+    if (cell->isMarkedAny())
         return MarkInfo::BLACK;
     return MarkInfo::UNMARKED;
 }
 
 uintptr_t*
 GetMarkWordAddress(Cell* cell)
 {
     if (!cell->isTenured())
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -256,17 +256,17 @@ oom:
     incrementalState = State::NotActive;
     js_delete(trc);
     verifyPreData = nullptr;
 }
 
 static bool
 IsMarkedOrAllocated(TenuredCell* cell)
 {
-    return cell->isMarked() || cell->arena()->allocatedDuringIncremental;
+    return cell->isMarkedAny() || cell->arena()->allocatedDuringIncremental;
 }
 
 struct CheckEdgeTracer : public JS::CallbackTracer {
     VerifyNode* node;
     explicit CheckEdgeTracer(JSRuntime* rt) : JS::CallbackTracer(rt), node(nullptr) {}
     void onChild(const JS::GCCellPtr& thing) override;
 };
 
@@ -648,18 +648,18 @@ void
 CheckGrayMarkingTracer::checkCell(Cell* cell)
 {
     Cell* parent = parentCell();
     if (!cell->isTenured() || !parent || !parent->isTenured())
         return;
 
     TenuredCell* tenuredCell = &cell->asTenured();
     TenuredCell* tenuredParent = &parent->asTenured();
-    if (tenuredParent->isMarked(BLACK) && !tenuredParent->isMarked(GRAY) &&
-        tenuredCell->isMarked(GRAY))
+    if (tenuredParent->isMarkedAny() && !tenuredParent->isMarkedGray() &&
+        tenuredCell->isMarkedGray())
     {
         failures++;
         fprintf(stderr, "Found black to gray edge to %s %p\n",
                 GCTraceKindToAscii(cell->getTraceKind()), cell);
         dumpCellPath();
     }
 }
 
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -157,17 +157,17 @@ Zone::sweepBreakpoints(FreeOp* fop)
 
                 // If we are sweeping, then we expect the script and the
                 // debugger object to be swept in the same sweep group, except
                 // if the breakpoint was added after we computed the sweep
                 // groups. In this case both script and debugger object must be
                 // live.
                 MOZ_ASSERT_IF(isGCSweeping() && dbgobj->zone()->isCollecting(),
                               dbgobj->zone()->isGCSweeping() ||
-                              (!scriptGone && dbgobj->asTenured().isMarked()));
+                              (!scriptGone && dbgobj->asTenured().isMarkedAny()));
 
                 bool dying = scriptGone || IsAboutToBeFinalized(&dbgobj);
                 MOZ_ASSERT_IF(!dying, !IsAboutToBeFinalized(&bp->getHandlerRef()));
                 if (dying)
                     bp->destroy(fop);
             }
         }
     }
--- a/js/src/jsapi-tests/testGCGrayMarking.cpp
+++ b/js/src/jsapi-tests/testGCGrayMarking.cpp
@@ -812,25 +812,25 @@ bool IterateObjectChain(JSObject* chain,
 
     return true;
 }
 
 static bool
 IsMarkedBlack(Cell* cell)
 {
     TenuredCell* tc = &cell->asTenured();
-    return tc->isMarked(BLACK) && !tc->isMarked(GRAY);
+    return tc->isMarkedAny() && !tc->isMarkedGray();
 }
 
 static bool
 IsMarkedGray(Cell* cell)
 {
     TenuredCell* tc = &cell->asTenured();
-    bool isGray = tc->isMarked(GRAY);
-    MOZ_ASSERT_IF(isGray, tc->isMarked(BLACK));
+    bool isGray = tc->isMarkedGray();
+    MOZ_ASSERT_IF(isGray, tc->isMarkedAny());
     return isGray;
 }
 
 static bool
 CheckCellColor(Cell* cell, uint32_t color)
 {
     MOZ_ASSERT(color == BLACK || color == GRAY);
     if (color == BLACK && !IsMarkedBlack(cell)) {
--- a/js/src/jsapi-tests/testGCHeapPostBarriers.cpp
+++ b/js/src/jsapi-tests/testGCHeapPostBarriers.cpp
@@ -209,18 +209,18 @@ BEGIN_TEST(testUnbarrieredEquality)
 
     // 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);
-    MOZ_ASSERT(cell->isMarked(GRAY));
-    MOZ_ASSERT(cell2->isMarked(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));
     }
 
@@ -241,62 +241,62 @@ BEGIN_TEST(testUnbarrieredEquality)
     }
 
     // Sanity check that the barriers normally mark things black.
     {
         JS::Heap<JSObject*> heap(obj);
         JS::Heap<JSObject*> heap2(obj2);
         heap.get();
         heap2.get();
-        CHECK(cell->isMarked(BLACK));
-        CHECK(cell2->isMarked(BLACK));
+        CHECK(cell->isMarkedAny());
+        CHECK(cell2->isMarkedAny());
     }
 
     return true;
 }
 
 template <typename ObjectT, typename WrapperT>
 bool
 TestWrapper(ObjectT obj, ObjectT obj2, WrapperT& wrapper, WrapperT& wrapper2)
 {
     using namespace js::gc;
 
     const TenuredCell& cell = obj->asTenured();
     const TenuredCell& cell2 = obj2->asTenured();
 
     int x = 0;
 
-    CHECK(cell.isMarked(GRAY));
-    CHECK(cell2.isMarked(GRAY));
+    CHECK(cell.isMarkedGray());
+    CHECK(cell2.isMarkedGray());
     x += obj == obj2;
-    CHECK(cell.isMarked(GRAY));
-    CHECK(cell2.isMarked(GRAY));
+    CHECK(cell.isMarkedGray());
+    CHECK(cell2.isMarkedGray());
     x += obj == wrapper2;
-    CHECK(cell.isMarked(GRAY));
-    CHECK(cell2.isMarked(GRAY));
+    CHECK(cell.isMarkedGray());
+    CHECK(cell2.isMarkedGray());
     x += wrapper == obj2;
-    CHECK(cell.isMarked(GRAY));
-    CHECK(cell2.isMarked(GRAY));
+    CHECK(cell.isMarkedGray());
+    CHECK(cell2.isMarkedGray());
     x += wrapper == wrapper2;
-    CHECK(cell.isMarked(GRAY));
-    CHECK(cell2.isMarked(GRAY));
+    CHECK(cell.isMarkedGray());
+    CHECK(cell2.isMarkedGray());
 
     CHECK(x == 0);
 
     x += obj != obj2;
-    CHECK(cell.isMarked(GRAY));
-    CHECK(cell2.isMarked(GRAY));
+    CHECK(cell.isMarkedGray());
+    CHECK(cell2.isMarkedGray());
     x += obj != wrapper2;
-    CHECK(cell.isMarked(GRAY));
-    CHECK(cell2.isMarked(GRAY));
+    CHECK(cell.isMarkedGray());
+    CHECK(cell2.isMarkedGray());
     x += wrapper != obj2;
-    CHECK(cell.isMarked(GRAY));
-    CHECK(cell2.isMarked(GRAY));
+    CHECK(cell.isMarkedGray());
+    CHECK(cell2.isMarkedGray());
     x += wrapper != wrapper2;
-    CHECK(cell.isMarked(GRAY));
-    CHECK(cell2.isMarked(GRAY));
+    CHECK(cell.isMarkedGray());
+    CHECK(cell2.isMarkedGray());
 
     CHECK(x == 4);
 
     return true;
 }
 
 END_TEST(testUnbarrieredEquality)
--- a/js/src/jsapi-tests/testGCMarking.cpp
+++ b/js/src/jsapi-tests/testGCMarking.cpp
@@ -362,19 +362,19 @@ BEGIN_TEST(testIncrementalRoots)
     rt->gc.startDebugGC(GC_NORMAL, budget);
 
     // We'd better be between iGC slices now. There's always a risk that
     // something will decide that we need to do a full GC (such as gczeal, but
     // that is turned off.)
     MOZ_ASSERT(JS::IsIncrementalGCInProgress(cx));
 
     // And assert that the mark bits are as we expect them to be.
-    MOZ_ASSERT(vec[0]->asTenured().isMarked());
-    MOZ_ASSERT(!leafHandle->asTenured().isMarked());
-    MOZ_ASSERT(!leafOwnerHandle->asTenured().isMarked());
+    MOZ_ASSERT(vec[0]->asTenured().isMarkedAny());
+    MOZ_ASSERT(!leafHandle->asTenured().isMarkedAny());
+    MOZ_ASSERT(!leafOwnerHandle->asTenured().isMarkedAny());
 
 #ifdef DEBUG
     // Remember the current GC number so we can assert that no GC occurs
     // between operations.
     auto currentGCNumber = rt->gc.gcNumber();
 #endif
 
     // Now do the incremental GC's worst nightmare: rip an unmarked object
@@ -382,37 +382,37 @@ BEGIN_TEST(testIncrementalRoots)
     // it off the un-prebarriered root, in fact). The pre-barrier on the
     // overwrite of the source location should cause this object to be marked.
     if (!JS_SetProperty(cx, leafOwnerHandle, "obj", JS::UndefinedHandleValue))
         return false;
     MOZ_ASSERT(rt->gc.gcNumber() == currentGCNumber);
     if (!JS_SetProperty(cx, vec[0], "newobj", leafValueHandle))
         return false;
     MOZ_ASSERT(rt->gc.gcNumber() == currentGCNumber);
-    MOZ_ASSERT(leafHandle->asTenured().isMarked());
+    MOZ_ASSERT(leafHandle->asTenured().isMarkedAny());
 
     // Also take an unmarked object 'leaf2' from the graph and add an
     // additional edge from the root to it. This will not be marked by any
     // pre-barrier, but it is still in the live graph so it will eventually get
     // marked.
     //
     // Note that the root->leaf2 edge will *not* be marked through, since the
     // root is already marked, but that only matters if doing a compacting GC
     // and the compacting GC repeats the whole marking phase to update
     // pointers.
     {
         JS::RootedValue leaf2(cx);
         if (!JS_GetProperty(cx, leafOwnerHandle, "leaf2", &leaf2))
             return false;
         MOZ_ASSERT(rt->gc.gcNumber() == currentGCNumber);
-        MOZ_ASSERT(!leaf2.toObject().asTenured().isMarked());
+        MOZ_ASSERT(!leaf2.toObject().asTenured().isMarkedAny());
         if (!JS_SetProperty(cx, vec[0], "leafcopy", leaf2))
             return false;
         MOZ_ASSERT(rt->gc.gcNumber() == currentGCNumber);
-        MOZ_ASSERT(!leaf2.toObject().asTenured().isMarked());
+        MOZ_ASSERT(!leaf2.toObject().asTenured().isMarkedAny());
     }
 
     // Finish the GC using an unlimited budget.
     auto unlimited = js::SliceBudget::unlimited();
     rt->gc.debugGCSlice(unlimited);
 
     // Access the leaf object to try to trigger a crash if it is dead.
     if (!JS_SetProperty(cx, leafHandle, "toes", JS::UndefinedHandleValue))
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -617,17 +617,17 @@ struct VisitGrayCallbackFunctor {
     GCThingCallback callback_;
     void* closure_;
     VisitGrayCallbackFunctor(GCThingCallback callback, void* closure)
       : callback_(callback), closure_(closure)
     {}
 
     template <class T>
     void operator()(T tp) const {
-        if ((*tp)->isTenured() && (*tp)->asTenured().isMarked(gc::GRAY))
+        if ((*tp)->isTenured() && (*tp)->asTenured().isMarkedGray())
             callback_(closure_, JS::GCCellPtr(*tp));
     }
 };
 } // namespace (anonymous)
 
 JS_FRIEND_API(void)
 js::VisitGrayWrapperTargets(Zone* zone, GCThingCallback callback, void* closure)
 {
@@ -1099,20 +1099,20 @@ struct DumpHeapTracer : public JS::Callb
 
     void onChild(const JS::GCCellPtr& thing) override;
 };
 
 static char
 MarkDescriptor(void* thing)
 {
     gc::TenuredCell* cell = gc::TenuredCell::fromPointer(thing);
-    if (cell->isMarked(gc::BLACK))
-        return cell->isMarked(gc::GRAY) ? 'G' : 'B';
+    if (cell->isMarkedAny())
+        return cell->isMarkedGray() ? 'G' : 'B';
     else
-        return cell->isMarked(gc::GRAY) ? 'X' : 'W';
+        return cell->isMarkedGray() ? 'X' : 'W';
 }
 
 static void
 DumpHeapVisitZone(JSRuntime* rt, void* data, Zone* zone)
 {
     DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
     fprintf(dtrc->output, "# zone %p\n", (void*)zone);
 }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -512,24 +512,24 @@ Arena::finalize(FreeOp* fop, AllocKind t
 
     FreeSpan newListHead;
     FreeSpan* newListTail = &newListHead;
     size_t nmarked = 0;
 
     if (MOZ_UNLIKELY(MemProfiler::enabled())) {
         for (ArenaCellIterUnderFinalize i(this); !i.done(); i.next()) {
             T* t = i.get<T>();
-            if (t->asTenured().isMarked())
+            if (t->asTenured().isMarkedAny())
                 MemProfiler::MarkTenured(reinterpret_cast<void*>(t));
         }
     }
 
     for (ArenaCellIterUnderFinalize i(this); !i.done(); i.next()) {
         T* t = i.get<T>();
-        if (t->asTenured().isMarked()) {
+        if (t->asTenured().isMarkedAny()) {
             uint_fast16_t thing = uintptr_t(t) & ArenaMask;
             if (thing != firstThingOrSuccessorOfLastMarkedThing) {
                 // We just finished passing over one or more free things,
                 // so record a new FreeSpan.
                 newListTail->initBounds(firstThingOrSuccessorOfLastMarkedThing,
                                         thing - thingSize, this);
                 newListTail = newListTail->nextSpanUnchecked(this);
             }
@@ -2042,18 +2042,18 @@ RelocateArena(Arena* arena, SliceBudget&
         sliceBudget.step();
     }
 
 #ifdef DEBUG
     for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
         TenuredCell* src = i.getCell();
         MOZ_ASSERT(RelocationOverlay::isCellForwarded(src));
         TenuredCell* dest = Forwarded(src);
-        MOZ_ASSERT(src->isMarked(BLACK) == dest->isMarked(BLACK));
-        MOZ_ASSERT(src->isMarked(GRAY) == dest->isMarked(GRAY));
+        MOZ_ASSERT(src->isMarkedAny() == dest->isMarkedAny());
+        MOZ_ASSERT(src->isMarkedGray() == dest->isMarkedGray());
     }
 #endif
 }
 
 static inline bool
 ShouldProtectRelocatedArenas(JS::gcreason::Reason reason)
 {
     // For zeal mode collections we don't release the relocated arenas
@@ -3622,17 +3622,17 @@ ArenaLists::checkEmptyArenaList(AllocKin
     if (!arenaLists(kind).isEmpty()) {
         size_t max_cells = 20;
         char *env = getenv("JS_GC_MAX_LIVE_CELLS");
         if (env && *env)
             max_cells = atol(env);
         for (Arena* current = arenaLists(kind).head(); current; current = current->next) {
             for (ArenaCellIterUnderGC i(current); !i.done(); i.next()) {
                 TenuredCell* t = i.getCell();
-                MOZ_ASSERT(t->isMarked(), "unmarked cells should have been finalized");
+                MOZ_ASSERT(t->isMarkedAny(), "unmarked cells should have been finalized");
                 if (++num_live <= max_cells) {
                     fprintf(stderr, "ERROR: GC found live Cell %p of kind %s at shutdown\n",
                             t, AllocKindToAscii(kind));
                 }
             }
         }
         fprintf(stderr, "ERROR: GC found %" PRIuSIZE " live Cells at shutdown\n", num_live);
     }
@@ -4472,26 +4472,26 @@ js::gc::MarkingValidator::validate()
             uintptr_t end = arena->thingsEnd();
             while (thing < end) {
                 Cell* cell = (Cell*)thing;
 
                 /*
                  * If a non-incremental GC wouldn't have collected a cell, then
                  * an incremental GC won't collect it.
                  */
-                if (bitmap->isMarked(cell, BLACK))
-                    MOZ_RELEASE_ASSERT(incBitmap->isMarked(cell, BLACK));
+                if (bitmap->isMarkedAny(cell))
+                    MOZ_RELEASE_ASSERT(incBitmap->isMarkedAny(cell));
 
                 /*
                  * If the cycle collector isn't allowed to collect an object
                  * after a non-incremental GC has run, then it isn't allowed to
                  * collected it after an incremental GC.
                  */
-                if (!bitmap->isMarked(cell, GRAY))
-                    MOZ_RELEASE_ASSERT(!incBitmap->isMarked(cell, GRAY));
+                if (!bitmap->isMarkedGray(cell))
+                    MOZ_RELEASE_ASSERT(!incBitmap->isMarkedGray(cell));
 
                 thing += Arena::thingSize(kind);
             }
         }
     }
 }
 
 #endif // JS_GC_ZEAL
@@ -4587,17 +4587,17 @@ void
 JSCompartment::findOutgoingEdges(ZoneComponentFinder& finder)
 {
     for (js::WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
         CrossCompartmentKey& key = e.front().mutableKey();
         MOZ_ASSERT(!key.is<JSString*>());
         bool needsEdge = true;
         if (key.is<JSObject*>()) {
             TenuredCell& other = key.as<JSObject*>()->asTenured();
-            needsEdge = !other.isMarked(BLACK) || other.isMarked(GRAY);
+            needsEdge = !other.isMarkedAny() || other.isMarkedGray();
         }
         key.applyToWrapped(AddOutgoingEdgeFunctor(needsEdge, finder));
     }
 }
 
 void
 Zone::findOutgoingEdges(ZoneComponentFinder& finder)
 {
@@ -4861,21 +4861,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(rt, &src) && src->asTenured().isMarked(GRAY))
+                if (IsMarkedUnbarriered(rt, &src) && src->asTenured().isMarkedGray())
                     TraceManuallyBarrieredEdge(&rt->gc.marker, &dst,
                                                "cross-compartment gray pointer");
             } else {
-                if (IsMarkedUnbarriered(rt, &src) && !src->asTenured().isMarked(GRAY))
+                if (IsMarkedUnbarriered(rt, &src) && !src->asTenured().isMarkedGray())
                     TraceManuallyBarrieredEdge(&rt->gc.marker, &dst,
                                                "cross-compartment black pointer");
             }
         }
 
         if (unlinkList)
             c->gcIncomingGrayPointers = nullptr;
     }
@@ -5511,17 +5511,17 @@ GCRuntime::drainMarkStack(SliceBudget& s
     /* Run a marking slice and return whether the stack is now empty. */
     gcstats::AutoPhase ap(stats(), phase);
     return marker.drainMarkStack(sliceBudget) ? Finished : NotFinished;
 }
 
 static void
 SweepThing(Shape* shape)
 {
-    if (!shape->isMarked())
+    if (!shape->isMarkedAny())
         shape->sweep();
 }
 
 static void
 SweepThing(JSScript* script, AutoClearTypeInferenceStateOnOOM* oom)
 {
     script->maybeSweepTypes(oom);
 }
@@ -6900,17 +6900,17 @@ GCRuntime::maybeDoCycleCollection()
     const static double ExcessiveGrayCompartments = 0.8;
     const static size_t LimitGrayCompartments = 200;
 
     size_t compartmentsTotal = 0;
     size_t compartmentsGray = 0;
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         ++compartmentsTotal;
         GlobalObject* global = c->unsafeUnbarrieredMaybeGlobal();
-        if (global && global->asTenured().isMarked(GRAY))
+        if (global && global->asTenured().isMarkedGray())
             ++compartmentsGray;
     }
     double grayFraction = double(compartmentsGray) / double(compartmentsTotal);
     if (grayFraction > ExcessiveGrayCompartments || compartmentsGray > LimitGrayCompartments)
         callDoCycleCollectionCallback(rt->activeContextFromOwnThread());
 }
 
 void
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3481,17 +3481,17 @@ js::detail::CopyScript(JSContext* cx, Ha
     if (src->treatAsRunOnce() && !src->functionNonDelazifying()) {
         JS_ReportErrorASCII(cx, "No cloning toplevel run-once scripts");
         return false;
     }
 
     /* NB: Keep this in sync with XDRScript. */
 
     /* Some embeddings are not careful to use ExposeObjectToActiveJS as needed. */
-    MOZ_ASSERT(!src->sourceObject()->asTenured().isMarked(gc::GRAY));
+    MOZ_ASSERT(!src->sourceObject()->asTenured().isMarkedGray());
 
     uint32_t nconsts   = src->hasConsts()   ? src->consts()->length   : 0;
     uint32_t nobjects  = src->hasObjects()  ? src->objects()->length  : 0;
     uint32_t nscopes   = src->scopes()->length;
     uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
     uint32_t nscopenotes = src->hasScopeNotes() ? src->scopeNotes()->length : 0;
     uint32_t nyieldoffsets = src->hasYieldAndAwaitOffsets() ? src->yieldAndAwaitOffsets().length() : 0;
 
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -134,17 +134,17 @@ ObjectValueMap::findZoneEdges()
     /*
      * For unmarked weakmap keys with delegates in a different zone, add a zone
      * edge to ensure that the delegate zone finishes marking before the key
      * zone.
      */
     JS::AutoSuppressGCAnalysis nogc;
     for (Range r = all(); !r.empty(); r.popFront()) {
         JSObject* key = r.front().key();
-        if (key->asTenured().isMarked(BLACK) && !key->asTenured().isMarked(GRAY))
+        if (key->asTenured().isMarkedAny() && !key->asTenured().isMarkedGray())
             continue;
         JSObject* delegate = getDelegate(key);
         if (!delegate)
             continue;
         Zone* delegateZone = delegate->zone();
         if (delegateZone == zone() || !delegateZone->isGCMarking())
             continue;
         if (!delegateZone->gcSweepGroupEdges().put(key->zone()))
--- a/js/src/proxy/Wrapper.cpp
+++ b/js/src/proxy/Wrapper.cpp
@@ -336,19 +336,19 @@ Wrapper::wrappedObject(JSObject* wrapper
     MOZ_ASSERT(wrapper->is<WrapperObject>());
     JSObject* target = wrapper->as<ProxyObject>().target();
 
     // Eagerly unmark gray wrapper targets so we can assert that we don't create
     // black to gray edges. An incremental GC will eventually mark the targets
     // of black wrappers black but while it is in progress we can observe gray
     // targets. Expose rather than returning a gray object in this case.
     if (target) {
-        if (wrapper->isMarked(gc::BLACK) && !wrapper->isMarked(gc::GRAY))
+        if (wrapper->isMarkedAny() && !wrapper->isMarkedGray())
             MOZ_ASSERT(JS::ObjectIsNotGray(target));
-        if (!wrapper->isMarked(gc::GRAY))
+        if (!wrapper->isMarkedGray())
             JS::ExposeObjectToActiveJS(target);
     }
 
     return target;
 }
 
 JS_FRIEND_API(JSObject*)
 js::UncheckedUnwrapWithoutExpose(JSObject* wrapped)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5817,19 +5817,19 @@ GetMarks(JSContext* cx, unsigned argc, V
 
     for (uint32_t i = 0; i < length; i++) {
         const char* color;
         JSObject* obj = observers->get()[i];
         if (!obj) {
             color = "dead";
         } else {
             gc::TenuredCell* cell = &obj->asTenured();
-            if (cell->isMarked(gc::GRAY))
+            if (cell->isMarkedGray())
                 color = "gray";
-            else if (cell->isMarked(gc::BLACK))
+            else if (cell->isMarkedAny())
                 color = "black";
             else
                 color = "unmarked";
         }
         JSString* s = JS_NewStringCopyZ(cx, color);
         if (!s)
             return false;
         if (!NewbornArrayPush(cx, ret, StringValue(s)))
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1469,25 +1469,25 @@ PropertyTree::inlinedGetChild(JSContext*
             Shape* tmp = existingShape;
             TraceManuallyBarrieredEdge(zone->barrierTracer(), &tmp, "read barrier");
             MOZ_ASSERT(tmp == existingShape);
             return existingShape;
         }
         if (!zone->isGCSweepingOrCompacting() ||
             !IsAboutToBeFinalizedUnbarriered(&existingShape))
         {
-            if (existingShape->isMarked(gc::GRAY))
+            if (existingShape->isMarkedGray())
                 UnmarkGrayShapeRecursively(existingShape);
             return existingShape;
         }
         /*
          * The shape we've found is unreachable and due to be finalized, so
          * remove our weak reference to it and don't use it.
          */
-        MOZ_ASSERT(parent->isMarked());
+        MOZ_ASSERT(parent->isMarkedAny());
         parent->removeChild(existingShape);
     }
 
     RootedShape parentRoot(cx, parent);
     Shape* shape = Shape::new_(cx, child, parentRoot->numFixedSlots());
     if (!shape)
         return nullptr;
 
@@ -1510,17 +1510,17 @@ Shape::sweep()
      * We detach the child from the parent if the parent is reachable.
      *
      * This test depends on shape arenas not being freed until after we finish
      * incrementally sweeping them. If that were not the case the parent pointer
      * could point to a marked cell that had been deallocated and then
      * reallocated, since allocating a cell in a zone that is being marked will
      * set the mark bit for that cell.
      */
-    if (parent && parent->isMarked()) {
+    if (parent && parent->isMarkedAny()) {
         if (inDictionary()) {
             if (parent->listp == &parent)
                 parent->listp = nullptr;
         } else {
             parent->removeChild(this);
         }
     }
 }
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3008,17 +3008,17 @@ ObjectGroup::detachNewScript(bool writeB
         unboxedLayout().setNewScript(nullptr, writeBarrier);
 }
 
 void
 ObjectGroup::maybeClearNewScriptOnOOM()
 {
     MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
 
-    if (!isMarked())
+    if (!isMarkedAny())
         return;
 
     TypeNewScript* newScript = anyNewScript();
     if (!newScript)
         return;
 
     addFlags(OBJECT_FLAG_NEW_SCRIPT_CLEARED);