Bug 1301343 - Trace pointers stored in MIR. r=jonco, r=nbp, a=ritu
authorJan de Mooij <jdemooij@mozilla.com>
Sun, 02 Oct 2016 15:49:57 +0200
changeset 355949 edbfbd7da7d0a1efc21393125e445dda533f8715
parent 355948 a9ba2aa61f5b9f5cbc25ed8431bb41f2d0c47703
child 355950 43c3d294f63cd8e49fca491a686f729cfd66a365
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco, nbp, ritu
bugs1301343
milestone51.0a2
Bug 1301343 - Trace pointers stored in MIR. r=jonco, r=nbp, a=ritu
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
@@ -2279,16 +2279,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),
@@ -14620,12 +14621,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
@@ -5284,16 +5284,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
@@ -5369,16 +5379,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;             \
@@ -1605,16 +1687,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
 {
@@ -3108,60 +3192,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_;
 
@@ -3256,16 +3296,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_;
@@ -3292,16 +3336,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,
@@ -3331,16 +3379,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_;
@@ -3375,16 +3427,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 };
@@ -3476,16 +3532,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)
@@ -3569,16 +3629,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_;
@@ -3865,16 +3929,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_;
 
@@ -3886,16 +3954,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)
     {
@@ -3952,16 +4024,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
@@ -4060,16 +4136,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.
@@ -4152,16 +4234,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:
@@ -4185,16 +4273,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()
     {
@@ -4939,16 +5033,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_;
 
@@ -7858,16 +7956,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.
 
@@ -7882,16 +7983,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_;
 
@@ -7907,16 +8011,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)
@@ -7941,16 +8048,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
 {
@@ -8212,16 +8322,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
@@ -8246,16 +8364,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_;
 
@@ -8272,16 +8393,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)
@@ -9558,16 +9682,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:
@@ -9682,16 +9809,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)
@@ -9944,16 +10074,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:
@@ -10157,16 +10291,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
 {
@@ -10380,16 +10518,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)
@@ -10433,16 +10574,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)
@@ -10527,33 +10670,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();
@@ -10573,17 +10726,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 {
@@ -10605,33 +10758,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),
@@ -10644,17 +10791,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 {
@@ -10680,16 +10827,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 {
@@ -10698,16 +10846,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) {
@@ -10792,16 +10943,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_;
 
@@ -10817,31 +10969,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_;
@@ -10862,16 +11016,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)
@@ -10938,16 +11096,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_;
@@ -10980,16 +11141,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_;
@@ -11036,16 +11200,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_;
@@ -11337,16 +11504,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)
@@ -11362,16 +11532,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:
@@ -11384,16 +11557,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)
@@ -11431,16 +11607,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_;
 
@@ -11548,16 +11727,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
 {
@@ -12131,16 +12313,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)
@@ -12305,16 +12491,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)
@@ -12523,16 +12712,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)
@@ -12544,16 +12736,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)
@@ -12598,16 +12793,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
@@ -1688,16 +1688,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);
 }