From 3796c7c1e110159cead58d84cc99288c66512f79 Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Thu, 26 Jan 2023 15:02:09 +0800 Subject: [PATCH] Issue #2083 - Part 1: Make RegExpShared a GC thing. Based on Mozilla bug 1345177. Changes from the original bug's patch: - We don't have JS::CurrentThreadIsHeapCollecting, so let's use trc->runtime()->isHeapCollecting() instead. - Mozilla bug 1337117 renamed runtimeFromMainThread to runtimeFromActiveCooperatingThread for Firefox 54, so let's use the former --- js/public/MemoryMetrics.h | 26 +-- js/public/Proxy.h | 3 +- js/public/TraceKind.h | 8 +- js/public/TracingAPI.h | 4 + js/src/gc/GCInternals.h | 5 + js/src/gc/GCRuntime.h | 2 + js/src/gc/Heap.h | 65 ++++---- js/src/gc/Marking.cpp | 10 ++ js/src/gc/Policy.h | 1 + js/src/gc/Statistics.cpp | 1 + js/src/gc/Statistics.h | 1 + js/src/gc/Tracer.h | 4 + js/src/jsapi.cpp | 4 +- js/src/jscompartment.cpp | 12 +- js/src/jscompartment.h | 2 +- js/src/jsfriendapi.h | 2 +- js/src/jsgc.cpp | 78 +++------ js/src/jsgc.h | 100 ++---------- js/src/jsgcinlines.h | 108 +++++++++++++ js/src/proxy/CrossCompartmentWrapper.cpp | 5 +- js/src/proxy/Proxy.h | 2 - js/src/vm/MemoryMetrics.cpp | 7 + js/src/vm/RegExpObject.cpp | 196 +++++++---------------- js/src/vm/RegExpObject.h | 123 ++++++-------- js/src/vm/SPSProfiler.cpp | 2 + js/src/vm/Shape-inl.h | 1 + js/src/vm/UbiNode.cpp | 3 + js/xpconnect/src/XPCJSContext.cpp | 16 ++ xpcom/base/CycleCollectedJSContext.h | 5 +- 29 files changed, 384 insertions(+), 412 deletions(-) diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index 2d7f06fb59..72764aec0e 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -581,7 +581,8 @@ struct UnusedGCThingSizes macro(Other, GCHeapUnused, string) \ macro(Other, GCHeapUnused, symbol) \ macro(Other, GCHeapUnused, jitcode) \ - macro(Other, GCHeapUnused, scope) + macro(Other, GCHeapUnused, scope) \ + macro(Other, GCHeapUnused, regExpShared) UnusedGCThingSizes() : FOR_EACH_SIZE(ZERO_SIZE) @@ -595,16 +596,17 @@ struct UnusedGCThingSizes void addToKind(JS::TraceKind kind, intptr_t n) { switch (kind) { - case JS::TraceKind::Object: object += n; break; - case JS::TraceKind::String: string += n; break; - case JS::TraceKind::Symbol: symbol += n; break; - case JS::TraceKind::Script: script += n; break; - case JS::TraceKind::Shape: shape += n; break; - case JS::TraceKind::BaseShape: baseShape += n; break; - case JS::TraceKind::JitCode: jitcode += n; break; - case JS::TraceKind::LazyScript: lazyScript += n; break; - case JS::TraceKind::ObjectGroup: objectGroup += n; break; - case JS::TraceKind::Scope: scope += n; break; + case JS::TraceKind::Object: object += n; break; + case JS::TraceKind::String: string += n; break; + case JS::TraceKind::Symbol: symbol += n; break; + case JS::TraceKind::Script: script += n; break; + case JS::TraceKind::Shape: shape += n; break; + case JS::TraceKind::BaseShape: baseShape += n; break; + case JS::TraceKind::JitCode: jitcode += n; break; + case JS::TraceKind::LazyScript: lazyScript += n; break; + case JS::TraceKind::ObjectGroup: objectGroup += n; break; + case JS::TraceKind::Scope: scope += n; break; + case JS::TraceKind::RegExpShared: regExpShared += n; break; default: MOZ_CRASH("Bad trace kind for UnusedGCThingSizes"); } @@ -646,6 +648,8 @@ struct ZoneStats macro(Other, MallocHeap, objectGroupsMallocHeap) \ macro(Other, GCHeapUsed, scopesGCHeap) \ macro(Other, MallocHeap, scopesMallocHeap) \ + macro(Other, GCHeapUsed, regExpSharedsGCHeap) \ + macro(Other, MallocHeap, regExpSharedsMallocHeap) \ macro(Other, MallocHeap, typePool) \ macro(Other, MallocHeap, baselineStubsOptimized) \ macro(Other, MallocHeap, uniqueIdMap) \ diff --git a/js/public/Proxy.h b/js/public/Proxy.h index 2453e2e140..42973bdc37 100644 --- a/js/public/Proxy.h +++ b/js/public/Proxy.h @@ -31,7 +31,8 @@ using JS::PrivateValue; using JS::PropertyDescriptor; using JS::Value; -class RegExpGuard; +using RegExpGuard = JS::Rooted; + class JS_FRIEND_API(Wrapper); /* diff --git a/js/public/TraceKind.h b/js/public/TraceKind.h index d3109d3606..13228a9612 100644 --- a/js/public/TraceKind.h +++ b/js/public/TraceKind.h @@ -15,6 +15,7 @@ namespace js { class BaseShape; class LazyScript; class ObjectGroup; +class RegExpShared; class Shape; class Scope; namespace jit { @@ -58,13 +59,15 @@ enum class TraceKind BaseShape = 0x0F, JitCode = 0x1F, LazyScript = 0x2F, - Scope = 0x3F + Scope = 0x3F, + RegExpShared = 0x4F }; const static uintptr_t OutOfLineTraceKindMask = 0x07; static_assert(uintptr_t(JS::TraceKind::BaseShape) & OutOfLineTraceKindMask, "mask bits are set"); static_assert(uintptr_t(JS::TraceKind::JitCode) & OutOfLineTraceKindMask, "mask bits are set"); static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "mask bits are set"); static_assert(uintptr_t(JS::TraceKind::Scope) & OutOfLineTraceKindMask, "mask bits are set"); +static_assert(uintptr_t(JS::TraceKind::RegExpShared) & OutOfLineTraceKindMask, "mask bits are set"); // When this header is imported inside SpiderMonkey, the class definitions are // available and we can query those definitions to find the correct kind @@ -87,7 +90,8 @@ struct MapTypeToTraceKind { D(Script, JSScript, true) \ D(Shape, js::Shape, true) \ D(String, JSString, false) \ - D(Symbol, JS::Symbol, false) + D(Symbol, JS::Symbol, false) \ + D(RegExpShared, js::RegExpShared, true) // Map from all public types to their trace kind. #define JS_EXPAND_DEF(name, type, _) \ diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h index 5e3692f55e..01d28d93c3 100644 --- a/js/public/TracingAPI.h +++ b/js/public/TracingAPI.h @@ -160,6 +160,9 @@ class JS_PUBLIC_API(CallbackTracer) : public JSTracer virtual void onScopeEdge(js::Scope** scopep) { onChild(JS::GCCellPtr(*scopep, JS::TraceKind::Scope)); } + virtual void onRegExpSharedEdge(js::RegExpShared** sharedp) { + onChild(JS::GCCellPtr(*sharedp, JS::TraceKind::RegExpShared)); + } // Override this method to receive notification when a node in the GC // heap graph is visited. @@ -230,6 +233,7 @@ class JS_PUBLIC_API(CallbackTracer) : public JSTracer void dispatchToOnEdge(js::jit::JitCode** codep) { onJitCodeEdge(codep); } void dispatchToOnEdge(js::LazyScript** lazyp) { onLazyScriptEdge(lazyp); } void dispatchToOnEdge(js::Scope** scopep) { onScopeEdge(scopep); } + void dispatchToOnEdge(js::RegExpShared** sharedp) { onRegExpSharedEdge(sharedp); } private: friend class AutoTracingName; diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h index 43c9e0fd37..5c51540351 100644 --- a/js/src/gc/GCInternals.h +++ b/js/src/gc/GCInternals.h @@ -75,6 +75,7 @@ struct MovingTracer : JS::CallbackTracer void onLazyScriptEdge(LazyScript** lazyp) override; void onBaseShapeEdge(BaseShape** basep) override; void onScopeEdge(Scope** basep) override; + void onRegExpSharedEdge(RegExpShared** sharedp) override; void onChild(const JS::GCCellPtr& thing) override { MOZ_ASSERT(!RelocationOverlay::isCellForwarded(thing.asCell())); } @@ -82,6 +83,10 @@ struct MovingTracer : JS::CallbackTracer #ifdef DEBUG TracerKind getTracerKind() const override { return TracerKind::Moving; } #endif + + private: + template + void updateEdge(T** thingp); }; // Structure for counting how many times objects in a particular group have diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index adc08bb121..de3db9bc31 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -735,6 +735,8 @@ class GCRuntime void enableCompactingGC(); bool isCompactingGCEnabled() const; + bool isShrinkingGC() const { return invocationKind == GC_SHRINK; } + void setGrayRootsTracer(JSTraceDataOp traceOp, void* data); MOZ_MUST_USE bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data); void removeBlackRootsTracer(JSTraceDataOp traceOp, void* data); diff --git a/js/src/gc/Heap.h b/js/src/gc/Heap.h index ce4a0dbd56..14604d3bbe 100644 --- a/js/src/gc/Heap.h +++ b/js/src/gc/Heap.h @@ -115,6 +115,7 @@ enum class AllocKind { SYMBOL, JITCODE, SCOPE, + REGEXP_SHARED, LIMIT, LAST = LIMIT - 1 }; @@ -122,38 +123,39 @@ enum class AllocKind { // Macro to enumerate the different allocation kinds supplying information about // the trace kind, C++ type and allocation size. #define FOR_EACH_OBJECT_ALLOCKIND(D) \ - /* AllocKind TraceKind TypeName SizedType */ \ - D(FUNCTION, Object, JSObject, JSFunction) \ - D(FUNCTION_EXTENDED, Object, JSObject, FunctionExtended) \ - D(OBJECT0, Object, JSObject, JSObject_Slots0) \ - D(OBJECT0_BACKGROUND, Object, JSObject, JSObject_Slots0) \ - D(OBJECT2, Object, JSObject, JSObject_Slots2) \ - D(OBJECT2_BACKGROUND, Object, JSObject, JSObject_Slots2) \ - D(OBJECT4, Object, JSObject, JSObject_Slots4) \ - D(OBJECT4_BACKGROUND, Object, JSObject, JSObject_Slots4) \ - D(OBJECT8, Object, JSObject, JSObject_Slots8) \ - D(OBJECT8_BACKGROUND, Object, JSObject, JSObject_Slots8) \ - D(OBJECT12, Object, JSObject, JSObject_Slots12) \ - D(OBJECT12_BACKGROUND, Object, JSObject, JSObject_Slots12) \ - D(OBJECT16, Object, JSObject, JSObject_Slots16) \ - D(OBJECT16_BACKGROUND, Object, JSObject, JSObject_Slots16) + /* AllocKind TraceKind TypeName SizedType */ \ + D(FUNCTION, Object, JSObject, JSFunction) \ + D(FUNCTION_EXTENDED, Object, JSObject, FunctionExtended) \ + D(OBJECT0, Object, JSObject, JSObject_Slots0) \ + D(OBJECT0_BACKGROUND, Object, JSObject, JSObject_Slots0) \ + D(OBJECT2, Object, JSObject, JSObject_Slots2) \ + D(OBJECT2_BACKGROUND, Object, JSObject, JSObject_Slots2) \ + D(OBJECT4, Object, JSObject, JSObject_Slots4) \ + D(OBJECT4_BACKGROUND, Object, JSObject, JSObject_Slots4) \ + D(OBJECT8, Object, JSObject, JSObject_Slots8) \ + D(OBJECT8_BACKGROUND, Object, JSObject, JSObject_Slots8) \ + D(OBJECT12, Object, JSObject, JSObject_Slots12) \ + D(OBJECT12_BACKGROUND, Object, JSObject, JSObject_Slots12) \ + D(OBJECT16, Object, JSObject, JSObject_Slots16) \ + D(OBJECT16_BACKGROUND, Object, JSObject, JSObject_Slots16) #define FOR_EACH_NONOBJECT_ALLOCKIND(D) \ - /* AllocKind TraceKind TypeName SizedType */ \ - D(SCRIPT, Script, JSScript, JSScript) \ - D(LAZY_SCRIPT, LazyScript, js::LazyScript, js::LazyScript) \ - D(SHAPE, Shape, js::Shape, js::Shape) \ - D(ACCESSOR_SHAPE, Shape, js::AccessorShape, js::AccessorShape) \ - D(BASE_SHAPE, BaseShape, js::BaseShape, js::BaseShape) \ - D(OBJECT_GROUP, ObjectGroup, js::ObjectGroup, js::ObjectGroup) \ - D(FAT_INLINE_STRING, String, JSFatInlineString, JSFatInlineString) \ - D(STRING, String, JSString, JSString) \ - D(EXTERNAL_STRING, String, JSExternalString, JSExternalString) \ - D(FAT_INLINE_ATOM, String, js::FatInlineAtom, js::FatInlineAtom) \ - D(ATOM, String, js::NormalAtom, js::NormalAtom) \ - D(SYMBOL, Symbol, JS::Symbol, JS::Symbol) \ - D(JITCODE, JitCode, js::jit::JitCode, js::jit::JitCode) \ - D(SCOPE, Scope, js::Scope, js::Scope) + /* AllocKind TraceKind TypeName SizedType */ \ + D(SCRIPT, Script, JSScript, JSScript) \ + D(LAZY_SCRIPT, LazyScript, js::LazyScript, js::LazyScript) \ + D(SHAPE, Shape, js::Shape, js::Shape) \ + D(ACCESSOR_SHAPE, Shape, js::AccessorShape, js::AccessorShape) \ + D(BASE_SHAPE, BaseShape, js::BaseShape, js::BaseShape) \ + D(OBJECT_GROUP, ObjectGroup, js::ObjectGroup, js::ObjectGroup) \ + D(FAT_INLINE_STRING, String, JSFatInlineString, JSFatInlineString) \ + D(STRING, String, JSString, JSString) \ + D(EXTERNAL_STRING, String, JSExternalString, JSExternalString) \ + D(FAT_INLINE_ATOM, String, js::FatInlineAtom, js::FatInlineAtom) \ + D(ATOM, String, js::NormalAtom, js::NormalAtom) \ + D(SYMBOL, Symbol, JS::Symbol, JS::Symbol) \ + D(JITCODE, JitCode, js::jit::JitCode, js::jit::JitCode) \ + D(SCOPE, Scope, js::Scope, js::Scope) \ + D(REGEXP_SHARED, RegExpShared, js::RegExpShared, js::RegExpShared) #define FOR_EACH_ALLOCKIND(D) \ FOR_EACH_OBJECT_ALLOCKIND(D) \ @@ -338,6 +340,9 @@ class TenuredCell : public Cell static MOZ_ALWAYS_INLINE void writeBarrierPost(void* cellp, TenuredCell* prior, TenuredCell* next); + // Default implementation for kinds that don't require finalization. + void finalize(FreeOp* fop) {} + // Default implementation for kinds that don't require fixup. void fixupAfterMovingGC() {} diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index da8da5c6b5..058c495a77 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -436,6 +436,14 @@ js::TraceNullableEdge(JSTracer* trc, WriteBarrieredBase* thingp, const char* 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::TraceEdge(JSTracer* trc, JS::Heap* thingp, const char* name) @@ -550,6 +558,7 @@ js::TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name) 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*); \ @@ -866,6 +875,7 @@ js::GCMarker::markAndTraceChildren(T* thing) namespace js { template <> void GCMarker::traverse(BaseShape* thing) { markAndTraceChildren(thing); } template <> void GCMarker::traverse(JS::Symbol* thing) { markAndTraceChildren(thing); } +template <> void GCMarker::traverse(RegExpShared* thing) { markAndTraceChildren(thing); } } // namespace js // Strings, LazyScripts, Shapes, and Scopes are extremely common, but have diff --git a/js/src/gc/Policy.h b/js/src/gc/Policy.h index 451dac1194..8a974dd0d4 100644 --- a/js/src/gc/Policy.h +++ b/js/src/gc/Policy.h @@ -84,6 +84,7 @@ class JitCode; D(js::PlainObject*) \ D(js::PropertyName*) \ D(js::RegExpObject*) \ + D(js::RegExpShared*) \ D(js::SavedFrame*) \ D(js::Scope*) \ D(js::ScriptSourceObject*) \ diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 7a4c8f315a..329851a669 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -165,6 +165,7 @@ static const PhaseInfo phases[] = { { PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP }, { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP }, { PHASE_SWEEP_SCOPE, "Sweep Scope", PHASE_SWEEP }, + { PHASE_SWEEP_REGEXP_SHARED, "Sweep RegExpShared", PHASE_SWEEP }, { PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP }, { PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP }, { PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP }, diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 2b1ce969d1..9e28be9f97 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -62,6 +62,7 @@ enum Phase : uint8_t { PHASE_SWEEP_STRING, PHASE_SWEEP_SCRIPT, PHASE_SWEEP_SCOPE, + PHASE_SWEEP_REGEXP_SHARED, PHASE_SWEEP_SHAPE, PHASE_SWEEP_JITCODE, PHASE_FINALIZE_END, diff --git a/js/src/gc/Tracer.h b/js/src/gc/Tracer.h index 4c846f95d6..76eec26182 100644 --- a/js/src/gc/Tracer.h +++ b/js/src/gc/Tracer.h @@ -64,6 +64,10 @@ template void TraceNullableEdge(JSTracer* trc, WriteBarrieredBase* thingp, const char* name); +template +void +TraceNullableEdge(JSTracer* trc, ReadBarriered* thingp, const char* 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. diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 0d23c96cb4..2aec76dbd9 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6144,7 +6144,7 @@ JS_GetRegExpFlags(JSContext* cx, HandleObject obj) RegExpGuard shared(cx); if (!RegExpToShared(cx, obj, &shared)) return false; - return shared.re()->getFlags(); + return shared->getFlags(); } JS_PUBLIC_API(JSString*) @@ -6156,7 +6156,7 @@ JS_GetRegExpSource(JSContext* cx, HandleObject obj) RegExpGuard shared(cx); if (!RegExpToShared(cx, obj, &shared)) return nullptr; - return shared.re()->getSource(); + return shared->getSource(); } /************************************************************************/ diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 8cf22026f3..7cccd25ad6 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -61,7 +61,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = data(nullptr), allocationMetadataBuilder(nullptr), lastAnimationTime(0), - regExps(runtime_), + regExps(zone), globalWriteBarriered(0), detachedTypedObjects(0), objectMetadataState(ImmediateMetadata()), @@ -210,6 +210,13 @@ JSCompartment::ensureJitCompartmentExists(JSContext* cx) } #ifdef JSGC_HASH_TABLE_CHECKS + +void +js::DtoaCache::checkCacheAfterMovingGC() +{ + MOZ_ASSERT(!s || !IsForwarded(s)); +} + namespace { struct CheckGCThingAfterMovingGCFunctor { template void operator()(T* t) { CheckGCThingAfterMovingGC(*t); } @@ -232,7 +239,8 @@ JSCompartment::checkWrapperMapAfterMovingGC() MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front()); } } -#endif + +#endif // JSGC_HASH_TABLE_CHECKS bool JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped, diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 7723aeffa4..becf9a39ff 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -64,7 +64,7 @@ class DtoaCache { } #ifdef JSGC_HASH_TABLE_CHECKS - void checkCacheAfterMovingGC() { MOZ_ASSERT(!s || !IsForwarded(s)); } + void checkCacheAfterMovingGC(); #endif }; diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 00da8295ad..f42a4def5f 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1114,7 +1114,7 @@ extern JS_FRIEND_API(unsigned) GetEnterCompartmentDepth(JSContext* cx); #endif -class RegExpGuard; +using RegExpGuard = JS::Rooted; extern JS_FRIEND_API(bool) RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp, RegExpGuard* shared); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 0ddc128e2d..7fb035794b 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -357,7 +357,12 @@ static const FinalizePhase BackgroundFinalizePhases[] = { }, { gcstats::PHASE_SWEEP_SCOPE, { - AllocKind::SCOPE + AllocKind::SCOPE, + } + }, + { + gcstats::PHASE_SWEEP_REGEXP_SHARED, { + AllocKind::REGEXP_SHARED, } }, { @@ -1599,7 +1604,6 @@ static const AllocKind AllocKindsToRelocate[] = { AllocKind::OBJECT16_BACKGROUND, AllocKind::SCRIPT, AllocKind::LAZY_SCRIPT, - AllocKind::SCOPE, AllocKind::SHAPE, AllocKind::ACCESSOR_SHAPE, AllocKind::BASE_SHAPE, @@ -1607,7 +1611,9 @@ static const AllocKind AllocKindsToRelocate[] = { AllocKind::STRING, AllocKind::EXTERNAL_STRING, AllocKind::FAT_INLINE_ATOM, - AllocKind::ATOM + AllocKind::ATOM, + AllocKind::SCOPE, + AllocKind::REGEXP_SHARED }; Arena* @@ -1931,61 +1937,23 @@ GCRuntime::relocateArenas(Zone* zone, JS::gcreason::Reason reason, Arena*& reloc return true; } -void -MovingTracer::onObjectEdge(JSObject** objp) +template +inline void +MovingTracer::updateEdge(T** thingp) { - JSObject* obj = *objp; - if (obj->runtimeFromAnyThread() == runtime() && IsForwarded(obj)) - *objp = Forwarded(obj); + auto thing = *thingp; + if (thing->runtimeFromAnyThread() == runtime() && IsForwarded(thing)) + *thingp = Forwarded(thing); } -void -MovingTracer::onShapeEdge(Shape** shapep) -{ - Shape* shape = *shapep; - if (shape->runtimeFromAnyThread() == runtime() && IsForwarded(shape)) - *shapep = Forwarded(shape); -} - -void -MovingTracer::onStringEdge(JSString** stringp) -{ - JSString* string = *stringp; - if (string->runtimeFromAnyThread() == runtime() && IsForwarded(string)) - *stringp = Forwarded(string); -} - -void -MovingTracer::onScriptEdge(JSScript** scriptp) -{ - JSScript* script = *scriptp; - if (script->runtimeFromAnyThread() == runtime() && IsForwarded(script)) - *scriptp = Forwarded(script); -} - -void -MovingTracer::onLazyScriptEdge(LazyScript** lazyp) -{ - LazyScript* lazy = *lazyp; - if (lazy->runtimeFromAnyThread() == runtime() && IsForwarded(lazy)) - *lazyp = Forwarded(lazy); -} - -void -MovingTracer::onBaseShapeEdge(BaseShape** basep) -{ - BaseShape* base = *basep; - if (base->runtimeFromAnyThread() == runtime() && IsForwarded(base)) - *basep = Forwarded(base); -} - -void -MovingTracer::onScopeEdge(Scope** scopep) -{ - Scope* scope = *scopep; - if (scope->runtimeFromAnyThread() == runtime() && IsForwarded(scope)) - *scopep = Forwarded(scope); -} +void MovingTracer::onObjectEdge(JSObject** objp) { updateEdge(objp); } +void MovingTracer::onShapeEdge(Shape** shapep) { updateEdge(shapep); } +void MovingTracer::onStringEdge(JSString** stringp) { updateEdge(stringp); } +void MovingTracer::onScriptEdge(JSScript** scriptp) { updateEdge(scriptp); } +void MovingTracer::onLazyScriptEdge(LazyScript** lazyp) { updateEdge(lazyp); } +void MovingTracer::onBaseShapeEdge(BaseShape** basep) { updateEdge(basep); } +void MovingTracer::onScopeEdge(Scope** scopep) { updateEdge(scopep); } +void MovingTracer::onRegExpSharedEdge(RegExpShared** sharedp) { updateEdge(sharedp); } void Zone::prepareForCompacting() diff --git a/js/src/jsgc.h b/js/src/jsgc.h index f31f0893b1..4d6e086521 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -121,6 +121,7 @@ IsNurseryAllocable(AllocKind kind) false, /* AllocKind::SYMBOL */ false, /* AllocKind::JITCODE */ false, /* AllocKind::SCOPE */ + false, /* AllocKind::REGEXP_SHARED */ }; JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT)); return map[size_t(kind)]; @@ -159,6 +160,7 @@ IsBackgroundFinalized(AllocKind kind) true, /* AllocKind::SYMBOL */ false, /* AllocKind::JITCODE */ true, /* AllocKind::SCOPE */ + true, /* AllocKind::REGEXP_SHARED */ }; JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT)); return map[size_t(kind)]; @@ -1172,109 +1174,29 @@ class RelocationOverlay // to allow slots to be accessed. template -struct MightBeForwarded -{ - static_assert(mozilla::IsBaseOf::value, - "T must derive from Cell"); - static_assert(!mozilla::IsSame::value && !mozilla::IsSame::value, - "T must not be Cell or TenuredCell"); - - static const bool value = mozilla::IsBaseOf::value || - mozilla::IsBaseOf::value || - mozilla::IsBaseOf::value || - mozilla::IsBaseOf::value || - mozilla::IsBaseOf::value || - mozilla::IsBaseOf::value || - mozilla::IsBaseOf::value; -}; +inline bool IsForwarded(T* t); +inline bool IsForwarded(const JS::Value& value); template -inline bool -IsForwarded(T* t) -{ - RelocationOverlay* overlay = RelocationOverlay::fromCell(t); - if (!MightBeForwarded::value) { - MOZ_ASSERT(!overlay->isForwarded()); - return false; - } +inline T* Forwarded(T* t); - return overlay->isForwarded(); -} - -struct IsForwardedFunctor : public BoolDefaultAdaptor { - template bool operator()(T* t) { return IsForwarded(t); } -}; - -inline bool -IsForwarded(const JS::Value& value) -{ - return DispatchTyped(IsForwardedFunctor(), value); -} +inline Value Forwarded(const JS::Value& value); template -inline T* -Forwarded(T* t) -{ - RelocationOverlay* overlay = RelocationOverlay::fromCell(t); - MOZ_ASSERT(overlay->isForwarded()); - return reinterpret_cast(overlay->forwardingAddress()); -} - -struct ForwardedFunctor : public IdentityDefaultAdaptor { - template inline Value operator()(T* t) { - return js::gc::RewrapTaggedPointer::wrap(Forwarded(t)); - } -}; - -inline Value -Forwarded(const JS::Value& value) -{ - return DispatchTyped(ForwardedFunctor(), value); -} - -template -inline T -MaybeForwarded(T t) -{ - if (IsForwarded(t)) - t = Forwarded(t); - MakeAccessibleAfterMovingGC(t); - return t; -} +inline T MaybeForwarded(T t); #ifdef JSGC_HASH_TABLE_CHECKS template -inline bool -IsGCThingValidAfterMovingGC(T* t) -{ - return !IsInsideNursery(t) && !RelocationOverlay::isCellForwarded(t); -} +inline bool IsGCThingValidAfterMovingGC(T* t); template -inline void -CheckGCThingAfterMovingGC(T* t) -{ - if (t) - MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t)); -} +inline void CheckGCThingAfterMovingGC(T* t); template -inline void -CheckGCThingAfterMovingGC(const ReadBarriered& t) -{ - CheckGCThingAfterMovingGC(t.unbarrieredGet()); -} +inline void CheckGCThingAfterMovingGC(const ReadBarriered& t); -struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor { - template void operator()(T* t) { CheckGCThingAfterMovingGC(t); } -}; - -inline void -CheckValueAfterMovingGC(const JS::Value& value) -{ - DispatchTyped(CheckValueAfterMovingGCFunctor(), value); -} +inline void CheckValueAfterMovingGC(const JS::Value& value); #endif // JSGC_HASH_TABLE_CHECKS diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 01ac9a64a2..1c96d37c00 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -477,6 +477,114 @@ RelocationOverlay::forwardTo(Cell* cell) newLocation_ = cell; } +template +struct MightBeForwarded +{ + static_assert(mozilla::IsBaseOf::value, + "T must derive from Cell"); + static_assert(!mozilla::IsSame::value && !mozilla::IsSame::value, + "T must not be Cell or TenuredCell"); + + static const bool value = mozilla::IsBaseOf::value || + mozilla::IsBaseOf::value || + mozilla::IsBaseOf::value || + mozilla::IsBaseOf::value || + mozilla::IsBaseOf::value || + mozilla::IsBaseOf::value || + mozilla::IsBaseOf::value || + mozilla::IsBaseOf::value; +}; + +template +inline bool +IsForwarded(T* t) +{ + RelocationOverlay* overlay = RelocationOverlay::fromCell(t); + if (!MightBeForwarded::value) { + MOZ_ASSERT(!overlay->isForwarded()); + return false; + } + + return overlay->isForwarded(); +} + +struct IsForwardedFunctor : public BoolDefaultAdaptor { + template bool operator()(T* t) { return IsForwarded(t); } +}; + +inline bool +IsForwarded(const JS::Value& value) +{ + return DispatchTyped(IsForwardedFunctor(), value); +} + +template +inline T* +Forwarded(T* t) +{ + RelocationOverlay* overlay = RelocationOverlay::fromCell(t); + MOZ_ASSERT(overlay->isForwarded()); + return reinterpret_cast(overlay->forwardingAddress()); +} + +struct ForwardedFunctor : public IdentityDefaultAdaptor { + template inline Value operator()(T* t) { + return js::gc::RewrapTaggedPointer::wrap(Forwarded(t)); + } +}; + +inline Value +Forwarded(const JS::Value& value) +{ + return DispatchTyped(ForwardedFunctor(), value); +} + +template +inline T +MaybeForwarded(T t) +{ + if (IsForwarded(t)) + t = Forwarded(t); + MakeAccessibleAfterMovingGC(t); + return t; +} + +#ifdef JSGC_HASH_TABLE_CHECKS + +template +inline bool +IsGCThingValidAfterMovingGC(T* t) +{ + return !IsInsideNursery(t) && !RelocationOverlay::isCellForwarded(t); +} + +template +inline void +CheckGCThingAfterMovingGC(T* t) +{ + if (t) + MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t)); +} + +template +inline void +CheckGCThingAfterMovingGC(const ReadBarriered& t) +{ + CheckGCThingAfterMovingGC(t.unbarrieredGet()); +} + +struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor { + template void operator()(T* t) { CheckGCThingAfterMovingGC(t); } +}; + +inline void +CheckValueAfterMovingGC(const JS::Value& value) +{ + DispatchTyped(CheckValueAfterMovingGCFunctor(), value); +} + +#endif // JSGC_HASH_TABLE_CHECKS + } /* namespace gc */ } /* namespace js */ diff --git a/js/src/proxy/CrossCompartmentWrapper.cpp b/js/src/proxy/CrossCompartmentWrapper.cpp index e12f96bfa7..bc698c8739 100644 --- a/js/src/proxy/CrossCompartmentWrapper.cpp +++ b/js/src/proxy/CrossCompartmentWrapper.cpp @@ -439,15 +439,14 @@ CrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject wrapper, bool bool CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper, RegExpGuard* g) const { - RegExpGuard wrapperGuard(cx); + RegExpGuard re(cx); { AutoCompartment call(cx, wrappedObject(wrapper)); - if (!Wrapper::regexp_toShared(cx, wrapper, &wrapperGuard)) + if (!Wrapper::regexp_toShared(cx, wrapper, &re)) return false; } // Get an equivalent RegExpShared associated with the current compartment. - RegExpShared* re = wrapperGuard.re(); return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), g); } diff --git a/js/src/proxy/Proxy.h b/js/src/proxy/Proxy.h index a1942b25de..4064b8a7f2 100644 --- a/js/src/proxy/Proxy.h +++ b/js/src/proxy/Proxy.h @@ -12,8 +12,6 @@ namespace js { -class RegExpGuard; - /* * Dispatch point for handlers that executes the appropriate C++ or scripted traps. * diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index ab8648ad3f..9b6d3dda72 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -595,6 +595,13 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JS::TraceKind traceKin break; } + case JS::TraceKind::RegExpShared: { + auto regexp = static_cast(thing); + zStats->regExpSharedsGCHeap += thingSize; + zStats->regExpSharedsMallocHeap += regexp->sizeOfExcludingThis(rtStats->mallocSizeOf_); + break; + } + default: MOZ_CRASH("invalid traceKind in StatsCellCallback"); } diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 151571085d..4cc2b539aa 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -8,6 +8,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/PodOperations.h" +#include "jshashutil.h" #include "jsstr.h" #ifdef DEBUG #include "jsutil.h" @@ -120,25 +121,11 @@ VectorMatchPairs::allocOrExpandArray(size_t pairCount) /* RegExpObject */ -static inline void -RegExpSharedReadBarrier(JSContext* cx, RegExpShared* shared) -{ - Zone* zone = cx->zone(); - if (zone->needsIncrementalBarrier()) - shared->trace(zone->barrierTracer()); - if (shared->isMarkedGray()) - shared->unmarkGray(); -} - /* static */ bool RegExpObject::getShared(JSContext* cx, Handle regexp, RegExpGuard* g) { - if (RegExpShared* shared = regexp->maybeShared()) { - // Fetching a RegExpShared from an object requires a read - // barrier, as the shared pointer might be weak. - RegExpSharedReadBarrier(cx, shared); - - g->init(*shared); + if (regexp->hasShared()) { + g->set(regexp->sharedRef()); return true; } @@ -179,26 +166,32 @@ RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlag* mask) /* static */ void RegExpObject::trace(JSTracer* trc, JSObject* obj) { - RegExpShared* shared = obj->as().maybeShared(); - if (!shared) - return; + obj->as().trace(trc); +} - // When tracing through the object normally, we have the option of - // unlinking the object from its RegExpShared so that the RegExpShared may - // be collected. To detect this we need to test all the following - // conditions, since: +static inline bool +IsMarkingTrace(JSTracer* trc) +{ + // Determine whether tracing is happening during normal marking. We need to + // test all the following conditions, since: + // // 1. During TraceRuntime, isHeapBusy() is true, but the tracer might not // be a marking tracer. // 2. When a write barrier executes, IsMarkingTracer is true, but // isHeapBusy() will be false. - if (trc->runtime()->isHeapCollecting() && - trc->isMarkingTracer() && - !obj->asTenured().zone()->isPreservingCode()) - { - obj->as().NativeObject::setPrivate(nullptr); - } else { - shared->trace(trc); - } + + return trc->runtime()->isHeapCollecting() && trc->isMarkingTracer(); +} + +void +RegExpObject::trace(JSTracer* trc) +{ + // When marking the object normally we have the option of unlinking the + // object from its RegExpShared so that the RegExpShared may be collected. + if (IsMarkingTrace(trc) && !zone()->isPreservingCode()) + sharedRef() = nullptr; + + TraceNullableEdge(trc, &sharedRef(), "RegExpObject shared"); } static JSObject* @@ -287,7 +280,7 @@ RegExpObject::create(ExclusiveContext* cx, HandleAtom source, RegExpFlag flags, /* static */ bool RegExpObject::createShared(JSContext* cx, Handle regexp, RegExpGuard* g) { - MOZ_ASSERT(!regexp->maybeShared()); + MOZ_ASSERT(!regexp->hasShared()); if (!cx->compartment()->regExps.get(cx, regexp->getSource(), regexp->getFlags(), g)) return false; @@ -902,7 +895,7 @@ RegExpObject::dumpBytecode(JSContext* cx, Handle regexp, if (!getShared(cx, regexp, &g)) return false; - return g.re()->dumpBytecode(cx, match_only, input); + return g->dumpBytecode(cx, match_only, input); } #endif @@ -951,7 +944,7 @@ js::StringHasRegExpMetaChars(JSLinearString* str) /* RegExpShared */ RegExpShared::RegExpShared(JSAtom* source, RegExpFlag flags) - : source(source), flags(flags), parenCount(0), canStringMatch(false), marked_(false), + : source(source), flags(flags), canStringMatch(false), parenCount(0), numNamedCaptures_(0), groupsTemplate_(nullptr) {} @@ -962,10 +955,11 @@ RegExpShared::~RegExpShared() } void -RegExpShared::trace(JSTracer* trc) +RegExpShared::traceChildren(JSTracer* trc) { - if (trc->isMarkingTracer()) - marked_ = true; + // Discard code to avoid holding onto ExecutablePools. + if (IsMarkingTrace(trc) && trc->runtime()->gc.isShrinkingGC()) + discardJitCode(); TraceNullableEdge(trc, &source, "RegExpShared source"); for (auto& comp : compilationArray) @@ -973,27 +967,11 @@ RegExpShared::trace(JSTracer* trc) TraceNullableEdge(trc, &groupsTemplate_, "RegExpShared groupsTemplate"); } -bool -RegExpShared::isMarkedGray() const -{ - if (source && source->isMarked(gc::GRAY)) - return true; - for (const auto& comp : compilationArray) { - if (comp.jitCode && comp.jitCode->isMarked(gc::GRAY)) - return true; - } - return false; -} - void -RegExpShared::unmarkGray() +RegExpShared::discardJitCode() { - if (source) - JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr(source)); - for (const auto& comp : compilationArray) { - if (comp.jitCode) - JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr(comp.jitCode.get())); - } + for (auto& comp : compilationArray) + comp.jitCode = nullptr; } bool @@ -1246,9 +1224,9 @@ RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start, } size_t -RegExpShared::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) +RegExpShared::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { - size_t n = mallocSizeOf(this); + size_t n = 0; for (size_t i = 0; i < ArrayLength(compilationArray); i++) { const RegExpCompilation& compilation = compilationArray[i]; @@ -1265,8 +1243,8 @@ RegExpShared::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) /* RegExpCompartment */ -RegExpCompartment::RegExpCompartment(JSRuntime* rt) - : set_(rt), +RegExpCompartment::RegExpCompartment(Zone* zone) + : set_(zone, Set(zone->runtimeFromMainThread())), matchResultTemplateObject_(nullptr), optimizableRegExpPrototypeShape_(nullptr), optimizableRegExpInstanceShape_(nullptr) @@ -1274,14 +1252,7 @@ RegExpCompartment::RegExpCompartment(JSRuntime* rt) RegExpCompartment::~RegExpCompartment() { - // Because of stray mark bits being set (see RegExpCompartment::sweep) - // there might still be RegExpShared instances which haven't been deleted. - if (set_.initialized()) { - for (Set::Enum e(set_); !e.empty(); e.popFront()) { - RegExpShared* shared = e.front(); - js_delete(shared); - } - } + MOZ_ASSERT_IF(set_.initialized(), set_.empty()); } ArrayObject* @@ -1291,7 +1262,7 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext* cx) /* Create template array object */ RootedArrayObject templateObject(cx, NewDenseUnallocatedArray(cx, RegExpObject::MaxPairCount, - nullptr, TenuredObject)); + nullptr, TenuredObject)); if (!templateObject) return matchResultTemplateObject_; // = nullptr @@ -1357,59 +1328,9 @@ RegExpCompartment::init(JSContext* cx) return true; } -bool -RegExpShared::needsSweep(JSRuntime* rt) -{ - // Sometimes RegExpShared instances are marked without the compartment - // being subsequently cleared. This can happen if a GC is restarted while - // in progress (i.e. performing a full GC in the middle of an incremental - // GC) or if a RegExpShared referenced via the stack is traced but is not - // in a zone being collected. - // - // Because of this we only treat the marked_ bit as a hint, and destroy the - // RegExpShared if it was accidentally marked earlier but wasn't marked by - // the current trace. - bool keep = marked() && IsMarked(rt, &source); - for (size_t i = 0; i < ArrayLength(compilationArray); i++) { - RegExpShared::RegExpCompilation& compilation = compilationArray[i]; - if (compilation.jitCode && gc::IsAboutToBeFinalized(&compilation.jitCode)) - keep = false; - } - - MOZ_ASSERT(rt->isHeapMajorCollecting()); - if (keep || rt->gc.isHeapCompacting()) { - clearMarked(); - return false; - } - - return true; -} - -void -RegExpShared::discardJitCode() -{ - for (size_t i = 0; i < ArrayLength(compilationArray); i++) - compilationArray[i].jitCode = nullptr; -} - void RegExpCompartment::sweep(JSRuntime* rt) { - if (!set_.initialized()) - return; - - for (Set::Enum e(set_); !e.empty(); e.popFront()) { - RegExpShared* shared = e.front(); - if (shared->needsSweep(rt)) { - js_delete(shared); - e.removeFront(); - } else { - // Discard code to avoid holding onto ExecutablePools. - if (rt->gc.isHeapCompacting()) - shared->discardJitCode(); - } - } - if (matchResultTemplateObject_ && IsAboutToBeFinalized(&matchResultTemplateObject_)) { @@ -1432,30 +1353,24 @@ RegExpCompartment::sweep(JSRuntime* rt) bool RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g) { - Key key(source, flags); - Set::AddPtr p = set_.lookupForAdd(key); + DependentAddPtr p(cx, set_.get(), Key(source, flags)); if (p) { - // Trigger a read barrier on existing RegExpShared instances fetched - // from the table (which only holds weak references). - RegExpSharedReadBarrier(cx, *p); - - g->init(**p); + g->set(*p); return true; } - ScopedJSDeletePtr shared(cx->new_(source, flags)); + auto shared = Allocate(cx); if (!shared) return false; - if (!set_.add(p, shared)) { + new (shared) RegExpShared(source, flags); + + if (!p.add(cx, set_.get(), Key(source, flags), shared)) { ReportOutOfMemory(cx); return false; } - // Trace RegExpShared instances created during an incremental GC. - RegExpSharedReadBarrier(cx, shared); - - g->init(*shared.forget()); + g->set(shared); return true; } @@ -1472,13 +1387,7 @@ RegExpCompartment::get(JSContext* cx, HandleAtom atom, JSString* opt, RegExpGuar size_t RegExpCompartment::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { - size_t n = 0; - n += set_.sizeOfExcludingThis(mallocSizeOf); - for (Set::Enum e(set_); !e.empty(); e.popFront()) { - RegExpShared* shared = e.front(); - n += shared->sizeOfIncludingThis(mallocSizeOf); - } - return n; + return set_.sizeOfExcludingThis(mallocSizeOf); } /* Functions */ @@ -1506,7 +1415,7 @@ js::CloneRegExpObject(JSContext* cx, JSObject* obj_) return nullptr; clone->initAndZeroLastIndex(source, g->getFlags(), cx); - clone->setShared(*g.re()); + clone->setShared(*g); return clone; } @@ -1649,3 +1558,10 @@ js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj, js::RegExpGuard* g) { return RegExpToShared(cx, obj, g); } + +JS::ubi::Node::Size +JS::ubi::Concrete::size(mozilla::MallocSizeOf mallocSizeOf) const +{ + return js::gc::Arena::thingSize(gc::AllocKind::REGEXP_SHARED) + + get().sizeOfExcludingThis(mallocSizeOf); +} diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index 547746529a..16ba7f41fd 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -46,7 +46,7 @@ class RegExpStatics; namespace frontend { class TokenStream; } -enum RegExpFlag +enum RegExpFlag : uint8_t { IgnoreCaseFlag = 0x01, GlobalFlag = 0x02, @@ -94,7 +94,7 @@ CloneRegExpObject(JSContext* cx, JSObject* regexp); * objects when we are preserving jitcode in their zone, to avoid the same * recompilation inefficiencies as normal Ion and baseline compilation. */ -class RegExpShared +class RegExpShared : public gc::TenuredCell { public: enum CompilationMode { @@ -115,7 +115,7 @@ class RegExpShared struct RegExpCompilation { - HeapPtr jitCode; + ReadBarriered jitCode; uint8_t* byteCode; RegExpCompilation() : byteCode(nullptr) {} @@ -127,12 +127,11 @@ class RegExpShared }; /* Source to the RegExp, for lazy compilation. */ - HeapPtr source; + HeapPtr source; RegExpFlag flags; - size_t parenCount; bool canStringMatch; - bool marked_; + size_t parenCount; uint32_t numNamedCaptures_; HeapPtr groupsTemplate_; @@ -151,6 +150,8 @@ class RegExpShared Vector tables; /* Internal functions. */ + RegExpShared(JSAtom* source, RegExpFlag flags); + bool compile(JSContext* cx, HandleLinearString input, CompilationMode mode, ForceByteCodeEnum force); bool compile(JSContext* cx, HandleAtom pattern, HandleLinearString input, @@ -168,7 +169,6 @@ class RegExpShared } public: - RegExpShared(JSAtom* source, RegExpFlag flags); ~RegExpShared(); // Execute this RegExp on input starting from searchIndex, filling in @@ -214,16 +214,9 @@ class RegExpShared || isCompiled(MatchOnly, true) || isCompiled(MatchOnly, false); } - void trace(JSTracer* trc); - bool needsSweep(JSRuntime* rt); + void traceChildren(JSTracer* trc); void discardJitCode(); - bool marked() const { return marked_; } - void clearMarked() { marked_ = false; } - - bool isMarkedGray() const; - void unmarkGray(); - static size_t offsetOfSource() { return offsetof(RegExpShared, source); } @@ -251,60 +244,13 @@ class RegExpShared return offsetof(RegExpShared, groupsTemplate_); } - size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); #ifdef DEBUG bool dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input); #endif }; -/* - * Extend the lifetime of a given RegExpShared to at least the lifetime of - * the guard object. See Regular Expression comment at the top. - */ -class RegExpGuard : public JS::CustomAutoRooter -{ - RegExpShared* re_; - - RegExpGuard(const RegExpGuard&) = delete; - void operator=(const RegExpGuard&) = delete; - - public: - explicit RegExpGuard(ExclusiveContext* cx) - : CustomAutoRooter(cx), re_(nullptr) - {} - - RegExpGuard(ExclusiveContext* cx, RegExpShared& re) - : CustomAutoRooter(cx), re_(nullptr) - { - init(re); - } - - ~RegExpGuard() { - release(); - } - - public: - void init(RegExpShared& re) { - MOZ_ASSERT(!initialized()); - re_ = &re; - } - - void release() { - re_ = nullptr; - } - - virtual void trace(JSTracer* trc) { - if (re_) - re_->trace(trc); - } - - bool initialized() const { return !!re_; } - RegExpShared* re() const { MOZ_ASSERT(initialized()); return re_; } - RegExpShared* operator->() { return re(); } - RegExpShared& operator*() { return *re(); } -}; - class RegExpCompartment { struct Key { @@ -315,8 +261,9 @@ class RegExpCompartment Key(JSAtom* atom, RegExpFlag flag) : atom(atom), flag(flag) { } - MOZ_IMPLICIT Key(RegExpShared* shared) - : atom(shared->getSource()), flag(shared->getFlags()) + MOZ_IMPLICIT Key(const ReadBarriered& shared) + : atom(shared.unbarrieredGet()->getSource()), + flag(shared.unbarrieredGet()->getFlags()) { } typedef Key Lookup; @@ -332,8 +279,8 @@ class RegExpCompartment * The set of all RegExpShareds in the compartment. On every GC, every * RegExpShared that was not marked is deleted and removed from the set. */ - typedef HashSet Set; - Set set_; + using Set = GCHashSet, Key, RuntimeAllocPolicy>; + JS::WeakCache set_; /* * This is the template object where the result of re.exec() is based on, @@ -366,7 +313,7 @@ class RegExpCompartment ArrayObject* createMatchResultTemplateObject(JSContext* cx); public: - explicit RegExpCompartment(JSRuntime* rt); + explicit RegExpCompartment(Zone* zone); ~RegExpCompartment(); bool init(JSContext* cx); @@ -502,12 +449,17 @@ class RegExpObject : public NativeObject static MOZ_MUST_USE bool getShared(JSContext* cx, Handle regexp, RegExpGuard* g); + bool hasShared() { + return !!sharedRef(); + } + void setShared(RegExpShared& shared) { - MOZ_ASSERT(!maybeShared()); - NativeObject::setPrivate(&shared); + MOZ_ASSERT(!hasShared()); + sharedRef() = &shared; } static void trace(JSTracer* trc, JSObject* obj); + void trace(JSTracer* trc); void initIgnoringLastIndex(HandleAtom source, RegExpFlag flags); @@ -528,8 +480,10 @@ class RegExpObject : public NativeObject */ static MOZ_MUST_USE bool createShared(JSContext* cx, Handle regexp, RegExpGuard* g); - RegExpShared* maybeShared() const { - return static_cast(NativeObject::getPrivate(PRIVATE_SLOT)); + + ReadBarriered& sharedRef() { + auto& ref = NativeObject::privateRef(PRIVATE_SLOT); + return reinterpret_cast&>(ref); } /* Call setShared in preference to setPrivate. */ @@ -575,4 +529,29 @@ StringHasRegExpMetaChars(JSLinearString* str); } /* namespace js */ +namespace JS { +namespace ubi { + +template <> +class Concrete : TracerConcrete +{ + protected: + explicit Concrete(js::RegExpShared* ptr) : TracerConcrete(ptr) { } + + public: + static void construct(void* storage, js::RegExpShared* ptr) { + new (storage) Concrete(ptr); + } + + CoarseType coarseType() const final { return CoarseType::Other; } + + Size size(mozilla::MallocSizeOf mallocSizeOf) const override; + + const char16_t* typeName() const override { return concreteTypeName; } + static const char16_t concreteTypeName[]; +}; + +} // namespace ubi +} // namespace JS + #endif /* vm_RegExpObject_h */ diff --git a/js/src/vm/SPSProfiler.cpp b/js/src/vm/SPSProfiler.cpp index 301bf98a3e..2ee9241a1e 100644 --- a/js/src/vm/SPSProfiler.cpp +++ b/js/src/vm/SPSProfiler.cpp @@ -18,6 +18,8 @@ #include "jit/JitFrames.h" #include "vm/StringBuffer.h" +#include "jsgcinlines.h" + using namespace js; using mozilla::DebugOnly; diff --git a/js/src/vm/Shape-inl.h b/js/src/vm/Shape-inl.h index 7b7561220e..34ac3b3d66 100644 --- a/js/src/vm/Shape-inl.h +++ b/js/src/vm/Shape-inl.h @@ -18,6 +18,7 @@ #include "jsatominlines.h" #include "jscntxtinlines.h" +#include "jsgcinlines.h" namespace js { diff --git a/js/src/vm/UbiNode.cpp b/js/src/vm/UbiNode.cpp index 80312bbb7a..ab966cbb4d 100644 --- a/js/src/vm/UbiNode.cpp +++ b/js/src/vm/UbiNode.cpp @@ -311,6 +311,7 @@ template JS::Zone* TracerConcrete::zone() const; template JS::Zone* TracerConcrete::zone() const; template JS::Zone* TracerConcrete::zone() const; template JS::Zone* TracerConcrete::zone() const; +template JS::Zone* TracerConcrete::zone() const; template JS::Zone* TracerConcrete::zone() const; template JS::Zone* TracerConcrete::zone() const; template JS::Zone* TracerConcrete::zone() const; @@ -333,6 +334,7 @@ template UniquePtr TracerConcrete::edges(JSContext* c template UniquePtr TracerConcrete::edges(JSContext* cx, bool wantNames) const; template UniquePtr TracerConcrete::edges(JSContext* cx, bool wantNames) const; template UniquePtr TracerConcrete::edges(JSContext* cx, bool wantNames) const; +template UniquePtr TracerConcrete::edges(JSContext* cx, bool wantNames) const; template UniquePtr TracerConcrete::edges(JSContext* cx, bool wantNames) const; template UniquePtr TracerConcrete::edges(JSContext* cx, bool wantNames) const; template UniquePtr TracerConcrete::edges(JSContext* cx, bool wantNames) const; @@ -397,6 +399,7 @@ const char16_t Concrete::concreteTypeName[] = u"js::Shape"; const char16_t Concrete::concreteTypeName[] = u"js::BaseShape"; const char16_t Concrete::concreteTypeName[] = u"js::ObjectGroup"; const char16_t Concrete::concreteTypeName[] = u"js::Scope"; +const char16_t Concrete::concreteTypeName[] = u"js::RegExpShared"; namespace JS { namespace ubi { diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index 37750e2a20..fc33f702cc 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -1837,6 +1837,14 @@ ReportZoneStats(const JS::ZoneStats& zStats, zStats.scopesMallocHeap, "Arrays of binding names and other binding-related data."); + ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("regexp-shareds/gc-heap"), + zStats.regExpSharedsGCHeap, + "Shared compiled regexp data."); + + ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("regexp-shareds/malloc-heap"), + zStats.regExpSharedsMallocHeap, + "Shared compiled regexp data."); + ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"), zStats.typePool, "Type sets and related data."); @@ -2855,6 +2863,10 @@ JSReporter::CollectReports(WindowPaths* windowPaths, KIND_OTHER, rtStats.zTotals.unusedGCThings.jitcode, "Unused jitcode cells within non-empty arenas."); + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/regexp-shareds"), + KIND_OTHER, rtStats.zTotals.unusedGCThings.regExpShared, + "Unused regexpshared cells within non-empty arenas."); + REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"), KIND_OTHER, rtStats.gcHeapChunkAdmin, "The same as 'explicit/js-non-window/gc-heap/chunk-admin'."); @@ -2906,6 +2918,10 @@ JSReporter::CollectReports(WindowPaths* windowPaths, KIND_OTHER, rtStats.zTotals.jitCodesGCHeap, "Used jitcode cells."); + MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/regexp-shareds"), + KIND_OTHER, rtStats.zTotals.regExpSharedsGCHeap, + "Used regexpshared cells."); + MOZ_ASSERT(gcThingTotal == rtStats.gcHeapGCThings); // Report xpconnect. diff --git a/xpcom/base/CycleCollectedJSContext.h b/xpcom/base/CycleCollectedJSContext.h index 366a1ea3bb..b9fc8e6045 100644 --- a/xpcom/base/CycleCollectedJSContext.h +++ b/xpcom/base/CycleCollectedJSContext.h @@ -543,7 +543,10 @@ void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer); // Returns true if the JS::TraceKind is one the cycle collector cares about. inline bool AddToCCKind(JS::TraceKind aKind) { - return aKind == JS::TraceKind::Object || aKind == JS::TraceKind::Script || aKind == JS::TraceKind::Scope; + return aKind == JS::TraceKind::Object || + aKind == JS::TraceKind::Script || + aKind == JS::TraceKind::Scope || + aKind == JS::TraceKind::RegExpShared; } bool