Bug 1158353 - Clean up eager string marking; r=jonco

This commit is contained in:
Terrence Cole 2015-04-24 14:52:55 -07:00
parent 6711327e25
commit bbba79f9db
3 changed files with 111 additions and 105 deletions

View File

@ -775,6 +775,109 @@ js::GCMarker::eagerlyMarkChildren(Shape* shape)
} while (shape && mark(shape));
}
void
JSString::traceChildren(JSTracer* trc)
{
if (hasBase())
traceBase(trc);
else if (isRope())
asRope().traceChildren(trc);
}
inline void
GCMarker::eagerlyMarkChildren(JSString* str)
{
if (str->isLinear())
eagerlyMarkChildren(&str->asLinear());
else
eagerlyMarkChildren(&str->asRope());
}
void
JSString::traceBase(JSTracer* trc)
{
MOZ_ASSERT(hasBase());
TraceManuallyBarrieredEdge(trc, &d.s.u3.base, "base");
}
inline void
js::GCMarker::eagerlyMarkChildren(JSLinearString* linearStr)
{
JS_COMPARTMENT_ASSERT(runtime(), linearStr);
MOZ_ASSERT(linearStr->isMarked());
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;
JS_COMPARTMENT_ASSERT(runtime(), linearStr);
if (!mark(static_cast<JSString*>(linearStr)))
break;
}
}
void
JSRope::traceChildren(JSTracer* trc) {
js::TraceManuallyBarrieredEdge(trc, &d.s.u2.left, "left child");
js::TraceManuallyBarrieredEdge(trc, &d.s.u3.right, "right child");
}
inline void
js::GCMarker::eagerlyMarkChildren(JSRope* rope)
{
// This function tries to scan the whole rope tree using the marking stack
// as temporary storage. If that becomes full, the unscanned ropes are
// added to the delayed marking list. When the function returns, the
// marking stack is at the same depth as it was on entry. This way we avoid
// using tags when pushing ropes to the stack as ropes never leak to other
// users of the stack. This also assumes that a rope can only point to
// other ropes or linear strings, it cannot refer to GC things of other
// types.
ptrdiff_t savedPos = stack.position();
JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
while (true) {
JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope());
JS_COMPARTMENT_ASSERT(runtime(), rope);
MOZ_ASSERT(rope->isMarked());
JSRope* next = nullptr;
JSString* right = rope->rightChild();
if (!right->isPermanentAtom() &&
mark(right))
{
if (right->isLinear())
eagerlyMarkChildren(&right->asLinear());
else
next = &right->asRope();
}
JSString* left = rope->leftChild();
if (!left->isPermanentAtom() &&
mark(left))
{
if (left->isLinear()) {
eagerlyMarkChildren(&left->asLinear());
} else {
// When both children are ropes, set aside the right one to
// scan it later.
if (next && !stack.push(reinterpret_cast<uintptr_t>(next)))
delayMarkingChildren(next);
next = &left->asRope();
}
}
if (next) {
rope = next;
} else if (savedPos != stack.position()) {
MOZ_ASSERT(savedPos < stack.position());
rope = reinterpret_cast<JSRope*>(stack.pop());
} else {
break;
}
}
MOZ_ASSERT(savedPos == stack.position());
}
template <typename T>
static inline void
CheckIsMarkedThing(T* thingp)
@ -1072,92 +1175,6 @@ gc::MarkIdForBarrier(JSTracer* trc, jsid* idp, const char* name)
/*** Push Mark Stack ***/
static inline void
ScanLinearString(GCMarker* gcmarker, JSLinearString* str)
{
JS_COMPARTMENT_ASSERT(gcmarker->runtime(), str);
MOZ_ASSERT(str->isMarked());
/*
* Add extra asserts to confirm the static type to detect incorrect string
* mutations.
*/
MOZ_ASSERT(str->JSString::isLinear());
while (str->hasBase()) {
str = str->base();
MOZ_ASSERT(str->JSString::isLinear());
if (str->isPermanentAtom())
break;
JS_COMPARTMENT_ASSERT(gcmarker->runtime(), str);
if (!str->markIfUnmarked())
break;
}
}
/*
* The function tries to scan the whole rope tree using the marking stack as
* temporary storage. If that becomes full, the unscanned ropes are added to
* the delayed marking list. When the function returns, the marking stack is
* at the same depth as it was on entry. This way we avoid using tags when
* pushing ropes to the stack as ropes never leaks to other users of the
* stack. This also assumes that a rope can only point to other ropes or
* linear strings, it cannot refer to GC things of other types.
*/
static void
ScanRope(GCMarker* gcmarker, JSRope* rope)
{
ptrdiff_t savedPos = gcmarker->stack.position();
JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
for (;;) {
JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope());
JS_COMPARTMENT_ASSERT(gcmarker->runtime(), rope);
MOZ_ASSERT(rope->isMarked());
JSRope* next = nullptr;
JSString* right = rope->rightChild();
if (!right->isPermanentAtom() && right->markIfUnmarked()) {
if (right->isLinear())
ScanLinearString(gcmarker, &right->asLinear());
else
next = &right->asRope();
}
JSString* left = rope->leftChild();
if (!left->isPermanentAtom() && left->markIfUnmarked()) {
if (left->isLinear()) {
ScanLinearString(gcmarker, &left->asLinear());
} else {
/*
* When both children are ropes, set aside the right one to
* scan it later.
*/
if (next && !gcmarker->stack.push(reinterpret_cast<uintptr_t>(next)))
gcmarker->delayMarkingChildren(next);
next = &left->asRope();
}
}
if (next) {
rope = next;
} else if (savedPos != gcmarker->stack.position()) {
MOZ_ASSERT(savedPos < gcmarker->stack.position());
rope = reinterpret_cast<JSRope*>(gcmarker->stack.pop());
} else {
break;
}
}
MOZ_ASSERT(savedPos == gcmarker->stack.position());
}
inline void
GCMarker::eagerlyMarkChildren(JSString* str)
{
if (str->isLinear())
ScanLinearString(this, &str->asLinear());
else
ScanRope(this, &str->asRope());
}
/*
* This function is used by the cycle collector to trace through a
* shape. The cycle collector does not care about shapes or base

View File

@ -14,6 +14,8 @@
#include "js/SliceBudget.h"
#include "js/TracingAPI.h"
class JSLinearString;
class JSRope;
namespace js {
class BaseShape;
class GCMarker;
@ -245,9 +247,11 @@ class GCMarker : public JSTracer
template <typename T> void markAndTraceChildren(T* thing);
template <typename T> void markAndPush(StackTag tag, T* thing);
template <typename T> void markAndScan(T* thing);
void eagerlyMarkChildren(Shape* shape);
void eagerlyMarkChildren(JSLinearString* str);
void eagerlyMarkChildren(JSRope* rope);
void eagerlyMarkChildren(JSString* str);
void eagerlyMarkChildren(LazyScript *thing);
void eagerlyMarkChildren(Shape* shape);
// We may not have concrete types yet, so this has to be out of the header.
template <typename T>

View File

@ -466,10 +466,7 @@ class JSString : public js::gc::TenuredCell
inline JSLinearString* base() const;
void traceBase(JSTracer* trc) {
MOZ_ASSERT(hasBase());
js::TraceManuallyBarrieredEdge(trc, &d.s.u3.base, "base");
}
void traceBase(JSTracer* trc);
/* Only called by the GC for strings with the AllocKind::STRING kind. */
@ -509,7 +506,7 @@ class JSString : public js::gc::TenuredCell
bool equals(const char* s);
#endif
inline void traceChildren(JSTracer* trc);
void traceChildren(JSTracer* trc);
static MOZ_ALWAYS_INLINE void readBarrier(JSString* thing) {
if (thing->isPermanentAtom())
@ -578,10 +575,7 @@ class JSRope : public JSString
return d.s.u3.right;
}
void traceChildren(JSTracer* trc) {
js::TraceManuallyBarrieredEdge(trc, &d.s.u2.left, "left child");
js::TraceManuallyBarrieredEdge(trc, &d.s.u3.right, "right child");
}
void traceChildren(JSTracer* trc);
static size_t offsetOfLeft() {
return offsetof(JSRope, d.s.u2.left);
@ -1272,15 +1266,6 @@ JSString::base() const
return d.s.u3.base;
}
inline void
JSString::traceChildren(JSTracer* trc)
{
if (hasBase())
traceBase(trc);
else if (isRope())
asRope().traceChildren(trc);
}
template<>
MOZ_ALWAYS_INLINE const char16_t*
JSLinearString::nonInlineChars(const JS::AutoCheckCannotGC& nogc) const