Bug 1464387 - Don't instantiate so many trace functions r=sfink

This commit is contained in:
Jon Coppeard 2018-05-29 11:08:09 +01:00
parent de4b4b8e83
commit a253acb336
5 changed files with 289 additions and 398 deletions

View File

@ -82,9 +82,9 @@ using mozilla::PodCopy;
// |TraceEdge| |TraceRoot| |TraceManuallyBarrieredEdge| ... |TraceRange| ... etc. // // |TraceEdge| |TraceRoot| |TraceManuallyBarrieredEdge| ... |TraceRange| ... etc. //
// '---------' '---------' '--------------------------' '----------' // // '---------' '---------' '--------------------------' '----------' //
// \ \ / / // // \ \ / / //
// \ \ .----------------. / / // // \ \ .-----------------. / / //
// o------------->o-|DispatchToTracer|-o<-----------------------o // // o------------->o-|TraceEdgeInternal|-o<----------------------o //
// '----------------' // // '-----------------' //
// / \ // // / \ //
// / \ // // / \ //
// .---------. .----------. .-----------------. // // .---------. .----------. .-----------------. //
@ -372,162 +372,38 @@ AssertShouldMarkInZone(JS::Symbol* sym)
#endif #endif
} }
static void #ifdef DEBUG
AssertRootMarkingPhase(JSTracer* trc) void
js::gc::AssertRootMarkingPhase(JSTracer* trc)
{ {
MOZ_ASSERT_IF(trc->isMarkingTracer(), MOZ_ASSERT_IF(trc->isMarkingTracer(),
trc->runtime()->gc.state() == State::NotActive || trc->runtime()->gc.state() == State::NotActive ||
trc->runtime()->gc.state() == State::MarkRoots); trc->runtime()->gc.state() == State::MarkRoots);
} }
#endif
/*** Tracing Interface ***************************************************************************/ /*** Tracing Interface ***************************************************************************/
// 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.
//
// The use of TraceKind::Null for the case where the type is not matched
// generates a compile error as no template instantiated for that kind.
//
// Usage:
// BaseGCType<T>::type
//
// Examples:
// BaseGCType<JSFunction>::type => JSObject
// BaseGCType<UnownedBaseShape>::type => BaseShape
// etc.
template <typename T, JS::TraceKind =
#define EXPAND_MATCH_TYPE(name, type, _) \
IsBaseOf<type, T>::value ? JS::TraceKind::name :
JS_FOR_EACH_TRACEKIND(EXPAND_MATCH_TYPE)
#undef EXPAND_MATCH_TYPE
JS::TraceKind::Null>
struct BaseGCType;
#define IMPL_BASE_GC_TYPE(name, type_, _) \
template <typename T> struct BaseGCType<T, JS::TraceKind:: name> { typedef type_ type; };
JS_FOR_EACH_TRACEKIND(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 { typedef T type; };
template <typename T> struct PtrBaseGCType<T*> { typedef typename BaseGCType<T>::type* type; };
template <typename T>
typename PtrBaseGCType<T>::type*
ConvertToBase(T* thingp)
{
return reinterpret_cast<typename PtrBaseGCType<T>::type*>(thingp);
}
template <typename T> void DispatchToTracer(JSTracer* trc, T* thingp, const char* name);
template <typename T> T DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name); template <typename T> T DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name);
template <typename T> void DoMarking(GCMarker* gcmarker, T* thing); template <typename T> void DoMarking(GCMarker* gcmarker, T* thing);
template <typename T> void DoMarking(GCMarker* gcmarker, const T& thing); template <typename T> void DoMarking(GCMarker* gcmarker, const T& thing);
template <typename T> void NoteWeakEdge(GCMarker* gcmarker, T** thingp); template <typename T> void NoteWeakEdge(GCMarker* gcmarker, T** thingp);
template <typename T> void NoteWeakEdge(GCMarker* gcmarker, T* thingp); template <typename T> void NoteWeakEdge(GCMarker* gcmarker, T* thingp);
template <typename T>
void
js::TraceEdge(JSTracer* trc, WriteBarrieredBase<T>* thingp, const char* name)
{
DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
}
template <typename T>
void
js::TraceEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name)
{
DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name);
}
template <typename T>
void
js::TraceNullableEdge(JSTracer* trc, WriteBarrieredBase<T>* thingp, const char* name)
{
if (InternalBarrierMethods<T>::isMarkable(thingp->get()))
DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
}
template <typename T>
void
js::TraceNullableEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name)
{
if (InternalBarrierMethods<T>::isMarkable(thingp->unbarrieredGet()))
DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name);
}
template <typename T> template <typename T>
JS_PUBLIC_API(void) JS_PUBLIC_API(void)
js::gc::TraceExternalEdge(JSTracer* trc, T* thingp, const char* name) js::gc::TraceExternalEdge(JSTracer* trc, T* thingp, const char* name)
{ {
MOZ_ASSERT(InternalBarrierMethods<T>::isMarkable(*thingp)); MOZ_ASSERT(InternalBarrierMethods<T>::isMarkable(*thingp));
DispatchToTracer(trc, ConvertToBase(thingp), name); TraceEdgeInternal(trc, ConvertToBase(thingp), name);
}
template <typename T>
void
js::TraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name)
{
DispatchToTracer(trc, ConvertToBase(thingp), name);
} }
template <typename T> template <typename T>
JS_PUBLIC_API(void) JS_PUBLIC_API(void)
js::UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name) js::UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name)
{ {
DispatchToTracer(trc, ConvertToBase(thingp), name); TraceEdgeInternal(trc, ConvertToBase(thingp), name);
}
template <typename T>
void
js::TraceWeakEdge(JSTracer* trc, WeakRef<T>* thingp, const char* name)
{
if (!trc->isMarkingTracer()) {
// Non-marking tracers can select whether or not they see weak edges.
if (trc->traceWeakEdges())
DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
return;
}
NoteWeakEdge(GCMarker::fromTracer(trc),
ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
}
template <typename T>
void
js::TraceRoot(JSTracer* trc, T* thingp, const char* name)
{
AssertRootMarkingPhase(trc);
DispatchToTracer(trc, ConvertToBase(thingp), name);
}
template <typename T>
void
js::TraceRoot(JSTracer* trc, ReadBarriered<T>* thingp, const char* name)
{
TraceRoot(trc, thingp->unsafeGet(), name);
}
template <typename T>
void
js::TraceNullableRoot(JSTracer* trc, T* thingp, const char* name)
{
AssertRootMarkingPhase(trc);
if (InternalBarrierMethods<T>::isMarkable(*thingp))
DispatchToTracer(trc, ConvertToBase(thingp), name);
}
template <typename T>
void
js::TraceNullableRoot(JSTracer* trc, ReadBarriered<T>* thingp, const char* name)
{
TraceNullableRoot(trc, thingp->unsafeGet(), name);
} }
template <typename T> template <typename T>
@ -538,48 +414,7 @@ JS::UnsafeTraceRoot(JSTracer* trc, T* thingp, const char* name)
js::TraceNullableRoot(trc, thingp, name); js::TraceNullableRoot(trc, thingp, name);
} }
template <typename T> // Instantiate a copy of the Tracing templates for each public GC pointer type.
void
js::TraceRange(JSTracer* trc, size_t len, WriteBarrieredBase<T>* vec, const char* name)
{
JS::AutoTracingIndex index(trc);
for (auto i : IntegerRange(len)) {
if (InternalBarrierMethods<T>::isMarkable(vec[i].get()))
DispatchToTracer(trc, ConvertToBase(vec[i].unsafeUnbarrieredForTracing()), name);
++index;
}
}
template <typename T>
void
js::TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name)
{
AssertRootMarkingPhase(trc);
JS::AutoTracingIndex index(trc);
for (auto i : IntegerRange(len)) {
if (InternalBarrierMethods<T>::isMarkable(vec[i]))
DispatchToTracer(trc, ConvertToBase(&vec[i]), name);
++index;
}
}
// Instantiate a copy of the Tracing templates for each derived type.
#define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \
template void js::TraceEdge<type>(JSTracer*, WriteBarrieredBase<type>*, const char*); \
template void js::TraceEdge<type>(JSTracer*, ReadBarriered<type>*, const char*); \
template void js::TraceNullableEdge<type>(JSTracer*, WriteBarrieredBase<type>*, const char*); \
template void js::TraceNullableEdge<type>(JSTracer*, ReadBarriered<type>*, const char*); \
template void js::TraceManuallyBarrieredEdge<type>(JSTracer*, type*, const char*); \
template void js::TraceWeakEdge<type>(JSTracer*, WeakRef<type>*, const char*); \
template void js::TraceRoot<type>(JSTracer*, type*, const char*); \
template void js::TraceRoot<type>(JSTracer*, ReadBarriered<type>*, const char*); \
template void js::TraceNullableRoot<type>(JSTracer*, type*, const char*); \
template void js::TraceNullableRoot<type>(JSTracer*, ReadBarriered<type>*, const char*); \
template void js::TraceRange<type>(JSTracer*, size_t, WriteBarrieredBase<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
#define INSTANTIATE_PUBLIC_TRACE_FUNCTIONS(type) \ #define INSTANTIATE_PUBLIC_TRACE_FUNCTIONS(type) \
template JS_PUBLIC_API(void) JS::UnsafeTraceRoot<type>(JSTracer*, type*, const char*); \ template JS_PUBLIC_API(void) JS::UnsafeTraceRoot<type>(JSTracer*, type*, const char*); \
template JS_PUBLIC_API(void) js::UnsafeTraceManuallyBarrieredEdge<type>(JSTracer*, type*, \ template JS_PUBLIC_API(void) js::UnsafeTraceManuallyBarrieredEdge<type>(JSTracer*, type*, \
@ -589,29 +424,46 @@ FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_PUBLIC_TRACE_FUNCTIONS)
FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_PUBLIC_TRACE_FUNCTIONS) FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_PUBLIC_TRACE_FUNCTIONS)
#undef INSTANTIATE_PUBLIC_TRACE_FUNCTIONS #undef INSTANTIATE_PUBLIC_TRACE_FUNCTIONS
namespace js {
namespace gc {
#define INSTANTIATE_INTERNAL_TRACE_FUNCTIONS(type) \
template void TraceEdgeInternal<type>(JSTracer*, type*, const char*); \
template void TraceWeakEdgeInternal<type>(JSTracer*, type*, const char*); \
template void TraceRangeInternal<type>(JSTracer*, size_t len, type*, const char*);
#define INSTANTIATE_INTERNAL_TRACE_FUNCTIONS_FROM_TRACEKIND(_1, type, _2) \
INSTANTIATE_INTERNAL_TRACE_FUNCTIONS(type*)
JS_FOR_EACH_TRACEKIND(INSTANTIATE_INTERNAL_TRACE_FUNCTIONS_FROM_TRACEKIND)
FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_INTERNAL_TRACE_FUNCTIONS)
#undef INSTANTIATE_INTERNAL_TRACE_FUNCTIONS_FROM_TRACEKIND
#undef INSTANTIATE_INTERNAL_TRACE_FUNCTIONS
} // namespace gc
} // namespace js
template <typename T> template <typename T>
void void
js::TraceManuallyBarrieredCrossCompartmentEdge(JSTracer* trc, JSObject* src, T* dst, js::TraceManuallyBarrieredCrossCompartmentEdge(JSTracer* trc, JSObject* src, T* dst,
const char* name) const char* name)
{ {
if (ShouldTraceCrossCompartment(trc, src, *dst)) if (ShouldTraceCrossCompartment(trc, src, *dst))
DispatchToTracer(trc, dst, name); TraceEdgeInternal(trc, dst, name);
} }
template void js::TraceManuallyBarrieredCrossCompartmentEdge<JSObject*>(JSTracer*, JSObject*, template void js::TraceManuallyBarrieredCrossCompartmentEdge<JSObject*>(JSTracer*, JSObject*,
JSObject**, const char*); JSObject**, const char*);
template void js::TraceManuallyBarrieredCrossCompartmentEdge<JSScript*>(JSTracer*, JSObject*, template void js::TraceManuallyBarrieredCrossCompartmentEdge<JSScript*>(JSTracer*, JSObject*,
JSScript**, const char*); JSScript**, const char*);
template <typename T>
void void
js::TraceCrossCompartmentEdge(JSTracer* trc, JSObject* src, WriteBarrieredBase<T>* dst, js::TraceCrossCompartmentEdge(JSTracer* trc, JSObject* src, WriteBarrieredBase<Value>* dst,
const char* name) const char* name)
{ {
if (ShouldTraceCrossCompartment(trc, src, dst->get())) if (ShouldTraceCrossCompartment(trc, src, dst->get()))
DispatchToTracer(trc, dst->unsafeUnbarrieredForTracing(), name); TraceEdgeInternal(trc, dst->unsafeUnbarrieredForTracing(), name);
} }
template void js::TraceCrossCompartmentEdge<Value>(JSTracer*, JSObject*,
WriteBarrieredBase<Value>*, const char*);
template <typename T> template <typename T>
void void
@ -676,7 +528,7 @@ js::TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, Cell** thingp, const
// a sufficiently smart C++ compiler may be able to devirtualize some paths. // a sufficiently smart C++ compiler may be able to devirtualize some paths.
template <typename T> template <typename T>
void void
DispatchToTracer(JSTracer* trc, T* thingp, const char* name) js::gc::TraceEdgeInternal(JSTracer* trc, T* thingp, const char* name)
{ {
#define IS_SAME_TYPE_OR(name, type, _) mozilla::IsSame<type*, T>::value || #define IS_SAME_TYPE_OR(name, type, _) mozilla::IsSame<type*, T>::value ||
static_assert( static_assert(
@ -694,6 +546,32 @@ DispatchToTracer(JSTracer* trc, T* thingp, const char* name)
DoCallback(trc->asCallbackTracer(), thingp, name); DoCallback(trc->asCallbackTracer(), thingp, name);
} }
template <typename T>
void
js::gc::TraceWeakEdgeInternal(JSTracer* trc, T* thingp, const char* name)
{
if (!trc->isMarkingTracer()) {
// Non-marking tracers can select whether or not they see weak edges.
if (trc->traceWeakEdges())
TraceEdgeInternal(trc, thingp, name);
return;
}
NoteWeakEdge(GCMarker::fromTracer(trc), thingp);
}
template <typename T>
void
js::gc::TraceRangeInternal(JSTracer* trc, size_t len, T* vec, const char* name)
{
JS::AutoTracingIndex index(trc);
for (auto i : IntegerRange(len)) {
if (InternalBarrierMethods<T>::isMarkable(vec[i]))
TraceEdgeInternal(trc, &vec[i], name);
++index;
}
}
/*** GC Marking Interface *************************************************************************/ /*** GC Marking Interface *************************************************************************/
@ -3361,27 +3239,25 @@ IsMarkedInternalCommon(T* thingp)
} }
template <typename T> template <typename T>
static bool struct MightBeNurseryAllocated
IsMarkedInternal(JSRuntime* rt, T** thingp) {
static const bool value = mozilla::IsBaseOf<JSObject, T>::value ||
mozilla::IsBaseOf<JSString, T>::value;
};
template <typename T>
bool
js::gc::IsMarkedInternal(JSRuntime* rt, T** thingp)
{ {
if (IsOwnedByOtherRuntime(rt, *thingp)) if (IsOwnedByOtherRuntime(rt, *thingp))
return true; return true;
return IsMarkedInternalCommon(thingp); if (MightBeNurseryAllocated<T>::value && IsInsideNursery(*thingp)) {
}
template <>
/* static */ bool
IsMarkedInternal(JSRuntime* rt, JSObject** thingp)
{
if (IsOwnedByOtherRuntime(rt, *thingp))
return true;
if (IsInsideNursery(*thingp)) {
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
Cell** cellp = reinterpret_cast<Cell**>(thingp); Cell** cellp = reinterpret_cast<Cell**>(thingp);
return Nursery::getForwardedPointer(cellp); return Nursery::getForwardedPointer(cellp);
} }
return IsMarkedInternalCommon(thingp); return IsMarkedInternalCommon(thingp);
} }
@ -3394,8 +3270,8 @@ struct IsMarkedFunctor : public IdentityDefaultAdaptor<S> {
}; };
template <typename T> template <typename T>
static bool bool
IsMarkedInternal(JSRuntime* rt, T* thingp) js::gc::IsMarkedInternal(JSRuntime* rt, T* thingp)
{ {
bool rv = true; bool rv = true;
*thingp = DispatchTyped(IsMarkedFunctor<T>(), *thingp, rt, &rv); *thingp = DispatchTyped(IsMarkedFunctor<T>(), *thingp, rt, &rv);
@ -3411,8 +3287,8 @@ js::gc::IsAboutToBeFinalizedDuringSweep(TenuredCell& tenured)
} }
template <typename T> template <typename T>
static bool bool
IsAboutToBeFinalizedInternal(T** thingp) js::gc::IsAboutToBeFinalizedInternal(T** thingp)
{ {
CheckIsMarkedThing(thingp); CheckIsMarkedThing(thingp);
T* thing = *thingp; T* thing = *thingp;
@ -3447,8 +3323,8 @@ struct IsAboutToBeFinalizedInternalFunctor : public IdentityDefaultAdaptor<S> {
}; };
template <typename T> template <typename T>
static bool bool
IsAboutToBeFinalizedInternal(T* thingp) js::gc::IsAboutToBeFinalizedInternal(T* thingp)
{ {
bool rv = false; bool rv = false;
*thingp = DispatchTyped(IsAboutToBeFinalizedInternalFunctor<T>(), *thingp, &rv); *thingp = DispatchTyped(IsAboutToBeFinalizedInternalFunctor<T>(), *thingp, &rv);
@ -3458,41 +3334,6 @@ IsAboutToBeFinalizedInternal(T* thingp)
namespace js { namespace js {
namespace gc { namespace gc {
template <typename T>
bool
IsMarkedUnbarriered(JSRuntime* rt, T* thingp)
{
return IsMarkedInternal(rt, ConvertToBase(thingp));
}
template <typename T>
bool
IsMarked(JSRuntime* rt, WriteBarrieredBase<T>* thingp)
{
return IsMarkedInternal(rt, ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
}
template <typename T>
bool
IsAboutToBeFinalizedUnbarriered(T* thingp)
{
return IsAboutToBeFinalizedInternal(ConvertToBase(thingp));
}
template <typename T>
bool
IsAboutToBeFinalized(WriteBarrieredBase<T>* thingp)
{
return IsAboutToBeFinalizedInternal(ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
}
template <typename T>
bool
IsAboutToBeFinalized(ReadBarrieredBase<T>* thingp)
{
return IsAboutToBeFinalizedInternal(ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
}
template <typename T> template <typename T>
JS_PUBLIC_API(bool) JS_PUBLIC_API(bool)
EdgeNeedsSweep(JS::Heap<T>* thingp) EdgeNeedsSweep(JS::Heap<T>* thingp)
@ -3507,20 +3348,25 @@ EdgeNeedsSweepUnbarrieredSlow(T* thingp)
return IsAboutToBeFinalizedInternal(ConvertToBase(thingp)); return IsAboutToBeFinalizedInternal(ConvertToBase(thingp));
} }
// Instantiate a copy of the Tracing templates for each derived type. // Instantiate a copy of the Tracing templates for each public GC type.
#define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \
template bool IsMarkedUnbarriered<type>(JSRuntime*, type*); \
template bool IsMarked<type>(JSRuntime*, WriteBarrieredBase<type>*); \
template bool IsAboutToBeFinalizedUnbarriered<type>(type*); \
template bool IsAboutToBeFinalized<type>(WriteBarrieredBase<type>*); \
template bool IsAboutToBeFinalized<type>(ReadBarrieredBase<type>*);
#define INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS(type) \ #define INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS(type) \
template JS_PUBLIC_API(bool) EdgeNeedsSweep<type>(JS::Heap<type>*); \ template JS_PUBLIC_API(bool) EdgeNeedsSweep<type>(JS::Heap<type>*); \
template JS_PUBLIC_API(bool) EdgeNeedsSweepUnbarrieredSlow<type>(type*); template JS_PUBLIC_API(bool) EdgeNeedsSweepUnbarrieredSlow<type>(type*);
FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS)
FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS) FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS)
FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS) FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS)
#undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
#define INSTANTIATE_INTERNAL_MARKING_FUNCTIONS(type) \
template bool IsMarkedInternal(JSRuntime* rt, type* thing); \
template bool IsAboutToBeFinalizedInternal(type* thingp);
#define INSTANTIATE_INTERNAL_MARKING_FUNCTIONS_FROM_TRACEKIND(_1, type, _2) \
INSTANTIATE_INTERNAL_MARKING_FUNCTIONS(type*)
JS_FOR_EACH_TRACEKIND(INSTANTIATE_INTERNAL_MARKING_FUNCTIONS_FROM_TRACEKIND)
FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_INTERNAL_MARKING_FUNCTIONS)
#undef INSTANTIATE_INTERNAL_MARKING_FUNCTIONS_FROM_TRACEKIND
#undef INSTANTIATE_INTERNAL_MARKING_FUNCTIONS
} /* namespace gc */ } /* namespace gc */
} /* namespace js */ } /* namespace js */

View File

@ -55,31 +55,66 @@ PushArena(GCMarker* gcmarker, Arena* arena);
/*** Liveness ***/ /*** Liveness ***/
// Report whether a thing has been marked. Things which are in zones that are // The IsMarkedInternal and IsAboutToBeFinalizedInternal function templates are
// not currently being collected or are owned by another runtime are always // used to implement the IsMarked and IsAboutToBeFinalized set of functions.
// reported as being marked. // These internal functions are instantiated for the base GC types and should
// not be called directly.
//
// Note that there are two function templates declared for each, not one
// template and a specialization. This is necessary so that pointer arguments
// (e.g. JSObject**) and tagged value arguments (e.g. JS::Value*) are routed to
// separate implementations.
template <typename T> template <typename T>
bool bool IsMarkedInternal(JSRuntime* rt, T* thing);
IsMarkedUnbarriered(JSRuntime* rt, T* thingp); template <typename T>
bool IsMarkedInternal(JSRuntime* rt, T** thing);
template <typename T>
bool IsAboutToBeFinalizedInternal(T* thingp);
template <typename T>
bool IsAboutToBeFinalizedInternal(T** thingp);
// Report whether a thing has been marked. Things which are in zones that are // Report whether a thing has been marked. Things which are in zones that are
// not currently being collected or are owned by another runtime are always // not currently being collected or are owned by another runtime are always
// reported as being marked. // reported as being marked.
template <typename T> template <typename T>
bool inline bool
IsMarked(JSRuntime* rt, WriteBarrieredBase<T>* thingp); IsMarkedUnbarriered(JSRuntime* rt, T* thingp)
{
return IsMarkedInternal(rt, ConvertToBase(thingp));
}
// Report whether a thing has been marked. Things which are in zones that are
// not currently being collected or are owned by another runtime are always
// reported as being marked.
template <typename T>
inline bool
IsMarked(JSRuntime* rt, WriteBarrieredBase<T>* thingp)
{
return IsMarkedInternal(rt, ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
}
template <typename T> template <typename T>
bool inline bool
IsAboutToBeFinalizedUnbarriered(T* thingp); IsAboutToBeFinalizedUnbarriered(T* thingp)
{
return IsAboutToBeFinalizedInternal(ConvertToBase(thingp));
}
template <typename T> template <typename T>
bool inline bool
IsAboutToBeFinalized(WriteBarrieredBase<T>* thingp); IsAboutToBeFinalized(WriteBarrieredBase<T>* thingp)
{
return IsAboutToBeFinalizedInternal(ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
}
template <typename T> template <typename T>
bool inline bool
IsAboutToBeFinalized(ReadBarrieredBase<T>* thingp); IsAboutToBeFinalized(ReadBarrieredBase<T>* thingp)
{
return IsAboutToBeFinalizedInternal(ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
}
bool bool
IsAboutToBeFinalizedDuringSweep(TenuredCell& tenured); IsAboutToBeFinalizedDuringSweep(TenuredCell& tenured);

View File

@ -14,107 +14,6 @@
#include "gc/Marking.h" #include "gc/Marking.h"
#include "js/GCPolicyAPI.h" #include "js/GCPolicyAPI.h"
// Forward declare the types we're defining policies for. This file is
// included in all places that define GC things, so the real definitions
// will be available when we do template expansion, allowing for use of
// static members in the underlying types. We cannot, however, use
// static_assert to verify relations between types.
class JSLinearString;
namespace js {
class AccessorShape;
class ArgumentsObject;
class ArrayBufferObject;
class ArrayBufferObjectMaybeShared;
class ArrayBufferViewObject;
class ArrayObject;
class BaseShape;
class DebugEnvironmentProxy;
class DebuggerFrame;
class ExportEntryObject;
class EnvironmentObject;
class GlobalObject;
class ImportEntryObject;
class LazyScript;
class LexicalEnvironmentObject;
class ModuleEnvironmentObject;
class ModuleNamespaceObject;
class ModuleObject;
class NativeObject;
class ObjectGroup;
class PlainObject;
class PropertyName;
class RegExpObject;
class SavedFrame;
class Scope;
class EnvironmentObject;
class RequestedModuleObject;
class ScriptSourceObject;
class Shape;
class SharedArrayBufferObject;
class StructTypeDescr;
class UnownedBaseShape;
class WasmGlobalObject;
class WasmFunctionScope;
class WasmMemoryObject;
namespace jit {
class JitCode;
} // namespace jit
} // namespace js
// Expand the given macro D for each valid GC reference type.
#define FOR_EACH_INTERNAL_GC_POINTER_TYPE(D) \
D(JSFlatString*) \
D(JSLinearString*) \
D(js::AccessorShape*) \
D(js::ArgumentsObject*) \
D(js::ArrayBufferObject*) \
D(js::ArrayBufferObjectMaybeShared*) \
D(js::ArrayBufferViewObject*) \
D(js::ArrayObject*) \
D(js::BaseShape*) \
D(js::DebugEnvironmentProxy*) \
D(js::DebuggerFrame*) \
D(js::ExportEntryObject*) \
D(js::EnvironmentObject*) \
D(js::GlobalObject*) \
D(js::ImportEntryObject*) \
D(js::LazyScript*) \
D(js::LexicalEnvironmentObject*) \
D(js::ModuleEnvironmentObject*) \
D(js::ModuleNamespaceObject*) \
D(js::ModuleObject*) \
D(js::NativeObject*) \
D(js::ObjectGroup*) \
D(js::PlainObject*) \
D(js::PropertyName*) \
D(js::RegExpObject*) \
D(js::RegExpShared*) \
D(js::RequestedModuleObject*) \
D(js::SavedFrame*) \
D(js::Scope*) \
D(js::ScriptSourceObject*) \
D(js::Shape*) \
D(js::SharedArrayBufferObject*) \
D(js::StructTypeDescr*) \
D(js::UnownedBaseShape*) \
D(js::WasmFunctionScope*) \
D(js::WasmInstanceObject*) \
D(js::WasmMemoryObject*) \
D(js::WasmTableObject*) \
D(js::WasmGlobalObject*) \
D(js::jit::JitCode*)
// Expand the given macro D for each internal tagged GC pointer type.
#define FOR_EACH_INTERNAL_TAGGED_GC_POINTER_TYPE(D) \
D(js::TaggedProto)
// Expand the macro D for every GC reference type that we know about.
#define FOR_EACH_GC_POINTER_TYPE(D) \
FOR_EACH_PUBLIC_GC_POINTER_TYPE(D) \
FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(D) \
FOR_EACH_INTERNAL_GC_POINTER_TYPE(D) \
FOR_EACH_INTERNAL_TAGGED_GC_POINTER_TYPE(D)
namespace js { namespace js {
// Define the GCPolicy for all internal pointers. // Define the GCPolicy for all internal pointers.

View File

@ -48,13 +48,27 @@ struct ConcreteTraceable {
void trace(JSTracer*) {} void trace(JSTracer*) {}
}; };
template <typename T, TraceFunction<T> TraceFn = TraceNullableRoot> template <typename T>
static inline void
TraceStackOrPersistentRoot(JSTracer* trc, T* thingp, const char* name)
{
TraceNullableRoot(trc, thingp, name);
}
template <>
inline void
TraceStackOrPersistentRoot(JSTracer* trc, ConcreteTraceable* thingp, const char* name)
{
js::DispatchWrapper<ConcreteTraceable>::TraceWrapped(trc, thingp, name);
}
template <typename T>
static inline void static inline void
TraceExactStackRootList(JSTracer* trc, JS::Rooted<void*>* rooter, const char* name) TraceExactStackRootList(JSTracer* trc, JS::Rooted<void*>* rooter, const char* name)
{ {
while (rooter) { while (rooter) {
T* addr = reinterpret_cast<JS::Rooted<T>*>(rooter)->address(); T* addr = reinterpret_cast<JS::Rooted<T>*>(rooter)->address();
TraceFn(trc, addr, name); TraceStackOrPersistentRoot(trc, addr, name);
rooter = rooter->previous(); rooter = rooter->previous();
} }
} }
@ -68,8 +82,7 @@ JS_FOR_EACH_TRACEKIND(TRACE_ROOTS)
#undef TRACE_ROOTS #undef TRACE_ROOTS
TraceExactStackRootList<jsid>(trc, stackRoots[JS::RootKind::Id], "exact-id"); TraceExactStackRootList<jsid>(trc, stackRoots[JS::RootKind::Id], "exact-id");
TraceExactStackRootList<Value>(trc, stackRoots[JS::RootKind::Value], "exact-value"); TraceExactStackRootList<Value>(trc, stackRoots[JS::RootKind::Value], "exact-value");
TraceExactStackRootList<ConcreteTraceable, TraceExactStackRootList<ConcreteTraceable>(
js::DispatchWrapper<ConcreteTraceable>::TraceWrapped>(
trc, stackRoots[JS::RootKind::Traceable], "Traceable"); trc, stackRoots[JS::RootKind::Traceable], "Traceable");
} }
@ -85,13 +98,13 @@ TraceExactStackRoots(JSContext* cx, JSTracer* trc)
cx->traceStackRoots(trc); cx->traceStackRoots(trc);
} }
template <typename T, TraceFunction<T> TraceFn = TraceNullableRoot> template <typename T>
static inline void static inline void
TracePersistentRootedList(JSTracer* trc, mozilla::LinkedList<PersistentRooted<void*>>& list, TracePersistentRootedList(JSTracer* trc, mozilla::LinkedList<PersistentRooted<void*>>& list,
const char* name) const char* name)
{ {
for (PersistentRooted<void*>* r : list) for (PersistentRooted<void*>* r : list)
TraceFn(trc, reinterpret_cast<PersistentRooted<T>*>(r)->address(), name); TraceStackOrPersistentRoot(trc, reinterpret_cast<PersistentRooted<T>*>(r)->address(), name);
} }
void void
@ -103,9 +116,8 @@ JS_FOR_EACH_TRACEKIND(TRACE_ROOTS)
#undef TRACE_ROOTS #undef TRACE_ROOTS
TracePersistentRootedList<jsid>(trc, heapRoots.ref()[JS::RootKind::Id], "persistent-id"); TracePersistentRootedList<jsid>(trc, heapRoots.ref()[JS::RootKind::Id], "persistent-id");
TracePersistentRootedList<Value>(trc, heapRoots.ref()[JS::RootKind::Value], "persistent-value"); TracePersistentRootedList<Value>(trc, heapRoots.ref()[JS::RootKind::Value], "persistent-value");
TracePersistentRootedList<ConcreteTraceable, TracePersistentRootedList<ConcreteTraceable>(
js::DispatchWrapper<ConcreteTraceable>::TraceWrapped>(trc, trc, heapRoots.ref()[JS::RootKind::Traceable], "persistent-traceable");
heapRoots.ref()[JS::RootKind::Traceable], "persistent-traceable");
} }
static void static void

View File

@ -49,76 +49,175 @@ namespace js {
// called "tracing" forever and changing it would be extremely difficult at // called "tracing" forever and changing it would be extremely difficult at
// this point. // this point.
namespace gc {
// Map from all trace kinds to the base GC type.
template <JS::TraceKind kind>
struct MapTraceKindToType {};
#define DEFINE_TRACE_KIND_MAP(name, type, _) \
template <> struct MapTraceKindToType<JS::TraceKind::name> { \
using Type = type; \
};
JS_FOR_EACH_TRACEKIND(DEFINE_TRACE_KIND_MAP);
#undef DEFINE_TRACE_KIND_MAP
// Map from a possibly-derived type to the base GC type.
template <typename T>
struct BaseGCType
{
using type = typename MapTraceKindToType<JS::MapTypeToTraceKind<T>::kind>::Type;
static_assert(mozilla::IsBaseOf<type, T>::value, "Failed to find base 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 { using type = T; };
template <typename T> struct PtrBaseGCType<T*> { using type = typename BaseGCType<T>::type* ; };
// Cast a possibly-derived T** pointer to a base class pointer.
template <typename T>
typename PtrBaseGCType<T>::type*
ConvertToBase(T* thingp)
{
return reinterpret_cast<typename PtrBaseGCType<T>::type*>(thingp);
}
// Internal methods to trace edges.
template <typename T> void TraceEdgeInternal(JSTracer* trc, T* thingp, const char* name);
template <typename T> void TraceWeakEdgeInternal(JSTracer* trc, T* thingp, const char* name);
template <typename T> void TraceRangeInternal(JSTracer* trc, size_t len, T* vec, const char* name);
#ifdef DEBUG
void AssertRootMarkingPhase(JSTracer* trc);
#else
inline void AssertRootMarkingPhase(JSTracer* trc) {}
#endif
} // namespace gc
// Trace through an edge in the live object graph on behalf of tracing. The // Trace through an edge in the live object graph on behalf of tracing. The
// effect of tracing the edge depends on the JSTracer being used. For pointer // effect of tracing the edge depends on the JSTracer being used. For pointer
// types, |*thingp| must not be null. // types, |*thingp| must not be null.
template <typename T>
void
TraceEdge(JSTracer* trc, WriteBarrieredBase<T>* thingp, const char* name);
template <typename T> template <typename T>
void inline void
TraceEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name); TraceEdge(JSTracer* trc, WriteBarrieredBase<T>* thingp, const char* name)
{
// Trace through an edge in the live object graph on behalf of tracing. gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
template <typename T> }
void
TraceNullableEdge(JSTracer* trc, WriteBarrieredBase<T>* thingp, const char* name);
template <typename T> template <typename T>
void inline void
TraceNullableEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name); TraceEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name)
{
gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp->unsafeGet()), name);
}
// Trace through a possibly-null edge in the live object graph on behalf of
// tracing.
template <typename T>
inline void
TraceNullableEdge(JSTracer* trc, WriteBarrieredBase<T>* thingp, const char* name)
{
if (InternalBarrierMethods<T>::isMarkable(thingp->get()))
TraceEdge(trc, thingp, name);
}
template <typename T>
inline void
TraceNullableEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name)
{
if (InternalBarrierMethods<T>::isMarkable(thingp->unbarrieredGet()))
TraceEdge(trc, thingp, name);
}
// Trace through a "root" edge. These edges are the initial edges in the object // Trace through a "root" edge. These edges are the initial edges in the object
// graph traversal. Root edges are asserted to only be traversed in the initial // graph traversal. Root edges are asserted to only be traversed in the initial
// phase of a GC. // phase of a GC.
template <typename T>
void
TraceRoot(JSTracer* trc, T* thingp, const char* name);
template <typename T> template <typename T>
void inline void
TraceRoot(JSTracer* trc, ReadBarriered<T>* thingp, const char* name); TraceRoot(JSTracer* trc, T* thingp, const char* name)
{
gc::AssertRootMarkingPhase(trc);
gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp), name);
}
template <typename T>
inline void
TraceRoot(JSTracer* trc, ReadBarriered<T>* thingp, const char* name)
{
TraceRoot(trc, thingp->unsafeGet(), name);
}
// Idential to TraceRoot, except that this variant will not crash if |*thingp| // Idential to TraceRoot, except that this variant will not crash if |*thingp|
// is null. // is null.
template <typename T>
void
TraceNullableRoot(JSTracer* trc, T* thingp, const char* name);
template <typename T> template <typename T>
void inline void
TraceNullableRoot(JSTracer* trc, ReadBarriered<T>* thingp, const char* name); TraceNullableRoot(JSTracer* trc, T* thingp, const char* name)
{
gc::AssertRootMarkingPhase(trc);
if (InternalBarrierMethods<T>::isMarkable(*thingp))
gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp), name);
}
template <typename T>
inline void
TraceNullableRoot(JSTracer* trc, ReadBarriered<T>* thingp, const char* name)
{
TraceNullableRoot(trc, thingp->unsafeGet(), name);
}
// Like TraceEdge, but for edges that do not use one of the automatic barrier // Like TraceEdge, but for edges that do not use one of the automatic barrier
// classes and, thus, must be treated specially for moving GC. This method is // classes and, thus, must be treated specially for moving GC. This method is
// separate from TraceEdge to make accidental use of such edges more obvious. // separate from TraceEdge to make accidental use of such edges more obvious.
template <typename T> template <typename T>
void inline void
TraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name); TraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name)
{
gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp), name);
}
// Visits a WeakRef, but does not trace its referents. If *thingp is not marked // Visits a WeakRef, but does not trace its referents. If *thingp is not marked
// at the end of marking, it is replaced by nullptr. This method records // at the end of marking, it is replaced by nullptr. This method records
// thingp, so the edge location must not change after this function is called. // thingp, so the edge location must not change after this function is called.
template <typename T> template <typename T>
void inline void
TraceWeakEdge(JSTracer* trc, WeakRef<T>* thingp, const char* name); TraceWeakEdge(JSTracer* trc, WeakRef<T>* thingp, const char* name)
{
gc::TraceWeakEdgeInternal(trc, gc::ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
}
// Trace all edges contained in the given array. // Trace all edges contained in the given array.
template <typename T> template <typename T>
void void
TraceRange(JSTracer* trc, size_t len, WriteBarrieredBase<T>* vec, const char* name); TraceRange(JSTracer* trc, size_t len, WriteBarrieredBase<T>* vec, const char* name)
{
gc::TraceRangeInternal(trc, len, gc::ConvertToBase(vec[0].unsafeUnbarrieredForTracing()), name);
}
// Trace all root edges in the given array. // Trace all root edges in the given array.
template <typename T> template <typename T>
void void
TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name); TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name)
{
gc::AssertRootMarkingPhase(trc);
gc::TraceRangeInternal(trc, len, gc::ConvertToBase(vec), name);
}
// Trace an edge that crosses compartment boundaries. If the compartment of the // Trace an edge that crosses compartment boundaries. If the compartment of the
// destination thing is not being GC'd, then the edge will not be traced. // destination thing is not being GC'd, then the edge will not be traced.
template <typename T>
void void
TraceCrossCompartmentEdge(JSTracer* trc, JSObject* src, WriteBarrieredBase<T>* dst, TraceCrossCompartmentEdge(JSTracer* trc, JSObject* src, WriteBarrieredBase<Value>* dst,
const char* name); const char* name);
// As above but with manual barriers. // As above but with manual barriers.