From a253acb3360b0ad0bb413b422db6868139caa5bd Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Tue, 29 May 2018 11:08:09 +0100 Subject: [PATCH] Bug 1464387 - Don't instantiate so many trace functions r=sfink --- js/src/gc/Marking.cpp | 336 +++++++++++--------------------------- js/src/gc/Marking.h | 61 +++++-- js/src/gc/Policy.h | 101 ------------ js/src/gc/RootMarking.cpp | 30 +++- js/src/gc/Tracer.h | 159 ++++++++++++++---- 5 files changed, 289 insertions(+), 398 deletions(-) diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 520dbc2b2a81..5676747147ee 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -82,9 +82,9 @@ using mozilla::PodCopy; // |TraceEdge| |TraceRoot| |TraceManuallyBarrieredEdge| ... |TraceRange| ... etc. // // '---------' '---------' '--------------------------' '----------' // // \ \ / / // -// \ \ .----------------. / / // -// o------------->o-|DispatchToTracer|-o<-----------------------o // -// '----------------' // +// \ \ .-----------------. / / // +// o------------->o-|TraceEdgeInternal|-o<----------------------o // +// '-----------------' // // / \ // // / \ // // .---------. .----------. .-----------------. // @@ -372,162 +372,38 @@ AssertShouldMarkInZone(JS::Symbol* sym) #endif } -static void -AssertRootMarkingPhase(JSTracer* trc) +#ifdef DEBUG +void +js::gc::AssertRootMarkingPhase(JSTracer* trc) { MOZ_ASSERT_IF(trc->isMarkingTracer(), trc->runtime()->gc.state() == State::NotActive || trc->runtime()->gc.state() == State::MarkRoots); } +#endif /*** 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::type -// -// Examples: -// BaseGCType::type => JSObject -// BaseGCType::type => BaseShape -// etc. -template ::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 struct BaseGCType { 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 struct PtrBaseGCType { typedef T type; }; -template struct PtrBaseGCType { typedef typename BaseGCType::type* type; }; - -template -typename PtrBaseGCType::type* -ConvertToBase(T* thingp) -{ - return reinterpret_cast::type*>(thingp); -} - -template void DispatchToTracer(JSTracer* trc, T* thingp, const char* name); template T DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name); template void DoMarking(GCMarker* gcmarker, T* thing); template void DoMarking(GCMarker* gcmarker, const T& thing); template void NoteWeakEdge(GCMarker* gcmarker, T** thingp); template void NoteWeakEdge(GCMarker* gcmarker, T* thingp); -template -void -js::TraceEdge(JSTracer* trc, WriteBarrieredBase* thingp, const char* name) -{ - DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name); -} - -template -void -js::TraceEdge(JSTracer* trc, ReadBarriered* thingp, const char* name) -{ - DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name); -} - -template -void -js::TraceNullableEdge(JSTracer* trc, WriteBarrieredBase* thingp, const char* name) -{ - if (InternalBarrierMethods::isMarkable(thingp->get())) - DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name); -} - -template -void -js::TraceNullableEdge(JSTracer* trc, ReadBarriered* thingp, const char* name) -{ - if (InternalBarrierMethods::isMarkable(thingp->unbarrieredGet())) - DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name); -} - template JS_PUBLIC_API(void) js::gc::TraceExternalEdge(JSTracer* trc, T* thingp, const char* name) { MOZ_ASSERT(InternalBarrierMethods::isMarkable(*thingp)); - DispatchToTracer(trc, ConvertToBase(thingp), name); -} - -template -void -js::TraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name) -{ - DispatchToTracer(trc, ConvertToBase(thingp), name); + TraceEdgeInternal(trc, ConvertToBase(thingp), name); } template JS_PUBLIC_API(void) js::UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name) { - DispatchToTracer(trc, ConvertToBase(thingp), name); -} - -template -void -js::TraceWeakEdge(JSTracer* trc, WeakRef* 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 -void -js::TraceRoot(JSTracer* trc, T* thingp, const char* name) -{ - AssertRootMarkingPhase(trc); - DispatchToTracer(trc, ConvertToBase(thingp), name); -} - -template -void -js::TraceRoot(JSTracer* trc, ReadBarriered* thingp, const char* name) -{ - TraceRoot(trc, thingp->unsafeGet(), name); -} - -template -void -js::TraceNullableRoot(JSTracer* trc, T* thingp, const char* name) -{ - AssertRootMarkingPhase(trc); - if (InternalBarrierMethods::isMarkable(*thingp)) - DispatchToTracer(trc, ConvertToBase(thingp), name); -} - -template -void -js::TraceNullableRoot(JSTracer* trc, ReadBarriered* thingp, const char* name) -{ - TraceNullableRoot(trc, thingp->unsafeGet(), name); + TraceEdgeInternal(trc, ConvertToBase(thingp), name); } template @@ -538,48 +414,7 @@ JS::UnsafeTraceRoot(JSTracer* trc, T* thingp, const char* name) js::TraceNullableRoot(trc, thingp, name); } -template -void -js::TraceRange(JSTracer* trc, size_t len, WriteBarrieredBase* vec, const char* name) -{ - JS::AutoTracingIndex index(trc); - for (auto i : IntegerRange(len)) { - if (InternalBarrierMethods::isMarkable(vec[i].get())) - DispatchToTracer(trc, ConvertToBase(vec[i].unsafeUnbarrieredForTracing()), name); - ++index; - } -} - -template -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::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(JSTracer*, WriteBarrieredBase*, const char*); \ - template void js::TraceEdge(JSTracer*, ReadBarriered*, const char*); \ - template void js::TraceNullableEdge(JSTracer*, WriteBarrieredBase*, const char*); \ - template void js::TraceNullableEdge(JSTracer*, ReadBarriered*, const char*); \ - template void js::TraceManuallyBarrieredEdge(JSTracer*, type*, const char*); \ - template void js::TraceWeakEdge(JSTracer*, WeakRef*, const char*); \ - template void js::TraceRoot(JSTracer*, type*, const char*); \ - template void js::TraceRoot(JSTracer*, ReadBarriered*, const char*); \ - template void js::TraceNullableRoot(JSTracer*, type*, const char*); \ - template void js::TraceNullableRoot(JSTracer*, ReadBarriered*, const char*); \ - template void js::TraceRange(JSTracer*, size_t, WriteBarrieredBase*, const char*); \ - template void js::TraceRootRange(JSTracer*, size_t, type*, const char*); -FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS) -#undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS - +// Instantiate a copy of the Tracing templates for each public GC pointer type. #define INSTANTIATE_PUBLIC_TRACE_FUNCTIONS(type) \ template JS_PUBLIC_API(void) JS::UnsafeTraceRoot(JSTracer*, type*, const char*); \ template JS_PUBLIC_API(void) js::UnsafeTraceManuallyBarrieredEdge(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) #undef INSTANTIATE_PUBLIC_TRACE_FUNCTIONS +namespace js { +namespace gc { + +#define INSTANTIATE_INTERNAL_TRACE_FUNCTIONS(type) \ + template void TraceEdgeInternal(JSTracer*, type*, const char*); \ + template void TraceWeakEdgeInternal(JSTracer*, type*, const char*); \ + template void TraceRangeInternal(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 void js::TraceManuallyBarrieredCrossCompartmentEdge(JSTracer* trc, JSObject* src, T* dst, const char* name) { if (ShouldTraceCrossCompartment(trc, src, *dst)) - DispatchToTracer(trc, dst, name); + TraceEdgeInternal(trc, dst, name); } template void js::TraceManuallyBarrieredCrossCompartmentEdge(JSTracer*, JSObject*, JSObject**, const char*); template void js::TraceManuallyBarrieredCrossCompartmentEdge(JSTracer*, JSObject*, JSScript**, const char*); -template void -js::TraceCrossCompartmentEdge(JSTracer* trc, JSObject* src, WriteBarrieredBase* dst, +js::TraceCrossCompartmentEdge(JSTracer* trc, JSObject* src, WriteBarrieredBase* dst, const char* name) { if (ShouldTraceCrossCompartment(trc, src, dst->get())) - DispatchToTracer(trc, dst->unsafeUnbarrieredForTracing(), name); + TraceEdgeInternal(trc, dst->unsafeUnbarrieredForTracing(), name); } -template void js::TraceCrossCompartmentEdge(JSTracer*, JSObject*, - WriteBarrieredBase*, const char*); template void @@ -676,7 +528,7 @@ js::TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, Cell** thingp, const // a sufficiently smart C++ compiler may be able to devirtualize some paths. template 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::value || static_assert( @@ -694,6 +546,32 @@ DispatchToTracer(JSTracer* trc, T* thingp, const char* name) DoCallback(trc->asCallbackTracer(), thingp, name); } +template +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 +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::isMarkable(vec[i])) + TraceEdgeInternal(trc, &vec[i], name); + ++index; + } +} + /*** GC Marking Interface *************************************************************************/ @@ -3361,27 +3239,25 @@ IsMarkedInternalCommon(T* thingp) } template -static bool -IsMarkedInternal(JSRuntime* rt, T** thingp) +struct MightBeNurseryAllocated +{ + static const bool value = mozilla::IsBaseOf::value || + mozilla::IsBaseOf::value; +}; + +template +bool +js::gc::IsMarkedInternal(JSRuntime* rt, T** thingp) { if (IsOwnedByOtherRuntime(rt, *thingp)) return true; - return IsMarkedInternalCommon(thingp); -} - -template <> -/* static */ bool -IsMarkedInternal(JSRuntime* rt, JSObject** thingp) -{ - if (IsOwnedByOtherRuntime(rt, *thingp)) - return true; - - if (IsInsideNursery(*thingp)) { + if (MightBeNurseryAllocated::value && IsInsideNursery(*thingp)) { MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); Cell** cellp = reinterpret_cast(thingp); return Nursery::getForwardedPointer(cellp); } + return IsMarkedInternalCommon(thingp); } @@ -3394,8 +3270,8 @@ struct IsMarkedFunctor : public IdentityDefaultAdaptor { }; template -static bool -IsMarkedInternal(JSRuntime* rt, T* thingp) +bool +js::gc::IsMarkedInternal(JSRuntime* rt, T* thingp) { bool rv = true; *thingp = DispatchTyped(IsMarkedFunctor(), *thingp, rt, &rv); @@ -3411,8 +3287,8 @@ js::gc::IsAboutToBeFinalizedDuringSweep(TenuredCell& tenured) } template -static bool -IsAboutToBeFinalizedInternal(T** thingp) +bool +js::gc::IsAboutToBeFinalizedInternal(T** thingp) { CheckIsMarkedThing(thingp); T* thing = *thingp; @@ -3447,8 +3323,8 @@ struct IsAboutToBeFinalizedInternalFunctor : public IdentityDefaultAdaptor { }; template -static bool -IsAboutToBeFinalizedInternal(T* thingp) +bool +js::gc::IsAboutToBeFinalizedInternal(T* thingp) { bool rv = false; *thingp = DispatchTyped(IsAboutToBeFinalizedInternalFunctor(), *thingp, &rv); @@ -3458,41 +3334,6 @@ IsAboutToBeFinalizedInternal(T* thingp) namespace js { namespace gc { -template -bool -IsMarkedUnbarriered(JSRuntime* rt, T* thingp) -{ - return IsMarkedInternal(rt, ConvertToBase(thingp)); -} - -template -bool -IsMarked(JSRuntime* rt, WriteBarrieredBase* thingp) -{ - return IsMarkedInternal(rt, ConvertToBase(thingp->unsafeUnbarrieredForTracing())); -} - -template -bool -IsAboutToBeFinalizedUnbarriered(T* thingp) -{ - return IsAboutToBeFinalizedInternal(ConvertToBase(thingp)); -} - -template -bool -IsAboutToBeFinalized(WriteBarrieredBase* thingp) -{ - return IsAboutToBeFinalizedInternal(ConvertToBase(thingp->unsafeUnbarrieredForTracing())); -} - -template -bool -IsAboutToBeFinalized(ReadBarrieredBase* thingp) -{ - return IsAboutToBeFinalizedInternal(ConvertToBase(thingp->unsafeUnbarrieredForTracing())); -} - template JS_PUBLIC_API(bool) EdgeNeedsSweep(JS::Heap* thingp) @@ -3507,20 +3348,25 @@ EdgeNeedsSweepUnbarrieredSlow(T* thingp) return IsAboutToBeFinalizedInternal(ConvertToBase(thingp)); } -// Instantiate a copy of the Tracing templates for each derived type. -#define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \ - template bool IsMarkedUnbarriered(JSRuntime*, type*); \ - template bool IsMarked(JSRuntime*, WriteBarrieredBase*); \ - template bool IsAboutToBeFinalizedUnbarriered(type*); \ - template bool IsAboutToBeFinalized(WriteBarrieredBase*); \ - template bool IsAboutToBeFinalized(ReadBarrieredBase*); +// Instantiate a copy of the Tracing templates for each public GC type. #define INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS(type) \ template JS_PUBLIC_API(bool) EdgeNeedsSweep(JS::Heap*); \ template JS_PUBLIC_API(bool) EdgeNeedsSweepUnbarrieredSlow(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_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 js */ diff --git a/js/src/gc/Marking.h b/js/src/gc/Marking.h index 0d11a07db9df..62ba44cba62e 100644 --- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -55,31 +55,66 @@ PushArena(GCMarker* gcmarker, Arena* arena); /*** Liveness ***/ -// 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. +// The IsMarkedInternal and IsAboutToBeFinalizedInternal function templates are +// used to implement the IsMarked and IsAboutToBeFinalized set of functions. +// 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 -bool -IsMarkedUnbarriered(JSRuntime* rt, T* thingp); +bool IsMarkedInternal(JSRuntime* rt, T* thing); +template +bool IsMarkedInternal(JSRuntime* rt, T** thing); + +template +bool IsAboutToBeFinalizedInternal(T* thingp); +template +bool IsAboutToBeFinalizedInternal(T** 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 -bool -IsMarked(JSRuntime* rt, WriteBarrieredBase* thingp); +inline bool +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 +inline bool +IsMarked(JSRuntime* rt, WriteBarrieredBase* thingp) +{ + return IsMarkedInternal(rt, ConvertToBase(thingp->unsafeUnbarrieredForTracing())); +} template -bool -IsAboutToBeFinalizedUnbarriered(T* thingp); +inline bool +IsAboutToBeFinalizedUnbarriered(T* thingp) +{ + return IsAboutToBeFinalizedInternal(ConvertToBase(thingp)); +} template -bool -IsAboutToBeFinalized(WriteBarrieredBase* thingp); +inline bool +IsAboutToBeFinalized(WriteBarrieredBase* thingp) +{ + return IsAboutToBeFinalizedInternal(ConvertToBase(thingp->unsafeUnbarrieredForTracing())); +} template -bool -IsAboutToBeFinalized(ReadBarrieredBase* thingp); +inline bool +IsAboutToBeFinalized(ReadBarrieredBase* thingp) +{ + return IsAboutToBeFinalizedInternal(ConvertToBase(thingp->unsafeUnbarrieredForTracing())); +} bool IsAboutToBeFinalizedDuringSweep(TenuredCell& tenured); diff --git a/js/src/gc/Policy.h b/js/src/gc/Policy.h index dc2aa5d1fac3..d97ad7318f1f 100644 --- a/js/src/gc/Policy.h +++ b/js/src/gc/Policy.h @@ -14,107 +14,6 @@ #include "gc/Marking.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 { // Define the GCPolicy for all internal pointers. diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 44a1c3249e63..e5f2edd24413 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -48,13 +48,27 @@ struct ConcreteTraceable { void trace(JSTracer*) {} }; -template TraceFn = TraceNullableRoot> +template +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::TraceWrapped(trc, thingp, name); +} + +template static inline void TraceExactStackRootList(JSTracer* trc, JS::Rooted* rooter, const char* name) { while (rooter) { T* addr = reinterpret_cast*>(rooter)->address(); - TraceFn(trc, addr, name); + TraceStackOrPersistentRoot(trc, addr, name); rooter = rooter->previous(); } } @@ -68,8 +82,7 @@ JS_FOR_EACH_TRACEKIND(TRACE_ROOTS) #undef TRACE_ROOTS TraceExactStackRootList(trc, stackRoots[JS::RootKind::Id], "exact-id"); TraceExactStackRootList(trc, stackRoots[JS::RootKind::Value], "exact-value"); - TraceExactStackRootList::TraceWrapped>( + TraceExactStackRootList( trc, stackRoots[JS::RootKind::Traceable], "Traceable"); } @@ -85,13 +98,13 @@ TraceExactStackRoots(JSContext* cx, JSTracer* trc) cx->traceStackRoots(trc); } -template TraceFn = TraceNullableRoot> +template static inline void TracePersistentRootedList(JSTracer* trc, mozilla::LinkedList>& list, const char* name) { for (PersistentRooted* r : list) - TraceFn(trc, reinterpret_cast*>(r)->address(), name); + TraceStackOrPersistentRoot(trc, reinterpret_cast*>(r)->address(), name); } void @@ -103,9 +116,8 @@ JS_FOR_EACH_TRACEKIND(TRACE_ROOTS) #undef TRACE_ROOTS TracePersistentRootedList(trc, heapRoots.ref()[JS::RootKind::Id], "persistent-id"); TracePersistentRootedList(trc, heapRoots.ref()[JS::RootKind::Value], "persistent-value"); - TracePersistentRootedList::TraceWrapped>(trc, - heapRoots.ref()[JS::RootKind::Traceable], "persistent-traceable"); + TracePersistentRootedList( + trc, heapRoots.ref()[JS::RootKind::Traceable], "persistent-traceable"); } static void diff --git a/js/src/gc/Tracer.h b/js/src/gc/Tracer.h index ca9d6b440640..eb6b362b771c 100644 --- a/js/src/gc/Tracer.h +++ b/js/src/gc/Tracer.h @@ -49,76 +49,175 @@ namespace js { // called "tracing" forever and changing it would be extremely difficult at // this point. +namespace gc { + +// Map from all trace kinds to the base GC type. +template +struct MapTraceKindToType {}; + +#define DEFINE_TRACE_KIND_MAP(name, type, _) \ + template <> struct MapTraceKindToType { \ + 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 +struct BaseGCType +{ + using type = typename MapTraceKindToType::kind>::Type; + static_assert(mozilla::IsBaseOf::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 struct PtrBaseGCType { using type = T; }; +template struct PtrBaseGCType { using type = typename BaseGCType::type* ; }; + +// Cast a possibly-derived T** pointer to a base class pointer. +template +typename PtrBaseGCType::type* +ConvertToBase(T* thingp) +{ + return reinterpret_cast::type*>(thingp); +} + +// Internal methods to trace edges. +template void TraceEdgeInternal(JSTracer* trc, T* thingp, const char* name); +template void TraceWeakEdgeInternal(JSTracer* trc, T* thingp, const char* name); +template 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 // effect of tracing the edge depends on the JSTracer being used. For pointer // types, |*thingp| must not be null. -template -void -TraceEdge(JSTracer* trc, WriteBarrieredBase* thingp, const char* name); template -void -TraceEdge(JSTracer* trc, ReadBarriered* thingp, const char* name); - -// Trace through an edge in the live object graph on behalf of tracing. -template -void -TraceNullableEdge(JSTracer* trc, WriteBarrieredBase* thingp, const char* name); +inline void +TraceEdge(JSTracer* trc, WriteBarrieredBase* thingp, const char* name) +{ + gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name); +} template -void -TraceNullableEdge(JSTracer* trc, ReadBarriered* thingp, const char* name); +inline void +TraceEdge(JSTracer* trc, ReadBarriered* 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 +inline void +TraceNullableEdge(JSTracer* trc, WriteBarrieredBase* thingp, const char* name) +{ + if (InternalBarrierMethods::isMarkable(thingp->get())) + TraceEdge(trc, thingp, name); +} + +template +inline void +TraceNullableEdge(JSTracer* trc, ReadBarriered* thingp, const char* name) +{ + if (InternalBarrierMethods::isMarkable(thingp->unbarrieredGet())) + TraceEdge(trc, thingp, name); +} // 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 // phase of a GC. -template -void -TraceRoot(JSTracer* trc, T* thingp, const char* name); template -void -TraceRoot(JSTracer* trc, ReadBarriered* thingp, const char* name); +inline void +TraceRoot(JSTracer* trc, T* thingp, const char* name) +{ + gc::AssertRootMarkingPhase(trc); + gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp), name); +} + +template +inline void +TraceRoot(JSTracer* trc, ReadBarriered* thingp, const char* name) +{ + TraceRoot(trc, thingp->unsafeGet(), name); +} // Idential to TraceRoot, except that this variant will not crash if |*thingp| // is null. -template -void -TraceNullableRoot(JSTracer* trc, T* thingp, const char* name); template -void -TraceNullableRoot(JSTracer* trc, ReadBarriered* thingp, const char* name); +inline void +TraceNullableRoot(JSTracer* trc, T* thingp, const char* name) +{ + gc::AssertRootMarkingPhase(trc); + if (InternalBarrierMethods::isMarkable(*thingp)) + gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp), name); +} + +template +inline void +TraceNullableRoot(JSTracer* trc, ReadBarriered* thingp, const char* name) +{ + TraceNullableRoot(trc, thingp->unsafeGet(), name); +} // 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 // separate from TraceEdge to make accidental use of such edges more obvious. + template -void -TraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name); +inline void +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 // 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. + template -void -TraceWeakEdge(JSTracer* trc, WeakRef* thingp, const char* name); +inline void +TraceWeakEdge(JSTracer* trc, WeakRef* thingp, const char* name) +{ + gc::TraceWeakEdgeInternal(trc, gc::ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name); +} // Trace all edges contained in the given array. + template void -TraceRange(JSTracer* trc, size_t len, WriteBarrieredBase* vec, const char* name); +TraceRange(JSTracer* trc, size_t len, WriteBarrieredBase* vec, const char* name) +{ + gc::TraceRangeInternal(trc, len, gc::ConvertToBase(vec[0].unsafeUnbarrieredForTracing()), name); +} // Trace all root edges in the given array. + template 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 // destination thing is not being GC'd, then the edge will not be traced. -template void -TraceCrossCompartmentEdge(JSTracer* trc, JSObject* src, WriteBarrieredBase* dst, +TraceCrossCompartmentEdge(JSTracer* trc, JSObject* src, WriteBarrieredBase* dst, const char* name); // As above but with manual barriers.