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 368650 e2339ab06af56cb05c0d5b917d6f660952b45a69
parent 368649 9fc4c6aabd8e2603f1a0495574d50b496e836c12
child 368651 04364ec85017d7646b11b1ed1f3e1cf61a59075b
push id46414
push usercbook@mozilla.com
push dateThu, 13 Jul 2017 14:44:58 +0000
treeherderautoland@6c8692cc9c49 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1380030
milestone56.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 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);