Bug 1171945: IonMonkey: Use tryXXX structure for jsop_binary in IonBuilder, r=jandem
authorHannes Verschore <hv1989@gmail.com>
Fri, 14 Aug 2015 12:45:47 +0200
changeset 257815 d791ba00bf065740fc504329a1075f5132cdc800
parent 257814 2db399cd414f8ca2e70b41d4c36d50fa0a9a8d87
child 257816 1989889145ce0f32bec326eb32d84d59eaf40eec
push id29230
push userryanvm@gmail.com
push dateFri, 14 Aug 2015 19:16:25 +0000
treeherdermozilla-central@09dd35dd1193 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1171945
milestone43.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 1171945: IonMonkey: Use tryXXX structure for jsop_binary in IonBuilder, r=jandem
js/src/jit/IonAnalysis.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jsapi-tests/testJitDCEinGVN.cpp
js/src/jsapi-tests/testJitFoldsTo.cpp
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2980,58 +2980,58 @@ jit::ConvertLinearSum(TempAllocator& all
     MDefinition* def = nullptr;
 
     for (size_t i = 0; i < sum.numTerms(); i++) {
         LinearTerm term = sum.term(i);
         MOZ_ASSERT(!term.term->isConstantValue());
         if (term.scale == 1) {
             if (def) {
                 def = MAdd::New(alloc, def, term.term);
-                def->toAdd()->setInt32();
+                def->toAdd()->setInt32Specialization();
                 block->insertAtEnd(def->toInstruction());
                 def->computeRange(alloc);
             } else {
                 def = term.term;
             }
         } else if (term.scale == -1) {
             if (!def) {
                 def = MConstant::New(alloc, Int32Value(0));
                 block->insertAtEnd(def->toInstruction());
                 def->computeRange(alloc);
             }
             def = MSub::New(alloc, def, term.term);
-            def->toSub()->setInt32();
+            def->toSub()->setInt32Specialization();
             block->insertAtEnd(def->toInstruction());
             def->computeRange(alloc);
         } else {
             MOZ_ASSERT(term.scale != 0);
             MConstant* factor = MConstant::New(alloc, Int32Value(term.scale));
             block->insertAtEnd(factor);
             MMul* mul = MMul::New(alloc, term.term, factor);
-            mul->setInt32();
+            mul->setInt32Specialization();
             block->insertAtEnd(mul);
             mul->computeRange(alloc);
             if (def) {
                 def = MAdd::New(alloc, def, mul);
-                def->toAdd()->setInt32();
+                def->toAdd()->setInt32Specialization();
                 block->insertAtEnd(def->toInstruction());
                 def->computeRange(alloc);
             } else {
                 def = mul;
             }
         }
     }
 
     if (convertConstant && sum.constant()) {
         MConstant* constant = MConstant::New(alloc, Int32Value(sum.constant()));
         block->insertAtEnd(constant);
         constant->computeRange(alloc);
         if (def) {
             def = MAdd::New(alloc, def, constant);
-            def->toAdd()->setInt32();
+            def->toAdd()->setInt32Specialization();
             block->insertAtEnd(def->toInstruction());
             def->computeRange(alloc);
         } else {
             def = constant;
         }
     }
 
     if (!def) {
@@ -3087,17 +3087,17 @@ jit::ConvertLinearInequality(TempAllocat
                 break;
             }
         }
 
         MDefinition* constant = MConstant::New(alloc, Int32Value(lhs.constant()));
         block->insertAtEnd(constant->toInstruction());
         constant->computeRange(alloc);
         lhsDef = MAdd::New(alloc, lhsDef, constant);
-        lhsDef->toAdd()->setInt32();
+        lhsDef->toAdd()->setInt32Specialization();
         block->insertAtEnd(lhsDef->toInstruction());
         lhsDef->computeRange(alloc);
     } while (false);
 
     if (!rhsDef) {
         rhsDef = MConstant::New(alloc, Int32Value(0));
         block->insertAtEnd(rhsDef->toInstruction());
         rhsDef->computeRange(alloc);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1651,17 +1651,17 @@ IonBuilder::inspectOpcode(JSOp op)
       case JSOP_URSH:
         return jsop_bitop(op);
 
       case JSOP_ADD:
       case JSOP_SUB:
       case JSOP_MUL:
       case JSOP_DIV:
       case JSOP_MOD:
-        return jsop_binary(op);
+        return jsop_binary_arith(op);
 
       case JSOP_POW:
         return jsop_pow();
 
       case JSOP_POS:
         return jsop_pos();
 
       case JSOP_NEG:
@@ -4540,70 +4540,179 @@ IonBuilder::jsop_bitop(JSOp op)
 
     current->push(ins);
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
 
     return true;
 }
 
-bool
-IonBuilder::jsop_binary(JSOp op, MDefinition* left, MDefinition* right)
-{
-    // Do a string concatenation if adding two inputs that are int or string
-    // and at least one is a string.
-    if (op == JSOP_ADD &&
-        ((left->type() == MIRType_String &&
-          (right->type() == MIRType_String ||
-           right->type() == MIRType_Int32 ||
-           right->type() == MIRType_Double)) ||
-         (left->type() == MIRType_Int32 &&
-          right->type() == MIRType_String) ||
-         (left->type() == MIRType_Double &&
-          right->type() == MIRType_String)))
-    {
-        MConcat* ins = MConcat::New(alloc(), left, right);
-        current->add(ins);
-        current->push(ins);
-        return maybeInsertResume();
-    }
-
-    MBinaryArithInstruction* ins;
+MDefinition::Opcode
+JSOpToMDefinition(JSOp op)
+{
     switch (op) {
       case JSOP_ADD:
-        ins = MAdd::New(alloc(), left, right);
-        break;
-
+        return MDefinition::Op_Add;
       case JSOP_SUB:
-        ins = MSub::New(alloc(), left, right);
-        break;
-
+        return MDefinition::Op_Sub;
       case JSOP_MUL:
-        ins = MMul::New(alloc(), left, right);
-        break;
-
+        return MDefinition::Op_Mul;
       case JSOP_DIV:
-        ins = MDiv::New(alloc(), left, right);
-        break;
-
+        return MDefinition::Op_Div;
       case JSOP_MOD:
-        ins = MMod::New(alloc(), left, right);
-        break;
-
+        return MDefinition::Op_Mod;
       default:
         MOZ_CRASH("unexpected binary opcode");
     }
+}
+
+bool
+IonBuilder::binaryArithTryConcat(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
+{
+    MOZ_ASSERT(*emitted == false);
+
+    // Try to convert an addition into a concat operation if the inputs
+    // indicate this might be a concatenation.
+
+    // Only try to replace this with concat when we have an addition.
+    if (op != JSOP_ADD)
+        return true;
+
+    // Make sure one of the inputs is a string.
+    if (left->type() != MIRType_String && right->type() != MIRType_String)
+        return true;
+
+    // The none-string input (if present) should be atleast a numerical type.
+    // Which we can easily coerce to string.
+    if (right->type() != MIRType_String && !IsNumberType(right->type()))
+        return true;
+    if (left->type() != MIRType_String && !IsNumberType(left->type()))
+        return true;
+
+    MConcat* ins = MConcat::New(alloc(), left, right);
+    current->add(ins);
+    current->push(ins);
+
+    if (!maybeInsertResume())
+        return false;
+
+    *emitted = true;
+    return true;
+}
+
+static inline bool
+SimpleArithOperand(MDefinition* op)
+{
+    return !op->mightBeType(MIRType_Object)
+        && !op->mightBeType(MIRType_String)
+        && !op->mightBeType(MIRType_Symbol)
+        && !op->mightBeType(MIRType_MagicOptimizedArguments)
+        && !op->mightBeType(MIRType_MagicHole)
+        && !op->mightBeType(MIRType_MagicIsConstructing);
+}
+
+bool
+IonBuilder::binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
+{
+    MOZ_ASSERT(*emitted == false);
+
+    // Try to emit a specialized binary instruction based on the input types
+    // of the operands.
+
+    // Anything complex - strings, symbols, and objects - are not specialized
+    if (!SimpleArithOperand(left) || !SimpleArithOperand(right))
+        return true;
+
+    // One of the inputs need to be a number.
+    if (!IsNumberType(left->type()) && !IsNumberType(right->type()))
+        return true;
+
+    MDefinition::Opcode defOp = JSOpToMDefinition(op);
+    MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
+    ins->setNumberSpecialization(alloc(), inspector, pc);
+
+    if (op == JSOP_ADD || op == JSOP_MUL)
+        ins->setCommutative();
 
     current->add(ins);
-    ins->infer(alloc(), inspector, pc);
+    current->push(ins);
+
+    MOZ_ASSERT(!ins->isEffectful());
+    if (!maybeInsertResume())
+        return false;
+
+    *emitted = true;
+    return true;
+}
+
+bool
+IonBuilder::binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
+                                                         MDefinition* left, MDefinition* right)
+{
+    MOZ_ASSERT(*emitted == false);
+
+    // Try to emit a specialized binary instruction speculating the
+    // type using the baseline caches.
+
+    MIRType specialization = inspector->expectedBinaryArithSpecialization(pc);
+    if (specialization == MIRType_None)
+        return true;
+
+    MDefinition::Opcode def_op = JSOpToMDefinition(op);
+    MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
+    ins->setSpecialization(specialization);
+
+    current->add(ins);
     current->push(ins);
 
-    if (ins->isEffectful())
-        return resumeAfter(ins);
-    return maybeInsertResume();
+    MOZ_ASSERT(!ins->isEffectful());
+    if (!maybeInsertResume())
+        return false;
+
+    *emitted = true;
+    return true;
+}
+
+bool
+IonBuilder::jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right)
+{
+    bool emitted = false;
+
+    if (!forceInlineCaches()) {
+        if (!binaryArithTryConcat(&emitted, op, left, right) || emitted)
+            return emitted;
+
+        if (!binaryArithTrySpecialized(&emitted, op, left, right) || emitted)
+            return emitted;
+
+        if (!binaryArithTrySpecializedOnBaselineInspector(&emitted, op, left, right) || emitted)
+            return emitted;
+    }
+
+    // Not possible to optimize. Do a slow vm call.
+    MDefinition::Opcode def_op = JSOpToMDefinition(op);
+    MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
+
+    // Decrease type from 'any type' to 'empty type' when one of the operands
+    // is 'empty typed'.
+    maybeMarkEmpty(ins);
+
+    current->add(ins);
+    current->push(ins);
+    MOZ_ASSERT(ins->isEffectful());
+    return resumeAfter(ins);
+}
+
+bool
+IonBuilder::jsop_binary_arith(JSOp op)
+{
+    MDefinition* right = current->pop();
+    MDefinition* left = current->pop();
+
+    return jsop_binary_arith(op, left, right);
 }
 
 bool
 IonBuilder::jsop_pow()
 {
     MDefinition* exponent = current->pop();
     MDefinition* base = current->pop();
 
@@ -4616,54 +4725,45 @@ IonBuilder::jsop_pow()
     // For now, use MIRType_Double, as a safe cover-all. See bug 1188079.
     MPow* pow = MPow::New(alloc(), base, exponent, MIRType_Double);
     current->add(pow);
     current->push(pow);
     return true;
 }
 
 bool
-IonBuilder::jsop_binary(JSOp op)
-{
-    MDefinition* right = current->pop();
-    MDefinition* left = current->pop();
-
-    return jsop_binary(op, left, right);
-}
-
-bool
 IonBuilder::jsop_pos()
 {
     if (IsNumberType(current->peek(-1)->type())) {
         // Already int32 or double. Set the operand as implicitly used so it
         // doesn't get optimized out if it has no other uses, as we could bail
         // out.
         current->peek(-1)->setImplicitlyUsedUnchecked();
         return true;
     }
 
     // Compile +x as x * 1.
     MDefinition* value = current->pop();
     MConstant* one = MConstant::New(alloc(), Int32Value(1));
     current->add(one);
 
-    return jsop_binary(JSOP_MUL, value, one);
+    return jsop_binary_arith(JSOP_MUL, value, one);
 }
 
 bool
 IonBuilder::jsop_neg()
 {
     // Since JSOP_NEG does not use a slot, we cannot push the MConstant.
     // The MConstant is therefore passed to JSOP_MUL without slot traffic.
     MConstant* negator = MConstant::New(alloc(), Int32Value(-1));
     current->add(negator);
 
     MDefinition* right = current->pop();
 
-    if (!jsop_binary(JSOP_MUL, negator, right))
+    if (!jsop_binary_arith(JSOP_MUL, negator, right))
         return false;
     return true;
 }
 
 class AutoAccumulateReturns
 {
     MIRGraph& graph_;
     MIRGraphReturns* prev_;
@@ -7161,16 +7261,34 @@ IonBuilder::maybeInsertResume()
         return true;
 
     MNop* ins = MNop::New(alloc());
     current->add(ins);
 
     return resumeAfter(ins);
 }
 
+void
+IonBuilder::maybeMarkEmpty(MDefinition* ins)
+{
+    MOZ_ASSERT(ins->type() == MIRType_Value);
+
+    // When one of the operands has no type information, mark the output
+    // as having no possible types too. This is to avoid degrading
+    // subsequent analysis.
+    for (size_t i = 0; i < ins->numOperands(); i++) {
+        if (!ins->emptyResultTypeSet())
+            continue;
+
+        TemporaryTypeSet* types = alloc().lifoAlloc()->new_<TemporaryTypeSet>();
+        if (types)
+            ins->setResultTypeSet(types);
+    }
+}
+
 // Return whether property lookups can be performed effectlessly on clasp.
 static bool
 ClassHasEffectlessLookup(const Class* clasp)
 {
     return (clasp == &UnboxedPlainObject::class_) ||
            (clasp == &UnboxedArrayObject::class_) ||
            IsTypedObjectClass(clasp) ||
            (clasp->isNative() && !clasp->ops.lookupProperty);
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -369,16 +369,18 @@ class IonBuilder
     // returns def.  Otherwise, returns an MInstruction that has that definite
     // type, infallibly unboxing ins as needed.  The new instruction will be
     // added to |current| in this case.
     MDefinition* ensureDefiniteType(MDefinition* def, MIRType definiteType);
 
     // Creates a MDefinition based on the given def improved with type as TypeSet.
     MDefinition* ensureDefiniteTypeSet(MDefinition* def, TemporaryTypeSet* types);
 
+    void maybeMarkEmpty(MDefinition* ins);
+
     JSObject* getSingletonPrototype(JSFunction* target);
 
     MDefinition* createThisScripted(MDefinition* callee);
     MDefinition* createThisScriptedSingleton(JSFunction* target, MDefinition* callee);
     MDefinition* createThisScriptedBaseline(MDefinition* callee);
     MDefinition* createThis(JSFunction* target, MDefinition* callee);
     MInstruction* createDeclEnvObject(MDefinition* callee, MDefinition* scopeObj);
     MInstruction* createCallObject(MDefinition* callee, MDefinition* scopeObj);
@@ -476,16 +478,23 @@ class IonBuilder
                                            MDefinition* obj,
                                            int32_t fieldOffset,
                                            MDefinition* value,
                                            TypedObjectPrediction fieldTypeReprs);
     bool setPropTryCache(bool* emitted, MDefinition* obj,
                          PropertyName* name, MDefinition* value,
                          bool barrier, TemporaryTypeSet* objTypes);
 
+    // jsop_binary_arith helpers.
+    MBinaryArithInstruction* binaryArithInstruction(JSOp op, MDefinition* left, MDefinition* right);
+    bool binaryArithTryConcat(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
+    bool binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
+    bool binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
+                                                      MDefinition* right);
+
     // binary data lookup helpers.
     TypedObjectPrediction typedObjectPrediction(MDefinition* typedObj);
     TypedObjectPrediction typedObjectPrediction(TemporaryTypeSet* types);
     bool typedObjectHasField(MDefinition* typedObj,
                              PropertyName* name,
                              size_t* fieldOffset,
                              TypedObjectPrediction* fieldTypeReprs,
                              size_t* fieldIndex);
@@ -617,18 +626,18 @@ class IonBuilder
     MDefinition* addLexicalCheck(MDefinition* input);
 
     bool tryFoldInstanceOf(MDefinition* lhs, JSObject* protoObject);
     bool hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* hasOnProto);
 
     bool jsop_add(MDefinition* left, MDefinition* right);
     bool jsop_bitnot();
     bool jsop_bitop(JSOp op);
-    bool jsop_binary(JSOp op);
-    bool jsop_binary(JSOp op, MDefinition* left, MDefinition* right);
+    bool jsop_binary_arith(JSOp op);
+    bool jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right);
     bool jsop_pow();
     bool jsop_pos();
     bool jsop_neg();
     bool jsop_setarg(uint32_t arg);
     bool jsop_defvar(uint32_t index);
     bool jsop_deffun(uint32_t index);
     bool jsop_notearg();
     bool jsop_checklexical();
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -3424,17 +3424,17 @@ IonBuilder::prepareForSimdLoadStore(Call
     // sure to be in bounds for 4 slots, so add 3, etc.).
     MOZ_ASSERT(Scalar::byteSize(simdType) % Scalar::byteSize(*arrayType) == 0);
     int32_t suppSlotsNeeded = Scalar::byteSize(simdType) / Scalar::byteSize(*arrayType) - 1;
     if (suppSlotsNeeded) {
         MConstant* suppSlots = constant(Int32Value(suppSlotsNeeded));
         MAdd* addedIndex = MAdd::New(alloc(), *index, suppSlots);
         // We're fine even with the add overflows, as long as the generated code
         // for the bounds check uses an unsigned comparison.
-        addedIndex->setInt32();
+        addedIndex->setInt32Specialization();
         current->add(addedIndex);
         indexForBoundsCheck = addedIndex;
     }
 
     MInstruction* length;
     addTypedArrayLengthAndData(array, SkipBoundsCheck, index, &length, elements);
 
     // It can be that the index is out of bounds, while the added index for the
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -108,16 +108,18 @@ MDefinition::constantToBoolean()
 }
 
 static MConstant*
 EvaluateConstantOperands(TempAllocator& alloc, MBinaryInstruction* ins, bool* ptypeChange = nullptr)
 {
     MDefinition* left = ins->getOperand(0);
     MDefinition* right = ins->getOperand(1);
 
+    MOZ_ASSERT(IsNumberType(left->type()) && IsNumberType(right->type()));
+
     if (!left->isConstantValue() || !right->isConstantValue())
         return nullptr;
 
     Value lhs = left->constantValue();
     Value rhs = right->constantValue();
     Value ret = UndefinedValue();
 
     switch (ins->op()) {
@@ -2231,16 +2233,61 @@ NeedNegativeZeroCheck(MDefinition* def)
             break;
           default:
             return true;
         }
     }
     return false;
 }
 
+MBinaryArithInstruction*
+MBinaryArithInstruction::New(TempAllocator& alloc, Opcode op,
+                             MDefinition* left, MDefinition* right)
+{
+    switch (op) {
+      case Op_Add:
+        return MAdd::New(alloc, left, right);
+      case Op_Sub:
+        return MSub::New(alloc, left, right);
+      case Op_Mul:
+        return MMul::New(alloc, left, right);
+      case Op_Div:
+        return MDiv::New(alloc, left, right);
+      case Op_Mod:
+        return MMod::New(alloc, left, right);
+      default:
+        MOZ_CRASH("unexpected binary opcode");
+    }
+}
+
+void
+MBinaryArithInstruction::setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector,
+                                                 jsbytecode* pc)
+{
+    setSpecialization(MIRType_Double);
+
+    // Try to specialize as int32.
+    if (getOperand(0)->type() == MIRType_Int32 && getOperand(1)->type() == MIRType_Int32) {
+        bool seenDouble = inspector->hasSeenDoubleResult(pc);
+
+        // Use int32 specialization if the operation doesn't overflow on its
+        // constant operands and if the operation has never overflowed.
+        if (!seenDouble && !constantDoubleResult(alloc))
+            setInt32Specialization();
+    }
+}
+
+bool
+MBinaryArithInstruction::constantDoubleResult(TempAllocator& alloc)
+{
+    bool typeChange = false;
+    EvaluateConstantOperands(alloc, this, &typeChange);
+    return typeChange;
+}
+
 MDefinition*
 MBinaryArithInstruction::foldsTo(TempAllocator& alloc)
 {
     if (specialization_ == MIRType_None)
         return this;
 
     MDefinition* lhs = getOperand(0);
     MDefinition* rhs = getOperand(1);
@@ -2649,91 +2696,16 @@ SimpleArithOperand(MDefinition* op)
     return !op->mightBeType(MIRType_Object)
         && !op->mightBeType(MIRType_String)
         && !op->mightBeType(MIRType_Symbol)
         && !op->mightBeType(MIRType_MagicOptimizedArguments)
         && !op->mightBeType(MIRType_MagicHole)
         && !op->mightBeType(MIRType_MagicIsConstructing);
 }
 
-void
-MBinaryArithInstruction::infer(TempAllocator& alloc, BaselineInspector* inspector, jsbytecode* pc)
-{
-    MOZ_ASSERT(this->type() == MIRType_Value);
-
-    specialization_ = MIRType_None;
-
-    // Anything complex - strings, symbols, and objects - are not specialized
-    // unless baseline type hints suggest it might be profitable
-    if (!SimpleArithOperand(getOperand(0)) || !SimpleArithOperand(getOperand(1)))
-        return inferFallback(inspector, pc);
-
-    // Retrieve type information of lhs and rhs.
-    MIRType lhs = getOperand(0)->type();
-    MIRType rhs = getOperand(1)->type();
-
-    // Guess a result type based on the inputs.
-    // Don't specialize for neither-integer-nor-double results.
-    if (lhs == MIRType_Int32 && rhs == MIRType_Int32) {
-        setResultType(MIRType_Int32);
-
-        // If the operation will always overflow on its constant operands, use a
-        // double specialization so that it can be constant folded later.
-        if (isMul() || isDiv()) {
-            bool typeChange = false;
-            EvaluateConstantOperands(alloc, this, &typeChange);
-            if (typeChange)
-                setResultType(MIRType_Double);
-        }
-
-        // If the operation has ever overflowed, use a double specialization.
-        if (inspector->hasSeenDoubleResult(pc))
-            setResultType(MIRType_Double);
-
-    } else if (IsFloatingPointType(lhs) || IsFloatingPointType(rhs)) {
-        // Double operations take precedence over float32 operations (i.e. if
-        // any operand needs a double as an input, convert all operands to
-        // doubles)
-        setResultType(MIRType_Double);
-    } else {
-        return inferFallback(inspector, pc);
-    }
-
-    MOZ_ASSERT(lhs < MIRType_String || lhs == MIRType_Value);
-    MOZ_ASSERT(rhs < MIRType_String || rhs == MIRType_Value);
-
-    if (isAdd() || isMul())
-        setCommutative();
-
-    MOZ_ASSERT(IsNumberType(this->type()));
-    specialization_ = this->type();
-}
-
-void
-MBinaryArithInstruction::inferFallback(BaselineInspector* inspector,
-                                       jsbytecode* pc)
-{
-    // Try to specialize based on what baseline observed in practice.
-    specialization_ = inspector->expectedBinaryArithSpecialization(pc);
-    if (specialization_ != MIRType_None) {
-        setResultType(specialization_);
-        return;
-    }
-
-    // If we can't specialize because we have no type information at all for
-    // the lhs or rhs, mark the binary instruction as having no possible types
-    // either to avoid degrading subsequent analysis.
-    if (getOperand(0)->emptyResultTypeSet() || getOperand(1)->emptyResultTypeSet()) {
-        LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
-        TemporaryTypeSet* types = alloc->new_<TemporaryTypeSet>();
-        if (types)
-            setResultTypeSet(types);
-    }
-}
-
 static bool
 SafelyCoercesToDouble(MDefinition* op)
 {
     // Strings and symbols are unhandled -- visitToDouble() doesn't support them yet.
     // Null is unhandled -- ToDouble(null) == 0, but (0 == null) is false.
     return SimpleArithOperand(op) && !op->mightBeType(MIRType_Null);
 }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5589,36 +5589,43 @@ class MBinaryArithInstruction
     // NeedNegativeZeroCheck to check if the result of a multiplication needs to
     // produce -0 double value, and for avoiding overflow checks.
 
     // This optimization happens when the multiplication cannot be truncated
     // even if all uses are truncating its result, such as when the range
     // analysis detect a precision loss in the multiplication.
     TruncateKind implicitTruncate_;
 
-    void inferFallback(BaselineInspector* inspector, jsbytecode* pc);
-
   public:
     MBinaryArithInstruction(MDefinition* left, MDefinition* right)
       : MBinaryInstruction(left, right),
         implicitTruncate_(NoTruncate)
     {
-        setMovable();
-    }
+        specialization_ = MIRType_None;
+        setMovable();
+    }
+
+    static MBinaryArithInstruction* New(TempAllocator& alloc, Opcode op,
+                                        MDefinition* left, MDefinition* right);
+
+    bool constantDoubleResult(TempAllocator& alloc);
 
     MDefinition* foldsTo(TempAllocator& alloc) override;
 
     virtual double getIdentity() = 0;
 
-    void infer(TempAllocator& alloc, BaselineInspector* inspector, jsbytecode* pc);
-
-    void setInt32() {
+    void setSpecialization(MIRType type) {
+        specialization_ = type;
+        setResultType(type);
+    }
+    void setInt32Specialization() {
         specialization_ = MIRType_Int32;
         setResultType(MIRType_Int32);
     }
+    void setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector, jsbytecode* pc);
 
     virtual void trySpecializeFloat32(TempAllocator& alloc) override;
 
     bool congruentTo(const MDefinition* ins) const override {
         return binaryCongruentTo(ins);
     }
     AliasSet getAliasSet() const override {
         if (specialization_ >= MIRType_Object)
--- a/js/src/jsapi-tests/testJitDCEinGVN.cpp
+++ b/js/src/jsapi-tests/testJitDCEinGVN.cpp
@@ -23,18 +23,22 @@ BEGIN_TEST(testJitDCEinGVN_ins)
 
     // mul0 = p * p
     // mul1 = mul0 * mul0
     // return p
     MParameter* p = func.createParameter();
     block->add(p);
     MMul* mul0 = MMul::New(func.alloc, p, p, MIRType_Double);
     block->add(mul0);
+    if (!mul0->typePolicy()->adjustInputs(func.alloc, mul0))
+        return false;
     MMul* mul1 = MMul::New(func.alloc, mul0, mul0, MIRType_Double);
     block->add(mul1);
+    if (!mul1->typePolicy()->adjustInputs(func.alloc, mul1))
+        return false;
     MReturn* ret = MReturn::New(func.alloc, p);
     block->end(ret);
 
     if (!func.runGVN())
         return false;
 
     // mul0 and mul1 should be deleted.
     for (MInstructionIterator ins = block->begin(); ins != block->end(); ins++) {
--- a/js/src/jsapi-tests/testJitFoldsTo.cpp
+++ b/js/src/jsapi-tests/testJitFoldsTo.cpp
@@ -23,26 +23,29 @@ BEGIN_TEST(testJitFoldsTo_DivReciprocal)
 
     // return p / 4.0
     MParameter* p = func.createParameter();
     block->add(p);
     MConstant* c = MConstant::New(func.alloc, DoubleValue(4.0));
     block->add(c);
     MDiv* div = MDiv::New(func.alloc, p, c, MIRType_Double);
     block->add(div);
+    if (!div->typePolicy()->adjustInputs(func.alloc, div))
+        return false;
+    MDefinition* left = div->getOperand(0);
     MReturn* ret = MReturn::New(func.alloc, div);
     block->end(ret);
 
     if (!func.runGVN())
         return false;
 
     // Test that the div got folded to p * 0.25.
     MDefinition* op = ret->getOperand(0);
     CHECK(op->isMul());
-    CHECK(op->getOperand(0) == p);
+    CHECK(op->getOperand(0) == left);
     CHECK(op->getOperand(1)->isConstant());
     CHECK(op->getOperand(1)->toConstant()->value().toNumber() == 0.25);
     return true;
 }
 END_TEST(testJitFoldsTo_DivReciprocal)
 
 BEGIN_TEST(testJitFoldsTo_NoDivReciprocal)
 {
@@ -51,27 +54,31 @@ BEGIN_TEST(testJitFoldsTo_NoDivReciproca
 
     // return p / 5.0
     MParameter* p = func.createParameter();
     block->add(p);
     MConstant* c = MConstant::New(func.alloc, DoubleValue(5.0));
     block->add(c);
     MDiv* div = MDiv::New(func.alloc, p, c, MIRType_Double);
     block->add(div);
+    if (!div->typePolicy()->adjustInputs(func.alloc, div))
+        return false;
+    MDefinition* left = div->getOperand(0);
+    MDefinition* right = div->getOperand(1);
     MReturn* ret = MReturn::New(func.alloc, div);
     block->end(ret);
 
     if (!func.runGVN())
         return false;
 
     // Test that the div didn't get folded.
     MDefinition* op = ret->getOperand(0);
     CHECK(op->isDiv());
-    CHECK(op->getOperand(0) == p);
-    CHECK(op->getOperand(1) == c);
+    CHECK(op->getOperand(0) == left);
+    CHECK(op->getOperand(1) == right);
     return true;
 }
 END_TEST(testJitFoldsTo_NoDivReciprocal)
 
 BEGIN_TEST(testJitNotNot)
 {
     MinimalFunc func;
     MBasicBlock* block = func.createEntryBlock();