Bug 1345177 - Make RegExpShared a GC thing r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 27 Mar 2017 10:38:29 +0100
changeset 400580 aac9899a66462ab8a3391ae9025c3e77a89975cc
parent 400579 b6c3fcf039a980c96cac45cea00d91ce441a42f0
child 400581 9625ba329e864ebc4233b8b48338baaaa59f1f2e
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1345177
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1345177 - Make RegExpShared a GC thing r=sfink
js/public/MemoryMetrics.h
js/public/Proxy.h
js/public/TraceKind.h
js/public/TracingAPI.h
js/src/gc/GCInternals.h
js/src/gc/GCRuntime.h
js/src/gc/Heap.h
js/src/gc/Marking.cpp
js/src/gc/Policy.h
js/src/gc/Statistics.cpp
js/src/gc/Statistics.h
js/src/gc/Tracer.h
js/src/jsapi.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcinlines.h
js/src/proxy/CrossCompartmentWrapper.cpp
js/src/proxy/Proxy.h
js/src/vm/GeckoProfiler.cpp
js/src/vm/MemoryMetrics.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/Shape-inl.h
js/src/vm/UbiNode.cpp
js/xpconnect/src/XPCJSContext.cpp
xpcom/base/CycleCollectedJSContext.h
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -573,40 +573,42 @@ struct UnusedGCThingSizes
     macro(Other, GCHeapUnused, script) \
     macro(Other, GCHeapUnused, lazyScript) \
     macro(Other, GCHeapUnused, shape) \
     macro(Other, GCHeapUnused, baseShape) \
     macro(Other, GCHeapUnused, objectGroup) \
     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)
         dummy()
     {}
 
     UnusedGCThingSizes(UnusedGCThingSizes&& other)
       : FOR_EACH_SIZE(COPY_OTHER_SIZE)
         dummy()
     {}
 
     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");
         }
     }
 
     void addSizes(const UnusedGCThingSizes& other) {
         FOR_EACH_SIZE(ADD_OTHER_SIZE)
     }
@@ -638,16 +640,18 @@ struct ZoneStats
     macro(Other,   GCHeapAdmin, gcHeapArenaAdmin) \
     macro(Other,   GCHeapUsed,  lazyScriptsGCHeap) \
     macro(Other,   MallocHeap,  lazyScriptsMallocHeap) \
     macro(Other,   GCHeapUsed,  jitCodesGCHeap) \
     macro(Other,   GCHeapUsed,  objectGroupsGCHeap) \
     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) \
     macro(Other,   MallocHeap,  shapeTables)
 
     ZoneStats()
       : FOR_EACH_SIZE(ZERO_SIZE)
         unusedGCThings(),
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -27,17 +27,18 @@ using JS::MutableHandle;
 using JS::MutableHandleObject;
 using JS::MutableHandleValue;
 using JS::NativeImpl;
 using JS::ObjectOpResult;
 using JS::PrivateValue;
 using JS::PropertyDescriptor;
 using JS::Value;
 
-class RegExpGuard;
+using RegExpGuard = JS::Rooted<RegExpShared*>;
+
 class JS_FRIEND_API(Wrapper);
 
 /*
  * A proxy is a JSObject with highly customizable behavior. ES6 specifies a
  * single kind of proxy, but the customization mechanisms we use to implement
  * ES6 Proxy objects are also useful wherever an object with weird behavior is
  * wanted. Proxies are used to implement:
  *
--- a/js/public/TraceKind.h
+++ b/js/public/TraceKind.h
@@ -11,16 +11,17 @@
 
 #include "js/TypeDecls.h"
 
 // Forward declarations of all the types a TraceKind can denote.
 namespace js {
 class BaseShape;
 class LazyScript;
 class ObjectGroup;
+class RegExpShared;
 class Shape;
 class Scope;
 namespace jit {
 class JitCode;
 } // namespace jit
 } // namespace js
 
 namespace JS {
@@ -54,23 +55,25 @@ enum class TraceKind
 
     // The kind associated with a nullptr.
     Null = 0x06,
 
     // The following kinds do not have an exposed C++ idiom.
     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
 // directly from the class hierarchy.
 template <typename T>
 struct MapTypeToTraceKind {
     static const JS::TraceKind kind = T::TraceKind;
 };
@@ -83,17 +86,18 @@ struct MapTypeToTraceKind {
     D(JitCode,       js::jit::JitCode,  true) \
     D(LazyScript,    js::LazyScript,    true) \
     D(Scope,         js::Scope,         true) \
     D(Object,        JSObject,          true) \
     D(ObjectGroup,   js::ObjectGroup,   true) \
     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, _) \
     template <> struct MapTypeToTraceKind<type> { \
         static const JS::TraceKind kind = JS::TraceKind::name; \
     };
 JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
 #undef JS_EXPAND_DEF
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -159,16 +159,19 @@ class JS_PUBLIC_API(CallbackTracer) : pu
         onChild(JS::GCCellPtr(*codep, JS::TraceKind::JitCode));
     }
     virtual void onLazyScriptEdge(js::LazyScript** lazyp) {
         onChild(JS::GCCellPtr(*lazyp, JS::TraceKind::LazyScript));
     }
     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.
     virtual void onChild(const JS::GCCellPtr& thing) = 0;
 
     // Access to the tracing context:
     // When tracing with a JS::CallbackTracer, we invoke the callback with the
     // edge location and the type of target. This is useful for operating on
@@ -229,16 +232,17 @@ class JS_PUBLIC_API(CallbackTracer) : pu
     void dispatchToOnEdge(JS::Symbol** symp) { onSymbolEdge(symp); }
     void dispatchToOnEdge(JSScript** scriptp) { onScriptEdge(scriptp); }
     void dispatchToOnEdge(js::Shape** shapep) { onShapeEdge(shapep); }
     void dispatchToOnEdge(js::ObjectGroup** groupp) { onObjectGroupEdge(groupp); }
     void dispatchToOnEdge(js::BaseShape** basep) { onBaseShapeEdge(basep); }
     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); }
 
   protected:
     void setTraceWeakEdges(bool value) {
         traceWeakEdges_ = value;
     }
 
   private:
     friend class AutoTracingName;
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -115,23 +115,28 @@ struct MovingTracer : JS::CallbackTracer
 
     void onObjectEdge(JSObject** objp) override;
     void onShapeEdge(Shape** shapep) override;
     void onStringEdge(JSString** stringp) override;
     void onScriptEdge(JSScript** scriptp) override;
     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()));
     }
 
 #ifdef DEBUG
     TracerKind getTracerKind() const override { return TracerKind::Moving; }
 #endif
+
+  private:
+    template <typename T>
+    void updateEdge(T** thingp);
 };
 
 // Structure for counting how many times objects in a particular group have
 // been tenured during a minor collection.
 struct TenureCount
 {
     ObjectGroup* group;
     int count;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -764,16 +764,18 @@ class GCRuntime
     bool isIncrementalGCAllowed() const { return incrementalAllowed; }
     void disallowIncrementalGC() { incrementalAllowed = false; }
 
     bool isIncrementalGCEnabled() const { return mode == JSGC_MODE_INCREMENTAL && incrementalAllowed; }
     bool isIncrementalGCInProgress() const { return state() != State::NotActive; }
 
     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);
 
     bool triggerGCForTooMuchMalloc() { return triggerGC(JS::gcreason::TOO_MUCH_MALLOC); }
     int32_t getMallocBytes() const { return mallocCounter.bytes(); }
     size_t maxMallocBytesAllocated() const { return mallocCounter.maxBytes(); }
     bool isTooMuchMalloc() const { return mallocCounter.isTooMuchMalloc(); }
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -104,55 +104,57 @@ enum class AllocKind {
     FAT_INLINE_STRING,
     STRING,
     EXTERNAL_STRING,
     FAT_INLINE_ATOM,
     ATOM,
     SYMBOL,
     JITCODE,
     SCOPE,
+    REGEXP_SHARED,
     LIMIT,
     LAST = LIMIT - 1
 };
 
 // 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) \
     FOR_EACH_NONOBJECT_ALLOCKIND(D)
 
 static_assert(int(AllocKind::FIRST) == 0, "Various places depend on AllocKind starting at 0, "
                                           "please audit them carefully!");
 static_assert(int(AllocKind::OBJECT_FIRST) == 0, "Various places depend on AllocKind::OBJECT_FIRST "
@@ -308,16 +310,19 @@ class TenuredCell : public Cell
     }
 
     static MOZ_ALWAYS_INLINE void readBarrier(TenuredCell* thing);
     static MOZ_ALWAYS_INLINE void writeBarrierPre(TenuredCell* thing);
 
     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() {}
 
 #ifdef DEBUG
     inline bool isAligned() const;
 #endif
 };
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -438,16 +438,24 @@ 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>
 JS_PUBLIC_API(void)
 JS::TraceEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name)
 {
     MOZ_ASSERT(thingp);
     if (InternalBarrierMethods<T>::isMarkable(*thingp->unsafeGet()))
         DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name);
 }
 
@@ -554,16 +562,17 @@ js::TraceRootRange(JSTracer* trc, size_t
     }
 }
 
 // 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*);
@@ -870,16 +879,17 @@ js::GCMarker::markAndTraceChildren(T* th
     if (ThingIsPermanentAtomOrWellKnownSymbol(thing))
         return;
     if (mark(thing))
         thing->traceChildren(this);
 }
 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
 // simple patterns of recursion. We traverse trees of these edges immediately,
 // with aggressive, manual inlining, implemented by eagerlyTraceChildren.
 template <typename T>
 void
 js::GCMarker::markAndScan(T* thing)
--- a/js/src/gc/Policy.h
+++ b/js/src/gc/Policy.h
@@ -81,16 +81,17 @@ class JitCode;
     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::SavedFrame*) \
     D(js::Scope*) \
     D(js::ScriptSourceObject*) \
     D(js::Shape*) \
     D(js::SharedArrayBufferObject*) \
     D(js::StructTypeDescr*) \
     D(js::UnownedBaseShape*) \
     D(js::WasmFunctionScope*) \
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -179,16 +179,17 @@ static const PhaseInfo phases[] = {
             { PHASE_SWEEP_MISC, "Sweep Miscellaneous", PHASE_SWEEP_COMPARTMENTS, 29 },
             { PHASE_SWEEP_TYPES, "Sweep type information", PHASE_SWEEP_COMPARTMENTS, 30 },
                 { PHASE_SWEEP_TYPES_BEGIN, "Sweep type tables and compilations", PHASE_SWEEP_TYPES, 31 },
                 { PHASE_SWEEP_TYPES_END, "Free type arena", PHASE_SWEEP_TYPES, 32 },
         { PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP, 33 },
         { PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP, 34 },
         { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP, 35 },
         { PHASE_SWEEP_SCOPE, "Sweep Scope", PHASE_SWEEP, 59 },
+        { PHASE_SWEEP_REGEXP_SHARED, "Sweep RegExpShared", PHASE_SWEEP, 61 },
         { PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP, 36 },
         { PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP, 37 },
         { PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP, 38 },
         { PHASE_DESTROY, "Deallocate", PHASE_SWEEP, 39 },
     { PHASE_COMPACT, "Compact", PHASE_NO_PARENT, 40 },
         { PHASE_COMPACT_MOVE, "Compact Move", PHASE_COMPACT, 41 },
         { PHASE_COMPACT_UPDATE, "Compact Update", PHASE_COMPACT, 42 },
             /* PHASE_MARK_ROOTS */
@@ -206,19 +207,19 @@ static const PhaseInfo phases[] = {
         { PHASE_BUFFER_GRAY_ROOTS, "Buffer Gray Roots", PHASE_MARK_ROOTS, 49 },
         { PHASE_MARK_CCWS, "Mark Cross Compartment Wrappers", PHASE_MARK_ROOTS, 50 },
         { PHASE_MARK_STACK, "Mark C and JS stacks", PHASE_MARK_ROOTS, 51 },
         { PHASE_MARK_RUNTIME_DATA, "Mark Runtime-wide Data", PHASE_MARK_ROOTS, 52 },
         { PHASE_MARK_EMBEDDING, "Mark Embedding", PHASE_MARK_ROOTS, 53 },
         { PHASE_MARK_COMPARTMENTS, "Mark Compartments", PHASE_MARK_ROOTS, 54 },
     { PHASE_PURGE_SHAPE_TABLES, "Purge ShapeTables", PHASE_NO_PARENT, 60 },
 
-    { PHASE_LIMIT, nullptr, PHASE_NO_PARENT, 60 }
+    { PHASE_LIMIT, nullptr, PHASE_NO_PARENT, 61 }
 
-    // Current number of telemetryBuckets is 60. If you insert new phases
+    // Current number of telemetryBuckets is 61. If you insert new phases
     // somewhere, start at that number and count up. Do not change any existing
     // numbers.
 };
 
 static mozilla::EnumeratedArray<Phase, PHASE_LIMIT, ExtraPhaseInfo> phaseExtra;
 
 // Mapping from all nodes with a multi-parented child to a Vector of all
 // multi-parented children and their descendants. (Single-parented children will
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -64,16 +64,17 @@ enum Phase : uint8_t {
     PHASE_SWEEP_MISC,
     PHASE_SWEEP_TYPES,
     PHASE_SWEEP_TYPES_BEGIN,
     PHASE_SWEEP_TYPES_END,
     PHASE_SWEEP_OBJECT,
     PHASE_SWEEP_STRING,
     PHASE_SWEEP_SCRIPT,
     PHASE_SWEEP_SCOPE,
+    PHASE_SWEEP_REGEXP_SHARED,
     PHASE_SWEEP_SHAPE,
     PHASE_SWEEP_JITCODE,
     PHASE_FINALIZE_END,
     PHASE_DESTROY,
     PHASE_COMPACT,
     PHASE_COMPACT_MOVE,
     PHASE_COMPACT_UPDATE,
     PHASE_COMPACT_UPDATE_CELLS,
--- a/js/src/gc/Tracer.h
+++ b/js/src/gc/Tracer.h
@@ -60,16 +60,20 @@ template <typename T>
 void
 TraceEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name);
 
 // Trace through an edge in the live object graph on behalf of tracing.
 template <typename T>
 void
 TraceNullableEdge(JSTracer* trc, WriteBarrieredBase<T>* thingp, const char* name);
 
+template <typename T>
+void
+TraceNullableEdge(JSTracer* trc, ReadBarriered<T>* 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.
 template <typename T>
 void
 TraceRoot(JSTracer* trc, T* thingp, const char* name);
 
 template <typename T>
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6096,29 +6096,29 @@ JS_PUBLIC_API(unsigned)
 JS_GetRegExpFlags(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     RegExpGuard shared(cx);
     if (!RegExpToShared(cx, obj, &shared))
         return false;
-    return shared.re()->getFlags();
+    return shared->getFlags();
 }
 
 JS_PUBLIC_API(JSString*)
 JS_GetRegExpSource(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     RegExpGuard shared(cx);
     if (!RegExpToShared(cx, obj, &shared))
         return nullptr;
-    return shared.re()->getSource();
+    return shared->getSource();
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(bool)
 JS_SetDefaultLocale(JSContext* cx, const char* locale)
 {
     AssertHeapIsIdle();
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -62,17 +62,17 @@ JSCompartment::JSCompartment(Zone* zone,
     firedOnNewGlobalObject(false),
 #endif
     global_(nullptr),
     enterCompartmentDepth(0),
     performanceMonitoring(runtime_),
     data(nullptr),
     allocationMetadataBuilder(nullptr),
     lastAnimationTime(0),
-    regExps(runtime_),
+    regExps(zone),
     globalWriteBarriered(0),
     detachedTypedObjects(0),
     objectMetadataState(ImmediateMetadata()),
     selfHostingScriptSource(nullptr),
     objectMetadataTable(nullptr),
     innerViews(zone, InnerViewTable()),
     lazyArrayBuffers(nullptr),
     wasm(zone),
@@ -223,16 +223,23 @@ JSCompartment::ensureJitCompartmentExist
         jitCompartment_ = nullptr;
         return false;
     }
 
     return true;
 }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
+
+void
+js::DtoaCache::checkCacheAfterMovingGC()
+{
+    MOZ_ASSERT(!s || !IsForwarded(s));
+}
+
 namespace {
 struct CheckGCThingAfterMovingGCFunctor {
     template <class T> void operator()(T* t) { CheckGCThingAfterMovingGC(*t); }
 };
 } // namespace (anonymous)
 
 void
 JSCompartment::checkWrapperMapAfterMovingGC()
@@ -245,17 +252,18 @@ JSCompartment::checkWrapperMapAfterMovin
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
         e.front().mutableKey().applyToWrapped(CheckGCThingAfterMovingGCFunctor());
         e.front().mutableKey().applyToDebugger(CheckGCThingAfterMovingGCFunctor());
 
         WrapperMap::Ptr ptr = crossCompartmentWrappers.lookup(e.front().key());
         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());
     }
 }
-#endif
+
+#endif // JSGC_HASH_TABLE_CHECKS
 
 bool
 JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
                           const js::Value& wrapper)
 {
     MOZ_ASSERT(wrapped.is<JSString*>() == wrapper.isString());
     MOZ_ASSERT_IF(!wrapped.is<JSString*>(), wrapper.isObject());
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -61,17 +61,17 @@ class DtoaCache {
 
     void cache(int base, double d, JSFlatString* s) {
         this->base = base;
         this->d = d;
         this->s = s;
     }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
-    void checkCacheAfterMovingGC() { MOZ_ASSERT(!s || !IsForwarded(s)); }
+    void checkCacheAfterMovingGC();
 #endif
 };
 
 // Cache to speed up the group/shape lookup in ProxyObject::create. A proxy's
 // group/shape is only determined by the Class + proto, so a small cache for
 // this is very effective in practice.
 class NewProxyCache
 {
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1158,17 +1158,17 @@ CastToJSFreeOp(FreeOp* fop)
 extern JS_FRIEND_API(JSFlatString*)
 GetErrorTypeName(JSContext* cx, int16_t exnType);
 
 #ifdef JS_DEBUG
 extern JS_FRIEND_API(unsigned)
 GetEnterCompartmentDepth(JSContext* cx);
 #endif
 
-class RegExpGuard;
+using RegExpGuard = JS::Rooted<RegExpShared*>;
 extern JS_FRIEND_API(bool)
 RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp, RegExpGuard* shared);
 
 /* Implemented in CrossCompartmentWrapper.cpp. */
 typedef enum NukeReferencesToWindow {
     NukeWindowReferences,
     DontNukeWindowReferences
 } NukeReferencesToWindow;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -362,17 +362,22 @@ static const FinalizePhase BackgroundFin
             AllocKind::OBJECT4_BACKGROUND,
             AllocKind::OBJECT8_BACKGROUND,
             AllocKind::OBJECT12_BACKGROUND,
             AllocKind::OBJECT16_BACKGROUND
         }
     },
     {
         gcstats::PHASE_SWEEP_SCOPE, {
-            AllocKind::SCOPE
+            AllocKind::SCOPE,
+        }
+    },
+    {
+        gcstats::PHASE_SWEEP_REGEXP_SHARED, {
+            AllocKind::REGEXP_SHARED,
         }
     },
     {
         gcstats::PHASE_SWEEP_STRING, {
             AllocKind::FAT_INLINE_STRING,
             AllocKind::STRING,
             AllocKind::FAT_INLINE_ATOM,
             AllocKind::ATOM,
@@ -1717,25 +1722,26 @@ static const AllocKind AllocKindsToReloc
     AllocKind::OBJECT8,
     AllocKind::OBJECT8_BACKGROUND,
     AllocKind::OBJECT12,
     AllocKind::OBJECT12_BACKGROUND,
     AllocKind::OBJECT16,
     AllocKind::OBJECT16_BACKGROUND,
     AllocKind::SCRIPT,
     AllocKind::LAZY_SCRIPT,
-    AllocKind::SCOPE,
     AllocKind::SHAPE,
     AllocKind::ACCESSOR_SHAPE,
     AllocKind::BASE_SHAPE,
     AllocKind::FAT_INLINE_STRING,
     AllocKind::STRING,
     AllocKind::EXTERNAL_STRING,
     AllocKind::FAT_INLINE_ATOM,
-    AllocKind::ATOM
+    AllocKind::ATOM,
+    AllocKind::SCOPE,
+    AllocKind::REGEXP_SHARED
 };
 
 Arena*
 ArenaList::removeRemainingArenas(Arena** arenap)
 {
     // This is only ever called to remove arenas that are after the cursor, so
     // we don't need to update it.
 #ifdef DEBUG
@@ -2049,71 +2055,33 @@ GCRuntime::relocateArenas(Zone* zone, JS
             freeCells += arena->countFreeCells();
         MOZ_ASSERT(freeCells < Arena::thingsPerArena(i));
     }
 #endif
 
     return true;
 }
 
-void
-MovingTracer::onObjectEdge(JSObject** objp)
-{
-    JSObject* obj = *objp;
-    if (obj->runtimeFromAnyThread() == runtime() && IsForwarded(obj))
-        *objp = Forwarded(obj);
-}
-
-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);
-}
+template <typename T>
+inline void
+MovingTracer::updateEdge(T** thingp)
+{
+    auto thing = *thingp;
+    if (thing->runtimeFromAnyThread() == runtime() && IsForwarded(thing))
+        *thingp = Forwarded(thing);
+}
+
+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()
 {
     FreeOp* fop = runtimeFromActiveCooperatingThread()->defaultFreeOp();
     discardJitCode(fop);
 }
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -116,16 +116,17 @@ IsNurseryAllocable(AllocKind kind)
         false,     /* AllocKind::FAT_INLINE_STRING */
         false,     /* AllocKind::STRING */
         false,     /* AllocKind::EXTERNAL_STRING */
         false,     /* AllocKind::FAT_INLINE_ATOM */
         false,     /* AllocKind::ATOM */
         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)];
 }
 
 static inline bool
 IsBackgroundFinalized(AllocKind kind)
 {
@@ -154,16 +155,17 @@ IsBackgroundFinalized(AllocKind kind)
         true,      /* AllocKind::FAT_INLINE_STRING */
         true,      /* AllocKind::STRING */
         false,     /* AllocKind::EXTERNAL_STRING */
         true,      /* AllocKind::FAT_INLINE_ATOM */
         true,      /* AllocKind::ATOM */
         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)];
 }
 
 static inline bool
 CanBeFinalizedInBackground(AllocKind kind, const Class* clasp)
 {
@@ -1131,119 +1133,39 @@ class RelocationOverlay
 //                  pointer to old location.
 //
 // MaybeForwarded - used before dereferencing a pointer that may refer to a
 //                  moved GC thing without updating it. For JSObjects this will
 //                  also update the object's shape pointer if it has been moved
 //                  to allow slots to be accessed.
 
 template <typename T>
-struct MightBeForwarded
-{
-    static_assert(mozilla::IsBaseOf<Cell, T>::value,
-                  "T must derive from Cell");
-    static_assert(!mozilla::IsSame<Cell, T>::value && !mozilla::IsSame<TenuredCell, T>::value,
-                  "T must not be Cell or TenuredCell");
-
-    static const bool value = mozilla::IsBaseOf<JSObject, T>::value ||
-                              mozilla::IsBaseOf<Shape, T>::value ||
-                              mozilla::IsBaseOf<BaseShape, T>::value ||
-                              mozilla::IsBaseOf<JSString, T>::value ||
-                              mozilla::IsBaseOf<JSScript, T>::value ||
-                              mozilla::IsBaseOf<js::LazyScript, T>::value ||
-                              mozilla::IsBaseOf<js::Scope, T>::value;
-};
+inline bool IsForwarded(T* t);
+inline bool IsForwarded(const JS::Value& value);
 
 template <typename T>
-inline bool
-IsForwarded(T* t)
-{
-    RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
-    if (!MightBeForwarded<T>::value) {
-        MOZ_ASSERT(!overlay->isForwarded());
-        return false;
-    }
+inline T* Forwarded(T* t);
 
-    return overlay->isForwarded();
-}
-
-struct IsForwardedFunctor : public BoolDefaultAdaptor<Value, false> {
-    template <typename T> 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 <typename T>
-inline T*
-Forwarded(T* t)
-{
-    RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
-    MOZ_ASSERT(overlay->isForwarded());
-    return reinterpret_cast<T*>(overlay->forwardingAddress());
-}
-
-struct ForwardedFunctor : public IdentityDefaultAdaptor<Value> {
-    template <typename T> inline Value operator()(T* t) {
-        return js::gc::RewrapTaggedPointer<Value, T>::wrap(Forwarded(t));
-    }
-};
-
-inline Value
-Forwarded(const JS::Value& value)
-{
-    return DispatchTyped(ForwardedFunctor(), value);
-}
-
-template <typename T>
-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 <typename T>
-inline bool
-IsGCThingValidAfterMovingGC(T* t)
-{
-    return !IsInsideNursery(t) && !RelocationOverlay::isCellForwarded(t);
-}
-
-template <typename T>
-inline void
-CheckGCThingAfterMovingGC(T* t)
-{
-    if (t)
-        MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t));
-}
+inline bool IsGCThingValidAfterMovingGC(T* t);
 
 template <typename T>
-inline void
-CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t)
-{
-    CheckGCThingAfterMovingGC(t.unbarrieredGet());
-}
+inline void CheckGCThingAfterMovingGC(T* t);
 
-struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor<Value> {
-    template <typename T> void operator()(T* t) { CheckGCThingAfterMovingGC(t); }
-};
+template <typename T>
+inline void CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t);
 
-inline void
-CheckValueAfterMovingGC(const JS::Value& value)
-{
-    DispatchTyped(CheckValueAfterMovingGCFunctor(), value);
-}
+inline void CheckValueAfterMovingGC(const JS::Value& value);
 
 #endif // JSGC_HASH_TABLE_CHECKS
 
 #define JS_FOR_EACH_ZEAL_MODE(D)               \
             D(Poke, 1)                         \
             D(Alloc, 2)                        \
             D(FrameGC, 3)                      \
             D(VerifierPre, 4)                  \
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -483,12 +483,120 @@ RelocationOverlay::forwardTo(Cell* cell)
     static_assert(offsetof(RelocationOverlay, magic_) == offsetof(JSObject, group_) &&
                   offsetof(RelocationOverlay, magic_) == offsetof(js::Shape, base_) &&
                   offsetof(RelocationOverlay, magic_) == offsetof(JSString, d.u1.flags),
                   "RelocationOverlay::magic_ is in the wrong location");
     magic_ = Relocated;
     newLocation_ = cell;
 }
 
+template <typename T>
+struct MightBeForwarded
+{
+    static_assert(mozilla::IsBaseOf<Cell, T>::value,
+                  "T must derive from Cell");
+    static_assert(!mozilla::IsSame<Cell, T>::value && !mozilla::IsSame<TenuredCell, T>::value,
+                  "T must not be Cell or TenuredCell");
+
+    static const bool value = mozilla::IsBaseOf<JSObject, T>::value ||
+                              mozilla::IsBaseOf<Shape, T>::value ||
+                              mozilla::IsBaseOf<BaseShape, T>::value ||
+                              mozilla::IsBaseOf<JSString, T>::value ||
+                              mozilla::IsBaseOf<JSScript, T>::value ||
+                              mozilla::IsBaseOf<js::LazyScript, T>::value ||
+                              mozilla::IsBaseOf<js::Scope, T>::value ||
+                              mozilla::IsBaseOf<js::RegExpShared, T>::value;
+};
+
+template <typename T>
+inline bool
+IsForwarded(T* t)
+{
+    RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
+    if (!MightBeForwarded<T>::value) {
+        MOZ_ASSERT(!overlay->isForwarded());
+        return false;
+    }
+
+    return overlay->isForwarded();
+}
+
+struct IsForwardedFunctor : public BoolDefaultAdaptor<Value, false> {
+    template <typename T> bool operator()(T* t) { return IsForwarded(t); }
+};
+
+inline bool
+IsForwarded(const JS::Value& value)
+{
+    return DispatchTyped(IsForwardedFunctor(), value);
+}
+
+template <typename T>
+inline T*
+Forwarded(T* t)
+{
+    RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
+    MOZ_ASSERT(overlay->isForwarded());
+    return reinterpret_cast<T*>(overlay->forwardingAddress());
+}
+
+struct ForwardedFunctor : public IdentityDefaultAdaptor<Value> {
+    template <typename T> inline Value operator()(T* t) {
+        return js::gc::RewrapTaggedPointer<Value, T>::wrap(Forwarded(t));
+    }
+};
+
+inline Value
+Forwarded(const JS::Value& value)
+{
+    return DispatchTyped(ForwardedFunctor(), value);
+}
+
+template <typename T>
+inline T
+MaybeForwarded(T t)
+{
+    if (IsForwarded(t))
+        t = Forwarded(t);
+    MakeAccessibleAfterMovingGC(t);
+    return t;
+}
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+
+template <typename T>
+inline bool
+IsGCThingValidAfterMovingGC(T* t)
+{
+    return !IsInsideNursery(t) && !RelocationOverlay::isCellForwarded(t);
+}
+
+template <typename T>
+inline void
+CheckGCThingAfterMovingGC(T* t)
+{
+    if (t)
+        MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t));
+}
+
+template <typename T>
+inline void
+CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t)
+{
+    CheckGCThingAfterMovingGC(t.unbarrieredGet());
+}
+
+struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor<Value> {
+    template <typename T> 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 */
 
 #endif /* jsgcinlines_h */
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -455,25 +455,24 @@ CrossCompartmentWrapper::fun_toString(JS
     if (!cx->compartment()->wrap(cx, &str))
         return nullptr;
     return str;
 }
 
 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();
     cx->markAtom(re->getSource());
     return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), g);
 }
 
 bool
 CrossCompartmentWrapper::boxedValue_unbox(JSContext* cx, HandleObject wrapper, MutableHandleValue vp) const
 {
     PIERCE(cx, wrapper,
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -8,18 +8,16 @@
 #define proxy_Proxy_h
 
 #include "NamespaceImports.h"
 
 #include "js/Class.h"
 
 namespace js {
 
-class RegExpGuard;
-
 /*
  * Dispatch point for handlers that executes the appropriate C++ or scripted traps.
  *
  * Important: All proxy methods need either (a) an AutoEnterPolicy in their
  * Proxy::foo entry point below or (b) an override in SecurityWrapper. See bug
  * 945826 comment 0.
  */
 class Proxy
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -14,16 +14,18 @@
 
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineJIT.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitFrameIterator.h"
 #include "jit/JitFrames.h"
 #include "vm/StringBuffer.h"
 
+#include "jsgcinlines.h"
+
 using namespace js;
 
 using mozilla::DebugOnly;
 
 GeckoProfiler::GeckoProfiler(JSRuntime* rt)
   : rt(rt),
     strings(mutexid::GeckoProfilerStrings),
     stack_(nullptr),
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -593,16 +593,23 @@ StatsCellCallback(JSRuntime* rt, void* d
 
       case JS::TraceKind::Scope: {
         Scope* scope = static_cast<Scope*>(thing);
         zStats->scopesGCHeap += thingSize;
         zStats->scopesMallocHeap += scope->sizeOfExcludingThis(rtStats->mallocSizeOf_);
         break;
       }
 
+      case JS::TraceKind::RegExpShared: {
+        auto regexp = static_cast<RegExpShared*>(thing);
+        zStats->regExpSharedsGCHeap += thingSize;
+        zStats->regExpSharedsMallocHeap += regexp->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        break;
+      }
+
       default:
         MOZ_CRASH("invalid traceKind in StatsCellCallback");
     }
 
     // Yes, this is a subtraction:  see StatsArenaCallback() for details.
     zStats->unusedGCThings.addToKind(traceKind, -thingSize);
 }
 
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/RegExpObject.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 
+#include "jshashutil.h"
 #include "jsstr.h"
 #ifdef DEBUG
 #include "jsutil.h"
 #endif
 
 #include "builtin/RegExp.h"
 #include "frontend/TokenStream.h"
 #ifdef DEBUG
@@ -114,35 +115,21 @@ VectorMatchPairs::allocOrExpandArray(siz
 
     pairs_ = &vec_[0];
     pairCount_ = pairCount;
     return true;
 }
 
 /* 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<RegExpObject*> 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;
     }
 
     return createShared(cx, regexp, g);
 }
 
 /* static */ bool
 RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlag* mask)
@@ -169,36 +156,42 @@ RegExpObject::isOriginalFlagGetter(JSNat
   }
 
   return false;
 }
 
 /* static */ void
 RegExpObject::trace(JSTracer* trc, JSObject* obj)
 {
-    RegExpShared* shared = obj->as<RegExpObject>().maybeShared();
-    if (!shared)
-        return;
+    obj->as<RegExpObject>().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, CurrentThreadIsHeapBusy() is true, but the
     //      tracer might not be a marking tracer.
     //   2. When a write barrier executes, IsMarkingTracer is true, but
     //      CurrentThreadIsHeapBusy() will be false.
-    if (JS::CurrentThreadIsHeapCollecting() &&
-        trc->isMarkingTracer() &&
-        !obj->asTenured().zone()->isPreservingCode())
-    {
-        obj->as<RegExpObject>().NativeObject::setPrivate(nullptr);
-    } else {
-        shared->trace(trc);
-    }
+
+    return JS::CurrentThreadIsHeapCollecting() && 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*
 CreateRegExpPrototype(JSContext* cx, JSProtoKey key)
 {
     return GlobalObject::createBlankPrototype(cx, cx->global(), &RegExpObject::protoClass_);
 }
 
@@ -277,17 +270,17 @@ RegExpObject::create(JSContext* cx, Hand
     regexp->initAndZeroLastIndex(source, flags, cx);
 
     return regexp;
 }
 
 /* static */ bool
 RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g)
 {
-    MOZ_ASSERT(!regexp->maybeShared());
+    MOZ_ASSERT(!regexp->hasShared());
     if (!cx->compartment()->regExps.get(cx, regexp->getSource(), regexp->getFlags(), g))
         return false;
 
     regexp->setShared(**g);
     return true;
 }
 
 Shape*
@@ -893,17 +886,17 @@ RegExpShared::dumpBytecode(JSContext* cx
 /* static */ bool
 RegExpObject::dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
                            bool match_only, HandleLinearString input)
 {
     RegExpGuard g(cx);
     if (!getShared(cx, regexp, &g))
         return false;
 
-    return g.re()->dumpBytecode(cx, match_only, input);
+    return g->dumpBytecode(cx, match_only, input);
 }
 #endif
 
 template <typename CharT>
 static MOZ_ALWAYS_INLINE bool
 IsRegExpMetaChar(CharT ch)
 {
     switch (ch) {
@@ -942,57 +935,42 @@ js::StringHasRegExpMetaChars(JSLinearStr
         return HasRegExpMetaChars(str->latin1Chars(nogc), str->length());
 
     return HasRegExpMetaChars(str->twoByteChars(nogc), str->length());
 }
 
 /* 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)
 {}
 
 RegExpShared::~RegExpShared()
 {
     for (size_t i = 0; i < tables.length(); i++)
         js_delete(tables[i]);
 }
 
 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)
         TraceNullableEdge(trc, &comp.jitCode, "RegExpShared code");
 }
 
-bool
-RegExpShared::isMarkedGray() const
+void
+RegExpShared::discardJitCode()
 {
-    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()
-{
-    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
 RegExpShared::compile(JSContext* cx, HandleLinearString input,
                       CompilationMode mode, ForceByteCodeEnum force)
 {
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
     AutoTraceLog logCompile(logger, TraceLogger_IrregexpCompile);
@@ -1179,62 +1157,55 @@ RegExpShared::execute(JSContext* cx, Han
     }
 
     if (result == RegExpRunStatus_Success && matches)
         matches->checkAgainst(length);
     return result;
 }
 
 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];
         if (compilation.byteCode)
             n += mallocSizeOf(compilation.byteCode);
     }
 
     n += tables.sizeOfExcludingThis(mallocSizeOf);
     for (size_t i = 0; i < tables.length(); i++)
         n += mallocSizeOf(tables[i]);
 
     return n;
 }
 
 /* RegExpCompartment */
 
-RegExpCompartment::RegExpCompartment(JSRuntime* rt)
-  : set_(rt),
+RegExpCompartment::RegExpCompartment(Zone* zone)
+  : set_(zone, Set(zone->runtimeFromActiveCooperatingThread())),
     matchResultTemplateObject_(nullptr),
     optimizableRegExpPrototypeShape_(nullptr),
     optimizableRegExpInstanceShape_(nullptr)
 {}
 
 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*
 RegExpCompartment::createMatchResultTemplateObject(JSContext* cx)
 {
     MOZ_ASSERT(!matchResultTemplateObject_);
 
     /* Create template array object */
     RootedArrayObject templateObject(cx, NewDenseUnallocatedArray(cx, RegExpObject::MaxPairCount,
-                                     nullptr, TenuredObject));
+                                                                  nullptr, TenuredObject));
     if (!templateObject)
         return matchResultTemplateObject_; // = nullptr
 
     // Create a new group for the template.
     Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto);
     if (!group)
         return matchResultTemplateObject_; // = nullptr
@@ -1280,69 +1251,19 @@ RegExpCompartment::init(JSContext* cx)
         if (cx)
             ReportOutOfMemory(cx);
         return false;
     }
 
     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(JS::CurrentThreadIsHeapMajorCollecting());
-    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_))
     {
         matchResultTemplateObject_.set(nullptr);
     }
 
     if (optimizableRegExpPrototypeShape_ &&
         IsAboutToBeFinalized(&optimizableRegExpPrototypeShape_))
@@ -1355,63 +1276,51 @@ RegExpCompartment::sweep(JSRuntime* rt)
     {
         optimizableRegExpInstanceShape_.set(nullptr);
     }
 }
 
 bool
 RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g)
 {
-    Key key(source, flags);
-    Set::AddPtr p = set_.lookupForAdd(key);
+    DependentAddPtr<Set> 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<RegExpShared> shared(cx->new_<RegExpShared>(source, flags));
+    auto shared = Allocate<RegExpShared>(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;
 }
 
 bool
 RegExpCompartment::get(JSContext* cx, HandleAtom atom, JSString* opt, RegExpGuard* g)
 {
     RegExpFlag flags = RegExpFlag(0);
     if (opt && !ParseRegExpFlags(cx, opt, &flags))
         return false;
 
     return get(cx, atom, flags, g);
 }
 
 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 */
 
 JSObject*
 js::CloneRegExpObject(JSContext* cx, JSObject* obj_)
 {
     Rooted<RegExpObject*> regex(cx, &obj_->as<RegExpObject>());
@@ -1429,17 +1338,17 @@ js::CloneRegExpObject(JSContext* cx, JSO
 
     Rooted<JSAtom*> source(cx, regex->getSource());
 
     RegExpGuard g(cx);
     if (!RegExpObject::getShared(cx, regex, &g))
         return nullptr;
 
     clone->initAndZeroLastIndex(source, g->getFlags(), cx);
-    clone->setShared(*g.re());
+    clone->setShared(*g);
 
     return clone;
 }
 
 static bool
 HandleRegExpFlag(RegExpFlag flag, RegExpFlag* flags)
 {
     if (*flags & flag)
@@ -1563,8 +1472,15 @@ js::CloneScriptRegExpObject(JSContext* c
     return RegExpObject::create(cx, source, reobj.getFlags(), nullptr, cx->tempLifoAlloc());
 }
 
 JS_FRIEND_API(bool)
 js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj, js::RegExpGuard* g)
 {
     return RegExpToShared(cx, obj, g);
 }
+
+JS::ubi::Node::Size
+JS::ubi::Concrete<RegExpShared>::size(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    return js::gc::Arena::thingSize(gc::AllocKind::REGEXP_SHARED) +
+        get().sizeOfExcludingThis(mallocSizeOf);
+}
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -41,17 +41,17 @@ namespace js {
 
 struct MatchPair;
 class MatchPairs;
 class RegExpShared;
 class RegExpStatics;
 
 namespace frontend { class TokenStream; }
 
-enum RegExpFlag
+enum RegExpFlag : uint8_t
 {
     IgnoreCaseFlag  = 0x01,
     GlobalFlag      = 0x02,
     MultilineFlag   = 0x04,
     StickyFlag      = 0x08,
     UnicodeFlag     = 0x10,
 
     NoFlags         = 0x00,
@@ -87,17 +87,17 @@ CloneRegExpObject(JSContext* cx, JSObjec
  *
  * During a GC, RegExpShared instances are marked and swept like GC things.
  * Usually, RegExpObjects clear their pointers to their RegExpShareds rather
  * than explicitly tracing them, so that the RegExpShared and any jitcode can
  * be reclaimed quicker. However, the RegExpShareds are traced through by
  * 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 {
         Normal,
         MatchOnly
     };
 
     enum ForceByteCodeEnum {
@@ -108,49 +108,50 @@ class RegExpShared
   private:
     friend class RegExpCompartment;
     friend class RegExpStatics;
 
     typedef frontend::TokenStream TokenStream;
 
     struct RegExpCompilation
     {
-        HeapPtr<jit::JitCode*> jitCode;
+        ReadBarriered<jit::JitCode*> jitCode;
         uint8_t* byteCode;
 
         RegExpCompilation() : byteCode(nullptr) {}
         ~RegExpCompilation() { js_free(byteCode); }
 
         bool compiled(ForceByteCodeEnum force = DontForceByteCode) const {
             return byteCode || (force == DontForceByteCode && jitCode);
         }
     };
 
     /* Source to the RegExp, for lazy compilation. */
-    HeapPtr<JSAtom*>     source;
+    HeapPtr<JSAtom*>   source;
 
     RegExpFlag         flags;
+    bool               canStringMatch;
     size_t             parenCount;
-    bool               canStringMatch;
-    bool               marked_;
 
     RegExpCompilation  compilationArray[4];
 
     static int CompilationIndex(CompilationMode mode, bool latin1) {
         switch (mode) {
           case Normal:    return latin1 ? 0 : 1;
           case MatchOnly: return latin1 ? 2 : 3;
         }
         MOZ_CRASH();
     }
 
     // Tables referenced by JIT code.
     Vector<uint8_t*, 0, SystemAllocPolicy> 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,
                  CompilationMode mode, ForceByteCodeEnum force);
 
     bool compileIfNecessary(JSContext* cx, HandleLinearString input,
                             CompilationMode mode, ForceByteCodeEnum force);
 
@@ -158,17 +159,16 @@ class RegExpShared
         return compilationArray[CompilationIndex(mode, latin1)];
     }
 
     RegExpCompilation& compilation(CompilationMode mode, bool latin1) {
         return compilationArray[CompilationIndex(mode, latin1)];
     }
 
   public:
-    RegExpShared(JSAtom* source, RegExpFlag flags);
     ~RegExpShared();
 
     // Execute this RegExp on input starting from searchIndex, filling in
     // matches if specified and otherwise only determining if there is a match.
     RegExpRunStatus execute(JSContext* cx, HandleLinearString input, size_t searchIndex,
                             MatchPairs* matches, size_t* endIndex);
 
     // Register a table with this RegExpShared, and take ownership.
@@ -198,26 +198,19 @@ class RegExpShared
                     ForceByteCodeEnum force = DontForceByteCode) const {
         return compilation(mode, latin1).compiled(force);
     }
     bool isCompiled() const {
         return isCompiled(Normal, true) || isCompiled(Normal, false)
             || 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);
     }
 
     static size_t offsetOfFlags() {
         return offsetof(RegExpShared, flags);
     }
 
@@ -231,99 +224,53 @@ class RegExpShared
              + offsetof(RegExpCompilation, jitCode);
     }
     static size_t offsetOfTwoByteJitCode(CompilationMode mode) {
         return offsetof(RegExpShared, compilationArray)
              + (CompilationIndex(mode, false) * sizeof(RegExpCompilation))
              + offsetof(RegExpCompilation, jitCode);
     }
 
-    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(JSContext* cx)
-      : CustomAutoRooter(cx), re_(nullptr)
-    {}
-
-    RegExpGuard(JSContext* 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 {
         JSAtom* atom;
         uint16_t flag;
 
         Key() {}
         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<RegExpShared*>& shared)
+          : atom(shared.unbarrieredGet()->getSource()),
+            flag(shared.unbarrieredGet()->getFlags())
         { }
 
         typedef Key Lookup;
         static HashNumber hash(const Lookup& l) {
             return DefaultHasher<JSAtom*>::hash(l.atom) ^ (l.flag << 1);
         }
         static bool match(Key l, Key r) {
             return l.atom == r.atom && l.flag == r.flag;
         }
     };
 
     /*
      * 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<RegExpShared*, Key, RuntimeAllocPolicy> Set;
-    Set set_;
+    using Set = GCHashSet<ReadBarriered<RegExpShared*>, Key, RuntimeAllocPolicy>;
+    JS::WeakCache<Set> set_;
 
     /*
      * This is the template object where the result of re.exec() is based on,
      * if there is a result. This is used in CreateRegExpMatchResult to set
      * the input/index properties faster.
      */
     ReadBarriered<ArrayObject*> matchResultTemplateObject_;
 
@@ -346,17 +293,17 @@ class RegExpCompartment
      *   * lastProperty is lastIndex
      *   * prototype is RegExp.prototype
      */
     ReadBarriered<Shape*> optimizableRegExpInstanceShape_;
 
     ArrayObject* createMatchResultTemplateObject(JSContext* cx);
 
   public:
-    explicit RegExpCompartment(JSRuntime* rt);
+    explicit RegExpCompartment(Zone* zone);
     ~RegExpCompartment();
 
     bool init(JSContext* cx);
     void sweep(JSRuntime* rt);
 
     bool empty() { return set_.empty(); }
 
     bool get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g);
@@ -481,22 +428,27 @@ class RegExpObject : public NativeObject
     bool sticky() const     { return getFlags() & StickyFlag; }
     bool unicode() const    { return getFlags() & UnicodeFlag; }
 
     static bool isOriginalFlagGetter(JSNative native, RegExpFlag* mask);
 
     static MOZ_MUST_USE bool getShared(JSContext* cx, Handle<RegExpObject*> 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);
 
     // NOTE: This method is *only* safe to call on RegExps that haven't been
     //       exposed to script, because it requires that the "lastIndex"
     //       property be writable.
     void initAndZeroLastIndex(HandleAtom source, RegExpFlag flags, JSContext* cx);
 
@@ -507,18 +459,20 @@ class RegExpObject : public NativeObject
 
   private:
     /*
      * Precondition: the syntax for |source| has already been validated.
      * Side effect: sets the private field.
      */
     static MOZ_MUST_USE bool createShared(JSContext* cx, Handle<RegExpObject*> regexp,
                                           RegExpGuard* g);
-    RegExpShared* maybeShared() const {
-        return static_cast<RegExpShared*>(NativeObject::getPrivate(PRIVATE_SLOT));
+
+    ReadBarriered<RegExpShared*>& sharedRef() {
+        auto& ref = NativeObject::privateRef(PRIVATE_SLOT);
+        return reinterpret_cast<ReadBarriered<RegExpShared*>&>(ref);
     }
 
     /* Call setShared in preference to setPrivate. */
     void setPrivate(void* priv) = delete;
 };
 
 /*
  * Parse regexp flags. Report an error and return false if an invalid
@@ -554,9 +508,34 @@ template <typename CharT>
 extern bool
 HasRegExpMetaChars(const CharT* chars, size_t length);
 
 extern bool
 StringHasRegExpMetaChars(JSLinearString* str);
 
 } /* namespace js */
 
+namespace JS {
+namespace ubi {
+
+template <>
+class Concrete<js::RegExpShared> : TracerConcrete<js::RegExpShared>
+{
+  protected:
+    explicit Concrete(js::RegExpShared* ptr) : TracerConcrete<js::RegExpShared>(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 */
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -14,16 +14,17 @@
 #include "jsobj.h"
 
 #include "gc/Allocator.h"
 #include "vm/Interpreter.h"
 #include "vm/TypedArrayObject.h"
 
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
+#include "jsgcinlines.h"
 
 namespace js {
 
 inline
 AutoKeepShapeTables::AutoKeepShapeTables(JSContext* cx)
   : cx_(cx),
     prev_(cx->zone()->keepShapeTables())
 {
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -307,16 +307,17 @@ TracerConcrete<Referent>::zone() const
     return get().zoneFromAnyThread();
 }
 
 template JS::Zone* TracerConcrete<JSScript>::zone() const;
 template JS::Zone* TracerConcrete<js::LazyScript>::zone() const;
 template JS::Zone* TracerConcrete<js::Shape>::zone() const;
 template JS::Zone* TracerConcrete<js::BaseShape>::zone() const;
 template JS::Zone* TracerConcrete<js::ObjectGroup>::zone() const;
+template JS::Zone* TracerConcrete<js::RegExpShared>::zone() const;
 template JS::Zone* TracerConcrete<js::Scope>::zone() const;
 template JS::Zone* TracerConcrete<JS::Symbol>::zone() const;
 template JS::Zone* TracerConcrete<JSString>::zone() const;
 
 template<typename Referent>
 UniquePtr<EdgeRange>
 TracerConcrete<Referent>::edges(JSContext* cx, bool wantNames) const {
     UniquePtr<SimpleEdgeRange, JS::DeletePolicy<SimpleEdgeRange>> range(js_new<SimpleEdgeRange>());
@@ -329,16 +330,17 @@ TracerConcrete<Referent>::edges(JSContex
     return UniquePtr<EdgeRange>(range.release());
 }
 
 template UniquePtr<EdgeRange> TracerConcrete<JSScript>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::LazyScript>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::Shape>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::BaseShape>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::ObjectGroup>::edges(JSContext* cx, bool wantNames) const;
+template UniquePtr<EdgeRange> TracerConcrete<js::RegExpShared>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::Scope>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<JS::Symbol>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<JSString>::edges(JSContext* cx, bool wantNames) const;
 
 template<typename Referent>
 JSCompartment*
 TracerConcreteWithCompartment<Referent>::compartment() const
 {
@@ -393,16 +395,17 @@ Concrete<JSObject>::jsObjectConstructorN
 const char16_t Concrete<JS::Symbol>::concreteTypeName[] = u"JS::Symbol";
 const char16_t Concrete<JSScript>::concreteTypeName[] = u"JSScript";
 const char16_t Concrete<js::LazyScript>::concreteTypeName[] = u"js::LazyScript";
 const char16_t Concrete<js::jit::JitCode>::concreteTypeName[] = u"js::jit::JitCode";
 const char16_t Concrete<js::Shape>::concreteTypeName[] = u"js::Shape";
 const char16_t Concrete<js::BaseShape>::concreteTypeName[] = u"js::BaseShape";
 const char16_t Concrete<js::ObjectGroup>::concreteTypeName[] = u"js::ObjectGroup";
 const char16_t Concrete<js::Scope>::concreteTypeName[] = u"js::Scope";
+const char16_t Concrete<js::RegExpShared>::concreteTypeName[] = u"js::RegExpShared";
 
 namespace JS {
 namespace ubi {
 
 RootList::RootList(JSContext* cx, Maybe<AutoCheckCannotGC>& noGC, bool wantNames /* = false */)
   : noGC(noGC),
     cx(cx),
     edges(),
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -1939,16 +1939,24 @@ ReportZoneStats(const JS::ZoneStats& zSt
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("scopes/gc-heap"),
         zStats.scopesGCHeap,
         "Scope information for scripts.");
 
     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("scopes/malloc-heap"),
         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.");
 
     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("baseline/optimized-stubs"),
         zStats.baselineStubsOptimized,
         "The Baseline JIT's optimized IC stubs (excluding code).");
 
@@ -2973,16 +2981,20 @@ JSReporter::CollectReports(WindowPaths* 
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/lazy-scripts"),
         KIND_OTHER, rtStats.zTotals.unusedGCThings.lazyScript,
         "Unused lazy script cells within non-empty arenas.");
 
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/jitcode"),
         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'.");
 
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"),
         KIND_OTHER, rtStats.zTotals.gcHeapArenaAdmin,
         "The same as 'js-main-runtime/zones/gc-heap-arena-admin'.");
 
@@ -3024,16 +3036,20 @@ JSReporter::CollectReports(WindowPaths* 
     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/lazy-scripts"),
         KIND_OTHER, rtStats.zTotals.lazyScriptsGCHeap,
         "Used lazy script cells.");
 
     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/jitcode"),
         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.
 
     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/runtime"),
         KIND_HEAP, xpcJSRuntimeSize,
         "The XPConnect runtime.");
 
--- a/xpcom/base/CycleCollectedJSContext.h
+++ b/xpcom/base/CycleCollectedJSContext.h
@@ -478,17 +478,20 @@ private:
   EnvironmentPreparer mEnvironmentPreparer;
 };
 
 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
 GetBuildId(JS::BuildIdCharVector* aBuildID);
 
 } // namespace mozilla
 
 #endif // mozilla_CycleCollectedJSContext_h__