Bug 1135985 - Introduce a new, strongly-typed tracing path; r=jonco, r=sfink

--HG--
extra : rebase_source : c664b207ab859504c66b32cfd4126a67cbf0cafd
This commit is contained in:
Terrence Cole 2015-02-26 14:15:26 -08:00
parent 6613491227
commit 1ad0c97fed
3 changed files with 409 additions and 29 deletions

View File

@ -119,7 +119,7 @@ class JS_PUBLIC_API(JSTracer)
}
void setTracingName(const char *name) {
setTracingDetails(nullptr, (void *)name, size_t(-1));
setTracingDetails(nullptr, (void *)name, InvalidIndex);
}
// Remove the currently set tracing details.
@ -128,6 +128,8 @@ class JS_PUBLIC_API(JSTracer)
debugPrintArg_ = nullptr;
}
const static size_t InvalidIndex = size_t(-1);
// Return true if tracing details are currently set.
bool hasTracingDetails() const;

View File

@ -7,6 +7,8 @@
#include "gc/Marking.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/TypeTraits.h"
#include "jsprf.h"
@ -31,6 +33,9 @@ using namespace js;
using namespace js::gc;
using mozilla::DebugOnly;
using mozilla::IsBaseOf;
using mozilla::IsSame;
using mozilla::MakeRange;
void * const js::NullPtr::constNullValue = nullptr;
@ -147,24 +152,33 @@ AsGCMarker(JSTracer *trc)
return static_cast<GCMarker *>(trc);
}
template <typename T> bool ThingIsPermanentAtom(T *thing) { return false; }
template <> bool ThingIsPermanentAtom<JSString>(JSString *str) { return str->isPermanentAtom(); }
template <> bool ThingIsPermanentAtom<JSFlatString>(JSFlatString *str) { return str->isPermanentAtom(); }
template <> bool ThingIsPermanentAtom<JSLinearString>(JSLinearString *str) { return str->isPermanentAtom(); }
template <> bool ThingIsPermanentAtom<JSAtom>(JSAtom *atom) { return atom->isPermanent(); }
template <> bool ThingIsPermanentAtom<PropertyName>(PropertyName *name) { return name->isPermanent(); }
template <> bool ThingIsPermanentAtom<JS::Symbol>(JS::Symbol *sym) { return sym->isWellKnownSymbol(); }
template <typename T> bool ThingIsPermanentAtomOrWellKnownSymbol(T *thing) { return false; }
template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JSString>(JSString *str) {
return str->isPermanentAtom();
}
template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JSFlatString>(JSFlatString *str) {
return str->isPermanentAtom();
}
template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JSLinearString>(JSLinearString *str) {
return str->isPermanentAtom();
}
template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JSAtom>(JSAtom *atom) {
return atom->isPermanent();
}
template <> bool ThingIsPermanentAtomOrWellKnownSymbol<PropertyName>(PropertyName *name) {
return name->isPermanent();
}
template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JS::Symbol>(JS::Symbol *sym) {
return sym->isWellKnownSymbol();
}
template<typename T>
static inline void
CheckMarkedThing(JSTracer *trc, T **thingp)
CheckMarkedThing(JSTracer *trc, T thing)
{
#ifdef DEBUG
MOZ_ASSERT(trc);
MOZ_ASSERT(thingp);
T *thing = *thingp;
MOZ_ASSERT(*thingp);
MOZ_ASSERT(thing);
thing = MaybeForwarded(thing);
@ -173,13 +187,13 @@ CheckMarkedThing(JSTracer *trc, T **thingp)
return;
MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc) && !Nursery::IsMinorCollectionTracer(trc),
!IsForwarded(*thingp));
!IsForwarded(thing));
/*
* Permanent atoms are not associated with this runtime, but will be ignored
* during marking.
*/
if (ThingIsPermanentAtom(thing))
if (ThingIsPermanentAtomOrWellKnownSymbol(thing))
return;
Zone *zone = thing->zoneFromAnyThread();
@ -189,10 +203,10 @@ CheckMarkedThing(JSTracer *trc, T **thingp)
MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc), CurrentThreadCanAccessRuntime(rt));
MOZ_ASSERT(zone->runtimeFromAnyThread() == trc->runtime());
MOZ_ASSERT(trc->hasTracingDetails());
MOZ_ASSERT(thing->isAligned());
MOZ_ASSERT(MapTypeToTraceKind<T>::kind == GetGCThingTraceKind(thing));
MOZ_ASSERT(MapTypeToTraceKind<typename mozilla::RemovePointer<T>::Type>::kind ==
GetGCThingTraceKind(thing));
/*
* Do not check IsMarkingTracer directly -- it should only be used in paths
@ -226,6 +240,37 @@ CheckMarkedThing(JSTracer *trc, T **thingp)
#endif
}
template<>
void
CheckMarkedThing<Value>(JSTracer *trc, Value val)
{
#ifdef DEBUG
if (val.isString())
CheckMarkedThing(trc, val.toString());
else if (val.isObject())
CheckMarkedThing(trc, &val.toObject());
else if (val.isSymbol())
CheckMarkedThing(trc, val.toSymbol());
#endif
}
template <>
void
CheckMarkedThing<jsid>(JSTracer *trc, jsid id)
{
#ifdef DEBUG
if (JSID_IS_STRING(id))
CheckMarkedThing(trc, JSID_TO_STRING(id));
else if (JSID_IS_SYMBOL(id))
CheckMarkedThing(trc, JSID_TO_SYMBOL(id));
#endif
}
#define JS_ROOT_MARKING_ASSERT(trc) \
MOZ_ASSERT_IF(trc->isMarkingTracer(), \
trc->runtime()->gc.state() == NO_INCREMENTAL || \
trc->runtime()->gc.state() == MARK_ROOTS);
/*
* We only set the maybeAlive flag for objects and scripts. It's assumed that,
* if a compartment is alive, then it will have at least some live object or
@ -260,12 +305,328 @@ SetMaybeAliveFlag(JSScript *thing)
thing->compartment()->maybeAlive = true;
}
#define FOR_EACH_GC_LAYOUT(D) \
D(Object, JSObject) \
D(String, JSString) \
D(Symbol, JS::Symbol) \
D(Script, JSScript) \
D(Shape, js::Shape) \
D(BaseShape, js::BaseShape) \
D(JitCode, js::jit::JitCode) \
D(LazyScript, js::LazyScript) \
D(ObjectGroup, js::ObjectGroup)
// A C++ version of JSGCTraceKind
enum class TraceKind {
#define NAMES(name, _) name,
FOR_EACH_GC_LAYOUT(NAMES)
#undef NAMES
};
#define FOR_EACH_GC_POINTER_TYPE(D) \
D(BaseShape *) \
D(UnownedBaseShape *) \
D(jit::JitCode *) \
D(NativeObject *) \
D(ArrayObject *) \
D(ArgumentsObject *) \
D(ArrayBufferObject *) \
D(ArrayBufferObjectMaybeShared *) \
D(ArrayBufferViewObject *) \
D(DebugScopeObject *) \
D(GlobalObject *) \
D(JSObject *) \
D(JSFunction *) \
D(NestedScopeObject *) \
D(PlainObject *) \
D(SavedFrame *) \
D(ScopeObject *) \
D(SharedArrayBufferObject *) \
D(SharedTypedArrayObject *) \
D(JSScript *) \
D(LazyScript *) \
D(Shape *) \
D(JSAtom *) \
D(JSString *) \
D(JSFlatString *) \
D(JSLinearString *) \
D(PropertyName *) \
D(JS::Symbol *) \
D(js::ObjectGroup *) \
D(Value) \
D(jsid)
// The second parameter to BaseGCType is derived automatically based on T. The
// relation here is that for any T, the TraceKind will automatically,
// statically select the correct Cell layout for marking. Below, we instantiate
// each override with a declaration of the most derived layout type.
//
// Usage:
// BaseGCType<T>::type
//
// Examples:
// BaseGCType<JSFunction>::type => JSObject
// BaseGCType<UnownedBaseShape>::type => BaseShape
// etc.
template <typename T,
TraceKind = IsBaseOf<JSObject, T>::value ? TraceKind::Object
: IsBaseOf<JSString, T>::value ? TraceKind::String
: IsBaseOf<JS::Symbol, T>::value ? TraceKind::Symbol
: IsBaseOf<JSScript, T>::value ? TraceKind::Script
: IsBaseOf<Shape, T>::value ? TraceKind::Shape
: IsBaseOf<BaseShape, T>::value ? TraceKind::BaseShape
: IsBaseOf<jit::JitCode, T>::value ? TraceKind::JitCode
: IsBaseOf<LazyScript, T>::value ? TraceKind::LazyScript
: TraceKind::ObjectGroup>
struct BaseGCType;
#define IMPL_BASE_GC_TYPE(name, type_) \
template <typename T> struct BaseGCType<T, TraceKind:: name> { typedef type_ type; };
FOR_EACH_GC_LAYOUT(IMPL_BASE_GC_TYPE);
#undef IMPL_BASE_GC_TYPE
// Our barrier templates are parameterized on the pointer types so that we can
// share the definitions with Value and jsid. Thus, we need to strip the
// pointer before sending the type to BaseGCType and re-add it on the other
// side. As such:
template <typename T> struct PtrBaseGCType {};
template <> struct PtrBaseGCType<Value> { typedef Value type; };
template <> struct PtrBaseGCType<jsid> { typedef jsid type; };
template <typename T> struct PtrBaseGCType<T *> { typedef typename BaseGCType<T>::type *type; };
template <typename T> void DispatchToTracer(JSTracer *trc, T *thingp, const char *name, size_t i);
template <typename T> void DoTracing(JS::CallbackTracer *trc, T *thingp, const char *name, size_t i);
template <typename T> void DoMarking(GCMarker *gcmarker, T thing);
template <typename T>
void
js::TraceEdge(JSTracer *trc, BarrieredBase<T> *thingp, const char *name)
{
auto layout = reinterpret_cast<typename PtrBaseGCType<T>::type *>(thingp->unsafeGet());
DispatchToTracer(trc, layout, name, JSTracer::InvalidIndex);
}
template <typename T>
void
js::TraceManuallyBarrieredEdge(JSTracer *trc, T *thingp, const char *name)
{
auto layout = reinterpret_cast<typename PtrBaseGCType<T>::type *>(thingp);
DispatchToTracer(trc, layout, name, JSTracer::InvalidIndex);
}
template <typename T>
void
js::TraceRoot(JSTracer *trc, T *thingp, const char *name)
{
JS_ROOT_MARKING_ASSERT(trc);
auto layout = reinterpret_cast<typename PtrBaseGCType<T>::type *>(thingp);
DispatchToTracer(trc, layout, name, JSTracer::InvalidIndex);
}
template <typename T>
void
js::TraceRange(JSTracer *trc, size_t len, BarrieredBase<T> *thingp, const char *name)
{
for (auto i : MakeRange(len)) {
auto layout = reinterpret_cast<typename PtrBaseGCType<T>::type *>(&thingp[i]);
DispatchToTracer(trc, layout, name, i);
}
}
template <typename T>
void
js::TraceRootRange(JSTracer *trc, size_t len, T *thingp, const char *name)
{
JS_ROOT_MARKING_ASSERT(trc);
for (auto i : MakeRange(len)) {
auto layout = reinterpret_cast<typename PtrBaseGCType<T>::type *>(&thingp[i]);
DispatchToTracer(trc, layout, name, i);
}
}
// Instantiate a copy of the Tracing templates for each derived type.
#define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \
template void js::TraceEdge<type>(JSTracer *, BarrieredBase<type> *, const char *); \
template void js::TraceManuallyBarrieredEdge<type>(JSTracer *, type *, const char *); \
template void js::TraceRoot<type>(JSTracer *, type *, const char *); \
template void js::TraceRange<type>(JSTracer *, size_t, BarrieredBase<type> *, const char *); \
template void js::TraceRootRange<type>(JSTracer *, size_t, type *, const char *);
FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS)
#undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
// This method is responsible for dynamic dispatch to the real tracer
// implementation. Consider replacing this choke point with virtual dispatch:
// a sufficiently smart C++ compiler may be able to devirtualize some paths.
template <typename T>
void
DispatchToTracer(JSTracer *trc, T *thingp, const char *name, size_t i)
{
#define IS_SAME_TYPE_OR(name, type) mozilla::IsSame<type *, T>::value ||
static_assert(
FOR_EACH_GC_LAYOUT(IS_SAME_TYPE_OR)
mozilla::IsSame<T, JS::Value>::value ||
mozilla::IsSame<T, jsid>::value,
"Only the base cell layout types are allowed into marking/tracing internals");
#undef IS_SAME_TYPE_OR
CheckMarkedThing(trc, *thingp);
if (trc->isMarkingTracer())
return DoMarking(static_cast<GCMarker*>(trc), *thingp);
return DoTracing(static_cast<JS::CallbackTracer*>(trc), thingp, name, i);
}
template <typename T>
static inline bool
MustSkipMarking(T thing)
{
// Don't mark things outside a zone if we are in a per-zone GC.
return !thing->zone()->isGCMarking();
}
template <>
bool
MustSkipMarking<JSObject*>(JSObject *obj)
{
// We may mark a Nursery thing outside the context of the
// MinorCollectionTracer because of a pre-barrier. The pre-barrier is not
// needed in this case because we perform a minor collection before each
// incremental slice.
if (IsInsideNursery(obj))
return true;
// Don't mark things outside a zone if we are in a per-zone GC. It is
// faster to check our own arena header, which we can do since we know that
// the object is tenured.
return !TenuredCell::fromPointer(obj)->zone()->isGCMarking();
}
template <>
bool
MustSkipMarking<JSString*>(JSString *str)
{
// Don't mark permanent atoms, as they may be associated with another
// runtime. Note that PushMarkStack() also checks this, but we need to not
// run the isGCMarking test from off-main-thread, so have to check it here
// too.
return str->isPermanentAtom() ||
!str->zone()->isGCMarking();
}
template <>
bool
MustSkipMarking<JS::Symbol*>(JS::Symbol *sym)
{
// As for JSString, don't touch a globally owned well-known symbol from
// off-main-thread.
return sym->isWellKnownSymbol() ||
!sym->zone()->isGCMarking();
}
template <typename T>
void
DoMarking(GCMarker *gcmarker, T thing)
{
// Do per-type marking precondition checks.
if (MustSkipMarking(thing))
return;
PushMarkStack(gcmarker, thing);
// Mark the compartment as live.
SetMaybeAliveFlag(thing);
}
template <>
void
DoMarking<Value>(GCMarker *gcmarker, Value val)
{
if (val.isString())
DoMarking(gcmarker, val.toString());
else if (val.isObject())
DoMarking(gcmarker, &val.toObject());
else if (val.isSymbol())
DoMarking(gcmarker, val.toSymbol());
else
gcmarker->clearTracingDetails();
}
template <>
void
DoMarking<jsid>(GCMarker *gcmarker, jsid id)
{
if (JSID_IS_STRING(id))
DoMarking(gcmarker, JSID_TO_STRING(id));
else if (JSID_IS_SYMBOL(id))
DoMarking(gcmarker, JSID_TO_SYMBOL(id));
else
gcmarker->clearTracingDetails();
}
template <typename T>
void
DoTracing(JS::CallbackTracer *trc, T *thingp, const char *name, size_t i)
{
JSGCTraceKind kind = MapTypeToTraceKind<typename mozilla::RemovePointer<T>::Type>::kind;
trc->setTracingIndex(name, i);
trc->invoke((void **)thingp, kind);
trc->unsetTracingLocation();
}
template <>
void
DoTracing<Value>(JS::CallbackTracer *trc, Value *vp, const char *name, size_t i)
{
if (vp->isObject()) {
JSObject *prior = &vp->toObject();
JSObject *obj = prior;
DoTracing(trc, &obj, name, i);
if (obj != prior)
vp->setObjectOrNull(obj);
} else if (vp->isString()) {
JSString *prior = vp->toString();
JSString *str = prior;
DoTracing(trc, &str, name, i);
if (str != prior)
vp->setString(str);
} else if (vp->isSymbol()) {
JS::Symbol *prior = vp->toSymbol();
JS::Symbol *sym = prior;
DoTracing(trc, &sym, name, i);
if (sym != prior)
vp->setSymbol(sym);
} else {
/* Unset realLocation manually if we do not call MarkInternal. */
trc->unsetTracingLocation();
}
}
template <>
void
DoTracing<jsid>(JS::CallbackTracer *trc, jsid *idp, const char *name, size_t i)
{
if (JSID_IS_STRING(*idp)) {
JSString *prior = JSID_TO_STRING(*idp);
JSString *str = prior;
DoTracing(trc, &str, name, i);
if (str != prior)
*idp = NON_INTEGER_ATOM_TO_JSID(reinterpret_cast<JSAtom *>(str));
} else if (JSID_IS_SYMBOL(*idp)) {
JS::Symbol *prior = JSID_TO_SYMBOL(*idp);
JS::Symbol *sym = prior;
DoTracing(trc, &sym, name, i);
if (sym != prior)
*idp = SYMBOL_TO_JSID(sym);
} else {
/* Unset realLocation manually if we do not call MarkInternal. */
trc->unsetTracingLocation();
}
}
template<typename T>
static void
MarkInternal(JSTracer *trc, T **thingp)
{
CheckMarkedThing(trc, thingp);
T *thing = *thingp;
CheckMarkedThing(trc, thing);
if (trc->isMarkingTracer()) {
/*
@ -282,7 +643,7 @@ MarkInternal(JSTracer *trc, T **thingp)
* runtime. Note that PushMarkStack() also checks this, but the tests
* and maybeAlive write below should only be done on the main thread.
*/
if (ThingIsPermanentAtom(thing))
if (ThingIsPermanentAtomOrWellKnownSymbol(thing))
return;
/*
@ -302,11 +663,6 @@ MarkInternal(JSTracer *trc, T **thingp)
trc->clearTracingDetails();
}
#define JS_ROOT_MARKING_ASSERT(trc) \
MOZ_ASSERT_IF(trc->isMarkingTracer(), \
trc->runtime()->gc.state() == NO_INCREMENTAL || \
trc->runtime()->gc.state() == MARK_ROOTS);
namespace js {
namespace gc {
@ -333,7 +689,7 @@ MarkPermanentAtom(JSTracer *trc, JSAtom *atom, const char *name)
MOZ_ASSERT(atom->isPermanent());
CheckMarkedThing(trc, &atom);
CheckMarkedThing(trc, atom);
if (trc->isMarkingTracer()) {
// Atoms do not refer to other GC things so don't need to go on the mark stack.
@ -358,7 +714,7 @@ MarkWellKnownSymbol(JSTracer *trc, JS::Symbol *sym)
trc->setTracingName("wellKnownSymbols");
MOZ_ASSERT(sym->isWellKnownSymbol());
CheckMarkedThing(trc, &sym);
CheckMarkedThing(trc, sym);
if (trc->isMarkingTracer()) {
// Permanent atoms are marked before well-known symbols.
MOZ_ASSERT(sym->description()->isMarked());
@ -417,7 +773,7 @@ template <typename T>
static bool
IsMarked(T **thingp)
{
MOZ_ASSERT_IF(!ThingIsPermanentAtom(*thingp),
MOZ_ASSERT_IF(!ThingIsPermanentAtomOrWellKnownSymbol(*thingp),
CurrentThreadCanAccessRuntime((*thingp)->runtimeFromMainThread()));
return IsMarkedFromAnyThread(thingp);
}
@ -447,7 +803,7 @@ template <typename T>
static bool
IsAboutToBeFinalized(T **thingp)
{
MOZ_ASSERT_IF(!ThingIsPermanentAtom(*thingp),
MOZ_ASSERT_IF(!ThingIsPermanentAtomOrWellKnownSymbol(*thingp),
CurrentThreadCanAccessRuntime((*thingp)->runtimeFromMainThread()));
return IsAboutToBeFinalizedFromAnyThread(thingp);
}
@ -463,7 +819,7 @@ IsAboutToBeFinalizedFromAnyThread(T **thingp)
JSRuntime *rt = thing->runtimeFromAnyThread();
/* Permanent atoms are never finalized by non-owning runtimes. */
if (ThingIsPermanentAtom(thing) && !TlsPerThreadData.get()->associatedWith(rt))
if (ThingIsPermanentAtomOrWellKnownSymbol(thing) && !TlsPerThreadData.get()->associatedWith(rt))
return false;
Nursery &nursery = rt->gc.nursery;

View File

@ -37,6 +37,28 @@ struct IonScript;
struct VMFunction;
}
/*** Tracing ***/
template <typename T>
void
TraceEdge(JSTracer *trc, BarrieredBase<T> *thingp, const char *name);
template <typename T>
void
TraceRoot(JSTracer *trc, T *thingp, const char *name);
template <typename T>
void
TraceManuallyBarrieredEdge(JSTracer *trc, T *thingp, const char *name);
template <typename T>
void
TraceRange(JSTracer *trc, size_t len, BarrieredBase<T> *thingp, const char *name);
template <typename T>
void
TraceRootRange(JSTracer *trc, size_t len, T *thingp, const char *name);
namespace gc {
/*** Object Marking ***/