Backed out changesets e66f564d9749 and 0380d914ad39 (bug 1345177) for rooting hazards
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 27 Mar 2017 11:09:54 +0100
changeset 400378 bf0a094026f7d736e56a5a1ea042fe8e531aabce
parent 400377 854b5f25ff6732613c1b51ed5833654bd1b5dc69
child 400379 ce25d866bd6510461df5cd3910cccf7723423b59
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)
bugs1345177
milestone55.0a1
backs oute66f564d9749c62a6400f1d2c5ba9654073d7684
0380d914ad395fca03f342ab911495a1284f1986
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
Backed out changesets e66f564d9749 and 0380d914ad39 (bug 1345177) for rooting hazards
js/ipc/WrapperOwner.cpp
js/ipc/WrapperOwner.h
js/public/MemoryMetrics.h
js/public/Proxy.h
js/public/TraceKind.h
js/public/TracingAPI.h
js/src/builtin/RegExp.cpp
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/jswrapper.h
js/src/proxy/BaseProxyHandler.cpp
js/src/proxy/CrossCompartmentWrapper.cpp
js/src/proxy/DeadObjectProxy.cpp
js/src/proxy/DeadObjectProxy.h
js/src/proxy/Proxy.cpp
js/src/proxy/Proxy.h
js/src/proxy/ScriptedProxyHandler.cpp
js/src/proxy/ScriptedProxyHandler.h
js/src/proxy/SecurityWrapper.cpp
js/src/proxy/Wrapper.cpp
js/src/vm/GeckoProfiler.cpp
js/src/vm/MemoryMetrics.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/RegExpStatics.cpp
js/src/vm/Shape-inl.h
js/src/vm/StructuredClone.cpp
js/src/vm/UbiNode.cpp
js/xpconnect/src/XPCJSContext.cpp
xpcom/base/CycleCollectedJSContext.h
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -124,18 +124,17 @@ class CPOWProxyHandler : public BaseProx
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
                                               AutoIdVector& props) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy,
                              MutableHandleValue v, bool* bp) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject obj, js::ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject obj,
                          IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
-    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
-                                 MutableHandle<RegExpShared*> shared) const override;
+    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
     virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
     virtual void objectMoved(JSObject* proxy, const JSObject* old) const override;
     virtual bool isCallable(JSObject* obj) const override;
     virtual bool isConstructor(JSObject* obj) const override;
     virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override;
     virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
                                         MutableHandleObject protop) const override;
 
@@ -850,24 +849,23 @@ WrapperOwner::getPrototypeIfOrdinary(JSC
         return false;
 
     objp.set(fromObjectOrNullVariant(cx, val));
 
     return true;
 }
 
 bool
-CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy,
-                                  MutableHandle<RegExpShared*> shared) const
+CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
 {
-    FORWARD(regexp_toShared, (cx, proxy, shared));
+    FORWARD(regexp_toShared, (cx, proxy, g));
 }
 
 bool
-WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandle<RegExpShared*> shared)
+WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
 {
     ObjectId objId = idOf(proxy);
 
     ReturnStatus status;
     nsString source;
     unsigned flags = 0;
     if (!SendRegExpToShared(objId, &status, &source, &flags))
         return ipcfail(cx);
@@ -877,17 +875,17 @@ WrapperOwner::regexp_toShared(JSContext*
     if (!ok(cx, status))
         return false;
 
     RootedObject regexp(cx);
     regexp = JS_NewUCRegExpObject(cx, source.get(), source.Length(), flags);
     if (!regexp)
         return false;
 
-    return js::RegExpToSharedNonInline(cx, regexp, shared);
+    return js::RegExpToSharedNonInline(cx, regexp, g);
 }
 
 void
 CPOWProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const
 {
     AuxCPOWData* aux = AuxCPOWDataOf(proxy);
 
     OwnerOf(proxy)->drop(proxy);
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -55,18 +55,17 @@ class WrapperOwner : public virtual Java
     bool hasInstance(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool* bp);
     bool getBuiltinClass(JSContext* cx, JS::HandleObject proxy, js::ESClass* cls);
     bool isArray(JSContext* cx, JS::HandleObject proxy, JS::IsArrayAnswer* answer);
     const char* className(JSContext* cx, JS::HandleObject proxy);
     bool getPrototype(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleObject protop);
     bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject proxy, bool* isOrdinary,
                                 JS::MutableHandleObject protop);
 
-    bool regexp_toShared(JSContext* cx, JS::HandleObject proxy,
-                         js::MutableHandle<js::RegExpShared*> shared);
+    bool regexp_toShared(JSContext* cx, JS::HandleObject proxy, js::RegExpGuard* g);
 
     nsresult instanceOf(JSObject* obj, const nsID* id, bool* bp);
 
     bool toString(JSContext* cx, JS::HandleObject callee, JS::CallArgs& args);
     bool DOMQI(JSContext* cx, JS::HandleObject callee, JS::CallArgs& args);
 
     /*
      * Check that |obj| is a DOM wrapper whose prototype chain contains
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -573,42 +573,40 @@ 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, regExpShared)
+    macro(Other, GCHeapUnused, scope)
 
     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::RegExpShared: regExpShared += 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;
           default:
             MOZ_CRASH("Bad trace kind for UnusedGCThingSizes");
         }
     }
 
     void addSizes(const UnusedGCThingSizes& other) {
         FOR_EACH_SIZE(ADD_OTHER_SIZE)
     }
@@ -640,18 +638,16 @@ 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,18 +27,17 @@ 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 RegExpShared;
-
+class RegExpGuard;
 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:
  *
@@ -326,18 +325,17 @@ class JS_FRIEND_API(BaseProxyHandler)
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy,
                                  ESClass* cls) const;
     virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
     virtual const char* className(JSContext* cx, HandleObject proxy) const;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const;
-    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
-                                 MutableHandle<js::RegExpShared*> shared) const;
+    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
     virtual void trace(JSTracer* trc, JSObject* proxy) const;
     virtual void finalize(JSFreeOp* fop, JSObject* proxy) const;
     virtual void objectMoved(JSObject* proxy, const JSObject* old) const;
 
     // Allow proxies, wrappers in particular, to specify callability at runtime.
     // Note: These do not take const JSObject*, but they do in spirit.
     //       We are not prepared to do this, as there's little const correctness
--- a/js/public/TraceKind.h
+++ b/js/public/TraceKind.h
@@ -11,17 +11,16 @@
 
 #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 {
@@ -55,25 +54,23 @@ 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,
-    RegExpShared = 0x4F
+    Scope = 0x3F
 };
 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;
 };
@@ -86,18 +83,17 @@ 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(RegExpShared,  js::RegExpShared,  true)
+    D(Symbol,        JS::Symbol,        false)
 
 // 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,19 +159,16 @@ 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
@@ -232,17 +229,16 @@ 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/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -139,17 +139,17 @@ ExecuteRegExpImpl(JSContext* cx, RegExpS
 }
 
 /* Legacy ExecuteRegExp behavior is baked into the JSAPI. */
 bool
 js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*> reobj,
                         HandleLinearString input, size_t* lastIndex, bool test,
                         MutableHandleValue rval)
 {
-    RootedRegExpShared shared(cx);
+    RegExpGuard shared(cx);
     if (!RegExpObject::getShared(cx, reobj, &shared))
         return false;
 
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
 
     RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex,
                                                &matches, nullptr);
     if (status == RegExpRunStatus_Error)
@@ -227,17 +227,17 @@ RegExpInitializeIgnoringLastIndex(JSCont
 
         /* Step 5. */
         if (!ParseRegExpFlags(cx, flagStr, &flags))
             return false;
     }
 
     if (sharedUse == UseRegExpShared) {
         /* Steps 7-8. */
-        RootedRegExpShared re(cx);
+        RegExpGuard re(cx);
         if (!cx->compartment()->regExps.get(cx, pattern, flags, &re))
             return false;
 
         /* Steps 9-12. */
         obj->initIgnoringLastIndex(pattern, flags);
 
         obj->setShared(*re);
     } else {
@@ -333,17 +333,17 @@ regexp_compile_impl(JSContext* cx, const
         // don't assume |patternObj.is<RegExpObject>()|.  For the same reason,
         // don't reuse the RegExpShared below.
         RootedObject patternObj(cx, &patternValue.toObject());
 
         RootedAtom sourceAtom(cx);
         RegExpFlag flags;
         {
             // Step 3b.
-            RootedRegExpShared g(cx);
+            RegExpGuard g(cx);
             if (!RegExpToShared(cx, patternObj, &g))
                 return false;
 
             sourceAtom = g->getSource();
             flags = g->getFlags();
         }
 
         // Step 5, minus lastIndex zeroing.
@@ -428,17 +428,17 @@ js::regexp_construct(JSContext* cx, unsi
         // don't assume |patternObj.is<RegExpObject>()|.  For the same reason,
         // don't reuse the RegExpShared below.
         RootedObject patternObj(cx, &patternValue.toObject());
 
         RootedAtom sourceAtom(cx);
         RegExpFlag flags;
         {
             // Step 4.a.
-            RootedRegExpShared g(cx);
+            RegExpGuard g(cx);
             if (!RegExpToShared(cx, patternObj, &g))
                 return false;
             sourceAtom = g->getSource();
 
             // Step 4.b.
             // Get original flags in all cases, to compare with passed flags.
             flags = g->getFlags();
         }
@@ -555,17 +555,17 @@ js::regexp_clone(JSContext* cx, unsigned
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject from(cx, &args[0].toObject());
 
     RootedAtom sourceAtom(cx);
     RegExpFlag flags;
     {
-        RootedRegExpShared g(cx);
+        RegExpGuard g(cx);
         if (!RegExpToShared(cx, from, &g))
             return false;
         sourceAtom = g->getSource();
         flags = g->getFlags();
     }
 
     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx));
     if (!regexp)
@@ -912,17 +912,17 @@ ExecuteRegExp(JSContext* cx, HandleObjec
      * WARNING: Despite the presence of spec step comment numbers, this
      *          algorithm isn't consistent with any ES6 version, draft or
      *          otherwise.  YOU HAVE BEEN WARNED.
      */
 
     /* Steps 1-2 performed by the caller. */
     Rooted<RegExpObject*> reobj(cx, &regexp->as<RegExpObject>());
 
-    RootedRegExpShared re(cx);
+    RegExpGuard re(cx);
     if (!RegExpObject::getShared(cx, reobj, &re))
         return RegExpRunStatus_Error;
 
     RegExpStatics* res;
     if (staticsUpdate == UpdateRegExpStatics) {
         res = GlobalObject::getRegExpStatics(cx, cx->global());
         if (!res)
             return RegExpRunStatus_Error;
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -115,28 +115,23 @@ 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,18 +764,16 @@ 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,57 +104,55 @@ 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) \
-    D(REGEXP_SHARED,       RegExpShared,  js::RegExpShared,  js::RegExpShared)
+ /* 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)
 
 #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 "
@@ -310,19 +308,16 @@ 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,24 +438,16 @@ 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);
 }
 
@@ -562,17 +554,16 @@ 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*);
@@ -879,17 +870,16 @@ 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,17 +81,16 @@ 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,17 +179,16 @@ 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 */
@@ -207,19 +206,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, 61 }
+    { PHASE_LIMIT, nullptr, PHASE_NO_PARENT, 60 }
 
-    // Current number of telemetryBuckets is 61. If you insert new phases
+    // Current number of telemetryBuckets is 60. 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,17 +64,16 @@ 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,20 +60,16 @@ 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
@@ -6093,32 +6093,32 @@ JS_ObjectIsRegExp(JSContext* cx, HandleO
 }
 
 JS_PUBLIC_API(unsigned)
 JS_GetRegExpFlags(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
-    RootedRegExpShared shared(cx);
+    RegExpGuard shared(cx);
     if (!RegExpToShared(cx, obj, &shared))
         return false;
-    return shared->getFlags();
+    return shared.re()->getFlags();
 }
 
 JS_PUBLIC_API(JSString*)
 JS_GetRegExpSource(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
-    RootedRegExpShared shared(cx);
+    RegExpGuard shared(cx);
     if (!RegExpToShared(cx, obj, &shared))
         return nullptr;
-    return shared->getSource();
+    return shared.re()->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(zone),
+    regExps(runtime_),
     globalWriteBarriered(0),
     detachedTypedObjects(0),
     objectMetadataState(ImmediateMetadata()),
     selfHostingScriptSource(nullptr),
     objectMetadataTable(nullptr),
     innerViews(zone, InnerViewTable()),
     lazyArrayBuffers(nullptr),
     wasm(zone),
@@ -223,23 +223,16 @@ 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()
@@ -252,18 +245,17 @@ 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 // JSGC_HASH_TABLE_CHECKS
+#endif
 
 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();
+    void checkCacheAfterMovingGC() { MOZ_ASSERT(!s || !IsForwarded(s)); }
 #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,19 +1158,19 @@ 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;
 extern JS_FRIEND_API(bool)
-RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp,
-                        JS::MutableHandle<RegExpShared*> shared);
+RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp, RegExpGuard* shared);
 
 /* Implemented in CrossCompartmentWrapper.cpp. */
 typedef enum NukeReferencesToWindow {
     NukeWindowReferences,
     DontNukeWindowReferences
 } NukeReferencesToWindow;
 
 typedef enum NukeReferencesFromTarget {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -362,22 +362,17 @@ static const FinalizePhase BackgroundFin
             AllocKind::OBJECT4_BACKGROUND,
             AllocKind::OBJECT8_BACKGROUND,
             AllocKind::OBJECT12_BACKGROUND,
             AllocKind::OBJECT16_BACKGROUND
         }
     },
     {
         gcstats::PHASE_SWEEP_SCOPE, {
-            AllocKind::SCOPE,
-        }
-    },
-    {
-        gcstats::PHASE_SWEEP_REGEXP_SHARED, {
-            AllocKind::REGEXP_SHARED,
+            AllocKind::SCOPE
         }
     },
     {
         gcstats::PHASE_SWEEP_STRING, {
             AllocKind::FAT_INLINE_STRING,
             AllocKind::STRING,
             AllocKind::FAT_INLINE_ATOM,
             AllocKind::ATOM,
@@ -1722,26 +1717,25 @@ 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::SCOPE,
-    AllocKind::REGEXP_SHARED
+    AllocKind::ATOM
 };
 
 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
@@ -2055,33 +2049,71 @@ GCRuntime::relocateArenas(Zone* zone, JS
             freeCells += arena->countFreeCells();
         MOZ_ASSERT(freeCells < Arena::thingsPerArena(i));
     }
 #endif
 
     return true;
 }
 
-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
+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);
+}
 
 void
 Zone::prepareForCompacting()
 {
     FreeOp* fop = runtimeFromActiveCooperatingThread()->defaultFreeOp();
     discardJitCode(fop);
 }
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -116,17 +116,16 @@ 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)
 {
@@ -155,17 +154,16 @@ 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)
 {
@@ -1133,39 +1131,119 @@ 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>
-inline bool IsForwarded(T* t);
-inline bool IsForwarded(const JS::Value& value);
+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;
+};
 
 template <typename T>
-inline T* Forwarded(T* t);
+inline bool
+IsForwarded(T* t)
+{
+    RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
+    if (!MightBeForwarded<T>::value) {
+        MOZ_ASSERT(!overlay->isForwarded());
+        return false;
+    }
 
-inline Value Forwarded(const JS::Value& value);
+    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 MaybeForwarded(T 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);
+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(T* t);
+inline void
+CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t)
+{
+    CheckGCThingAfterMovingGC(t.unbarrieredGet());
+}
 
-template <typename T>
-inline void CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t);
+struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor<Value> {
+    template <typename T> void operator()(T* t) { CheckGCThingAfterMovingGC(t); }
+};
 
-inline void CheckValueAfterMovingGC(const JS::Value& value);
+inline void
+CheckValueAfterMovingGC(const JS::Value& value)
+{
+    DispatchTyped(CheckValueAfterMovingGCFunctor(), 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,120 +483,12 @@ 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/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -113,17 +113,17 @@ class JS_FRIEND_API(Wrapper) : public Ba
                              bool* bp) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject proxy,
                          JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
                                    unsigned indent) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
-                                 MutableHandle<RegExpShared*> shared) const override;
+                                 RegExpGuard* g) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
                                   MutableHandleValue vp) const override;
     virtual bool isCallable(JSObject* obj) const override;
     virtual bool isConstructor(JSObject* obj) const override;
     virtual JSObject* weakmapKeyDelegate(JSObject* proxy) const override;
 
   public:
     using BaseProxyHandler::Action;
@@ -208,18 +208,17 @@ class JS_FRIEND_API(CrossCompartmentWrap
                                               AutoIdVector& props) const override;
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
                              bool* bp) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
                                    unsigned indent) const override;
-    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
-                                 MutableHandle<RegExpShared*> shared) const override;
+    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
 
     // Allocate CrossCompartmentWrappers in the nursery.
     virtual bool canNurseryAllocate() const override { return true; }
 
     static const CrossCompartmentWrapper singleton;
     static const CrossCompartmentWrapper singletonWithPrototype;
 };
@@ -307,18 +306,17 @@ class JS_FRIEND_API(SecurityWrapper) : p
     virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
                               ObjectOpResult& result) const override;
     virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const override;
 
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override;
-    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
-                                 MutableHandle<RegExpShared*> shared) const override;
+    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
 
     // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded
     // against.
 
     virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                        JS::HandleObject callable) const override;
     virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const override;
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -322,17 +322,17 @@ BaseProxyHandler::fun_toString(JSContext
         return JS_NewStringCopyZ(cx, "function () {\n    [native code]\n}");
     RootedValue v(cx, ObjectValue(*proxy));
     ReportIsNotFunction(cx, v);
     return nullptr;
 }
 
 bool
 BaseProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy,
-                                  MutableHandleRegExpShared shared) const
+                                  RegExpGuard* g) const
 {
     MOZ_CRASH("This should have been a wrapped regexp");
 }
 
 bool
 BaseProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const
 {
     vp.setUndefined();
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -453,29 +453,29 @@ CrossCompartmentWrapper::fun_toString(JS
             return nullptr;
     }
     if (!cx->compartment()->wrap(cx, &str))
         return nullptr;
     return str;
 }
 
 bool
-CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper,
-                                         MutableHandleRegExpShared shared) const
+CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper, RegExpGuard* g) const
 {
-    RootedRegExpShared re(cx);
+    RegExpGuard wrapperGuard(cx);
     {
         AutoCompartment call(cx, wrappedObject(wrapper));
-        if (!Wrapper::regexp_toShared(cx, wrapper, &re))
+        if (!Wrapper::regexp_toShared(cx, wrapper, &wrapperGuard))
             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(), shared);
+    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,
            NOTHING,
            Wrapper::boxedValue_unbox(cx, wrapper, vp),
--- a/js/src/proxy/DeadObjectProxy.cpp
+++ b/js/src/proxy/DeadObjectProxy.cpp
@@ -137,18 +137,17 @@ DeadObjectProxy::className(JSContext* cx
 JSString*
 DeadObjectProxy::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
 {
     ReportDead(cx);
     return nullptr;
 }
 
 bool
-DeadObjectProxy::regexp_toShared(JSContext* cx, HandleObject proxy,
-                                 MutableHandle<RegExpShared*> shared) const
+DeadObjectProxy::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
 {
     ReportDead(cx);
     return false;
 }
 
 const char DeadObjectProxy::family = 0;
 const DeadObjectProxy DeadObjectProxy::singleton;
 
--- a/js/src/proxy/DeadObjectProxy.h
+++ b/js/src/proxy/DeadObjectProxy.h
@@ -44,18 +44,17 @@ class DeadObjectProxy : public BaseProxy
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
                              bool* bp) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
-    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
-                                 MutableHandle<RegExpShared*> shared) const override;
+    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
 
     static const char family;
     static const DeadObjectProxy singleton;
 };
 
 bool
 IsDeadProxyObject(JSObject* obj);
 
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -554,21 +554,21 @@ Proxy::fun_toString(JSContext* cx, Handl
                            BaseProxyHandler::GET, /* mayThrow = */ false);
     // Do the safe thing if the policy rejects.
     if (!policy.allowed())
         return handler->BaseProxyHandler::fun_toString(cx, proxy, indent);
     return handler->fun_toString(cx, proxy, indent);
 }
 
 bool
-Proxy::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandleRegExpShared shared)
+Proxy::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
 {
     if (!CheckRecursionLimit(cx))
         return false;
-    return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, shared);
+    return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, g);
 }
 
 bool
 Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp)
 {
     if (!CheckRecursionLimit(cx))
         return false;
     return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -8,16 +8,18 @@
 #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
@@ -55,18 +57,17 @@ class Proxy
                                              AutoIdVector& props);
     static bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                            const CallArgs& args);
     static bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp);
     static bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls);
     static bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer);
     static const char* className(JSContext* cx, HandleObject proxy);
     static JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent);
-    static bool regexp_toShared(JSContext* cx, HandleObject proxy,
-                                MutableHandle<RegExpShared*> shared);
+    static bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g);
     static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp);
 
     static bool watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable);
     static bool unwatch(JSContext* cx, HandleObject proxy, HandleId id);
 
     static bool getElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end,
                             ElementAdder* adder);
 
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -1263,18 +1263,17 @@ JSString*
 ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
 {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                               js_Function_str, js_toString_str, "object");
     return nullptr;
 }
 
 bool
-ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy,
-                                      MutableHandleRegExpShared shared) const
+ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
 {
     MOZ_CRASH("Should not end up in ScriptedProxyHandler::regexp_toShared");
     return false;
 }
 
 bool
 ScriptedProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy,
                                        MutableHandleValue vp) const
--- a/js/src/proxy/ScriptedProxyHandler.h
+++ b/js/src/proxy/ScriptedProxyHandler.h
@@ -65,17 +65,17 @@ class ScriptedProxyHandler : public Base
                              bool* bp) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject proxy,
                          JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
                                    unsigned indent) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
-                                 MutableHandle<RegExpShared*> shared) const override;
+                                 RegExpGuard* g) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
                                   MutableHandleValue vp) const override;
 
     virtual bool isCallable(JSObject* obj) const override;
     virtual bool isConstructor(JSObject* obj) const override;
 
     virtual bool isScripted() const override { return true; }
 
--- a/js/src/proxy/SecurityWrapper.cpp
+++ b/js/src/proxy/SecurityWrapper.cpp
@@ -83,20 +83,19 @@ SecurityWrapper<Base>::isArray(JSContext
 {
     // This should ReportAccessDenied(cx), but bug 849730 disagrees.  :-(
     *answer = JS::IsArrayAnswer::NotArray;
     return true;
 }
 
 template <class Base>
 bool
-SecurityWrapper<Base>::regexp_toShared(JSContext* cx, HandleObject obj,
-                                       MutableHandle<RegExpShared*> shared) const
+SecurityWrapper<Base>::regexp_toShared(JSContext* cx, HandleObject obj, RegExpGuard* g) const
 {
-    return Base::regexp_toShared(cx, obj, shared);
+    return Base::regexp_toShared(cx, obj, g);
 }
 
 template <class Base>
 bool
 SecurityWrapper<Base>::boxedValue_unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp) const
 {
     vp.setUndefined();
     return true;
--- a/js/src/proxy/Wrapper.cpp
+++ b/js/src/proxy/Wrapper.cpp
@@ -263,20 +263,20 @@ JSString*
 Wrapper::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return fun_toStringHelper(cx, target, indent);
 }
 
 bool
-Wrapper::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandleRegExpShared shared) const
+Wrapper::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
-    return RegExpToShared(cx, target, shared);
+    return RegExpToShared(cx, target, g);
 }
 
 bool
 Wrapper::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return Unbox(cx, target, vp);
 }
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -14,18 +14,16 @@
 
 #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,23 +593,16 @@ 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,17 +4,16 @@
  * 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
@@ -115,26 +114,39 @@ 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,
-                        MutableHandleRegExpShared shared)
+RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g)
 {
-    if (regexp->hasShared()) {
-        shared.set(regexp->sharedRef());
+    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);
         return true;
     }
 
-    return createShared(cx, regexp, shared);
+    return createShared(cx, regexp, g);
 }
 
 /* static */ bool
 RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlag* mask)
 {
   if (native == regexp_global) {
       *mask = GlobalFlag;
       return true;
@@ -157,42 +169,36 @@ RegExpObject::isOriginalFlagGetter(JSNat
   }
 
   return false;
 }
 
 /* static */ void
 RegExpObject::trace(JSTracer* trc, JSObject* obj)
 {
-    obj->as<RegExpObject>().trace(trc);
-}
+    RegExpShared* shared = obj->as<RegExpObject>().maybeShared();
+    if (!shared)
+        return;
 
-static inline bool
-IsMarkingTrace(JSTracer* trc)
-{
-    // Determine whether tracing is happening during normal marking.  We need to
-    // test all the following conditions, since:
-    //
+    // 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:
     //   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.
-
-    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");
+    if (JS::CurrentThreadIsHeapCollecting() &&
+        trc->isMarkingTracer() &&
+        !obj->asTenured().zone()->isPreservingCode())
+    {
+        obj->as<RegExpObject>().NativeObject::setPrivate(nullptr);
+    } else {
+        shared->trace(trc);
+    }
 }
 
 static JSObject*
 CreateRegExpPrototype(JSContext* cx, JSProtoKey key)
 {
     return GlobalObject::createBlankPrototype(cx, cx->global(), &RegExpObject::protoClass_);
 }
 
@@ -269,24 +275,23 @@ RegExpObject::create(JSContext* cx, Hand
         return nullptr;
 
     regexp->initAndZeroLastIndex(source, flags, cx);
 
     return regexp;
 }
 
 /* static */ bool
-RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp,
-                           MutableHandleRegExpShared shared)
+RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g)
 {
-    MOZ_ASSERT(!regexp->hasShared());
-    if (!cx->compartment()->regExps.get(cx, regexp->getSource(), regexp->getFlags(), shared))
+    MOZ_ASSERT(!regexp->maybeShared());
+    if (!cx->compartment()->regExps.get(cx, regexp->getSource(), regexp->getFlags(), g))
         return false;
 
-    regexp->setShared(*shared);
+    regexp->setShared(**g);
     return true;
 }
 
 Shape*
 RegExpObject::assignInitialShape(JSContext* cx, Handle<RegExpObject*> self)
 {
     MOZ_ASSERT(self->empty());
 
@@ -884,21 +889,21 @@ RegExpShared::dumpBytecode(JSContext* cx
 
     return true;
 }
 
 /* static */ bool
 RegExpObject::dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
                            bool match_only, HandleLinearString input)
 {
-    RootedRegExpShared shared(cx);
-    if (!getShared(cx, regexp, &shared))
+    RegExpGuard g(cx);
+    if (!getShared(cx, regexp, &g))
         return false;
 
-    return shared->dumpBytecode(cx, match_only, input);
+    return g.re()->dumpBytecode(cx, match_only, input);
 }
 #endif
 
 template <typename CharT>
 static MOZ_ALWAYS_INLINE bool
 IsRegExpMetaChar(CharT ch)
 {
     switch (ch) {
@@ -937,42 +942,57 @@ 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), canStringMatch(false), parenCount(0)
+  : source(source), flags(flags), parenCount(0), canStringMatch(false), marked_(false)
 {}
 
 RegExpShared::~RegExpShared()
 {
     for (size_t i = 0; i < tables.length(); i++)
         js_delete(tables[i]);
 }
 
 void
-RegExpShared::traceChildren(JSTracer* trc)
+RegExpShared::trace(JSTracer* trc)
 {
-    // Discard code to avoid holding onto ExecutablePools.
-    if (IsMarkingTrace(trc) && trc->runtime()->gc.isShrinkingGC())
-        discardJitCode();
+    if (trc->isMarkingTracer())
+        marked_ = true;
 
     TraceNullableEdge(trc, &source, "RegExpShared source");
     for (auto& comp : compilationArray)
         TraceNullableEdge(trc, &comp.jitCode, "RegExpShared code");
 }
 
-void
-RegExpShared::discardJitCode()
+bool
+RegExpShared::isMarkedGray() const
 {
-    for (auto& comp : compilationArray)
-        comp.jitCode = nullptr;
+    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()));
+    }
 }
 
 bool
 RegExpShared::compile(JSContext* cx, HandleLinearString input,
                       CompilationMode mode, ForceByteCodeEnum force)
 {
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
     AutoTraceLog logCompile(logger, TraceLogger_IrregexpCompile);
@@ -1159,55 +1179,62 @@ RegExpShared::execute(JSContext* cx, Han
     }
 
     if (result == RegExpRunStatus_Success && matches)
         matches->checkAgainst(length);
     return result;
 }
 
 size_t
-RegExpShared::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
+RegExpShared::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
-    size_t n = 0;
+    size_t n = mallocSizeOf(this);
 
     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(Zone* zone)
-  : set_(zone, Set(zone->runtimeFromActiveCooperatingThread())),
+RegExpCompartment::RegExpCompartment(JSRuntime* rt)
+  : set_(rt),
     matchResultTemplateObject_(nullptr),
     optimizableRegExpPrototypeShape_(nullptr),
     optimizableRegExpInstanceShape_(nullptr)
 {}
 
 RegExpCompartment::~RegExpCompartment()
 {
-    MOZ_ASSERT_IF(set_.initialized(), set_.empty());
+    // 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);
+        }
+    }
 }
 
 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
@@ -1253,19 +1280,69 @@ 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_))
@@ -1276,55 +1353,65 @@ RegExpCompartment::sweep(JSRuntime* rt)
     if (optimizableRegExpInstanceShape_ &&
         IsAboutToBeFinalized(&optimizableRegExpInstanceShape_))
     {
         optimizableRegExpInstanceShape_.set(nullptr);
     }
 }
 
 bool
-RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags,
-                       MutableHandleRegExpShared result)
+RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g)
 {
-    DependentAddPtr<Set> p(cx, set_.get(), Key(source, flags));
+    Key key(source, flags);
+    Set::AddPtr p = set_.lookupForAdd(key);
     if (p) {
-        result.set(*p);
+        // Trigger a read barrier on existing RegExpShared instances fetched
+        // from the table (which only holds weak references).
+        RegExpSharedReadBarrier(cx, *p);
+
+        g->init(**p);
         return true;
     }
 
-    auto shared = Allocate<RegExpShared>(cx);
+    ScopedJSDeletePtr<RegExpShared> shared(cx->new_<RegExpShared>(source, flags));
     if (!shared)
         return false;
 
-    new (shared) RegExpShared(source, flags);
-
-    if (!p.add(cx, set_.get(), Key(source, flags), shared)) {
+    if (!set_.add(p, shared)) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    result.set(shared);
+    // Trace RegExpShared instances created during an incremental GC.
+    RegExpSharedReadBarrier(cx, shared);
+
+    g->init(*shared.forget());
     return true;
 }
 
 bool
-RegExpCompartment::get(JSContext* cx, HandleAtom atom, JSString* opt,
-                       MutableHandleRegExpShared shared)
+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, shared);
+    return get(cx, atom, flags, g);
 }
 
 size_t
 RegExpCompartment::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
-    return set_.sizeOfExcludingThis(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;
 }
 
 /* Functions */
 
 JSObject*
 js::CloneRegExpObject(JSContext* cx, JSObject* obj_)
 {
     Rooted<RegExpObject*> regex(cx, &obj_->as<RegExpObject>());
@@ -1337,22 +1424,22 @@ js::CloneRegExpObject(JSContext* cx, JSO
         return nullptr;
     clone->initPrivate(nullptr);
 
     if (!EmptyShape::ensureInitialCustomShape<RegExpObject>(cx, clone))
         return nullptr;
 
     Rooted<JSAtom*> source(cx, regex->getSource());
 
-    RootedRegExpShared shared(cx);
-    if (!RegExpObject::getShared(cx, regex, &shared))
+    RegExpGuard g(cx);
+    if (!RegExpObject::getShared(cx, regex, &g))
         return nullptr;
 
-    clone->initAndZeroLastIndex(source, shared->getFlags(), cx);
-    clone->setShared(*shared);
+    clone->initAndZeroLastIndex(source, g->getFlags(), cx);
+    clone->setShared(*g.re());
 
     return clone;
 }
 
 static bool
 HandleRegExpFlag(RegExpFlag flag, RegExpFlag* flags)
 {
     if (*flags & flag)
@@ -1472,19 +1559,12 @@ js::CloneScriptRegExpObject(JSContext* c
 
     RootedAtom source(cx, reobj.getSource());
     cx->markAtom(source);
 
     return RegExpObject::create(cx, source, reobj.getFlags(), nullptr, cx->tempLifoAlloc());
 }
 
 JS_FRIEND_API(bool)
-js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj, MutableHandleRegExpShared shared)
+js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj, js::RegExpGuard* g)
 {
-    return RegExpToShared(cx, obj, shared);
+    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
@@ -27,29 +27,31 @@
  *   RegExpObject - The JS-visible object whose .[[Class]] equals "RegExp"
  *
  *   RegExpShared - The compiled representation of the regexp.
  *
  *   RegExpCompartment - Owns all RegExpShared instances in a compartment.
  *
  * To save memory, a RegExpShared is not created for a RegExpObject until it is
  * needed for execution. When a RegExpShared needs to be created, it is looked
- * up in a per-compartment table to allow reuse between objects. Lastly, on GC,
- * every RegExpShared that is not in active use is discarded.
+ * up in a per-compartment table to allow reuse between objects. Lastly, on
+ * GC, every RegExpShared (that is not active on the callstack) is discarded.
+ * Because of the last point, any code using a RegExpShared (viz., by executing
+ * a regexp) must indicate the RegExpShared is active via RegExpGuard.
  */
 namespace js {
 
 struct MatchPair;
 class MatchPairs;
 class RegExpShared;
 class RegExpStatics;
 
 namespace frontend { class TokenStream; }
 
-enum RegExpFlag : uint8_t
+enum RegExpFlag
 {
     IgnoreCaseFlag  = 0x01,
     GlobalFlag      = 0x02,
     MultilineFlag   = 0x04,
     StickyFlag      = 0x08,
     UnicodeFlag     = 0x10,
 
     NoFlags         = 0x00,
@@ -85,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 : public gc::TenuredCell
+class RegExpShared
 {
   public:
     enum CompilationMode {
         Normal,
         MatchOnly
     };
 
     enum ForceByteCodeEnum {
@@ -106,50 +108,49 @@ class RegExpShared : public gc::TenuredC
   private:
     friend class RegExpCompartment;
     friend class RegExpStatics;
 
     typedef frontend::TokenStream TokenStream;
 
     struct RegExpCompilation
     {
-        ReadBarriered<jit::JitCode*> jitCode;
+        HeapPtr<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;
+    size_t             parenCount;
     bool               canStringMatch;
-    size_t             parenCount;
+    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);
 
@@ -157,16 +158,17 @@ class RegExpShared : public gc::TenuredC
         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.
@@ -196,19 +198,26 @@ class RegExpShared : public gc::TenuredC
                     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 traceChildren(JSTracer* trc);
+    void trace(JSTracer* trc);
+    bool needsSweep(JSRuntime* rt);
     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);
     }
 
@@ -222,57 +231,99 @@ class RegExpShared : public gc::TenuredC
              + offsetof(RegExpCompilation, jitCode);
     }
     static size_t offsetOfTwoByteJitCode(CompilationMode mode) {
         return offsetof(RegExpShared, compilationArray)
              + (CompilationIndex(mode, false) * sizeof(RegExpCompilation))
              + offsetof(RegExpCompilation, jitCode);
     }
 
-    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
 #ifdef DEBUG
     bool dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input);
 #endif
 };
 
-using RootedRegExpShared = JS::Rooted<RegExpShared*>;
-using HandleRegExpShared = JS::Handle<RegExpShared*>;
-using MutableHandleRegExpShared = JS::MutableHandle<RegExpShared*>;
+/*
+ * 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(const ReadBarriered<RegExpShared*>& shared)
-          : atom(shared.unbarrieredGet()->getSource()),
-            flag(shared.unbarrieredGet()->getFlags())
+        MOZ_IMPLICIT Key(RegExpShared* shared)
+          : atom(shared->getSource()), flag(shared->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.
      */
-    using Set = GCHashSet<ReadBarriered<RegExpShared*>, Key, RuntimeAllocPolicy>;
-    JS::WeakCache<Set> set_;
+    typedef HashSet<RegExpShared*, Key, RuntimeAllocPolicy> Set;
+    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_;
 
@@ -295,29 +346,28 @@ class RegExpCompartment
      *   * lastProperty is lastIndex
      *   * prototype is RegExp.prototype
      */
     ReadBarriered<Shape*> optimizableRegExpInstanceShape_;
 
     ArrayObject* createMatchResultTemplateObject(JSContext* cx);
 
   public:
-    explicit RegExpCompartment(Zone* zone);
+    explicit RegExpCompartment(JSRuntime* rt);
     ~RegExpCompartment();
 
     bool init(JSContext* cx);
     void sweep(JSRuntime* rt);
 
     bool empty() { return set_.empty(); }
 
-    bool get(JSContext* cx, JSAtom* source, RegExpFlag flags, MutableHandleRegExpShared shared);
+    bool get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g);
 
     /* Like 'get', but compile 'maybeOpt' (if non-null). */
-    bool get(JSContext* cx, HandleAtom source, JSString* maybeOpt,
-             MutableHandleRegExpShared shared);
+    bool get(JSContext* cx, HandleAtom source, JSString* maybeOpt, RegExpGuard* g);
 
     /* Get or create template object used to base the result of .exec() on. */
     ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) {
         if (matchResultTemplateObject_)
             return matchResultTemplateObject_;
         return createMatchResultTemplateObject(cx);
     }
 
@@ -429,29 +479,24 @@ class RegExpObject : public NativeObject
     bool global() const     { return getFlags() & GlobalFlag; }
     bool multiline() const  { return getFlags() & MultilineFlag; }
     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,
-                                       MutableHandleRegExpShared shared);
-
-    bool hasShared() {
-        return !!sharedRef();
-    }
+                                       RegExpGuard* g);
 
     void setShared(RegExpShared& shared) {
-        MOZ_ASSERT(!hasShared());
-        sharedRef() = &shared;
+        MOZ_ASSERT(!maybeShared());
+        NativeObject::setPrivate(&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);
 
@@ -461,21 +506,19 @@ class RegExpObject : public NativeObject
 #endif
 
   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,
-                                          MutableHandleRegExpShared shared);
-
-    ReadBarriered<RegExpShared*>& sharedRef() {
-        auto& ref = NativeObject::privateRef(PRIVATE_SLOT);
-        return reinterpret_cast<ReadBarriered<RegExpShared*>&>(ref);
+                                          RegExpGuard* g);
+    RegExpShared* maybeShared() const {
+        return static_cast<RegExpShared*>(NativeObject::getPrivate(PRIVATE_SLOT));
     }
 
     /* Call setShared in preference to setPrivate. */
     void setPrivate(void* priv) = delete;
 };
 
 /*
  * Parse regexp flags. Report an error and return false if an invalid
@@ -483,22 +526,22 @@ class RegExpObject : public NativeObject
  *
  * N.B. flagStr must be rooted.
  */
 bool
 ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlag* flagsOut);
 
 /* Assuming GetBuiltinClass(obj) is ESClass::RegExp, return a RegExpShared for obj. */
 inline bool
-RegExpToShared(JSContext* cx, HandleObject obj, MutableHandleRegExpShared shared)
+RegExpToShared(JSContext* cx, HandleObject obj, RegExpGuard* g)
 {
     if (obj->is<RegExpObject>())
-        return RegExpObject::getShared(cx, obj.as<RegExpObject>(), shared);
+        return RegExpObject::getShared(cx, obj.as<RegExpObject>(), g);
 
-    return Proxy::regexp_toShared(cx, obj, shared);
+    return Proxy::regexp_toShared(cx, obj, g);
 }
 
 template<XDRMode mode>
 bool
 XDRScriptRegExpObject(XDRState<mode>* xdr, MutableHandle<RegExpObject*> objp);
 
 extern JSObject*
 CloneScriptRegExpObject(JSContext* cx, RegExpObject& re);
@@ -511,34 +554,9 @@ 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/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -76,28 +76,28 @@ RegExpStatics::executeLazy(JSContext* cx
     if (!pendingLazyEvaluation)
         return true;
 
     MOZ_ASSERT(lazySource);
     MOZ_ASSERT(matchesInput);
     MOZ_ASSERT(lazyIndex != size_t(-1));
 
     /* Retrieve or create the RegExpShared in this compartment. */
-    RootedRegExpShared shared(cx);
-    if (!cx->compartment()->regExps.get(cx, lazySource, lazyFlags, &shared))
+    RegExpGuard g(cx);
+    if (!cx->compartment()->regExps.get(cx, lazySource, lazyFlags, &g))
         return false;
 
     /*
      * It is not necessary to call aboutToWrite(): evaluation of
      * implicit copies is safe.
      */
 
     /* Execute the full regular expression. */
     RootedLinearString input(cx, matchesInput);
-    RegExpRunStatus status = shared->execute(cx, input, lazyIndex, &this->matches, nullptr);
+    RegExpRunStatus status = g->execute(cx, input, lazyIndex, &this->matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     /*
      * RegExpStatics are only updated on successful (matching) execution.
      * Re-running the same expression must therefore produce a matching result.
      */
     MOZ_ASSERT(status == RegExpRunStatus_Success);
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -14,17 +14,16 @@
 #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/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -1408,17 +1408,17 @@ JSStructuredCloneWriter::startWrite(Hand
         if (backref)
             return true;
 
         ESClass cls;
         if (!GetBuiltinClass(context(), obj, &cls))
             return false;
 
         if (cls == ESClass::RegExp) {
-            RootedRegExpShared re(context());
+            RegExpGuard re(context());
             if (!RegExpToShared(context(), obj, &re))
                 return false;
             return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) &&
                    writeString(SCTAG_STRING, re->getSource());
         } else if (cls == ESClass::Date) {
             RootedValue unboxed(context());
             if (!Unbox(context(), obj, &unboxed))
                 return false;
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -307,17 +307,16 @@ 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>());
@@ -330,17 +329,16 @@ 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
 {
@@ -395,17 +393,16 @@ 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,24 +1939,16 @@ 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).");
 
@@ -2981,20 +2973,16 @@ 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'.");
 
@@ -3036,20 +3024,16 @@ 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,20 +478,17 @@ 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 ||
-         aKind == JS::TraceKind::RegExpShared;
+  return aKind == JS::TraceKind::Object || aKind == JS::TraceKind::Script || aKind == JS::TraceKind::Scope;
 }
 
 bool
 GetBuildId(JS::BuildIdCharVector* aBuildID);
 
 } // namespace mozilla
 
 #endif // mozilla_CycleCollectedJSContext_h__