Bug 1010586 - IonMonkey: Make MUse enforce use-list invariants automatically. r=nbp
authorDan Gohman <sunfish@mozilla.com>
Tue, 20 May 2014 18:43:16 -0700
changeset 184048 8eaecba43dc1c67454049fa191ae3dfa780ae238
parent 184047 13a2d22d5eb3048118a97212dd86838218e47a79
child 184049 29a86e1eb5d2328d97fed43808a207a68d8e1b81
push id26810
push usercbook@mozilla.com
push dateWed, 21 May 2014 11:46:36 +0000
treeherdermozilla-central@50fb8c4db2fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1010586
milestone32.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 1010586 - IonMonkey: Make MUse enforce use-list invariants automatically. r=nbp
js/src/jit/IonAnalysis.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MIRGraph.cpp
js/src/jit/RangeAnalysis.cpp
js/src/jit/UnreachableCodeElimination.cpp
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -135,41 +135,39 @@ jit::EliminateDeadResumePointOperands(MI
                 maxDefinition = Max(maxDefinition, def->id());
             }
             if (maxDefinition == UINT32_MAX)
                 continue;
 
             // Walk the uses a second time, removing any in resume points after
             // the last use in a definition.
             for (MUseIterator uses(ins->usesBegin()); uses != ins->usesEnd(); ) {
-                if (uses->consumer()->isDefinition()) {
-                    uses++;
+                MUse *use = *uses++;
+                if (use->consumer()->isDefinition())
                     continue;
-                }
-                MResumePoint *mrp = uses->consumer()->toResumePoint();
+                MResumePoint *mrp = use->consumer()->toResumePoint();
                 if (mrp->block() != *block ||
                     !mrp->instruction() ||
                     mrp->instruction() == *ins ||
                     mrp->instruction()->id() <= maxDefinition)
                 {
-                    uses++;
                     continue;
                 }
 
                 // Store an optimized out magic value in place of all dead
                 // resume point operands. Making any such substitution can in
                 // general alter the interpreter's behavior, even though the
                 // code is dead, as the interpreter will still execute opcodes
                 // whose effects cannot be observed. If the magic value value
                 // were to flow to, say, a dead property access the
                 // interpreter could throw an exception; we avoid this problem
                 // by removing dead operands before removing dead code.
                 MConstant *constant = MConstant::New(graph.alloc(), MagicValue(JS_OPTIMIZED_OUT));
                 block->insertBefore(*(block->begin()), constant);
-                uses = mrp->replaceOperand(uses, constant);
+                use->replaceProducer(constant);
             }
         }
     }
 
     return true;
 }
 
 // Instructions are useless if they are unused and have no side effects.
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -25,20 +25,22 @@
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::NumbersAreIdentical;
 using mozilla::IsFloat32Representable;
 using mozilla::Maybe;
 
+#ifdef DEBUG
 size_t MUse::index() const
 {
     return consumer()->indexOf(this);
 }
+#endif
 
 template<size_t Op> static void
 ConvertDefinitionToDouble(TempAllocator &alloc, MDefinition *def, MInstruction *consumer)
 {
     MInstruction *replace = MToDouble::New(alloc, def);
     consumer->replaceOperand(Op, replace);
     consumer->block()->insertBefore(consumer, replace);
 }
@@ -380,88 +382,30 @@ MDefinition::hasLiveDefUses() const
             continue;
         if (!ins->toDefinition()->isRecoveredOnBailout())
             return true;
     }
 
     return false;
 }
 
-MUseIterator
-MDefinition::removeUse(MUseIterator use)
-{
-    return uses_.removeAt(use);
-}
-
-MUseIterator
-MNode::replaceOperand(MUseIterator use, MDefinition *def)
-{
-    JS_ASSERT(def != nullptr);
-    uint32_t index = use->index();
-    MDefinition *prev = use->producer();
-
-    JS_ASSERT(use->producer() == getOperand(index));
-    JS_ASSERT(use->consumer() == this);
-
-    if (prev == def)
-        return use;
-
-    MUseIterator result(prev->removeUse(use));
-    setOperand(index, def);
-    return result;
-}
-
-void
-MNode::replaceOperand(size_t index, MDefinition *def)
-{
-    JS_ASSERT(def != nullptr);
-    MUse *use = getUseFor(index);
-    MDefinition *prev = use->producer();
-
-    JS_ASSERT(use->index() == index);
-    JS_ASSERT(use->producer() == getOperand(index));
-    JS_ASSERT(use->consumer() == this);
-
-    if (prev == def)
-        return;
-
-    prev->removeUse(use);
-    setOperand(index, def);
-}
-
-void
-MNode::discardOperand(size_t index)
-{
-    MUse *use = getUseFor(index);
-
-    JS_ASSERT(use->index() == index);
-    JS_ASSERT(use->producer() == getOperand(index));
-    JS_ASSERT(use->consumer() == this);
-
-    use->producer()->removeUse(use);
-
-#ifdef DEBUG
-    // Causes any producer/consumer lookups to trip asserts.
-    use->set(nullptr, nullptr);
-#endif
-}
-
 void
 MDefinition::replaceAllUsesWith(MDefinition *dom)
 {
     JS_ASSERT(dom != nullptr);
     if (dom == this)
         return;
 
     for (size_t i = 0, e = numOperands(); i < e; i++)
         getOperand(i)->setUseRemovedUnchecked();
 
     for (MUseIterator i(usesBegin()); i != usesEnd(); ) {
-        JS_ASSERT(i->producer() == this);
-        i = i->consumer()->replaceOperand(i, dom);
+        MUse *use = *i++;
+        JS_ASSERT(use->producer() == this);
+        use->replaceProducer(dom);
     }
 }
 
 bool
 MDefinition::emptyResultTypeSet() const
 {
     return resultTypeSet() && resultTypeSet()->empty();
 }
@@ -966,42 +910,41 @@ MTypeBarrier::printOpcode(FILE *fp) cons
     PrintOpcodeName(fp, op());
     fprintf(fp, " ");
     getOperand(0)->printName(fp);
 }
 
 void
 MPhi::removeOperand(size_t index)
 {
-    MUse *use = getUseFor(index);
-
-    JS_ASSERT(index < inputs_.length());
-    JS_ASSERT(inputs_.length() > 1);
-
-    JS_ASSERT(use->index() == index);
-    JS_ASSERT(use->producer() == getOperand(index));
-    JS_ASSERT(use->consumer() == this);
-
-    // Remove use from producer's use chain.
-    use->producer()->removeUse(use);
+    JS_ASSERT(index < numOperands());
+    JS_ASSERT(numOperands() > 1);
+    JS_ASSERT(getUseFor(index)->index() == index);
+    JS_ASSERT(getUseFor(index)->consumer() == this);
 
     // If we have phi(..., a, b, c, d, ..., z) and we plan
     // on removing a, then first shift downward so that we have
     // phi(..., b, c, d, ..., z, z):
     size_t length = inputs_.length();
-    for (size_t i = index; i < length - 1; i++) {
-        MUse *next = MPhi::getUseFor(i + 1);
-        next->producer()->removeUse(next);
-        MPhi::setOperand(i, next->producer());
-    }
+    for (size_t i = index; i < length - 1; i++)
+        inputs_[i].replaceProducer(inputs_[i + 1].producer());
 
     // truncate the inputs_ list:
+    inputs_[length - 1].discardProducer();
     inputs_.shrinkBy(1);
 }
 
+void
+MPhi::removeAllOperands()
+{
+    for (size_t i = 0; i < inputs_.length(); i++)
+        inputs_[i].discardProducer();
+    inputs_.clear();
+}
+
 MDefinition *
 MPhi::operandIfRedundant()
 {
     JS_ASSERT(inputs_.length() != 0);
 
     // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)),
     // returns the operand that it will always be equal to (a, in
     // those two cases).
@@ -1198,19 +1141,18 @@ MPhi::typeIncludes(MDefinition *def)
 
 void
 MPhi::addInput(MDefinition *ins)
 {
     // This can only been done if the length was reserved through reserveLength,
     // else the slower addInputSlow need to get called.
     JS_ASSERT(inputs_.length() < capacity_);
 
-    uint32_t index = inputs_.length();
     inputs_.append(MUse());
-    MPhi::setOperand(index, ins);
+    inputs_.back().init(ins, this);
 }
 
 bool
 MPhi::addInputSlow(MDefinition *ins, bool *ptypeChange)
 {
     // The list of inputs to an MPhi is given as a vector of MUse nodes,
     // each of which is in the list of the producer MDefinition.
     // Because appending to a vector may reallocate the vector, it is possible
@@ -1227,17 +1169,17 @@ MPhi::addInputSlow(MDefinition *ins, boo
             use->producer()->removeUse(use);
         }
     }
 
     // Insert the new input.
     if (!inputs_.append(MUse()))
         return false;
 
-    MPhi::setOperand(index, ins);
+    inputs_.back().init(ins, this);
 
     if (ptypeChange) {
         MIRType resultType = this->type();
         types::TemporaryTypeSet *resultTypeSet = this->resultTypeSet();
 
         if (!MergeTypes(&resultType, &resultTypeSet, ins->type(), ins->resultTypeSet()))
             return false;
 
@@ -1259,17 +1201,18 @@ MPhi::addInputSlow(MDefinition *ins, boo
     return true;
 }
 
 void
 MCall::addArg(size_t argnum, MDefinition *arg)
 {
     // The operand vector is initialized in reverse order by the IonBuilder.
     // It cannot be checked for consistency until all arguments are added.
-    setOperand(argnum + NumNonArgumentOperands, arg);
+    // FixedList doesn't initialize its elements, so do an unchecked init.
+    initOperand(argnum + NumNonArgumentOperands, arg);
 }
 
 void
 MBitNot::infer()
 {
     if (getOperand(0)->mightBeType(MIRType_Object))
         specialization_ = MIRType_None;
     else
@@ -2322,32 +2265,35 @@ MResumePoint::New(TempAllocator &alloc, 
         return nullptr;
     resume->inherit(block);
     return resume;
 }
 
 MResumePoint::MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *caller,
                            Mode mode)
   : MNode(block),
-    stackDepth_(block->stackDepth()),
     pc_(pc),
     caller_(caller),
     instruction_(nullptr),
     mode_(mode)
 {
     block->addResumePoint(this);
 }
 
+bool MResumePoint::init(TempAllocator &alloc)
+{
+    return operands_.init(alloc, block()->stackDepth());
+}
+
 void
 MResumePoint::inherit(MBasicBlock *block)
 {
-    for (size_t i = 0; i < stackDepth(); i++) {
-        MDefinition *def = block->getSlot(i);
-        setOperand(i, def);
-    }
+    // FixedList doesn't initialize its elements, so do unchecked inits.
+    for (size_t i = 0; i < stackDepth(); i++)
+        initOperand(i, block->getSlot(i));
 }
 
 void
 MResumePoint::dump(FILE *fp) const
 {
     fprintf(fp, "resumepoint mode=");
 
     switch (mode()) {
@@ -3098,20 +3044,21 @@ MAsmJSCall::New(TempAllocator &alloc, co
 
     if (!call->argRegs_.init(alloc, args.length()))
         return nullptr;
     for (size_t i = 0; i < call->argRegs_.length(); i++)
         call->argRegs_[i] = args[i].reg;
 
     if (!call->operands_.init(alloc, call->argRegs_.length() + (callee.which() == Callee::Dynamic ? 1 : 0)))
         return nullptr;
+    // FixedList doesn't initialize its elements, so do an unchecked init.
     for (size_t i = 0; i < call->argRegs_.length(); i++)
-        call->setOperand(i, args[i].def);
+        call->initOperand(i, args[i].def);
     if (callee.which() == Callee::Dynamic)
-        call->setOperand(call->argRegs_.length(), callee.dynamic());
+        call->initOperand(call->argRegs_.length(), callee.dynamic());
 
     return call;
 }
 
 void
 MSqrt::trySpecializeFloat32(TempAllocator &alloc) {
     if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) {
         if (input()->type() == MIRType_Float32)
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -121,38 +121,45 @@ class MUse : public TempObject, public I
     { }
 
   public:
     // Default constructor for use in vectors.
     MUse()
       : producer_(nullptr), consumer_(nullptr)
     { }
 
-    // Set data inside the MUse.
-    void set(MDefinition *producer, MNode *consumer) {
-        producer_ = producer;
-        consumer_ = consumer;
-    }
+    // Set this use, which was previously clear.
+    inline void init(MDefinition *producer, MNode *consumer);
+    // Like init, but works even when the use contains uninitialized data.
+    inline void initUnchecked(MDefinition *producer, MNode *consumer);
+    // Like initUnchecked, but set the producer to nullptr.
+    inline void initUncheckedWithoutProducer(MNode *consumer);
+    // Set this use, which was not previously clear.
+    inline void replaceProducer(MDefinition *producer);
+    // Clear this use.
+    inline void discardProducer();
 
     MDefinition *producer() const {
         JS_ASSERT(producer_ != nullptr);
         return producer_;
     }
     bool hasProducer() const {
         return producer_ != nullptr;
     }
     MNode *consumer() const {
         JS_ASSERT(consumer_ != nullptr);
         return consumer_;
     }
 
-    // Return the operand index of this MUse in its consumer. In general,
-    // code should prefer to call indexOf on the casted consumer directly,
-    // to allow it to be devirtualized and inlined.
+#ifdef DEBUG
+    // Return the operand index of this MUse in its consumer. This is DEBUG-only
+    // as normal code should instead to call indexOf on the casted consumer
+    // directly, to allow it to be devirtualized and inlined.
     size_t index() const;
+#endif
 };
 
 typedef InlineList<MUse>::iterator MUseIterator;
 
 // A node is an entry in the MIR graph. It has two kinds:
 //   MInstruction: an instruction which appears in the IR stream.
 //   MResumePoint: a list of instructions that correspond to the state of the
 //                 interpreter/Baseline stack.
@@ -198,38 +205,35 @@ class MNode : public TempObject
     }
 
     // Instructions needing to hook into type analysis should return a
     // TypePolicy.
     virtual TypePolicy *typePolicy() {
         return nullptr;
     }
 
-    // Replaces an already-set operand during iteration over a use chain.
-    MUseIterator replaceOperand(MUseIterator use, MDefinition *ins);
-
-    // Replaces an already-set operand, updating use information.
-    void replaceOperand(size_t index, MDefinition *ins);
+    // Sets an already set operand, updating use information. If you're looking
+    // for setOperand, this is probably what you want.
+    virtual void replaceOperand(size_t index, MDefinition *operand) = 0;
 
     // Resets the operand to an uninitialized state, breaking the link
     // with the previous operand's producer.
-    void discardOperand(size_t index);
+    void discardOperand(size_t index) {
+        getUseFor(index)->discardProducer();
+    }
 
     inline MDefinition *toDefinition();
     inline MResumePoint *toResumePoint();
 
     virtual bool writeRecoverData(CompactBufferWriter &writer) const;
 
     virtual void dump(FILE *fp) const = 0;
     virtual void dump() const = 0;
 
   protected:
-    // Sets an unset operand, updating use information.
-    virtual void setOperand(size_t index, MDefinition *operand) = 0;
-
     // Gets the MUse corresponding to given operand.
     virtual MUse *getUseFor(size_t index) = 0;
     virtual const MUse *getUseFor(size_t index) const = 0;
 };
 
 class AliasSet {
   private:
     uint32_t flags_;
@@ -579,17 +583,16 @@ class MDefinition : public MNode
         return uses_.end();
     }
 
     bool canEmitAtUses() const {
         return !isEmittedAtUses();
     }
 
     // Removes a use at the given position
-    MUseIterator removeUse(MUseIterator use);
     void removeUse(MUse *use) {
         uses_.remove(use);
     }
 
 #ifdef DEBUG
     // Number of uses of this instruction. This function is only available
     // in DEBUG mode since it requires traversing the list. Most users should
     // use hasUses() or hasOneUse() instead.
@@ -617,16 +620,19 @@ class MDefinition : public MNode
     // (only counting MDefinitions, ignoring MResumePoints)
     bool hasLiveDefUses() const;
 
     bool hasUses() const {
         return !uses_.empty();
     }
 
     void addUse(MUse *use) {
+        // The use can't be in the list at all, but we only check the first
+        // element for now, as that's where it's most likely to be.
+        MOZ_ASSERT(uses_.empty() || use != *uses_.begin());
         uses_.pushFront(use);
     }
     void replaceAllUsesWith(MDefinition *dom);
 
     // Mark this instruction as having replaced all uses of ins, as during GVN,
     // returning false if the replacement should not be performed. For use when
     // GVN eliminates instructions which are not equivalent to one another.
     virtual bool updateForReplacement(MDefinition *ins) {
@@ -774,70 +780,70 @@ class MInstruction
     }                                                                       \
     bool accept(MInstructionVisitor *visitor) {                             \
         return visitor->visit##opcode(this);                                \
     }
 
 template <size_t Arity>
 class MAryInstruction : public MInstruction
 {
+    mozilla::Array<MUse, Arity> operands_;
+
   protected:
-    mozilla::Array<MUse, Arity> operands_;
-
-    void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
-        operands_[index].set(operand, this);
-        operand->addUse(&operands_[index]);
-    }
-
     MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE {
         return &operands_[index];
     }
-
     const MUse *getUseFor(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
         return &operands_[index];
     }
+    void initOperand(size_t index, MDefinition *operand) {
+        operands_[index].init(operand, this);
+    }
 
   public:
     MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
         return operands_[index].producer();
     }
     size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE {
         return Arity;
     }
     size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
         MOZ_ASSERT(u >= &operands_[0]);
         MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
         return u - &operands_[0];
     }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        operands_[index].replaceProducer(operand);
+    }
 };
 
 class MNullaryInstruction : public MAryInstruction<0>
 { };
 
 class MUnaryInstruction : public MAryInstruction<1>
 {
   protected:
     MUnaryInstruction(MDefinition *ins)
     {
-        setOperand(0, ins);
+        initOperand(0, ins);
     }
 
   public:
     MDefinition *input() const {
         return getOperand(0);
     }
 };
 
 class MBinaryInstruction : public MAryInstruction<2>
 {
   protected:
     MBinaryInstruction(MDefinition *left, MDefinition *right)
     {
-        setOperand(0, left);
-        setOperand(1, right);
+        initOperand(0, left);
+        initOperand(1, right);
     }
 
   public:
     MDefinition *lhs() const {
         return getOperand(0);
     }
     MDefinition *rhs() const {
         return getOperand(1);
@@ -897,19 +903,19 @@ class MBinaryInstruction : public MAryIn
     bool tryUseUnsignedOperands();
 };
 
 class MTernaryInstruction : public MAryInstruction<3>
 {
   protected:
     MTernaryInstruction(MDefinition *first, MDefinition *second, MDefinition *third)
     {
-        setOperand(0, first);
-        setOperand(1, second);
-        setOperand(2, third);
+        initOperand(0, first);
+        initOperand(1, second);
+        initOperand(2, third);
     }
 
   protected:
     HashNumber valueHash() const
     {
         MDefinition *first = getOperand(0);
         MDefinition *second = getOperand(1);
         MDefinition *third = getOperand(2);
@@ -919,20 +925,20 @@ class MTernaryInstruction : public MAryI
 };
 
 class MQuaternaryInstruction : public MAryInstruction<4>
 {
   protected:
     MQuaternaryInstruction(MDefinition *first, MDefinition *second,
                            MDefinition *third, MDefinition *fourth)
     {
-        setOperand(0, first);
-        setOperand(1, second);
-        setOperand(2, third);
-        setOperand(3, fourth);
+        initOperand(0, first);
+        initOperand(1, second);
+        initOperand(2, third);
+        initOperand(3, fourth);
     }
 
   protected:
     HashNumber valueHash() const
     {
         MDefinition *first = getOperand(0);
         MDefinition *second = getOperand(1);
         MDefinition *third = getOperand(2);
@@ -1161,34 +1167,33 @@ class MTableSwitch MOZ_FINAL
 
     // Contains the blocks/cases that still need to get build
     Vector<MBasicBlock*, 0, IonAllocPolicy> blocks_;
 
     MUse operand_;
     int32_t low_;
     int32_t high_;
 
+    void initOperand(size_t index, MDefinition *operand) {
+        JS_ASSERT(index == 0);
+        operand_.init(operand, this);
+    }
+
     MTableSwitch(TempAllocator &alloc, MDefinition *ins,
                  int32_t low, int32_t high)
       : successors_(alloc),
         cases_(alloc),
         blocks_(alloc),
         low_(low),
         high_(high)
     {
-        setOperand(0, ins);
+        initOperand(0, ins);
     }
 
   protected:
-    void setOperand(size_t index, MDefinition *operand) {
-        JS_ASSERT(index == 0);
-        operand_.set(operand, this);
-        operand->addUse(&operand_);
-    }
-
     MUse *getUseFor(size_t index) {
         JS_ASSERT(index == 0);
         return &operand_;
     }
 
     const MUse *getUseFor(size_t index) const {
         JS_ASSERT(index == 0);
         return &operand_;
@@ -1275,56 +1280,62 @@ class MTableSwitch MOZ_FINAL
         return 1;
     }
 
     size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
         MOZ_ASSERT(u == getUseFor(0));
         return 0;
     }
 
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        JS_ASSERT(index == 0);
+        operand_.replaceProducer(operand);
+    }
+
     TypePolicy *typePolicy() {
         return this;
     }
 };
 
 template <size_t Arity, size_t Successors>
 class MAryControlInstruction : public MControlInstruction
 {
     mozilla::Array<MUse, Arity> operands_;
     mozilla::Array<MBasicBlock *, Successors> successors_;
 
   protected:
-    void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
-        operands_[index].set(operand, this);
-        operand->addUse(&operands_[index]);
-    }
     void setSuccessor(size_t index, MBasicBlock *successor) {
         successors_[index] = successor;
     }
 
     MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE {
         return &operands_[index];
     }
-
     const MUse *getUseFor(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
         return &operands_[index];
     }
+    void initOperand(size_t index, MDefinition *operand) {
+        operands_[index].init(operand, this);
+    }
 
   public:
     MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
         return operands_[index].producer();
     }
     size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE {
         return Arity;
     }
     size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
         MOZ_ASSERT(u >= &operands_[0]);
         MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
         return u - &operands_[0];
     }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        operands_[index].replaceProducer(operand);
+    }
     size_t numSuccessors() const MOZ_FINAL MOZ_OVERRIDE {
         return Successors;
     }
     MBasicBlock *getSuccessor(size_t i) const MOZ_FINAL MOZ_OVERRIDE {
         return successors_[i];
     }
     void replaceSuccessor(size_t i, MBasicBlock *succ) MOZ_FINAL MOZ_OVERRIDE {
         successors_[i] = succ;
@@ -1367,17 +1378,17 @@ class MTest
   : public MAryControlInstruction<1, 2>,
     public TestPolicy
 {
     bool operandMightEmulateUndefined_;
 
     MTest(MDefinition *ins, MBasicBlock *if_true, MBasicBlock *if_false)
       : operandMightEmulateUndefined_(true)
     {
-        setOperand(0, ins);
+        initOperand(0, ins);
         setSuccessor(0, if_true);
         setSuccessor(1, if_false);
     }
 
   public:
     INSTRUCTION_HEADER(Test)
     static MTest *New(TempAllocator &alloc, MDefinition *ins,
                       MBasicBlock *ifTrue, MBasicBlock *ifFalse);
@@ -1426,17 +1437,17 @@ class MTest
 };
 
 // Returns from this function to the previous caller.
 class MReturn
   : public MAryControlInstruction<1, 0>,
     public BoxInputsPolicy
 {
     MReturn(MDefinition *ins) {
-        setOperand(0, ins);
+        initOperand(0, ins);
     }
 
   public:
     INSTRUCTION_HEADER(Return)
     static MReturn *New(TempAllocator &alloc, MDefinition *ins) {
         return new(alloc) MReturn(ins);
     }
 
@@ -1451,17 +1462,17 @@ class MReturn
     }
 };
 
 class MThrow
   : public MAryControlInstruction<1, 0>,
     public BoxInputsPolicy
 {
     MThrow(MDefinition *ins) {
-        setOperand(0, ins);
+        initOperand(0, ins);
     }
 
   public:
     INSTRUCTION_HEADER(Throw)
     static MThrow *New(TempAllocator &alloc, MDefinition *ins) {
         return new(alloc) MThrow(ins);
     }
 
@@ -1734,18 +1745,18 @@ class MAbortPar : public MAryControlInst
 // Setting __proto__ in an object literal.
 class MMutateProto
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
 {
   protected:
     MMutateProto(MDefinition *obj, MDefinition *value)
     {
-        setOperand(0, obj);
-        setOperand(1, value);
+        initOperand(0, obj);
+        initOperand(1, value);
         setResultType(MIRType_None);
     }
 
   public:
     INSTRUCTION_HEADER(MutateProto)
 
     static MMutateProto *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value)
     {
@@ -1774,18 +1785,18 @@ class MInitProp
 {
   public:
     CompilerRootPropertyName name_;
 
   protected:
     MInitProp(MDefinition *obj, PropertyName *name, MDefinition *value)
       : name_(name)
     {
-        setOperand(0, obj);
-        setOperand(1, value);
+        initOperand(0, obj);
+        initOperand(1, value);
         setResultType(MIRType_None);
     }
 
   public:
     INSTRUCTION_HEADER(InitProp)
 
     static MInitProp *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name,
                           MDefinition *value)
@@ -1846,19 +1857,19 @@ class MInitPropGetterSetter
 };
 
 class MInitElem
   : public MAryInstruction<3>,
     public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >
 {
     MInitElem(MDefinition *obj, MDefinition *id, MDefinition *value)
     {
-        setOperand(0, obj);
-        setOperand(1, id);
-        setOperand(2, value);
+        initOperand(0, obj);
+        initOperand(1, id);
+        initOperand(2, value);
         setResultType(MIRType_None);
     }
 
   public:
     INSTRUCTION_HEADER(InitElem)
 
     static MInitElem *New(TempAllocator &alloc, MDefinition *obj, MDefinition *id,
                           MDefinition *value)
@@ -1917,41 +1928,42 @@ class MInitElemGetterSetter
 class MVariadicInstruction : public MInstruction
 {
     FixedList<MUse> operands_;
 
   protected:
     bool init(TempAllocator &alloc, size_t length) {
         return operands_.init(alloc, length);
     }
+    void initOperand(size_t index, MDefinition *operand) {
+        // FixedList doesn't initialize its elements, so do an unchecked init.
+        operands_[index].initUnchecked(operand, this);
+    }
+    MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE {
+        return &operands_[index];
+    }
+    const MUse *getUseFor(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
+        return &operands_[index];
+    }
 
   public:
     // Will assert if called before initialization.
     MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
         return operands_[index].producer();
     }
     size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE {
         return operands_.length();
     }
     size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
         MOZ_ASSERT(u >= &operands_[0]);
         MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
         return u - &operands_[0];
     }
-    void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
-        operands_[index].set(operand, this);
-        operand->addUse(&operands_[index]);
-    }
-
-    MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE {
-        return &operands_[index];
-    }
-
-    const MUse *getUseFor(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
-        return &operands_[index];
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        operands_[index].replaceProducer(operand);
     }
 };
 
 class MCall
   : public MVariadicInstruction,
     public CallPolicy
 {
   private:
@@ -1980,17 +1992,17 @@ class MCall
     }
 
   public:
     INSTRUCTION_HEADER(Call)
     static MCall *New(TempAllocator &alloc, JSFunction *target, size_t maxArgc, size_t numActualArgs,
                       bool construct, bool isDOMCall);
 
     void initFunction(MDefinition *func) {
-        return setOperand(FunctionOperandIndex, func);
+        initOperand(FunctionOperandIndex, func);
     }
 
     bool needsArgCheck() const {
         return needsArgCheck_;
     }
 
     void disableArgCheck() {
         needsArgCheck_ = false;
@@ -2143,19 +2155,19 @@ class MApplyArgs
 {
   protected:
     // Monomorphic cache of single target from TI, or nullptr.
     CompilerRootFunction target_;
 
     MApplyArgs(JSFunction *target, MDefinition *fun, MDefinition *argc, MDefinition *self)
       : target_(target)
     {
-        setOperand(0, fun);
-        setOperand(1, argc);
-        setOperand(2, self);
+        initOperand(0, fun);
+        initOperand(1, argc);
+        initOperand(2, self);
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(ApplyArgs)
     static MApplyArgs *New(TempAllocator &alloc, JSFunction *target, MDefinition *fun,
                            MDefinition *argc, MDefinition *self);
 
@@ -2228,18 +2240,18 @@ class MAssertFloat32 : public MUnaryInst
 
 class MGetDynamicName
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >
 {
   protected:
     MGetDynamicName(MDefinition *scopeChain, MDefinition *name)
     {
-        setOperand(0, scopeChain);
-        setOperand(1, name);
+        initOperand(0, scopeChain);
+        initOperand(1, name);
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(GetDynamicName)
 
     static MGetDynamicName *
     New(TempAllocator &alloc, MDefinition *scopeChain, MDefinition *name) {
@@ -2264,17 +2276,17 @@ class MGetDynamicName
 // Bailout if the input string contains 'arguments' or 'eval'.
 class MFilterArgumentsOrEval
   : public MAryInstruction<1>,
     public BoxExceptPolicy<0, MIRType_String>
 {
   protected:
     MFilterArgumentsOrEval(MDefinition *string)
     {
-        setOperand(0, string);
+        initOperand(0, string);
         setGuard();
         setResultType(MIRType_None);
     }
 
   public:
     INSTRUCTION_HEADER(FilterArgumentsOrEval)
 
     static MFilterArgumentsOrEval *New(TempAllocator &alloc, MDefinition *string) {
@@ -2298,19 +2310,19 @@ class MCallDirectEval
     public MixPolicy<ObjectPolicy<0>,
                      MixPolicy<BoxExceptPolicy<1, MIRType_String>, BoxPolicy<2> > >
 {
   protected:
     MCallDirectEval(MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue,
                     jsbytecode *pc)
         : pc_(pc)
     {
-        setOperand(0, scopeChain);
-        setOperand(1, string);
-        setOperand(2, thisValue);
+        initOperand(0, scopeChain);
+        initOperand(1, string);
+        initOperand(2, thisValue);
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(CallDirectEval)
 
     static MCallDirectEval *
     New(TempAllocator &alloc, MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue,
@@ -2976,18 +2988,18 @@ class MRunOncePrologue
 // If the Value may be safely unboxed to an Object, return Object(A).
 // Otherwise, return B.
 // Used to implement return behavior for inlined constructors.
 class MReturnFromCtor
   : public MAryInstruction<2>,
     public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >
 {
     MReturnFromCtor(MDefinition *value, MDefinition *object) {
-        setOperand(0, value);
-        setOperand(1, object);
+        initOperand(0, value);
+        initOperand(1, object);
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(ReturnFromCtor)
     static MReturnFromCtor *New(TempAllocator &alloc, MDefinition *value, MDefinition *object)
     {
         return new(alloc) MReturnFromCtor(value, object);
@@ -4740,16 +4752,21 @@ class MPhi MOZ_FINAL : public MDefinitio
 
 #if DEBUG
     bool specialized_;
     uint32_t capacity_;
 #endif
 
   protected:
     MUse *getUseFor(size_t index) {
+        // Note: after the initial IonBuilder pass, it is OK to change phi
+        // operands such that they do not include the type sets of their
+        // operands. This can arise during e.g. value numbering, where
+        // definitions producing the same value may have different type sets.
+        JS_ASSERT(index < numOperands());
         return &inputs_[index];
     }
     const MUse *getUseFor(size_t index) const {
         return &inputs_[index];
     }
 
   public:
     INSTRUCTION_HEADER(Phi)
@@ -4768,39 +4785,33 @@ class MPhi MOZ_FINAL : public MDefinitio
     {
         setResultType(resultType);
     }
 
     static MPhi *New(TempAllocator &alloc, MIRType resultType = MIRType_Value) {
         return new(alloc) MPhi(alloc, resultType);
     }
 
-    void setOperand(size_t index, MDefinition *operand) {
-        // Note: after the initial IonBuilder pass, it is OK to change phi
-        // operands such that they do not include the type sets of their
-        // operands. This can arise during e.g. value numbering, where
-        // definitions producing the same value may have different type sets.
-        JS_ASSERT(index < numOperands());
-        inputs_[index].set(operand, this);
-        operand->addUse(&inputs_[index]);
-    }
-
     void removeOperand(size_t index);
+    void removeAllOperands();
 
     MDefinition *getOperand(size_t index) const {
         return inputs_[index].producer();
     }
     size_t numOperands() const {
         return inputs_.length();
     }
     size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
         MOZ_ASSERT(u >= &inputs_[0]);
         MOZ_ASSERT(u <= &inputs_[numOperands() - 1]);
         return u - &inputs_[0];
     }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        inputs_[index].replaceProducer(operand);
+    }
     bool hasBackedgeType() const {
         return hasBackedgeType_;
     }
     bool triedToSpecialize() const {
         return triedToSpecialize_;
     }
     void specialize(MIRType type) {
         triedToSpecialize_ = true;
@@ -5701,18 +5712,18 @@ class MInitializedLength
 };
 
 // Store to the initialized length in an elements header. Note the input is an
 // *index*, one less than the desired length.
 class MSetInitializedLength
   : public MAryInstruction<2>
 {
     MSetInitializedLength(MDefinition *elements, MDefinition *index) {
-        setOperand(0, elements);
-        setOperand(1, index);
+        initOperand(0, elements);
+        initOperand(1, index);
     }
 
   public:
     INSTRUCTION_HEADER(SetInitializedLength)
 
     static MSetInitializedLength *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index) {
         return new(alloc) MSetInitializedLength(elements, index);
     }
@@ -5760,18 +5771,18 @@ class MArrayLength
 };
 
 // Store to the length in an elements header. Note the input is an *index*, one
 // less than the desired length.
 class MSetArrayLength
   : public MAryInstruction<2>
 {
     MSetArrayLength(MDefinition *elements, MDefinition *index) {
-        setOperand(0, elements);
-        setOperand(1, index);
+        initOperand(0, elements);
+        initOperand(1, index);
     }
 
   public:
     INSTRUCTION_HEADER(SetArrayLength)
 
     static MSetArrayLength *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index) {
         return new(alloc) MSetArrayLength(elements, index);
     }
@@ -6302,19 +6313,19 @@ class MStoreElementCommon
 class MStoreElement
   : public MAryInstruction<3>,
     public MStoreElementCommon,
     public MixPolicy<SingleObjectPolicy, NoFloatPolicy<2> >
 {
     bool needsHoleCheck_;
 
     MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value, bool needsHoleCheck) {
-        setOperand(0, elements);
-        setOperand(1, index);
-        setOperand(2, value);
+        initOperand(0, elements);
+        initOperand(1, index);
+        initOperand(2, value);
         needsHoleCheck_ = needsHoleCheck;
         JS_ASSERT(elements->type() == MIRType_Elements);
         JS_ASSERT(index->type() == MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(StoreElement)
 
@@ -6351,20 +6362,20 @@ class MStoreElement
 // vector.
 class MStoreElementHole
   : public MAryInstruction<4>,
     public MStoreElementCommon,
     public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >
 {
     MStoreElementHole(MDefinition *object, MDefinition *elements,
                       MDefinition *index, MDefinition *value) {
-        setOperand(0, object);
-        setOperand(1, elements);
-        setOperand(2, index);
-        setOperand(3, value);
+        initOperand(0, object);
+        initOperand(1, elements);
+        initOperand(2, index);
+        initOperand(3, value);
         JS_ASSERT(elements->type() == MIRType_Elements);
         JS_ASSERT(index->type() == MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(StoreElementHole)
 
     static MStoreElementHole *New(TempAllocator &alloc, MDefinition *object, MDefinition *elements,
@@ -6773,20 +6784,20 @@ class MStoreTypedArrayElementHole
     public StoreTypedArrayHolePolicy
 {
     int arrayType_;
 
     MStoreTypedArrayElementHole(MDefinition *elements, MDefinition *length, MDefinition *index,
                                 MDefinition *value, int arrayType)
       : MAryInstruction<4>(), arrayType_(arrayType)
     {
-        setOperand(0, elements);
-        setOperand(1, length);
-        setOperand(2, index);
-        setOperand(3, value);
+        initOperand(0, elements);
+        initOperand(1, length);
+        initOperand(2, index);
+        initOperand(3, value);
         setMovable();
         JS_ASSERT(elements->type() == MIRType_Elements);
         JS_ASSERT(length->type() == MIRType_Int32);
         JS_ASSERT(index->type() == MIRType_Int32);
         JS_ASSERT(arrayType >= 0 && arrayType < ScalarTypeDescr::TYPE_MAX);
     }
 
   public:
@@ -7375,29 +7386,29 @@ class MDispatchInstruction
         { }
     };
     Vector<Entry, 4, IonAllocPolicy> map_;
 
     // An optional fallback path that uses MCall.
     MBasicBlock *fallback_;
     MUse operand_;
 
+    void initOperand(size_t index, MDefinition *operand) {
+        JS_ASSERT(index == 0);
+        operand_.init(operand, this);
+    }
+
   public:
     MDispatchInstruction(TempAllocator &alloc, MDefinition *input)
       : map_(alloc), fallback_(nullptr)
     {
-        setOperand(0, input);
+        initOperand(0, input);
     }
 
   protected:
-    void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
-        JS_ASSERT(index == 0);
-        operand_.set(operand, this);
-        operand->addUse(&operand_);
-    }
     MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE {
         JS_ASSERT(index == 0);
         return &operand_;
     }
     const MUse *getUseFor(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
         JS_ASSERT(index == 0);
         return &operand_;
     }
@@ -7407,16 +7418,20 @@ class MDispatchInstruction
     }
     size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE {
         return 1;
     }
     size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
         JS_ASSERT(u == getUseFor(0));
         return 0;
     }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        JS_ASSERT(index == 0);
+        operand_.replaceProducer(operand);
+    }
 
   public:
     void setSuccessor(size_t i, MBasicBlock *successor) {
         JS_ASSERT(i < numSuccessors());
         if (i == map_.length())
             fallback_ = successor;
         else
             map_[i].block = successor;
@@ -8461,18 +8476,18 @@ class MCallInitElementArray
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
 {
     uint32_t index_;
 
     MCallInitElementArray(MDefinition *obj, uint32_t index, MDefinition *val)
       : index_(index)
     {
-        setOperand(0, obj);
-        setOperand(1, val);
+        initOperand(0, obj);
+        initOperand(1, val);
     }
 
   public:
     INSTRUCTION_HEADER(CallInitElementArray)
 
     static MCallInitElementArray *New(TempAllocator &alloc, MDefinition *obj, uint32_t index,
                                       MDefinition *val)
     {
@@ -8503,18 +8518,18 @@ class MSetDOMProperty
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
 {
     const JSJitSetterOp func_;
 
     MSetDOMProperty(const JSJitSetterOp func, MDefinition *obj, MDefinition *val)
       : func_(func)
     {
-        setOperand(0, obj);
-        setOperand(1, val);
+        initOperand(0, obj);
+        initOperand(1, val);
     }
 
   public:
     INSTRUCTION_HEADER(SetDOMProperty)
 
     static MSetDOMProperty *New(TempAllocator &alloc, const JSJitSetterOp func, MDefinition *obj,
                                 MDefinition *val)
     {
@@ -8551,20 +8566,20 @@ class MGetDOMProperty
 
   protected:
     MGetDOMProperty(const JSJitInfo *jitinfo, MDefinition *obj, MDefinition *guard)
       : info_(jitinfo)
     {
         JS_ASSERT(jitinfo);
         JS_ASSERT(jitinfo->type() == JSJitInfo::Getter);
 
-        setOperand(0, obj);
+        initOperand(0, obj);
 
         // Pin the guard as an operand if we want to hoist later
-        setOperand(1, guard);
+        initOperand(1, guard);
 
         // We are movable iff the jitinfo says we can be.
         if (isDomMovable()) {
             JS_ASSERT(jitinfo->aliasSet() != JSJitInfo::AliasEverything);
             setMovable();
         } else {
             // If we're not movable, that means we shouldn't be DCEd either,
             // because we might throw an exception when called, and getting rid
@@ -9724,42 +9739,32 @@ class MResumePoint MOZ_FINAL : public MN
         Outer        // State before inlining.
     };
 
   private:
     friend class MBasicBlock;
     friend void AssertBasicGraphCoherency(MIRGraph &graph);
 
     FixedList<MUse> operands_;
-    uint32_t stackDepth_;
     jsbytecode *pc_;
     MResumePoint *caller_;
     MInstruction *instruction_;
     Mode mode_;
 
     MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *parent, Mode mode);
     void inherit(MBasicBlock *state);
 
   protected:
     // Initializes operands_ to an empty array of a fixed length.
     // The array may then be filled in by inherit().
-    bool init(TempAllocator &alloc) {
-        return operands_.init(alloc, stackDepth_);
-    }
-
-    // Overwrites an operand without updating its Uses.
-    void setOperand(size_t index, MDefinition *operand) {
-        JS_ASSERT(index < stackDepth_);
-        operands_[index].set(operand, this);
-        operand->addUse(&operands_[index]);
-    }
+    bool init(TempAllocator &alloc);
 
     void clearOperand(size_t index) {
-        JS_ASSERT(index < stackDepth_);
-        operands_[index].set(nullptr, this);
+        // FixedList doesn't initialize its elements, so do an unchecked init.
+        operands_[index].initUncheckedWithoutProducer(this);
     }
 
     MUse *getUseFor(size_t index) {
         return &operands_[index];
     }
     const MUse *getUseFor(size_t index) const {
         return &operands_[index];
     }
@@ -9767,36 +9772,42 @@ class MResumePoint MOZ_FINAL : public MN
   public:
     static MResumePoint *New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc,
                              MResumePoint *parent, Mode mode);
 
     MNode::Kind kind() const {
         return MNode::ResumePoint;
     }
     size_t numOperands() const {
-        return stackDepth_;
+        return operands_.length();
     }
     size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
         MOZ_ASSERT(u >= &operands_[0]);
         MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
         return u - &operands_[0];
     }
+    void initOperand(size_t index, MDefinition *operand) {
+        // FixedList doesn't initialize its elements, so do an unchecked init.
+        operands_[index].initUnchecked(operand, this);
+    }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        operands_[index].replaceProducer(operand);
+    }
 
     bool isObservableOperand(MUse *u) const;
     bool isObservableOperand(size_t index) const;
 
     MDefinition *getOperand(size_t index) const {
-        JS_ASSERT(index < stackDepth_);
         return operands_[index].producer();
     }
     jsbytecode *pc() const {
         return pc_;
     }
     uint32_t stackDepth() const {
-        return stackDepth_;
+        return operands_.length();
     }
     MResumePoint *caller() const {
         return caller_;
     }
     void setCaller(MResumePoint *caller) {
         caller_ = caller;
     }
     uint32_t frameCount() const {
@@ -9811,19 +9822,19 @@ class MResumePoint MOZ_FINAL : public MN
     void setInstruction(MInstruction *ins) {
         instruction_ = ins;
     }
     Mode mode() const {
         return mode_;
     }
 
     void discardUses() {
-        for (size_t i = 0; i < stackDepth_; i++) {
+        for (size_t i = 0; i < operands_.length(); i++) {
             if (operands_[i].hasProducer())
-                operands_[i].producer()->removeUse(&operands_[i]);
+                operands_[i].discardProducer();
         }
     }
 
     bool writeRecoverData(CompactBufferWriter &writer) const;
 
     virtual void dump(FILE *fp) const;
     virtual void dump() const;
 };
@@ -10160,17 +10171,17 @@ class MAsmJSParameter : public MNullaryI
     }
 
     ABIArg abi() const { return abi_; }
 };
 
 class MAsmJSReturn : public MAryControlInstruction<1, 0>
 {
     MAsmJSReturn(MDefinition *ins) {
-        setOperand(0, ins);
+        initOperand(0, ins);
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSReturn);
     static MAsmJSReturn *New(TempAllocator &alloc, MDefinition *ins) {
         return new(alloc) MAsmJSReturn(ins);
     }
 };
@@ -10240,25 +10251,26 @@ class MAsmJSCall MOZ_FINAL : public MIns
     };
 
     CallSiteDesc desc_;
     Callee callee_;
     FixedList<MUse> operands_;
     FixedList<AnyRegister> argRegs_;
     size_t spIncrement_;
 
+    void initOperand(size_t index, MDefinition *operand) {
+        // FixedList doesn't initialize its elements, so do an unchecked init.
+        operands_[index].initUnchecked(operand, this);
+    }
+
     MAsmJSCall(const CallSiteDesc &desc, Callee callee, size_t spIncrement)
      : desc_(desc), callee_(callee), spIncrement_(spIncrement)
     { }
 
   protected:
-    void setOperand(size_t index, MDefinition *operand) {
-        operands_[index].set(operand, this);
-        operand->addUse(&operands_[index]);
-    }
     MUse *getUseFor(size_t index) {
         return &operands_[index];
     }
     const MUse *getUseFor(size_t index) const {
         return &operands_[index];
     }
 
   public:
@@ -10277,16 +10289,19 @@ class MAsmJSCall MOZ_FINAL : public MIns
     size_t numOperands() const {
         return operands_.length();
     }
     size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
         MOZ_ASSERT(u >= &operands_[0]);
         MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
         return u - &operands_[0];
     }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        operands_[index].replaceProducer(operand);
+    }
     MDefinition *getOperand(size_t index) const {
         JS_ASSERT(index < numOperands());
         return operands_[index].producer();
     }
     size_t numArgs() const {
         return argRegs_.length();
     }
     AnyRegister registerForArg(size_t index) const {
@@ -10308,16 +10323,53 @@ class MAsmJSCall MOZ_FINAL : public MIns
         return spIncrement_;
     }
 
     bool possiblyCalls() const {
         return true;
     }
 };
 
+void MUse::init(MDefinition *producer, MNode *consumer)
+{
+    MOZ_ASSERT(!consumer_, "Initializing MUse that already has a consumer");
+    MOZ_ASSERT(!producer_, "Initializing MUse that already has a producer");
+    initUnchecked(producer, consumer);
+}
+
+void MUse::initUnchecked(MDefinition *producer, MNode *consumer)
+{
+    MOZ_ASSERT(consumer, "Initializing to null consumer");
+    consumer_ = consumer;
+    producer_ = producer;
+    producer_->addUse(this);
+}
+
+void MUse::initUncheckedWithoutProducer(MNode *consumer)
+{
+    MOZ_ASSERT(consumer, "Initializing to null consumer");
+    consumer_ = consumer;
+    producer_ = nullptr;
+}
+
+void MUse::replaceProducer(MDefinition *producer)
+{
+    MOZ_ASSERT(consumer_, "Resetting MUse without a consumer");
+    producer_->removeUse(this);
+    producer_ = producer;
+    producer_->addUse(this);
+}
+
+void MUse::discardProducer()
+{
+    MOZ_ASSERT(consumer_, "Clearing MUse without a consumer");
+    producer_->removeUse(this);
+    producer_ = nullptr;
+}
+
 #undef INSTRUCTION_HEADER
 
 // Implement opcode casts now that the compiler can see the inheritance.
 #define OPCODE_CASTS(opcode)                                                \
     M##opcode *MDefinition::to##opcode()                                    \
     {                                                                       \
         JS_ASSERT(is##opcode());                                            \
         return static_cast<M##opcode *>(this);                              \
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -392,42 +392,42 @@ MBasicBlock::inherit(TempAllocator &allo
         if (kind_ == PENDING_LOOP_HEADER) {
             size_t i = 0;
             for (i = 0; i < info().firstStackSlot(); i++) {
                 MPhi *phi = MPhi::New(alloc);
                 if (!phi->addInputSlow(pred->getSlot(i)))
                     return false;
                 addPhi(phi);
                 setSlot(i, phi);
-                entryResumePoint()->setOperand(i, phi);
+                entryResumePoint()->initOperand(i, phi);
             }
 
             JS_ASSERT(stackPhiCount <= stackDepth());
             JS_ASSERT(info().firstStackSlot() <= stackDepth() - stackPhiCount);
 
             // Avoid creating new phis for stack values that aren't part of the
             // loop.  Note that for loop headers that can OSR, all values on the
             // stack are part of the loop.
             for (; i < stackDepth() - stackPhiCount; i++) {
                 MDefinition *val = pred->getSlot(i);
                 setSlot(i, val);
-                entryResumePoint()->setOperand(i, val);
+                entryResumePoint()->initOperand(i, val);
             }
 
             for (; i < stackDepth(); i++) {
                 MPhi *phi = MPhi::New(alloc);
                 if (!phi->addInputSlow(pred->getSlot(i)))
                     return false;
                 addPhi(phi);
                 setSlot(i, phi);
-                entryResumePoint()->setOperand(i, phi);
+                entryResumePoint()->initOperand(i, phi);
             }
         } else {
             for (size_t i = 0; i < stackDepth(); i++)
-                entryResumePoint()->setOperand(i, getSlot(i));
+                entryResumePoint()->initOperand(i, getSlot(i));
         }
     } else {
         /*
          * Don't leave the operands uninitialized for the caller, as it may not
          * initialize them later on.
          */
         for (size_t i = 0; i < stackDepth(); i++)
             entryResumePoint()->clearOperand(i);
@@ -479,17 +479,17 @@ MBasicBlock::getSlot(uint32_t index)
     return slots_[index];
 }
 
 void
 MBasicBlock::initSlot(uint32_t slot, MDefinition *ins)
 {
     slots_[slot] = ins;
     if (entryResumePoint())
-        entryResumePoint()->setOperand(slot, ins);
+        entryResumePoint()->initOperand(slot, ins);
 }
 
 void
 MBasicBlock::shimmySlots(int discardDepth)
 {
     // Move all slots above the given depth down by one,
     // overwriting the MDefinition at discardDepth.
 
@@ -780,21 +780,18 @@ MBasicBlock::discardAllInstructions()
         iter = instructions_.removeAt(iter);
     }
     lastIns_ = nullptr;
 }
 
 void
 MBasicBlock::discardAllPhiOperands()
 {
-    for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) {
-        MPhi *phi = *iter;
-        for (size_t i = 0, e = phi->numOperands(); i < e; i++)
-            phi->discardOperand(i);
-    }
+    for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++)
+        iter->removeAllOperands();
 
     for (MBasicBlock **pred = predecessors_.begin(); pred != predecessors_.end(); pred++)
         (*pred)->setSuccessorWithPhis(nullptr, 0);
 }
 
 void
 MBasicBlock::discardAllPhis()
 {
@@ -863,18 +860,17 @@ MBasicBlock::addPhi(MPhi *phi)
     graph().allocDefinitionId(phi);
 }
 
 MPhiIterator
 MBasicBlock::discardPhiAt(MPhiIterator &at)
 {
     JS_ASSERT(!phis_.empty());
 
-    for (size_t i = 0, e = at->numOperands(); i < e; i++)
-        at->discardOperand(i);
+    at->removeAllOperands();
 
     MPhiIterator result = phis_.removeAt(at);
 
     if (phis_.empty()) {
         for (MBasicBlock **pred = predecessors_.begin(); pred != predecessors_.end(); pred++)
             (*pred)->setSuccessorWithPhis(nullptr, 0);
     }
     return result;
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -125,20 +125,19 @@ RangeAnalysis::alloc() const
     return graph_.alloc();
 }
 
 void
 RangeAnalysis::replaceDominatedUsesWith(MDefinition *orig, MDefinition *dom,
                                             MBasicBlock *block)
 {
     for (MUseIterator i(orig->usesBegin()); i != orig->usesEnd(); ) {
-        if (i->consumer() != dom && IsDominatedUse(block, *i))
-            i = i->consumer()->replaceOperand(i, dom);
-        else
-            i++;
+        MUse *use = *i++;
+        if (use->consumer() != dom && IsDominatedUse(block, use))
+            use->replaceProducer(dom);
     }
 }
 
 bool
 RangeAnalysis::addBetaNodes()
 {
     IonSpew(IonSpew_Range, "Adding beta nodes");
 
--- a/js/src/jit/UnreachableCodeElimination.cpp
+++ b/js/src/jit/UnreachableCodeElimination.cpp
@@ -203,21 +203,20 @@ void
 UnreachableCodeElimination::checkDependencyAndRemoveUsesFromUnmarkedBlocks(MDefinition *instr)
 {
     // When the instruction depends on removed block,
     // alias analysis needs to get rerun to have the right dependency.
     if (!disableAliasAnalysis_ && instr->dependency() && !instr->dependency()->block()->isMarked())
         rerunAliasAnalysis_ = true;
 
     for (MUseIterator iter(instr->usesBegin()); iter != instr->usesEnd(); ) {
-        if (!iter->consumer()->block()->isMarked()) {
+        MUse *use = *iter++;
+        if (!use->consumer()->block()->isMarked()) {
             instr->setUseRemovedUnchecked();
-            iter = instr->removeUse(iter);
-        } else {
-            iter++;
+            use->discardProducer();
         }
     }
 }
 
 bool
 UnreachableCodeElimination::removeUnmarkedBlocksAndClearDominators()
 {
     // Removes blocks that are not marked from the graph.  For blocks