Bug 1301343 - Trace pointers stored in MIR. r=jonco,nbp
authorJan de Mooij <jdemooij@mozilla.com>
Sun, 02 Oct 2016 15:49:57 +0200
changeset 316150 f5d61890ecb53a10133d41f6a09f2d36d988cd92
parent 316149 3649c55173d2bab464e4bd9a84e1d493cad7fe97
child 316151 a92b2f6ab6ad5279682451506da866f926d27be9
push id30764
push userphilringnalda@gmail.com
push dateMon, 03 Oct 2016 05:33:02 +0000
treeherdermozilla-central@955840bfd3c2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco, nbp
bugs1301343
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1301343 - Trace pointers stored in MIR. r=jonco,nbp
js/src/jit/Ion.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/IonAnalysis.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/vm/HelperThreads.cpp
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2259,16 +2259,19 @@ IonCompile(JSContext* cx, JSScript* scri
     }
 
     // If possible, compile the script off thread.
     if (options.offThreadCompilationAvailable()) {
         JitSpew(JitSpew_IonSyncLogs, "Can't log script %s:%" PRIuSIZE
                 ". (Compiled on background thread.)",
                 builderScript->filename(), builderScript->lineno());
 
+        if (!CreateMIRRootList(*builder))
+            return AbortReason_Alloc;
+
         if (!StartOffThreadIonCompile(cx, builder)) {
             JitSpew(JitSpew_IonAbort, "Unable to start off-thread ion compilation.");
             builder->graphSpewer().endFunction();
             return AbortReason_Alloc;
         }
 
         if (!recompile)
             builderScript->setIonScript(cx->runtime(), ION_COMPILING_SCRIPT);
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -4586,8 +4586,69 @@ jit::MakeLoopsContiguous(MIRGraph& graph
 
         // Move all blocks between header and backedge that aren't marked to
         // the end of the loop, making the loop itself contiguous.
         MakeLoopContiguous(graph, header, numMarked);
     }
 
     return true;
 }
+
+MRootList::MRootList(TempAllocator& alloc)
+{
+#define INIT_VECTOR(name, _0, _1) \
+    roots_[JS::RootKind::name].emplace(alloc);
+JS_FOR_EACH_TRACEKIND(INIT_VECTOR)
+#undef INIT_VECTOR
+}
+
+template <typename T>
+static void
+TraceVector(JSTracer* trc, const MRootList::RootVector& vector, const char* name)
+{
+    for (auto ptr : vector) {
+        T ptrT = static_cast<T>(ptr);
+        TraceManuallyBarrieredEdge(trc, &ptrT, name);
+        MOZ_ASSERT(ptr == ptrT, "Shouldn't move without updating MIR pointers");
+    }
+}
+
+void
+MRootList::trace(JSTracer* trc)
+{
+#define TRACE_ROOTS(name, type, _) \
+    TraceVector<type*>(trc, *roots_[JS::RootKind::name], "mir-root-" #name);
+JS_FOR_EACH_TRACEKIND(TRACE_ROOTS)
+#undef TRACE_ROOTS
+}
+
+MOZ_MUST_USE bool
+jit::CreateMIRRootList(IonBuilder& builder)
+{
+    MOZ_ASSERT(!builder.info().isAnalysis());
+
+    TempAllocator& alloc = builder.alloc();
+    MIRGraph& graph = builder.graph();
+
+    MRootList* roots = new(alloc.fallible()) MRootList(alloc);
+    if (!roots)
+        return false;
+
+    JSScript* prevScript = nullptr;
+
+    for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
+
+        JSScript* script = block->info().script();
+        if (script != prevScript) {
+            if (!roots->append(script))
+                return false;
+            prevScript = script;
+        }
+
+        for (MInstructionIterator iter(block->begin()), end(block->end()); iter != end; iter++) {
+            if (!iter->appendRoots(*roots))
+                return false;
+        }
+    }
+
+    builder.setRootList(*roots);
+    return true;
+}
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -61,16 +61,19 @@ RenumberBlocks(MIRGraph& graph);
 
 MOZ_MUST_USE bool
 AccountForCFGChanges(MIRGenerator* mir, MIRGraph& graph, bool updateAliasAnalysis,
                      bool underValueNumberer = false);
 
 MOZ_MUST_USE bool
 RemoveUnmarkedBlocks(MIRGenerator* mir, MIRGraph& graph, uint32_t numMarkedBlocks);
 
+MOZ_MUST_USE bool
+CreateMIRRootList(IonBuilder& builder);
+
 void
 ClearDominatorTree(MIRGraph& graph);
 
 MOZ_MUST_USE bool
 BuildDominatorTree(MIRGraph& graph);
 
 MOZ_MUST_USE bool
 BuildPhiReverseMapping(MIRGraph& graph);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -120,16 +120,17 @@ IonBuilder::IonBuilder(JSContext* analys
                        const OptimizationInfo* optimizationInfo,
                        BaselineFrameInspector* baselineFrame, size_t inliningDepth,
                        uint32_t loopDepth)
   : MIRGenerator(comp, options, temp, graph, info, optimizationInfo),
     backgroundCodegen_(nullptr),
     actionableAbortScript_(nullptr),
     actionableAbortPc_(nullptr),
     actionableAbortMessage_(nullptr),
+    rootList_(nullptr),
     analysisContext(analysisContext),
     baselineFrame_(baselineFrame),
     constraints_(constraints),
     analysis_(*temp, info->script()),
     thisTypes(nullptr),
     argTypes(nullptr),
     typeArray(nullptr),
     typeArrayHint(0),
@@ -14630,12 +14631,11 @@ IonBuilder::convertToBoolean(MDefinition
 }
 
 void
 IonBuilder::trace(JSTracer* trc)
 {
     if (!compartment->runtime()->runtimeMatches(trc->runtime()))
         return;
 
-    TraceManuallyBarrieredEdge(trc, &script_, "IonBuiler::script_");
-    if (actionableAbortScript_)
-        TraceManuallyBarrieredEdge(trc, &actionableAbortScript_, "IonBuiler::actionableAbortScript_");
-}
+    MOZ_ASSERT(rootList_);
+    rootList_->trace(trc);
+}
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -1109,17 +1109,23 @@ class IonBuilder
     // Some aborts are actionable (e.g., using an unsupported bytecode). When
     // optimization tracking is enabled, the location and message of the abort
     // are recorded here so they may be propagated to the script's
     // corresponding JitcodeGlobalEntry::BaselineEntry.
     JSScript* actionableAbortScript_;
     jsbytecode* actionableAbortPc_;
     const char* actionableAbortMessage_;
 
+    MRootList* rootList_;
+
   public:
+    void setRootList(MRootList& rootList) {
+        MOZ_ASSERT(!rootList_);
+        rootList_ = &rootList;
+    }
     void clearForBackEnd();
     JSObject* checkNurseryObject(JSObject* obj);
 
     JSScript* script() const { return script_; }
     bool scriptHasIonScript() const { return scriptHasIonScript_; }
 
     CodeGenerator* backgroundCodegen() const { return backgroundCodegen_; }
     void setBackgroundCodegen(CodeGenerator* codegen) { backgroundCodegen_ = codegen; }
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5289,16 +5289,26 @@ InlinePropertyTable::buildTypeSetForFunc
         return nullptr;
     for (size_t i = 0; i < numEntries(); i++) {
         if (entries_[i]->func == func)
             types->addType(TypeSet::ObjectType(entries_[i]->group), alloc);
     }
     return types;
 }
 
+bool
+InlinePropertyTable::appendRoots(MRootList& roots) const
+{
+    for (const Entry* entry : entries_) {
+        if (!entry->appendRoots(roots))
+            return false;
+    }
+    return true;
+}
+
 SharedMem<void*>
 MLoadTypedArrayElementStatic::base() const
 {
     return someTypedArray_->as<TypedArrayObject>().viewDataEither();
 }
 
 size_t
 MLoadTypedArrayElementStatic::length() const
@@ -5374,16 +5384,105 @@ MGetPropertyPolymorphic::mightAlias(cons
         }
 
         return AliasType::MayAlias;
     }
 
     return AliasType::NoAlias;
 }
 
+bool
+MGetPropertyPolymorphic::appendRoots(MRootList& roots) const
+{
+    if (!roots.append(name_))
+        return false;
+
+    for (const PolymorphicEntry& entry : receivers_) {
+        if (!entry.appendRoots(roots))
+            return false;
+    }
+
+    return true;
+}
+
+bool
+MSetPropertyPolymorphic::appendRoots(MRootList& roots) const
+{
+    if (!roots.append(name_))
+        return false;
+
+    for (const PolymorphicEntry& entry : receivers_) {
+        if (!entry.appendRoots(roots))
+            return false;
+    }
+
+    return true;
+}
+
+bool
+MGuardReceiverPolymorphic::appendRoots(MRootList& roots) const
+{
+    for (const ReceiverGuard& guard : receivers_) {
+        if (!roots.append(guard))
+            return false;
+    }
+    return true;
+}
+
+bool
+MDispatchInstruction::appendRoots(MRootList& roots) const
+{
+    for (const Entry& entry : map_) {
+        if (!entry.appendRoots(roots))
+            return false;
+    }
+    return true;
+}
+
+bool
+MObjectGroupDispatch::appendRoots(MRootList& roots) const
+{
+    if (inlinePropertyTable_ && !inlinePropertyTable_->appendRoots(roots))
+        return false;
+    return MDispatchInstruction::appendRoots(roots);
+}
+
+bool
+MFunctionDispatch::appendRoots(MRootList& roots) const
+{
+    return MDispatchInstruction::appendRoots(roots);
+}
+
+bool
+MConstant::appendRoots(MRootList& roots) const
+{
+    switch (type()) {
+      case MIRType::String:
+        return roots.append(toString());
+      case MIRType::Symbol:
+        return roots.append(toSymbol());
+      case MIRType::Object:
+        return roots.append(&toObject());
+      case MIRType::Undefined:
+      case MIRType::Null:
+      case MIRType::Boolean:
+      case MIRType::Int32:
+      case MIRType::Double:
+      case MIRType::Float32:
+      case MIRType::MagicOptimizedArguments:
+      case MIRType::MagicOptimizedOut:
+      case MIRType::MagicHole:
+      case MIRType::MagicIsConstructing:
+      case MIRType::MagicUninitializedLexical:
+        return true;
+      default:
+        MOZ_CRASH("Unexpected type");
+    }
+}
+
 void
 MGetPropertyCache::setBlock(MBasicBlock* block)
 {
     MDefinition::setBlock(block);
     // Track where we started.
     if (!location_.pc) {
         location_.pc = block->trackedPc();
         location_.script = block->info().script();
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1020,16 +1020,92 @@ class MUseDefIterator
     MUse* use() const {
         return *current_;
     }
     MDefinition* def() const {
         return current_->consumer()->toDefinition();
     }
 };
 
+#ifdef DEBUG
+bool
+IonCompilationCanUseNurseryPointers();
+#endif
+
+// Helper class to check that GC pointers embedded in MIR instructions are in
+// in the nursery only when the store buffer has been marked as needing to
+// cancel all ion compilations. Otherwise, off-thread Ion compilation and
+// nursery GCs can happen in parallel, so it's invalid to store pointers to
+// nursery things. There's no need to root these pointers, as GC is suppressed
+// during compilation and off-thread compilations are canceled on major GCs.
+template <typename T>
+class CompilerGCPointer
+{
+    js::gc::Cell* ptr_;
+
+  public:
+    explicit CompilerGCPointer(T ptr)
+      : ptr_(ptr)
+    {
+        MOZ_ASSERT_IF(IsInsideNursery(ptr), IonCompilationCanUseNurseryPointers());
+#ifdef DEBUG
+        PerThreadData* pt = TlsPerThreadData.get();
+        MOZ_ASSERT_IF(pt->runtimeIfOnOwnerThread(), pt->suppressGC);
+#endif
+    }
+
+    operator T() const { return static_cast<T>(ptr_); }
+    T operator->() const { return static_cast<T>(ptr_); }
+
+  private:
+    CompilerGCPointer() = delete;
+    CompilerGCPointer(const CompilerGCPointer<T>&) = delete;
+    CompilerGCPointer<T>& operator=(const CompilerGCPointer<T>&) = delete;
+};
+
+typedef CompilerGCPointer<JSObject*> CompilerObject;
+typedef CompilerGCPointer<NativeObject*> CompilerNativeObject;
+typedef CompilerGCPointer<JSFunction*> CompilerFunction;
+typedef CompilerGCPointer<JSScript*> CompilerScript;
+typedef CompilerGCPointer<PropertyName*> CompilerPropertyName;
+typedef CompilerGCPointer<Shape*> CompilerShape;
+typedef CompilerGCPointer<ObjectGroup*> CompilerObjectGroup;
+
+class MRootList : public TempObject
+{
+  public:
+    using RootVector = Vector<void*, 0, JitAllocPolicy>;
+
+  private:
+    mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit, mozilla::Maybe<RootVector>> roots_;
+
+    MRootList(const MRootList&) = delete;
+    void operator=(const MRootList&) = delete;
+
+  public:
+    explicit MRootList(TempAllocator& alloc);
+
+    void trace(JSTracer* trc);
+
+    template <typename T>
+    MOZ_MUST_USE bool append(T ptr) {
+        if (ptr)
+            return roots_[JS::MapTypeToRootKind<T>::kind]->append(ptr);
+        return true;
+    }
+
+    template <typename T>
+    MOZ_MUST_USE bool append(const CompilerGCPointer<T>& ptr) {
+        return append(static_cast<T>(ptr));
+    }
+    MOZ_MUST_USE bool append(const ReceiverGuard& guard) {
+        return append(guard.group) && append(guard.shape);
+    }
+};
+
 // An instruction is an SSA name that is inserted into a basic block's IR
 // stream.
 class MInstruction
   : public MDefinition,
     public InlineListNode<MInstruction>
 {
     MResumePoint* resumePoint_;
 
@@ -1081,16 +1157,22 @@ class MInstruction
     // resume points.
     virtual bool canClone() const {
         return false;
     }
     virtual MInstruction* clone(TempAllocator& alloc, const MDefinitionVector& inputs) const {
         MOZ_CRASH();
     }
 
+    // MIR instructions containing GC pointers should override this to append
+    // these pointers to the root list.
+    virtual bool appendRoots(MRootList& roots) const {
+        return true;
+    }
+
     // Instructions needing to hook into type analysis should return a
     // TypePolicy.
     virtual TypePolicy* typePolicy() = 0;
     virtual MIRType typePolicySpecialization() = 0;
 };
 
 #define INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode)                       \
     static const Opcode classOpcode = MDefinition::Op_##opcode;             \
@@ -1613,16 +1695,18 @@ class MConstant : public MNullaryInstruc
             return toDouble();
         return toFloat32();
     }
 
     // Convert this constant to a js::Value. Float32 constants will be stored
     // as DoubleValue and NaNs are canonicalized. Callers must be careful: not
     // all constants can be represented by js::Value (wasm supports int64).
     Value toJSValue() const;
+
+    bool appendRoots(MRootList& roots) const override;
 };
 
 // Generic constructor of SIMD valuesX4.
 class MSimdValueX4
   : public MQuaternaryInstruction,
     public Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>,
                       SimdScalarPolicy<2>, SimdScalarPolicy<3> >::Data
 {
@@ -3114,60 +3198,16 @@ TypeSetIncludes(TypeSet* types, MIRType 
 bool
 EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
            MIRType type2, TemporaryTypeSet* typeset2);
 
 bool
 CanStoreUnboxedType(TempAllocator& alloc,
                     JSValueType unboxedType, MIRType input, TypeSet* inputTypes);
 
-#ifdef DEBUG
-bool
-IonCompilationCanUseNurseryPointers();
-#endif
-
-// Helper class to check that GC pointers embedded in MIR instructions are in
-// in the nursery only when the store buffer has been marked as needing to
-// cancel all ion compilations. Otherwise, off-thread Ion compilation and
-// nursery GCs can happen in parallel, so it's invalid to store pointers to
-// nursery things. There's no need to root these pointers, as GC is suppressed
-// during compilation and off-thread compilations are canceled on major GCs.
-template <typename T>
-class CompilerGCPointer
-{
-    js::gc::Cell* ptr_;
-
-  public:
-    explicit CompilerGCPointer(T ptr)
-      : ptr_(ptr)
-    {
-        MOZ_ASSERT_IF(IsInsideNursery(ptr), IonCompilationCanUseNurseryPointers());
-#ifdef DEBUG
-        PerThreadData* pt = TlsPerThreadData.get();
-        MOZ_ASSERT_IF(pt->runtimeIfOnOwnerThread(), pt->suppressGC);
-#endif
-    }
-
-    operator T() const { return static_cast<T>(ptr_); }
-    T operator->() const { return static_cast<T>(ptr_); }
-
-  private:
-    CompilerGCPointer() = delete;
-    CompilerGCPointer(const CompilerGCPointer<T>&) = delete;
-    CompilerGCPointer<T>& operator=(const CompilerGCPointer<T>&) = delete;
-};
-
-typedef CompilerGCPointer<JSObject*> CompilerObject;
-typedef CompilerGCPointer<NativeObject*> CompilerNativeObject;
-typedef CompilerGCPointer<JSFunction*> CompilerFunction;
-typedef CompilerGCPointer<JSScript*> CompilerScript;
-typedef CompilerGCPointer<PropertyName*> CompilerPropertyName;
-typedef CompilerGCPointer<Shape*> CompilerShape;
-typedef CompilerGCPointer<ObjectGroup*> CompilerObjectGroup;
-
 class MNewArray
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
   private:
     // Number of elements to allocate for the array.
     uint32_t length_;
 
@@ -3262,16 +3302,20 @@ class MNewArrayCopyOnWrite : public MNul
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     virtual AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject_);
+    }
 };
 
 class MNewArrayDynamicLength
   : public MUnaryInstruction,
     public IntPolicy<0>::Data
 {
     CompilerObject templateObject_;
     gc::InitialHeap initialHeap_;
@@ -3298,16 +3342,20 @@ class MNewArrayDynamicLength
     }
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     virtual AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject_);
+    }
 };
 
 class MNewTypedArray : public MNullaryInstruction
 {
     CompilerGCPointer<TypedArrayObject*> templateObject_;
     gc::InitialHeap initialHeap_;
 
     MNewTypedArray(CompilerConstraintList* constraints, TypedArrayObject* templateObject,
@@ -3337,16 +3385,20 @@ class MNewTypedArray : public MNullaryIn
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     virtual AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject_);
+    }
 };
 
 class MNewTypedArrayDynamicLength
   : public MUnaryInstruction,
     public IntPolicy<0>::Data
 {
     CompilerObject templateObject_;
     gc::InitialHeap initialHeap_;
@@ -3381,16 +3433,20 @@ class MNewTypedArrayDynamicLength
     }
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     virtual AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject_);
+    }
 };
 
 class MNewObject
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
   public:
     enum Mode { ObjectLiteral, ObjectCreate };
@@ -3482,16 +3538,20 @@ class MNewTypedObject : public MNullaryI
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     virtual AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject_);
+    }
 };
 
 class MTypedObjectDescr
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
   private:
     explicit MTypedObjectDescr(MDefinition* object)
@@ -3575,16 +3635,20 @@ class MSimdBox
         return AliasSet::None();
     }
 
     void printOpcode(GenericPrinter& out) const override;
     MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return true;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject_);
+    }
 };
 
 class MSimdUnbox
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
   protected:
     SimdType simdType_;
@@ -3871,16 +3935,20 @@ class MInitProp
 
     PropertyName* propertyName() const {
         return name_;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MInitPropGetterSetter
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
 {
     CompilerPropertyName name_;
 
@@ -3892,16 +3960,20 @@ class MInitPropGetterSetter
   public:
     INSTRUCTION_HEADER(InitPropGetterSetter)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, object), (1, value))
 
     PropertyName* name() const {
         return name_;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MInitElem
   : public MAryInstruction<3>,
     public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >::Data
 {
     MInitElem(MDefinition* obj, MDefinition* id, MDefinition* value)
     {
@@ -3958,16 +4030,20 @@ class WrappedFunction : public TempObjec
     bool isSelfHostedBuiltin() const { return isSelfHostedBuiltin_; }
 
     // fun->native() and fun->jitInfo() can safely be called off-thread: these
     // fields never change.
     JSNative native() const { return fun_->native(); }
     const JSJitInfo* jitInfo() const { return fun_->jitInfo(); }
 
     JSFunction* rawJSFunction() const { return fun_; }
+
+    bool appendRoots(MRootList& roots) const {
+        return roots.append(fun_);
+    }
 };
 
 class MCall
   : public MVariadicInstruction,
     public CallPolicy::Data
 {
   private:
     // An MCall uses the MPrepareCall, MDefinition for the function, and
@@ -4066,16 +4142,22 @@ class MCall
     }
 
     // A method that can be called to tell the MCall to figure out whether it's
     // movable or not.  This can't be done in the constructor, because it
     // depends on the arguments to the call, and those aren't passed to the
     // constructor but are set up later via addArg.
     virtual void computeMovable() {
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        if (target_)
+            return target_->appendRoots(roots);
+        return true;
+    }
 };
 
 class MCallDOMNative : public MCall
 {
     // A helper class for MCalls for DOM natives.  Note that this is NOT
     // actually a separate MIR op from MCall, because all sorts of places use
     // isCall() to check for calls and all we really want is to overload a few
     // virtual things from MCall.
@@ -4158,16 +4240,22 @@ class MApplyArgs
     // For TI-informed monomorphic callsites.
     WrappedFunction* getSingleTarget() const {
         return target_;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        if (target_)
+            return target_->appendRoots(roots);
+        return true;
+    }
 };
 
 // fun.apply(fn, array)
 class MApplyArray
   : public MAryInstruction<3>,
     public Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2> >::Data
 {
   protected:
@@ -4191,16 +4279,22 @@ class MApplyArray
     // For TI-informed monomorphic callsites.
     WrappedFunction* getSingleTarget() const {
         return target_;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        if (target_)
+            return target_->appendRoots(roots);
+        return true;
+    }
 };
 
 class MBail : public MNullaryInstruction
 {
   protected:
     explicit MBail(BailoutKind kind)
       : MNullaryInstruction()
     {
@@ -4945,16 +5039,20 @@ class MCreateArgumentsObject
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 
     bool possiblyCalls() const override {
         return true;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObj_);
+    }
 };
 
 class MGetArgumentsObjectArg
   : public MUnaryInstruction,
     public ObjectPolicy<0>::Data
 {
     size_t argno_;
 
@@ -7864,16 +7962,19 @@ class MDefVar
     }
     unsigned attrs() const {
         return attrs_;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MDefLexical
   : public MNullaryInstruction
 {
     CompilerPropertyName name_; // Target name to be defined.
     unsigned attrs_; // Attributes to be set.
 
@@ -7888,16 +7989,19 @@ class MDefLexical
     TRIVIAL_NEW_WRAPPERS
 
     PropertyName* name() const {
         return name_;
     }
     unsigned attrs() const {
         return attrs_;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MDefFun
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
     CompilerFunction fun_;
 
@@ -7913,16 +8017,19 @@ class MDefFun
     NAMED_OPERANDS((0, environmentChain))
 
     JSFunction* fun() const {
         return fun_;
     }
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(fun_);
+    }
 };
 
 class MRegExp : public MNullaryInstruction
 {
     CompilerGCPointer<RegExpObject*> source_;
     bool mustClone_;
 
     MRegExp(CompilerConstraintList* constraints, RegExpObject* source)
@@ -7947,16 +8054,19 @@ class MRegExp : public MNullaryInstructi
         return source_;
     }
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(source_);
+    }
 };
 
 class MRegExpMatcher
   : public MAryInstruction<3>,
     public Mix3Policy<ObjectPolicy<0>,
                       StringPolicy<1>,
                       IntPolicy<2> >::Data
 {
@@ -8218,16 +8328,24 @@ struct LambdaFunctionInfo
       : fun(fun), flags(fun->flags()), nargs(fun->nargs()),
         scriptOrLazyScript(fun->hasScript()
                            ? (gc::Cell*) fun->nonLazyScript()
                            : (gc::Cell*) fun->lazyScript()),
         singletonType(fun->isSingleton()),
         useSingletonForClone(ObjectGroup::useSingletonForClone(fun))
     {}
 
+    bool appendRoots(MRootList& roots) const {
+        if (!roots.append(fun))
+            return false;
+        if (fun->hasScript())
+            return roots.append(fun->nonLazyScript());
+        return roots.append(fun->lazyScript());
+    }
+
   private:
     LambdaFunctionInfo(const LambdaFunctionInfo&) = delete;
     void operator=(const LambdaFunctionInfo&) = delete;
 };
 
 class MLambda
   : public MBinaryInstruction,
     public SingleObjectPolicy::Data
@@ -8252,16 +8370,19 @@ class MLambda
     }
     const LambdaFunctionInfo& info() const {
         return info_;
     }
     MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return info_.appendRoots(roots);
+    }
 };
 
 class MLambdaArrow
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>>::Data
 {
     const LambdaFunctionInfo info_;
 
@@ -8278,16 +8399,19 @@ class MLambdaArrow
   public:
     INSTRUCTION_HEADER(LambdaArrow)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, environmentChain), (1, newTargetDef))
 
     const LambdaFunctionInfo& info() const {
         return info_;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return info_.appendRoots(roots);
+    }
 };
 
 // Returns obj->slots.
 class MSlots
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
     explicit MSlots(MDefinition* object)
@@ -9564,16 +9688,19 @@ class MConvertUnboxedObjectToNative
         // in this opcode).
         //
         // Later accesses can assume the object has a native representation
         // and optimize accordingly. Those accesses cannot be reordered before
         // this instruction, however. This is prevented by chaining this
         // instruction with the object itself, in the same way as MBoundsCheck.
         return AliasSet::None();
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(group_);
+    }
 };
 
 // Array.prototype.pop or Array.prototype.shift on a dense array.
 class MArrayPopShift
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
   public:
@@ -9688,16 +9815,19 @@ class MArraySlice
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedType()) |
                                AliasSet::ObjectFields);
     }
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObj_);
+    }
 };
 
 class MArrayJoin
     : public MBinaryInstruction,
       public MixPolicy<ObjectPolicy<0>, StringPolicy<1> >::Data
 {
     MArrayJoin(MDefinition* array, MDefinition* sep)
         : MBinaryInstruction(array, sep)
@@ -9950,16 +10080,20 @@ class MLoadTypedArrayElementStatic
     void setInfallible() {
         fallible_ = false;
     }
 
     void computeRange(TempAllocator& alloc) override;
     bool needTruncation(TruncateKind kind) override;
     bool canProduceFloat32() const override { return accessType() == Scalar::Float32; }
     void collectRangeInfoPreTrunc() override;
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(someTypedArray_);
+    }
 };
 
 // Base class for MIR ops that write unboxed scalar values.
 class StoreUnboxedScalarBase
 {
     Scalar::Type writeType_;
 
   protected:
@@ -10163,16 +10297,20 @@ class MStoreTypedArrayElementStatic :
         return AliasSet::Store(AliasSet::UnboxedElement);
     }
     TruncateKind operandTruncateKind(size_t index) const override;
 
     bool canConsumeFloat32(MUse* use) const override {
         return use == getUseFor(1) && accessType() == Scalar::Float32;
     }
     void collectRangeInfoPreTrunc() override;
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(someTypedArray_);
+    }
 };
 
 // Compute an "effective address", i.e., a compound computation of the form:
 //   base + index * scale + displacement
 class MEffectiveAddress
   : public MBinaryInstruction,
     public NoTypePolicy::Data
 {
@@ -10386,16 +10524,19 @@ class InlinePropertyTable : public TempO
 {
     struct Entry : public TempObject {
         CompilerObjectGroup group;
         CompilerFunction func;
 
         Entry(ObjectGroup* group, JSFunction* func)
           : group(group), func(func)
         { }
+        bool appendRoots(MRootList& roots) const {
+            return roots.append(group) && roots.append(func);
+        }
     };
 
     jsbytecode* pc_;
     MResumePoint* priorResumePoint_;
     Vector<Entry*, 4, JitAllocPolicy> entries_;
 
   public:
     InlinePropertyTable(TempAllocator& alloc, jsbytecode* pc)
@@ -10439,16 +10580,18 @@ class InlinePropertyTable : public TempO
 
     TemporaryTypeSet* buildTypeSetForFunction(JSFunction* func) const;
 
     // Remove targets that vetoed inlining from the InlinePropertyTable.
     void trimTo(const ObjectVector& targets, const BoolVector& choiceSet);
 
     // Ensure that the InlinePropertyTable's domain is a subset of |targets|.
     void trimToTargets(const ObjectVector& targets);
+
+    bool appendRoots(MRootList& roots) const;
 };
 
 class CacheLocationList : public InlineConcatList<CacheLocationList>
 {
   public:
     CacheLocationList()
       : pc(nullptr),
         script(nullptr)
@@ -10533,33 +10676,43 @@ class MGetPropertyCache
         }
         return AliasSet::Store(AliasSet::Any);
     }
 
     void setBlock(MBasicBlock* block) override;
     MOZ_MUST_USE bool updateForReplacement(MDefinition* ins) override;
 
     bool allowDoubleResult() const;
+
+    bool appendRoots(MRootList& roots) const override {
+        if (inlinePropertyTable_)
+            return inlinePropertyTable_->appendRoots(roots);
+        return true;
+    }
+};
+
+struct PolymorphicEntry {
+    // The group and/or shape to guard against.
+    ReceiverGuard receiver;
+
+    // The property to load, null for loads from unboxed properties.
+    Shape* shape;
+
+    bool appendRoots(MRootList& roots) const {
+        return roots.append(receiver) && roots.append(shape);
+    }
 };
 
 // Emit code to load a value from an object if it matches one of the receivers
 // observed by the baseline IC, else bails out.
 class MGetPropertyPolymorphic
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
-    struct Entry {
-        // The group and/or shape to guard against.
-        ReceiverGuard receiver;
-
-        // The property to load, null for loads from unboxed properties.
-        Shape* shape;
-    };
-
-    Vector<Entry, 4, JitAllocPolicy> receivers_;
+    Vector<PolymorphicEntry, 4, JitAllocPolicy> receivers_;
     CompilerPropertyName name_;
 
     MGetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, PropertyName* name)
       : MUnaryInstruction(obj),
         receivers_(alloc),
         name_(name)
     {
         setGuard();
@@ -10579,17 +10732,17 @@ class MGetPropertyPolymorphic
         if (!ins->isGetPropertyPolymorphic())
             return false;
         if (name() != ins->toGetPropertyPolymorphic()->name())
             return false;
         return congruentIfOperandsEqual(ins);
     }
 
     MOZ_MUST_USE bool addReceiver(const ReceiverGuard& receiver, Shape* shape) {
-        Entry entry;
+        PolymorphicEntry entry;
         entry.receiver = receiver;
         entry.shape = shape;
         return receivers_.append(entry);
     }
     size_t numReceivers() const {
         return receivers_.length();
     }
     const ReceiverGuard receiver(size_t i) const {
@@ -10611,33 +10764,27 @@ class MGetPropertyPolymorphic
         }
         return AliasSet::Load(AliasSet::ObjectFields |
                               AliasSet::FixedSlot |
                               AliasSet::DynamicSlot |
                               (hasUnboxedLoad ? AliasSet::UnboxedElement : 0));
     }
 
     AliasType mightAlias(const MDefinition* store) const override;
+
+    bool appendRoots(MRootList& roots) const override;
 };
 
 // Emit code to store a value to an object's slots if its shape/group matches
 // one of the shapes/groups observed by the baseline IC, else bails out.
 class MSetPropertyPolymorphic
   : public MBinaryInstruction,
     public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
 {
-    struct Entry {
-        // The group and/or shape to guard against.
-        ReceiverGuard receiver;
-
-        // The property to store, null for stores to unboxed properties.
-        Shape* shape;
-    };
-
-    Vector<Entry, 4, JitAllocPolicy> receivers_;
+    Vector<PolymorphicEntry, 4, JitAllocPolicy> receivers_;
     CompilerPropertyName name_;
     bool needsBarrier_;
 
     MSetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, MDefinition* value,
                             PropertyName* name)
       : MBinaryInstruction(obj, value),
         receivers_(alloc),
         name_(name),
@@ -10650,17 +10797,17 @@ class MSetPropertyPolymorphic
     NAMED_OPERANDS((0, object), (1, value))
 
     static MSetPropertyPolymorphic* New(TempAllocator& alloc, MDefinition* obj, MDefinition* value,
                                         PropertyName* name) {
         return new(alloc) MSetPropertyPolymorphic(alloc, obj, value, name);
     }
 
     MOZ_MUST_USE bool addReceiver(const ReceiverGuard& receiver, Shape* shape) {
-        Entry entry;
+        PolymorphicEntry entry;
         entry.receiver = receiver;
         entry.shape = shape;
         return receivers_.append(entry);
     }
     size_t numReceivers() const {
         return receivers_.length();
     }
     const ReceiverGuard& receiver(size_t i) const {
@@ -10686,16 +10833,17 @@ class MSetPropertyPolymorphic
                 break;
             }
         }
         return AliasSet::Store(AliasSet::ObjectFields |
                                AliasSet::FixedSlot |
                                AliasSet::DynamicSlot |
                                (hasUnboxedStore ? AliasSet::UnboxedElement : 0));
     }
+    bool appendRoots(MRootList& roots) const override;
 };
 
 class MDispatchInstruction
   : public MControlInstruction,
     public SingleObjectPolicy::Data
 {
     // Map from JSFunction* -> MBasicBlock.
     struct Entry {
@@ -10704,16 +10852,19 @@ class MDispatchInstruction
         // |funcGroup| holds the ObjectGroup for |func|, and dispatch guards
         // on the group instead of directly on the function.
         ObjectGroup* funcGroup;
         MBasicBlock* block;
 
         Entry(JSFunction* func, ObjectGroup* funcGroup, MBasicBlock* block)
           : func(func), funcGroup(funcGroup), block(block)
         { }
+        bool appendRoots(MRootList& roots) const {
+            return roots.append(func) && roots.append(funcGroup);
+        }
     };
     Vector<Entry, 4, JitAllocPolicy> map_;
 
     // An optional fallback path that uses MCall.
     MBasicBlock* fallback_;
     MUse operand_;
 
     void initOperand(size_t index, MDefinition* operand) {
@@ -10798,16 +10949,17 @@ class MDispatchInstruction
     void addFallback(MBasicBlock* block) {
         MOZ_ASSERT(!hasFallback());
         fallback_ = block;
     }
     MBasicBlock* getFallback() const {
         MOZ_ASSERT(hasFallback());
         return fallback_;
     }
+    bool appendRoots(MRootList& roots) const override;
 };
 
 // Polymorphic dispatch for inlining, keyed off incoming ObjectGroup.
 class MObjectGroupDispatch : public MDispatchInstruction
 {
     // Map ObjectGroup (of CallProp's Target Object) -> JSFunction (yielded by the CallProp).
     InlinePropertyTable* inlinePropertyTable_;
 
@@ -10823,31 +10975,33 @@ class MObjectGroupDispatch : public MDis
                                      InlinePropertyTable* table)
     {
         return new(alloc) MObjectGroupDispatch(alloc, ins, table);
     }
 
     InlinePropertyTable* propTable() const {
         return inlinePropertyTable_;
     }
+    bool appendRoots(MRootList& roots) const override;
 };
 
 // Polymorphic dispatch for inlining, keyed off incoming JSFunction*.
 class MFunctionDispatch : public MDispatchInstruction
 {
     MFunctionDispatch(TempAllocator& alloc, MDefinition* input)
       : MDispatchInstruction(alloc, input)
     { }
 
   public:
     INSTRUCTION_HEADER(FunctionDispatch)
 
     static MFunctionDispatch* New(TempAllocator& alloc, MDefinition* ins) {
         return new(alloc) MFunctionDispatch(alloc, ins);
     }
+    bool appendRoots(MRootList& roots) const override;
 };
 
 class MBindNameCache
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
     CompilerPropertyName name_;
     CompilerScript script_;
@@ -10868,16 +11022,20 @@ class MBindNameCache
         return name_;
     }
     JSScript* script() const {
         return script_;
     }
     jsbytecode* pc() const {
         return pc_;
     }
+    bool appendRoots(MRootList& roots) const override {
+        // Don't append the script, all scripts are added anyway.
+        return roots.append(name_);
+    }
 };
 
 class MCallBindVar
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
     explicit MCallBindVar(MDefinition* envChain)
       : MUnaryInstruction(envChain)
@@ -10944,16 +11102,19 @@ class MGuardShape
             return false;
         if (bailoutKind() != ins->toGuardShape()->bailoutKind())
             return false;
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const override {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(shape_);
+    }
 };
 
 // Bail if the object's shape or unboxed group is not in the input list.
 class MGuardReceiverPolymorphic
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
     Vector<ReceiverGuard, 4, JitAllocPolicy> receivers_;
@@ -10986,16 +11147,19 @@ class MGuardReceiverPolymorphic
         return receivers_[i];
     }
 
     bool congruentTo(const MDefinition* ins) const override;
 
     AliasSet getAliasSet() const override {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
+
+    bool appendRoots(MRootList& roots) const override;
+
 };
 
 // Guard on an object's group, inclusively or exclusively.
 class MGuardObjectGroup
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
     CompilerObjectGroup group_;
@@ -11042,16 +11206,19 @@ class MGuardObjectGroup
             return false;
         if (bailoutKind() != ins->toGuardObjectGroup()->bailoutKind())
             return false;
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const override {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(group_);
+    }
 };
 
 // Guard on an object's identity, inclusively or exclusively.
 class MGuardObjectIdentity
   : public MBinaryInstruction,
     public SingleObjectPolicy::Data
 {
     bool bailOnEquality_;
@@ -11343,16 +11510,19 @@ class MGetNameCache
     NAMED_OPERANDS((0, envObj))
 
     PropertyName* name() const {
         return name_;
     }
     AccessKind accessKind() const {
         return kind_;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MCallGetIntrinsicValue : public MNullaryInstruction
 {
     CompilerPropertyName name_;
 
     explicit MCallGetIntrinsicValue(PropertyName* name)
       : name_(name)
@@ -11368,16 +11538,19 @@ class MCallGetIntrinsicValue : public MN
         return name_;
     }
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MSetPropertyInstruction : public MBinaryInstruction
 {
     CompilerPropertyName name_;
     bool strict_;
 
   protected:
@@ -11390,16 +11563,19 @@ class MSetPropertyInstruction : public M
   public:
     NAMED_OPERANDS((0, object), (1, value))
     PropertyName* name() const {
         return name_;
     }
     bool strict() const {
         return strict_;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MSetElementInstruction
   : public MTernaryInstruction
 {
     bool strict_;
   protected:
     MSetElementInstruction(MDefinition* object, MDefinition* index, MDefinition* value, bool strict)
@@ -11437,16 +11613,19 @@ class MDeleteProperty
     NAMED_OPERANDS((0, value))
 
     PropertyName* name() const {
         return name_;
     }
     bool strict() const {
         return strict_;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 class MDeleteElement
   : public MBinaryInstruction,
     public BoxInputsPolicy::Data
 {
     bool strict_;
 
@@ -11554,16 +11733,19 @@ class MCallGetProperty
     AliasSet getAliasSet() const override {
         if (!idempotent_)
             return AliasSet::Store(AliasSet::Any);
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(name_);
+    }
 };
 
 // Inline call to handle lhs[rhs]. The first input is a Value so that this
 // instruction can handle both objects and strings.
 class MCallGetElement
   : public MBinaryInstruction,
     public BoxInputsPolicy::Data
 {
@@ -12137,16 +12319,20 @@ class MInstanceOf
 
   public:
     INSTRUCTION_HEADER(InstanceOf)
     TRIVIAL_NEW_WRAPPERS
 
     JSObject* prototypeObject() {
         return protoObj_;
     }
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(protoObj_);
+    }
 };
 
 // Implementation for instanceof operator with unknown rhs.
 class MCallInstanceOf
   : public MBinaryInstruction,
     public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >::Data
 {
     MCallInstanceOf(MDefinition* obj, MDefinition* proto)
@@ -12311,16 +12497,19 @@ class MRest
     NAMED_OPERANDS((0, numActuals))
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObject());
+    }
 };
 
 class MFilterTypeSet
   : public MUnaryInstruction,
     public FilterTypeSetPolicy::Data
 {
     MFilterTypeSet(MDefinition* def, TemporaryTypeSet* types)
       : MUnaryInstruction(def)
@@ -12529,16 +12718,19 @@ class MNewNamedLambdaObject : public MNu
     TRIVIAL_NEW_WRAPPERS
 
     LexicalEnvironmentObject* templateObj() {
         return templateObj_;
     }
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObj_);
+    }
 };
 
 class MNewCallObjectBase : public MNullaryInstruction
 {
     CompilerGCPointer<CallObject*> templateObj_;
 
   protected:
     explicit MNewCallObjectBase(CallObject* templateObj)
@@ -12550,16 +12742,19 @@ class MNewCallObjectBase : public MNulla
 
   public:
     CallObject* templateObject() {
         return templateObj_;
     }
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObj_);
+    }
 };
 
 class MNewCallObject : public MNewCallObjectBase
 {
   public:
     INSTRUCTION_HEADER(NewCallObject)
 
     explicit MNewCallObject(CallObject* templateObj)
@@ -12604,16 +12799,20 @@ class MNewStringObject :
         setResultType(MIRType::Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewStringObject)
     TRIVIAL_NEW_WRAPPERS
 
     StringObject* templateObj() const;
+
+    bool appendRoots(MRootList& roots) const override {
+        return roots.append(templateObj_);
+    }
 };
 
 // This is an alias for MLoadFixedSlot.
 class MEnclosingEnvironment : public MLoadFixedSlot
 {
     explicit MEnclosingEnvironment(MDefinition* obj)
       : MLoadFixedSlot(obj, EnvironmentObject::enclosingEnvironmentSlot())
     {
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1776,16 +1776,30 @@ GlobalHelperThreadState::compressionTask
 void
 GlobalHelperThreadState::trace(JSTracer* trc)
 {
     AutoLockHelperThreadState lock;
     for (auto builder : ionWorklist(lock))
         builder->trace(trc);
     for (auto builder : ionFinishedList(lock))
         builder->trace(trc);
+
+    if (HelperThreadState().threads) {
+        for (auto& helper : *HelperThreadState().threads) {
+            if (auto builder = helper.ionBuilder())
+                builder->trace(trc);
+        }
+    }
+
+    jit::IonBuilder* builder = trc->runtime()->ionLazyLinkList().getFirst();
+    while (builder) {
+        builder->trace(trc);
+        builder = builder->getNext();
+    }
+
     for (auto parseTask : parseWorklist_)
         parseTask->trace(trc);
     for (auto parseTask : parseFinishedList_)
         parseTask->trace(trc);
     for (auto parseTask : parseWaitingOnGC_)
         parseTask->trace(trc);
 }