Bug 1380030 - Remove color constants from public API and replace with an internal MarkColor enum r=sfink

This commit is contained in:
Jon Coppeard 2017-07-12 18:31:55 +01:00
parent 6302159903
commit f761d9a064
7 changed files with 83 additions and 81 deletions

View File

@ -58,16 +58,10 @@ 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

View File

@ -242,6 +242,13 @@ FOR_EACH_ALLOCKIND(EXPAND_ELEMENT)
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.
@ -299,7 +306,7 @@ class TenuredCell : public Cell
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);
@ -897,6 +904,15 @@ static_assert(ArenasPerChunk == 62, "Do not accidentally change our heap's densi
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
{
@ -905,37 +921,38 @@ struct ChunkBitmap
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 {
/*
@ -950,13 +967,14 @@ struct ChunkBitmap
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);
@ -979,7 +997,8 @@ struct ChunkBitmap
"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;
}
};
@ -1155,15 +1174,6 @@ 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
{
@ -1289,9 +1299,8 @@ TenuredCell::isMarkedGray() const
}
bool
TenuredCell::markIfUnmarked(uint32_t color /* = BLACK */) const
TenuredCell::markIfUnmarked(MarkColor color /* = Black */) const
{
AssertValidColor(this, color);
return chunk()->bitmap.markIfUnmarked(this, color);
}
@ -1454,8 +1463,8 @@ 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,
};
@ -1489,10 +1498,10 @@ GetMarkInfo(js::gc::Cell* cell);
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 */

View File

@ -244,7 +244,7 @@ js::CheckTracedThing(JSTracer* trc, T* thing)
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()));
@ -295,17 +295,16 @@ 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
@ -619,7 +618,7 @@ js::TraceProcessGlobalRoot(JSTracer* trc, T* thing, const char* name)
// 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);
}
@ -983,7 +982,7 @@ js::GCMarker::mark(T* 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);
}
@ -1062,8 +1061,8 @@ Shape::traceChildren(JSTracer* trc)
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
@ -2343,7 +2342,7 @@ MarkStackIter::saveValueArray(NativeObject* obj, uintptr_t index, HeapSlot::Kind
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)
@ -2366,7 +2365,7 @@ GCMarker::start()
MOZ_ASSERT(!started);
started = true;
#endif
color = BLACK;
color = MarkColor::Black;
linearWeakMarkingDisabled_ = false;
MOZ_ASSERT(!unmarkedArenaStackTop);
@ -2398,7 +2397,7 @@ GCMarker::stop()
void
GCMarker::reset()
{
color = BLACK;
color = MarkColor::Black;
stack.reset();
MOZ_ASSERT(isMarkStackEmpty());
@ -2440,8 +2439,8 @@ GCMarker::pushValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end)
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);
}
@ -3465,14 +3464,14 @@ GetMarkWordAddress(Cell* cell)
}
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);

View File

@ -265,15 +265,15 @@ class GCMarker : public JSTracer
*/
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();
@ -366,7 +366,7 @@ class GCMarker : public JSTracer
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;

View File

@ -590,19 +590,19 @@ TestGrayUnmarking()
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;
@ -612,10 +612,10 @@ TestGrayUnmarking()
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;
@ -635,7 +635,7 @@ struct ColorCheckFunctor
// 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++;
@ -830,13 +830,13 @@ IsMarkedGray(Cell* cell)
}
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;
}

View File

@ -212,8 +212,8 @@ BEGIN_TEST(testUnbarrieredEquality)
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());

View File

@ -4471,7 +4471,7 @@ js::gc::MarkingValidator::validate()
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
@ -4841,21 +4841,21 @@ js::DelayCrossCompartmentGrayMarking(JSObject* src)
}
static void
MarkIncomingCrossCompartmentPointers(JSRuntime* rt, const uint32_t color)
MarkIncomingCrossCompartmentPointers(JSRuntime* rt, MarkColor color)
{
MOZ_ASSERT(color == BLACK || color == GRAY);
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]);
gcstats::AutoPhase ap1(rt->gc.stats(), statsPhases[unsigned(color)]);
bool unlinkList = color == GRAY;
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;
@ -4865,7 +4865,7 @@ MarkIncomingCrossCompartmentPointers(JSRuntime* rt, const uint32_t color)
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");
@ -4976,7 +4976,7 @@ GCRuntime::endMarkingSweepGroup()
* 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);
/*
@ -4992,7 +4992,7 @@ GCRuntime::endMarkingSweepGroup()
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);